From 9fb4de853fc8aab2000f312ba2b8907e3dc9a61d Mon Sep 17 00:00:00 2001
From: 张含笑 <zhx18749296735@163.com>
Date: Thu, 29 Jan 2026 17:33:29 +0800
Subject: [PATCH] feat:调整
---
applications/drone-command/src/components/map-container/device-map-container.vue | 561 ++++++++++++++++++++++++++++++++++++++++---------------
1 files changed, 406 insertions(+), 155 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 c2a8168..f7fb7e9 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
@@ -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="handleDroneFavorite"
+ @signal="handleDroneSignal" @counter="handleDroneCounter" />
</div>
</template>
@@ -64,12 +67,18 @@
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
const props = defineProps({
onlineDevices: {
+ type: Array,
+ default: () => [],
+ },
+ alarmDrones: {
type: Array,
default: () => [],
},
@@ -86,12 +95,13 @@
default: true,
},
})
-const emit = defineEmits(['droneSignal', 'droneCounter'])
+const emit = defineEmits(['droneSignal', 'droneCounter', 'droneFavorite'])
const DEFAULT_ZONE_PAGE_SIZE = 999
const mapRef = ref(null)
let viewer = null
+let publicCesium = null
let cockpitPrimitiveLayer = null
const devicePickMap = new Map()
const dronePickMap = new Map()
@@ -109,11 +119,20 @@
let droneTrackTickHandler = null
let droneTrackStartTime = null
let droneTrackRuntime = []
+let favoritePulseRafId = null
+let favoritePulseStartAt = 0
+let favoritePulseElapsed = 0
+const favoritePulseEntities = []
+const pulseBaseColor = Cesium.Color.fromCssColorString('#FF3B30')
+const PULSE_MIN_RADIUS_M = 60
+const PULSE_MAX_RADIUS_M = 180
+const PULSE_DURATION_S = 1.6
const detailVisible = ref(true)
const clusterVisible = ref(false)
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 +142,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 +177,60 @@
{ key: 'route', label: '飞行航路' },
],
},
-])
+ */
+
+const adminBoundaryVisible = ref(treeCheckedKeys.value.includes('admin'))
+
+const isFavorited = item => {
+ const value = item?.favorited ?? item?.isFavorite
+ return value === 1 || value === '1' || value === true
+}
+
+let pulseCanvas = null
+const getPulseCanvas = () => {
+ if (pulseCanvas) return pulseCanvas
+ const size = 256
+ const canvas = document.createElement('canvas')
+ canvas.width = size
+ canvas.height = size
+ const ctx = canvas.getContext('2d')
+ const center = size / 2
+ const radius = center - 2
+ const gradient = ctx.createRadialGradient(center, center, radius * 0.1, center, center, radius)
+ gradient.addColorStop(0, 'rgba(255,59,48,0.55)')
+ gradient.addColorStop(0.35, 'rgba(255,59,48,0.35)')
+ gradient.addColorStop(0.7, 'rgba(255,59,48,0.15)')
+ gradient.addColorStop(1, 'rgba(255,59,48,0)')
+ ctx.fillStyle = gradient
+ ctx.beginPath()
+ ctx.arc(center, center, radius, 0, Math.PI * 2)
+ ctx.fill()
+ pulseCanvas = canvas
+ return canvas
+}
+
+const createFavoritePulseEntity = (billboard) => {
+ if (!viewer || !billboard) return
+ const material = new Cesium.ImageMaterialProperty({
+ image: getPulseCanvas(),
+ transparent: true,
+ })
+ const pulse = {
+ material,
+ offset: Math.random() * PULSE_DURATION_S,
+ entity: null,
+ }
+ pulse.entity = viewer.entities.add({
+ position: new Cesium.CallbackProperty(() => billboard.position, false),
+ ellipse: {
+ semiMajorAxis: new Cesium.CallbackProperty(() => getPulseRadius(pulse), false),
+ semiMinorAxis: new Cesium.CallbackProperty(() => getPulseRadius(pulse), false),
+ material,
+ heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
+ },
+ })
+ favoritePulseEntities.push(pulse)
+}
const getDevicePosition = item => {
const longitudeRaw = item.longitude ?? item.lng ?? item.lon
@@ -291,6 +370,12 @@
}
}
+const getPulseRadius = pulse => {
+ const phase = ((favoritePulseElapsed + pulse.offset) % PULSE_DURATION_S) / PULSE_DURATION_S
+ const wave = phase < 0.5 ? phase * 2 : (1 - phase) * 2
+ return PULSE_MIN_RADIUS_M + (PULSE_MAX_RADIUS_M - PULSE_MIN_RADIUS_M) * wave
+}
+
const setDetailVisibility = visible => {
detailVisible.value = visible
if (defenseZonePrimitive) defenseZonePrimitive.show = visible
@@ -314,12 +399,22 @@
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 => {
if (droneTrackBillboardCollection) droneTrackBillboardCollection.show = visible
if (droneTrackPolylineCollection) droneTrackPolylineCollection.show = visible
+ if (favoritePulseEntities.length) {
+ favoritePulseEntities.forEach(pulse => {
+ if (pulse?.entity) pulse.entity.show = visible
+ })
+ }
if (!visible && selectedTargetType.value === 'drone') {
closePopup()
}
@@ -342,50 +437,93 @@
})
}
-const buildSimulatedTrackPoints = () => {
- return [
- {
- longitude: 114.963191,
- latitude: 27.136716,
- height: 120
- },
- {
- longitude:114.957308,
- latitude: 27.138452,
- height: 120
- },
- {
- longitude:114.952,
- latitude: 27.136317,
- height: 120
- },
- {
- longitude:114.949293,
- latitude: 27.133864,
- height: 120
- },
- {
- longitude:114.944666,
- latitude: 27.130526,
- height: 120
- },
- {
- longitude:114.945909,
- latitude:27.127845,
- height: 120
- },
- {
- longitude:114.962974,
- latitude:27.136242,
- height: 120
- },
+const buildSimulatedTrackPoints = center => {
+ const basePoints = [
+ [
+ {
+ longitude: 114.963191,
+ latitude: 27.136716,
+ height: 120
+ },
+ {
+ longitude: 114.957308,
+ latitude: 27.138452,
+ height: 120
+ },
+ {
+ longitude: 114.952,
+ latitude: 27.136317,
+ height: 120
+ },
+ {
+ longitude: 114.949293,
+ latitude: 27.133864,
+ height: 120
+ },
+ {
+ longitude: 114.944666,
+ latitude: 27.130526,
+ height: 120
+ },
+ {
+ longitude: 114.945909,
+ latitude: 27.127845,
+ height: 120
+ },
+ {
+ longitude: 114.962974,
+ latitude: 27.136242,
+ height: 120
+ },
+ ],
+
+ [
+ {
+ longitude: 114.931063,
+ latitude: 27.095052,
+ height: 120
+ },
+ {
+ longitude: 114.928653,
+ latitude: 27.096307,
+ height: 120
+ },
+ {
+ longitude: 114.925958,
+ latitude: 27.096963,
+ height: 120
+ },
+ {
+ longitude: 114.925836,
+ latitude: 27.096102,
+ height: 120
+ },
+ {
+ longitude: 114.928384,
+ latitude: 27.095866,
+ height: 120
+ },
+ {
+ longitude: 114.929874,
+ latitude: 27.096095,
+ height: 120
+ },
+ {
+ longitude: 114.931048,
+ latitude: 27.097666,
+ height: 120
+ },
+ ]
]
+
+ return basePoints[center.trackIndex]
}
const clearDroneTrackEntities = () => {
if (!viewer) return
stopDroneTrackAnimation()
+ stopFavoritePulseAnimation()
if (droneTrackBillboardCollection) {
if (!droneTrackBillboardCollection.isDestroyed?.()) {
removeCockpitPrimitive(droneTrackBillboardCollection)
@@ -399,6 +537,12 @@
droneTrackPolylineCollection = null
}
droneTrackRuntime = []
+ if (favoritePulseEntities.length) {
+ favoritePulseEntities.forEach(pulse => {
+ if (pulse?.entity) viewer?.entities?.remove(pulse.entity)
+ })
+ favoritePulseEntities.length = 0
+ }
dronePickMap.clear()
if (selectedTargetType.value === 'drone') {
closePopup()
@@ -465,72 +609,95 @@
droneTrackTickHandler = null
}
-const renderSimulatedDroneTrack = () => {
+const startFavoritePulseAnimation = () => {
+ if (!viewer || favoritePulseRafId) return
+ favoritePulseStartAt = performance.now()
+ const tick = now => {
+ if (!viewer || viewer.isDestroyed?.() || !favoritePulseEntities.length) {
+ favoritePulseRafId = null
+ return
+ }
+ favoritePulseElapsed = (now - favoritePulseStartAt) / 1000
+ viewer.scene.requestRender()
+ favoritePulseRafId = requestAnimationFrame(tick)
+ }
+ favoritePulseRafId = requestAnimationFrame(tick)
+}
+
+const stopFavoritePulseAnimation = () => {
+ if (favoritePulseRafId) {
+ cancelAnimationFrame(favoritePulseRafId)
+ favoritePulseRafId = null
+ }
+}
+
+const renderSimulatedDroneTrack = (list) => {
if (!viewer) return
clearDroneTrackEntities()
+ if (!list?.length) return
ensureDroneTrackCollections()
droneTrackBillboardCollection.show = detailVisible.value
droneTrackPolylineCollection.show = detailVisible.value
droneTrackRuntime = []
- const baseCenters = [['默认区域', { longitude:114.958541, latitude: 27.121917 }]]
const segmentDuration = 6
const baseTrackColor = Cesium.Color.fromCssColorString('red')
- baseCenters.forEach(([centerName, center], trackIndex) => {
- const points = buildSimulatedTrackPoints(center)
- if (points.length < 2) return
- const positions = points.map(point =>
- Cesium.Cartesian3.fromDegrees(point.longitude, point.latitude, point.height)
- )
- let trackMaterial = createDroneTrackMaterial({
- color: baseTrackColor,
- speed: 4.5,
- headWidth: 0.2,
- glowPower: 1.8,
- backgroundAlpha: 0.34,
- })
- if (!trackMaterial) {
- trackMaterial = Cesium.Material.fromType('Color', { color: baseTrackColor })
- }
- droneTrackPolylineCollection.add({
- positions,
- width: 3,
- material: trackMaterial,
- })
- const droneId = `drone-track-${trackIndex}`
- const billboard = droneTrackBillboardCollection.add({
- position: positions[positions.length - 1],
- image: droneIcon,
- width: 36,
- height: 36,
- verticalOrigin: Cesium.VerticalOrigin.CENTER,
- disableDepthTestDistance: Number.POSITIVE_INFINITY,
- })
- billboard.id = droneId
- const speedMs = Math.round(Cesium.Cartesian3.distance(positions[0], positions[1]) / segmentDuration)
+ ; (list || []).forEach((item, trackIndex) => {
+ const position = getDevicePosition(item)
+ if (!position) return
+ const points = buildSimulatedTrackPoints({ ...position, height: item.flightHeightM, trackIndex })
+ if (points.length < 2) return
+ const positions = points.map(point =>
+ Cesium.Cartesian3.fromDegrees(point.longitude, point.latitude, point.height)
+ )
+ let trackMaterial = createDroneTrackMaterial({
+ color: baseTrackColor,
+ speed: 4.5,
+ headWidth: 0.2,
+ glowPower: 1.8,
+ backgroundAlpha: 0.34,
+ })
+ if (!trackMaterial) {
+ trackMaterial = Cesium.Material.fromType('Color', { color: baseTrackColor })
+ }
+ droneTrackPolylineCollection.add({
+ positions,
+ width: 3,
+ material: trackMaterial,
+ })
+ const droneId = `drone-alarm-${item?.alarmRecordId ?? item?.id ?? trackIndex}`
+ const billboard = droneTrackBillboardCollection.add({
+ position: positions[positions.length - 1],
+ image: droneIcon,
+ width: 36,
+ height: 36,
+ verticalOrigin: Cesium.VerticalOrigin.CENTER,
+ disableDepthTestDistance: Number.POSITIVE_INFINITY,
+ })
+ billboard.id = droneId
+ const speedMs = Math.round(Cesium.Cartesian3.distance(positions[0], positions[1]) / segmentDuration)
dronePickMap.set(droneId, {
data: {
- droneName: `无人机-${trackIndex + 1}`,
- droneSerialNo: `UAV-${trackIndex + 1}`,
- flightHeightM: points[0].height,
- flightSpeedMs: speedMs,
- longitude: points[0].longitude,
- latitude: points[0].latitude,
- flightStatus: 1,
- deviceName: centerName,
- dataSource: centerName,
- counterDeviceName: centerName,
+ ...item,
+ flightHeightM: item.flightHeightM ?? points[0].height,
+ flightSpeedMs: item.flightSpeedMs ?? speedMs,
+ longitude: item.longitude ?? points[0].longitude,
+ latitude: item.latitude ?? points[0].latitude,
},
billboard,
})
- droneTrackRuntime.push({
- positions,
- billboard,
- segmentDuration,
- duration: (points.length - 1) * segmentDuration,
+ if (isFavorited(item)) {
+ createFavoritePulseEntity(billboard)
+ }
+ droneTrackRuntime.push({
+ positions,
+ billboard,
+ segmentDuration,
+ duration: (points.length - 1) * segmentDuration,
+ })
})
- })
viewer.clock.multiplier = 1
startDroneTrackAnimation()
+ startFavoritePulseAnimation()
reorderCockpitPrimitives()
}
@@ -629,34 +796,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 +899,7 @@
viewer.dataSources.add(aggregationSource)
}
clearAggregationEntities()
- aggregationSource.show = clusterVisible.value
+ updateAggregationVisibility()
const countMap = new Map()
; (list || []).forEach(item => {
if (!item?.type) return
@@ -839,9 +1006,9 @@
const popupPosition = ref({ x: 0, y: 0 })
const popupVisible = computed(() => Boolean(selectedDevice.value))
const isDronePopup = computed(() => selectedTargetType.value === 'drone')
-const toggleDroneFavorite = () => {
+const handleDroneFavorite = () => {
if (!selectedDevice.value) return
- selectedDevice.value.isFavorite = !selectedDevice.value.isFavorite
+ emit('droneFavorite', selectedDevice.value)
}
const handleDroneSignal = () => emit('droneSignal', selectedDevice.value)
const handleDroneCounter = () => emit('droneCounter', selectedDevice.value)
@@ -851,17 +1018,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()
}
@@ -895,6 +1062,19 @@
)
watch(
+ () => props.alarmDrones,
+ list => {
+ renderSimulatedDroneTrack(list || [])
+ if (selectedTargetType.value === 'drone' && selectedDevice.value) {
+ const selectedId = selectedDevice.value.alarmRecordId ?? selectedDevice.value.id
+ const match = (list || []).find(item => (item?.alarmRecordId ?? item?.id) === selectedId)
+ if (match) selectedDevice.value = { ...selectedDevice.value, ...match }
+ }
+ },
+ { deep: true }
+)
+
+watch(
() => props.leftCollapsed,
isCollapsed => {
if (isCollapsed) showLayerPanel.value = false
@@ -906,6 +1086,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 +1116,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)
@@ -924,7 +1129,7 @@
loadPartitions()
loadAggregation()
loadCommandPosts()
- renderSimulatedDroneTrack()
+ renderSimulatedDroneTrack(props.alarmDrones)
initDeviceClickHandler()
}
@@ -960,6 +1165,7 @@
}
cockpitPrimitiveLayer = null
viewer = null
+ publicCesium = null
})
</script>
@@ -1041,6 +1247,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>
-
--
Gitblit v1.9.3