无人机管理后台前端(已迁走)
张含笑
2025-08-14 41b8a8d0ecea96ace27b5afb9ce63b4193420c49
src/views/job/components/DeviceJobDetailsMap.vue
@@ -1,40 +1,122 @@
<template>
   <div id="deviceJobDetailsMap" class="ztzf-cesium">
      <PlanarRouteLineList :curRouteLineData="curRouteLineData" @routeLineListClick="routeLineListClick" />
   </div>
</template>
<script setup>
import PlanarRouteLineList from '@/components/PlanarRouteLineList/PlanarRouteLineList.vue'
import * as Cesium from 'cesium'
import { PublicCesium } from '@/utils/cesium/publicCesium'
const props = defineProps(['detailsData','yuanImages'])
console.log('props.yua',props.yuanImages);
import { useRouteLine } from '@/hooks/useRouteLine/useRouteLine.js'
// 加载航线hook
const { curRouteLineData, routeLineListClick, initViewer, renderPreviewLine } = useRouteLine()
import { flyVisual } from '@/utils/cesium/mapUtil'
import _ from 'lodash'
import photoDirection from '@/assets/images/historyMapPopup/photoDirection-new.png'
import panoramaPoint from '@/assets/images/panorama/panorama-point.png' //全景图标
import { getShowImg } from '@/utils/util'
import { getEventImage } from '@/utils/stateToImageMap/event'
import { getPanoramaList } from '@/api/panorama'
import CreateRouteLine from '@/utils/cesium/createRouteLine'
import { useStore } from 'vuex'
import { getOdmToken } from '@/api/model'
const createRouteLineExample = new CreateRouteLine()
const store = useStore()
const userAreaCode = computed(() => store.state.user.userInfo.detail.areaCode)
let viewer = null
let publicCesiumInstance = null
const viewInstance = shallowRef(null)
import { cloneDeep } from 'lodash'
let handler = null
let curMoveEntity = null
const tokenStr = ref('')
const emit = defineEmits(['showImageeclick'])
const props = defineProps(['detailsData', 'yuanImages', 'jobId'])
const initMap = () => {
   viewer = new PublicCesium({ dom: 'deviceJobDetailsMap' }).getViewer()
   publicCesiumInstance = new PublicCesium({
      dom: 'deviceJobDetailsMap',
      terrain: true,
      flatMode: false,
      layerMode: 4,
   })
   initViewer(viewer)
   viewer = publicCesiumInstance.getViewer()
   viewInstance.value = publicCesiumInstance
   viewer.scene.globe.depthTestAgainstTerrain = true
   createRouteLineExample.initCreateRoute(viewer)
   handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
   handler.setInputAction(click => {
      const pickedObjects = viewer.scene.drillPick(click.position)
      LeftClickEvent(click, pickedObjects, viewer)
   }, Cesium.ScreenSpaceEventType.LEFT_CLICK)
   handler.setInputAction(e => {
      let pick = viewer.scene.drillPick(e.endPosition)
   }, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
}
// 绘制线和飞行
const drawLine = async () => {
   Promise.all(
      props.detailsData.way_lines.map(
         async item => await renderPreviewLine(`${item.url}?_t=${new Date().getTime()}`, item.wayline_type)
         async item =>
            await createRouteLineExample.parsingFiles(
               {
                  ...item,
                  url: `${item.url}?_t=${new Date().getTime()}`,
               },
               {
                  longitude: item.longitude,
                  latitude: item.latitude,
                  height: item.height,
               },
               true,
               false
            )
      )
   )
   ).then(res => {
      createRouteLineExample.showDetailMapPopup(props.detailsData.way_lines)
      let arr = res.map(i => i.filePositions)
      let newArr = _.flatten(arr)
      flyVisual({
         positionsData: newArr.map(i => [i.lng, i.lat, i.alt]),
         viewer,
         pitch: -60,
      })
   })
}
const lastSelectedId = ref(null)
// 鼠标左键点击事件
const LeftClickEvent = (click, pick, viewer) => {
   // pick是一个数组
   if (pick && pick.length > 0) {
      let startEntity = pick.find(
         i => i.id.name === 'work-drone-route-planar-polyline' || i.id.name === 'work-drone-route-point-polyline'
      )?.id
      if (startEntity) {
         createRouteLineExample.showSingleDetailMapPopup(startEntity?.customData?.data)
         return
      }
      let imagePath = []
      let categories = ''
      let panoramic = ''
      pick.forEach(item => {
         const index = item.id.index
         if (item.id.customType == 'spotImages') {
            imagePath.push(item.id.customInfo.link)
         } else if (item.id.customType == 'aggregationSplashed') {
            imagePath.push(item.id.customInfo.link)
         } else if (item.id.customType == 'panorama') {
            //全景
            imagePath.push(item.id.customInfo.link)
            categories = item.id.customInfo.resultType
         }
      })
      if (imagePath.length === 0) return
      const finalImagePaths = categories === 5 ? imagePath : imagePath.map(i => getShowImg(i))
      emit('showImageeclick', finalImagePaths, 0, categories)
   }
}
const removeMap = () => {
   viewer.entities.removeAll()
   viewer.destroy()
   publicCesiumInstance?.viewerDestroy()
   publicCesiumInstance = null
   viewer = null
}
watch(
@@ -45,12 +127,187 @@
      }
   }
)
const zsList = ref([])
// 获取odmtoken
function getOdmTokenStr() {
   getOdmToken().then(res => {
      if (res.data.code !== 200) return
      tokenStr.value = res.data.data
   })
}
// 获取正射列表
function getZSList(newVal) {
   let list = []
   newVal.map((item, index) => {
      item.childrens.map(zschilditem => {
         if (zschilditem?.data) {
            zschilditem?.data.map(dataitem => {
               list.push(dataitem)
            })
         }
      })
   })
   return list
}
// 获取正射请求地图聚合数据
const zsMapList = ref([])
function getZSMapList(valSn) {
   const params = {
      areaCode: userAreaCode.value,
      deviceSn: valSn,
      resultType: 4,
      wayLineJobId: props.jobId,
   }
   getPanoramaList(params).then(res => {
      const resData = res?.data?.data
      zsMapList.value = resData?.childrens || []
      zsList.value = getZSList(zsMapList.value)
      renderOrthophoto()
   })
}
// 解析经纬度
function getLonLat(str) {
   if (!str) return []
   return str
      .replace('POLYGON((', '')
      .replace('))', '')
      .split(',')
      .map(pair => {
         const [lon, lat] = pair.trim().split(' ').map(Number)
         return [lon, lat]
      })
}
// 渲染正射
function renderOrthophoto() {
   zsList.value.forEach((item, index) => {
      const cur = viewInstance.value.addCustomImageryProviderDataSource(
         new Cesium.UrlTemplateImageryProvider({
            url: `${import.meta.env.VITE_APP_AREA_NAME}/webodm${item.url}?jwt=${tokenStr.value.split(' ')[1]}`,
         })
      )
   })
}
watch(
   () => props.yuanImages,
   (newValue, oldValue) => {
      // 增加箭头
        if (!viewer || !newValue?.length) return;
         // 清除旧实体
    const oldEntities = viewer.entities.values.filter(e =>
      e.id?.startsWith('spotImages') ||
      e.id?.startsWith('aggregationSplashed') ||
      e.id?.startsWith('panorama')
    );
    oldEntities.forEach(e => viewer.entities.remove(e));
   // 添加新实体
      let eventArr = newValue.filter(i => i.resultType === 2)
      let originalArr = newValue.filter(i => i.resultType === 0)
      let panoramicArr = newValue.filter(i => i.resultType === 5)
      let orthographicArr = newValue.filter(i => i.resultType === 4)
      if (orthographicArr.length >0) {
         getZSMapList(orthographicArr[0]?.deviceSn)
      }
      originalArr.forEach((item_in, index) => {
         // 根据偏航角度展示图片拍摄的方向
         let yawAngleDegrees =
            item_in.metadata.gimbalYawDegree < 0
               ? 360 + Number(item_in.metadata.gimbalYawDegree)
               : Number(item_in.metadata.gimbalYawDegree)
         viewer.entities.add({
            position: Cesium.Cartesian3.fromDegrees(
               +item_in.metadata.shootPosition.lng,
               +item_in.metadata.shootPosition.lat,
               +item_in.metadata.absoluteAltitude
            ),
            id: 'spotImages' + item_in.id,
            customType: 'spotImages',
            customInfo: {
               ...item_in,
            },
            index: index,
            billboard: {
               image: photoDirection,
               outlineWidth: 0,
               width: 14,
               height: 21,
               disableDepthTestDistance: Number.POSITIVE_INFINITY,
               verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
            },
         })
      })
      eventArr.forEach((item, index) => {
         viewer.entities.add({
            id: `aggregationSplashed${item.id}`,
            customType: 'aggregationSplashed',
            customInfo: {
               ...item,
            },
            position: Cesium.Cartesian3.fromDegrees(
               +item.metadata.shootPosition.lng,
               +item.metadata.shootPosition.lat,
               +item.metadata.absoluteAltitude
            ),
            billboard: {
               image: getEventImage(item.status),
               width: 30,
               height: 30,
               disableDepthTestDistance: Number.POSITIVE_INFINITY,
            },
            index: index,
            properties: {
               customData: {
                  data: item,
               },
            },
         })
      })
      // 全景
      panoramicArr.forEach((item, index) => {
         viewer.entities.add({
            id: `panorama${item.id}`,
            customType: 'panorama',
            customInfo: {
               ...item,
            },
            position: Cesium.Cartesian3.fromDegrees(
               +item.metadata.shootPosition.lng,
               +item.metadata.shootPosition.lat,
               +item.metadata.absoluteAltitude
            ),
            billboard: {
               image: panoramaPoint,
               width: 30,
               height: 30,
               disableDepthTestDistance: Number.POSITIVE_INFINITY,
            },
            index: index,
            properties: {
               customData: {
                  data: item,
               },
            },
         })
      })
   },
   { immediate: true, deep: true }
)
onBeforeUnmount(() => {
   // 移除事件监听
   handler?.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK)
   handler?.destroy()
   handler = null
   createRouteLineExample.destroyedCustomDom()
   removeMap()
})
onMounted(() => {
   getOdmTokenStr()
   nextTick(() => {
      initMap()
   })