src/api/tickets/orderLog.js
@@ -86,6 +86,15 @@ }); }; // 获取返航最高 高度 export const getWaylineMaxTerrainHeight = data => { return request({ url: `/drone-odm/odmTaskInfo/odmTaskInfo/getWaylineMaxTerrainHeight`, method: 'post', data, }) } src/api/tickets/ticket.js
@@ -103,4 +103,13 @@ url: `/drone-device-core/wayline/waylineJobInfo/createEventJob/${id}`, method: 'post', }) } // 导出工单 export const exportTheTicket = params => { return request({ url: `/drone-device-core/jobEvent/downloadEvent`, method: 'get', responseType: 'blob', params, }) } src/hooks/useRouteLine/useRouteLine.js
@@ -51,7 +51,7 @@ resetCurRouteLineData() const { pointPlacemark, polygonList, templateType, startPoint, execute_height_mode, auto_flight_speed } = const { pointPlacemark, polygonList, templateType, startPoint, execute_height_mode, auto_flight_speed, take_off_security_height } = await analysisPointLineKmz(kmzUrl) curRouteLineData.value = { @@ -61,6 +61,7 @@ execute_height_mode, auto_flight_speed, wayline_type, take_off_security_height, infos: infos } @@ -194,9 +195,10 @@ height: 50, }, }) // 获取最后一点高度 console.log(pointList, '查看数组最后一点高度') store.commit('setLastHeight', Number(pointList[pointList.length - 1].height)) // 获取航线拆分所有点 store.commit('setPositionsArr', pointList) // 获取安全起飞高度 store.commit('setSafeHeight', curRouteLineData.value.take_off_security_height) flyVisual({ positionsData:pointList.map(i => [Number(i.longitude), Number(i.latitude), Number(i.height)]), viewer, @@ -217,6 +219,7 @@ execute_height_mode: '', auto_flight_speed: '', wayline_type: '', take_off_security_height: '' } } src/store/modules/common.js
@@ -21,11 +21,15 @@ htsjzx: 100, //数据中心 htlsrwxq: 100, //历史任务详情 }, lastHeight: 0, // 获取最后一点高度 safeHeight: 0, // 获取选择航线高度 positionsArr: [], // 获取选择航线点数据 }, mutations: { setLastHeight(state, data) { state.lastHeight = data setSafeHeight (state, data) { state.safeHeight = data }, setPositionsArr(state, data) { state.positionsArr = data }, setDownloadProgress(state, data) { state.downloadProgress = data src/views/algorithmRepository/algorithmRepository.vue
@@ -4,7 +4,7 @@ <div class="algorithItem" v-if="!showDetail"> <div class="item" v-for="(item, index) in AlgorithmData" :key="index" :class="{ 'active-bg': activeItem === item.dictValue }" @click="jumpDatail(item)"> <img class="imgicon" :src="`${baseUrl}/后台-算法仓库/${item.dictValue}.png`" alt="" /> <img class="imgicon" :src="`${baseUrl}/manage-sf-icon-source/${item.dictKey}.png`" alt="" /> <div class="item-title">{{ item.dictValue }}</div> <div :class="!statusSign && item.dictValue === nameSign ? 'stopStatus' : 'normalStatus'" @click.stop="changeStatus(item)"> @@ -101,6 +101,8 @@ // 处理数据 AlgorithmData.value = res.data.data['SF'] } console.log('算法',AlgorithmData.value); }) } const activeItem = ref(null) src/views/device/airport.vue
@@ -721,6 +721,7 @@ prop: 'login_time', type: 'date', viewDisplay: true, //查看显示 editDisabled: true, labelWidth: 145, overHidden: true, format: 'YYYY-MM-DD HH:mm:ss', @@ -754,6 +755,7 @@ type: 'date', hide: true, viewDisplay: true, //查看显示 editDisabled: true, overHidden: true, labelWidth: 145, width: 160, src/views/job/components/DeviceJobDetails.vue
@@ -189,6 +189,7 @@ { name: '任务时间', value: '', field: 'cycle_time_value' }, { name: '关联算法', value: '', field: 'ai_type_str' }, { name: '任务描述', value: '', field: 'remark' }, { name: '自定义识别区', value: '', field: 'enable_custom_area' }, // { name: '任务执行次数', value: '', field: 'job_num' }, ]) // 机巢状态 @@ -272,6 +273,9 @@ droneEventList.filter(({ value }) => actionActuatorFunc === value) ) } if (item.name === '自定义识别区') { item.value = res.data.data.enable_custom_area ? '是': '否' } getJobsAllFiles({ wayLineJobId: detailsData.value.way_lines.map(item => item.job_id).join(','), resultTypes: [0, 2, 4, 5], src/views/layerManagement/components/folderFile.vue
@@ -8,12 +8,12 @@ <el-input class="ztzf-layer-input inputName" v-model="formData.name" placeholder="请输入" /> </div> </div> <div class="itemRow" v-if="layerParams.addAnEditingFolder === 1 && layerParams.fileType !==2 && !itSLandPlanning"> <div class="itemRow" v-if="(layerParams.addAnEditingFolder === 1 && layerParams.fileType !==2) || (layerParams.addAnEditingFolder === 2 && layerParams.fileType !==2) && !itSLandPlanning"> <div class="label">关联算法</div> <div> <el-select class="ztzf-layer-search filter-item" @change="handleTypeChange" v-model="formData.event_type" placeholder="请选择" @@ -28,7 +28,6 @@ </el-select> <el-select v-model="formData.algorithm_name" class="ztzf-layer-search" placeholder="请选择" clearable @@ -47,13 +46,14 @@ <div class="detailInfo" v-if="layerParams.editFolder"> <div class="infpItem" v-if="!itSLandPlanning"> <div class="itemData"> <div class="title">围栏总数量</div> <div class="title">{{ layerParams.fileType === 1 ? '识别区' : '禁飞区' }}总数量</div> <div class="num"> <span>{{ formData.total_count }}</span> 个 </div> </div> <div class="itemData"> <div class="title">围栏总面积</div> <div class="title">{{ layerParams.fileType === 1 ? '识别区' : '禁飞区' }}总面积</div> <div class="num"> <span class="areaStyle">{{ formData.total_area }}</span> m² </div> @@ -233,7 +233,8 @@ width: 324px; overflow: hidden; background: rgba(0, 0, 0, 0.8); background: rgba(0,0,0,0.51); backdrop-filter: blur(4px); border-radius: 8px 8px 8px 8px; padding: 16px 12px; src/views/layerManagement/components/leftList.vue
@@ -13,7 +13,7 @@ </div> <div class="btnGroups"> <el-button v-if="activeName !== '国土空间规划'" class="ztzf-button" @click="addFence">{{ props.activeName === '电子围栏' ? '新增围栏' : '新增禁飞区' props.activeName === '自定义识别区' ? '新增识别区' : '新增禁飞区' }}</el-button> <el-button v-if="activeName !== '国土空间规划'" class="ztzf-button" @click="addFolder" >新增文件夹</el-button @@ -36,7 +36,10 @@ <el-button v-else class="ztzf-button" @click="synchronize"> 同步 </el-button> </template> --> <el-button class="ztzf-button" @click="selectAll">全选</el-button> <!-- <el-button class="ztzf-button" @click="selectAll">全选</el-button> --> <el-button class="ztzf-button" @click="selectAll"> {{ checkedKeys.length === getAllFenceKeys().length ? '取消全选' : '全选' }} </el-button> </div> <div class="tableListBox" @@ -54,6 +57,7 @@ node-key="id" default-expand-all :expand-on-click-node="false" :check-on-click-node="false" :filter-node-method="filterNode" @check="handleCheck" :props="{ @@ -147,28 +151,33 @@ ); }; const handleCheck = (checkedNodesData, checkedKeysData) => { layerParams.value.isSingleLocating = false const filteredNodes = checkedKeysData.checkedNodes.filter(node => node.level === 3); checkedNodes.value = checkedNodesData.checkedNodes; checkedNodes.value = filteredNodes checkedKeys.value = filteredNodes.map(node => node.id); coverData.value = filteredNodes; emit('update:coverData', coverData.value); }; // 全选方法 const getAllFenceKeys = () => { return treeData.value.flatMap( folder => folder.children.map(fence => fence.id) ); }; const selectAll = () => { layerParams.value.isSingleLocating = false if (treeRef.value) { const allFenceKeys = treeData.value.flatMap( folder => folder.children.map(fence => fence.id) // folder 是 level 2,children 是 level 3 ); const allFenceKeys = getAllFenceKeys(); const allFenceNodes = treeData.value.flatMap(folder => folder.children); if (checkedKeys.value.length === allFenceKeys.length) { // 取消全选:清空 keys 和数据 // 取消全选 treeRef.value.setCheckedKeys([]); checkedKeys.value = []; checkedNodes.value = []; coverData.value = []; } else { // 全选:仅设置 level 3 节点的 id // 全选 treeRef.value.setCheckedKeys(allFenceKeys); checkedKeys.value = allFenceKeys; checkedNodes.value = allFenceNodes; @@ -179,27 +188,44 @@ }; // 定位按钮事件 const handleLocation = node => { checkedKeys.value = []; checkedNodes.value = []; coverData.value = []; if (node.data.level === 2) { layerParams.value.isSingleLocating = false; } else if (node.data.level === 3) { layerParams.value.isSingleLocating = true; } const currentCheckedKeys = [...checkedKeys.value]; const currentCheckedNodes = [...checkedNodes.value]; EventBus.emit('focusOnNode', node.data); if (node.data.level === 2) { const folderChildren = node.data.children || []; // const childrenIds = folderChildren.map(child => child.id); // treeRef.value.setCheckedKeys(childrenIds); // checkedKeys.value = childrenIds; // checkedNodes.value = folderChildren; coverData.value = folderChildren; emit('update:coverData', coverData.value); } else { emit('update:coverData', [node.data]); const childrenIds = folderChildren.map(child => child.id); // 合并已选中的节点和当前文件夹的子节点 const newCheckedKeys = [...new Set([...currentCheckedKeys, ...childrenIds])]; const newCheckedNodes = [...currentCheckedNodes, ...folderChildren]; treeRef.value.setCheckedKeys(newCheckedKeys); checkedKeys.value = newCheckedKeys; checkedNodes.value = newCheckedNodes; coverData.value = newCheckedNodes; } // 如果是单个节点(level 3),仅添加到选中列表(如果未选中) else if (!currentCheckedKeys.includes(node.data.id)) { layerParams.value.isSingleLocating = true currentCheckedKeys.push(node.data.id); currentCheckedNodes.push(node.data); treeRef.value.setCheckedKeys(currentCheckedKeys); checkedKeys.value = currentCheckedKeys; checkedNodes.value = currentCheckedNodes; coverData.value = currentCheckedNodes; } emit('update:coverData', coverData.value); }; // 编辑按钮事件 const handleEdit = node => { if (node.data.level == 2) { layerParams.value.editFolder = true; layerParams.value.addAnEditingFolder = 2; if (props.activeName === '电子围栏') { if (props.activeName === '自定义识别区') { layerParams.value.fileType = 1; } else if (props.activeName === '自定义禁飞区') { layerParams.value.fileType = 2; @@ -211,10 +237,10 @@ coverData.value = []; layerParams.value.editNest = true; layerParams.value.decideWhetherToAddOrEdit = 2; if (props.activeName === '电子围栏') { if (props.activeName === '自定义识别区') { layerParams.value.fenceType = 1; // 电子围栏类型标识 } else if (props.activeName === '自定义禁飞区') { layerParams.value.fenceType = 2; // 自定义禁飞区类型标识 layerParams.value.fenceType = 2; // 自定义禁飞区类型标识 } emit('update:deitData', node.data); } @@ -234,7 +260,6 @@ deleteFolderApi(id).then(res => { ElMessage.success('删除成功'); gettreeDataApi(); // 通知父组件删除该文件夹下所有围栏的地图实体 EventBus.emit('deleteMapEntitiesByFolderId', id); }); } else if (node.data.level === 3) { @@ -242,7 +267,6 @@ deleteFenceApi(id).then(res => { ElMessage.success('删除成功'); gettreeDataApi(); // 通知父组件删除对应的地图实体 EventBus.emit('deleteMapEntityById', id); }); } @@ -255,7 +279,7 @@ const addFence = () => { layerParams.value.addNest = true; layerParams.value.decideWhetherToAddOrEdit = 1; if (props.activeName === '电子围栏') { if (props.activeName === '自定义识别区') { layerParams.value.fenceType = 1; // 电子围栏类型标识 } else if (props.activeName === '自定义禁飞区') { layerParams.value.fenceType = 2; // 自定义禁飞区类型标识 @@ -266,13 +290,12 @@ const addFolder = () => { layerParams.value.addFolder = true; layerParams.value.addAnEditingFolder = 1; if (props.activeName === '电子围栏') { if (props.activeName === '自定义识别区') { layerParams.value.fileType = 1; } else if (props.activeName === '自定义禁飞区') { layerParams.value.fileType = 2; } }; function uploadFlightFile(file, t) { const fileSuffix = file.name.substring(file.name.lastIndexOf('.') + 1); if (!['zip'].includes(fileSuffix)) { @@ -360,7 +383,8 @@ z-index: 99; width: 382px; overflow: hidden; background: rgba(0, 0, 0, 0.8); background: rgba(0,0,0,0.51); backdrop-filter: blur(4px); border-radius: 8px 8px 8px 8px; padding: 16px 0 16px 12px; display: flex; src/views/layerManagement/components/rightEdit.vue
@@ -1,6 +1,6 @@ <template> <div class="rightContainer"> <div class="contenttitle">{{ layerParams.fenceType === 1 ? '围栏' : '禁飞区' }}信息</div> <div class="contenttitle">{{ layerParams.fenceType === 1 ? '识别区' : '禁飞区' }}信息</div> <div class="centerBox"> <div class="itemRow"> <div class="label">文件夹名称</div> @@ -22,7 +22,7 @@ </div> </div> <div class="itemRow"> <div class="label">{{ layerParams.fenceType === 1 ? '围栏' : '禁飞区' }}名称</div> <div class="label">{{ layerParams.fenceType === 1 ? '识别区' : '禁飞区' }}名称</div> <div> <el-input :disabled="isDisabled" @@ -49,7 +49,7 @@ </div> </div> <div class="itemRow"> <div class="label">{{ layerParams.fenceType === 1 ? '围栏' : '禁飞区' }}备注</div> <div class="label">{{ layerParams.fenceType === 1 ? '识别区' : '禁飞区' }}备注</div> <div> <el-input :disabled="isDisabled" @@ -64,7 +64,7 @@ <div class="detailInfo"> <div class="infpItem" v-if="layerParams.editNest && layerParams.fenceType === 1"> <div class="itemData"> <div class="title">围栏面积</div> <div class="title">识别区面积</div> <div class="num"> <span class="areaStyle">{{ fenceArea }}</span> m² </div> @@ -336,7 +336,8 @@ width: 324px; overflow: hidden; background: rgba(0, 0, 0, 0.8); background: rgba(0,0,0,0.51); backdrop-filter: blur(4px); border-radius: 8px 8px 8px 8px; padding: 16px 12px; src/views/layerManagement/index.vue
@@ -9,12 +9,6 @@ </div> <!-- 地图 --> <div class="mapContainer"> <!-- <div class="tool-tip warning" v-show="isShowWaringTip"> <span class="icon"> <el-icon><WarningFilled /></el-icon> </span> <span>测区不支持交叉面,无法生成航线</span> </div> --> <div class="tool-tip" v-if="layerParams.addNest">点击地图生成测绘区域</div> <div id="layMap" class="ztzf-cesium"></div> <leftList @@ -65,7 +59,7 @@ const areaCode = userInfo.value?.detail?.areaCode || ''; // 红色警告交叉面提示窗 const isShowWaringTip = ref(false); const activeName = ref('电子围栏'); const activeName = ref('自定义识别区'); const activeType = ref('0'); let tbJwdList = []; const selectDataList = ref([]); @@ -73,7 +67,7 @@ let curPolygonPosition = []; const tabData = ref([ { name: '电子围栏', name: '自定义识别区', type: '0', }, { @@ -103,6 +97,7 @@ editFolderData: null, editingIsProhibited: false, //禁止编辑 isDetailShow: false, isSingleLocating:false }); const handleClick = tab => { @@ -121,7 +116,7 @@ }; const getdataFolderApi = () => { let id; if (activeName.value === '电子围栏') { if (activeName.value === '自定义识别区') { id = 1; } else if (activeName.value === '自定义禁飞区') { id = 2; @@ -176,6 +171,7 @@ const handleCoverDataUpdate = data => { selectDataList.value = data; if (selectDataList.value.length > 0) { loadDataToMap(selectDataList.value); } else { viewer.entities.removeAll(); @@ -246,7 +242,11 @@ zIndex: 99, }); }); focusOnAllFeatures(); if (!layerParams.value.isSingleLocating) { focusOnAllFeatures(); } viewInstance.value?.addLeftClickEvent(null, handleFenceClick); }; const focusOnAllFeatures = () => { @@ -259,7 +259,7 @@ flyVisual({ positionsData, viewer, multiple: 13, // 缩放倍数 multiple:activeName.value == '国土空间规划' ? 13 :4, // 缩放倍数 pitch: -90 }); }; @@ -267,22 +267,31 @@ const handleFenceClick = movement => { if (!viewer) return; layerParams.value.editDetailData = null; const pickedObject = viewer.scene.pick(movement.position); if (Cesium.defined(pickedObject) && pickedObject.id?.customType === 'fence_polygon') { const selectedEntity = pickedObject.id; const selectedData = selectedEntity.customInfo; // viewer.entities.values.forEach(entity => { if(activeName.value !== '国土空间规划') { const pickedObjects = viewer.scene.drillPick(movement.position, 10); // 遍历所有被点击的实体,找到围栏类型(customType: 'fence_polygon') let selectedEntity = null; for (let i = 0; i < pickedObjects.length; i++) { const pick = pickedObjects[i]; if (Cesium.defined(pick.id) && pick.id?.customType === 'fence_polygon') { selectedEntity = pick.id; break; } } // viewer.entities.values.forEach(entity => { // if (entity.customType === 'fence_polygon') { // entity.polygon.material = entity === selectedEntity // ? Cesium.Color.RED.withAlpha(0.5) // : Cesium.Color.YELLOW.withAlpha(0.5); // } // }); if (selectedEntity) { const selectedData = selectedEntity.customInfo; if (activeName.value !== '国土空间规划') { layerParams.value.editNest = true; // 显示右侧编辑组件 layerParams.value.decideWhetherToAddOrEdit = 2; // 标记为编辑模式 // 根据围栏类型设置fenceType if (activeName.value === '电子围栏') { layerParams.value.editNest = true; layerParams.value.decideWhetherToAddOrEdit = 2; if (activeName.value === '自定义识别区') { layerParams.value.fenceType = 1; } else if (activeName.value === '自定义禁飞区') { layerParams.value.fenceType = 2; @@ -292,6 +301,11 @@ layerParams.value.editingIsProhibited = true; } } }else { } }; let publicCesiumInstance = null; let viewer = null; @@ -303,11 +317,12 @@ dom: 'layMap', flatMode: false, terrain: true, layerMode: 4, contour: false, layerMode: 4, //轮廓线 dockOptions:{showDock:true}//机巢 }); viewer = publicCesiumInstance.getViewer(); viewInstance.value = publicCesiumInstance; viewInstance.value?.flyToContour(); const nanChangPosition = Cesium.Cartesian3.fromDegrees( 115.892151, // 经度 28.676493, // 纬度 @@ -394,13 +409,45 @@ } }; provide('layerParams', layerParams); // onMounted(() => { // initMap(); // cesiumContextMenu(); // getdataFolderApi(); // EventBus.on('deleteMapEntityById', deleteMapEntityById); // EventBus.on('deleteMapEntitiesByFolderId', deleteMapEntitiesByFolderId); // }); onMounted(() => { initMap(); cesiumContextMenu(); getdataFolderApi(); // 监听定位事件 EventBus.on('focusOnNode', (nodeData) => { focusOnNode(nodeData); }); EventBus.on('deleteMapEntityById', deleteMapEntityById); EventBus.on('deleteMapEntitiesByFolderId', deleteMapEntitiesByFolderId); }); //定位到指定节点 const focusOnNode = (nodeData) => { if (!viewer || !nodeData?.geo_data) return; // 解析节点坐标 const positions = parseGeoDataToPositions(nodeData.geo_data, nodeData.altitude); if (positions.length < 3) { return; } flyVisual({ positionsData: positions.map(pos => [pos.lng, pos.lat, pos.height || 0]), viewer, multiple:activeName.value == '国土空间规划' ? 13 :7, // 缩放倍数 pitch: -90 }); }; onBeforeUnmount(() => { destroyMap(); cesiumContextMenu(false); @@ -440,18 +487,6 @@ position: relative; width: 100%; height: 80vh; .warning { position: absolute; top: 234px; color: #fff; background: rgba(140, 0, 0, 0.4); .icon { display: flex; align-items: center; color: #ff1f1f; } } #layMap { width: 100%; height: 100%; src/views/system/user.vue
@@ -26,9 +26,9 @@ <el-button type="danger" plain icon="el-icon-delete" v-if="permission.user_delete && !auditMode" @click="handleDelete">删 除 </el-button> <el-button type="primary" plain icon="el-icon-operation" v-if="userInfo.role_name.includes('admin') && !auditMode" @click="handleAudit">审 核 </el-button> <!-- <el-button type="primary" plain icon="el-icon-operation"--> <!-- v-if="userInfo.role_name.includes('admin') && !auditMode" @click="handleAudit">审 核--> <!-- </el-button>--> <el-button type="success" plain icon="el-icon-check" v-if="userInfo.role_name.includes('admin') && auditMode" @click="handleAuditPass">通 过 </el-button> @@ -44,9 +44,9 @@ <el-button type="info" plain v-if="permission.user_reset && !auditMode" icon="el-icon-refresh" @click="handleReset">密码重置 </el-button> <el-button type="info" plain v-if="userInfo.role_name.includes('admin') && !auditMode" icon="el-icon-setting" @click="handlePlatform">平台配置 </el-button> <!-- <el-button type="info" plain v-if="userInfo.role_name.includes('admin') && !auditMode"--> <!-- icon="el-icon-setting" @click="handlePlatform">平台配置--> <!-- </el-button>--> <el-button type="info" plain v-if="userInfo.role_name.includes('admin') && !auditMode" icon="el-icon-coordinate" @click="handleLock">账号解封 </el-button> src/views/tickets/orderLog.vue
@@ -10,7 +10,7 @@ <el-col :span="4"> <el-form-item label="关键字:"> <el-input v-model="filters.key_word" placeholder="编号/名称/内容/姓名" clearable @keyup.enter="handleSearch" /> @keyup.enter="handleSearch" /> </el-form-item> <!-- <div class="search-bar-box-item">--> @@ -23,16 +23,16 @@ <el-col :span="8"> <el-form-item label="选择日期:"> <el-date-picker @change="handleSearch" v-model="filters.dateRange" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" :default-value="datePickerDefaultVal" /> range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" :default-value="datePickerDefaultVal" /> </el-form-item> </el-col> <el-col :span="4"> <el-form-item label="关联航线:"> <el-select @change="handleSearch" v-model="filters.file_id" placeholder="请选择关联航线" filterable clearable> clearable> <el-option v-for="item in wayLineList" :key="item.wayline_id" :label="item.name" :value="item.wayline_id" /> :value="item.wayline_id" /> </el-select> </el-form-item> </el-col> @@ -40,7 +40,7 @@ <el-form-item label="关联算法:"> <el-select @change="handleSearch" v-model="filters.ai_types" placeholder="关联算法" clearable> <el-option v-for="item in ai_types" :key="item.dictKey" :label="item.dictValue" :value="item.dictKey" /> :value="item.dictKey" /> </el-select> </el-form-item> @@ -55,7 +55,7 @@ <el-col :span="4"> <el-form-item label="工单状态:"> <el-select @change="handleSearch" v-model="filters.status" placeholder="请选择工单状态" clearable :disabled="activeTab !== 'all'"> :disabled="activeTab !== 'all'"> <el-option v-for="item in statuses" :key="item.value" :label="item.label" :value="item.value" /> </el-select> </el-form-item> @@ -65,8 +65,8 @@ <el-col :span="8"> <el-form-item label="工单周期:"> <el-date-picker @change="handleSearch" v-model="filters.cycleDateRange" type="daterange" range-separator="至" start-placeholder="工单周期开始日期" end-placeholder="工单周期结束日期" :default-value="datePickerDefaultVal" /> range-separator="至" start-placeholder="工单周期开始日期" end-placeholder="工单周期结束日期" :default-value="datePickerDefaultVal" /> </el-form-item> </el-col> <el-col :span="4"> @@ -79,7 +79,7 @@ <el-col :span="4"> <el-form-item label="执行时间:"> <el-time-picker @change="handleSearch" v-model="filters.deal_time" placeholder="请选择执行时间" prop="deal_time" value-format="HH:mm" :picker-options="{ prop="deal_time" value-format="HH:mm" :picker-options="{ selectableRange: '00:00 - 23:59', }" /> </el-form-item> @@ -95,8 +95,8 @@ <!-- 表格部分 --> <avue-crud class="ztzf-public-general-avue-crud" :data="tableData" :option="option" v-model:page="page" ref="crud" :table-loading="loading" @current-change="currentChange" @refresh-change="refreshChange" @on-load="onLoad" @search-change="searchChange" @size-change="sizeChange" v-if="activeTab === tab.name"> ref="crud" :table-loading="loading" @current-change="currentChange" @refresh-change="refreshChange" @on-load="onLoad" @search-change="searchChange" @size-change="sizeChange" v-if="activeTab === tab.name"> <template #job_info_num="{ row }"> <el-tooltip-copy :content="row.job_info_num" :showCopyText="true" textAlign="left"> {{ row.job_info_num }} @@ -111,7 +111,7 @@ <template #menu-left> <el-button v-if="hasAddBtnPermission() && activeTab != 'WAIT_AUDIT'" type="primary" icon="el-icon-plus" @click="handleAdd">新建工单 @click="handleAdd">新建工单 </el-button> <el-button type="success" plain icon="el-icon-download" @click="exportData">导出 </el-button> @@ -121,7 +121,7 @@ <div class="menu-custom-box"> <template v-if="row.status == 1"> <el-button v-if="hasPaddingBtnPermission()" type="text" icon="el-icon-view" @click="handleCheckDetail(row)">审核 @click="handleCheckDetail(row)">审核 </el-button> </template> <!-- v-if=" @@ -131,7 +131,7 @@ <template v-if="row.status == 1"> <!--待审核状态--> <el-button type="text" icon="el-icon-warning" v-if="hasRecallPaddingBtnPermission()" @click="orderLogRecall(row.id)">撤回 @click="orderLogRecall(row.id)">撤回 </el-button> <!-- <el-button type="text" icon="el-icon-view" @click="handleViewDetail(row)">详情</el-button> --> </template> @@ -140,7 +140,7 @@ <el-button type="text" icon="el-icon-warning" @click="rejectDetail(row.id)">驳回原因 </el-button> <el-button v-if="userInfo.user_id == row.create_user" type="text" icon="el-icon-view" @click="handleViewDetail(row)">编辑 @click="handleViewDetail(row)">编辑 </el-button> <el-button v-else type="text" icon="el-icon-view" @click="handleViewDetail(row)">详情 </el-button> @@ -176,7 +176,7 @@ <!-- 新建工单对话框 --> <el-dialog class="ztzf-dialog-mange" v-model="dialogVisible" title="新建工单" width="70%" :close-on-click-modal="false" @close="resetForm"> @close="resetForm"> <el-form :model="form" :rules="rules" ref="testform" label-width="100px"> <el-row :gutter="20"> <el-col :span="12"> @@ -188,13 +188,13 @@ <el-form-item label="关联航线" prop="file_id"> <el-select v-model="form.file_id" placeholder="请选择航线" filterable @change="getFlyingNestBy"> <el-option v-for="item in wayLineList" :key="item.wayline_id" :label="item.name" :value="item.wayline_id" /> :value="item.wayline_id" /> </el-select> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="安全返航真高" prop="rth_altitude" class="safe-height"> <el-input-number v-model="form.rth_altitude" :min="50" :max="300"></el-input-number> <el-input-number v-model="form.rth_altitude" :min="50" :max="500"></el-input-number> </el-form-item> </el-col> </el-row> @@ -203,15 +203,20 @@ <el-form-item label="关联机巢" prop="device_sns"> <el-select v-model="form.device_sns" placeholder="请选择机巢" multiple :disabled="!device_sns.length"> <el-option v-for="item in device_sns" :key="item.device_sn" :label="item.nickname" :value="item.device_sn" /> :value="item.device_sn" /> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-col :span="6"> <el-form-item label="关联算法" prop="ai_types"> <el-select v-model="form.ai_types" placeholder="请选择关联算法" multiple> <el-option v-for="item in ai_types" :key="item.dictKey" :label="item.dictValue" :value="item.dictKey" /> </el-select> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="自定义识别区" prop="enable_custom_area" class="safe-height"> <el-checkbox v-model="form.enable_custom_area"></el-checkbox> </el-form-item> </el-col> </el-row> @@ -220,14 +225,14 @@ <el-col :span="12"> <el-form-item label="工单内容" prop="content"> <el-input type="textarea" v-model="form.content" rows="4" placeholder="请输入工单内容" maxlength="255" show-word-limit></el-input> show-word-limit></el-input> </el-form-item> </el-col> <el-col :span="8"> <el-form-item label="周期频次" prop="date_range"> <el-date-picker v-model="form.date_range" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" :disabled-date="disabledDate" /> end-placeholder="结束日期" :disabled-date="disabledDate" /> </el-form-item> </el-col> <el-col :span="4"> @@ -239,7 +244,7 @@ </div> <div class="flex-1"> <el-time-picker style="width: 100px" v-model="form.deal_time" prop="deal_time" value-format="HH:mm" :picker-options="{ :picker-options="{ selectableRange: '00:00 - 23:59', }" /> </div> @@ -272,14 +277,14 @@ <!-- 工单详情对话框 --> <el-dialog class="ztzf-dialog-mange" align-center v-model="detailVisible" :title="detailTitle" width="70%" :close-on-click-modal="false" @close="resetForm"> :close-on-click-modal="false" @close="resetForm"> <div class="event-title-center">{{ form.name }}</div> <el-form :model="form" :rules="rules" ref="testform" label-width="100px"> <div class="custom-steps-container"> <!-- 标题行 --> <div class="steps-titles" v-if="filters.status !== '0'"> <div v-for="(record, index) in form.record_list" :class="{ active: record.user_id >= 0 }" :key="index" class="step-title"> class="step-title"> {{ record.status_str }} </div> </div> @@ -311,16 +316,16 @@ <el-col :span="6"> <el-form-item label="关联航线" prop="file_id"> <el-select v-model="form.file_id" placeholder="请选择航线" @change="getFlyingNestBy" filterable :disabled="detailTitle === '工单详情'"> :disabled="detailTitle === '工单详情'"> <el-option v-for="item in wayLineList" :key="item.wayline_id" :label="item.name" :value="item.wayline_id" /> :value="item.wayline_id" /> </el-select> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="安全返航真高" prop="rth_altitude" class="safe-height"> <el-input-number :disabled="detailTitle === '工单详情'" v-model="form.rth_altitude" :min="50" :max="300"></el-input-number> :max="500"></el-input-number> </el-form-item> </el-col> </el-row> @@ -329,15 +334,20 @@ <el-form-item label="关联机巢" prop="device_sns"> <el-select v-model="form.device_sns" placeholder="请选择机巢" multiple :disabled="detailTitle === '工单详情'"> <el-option v-for="item in device_sns" :key="item.device_sn" :label="item.nickname" :value="item.device_sn" /> :value="item.device_sn" /> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-col :span="6"> <el-form-item label="关联算法" prop="ai_types"> <el-select v-model="form.ai_types" placeholder="请选择关联算法" multiple :disabled="detailTitle === '工单详情'"> <el-option v-for="item in ai_types" :key="item.dictKey" :label="item.dictValue" :value="item.dictKey" /> </el-select> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="自定义识别区" prop="enable_custom_area" class="safe-height"> <el-checkbox v-model="form.enable_custom_area"></el-checkbox> </el-form-item> </el-col> </el-row> @@ -352,7 +362,7 @@ <el-col :span="6"> <el-form-item label="周期频次" prop="date_range"> <el-date-picker v-model="form.date_range" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" :disabled-date="disabledDate" :disabled="detailTitle === '工单详情'" /> end-placeholder="结束日期" :disabled-date="disabledDate" :disabled="detailTitle === '工单详情'" /> </el-form-item> </el-col> @@ -364,7 +374,7 @@ <el-col :span="3"> <el-time-picker class="timeStyle" :style="{ width: pxToRem(140) }" v-model="form.deal_time" prop="deal_time" value-format="HH:mm" :picker-options="{ value-format="HH:mm" :picker-options="{ selectableRange: '00:00 - 23:59', }" :disabled="detailTitle === '工单详情'" /> </el-col> @@ -374,7 +384,7 @@ <el-col :span="12"> <el-form-item label="工单内容" prop="content"> <el-input type="textarea" v-model="form.content" rows="4" placeholder="请输入工单内容" maxlength="255" show-word-limit :disabled="detailTitle === '工单详情'"></el-input> show-word-limit :disabled="detailTitle === '工单详情'"></el-input> </el-form-item> </el-col> </el-row> @@ -388,20 +398,20 @@ <template #footer> <div class="dialog-footer"> <el-button type="danger" v-if="form.status == 0 || (form.status == 2 && userInfo.user_id == form.create_user)" @click="submitForm(1)" icon="el-icon-position">发布 @click="submitForm(1)" icon="el-icon-position">发布 </el-button> <el-button type="primary" v-if="form.status == 0 || (form.status == 2 && userInfo.user_id == form.create_user)" @click="submitForm(0)" icon="el-icon-plus">保存 v-if="form.status == 0 || (form.status == 2 && userInfo.user_id == form.create_user)" @click="submitForm(0)" icon="el-icon-plus">保存 </el-button> <!-- <el-button type="primary" v-if="form.status == 0 || userInfo.user_id == form.create_user" @click="submitForm(0)">保存</el-button> --> <el-button type="primary" v-if="form.status == 1 && hasPaddingBtnPermission()" @click="orderLogPass(form.id)">通过 @click="orderLogPass(form.id)">通过 </el-button> <el-button type="danger" v-if="form.status == 1 && hasRejectionBtnPermission()" @click="orderLogReject(form.id)">驳回 @click="orderLogReject(form.id)">驳回 </el-button> </div> </template> @@ -409,14 +419,14 @@ <!-- 工单详情 --> <el-dialog class="ztzf-dialog-mange" align-center v-model="detailVisibleCopy" title="工单详情" width="70%" :close-on-click-modal="false" @close="resetForm"> :close-on-click-modal="false" @close="resetForm"> <div class="event-title-center">{{ form.name }}</div> <el-form :model="form" ref="testform" label-width="100px"> <div class="custom-steps-container"> <!-- 标题行 --> <div class="steps-titles" v-if="filters.status !== '0'"> <div v-for="(record, index) in form.record_list" :class="{ active: record.user_id >= 0 }" :key="index" class="step-title"> class="step-title"> {{ record.status_str }} </div> </div> @@ -449,13 +459,13 @@ <el-form-item label="关联航线" prop="file_id"> <el-select v-model="form.file_id" placeholder="请选择航线" :disabled="true"> <el-option v-for="item in wayLineList" :key="item.wayline_id" :label="item.name" :value="item.wayline_id" /> :value="item.wayline_id" /> </el-select> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="安全返航真高" prop="rth_altitude" class="safe-height"> <el-input-number :disabled="true" v-model="form.rth_altitude" :min="50" :max="300"></el-input-number> <el-input-number :disabled="true" v-model="form.rth_altitude" :min="50" :max="500"></el-input-number> </el-form-item> </el-col> </el-row> @@ -465,15 +475,20 @@ <el-form-item label="关联机巢" prop="device_sns"> <el-select v-model="form.device_sns" placeholder="请选择机巢" multiple :disabled="true"> <el-option v-for="item in device_sns" :key="item.device_sn" :label="item.nickname" :value="item.device_sn" /> :value="item.device_sn" /> </el-select> </el-form-item> </el-col> <el-col :span="12"> <el-col :span="6"> <el-form-item label="关联算法" prop="ai_types"> <el-select v-model="form.ai_types" placeholder="请选择关联算法" multiple :disabled="true"> <el-option v-for="item in ai_types" :key="item.dictKey" :label="item.dictValue" :value="item.dictKey" /> </el-select> </el-form-item> </el-col> <el-col :span="6"> <el-form-item label="自定义识别区" prop="enable_custom_area" class="safe-height"> <el-checkbox v-model="form.enable_custom_area"></el-checkbox> </el-form-item> </el-col> </el-row> @@ -488,7 +503,7 @@ <el-col :span="6"> <el-form-item label="周期频次" prop="date_range"> <el-date-picker v-model="form.date_range" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" :disabled="true" /> end-placeholder="结束日期" :disabled="true" /> </el-form-item> </el-col> <el-col :span="3"> @@ -499,7 +514,7 @@ <el-col :span="3"> <el-time-picker style="width: 100px" v-model="form.deal_time" prop="deal_time" :disabled="true" value-format="HH:mm" :picker-options="{ value-format="HH:mm" :picker-options="{ selectableRange: '00:00 - 23:59', }" /> </el-col> @@ -509,7 +524,7 @@ <el-col :span="12"> <el-form-item label="工单内容" prop="content"> <el-input type="textarea" v-model="form.content" rows="2" placeholder="请输入工单内容" maxlength="255" show-word-limit :readonly="true" :disabled="true"></el-input> show-word-limit :readonly="true" :disabled="true"></el-input> </el-form-item> </el-col> </el-row> @@ -524,16 +539,16 @@ <template #footer> <div class="dialog-footer"> <el-button type="danger" v-if="form.status == 0 || (form.status == 2 && userInfo.user_id == form.create_user)" @click="submitForm(1)" icon="el-icon-position">发布 @click="submitForm(1)" icon="el-icon-position">发布 </el-button> <!-- <el-button type="primary" v-if="form.status == 0 || userInfo.user_id == form.create_user" @click="submitForm(0)">保存</el-button> --> <el-button type="primary" v-if="form.status == 1 && hasPaddingBtnPermission()" @click="orderLogPass(form.id)" icon="el-icon-check">通过 icon="el-icon-check">通过 </el-button> <el-button type="danger" v-if="form.status == 1 && hasRejectionBtnPermission()" @click="orderLogReject(form.id)" icon="el-icon-close">驳回 @click="orderLogReject(form.id)" icon="el-icon-close">驳回 </el-button> <el-button @click="detailVisibleCopy = false" icon="el-icon-circle-close">取消</el-button> </div> @@ -555,7 +570,8 @@ orderLogExport, jobStatusNum, userPublish, deleteOrderLog deleteOrderLog, getWaylineMaxTerrainHeight } from '@/api/tickets/orderLog' import { getTicketInfo } from '@/api/tickets/ticket' import { getDictionaryByCode } from '@/api/system/dictbiz' @@ -810,9 +826,13 @@ return CircleClose }, ...mapGetters(['userInfo', 'permission']), lastHeight () { const height = this.$store.state.common.lastHeight return Number(height) || 0 // 确保是数字类型 safeHeight () { const safeHeight = this.$store.state.common.safeHeight return Number(safeHeight) || 0 // 确保是数字类型 }, positionsArr () { const positionsArr = this.$store.state.common.positionsArr return positionsArr || [] // 确保是数字类型 }, filteredTabs () { // rejection_and_draft 权限控制“已驳回”和“草稿”tab @@ -992,15 +1012,22 @@ const maxItem = checkedList.reduce((max, item) => { return item.drone_height > max.drone_height ? item : max; }); console.log(maxItem.drone_height, '高度'); console.log(this.lastHeight, '最后一点高度'); let result = _.round((maxItem.drone_height + this.form.rth_altitude), 2); // 返航绝对高度 let backHeight = this.form.rth_altitude // + maxItem.drone_height console.log(backHeight,'backHeight') // 安全起飞高度 let positions = this.positionsArr positions.unshift({latitude: maxItem.latitude, longitude: maxItem.longitude, height:maxItem.drone_height }) positions.push({latitude: maxItem.latitude, longitude: maxItem.longitude, height:maxItem.drone_height}) let resultHeight = 0 await getWaylineMaxTerrainHeight(positions).then(res => { resultHeight = this.safeHeight > (res.data.data + 30) ? this.safeHeight : (res.data.data + 30) }) let valueHeight = _.round(backHeight - resultHeight, 2) console.log('打印比较值', valueHeight) let resultHeight = this.lastHeight - maxItem.drone_height let resultH = _.round(resultHeight, 2) if (this.lastHeight > result) { this.$confirm(`当前返航高度存在安全隐患,建议调整为${resultH}米以上后进行发布`, '提示', { if (valueHeight < 0) { this.$confirm(`当前返航高度存在安全隐患,建议调整为${resultHeight}米以上后进行发布`, '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' @@ -1190,10 +1217,10 @@ // 更新机巢列表 this.device_sns = data.device_map_infos;// data.device_list; this.permission && (this.permission.order_log_review || this.permission.order_log_recall) && (data.status == 1 || data.status == 3 || (data.status == 2 && this.userInfo.user_id !== this.form.create_user)) (this.permission.order_log_review || this.permission.order_log_recall) && (data.status == 1 || data.status == 3 || (data.status == 2 && this.userInfo.user_id !== this.form.create_user)) ? (this.detailTitle = '工单详情') : (this.detailTitle = '编辑工单') @@ -1331,7 +1358,7 @@ if (this.$refs.MapContainer && this.$refs.MapContainer.initAddEntity) { this.$refs.MapContainer.initAddEntity('polyline', { url: `${import.meta.env.VITE_APP_AIRLINE_URL + currentLine.object_key }?_t=${new Date().getTime()}`, }?_t=${new Date().getTime()}`, type: currentLine.wayline_type, cb, infos src/views/tickets/ticket.vue
@@ -134,7 +134,6 @@ collapse-tags-tooltip clearable @check="handleCheck" @node-click="handleSFNodeClick" @clear="handleClear" /> </el-form-item> @@ -207,6 +206,7 @@ @click="openReviewDialog" >批量审核 </el-button> <el-button v-if="permissionList.exportBtn" type="success" @@ -233,7 +233,12 @@ >详情 </el-button> </template> <template v-if="row.status === 4 && activeTab === 'completed'"> <el-button type="text" icon="el-icon-download" @click="exportTheTick(row)" >导出 </el-button> </template> <template v-if="permission.tickets_repeat_review"> <el-button v-if="row.status === 4 && row.isReview !== 1" @@ -1011,6 +1016,7 @@ getStepInfo, getReviewById, getCreateEventJob, exportTheTicket } from '@/api/tickets/ticket'; import { getSFDictionaryTree } from '@/api/job/task'; import { export_json_to_excel } from '@/utils/exportExcel'; @@ -1108,7 +1114,7 @@ { label: '工单编号', prop: 'orderNumber', width: 170 }, { label: '工单名称', prop: 'orderName', width: 150, overHidden: true, tooltip: true }, { label: '所属部门', prop: 'department', overHidden: true, tooltip: true }, { label: '发起时间', prop: 'startTime', width: 160, overHidden: true }, { label: '发起任务时间', prop: 'startTime', width: 160, overHidden: true }, { label: '关联算法', prop: 'aiType', overHidden: true, tooltip: true }, { label: '工单类型', @@ -1788,7 +1794,8 @@ try { const currentTab = this.tabs.find(tab => tab.name === this.activeTab); const params = { word_order_types: this.filters.type || undefined, // word_order_types: this.filters.type || undefined, ai_types: this.filters.type || undefined, // 算法使用该字段筛选 status: currentTab?.name === 'myTickets' ? undefined @@ -1838,7 +1845,7 @@ orderName: item.event_name, department: this.departments.find(d => d.value === item.dept_id)?.label || item.dept_name, startTime: item.create_time, startTime: item.job_create_time || '/', aiType: item.ai_types, content: item.content, // 将后端返回的 content 映射为 content type: item.work_order_type_dict_key, @@ -1847,7 +1854,7 @@ ? `${longitude.toFixed(6)}, ${latitude.toFixed(6)}` : '未知位置', address: item.address, creator: item.event_num?.slice(0, 2) === 'AI' ? 'AI 小飞' : item.create_user, creator: item.event_num?.slice(0, 2) === 'AI' ? '智飞agent' : item.create_user, handler: item.update_user || '未分配', status: Number(item.status || 0), // 保存原始字段 @@ -3188,6 +3195,23 @@ }); } }, // 导出工单报表 exportTheTick(row){ const params = { num:row.orderNumber } exportTheTicket(params).then(res=>{ const elink = document.createElement('a') elink.download = row.orderName + '.docx' elink.style.display = 'none' const blob = new Blob([res.data]) elink.href = URL.createObjectURL(blob) document.body.appendChild(elink) elink.click() document.body.removeChild(elink) }) this.$message.success('数据导出成功'); } }, activated() { this.handleReset();