From efb3b5e84ed4493f82f30ed3c3dcdff0fd5a8083 Mon Sep 17 00:00:00 2001
From: shuishen <1109946754@qq.com>
Date: Fri, 16 Jan 2026 10:55:55 +0800
Subject: [PATCH] feat:任务工单后台地图

---
 applications/task-work-order/src/utils/cesium/DrawPolygon.js                 |  803 +++++++++++
 applications/task-work-order/src/utils/cesium/createRouteLine.js             |  923 ++++++++-----
 applications/task-work-order/src/utils/cesium/frustum/CesiumVideoFrustum.js  |  712 ++++++++++
 applications/task-work-order/src/utils/cesium/use-kmz-tsa.js                 |  675 +++++++++
 applications/task-work-order/src/utils/cesium/mapUtil.js                     |  207 ++
 applications/task-work-order/src/utils/cesium/compressImage.js               |   55 
 /dev/null                                                                    |    5 
 applications/task-work-order/src/utils/cesium/kmz.js                         |   10 
 applications/task-work-order/src/utils/cesium/publicCesium.js                |  168 +
 applications/task-work-order/src/utils/cesium/useBoundary.js                 |  210 ++
 applications/task-work-order/src/utils/cesium/common.js                      |    8 
 applications/task-work-order/src/utils/cesium/frustum/CoordinateTranslate.js |  229 +++
 applications/task-work-order/src/utils/cesium/Material/index.js              |   18 
 applications/task-work-order/src/utils/cesium/compressImage2.js              |   46 
 14 files changed, 3,550 insertions(+), 519 deletions(-)

diff --git a/applications/task-work-order/src/utils/cesium/DrawPolygon.js b/applications/task-work-order/src/utils/cesium/DrawPolygon.js
new file mode 100644
index 0000000..6c37a35
--- /dev/null
+++ b/applications/task-work-order/src/utils/cesium/DrawPolygon.js
@@ -0,0 +1,803 @@
+import * as Cesium from 'cesium'
+import * as turf from '@turf/turf'
+import { boxTransformScale } from '@/utils/turfFunc'
+import { ElMessage } from 'element-plus'
+import { flyVisual, getPointPositionsHeight } from '@/utils/cesium/mapUtil'
+
+/**
+ * 多边形绘制与编辑工具类
+ * 功能:
+ *  - 绘制多边形
+ *  - 拖动编辑端点
+ *  - 删除端点、删除整个多边形
+ *  - 多边形自交检查(避免非法几何)
+ *  - 外部订阅/通知机制
+ */
+export class DrawPolygon {
+	constructor() {
+		// 图斑预览模式标记
+		this.isPureSpotPreview = false
+		// 是否删除测区
+		this.isDeleteTheArea = true
+		//是否可以编辑图斑
+		this.isPreviewMode = true
+		// Cesium 视图对象
+		this.viewer = null
+		// 当前绘制的多边形
+		this.curPolygon = null
+		// 绘制模式标识
+		this.drawingMode = false
+		// 编辑模式标识
+		this.editingMode = false
+		// 是否正在拖拽端点
+		this.isDragging = false
+		// 当前拖拽的点实体
+		this.draggedEntity = null
+		// 多边形实体
+		this.polygonEntity = null
+		// 存储端点的 DataSource
+		this.editPolygonDataSource = null
+		this.editPolygonPointDataSource = null
+		// 存储中点(边中点)的 DataSource:用于编辑态插入新端点
+		this.editPolygonMidPointDataSource = null
+		// 鼠标事件处理器
+		this.handler = null
+		// 右键菜单 DOM
+		this.menuPopup = null
+		// 被右键选中的点
+		this.delPolygonPoint = null
+		// 是否显示警告提示(自交)
+		this.isShowWaringTip = false
+		// 当前拖拽点是否合法
+		this.currentDragPointIsValid = false
+		// 当前拖拽点的坐标
+		this.currentDragPointPosition = null
+
+		// 事件回调函数绑定 this
+		this.handleLeftDown = this.handleLeftDown.bind(this)
+		this.handleLeftUp = this.handleLeftUp.bind(this)
+		this.handleMouseMove = this.handleMouseMove.bind(this)
+		this.handleLeftClick = this.handleLeftClick.bind(this)
+		this.handleRightClick = this.handleRightClick.bind(this)
+
+		this.delPolygon = this.delPolygon.bind(this)
+		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',
+	}
+
+	// 颜色常量
+	static COLORS = {
+		DEFAULT_POLYGON: Cesium.Color.fromBytes(45, 140, 240, 99), // 默认面颜色
+		DEFAULT_LINE: Cesium.Color.fromBytes(45, 140, 240, 255), // 默认边界线颜色
+		ERROR_POLYGON: Cesium.Color.fromCssColorString('rgba(255, 0, 0, .3)'), // 错误(自交)面颜色
+		ERROR_LINE: Cesium.Color.fromCssColorString('rgba(255, 0, 0, 1)'), // 错误(自交)线颜色
+	}
+
+	// ============ 发布订阅机制 ============
+
+	// 外部订阅数据变化
+	subscribe(key, listener) {
+		this.listeners.push({ key, listener })
+	}
+
+	// 通知订阅者
+	notify(key, data) {
+		this.listeners.filter(subscriber => subscriber.key === key).forEach(subscriber => subscriber.listener(data))
+	}
+
+	// ============ 绘制相关 ============
+	// 编辑图斑
+	editThePatch(data) {
+		this.isPreviewMode = 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) {
+		this.isDeleteTheArea = data
+	}
+
+	// 开始绘制
+	startDrawing() {
+		this.drawingMode = true
+		this.curPolygon = new Cesium.PolygonHierarchy()
+
+		// 如果还没有 DataSource,就新建一个
+		if (!this.editPolygonDataSource) {
+			this.editPolygonDataSource = new Cesium.CustomDataSource('editPolygonDataSource')
+			this.viewer?.dataSources.add(this.editPolygonDataSource)
+		}
+
+		if (!this.editPolygonPointDataSource) {
+			this.editPolygonPointDataSource = new Cesium.CustomDataSource('editPolygonPointDataSource')
+			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()
+	}
+
+	// 创建多边形实体(含边界线)
+	createPolygonEntity() {
+		this.polygonEntity = this.editPolygonDataSource.entities.add({
+			name: DrawPolygon.ENTITY_NAMES.POLYGON,
+			id: DrawPolygon.ENTITY_NAMES.POLYGON_ID,
+			polygon: {
+				hierarchy: new Cesium.CallbackProperty(() => this.curPolygon, false),
+				material: DrawPolygon.COLORS.DEFAULT_POLYGON,
+				outline: false,
+				outlineWidth: 2,
+				heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
+			},
+			polyline: {
+				width: 2,
+				material: DrawPolygon.COLORS.DEFAULT_LINE,
+				clampToGround: true,
+				positions: new Cesium.CallbackProperty(
+					() => [...this.curPolygon.positions, this.curPolygon.positions[0]], // 闭合线
+					false
+				),
+			},
+		})
+	}
+
+	// 创建端点实体
+	createPointEntity(position, isAdd) {
+		const pointIndex = isAdd ? this.curPolygon.positions.length - 2 : this.curPolygon.positions.length - 1
+
+		this.editPolygonPointDataSource.entities.add({
+			name: DrawPolygon.ENTITY_NAMES.POINT,
+			id: `${DrawPolygon.ENTITY_NAMES.POINT_ID_PREFIX}${pointIndex}`,
+			position: position.clone(),
+			point: {
+				pixelSize: 14,
+				color: Cesium.Color.WHITE,
+				heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
+				disableDepthTestDistance: Number.POSITIVE_INFINITY,
+			},
+			customData: {
+				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) {
+		// 第一个点要重复压入一次,形成动态绘制效果
+		if (this.curPolygon.positions.length === 0 && isAdd) {
+			this.curPolygon.positions.push(position.clone())
+		}
+
+		this.curPolygon.positions.push(position.clone())
+
+		// 如果没有实体则创建
+		if (!this.polygonEntity) {
+			this.createPolygonEntity()
+		}
+
+		// 创建端点实体
+		if (this.isPreviewMode) {
+			this.createPointEntity(position, isAdd)
+		}
+
+		this.notify('getPoints', position)
+	}
+
+	// ============ 鼠标事件 ============
+
+	// 鼠标左键按下(选中端点拖动)
+	handleLeftDown(movement) {
+		if (!this.editingMode) return
+
+		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)
+		}
+	}
+
+	// 鼠标左键抬起(拖拽结束)
+	handleLeftUp() {
+		if (!(this.editingMode && this.curPolygon?.positions && this.draggedEntity)) return
+
+		if (this.currentDragPointIsValid && this.isDragging) {
+			// 更新点位置
+			this.draggedEntity.position = this.currentDragPointPosition
+			this.curPolygon.positions[this.draggedEntity?.customData.ind] = this.currentDragPointPosition
+
+			// 校验多边形是否自交
+			if (!this.curDragPointIsValid(this.curPolygon.positions)) {
+				this.isShowWaringTip = true
+				this.currentDragPointIsValid = true
+				this.updatePolygonAppearance(DrawPolygon.COLORS.ERROR_POLYGON, DrawPolygon.COLORS.ERROR_LINE)
+			} else {
+				this.isShowWaringTip = false
+				this.currentDragPointIsValid = false
+				this.updatePolygonAppearance(DrawPolygon.COLORS.DEFAULT_POLYGON, DrawPolygon.COLORS.DEFAULT_LINE)
+			}
+
+			this.notify('getShowWaringTip', this.isShowWaringTip)
+		}
+
+		// 拖拽结束,恢复交互
+		if (this.isDragging) {
+			this.notify('getPolygonPositions', this.curPolygon.positions)
+			this.isDragging = false
+			this.draggedEntity = null
+			this.enableMapControl()
+			this.rebuildMidPoints()
+		}
+	}
+
+	// 鼠标移动
+	handleMouseMove(movement) {
+		if (!this.drawingMode && !this.editingMode) return
+
+		const cartesian = this.viewer.scene.pickPosition(movement.endPosition)
+		if (!cartesian) return
+
+		// 编辑模式下,拖拽点实时更新
+		if (this.editingMode && this.draggedEntity?.customData) {
+			this.draggedEntity.position = cartesian
+			this.curPolygon.positions[this.draggedEntity?.customData.ind] = cartesian
+		}
+
+		// 绘制模式下,实时更新最后一个点
+		if (this.drawingMode && this.polygonEntity && this.curPolygon.positions.length >= 1) {
+			this.curPolygon.positions.pop()
+			this.curPolygon.positions.push(cartesian)
+		}
+
+		// 实时检查是否自交
+		if (
+			(this.editingMode && this.draggedEntity?.customData) ||
+			(this.drawingMode && this.polygonEntity && this.curPolygon.positions.length >= 3)
+		) {
+			if (!this.curDragPointIsValid(this.curPolygon.positions)) {
+				this.isShowWaringTip = true
+				this.currentDragPointIsValid = true
+				this.updatePolygonAppearance(DrawPolygon.COLORS.ERROR_POLYGON, DrawPolygon.COLORS.ERROR_LINE)
+			} else {
+				this.isShowWaringTip = false
+				this.currentDragPointIsValid = false
+				this.updatePolygonAppearance(DrawPolygon.COLORS.DEFAULT_POLYGON, DrawPolygon.COLORS.DEFAULT_LINE)
+			}
+
+			this.notify('getShowWaringTip', this.isShowWaringTip)
+		}
+	}
+
+	// 鼠标左键点击
+	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 (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
+		}
+
+		// 点击闭合多边形
+		if (this.curPolygon.positions.length < 4 && isPolygonPoint) {
+			return
+		}
+
+		if (this.curPolygon.positions.length >= 4 && isPolygonPoint) {
+			if (!this.drawingMode) return
+
+			this.finishDrawing()
+			return
+		}
+
+		// 添加新的点
+		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) {
+		const that = this
+		if (that.drawingMode) return
+
+		that.removeMenuPopup()
+
+		const pickedAllEntity = that.viewer.scene.drillPick(click.position).filter(i => i.id)
+		const isPolygon = pickedAllEntity.find(i => i.id.name === DrawPolygon.ENTITY_NAMES.POLYGON)
+		const isEditPoint = pickedAllEntity.find(i => i.id.name === DrawPolygon.ENTITY_NAMES.POINT)
+		const {
+			position: { x, y },
+		} = click
+
+		let pickedEntity, tooltipEvent, menuType
+		if (isEditPoint) {
+			pickedEntity = isEditPoint
+			tooltipEvent = that.delPoint
+			menuType = 'edit-point'
+		} else if (isPolygon) {
+			pickedEntity = isPolygon
+			tooltipEvent = that.delPolygon
+			menuType = 'polygon'
+		}
+
+		if (pickedEntity && this.isDeleteTheArea) {
+			that.delPolygonPoint = pickedEntity.id
+			that.menuPopup = that.createMenuPopup(menuType)
+			that.menuPopup.style.transform = `translate3d(${x - 10}px, ${y - 10}px, 0)`
+			that.viewer.container.appendChild(that.menuPopup)
+			that.menuPopup.addEventListener('click', tooltipEvent)
+		}
+	}
+
+	// ============ 删除相关 ============
+
+	// 删除所有实体
+	removeEntities() {
+		if (this.editPolygonDataSource) {
+			this.editPolygonDataSource.entities.removeAll()
+			this.editPolygonDataSource = null
+		}
+
+		if (this.editPolygonPointDataSource) {
+			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
+	}
+
+	// 完成绘制
+	finishDrawing() {
+		this.curPolygon.positions.pop()
+
+		if (this.curPolygon.positions.length >= 3) {
+			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
+				this.currentDragPointIsValid = true
+				this.updatePolygonAppearance(DrawPolygon.COLORS.ERROR_POLYGON, DrawPolygon.COLORS.ERROR_LINE)
+			} else {
+				this.isShowWaringTip = false
+				this.currentDragPointIsValid = false
+				this.updatePolygonAppearance(DrawPolygon.COLORS.DEFAULT_POLYGON, DrawPolygon.COLORS.DEFAULT_LINE)
+			}
+
+			this.notify('getShowWaringTip', this.isShowWaringTip)
+			this.notify('getPolygonPositions', this.curPolygon.positions)
+		}
+	}
+
+	// 删除多边形
+	delPolygon() {
+		this.removeEntities()
+		this.removeMenuPopup()
+		this.notify('getPolygonPositions', [])
+		this.startDrawing()
+	}
+	// 删除图斑
+	delSpot() {
+		this.removeEntities()
+		this.isPreviewMode = true
+		this.startDrawing()
+	}
+	// 删除端点
+	delPoint() {
+		if (this.curPolygon.positions.length <= 3) {
+			this.removeMenuPopup()
+			return ElMessage.warning('端点不可少于3个')
+		}
+		if (!this.delPolygonPoint) return
+
+		this.curPolygon.positions.splice(this.delPolygonPoint.customData.ind, 1)
+		this.removeMenuPopup()
+		this.rebuildEditPoints()
+		this.rebuildMidPoints()
+
+		this.notify('getPolygonPositions', this.curPolygon.positions)
+	}
+
+	// ============ 工具方法 ============
+
+	// 创建右键菜单
+	createMenuPopup(type = 'polygon') {
+		const menuPopupVBox = document.createElement('div')
+		menuPopupVBox.id = 'planarPolygonEdit'
+		menuPopupVBox.className = 'planar-polygon-edit-tooltip'
+
+		const menuPopup = document.createElement('div')
+		menuPopup.id = 'planarPolygonEditMenu'
+		menuPopup.className = 'planar-polygon-edit-menu'
+
+		const menuItems =
+			type === 'polygon'
+				? [{ title: '删除测区', class: 'del-planar-polygon' }]
+				: [{ title: '删除端点', class: 'del-planar-point' }]
+
+		menuItems.forEach(item => {
+			const titleDiv = document.createElement('div')
+			titleDiv.innerText = item.title
+			titleDiv.className = item.class
+			menuPopup.appendChild(titleDiv)
+		})
+		this.isPreviewMode = true
+		menuPopupVBox.appendChild(menuPopup)
+		return menuPopupVBox
+	}
+
+	// 移除菜单
+	removeMenuPopup() {
+		const that = this
+		if (that.menuPopup) {
+			that.menuPopup.removeEventListener('click', that.delPolygon)
+			that.menuPopup.removeEventListener('click', that.delPoint)
+			that.viewer.container.removeChild(that.menuPopup)
+			that.menuPopup = null
+		}
+		that.delPolygonPoint = null
+	}
+
+	// 更新多边形样式(正常/错误)
+	updatePolygonAppearance(polygonColor, lineColor) {
+		this.polygonEntity.polygon.material = polygonColor
+		this.polygonEntity.polyline.material = lineColor
+	}
+
+	// 禁用地图交互
+	disableMapControl() {
+		const controller = this.viewer.scene.screenSpaceCameraController
+		controller.enableRotate = false
+		controller.enableTranslate = false
+		controller.enableZoom = false
+	}
+
+	// 启用地图交互
+	enableMapControl() {
+		const controller = this.viewer.scene.screenSpaceCameraController
+		controller.enableRotate = true
+		controller.enableTranslate = true
+		controller.enableZoom = true
+	}
+
+	// 检查多边形是否自交
+	curDragPointIsValid(positions) {
+		if (positions.length < 3) return true
+
+		const cartographics = Cesium.Ellipsoid.WGS84.cartesianArrayToCartographicArray(positions)
+		const latLngPoints = cartographics.map(cartographic => [
+			Cesium.Math.toDegrees(cartographic.longitude),
+			Cesium.Math.toDegrees(cartographic.latitude),
+		])
+
+		// 用 turf.js 检查自交
+		const poly = turf.polygon([[...latLngPoints, latLngPoints[0]]])
+		const intersections = turf.kinks(poly)
+
+		return intersections.features.length === 0
+	}
+	// 初始化已有多边形
+	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))
+		})
+		// 预览航线的时候调用
+		if (!this.isPureSpotPreview) {
+			this.notify('getPolygonPositions', newPosition)
+		}
+
+		newPosition.forEach(item => {
+			this.addPosition(item, false)
+		})
+
+		// 视角飞入区域
+		const newBox = boxTransformScale(
+			positions.map(item => [item.lng, item.lat]),
+			5
+		)
+		viewer.camera.flyTo({
+			destination: Cesium.Rectangle.fromDegrees(...newBox),
+			offset: new Cesium.HeadingPitchRange(0, Cesium.Math.toRadians(-90), 0),
+			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
+		this.startDrawing()
+
+		if (!this.handler) {
+			this.handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
+
+			// 注册鼠标事件
+			const events = [
+				[Cesium.ScreenSpaceEventType.LEFT_DOWN, this.handleLeftDown],
+				[Cesium.ScreenSpaceEventType.LEFT_UP, this.handleLeftUp],
+				[Cesium.ScreenSpaceEventType.MOUSE_MOVE, this.handleMouseMove],
+				[Cesium.ScreenSpaceEventType.LEFT_CLICK, this.handleLeftClick],
+				[Cesium.ScreenSpaceEventType.RIGHT_CLICK, this.handleRightClick],
+			]
+
+			events.forEach(([type, handler]) => {
+				this.handler.setInputAction(handler, type)
+			})
+		}
+	}
+
+	// 移除事件处理器
+	removeHandler() {
+		if (this.handler) {
+			const eventTypes = [
+				Cesium.ScreenSpaceEventType.LEFT_DOWN,
+				Cesium.ScreenSpaceEventType.LEFT_UP,
+				Cesium.ScreenSpaceEventType.MOUSE_MOVE,
+				Cesium.ScreenSpaceEventType.LEFT_CLICK,
+				Cesium.ScreenSpaceEventType.RIGHT_CLICK,
+			]
+
+			eventTypes.forEach(type => {
+				this.handler.removeInputAction(type)
+			})
+
+			this.handler = null
+		}
+	}
+
+	/**
+	 * 销毁实例,释放资源
+	 */
+	destroy() {
+		if (!this.viewer) return
+
+		this.removeMenuPopup()
+		this.removeEntities()
+		this.removeHandler()
+		this.enableMapControl()
+	}
+}
diff --git a/applications/task-work-order/src/utils/cesium/Material/index.js b/applications/task-work-order/src/utils/cesium/Material/index.js
index f96f7a8..d5aff41 100644
--- a/applications/task-work-order/src/utils/cesium/Material/index.js
+++ b/applications/task-work-order/src/utils/cesium/Material/index.js
@@ -54,6 +54,7 @@
     this._opacity = undefined
     this._alphaPower = undefined
     this._speed = undefined
+    this._xRepeatCount = undefined
 
     this.color = options.color || Cesium.Color.fromBytes(0, 255, 255, 255)
     this.glowPower = .25
@@ -64,6 +65,9 @@
     this.opacity = options.opacity || 0.5
     this.alphaPower = options.alphaPower || 1.5
     this.speed = options.speed || 5
+    this.xRepeatCount = options.xRepeatCount || 1
+
+
   }
 
   get isConstant () {
@@ -132,15 +136,17 @@
       color: new Cesium.Color(1.0, 1.0, 0.0, 0.7),
       bgColor: new Cesium.Color(0.0, 1.0, 0.0, 0.0),
       speed: 5,
-      globalAlpha: 1.0
+      globalAlpha: 1.0,
+      xRepeatCount: 1.0
     },
     source: `
           uniform vec4 bgColor;
           uniform vec4 color;
           uniform float speed;
           uniform float globalAlpha;
+          uniform float xRepeatCount; // 新增的 uniform 在着色器中
 
-          czm_material czm_getMaterial(czm_materialInput materialInput) {
+           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);
@@ -150,7 +156,7 @@
                   colorMars3D = vec3(1.0);
               }
 
-              material.alpha = color.a * 1.5 * smoothstep(0.0, 1.0, fract(st.s - time));
+              material.alpha = color.a * 1.5 * smoothstep(0.0, 1.0, fract(st.s * xRepeatCount - time)); // 使用 xRepeatCount 来调整纹理重复
               material.diffuse = max(colorMars3D.rgb * material.alpha, colorMars3D.rgb);
 
               if (material.alpha < bgColor.a) {
@@ -182,6 +188,8 @@
     result.color = Cesium.Property.getValueOrUndefined(this._color, time)
     result.globalAlpha = Cesium.Property.getValueOrUndefined(this._opacity, time)
     result.speed = Cesium.Property.getValueOrUndefined(this._speed, time)
+    result.xRepeatCount = Cesium.Property.getValueOrUndefined(this._xRepeatCount, time)
+
     return result
   }
 
@@ -191,7 +199,8 @@
       (other instanceof LineTrailMaterial &&
         Cesium.Property.equals(this._color, other._color) &&
         Cesium.Property.equals(this._opacity, other._opacity) &&
-        Cesium.Property.equals(this._speed, other._speed))
+        Cesium.Property.equals(this._speed, other._speed) &&
+        Cesium.Property.equals(this._xRepeatCount, other._xRepeatCount))
     )
   }
 }
@@ -200,6 +209,7 @@
   color: Cesium.createPropertyDescriptor('color'),
   opacity: Cesium.createPropertyDescriptor('opacity'),
   speed: Cesium.createPropertyDescriptor('speed'),
+  xRepeatCount: Cesium.createPropertyDescriptor('xRepeatCount'),
 })
 
 
diff --git a/applications/task-work-order/src/utils/cesium/common.js b/applications/task-work-order/src/utils/cesium/common.js
index 0e1aefc..5c8ce3e 100644
--- a/applications/task-work-order/src/utils/cesium/common.js
+++ b/applications/task-work-order/src/utils/cesium/common.js
@@ -131,22 +131,22 @@
 	let newLat = lat
 
 	switch (direction) {
-		case 'W': // 向前(heading 方向)
+		case 'KeyW': // 向前(heading 方向)
 			newLon += moveStep * Math.sin(headingRad)
 			newLat += moveStep * Math.cos(headingRad)
 			break
 
-		case 'S': // 向后(反方向)
+		case 'KeyS': // 向后(反方向)
 			newLon -= moveStep * Math.sin(headingRad)
 			newLat -= moveStep * Math.cos(headingRad)
 			break
 
-		case 'D': // 向右(heading + 90°)
+		case 'KeyD': // 向右(heading + 90°)
 			newLon += moveStep * Math.cos(headingRad)
 			newLat -= moveStep * Math.sin(headingRad)
 			break
 
-		case 'A': // 向左(heading - 90°)
+		case 'KeyA': // 向左(heading - 90°)
 			newLon -= moveStep * Math.cos(headingRad)
 			newLat += moveStep * Math.sin(headingRad)
 			break
diff --git a/applications/task-work-order/src/utils/cesium/compressImage.js b/applications/task-work-order/src/utils/cesium/compressImage.js
new file mode 100644
index 0000000..0b09680
--- /dev/null
+++ b/applications/task-work-order/src/utils/cesium/compressImage.js
@@ -0,0 +1,55 @@
+/**
+ * @description: 图片压缩
+ * @param {*} src 图片地址
+ * @param {*} quality 压缩质量
+ * @param {*} maxWidth 最大宽度
+ * @param {*} callback 回调函数
+ * @return {*}
+ */
+function compressImage(src, quality, maxWidth, callback) {
+	const img = new Image()
+	img.setAttribute('crossOrigin', 'Anonymous')
+	img.onload = function () {
+		const canvas = document.createElement('canvas')
+		const ctx = canvas.getContext('2d')
+		let width = img.width
+		let height = img.height
+
+		if (width > maxWidth) {
+			height *= maxWidth / width
+			width = maxWidth
+		}
+
+		canvas.width = width
+		canvas.height = height
+		ctx.drawImage(img, 0, 0, width, height)
+
+		const newData = canvas.toDataURL('image/jpeg', quality)
+		// 使用atob()将base64转换为二进制字符串
+		const binaryString = window.atob(newData.split(',')[1])
+		const mimeString = newData.split(',')[0].split(':')[1].split(';')[0]
+		// 创建Blob对象
+		const array = []
+		for (let i = 0; i < binaryString.length; i++) {
+			array.push(binaryString.charCodeAt(i))
+		}
+		const blob = new Blob([new Uint8Array(array)], { type: mimeString })
+
+		callback(blob)
+	}
+	img.src = src
+
+	return src
+}
+
+export default function imageCompress(src, quality, maxWidth) {
+	// 使用例子
+	return compressImage(src, quality, maxWidth, function (blob) {
+		// 创建一个新的图片URL
+		const imageUrl = URL.createObjectURL(blob)
+		// 可以在这里使用imageUrl,比如设置为img元素的src
+		// document.getElementById('your-image-element').src = imageUrl;
+		// 当不需要这个URL的时候,释放它
+		URL.revokeObjectURL(imageUrl)
+	})
+}
diff --git a/applications/task-work-order/src/utils/cesium/compressImage2.js b/applications/task-work-order/src/utils/cesium/compressImage2.js
new file mode 100644
index 0000000..e28a427
--- /dev/null
+++ b/applications/task-work-order/src/utils/cesium/compressImage2.js
@@ -0,0 +1,46 @@
+/*
+ * @Author: GuLiMmo 2820890765@qq.com
+ * @Date: 2024-05-21 10:14:11
+ * @LastEditors: GuLiMmo 2820890765@qq.com
+ * @LastEditTime: 2024-05-21 10:20:34
+ * @FilePath: /bigScreen/src/utils/cesium/compressImage2.js
+ * @Description:
+ * Copyright (c) 2024 by GuLiMmo, All Rights Reserved.
+ */
+export default function compressImage(
+	src,
+	maxWidth,
+	maxHeight,
+	outputFormat = 'image/jpeg',
+	quality = 0.7
+) {
+	return new Promise((resolve, reject) => {
+		const image = new Image()
+		image.setAttribute('crossOrigin', 'Anonymous')
+		image.src = src
+		image.onload = () => {
+			let width = image.width
+			let height = image.height
+
+			if (width > maxWidth) {
+				height *= maxWidth / width
+				width = maxWidth
+			}
+
+			if (height > maxHeight) {
+				width *= maxHeight / height
+				height = maxHeight
+			}
+
+			const canvas = document.createElement('canvas')
+			canvas.width = width
+			canvas.height = height
+			const ctx = canvas.getContext('2d')
+			ctx.drawImage(image, 0, 0, width, height)
+
+			const newDataUrl = canvas.toDataURL(outputFormat, quality)
+			resolve(newDataUrl)
+		}
+		image.onerror = reject
+	})
+}
diff --git a/applications/task-work-order/src/utils/cesium/createRouteLine.js b/applications/task-work-order/src/utils/cesium/createRouteLine.js
index 2c8195e..6ec1c1f 100644
--- a/applications/task-work-order/src/utils/cesium/createRouteLine.js
+++ b/applications/task-work-order/src/utils/cesium/createRouteLine.js
@@ -2,17 +2,16 @@
 import { Decimal } from 'decimal.js'
 import _, { cloneDeep, throttle } from 'lodash'
 import { v4 as uuidv4 } from 'uuid'
-
 import { render } from 'vue'
-
+import { analysisPointLineKmz, handlePointListForKmz } from '@/views/newRoutePlan/Waypoint/pointWayLineUtils'
 import HistoryDronePopup from '@/components/DeviceJobDetails/HistoryDronePopup.vue'
 
-import { getNewPolygonData } from '@/views/RoutePlan/PlanarAirLine/planarRouteUtils'
+import {
+	getNewPolygonData,
+} from '@/views/newRoutePlan/routeUtils'
 
 import { analyzeKmzFile, removeTextKey, XMLToJSON } from '@/utils/cesium/kmz'
-
-import { ArrowLineMaterialProperty, LineTrailMaterial, PolylineGlowMaterial, } from '@/utils/cesium/Material'
-
+import { ArrowLineMaterialProperty, LineTrailMaterial, PolylineGlowMaterial } from '@/utils/cesium/Material'
 import CreateFrustum from '@/utils/cesium/frustum/CreateFrustum'
 import { ElMessage } from 'element-plus'
 
@@ -23,61 +22,21 @@
 import aircraftGltf from '@/assets/gltf/aircraft.gltf'
 import newNumPoint from '@/assets/images/newStartPoint.png'
 
-import {
-	getPolyLine,
-} from '@/views/RoutePlan/PointAirLine/pointWayLineUtils'
+import { getPolyLine } from '@/views/newRoutePlan/Waypoint/pointWayLineUtils'
 import store from '@/store/index'
+import newStartPoint from '@/assets/images/newStartPoint.png'
+import newStartPointMore from '@/assets/images/newStartPointMore.png'
+import {
+	arrowLineMaterialProperty, arrowLineMaterialPropertyGray, arrowLineMaterialPropertyGrayT,
+	arrowLineMaterialPropertyGreen,
+	arrowLineMaterialPropertyOrange,
+} from '@/hooks/useMorePointLine/useMorePointLine'
 
-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
-})
 
-let arrowLineMaterialPropertyOrange = new ArrowLineMaterialProperty({
-	color: new Cesium.Color(255 / 255, 185 / 255, 58 / 255, 1),
-	directionColor: new Cesium.Color(1, 1, 1, 1),
-	outlineColor: new Cesium.Color(1, 1, 1, 1),
-	outlineWidth: 0,
-	speed: 5,
-})
-
-let arrowLineMaterialPropertyGreen = new ArrowLineMaterialProperty({
-	color: new Cesium.Color(6 / 255, 217 / 255, 87 / 255, 1),
-	directionColor: new Cesium.Color(1, 1, 1, 1),
-	outlineColor: new Cesium.Color(1, 1, 1, 1),
-	outlineWidth: 0,
-	speed: 5,
-})
-
-// 记录index
-let indexLog = 0
-// 颜色
-function getLineColor(lineNumber) {
-	// 颜色顺序: 蓝(0), 黄(1), 绿(2)
-	const colors = [arrowLineMaterialProperty, arrowLineMaterialPropertyOrange, arrowLineMaterialPropertyGreen]
-	// 计算颜色索引 (从0开始)
-	const colorIndex = (lineNumber - 1) % 3
-	// 返回对应颜色
-	return colors[colorIndex]
-}
-
-let runningLineMaterial = new LineTrailMaterial({
-	color: Cesium.Color.fromCssColorString('#1FFF69'),
-	opacity: 1,
-	speed: 10,
-})
 let runningTextColor = Cesium.Color.fromCssColorString('#1FFF69')
 let runningTextOffset = new Cesium.Cartesian2(0, -32)
-
-let pendingLineMaterial = new PolylineGlowMaterial({
-	color: Cesium.Color.fromCssColorString('#FFB81D'),
-})
 let pendingTextColor = Cesium.Color.fromCssColorString('#FFB81D')
 let pendingTextOffset = new Cesium.Cartesian2(0, 32)
-
 let MapPopUpBox = HistoryDronePopup
 
 function getZoomFactor (camerasInfo, type) {
@@ -95,10 +54,108 @@
 
 	return zoom_factor
 }
+// 记录index
+let indexLog = 0
+// 颜色
+function getPendingExecutionLineColor (lineNumber) {
+	// 颜色顺序: 蓝(灰), 更灰
+	const colors = [arrowLineMaterialPropertyGray, arrowLineMaterialPropertyGrayT]
+	// 计算颜色索引 (从0开始)
+	const colorIndex = (lineNumber - 1) % 2
+	// 返回对应颜色
+	return colors[colorIndex]
+}
+
+function getLineColor (lineNumber) {
+	// 颜色顺序: 蓝(0), 黄(1), 绿(2)
+	const colors = [arrowLineMaterialProperty, arrowLineMaterialPropertyOrange, arrowLineMaterialPropertyGreen]
+	// 计算颜色索引 (从0开始)
+	const colorIndex = (lineNumber - 1) % 3
+	// 返回对应颜色
+	return colors[colorIndex]
+}
+
+function getBase64Image (imgUrl) {
+	const img = new Image()
+	const canvas = document.createElement('canvas')
+	const ctx = canvas.getContext('2d')
+
+	return new Promise(resolve => {
+		img.onload = function () {
+			canvas.width = img.width
+			canvas.height = img.height
+			ctx.drawImage(img, 0, 0)
+			const base64 = canvas.toDataURL('image/png')
+			resolve(base64)
+		}
+		img.src = imgUrl
+	})
+}
+async function createCombinedSVG (index, billboardImageUrl) {
+	const base64Image = await getBase64Image(billboardImageUrl)
+	const svg = `
+	  <svg width="100" height="100" xmlns="http://www.w3.org/2000/svg">
+		<image href="${base64Image}" x="10" y="10" width="80" height="80"/>
+		<text x="50" y="54" font-size="26" fill="white" text-anchor="middle" dominant-baseline="middle">
+		  ${index + 1}
+		</text>
+	  </svg>
+	`
+	return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`
+}
+
+function findHeightByCoordinates (data, targetLng, targetLat) {
+	// 展平二维数组,然后查找第一个匹配的点
+	const allPoints = data.flat()
+	const point = allPoints.find(point =>
+		point.longitude === targetLng && point.latitude === targetLat
+	)
+
+	return point ? point.height : null
+}
+
+function renderingMachineNest (options) {
+	const { viewer, dockTransformPosition, dockPosition, data } = options
+	viewer.entities.add({
+		name: 'work-drone-route-point-dock',
+		position: dockTransformPosition,
+		billboard: {
+			image: endingOnlineImg,
+			width: 50,
+			height: 50,
+			disableDepthTestDistance: Number.POSITIVE_INFINITY,
+		},
+		customData: {
+			data,
+		},
+	})
+	viewer.entities.add({
+		name: 'work-drone-route--point-polyline',
+		position: dockTransformPosition,
+		polyline: getPolyLine(viewer, { value: [dockPosition] }, 0),
+	})
+}
+
+function renderingStartingPoint (options) {
+	const { viewer, startPosition, data } = options
+	viewer.entities.add({
+		name: 'work-drone-route-point-start',
+		position: startPosition,
+		billboard: {
+			image: rwqfdImg,
+			width: 50,
+			height: 50,
+			disableDepthTestDistance: Number.POSITIVE_INFINITY,
+		},
+		customData: {
+			data,
+		},
+	})
+}
 
 export default class CreateRouteLine {
 	/**
-	 * 
+	 *
 	 * @param {*} options 参数
 	 */
 	constructor(options) {
@@ -119,10 +176,9 @@
 
 	/**
 	 * 初始化viewer
-	 * @param {*} viewer 
+	 * @param {*} viewer
 	 */
 	initCreateRoute (viewer) {
-		indexLog = 0
 		this.viewer = viewer
 	}
 
@@ -138,24 +194,27 @@
 			const currentWaypointIndex = output?.ext['current_waypoint_index']
 
 			this.updataMultiNestData(sn, {
-				currentWaypointIndex
+				currentWaypointIndex,
 			})
 		}
 	}
 
 	/**
-	 * 请求航线文件数据并解析处理
+	 * 请求航线文件数据并绘制解析处理
 	 * @param {*} data 包含文件url等信息
-	 * @param {*} dronePosition 无人机位置
-	 * @returns  
+	 * @param {*} dockPosition 无人机位置
+	 * @returns
 	 */
-	async parsingFiles (data, dronePosition = null, isShowDock = false, isShowPointBillboard = true) {
+	async parsingFiles (data, dockPosition = null, isShowDock = false, isShowPointBillboard = true) {
+		console.log(data,'888')
 		const { url, wayline_type, device_sn } = data
-
-		if (!url) return dronePosition != null ? {
-			dronePosition: [{ lng: dronePosition.longitude, lat: dronePosition.latitude, alt: dronePosition.height }]
-		} : {}
-
+		indexLog = 0
+		if (!url)
+			return dockPosition != null
+				? {
+					dronePosition: [{ lng: dockPosition.longitude, lat: dockPosition.latitude, alt: dockPosition.height }],
+				}
+				: {}
 		this.updataMultiNestData(device_sn)
 
 		const res = await analyzeKmzFile(`${url}?_t=${new Date().getTime()}`)
@@ -178,16 +237,16 @@
 		const missionConfig = removeTextKey(waylinesXmlJson.missionConfig)
 		const waylinesXMLObj = removeTextKey(waylinesXmlJson.Folder)
 
-		if (templateType === "mapping3d") {
+		if (templateType === 'mapping3d') {
 			if (!waylinesXMLObj[0].Placemark.length) return ElMessage.error('没有航线点位')
 		} else {
 			if (!waylinesXMLObj.Placemark.length) return ElMessage.error('没有航线点位')
 		}
 
-		if ([2, 4, 5, 6, 7, 10].includes(Number(wayline_type))) {
-			if (templateType === "mapping3d") {
+		if ([2, 4, 5, 7, 10].includes(Number(wayline_type))) {
+			if (templateType === 'mapping3d') {
 				let morePolygonData = waylinesXMLObj.map(i => ({
-					data: i.Placemark
+					data: i.Placemark,
 				}))
 
 				let newPolygonData = getNewPolygonData(morePolygonData)
@@ -214,7 +273,7 @@
 					waylinesXMLObj[0],
 					missionConfig,
 					templateXmlJson,
-					dronePosition,
+					dockPosition,
 					data,
 					startPoint,
 					isShowDock,
@@ -227,7 +286,7 @@
 				waylinesXMLObj,
 				missionConfig,
 				templateXmlJson,
-				dronePosition,
+				dockPosition,
 				data,
 				startPoint,
 				isShowDock,
@@ -238,7 +297,7 @@
 			return this.drawPointRoute(
 				waylinesXMLObj,
 				missionConfig,
-				dronePosition,
+				dockPosition,
 				data,
 				startPoint,
 				isShowDock,
@@ -248,50 +307,231 @@
 		}
 	}
 
+	// 拆分航线
+	async splitWayLine (data, dronePosition = null, isShowDock = false, isShowPointBillboard = true) {
+		const {
+			pointPlacemark,
+			polygonList,
+			pointList,
+			templateType,
+			startPoint,
+			execute_height_mode,
+			auto_flight_speed,
+			take_off_security_height,
+			buffer_distance_meters,
+			missionConfig,
+			waylinesXMLObjR,
+		} = await analysisPointLineKmz(`${data.domain_url}${data.object_key}`)
+		const { positionArray, filePositions } = this.disposeData(
+			waylinesXMLObjR,
+			missionConfig,
+			null,
+			data,
+			startPoint,
+			0
+		)
+		let bigPointList = handlePointListForKmz({
+			placemark: pointPlacemark,
+			startPoint: startPoint,
+			execute_height_mode: execute_height_mode,
+			auto_flight_speed: auto_flight_speed,
+		})
+		// 路径线
+		let entityConfig
+		bigPointList.shift()
+		let flyPositions = bigPointList.map(i => [Number(i.longitude), Number(i.latitude), Number(i.height)])
+		// 渲染多个点
+		let firstTopPosition = null
+		let resultPosotions = []
+		await Promise.all(data.wayline_file_list.map(async (item, index) => {
+			// 小航线渲染线
+			const kmzUrl = import.meta.env.VITE_APP_AIRLINE_URL + item.object_key
+			const { pointList } = await analysisPointLineKmz(kmzUrl)
+			resultPosotions.push(pointList)
+		})).then(res => {
+			// 目的是区分大航线点属于哪条小航线
+			bigPointList.forEach(bigObj => {
+				let foundIndex = -1
+				// 遍历 resultPositions 查找匹配项
+				resultPosotions.some((subArray, subIndex) => {
+					const found = subArray.some(obj =>
+						obj.latitude === bigObj.latitude &&
+						obj.longitude === bigObj.longitude
+					)
+					if (found) {
+						foundIndex = subIndex
+						return true // 找到就停止搜索
+					}
+					return false
+				})
+				// 如果找到了,给 bigPointList 的对象添加标记
+				if (foundIndex !== -1) {
+					bigObj.log = foundIndex // 或 bigObj.index = foundIndex
+				}
+			})
+			console.log(bigPointList, '查看结果值') // 现在 bigPointList 会有 log 属性
+			bigPointList.map(async (i, index) => {
+				firstTopPosition = i
+				const position = Cesium.Cartesian3.fromDegrees(Number(i.longitude), Number(i.latitude), Number(findHeightByCoordinates(resultPosotions, i.longitude, i.latitude)))
+				const combinedImage = await createCombinedSVG(index, i.log % 2 ? newStartPoint : newStartPointMore)
+
+				entityConfig = {
+					name: 'work-drone-route-point-split',
+					position: position,
+					billboard: {
+						disableDepthTestDistance: Number.POSITIVE_INFINITY,
+						image: combinedImage,
+						width: 50,
+						height: 50,
+						verticalOrigin: Cesium.VerticalOrigin.CENTER,
+						horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
+					}
+				}
+
+				// 只在第一个点添加 label
+				if (index === 0) {
+					entityConfig.label = {
+						text: data.task_name || '',
+						font: 'bold 16px sans-serif',
+						fillColor: data.status === 2 ? runningTextColor : pendingTextColor,
+						pixelOffset: data.status === 2 ? runningTextOffset : pendingTextOffset,
+						style: Cesium.LabelStyle.FILL_AND_OUTLINE,
+						outlineWidth: 2,
+						outlineColor: Cesium.Color.BLACK,
+						disableDepthTestDistance: Number.POSITIVE_INFINITY,
+					}
+				}
+
+				this.viewer.entities.add(entityConfig)
+			})
+		})
+
+		// 渲染航线
+		data.wayline_file_list.map(async (item, index) => {
+			// 小航线渲染线
+			const kmzUrl = import.meta.env.VITE_APP_AIRLINE_URL + item.object_key
+			const { pointList } = await analysisPointLineKmz(kmzUrl)
+			// if (curRouteLineData.value.droneData && splitLines.length === 1) {
+			// 	// 把pointList的第一个点替换为无人机位置
+			// 	pointList[0] = curRouteLineData.value.droneData[0]
+			// }
+			if (take_off_security_height + pointList[0].height > firstTopPosition.height) {
+				// 安全高度+机巢高度 > 第一个点高度
+				const positionTest = {
+					longitude: pointList[0].longitude,
+					latitude: pointList[0].latitude,
+					height: take_off_security_height + pointList[0].height,
+				}
+
+				const positionTest1 = {
+					longitude: pointList[1].longitude,
+					latitude: pointList[1].latitude,
+					height: take_off_security_height + pointList[0].height,
+				}
+				pointList.splice(1, 0, positionTest)
+				// 往数组中插入元素
+				pointList.splice(2, 0, positionTest1)
+				// console.log(pointList, 'pointList')
+			} else if (take_off_security_height + pointList[0].height < firstTopPosition.height) {
+				const positionTest = {
+					longitude: pointList[0].longitude,
+					latitude: pointList[0].latitude,
+					height: firstTopPosition.height,
+				}
+				pointList.splice(1, 0, positionTest)
+			}
+			const routePositions = pointList.map(i =>
+				Cesium.Cartesian3.fromDegrees(Number(i.longitude), Number(i.latitude), Number(i.height))
+			)
+
+			this.viewer.entities.add({
+				name: 'work-drone-route-line-split',
+				polyline: {
+					width: 4,
+					positions: routePositions,
+					material: data.status === 1 ? arrowLineMaterialPropertyGray : getLineColor(index + 1),
+					clampToGround: false,
+				},
+			})
+		})
+			// 落点线
+			; (bigPointList || [])?.forEach((item, index) => {
+				const topPosition = Cesium.Cartesian3.fromDegrees(
+					Number(item.longitude),
+					Number(item.latitude),
+					Number(item.height)
+				)
+				let setting = {
+					name: 'work-drone-route--planar-polyline-split',
+					position: topPosition,
+					polyline: getPolyLine(this.viewer, { value: bigPointList }, index),
+				}
+				this.viewer.entities.add(setting)
+			})
+		return {
+			entity: entityConfig,
+			routeLinePositions: positionArray,
+			filePositions:
+				dronePosition != null
+					? [{ lng: dronePosition.longitude, lat: dronePosition.latitude, alt: dronePosition.height }, ...filePositions]
+					: filePositions,
+		}
+	}
+
 	/**
 	 * 绘制面状航线
 	 * @param {*} lineObj 航线文件中的
 	 * @param {*} missionConfig 航线文件中的
 	 * @param {*} templateXmlJson 航线文件中的
-	 * @param {*} dronePosition 无人机位置
-	 * @param {*} data 
-	 * @param {*} startPoint 
-	 * @param {*} isShowPointBillboard 是否显示,航线起飞点,终点等标注
-	 * @returns 
+	 * @param {*} dockPosition 无人机位置
+	 * @param {*} data
+	 * @param {*} startPoint
+	 * @returns
 	 */
-	async drawPlanarRoute (lineObj, missionConfig, templateXmlJson, dronePosition, data, startPoint, isShowDock, isShowPointBillboard, wayline_type) {
-		const { positionArray, filePositions } = this.disposeData(lineObj, missionConfig, dronePosition, data, startPoint, wayline_type)
+	async drawPlanarRoute (
+		lineObj,
+		missionConfig,
+		templateXmlJson,
+		dockPosition,
+		data,
+		startPoint,
+		isShowDock,
+		isShowPointBillboard,
+		wayline_type
+	) {
+		const { positionArray, filePositions } = this.disposeData(
+			lineObj,
+			missionConfig,
+			dockPosition,
+			data,
+			startPoint,
+			wayline_type
+		)
 		const { device_sn } = data
 
-		const droneTransformPosition = Cesium.Cartesian3.fromDegrees(
-			Number(dronePosition.longitude),
-			Number(dronePosition.latitude),
-			Number(dronePosition.height),
+		const dockTransformPosition = Cesium.Cartesian3.fromDegrees(
+			Number(dockPosition.longitude),
+			Number(dockPosition.latitude),
+			Number(dockPosition.height)
 		)
 
 		let coordArr = null
 
 		const placemark = templateXmlJson.Folder?.Placemark
 		// 取出点位
-		const coordinates =
-			placemark.Polygon?.outerBoundaryIs.LinearRing.coordinates?.[
-				'#text'
-			]?.split('\n') || []
-
+		const coordinates = placemark.Polygon?.outerBoundaryIs.LinearRing.coordinates?.['#text']?.split('\n') || []
 		// 数组转换
-		coordArr = coordinates.map((coordinate) =>
+		coordArr = coordinates.map(coordinate =>
 			coordinate
 				.replace(/\s+/g, '')
 				.split(',')
-				.map((v) => Number(v)),
+				.map(v => Number(v))
 		)
 
 		// 获取当前经纬度绝对高度
-		let newCoordArr = coordArr.map(item => ([item[0], item[1], 0]))
-
+		let newCoordArr = coordArr.map(item => [item[0], item[1], 0])
 		let planarRouteEntity = this.viewer.entities.add({
 			name: 'work-drone-route-planar-polyline',
-
 			polyline: {
 				width: 3,
 				positions: positionArray,
@@ -300,30 +540,30 @@
 			},
 
 			customData: {
-				data
-			}
+				data,
+			},
 		})
 
 		let droppointPositions = (filePositions || [])?.map(i => ({
 			longitude: Number(i.lng),
 			latitude: Number(i.lat),
 			height: Number(i.alt),
-		}));
+		}))
 
-		// 落点线
-		(droppointPositions || [])?.forEach((item, index) => {
-			const topPosition = Cesium.Cartesian3.fromDegrees(
-				Number(item.longitude),
-				Number(item.latitude),
-				Number(item.height)
-			)
-			let setting = {
-				name: 'work-drone-route-planar-polyline',
-				position: topPosition,
-				polyline: getPolyLine(this.viewer, { value: droppointPositions }, index),
-			}
-			this.viewer.entities.add(setting)
-		})
+			// 落点线
+			; (droppointPositions || [])?.forEach((item, index) => {
+				const topPosition = Cesium.Cartesian3.fromDegrees(
+					Number(item.longitude),
+					Number(item.latitude),
+					Number(item.height)
+				)
+				let setting = {
+					name: 'work-drone-route--planar-polyline',
+					position: topPosition,
+					polyline: getPolyLine(this.viewer, { value: droppointPositions }, index),
+				}
+				this.viewer.entities.add(setting)
+			})
 
 		// 面状航线第一个点突出展示
 		isShowPointBillboard && this.startingIncreasePoint(positionArray)
@@ -340,9 +580,7 @@
 			name: 'work-drone-route-planar-border',
 
 			polyline: {
-				positions: Cesium.Cartesian3.fromDegreesArrayHeights(
-					cloneCoordArr.flat(),
-				),
+				positions: Cesium.Cartesian3.fromDegreesArrayHeights(cloneCoordArr.flat()),
 				width: 4,
 				material: new Cesium.PolylineOutlineMaterialProperty({
 					color: new Cesium.Color.fromBytes(74, 138, 233),
@@ -357,11 +595,8 @@
 		// 绘制面状地块--------------------
 		this.viewer.entities.add({
 			name: 'work-drone-route-planar-polygon',
-
 			polygon: {
-				hierarchy: Cesium.Cartesian3.fromDegreesArrayHeights(
-					newCoordArr.flat(),
-				),
+				hierarchy: Cesium.Cartesian3.fromDegreesArrayHeights(newCoordArr.flat()),
 				material: new Cesium.Color.fromBytes(75, 159, 221, 100),
 				outline: true,
 				outlineColor: new Cesium.Color.fromBytes(35, 85, 216, 255),
@@ -384,7 +619,7 @@
 
 				label: {
 					text: data.job_name || '',
-					font: 'bold 16px YouSheBiaoTiHei',
+					font: 'bold 16px sans-serif',
 					fillColor: Cesium.Color.WHITE,
 					pixelOffset: new Cesium.Cartesian2(0, -50),
 					style: Cesium.LabelStyle.FILL_AND_OUTLINE,
@@ -395,64 +630,31 @@
 			})
 		} else {
 			// 显示机巢位置
-			if (isShowDock) {
-				// 起点
-				this.viewer.entities.add({
-					name: 'work-drone-route-point-dock',
-
-					position: droneTransformPosition,
-
-					billboard: {
-						image: endingOnlineImg,
-						width: 50,
-						height: 50,
-						disableDepthTestDistance: Number.POSITIVE_INFINITY,
-					},
-
-					customData: {
-						data
-					}
-				})
-
-				this.viewer.entities.add({
-					name: 'work-drone-route--point-polyline',
-					position: droneTransformPosition,
-					polyline: getPolyLine(this.viewer, { value: [dronePosition] }, 0),
-				})
-			}
-
-			const startPosition = Cesium.Cartesian3.fromDegrees(
-				Number(filePositions[0].lng),
-				Number(filePositions[0].lat),
-				Number(filePositions[0].alt)
-			)
-
+			isShowDock && renderingMachineNest({
+				viewer: this.viewer,
+				dockTransformPosition,
+				dockPosition,
+				data
+			})
 			// 起点
-			isShowPointBillboard && this.viewer.entities.add({
-				name: 'work-drone-route-point-start',
-
-				position: startPosition,
-
-				billboard: {
-					image: rwqfdImg,
-					width: 50,
-					height: 50,
-					disableDepthTestDistance: Number.POSITIVE_INFINITY,
-				},
-
-				customData: {
-					data
-				}
+			isShowPointBillboard && renderingStartingPoint({
+				viewer: this.viewer,
+				startPosition: Cesium.Cartesian3.fromDegrees(
+					Number(filePositions[0].lng),
+					Number(filePositions[0].lat),
+					Number(filePositions[0].alt)
+				),
+				data
 			})
 		}
 
 		return {
 			entity: planarRouteEntity,
 			routeLinePositions: positionArray,
-			filePositions: dronePosition != null ? [
-				{ lng: dronePosition.longitude, lat: dronePosition.latitude, alt: dronePosition.height },
-				...filePositions
-			] : filePositions
+			filePositions:
+				dockPosition != null
+					? [{ lng: dockPosition.longitude, lat: dockPosition.latitude, alt: dockPosition.height }, ...filePositions]
+					: filePositions,
 		}
 	}
 
@@ -472,9 +674,7 @@
 
 		let setting = {
 			name: 'work-drone-route-planar-start',
-
 			position: positions,
-
 			billboard: {
 				image: this.planarBillboard('#2D8CF0'),
 				pixelOffset: new Cesium.Cartesian2(146, 72),
@@ -489,35 +689,48 @@
 	 * 绘制点航线
 	 * @param {*} lineObj 航线文件中的
 	 * @param {*} missionConfig 航线文件中的
-	 * @param {*} dronePosition 无人机位置
+	 * @param {*} dockPosition 无人机位置
 	 * @param {*} data 任务信息,航线地址等
-	 * @param {*} startPoint 
+	 * @param {*} startPoint
 	 * @param {*} isShowDock 是否显示机巢位置
-	 * @param {*} isShowPointBillboard 是否显示,航线起飞点,终点等标注
-	 * @returns 
+	 * @returns
 	 */
-	async drawPointRoute (lineObj, missionConfig, dronePosition, data, startPoint, isShowDock, isShowPointBillboard, wayline_type) {
-		const { positionArray, filePositions } = this.disposeData(lineObj, missionConfig, dronePosition, data, startPoint, wayline_type)
-		
-		const droneTransformPosition = Cesium.Cartesian3.fromDegrees(
-			Number(dronePosition.longitude),
-			Number(dronePosition.latitude),
-			Number(dronePosition.height),
+	async drawPointRoute (
+		lineObj,
+		missionConfig,
+		dockPosition,
+		data,
+		startPoint,
+		isShowDock,
+		isShowPointBillboard,
+		wayline_type
+	) {
+		const { positionArray, filePositions } = this.disposeData(
+			lineObj,
+			missionConfig,
+			dockPosition,
+			data,
+			startPoint,
+			wayline_type
 		)
 
+		const dockTransformPosition = Cesium.Cartesian3.fromDegrees(
+			Number(dockPosition.longitude),
+			Number(dockPosition.latitude),
+			Number(dockPosition.height)
+		)
 		// 路径线
 		let polyline
-		
+
+		indexLog = indexLog + 1
 
 		if (this.type === 'clusterScheduling') {
 			filePositions.forEach((item, index) => {
 				let position = Cesium.Cartesian3.fromDegrees(item.lng, item.lat, item.alt)
-
 				if (index === 0) {
 					this.viewer.entities.add({
 						name: 'work-drone-route-point-start',
 						position,
-
 						billboard: {
 							image: rwqfdImg,
 							outlineWidth: 0,
@@ -525,10 +738,9 @@
 							height: 50,
 							disableDepthTestDistance: Number.POSITIVE_INFINITY,
 						},
-
 						label: {
 							text: data.job_name || '',
-							font: 'bold 16px YouSheBiaoTiHei',
+							font: 'bold 16px sans-serif',
 							fillColor: data.type === 2 ? runningTextColor : pendingTextColor,
 							pixelOffset: data.type === 2 ? runningTextOffset : pendingTextOffset,
 							style: Cesium.LabelStyle.FILL_AND_OUTLINE,
@@ -540,9 +752,7 @@
 				} else if (index === filePositions.length - 1) {
 					this.viewer.entities.add({
 						name: 'work-drone-route-point-end',
-
 						position,
-
 						billboard: {
 							image: newEndPointImg,
 							outlineWidth: 0,
@@ -556,9 +766,7 @@
 				} else {
 					this.viewer.entities.add({
 						name: 'work-drone-route-point-approach',
-
 						position,
-
 						billboard: {
 							image: newNumPoint,
 							width: 50,
@@ -577,96 +785,56 @@
 					})
 				}
 			})
-
 			polyline = this.viewer.entities.add({
 				name: 'work-drone-route-point-polyline',
-
 				polyline: {
 					width: 4,
 					positions: positionArray,
-					material: data.type === 2 ? runningLineMaterial : pendingLineMaterial,
+					material: data.type === 2 ? getLineColor(indexLog) : arrowLineMaterialPropertyGray,
 					clampToGround: false,
-				}
+				},
 			})
 		} else {
 			// 显示机巢位置
-			if (isShowDock) {
-				// 起点
-				this.viewer.entities.add({
-					name: 'work-drone-route-point-dock',
-
-					position: droneTransformPosition,
-
-					billboard: {
-						image: endingOnlineImg,
-						width: 50,
-						height: 50,
-						disableDepthTestDistance: Number.POSITIVE_INFINITY,
-					},
-
-					customData: {
-						data
-					}
-				})
-
-				this.viewer.entities.add({
-					name: 'work-drone-route--point-polyline',
-					position: droneTransformPosition,
-					polyline: getPolyLine(this.viewer, { value: [dronePosition] }, 0),
-				})
-			}
-
-			const startPosition = Cesium.Cartesian3.fromDegrees(
-				Number(filePositions[0].lng),
-				Number(filePositions[0].lat),
-				Number(filePositions[0].alt)
-			)
-
+			isShowDock && renderingMachineNest({
+				viewer: this.viewer,
+				dockTransformPosition,
+				dockPosition
+			})
 			// 起点
-			isShowPointBillboard && this.viewer.entities.add({
-				name: 'work-drone-route-point-start',
-
-				position: startPosition,
-
-				billboard: {
-					image: rwqfdImg,
-					width: 50,
-					height: 50,
-					disableDepthTestDistance: Number.POSITIVE_INFINITY,
-				},
-
-				customData: {
-					data
-				}
+			isShowPointBillboard && renderingStartingPoint({
+				viewer: this.viewer,
+				startPosition: Cesium.Cartesian3.fromDegrees(
+					Number(filePositions[0].lng),
+					Number(filePositions[0].lat),
+					Number(filePositions[0].alt)
+				),
+				data
 			})
-
 			// 终点
-			isShowPointBillboard && this.viewer.entities.add({
-				name: 'work-drone-route-point-end',
-
-				position: positionArray[positionArray.length - 1],
-
-				billboard: {
-					image: new Cesium.ConstantProperty(endPointImg),
-					width: 30,
-					height: 30,
-					verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 底部对齐
-				},
-			})
-
+			isShowPointBillboard &&
+				this.viewer.entities.add({
+					name: 'work-drone-route-point-end',
+					position: positionArray[positionArray.length - 1],
+					billboard: {
+						image: new Cesium.ConstantProperty(endPointImg),
+						width: 30,
+						height: 30,
+						verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 底部对齐
+					},
+				})
+			console.log(data.status, '失败')
 			polyline = this.viewer.entities.add({
 				name: 'work-drone-route-point-polyline',
-
 				polyline: {
 					width: 4,
 					positions: positionArray,
-					material: getLineColor(indexLog),
+					material: data.status === 5 ? arrowLineMaterialPropertyGray : getLineColor(indexLog),
 					clampToGround: false,
 				},
-
 				customData: {
-					data
-				}
+					data,
+				},
 			})
 		}
 
@@ -674,47 +842,45 @@
 			longitude: Number(i.lng),
 			latitude: Number(i.lat),
 			height: Number(i.alt),
-		}));
+		}))
 
-		// 落点线
-		(droppointPositions || [])?.forEach((item, index) => {
-			const topPosition = Cesium.Cartesian3.fromDegrees(
-				Number(item.longitude),
-				Number(item.latitude),
-				Number(item.height)
-			)
+			// 落点线
+			; (droppointPositions || [])?.forEach((item, index) => {
+				const topPosition = Cesium.Cartesian3.fromDegrees(
+					Number(item.longitude),
+					Number(item.latitude),
+					Number(item.height)
+				)
 
-			let setting = {
-				name: 'work-drone-route-point-polyline',
-				position: topPosition,
-				polyline: getPolyLine(this.viewer, { value: droppointPositions }, index),
-			}
-			this.viewer.entities.add(setting)
-		})
+				let setting = {
+					name: 'work-drone-route--point-polyline',
+					position: topPosition,
+					polyline: getPolyLine(this.viewer, { value: droppointPositions }, index),
+				}
+				this.viewer.entities.add(setting)
+			})
 
 		return {
 			entity: polyline,
 			routeLinePositions: positionArray,
-			filePositions: dronePosition != null ? [
-				{ lng: dronePosition.longitude, lat: dronePosition.latitude, alt: dronePosition.height },
-				...filePositions
-			] : filePositions,
+			filePositions:
+				dockPosition !== null
+					? [{ lng: dockPosition.longitude, lat: dockPosition.latitude, alt: dockPosition.height }, ...filePositions]
+					: filePositions,
 		}
 	}
 
 	/**
 	 * 通用处理数据
-	 * @param {*} lineObj 
-	 * @param {*} missionConfig 
-	 * @param {*} dronePosition 
-	 * @param {*} startPoint 
+	 * @param {*} lineObj
+	 * @param {*} missionConfig
+	 * @param {*} dronePosition
+	 * @param {*} startPoint
 	 * @returns {} {positionArray: 带拼接点位置, filePositions:光航线点位置}
 	 */
 	disposeData (lineObj, missionConfig, dronePosition, data, startPoint, wayline_type) {
-		indexLog = indexLog + 1
 		const { device_sn } = data
-
-		const executeHeightMode = lineObj.executeHeightMode === "WGS84"
+		const executeHeightMode = lineObj.executeHeightMode === 'WGS84'
 
 		let filePositions = lineObj.Placemark.map(item => {
 			const [lng, lat] = item.Point.coordinates.split(',')
@@ -730,12 +896,14 @@
 		let safetyHeight
 
 		if (executeHeightMode) {
-			let startHeight = new Decimal(missionConfig.takeOffSecurityHeight)
-				.plus(dronePosition.height).toNumber()
+			let startHeight = new Decimal(missionConfig.takeOffSecurityHeight).plus(dronePosition?.height || 0).toNumber()
 
 			safetyHeight = startHeight < filePositions[0].alt ? filePositions[0].alt : startHeight
 		} else {
-			safetyHeight = missionConfig.takeOffSecurityHeight < filePositions[0].alt ? filePositions[0].alt : missionConfig.takeOffSecurityHeight
+			safetyHeight =
+				missionConfig.takeOffSecurityHeight < filePositions[0].alt
+					? filePositions[0].alt
+					: missionConfig.takeOffSecurityHeight
 		}
 
 		let safetyPositions = []
@@ -773,28 +941,38 @@
 		})
 
 		this.updataMultiNestData(device_sn, {
-			currentRoutePositions
+			currentRoutePositions,
 		})
 
 		if ([0].includes(wayline_type)) {
-			let startHeight = executeHeightMode ? filePositions[0].alt : filePositions[0].alt + (dronePosition != null ? dronePosition.height : 0)
-			let endHeight = executeHeightMode ? filePositions[1].alt : filePositions[1].alt + (dronePosition != null ? dronePosition.height : 0)
+			let startHeight = executeHeightMode
+				? filePositions[0].alt
+				: filePositions[0].alt + (dronePosition != null ? dronePosition.height : 0)
+			let endHeight = executeHeightMode
+				? filePositions[1].alt
+				: filePositions[1].alt + (dronePosition != null ? dronePosition.height : 0)
 
 			this.updataMultiNestData(device_sn, {
 				showPopupPosition: Cesium.Cartesian3.midpoint(
 					Cesium.Cartesian3.fromDegrees(filePositions[0].lng, filePositions[0].lat, startHeight),
 					Cesium.Cartesian3.fromDegrees(filePositions[1].lng, filePositions[1].lat, endHeight),
-					new Cesium.Cartesian3())
+					new Cesium.Cartesian3()
+				),
 			})
 		} else {
-			let startHeight = executeHeightMode ? safetyPositions[1].alt : safetyPositions[1].alt + (dronePosition != null ? dronePosition.height : 0)
-			let endHeight = executeHeightMode ? safetyPositions[2].alt : safetyPositions[2].alt + (dronePosition != null ? dronePosition.height : 0)
+			let startHeight = executeHeightMode
+				? safetyPositions[1].alt
+				: safetyPositions[1].alt + (dronePosition != null ? dronePosition.height : 0)
+			let endHeight = executeHeightMode
+				? safetyPositions[2].alt
+				: safetyPositions[2].alt + (dronePosition != null ? dronePosition.height : 0)
 
 			this.updataMultiNestData(device_sn, {
 				showPopupPosition: Cesium.Cartesian3.midpoint(
 					Cesium.Cartesian3.fromDegrees(safetyPositions[1].lng, safetyPositions[1].lat, startHeight),
 					Cesium.Cartesian3.fromDegrees(safetyPositions[2].lng, safetyPositions[2].lat, endHeight),
-					new Cesium.Cartesian3())
+					new Cesium.Cartesian3()
+				),
 			})
 		}
 
@@ -805,9 +983,9 @@
 
 				return {
 					...item,
-					alt: Number(height)
+					alt: Number(height),
 				}
-			})
+			}),
 		}
 	}
 
@@ -819,9 +997,10 @@
 			return i?.name && i?.name.includes('work-drone-route-')
 		})
 
-		Array.isArray(entities) && entities.forEach(item => {
-			this.viewer?.entities.remove(item)
-		})
+		Array.isArray(entities) &&
+			entities.forEach(item => {
+				this.viewer?.entities.remove(item)
+			})
 	}
 
 	// 当前无人机视椎体相关
@@ -847,11 +1026,22 @@
 				['wide', 'ir', 'zoom'].includes(curDroneData?.camera_type)
 			) {
 				if (curDroneData.camera_type === 'wide') {
-					fov = 60
+					fov = 65
 				} else {
-					const diffFocal = zoomFactor.value[curDroneData.camera_type].max - getZoomFactor(host?.cameras?.[0], curDroneData.camera_type)
-
-					fov = (60 / zoomFactor.value[curDroneData.camera_type].max) * (diffFocal === 0 ? 1 : diffFocal)
+					const cameraMultiplier = getZoomFactor(host?.cameras?.[0], curDroneData.camera_type)
+					switch (cameraMultiplier) {
+						case 2: fov = 30; break
+						case 3: fov = 19; break
+						case 4: fov = 15; break
+						case 5: fov = 13; break
+						case 6: fov = 11; break
+						case 7: fov = 9; break
+						case 8: fov = 8.34; break
+						case 9: fov = 7.68; break
+						case 10: fov = 6.9; break
+						case 14: fov = 5; break
+						default: fov = 55.946 * Math.exp(-0.4504 * cameraMultiplier) + 6.323
+					}
 				}
 			}
 
@@ -871,8 +1061,8 @@
 					pitch: 0,
 					heading: attitude_head,
 
-					isShowVideoPlan: this.isShowVideoPlan
-				})
+					isShowVideoPlan: this.isShowVideoPlan,
+				}),
 			})
 		}
 	}
@@ -882,7 +1072,7 @@
 
 		if (device_sn && live_status) {
 			this.updataMultiNestData(device_sn, {
-				camera_type: live_status?.[0].video_type || 'wide'
+				camera_type: live_status?.[0].video_type || 'wide',
 			})
 		}
 	}
@@ -897,14 +1087,9 @@
 		}
 	}
 
+	// 设置当前无人机视椎体信息面板
 	setFrustumInfoDetails (data) {
-		const {
-			dock_sn,
-			distance_to_airport,
-			distance_to_next_point,
-			estimated_time_to_next_point,
-			plane_mileage
-		} = data
+		const { dock_sn, distance_to_airport, distance_to_next_point, estimated_time_to_next_point, plane_mileage } = data
 
 		if (dock_sn) {
 			const aircraftEntity = this.viewer?.entities.values.find(i => {
@@ -924,7 +1109,9 @@
 				}
 
 				aircraftEntity.label = new Cesium.LabelGraphics({
-					text: `离机场平面距离:${distance_to_airport}m\n离下个航点平面距离:${Math.round(distance_to_next_point)}m\n到达下个航点剩余时间:${arrivalTime}\n航线总平面里程:${Math.round(plane_mileage)}m`,
+					text: `离机场平面距离:${distance_to_airport}m\n离下个航点平面距离:${Math.round(
+						distance_to_next_point
+					)}m\n到达下个航点剩余时间:${arrivalTime}\n航线总平面里程:${Math.round(plane_mileage)}m`,
 					font: '13px monospace',
 					showBackground: true,
 					horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
@@ -936,7 +1123,6 @@
 
 				return
 			}
-
 		}
 	}
 
@@ -954,8 +1140,10 @@
 		}
 	}
 
+	// 设置无人机模型
 	setAircraftGltf (host) {
 		const parent_sn = host?.parent_sn
+		const attitude_head = Number(host?.payloads?.[0]?.gimbal_yaw) - 90 || 0
 
 		if (parent_sn) {
 			const aircraftEntity = this.viewer?.entities.values.find(i => {
@@ -963,9 +1151,18 @@
 			})
 
 			const position = Cesium.Cartesian3.fromDegrees(host?.longitude, host?.latitude, host?.height)
+			// ✅ 新增:Heading / Pitch / Roll(单位:度 -> 弧度)
+			const heading = Cesium.Math.toRadians(attitude_head) // 航向角
+			const pitch = Cesium.Math.toRadians(0)     // 俯仰角
+			const roll = Cesium.Math.toRadians(0)       // 横滚角
+
+			// ✅ 新增:构造 HeadingPitchRoll 和 四元数
+			const hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll)
+			const orientation = Cesium.Transforms.headingPitchRollQuaternion(position, hpr)
 
 			if (aircraftEntity) {
 				aircraftEntity.position = new Cesium.ConstantPositionProperty(position)
+				aircraftEntity.orientation = new Cesium.ConstantProperty(orientation)
 
 				return
 			}
@@ -974,18 +1171,35 @@
 				this.viewer?.entities.add({
 					name: `aircraft-glf-${parent_sn}`,
 					position,
+					orientation,
 					label: {},
 					model: {
 						uri: aircraftGltf, // 或 .glb
-						scale: 1.0, // 缩放比例
+						scale: 64, // 缩放比例
 						minimumPixelSize: 64, // 最小像素尺寸(保证模型远处可见)
-						maximumScale: 128, // 最大缩放(可选)
+						maximumScale: 64, // 最大缩放(可选)
 					},
 				})
 			}
 		}
 	}
 
+	// 移除无人机模型
+	removeAircraftGltf (host) {
+		const parent_sn = host?.parent_sn
+
+		if (parent_sn) {
+			const aircraftEntity = this.viewer?.entities.values.find(i => {
+				return i?.name && i?.name.includes(`aircraft-glf-${parent_sn}`)
+			})
+
+			if (aircraftEntity) {
+				this.viewer?.entities.remove(aircraftEntity)
+			}
+		}
+	}
+
+	// 移除所有无人机视椎体
 	removeViewInfoFrustum () {
 		this.multiNestData.forEach(item => {
 			if ('viewInfoFrustum' in item && item.viewInfoFrustum) {
@@ -1010,9 +1224,10 @@
 				return i?.name && i?.name.includes(`aircraft-glf-${parent_sn}`)
 			})
 
-			Array.isArray(entities) && entities.forEach(item => {
-				this.viewer?.entities.remove(item)
-			})
+			Array.isArray(entities) &&
+				entities.forEach(item => {
+					this.viewer?.entities.remove(item)
+				})
 		}
 	}
 
@@ -1021,13 +1236,15 @@
 			return i?.name && i?.name.includes(`aircraft-glf-`)
 		})
 
-		Array.isArray(entities) && entities.forEach(item => {
-			this.viewer?.entities.remove(item)
-		})
+		Array.isArray(entities) &&
+			entities.forEach(item => {
+				this.viewer?.entities.remove(item)
+			})
 
-		this.multiNestData.length > 0 && (this.multiNestData.forEach(item => {
-			item.viewInfoFrustum?.clear()
-		}))
+		this.multiNestData.length > 0 &&
+			this.multiNestData.forEach(item => {
+				item.viewInfoFrustum?.clear()
+			})
 	}
 
 	/**
@@ -1043,19 +1260,19 @@
 			this.multiNestData[isHaveInd] = {
 				...this.multiNestData[isHaveInd],
 
-				...data
+				...data,
 			}
 		} else {
 			this.multiNestData.push({
 				device_sn,
-				...data
+				...data,
 			})
 		}
 	}
 
 	/**
 	 * 根据机巢sn获取当前机巢对应的相关信息
-	 * @param {*} device_sn 
+	 * @param {*} device_sn
 	 */
 	getCurDroneData (device_sn) {
 		const isHave = this.multiNestData.find(item => item.device_sn === device_sn)
@@ -1071,14 +1288,16 @@
 			return {
 				...item,
 				show: true,
-				uuid: uuidv4()
+				uuid: uuidv4(),
 			}
 		})
 		this.viewer.scene.postRender.addEventListener(that.labelBoxRenderHandler)
 	}
 
 	showSingleDetailMapPopup (data) {
-		this.detailsMapPopupData.forEach(i => i.job_id === data.job_id && i.device_sn === data.device_sn && (i.show = true))
+		this.detailsMapPopupData.forEach(
+			i => i?.job_id === data?.job_id && i?.device_sn === data?.device_sn && (i.show = true)
+		)
 	}
 
 	getLabelDom (data) {
@@ -1090,28 +1309,30 @@
 		tooltipContainer.style.position = 'absolute'
 		tooltipContainer.style.transform = 'translate(-50%,10%)'
 		tooltipContainer.style.pointerEvents = 'none'
-		document.querySelector('.content-map-popups')?.append(tooltipContainer)
+		document.querySelector('.content-map-popups').append(tooltipContainer)
 		render(vNode, tooltipContainer)
 		return tooltipContainer
 	}
 
 	labelBoxRender () {
-		this.detailsMapPopupData.filter(i => i.show === true).forEach(item => {
-			const { showPopupPosition } = this.getCurDroneData(item.device_sn)
+		this.detailsMapPopupData
+			.filter(i => i.show === true)
+			.forEach(item => {
+				const { showPopupPosition } = this.getCurDroneData(item.device_sn)
 
-			let dom = document.querySelector(`[id="${item.uuid}"]`)
+				let dom = document.querySelector(`[id="${item.uuid}"]`)
 
-			if (!dom) {
-				dom = this.getLabelDom(item)
-			}
+				if (!dom) {
+					dom = this.getLabelDom(item)
+				}
 
-			const screenPosition = this.viewer?.scene.cartesianToCanvasCoordinates(showPopupPosition)
-			if (screenPosition) {
-				dom.style.left = `${screenPosition.x}px`
-				dom.style.top = `${screenPosition.y}px`
-				dom.style.display = 'block'
-			}
-		})
+				const screenPosition = this.viewer?.scene.cartesianToCanvasCoordinates(showPopupPosition)
+				if (screenPosition) {
+					dom.style.left = `${screenPosition.x}px`
+					dom.style.top = `${screenPosition.y}px`
+					dom.style.display = 'block'
+				}
+			})
 	}
 
 	// 移除弹框标签
@@ -1137,4 +1358,4 @@
 		const that = this
 		that.viewer?.scene.postRender.removeEventListener(that.labelBoxRenderHandler)
 	}
-}
\ No newline at end of file
+}
diff --git a/applications/task-work-order/src/utils/cesium/eventBus.js b/applications/task-work-order/src/utils/cesium/eventBus.js
deleted file mode 100644
index 674e1dc..0000000
--- a/applications/task-work-order/src/utils/cesium/eventBus.js
+++ /dev/null
@@ -1,5 +0,0 @@
-import mitt from 'mitt'
-
-const emitter = mitt()
-
-export default emitter
diff --git a/applications/task-work-order/src/utils/cesium/frustum/CesiumVideoFrustum.js b/applications/task-work-order/src/utils/cesium/frustum/CesiumVideoFrustum.js
new file mode 100644
index 0000000..6dd75b8
--- /dev/null
+++ b/applications/task-work-order/src/utils/cesium/frustum/CesiumVideoFrustum.js
@@ -0,0 +1,712 @@
+/*
+ * @Author       : yuan
+ * @Date         : 2025-08-16 17:01:46
+ * @LastEditors  : yuan
+ * @LastEditTime : 2025-09-26 16:27:20
+ * @FilePath     : \src\utils\cesium\frustum\CesiumVideoFrustum.js
+ * @Description  :
+ * Copyright 2025 OBKoro1, All Rights Reserved.
+ * 2025-08-16 17:01:46
+ */
+import * as Cesium from 'cesium'
+
+var videoShed3dShader = `
+uniform float ztzf_frustum_opacity;
+
+uniform sampler2D ztzf_frustum_videoTexture;
+uniform sampler2D ztzf_frustum_maskTexture;
+uniform vec4 ztzf_frustum_hiddenAreaColor;
+
+uniform sampler2D shadowMap_texture;
+uniform mat4 shadowMap_matrix;
+uniform vec4 shadowMap_lightPositionEC;
+uniform vec4 shadowMap_texelSizeDepthBias;
+uniform vec4 shadowMap_normalOffsetScale;
+uniform bool ztzf_frustum_flipx;
+uniform bool ztzf_frustum_flipy;
+
+// 新增边框参数
+uniform vec4 ztzf_frustum_borderColor;  // 边框颜色(RGBA)
+uniform float ztzf_frustum_borderWidth; // 边框宽度(0.0-1.0)
+
+uniform sampler2D colorTexture;
+uniform sampler2D depthTexture;
+in vec2 v_textureCoordinates;
+
+vec4 toEye(in vec2 uv, in float depth) {
+  vec2 xy = vec2((uv.x * 2.0 - 1.0), (uv.y * 2.0 - 1.0));
+  vec4 posInCamera = czm_inverseProjection * vec4(xy, depth, 1.0);
+  posInCamera = posInCamera / posInCamera.w;
+  return posInCamera;
+}
+
+float getDepthMars3D(in vec4 depth) {
+  float z_window = czm_unpackDepth(depth);
+  z_window = czm_reverseLogDepth(z_window);
+  float n_range = czm_depthRange.near;
+  float f_range = czm_depthRange.far;
+  return (2.0 * z_window - n_range - f_range) / (f_range - n_range);
+}
+float _czm_sampleShadowMap(sampler2D shadowMap, vec2 uv) {
+  return texture(shadowMap, uv).r;
+}
+float _czm_shadowDepthCompare(sampler2D shadowMap, vec2 uv, float depth) {
+  return step(depth, _czm_sampleShadowMap(shadowMap, uv));
+}
+float _czm_shadowVisibility(sampler2D shadowMap, czm_shadowParameters shadowParameters) {
+  float depthBias = shadowParameters.depthBias;
+  float depth = shadowParameters.depth;
+  float nDotL = shadowParameters.nDotL;
+  float normalShadingSmooth = shadowParameters.normalShadingSmooth;
+  float darkness = shadowParameters.darkness;
+  vec2 uv = shadowParameters.texCoords;
+  depth -= depthBias;
+  vec2 texelStepSize = shadowParameters.texelStepSize;
+  float radius = 1.0;
+  float dx0 = -texelStepSize.x * radius;
+  float dy0 = -texelStepSize.y * radius;
+  float dx1 = texelStepSize.x * radius;
+  float dy1 = texelStepSize.y * radius;
+  float visibility = (_czm_shadowDepthCompare(shadowMap, uv, depth) +
+    _czm_shadowDepthCompare(shadowMap, uv + vec2(dx0, dy0), depth) +
+    _czm_shadowDepthCompare(shadowMap, uv + vec2(0.0, dy0), depth) +
+    _czm_shadowDepthCompare(shadowMap, uv + vec2(dx1, dy0), depth) +
+    _czm_shadowDepthCompare(shadowMap, uv + vec2(dx0, 0.0), depth) +
+    _czm_shadowDepthCompare(shadowMap, uv + vec2(dx1, 0.0), depth) +
+    _czm_shadowDepthCompare(shadowMap, uv + vec2(dx0, dy1), depth) +
+    _czm_shadowDepthCompare(shadowMap, uv + vec2(0.0, dy1), depth) +
+    _czm_shadowDepthCompare(shadowMap, uv + vec2(dx1, dy1), depth)) * (1.0 / 9.0);
+  return visibility;
+}
+
+vec3 pointProjectOnPlane(in vec3 planeNormal, in vec3 planeOrigin, in vec3 point) {
+  vec3 v01 = point - planeOrigin;
+  float d = dot(planeNormal, v01);
+  return (point - planeNormal * d);
+}
+float ptm(vec3 pt) {
+  return sqrt(pt.x * pt.x + pt.y * pt.y + pt.z * pt.z);
+}
+
+void main() {
+  const float PI = 3.141592653589793;
+  vec4 color = texture(colorTexture, v_textureCoordinates);
+  vec4 currD = texture(depthTexture, v_textureCoordinates);
+
+  float depth = getDepthMars3D(currD);
+  vec4 positionEC = toEye(v_textureCoordinates, depth);
+  vec3 normalEC = vec3(1.0);
+  czm_shadowParameters shadowParameters;
+  shadowParameters.texelStepSize = shadowMap_texelSizeDepthBias.xy;
+  shadowParameters.depthBias = shadowMap_texelSizeDepthBias.z;
+  shadowParameters.normalShadingSmooth = shadowMap_texelSizeDepthBias.w;
+  shadowParameters.darkness = shadowMap_normalOffsetScale.w;
+  shadowParameters.depthBias *= max(depth * 0.01, 1.0);
+
+  vec3 directionEC = normalize(positionEC.xyz - shadowMap_lightPositionEC.xyz);
+  float nDotL = clamp(dot(normalEC, -directionEC), 0.0, 1.0);
+  vec4 shadowPosition = shadowMap_matrix * positionEC;
+  shadowPosition /= shadowPosition.w;
+  if(any(lessThan(shadowPosition.xyz, vec3(0.0))) || any(greaterThan(shadowPosition.xyz, vec3(1.0)))) {
+    out_FragColor = color;
+    return;
+  }
+
+  shadowParameters.texCoords = shadowPosition.xy;
+  shadowParameters.depth = shadowPosition.z;
+  shadowParameters.nDotL = nDotL;
+  float visibility = _czm_shadowVisibility(shadowMap_texture, shadowParameters);
+
+  //视频投射
+  if(visibility == 1.0) {
+    if(ztzf_frustum_flipx){
+      shadowPosition.x = shadowPosition.x + (0.5 - shadowPosition.x) * 2.0;
+    }
+    if(ztzf_frustum_flipy){
+      shadowPosition.y = shadowPosition.y + (0.5 - shadowPosition.y) * 2.0;
+    }
+
+    vec4 videoColor = texture(ztzf_frustum_videoTexture, shadowPosition.xy);
+    vec4 maskColor = texture(ztzf_frustum_maskTexture, shadowPosition.xy);
+    videoColor *= maskColor;
+    
+    // 计算到边缘的距离
+    float distToEdge = min(
+        min(shadowPosition.x, 1.0 - shadowPosition.x),
+        min(shadowPosition.y, 1.0 - shadowPosition.y)
+    );
+    
+    // 混合视频颜色和背景
+    vec4 finalColor = mix(color, vec4(videoColor.xyz, 1.0), ztzf_frustum_opacity * videoColor.a);
+    
+    // 添加边框效果
+    if(distToEdge < ztzf_frustum_borderWidth && videoColor.a > 0.0) {
+        float borderFactor = smoothstep(0.0, ztzf_frustum_borderWidth, distToEdge);
+        finalColor = mix(ztzf_frustum_borderColor, finalColor, borderFactor);
+    }
+    
+    out_FragColor = finalColor;
+  } else {
+    if(abs(shadowPosition.z - 0.0) < 0.01) {
+      return;
+    }
+    out_FragColor = vec4(mix(color.rgb, ztzf_frustum_hiddenAreaColor.rgb, ztzf_frustum_hiddenAreaColor.a), ztzf_frustum_hiddenAreaColor.a);
+  }
+}
+    `
+
+class CesiumVideoFrustum {
+    constructor(viewer, param) {
+        this.param = param
+
+        var option = this._initCameraParam()
+
+        if (option || (option = {}), this.viewer = viewer, this._cameraPosition = option.cameraPosition, this._position = option.position,
+            this.type = option.type, this._alpha = option.alpha || 0, this.element = option.element, this.color = option.color,
+            this._debugFrustum = Cesium.defaultValue(option.debugFrustum, !0), this._aspectRatio = option.aspectRatio || this._getWinWidHei(),
+            this._camerafov = option.fov || Cesium.Math.toDegrees(this.viewer.scene.camera.frustum.fov),
+            this._isShadowMap = option.isShadowMap || false,
+            this.texture = option.texture || new Cesium.Texture({
+                context: this.viewer.scene.context,
+                source: {
+                    width: 1,
+                    height: 1,
+                    arrayBufferView: new Uint8Array([255, 255, 255, 255])
+                },
+                flipY: !1
+            }), this.defaultShow = Cesium.defaultValue(option.show, !0), !this.cameraPosition || !this.position) return void console.log('初始化失败:请确认相机位置与视点位置正确!')
+
+        this.activeVideo()
+        this._createShadowMap()
+        this._getOrientation()
+        this._addCameraFrustum()
+        this._addPostProcess()
+        this.viewer.scene.primitives.add(this)
+    }
+
+    get alpha () {
+        return this._alpha
+    }
+
+    set alpha (e) {
+        this._alpha = e
+        this._changeViewPos()
+    }
+
+    get aspectRatio () {
+        return this._aspectRatio
+    }
+
+    set aspectRatio (e) {
+        this._aspectRatio = e
+        this._changeVideoWidHei()
+    }
+
+    get debugFrustum () {
+        return this._debugFrustum
+    }
+
+    set debugFrustum (e) {
+        this._debugFrustum = e
+        this.cameraFrustum.show = e
+        this.cameraFrustumOutline.show = e
+    }
+
+    get fov () {
+        return this._camerafov
+    }
+
+    set fov (e) {
+        this._camerafov = e
+        this._changeCameraFov()
+    }
+
+    get cameraPosition () {
+        return this._cameraPosition
+    }
+
+    set cameraPosition (e) {
+        e && (this._cameraPosition = e, this._changeCameraPos())
+    }
+
+    get isShadowMap () {
+        return this._isShadowMap
+    }
+
+    set isShadowMap (e) {
+        this._isShadowMap = e
+        this._changeCameraIsShadowMap()
+    }
+
+    get position () {
+        return this._position
+    }
+
+    set position (e) {
+        e && (this._position = e, this._changeViewPos())
+    }
+
+    get params () {
+        var t = {}
+        return t.element = this.element,
+            t.position = this.position,
+            t.cameraPosition = this.cameraPosition,
+            t.fov = this.fov,
+            t.isShadowMap = this.isShadowMap,
+            t.aspectRatio = this.aspectRatio,
+            t.alpha = this.alpha,
+            t.debugFrustum = this.debugFrustum,
+            t.show = this.show
+    }
+
+    get show () {
+        return this.defaultShow
+    }
+
+    set show (e) {
+        this.defaultShow = Boolean(e),
+            this._switchShow()
+    }
+
+    isDestroyed () {
+        return false
+    }
+
+    _initCameraParam () {
+        var cameraPosition = Cesium.Cartesian3.fromDegrees(this.param.position.x * 1, this.param.position.y * 1, this.param.position.z * 1)
+
+        const hpr = Cesium.HeadingPitchRoll.fromDegrees(this.param.rotation.y * 1, this.param.rotation.x * 1, 0)
+        const orientation = Cesium.Transforms.headingPitchRollQuaternion(cameraPosition, hpr)
+
+        let matrix = Cesium.Matrix3.fromQuaternion(
+            orientation,
+            new Cesium.Matrix3()
+        )
+
+        let direction = Cesium.Matrix3.getColumn(matrix, 2, new Cesium.Cartesian3())
+
+        let frontDirect = Cesium.Cartesian3.multiplyByScalar(
+            direction,
+            250,
+            new Cesium.Cartesian3()
+        )
+        let position = Cesium.Cartesian3.add(cameraPosition, frontDirect, new Cesium.Cartesian3())
+
+        return {
+            cameraPosition: cameraPosition,
+            position: position,
+            alpha: this.param.alpha,
+            near: this.param.near,
+            fov: this.param.fov,
+            isShadowMap: this.param.isShadowMap,
+            debugFrustum: this.param.debugFrustum,
+            aspectRatio: this.param.aspectRatio,
+            element: this.param.element,
+        }
+    }
+
+    _changeAlpha (e) {
+        this.alpha = e
+    }
+
+    _changeAspectRatio (e) {
+        this.aspectRatio = e
+    }
+
+    _changeRotation (e) {
+        if (e) {
+            this.param.rotation = e
+            var option = this._initCameraParam()
+            this.position = option.position
+        }
+    }
+
+    _changeCameraPosition (e) {
+        if (e) {
+            this.param.position = e
+            var option = this._initCameraParam()
+            this.cameraPosition = option.cameraPosition
+        }
+    }
+
+    _changeFov (e) {
+        if (e) {
+            this.param.fov = e
+            var option = this._initCameraParam()
+            this.fov = option.fov
+        }
+    }
+
+    _changeIsShadowMap (e) {
+        this.param.isShadowMap = e
+        var option = this._initCameraParam()
+        this.isShadowMap = option.isShadowMap
+    }
+
+    _changeFar (e) {
+        if (e) {
+            this.param.far = e
+            var option = this._initCameraParam()
+            this.position = option.position
+        }
+    }
+
+    _changeNear (e) {
+        if (e) {
+            this.param.near = e
+            this.near = this.param.near
+            this._changeCameraPos()
+        }
+    }
+
+    _getWinWidHei () {
+        var viewer = this.viewer.scene
+        return viewer.canvas.clientWidth / viewer.canvas.clientHeight
+    }
+
+    _changeCameraFov () {
+        this.postProcess && this.viewer.scene.postProcessStages.remove(this.postProcess)
+
+        this.viewer.scene.primitives.remove(this.cameraFrustum),
+            this.viewer.scene.primitives.remove(this.cameraFrustumOutline),
+            this._createShadowMap(this.cameraPosition, this.position),
+            this._getOrientation(),
+            this._addCameraFrustum(),
+            this._addPostProcess()
+    }
+
+    _changeCameraIsShadowMap () {
+        this.postProcess && this.viewer.scene.postProcessStages.remove(this.postProcess)
+
+        this.viewShadowMap && this.viewShadowMap.destroy()
+        this.viewShadowMap = null
+
+        this.viewer.scene.primitives.remove(this.cameraFrustum),
+            this.viewer.scene.primitives.remove(this.cameraFrustumOutline),
+            this._createShadowMap(this.cameraPosition, this.position),
+            this._getOrientation(),
+            this._addCameraFrustum(),
+            this._addPostProcess()
+    }
+
+    _changeVideoWidHei () {
+        this.postProcess && this.viewer.scene.postProcessStages.remove(this.postProcess)
+
+        this.viewer.scene.primitives.remove(this.cameraFrustum),
+            this.viewer.scene.primitives.remove(this.cameraFrustumOutline),
+            this._createShadowMap(this.cameraPosition, this.position),
+            this._getOrientation(),
+            this._addCameraFrustum(),
+            this._addPostProcess()
+    }
+
+    _changeCameraPos () {
+        this.postProcess && this.viewer.scene.postProcessStages.remove(this.postProcess)
+
+        this.viewShadowMap && this.viewShadowMap.destroy()
+        this.viewShadowMap = null
+
+        this.viewer.scene.primitives.remove(this.cameraFrustum),
+            this.viewer.scene.primitives.remove(this.cameraFrustumOutline),
+            // this.cameraFrustum.destroy(),
+            this._createShadowMap(this.cameraPosition, this.position),
+            this._getOrientation(),
+            this._addCameraFrustum(),
+            this._addPostProcess()
+    }
+
+    _changeViewPos () {
+        this.postProcess && this.viewer.scene.postProcessStages.remove(this.postProcess)
+
+        this.viewShadowMap && this.viewShadowMap.destroy()
+        this.viewShadowMap = null
+
+        this.viewer.scene.primitives.remove(this.cameraFrustum),
+            this.viewer.scene.primitives.remove(this.cameraFrustumOutline),
+            // this.cameraFrustum.destroy(),
+            this._createShadowMap(this.cameraPosition, this.position),
+            this._getOrientation(),
+            this._addCameraFrustum(),
+            this._addPostProcess()
+    }
+
+    _switchShow () {
+        this.show ? !this.postProcess && this._addPostProcess() : (this.postProcess && this.viewer.scene.postProcessStages.remove(this.postProcess), delete this.postProcess, this.postProcess = null),
+            this.cameraFrustum.show = this.show,
+            this.cameraFrustumOutline.show = this.show
+    }
+
+    activeVideo () {
+        var video = this.element,
+            that = this
+        if (video) {
+            var viewer = this.viewer
+            this.activeVideoListener || (this.activeVideoListener = function () {
+                that.videoTexture && that.videoTexture.destroy(),
+                    that.videoTexture = new Cesium.Texture({
+                        context: viewer.scene.context,
+                        source: video,
+                        width: 1,
+                        height: 1,
+                        pixelFormat: Cesium.PixelFormat.RGBA,
+                        pixelDatatype: Cesium.PixelDatatype.UNSIGNED_BYTE
+                    })
+            }),
+                viewer.clock.onTick.addEventListener(this.activeVideoListener)
+        }
+    }
+
+    locate () {
+        var cameraPosition = Cesium.clone(this.cameraPosition),
+            position = Cesium.clone(this.position)
+        this.viewer.Camera.position = cameraPosition,
+            this.viewer.camera.direction = Cesium.Cartesian3.subtract(position, cameraPosition, new Cesium.Cartesian3(0, 0, 0)),
+            this.viewer.camera.up = Cesium.Cartesian3.normalize(cameraPosition, new Cesium.Cartesian3(0, 0, 0))
+    }
+
+    update (e) {
+        this.viewShadowMap && this.viewer.scene.frameState.shadowMaps.push(this.viewShadowMap) // *重点* 多投影
+    }
+
+    _createShadowMap () {
+        var e = Cesium.Cartesian3.fromDegrees(this.param.position.x * 1, this.param.position.y * 1, this.param.position.z * 1)
+        var t = this.position
+        var i = this.viewer.scene
+        var a = new Cesium.Camera(i)
+        a.position = e
+
+        this.customFrustum = new Cesium.PerspectiveFrustum({
+            fov: Cesium.Math.toRadians(this.fov),
+            aspectRatio: this.aspectRatio,
+            near: this.near,
+            far: 250
+        })
+
+        a.frustum = new Cesium.PerspectiveFrustum({
+            fov: Cesium.Math.toRadians(this.fov),
+            aspectRatio: this.aspectRatio,
+            near: this.near,
+            far: 250
+        })
+
+        let curOrientation = {}
+
+        const headingPitchRoll = new Cesium.HeadingPitchRoll(
+            Cesium.Math.toRadians(this.param.rotation.y, 0),
+            Cesium.Math.toRadians(this.param.rotation.x, 0),
+            Cesium.Math.toRadians(0),
+        )
+
+        curOrientation['heading'] = headingPitchRoll['heading']
+        curOrientation['pitch'] = headingPitchRoll['pitch']
+        curOrientation['roll'] = headingPitchRoll['roll']
+
+        let curDestination = {}
+
+        curDestination.destination = e
+        curDestination.orientation = curOrientation
+
+        a.setView(curDestination)
+
+        var n = Cesium.Cartesian3.distance(t, e)
+        this.viewDis = n
+
+        if (!this.isShadowMap) return
+
+        this.viewShadowMap = new Cesium.ShadowMap({
+            lightCamera: a,
+            enable: !1,
+            isPointLight: !1,
+            isSpotLight: !0,
+            cascadesEnabled: !1,
+            context: i.context,
+            pointLightRadius: n,
+        })
+    }
+
+    _getOrientation () {
+        const hpr = Cesium.HeadingPitchRoll.fromDegrees(180 + this.param.rotation.y * 1, 0, 90 - this.param.rotation.x * 1 || 0)
+        const orientation = Cesium.Transforms.headingPitchRollQuaternion(this.cameraPosition, hpr)
+
+        return this.orientation = orientation,
+            orientation
+    }
+
+    _addCameraFrustum () {
+        var e = this
+
+        this.cameraFrustum = new Cesium.Primitive({
+            geometryInstances: new Cesium.GeometryInstance({
+                geometry: new Cesium.FrustumGeometry({
+                    origin: e.cameraPosition,
+                    orientation: e.orientation,
+                    frustum: e.customFrustum,
+                    _drawNearPlane: !0
+                }),
+                attributes: {
+                    color: Cesium.ColorGeometryInstanceAttribute.fromColor(
+                        Cesium.Color.fromBytes(0, 213, 144, 20)
+                    ),
+                }
+            }),
+            releaseGeometryInstances: false,
+            appearance: new Cesium.PerInstanceColorAppearance({
+                translucent: true,
+                flat: !0,
+                closed: true,
+            }),
+            asynchronous: false,
+            show: this.debugFrustum && this.show
+        })
+
+        this.cameraFrustumOutline = new Cesium.Primitive({
+            geometryInstances: new Cesium.GeometryInstance({
+                geometry: new Cesium.FrustumOutlineGeometry({
+                    origin: e.cameraPosition,
+                    orientation: e.orientation,
+                    frustum: e.customFrustum,
+                    _drawNearPlane: !0
+                }),
+                attributes: {
+                    color: Cesium.ColorGeometryInstanceAttribute.fromColor(
+                        Cesium.Color.fromBytes(0, 213, 144, 255)
+                    ),
+                }
+            }),
+            appearance: new Cesium.PerInstanceColorAppearance({
+                closed: true,
+                flat: true,
+                translucent: true
+            }),
+            asynchronous: false,
+            show: this.debugFrustum && this.show
+        })
+
+        this.viewer.scene.primitives.add(this.cameraFrustum)
+        this.viewer.scene.primitives.add(this.cameraFrustumOutline)
+    }
+
+    _addPostProcess () {
+        if (!this.isShadowMap) return
+
+        var e = this,
+            t = videoShed3dShader
+
+        const i = e.viewShadowMap['_primitiveBias']
+
+        this.postProcess = new Cesium.PostProcessStage({
+            fragmentShader: t,
+            uniforms: {
+                ztzf_frustum_videoTexture: function () {
+                    return e.videoTexture
+                },
+
+                ztzf_frustum_maskTexture: function () {
+                    return new Cesium['Texture']({
+                        'context': e.viewer.scene.context,
+                        'source': {
+                            'width': 0x1,
+                            'height': 0x1,
+                            'arrayBufferView': new Uint8Array([255, 255, 255, 255])
+                        },
+                        'flipY': ![]
+                    })
+                },
+
+                ztzf_frustum_opacity: function () {
+                    return e.alpha
+                },
+
+                ztzf_frustum_hiddenAreaColor: () => {
+                    return new Cesium.Color(0, 0, 0, 0.5)
+                },
+
+                shadowMap_texture: function () {
+                    if (!e.viewShadowMap || !e.viewShadowMap['_shadowMapTexture'] || e.viewShadowMap['_shadowMapTexture']['isDestroyed']()) {
+                        return new Cesium['Texture']({
+                            'context': e.viewer.scene.context,
+                            'source': {
+                                'width': 0x1,
+                                'height': 0x1,
+                                'arrayBufferView': new Uint8Array([0x0, 0x0, 0x0, 0x0])
+                            },
+                            'flipY': ![]
+                        })
+                    }
+
+                    return e.viewShadowMap._shadowMapTexture
+                },
+
+                shadowMap_matrix: function () {
+                    return e.viewShadowMap._shadowMapMatrix
+                },
+
+                shadowMap_lightPositionEC: function () {
+                    return e.viewShadowMap._lightPositionEC
+                },
+
+                shadowMap_texelSizeDepthBias: function () {
+                    var t = new Cesium.Cartesian2
+                    return t.x = 1 / e.viewShadowMap._textureSize.x,
+                        t.y = 1 / e.viewShadowMap._textureSize.y,
+                        Cesium.Cartesian4.fromElements(t.x, t.y, i.depthBias, i.normalShadingSmooth, this.combinedUniforms1)
+                },
+
+                shadowMap_normalOffsetScale: function () {
+                    return Cesium.Cartesian4.fromElements(i.normalOffsetScale, e.viewShadowMap._distance, e.viewShadowMap.maximumDistance, e.viewShadowMap._darkness, this.combinedUniforms2)
+                },
+
+                ztzf_frustum_flipx: () => {
+                    return false
+                },
+
+                ztzf_frustum_flipy: () => {
+                    return false
+                },
+
+                ztzf_frustum_borderColor: () => {
+                    return new Cesium.Color(0 / 255, 213 / 255, 144 / 255, 255 / 255)
+                },
+
+                ztzf_frustum_borderWidth: () => {
+                    return 0.01
+                },
+            }
+        }),
+            this.viewer.scene.postProcessStages.add(this.postProcess)
+    }
+
+    destroy () {
+        this.viewer.scene.postProcessStages.remove(this.postProcess),
+            this.viewer.scene.primitives.remove(this.cameraFrustum),
+            this.viewer.scene.primitives.remove(this.cameraFrustumOutline),
+            //this._videoEle && this._videoEle.parentNode.removeChild(this._videoEle),
+            this.activeVideoListener && this.viewer.clock.onTick.removeEventListener(this.activeVideoListener),
+            this.activeVideoListener && delete this.activeVideoListener,
+            delete this.postProcess,
+            delete this.viewShadowMap,
+            delete this.color,
+            delete this.viewDis,
+            delete this.cameraPosition,
+            delete this.position,
+            delete this.alpha,
+            delete this._camerafov,
+            delete this._isShadowMap,
+            delete this._cameraPosition,
+            delete this.videoTexture,
+            delete this.cameraFrustum,
+            delete this.cameraFrustumOutline,
+            delete this._videoEle,
+            delete this._debugFrustum,
+            delete this._position,
+            delete this._aspectRatio,
+            delete this.element,
+            delete this.orientation,
+            delete this.texture,
+            delete this.videoId,
+            this.viewer.scene.primitives.remove(this),
+            delete this.viewer
+    }
+}
+
+export default CesiumVideoFrustum
diff --git a/applications/task-work-order/src/utils/cesium/frustum/CoordinateTranslate.js b/applications/task-work-order/src/utils/cesium/frustum/CoordinateTranslate.js
new file mode 100644
index 0000000..24e0675
--- /dev/null
+++ b/applications/task-work-order/src/utils/cesium/frustum/CoordinateTranslate.js
@@ -0,0 +1,229 @@
+/*
+ * @Author       : yuan
+ * @Date         : 2025-08-15 14:06:49
+ * @LastEditors  : yuan
+ * @LastEditTime : 2025-08-15 14:06:59
+ * @FilePath     : \src\utils\cesium\frustum\CoordinateTranslate.js
+ * @Description  : 
+ * Copyright 2025 OBKoro1, All Rights Reserved. 
+ * 2025-08-15 14:06:49
+ */
+let ECEF = (function () {
+	var _ = function () {
+		this.PI = 3.141592653589793238
+		this.a = 6378137.0
+		this.b = 6356752.3142
+		this.f = (this.a - this.b) / this.a
+		this.e_sq = this.f * (2.0 - this.f)
+		this.ee = 0.00669437999013
+		this.WGSF = 1 / 298.257223563
+		this.WGSe2 = this.WGSF * (2 - this.WGSF)
+		this.WGSa = 6378137.00000
+		this.EPSILON = 1.0e-12
+	}
+	_.prototype.CalculateCoordinates = function (point, azimuth, elevation, distance) {
+		var vertical_height = distance * Math.sin(2 * this.PI / 360 * elevation)//垂直高度
+		var horizontal_distance = distance * Math.cos(2 * this.PI / 360 * elevation)//水平距离
+		if (azimuth > 360) azimuth = azimuth % 360
+		if (azimuth < 0) azimuth = 360 + (azimuth % 360)
+
+		var point1 = this.lonLat2WebMercator(point)
+		var lnglat = null
+
+		var x_length, y_length
+		if (azimuth <= 90) {//第四象限
+			x_length = horizontal_distance * Math.cos(2 * this.PI / 360 * azimuth)
+			y_length = horizontal_distance * Math.sin(2 * this.PI / 360 * azimuth)
+			lnglat = {
+				x: point1.x + x_length,
+				y: point1.y - y_length
+			}
+		} else if (azimuth > 90 && azimuth <= 180) {//第三象限
+			x_length = horizontal_distance * Math.sin(2 * this.PI / 360 * (azimuth - 90))
+			y_length = horizontal_distance * Math.cos(2 * this.PI / 360 * (azimuth - 90))
+			lnglat = {
+				x: point1.x - x_length,
+				y: point1.y - y_length
+			}
+		} else if (azimuth > 180 && azimuth <= 270) {//第二象限
+			x_length = horizontal_distance * Math.cos(2 * this.PI / 360 * (azimuth - 180))
+			y_length = horizontal_distance * Math.sin(2 * this.PI / 360 * (azimuth - 180))
+			lnglat = {
+				x: point1.x - x_length,
+				y: point1.y + y_length
+			}
+		} else {//第一象限
+			x_length = horizontal_distance * Math.sin(2 * this.PI / 360 * (azimuth - 270))
+			y_length = horizontal_distance * Math.cos(2 * this.PI / 360 * (azimuth - 270))
+			lnglat = {
+				x: point1.x + x_length,
+				y: point1.y + y_length
+			}
+		}
+		lnglat = this.webMercator2LonLat(lnglat)
+		return {
+			lng: lnglat.x,
+			lat: lnglat.y,
+			height: vertical_height
+		}
+	}
+	/*
+	   *经纬度转Web墨卡托
+	   *@lonLat 经纬度
+	   */
+	_.prototype.lonLat2WebMercator = function (lonLat) {
+		let x = lonLat.x * this.a / 180
+		let y = Math.log(Math.tan((90 + lonLat.y) * this.PI / 360)) / (this.PI / 180)
+		y = y * this.a / 180
+		return {
+			x: x,
+			y: y
+		}
+	}
+
+	/*
+	   *Web墨卡托转经纬度
+	   *@mercator 平面坐标
+	   */
+	_.prototype.webMercator2LonLat = function (mercator) {
+		let x = mercator.x / this.a * 180
+		let y = mercator.y / this.a * 180
+		y = 180 / this.PI * (2 * (Math.exp(y * this.PI / 180)) - this.PI / 2)
+		return {
+			x: x,
+			y: y
+		}
+	}
+
+	_.prototype.get_atan = function (z, y) {
+		let x
+		if (z == 0) {
+			x = this.PI / 2
+		} else {
+			if (y == 0) {
+				x = this.PI
+			} else {
+				x = Math.atan(Math.abs(y / z))
+				if ((y > 0) && (z < 0)) {
+					x = this.PI - x
+				} else if ((y < 0) && (z < 0)) {
+					x = this.PI + x
+				} else if ((y < 0) && (z > 0)) {
+					x = 2 * this.M_PI - x
+				}
+			}
+		}
+		return x
+	}
+	//WGS84转ECEF坐标系
+	_.prototype.ConvertLLAToXYZ = function (LLACoor) {
+		let lon = this.PI / 180 * LLACoor.longitude
+		let lat = this.PI / 180 * LLACoor.latitude
+		let H = LLACoor.altitude
+		let N0 = this.a / Math.sqrt(1.0 - this.ee * Math.sin(lat) * Math.sin(lat))
+		let x = (N0 + H) * Math.cos(lat) * Math.cos(lon)
+		let y = (N0 + H) * Math.cos(lat) * Math.sin(lon)
+		let z = (N0 * (1.0 - this.ee) + H) * Math.sin(lat)
+		return {
+			x: x,
+			y: y,
+			z: z
+		}
+	}
+
+	//ECEF坐标系转WGS84
+	_.prototype.ConvertXYZToLLA = function (XYZCoor) {
+		let longitude = this.get_atan(XYZCoor.x, XYZCoor.y)
+		if (longitude < 0) {
+			longitude = longitude + this.PI
+		}
+		let latitude = this.get_atan(Math.sqrt(XYZCoor.x * XYZCoor.x + XYZCoor.y * XYZCoor.y), XYZCoor.z)
+
+		let W = Math.sqrt(1 - this.WGSe2 * Math.sin(latitude) * Math.sin(latitude))
+		let N = this.WGSa / W
+		let B1
+		do {
+			B1 = latitude
+			W = Math.sqrt(1 - this.WGSe2 * Math.sin(B1) * Math.sin(B1))
+			N = this.WGSa / W
+			latitude = this.get_atan(Math.sqrt(XYZCoor.x * XYZCoor.x + XYZCoor.y * XYZCoor.y), (XYZCoor.z + N * this.WGSe2 * Math.sin(B1)))
+		}
+		while (Math.abs(latitude - B1) > this.EPSILON)
+
+		var altitude = Math.sqrt(XYZCoor.x * XYZCoor.x + XYZCoor.y * XYZCoor.y) / Math.cos(latitude) - this.WGSa / Math.sqrt(1 - this.WGSe2 * Math.sin(latitude) * Math.sin(latitude))
+
+		return {
+			longitude: longitude * 180 / this.PI,
+			latitude: latitude * 180 / this.PI,
+			altitude: altitude
+		}
+	}
+	/*北东天坐标系转WGS84
+	@ a A点坐标
+	@ p 相对参数,距离、方位角、仰角
+	*/
+	//	俯视角pitch -elevation 
+	//航向角heading(yaw) -azimuth 
+	_.prototype.enu_to_ecef = function (a, p) {
+		//距离
+		let distance = p.distance
+		//方位角
+		let azimuth = p.azimuth
+		//仰角
+		let elevation = p.elevation
+
+		let zUp = elevation >= 0 ? distance * Math.sin(this.PI / 180 * elevation) : (-1) * distance * Math.sin(this.PI / 180 * Math.abs(elevation))
+
+		let d = distance * Math.cos(this.PI / 180 * Math.abs(elevation))
+		let xEast
+		let yNorth
+		if (azimuth <= 90) {
+			xEast = d * Math.sin(this.PI / 180 * azimuth)
+			yNorth = d * Math.cos(this.PI / 180 * azimuth)
+		} else if (azimuth > 90 && azimuth < 180) {
+			xEast = d * Math.cos(this.PI / 180 * (azimuth - 90))
+			yNorth = (-1) * d * Math.sin(this.PI / 180 * (azimuth - 90))
+		} else if (azimuth > 180 && azimuth < 270) {
+			xEast = (-1) * d * Math.sin(this.PI / 180 * (azimuth - 180))
+			yNorth = (-1) * d * Math.cos(this.PI / 180 * (azimuth - 180))
+		} else {
+			xEast = (-1) * d * Math.sin(this.PI / 180 * (360 - azimuth))
+			yNorth = d * Math.cos(this.PI / 180 * (360 - azimuth))
+		}
+
+		let lamb = this.radians(a.latitude)
+		let phi = this.radians(a.longitude)
+		let h0 = a.altitude
+
+		let s = Math.sin(lamb)
+		let N = this.a / Math.sqrt(1.0 - this.e_sq * s * s)
+
+		let sin_lambda = Math.sin(lamb)
+		let cos_lambda = Math.cos(lamb)
+
+		let sin_phi = Math.sin(phi)
+		let cos_phi = Math.cos(phi)
+
+		let x0 = (h0 + N) * cos_lambda * cos_phi
+		let y0 = (h0 + N) * cos_lambda * sin_phi
+		let z0 = (h0 + (1 - this.e_sq) * N) * sin_lambda
+
+		let t = cos_lambda * zUp - sin_lambda * yNorth
+
+		let zd = sin_lambda * zUp + cos_lambda * yNorth
+		let xd = cos_phi * t - sin_phi * xEast
+		let yd = sin_phi * t + cos_phi * xEast
+
+		return this.ConvertXYZToLLA({
+			x: xd + x0,
+			y: yd + y0,
+			z: zd + z0
+		})
+	}
+	_.prototype.radians = function (degree) {
+		return this.PI / 180 * degree
+	}
+	return _
+})()
+
+export default ECEF
\ No newline at end of file
diff --git a/applications/task-work-order/src/utils/cesium/kmz.js b/applications/task-work-order/src/utils/cesium/kmz.js
index a6cf77f..79e56ea 100644
--- a/applications/task-work-order/src/utils/cesium/kmz.js
+++ b/applications/task-work-order/src/utils/cesium/kmz.js
@@ -209,6 +209,11 @@
 }
 
 
+/**
+ * 递归处理对象,移除 #text 键并返回其值,同时处理数值字符串到数字的转换
+ * @param {any} obj - 需要处理的对象或值
+ * @returns {any} 处理后的对象或值
+ */
 export function removeTextKey(obj) {
 	if (typeof obj !== 'object' || obj === null) {
 		// 如果是数值字符串,转换为数值
@@ -219,18 +224,21 @@
 	}
 
 	if (Array.isArray(obj)) {
+		// 递归处理数组中的每个元素
 		return obj.map(item => removeTextKey(item));
 	}
 
 	if (Object.prototype.hasOwnProperty.call(obj, '#text')) {
-		// 如果 #text 的值是数值字符串,转换为数值
+		// 如果存在 #text 属性,返回其值(如果是数值字符串则转换为数字)
 		const textValue = obj['#text'];
 		return typeof textValue === 'string' && /^-?\d+(\.\d+)?$/.test(textValue) ? Number(textValue) : textValue;
 	}
 
+	// 递归处理对象属性
 	const newObj = {};
 	for (const key in obj) {
 		if (Object.prototype.hasOwnProperty.call(obj, key)) {
+			// 如果属性值是空对象,则设为空字符串;否则递归处理
 			newObj[key] = Object.keys(obj[key]).length === 0 ? '' : removeTextKey(obj[key]);
 		}
 	}
diff --git a/applications/task-work-order/src/utils/cesium/mapUtil.js b/applications/task-work-order/src/utils/cesium/mapUtil.js
index eabf803..89bf787 100644
--- a/applications/task-work-order/src/utils/cesium/mapUtil.js
+++ b/applications/task-work-order/src/utils/cesium/mapUtil.js
@@ -1,7 +1,8 @@
 import * as Cesium from 'cesium'
 import * as turf from '@turf/turf'
-const { VITE_APP_BASE, VITE_APP_ENV, VITE_APP_REGION_URL } = import.meta.env
+import store from '@/store'
 
+const { VITE_APP_BASE, VITE_APP_ENV, VITE_APP_REGION_URL } = import.meta.env
 export const getLngLatDistance = (lat1, lng1, lat2, lng2) => {
 	const radLat1 = (lat1 * Math.PI) / 180.0
 	const radLat2 = (lat2 * Math.PI) / 180.0
@@ -190,6 +191,66 @@
 	const newArr = arr.sort((a, b) => a.distance - b.distance)
 	return newArr[0]
 }
+// 获取多边形内部数据
+export const samplePolygonInterior = (positions, samplingDistance) => {
+	if (!positions.length) return []
+	let minLng = Infinity,
+		maxLng = -Infinity
+	let minLat = Infinity,
+		maxLat = -Infinity
+	positions.forEach(pos => {
+		minLng = Math.min(minLng, pos.lng)
+		maxLng = Math.max(maxLng, pos.lng)
+		minLat = Math.min(minLat, pos.lat)
+		maxLat = Math.max(maxLat, pos.lat)
+	})
+
+	//  将 Cesium 多边形格式转为 Turf 格式
+	const turfPolygonCoords = positions.map(pos => [pos.lng, pos.lat])
+	turfPolygonCoords.push([positions[0].lng, positions[0].lat]) // 闭合多边形
+	const turfPolygon = turf.polygon([turfPolygonCoords])
+
+	//  生成网格点并判断是否在多边形内
+	const sampledPoints = []
+	for (let lng = minLng; lng <= maxLng; lng += samplingDistance) {
+		for (let lat = minLat; lat <= maxLat; lat += samplingDistance) {
+			// 创建 Turf 点
+			const turfPoint = turf.point([lng, lat])
+			// Turf 原生方法判断点是否在多边形内
+			if (turf.booleanPointInPolygon(turfPoint, turfPolygon)) {
+				sampledPoints.push({
+					lng: Number(lng.toFixed(6)),
+					lat: Number(lat.toFixed(6)),
+					height: 0,
+				})
+			}
+		}
+	}
+
+	return sampledPoints
+}
+// 获取多边形面内的最高点(相对地形高度)
+export const getPolygonHighestPoint = async (positions, viewer, samplingDistance = 0.0002) => {
+	try {
+		const interiorPoints = samplePolygonInterior(positions, samplingDistance, viewer)
+		const allPoints = [...positions, ...interiorPoints]
+		// 批量获取所有点的地形高度
+		const pointsWithTerrain = await getPointPositionsHeight(allPoints, viewer)
+		// console.log('所有点的地形高度', pointsWithTerrain)
+
+		// 地形高度(ASL)最高的点
+		const highestTerrainPoint = pointsWithTerrain.reduce(
+			(max, curr) => (curr.ASL > max.ASL ? curr : max),
+			pointsWithTerrain[0]
+		)
+
+		// console.log('地形最高点信息:', highestTerrainPoint)
+		return highestTerrainPoint
+	} catch (error) {
+		console.error('获取地形最高点失败:', error)
+		return null
+	}
+}
 
 // 获取当前经纬度地形数据
 export const getLnglatAltitude = (longitude, latitude, viewer) => {
@@ -285,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,
@@ -317,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 })))
 			})
 	})
 }
@@ -410,11 +460,15 @@
 	if (!Array.isArray(positionsData) || positionsData.length === 0) return
 
 	// 如果是一个点,加两个点方便后续生成外包围盒
-	if (positionsData.length === 1){
-		const [lon,lat,height] = positionsData[0]
-		positionsData = [[lon+0.001,lat+0.001,height],[lon-0.001,lat-0.001,height],[lon,lat,height]]
+	if (positionsData.length === 1) {
+		const [lon, lat, height = 0] = positionsData[0]
+		positionsData = [
+			[lon + 0.001, lat + 0.001, height],
+			[lon - 0.001, lat - 0.001, height],
+			[lon, lat, height],
+		]
 	}
-	const positions = positionsData.map(([lon, lat, height]) =>
+	const positions = positionsData.map(([lon, lat, height = 0]) =>
 		Cesium.Cartesian3.fromDegrees(Number(lon), Number(lat), Number(height || 0))
 	)
 
@@ -439,7 +493,7 @@
 			range: boundingSphere.radius * multiple,
 		},
 		complete: () => {
-			// 飞行完成后检查相机高度是否小于最高点,如果小于直接飞到最高点
+			// 飞行完成后检查相机高度是否小于最高点
 			const cameraHeight = Cesium.Cartographic.fromCartesian(viewer.camera.position).height
 			if (cameraHeight < maxHeight) {
 				viewer.camera.flyTo({
@@ -529,7 +583,7 @@
 
 // 辅助函数:创建旋转文字的画布
 export function createRotatedTextCanvas(time) {
-	const prefix = '预计'
+	const prefix = '预计到达'
 	const suffix = '分钟'
 	const canvas = document.createElement('canvas')
 	const context = canvas.getContext('2d')
@@ -537,7 +591,9 @@
 	context.font = 'bold 16px Source Han Sans CN'
 	// 测量各部分文本宽度
 	const prefixWidth = context.measureText(prefix).width
-	const timeWidth = context.measureText(time).width
+	let timeWidth = context.measureText(time).width
+	const timeWidthMore = `${time >= 10 ? 6 : 0}`
+	timeWidth = timeWidth + Number(timeWidthMore)
 	const suffixWidth = context.measureText(suffix).width
 	const totalWidth = prefixWidth + timeWidth + suffixWidth
 	const textHeight = 20 // 估算高度
@@ -550,17 +606,116 @@
 	context.textAlign = 'left' // 改为左对齐,方便分段绘制
 	// 清除画布
 	context.clearRect(0, 0, canvas.width, canvas.height)
-	// 绘制“预计”(白色)
+	// 绘制“预计到达”(白色)
 	context.fillStyle = '#FFFFFF'
 	context.fillText(prefix, 10, 15)
 	// 绘制时间(蓝色)
-	context.font = 'bold 30px Source Han Sans CN'
+	context.font = 'bold 24px Source Han Sans CN'
 	context.fillStyle = '#1EE7E7' // 亮蓝色
-	context.fillText(time, 10 + prefixWidth, 12)
+	// 设置宽度
+	context.textBaseline = 'middle'
+	context.fillText(time, 10 + prefixWidth, 14)
 	// 绘制“分钟”(白色)
 	context.font = 'bold 16px Source Han Sans CN'
 	context.fillStyle = '#FFFFFF'
-	context.fillText(suffix, 20 + prefixWidth + timeWidth, 15)
+	context.fillText(suffix, 14 + prefixWidth + timeWidth, 15)
 
 	return canvas
 }
+
+
+export const saveCurrentCameraPosition = viewer => {
+	const position = viewer?.camera.position.clone() // 相机位置(Cartesian3)
+	const heading = viewer?.camera.heading // 航向角(弧度)
+	const pitch = viewer?.camera.pitch // 俯仰角(弧度)
+	const roll = viewer?.camera.roll // 翻滚角(弧度)
+	store.commit('setCameraPosition', {
+		duration: 0,
+		destination: position,
+		orientation: { heading, pitch, roll },
+	})
+}
+
+// 连接地面线
+export function getDockPolyLine(data, viewer) {
+	if (!data.longitude || !data.latitude) return {}
+	return {
+		positions: new Cesium.CallbackProperty(() => {
+			const pointPosition = Cesium.Cartesian3.fromDegrees(+data.longitude, +data.latitude, +data.height)
+			if (!pointPosition) return []
+			// 获取点的位置
+			const cartographic = Cesium.Cartographic.fromCartesian(pointPosition)
+			// 获取地形高度
+			const terrainHeight = viewer.scene.globe.getHeight(cartographic) || 0
+			// 创建地面点位置
+			const groundPosition = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, terrainHeight)
+			return [pointPosition, groundPosition]
+		}, false),
+		width: 0.5,
+		material: Cesium.Color.WHITE,
+	}
+}
+
+// 飞向一个机巢中心
+export const flyDockCenter = (viewer, info) => {
+	const { longitude = 115, latitude = 28, height = 0 } = info
+	const position = Cesium.Cartesian3.fromDegrees(+longitude, +latitude, +height)
+	const droneEntity = viewer?.entities.add({ position: position, point: { pixelSize: 0 } })
+	viewer?.flyTo(droneEntity, {
+		offset: new Cesium.HeadingPitchRange(0, Cesium.Math.toRadians(-60), 22000),
+		duration: 0.5,
+		complete: () => {
+			viewer?.entities.remove(droneEntity)
+		},
+	})
+}
+
+// 在固定的经度上获得高度
+export function getHeightOnFixedLonLat(ray, pointParams, ellipsoid = Cesium.Ellipsoid.WGS84) {
+	const { longitude: lon, latitude: lat } = pointParams
+	// 1. 地表点(高度=0)
+	const p0 = Cesium.Cartesian3.fromDegrees(lon, lat, 0, ellipsoid)
+
+	// 2. 该点处的法线方向(up 向量,单位向量)
+	const up = ellipsoid.geodeticSurfaceNormal(p0, new Cesium.Cartesian3())
+
+	// 3. 射线起点和方向
+	const r0 = ray.origin // 相机位置
+	const rd = ray.direction // 射线方向(已归一化?不一定,但不影响)
+
+	// 4. 解:求直线 L(t) = p0 + t * up 与射线 R(s) = r0 + s * rd 的最近点
+	// 使用向量公式求两条直线的最近点参数 t
+	const w0 = Cesium.Cartesian3.subtract(p0, r0, new Cesium.Cartesian3())
+
+	const a = Cesium.Cartesian3.dot(up, up) // = 1(因为 up 是单位向量)
+	const b = Cesium.Cartesian3.dot(up, rd)
+	const c = Cesium.Cartesian3.dot(rd, rd) // = |rd|^2
+	const d = Cesium.Cartesian3.dot(up, w0)
+	const e = Cesium.Cartesian3.dot(rd, w0)
+
+	const denom = a * c - b * b
+	let t
+
+	if (Math.abs(denom) < Cesium.Math.EPSILON10) {
+		// 两直线平行,取 t = 0
+		t = 0
+	} else {
+		t = (b * e - c * d) / denom
+	}
+
+	// 5. 得到高度线上对应的点
+	const resultPoint = Cesium.Cartesian3.add(
+		p0,
+		Cesium.Cartesian3.multiplyByScalar(up, t, new Cesium.Cartesian3()),
+		new Cesium.Cartesian3()
+	)
+
+	// 6. 转回 Cartographic 获取精确高度
+	const carto = Cesium.Cartographic.fromCartesian(resultPoint, ellipsoid)
+	return {
+		cartesian: resultPoint,
+		height: carto.height,
+		longitude: Cesium.Math.toDegrees(carto.longitude),
+		latitude: Cesium.Math.toDegrees(carto.latitude),
+	}
+}
diff --git a/applications/task-work-order/src/utils/cesium/publicCesium.js b/applications/task-work-order/src/utils/cesium/publicCesium.js
index 7af504f..e0fe177 100644
--- a/applications/task-work-order/src/utils/cesium/publicCesium.js
+++ b/applications/task-work-order/src/utils/cesium/publicCesium.js
@@ -70,6 +70,7 @@
  * @param {boolean} [options.flyToContour=false] - 是否飞行到轮廓,默认为 false
  * @param {number} [options.multiple=1.4] - 范围倍数
  * @param {number} [options.dockOptions={}] - 机巢显示
+ * @param {array} [options.terrainLoadCallback] - 地形加载回调
  * @param {array} [options.boundaryChange] - 边界改变事件
  * @param {array} [options.boundaryColor] - 边界颜色
  * @param {array} [options.dockRangeType] - 机巢范围类型 1显示覆盖圆;2显示覆盖圈
@@ -92,15 +93,14 @@
 			dom,
 			flatMode = true,
 			terrain = false,
-			layerMode = 0,
+			layerMode = 17,
 			contour = true,
 			flyToContour = false,
 			multiple = 1.4,
-
 			dockOptions = {},
-
-			boundaryColor = '#7FFFD4',
 			boundaryChange,
+			terrainLoadCallback,
+			boundaryColor = '#7FFFD4',
 			dockRangeType = 1,
 			useDockHeight = false
 		} = options
@@ -120,6 +120,10 @@
 			baseLayer: false,
 			fullscreenButton: false,
 		}
+
+		// 尝试解决出现多个id相同的dom
+		const domIdList = document.querySelectorAll(`#${dom}`)
+		if (domIdList.length>1) domIdList[1].remove()
 
 		this.viewer = new Viewer(dom, viewerOptions)
 		this.viewerDom = dom
@@ -143,33 +147,47 @@
 		this.viewer?.cesiumWidget.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK) // 禁用双击
 
 		if (terrain) {
-			// 正则:第一位非0,第二位任意数字,后10位都是0
-			const re = /^[1-9]\d0{10}$/
-
-			let result = null
-
-			// 1️⃣先判断 areaCode
-			if (re.test(store.state.user.userInfo.detail.areaCode)) {
-				result = store.state.user.userInfo.detail.areaCode.slice(0, 6)
-			} else {
-				// 2️⃣如果不满足,从 ancestors 里找
-				const matchedItems = store.state.user.userInfo.detail.ancestors
-					.split(',')
-					.filter(item => re.test(item)) // 过滤出符合条件的
-
-				// 取第一个符合条件的前6位(可以改成 last one 看需求)
-				if (matchedItems.length > 0) {
-					result = matchedItems[0].slice(0, 6)
-				}
-			}
-
 			try {
-				Cesium.CesiumTerrainProvider.fromUrl(`${import.meta.env.VITE_APP_TERRAIN_URL}${result}`, {
-					requestVertexNormals: true, // 启用地形法线增强立体感
-					requestWaterMask: true, // 启用水体遮罩效果
-				}).then(terrainProvider => {
-					this.viewer.terrainProvider = terrainProvider
-				})
+				const noTerrainMechanism = ['1962779164650135554']
+				if (noTerrainMechanism.includes(store.state.user.userInfo?.deptId)) {
+					// 使用Cesium World Terrain
+					Cesium.createWorldTerrainAsync({
+						requestWaterMask: false,    // 请求水域效果
+						requestVertexNormals: true // 请求光照和地形法线
+					}).then(terrainProvider => {
+						this.viewer.terrainProvider = terrainProvider
+						terrainLoadCallback?.()
+					})
+				} else {
+					/*// 正则:第一位非0,第二位任意数字,后10位都是0
+					const re = /^[1-9]\d0{10}$/
+
+					let result = null
+
+					// 1️⃣先判断 areaCode
+					if (re.test(store.state.user.userInfo.detail.areaCode)) {
+						result = store.state.user.userInfo.detail.areaCode.slice(0, 6)
+					} else {
+						// 2️⃣如果不满足,从 ancestors 里找
+						const matchedItems = store.state.user.userInfo.detail.ancestors
+							.split(',')
+							.filter(item => re.test(item)) // 过滤出符合条件的
+
+						// 取第一个符合条件的前6位(可以改成 last one 看需求)
+						if (matchedItems.length > 0) {
+							result = matchedItems[0].slice(0, 6)
+						}
+					}
+
+					// 使用公司地形
+					Cesium.CesiumTerrainProvider.fromUrl(`${import.meta.env.VITE_APP_TERRAIN_URL}${result}`, {
+						requestVertexNormals: true, // 启用地形法线增强立体感
+						requestWaterMask: true, // 启用水体遮罩效果
+					}).then(terrainProvider => {
+						this.viewer.terrainProvider = terrainProvider
+						terrainLoadCallback?.()
+					})*/
+				}
 			} catch (error) {
 				console.error('地形加载失败:', error)
 			}
@@ -180,6 +198,12 @@
 		this.switchFlatMode(flatMode)
 		this.switchContour(contour)
 		flyToContour && this.flyToContour(contour)
+
+		if (Cesium.FeatureDetection.supportsImageRenderingPixelated()) {
+			let dpr = window.devicePixelRatio
+			while (dpr >= 2.0) dpr /= 2.0 // 避免过高缩放导致模糊
+			this.viewer.resolutionScale = dpr // 设置分辨率缩放比例
+		}
 	}
 
 	getViewer () {
@@ -188,53 +212,69 @@
 
 	// 销毁viewer
 	viewerDestroy (removeDom = true) {
+		if (!this.viewer) return
+		// // 👇 关键:先停止所有动态行为
+		// this.viewer.clock.shouldAnimate = false;
+		// this.viewer.scene.requestRenderMode = false;
+		// // 取消所有可能的 pending 渲染
+		// if (this.viewer.cesiumWidget) {
+		// 	this.viewer.cesiumWidget.render = () => {}; // 空函数覆盖
+		// }
+		// // 清理实体和图层
+		// this.viewer?.entities.removeAll();
+		// this.viewer?.imageryLayers.removeAll();
+		// // 清理数据源
+		// try {
+		// 	const dataSources = this.viewer.dataSources;
+		// 	while (dataSources.length > 0) {
+		// 		dataSources.remove(dataSources.get(0));
+		// 	}
+		// } catch (e) {
+		// 	console.warn('清理数据源时出错:', e);
+		// }
+		// // 清理场景中的图元
+		// try {
+		// 	this.viewer?.scene.primitives.removeAll();
+		// } catch (e) {
+		// 	console.warn('清理图元时出错:', e);
+		// }
+		// // 获取webgl上下文
+		// let gl = this.viewer?.scene.context._originalGLContext
+		// if (gl) {
+		// 	try {
+		// 		gl.canvas.width = 1
+		// 		gl.canvas.height = 1
+		// 		gl.getExtension("WEBGL_lose_context").loseContext()
+		// 	} catch (e) {
+		// 		console.warn('释放WebGL上下文时出错:', e)
+		// 	}
+		// 	gl = null
+		// }
+		this.viewer?.destroy() // 销毁Viewer实例
+		this.viewer = null
 		smallMpaCenterDistance = null
-
-		if (this.viewer) {
-			this.viewer?.entities.removeAll()
-			this.viewer?.imageryLayers.removeAll()
-			this.viewer?.dataSources.removeAll()
-
-
-			// try {
-			// 	this.viewer?.scene?.primitives?.removeAll()
-			// 	this.viewer?.entities?.removeAll()
-			// } catch (e) {
-			// 	console.warn('清理资源时出错:', e)
-
-			// 	console.log(this.viewer, 1111111111111)
-			// }
-
-			// 获取webgl上下文
-			let gl = this.viewer?.scene.context._originalGLContext
-			gl.canvas.width = 1
-			gl.canvas.height = 1
-			gl.getExtension("WEBGL_lose_context").loseContext()
-			gl = null
-			this.viewer?.destroy() // 销毁Viewer实例
-			this.viewer = null
-			var cesiumContainer = document.getElementById(this.viewerDom)
-			if (cesiumContainer && removeDom) {
-				cesiumContainer.remove() // 移除与地图相关的DOM元素
-				this.viewerDom = null
-			}
+		var cesiumContainer = document.getElementById(this.viewerDom)
+		if (cesiumContainer && removeDom) {
+			cesiumContainer.remove() // 移除与地图相关的DOM元素
+			this.viewerDom = null
 		}
 	}
 
 	setShowDock (sns) {
 		this.boundary?.setShowDock(sns)
 	}
-
+	setDockCoverColor (sns) {
+		this.boundary?.setDockCoverColor(sns)
+	}
 	// 切换轮廓显示
 	async switchContour (open) {
 		if (open) {
 			await this.boundary?.openContour()
-
 		} else {
 			this.boundary?.closeContour()
 		}
 	}
-	// // 飞向轮廓居中
+	// 飞向轮廓居中
 	flyToContour () {
 		this.boundary?.flyToBoundary()
 	}
@@ -257,7 +297,7 @@
 		this.globalBaseMapLayers?.forEach(item => {
 			if (item.mapLayer) item.mapLayer.show = false
 		})
-		// store.state.common.mapSetting.mode = mode
+		store.state.common.mapSetting.mode = mode
 		// 标准地图(天地图矢量)加载
 		if (mode === 0) {
 			mapLayers.push(
@@ -324,9 +364,7 @@
 			this.viewer?.imageryLayers.lowerToBottom(find.mapLayer)
 		})
 	}
-
 	// 设置或重置滤镜
-
 	setCurrentLayerFilter (data, mode) {
 		if (mode === 17) {
 			data.mapLayer.brightness = 0.6 // 亮度(默认值)
diff --git a/applications/task-work-order/src/utils/cesium/use-kmz-tsa.js b/applications/task-work-order/src/utils/cesium/use-kmz-tsa.js
new file mode 100644
index 0000000..62ba213
--- /dev/null
+++ b/applications/task-work-order/src/utils/cesium/use-kmz-tsa.js
@@ -0,0 +1,675 @@
+import _ from 'lodash'
+import JSZIP from 'jszip'
+import { JSONToXML } from './kmz'
+import { getDateFromTimestamp } from '@/utils/date'
+
+export default function kmzFile () {
+	// 云台信息
+	const droneModel = {
+		52: '0',
+		53: '1',
+		66: '0',
+		67: '1',
+		80: '0',
+		81: '1',
+	}
+	// 创建图斑航线
+	const create = async (waylineBasicInfo, lnglats, spotList) => {
+		try {
+			const date = new Date()
+			const username = window.localStorage.getItem('bs_username')
+			const updateTime = date.getTime()
+			const fileName = '图斑航线规划_' + date.getTime()
+			// 无人机机型、云台型号、航线高度
+			const { droneEnumValue, payloadEnumValue, height } = waylineBasicInfo
+			const defaultHeight = height || 100
+			// 起始点位 (参考点位-具体以机场为准)
+			const startPoint = lnglats[0]
+			const takeOffPoint = [startPoint[1], startPoint[0], 0].join(',')
+			// template.xml对象模版
+			const fileTemplate = {
+				author: { '#text': username },
+				createTime: { '#text': updateTime },
+				updateTime: { '#text': updateTime },
+				missionConfig: {
+					// 飞向首航点模式
+					flyToWaylineMode: { '#text': 'safely' },
+					// 航线结束动作
+					finishAction: { '#text': 'goHome' },
+					// 失控是否继续执行航线
+					exitOnRCLost: { '#text': 'goContinue' },
+					// 失控动作类型
+					executeRCLostAction: { '#text': 'goBack' },
+					// 安全起飞高度
+					takeOffSecurityHeight: { '#text': 20 },
+					// 参考起飞点
+					takeOffRefPoint: { '#text': takeOffPoint },
+					// 参考起飞点绝对高度
+					takeOffRefPointAGLHeight: { '#text': 0 },
+					// 全局航线过渡速度
+					globalTransitionalSpeed: { '#text': 10 },
+					// 全局返航高度
+					globalRTHHeight: { '#text': defaultHeight },
+					// 无人机参数
+					droneInfo: {
+						droneEnumValue: { '#text': droneEnumValue },
+						droneSubEnumValue: {
+							'#text': droneModel[payloadEnumValue],
+						},
+					},
+					// 云台参数
+					payloadInfo: {
+						payloadEnumValue: { '#text': payloadEnumValue },
+						payloadSubEnumValue: {
+							'#text': droneModel[payloadEnumValue],
+						},
+						payloadPositionIndex: { '#text': 0 },
+					},
+				},
+				Folder: {
+					// 预定义模板类型 - 航点飞行
+					templateType: { '#text': 'waypoint' },
+					// 是否使用全局航线过渡速度
+					useGlobalTransitionalSpeed: { '#text': 0 },
+					templateId: { '#text': 0 },
+					// 坐标系参数信息
+					waylineCoordinateSysParam: {
+						// 经纬度坐标系 - 固定值
+						coordinateMode: { '#text': 'WGS84' },
+						// 航点高程参考平面	- 相对高度
+						heightMode: { '#text': 'relativeToStartPoint' },
+					},
+					// 全局航线飞行速度
+					autoFlightSpeed: { '#text': 10 },
+					// 云台俯仰角控制模式
+					gimbalPitchMode: { '#text': 'usePointSetting' },
+					// 全局高度
+					globalHeight: { '#text': '100' },
+					// 全局偏航角模式参数 - 可使用当前默认值
+					globalWaypointHeadingParam: {
+						waypointHeadingMode: { '#text': 'followWayline' },
+						waypointHeadingAngle: { '#text': 0 },
+						waypointPoiPoint: {
+							'#text': '0.000000,0.000000,0.000000',
+						},
+						waypointHeadingPathMode: { '#text': 'followBadArc' },
+						waypointHeadingPoiIndex: { '#text': 0 },
+					},
+					// 全局航点类型 - 直线飞行,飞行器到点停
+					globalWaypointTurnMode: {
+						'#text': 'toPointAndStopWithDiscontinuityCurvature',
+					},
+					// 全局航段轨迹是否尽量贴合直线
+					globalUseStraightLine: { '#text': 1 },
+					// 航点点位信息 - 位置、事件等
+					Placemark: [],
+					// payloadParam: {
+					//   payloadPositionIndex: { '#text': 0 },
+					//   focusMode: { '#text': 'firstPoint' },
+					//   meteringMode: { '#text': 'average' },
+					//   returnMode: { '#text': 'singleReturnFirst' },
+					//   samplingRate: { '#text': '240000' },
+					//   scanningMode: { '#text': 'repetitive' },
+					//   imageFormat: { '#text': 'wide,zoom' },
+					// },
+				},
+			}
+			// 航点创建
+			const polygonNo = {
+				no: '',
+				index: 0,
+			}
+			lnglats.forEach((lnglat, index) => {
+				const obj = spotList.find(spot => spot.dkfw.find(item => _.isEqual(item, lnglat)))
+				let fileSuffix = ''
+				if (obj) {
+					if (polygonNo.no === obj.dkbh) {
+						polygonNo.index++
+					} else {
+						polygonNo.no = obj.dkbh
+						polygonNo.index = 1
+					}
+					fileSuffix = `图斑航线拍摄(${obj.dkbh}-${polygonNo.index})`
+				} else {
+					fileSuffix = '图斑航线拍摄(起点)'
+				}
+				const placemark = {
+					Point: {
+						// 航点经纬度
+						coordinates: { '#text': `${lnglat[0]},${lnglat[1]}` },
+					},
+					// 航点index
+					index: { '#text': index },
+					// 全局航线高度(椭球高)
+					ellipsoidHeight: { '#text': defaultHeight },
+					// 全局航线高度 (EGM96海拔高/相对起飞点高度/AGL相对地面高度)
+					height: { '#text': defaultHeight },
+					// 航点飞行速度
+					waypointSpeed: { '#text': 10 },
+					// 沿航线方向 - 可使用当前默认值
+					waypointHeadingParam: {
+						waypointHeadingMode: { '#text': 'followWayline' },
+						waypointHeadingAngle: { '#text': 0 },
+						waypointPoiPoint: {
+							'#text': '0.000000,0.000000,0.000000',
+						},
+						waypointHeadingPathMode: { '#text': 'followBadArc' },
+						waypointHeadingPoiIndex: { '#text': 0 },
+					},
+					// 航点类型
+					waypointTurnParam: {
+						waypointTurnMode: {
+							'#text': 'toPointAndStopWithDiscontinuityCurvature',
+						},
+						waypointTurnDampingDist: { '#text': 0.2 },
+					},
+					// 是否全局参数
+					useGlobalHeight: { '#text': 1 },
+					useGlobalSpeed: { '#text': 1 },
+					useGlobalHeadingParam: { '#text': 1 },
+					useGlobalTurnParam: { '#text': 1 },
+					useStraightLine: { '#text': 1 },
+					// 事件组
+					actionGroup: {
+						actionGroupId: { '#text': index },
+						actionGroupStartIndex: { '#text': index },
+						actionGroupEndIndex: { '#text': index },
+						actionGroupMode: { '#text': 'sequence' },
+						actionTrigger: {
+							actionTriggerType: { '#text': 'reachPoint' },
+						},
+						// 单个事件 - 如果是多个事件action可改成数组 action: [{}]
+						action: {
+							actionId: { '#text': 0 },
+							actionActuatorFunc: { '#text': 'takePhoto' },
+							actionActuatorFuncParam: {
+								fileSuffix: { '#text': fileSuffix },
+								payloadPositionIndex: { '#text': '0' },
+								useGlobalPayloadLensIndex: { '#text': '1' },
+							},
+						},
+					},
+				}
+				fileTemplate.Folder.Placemark.push(placemark)
+			})
+			const fileWaylines = waylinesContext(fileTemplate)
+
+			return {
+				fileName,
+				template: fileTemplate,
+				waylines: fileWaylines,
+			}
+		} catch (error) {
+			console.log(error)
+			return false
+		}
+	}
+
+	// 创建正常航线
+	const createNoramlWaylines = (waylineBasicInfo, lnglats) => {
+		try {
+			const date = new Date()
+			const username = window.localStorage.getItem('bs_username')
+			const updateTime = date.getTime()
+			const { year, month, day, hours, minutes } = getDateFromTimestamp(updateTime)
+			const fileName = '新建航线_' + year + month + day + hours + minutes
+			// 无人机机型、云台型号、航线高度
+			const { droneEnumValue, payloadEnumValue, height, waylineName } = waylineBasicInfo
+			const defaultHeight = height || 100
+			// 起始点位 (参考点位-具体以机场为准)
+			const startPoint = lnglats[0]
+			const takeOffPoint = [startPoint.latitude, startPoint.longitude, startPoint.height].join(',')
+			// template.xml对象模版
+			const fileTemplate = {
+				author: { '#text': username },
+				createTime: { '#text': updateTime },
+				updateTime: { '#text': updateTime },
+				missionConfig: {
+					// 飞向首航点模式
+					flyToWaylineMode: { '#text': 'safely' },
+					// 航线结束动作
+					finishAction: { '#text': 'goHome' },
+					// 失控是否继续执行航线
+					exitOnRCLost: { '#text': 'goContinue' },
+					// 失控动作类型
+					executeRCLostAction: { '#text': 'goBack' },
+					// 安全起飞高度
+					takeOffSecurityHeight: { '#text': 20 },
+					// 参考起飞点
+					takeOffRefPoint: { '#text': takeOffPoint },
+					// 参考起飞点绝对高度
+					takeOffRefPointAGLHeight: { '#text': 0 },
+					// 全局航线过渡速度
+					globalTransitionalSpeed: { '#text': 10 },
+					// 全局返航高度
+					globalRTHHeight: { '#text': defaultHeight },
+					// 无人机参数
+					droneInfo: {
+						droneEnumValue: { '#text': droneEnumValue },
+						droneSubEnumValue: {
+							'#text': droneModel[payloadEnumValue],
+						},
+					},
+					// 是否自动绕行(暂未验证M30T机型使用此参数后会不会无法起飞)
+					autoRerouteInfo: {
+						missionAutoRerouteMode: { '#text': 1 },
+						transitionalAutoRerouteMode: { '#text': 1 },
+					},
+					// 云台参数
+					payloadInfo: {
+						payloadEnumValue: { '#text': payloadEnumValue },
+						payloadSubEnumValue: {
+							'#text': droneModel[payloadEnumValue],
+						},
+						payloadPositionIndex: { '#text': 0 },
+					},
+				},
+				Folder: {
+					// 预定义模板类型 - 航点飞行
+					templateType: { '#text': 'waypoint' },
+					// 是否使用全局航线过渡速度
+					useGlobalTransitionalSpeed: { '#text': 0 },
+					templateId: { '#text': 0 },
+					// 坐标系参数信息
+					waylineCoordinateSysParam: {
+						// 经纬度坐标系 - 固定值
+						coordinateMode: { '#text': 'WGS84' },
+						// 航点高程参考平面	- 相对高度
+						heightMode: { '#text': 'relativeToStartPoint' },
+					},
+					// 全局航线飞行速度
+					autoFlightSpeed: { '#text': 10 },
+					// 云台俯仰角控制模式
+					gimbalPitchMode: { '#text': 'usePointSetting' },
+					// 全局高度
+					globalHeight: { '#text': '100' },
+					// 全局偏航角模式参数 - 可使用当前默认值
+					globalWaypointHeadingParam: {
+						waypointHeadingMode: { '#text': 'followWayline' },
+						waypointHeadingAngle: { '#text': 0 },
+						waypointPoiPoint: {
+							'#text': '0.000000,0.000000,0.000000',
+						},
+						waypointHeadingPathMode: { '#text': 'followBadArc' },
+						waypointHeadingPoiIndex: { '#text': 0 },
+					},
+					// 全局航点类型 - 直线飞行,飞行器到点停
+					globalWaypointTurnMode: {
+						'#text': 'toPointAndStopWithDiscontinuityCurvature',
+					},
+					// 全局航段轨迹是否尽量贴合直线
+					globalUseStraightLine: { '#text': 1 },
+					// 航点点位信息 - 位置、事件等
+					Placemark: [],
+					payloadParam: {
+						payloadPositionIndex: { '#text': 0 },
+						focusMode: { '#text': 'firstPoint' },
+						meteringMode: { '#text': 'average' },
+						returnMode: { '#text': 'singleReturnFirst' },
+						samplingRate: { '#text': '240000' },
+						scanningMode: { '#text': 'repetitive' },
+						imageFormat: { '#text': 'wide,zoom' },
+					},
+				},
+			}
+			lnglats.forEach((lnglat, index) => {
+				const placemark = {
+					Point: {
+						// 航点经纬度
+						coordinates: {
+							'#text': `${lnglat.longitude},${lnglat.latitude}`,
+						},
+					},
+					// 航点index
+					index: { '#text': index },
+					// 全局航线高度(椭球高)
+					ellipsoidHeight: { '#text': lnglat.height },
+					// 全局航线高度 (EGM96海拔高/相对起飞点高度/AGL相对地面高度)
+					height: { '#text': lnglat.height },
+					// 航点飞行速度
+					waypointSpeed: { '#text': lnglat?.speed || 10 },
+					// 沿航线方向 - 可使用当前默认值
+					waypointHeadingParam: {
+						waypointHeadingMode: { '#text': 'followWayline' },
+						waypointHeadingAngle: { '#text': 0 },
+						waypointPoiPoint: {
+							'#text': '0.000000,0.000000,0.000000',
+						},
+						waypointHeadingPathMode: { '#text': 'followBadArc' },
+						waypointHeadingPoiIndex: { '#text': 0 },
+					},
+					// 航点类型
+					waypointTurnParam: {
+						waypointTurnMode: {
+							'#text': 'toPointAndStopWithDiscontinuityCurvature',
+						},
+						waypointTurnDampingDist: { '#text': 0.2 },
+					},
+					// 是否全局参数
+					useGlobalHeight: { '#text': 1 },
+					useGlobalSpeed: { '#text': 0 },
+					useGlobalHeadingParam: { '#text': 1 },
+					useGlobalTurnParam: { '#text': 1 },
+					useStraightLine: { '#text': 1 },
+				}
+				if (lnglat?.actions) {
+					const actionGroup = {
+						actionGroupId: { '#text': index },
+						actionGroupStartIndex: { '#text': index },
+						actionGroupEndIndex: { '#text': index },
+						actionGroupMode: { '#text': 'sequence' },
+						actionTrigger: {
+							actionTriggerType: { '#text': 'reachPoint' },
+						},
+						// 单个事件 - 如果是多个事件action可改成数组 action: [{}]
+						// action: [{
+						//   actionId: { '#text': 0 },
+						//   actionActuatorFunc: { '#text': '' },
+						//   actionActuatorFuncParam: {},
+						// }],
+						action: [],
+					}
+					lnglat?.actions.forEach((action, index) => {
+						const singleAction = {
+							actionId: { '#text': index },
+							actionActuatorFunc: { '#text': '' },
+							actionActuatorFuncParam: {},
+						}
+						// actionGroup.action['actionActuatorFunc'] = { '#text': action.key }
+						// action.params.forEach(p => {
+						//   if (p.value || p.value !== '') {
+						//     actionGroup.action['actionActuatorFuncParam'][p.key] = { '#text': p.value }
+						//   }
+						// })
+						singleAction['actionActuatorFunc'] = {
+							'#text': action.key,
+						}
+						action.params.forEach(p => {
+							if (p.value || p.value !== '') {
+								singleAction['actionActuatorFuncParam'][p.key] = {
+									'#text': p.value,
+								}
+							}
+						})
+						if (lnglat?.actions.length > 1) {
+							actionGroup.action.push(singleAction)
+						} else {
+							actionGroup.action = singleAction
+						}
+					})
+					placemark.actionGroup = actionGroup
+				}
+				fileTemplate.Folder.Placemark.push(placemark)
+			})
+			const fileWaylines = waylinesContext(fileTemplate)
+			return {
+				fileName: waylineName || fileName,
+				template: fileTemplate,
+				waylines: fileWaylines,
+			}
+		} catch (error) {
+			console.log(error)
+			return error
+		}
+	}
+
+	// 创建面状航线
+	const createPlanarWayline = (waylineBasicInfo, lnglats) => { }
+
+	// 创建点状航线
+	const createPointWayLines = (waylineBasicInfo, points, settingInfo, dockPosition) => {
+		try {
+			const { droneEnumValue, payloadEnumValue, waylineName } = waylineBasicInfo
+
+			const {
+				imageFormat,
+				heightMode,
+				globalHeight,
+				autoFlightSpeed,
+				gimbalPitchMode,
+				waypointTurnMode,
+				waypointHeadingMode,
+				finishAction,
+				globalTransitionalSpeed,
+			} = settingInfo
+
+			const date = new Date()
+			const username = window.localStorage.getItem('bs_username')
+			const updateTime = date.getTime()
+			const { year, month, day, hours, minutes } = getDateFromTimestamp(updateTime)
+			const fileName = '新建航线_' + year + month + day + hours + minutes
+			// 无人机机型、云台型号、航线高度
+
+			const defaultHeight = 100
+			// 起始点位 (参考点位-具体以机场为准)
+			const takeOffPoint = [dockPosition.latitude, dockPosition.longitude, 0].join(',')
+
+			// template.xml对象模版
+			const fileTemplate = {
+				author: { '#text': username },
+				createTime: { '#text': updateTime },
+				updateTime: { '#text': updateTime },
+				missionConfig: {
+					// 飞向首航点模式
+					flyToWaylineMode: { '#text': 'safely' },
+					// 航线结束动作
+					finishAction: { '#text': finishAction || 'goHome' },
+					// 失控是否继续执行航线
+					exitOnRCLost: { '#text': 'executeLostAction' },
+					// 失控动作类型
+					executeRCLostAction: { '#text': 'goBack' },
+					// 安全起飞高度
+					takeOffSecurityHeight: { '#text': 120 },
+					// 参考起飞点
+					takeOffRefPoint: { '#text': takeOffPoint },
+					// 参考起飞点绝对高度
+					takeOffRefPointAGLHeight: { '#text': 0 },
+					// 全局航线过渡速度
+					globalTransitionalSpeed: { '#text': globalTransitionalSpeed || 10 },
+					// 全局返航高度
+					globalRTHHeight: { '#text': defaultHeight },
+					// 无人机参数
+					droneInfo: {
+						droneEnumValue: { '#text': droneEnumValue },
+						droneSubEnumValue: {
+							'#text': droneModel[payloadEnumValue],
+						},
+					},
+					// 是否自动绕行(暂未验证M30T机型使用此参数后会不会无法起飞)
+					autoRerouteInfo: {
+						missionAutoRerouteMode: { '#text': 1 },
+						transitionalAutoRerouteMode: { '#text': 1 },
+					},
+					// 云台参数
+					payloadInfo: {
+						payloadEnumValue: { '#text': payloadEnumValue },
+						payloadSubEnumValue: {
+							'#text': droneModel[payloadEnumValue],
+						},
+						payloadPositionIndex: { '#text': 0 },
+					},
+				},
+				Folder: {
+					// 预定义模板类型 - 航点飞行
+					templateType: { '#text': 'waypoint' },
+					// 是否使用全局航线过渡速度
+					useGlobalTransitionalSpeed: { '#text': 0 },
+					templateId: { '#text': 0 },
+					// 坐标系参数信息
+					waylineCoordinateSysParam: {
+						// 经纬度坐标系 - 固定值
+						coordinateMode: { '#text': 'WGS84' },
+						// 航点高程参考平面	- 相对高度
+						heightMode: heightMode,
+					},
+					// 全局航线飞行速度
+					autoFlightSpeed: { '#text': autoFlightSpeed || 10 },
+					// 云台俯仰角控制模式
+					gimbalPitchMode: { '#text': gimbalPitchMode || 'manual' },
+					// 全局高度
+					globalHeight: { '#text': globalHeight || '100' },
+					// 全局偏航角模式参数 - 可使用当前默认值
+					globalWaypointHeadingParam: {
+						waypointHeadingMode: { '#text': waypointHeadingMode || 'followWayline' },
+						waypointHeadingAngle: { '#text': 0 },
+						waypointPoiPoint: {
+							'#text': '0.000000,0.000000,0.000000',
+						},
+						waypointHeadingPathMode: { '#text': 'followBadArc' },
+						waypointHeadingPoiIndex: { '#text': 0 },
+					},
+					// 全局航点类型 - 直线飞行,飞行器到点停
+					globalWaypointTurnMode: {
+						'#text': waypointTurnMode || 'toPointAndStopWithDiscontinuityCurvature',
+					},
+					// 全局航段轨迹是否尽量贴合直线
+					globalUseStraightLine: { '#text': 1 },
+					// 航点点位信息 - 位置、事件等
+					Placemark: [],
+					payloadParam: {
+						payloadPositionIndex: { '#text': 0 },
+						focusMode: { '#text': 'firstPoint' },
+						meteringMode: { '#text': 'average' },
+						returnMode: { '#text': 'singleReturnFirst' },
+						samplingRate: { '#text': '240000' },
+						scanningMode: { '#text': 'repetitive' },
+						imageFormat: imageFormat,
+					},
+				},
+			}
+			points.length &&
+				points.forEach((detail, ind) => {
+					const { Point, index, height, ellipsoidHeight, actionGroup, waypointSpeed } = detail
+
+					const placemark = {
+						Point,
+						// 航点index
+						index,
+						// 全局航线高度(椭球高)
+						ellipsoidHeight,
+						// 全局航线高度 (EGM96海拔高/相对起飞点高度/AGL相对地面高度)
+						height,
+						// 航点飞行速度
+						waypointSpeed: { '#text': waypointSpeed || 10 },
+						// 沿航线方向 - 可使用当前默认值
+						waypointHeadingParam: {
+							waypointHeadingMode: {
+								'#text': waypointHeadingMode || 'followWayline',
+							},
+							waypointHeadingAngle: { '#text': 0 },
+							waypointPoiPoint: {
+								'#text': '0.000000,0.000000,0.000000',
+							},
+							waypointHeadingPathMode: { '#text': 'followBadArc' },
+							waypointHeadingPoiIndex: { '#text': 0 },
+						},
+						// 航点类型
+						waypointTurnParam: {
+							waypointTurnMode: {
+								'#text': waypointTurnMode || 'toPointAndStopWithDiscontinuityCurvature',
+							},
+							waypointTurnDampingDist: { '#text': 0.2 },
+						},
+						// 是否全局参数
+						useGlobalHeight: { '#text': 1 },
+						useGlobalSpeed: { '#text': 0 },
+						useGlobalHeadingParam: { '#text': 1 },
+						useGlobalTurnParam: { '#text': 1 },
+						useStraightLine: { '#text': 1 },
+					}
+					if (actionGroup) {
+						placemark.actionGroup = actionGroup
+					}
+
+					fileTemplate.Folder.Placemark.push(placemark)
+				})
+
+			const fileWaylines = waylinesContext(fileTemplate)
+			return {
+				fileName: waylineName || fileName,
+				template: fileTemplate,
+				waylines: fileWaylines,
+			}
+		} catch (error) {
+			console.log(error)
+			return error
+		}
+	}
+
+	const save = async fileInfo => {
+		const JsZip = new JSZIP()
+		const { fileName, template, waylines } = fileInfo
+		const templateXml = JSONToXML(template, '', true)
+		const waylinesWpml = JSONToXML(waylines, '', true)
+		JsZip.file('wpmz/template.kml', templateXml)
+		JsZip.file('wpmz/waylines.wpml', waylinesWpml)
+		const fileBlob = await JsZip.generateAsync({ type: 'blob' })
+		// saveAs(kmzFile, `${fileName}.kmz`)
+		return fileBlob
+	}
+
+	const waylinesContext = templateJson => {
+		const waylinesObj = {
+			// 标准模版
+			missionConfig: {
+				flyToWaylineMode: null,
+				finishAction: null,
+				exitOnRCLost: null,
+				executeRCLostAction: null,
+				takeOffSecurityHeight: null,
+				globalTransitionalSpeed: null,
+				globalRTHHeight: null,
+				droneInfo: null,
+				payloadInfo: null,
+			},
+			Folder: {
+				templateId: null,
+				executeHeightMode: null,
+				waylineId: null,
+				autoFlightSpeed: null,
+				/*
+		  waylines.wpml和template.xml的Placemark通用
+		  不同点:
+			waylineId = templateId
+			ellipsoidHeight、 height 替换成 executeHeight
+		*/
+				Placemark: null,
+			},
+		}
+		Object.keys(waylinesObj).forEach(key => {
+			if (Object.prototype.toString.call(waylinesObj[key]) === '[object Object]') {
+				Object.keys(waylinesObj[key]).forEach(item => {
+					waylinesObj[key][item] = templateJson[key][item]
+					if (item === 'executeHeightMode') {
+						waylinesObj[key][item] = templateJson[key].waylineCoordinateSysParam.heightMode
+					}
+					if (item === 'waylineId') {
+						waylinesObj[key][item] = templateJson[key].templateId
+					}
+					if (item === 'Placemark') {
+						const placemarks = _.cloneDeep(templateJson[key][item])
+						placemarks.forEach(placemark => {
+							placemark.executeHeight = {
+								'#text': placemark.ellipsoidHeight?.['#text'] || '',
+							}
+							delete placemark.ellipsoidHeight
+							delete placemark.height
+						})
+						waylinesObj[key][item] = placemarks
+					}
+				})
+			} else {
+				waylinesObj[key] = templateJson[key]
+			}
+		})
+		return waylinesObj
+	}
+
+	return {
+		create,
+		createNoramlWaylines,
+		createPlanarWayline,
+		createPointWayLines,
+		save,
+	}
+}
diff --git a/applications/task-work-order/src/utils/cesium/useBoundary.js b/applications/task-work-order/src/utils/cesium/useBoundary.js
index d04ffb9..053ddd6 100644
--- a/applications/task-work-order/src/utils/cesium/useBoundary.js
+++ b/applications/task-work-order/src/utils/cesium/useBoundary.js
@@ -6,19 +6,17 @@
 import { useStore } from 'vuex'
 import { getDeviceRegion, getDeviceRegionCount } from '@/api/home/aggregation'
 import userStore from '@/store/modules/user'
-import { areaCodeToArr, getContourByCode } from '@/utils/cesium/mapUtil'
-// import { getEllipse } from '@/hooks/useSingleDroneMap/useSingleDroneMap'
+import { areaCodeToArr, getContourByCode, getDockPolyLine, getLnglatAltitude } from '@/utils/cesium/mapUtil'
 import getBaseConfig from '@/buildConfig/config'
 import { getDroneStatusImage } from '@/utils/stateToImageMap/drone'
-import {
-	GroundCirclePrimitiveManager
-} from '@/utils/mapUtils'
+import { GroundCirclePrimitiveManager } from '@/utils/mapUtils'
+
+import lowBattery from '@/assets/images/aiNowFly/low-battery.svg'
+import mediumBattery from '@/assets/images/aiNowFly/medium-battery.svg'
+import fullBattery from '@/assets/images/aiNowFly/full-charge.svg'
 
 const { VITE_APP_BASE, VITE_APP_ENV, VITE_APP_REGION_URL } = import.meta.env
-// const { singleDockSystem } = getBaseConfig()
 const { singleDockSystem } = getBaseConfig()
-
-
 
 /**
  * 使用通用的边界
@@ -26,8 +24,15 @@
  * @param options
  */
 export const useBoundary = (viewer, options = {}) => {
-	const { multiple = 1.4, dockOptions = {}, boundaryChange, boundaryColor, dockRangeType, useDockHeight = false } = options
-	const { scrollShowDock = false, showDock = false } = dockOptions
+	const {
+		multiple = 1.4,
+		dockOptions = {},
+		boundaryChange,
+		boundaryColor,
+		dockRangeType,
+		useDockHeight = false,
+	} = options
+	const { scrollShowDock = false, showDock = false, showDockNameAndBattery = false } = dockOptions
 
 	const manager = new GroundCirclePrimitiveManager()
 	manager.init(viewer)
@@ -56,7 +61,7 @@
 	}
 
 	// 获取设备列表
-	async function getDeviceRegionFun () {
+	async function getDeviceRegionFun() {
 		const res = await getDeviceRegion({ areaCode: selectedAreaCode })
 		return res?.data?.data || []
 	}
@@ -100,60 +105,127 @@
 		return await res.json()
 	}
 
+	function getBase64Image(imgUrl) {
+		const img = new Image()
+		const canvas = document.createElement('canvas')
+		const ctx = canvas.getContext('2d')
+
+		return new Promise(resolve => {
+			img.onload = function () {
+				canvas.width = img.width
+				canvas.height = img.height
+				ctx.drawImage(img, 0, 0)
+				const base64 = canvas.toDataURL('image/png')
+				resolve(base64)
+			}
+			img.src = imgUrl
+		})
+	}
+
+	function batteryLevelSvg(batteryNum) {
+		if (batteryNum <= 30) {
+			return lowBattery
+		} else if (batteryNum > 30 && batteryNum <= 60) {
+			return mediumBattery
+		} else {
+			return fullBattery
+		}
+	}
+
+	async function nestSVG(nickname, batteryImgUrl, batteryNum) {
+		let txt = nickname.length < 4 ? 130 : nickname.length * 38
+		let tranx = nickname.length < 4 ? 86 : nickname.length * 26 + 3
+		const batteryBase64Image = await getBase64Image(batteryImgUrl)
+		const svg = `
+		  <svg width="${txt}" height="100" xmlns="http://www.w3.org/2000/svg">
+			<g transform="translate(${tranx}, 10)">
+				<!-- 电池图标 -->
+				<image href="${batteryBase64Image}" x="0" y="2" width="12" height="13"/>
+				<!-- 电量值 -->
+				<text x="12" y="10" font-size="14" fill="${
+					batteryNum <= 30 ? '#FF604B' : batteryNum >= 60 ? '#40FF5C' : '#00FFF2'
+				}"  text-anchor="start" dominant-baseline="middle" font-weight="bolder" font-family="Source Han Sans CN" text-rendering="geometricPrecision">
+					${batteryNum}%
+				</text>
+			</g>
+		  </svg>
+		`
+		return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`
+	}
+
 	// 无人机散点渲染
 	const droneSplashed = () => {
-		let showDockList = showDockSnList === null
-			? _.cloneDeep(initDockList)
-			: initDockList.filter(item => showDockSnList.includes(item.device_sn))
+		manager.removeAll()
+		let showDockList =
+			showDockSnList === null
+				? _.cloneDeep(initDockList)
+				: initDockList.filter(item => showDockSnList.includes(item.device_sn))
 
-		showDockList.forEach((item, index) => {
+		showDockList.forEach(async (item, index) => {
+			if (item.status === 'OFFLINE') return
 			let polyline = {}
 
 			if (dockRangeType === 2) {
 				manager.addCircleOutline({
 					data: {
 						lng: item.longitude,
-						lat: item.latitude
+						lat: item.latitude,
 					},
-					frameColor: Cesium.Color.fromCssColorString('#7FFFD4')
+					frameColor: Cesium.Color.fromCssColorString('#7FFFD4'),
 				})
 			} else {
 				manager.addCircle({
 					data: {
 						lng: item.longitude,
-						lat: item.latitude
+						lat: item.latitude,
 					},
-					materialColor: Cesium.Color.CORNFLOWERBLUE.withAlpha(0.3)
+					materialColor: item.color ? item.color : Cesium.Color.CORNFLOWERBLUE.withAlpha(0.3),
 				})
 			}
 
 			if (useDockHeight) {
-				polyline = {
-					positions: new Cesium.CallbackProperty(() => {
-						const pointPosition = Cesium.Cartesian3.fromDegrees(+item.longitude, +item.latitude, +item.height)
-						if (!pointPosition) return []
-						// 获取点的位置
-						const cartographic = Cesium.Cartographic.fromCartesian(pointPosition)
-						// 获取地形高度
-						const terrainHeight = viewer.scene.globe.getHeight(cartographic) || 0
-						// 创建地面点位置
-						const groundPosition = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, terrainHeight)
-
-						return [pointPosition, groundPosition]
-					}, false),
-					width: 0.5,
-					material: Cesium.Color.WHITE,
-				}
+				polyline = getDockPolyLine(item, viewer)
 			}
-
+			const batteryImgUrl = batteryLevelSvg(item.capacity_percent)
+			const combinedImage = await nestSVG(item.nickname, batteryImgUrl, item.capacity_percent)
+			let pointObj = {}
+			try {
+				pointObj = await getLnglatAltitude(item.longitude, item.latitude, viewer)
+			} catch (e) {}
+			const position = Cesium.Cartesian3.fromDegrees(
+				+item.longitude,
+				+item.latitude,
+				useDockHeight ? +item.height : pointObj?.height || 0
+			)
+			if (showDockNameAndBattery) {
+				// 带电量
+				dockSource.entities.add({
+					position,
+					billboard: {
+						// new Cesium.ConstantProperty(pointData.status === 'OFFLINE' ? endingImg : unSelectedOnline),
+						image: combinedImage,
+					},
+				})
+			}
 			dockSource.entities.add({
-				position: Cesium.Cartesian3.fromDegrees(+item.longitude, +item.latitude, !useDockHeight ? 0 : +item.height),
+				label: {
+					text: item.nickname,
+					font: 'bold 16px Source Han Sans CN',
+					fillColor: Cesium.Color.WHITE,
+					outlineColor: Cesium.Color.BLACK,
+					outlineWidth: 2,
+					style: Cesium.LabelStyle.FILL_AND_OUTLINE,
+					verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
+					pixelOffset: new Cesium.Cartesian2(0, -24),
+				},
+				position,
 				polyline,
 				billboard: {
 					image: getDroneStatusImage(item.status),
 					width: 60,
 					height: 60,
-					eyeOffset: new Cesium.Cartesian3(0, 0, -5)
+					disableDepthTestDistance: Number.POSITIVE_INFINITY,
+					eyeOffset: new Cesium.Cartesian3(0, 0, -5),
 				},
 			})
 		})
@@ -186,7 +258,7 @@
 							positions: positions,
 							width: 5, // 直接设置宽度
 							clampToGround: true,
-							material: Cesium.Color.fromCssColorString(boundaryColor)
+							material: Cesium.Color.fromCssColorString(boundaryColor),
 						},
 						polygon,
 					})
@@ -205,7 +277,7 @@
 						positions: positions,
 						width: 1, // 直接设置宽度
 						clampToGround: true,
-						material: Cesium.Color.fromCssColorString(boundaryColor)
+						material: Cesium.Color.fromCssColorString(boundaryColor),
 					},
 				})
 			})
@@ -217,7 +289,6 @@
 	// 打开边界
 	const openContour = async () => {
 		const areaCode = selectedAreaCode || userAreaCode
-
 		viewer.scene.postRender.removeEventListener(determineScaling)
 		if (!areaCode) return
 		const hierarchy = areaCodeToArr(areaCode.slice(0, 6))
@@ -258,25 +329,29 @@
 			const outlineGJson = await getContourByCode(areaCode.slice(0, 6))
 			scalingJudgment.forEach(item => item.show && (item.outline = outlineGJson))
 			renderOutline(scalingJudgment[(hierarchy.length - 3) * -1])
-		} catch (e) { }
-		viewer.scene.postRender.addEventListener(determineScaling)
+		} catch (e) {}
+		try {
+			viewer?.scene?.postRender?.addEventListener(determineScaling)
+		}catch (e) {
+
+		}
 	}
 
 	// 关闭边界
-	function closeContour () {
+	function closeContour() {
 		removeAllEntities()
 		viewer.scene.postRender.removeEventListener(determineScaling)
 	}
 
 	// 飞向边界
-	async function flyToBoundary () {
+	async function flyToBoundary() {
 		// 单机巢系统
 		if (singleDockSystem) {
 			if (!initDockList.length) {
 				initDockList = await getDeviceRegionFun()
 			}
 			viewer?.camera.flyTo({
-				destination: Cesium.Cartesian3.fromDegrees(initDockList[0].longitude, initDockList[0].latitude, 24000),
+				destination: Cesium.Cartesian3.fromDegrees(initDockList[0].longitude, initDockList[0].latitude, 22000),
 				duration: 0,
 			})
 			return
@@ -286,7 +361,8 @@
 		const dataSource = await Cesium.GeoJsonDataSource.load(gJson)
 		if (!dataSource) return
 		viewer.dataSources.add(dataSource)
-
+		// 获取多边形边界所有点
+		let positionList = []
 		dataSource.entities.values.forEach(function (entity) {
 			if (entity.polygon) {
 				// 获取多边形的边界球
@@ -295,18 +371,16 @@
 					let cartographic = Cesium.Cartographic.fromCartesian(item)
 					let lng = Cesium.Math.toDegrees(cartographic.longitude) // 经度
 					let lat = Cesium.Math.toDegrees(cartographic.latitude) // 纬度
-
 					return [_.round(lng, 6), _.round(lat, 6)]
 				})
-
-				const newBox = boxTransformScale(curPolygonPosition, multiple)
-
-				viewer.camera.flyTo({
-					destination: Cesium.Rectangle.fromDegrees(...newBox),
-					offset: new Cesium.HeadingPitchRange(0, Cesium.Math.toRadians(-90), 0),
-					duration: 0.5,
-				})
+				positionList = positionList.concat(curPolygonPosition)
 			}
+		})
+		const newBox = boxTransformScale(positionList, multiple)
+		viewer.camera.flyTo({
+			destination: Cesium.Rectangle.fromDegrees(...newBox),
+			offset: new Cesium.HeadingPitchRange(0, Cesium.Math.toRadians(-90), 0),
+			duration: 0.5,
 		})
 
 		dataSource.entities.values.forEach(entity => {
@@ -320,7 +394,7 @@
 	 * 设置要显示的机巢列表
 	 * @param dockSns - 要显示的机巢设备序列号数组。如果为 undefined,则显示所有已初始化的机巢;如果为空数组,则不显示任何机巢。
 	 */
-	function setShowDock (dockSns) {
+	function setShowDock(dockSns) {
 		if (dockSns === undefined) {
 			showDockSnList = null
 		} else if (dockSns.length === 0) {
@@ -328,21 +402,31 @@
 		} else {
 			showDockSnList = dockSns
 		}
-
 		removeDockCover()
-
-		if (active === '县') droneSplashed()
+		if (!showDock) return
+		if (scrollShowDock ? active === '县' : true) {
+			droneSplashed()
+		}
 	}
-
+	function setDockCoverColor(arr) {
+		initDockList.forEach(item => {
+			arr.forEach(dock => {
+				item.color = dock.device_sn === item.device_sn ? dock.color : null
+			})
+		})
+		if (scrollShowDock ? active === '县' : true) {
+			droneSplashed()
+		}
+	}
 	onBeforeUnmount(() => {
 		manager.destroy()
 	})
-
 
 	return {
 		openContour,
 		closeContour,
 		flyToBoundary,
-		setShowDock
+		setShowDock,
+		setDockCoverColor,
 	}
 }

--
Gitblit v1.9.3