吉安感知网项目-前端
罗广辉
2026-01-27 b6d97f9b6a332825646d0a232a99acd848fca30c
applications/drone-command/src/components/map-container/device-map-container.vue
@@ -11,33 +11,36 @@
               <img :src="layerControlIcon" alt="图层控制" />
            </div>
            <div v-if="showLayerPanel" class="layer-panel">
               <div class="panel-title">图层管理</div>
               <div class="panel-title">底图切换</div>
               <div class="base-map-options">
                  <div class="base-map-card" :class="{ active: baseLayerKey === 'base-standard' }"
                     @click="handleBaseLayerSelect('base-standard')">
                     <img class="base-map-thumb standard" :src="dzIcon" alt="">
                     <div class="base-map-label">标准地图</div>
                  </div>
                  <div class="base-map-card" :class="{ active: baseLayerKey === 'base-satellite' }"
                     @click="handleBaseLayerSelect('base-satellite')">
                     <img class="base-map-thumb satellite" :src="yxIcon" alt="">
                     <div class="base-map-label">卫星地图</div>
                  </div>
               </div>
               <div class="panel-title">图层管理</div>
               <div class="panel-content">
                  <el-tree class="command-tree" :data="layerTree" show-checkbox default-expand-all node-key="key"
                     :props="layerTreeProps" :default-checked-keys="defaultCheckedKeys" />
                  <el-tree ref="layerTreeRef" class="command-tree map-layer-tree" :data="layerTree" show-checkbox
                     default-expand-all node-key="key" :props="layerTreeProps"
                     :default-checked-keys="treeCheckedKeys" @check="handleLayerCheck" />
               </div>
            </div>
         </div>
      </div>
      <DevicePopup
         :visible="popupVisible && !isDronePopup"
         :position="popupPosition"
         :device="selectedDevice"
         @close="closePopup"
      />
      <DevicePopup :visible="popupVisible && !isDronePopup" :position="popupPosition" :device="selectedDevice"
         @close="closePopup" />
      <DronePopup
         :visible="popupVisible && isDronePopup"
         :position="popupPosition"
         :drone="selectedDevice"
         :favorite="Boolean(selectedDevice?.isFavorite)"
         @close="closePopup"
         @toggle-favorite="toggleDroneFavorite"
         @signal="handleDroneSignal"
         @counter="handleDroneCounter"
      />
      <DronePopup :visible="popupVisible && isDronePopup" :position="popupPosition" :drone="selectedDevice"
         :favorite="Boolean(selectedDevice?.isFavorite)" @close="closePopup" @toggle-favorite="toggleDroneFavorite"
         @signal="handleDroneSignal" @counter="handleDroneCounter" />
   </div>
</template>
@@ -64,6 +67,8 @@
   createRadialGradientMaterial,
   getTexturedVertexFormat,
} from './device-map-materials'
import dzIcon from '@/assets/images/dataCockpit/map/dz-map-layer.png'
import yxIcon from '@/assets/images/dataCockpit/map/yx-map-layer.png'
const CLUSTER_HEIGHT = 100000
const DETAIL_HEIGHT = 10000
@@ -92,6 +97,7 @@
const mapRef = ref(null)
let viewer = null
let publicCesium = null
let cockpitPrimitiveLayer = null
const devicePickMap = new Map()
const dronePickMap = new Map()
@@ -114,6 +120,7 @@
const countyCenterMap = new Map()
const showLayerPanel = ref(false)
const layerWrapRef = ref(null)
const layerTreeRef = ref(null)
const selectedDevice = ref(null)
const selectedTargetType = ref('device')
let selectedDeviceBillboard = null
@@ -123,17 +130,24 @@
   label: 'label',
   children: 'children',
}
const defaultCheckedKeys = ['global', 'city-base']
const baseLayerKeys = ['base-standard', 'base-satellite']
const defaultCheckedKeys = ['ja-terrain', 'admin', 'city-base']
const baseLayerKey = ref('base-satellite')
const treeCheckedKeys = ref([...defaultCheckedKeys])
const layerTree = ref([
   {
      key: 'base',
      label: '地理信息图层',
      children: [
         { key: 'global', label: '全球地形' },
         { key: 'ja-terrain', label: '吉安地形' },
         { key: 'admin', label: '行政区划' },
      ],
   },
   {
])
/**
 * {
      key: 'city',
      label: '城市CIM图层',
      children: [
@@ -151,7 +165,9 @@
         { key: 'route', label: '飞行航路' },
      ],
   },
])
 */
const adminBoundaryVisible = ref(treeCheckedKeys.value.includes('admin'))
const getDevicePosition = item => {
   const longitudeRaw = item.longitude ?? item.lng ?? item.lon
@@ -314,7 +330,12 @@
const setClusterVisibility = visible => {
   clusterVisible.value = visible
   if (aggregationSource) aggregationSource.show = visible
   updateAggregationVisibility()
}
const updateAggregationVisibility = () => {
   if (!aggregationSource) return
   aggregationSource.show = clusterVisible.value && adminBoundaryVisible.value
}
const setDroneVisibility = visible => {
@@ -350,33 +371,33 @@
         height: 120
      },
      {
         longitude:114.957308,
         longitude: 114.957308,
         latitude: 27.138452,
         height: 120
      },
      {
         longitude:114.952,
         longitude: 114.952,
         latitude: 27.136317,
         height: 120
      },
      {
         longitude:114.949293,
         longitude: 114.949293,
         latitude: 27.133864,
         height: 120
      },
      {
         longitude:114.944666,
         longitude: 114.944666,
         latitude: 27.130526,
         height: 120
      },
      {
         longitude:114.945909,
         latitude:27.127845,
         longitude: 114.945909,
         latitude: 27.127845,
         height: 120
      },
      {
         longitude:114.962974,
         latitude:27.136242,
         longitude: 114.962974,
         latitude: 27.136242,
         height: 120
      },
   ]
@@ -472,7 +493,7 @@
   droneTrackBillboardCollection.show = detailVisible.value
   droneTrackPolylineCollection.show = detailVisible.value
   droneTrackRuntime = []
   const baseCenters = [['默认区域', { longitude:114.958541, latitude: 27.121917 }]]
   const baseCenters = [['默认区域', { longitude: 114.958541, latitude: 27.121917 }]]
   const segmentDuration = 6
   const baseTrackColor = Cesium.Color.fromCssColorString('red')
   baseCenters.forEach(([centerName, center], trackIndex) => {
@@ -629,34 +650,34 @@
   const polygonInstances = []
   const lineInstances = []
   const texturedVertexFormat = getTexturedVertexFormat()
   ; (zones || []).forEach((zone, index) => {
      if (!zone?.geom) return
      const positions = getDefenseZonePositions(zone.geom)
      if (!positions.length) return
      const polygon = new Cesium.PolygonGeometry({
         polygonHierarchy: new Cesium.PolygonHierarchy(positions),
         vertexFormat: texturedVertexFormat,
      ; (zones || []).forEach((zone, index) => {
         if (!zone?.geom) return
         const positions = getDefenseZonePositions(zone.geom)
         if (!positions.length) return
         const polygon = new Cesium.PolygonGeometry({
            polygonHierarchy: new Cesium.PolygonHierarchy(positions),
            vertexFormat: texturedVertexFormat,
         })
         polygonInstances.push(
            new Cesium.GeometryInstance({
               geometry: polygon,
            })
         )
         const linePositions = positions.length > 1 ? [...positions, positions[0]] : positions
         lineInstances.push(
            new Cesium.GeometryInstance({
               geometry: new Cesium.GroundPolylineGeometry({
                  positions: linePositions,
                  width: 2,
               }),
               attributes: {
                  color: Cesium.ColorGeometryInstanceAttribute.fromColor(
                     Cesium.Color.fromCssColorString(lineColor)
                  ),
               },
            })
         )
      })
      polygonInstances.push(
         new Cesium.GeometryInstance({
            geometry: polygon,
         })
      )
      const linePositions = positions.length > 1 ? [...positions, positions[0]] : positions
      lineInstances.push(
         new Cesium.GeometryInstance({
            geometry: new Cesium.GroundPolylineGeometry({
               positions: linePositions,
               width: 2,
            }),
            attributes: {
               color: Cesium.ColorGeometryInstanceAttribute.fromColor(
                  Cesium.Color.fromCssColorString(lineColor)
               ),
            },
         })
      )
   })
   let primitive = null
   if (polygonInstances.length) {
      const baseColor1 = Cesium.Color.fromCssColorString(fillColor1)
@@ -732,7 +753,7 @@
      viewer.dataSources.add(aggregationSource)
   }
   clearAggregationEntities()
   aggregationSource.show = clusterVisible.value
   updateAggregationVisibility()
   const countMap = new Map()
      ; (list || []).forEach(item => {
         if (!item?.type) return
@@ -851,17 +872,17 @@
   ensureCommandPostCollection()
   commandPostBillboardCollection.removeAll()
   commandPostBillboardCollection.show = detailVisible.value
   ; (list || []).forEach((item, index) => {
      const position = getDevicePosition(item)
      if (!position) return
      commandPostBillboardCollection.add({
         position: Cesium.Cartesian3.fromDegrees(position.longitude, position.latitude, 0),
         image: commandPostIcon,
         width: 40,
         height: 56,
         verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
      ; (list || []).forEach((item, index) => {
         const position = getDevicePosition(item)
         if (!position) return
         commandPostBillboardCollection.add({
            position: Cesium.Cartesian3.fromDegrees(position.longitude, position.latitude, 0),
            image: commandPostIcon,
            width: 40,
            height: 56,
            verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
         })
      })
   })
   reorderCockpitPrimitives()
}
@@ -906,6 +927,29 @@
   showLayerPanel.value = !showLayerPanel.value
}
const applyLayerVisibility = checkedKeys => {
   publicCesium?.switchLayers?.(baseLayerKey.value === 'base-standard' ? 0 : 4)
   const showTerrain = checkedKeys.includes('ja-terrain')
   publicCesium?.setTerrainVisible?.(showTerrain)
   const showAdmin = checkedKeys.includes('admin')
   adminBoundaryVisible.value = showAdmin
   mapRef.value?.setAdminBoundaryVisible?.(showAdmin)
   updateAggregationVisibility()
}
const handleLayerCheck = (_data, state) => {
   const checkedKeys = state?.checkedKeys ?? layerTreeRef.value?.getCheckedKeys?.() ?? []
   treeCheckedKeys.value = checkedKeys
   applyLayerVisibility(checkedKeys)
}
const handleBaseLayerSelect = key => {
   if (!baseLayerKeys.includes(key)) return
   if (baseLayerKey.value === key) return
   baseLayerKey.value = key
   applyLayerVisibility(treeCheckedKeys.value)
}
const updateStageDisplay = stage => {
   const showCluster = stage === 'cluster'
   setClusterVisibility(showCluster)
@@ -913,9 +957,11 @@
   setDroneVisibility(!showCluster)
}
const handleMapReady = ({ viewer: mapViewer }) => {
const handleMapReady = ({ viewer: mapViewer, publicCesium: mapPublic }) => {
   viewer = mapViewer
   publicCesium = mapPublic
   ensureCockpitPrimitiveLayer()
   applyLayerVisibility(treeCheckedKeys.value)
   const height = viewer?.camera?.positionCartographic?.height
   const stage = getStageByHeight(height)
   updateStageDisplay(stage)
@@ -960,6 +1006,7 @@
   }
   cockpitPrimitiveLayer = null
   viewer = null
   publicCesium = null
})
</script>
@@ -1041,6 +1088,51 @@
      flex: 1;
      overflow: auto;
   }
   .base-map-title {
      margin-bottom: 8px;
      font-size: 12px;
      color: #ffffff;
      font-weight: 600;
   }
   .base-map-options {
      padding: 16px;
      display: grid;
      grid-template-columns: repeat(2, minmax(0, 1fr));
      gap: 8px;
   }
   .base-map-card {
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      gap: 6px;
      border-radius: 6px;
      color: #d8e6ff;
      font-size: 12px;
      cursor: pointer;
      transition: all 0.2s ease;
      .base-map-thumb {
         width: 46px;
         height: 46px;
         border-radius: 4px;
         background-size: cover;
         background-position: center;
         box-sizing: border-box;
      }
      &.active {
         .base-map-thumb {
            border: 2px solid #2ea8ff;
         }
         .base-map-label {
            color: #2ea8ff;
         }
      }
   }
}
</style>