| | |
| | | > |
| | | <basic-main-content> |
| | | <!-- 查询条件筛选栏 --> |
| | | <!-- 查询条件筛选栏 --> |
| | | <div class="ztzf-form-search"> |
| | | <el-form :model="filters" inline> |
| | | <el-row :gutter="24"> |
| | |
| | | </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 |
| | |
| | | </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 |
| | |
| | | </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 |
| | |
| | | flowEvent, |
| | | getstatusCount, |
| | | getStepInfo, |
| | | getReviewById, |
| | | getCreateEventJob, |
| | | exportTheTicket |
| | | } from '@/api/tickets/ticket'; |
| | | import { getSFDictionaryTree } from '@/api/job/task'; |
| | |
| | | data() { |
| | | return { |
| | | currentIndex: null, // 当前显示的数据索引 |
| | | |
| | | |
| | | approveLoading: false, |
| | | rejectLoading: false, |
| | | dispatchLoading: false, |
| | | completeLoading: false, |
| | | finalizeLoading: false, |
| | | createoredit: '', |
| | | activeTab: 'all', |
| | |
| | | |
| | | algorithms: [], |
| | | algorithms2: [], |
| | | algorithms3: [], |
| | | statuses: [ |
| | | { label: '待审核', value: '2' }, |
| | | { label: '待处理', value: '0' }, |
| | |
| | | }, |
| | | created() { |
| | | this.inputMapShowDefaultCenter = null; |
| | | |
| | | this.loadAMapScripts(); |
| | | this.fetchDropdownData(); |
| | | }, |
| | |
| | | ? 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)); |
| | |
| | | // 更新当前详情 |
| | | 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; |
| | |
| | | this.currentDetail = detail; |
| | | this.dispatchDialogVisible = true; |
| | | }, |
| | | async handleQRCode(val) { |
| | | val.showQR = !val.showQR; |
| | | }, |
| | | |
| | | async loadAMapScripts() { |
| | | try { |
| | | const areaCode = this.userInfo.detail.areaCode; |
| | |
| | | this.$message.error('地图加载失败,请检查网络或API Key配置'); |
| | | } |
| | | }, |
| | | |
| | | async fetchDropdownData() { |
| | | try { |
| | | const response = await getTicketInfo(); |
| | |
| | | } |
| | | }, |
| | | |
| | | |
| | | // 获取表格数据 |
| | | async fetchTableData() { |
| | | if (this.isFetching) return; |
| | | this.isFetching = true; |
| | |
| | | } |
| | | }, |
| | | |
| | | 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); |
| | |
| | | }, |
| | | |
| | | |
| | | formatLocation(location) { |
| | | if (!Array.isArray(location)) { |
| | | return '未知位置'; |
| | | } |
| | | return `${location[0].toFixed(6)}, ${location[1].toFixed(6)}`; |
| | | }, |
| | | |
| | | async getStepInfoData(val) { |
| | | try { |
| | | const orderNum = val |
| | |
| | | }; |
| | | }); |
| | | } |
| | | console.log('stepInfos',this.stepInfos); |
| | | |
| | | return true; // 表示成功获取数据 |
| | | } catch (error) { |
| | | console.error('获取步骤信息失败:', error); |
| | |
| | | |
| | | // 获取步骤信息 |
| | | await this.getStepInfoData(row.orderNumber); |
| | | |
| | | this.currentDetail.status = row.status; |
| | | |
| | | console.log(detailData, 'detailDatadetailDatadetailData'); |
| | | |
| | | this.currentDetail = { |
| | | ...detailData, |
| | | showQR: false, |
| | |
| | | console.log('this.currentDetail', this.currentDetail); |
| | | |
| | | this.detailVisible = true; |
| | | |
| | | this.handleTypeChange(this.currentDetail.type); |
| | | // 重置上传组件的文件列表 |
| | | this.$nextTick(() => { |
| | | if (this.$refs.MapContainer && this.$refs.MapContainer.initAddEntity) { |
| | |
| | | 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; |
| | |
| | | 工单状态: 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(); |
| | |
| | | // 可以在这里处理错误,或者保持空实现 |
| | | 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; |
| | |
| | | }; |
| | | </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; |