vue-chat-kit 0.3.8 → 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/dist/vue-chat-kit.css +1 -1
- package/dist/vue-chat-kit.es.js +2967 -2103
- package/dist/vue-chat-kit.umd.js +1 -1
- package/package.json +1 -1
- package/src/components/ChatPanel.vue +1498 -30
- package/src/composables/useChat.js +157 -596
- 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 -2
- package/src/core/adapter-example.js +90 -0
- package/src/core/api.js +189 -0
- package/src/core/websocket.js +25 -9
- package/src/index.js +0 -4
- package/src/components/ChatWindow.vue +0 -2144
|
@@ -0,0 +1,748 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 群聊逻辑
|
|
3
|
+
*/
|
|
4
|
+
import { ref, computed } from 'vue'
|
|
5
|
+
|
|
6
|
+
export function useGroupChat(core) {
|
|
7
|
+
const { api, myUsername, myAvatar, config, scrollToBottom, getSocket } = core
|
|
8
|
+
|
|
9
|
+
// ========== 状态 ==========
|
|
10
|
+
const activeTab = ref('friends')
|
|
11
|
+
const groupList = ref([])
|
|
12
|
+
const currentSelectGroup = ref(null)
|
|
13
|
+
const groupMsgList = ref([])
|
|
14
|
+
const groupMemberList = ref([])
|
|
15
|
+
const groupMembersMap = ref({})
|
|
16
|
+
const createGroupDialogVisible = ref(false)
|
|
17
|
+
const groupDetailVisible = ref(false)
|
|
18
|
+
const newGroupName = ref('')
|
|
19
|
+
const newGroupRemark = ref('')
|
|
20
|
+
const selectedMembersForCreate = ref([])
|
|
21
|
+
const inviteMemberDialogVisible = ref(false)
|
|
22
|
+
const selectedMembersForInvite = ref([])
|
|
23
|
+
// 新增状态
|
|
24
|
+
const groupInfoVisible = ref(false)
|
|
25
|
+
const editingGroupInfo = ref({
|
|
26
|
+
groupNickname: '',
|
|
27
|
+
remark: '',
|
|
28
|
+
notice: ''
|
|
29
|
+
})
|
|
30
|
+
const editingMemberNick = ref({
|
|
31
|
+
targetUsername: '',
|
|
32
|
+
memberNick: ''
|
|
33
|
+
})
|
|
34
|
+
const memberNickDialogVisible = ref(false)
|
|
35
|
+
const msgReadUserDialogVisible = ref(false)
|
|
36
|
+
const currentMsgReadList = ref({
|
|
37
|
+
readUserList: [],
|
|
38
|
+
unreadUserList: []
|
|
39
|
+
})
|
|
40
|
+
// 当前群聊详情信息
|
|
41
|
+
const currentGroupInfo = ref(null)
|
|
42
|
+
// 编辑状态
|
|
43
|
+
const editingFields = ref({
|
|
44
|
+
groupNickname: false,
|
|
45
|
+
remark: false,
|
|
46
|
+
notice: false
|
|
47
|
+
})
|
|
48
|
+
// 临时编辑值
|
|
49
|
+
const tempEditValues = ref({
|
|
50
|
+
groupNickname: '',
|
|
51
|
+
remark: '',
|
|
52
|
+
notice: ''
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
// ========== 计算属性 ==========
|
|
56
|
+
|
|
57
|
+
// 过滤后的群聊列表
|
|
58
|
+
const filteredGroupList = computed(() => {
|
|
59
|
+
let list = groupList.value
|
|
60
|
+
if (core.searchText.value) {
|
|
61
|
+
const keyword = core.searchText.value.toLowerCase()
|
|
62
|
+
list = list.filter(item =>
|
|
63
|
+
(item.groupName && item.groupName.toLowerCase().includes(keyword)) ||
|
|
64
|
+
(item.remark && item.remark.toLowerCase().includes(keyword))
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
return list.map(item => {
|
|
68
|
+
const members = groupMembersMap.value[item.groupId] || []
|
|
69
|
+
const memberAvatars = members.slice(0, 4).map(member => ({
|
|
70
|
+
username: member.username,
|
|
71
|
+
avatar: member.avatar
|
|
72
|
+
? `${config.api.baseUrl}${member.avatar}`
|
|
73
|
+
: `https://api.dicebear.com/7.x/avataaars/svg?seed=${member.username}`
|
|
74
|
+
}))
|
|
75
|
+
|
|
76
|
+
// 获取最后一条消息,处理不同的字段名
|
|
77
|
+
let lastMsg = '暂无消息'
|
|
78
|
+
if (item.lastMsg) {
|
|
79
|
+
lastMsg = item.lastMsg
|
|
80
|
+
} else if (item.lastMessage) {
|
|
81
|
+
lastMsg = item.lastMessage
|
|
82
|
+
} else if (item.latestMessage) {
|
|
83
|
+
lastMsg = item.latestMessage
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// 获取未读数,处理不同的字段名
|
|
87
|
+
let unread = 0
|
|
88
|
+
if (item.unReadNum !== undefined && item.unReadNum !== null) {
|
|
89
|
+
unread = item.unReadNum
|
|
90
|
+
} else if (item.unreadCount !== undefined && item.unreadCount !== null) {
|
|
91
|
+
unread = item.unreadCount
|
|
92
|
+
} else if (item.unread !== undefined && item.unread !== null) {
|
|
93
|
+
unread = item.unread
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 获取最后时间,处理不同的字段名
|
|
97
|
+
let lastTime = item.lastTime
|
|
98
|
+
if (!lastTime && item.latestTime) {
|
|
99
|
+
lastTime = item.latestTime
|
|
100
|
+
} else if (!lastTime && item.lastMsgTime) {
|
|
101
|
+
lastTime = item.lastMsgTime
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
id: item.groupId,
|
|
106
|
+
groupId: item.groupId,
|
|
107
|
+
name: item.groupName,
|
|
108
|
+
remark: item.remark,
|
|
109
|
+
owner: item.ownerUsername,
|
|
110
|
+
avatar: `https://api.dicebear.com/7.x/avataaars/svg?seed=${item.groupId}`,
|
|
111
|
+
memberAvatars: memberAvatars,
|
|
112
|
+
lastMsg: lastMsg,
|
|
113
|
+
lastTime: lastTime,
|
|
114
|
+
unread: unread,
|
|
115
|
+
memberCount: item.memberCount || 0,
|
|
116
|
+
// 保留原始字段用于编辑功能
|
|
117
|
+
notice: item.notice,
|
|
118
|
+
groupNickname: item.groupNickname
|
|
119
|
+
}
|
|
120
|
+
})
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
// 处理后的群聊消息
|
|
124
|
+
const currentGroupMessages = computed(() => {
|
|
125
|
+
return groupMsgList.value.map(item => {
|
|
126
|
+
const isFileMessage = item.type === 'file' || item.fileUrl || item.fileName
|
|
127
|
+
const fileName = item.fileName || item.msgContent
|
|
128
|
+
|
|
129
|
+
// 查找发送消息的成员信息
|
|
130
|
+
const member = groupMemberList.value.find(m => m.username === item.sendUsername)
|
|
131
|
+
|
|
132
|
+
// 优先显示群昵称,如果没有群昵称则显示用户名
|
|
133
|
+
const displayName = member?.memberNick || item.sendUsername
|
|
134
|
+
|
|
135
|
+
const avatar = member?.avatar
|
|
136
|
+
? `${config.api.baseUrl}${member.avatar}`
|
|
137
|
+
: `https://api.dicebear.com/7.x/avataaars/svg?seed=${item.sendUsername}`
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
text: item.msgContent,
|
|
141
|
+
isSelf: item.sendUsername === myUsername,
|
|
142
|
+
time: item.createTime,
|
|
143
|
+
sendUsername: item.sendUsername,
|
|
144
|
+
displayName: displayName, // 添加显示名称字段
|
|
145
|
+
avatar: avatar,
|
|
146
|
+
type: isFileMessage ? 'file' : 'text',
|
|
147
|
+
fileType: core.isImageFile(fileName) ? 'image' : core.getFileIconType(fileName),
|
|
148
|
+
fileUrl: item.fileUrl || '',
|
|
149
|
+
fileName: fileName,
|
|
150
|
+
fileSize: item.fileSize || 0
|
|
151
|
+
}
|
|
152
|
+
})
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
// ========== API 方法 ==========
|
|
156
|
+
|
|
157
|
+
// 获取群聊列表
|
|
158
|
+
const getGroupList = async () => {
|
|
159
|
+
try {
|
|
160
|
+
const res = await api.getMyGroups(myUsername)
|
|
161
|
+
groupList.value = res.data || []
|
|
162
|
+
|
|
163
|
+
for (const group of groupList.value) {
|
|
164
|
+
try {
|
|
165
|
+
const membersRes = await api.getGroupMembers(group.groupId, myUsername)
|
|
166
|
+
groupMembersMap.value[group.groupId] = membersRes.data || []
|
|
167
|
+
} catch (err) {
|
|
168
|
+
console.error(`[VueChatKit] 获取群 ${group.groupId} 成员失败`, err)
|
|
169
|
+
groupMembersMap.value[group.groupId] = []
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
} catch (error) {
|
|
173
|
+
console.error('[VueChatKit] 获取群聊列表失败', error)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// 获取群聊历史消息
|
|
178
|
+
const getGroupHistory = async (groupId) => {
|
|
179
|
+
try {
|
|
180
|
+
const res = await api.getGroupHistory(groupId, myUsername)
|
|
181
|
+
groupMsgList.value = res.data || []
|
|
182
|
+
scrollToBottom()
|
|
183
|
+
} catch (error) {
|
|
184
|
+
console.error('[VueChatKit] 获取群聊历史失败', error)
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// 获取群成员列表
|
|
189
|
+
const getGroupMembers = async (groupId) => {
|
|
190
|
+
try {
|
|
191
|
+
const res = await api.getGroupMembers(groupId, myUsername)
|
|
192
|
+
groupMemberList.value = res.data || []
|
|
193
|
+
} catch (error) {
|
|
194
|
+
console.error('[VueChatKit] 获取群成员失败', error)
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// 选择群聊
|
|
199
|
+
const selectGroup = async (group) => {
|
|
200
|
+
// 1. 先标记该群全部消息为已读
|
|
201
|
+
await readAllGroupMsg(group.groupId)
|
|
202
|
+
|
|
203
|
+
// 2. 刷新群列表,未读数清零
|
|
204
|
+
await getGroupList()
|
|
205
|
+
|
|
206
|
+
// 3. 设置当前选中的群
|
|
207
|
+
currentSelectGroup.value = group
|
|
208
|
+
|
|
209
|
+
// 4. 加载该群历史聊天消息
|
|
210
|
+
await getGroupHistory(group.groupId)
|
|
211
|
+
await getGroupMembers(group.groupId)
|
|
212
|
+
|
|
213
|
+
scrollToBottom()
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// 获取群未读消息数
|
|
217
|
+
const getGroupUnreadCount = async (groupId) => {
|
|
218
|
+
try {
|
|
219
|
+
const res = await api.getGroupUnreadCount(groupId, myUsername)
|
|
220
|
+
return res.data || 0
|
|
221
|
+
} catch (error) {
|
|
222
|
+
console.error('[VueChatKit] 获取群未读数失败', error)
|
|
223
|
+
return 0
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// 获取单条消息的已读/未读成员
|
|
228
|
+
const getMsgReadUserList = async (groupId, groupMsgId) => {
|
|
229
|
+
try {
|
|
230
|
+
const res = await api.getMsgReadUserList(groupId, groupMsgId, myUsername)
|
|
231
|
+
currentMsgReadList.value = res.data || { readUserList: [], unreadUserList: [] }
|
|
232
|
+
msgReadUserDialogVisible.value = true
|
|
233
|
+
} catch (error) {
|
|
234
|
+
console.error('[VueChatKit] 获取消息已读成员失败', error)
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// 获取群聊详情
|
|
239
|
+
const fetchGroupDetail = async (groupId) => {
|
|
240
|
+
try {
|
|
241
|
+
const res = await api.getGroupInfo(groupId, myUsername)
|
|
242
|
+
if (res.code === 200 && res.data) {
|
|
243
|
+
currentGroupInfo.value = res.data
|
|
244
|
+
// 初始化临时编辑值
|
|
245
|
+
tempEditValues.value = {
|
|
246
|
+
groupNickname: res.data.groupNickname || res.data.groupName || '',
|
|
247
|
+
remark: res.data.remark || '',
|
|
248
|
+
notice: res.data.notice || ''
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
} catch (error) {
|
|
252
|
+
console.error('[VueChatKit] 获取群详情失败', error)
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// 修改单个群设置字段
|
|
257
|
+
const updateSingleGroupField = async (field) => {
|
|
258
|
+
if (!currentSelectGroup.value || !currentGroupInfo.value) return false
|
|
259
|
+
try {
|
|
260
|
+
const updateData = {
|
|
261
|
+
groupId: currentSelectGroup.value.groupId,
|
|
262
|
+
currentUser: myUsername,
|
|
263
|
+
groupNickname: field === 'groupNickname' ? tempEditValues.value.groupNickname : currentGroupInfo.value.groupNickname,
|
|
264
|
+
remark: field === 'remark' ? tempEditValues.value.remark : currentGroupInfo.value.remark,
|
|
265
|
+
notice: field === 'notice' ? tempEditValues.value.notice : currentGroupInfo.value.notice
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const res = await api._updateGroupInfo(
|
|
269
|
+
updateData.groupId,
|
|
270
|
+
updateData.currentUser,
|
|
271
|
+
updateData.groupNickname,
|
|
272
|
+
updateData.remark,
|
|
273
|
+
updateData.notice
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
if (res.code === 200) {
|
|
277
|
+
// 刷新群详情
|
|
278
|
+
await fetchGroupDetail(currentSelectGroup.value.groupId)
|
|
279
|
+
// 刷新群列表
|
|
280
|
+
await getGroupList()
|
|
281
|
+
return true
|
|
282
|
+
}
|
|
283
|
+
return false
|
|
284
|
+
} catch (error) {
|
|
285
|
+
console.error('[VueChatKit] 修改群信息失败', error)
|
|
286
|
+
return false
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// 开始编辑字段
|
|
291
|
+
const startEditField = (field) => {
|
|
292
|
+
// 先确保有最新数据
|
|
293
|
+
if (!currentGroupInfo.value && currentSelectGroup.value) {
|
|
294
|
+
fetchGroupDetail(currentSelectGroup.value.groupId)
|
|
295
|
+
}
|
|
296
|
+
editingFields.value[field] = true
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// 取消编辑字段
|
|
300
|
+
const cancelEditField = (field) => {
|
|
301
|
+
editingFields.value[field] = false
|
|
302
|
+
// 重置临时值
|
|
303
|
+
if (currentGroupInfo.value) {
|
|
304
|
+
tempEditValues.value[field] = currentGroupInfo.value[field] || ''
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// 保存编辑字段
|
|
309
|
+
const saveEditField = async (field) => {
|
|
310
|
+
const success = await updateSingleGroupField(field)
|
|
311
|
+
if (success) {
|
|
312
|
+
editingFields.value[field] = false
|
|
313
|
+
}
|
|
314
|
+
return success
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// 修改群基础信息(保留旧方法用于兼容性)
|
|
318
|
+
const updateGroupInfo = async () => {
|
|
319
|
+
if (!currentSelectGroup.value) return false
|
|
320
|
+
try {
|
|
321
|
+
const res = await api.updateGroupInfo(
|
|
322
|
+
currentSelectGroup.value.groupId,
|
|
323
|
+
myUsername,
|
|
324
|
+
editingGroupInfo.value.groupNickname,
|
|
325
|
+
editingGroupInfo.value.remark,
|
|
326
|
+
editingGroupInfo.value.notice
|
|
327
|
+
)
|
|
328
|
+
if (res.code === 200) {
|
|
329
|
+
groupInfoVisible.value = false
|
|
330
|
+
await getGroupList()
|
|
331
|
+
// 刷新群详情
|
|
332
|
+
if (currentSelectGroup.value) {
|
|
333
|
+
await fetchGroupDetail(currentSelectGroup.value.groupId)
|
|
334
|
+
}
|
|
335
|
+
return true
|
|
336
|
+
}
|
|
337
|
+
return false
|
|
338
|
+
} catch (error) {
|
|
339
|
+
console.error('[VueChatKit] 修改群信息失败', error)
|
|
340
|
+
return false
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// 修改群成员昵称
|
|
345
|
+
const updateMemberNick = async () => {
|
|
346
|
+
if (!currentSelectGroup.value) return false
|
|
347
|
+
try {
|
|
348
|
+
const res = await api.updateMemberNick(
|
|
349
|
+
currentSelectGroup.value.groupId,
|
|
350
|
+
myUsername,
|
|
351
|
+
editingMemberNick.value.targetUsername,
|
|
352
|
+
editingMemberNick.value.memberNick
|
|
353
|
+
)
|
|
354
|
+
if (res.code === 200) {
|
|
355
|
+
memberNickDialogVisible.value = false
|
|
356
|
+
await getGroupMembers(currentSelectGroup.value.groupId)
|
|
357
|
+
return true
|
|
358
|
+
}
|
|
359
|
+
return false
|
|
360
|
+
} catch (error) {
|
|
361
|
+
console.error('[VueChatKit] 修改群成员昵称失败', error)
|
|
362
|
+
return false
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// 解散群聊
|
|
367
|
+
const deleteGroup = async () => {
|
|
368
|
+
if (!currentSelectGroup.value) return false
|
|
369
|
+
try {
|
|
370
|
+
const res = await api.deleteGroup(currentSelectGroup.value.groupId, myUsername)
|
|
371
|
+
if (res.code === 200) {
|
|
372
|
+
currentSelectGroup.value = null
|
|
373
|
+
groupMsgList.value = []
|
|
374
|
+
groupDetailVisible.value = false
|
|
375
|
+
await getGroupList()
|
|
376
|
+
return true
|
|
377
|
+
}
|
|
378
|
+
return false
|
|
379
|
+
} catch (error) {
|
|
380
|
+
console.error('[VueChatKit] 解散群聊失败', error)
|
|
381
|
+
return false
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// 移除群成员
|
|
386
|
+
const removeGroupMember = async (targetUsername) => {
|
|
387
|
+
if (!currentSelectGroup.value) return false
|
|
388
|
+
try {
|
|
389
|
+
const res = await api.removeGroupMember(
|
|
390
|
+
currentSelectGroup.value.groupId,
|
|
391
|
+
myUsername,
|
|
392
|
+
targetUsername
|
|
393
|
+
)
|
|
394
|
+
if (res.code === 200) {
|
|
395
|
+
await getGroupMembers(currentSelectGroup.value.groupId)
|
|
396
|
+
return true
|
|
397
|
+
}
|
|
398
|
+
return false
|
|
399
|
+
} catch (error) {
|
|
400
|
+
console.error('[VueChatKit] 移除成员失败', error)
|
|
401
|
+
return false
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// 转让群主
|
|
406
|
+
const transferGroupOwner = async (newOwnerUsername) => {
|
|
407
|
+
if (!currentSelectGroup.value) return false
|
|
408
|
+
try {
|
|
409
|
+
const res = await api.transferGroupOwner(
|
|
410
|
+
currentSelectGroup.value.groupId,
|
|
411
|
+
myUsername,
|
|
412
|
+
newOwnerUsername
|
|
413
|
+
)
|
|
414
|
+
if (res.code === 200) {
|
|
415
|
+
await getGroupMembers(currentSelectGroup.value.groupId)
|
|
416
|
+
return true
|
|
417
|
+
}
|
|
418
|
+
return false
|
|
419
|
+
} catch (error) {
|
|
420
|
+
console.error('[VueChatKit] 转让群主失败', error)
|
|
421
|
+
return false
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// 标记单条消息为已读
|
|
426
|
+
const readSingleGroupMsg = async (groupMsgId) => {
|
|
427
|
+
if (!currentSelectGroup.value) return false
|
|
428
|
+
try {
|
|
429
|
+
const res = await api.readSingleGroupMsg(
|
|
430
|
+
currentSelectGroup.value.groupId,
|
|
431
|
+
myUsername,
|
|
432
|
+
groupMsgId
|
|
433
|
+
)
|
|
434
|
+
return res.code === 200
|
|
435
|
+
} catch (error) {
|
|
436
|
+
console.error('[VueChatKit] 标记消息已读失败', error)
|
|
437
|
+
return false
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// 一键标记全部消息已读
|
|
442
|
+
const readAllGroupMsg = async (groupId) => {
|
|
443
|
+
const targetGroupId = groupId || currentSelectGroup.value?.groupId
|
|
444
|
+
if (!targetGroupId) return false
|
|
445
|
+
try {
|
|
446
|
+
const res = await api.readAllGroupMsg(targetGroupId, myUsername)
|
|
447
|
+
return res.code === 200
|
|
448
|
+
} catch (error) {
|
|
449
|
+
console.error('[VueChatKit] 标记全部已读失败', error)
|
|
450
|
+
return false
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// 打开编辑群信息对话框
|
|
455
|
+
const openEditGroupInfo = () => {
|
|
456
|
+
if (!currentSelectGroup.value) return
|
|
457
|
+
editingGroupInfo.value = {
|
|
458
|
+
groupNickname: currentSelectGroup.value.name || '',
|
|
459
|
+
remark: currentSelectGroup.value.remark || '',
|
|
460
|
+
notice: currentSelectGroup.value.notice || ''
|
|
461
|
+
}
|
|
462
|
+
groupInfoVisible.value = true
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// 打开编辑成员昵称对话框
|
|
466
|
+
const openEditMemberNick = (member) => {
|
|
467
|
+
editingMemberNick.value = {
|
|
468
|
+
targetUsername: member.username,
|
|
469
|
+
memberNick: member.memberNick || ''
|
|
470
|
+
}
|
|
471
|
+
memberNickDialogVisible.value = true
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// 创建群聊
|
|
475
|
+
const createGroup = async () => {
|
|
476
|
+
if (!newGroupName.value.trim()) {
|
|
477
|
+
console.warn('[VueChatKit] 请输入群名称')
|
|
478
|
+
return false
|
|
479
|
+
}
|
|
480
|
+
try {
|
|
481
|
+
const res = await api.createGroup({
|
|
482
|
+
groupName: newGroupName.value.trim(),
|
|
483
|
+
remark: newGroupRemark.value.trim(),
|
|
484
|
+
ownerUsername: myUsername
|
|
485
|
+
})
|
|
486
|
+
if (res.code === 200 && res.data) {
|
|
487
|
+
if (selectedMembersForCreate.value.length > 0) {
|
|
488
|
+
const targetUserList = selectedMembersForCreate.value.map(m => m.id)
|
|
489
|
+
await api.inviteGroupMembers(
|
|
490
|
+
res.data.groupId || res.data,
|
|
491
|
+
myUsername,
|
|
492
|
+
targetUserList
|
|
493
|
+
)
|
|
494
|
+
}
|
|
495
|
+
newGroupName.value = ''
|
|
496
|
+
newGroupRemark.value = ''
|
|
497
|
+
selectedMembersForCreate.value = []
|
|
498
|
+
createGroupDialogVisible.value = false
|
|
499
|
+
await getGroupList()
|
|
500
|
+
return true
|
|
501
|
+
}
|
|
502
|
+
return false
|
|
503
|
+
} catch (error) {
|
|
504
|
+
console.error('[VueChatKit] 创建群聊失败', error)
|
|
505
|
+
return false
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
// 邀请成员到群聊
|
|
510
|
+
const inviteGroupMember = async () => {
|
|
511
|
+
if (!currentSelectGroup.value || selectedMembersForInvite.value.length === 0) {
|
|
512
|
+
return false
|
|
513
|
+
}
|
|
514
|
+
try {
|
|
515
|
+
const targetUserList = selectedMembersForInvite.value.map(m => m.id)
|
|
516
|
+
await api.inviteGroupMembers(
|
|
517
|
+
currentSelectGroup.value.groupId,
|
|
518
|
+
myUsername,
|
|
519
|
+
targetUserList
|
|
520
|
+
)
|
|
521
|
+
selectedMembersForInvite.value = []
|
|
522
|
+
inviteMemberDialogVisible.value = false
|
|
523
|
+
await getGroupMembers(currentSelectGroup.value.groupId)
|
|
524
|
+
return true
|
|
525
|
+
} catch (error) {
|
|
526
|
+
console.error('[VueChatKit] 邀请成员失败', error)
|
|
527
|
+
return false
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// 退出群聊
|
|
532
|
+
const quitGroup = async () => {
|
|
533
|
+
if (!currentSelectGroup.value) return false
|
|
534
|
+
try {
|
|
535
|
+
await api.quitGroup(currentSelectGroup.value.groupId, myUsername)
|
|
536
|
+
currentSelectGroup.value = null
|
|
537
|
+
groupMsgList.value = []
|
|
538
|
+
groupDetailVisible.value = false
|
|
539
|
+
await getGroupList()
|
|
540
|
+
return true
|
|
541
|
+
} catch (error) {
|
|
542
|
+
console.error('[VueChatKit] 退出群聊失败', error)
|
|
543
|
+
return false
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// 发送群聊文本消息
|
|
548
|
+
const sendGroupMessage = () => {
|
|
549
|
+
const socket = getSocket()
|
|
550
|
+
if (!core.inputText.value.trim() || !currentSelectGroup.value || !socket) return
|
|
551
|
+
const success = socket.send(currentSelectGroup.value.groupId, core.inputText.value.trim(), 'text', '', '', 0, true)
|
|
552
|
+
if (success) {
|
|
553
|
+
const tempMsg = {
|
|
554
|
+
msgContent: core.inputText.value.trim(),
|
|
555
|
+
sendUsername: myUsername,
|
|
556
|
+
toGroupId: currentSelectGroup.value.groupId,
|
|
557
|
+
createTime: new Date(),
|
|
558
|
+
isRead: 0,
|
|
559
|
+
type: 'text'
|
|
560
|
+
}
|
|
561
|
+
groupMsgList.value.push(tempMsg)
|
|
562
|
+
core.inputText.value = ''
|
|
563
|
+
scrollToBottom()
|
|
564
|
+
setTimeout(() => {
|
|
565
|
+
getGroupHistory(currentSelectGroup.value.groupId)
|
|
566
|
+
getGroupList()
|
|
567
|
+
}, 300)
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
// 发送群聊文件
|
|
572
|
+
const sendGroupFile = async (file) => {
|
|
573
|
+
const socket = getSocket()
|
|
574
|
+
if (!currentSelectGroup.value || !socket) return false
|
|
575
|
+
|
|
576
|
+
try {
|
|
577
|
+
const uploadRes = await api.uploadFile(file)
|
|
578
|
+
|
|
579
|
+
if (uploadRes.code === 200 && uploadRes.data) {
|
|
580
|
+
const { fileUrl, fileName } = uploadRes.data
|
|
581
|
+
|
|
582
|
+
const success = socket.send(
|
|
583
|
+
currentSelectGroup.value.groupId,
|
|
584
|
+
fileName,
|
|
585
|
+
'file',
|
|
586
|
+
fileUrl,
|
|
587
|
+
fileName,
|
|
588
|
+
file.size,
|
|
589
|
+
true
|
|
590
|
+
)
|
|
591
|
+
|
|
592
|
+
if (success) {
|
|
593
|
+
const tempMsg = {
|
|
594
|
+
msgContent: fileName,
|
|
595
|
+
sendUsername: myUsername,
|
|
596
|
+
toGroupId: currentSelectGroup.value.groupId,
|
|
597
|
+
createTime: new Date(),
|
|
598
|
+
isRead: 0,
|
|
599
|
+
type: 'file',
|
|
600
|
+
fileUrl: fileUrl,
|
|
601
|
+
fileName: fileName,
|
|
602
|
+
fileSize: file.size
|
|
603
|
+
}
|
|
604
|
+
groupMsgList.value.push(tempMsg)
|
|
605
|
+
scrollToBottom()
|
|
606
|
+
return true
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
return false
|
|
610
|
+
} catch (error) {
|
|
611
|
+
console.error('[VueChatKit] 发送群聊文件失败', error)
|
|
612
|
+
return false
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
// 批量发送群聊文件和文本
|
|
617
|
+
const sendGroupFilesAndText = async (files, text) => {
|
|
618
|
+
const socket = getSocket()
|
|
619
|
+
if (!currentSelectGroup.value || !socket) return
|
|
620
|
+
|
|
621
|
+
if (text && text.trim()) {
|
|
622
|
+
const textSuccess = socket.send(currentSelectGroup.value.groupId, text.trim(), 'text', '', '', 0, true)
|
|
623
|
+
if (textSuccess) {
|
|
624
|
+
const tempMsg = {
|
|
625
|
+
msgContent: text.trim(),
|
|
626
|
+
sendUsername: myUsername,
|
|
627
|
+
toGroupId: currentSelectGroup.value.groupId,
|
|
628
|
+
createTime: new Date(),
|
|
629
|
+
isRead: 0,
|
|
630
|
+
type: 'text'
|
|
631
|
+
}
|
|
632
|
+
groupMsgList.value.push(tempMsg)
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
for (const fileObj of files) {
|
|
637
|
+
const file = fileObj.file || fileObj
|
|
638
|
+
await sendGroupFile(file)
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
setTimeout(() => {
|
|
642
|
+
getGroupHistory(currentSelectGroup.value.groupId)
|
|
643
|
+
getGroupList()
|
|
644
|
+
}, 300)
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// 处理群聊 WebSocket 消息
|
|
648
|
+
const handleGroupWsMessage = (msgInfo) => {
|
|
649
|
+
if (currentSelectGroup.value && currentSelectGroup.value.groupId === msgInfo.toGroupId) {
|
|
650
|
+
try {
|
|
651
|
+
let tempMsg = {
|
|
652
|
+
msgContent: msgInfo.content,
|
|
653
|
+
sendUsername: msgInfo.from || '未知用户',
|
|
654
|
+
toGroupId: msgInfo.toGroupId,
|
|
655
|
+
createTime: new Date(),
|
|
656
|
+
isRead: 0,
|
|
657
|
+
type: msgInfo.type || 'text',
|
|
658
|
+
fileUrl: msgInfo.fileUrl || '',
|
|
659
|
+
fileName: msgInfo.fileName || '',
|
|
660
|
+
fileSize: msgInfo.fileSize || 0
|
|
661
|
+
}
|
|
662
|
+
groupMsgList.value.push(tempMsg)
|
|
663
|
+
scrollToBottom()
|
|
664
|
+
} catch (e) {
|
|
665
|
+
console.error('[VueChatKit] 添加临时群聊消息失败', e)
|
|
666
|
+
}
|
|
667
|
+
getGroupHistory(msgInfo.toGroupId)
|
|
668
|
+
}
|
|
669
|
+
getGroupList()
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
// 切换 Tab
|
|
673
|
+
const switchTab = (tab) => {
|
|
674
|
+
activeTab.value = tab
|
|
675
|
+
if (tab === 'groups') {
|
|
676
|
+
getGroupList()
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// 重置群聊状态
|
|
681
|
+
const resetGroupChat = () => {
|
|
682
|
+
currentSelectGroup.value = null
|
|
683
|
+
groupMsgList.value = []
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
return {
|
|
687
|
+
// 状态
|
|
688
|
+
activeTab,
|
|
689
|
+
groupList,
|
|
690
|
+
currentSelectGroup,
|
|
691
|
+
groupMsgList,
|
|
692
|
+
groupMemberList,
|
|
693
|
+
groupMembersMap,
|
|
694
|
+
createGroupDialogVisible,
|
|
695
|
+
groupDetailVisible,
|
|
696
|
+
newGroupName,
|
|
697
|
+
newGroupRemark,
|
|
698
|
+
selectedMembersForCreate,
|
|
699
|
+
inviteMemberDialogVisible,
|
|
700
|
+
selectedMembersForInvite,
|
|
701
|
+
groupInfoVisible,
|
|
702
|
+
editingGroupInfo,
|
|
703
|
+
editingMemberNick,
|
|
704
|
+
memberNickDialogVisible,
|
|
705
|
+
msgReadUserDialogVisible,
|
|
706
|
+
currentMsgReadList,
|
|
707
|
+
currentGroupInfo,
|
|
708
|
+
editingFields,
|
|
709
|
+
tempEditValues,
|
|
710
|
+
|
|
711
|
+
// 计算属性
|
|
712
|
+
filteredGroupList,
|
|
713
|
+
currentGroupMessages,
|
|
714
|
+
|
|
715
|
+
// 方法
|
|
716
|
+
getGroupList,
|
|
717
|
+
getGroupHistory,
|
|
718
|
+
getGroupMembers,
|
|
719
|
+
selectGroup,
|
|
720
|
+
createGroup,
|
|
721
|
+
inviteGroupMember,
|
|
722
|
+
quitGroup,
|
|
723
|
+
sendGroupMessage,
|
|
724
|
+
sendGroupFile,
|
|
725
|
+
sendGroupFilesAndText,
|
|
726
|
+
handleGroupWsMessage,
|
|
727
|
+
switchTab,
|
|
728
|
+
resetGroupChat,
|
|
729
|
+
getGroupUnreadCount,
|
|
730
|
+
getMsgReadUserList,
|
|
731
|
+
updateGroupInfo,
|
|
732
|
+
updateMemberNick,
|
|
733
|
+
deleteGroup,
|
|
734
|
+
removeGroupMember,
|
|
735
|
+
transferGroupOwner,
|
|
736
|
+
readSingleGroupMsg,
|
|
737
|
+
readAllGroupMsg,
|
|
738
|
+
openEditGroupInfo,
|
|
739
|
+
openEditMemberNick,
|
|
740
|
+
fetchGroupDetail,
|
|
741
|
+
updateSingleGroupField,
|
|
742
|
+
startEditField,
|
|
743
|
+
cancelEditField,
|
|
744
|
+
saveEditField
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
export default useGroupChat
|