From dc89af73e55f7bda6018bace4813a4107b5248d4 Mon Sep 17 00:00:00 2001
From: shuishen <1109946754@qq.com>
Date: Mon, 26 Jan 2026 19:11:33 +0800
Subject: [PATCH] feat:数据驾驶舱等页面调整

---
 applications/drone-command/src/components/map-container/device-map-container.vue                         |  119 ++++++++++++++++++++++-------
 applications/drone-command/src/views/dataCockpit/components/LegendBar.vue                                |    8 +-
 applications/drone-command/src/views/detectionCountermeasure/detectionRange/DetectionRangeDialog.vue     |   28 ++++++-
 applications/drone-command/src/components/map-container/device-map-materials.js                          |   40 +++++++--
 applications/drone-command/src/views/dataCockpit/components/templateComponents/RealEquipmentTemplate.vue |    2 
 applications/drone-command/src/views/detectionCountermeasure/detectionRange/index.vue                    |    6 +
 applications/drone-command/src/api/dataCockpit/index.js                                                  |    5 
 7 files changed, 155 insertions(+), 53 deletions(-)

diff --git a/applications/drone-command/src/api/dataCockpit/index.js b/applications/drone-command/src/api/dataCockpit/index.js
index f08ac7f..68024bd 100644
--- a/applications/drone-command/src/api/dataCockpit/index.js
+++ b/applications/drone-command/src/api/dataCockpit/index.js
@@ -2,7 +2,7 @@
  * @Author       : yuan
  * @Date         : 2026-01-09 11:14:04
  * @LastEditors  : yuan
- * @LastEditTime : 2026-01-26 09:38:16
+ * @LastEditTime : 2026-01-26 19:02:07
  * @FilePath     : \applications\drone-command\src\api\dataCockpit\index.js
  * @Description  : 
  * Copyright 2026 OBKoro1, All Rights Reserved. 
@@ -78,7 +78,8 @@
 export const cockpitAggregationApi = (params) => {
 	return request({
 		url: '/drone-fw/device/fwDevice/statisticalDeviceOut',
-		method: 'get'
+		method: 'get',
+		params
 	})
 }
 
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 a8f8566..43b373e 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
@@ -2,8 +2,9 @@
 	<div class="map-shell">
 		<CommonCesiumMap ref="mapRef" class="command-cesium map-container" :dom-id="props.containerId" :active="true"
 			:flat-mode="false" :terrain="true" :layer-mode="4" :contour="false" :boundary="false"
-			:show-admin-boundary="true" :zoom-to-boundary="true" :enable-stage-emit="true" :cluster-height="100000"
-			@ready="handleMapReady" @stage-change="handleStageChange" />
+			: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 class="layer-control-wrap" ref="layerWrapRef">
 				<div class="layer-control" @click="toggleLayerPanel">
@@ -63,6 +64,9 @@
 	createRadialGradientMaterial,
 	getTexturedVertexFormat,
 } from './device-map-materials'
+
+const CLUSTER_HEIGHT = 100000
+const DETAIL_HEIGHT = 10000
 
 const props = defineProps({
 	onlineDevices: {
@@ -157,6 +161,14 @@
 	const latitude = Number(latitudeRaw)
 	if (!Number.isFinite(longitude) || !Number.isFinite(latitude)) return null
 	return { longitude, latitude }
+}
+
+const getDeviceRange = item => {
+	if (!item) return null
+	const rawRange = item.effectiveRangeKm ?? item.range ?? item.coverRadiusM
+	const range = Number(rawRange)
+	if (!Number.isFinite(range) || range <= 0) return null
+	return range
 }
 
 const ensureCockpitPrimitiveLayer = () => {
@@ -305,6 +317,20 @@
 	if (aggregationSource) aggregationSource.show = visible
 }
 
+const setDroneVisibility = visible => {
+	if (droneTrackBillboardCollection) droneTrackBillboardCollection.show = visible
+	if (droneTrackPolylineCollection) droneTrackPolylineCollection.show = visible
+	if (!visible && selectedTargetType.value === 'drone') {
+		closePopup()
+	}
+}
+const getStageByHeight = height => {
+	if (height == null) return 'detail'
+	if (height >= CLUSTER_HEIGHT) return 'cluster'
+	if (height <= DETAIL_HEIGHT) return 'detail'
+	return 'mid'
+}
+
 const ensureCountyCenterMap = () => {
 	if (countyCenterMap.size) return
 	const geojson = JSON.parse(jaGeojsonRaw)
@@ -316,24 +342,44 @@
 	})
 }
 
-const buildSimulatedTrackPoints = base => {
-	ensureCountyCenterMap()
-	const anchor = base || { longitude: 116.397, latitude: 39.908 }
-	const offsets = [
-		[0, 0, 120],
-		[0.02, 0.012, 150],
-		[0.04, 0.006, 180],
-		[0.06, -0.008, 200],
-		[0.03, -0.02, 160],
-		[0.0, -0.015, 140],
-		[-0.02, -0.005, 120],
-		[-0.01, 0.01, 130],
+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
+		},
 	]
-	return offsets.map(item => ({
-		longitude: anchor.longitude + item[0],
-		latitude: anchor.latitude + item[1],
-		height: item[2],
-	}))
 }
 
 
@@ -422,13 +468,11 @@
 const renderSimulatedDroneTrack = () => {
 	if (!viewer) return
 	clearDroneTrackEntities()
-	ensureCountyCenterMap()
 	ensureDroneTrackCollections()
+	droneTrackBillboardCollection.show = detailVisible.value
+	droneTrackPolylineCollection.show = detailVisible.value
 	droneTrackRuntime = []
-	const centers = Array.from(countyCenterMap.entries())
-	const baseCenters = centers.length
-		? centers.slice(0, 4)
-		: [['默认区域', { longitude: 116.397, latitude: 39.908 }]]
+	const baseCenters = [['默认区域', { longitude:114.958541, latitude: 27.121917 }]]
 	const segmentDuration = 6
 	const baseTrackColor = Cesium.Color.fromCssColorString('red')
 	baseCenters.forEach(([centerName, center], trackIndex) => {
@@ -454,7 +498,7 @@
 		})
 		const droneId = `drone-track-${trackIndex}`
 		const billboard = droneTrackBillboardCollection.add({
-			position: positions[0],
+			position: positions[positions.length - 1],
 			image: droneIcon,
 			width: 36,
 			height: 36,
@@ -511,7 +555,8 @@
 		const position = getDevicePosition(item)
 		if (!position) return
 		const entityId = `online-device-${item.id ?? index}-${index}`
-		addDeviceRings(position, ringFillInstancesByStyle, ringOutlineInstancesByStyle)
+		const rangeMeters = getDeviceRange(item)
+		addDeviceRings(position, ringFillInstancesByStyle, ringOutlineInstancesByStyle, rangeMeters)
 		const billboard = deviceBillboardCollection.add({
 			position: Cesium.Cartesian3.fromDegrees(position.longitude, position.latitude, 0),
 			image: equipmentIcon,
@@ -528,9 +573,20 @@
 	ringFillInstancesByStyle.forEach((instances, index) => {
 		if (!instances.length) return
 		const ring = RING_STYLES[index]
+		const alpha = ring.alpha ?? 0.64
+		const innerRatio =
+			typeof ring.innerRatio === 'number'
+				? ring.innerRatio
+				: ring.inner && ring.outer
+					? Math.min(Math.max(ring.inner / ring.outer, 0), 0.9)
+					: 0
 		const material = createRadialGradientMaterial(
-			Cesium.Color.fromCssColorString(ring.gradient[0]).withAlpha(0.64),
-			Cesium.Color.fromCssColorString(ring.gradient[1]).withAlpha(0.64)
+			Cesium.Color.fromCssColorString(ring.gradient[0]).withAlpha(alpha),
+			Cesium.Color.fromCssColorString(ring.gradient[1]).withAlpha(alpha),
+			{
+				gamma: 1.7,
+				innerCutoff: innerRatio,
+			}
 		)
 		const primitive = new Cesium.GroundPrimitive({
 			geometryInstances: instances,
@@ -629,7 +685,7 @@
 const renderDefenseZones = zones => {
 	if (!viewer) return
 	clearDefenseZoneEntities()
-	const result = buildZonePrimitives(zones, '#19D266', '#2AEDBF', '#012B11')
+	const result = buildZonePrimitives(zones, '#72F3FF', '#72F3FF', '#0A7C88')
 	defenseZonePrimitive = result.primitive
 	defenseZoneOutlinePrimitive = result.outlinePrimitive
 	if (defenseZonePrimitive) defenseZonePrimitive.show = detailVisible.value
@@ -640,7 +696,7 @@
 const renderPartitions = zones => {
 	if (!viewer) return
 	clearPartitionEntities()
-	const result = buildZonePrimitives(zones, '#FFCD2A', '#FFC609', '#583300')
+	const result = buildZonePrimitives(zones, '#FFD772', '#FFD772', '#A86B00')
 	partitionPrimitive = result.primitive
 	partitionOutlinePrimitive = result.outlinePrimitive
 	if (partitionPrimitive) partitionPrimitive.show = detailVisible.value
@@ -855,13 +911,14 @@
 	const showCluster = stage === 'cluster'
 	setClusterVisibility(showCluster)
 	setDetailVisibility(!showCluster)
+	setDroneVisibility(!showCluster)
 }
 
 const handleMapReady = ({ viewer: mapViewer }) => {
 	viewer = mapViewer
 	ensureCockpitPrimitiveLayer()
 	const height = viewer?.camera?.positionCartographic?.height
-	const stage = height != null && height >= 100000 ? 'cluster' : 'detail'
+	const stage = getStageByHeight(height)
 	updateStageDisplay(stage)
 	renderDeviceEntities(props.onlineDevices)
 	loadDefenseZones()
diff --git a/applications/drone-command/src/components/map-container/device-map-materials.js b/applications/drone-command/src/components/map-container/device-map-materials.js
index bf2edc8..6177bb7 100644
--- a/applications/drone-command/src/components/map-container/device-map-materials.js
+++ b/applications/drone-command/src/components/map-container/device-map-materials.js
@@ -1,6 +1,15 @@
 import * as Cesium from 'cesium'
 
-export const RING_STYLES = [{ inner: 0, outer: 2000, gradient: ['#FF361C', '#360B00'] }]
+export const RING_STYLES = [
+	{
+		inner: 0,
+		outer: 2000,
+		gradient: ['#FF6A57', '#A11806'],
+		alpha: 0.42,
+		outlineAlpha: 0.85,
+		innerRatio: 0.12,
+	},
+]
 
 export const MATERIAL_TYPE = 'RadialGradientMaterial'
 export const DRONE_TRACK_MATERIAL_TYPE = 'DroneTrackFlowMaterial'
@@ -26,15 +35,25 @@
 			uniforms: {
 				color1: new Cesium.Color(1.0, 1.0, 1.0, 1.0),
 				color2: new Cesium.Color(0.0, 0.0, 0.0, 1.0),
+				gamma: 1.7,
+				innerCutoff: 0.0,
 			},
 			source: `
+				uniform vec4 color1;
+				uniform vec4 color2;
+				uniform float gamma;
+				uniform float innerCutoff;
+
 				czm_material czm_getMaterial(czm_materialInput materialInput) {
 					czm_material material = czm_getDefaultMaterial(materialInput);
 					vec2 st = materialInput.st - vec2(0.5);
 					float t = clamp(length(st) * 2.0, 0.0, 1.0);
 					vec4 color = mix(color1, color2, t);
+					float ramp = smoothstep(innerCutoff, 1.0, t);
+					float shaped = pow(t, gamma);
+					float alpha = color.a * ramp * shaped;
 					material.diffuse = color.rgb;
-					material.alpha = color.a;
+					material.alpha = alpha;
 					return material;
 				}
 			`,
@@ -82,9 +101,9 @@
 	})
 }
 
-export const createRadialGradientMaterial = (color1, color2) => {
+export const createRadialGradientMaterial = (color1, color2, options = {}) => {
 	registerRadialGradientMaterial()
-	return Cesium.Material.fromType(MATERIAL_TYPE, { color1, color2 })
+	return Cesium.Material.fromType(MATERIAL_TYPE, { color1, color2, ...(options || {}) })
 }
 
 export const createDroneTrackMaterial = options => {
@@ -115,18 +134,19 @@
 	return positions
 }
 
-export const addDeviceRings = (center, ringFillInstancesByStyle, ringOutlineInstancesByStyle) => {
+export const addDeviceRings = (center, ringFillInstancesByStyle, ringOutlineInstancesByStyle, rangeMeters) => {
 	if (!ringFillInstancesByStyle) return
 	const centerPosition = Cesium.Cartesian3.fromDegrees(center.longitude, center.latitude, 0)
 	const vertexFormat = getTexturedVertexFormat()
 	RING_STYLES.forEach((ring, index) => {
 		if (!ringFillInstancesByStyle[index]) return
+		const outer = Number.isFinite(rangeMeters) && rangeMeters > 0 ? rangeMeters : ring.outer
 		ringFillInstancesByStyle[index].push(
 			new Cesium.GeometryInstance({
 				geometry: new Cesium.EllipseGeometry({
 					center: centerPosition,
-					semiMajorAxis: ring.outer,
-					semiMinorAxis: ring.outer,
+					semiMajorAxis: outer,
+					semiMinorAxis: outer,
 					vertexFormat,
 				}),
 			})
@@ -135,7 +155,9 @@
 	if (!ringOutlineInstancesByStyle) return
 	RING_STYLES.forEach((ring, index) => {
 		if (!ringOutlineInstancesByStyle[index]) return
-		const positions = buildCirclePositions(center, ring.outer)
+		const outer = Number.isFinite(rangeMeters) && rangeMeters > 0 ? rangeMeters : ring.outer
+		const positions = buildCirclePositions(center, outer)
+		const outlineAlpha = typeof ring.outlineAlpha === 'number' ? ring.outlineAlpha : 0.8
 		ringOutlineInstancesByStyle[index].push(
 			new Cesium.GeometryInstance({
 				geometry: new Cesium.GroundPolylineGeometry({
@@ -144,7 +166,7 @@
 				}),
 				attributes: {
 					color: Cesium.ColorGeometryInstanceAttribute.fromColor(
-						Cesium.Color.fromCssColorString(ring.gradient[0]).withAlpha(0.8)
+						Cesium.Color.fromCssColorString(ring.gradient[0]).withAlpha(outlineAlpha)
 					),
 				},
 			})
diff --git a/applications/drone-command/src/views/dataCockpit/components/LegendBar.vue b/applications/drone-command/src/views/dataCockpit/components/LegendBar.vue
index d115225..52535aa 100644
--- a/applications/drone-command/src/views/dataCockpit/components/LegendBar.vue
+++ b/applications/drone-command/src/views/dataCockpit/components/LegendBar.vue
@@ -85,13 +85,13 @@
 }
 
 .legend-color.defense {
-	background: radial-gradient(50% 50% at 50% 50%, #2aedbf 0%, #012b11 100%);
-	border: 1px solid #19d266;
+	background: radial-gradient(50% 50% at 50% 50%, rgba(114, 243, 255, 0.45) 0%, rgba(10, 124, 136, 0.55) 100%);
+	border: 1px solid rgba(114, 243, 255, 0.85);
 }
 
 .legend-color.partition {
-	background: radial-gradient(50% 50% at 50% 50%, #ffc609 0%, #583300 100%);
-	border: 1px solid #ffcd2a;
+	background: radial-gradient(50% 50% at 50% 50%, rgba(255, 215, 114, 0.5) 0%, rgba(168, 107, 0, 0.6) 100%);
+	border: 1px solid rgba(255, 215, 114, 0.9);
 }
 
 .legend-icon {
diff --git a/applications/drone-command/src/views/dataCockpit/components/templateComponents/RealEquipmentTemplate.vue b/applications/drone-command/src/views/dataCockpit/components/templateComponents/RealEquipmentTemplate.vue
index 631a674..0315403 100644
--- a/applications/drone-command/src/views/dataCockpit/components/templateComponents/RealEquipmentTemplate.vue
+++ b/applications/drone-command/src/views/dataCockpit/components/templateComponents/RealEquipmentTemplate.vue
@@ -67,7 +67,7 @@
 				</div>
 				<div class="col">
 					<span class="label">有效范围</span>
-					{{ data.effectiveRangeKm }}m
+					{{ Number(data.effectiveRangeKm).toFixed(0) }}m
 				</div>
 			</div>
 		</div>
diff --git a/applications/drone-command/src/views/detectionCountermeasure/detectionRange/DetectionRangeDialog.vue b/applications/drone-command/src/views/detectionCountermeasure/detectionRange/DetectionRangeDialog.vue
index ca54c44..a939219 100644
--- a/applications/drone-command/src/views/detectionCountermeasure/detectionRange/DetectionRangeDialog.vue
+++ b/applications/drone-command/src/views/detectionCountermeasure/detectionRange/DetectionRangeDialog.vue
@@ -123,6 +123,7 @@
 							v-model="formData.effectiveRangeKm"
 							:min="0"
 							:precision="0"
+							:step="1"
 							:controls="false"
 							placeholder="请输入"
 						/>
@@ -227,7 +228,14 @@
 
 function formatRange(value) {
 	if (value == null || value === '') return '-'
-	return `${value}`
+	const normalized = normalizeRange(value)
+	return normalized == null ? '-' : `${normalized}`
+}
+
+function normalizeRange(value) {
+	const numeric = Number(value)
+	if (!Number.isFinite(numeric)) return null
+	return Math.trunc(numeric)
 }
 
 // 关闭弹框
@@ -242,14 +250,16 @@
 	submitting.value = true
 	try {
 		const {longitude, latitude} = parseDeployLocation(formData.value.deployLocation)
+		const normalizedRange = normalizeRange(formData.value.effectiveRangeKm)
 
 		const payload = {
 			id: formData.value.id || formData.value.deviceId || undefined,
-			effectiveRangeKmIsNotNull: formData.value.effectiveRangeKm ? 1 : 2,
-			effectiveRangeKm: formData.value.effectiveRangeKm,
+			effectiveRangeKmIsNotNull: normalizedRange != null ? 1 : 2,
+			effectiveRangeKm: normalizedRange,
 			longitude,
 			latitude
 		}
+		formData.value.effectiveRangeKm = normalizedRange
 		delete payload.deviceId
 		await detectionRangeSubmitApi(payload)
 
@@ -280,6 +290,7 @@
 		current: 1,
 		size: 200,
 		effectiveRangeKmIsNotNull: 2,
+		isTrack: 1
 	})
 	const records = res?.data?.data?.records ?? []
 	deviceOptions.value = records.map(item => ({
@@ -432,9 +443,18 @@
 watch(
 	() => formData.value.effectiveRangeKm,
 	value => {
+		const normalized = normalizeRange(value)
+		if (value != null && normalized == null) {
+			formData.value.effectiveRangeKm = null
+			return
+		}
+		if (value != null && normalized !== value) {
+			formData.value.effectiveRangeKm = normalized
+			return
+		}
 		const parsed = parseDeployLocation(formData.value.deployLocation)
 		if (!parsed) return
-		updateRangeCircle(parsed.longitude, parsed.latitude, value)
+		updateRangeCircle(parsed.longitude, parsed.latitude, normalized)
 	}
 )
 </script>
diff --git a/applications/drone-command/src/views/detectionCountermeasure/detectionRange/index.vue b/applications/drone-command/src/views/detectionCountermeasure/detectionRange/index.vue
index c2bda53..34735d3 100644
--- a/applications/drone-command/src/views/detectionCountermeasure/detectionRange/index.vue
+++ b/applications/drone-command/src/views/detectionCountermeasure/detectionRange/index.vue
@@ -204,8 +204,10 @@
 
 function formatRange(row) {
 	if (!row) return '-'
-	const value = row.effectiveRangeKm ?? row.range
-	return value === null || value === undefined || value === '' ? '-' : value
+
+	let value = row.effectiveRangeKm ?? row.range
+	
+	return value === null || value === undefined || value === '' ? '-' : Number(value).toFixed(0)
 }
 
 function formatDeviceCode(row) {

--
Gitblit v1.9.3