From 8049ae0a3c7ea0cf8e3fe0b4adf32f98439b8591 Mon Sep 17 00:00:00 2001
From: 罗广辉 <guanghui.luo@foxmail.com>
Date: Fri, 16 Jan 2026 14:48:17 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master'

---
 applications/task-work-order/src/utils/cesium/DrawPolygon.js |  301 ++++++++++++++++++++++++++++++++++++++++---------
 1 files changed, 243 insertions(+), 58 deletions(-)

diff --git a/applications/task-work-order/src/views/layerManagement/components/utils.js b/applications/task-work-order/src/utils/cesium/DrawPolygon.js
similarity index 65%
rename from applications/task-work-order/src/views/layerManagement/components/utils.js
rename to applications/task-work-order/src/utils/cesium/DrawPolygon.js
index 7001570..6c37a35 100644
--- a/applications/task-work-order/src/views/layerManagement/components/utils.js
+++ b/applications/task-work-order/src/utils/cesium/DrawPolygon.js
@@ -1,7 +1,8 @@
 import * as Cesium from 'cesium'
-import _, { cloneDeep, throttle } from 'lodash'
 import * as turf from '@turf/turf'
 import { boxTransformScale } from '@/utils/turfFunc'
+import { ElMessage } from 'element-plus'
+import { flyVisual, getPointPositionsHeight } from '@/utils/cesium/mapUtil'
 
 /**
  * 多边形绘制与编辑工具类
@@ -14,8 +15,6 @@
  */
 export class DrawPolygon {
 	constructor() {
-		// 是否绘制
-		this.whetherToDraw =false
 		// 图斑预览模式标记
 		this.isPureSpotPreview = false
 		// 是否删除测区
@@ -39,6 +38,8 @@
 		// 存储端点的 DataSource
 		this.editPolygonDataSource = null
 		this.editPolygonPointDataSource = null
+		// 存储中点(边中点)的 DataSource:用于编辑态插入新端点
+		this.editPolygonMidPointDataSource = null
 		// 鼠标事件处理器
 		this.handler = null
 		// 右键菜单 DOM
@@ -63,15 +64,17 @@
 		this.delPoint = this.delPoint.bind(this)
 		// 外部订阅者
 		this.listeners = []
-		
 	}
 
 	// 实体命名常量
 	static ENTITY_NAMES = {
 		POLYGON: '区域-面',
 		POINT: '区域-端点',
+		// 编辑态中点(用于快速插入新端点)
+		MID_POINT: '区域-中点',
 		POLYGON_ID: 'planar-route-polygon',
 		POINT_ID_PREFIX: 'planar-route-point',
+		MID_POINT_ID_PREFIX: 'planar-route-mid-point',
 	}
 
 	// 颜色常量
@@ -98,10 +101,20 @@
 	// 编辑图斑
 	editThePatch(data) {
 		this.isPreviewMode = data
-	}
-	// 绘制
-	drawTheArea(data){
-		this.whetherToDraw = data		
+		// 关闭编辑能力时隐藏端点/中点,并清空中点,避免残留交互
+		if (!this.isPreviewMode) {
+			this.editPolygonPointDataSource && (this.editPolygonPointDataSource.entities.show = false)
+			this.editPolygonMidPointDataSource && (this.editPolygonMidPointDataSource.entities.show = false)
+			this.editPolygonMidPointDataSource?.entities?.removeAll?.()
+			return
+		}
+
+		if (this.editingMode) {
+			this.editPolygonPointDataSource && (this.editPolygonPointDataSource.entities.show = true)
+			this.editPolygonMidPointDataSource && (this.editPolygonMidPointDataSource.entities.show = true)
+			this.rebuildEditPoints()
+			this.rebuildMidPoints()
+		}
 	}
 	// 删除测区
 	deleteTheArea(data) {
@@ -110,12 +123,6 @@
 
 	// 开始绘制
 	startDrawing() {
-		if (!this.whetherToDraw) {
-		
-			return;
-		  }
-		 
-		  
 		this.drawingMode = true
 		this.curPolygon = new Cesium.PolygonHierarchy()
 
@@ -130,9 +137,16 @@
 			this.viewer?.dataSources.add(this.editPolygonPointDataSource)
 		}
 
+		// 中点数据源:编辑模式下用于“边上插点”
+		if (!this.editPolygonMidPointDataSource) {
+			this.editPolygonMidPointDataSource = new Cesium.CustomDataSource('editPolygonMidPointDataSource')
+			this.viewer?.dataSources.add(this.editPolygonMidPointDataSource)
+		}
+
 		// 清空之前的点
 		this.editPolygonDataSource?.entities.removeAll()
 		this.editPolygonPointDataSource?.entities.removeAll()
+		this.editPolygonMidPointDataSource?.entities.removeAll()
 	}
 
 	// 创建多边形实体(含边界线)
@@ -177,10 +191,162 @@
 				ind: pointIndex, // 索引记录
 			},
 		})
-	
-		
 	}
 
+	createMidPointEntity(startInd, position) {
+		// 使用 CallbackProperty 让中点位置随端点拖拽实时计算
+		// startInd 表示该中点属于边:(startInd) -> (startInd + 1)
+		const updatePosition = () => {
+			const positions = this.curPolygon?.positions
+			if (!positions || positions.length < 2) return position
+
+			const n = positions.length
+			const p1 = positions[startInd]
+			const p2 = positions[(startInd + 1) % n]
+			if (!p1 || !p2) return position
+
+			return Cesium.Cartesian3.midpoint(p1, p2, new Cesium.Cartesian3())
+		}
+
+		this.editPolygonMidPointDataSource.entities.add({
+			name: DrawPolygon.ENTITY_NAMES.MID_POINT,
+			id: `${DrawPolygon.ENTITY_NAMES.MID_POINT_ID_PREFIX}${startInd}`,
+			position: new Cesium.CallbackProperty(updatePosition, false),
+			point: {
+				pixelSize: 10,
+				color: Cesium.Color.fromCssColorString('rgba(255, 255, 255, .7)'),
+				heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
+				disableDepthTestDistance: Number.POSITIVE_INFINITY,
+			},
+			customData: {
+				startInd,
+			},
+		})
+	}
+
+	rebuildEditPoints() {
+		// 插入/删除端点后:端点实体 id 与 customData.ind 需要全量重建,保证索引与 positions 一致
+		if (!this.isPreviewMode) return
+		if (!this.editPolygonPointDataSource || !this.curPolygon?.positions) return
+
+		this.editPolygonPointDataSource.entities.removeAll()
+		this.curPolygon.positions.forEach((position, index) => {
+			this.editPolygonPointDataSource.entities.add({
+				name: DrawPolygon.ENTITY_NAMES.POINT,
+				id: `${DrawPolygon.ENTITY_NAMES.POINT_ID_PREFIX}${index}`,
+				position: position.clone(),
+				point: {
+					pixelSize: 14,
+					color: Cesium.Color.WHITE,
+					heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
+					disableDepthTestDistance: Number.POSITIVE_INFINITY,
+				},
+				customData: {
+					ind: index,
+				},
+			})
+		})
+	}
+
+	rebuildMidPoints() {
+		// 仅在“顶点数量变化/切换编辑态”时维护中点实体数量
+		// 拖拽过程中中点位置由 CallbackProperty 自动更新,不需要重建
+		if (!this.isPreviewMode) return
+		if (!this.editPolygonMidPointDataSource || !this.curPolygon?.positions) return
+
+		const positions = this.curPolygon.positions
+		const n = positions.length
+		const entities = this.editPolygonMidPointDataSource.entities
+
+		if (!this.editingMode || n < 2) {
+			entities.removeAll()
+			return
+		}
+
+		const neededIds = new Set()
+		for (let i = 0; i < n; i += 1) {
+			const id = `${DrawPolygon.ENTITY_NAMES.MID_POINT_ID_PREFIX}${i}`
+			neededIds.add(id)
+			// 缺少则补齐:保证每条边都有一个中点实体
+			if (!entities.getById(id)) {
+				const p1 = positions[i]
+				const p2 = positions[(i + 1) % n]
+				if (!p1 || !p2) continue
+				const mid = Cesium.Cartesian3.midpoint(p1, p2, new Cesium.Cartesian3())
+				this.createMidPointEntity(i, mid)
+			}
+		}
+
+		entities.values.slice().forEach(entity => {
+			if (entity?.name !== DrawPolygon.ENTITY_NAMES.MID_POINT) return
+			// 多余则移除:例如删除端点导致边数量减少
+			if (!neededIds.has(entity.id)) {
+				entities.remove(entity)
+			}
+		})
+	}
+
+	insertPointFromMidPoint(midPointEntity) {
+		// 点击中点:在对应边上插入一个新端点(白点),并立即进入拖拽态
+		if (!this.isPreviewMode) return
+		if (!this.editingMode) return
+		if (!midPointEntity?.customData) return
+
+		const startInd = midPointEntity.customData.startInd
+		// 中点位置可能是 CallbackProperty,需要取当前时刻的值
+		const positionProperty = midPointEntity.position
+		const entityPosition = positionProperty?.getValue
+			? positionProperty.getValue(Cesium.JulianDate.now())
+			: positionProperty
+		if (!entityPosition) return
+
+		// 插入到 startInd 与 startInd+1 之间
+		const insertIndex = Math.min(Math.max(startInd + 1, 0), this.curPolygon.positions.length)
+		this.curPolygon.positions.splice(insertIndex, 0, entityPosition.clone ? entityPosition.clone() : entityPosition)
+
+		this.rebuildEditPoints()
+		this.rebuildMidPoints()
+
+		const newPointEntity = this.editPolygonPointDataSource?.entities?.getById(
+			`${DrawPolygon.ENTITY_NAMES.POINT_ID_PREFIX}${insertIndex}`
+		)
+		if (newPointEntity) {
+			this.isDragging = true
+			this.draggedEntity = newPointEntity
+			this.currentDragPointPosition = this.curPolygon.positions[insertIndex]
+			this.disableMapControl()
+		}
+		this.notify('getPolygonPositions', this.curPolygon.positions)
+	}
+	// 清除不在范围内的点
+	removeLastInvalidPoint() {
+		const posLen = this.curPolygon.positions.length
+		if (posLen === 0) return
+
+		let targetPointId = ''
+		let removeCount = 0
+
+		if (posLen === 2) {
+			removeCount = 2
+			targetPointId = `${DrawPolygon.ENTITY_NAMES.POINT_ID_PREFIX}0`
+		} else if (posLen > 2) {
+			removeCount = 1
+			const pointIndex = posLen - 2
+			targetPointId = `${DrawPolygon.ENTITY_NAMES.POINT_ID_PREFIX}${pointIndex}`
+		}
+
+		this.curPolygon.positions.splice(posLen - removeCount, removeCount)
+
+		const invalidPoint = this.editPolygonPointDataSource.entities.getById(targetPointId)
+		if (invalidPoint) {
+			this.editPolygonPointDataSource.entities.remove(invalidPoint)
+		}
+
+		if (this.curPolygon.positions.length === 0 && this.polygonEntity) {
+			this.editPolygonDataSource.entities.remove(this.polygonEntity)
+			this.polygonEntity = null
+		}
+	}
 	// 添加一个点
 	addPosition(position, isAdd = true) {
 		// 第一个点要重复压入一次,形成动态绘制效果
@@ -211,12 +377,18 @@
 
 		const pickedEntity = this.viewer.scene.pick(movement.position)?.id
 		const isPoint = pickedEntity?.name === DrawPolygon.ENTITY_NAMES.POINT
+		const isMidPoint = pickedEntity?.name === DrawPolygon.ENTITY_NAMES.MID_POINT
 
 		if (pickedEntity && isPoint) {
 			this.isDragging = true
 			this.draggedEntity = pickedEntity
 			this.currentDragPointPosition = this.curPolygon.positions[this.draggedEntity?.customData.ind]
 			this.disableMapControl() // 禁止地图交互
+			return
+		}
+
+		if (pickedEntity && isMidPoint) {
+			this.insertPointFromMidPoint(pickedEntity)
 		}
 	}
 
@@ -249,6 +421,7 @@
 			this.isDragging = false
 			this.draggedEntity = null
 			this.enableMapControl()
+			this.rebuildMidPoints()
 		}
 	}
 
@@ -262,7 +435,7 @@
 		// 编辑模式下,拖拽点实时更新
 		if (this.editingMode && this.draggedEntity?.customData) {
 			this.draggedEntity.position = cartesian
-			this.curPolygon.positions[this.draggedEntity?.customData.ind] = cartesian			
+			this.curPolygon.positions[this.draggedEntity?.customData.ind] = cartesian
 		}
 
 		// 绘制模式下,实时更新最后一个点
@@ -293,27 +466,28 @@
 	// 鼠标左键点击
 	handleLeftClick(click) {
 		this.removeMenuPopup()
+
 		const pickedAllEntity = this.viewer.scene.drillPick(click.position).filter(i => i.id)
+		const isStartPoint = pickedAllEntity.find(i => i.id.name === '起飞点')
 		const isPolygonPoint = pickedAllEntity.find(i => i.id.name === DrawPolygon.ENTITY_NAMES.POINT)
 		const isPolygon = pickedAllEntity.find(i => i.id.name === DrawPolygon.ENTITY_NAMES.POLYGON)
-		if (this.drawingMode && !this.whetherToDraw) {
-		
-			return; // 阻断后续绘制逻辑
-		  }
+
+		if (isStartPoint) return
+
 		// 如果不是绘制模式
 		if (!this.drawingMode) {
 			if (!isPolygon) {
-				
-				
 				this.editingMode = false
 				this.editPolygonPointDataSource.entities.show = false
+				this.editPolygonMidPointDataSource && (this.editPolygonMidPointDataSource.entities.show = false)
 				return
 			}
 
 			if (isPolygon) {
-			
 				this.editingMode = true
 				this.editPolygonPointDataSource.entities.show = true
+				this.editPolygonMidPointDataSource && (this.editPolygonMidPointDataSource.entities.show = true)
+				this.rebuildMidPoints()
 			}
 			return
 		}
@@ -334,13 +508,22 @@
 		const cartesian = this.viewer.scene.pickPosition(click.position)
 		if (!cartesian) return
 
+		let arr = [...this.curPolygon.positions]
+		arr.pop()
+		// 校验多边形是否自交
+		if (arr.length > 2 && !this.curDragPointIsValid([...arr, cartesian])) {
+			ElMessage.warning('测区不支持交叉绘制,请重新绘制')
+			return
+		}
+
 		this.addPosition(cartesian)
 	}
 
 	// 鼠标右键点击(弹出菜单)
-	handleRightClick(click) {		
+	handleRightClick(click) {
 		const that = this
 		if (that.drawingMode) return
+
 		that.removeMenuPopup()
 
 		const pickedAllEntity = that.viewer.scene.drillPick(click.position).filter(i => i.id)
@@ -351,16 +534,14 @@
 		} = click
 
 		let pickedEntity, tooltipEvent, menuType
-		if (isPolygon) {
-			pickedEntity = isPolygon
-			tooltipEvent = that.delPolygon
-			menuType = 'polygon'
-		
-		} else if (isEditPoint) {
+		if (isEditPoint) {
 			pickedEntity = isEditPoint
 			tooltipEvent = that.delPoint
 			menuType = 'edit-point'
-			
+		} else if (isPolygon) {
+			pickedEntity = isPolygon
+			tooltipEvent = that.delPolygon
+			menuType = 'polygon'
 		}
 
 		if (pickedEntity && this.isDeleteTheArea) {
@@ -385,6 +566,11 @@
 			this.editPolygonPointDataSource.entities.removeAll()
 			this.editPolygonPointDataSource = null
 		}
+
+		if (this.editPolygonMidPointDataSource) {
+			this.editPolygonMidPointDataSource.entities.removeAll()
+			this.editPolygonMidPointDataSource = null
+		}
 		this.editingMode = false
 		this.polygonEntity = null
 		this.curPolygon = null
@@ -398,6 +584,8 @@
 			this.drawingMode = false
 			this.editingMode = true
 			this.editPolygonPointDataSource.entities.show = true
+			this.editPolygonMidPointDataSource && (this.editPolygonMidPointDataSource.entities.show = true)
+			this.rebuildMidPoints()
 
 			if (!this.curDragPointIsValid(this.curPolygon.positions)) {
 				this.isShowWaringTip = true
@@ -421,7 +609,12 @@
 		this.notify('getPolygonPositions', [])
 		this.startDrawing()
 	}
-
+	// 删除图斑
+	delSpot() {
+		this.removeEntities()
+		this.isPreviewMode = true
+		this.startDrawing()
+	}
 	// 删除端点
 	delPoint() {
 		if (this.curPolygon.positions.length <= 3) {
@@ -431,13 +624,9 @@
 		if (!this.delPolygonPoint) return
 
 		this.curPolygon.positions.splice(this.delPolygonPoint.customData.ind, 1)
-		this.editPolygonPointDataSource.entities.remove(this.delPolygonPoint)
 		this.removeMenuPopup()
-
-		// 更新剩余点索引
-		this.editPolygonPointDataSource.entities.values.forEach((item, index) => {
-			item.customData.ind = index
-		})
+		this.rebuildEditPoints()
+		this.rebuildMidPoints()
 
 		this.notify('getPolygonPositions', this.curPolygon.positions)
 	}
@@ -454,7 +643,6 @@
 		menuPopup.id = 'planarPolygonEditMenu'
 		menuPopup.className = 'planar-polygon-edit-menu'
 
-
 		const menuItems =
 			type === 'polygon'
 				? [{ title: '删除测区', class: 'del-planar-polygon' }]
@@ -466,7 +654,6 @@
 			titleDiv.className = item.class
 			menuPopup.appendChild(titleDiv)
 		})
-	
 		this.isPreviewMode = true
 		menuPopupVBox.appendChild(menuPopup)
 		return menuPopupVBox
@@ -522,23 +709,18 @@
 
 		return intersections.features.length === 0
 	}
-
 	// 初始化已有多边形
-	initPolygon(viewer, positions, isPurePreview = false) {
+	async initPolygon(viewer, positions, isPurePreview = false) {
 		this.initHandler(viewer)
-	
-		if (this.whetherToDraw) {
-			this.startDrawing();
-		  } else {
-			// 新增:不允许绘制时,确保绘制模式为 false,避免误触发
-			this.drawingMode = false;
-		
-		  }
-		
+		this.isPureSpotPreview = isPurePreview
+		this.startDrawing()
 		let newPosition = positions.map(item => {
-			return Cesium.Cartesian3.fromDegrees(Number(item.lng), Number(item.lat), Number(item.height))
+			return Cesium.Cartesian3.fromDegrees(Number(item.lng), Number(item.lat), Number(item?.height || 0))
 		})
-		
+		// 预览航线的时候调用
+		if (!this.isPureSpotPreview) {
+			this.notify('getPolygonPositions', newPosition)
+		}
 
 		newPosition.forEach(item => {
 			this.addPosition(item, false)
@@ -555,17 +737,20 @@
 			duration: 0.5,
 		})
 
+		let pointList = await getPointPositionsHeight(positions, viewer)
+		flyVisual({ positionsData: pointList.map(item => [item.lng, item.lat, item.ASL]), viewer })
+
 		this.drawingMode = false
 		this.editingMode = true
+		this.editPolygonPointDataSource && (this.editPolygonPointDataSource.entities.show = true)
+		this.editPolygonMidPointDataSource && (this.editPolygonMidPointDataSource.entities.show = true)
+		this.rebuildMidPoints()
 	}
 
 	// 初始化事件处理器
 	initHandler(viewer) {
 		this.viewer = viewer
-
-		if(this.whetherToDraw){
-			this.startDrawing()
-		}
+		this.startDrawing()
 
 		if (!this.handler) {
 			this.handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)

--
Gitblit v1.9.3