吉安感知网项目-前端
shuishen
2026-02-25 40780bc04d8747f0918ee7e2a551776e69c44232
applications/drone-command/src/components/map-container/device-map-container.vue
@@ -7,30 +7,41 @@
         @stage-change="handleStageChange" />
      <div v-if="props.showLayerControl" class="layer-control-root" :class="{ collapsed: props.rightCollapsed }">
         <div class="layer-control-wrap" ref="layerWrapRef">
            <div class="layer-control" @click="toggleLayerPanel">
            <div class="layer-control" @click.stop="toggleLayerPanel">
               <img :src="layerControlIcon" alt="图层控制" />
            </div>
            <transition name="layer-panel-slide">
               <div v-if="showLayerPanel" class="layer-panel">
                  <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 ref="layerTreeRef" class="command-tree map-layer-tree" :data="layerTree" show-checkbox
                        default-expand-all node-key="key" :props="layerTreeProps"
                     <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>
            </transition>
         </div>
      </div>
      <div v-if="props.showLayerControl" class="base-layer-switch-root" :class="{ collapsed: props.rightCollapsed }">
         <div class="base-layer-switch-wrap" @mouseenter="showBaseLayerPanel = true"
            @mouseleave="showBaseLayerPanel = false">
            <div class="base-layer-trigger">
               <img class="base-layer-trigger-thumb" :src="currentBaseLayer.icon" :alt="currentBaseLayer.label">
               <div class="base-layer-trigger-label">{{ currentBaseLayer.shortLabel }}</div>
            </div>
            <transition name="layer-panel-slide">
               <div v-if="showBaseLayerPanel" class="base-layer-panel">
                  <div class="base-map-options">
                     <div class="base-map-card" :class="{ active: baseLayerKey === 'base-satellite' }"
                        @click="handleBaseLayerSelect('base-satellite')">
                        <img class="base-map-thumb" :src="yxIcon" alt="">
                        <div class="base-map-label">卫星地图</div>
                     </div>
                     <div class="base-map-card" :class="{ active: baseLayerKey === 'base-standard' }"
                        @click="handleBaseLayerSelect('base-standard')">
                        <img class="base-map-thumb" :src="dzIcon" alt="">
                        <div class="base-map-label">标准地图</div>
                     </div>
                  </div>
               </div>
            </transition>
@@ -141,6 +152,7 @@
const clusterVisible = ref(false)
const countyCenterMap = new Map()
const showLayerPanel = ref(false)
const showBaseLayerPanel = ref(false)
const layerWrapRef = ref(null)
const layerTreeRef = ref(null)
const selectedDevice = ref(null)
@@ -155,6 +167,11 @@
const baseLayerKeys = ['base-standard', 'base-satellite']
const defaultCheckedKeys = ['ja-terrain', 'admin', 'city-base']
const baseLayerKey = ref('base-satellite')
const baseLayerMeta = {
   'base-standard': { icon: dzIcon, label: '标准地图', shortLabel: '标准' },
   'base-satellite': { icon: yxIcon, label: '卫星地图', shortLabel: '影像' },
}
const currentBaseLayer = computed(() => baseLayerMeta[baseLayerKey.value] || baseLayerMeta['base-satellite'])
const treeCheckedKeys = ref([...defaultCheckedKeys])
const layerTree = ref([
   {
@@ -1294,7 +1311,9 @@
watch(
   () => props.rightCollapsed,
   isCollapsed => {
      if (isCollapsed) showLayerPanel.value = false
      if (!isCollapsed) return
      showLayerPanel.value = false
      showBaseLayerPanel.value = false
   }
)
@@ -1333,7 +1352,7 @@
   setDroneVisibility(!showCluster)
}
const  handleMapReady = async ({ viewer: mapViewer, publicCesium: mapPublic }) => {
const handleMapReady = async ({ viewer: mapViewer, publicCesium: mapPublic }) => {
   if (mapReadyHandled) return
   mapReadyHandled = true
   viewer = mapViewer
@@ -1355,7 +1374,7 @@
   updateStageDisplay(stage)
}
const handleClickOutside = event => {
const handleLayerClickOutside = event => {
   if (!showLayerPanel.value) return
   const target = event.target
   if (layerWrapRef.value?.contains(target)) return
@@ -1363,13 +1382,13 @@
}
onMounted(() => {
   document.addEventListener('click', handleClickOutside)
   document.addEventListener('click', handleLayerClickOutside)
   const map = mapRef.value?.getMap()
   if (map?.viewer) handleMapReady(map)
})
onBeforeUnmount(() => {
   document.removeEventListener('click', handleClickOutside)
   document.removeEventListener('click', handleLayerClickOutside)
   clearDeviceEntities()
   clearPartitionEntities()
   clearAggregationEntities()
@@ -1480,51 +1499,125 @@
      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-layer-switch-root {
   position: absolute;
   right: 337px;
   bottom: 78px;
   z-index: 11;
   transition: transform 0.3s ease-in-out;
   pointer-events: none;
   .base-map-card {
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
      gap: 6px;
   &.collapsed {
      transform: translateX(317px);
   }
}
.base-layer-switch-wrap {
   position: relative;
   pointer-events: auto;
}
.base-layer-trigger {
   width: 55px;
   padding: 8px 0;
   border-radius: 8px;
   background: #191932;
   display: flex;
   flex-direction: column;
   align-items: center;
   gap: 6px;
   cursor: pointer;
}
.base-layer-trigger-thumb {
   width: 36px;
   height: 36px;
   border-radius: 6px;
   display: block;
   // border: 2px solid #fff;
   background-size: cover;
   background-position: center;
   box-sizing: border-box;
}
.base-layer-trigger-label {
   margin-top: 8px;
   font-size: 12px;
   line-height: 1;
   color: #ffffff;
   font-weight: 700;
}
.base-layer-panel {
   position: absolute;
   right: calc(100% + 10px);
   bottom: 0;
   margin-bottom: 0;
   width: 160px;
   background: #191932;
   border-radius: 8px;
   z-index: 99;
   &::after {
      content: '';
      position: absolute;
      right: -10px;
      top: 0;
      width: 10px;
      height: 100%;
      background: transparent;
   }
}
.base-map-options {
   padding: 8px;
   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: 36px;
      height: 36px;
      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;
         }
      }
      // border: 2px solid #fff;
      background-size: cover;
      background-position: center;
      box-sizing: border-box;
   }
   .base-map-label {
      margin-top: 8px;
      font-size: 12px;
      line-height: 1;
      color: #ffffff;
      font-weight: 700;
   }
   // &.active {
   //    .base-map-thumb {
   //       border: 2px solid #2ea8ff;
   //    }
   //    .base-map-label {
   //       color: #2ea8ff;
   //    }
   // }
}
.layer-panel-slide-enter-active,