From cb43af5c2caf2184bbf1a8e417f44e9aeaa2fdd9 Mon Sep 17 00:00:00 2001
From: shuishen <1109946754@qq.com>
Date: Thu, 29 Jan 2026 13:38:20 +0800
Subject: [PATCH] feat:增加设备特效椭球

---
 applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue |  139 +++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 137 insertions(+), 2 deletions(-)

diff --git a/applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue b/applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue
index c253bbf..f1a0cd1 100644
--- a/applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue
+++ b/applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue
@@ -116,7 +116,7 @@
 					<div class="command-table-container">
 						<div class="command-table-content">
 							<el-table class="command-table" ref="deviceTableRef" :data="deviceOptions"
-								row-key="id">
+								row-key="id" @row-click="handleDeviceRowClick">
 								<el-table-column prop="deviceName" label="设备名称" />
 								<el-table-column prop="deviceType" label="类型">
 									<template v-slot="{ row }">
@@ -215,7 +215,8 @@
 						<el-form-item prop="deviceIds" label-width="0">
 							<el-table class="command-table" ref="deviceTableRef"
 								:data="deviceOptions.filter(item => item.deviceName.includes(searchName))" row-key="id"
-								@selection-change="handleDeviceSelectionChange">
+								@selection-change="handleDeviceSelectionChange"
+								@row-click="handleDeviceRowClick">
 								<el-table-column type="selection" width="55" :reserve-selection="true" />
 								<el-table-column prop="deviceName" label="设备名称" />
 								<el-table-column prop="deviceType" label="类型">
@@ -323,6 +324,8 @@
 let pointList = []
 let viewEntity
 let shapeDisplaySource
+const deviceRangePrimitiveMap = new Map()
+let deviceRangeMaterialRegistered = false
 const activeToolMode = ref('')
 
 const rules = {
@@ -424,6 +427,7 @@
 			clearActiveTool()
 			hideTypePanel()
 			clearShapeDisplay()
+			clearDeviceRangePrimitives()
 		}
 	}
 )
@@ -468,6 +472,124 @@
 		viewer.dataSources.add(shapeDisplaySource)
 	}
 	return shapeDisplaySource
+}
+
+function registerDeviceRangeMaterial () {
+	if (deviceRangeMaterialRegistered || !Cesium?.Material) return
+	deviceRangeMaterialRegistered = true
+	Cesium.Material._materialCache.addMaterial('DeviceRangeEllipsoid', {
+		fabric: {
+			type: 'DeviceRangeEllipsoid',
+			uniforms: {
+				color: new Cesium.Color(1.0, 0.0, 0.0, 0.7),
+				speed: 3.0,
+			},
+			source: `
+				uniform vec4 color;
+				uniform float speed;
+				czm_material czm_getMaterial(czm_materialInput materialInput){
+					czm_material material = czm_getDefaultMaterial(materialInput);
+					vec2 st = materialInput.st;
+					float time = fract(czm_frameNumber * speed / 1000.0);
+					float alpha = abs(smoothstep(0.5,1.,fract( -st.t - time)));
+					alpha += .1;
+					material.alpha = alpha;
+					material.diffuse = color.rgb;
+					return material;
+				}
+			`,
+		},
+		translucent: () => true,
+	})
+}
+
+function createDeviceRangeMaterial () {
+	registerDeviceRangeMaterial()
+	return Cesium.Material.fromType('DeviceRangeEllipsoid', {
+		color: Cesium.Color.fromBytes(0, 255, 255, 155),
+	})
+}
+
+function clearDeviceRangePrimitives () {
+	if (!viewer || !deviceRangePrimitiveMap.size) return
+	for (const primitive of deviceRangePrimitiveMap.values()) {
+		viewer.scene.primitives.remove(primitive)
+	}
+	deviceRangePrimitiveMap.clear()
+}
+
+function normalizeDeviceRange (value) {
+	const range = Number(value)
+	if (!Number.isFinite(range) || range <= 0) return 2000
+	return range
+}
+
+function getDeviceLngLat (device) {
+	const lngRaw = device?.longitude ?? device?.lng ?? device?.lon
+	const latRaw = device?.latitude ?? device?.lat
+	const lng = Number(lngRaw)
+	const lat = Number(latRaw)
+	if (!Number.isFinite(lng) || !Number.isFinite(lat)) return null
+	return { lng, lat }
+}
+
+function createDeviceRangePrimitive (device) {
+	if (!viewer) return null
+	if (!device) return null
+	const position = getDeviceLngLat(device)
+	if (!position) return null
+	const rangeMeters = normalizeDeviceRange(device.effectiveRangeKm)
+	const center = Cesium.Cartesian3.fromDegrees(position.lng, position.lat, 0)
+	const radii = new Cesium.Cartesian3(rangeMeters, rangeMeters, rangeMeters)
+	const geometry = new Cesium.EllipsoidGeometry({
+		radii,
+		vertexFormat: Cesium.MaterialAppearance.MaterialSupport.TEXTURED?.vertexFormat
+			|| Cesium.VertexFormat.POSITION_AND_NORMAL,
+	})
+	const instance = new Cesium.GeometryInstance({
+		geometry,
+		modelMatrix: Cesium.Transforms.eastNorthUpToFixedFrame(center),
+	})
+	return new Cesium.Primitive({
+		geometryInstances: instance,
+		appearance: new Cesium.MaterialAppearance({
+			material: createDeviceRangeMaterial(),
+			closed: true,
+			translucent: true,
+			flat: true,
+			faceForward: true,
+		}),
+		asynchronous: false,
+	})
+}
+
+function renderDeviceRangeSingle (device) {
+	if (!viewer) return
+	clearDeviceRangePrimitives()
+	const primitive = createDeviceRangePrimitive(device)
+	if (!primitive) return
+	deviceRangePrimitiveMap.set(String(device?.id ?? Date.now()), primitive)
+	viewer.scene.primitives.add(primitive)
+}
+
+function renderDeviceRanges (rows) {
+	if (!viewer) return
+	const selectedRows = Array.isArray(rows) ? rows : []
+	const nextIds = new Set(selectedRows.map(row => String(row?.id)))
+	for (const [id, primitive] of deviceRangePrimitiveMap.entries()) {
+		if (!nextIds.has(id)) {
+			viewer.scene.primitives.remove(primitive)
+			deviceRangePrimitiveMap.delete(id)
+		}
+	}
+	selectedRows.forEach(row => {
+		const id = String(row?.id)
+		if (!id || deviceRangePrimitiveMap.has(id)) return
+		const primitive = createDeviceRangePrimitive(row)
+		if (!primitive) return
+		deviceRangePrimitiveMap.set(id, primitive)
+		viewer.scene.primitives.add(primitive)
+	})
 }
 
 function clearShapeDisplay () {
@@ -922,6 +1044,15 @@
 	selectedDeviceRows.value = rows
 	const ids = rows.map(item => item.id)
 	formData.value.deviceIds = ids.join(',')
+	renderDeviceRanges(rows)
+}
+
+function handleDeviceRowClick (row) {
+	if (readonly.value) {
+		renderDeviceRangeSingle(row)
+		return
+	}
+	renderDeviceRanges(selectedDeviceRows.value)
 }
 
 // 同步选择状态
@@ -949,6 +1080,7 @@
 	await nextTick()
 	initMap()
 	clearShapeDisplay()
+	clearDeviceRangePrimitives()
 	viewEntity && viewer?.entities?.remove(viewEntity)
 	viewEntity = null
 	clearActiveTool()
@@ -976,6 +1108,9 @@
 	}
 	await nextTick()
 	syncDeviceSelection()
+	if (!readonly.value) {
+		renderDeviceRanges(selectedDeviceRows.value)
+	}
 }
 
 onMounted(() => {

--
Gitblit v1.9.3