From 6539b3c20d9eba747bdd429f532469ccd4491a26 Mon Sep 17 00:00:00 2001
From: shuishen <1109946754@qq.com>
Date: Sat, 17 Jan 2026 13:52:51 +0800
Subject: [PATCH] feat:数据驾驶舱相关显示处理

---
 applications/drone-command/src/components/map-container/device-map-container.vue |  243 ++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 234 insertions(+), 9 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 44cf467..704eaf9 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
@@ -26,6 +26,10 @@
 <script setup>
 import * as Cesium from 'cesium'
 import { PublicCesium } from '@/utils/cesium/publicCesium'
+import { geomAnalysis } from '@ztzf/utils'
+import { fwDefenseZonePageApi } from '@/views/areaManage/defenseZone/defenseZoneApi'
+import { fwAreaDividePageApi } from '@/views/areaManage/partition/partitionApi'
+import jaGeojsonRaw from '@/assets/geojson/ja.geojson?raw'
 import layerControlIcon from '@/assets/images/dataCockpit/layerControl.png'
 import equipmentIcon from '@/assets/images/dataCockpit/map/equipment.png'
 
@@ -48,9 +52,17 @@
 	},
 })
 
+const DEFAULT_ZONE_PAGE_SIZE = 999
+
 let viewInstance = null
 let viewer = null
 const deviceEntityIds = new Set()
+let defenseZoneSource = null
+let partitionSource = null
+let adminBoundarySource = null
+let adminBoundaryLabelSource = null
+let adminBoundaryLineSource = null
+let adminBoundaryFlyDone = false
 const showLayerPanel = ref(false)
 const layerWrapRef = ref(null)
 const layerTreeProps = {
@@ -105,10 +117,33 @@
 	deviceEntityIds.clear()
 }
 
+const clearDefenseZoneEntities = () => {
+	if (!defenseZoneSource) return
+	defenseZoneSource.entities.removeAll()
+}
+
+const clearPartitionEntities = () => {
+	if (!partitionSource) return
+	partitionSource.entities.removeAll()
+}
+
+const clearAdminBoundaryEntities = () => {
+	if (!adminBoundarySource) return
+	adminBoundarySource.entities.removeAll()
+}
+
+const clearAdminBoundaryLabels = () => {
+	if (!adminBoundaryLabelSource) return
+	adminBoundaryLabelSource.entities.removeAll()
+}
+
+const clearAdminBoundaryLines = () => {
+	if (!adminBoundaryLineSource) return
+	adminBoundaryLineSource.entities.removeAll()
+}
+
 const RING_STYLES = [
-	{ inner: 0, outer: 5000, gradient: ['#FF361C', '#360B00'] },
-	{ inner: 5000, outer: 8000, gradient: ['#FFC609', '#583300'] },
-	{ inner: 8000, outer: 10000, gradient: ['#2AEDBF', '#012B11'] },
+	{ inner: 0, outer: 2000, gradient: ['#FF361C', '#360B00'] }
 ]
 
 const MATERIAL_TYPE = 'RadialGradientMaterial'
@@ -235,6 +270,180 @@
 	})
 }
 
+const getDefenseZonePositions = geom => {
+	const points = geomAnalysis(geom)
+	if (points.length < 3) return []
+	const first = points[0]
+	const last = points[points.length - 1]
+	const list =
+		first && last && first.longitude === last.longitude && first.latitude === last.latitude
+			? points.slice(0, -1)
+			: points
+	return list.map(item => Cesium.Cartesian3.fromDegrees(item.longitude, item.latitude))
+}
+
+const renderZonePolygons = ({ zones, source, idPrefix, lineColor, fillGradient }) => {
+	if (!viewer || !source) return
+	registerRadialGradientMaterial()
+	const borderColor = Cesium.Color.fromCssColorString(lineColor)
+	const fillColorStart = Cesium.Color.fromCssColorString(fillGradient[0]).withAlpha(0.34)
+	const fillColorEnd = Cesium.Color.fromCssColorString(fillGradient[1]).withAlpha(0.34)
+	const fillMaterial = new RadialGradientMaterialProperty(fillColorStart, fillColorEnd)
+	zones.forEach((zone, index) => {
+		if (!zone?.geom) return
+		const positions = getDefenseZonePositions(zone.geom)
+		if (!positions.length) return
+		const entityId = `${idPrefix}-${zone.id ?? index}`
+		const linePositions = positions.length > 1 ? [...positions, positions[0]] : positions
+		source.entities.add({
+			id: entityId,
+			polygon: {
+				hierarchy: new Cesium.PolygonHierarchy(positions),
+				material: fillMaterial,
+				outline: true,
+				outlineColor: borderColor,
+				heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
+			},
+			polyline: {
+				positions: linePositions,
+				clampToGround: true,
+				width: 2,
+				material: borderColor,
+			},
+		})
+	})
+}
+
+const renderDefenseZones = zones => {
+	if (!viewer) return
+	if (!defenseZoneSource) {
+		defenseZoneSource = new Cesium.CustomDataSource('defenseZoneSource')
+		viewer.dataSources.add(defenseZoneSource)
+	}
+	clearDefenseZoneEntities()
+	renderZonePolygons({
+		zones,
+		source: defenseZoneSource,
+		idPrefix: 'defense-zone',
+		lineColor: '#19D266',
+		fillGradient: ['#2AEDBF', '#012B11'],
+	})
+}
+
+const renderPartitions = zones => {
+	if (!viewer) return
+	if (!partitionSource) {
+		partitionSource = new Cesium.CustomDataSource('partitionSource')
+		viewer.dataSources.add(partitionSource)
+	}
+	clearPartitionEntities()
+	renderZonePolygons({
+		zones,
+		source: partitionSource,
+		idPrefix: 'partition-zone',
+		lineColor: '#FFCD2A',
+		fillGradient: ['#FFC609', '#583300'],
+	})
+}
+
+const loadDefenseZones = async () => {
+	if (!viewer) return
+	try {
+		const res = await fwDefenseZonePageApi({ current: 1, size: DEFAULT_ZONE_PAGE_SIZE })
+		renderDefenseZones(res?.data?.data?.records ?? [])
+	} catch (error) {
+		renderDefenseZones([])
+	}
+}
+
+const loadPartitions = async () => {
+	if (!viewer) return
+	try {
+		const res = await fwAreaDividePageApi({ current: 1, size: DEFAULT_ZONE_PAGE_SIZE })
+		renderPartitions(res?.data?.data?.records ?? [])
+	} catch (error) {
+		renderPartitions([])
+	}
+}
+
+const loadAdminBoundary = async () => {
+	if (!viewer) return
+	try {
+		if (adminBoundarySource) {
+			viewer.dataSources.remove(adminBoundarySource)
+			adminBoundarySource = null
+		}
+		if (adminBoundaryLabelSource) {
+			viewer.dataSources.remove(adminBoundaryLabelSource)
+			adminBoundaryLabelSource = null
+		}
+		if (adminBoundaryLineSource) {
+			viewer.dataSources.remove(adminBoundaryLineSource)
+			adminBoundaryLineSource = null
+		}
+		const geojson = JSON.parse(jaGeojsonRaw)
+		adminBoundarySource = await Cesium.GeoJsonDataSource.load(geojson, {
+			stroke: Cesium.Color.fromCssColorString('#FFFFFF'),
+			strokeWidth: 2,
+			fill: Cesium.Color.fromCssColorString('#FFFFFF').withAlpha(0.05),
+			clampToGround: true,
+		})
+		viewer.dataSources.add(adminBoundarySource)
+		if (!adminBoundaryFlyDone) {
+			adminBoundaryFlyDone = true
+			viewer.flyTo(adminBoundarySource, {
+				duration: 0,
+				offset: new Cesium.HeadingPitchRange(0, Cesium.Math.toRadians(-75), 0),
+			})
+		}
+
+		adminBoundaryLineSource = new Cesium.CustomDataSource('adminBoundaryLineSource')
+		adminBoundarySource.entities.values.forEach(entity => {
+			const polygon = entity.polygon
+			if (!polygon) return
+			const hierarchy = polygon.hierarchy?.getValue?.(viewer.clock.currentTime)
+			const positions = hierarchy?.positions
+			if (!positions?.length) return
+			adminBoundaryLineSource.entities.add({
+				polyline: {
+					positions,
+					clampToGround: true,
+					width: 2,
+					material: Cesium.Color.WHITE,
+				},
+			})
+		})
+		viewer.dataSources.add(adminBoundaryLineSource)
+
+		adminBoundaryLabelSource = new Cesium.CustomDataSource('adminBoundaryLabelSource')
+		geojson.features?.forEach(feature => {
+			const name = feature?.properties?.name
+			const point = feature?.properties?.centroid || feature?.properties?.center
+			if (!name || !Array.isArray(point) || point.length < 2) return
+			adminBoundaryLabelSource.entities.add({
+				position: Cesium.Cartesian3.fromDegrees(point[0], point[1]),
+				label: {
+					text: name,
+					font: '14px Source Han Sans CN',
+					fillColor: Cesium.Color.WHITE,
+					outlineColor: Cesium.Color.BLACK.withAlpha(0.6),
+					outlineWidth: 2,
+					style: Cesium.LabelStyle.FILL_AND_OUTLINE,
+					heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
+					verticalOrigin: Cesium.VerticalOrigin.CENTER,
+					horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
+					disableDepthTestDistance: Number.POSITIVE_INFINITY,
+				},
+			})
+		})
+		viewer.dataSources.add(adminBoundaryLabelSource)
+	} catch (error) {
+		clearAdminBoundaryEntities()
+		clearAdminBoundaryLabels()
+		clearAdminBoundaryLines()
+	}
+}
+
 watch(
 	() => props.onlineDevices,
 	devices => {
@@ -274,11 +483,19 @@
 
 	viewer = viewInstance.getViewer()
 	renderDeviceEntities(props.onlineDevices)
+	loadDefenseZones()
+	loadPartitions()
+	loadAdminBoundary()
 })
 
 onBeforeUnmount(() => {
 	document.removeEventListener('click', handleClickOutside)
 	clearDeviceEntities()
+	clearDefenseZoneEntities()
+	clearPartitionEntities()
+	clearAdminBoundaryEntities()
+	clearAdminBoundaryLabels()
+	clearAdminBoundaryLines()
 	if (viewInstance) {
 		viewInstance?.viewerDestroy()
 		viewInstance = null
@@ -299,14 +516,14 @@
 
 .layer-control-root {
 	position: absolute;
-	left: 411px;
+	left: 337px;
 	bottom: 22px;
 	z-index: 9;
 	transition: transform 0.3s ease-in-out;
 	pointer-events: none;
 
 	&.collapsed {
-		transform: translateX(-401px);
+		transform: translateX(-317px);
 	}
 }
 
@@ -318,8 +535,8 @@
 }
 
 .layer-control {
-	width: 34px;
-	height: 34px;
+	width: 46px;
+	height: 46px;
 	cursor: pointer;
 
 	img {
@@ -333,7 +550,7 @@
 	display: flex;
 	flex-direction: column;
 	position: absolute;
-	left: 46px;
+	left: 66px;
 	bottom: 0;
 	width: 160px;
 	max-height: 442px;
@@ -370,6 +587,7 @@
 			line-height: 30px !important;
 
 			.el-tree-node__content {
+				padding-left: 0 !important;
 				display: flex;
 				align-items: center;
 				height: 40px !important;
@@ -381,6 +599,12 @@
 			.el-tree-node__children {
 				.el-tree-node__content {
 					border: none;
+				}
+			}
+
+			&:focus {
+				.el-tree-node__content {
+					background: transparent !important;
 				}
 			}
 		}
@@ -412,7 +636,8 @@
 	display: none;
 }
 
-.layer-panel :deep(.el-tree-node__content:hover) {
+.layer-panel :deep(.el-tree-node__content:hover),
+.layer-panel :deep(.el-tree-node__content:focus) {
 	background: transparent !important;
 }
 

--
Gitblit v1.9.3