无人机管理后台前端(已迁走)
张含笑
2025-11-20 771cd475a2aad9938f4e2085ca018ba31bdc1d7f
src/views/tickets/ticket.vue
@@ -9,7 +9,6 @@
      >
        <basic-main-content>
          <!-- 查询条件筛选栏 -->
          <!-- 查询条件筛选栏 -->
          <div class="ztzf-form-search">
            <el-form :model="filters" inline>
              <el-row :gutter="24">
@@ -26,33 +25,7 @@
                  </el-form-item>
                </el-col>
                <!-- <el-select
                  v-model="filters.department"
                  placeholder="请选择所属部门"
                  class="filter-item"
                  clearable
                >
                  <el-option
                    v-for="item in departments"
                    :key="item.value"
                    :label="item.label"
                    :value="item.value"
                  />
                </el-select> -->
                <!-- <el-select
                  v-model="filters.type"
                  placeholder="请选择工单类型"
                  class="filter-item"
                  clearable
                  @change="handleSearch"
                >
                  <el-option
                    v-for="item in types"
                    :key="item.value"
                    :label="item.label"
                    :value="item.value"
                  />
                </el-select> -->
                <el-col :span="5">
                  <el-form-item label="选择日期:">
                  <el-date-picker
@@ -87,34 +60,6 @@
                  </el-form-item>
                </el-col>
                <!-- <el-select
                  v-model="filters.algorithm"
                  placeholder="请选择关联算法"
                  class="filter-item"
                  clearable
                  @change="handleSearch"
                >
                  <el-option
                    v-for="item in algorithms"
                    :key="item.dict_key"
                    :label="item.dict_value"
                    :value="item.dict_key"
                  />
                </el-select> -->
                <!-- <el-tree-select
                  popper-class="custom-tree-select"
                  :style="{ width: pxToRem(186) }"
                  placeholder="请选择关联算法"
                  v-model="dictKey"
                  :data="dataList"
                  :default-expanded-keys="[dictKey]"
                  check-strictly
                  node-key="id"
                  :props="treePropsSF"
                  @node-click="handleSFNodeClick"
                  clearable
                  @clear="handleClear"
                /> -->
                <el-col :span="4">
                  <el-form-item label="选择算法:">
                  <el-tree-select
@@ -270,37 +215,37 @@
    </el-tabs>
    <!-- 新建工单对话框 -->
   <create-ticket-dialog
  v-model="dialogVisible"
  :departments="departments"
  :department-users="departmentUsers"
  :types="types"
  :all-algorithms="allAlgorithms"
  :edit-data="editFormData"
  :map-center="inputMapShowDefaultCenter"
  @create-success="handleCreateSuccess"
  @draft-success="handleDraftSuccess"
  @create-error="handleCreateError"
/>
        <create-ticket-dialog
        v-model="dialogVisible"
        :departments="departments"
        :department-users="departmentUsers"
        :types="types"
        :all-algorithms="allAlgorithms"
        :edit-data="editFormData"
        :map-center="inputMapShowDefaultCenter"
        @create-success="handleCreateSuccess"
        @draft-success="handleDraftSuccess"
        @create-error="handleCreateError"
      />
    <!-- 工单详情对话框 -->
<ticket-detail-dialog
  v-model="detailVisible"
  :current-detail="currentDetail"
  :current-index="currentIndex"
  :total-items="tableData.length"
  :algorithms="algorithms"
  :types="types"
  :permission="permission"
  :step-status-list="stepStatusList"
  :work-type="workType"
  @detail-success="handleDetailSuccess"
  @detail-error="handleDetailError"
  @prev-item="handlePrevItem"
  @next-item="handleNextItem"
  @update:current-detail="handleCurrentDetailUpdate"
  @approve-and-dispatch="handleApproveAndDispatch"
/>
      <ticket-detail-dialog
        v-model="detailVisible"
        :current-detail="currentDetail"
        :current-index="currentIndex"
        :total-items="tableData.length"
        :algorithms="algorithms2"
        :types="types"
        :permission="permission"
        :step-status-list="stepStatusList"
        :work-type="workType"
        @detail-success="handleDetailSuccess"
        @detail-error="handleDetailError"
        @prev-item="handlePrevItem"
        @next-item="handleNextItem"
        @update:current-detail="handleCurrentDetailUpdate"
        @approve-and-dispatch="handleApproveAndDispatch"
      />
    <!-- 派发工单对话框 -->
      <dispatch-dialog
@@ -418,8 +363,6 @@
  flowEvent,
  getstatusCount,
  getStepInfo,
  getReviewById,
  getCreateEventJob,
  exportTheTicket
} from '@/api/tickets/ticket';
import { getSFDictionaryTree } from '@/api/job/task';
@@ -448,12 +391,6 @@
  data() {
    return {
      currentIndex: null, // 当前显示的数据索引
      approveLoading: false,
      rejectLoading: false,
      dispatchLoading: false,
      completeLoading: false,
      finalizeLoading: false,
      createoredit: '',
      activeTab: 'all',
@@ -486,6 +423,7 @@
      algorithms: [],
      algorithms2: [],
      algorithms3: [],
      statuses: [
        { label: '待审核', value: '2' },
        { label: '待处理', value: '0' },
@@ -604,7 +542,6 @@
  },
  created() {
    this.inputMapShowDefaultCenter = null;
    this.loadAMapScripts();
    this.fetchDropdownData();
  },
@@ -691,134 +628,7 @@
        ? this.departmentUsers[this.dispatchForm.department] || []
        : [];
    },
    detailTableData() {
      return [
        {
          label: '工单名称',
          value: this.currentDetail.orderName,
          editable: this.currentDetail.status === 0,
          type: 'input',
        },
        {
          label: '关键任务',
          value: this.currentDetail.keyData,
          editable: false,
        },
        {
          label: '工单创建人',
          value: this.currentDetail.creator,
          editable: false,
        },
        {
          label: '当前状态',
          value: this.mapStatus(this.currentDetail.status),
          editable: false,
        },
        {
          label: '事件地址',
          value: this.currentDetail.address,
          editable: false,
        },
        {
          label: '工单类型',
          value: this.currentDetail.type,
          editable: this.currentDetail.status === 0,
          type: 'select',
          options: this.types,
        },
        {
          label: '关联算法',
          value: this.currentDetail.aiType,
          editable: false,
        },
        {
          label: '发起单位',
          value: this.currentDetail.department,
          editable: false,
        },
        {
          label: '发起任务时间',
          value: this.currentDetail.startTime,
          editable: false,
        },
        {
          label: '工单内容',
          value: this.currentDetail.content,
          editable: this.currentDetail.status === 0,
          type: 'textarea',
        },
      ];
    },
    detailFields() {
      return [
        {
          label: '工单名称',
          value: this.currentDetail.orderName,
          editable: this.currentDetail.status === 0,
          type: 'input',
        },
        { label: '关键任务', value: this.currentDetail.keyData, editable: false },
        { label: '工单创建人', value: this.currentDetail.creator, editable: false },
        { label: '当前状态', value: this.mapStatus(this.currentDetail.status), editable: false },
        { label: '事件地址', value: this.currentDetail.address, editable: false },
        {
          label: '工单类型',
          value: this.currentDetail.type,
          editable: this.currentDetail.status === 0,
          type: 'select',
          options: this.types,
        },
        { label: '关联算法', value: this.currentDetail.aiType, editable: false },
        { label: '发起单位', value: this.currentDetail.department, editable: false },
        { label: '发起任务时间', value: this.currentDetail.startTime, editable: false },
        {
          label: '工单内容',
          value: this.currentDetail.content,
          editable: this.currentDetail.status === 0,
          type: 'textarea',
        },
      ];
    },
    formattedDetailFields() {
      const fields = [
        { label: '工单名称', value: this.currentDetail.orderName },
        {
          label: '工单类型',
          // 修改这里:使用 types 数组查找对应的 label
          value:
            this.types.find(t => t.value === this.currentDetail.type)?.label ||
            this.currentDetail.type,
        },
        { label: '关联任务', value: this.currentDetail.job_name || '/' },
        { label: '工单创建人', value: this.currentDetail.creator },
        { label: '当前状态', value: this.mapStatus(this.currentDetail.status) },
        { label: '事件地址', value: this.currentDetail.address || this.currentDetail.latAndLon }, // 包含经纬度信息
        {
          label: '关联算法',
          value:
            this.algorithms.find(t => t.value === this.currentDetail.aiType)?.label ||
            this.currentDetail.aiType,
        },
        { label: '发起单位', value: this.currentDetail.department },
        { label: '发起任务时间', value: this.currentDetail.startTime },
        { label: '工单内容', value: this.currentDetail.content },
      ];
      // 过滤掉值为 '/' 的列
      const filteredFields = fields.filter(field => field.value !== '/');
      // 将字段分成两列
      const formattedFields = [];
      for (let i = 0; i < filteredFields.length; i += 2) {
        formattedFields.push({
          label1: filteredFields[i]?.label || '',
          value1: filteredFields[i]?.value || (filteredFields[i]?.label ? '暂无数据' : ''),
          label2: filteredFields[i + 1]?.label || '',
          value2: filteredFields[i + 1]?.value || (filteredFields[i + 1]?.label ? '暂无数据' : ''),
        });
      }
      return formattedFields;
    },
    dynamicFixedStatuses() {
      // 直接使用接口返回的 stepInfos
      return this.stepInfos.map(step => String(step.status));
@@ -962,7 +772,6 @@
    // 更新当前详情
    updateCurrentDetail() {  
      this.currentDetail = this.tableData[this.currentIndex];
      console.log('当前',this.currentDetail);
      this.currentDetail.mediaUrl = this.currentDetail.photo_url;
      this.currentDetail.updatePhotoUrl = this.currentDetail.update_photo_url;
      this.currentDetail.processingDetail = this.currentDetail.processingDetail;
@@ -981,9 +790,7 @@
      this.currentDetail = detail;
      this.dispatchDialogVisible = true;
    },
    async handleQRCode(val) {
      val.showQR = !val.showQR;
    },
    async loadAMapScripts() {
      try {
        const areaCode = this.userInfo.detail.areaCode;
@@ -1015,7 +822,6 @@
        this.$message.error('地图加载失败,请检查网络或API Key配置');
      }
    },
    async fetchDropdownData() {
      try {
        const response = await getTicketInfo();
@@ -1067,7 +873,7 @@
      }
    },
    // 获取表格数据
    async fetchTableData() {
      if (this.isFetching) return;
      this.isFetching = true;
@@ -1172,7 +978,41 @@
      }
    },
 handleTypeChange(typeValue) {
      // this.form.algorithm = [];
      if (!typeValue) {
        // 未选择类型时清空算法列表
        this.algorithms2 = [];
        this.algorithms3 = [];
        return;
      }
      const matchedCategory = this.allAlgorithms.find(category => category.dict_key === typeValue);
      if (
        !matchedCategory ||
        !matchedCategory.algorithms ||
        matchedCategory.algorithms.length === 0
      ) {
        // 无匹配的算法时清空
        this.algorithms2 = [];
         this.algorithms3 = [];
        this.$message.info('该工单类型暂无关联算法');
        return;
      }
      this.algorithms2 = matchedCategory.algorithms.map(algo => ({
        label: algo.dict_value,
        value: algo.dict_key,
        dict_key: algo.dict_key,
        dict_value: algo.dict_value,
      }));
        this.algorithms3 = matchedCategory.algorithms.map(algo => ({
        label: algo.dict_value,
        value: algo.dict_key,
        dict_key: algo.dict_key,
        dict_value: algo.dict_value,
      }));
    },
    formatDate(date) {
      if (!date) return undefined;
      const d = new Date(date);
@@ -1294,13 +1134,6 @@
    },
    formatLocation(location) {
      if (!Array.isArray(location)) {
        return '未知位置';
      }
      return `${location[0].toFixed(6)}, ${location[1].toFixed(6)}`;
    },
async getStepInfoData(val) {
  try {
  const orderNum = val
@@ -1331,8 +1164,6 @@
        };
      });
    }
    console.log('stepInfos',this.stepInfos);
    return true; // 表示成功获取数据
  } catch (error) {
    console.error('获取步骤信息失败:', error);
@@ -1367,10 +1198,8 @@
  // 获取步骤信息
  await this.getStepInfoData(row.orderNumber);
  this.currentDetail.status = row.status;
  console.log(detailData, 'detailDatadetailDatadetailData');
  this.currentDetail = {
    ...detailData,
    showQR: false,
@@ -1380,7 +1209,7 @@
  console.log('this.currentDetail', this.currentDetail);
  this.detailVisible = true;
 this.handleTypeChange(this.currentDetail.type);
  // 重置上传组件的文件列表
  this.$nextTick(() => {
    if (this.$refs.MapContainer && this.$refs.MapContainer.initAddEntity) {
@@ -1397,41 +1226,7 @@
    handleDetailError(error) {
      console.error('工单操作失败:', error);
    },
    getStepHandler(status) {
      const step = this.stepInfos.find(step => step.status === status);
      return step ? step.name : '';
    },
    getStepTime(status) {
      const step = this.stepInfos.find(step => step.status === status);
      // 如果 step 不存在或 step.time 为 0,返回 false
      return step && step.time && step.time !== '0' ? step.time : false;
    },
    getStepCreateTime(status) {
      const step = this.stepInfos.find(step => step.status === status);
      return step ? step.create_time : null;
    },
    getActiveStep() {
      // 步骤索引适配
      const arr = this.stepStatusList;
      const index = arr.indexOf(String(this.currentDetail.status));
      return index !== -1 ? index + 2 : 1;
    },
    openMap() {
      const areaCode = this.userInfo.detail.areaCode;
      const subAreaCode = areaCode ? areaCode.substring(0, 6) : '';
      getAdcodeObj(geoJson, 'adcode', subAreaCode);
      this.$message.info('地图选址功能暂未实现');
    },
    handlePreview(file) {
      this.$message.info(`预览图片:${file.name}`);
    },
    handleRemove(file) {
      this.$message.info(`移除图片:${file.name}`);
    },
    refreshChange() {
      if (this.isFetching) return;
@@ -1508,113 +1303,11 @@
        工单状态: this.mapStatus(Number(item.status || 0)),
      };
    },
    async approveTicket() {
      if (this.approveLoading) return;
      this.approveLoading = true;
      try {
        const data = {
          id: this.currentDetail.id,
          status: this.currentDetail.status,
          isPass: 0, // 0 表示通过
          eventNum: this.currentDetail.orderNumber,
        };
        const file = this.currentDetail.file || null; // 如果没有文件,则为 null
        const response = await flowEvent(data, file);
        if (response.data.code === 0) {
          this.$message.success('工单已通过');
          this.detailVisible = false;
          this.fetchTableData();
        } else {
          throw new Error(response.data.msg || '操作失败');
        }
      } catch (error) {
        this.$message.error(error.message || '操作失败,请稍后重试');
      } finally {
        this.approveLoading = false;
      }
    },
    async rejectTicket() {
      if (this.rejectLoading) return;
      this.rejectLoading = true;
      try {
        const data = {
          id: this.currentDetail.id,
          status: this.currentDetail.status,
          isPass: 1, // 1 表示不通过
        };
        const response = await flowEvent(data);
        if (response.data.code === 0) {
          this.$message.success('工单未通过');
          this.detailVisible = false;
          this.fetchTableData();
        } else {
          throw new Error(response.data.msg || '操作失败');
        }
      } catch (error) {
        this.$message.error(error.message || '操作失败,请稍后重试');
      } finally {
        this.rejectLoading = false;
      }
    },
    async submitProcessing() {
      if (this.currentDetail.status !== 3) {
        this.$message.warning('只有处理中状态的工单可以提交处理详情');
        return;
      }
      try {
        const data = {
          id: this.currentDetail.id, // 当前工单 ID
          status: this.currentDetail.status, // 当前工单状态
          processing_details: this.currentDetail.processingDetail, // 事件处理详情
        };
        // 如果有图片,添加 file 参数
        const file = this.currentDetail.photos?.[0]?.raw || null;
        const response = await flowEvent(data, file);
        if (response.data.code === 0) {
          this.$message.success('处理详情提交成功');
          this.detailVisible = false;
          this.fetchTableData();
        } else {
          throw new Error(response.data.msg || '提交失败');
        }
      } catch (error) {
        console.error('处理详情提交失败:', error);
        this.$message.error(error.message || '提交失败,请稍后重试');
      }
    },
    markAsCompleted() {
      this.$message.success('工单已标记为完成');
    },
    async approveAndDispatch() {
      if (this.dispatchLoading) return;
      // 添加必填项检查
      if (!this.currentDetail.orderName || !this.currentDetail.orderName.trim()) {
        this.$message.warning('请填写工单名称');
        return;
      }
      if (!this.currentDetail.aiType) {
        this.$message.warning('请选择关联算法');
        return;
      }
      if (!this.currentDetail.content || !this.currentDetail.content.trim()) {
        this.$message.warning('请填写工单内容');
        return;
      }
      // 通过验证后,打开派发对话框
      this.dispatchDialogVisible = true;
      console.log('受理', this.currentDetail);
    },
      // 派发成功回调
    // 派发成功回调
    handleDispatchSuccess() {
      this.detailVisible = false;
      this.fetchTableData();
@@ -1625,21 +1318,7 @@
      // 可以在这里处理错误,或者保持空实现
      console.error('派发失败:', error);
    },
    hasProcessingBtnPermission() {
      // undefined 或 false 都返回 false,只有 true 返回 true
      //  console.log('权限检查:', this.permission.tickets_processing_btn)
      return this.permission && this.permission.tickets_processing_btn === true;
    },
    hasProcessedAndOverBtnPermission() {
      // undefined 或 false 都返回 false,只有 true 返回 true
      //console.log('权限检查:', this.permission)
      return this.permission && this.permission.tickets_view_processedAndOver === true;
    },
    hasReviewBtnPermission() {
      // undefined 或 false 都返回 false,只有 true 返回 true
      // console.log('权限检查:', this.permission)
      return this.permission && this.permission.tickets_review_btn === true;
    },
    async finalizeTicket() {
      if (this.finalizeLoading) return;
      this.finalizeLoading = true;
@@ -2032,14 +1711,6 @@
};
</script>
<style lang="scss">
.custom-dialog {
  max-height: 96vh;
  .el-dialog__body {
    border-top: 0.1rem solid #f0f0f0;
  }
}
.custom-qrcode-popover {
  min-width: 160px !important;
  min-height: 140px !important;