无人机管理后台前端(已迁走)
罗广辉
2025-11-21 b5e2aaaba43d7456ab6ffedefa16c97a22cbe479
Merge branch 'feature/v8.0/8.0.2' into prod
16 files modified
430 ■■■■ changed files
.env.development patch | view | raw | blame | history
src/api/device/device.js 16 ●●●●● patch | view | raw | blame | history
src/api/tickets/orderLog.js patch | view | raw | blame | history
src/api/tickets/ticket.js 9 ●●●●● patch | view | raw | blame | history
src/hooks/useRouteLine/useRouteLine.js 11 ●●●●● patch | view | raw | blame | history
src/store/modules/common.js 10 ●●●● patch | view | raw | blame | history
src/views/dataCenter/components/searchData.vue 4 ●●●● patch | view | raw | blame | history
src/views/dataCenter/dataCenter.vue 2 ●●● patch | view | raw | blame | history
src/views/device/airport.vue 29 ●●●●● patch | view | raw | blame | history
src/views/job/components/SearchBox.vue 80 ●●●●● patch | view | raw | blame | history
src/views/layerManagement/components/folderFile.vue 1 ●●●● patch | view | raw | blame | history
src/views/layerManagement/components/leftList.vue 76 ●●●●● patch | view | raw | blame | history
src/views/layerManagement/components/rightEdit.vue 6 ●●●● patch | view | raw | blame | history
src/views/layerManagement/index.vue 65 ●●●●● patch | view | raw | blame | history
src/views/tickets/orderLog.vue 93 ●●●●● patch | view | raw | blame | history
src/views/tickets/ticket.vue 28 ●●●●● patch | view | raw | blame | history
.env.development
src/api/device/device.js
@@ -168,3 +168,19 @@
    data: param
  })
}
// 初始化机场附近五公
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
  })
}
src/api/tickets/orderLog.js
src/api/tickets/ticket.js
@@ -104,3 +104,12 @@
    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/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">
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)
              }}
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'
@@ -911,7 +919,24 @@
      this.cancenOperate = true
      this.cancelSNName = row.nickname
      this.cancenOperateRow = row
    },
    // 设备
    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 () {
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"
            @change="(id) => handleDeptNodeClick({id})"
            />
          </el-select>
        </el-form-item> -->
        </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) {
  getDeptLazyTree({ parentId: 0, level: 0 }).then(res => {
      deptData.value = res.data.data;
    }
  });
};
@@ -225,18 +222,41 @@
// 机巢
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',
}
@@ -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);
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) {
    // 无匹配的算法时清空
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,
@@ -190,35 +192,42 @@
const handleLocation = node => {
 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.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);
  }
};
// 编辑按钮事件
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>
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 => {
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();
@@ -242,8 +246,6 @@
      zIndex: 99, 
    });
  });
 if (!layerParams.value.isSingleLocating) {
    focusOnAllFeatures(); 
  }
@@ -251,6 +253,34 @@
};
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,
@@ -259,7 +289,7 @@
  flyVisual({
    positionsData,
    viewer,
      multiple:activeName.value == '国土空间规划' ? 13 :4, // 缩放倍数
    multiple: activeName.value === '国土空间规划' ? 13 : 4,
    pitch: -90 
  });
};
@@ -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) {
  if (positions.length < 3) return;
 
    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, // 缩放倍数
      multiple: activeName.value === '国土空间规划' ? 13 : 7, // 缩放倍数
    pitch: -90
  });
  }
};
onBeforeUnmount(() => {
  destroyMap();
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>
src/views/tickets/ticket.vue
@@ -206,6 +206,7 @@
                @click="openReviewDialog"
                >批量审核
              </el-button>
              <el-button
                v-if="permissionList.exportBtn"
                type="success"
@@ -233,6 +234,11 @@
                </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();