From 2d40c08a9358a1fc1183a45fa806d5e433822e21 Mon Sep 17 00:00:00 2001
From: shuishen <1109946754@qq.com>
Date: Mon, 02 Feb 2026 11:41:16 +0800
Subject: [PATCH] feat:区域划分新增根据绘制区域筛选设备处理

---
 applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue |  166 ++++++++++++++++++++++++++-
 applications/drone-command/src/views/api.txt                             |  122 +++++++++++++++----
 applications/drone-command/env/.env.development                          |    4 
 applications/drone-command/src/views/areaManage/partition/1.json         |    0 
 applications/drone-command/src/views/basicManage/deviceStock/fwDevice.js |    9 +
 5 files changed, 262 insertions(+), 39 deletions(-)

diff --git a/applications/drone-command/env/.env.development b/applications/drone-command/env/.env.development
index 91e13eb..3388898 100644
--- a/applications/drone-command/env/.env.development
+++ b/applications/drone-command/env/.env.development
@@ -2,7 +2,7 @@
  # @Author       : yuan
  # @Date         : 2026-01-31 14:34:31
  # @LastEditors  : yuan
- # @LastEditTime : 2026-01-31 15:18:17
+ # @LastEditTime : 2026-02-02 11:01:46
  # @FilePath     : \applications\drone-command\env\.env.development
  # @Description  : 
  # Copyright 2026 OBKoro1, All Rights Reserved. 
@@ -16,7 +16,7 @@
 
 #开发环境代理地址(推荐本地新建文件 .env.development.local 来进行覆盖)
 # VITE_APP_URL=https://wrj.shuixiongit.com/api
-VITE_APP_URL=http://192.168.1.33
+VITE_APP_URL=http://192.168.1.168
 
 #新大屏地址
 VITE_APP_DASHBOARD_URL='https://wrj.shuixiongit.com/command-center-dashboard/'
diff --git a/applications/drone-command/src/views/api.txt b/applications/drone-command/src/views/api.txt
index 348cc4e..405987f 100644
--- a/applications/drone-command/src/views/api.txt
+++ b/applications/drone-command/src/views/api.txt
@@ -1,22 +1,36 @@
 
 
-## 列表
+## 列表-多边形过滤
 
 
-**接口地址**:`/area/fwDefenseSceneManage/list`
+**接口地址**:`/device/fwDevice/listByPolygons`
 
 
-**请求方式**:`GET`
+**请求方式**:`POST`
 
 
-**请求数据类型**:`application/x-www-form-urlencoded`
+**请求数据类型**:`application/json`
 
 
 **响应数据类型**:`*/*`
 
 
-**接口描述**:<p>传入time</p>
+**接口描述**:<p>传入isAreaSelect、areaId、status、isTrack、polygons</p>
 
+
+
+**请求示例**:
+
+
+```javascript
+{
+  "areaId": 0,
+  "isAreaSelect": 0,
+  "isTrack": 0,
+  "polygons": [],
+  "status": 0
+}
+```
 
 
 **请求参数**:
@@ -27,7 +41,12 @@
 
 | 参数名称 | 参数说明 | 请求类型    | 是否必须 | 数据类型 | schema |
 | -------- | -------- | ----- | -------- | -------- | ------ |
-|time|时间筛选(yyyy-MM-dd HH:mm:ss)|query|false|string(date-time)||
+|query|设备列表多边形查询参数|body|true|FwDevicePolygonQueryDTO|FwDevicePolygonQueryDTO|
+|&emsp;&emsp;areaId|区域id||false|integer(int64)||
+|&emsp;&emsp;isAreaSelect|是否区域选择(1过滤)||false|integer(int32)||
+|&emsp;&emsp;isTrack|是否出入库 1已出库 2未出库||false|integer(int32)||
+|&emsp;&emsp;polygons|多边形WKT集合||false|array|string|
+|&emsp;&emsp;status|状态||false|integer(int32)||
 
 
 **响应状态**:
@@ -35,7 +54,8 @@
 
 | 状态码 | 说明 | schema |
 | -------- | -------- | ----- | 
-|200|OK|R«List«FwDefenseSceneManageVO»»|
+|200|OK|R«List«FwDeviceVO»»|
+|201|Created||
 |401|Unauthorized||
 |403|Forbidden||
 |404|Not Found||
@@ -47,18 +67,41 @@
 | 参数名称 | 参数说明 | 类型 | schema |
 | -------- | -------- | ----- |----- | 
 |code|状态码|integer(int32)|integer(int32)|
-|data|承载数据|array|FwDefenseSceneManageVO|
-|&emsp;&emsp;areaCode|区域编码|string||
-|&emsp;&emsp;defenseLeader|防控负责人|string||
-|&emsp;&emsp;defenseSceneId|关联场景配置ID|integer(int64)||
-|&emsp;&emsp;defenseSceneName|场景配置名称|string||
-|&emsp;&emsp;effectiveDateEnd|有效时间-结束|string(date-time)||
-|&emsp;&emsp;effectiveDateStart|有效时间-开始|string(date-time)||
-|&emsp;&emsp;id||integer(int64)||
-|&emsp;&emsp;latitude|指挥点纬度|number(double)||
-|&emsp;&emsp;leaderPhone|防控负责人电话|string||
-|&emsp;&emsp;longitude|指挥点经度|number(double)||
+|data|承载数据|array|FwDeviceVO|
+|&emsp;&emsp;azimuth|方位角 0-360°|integer(int32)||
+|&emsp;&emsp;batteryPct|电量百分比 0-100|integer(int32)||
+|&emsp;&emsp;belongDept|所属部门id|integer(int64)||
+|&emsp;&emsp;belongDeptName|所属部门名称|string||
+|&emsp;&emsp;charger|负责人|string||
+|&emsp;&emsp;contactPhone|联系电话|string||
+|&emsp;&emsp;createDept|创建部门|integer(int64)||
+|&emsp;&emsp;createTime|创建时间|string(date-time)||
+|&emsp;&emsp;createUser|创建人|integer(int64)||
+|&emsp;&emsp;detectTargetCnt|侦测目标数量|integer(int32)||
+|&emsp;&emsp;deviceAtt|设备属性(无线电/光电/雷达)|string||
+|&emsp;&emsp;deviceModel|型号|string||
+|&emsp;&emsp;deviceName|设备名称|string||
+|&emsp;&emsp;deviceSn|设备sn|string||
+|&emsp;&emsp;deviceSpecification|规格|string||
+|&emsp;&emsp;deviceType|设备类型(察打一体/便捷侦测箱/反制枪)|string||
+|&emsp;&emsp;effectiveRangeKm|有效范围 km|number||
+|&emsp;&emsp;elevation|俯仰角 -90-+90°|integer(int32)||
+|&emsp;&emsp;id|主键|integer(int64)||
+|&emsp;&emsp;isShared|是否被共享给当前机构|boolean||
+|&emsp;&emsp;latitude|纬度|string||
+|&emsp;&emsp;longitude|经度|string||
+|&emsp;&emsp;manufacturer|生产厂商|string||
+|&emsp;&emsp;outTarget|出库去向|string||
+|&emsp;&emsp;purpose|用途|string||
+|&emsp;&emsp;remark|备注|string||
 |&emsp;&emsp;sceneName|场景名称|string||
+|&emsp;&emsp;shareId|共享记录ID|integer(int64)||
+|&emsp;&emsp;source|来源|string||
+|&emsp;&emsp;status|运行状态(0:在线/1:离线/2:故障/3:报废)|integer(int32)||
+|&emsp;&emsp;trackStatus|设备出入库状态 0未出库1已出库|integer(int32)||
+|&emsp;&emsp;updateTime|更新时间|string(date-time)||
+|&emsp;&emsp;updateUser|更新人|integer(int64)||
+|&emsp;&emsp;workMode|工作模式:1.侦测中/2.信号干扰中/3.诱导驱离中/4.待机|string||
 |msg|返回消息|string||
 |success|是否成功|boolean||
 
@@ -69,17 +112,40 @@
 	"code": 0,
 	"data": [
 		{
-			"areaCode": "",
-			"defenseLeader": "",
-			"defenseSceneId": 0,
-			"defenseSceneName": "",
-			"effectiveDateEnd": "",
-			"effectiveDateStart": "",
+			"azimuth": 0,
+			"batteryPct": 0,
+			"belongDept": 0,
+			"belongDeptName": "",
+			"charger": "",
+			"contactPhone": "",
+			"createDept": 0,
+			"createTime": "",
+			"createUser": 0,
+			"detectTargetCnt": 0,
+			"deviceAtt": "",
+			"deviceModel": "",
+			"deviceName": "",
+			"deviceSn": "",
+			"deviceSpecification": "",
+			"deviceType": "",
+			"effectiveRangeKm": 0,
+			"elevation": 0,
 			"id": 0,
-			"latitude": 0,
-			"leaderPhone": "",
-			"longitude": 0,
-			"sceneName": ""
+			"isShared": true,
+			"latitude": "",
+			"longitude": "",
+			"manufacturer": "",
+			"outTarget": "",
+			"purpose": "",
+			"remark": "",
+			"sceneName": "",
+			"shareId": 0,
+			"source": "",
+			"status": 0,
+			"trackStatus": 0,
+			"updateTime": "",
+			"updateUser": 0,
+			"workMode": ""
 		}
 	],
 	"msg": "",
diff --git a/applications/drone-command/src/views/areaManage/partition/1.json b/applications/drone-command/src/views/areaManage/partition/1.json
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/applications/drone-command/src/views/areaManage/partition/1.json
diff --git a/applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue b/applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue
index 1af5da2..a2ee6e4 100644
--- a/applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue
+++ b/applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue
@@ -249,7 +249,7 @@
 import * as Cesium from 'cesium'
 import { AREA_TYPE_STYLE_MAP, BUFFER_LEVEL_STYLES, DEFAULT_AREA_STYLE } from '@ztzf/constants'
 import { fwPoliceStationListApi } from '@/views/areaManage/precinctInfo/precinctInfoApi'
-import { fwDeviceListApi } from '@/views/basicManage/deviceStock/fwDevice'
+import { fwDeviceListByPolygonsApi } from '@/views/basicManage/deviceStock/fwDevice'
 import polygonIcon from '@/assets/images/areaMap/polygon.png'
 import rectIcon from '@/assets/images/areaMap/rect.png'
 import ellipseIcon from '@/assets/images/areaMap/ellipse.png'
@@ -307,8 +307,11 @@
 const deviceRangePrimitiveMap = new Map()
 const activeToolMode = ref('')
 const suppressDeviceFly = ref(false)
+const isDialogInitializing = ref(false)
 let lastSelectedDeviceIds = []
 let deviceRangeRenderToken = 0
+let deviceListTimer = null
+let deviceListRequestToken = 0
 
 const rules = {
 	areaName: fieldRules(true, 50),
@@ -411,6 +414,10 @@
 	() => visible.value,
 	val => {
 		if (!val) {
+			if (deviceListTimer) {
+				clearTimeout(deviceListTimer)
+				deviceListTimer = null
+			}
 			viewEntity && viewer?.entities?.remove(viewEntity)
 			viewEntity = null
 			clearActiveTool()
@@ -425,6 +432,14 @@
 	val => {
 		updateActiveShapeAreaType(val)
 	}
+)
+watch(
+	() => shapeList.value,
+	() => {
+		if (isDialogInitializing.value) return
+		queueDeviceListRefresh()
+	},
+	{ deep: true }
 )
 
 // 加载详情
@@ -474,6 +489,98 @@
 		viewer.scene.primitives.remove(primitive)
 	}
 	deviceRangePrimitiveMap.clear()
+}
+
+function clearDeviceListState () {
+	deviceOptions.value = []
+	selectedDeviceRows.value = []
+	formData.value.deviceIds = ''
+	if (deviceTableRef.value) {
+		deviceTableRef.value.clearSelection()
+	}
+}
+
+function formatWktNumber (value) {
+	if (!Number.isFinite(Number(value))) return null
+	return Number(value).toFixed(6)
+}
+
+function isSameWktPoint (left, right) {
+	if (!left || !right) return false
+	return Math.abs(left.lat - right.lat) < 0.000001 && Math.abs(left.lng - right.lng) < 0.000001
+}
+
+function buildPolygonWktFromPoints (points) {
+	if (!Array.isArray(points) || points.length < 3) return null
+	const ring = points
+		.map(resolveLngLatPoint)
+		.filter(Boolean)
+		.map(point => ({ lng: point.lng, lat: point.lat }))
+	if (ring.length < 3) return null
+	const first = ring[0]
+	const last = ring[ring.length - 1]
+	if (!isSameWktPoint(first, last)) {
+		ring.push({ ...first })
+	}
+	const coordText = ring
+		.map(point => {
+			const lng = formatWktNumber(point.lng)
+			const lat = formatWktNumber(point.lat)
+			if (lat === null || lng === null) return null
+			return `${lng} ${lat}`
+		})
+		.filter(Boolean)
+		.join(', ')
+	if (!coordText) return null
+	return `POLYGON((${coordText}))`
+}
+
+function buildEllipseWktFromMeta (center, semiMajor, semiMinor) {
+	if (!viewer) return null
+	const resolvedCenter = resolveLngLatPoint(center)
+	if (!resolvedCenter) return null
+	if (!Number.isFinite(Number(semiMajor)) || !Number.isFinite(Number(semiMinor))) return null
+	const centerCartesian = Cesium.Cartesian3.fromDegrees(
+		resolvedCenter.lng,
+		resolvedCenter.lat,
+		resolvedCenter.height || 0
+	)
+	const positions = buildEllipsePositions(centerCartesian, Number(semiMajor), Number(semiMinor))
+	if (!positions.length) return null
+	const points = positions
+		.map(item => {
+			const converted = cartesian3Convert(item, viewer)
+			return { lng: converted.longitude, lat: converted.latitude }
+		})
+		.filter(Boolean)
+	return buildPolygonWktFromPoints(points)
+}
+
+function resolveShapePolygonsWkt () {
+	const polygons = []
+	shapeList.value.forEach(shape => {
+		if (!shape) return
+		if (shape.drawType === 'buffer') {
+			const radii = Array.isArray(shape?.meta?.bufferRadii)
+				? shape.meta.bufferRadii.filter(item => Number.isFinite(item) && item > 0)
+				: []
+			const maxRadius = radii.length ? Math.max(...radii) : null
+			const wkt = buildEllipseWktFromMeta(shape?.meta?.center, maxRadius, maxRadius)
+			if (wkt) polygons.push(wkt)
+			return
+		}
+		if (shape.drawType === 'ellipse') {
+			const wkt = buildEllipseWktFromMeta(shape?.meta?.center, shape?.meta?.semiMajor, shape?.meta?.semiMinor)
+			if (wkt) {
+				polygons.push(wkt)
+				return
+			}
+		}
+		const points = getShapeDisplayPoints(shape)
+		const wkt = buildPolygonWktFromPoints(points)
+		if (wkt) polygons.push(wkt)
+	})
+	return polygons
 }
 
 async function renderDeviceRangeSingle (device) {
@@ -1075,12 +1182,44 @@
 }
 
 // 获取设备列表
-async function getDeviceList () {
-	if (deviceOptions.value.length) return
-	const res = await fwDeviceListApi({ isAreaSelect: 1, areaId: formData.value.id, isTrack: 1 })
+function queueDeviceListRefresh () {
+	if (!visible.value) return
+	if (deviceListTimer) {
+		clearTimeout(deviceListTimer)
+	}
+	deviceListTimer = setTimeout(() => {
+		deviceListTimer = null
+		void getDeviceListByPolygons()
+	}, 200)
+}
+
+async function getDeviceListByPolygons () {
+	if (!visible.value) return
+	const polygons = resolveShapePolygonsWkt()
+	if (!polygons.length) {
+		clearDeviceListState()
+		return
+	}
+	deviceListRequestToken += 1
+	const requestToken = deviceListRequestToken
+	const res = await fwDeviceListByPolygonsApi({
+		isAreaSelect: 1,
+		isTrack: 1,
+		polygons,
+	})
+	if (requestToken !== deviceListRequestToken) return
 	deviceOptions.value = res?.data?.data ?? []
-	if (dialogMode.value === 'view') {
-		deviceOptions.value = deviceOptions.value.filter(item => formData.value.deviceIds.includes(item.id))
+	if (dialogMode.value === 'view' && formData.value.deviceIds) {
+		const targetIds = formData.value.deviceIds.split(',')
+		deviceOptions.value = deviceOptions.value.filter(item => targetIds.includes(String(item.id)))
+	}
+	await nextTick()
+	const prevSuppressDeviceFly = suppressDeviceFly.value
+	suppressDeviceFly.value = true
+	syncDeviceSelection()
+	suppressDeviceFly.value = prevSuppressDeviceFly
+	if (!readonly.value) {
+		renderDeviceRanges(selectedDeviceRows.value)
 	}
 }
 
@@ -1114,14 +1253,16 @@
 
 // 同步选择状态
 function syncDeviceSelection () {
+	if (isDialogInitializing.value && !deviceOptions.value.length) return
 	const rows = []
-	const arr = formData.value.deviceIds.split(',')
+	const arr = formData.value.deviceIds ? formData.value.deviceIds.split(',') : []
 	deviceOptions.value.forEach(row => {
-		if (arr.includes(row.id)) {
+		if (arr.includes(String(row.id))) {
 			rows.push(row)
 		}
 	})
 	selectedDeviceRows.value = rows
+	formData.value.deviceIds = rows.map(item => item.id).join(',')
 	if (readonly.value) {
 		renderDeviceRanges(selectedDeviceRows.value)
 		return
@@ -1135,9 +1276,11 @@
 
 // 打开弹框
 async function open ({ mode, row } = {}) {
+	isDialogInitializing.value = true
 	dialogMode.value = mode || 'add'
 	formData.value = dialogMode.value === 'add' ? initForm() : row
 	selectedDeviceRows.value = []
+	deviceOptions.value = []
 	shapeList.value = []
 	activeShapeId.value = null
 	activeAreaType.value = ''
@@ -1149,7 +1292,6 @@
 	viewEntity && viewer?.entities?.remove(viewEntity)
 	viewEntity = null
 	clearActiveTool()
-	await getDeviceList()
 	if (dialogMode.value === 'add') {
 		mapRef.value?.zoomToAdminBoundary?.()
 		// default no draw mode
@@ -1169,6 +1311,8 @@
 	if (!readonly.value) {
 		renderDeviceRanges(selectedDeviceRows.value)
 	}
+	isDialogInitializing.value = false
+	queueDeviceListRefresh()
 }
 
 onMounted(() => {
@@ -1176,6 +1320,10 @@
 })
 
 onBeforeUnmount(() => {
+	if (deviceListTimer) {
+		clearTimeout(deviceListTimer)
+		deviceListTimer = null
+	}
 	hideTypePanel()
 })
 
diff --git a/applications/drone-command/src/views/basicManage/deviceStock/fwDevice.js b/applications/drone-command/src/views/basicManage/deviceStock/fwDevice.js
index ed1c2c7..c385441 100644
--- a/applications/drone-command/src/views/basicManage/deviceStock/fwDevice.js
+++ b/applications/drone-command/src/views/basicManage/deviceStock/fwDevice.js
@@ -18,6 +18,15 @@
 	})
 }
 
+// 多边形过滤设备列表
+export const fwDeviceListByPolygonsApi = data => {
+	return request({
+		url: `/drone-fw/device/fwDevice/listByPolygons`,
+		method: 'post',
+		data,
+	})
+}
+
 // 增加或更新
 export const fwDeviceSubmitApi = data => {
 	return request({

--
Gitblit v1.9.3