huweili-cesium 1.3.4 → 1.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -46,8 +46,22 @@ const wsIndicatorClass = computed(() => {
46
46
  return 'connected'
47
47
  })
48
48
 
49
+ /** 兼容 template 传入的 HTMLElement 与 ref 对象两种形态 */
50
+ const getContainerRoot = () => {
51
+ const container = props.container
52
+ if (!container) return null
53
+ if (typeof HTMLElement !== 'undefined' && container instanceof HTMLElement) {
54
+ return container
55
+ }
56
+ const refValue = container.value
57
+ if (typeof HTMLElement !== 'undefined' && refValue instanceof HTMLElement) {
58
+ return refValue
59
+ }
60
+ return null
61
+ }
62
+
49
63
  const resolveViewerElement = () => {
50
- const root = props.container?.value
64
+ const root = getContainerRoot()
51
65
  if (!root) return null
52
66
  return root.querySelector('.cesium-viewer')
53
67
  }
@@ -72,7 +86,7 @@ const startResolveViewerElement = () => {
72
86
  stopResolveViewerElement()
73
87
  if (bindViewerElement()) return
74
88
 
75
- const root = props.container?.value
89
+ const root = getContainerRoot()
76
90
  if (!root) return
77
91
 
78
92
  viewerObserver = new MutationObserver(() => {
@@ -106,7 +120,7 @@ const handleWsDisconnected = () => {
106
120
  }
107
121
 
108
122
  watch(
109
- () => props.container?.value,
123
+ () => getContainerRoot(),
110
124
  () => {
111
125
  cesiumViewerEl.value = null
112
126
  startResolveViewerElement()
package/js/groundLink.js CHANGED
@@ -60,12 +60,16 @@ export function groundLinkConfig() {
60
60
 
61
61
  // 检查是否已存在连接线
62
62
  if (mapStore.hasGroundLink(pointId, mapId)) {
63
- // 连接线已存在,只更新地面点位经纬度(确保位置同步)
63
+ // 连接线已存在,同步地面点位经纬度与状态颜色
64
64
  modelEntity.groundLinkPosition = {
65
65
  lng: options.GROUND_LINK_LNG,
66
66
  lat: options.GROUND_LINK_LAT
67
67
  }
68
- console.warn(`无人机${pointId}的地面连接线已存在,仅更新位置,mapId: ${mapId || 'default'}`)
68
+ updateGroundLinkStatus({
69
+ pointId,
70
+ status: options.status ?? DroneStatus.WHITELIST,
71
+ mapId,
72
+ })
69
73
  return
70
74
  }
71
75
 
@@ -184,9 +188,36 @@ export function groundLinkConfig() {
184
188
  }
185
189
  }
186
190
 
191
+ /**
192
+ * 增量更新无人机与地面固定点连线颜色
193
+ * @param {Object} options
194
+ * @param {string} options.pointId 连接线 ID(如 drone-fly-line-${uavId})
195
+ * @param {number} options.status 无人机状态
196
+ * @param {string} [options.mapId] 地图 ID
197
+ */
198
+ const updateGroundLinkStatus = (options = {}) => {
199
+ const { pointId, status, mapId } = options
200
+ if (!pointId) return false
201
+
202
+ const groundLinkEntity = mapStore.getGroundLink(pointId, mapId)
203
+ if (!groundLinkEntity?.polyline) return false
204
+
205
+ const statusConfig = DroneStatusMap[status] || DroneStatusMap[DroneStatus.WHITELIST]
206
+ const groundLinkColor = Cesium.Color.fromCssColorString(statusConfig.groundLinkColor)
207
+
208
+ groundLinkEntity.polyline.material = new Cesium.PolylineDashMaterialProperty({
209
+ color: groundLinkColor,
210
+ dashLength: 12,
211
+ gapColor: groundLinkColor.withAlpha(0.01),
212
+ })
213
+
214
+ return true
215
+ }
216
+
187
217
  return {
188
218
  createAndMoveGroundLink,
189
219
  clearGroundLink,
190
- toggleGroundLinkVisibility
220
+ toggleGroundLinkVisibility,
221
+ updateGroundLinkStatus
191
222
  }
192
223
  }
package/js/labelDiv.js CHANGED
@@ -105,6 +105,23 @@ function resolveInfoDisplay(info, cached) {
105
105
  return merged;
106
106
  }
107
107
 
108
+ /**
109
+ * 根据状态生成无人机头顶标牌文案
110
+ * @param {number} status
111
+ * @param {string} uavName
112
+ * @returns {string}
113
+ */
114
+ export function buildDroneLabelText(status, uavName) {
115
+ const statusConfig = DroneStatusMap[status] || DroneStatusMap[DroneStatus.WHITELIST]
116
+ if (status === DroneStatus.UNKNOWN) {
117
+ return statusConfig.description
118
+ }
119
+ if (status === DroneStatus.MACHINE_NEST) {
120
+ return '机巢无人机'
121
+ }
122
+ return `${statusConfig.description}: ${uavName || ''}`
123
+ }
124
+
108
125
  /**
109
126
  * 各种标签div合集
110
127
  */
@@ -435,7 +452,63 @@ export function labelDiv() {
435
452
 
436
453
  batchManager.container.appendChild(labelDiv);
437
454
 
455
+ const applyDetailStatusTheme = (nextStatus) => {
456
+ if (!detailDiv) return
457
+
458
+ const statusConfig = DroneStatusMap[nextStatus]
459
+ if (!statusConfig) return
460
+
461
+ detailDiv.style.setProperty('--drone-bg-color', statusConfig.backgroundColor)
462
+ detailDiv.style.setProperty('--drone-border-color', statusConfig.borderColor)
463
+ detailDiv.style.setProperty('--drone-text-color', statusConfig.textColor)
464
+ detailDiv.style.setProperty('--drone-detail-divider-color', statusConfig.detailDividerColor)
465
+
466
+ const badgeSpan = detailDiv.querySelector('.drone-badge')
467
+ if (badgeSpan) {
468
+ badgeSpan.style.backgroundImage = `url(${process.env.BASE_URL}${statusConfig.badgeImage})`
469
+ }
470
+
471
+ if (dragLineSvg) {
472
+ dragLineSvg.style.setProperty('--drone-drag-line-color', statusConfig.dragLineColor)
473
+ }
474
+ }
475
+
476
+ const updateStatus = (nextStatus, nextUavName) => {
477
+ const resolvedStatus = nextStatus ?? DroneStatus.WHITELIST
478
+ const resolvedName = nextUavName ?? labelItem.uavName ?? ''
479
+ labelItem.status = resolvedStatus
480
+ labelItem.uavName = resolvedName
481
+
482
+ const statusConfig = DroneStatusMap[resolvedStatus]
483
+ if (!statusConfig) return
484
+
485
+ const newText = buildDroneLabelText(resolvedStatus, resolvedName)
486
+ labelDiv.style.setProperty('--drone-label-bg-color', statusConfig.labelBgColor)
487
+
488
+ const childrenToKeep = []
489
+ if (detailDiv?.parentElement === labelDiv) childrenToKeep.push(detailDiv)
490
+ if (dragLineSvg?.parentElement === labelDiv) childrenToKeep.push(dragLineSvg)
491
+
492
+ while (labelDiv.firstChild) {
493
+ labelDiv.removeChild(labelDiv.firstChild)
494
+ }
495
+ labelDiv.appendChild(document.createTextNode(newText))
496
+ for (const el of childrenToKeep) {
497
+ labelDiv.appendChild(el)
498
+ }
499
+
500
+ applyDetailStatusTheme(resolvedStatus)
501
+
502
+ if (detailDiv) {
503
+ const titleEl = detailDiv.querySelector('.drone-detail-title')
504
+ if (titleEl) {
505
+ titleEl.textContent = resolvedName
506
+ }
507
+ }
508
+ }
509
+
438
510
  const batchEntry = {
511
+ updateStatus,
439
512
  updatePosition(hideByHeight) {
440
513
  if (!labelItem.map || !labelItem.map.scene) {
441
514
  labelDiv.style.display = 'none';
@@ -486,8 +559,7 @@ export function labelDiv() {
486
559
 
487
560
  batchManager.add(batchEntry);
488
561
 
489
- // 返回销毁函数
490
- return () => {
562
+ const destroy = () => {
491
563
  cancelAnimationFrame(rafId);
492
564
  rafId = 0;
493
565
  if (dragRafId) {
@@ -509,6 +581,8 @@ export function labelDiv() {
509
581
  batchManager.remove(batchEntry);
510
582
  labelDiv.remove();
511
583
  };
584
+
585
+ return { destroy, updateStatus };
512
586
  };
513
587
 
514
588
  return {
package/js/movePoint.js CHANGED
@@ -10,11 +10,11 @@
10
10
  import * as Cesium from 'cesium'
11
11
  import { useMapStore } from './stores/mapStore.js'
12
12
  import { useEventBus } from './utils/useEventBus.js'
13
- import { DroneStatus } from './config/index.js'
13
+ import { DroneStatus, DroneStatusMap } from './config/index.js'
14
14
  import { setPoint } from './setPoint'
15
+ import { setPath } from './setPath'
15
16
  import { getClickHandler } from './clickHandler'
16
17
  import { movePathConfig } from './movePath'
17
- import { setPath } from './setPath'
18
18
  import { pointGetPool } from './cardPool'
19
19
  import { scheduleRefreshFlyHandQRCode } from './qrCodeGenerator'
20
20
  import { groundLinkConfig } from './groundLink'
@@ -26,22 +26,25 @@ export function movePointConfig(baseUrl) {
26
26
  const mapStore = useMapStore()
27
27
  const {
28
28
  setDronePointByGlb,
29
+ updateFlyPointStatus,
29
30
  } = setPoint(baseUrl)
30
31
 
31
32
  const {
32
- moveDroneTrail,
33
- } = movePathConfig()
34
-
35
- const {
33
+ updateDroneTrailStatus,
36
34
  toggleDroneTrail,
37
- clearDroneTrail
35
+ clearDroneTrail,
38
36
  } = setPath()
39
37
 
40
38
  const {
41
39
  clearGroundLink,
42
- toggleGroundLinkVisibility
40
+ toggleGroundLinkVisibility,
41
+ updateGroundLinkStatus,
43
42
  } = groundLinkConfig()
44
43
 
44
+ const {
45
+ moveDroneTrail,
46
+ } = movePathConfig()
47
+
45
48
  const clickHandler = getClickHandler() // 获取点击处理器实例
46
49
 
47
50
  const { on } = useEventBus()
@@ -291,6 +294,61 @@ export function movePointConfig(baseUrl) {
291
294
  modelEntity.entity.position = positionProperty
292
295
  }
293
296
 
297
+ /**
298
+ * 增量更新无人机地图视觉状态(白名单/非白名单/未知切换)
299
+ * @param {Object} options
300
+ * @param {string} options.pointId 无人机 ID
301
+ * @param {string} [options.mapId] 地图 ID
302
+ * @param {number} options.status 无人机状态
303
+ * @param {string} [options.uavName] 无人机名称
304
+ * @param {string} [options.flyingHandName] 飞手姓名
305
+ * @param {string} [options.flyingHandPhone] 飞手电话
306
+ */
307
+ const updateDronePointStatus = (options = {}) => {
308
+ const {
309
+ pointId,
310
+ mapId,
311
+ status = DroneStatus.WHITELIST,
312
+ uavName,
313
+ flyingHandName,
314
+ flyingHandPhone,
315
+ } = options
316
+
317
+ if (!pointId) return false
318
+
319
+ const modelEntity = mapStore.getGraphicMap(pointId, mapId)
320
+ if (!modelEntity) return false
321
+
322
+ const statusConfig = DroneStatusMap[status] || DroneStatusMap[DroneStatus.WHITELIST]
323
+ const resolvedUavName = uavName ?? modelEntity.uavName ?? modelEntity.info?.uavName ?? ''
324
+
325
+ if (modelEntity.entity?.billboard) {
326
+ modelEntity.entity.billboard.image = baseUrl + (statusConfig.droneImagePath || '/images/new/drone.png')
327
+ }
328
+
329
+ if (typeof modelEntity.updateLabelStatus === 'function') {
330
+ modelEntity.updateLabelStatus(status, resolvedUavName)
331
+ }
332
+
333
+ updateDroneTrailStatus({ pointId, status, mapId })
334
+ updateGroundLinkStatus({
335
+ pointId: `drone-fly-line-${pointId}`,
336
+ status,
337
+ mapId,
338
+ })
339
+ updateFlyPointStatus({
340
+ id: `fly-${pointId}`,
341
+ status,
342
+ mapId,
343
+ name: flyingHandName ?? modelEntity.info?.flyingHandName,
344
+ phone: flyingHandPhone ?? modelEntity.info?.flyingHandPhone,
345
+ })
346
+
347
+ modelEntity.status = status
348
+ modelEntity.uavName = resolvedUavName
349
+ return true
350
+ }
351
+
294
352
  const moveDronePoint = (options) => {
295
353
  const { pointId, mapId, uavName } = options
296
354
  const map = mapStore.getMap(mapId)
@@ -352,6 +410,21 @@ export function movePointConfig(baseUrl) {
352
410
  info.distance = options.distance || ''
353
411
  info.receiveTime = options.receiveTime || ''
354
412
 
413
+ const prevStatus = modelEntity.status
414
+ if (prevStatus !== undefined && prevStatus !== status) {
415
+ updateDronePointStatus({
416
+ pointId,
417
+ mapId,
418
+ status,
419
+ uavName,
420
+ flyingHandName: options.flyingHandName,
421
+ flyingHandPhone: options.flyingHandPhone,
422
+ })
423
+ } else if (prevStatus === undefined) {
424
+ modelEntity.status = status
425
+ modelEntity.uavName = uavName
426
+ }
427
+
355
428
  const targetPosition = Cesium.Cartesian3.fromDegrees(lng, lat, height)
356
429
  snapDronePosition(modelEntity, map, targetPosition, currentTime)
357
430
 
@@ -506,6 +579,7 @@ export function movePointConfig(baseUrl) {
506
579
  movePointByGlb,
507
580
  movePointByGlbInfo,
508
581
  moveDronePoint,
582
+ updateDronePointStatus,
509
583
  toggleDroneAndTrailVisibility,
510
584
  destroyDroneTrail
511
585
  }
package/js/setPath.js CHANGED
@@ -118,9 +118,30 @@ export function setPath() {
118
118
  }
119
119
  }
120
120
 
121
+ /**
122
+ * 增量更新无人机轨迹颜色
123
+ * @param {Object} options
124
+ * @param {string} options.pointId 无人机 ID
125
+ * @param {number} options.status 无人机状态
126
+ * @param {string} [options.mapId] 地图 ID
127
+ */
128
+ const updateDroneTrailStatus = (options = {}) => {
129
+ const { pointId, status, mapId } = options
130
+ if (!pointId) return false
131
+
132
+ const trailData = mapStore.getDroneTrail(pointId, mapId)
133
+ if (!trailData?.entity?.polyline) return false
134
+
135
+ const statusConfig = DroneStatusMap[status] || DroneStatusMap[DroneStatus.WHITELIST]
136
+ trailData.entity.polyline.material = Cesium.Color.fromCssColorString(statusConfig.trailColor)
137
+ trailData.status = status
138
+ return true
139
+ }
140
+
121
141
  return {
122
142
  setDroneTrail,
123
143
  clearDroneTrail,
124
- toggleDroneTrail
144
+ toggleDroneTrail,
145
+ updateDroneTrailStatus
125
146
  }
126
147
  }
package/js/setPoint.js CHANGED
@@ -10,7 +10,7 @@
10
10
  import * as Cesium from 'cesium'
11
11
  import { useMapStore } from './stores/mapStore.js'
12
12
  import { setPath } from './setPath'
13
- import { labelDiv } from './labelDiv'
13
+ import { labelDiv, buildDroneLabelText } from './labelDiv'
14
14
  import { getClickHandler } from './clickHandler'
15
15
  import { DroneStatus, DroneStatusMap, MapInstanceIds } from './config/index.js'
16
16
  import { pointGetPool, PonitInfoCardPool } from './cardPool'
@@ -142,6 +142,9 @@ export function setPoint(baseUrl) {
142
142
 
143
143
  const result = {
144
144
  id,
145
+ status,
146
+ flyName: options.name || '--',
147
+ flyPhone: options.phone || '--',
145
148
  billboardCollection,
146
149
  billboard,
147
150
  show: showCard,
@@ -160,6 +163,57 @@ export function setPoint(baseUrl) {
160
163
  return result
161
164
  }
162
165
 
166
+ /**
167
+ * 增量更新飞手点位图标与信息牌主题色(白名单/非白名单切换)
168
+ * @param {Object} options
169
+ * @param {string} options.id 飞手点位 ID(如 fly-${uavId})
170
+ * @param {number} options.status 无人机状态
171
+ * @param {string} [options.mapId] 地图 ID
172
+ * @param {string} [options.name] 飞手姓名
173
+ * @param {string} [options.phone] 飞手电话
174
+ */
175
+ const updateFlyPointStatus = (options = {}) => {
176
+ const { id, status, mapId, name, phone } = options
177
+ if (!id) return false
178
+
179
+ const point = mapStore.getGraphicMap(id, mapId)
180
+ if (!point?.billboard) return false
181
+
182
+ const resolvedStatus = status ?? DroneStatus.WHITELIST
183
+ const statusConfig = DroneStatusMap[resolvedStatus] || DroneStatusMap[DroneStatus.WHITELIST]
184
+
185
+ point.status = resolvedStatus
186
+ if (name !== undefined) point.flyName = name
187
+ if (phone !== undefined) point.flyPhone = phone
188
+
189
+ point.billboard.image = new URL(
190
+ `${process.env.BASE_URL}${statusConfig.flyingHandImagePath}`,
191
+ import.meta.url
192
+ ).href
193
+
194
+ const map = mapStore.getMap(mapId)
195
+ if (!map) return true
196
+
197
+ const pool = pointGetPool(map, mapId)
198
+ const card = pool.getActiveCard(id)
199
+ if (card) {
200
+ const infoCard = card.querySelector('.info-card')
201
+ if (infoCard) {
202
+ infoCard.style.setProperty('--flying-hand-bg-color', statusConfig.flyingHandInfoBoardColor)
203
+ }
204
+ const nameEl = card.querySelector('.fly-name')
205
+ const phoneEl = card.querySelector('.fly-phone')
206
+ if (nameEl && name !== undefined) {
207
+ nameEl.textContent = `姓名:${name || '--'}`
208
+ }
209
+ if (phoneEl && phone !== undefined) {
210
+ phoneEl.textContent = `电话:${phone || '--'}`
211
+ }
212
+ }
213
+
214
+ return true
215
+ }
216
+
163
217
  /**
164
218
  * 设置点位 (通过提供的glb模型设置点位)
165
219
  * @param options 配置选项
@@ -439,16 +493,16 @@ export function setPoint(baseUrl) {
439
493
  getPosition: () => modelEntity.positionProperty.getValue(map.clock.currentTime),
440
494
  getInfo: () => modelEntity.info, // 后续移动时,真正把业务字段写进去, 在 @js/movePoint.js 里,移动无人机时会执行:
441
495
  getVisible: () => modelEntity.entity.show,
442
- text: status === DroneStatus.UNKNOWN
443
- ? statusConfig.description
444
- : status === DroneStatus.MACHINE_NEST
445
- ? '机巢无人机'
446
- : `${statusConfig.description}: ${uavName}`,
496
+ text: buildDroneLabelText(status, uavName),
447
497
  status,
448
498
  map,
449
499
  mapId
450
500
  })
451
501
 
502
+ modelEntity.status = status
503
+ modelEntity.uavName = uavName
504
+ modelEntity.updateLabelStatus = destroyDroneLabelDiv?.updateStatus
505
+
452
506
  if (type === 'png') {
453
507
  modelEntity.rippleEffect = createDroneRippleDom({
454
508
  map,
@@ -459,7 +513,7 @@ export function setPoint(baseUrl) {
459
513
  }
460
514
 
461
515
  modelEntity.destroy = () => {
462
- destroyDroneLabelDiv?.()
516
+ destroyDroneLabelDiv?.destroy?.()
463
517
  modelEntity.rippleEffect?.destroy?.()
464
518
  map.entities.remove(modelEntity.entity)
465
519
  }
@@ -652,6 +706,7 @@ export function setPoint(baseUrl) {
652
706
  setPointByGlb,
653
707
  setPointByGlbInfo,
654
708
  setDronePointByGlb,
709
+ updateFlyPointStatus,
655
710
  pickMapPointByClick,
656
711
  createPickedImagePoint,
657
712
  destroyPickedImagePoint
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "huweili-cesium",
3
- "version": "1.3.4",
3
+ "version": "1.3.6",
4
4
  "description": "基于 Cesium 的地图工具库(无人机态势、轨迹、围栏、工具栏等)",
5
5
  "type": "module",
6
6
  "main": "./index.js",