| | |
| | | <el-option v-for="item in types" :key="item.value" :label="item.label" :value="item.value" /> |
| | | </el-select> |
| | | <el-date-picker v-model="filters.dateRange" type="daterange" class="filter-item" range-separator="至" |
| | | start-placeholder="开始日期" end-placeholder="结束日期"> |
| | | start-placeholder="开始日期" end-placeholder="结束日期" :default-value="datePickerDefaultVal"> |
| | | </el-date-picker> |
| | | <el-select v-model="filters.status" placeholder="请选择状态" class="filter-item" clearable> |
| | | <el-option v-for="item in statuses" :key="item.value" :label="item.label" :value="item.value" /> |
| | |
| | | <el-option v-for="item in algorithms" :key="item.dict_key" :label="item.dict_value" |
| | | :value="item.dict_key" /> |
| | | </el-select> |
| | | <el-button type="primary" icon="el-icon-search" @click="handleSearch">查询</el-button> |
| | | <el-button icon="el-icon-refresh" @click="handleReset">重置</el-button> |
| | | <el-select v-model="filters.isReview" placeholder="请选择复核状态" class="filter-item" clearable> |
| | | <el-option v-for="item in reviewStatuses" :key="item.value" :label="item.label" :value="item.value" /> |
| | | </el-select> |
| | | <el-button type="primary" icon="el-icon-search" @click="handleSearch">搜索</el-button> |
| | | <el-button icon="el-icon-refresh" @click="handleReset">清空</el-button> |
| | | </div> |
| | | |
| | | <!-- 表格部分 --> |
| | |
| | | <el-button type="text" icon="el-icon-delete" class="danger-button" |
| | | @click="handleDelete(row)">删除</el-button> |
| | | </template> |
| | | |
| | | <template v-else> |
| | | <el-button type="text" icon="el-icon-view" @click="handleViewDetail(row)">详情</el-button> |
| | | </template> |
| | | |
| | | <el-button v-if="row.status === 4 && row.isReview !== 1" type="text" icon="el-icon-check" |
| | | @click="reCheck(row)">复核</el-button> |
| | | </template> |
| | | <template #status="{ row }"> |
| | | <span :style="getStatusTagType(row.status) ? 'color:' + getStatusTagType(row.status) : ''"> |
| | |
| | | </template> |
| | | <template #keyData="{ row }"> |
| | | <span>{{ row.address }}</span> |
| | | </template> |
| | | |
| | | <template #isReview="{ row }"> |
| | | <span>{{ showIsReviewText(row) }}</span> |
| | | </template> |
| | | </avue-crud> |
| | | </div> |
| | |
| | | </div> |
| | | |
| | | <!-- 上传图片 --> |
| | | <div v-if="[3, 4].includes(currentDetail.status)" class="form-section" style="margin-bottom: 32px;"> |
| | | <div v-if="[3].includes(currentDetail.status)" class="form-section" style="margin-bottom: 32px;"> |
| | | <div class="section-title" v-if="hasProcessedAndOverBtnPermission()"> |
| | | <!-- 已完成状态显示必填星号 --> |
| | | <template v-if="currentDetail.status === 4"> |
| | | <template v-if="currentDetail.status === 3"> |
| | | <span class="required-label"> |
| | | <span class="required-star">*</span>上传图片 |
| | | </span> |
| | |
| | | <el-col :span="12"> |
| | | <div class="media-box"> |
| | | <!-- 根据状态显示不同的标题和内容 --> |
| | | <template v-if="currentDetail.status === 5"> |
| | | <template v-if="currentDetail.status === 4"> |
| | | <div class="media-title">工单处理图片</div> |
| | | <div class="media-content"> |
| | | <el-image v-if="currentDetail.updatePhotoUrl" :src="getThumbUrl(currentDetail.updatePhotoUrl)" |
| | |
| | | </template> |
| | | <template v-else-if="currentDetail.status === 0"> |
| | | <el-button v-if="hasProcessingBtnPermission()" type="primary" :loading="dispatchLoading" |
| | | @click="approveAndDispatch">通过并派发</el-button> |
| | | @click="approveAndDispatch">受理</el-button> |
| | | <el-button v-if="hasProcessingBtnPermission()" type="danger" :loading="rejectLoading" |
| | | @click="rejectTicket">不通过</el-button> |
| | | @click="rejectTicket">不受理</el-button> |
| | | <el-button @click="detailVisible = false">取消</el-button> |
| | | </template> |
| | | <template v-if="currentDetail.status === 3"> |
| | |
| | | </template> |
| | | <template v-else-if="currentDetail.status === 4"> |
| | | <!-- 已完成 --> |
| | | <el-button v-if="hasProcessedAndOverBtnPermission()" type="primary" :loading="finalizeLoading" |
| | | @click="finalizeTicket">完结工单</el-button> |
| | | <!-- <el-button v-if="hasProcessedAndOverBtnPermission()" type="primary" :loading="finalizeLoading" |
| | | @click="finalizeTicket">完结工单</el-button> --> |
| | | <el-button @click="detailVisible = false">取消</el-button> |
| | | </template> |
| | | <template v-else-if="currentDetail.status === 5"> |
| | |
| | | </div> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | | <!-- 复核弹出层 --> |
| | | |
| | | <el-dialog v-model="reCheckDialog" title="工单复核" width="30%" append-to-body custom-class="re-check-dialog" |
| | | @close="reCheckDialog = false"> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="reCheckConfirm(1)">人工复核</el-button> |
| | | <el-button type="primary" @click="reCheckConfirm(2)">机器复核</el-button> |
| | | </div> |
| | | </el-dialog> |
| | | </basic-container> |
| | | </template> |
| | | |
| | | <script> |
| | | import { getList, createTicket, getTicketInfo, flowEvent, getstatusCount, getStepInfo } from '@/api/tickets/ticket' |
| | | import { ElMessageBox, ElLoading } from 'element-plus' |
| | | import { calculateDefaultRange } from '@/utils/util' |
| | | import { gcj02ToWgs84, wgs84ToGcj02 } from '@/utils/coordinateTransformation' |
| | | import { |
| | | getList, |
| | | createTicket, |
| | | getTicketInfo, |
| | | flowEvent, |
| | | getstatusCount, |
| | | getStepInfo, |
| | | getReviewById, |
| | | getCreateEventJob |
| | | } from '@/api/tickets/ticket' |
| | | import { export_json_to_excel } from '@/utils/exportExcel' |
| | | import geoJson from '@/assets/geoJson.json' |
| | | |
| | |
| | | { label: "待处理", name: "processing", value: 0, count: 0 }, |
| | | { label: "处理中", name: "inProgress", value: 3, count: 0 }, |
| | | { label: "已完成", name: "completed", value: 4, count: 0 }, |
| | | { label: "已完结", name: "closed", value: 5, count: 0 }, |
| | | // { label: "已完结", name: "closed", value: 5, count: 0 }, |
| | | { label: "我发起的工单", name: "myTickets", value: null, count: 0 }, |
| | | ], |
| | | filters: { |
| | |
| | | dateRange: [], |
| | | status: "", |
| | | algorithm: "", // 新增算法筛选字段 |
| | | isReview: "", // 添加复核状态筛选字段 |
| | | }, |
| | | departments: [], |
| | | types: [], |
| | |
| | | { label: "处理中", value: "3" }, |
| | | { label: "已完成", value: "4" }, |
| | | { label: "已完结", value: "5" }, |
| | | ], |
| | | reviewStatuses: [ |
| | | { label: "否", value: 0 }, |
| | | { label: "是", value: 1 } |
| | | ], |
| | | tableData: [], |
| | | option: { |
| | |
| | | page: true, |
| | | column: [ |
| | | // { label: "序号", prop: "id", width: 70 }, |
| | | { label: "工单编号", prop: "orderNumber", width: 150, overHidden: true, tooltip: true }, |
| | | { label: "工单名称", prop: "orderName", width: 170, overHidden: true, tooltip: true }, |
| | | { label: "所属单位", prop: "department", width: 100, overHidden: true, tooltip: true }, |
| | | { label: "工单编号", prop: "orderNumber", width: 120, overHidden: true, tooltip: true }, |
| | | { label: "工单名称", prop: "orderName", width: 150, overHidden: true, tooltip: true }, |
| | | { label: "所属单位", prop: "department", overHidden: true, tooltip: true }, |
| | | { label: "发起时间", prop: "startTime", width: 160 }, |
| | | { label: "关联算法", prop: "aiType", width: 160, overHidden: true, tooltip: true }, |
| | | { label: "工单类型", prop: "type", width: 120, overHidden: true, tooltip: true }, |
| | | { label: "关联算法", prop: "aiType", width: 150, overHidden: true, tooltip: true }, |
| | | { label: "工单类型", prop: "type", width: 130, overHidden: true, tooltip: true }, |
| | | { |
| | | label: "工单内容", |
| | | prop: "content", |
| | | slot: true, |
| | | width: 190, |
| | | width: 152, |
| | | overHidden: true |
| | | }, |
| | | { label: "创建人", prop: "creator", width: 100 }, |
| | | { label: "处理人", prop: "handler", width: 100 }, |
| | | { label: "工单状态", prop: "status", slot: true, width: 90 } |
| | | { |
| | | slot: true, |
| | | hide: false, |
| | | label: "复核状态", |
| | | prop: "isReview", |
| | | width: 90, |
| | | }, |
| | | { label: "工单状态", prop: "status", slot: true, width: 90 }, |
| | | |
| | | ], |
| | | }, |
| | | page: { |
| | |
| | | currentImageIndex: 1, // 新增:当前图片索引 |
| | | totalTime: '', |
| | | isShowInfo: false, |
| | | |
| | | // 配置时间选择器默认配置 |
| | | datePickerDefaultVal: calculateDefaultRange(), |
| | | |
| | | // 复核弹窗 |
| | | reCheckDialog: false, |
| | | } |
| | | }, |
| | | created () { |
| | |
| | | // 其它tab直接用接口返回的stepInfos |
| | | return this.stepInfos.map(step => String(step.status)) |
| | | }, |
| | | |
| | | showIsReviewText () { |
| | | return (row) => { |
| | | if (['4', '5'].includes(String(row.status))) return row.isReview === 1 ? '是' : '否' |
| | | |
| | | return '/' |
| | | } |
| | | } |
| | | }, |
| | | methods: { |
| | | async loadAMapScripts () { |
| | |
| | | try { |
| | | const currentTab = this.tabs.find(tab => tab.name === this.activeTab) |
| | | const params = { |
| | | work_order_type_dict_key: this.filters.type || undefined, |
| | | word_order_type: this.filters.type || undefined, |
| | | status: currentTab?.name === 'myTickets' ? undefined : |
| | | this.filters.status !== "" ? Number(this.filters.status) : |
| | | currentTab?.value, |
| | |
| | | is_draft: currentTab?.name === 'myTickets' ? 1 : undefined, |
| | | |
| | | user_id: currentTab?.name === 'myTickets' ? this.userInfo.user_id : undefined, |
| | | is_review: this.filters.isReview === '' ? undefined : this.filters.isReview, // 添加复核状态查询参数 |
| | | } |
| | | |
| | | const response = await getList(params) |
| | |
| | | work_type: item.work_type !== undefined ? Number(item.work_type) : 0, // 保留work_type字段并转为数字 |
| | | job_name: item.job_name || '', |
| | | job_create_time: item.job_create_time || '', |
| | | isReview: item.is_review, // 添加复核状态字段映射 |
| | | } |
| | | }) |
| | | |
| | |
| | | return |
| | | } |
| | | |
| | | let [lng, lat] = this.disposeLocation(true, this.form) |
| | | |
| | | const submitData = { |
| | | eventName: this.form.name, |
| | | content: this.form.content, |
| | | workType: "1", |
| | | longitude: String(this.form.location[0]), |
| | | latitude: String(this.form.location[1]), |
| | | longitude: lng, |
| | | latitude: lat, |
| | | address: this.form.address, |
| | | workOrderTypeDictKey: this.form.type, |
| | | aiType: Array.isArray(this.form.algorithm) ? this.form.algorithm : [this.form.algorithm], // 传数组 |
| | |
| | | if (!handlerValue || handlerValue === '未分配') { |
| | | handlerValue = undefined |
| | | } |
| | | |
| | | let [lng, lat] = this.disposeLocation(true, this.form) |
| | | |
| | | const submitData = { |
| | | id: this.form.id, |
| | | eventName: this.form.name || undefined, |
| | | content: this.form.content || undefined, |
| | | workType: "1", |
| | | longitude: this.form.location?.[0] ? String(this.form.location[0]) : undefined, |
| | | latitude: this.form.location?.[1] ? String(this.form.location[1]) : undefined, |
| | | longitude: lng, |
| | | latitude: lat, |
| | | address: this.form.address || undefined, |
| | | workOrderTypeDictKey: this.form.type || undefined, |
| | | aiType: this.form.algorithm && this.form.algorithm.length > 0 |
| | |
| | | |
| | | handleTabChange (tab) { |
| | | this.activeTab = tab.props?.name || tab.name |
| | | |
| | | const isReview = this.findObject(this.option.column, 'isReview') |
| | | // 根据条件切换显隐 |
| | | isReview.hide = !['all', 'completed', 'myTickets'].includes(this.activeTab) |
| | | |
| | | this.handleReset() |
| | | this.page.currentPage = 1 |
| | | this.fetchTableData() |
| | |
| | | dateRange: [], |
| | | status: "", |
| | | algorithm: "", // 重置时清空算法筛选 |
| | | isReview: "", // 重置时清空复核状态 |
| | | } |
| | | this.page.currentPage = 1 |
| | | this.fetchTableData() |
| | |
| | | const steps = Array.isArray(stepResponse.data.data) |
| | | ? stepResponse.data.data |
| | | : stepResponse.data.data?.steps || [] |
| | | const finishedStep = steps.find(s => String(s.status) === '5') |
| | | 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 => ({ |
| | |
| | | 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, |
| | |
| | | isPass: 0, // 0 表示通过 |
| | | eventName: this.currentDetail.orderName, // 工单名称 |
| | | eventNum: this.currentDetail.orderNumber, |
| | | workOrderTypeDictKey: this.currentDetail.type, // 工单类型 |
| | | workOrderTypeDictKey: this.currentDetail.type, // 直接使用原始的 dict_key |
| | | content: this.currentDetail.content, // 使用 content 替代原来的 remark |
| | | createDept: this.dispatchForm.department, // 派发部门 ID |
| | | updateUser: this.dispatchForm.handler, // 处理人 ID |
| | | } |
| | | |
| | | |
| | | const file = this.currentDetail.file || null // 如果没有文件,则为 null |
| | | |
| | |
| | | photos: [], |
| | | } |
| | | |
| | | this.form.location = Array.isArray(row.location) && row.location.length >= 2 |
| | | ? [ |
| | | Number(row.location[0]), |
| | | Number(row.location[1]), |
| | | let curLocation = [] |
| | | |
| | | if (Array.isArray(row.location) && row.location.length >= 2) { |
| | | let [lng, lat] = this.disposeLocation(false, row) |
| | | |
| | | curLocation = [ |
| | | lng, |
| | | lat, |
| | | row.location[2] || row.address || '' |
| | | ] |
| | | : [] |
| | | } |
| | | |
| | | this.form.location = curLocation |
| | | this.form.address = this.form.location[2] || '' |
| | | // 设置地图中心点 |
| | | if (Array.isArray(this.form.location) && this.form.location.length >= 2) { |
| | |
| | | }) |
| | | |
| | | this.dialogVisible = true |
| | | }, |
| | | |
| | | // 添加数据监听以确保算法数据的响应性 |
| | | watch: { |
| | | 'form.algorithm': { |
| | | handler (newVal) { |
| | | console.log('算法值变化:', newVal) |
| | | }, |
| | | deep: true |
| | | } |
| | | }, |
| | | |
| | | // 添加删除方法 |
| | |
| | | if (lastDot === -1) return url |
| | | return url.slice(0, lastDot) + '_show' + url.slice(lastDot) |
| | | }, |
| | | |
| | | /** |
| | | * 坐标转换方法处理 |
| | | * @param goWgs84 是否由国测转换到84,默认false----由84转换到国测 |
| | | */ |
| | | disposeLocation (goWgs84 = false, data) { |
| | | let lng, lat |
| | | if (goWgs84) { |
| | | lng = data.location?.[0] ? String(data.location[0]) : undefined |
| | | lat = data.location?.[1] ? String(data.location[1]) : undefined |
| | | |
| | | if (lng && lat) { |
| | | [lng, lat] = gcj02ToWgs84(Number(lng), Number(lat)) |
| | | } |
| | | } else { |
| | | lng = Number(data.location[0]) |
| | | lat = Number(data.location[1]) |
| | | |
| | | if (lng && lat) { |
| | | [lng, lat] = wgs84ToGcj02(Number(lng), Number(lat)) |
| | | } |
| | | } |
| | | |
| | | return [String(lng), String(lat)] |
| | | }, |
| | | |
| | | // 复核按钮 |
| | | reCheck (row) { |
| | | this.reCheckData = row |
| | | this.reCheckDialog = true |
| | | }, |
| | | |
| | | // 复核确认框按钮事件 |
| | | reCheckConfirm (key) { |
| | | const that = this |
| | | if (key == 1) { |
| | | getReviewById(this.reCheckData.id).then(res => { |
| | | this.reCheckDialog = false |
| | | this.page.currentPage = 1 |
| | | this.fetchTableData() |
| | | this.fetchTabCounts() |
| | | }) |
| | | } else { |
| | | |
| | | const loading = ElLoading.service({ |
| | | lock: true, |
| | | text: '复核任务创建中……', |
| | | background: 'rgba(0, 0, 0, 0.7)', |
| | | }) |
| | | |
| | | // 获取时间的接口 |
| | | getCreateEventJob(that.reCheckData.id).then(res => { |
| | | loading.close() |
| | | |
| | | ElMessageBox.confirm(`预计复核执行时间为${res.data.data}`, '提示', { |
| | | confirmButtonText: '确定', |
| | | showCancelButton: false, // 关键配置 |
| | | type: 'warning' |
| | | }).then(() => { |
| | | this.reCheckDialog = false |
| | | this.page.currentPage = 1 |
| | | this.fetchTableData() |
| | | this.fetchTabCounts() |
| | | }) |
| | | }) |
| | | } |
| | | } |
| | | }, |
| | | } |
| | | </script> |
| | |
| | | } |
| | | } |
| | | |
| | | .re-check-dialog { |
| | | :deep(.el-dialog__body) { |
| | | padding: 0; |
| | | background-color: #f5f7fa; |
| | | } |
| | | } |
| | | |
| | | .review-container { |
| | | position: relative; |
| | | |