forked from drone/command-center-dashboard

张含笑
2025-04-10 fee18d19a1d961847cd09469a20fc8231c1df46c
src/views/TaskManage/TaskIntermediateContent/TaskMap.vue
@@ -1,5 +1,5 @@
<template>
   <div id="taskMap" />
   <div id="taskMap"></div>
</template>
<script setup>
import * as Cesium from 'cesium'
@@ -12,9 +12,29 @@
import EndPointicon from '@/assets/images/EndPointicon.png';
import uavImg from '@/assets/images/home/useUavHome/uavImg.png'
import { getCenterPoint } from '@/utils/cesium/mapUtil'
import { getWaylineByArea } from '@/api/home/task';
import { useStore } from 'vuex';
const props = defineProps(['wayLineFile'])
const store = useStore();
const userAreaPosition = computed(() => store.state.home.userAreaPosition);
// 声明事件
const emit = defineEmits(['clickPosition', 'saveWayline']);
const props = defineProps({
   wayLineFile: {
    type: String,
    default: ''
  },
  checkedTableData: {
    type: Array,
    default: () => []
  },
   waylineTypeTest: {
    type: Number,
    default: 3
  }
})
const imageryProvider_ammapSL = new Cesium.UrlTemplateImageryProvider({
   url: 'https://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}',
@@ -26,8 +46,18 @@
   maximumLevel: 18,
   tilingScheme: new AmapMercatorTilingScheme(),
   credit: 'amap_SL',
})
let viewer = null
});
let viewer = null;
let currentEntity = null;
let connectLines = []; // 存储连接线实体
let polygonPoints = []; // 存储多边形的点
let polygonEntity = null; // 存储多边形实体
let existingEntity = null; // 后端返回数据生成得面状线
// 添加变量跟踪当前菜单
let currentMenu = null;
const init = () => {
   viewer = new Viewer('taskMap', {
      terrain: Terrain.fromWorldTerrain(),
@@ -46,22 +76,214 @@
   })
   viewer.imageryLayers.addImageryProvider(imageryProvider_ammapSL)
   viewer.scene.morphTo2D(0);
   //设置默认点
   const { longitude = 115.763819, latitude = 28.787374, height = 10 } = userAreaPosition.value || {};
   viewer.camera.setView({
      destination: Cartesian3.fromDegrees(longitude, latitude, height),
   });
}
// 渲染线和点
// 单点左键点击事件
const singlePointLeftClick = () => {
  viewer.screenSpaceEventHandler.setInputAction((click) => {
      if (props.waylineTypeTest !== 1) return
    const cartesian = viewer.camera.pickEllipsoid(
      click.position,
      viewer.scene.globe.ellipsoid
    );
    if (cartesian) {
      // 清除之前的实体
      viewer.entities.removeAll();
      // 添加新点
      const point = viewer.entities.add({
        position: cartesian,
        point: {
          pixelSize: 10,
          color: Cesium.Color.WHITE
        }
      });
      // 转换坐标
      const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
      const longitude = Cesium.Math.toDegrees(cartographic.longitude);
      const latitude = Cesium.Math.toDegrees(cartographic.latitude);
      // 更新当前实体引用
      currentEntity = point;
      // 发送坐标
      emit('clickPosition', { longitude, latitude });
    }
  }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
};
// 智能规划航线
const intelligentPlanning = () => {
   // 添加点击事件监听
  viewer.screenSpaceEventHandler.setInputAction((click) => {
      if (props.waylineTypeTest !== 2) return
      const cartesian = viewer.camera.pickEllipsoid(
         click.position,
         viewer.scene.globe.ellipsoid
      );
      if (cartesian) {
         // 添加新点
         const point = viewer.entities.add({
            position: cartesian,
            point: {
               pixelSize: 10,
               color: Cesium.Color.WHITE
            }
         });
         // 存储点位
         polygonPoints.push(cartesian);
         // 当点击超过2个点时绘制多边形
         if (polygonPoints.length > 2) {
            // 移除旧的多边形
            if (polygonEntity) {
               viewer.entities.remove(polygonEntity);
            }
            // 创建新的多边形
            polygonEntity = viewer.entities.add({
               polygon: {
                  hierarchy: new Cesium.PolygonHierarchy(polygonPoints),
                  // material: new Cesium.Color.fromBytes(212, 46, 32, 100),
                  material: new Cesium.Color(0, 0.5, 1, 0.3), // 蓝色
                  outline: true,
                  // outlineColor: new Cesium.Color.fromBytes(212, 46, 32, 255),
                  outlineColor: new Cesium.Color(0, 0.5, 1, 1), // 蓝
                  outlineWidth: 2,
                  height: 0, // Set explicit height
                  heightReference: Cesium.HeightReference.NONE // Disable terrain clamping
               }
            });
         }
         // 转换坐标并发送
         const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
         const longitude = Cesium.Math.toDegrees(cartographic.longitude);
         const latitude = Cesium.Math.toDegrees(cartographic.latitude);
         // emit('clickPosition', cartographic);
      }
   }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
   // 修改右键点击事件,添加菜单
   viewer.screenSpaceEventHandler.setInputAction((movement) => {
      if (props.waylineTypeTest !== 2) return
      if (polygonPoints.length > 2) {
         // 清除之前的菜单
         if (currentMenu) {
            document.body.querySelectorAll('.context-menu').forEach(menu => menu.remove());
         }
         const menuContainer = document.createElement('div');
         menuContainer.className = 'context-menu';
         // 获取地图容器
         const mapContainer = document.getElementById('taskMap');
         // 使用鼠标右键点击的实际位置
         menuContainer.style.position = 'absolute';
         menuContainer.style.left = `${movement.position.x}px`;
         menuContainer.style.top = `${movement.position.y}px`;
         menuContainer.style.zIndex = '1000';
         menuContainer.innerHTML = `
            <div class="menu-item" id="saveWayline">保存航线</div>
            <div class="menu-item" id="cancelDraw">取消绘制</div>
         `;
         mapContainer.appendChild(menuContainer);
         currentMenu = menuContainer;
         // 添加全局点击事件监听
         const handleClickOutside = (e) => {
            if (!menuContainer) return;
            const isClickInside = menuContainer.contains(e.target);
            if (!isClickInside) {
               menuContainer.remove();
               document.removeEventListener('mousedown', handleClickOutside);
            }
         };
         // 延迟添加事件监听,避免右键点击立即触发
         setTimeout(() => {
            document.addEventListener('mousedown', handleClickOutside);
         }, 100);
         // 菜单按钮点击事件
         document.getElementById('saveWayline').onclick = () => {
            const coordinates = polygonPoints.map(point => {
               const cartographic = Cesium.Cartographic.fromCartesian(point);
               return {
                  longitude: Cesium.Math.toDegrees(cartographic.longitude),
                  latitude: Cesium.Math.toDegrees(cartographic.latitude)
               };
            });
            emit('saveWayline', coordinates);
            saveWaylineByArea(coordinates);
            mapContainer.removeChild(menuContainer);
         };
         document.getElementById('cancelDraw').onclick = () => {
            polygonPoints = [];
            viewer.entities.removeAll();
            mapContainer.removeChild(menuContainer);
         };
      }
   }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
};
// 保存航线并且获取线
const saveWaylineByArea = (dataValue) => {
   const polygonArray = dataValue.map(point => [point.longitude, point.latitude]);
   getWaylineByArea({ type: 2, polygon: polygonArray }).then(res => {
      if (res.data.code !== 0) retrun;
      drawResultWayline(res.data.data);
   });
}
// 绘制后端生成得面状线
const drawResultWayline = (dataValue) => {
   // 先检查并删除已存在的航线
  existingEntity = viewer.entities.getById('result_wayline');
  if (existingEntity) {
    viewer.entities.remove(existingEntity);
  }
   const cartesian3List = ref([]);
   dataValue.forEach((lnglat) => {
      const cartesian3 = Cesium.Cartesian3.fromDegrees(
         Number(lnglat.x),
         Number(lnglat.y),
         Number(100), // 默认100
      )
      cartesian3List.value.push(cartesian3)
   });
   const setting = {
      id: 'result_wayline',
      polyline: {
         width: 2,
         positions: cartesian3List.value,
         material: Cesium.Color.CHARTREUSE,
      },
   }
   existingEntity = viewer?.entities.add({
      polyline: setting.polyline,
      id: setting.id,
   })
};
// 选中航线时调用 渲染线和点 type = 0
const renderingLine = lineObj => {
   const positions = lineObj.Placemark.map(item => {
      const [lon, lat] = item.Point.coordinates.split(',')
      return Cartesian3.fromDegrees(Number(lon), Number(lat))
   })
   // viewer.entities.add({
   //    position: positions[0],
   //    billboard: {
   //       image: new Cesium.ConstantProperty(uavImg),
   //       width: 24,
   //       height: 24,
   //    },
   // });
   viewer.entities.add({
      polyline: {
         width: 4,
@@ -75,24 +297,26 @@
         clampToGround: false,
      },
   })
  console.log('positions',positions)
  positions.forEach((point, index) => {
    let setting = {};
    if (index === 0) {
      //TODO
    } else if (index === 1) {
      setting = {
        position: point,
        id: `point_${index}`,
        billboard: {
          image: Startingpointicon,
          outlineWidth: 0,
          width: 20,
          height: 20,
          scale: 1.0,
        },
      }
    } else if (index === positions.length - 1) {
    // if (index === 0) {
    //   //TODO
    // }
      // else if (index === 1) {
    //   setting = {
    //     position: point,
    //     id: `point_${index}`,
    //     billboard: {
    //       image: Startingpointicon,
    //       outlineWidth: 0,
    //       width: 20,
    //       height: 20,
    //       scale: 1.0,
    //     },
    //   }
    // } else
      if (index === positions.length - 1) {
      setting = {
        position: point,
        id: `point_${index}`,
@@ -109,7 +333,7 @@
        position: point,
        id: `point_${index}`,
        label: {
          text: `${index}`,
          text: `${index+1}`,
          font: 'bold 14px serif',
          style: Cesium.LabelStyle.FILL,
          verticalOrigin: Cesium.VerticalOrigin.CENTER, // 垂直居中
@@ -144,25 +368,11 @@
   })
}
// const drawLine = () => {
//   console.log('尽量么');
//    // props.wayLineFile.way_lines.forEach(item => {
//   // let prexUrl = ref(import.meta.env.VITE_APP_AIRLINE_URL+ props.wayLineFile);
//   let prexUrl2 = ref('https://wrj.shuixiongit.com/minio/cloud-bucket/wayline/20241224/wayline_1735032173222.kmz');
//       analyzeKmzFile(`${prexUrl2.value}?_t=${new Date().getTime()}`).then(async res => {
//          const templateXML = await res.fileInfoObj['wpmz/template.kml']
//          const templateXMLJSON = XMLToJSON(templateXML)?.['Document']
//          const templateXMLObj = removeTextKey(templateXMLJSON.Folder)
//          renderingLine(templateXMLObj)
//       })
//    // })
// }
// 异步解析kmz文件
const analysis = async url => {
   return new Promise(async resolve => {
      const res = await analyzeKmzFile(`${url}?_t=${new Date().getTime()}`)
      const res = await analyzeKmzFile(`${url}?_t=${new Date().getTime()}`);
      const templateXML = await res.fileInfoObj['wpmz/template.kml']
      const templateXMLJSON = XMLToJSON(templateXML)?.['Document']
      const templateXMLObj = removeTextKey(templateXMLJSON.Folder)
@@ -174,37 +384,239 @@
const drawLine = async () => {
  let prexUrl = ref(import.meta.env.VITE_APP_AIRLINE_URL+ props.wayLineFile);
   const res = await analysis(prexUrl.value);
   if (!res.Placemark.length) return
  renderingLine(res);
   const points = res.Placemark.map(item => item.Point.coordinates.split(','))
   flyToPoints(points)
}
watch(() => props.wayLineFile, async (newValue, oldValue) => {
   if(newValue){
    await removeMap();
    await init();
// 选择航线时,根据选择机巢连线
const selectLineFile = (newVal) => {
   // 清除之前的实体
   // viewer.entities.removeAll();
   // 重新绘制航线
   // if (props.wayLineFile) {
   //   drawLine();
   // }
   // 添加选中点和连接线
   const positions = newVal.map(item => {
      const position = Cartesian3.fromDegrees(Number(item.longitude), Number(item.latitude));
      // 添加机巢点
      viewer.entities.add({
         position: position,
         billboard: {
            image: new Cesium.ConstantProperty(uavImg),
            width: 24,
            height: 24,
         },
      });
      return position;
   });
   // 添加连接线
   if (positions.length > 1) {
      viewer.entities.add({
         polyline: {
            positions: positions,
            width: 2,
            material: new Cesium.PolylineDashMaterialProperty({
               color: Cesium.Color.RED,
               dashLength: 8.0
            })
         }
      });
   }
   // 飞到中心点
   const lngLatArr = newVal.map(item => [item.longitude, item.latitude]);
   flyToPoints(lngLatArr);
};
// 单个点生成选择多个机巢生成航线
const singlePointLines = (newVal) => {
   // 清除之前的连接线
   connectLines.forEach(line => viewer.entities.remove(line));
   connectLines = [];
   if (currentEntity) {
      // 获取当前点的位置
      const currentPosition = currentEntity.position.getValue();
      // 为每个选中的机巢创建点和连接线
      newVal.forEach(item => {
         // 创建机巢点
         const nestPosition = Cartesian3.fromDegrees(
            Number(item.longitude),
            Number(item.latitude)
         );
         viewer.entities.add({
            position: nestPosition,
            billboard: {
               image: new Cesium.ConstantProperty(uavImg),
               width: 24,
               height: 24,
            },
         });
         // 创建连接线
         const line = viewer.entities.add({
            polyline: {
               positions: [currentPosition, nestPosition],
               width: 2,
               material: new Cesium.PolylineDashMaterialProperty({
                  color: Cesium.Color.RED,
                  dashLength: 8.0
               })
            }
         });
         connectLines.push(line);
      });
      // 飞到所有点的中心位置
      const lngLatArr = newVal.map(item => [item.longitude, item.latitude]);
      flyToPoints(lngLatArr);
   }
};
// 智慧规划航线-面状航线
const planarPointsLines = (newVal) => {
  // 如果存在面状航线
  if (existingEntity) {
    const waylinePositions = existingEntity.polyline.positions.getValue();
    newVal.forEach(item => {
      // 创建机巢点
      const nestPosition = Cartesian3.fromDegrees(
        Number(item.longitude),
        Number(item.latitude)
      );
      // 添加机巢图标
      viewer.entities.add({
        position: nestPosition,
        billboard: {
          image: new Cesium.ConstantProperty(uavImg),
          width: 24,
          height: 24,
        },
      });
      // 找到最近的航线点并连线
      let minDistance = Number.MAX_VALUE;
      let closestPosition = null;
      waylinePositions.forEach(waylinePos => {
        const distance = Cartesian3.distance(nestPosition, waylinePos);
        if (distance < minDistance) {
          minDistance = distance;
          closestPosition = waylinePos;
        }
      });
      // 创建连接线
      if (closestPosition) {
        const line = viewer.entities.add({
          polyline: {
            positions: [nestPosition, closestPosition],
            width: 2,
            material: new Cesium.PolylineDashMaterialProperty({
              color: Cesium.Color.CHARTREUSE,
              dashLength: 8.0
            })
          }
        });
        connectLines.push(line);
      }
    });
    // 飞到所有点的中心位置
    const lngLatArr = newVal.map(item => [item.longitude, item.latitude]);
    flyToPoints(lngLatArr);
  }
};
// 监听选择航线文件事件
watch(() => props.wayLineFile, async (newVal, oldValue) => {
   await removeMap();
   if(newVal){
      await drawLine();
   }
})
}, { deep: true });
// 监听表格选中数据变化
watch(() => props.checkedTableData, (newVal) => {
   if (newVal.length > 0 && props.waylineTypeTest === 0) {
      selectLineFile(newVal);
   } else if (newVal.length > 0 && props.waylineTypeTest === 1) {
      singlePointLines(newVal);
   } else if (newVal.length > 0 && props.waylineTypeTest === 2) {
      planarPointsLines(newVal);
   }
}, { deep: true });
// 监听航线类型
watch(() => props.waylineTypeTest, async (newVal) => {
   await removeMap();
   if (newVal === 1) {
      await singlePointLeftClick();
   } else if (newVal === 2) {
      await intelligentPlanning();
   }
}, { deep: true });
const removeEvent = () => {
   // 清除事件监听器
   viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
   viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK);
};
const removeMap = () => {
   viewer.entities.removeAll()
   viewer.destroy()
  // 清除所有实体
  if (viewer) {
    // 清除连接线
    connectLines.forEach(line => viewer.entities.remove(line));
    connectLines = [];
    // 清除多边形点和实体
    polygonPoints = [];
    if (polygonEntity) {
      viewer.entities.remove(polygonEntity);
    }
    // 清除当前实体和面状航线
    if (currentEntity) {
      viewer.entities.remove(currentEntity);
    }
    if (existingEntity) {
      viewer.entities.remove(existingEntity);
    }
      viewer.entities.removeAll();
    // 重置所有变量
    currentEntity = null;
    polygonEntity = null;
    existingEntity = null;
  }
}
onBeforeUnmount(() => {
   removeMap()
   removeMap();
   removeEvent();
   // 移除所有实体并销毁viewer
   viewer.destroy();
   viewer = null;
})
onMounted(() => {
   nextTick(() => {
      init()
   })
})
});
</script>
<style scoped lang="scss">
#taskMap {
   position: relative;
   height: 100%;
   :deep() {
@@ -232,5 +644,27 @@
         display: none;
      }
   }
:deep(.context-menu) {
   position: absolute;
   background: rgba(0, 21, 41, 0.9);;
   border-radius: 4px;
   padding: 8px 0;
   min-width: 120px;
   box-shadow: 0 2px 12px rgba(0, 0, 0, 0.3);
      .menu-item {
         padding: 8px 16px;
         color: #fff;
         cursor: pointer;
         transition: all 0.3s;
         font-size: 14px;
         &:hover {
            background: rgba(255, 255, 255, 0.1);
         }
      }
   }
}
</style>