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/useBoundary.js | 210 ++++++++++++++++++++++++++++++++++++---------------
1 files changed, 147 insertions(+), 63 deletions(-)
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