@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.
- package/ganwei-iotcenter-index-6.2.3/configuration/moduleConfiguration.json +2 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/env.d.ts +8 -5
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/index.html +1 -1
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/public/static/css/ElementPlusAdapter.css +437 -68
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/public/static/css/reset-6-1.css +1 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/public/static/css/reset-plus.css +396 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/public/static/http/createAxios.js +38 -13
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/public/static/js/getLanguage.js +19 -12
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/public/static/themes/dark-6-1.css +3 -3
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/public/static/themes/green-6-1.css +12 -11
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/App.vue +11 -6
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/indexRightContent/headerRight/equipAlarmDialog/index.vue +6 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/indexRightContent/headerRight/expirationReminder/index.vue +7 -3
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/indexRightContent/headerRight/index.scss +4 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/indexRightContent/headerRight/userInfo/index.js +35 -3
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/indexRightContent/headerRight/userInfo/index.scss +7 -1
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/indexRightContent/headerRight/userInfo/index.vue +2 -2
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/layouts/Navigation/ContractMenu.vue +11 -37
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/layouts/Navigation/TopNav.vue +3 -2
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/components/layouts/Sidebar/LeftContent/index.vue +7 -1
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/router.js +120 -18
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/date.ts +80 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/dom.ts +99 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/env.ts +20 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/file.ts +74 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/index.ts +26 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/number.ts +83 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/performance.ts +69 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/storage.ts +80 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/string.ts +116 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/xss-filter.ts +260 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/views/jumpIframe/index.vue +45 -28
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/tsconfig.json +1 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/vite.config.ts +10 -1
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-login/public/static/http/createAxios.js +40 -15
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-login/public/static/js/getLanguage.js +10 -2
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-login/src/views/login.vue +55 -53
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-login/src/views/ssoLogin.vue +10 -9
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/.env.development +0 -3
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/.env.production +1 -4
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/.env.test +0 -3
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/.eslintrc.cjs +2 -2
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/env.d.ts +9 -5
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/index.html +1 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/package.json +3 -2
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/public/static/http/createAxios.js +38 -13
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/public/static/js/getLanguage.js +10 -2
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/App.vue +1 -1
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/request/api.ts +0 -1
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/request/models/response/template.ts +0 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/date.ts +79 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/dom.ts +99 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/env.ts +20 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/file.ts +74 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/index.ts +29 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/number.ts +83 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/performance.ts +69 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/signalr.ts +564 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/storage.ts +80 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/string.ts +116 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/xss-filter.ts +260 -0
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/views/template.vue +0 -1
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/tsconfig.json +7 -0
- package/ganwei-iotcenter-index-6.2.3/pnpm-lock.yaml +489 -155
- package/package.json +1 -1
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/mixins/judgePermission.js +0 -60
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/src/utils/setStorage.js +0 -5
- package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/utils/setStorage.js +0 -5
- /package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-index/build/{enteryJson.js → entryJson.js} +0 -0
- /package/ganwei-iotcenter-index-6.2.3/packages/ganwei-iotcenter-template/src/{request/models/request/index.ts → enum/template.ts} +0 -0
- /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
|
+
}
|