| | |
| | | v-model="visible" |
| | | :title="titleEnum[dialogMode]" |
| | | @closed="visible = false" |
| | | :style="{ width: pxToRem(1216) }" |
| | | destroy-on-close |
| | | :close-on-click-modal="false" |
| | | width="80%" |
| | | > |
| | | <div class="content" style="display: flex"> |
| | | <div class="leftBox"> |
| | | <div class="detail-container" style="display: flex"> |
| | | <div class="processBox"> |
| | | <div class="detail-title">巡查需求记录</div> |
| | | <div class="detail-left" v-if="dialogMode !== 'add' && processList.length"> |
| | | <el-timeline class="gd-timeline"> |
| | | <el-timeline-item |
| | | v-for="(activity, index) in processList" |
| | | :key="index" |
| | | :icon="Check" |
| | | :type="index === processList.length - 1 ? 'success' : 'info'" |
| | | :color="activity.color" |
| | | :hollow="activity.hollow" |
| | | :timestamp="activity.createTime" |
| | | > |
| | | <div class="item-content"> |
| | | <div>{{ activity.operator }}</div> |
| | | <div class="flowName">{{ activity.flowName }}</div> |
| | | </div> |
| | | </el-timeline-item> |
| | | </el-timeline> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="detail-right"> |
| | | <!-- 查看模式 --> |
| | | <div class="detail-container" v-if="dialogReadonly"> |
| | | <div class="view-container" v-if="dialogReadonly"> |
| | | <div class="detail-title">巡查任务详情</div> |
| | | <el-row class="detail-row-view"> |
| | | <el-col :span="12"> |
| | | <div class="label">巡查任务名称</div> |
| | |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="label">巡查任务类型</div> |
| | | <div class="val">{{ getDictLabel(formData.patrolTaskType, dictObj.patrolTaskType) }}</div> |
| | | <div class="val">{{ getDictLabel(formData.patrolTaskType, dictObj.workOrderType) }}</div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="label">任务执行时间</div> |
| | |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="label">巡查任务航线</div> |
| | | <div class="val">{{ getRouteName(formData.patrolRouteUrl) }}</div> |
| | | <div class="val val-long"> |
| | | <el-tooltip :content="getAirName(formData.patrolRouteUrl)" placement="top"> |
| | | {{ getAirName(formData.patrolRouteUrl) }} |
| | | </el-tooltip> |
| | | </div> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <div class="label">推荐飞手</div> |
| | |
| | | <div class="label">关联工单</div> |
| | | <div class="val">{{ getWorkOrderName(formData.workOrderId) }}</div> |
| | | </el-col> |
| | | <el-col :span="24"> |
| | | <el-col :span="12"> |
| | | <div class="label">巡查任务描述</div> |
| | | <div class="val">{{ formData.taskDesc }}</div> |
| | | </el-col> |
| | | </el-row> |
| | | <template v-if="['6', '7', '8'].includes(taskStatus)"> |
| | | <div>任务成功({{ list.length || 0 }}条)</div> |
| | | <div style="display: flex"> |
| | | <div v-for="item in list"> |
| | | <div class="detail-title" :style="{ marginTop: pxToRem(10) }"> |
| | | 任务成果({{ taskResultList.length || 0 }}条) |
| | | </div> |
| | | <div class="imgBox"> |
| | | <div v-for="item in taskResultList"> |
| | | <el-image |
| | | v-if="item.resultUrl" |
| | | :src="item.resultUrl" |
| | | :preview-src-list="[item.resultUrl]" |
| | | fit="cover" |
| | | style="width: 80px; height: 60px" |
| | | preview-teleported |
| | | /> |
| | | </div> |
| | |
| | | </div> |
| | | |
| | | <!-- 编辑模式 --> |
| | | <el-form class="gd-dialog-form" v-else ref="formRef" :model="formData" :rules="rules" label-width="110px"> |
| | | <el-form class="gd-dialog-form" v-else ref="formRef" :model="formData" :rules="rules" label-width="130px"> |
| | | <div class="detail-title">巡查任务详情</div> |
| | | <el-row> |
| | | <el-col :span="12"> |
| | | <el-form-item label="巡查任务名称" prop="patrolTaskName"> |
| | |
| | | clearable |
| | | > |
| | | <el-option |
| | | v-for="item in dictObj.patrolTaskType" |
| | | v-for="item in dictObj.workOrderType" |
| | | :key="item.dictKey" |
| | | :label="item.dictValue" |
| | | :value="item.dictKey" |
| | |
| | | <el-form-item label="任务执行时间" prop="executeTime"> |
| | | <el-date-picker |
| | | class="gd-date-picker" |
| | | popper-class="gd-date-picker-popper" |
| | | v-model="formData.executeTime" |
| | | type="datetime" |
| | | placeholder="请选择" |
| | |
| | | v-model="formData.patrolRouteUrl" |
| | | placeholder="请选择" |
| | | clearable |
| | | @change="getAirDetails" |
| | | > |
| | | <el-option v-for="item in routeOptions" :key="item.id" :label="item.name" :value="item.id" /> |
| | | </el-select> |
| | |
| | | </el-col> |
| | | <el-col :span="24"> |
| | | <el-form-item label="巡查任务描述" prop="taskDesc"> |
| | | <el-input class="gd-input" type="textarea" :rows="4" v-model="formData.taskDesc" placeholder="请输入" clearable /> |
| | | <el-input |
| | | class="gd-input" |
| | | type="textarea" |
| | | :rows="4" |
| | | v-model="formData.taskDesc" |
| | | placeholder="请输入" |
| | | clearable |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | </el-form> |
| | | |
| | | <div class="detail-title" :style="{ marginTop: pxToRem(10) }">任务航线</div> |
| | | <!-- 地图组件 --> |
| | | <CommonCesiumMap |
| | | ref="mapRef" |
| | | class="leftMap gd-cesium" |
| | | class="gd-cesium" |
| | | :active="visible" |
| | | :flat-mode="false" |
| | | :terrain="true" |
| | | :layer-mode="4" |
| | | :boundary="false" |
| | | :zoomToBoundary="false" |
| | | @ready="readyMap" |
| | | /> |
| | | </div> |
| | | </div> |
| | | |
| | | <template #footer> |
| | | <template v-if="requester"> |
| | | <el-button |
| | | v-if="!dialogReadonly || ['1', '4'].includes(taskStatus)" |
| | | :loading="submitting" |
| | | :disabled="submitting" |
| | | @click="handleSubmit" |
| | | > |
| | | 提交 |
| | | </el-button> |
| | | <el-button v-if="['1', '4'].includes(taskStatus)" @click="viewDescription"> |
| | | {{ gdStatusObj[taskStatus].reason }} |
| | | </el-button> |
| | | <el-button @click="statusChange(3)" v-if="taskStatus === '3'">撤回任务</el-button> |
| | | <el-button @click="refusalToAccept" v-if="taskStatus === '6'">拒绝验收</el-button> |
| | | <el-button @click="statusChange(6)" v-if="taskStatus === '6'">验收通过</el-button> |
| | | <el-button v-if="['1', '4', '7'].includes(taskStatus)" @click="viewDescription" color="#F2F3F5"> |
| | | {{ gdStatusObj[taskStatus].reason }} |
| | | </el-button> |
| | | <el-button @click="statusChange(3)" v-if="taskStatus === '3' && permission.flyOrder_revoke" color="#4C34FF"> |
| | | 撤回任务 |
| | | </el-button> |
| | | |
| | | <template v-if="permission.flyOrder_controlAcceptance"> |
| | | <el-button @click="refusalToAccept" v-if="taskStatus === '6'" color="#F2F3F5">拒绝验收</el-button> |
| | | <el-button @click="statusChange(6)" v-if="taskStatus === '6'" color="#4C34FF">验收通过</el-button> |
| | | </template> |
| | | <template v-else> |
| | | <el-button v-if="['7'].includes(taskStatus)" @click="viewDescription"> |
| | | {{ gdStatusObj[taskStatus].reason }} |
| | | </el-button> |
| | | <el-button v-if="taskStatus === '0'" @click="addDescription">拒绝签收</el-button> |
| | | <el-button v-if="taskStatus === '0'" @click="statusChange(1)">签收</el-button> |
| | | <el-button v-if="taskStatus === '3'" @click="addDescription">驳回</el-button> |
| | | <el-button v-if="taskStatus === '3'" @click="statusChange(4)">同意</el-button> |
| | | <el-button |
| | | v-if="!dialogReadonly || (['1', '4'].includes(taskStatus) && permission.flyOrder_add)" |
| | | :loading="submitting" |
| | | :disabled="submitting" |
| | | color="#4C34FF" |
| | | @click="handleSubmit" |
| | | > |
| | | 提交 |
| | | </el-button> |
| | | <template v-if="permission.flyOrder_controlSignFor"> |
| | | <el-button v-if="taskStatus === '0'" @click="addDescription" color="#F2F3F5">拒绝签收</el-button> |
| | | <el-button v-if="taskStatus === '0'" @click="statusChange(1)" color="#4C34FF">签收</el-button> |
| | | </template> |
| | | <template v-if="permission.flyOrder_controlReview"> |
| | | <el-button v-if="taskStatus === '3'" @click="addDescription" color="#F2F3F5">驳回</el-button> |
| | | <el-button v-if="taskStatus === '3'" @click="statusChange(4)" color="#4C34FF">通过</el-button> |
| | | </template> |
| | | </template> |
| | | |
| | | <RefuseOrderDialog1 |
| | | ref="refuseOrderDialogRef" |
| | | v-if="rejectVisible" |
| | |
| | | <script setup> |
| | | import { computed, ref, onMounted } from 'vue' |
| | | import { ElMessage } from 'element-plus' |
| | | import { fieldRules, getDictLabel } from '@ztzf/utils' |
| | | import { gdPatrolTaskRepublish, gdFlyerPageApi, gdPatrolTaskAuditApi } from './inspectionRequestApi' |
| | | import { gdWorkOrderPageApi } from '../orderManage/orderManageApi' |
| | | import { fieldRules, flyVisual, getDictLabel } from '@ztzf/utils' |
| | | import { |
| | | gdPatrolTaskRepublish, |
| | | gdFlyerPageApi, |
| | | gdPatrolTaskAuditApi, |
| | | gdPatrolTaskDetailApi, |
| | | } from './inspectionRequestApi' |
| | | import { gdWorkOrderFlowListApi, gdWorkOrderFlowPatrolListApi, gdWorkOrderPageApi } from '../orderManage/orderManageApi' |
| | | import { gdManageDeviceListApi } from '../orderManage/gdManageDeviceApi' |
| | | import { pxToRem } from '@/utils/rem' |
| | | import CommonCesiumMap from '@/components/map-container/common-cesium-map.vue' |
| | | import { gdTaskResultListApi } from '@/views/orderView/orderManage/clueEvents/achievementApi' |
| | | import RefuseOrderDialog from '@/views/orderView/orderManage/orderManage/RefuseOrderDialog.vue' |
| | | import RefuseOrderDialog1 from '@/views/orderView/orderManage/inspectionRequest/RefuseOrderDialog1.vue' |
| | | import { Check } from '@element-plus/icons-vue' |
| | | import { queryAirById } from '@/api/zkxt' |
| | | import * as Cesium from 'cesium' |
| | | import { useStore } from 'vuex' |
| | | |
| | | // 初始化表单数据 |
| | | const initForm = () => ({ |
| | |
| | | taskDesc: '', |
| | | workOrderId: null, |
| | | }) |
| | | const store = useStore() |
| | | const permission = computed(() => store.state.user.permission) |
| | | |
| | | const rejectVisible = ref(false) |
| | | const mapRef = ref(null) |
| | | const dictObj = inject('dictObj') |
| | | const requester = inject('requester') |
| | | const emit = defineEmits(['success', 'refusalAccept']) |
| | | const formRef = ref(null) |
| | | const formData = ref(initForm()) |
| | |
| | | const flyerList = ref([]) |
| | | const deviceList = ref([]) |
| | | const workOrderList = ref([]) |
| | | const routeOptions = ref([ |
| | | { name: '航线1', id: '1' }, |
| | | { name: '航线2', id: '2' }, |
| | | { name: '航线3', id: '3' }, |
| | | ]) |
| | | const routeOptions = inject('routeOptions') |
| | | const getAirName = inject('getAirName') |
| | | |
| | | const gdStatusObj = { |
| | | '0': { reason: '拒绝原因', operationType: '2' }, |
| | | '0': { reason: '拒绝原因', operationType: '2', title: '拒绝签收' }, |
| | | '1': { reason: '拒绝原因' }, |
| | | '2': { reason: '' }, |
| | | '3': { reason: '驳回原因', operationType: '5' }, |
| | | '3': { reason: '驳回原因', operationType: '5', title: '驳回' }, |
| | | '4': { reason: '驳回原因' }, |
| | | '5': { reason: '' }, |
| | | '6': { reason: '拒绝原因' }, |
| | |
| | | row: formData.value, |
| | | type: gdStatusObj[taskStatus.value].operationType, |
| | | formLabel: gdStatusObj[taskStatus.value].reason, |
| | | title: gdStatusObj[taskStatus.value]?.title, |
| | | }) |
| | | }) |
| | | } |
| | |
| | | |
| | | // 获取飞手列表 |
| | | async function getFlyerList() { |
| | | const res = await gdFlyerPageApi({ size: 999 }) |
| | | const res = await gdFlyerPageApi({ size: 999, current: 1 }) |
| | | flyerList.value = res?.data?.data?.records ?? [] |
| | | } |
| | | |
| | |
| | | deviceList.value = res?.data?.data ?? [] |
| | | } |
| | | |
| | | const processList = ref([]) |
| | | // 加载时间线list |
| | | function loadList() { |
| | | gdWorkOrderFlowPatrolListApi({ workOrderId: formData.value.id, type: '1' }).then(res => { |
| | | processList.value = res.data.data |
| | | }) |
| | | } |
| | | |
| | | // 提交编辑 |
| | | async function handleSubmit() { |
| | | const isValid = await formRef.value?.validate().catch(() => false) |
| | |
| | | } |
| | | } |
| | | |
| | | let list = ref([]) |
| | | let taskResultList = ref([]) |
| | | // 获取成果列表 |
| | | async function getList() { |
| | | async function getTaskResultList() { |
| | | if (!formData.value?.id) return |
| | | try { |
| | | const res = await gdTaskResultListApi({ patrolTaskId: formData.value.id }) |
| | | list.value = res?.data?.data ?? [] |
| | | taskResultList.value = res?.data?.data ?? [] |
| | | } finally { |
| | | } |
| | | } |
| | | import { ArrowLineMaterialProperty } from '@/utils/cesium/Material' |
| | | import OrderStepBar from '@/views/orderView/orderManage/orderManage/OrderStepBar.vue' |
| | | let arrowLineMaterialProperty = new ArrowLineMaterialProperty({ |
| | | color: new Cesium.Color(128 / 255, 215 / 255, 255 / 255, 1), |
| | | directionColor: new Cesium.Color(1, 1, 1, 1), |
| | | outlineColor: new Cesium.Color(1, 1, 1, 1), |
| | | outlineWidth: 0, |
| | | speed: 5, |
| | | }) |
| | | // 获取航线详情 |
| | | function getAirDetails() { |
| | | console.log(formData.value) |
| | | queryAirById(formData.value.patrolRouteUrl).then(res => { |
| | | const list = res.data.data.airlineWaypoints |
| | | if (!list.length) return mapRef.value?.flyBoundary() |
| | | const result = list.map(item => [Number(item.longitude), Number(item.latitude)]).flat() |
| | | viewer.entities.add({ |
| | | polyline: { |
| | | positions: Cesium.Cartesian3.fromDegreesArray(result), |
| | | clampToGround: true, |
| | | width: 4, |
| | | material: arrowLineMaterialProperty, |
| | | }, |
| | | }) |
| | | // 渲染起点(蓝色) |
| | | const startPoint = list[0] |
| | | viewer.entities.add({ |
| | | position: Cesium.Cartesian3.fromDegrees( |
| | | Number(startPoint.longitude), |
| | | Number(startPoint.latitude), |
| | | startPoint.height || 0 |
| | | ), |
| | | point: { |
| | | pixelSize: 12, |
| | | color: Cesium.Color.BLUE, |
| | | outlineColor: Cesium.Color.WHITE, |
| | | outlineWidth: 2, |
| | | heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, |
| | | }, |
| | | }) |
| | | // 渲染终点(红色) |
| | | const endPoint = list[list.length - 1] |
| | | viewer.entities.add({ |
| | | position: Cesium.Cartesian3.fromDegrees( |
| | | Number(endPoint.longitude), |
| | | Number(endPoint.latitude), |
| | | endPoint.height || 0 |
| | | ), |
| | | point: { |
| | | pixelSize: 12, |
| | | color: Cesium.Color.RED, |
| | | outlineColor: Cesium.Color.WHITE, |
| | | outlineWidth: 2, |
| | | heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, |
| | | }, |
| | | }) |
| | | flyVisual({ |
| | | positionsData: list.map(i => [Number(i.longitude), Number(i.latitude), i.height || 0]), |
| | | viewer, |
| | | }) |
| | | }) |
| | | } |
| | | |
| | | // 打开弹框 |
| | | async function open({ mode = 'view', row } = {}) { |
| | | dialogMode.value = mode |
| | | formData.value = { ...initForm(), ...row } |
| | | ;['6', '7', '8'].includes(row.taskStatus) && getList() |
| | | const res = await gdPatrolTaskDetailApi({ id: row.id }) |
| | | formData.value = { ...initForm(), ...res.data.data } |
| | | ;['6', '7', '8'].includes(row.taskStatus) && await getTaskResultList() |
| | | loadList() |
| | | initMap() |
| | | getAirDetails() |
| | | } |
| | | |
| | | // 初始化地图实例 |
| | |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .content { |
| | | display: flex; |
| | | gap: 0 20px; |
| | | .leftBox { |
| | | width: 0; |
| | | flex: 1; |
| | | .gd-cesium { |
| | | width: 100%; |
| | | height: 423px; |
| | | /* 时间线样式 */ |
| | | :deep(.gd-timeline) { |
| | | padding-left: 90px; |
| | | } |
| | | |
| | | :deep(.el-timeline-item) { |
| | | padding-bottom: 20px; |
| | | .item-content { |
| | | position: relative; |
| | | .flowName { |
| | | width: 72px; |
| | | position: absolute; |
| | | left: -120px; |
| | | top: 0px; |
| | | } |
| | | } |
| | | } |
| | | .imgBox { |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 10px; |
| | | > div { |
| | | width: calc((100% - 40px) / 5); /* 5列布局,减去4个10px间隙 */ |
| | | height: 110px; |
| | | flex-shrink: 0; |
| | | } |
| | | .el-image { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | } |
| | | :deep(.el-timeline-item__timestamp) { |
| | | font-size: 12px; |
| | | color: #999; |
| | | margin-top: 2px; |
| | | } |
| | | |
| | | .processBox { |
| | | display: flex; |
| | | flex-direction: column; |
| | | |
| | | .detail-left { |
| | | height: 100%; |
| | | overflow: auto; |
| | | } |
| | | } |
| | | </style> |