| | |
| | | </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 |
| | |
| | | data() { |
| | | return { |
| | | currentIndex: null, // 当前显示的数据索引 |
| | | |
| | | |
| | | |
| | | approveLoading: false, |
| | | rejectLoading: false, |
| | | dispatchLoading: false, |
| | | completeLoading: false, |
| | | finalizeLoading: false, |
| | | createoredit: '', |
| | | activeTab: 'all', |
| | | // tabs 只保留静态结构,不做权限判断 |
| | |
| | | }, |
| | | dialogVisible: false, |
| | | editFormData: null, // 专门用于存储编辑数据的对象 |
| | | |
| | | createoredit: '', // 可以删除这个字段,因为组件内部会判断 |
| | | detailVisible: false, |
| | | currentDetail: {}, |
| | | |
| | |
| | | dispatchDepartment: '', // 新增:派发部门 |
| | | dispatchHandler: '', // 新增:派发处理人 |
| | | dispatchDialogVisible: false, // 新增:派发对话框可见性 |
| | | |
| | | stepInfos: [], // 新增:存储步骤信息 |
| | | fixedStatuses: ['2', '0', '3', '4'], // 固定的五个状态 |
| | | userNameToIdMap: {}, // 新增用户名到ID的映射 |
| | | workType: 0, // 新增:当前工单work_type |
| | |
| | | reviewDialogVisible: false, // 新增:审核对话框可见性 |
| | | currentReviewImage: '', // 新增:当前审核图片 |
| | | currentImageIndex: 1, // 新增:当前图片索引 |
| | | |
| | | totalTime: '', |
| | | isShowInfo: false, |
| | | |
| | | // 配置时间选择器默认配置 |
| | |
| | | }); |
| | | } |
| | | }, |
| | | // 上一项 |
| | | handlePrevItem() { |
| | | this.currentIndex = Math.max(0, this.currentIndex - 1); |
| | | this.updateCurrentDetail(); |
| | | }, |
| | | |
| | | // 下一项 |
| | | handleNextItem() { |
| | | this.currentIndex = Math.min(this.tableData.length - 1, this.currentIndex + 1); |
| | | this.updateCurrentDetail(); |
| | | }, |
| | | |
| | | // 更新当前详情 |
| | | updateCurrentDetail() { |
| | | |
| | | updateCurrentDetail() { |
| | | this.currentDetail = this.tableData[this.currentIndex]; |
| | | this.currentDetail.mediaUrl = this.currentDetail.photo_url; |
| | | this.currentDetail.updatePhotoUrl = this.currentDetail.update_photo_url; |
| | |
| | | this.currentDetail.showQR = false; |
| | | this.currentDetail.latAndLon = _.round(this.currentDetail.location[0], 6) + ',' + _.round(this.currentDetail.location[1], 6); |
| | | }, |
| | | // 更新当前详情数据 |
| | | |
| | | // 更新当前详情数据 |
| | | handleCurrentDetailUpdate(updatedDetail) { |
| | | this.currentDetail = updatedDetail; |
| | | }, |
| | | |
| | | // 受理并派发 |
| | | handleApproveAndDispatch(detail) { |
| | | this.currentDetail = detail; |
| | |
| | | this.$message.error('地图加载失败,请检查网络或API Key配置'); |
| | | } |
| | | }, |
| | | async handleBatchApprove() { |
| | | try { |
| | | if (this.selections.length === 0) { |
| | | this.$message.warning('没有选中的工单'); |
| | | return; |
| | | } |
| | | |
| | | const currentItem = this.selections[this.currentImageIndex - 1]; |
| | | if (!currentItem) { |
| | | this.$message.warning('当前工单数据无效'); |
| | | return; |
| | | } |
| | | |
| | | await this.$confirm('确认审核通过当前工单?', '提示', { |
| | | confirmButtonText: '确定', |
| | | cancelButtonText: '取消', |
| | | type: 'warning', |
| | | }); |
| | | |
| | | const data = { |
| | | id: currentItem.id, |
| | | status: currentItem.status, |
| | | isPass: 0, |
| | | eventNum: currentItem.orderNumber, |
| | | eventName: currentItem.orderName, |
| | | }; |
| | | |
| | | const response = await flowEvent(data); |
| | | if (response.data.code === 0) { |
| | | this.$message.success('工单审核通过'); |
| | | |
| | | // 创建新的数组而不是修改原数组 |
| | | const newSelections = [...this.selections]; |
| | | newSelections.splice(this.currentImageIndex - 1, 1); |
| | | this.selections = newSelections; |
| | | |
| | | // 修正索引并更新图片 |
| | | if (this.selections.length > 0) { |
| | | if (this.currentImageIndex > this.selections.length) { |
| | | this.currentImageIndex = this.selections.length; |
| | | } |
| | | this.updateCurrentReviewImage(); |
| | | } else { |
| | | this.reviewDialogVisible = false; |
| | | this.currentImageIndex = 1; |
| | | this.currentReviewImage = ''; |
| | | } |
| | | |
| | | // 刷新表格数据 |
| | | this.fetchTableData(); |
| | | } else { |
| | | throw new Error(response.data.msg || '审核失败'); |
| | | } |
| | | } catch (error) { |
| | | if (error === 'cancel') return; |
| | | this.$message.error(error.message || '审核失败,请稍后重试'); |
| | | } |
| | | }, |
| | | async fetchDropdownData() { |
| | | try { |
| | | const response = await getTicketInfo(); |
| | |
| | | } |
| | | return `${location[0].toFixed(6)}, ${location[1].toFixed(6)}`; |
| | | }, |
| | | handleViewDetail(row) { |
| | | |
| | | async handleViewDetail(row) { |
| | | // 找到当前行在tableData中的索引 |
| | | this.currentIndex = this.tableData.findIndex(item => item.id === row.id); |
| | | // 先设置workType,直接从row读取 |
| | | this.workType = row.work_type !== undefined ? Number(row.work_type) : 0; |
| | | |
| | | |
| | | // 重置上传组件的文件列表 |
| | | this.$nextTick(() => { |
| | | if (this.$refs.MapContainer && this.$refs.MapContainer.initAddEntity) { |
| | | this.$refs.MapContainer.initAddEntity('point', this.currentDetail.location); |
| | | } |
| | | }); |
| | | |
| | | const detailData = { |
| | | ...row, |
| | | processingDetail: row.processing_details || '', |
| | | mediaUrl: row.photo_url || row.video_url || '', |
| | | updatePhotoUrl: row.update_photo_url || '', |
| | | photos: [], |
| | | job_name: row.job_name || '', |
| | | showQR: false, |
| | | latAndLon: _.round(row.location[0], 6) + ',' + _.round(row.location[1], 6), |
| | | job_name: row.job_name || '', // 新增 |
| | | }; |
| | | |
| | | this.currentDetail = detailData; |
| | | let stepArr = []; |
| | | try { |
| | | const stepResponse = await getStepInfo(row.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 : ''; |
| | | if (this.activeTab !== 'myTickets') { |
| | | this.stepInfos = steps.map(step => ({ |
| | | status: String(step.status), |
| | | name: step.name, |
| | | time: step.time, |
| | | create_time: step.create_time, |
| | | })); |
| | | } else { |
| | | const statusArr = this.workType === 1 ? ['3', '4'] : this.fixedStatuses; |
| | | this.stepInfos = statusArr.map(status => { |
| | | const step = steps.find(s => String(s.status) === String(status)); |
| | | return { |
| | | status, |
| | | name: step ? step.name : '', |
| | | time: step ? step.time : null, |
| | | create_time: step ? step.create_time : null, |
| | | }; |
| | | }); |
| | | console.log('this.stepInfos', this.stepInfos); |
| | | |
| | | } |
| | | this.currentDetail.status = row.status; |
| | | } catch (error) { |
| | | if (this.activeTab === 'myTickets') { |
| | | const statusArr = this.workType === 1 ? ['3', '4'] : this.fixedStatuses; |
| | | this.stepInfos = statusArr.map(status => ({ |
| | | status, |
| | | name: status === row.status ? row.handler || '未分配' : '未处理', |
| | | time: status === row.status ? row.startTime || '未知时间' : null, |
| | | })); |
| | | } else { |
| | | this.stepInfos = []; |
| | | } |
| | | } |
| | | console.log(detailData, 'detailDatadetailDatadetailData'); |
| | | this.currentDetail = { |
| | | ...detailData, |
| | | // address: null, |
| | | showQR: false, |
| | | latAndLon: _.round(detailData.location[0], 6) + ',' + _.round(detailData.location[1], 6), |
| | | }; |
| | | |
| | | console.log('this.currentDetail', this.currentDetail); |
| | | |
| | | this.detailVisible = true; |
| | | |
| | | |
| | | |
| | | this.$nextTick(() => { |
| | | if (this.$refs.MapContainer && this.$refs.MapContainer.initAddEntity) { |
| | | this.$refs.MapContainer.initAddEntity('point', this.currentDetail.location); |
| | | } |
| | | }); |
| | | }, |
| | | // 详情成功回调 |
| | | // 详情成功回调 |
| | | handleDetailSuccess() { |
| | | this.fetchTableData(); |
| | | }, |
| | |
| | | handleDetailError(error) { |
| | | console.error('工单操作失败:', error); |
| | | }, |
| | | // 上一项 |
| | | handlePrevItem() { |
| | | this.currentIndex = Math.max(0, this.currentIndex - 1); |
| | | this.updateCurrentDetail(); |
| | | }, |
| | | |
| | | // 下一项 |
| | | handleNextItem() { |
| | | this.currentIndex = Math.min(this.tableData.length - 1, this.currentIndex + 1); |
| | | this.updateCurrentDetail(); |
| | | }, |
| | | |
| | | |
| | | |
| | | getStepHandler(status) { |
| | | const step = this.stepInfos.find(step => step.status === status); |
| | | return step ? step.name : ''; |
| | |
| | | 工单状态: 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('只有处理中状态的工单可以提交处理详情'); |
| | |
| | | markAsCompleted() { |
| | | this.$message.success('工单已标记为完成'); |
| | | }, |
| | | |
| | | |
| | | async approveAndDispatch() { |
| | | if (this.dispatchLoading) return; |
| | | |
| | |
| | | // 可以在这里处理错误,或者保持空实现 |
| | | console.error('派发失败:', error); |
| | | }, |
| | | |
| | | |
| | | |
| | | |
| | | hasProcessingBtnPermission() { |
| | | // undefined 或 false 都返回 false,只有 true 返回 true |
| | | // console.log('权限检查:', this.permission.tickets_processing_btn) |
| | |
| | | // console.log('权限检查:', this.permission) |
| | | return this.permission && this.permission.tickets_review_btn === true; |
| | | }, |
| | | |
| | | |
| | | async finalizeTicket() { |
| | | if (this.finalizeLoading) return; |
| | | this.finalizeLoading = true; |
| | |
| | | // 添加全选方法 |
| | | handleSelectAll(val) { |
| | | this.$refs.avueCrud.toggleSelection(val); |
| | | }, |
| | | |
| | | // 添加单行选择方法 |
| | | handleSelect(selection, row) { |
| | | console.log('选中行变化:', selection, row); |
| | | }, |
| | | |
| | | // 如果需要手动选中某些行 |
| | |
| | | margin-bottom: 32px; |
| | | } |
| | | |
| | | |
| | | .tableCss { |
| | | width: 100%; |
| | | margin-bottom: 10px; |
| | | } |
| | | |
| | | .step-timer { |
| | | position: absolute; |
| | |
| | | font-size: 12px; |
| | | } |
| | | |
| | | |
| | | .event-total-time { |
| | | text-align: center; |
| | | color: #666; |
| | | font-size: 15px; |
| | | margin-bottom: 12px; |
| | | } |
| | | |
| | | .action-bar { |
| | | margin-bottom: 16px; |
| | |
| | | margin-top: 12px; |
| | | } |
| | | |
| | | .create-ticket-form { |
| | | padding: 20px 10px; |
| | | |
| | | .form-section { |
| | | background-color: #fff; |
| | | border-radius: 4px; |
| | | |
| | | .el-row { |
| | | margin-bottom: 16px; |
| | | |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .location-wrapper { |
| | | width: 100%; // 修改为100%以适应父容器 |
| | | height: auto; // 修改为auto以自适应内容 |
| | | |
| | | .map-button { |
| | | width: 100%; // 让按钮填满容器宽度 |
| | | height: 36px; // 与其他输入框保持一致的高度 |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | |
| | | i { |
| | | margin-right: 4px; |
| | | } |
| | | } |
| | | |
| | | .location-text { |
| | | margin-top: 8px; |
| | | padding: 8px 12px; |
| | | background-color: #f5f7fa; |
| | | border-radius: 4px; |
| | | color: #606266; |
| | | font-size: 13px; |
| | | line-height: 1.4; |
| | | } |
| | | } |
| | | |
| | | .upload-wrapper { |
| | | .uploader { |
| | | :deep(.el-upload--picture-card) { |
| | | width: 120px; |
| | | height: 100px; |
| | | line-height: 128px; |
| | | } |
| | | |
| | | :deep(.el-upload-list__item) { |
| | | width: 120px; |
| | | height: 120px; |
| | | } |
| | | } |
| | | |
| | | .upload-tip { |
| | | font-size: 12px; |
| | | color: #909399; |
| | | line-height: 1.4; |
| | | margin-top: 8px; |
| | | } |
| | | } |
| | | |
| | | .el-form-item { |
| | | margin-bottom: 18px; |
| | | |
| | | &:last-child { |
| | | margin-bottom: 0; |
| | | } |
| | | } |
| | | |
| | | :deep(.el-form-item__label) { |
| | | font-weight: 500; |
| | | color: #606266; |
| | | } |
| | | |
| | | // :deep(.el-input__inner) { |
| | | // height: 36px; |
| | | // line-height: 36px; |
| | | // } |
| | | |
| | | :deep(.el-textarea__inner) { |
| | | padding: 8px 12px; |
| | | } |
| | | |
| | | // :deep(.el-input__inner), |
| | | // :deep(.el-select), |
| | | // :deep(.el-select .el-input) { |
| | | // width: 100%; // 确保所有输入框和选择框宽度一致 |
| | | // } |
| | | |
| | | .full-width { |
| | | width: 100%; |
| | | } |
| | | } |
| | | |
| | | :deep(.el-dialog__body) { |
| | | border-top: 0.1rem solid #f0f0f0; |
| | |
| | | } |
| | | } |
| | | |
| | | // .status-flow { |
| | | // margin-bottom: 20px; |
| | | .status-flow { |
| | | margin-bottom: 20px; |
| | | |
| | | // .custom-steps { |
| | | // .el-step__description { |
| | | // position: relative; |
| | | // margin: 0; |
| | | // padding: 0; |
| | | // } |
| | | .custom-steps { |
| | | .el-step__description { |
| | | position: relative; |
| | | margin: 0; |
| | | padding: 0; |
| | | } |
| | | |
| | | // // 添加发起任务步骤的特殊样式 |
| | | // .init-step-info { |
| | | // display: flex; |
| | | // flex-direction: column; |
| | | // align-items: center; |
| | | // text-align: center; |
| | | // width: 130px; |
| | | // 添加发起任务步骤的特殊样式 |
| | | .init-step-info { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | text-align: center; |
| | | width: 130px; |
| | | |
| | | // .creator-name { |
| | | // font-size: 14px; |
| | | // font-weight: bold; |
| | | // color: #303133; |
| | | // margin-bottom: 4px; |
| | | // } |
| | | .creator-name { |
| | | font-size: 14px; |
| | | font-weight: bold; |
| | | color: #303133; |
| | | margin-bottom: 4px; |
| | | } |
| | | |
| | | // .create-time { |
| | | // font-size: 10px; |
| | | // color: #909399; |
| | | // } |
| | | // } |
| | | .create-time { |
| | | font-size: 10px; |
| | | color: #909399; |
| | | } |
| | | } |
| | | |
| | | // // 保持其他步骤的原有样式 |
| | | // .step-info { |
| | | // display: flex; |
| | | // margin: 0; |
| | | // padding: 0; |
| | | // justify-content: space-between; |
| | | // align-items: center; |
| | | // width: 200px; |
| | | // 保持其他步骤的原有样式 |
| | | .step-info { |
| | | display: flex; |
| | | margin: 0; |
| | | padding: 0; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | width: 200px; |
| | | |
| | | // .process-time { |
| | | // font-size: 10px; |
| | | // color: #909399; |
| | | // text-align: left; |
| | | // margin-left: -50%; |
| | | // padding-left: 0%; |
| | | // flex: 1; |
| | | // } |
| | | .process-time { |
| | | font-size: 10px; |
| | | color: #909399; |
| | | text-align: left; |
| | | margin-left: -50%; |
| | | padding-left: 0%; |
| | | flex: 1; |
| | | } |
| | | |
| | | // .handler-name { |
| | | // font-size: 14px; |
| | | // font-weight: bold; |
| | | // color: #303133; |
| | | // text-align: left; |
| | | // flex: 1; |
| | | // } |
| | | // } |
| | | // } |
| | | // } |
| | | .handler-name { |
| | | font-size: 14px; |
| | | font-weight: bold; |
| | | color: #303133; |
| | | text-align: left; |
| | | flex: 1; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .basic-info { |
| | | margin-bottom: 20px; |
| | |
| | | opacity: 0.3 !important; |
| | | } |
| | | |
| | | |
| | | .PopUpTableScrolls { |
| | | max-height: 600px; |
| | | overflow-y: scroll; |
| | | overflow-x: hidden; |
| | | } |
| | | |
| | | .media-box { |
| | | width: 100%; |
| | |
| | | word-break: break-word; |
| | | } |
| | | |
| | | .readonly-processing-detail { |
| | | background-color: #f5f7fa; |
| | | padding: 12px; |
| | | border-radius: 4px; |
| | | min-height: 30px; |
| | | color: #606266; |
| | | line-height: 1.5; |
| | | display: block; |
| | | text-align: left; |
| | | margin-bottom: 5px; |
| | | |
| | | &:first-child { |
| | | font-weight: bold; |
| | | margin-bottom: 4px; |
| | | } |
| | | } |
| | | |
| | | // 添加删除按钮样式 |
| | | .danger-button { |
| | |
| | | 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; |
| | |
| | | } |
| | | } |
| | | |
| | | .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 { |