huweili-cesium 1.3.3 → 1.3.4

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.
@@ -1,152 +1,175 @@
1
1
  <template>
2
- <!-- WebSocket连接状态指示器:动态挂载到 Cesium 自动生成的 .cesium-viewer -->
3
- <Teleport v-if="cesiumViewerEl" :to="cesiumViewerEl">
4
- <div class="ws-drone-indicator">
5
- <div class="ws-drone-indicator-dot" :class="wsIndicatorClass"></div>
6
- </div>
7
- </Teleport>
8
- </template>
9
-
10
- <script setup>
11
- /**
12
- * WsIndicator.vue - WebSocket连接状态指示器组件
13
- *
14
- * 组件功能:
15
- * - 实时显示WebSocket连接状态
16
- * - 提供可视化的连接状态反馈
17
- *
18
- * 状态说明:
19
- * - 断开连接时红色高亮(.disconnected)
20
- * - 连接正常时微弱绿色(.connected)
21
- * - 收到无人机数据时绿色高亮闪烁(.active)
22
- */
23
- import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue'
24
- import { useEventBus } from '../../js/utils/useEventBus.js'
25
-
26
- const { on, off } = useEventBus()
27
-
28
- // Cesium Viewer 自动生成的 DOM 容器
29
- const cesiumViewerEl = ref(null)
30
-
31
- // WebSocket连接状态
32
- const wsConnected = ref(false)
33
- // 无人机数据接收高亮状态(用于闪烁效果)
34
- const wsDroneIndicatorActive = ref(false)
35
-
36
- /**
37
- * 计算指示器的CSS类名
38
- * 根据连接状态和数据接收状态返回对应的类名
39
- */
40
- const wsIndicatorClass = computed(() => {
41
- if (!wsConnected.value) {
42
- return 'disconnected' // 断开连接时红色高亮
43
- }
44
- if (wsDroneIndicatorActive.value) {
45
- return 'active' // 收到无人机数据时绿色高亮
46
- }
47
- return 'connected' // 连接正常但无数据时微弱绿色
48
- })
49
-
50
- /**
51
- * 处理无人机数据接收事件
52
- * 收到数据时设置连接状态为true,并触发高亮闪烁
53
- */
54
- const handleWsDroneDataReceived = () => {
55
- wsConnected.value = true // 收到数据说明连接正常
56
- wsDroneIndicatorActive.value = true // 触发绿色高亮
57
- // 300ms后恢复微弱绿色状态
58
- setTimeout(() => {
59
- wsDroneIndicatorActive.value = false
60
- }, 300)
2
+ <!-- WebSocket连接状态指示器:挂载到当前地图容器内的 .cesium-viewer -->
3
+ <Teleport v-if="cesiumViewerEl" :to="cesiumViewerEl">
4
+ <div class="ws-drone-indicator">
5
+ <div class="ws-drone-indicator-dot" :class="wsIndicatorClass"></div>
6
+ </div>
7
+ </Teleport>
8
+ </template>
9
+
10
+ <script setup>
11
+ /**
12
+ * WsIndicator.vue - WebSocket连接状态指示器组件
13
+ *
14
+ * 状态说明:
15
+ * - 断开连接时红色高亮(.disconnected)
16
+ * - 连接正常时微弱绿色(.connected)
17
+ * - 收到无人机数据时绿色高亮闪烁(.active)
18
+ */
19
+ import { ref, computed, watch, onMounted, onUnmounted } from 'vue'
20
+ import { useEventBus } from '../../js/utils/useEventBus.js'
21
+
22
+ const props = defineProps({
23
+ /** 地图根容器 ref(index.vue 中的 cesium-container) */
24
+ container: {
25
+ type: Object,
26
+ default: null,
27
+ },
28
+ })
29
+
30
+ const { on, off } = useEventBus()
31
+
32
+ const cesiumViewerEl = ref(null)
33
+ const wsConnected = ref(false)
34
+ const wsDroneIndicatorActive = ref(false)
35
+
36
+ let viewerObserver = null
37
+ let resolveTimer = null
38
+
39
+ const wsIndicatorClass = computed(() => {
40
+ if (!wsConnected.value) {
41
+ return 'disconnected'
61
42
  }
62
-
63
- /**
64
- * 处理WebSocket连接成功事件
65
- */
66
- const handleWsConnected = () => {
67
- wsConnected.value = true
43
+ if (wsDroneIndicatorActive.value) {
44
+ return 'active'
68
45
  }
69
-
70
- /**
71
- * 处理WebSocket断开连接事件
72
- */
73
- const handleWsDisconnected = () => {
74
- wsConnected.value = false
46
+ return 'connected'
47
+ })
48
+
49
+ const resolveViewerElement = () => {
50
+ const root = props.container?.value
51
+ if (!root) return null
52
+ return root.querySelector('.cesium-viewer')
53
+ }
54
+
55
+ const bindViewerElement = () => {
56
+ const viewer = resolveViewerElement()
57
+ if (!viewer) return false
58
+ cesiumViewerEl.value = viewer
59
+ return true
60
+ }
61
+
62
+ const stopResolveViewerElement = () => {
63
+ viewerObserver?.disconnect()
64
+ viewerObserver = null
65
+ if (resolveTimer) {
66
+ clearTimeout(resolveTimer)
67
+ resolveTimer = null
75
68
  }
76
-
77
- /**
78
- * 组件挂载时订阅事件
79
- */
80
- onMounted(async () => {
81
- await nextTick()
82
- cesiumViewerEl.value = document.querySelector('.cesium-viewer')
83
-
84
- // 订阅无人机数据接收事件
85
- on('wsDroneDataReceived', handleWsDroneDataReceived)
86
- // 订阅WebSocket连接成功事件
87
- on('wsConnected', handleWsConnected)
88
- // 订阅WebSocket断开连接事件
89
- on('wsDisconnected', handleWsDisconnected)
90
-
91
- // 检查全局连接状态(处理组件挂载前已连接的情况)
92
- if (window.wsConnected) {
93
- wsConnected.value = true
69
+ }
70
+
71
+ const startResolveViewerElement = () => {
72
+ stopResolveViewerElement()
73
+ if (bindViewerElement()) return
74
+
75
+ const root = props.container?.value
76
+ if (!root) return
77
+
78
+ viewerObserver = new MutationObserver(() => {
79
+ if (bindViewerElement()) {
80
+ stopResolveViewerElement()
94
81
  }
95
82
  })
96
-
97
- /**
98
- * 组件卸载时取消订阅,避免内存泄漏
99
- */
100
- onUnmounted(() => {
101
- off('wsDroneDataReceived', handleWsDroneDataReceived)
102
- off('wsConnected', handleWsConnected)
103
- off('wsDisconnected', handleWsDisconnected)
104
- })
105
- </script>
106
-
107
- <style scoped lang="scss">
108
- /* WebSocket指示器容器 */
109
- .ws-drone-indicator {
110
- position: absolute;
111
- left: 0px;
112
- bottom: 0px;
113
- width: 13px;
114
- height: 13px;
115
- border-radius: 3px;
116
- background: rgba(40, 40, 40, 0.7);
117
- display: flex;
118
- align-items: center;
119
- justify-content: center;
120
- z-index: 2147483647;
121
- pointer-events: none;
122
- }
123
-
124
- /* 指示器圆点基础样式 */
125
- .ws-drone-indicator-dot {
126
- width: 4px;
127
- height: 4px;
128
- border-radius: 50%;
129
- background: #0f3;
130
- opacity: 0.25;
131
- transition: opacity 0.08s ease, box-shadow 0.08s ease;
132
- }
133
-
134
- /* 连接正常状态 - 微弱绿色 */
135
- .ws-drone-indicator-dot.connected {
136
- opacity: 0.3;
137
- box-shadow: none;
138
- }
139
-
140
- /* 收到数据状态 - 绿色高亮 */
141
- .ws-drone-indicator-dot.active {
142
- opacity: 1;
143
- box-shadow: 0 0 6px #0f3;
144
- }
145
-
146
- /* 断开连接状态 - 红色高亮 */
147
- .ws-drone-indicator-dot.disconnected {
148
- background: #f33;
149
- opacity: 1;
150
- box-shadow: 0 0 6px #f33;
83
+ viewerObserver.observe(root, { childList: true, subtree: true })
84
+
85
+ // 地图初始化较慢时兜底重试,避免 Teleport 目标一直为空
86
+ resolveTimer = window.setTimeout(() => {
87
+ bindViewerElement()
88
+ stopResolveViewerElement()
89
+ }, 10000)
90
+ }
91
+
92
+ const handleWsDroneDataReceived = () => {
93
+ wsConnected.value = true
94
+ wsDroneIndicatorActive.value = true
95
+ setTimeout(() => {
96
+ wsDroneIndicatorActive.value = false
97
+ }, 300)
98
+ }
99
+
100
+ const handleWsConnected = () => {
101
+ wsConnected.value = true
102
+ }
103
+
104
+ const handleWsDisconnected = () => {
105
+ wsConnected.value = false
106
+ }
107
+
108
+ watch(
109
+ () => props.container?.value,
110
+ () => {
111
+ cesiumViewerEl.value = null
112
+ startResolveViewerElement()
113
+ },
114
+ { immediate: true },
115
+ )
116
+
117
+ onMounted(() => {
118
+ on('wsDroneDataReceived', handleWsDroneDataReceived)
119
+ on('wsConnected', handleWsConnected)
120
+ on('wsDisconnected', handleWsDisconnected)
121
+
122
+ if (window.wsConnected) {
123
+ wsConnected.value = true
151
124
  }
152
- </style>
125
+ })
126
+
127
+ onUnmounted(() => {
128
+ stopResolveViewerElement()
129
+ off('wsDroneDataReceived', handleWsDroneDataReceived)
130
+ off('wsConnected', handleWsConnected)
131
+ off('wsDisconnected', handleWsDisconnected)
132
+ })
133
+ </script>
134
+
135
+ <style scoped lang="scss">
136
+ .ws-drone-indicator {
137
+ position: absolute;
138
+ left: 0px;
139
+ bottom: 0px;
140
+ width: 13px;
141
+ height: 13px;
142
+ border-radius: 3px;
143
+ background: rgba(40, 40, 40, 0.7);
144
+ display: flex;
145
+ align-items: center;
146
+ justify-content: center;
147
+ z-index: 2147483647;
148
+ pointer-events: none;
149
+ }
150
+
151
+ .ws-drone-indicator-dot {
152
+ width: 4px;
153
+ height: 4px;
154
+ border-radius: 50%;
155
+ background: #0f3;
156
+ opacity: 0.25;
157
+ transition: opacity 0.08s ease, box-shadow 0.08s ease;
158
+ }
159
+
160
+ .ws-drone-indicator-dot.connected {
161
+ opacity: 0.3;
162
+ box-shadow: none;
163
+ }
164
+
165
+ .ws-drone-indicator-dot.active {
166
+ opacity: 1;
167
+ box-shadow: 0 0 6px #0f3;
168
+ }
169
+
170
+ .ws-drone-indicator-dot.disconnected {
171
+ background: #f33;
172
+ opacity: 1;
173
+ box-shadow: 0 0 6px #f33;
174
+ }
175
+ </style>
package/index.vue CHANGED
@@ -1,7 +1,7 @@
1
1
  <template>
2
2
  <div class="cesium-map-wrapper">
3
3
  <div class="cesium-container" ref="cesiumContainer" />
4
- <WsIndicator />
4
+ <WsIndicator :container="cesiumContainer" />
5
5
  </div>
6
6
  </template>
7
7
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "huweili-cesium",
3
- "version": "1.3.3",
3
+ "version": "1.3.4",
4
4
  "description": "基于 Cesium 的地图工具库(无人机态势、轨迹、围栏、工具栏等)",
5
5
  "type": "module",
6
6
  "main": "./index.js",