From df7347d9c0f39d05c3878c917ff4f4ba8d5f3028 Mon Sep 17 00:00:00 2001
From: 张含笑 <zhx18749296735@163.com>
Date: Tue, 27 Jan 2026 09:04:02 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master'

---
 applications/task-work-order/src/views/orderView/flyingHand/FlyingHandDialog.vue                         |  278 ++++++++++++++++++
 applications/task-work-order/src/views/orderView/flyingHand/index.vue                                    |  222 ++++++++++++++
 applications/drone-command/src/views/detectionCountermeasure/deviceAppConfig/index.vue                   |    6 
 applications/drone-command/src/views/dataCockpit/components/templateComponents/RealEquipmentTemplate.vue |    2 
 /dev/null                                                                                                |   96 ------
 applications/drone-command/src/components/map-container/device-map-container.vue                         |  122 +++++--
 applications/drone-command/src/views/basicManage/deviceStock/index.vue                                   |    3 
 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/detectionCountermeasure/detectionRange/index.vue                    |    6 
 applications/task-work-order/src/views/orderView/flyingHand/flyingHandApi.js                             |   37 ++
 applications/drone-command/src/api/dataCockpit/index.js                                                  |    5 
 13 files changed, 696 insertions(+), 157 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..f5813aa 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
@@ -693,7 +749,7 @@
 				width: 66.73,
 				height: 43,
 				verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
-				heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
+				// disableDepthTestDistance: Number.POSITIVE_INFINITY,
 			},
 			label: {
 				text: `${count}`,
@@ -706,7 +762,6 @@
 
 				pixelOffset: new Cesium.Cartesian2(0, -35),
 				disableDepthTestDistance: Number.POSITIVE_INFINITY,
-				heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
 			},
 		})
 	})
@@ -855,13 +910,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/basicManage/deviceStock/index.vue b/applications/drone-command/src/views/basicManage/deviceStock/index.vue
index 4933b82..e635bdd 100644
--- a/applications/drone-command/src/views/basicManage/deviceStock/index.vue
+++ b/applications/drone-command/src/views/basicManage/deviceStock/index.vue
@@ -62,7 +62,6 @@
 		<div class="command-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
 			<div class="command-table-content command-table-content-bg">
 				<el-table class="command-table" :data="list" @selection-change="handleSelectionChange">
-					<el-table-column type="selection" width="46" />
 					<el-table-column type="index" show-overflow-tooltip width="64" label="序号" />
 					<el-table-column prop="deviceName" show-overflow-tooltip width="130" label="设备名称" />
 					<el-table-column prop="deviceType" show-overflow-tooltip width="130" label="设备类型">
@@ -80,7 +79,7 @@
 					</el-table-column>
 					<el-table-column prop="source" show-overflow-tooltip width="112" label="来源" />
 					<el-table-column prop="purpose" show-overflow-tooltip width="206" label="用途" />
-					<el-table-column prop="belongDeptName" show-overflow-tooltip width="124" label="所属部门" />
+					<el-table-column prop="belongDeptName" show-overflow-tooltip label="所属部门" />
 					<el-table-column prop="charger" show-overflow-tooltip width="100" label="负责人" />
 					<el-table-column prop="yxzt" show-overflow-tooltip width="116" label="运行状态">
 						<template v-slot="{ row }">
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) {
diff --git a/applications/drone-command/src/views/detectionCountermeasure/deviceAppConfig/index.vue b/applications/drone-command/src/views/detectionCountermeasure/deviceAppConfig/index.vue
index ea7f4af..d56f1f3 100644
--- a/applications/drone-command/src/views/detectionCountermeasure/deviceAppConfig/index.vue
+++ b/applications/drone-command/src/views/detectionCountermeasure/deviceAppConfig/index.vue
@@ -151,7 +151,10 @@
 async function getList() {
 	loading.value = true
 	try {
-		const res = await fwDevicePageApi(searchParams.value)
+		const res = await fwDevicePageApi({
+			...searchParams.value,
+			deviceStatusList: '0,1,2'
+		})
 		list.value = res?.data?.data?.records ?? []
 		total.value = res?.data?.data?.total ?? 0
 	} finally {
@@ -176,6 +179,7 @@
 function getDictList() {
 	getDictionaryByCode('deviceType,deviceStatus,deviceAtt').then(res => {
 		dictObj.value = res.data.data
+		dictObj.value.deviceStatus = dictObj.value.deviceStatus.filter(item => item.dictKey != 3)
 	})
 }
 
diff --git a/applications/task-work-order/src/components/map-container/device-map-container.vue b/applications/task-work-order/src/components/map-container/device-map-container.vue
deleted file mode 100644
index 46199a1..0000000
--- a/applications/task-work-order/src/components/map-container/device-map-container.vue
+++ /dev/null
@@ -1,1094 +0,0 @@
-<template>
-	<div class="map-shell">
-		<CommonCesiumMap ref="mapRef" class="command-cesium map-container" :dom-id="props.containerId" :active="true"
-			:flat-mode="false" :terrain="false" :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" />
-		<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">
-					<img :src="layerControlIcon" alt="图层控制" />
-				</div>
-				<div v-if="showLayerPanel" class="layer-panel">
-					<div class="panel-title">图层管理</div>
-
-					<div class="panel-content">
-						<el-tree :data="layerTree" show-checkbox default-expand-all node-key="key"
-							:props="layerTreeProps" :default-checked-keys="defaultCheckedKeys" />
-					</div>
-				</div>
-			</div>
-		</div>
-
-		<div v-if="popupVisible" class="device-popup" :style="popupStyle" @click.stop>
-			<div class="device-popup__header">
-				<el-tooltip
-					class="device-popup__title-tooltip"
-					:content="popupTitle"
-					placement="top"
-					effect="dark"
-					:show-after="200"
-				>
-					<span class="device-popup__title">{{ popupTitleDisplay }}</span>
-				</el-tooltip>
-
-				<img class="device-popup__close" :src="popupClose" alt="" @click.stop="closePopup">
-			</div>
-			<div class="device-popup__type">
-				<span class="value">{{ popupTypeText }}</span>
-			</div>
-
-			<div class="device-popup__subtitle">
-				<span class="device-popup__dot" :class="popupActionClass"></span>
-				<span class="device-popup__action">{{ popupActionText }}</span>
-			</div>
-
-			<div class="device-popup__rows">
-				<div class="device-popup__row">
-					<div class="device-popup__col">
-						<span class="label">状态</span>
-						<span class="value" :class="popupStatusClass">{{ popupStatusText }}</span>
-					</div>
-
-					<span class="divider">|</span>
-
-					<div class="device-popup__col">
-						<span class="label">电量</span>
-						<span class="value">{{ popupBatteryText }}</span>
-					</div>
-				</div>
-				<div class="device-popup__row">
-					<div class="device-popup__col">
-						<span class="label">方位角</span>
-						<span class="value">{{ popupAzimuthText }}</span>
-					</div>
-
-					<span class="divider">|</span>
-
-					<div class="device-popup__col">
-						<span class="label">俯仰角</span>
-						<span class="value">{{ popupElevationText }}</span>
-					</div>
-				</div>
-				<div class="device-popup__row">
-					<span class="label">侦测目标</span>
-					<span class="value">{{ popupDetectCountText }}</span>
-				</div>
-				<div class="device-popup__row">
-					<span class="label">有效范围</span>
-					<span class="value">{{ popupRangeText }}</span>
-				</div>
-			</div>
-		</div>
-	</div>
-</template>
-
-<script setup>
-import * as Cesium from 'cesium'
-import CommonCesiumMap from '@/components/map-container/common-cesium-map.vue'
-import { geomAnalysis } from '@ztzf/utils'
-import { fwDefenseZonePageApi } from '@/views/areaManage/defenseZone/defenseZoneApi'
-import { fwAreaDividePageApi } from '@/views/areaManage/partition/partitionApi'
-import { fwDefenseSceneListApi } from '@/views/areaManage/sceneConfig/sceneConfigApi'
-import { cockpitAggregationApi } from '@/api/dataCockpit'
-import layerControlIcon from '@/assets/images/dataCockpit/layerControl.png'
-import equipmentIcon from '@/assets/images/dataCockpit/map/equipment.png'
-import aggregationIcon from '@/assets/images/dataCockpit/map/aggregation.png'
-import commandPostIcon from '@/assets/images/dataCockpit/map/command-post.png'
-import popupClose from '@/assets/images/dataCockpit/map/popup-close.png'
-import jaGeojsonRaw from '@/assets/geojson/ja.geojson?raw'
-
-const props = defineProps({
-	onlineDevices: {
-		type: Array,
-		default: () => [],
-	},
-	leftCollapsed: {
-		type: Boolean,
-		default: false,
-	},
-	containerId: {
-		type: String,
-		default: 'device-map-container',
-	},
-	showLayerControl: {
-		type: Boolean,
-		default: true,
-	},
-})
-
-const DEFAULT_ZONE_PAGE_SIZE = 999
-
-const mapRef = ref(null)
-let viewer = null
-const deviceEntityIds = new Set()
-const deviceEntityMap = new Map()
-let defenseZoneSource = null
-let partitionSource = null
-let aggregationSource = null
-let commandPostSource = null
-const detailVisible = ref(true)
-const clusterVisible = ref(false)
-const countyCenterMap = new Map()
-const showLayerPanel = ref(false)
-const layerWrapRef = ref(null)
-const selectedDevice = ref(null)
-let selectedDeviceEntity = null
-let deviceClickHandler = null
-let popupRenderHandler = null
-const layerTreeProps = {
-	label: 'label',
-	children: 'children',
-}
-const defaultCheckedKeys = ['global', 'city-base']
-const layerTree = ref([
-	{
-		key: 'base',
-		label: '地理信息图层',
-		children: [
-			{ key: 'global', label: '全球地形' },
-			{ key: 'admin', label: '行政区划' },
-		],
-	},
-	{
-		key: 'city',
-		label: '城市CIM图层',
-		children: [
-			{ key: 'city-base', label: '皖山白模' },
-			{ key: 'city-grid', label: '皖山白模光栅网格' },
-			{ key: 'city-tilt', label: '皖山倾斜摄影' },
-			{ key: 'city-tilt-grid', label: '皖山倾斜摄影网格' },
-		],
-	},
-	{
-		key: 'sky',
-		label: '空域要素图层',
-		children: [
-			{ key: 'airspace', label: '空域边界' },
-			{ key: 'route', label: '飞行航路' },
-		],
-	},
-])
-
-const getDevicePosition = item => {
-	const longitudeRaw = item.longitude ?? item.lng ?? item.lon
-	const latitudeRaw = item.latitude ?? item.lat
-	if (longitudeRaw == null || latitudeRaw == null) return null
-	const longitude = Number(longitudeRaw)
-	const latitude = Number(latitudeRaw)
-	if (!Number.isFinite(longitude) || !Number.isFinite(latitude)) return null
-	return { longitude, latitude }
-}
-
-const clearDeviceEntities = () => {
-	if (!viewer) return
-	deviceEntityIds.forEach(id => {
-		viewer.entities.removeById(id)
-	})
-	deviceEntityIds.clear()
-	deviceEntityMap.clear()
-}
-
-const clearDefenseZoneEntities = () => {
-	if (!defenseZoneSource) return
-	defenseZoneSource.entities.removeAll()
-}
-
-const clearPartitionEntities = () => {
-	if (!partitionSource) return
-	partitionSource.entities.removeAll()
-}
-
-const clearAggregationEntities = () => {
-	if (!aggregationSource) return
-	aggregationSource.entities.removeAll()
-}
-
-const clearCommandPostEntities = () => {
-	if (!commandPostSource) return
-	commandPostSource.entities.removeAll()
-}
-
-const setDetailVisibility = visible => {
-	detailVisible.value = visible
-	if (defenseZoneSource) defenseZoneSource.show = visible
-	if (partitionSource) partitionSource.show = visible
-	if (commandPostSource) commandPostSource.show = visible
-	if (!visible) closePopup()
-	deviceEntityIds.forEach(id => {
-		const entity = viewer?.entities?.getById(id)
-		if (entity) entity.show = visible
-	})
-}
-
-const setClusterVisibility = visible => {
-	clusterVisible.value = visible
-	if (aggregationSource) aggregationSource.show = visible
-}
-
-const ensureCountyCenterMap = () => {
-	if (countyCenterMap.size) return
-	const geojson = JSON.parse(jaGeojsonRaw)
-	geojson.features?.forEach(feature => {
-		const name = feature?.properties?.name
-		const center = feature?.properties?.centroid || feature?.properties?.center
-		if (!name || !Array.isArray(center) || center.length < 2) return
-		countyCenterMap.set(name, { longitude: center[0], latitude: center[1] })
-	})
-}
-
-
-const RING_STYLES = [
-	{ inner: 0, outer: 2000, gradient: ['#FF361C', '#360B00'] }
-]
-
-const MATERIAL_TYPE = 'RadialGradientMaterial'
-let materialRegistered = false
-
-const registerRadialGradientMaterial = () => {
-	if (materialRegistered || !Cesium?.Material) return
-	materialRegistered = true
-	Cesium.Material._materialCache.addMaterial(MATERIAL_TYPE, {
-		fabric: {
-			type: MATERIAL_TYPE,
-			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),
-			},
-			source: `
-				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);
-					material.diffuse = color.rgb;
-					material.alpha = color.a;
-					return material;
-				}
-			`,
-		},
-		translucent: () => true,
-	})
-}
-
-const buildCirclePositions = (center, radiusMeters, steps = 64) => {
-	const positions = []
-	const latRad = Cesium.Math.toRadians(center.latitude)
-	const metersToLat = 1 / 111320
-	const metersToLon = 1 / (111320 * Math.cos(latRad))
-	for (let i = 0; i <= steps; i += 1) {
-		const angle = Cesium.Math.toRadians((i / steps) * 360)
-		const dx = radiusMeters * Math.cos(angle)
-		const dy = radiusMeters * Math.sin(angle)
-		const lon = center.longitude + dx * metersToLon
-		const lat = center.latitude + dy * metersToLat
-		positions.push(Cesium.Cartesian3.fromDegrees(lon, lat))
-	}
-	return positions
-}
-
-const addDeviceRings = (center, baseId, visible) => {
-	RING_STYLES.forEach((ring, index) => {
-		const outerPositions = buildCirclePositions(center, ring.outer)
-		const holes = ring.inner
-			? [new Cesium.PolygonHierarchy(buildCirclePositions(center, ring.inner))]
-			: []
-		const entityId = `${baseId}-ring-${index}`
-		deviceEntityIds.add(entityId)
-		registerRadialGradientMaterial()
-		const [startColor, endColor] = ring.gradient
-		const color1 = Cesium.Color.fromCssColorString(startColor).withAlpha(0.34)
-		const color2 = Cesium.Color.fromCssColorString(endColor).withAlpha(0.34)
-		const material = new RadialGradientMaterialProperty(color1, color2)
-		viewer.entities.add({
-			id: entityId,
-			show: visible,
-			polygon: {
-				hierarchy: new Cesium.PolygonHierarchy(outerPositions, holes),
-				material,
-			},
-		})
-	})
-}
-
-class RadialGradientMaterialProperty {
-	constructor(color1, color2) {
-		this._definitionChanged = new Cesium.Event()
-		this.color1 = color1
-		this.color2 = color2
-	}
-
-	get isConstant () {
-		return true
-	}
-
-	get definitionChanged () {
-		return this._definitionChanged
-	}
-
-	getType () {
-		return MATERIAL_TYPE
-	}
-
-	getValue (time, result) {
-		const target = result || {}
-		target.color1 = this.color1
-		target.color2 = this.color2
-		return target
-	}
-
-	equals (other) {
-		return (
-			other instanceof RadialGradientMaterialProperty &&
-			Cesium.Color.equals(this.color1, other.color1) &&
-			Cesium.Color.equals(this.color2, other.color2)
-		)
-	}
-}
-
-const renderDeviceEntities = devices => {
-	if (!viewer) return
-	clearDeviceEntities()
-	devices.forEach((item, index) => {
-		const position = getDevicePosition(item)
-		if (!position) return
-		const entityId = `online-device-${item.id ?? index}`
-		deviceEntityIds.add(entityId)
-		addDeviceRings(position, entityId, detailVisible.value)
-		const entity = viewer.entities.add({
-			id: entityId,
-			show: detailVisible.value,
-			position: Cesium.Cartesian3.fromDegrees(position.longitude, position.latitude, 0),
-			billboard: {
-				image: equipmentIcon,
-				width: 40,
-				height: 56,
-				verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
-			},
-		})
-		deviceEntityMap.set(entityId, { data: item, entity })
-	})
-	if (selectedDeviceEntity && !deviceEntityMap.has(selectedDeviceEntity.id)) {
-		closePopup()
-	}
-}
-
-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()
-	defenseZoneSource.show = detailVisible.value
-	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()
-	partitionSource.show = detailVisible.value
-	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 renderAggregation = list => {
-	if (!viewer) return
-	ensureCountyCenterMap()
-	if (!aggregationSource) {
-		aggregationSource = new Cesium.CustomDataSource('aggregationSource')
-		viewer.dataSources.add(aggregationSource)
-	}
-	clearAggregationEntities()
-	aggregationSource.show = clusterVisible.value
-	const countMap = new Map()
-		; (list || []).forEach(item => {
-			if (!item?.type) return
-			countMap.set(item.type, Number(item.count ?? 0))
-		})
-	Array.from(countyCenterMap.entries()).forEach(([name, center], index) => {
-		const position = Cesium.Cartesian3.fromDegrees(center.longitude, center.latitude, 0)
-		const count = countMap.get(name) ?? 0
-		aggregationSource.entities.add({
-			id: `aggregation-${name}-${index}`,
-			position,
-			billboard: {
-				image: aggregationIcon,
-				width: 66.73,
-				height: 43,
-				verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
-				heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
-			},
-			label: {
-				text: `${count}`,
-				fillColor: Cesium.Color.WHITE,
-				style: Cesium.LabelStyle.FILL_AND_OUTLINE,
-				verticalOrigin: Cesium.VerticalOrigin.CENTER,
-				horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
-				font: '11pt Source Han Sans CN',
-				eyeOffset: new Cesium.Cartesian3(0, 0, -20), // 让label "浮" 在广告牌前面
-
-				pixelOffset: new Cesium.Cartesian2(0, -35),
-				disableDepthTestDistance: Number.POSITIVE_INFINITY,
-				heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
-			},
-		})
-	})
-}
-
-const getPickedDevice = picks => {
-	for (const pick of picks) {
-		const entityId = typeof pick?.id === 'string' ? pick.id : pick?.id?.id
-		if (entityId && deviceEntityMap.has(entityId)) {
-			return deviceEntityMap.get(entityId)
-		}
-	}
-	return null
-}
-
-const updatePopupPosition = () => {
-	if (!viewer || !selectedDeviceEntity) return
-	const cartesian =
-		selectedDeviceEntity.position?.getValue?.(Cesium.JulianDate.now()) ||
-		selectedDeviceEntity.position?._value
-	if (!cartesian) return
-	const screenPosition = viewer.scene.cartesianToCanvasCoordinates(cartesian)
-	if (!screenPosition) return
-	popupPosition.value = { x: screenPosition.x, y: screenPosition.y }
-}
-
-const startPopupRender = () => {
-	if (!viewer || popupRenderHandler) return
-	popupRenderHandler = () => updatePopupPosition()
-	viewer.scene.postRender.addEventListener(popupRenderHandler)
-	updatePopupPosition()
-}
-
-const stopPopupRender = () => {
-	if (!viewer || !popupRenderHandler) return
-	viewer.scene.postRender.removeEventListener(popupRenderHandler)
-	popupRenderHandler = null
-}
-
-const handleDeviceClick = movement => {
-	if (!viewer) return
-	const picks = viewer.scene.drillPick(movement.position) || []
-	const pickedDevice = getPickedDevice(picks)
-	if (!pickedDevice) {
-		closePopup()
-		return
-	}
-	selectedDevice.value = pickedDevice.data
-	selectedDeviceEntity = pickedDevice.entity
-	startPopupRender()
-}
-
-const initDeviceClickHandler = () => {
-	if (deviceClickHandler || !viewer) return
-	deviceClickHandler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
-	deviceClickHandler.setInputAction(handleDeviceClick, Cesium.ScreenSpaceEventType.LEFT_CLICK)
-}
-
-const destroyDeviceClickHandler = () => {
-	deviceClickHandler?.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK)
-	deviceClickHandler?.destroy()
-	deviceClickHandler = null
-}
-
-function closePopup () {
-	selectedDevice.value = null
-	selectedDeviceEntity = null
-	stopPopupRender()
-}
-
-const popupPosition = ref({ x: 0, y: 0 })
-const popupVisible = computed(() => Boolean(selectedDevice.value))
-const popupStyle = computed(() => ({
-	left: `${popupPosition.value.x}px`,
-	top: `${popupPosition.value.y}px`,
-}))
-
-const POPUP_TITLE_MAX = 12
-const popupTitle = computed(() => selectedDevice.value?.deviceName || selectedDevice.value?.deviceModel || '-')
-const popupTitleOverflow = computed(() => popupTitle.value.length > POPUP_TITLE_MAX)
-const popupTitleDisplay = computed(() => {
-	if (!popupTitleOverflow.value) return popupTitle.value
-	return `${popupTitle.value.slice(0, POPUP_TITLE_MAX)}……`
-})
-const popupStatusText = computed(() => {
-	const status = selectedDevice.value?.status
-	if (status === 0) return '在线'
-	if (status === 1) return '离线'
-	if (status === 2) return '故障'
-	if (status === 3) return '报废'
-	return '-'
-})
-const popupStatusClass = computed(() => (selectedDevice.value?.status === 0 ? 'online' : ''))
-const popupActionText = computed(() => {
-	const workMode = selectedDevice.value?.workMode
-	if (workMode === 1 || workMode === '1') return '侦测中'
-	if (workMode === 2 || workMode === '2') return '信号干扰中'
-	if (workMode === 3 || workMode === '3') return '诱导驱离中'
-	if (workMode === 4 || workMode === '4') return '待机'
-	return workMode || '-'
-})
-const popupActionClass = computed(() => {
-	const workMode = selectedDevice.value?.workMode
-	if (workMode === 1 || workMode === '1') return 'is-detecting'
-	if (workMode === 2 || workMode === '2') return 'is-jamming'
-	if (workMode === 3 || workMode === '3') return 'is-driving'
-	if (workMode === 4 || workMode === '4') return 'is-standby'
-	return 'is-standby'
-})
-const popupTypeText = computed(() => {
-	const type = selectedDevice.value?.deviceType
-	if (type === 1 || type === '1') return '便捷侦测箱'
-	if (type === 2 || type === '2') return '反制枪'
-	if (type === 3 || type === '3') return '察打一体'
-	return type || '-'
-})
-const popupBatteryText = computed(() => {
-	const val = selectedDevice.value?.batteryPct
-	if (val == null) return '-'
-	return `${val}%`
-})
-const popupAzimuthText = computed(() => {
-	const val = selectedDevice.value?.azimuth
-	if (val == null) return '-'
-	return `${val}°`
-})
-const popupElevationText = computed(() => {
-	const val = selectedDevice.value?.elevation
-	if (val == null) return '-'
-	return `${val}°`
-})
-const popupDetectCountText = computed(() => {
-	const val = selectedDevice.value?.detectTargetCnt
-	if (val == null) return '-'
-	return `${val}台`
-})
-const popupRangeText = computed(() => {
-	const val = selectedDevice.value?.effectiveRangeKm
-	if (val == null) return '-'
-	return `${val}KM`
-})
-
-const renderCommandPosts = list => {
-	if (!viewer) return
-	if (!commandPostSource) {
-		commandPostSource = new Cesium.CustomDataSource('commandPostSource')
-		viewer.dataSources.add(commandPostSource)
-	}
-	clearCommandPostEntities()
-	commandPostSource.show = detailVisible.value
-		; (list || []).forEach((item, index) => {
-			const position = getDevicePosition(item)
-			if (!position) return
-			commandPostSource.entities.add({
-				id: `command-post-${item.id ?? index}`,
-				position: Cesium.Cartesian3.fromDegrees(position.longitude, position.latitude, 0),
-				billboard: {
-					image: commandPostIcon,
-					width: 40,
-					height: 56,
-					verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
-					heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
-				},
-			})
-		})
-}
-
-const loadAggregation = async () => {
-	try {
-		const res = await cockpitAggregationApi()
-		renderAggregation(res?.data?.data ?? [])
-	} catch (error) {
-		renderAggregation([])
-	}
-}
-
-const loadCommandPosts = async () => {
-	try {
-		const res = await fwDefenseSceneListApi()
-		renderCommandPosts(res?.data?.data ?? [])
-	} catch (error) {
-		renderCommandPosts([])
-	}
-}
-
-
-watch(
-	() => props.onlineDevices,
-	devices => {
-		renderDeviceEntities(devices || [])
-	},
-	{ deep: true }
-)
-
-watch(
-	() => props.leftCollapsed,
-	isCollapsed => {
-		if (isCollapsed) showLayerPanel.value = false
-	}
-)
-
-const toggleLayerPanel = () => {
-	if (!props.showLayerControl) return
-	showLayerPanel.value = !showLayerPanel.value
-}
-
-const updateStageDisplay = stage => {
-	const showCluster = stage === 'cluster'
-	setClusterVisibility(showCluster)
-	setDetailVisibility(!showCluster)
-}
-
-const handleMapReady = ({ viewer: mapViewer }) => {
-	viewer = mapViewer
-	const height = viewer?.camera?.positionCartographic?.height
-	const stage = height != null && height >= 100000 ? 'cluster' : 'detail'
-	updateStageDisplay(stage)
-	renderDeviceEntities(props.onlineDevices)
-	loadDefenseZones()
-	loadPartitions()
-	loadAggregation()
-	loadCommandPosts()
-	initDeviceClickHandler()
-}
-
-const handleStageChange = stage => {
-	updateStageDisplay(stage)
-}
-
-const handleClickOutside = event => {
-	if (!showLayerPanel.value) return
-	const target = event.target
-	if (layerWrapRef.value?.contains(target)) return
-	showLayerPanel.value = false
-}
-
-onMounted(() => {
-	document.addEventListener('click', handleClickOutside)
-	const map = mapRef.value?.getMap()
-	if (map?.viewer) handleMapReady(map)
-})
-
-onBeforeUnmount(() => {
-	document.removeEventListener('click', handleClickOutside)
-	clearDeviceEntities()
-	clearDefenseZoneEntities()
-	clearPartitionEntities()
-	clearAggregationEntities()
-	clearCommandPostEntities()
-	closePopup()
-	destroyDeviceClickHandler()
-	viewer = null
-})
-</script>
-
-<style lang="scss" scoped>
-.map-shell {
-	position: relative;
-	width: 100%;
-	height: 100%;
-}
-
-.map-container {
-	position: absolute;
-	top: 0;
-	left: 0;
-	width: 100%;
-	height: 100%;
-}
-
-.device-popup {
-	position: absolute;
-	transform: translate(-50%, calc(-100% - 72px));
-	width: 202px;
-	padding: 0 16px 20px;
-	border-radius: 16px;
-	background: rgba(5, 5, 15, 0.7);
-	backdrop-filter: blur(16px);
-	color: #C3C3DD;
-	font-family: Source Han Sans CN, Source Han Sans CN;
-	font-size: 12px;
-	pointer-events: auto;
-	z-index: 11;
-	box-sizing: border-box;
-
-	&::after {
-		content: "";
-		position: absolute;
-		left: 50%;
-		bottom: -8px;
-		transform: translateX(-50%);
-		width: 0;
-		height: 0;
-		border-left: 8px solid transparent;
-		border-right: 8px solid transparent;
-		border-top: 8px solid rgba(5, 5, 15, 0.7);
-	}
-
-	.device-popup__header {
-		height: 40px;
-		display: flex;
-		align-items: center;
-		justify-content: space-between;
-		position: relative;
-
-		font-weight: bold;
-		font-size: 14px;
-		color: #FFFFFF;
-
-		&::after {
-			content: "";
-			position: absolute;
-			left: 0;
-			bottom: 0;
-			width: 50px;
-			height: 2px;
-			background: #284FE3;
-			box-shadow: 0px 3px 2px 0px rgba(15, 89, 255, 0.1), 0px 7px 5px 0px rgba(15, 89, 255, 0.15), 0px 13px 10px 0px rgba(15, 89, 255, 0.18), 0px 22px 18px 0px rgba(15, 89, 255, 0.21), 0px 42px 33px 0px rgba(15, 89, 255, 0.26), 0px 100px 80px 0px rgba(15, 89, 255, 0.36);
-			border-radius: 5px 5px 0px 0px;
-		}
-
-		&::before {
-			content: "";
-			position: absolute;
-			left: 55px;
-			right: 0;
-			bottom: 0;
-			height: 2px;
-			background: #323245;
-			border-radius: 0px 0px 5px 5px;
-		}
-
-		.device-popup__title-tooltip {
-			flex: 1;
-			min-width: 0;
-			display: block;
-		}
-
-		.device-popup__title {
-			display: inline-block;
-			max-width: 100%;
-			white-space: nowrap;
-			overflow: hidden;
-			text-overflow: ellipsis;
-		}
-
-		.device-popup__close {
-			cursor: pointer;
-		}
-	}
-
-	.device-popup__type {
-		display: flex;
-		align-items: center;
-		gap: 6px;
-		font-size: 12px;
-		margin-top: 8px;
-		margin-bottom: 10px;
-		color: #d2d7df;
-
-		.label {
-			color: #9aa4b2;
-		}
-	}
-
-	.device-popup__subtitle {
-		display: inline-flex;
-		align-items: center;
-		gap: 6px;
-		padding: 2px 10px;
-		border-radius: 999px;
-		background: rgba(255, 255, 255, 0.06);
-		font-size: 12px;
-		margin-bottom: 10px;
-
-		.device-popup__dot {
-			width: 6px;
-			height: 6px;
-			border-radius: 50%;
-			background: #6b7685;
-
-			&.is-detecting {
-				background: #35f08c;
-				box-shadow: 0 0 6px rgba(53, 240, 140, 0.8);
-			}
-
-			&.is-jamming {
-				background: #ff4444;
-				box-shadow: 0 0 6px rgba(255, 68, 68, 0.7);
-			}
-
-			&.is-driving {
-				background: #ffb020;
-				box-shadow: 0 0 6px rgba(255, 176, 32, 0.7);
-			}
-
-			&.is-standby {
-				background: #9aa4b2;
-			}
-		}
-	}
-
-	.device-popup__rows {
-		display: flex;
-		flex-direction: column;
-		gap: 6px;
-		font-size: 12px;
-		color: #d2d7df;
-
-		.device-popup__row {
-			display: flex;
-			align-items: center;
-			margin-bottom: 10px;
-
-			.device-popup__col {
-				flex: 1;
-				display: flex;
-				align-items: center;
-
-				&:last-child {
-					justify-content: flex-end;
-				}
-			}
-		}
-
-		.value {
-			margin-left: 10px;
-		}
-
-		.value.online {
-			color: #35f08c;
-		}
-	}
-
-}
-
-.layer-control-root {
-	position: absolute;
-	left: 337px;
-	bottom: 22px;
-	z-index: 9;
-	transition: transform 0.3s ease-in-out;
-	pointer-events: none;
-
-	&.collapsed {
-		transform: translateX(-317px);
-	}
-}
-
-.layer-control-wrap {
-	position: relative;
-	display: flex;
-	align-items: flex-end;
-	pointer-events: auto;
-}
-
-.layer-control {
-	width: 46px;
-	height: 46px;
-	cursor: pointer;
-
-	img {
-		width: 100%;
-		height: 100%;
-		display: block;
-	}
-}
-
-.layer-panel {
-	display: flex;
-	flex-direction: column;
-	position: absolute;
-	left: 66px;
-	bottom: 0;
-	width: 160px;
-	max-height: 442px;
-	background: #191932;
-	border-radius: 8px 8px 8px 8px;
-
-	.panel-title {
-		padding: 0 16px;
-		line-height: 42px;
-		font-family: 'Open Sans', Open Sans;
-		font-weight: 400;
-		font-size: 12px;
-		color: #ffffff;
-		text-align: left;
-		font-style: normal;
-		text-transform: none;
-		border-bottom: 1px solid rgba(70, 70, 100, 0.5);
-		box-sizing: border-box;
-	}
-
-	.panel-content {
-		padding: 0 16px;
-		height: 0;
-		flex: 1;
-		overflow: auto;
-	}
-
-	::v-deep(.el-tree) {
-		background: transparent;
-		color: #c3c3dd;
-		font-size: 12px;
-
-		.el-tree-node {
-			line-height: 30px !important;
-
-			.el-tree-node__content {
-				padding-left: 0 !important;
-				display: flex;
-				align-items: center;
-				height: 40px !important;
-				line-height: 40px !important;
-				border-bottom: 1px solid rgba(70, 70, 100, 0.5);
-				box-sizing: border-box;
-			}
-
-			.el-tree-node__children {
-				.el-tree-node__content {
-					border: none;
-				}
-			}
-
-			&:focus {
-				.el-tree-node__content {
-					background: transparent !important;
-				}
-			}
-		}
-	}
-}
-
-.layer-panel :deep(.el-tree-node__label) {
-	color: #c3c3dd;
-}
-
-.layer-panel :deep(.el-tree-node__expand-icon) {
-	order: 3;
-	margin-left: auto;
-}
-
-.layer-panel :deep(.el-tree-node__expand-icon.is-leaf) {
-	visibility: hidden;
-}
-
-.layer-panel :deep(.el-checkbox) {
-	order: 1;
-}
-
-.layer-panel :deep(.el-tree-node__label) {
-	order: 2;
-}
-
-.layer-panel :deep(.el-tree-node__expand-icon:not(.is-leaf) ~ .el-checkbox) {
-	display: none;
-}
-
-.layer-panel :deep(.el-tree-node__content:hover),
-.layer-panel :deep(.el-tree-node__content:focus) {
-	background: transparent !important;
-}
-
-.layer-panel :deep(.el-tree-node.is-current > .el-tree-node__content) {
-	background: transparent !important;
-	color: #ffffff;
-}
-
-.layer-panel :deep(.el-tree-node.is-current > .el-tree-node__content .el-tree-node__label) {
-	color: #ffffff;
-}
-
-.layer-panel :deep(.el-checkbox__input.is-checked .el-checkbox__inner) {
-	background-color: #023aff;
-	border-color: #023aff;
-}
-
-.layer-panel :deep(.el-checkbox__inner) {
-	background-color: transparent;
-	border-color: #a1a3d4;
-}
-</style>
diff --git a/applications/task-work-order/src/components/map-container/mapContainer-copy.vue b/applications/task-work-order/src/components/map-container/mapContainer-copy.vue
deleted file mode 100644
index 66d1b89..0000000
--- a/applications/task-work-order/src/components/map-container/mapContainer-copy.vue
+++ /dev/null
@@ -1,264 +0,0 @@
-<!--
- * @Author: shuishen 1109946754@qq.com
- * @Date: 2024-10-25 15:07:51
- * @LastEditors: shuishen 1109946754@qq.com
- * @LastEditTime: 2025-04-28 11:34:40
- * @FilePath: \drone-command\src\components\map-container\mapContainer.vue
- * @Description:
- *
- * Copyright (c) 2024 by shuishen, All Rights Reserved.
--->
-<template>
-  <div class="map-container">
-      <div class="viewer-container" id="viewer-container">
-          <div class="content">
-              <slot name="content"></slot>
-          </div>
-      </div>
-  </div>
-</template>
-
-<script setup>
-import * as turf from '@turf/turf'
-
-import { nextTick, onMounted, onUnmounted } from 'vue'
-import { read } from 'xlsx'
-
-import startPng from '@/assets/map_images/Startingpointicon.png'
-import endPng from '@/assets/map_images/EndPointicon.png'
-
-window.$viewer = null
-window.$Cesium = null
-let pointLayer = null
-let polylineLayer = null
-let pointHtmlLayer = null
-
-const { VITE_APP_BASE } = import.meta.env
-// import * as Cesium from 'cesium'
-// import 'cesium/Build/Cesium/Widgets/widgets.css'
-const isViewerReady = ref(false)
-const { rowDetails } = defineProps({
-  rowDetails: {
-      type: Object,
-      default: () => ({}),
-  }
-})
-
-
-async function initMap () {
-  if (window.$viewer) return
-
-  await DC.ready({
-      // Cesium: Cesium,
-      baseUrl: `${VITE_APP_BASE}/libs/dc-sdk/resources/`,
-  })
-
-  window.$Cesium = DC.getLib('Cesium')
-
-  // 天地图地图
-  const imageryProvider_standZh = new window.$Cesium.UrlTemplateImageryProvider({
-      url: 'https://t{s}.tianditu.gov.cn/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=e45274b0235bb913eceb393aabbf9c9c',
-      subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
-      maximumLevel: 18,
-      credit: 'stand_zj',
-  })
-  const imageryProvider_stand = new window.$Cesium.UrlTemplateImageryProvider({
-      url: 'https://t{s}.tianditu.gov.cn/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=e45274b0235bb913eceb393aabbf9c9c',
-      subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
-      // format: 'image/jpeg',
-      // show: true,
-      maximumLevel: 18,
-      credit: 'stand_tc',
-  })
-
-  window.$viewer = new DC.Viewer('viewer-container', {
-      "sceneMode": 2 //1: 2.5D,2: 2D,3: 3D
-  })
-
-  window.$viewer.locationBar.enable = false
-
-  window.$viewer?.zoomToPosition(new DC.Position(115.892151, 28.676493, 1000000, 0, -90, 0))
-
-  window.$viewer?.imageryLayers.addImageryProvider(imageryProvider_stand)
-  window.$viewer?.imageryLayers.addImageryProvider(imageryProvider_standZh)
-
-  pointLayer = new DC.VectorLayer('pointLayer')
-  window.$viewer?.addLayer(pointLayer)
-  polylineLayer = new DC.VectorLayer('polylineLayer')
-  window.$viewer?.addLayer(polylineLayer)
-  pointHtmlLayer = new DC.HtmlLayer('pointHtmlLayer')
-  window.$viewer?.addLayer(pointHtmlLayer)
-
-  isViewerReady.value = true
-}
-
-/**
-* 初始化标注添加
-* @param type 类型
-* @param data 数据
-*/
-const initAddEntity = (type, data) => {
-  watch(() => isViewerReady.value,
-      (ready) => {
-          if (ready) {
-              type === 'point' ? addPoint(data) : addPolyline(data)
-          }
-      },
-      { deep: true, immediate: true } // 初始化时立即执行
-  )
-}
-
-/**
-* 添加点标注
-* @param data 数据  数据格式 [lng, lat]
-*/
-function addPoint (data) {
-  if (pointLayer) pointLayer.clear()
-
-  const [lng, lat] = data
-
-  if (!lng || !lat) return
-
-  let point = new DC.Point(new DC.Position(lng, lat))
-  pointLayer.addOverlay(point)
-
-  window.$viewer?.zoomTo(pointLayer)
-}
-
-/**
-* 添加点标注
-* @param data 数据  数据格式 [[lng, lat], [lng, lat], [lng, lat]]
-*/
-function addPolyline (data) {
-  if (polylineLayer) polylineLayer.clear()
-  if (pointHtmlLayer) pointHtmlLayer.clear()
-
-  if (data.length === 0) return
-
-  const positionStr = data.map(item => {
-      const [lng, lat] = item
-
-      return `${lng}, ${lat}`
-  }).join(';')
-
-  let polyline = new DC.Polyline(positionStr)
-  polyline.setStyle({
-      width: 4,
-      material: new DC.PolylineTrailMaterialProperty({
-          color: DC.Color.DEEPSKYBLUE,
-          speed: 10
-      }),
-      clampToGround: true
-  })
-  polylineLayer.addOverlay(polyline)
-
-
-  data.forEach((item, index) => {
-      const [lng, lat] = item
-      let position = new DC.Position(lng, lat)
-
-      console.log(lng, lat)
-      let billboard = null
-
-      if (index === 0) {
-          billboard = new DC.Billboard(position, startPng)
-      }
-
-      if (index === data.length - 1) {
-          billboard = new DC.Billboard(position, endPng)
-      }
-
-
-      billboard && (billboard.size = [20, 20])
-      billboard && (billboard.setStyle({
-          "pixelOffset": { "x": 0, "y": -8 }
-      }))
-      billboard && polylineLayer.addOverlay(billboard)
-
-      if (index !== 0 && index !== data.length - 1) {
-          let divIcon = new DC.DivIcon(
-              position,
-              `<div class="point-icon-box">${index}</div>`
-          )
-          pointHtmlLayer.addOverlay(divIcon)
-      }
-  })
-
-  const line = turf.lineString(positionStr.split(';').map(i => i.split(',')))
-  const bbox = turf.bbox(line)
-  const bboxPolygon = turf.bboxPolygon(bbox)
-  const scaledPolygon = turf.transformScale(bboxPolygon, 3)
-  const newBbox = turf.bbox(scaledPolygon)
-
-  window.$viewer?.flyToBounds(
-      newBbox,
-      { heading: 0, pitch: -90, roll: 0 },
-      () => { },
-      0
-  )
-}
-
-onMounted(() => {
-  nextTick(() => {
-      initMap()
-  })
-})
-
-onUnmounted(() => {
-  if (pointLayer) {
-      window.$viewer?.removeLayer(pointLayer)
-      pointLayer = null
-  }
-
-  if (polylineLayer) {
-      window.$viewer?.removeLayer(polylineLayer)
-      polylineLayer = null
-  }
-
-  window.$viewer?.entities.removeAll()
-  window.$viewer?.imageryLayers.removeAll()
-  window.$viewer?.dataSources.removeAll()
-  let gl = window.$viewer.scene.context._originalGLContext
-  gl.canvas.width = 1
-  gl.canvas.height = 1
-
-  window.$viewer && window.$viewer.setTerrain()
-  window.$viewer && window.$viewer.destroy()
-  window.$viewer = null
-  delete window.$viewer
-  window.$Cesium = null
-  delete window.$Cesium
-  var cesiumContainer = document.getElementById('viewer-container')
-  if (cesiumContainer) {
-      cesiumContainer.remove() // 移除与地图相关的DOM元素
-  }
-})
-
-defineExpose({
-  initAddEntity
-})
-</script>
-
-<script>
-export default {
-  name: 'MapContainer'
-}
-</script>
-
-<style lang="scss" scoped>
-.map-container {
-  position: relative;
-  width: 100% !important;
-  height: 100% !important;
-  overflow: hidden;
-}
-
-.viewer-container {
-  position: absolute;
-  top: 50%;
-  left: 50%;
-  width: 120%;
-  height: 120%;
-  transform: translate(-50%, -50%);
-}
-</style>
diff --git a/applications/task-work-order/src/views/dataCockpit/components/CenterContainer.vue b/applications/task-work-order/src/views/dataCockpit/components/CenterContainer.vue
deleted file mode 100644
index 170826c..0000000
--- a/applications/task-work-order/src/views/dataCockpit/components/CenterContainer.vue
+++ /dev/null
@@ -1,237 +0,0 @@
-<template>
-	<div class="center-container">
-		<div class="left-box">
-			<div class="logo">
-				<img :src="warningLogo" alt="">
-			</div>
-
-			<div class="list">
-				<div class="item" v-for="(item, ind) in warningList" :key="ind">
-					<div class="val" @click="warningTypeClick(item.type)">
-						{{ item.value }}
-					</div>
-
-					<div class="name">
-						{{ item.title }}
-					</div>
-				</div>
-			</div>
-		</div>
-
-		<div class="right-box">
-			<div class="logo">
-				<img :src="equipmentLogo" alt="">
-			</div>
-
-			<div class="list">
-				<div class="item" v-for="(item, ind) in equipmentList" :key="ind">
-					<div class="val" :class="{highlight: item.isHighlight}" @click="equipmentClick(item.isHighlight)">
-						{{ item.value }}
-					</div>
-
-					<div class="name">
-						{{ item.title }}
-					</div>
-				</div>
-			</div>
-		</div>
-	</div>
-</template>
-
-<script setup>
-import { onMounted, ref } from 'vue'
-import warningLogo from '@/assets/images/dataCockpit/warning.png'
-import equipmentLogo from '@/assets/images/dataCockpit/equipment.png'
-import { alarmStatisticsApi } from '@/api/dataCockpit'
-
-const warningList = ref([
-	{
-		title: '实时告警',
-		value: 0,
-		type: 1
-	},
-	{
-		title: '设备告警',
-		value: 0,
-		type: 2
-	},
-	{
-		title: '历史告警',
-		value: 0,
-		type: 3
-	},
-])
-
-const equipmentList = ref([
-	{
-		title: '设备统计',
-		value: 0,
-		isHighlight: true,
-	},
-	{
-		title: '无线电',
-		value: 0,
-	},
-	{
-		title: '光电',
-		value: 0,
-	},
-	{
-		title: '雷达',
-		value: 0,
-	}
-])
-
-const emit = defineEmits(['select-warning', 'select-equipment'])
-
-const warningTypeClick = (type) => {
-	emit('select-warning', type)
-}
-
-const equipmentClick = (isHighlight) => {
-	if (isHighlight) {
-		emit('select-equipment')
-	}
-}
-
-const updateStats = async () => {
-	const res = await alarmStatisticsApi()
-	const data = res?.data?.data || {}
-	warningList.value = [
-		{ title: '实时告警', value: data.realTimeAlarmCount ?? 0, type: 1 },
-		{ title: '设备告警', value: data.deviceAlarmCount ?? 0, type: 2 },
-		{ title: '历史告警', value: data.historyAlarmCount ?? 0, type: 3 }
-	]
-	equipmentList.value = [
-		{ title: '设备统计', value: data.deviceCount ?? 0, isHighlight: true },
-		{ title: '无线电', value: data.radioCount ?? 0 },
-		{ title: '光电', value: data.opticalCount ?? 0 },
-		{ title: '雷达', value: data.radarCount ?? 0 }
-	]
-}
-
-onMounted(() => {
-	updateStats()
-})
-</script>
-
-<style lang="scss" scoped>
-.center-container {
-	display: flex;
-	position: absolute;
-	top: 75px;
-	left: 465px;
-	z-index: 9;
-	pointer-events: none;
-
-	.left-box {
-		width: 356px;
-
-		.list {
-			.item {
-				.val {
-					color: #FF4444;
-					cursor: pointer;
-				}
-			}
-		}
-	}
-
-	.right-box {
-		margin-left: 20px;
-		width: 455px;
-
-		.list {
-			.item {
-				.val.highlight {
-					color: #6382FF;
-					cursor: pointer;
-				}
-			}
-		}
-	}
-
-	.left-box,
-	.right-box {
-		position: relative;
-		padding: 17px 20px;
-		display: flex;
-		align-items: center;
-		height: 100px;
-		background: rgba(0,0,0,0.3);
-		box-sizing: border-box;
-		border-radius: 16px 16px 16px 16px;
-		pointer-events: auto;
-
-		&::after {
-			content: '';
-			position: absolute;
-			top: 0;
-			left: 0;
-			width: 100%;
-			height: 100%;
-			z-index: -1;
-			background: rgba(17, 23, 34, 0.56);
-      		backdrop-filter: blur(2px);
-			border-radius: 16px 16px 16px 16px;
-		}
-
-
-		.logo {
-			img {
-				width: 40px;
-				height: 40px;
-			}
-		}
-
-		.list {
-			width: 0;
-			flex: 1;
-			height: 100%;
-			display: flex;
-
-			.item {
-				width: 0;
-				flex: 1;
-				position: relative;
-				padding-left: 20px;
-				color: #fff;
-
-				.name {
-					font-family: Open Sans, Open Sans;
-					font-weight: 400;
-					font-size: 14px;
-					color: #FFFFFF;
-					text-align: left;
-					font-style: normal;
-					text-transform: none;
-				}
-
-				.val {
-					font-family: Source Han Sans CN, Source Han Sans CN;
-					font-weight: bold;
-					font-size: 30px;
-					text-align: left;
-					font-style: normal;
-					text-transform: none;
-				}
-
-				&::after {
-					content: '';
-					position: absolute;
-					top: 0;
-					right: 0;
-					width: 1px;
-					height: 100%;
-					width: 1px;
-					background: #333355;
-				}
-
-				&:last-child::after {
-					display: none;
-				}
-			}
-		}
-	}
-}
-</style>
diff --git a/applications/task-work-order/src/views/dataCockpit/components/DeviceHistoryDialog.vue b/applications/task-work-order/src/views/dataCockpit/components/DeviceHistoryDialog.vue
deleted file mode 100644
index 85e5351..0000000
--- a/applications/task-work-order/src/views/dataCockpit/components/DeviceHistoryDialog.vue
+++ /dev/null
@@ -1,208 +0,0 @@
-<template>
-	<el-dialog class="gd-dialog1" append-to-body v-model="visibleModel" :title="dialogTitle"
-		:close-on-click-modal="false" :destroy-on-close="true">
-		<el-form ref="queryParamsRef" :model="searchParams" class="gd-dialog-form">
-			<el-form-item label="关键字" prop="keyword">
-				<el-input class="gd-input" v-model="searchParams.keyword" placeholder="名称/序列号"
-					clearable @clear="handleSearch" />
-			</el-form-item>
-
-			<el-form-item prop="dateRange">
-				<el-date-picker class="gd-date-picker"
-					popper-class="gd-tree-select-popper" v-model="searchParams.dateRange"
-					type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"
-					value-format="YYYY/MM/DD" @change="handleSearch" />
-			</el-form-item>
-
-			<el-form-item label="区域" prop="area">
-				<el-tree-select class="gd-select" popper-class="gd-tree-select-popper"
-					v-model="searchParams.area" :data="areaTree" :props="areaTreeProps" node-key="value" check-strictly
-					clearable placeholder="请选择" @change="handleSearch" />
-			</el-form-item>
-
-			<el-form-item label="无人机类型" prop="droneType">
-				<el-select class="gd-select" popper-class="gd-select-popper"
-					v-model="searchParams.droneType" placeholder="请选择" clearable @change="handleSearch">
-					<el-option v-for="item in droneTypeOptions" :key="item.value" :label="item.label"
-						:value="item.value" />
-				</el-select>
-			</el-form-item>
-
-			<el-form-item class="gd-search-actions">
-				<el-button :icon="RefreshRight" @click="resetForm"></el-button>
-				<el-button :icon="Search" @click="handleSearch"></el-button>
-			</el-form-item>
-		</el-form>
-
-		<div class="gd-table-container" v-loading="loading" element-loading-background="rgba(5, 5, 15, 0.6)">
-			<div class="gd-table-content">
-				<el-table class="gd-table" :data="list">
-					<el-table-column type="index" width="60" label="序号" />
-					<el-table-column prop="droneName" label="无人机名称" min-width="120" />
-					<el-table-column prop="droneSerialNo" label="无人机序列号" min-width="140" />
-					<el-table-column label="无人机类型" min-width="110">
-						<template #default="{ row }">
-							{{ getDroneTypeText(row.droneType) }}
-						</template>
-					</el-table-column>
-					<el-table-column prop="areaName" label="区域" min-width="90" />
-					<el-table-column prop="deviceName" label="数据来源" min-width="110" />
-					<el-table-column label="信号频段" min-width="100">
-						<template #default="{ row }">
-							{{ formatFrequency(row.signalFreqMhz) }}
-						</template>
-					</el-table-column>
-					<el-table-column prop="alarmTime" label="发现时间" min-width="150" />
-					<el-table-column prop="stayDuration" label="停留时间" min-width="110" />
-					<el-table-column label="反制方式" min-width="110">
-						<template #default="{ row }">
-							{{ getCounterWayText(row.counterWay) }}
-						</template>
-					</el-table-column>
-				</el-table>
-			</div>
-
-			<div class="gd-pagination-parent">
-				<el-pagination popper-class="gd-select-popper"
-					v-model:current-page="searchParams.current" v-model:page-size="searchParams.size"
-					layout="total, prev, pager, next, sizes" :total="total" @change="getList" />
-			</div>
-		</div>
-	</el-dialog>
-</template>
-
-<script setup>
-import { Search, RefreshRight } from '@element-plus/icons-vue'
-import { computed, ref, watch } from 'vue'
-import { alarmLogApi } from '@/api/dataCockpit'
-
-const props = defineProps({
-	modelValue: {
-		type: Boolean,
-		default: false
-	},
-	device: {
-		type: Object,
-		default: () => null
-	}
-})
-
-const emit = defineEmits(['update:modelValue'])
-
-const visibleModel = computed({
-	get: () => props.modelValue,
-	set: (val) => emit('update:modelValue', val)
-})
-
-const dialogTitle = computed(() => {
-	return `反制设备名称 ${props.device?.name || ''}`.trim()
-})
-
-const initSearchParams = () => ({
-	keyword: '',
-	dateRange: [],
-	area: '',
-	droneType: '',
-	current: 1,
-	size: 10
-})
-const searchParams = ref(initSearchParams())
-const queryParamsRef = ref(null)
-const loading = ref(true)
-const list = ref([])
-const total = ref(0)
-const areaTree = ref([
-	{
-		label: '古村区',
-		value: '古村区'
-	},
-	{
-		label: '新区',
-		value: '新区'
-	},
-	{
-		label: '产业园',
-		value: '产业园'
-	}
-])
-const areaTreeProps = {
-	label: 'label',
-	children: 'children'
-}
-
-const droneTypeOptions = [
-	{ label: '微型机', value: '1' },
-	{ label: '植保机', value: '2' }
-]
-
-const getList = async () => {
-	if (!props.device?.id) {
-		list.value = []
-		total.value = 0
-		return
-	}
-	const [startTime, endTime] = searchParams.value.dateRange || []
-	const keyword = (searchParams.value.keyword || '').trim()
-	const params = {
-		alarmType: '2',
-		deviceId: props.device.id,
-		current: searchParams.value.current,
-		size: searchParams.value.size,
-		startTime,
-		endTime,
-		areaName: searchParams.value.area || undefined,
-		droneType: searchParams.value.droneType || undefined,
-		droneName: keyword || undefined,
-	}
-	loading.value = true
-	try {
-		const res = await alarmLogApi(params)
-		list.value = res?.data?.data?.records ?? []
-		total.value = res?.data?.data?.total ?? 0
-	} finally {
-		loading.value = false
-	}
-}
-
-function handleSearch () {
-	searchParams.value.current = 1
-	getList()
-}
-
-function resetForm () {
-	searchParams.value = initSearchParams()
-
-	getList()
-}
-
-const formatFrequency = (value) => {
-	if (value == null || value === '') return '-'
-	return `${value}MHZ`
-}
-
-const getCounterWayText = (value) => {
-	if (value === 1 || value === '1') return '信号干扰'
-	if (value === 2 || value === '2') return '诱导驱离'
-	if (value === 3 || value === '3') return '无'
-	return value || '-'
-}
-
-const getDroneTypeText = (value) => {
-	if (value === 1 || value === '1') return '微型机'
-	if (value === 2 || value === '2') return '植保机'
-	return value || '-'
-}
-
-watch(
-	() => visibleModel.value,
-	(visible) => {
-		if (visible) {
-			searchParams.value.current = 1
-			getList()
-		}
-	}
-)
-
-</script>
-
-<style lang="scss" scoped></style>
diff --git a/applications/task-work-order/src/views/dataCockpit/components/EmptyState.vue b/applications/task-work-order/src/views/dataCockpit/components/EmptyState.vue
deleted file mode 100644
index 929fb57..0000000
--- a/applications/task-work-order/src/views/dataCockpit/components/EmptyState.vue
+++ /dev/null
@@ -1,59 +0,0 @@
-<!--
- * @Author       : yuan
- * @Date         : 2026-01-09 15:06:02
- * @LastEditors  : yuan
- * @LastEditTime : 2026-01-09 15:10:44
- * @FilePath     : \applications\drone-command\src\views\dataCockpit\components\EmptyState.vue
- * @Description  : 
- * Copyright 2026 OBKoro1, All Rights Reserved. 
- * 2026-01-09 15:06:02
--->
-<template>
-	<div class="empty-state">
-		<div class="empty-content">
-			<img :src="noDataBg" alt="暂无数据">
-			<span class="empty-text">暂无数据</span>
-		</div>
-	</div>
-</template>
-
-<script setup>
-import noDataBg from '@/assets/images/no-data-bg.png'
-</script>
-
-<style scoped lang="scss">
-.empty-state {
-	display: flex;
-	align-items: center;
-	justify-content: center;
-	flex: 1;
-	width: 100%;
-	height: 100%;
-	min-height: 220px;
-
-	.empty-content {
-		display: flex;
-		align-items: center;
-		justify-content: center;
-		flex-direction: column;
-		text-align: center;
-	}
-
-	img {
-		max-width: 78%;
-		max-height: 78%;
-		object-fit: contain;
-	}
-
-	.empty-text {
-		font-family: Source Han Sans CN, Source Han Sans CN;
-		font-weight: 400;
-		font-size: 14px;
-		color: #9E9EBA;
-		line-height: 18px;
-		text-align: center;
-		font-style: normal;
-		text-transform: none;
-	}
-}
-</style>
diff --git a/applications/task-work-order/src/views/dataCockpit/components/EquipmentWarning.vue b/applications/task-work-order/src/views/dataCockpit/components/EquipmentWarning.vue
deleted file mode 100644
index ddbd81d..0000000
--- a/applications/task-work-order/src/views/dataCockpit/components/EquipmentWarning.vue
+++ /dev/null
@@ -1,101 +0,0 @@
-<!--
- * @Author       : yuan
- * @Date         : 2026-01-08 09:29:07
- * @LastEditors  : yuan
- * @LastEditTime : 2026-01-08 14:49:25
- * @FilePath     : \applications\drone-command\src\views\dataCockpit\components\EquipmentWarning.vue
- * @Description  : 
- * Copyright 2026 OBKoro1, All Rights Reserved. 
- * 2026-01-08 09:29:07
--->
-<template>
-	<div
-		class="equipment-warning"
-		v-loading="loading"
-		element-loading-background="rgba(5, 5, 15, 0.6)"
-		element-loading-text="加载中..."
-	>
-		<EmptyState v-if="!equipmentWarningData.length" />
-		<EquipmentTemplate
-			v-else
-			v-for="(item, ind) in equipmentWarningData"
-			:key="ind"
-			:data="item"
-			@click="onCardClick"
-		/>
-	</div>
-</template>
-
-<script setup>
-import { onMounted, ref } from 'vue'
-import EquipmentTemplate from './templateComponents/EquipmentTemplate.vue'
-import { deviceSearchApi } from '@/api/dataCockpit'
-import EmptyState from './EmptyState.vue'
-
-const equipmentWarningData = ref([])
-const loading = ref(true)
-const minLoadingMs = 400
-
-const statusMap = {
-	0: { text: '在线', statusType: 'online' },
-	1: { text: '离线', statusType: 'offline' },
-	2: { text: '故障', statusType: 'fault' },
-	3: { text: '报废', statusType: 'offline' }
-}
-const deviceTypeMap = {
-	1: '便捷侦测箱',
-	2: '反制枪',
-	3: '察打一体'
-}
-
-const formatDeviceItem = (item) => {
-	const statusInfo = statusMap[item?.status] || { text: '-', statusType: '' }
-	return {
-		id: item.id,
-		name: item.deviceName || item.deviceModel || '-',
-		status: statusInfo.text,
-		statusType: statusInfo.statusType,
-		type: deviceTypeMap[item.deviceType] || item.deviceType || '-',
-		manufacturer: item.manufacturer || '-',
-		azimuth: item.azimuth ?? 0,
-		pitch: item.elevation ?? 0,
-		range: item.effectiveRangeKm ?? 0,
-		model: item.deviceModel || '-',
-		longitude: item.longitude ?? '-',
-		latitude: item.latitude ?? '-'
-	}
-}
-
-const fetchDeviceWarning = async () => {
-	const startAt = Date.now()
-	loading.value = true
-	try {
-		const params = { current: 1, size: 20, deviceStatusList: '1,2,3' }
-		const res = await deviceSearchApi(params)
-		const records = res?.data?.data?.records ?? []
-		equipmentWarningData.value = records.map(formatDeviceItem)
-	} finally {
-		const elapsed = Date.now() - startAt
-		if (elapsed < minLoadingMs) {
-			await new Promise((resolve) => setTimeout(resolve, minLoadingMs - elapsed))
-		}
-		loading.value = false
-	}
-}
-
-onMounted(() => {
-	fetchDeviceWarning()
-})
-
-const onCardClick = (item) => {
-	console.log('点击设备卡片:', item.id)
-}
-</script>
-
-<style lang="scss" scoped>
-.equipment-warning {
-	width: 100%;
-	height: 100%;
-}
-
-</style>
diff --git a/applications/task-work-order/src/views/dataCockpit/components/HistoryWarning.vue b/applications/task-work-order/src/views/dataCockpit/components/HistoryWarning.vue
deleted file mode 100644
index 79160f1..0000000
--- a/applications/task-work-order/src/views/dataCockpit/components/HistoryWarning.vue
+++ /dev/null
@@ -1,123 +0,0 @@
-<template>
-	<div
-		class="history-warning"
-		v-loading="loading"
-		element-loading-background="rgba(5, 5, 15, 0.6)"
-		element-loading-text="加载中..."
-	>
-		<!-- 搜索区(设计稿红框) -->
-		<div class="search-box">
-			<!-- 关键字 -->
-			<el-input v-model="query.keyword" class="gd-input" placeholder="请输入数据名称" clearable
-				@keyup.enter="onSearch">
-			</el-input>
-
-			<!-- 日期范围 -->
-			<el-date-picker v-model="query.dateRange"
-			  	class="gd-date-picker"
-			 	popper-class="gd-date-picker-popper"
-			    type="daterange"
-				range-separator="~"
-				start-placeholder="创建开始日期" end-placeholder="结束日期" format="YYYY年MM月DD日" value-format="YYYY-MM-DD"
-				:clearable="true" @change="onSearch">
-			</el-date-picker>
-		</div>
-
-		<!-- 列表 -->
-		<div class="list-content">
-			<EmptyState v-if="!filteredList.length" />
-			<HistoryTemplate
-				v-else
-				v-for="(item, ind) in filteredList"
-				:key="item.id || ind"
-				:data="item"
-				@click="onCardClick"
-			/>
-		</div>
-	</div>
-</template>
-
-<script setup>
-import { ref, computed, onMounted } from 'vue'
-import HistoryTemplate from './templateComponents/HistoryTemplate.vue'
-import { alarmLogApi } from '@/api/dataCockpit'
-import EmptyState from './EmptyState.vue'
-
-const historyWarningData = ref([])
-const loading = ref(true)
-const minLoadingMs = 400
-
-// 查询条件:keyword + dateRange(YYYY-MM-DD数组)
-const query = ref({
-	keyword: '',
-	dateRange: [] // [start, end]
-})
-
-const onSearch = () => {
-	fetchHistoryWarning()
-}
-
-const onCardClick = (item) => {
-	console.log('点击无人机卡片:', item.id)
-}
-
-const fetchHistoryWarning = async () => {
-	const startAt = Date.now()
-	loading.value = true
-	try {
-		const [startTime, endTime] = query.value.dateRange || []
-		const keyword = (query.value.keyword || '').trim()
-		const params = {
-			current: 1,
-			size: 20,
-			alarmType: '2',
-			startTime,
-			endTime,
-			droneName: keyword || undefined,
-			droneSerialNo: keyword || undefined
-		}
-		const res = await alarmLogApi(params)
-		const records = res?.data?.data?.records ?? []
-		historyWarningData.value = records
-	} finally {
-		const elapsed = Date.now() - startAt
-		if (elapsed < minLoadingMs) {
-			await new Promise((resolve) => setTimeout(resolve, minLoadingMs - elapsed))
-		}
-		loading.value = false
-	}
-}
-
-const filteredList = computed(() => {
-	return historyWarningData.value
-})
-
-onMounted(() => {
-	fetchHistoryWarning()
-})
-</script>
-
-<style scoped lang="scss">
-.history-warning {
-	display: flex;
-	flex-direction: column;
-	width: 100%;
-	height: 100%;
-
-	.search-box {
-		display: flex;
-		flex-direction: column;
-		gap: 10px;
-
-		::v-deep(.gd-date-picker) {
-			width: 100% !important;
-		}
-	}
-
-	.list-content {
-		height: 0;
-		flex: 1;
-	}
-}
-
-</style>
diff --git a/applications/task-work-order/src/views/dataCockpit/components/LeftContainer.vue b/applications/task-work-order/src/views/dataCockpit/components/LeftContainer.vue
deleted file mode 100644
index 7b54029..0000000
--- a/applications/task-work-order/src/views/dataCockpit/components/LeftContainer.vue
+++ /dev/null
@@ -1,219 +0,0 @@
-<template>
-	<div class="left-container" :class="{ collapsed: collapsedModel }">
-		<div class="wrapper">
-			<TitleTemplate>异常情况告警</TitleTemplate>
-
-			<div class="category-container">
-				<div class="category-item" v-for="(item, ind) in categoryList" :key="ind"
-					@click="activeIdModel = item.id" :class="{ active: activeIdModel === item.id }">
-					{{ item.name }}
-					<span class="badge" v-if="item.badge">{{ item.badge }}</span>
-				</div>
-			</div>
-
-			<div class="content">
-				<component :is="currentComponent" />
-			</div>
-		</div>
-
-		<div class="collapse-btn" @click="toggleCollapse">
-			<span class="arrow" :class="collapsedModel ? 'right' : 'left'"></span>
-		</div>
-	</div>
-</template>
-
-<script setup>
-import TitleTemplate from './templateComponents/TitleTemplate.vue'
-import RealWarning from './RealWarning.vue'
-import EquipmentWarning from './EquipmentWarning.vue'
-import HistoryWarning from './HistoryWarning.vue'
-import { alarmStatisticsApi } from '@/api/dataCockpit'
-import { onMounted } from 'vue'
-
-const props = defineProps({
-	activeId: {
-		type: Number,
-		default: 1
-	},
-	collapsed: {
-		type: Boolean,
-		default: false
-	}
-})
-
-const emit = defineEmits(['update:activeId', 'update:collapsed'])
-
-const activeIdModel = computed({
-	get: () => props.activeId,
-	set: (val) => emit('update:activeId', val)
-})
-
-const collapsedModel = computed({
-	get: () => props.collapsed,
-	set: (val) => emit('update:collapsed', val)
-})
-
-const categoryList = ref([
-	{
-		id: 1,
-		name: '实时告警',
-		badge: 0,
-		component: RealWarning
-	},
-	{
-		id: 2,
-		name: '设备告警',
-		badge: 0,
-		component: EquipmentWarning
-	},
-	{
-		id: 3,
-		name: '历史告警',
-		badge: 0,
-		component: HistoryWarning
-	}
-])
-
-const currentComponent = computed(() => {
-	return categoryList.value.find(i => i.id === activeIdModel.value)?.component
-})
-
-const toggleCollapse = () => {
-	collapsedModel.value = !collapsedModel.value
-}
-
-const updateBadges = async () => {
-	const res = await alarmStatisticsApi()
-	const data = res?.data?.data || {}
-	const realTotal = data.realTimeAlarmCount ?? 0
-	const historyTotal = data.historyAlarmCount ?? 0
-	const deviceTotal = data.deviceAlarmCount ?? 0
-	categoryList.value = categoryList.value.map((item) => {
-		if (item.id === 1) return { ...item, badge: realTotal }
-		if (item.id === 2) return { ...item, badge: deviceTotal }
-		if (item.id === 3) return { ...item, badge: historyTotal }
-		return item
-	})
-}
-
-onMounted(() => {
-	updateBadges()
-})
-</script>
-
-<style lang="scss" scoped>
-.left-container {
-	position: relative;
-	transition: transform 0.3s ease-in-out;
-
-	&.collapsed {
-		transform: translateX(-100%);
-	}
-}
-
-.collapse-btn {
-	position: absolute;
-	top: 50%;
-	left: 357px;
-	cursor: pointer;
-	display: flex;
-	align-items: center;
-	justify-content: center;
-	color: #fff;
-	z-index: 10;
-	transform: translateY(-50%);
-
-	.arrow {
-		width: 24px;
-		height: 24px;
-		background: url('@/assets/images/dataCockpit/left-btn.png') center / 100% 100% no-repeat;
-	}
-
-	&:hover {
-		background: rgba(40, 79, 227, 0.6);
-	}
-}
-
-.category-container {
-	position: relative;
-	display: flex;
-	justify-content: space-between;
-
-	&::after {
-		content: '';
-		position: absolute;
-		left: 0;
-		bottom: 0;
-		width: 100%;
-		height: 1px;
-		background: #333355;
-	}
-
-	.category-item {
-		position: relative;
-		width: 0;
-		flex: 1;
-		margin-left: 26px;
-		height: 68px;
-		line-height: 68px;
-
-		font-family: Source Code Pro, Source Code Pro;
-		font-weight: 400;
-		font-size: 14px;
-		color: #86909C;
-		text-align: center;
-		font-style: normal;
-		text-transform: none;
-		cursor: pointer;
-
-		&:first-child {
-			margin-left: 0;
-		}
-
-		&.active {
-			font-weight: normal;
-			color: #FFFFFF;
-		}
-
-		&::after {
-			content: '';
-			position: absolute;
-			left: 0;
-			bottom: 0;
-			width: 100%;
-			height: 0;
-		}
-
-		&.active::after {
-			height: 3px;
-			background: #284FE3;
-			box-shadow: 0px 3px 2px 0px rgba(15, 89, 255, 0.1), 0px 7px 5px 0px rgba(15, 89, 255, 0.15), 0px 13px 10px 0px rgba(15, 89, 255, 0.18), 0px 22px 18px 0px rgba(15, 89, 255, 0.21), 0px 42px 33px 0px rgba(15, 89, 255, 0.26), 0px 100px 80px 0px rgba(15, 89, 255, 0.36);
-			border-radius: 5px 5px 0px 0px;
-			z-index: 9;
-		}
-
-		.badge {
-			position: absolute;
-			top: 16px;
-			right: 4px;
-			min-width: 18px;
-			line-height: 18px;
-			font-size: 12px;
-			color: #FFFFFF;
-			background: #FF3B30;
-			border-radius: 50%;
-			text-align: center;
-			box-shadow: 0px 2px 6px rgba(255, 59, 48, 0.6);
-			pointer-events: none;
-		}
-	}
-}
-
-.content {
-	height: 0;
-	flex: 1;
-	margin-top: 20px;
-	overflow-x: hidden;
-	overflow-y: auto;
-}
-</style>
diff --git a/applications/task-work-order/src/views/dataCockpit/components/RealWarning.vue b/applications/task-work-order/src/views/dataCockpit/components/RealWarning.vue
deleted file mode 100644
index 607b94d..0000000
--- a/applications/task-work-order/src/views/dataCockpit/components/RealWarning.vue
+++ /dev/null
@@ -1,109 +0,0 @@
-<!--
- * @Author       : yuan
- * @Date         : 2026-01-08 09:29:07
- * @LastEditors  : yuan
- * @LastEditTime : 2026-01-08 14:49:09
- * @FilePath     : \applications\drone-command\src\views\dataCockpit\components\RealWarning.vue
- * @Description  : 
- * Copyright 2026 OBKoro1, All Rights Reserved. 
- * 2026-01-08 09:29:07
--->
-<template>
-	<div
-		class="real-warning"
-		v-loading="loading"
-		element-loading-background="rgba(5, 5, 15, 0.6)"
-		element-loading-text="加载中..."
-	>
-		<EmptyState v-if="!realWarningData.length" />
-		<RealTemplate
-			v-else
-			v-for="(item, ind) in realWarningData"
-			:key="ind"
-			:data="item"
-			@signal="() => handleAction('signal', item)"
-			@counter="() => handleAction('counter', item)"
-			@favorite="() => console.log('收藏', item.id)"
-		/>
-	</div>
-</template>
-
-<script setup>
-import RealTemplate from './templateComponents/RealTemplate.vue'
-import { alarmLogApi, interferenceAndExpulsionApi } from '@/api/dataCockpit'
-import { onMounted, ref } from 'vue'
-import EmptyState from './EmptyState.vue'
-import { ElMessage } from 'element-plus'
-
-const realWarningData = ref([])
-const loading = ref(true)
-const minLoadingMs = 400
-
-const actionTextMap = {
-	signal: '信号干扰',
-	counter: '诱导驱离'
-}
-
-const buildPayload = (item) => ({
-	alarmRecordId: item.id,
-	areaCode: item.areaCode,
-	counterEffect: '',
-	coverRadiusM: item.coverRadiusM,
-	deployLatitude: item.latitude,
-	deployLongitude: item.longitude,
-	deviceCode: item.deviceCode,
-	deviceId: item.deviceId,
-	deviceModel: item.deviceModel,
-	deviceName: item.deviceName,
-	deviceType: item.deviceType,
-	droneDeviceCode: item.droneSerialNo,
-	droneName: item.droneName,
-	droneType: item.droneType,
-	findTime: item.alarmTime,
-	id: item.id,
-	workMode: ''
-})
-
-const handleAction = async (type, item) => {
-	const actionText = actionTextMap[type] || '操作'
-	try {
-		const res = await interferenceAndExpulsionApi(buildPayload(item))
-		if (res?.data?.success) {
-			ElMessage({ type: 'success', message: `${actionText}成功` })
-		} else {
-			ElMessage({ type: 'error', message: res?.data?.msg || `${actionText}失败` })
-		}
-	} catch (error) {
-		ElMessage({ type: 'error', message: `${actionText}失败` })
-	}
-}
-
-const fetchRealWarning = async () => {
-	const startAt = Date.now()
-	loading.value = true
-	try {
-		const params = { current: 1, size: 20, alarmType: '1' }
-		const res = await alarmLogApi(params)
-		const records = res?.data?.data?.records ?? []
-		realWarningData.value = records
-	} finally {
-		const elapsed = Date.now() - startAt
-		if (elapsed < minLoadingMs) {
-			await new Promise((resolve) => setTimeout(resolve, minLoadingMs - elapsed))
-		}
-		loading.value = false
-	}
-}
-
-onMounted(() => {
-	fetchRealWarning()
-})
-</script>
-
-<style lang="scss" scoped>
-.real-warning {
-	width: 100%;
-	height: 100%;
-}
-
-</style>
diff --git a/applications/task-work-order/src/views/dataCockpit/components/RightContainer.vue b/applications/task-work-order/src/views/dataCockpit/components/RightContainer.vue
deleted file mode 100644
index b014989..0000000
--- a/applications/task-work-order/src/views/dataCockpit/components/RightContainer.vue
+++ /dev/null
@@ -1,288 +0,0 @@
-<!--
- * @Author       : yuan
- * @Date         : 2026-01-07 15:17:54
- * @LastEditors  : yuan
- * @LastEditTime : 2026-01-09 16:54:31
- * @FilePath     : \applications\drone-command\src\views\dataCockpit\components\RightContainer.vue
- * @Description  : 
- * Copyright 2026 OBKoro1, All Rights Reserved. 
- * 2026-01-07 15:17:54
--->
-<template>
-	<div class="right-container" :class="{ collapsed: collapsedModel }">
-		<div class="wrapper">
-			<TitleTemplate>侦测反制设备</TitleTemplate>
-
-			<div class="category-container">
-				<div class="category-item" v-for="(item, ind) in categoryList" :key="ind">
-					<div class="category-item-logo">
-						<img :src="item.logo" alt="">
-					</div>
-
-					<div class="category-item-content">
-						<span class="name">{{ item.name }}</span>
-						<span class="val">{{ item.val }}</span>
-					</div>
-				</div>
-			</div>
-
-			<div
-				class="content"
-				v-loading="loading"
-				element-loading-background="rgba(5, 5, 15, 0.6)"
-				element-loading-text="加载中..."
-			>
-				<EmptyState v-if="!counterDeviceStatusList.length" />
-				<RealEquipmentTemplate
-					v-else
-					v-for="item in counterDeviceStatusList"
-					:key="item.id"
-					:data="item"
-					@history="onHistory"
-					@click="onCardClick"
-				/>
-			</div>
-		</div>
-
-		<div class="collapse-btn" @click="toggleCollapse">
-			<span class="arrow" :class="collapsedModel ? 'left' : 'right'"></span>
-		</div>
-
-		<DeviceHistoryDialog v-model="historyVisible" :device="historyDevice" />
-	</div>
-</template>
-
-<script setup>
-import TitleTemplate from './templateComponents/TitleTemplate.vue'
-import RealEquipmentTemplate from './templateComponents/RealEquipmentTemplate.vue'
-import DeviceHistoryDialog from './DeviceHistoryDialog.vue'
-import EmptyState from './EmptyState.vue'
-import zcsbLogo from '@/assets/images/dataCockpit/zcsb.png'
-import fzsbLogo from '@/assets/images/dataCockpit/fzsb.png'
-import { deviceSearchApi, deviceStatisticsApi } from '@/api/dataCockpit'
-
-const props = defineProps({
-	collapsed: {
-		type: Boolean,
-		default: false
-	}
-})
-
-const emit = defineEmits(['update:collapsed', 'update:onlineDevices'])
-
-const collapsedModel = computed({
-	get: () => props.collapsed,
-	set: (val) => emit('update:collapsed', val)
-})
-
-const categoryList = ref([
-	{
-		id: 1,
-		name: '侦测设备',
-		val: 0,
-		logo: zcsbLogo
-	},
-	{
-		id: 2,
-		name: '反制设备',
-		val: 0,
-		logo: fzsbLogo
-	}
-])
-
-const counterDeviceStatusList = ref([])
-
-const historyVisible = ref(false)
-const historyDevice = ref(null)
-const loading = ref(true)
-const minLoadingMs = 400
-
-const onHistory = (item) => {
-	historyDevice.value = item
-	historyVisible.value = true
-	console.log('历史数据:', item.id)
-}
-
-const onCardClick = (item) => {
-	console.log('点击卡片:', item.id)
-}
-const toggleCollapse = () => {
-	collapsedModel.value = !collapsedModel.value
-}
-
-const statusMap = {
-	0: { text: '在线', actionType: 'detecting' },
-	1: { text: '离线', actionType: '' },
-	2: { text: '故障', actionType: 'jamming' },
-	3: { text: '报废', actionType: '' }
-}
-const deviceTypeMap = {
-	1: '便捷侦测箱',
-	2: '反制枪',
-	3: '察打一体'
-}
-
-const formatDeviceItem = (item) => {
-	const statusInfo = statusMap[item?.status] || { text: '-', actionType: '' }
-	return {
-		id: item.id,
-		name: item.deviceName || item.deviceModel || '-',
-		actionText: statusInfo.text,
-		actionType: statusInfo.actionType,
-		type: deviceTypeMap[item.deviceType] || item.deviceType || '-',
-		batteryPct: item.batteryPct ?? 0,
-		azimuth: item.azimuth ?? 0,
-		elevation: item.elevation ?? 0,
-		status: item.status ?? '-',
-		manufacturer: item.manufacturer || '-',
-		detectTargetCnt: item.detectTargetCnt ?? 0,
-		effectiveRangeKm: item.effectiveRangeKm ?? 0
-	}
-}
-
-const fetchDeviceList = async () => {
-	const startAt = Date.now()
-	loading.value = true
-	try {
-		const params = { current: 1, size: 20, deviceStatusList: '0' }
-		const res = await deviceSearchApi(params)
-		const records = res?.data?.data?.records ?? []
-		counterDeviceStatusList.value = records.map(formatDeviceItem)
-		emit('update:onlineDevices', records)
-	} finally {
-		const elapsed = Date.now() - startAt
-		if (elapsed < minLoadingMs) {
-			await new Promise((resolve) => setTimeout(resolve, minLoadingMs - elapsed))
-		}
-		loading.value = false
-	}
-}
-
-onMounted(() => {
-	fetchDeviceList()
-	fetchDeviceStatistics()
-})
-
-const normalizeType = (value) => {
-	if (value === 1 || value === '1') return '侦测设备'
-	if (value === 2 || value === '2') return '反制设备'
-	if (value === 'detect') return '侦测设备'
-	if (value === 'counter') return '反制设备'
-	return value
-}
-
-const fetchDeviceStatistics = async () => {
-	const res = await deviceStatisticsApi()
-	const list = res?.data?.data ?? []
-	const map = new Map(list.map((item) => [normalizeType(item.type), item.count]))
-	categoryList.value = categoryList.value.map((item) => ({
-		...item,
-		val: map.has(item.name) ? map.get(item.name) : item.val
-	}))
-}
-</script>
-
-<style lang="scss" scoped>
-.category-container {
-	margin-top: 20px;
-	display: flex;
-	align-items: center;
-	justify-content: space-between;
-	gap: 28px;
-	box-sizing: border-box;
-
-	.category-item {
-		position: relative;
-		width: 0;
-		flex: 1;
-		display: flex;
-		height: 64px;
-
-		&-logo {
-			display: flex;
-			align-items: center;
-
-			img {
-				width: 40px;
-				height: 40px;
-			}
-		}
-
-		&-content {
-			width: 0;
-			flex: 1;
-			display: flex;
-			flex-direction: column;
-			border-bottom: 1px solid #6C6C89;
-
-			span {
-				margin-left: 8px;
-			}
-
-			.name {
-				font-family: Open Sans, Open Sans;
-				font-weight: 400;
-				font-size: 16px;
-				color: #C3C3DD;
-				line-height: 20px;
-				text-align: left;
-				font-style: normal;
-				text-transform: none;
-			}
-
-			.val {
-				margin-top: 10px;
-				font-family: DIN, DIN;
-				font-weight: bold;
-				font-size: 22px;
-				color: #FFFFFF;
-				line-height: 20px;
-				text-align: left;
-				font-style: normal;
-				text-transform: none;
-			}
-		}
-	}
-}
-
-.content {
-	height: 0;
-	flex: 1;
-	margin-top: 20px;
-	overflow-x: hidden;
-	overflow-y: auto;
-}
-
-
-.right-container {
-	transition: transform 0.3s ease-in-out;
-	position: relative;
-
-	&.collapsed {
-		transform: translateX(100%);
-	}
-}
-
-.collapse-btn {
-	position: absolute;
-	top: 50%;
-	right: 357px;
-	cursor: pointer;
-	display: flex;
-	align-items: center;
-	justify-content: center;
-	color: #fff;
-	z-index: 10;
-	transform: translateY(-50%);
-
-	.arrow {
-		width: 24px;
-		height: 24px;
-		background: url('@/assets/images/dataCockpit/right-btn.png') center / 100% 100% no-repeat;
-	}
-
-	&:hover {
-		background: rgba(40, 79, 227, 0.6);
-	}
-}
-</style>
diff --git a/applications/task-work-order/src/views/dataCockpit/components/templateComponents/EquipmentTemplate.vue b/applications/task-work-order/src/views/dataCockpit/components/templateComponents/EquipmentTemplate.vue
deleted file mode 100644
index 7b09cfb..0000000
--- a/applications/task-work-order/src/views/dataCockpit/components/templateComponents/EquipmentTemplate.vue
+++ /dev/null
@@ -1,192 +0,0 @@
-<template>
-	<div class="device-card" @click="emit('click', data)">
-		<div class="header">
-			<div class="title">
-				<el-tooltip
-					class="title-tooltip"
-					:content="data.name || '-'"
-					placement="top"
-					effect="dark"
-					:show-after="200"
-				>
-					<span class="name">{{ data.name }}</span>
-				</el-tooltip>
-
-				<span class="status">
-					<span :class="data.statusType">
-
-					</span>
-
-					<span>
-						{{ data.status }}
-					</span>
-				</span>
-			</div>
-		</div>
-
-		<div class="content">
-			<div class="row">
-				<div class="col">
-					<span class="label">类型</span>
-					{{ data.type }}
-				</div>
-				<div class="col">
-					<span class="label">厂商</span>
-					{{ data.manufacturer }}
-				</div>
-			</div>
-			<div class="row">
-				<div class="col">
-					<span class="label">方位角</span>
-					{{ data.azimuth }}°
-				</div>
-				<div class="col">
-					<span class="label">俯仰角</span>
-					{{ data.pitch }}°
-				</div>
-			</div>
-			<div class="row">
-				<div class="col">
-					<span class="label">有效范围</span>
-					{{ data.range }}
-				</div>
-				<div class="col">
-					<span class="label">设备型号</span>
-					{{ data.model }}
-				</div>
-			</div>
-
-			<div>
-				<span class="label">部署位置</span>
-				<span class="value">{{ data.longitude }},{{ data.latitude }}</span>
-			</div>
-		</div>
-	</div>
-</template>
-
-<script setup>
-const props = defineProps({
-	data: {
-		type: Object,
-		required: true,
-		default: () => ({})
-	}
-})
-
-const emit = defineEmits(['click'])
-</script>
-
-<style scoped lang="scss">
-.device-card {
-	margin-top: 10px;
-	padding: 10px;
-	color: #fff;
-	background: #191933;
-	border-radius: 6px 6px 6px 6px;
-
-	&:first-child {
-		margin-top: 0;
-	}
-
-	.header {
-		display: flex;
-		justify-content: space-between;
-		align-items: center;
-
-		.title {
-			display: flex;
-			align-items: center;
-			gap: 6px;
-			min-width: 0;
-
-			.title-tooltip {
-				flex: 1;
-				min-width: 0;
-				display: block;
-			}
-
-			.name {
-				display: inline-block;
-				max-width: 100%;
-				font-family: Source Han Sans CN, Source Han Sans CN;
-				font-weight: bold;
-				font-size: 14px;
-				color: #FFFFFF;
-				text-align: left;
-				font-style: normal;
-				text-transform: none;
-				white-space: nowrap;
-				overflow: hidden;
-				text-overflow: ellipsis;
-			}
-
-			.status {
-				flex-shrink: 0;
-				padding: 0 10px;
-				display: flex;
-				align-items: center;
-				justify-content: space-between;
-				height: 21px;
-
-				background: #303041;
-				border-radius: 10px;
-				gap: 6px;
-
-				span {
-					font-family: Source Han Sans CN, Source Han Sans CN;
-					font-weight: 400;
-					font-size: 10px;
-					color: #FFFFFF;
-					text-align: center;
-					font-style: normal;
-					text-transform: none;
-				}
-
-
-				span.offline {
-					width: 4px;
-					height: 4px;
-					background: #939393;
-					border-radius: 50%;
-				}
-
-				span.fault {
-					width: 4px;
-					height: 4px;
-					background: #FF4444;
-					border-radius: 50%;
-				}
-			}
-		}
-	}
-
-	.content {
-		font-family: Source Han Sans CN, Source Han Sans CN;
-		font-weight: 400;
-		font-size: 12px;
-		color: #9E9EBA;
-		text-align: left;
-		font-style: normal;
-		text-transform: none;
-
-		&>div {
-			margin-top: 10px;
-			line-height: 22px;
-
-			span.label {
-				margin-right: 10px;
-				color: #D4D5D7;
-			}
-		}
-
-		.row {
-			display: flex;
-
-			&>div {
-				width: 0;
-				flex: 1;
-			}
-		}
-	}
-}
-</style>
diff --git a/applications/task-work-order/src/views/dataCockpit/components/templateComponents/HistoryTemplate.vue b/applications/task-work-order/src/views/dataCockpit/components/templateComponents/HistoryTemplate.vue
deleted file mode 100644
index 66a633d..0000000
--- a/applications/task-work-order/src/views/dataCockpit/components/templateComponents/HistoryTemplate.vue
+++ /dev/null
@@ -1,169 +0,0 @@
-<template>
-	<div class="drone-info-card" @click="emit('click', data)">
-		<div class="title">
-			<el-tooltip
-				class="title-tooltip"
-				:content="data.droneName || '-'"
-				placement="top"
-				effect="dark"
-				:show-after="200"
-			>
-				<span class="name">
-					{{ data.droneName }}
-				</span>
-			</el-tooltip>
-		</div>
-
-		<div class="rows">
-			<div class="row">
-				<span class="label">序列号</span>
-				<span class="value">{{ data.droneSerialNo }}</span>
-			</div>
-
-			<div class="row">
-				<span class="label">数据源</span>
-				<span class="value">{{ getDataSource(data) }}</span>
-			</div>
-
-			<div class="row">
-				<span class="label">信号频段</span>
-				<span class="value">{{ formatFrequency(data.signalFreqMhz) }}</span>
-			</div>
-
-			<div class="row">
-				<span class="label">发现时间</span>
-				<span class="value">
-					<span class="date">{{ getAlarmDate(data.alarmTime) }}</span>
-					<span class="time">{{ getAlarmTime(data.alarmTime) }}</span>
-				</span>
-			</div>
-
-			<div class="row">
-				<span class="label">停留时间</span>
-				<span class="value">{{ data.stayDuration }}</span>
-			</div>
-
-			<div class="row">
-				<span class="label">反制方式</span>
-				<span class="value">{{ getCounterWayText(data.counterWay) }}</span>
-			</div>
-
-			<div class="row">
-				<span class="label">飞行状态</span>
-				<span class="value">{{ getFlightStatusText(data.flightStatus) }}</span>
-			</div>
-		</div>
-	</div>
-</template>
-
-<script setup>
-defineProps({
-	data: {
-		type: Object,
-		required: true,
-		default: () => ({})
-	}
-})
-
-const emit = defineEmits(['click'])
-
-const getDataSource = (item) => {
-	return item?.deviceName || item?.areaName || '-'
-}
-
-const formatFrequency = (value) => {
-	if (value == null || value === '') return '-'
-	return `${value}MHZ`
-}
-
-const getAlarmDate = (value) => {
-	if (!value) return '-'
-	const parts = value.replace('T', ' ').split(' ')
-	return parts[0] || value
-}
-
-const getAlarmTime = (value) => {
-	if (!value) return '-'
-	const parts = value.replace('T', ' ').split(' ')
-	return parts[1] || value
-}
-
-const getFlightStatusText = (value) => {
-	if (value === 1 || value === '1') return '侦测中'
-	if (value === 2 || value === '2') return '反制中'
-	return value || '-'
-}
-
-const getCounterWayText = (value) => {
-	if (value === 1 || value === '1') return '信号干扰'
-	if (value === 2 || value === '2') return '诱导驱离'
-	if (value === 3 || value === '3') return '无'
-	return value || '-'
-}
-</script>
-
-<style scoped lang="scss">
-.drone-info-card {
-	margin-top: 10px;
-	padding: 10px;
-	color: #fff;
-	background: #191933;
-	border-radius: 6px 6px 6px 6px;
-
-	&:hover {
-		filter: brightness(1.05);
-	}
-
-	.title {
-		display: flex;
-		align-items: center;
-		gap: 6px;
-		min-width: 0;
-
-		.title-tooltip {
-			flex: 1;
-			min-width: 0;
-			display: block;
-		}
-
-		.name {
-			display: inline-block;
-			max-width: 100%;
-			font-family: Source Han Sans CN, Source Han Sans CN;
-			font-weight: bold;
-			font-size: 14px;
-			color: #FFFFFF;
-			text-align: left;
-			font-style: normal;
-			text-transform: none;
-			white-space: nowrap;
-			overflow: hidden;
-			text-overflow: ellipsis;
-		}
-	}
-
-	.rows {
-		margin-top: 10px;
-		display: flex;
-		flex-direction: column;
-		gap: 10px;
-
-		font-family: Source Han Sans CN, Source Han Sans CN;
-		font-weight: 400;
-		font-size: 12px;
-		color: #9E9EBA;
-		text-align: left;
-		font-style: normal;
-		text-transform: none;
-
-		.row {
-			line-height: 22px;
-
-			.label {
-				margin-right: 10px;
-				color: #D4D5D7;
-			}
-		}
-	}
-}
-</style>
diff --git a/applications/task-work-order/src/views/dataCockpit/components/templateComponents/RealEquipmentTemplate.vue b/applications/task-work-order/src/views/dataCockpit/components/templateComponents/RealEquipmentTemplate.vue
deleted file mode 100644
index 559b6e5..0000000
--- a/applications/task-work-order/src/views/dataCockpit/components/templateComponents/RealEquipmentTemplate.vue
+++ /dev/null
@@ -1,234 +0,0 @@
-<template>
-	<div class="counter-card" @click="emit('click', data)">
-		<!-- 头部 -->
-		<div class="header">
-			<div class="title">
-				<el-tooltip
-					class="title-tooltip"
-					:content="data.name || '-'"
-					placement="top"
-					effect="dark"
-					:show-after="200"
-				>
-					<span class="name">{{ data.name }}</span>
-				</el-tooltip>
-
-				<span class="status">
-					<span :class="getStatusType(data.status)">
-
-					</span>
-
-					<span>
-						{{ getStatusText(data.status) }}
-					</span>
-				</span>
-			</div>
-		</div>
-
-		<!-- 字段区:两列 -->
-		<div class="content">
-			<div class="row">
-				<div class="col">
-					<span class="label">类型</span>
-					{{ data.type }}
-				</div>
-				<div class="col">
-					<span class="label">电量</span>
-					{{ data.batteryPct }}%
-				</div>
-			</div>
-
-			<div class="row">
-				<div class="col">
-					<span class="label">方位角</span>
-					{{ data.azimuth }}°
-				</div>
-				<div class="col">
-					<span class="label">俯仰角</span>
-					{{ data.elevation }}°
-				</div>
-			</div>
-
-			<div class="row">
-				<div class="col">
-					<span class="label">状态</span>
-					{{ getStatusText(data.status) }}
-				</div>
-				<div class="col">
-					<span class="label">厂商</span>
-					{{ data.manufacturer }}
-				</div>
-			</div>
-
-			<div class="row">
-				<div class="col">
-					<span class="label">侦测目标</span>
-					{{ data.detectTargetCnt }}台
-				</div>
-				<div class="col">
-					<span class="label">有效范围</span>
-					{{ data.effectiveRangeKm }}KM
-				</div>
-			</div>
-		</div>
-
-		<!-- 底部按钮 -->
-		<div class="footer">
-			<el-button color="#284FE3" type="primary" @click.stop="emit('history', data)">历史数据</el-button>
-		</div>
-	</div>
-</template>
-
-<script setup>
-defineProps({
-	data: {
-		type: Object,
-		required: true,
-		default: () => ({})
-	}
-})
-
-const emit = defineEmits(['history', 'click'])
-
-const getStatusText = (value) => {
-	if (value === 0 || value === '0') return '在线'
-	if (value === 1 || value === '1') return '离线'
-	if (value === 2 || value === '2') return '故障'
-	if (value === 3 || value === '3') return '报废'
-	return value || '-'
-}
-
-const getStatusType = (value) => {
-	if (value === 0 || value === '0') return 'detecting'
-	if (value === 2 || value === '2') return 'jamming'
-	return ''
-}
-</script>
-
-<style scoped lang="scss">
-.counter-card {
-	margin-top: 10px;
-	padding: 10px;
-	color: #fff;
-	background: #191933;
-	border-radius: 6px 6px 6px 6px;
-
-	&:first-child {
-		margin-top: 0;
-	}
-
-	.header {
-		display: flex;
-		justify-content: space-between;
-		align-items: center;
-
-		.title {
-			display: flex;
-			align-items: center;
-			gap: 6px;
-			min-width: 0;
-
-			.title-tooltip {
-				flex: 1;
-				min-width: 0;
-				display: block;
-			}
-
-			.name {
-				display: inline-block;
-				max-width: 100%;
-				font-family: Source Han Sans CN, Source Han Sans CN;
-				font-weight: bold;
-				font-size: 14px;
-				color: #FFFFFF;
-				text-align: left;
-				font-style: normal;
-				text-transform: none;
-				white-space: nowrap;
-				overflow: hidden;
-				text-overflow: ellipsis;
-			}
-
-			.status {
-				flex-shrink: 0;
-				padding: 0 10px;
-				display: flex;
-				align-items: center;
-				justify-content: space-between;
-				height: 21px;
-
-				background: #303041;
-				border-radius: 10px;
-				gap: 6px;
-
-				span {
-					font-family: Source Han Sans CN, Source Han Sans CN;
-					font-weight: 400;
-					font-size: 10px;
-					color: #FFFFFF;
-					text-align: center;
-					font-style: normal;
-					text-transform: none;
-				}
-
-
-				span.detecting {
-					width: 4px;
-					height: 4px;
-					background: #18FF4A;
-					border-radius: 50%;
-				}
-
-				span.jamming {
-					width: 4px;
-					height: 4px;
-					background: #FF4444;
-					border-radius: 50%;
-				}
-			}
-		}
-	}
-
-	.content {
-		font-family: Source Han Sans CN, Source Han Sans CN;
-		font-weight: 400;
-		font-size: 12px;
-		color: #9E9EBA;
-		text-align: left;
-		font-style: normal;
-		text-transform: none;
-
-		&>div {
-			margin-top: 10px;
-			line-height: 22px;
-
-			span.label {
-				margin-right: 10px;
-				color: #D4D5D7;
-			}
-		}
-
-		.row {
-			display: flex;
-
-			&>div {
-				width: 0;
-				flex: 1;
-			}
-		}
-	}
-
-	.footer {
-		display: flex;
-		align-items: center;
-		justify-content: center;
-		gap: 10px;
-		margin-top: 10px;
-
-		::v-deep(.el-button) {
-			width: 96px;
-			height: 36px;
-		}
-	}
-}
-</style>
diff --git a/applications/task-work-order/src/views/dataCockpit/components/templateComponents/RealTemplate.vue b/applications/task-work-order/src/views/dataCockpit/components/templateComponents/RealTemplate.vue
deleted file mode 100644
index 2e7c9a8..0000000
--- a/applications/task-work-order/src/views/dataCockpit/components/templateComponents/RealTemplate.vue
+++ /dev/null
@@ -1,266 +0,0 @@
-<template>
-	<div class="drone-card">
-		<div class="header">
-			<div class="title">
-				<el-tooltip
-					class="title-tooltip"
-					:content="data.droneName || '-'"
-					placement="top"
-					effect="dark"
-					:show-after="200"
-				>
-					<span class="name">{{ data.droneName }}</span>
-				</el-tooltip>
-				<span class="status">
-					<span :class="getStatusType(data)">
-
-					</span>
-
-					<span>
-						{{ getStatusText(data) }}
-					</span>
-				</span>
-			</div>
-
-			<div class="actions">
-				<span class="star" @click="emit('favorite')">
-					{{ data.isFavorite ? '★' : '☆' }}
-				</span>
-			</div>
-		</div>
-
-		<div class="content">
-			<div>
-				<span class="label">序列号</span>
-				{{ data.droneSerialNo }}
-			</div>
-			<div>
-				<span class="label">数据源</span>
-				{{ getDataSource(data) }}
-			</div>
-			<div>
-				<span class="label">经纬度</span>
-				{{ data.longitude }}, {{ data.latitude }}
-			</div>
-			<div class="row">
-				<div class="col">
-					<span class="label">信号频段</span>
-					{{ formatFrequency(data.signalFreqMhz) }}
-				</div>
-				<div class="col">
-					<span class="label">发现时间</span>
-					{{ getAlarmTime(data.alarmTime) }}
-				</div>
-			</div>
-			<div class="row">
-				<div class="col">
-					<span class="label">飞行高度</span>
-					{{ formatHeight(data.flightHeightM) }}
-				</div>
-				<div class="col">
-					<span class="label">飞行速度</span>
-					{{ formatSpeed(data.flightSpeedMs) }}
-				</div>
-			</div>
-		</div>
-
-		<div class="footer">
-			<el-button class="general" color="#2B2B4C" @click="emit('signal')">信号干扰</el-button>
-			<el-button color="#284FE3" type="primary" @click="emit('counter')">诱导驱离</el-button>
-		</div>
-	</div>
-</template>
-
-<script setup>
-const props = defineProps({
-	data: {
-		type: Object,
-		required: true,
-		default: () => ({})
-	}
-})
-
-const emit = defineEmits(['signal', 'counter', 'favorite'])
-
-const getStatusText = (item) => {
-	const status = item?.flightStatus
-	if (status === 1 || status === '1') return '侦测中'
-	if (status === 2 || status === '2') return '反制中'
-	return status || '侦测中'
-}
-
-const getStatusType = (item) => {
-	const statusText = getStatusText(item)
-	const counterWay = item?.counterWay
-	if (statusText.includes('反制') || counterWay === 2 || counterWay === '2' || counterWay === '诱导驱离') {
-		return 'countering'
-	}
-	return 'detecting'
-}
-
-const getDataSource = (item) => {
-	return item?.deviceName || item?.areaName || '-'
-}
-
-const formatFrequency = (value) => {
-	if (value == null || value === '') return '-'
-	return `${value}MHz`
-}
-
-const getAlarmTime = (value) => {
-	if (!value) return '-'
-	const parts = value.replace('T', ' ').split(' ')
-	return parts[1] || value
-}
-
-const formatHeight = (value) => {
-	if (value == null || value === '') return '-'
-	return `${value}m`
-}
-
-const formatSpeed = (value) => {
-	if (value == null || value === '') return '-'
-	return `${value}m/s`
-}
-</script>
-
-<style scoped lang="scss">
-.drone-card {
-	margin-top: 10px;
-	padding: 10px;
-	color: #fff;
-	background: #191933;
-	border-radius: 6px 6px 6px 6px;
-
-	&:hover {
-		background: #29294D;
-		;
-	}
-
-	&:first-child {
-		margin-top: 0;
-	}
-
-	.header {
-		display: flex;
-		justify-content: space-between;
-		align-items: center;
-
-		.title {
-			display: flex;
-			align-items: center;
-			gap: 6px;
-			min-width: 0;
-
-			.title-tooltip {
-				flex: 1;
-				min-width: 0;
-				display: block;
-			}
-
-			.name {
-				display: inline-block;
-				max-width: 100%;
-				font-family: Source Han Sans CN, Source Han Sans CN;
-				font-weight: bold;
-				font-size: 14px;
-				color: #FFFFFF;
-				text-align: left;
-				font-style: normal;
-				text-transform: none;
-				white-space: nowrap;
-				overflow: hidden;
-				text-overflow: ellipsis;
-			}
-
-			.status {
-				flex-shrink: 0;
-				padding: 0 10px;
-				display: flex;
-				align-items: center;
-				justify-content: space-between;
-				height: 21px;
-
-				background: #303041;
-				border-radius: 10px;
-				gap: 6px;
-
-				span {
-					font-family: Source Han Sans CN, Source Han Sans CN;
-					font-weight: 400;
-					font-size: 10px;
-					color: #FFFFFF;
-					text-align: center;
-					font-style: normal;
-					text-transform: none;
-				}
-
-
-				span.detecting {
-					width: 4px;
-					height: 4px;
-					background: #18FF4A;
-					border-radius: 50%;
-				}
-
-				span.countering {
-					width: 4px;
-					height: 4px;
-					background: #FF4444;
-					border-radius: 50%;
-				}
-			}
-		}
-
-		.actions {
-			display: flex;
-			gap: 8px;
-			font-size: 16px;
-			cursor: pointer;
-		}
-	}
-
-	.content {
-		font-family: Source Han Sans CN, Source Han Sans CN;
-		font-weight: 400;
-		font-size: 12px;
-		color: #9E9EBA;
-		text-align: left;
-		font-style: normal;
-		text-transform: none;
-
-		&>div {
-			margin-top: 10px;
-			line-height: 22px;
-
-			span.label {
-				margin-right: 10px;
-				color: #D4D5D7;
-			}
-		}
-
-		.row {
-			display: flex;
-			justify-content: space-between;
-		}
-	}
-
-	.footer {
-		display: flex;
-		align-items: center;
-		justify-content: center;
-		gap: 10px;
-		margin-top: 10px;
-
-		::v-deep(.el-button) {
-			width: 96px;
-			height: 36px;
-
-			&.general.el-button {
-				border-radius: 4px 4px 4px 4px;
-				border: 1px solid rgba(255, 255, 255, 0.5);
-			}
-		}
-	}
-}
-</style>
diff --git a/applications/task-work-order/src/views/dataCockpit/components/templateComponents/TitleTemplate.vue b/applications/task-work-order/src/views/dataCockpit/components/templateComponents/TitleTemplate.vue
deleted file mode 100644
index 9d3c9b0..0000000
--- a/applications/task-work-order/src/views/dataCockpit/components/templateComponents/TitleTemplate.vue
+++ /dev/null
@@ -1,43 +0,0 @@
-<!--
- * @Author       : yuan
- * @Date         : 2026-01-08 09:32:52
- * @LastEditors  : yuan
- * @LastEditTime : 2026-01-08 09:43:00
- * @FilePath     : \applications\drone-command\src\views\dataCockpit\components\templateComponents\TitleTemplate.vue
- * @Description  : 
- * Copyright 2026 OBKoro1, All Rights Reserved. 
- * 2026-01-08 09:32:52
--->
-<template>
-	<div class="title-box">
-		<div class="title">
-			<slot></slot>
-		</div>
-	</div>
-</template>
-
-<script setup>
-
-</script>
-
-<style lang="scss" scoped>
-.title-box {
-	padding-left: 26px;
-	background: url('@/assets/images/dataCockpit/title-bg.png') center / 100% 100% no-repeat;
-	height: 34px;
-
-	.title {
-		height: 25px;
-		line-height: 25px;
-
-		font-family: PangMen; 
-		font-weight: 400;
-		font-size: 18px;
-		color: #FFFFFF;
-		letter-spacing: 1px;
-		text-align: left;
-		font-style: normal;
-		text-transform: none;
-	}
-}
-</style>
\ No newline at end of file
diff --git a/applications/task-work-order/src/views/dataCockpit/index.vue b/applications/task-work-order/src/views/dataCockpit/index.vue
deleted file mode 100644
index 137328a..0000000
--- a/applications/task-work-order/src/views/dataCockpit/index.vue
+++ /dev/null
@@ -1,96 +0,0 @@
-<!--
- * @Author       : yuan
- * @Date         : 2026-01-06 16:35:50
- * @LastEditors  : yuan
- * @LastEditTime : 2026-01-09 16:34:39
- * @FilePath     : \applications\drone-command\src\views\dataCockpit\index.vue
- * @Description  : 
- * Copyright 2026 OBKoro1, All Rights Reserved. 
- * 2026-01-06 16:35:50
--->
-<template>
-  <div class="page-container">
-    <DeviceMapContainer
-      :online-devices="onlineDevices"
-      :left-collapsed="leftCollapsed"
-      container-id="data-cockpit-map"
-    />  
-    <LeftContainer class="left-container" v-model:activeId="leftActiveId" v-model:collapsed="leftCollapsed" />  
-    <RightContainer class="right-container" v-model:collapsed="rightCollapsed" v-model:onlineDevices="onlineDevices" />  
-    <CenterContainer @select-warning="onSelectWarning" @select-equipment="onSelectEquipment" />  
-  </div>
-</template>
-
-<script setup>
-  import DeviceMapContainer from '@/components/map-container/device-map-container.vue';
-  import LeftContainer from './components/LeftContainer.vue';
-  import RightContainer from './components/RightContainer.vue';
-  import CenterContainer from './components/CenterContainer.vue';
-
-  const leftActiveId = ref(1);
-  const leftCollapsed = ref(false);
-
-  const rightCollapsed = ref(false);
-  const onlineDevices = ref([]);
-
-  const onSelectWarning = (type) => {
-    leftActiveId.value = type;
-    leftCollapsed.value = false;
-  };
-
-  const onSelectEquipment = () => {
-    rightCollapsed.value = false;
-  };
-</script>
-
-<style scoped lang="scss">
-  .page-container {
-    position: relative;
-    width: 100%;
-    height: 100%;
-  }
-
-  .left-container,
-  .right-container {
-    position: absolute;
-    top: 96px;
-    bottom: 0;
-    width: 401px;
-    z-index: 9;
-    backdrop-filter: blur(2px);
-
-    ::v-deep(.wrapper) {
-      display: flex;
-      flex-direction: column;
-      position: absolute;
-      top: 17px;
-      bottom: 20px;
-      padding: 20px;
-      width: 300px;
-      background: rgba(0,0,0,0.3);
-      backdrop-filter: blur(16px);
-      border-radius: 10px 10px 10px 10px;
-    }
-  }
-
-  .left-container {
-    top: 96px;
-    left: 0;
-    background: linear-gradient( 270deg, rgba(17,23,34,0) 0%, rgba(17,23,34,0.56) 50%, rgba(17,23,34,0.96) 100%);
-
-    ::v-deep(.wrapper) {
-      left: 17px;
-    }
-  }
-
-  .right-container {
-    top: 54px;
-    right: 0;
-    background: linear-gradient( 270deg, rgba(17,23,34,0.96) 0.16%, rgba(17,23,34,0.56) 57.57%, rgba(4,30,37,0) 100%);
-
-    ::v-deep(.wrapper) {
-      top: 21px;
-      right: 17px;
-    }
-  }
-</style>
diff --git a/applications/task-work-order/src/views/orderView/flyingHand/FlyingHandDialog.vue b/applications/task-work-order/src/views/orderView/flyingHand/FlyingHandDialog.vue
new file mode 100644
index 0000000..61aa55f
--- /dev/null
+++ b/applications/task-work-order/src/views/orderView/flyingHand/FlyingHandDialog.vue
@@ -0,0 +1,278 @@
+<!-- 飞手信息弹窗 -->
+<template>
+	<el-dialog
+		v-model="dialogVisible"
+		:title="dialogTitle"
+		class="gd-dialog"
+		destroy-on-close
+		:close-on-click-modal="false"
+	>
+		<el-form
+			ref="formRef"
+			:model="formData"
+			:rules="rules"
+			class="gd-form"
+			label-width="160px"
+		>
+			<el-row>
+				<el-col :span="12">
+					<el-form-item label="飞手姓名" prop="flyerName">
+						<el-select
+							class="gd-select"
+							popper-class="gd-select-popper"
+							v-model="formData.flyerName"
+							placeholder="请选择飞手姓名"
+							:disabled="dialogType === 'view'"
+						>
+							<el-option
+								v-for="item in flyingHandList"
+								:key="item.id"
+								:label="item.name"
+								:value="item.id"
+							/>
+						</el-select>
+					</el-form-item>
+				</el-col>
+				<el-col :span="12">
+					<el-form-item label="飞手电话" prop="flyerPhone">
+						<el-input
+						class="gd-input"
+							v-model="formData.flyerPhone"
+							placeholder="请输入飞手电话"
+							:disabled="dialogType === 'view'"
+						/>
+					</el-form-item>
+				</el-col>
+				<el-col :span="12">
+					<el-form-item label="飞行时长(小时)" prop="flightHours">
+						<el-input
+							class="gd-input"
+							v-model.number="formData.flightHours"
+							placeholder="请输入飞行时长"
+							type="number"
+							:disabled="dialogType === 'view'"
+						/>
+					</el-form-item>
+				</el-col>
+				<el-col :span="12">
+					<el-form-item label="技术特长" prop="technicalStrength">
+						<el-select
+							class="gd-select"
+							popper-class="gd-select-popper"
+							v-model="formData.technicalStrength"
+							placeholder="请选择技术特长"
+							:disabled="dialogType === 'view'"
+						>
+							<el-option
+								v-for="item in dictObj.technicalStrength"
+								:key="item.dictKey"
+								:label="item.dictValue"
+								:value="item.dictKey"
+							/>
+						</el-select>
+					</el-form-item>
+				</el-col>
+				<el-col :span="12">
+					<el-form-item label="擅长机型" prop="skilledUavType">
+						<el-select
+							class="gd-select"
+							popper-class="gd-select-popper"
+							v-model="formData.skilledUavType"
+							placeholder="请选择擅长机型"
+							clearable
+							@change="handleSearch"
+						>
+							<el-option
+								v-for="item in dictObj.skilledUavType"
+								:key="item.dictKey"
+								:label="item.dictValue"
+								:value="item.dictKey"
+							/>
+				</el-select>
+					</el-form-item>
+				</el-col>
+				<el-col :span="12">
+					<el-form-item label="擅长任务类型" prop="skilledTaskType">
+						<el-select
+							class="gd-select"
+							popper-class="gd-select-popper"
+							v-model="formData.skilledTaskType"
+							placeholder="请选择擅长任务类型"
+							:disabled="dialogType === 'view'"
+						>
+							<el-option
+								v-for="item in dictObj.skilledTaskType"
+								:key="item.dictKey"
+								:label="item.dictValue"
+								:value="item.dictKey"
+							/>
+						</el-select>
+					</el-form-item>
+				</el-col>
+				<el-col :span="24">
+					<el-form-item label="项目经验" prop="projectExperience">
+						<el-input
+							class="gd-input"
+							v-model="formData.projectExperience"
+							placeholder="请输入项目经验"
+							type="textarea"
+							rows="3"
+							:disabled="dialogType === 'view'"
+						/>
+					</el-form-item>
+				</el-col>
+			</el-row>
+		</el-form>
+		<template #footer>
+			<el-button @click="handleClose">取消</el-button>
+			<el-button
+				v-if="dialogType !== 'view'"
+				type="primary"
+				@click="handleSubmit"
+			>
+				确定
+			</el-button>
+		</template>
+	</el-dialog>
+</template>
+<script setup>
+import { ref, reactive, computed, watch, inject } from 'vue'
+import { submitFlyingHand } from './flyingHandApi'
+import { ElMessage } from 'element-plus'
+
+const props = defineProps({
+	modelValue: {
+		type: Boolean,
+		default: false
+	},
+	dialogType: {
+		type: String,
+		default: 'add', // add, edit, view
+		validator: (value) => ['add', 'edit', 'view'].includes(value)
+	},
+	flyingHandData: {
+		type: Object,
+		default: () => ({})
+	}
+})
+
+const dictObj = inject('dictObj') // 擅长任务类型
+
+const flyingHandList = inject('flyingHandList') // 飞手列表
+
+const emit = defineEmits(['update:modelValue', 'confirm'])
+
+const dialogVisible = computed({
+	get: () => props.modelValue,
+	set: (value) => emit('update:modelValue', value)
+})
+
+const dialogTitle = computed(() => {
+	switch (props.dialogType) {
+		case 'add':
+			return '添加飞手信息'
+		case 'edit':
+			return '编辑飞手信息'
+		case 'view':
+			return '查看飞手信息'
+		default:
+			return '飞手信息'
+	}
+})
+
+const formRef = ref(null)
+const formData = reactive({
+	flyerName: '',
+	flyerPhone: '',
+	flightHours: 0,
+	projectExperience: '',
+	technicalStrength: '',
+	skilledUavType: ''
+})
+
+const rules = {
+	flyerName: [
+		{ required: true, message: '请输入飞手姓名', trigger: 'blur' }
+	],
+	flyerPhone: [
+		{ required: false, message: '请输入飞手电话', trigger: 'blur' },
+		{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
+	],
+	flightHours: [
+		{ required: true, message: '请输入飞行时长', trigger: 'blur' },
+		{ type: 'number', min: 0, message: '飞行时长必须大于等于0', trigger: 'blur' }
+	],
+	technicalSkills: [
+		{ required: true, message: '请选择技术特长', trigger: 'change' }
+	],
+	skilledTaskType: [
+		{ required: true, message: '请选择擅长任务类型', trigger: 'change' }
+	],
+	projectExperience: [
+		{ required: true, message: '请输入项目经验', trigger: 'blur' }
+	]
+}
+
+// 监听飞手数据变化,更新表单
+watch(
+	() => props.flyingHandData,
+	(newData) => {
+		if (newData) {
+			Object.assign(formData, newData)
+		}
+	},
+	{ deep: true, immediate: true }
+)
+
+// 处理关闭
+function handleClose() {
+	dialogVisible.value = false
+	resetForm()
+}
+
+// 重置表单
+function resetForm() {
+	formRef.value?.resetFields()
+	// 重置为默认值
+	Object.assign(formData, {
+		name: '',
+		phone: '',
+		flightHours: 0,
+		projectExperience: '',
+		technicalSkills: '',
+		technicalExpertise: '',
+		goodAtAircraftType: '',
+		goodAtTaskType: ''
+	})
+}
+
+// 处理提交
+async function handleSubmit() {
+	const isValid = await formRef.value?.validate()
+	if (!isValid) {
+		return
+	}
+
+	try {
+		if (props.dialogType === 'add') {
+			await submitFlyingHand(formData)
+			ElMessage.success('添加成功')
+		} else if (props.dialogType === 'edit') {
+			await submitFlyingHand(formData)
+			ElMessage.success('编辑成功')
+		}
+		emit('confirm')
+		dialogVisible.value = false
+		resetForm()
+	} catch (error) {
+		ElMessage.error('操作失败')
+	}
+}
+</script>
+<style scoped lang="scss">
+.gd-form {
+	.el-form-item {
+		margin-bottom: 20px;
+	}
+}
+</style>
\ No newline at end of file
diff --git a/applications/task-work-order/src/views/orderView/flyingHand/flyingHandApi.js b/applications/task-work-order/src/views/orderView/flyingHand/flyingHandApi.js
new file mode 100644
index 0000000..3d38189
--- /dev/null
+++ b/applications/task-work-order/src/views/orderView/flyingHand/flyingHandApi.js
@@ -0,0 +1,37 @@
+import request from '@/axios'
+
+// 飞手分页查询
+export function getFlyingHandPage(params) {
+    return request({
+        url: '/drone-gd/flyer/gdFlyer/page',
+        method: 'get',
+        params
+    })
+}
+
+// 飞手提交
+export function submitFlyingHand(data) {
+    return request({
+        url: '/drone-gd/flyer/gdFlyer/submit',
+        method: 'post',
+        data
+    })
+}
+
+// 飞手详情查询
+export function getFlyingHandDetail(params) {
+    return request({
+        url: '/drone-gd/flyer/gdFlyer/detail',
+        method: 'get',
+        params
+    })
+}
+
+// 飞手删除
+export function removeFlyingHand(data) {
+    return request({
+        url: '/drone-gd/flyer/gdFlyer/remove',
+        method: 'post',
+        data
+    })
+}
diff --git a/applications/task-work-order/src/views/orderView/flyingHand/index.vue b/applications/task-work-order/src/views/orderView/flyingHand/index.vue
index 60a2c0d..ac5e9d1 100644
--- a/applications/task-work-order/src/views/orderView/flyingHand/index.vue
+++ b/applications/task-work-order/src/views/orderView/flyingHand/index.vue
@@ -1,7 +1,223 @@
+<!-- 飞手信息管理 -->
 <template>
-	<basic-container>飞手</basic-container>
+	<basic-container>
+		<el-form ref="queryParamsRef" :model="searchParams" class="gd-search-form">
+			<el-form-item label="飞手姓名" prop="flyerName">
+				<el-input
+					class="gd-input gray"
+					v-model="searchParams.flyerName"
+					placeholder="请输入飞手姓名"
+					clearable
+					@clear="handleSearch"
+				/>
+			</el-form-item>
+
+			<el-form-item label="擅长机型" prop="skilledUavType">
+				<el-select
+					class="gd-select gray"
+					popper-class="gd-select-popper"
+					v-model="searchParams.skilledUavType"
+					placeholder="请选择"
+					clearable
+					@change="handleSearch"
+				>
+					<el-option
+						v-for="item in dictObj.skilledUavType"
+						:key="item.dictKey"
+						:label="item.dictValue"
+						:value="item.dictKey"
+					/>
+				</el-select>
+			</el-form-item>
+
+			<el-form-item class="gd-search-actions">
+				<el-button :icon="RefreshRight" @click="resetForm"></el-button>
+				<el-button class="search-btn" :icon="Search" @click="handleSearch"></el-button>
+			</el-form-item>
+		</el-form>
+
+        <div class="gd-table-toolbar">
+			<el-button :icon="Plus" color="#4C34FF" type="primary" @click="addFlyingHand">新增</el-button>
+		</div>
+
+		<div class="gd-table-container" v-loading="loading">
+			<div class="gd-table-content gd-table-content-bg">
+				<el-table class="gd-table" :data="list">
+					<el-table-column type="index" width="64" label="序号" />
+					<el-table-column prop="flyerName" show-overflow-tooltip label="飞手姓名" />
+					<el-table-column prop="flyerPhone" show-overflow-tooltip label="飞手电话" />
+					<el-table-column prop="flightHours" show-overflow-tooltip label="飞行时长(小时)" />
+					<el-table-column prop="projectExperience" show-overflow-tooltip label="项目经验" />
+					<el-table-column prop="technicalStrength" show-overflow-tooltip label="技术特长">
+						<template v-slot="{ row }">
+							{{ getDictLabel(row.technicalStrength, dictObj.technicalStrength) }}
+						</template>
+					</el-table-column>
+					<el-table-column prop="skilledUavType" show-overflow-tooltip label="擅长机型" />
+					<el-table-column prop="skilledTaskType" show-overflow-tooltip label="擅长任务类型">
+						<template v-slot="{ row }">
+							{{ getDictLabel(row.skilledTaskType, dictObj.skilledTaskType) }}
+						</template>
+					</el-table-column>
+					<el-table-column label="操作" class-name="operation-btns" width="180">
+						<template v-slot="{ row }">
+							<el-link @click="viewFlyingHand(row)">查看</el-link>
+							<el-link type="primary" @click="editFlyingHand(row)">编辑</el-link>
+							<el-link type="danger" @click="deleteFlyingHand(row.id)">删除</el-link>
+						</template>
+					</el-table-column>
+				</el-table>
+			</div>
+
+			<div class="gd-pagination-parent">
+				<el-pagination
+					popper-class="gd-select-popper"
+					v-model:current-page="searchParams.current"
+					v-model:page-size="searchParams.size"
+					layout="total, prev, pager, next, sizes"
+					:total="total"
+					@change="getList"
+				/>
+			</div>
+		</div>
+
+		<!-- 飞手信息弹窗 -->
+		<FlyingHandDialog
+			ref="flyingHandDialogRef"
+			v-model="dialogVisible"
+			:dialog-type="dialogType"
+			:flying-hand-data="flyingHandData"
+			@confirm="handleConfirm"
+		/>
+	</basic-container>
 </template>
+<script setup>
+import { Search, RefreshRight, Plus } from '@element-plus/icons-vue'
+import { onMounted, ref, nextTick } from 'vue'
+import { getDictionaryByCode } from '@/api/system/dictbiz'
+import { getDictLabel } from '@ztzf/utils'
+import { getFlyingHandPage, removeFlyingHand } from './flyingHandApi'
+import { deviceFlyerApi } from '@/api/zkxt'
+import FlyingHandDialog from './FlyingHandDialog.vue'
+import { prod } from 'mathjs'
 
-<script setup></script>
+// 初始化查询参数
+const initSearchParams = () => ({
+	flyerName: '', // 飞手姓名
+	skilledUavType: '', // 擅长任务类型
+	current: 1, // 当前页
+	size: 10, // 每页大小
+})
+const searchParams = ref(initSearchParams()) // 查询参数
+const total = ref(0) // 总条数
+const loading = ref(true) // 列表加载中
+const list = ref([]) // 列表数据
+const queryParamsRef = ref(null) // 查询表单实例
+const flyingHandDialogRef = ref(null)
+const dialogVisible = ref(false)
+const dialogType = ref('add') // add, edit, view
+const flyingHandData = ref({})
 
-<style scoped lang="scss"></style>
+const dictObj = ref({
+	skilledTaskType: [], // 擅长任务类型
+	skilledUavType: [], // 擅长机型
+	technicalStrength: [] // 技术特长
+})
+
+provide('dictObj', dictObj)
+
+// 获取列表
+async function getList() {
+	loading.value = true
+	try {
+		const res = await getFlyingHandPage(searchParams.value)
+		list.value = res?.data?.data?.records ?? []
+		total.value = res?.data?.data?.total ?? 0
+	} finally {
+		loading.value = false
+	}
+}
+
+// 查询
+function handleSearch() {
+	searchParams.value.current = 1
+	getList()
+}
+
+// 重置查询
+function resetForm() {
+	queryParamsRef.value?.resetFields()
+	searchParams.value.current = 1
+	getList()
+}
+
+// 添加飞手
+function addFlyingHand() {
+	dialogType.value = 'add'
+	flyingHandData.value = {}
+	dialogVisible.value = true
+}
+
+// 查看飞手
+function viewFlyingHand(row) {
+	dialogType.value = 'view'
+	flyingHandData.value = { ...row }
+	dialogVisible.value = true
+}
+
+// 编辑飞手
+function editFlyingHand(row) {
+	dialogType.value = 'edit'
+	flyingHandData.value = { ...row }
+	dialogVisible.value = true
+}
+
+// 删除飞手
+function deleteFlyingHand(id) {
+	ElMessageBox.confirm('确定要删除该飞手信息吗?', '提示', {
+		confirmButtonText: '确定',
+		cancelButtonText: '取消',
+		type: 'warning',
+	}).then(async () => {
+		try {
+			await removeFlyingHand(id)
+			ElMessage.success('删除成功')
+			getList()
+		} catch (error) {
+			ElMessage.error('删除失败')
+		}
+	}).catch(() => {
+		// 取消删除
+	})
+}
+
+// 处理弹窗确认
+function handleConfirm() {
+	dialogVisible.value = false
+	getList()
+}
+
+// 获取字典
+function getDictList() {
+	getDictionaryByCode('skilledTaskType,skilledUavType,technicalStrength').then(res => {
+		dictObj.value = res.data.data
+	})
+}
+
+// 获取飞手列表
+const flyingHandList = ref([])
+function getFlyingHandList() {
+    deviceFlyerApi({ current: 1, size: 10 }).then(res => {
+        flyingHandList.value = res?.data?.data ?? []
+    })
+}
+
+provide('flyingHandList', flyingHandList)
+
+onMounted(() => {
+	getList()
+	getDictList()
+    getFlyingHandList()
+})
+</script>
+<style scoped lang="scss"></style>
\ No newline at end of file

--
Gitblit v1.9.3