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

---
 applications/drone-command/src/utils/cesium/DrawPolygon.js                      |   70 ++--
 uniapps/work-wx/src/pages.json                                                  |    2 
 uniapps/work-wx/src/static/images/fj.png                                        |    0 
 applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue        |  368 ++++++++++++++++++++++++++++++
 applications/drone-command/src/views/areaManage/partition/index.vue             |  185 +++++++++++++++
 uniapps/work-wx/src/static/images/fj.svg                                        |   10 
 applications/drone-command/src/views/areaManage/partition/partitionApi.js       |   45 +++
 applications/drone-command/src/views/areaManage/precinctInfo/precinctInfoApi.js |   11 
 applications/drone-command/src/utils/cesium/mapUtil.js                          |   17 -
 applications/drone-command/src/views/basicManage/deviceStock/fwDevice.js        |   11 
 10 files changed, 662 insertions(+), 57 deletions(-)

diff --git a/applications/drone-command/src/utils/cesium/DrawPolygon.js b/applications/drone-command/src/utils/cesium/DrawPolygon.js
index 59cbf6c..6c37a35 100644
--- a/applications/drone-command/src/utils/cesium/DrawPolygon.js
+++ b/applications/drone-command/src/utils/cesium/DrawPolygon.js
@@ -88,18 +88,18 @@
 	// ============ 发布订阅机制 ============
 
 	// 外部订阅数据变化
-	subscribe (key, listener) {
+	subscribe(key, listener) {
 		this.listeners.push({ key, listener })
 	}
 
 	// 通知订阅者
-	notify (key, data) {
+	notify(key, data) {
 		this.listeners.filter(subscriber => subscriber.key === key).forEach(subscriber => subscriber.listener(data))
 	}
 
 	// ============ 绘制相关 ============
 	// 编辑图斑
-	editThePatch (data) {
+	editThePatch(data) {
 		this.isPreviewMode = data
 		// 关闭编辑能力时隐藏端点/中点,并清空中点,避免残留交互
 		if (!this.isPreviewMode) {
@@ -117,12 +117,12 @@
 		}
 	}
 	// 删除测区
-	deleteTheArea (data) {
+	deleteTheArea(data) {
 		this.isDeleteTheArea = data
 	}
 
 	// 开始绘制
-	startDrawing () {
+	startDrawing() {
 		this.drawingMode = true
 		this.curPolygon = new Cesium.PolygonHierarchy()
 
@@ -150,7 +150,7 @@
 	}
 
 	// 创建多边形实体(含边界线)
-	createPolygonEntity () {
+	createPolygonEntity() {
 		this.polygonEntity = this.editPolygonDataSource.entities.add({
 			name: DrawPolygon.ENTITY_NAMES.POLYGON,
 			id: DrawPolygon.ENTITY_NAMES.POLYGON_ID,
@@ -174,7 +174,7 @@
 	}
 
 	// 创建端点实体
-	createPointEntity (position, isAdd) {
+	createPointEntity(position, isAdd) {
 		const pointIndex = isAdd ? this.curPolygon.positions.length - 2 : this.curPolygon.positions.length - 1
 
 		this.editPolygonPointDataSource.entities.add({
@@ -193,7 +193,7 @@
 		})
 	}
 
-	createMidPointEntity (startInd, position) {
+	createMidPointEntity(startInd, position) {
 		// 使用 CallbackProperty 让中点位置随端点拖拽实时计算
 		// startInd 表示该中点属于边:(startInd) -> (startInd + 1)
 		const updatePosition = () => {
@@ -224,7 +224,7 @@
 		})
 	}
 
-	rebuildEditPoints () {
+	rebuildEditPoints() {
 		// 插入/删除端点后:端点实体 id 与 customData.ind 需要全量重建,保证索引与 positions 一致
 		if (!this.isPreviewMode) return
 		if (!this.editPolygonPointDataSource || !this.curPolygon?.positions) return
@@ -248,7 +248,7 @@
 		})
 	}
 
-	rebuildMidPoints () {
+	rebuildMidPoints() {
 		// 仅在“顶点数量变化/切换编辑态”时维护中点实体数量
 		// 拖拽过程中中点位置由 CallbackProperty 自动更新,不需要重建
 		if (!this.isPreviewMode) return
@@ -286,7 +286,7 @@
 		})
 	}
 
-	insertPointFromMidPoint (midPointEntity) {
+	insertPointFromMidPoint(midPointEntity) {
 		// 点击中点:在对应边上插入一个新端点(白点),并立即进入拖拽态
 		if (!this.isPreviewMode) return
 		if (!this.editingMode) return
@@ -319,7 +319,7 @@
 		this.notify('getPolygonPositions', this.curPolygon.positions)
 	}
 	// 清除不在范围内的点
-	removeLastInvalidPoint () {
+	removeLastInvalidPoint() {
 		const posLen = this.curPolygon.positions.length
 		if (posLen === 0) return
 
@@ -348,7 +348,7 @@
 		}
 	}
 	// 添加一个点
-	addPosition (position, isAdd = true) {
+	addPosition(position, isAdd = true) {
 		// 第一个点要重复压入一次,形成动态绘制效果
 		if (this.curPolygon.positions.length === 0 && isAdd) {
 			this.curPolygon.positions.push(position.clone())
@@ -372,7 +372,7 @@
 	// ============ 鼠标事件 ============
 
 	// 鼠标左键按下(选中端点拖动)
-	handleLeftDown (movement) {
+	handleLeftDown(movement) {
 		if (!this.editingMode) return
 
 		const pickedEntity = this.viewer.scene.pick(movement.position)?.id
@@ -393,7 +393,7 @@
 	}
 
 	// 鼠标左键抬起(拖拽结束)
-	handleLeftUp () {
+	handleLeftUp() {
 		if (!(this.editingMode && this.curPolygon?.positions && this.draggedEntity)) return
 
 		if (this.currentDragPointIsValid && this.isDragging) {
@@ -426,7 +426,7 @@
 	}
 
 	// 鼠标移动
-	handleMouseMove (movement) {
+	handleMouseMove(movement) {
 		if (!this.drawingMode && !this.editingMode) return
 
 		const cartesian = this.viewer.scene.pickPosition(movement.endPosition)
@@ -464,7 +464,7 @@
 	}
 
 	// 鼠标左键点击
-	handleLeftClick (click) {
+	handleLeftClick(click) {
 		this.removeMenuPopup()
 
 		const pickedAllEntity = this.viewer.scene.drillPick(click.position).filter(i => i.id)
@@ -520,7 +520,7 @@
 	}
 
 	// 鼠标右键点击(弹出菜单)
-	handleRightClick (click) {
+	handleRightClick(click) {
 		const that = this
 		if (that.drawingMode) return
 
@@ -556,7 +556,7 @@
 	// ============ 删除相关 ============
 
 	// 删除所有实体
-	removeEntities () {
+	removeEntities() {
 		if (this.editPolygonDataSource) {
 			this.editPolygonDataSource.entities.removeAll()
 			this.editPolygonDataSource = null
@@ -577,7 +577,7 @@
 	}
 
 	// 完成绘制
-	finishDrawing () {
+	finishDrawing() {
 		this.curPolygon.positions.pop()
 
 		if (this.curPolygon.positions.length >= 3) {
@@ -603,20 +603,20 @@
 	}
 
 	// 删除多边形
-	delPolygon () {
+	delPolygon() {
 		this.removeEntities()
 		this.removeMenuPopup()
 		this.notify('getPolygonPositions', [])
 		this.startDrawing()
 	}
 	// 删除图斑
-	delSpot () {
+	delSpot() {
 		this.removeEntities()
 		this.isPreviewMode = true
 		this.startDrawing()
 	}
 	// 删除端点
-	delPoint () {
+	delPoint() {
 		if (this.curPolygon.positions.length <= 3) {
 			this.removeMenuPopup()
 			return ElMessage.warning('端点不可少于3个')
@@ -634,7 +634,7 @@
 	// ============ 工具方法 ============
 
 	// 创建右键菜单
-	createMenuPopup (type = 'polygon') {
+	createMenuPopup(type = 'polygon') {
 		const menuPopupVBox = document.createElement('div')
 		menuPopupVBox.id = 'planarPolygonEdit'
 		menuPopupVBox.className = 'planar-polygon-edit-tooltip'
@@ -660,7 +660,7 @@
 	}
 
 	// 移除菜单
-	removeMenuPopup () {
+	removeMenuPopup() {
 		const that = this
 		if (that.menuPopup) {
 			that.menuPopup.removeEventListener('click', that.delPolygon)
@@ -672,13 +672,13 @@
 	}
 
 	// 更新多边形样式(正常/错误)
-	updatePolygonAppearance (polygonColor, lineColor) {
+	updatePolygonAppearance(polygonColor, lineColor) {
 		this.polygonEntity.polygon.material = polygonColor
 		this.polygonEntity.polyline.material = lineColor
 	}
 
 	// 禁用地图交互
-	disableMapControl () {
+	disableMapControl() {
 		const controller = this.viewer.scene.screenSpaceCameraController
 		controller.enableRotate = false
 		controller.enableTranslate = false
@@ -686,7 +686,7 @@
 	}
 
 	// 启用地图交互
-	enableMapControl () {
+	enableMapControl() {
 		const controller = this.viewer.scene.screenSpaceCameraController
 		controller.enableRotate = true
 		controller.enableTranslate = true
@@ -694,7 +694,7 @@
 	}
 
 	// 检查多边形是否自交
-	curDragPointIsValid (positions) {
+	curDragPointIsValid(positions) {
 		if (positions.length < 3) return true
 
 		const cartographics = Cesium.Ellipsoid.WGS84.cartesianArrayToCartographicArray(positions)
@@ -710,12 +710,12 @@
 		return intersections.features.length === 0
 	}
 	// 初始化已有多边形
-	async initPolygon (viewer, positions, isPurePreview = false) {
+	async initPolygon(viewer, positions, isPurePreview = false) {
 		this.initHandler(viewer)
 		this.isPureSpotPreview = isPurePreview
 		this.startDrawing()
 		let newPosition = positions.map(item => {
-			return Cesium.Cartesian3.fromDegrees(Number(item.lng), Number(item.lat), Number(item?.height||0))
+			return Cesium.Cartesian3.fromDegrees(Number(item.lng), Number(item.lat), Number(item?.height || 0))
 		})
 		// 预览航线的时候调用
 		if (!this.isPureSpotPreview) {
@@ -737,7 +737,7 @@
 			duration: 0.5,
 		})
 
-		const pointList = await getPointPositionsHeight(positions, viewer)
+		let pointList = await getPointPositionsHeight(positions, viewer)
 		flyVisual({ positionsData: pointList.map(item => [item.lng, item.lat, item.ASL]), viewer })
 
 		this.drawingMode = false
@@ -748,7 +748,7 @@
 	}
 
 	// 初始化事件处理器
-	initHandler (viewer) {
+	initHandler(viewer) {
 		this.viewer = viewer
 		this.startDrawing()
 
@@ -771,7 +771,7 @@
 	}
 
 	// 移除事件处理器
-	removeHandler () {
+	removeHandler() {
 		if (this.handler) {
 			const eventTypes = [
 				Cesium.ScreenSpaceEventType.LEFT_DOWN,
@@ -792,7 +792,7 @@
 	/**
 	 * 销毁实例,释放资源
 	 */
-	destroy () {
+	destroy() {
 		if (!this.viewer) return
 
 		this.removeMenuPopup()
diff --git a/applications/drone-command/src/utils/cesium/mapUtil.js b/applications/drone-command/src/utils/cesium/mapUtil.js
index 117ada6..89bf787 100644
--- a/applications/drone-command/src/utils/cesium/mapUtil.js
+++ b/applications/drone-command/src/utils/cesium/mapUtil.js
@@ -346,31 +346,24 @@
 	return new Promise((resolve, reject) => {
 		if (!data || !data.length) {
 			resolve([])
-
 			return
 		}
-
 		// 假设 viewer 已经初始化并且 terrainProvider 是有效的
 		const terrainProvider = viewer?.terrainProvider
-
 		// 创建 Cartographic 对象
 		const cartographics = data.map(item => {
 			const { lng, lat } = item
-
 			return Cesium.Cartographic.fromDegrees(Number(lng), Number(lat))
 		})
-
 		// 获取地形数据的Promise
 		const promise = Cesium.sampleTerrainMostDetailed(terrainProvider, cartographics)
-
 		// 使用 Cesium.when 处理 Promise
 		promise
 			.then(function (updatedPositions) {
-				// updatedPositions 是一个数组,包含更新后的 Cartographic 对象
+				//  是一个数组,包含更新后的 Cartographic 对象
 				const newPosition = updatedPositions.map((item, index) => {
 					const longitude = Cesium.Math.toDegrees(item.longitude)
 					const latitude = Cesium.Math.toDegrees(item.latitude)
-
 					let pointData = {
 						...data[index],
 						longitude,
@@ -378,18 +371,14 @@
 						ASL: Number(item.height),
 						customHeight: Number(item.height),
 					}
-
 					if (droneHeight) pointData.TH = Number(item.height) + Number(droneHeight)
-
 					return pointData
 				})
-
 				resolve(newPosition)
-				// 在这里,你可以使用 height 值进行后续操作
 			})
 			.catch(function (error) {
-				// console.error('获取高程时发生错误:', error);
-				reject(error)
+				console.log('获取高程时发生错误:', error)
+				resolve(data.map(item => ({ ...item, ASL: 0, customHeight: 0, longitude: item.lng, latitude: item.lat })))
 			})
 	})
 }
diff --git a/applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue b/applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue
new file mode 100644
index 0000000..2bda091
--- /dev/null
+++ b/applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue
@@ -0,0 +1,368 @@
+<template>
+	<el-dialog v-model="visible" :title="titleEnum[dialogMode]" :close-on-click-modal="false" width="80%">
+		<div class="bodyBox">
+			<div class="leftMap ztzf-cesium" id="mapContainer"></div>
+			<div class="rightInfo">
+				<div v-if="readonly">
+					<el-row>
+						<el-col :span="24">
+							<div>区域名称: {{ formData.areaName }}</div>
+						</el-col>
+						<el-col :span="24">
+							<div>区域位置: {{ formatLocation(formData) }}</div>
+						</el-col>
+						<el-col :span="24">
+							<div>区域面积: {{ formData.areaSize }}k㎡</div>
+						</el-col>
+						<el-col :span="24">
+							<div>区域类型: {{ getDictLabel(formData.areaType, dictObj.areaType) }}</div>
+						</el-col>
+						<el-col :span="24">
+							<div>触发条件: {{ formData.triggerCondition }}</div>
+						</el-col>
+						<el-col :span="24">
+							<div>响应机制: {{ formData.responseMechanism }}</div>
+						</el-col>
+						<el-col :span="24">
+							<div>管控级别: {{ getDictLabel(formData.controlLevel, dictObj.controlLevel) }}</div>
+						</el-col>
+						<el-col :span="24">
+							<div>关联派出所: {{ formData.policeStationName }}</div>
+						</el-col>
+						<el-col :span="24">
+							<div>可飞行时间段: {{ formatFlyDate(formData) }}</div>
+						</el-col>
+					</el-row>
+				</div>
+				<el-form v-else ref="formRef" :model="formData" :rules="rules" :disabled="readonly" label-width="100px">
+					<el-row>
+						<el-col :span="24">
+							<el-form-item label="区域名称" prop="areaName">
+								<el-input v-model="formData.areaName" maxlength="50" placeholder="请输入" clearable />
+							</el-form-item>
+						</el-col>
+						<el-col :span="24">
+							<el-form-item label="区域位置" prop="longitude">
+								<div>{{ formatLocation(formData) }}</div>
+							</el-form-item>
+						</el-col>
+						<el-col :span="24">
+							<el-form-item label="区域面积" prop="areaSize">{{ formData.areaSize }}k㎡</el-form-item>
+						</el-col>
+						<el-col :span="24">
+							<el-form-item label="区域类型" prop="areaType">
+								<el-select 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-form-item>
+						</el-col>
+						<el-col :span="24">
+							<el-form-item label="响应机制" prop="responseMechanism">
+								<el-input v-model="formData.responseMechanism" maxlength="200" placeholder="请输入" clearable />
+							</el-form-item>
+						</el-col>
+						<el-col :span="24">
+							<el-form-item label="触发条件" prop="triggerCondition">
+								<el-input v-model="formData.triggerCondition" maxlength="200" placeholder="请输入" clearable />
+							</el-form-item>
+						</el-col>
+						<el-col :span="24">
+							<el-form-item label="管控级别" prop="controlLevel">
+								<el-select v-model="formData.controlLevel" placeholder="请选择" clearable>
+									<el-option
+										v-for="item in dictObj.controlLevel"
+										:key="item.dictKey"
+										:label="item.dictValue"
+										:value="item.dictKey"
+									/>
+								</el-select>
+							</el-form-item>
+						</el-col>
+						<el-col :span="24">
+							<el-form-item label="可飞行时段" prop="flyDateRange">
+								<el-date-picker
+									v-model="flyDateRange"
+									type="datetimerange"
+									range-separator="至"
+									start-placeholder="开始时间"
+									end-placeholder="结束时间"
+									value-format="YYYY-MM-DD HH:mm:ss"
+									clearable
+								/>
+							</el-form-item>
+						</el-col>
+						<el-col :span="24">
+							<el-form-item label="关联派出所" prop="policeStationId">
+								<el-select v-model="formData.policeStationId" placeholder="请选择" clearable>
+									<el-option
+										v-for="item in policeStationOptions"
+										:key="item.id"
+										:label="item.stationName"
+										:value="item.id"
+									/>
+								</el-select>
+							</el-form-item>
+						</el-col>
+						<el-col :span="24">
+							<el-form-item label="关联设备" prop="deviceIds">
+								<el-select v-model="deviceIdList" multiple collapse-tags placeholder="请选择" clearable>
+									<el-option v-for="item in deviceOptions" :key="item.id" :label="item.deviceName" :value="item.id" />
+								</el-select>
+							</el-form-item>
+						</el-col>
+					</el-row>
+				</el-form>
+			</div>
+		</div>
+		<template #footer>
+			<el-button @click="handleCancel">{{ readonly ? '关闭' : '取消' }}</el-button>
+			<el-button v-if="!readonly" type="primary" :loading="submitting" :disabled="submitting" @click="handleSubmit">
+				确定
+			</el-button>
+		</template>
+	</el-dialog>
+</template>
+
+<script setup>
+import { computed, inject, nextTick, onMounted, ref, watch } from 'vue'
+import { ElMessage } from 'element-plus'
+import {
+	fwAreaDivideDetailApi,
+	fwAreaDivideSubmitApi,
+} from './partitionApi'
+import { fieldRules, getDictLabel } from '@ztzf/utils'
+import { PublicCesium } from '@/utils/cesium/publicCesium'
+import { DrawPolygon } from '@/utils/cesium/DrawPolygon'
+import { cartesian3Convert } from '@/utils/cesium/mapUtil'
+import * as turf from '@turf/turf'
+import * as Cesium from 'cesium'
+import { fwPoliceStationListApi } from '@/views/areaManage/precinctInfo/precinctInfoApi'
+import { fwDeviceListApi } from '@/views/basicManage/deviceStock/fwDevice'
+
+const initForm = () => ({
+	areaName: '', // 区域名称
+	areaSize: null, // 区域面积
+	areaType: '', // 区域类型
+	controlLevel: '', // 管控级别
+	deviceIds: '', // 关联设备ID
+	flyDateEnd: '', // 可飞行结束
+	flyDateStart: '', // 可飞行开始
+	latitude: null, // 区域中心纬度
+	longitude: null, // 区域中心经度
+	policeStationId: '', // 关联派出所id
+	responseMechanism: '', // 响应机制
+	triggerCondition: '', // 触发条件
+})
+
+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 flyDateRange = ref([]) // 飞行时间
+const deviceIdList = ref([]) // 关联设备ID
+const policeStationOptions = ref([]) // 关联派出所
+const deviceOptions = ref([]) // 关联设备
+const dictObj = inject('dictObj')
+let viewer
+let drawPolygonExample
+let pointList = []
+
+const rules = {
+	areaName: fieldRules(true, 50),
+	areaType: fieldRules(true, 0),
+	controlLevel: fieldRules(true, 0),
+}
+
+// 关闭弹框
+function handleCancel() {
+	visible.value = false
+}
+
+// 提交新增/编辑
+async function handleSubmit() {
+	const isValid = await formRef.value?.validate().catch(() => false)
+	if (!isValid) return
+	submitting.value = true
+	try {
+		const [start, end] = flyDateRange.value ?? []
+		formData.value.flyDateStart = start || ''
+		formData.value.flyDateEnd = end || ''
+		formData.value.deviceIds = deviceIdList.value.length ? deviceIdList.value.join(',') : ''
+		let str = [...pointList, pointList[0]].map(item => `${item.longitude} ${item.latitude}`).join(',')
+		formData.value.geom = `POLYGON((${str}))`
+		await fwAreaDivideSubmitApi(formData.value)
+		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 fwAreaDivideDetailApi({ id: formData.value.id })
+	formData.value = res?.data?.data ?? initForm()
+	flyDateRange.value = [formData.value.flyDateStart, formData.value.flyDateEnd].filter(Boolean)
+	deviceIdList.value = formData.value.deviceIds ? formData.value.deviceIds.split(',') : []
+}
+
+// 格式化区域位置
+function formatLocation(row) {
+	if (row?.longitude == null || row?.latitude == null) return ''
+	return `${_.round(row.longitude, 6)}, ${_.round(row.latitude, 6)}`
+}
+
+// 格式化可飞行时间段
+function formatFlyDate(row) {
+	if (!row?.flyDateStart && !row?.flyDateEnd) return ''
+	return `${row.flyDateStart || '-'} ~ ${row.flyDateEnd || '-'}`
+}
+
+// 初始化地图实例
+function initMap() {
+	const publicCesiumInstance = new PublicCesium({
+		dom: 'mapContainer',
+		flatMode: false,
+		terrain: true,
+		layerMode: 4,
+		boundary: false,
+	})
+	viewer = publicCesiumInstance.getViewer()
+}
+
+// 绘制完成回调
+const drawFinished = async data => {
+	pointList = _.cloneDeep(data).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 getLonLat(str) {
+	if (!str) return []
+	return str
+		.replace('POLYGON((', '')
+		.replace('))', '')
+		.split(',')
+		.map(pair => {
+			const [lon, lat] = pair.trim().split(' ').map(Number)
+			return [lon, lat]
+		})
+}
+
+// 编辑面
+function editPolygon() {
+	if (!formData.value?.geom) return
+	const grouped = getLonLat(formData.value.geom)
+	grouped.pop()
+	pointList = grouped.map(item => ({ longitude: item[0], latitude: item[1] }))
+	drawPolygonExample?.destroy()
+	drawPolygonExample = new DrawPolygon(viewer)
+	drawPolygonExample.initPolygon(
+		viewer,
+		grouped.map(item => ({ lng: item[0], lat: item[1] }))
+	)
+	drawPolygonExample.subscribe('getPolygonPositions', drawFinished)
+}
+
+// 查看面
+function viewPolygon() {
+	if (!formData.value?.geom) return
+	const grouped = getLonLat(formData.value.geom)
+	grouped.pop()
+	pointList = grouped.map(item => ({ longitude: item[0], latitude: item[1] }))
+	const result = grouped.flat().flat()
+	const mian = viewer.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,
+		},
+	})
+	viewer.flyTo(mian)
+}
+
+// 获取派出所列表
+async function getPoliceStationList() {
+	const res = await fwPoliceStationListApi()
+	policeStationOptions.value = res?.data?.data ?? []
+}
+
+// 获取设备列表
+async function getDeviceList() {
+	const res = await fwDeviceListApi({ isAreaSelect: 1 })
+	deviceOptions.value = res?.data?.data ?? []
+}
+
+// 打开弹框
+async function open({ mode, row } = {}) {
+	dialogMode.value = mode || 'add'
+	formData.value = dialogMode.value === 'add' ? initForm() : row
+	await nextTick()
+	initMap()
+	if (dialogMode.value === 'add') {
+		addPolygon()
+	} else if (dialogMode.value === 'edit') {
+		await loadDetail()
+		editPolygon()
+	} else {
+		await loadDetail()
+		viewPolygon()
+	}
+}
+
+onMounted(() => {
+	getPoliceStationList()
+	getDeviceList()
+})
+
+defineExpose({
+	open,
+})
+</script>
+
+<style scoped lang="scss">
+.bodyBox {
+	display: flex;
+	height: 600px;
+	.leftMap {
+		width: 70%;
+		height: 100%;
+	}
+	.rightInfo {
+		width: 30%;
+		height: 100%;
+		overflow: auto;
+	}
+}
+</style>
diff --git a/applications/drone-command/src/views/areaManage/partition/index.vue b/applications/drone-command/src/views/areaManage/partition/index.vue
index 14a6b62..34c9406 100644
--- a/applications/drone-command/src/views/areaManage/partition/index.vue
+++ b/applications/drone-command/src/views/areaManage/partition/index.vue
@@ -1,9 +1,184 @@
 <template>
-   <basic-container>
-    区域划分
-  </basic-container>
+	<basic-container>
+		<el-form ref="queryParamsRef" :model="searchParams">
+			<el-row :gutter="16">
+				<el-col :span="4">
+					<el-form-item label="区域名称" prop="areaName">
+						<el-input v-model="searchParams.areaName" placeholder="请输入" clearable @clear="handleSearch" />
+					</el-form-item>
+				</el-col>
+				<el-col :span="4">
+					<el-form-item label="区域类型" prop="areaType">
+						<el-select v-model="searchParams.areaType" placeholder="请选择" clearable @change="handleSearch">
+							<el-option
+								v-for="item in dictObj.areaType"
+								:key="item.dictKey"
+								:label="item.dictValue"
+								:value="item.dictKey"
+							/>
+						</el-select>
+					</el-form-item>
+				</el-col>
+				<el-col :span="4">
+					<el-form-item>
+						<el-button @click="resetForm">重置</el-button>
+						<el-button type="primary" @click="handleSearch">查询</el-button>
+					</el-form-item>
+				</el-col>
+			</el-row>
+		</el-form>
+		<div>
+			<el-button type="primary" @click="openForm('add')">新增</el-button>
+			<el-button type="danger" :disabled="!selectedIds.length" @click="handleDelete()">删除</el-button>
+		</div>
+		<el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
+			<el-table-column type="selection" width="55" />
+			<el-table-column type="index" width="60" label="序号" />
+			<el-table-column prop="areaName" label="区域名称" />
+			<el-table-column label="区域位置">
+				<template v-slot="{ row }">
+					{{ formatLocation(row) }}
+				</template>
+			</el-table-column>
+			<el-table-column prop="areaSize" label="区域面积(k㎡)" />
+			<el-table-column prop="areaType" label="区域类型">
+				<template v-slot="{ row }">
+					{{ getDictLabel(row.areaType, dictObj.areaType) }}
+				</template>
+			</el-table-column>
+			<el-table-column prop="triggerCondition" label="触发条件" />
+			<el-table-column prop="responseMechanism" label="响应机制" />
+			<el-table-column prop="controlLevel" label="管控级别">
+				<template v-slot="{ row }">
+					{{ getDictLabel(row.controlLevel, dictObj.controlLevel) }}
+				</template>
+			</el-table-column>
+			<el-table-column prop="policeStationName" label="关联派出所" />
+			<el-table-column label="可飞行时间段">
+				<template v-slot="{ row }">
+					{{ formatFlyDate(row) }}
+				</template>
+			</el-table-column>
+			<el-table-column label="操作">
+				<template v-slot="{ row }">
+					<el-link @click="openForm('view', row)" type="primary">查看</el-link>
+					<el-link @click="openForm('edit', row)" type="warning">编辑</el-link>
+					<el-link @click="handleDelete(row)" type="danger">删除</el-link>
+				</template>
+			</el-table-column>
+		</el-table>
+		<div>
+			<el-pagination
+				v-model:current-page="searchParams.current"
+				v-model:page-size="searchParams.size"
+				:total="total"
+				@change="getList"
+			/>
+		</div>
+
+		<FormDiaLog v-if="dialogVisible" v-model="dialogVisible" ref="dialogRef" @success="getList" />
+	</basic-container>
 </template>
+
 <script setup>
+import { nextTick, onMounted, ref } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { fwAreaDividePageApi, fwAreaDivideRemoveApi } from './partitionApi'
+import FormDiaLog from './FormDiaLog.vue'
+import { getDictionaryByCode } from '@/api/system/dictbiz'
+import { getDictLabel } from '@ztzf/utils'
+
+const initSearchParams = () => ({
+	areaName: '', // 区域名称
+	areaType: '', // 区域类型
+	current: 1, // 当前页
+	size: 10, // 每页大小
+})
+
+const searchParams = ref(initSearchParams()) // 查询参数
+const total = ref(0) // 总条数
+const loading = ref(false) // 列表加载中
+const list = ref([]) // 列表数据
+const selectedIds = ref([]) // 勾选的ID列表
+const queryParamsRef = ref(null) // 查询表单实例
+const dialogRef = ref(null) // 弹框实例
+const dialogVisible = ref(false)
+
+// 获取列表
+async function getList() {
+	loading.value = true
+	try {
+		const res = await fwAreaDividePageApi(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' })
+	const ids = row ? row.id : selectedIds.value.join(',')
+	await fwAreaDivideRemoveApi({ ids })
+	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 `${row.longitude}, ${row.latitude}`
+}
+
+function formatFlyDate(row) {
+	if (!row?.flyDateStart && !row?.flyDateEnd) return ''
+	return `${row.flyDateStart || '-'} ~ ${row.flyDateEnd || '-'}`
+}
+
+const dictObj = ref({
+	areaType: [], //区域类型
+	controlLevel: [], //管控级别
+})
+provide('dictObj', dictObj)
+
+// 获取字典
+function getDictList() {
+	getDictionaryByCode('areaType,controlLevel').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>
+<style scoped lang="scss"></style>
diff --git a/applications/drone-command/src/views/areaManage/partition/partitionApi.js b/applications/drone-command/src/views/areaManage/partition/partitionApi.js
new file mode 100644
index 0000000..2d604dd
--- /dev/null
+++ b/applications/drone-command/src/views/areaManage/partition/partitionApi.js
@@ -0,0 +1,45 @@
+import request from '@/axios'
+
+// 查page
+export const fwAreaDividePageApi = params => {
+	return request({
+		url: `/drone-fw/area/fwAreaDivide/page`,
+		method: 'get',
+		params,
+	})
+}
+// 查list
+export const fwAreaDivideListApi = params => {
+	return request({
+		url: `/drone-fw/area/fwAreaDivide/list`,
+		method: 'get',
+		params,
+	})
+}
+
+// 增加或更新
+export const fwAreaDivideSubmitApi = data => {
+	return request({
+		url: `/drone-fw/area/fwAreaDivide/submit`,
+		method: 'post',
+		data,
+	})
+}
+
+//删除
+export const fwAreaDivideRemoveApi = params => {
+	return request({
+		url: `/drone-fw/area/fwAreaDivide/remove`,
+		method: 'post',
+		params,
+	})
+}
+
+//详情
+export const fwAreaDivideDetailApi = params => {
+	return request({
+		url: `/drone-fw/area/fwAreaDivide/detail`,
+		method: 'get',
+		params,
+	})
+}
diff --git a/applications/drone-command/src/views/areaManage/precinctInfo/precinctInfoApi.js b/applications/drone-command/src/views/areaManage/precinctInfo/precinctInfoApi.js
index 3463828..0168fea 100644
--- a/applications/drone-command/src/views/areaManage/precinctInfo/precinctInfoApi.js
+++ b/applications/drone-command/src/views/areaManage/precinctInfo/precinctInfoApi.js
@@ -1,6 +1,6 @@
 import request from '@/axios'
 
-// 查list
+// 查page
 export const fwPoliceStationPageApi = params => {
 	return request({
 		url: `/drone-fw/area/fwPoliceStation/page`,
@@ -9,6 +9,15 @@
 	})
 }
 
+// 查list
+export const fwPoliceStationListApi = params => {
+	return request({
+		url: `/drone-fw/area/fwPoliceStation/list`,
+		method: 'get',
+		params,
+	})
+}
+
 // 增加或更新
 export const fwPoliceStationSubmitApi = data => {
 	return request({
diff --git a/applications/drone-command/src/views/basicManage/deviceStock/fwDevice.js b/applications/drone-command/src/views/basicManage/deviceStock/fwDevice.js
index 8895046..1e3cbf8 100644
--- a/applications/drone-command/src/views/basicManage/deviceStock/fwDevice.js
+++ b/applications/drone-command/src/views/basicManage/deviceStock/fwDevice.js
@@ -1,6 +1,6 @@
 import request from '@/axios'
 
-// 查list
+// 查page
 export const fwDevicePageApi = params => {
 	return request({
 		url: `/drone-fw/device/fwDevice/page`,
@@ -9,6 +9,15 @@
 	})
 }
 
+// 查list
+export const fwDeviceListApi = params => {
+	return request({
+		url: `/drone-fw/device/fwDevice/list`,
+		method: 'get',
+		params,
+	})
+}
+
 // 增加或更新
 export const fwDeviceSubmitApi = data => {
 	return request({
diff --git a/uniapps/work-wx/src/pages.json b/uniapps/work-wx/src/pages.json
index ee421ad..f6784de 100644
--- a/uniapps/work-wx/src/pages.json
+++ b/uniapps/work-wx/src/pages.json
@@ -84,7 +84,7 @@
 				{
 					"path": "regulationsDetail/details",
 					"style": {
-						"navigationBarTitleText": "法律政策详情"
+						"navigationBarTitleText": "详情"
 					}
 				},
 				{
diff --git a/uniapps/work-wx/src/static/images/fj.png b/uniapps/work-wx/src/static/images/fj.png
new file mode 100644
index 0000000..e4b111c
--- /dev/null
+++ b/uniapps/work-wx/src/static/images/fj.png
Binary files differ
diff --git a/uniapps/work-wx/src/static/images/fj.svg b/uniapps/work-wx/src/static/images/fj.svg
new file mode 100644
index 0000000..2438cce
--- /dev/null
+++ b/uniapps/work-wx/src/static/images/fj.svg
@@ -0,0 +1,10 @@
+<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g id="Frame" clip-path="url(#clip0_126_1748)">
+<path id="Vector" d="M10.8359 1.48807C11.1239 1.77607 11.3379 2.09807 11.4779 2.45407C11.6179 2.81007 11.6879 3.17607 11.6879 3.55207C11.6879 3.92807 11.6179 4.29407 11.4779 4.65007C11.3379 5.00607 11.1239 5.32807 10.8359 5.61607L6.23991 10.1761C5.95191 10.4641 5.59791 10.7041 5.17791 10.8961C4.75791 11.0881 4.31391 11.2001 3.84591 11.2321C3.37791 11.2641 2.89991 11.2001 2.41191 11.0401C1.92391 10.8801 1.47191 10.5921 1.05591 10.1761C0.647906 9.76807 0.363906 9.32607 0.203906 8.85007C0.0439063 8.37407 -0.0200938 7.90007 0.0119062 7.42807C0.0439062 6.95607 0.153906 6.51207 0.341906 6.09607C0.529906 5.68007 0.767906 5.32807 1.05591 5.04007L5.13591 0.996074C5.20791 0.924074 5.31391 0.906074 5.45391 0.942074C5.59391 0.978074 5.69991 1.03207 5.77191 1.10407C5.83591 1.17607 5.88791 1.28207 5.92791 1.42207C5.96791 1.56207 5.95191 1.66807 5.87991 1.74007L1.81191 5.77207C1.59591 5.98807 1.41591 6.24007 1.27191 6.52807C1.12791 6.81607 1.04391 7.12407 1.01991 7.45207C0.995906 7.78007 1.04391 8.11207 1.16391 8.44807C1.28391 8.78407 1.49991 9.10807 1.81191 9.42007C2.09191 9.70007 2.40191 9.89807 2.74191 10.0141C3.08191 10.1301 3.41991 10.1781 3.75591 10.1581C4.09191 10.1381 4.41191 10.0641 4.71591 9.93607C5.01991 9.80807 5.27991 9.63607 5.49591 9.42007L10.0799 4.87207C10.2959 4.65607 10.4499 4.43207 10.5419 4.20007C10.6339 3.96807 10.6779 3.73807 10.6739 3.51007C10.6699 3.28207 10.6159 3.06007 10.5119 2.84407C10.4079 2.62807 10.2639 2.42807 10.0799 2.24407C9.73591 1.90807 9.34991 1.75007 8.92191 1.77007C8.49391 1.79007 8.06391 2.01607 7.63191 2.44807L3.49191 6.56407C3.30791 6.74807 3.21791 6.95207 3.22191 7.17607C3.22591 7.40007 3.29991 7.58407 3.44391 7.72807C3.61991 7.90407 3.82191 7.97607 4.04991 7.94407C4.27791 7.91207 4.46391 7.82407 4.60791 7.68007L8.36391 3.94807C8.43591 3.87607 8.54191 3.85807 8.68191 3.89407C8.82191 3.93007 8.92791 3.98407 8.99991 4.05607C9.07191 4.12807 9.12791 4.23407 9.16791 4.37407C9.20791 4.51407 9.19191 4.62007 9.11991 4.69207L5.35191 8.42407C5.06391 8.71207 4.78791 8.91207 4.52391 9.02407C4.25991 9.13607 4.01191 9.18607 3.77991 9.17407C3.54791 9.16207 3.33191 9.10007 3.13191 8.98807C2.93191 8.87607 2.74791 8.74007 2.57991 8.58007C2.44391 8.45207 2.32191 8.28807 2.21391 8.08807C2.10591 7.88807 2.04391 7.66807 2.02791 7.42807C2.01191 7.18807 2.05391 6.93207 2.15391 6.66007C2.25391 6.38807 2.44791 6.11207 2.73591 5.83207C2.87991 5.68807 3.00791 5.55607 3.11991 5.43607C3.21591 5.33207 3.30591 5.24007 3.38991 5.16007C3.47391 5.08007 3.51991 5.03607 3.52791 5.02807L6.87591 1.69207C7.16391 1.40407 7.47791 1.17807 7.81791 1.01407C8.15791 0.850074 8.50391 0.756074 8.85591 0.732074C9.20791 0.708074 9.55391 0.756074 9.89391 0.876074C10.2339 0.996074 10.5479 1.20007 10.8359 1.48807Z" fill="#1D6FE9"/>
+</g>
+<defs>
+<clipPath id="clip0_126_1748">
+<rect width="12" height="12" fill="white"/>
+</clipPath>
+</defs>
+</svg>

--
Gitblit v1.9.3