无人机管理后台前端(已迁走)
张含笑
2025-10-31 69cc010e3c7f994f302426c6f3acdc15c2b49cbb
feat:新增/编辑文件夹
5 files modified
534 ■■■■■ changed files
src/api/layer/index.js 91 ●●●●● patch | view | raw | blame | history
src/views/layerManagement/components/folderFile.vue 173 ●●●●● patch | view | raw | blame | history
src/views/layerManagement/components/leftList.vue 95 ●●●●● patch | view | raw | blame | history
src/views/layerManagement/components/rightEdit.vue 28 ●●●● patch | view | raw | blame | history
src/views/layerManagement/index.vue 147 ●●●● patch | view | raw | blame | history
src/api/layer/index.js
@@ -1,42 +1,67 @@
import request from '@/axios';
export const treeDataApi = (params) => {
    return request({
        url: `/drone-device-core/layer/api/v1/tree`,
        method: 'get',
        params,
    })
  };
export const treeDataApi = params => {
  return request({
    url: `/drone-device-core/layer/api/v1/tree`,
    method: 'get',
    params,
  });
};
//   电子围栏
//   编辑
export const deitFenceApi = data => {
    return request({
      url:  `/drone-device-core/layer/api/v1/data`,
      method: 'put',
      data: data,
    });
  };
  return request({
    url: `/drone-device-core/layer/api/v1/data`,
    method: 'put',
    data: data,
  });
};
// 删除
export const deleteFenceApi = (id) => {
    return request({
      url: `/drone-device-core/layer/api/v1/data/${id}`,
      method: 'delete',
    });
  };
export const deleteFenceApi = id => {
  return request({
    url: `/drone-device-core/layer/api/v1/data/${id}`,
    method: 'delete',
  });
};
// 新增
export const addFenceApi = (data) => {
    return request({
        url: `/drone-device-core/layer/api/v1/data`,
        method: 'post',
        data
    })
}
export const addFenceApi = data => {
  return request({
    url: `/drone-device-core/layer/api/v1/data`,
    method: 'post',
    data,
  });
};
// 获取文件夹
export const dataFolderApi = (id) => {
    return request({
        url:`/drone-device-core/layer/api/v1/getFolderByCategoryId/${id}`,
        method: 'get',
    })
  };
export const dataFolderApi = id => {
  return request({
    url: `/drone-device-core/layer/api/v1/getFolderByCategoryId/${id}`,
    method: 'get',
  });
};
//  文件夹
//   编辑
export const deitFolderApi = data => {
  return request({
    url: `/drone-device-core/layer/api/v1/folder`,
    method: 'put',
    data: data,
  });
};
// 删除
export const deleteFolderApi = id => {
  return request({
    url: `/drone-device-core/layer/api/v1/folder/${id}`,
    method: 'delete',
  });
};
// 新增
export const addFolderApi = data => {
  return request({
    url: `/drone-device-core/layer/api/v1/folder`,
    method: 'post',
    data,
  });
};
src/views/layerManagement/components/folderFile.vue
@@ -2,7 +2,6 @@
  <div class="rightContainer">
    <div class="contenttitle">文件夹信息</div>
    <div class="centerBox">
      <div class="itemRow">
        <div class="label">文件夹名称</div>
        <div>
@@ -16,7 +15,7 @@
            class="ztzf-layer-search filter-item"
            popper-class="ztzf-select-popper"
            @change="handleTypeChange"
            v-model="formData.type"
            v-model="formData.event_type"
            placeholder="请选择"
            clearable
          >
@@ -28,12 +27,12 @@
            />
          </el-select>
          <el-select
            v-model="formData.algorithm"
            v-model="formData.algorithm_name"
            popper-class="ztzf-select-popper"
            class="ztzf-layer-search"
            placeholder="请选择"
            clearable
            :disabled="!formData.type"
            :disabled="!formData.event_type"
          >
            <el-option
              v-for="item in algorithms"
@@ -45,25 +44,33 @@
        </div>
      </div>
    </div>
    <div class="detailInfo">
    <div class="detailInfo" v-if="layerParams.editFolder">
      <div class="infpItem">
        <div class="itemData">
          <div class="title">围栏总数量</div>
          <div class="num"><span>{{totalCount}}</span>个</div>
          <div class="num">
            <span>{{ formData.total_count }}</span> 个
          </div>
        </div>
        <div class="itemData">
          <div class="title">围栏总面积</div>
          <div class="num"><span>{{totalArea}}</span>m²</div>
          <div class="num">
            <span>{{ formData.total_area }}</span> m²
          </div>
        </div>
      </div>
      <div class="infpItem">
        <div class="itemData">
          <div class="title">创建时间</div>
          <div class="num"><span>{{formData?.create_time}}</span></div>
          <div class="num">
            <span>{{ formData?.create_time }}</span>
          </div>
        </div>
        <div class="itemData">
          <div class="title">创建人</div>
          <div class="num"><span>{{formData?.create_name}}</span></div>
          <div class="num">
            <span>{{ formData?.create_name }}</span>
          </div>
        </div>
      </div>
    </div>
@@ -75,86 +82,130 @@
</template>
<script setup>
import { deitFenceApi } from '@/api/layer/index';
import {
  getTicketInfo,
} from '@/api/tickets/ticket';
import { ElMessage } from 'element-plus';
import dayjs from 'dayjs';
import { deleteFolderApi, deitFolderApi, addFolderApi } from '@/api/layer/index';
import { getTicketInfo } from '@/api/tickets/ticket';
const layerParams = inject('layerParams');
const types = ref([]) //工单类型
const allAlgorithms = ref([])
const algorithms = ref([])
const types = ref([]); //工单类型
const allAlgorithms = ref([]);
const algorithms = ref([]);
const formData = ref({
  folder_id: '',
  category_id: '',
  name: '',
  description: '',
  is_enabled: false,
  area: '',
  altitude: '',
  control_start_time: '',
  control_end_time: '',
  geo_data: '',
  algorithm:'',
  type: '',
  event_type: '',
  algorithm_name: '',
  total_count: 0,
  total_area: 0,
});
const detailData = ref(null)
const totalArea = ref(null)
const totalCount = ref(null)
detailData.value = layerParams.value.editDetailData
totalArea.value = layerParams.value.total_area
totalCount.value = layerParams.value.total_count
const detailData = ref(null);
detailData.value = layerParams.value.editFolderData;
const getTicketInfoData = async () => {
  const response = await getTicketInfo()
  const { dept_data, event_type, ai_type, info } = response.data.data
  allAlgorithms.value = info
  const response = await getTicketInfo();
  const { dept_data, event_type, ai_type, info } = response.data.data;
  allAlgorithms.value = info;
  types.value = Object.entries(event_type).map(([key, value]) => ({
    label: value,
    value: key,
  }))
}
const handleTypeChange=(typeValue)=>{
  const matchedCategory = allAlgorithms.value.find(category => category.dict_key === typeValue)
  }));
};
const handleTypeChange = typeValue => {
  const matchedCategory = allAlgorithms.value.find(category => category.dict_key === typeValue);
  if (!matchedCategory || !matchedCategory.algorithms || matchedCategory.algorithms.length === 0) {
    // 无匹配的算法时清空
    algorithms.value = []
    return
    algorithms.value = [];
    return;
  }
  algorithms.value = matchedCategory.algorithms.map(algo => ({
    label: algo.dict_value,
    value: algo.dict_key,
    dict_key: algo.dict_key,
    dict_value: algo.dict_value,
  }))
  console.log(' algorithms.value ', algorithms.value );
}
  }));
};
const clearAllData = () => {
  detailData.value = {}
  // 清空回显相关数据
  formData.value = {
    category_id: '',
    name: '',
    event_type: '',
    algorithm_name: '',
    total_count: 0,
    total_area: 0,
    create_time: '',
    create_name: '',
  };
  algorithms.value = [];
  detailData.value = {};
  totalArea.value = null;
  totalCount.value = null;
  // 重置判断状态(可选,根据业务需求是否需要)
  layerParams.value.decideWhetherToAddOrEdit = 1;
  // 重置面板状态和缓存数据
  layerParams.value.addFolder = false;
  layerParams.value.editFolder = false;
  layerParams.value.editDetailData = null;
  layerParams.value.polygonPosition = null;
};
const cancelHandel = () => {
  clearAllData()
  clearAllData();
};
const submitHandle = () => {
  clearAllData()
  const params = {
    category_id: layerParams.value.fileType === 1 ? 1 : 2,
    name: formData.value.name,
    event_type: formData.value.event_type,
    algorithm_name: formData.value.algorithm_name,
  };
  // 如果是编辑模式,才添加 id
  if (layerParams.value.addAnEditingFolder === 2) {
    params.id = detailData.value.id;
  }
  if (layerParams.value.addAnEditingFolder === 1) {
    // 文件夹新增
    addFolderApi(params).then(res => {
      if (res.status === 200) {
        ElMessage({
          message: '新增成功',
          type: 'success',
        });
      }
    });
  } else {
    //文件夹编辑
    deitFolderApi(params).then(res => {
      if (res.status === 200) {
        ElMessage({
          message: '编辑成功',
          type: 'success',
        });
      }
    });
  }
  clearAllData();
};
onMounted(() => {
  getTicketInfoData()
onMounted(async () => {
  await getTicketInfoData();
  const addOrEditStatus = layerParams.value.addAnEditingFolder || 1;
  //  判断是否有编辑数据,有则回显
  if (detailData.value && addOrEditStatus === 2) {
    const editData = detailData.value;
    formData.value = {
      ...formData.value,
      category_id: editData.category_id,
      name: editData.name,
      event_type: editData.event_type, // 回显“关联算法类型”值
      algorithm_name: editData.algorithm_name, // 回显“算法”值
      total_count: editData.total_count,
      total_area: editData.total_area,
      create_time: editData.create_time ? dayjs(editData.create_time).format('YYYY年MM月DD日') : '',
      create_name: editData.create_name,
    };
    if (formData.value.event_type) {
      handleTypeChange(formData.value.event_type);
      setTimeout(() => {
        formData.value.algorithm_name = editData.algorithm_name;
      }, 100);
    }
  }
});
</script>
src/views/layerManagement/components/leftList.vue
@@ -12,7 +12,9 @@
      </div>
    </div>
    <div class="btnGroups">
      <el-button class="ztzf-button" @click="addFence">{{props.activeName === '电子围栏' ? '新增围栏' : '新增禁飞区'}}</el-button>
      <el-button class="ztzf-button" @click="addFence">{{
        props.activeName === '电子围栏' ? '新增围栏' : '新增禁飞区'
      }}</el-button>
      <el-button class="ztzf-button" @click="addFolder">新增文件夹</el-button>
      <el-button class="ztzf-button" @click="selectAll">全选</el-button>
    </div>
@@ -38,7 +40,8 @@
            <span>{{ node.label }}</span>
            <!-- 操作按钮组 -->
            <div class="tree-node-actions">
              <el-button v-if="node.level !== 1"
              <el-button
                icon="el-icon-location"
                type="text"
                @click="handleLocation(node)"
@@ -54,10 +57,11 @@
</template>
<script setup>
import { treeDataApi ,deleteFenceApi} from '@/api/layer/index';
import { ElMessage } from 'element-plus';
import { treeDataApi, deleteFenceApi } from '@/api/layer/index';
import { inject, onMounted } from 'vue';
const emit = defineEmits(['update:coverData', 'update:deitData','newFencesMethods']);
import EventBus from '@/utils/eventBus'
const emit = defineEmits(['update:coverData', 'update:deitData', 'newFencesMethods']);
import EventBus from '@/utils/eventBus';
const coverData = ref();
const layerParams = inject('layerParams');
const checkedKeys = ref([]); // 存储勾选的节点key
@@ -81,7 +85,7 @@
  try {
    const res = await treeDataApi();
    treeAllData.value = res.data.data;
console.log('接口数据',treeAllData.value);
    console.log('接口数据', treeAllData.value);
    setupWatch();
  } catch (error) {
@@ -99,9 +103,9 @@
      if (treeData.value && treeData.value.length > 0) {
        const folderNode = treeData.value[0];
        // 赋值给 layerParams
        layerParams.value.total_count = folderNode.total_count ?folderNode.total_count :0;
        layerParams.value.total_area = folderNode.total_area ? folderNode.total_area :0;
      }
        layerParams.value.total_count = folderNode.total_count ? folderNode.total_count : 0;
        layerParams.value.total_area = folderNode.total_area ? folderNode.total_area : 0;
      }
    },
    { immediate: true }
  );
@@ -144,51 +148,72 @@
// 编辑按钮事件
const handleEdit = node => {
  checkedKeys.value = [];
  checkedNodes.value = [];
  coverData.value = [];
  if (node.data.level == 2) {
    layerParams.value.editFolder = true;
    layerParams.value.addAnEditingFolder = 2;
    if (props.activeName === '电子围栏') {
      layerParams.value.fileType = 1;
    } else if (props.activeName === '自定义禁飞区') {
      layerParams.value.fileType = 2;
    }
    emit('update:editFolder', node.data);
  } else if (node.data.level === 3) {
    checkedKeys.value = [];
    checkedNodes.value = [];
    coverData.value = [];
    layerParams.value.editNest = true;
    layerParams.value.decideWhetherToAddOrEdit = 2
      if (props.activeName === '电子围栏') {
    layerParams.value.fenceType = 1; // 电子围栏类型标识
  } else if (props.activeName === '自定义禁飞区') {
    layerParams.value.fenceType = 2; // 自定义禁飞区类型标识
    layerParams.value.decideWhetherToAddOrEdit = 2;
    if (props.activeName === '电子围栏') {
      layerParams.value.fenceType = 1; // 电子围栏类型标识
    } else if (props.activeName === '自定义禁飞区') {
      layerParams.value.fenceType = 2; // 自定义禁飞区类型标识
    }
    emit('update:deitData', node.data);
  }
  emit('update:deitData', node.data);
};
// 删除按钮事件
const handleDelete = node => {
let id = node.data.id
  deleteFenceApi(id).then(res=>{
    console.log('删除成功',res);
  })
    gettreeDataApi();
  let id = node.data.id;
  deleteFenceApi(id).then(res => {
   if (res.status === 200) {
        ElMessage({
          message: '删除成功',
          type: 'success',
        });
      }
  });
  gettreeDataApi();
};
// 新增围栏
const addFence = () => {
  layerParams.value.addNest = true;
   layerParams.value.decideWhetherToAddOrEdit = 1
   if (props.activeName === '电子围栏') {
  layerParams.value.decideWhetherToAddOrEdit = 1;
  if (props.activeName === '电子围栏') {
    layerParams.value.fenceType = 1; // 电子围栏类型标识
  } else if (props.activeName === '自定义禁飞区') {
    layerParams.value.fenceType = 2; // 自定义禁飞区类型标识
  }
   emit('newFencesMethods');
  emit('newFencesMethods');
};
// 新增文件夹
const addFolder = () => {
  layerParams.value.addFolder = true
  layerParams.value.addAnEditingFolder =1
  layerParams.value.addFolder = true;
  layerParams.value.addAnEditingFolder = 1;
  if (props.activeName === '电子围栏') {
    layerParams.value.fileType = 1;
  } else if (props.activeName === '自定义禁飞区') {
    layerParams.value.fileType = 2;
  }
};
onMounted(() => {
  gettreeDataApi();
  EventBus.on('gettreeDataApi', gettreeDataApi)
  EventBus.on('gettreeDataApi', gettreeDataApi);
});
onBeforeUnmount(()=>{
    EventBus.off('gettreeDataApi', gettreeDataApi)
})
onBeforeUnmount(() => {
  EventBus.off('gettreeDataApi', gettreeDataApi);
});
</script>
<style scoped lang="scss">
@@ -264,9 +289,8 @@
  //  }
  //}
  :deep(){
    .el-checkbox{
  :deep() {
    .el-checkbox {
      --el-checkbox-bg-color: transparent !important;
    }
  }
@@ -275,7 +299,6 @@
    justify-content: space-between;
    align-items: center;
    width: 100%;
  }
  .tree-node-actions {
src/views/layerManagement/components/rightEdit.vue
@@ -51,7 +51,7 @@
      </div>
    </div>
    <div class="detailInfo">
      <div class="infpItem" v-if="layerParams.fenceType === 1">
      <div class="infpItem" v-if="layerParams.editNest">
       
        <div class="itemData">
          <div class="title">围栏面积</div>
@@ -122,20 +122,12 @@
  control_start_time: '',
  control_end_time: '',
  geo_data: '',
  noFlyZoneArea: '',
  altitude:''
});
const detailData = ref(null);
const totalArea = ref(null);
const totalCount = ref(null);
const fenceArea = ref(0)
detailData.value = layerParams.value.editDetailData;
totalArea.value = layerParams.value.total_area;
totalCount.value = layerParams.value.total_count;
fenceArea.value = layerParams.value.fenceArea
watch(
  () => layerParams.value.fenceArea,
  (newArea) => {
@@ -149,7 +141,6 @@
watch(
  () => timeValue.value,
  (newTimeArr) => {
    if (Array.isArray(newTimeArr) && newTimeArr.length === 2) {
      const startTime = newTimeArr[0]; 
      const endTime = newTimeArr[1];   
@@ -165,11 +156,6 @@
);
const clearAllData = () => {
  detailData.value = {};
  // 清空回显相关数据
  detailData.value = {};
  totalArea.value = null;
  totalCount.value = null;
  // 重置判断状态(可选,根据业务需求是否需要)
  layerParams.value.decideWhetherToAddOrEdit = 1;
  // 重置面板状态和缓存数据
  layerParams.value.addNest = false;
@@ -184,7 +170,7 @@
};
const submitHandle = () => {
  const params = {
    folder_id: layerParams.value.fenceType === 1 ? 1 : 2,
     folder_id: formData.value.folder_id,
    name: formData.value.name,
    description: formData.value.description,
    is_enabled: true,
@@ -198,7 +184,6 @@
  if (layerParams.value.decideWhetherToAddOrEdit === 2) {
    params.id = detailData.value.id;
  }
  console.log('formData', params);
  if (layerParams.value.decideWhetherToAddOrEdit === 1) {
    //围栏新增
@@ -262,15 +247,8 @@
        dayjs(editData.control_end_time).toDate()
      ];
    }
    // 回显总数和总面积(编辑时可能需要展示)
    totalArea.value = layerParams.value.total_area || 0;
    totalCount.value = layerParams.value.total_count || 0;
       fenceArea.value = editData.area|| 0;
  } else {
    // 新增状态:不回显数据,清空总数和总面积
    totalArea.value = null;
    totalCount.value = null;
  }
  }
});
</script>
src/views/layerManagement/index.vue
@@ -11,17 +11,25 @@
      <div class="mapContainer">
        <div id="layMap" class="ztzf-cesium"></div>
        <leftList
          v-if="!layerParams.addNest && !layerParams.editNest&& !layerParams.addFolder && activeType !== '2'"
          v-if="
            !layerParams.addNest &&
            !layerParams.editNest &&
            !layerParams.addFolder &&
            activeType !== '2'
          "
          @update:coverData="handleCoverDataUpdate"
          @update:deitData="handleEdit"
          @update:editFolder="handleFolder"
          :activeName="activeName"
          @newFencesMethods="newFencesMethods"
        ></leftList>
        <rightEdit
          v-if="layerParams.addNest || layerParams.editNest && activeType !== '2'"
          v-if="layerParams.addNest || (layerParams.editNest && activeType !== '2')"
          @callParentMethod="parentMethod"
        ></rightEdit>
        <folderFile  v-if="layerParams.addFolder || layerParams.editFolder && activeType !== '2'"></folderFile>
        <folderFile
          v-if="layerParams.addFolder || (layerParams.editFolder && activeType !== '2')"
        ></folderFile>
        <nationalSpatialPlanning v-if="activeType === '2'" />
      </div>
    </div>
@@ -29,14 +37,14 @@
</template>
<script setup>
import * as turf from '@turf/turf'
import * as turf from '@turf/turf';
import { dataFolderApi } from '@/api/layer/index';
import folderFile from '@/views/layerManagement/components/folderFile.vue'
import folderFile from '@/views/layerManagement/components/folderFile.vue';
import { parseGeoDataToPositions } from '@/utils/geoParseUtil';
import { flyVisual } from '@/utils/cesium/mapUtil';
import rightEdit from '@/views/layerManagement/components/rightEdit.vue';
import leftList from '@/views/layerManagement/components/leftList.vue';
import nationalSpatialPlanning from '@/views/layerManagement/components/nationalSpatialPlanning.vue'
import nationalSpatialPlanning from '@/views/layerManagement/components/nationalSpatialPlanning.vue';
import { DrawPolygon } from '@/views/layerManagement/components/utils';
import * as Cesium from 'cesium';
import { PublicCesium } from '@/utils/cesium/publicCesium';
@@ -75,14 +83,16 @@
  editDetailData: null,
  total_count: 0,
  total_area: 0,
  polygonPosition:null,
  decideWhetherToAddOrEdit:null,//新增or编辑围栏
  addAnEditingFolder:null,//新增or编辑文件夹
  addFolder:false,
  editFolder:false,
  folderOption:[] ,//文件夹选项
  fenceType:1,
  fenceArea:0,//围栏面积
  polygonPosition: null,
  decideWhetherToAddOrEdit: null, //新增or编辑围栏
  addAnEditingFolder: null, //新增or编辑文件夹
  addFolder: false,
  editFolder: false,
  folderOption: [], //文件夹选项
  fenceType: 1,
  fenceArea: 0, //围栏面积
  fileType: 1, //文件夹类型
  editFolderData: null,
});
const handleClick = tab => {
@@ -90,50 +100,53 @@
  activeType.value = clickedTab.type;
  activeName.value = clickedTab.name;
    getdataFolderApi();
  getdataFolderApi();
};
const getdataFolderApi=()=>{
 let id;
const getdataFolderApi = () => {
  let id;
  if (activeName.value === '电子围栏') {
    id = 1;
  } else if (activeName.value === '自定义禁飞区') {
    id = 2;
  } else {
    return;
  }
  dataFolderApi(id).then(res=>{
 const originalFolderList = res.data.data || [];
  const formattedFolderOption = originalFolderList.map(folder => ({
        label: folder.name,
        value: folder.id.toString()
      }));
       layerParams.value.folderOption = formattedFolderOption;
  })
}
  dataFolderApi(id).then(res => {
    const originalFolderList = res.data.data || [];
    const formattedFolderOption = originalFolderList.map(folder => ({
      label: folder.name,
      value: folder.id.toString(),
    }));
    layerParams.value.folderOption = formattedFolderOption;
  });
};
// 编辑围栏区域
const handleEdit = val => {
viewer.entities.removeAll();
  viewer.entities.removeAll();
  layerParams.value.editDetailData = val;
  drawPolygonExample.editThePatch(true);
  const positions = parseGeoDataToPositions(val.geo_data, val.altitude);
  const positions = parseGeoDataToPositions(val.geo_data, val.altitude);
  if (positions.length < 3) return; // 少于3个点无法构成多边形
   drawPolygonExample.drawTheArea(true)
  drawPolygonExample.drawTheArea(true);
  drawPolygonExample.initPolygon(viewer, positions, true);
};
// 编辑文件夹
const handleFolder = val => {
  layerParams.value.editFolderData = val;
  console.log('文件夹', layerParams.value.editFolderData);
};
// 新增开启绘制
const newFencesMethods =()=>{
viewer.entities.removeAll();
drawPolygonExample.initHandler(viewer);
   drawPolygonExample.drawTheArea(true)
   drawPolygonExample.startDrawing();
}
const newFencesMethods = () => {
  viewer.entities.removeAll();
  drawPolygonExample.initHandler(viewer);
  drawPolygonExample.drawTheArea(true);
  drawPolygonExample.startDrawing();
};
// 清除地图数据
const parentMethod = () => {
drawPolygonExample.delPolygon()
drawPolygonExample.drawTheArea(false)
  drawPolygonExample.delPolygon();
  drawPolygonExample.drawTheArea(false);
};
// 点击显示区域
const handleCoverDataUpdate = data => {
@@ -151,7 +164,7 @@
  tbJwdList = [];
  dataList.forEach(item => {
    let positions = parseGeoDataToPositions(item.geo_data, item.altitude);
    positions.push(positions[0])
    positions.push(positions[0]);
    if (positions.length < 3) {
      console.warn(`数据 ${item.name} 坐标点不足,无法绘制`);
      return;
@@ -187,7 +200,7 @@
    });
  });
  focusOnAllFeatures();
  viewInstance.value?.addLeftClickEvent(null, handleFenceClick, );
  viewInstance.value?.addLeftClickEvent(null, handleFenceClick);
};
//计算所有图斑的包围球并定位
const focusOnAllFeatures = () => {
@@ -254,30 +267,28 @@
  viewInstance.value = publicCesiumInstance;
  const nanChangPosition = Cesium.Cartesian3.fromDegrees(
    115.892151,  // 经度
    28.676493,   // 纬度
    15000        // 相机高度
    115.892151, // 经度
    28.676493, // 纬度
    15000 // 相机高度
  );
  // 执行相机飞行
  viewer.camera.flyTo({
    destination: nanChangPosition,
    duration: 0,
    destination: nanChangPosition,
    duration: 0,
    orientation: {
      heading: Cesium.Math.toRadians(0),
      pitch: Cesium.Math.toRadians(-90),
      roll: 0
    }
      heading: Cesium.Math.toRadians(0),
      pitch: Cesium.Math.toRadians(-90),
      roll: 0,
    },
  });
};
// 编辑/绘制
const loadPlanarRoute = async (positions = null, save = false) => {
  if (positions) {
    curPolygonPosition = positions.map(item => {
      let cartographic = Cesium.Cartographic.fromCartesian(item);
      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); //高度
@@ -289,41 +300,33 @@
    });
  }
  let polygon = curPolygonPosition.map(item => [item?.lng, item?.lat]);
  let polygonString = JSON.stringify(polygon);
  // polygon.push([curPolygonPosition[0]?.lng, curPolygonPosition[0]?.lat]);
  layerParams.value.polygonPosition = polygonString
   if (polygon.length >= 3) {
  layerParams.value.polygonPosition = polygonString;
  if (polygon.length >= 3) {
    //  确保多边形闭合:最后一个点与第一个点一致(避免计算偏差)
    const firstPoint = polygon[0];
    const lastPoint = polygon[polygon.length - 1];
    const isClosed = (
    const isClosed =
      _.round(firstPoint[0], 6) === _.round(lastPoint[0], 6) &&
      _.round(firstPoint[1], 6) === _.round(lastPoint[1], 6)
    );
      _.round(firstPoint[1], 6) === _.round(lastPoint[1], 6);
    // 若未闭合,手动添加闭合点
    const closedPolygon = isClosed ? polygon : [...polygon, firstPoint];
    //  转换为 turf 支持的 GeoJSON 格式(Polygon 类型需外层嵌套数组)
    const turfPolygon = turf.polygon([closedPolygon]);
    //  计算面积(turf.area() 返回平方米,保留2位小数)
    const area = _.round(turf.area(turfPolygon), 2);
    //  赋值给 layerParams,供页面显示或提交使用
    //  赋值给 layerParams,
    layerParams.value.fenceArea = area;
    console.log('计算的多边形面积(平方米):', layerParams.value.fenceArea);
  } else {
    // 坐标点不足3个时,清空面积
    layerParams.value.fenceArea = 0;
  }
  };
  const throttleLoadPlanarRoute = throttle(loadPlanarRoute, 200);
  drawPolygonExample.subscribe('getPolygonPositions', data => {
    throttleLoadPlanarRoute(data);
  });
};
const throttleLoadPlanarRoute = throttle(loadPlanarRoute, 200);
drawPolygonExample.subscribe('getPolygonPositions', data => {
  throttleLoadPlanarRoute(data);
});
// 地图销毁
const destroyMap = () => {
@@ -352,7 +355,7 @@
onMounted(() => {
  initMap();
  cesiumContextMenu();
  getdataFolderApi()
  getdataFolderApi();
});
onBeforeUnmount(() => {