forked from drone/command-center-dashboard

chenyao
2025-04-17 2ddedb48ebcb4952e57aefe2fa0b2ba8094ea4c0
src/hooks/useMapAggregation/useMapAggregation.js
@@ -6,7 +6,9 @@
import DevicePopUpBox from '@/hooks/components/DevicePopUpBox.vue'
import EventPopUpBox from '@/hooks/components/EventPopUpBox.vue'
// 图标
import offlineImg from '@/assets/images/home/useEventOperate/offline.png'
import onlineImg from '@/assets/images/home/useEventOperate/eventSingle.png'
import { render } from 'vue'
import { useStore } from 'vuex'
import { getCenterPoint } from '@/utils/cesium/mapUtil'
@@ -18,548 +20,577 @@
 * 机巢聚合功能
 */
let arrColor = ["rgb(15,176,255)", "rgb(18,76,154)", "#40C4E4", "#42B2BE", "rgb(51,176,204)", "#8CB7E5", "rgb(0,244,188)", "#139FF0"]
let arrColor = [
   'rgb(15,176,255)',
   'rgb(18,76,154)',
   '#40C4E4',
   '#42B2BE',
   'rgb(51,176,204)',
   '#8CB7E5',
   'rgb(0,244,188)',
   '#139FF0',
]
let index = 0
function getColor () {
  return arrColor[++index % arrColor.length]
function getColor() {
   return arrColor[++index % arrColor.length]
}
export const useMapAggregation = type => {
  const { flyTo } = cesiumOperation()
export const useMapAggregation = (type, status) => {
   const { flyTo } = cesiumOperation()
  const singleImg = type === 'device' ? uavImg : eventSingle
  const mergeImg = type === 'device' ? aggregationImg : eventAggregationImg
  const MapPopUpBox = type === 'device' ? DevicePopUpBox : EventPopUpBox
  const styleTransform = type === 'device' ? 'translateY(-50%)' : 'translate(-50%,-110%)'
   const singleImg = type === 'device' ? onlineImg : eventSingle
  const offlinesingleImg = type === 'device' ? offlineImg : eventSingle
   const mergeImg = type === 'device' ? aggregationImg : eventAggregationImg
   const MapPopUpBox = type === 'device' ? DevicePopUpBox : EventPopUpBox
   const styleTransform = type === 'device' ? 'translateY(-50%)' : 'translate(-50%,-110%)'
  let scalingJudgment = [
    { name: '县', splashedList: [], gJson: null, show: false, outline: {}, value: [0, 48651], height: 31753 },
    { name: '市', splashedList: [], gJson: null, show: false, outline: {}, value: [48651, 314863], height: 257731 },
    {
      name: '省',
      splashedList: [],
      gJson: null,
      show: false,
      outline: {},
      value: [314863, 3796280000],
      height: 1987280,
    },
  ]
  let viewer = null
  let active = null
  let handler = null
  let currentEntity = null
   let scalingJudgment = [
      { name: '县', splashedList: [], gJson: null, show: false, outline: {}, value: [0, 48651], height: 31753 },
      { name: '市', splashedList: [], gJson: null, show: false, outline: {}, value: [48651, 314863], height: 257731 },
      {
         name: '省',
         splashedList: [],
         gJson: null,
         show: false,
         outline: {},
         value: [314863, 3796280000],
         height: 1987280,
      },
   ]
   let viewer = null
   let active = null
   let handler = null
   let currentEntity = null
  const store = useStore()
  const userAreaCode = computed(() => store.state.user.userInfo.detail.areaCode)
  const selectedAreaCode = computed(() => store.state.user.selectedAreaCode)
   const store = useStore()
   const userAreaCode = computed(() => store.state.user.userInfo.detail.areaCode)
   const selectedAreaCode = computed(() => store.state.user.selectedAreaCode)
   const eventTimeType = computed(() => store.state.home.eventTimeType)
   const eventTimeRang = computed(() => store.state.home.eventTimeRang)
  const eventTimeType = computed(() => store.state.home.eventTimeType)
  const eventTimeRang = computed(() => store.state.home.eventTimeRang)
   const singleUavHome = computed(() => store.state.home.singleUavHome)
  const singleUavHome = computed(() => store.state.home.singleUavHome)
   let needFly = true
  let needFly = true
   const combinedValues = computed(() => ({
      selectedAreaCode: selectedAreaCode.value,
      eventTimeType: eventTimeType.value,
      eventTimeRang: eventTimeRang.value,
      singleUavHome: singleUavHome.value,
   }))
  const combinedValues = computed(() => ({
    selectedAreaCode: selectedAreaCode.value,
    eventTimeType: eventTimeType.value,
    eventTimeRang: eventTimeRang.value,
    singleUavHome: singleUavHome.value,
  }))
   let saveParams = { area_code: '', date_enum: 'CURRENT_WEEK' }
  let saveParams = { area_code: '', date_enum: 'CURRENT_WEEK' }
   // 确定缩放比例
   const determineScaling = () => {
      // console.log('确定缩放比例');
      if (!viewer) return
      let height = viewer.camera.positionCartographic.height
      // 根据高度展示对应的 gJson
      for (let [index, item] of scalingJudgment.entries()) {
         if (!item.show) return
         if (height > item.value[0] && height <= item.value[1]) {
            if (active === item.name) return
            active = item.name
            removeEntities()
            removeLabel()
            renderOutline(item)
            if (!item.gJson && !item.splashedList?.length) return
            item.gJson ? aggregation(item) : splashed(item)
            break
         }
      }
   }
   // 转换为目录结构, 返回数组,
   function convertToHierarchy(code) {
      const codeStr = code.toString()
      const provinceCode = codeStr.slice(0, 2) + '0000'
      const cityCode = codeStr.slice(0, 4) + '00'
      if (codeStr.slice(2, 4) === '00' && codeStr.slice(4, 6) === '00') {
         return [provinceCode]
      } else if (codeStr.slice(4, 6) === '00') {
         return [provinceCode, cityCode]
      }
      return [provinceCode, cityCode, code]
   }
  // 确定缩放比例
  const determineScaling = () => {
    // console.log('确定缩放比例');
    if (!viewer) return
    let height = viewer.camera.positionCartographic.height
    // 根据高度展示对应的 gJson
    for (let [index, item] of scalingJudgment.entries()) {
      if (!item.show) return
      if (height > item.value[0] && height <= item.value[1]) {
        if (active === item.name) return
        active = item.name
        removeEntities()
        removeLabel()
        renderOutline(item)
        if (!item.gJson && !item.splashedList?.length) return
        item.gJson ? aggregation(item) : splashed(item)
        break
      }
    }
  }
   // 获取设备聚合
   function getDeviceCount(areaCode) {
      return getDeviceRegionCount({ areaCode }).then(res => {
         return res?.data?.data || []
      })
   }
   // 获取设备散点
   function getDeviceList(areaCode) {
      return getDeviceRegion({ areaCode }).then(res => {
         return (res?.data?.data || []).map(i => ({ ...i, type }))
  // 转换为目录结构, 返回数组,
  function convertToHierarchy (code) {
    const codeStr = code.toString()
    const provinceCode = codeStr.slice(0, 2) + '0000'
    const cityCode = codeStr.slice(0, 4) + '00'
    if (codeStr.slice(2, 4) === '00' && codeStr.slice(4, 6) === '00') {
      return [provinceCode]
    } else if (codeStr.slice(4, 6) === '00') {
      return [provinceCode, cityCode]
    }
    return [provinceCode, cityCode, code]
  }
      })
   }
  // 获取设备聚合
  function getDeviceCount (areaCode) {
    return getDeviceRegionCount({ areaCode }).then(res => {
      return (res?.data?.data || [])
    })
  }
  // 获取设备散点
  function getDeviceList (areaCode) {
    return getDeviceRegion({ areaCode }).then(res => {
      return (res?.data?.data || []).map(i => ({ ...i, type }))
    })
  }
   // 事件散点
   let eventList = []
   function processChildren(childrens) {
      // console.log(childrens, '事件点')
      return (childrens || []).map(item => {
         const arr = processChildren(item.childrens)
         if (item.data) {
            eventList = eventList.concat(item.data)
         }
         const returnObj = {
            total_device_count: item.number,
            region_code: item.id,
            region_name: item.name,
         }
         if (arr.length !== 0) {
            returnObj.childrens = arr
         }
         return returnObj
      })
   }
  // 事件散点
  let eventList = []
  function processChildren (childrens) {
    // console.log(childrens, '事件点')
    return (childrens || []).map(item => {
      const arr = processChildren(item.childrens)
      if (item.data) {
        eventList = eventList.concat(item.data)
      }
      const returnObj = {
        total_device_count: item.number,
        region_code: item.id,
        region_name: item.name,
      }
      if (arr.length !== 0) {
        returnObj.childrens = arr
      }
      return returnObj
    })
  }
   // 获取事件聚合
   function getMapEventCount(params) {
      return getMapEvents(params).then(res => {
         const resData = res?.data?.data
         if (resData?.data) {
            eventList = resData?.data
            return []
         }
         return processChildren(resData?.childrens)
      })
   }
  // 获取事件聚合
  function getMapEventCount (params) {
    return getMapEvents(params).then(res => {
      const resData = res?.data?.data
      if (resData?.data) {
        eventList = resData?.data
        return []
      }
      return processChildren(resData?.childrens)
    })
  }
   const findFun = (featItem, numItem) => Number(featItem.region_code.slice(0, 6)) === numItem.properties.adcode
   const { VITE_APP_BASE, VITE_APP_ENV } = import.meta.env
   const defaultDir = `${VITE_APP_BASE}${VITE_APP_ENV === 'development' ? 'public/' : ''}geoJson/100000/`
  const findFun = (featItem, numItem) => Number(featItem.region_code.slice(0, 6)) === numItem.properties.adcode
  const { VITE_APP_BASE, VITE_APP_ENV } = import.meta.env
  const defaultDir = `${VITE_APP_BASE}${VITE_APP_ENV === 'development' ? 'public/' : ''}geoJson/100000/`
   const getFiler = async url => {
      const res = await fetch(url)
      return await res.json()
   }
   const getOutLine = async (jsonPathPre, hierarchy) => {
      const parentGJson = await getFiler(`${defaultDir}${jsonPathPre}/index.json`)
      let features = parentGJson.features.find(item => item.properties.adcode === Number(hierarchy[hierarchy.length - 1]))
      return { type: 'FeatureCollection', features: [features] }
   }
  const getFiler = async url => {
    const res = await fetch(url)
    return await res.json()
  }
  const getOutLine = async (jsonPathPre, hierarchy) => {
    const parentGJson = await getFiler(`${defaultDir}${jsonPathPre}/index.json`)
    let features = parentGJson.features.find(
      item => item.properties.adcode === Number(hierarchy[hierarchy.length - 1])
    )
    return { type: 'FeatureCollection', features: [features] }
  }
   const injectData = (gJson, dataList) => {
      return {
         ...gJson,
         features: gJson.features.map(item => {
            const findData = dataList.find(item1 => findFun(item1, item))
            return { ...item, data: { ...findData, type: type + 'Aggregation' } }
         }),
      }
   }
  const injectData = (gJson, dataList) => {
    return {
      ...gJson,
      features: gJson.features.map(item => {
        const findData = dataList.find(item1 => findFun(item1, item))
        return { ...item, data: { ...findData, type: type + 'Aggregation' } }
      }),
    }
  }
   const initMapData = async areaCode => {
      eventList = []
      if (!areaCode) return
      saveParams.area_code = areaCode
      const list = type === 'device' ? await getDeviceCount(areaCode) : await getMapEventCount(saveParams)
      const splashedList =
         type === 'device'
            ? await getDeviceList(areaCode)
            : eventList.map(i => ({
                  eventId: i.id,
                  latitude: Number(i.latitude),
                  longitude: Number(i.longitude),
                  type: 'event',
              }))
      const hierarchy = convertToHierarchy(areaCode.slice(0, 6))
      const jsonPath = hierarchy.join('/')
      const jsonPathPre = hierarchy.slice(0, hierarchy.length - 1).join('/')
      scalingJudgment = scalingJudgment.map(item => ({ ...item, show: true }))
      scalingJudgment[0].gJson = null
      scalingJudgment[0].splashedList = splashedList
      active = null
      // 省
      if (hierarchy.length === 1) {
         const gJson1 = await getFiler(`${defaultDir}${jsonPath}/indexDistrict.json`)
         const gJson2 = await getFiler(`${defaultDir}${jsonPath}/index.json`)
         scalingJudgment[1].gJson = {
            ...gJson1,
            features: gJson1.features.map(item => {
               const findData = list.flatMap(item => item.childrens || []).find(item1 => findFun(item1, item))
               return { ...item, data: { ...findData, type: type + 'Aggregation' } }
            }),
         }
         scalingJudgment[2].gJson = injectData(gJson2, list)
      }
      // 市
      if (hierarchy.length === 2) {
         scalingJudgment[2].gJson = null
         scalingJudgment[2].show = false
         const gJson1 = await getFiler(`${defaultDir}${jsonPath}/index.json`)
         scalingJudgment[1].gJson = injectData(gJson1, list)
      }
      // 区县
      if (hierarchy.length === 3) {
         scalingJudgment[1].gJson = null
         scalingJudgment[1].show = false
         scalingJudgment[2].gJson = null
         scalingJudgment[2].show = false
      }
      // 轮廓
      const outlineGJson = await getOutLine(jsonPathPre, hierarchy)
      scalingJudgment.forEach(item => item.show && (item.outline = outlineGJson))
      const [longitude, latitude] = outlineGJson.features[0].properties.centroid
      const height = scalingJudgment[(hierarchy.length - 3) * -1].height
      setCenterPosition({ longitude, latitude, height })
      flyTo({ longitude, latitude }, 0, height)
   }
  const initMapData = async areaCode => {
    eventList = []
    if (!areaCode) return
    saveParams.area_code = areaCode
    const list = type === 'device' ? await getDeviceCount(areaCode) : await getMapEventCount(saveParams)
    const splashedList = type === 'device'
      ? await getDeviceList(areaCode)
      : eventList.map(i => ({ eventId: i.id, latitude: Number(i.latitude), longitude: Number(i.longitude), type: 'event' }))
    const hierarchy = convertToHierarchy(areaCode.slice(0, 6))
    const jsonPath = hierarchy.join('/')
    const jsonPathPre = hierarchy.slice(0, hierarchy.length - 1).join('/')
    scalingJudgment = scalingJudgment.map(item => ({ ...item, show: true }))
    scalingJudgment[0].gJson = null
    scalingJudgment[0].splashedList = splashedList
    active = null
    // 省
    if (hierarchy.length === 1) {
      const gJson1 = await getFiler(`${defaultDir}${jsonPath}/indexDistrict.json`)
      const gJson2 = await getFiler(`${defaultDir}${jsonPath}/index.json`)
      scalingJudgment[1].gJson = {
        ...gJson1,
        features: gJson1.features.map(item => {
          const findData = list.flatMap(item => item.childrens || []).find(item1 => findFun(item1, item))
          return { ...item, data: { ...findData, type: type + 'Aggregation' } }
        }),
      }
      scalingJudgment[2].gJson = injectData(gJson2, list)
    }
    // 市
    if (hierarchy.length === 2) {
      scalingJudgment[2].gJson = null
      scalingJudgment[2].show = false
      const gJson1 = await getFiler(`${defaultDir}${jsonPath}/index.json`)
      scalingJudgment[1].gJson = injectData(gJson1, list)
    }
    // 区县
    if (hierarchy.length === 3) {
      scalingJudgment[1].gJson = null
      scalingJudgment[1].show = false
      scalingJudgment[2].gJson = null
      scalingJudgment[2].show = false
    }
    // 轮廓
    const outlineGJson = await getOutLine(jsonPathPre, hierarchy)
    scalingJudgment.forEach(item => item.show && (item.outline = outlineGJson))
    const [longitude, latitude] = outlineGJson.features[0].properties.centroid
    const height = scalingJudgment[(hierarchy.length - 3) * (-1)].height
    setCenterPosition({ longitude, latitude, height })
    flyTo({ longitude, latitude }, 0, height)
  }
   const userAreaPosition = computed(() => store.state.home.userAreaPosition)
  const userAreaPosition = computed(() => store.state.home.userAreaPosition)
   const setCenterPosition = position => {
      store.commit('setCurrentAreaPosition', position)
      if (!userAreaPosition.value.longitude) {
         store.commit('setUserAreaPosition', position)
      }
   }
  const setCenterPosition = (position) => {
    store.commit('setCurrentAreaPosition', position)
    if (!userAreaPosition.value.longitude) {
      store.commit('setUserAreaPosition', position)
    }
  }
   watch(
      combinedValues,
      async (newValue, oldValue) => {
         if (newValue.singleUavHome?.device_sn) {
            clearMapEntity()
            return
         }
  watch(combinedValues, async (newValue, oldValue) => {
    if (newValue.singleUavHome?.device_sn) {
      clearMapEntity()
      return
    }
         if (newValue.eventTimeType) {
            saveParams = { area_code: newValue.selectedAreaCode, date_enum: store.state.home.eventTimeParams }
         }
    if (newValue.eventTimeType) {
      saveParams = { area_code: newValue.selectedAreaCode, date_enum: store.state.home.eventTimeParams }
    }
         if (newValue.eventTimeRang) {
            saveParams = {
               area_code: newValue.selectedAreaCode,
               start_date: newValue.eventTimeRang[0],
               end_date: newValue.eventTimeRang[1],
            }
         }
    if (newValue.eventTimeRang) {
      saveParams = { area_code: newValue.selectedAreaCode, start_date: newValue.eventTimeRang[0], end_date: newValue.eventTimeRang[1] }
    }
         needFly = true
         if (!viewer) return
         handlerInit()
    needFly = true
    if (!viewer) return
    handlerInit()
         viewer.scene.postRender.removeEventListener(determineScaling)
    viewer.scene.postRender.removeEventListener(determineScaling)
         initMapData(newValue.selectedAreaCode).then(() => {
            viewer.scene.postRender.addEventListener(determineScaling)
         })
      },
      { deep: true }
   )
    initMapData(newValue.selectedAreaCode).then(() => {
      viewer.scene.postRender.addEventListener(determineScaling)
    })
  },
    { deep: true }
  )
   //散点机巢
   function splashed(row) {
      row.splashedList.forEach((item, index) => {
  //散点机巢
  function splashed (row) {
    row.splashedList.forEach((item, index) => {
      viewer.entities.add({
        id: `aggregation-splashed-${index}`,
        position: Cesium.Cartesian3.fromDegrees(item.longitude, item.latitude),
        label: {
          text: item.nickname,
          font: '12pt Source Han Sans CN',
          fillColor: Cesium.Color.WHITE,
          outlineColor: Cesium.Color.BLACK,
          outlineWidth: 2,
          style: Cesium.LabelStyle.FILL_AND_OUTLINE,
          verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
          pixelOffset: new Cesium.Cartesian2(0, -9),
        },
        billboard: {
          image: new Cesium.ConstantProperty(singleImg),
          width: 24,
          height: 24,
        },
        properties: {
          customData: {
            data: item,
          },
        },
      })
    })
  }
         viewer.entities.add({
            id: `aggregation-splashed-${index}`,
            position: Cesium.Cartesian3.fromDegrees(item.longitude, item.latitude),
            label: {
               text: item.nickname,
               font: '12pt Source Han Sans CN',
               fillColor: Cesium.Color.WHITE,
               outlineColor: Cesium.Color.BLACK,
               outlineWidth: 2,
               style: Cesium.LabelStyle.FILL_AND_OUTLINE,
               verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
               pixelOffset: new Cesium.Cartesian2(0, -9),
            },
            billboard: {
          // singleImg
               image: new Cesium.ConstantProperty(()=>{
            return item.status ==="OFFLINE" ? offlinesingleImg:singleImg
          }),
               width: 24,
               height: 24,
            },
            properties: {
               customData: {
                  data: item,
               },
            },
         })
      })
   }
  // 渲染轮廓
  const renderOutline = item => {
    item.outline &&
      Cesium.GeoJsonDataSource.load(item.outline).then(dataSource => {
        const entities = dataSource.entities.values
        entities.forEach((entity, index) => {
          // 创建独立折线作为轮廓
          const positions = entity.polygon.hierarchy.getValue().positions
          const randomInt = Math.floor(Math.random() * 5) + 1
   // 渲染轮廓
   const renderOutline = item => {
      item.outline &&
         Cesium.GeoJsonDataSource.load(item.outline).then(dataSource => {
            const entities = dataSource.entities.values
            entities.forEach((entity, index) => {
               // 创建独立折线作为轮廓
               const positions = entity.polygon.hierarchy.getValue().positions
               const randomInt = Math.floor(Math.random() * 5) + 1
          let polygon = {}
               let polygon = {}
          if (item.name === '县') {
            let material = new PolyGradientMaterial({
              color: Cesium.Color.fromCssColorString(arrColor[randomInt]),
              opacity: 0.7,
              alphaPower: 1.3
            })
               if (item.name === '县') {
                  let material = new PolyGradientMaterial({
                     color: Cesium.Color.fromCssColorString(arrColor[randomInt]),
                     opacity: 0.7,
                     alphaPower: 1.3,
                  })
            entity.polygon.extrudedHeight = (entity.properties.childrenNum._value || 1) * 500
                  entity.polygon.extrudedHeight = (entity.properties.childrenNum._value || 1) * 500
            entity.polygon.material = material
                  entity.polygon.material = material
            polygon = entity.polygon
            entity.polygon.outline = false // 显示边框
          }
                  polygon = entity.polygon
                  entity.polygon.outline = false // 显示边框
               }
          viewer.entities.add({
            id: `aggregation-outline-${index}`,
            polyline: {
              positions: positions,
              width: 5, // 直接设置宽度
              material: new Cesium.PolylineGlowMaterialProperty({
                glowPower: 0.5,
                color: Cesium.Color.AQUA,
              }),
            },
               viewer.entities.add({
                  id: `aggregation-outline-${index}`,
                  polyline: {
                     positions: positions,
                     width: 5, // 直接设置宽度
                     material: new Cesium.PolylineGlowMaterialProperty({
                        glowPower: 0.5,
                        color: Cesium.Color.AQUA,
                     }),
                  },
            polygon
          })
        })
      })
  }
                  polygon,
               })
            })
         })
   }
  // 聚合机巢
  const aggregation = (item) => {
    if (!item.gJson) return
    const featuresList = item.gJson.features.map(item1 => {
      // const {lng,lat} = getCenterPoint(item1.geometry.coordinates[0][0])
      return {
        name: item1.properties.name,
        position: item1.properties.centroid,
        data: item1.data,
        id: item1.region_code,
      }
    })
    // 遍历特征并添加实体
    featuresList.forEach((feature, index) => {
      if (!feature.position) return
      const position = Cesium.Cartesian3.fromDegrees(feature.position[0], feature.position[1], 0)
      viewer.entities.add({
        position: position,
        id: `aggregation-name-${index}`,
        label: {
          text: feature.name,
          font: '14pt Source Han Sans CN',
          fillColor: Cesium.Color.WHITE,
          outlineColor: Cesium.Color.BLACK,
          outlineWidth: 2,
          style: Cesium.LabelStyle.FILL_AND_OUTLINE,
          verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
          pixelOffset: new Cesium.Cartesian2(0, -9),
        },
      })
      viewer.entities.add({
        id: `aggregation-count-${index}`,
        position: position,
        label: {
          text: (feature.data.total_device_count || 0).toString(),
          font: '12pt Source Han Sans CN',
          fillColor: Cesium.Color.BLACK,
          outlineColor: Cesium.Color.BLACK,
          style: Cesium.LabelStyle.FILL_AND_OUTLINE,
          eyeOffset: new Cesium.Cartesian3(0, 0, -10), // 让label "浮" 在广告牌前面
        },
        billboard: {
          image: new Cesium.ConstantProperty(mergeImg),
          width: 35,
          height: 35,
        },
        properties: {
          id: feature.id,
          customData: {
            data: feature.data,
          },
        },
      })
    })
   // 聚合机巢
   const aggregation = item => {
      if (!item.gJson) return
      const featuresList = item.gJson.features.map(item1 => {
         // const {lng,lat} = getCenterPoint(item1.geometry.coordinates[0][0])
         return {
            name: item1.properties.name,
            position: item1.properties.centroid,
            data: item1.data,
            id: item1.region_code,
         }
      })
      // 遍历特征并添加实体
      featuresList.forEach((feature, index) => {
         if (!feature.position) return
         const position = Cesium.Cartesian3.fromDegrees(feature.position[0], feature.position[1], 0)
         viewer.entities.add({
            position: position,
            id: `aggregation-name-${index}`,
            label: {
               text: feature.name,
               font: '14pt Source Han Sans CN',
               fillColor: Cesium.Color.WHITE,
               outlineColor: Cesium.Color.BLACK,
               outlineWidth: 2,
               style: Cesium.LabelStyle.FILL_AND_OUTLINE,
               verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
               pixelOffset: new Cesium.Cartesian2(0, -9),
            },
         })
         viewer.entities.add({
            id: `aggregation-count-${index}`,
            position: position,
            label: {
               text: (feature.data.total_device_count || 0).toString(),
               font: '12pt Source Han Sans CN',
               fillColor: Cesium.Color.BLACK,
               outlineColor: Cesium.Color.BLACK,
               style: Cesium.LabelStyle.FILL_AND_OUTLINE,
               eyeOffset: new Cesium.Cartesian3(0, 0, -10), // 让label "浮" 在广告牌前面
            },
            billboard: {
               image: new Cesium.ConstantProperty(mergeImg),
               width: 35,
               height: 35,
            },
            properties: {
               id: feature.id,
               customData: {
                  data: feature.data,
               },
            },
         })
      })
    // 加载边界
    Cesium.GeoJsonDataSource.load(item.gJson).then(dataSource => {
      viewer.dataSources.add(dataSource)
      item.BJDataSource = dataSource // 保存数据源以便后续删除
      // 获取数据源中的实体
      const entities = dataSource.entities.values
      // 加载边界
      Cesium.GeoJsonDataSource.load(item.gJson).then(dataSource => {
         viewer.dataSources.add(dataSource)
         item.BJDataSource = dataSource // 保存数据源以便后续删除
         // 获取数据源中的实体
         const entities = dataSource.entities.values
      entities.forEach(entity => {
        let material = new PolyGradientMaterial({
          color: Cesium.Color.fromCssColorString(getColor()),
          opacity: 0.7,
          alphaPower: 1.3
        })
         entities.forEach(entity => {
            let material = new PolyGradientMaterial({
               color: Cesium.Color.fromCssColorString(getColor()),
               opacity: 0.7,
               alphaPower: 1.3,
            })
        const randomInt = Math.floor(Math.random() * 8) + 1
            const randomInt = Math.floor(Math.random() * 8) + 1
        entity.polygon.extrudedHeight = (entity.properties.childrenNum._value || randomInt) * 500
            entity.polygon.extrudedHeight = (entity.properties.childrenNum._value || randomInt) * 500
        entity.polygon.material = material
        entity.polygon.outline = false // 显示边框
      })
            entity.polygon.material = material
            entity.polygon.outline = false // 显示边框
         })
      needFly && viewer.flyTo(dataSource, {
        offset: new Cesium.HeadingPitchRange(0, Cesium.Math.toRadians(-60), 0),
        duration: 0.5,
      })
      needFly = false
    })
  }
         needFly &&
            viewer.flyTo(dataSource, {
               offset: new Cesium.HeadingPitchRange(0, Cesium.Math.toRadians(-60), 0),
               duration: 0.5,
            })
         needFly = false
      })
   }
  // 获取弹框box
  const getLabelDom = data => {
    const vNode = h(MapPopUpBox, { data, removeLabel })
    const tooltipContainer = document.createElement('div')
    tooltipContainer.id = 'mapPopUpBox'
    tooltipContainer.style.position = 'absolute'
    tooltipContainer.style.transform = styleTransform
    tooltipContainer.style.pointerEvents = 'none'
    document.querySelector('.page-index').append(tooltipContainer)
    render(vNode, tooltipContainer)
    return tooltipContainer
  }
   // 获取弹框box
   const getLabelDom = data => {
      const vNode = h(MapPopUpBox, { data, removeLabel })
      const tooltipContainer = document.createElement('div')
      tooltipContainer.id = 'mapPopUpBox'
      tooltipContainer.style.position = 'absolute'
      tooltipContainer.style.transform = styleTransform
      tooltipContainer.style.pointerEvents = 'none'
      document.querySelector('.page-index').append(tooltipContainer)
      render(vNode, tooltipContainer)
      return tooltipContainer
   }
  // 弹框位置刷新
  const labelBoxRender = () => {
    if (!currentEntity) return
    let dom = document.querySelector('#mapPopUpBox')
    if (!dom) {
      dom = getLabelDom(currentEntity.properties.customData._value.data)
    }
    const screenPosition = viewer.scene.cartesianToCanvasCoordinates(currentEntity?.position?._value)
    if (screenPosition) {
      dom.style.left = `${screenPosition.x}px`
      dom.style.top = `${screenPosition.y}px`
      dom.style.display = 'block'
    }
  }
   // 弹框位置刷新
   const labelBoxRender = () => {
      if (!currentEntity) return
      let dom = document.querySelector('#mapPopUpBox')
      if (!dom) {
         dom = getLabelDom(currentEntity.properties.customData._value.data)
      }
      const screenPosition = viewer.scene.cartesianToCanvasCoordinates(currentEntity?.position?._value)
      if (screenPosition) {
         dom.style.left = `${screenPosition.x}px`
         dom.style.top = `${screenPosition.y}px`
         dom.style.display = 'block'
      }
   }
  /**
   * 根据条件获取项
   * @param {Array} arr 数据源
   * @param {Function} condition 条件
   * @returns
   */
  const findTypeItem = (arr, condition) => {
    return arr.find(item => condition(item))
  }
   /**
    * 根据条件获取项
    * @param {Array} arr 数据源
    * @param {Function} condition 条件
    * @returns
    */
   const findTypeItem = (arr, condition) => {
      return arr.find(item => condition(item))
   }
  // 左键单机事件
  const singleMachineEvent = async click => {
    let clickTargets = viewer.scene.drillPick(click.position).map(item => item.id)
    if (!clickTargets.length) return
   // 左键单机事件
   const singleMachineEvent = async click => {
      let clickTargets = viewer.scene.drillPick(click.position).map(item => item.id)
      if (!clickTargets.length) return
    console.log(clickTargets, 11111)
      // console.log(clickTargets, 11111)
    let deviceAggregationFind = findTypeItem(clickTargets, (item) => item?.properties?.customData?._value?.data?.type === 'deviceAggregation')
    let deviceFind = findTypeItem(clickTargets, (item) => item?.properties?.customData?._value?.data?.type === 'device')
    // "event"
    let eventFind = findTypeItem(clickTargets, (item) => item?.properties?.customData?._value?.data?.type === 'event')
    // let eventFind = findTypeItem(clickTargets, (item) => item?.properties?.customData?._value?.data?.type === 'eventAggregation')
    currentEntity = deviceAggregationFind || deviceFind || eventFind
      let deviceAggregationFind = findTypeItem(
         clickTargets,
         item => item?.properties?.customData?._value?.data?.type === 'deviceAggregation'
      )
      let deviceFind = findTypeItem(clickTargets, item => item?.properties?.customData?._value?.data?.type === 'device')
      // "event"
      let eventFind = findTypeItem(clickTargets, item => item?.properties?.customData?._value?.data?.type === 'event')
      // let eventFind = findTypeItem(clickTargets, (item) => item?.properties?.customData?._value?.data?.type === 'eventAggregation')
      currentEntity = deviceAggregationFind || deviceFind || eventFind
    if (!currentEntity) return
    if (!currentEntity?.position?._value) return
    // 一定要移除
    removeLabel()
    if (deviceAggregationFind || eventFind) {
      viewer.scene.postRender.addEventListener(labelBoxRender)
    }
    if (deviceFind) {
      const device = deviceFind.properties.customData._value.data
      store.commit('setSingleUavHome', device)
    }
  }
      if (!currentEntity) return
      if (!currentEntity?.position?._value) return
      // 一定要移除
      removeLabel()
      if (deviceAggregationFind || eventFind) {
         viewer.scene.postRender.addEventListener(labelBoxRender)
      }
      if (deviceFind) {
         const device = deviceFind.properties.customData._value.data
         store.commit('setSingleUavHome', device)
      }
   }
  const removeDom = () => {
    const dom = document.querySelector('#mapPopUpBox')
    if (dom && dom.parentNode) {
      dom.parentNode.removeChild(dom)
    }
  }
   const removeDom = () => {
      const dom = document.querySelector('#mapPopUpBox')
      if (dom && dom.parentNode) {
         dom.parentNode.removeChild(dom)
      }
   }
  // 移除 点 和 gjson 实体
  const removeEntities = () => {
    // dataSources移除
    scalingJudgment.forEach(item => {
      item.BJDataSource && viewer.dataSources.remove(item.BJDataSource)
      item.BJDataSource = null
    })
    // entities移除
    const entitiesIDs = viewer.entities.values.map(i => i.id)
    entitiesIDs.forEach(item => {
      item.includes('aggregation-') && viewer.entities.removeById(item)
    })
  }
  // 移除弹框标签
  const removeLabel = () => {
    viewer?.scene.postRender.removeEventListener(labelBoxRender)
    removeDom()
  }
   // 移除 点 和 gjson 实体
   const removeEntities = () => {
      // dataSources移除
      scalingJudgment.forEach(item => {
         item.BJDataSource && viewer.dataSources.remove(item.BJDataSource)
         item.BJDataSource = null
      })
      // entities移除
      const entitiesIDs = viewer.entities.values.map(i => i.id)
      entitiesIDs.forEach(item => {
         item.includes('aggregation-') && viewer.entities.removeById(item)
      })
   }
   // 移除弹框标签
   const removeLabel = () => {
      viewer?.scene.postRender.removeEventListener(labelBoxRender)
      removeDom()
   }
  // 移除所有监听事件,变量置空
  const removeAll = () => {
    clearMapEntity()
    viewer = null
  }
   // 移除所有监听事件,变量置空
   const removeAll = () => {
      clearMapEntity()
      viewer = null
   }
  const clearMapEntity = () => {
    if (!viewer) return
    removeEntities()
    removeLabel()
    // viewer.camera.moveEnd.removeEventListener(determineScaling);
    viewer.scene.postRender.removeEventListener(determineScaling)
    handler?.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK)
    handler?.destroy()
    active = null
    handler = null
    currentEntity = null
  }
   const clearMapEntity = () => {
      if (!viewer) return
      removeEntities()
      removeLabel()
      // viewer.camera.moveEnd.removeEventListener(determineScaling);
      viewer.scene.postRender.removeEventListener(determineScaling)
      handler?.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK)
      handler?.destroy()
      active = null
      handler = null
      currentEntity = null
   }
  const init = () => {
    viewer = window.$viewer
    viewer.scene.postRender.removeEventListener(determineScaling)
    initMapData(selectedAreaCode.value || userAreaCode.value).then(() => {
      viewer.scene.postRender.addEventListener(determineScaling)
    })
   const init = () => {
      viewer = window.$viewer
      viewer.scene.postRender.removeEventListener(determineScaling)
      initMapData(selectedAreaCode.value || userAreaCode.value).then(() => {
         viewer.scene.postRender.addEventListener(determineScaling)
      })
    handlerInit()
  }
      handlerInit()
   }
  const handlerInit = () => {
    if (handler) return
   const handlerInit = () => {
      if (handler) return
    handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
    handler.setInputAction(singleMachineEvent, Cesium.ScreenSpaceEventType.LEFT_CLICK)
  }
      handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
      handler.setInputAction(singleMachineEvent, Cesium.ScreenSpaceEventType.LEFT_CLICK)
   }
  onBeforeUnmount(() => { })
  // onMounted(() => {
  //   nextTick(() => {
  //     viewer = window.$viewer;
  //     handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
  //     handler.setInputAction(singleMachineEvent, Cesium.ScreenSpaceEventType.LEFT_CLICK);
  //   });
  // });
   onBeforeUnmount(() => {})
   // onMounted(() => {
   //   nextTick(() => {
   //     viewer = window.$viewer;
   //     handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
   //     handler.setInputAction(singleMachineEvent, Cesium.ScreenSpaceEventType.LEFT_CLICK);
   //   });
   // });
  return { init, removeAll }
   return { init, removeAll }
}