@ganwei-web/ganwei-pc-cli 6.2.9 → 6.3.2

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.
Files changed (71) hide show
  1. package/ganwei-iotcenter-index-6.2.3/configuration/moduleConfiguration.json +2 -0
  2. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/env.d.ts +8 -5
  3. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/index.html +1 -1
  4. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/public/static/css/ElementPlusAdapter.css +437 -68
  5. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/public/static/css/reset-6-1.css +1 -0
  6. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/public/static/css/reset-plus.css +396 -0
  7. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/public/static/http/createAxios.js +38 -13
  8. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/public/static/js/getLanguage.js +19 -12
  9. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/public/static/themes/dark-6-1.css +3 -3
  10. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/public/static/themes/green-6-1.css +12 -11
  11. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/App.vue +11 -6
  12. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/indexRightContent/headerRight/equipAlarmDialog/index.vue +6 -0
  13. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/indexRightContent/headerRight/expirationReminder/index.vue +7 -3
  14. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/indexRightContent/headerRight/index.scss +4 -0
  15. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/indexRightContent/headerRight/userInfo/index.js +35 -3
  16. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/indexRightContent/headerRight/userInfo/index.scss +7 -1
  17. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/indexRightContent/headerRight/userInfo/index.vue +2 -2
  18. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/layouts/Navigation/ContractMenu.vue +11 -37
  19. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/layouts/Navigation/TopNav.vue +3 -2
  20. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/layouts/Sidebar/LeftContent/index.vue +7 -1
  21. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/router.js +120 -18
  22. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/date.ts +80 -0
  23. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/dom.ts +99 -0
  24. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/env.ts +20 -0
  25. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/file.ts +74 -0
  26. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/index.ts +26 -0
  27. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/number.ts +83 -0
  28. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/performance.ts +69 -0
  29. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/storage.ts +80 -0
  30. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/string.ts +116 -0
  31. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/xss-filter.ts +260 -0
  32. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/views/jumpIframe/index.vue +45 -28
  33. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/tsconfig.json +1 -0
  34. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/vite.config.ts +10 -1
  35. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-login/public/static/http/createAxios.js +40 -15
  36. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-login/public/static/js/getLanguage.js +10 -2
  37. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-login/src/views/login.vue +55 -53
  38. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-login/src/views/ssoLogin.vue +10 -9
  39. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/.env.development +0 -3
  40. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/.env.production +1 -4
  41. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/.env.test +0 -3
  42. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/.eslintrc.cjs +2 -2
  43. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/env.d.ts +9 -5
  44. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/index.html +1 -0
  45. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/package.json +3 -2
  46. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/public/static/http/createAxios.js +38 -13
  47. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/public/static/js/getLanguage.js +10 -2
  48. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/App.vue +1 -1
  49. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/request/api.ts +0 -1
  50. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/request/models/response/template.ts +0 -0
  51. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/date.ts +79 -0
  52. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/dom.ts +99 -0
  53. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/env.ts +20 -0
  54. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/file.ts +74 -0
  55. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/index.ts +29 -0
  56. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/number.ts +83 -0
  57. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/performance.ts +69 -0
  58. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/signalr.ts +564 -0
  59. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/storage.ts +80 -0
  60. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/string.ts +116 -0
  61. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/xss-filter.ts +260 -0
  62. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/views/template.vue +0 -1
  63. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/tsconfig.json +7 -0
  64. package/ganwei-iotcenter-index-6.2.3/pnpm-lock.yaml +489 -155
  65. package/package.json +1 -1
  66. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/mixins/judgePermission.js +0 -60
  67. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/setStorage.js +0 -5
  68. package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/setStorage.js +0 -5
  69. /package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/build/{enteryJson.js → entryJson.js} +0 -0
  70. /package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/{request/models/request/index.ts → enum/template.ts} +0 -0
  71. /package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/request/models/{response/index.ts → request/template.ts} +0 -0
@@ -0,0 +1,564 @@
1
+ /**
2
+ * SignalR 实时通信工具类
3
+ * @version 1.0.0
4
+ * @description 基于 @aspnet/signalr ^1.1.4 的封装,提供连接管理、断线重连、消息订阅等功能
5
+ */
6
+
7
+ import * as signalR from '@aspnet/signalr'
8
+
9
+ /**
10
+ * SignalR 连接配置
11
+ */
12
+ export interface SignalRConfig {
13
+ /** Hub 路径 */
14
+ path: string
15
+ /** 连接标识(可选) */
16
+ connectionId?: string
17
+ /** 设备编号(可选) */
18
+ equipNo?: number | string
19
+ /** 服务器超时时间(毫秒),默认 500000000 */
20
+ serverTimeout?: number
21
+ /** 心跳间隔时间(毫秒),默认 500000000 */
22
+ keepAliveInterval?: number
23
+ /** 是否自动重连,默认 true */
24
+ autoReconnect?: boolean
25
+ /** 重连延迟(毫秒),默认 3000 */
26
+ reconnectDelay?: number
27
+ /** 最大重连次数,默认 5 */
28
+ maxReconnectAttempts?: number
29
+ }
30
+
31
+ /**
32
+ * 消息处理器
33
+ */
34
+ export interface MessageHandler {
35
+ /** 事件名称 */
36
+ eventName: string
37
+ /** 回调函数 */
38
+ callback: (data: any) => void
39
+ }
40
+
41
+ /**
42
+ * SignalR 连接状态
43
+ */
44
+ export enum ConnectionState {
45
+ /** 已连接 */
46
+ Connected = 'Connected',
47
+ /** 连接中 */
48
+ Connecting = 'Connecting',
49
+ /** 已断开 */
50
+ Disconnected = 'Disconnected',
51
+ /** 重连中 */
52
+ Reconnecting = 'Reconnecting'
53
+ }
54
+
55
+ /**
56
+ * SignalR 管理器类
57
+ */
58
+ export class SignalRManager {
59
+ private connection: signalR.HubConnection | null = null
60
+ private config: Required<SignalRConfig>
61
+ private reconnectAttempts = 0
62
+ private isReconnecting = false
63
+ private messageHandlers: Map<string, Set<Function>> = new Map()
64
+ private state: ConnectionState = ConnectionState.Disconnected
65
+ private stateChangeCallbacks: ((state: ConnectionState) => void)[] = []
66
+
67
+ constructor(config: SignalRConfig) {
68
+ this.config = {
69
+ path: config.path,
70
+ connectionId: config.connectionId || '',
71
+ equipNo: config.equipNo || '',
72
+ serverTimeout: config.serverTimeout || 500000000,
73
+ keepAliveInterval: config.keepAliveInterval || 500000000,
74
+ autoReconnect: config.autoReconnect !== false,
75
+ reconnectDelay: config.reconnectDelay || 3000,
76
+ maxReconnectAttempts: config.maxReconnectAttempts || 5
77
+ }
78
+ }
79
+
80
+ /**
81
+ * 获取连接状态
82
+ */
83
+ getState(): ConnectionState {
84
+ return this.state
85
+ }
86
+
87
+ /**
88
+ * 监听连接状态变化
89
+ */
90
+ onStateChange(callback: (state: ConnectionState) => void) {
91
+ this.stateChangeCallbacks.push(callback)
92
+ return () => {
93
+ const index = this.stateChangeCallbacks.indexOf(callback)
94
+ if (index > -1) {
95
+ this.stateChangeCallbacks.splice(index, 1)
96
+ }
97
+ }
98
+ }
99
+
100
+ /**
101
+ * 设置连接状态
102
+ */
103
+ private setState(state: ConnectionState) {
104
+ this.state = state
105
+ this.stateChangeCallbacks.forEach(cb => cb(state))
106
+ }
107
+
108
+ /**
109
+ * 建立连接
110
+ */
111
+ async connect(): Promise<signalR.HubConnection> {
112
+ // 如果已连接,先断开
113
+ if (this.connection) {
114
+ await this.disconnect()
115
+ }
116
+
117
+ this.setState(ConnectionState.Connecting)
118
+
119
+ try {
120
+ // 创建连接
121
+ this.connection = new signalR.HubConnectionBuilder()
122
+ .withUrl(this.config.path)
123
+ .build()
124
+
125
+ // 设置超时时间
126
+ this.connection.serverTimeoutInMilliseconds = this.config.serverTimeout
127
+ this.connection.keepAliveIntervalInMilliseconds = this.config.keepAliveInterval
128
+
129
+ // 监听断线事件
130
+ this.connection.onclose((error) => {
131
+ console.log('SignalR 连接断开:', error)
132
+ this.setState(ConnectionState.Disconnected)
133
+
134
+ // 自动重连
135
+ if (this.config.autoReconnect) {
136
+ this.handleReconnect()
137
+ }
138
+ })
139
+
140
+ // 启动连接
141
+ await this.connection.start()
142
+
143
+ // 发送连接标识
144
+ if (this.config.connectionId) {
145
+ await this.sendConnectionId()
146
+ }
147
+
148
+ // 重新订阅消息
149
+ this.resubscribeMessages()
150
+
151
+ this.setState(ConnectionState.Connected)
152
+ this.reconnectAttempts = 0
153
+
154
+ console.log('SignalR 连接成功:', this.config.path)
155
+ return this.connection
156
+ } catch (error) {
157
+ console.error('SignalR 连接失败:', error)
158
+ this.setState(ConnectionState.Disconnected)
159
+ throw error
160
+ }
161
+ }
162
+
163
+ /**
164
+ * 发送连接标识
165
+ */
166
+ private async sendConnectionId() {
167
+ if (!this.connection || !this.config.connectionId) return
168
+
169
+ try {
170
+ await this.connection.invoke(this.config.connectionId, this.config.equipNo)
171
+ } catch (error) {
172
+ console.error('发送连接标识失败:', error)
173
+ }
174
+ }
175
+
176
+ /**
177
+ * 处理重连
178
+ */
179
+ private async handleReconnect() {
180
+ if (this.isReconnecting) return
181
+ this.isReconnecting = true
182
+ this.setState(ConnectionState.Reconnecting)
183
+
184
+ while (this.reconnectAttempts < this.config.maxReconnectAttempts) {
185
+ this.reconnectAttempts++
186
+ console.log(
187
+ `SignalR 尝试重连 (${this.reconnectAttempts}/${this.config.maxReconnectAttempts})...`
188
+ )
189
+
190
+ try {
191
+ await this.connect()
192
+ console.log('SignalR 重连成功')
193
+ this.isReconnecting = false
194
+ return
195
+ } catch (error) {
196
+ console.error(`SignalR 重连失败:`, error)
197
+
198
+ if (this.reconnectAttempts < this.config.maxReconnectAttempts) {
199
+ await this.sleep(this.config.reconnectDelay)
200
+ }
201
+ }
202
+ }
203
+
204
+ console.error('SignalR 重连失败次数过多,停止重连')
205
+ this.isReconnecting = false
206
+ this.setState(ConnectionState.Disconnected)
207
+ }
208
+
209
+ /**
210
+ * 重新订阅消息
211
+ */
212
+ private resubscribeMessages() {
213
+ if (!this.connection) return
214
+
215
+ this.messageHandlers.forEach((callbacks, eventName) => {
216
+ callbacks.forEach(callback => {
217
+ this.connection!.on(eventName, callback as any)
218
+ })
219
+ })
220
+ }
221
+
222
+ /**
223
+ * 监听消息
224
+ */
225
+ on(eventName: string, callback: (data: any) => void): () => void {
226
+ // 添加到处理器列表
227
+ if (!this.messageHandlers.has(eventName)) {
228
+ this.messageHandlers.set(eventName, new Set())
229
+ }
230
+ this.messageHandlers.get(eventName)!.add(callback)
231
+
232
+ // 如果已连接,立即订阅
233
+ if (this.connection) {
234
+ this.connection.on(eventName, callback)
235
+ }
236
+
237
+ // 返回取消订阅函数
238
+ return () => {
239
+ this.off(eventName, callback)
240
+ }
241
+ }
242
+
243
+ /**
244
+ * 取消监听
245
+ */
246
+ off(eventName: string, callback?: Function) {
247
+ if (callback) {
248
+ // 取消特定回调
249
+ const callbacks = this.messageHandlers.get(eventName)
250
+ if (callbacks) {
251
+ callbacks.delete(callback)
252
+ if (callbacks.size === 0) {
253
+ this.messageHandlers.delete(eventName)
254
+ }
255
+ }
256
+
257
+ if (this.connection) {
258
+ this.connection.off(eventName, callback as any)
259
+ }
260
+ } else {
261
+ // 取消所有回调
262
+ this.messageHandlers.delete(eventName)
263
+
264
+ if (this.connection) {
265
+ this.connection.off(eventName)
266
+ }
267
+ }
268
+ }
269
+
270
+ /**
271
+ * 调用服务器方法
272
+ */
273
+ async invoke<T = any>(methodName: string, ...args: any[]): Promise<T> {
274
+ if (!this.connection) {
275
+ throw new Error('SignalR 未连接')
276
+ }
277
+
278
+ if (this.state !== ConnectionState.Connected) {
279
+ throw new Error(`SignalR 连接状态异常: ${this.state}`)
280
+ }
281
+
282
+ try {
283
+ return await this.connection.invoke<T>(methodName, ...args)
284
+ } catch (error) {
285
+ console.error(`调用服务器方法失败 [${methodName}]:`, error)
286
+ throw error
287
+ }
288
+ }
289
+
290
+ /**
291
+ * 发送消息(不等待响应)
292
+ */
293
+ async send(methodName: string, ...args: any[]): Promise<void> {
294
+ if (!this.connection) {
295
+ throw new Error('SignalR 未连接')
296
+ }
297
+
298
+ if (this.state !== ConnectionState.Connected) {
299
+ throw new Error(`SignalR 连接状态异常: ${this.state}`)
300
+ }
301
+
302
+ try {
303
+ await this.connection.send(methodName, ...args)
304
+ } catch (error) {
305
+ console.error(`发送消息失败 [${methodName}]:`, error)
306
+ throw error
307
+ }
308
+ }
309
+
310
+ /**
311
+ * 断开连接
312
+ */
313
+ async disconnect() {
314
+ if (this.connection) {
315
+ try {
316
+ await this.connection.stop()
317
+ } catch (error) {
318
+ console.error('SignalR 断开连接失败:', error)
319
+ }
320
+ this.connection = null
321
+ }
322
+
323
+ this.setState(ConnectionState.Disconnected)
324
+ }
325
+
326
+ /**
327
+ * 获取原始连接实例
328
+ */
329
+ getConnection(): signalR.HubConnection | null {
330
+ return this.connection
331
+ }
332
+
333
+ /**
334
+ * 清空所有消息订阅
335
+ */
336
+ clearAllHandlers() {
337
+ this.messageHandlers.forEach((callbacks, eventName) => {
338
+ if (this.connection) {
339
+ this.connection.off(eventName)
340
+ }
341
+ })
342
+ this.messageHandlers.clear()
343
+ }
344
+
345
+ /**
346
+ * 销毁实例
347
+ */
348
+ async destroy() {
349
+ this.stateChangeCallbacks = []
350
+ this.clearAllHandlers()
351
+ await this.disconnect()
352
+ }
353
+
354
+ /**
355
+ * 休眠
356
+ */
357
+ private sleep(ms: number): Promise<void> {
358
+ return new Promise(resolve => setTimeout(resolve, ms))
359
+ }
360
+ }
361
+
362
+ /**
363
+ * 创建 SignalR 管理器实例
364
+ */
365
+ export function createSignalR(config: SignalRConfig): SignalRManager {
366
+ return new SignalRManager(config)
367
+ }
368
+
369
+ /**
370
+ * SignalR 连接池管理器
371
+ */
372
+ export class SignalRPool {
373
+ private connections: Map<string, SignalRManager> = new Map()
374
+
375
+ /**
376
+ * 创建或获取连接
377
+ */
378
+ getOrCreate(config: SignalRConfig): SignalRManager {
379
+ const key = config.path
380
+
381
+ if (!this.connections.has(key)) {
382
+ this.connections.set(key, new SignalRManager(config))
383
+ }
384
+
385
+ return this.connections.get(key)!
386
+ }
387
+
388
+ /**
389
+ * 获取连接
390
+ */
391
+ get(path: string): SignalRManager | undefined {
392
+ return this.connections.get(path)
393
+ }
394
+
395
+ /**
396
+ * 断开指定连接
397
+ */
398
+ async disconnect(path: string) {
399
+ const connection = this.connections.get(path)
400
+ if (connection) {
401
+ await connection.destroy()
402
+ this.connections.delete(path)
403
+ }
404
+ }
405
+
406
+ /**
407
+ * 断开所有连接
408
+ */
409
+ async disconnectAll() {
410
+ const promises: Promise<void>[] = []
411
+
412
+ this.connections.forEach(connection => {
413
+ promises.push(connection.destroy())
414
+ })
415
+
416
+ await Promise.all(promises)
417
+ this.connections.clear()
418
+ }
419
+
420
+ /**
421
+ * 获取所有连接状态
422
+ */
423
+ getAllStates(): Record<string, ConnectionState> {
424
+ const states: Record<string, ConnectionState> = {}
425
+
426
+ this.connections.forEach((connection, path) => {
427
+ states[path] = connection.getState()
428
+ })
429
+
430
+ return states
431
+ }
432
+ }
433
+
434
+ /**
435
+ * 全局连接池
436
+ */
437
+ export const signalRPool = new SignalRPool()
438
+
439
+ /**
440
+ * Vue 3 Composition API Hook
441
+ */
442
+ export function useSignalR(config: SignalRConfig) {
443
+ const manager = ref<SignalRManager | null>(null)
444
+ const state = ref<ConnectionState>(ConnectionState.Disconnected)
445
+ const isConnected = computed(() => state.value === ConnectionState.Connected)
446
+
447
+ let unsubscribe: (() => void) | null = null
448
+
449
+ const connect = async () => {
450
+ if (!manager.value) {
451
+ manager.value = createSignalR(config)
452
+
453
+ // 监听状态变化
454
+ unsubscribe = manager.value.onStateChange((newState) => {
455
+ state.value = newState
456
+ })
457
+ }
458
+
459
+ await manager.value.connect()
460
+ }
461
+
462
+ const disconnect = async () => {
463
+ if (manager.value) {
464
+ await manager.value.disconnect()
465
+ }
466
+ }
467
+
468
+ const on = (eventName: string, callback: (data: any) => void) => {
469
+ if (!manager.value) {
470
+ console.warn('SignalR 未初始化,请先调用 connect()')
471
+ return () => {}
472
+ }
473
+
474
+ return manager.value.on(eventName, callback)
475
+ }
476
+
477
+ const off = (eventName: string, callback?: Function) => {
478
+ if (!manager.value) return
479
+ manager.value.off(eventName, callback)
480
+ }
481
+
482
+ const invoke = async <T = any>(methodName: string, ...args: any[]): Promise<T> => {
483
+ if (!manager.value) {
484
+ throw new Error('SignalR 未初始化')
485
+ }
486
+ return manager.value.invoke<T>(methodName, ...args)
487
+ }
488
+
489
+ const send = async (methodName: string, ...args: any[]): Promise<void> => {
490
+ if (!manager.value) {
491
+ throw new Error('SignalR 未初始化')
492
+ }
493
+ return manager.value.send(methodName, ...args)
494
+ }
495
+
496
+ // 自动连接
497
+ onMounted(() => {
498
+ connect()
499
+ })
500
+
501
+ // 自动断开
502
+ onBeforeUnmount(() => {
503
+ if (unsubscribe) {
504
+ unsubscribe()
505
+ }
506
+
507
+ if (manager.value) {
508
+ manager.value.destroy()
509
+ manager.value = null
510
+ }
511
+ })
512
+
513
+ return {
514
+ manager,
515
+ state,
516
+ isConnected,
517
+ connect,
518
+ disconnect,
519
+ on,
520
+ off,
521
+ invoke,
522
+ send
523
+ }
524
+ }
525
+
526
+ // 导入 Vue 相关依赖
527
+ import { ref, computed, onMounted, onBeforeUnmount } from 'vue'
528
+
529
+ /**
530
+ * 创建消息处理器工厂
531
+ */
532
+ export function createMessageHandler(
533
+ eventName: string,
534
+ callback: (data: any) => void
535
+ ) {
536
+ return {
537
+ eventName,
538
+ callback,
539
+ subscribe(connection: SignalRManager) {
540
+ connection.on(eventName, callback)
541
+ },
542
+ unsubscribe(connection: SignalRManager) {
543
+ connection.off(eventName, callback)
544
+ }
545
+ }
546
+ }
547
+
548
+ /**
549
+ * 批量订阅消息
550
+ */
551
+ export function batchSubscribe(
552
+ connection: SignalRManager,
553
+ handlers: MessageHandler[]
554
+ ): () => void {
555
+ handlers.forEach(handler => {
556
+ connection.on(handler.eventName, handler.callback)
557
+ })
558
+
559
+ return () => {
560
+ handlers.forEach(handler => {
561
+ connection.off(handler.eventName, handler.callback)
562
+ })
563
+ }
564
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * 本地存储工具类
3
+ */
4
+ class StorageUtil {
5
+ private storage: Storage
6
+
7
+ constructor(storage: Storage) {
8
+ this.storage = storage
9
+ }
10
+
11
+ /**
12
+ * 设置存储项
13
+ * @param key - 键名
14
+ * @param value - 值
15
+ * @param expires - 过期时间(毫秒)
16
+ */
17
+ set<T>(key: string, value: T, expires?: number): void {
18
+ const data = {
19
+ value,
20
+ expires: expires ? Date.now() + expires : undefined
21
+ }
22
+ this.storage.setItem(key, JSON.stringify(data))
23
+ }
24
+
25
+ /**
26
+ * 获取存储项
27
+ */
28
+ get<T>(key: string): T | null {
29
+ const item = this.storage.getItem(key)
30
+ if (!item) return null
31
+
32
+ try {
33
+ const data = JSON.parse(item)
34
+
35
+ // 检查是否过期
36
+ if (data.expires && Date.now() > data.expires) {
37
+ this.remove(key)
38
+ return null
39
+ }
40
+
41
+ return data.value as T
42
+ } catch {
43
+ return null
44
+ }
45
+ }
46
+
47
+ /**
48
+ * 移除存储项
49
+ */
50
+ remove(key: string): void {
51
+ this.storage.removeItem(key)
52
+ }
53
+
54
+ /**
55
+ * 清空存储
56
+ */
57
+ clear(): void {
58
+ this.storage.clear()
59
+ }
60
+
61
+ /**
62
+ * 获取所有键
63
+ */
64
+ keys(): string[] {
65
+ const keys: string[] = []
66
+ for (let i = 0; i < this.storage.length; i++) {
67
+ const key = this.storage.key(i)
68
+ if (key) keys.push(key)
69
+ }
70
+ return keys
71
+ }
72
+ }
73
+
74
+ export const localStorageUtil = new StorageUtil(window.localStorage)
75
+ export const sessionStorageUtil = new StorageUtil(window.sessionStorage)
76
+ export const setTheme = (theme: string) => {
77
+ localStorage.setItem('theme', theme);
78
+ sessionStorage.setItem('theme', theme);
79
+ window.document.documentElement.setAttribute('data-theme', theme)
80
+ }