From d85d2337a8862d96bf7a48019fddf356a27034cd Mon Sep 17 00:00:00 2001
From: 张含笑 <zhx18749296735@163.com>
Date: Fri, 30 Jan 2026 10:08:22 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master'

---
 applications/drone-command/src/assets/images/areaMap/buffer.png                                   |    0 
 applications/drone-command/src/views/areaManage/partition/FormDiaLog copy.vue                     |  671 ------------
 applications/drone-command/src/styles/common/cockpit.scss                                         |    9 
 applications/drone-command/src/views/areaManage/sceneConfig/index.vue                             |    7 
 applications/drone-command/src/views/areaManage/partition/shapeTools/edit/EditBufferCircleTool.js |   45 
 applications/drone-command/src/views/areaManage/sceneManage/index.vue                             |  195 +++
 applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue                          | 1114 +++++++++++++++++++--
 applications/drone-command/src/assets/images/areaMap/ellipse.png                                  |    0 
 applications/drone-command/src/assets/images/areaMap/polygon.png                                  |    0 
 applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/ViewDiaLog.vue     |   13 
 applications/drone-command/src/views/areaManage/partition/shapeTools/edit/EditPolygonTool.js      |    2 
 applications/drone-command/src/views/areaManage/sceneManage/sceneConfigApi.js                     |   46 
 applications/drone-command/src/views/areaManage/partition/shapeTools/draw/DrawEllipseTool.js      |   11 
 applications/task-work-order/src/views/orderView/orderManage/orderManage/FormDiaLog.vue           |    5 
 applications/drone-command/src/views/areaManage/partition/shapeTools/draw/DrawBufferCircleTool.js |   12 
 applications/drone-command/src/assets/images/areaMap/trash.png                                    |    0 
 applications/drone-command/src/assets/images/areaMap/rect.png                                     |    0 
 applications/drone-command/src/views/areaManage/partition/index.vue                               |   28 
 applications/drone-command/src/views/areaManage/sceneManage/FormDiaLog.vue                        |  356 ++++++
 applications/drone-command/src/views/api.txt                                                      |  149 ++
 applications/drone-command/env/.env.development                                                   |    4 
 applications/drone-command/src/views/areaManage/precinctInfo/FormDiaLog.vue                       |   37 
 applications/drone-command/src/assets/images/areaManage/positionIcon.png                          |    0 
 applications/drone-command/src/views/areaManage/sceneConfig/FormDiaLog.vue                        |  229 +---
 applications/drone-command/src/views/areaManage/partition/shapeTools/edit/EditEllipseTool.js      |  105 +
 25 files changed, 2,033 insertions(+), 1,005 deletions(-)

diff --git a/applications/drone-command/env/.env.development b/applications/drone-command/env/.env.development
index 6524616..1c1032c 100644
--- a/applications/drone-command/env/.env.development
+++ b/applications/drone-command/env/.env.development
@@ -2,7 +2,7 @@
  # @Author       : yuan
  # @Date         : 2026-01-07 14:58:30
  # @LastEditors  : yuan
- # @LastEditTime : 2026-01-28 09:29:05
+ # @LastEditTime : 2026-01-29 15:22:54
  # @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.227
+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/assets/images/areaManage/positionIcon.png b/applications/drone-command/src/assets/images/areaManage/positionIcon.png
new file mode 100644
index 0000000..a569e90
--- /dev/null
+++ b/applications/drone-command/src/assets/images/areaManage/positionIcon.png
Binary files differ
diff --git a/applications/drone-command/src/assets/images/areaMap/buffer.png b/applications/drone-command/src/assets/images/areaMap/buffer.png
new file mode 100644
index 0000000..9c39133
--- /dev/null
+++ b/applications/drone-command/src/assets/images/areaMap/buffer.png
Binary files differ
diff --git a/applications/drone-command/src/assets/images/areaMap/ellipse.png b/applications/drone-command/src/assets/images/areaMap/ellipse.png
new file mode 100644
index 0000000..548ba94
--- /dev/null
+++ b/applications/drone-command/src/assets/images/areaMap/ellipse.png
Binary files differ
diff --git a/applications/drone-command/src/assets/images/areaMap/polygon.png b/applications/drone-command/src/assets/images/areaMap/polygon.png
new file mode 100644
index 0000000..c893971
--- /dev/null
+++ b/applications/drone-command/src/assets/images/areaMap/polygon.png
Binary files differ
diff --git a/applications/drone-command/src/assets/images/areaMap/rect.png b/applications/drone-command/src/assets/images/areaMap/rect.png
new file mode 100644
index 0000000..d00b20e
--- /dev/null
+++ b/applications/drone-command/src/assets/images/areaMap/rect.png
Binary files differ
diff --git a/applications/drone-command/src/assets/images/areaMap/trash.png b/applications/drone-command/src/assets/images/areaMap/trash.png
new file mode 100644
index 0000000..acf8c69
--- /dev/null
+++ b/applications/drone-command/src/assets/images/areaMap/trash.png
Binary files differ
diff --git a/applications/drone-command/src/styles/common/cockpit.scss b/applications/drone-command/src/styles/common/cockpit.scss
index 7837a9f..74e128f 100644
--- a/applications/drone-command/src/styles/common/cockpit.scss
+++ b/applications/drone-command/src/styles/common/cockpit.scss
@@ -582,6 +582,15 @@
     .el-input__wrapper {
         --el-input-text-color: #161B2C !important;
     }
+
+    .el-select__selection.is-near {
+        margin-left: 0;
+
+        .el-tag {
+            color: #ffffff;
+            background: #161B2C;
+        }
+    }
 }
 
 .command-select-popper.el-select__popper {
diff --git a/applications/drone-command/src/views/api.txt b/applications/drone-command/src/views/api.txt
index 22ac09a..820ace8 100644
--- a/applications/drone-command/src/views/api.txt
+++ b/applications/drone-command/src/views/api.txt
@@ -1,9 +1,9 @@
 
 
-## 重点关注
+## 新增或修改
 
 
-**接口地址**:`/drone-fw/record/fwAlarmFavorite/save`
+**接口地址**:`/area/fwAreaDivide/submit`
 
 
 **请求方式**:`POST`
@@ -15,7 +15,7 @@
 **响应数据类型**:`*/*`
 
 
-**接口描述**:<p>传入fwAlarmFavorite</p>
+**接口描述**:<p>传入主表对象与扩展面列表</p>
 
 
 
@@ -24,7 +24,42 @@
 
 ```javascript
 {
-  "alarmRecordId": 0
+  "areaDivide": {
+    "areaCode": "",
+    "areaName": "",
+    "controlLevel": "",
+    "createDept": 0,
+    "createTime": "",
+    "createUser": 0,
+    "deviceIds": "",
+    "flyDateEnd": "",
+    "flyDateStart": "",
+    "id": 0,
+    "isDeleted": 0,
+    "policeStationId": 0,
+    "responseMechanism": "",
+    "status": 0,
+    "triggerCondition": "",
+    "updateTime": "",
+    "updateUser": 0
+  },
+  "areaDivideExtList": [
+    {
+      "areaCode": "",
+      "areaDivideId": 0,
+      "areaTypeKey": "",
+      "areaTypeValue": "",
+      "createDept": 0,
+      "createTime": "",
+      "createUser": 0,
+      "geomJson": "",
+      "id": 0,
+      "isDeleted": 0,
+      "status": 0,
+      "updateTime": "",
+      "updateUser": 0
+    }
+  ]
 }
 ```
 
@@ -37,8 +72,39 @@
 
 | 参数名称 | 参数说明 | 请求类型    | 是否必须 | 数据类型 | schema |
 | -------- | -------- | ----- | -------- | -------- | ------ |
-|fwAlarmFavorite|告警记录关注表|body|true|FwAlarmFavorite对象|FwAlarmFavorite对象|
-|&emsp;&emsp;alarmRecordId|告警记录id||false|integer(int64)||
+|fwAreaDivideSubmit|fwAreaDivideSubmit|body|true|FwAreaDivideSubmitDTO|FwAreaDivideSubmitDTO|
+|&emsp;&emsp;areaDivide|区域划分表||false|FwAreaDivide对象|FwAreaDivide对象|
+|&emsp;&emsp;&emsp;&emsp;areaCode|区域编码||false|string||
+|&emsp;&emsp;&emsp;&emsp;areaName|区域名称||false|string||
+|&emsp;&emsp;&emsp;&emsp;controlLevel|管控级别:日常/重点/严密||false|string||
+|&emsp;&emsp;&emsp;&emsp;createDept|创建部门||false|integer||
+|&emsp;&emsp;&emsp;&emsp;createTime|创建时间||false|string||
+|&emsp;&emsp;&emsp;&emsp;createUser|创建人||false|integer||
+|&emsp;&emsp;&emsp;&emsp;deviceIds|关联设备ID,逗号分隔||false|string||
+|&emsp;&emsp;&emsp;&emsp;flyDateEnd|可飞行日期时间-结束||false|string||
+|&emsp;&emsp;&emsp;&emsp;flyDateStart|可飞行日期时间-开始||false|string||
+|&emsp;&emsp;&emsp;&emsp;id|主键id||false|integer||
+|&emsp;&emsp;&emsp;&emsp;isDeleted|是否已删除||false|integer||
+|&emsp;&emsp;&emsp;&emsp;policeStationId|关联派出所id||false|integer||
+|&emsp;&emsp;&emsp;&emsp;responseMechanism|响应机制内容||false|string||
+|&emsp;&emsp;&emsp;&emsp;status|业务状态||false|integer||
+|&emsp;&emsp;&emsp;&emsp;triggerCondition|触发条件||false|string||
+|&emsp;&emsp;&emsp;&emsp;updateTime|更新时间||false|string||
+|&emsp;&emsp;&emsp;&emsp;updateUser|更新人||false|integer||
+|&emsp;&emsp;areaDivideExtList|区域划分扩展表(多面数据/多区域类型)||false|array|FwAreaDivideExt对象|
+|&emsp;&emsp;&emsp;&emsp;areaCode|区域编码||false|string||
+|&emsp;&emsp;&emsp;&emsp;areaDivideId|关联主表区域划分ID||false|integer||
+|&emsp;&emsp;&emsp;&emsp;areaTypeKey|区域类型数字字典key||false|string||
+|&emsp;&emsp;&emsp;&emsp;areaTypeValue|区域类型数字字典value||false|string||
+|&emsp;&emsp;&emsp;&emsp;createDept|创建部门||false|integer||
+|&emsp;&emsp;&emsp;&emsp;createTime|创建时间||false|string||
+|&emsp;&emsp;&emsp;&emsp;createUser|创建人||false|integer||
+|&emsp;&emsp;&emsp;&emsp;geomJson|几何JSON||false|string||
+|&emsp;&emsp;&emsp;&emsp;id|主键id||false|integer||
+|&emsp;&emsp;&emsp;&emsp;isDeleted|是否已删除||false|integer||
+|&emsp;&emsp;&emsp;&emsp;status|业务状态||false|integer||
+|&emsp;&emsp;&emsp;&emsp;updateTime|更新时间||false|string||
+|&emsp;&emsp;&emsp;&emsp;updateUser|更新人||false|integer||
 
 
 **响应状态**:
@@ -77,22 +143,22 @@
 
 
 
-## 取消关注
+## 详情
 
 
-**接口地址**:`/drone-fw/record/fwAlarmFavorite/remove`
+**接口地址**:`/area/fwAreaDivide/detail`
 
 
-**请求方式**:`POST`
+**请求方式**:`GET`
 
 
-**请求数据类型**:`application/json`
+**请求数据类型**:`application/x-www-form-urlencoded`
 
 
 **响应数据类型**:`*/*`
 
 
-**接口描述**:<p>传入ids</p>
+**接口描述**:<p>传入id</p>
 
 
 
@@ -104,7 +170,7 @@
 
 | 参数名称 | 参数说明 | 请求类型    | 是否必须 | 数据类型 | schema |
 | -------- | -------- | ----- | -------- | -------- | ------ |
-|ids|主键集合|query|true|string||
+|id|主键id|query|true|integer(int64)||
 
 
 **响应状态**:
@@ -112,8 +178,7 @@
 
 | 状态码 | 说明 | schema |
 | -------- | -------- | ----- | 
-|200|OK|R|
-|201|Created||
+|200|OK|R«FwAreaDivideVO»|
 |401|Unauthorized||
 |403|Forbidden||
 |404|Not Found||
@@ -125,7 +190,24 @@
 | 参数名称 | 参数说明 | 类型 | schema |
 | -------- | -------- | ----- |----- | 
 |code|状态码|integer(int32)|integer(int32)|
-|data|承载数据|object||
+|data|承载数据|FwAreaDivideVO|FwAreaDivideVO|
+|&emsp;&emsp;areaCode|区域编码|string||
+|&emsp;&emsp;areaName|区域名称|string||
+|&emsp;&emsp;controlLevel|管控级别:日常/重点/严密|string||
+|&emsp;&emsp;deviceIds|关联设备ID,逗号分隔|string||
+|&emsp;&emsp;flyDateEnd|可飞行日期时间-结束|string(date-time)||
+|&emsp;&emsp;flyDateStart|可飞行日期时间-开始|string(date-time)||
+|&emsp;&emsp;fwAreaDivideExtList|面数据|array|FwAreaDivideExtVO|
+|&emsp;&emsp;&emsp;&emsp;areaCode|区域编码|string||
+|&emsp;&emsp;&emsp;&emsp;areaDivideId|关联主表区域划分ID|integer||
+|&emsp;&emsp;&emsp;&emsp;areaTypeKey|区域类型数字字典key|string||
+|&emsp;&emsp;&emsp;&emsp;areaTypeValue|区域类型数字字典value|string||
+|&emsp;&emsp;&emsp;&emsp;geomJson|几何JSON|string||
+|&emsp;&emsp;&emsp;&emsp;id|主键id|integer||
+|&emsp;&emsp;id|主键id|integer(int64)||
+|&emsp;&emsp;policeStationId|关联派出所id|string||
+|&emsp;&emsp;responseMechanism|响应机制内容|string||
+|&emsp;&emsp;triggerCondition|触发条件|string||
 |msg|返回消息|string||
 |success|是否成功|boolean||
 
@@ -133,9 +215,38 @@
 **响应示例**:
 ```javascript
 {
-	"code": 0,
-	"data": {},
-	"msg": "",
-	"success": true
+    "code": 200,
+    "success": true,
+    "data": {
+        "id": "2016778024413888513",
+        "areaName": "新版测试1",
+        "triggerCondition": "1",
+        "responseMechanism": "1",
+        "controlLevel": "1",
+        "policeStationId": "6000000000001",
+        "deviceIds": "",
+        "flyDateStart": "2026-01-29 00:00:00",
+        "flyDateEnd": "2026-01-31 00:00:00",
+        "areaCode": "",
+        "fwAreaDivideExtList": [
+            {
+                "id": "21",
+                "areaDivideId": "2016778024413888513",
+                "areaTypeKey": "1",
+                "areaTypeValue": "侦测区",
+                "geomJson": "{\"id\": \"shape_1769672288759_0bdd\", \"meta\": null, \"points\": [{\"lat\": 27.131594958673237, \"lng\": 114.97629628660002, \"height\": 0, \"latitude\": 27.131594958673237, \"longitude\": 114.97629628660002}, {\"lat\": 27.08982649211561, \"lng\": 114.95070164271222, \"height\": 0, \"latitude\": 27.08982649211561, \"longitude\": 114.95070164271222}, {\"lat\": 27.078929811185212, \"lng\": 114.9933846220512, \"height\": 0, \"latitude\": 27.078929811185212, \"longitude\": 114.9933846220512}, {\"lat\": 27.107909998247127, \"lng\": 115.02860852426171, \"height\": 0, \"latitude\": 27.107909998247127, \"longitude\": 115.02860852426171}], \"areaType\": \"1\", \"drawType\": \"polygon\", \"displayPoints\": null}",
+                "areaCode": ""
+            },
+            {
+                "id": "22",
+                "areaDivideId": "2016778024413888513",
+                "areaTypeKey": "1",
+                "areaTypeValue": "侦测区",
+                "geomJson": "{\"id\": \"shape_1769672293742_947a\", \"meta\": {\"center\": {\"lat\": 27.117257447017685, \"lng\": 115.1020385237064, \"height\": 238.8343626960753}, \"semiMajor\": 6700.514105765581, \"semiMinor\": 1943.840234046766, \"majorPoint\": {\"lat\": 27.10775369958058, \"lng\": 115.168774284004, \"height\": 104.8557871185756}, \"minorPoint\": {\"lat\": 27.10046558321718, \"lng\": 115.1077128183224, \"height\": 184.11135376718033}, \"controlPoints\": [{\"lat\": 27.117257447017685, \"lng\": 115.1020385237064, \"height\": 238.8343626960753}, {\"lat\": 27.10775369958058, \"lng\": 115.168774284004, \"height\": 104.8557871185756}, {\"lat\": 27.10046558321718, \"lng\": 115.1077128183224, \"height\": 184.11135376718033}]}, \"points\": [{\"lat\": 27.117257447017685, \"lng\": 115.1020385237064, \"height\": 238.8343626960753}, {\"lat\": 27.10775369958058, \"lng\": 115.168774284004, \"height\": 104.8557871185756}, {\"lat\": 27.10046558321718, \"lng\": 115.1077128183224, \"height\": 184.11135376718033}], \"areaType\": \"1\", \"drawType\": \"ellipse\", \"displayPoints\": [{\"lat\": 27.1172411931675, \"lng\": 115.16961434879582, \"height\": 242.35137437266533, \"latitude\": 27.1172411931675, \"longitude\": 115.16961434879582}, {\"lat\": 27.11907502581333, \"lng\": 115.16924525730374, \"height\": 242.3161770455558, \"latitude\": 27.11907502581333, \"longitude\": 115.16924525730374}, {\"lat\": 27.120889115581708, \"lng\": 115.16813979957712, \"height\": 242.2121648606328, \"latitude\": 27.120889115581708, \"longitude\": 115.16813979957712}, {\"lat\": 27.12266356982587, \"lng\": 115.1663100528295, \"height\": 242.0438829806018, \"latitude\": 27.12266356982587, \"longitude\": 115.1663100528295}, {\"lat\": 27.124378919527693, \"lng\": 115.16377603274732, \"height\": 241.81868547594897, \"latitude\": 27.124378919527693, \"longitude\": 115.16377603274732}, {\"lat\": 27.12601633381306, \"lng\": 115.16056547557488, \"height\": 241.54641391722672, \"latitude\": 27.12601633381306, \"longitude\": 115.16056547557488}, {\"lat\": 27.127557827888296, \"lng\": 115.15671353542164, \"height\": 241.238967263203, \"latitude\": 27.127557827888296, \"longitude\": 115.15671353542164}, {\"lat\": 27.128986462048708, \"lng\": 115.1522624000547, \"height\": 240.9097818279928, \"latitude\": 27.128986462048708, \"longitude\": 115.1522624000547}, {\"lat\": 27.130286529493247, \"lng\": 115.14726082934388, \"height\": 240.57324405610856, \"latitude\": 27.130286529493247, \"longitude\": 115.14726082934388}, {\"lat\": 27.131443730790306, \"lng\": 115.14176362138588, \"height\": 240.24406177058316, \"latitude\": 27.131443730790306, \"longitude\": 115.14176362138588}, {\"lat\": 27.132445332979348, \"lng\": 115.13583101214034, \"height\": 239.93662137804984, \"latitude\": 27.132445332979348, \"longitude\": 115.13583101214034}, {\"lat\": 27.13328031145803, \"lng\": 115.12952801515418, \"height\": 239.66435911715624, \"latitude\": 27.13328031145803, \"longitude\": 115.12952801515418}, {\"lat\": 27.133939472996364, \"lng\": 115.12292370862173, \"height\": 239.43917383966289, \"latitude\": 27.133939472996364, \"longitude\": 115.12292370862173}, {\"lat\": 27.134415558431908, \"lng\": 115.1160904776194, \"height\": 239.27090697284905, \"latitude\": 27.134415558431908, \"longitude\": 115.1160904776194}, {\"lat\": 27.13470332383455, \"lng\": 115.10910321985791, \"height\": 239.16691240515263, \"latitude\": 27.13470332383455, \"longitude\": 115.10910321985791}, {\"lat\": 27.134799599179868, \"lng\": 115.1020385237064, \"height\": 239.131735087732, \"latitude\": 27.134799599179868, \"longitude\": 115.1020385237064}, {\"lat\": 27.13470332383455, \"lng\": 115.09497382755488, \"height\": 239.16691240471368, \"latitude\": 27.13470332383455, \"longitude\": 115.09497382755488}, {\"lat\": 27.134415558431908, \"lng\": 115.08798656979342, \"height\": 239.27090697166375, \"latitude\": 27.134415558431908, \"longitude\": 115.08798656979342}, {\"lat\": 27.133939472996364, \"lng\": 115.08115333879108, \"height\": 239.43917384027625, \"latitude\": 27.133939472996364, \"longitude\": 115.08115333879108}, {\"lat\": 27.13328031145803, \"lng\": 115.0745490322586, \"height\": 239.66435911740945, \"latitude\": 27.13328031145803, \"longitude\": 115.0745490322586}, {\"lat\": 27.132445332979348, \"lng\": 115.0682460352724, \"height\": 239.9366213773679, \"latitude\": 27.132445332979348, \"longitude\": 115.0682460352724}, {\"lat\": 27.1314437307903, \"lng\": 115.06231342602688, \"height\": 240.24406177080297, \"latitude\": 27.1314437307903, \"longitude\": 115.06231342602688}, {\"lat\": 27.130286529493247, \"lng\": 115.0568162180689, \"height\": 240.57324405754517, \"latitude\": 27.130286529493247, \"longitude\": 115.0568162180689}, {\"lat\": 27.128986462048708, \"lng\": 115.0518146473581, \"height\": 240.9097818281966, \"latitude\": 27.128986462048708, \"longitude\": 115.0518146473581}, {\"lat\": 27.127557827888293, \"lng\": 115.04736351199114, \"height\": 241.238967262521, \"latitude\": 27.127557827888293, \"longitude\": 115.04736351199114}, {\"lat\": 27.126016333813062, \"lng\": 115.04351157183788, \"height\": 241.54641391765136, \"latitude\": 27.126016333813062, \"longitude\": 115.04351157183788}, {\"lat\": 27.12437891952769, \"lng\": 115.04030101466547, \"height\": 241.81868547645408, \"latitude\": 27.12437891952769, \"longitude\": 115.04030101466547}, {\"lat\": 27.122663569825875, \"lng\": 115.03776699458332, \"height\": 242.04388298107517, \"latitude\": 27.122663569825875, \"longitude\": 115.03776699458332}, {\"lat\": 27.120889115581715, \"lng\": 115.03593724783563, \"height\": 242.2121648595728, \"latitude\": 27.120889115581715, \"longitude\": 115.03593724783563}, {\"lat\": 27.119075025813327, \"lng\": 115.03483179010902, \"height\": 242.3161770439311, \"latitude\": 27.119075025813327, \"longitude\": 115.03483179010902}, {\"lat\": 27.117241193167494, \"lng\": 115.03446269861692, \"height\": 242.35137437283345, \"latitude\": 27.117241193167494, \"longitude\": 115.03446269861692}, {\"lat\": 27.11540771522819, \"lng\": 115.03483398128904, \"height\": 242.31621922677547, \"latitude\": 27.11540771522819, \"longitude\": 115.03483398128904}, {\"lat\": 27.11359467407704, \"lng\": 115.03594153443072, \"height\": 242.2122487268214, \"latitude\": 27.11359467407704, \"longitude\": 115.03594153443072}, {\"lat\": 27.111821916531323, \"lng\": 115.03777318924902, \"height\": 242.04400754760337, \"latitude\": 27.111821916531323, \"longitude\": 115.03777318924902}, {\"lat\": 27.110108837455485, \"lng\": 115.04030884666572, \"height\": 241.81884927999585, \"latitude\": 27.110108837455485, \"longitude\": 115.04030884666572}, {\"lat\": 27.10847416848657, \"lng\": 115.04352069887712, \"height\": 241.5466150531188, \"latitude\": 27.10847416848657, \"longitude\": 115.04352069887712}, {\"lat\": 27.106935774434948, \"lng\": 115.0473735351744, \"height\": 241.23920341133748, \"latitude\": 27.106935774434948, \"longitude\": 115.0473735351744}, {\"lat\": 27.10551045951957, \"lng\": 115.05182512862444, \"height\": 240.91005029162608, \"latitude\": 27.10551045951957, \"longitude\": 115.05182512862444}, {\"lat\": 27.104213785474624, \"lng\": 115.05682669933678, \"height\": 240.57354179818523, \"latitude\": 27.104213785474624, \"longitude\": 115.05682669933678}, {\"lat\": 27.10305990342386, \"lng\": 115.06232344921456, \"height\": 240.24438546029003, \"latitude\": 27.10305990342386, \"longitude\": 115.06232344921456}, {\"lat\": 27.10206140126076, \"lng\": 115.06825516231812, \"height\": 239.93696743263703, \"latitude\": 27.10206140126076, \"longitude\": 115.06825516231812}, {\"lat\": 27.10122916810148, \"lng\": 115.07455686426626, \"height\": 239.66472374787935, \"latitude\": 27.10122916810148, \"longitude\": 115.07455686426626}, {\"lat\": 27.100572277192217, \"lng\": 115.0811595334639, \"height\": 239.43955308582795, \"latitude\": 27.100572277192217, \"longitude\": 115.0811595334639}, {\"lat\": 27.10009788845782, \"lng\": 115.087990856394, \"height\": 239.2712967482805, \"latitude\": 27.10009788845782, \"longitude\": 115.087990856394}, {\"lat\": 27.099811171674233, \"lng\": 115.0949760187379, \"height\": 239.16730853166112, \"latitude\": 27.099811171674233, \"longitude\": 115.0949760187379}, {\"lat\": 27.09971525103608, \"lng\": 115.1020385237064, \"height\": 239.13213333836975, \"latitude\": 27.09971525103608, \"longitude\": 115.1020385237064}, {\"lat\": 27.099811171674233, \"lng\": 115.10910102867491, \"height\": 239.167308531655, \"latitude\": 27.099811171674233, \"longitude\": 115.10910102867491}, {\"lat\": 27.10009788845782, \"lng\": 115.11608619101878, \"height\": 239.27129674685, \"latitude\": 27.10009788845782, \"longitude\": 115.11608619101878}, {\"lat\": 27.10057227719222, \"lng\": 115.1229175139489, \"height\": 239.43955308551512, \"latitude\": 27.10057227719222, \"longitude\": 115.1229175139489}, {\"lat\": 27.10122916810148, \"lng\": 115.12952018314652, \"height\": 239.66472374829976, \"latitude\": 27.10122916810148, \"longitude\": 115.12952018314652}, {\"lat\": 27.10206140126076, \"lng\": 115.13582188509464, \"height\": 239.93696743312785, \"latitude\": 27.10206140126076, \"longitude\": 115.13582188509464}, {\"lat\": 27.10305990342386, \"lng\": 115.14175359819826, \"height\": 240.2443854608264, \"latitude\": 27.10305990342386, \"longitude\": 115.14175359819826}, {\"lat\": 27.104213785474617, \"lng\": 115.14725034807596, \"height\": 240.5735417970414, \"latitude\": 27.104213785474617, \"longitude\": 115.14725034807596}, {\"lat\": 27.105510459519557, \"lng\": 115.15225191878834, \"height\": 240.91005029170333, \"latitude\": 27.105510459519557, \"longitude\": 115.15225191878834}, {\"lat\": 27.106935774434948, \"lng\": 115.15670351223838, \"height\": 241.23920341122712, \"latitude\": 27.106935774434948, \"longitude\": 115.15670351223838}, {\"lat\": 27.10847416848657, \"lng\": 115.16055634853564, \"height\": 241.5466150535108, \"latitude\": 27.10847416848657, \"longitude\": 115.16055634853564}, {\"lat\": 27.110108837455485, \"lng\": 115.16376820074704, \"height\": 241.81884927874424, \"latitude\": 27.110108837455485, \"longitude\": 115.16376820074704}, {\"lat\": 27.11182191653132, \"lng\": 115.16630385816376, \"height\": 242.04400754640932, \"latitude\": 27.11182191653132, \"longitude\": 115.16630385816376}, {\"lat\": 27.11359467407704, \"lng\": 115.16813551298206, \"height\": 242.2122487283748, \"latitude\": 27.11359467407704, \"longitude\": 115.16813551298206}, {\"lat\": 27.11540771522819, \"lng\": 115.16924306612378, \"height\": 242.31621922659275, \"latitude\": 27.11540771522819, \"longitude\": 115.16924306612378}]}",
+                "areaCode": ""
+            }
+        ]
+    },
+    "msg": "操作成功"
 }
 ```
\ No newline at end of file
diff --git a/applications/drone-command/src/views/areaManage/partition/FormDiaLog copy.vue b/applications/drone-command/src/views/areaManage/partition/FormDiaLog copy.vue
index cc492ca..302d14e 100644
--- a/applications/drone-command/src/views/areaManage/partition/FormDiaLog copy.vue
+++ b/applications/drone-command/src/views/areaManage/partition/FormDiaLog copy.vue
@@ -12,54 +12,6 @@
 					:layer-mode="4"
 					:boundary="false"
 				/>
-				<div
-					v-if="showTypePanel && !readonly"
-					class="shape-type-panel"
-				>
-					<div class="panel-header">
-						<span>{{ drawTypeLabelMap[currentShapeType] || '区域' }}</span>
-						<el-icon class="panel-close" @click.stop="handleTypePanelClose"><Close /></el-icon>
-					</div>
-					<div class="panel-body">
-						<span class="panel-label">区域类型</span>
-						<el-select
-							class="command-select"
-							popper-class="command-select-popper"
-							v-model="formData.areaType"
-							placeholder="请选择"
-							@change="handleAreaTypeChange"
-						>
-							<el-option
-								v-for="item in dictObj.areaType"
-								:key="item.dictKey"
-								:label="item.dictValue"
-								:value="item.dictKey"
-							/>
-						</el-select>
-					</div>
-				</div>
-				<div class="map-toolbar">
-					<button class="tool-btn" type="button" @click="handleDraw('polygon')">
-						<span class="tool-icon polygon"></span>
-						<span class="tool-text">多边形</span>
-					</button>
-					<button class="tool-btn" type="button" @click="handleDraw('rectangle')">
-						<span class="tool-icon rect"></span>
-						<span class="tool-text">矩形</span>
-					</button>
-					<button class="tool-btn" type="button" @click="handleDraw('ellipse')">
-						<span class="tool-icon ellipse"></span>
-						<span class="tool-text">椭圆</span>
-					</button>
-					<button class="tool-btn" type="button" @click="handleDraw('buffer')">
-						<span class="tool-icon buffer"></span>
-						<span class="tool-text">缓冲圆</span>
-					</button>
-					<button class="tool-btn danger" type="button" @click="handleClearShape">
-						<span class="tool-icon trash"></span>
-						<span class="tool-text">删除</span>
-					</button>
-				</div>
 			</div>
 
 			<div class="right-container">
@@ -180,28 +132,6 @@
 						</el-select>
 					</el-form-item>
 
-					<div class="shape-table-container">
-						<div class="shape-table-title">绘制区域列表</div>
-						<el-table class="command-table" :data="shapeList" row-key="id" height="360">
-							<el-table-column prop="areaType" label="类型" width="120">
-								<template v-slot="{ row }">
-									{{ getDictLabel(row.areaType, dictObj.areaType) }}
-								</template>
-							</el-table-column>
-							<el-table-column prop="drawType" label="绘制类型" width="120">
-								<template v-slot="{ row }">
-									{{ drawTypeLabelMap[row.drawType] || '-' }}
-								</template>
-							</el-table-column>
-							<el-table-column label="操作" class-name="operation-btns">
-								<template v-slot="{ row }">
-									<el-link @click="handleShapeEdit(row)">编辑</el-link>
-									<el-link @click="handleShapeDelete(row)">删除</el-link>
-								</template>
-							</el-table-column>
-						</el-table>
-					</div>
-
 					<div class="search-table-container">
 						<div class="search-box">
 							<div class="label">
@@ -243,17 +173,15 @@
 <script setup>
 import { Close } from '@element-plus/icons-vue'
 
-import { computed, inject, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
+import { computed, inject, nextTick, onMounted, ref, watch } from 'vue'
 import { ElMessage } from 'element-plus'
 import { fwAreaDivideDetailApi, fwAreaDivideSubmitApi } from './partitionApi'
 import { saveOperationLog } from '@ztzf/apis'
 import { useRoute } from 'vue-router'
 import { fieldRules, geomAnalysis, getDictLabel } from '@ztzf/utils'
 import CommonCesiumMap from '@/components/map-container/common-cesium-map.vue'
-import { DrawManager } from './shapeTools/DrawManager'
-import { EditManager } from './shapeTools/EditManager'
+import { DrawPolygon } from '@/utils/cesium/DrawPolygon'
 import { cartesian3Convert } from '@/utils/cesium/mapUtil'
-import { buildEllipsePositions } from './shapeTools/shapeUtils'
 import * as turf from '@turf/turf'
 import * as Cesium from 'cesium'
 import { fwPoliceStationListApi } from '@/views/areaManage/precinctInfo/precinctInfoApi'
@@ -291,39 +219,9 @@
 const deviceOptions = ref([]) // 关联设备
 const dictObj = inject('dictObj')
 const mapRef = ref(null)
-const currentShapeType = ref('polygon')
-const shapeList = ref([])
-const activeShapeId = ref(null)
-const drawTypeLabelMap = {
-	polygon: '多边形',
-	rectangle: '矩形',
-	ellipse: '椭圆',
-	buffer: '缓冲圆',
-}
-const showTypePanel = ref(false)
-const suppressTypePanelOnce = ref(false)
-const areaTypeStyleMap = {
-	'1': {
-		fill: Cesium.Color.fromBytes(25, 178, 230, 128),
-		outline: Cesium.Color.fromBytes(0, 251, 255, 255),
-	},
-	'2': {
-		fill: Cesium.Color.fromBytes(255, 235, 59, 128),
-		outline: Cesium.Color.fromBytes(255, 235, 59, 255),
-	},
-	'3': {
-		fill: Cesium.Color.fromBytes(247, 20, 20, 128),
-		outline: Cesium.Color.fromBytes(255, 0, 0, 255),
-	},
-}
 let viewer
-let drawManager
-let editManager
-let activeTool
+let drawPolygonExample
 let pointList = []
-let viewEntity
-let shapeDisplaySource
-const activeToolMode = ref('')
 
 const rules = {
 	areaName: fieldRules(true, 50),
@@ -408,24 +306,6 @@
 		formData.value.flyDateEnd = flyDateRange.value[1] || ''
 	}
 )
-watch(
-	() => visible.value,
-	val => {
-		if (!val) {
-			viewEntity && viewer?.entities?.remove(viewEntity)
-			viewEntity = null
-			clearActiveTool()
-			hideTypePanel()
-			clearShapeDisplay()
-		}
-	}
-)
-watch(
-	() => formData.value.areaType,
-	val => {
-		updateActiveShapeAreaType(val)
-	}
-)
 
 // 加载详情
 async function loadDetail () {
@@ -454,191 +334,14 @@
 	viewer = map?.viewer || null
 }
 
-function ensureShapeDisplaySource () {
-	if (!viewer) return null
-	if (!shapeDisplaySource) {
-		shapeDisplaySource = new Cesium.CustomDataSource('partition-shape-display')
-		viewer.dataSources.add(shapeDisplaySource)
-	}
-	return shapeDisplaySource
-}
-
-function clearShapeDisplay () {
-	if (!shapeDisplaySource) return
-	shapeDisplaySource.entities.removeAll()
-}
-
-function normalizeShapePoint (point) {
-	const lng = point?.lng ?? point?.longitude
-	const lat = point?.lat ?? point?.latitude
-	if (!Number.isFinite(Number(lng)) || !Number.isFinite(Number(lat))) return null
-	return {
-		lng: Number(lng),
-		lat: Number(lat),
-		height: Number.isFinite(Number(point?.height)) ? Number(point.height) : 0,
-	}
-}
-
-function buildShapePositions (points = []) {
-	const normalized = points.map(normalizeShapePoint).filter(Boolean)
-	return normalized.map(point =>
-		Cesium.Cartesian3.fromDegrees(point.lng, point.lat, point.height)
-	)
-}
-
-function getAreaTypeStyle (areaType) {
-	return areaTypeStyleMap?.[String(areaType)] || {
-		fill: Cesium.Color.fromBytes(45, 140, 240, 99),
-		outline: Cesium.Color.fromBytes(45, 140, 240, 255),
-	}
-}
-
-function renderShapeList () {
-	if (!viewer || !visible.value || readonly.value) {
-		clearShapeDisplay()
-		return
-	}
-	const dataSource = ensureShapeDisplaySource()
-	if (!dataSource) return
-	dataSource.entities.removeAll()
-	shapeList.value.forEach(shape => {
-		if (!shape?.points?.length) return
-		if (activeToolMode.value === 'edit' && shape.id === activeShapeId.value) return
-		if (shape.drawType === 'buffer' && shape.meta?.bufferRadii?.length && shape.meta?.center) {
-			const center = shape.meta.center
-			const centerCartesian = Cesium.Cartesian3.fromDegrees(center.lng, center.lat, center.height || 0)
-			const [radius1, radius2, radius3] = shape.meta.bufferRadii
-			const radii = [radius1, radius2, radius3].filter(item => Number.isFinite(item) && item > 0)
-			if (radii.length) {
-				const styles = [
-					{
-						fill: Cesium.Color.fromBytes(247, 20, 20, 128),
-						outline: Cesium.Color.fromBytes(255, 0, 0, 255),
-					},
-					{
-						fill: Cesium.Color.fromBytes(255, 235, 59, 128),
-						outline: Cesium.Color.fromBytes(255, 235, 59, 255),
-					},
-					{
-						fill: Cesium.Color.fromBytes(25, 178, 230, 128),
-						outline: Cesium.Color.fromBytes(0, 251, 255, 255),
-					},
-				]
-				radii.forEach((radius, index) => {
-					const style = styles[index] || styles[styles.length - 1]
-					dataSource.entities.add({
-						name: 'shape-display',
-						customData: { shapeId: shape.id, drawType: shape.drawType, level: index + 1 },
-						position: centerCartesian,
-						ellipse: {
-							semiMajorAxis: radius,
-							semiMinorAxis: radius,
-							material: style.fill,
-							outline: false,
-							heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
-						},
-					})
-					const positions = buildEllipsePositions(centerCartesian, radius, radius)
-					dataSource.entities.add({
-						name: 'shape-display',
-						customData: { shapeId: shape.id, drawType: shape.drawType, level: index + 1, outline: true },
-						polyline: {
-							positions: positions.length ? [...positions, positions[0]] : positions,
-							clampToGround: true,
-							width: 2,
-							material: style.outline,
-						},
-					})
-				})
-			}
-			return
-		}
-
-		const positions = buildShapePositions(shape.points)
-		if (positions.length < 3) return
-		const style = getAreaTypeStyle(shape.areaType)
-		dataSource.entities.add({
-			name: 'shape-display',
-			customData: { shapeId: shape.id, drawType: shape.drawType },
-			polygon: {
-				hierarchy: new Cesium.PolygonHierarchy(positions),
-				material: style.fill,
-				outline: false,
-				heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
-			},
-			polyline: {
-				positions: positions.length ? [...positions, positions[0]] : positions,
-				clampToGround: true,
-				width: 2,
-				material: style.outline,
-			},
-		})
-	})
-}
-
 // 绘制完成回调
 const drawFinished = async data => {
-	const positions = Array.isArray(data?.positions) ? data.positions : data
-	if (!Array.isArray(positions) || positions.length < 3) {
-		pointList = []
-		formData.value.areaSize = null
-		formData.value.longitude = null
-		formData.value.latitude = null
-		hideTypePanel()
-		return
-	}
-	const meta = data?.meta
-	const bufferRadii = Array.isArray(meta?.bufferRadii) ? meta.bufferRadii : null
-	const centerCartesian = meta?.center
-	const centerLngLat = centerCartesian ? cartesian3Convert(centerCartesian, viewer) : null
-	pointList = _.cloneDeep(positions).map(item => {
+	pointList = _.cloneDeep(data).map(item => {
 		const val = cartesian3Convert(item, viewer)
 		return { ...val, lng: val.longitude, lat: val.latitude }
 	})
-	applyPolygonMetrics(pointList)
-	if (!formData.value.areaType) {
-		const detectAreaType = getDetectAreaTypeKey()
-		if (detectAreaType) {
-			formData.value.areaType = detectAreaType
-		}
-	}
-	if (!suppressTypePanelOnce.value) {
-		showTypePanelAtCurrent()
-	}
-	suppressTypePanelOnce.value = false
-
-	if (!activeShapeId.value) {
-		activeShapeId.value = `shape_${Date.now()}_${Math.random().toString(16).slice(2, 6)}`
-	}
-	const targetIndex = shapeList.value.findIndex(item => item.id === activeShapeId.value)
-	const shapePayload = {
-		id: activeShapeId.value,
-		areaType: formData.value.areaType,
-		drawType: currentShapeType.value,
-		points: _.cloneDeep(pointList),
-		meta: bufferRadii && centerLngLat
-			? {
-				bufferRadii,
-				center: {
-					lng: centerLngLat.longitude,
-					lat: centerLngLat.latitude,
-					height: centerLngLat.height ?? 0,
-				},
-			}
-			: null,
-	}
-	if (targetIndex === -1) {
-		shapeList.value.push(shapePayload)
-	} else {
-		shapeList.value.splice(targetIndex, 1, shapePayload)
-	}
-	renderShapeList()
-}
-
-function applyPolygonMetrics(points) {
-	if (!points?.length) return
 	const polygon = turf.polygon([
-		[...points.map(item => [item.longitude, item.latitude]), [points[0].longitude, points[0].latitude]],
+		[...pointList.map(item => [item.longitude, item.latitude]), [pointList[0].longitude, pointList[0].latitude]],
 	])
 	const center = turf.centerOfMass(polygon)
 	const areaSqm = turf.area(polygon) // 平方米
@@ -647,152 +350,41 @@
 	formData.value.latitude = center.geometry.coordinates[1]
 }
 
-function getDetectAreaTypeKey () {
-	const areaTypeOptions = Array.isArray(dictObj?.value?.areaType)
-		? dictObj.value.areaType
-		: dictObj?.areaType
-	if (!Array.isArray(areaTypeOptions)) return ''
-	const target = areaTypeOptions.find(item => item?.dictKey === '1')
-	return target?.dictKey ?? ''
+// 新增模式绘制
+function addPolygon () {
+	drawPolygonExample = new DrawPolygon(viewer)
+	drawPolygonExample.initHandler(viewer)
+	drawPolygonExample.subscribe('getPolygonPositions', drawFinished)
 }
 
-function showTypePanelAtCurrent () {
-	if (readonly.value) return
-	showTypePanel.value = true
-}
-
-function hideTypePanel () {
-	showTypePanel.value = false
-}
-
-function handleTypePanelClose () {
-	hideTypePanel()
-	if (readonly.value) return
-	if (activeToolMode.value !== 'edit') return
-	const positions = activeTool?.getPositions?.()
-	if (Array.isArray(positions) && positions.length >= 3) {
-		suppressTypePanelOnce.value = true
-		drawFinished(positions)
-	}
-	clearActiveTool()
-}
-
-function handleAreaTypeChange (value) {
-	updateActiveShapeAreaType(value)
-}
-
-function updateActiveShapeAreaType (value) {
-	if (currentShapeType.value !== 'buffer') {
-		activeTool?.setStyle?.(getAreaTypeStyle(value))
-	}
-	if (!activeShapeId.value) return
-	const targetIndex = shapeList.value.findIndex(item => item.id === activeShapeId.value)
-	if (targetIndex === -1) return
-	const target = shapeList.value[targetIndex]
-	shapeList.value.splice(targetIndex, 1, {
-		...target,
-		areaType: value,
-	})
-	renderShapeList()
-}
-
-// Tool lifecycle
-function clearActiveTool () {
-	activeTool?.destroy?.()
-	activeTool = null
-	drawManager?.destroy()
-	editManager?.destroy()
-	activeToolMode.value = ''
-	renderShapeList()
-}
-
-function startDraw (type) {
-	currentShapeType.value = type
-	activeToolMode.value = 'draw'
-	drawManager ||= new DrawManager(viewer)
-	activeTool = drawManager.start(type, { style: getAreaTypeStyle(formData.value.areaType) })
-	activeTool?.subscribe('getPolygonPositions', drawFinished)
-}
-
-function startEdit (type, points) {
-	currentShapeType.value = type
-	activeToolMode.value = 'edit'
-	editManager ||= new EditManager(viewer)
-	activeTool = editManager.start(type, points, { style: getAreaTypeStyle(formData.value.areaType) })
-	activeTool?.subscribe('getPolygonPositions', drawFinished)
-}
-
-function handleDraw (type) {
-	if (readonly.value) return
-	if (!viewer) return
-	clearActiveTool()
-	viewEntity && viewer?.entities?.remove(viewEntity)
-	viewEntity = null
-	hideTypePanel()
-	renderShapeList()
-	if (dialogMode.value === 'edit' && pointList.length >= 3) {
-		const editPoints = _.cloneDeep(pointList)
-		if (editPoints.length > 1) {
-			const first = editPoints[0]
-			const last = editPoints[editPoints.length - 1]
-			if (first.longitude === last.longitude && first.latitude === last.latitude) {
-				editPoints.pop()
-			}
+// 编辑面
+function editPolygon () {
+	if (!formData.value?.geom) return
+	pointList = geomAnalysis(formData.value.geom)
+	drawPolygonExample?.destroy()
+	drawPolygonExample = new DrawPolygon(viewer)
+	let pointList1 = _.cloneDeep(pointList)
+	pointList1.pop()
+	drawPolygonExample.initPolygon(
+		viewer,
+		pointList.map(item => ({ lng: item.longitude, lat: item.latitude }))
+	)
+	drawPolygonExample.subscribe('getPolygonPositions', drawFinished)
+	if (pointList1.length) {
+		const result = pointList1.map(item => [item.longitude, item.latitude]).flat()
+		const tempEntity = viewer.entities?.add({
+			position: Cesium.Cartesian3.fromDegrees(result[0], result[1]),
+			polyline: {
+				positions: Cesium.Cartesian3.fromDegreesArray(result),
+				clampToGround: true,
+				width: 1,
+				material: Cesium.Color.TRANSPARENT,
+			},
+		})
+		if (tempEntity) {
+			viewer.flyTo(tempEntity, { duration: 0 })
+			viewer.entities.remove(tempEntity)
 		}
-		activeShapeId.value = activeShapeId.value || (shapeList.value[0]?.id ?? null)
-		startEdit(type, editPoints)
-		return
-	}
-	const defaultAreaType = getDetectAreaTypeKey()
-	if (defaultAreaType) {
-		formData.value.areaType = defaultAreaType
-	}
-	activeShapeId.value = null
-	startDraw(type)
-}
-
-function handleClearShape () {
-	if (readonly.value) return
-	clearActiveTool()
-	viewEntity && viewer?.entities?.remove(viewEntity)
-	viewEntity = null
-	pointList = []
-	shapeList.value = []
-	activeShapeId.value = null
-	formData.value.areaSize = null
-	formData.value.longitude = null
-	formData.value.latitude = null
-	hideTypePanel()
-	clearShapeDisplay()
-}
-
-function handleShapeEdit (row) {
-	if (!row?.points?.length) return
-	if (activeToolMode.value === 'edit' && activeShapeId.value === row.id) return
-	activeShapeId.value = row.id
-	currentShapeType.value = row.drawType
-	formData.value.areaType = row.areaType
-	pointList = _.cloneDeep(row.points)
-	clearActiveTool()
-	const editPayload = row.drawType === 'buffer' ? { points: row.points, meta: row.meta } : row.points
-	startEdit(row.drawType, editPayload)
-	applyPolygonMetrics(pointList)
-	showTypePanelAtCurrent()
-	renderShapeList()
-}
-
-function handleShapeDelete (row) {
-	if (!row) return
-	shapeList.value = shapeList.value.filter(item => item.id !== row.id)
-	renderShapeList()
-	if (activeShapeId.value === row.id) {
-		activeShapeId.value = null
-		clearActiveTool()
-		pointList = []
-		formData.value.areaSize = null
-		formData.value.longitude = null
-		formData.value.latitude = null
-		hideTypePanel()
 	}
 }
 
@@ -801,7 +393,7 @@
 	if (!formData.value?.geom) return
 	pointList = geomAnalysis(formData.value.geom)
 	const result = pointList.map(item => [item.longitude, item.latitude]).flat()
-	viewEntity = viewer.entities?.add({
+	const mian = viewer.entities?.add({
 		customType: 'control_group',
 		position: Cesium.Cartesian3.fromDegrees(result[0], result[1]),
 		polyline: {
@@ -811,7 +403,7 @@
 			material: Cesium.Color.RED,
 		},
 	})
-	viewer.flyTo(viewEntity, { duration: 0 })
+	viewer.flyTo(mian, { duration: 0 })
 }
 
 // 获取派出所列表
@@ -857,32 +449,14 @@
 	dialogMode.value = mode || 'add'
 	formData.value = dialogMode.value === 'add' ? initForm() : row
 	selectedDeviceRows.value = []
-	shapeList.value = []
-	activeShapeId.value = null
 	await nextTick()
 	initMap()
-	clearShapeDisplay()
-	viewEntity && viewer?.entities?.remove(viewEntity)
-	viewEntity = null
-	clearActiveTool()
 	await getDeviceList()
 	if (dialogMode.value === 'add') {
-		// default no draw mode
+		addPolygon()
 	} else if (dialogMode.value === 'edit') {
 		await loadDetail()
-		pointList = geomAnalysis(formData.value.geom) || []
-		if (pointList.length) {
-			activeShapeId.value = `shape_${Date.now()}_${Math.random().toString(16).slice(2, 6)}`
-			shapeList.value = [
-				{
-					id: activeShapeId.value,
-					areaType: formData.value.areaType,
-					drawType: currentShapeType.value,
-					points: _.cloneDeep(pointList),
-				},
-			]
-		}
-		viewPolygon()
+		editPolygon()
 	} else {
 		await loadDetail()
 		viewPolygon()
@@ -896,170 +470,9 @@
 	getDeviceList()
 })
 
-onBeforeUnmount(() => {
-	hideTypePanel()
-})
-
 defineExpose({
 	open,
 })
 </script>
 
-<style scoped lang="scss">
-.left-container {
-	position: relative;
-}
-
-.shape-type-panel {
-	position: absolute;
-	right: 16px;
-	bottom: 16px;
-	z-index: 6;
-	min-width: 260px;
-	background: rgba(22, 33, 44, 0.9);
-	border: 1px solid rgba(255, 255, 255, 0.15);
-	border-radius: 8px;
-	box-shadow: 0 10px 24px rgba(0, 0, 0, 0.35);
-	backdrop-filter: blur(6px);
-	color: #e5e7f0;
-}
-
-.panel-header {
-	display: flex;
-	align-items: center;
-	justify-content: space-between;
-	padding: 8px 12px;
-	border-bottom: 1px solid rgba(255, 255, 255, 0.12);
-	font-size: 13px;
-	font-weight: 600;
-}
-
-.panel-close {
-	cursor: pointer;
-	color: rgba(255, 255, 255, 0.7);
-}
-
-.panel-body {
-	display: flex;
-	align-items: center;
-	gap: 8px;
-	padding: 10px 12px 12px;
-}
-
-.panel-label {
-	font-size: 12px;
-	color: rgba(255, 255, 255, 0.75);
-	white-space: nowrap;
-}
-
-.map-toolbar {
-	position: absolute;
-	top: 64px;
-	right: 16px;
-	display: flex;
-	flex-direction: column;
-	gap: 10px;
-	padding: 10px 8px;
-	background: rgba(19, 28, 36, 0.75);
-	backdrop-filter: blur(4px);
-	border-radius: 8px;
-	box-shadow: 0 6px 18px rgba(0, 0, 0, 0.2);
-}
-
-.tool-btn {
-	display: flex;
-	flex-direction: column;
-	align-items: center;
-	gap: 6px;
-	width: 64px;
-	padding: 8px 6px;
-	background: rgba(255, 255, 255, 0.08);
-	border: 1px solid rgba(255, 255, 255, 0.18);
-	border-radius: 6px;
-	color: #ffffff;
-	font-size: 12px;
-	line-height: 1;
-	cursor: pointer;
-	transition: all 0.2s ease;
-}
-
-.tool-btn:hover {
-	background: rgba(255, 255, 255, 0.16);
-	border-color: rgba(255, 255, 255, 0.35);
-	transform: translateY(-1px);
-}
-
-.tool-btn:active {
-	transform: translateY(0);
-}
-
-.tool-btn.danger {
-	border-color: rgba(255, 112, 112, 0.6);
-	color: #ffb3b3;
-}
-
-.tool-icon {
-	position: relative;
-	width: 20px;
-	height: 20px;
-	border: 2px solid #ffffff;
-	border-radius: 3px;
-}
-
-.tool-icon.polygon {
-	border-radius: 4px;
-	transform: rotate(15deg);
-}
-
-.tool-icon.rect {
-	border-radius: 2px;
-}
-
-.tool-icon.ellipse {
-	border-radius: 50%;
-}
-
-.tool-icon.buffer {
-	border-radius: 50%;
-	box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5);
-}
-
-.tool-icon.trash {
-	border: none;
-	box-shadow: none;
-}
-
-.tool-icon.trash::before,
-.tool-icon.trash::after {
-	content: "";
-	position: absolute;
-	left: 4px;
-	right: 4px;
-	background: #ffffff;
-}
-
-.tool-icon.trash::before {
-	top: 4px;
-	height: 10px;
-	border-radius: 2px;
-}
-
-.tool-icon.trash::after {
-	top: 1px;
-	height: 3px;
-	border-radius: 2px;
-}
-
-.shape-table-container {
-	margin-top: 8px;
-	padding: 8px;
-	border-radius: 8px;
-	background: rgba(27, 34, 56, 0.6);
-}
-
-.shape-table-title {
-	margin-bottom: 8px;
-	color: #e5e7f0;
-	font-size: 13px;
-}
-</style>
+<style scoped lang="scss"></style>
diff --git a/applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue b/applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue
index 302d14e..c079e8e 100644
--- a/applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue
+++ b/applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue
@@ -1,24 +1,62 @@
 <template>
-	<el-dialog class="command-page-map-view-dialog" v-model="visible" :show-close="false"
-		:close-on-click-modal="false">
+	<el-dialog class="command-page-map-view-dialog" v-model="visible" :show-close="false" :close-on-click-modal="false">
 		<div class="dialog-container">
 			<div class="left-container">
-				<CommonCesiumMap
-					ref="mapRef"
-					class="leftMap command-cesium"
-					:active="visible"
-					:flat-mode="false"
-					:terrain="true"
-					:layer-mode="4"
-					:boundary="false"
-				/>
+				<CommonCesiumMap ref="mapRef" class="leftMap command-cesium" :active="visible" :flat-mode="false"
+					:terrain="true" :layer-mode="4" :boundary="false" />
+				<div v-if="showTypePanel && !readonly" class="shape-type-panel">
+					<div class="panel-header">
+						<span>{{ drawTypeLabelMap[currentShapeType] || '区域' }}</span>
+						<el-icon class="panel-close" @click.stop="handleTypePanelClose">
+							<Close />
+						</el-icon>
+					</div>
+					<div class="panel-body">
+						<span class="panel-label">区域类型</span>
+						<el-select class="command-select" popper-class="command-select-popper" v-model="activeAreaType"
+							placeholder="请选择" :disabled="currentShapeType === 'buffer'" collapse-tags :multiple="currentShapeType === 'buffer'"
+							@change="handleAreaTypeChange">
+							<el-option v-for="item in dictObj.areaType" :key="item.dictKey" :label="item.dictValue"
+								:value="item.dictKey" />
+						</el-select>
+					</div>
+				</div>
+				<div class="map-toolbar">
+					<el-tooltip content="多边形" placement="left">
+						<button class="tool-btn" type="button" @click="handleDraw('polygon')">
+							<img class="tool-icon-image" :src="polygonIcon" alt="多边形" />
+						</button>
+					</el-tooltip>
+					<el-tooltip content="矩形" placement="left">
+						<button class="tool-btn" type="button" @click="handleDraw('rectangle')">
+							<img class="tool-icon-image" :src="rectIcon" alt="矩形" />
+						</button>
+					</el-tooltip>
+					<el-tooltip content="椭圆" placement="left">
+						<button class="tool-btn" type="button" @click="handleDraw('ellipse')">
+							<img class="tool-icon-image" :src="ellipseIcon" alt="椭圆" />
+						</button>
+					</el-tooltip>
+					<el-tooltip content="缓冲圆" placement="left">
+						<button class="tool-btn" type="button" @click="handleDraw('buffer')">
+							<img class="tool-icon-image" :src="bufferIcon" alt="缓冲圆" />
+						</button>
+					</el-tooltip>
+					<el-tooltip content="删除" placement="left">
+						<button class="tool-btn danger" type="button" @click="handleClearShape">
+							<img class="tool-icon-image" :src="trashIcon" alt="删除" />
+						</button>
+					</el-tooltip>
+				</div>
 			</div>
 
 			<div class="right-container">
 				<div class="header">
 					<span>{{ titleEnum[dialogMode] }}</span>
-				
-					<el-icon class="close-btn" @click.stop="visible = false"><Close /></el-icon>
+
+					<el-icon class="close-btn" @click.stop="visible = false">
+						<Close />
+					</el-icon>
 				</div>
 
 				<div class="content" v-if="readonly">
@@ -27,18 +65,6 @@
 						<el-col :span="24">
 							<div class="label">区域名称</div>
 							<div class="val">{{ formData.areaName }}</div>
-						</el-col>
-						<el-col :span="24">
-							<div class="label">区域位置</div>
-							<div class="val">{{ formatLocation(formData) }}</div>
-						</el-col>
-						<el-col :span="24">
-							<div class="label">区域面积</div>
-							<div class="val">{{ formData.areaSize || '-' }}k㎡</div>
-						</el-col>
-						<el-col :span="24">
-							<div class="label">区域类型</div>
-							<div class="val">{{ getDictLabel(formData.areaType, dictObj.areaType) }}</div>
 						</el-col>
 						<el-col :span="24">
 							<div class="label">触发条件</div>
@@ -64,11 +90,29 @@
 						</el-col>
 					</el-row>
 
+					<div class="detail-title">绘制区域列表</div>
+					<div class="command-table-container">
+						<div class="command-table-content">
+							<el-table class="command-table" :data="shapeList" row-key="id">
+								<el-table-column prop="areaType" label="区域类型">
+									<template v-slot="{ row }">
+										{{ resolveShapeAreaTypeLabel(row) }}
+									</template>
+								</el-table-column>
+								<el-table-column prop="drawType" label="绘制类型">
+									<template v-slot="{ row }">
+										{{ drawTypeLabelMap[row.drawType] || '-' }}
+									</template>
+								</el-table-column>
+							</el-table>
+						</div>
+					</div>
+
 					<div class="detail-title">关联设备</div>
 					<div class="command-table-container">
 						<div class="command-table-content">
-							<el-table class="command-table" ref="deviceTableRef" :data="deviceOptions"
-								row-key="id">
+							<el-table class="command-table" ref="deviceTableRef" :data="deviceOptions" row-key="id"
+								@row-click="handleDeviceRowClick">
 								<el-table-column prop="deviceName" label="设备名称" />
 								<el-table-column prop="deviceType" label="类型">
 									<template v-slot="{ row }">
@@ -83,25 +127,12 @@
 				<el-form class="dialog-form" v-else ref="formRef" :model="formData" :rules="rules" :disabled="readonly"
 					label-width="96px">
 					<el-form-item label="区域名称" prop="areaName">
-						<el-input class="command-input" v-model="formData.areaName" maxlength="50"
-							placeholder="请输入" clearable />
-					</el-form-item>
-					<el-form-item label="区域位置" prop="longitude">
-						<div class="val">{{ formatLocation(formData) }}</div>
-					</el-form-item>
-					<el-form-item label="区域面积" prop="areaSize">
-						<div class="val">{{ formData.areaSize || '-' }}k㎡</div>
-					</el-form-item>
-					<el-form-item label="区域类型" prop="areaType">
-						<el-select class="command-select" popper-class="command-select-popper"
-							v-model="formData.areaType" placeholder="请选择" clearable>
-							<el-option v-for="item in dictObj.areaType" :key="item.dictKey" :label="item.dictValue"
-								:value="item.dictKey" />
-						</el-select>
+						<el-input class="command-input" v-model="formData.areaName" maxlength="50" placeholder="请输入"
+							clearable />
 					</el-form-item>
 					<el-form-item label="响应机制" prop="responseMechanism">
-						<el-input class="command-input" v-model="formData.responseMechanism"
-							maxlength="200" placeholder="请输入" clearable />
+						<el-input class="command-input" v-model="formData.responseMechanism" maxlength="200"
+							placeholder="请输入" clearable />
 					</el-form-item>
 					<el-form-item label="触发条件" prop="triggerCondition">
 						<el-select class="command-select" popper-class="command-select-popper"
@@ -118,10 +149,9 @@
 						</el-select>
 					</el-form-item>
 					<el-form-item label="可飞行时段" prop="flyDateStart">
-						<el-date-picker class="command-date-picker"
-							popper-class="command-date-picker-popper" v-model="flyDateRange"
-							type="datetimerange" range-separator="至" start-placeholder="开始时间" end-placeholder="结束时间"
-							:disabled-date="disabledFlyDate" :disabled-time="disabledFlyTime"
+						<el-date-picker class="command-date-picker" popper-class="command-date-picker-popper"
+							v-model="flyDateRange" type="datetimerange" range-separator="至" start-placeholder="开始时间"
+							end-placeholder="结束时间" :disabled-date="disabledFlyDate" :disabled-time="disabledFlyTime"
 							value-format="YYYY-MM-DD HH:mm:ss" clearable />
 					</el-form-item>
 					<el-form-item label="关联派出所" prop="policeStationId">
@@ -132,20 +162,41 @@
 						</el-select>
 					</el-form-item>
 
+					<div class="shape-table-container">
+						<div class="shape-table-title">绘制区域列表</div>
+						<el-table class="command-table" :data="shapeList" row-key="id" height="auto">
+							<el-table-column prop="areaType" label="区域类型">
+								<template v-slot="{ row }">
+									{{ resolveShapeAreaTypeLabel(row) }}
+								</template>
+							</el-table-column>
+							<el-table-column prop="drawType" label="绘制类型">
+								<template v-slot="{ row }">
+									{{ drawTypeLabelMap[row.drawType] || '-' }}
+								</template>
+							</el-table-column>
+							<el-table-column label="操作" class-name="operation-btns" width="120">
+								<template v-slot="{ row }">
+									<el-link @click="handleShapeEdit(row)">编辑</el-link>
+									<el-link @click="handleShapeDelete(row)">删除</el-link>
+								</template>
+							</el-table-column>
+						</el-table>
+					</div>
+
 					<div class="search-table-container">
 						<div class="search-box">
 							<div class="label">
 								关联设备
 							</div>
 
-							<el-input class="command-input" v-model="searchName" placeholder="请输入"
-								clearable></el-input>
+							<el-input class="command-input" v-model="searchName" placeholder="请输入" clearable></el-input>
 						</div>
 
 						<el-form-item prop="deviceIds" label-width="0">
 							<el-table class="command-table" ref="deviceTableRef"
 								:data="deviceOptions.filter(item => item.deviceName.includes(searchName))" row-key="id"
-								@selection-change="handleDeviceSelectionChange">
+								@selection-change="handleDeviceSelectionChange" @row-click="handleDeviceRowClick">
 								<el-table-column type="selection" width="55" :reserve-selection="true" />
 								<el-table-column prop="deviceName" label="设备名称" />
 								<el-table-column prop="deviceType" label="类型">
@@ -159,7 +210,9 @@
 				</el-form>
 
 				<div class="footer">
-					<el-button v-if="dialogMode != 'view'" color="#2B2B4C" @click="handleCancel">{{ readonly ? '关闭' : '取消' }}</el-button>
+					<el-button v-if="dialogMode != 'view'" color="#2B2B4C" @click="handleCancel">{{ readonly ? '关闭' :
+						'取消'
+					}}</el-button>
 					<el-button color="#284FE3" v-if="!readonly" type="primary" :loading="submitting"
 						:disabled="submitting" @click="handleSubmit">
 						确定
@@ -173,30 +226,32 @@
 <script setup>
 import { Close } from '@element-plus/icons-vue'
 
-import { computed, inject, nextTick, onMounted, ref, watch } from 'vue'
+import { computed, inject, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
 import { ElMessage } from 'element-plus'
 import { fwAreaDivideDetailApi, fwAreaDivideSubmitApi } from './partitionApi'
 import { saveOperationLog } from '@ztzf/apis'
 import { useRoute } from 'vue-router'
 import { fieldRules, geomAnalysis, getDictLabel } from '@ztzf/utils'
 import CommonCesiumMap from '@/components/map-container/common-cesium-map.vue'
-import { DrawPolygon } from '@/utils/cesium/DrawPolygon'
+import { DrawManager } from './shapeTools/DrawManager'
+import { EditManager } from './shapeTools/EditManager'
 import { cartesian3Convert } from '@/utils/cesium/mapUtil'
-import * as turf from '@turf/turf'
+import { buildEllipsePositions } from './shapeTools/shapeUtils'
 import * as Cesium from 'cesium'
 import { fwPoliceStationListApi } from '@/views/areaManage/precinctInfo/precinctInfoApi'
 import { fwDeviceListApi } 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'
+import bufferIcon from '@/assets/images/areaMap/buffer.png'
+import trashIcon from '@/assets/images/areaMap/trash.png'
 
 const initForm = () => ({
 	areaName: '', // 区域名称
-	areaSize: null, // 区域面积
-	areaType: '', // 区域类型
 	controlLevel: '', // 管控级别
 	deviceIds: '', // 关联设备ID
 	flyDateEnd: '', // 可飞行结束
 	flyDateStart: '', // 可飞行开始
-	latitude: null, // 区域中心纬度
-	longitude: null, // 区域中心经度
 	policeStationId: '', // 关联派出所id
 	responseMechanism: '', // 响应机制
 	triggerCondition: '', // 触发条件
@@ -219,14 +274,46 @@
 const deviceOptions = ref([]) // 关联设备
 const dictObj = inject('dictObj')
 const mapRef = ref(null)
+const currentShapeType = ref('polygon')
+const activeAreaType = ref('')
+const shapeList = ref([])
+const activeShapeId = ref(null)
+const drawTypeLabelMap = {
+	polygon: '多边形',
+	rectangle: '矩形',
+	ellipse: '椭圆',
+	buffer: '缓冲圆',
+}
+const bufferAreaTypeValue = '1,2,3'
+const showTypePanel = ref(false)
+const suppressTypePanelOnce = ref(false)
+const areaTypeStyleMap = {
+	'1': {
+		fill: Cesium.Color.fromBytes(25, 178, 230, 128),
+		outline: Cesium.Color.fromBytes(0, 251, 255, 255),
+	},
+	'2': {
+		fill: Cesium.Color.fromBytes(255, 235, 59, 128),
+		outline: Cesium.Color.fromBytes(255, 235, 59, 255),
+	},
+	'3': {
+		fill: Cesium.Color.fromBytes(247, 20, 20, 128),
+		outline: Cesium.Color.fromBytes(255, 0, 0, 255),
+	},
+}
 let viewer
-let drawPolygonExample
+let drawManager
+let editManager
+let activeTool
 let pointList = []
+let viewEntity
+let shapeDisplaySource
+const deviceRangePrimitiveMap = new Map()
+let deviceRangeMaterialRegistered = false
+const activeToolMode = ref('')
 
 const rules = {
 	areaName: fieldRules(true, 50),
-	longitude: fieldRules(true),
-	areaType: fieldRules(true),
 	responseMechanism: fieldRules(true, 200),
 	triggerCondition: fieldRules(true, 200),
 	controlLevel: fieldRules(true),
@@ -274,17 +361,33 @@
 
 // 提交新增/编辑
 async function handleSubmit () {
-	const isValid = await formRef.value?.validate().catch(() => false)
-	if (!isValid) return
-	if (dialogMode.value === 'add' && Number(formData.value.areaSize) < 0.5) {
-		ElMessage.warning('区域面积不能小于0.5km²,请重新绘制')
+	if (!shapeList.value.length) {
+		ElMessage.warning('区域不可为空,请先绘制区域')
 		return
 	}
+
+	const isValid = await formRef.value?.validate().catch(() => false)
+	if (!isValid) return
 	submitting.value = true
 	try {
-		let str = [...pointList, pointList[0]].map(item => `${item.longitude} ${item.latitude}`).join(',')
-		formData.value.geom = `POLYGON((${str}))`
-		await fwAreaDivideSubmitApi(formData.value)
+		const areaDivide = { ...formData.value }
+		delete areaDivide.geom
+		const areaDivideExtList = shapeList.value.map(shape => {
+			const areaTypeKey = shape?.areaType ?? ''
+			const areaTypeValue = getDictLabel(areaTypeKey, resolveAreaTypeOptions())
+			return {
+				id: shape?.extId,
+				areaDivideId: areaDivide?.id ?? '',
+				areaCode: areaDivide?.areaCode ?? '',
+				areaTypeKey,
+				areaTypeValue,
+				geomJson: JSON.stringify(shape),
+			}
+		})
+		await fwAreaDivideSubmitApi({
+			areaDivide,
+			areaDivideExtList,
+		})
 		const actionText = dialogMode.value === 'add' ? '新增' : '编辑'
 		saveOperationLog({
 			requestUri: route.path,
@@ -306,19 +409,43 @@
 		formData.value.flyDateEnd = flyDateRange.value[1] || ''
 	}
 )
+watch(
+	() => visible.value,
+	val => {
+		if (!val) {
+			viewEntity && viewer?.entities?.remove(viewEntity)
+			viewEntity = null
+			clearActiveTool()
+			hideTypePanel()
+			clearShapeDisplay()
+			clearDeviceRangePrimitives()
+		}
+	}
+)
+watch(
+	() => activeAreaType.value,
+	val => {
+		updateActiveShapeAreaType(val)
+	}
+)
 
 // 加载详情
 async function loadDetail () {
 	if (!formData.value.id) return
 	const res = await fwAreaDivideDetailApi({ id: formData.value.id })
-	formData.value = res?.data?.data ?? initForm()
+	const detail = res?.data?.data ?? {}
+	const nextForm = initForm()
+	Object.keys(nextForm).forEach(key => {
+		if (detail[key] !== undefined) {
+			nextForm[key] = detail[key]
+		}
+	})
+	if (detail?.id) {
+		nextForm.id = detail.id
+	}
+	formData.value = nextForm
 	flyDateRange.value = [formData.value.flyDateStart, formData.value.flyDateEnd].filter(Boolean)
-}
-
-// 格式化区域位置
-function formatLocation (row) {
-	if (row?.longitude == null || row?.latitude == null) return ''
-	return `${_.round(row.longitude, 6)}, ${_.round(row.latitude, 6)}`
+	syncShapeListFromDetail(detail)
 }
 
 // 格式化可飞行时间段
@@ -334,66 +461,599 @@
 	viewer = map?.viewer || null
 }
 
+function ensureShapeDisplaySource () {
+	if (!viewer) return null
+	if (!shapeDisplaySource) {
+		shapeDisplaySource = new Cesium.CustomDataSource('partition-shape-display')
+		viewer.dataSources.add(shapeDisplaySource)
+	}
+	return shapeDisplaySource
+}
+
+function registerDeviceRangeMaterial () {
+	if (deviceRangeMaterialRegistered || !Cesium?.Material) return
+	deviceRangeMaterialRegistered = true
+	Cesium.Material._materialCache.addMaterial('DeviceRangeEllipsoid', {
+		fabric: {
+			type: 'DeviceRangeEllipsoid',
+			uniforms: {
+				color: new Cesium.Color(1.0, 0.0, 0.0, 0.7),
+				speed: 3.0,
+			},
+			source: `
+				uniform vec4 color;
+				uniform float speed;
+				czm_material czm_getMaterial(czm_materialInput materialInput){
+					czm_material material = czm_getDefaultMaterial(materialInput);
+					vec2 st = materialInput.st;
+					float time = fract(czm_frameNumber * speed / 1000.0);
+					float alpha = abs(smoothstep(0.5,1.,fract( -st.t - time)));
+					alpha += .1;
+					material.alpha = alpha;
+					material.diffuse = color.rgb;
+					return material;
+				}
+			`,
+		},
+		translucent: () => true,
+	})
+}
+
+function createDeviceRangeMaterial () {
+	registerDeviceRangeMaterial()
+	return Cesium.Material.fromType('DeviceRangeEllipsoid', {
+		color: Cesium.Color.fromBytes(0, 255, 255, 155),
+	})
+}
+
+function clearDeviceRangePrimitives () {
+	if (!viewer || !deviceRangePrimitiveMap.size) return
+	for (const primitive of deviceRangePrimitiveMap.values()) {
+		viewer.scene.primitives.remove(primitive)
+	}
+	deviceRangePrimitiveMap.clear()
+}
+
+function normalizeDeviceRange (value) {
+	const range = Number(value)
+	if (!Number.isFinite(range) || range <= 0) return 2000
+	return range
+}
+
+function getDeviceLngLat (device) {
+	const lngRaw = device?.longitude ?? device?.lng ?? device?.lon
+	const latRaw = device?.latitude ?? device?.lat
+	const lng = Number(lngRaw)
+	const lat = Number(latRaw)
+	if (!Number.isFinite(lng) || !Number.isFinite(lat)) return null
+	return { lng, lat }
+}
+
+function createDeviceRangePrimitive (device) {
+	if (!viewer) return null
+	if (!device) return null
+	const position = getDeviceLngLat(device)
+	if (!position) return null
+	const rangeMeters = normalizeDeviceRange(device.effectiveRangeKm)
+	const center = Cesium.Cartesian3.fromDegrees(position.lng, position.lat, 0)
+	const radii = new Cesium.Cartesian3(rangeMeters, rangeMeters, rangeMeters)
+	const geometry = new Cesium.EllipsoidGeometry({
+		radii,
+		vertexFormat: Cesium.MaterialAppearance.MaterialSupport.TEXTURED?.vertexFormat
+			|| Cesium.VertexFormat.POSITION_AND_NORMAL,
+	})
+	const instance = new Cesium.GeometryInstance({
+		geometry,
+		modelMatrix: Cesium.Transforms.eastNorthUpToFixedFrame(center),
+	})
+	return new Cesium.Primitive({
+		geometryInstances: instance,
+		appearance: new Cesium.MaterialAppearance({
+			material: createDeviceRangeMaterial(),
+			closed: true,
+			translucent: true,
+			flat: true,
+			faceForward: true,
+		}),
+		asynchronous: false
+	})
+}
+
+function renderDeviceRangeSingle (device) {
+	if (!viewer) return
+	clearDeviceRangePrimitives()
+	const primitive = createDeviceRangePrimitive(device)
+	if (!primitive) return
+	deviceRangePrimitiveMap.set(String(device?.id ?? Date.now()), primitive)
+	viewer.scene.primitives.add(primitive)
+}
+
+function renderDeviceRanges (rows) {
+	if (!viewer) return
+	const selectedRows = Array.isArray(rows) ? rows : []
+	const nextIds = new Set(selectedRows.map(row => String(row?.id)))
+	for (const [id, primitive] of deviceRangePrimitiveMap.entries()) {
+		if (!nextIds.has(id)) {
+			viewer.scene.primitives.remove(primitive)
+			deviceRangePrimitiveMap.delete(id)
+		}
+	}
+	selectedRows.forEach(row => {
+		const id = String(row?.id)
+		if (!id || deviceRangePrimitiveMap.has(id)) return
+		const primitive = createDeviceRangePrimitive(row)
+		if (!primitive) return
+		deviceRangePrimitiveMap.set(id, primitive)
+		viewer.scene.primitives.add(primitive)
+	})
+}
+
+function clearShapeDisplay () {
+	if (!shapeDisplaySource) return
+	shapeDisplaySource.entities.removeAll()
+}
+
+function normalizeShapePoint (point) {
+	const lng = point?.lng ?? point?.longitude
+	const lat = point?.lat ?? point?.latitude
+	if (!Number.isFinite(Number(lng)) || !Number.isFinite(Number(lat))) return null
+	return {
+		lng: Number(lng),
+		lat: Number(lat),
+		height: Number.isFinite(Number(point?.height)) ? Number(point.height) : 0,
+	}
+}
+
+function resolveLngLatPoint (point) {
+	if (!point) return null
+	const lng = point?.lng ?? point?.longitude
+	const lat = point?.lat ?? point?.latitude
+	if (Number.isFinite(Number(lng)) && Number.isFinite(Number(lat))) {
+		return {
+			lng: Number(lng),
+			lat: Number(lat),
+			height: Number.isFinite(Number(point?.height)) ? Number(point.height) : 0,
+		}
+	}
+	if (!viewer || !Number.isFinite(point?.x) || !Number.isFinite(point?.y) || !Number.isFinite(point?.z)) {
+		return null
+	}
+	const val = cartesian3Convert(point, viewer)
+	return {
+		lng: val.longitude,
+		lat: val.latitude,
+		height: Number.isFinite(val.height) ? val.height : 0,
+	}
+}
+
+function resolveControlPoints (meta, drawType) {
+	if (!meta) return []
+	if (Array.isArray(meta?.controlPoints)) {
+		return meta.controlPoints.map(resolveLngLatPoint).filter(Boolean)
+	}
+	if (drawType === 'ellipse') {
+		return [meta.center, meta.majorPoint, meta.minorPoint].map(resolveLngLatPoint).filter(Boolean)
+	}
+	if (drawType === 'buffer') {
+		return [meta.center, meta.radiusPoint1, meta.radiusPoint2, meta.radiusPoint3]
+			.map(resolveLngLatPoint)
+			.filter(Boolean)
+	}
+	return []
+}
+
+function buildShapePositions (points = []) {
+	const normalized = points.map(normalizeShapePoint).filter(Boolean)
+	return normalized.map(point =>
+		Cesium.Cartesian3.fromDegrees(point.lng, point.lat, point.height)
+	)
+}
+
+function getShapeDisplayPoints (shape) {
+	if (Array.isArray(shape?.displayPoints) && shape.displayPoints.length) {
+		return shape.displayPoints
+	}
+	return shape?.points || []
+}
+
+function getAreaTypeStyle (areaType) {
+	return areaTypeStyleMap?.[String(areaType)] || {
+		fill: Cesium.Color.fromBytes(45, 140, 240, 99),
+		outline: Cesium.Color.fromBytes(45, 140, 240, 255),
+	}
+}
+
+function renderShapeList () {
+	if (!viewer || !visible.value) {
+		clearShapeDisplay()
+		return
+	}
+	const dataSource = ensureShapeDisplaySource()
+	if (!dataSource) return
+	dataSource.entities.removeAll()
+	shapeList.value.forEach(shape => {
+		if (activeToolMode.value === 'edit' && shape.id === activeShapeId.value) return
+		if (shape.drawType === 'buffer' && shape.meta?.bufferRadii?.length && shape.meta?.center) {
+			const center = shape.meta.center
+			const centerCartesian = Cesium.Cartesian3.fromDegrees(center.lng, center.lat, center.height || 0)
+			const [radius1, radius2, radius3] = shape.meta.bufferRadii
+			const radii = [radius1, radius2, radius3].filter(item => Number.isFinite(item) && item > 0)
+			if (radii.length) {
+				const styles = [
+					{
+						fill: Cesium.Color.fromBytes(247, 20, 20, 128),
+						outline: Cesium.Color.fromBytes(255, 0, 0, 255),
+					},
+					{
+						fill: Cesium.Color.fromBytes(255, 235, 59, 128),
+						outline: Cesium.Color.fromBytes(255, 235, 59, 255),
+					},
+					{
+						fill: Cesium.Color.fromBytes(25, 178, 230, 128),
+						outline: Cesium.Color.fromBytes(0, 251, 255, 255),
+					},
+				]
+				radii.forEach((radius, index) => {
+					const style = styles[index] || styles[styles.length - 1]
+					dataSource.entities.add({
+						name: 'shape-display',
+						customData: { shapeId: shape.id, drawType: shape.drawType, level: index + 1 },
+						position: centerCartesian,
+						ellipse: {
+							semiMajorAxis: radius,
+							semiMinorAxis: radius,
+							material: style.fill,
+							outline: false,
+							heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
+						},
+					})
+					const positions = buildEllipsePositions(centerCartesian, radius, radius)
+					dataSource.entities.add({
+						name: 'shape-display',
+						customData: { shapeId: shape.id, drawType: shape.drawType, level: index + 1, outline: true },
+						polyline: {
+							positions: positions.length ? [...positions, positions[0]] : positions,
+							clampToGround: true,
+							width: 2,
+							material: style.outline,
+						},
+					})
+				})
+			}
+			return
+		}
+
+		if (!shape?.points?.length) return
+		const positions = buildShapePositions(getShapeDisplayPoints(shape))
+		if (positions.length < 3) return
+		const style = getAreaTypeStyle(shape.areaType)
+		dataSource.entities.add({
+			name: 'shape-display',
+			customData: { shapeId: shape.id, drawType: shape.drawType },
+			polygon: {
+				hierarchy: new Cesium.PolygonHierarchy(positions),
+				material: style.fill,
+				outline: false,
+				heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
+			},
+			polyline: {
+				positions: positions.length ? [...positions, positions[0]] : positions,
+				clampToGround: true,
+				width: 2,
+				material: style.outline,
+			},
+		})
+	})
+}
+
 // 绘制完成回调
 const drawFinished = async data => {
-	pointList = _.cloneDeep(data).map(item => {
+	const positions = Array.isArray(data?.positions) ? data.positions : data
+	if (!Array.isArray(positions) || positions.length < 3) {
+		pointList = []
+		hideTypePanel()
+		return
+	}
+	const meta = data?.meta
+	const bufferRadii = Array.isArray(meta?.bufferRadii) ? meta.bufferRadii : null
+	const centerCartesian = meta?.center
+	const centerLngLat = centerCartesian ? cartesian3Convert(centerCartesian, viewer) : null
+	pointList = _.cloneDeep(positions).map(item => {
 		const val = cartesian3Convert(item, viewer)
 		return { ...val, lng: val.longitude, lat: val.latitude }
 	})
-	const polygon = turf.polygon([
-		[...pointList.map(item => [item.longitude, item.latitude]), [pointList[0].longitude, pointList[0].latitude]],
-	])
-	const center = turf.centerOfMass(polygon)
-	const areaSqm = turf.area(polygon) // 平方米
-	formData.value.areaSize = _.round(areaSqm / 1_000_000, 2) // 平方千米
-	formData.value.longitude = center.geometry.coordinates[0]
-	formData.value.latitude = center.geometry.coordinates[1]
-}
-
-// 新增模式绘制
-function addPolygon () {
-	drawPolygonExample = new DrawPolygon(viewer)
-	drawPolygonExample.initHandler(viewer)
-	drawPolygonExample.subscribe('getPolygonPositions', drawFinished)
-}
-
-// 编辑面
-function editPolygon () {
-	if (!formData.value?.geom) return
-	pointList = geomAnalysis(formData.value.geom)
-	drawPolygonExample?.destroy()
-	drawPolygonExample = new DrawPolygon(viewer)
-	let pointList1 = _.cloneDeep(pointList)
-	pointList1.pop()
-	drawPolygonExample.initPolygon(
-		viewer,
-		pointList.map(item => ({ lng: item.longitude, lat: item.latitude }))
-	)
-	drawPolygonExample.subscribe('getPolygonPositions', drawFinished)
-	if (pointList1.length) {
-		const result = pointList1.map(item => [item.longitude, item.latitude]).flat()
-		const tempEntity = viewer.entities?.add({
-			position: Cesium.Cartesian3.fromDegrees(result[0], result[1]),
-			polyline: {
-				positions: Cesium.Cartesian3.fromDegreesArray(result),
-				clampToGround: true,
-				width: 1,
-				material: Cesium.Color.TRANSPARENT,
-			},
-		})
-		if (tempEntity) {
-			viewer.flyTo(tempEntity, { duration: 0 })
-			viewer.entities.remove(tempEntity)
+	const controlPoints = resolveControlPoints(meta, currentShapeType.value)
+	if (!activeAreaType.value) {
+		const detectAreaType = getDetectAreaTypeKey()
+		if (detectAreaType) {
+			activeAreaType.value = detectAreaType
 		}
+	}
+	if (currentShapeType.value === 'buffer') {
+		activeAreaType.value = bufferAreaTypeValue.split(',')
+	}
+	if (!suppressTypePanelOnce.value) {
+		showTypePanelAtCurrent()
+	}
+	suppressTypePanelOnce.value = false
+
+	if (!activeShapeId.value) {
+		activeShapeId.value = `shape_${Date.now()}_${Math.random().toString(16).slice(2, 6)}`
+	}
+	const targetIndex = shapeList.value.findIndex(item => item.id === activeShapeId.value)
+	const displayPoints = ['ellipse', 'buffer'].includes(currentShapeType.value)
+		? _.cloneDeep(pointList)
+		: null
+	let shapeMeta = null
+	if (currentShapeType.value === 'buffer' && bufferRadii?.length && centerLngLat) {
+		shapeMeta = {
+			bufferRadii,
+			center: { lng: centerLngLat.longitude, lat: centerLngLat.latitude, height: centerLngLat.height ?? 0 },
+		}
+		if (controlPoints.length) {
+			shapeMeta.controlPoints = controlPoints
+		}
+	} else if (currentShapeType.value === 'ellipse') {
+		const centerPoint = resolveLngLatPoint(meta?.center)
+		const majorPoint = resolveLngLatPoint(meta?.majorPoint)
+		const minorPoint = resolveLngLatPoint(meta?.minorPoint)
+		if (centerPoint || majorPoint || minorPoint) {
+			shapeMeta = {
+				center: centerPoint,
+				majorPoint,
+				minorPoint,
+				semiMajor: Number.isFinite(Number(meta?.semiMajor)) ? Number(meta.semiMajor) : null,
+				semiMinor: Number.isFinite(Number(meta?.semiMinor)) ? Number(meta.semiMinor) : null,
+			}
+			if (controlPoints.length) {
+				shapeMeta.controlPoints = controlPoints
+			}
+		}
+	}
+	const shapePayload = {
+		id: activeShapeId.value,
+		areaType: currentShapeType.value === 'buffer' ? bufferAreaTypeValue : activeAreaType.value,
+		drawType: currentShapeType.value,
+		points: ['ellipse', 'buffer'].includes(currentShapeType.value) && controlPoints.length
+			? controlPoints
+			: _.cloneDeep(pointList),
+		displayPoints,
+		meta: shapeMeta,
+	}
+	if (targetIndex === -1) {
+		shapeList.value.push(shapePayload)
+	} else {
+		shapeList.value.splice(targetIndex, 1, shapePayload)
+	}
+	renderShapeList()
+}
+
+function getDetectAreaTypeKey () {
+	const areaTypeOptions = resolveAreaTypeOptions()
+	if (!Array.isArray(areaTypeOptions)) return ''
+	const target = areaTypeOptions.find(item => item?.dictKey === '1')
+	return target?.dictKey ?? ''
+}
+
+function resolveAreaTypeOptions () {
+	return Array.isArray(dictObj?.value?.areaType)
+		? dictObj.value.areaType
+		: dictObj?.areaType
+}
+
+function resolveShapeAreaTypeLabel (shape) {
+	if (shape?.drawType === 'buffer') {
+		return '侦测区/报警区/重点管制区'
+	}
+	return getDictLabel(shape?.areaType, resolveAreaTypeOptions())
+}
+
+function parseGeomJson (geomJson) {
+	if (!geomJson) return null
+	if (typeof geomJson === 'object') return geomJson
+	if (typeof geomJson !== 'string') return null
+	const trimmed = geomJson.trim()
+	if (!trimmed) return null
+	try {
+		return JSON.parse(trimmed)
+	} catch (error) {
+		const legacyPoints = geomAnalysis(trimmed)
+		if (Array.isArray(legacyPoints) && legacyPoints.length >= 3) {
+			return { drawType: 'polygon', points: legacyPoints }
+		}
+	}
+	return null
+}
+
+function syncShapeListFromDetail (detail) {
+	const extList = Array.isArray(detail?.fwAreaDivideExtList) ? detail.fwAreaDivideExtList : []
+	if (!extList.length) {
+		shapeList.value = []
+		activeAreaType.value = ''
+		currentShapeType.value = 'polygon'
+		return
+	}
+	shapeList.value = extList.map((item, index) => {
+		const isShapePayload = item?.drawType || item?.points
+		if (isShapePayload) {
+			const areaType = item?.drawType === 'buffer'
+				? bufferAreaTypeValue
+				: (item?.areaType ?? '')
+			return {
+				id: item?.id || `shape_${Date.now()}_${index}_${Math.random().toString(16).slice(2, 6)}`,
+				extId: item?.extId ?? item?.id,
+				areaType,
+				drawType: item?.drawType ?? 'polygon',
+				points: Array.isArray(item?.points) ? item.points : [],
+				displayPoints: Array.isArray(item?.displayPoints) ? item.displayPoints : null,
+				meta: item?.meta ?? null,
+			}
+		}
+		const parsed = parseGeomJson(item?.geomJson) || {}
+		const drawType = parsed?.drawType || 'polygon'
+		const areaType = drawType === 'buffer'
+			? bufferAreaTypeValue
+			: (item?.areaTypeKey ?? parsed?.areaType ?? '')
+		const points = Array.isArray(parsed?.points) ? parsed.points : []
+		const displayPoints = Array.isArray(parsed?.displayPoints) ? parsed.displayPoints : null
+		const meta = parsed?.meta ?? null
+		return {
+			id: parsed?.id || `shape_${Date.now()}_${index}_${Math.random().toString(16).slice(2, 6)}`,
+			extId: item?.id,
+			areaType,
+			drawType,
+			points,
+			displayPoints,
+			meta,
+		}
+	})
+	activeAreaType.value = shapeList.value[0]?.drawType === 'buffer'
+		? bufferAreaTypeValue.split(',')
+		: (shapeList.value[0]?.areaType ?? '')
+	currentShapeType.value = shapeList.value[0]?.drawType ?? 'polygon'
+}
+
+function showTypePanelAtCurrent () {
+	if (readonly.value) return
+	showTypePanel.value = true
+}
+
+function hideTypePanel () {
+	showTypePanel.value = false
+}
+
+function handleTypePanelClose () {
+	hideTypePanel()
+	if (readonly.value) return
+	if (activeToolMode.value !== 'edit') return
+	const positions = activeTool?.getPositions?.()
+	if (Array.isArray(positions) && positions.length >= 3) {
+		suppressTypePanelOnce.value = true
+		drawFinished(positions)
+	}
+	if (positions?.positions?.length >= 3) {
+		suppressTypePanelOnce.value = true
+		drawFinished(positions)
+	}
+	clearActiveTool()
+}
+
+function handleAreaTypeChange (value) {
+	updateActiveShapeAreaType(value)
+}
+
+function updateActiveShapeAreaType (value) {
+	if (currentShapeType.value === 'buffer') return
+	activeTool?.setStyle?.(getAreaTypeStyle(value))
+	if (!activeShapeId.value) return
+	const targetIndex = shapeList.value.findIndex(item => item.id === activeShapeId.value)
+	if (targetIndex === -1) return
+	const target = shapeList.value[targetIndex]
+	shapeList.value.splice(targetIndex, 1, {
+		...target,
+		areaType: value,
+	})
+	renderShapeList()
+}
+
+// Tool lifecycle
+function clearActiveTool () {
+	activeTool?.destroy?.()
+	activeTool = null
+	drawManager?.destroy()
+	editManager?.destroy()
+	activeToolMode.value = ''
+	renderShapeList()
+}
+
+function startDraw (type) {
+	currentShapeType.value = type
+	activeToolMode.value = 'draw'
+	drawManager ||= new DrawManager(viewer)
+	activeTool = drawManager.start(type, { style: getAreaTypeStyle(activeAreaType.value) })
+	activeTool?.subscribe('getPolygonPositions', drawFinished)
+}
+
+function startEdit (type, points) {
+	currentShapeType.value = type
+	activeToolMode.value = 'edit'
+	editManager ||= new EditManager(viewer)
+	activeTool = editManager.start(type, points, { style: getAreaTypeStyle(activeAreaType.value) })
+	activeTool?.subscribe('getPolygonPositions', drawFinished)
+}
+
+function handleDraw (type) {
+	if (readonly.value) return
+	if (!viewer) return
+	clearActiveTool()
+	viewEntity && viewer?.entities?.remove(viewEntity)
+	viewEntity = null
+	hideTypePanel()
+	renderShapeList()
+	pointList = []
+	const defaultAreaType = getDetectAreaTypeKey()
+	if (type === 'buffer') {
+		activeAreaType.value = bufferAreaTypeValue
+	} else if (defaultAreaType) {
+		activeAreaType.value = defaultAreaType
+	}
+	activeShapeId.value = null
+	startDraw(type)
+}
+
+function handleClearShape () {
+	if (readonly.value) return
+	clearActiveTool()
+	viewEntity && viewer?.entities?.remove(viewEntity)
+	viewEntity = null
+	pointList = []
+	shapeList.value = []
+	activeShapeId.value = null
+	activeAreaType.value = ''
+	hideTypePanel()
+	clearShapeDisplay()
+}
+
+function handleShapeEdit (row) {
+	if (!row?.points?.length) return
+	if (activeToolMode.value === 'edit' && activeShapeId.value === row.id) return
+	activeShapeId.value = row.id
+	currentShapeType.value = row.drawType
+	activeAreaType.value = row.drawType === 'buffer' ? bufferAreaTypeValue.split(',') : row.areaType
+	const displayPoints = getShapeDisplayPoints(row)
+	pointList = _.cloneDeep(displayPoints)
+	clearActiveTool()
+	const editPayload = ['buffer', 'ellipse'].includes(row.drawType)
+		? { points: displayPoints, meta: row.meta }
+		: row.points
+	startEdit(row.drawType, editPayload)
+	showTypePanelAtCurrent()
+	renderShapeList()
+}
+
+function handleShapeDelete (row) {
+	if (!row) return
+	shapeList.value = shapeList.value.filter(item => item.id !== row.id)
+	renderShapeList()
+	if (activeShapeId.value === row.id) {
+		activeShapeId.value = null
+		clearActiveTool()
+		pointList = []
+		hideTypePanel()
 	}
 }
 
 // 查看面
 function viewPolygon () {
+	if (!viewer) return
+	if (shapeList.value.length) {
+		renderShapeList()
+		return
+	}
 	if (!formData.value?.geom) return
 	pointList = geomAnalysis(formData.value.geom)
 	const result = pointList.map(item => [item.longitude, item.latitude]).flat()
-	const mian = viewer.entities?.add({
+	viewEntity = viewer.entities?.add({
 		customType: 'control_group',
 		position: Cesium.Cartesian3.fromDegrees(result[0], result[1]),
 		polyline: {
@@ -403,7 +1063,7 @@
 			material: Cesium.Color.RED,
 		},
 	})
-	viewer.flyTo(mian, { duration: 0 })
+	viewer.flyTo(viewEntity, { duration: 0 })
 }
 
 // 获取派出所列表
@@ -427,21 +1087,36 @@
 	selectedDeviceRows.value = rows
 	const ids = rows.map(item => item.id)
 	formData.value.deviceIds = ids.join(',')
+	renderDeviceRanges(rows)
+}
+
+function handleDeviceRowClick (row) {
+	if (readonly.value) {
+		renderDeviceRangeSingle(row)
+		return
+	}
+	renderDeviceRanges(selectedDeviceRows.value)
 }
 
 // 同步选择状态
 function syncDeviceSelection () {
-	if (!deviceTableRef.value) return
-	deviceTableRef.value.clearSelection()
 	const rows = []
 	const arr = formData.value.deviceIds.split(',')
 	deviceOptions.value.forEach(row => {
 		if (arr.includes(row.id)) {
-			deviceTableRef.value.toggleRowSelection(row, true)
 			rows.push(row)
 		}
 	})
 	selectedDeviceRows.value = rows
+	if (readonly.value) {
+		renderDeviceRanges(selectedDeviceRows.value)
+		return
+	}
+	if (!deviceTableRef.value) return
+	deviceTableRef.value.clearSelection()
+	rows.forEach(row => {
+		deviceTableRef.value.toggleRowSelection(row, true)
+	})
 }
 
 // 打开弹框
@@ -449,25 +1124,40 @@
 	dialogMode.value = mode || 'add'
 	formData.value = dialogMode.value === 'add' ? initForm() : row
 	selectedDeviceRows.value = []
+	shapeList.value = []
+	activeShapeId.value = null
+	activeAreaType.value = ''
+	pointList = []
 	await nextTick()
 	initMap()
+	clearShapeDisplay()
+	clearDeviceRangePrimitives()
+	viewEntity && viewer?.entities?.remove(viewEntity)
+	viewEntity = null
+	clearActiveTool()
 	await getDeviceList()
 	if (dialogMode.value === 'add') {
-		addPolygon()
+		// default no draw mode
 	} else if (dialogMode.value === 'edit') {
 		await loadDetail()
-		editPolygon()
+		viewPolygon()
 	} else {
 		await loadDetail()
 		viewPolygon()
 	}
 	await nextTick()
 	syncDeviceSelection()
+	if (!readonly.value) {
+		renderDeviceRanges(selectedDeviceRows.value)
+	}
 }
 
 onMounted(() => {
 	getPoliceStationList()
-	getDeviceList()
+})
+
+onBeforeUnmount(() => {
+	hideTypePanel()
 })
 
 defineExpose({
@@ -475,4 +1165,168 @@
 })
 </script>
 
-<style scoped lang="scss"></style>
+<style scoped lang="scss">
+.left-container {
+	position: relative;
+}
+
+.shape-type-panel {
+	position: absolute;
+	right: 16px;
+	bottom: 16px;
+	z-index: 6;
+	min-width: 260px;
+	background: rgba(22, 33, 44, 0.9);
+	border: 1px solid rgba(255, 255, 255, 0.15);
+	border-radius: 8px;
+	box-shadow: 0 10px 24px rgba(0, 0, 0, 0.35);
+	backdrop-filter: blur(6px);
+	color: #e5e7f0;
+}
+
+.panel-header {
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+	padding: 8px 12px;
+	border-bottom: 1px solid rgba(255, 255, 255, 0.12);
+	font-size: 13px;
+	font-weight: 600;
+}
+
+.panel-close {
+	cursor: pointer;
+	color: rgba(255, 255, 255, 0.7);
+}
+
+.panel-body {
+	display: flex;
+	align-items: center;
+	gap: 8px;
+	padding: 10px 12px 12px;
+}
+
+.panel-label {
+	font-size: 12px;
+	color: rgba(255, 255, 255, 0.75);
+	white-space: nowrap;
+}
+
+.map-toolbar {
+	position: absolute;
+	top: 64px;
+	right: 16px;
+	display: flex;
+	flex-direction: column;
+	gap: 10px;
+	padding: 10px 8px;
+	background: rgba(19, 28, 36, 0.75);
+	backdrop-filter: blur(4px);
+	border-radius: 8px;
+	box-shadow: 0 6px 18px rgba(0, 0, 0, 0.2);
+}
+
+.tool-btn {
+	padding: 0;
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	gap: 6px;
+	background: rgba(255, 255, 255, 0.08);
+	border: 1px solid rgba(255, 255, 255, 0.18);
+	border-radius: 8px;
+	color: #ffffff;
+	font-size: 12px;
+	line-height: 1;
+	cursor: pointer;
+	transition: all 0.2s ease;
+	overflow: hidden;
+}
+
+.tool-btn:hover {
+	background: rgba(255, 255, 255, 0.16);
+	border-color: rgba(255, 255, 255, 0.35);
+	transform: translateY(-1px);
+}
+
+.tool-btn:active {
+	transform: translateY(0);
+}
+
+.tool-btn.danger {
+	border-color: rgba(255, 112, 112, 0.6);
+	color: #ffb3b3;
+}
+
+.tool-icon {
+	position: relative;
+}
+
+.tool-icon-image {
+	width: 36px;
+	height: 36px;
+}
+
+.tool-icon.polygon {
+	border-radius: 4px;
+	transform: rotate(15deg);
+}
+
+.tool-icon.rect {
+	border-radius: 2px;
+}
+
+.tool-icon.ellipse {
+	border-radius: 50%;
+}
+
+.tool-icon.buffer {
+	border-radius: 50%;
+	box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.5);
+}
+
+.tool-icon.trash {
+	border: none;
+	box-shadow: none;
+}
+
+.tool-icon.trash::before,
+.tool-icon.trash::after {
+	content: "";
+	position: absolute;
+	left: 4px;
+	right: 4px;
+	background: #ffffff;
+}
+
+.tool-icon.trash::before {
+	top: 4px;
+	height: 10px;
+	border-radius: 2px;
+}
+
+.tool-icon.trash::after {
+	top: 1px;
+	height: 3px;
+	border-radius: 2px;
+}
+
+.shape-table-container {
+	margin-bottom: 20px;
+
+	.shape-table-title {
+		margin-bottom: 20px;
+		display: flex;
+		align-items: center;
+		justify-content: flex-start;
+		padding-right: 1.2rem;
+		font-family: Source Han Sans CN, Source Han Sans CN;
+		font-weight: 500;
+		font-size: 1.4rem;
+		color: #D4D5D7;
+		font-style: normal;
+		text-transform: none;
+		box-sizing: border-box;
+	}
+}
+</style>
diff --git a/applications/drone-command/src/views/areaManage/partition/index.vue b/applications/drone-command/src/views/areaManage/partition/index.vue
index bd3f8ae..63cd183 100644
--- a/applications/drone-command/src/views/areaManage/partition/index.vue
+++ b/applications/drone-command/src/views/areaManage/partition/index.vue
@@ -5,10 +5,9 @@
 				<el-input class="command-input" v-model="searchParams.areaName" placeholder="请输入"
 					clearable @clear="handleSearch" />
 			</el-form-item>
-
-			<el-form-item label="区域类型" prop="areaType">
+			<el-form-item label="区域类型" prop="areaTypeKeys">
 				<el-select class="command-select" popper-class="command-select-popper"
-					v-model="searchParams.areaType" placeholder="请选择" clearable @change="handleSearch">
+					v-model="searchParams.areaTypeKeys" placeholder="请选择" clearable @change="handleSearch">
 					<el-option v-for="item in dictObj.areaType" :key="item.dictKey" :label="item.dictValue"
 						:value="item.dictKey" />
 				</el-select>
@@ -31,24 +30,18 @@
 					<el-table-column type="selection" width="46" />
 					<el-table-column type="index" show-overflow-tooltip width="64" label="序号" />
 					<el-table-column prop="areaName" show-overflow-tooltip width="150" label="区域名称" />
-					<el-table-column show-overflow-tooltip width="160" label="区域位置">
+					<el-table-column prop="areaTypeKeys" show-overflow-tooltip width="120" label="区域类型">
 						<template v-slot="{ row }">
-							{{ formatLocation(row) }}
+							{{ getDictLabel(row.areaTypeKeys, dictObj.areaType) }}
 						</template>
 					</el-table-column>
-					<el-table-column prop="areaSize" show-overflow-tooltip width="130" label="区域面积(k㎡)" />
-					<el-table-column prop="areaType" show-overflow-tooltip width="120" label="区域类型">
-						<template v-slot="{ row }">
-							{{ getDictLabel(row.areaType, dictObj.areaType) }}
-						</template>
-					</el-table-column>
-					<el-table-column prop="triggerCondition" show-overflow-tooltip width="130" label="触发条件">
+					<el-table-column prop="triggerCondition" show-overflow-tooltip label="触发条件">
 						<template v-slot="{ row }">
 							{{ getDictLabel(row.triggerCondition, dictObj.triggeringCondition) }}
 						</template>
 					</el-table-column>
-					<el-table-column prop="responseMechanism" show-overflow-tooltip width="130" label="响应机制" />
-					<el-table-column prop="controlLevel" show-overflow-tooltip width="120" label="管控级别">
+					<el-table-column prop="responseMechanism" show-overflow-tooltip label="响应机制" />
+					<el-table-column prop="controlLevel" show-overflow-tooltip label="管控级别">
 						<template v-slot="{ row }">
 							{{ getDictLabel(row.controlLevel, dictObj.controlLevel) }}
 						</template>
@@ -93,7 +86,7 @@
 
 const initSearchParams = () => ({
 	areaName: '', // 区域名称
-	areaType: '', // 区域类型
+	areaTypeKeys: '', // 区域类型
 	current: 1, // 当前页
 	size: 10, // 每页大小
 })
@@ -157,11 +150,6 @@
 // 勾选值设置
 function handleSelectionChange (rows) {
 	selectedIds.value = rows.map(item => item.id)
-}
-
-function formatLocation (row) {
-	if (row?.longitude == null || row?.latitude == null) return ''
-	return `${row.longitude}, ${row.latitude}`
 }
 
 function formatFlyDate (row) {
diff --git a/applications/drone-command/src/views/areaManage/partition/shapeTools/draw/DrawBufferCircleTool.js b/applications/drone-command/src/views/areaManage/partition/shapeTools/draw/DrawBufferCircleTool.js
index ca5d4a9..937ce48 100644
--- a/applications/drone-command/src/views/areaManage/partition/shapeTools/draw/DrawBufferCircleTool.js
+++ b/applications/drone-command/src/views/areaManage/partition/shapeTools/draw/DrawBufferCircleTool.js
@@ -18,6 +18,9 @@
 		this.circleLevel1OutlineEntity = null
 		this.circleLevel2OutlineEntity = null
 		this.circleLevel3OutlineEntity = null
+		this.radiusPoint1 = null
+		this.radiusPoint2 = null
+		this.radiusPoint3 = null
 		this.isDrawing = false
 		this.drawStep = 0
 		this.isCompleted = false
@@ -56,6 +59,7 @@
 			this.radiusLevel1 = radius
 			this.radiusLevel2 = radius
 			this.radiusLevel3 = radius
+			this.radiusPoint1 = position
 			this.drawStep = 2
 			this.tooltip.show('单击设置二级缓冲圆', click.position)
 			return
@@ -65,6 +69,7 @@
 			const radius = Math.max(1, this.getSurfaceDistance(this.centerCartesian, position))
 			this.radiusLevel2 = Math.max(radius, this.radiusLevel1)
 			this.radiusLevel3 = this.radiusLevel2
+			this.radiusPoint2 = position
 			this.drawStep = 3
 			this.tooltip.show('单击设置三级缓冲圆', click.position)
 			return
@@ -73,6 +78,7 @@
 		if (this.drawStep === 3) {
 			const radius = Math.max(1, this.getSurfaceDistance(this.centerCartesian, position))
 			this.radiusLevel3 = Math.max(radius, this.radiusLevel2)
+			this.radiusPoint3 = position
 			this.isDrawing = false
 			this.drawStep = 0
 			this.isCompleted = true
@@ -82,6 +88,9 @@
 				meta: {
 					center: this.centerCartesian,
 					bufferRadii: [this.radiusLevel1, this.radiusLevel2, this.radiusLevel3],
+					controlPoints: [this.centerCartesian, this.radiusPoint1, this.radiusPoint2, this.radiusPoint3].filter(
+						Boolean
+					),
 				},
 			})
 			this.tooltip.hide()
@@ -229,6 +238,9 @@
 		this.radiusLevel1 = 0
 		this.radiusLevel2 = 0
 		this.radiusLevel3 = 0
+		this.radiusPoint1 = null
+		this.radiusPoint2 = null
+		this.radiusPoint3 = null
 		this.isDrawing = false
 		this.drawStep = 0
 		this.isCompleted = false
diff --git a/applications/drone-command/src/views/areaManage/partition/shapeTools/draw/DrawEllipseTool.js b/applications/drone-command/src/views/areaManage/partition/shapeTools/draw/DrawEllipseTool.js
index 724288c..5f48d80 100644
--- a/applications/drone-command/src/views/areaManage/partition/shapeTools/draw/DrawEllipseTool.js
+++ b/applications/drone-command/src/views/areaManage/partition/shapeTools/draw/DrawEllipseTool.js
@@ -70,7 +70,16 @@
 			this.drawStep = 0
 			this.isCompleted = true
 			const positions = buildEllipsePositions(this.centerCartesian, this.semiMajor, this.semiMinor)
-			this.notify('getPolygonPositions', positions)
+			this.notify('getPolygonPositions', {
+				positions,
+				meta: {
+					center: this.centerCartesian,
+					majorPoint: this.majorPoint,
+					minorPoint: this.minorPoint,
+					semiMajor: this.semiMajor,
+					semiMinor: this.semiMinor,
+				},
+			})
 			this.tooltip.hide()
 			this.clearPreviewEntities()
 		}
diff --git a/applications/drone-command/src/views/areaManage/partition/shapeTools/edit/EditBufferCircleTool.js b/applications/drone-command/src/views/areaManage/partition/shapeTools/edit/EditBufferCircleTool.js
index 1808125..61e40b4 100644
--- a/applications/drone-command/src/views/areaManage/partition/shapeTools/edit/EditBufferCircleTool.js
+++ b/applications/drone-command/src/views/areaManage/partition/shapeTools/edit/EditBufferCircleTool.js
@@ -9,6 +9,26 @@
 		lat: point.lat ?? point.latitude,
 	}))
 
+const resolveLngLat = point => {
+	if (!point) return null
+	const lng = point.lng ?? point.longitude
+	const lat = point.lat ?? point.latitude
+	if (Number.isFinite(lng) && Number.isFinite(lat)) {
+		return { lng: Number(lng), lat: Number(lat), height: Number(point.height) || 0 }
+	}
+	return null
+}
+
+const resolveCartesian = point => {
+	if (!point) return null
+	if (Number.isFinite(point.x) && Number.isFinite(point.y) && Number.isFinite(point.z)) {
+		return new Cesium.Cartesian3(point.x, point.y, point.z)
+	}
+	const lngLat = resolveLngLat(point)
+	if (!lngLat) return null
+	return Cesium.Cartesian3.fromDegrees(lngLat.lng, lngLat.lat, lngLat.height || 0)
+}
+
 export class EditBufferCircleTool extends ToolBase {
 	constructor(viewer) {
 		super(viewer)
@@ -34,10 +54,15 @@
 	start(data = []) {
 		const inputPoints = Array.isArray(data?.points) ? data.points : data
 		const normalized = normalizePoints(inputPoints)
-		if (!normalized.length) return
-		const centerLng = normalized.reduce((sum, p) => sum + p.lng, 0) / normalized.length
-		const centerLat = normalized.reduce((sum, p) => sum + p.lat, 0) / normalized.length
-		this.centerCartesian = Cesium.Cartesian3.fromDegrees(centerLng, centerLat)
+		const metaCenter = resolveCartesian(data?.meta?.center)
+		if (metaCenter) {
+			this.centerCartesian = metaCenter
+		} else {
+			if (!normalized.length) return
+			const centerLng = normalized.reduce((sum, p) => sum + p.lng, 0) / normalized.length
+			const centerLat = normalized.reduce((sum, p) => sum + p.lat, 0) / normalized.length
+			this.centerCartesian = Cesium.Cartesian3.fromDegrees(centerLng, centerLat)
+		}
 		const radii = Array.isArray(data?.meta?.bufferRadii) ? data.meta.bufferRadii : null
 		if (radii?.length) {
 			const [r1, r2, r3] = radii
@@ -124,6 +149,12 @@
 			meta: {
 				center: this.centerCartesian,
 				bufferRadii: [this.radiusLevel1, this.radiusLevel2, this.radiusLevel3],
+				controlPoints: [
+					this.centerCartesian,
+					this.getRadiusPoint(this.radiusLevel1),
+					this.getRadiusPoint(this.radiusLevel2),
+					this.getRadiusPoint(this.radiusLevel3),
+				],
 			},
 		})
 	}
@@ -256,6 +287,12 @@
 			meta: {
 				center: this.centerCartesian,
 				bufferRadii: [this.radiusLevel1, this.radiusLevel2, this.radiusLevel3],
+				controlPoints: [
+					this.centerCartesian,
+					this.getRadiusPoint(this.radiusLevel1),
+					this.getRadiusPoint(this.radiusLevel2),
+					this.getRadiusPoint(this.radiusLevel3),
+				],
 			},
 		}
 	}
diff --git a/applications/drone-command/src/views/areaManage/partition/shapeTools/edit/EditEllipseTool.js b/applications/drone-command/src/views/areaManage/partition/shapeTools/edit/EditEllipseTool.js
index c3b2904..228eae3 100644
--- a/applications/drone-command/src/views/areaManage/partition/shapeTools/edit/EditEllipseTool.js
+++ b/applications/drone-command/src/views/areaManage/partition/shapeTools/edit/EditEllipseTool.js
@@ -24,6 +24,36 @@
 		lat: point.lat ?? point.latitude,
 	}))
 
+const resolveLngLat = point => {
+	if (!point) return null
+	const lng = point.lng ?? point.longitude
+	const lat = point.lat ?? point.latitude
+	if (Number.isFinite(lng) && Number.isFinite(lat)) {
+		return { lng: Number(lng), lat: Number(lat), height: Number(point.height) || 0 }
+	}
+	return null
+}
+
+const resolveCartesian = point => {
+	if (!point) return null
+	if (Number.isFinite(point.x) && Number.isFinite(point.y) && Number.isFinite(point.z)) {
+		return new Cesium.Cartesian3(point.x, point.y, point.z)
+	}
+	const lngLat = resolveLngLat(point)
+	if (!lngLat) return null
+	return Cesium.Cartesian3.fromDegrees(lngLat.lng, lngLat.lat, lngLat.height || 0)
+}
+
+const resolveCartographic = point => {
+	if (!point) return null
+	if (Number.isFinite(point.x) && Number.isFinite(point.y) && Number.isFinite(point.z)) {
+		return Cesium.Cartographic.fromCartesian(point)
+	}
+	const lngLat = resolveLngLat(point)
+	if (!lngLat) return null
+	return Cesium.Cartographic.fromDegrees(lngLat.lng, lngLat.lat, lngLat.height || 0)
+}
+
 export class EditEllipseTool extends ToolBase {
 	constructor(viewer, options = {}) {
 		super(viewer)
@@ -40,22 +70,43 @@
 		this.style = resolveStyle(options?.style)
 	}
 
-	start(points = []) {
-		const normalized = normalizePoints(points)
-		const bounds = getBoundsFromLngLats(normalized)
-		const center = getCenterFromBounds(bounds)
-		if (!center) return
-		this.centerCartesian = Cesium.Cartesian3.fromRadians(center.longitude, center.latitude)
-		const eastWest = getMetersBetween(
-			new Cesium.Cartographic(bounds.west, center.latitude),
-			new Cesium.Cartographic(bounds.east, center.latitude)
-		)
-		const northSouth = getMetersBetween(
-			new Cesium.Cartographic(center.longitude, bounds.south),
-			new Cesium.Cartographic(center.longitude, bounds.north)
-		)
-		this.semiMajor = Math.max(1, eastWest / 2)
-		this.semiMinor = Math.max(1, northSouth / 2)
+	start(data = []) {
+		const inputPoints = Array.isArray(data?.points) ? data.points : data
+		const meta = data?.meta || null
+		const metaCenter = resolveCartesian(meta?.center)
+		if (metaCenter) {
+			this.centerCartesian = metaCenter
+			const centerCarto = Cesium.Cartographic.fromCartesian(metaCenter)
+			const majorFromMeta = Number.isFinite(meta?.semiMajor) ? Number(meta.semiMajor) : 0
+			const minorFromMeta = Number.isFinite(meta?.semiMinor) ? Number(meta.semiMinor) : 0
+			const majorPoint = resolveCartographic(meta?.majorPoint)
+			const minorPoint = resolveCartographic(meta?.minorPoint)
+			const majorDistance =
+				majorFromMeta || (majorPoint ? getMetersBetween(centerCarto, majorPoint) : 0)
+			const minorDistance =
+				minorFromMeta || (minorPoint ? getMetersBetween(centerCarto, minorPoint) : 0)
+			if (majorDistance > 0) this.semiMajor = Math.max(1, majorDistance)
+			if (minorDistance > 0) this.semiMinor = Math.max(1, minorDistance)
+			if (this.semiMinor > this.semiMajor) this.semiMinor = this.semiMajor
+		}
+
+		if (!this.centerCartesian || !this.semiMajor || !this.semiMinor) {
+			const normalized = normalizePoints(inputPoints)
+			const bounds = getBoundsFromLngLats(normalized)
+			const center = getCenterFromBounds(bounds)
+			if (!center) return
+			this.centerCartesian = Cesium.Cartesian3.fromRadians(center.longitude, center.latitude)
+			const eastWest = getMetersBetween(
+				new Cesium.Cartographic(bounds.west, center.latitude),
+				new Cesium.Cartographic(bounds.east, center.latitude)
+			)
+			const northSouth = getMetersBetween(
+				new Cesium.Cartographic(center.longitude, bounds.south),
+				new Cesium.Cartographic(center.longitude, bounds.north)
+			)
+			this.semiMajor = Math.max(1, eastWest / 2)
+			this.semiMinor = Math.max(1, northSouth / 2)
+		}
 
 		this.dataSource = new Cesium.CustomDataSource('edit-ellipse')
 		this.viewer.dataSources.add(this.dataSource)
@@ -113,7 +164,16 @@
 		this.dragType = null
 		this.enableMapControl()
 		const positions = buildEllipsePositions(this.centerCartesian, this.semiMajor, this.semiMinor)
-		this.notify('getPolygonPositions', positions)
+		this.notify('getPolygonPositions', {
+			positions,
+			meta: {
+				center: this.centerCartesian,
+				majorPoint: this.getAxisPoint(this.semiMajor, 0),
+				minorPoint: this.getAxisPoint(0, this.semiMinor),
+				semiMajor: this.semiMajor,
+				semiMinor: this.semiMinor,
+			},
+		})
 	}
 
 	createEntities() {
@@ -172,7 +232,16 @@
 
 	getPositions() {
 		if (!this.centerCartesian) return []
-		return buildEllipsePositions(this.centerCartesian, this.semiMajor, this.semiMinor)
+		return {
+			positions: buildEllipsePositions(this.centerCartesian, this.semiMajor, this.semiMinor),
+			meta: {
+				center: this.centerCartesian,
+				majorPoint: this.getAxisPoint(this.semiMajor, 0),
+				minorPoint: this.getAxisPoint(0, this.semiMinor),
+				semiMajor: this.semiMajor,
+				semiMinor: this.semiMinor,
+			},
+		}
 	}
 
 	getAxisPoint(eastMeters, northMeters) {
diff --git a/applications/drone-command/src/views/areaManage/partition/shapeTools/edit/EditPolygonTool.js b/applications/drone-command/src/views/areaManage/partition/shapeTools/edit/EditPolygonTool.js
index c6b2a13..0e5cd6e 100644
--- a/applications/drone-command/src/views/areaManage/partition/shapeTools/edit/EditPolygonTool.js
+++ b/applications/drone-command/src/views/areaManage/partition/shapeTools/edit/EditPolygonTool.js
@@ -74,7 +74,7 @@
 	}
 
 	handleMouseMove(movement) {
-		this.tooltip.show('拖动顶点编辑,右键删除', movement.endPosition)
+		this.tooltip.show('拖动顶点编辑,右键删除点', movement.endPosition)
 		if (!this.isDragging || this.draggedIndex < 0) return
 		const position = this.getPositionFromScreen(movement.endPosition)
 		if (!position) return
diff --git a/applications/drone-command/src/views/areaManage/precinctInfo/FormDiaLog.vue b/applications/drone-command/src/views/areaManage/precinctInfo/FormDiaLog.vue
index 064f7ad..50e15ee 100644
--- a/applications/drone-command/src/views/areaManage/precinctInfo/FormDiaLog.vue
+++ b/applications/drone-command/src/views/areaManage/precinctInfo/FormDiaLog.vue
@@ -120,6 +120,7 @@
 import axios from 'axios'
 import { saveOperationLog } from '@ztzf/apis'
 import { useRoute } from 'vue-router'
+import positionIcon from '@/assets/images/areaManage/positionIcon.png'
 
 const initForm = () => ({
 	address: '', // 位置
@@ -185,7 +186,7 @@
 let redPointEntity
 let leftClickBound = false
 const labelParams = {
-	font: '16px',
+	font: '18px',
 	fillColor: Cesium.Color.WHITE, // 文字颜色:白色
 	backgroundColor: Cesium.Color.BLACK, //背景颜色
 	backgroundPadding: new Cesium.Cartesian2(5, 5), // 水平/垂直内边距(像素)
@@ -200,17 +201,17 @@
 }
 
 function LeftClickEvent(click) {
-	const pos = click.position // Cartesian2 屏幕坐标
-	// 屏幕坐标 -> 椭球面坐标(不考虑地形/模型)
-	const cartesian3 = viewer.camera.pickEllipsoid(pos, viewer.scene.globe.ellipsoid)
-	if (!cartesian3) {
-		return { longitude: 112, latitude: 23 }
+	const pos = click.position
+	// 优先使用 pickPosition 获取贴地坐标(包含地形高度)
+	let cartesian3 = viewer.scene.pickPosition(pos)
+	// 如果 pickPosition 失败,回退到 pickEllipsoid
+	if (!cartesian3 || !Cesium.defined(cartesian3)) {
+		cartesian3 = viewer.camera.pickEllipsoid(pos, viewer.scene.globe.ellipsoid)
 	}
-	// 椭球面坐标 -> 经纬度
+	if (!cartesian3) return
 	const carto = Cesium.Cartographic.fromCartesian(cartesian3)
 	const longitude = Cesium.Math.toDegrees(carto.longitude)
 	const latitude = Cesium.Math.toDegrees(carto.latitude)
-	// 直接更新 formData
 	formData.value.longitude = _.round(longitude, 6)
 	formData.value.latitude = _.round(latitude, 6)
 	getLocationName(longitude, latitude)
@@ -219,14 +220,16 @@
 	if (!redPointEntity) {
 		redPointEntity = viewer.entities.add({
 			position: Cesium.Cartesian3.fromDegrees(longitude, latitude),
-			point: {
-				color: Cesium.Color.RED,
-				pixelSize: 10,
+			billboard: {
+				image: positionIcon,
+				width: 20,
+				height: 20,
+				verticalOrigin: Cesium.VerticalOrigin.CENTER,
 				heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
 			},
 			label: {
 				text: `${longitude.toFixed(6)}, ${latitude.toFixed(6)}`,
-				...labelParams
+				...labelParams,
 			},
 		})
 	} else {
@@ -240,14 +243,16 @@
 	if (!redPointEntity) {
 		redPointEntity = viewer.entities.add({
 			position: Cesium.Cartesian3.fromDegrees(longitude, latitude),
-			point: {
-				color: Cesium.Color.RED,
-				pixelSize: 10,
+			billboard: {
+				image: positionIcon,
+				width: 20,
+				height: 20,
+				verticalOrigin: Cesium.VerticalOrigin.CENTER,
 				heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
 			},
 			label: {
 				text: `${longitude.toFixed(6)}, ${latitude.toFixed(6)}`,
-				...labelParams
+				...labelParams,
 			},
 		})
 	} else {
diff --git a/applications/drone-command/src/views/areaManage/sceneConfig/FormDiaLog.vue b/applications/drone-command/src/views/areaManage/sceneConfig/FormDiaLog.vue
index e546c27..0b15bc6 100644
--- a/applications/drone-command/src/views/areaManage/sceneConfig/FormDiaLog.vue
+++ b/applications/drone-command/src/views/areaManage/sceneConfig/FormDiaLog.vue
@@ -15,7 +15,7 @@
 			<div class="right-container">
 				<div class="header">
 					<span>{{ titleEnum[dialogMode] }}</span>
-				
+
 					<el-icon class="close-btn" @click.stop="visible = false"><Close /></el-icon>
 				</div>
 
@@ -27,28 +27,12 @@
 							<div class="val">{{ formData.sceneName }}</div>
 						</el-col>
 						<el-col :span="24">
-							<div class="label">指挥点位置</div>
-							<div class="val">{{ formatLocation(formData) }}</div>
-						</el-col>
-						<el-col :span="24">
 							<div class="label">场景类型</div>
 							<div class="val">{{ getDictLabel(formData.sceneType, dictObj.CommandSceneType) }}</div>
 						</el-col>
 						<el-col :span="24">
-							<div class="label">负责人</div>
-							<div class="val">{{ formData.defenseLeader }}</div>
-						</el-col>
-						<el-col :span="24">
-							<div class="label">负责人电话</div>
-							<div class="val">{{ formData.leaderPhone }}</div>
-						</el-col>
-						<el-col :span="24">
 							<div class="label">设备模式</div>
 							<div class="val">{{ getDictLabel(formData.deviceMode, dictObj.deviceMode) }}</div>
-						</el-col>
-						<el-col :span="24">
-							<div class="label">防控面积</div>
-							<div class="val">{{ formatDefenseArea(formData.defenseArea) }}</div>
 						</el-col>
 					</el-row>
 
@@ -67,55 +51,72 @@
 					</div>
 				</div>
 
-				<el-form class="dialog-form" v-else ref="formRef" :model="formData" :rules="rules" :disabled="readonly"
-					label-width="96px">
+				<el-form
+					class="dialog-form"
+					v-else
+					ref="formRef"
+					:model="formData"
+					:rules="rules"
+					:disabled="readonly"
+					label-width="96px"
+				>
 					<el-form-item label="场景名称" prop="sceneName">
-						<el-input class="command-input" v-model="formData.sceneName" maxlength="50"
-							placeholder="请输入" clearable />
-					</el-form-item>
-					<el-form-item label="指挥点位置" prop="longitude">
-						<div class="val">{{ formatLocation(formData) }}</div>
+						<el-input
+							class="command-input"
+							v-model="formData.sceneName"
+							maxlength="50"
+							placeholder="请输入"
+							clearable
+						/>
 					</el-form-item>
 					<el-form-item label="场景类型" prop="sceneType">
-						<el-select class="command-select" popper-class="command-select-popper"
-							v-model="formData.sceneType" placeholder="请选择" clearable>
-							<el-option v-for="item in dictObj.CommandSceneType" :key="item.dictKey" :label="item.dictValue"
-								:value="item.dictKey" />
+						<el-select
+							class="command-select"
+							popper-class="command-select-popper"
+							v-model="formData.sceneType"
+							placeholder="请选择"
+							clearable
+						>
+							<el-option
+								v-for="item in dictObj.CommandSceneType"
+								:key="item.dictKey"
+								:label="item.dictValue"
+								:value="item.dictKey"
+							/>
 						</el-select>
-					</el-form-item>
-					<el-form-item label="负责人" prop="defenseLeader">
-						<el-input class="command-input" v-model="formData.defenseLeader" maxlength="50"
-							placeholder="请输入" clearable />
-					</el-form-item>
-					<el-form-item label="负责人电话" prop="leaderPhone">
-						<el-input class="command-input" v-model="formData.leaderPhone" maxlength="50"
-							placeholder="请输入" clearable />
 					</el-form-item>
 					<el-form-item label="设备模式" prop="deviceMode">
-						<el-select class="command-select" popper-class="command-select-popper"
-							v-model="formData.deviceMode" placeholder="请选择" clearable>
-							<el-option v-for="item in dictObj.deviceMode" :key="item.dictKey" :label="item.dictValue"
-								:value="item.dictKey" />
+						<el-select
+							class="command-select"
+							popper-class="command-select-popper"
+							v-model="formData.deviceMode"
+							placeholder="请选择"
+							clearable
+						>
+							<el-option
+								v-for="item in dictObj.deviceMode"
+								:key="item.dictKey"
+								:label="item.dictValue"
+								:value="item.dictKey"
+							/>
 						</el-select>
-					</el-form-item>
-					<el-form-item label="防控面积" prop="defenseArea">
-						<div class="val">{{ formatDefenseArea(formData.defenseArea) }}</div>
 					</el-form-item>
 
 					<div class="search-table-container">
 						<div class="search-box">
-							<div class="label">
-								关联区域
-							</div>
+							<div class="label">关联区域</div>
 
-							<el-input class="command-input" v-model="searchName" placeholder="请输入"
-								clearable></el-input>
+							<el-input class="command-input" v-model="searchName" placeholder="请输入" clearable></el-input>
 						</div>
 
 						<el-form-item prop="areaDivideIds" label-width="0">
-							<el-table class="command-table" ref="areaTableRef"
-								:data="areaList.filter(item => item.areaName.includes(searchName))" row-key="id"
-								@selection-change="handleAreaSelectionChange">
+							<el-table
+								class="command-table"
+								ref="areaTableRef"
+								:data="areaList.filter(item => item.areaName.includes(searchName))"
+								row-key="id"
+								@selection-change="handleAreaSelectionChange"
+							>
 								<el-table-column type="selection" width="55" :reserve-selection="true" />
 								<el-table-column prop="areaName" show-overflow-tooltip label="区域名称" />
 								<el-table-column prop="areaType" show-overflow-tooltip label="区域类型">
@@ -129,9 +130,17 @@
 				</el-form>
 
 				<div class="footer">
-					<el-button v-if="dialogMode != 'view'" color="#2B2B4C" @click="handleCancel">{{ readonly ? '关闭' : '取消' }}</el-button>
-					<el-button color="#284FE3" v-if="!readonly" type="primary" :loading="submitting"
-						:disabled="submitting" @click="handleSubmit">
+					<el-button v-if="dialogMode != 'view'" color="#2B2B4C" @click="handleCancel">
+						{{ readonly ? '关闭' : '取消' }}
+					</el-button>
+					<el-button
+						color="#284FE3"
+						v-if="!readonly"
+						type="primary"
+						:loading="submitting"
+						:disabled="submitting"
+						@click="handleSubmit"
+					>
 						确定
 					</el-button>
 				</div>
@@ -158,14 +167,9 @@
 const initForm = () => ({
 	sceneName: '', // 场景名称
 	sceneType: '', // 场景类型
-	defenseLeader: '', // 负责人
-	leaderPhone: '', // 负责人电话
 	deviceMode: '', // 设备模式
 	areaDivideIds: '', // 关联区域ID
 	areaCount: 0, // 区域数量
-	defenseArea: 0, // 防控面积
-	latitude: null, // 指挥点纬度
-	longitude: null, // 指挥点经度
 })
 
 const emit = defineEmits(['success'])
@@ -190,20 +194,17 @@
 const rules = {
 	sceneName: fieldRules(true, 50),
 	sceneType: fieldRules(true),
-	defenseLeader: fieldRules(true, 50),
-	leaderPhone: fieldRules(true, 50),
 	deviceMode: fieldRules(true),
-	longitude: fieldRules(true),
 	areaDivideIds: fieldRules(false),
 }
 
 // 关闭弹框
-function handleCancel () {
+function handleCancel() {
 	visible.value = false
 }
 
 // 提交新增/编辑
-async function handleSubmit () {
+async function handleSubmit() {
 	const isValid = await formRef.value?.validate().catch(() => false)
 	if (!isValid) return
 	submitting.value = true
@@ -211,7 +212,6 @@
 		const ids = selectedAreaRows.value.map(item => item.id)
 		formData.value.areaDivideIds = ids.join(',')
 		formData.value.areaCount = ids.length
-		formData.value.defenseArea = calcDefenseArea(selectedAreaRows.value)
 		await fwDefenseSceneSubmitApi(formData.value)
 		saveOperationLog({
 			requestUri: route.path,
@@ -227,33 +227,30 @@
 }
 
 // 加载详情
-async function loadDetail () {
+async function loadDetail() {
 	if (!formData.value.id) return
 	const res = await fwDefenseSceneDetailApi({ id: formData.value.id })
 	formData.value = res?.data?.data ?? initForm()
 }
 
 // 获取区域列表
-async function getAreaList () {
+async function getAreaList() {
 	if (areaList.value.length) return
 	const res = await fwAreaDivideListApi({ filterSelected: 1, sceneId: formData.value.id })
 	areaList.value = res?.data?.data ?? []
 }
 
 // 关联区域变更
-async function handleAreaSelectionChange (rows) {
+async function handleAreaSelectionChange(rows) {
 	const selectedRows = await ensureAreaGeom(rows)
 	selectedAreaRows.value = selectedRows
 	const ids = selectedRows.map(item => item.id)
 	formData.value.areaDivideIds = ids.join(',')
 	formData.value.areaCount = ids.length
-	formData.value.defenseArea = calcDefenseArea(selectedRows)
 }
 
 // 渲染面
-function renderingSurface () {
-	console.log(geometricSource, 11111111)
-
+function renderingSurface() {
 	if (!geometricSource) {
 		initMap()
 	}
@@ -279,12 +276,7 @@
 
 watch(() => selectedAreaRows.value, renderingSurface)
 
-function calcDefenseArea (rows) {
-	const total = rows.reduce((sum, item) => sum + (Number(item.areaSize) || 0), 0)
-	return _.round(total, 2)
-}
-
-async function ensureAreaGeom (rows) {
+async function ensureAreaGeom(rows) {
 	const missingRows = rows.filter(row => row?.id && !row.geom)
 	if (!missingRows.length) return rows
 	const detailList = await Promise.all(
@@ -294,9 +286,7 @@
 				.catch(() => null)
 		)
 	)
-	const detailMap = new Map(
-		detailList.filter(Boolean).map(item => [String(item.id), item])
-	)
+	const detailMap = new Map(detailList.filter(Boolean).map(item => [String(item.id), item]))
 	if (!detailMap.size) return rows
 	areaList.value = areaList.value.map(item => {
 		const detail = detailMap.get(String(item.id))
@@ -309,7 +299,7 @@
 }
 
 // 同步关联区域
-function syncAreaSelection () {
+function syncAreaSelection() {
 	if (!areaTableRef.value) return
 	areaTableRef.value.clearSelection()
 	const rows = []
@@ -317,8 +307,8 @@
 	const arr = Array.isArray(areaDivideIds)
 		? areaDivideIds.map(item => String(item))
 		: String(areaDivideIds || '')
-			.split(',')
-			.filter(Boolean)
+				.split(',')
+				.filter(Boolean)
 	areaList.value.forEach(row => {
 		if (arr.includes(String(row.id))) {
 			areaTableRef.value.toggleRowSelection(row, true)
@@ -328,84 +318,23 @@
 	selectedAreaRows.value = rows
 	if (!rows.length) return
 	formData.value.areaCount = rows.length
-	formData.value.defenseArea = calcDefenseArea(rows)
-}
-
-function formatDefenseArea (value) {
-	if (value == null || value === '') return ''
-	return `${value}k㎡`
-}
-
-// 格式化指挥点位置
-function formatLocation (row) {
-	if (row?.longitude == null || row?.latitude == null) return ''
-	return `${_.round(row.longitude, 6)}, ${_.round(row.latitude, 6)}`
 }
 
 let geometricSource
 // 初始化地图实例
-function initMap () {
+function initMap() {
 	if (viewer) return
 	const map = mapRef.value?.getMap()
 	if (!map?.viewer) return
 	viewer = map.viewer
-	if (!readonly.value && !leftClickBound) {
-		map.publicCesium?.addLeftClickEvent?.(null, LeftClickEvent)
-		leftClickBound = true
-	}
 	if (!geometricSource) {
 		geometricSource = new Cesium.CustomDataSource('geometricSource')
 		viewer.dataSources.add(geometricSource)
 	}
 }
 
-function LeftClickEvent (click) {
-	const pos = click.position
-	const cartesian3 = viewer.camera.pickEllipsoid(pos, viewer.scene.globe.ellipsoid)
-	if (!cartesian3) return
-	const carto = Cesium.Cartographic.fromCartesian(cartesian3)
-	const longitude = Cesium.Math.toDegrees(carto.longitude)
-	const latitude = Cesium.Math.toDegrees(carto.latitude)
-	formData.value.longitude = _.round(longitude, 6)
-	formData.value.latitude = _.round(latitude, 6)
-	setMapPoint(formData.value.longitude, formData.value.latitude)
-}
-
-function setMapPoint (longitude, latitude) {
-	if (!viewer || longitude == null || latitude == null) return
-	if (!redPointEntity) {
-		redPointEntity = viewer.entities.add({
-			position: Cesium.Cartesian3.fromDegrees(longitude, latitude),
-			billboard: {
-				image: commandPost, // 这里替换为你的图片路径或变量 deviceZcImg
-				width: 40,
-				height: 56,
-				verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 设置图片底部对齐坐标点
-			},
-			label: {
-				text: `${longitude.toFixed(6)}, ${latitude.toFixed(6)}`,
-				font: '14px',
-				fillColor: Cesium.Color.WHITE,
-				backgroundColor: Cesium.Color.BLACK.withAlpha(0.6),
-				backgroundPadding: new Cesium.Cartesian2(5, 5),
-				showBackground: true,
-				style: Cesium.LabelStyle.FILL_AND_OUTLINE,
-				horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
-				verticalOrigin: Cesium.VerticalOrigin.CENTER,
-				outlineWidth: 1,
-				outlineColor: Cesium.Color.WHITE,
-				pixelOffset: new Cesium.Cartesian2(0, -72),
-				eyeOffset: new Cesium.Cartesian3(0, 0, -20),
-			},
-		})
-	} else {
-		redPointEntity.position = Cesium.Cartesian3.fromDegrees(longitude, latitude)
-		redPointEntity.label.text = `${longitude.toFixed(6)}, ${latitude.toFixed(6)}`
-	}
-}
-
 // 打开弹框
-async function open ({ mode, row } = {}) {
+async function open({ mode, row } = {}) {
 	dialogMode.value = mode || 'add'
 	formData.value = dialogMode.value === 'add' ? initForm() : row
 	selectedAreaRows.value = []
@@ -418,13 +347,7 @@
 	}
 	await nextTick()
 	syncAreaSelection()
-	setMapPoint(formData.value.longitude, formData.value.latitude)
 }
-
-onMounted(() => {
-	getAreaList()
-})
-
 defineExpose({
 	open,
 })
diff --git a/applications/drone-command/src/views/areaManage/sceneConfig/index.vue b/applications/drone-command/src/views/areaManage/sceneConfig/index.vue
index 817b481..1d744f4 100644
--- a/applications/drone-command/src/views/areaManage/sceneConfig/index.vue
+++ b/applications/drone-command/src/views/areaManage/sceneConfig/index.vue
@@ -52,13 +52,6 @@
 							{{ getDictLabel(row.sceneType, dictObj.CommandSceneType) }}
 						</template>
 					</el-table-column>
-					<el-table-column show-overflow-tooltip label="指挥点位置">
-						<template v-slot="{ row }">
-							{{ formatLocation(row) }}
-						</template>
-					</el-table-column>
-					<el-table-column prop="defenseLeader" show-overflow-tooltip label="防控负责人" />
-					<el-table-column prop="leaderPhone" show-overflow-tooltip label="防控负责人电话" />
 					<el-table-column prop="areaCount" show-overflow-tooltip label="区域数量" />
 					<el-table-column prop="counterDeviceCount" show-overflow-tooltip label="反制设备数量" />
 					<el-table-column prop="detectDeviceCount" show-overflow-tooltip label="探测设备数量" />
diff --git a/applications/drone-command/src/views/areaManage/sceneManage/FormDiaLog.vue b/applications/drone-command/src/views/areaManage/sceneManage/FormDiaLog.vue
new file mode 100644
index 0000000..0b15bc6
--- /dev/null
+++ b/applications/drone-command/src/views/areaManage/sceneManage/FormDiaLog.vue
@@ -0,0 +1,356 @@
+<template>
+	<el-dialog class="command-page-map-view-dialog" v-model="visible" :show-close="false" :close-on-click-modal="false">
+		<div class="dialog-container">
+			<div class="left-container">
+				<CommonCesiumMap
+					ref="mapRef"
+					class="leftMap command-cesium"
+					:active="visible"
+					:flat-mode="false"
+					:terrain="true"
+					:layer-mode="4"
+					:boundary="false"
+				/>
+			</div>
+			<div class="right-container">
+				<div class="header">
+					<span>{{ titleEnum[dialogMode] }}</span>
+
+					<el-icon class="close-btn" @click.stop="visible = false"><Close /></el-icon>
+				</div>
+
+				<div class="content" v-if="readonly">
+					<div class="detail-title">场景详情</div>
+					<el-row>
+						<el-col :span="24">
+							<div class="label">场景名称</div>
+							<div class="val">{{ formData.sceneName }}</div>
+						</el-col>
+						<el-col :span="24">
+							<div class="label">场景类型</div>
+							<div class="val">{{ getDictLabel(formData.sceneType, dictObj.CommandSceneType) }}</div>
+						</el-col>
+						<el-col :span="24">
+							<div class="label">设备模式</div>
+							<div class="val">{{ getDictLabel(formData.deviceMode, dictObj.deviceMode) }}</div>
+						</el-col>
+					</el-row>
+
+					<div class="detail-title">关联区域</div>
+					<div class="command-table-container">
+						<div class="command-table-content">
+							<el-table class="command-table" ref="areaTableRef" :data="areaList" row-key="id">
+								<el-table-column prop="areaName" label="区域名称" />
+								<el-table-column prop="areaType" label="区域类型">
+									<template v-slot="{ row }">
+										{{ getDictLabel(row.areaType, dictObj.areaType) }}
+									</template>
+								</el-table-column>
+							</el-table>
+						</div>
+					</div>
+				</div>
+
+				<el-form
+					class="dialog-form"
+					v-else
+					ref="formRef"
+					:model="formData"
+					:rules="rules"
+					:disabled="readonly"
+					label-width="96px"
+				>
+					<el-form-item label="场景名称" prop="sceneName">
+						<el-input
+							class="command-input"
+							v-model="formData.sceneName"
+							maxlength="50"
+							placeholder="请输入"
+							clearable
+						/>
+					</el-form-item>
+					<el-form-item label="场景类型" prop="sceneType">
+						<el-select
+							class="command-select"
+							popper-class="command-select-popper"
+							v-model="formData.sceneType"
+							placeholder="请选择"
+							clearable
+						>
+							<el-option
+								v-for="item in dictObj.CommandSceneType"
+								:key="item.dictKey"
+								:label="item.dictValue"
+								:value="item.dictKey"
+							/>
+						</el-select>
+					</el-form-item>
+					<el-form-item label="设备模式" prop="deviceMode">
+						<el-select
+							class="command-select"
+							popper-class="command-select-popper"
+							v-model="formData.deviceMode"
+							placeholder="请选择"
+							clearable
+						>
+							<el-option
+								v-for="item in dictObj.deviceMode"
+								:key="item.dictKey"
+								:label="item.dictValue"
+								:value="item.dictKey"
+							/>
+						</el-select>
+					</el-form-item>
+
+					<div class="search-table-container">
+						<div class="search-box">
+							<div class="label">关联区域</div>
+
+							<el-input class="command-input" v-model="searchName" placeholder="请输入" clearable></el-input>
+						</div>
+
+						<el-form-item prop="areaDivideIds" label-width="0">
+							<el-table
+								class="command-table"
+								ref="areaTableRef"
+								:data="areaList.filter(item => item.areaName.includes(searchName))"
+								row-key="id"
+								@selection-change="handleAreaSelectionChange"
+							>
+								<el-table-column type="selection" width="55" :reserve-selection="true" />
+								<el-table-column prop="areaName" show-overflow-tooltip label="区域名称" />
+								<el-table-column prop="areaType" show-overflow-tooltip label="区域类型">
+									<template v-slot="{ row }">
+										{{ getDictLabel(row.areaType, dictObj.areaType) }}
+									</template>
+								</el-table-column>
+							</el-table>
+						</el-form-item>
+					</div>
+				</el-form>
+
+				<div class="footer">
+					<el-button v-if="dialogMode != 'view'" color="#2B2B4C" @click="handleCancel">
+						{{ readonly ? '关闭' : '取消' }}
+					</el-button>
+					<el-button
+						color="#284FE3"
+						v-if="!readonly"
+						type="primary"
+						:loading="submitting"
+						:disabled="submitting"
+						@click="handleSubmit"
+					>
+						确定
+					</el-button>
+				</div>
+			</div>
+		</div>
+	</el-dialog>
+</template>
+
+<script setup>
+import { Close } from '@element-plus/icons-vue'
+
+import { computed, inject, nextTick, onMounted, ref, watch } from 'vue'
+import { ElMessage } from 'element-plus'
+import { fwDefenseSceneDetailApi, fwDefenseSceneSubmitApi } from './sceneConfigApi'
+import { fieldRules, geomAnalysis, getDictLabel } from '@ztzf/utils'
+import CommonCesiumMap from '@/components/map-container/common-cesium-map.vue'
+import * as Cesium from 'cesium'
+import { fwAreaDivideDetailApi, fwAreaDivideListApi } from '../partition/partitionApi'
+import { DrawPolygon } from '@/utils/cesium/DrawPolygon'
+import commandPost from '@/assets/images/dataCockpit/legend/command-post.png'
+import { saveOperationLog } from '@ztzf/apis'
+import { useRoute } from 'vue-router'
+
+const initForm = () => ({
+	sceneName: '', // 场景名称
+	sceneType: '', // 场景类型
+	deviceMode: '', // 设备模式
+	areaDivideIds: '', // 关联区域ID
+	areaCount: 0, // 区域数量
+})
+
+const emit = defineEmits(['success'])
+const formRef = ref(null) // 表单实例
+const formData = ref(initForm()) // 表单数据
+const visible = defineModel() // 弹框显隐
+const dialogMode = ref('add') // 弹框模式
+const submitting = ref(false) // 提交中
+const readonly = computed(() => dialogMode.value === 'view')
+const titleEnum = ref({ edit: '编辑', view: '查看', add: '新增' })
+const areaTableRef = ref(null)
+const searchName = ref('')
+const areaList = ref([]) // 关联区域列表
+const selectedAreaRows = ref([]) //选中列表
+const dictObj = inject('dictObj')
+const mapRef = ref(null)
+const route = useRoute()
+let viewer
+let redPointEntity
+let leftClickBound = false
+
+const rules = {
+	sceneName: fieldRules(true, 50),
+	sceneType: fieldRules(true),
+	deviceMode: fieldRules(true),
+	areaDivideIds: fieldRules(false),
+}
+
+// 关闭弹框
+function handleCancel() {
+	visible.value = false
+}
+
+// 提交新增/编辑
+async function handleSubmit() {
+	const isValid = await formRef.value?.validate().catch(() => false)
+	if (!isValid) return
+	submitting.value = true
+	try {
+		const ids = selectedAreaRows.value.map(item => item.id)
+		formData.value.areaDivideIds = ids.join(',')
+		formData.value.areaCount = ids.length
+		await fwDefenseSceneSubmitApi(formData.value)
+		saveOperationLog({
+			requestUri: route.path,
+			title: `${route.name || '场景配置管理'}-${dialogMode.value === 'add' ? '新增' : '编辑'}`,
+			type: 1,
+		})
+		ElMessage.success(dialogMode.value === 'add' ? '新增成功' : '更新成功')
+		visible.value = false
+		emit('success')
+	} finally {
+		submitting.value = false
+	}
+}
+
+// 加载详情
+async function loadDetail() {
+	if (!formData.value.id) return
+	const res = await fwDefenseSceneDetailApi({ id: formData.value.id })
+	formData.value = res?.data?.data ?? initForm()
+}
+
+// 获取区域列表
+async function getAreaList() {
+	if (areaList.value.length) return
+	const res = await fwAreaDivideListApi({ filterSelected: 1, sceneId: formData.value.id })
+	areaList.value = res?.data?.data ?? []
+}
+
+// 关联区域变更
+async function handleAreaSelectionChange(rows) {
+	const selectedRows = await ensureAreaGeom(rows)
+	selectedAreaRows.value = selectedRows
+	const ids = selectedRows.map(item => item.id)
+	formData.value.areaDivideIds = ids.join(',')
+	formData.value.areaCount = ids.length
+}
+
+// 渲染面
+function renderingSurface() {
+	if (!geometricSource) {
+		initMap()
+	}
+	if (!geometricSource) return
+	geometricSource && geometricSource.entities.removeAll()
+	selectedAreaRows.value.forEach(item => {
+		if (item.geom) {
+			const pointList = geomAnalysis(item.geom)
+			const result = pointList.map(item => [item.longitude, item.latitude]).flat()
+			geometricSource.entities?.add({
+				customType: 'control_group',
+				position: Cesium.Cartesian3.fromDegrees(result[0], result[1]),
+				polyline: {
+					positions: Cesium.Cartesian3.fromDegreesArray(result),
+					clampToGround: true,
+					width: 3,
+					material: Cesium.Color.RED,
+				},
+			})
+		}
+	})
+}
+
+watch(() => selectedAreaRows.value, renderingSurface)
+
+async function ensureAreaGeom(rows) {
+	const missingRows = rows.filter(row => row?.id && !row.geom)
+	if (!missingRows.length) return rows
+	const detailList = await Promise.all(
+		missingRows.map(row =>
+			fwAreaDivideDetailApi({ id: row.id })
+				.then(res => res?.data?.data)
+				.catch(() => null)
+		)
+	)
+	const detailMap = new Map(detailList.filter(Boolean).map(item => [String(item.id), item]))
+	if (!detailMap.size) return rows
+	areaList.value = areaList.value.map(item => {
+		const detail = detailMap.get(String(item.id))
+		return detail ? { ...item, ...detail } : item
+	})
+	return rows.map(item => {
+		const detail = detailMap.get(String(item.id))
+		return detail ? { ...item, ...detail } : item
+	})
+}
+
+// 同步关联区域
+function syncAreaSelection() {
+	if (!areaTableRef.value) return
+	areaTableRef.value.clearSelection()
+	const rows = []
+	const areaDivideIds = formData.value.areaDivideIds
+	const arr = Array.isArray(areaDivideIds)
+		? areaDivideIds.map(item => String(item))
+		: String(areaDivideIds || '')
+				.split(',')
+				.filter(Boolean)
+	areaList.value.forEach(row => {
+		if (arr.includes(String(row.id))) {
+			areaTableRef.value.toggleRowSelection(row, true)
+			rows.push(row)
+		}
+	})
+	selectedAreaRows.value = rows
+	if (!rows.length) return
+	formData.value.areaCount = rows.length
+}
+
+let geometricSource
+// 初始化地图实例
+function initMap() {
+	if (viewer) return
+	const map = mapRef.value?.getMap()
+	if (!map?.viewer) return
+	viewer = map.viewer
+	if (!geometricSource) {
+		geometricSource = new Cesium.CustomDataSource('geometricSource')
+		viewer.dataSources.add(geometricSource)
+	}
+}
+
+// 打开弹框
+async function open({ mode, row } = {}) {
+	dialogMode.value = mode || 'add'
+	formData.value = dialogMode.value === 'add' ? initForm() : row
+	selectedAreaRows.value = []
+	redPointEntity = null
+	await nextTick()
+	initMap()
+	await getAreaList()
+	if (dialogMode.value !== 'add') {
+		await loadDetail()
+	}
+	await nextTick()
+	syncAreaSelection()
+}
+defineExpose({
+	open,
+})
+</script>
+
+<style scoped lang="scss"></style>
diff --git a/applications/drone-command/src/views/areaManage/sceneManage/index.vue b/applications/drone-command/src/views/areaManage/sceneManage/index.vue
new file mode 100644
index 0000000..1d744f4
--- /dev/null
+++ b/applications/drone-command/src/views/areaManage/sceneManage/index.vue
@@ -0,0 +1,195 @@
+<template>
+	<basic-container>
+		<el-form ref="queryParamsRef" :model="searchParams" class="command-page-history-search">
+			<el-form-item label="场景名称" prop="sceneName">
+				<el-input
+					class="command-input"
+					v-model="searchParams.sceneName"
+					placeholder="请输入"
+					clearable
+					@clear="handleSearch"
+				/>
+			</el-form-item>
+
+			<el-form-item label="场景类型" prop="sceneType">
+				<el-select
+					class="command-select"
+					popper-class="command-select-popper"
+					v-model="searchParams.sceneType"
+					placeholder="请选择"
+					clearable
+					@change="handleSearch"
+				>
+					<el-option
+						v-for="item in dictObj.CommandSceneType"
+						:key="item.dictKey"
+						:label="item.dictValue"
+						:value="item.dictKey"
+					/>
+				</el-select>
+			</el-form-item>
+
+			<el-form-item class="history-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="command-table-toolbar">
+			<el-button :icon="Plus" color="#284FE3" type="primary" @click="openForm('add')">新增</el-button>
+			<el-button :icon="Delete" color="#1A2652" :disabled="!selectedIds.length" @click="handleDelete()">删除</el-button>
+		</div>
+
+		<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="sceneName" show-overflow-tooltip label="场景名称" />
+					<el-table-column prop="sceneType" show-overflow-tooltip label="场景类型">
+						<template v-slot="{ row }">
+							{{ getDictLabel(row.sceneType, dictObj.CommandSceneType) }}
+						</template>
+					</el-table-column>
+					<el-table-column prop="areaCount" show-overflow-tooltip label="区域数量" />
+					<el-table-column prop="counterDeviceCount" show-overflow-tooltip label="反制设备数量" />
+					<el-table-column prop="detectDeviceCount" show-overflow-tooltip label="探测设备数量" />
+					<el-table-column label="操作" class-name="operation-btns">
+						<template v-slot="{ row }">
+							<el-link @click="openForm('view', row)">查看</el-link>
+							<el-link @click="openForm('edit', row)">编辑</el-link>
+							<el-link @click="handleDelete(row)">删除</el-link>
+						</template>
+					</el-table-column>
+				</el-table>
+			</div>
+
+			<div class="command-table-pagination">
+				<el-pagination
+					popper-class="command-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>
+
+		<FormDiaLog v-if="dialogVisible" v-model="dialogVisible" ref="dialogRef" @success="getList" />
+	</basic-container>
+</template>
+
+<script setup>
+import { Search, RefreshRight, Plus, Delete } from '@element-plus/icons-vue'
+import { nextTick, onMounted, provide, ref } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { fwDefenseScenePageApi, fwDefenseSceneRemoveApi } from './sceneConfigApi'
+import FormDiaLog from './FormDiaLog.vue'
+import { getDictionaryByCode } from '@/api/system/dictbiz'
+import { getDictLabel } from '@ztzf/utils'
+import { saveOperationLog } from '@ztzf/apis'
+import { useRoute } from 'vue-router'
+
+const initSearchParams = () => ({
+	sceneName: '', // 场景名称
+	sceneType: '', // 场景类型
+	current: 1, // 当前页
+	size: 10, // 每页大小
+})
+
+const searchParams = ref(initSearchParams()) // 查询参数
+const total = ref(0) // 总条数
+const loading = ref(true) // 列表加载中
+const list = ref([]) // 列表数据
+const selectedIds = ref([]) // 勾选的ID列表
+const queryParamsRef = ref(null) // 查询表单实例
+const dialogRef = ref(null) // 弹框实例
+const dialogVisible = ref(false)
+const route = useRoute()
+
+// 获取列表
+async function getList() {
+	loading.value = true
+	try {
+		const res = await fwDefenseScenePageApi(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()
+}
+
+// 删除
+async function handleDelete(row) {
+	const tips = row ? '该条' : '选中的项'
+	await ElMessageBox.confirm(`确认删除${tips}吗?`, '提示', {
+		type: 'warning',
+		customClass: 'command-page-view-message-box',
+		confirmButtonClass: 'command-message-box-confirm',
+		cancelButtonClass: 'command-message-box-cancel',
+	})
+	const ids = row ? row.id : selectedIds.value.join(',')
+	await fwDefenseSceneRemoveApi({ ids })
+	saveOperationLog({
+		requestUri: route.path,
+		title: `${route.name || '场景配置管理'}-删除`,
+		type: 1,
+	})
+	ElMessage.success('删除成功')
+	selectedIds.value = []
+	getList()
+}
+
+// 勾选值设置
+function handleSelectionChange(rows) {
+	selectedIds.value = rows.map(item => item.id)
+}
+
+function formatLocation(row) {
+	if (row?.longitude == null || row?.latitude == null) return ''
+	return `${_.round(row.longitude, 6)}, ${_.round(row.latitude, 6)}`
+}
+
+const dictObj = ref({
+	sceneType: [], // 场景类型
+	deviceMode: [], // 设备模式
+	areaType: [], // 区域类型
+})
+provide('dictObj', dictObj)
+
+// 获取字典
+function getDictList() {
+	getDictionaryByCode('CommandSceneType,deviceMode,areaType').then(res => {
+		dictObj.value = res.data.data
+	})
+}
+
+// 新增/编辑/查看 弹框
+function openForm(mode, row) {
+	dialogVisible.value = true
+	nextTick(() => {
+		dialogRef.value?.open({ mode, row })
+	})
+}
+
+onMounted(() => {
+	getList()
+	getDictList()
+})
+</script>
+<style scoped lang="scss"></style>
diff --git a/applications/drone-command/src/views/areaManage/sceneManage/sceneConfigApi.js b/applications/drone-command/src/views/areaManage/sceneManage/sceneConfigApi.js
new file mode 100644
index 0000000..fbd0373
--- /dev/null
+++ b/applications/drone-command/src/views/areaManage/sceneManage/sceneConfigApi.js
@@ -0,0 +1,46 @@
+import request from '@/axios'
+
+// 查page
+export const fwDefenseScenePageApi = params => {
+	return request({
+		url: `/drone-fw/area/fwDefenseScene/page`,
+		method: 'get',
+		params: { descs: 'update_time', ...params },
+	})
+}
+
+// 查list
+export const fwDefenseSceneListApi = params => {
+	return request({
+		url: `/drone-fw/area/fwDefenseScene/list`,
+		method: 'get',
+		params,
+	})
+}
+
+// 增加或更新
+export const fwDefenseSceneSubmitApi = data => {
+	return request({
+		url: `/drone-fw/area/fwDefenseScene/submit`,
+		method: 'post',
+		data,
+	})
+}
+
+//删除
+export const fwDefenseSceneRemoveApi = params => {
+	return request({
+		url: `/drone-fw/area/fwDefenseScene/remove`,
+		method: 'post',
+		params,
+	})
+}
+
+//详情
+export const fwDefenseSceneDetailApi = params => {
+	return request({
+		url: `/drone-fw/area/fwDefenseScene/detail`,
+		method: 'get',
+		params,
+	})
+}
diff --git a/applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/ViewDiaLog.vue b/applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/ViewDiaLog.vue
index 2c62033..73953d4 100644
--- a/applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/ViewDiaLog.vue
+++ b/applications/task-work-order/src/views/orderView/orderManage/inspectionRequest/ViewDiaLog.vue
@@ -429,7 +429,14 @@
 	} finally {
 	}
 }
-
+import { ArrowLineMaterialProperty } from '@/utils/cesium/Material'
+let arrowLineMaterialProperty = new ArrowLineMaterialProperty({
+	color: new Cesium.Color(128 / 255, 215 / 255, 255 / 255, 1),
+	directionColor: new Cesium.Color(1, 1, 1, 1),
+	outlineColor: new Cesium.Color(1, 1, 1, 1),
+	outlineWidth: 0,
+	speed: 5,
+})
 // 获取航线详情
 function getAirDetails() {
 	queryAirById(formData.value.patrolRouteUrl).then(res => {
@@ -440,8 +447,8 @@
 			polyline: {
 				positions: Cesium.Cartesian3.fromDegreesArray(result),
 				clampToGround: true,
-				width: 3,
-				material: Cesium.Color.RED,
+				width: 4,
+				material: arrowLineMaterialProperty,
 			},
 		})
 		// 渲染起点(蓝色)
diff --git a/applications/task-work-order/src/views/orderView/orderManage/orderManage/FormDiaLog.vue b/applications/task-work-order/src/views/orderView/orderManage/orderManage/FormDiaLog.vue
index 94c76cd..1da7cd5 100644
--- a/applications/task-work-order/src/views/orderView/orderManage/orderManage/FormDiaLog.vue
+++ b/applications/task-work-order/src/views/orderView/orderManage/orderManage/FormDiaLog.vue
@@ -265,6 +265,9 @@
 import CommonCesiumMap from '@/components/map-container/common-cesium-map.vue'
 import { gdPatrolTaskPageApi } from '@/views/orderView/orderManage/inspectionRequest/inspectionRequestApi'
 import { Check } from '@element-plus/icons-vue'
+import droneIcon from '@/assets/images/orderView/orderManage/drone.png'
+import droneActiveIcon from '@/assets/images/orderView/orderManage/droneActive.png'
+
 // 初始化表单数据
 const initForm = () => ({
 	workOrderName: '',
@@ -302,8 +305,6 @@
 const processList = ref([])
 let viewPlane
 const hasPatrolTaskList = computed(() => ['30', '40', '50', '60'].includes(String(formData.value.workOrderStatus)))
-import droneIcon from '@/assets/images/orderView/orderManage/drone.png'
-import droneActiveIcon from '@/assets/images/orderView/orderManage/droneActive.png'
 
 const gdStatusObj = {
 	'10': { reason: '拒绝原因', operationType: '2' },

--
Gitblit v1.9.3