vue-chat-kit 0.3.7 → 0.3.9
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/README.md +0 -4
- package/dist/vue-chat-kit.css +1 -1
- package/dist/vue-chat-kit.es.js +3392 -2122
- package/dist/vue-chat-kit.umd.js +1 -1
- package/package.json +1 -1
- package/src/components/ChatPanel.vue +1570 -57
- package/src/components/EmojiPicker.vue +197 -0
- package/src/composables/useChat.js +157 -613
- package/src/composables/useChatCore.js +207 -0
- package/src/composables/useFriendChat.js +423 -0
- package/src/composables/useGroupChat.js +748 -0
- package/src/config/index.js +21 -4
- package/src/core/adapter-example.js +90 -20
- package/src/core/api.js +189 -13
- package/src/core/websocket.js +25 -9
- package/src/index.js +0 -4
- package/src/components/ChatWindow.vue +0 -2094
|
@@ -1,613 +1,157 @@
|
|
|
1
|
-
/**
|
|
2
|
-
*
|
|
3
|
-
*/
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
|
|
9
|
-
export function useChat(config, emit) {
|
|
10
|
-
// ==========
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
//
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
return
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// ========== API 方法 ==========
|
|
161
|
-
|
|
162
|
-
// 获取好友列表
|
|
163
|
-
const getFriendList = async () => {
|
|
164
|
-
try {
|
|
165
|
-
const res = await api.getFriends(myUsername)
|
|
166
|
-
const allFriends = res.data || []
|
|
167
|
-
friendList.value = allFriends
|
|
168
|
-
chatList.value = allFriends.filter(item => item.isChatting === 1)
|
|
169
|
-
|
|
170
|
-
// 获取未读数
|
|
171
|
-
for (const friend of chatList.value) {
|
|
172
|
-
try {
|
|
173
|
-
const historyRes = await api.getHistory(myUsername, friend.username)
|
|
174
|
-
const messages = historyRes.data || []
|
|
175
|
-
friend.unReadNum = messages.filter(msg =>
|
|
176
|
-
msg.isRead === 0 && msg.sendUsername === friend.username
|
|
177
|
-
).length
|
|
178
|
-
} catch {
|
|
179
|
-
friend.unReadNum = 0
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
} catch (error) {
|
|
183
|
-
console.error('[VueChatKit] 获取好友列表失败', error)
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// 获取聊天历史
|
|
188
|
-
const getChatHistory = async (targetName) => {
|
|
189
|
-
try {
|
|
190
|
-
const res = await api.getHistory(myUsername, targetName)
|
|
191
|
-
chatMsgList.value = res.data || []
|
|
192
|
-
scrollToBottom()
|
|
193
|
-
} catch (error) {
|
|
194
|
-
console.error('[VueChatKit] 获取聊天历史失败', error)
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// 标记已读
|
|
199
|
-
const markAsRead = async (friendUser) => {
|
|
200
|
-
try {
|
|
201
|
-
await api.setRead(myUsername, friendUser)
|
|
202
|
-
getFriendList()
|
|
203
|
-
} catch (error) {
|
|
204
|
-
console.error('[VueChatKit] 标记已读失败', error)
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// 选择聊天用户
|
|
209
|
-
const selectUser = async (user) => {
|
|
210
|
-
currentSelectName.value = user.id
|
|
211
|
-
await getChatHistory(user.id)
|
|
212
|
-
await markAsRead(user.id)
|
|
213
|
-
scrollToBottom()
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// 设置好友聊天状态
|
|
217
|
-
const setFriendToChatStatus = async (friendUser, status = 1) => {
|
|
218
|
-
try {
|
|
219
|
-
await api.setChatStatus(myUsername, friendUser, status)
|
|
220
|
-
await getFriendList()
|
|
221
|
-
return true
|
|
222
|
-
} catch (error) {
|
|
223
|
-
console.error('[VueChatKit] 设置聊天状态失败', error)
|
|
224
|
-
return false
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// 发送文本消息
|
|
229
|
-
const sendMessage = () => {
|
|
230
|
-
if (!inputText.value.trim() || !currentSelectName.value || !socket) return
|
|
231
|
-
|
|
232
|
-
const success = socket.send(currentSelectName.value, inputText.value.trim(), 'text')
|
|
233
|
-
if (success) {
|
|
234
|
-
const tempMsg = {
|
|
235
|
-
msgContent: inputText.value.trim(),
|
|
236
|
-
sendUsername: myUsername,
|
|
237
|
-
receiveUsername: currentSelectName.value,
|
|
238
|
-
createTime: new Date(),
|
|
239
|
-
isRead: 0,
|
|
240
|
-
type: 'text'
|
|
241
|
-
}
|
|
242
|
-
chatMsgList.value.push(tempMsg)
|
|
243
|
-
inputText.value = ''
|
|
244
|
-
scrollToBottom()
|
|
245
|
-
setTimeout(() => {
|
|
246
|
-
getChatHistory(currentSelectName.value)
|
|
247
|
-
getFriendList()
|
|
248
|
-
}, 300)
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// 发送文件
|
|
253
|
-
const sendFile = async (file) => {
|
|
254
|
-
if (!currentSelectName.value || !socket) return false
|
|
255
|
-
|
|
256
|
-
try {
|
|
257
|
-
const uploadRes = await api.uploadFile(file)
|
|
258
|
-
|
|
259
|
-
if (uploadRes.code === 200 && uploadRes.data) {
|
|
260
|
-
const { fileUrl, fileName } = uploadRes.data
|
|
261
|
-
|
|
262
|
-
const success = socket.send(
|
|
263
|
-
currentSelectName.value,
|
|
264
|
-
fileName,
|
|
265
|
-
'file',
|
|
266
|
-
fileUrl,
|
|
267
|
-
fileName,
|
|
268
|
-
file.size
|
|
269
|
-
)
|
|
270
|
-
|
|
271
|
-
if (success) {
|
|
272
|
-
const tempMsg = {
|
|
273
|
-
msgContent: fileName,
|
|
274
|
-
sendUsername: myUsername,
|
|
275
|
-
receiveUsername: currentSelectName.value,
|
|
276
|
-
createTime: new Date(),
|
|
277
|
-
isRead: 0,
|
|
278
|
-
type: 'file',
|
|
279
|
-
fileUrl: fileUrl,
|
|
280
|
-
fileName: fileName,
|
|
281
|
-
fileSize: file.size
|
|
282
|
-
}
|
|
283
|
-
chatMsgList.value.push(tempMsg)
|
|
284
|
-
scrollToBottom()
|
|
285
|
-
return true
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
return false
|
|
289
|
-
} catch (error) {
|
|
290
|
-
console.error('[VueChatKit] 发送文件失败', error)
|
|
291
|
-
return false
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// 批量发送文件和文本
|
|
296
|
-
const sendFilesAndText = async (files, text) => {
|
|
297
|
-
if (!currentSelectName.value || !socket) return
|
|
298
|
-
|
|
299
|
-
if (text && text.trim()) {
|
|
300
|
-
const textSuccess = socket.send(currentSelectName.value, text.trim(), 'text')
|
|
301
|
-
if (textSuccess) {
|
|
302
|
-
const tempMsg = {
|
|
303
|
-
msgContent: text.trim(),
|
|
304
|
-
sendUsername: myUsername,
|
|
305
|
-
receiveUsername: currentSelectName.value,
|
|
306
|
-
createTime: new Date(),
|
|
307
|
-
isRead: 0,
|
|
308
|
-
type: 'text'
|
|
309
|
-
}
|
|
310
|
-
chatMsgList.value.push(tempMsg)
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
for (const fileObj of files) {
|
|
315
|
-
const file = fileObj.file || fileObj
|
|
316
|
-
await sendFile(file)
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
setTimeout(() => {
|
|
320
|
-
getChatHistory(currentSelectName.value)
|
|
321
|
-
getFriendList()
|
|
322
|
-
}, 300)
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// ========== WebSocket 相关 ==========
|
|
326
|
-
|
|
327
|
-
// 解析 WebSocket 消息
|
|
328
|
-
const parseWsMessage = (data) => {
|
|
329
|
-
try {
|
|
330
|
-
try {
|
|
331
|
-
const jsonData = JSON.parse(data)
|
|
332
|
-
if (jsonData.to || jsonData.msg) {
|
|
333
|
-
return {
|
|
334
|
-
to: jsonData.to,
|
|
335
|
-
content: jsonData.msg,
|
|
336
|
-
type: jsonData.type || 'text',
|
|
337
|
-
fileUrl: jsonData.fileUrl || '',
|
|
338
|
-
fileName: jsonData.fileName || '',
|
|
339
|
-
fileSize: jsonData.fileSize || 0
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
} catch {
|
|
343
|
-
// 旧格式
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
const match = data.match(/^\[(.+?)\]:(.+)$/)
|
|
347
|
-
if (match) {
|
|
348
|
-
return {
|
|
349
|
-
username: match[1],
|
|
350
|
-
content: match[2],
|
|
351
|
-
type: 'text'
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
} catch (e) {
|
|
355
|
-
console.error('[VueChatKit] 解析消息失败', e)
|
|
356
|
-
}
|
|
357
|
-
return null
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
// 处理 WebSocket 消息
|
|
361
|
-
const handleWsMessage = (data) => {
|
|
362
|
-
if (data.includes('【状态变更】')) {
|
|
363
|
-
const reg = /【状态变更】(.+?) 已(上线|下线)/
|
|
364
|
-
const res = data.match(reg)
|
|
365
|
-
if (res) {
|
|
366
|
-
const targetName = res[1]
|
|
367
|
-
const isOnline = res[2] === '上线'
|
|
368
|
-
const targetFriend = friendList.value.find(item => item.username === targetName)
|
|
369
|
-
if (targetFriend) {
|
|
370
|
-
targetFriend.online = isOnline
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
return
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
const msgInfo = parseWsMessage(data)
|
|
377
|
-
if (msgInfo) {
|
|
378
|
-
const message = {
|
|
379
|
-
content: msgInfo.content,
|
|
380
|
-
username: msgInfo.username || currentSelectName.value,
|
|
381
|
-
type: msgInfo.type || 'text',
|
|
382
|
-
fileUrl: msgInfo.fileUrl || '',
|
|
383
|
-
fileName: msgInfo.fileName || '',
|
|
384
|
-
fileSize: msgInfo.fileSize || 0,
|
|
385
|
-
timestamp: new Date()
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
if (currentSelectName.value) {
|
|
389
|
-
try {
|
|
390
|
-
let tempMsg = {
|
|
391
|
-
msgContent: msgInfo.content,
|
|
392
|
-
sendUsername: message.username,
|
|
393
|
-
receiveUsername: myUsername,
|
|
394
|
-
createTime: new Date(),
|
|
395
|
-
isRead: 0,
|
|
396
|
-
type: msgInfo.type || 'text',
|
|
397
|
-
fileUrl: msgInfo.fileUrl || '',
|
|
398
|
-
fileName: msgInfo.fileName || '',
|
|
399
|
-
fileSize: msgInfo.fileSize || 0
|
|
400
|
-
}
|
|
401
|
-
chatMsgList.value.push(tempMsg)
|
|
402
|
-
scrollToBottom()
|
|
403
|
-
} catch (e) {
|
|
404
|
-
console.error('[VueChatKit] 添加临时消息失败', e)
|
|
405
|
-
}
|
|
406
|
-
getChatHistory(currentSelectName.value)
|
|
407
|
-
}
|
|
408
|
-
getFriendList()
|
|
409
|
-
|
|
410
|
-
// emit 消息事件
|
|
411
|
-
if (emit && typeof emit === 'function') {
|
|
412
|
-
emit(message)
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
// 初始化 WebSocket
|
|
418
|
-
const initWebSocket = () => {
|
|
419
|
-
const wsUrl = `${config.api.websocketUrl}?userId=${myUsername}`
|
|
420
|
-
socket = new ChatWebSocket(myUsername, {
|
|
421
|
-
wsUrl,
|
|
422
|
-
maxReconnectAttempts: config.websocket.maxReconnectAttempts,
|
|
423
|
-
reconnectDelay: config.websocket.reconnectDelay
|
|
424
|
-
})
|
|
425
|
-
socket.on('message', handleWsMessage)
|
|
426
|
-
socket.connect()
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
// 关闭 WebSocket
|
|
430
|
-
const closeWebSocket = () => {
|
|
431
|
-
if (socket) {
|
|
432
|
-
socket.close()
|
|
433
|
-
socket = null
|
|
434
|
-
}
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
// ========== 添加好友相关 ==========
|
|
438
|
-
|
|
439
|
-
const openAddFriendDialog = async () => {
|
|
440
|
-
addFriendDialogVisible.value = true
|
|
441
|
-
addFriendSearchText.value = ''
|
|
442
|
-
availableUsers.value = [] // 清空列表,打开时不搜索
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
const loadAvailableUsers = async (keyword = '') => {
|
|
446
|
-
loadingAvailableUsers.value = true
|
|
447
|
-
try {
|
|
448
|
-
let res
|
|
449
|
-
if (keyword) {
|
|
450
|
-
// 使用模糊查询接口搜索用户
|
|
451
|
-
res = await api.searchUser(keyword)
|
|
452
|
-
} else {
|
|
453
|
-
// 使用获取可添加好友接口
|
|
454
|
-
res = await api.getAvailableUsers(myUsername)
|
|
455
|
-
}
|
|
456
|
-
availableUsers.value = res?.data || []
|
|
457
|
-
} catch (error) {
|
|
458
|
-
console.error('[VueChatKit] 获取可用用户失败', error)
|
|
459
|
-
} finally {
|
|
460
|
-
loadingAvailableUsers.value = false
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
const addFriend = async (user) => {
|
|
465
|
-
try {
|
|
466
|
-
await api.addFriend(myUsername, user.username)
|
|
467
|
-
await getFriendList()
|
|
468
|
-
addFriendDialogVisible.value = false
|
|
469
|
-
} catch (error) {
|
|
470
|
-
console.error('[VueChatKit] 添加好友失败', error)
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
const loadFriendApplyList = async () => {
|
|
475
|
-
loadingFriendApply.value = true
|
|
476
|
-
try {
|
|
477
|
-
const res = await api.getApplyList(myUsername)
|
|
478
|
-
friendApplyList.value = res.data || []
|
|
479
|
-
} catch (error) {
|
|
480
|
-
console.error('[VueChatKit] 获取好友申请列表失败', error)
|
|
481
|
-
} finally {
|
|
482
|
-
loadingFriendApply.value = false
|
|
483
|
-
}
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
const agreeFriend = async (applyUser) => {
|
|
487
|
-
try {
|
|
488
|
-
await api.agreeFriend(applyUser, myUsername)
|
|
489
|
-
await loadFriendApplyList()
|
|
490
|
-
await getFriendList()
|
|
491
|
-
} catch (error) {
|
|
492
|
-
console.error('[VueChatKit] 同意好友申请失败', error)
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
// ========== 用户信息相关 ==========
|
|
497
|
-
|
|
498
|
-
const initUserAvatar = async () => {
|
|
499
|
-
try {
|
|
500
|
-
const res = await api.getUserAvatar(myUsername)
|
|
501
|
-
if (res.code === 200 && res.data) {
|
|
502
|
-
myAvatar.value = res.data
|
|
503
|
-
}
|
|
504
|
-
} catch (error) {
|
|
505
|
-
console.warn('[VueChatKit] 加载头像失败', error)
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
const updateMyAvatar = (avatarUrl) => {
|
|
510
|
-
myAvatar.value = avatarUrl
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
const getUserInfo = async () => {
|
|
514
|
-
loadingUserInfo.value = true
|
|
515
|
-
try {
|
|
516
|
-
const res = await api.getUserInfo(myUsername)
|
|
517
|
-
if (res.code === 200 && res.data) {
|
|
518
|
-
userInfo.value = {
|
|
519
|
-
...userInfo.value,
|
|
520
|
-
...res.data
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
} catch (error) {
|
|
524
|
-
console.error('[VueChatKit] 获取用户信息失败', error)
|
|
525
|
-
} finally {
|
|
526
|
-
loadingUserInfo.value = false
|
|
527
|
-
}
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
const updateUserInfo = async (data) => {
|
|
531
|
-
try {
|
|
532
|
-
const res = await api.updateUserInfo(myUsername, data)
|
|
533
|
-
if (res.code === 200) {
|
|
534
|
-
userInfo.value = {
|
|
535
|
-
...userInfo.value,
|
|
536
|
-
...data
|
|
537
|
-
}
|
|
538
|
-
return true
|
|
539
|
-
}
|
|
540
|
-
return false
|
|
541
|
-
} catch (error) {
|
|
542
|
-
console.error('[VueChatKit] 更新用户信息失败', error)
|
|
543
|
-
return false
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
// 重置状态
|
|
548
|
-
const reset = () => {
|
|
549
|
-
currentSelectName.value = ''
|
|
550
|
-
chatMsgList.value = []
|
|
551
|
-
inputText.value = ''
|
|
552
|
-
searchText.value = ''
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
// 初始化头像
|
|
556
|
-
initUserAvatar()
|
|
557
|
-
|
|
558
|
-
// 监听添加好友搜索关键词变化,实时调用模糊查询
|
|
559
|
-
watch(addFriendSearchText, async (newKeyword) => {
|
|
560
|
-
await loadAvailableUsers(newKeyword)
|
|
561
|
-
})
|
|
562
|
-
|
|
563
|
-
return {
|
|
564
|
-
// 状态
|
|
565
|
-
myUsername,
|
|
566
|
-
myAvatar,
|
|
567
|
-
userInfo,
|
|
568
|
-
loadingUserInfo,
|
|
569
|
-
friendList,
|
|
570
|
-
chatList,
|
|
571
|
-
filteredFriendList,
|
|
572
|
-
chatMsgList,
|
|
573
|
-
currentSelectName,
|
|
574
|
-
searchText,
|
|
575
|
-
inputText,
|
|
576
|
-
messagesContainer,
|
|
577
|
-
filteredUsers,
|
|
578
|
-
filteredAvailableUsers,
|
|
579
|
-
currentUser,
|
|
580
|
-
currentMessages,
|
|
581
|
-
addFriendDialogVisible,
|
|
582
|
-
addFriendSearchText,
|
|
583
|
-
availableUsers,
|
|
584
|
-
loadingAvailableUsers,
|
|
585
|
-
friendApplyList,
|
|
586
|
-
loadingFriendApply,
|
|
587
|
-
|
|
588
|
-
// 方法
|
|
589
|
-
formatTime,
|
|
590
|
-
formatLastTime,
|
|
591
|
-
scrollToBottom,
|
|
592
|
-
getFriendList,
|
|
593
|
-
getChatHistory,
|
|
594
|
-
setFriendToChatStatus,
|
|
595
|
-
selectUser,
|
|
596
|
-
sendMessage,
|
|
597
|
-
sendFile,
|
|
598
|
-
sendFilesAndText,
|
|
599
|
-
initWebSocket,
|
|
600
|
-
closeWebSocket,
|
|
601
|
-
reset,
|
|
602
|
-
openAddFriendDialog,
|
|
603
|
-
loadAvailableUsers,
|
|
604
|
-
addFriend,
|
|
605
|
-
loadFriendApplyList,
|
|
606
|
-
agreeFriend,
|
|
607
|
-
updateMyAvatar,
|
|
608
|
-
getUserInfo,
|
|
609
|
-
updateUserInfo
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
export default useChat
|
|
1
|
+
/**
|
|
2
|
+
* 聊天主入口 - 整合私聊和群聊
|
|
3
|
+
*/
|
|
4
|
+
import { computed } from 'vue'
|
|
5
|
+
import useChatCore from './useChatCore'
|
|
6
|
+
import useFriendChat from './useFriendChat'
|
|
7
|
+
import useGroupChat from './useGroupChat'
|
|
8
|
+
|
|
9
|
+
export function useChat(config, emit) {
|
|
10
|
+
// ========== 初始化核心模块 ==========
|
|
11
|
+
const core = useChatCore(config, emit)
|
|
12
|
+
const friendChat = useFriendChat(core)
|
|
13
|
+
const groupChat = useGroupChat(core)
|
|
14
|
+
|
|
15
|
+
// ========== 共享计算属性 ==========
|
|
16
|
+
|
|
17
|
+
// 当前选择的聊天目标
|
|
18
|
+
const currentChatTarget = computed(() => {
|
|
19
|
+
if (groupChat.currentSelectGroup.value) {
|
|
20
|
+
return { type: 'group', data: groupChat.currentSelectGroup.value }
|
|
21
|
+
}
|
|
22
|
+
if (friendChat.currentSelectName.value && friendChat.currentUser.value) {
|
|
23
|
+
return { type: 'friend', data: friendChat.currentUser.value }
|
|
24
|
+
}
|
|
25
|
+
return null
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
// 当前显示的消息列表
|
|
29
|
+
const currentAllMessages = computed(() => {
|
|
30
|
+
if (groupChat.currentSelectGroup.value) {
|
|
31
|
+
return groupChat.currentGroupMessages.value
|
|
32
|
+
}
|
|
33
|
+
return friendChat.currentMessages.value
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
// ========== WebSocket 消息处理 ==========
|
|
37
|
+
|
|
38
|
+
const handleWsMessage = (data) => {
|
|
39
|
+
if (data.includes('【状态变更】')) {
|
|
40
|
+
const reg = /【状态变更】(.+?) 已(上线|下线)/
|
|
41
|
+
const res = data.match(reg)
|
|
42
|
+
if (res) {
|
|
43
|
+
const targetName = res[1]
|
|
44
|
+
const isOnline = res[2] === '上线'
|
|
45
|
+
friendChat.handleFriendStatusChange(targetName, isOnline)
|
|
46
|
+
}
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const msgInfo = core.parseWsMessage(data)
|
|
51
|
+
if (msgInfo) {
|
|
52
|
+
if (msgInfo.toGroupId) {
|
|
53
|
+
groupChat.handleGroupWsMessage(msgInfo)
|
|
54
|
+
} else {
|
|
55
|
+
friendChat.handleFriendWsMessage(msgInfo)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (emit && typeof emit === 'function') {
|
|
59
|
+
emit({
|
|
60
|
+
...msgInfo,
|
|
61
|
+
isGroup: !!msgInfo.toGroupId
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 初始化 WebSocket
|
|
68
|
+
const initWebSocket = () => {
|
|
69
|
+
core.initWebSocket(handleWsMessage)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ========== 选择聊天对象 ==========
|
|
73
|
+
|
|
74
|
+
const selectUser = async (user) => {
|
|
75
|
+
groupChat.resetGroupChat()
|
|
76
|
+
await friendChat.selectUser(user)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const selectGroup = async (group) => {
|
|
80
|
+
friendChat.resetFriendChat()
|
|
81
|
+
await groupChat.selectGroup(group)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ========== 切换 Tab ==========
|
|
85
|
+
|
|
86
|
+
const switchTab = (tab) => {
|
|
87
|
+
groupChat.switchTab(tab)
|
|
88
|
+
if (tab === 'apply') {
|
|
89
|
+
friendChat.loadFriendApplyList()
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ========== 发送消息 ==========
|
|
94
|
+
|
|
95
|
+
const sendMessage = () => {
|
|
96
|
+
if (groupChat.currentSelectGroup.value) {
|
|
97
|
+
groupChat.sendGroupMessage()
|
|
98
|
+
} else if (friendChat.currentSelectName.value) {
|
|
99
|
+
friendChat.sendMessage()
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const sendFile = async (file) => {
|
|
104
|
+
if (groupChat.currentSelectGroup.value) {
|
|
105
|
+
return await groupChat.sendGroupFile(file)
|
|
106
|
+
} else if (friendChat.currentSelectName.value) {
|
|
107
|
+
return await friendChat.sendFile(file)
|
|
108
|
+
}
|
|
109
|
+
return false
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const sendFilesAndText = async (files, text) => {
|
|
113
|
+
if (groupChat.currentSelectGroup.value) {
|
|
114
|
+
await groupChat.sendGroupFilesAndText(files, text)
|
|
115
|
+
} else if (friendChat.currentSelectName.value) {
|
|
116
|
+
await friendChat.sendFilesAndText(files, text)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// ========== 重置状态 ==========
|
|
121
|
+
|
|
122
|
+
const reset = () => {
|
|
123
|
+
friendChat.resetFriendChat()
|
|
124
|
+
groupChat.resetGroupChat()
|
|
125
|
+
core.inputText.value = ''
|
|
126
|
+
core.searchText.value = ''
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// ========== 整合返回 ==========
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
// ========== 核心模块 ==========
|
|
133
|
+
...core,
|
|
134
|
+
initWebSocket,
|
|
135
|
+
|
|
136
|
+
// ========== 私聊模块 ==========
|
|
137
|
+
...friendChat,
|
|
138
|
+
selectUser,
|
|
139
|
+
|
|
140
|
+
// ========== 群聊模块 ==========
|
|
141
|
+
...groupChat,
|
|
142
|
+
selectGroup,
|
|
143
|
+
|
|
144
|
+
// ========== 共享计算属性 ==========
|
|
145
|
+
currentChatTarget,
|
|
146
|
+
currentAllMessages,
|
|
147
|
+
|
|
148
|
+
// ========== 整合方法 ==========
|
|
149
|
+
switchTab,
|
|
150
|
+
sendMessage,
|
|
151
|
+
sendFile,
|
|
152
|
+
sendFilesAndText,
|
|
153
|
+
reset
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export default useChat
|