| | |
| | | > |
| | | <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; |
| | |
| | | this.dialogVisible = false; |
| | | this.resetForm(); |
| | | }, |
| | | |
| | | handleTypeChange(typeValue) { |
| | | this.form.algorithm = []; |
| | | if (!typeValue) { |
| | | this.availableAlgorithms = []; |
| | | return; |
| | | } |
| | | |
| | | const matchedCategory = this.allAlgorithms.find(category => category.dict_key === typeValue); |
| | | |
| | | if (!matchedCategory || !matchedCategory.algorithms || matchedCategory.algorithms.length === 0) { |
| | | this.availableAlgorithms = []; |
| | | this.$message.info('该工单类型暂无关联算法'); |
| | |
| | | })); |
| | | }, |
| | | |
| | | handleDepartmentChange(deptId) { |
| | | handleDepartmentChange() { |
| | | this.form.handler = ''; |
| | | }, |
| | | |
| | |
| | | </div> |
| | | <div class="event-title-center">{{ currentDetail.orderName || '事件名称' }}</div> |
| | | </div> |
| | | <div v-if="totalTime" class="event-total-time">总耗时:{{ totalTime }}</div> |
| | | |
| | | <div v-if="totalTime" class="event-total-time">总耗时:{{ totalTime }}</div> |
| | | <!-- 工单状态流程 --> |
| | | <div class="custom-steps-container"> |
| | | <!-- 标题行 --> |
| | |
| | | </el-dialog> |
| | | </template> |
| | | |
| | | <script> |
| | | <script setup> |
| | | import { ref, computed, watch, nextTick } from 'vue'; |
| | | import { flowEvent, getStepInfo } from '@/api/tickets/ticket'; |
| | | import { getSmallImg, getShowImg } from '@/utils/util'; |
| | | import CreateQRcode from '@/components/CreateQRcode/CreateQRcode.vue'; |
| | | |
| | | export default { |
| | | name: 'TicketDetailDialog', |
| | | components: { |
| | | CreateQRcode |
| | | import { ElMessage} from 'element-plus'; |
| | | // 定义props |
| | | const props = defineProps({ |
| | | modelValue: { |
| | | type: Boolean, |
| | | default: false |
| | | }, |
| | | props: { |
| | | modelValue: { |
| | | type: Boolean, |
| | | default: false |
| | | currentDetail: { |
| | | type: Object, |
| | | default: () => ({}) |
| | | }, |
| | | currentIndex: { |
| | | type: Number, |
| | | default: 0 |
| | | }, |
| | | totalItems: { |
| | | type: Number, |
| | | default: 0 |
| | | }, |
| | | algorithms: { |
| | | type: Array, |
| | | default: () => [] |
| | | }, |
| | | types: { |
| | | type: Array, |
| | | default: () => [] |
| | | }, |
| | | permission: { |
| | | type: Object, |
| | | default: () => ({}) |
| | | }, |
| | | stepStatusList: { |
| | | type: Array, |
| | | default: () => [] |
| | | }, |
| | | workType: { |
| | | type: Number, |
| | | default: 0 |
| | | } |
| | | }); |
| | | |
| | | // 定义emits |
| | | const emit = defineEmits([ |
| | | 'update:modelValue', |
| | | 'detail-success', |
| | | 'detail-error', |
| | | 'prev-item', |
| | | 'next-item', |
| | | 'update:currentDetail', |
| | | 'approve-and-dispatch' |
| | | ]); |
| | | |
| | | // 响应式数据 |
| | | const stepInfos = ref([]); |
| | | const totalTime = ref(''); |
| | | const approveLoading = ref(false); |
| | | const rejectLoading = ref(false); |
| | | const dispatchLoading = ref(false); |
| | | const completeLoading = ref(false); |
| | | const finalizeLoading = ref(false); |
| | | const uploadRef = ref(null); |
| | | const mapContainerRef = ref(null); |
| | | |
| | | // 计算属性 |
| | | const dialogVisible = computed({ |
| | | get() { |
| | | return props.modelValue; |
| | | }, |
| | | set(value) { |
| | | emit('update:modelValue', value); |
| | | } |
| | | }); |
| | | |
| | | const isEditMode = computed(() => { |
| | | return props.currentDetail.status === -1; |
| | | }); |
| | | |
| | | const formattedDetailFields = computed(() => { |
| | | const fields = [ |
| | | { label: '工单名称', value: props.currentDetail.orderName }, |
| | | { |
| | | label: '工单类型', |
| | | value: props.types.find(t => t.value === props.currentDetail.type)?.label || props.currentDetail.type, |
| | | }, |
| | | currentDetail: { |
| | | type: Object, |
| | | default: () => ({}) |
| | | { label: '关联任务', value: props.currentDetail.job_name || '/' }, |
| | | { label: '工单创建人', value: props.currentDetail.creator }, |
| | | { label: '当前状态', value: mapStatus(props.currentDetail.status) }, |
| | | { label: '事件地址', value: props.currentDetail.address || props.currentDetail.latAndLon }, |
| | | { |
| | | label: '关联算法', |
| | | value: props.algorithms.find(t => t.value === props.currentDetail.aiType)?.label || props.currentDetail.aiType, |
| | | }, |
| | | currentIndex: { |
| | | type: Number, |
| | | default: 0 |
| | | }, |
| | | totalItems: { |
| | | type: Number, |
| | | default: 0 |
| | | }, |
| | | algorithms: { |
| | | type: Array, |
| | | default: () => [] |
| | | }, |
| | | types: { |
| | | type: Array, |
| | | default: () => [] |
| | | }, |
| | | permission: { |
| | | type: Object, |
| | | default: () => ({}) |
| | | }, |
| | | stepStatusList: { |
| | | type: Array, |
| | | default: () => [] |
| | | }, |
| | | workType: { |
| | | type: Number, |
| | | default: 0 |
| | | { label: '发起单位', value: props.currentDetail.department }, |
| | | { label: '发起任务时间', value: props.currentDetail.startTime }, |
| | | { label: '工单内容', value: props.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; |
| | | }); |
| | | |
| | | // 监听 |
| | | watch( |
| | | () => props.modelValue, |
| | | (newVal) => { |
| | | if (newVal) { |
| | | loadStepInfo(); |
| | | initMap(); |
| | | } |
| | | } |
| | | ); |
| | | |
| | | watch( |
| | | () => props.currentDetail, |
| | | (newVal) => { |
| | | if (newVal && dialogVisible.value) { |
| | | loadStepInfo(); |
| | | initMap(); |
| | | } |
| | | }, |
| | | emits: [ |
| | | 'update:modelValue', |
| | | 'detail-success', |
| | | 'detail-error', |
| | | 'prev-item', |
| | | 'next-item', |
| | | 'update:currentDetail' |
| | | ], |
| | | data() { |
| | | return { |
| | | stepInfos: [], |
| | | totalTime: '', |
| | | approveLoading: false, |
| | | rejectLoading: false, |
| | | dispatchLoading: false, |
| | | completeLoading: false, |
| | | finalizeLoading: false |
| | | { deep: true } |
| | | ); |
| | | |
| | | // 方法 |
| | | const handleClose = () => { |
| | | dialogVisible.value = false; |
| | | }; |
| | | |
| | | const handlePrev = () => { |
| | | if (props.currentIndex > 0) { |
| | | emit('prev-item'); |
| | | } |
| | | }; |
| | | |
| | | const handleNext = () => { |
| | | if (props.currentIndex < props.totalItems - 1) { |
| | | emit('next-item'); |
| | | } |
| | | }; |
| | | |
| | | const loadStepInfo = async () => { |
| | | try { |
| | | const stepResponse = await getStepInfo(props.currentDetail.orderNumber); |
| | | const steps = Array.isArray(stepResponse.data.data) |
| | | ? stepResponse.data.data |
| | | : stepResponse.data.data?.steps || []; |
| | | |
| | | const finishedStep = steps.find(s => String(s.status) === '4'); |
| | | totalTime.value = finishedStep && finishedStep.total_time ? finishedStep.total_time : ''; |
| | | |
| | | stepInfos.value = steps.map(step => ({ |
| | | status: String(step.status), |
| | | name: step.name, |
| | | time: step.time, |
| | | create_time: step.create_time, |
| | | })); |
| | | } catch (error) { |
| | | stepInfos.value = []; |
| | | } |
| | | }; |
| | | |
| | | const initMap = () => { |
| | | nextTick(() => { |
| | | if (mapContainerRef.value && mapContainerRef.value.initAddEntity && props.currentDetail.location) { |
| | | mapContainerRef.value.initAddEntity('point', props.currentDetail.location); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | const mapStatus = (status) => { |
| | | const statusTextMap = { |
| | | '-1': '草稿', |
| | | 2: '待审核', |
| | | 0: '待处理', |
| | | 3: '处理中', |
| | | 4: '已完成', |
| | | }; |
| | | return statusTextMap[status] || '未知状态'; |
| | | }; |
| | | |
| | | const getStepHandler = (status) => { |
| | | const step = stepInfos.value.find(step => step.status === status); |
| | | return step ? step.name : ''; |
| | | }; |
| | | |
| | | const getStepTime = (status) => { |
| | | const step = stepInfos.value.find(step => step.status === status); |
| | | return step && step.time && step.time !== '0' ? step.time : false; |
| | | }; |
| | | |
| | | const getStepCreateTime = (status) => { |
| | | const step = stepInfos.value.find(step => step.status === status); |
| | | return step ? step.create_time : null; |
| | | }; |
| | | |
| | | const getActiveStep = () => { |
| | | const arr = props.stepStatusList; |
| | | const index = arr.indexOf(String(props.currentDetail.status)); |
| | | return index !== -1 ? index + 2 : 1; |
| | | }; |
| | | |
| | | const handleQRCode = (val) => { |
| | | emit('update:currentDetail', { |
| | | ...props.currentDetail, |
| | | showQR: !val.showQR |
| | | }); |
| | | }; |
| | | |
| | | const getThumbUrl = (url) => { |
| | | if (!url) return ''; |
| | | return getSmallImg(url); |
| | | }; |
| | | |
| | | const getPreviewUrl = (url) => { |
| | | if (!url) return ''; |
| | | return getShowImg(url); |
| | | }; |
| | | |
| | | const hasProcessingBtnPermission = () => { |
| | | return props.permission && props.permission.tickets_processing_btn === true; |
| | | }; |
| | | |
| | | const hasProcessedAndOverBtnPermission = () => { |
| | | return props.permission && props.permission.tickets_view_processedAndOver === true; |
| | | }; |
| | | |
| | | const hasReviewBtnPermission = () => { |
| | | return props.permission && props.permission.tickets_review_btn === true; |
| | | }; |
| | | |
| | | // 文件操作方法 |
| | | const handleFileChange = (file, fileList) => { |
| | | emit('update:currentDetail', { |
| | | ...props.currentDetail, |
| | | photos: fileList |
| | | }); |
| | | }; |
| | | |
| | | const handleUploadRemove = (file, fileList) => { |
| | | emit('update:currentDetail', { |
| | | ...props.currentDetail, |
| | | photos: fileList |
| | | }); |
| | | }; |
| | | |
| | | const beforeUpload = (file) => { |
| | | const isImage = file.type.includes('image'); |
| | | const isLt5M = file.size / 1024 / 1024 < 5; |
| | | |
| | | if (!isImage) { |
| | | ElMessage.error('只能上传图片文件!'); |
| | | return false; |
| | | } |
| | | if (!isLt5M) { |
| | | ElMessage.error('图片大小不能超过5MB!'); |
| | | return false; |
| | | } |
| | | return true; |
| | | }; |
| | | |
| | | // 工单操作方法 |
| | | const handleApprove = async () => { |
| | | if (approveLoading.value) return; |
| | | approveLoading.value = true; |
| | | |
| | | try { |
| | | const data = { |
| | | id: props.currentDetail.id, |
| | | status: props.currentDetail.status, |
| | | isPass: 0, |
| | | eventNum: props.currentDetail.orderNumber, |
| | | }; |
| | | }, |
| | | computed: { |
| | | dialogVisible: { |
| | | get() { |
| | | return this.modelValue; |
| | | }, |
| | | set(value) { |
| | | this.$emit('update:modelValue', value); |
| | | } |
| | | }, |
| | | isEditMode() { |
| | | return this.currentDetail.status === -1; |
| | | }, |
| | | formattedDetailFields() { |
| | | const fields = [ |
| | | { label: '工单名称', value: this.currentDetail.orderName }, |
| | | { |
| | | 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; |
| | | const response = await flowEvent(data); |
| | | if (response.data.code === 0) { |
| | | ElMessage.success('工单已通过'); |
| | | emit('detail-success'); |
| | | handleClose(); |
| | | } else { |
| | | throw new Error(response.data.msg || '操作失败'); |
| | | } |
| | | }, |
| | | watch: { |
| | | modelValue(newVal) { |
| | | if (newVal) { |
| | | this.loadStepInfo(); |
| | | this.initMap(); |
| | | } |
| | | }, |
| | | currentDetail: { |
| | | handler(newVal) { |
| | | if (newVal && this.dialogVisible) { |
| | | this.loadStepInfo(); |
| | | this.initMap(); |
| | | } |
| | | }, |
| | | deep: true |
| | | } catch (error) { |
| | | ElMessage.error(error.message || '操作失败,请稍后重试'); |
| | | emit('detail-error', error); |
| | | } finally { |
| | | approveLoading.value = false; |
| | | } |
| | | }; |
| | | |
| | | const handleReject = async () => { |
| | | if (rejectLoading.value) return; |
| | | rejectLoading.value = true; |
| | | |
| | | try { |
| | | const data = { |
| | | id: props.currentDetail.id, |
| | | status: props.currentDetail.status, |
| | | isPass: 1, |
| | | }; |
| | | |
| | | const response = await flowEvent(data); |
| | | if (response.data.code === 0) { |
| | | ElMessage.success('工单未通过'); |
| | | emit('detail-success'); |
| | | handleClose(); |
| | | } else { |
| | | throw new Error(response.data.msg || '操作失败'); |
| | | } |
| | | }, |
| | | methods: { |
| | | handleClose() { |
| | | this.dialogVisible = false; |
| | | }, |
| | | } catch (error) { |
| | | ElMessage.error(error.message || '操作失败,请稍后重试'); |
| | | emit('detail-error', error); |
| | | } finally { |
| | | rejectLoading.value = false; |
| | | } |
| | | }; |
| | | |
| | | handlePrev() { |
| | | if (this.currentIndex > 0) { |
| | | this.$emit('prev-item'); |
| | | } |
| | | }, |
| | | const handleApproveAndDispatch = () => { |
| | | // 触发派发工单操作,由父组件处理 |
| | | emit('approve-and-dispatch', props.currentDetail); |
| | | }; |
| | | |
| | | handleNext() { |
| | | if (this.currentIndex < this.totalItems - 1) { |
| | | this.$emit('next-item'); |
| | | } |
| | | }, |
| | | |
| | | async loadStepInfo() { |
| | | try { |
| | | const stepResponse = await getStepInfo(this.currentDetail.orderNumber); |
| | | const steps = Array.isArray(stepResponse.data.data) |
| | | ? stepResponse.data.data |
| | | : stepResponse.data.data?.steps || []; |
| | | |
| | | const finishedStep = steps.find(s => String(s.status) === '4'); |
| | | this.totalTime = finishedStep && finishedStep.total_time ? finishedStep.total_time : ''; |
| | | |
| | | this.stepInfos = steps.map(step => ({ |
| | | status: String(step.status), |
| | | name: step.name, |
| | | time: step.time, |
| | | create_time: step.create_time, |
| | | })); |
| | | } catch (error) { |
| | | console.error('加载步骤信息失败:', error); |
| | | this.stepInfos = []; |
| | | } |
| | | }, |
| | | |
| | | initMap() { |
| | | this.$nextTick(() => { |
| | | if (this.$refs.mapContainerRef && this.$refs.mapContainerRef.initAddEntity && this.currentDetail.location) { |
| | | this.$refs.mapContainerRef.initAddEntity('point', this.currentDetail.location); |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | mapStatus(status) { |
| | | const statusTextMap = { |
| | | '-1': '草稿', |
| | | 2: '待审核', |
| | | 0: '待处理', |
| | | 3: '处理中', |
| | | 4: '已完成', |
| | | }; |
| | | return statusTextMap[status] || '未知状态'; |
| | | }, |
| | | |
| | | 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); |
| | | 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; |
| | | console.log('stepStatusList',this.stepStatusList); |
| | | |
| | | const index = arr.indexOf(String(this.currentDetail.status)); |
| | | return index !== -1 ? index + 2 : 1; |
| | | }, |
| | | |
| | | handleQRCode(val) { |
| | | this.$emit('update:currentDetail', { |
| | | ...this.currentDetail, |
| | | showQR: !val.showQR |
| | | }); |
| | | }, |
| | | |
| | | getThumbUrl(url) { |
| | | if (!url) return ''; |
| | | return getSmallImg(url); |
| | | }, |
| | | |
| | | getPreviewUrl(url) { |
| | | if (!url) return ''; |
| | | return getShowImg(url); |
| | | }, |
| | | |
| | | hasProcessingBtnPermission() { |
| | | return this.permission && this.permission.tickets_processing_btn === true; |
| | | }, |
| | | |
| | | hasProcessedAndOverBtnPermission() { |
| | | return this.permission && this.permission.tickets_view_processedAndOver === true; |
| | | }, |
| | | |
| | | hasReviewBtnPermission() { |
| | | return this.permission && this.permission.tickets_review_btn === true; |
| | | }, |
| | | |
| | | // 文件操作方法 |
| | | handleFileChange(file, fileList) { |
| | | this.$emit('update:currentDetail', { |
| | | ...this.currentDetail, |
| | | photos: fileList |
| | | }); |
| | | }, |
| | | |
| | | handleUploadRemove(file, fileList) { |
| | | this.$emit('update:currentDetail', { |
| | | ...this.currentDetail, |
| | | photos: fileList |
| | | }); |
| | | }, |
| | | |
| | | beforeUpload(file) { |
| | | const isImage = file.type.includes('image'); |
| | | const isLt5M = file.size / 1024 / 1024 < 5; |
| | | |
| | | if (!isImage) { |
| | | this.$message.error('只能上传图片文件!'); |
| | | return false; |
| | | } |
| | | if (!isLt5M) { |
| | | this.$message.error('图片大小不能超过5MB!'); |
| | | return false; |
| | | } |
| | | return true; |
| | | }, |
| | | |
| | | // 工单操作方法 |
| | | async handleApprove() { |
| | | if (this.approveLoading) return; |
| | | this.approveLoading = true; |
| | | |
| | | try { |
| | | const data = { |
| | | id: this.currentDetail.id, |
| | | status: this.currentDetail.status, |
| | | isPass: 0, |
| | | eventNum: this.currentDetail.orderNumber, |
| | | }; |
| | | |
| | | const response = await flowEvent(data); |
| | | if (response.data.code === 0) { |
| | | this.$message.success('工单已通过'); |
| | | this.$emit('detail-success'); |
| | | this.handleClose(); |
| | | } else { |
| | | throw new Error(response.data.msg || '操作失败'); |
| | | } |
| | | } catch (error) { |
| | | this.$message.error(error.message || '操作失败,请稍后重试'); |
| | | this.$emit('detail-error', error); |
| | | } finally { |
| | | this.approveLoading = false; |
| | | } |
| | | }, |
| | | |
| | | async handleReject() { |
| | | if (this.rejectLoading) return; |
| | | this.rejectLoading = true; |
| | | |
| | | try { |
| | | const data = { |
| | | id: this.currentDetail.id, |
| | | status: this.currentDetail.status, |
| | | isPass: 1, |
| | | }; |
| | | |
| | | const response = await flowEvent(data); |
| | | if (response.data.code === 0) { |
| | | this.$message.success('工单未通过'); |
| | | this.$emit('detail-success'); |
| | | this.handleClose(); |
| | | } else { |
| | | throw new Error(response.data.msg || '操作失败'); |
| | | } |
| | | } catch (error) { |
| | | this.$message.error(error.message || '操作失败,请稍后重试'); |
| | | this.$emit('detail-error', error); |
| | | } finally { |
| | | this.rejectLoading = false; |
| | | } |
| | | }, |
| | | |
| | | handleApproveAndDispatch() { |
| | | // 触发派发工单操作,由父组件处理 |
| | | this.$emit('approve-and-dispatch', this.currentDetail); |
| | | }, |
| | | |
| | | async handleComplete() { |
| | | if (this.completeLoading) return; |
| | | this.completeLoading = true; |
| | | |
| | | try { |
| | | if (!this.currentDetail.processingDetail) { |
| | | this.$message.warning('请先填写事件处理详情'); |
| | | return; |
| | | } |
| | | |
| | | if (!this.currentDetail.photos || this.currentDetail.photos.length === 0) { |
| | | this.$message.warning('请选择上传图片'); |
| | | this.completeLoading = false; |
| | | return; |
| | | } |
| | | |
| | | const data = { |
| | | id: this.currentDetail.id, |
| | | status: this.currentDetail.status, |
| | | processingDetails: this.currentDetail.processingDetail, |
| | | eventNum: this.currentDetail.orderNumber, |
| | | }; |
| | | |
| | | const file = this.currentDetail.photos?.[0]?.raw || null; |
| | | |
| | | const response = await flowEvent(data, file); |
| | | |
| | | if (response.data.code === 0) { |
| | | this.$message.success('工单已完成'); |
| | | this.$emit('detail-success'); |
| | | this.handleClose(); |
| | | } else { |
| | | throw new Error(response.data.msg || '操作失败'); |
| | | } |
| | | } catch (error) { |
| | | console.error('完成工单失败:', error); |
| | | this.$message.error(error.message || '操作失败,请稍后重试'); |
| | | this.$emit('detail-error', error); |
| | | } finally { |
| | | this.completeLoading = false; |
| | | } |
| | | const handleComplete = async () => { |
| | | if (completeLoading.value) return; |
| | | completeLoading.value = true; |
| | | |
| | | try { |
| | | if (!props.currentDetail.processingDetail) { |
| | | ElMessage.warning('请先填写事件处理详情'); |
| | | return; |
| | | } |
| | | |
| | | if (!props.currentDetail.photos || props.currentDetail.photos.length === 0) { |
| | | ElMessage.warning('请选择上传图片'); |
| | | completeLoading.value = false; |
| | | return; |
| | | } |
| | | |
| | | const data = { |
| | | id: props.currentDetail.id, |
| | | status: props.currentDetail.status, |
| | | processingDetails: props.currentDetail.processingDetail, |
| | | eventNum: props.currentDetail.orderNumber, |
| | | }; |
| | | |
| | | const file = props.currentDetail.photos?.[0]?.raw || null; |
| | | |
| | | const response = await flowEvent(data, file); |
| | | |
| | | if (response.data.code === 0) { |
| | | ElMessage.success('工单已完成'); |
| | | emit('detail-success'); |
| | | handleClose(); |
| | | } else { |
| | | throw new Error(response.data.msg || '操作失败'); |
| | | } |
| | | handleClose(); |
| | | } catch (error) { |
| | | console.error('完成工单失败:', error); |
| | | ElMessage.error(error.message || '操作失败,请稍后重试'); |
| | | emit('detail-error', error); |
| | | handleClose(); |
| | | } finally { |
| | | completeLoading.value = false; |
| | | handleClose(); |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | .custom-dialog { |
| | | max-height: 95vh; |
| | | |
| | | :deep(.el-dialog__body) { |
| | | max-height: 96vh; |
| | | transition: max-height 0.3s ease-out; |
| | | .el-dialog__body { |
| | | border-top: 0.1rem solid #f0f0f0; |
| | | } |
| | | } |
| | | |
| | | .custom-qrcode-popover { |
| | | min-width: 160px !important; |
| | | min-height: 140px !important; |
| | | position: relative; |
| | | |
| | | .qrcode-content { |
| | | margin-top: 8px; |
| | | |
| | | .close-btn { |
| | | position: absolute; |
| | | top: 2px; |
| | | right: 2px; |
| | | width: 16px; |
| | | height: 16px; |
| | | line-height: 16px; |
| | | text-align: center; |
| | | cursor: pointer; |
| | | color: #999; |
| | | font-size: 16px; |
| | | z-index: 10; |
| | | } |
| | | } |
| | | } |
| | | </style> |
| | | <style lang="scss" scoped> |
| | | .uploadImg { |
| | | margin-bottom: 32px; |
| | | } |
| | | |
| | | .tableCss { |
| | | width: 100%; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .step-timer { |
| | | position: absolute; |
| | |
| | | color: #666; |
| | | font-size: 12px; |
| | | } |
| | | |
| | | .event-total-time { |
| | | text-align: center; |
| | | color: #666; |
| | | font-size: 15px; |
| | | margin-bottom: 12px; |
| | | } |
| | | |
| | | |
| | | .el-dialog { |
| | | .el-form-item { |
| | | margin-bottom: 20px; |
| | | } |
| | | } |
| | | |
| | | .el-upload { |
| | | width: 100%; |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | |
| | | :deep(.el-upload-list__item) { |
| | | transition: all 0.3s ease; |
| | | } |
| | | |
| | | :deep(.el-upload-list__item:hover) { |
| | | background-color: #f5f7fa; |
| | | } |
| | | } |
| | | |
| | | .el-upload__tip { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | margin-top: 12px; |
| | | } |
| | | |
| | | :deep(.el-dialog__body) { |
| | | border-top: 0.1rem solid #f0f0f0; |
| | | } |
| | | |
| | | |
| | | .dialog-footer1-new { |
| | | position: sticky; |
| | | bottom: 28px; |
| | | left: 0; |
| | | right: 0; |
| | | background: white; |
| | | z-index: 10; |
| | | padding: 16px 20px; |
| | | border-top: 1px solid #ebeef5; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | |
| | | .el-button + .el-button { |
| | | margin-left: 12px; |
| | | } |
| | | |
| | | .btngroups { |
| | | margin-left: 12px; |
| | | } |
| | | |
| | | .el-button { |
| | | padding: 9px 20px; |
| | | |
| | | &:last-child { |
| | | margin-left: 12px; |
| | | margin-right: 12px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .map-container { |
| | | width: 100%; |
| | | height: 400px; |
| | | margin-bottom: 15px; |
| | | border: 1px solid #dcdfe6; |
| | | border-radius: 4px; |
| | | overflow: hidden; |
| | | |
| | | :deep(.el-input-map) { |
| | | height: 100%; |
| | | } |
| | | } |
| | | |
| | | |
| | | .map-select { |
| | | display: flex; |
| | | align-items: flex-start; |
| | | gap: 15px; |
| | | |
| | | .selected-location { |
| | | flex: 1; |
| | | padding: 5px 10px; |
| | | background-color: #f5f7fa; |
| | | border-radius: 4px; |
| | | |
| | | p { |
| | | margin: 5px 0; |
| | | color: #606266; |
| | | font-size: 14px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .preview-image { |
| | | border-radius: 4px; |
| | | overflow: hidden; |
| | | background-color: #f5f7fa; |
| | | } |
| | | |
| | | .image-placeholder, |
| | | .image-error, |
| | | .no-media { |
| | | height: 200px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | color: #909399; |
| | | background-color: #f5f7fa; |
| | | border-radius: 4px; |
| | | |
| | | i { |
| | | font-size: 32px; |
| | | margin-bottom: 8px; |
| | | } |
| | | } |
| | | |
| | | .no-media { |
| | | border: 1px dashed #d9d9d9; |
| | | } |
| | | |
| | | .detail-container { |
| | | padding: 0 20px; |
| | | |
| | |
| | | } |
| | | } |
| | | } |
| | | .event-title-center { |
| | | text-align: center; |
| | | font-size: 20px; |
| | | font-weight: bold; |
| | | margin-bottom: 5px; |
| | | color: #333; |
| | | } |
| | | .event-total-time { |
| | | text-align: center; |
| | | color: #666; |
| | | font-size: 15px; |
| | | margin-bottom: 12px; |
| | | } |
| | | .custom-steps-container { |
| | | width: 100%; |
| | | margin: 10px 0; |
| | | } |
| | | |
| | | .steps-titles { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 14px; |
| | | position: relative; |
| | | } |
| | | |
| | | .step-title { |
| | | text-align: center; |
| | | flex: 1; |
| | | font-size: 14px; |
| | | color: #999; |
| | | position: relative; |
| | | padding-bottom: 5px; |
| | | } |
| | | |
| | | .step-title.active { |
| | | color: #409eff; |
| | | font-weight: bold; |
| | | } |
| | | .status-flow { |
| | | margin-bottom: 20px; |
| | | |
| | |
| | | } |
| | | } |
| | | } |
| | | .step-description { |
| | | font-size: 14px; |
| | | color: #666; |
| | | line-height: 1.5; |
| | | display: block; |
| | | text-align: center; |
| | | |
| | | &:first-child { |
| | | font-weight: bold; |
| | | margin-bottom: 4px; |
| | | } |
| | | .basic-info { |
| | | margin-bottom: 20px; |
| | | } |
| | | .PopUpTableScrolls { |
| | | max-height: 600px; |
| | | overflow-y: scroll; |
| | | overflow-x: hidden; |
| | | } |
| | | .tableCss { |
| | | width: 100%; |
| | | margin-bottom: 10px; |
| | | } |
| | | /* 必填项样式 */ |
| | | .required-label { |
| | | position: relative; |
| | | display: inline-block; |
| | | |
| | | .required-star { |
| | | color: #f56c6c; |
| | | margin-right: 4px; |
| | | } |
| | | } |
| | | /* 必填输入框样式 */ |
| | | .required-input { |
| | | width: 100%; |
| | | |
| | | :deep(.el-input__inner), |
| | | :deep(.el-textarea__inner) { |
| | | &:focus { |
| | | border-color: #409eff; |
| | | } |
| | | } |
| | | } |
| | | /* 新建工单和处理工单的上传组件样式 */ |
| | | .create-upload, |
| | | .detail-upload { |
| | | :deep(.el-upload--picture-card) { |
| | | width: 120px; |
| | | height: 100px; |
| | | line-height: 100px; |
| | | } |
| | | |
| | | /* 隐藏额外的上传按钮 */ |
| | | :deep(.el-upload.el-upload--picture-card) { |
| | | display: none; |
| | | } |
| | | |
| | | /* 当没有图片时显示上传按钮 */ |
| | | :deep(.el-upload.el-upload--picture-card:first-child) { |
| | | display: flex; |
| | | } |
| | | |
| | | /* 上传组件的预览图片样式 */ |
| | | :deep(.el-upload-list--picture-card .el-upload-list__item) { |
| | | width: 120px; |
| | | height: 100px; |
| | | } |
| | | } |
| | | .el-upload__tip { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | margin-top: 12px; |
| | | } |
| | | .media-section { |
| | | // margin-bottom: 20px; |
| | | |
| | |
| | | align-items: center; |
| | | } |
| | | } |
| | | |
| | | .leftBtn { |
| | | width: 70px; |
| | | height: 32px; |
| | | background-color: #999; |
| | | border-radius: 5px; |
| | | text-align: center; |
| | | line-height: 32px; |
| | | color: #fff; |
| | | cursor: pointer; |
| | | opacity: 0.8; |
| | | } |
| | | |
| | | .disableds { |
| | | background: #999 !important; |
| | | cursor: not-allowed !important; |
| | | pointer-events: none; |
| | | opacity: 0.3 !important; |
| | | } |
| | | |
| | | .PopUpTableScrolls { |
| | | max-height: 600px; |
| | | overflow-y: scroll; |
| | | overflow-x: hidden; |
| | | } |
| | | |
| | | .media-box { |
| | | width: 100%; |
| | | border: 1px solid #dcdfe6; |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | .image-placeholder, |
| | | .image-error, |
| | | .no-media { |
| | | height: 200px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | justify-content: center; |
| | | height: 100%; |
| | | color: #909399; |
| | | background-color: #f5f7fa; |
| | | border-radius: 4px; |
| | | } |
| | | |
| | | i { |
| | | font-size: 32px; |
| | | margin-bottom: 8px; |
| | | } |
| | | .image-placeholder i, |
| | | .image-error i { |
| | | font-size: 32px; |
| | | margin-bottom: 8px; |
| | | } |
| | | |
| | | .no-media { |
| | | border: 1px dashed #d9d9d9; |
| | | } |
| | | .dialog-footer1-new { |
| | | position: sticky; |
| | | bottom: 28px; |
| | | left: 0; |
| | | right: 0; |
| | | background: white; |
| | | z-index: 10; |
| | | padding: 16px 20px; |
| | | border-top: 1px solid #ebeef5; |
| | | |
| | | .info-table { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | .info-item { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | |
| | | .el-button + .el-button { |
| | | margin-left: 12px; |
| | | } |
| | | |
| | | .btngroups { |
| | | margin-left: 12px; |
| | | } |
| | | |
| | | .el-button { |
| | | padding: 9px 20px; |
| | | |
| | | &:last-child { |
| | | margin-left: 12px; |
| | | margin-right: 12px; |
| | | } |
| | | } |
| | | } |
| | | .leftBtn { |
| | | width: 70px; |
| | | height: 32px; |
| | | background-color: #999; |
| | | border-radius: 5px; |
| | | text-align: center; |
| | | line-height: 32px; |
| | | color: #fff; |
| | | cursor: pointer; |
| | | opacity: 0.8; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .disableds { |
| | | background: #999 !important; |
| | | cursor: not-allowed !important; |
| | | pointer-events: none; |
| | | opacity: 0.3 !important; |
| | | .info-label { |
| | | font-weight: bold; |
| | | width: 120px; |
| | | color: #606266; |
| | | } |
| | | |
| | | .info-value { |
| | | flex: 1; |
| | | color: #303133; |
| | | word-break: break-word; |
| | | } |
| | | |
| | | .readonly-processing-detail { |
| | | background-color: #f5f7fa; |
| | | padding: 12px; |
| | |
| | | margin-bottom: 4px; |
| | | } |
| | | } |
| | | </style> |
| | | |
| | | // 添加删除按钮样式 |
| | | .danger-button { |
| | | color: #f56c6c; |
| | | } |
| | | |
| | | .danger-button:hover { |
| | | color: #f78989; |
| | | } |
| | | |
| | | .custom-steps-container { |
| | | width: 100%; |
| | | margin: 10px 0; |
| | | } |
| | | |
| | | .steps-titles { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | margin-bottom: 14px; |
| | | position: relative; |
| | | } |
| | | |
| | | .step-title { |
| | | text-align: center; |
| | | flex: 1; |
| | | font-size: 14px; |
| | | color: #999; |
| | | position: relative; |
| | | padding-bottom: 5px; |
| | | } |
| | | |
| | | .step-title.active { |
| | | color: #409eff; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .event-title-center { |
| | | text-align: center; |
| | | font-size: 20px; |
| | | font-weight: bold; |
| | | margin-bottom: 5px; |
| | | color: #333; |
| | | } |
| | | |
| | | .custom-steps { |
| | | margin-top: -20px; |
| | | |
| | | :deep(.el-step__description) { |
| | | margin-top: 8px; |
| | | padding: 0 20px; |
| | | } |
| | | } |
| | | |
| | | .step-description { |
| | | font-size: 14px; |
| | | color: #666; |
| | | line-height: 1.5; |
| | | display: block; |
| | | text-align: center; |
| | | |
| | | &:first-child { |
| | | font-weight: bold; |
| | | margin-bottom: 4px; |
| | | } |
| | | } |
| | | |
| | | // 覆盖其他相关样式 |
| | | .status-flow { |
| | | .custom-steps { |
| | | .el-step__description { |
| | | position: relative; |
| | | margin: 0; |
| | | padding: 0; |
| | | } |
| | | |
| | | // 移除之前的样式 |
| | | .init-step-info, |
| | | .step-info { |
| | | display: block; |
| | | width: auto; |
| | | text-align: center; |
| | | } |
| | | } |
| | | } |
| | | |
| | | /* 新建工单和处理工单的上传组件样式 */ |
| | | .create-upload, |
| | | .detail-upload { |
| | | :deep(.el-upload--picture-card) { |
| | | width: 120px; |
| | | height: 100px; |
| | | line-height: 100px; |
| | | } |
| | | |
| | | /* 隐藏额外的上传按钮 */ |
| | | :deep(.el-upload.el-upload--picture-card) { |
| | | display: none; |
| | | } |
| | | |
| | | /* 当没有图片时显示上传按钮 */ |
| | | :deep(.el-upload.el-upload--picture-card:first-child) { |
| | | display: flex; |
| | | } |
| | | |
| | | /* 上传组件的预览图片样式 */ |
| | | :deep(.el-upload-list--picture-card .el-upload-list__item) { |
| | | width: 120px; |
| | | height: 100px; |
| | | } |
| | | } |
| | | |
| | | /* 原有图片预览的样式 */ |
| | | .el-image-viewer__wrapper { |
| | | :deep(.el-image-viewer__img) { |
| | | max-width: 100%; |
| | | max-height: 100%; |
| | | object-fit: contain; |
| | | } |
| | | } |
| | | /* 必填项样式 */ |
| | | .required-label { |
| | | position: relative; |
| | | display: inline-block; |
| | | |
| | | .required-star { |
| | | color: #f56c6c; |
| | | margin-right: 4px; |
| | | } |
| | | } |
| | | /* 必填输入框样式 */ |
| | | .required-input { |
| | | width: 100%; |
| | | |
| | | :deep(.el-input__inner), |
| | | :deep(.el-textarea__inner) { |
| | | &:focus { |
| | | border-color: #409eff; |
| | | } |
| | | } |
| | | } |
| | | </style> |