From b5e2aaaba43d7456ab6ffedefa16c97a22cbe479 Mon Sep 17 00:00:00 2001
From: 罗广辉 <guanghui.luo@foxmail.com>
Date: Fri, 21 Nov 2025 18:40:01 +0800
Subject: [PATCH] Merge branch 'feature/v8.0/8.0.2' into prod

---
 src/views/tickets/ticket.vue                        |   30 ++
 src/hooks/useRouteLine/useRouteLine.js              |   11 
 src/views/layerManagement/components/rightEdit.vue  |    6 
 src/views/layerManagement/components/folderFile.vue |    1 
 src/views/layerManagement/components/leftList.vue   |   84 +++++--
 src/views/device/airport.vue                        |  121 +++++++----
 src/api/device/device.js                            |   18 +
 src/api/tickets/orderLog.js                         |   10 
 src/views/job/components/SearchBox.vue              |   84 +++++---
 .env.development                                    |    2 
 src/views/dataCenter/components/searchData.vue      |    4 
 src/api/tickets/ticket.js                           |    9 
 src/views/tickets/orderLog.vue                      |   93 ++++++--
 src/store/modules/common.js                         |   10 
 src/views/dataCenter/dataCenter.vue                 |    2 
 src/views/layerManagement/index.vue                 |   85 +++++---
 16 files changed, 381 insertions(+), 189 deletions(-)

diff --git a/.env.development b/.env.development
index b574a32..c452ea6 100644
--- a/.env.development
+++ b/.env.development
@@ -22,7 +22,7 @@
 # 服务地址
 VITE_APP_URL = https://wrj.shuixiongit.com/api
 #VITE_APP_URL= http://192.168.1.168
-#VITE_APP_URL= http://192.168.1.33
+# VITE_APP_URL= http://192.168.1.33
 #VITE_APP_URL= http://192.168.1.204
 #新大屏地址
 VITE_APP_DASHBOARD_URL = 'https://wrj.shuixiongit.com/command-center-dashboard/'
diff --git a/src/api/device/device.js b/src/api/device/device.js
index da62856..84fdf2c 100644
--- a/src/api/device/device.js
+++ b/src/api/device/device.js
@@ -167,4 +167,20 @@
     method: 'post',
     data: param
   })
-}
\ No newline at end of file
+}
+// 初始化机场附近五公
+export const initAirportTerrainHeight = (params) => {
+  return request({
+    url: `/drone-odm/odmTaskInfo/odmTaskInfo/initAirportTerrainHeight`,
+    method: 'get',
+    params: params
+  })
+}
+// 删除机场附近五公里
+export const deleteAirportTerrainHeight = (params) => {
+  return request({
+    url: `/drone-odm/odmTaskInfo/odmTaskInfo/deleteAirportTerrainHeight`,
+    method: 'get',
+    params: params
+  })
+}
diff --git a/src/api/tickets/orderLog.js b/src/api/tickets/orderLog.js
index 2179bc6..037bd05 100644
--- a/src/api/tickets/orderLog.js
+++ b/src/api/tickets/orderLog.js
@@ -88,11 +88,11 @@
 
 // 获取返航最高 高度
 export const getWaylineMaxTerrainHeight = data => {
-	return request({
-		url: `/drone-odm/odmTaskInfo/odmTaskInfo/getWaylineMaxTerrainHeight`,
-		method: 'post',
-		data,
-	})
+  return request({
+    url: `/drone-odm/odmTaskInfo/odmTaskInfo/getWaylineMaxTerrainHeight`,
+    method: 'post',
+    data,
+  })
 }
 
 
diff --git a/src/api/tickets/ticket.js b/src/api/tickets/ticket.js
index 9b1eeb8..a8cf0cb 100644
--- a/src/api/tickets/ticket.js
+++ b/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,
+	})
 }
\ No newline at end of file
diff --git a/src/hooks/useRouteLine/useRouteLine.js b/src/hooks/useRouteLine/useRouteLine.js
index 6e4f50e..32c3d3a 100644
--- a/src/hooks/useRouteLine/useRouteLine.js
+++ b/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: ''
 		}
 	}
 
diff --git a/src/store/modules/common.js b/src/store/modules/common.js
index c12ef73..ce4a88f 100644
--- a/src/store/modules/common.js
+++ b/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
diff --git a/src/views/dataCenter/components/searchData.vue b/src/views/dataCenter/components/searchData.vue
index 1988466..8a4b29c 100644
--- a/src/views/dataCenter/components/searchData.vue
+++ b/src/views/dataCenter/components/searchData.vue
@@ -115,8 +115,8 @@
         </el-form-item>
         </el-col>
         <el-col :span="4">
-          <el-button  type="primary" icon="el-icon-search" @click="handleSearch">搜索</el-button>
-          <el-button  icon="el-icon-refresh" @click="handleReset">清空</el-button>
+          <el-button :disabled="viewDetailsDisabled" type="primary" icon="el-icon-search" @click="handleSearch">搜索</el-button>
+          <el-button :disabled="viewDetailsDisabled" icon="el-icon-refresh" @click="handleReset">清空</el-button>
         </el-col>
       </el-row>
       <div class="search-first">
diff --git a/src/views/dataCenter/dataCenter.vue b/src/views/dataCenter/dataCenter.vue
index 32c1d85..157b928 100644
--- a/src/views/dataCenter/dataCenter.vue
+++ b/src/views/dataCenter/dataCenter.vue
@@ -242,7 +242,7 @@
             <div>
               拍摄机巢:{{ dialogDetailList?.nestName ? dialogDetailList?.nestName : '--' }}
             </div>
-            <div v-if="dialogDetailList?.resultType != 4">
+            <div v-if="dialogDetailList?.resultType == 0">
               照片位置:{{ _.round(dialogDetailList?.longitude, 3) }},{{
                 _.round(dialogDetailList?.latitude, 3)
               }}
diff --git a/src/views/device/airport.vue b/src/views/device/airport.vue
index 31f1282..fdddfba 100644
--- a/src/views/device/airport.vue
+++ b/src/views/device/airport.vue
@@ -85,7 +85,13 @@
                 </el-button>
               </el-dropdown-item>
               <el-dropdown-item command="e"><el-button type="primary" text icon="el-icon-document-delete"
-                  v-if="permission.fly_device_offline" @click.stop="handleDeviceOffline(scope.row)">注销</el-button>
+                  v-if="permission.fly_device_offline && scope.row.mode_code !==0" @click.stop="handleDeviceOffline(scope.row)">注销</el-button>
+              </el-dropdown-item>
+              <el-dropdown-item command="e">
+                <el-button type="primary" text icon="el-icon-document-delete" v-if="permission.fly_device_offline"
+                  :disabled="scope.row.dem_status === 1" @click.stop="handleStatus(scope.row)">
+                  {{ scope.row.dem_status === 0 ? '初始化地形' : (scope.row.dem_status === 1 ? '初始化中' : '更新地形') }}
+                </el-button>
               </el-dropdown-item>
             </el-dropdown-menu>
           </template>
@@ -222,7 +228,9 @@
   getDevices,
   deviceOffline,
   devicesUpdate,
-  devicesUpAndDown
+  devicesUpAndDown,
+  initAirportTerrainHeight,
+  deleteAirportTerrainHeight
 } from '@/api/device/device'
 import { deleteByOssId, addOrUpdate as addOrUpdateOssBind } from '@/api/device/ossBind'
 import { getListPage as getOssList } from '@/api/resource/oss'
@@ -242,7 +250,7 @@
     DevicePerShare,
     DockControlPanel,
   },
-  data () {
+  data() {
     // const self = this
 
     const validatePass = (rule, value, callback) => {
@@ -781,7 +789,7 @@
 
   computed: {
     ...mapGetters(['permission', 'userInfo']),
-    permissionList () {
+    permissionList() {
       return {
         addBtn: this.validData(this.permission.air_add, true),
         viewBtn: this.validData(this.permission.air_view, true),
@@ -789,7 +797,7 @@
         editBtn: this.validData(this.permission.air_edit, true),
       }
     },
-    ids () {
+    ids() {
       let ids = []
       this.selectionList.forEach(ele => {
         ids.push(ele.id)
@@ -797,7 +805,7 @@
       return ids.join(',')
     },
   },
-  destroyed () {
+  destroyed() {
     this.treeResolveMap.clear()
     this.websocketMap.forEach((k, v) => {
       if (null != v) {
@@ -807,17 +815,17 @@
       }
     })
   },
-  mounted () {
+  mounted() {
     getRegionTreeAll({ parentCode: this.userInfo.detail.areaCode }).then(res => {
       const column = this.findObject(this.option.column, 'area_code')
       column.dicData = res.data.data ? [res.data.data] : []
     })
   },
   methods: {
-    getDockModeText (value) {
+    getDockModeText(value) {
       return EDockModeText[value] || ''
     },
-    getModelText (value) {
+    getModelText(value) {
       let txt = '离线'
       if (value === 0 || value === 4) {
         txt = '在线'
@@ -831,7 +839,7 @@
       return txt
     },
     // 关闭所有的webscoket
-    closeAllWebsoket () {
+    closeAllWebsoket() {
       this.websocketMap.forEach((k, v) => {
         if (null != v) {
           v.close()
@@ -841,7 +849,7 @@
       })
     },
     // 连接webSocket
-    connectWebSocket (data) {
+    connectWebSocket(data) {
       const webSocketId = data.workspace_id
       if (!this.webSocketIdSet.has(webSocketId)) {
         // 防止重复连接
@@ -851,14 +859,14 @@
         this.useConnectWebSocket1(webSocketId, webSorketUrl)
       }
     },
-    useConnectWebSocket1 (webSocketId, url) {
+    useConnectWebSocket1(webSocketId, url) {
       const websocket = new ConnectWebSocket(url)
       // 加入 webscoket map
       this.websocketMap.set(webSocketId, websocket)
       websocket?.registerMessageHandler(this.messageHandler)
       websocket?.initSocket()
     },
-    async messageHandler (payload) {
+    async messageHandler(payload) {
       if (!payload) {
         return
       }
@@ -883,7 +891,7 @@
         }
       })
     },
-    init () {
+    init() {
       (this.page = {
         pageSize: 10,
         currentPage: 1,
@@ -892,7 +900,7 @@
         this.onLoad(this.page)
     },
     // 打开权限分享页面
-    handleOpenDevicePerShare (row) {
+    handleOpenDevicePerShare(row) {
       var that = this
       this.devicePerShareVisible = true
       this.$nextTick(() => {
@@ -900,21 +908,38 @@
       })
     },
     // 打开远程调试
-    handleOpenRemoteDebugging (row) {
+    handleOpenRemoteDebugging(row) {
       this.curDeviceInfo = row
       this.remoteDebuggingShow = true
       this.operateTitle = row.nickname + ' - ' + row.device_sn
     },
 
     // 设备注销
-    handleDeviceOffline (row) {
+    handleDeviceOffline(row) {
       this.cancenOperate = true
       this.cancelSNName = row.nickname
       this.cancenOperateRow = row
-
     },
 
-    cancenOperateDo () {
+    // 设备
+    handleStatus(row) {
+      // dem_status
+      if (row.dem_status === 2 || row.dem_status === 3) {
+        deleteAirportTerrainHeight({ dockSn: row.device_sn }).then(res => {
+          initAirportTerrainHeight({ dockSn: row.device_sn, centerLon: row.longitude, centerLat: row.latitude, }).then(res => {
+            ElMessage.success('操作成功')
+            this.init()
+          })
+        })
+      } else {
+        initAirportTerrainHeight({ dockSn: row.device_sn, centerLon: row.longitude, centerLat: row.latitude,  }).then(res => {
+            ElMessage.success('操作成功')
+            this.init()
+        })
+      }
+    },
+
+    cancenOperateDo() {
       this.cancenOperate = false
       this.deleteFlag = false
       deviceOffline(this.cancenOperateRow.device_sn, true).then(res => {
@@ -927,7 +952,7 @@
     },
 
     // 打开存储对象配置页面
-    handleOpenOssSet (row) {
+    handleOpenOssSet(row) {
       var that = this
       this.deviceSn = row.device_sn
       this.ossSetBox = true
@@ -943,7 +968,7 @@
       })
     },
     // 打开固件管理页面
-    handleOpenFirmwarm (row) {
+    handleOpenFirmwarm(row) {
       var that = this
       this.firmwareManageVisible = true
       this.$nextTick(() => {
@@ -951,7 +976,7 @@
       })
     },
     // 设备下线
-    dockNotLine (row) {
+    dockNotLine(row) {
       let txt = row.hidden_flag === 1 ? '上线' : '下线'
       ElMessageBox.confirm(`确定${txt}该设备吗?`, '提示', {
         confirmButtonText: '确定',
@@ -972,7 +997,7 @@
         .catch(() => { })
     },
     // 版本回退点击事件
-    rollFirmware (row) {
+    rollFirmware(row) {
       if (row.status == 0) {
         this.$message({
           type: 'warning',
@@ -1021,7 +1046,7 @@
       })
     },
     // 比较版本大小
-    compare (version1, version2) {
+    compare(version1, version2) {
       let arr1 = version1.split('.')
       let arr2 = version2.split('.')
       let length = Math.max(arr1.length, arr2.length)
@@ -1035,7 +1060,7 @@
       return 0
     },
     // 版本回退
-    rollFirmwareConfirm () {
+    rollFirmwareConfirm() {
       // 无人机升级/回滚
       if (this.rollFirmVersion == '') {
         this.$message({
@@ -1110,7 +1135,7 @@
       })
     },
     // 升级固件按钮事件
-    updateFirmware (row) {
+    updateFirmware(row) {
       if (row.status == 0) {
         this.$message({
           type: 'warning',
@@ -1142,7 +1167,7 @@
       })
     },
     // 确定升级固件版本
-    updateFirmwareConfirm () {
+    updateFirmwareConfirm() {
       var that = this
       var arr = []
       arr.push(that.firmwareInfo)
@@ -1158,13 +1183,13 @@
         done()
       })
     },
-    handleOssSetClose () {
+    handleOssSetClose() {
       this.ossEnd = ''
       this.deviceSn = ''
       this.bindOssId = null
     },
     // 存储对象配置确定
-    ossSetConfirm () {
+    ossSetConfirm() {
       var that = this
       // 判断是否已选择
       if (!this.ossEnd) {
@@ -1191,7 +1216,7 @@
       })
     },
     // 取消配置
-    cancelOssSetConfirm () {
+    cancelOssSetConfirm() {
       var that = this
       // 判断是否已有配置
       if (!this.bindOssId) {
@@ -1211,13 +1236,13 @@
       })
     },
     // 取消按钮 oss
-    cancelOssSet () {
+    cancelOssSet() {
       this.ossEnd = ''
       this.deviceSn = ''
       this.bindOssId = null
       this.ossSetBox = false
     },
-    rowSave (row, done, loading) {
+    rowSave(row, done, loading) {
       let areaCode = row.area_code
       if (Array.isArray(areaCode)) {
         areaCode = areaCode[areaCode.length - 1]
@@ -1242,7 +1267,7 @@
         }
       )
     },
-    rowUpdate (row, index, done, loading) {
+    rowUpdate(row, index, done, loading) {
       const submitData = {
         ...row,
         area_code: row.area_code.split(',').pop(),
@@ -1289,7 +1314,7 @@
         }
       )
     },
-    rowDel (row) {
+    rowDel(row) {
       this.$confirm('确定将选择数据删除?', {
         confirmButtonText: '确定',
         cancelButtonText: '取消',
@@ -1306,24 +1331,24 @@
           })
         })
     },
-    searchReset () {
+    searchReset() {
       this.query = {}
       this.onLoad(this.page)
     },
-    searchChange (params, done) {
+    searchChange(params, done) {
       this.query = params
       this.page.currentPage = 1
       this.onLoad(this.page, params)
       done()
     },
-    selectionChange (list) {
+    selectionChange(list) {
       this.selectionList = list
     },
-    selectionClear () {
+    selectionClear() {
       this.selectionList = []
       this.$refs.crud.toggleSelection()
     },
-    handleDelete () {
+    handleDelete() {
       if (this.selectionList.length === 0) {
         this.$message.warning('请选择至少一条数据')
         return
@@ -1345,7 +1370,7 @@
           this.$refs.crud.toggleSelection()
         })
     },
-    async getFullAreaCode (areaCode) {
+    async getFullAreaCode(areaCode) {
       console.log('getFullAreaCode', areaCode)
       if (!areaCode) return ''
 
@@ -1364,7 +1389,7 @@
         return `${provinceCode},${cityCode},${code}`
       }
     },
-    beforeOpen (done, type) {
+    beforeOpen(done, type) {
       if (['edit', 'view'].includes(type)) {
         getDetail(this.form.id).then(async res => {
           const data = res.data.data
@@ -1382,16 +1407,16 @@
         })
       }
     },
-    currentChange (currentPage) {
+    currentChange(currentPage) {
       this.page.currentPage = currentPage
     },
-    sizeChange (pageSize) {
+    sizeChange(pageSize) {
       this.page.pageSize = pageSize
     },
-    refreshChange () {
+    refreshChange() {
       this.onLoad(this.page, this.query)
     },
-    onLoad (page, params = {}) {
+    onLoad(page, params = {}) {
       // this.closeAllWebsoket();
       const { releaseTimeRange } = this.query
       let values = {
@@ -1426,7 +1451,7 @@
         this.selectionClear()
       })
     },
-    treeLoad (tree, treeNode, resolve) {
+    treeLoad(tree, treeNode, resolve) {
       // 保存resolve
       this.treeResolveMap.set(tree.child_sn, resolve)
       var params = {
@@ -1458,7 +1483,7 @@
     //     })
     // },
     // 设置无人机操作密码
-    setOperatePasswordConfirm () {
+    setOperatePasswordConfirm() {
       var that = this
       this.$refs.passwordForm.validate(valid => {
         if (valid) {
@@ -1485,7 +1510,7 @@
       })
     },
     // 操作密码设置
-    handleOperatePassword (row) {
+    handleOperatePassword(row) {
       this.operatePasswordSetBox = true
       this.passwordForm.id = row.id
     },
diff --git a/src/views/job/components/SearchBox.vue b/src/views/job/components/SearchBox.vue
index d69206c..6f6e0af 100644
--- a/src/views/job/components/SearchBox.vue
+++ b/src/views/job/components/SearchBox.vue
@@ -94,21 +94,19 @@
             @clear="handleClear"
           />
         </el-form-item>
-        <!-- <el-form-item label="所属部门:" v-if="isExpand">
-          <el-select
-            :teleported="false"
+        <el-form-item label="所属部门:" v-if="isExpand">
+          <el-tree-select
+            popper-class="custom-tree-select"
             v-model="searchForm.create_dept"
-            placeholder="请选择"
+            :data="deptData"
+            :default-expanded-keys="[searchForm.create_dept]"
+            check-strictly
+            node-key="id"
+            :props="treeDeptProps"
             clearable
-          >
-            <el-option
-              v-for="item in deptData"
-              :key="item.id"
-              :label="item.deptName"
-              :value="item.id"
-            />
-          </el-select>
-        </el-form-item> -->
+            @change="(id) => handleDeptNodeClick({id})"
+          />
+        </el-form-item>
         <el-form-item label="任务状态:" v-if="isExpand">
           <el-select
           style="z-index: 1000"
@@ -149,6 +147,7 @@
 import { pxToRem } from '@/utils/rem';
 import { ElMessage } from 'element-plus';
 import { deptsByAreaCode, getSFDictionaryTree  } from '@/api/job/task';
+import { getDeptLazyTree } from '@/api/system/dept'
 // import TaskAlgorithmBusiness from './TaskAlgorithmBusiness.vue';
 import dayjs from 'dayjs';
 import { useStore } from 'vuex';
@@ -213,10 +212,8 @@
 let deptData = ref([]);
 // 所属部门信息
 const getDeptsByAreaCode = () => {
-  deptsByAreaCode(searchForm.area_code).then(res => {
-    if (res.code !== 0) {
-      deptData.value = res.data.data;
-    }
+  getDeptLazyTree({ parentId: 0, level: 0 }).then(res => {
+    deptData.value = res.data.data;
   });
 };
 
@@ -225,20 +222,43 @@
 // 机巢
 let machineData = ref([]);
 
+// 记录选中行政区划值
+let area_code_log = ref('')
+// 记录选中部门
+let dept_id_log = ref('')
+
 // 部门下得机巢
 const requestDockInfo = () => {
   getRegionTreeAll({ parentCode: userAreaCode.value }).then(res => {
     deptTreeData.value = res.data.data ? [res.data.data] : [];
 
-    handleNodeClick({ id: userAreaCode.value });
+    area_code_log.value = userAreaCode.value
+    roadDroneList()
   });
 };
+
+function handleNodeClick(data) {
+  area_code_log.value = data?.id
+  roadDroneList()
+}
+
+// 筛选部门管理
+function handleDeptNodeClick(data) {
+  dept_id_log.value = data.id
+  roadDroneList()
+}
 
 // 算法
 const treePropsSF = {
     label: 'dictValue',
     value: 'id',
     children: 'children',
+}
+// 部门
+const treeDeptProps = {
+  label: 'title',
+  value: 'id',
+  children: 'children',
 }
 const dictKey = ref('')
 const dataList = ref([])
@@ -280,24 +300,19 @@
     handleSearch()
 }
 
-const handleNodeClick = async data => {
+async function roadDroneList() {
   // 处理机巢数据
-  searchForm.device_sn = '';
-  machineData.value = '';
+  searchForm.device_sn = ''
+  machineData.value = ''
+  const droneList = await getDeviceRegion({ area_code: area_code_log.value, dept_id: dept_id_log.value })
 
-  const droneList = await getDeviceRegion({ areaCode: data.id });
-
-  machineData.value = droneList?.data?.data;
+  machineData.value = droneList?.data?.data
   // 默认选中值
   if (signDevice_sn.value) {
-    searchForm.device_sn = signDevice_sn.value;
+    searchForm.device_sn = signDevice_sn.value
   }
-  // 所属部门重新请求值
-  searchForm.create_dept = '';
-  deptData.value = [];
-  getDeptsByAreaCode();
-  handleSearch();
-};
+  handleSearch()
+}
 
 // 日期 和周期
 let timeList = ['today', 'week', 'month', 'year'];
@@ -359,8 +374,10 @@
   // 清除算法
   dictKey.value = '';
 	// algorithmRef.value?.clear()
-  handleNodeClick({ id: userAreaCode.value });
-  handleSearch();
+  area_code_log.value = userAreaCode.value
+  dept_id_log.value = ''
+  roadDroneList()
+  // handleSearch();
 };
 
 // 新增任务
@@ -416,6 +433,7 @@
 
 onMounted(() => {
   requestDockInfo();
+  getDeptsByAreaCode()
   getAlgorithmList();
   // 日历传值
   const calendarday = ref(route.query.day);
diff --git a/src/views/layerManagement/components/folderFile.vue b/src/views/layerManagement/components/folderFile.vue
index 27bfa17..3365405 100644
--- a/src/views/layerManagement/components/folderFile.vue
+++ b/src/views/layerManagement/components/folderFile.vue
@@ -117,6 +117,7 @@
   }));
 };
 const handleTypeChange = typeValue => {
+formData.value.algorithm_name = '';
   const matchedCategory = allAlgorithms.value.find(category => category.dict_key === typeValue);
   if (!matchedCategory || !matchedCategory.algorithms || matchedCategory.algorithms.length === 0) {
     // 无匹配的算法时清空
diff --git a/src/views/layerManagement/components/leftList.vue b/src/views/layerManagement/components/leftList.vue
index 23fb1a6..25a3eaf 100644
--- a/src/views/layerManagement/components/leftList.vue
+++ b/src/views/layerManagement/components/leftList.vue
@@ -69,16 +69,18 @@
         <template #default="{ node }">
           <div class="tree-node">
             <!-- 节点名称 -->
-            <span>{{ node.label }}</span>
+            <span class="nodeName">{{ node.label }}</span>
             <!-- 操作按钮组 -->
             <div class="tree-node-actions">
               <el-button
                 link
-               
+               icon="el-icon-location"
                 @click.stop="handleLocation(node)"
-              >定位</el-button>
-              <el-button link @click.stop="handleEdit(node)">编辑</el-button>
-              <el-button link @click.stop="handleDelete(node)">删除</el-button>
+                :disabled="node.data.level === 2 && (!node.data.children || node.data.children.length === 0)"
+              class="location-btn" 
+              ></el-button>
+              <el-button icon="el-icon-edit" link @click.stop="handleEdit(node)"></el-button>
+              <el-button icon="el-icon-delete" link @click.stop="handleDelete(node)"></el-button>
             </div>
           </div>
         </template>
@@ -106,7 +108,7 @@
 const checkedKeys = ref([]); // 存储勾选的节点key
 const treeRef = ref(null); // 获取tree实例
 const checkedNodes = ref([]); // 存储勾选的节点数据
-const props = defineProps(['activeName']);
+const props = defineProps(['activeName','resetCheck']);
 const formData = ref({
   isPush: undefined,
   page: 1,
@@ -188,37 +190,44 @@
 };
 // 定位按钮事件
 const handleLocation = node => {
- if (node.data.level === 2) {
-    layerParams.value.isSingleLocating = false; 
+  if (node.data.level === 2) {
+    layerParams.value.isSingleLocating = false;
+    // 新增:标记当前定位的文件夹ID
+    layerParams.value.currentLocationFolderId = node.data.id;
   } else if (node.data.level === 3) {
-    layerParams.value.isSingleLocating = true; 
+    layerParams.value.isSingleLocating = true;
+    // 清空文件夹定位标识
+    layerParams.value.currentLocationFolderId = null;
   }
 
   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);
-    // 合并已选中的节点和当前文件夹的子节点
-    const newCheckedKeys = [...new Set([...currentCheckedKeys, ...childrenIds])];
-    const newCheckedNodes = [...currentCheckedNodes, ...folderChildren];
+    const newChildrenIds = childrenIds.filter(id => !currentCheckedKeys.includes(id));
+    const newChildrenNodes = folderChildren.filter(child => newChildrenIds.includes(child.id));
+    
+    const newCheckedKeys = [...new Set([...currentCheckedKeys, ...newChildrenIds])];
+    const newCheckedNodes = [...currentCheckedNodes, ...newChildrenNodes];
+    
     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
+    emit('update:coverData', coverData.value);
+  } 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);
   }
-  emit('update:coverData', coverData.value);
 };
 // 编辑按钮事件
 const handleEdit = node => {
@@ -363,7 +372,19 @@
     ElMessage.success('取消同步成功');
   });
 }
-
+// 切换tab清空复选框的数据
+watch(
+  () => props.resetCheck,
+  (newVal) => {
+    if (newVal !== undefined) {
+      checkedKeys.value = []; 
+      checkedNodes.value = []; 
+      coverData.value = []; 
+      treeRef.value?.setCheckedKeys([]); 
+    }
+  },
+  { immediate: true }
+);
 onMounted(() => {
   failuresList();
   gettreeDataApi();
@@ -450,8 +471,8 @@
   flex-grow: 1;
   padding-right: 12px;
   :deep(.el-tree-node__content) {
-  height: 38px !important;
-  line-height: 38px !important;
+  height: 35px !important;
+  line-height: 35px !important;
   }
 
   .isTheTerritory {
@@ -472,19 +493,28 @@
     justify-content: space-between;
     align-items: center;
     width: 100%;
+       .nodeName {
+    font-size: 12px !important;
+    }
   }
 
   .tree-node-actions {
     display: flex;
-    // gap: 8px; // 按钮之间的间距
+    // gap: 8px;
+ 
     button {
-      padding: 0; // 清除默认内边距
-      // color: #fff; // 按钮图标颜色
-       color: #409eff;
-      &:hover {
-        color: #409eff; //  hover 时的颜色
-      }
+      padding: 0; 
+      color: #fff; 
+      width: 17px;
+      height: 17px;
+      //  color: #409eff;
+      // &:hover {
+      //   color: #409eff; 
+      // }
     }
   }
+  ::v-deep(.location-btn.el-button.is-link.is-disabled) {
+  color:  #999 !important; 
+}
 }
 </style>
diff --git a/src/views/layerManagement/components/rightEdit.vue b/src/views/layerManagement/components/rightEdit.vue
index 4566fc7..9e9dee6 100644
--- a/src/views/layerManagement/components/rightEdit.vue
+++ b/src/views/layerManagement/components/rightEdit.vue
@@ -45,6 +45,7 @@
             range-separator="至"
             start-placeholder="开始日期"
             end-placeholder="结束日期"
+                :disabled-date="disabledDate"
           />
         </div>
       </div>
@@ -156,7 +157,10 @@
 const isDisabled = computed(() => {
   return layerParams.value.editingIsProhibited;
 });
-
+// 禁用当天之前的日期
+const disabledDate = time => {
+  return time.getTime() < Date.now() - 8.64e7 // 86400000 = 24 * 60 * 60 * 1000
+}
 watch(
   () => layerParams.value.fenceArea,
   newArea => {
diff --git a/src/views/layerManagement/index.vue b/src/views/layerManagement/index.vue
index 9ff0fda..0c56040 100644
--- a/src/views/layerManagement/index.vue
+++ b/src/views/layerManagement/index.vue
@@ -23,6 +23,7 @@
           @update:editFolder="handleFolder"
           :activeName="activeName"
           @newFencesMethods="newFencesMethods"
+          :reset-check="resetCheck" 
         ></leftList>
         <rightEdit
           v-if="layerParams.addNest || layerParams.editNest"
@@ -97,19 +98,23 @@
   editFolderData: null,
   editingIsProhibited: false, //禁止编辑
   isDetailShow: false,
-  isSingleLocating:false
+  isSingleLocating:false , //是否是单个定位
+  currentLocationFolderId:null,
 });
-
+const resetCheck = ref(false);
 const handleClick = tab => {
   const clickedTab = tabData.value.find(item => item.type === tab.paneName);
   activeType.value = clickedTab.type;
   activeName.value = clickedTab.name;
   getdataFolderApi();
   parentMethod();
+  handleCoverDataUpdate([])
   layerParams.value.addNest = false
     layerParams.value.editNest = false
      layerParams.value.addFolder = false
     layerParams.value.editFolder = false
+    resetCheck.value = !resetCheck.value;
+    
 };
 const refreshMethod = () => {
   getdataFolderApi();
@@ -170,8 +175,7 @@
 // 点击显示区域
 const handleCoverDataUpdate = data => {
   selectDataList.value = data;
-  if (selectDataList.value.length > 0) {
-  
+  if (selectDataList.value?.length > 0) {
     loadDataToMap(selectDataList.value);
   } else {
     viewer.entities.removeAll();
@@ -218,7 +222,7 @@
         : item.category_id === 3 
           ? colorMap.border3 
           : Cesium.Color.YELLOW;
-    viewer.entities.add({
+      viewer.entities.add({
       id: `polygon_${item.id}`,
       customType: 'fence_polygon',
       customInfo: item,
@@ -241,9 +245,7 @@
       },
       zIndex: 99, 
     });
-  });
-
-  
+  });  
  if (!layerParams.value.isSingleLocating) {
     focusOnAllFeatures(); 
   }
@@ -251,16 +253,44 @@
 };
 const focusOnAllFeatures = () => {
   if (tbJwdList.length === 0 || !viewer) return;
+  
+  // 判断是否有指定定位的文件夹
+  if (layerParams.value.currentLocationFolderId) {
+    // 筛选当前文件夹下的所有子节点坐标
+    const targetFolderPositions = selectDataList.value
+      .filter(item => item.folder_id === layerParams.value.currentLocationFolderId)
+      .flatMap(item => {
+        const positions = parseGeoDataToPositions(item.geo_data, item.altitude);
+        return positions.length >= 3 ? positions : [];
+      });
+    
+    if (targetFolderPositions.length > 0) {
+      const positionsData = targetFolderPositions.map(pos => [
+        pos.lng,
+        pos.lat,
+        pos.height || 0
+      ]);
+      flyVisual({
+        positionsData,
+        viewer,
+        multiple: activeName.value === '国土空间规划' ? 13 : 10,
+        pitch: -90
+      });
+      return; // 仅聚焦当前文件夹,结束方法
+    }
+  }
+
+  // 无指定文件夹时,聚焦所有选中数据
   const positionsData = tbJwdList.map(pos => [
     pos.lng,
     pos.lat,
-    pos.height || 0 
+    pos.height || 0
   ]);
   flyVisual({
     positionsData,
     viewer,
-      multiple:activeName.value == '国土空间规划' ? 13 :4, // 缩放倍数
-    pitch: -90 
+    multiple: activeName.value === '国土空间规划' ? 13 : 4,
+    pitch: -90
   });
 };
 // 区域点击显示详细信息
@@ -348,7 +378,7 @@
       let cartographic = Cesium.Cartographic.fromCartesian(item);
       let lng = Cesium.Math.toDegrees(cartographic.longitude); // 经度
       let lat = Cesium.Math.toDegrees(cartographic.latitude); // 纬度
-      let height = Cesium.Math.toDegrees(cartographic.height); //高度
+      let height = Cesium.Math.toDegrees(cartographic.height); //高度 
       return {
         lng: _.round(lng, 6),
         lat: _.round(lat, 6),
@@ -409,14 +439,6 @@
   }
 };
 provide('layerParams', layerParams);
-// onMounted(() => {
-//   initMap();
-//   cesiumContextMenu();
-//   getdataFolderApi();
-//   EventBus.on('deleteMapEntityById', deleteMapEntityById);
-//   EventBus.on('deleteMapEntitiesByFolderId', deleteMapEntitiesByFolderId);
-  
-// });
 onMounted(() => {
   initMap();
   cesiumContextMenu();
@@ -431,22 +453,23 @@
 
 //定位到指定节点
 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
-  });
+  if (positions.length < 3) return;
 
+  // level 2 节点走全量聚焦,level 3 节点走单个聚焦
+  if (nodeData.level === 2) {
+    focusOnAllFeatures();
+  } else {
+    flyVisual({
+      positionsData: positions.map(pos => [pos.lng, pos.lat, pos.height || 0]),
+      viewer,
+      multiple: activeName.value === '国土空间规划' ? 13 : 7, // 缩放倍数
+      pitch: -90
+    });
+  }
 };
 onBeforeUnmount(() => {
   destroyMap();
diff --git a/src/views/tickets/orderLog.vue b/src/views/tickets/orderLog.vue
index 35344b8..fe17d06 100644
--- a/src/views/tickets/orderLog.vue
+++ b/src/views/tickets/orderLog.vue
@@ -194,7 +194,7 @@
           </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>
@@ -229,15 +229,15 @@
             </el-form-item>
           </el-col>
 
-          <el-col :span="8">
+          <el-col :span="7">
             <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" />
             </el-form-item>
           </el-col>
-          <el-col :span="4">
+          <el-col :span="5">
             <div class="flex">
-              <div class="flex-1">
+              <div class="flex-1 flex-3">
                 <el-select v-model="form.rep_fre_type" placeholder="请选择频次">
                   <el-option v-for="item in cycles" :key="item" :label="item" :value="item" />
                 </el-select>
@@ -325,7 +325,7 @@
           <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>
@@ -359,25 +359,35 @@
             </el-form-item>
           </el-col>
 
-          <el-col :span="6">
+          <el-col :span="7">
             <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 === '工单详情'" />
             </el-form-item>
           </el-col>
 
-          <el-col :span="3">
+          <el-col :span="5">
+            <div class="flex">
+              <div class="flex-1 flex-3">
             <el-select v-model="form.rep_fre_type" placeholder="请选择频次" :disabled="detailTitle === '工单详情'">
               <el-option v-for="item in cycles" :key="item" :label="item" :value="item" />
             </el-select>
-          </el-col>
-
-          <el-col :span="3">
-            <el-time-picker class="timeStyle" :style="{ width: pxToRem(140) }" v-model="form.deal_time" prop="deal_time"
+              </div>
+              <div class="flex-1">
+            <el-time-picker v-model="form.deal_time" prop="deal_time"
                             value-format="HH:mm" :picker-options="{
                 selectableRange: '00:00 - 23:59',
               }" :disabled="detailTitle === '工单详情'" />
+              </div>
+            </div>
           </el-col>
+
+<!--          <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="{-->
+<!--                selectableRange: '00:00 - 23:59',-->
+<!--              }" :disabled="detailTitle === '工单详情'" />-->
+<!--          </el-col>-->
         </el-row>
 
         <el-row :gutter="20">
@@ -465,7 +475,7 @@
           </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>
@@ -500,24 +510,34 @@
             </el-form-item>
           </el-col>
 
-          <el-col :span="6">
+          <el-col :span="7">
             <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" />
             </el-form-item>
           </el-col>
-          <el-col :span="3">
+          <el-col :span="5">
+            <div class="flex">
+              <div class="flex-1 flex-3">
             <el-select v-model="form.rep_fre_type" placeholder="请选择频次" :disabled="true">
               <el-option v-for="item in cycles" :key="item" :label="item" :value="item" />
             </el-select>
-          </el-col>
-
-          <el-col :span="3">
+              </div>
+              <div class="flex-1">
             <el-time-picker style="width: 100px" v-model="form.deal_time" prop="deal_time" :disabled="true"
                             value-format="HH:mm" :picker-options="{
                 selectableRange: '00:00 - 23:59',
               }" />
+              </div>
+            </div>
           </el-col>
+
+<!--          <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="{-->
+<!--                selectableRange: '00:00 - 23:59',-->
+<!--              }" />-->
+<!--          </el-col>-->
         </el-row>
 
         <el-row :gutter="20">
@@ -570,7 +590,8 @@
   orderLogExport,
   jobStatusNum,
   userPublish,
-  deleteOrderLog
+  deleteOrderLog,
+  getWaylineMaxTerrainHeight
 } from '@/api/tickets/orderLog'
 import { getTicketInfo } from '@/api/tickets/ticket'
 import { getDictionaryByCode } from '@/api/system/dictbiz'
@@ -825,9 +846,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
@@ -1007,15 +1032,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'
@@ -1668,4 +1700,7 @@
     width: 100% !important;
   }
 }
+.flex-3 {
+  margin-right: 4px;
+}
 </style>
diff --git a/src/views/tickets/ticket.vue b/src/views/tickets/ticket.vue
index c33af5f..91b754b 100644
--- a/src/views/tickets/ticket.vue
+++ b/src/views/tickets/ticket.vue
@@ -206,6 +206,7 @@
                 @click="openReviewDialog"
                 >批量审核
               </el-button>
+            
               <el-button
                 v-if="permissionList.exportBtn"
                 type="success"
@@ -232,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"
@@ -1010,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';
@@ -1107,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: '工单类型',
@@ -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,
@@ -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();

--
Gitblit v1.9.3