无人机管理后台前端(已迁走)
张含笑
2025-06-12 81701042c720dbf040d5b3820b363f0a656df46b
src/views/dataCenter/components/dataCenterMap.vue
@@ -1,42 +1,55 @@
<template>
  <el-dialog modal-class="mapDialog" v-model="isShow"  width="80%">
  <el-dialog modal-class="mapDialog" v-model="isShow" width="80%">
    <div class="mapBox">
      <div id="dataCenterMap" class="ztzf-cesium"></div>
      <div v-if="isShow" id="dataCenterMap" class="ztzf-cesium"></div>
    </div>
  </el-dialog>
</template>
<script setup>
import { getMapInfoAPI } from '@/api/dataCenter/dataCenter';
import { useStore } from 'vuex';
import { PublicCesium } from '@/utils/cesium/publicCesium';
import { Cartesian3 } from 'cesium';
import * as Cesium from 'cesium';
import EventPopUpBox from '@/hooks/components/EventPopUpBox.vue';
import { render } from 'vue';
import defaultIcon from '@/assets/images/dataCenter/datamap/eventCompleted.png';
import { render, nextTick, watch, onMounted, onBeforeUnmount, shallowRef, ref, h } from 'vue';
import defaultIcon from '@/assets/images/dataCenter/datamap/eventCompleted.png'; //默认图标
import activeIcon from '@/assets/images/dataCenter/datamap/activeevent.png'; // 激活图标
const emit = defineEmits(['lookDetail']);
const isShow = defineModel('show');
const viewerRef = shallowRef(null);
let viewer = null;
const viewInstance = shallowRef(null);
const store = useStore();
const currentAreaPosition = ref({ height: 1987280, latitude: 27.636112, longitude: 115.732975 });
let handler = null;
const props = defineProps(['jobId','mapList']);
// 存储地图实体引用
const dataPointEntities = ref([]);
const isMapInitialized = ref(false); //地图加载
const dataPointList = ref([]);
const activeEntity = ref(null); // 当前激活的点
// 获取弹框box
const detailId = ref('')
const createLabelDom = data => {
console.log('data',data);
   const vNode = h(EventPopUpBox, { data, removeLabel })
      const tooltipContainer = document.createElement('div')
      tooltipContainer.id = 'mapPopUpBox'
      tooltipContainer.style.position = 'absolute'
      tooltipContainer.style.transform = 'translate(-50%,-135%)'
      tooltipContainer.style.pointerEvents = 'none'
      document.querySelector('#dataCenterMap').append(tooltipContainer)
      render(vNode, tooltipContainer)
      return tooltipContainer
  detailId.value = data
  const vNode = h(EventPopUpBox, { data, removeLabel, detailClick });
  const tooltipContainer = document.createElement('div');
  tooltipContainer.id = 'mapPopUpBox';
  tooltipContainer.style.position = 'absolute';
  tooltipContainer.style.transform = 'translate(-50%,-125%)';
  tooltipContainer.style.pointerEvents = 'none';
  document.querySelector('#dataCenterMap').append(tooltipContainer);
  render(vNode, tooltipContainer);
  return tooltipContainer;
};
let currentClickEntity = null;
// 弹框位置刷新
const labelBoxUpdate = () => {
  if (!currentClickEntity) return;
@@ -53,6 +66,10 @@
    dom.style.display = 'block';
  }
};
// 暴露方法给父组件
defineExpose({
  labelBoxUpdate
});
const removeDom = () => {
  const dom = document.querySelector('#mapPopUpBox');
  if (dom && dom.parentNode) {
@@ -64,13 +81,48 @@
const removeLabel = () => {
  viewer?.scene.postRender.removeEventListener(labelBoxUpdate);
  removeDom();
};
// 点击去到详情页面
const detailClick = () => {
  removeLabel()
  // 给父组件传值
  emit('update:show', false);
  emit('lookDetail', detailId.value);
}
// 恢复所有点的默认图标
const restoreAllIcons = () => {
  dataPointEntities.value.forEach(entity => {
    if (entity.billboard) {
      entity.billboard.image = defaultIcon;
    }
  });
  activeEntity.value = null;
};
// 左键单机事件
const singleMachineEvent = async click => {
  let clickedEntities = viewer?.scene.drillPick(click.position).map(item => item.id);
  if (!clickedEntities.length) return;
  if (!clickedEntities.length) {
    // 点击空白处恢复所有图标并移除弹窗
    restoreAllIcons();
    removeLabel();
    return;
  }
  currentClickEntity = clickedEntities[0];
  // 恢复所有点的默认图标
  restoreAllIcons();
  // 设置当前点击点为激活状态
  if (currentClickEntity.billboard) {
    currentClickEntity.billboard.image = activeIcon;
    activeEntity.value = currentClickEntity;
  }
  removeLabel();
  viewer.scene.postRender.addEventListener(labelBoxUpdate);
};
@@ -82,38 +134,36 @@
  handler.setInputAction(singleMachineEvent, Cesium.ScreenSpaceEventType.LEFT_CLICK);
};
// 清除所有数据点实体
const clearDataPoints = () => {
  if (!viewer) return;
  dataPointEntities.value.forEach(entity => {
    viewer.entities.remove(entity);
  });
  dataPointEntities.value = [];
  activeEntity.value = null;
};
const removeHandler = () => {
  handler?.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
  handler?.destroy();
  handler = null;
};
const dataPointList = [
  {
    longitude: '115.85708286111111',
    latitude: '28.62837602777778',
  },
  {
    longitude: '115.8566981111111',
    latitude: '28.624613027777777',
  },
  {
    longitude: '115.85474438888889',
    latitude: '28.624930444444445',
  },
  {
    longitude: '115.85518375',
    latitude: '28.62479547222222',
  },
];
function renderDataPoint() {
  dataPointList.forEach((item, index) => {
    const sizeObj = { width: 30, height: 30 };
    viewer.entities.add({
      id: `renderDataPoint-${index}`,
      position: Cesium.Cartesian3.fromDegrees(Number(item.longitude), Number(item.latitude)),
const renderDataPoint = mapList => {
  if (!viewer || !mapList?.length) return;
  // 清除旧实体
  clearDataPoints();
  // 添加新实体
  mapList.forEach((item, index) => {
    const entity = viewer.entities.add({
      id: `dataCenter-point-${index}-${Date.now()}`,
      position: Cesium.Cartesian3.fromDegrees(
        Number(item.metadata.shootPosition.lng),
        Number(item.metadata.shootPosition.lat)
      ),
      label: {
        font: '12pt Source Han Sans CN',
        fillColor: Cesium.Color.WHITE,
@@ -121,11 +171,14 @@
        outlineWidth: 2,
        style: Cesium.LabelStyle.FILL_AND_OUTLINE,
        verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
        pixelOffset: new Cesium.Cartesian2(0, -9),
        eyeOffset: new Cesium.Cartesian3(0, 0, -9),
        pixelOffset: new Cesium.Cartesian2(0, 55),
      },
      billboard: {
        image: defaultIcon,
        ...sizeObj,
        image: defaultIcon, // 初始为默认图标
        width: 40,
        height: 40,
        pixelOffset: new Cesium.Cartesian2(0, -15),
      },
      properties: {
        customData: {
@@ -133,67 +186,116 @@
        },
      },
    });
    dataPointEntities.value.push(entity);
  });
}
};
const initMap = () => {
  const publicCesiumInstance = new PublicCesium({
    dom: 'dataCenterMap',
    flatMode: false,
    terrain: false,
    mapFilter: true,
  });
  viewerRef.value = publicCesiumInstance.getViewer();
  viewer = publicCesiumInstance.getViewer();
  const { longitude, latitude, height } = currentAreaPosition.value;
  const position = Cartesian3.fromDegrees(longitude, latitude, height);
  viewerRef.value.camera.flyTo({
    duration: 0,
    destination: position,
    orientation: {
      heading: Cesium.Math.toRadians(0.0),
      pitch: Cesium.Math.toRadians(-90.0),
      roll: 0.0,
    },
  });
  viewInstance.value = publicCesiumInstance;
  if (viewer || isMapInitialized.value) return;
  const container = document.getElementById('dataCenterMap');
  if (!container) {
    console.error('地图容器未找到');
    return;
  }
  try {
    const publicCesiumInstance = new PublicCesium({
      dom: 'dataCenterMap',
      flatMode: false,
      terrain: false,
      mapFilter: true,
    });
    viewer = publicCesiumInstance.getViewer();
    viewerRef.value = viewer;
    // 相机飞行
   const { longitude, latitude, height } = currentAreaPosition.value
   const position = Cartesian3.fromDegrees(longitude, latitude, height)
   viewerRef.value.camera.flyTo({
      duration: 0,
      destination: position,
      orientation: {
         heading: Cesium.Math.toRadians(0.0),
         pitch: Cesium.Math.toRadians(-90.0),
         roll: 0.0,
      },
   })
    // 初始化事件处理器
    handlerInit();
    isMapInitialized.value = true;
    console.log('地图初始化完成');
    // 初始化后立即渲染已有数据
    if (dataPointList.value.length > 0) {
      renderDataPoint(dataPointList.value);
    }
  } catch (error) {
    console.error('地图初始化失败:', error);
  }
};
// 地图接口
const loading = ref(false);
const getMapInfoAPIFun = async ids => {
  try {
    const res = await getMapInfoAPI(ids);
    dataPointList.value = res.data.data || [];
    // 确保地图已初始化后再渲染
    if (isMapInitialized.value && viewer) {
      renderDataPoint(dataPointList.value);
    }
  } catch (error) {
    console.error('获取地图数据失败:', error);
  }
};
watch(() => props.jobId, (newVal) => {
  if (newVal) {
    getMapInfoAPIFun(newVal);
  }
}, { immediate: true });
// 监听对话框状态
watch(isShow, (newVal) => {
  if (newVal) {
    nextTick(() => {
      initMap();
      handlerInit();
      renderDataPoint();
    });
  } else {
    // 对话框关闭时清理资源
    viewInstance.value?.viewerDestroy();
    // 清理资源
    if (viewer) {
      viewer.destroy();
      viewer = null;
    }
    isMapInitialized.value = false;
    removeHandler();
    clearDataPoints();
  }
});
onMounted(() => {
  nextTick(() => {
    initMap();
    handlerInit();
    renderDataPoint();
  });
});
onMounted(() => {});
onBeforeUnmount(() => {
  viewInstance.value?.viewerDestroy();
  if (viewer) {
    viewer.destroy();
  }
  removeHandler();
  clearDataPoints();
});
</script>
<style >
<style>
.mapDialog .el-dialog__body {
  height: 700px !important;
  padding: 0 !important;
}
</style>
<style scoped lang="scss">
<style scoped lang="scss">
.mapBox {
  z-index: 2;
  height: 650px;
@@ -207,4 +309,4 @@
    height: 100%;
  }
}
</style>
</style>