From ad57696cef86a678512e93207bc5538d30bc40ee Mon Sep 17 00:00:00 2001
From: 罗广辉 <guanghui.luo@foxmail.com>
Date: Mon, 03 Aug 2026 10:39:46 +0800
Subject: [PATCH] feat: 播放按钮居中
---
applications/drone-command/src/components/map-container/device-map-container.vue | 328 ++++++++++++++++++++++++++++++++++++------------------
1 files changed, 219 insertions(+), 109 deletions(-)
diff --git a/applications/drone-command/src/components/map-container/device-map-container.vue b/applications/drone-command/src/components/map-container/device-map-container.vue
index cb4e5a2..a84f2e0 100644
--- a/applications/drone-command/src/components/map-container/device-map-container.vue
+++ b/applications/drone-command/src/components/map-container/device-map-container.vue
@@ -5,33 +5,47 @@
:show-admin-boundary="true" :zoom-to-boundary="true" :enable-stage-emit="true"
:cluster-height="CLUSTER_HEIGHT" :detail-height="DETAIL_HEIGHT" @ready="handleMapReady"
@stage-change="handleStageChange" />
- <div v-if="props.showLayerControl" class="layer-control-root" :class="{ collapsed: props.leftCollapsed }">
+ <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>
- <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>
+ <transition name="layer-panel-slide">
+ <div v-if="showLayerPanel" class="layer-panel">
+ <div class="panel-title">图层管理</div>
+ <div class="panel-content">
+ <el-tree ref="layerTreeRef" class="command-tree map-layer-tree" :data="layerTree"
+ icon="el-icon-arrow-down-bold"
+ show-checkbox default-expand-all node-key="key" :props="layerTreeProps"
+ :default-checked-keys="treeCheckedKeys" @check="handleLayerCheck" />
</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"
- :default-checked-keys="treeCheckedKeys" @check="handleLayerCheck" />
- </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>
</div>
</div>
@@ -45,6 +59,7 @@
</template>
<script setup>
+
import * as Cesium from 'cesium'
import CommonCesiumMap from '@/components/map-container/common-cesium-map.vue'
import { buildEllipsePositions } from '@/utils/cesium/shapeTools'
@@ -77,7 +92,7 @@
const DETAIL_HEIGHT = 10000
const POLYGON_HEIGHT_M = 0.2
const DRONE_TRACK_DURATION_S = 30 * 60
-const FIXED_DRONE_TRACK_IDS = ['6000000000602', '6000000000601']
+const FIXED_DRONE_TRACK_IDS = ['6000000000609', '6000000000608']
const props = defineProps({
allDevices: {
@@ -88,7 +103,7 @@
type: Array,
default: () => [],
},
- leftCollapsed: {
+ rightCollapsed: {
type: Boolean,
default: false,
},
@@ -139,6 +154,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)
@@ -153,6 +169,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([
{
@@ -450,86 +471,86 @@
const basePoints = [
[
{
- longitude: 114.964975,
- latitude: 27.134855,
+ longitude: 114.929475,
+ latitude: 27.136575,
height: 200
},
{
- longitude: 114.963966,
- latitude: 27.132836,
+ longitude: 114.928466,
+ latitude: 27.134556,
height: 200
},
{
- longitude: 114.963966,
- latitude: 27.132836,
+ longitude: 114.928466,
+ latitude: 27.134556,
height: 320
},
{
- longitude: 114.963974,
- latitude: 27.132677,
+ longitude: 114.928474,
+ latitude: 27.134397,
height: 320
},
{
- longitude: 114.963432,
- latitude: 27.131665,
+ longitude: 114.927932,
+ latitude: 27.133385,
height: 320
},
{
- longitude: 114.962865,
- latitude: 27.130377,
+ longitude: 114.927365,
+ latitude: 27.132097,
height: 320
},
{
- longitude: 114.965318,
- latitude: 27.129925,
+ longitude: 114.929818,
+ latitude: 27.131645,
height: 320
},
{
- longitude: 114.960144,
- latitude: 27.134902,
+ longitude: 114.924644,
+ latitude: 27.136622,
height: 120
},
],
[
{
- longitude: 114.931063,
- latitude: 27.095052,
+ longitude: 114.936386,
+ latitude: 26.825890,
height: 120
},
{
- longitude: 114.928653,
- latitude: 27.096307,
+ longitude: 114.933976,
+ latitude: 26.827145,
height: 120
},
{
- longitude: 114.925958,
- latitude: 27.096963,
+ longitude: 114.931281,
+ latitude: 26.827801,
height: 120
},
{
- longitude: 114.925958,
- latitude: 27.096963,
+ longitude: 114.931281,
+ latitude: 26.827801,
height: 400
},
{
- longitude: 114.925836,
- latitude: 27.096102,
+ longitude: 114.931159,
+ latitude: 26.826940,
height: 400
},
{
- longitude: 114.928384,
- latitude: 27.095866,
+ longitude: 114.933707,
+ latitude: 26.826704,
height: 400
},
{
- longitude: 114.929874,
- latitude: 27.096095,
+ longitude: 114.935197,
+ latitude: 26.826933,
height: 400
},
{
- longitude: 114.931048,
- latitude: 27.097666,
+ longitude: 114.936371,
+ latitude: 26.828504,
height: 400
},
],
@@ -780,7 +801,7 @@
},
})
const speedMs = Math.round(Cesium.Cartesian3.distance(positions[0], positions[1]) / segmentDuration)
- dronePickMap.set(droneId, {
+ dronePickMap.set(droneId, {
data: {
...item,
flightHeightM: item.flightHeightM ?? points[0].height,
@@ -1290,9 +1311,11 @@
)
watch(
- () => props.leftCollapsed,
+ () => props.rightCollapsed,
isCollapsed => {
- if (isCollapsed) showLayerPanel.value = false
+ if (!isCollapsed) return
+ showLayerPanel.value = false
+ showBaseLayerPanel.value = false
}
)
@@ -1331,7 +1354,7 @@
setDroneVisibility(!showCluster)
}
-const handleMapReady = async ({ viewer: mapViewer, publicCesium: mapPublic }) => {
+const handleMapReady = async ({ viewer: mapViewer, publicCesium: mapPublic }) => {
if (mapReadyHandled) return
mapReadyHandled = true
viewer = mapViewer
@@ -1353,7 +1376,7 @@
updateStageDisplay(stage)
}
-const handleClickOutside = event => {
+const handleLayerClickOutside = event => {
if (!showLayerPanel.value) return
const target = event.target
if (layerWrapRef.value?.contains(target)) return
@@ -1361,13 +1384,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()
@@ -1415,14 +1438,14 @@
.layer-control-root {
position: absolute;
- left: 337px;
- bottom: 22px;
- z-index: 9;
+ right: 337px;
+ top: 95px;
+ z-index: 11;
transition: transform 0.3s ease-in-out;
pointer-events: none;
&.collapsed {
- transform: translateX(-317px);
+ transform: translateX(317px);
}
}
@@ -1449,12 +1472,13 @@
display: flex;
flex-direction: column;
position: absolute;
- left: 66px;
- bottom: 0;
+ right: 66px;
+ top: 0;
width: 160px;
max-height: 442px;
background: #191932;
border-radius: 8px 8px 8px 8px;
+ z-index: 99;
.panel-title {
padding: 0 16px;
@@ -1477,50 +1501,136 @@
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,
+.layer-panel-slide-leave-active {
+ transition: transform 0.24s ease, opacity 0.24s ease;
+ transform-origin: right center;
+}
+
+.layer-panel-slide-enter-from,
+.layer-panel-slide-leave-to {
+ transform: translateX(24px);
+ opacity: 0;
}
</style>
--
Gitblit v1.9.3