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|
+|  areaId|区域id||false|integer(int64)||
+|  isAreaSelect|是否区域选择(1过滤)||false|integer(int32)||
+|  isTrack|是否出入库 1已出库 2未出库||false|integer(int32)||
+|  polygons|多边形WKT集合||false|array|string|
+|  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|
-|  areaCode|区域编码|string||
-|  defenseLeader|防控负责人|string||
-|  defenseSceneId|关联场景配置ID|integer(int64)||
-|  defenseSceneName|场景配置名称|string||
-|  effectiveDateEnd|有效时间-结束|string(date-time)||
-|  effectiveDateStart|有效时间-开始|string(date-time)||
-|  id||integer(int64)||
-|  latitude|指挥点纬度|number(double)||
-|  leaderPhone|防控负责人电话|string||
-|  longitude|指挥点经度|number(double)||
+|data|承载数据|array|FwDeviceVO|
+|  azimuth|方位角 0-360°|integer(int32)||
+|  batteryPct|电量百分比 0-100|integer(int32)||
+|  belongDept|所属部门id|integer(int64)||
+|  belongDeptName|所属部门名称|string||
+|  charger|负责人|string||
+|  contactPhone|联系电话|string||
+|  createDept|创建部门|integer(int64)||
+|  createTime|创建时间|string(date-time)||
+|  createUser|创建人|integer(int64)||
+|  detectTargetCnt|侦测目标数量|integer(int32)||
+|  deviceAtt|设备属性(无线电/光电/雷达)|string||
+|  deviceModel|型号|string||
+|  deviceName|设备名称|string||
+|  deviceSn|设备sn|string||
+|  deviceSpecification|规格|string||
+|  deviceType|设备类型(察打一体/便捷侦测箱/反制枪)|string||
+|  effectiveRangeKm|有效范围 km|number||
+|  elevation|俯仰角 -90-+90°|integer(int32)||
+|  id|主键|integer(int64)||
+|  isShared|是否被共享给当前机构|boolean||
+|  latitude|纬度|string||
+|  longitude|经度|string||
+|  manufacturer|生产厂商|string||
+|  outTarget|出库去向|string||
+|  purpose|用途|string||
+|  remark|备注|string||
|  sceneName|场景名称|string||
+|  shareId|共享记录ID|integer(int64)||
+|  source|来源|string||
+|  status|运行状态(0:在线/1:离线/2:故障/3:报废)|integer(int32)||
+|  trackStatus|设备出入库状态 0未出库1已出库|integer(int32)||
+|  updateTime|更新时间|string(date-time)||
+|  updateUser|更新人|integer(int64)||
+|  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