From 7224851bf14cab706d36f695331488941ed4aba6 Mon Sep 17 00:00:00 2001
From: chenyao <1219716595@qq.com>
Date: Fri, 31 Jul 2026 18:01:05 +0800
Subject: [PATCH] feat:去除dialog滚动条

---
 applications/drone-command/src/components/map-container/device-map-container.vue |  539 ++++++++++++++++++++++++++++++++++-------------------------
 1 files changed, 308 insertions(+), 231 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 7d6383c..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,6 +92,7 @@
 const DETAIL_HEIGHT = 10000
 const POLYGON_HEIGHT_M = 0.2
 const DRONE_TRACK_DURATION_S = 30 * 60
+const FIXED_DRONE_TRACK_IDS = ['6000000000609', '6000000000608']
 
 const props = defineProps({
 	allDevices: {
@@ -87,7 +103,7 @@
 		type: Array,
 		default: () => [],
 	},
-	leftCollapsed: {
+	rightCollapsed: {
 		type: Boolean,
 		default: false,
 	},
@@ -131,13 +147,14 @@
 let favoritePulseElapsed = 0
 const favoritePulseEntities = []
 const pulseBaseColor = Cesium.Color.fromCssColorString('#FF3B30')
-const PULSE_MIN_RADIUS_M = 2
-const PULSE_MAX_RADIUS_M = 30
+const PULSE_MIN_RADIUS_M = 20
+const PULSE_MAX_RADIUS_M = 80
 const PULSE_DURATION_S = 1.4
 const detailVisible = ref(true)
 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)
@@ -152,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([
 	{
@@ -216,93 +238,17 @@
 	return canvas
 }
 
-class PulseSphereMaterialProperty {
-	constructor(color, speed = 1.2, bands = 3.0, softness = 0.18) {
-		this._definitionChanged = new Cesium.Event()
-		this.color = color
-		this.speed = speed
-		this.bands = bands
-		this.softness = softness
-	}
-	get isConstant() {
-		return true
-	}
-	get definitionChanged() {
-		return this._definitionChanged
-	}
-	getType() {
-		return 'PulseSphereMaterial'
-	}
-	getValue(_time, result = {}) {
-		result.color = this.color
-		result.time = (performance.now() / 1000) * this.speed
-		result.bands = this.bands
-		result.softness = this.softness
-		return result
-	}
-	equals(other) {
-		return (
-			other instanceof PulseSphereMaterialProperty &&
-			Cesium.Color.equals(this.color, other.color) &&
-			this.speed === other.speed &&
-			this.bands === other.bands &&
-			this.softness === other.softness
-		)
-	}
-}
-
-let pulseSphereMaterial = null
-const getPulseSphereMaterial = () => {
-	if (pulseSphereMaterial) return pulseSphereMaterial
-	Cesium.Material._materialCache.addMaterial('PulseSphereMaterial', {
-		fabric: {
-			type: 'PulseSphereMaterial',
-			uniforms: {
-				color: pulseBaseColor.withAlpha(0.45),
-				time: 0,
-				bands: 3.0,
-				softness: 0.18,
-			},
-			source: `
-				czm_material czm_getMaterial(czm_materialInput materialInput)
-				{
-					czm_material material = czm_getDefaultMaterial(materialInput);
-					vec3 normalEC = normalize(materialInput.normalEC);
-					vec3 viewEC = normalize(-materialInput.positionToEyeEC);
-					float ndv = max(dot(normalEC, viewEC), 0.0);
-					float fresnel = pow(1.0 - ndv, 1.6);
-					float wave = sin((ndv * bands + time) * 6.2831853);
-					float ring = smoothstep(-softness, softness, wave);
-					float alpha = (ring * 0.6 + fresnel * 0.4) * color.a;
-					material.diffuse = color.rgb;
-					material.alpha = alpha;
-					return material;
-				}
-			`,
-		},
-		translucent: () => true,
-	})
-	pulseSphereMaterial = new PulseSphereMaterialProperty(pulseBaseColor.withAlpha(0.45), 1.1, 3.2, 0.22)
-	return pulseSphereMaterial
-}
-
 const createFavoritePulseEntity = (billboard) => {
 	if (!viewer || !billboard) return
-	const material = getPulseSphereMaterial()
 	const pulse = {
-		material,
 		offset: Math.random() * PULSE_DURATION_S,
 		entity: null,
 	}
-	const getPulseRadius3D = () => {
-		const radius = getPulseRadius(pulse)
-		return new Cesium.Cartesian3(radius, radius, radius)
-	}
 	pulse.entity = viewer.entities.add({
 		position: new Cesium.CallbackProperty(() => getBillboardPosition(billboard), false),
-		ellipsoid: {
-			radii: new Cesium.CallbackProperty(() => getPulseRadius3D(), false),
-			material,
+		point: {
+			pixelSize: new Cesium.CallbackProperty(() => getPulseRadius(pulse), false),
+			color: pulseBaseColor.withAlpha(0.4),
 		},
 	})
 	favoritePulseEntities.push(pulse)
@@ -378,10 +324,10 @@
 	if (deviceRingOutlinePrimitives.length) {
 		deviceRingOutlinePrimitives.forEach(primitive => cockpitPrimitiveLayer.add(primitive))
 	}
+	if (commandPostBillboardCollection) cockpitPrimitiveLayer.add(commandPostBillboardCollection)
+	if (deviceBillboardCollection) cockpitPrimitiveLayer.add(deviceBillboardCollection)
 	if (droneTrackPolylineCollection) cockpitPrimitiveLayer.add(droneTrackPolylineCollection)
 	if (droneTrackBillboardCollection) cockpitPrimitiveLayer.add(droneTrackBillboardCollection)
-	if (deviceBillboardCollection) cockpitPrimitiveLayer.add(deviceBillboardCollection)
-	if (commandPostBillboardCollection) cockpitPrimitiveLayer.add(commandPostBillboardCollection)
 }
 
 const clearDeviceEntities = () => {
@@ -525,82 +471,92 @@
 	const basePoints = [
 		[
 			{
-				longitude: 114.963191,
-				latitude: 27.136716,
-				height: 120
+				longitude: 114.929475,
+				latitude: 27.136575,
+				height: 200
 			},
 			{
-				longitude: 114.957308,
-				latitude: 27.138452,
-				height: 120
+				longitude: 114.928466,
+				latitude: 27.134556,
+				height: 200
 			},
 			{
-				longitude: 114.952,
-				latitude: 27.136317,
-				height: 120
+				longitude: 114.928466,
+				latitude: 27.134556,
+				height: 320
 			},
 			{
-				longitude: 114.949293,
-				latitude: 27.133864,
-				height: 120
+				longitude: 114.928474,
+				latitude: 27.134397,
+				height: 320
 			},
 			{
-				longitude: 114.944666,
-				latitude: 27.130526,
-				height: 120
+				longitude: 114.927932,
+				latitude: 27.133385,
+				height: 320
 			},
 			{
-				longitude: 114.945909,
-				latitude: 27.127845,
-				height: 120
+				longitude: 114.927365,
+				latitude: 27.132097,
+				height: 320
 			},
 			{
-				longitude: 114.962974,
-				latitude: 27.136242,
+				longitude: 114.929818,
+				latitude: 27.131645,
+				height: 320
+			},
+			{
+				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.925836, 
-				latitude: 27.096102,
-				height: 120
+				longitude: 114.931281,
+				latitude: 26.827801,
+				height: 400
 			},
 			{
-				longitude: 114.928384, 
-				latitude: 27.095866,
-				height: 120
+				longitude: 114.931159,
+				latitude: 26.826940,
+				height: 400
 			},
 			{
-				longitude: 114.929874, 
-				latitude: 27.096095,
-				height: 120
+				longitude: 114.933707,
+				latitude: 26.826704,
+				height: 400
 			},
 			{
-				longitude: 114.931048, 
-				latitude: 27.097666,
-				height: 120
+				longitude: 114.935197,
+				latitude: 26.826933,
+				height: 400
 			},
-		]
+			{
+				longitude: 114.936371,
+				latitude: 26.828504,
+				height: 400
+			},
+		],
 	]
 
-	return basePoints[center.trackIndex]
+	return basePoints[center.trackIndex] || basePoints[1]
 }
 
 
@@ -667,7 +623,9 @@
 
 const setupDroneTrackClock = () => {
 	if (!viewer) return
-	const startTime = Cesium.JulianDate.now()
+	const now = new Date()
+	const noon = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 12, 0, 0, 0)
+	const startTime = Cesium.JulianDate.fromDate(noon)
 	const stopTime = Cesium.JulianDate.addSeconds(startTime, DRONE_TRACK_DURATION_S, new Cesium.JulianDate())
 	viewer.clock.startTime = startTime
 	viewer.clock.stopTime = stopTime
@@ -692,11 +650,11 @@
 		const pos = isEntityPosition
 			? getBillboardPosition(track.billboard)
 			: Cesium.Cartesian3.lerp(
-					track.positions[seg],
-					track.positions[seg + 1],
-					ratio,
-					new Cesium.Cartesian3()
-			  )
+				track.positions[seg],
+				track.positions[seg + 1],
+				ratio,
+				new Cesium.Cartesian3()
+			)
 		if (!pos) return
 		if (!isEntityPosition) {
 			track.billboard.position = pos
@@ -782,7 +740,9 @@
 	droneTrackPolylineCollection.show = detailVisible.value
 	droneTrackRuntime = []
 	const baseTrackColor = Cesium.Color.fromCssColorString('red')
-		; (list || []).forEach((item, trackIndex) => {
+		; (FIXED_DRONE_TRACK_IDS || []).forEach((trackId, trackIndex) => {
+			const item = (list || []).find(entry => String(entry?.id) === String(trackId))
+			if (!item) return
 			const position = getDevicePosition(item)
 			if (!position) return
 			const points = buildSimulatedTrackPoints({ ...position, height: item.flightHeightM, trackIndex })
@@ -806,7 +766,7 @@
 				width: 3,
 				material: trackMaterial,
 			})
-			const droneId = `drone-alarm-${item?.alarmRecordId ?? item?.id ?? trackIndex}`
+			const droneId = `drone-alarm-${trackId}`
 			const startTime = viewer.clock.startTime
 			const stopTime = viewer.clock.stopTime
 			const positionProperty = new Cesium.SampledPositionProperty()
@@ -841,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,
@@ -892,32 +852,27 @@
 		const entityId = `online-device-${item.id ?? index}-${index}`
 		const rangeMeters = getDeviceRange(item)
 		devicePositions.push({ lng: position.longitude, lat: position.latitude })
-		const billboard = deviceBillboardCollection.add({
-			position: Cesium.Cartesian3.fromDegrees(position.longitude, position.latitude, 0),
-			image: isOnline ? equipmentIcon : offlineEquipmentIcon,
-			width: 40,
-			height: 56,
-			verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
-		})
-		billboard.id = entityId
-		devicePickMap.set(entityId, { data: item, billboard })
-		deviceEntries.push({ position, rangeMeters, billboard, isOnline })
+		deviceEntries.push({ position, rangeMeters, isOnline, entityId, data: item })
 	})
-	if (selectedTargetType.value === 'device' && selectedDeviceBillboard && !devicePickMap.has(selectedDeviceBillboard.id)) {
-		closePopup()
-	}
 	if (deviceEntries.length) {
 		const heights = await getPointPositionsHeight(devicePositions, viewer)
 		deviceEntries.forEach((entry, index) => {
 			const height = Number(heights?.[index]?.ASL)
 			const centerHeight = Number.isFinite(height) ? height : 0
-			if (entry.billboard) {
-				entry.billboard.position = Cesium.Cartesian3.fromDegrees(
+			const billboard = deviceBillboardCollection.add({
+				position: Cesium.Cartesian3.fromDegrees(
 					entry.position.longitude,
 					entry.position.latitude,
 					centerHeight
-				)
-			}
+				),
+				image: entry.isOnline ? equipmentIcon : offlineEquipmentIcon,
+				width: 40,
+				height: 56,
+				verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
+				disableDepthTestDistance: Number.POSITIVE_INFINITY,
+			})
+			billboard.id = entry.entityId
+			devicePickMap.set(entry.entityId, { data: entry.data, billboard })
 			if (!entry.isOnline) return
 			if (!Number.isFinite(entry.rangeMeters) || entry.rangeMeters <= 0) return
 			const primitive = createDeviceRangePrimitiveWithHeight(entry.position, entry.rangeMeters, centerHeight)
@@ -926,6 +881,9 @@
 			addCockpitPrimitive(primitive)
 			deviceRingFillPrimitives.push(primitive)
 		})
+	}
+	if (selectedTargetType.value === 'device' && selectedDeviceBillboard && !devicePickMap.has(selectedDeviceBillboard.id)) {
+		closePopup()
 	}
 	reorderCockpitPrimitives()
 }
@@ -1040,7 +998,7 @@
 	if (!trimmed) return null
 	try {
 		return JSON.parse(trimmed)
-	} catch (error) {}
+	} catch (error) { }
 	return null
 }
 
@@ -1093,7 +1051,7 @@
 						.filter(radius => Number.isFinite(radius) && radius > 0)
 					radii.forEach((radius, levelIndex) => {
 						const positions = buildEllipsePositions(centerCartesian, radius, radius)
-					const style = BUFFER_LEVEL_STYLES[levelIndex] || BUFFER_LEVEL_STYLES[BUFFER_LEVEL_STYLES.length - 1]
+						const style = BUFFER_LEVEL_STYLES[levelIndex] || BUFFER_LEVEL_STYLES[BUFFER_LEVEL_STYLES.length - 1]
 						if (positions.length >= 3) {
 							shapes.push({
 								positions,
@@ -1273,22 +1231,38 @@
 const handleDroneSignal = () => emit('droneSignal', selectedDevice.value)
 const handleDroneCounter = () => emit('droneCounter', selectedDevice.value)
 
-const renderCommandPosts = list => {
+const renderCommandPosts = async list => {
 	if (!viewer) return
 	ensureCommandPostCollection()
 	commandPostBillboardCollection.removeAll()
 	commandPostBillboardCollection.show = detailVisible.value
+	const entries = []
+	const positions = []
 		; (list || []).forEach((item, index) => {
 			const position = getDevicePosition(item)
 			if (!position) return
+			entries.push({ position, index })
+			positions.push({ lng: position.longitude, lat: position.latitude })
+		})
+	if (positions.length) {
+		const heights = await getPointPositionsHeight(positions, viewer)
+		entries.forEach((entry, idx) => {
+			const height = Number(heights?.[idx]?.ASL)
+			const centerHeight = Number.isFinite(height) ? height : 0
 			commandPostBillboardCollection.add({
-				position: Cesium.Cartesian3.fromDegrees(position.longitude, position.latitude, 0),
+				position: Cesium.Cartesian3.fromDegrees(
+					entry.position.longitude,
+					entry.position.latitude,
+					centerHeight
+				),
 				image: commandPostIcon,
 				width: 40,
 				height: 56,
 				verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
+				disableDepthTestDistance: Number.POSITIVE_INFINITY,
 			})
 		})
+	}
 	reorderCockpitPrimitives()
 }
 
@@ -1308,9 +1282,9 @@
 		const res = await newDefenseSceneManageList({
 			time: dayjs().format('YYYY-MM-DD HH:mm:ss')
 		})
-		renderCommandPosts(res?.data?.data ?? [])
+		await renderCommandPosts(res?.data?.data ?? [])
 	} catch (error) {
-		renderCommandPosts([])
+		await renderCommandPosts([])
 	}
 }
 
@@ -1337,9 +1311,11 @@
 )
 
 watch(
-	() => props.leftCollapsed,
+	() => props.rightCollapsed,
 	isCollapsed => {
-		if (isCollapsed) showLayerPanel.value = false
+		if (!isCollapsed) return
+		showLayerPanel.value = false
+		showBaseLayerPanel.value = false
 	}
 )
 
@@ -1378,7 +1354,7 @@
 	setDroneVisibility(!showCluster)
 }
 
-const handleMapReady = ({ viewer: mapViewer, publicCesium: mapPublic }) => {
+const handleMapReady = async ({ viewer: mapViewer, publicCesium: mapPublic }) => {
 	if (mapReadyHandled) return
 	mapReadyHandled = true
 	viewer = mapViewer
@@ -1388,10 +1364,10 @@
 	const height = viewer?.camera?.positionCartographic?.height
 	const stage = getStageByHeight(height)
 	updateStageDisplay(stage)
+	await loadCommandPosts()
 	renderDeviceEntities(props.allDevices)
 	loadPartitions()
 	loadAggregation()
-	loadCommandPosts()
 	renderSimulatedDroneTrack(props.alarmDrones)
 	initDeviceClickHandler()
 }
@@ -1400,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
@@ -1408,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()
@@ -1428,6 +1404,20 @@
 	cockpitPrimitiveLayer = null
 	viewer = null
 	publicCesium = null
+})
+
+const getMap = () => {
+	return mapRef.value?.getMap()
+}
+
+// 暴露给父组件调用
+const flyTo = (options) => {
+	mapRef.value?.flyToPoint(options)
+}
+
+defineExpose({
+	getMap,
+	flyTo
 })
 </script>
 
@@ -1448,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);
 	}
 }
 
@@ -1482,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;
@@ -1510,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