无人机管理后台前端(已迁走)
张含笑
2025-10-30 6f3c7a7f01998a51cc70f7a6b5133a8253b51454
feat:计算面积
2 files modified
138 ■■■■■ changed files
src/views/layerManagement/components/rightEdit.vue 56 ●●●● patch | view | raw | blame | history
src/views/layerManagement/index.vue 82 ●●●● patch | view | raw | blame | history
src/views/layerManagement/components/rightEdit.vue
@@ -52,17 +52,11 @@
    </div>
    <div class="detailInfo">
      <div class="infpItem" v-if="layerParams.fenceType === 1">
        <div class="itemData">
          <div class="title">围栏总数量</div>
          <div class="title">围栏面积</div>
          <div class="num">
            <span>{{ totalCount }}</span
            >个
          </div>
        </div>
        <div class="itemData">
          <div class="title">围栏总面积</div>
          <div class="num">
            <span>{{ totalArea }}</span
            <span>{{ fenceArea }}</span
            >m²
          </div>
        </div>
@@ -136,30 +130,38 @@
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) => {
    if (newArea !== undefined && newArea !== null) {
      fenceArea.value = newArea;
      formData.value.area = newArea;
    }
  },
  { immediate: true }
);
watch(
  () => timeValue.value,
  (newTimeArr) => {
    // timeValue 是数组:[开始时间, 结束时间](未选择时为 '' 或空数组)
    if (Array.isArray(newTimeArr) && newTimeArr.length === 2) {
      const startTime = newTimeArr[0]; // 开始时间(Date 对象或时间字符串)
      const endTime = newTimeArr[1];   // 结束时间(Date 对象或时间字符串)
      // 用 dayjs 格式化为 "YYYY-MM-DD HH:mm:ss" 格式
    if (Array.isArray(newTimeArr) && newTimeArr.length === 2) {
      const startTime = newTimeArr[0];
      const endTime = newTimeArr[1];
      formData.value.control_start_time = dayjs(startTime).format('YYYY-MM-DD HH:mm:ss');
      formData.value.control_end_time = dayjs(endTime).format('YYYY-MM-DD HH:mm:ss');
      console.log('格式化后开始时间:', formData.value.control_start_time);
      console.log('格式化后结束时间:', formData.value.control_end_time);
    } else {
      // 未选择时间时,清空两个字段
      formData.value.control_start_time = '';
      formData.value.control_end_time = '';
    }
  },
  { immediate: true } // 初始化时执行一次,处理默认值
  { immediate: true }
);
const clearAllData = () => {
  detailData.value = {};
@@ -229,10 +231,10 @@
onMounted(() => {
  // 获取判断状态(默认设为 1)
  const addOrEditStatus = layerParams.value.decideWhetherToAddOrEdit || 1;
  if (addOrEditStatus === 2) {
    // 编辑状态:回显数据(从 layerParams.editDetailData 读取)
    const editData = layerParams.value.editDetailData || {};
    formData.value = {
      folder_id: editData.folder_id || '',
@@ -245,22 +247,24 @@
      control_end_time: editData.control_end_time || '',
      geo_data: editData.geo_data || '',
    };
     if (editData.folder_id && options.value.length > 0) {
     if (editData.folder_id && options.value.length > 0) {
      const matchedFolder = options.value.find(
        item => item.value === String(editData.folder_id)
      );
      if (matchedFolder) {
        formData.value.folder_id = matchedFolder.value;
      }
    }
 if (editData.control_start_time && editData.control_end_time) {
      timeValue.value = [
        dayjs(editData.control_start_time).toDate(),
        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;
src/views/layerManagement/index.vue
@@ -29,6 +29,7 @@
</template>
<script setup>
import * as turf from '@turf/turf'
import { dataFolderApi } from '@/api/layer/index';
import folderFile from '@/views/layerManagement/components/folderFile.vue'
import { parseGeoDataToPositions } from '@/utils/geoParseUtil';
@@ -42,6 +43,12 @@
import _, { cloneDeep, throttle } from 'lodash';
import { ElButton } from 'element-plus';
import { provide } from 'vue';
import { useStore } from 'vuex';
const store = useStore();
const userInfo = computed(() => store.getters.userInfo);
const areaCode = userInfo.value?.detail?.areaCode || '';
// console.log('用户信息',areaCode);
const activeName = ref('电子围栏');
const activeType = ref('0');
let tbJwdList = [];
@@ -75,6 +82,7 @@
  editFolder:false,
  folderOption:[] ,//文件夹选项
  fenceType:1,
  fenceArea:0,//围栏面积
});
const handleClick = tab => {
@@ -179,11 +187,7 @@
    });
  });
  focusOnAllFeatures();
  //  添加点击事件(参考 entitiesAddSpot 的 spotHighlighting 逻辑)
  // 移除已有点击事件,避免重复绑定
  // viewInstance.value?.removeLeftClickEvent('fenceHighlighting');
  // 绑定新的点击事件,用于高亮选中的围栏
  // viewInstance.value?.addLeftClickEvent(null, handleFenceClick, 'fenceHighlighting');
  viewInstance.value?.addLeftClickEvent(null, handleFenceClick, );
};
//计算所有图斑的包围球并定位
const focusOnAllFeatures = () => {
@@ -203,26 +207,24 @@
    ),
  });
};
// 围栏点击处理函数(高亮选中项 + 可联动右侧编辑组件)
// 围栏点击处理函数
const handleFenceClick = movement => {
  if (!viewer) return;
  // 拾取点击的实体
  const pickedObject = viewer.scene.pick(movement.position);
  if (Cesium.defined(pickedObject) && pickedObject.id?.customType === 'fence_polygon') {
    const selectedEntity = pickedObject.id;
    console.log('选中的围栏数据:', selectedEntity.customInfo);
    // 1. 高亮处理(清除其他实体高亮,仅高亮当前选中)
    viewer.entities.values.forEach(entity => {
      if (entity.customType === 'fence_polygon') {
        // 恢复默认样式
        entity.polygon.material =
          entity === selectedEntity
            ? selectedEntity.polygon.material.color.withAlpha(0.8) // 选中时加深透明度
            : selectedEntity.polygon.material.color.withAlpha(0.5); // 未选中恢复默认
      }
    });
    // // 1. 高亮处理(清除其他实体高亮,仅高亮当前选中)
    // viewer.entities.values.forEach(entity => {
    //   if (entity.customType === 'fence_polygon') {
    //     // 恢复默认样式
    //     entity.polygon.material =
    //       entity === selectedEntity
    //         ? selectedEntity.polygon.material.color.withAlpha(0.8) // 选中时加深透明度
    //         : selectedEntity.polygon.material.color.withAlpha(0.5); // 未选中恢复默认
    //   }
    // });
    // 2. 联动右侧编辑组件(假设 rightEdit 有接收数据的方法)
    // 例如:通过 ref 调用 rightEdit 的 setData 方法
@@ -238,6 +240,7 @@
const drawPolygonExample = new DrawPolygon();
// 地图初始化
// 地图初始化
const initMap = () => {
  publicCesiumInstance = new PublicCesium({
    dom: 'layMap',
@@ -249,6 +252,23 @@
  viewer = publicCesiumInstance.getViewer();
  viewInstance.value = publicCesiumInstance;
  const nanChangPosition = Cesium.Cartesian3.fromDegrees(
    115.892151,  // 经度
    28.676493,   // 纬度
    15000        // 相机高度
  );
  // 执行相机飞行
  viewer.camera.flyTo({
    destination: nanChangPosition,
    duration: 0,
    orientation: {
      heading: Cesium.Math.toRadians(0),
      pitch: Cesium.Math.toRadians(-90),
      roll: 0
    }
  });
  
};
@@ -271,10 +291,34 @@
  
  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) {
    //  确保多边形闭合:最后一个点与第一个点一致(避免计算偏差)
    const firstPoint = polygon[0];
    const lastPoint = polygon[polygon.length - 1];
    const isClosed = (
      _.round(firstPoint[0], 6) === _.round(lastPoint[0], 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.value.fenceArea = area;
    console.log('计算的多边形面积(平方米):', layerParams.value.fenceArea);
  } else {
    // 坐标点不足3个时,清空面积
    layerParams.value.fenceArea = 0;
  }
  };
  const throttleLoadPlanarRoute = throttle(loadPlanarRoute, 200);
  drawPolygonExample.subscribe('getPolygonPositions', data => {