From 66e8db2e4645e89ecde3ff477e1401bac78e6558 Mon Sep 17 00:00:00 2001 From: liqi Date: Fri, 5 Dec 2025 15:23:14 +0800 Subject: [PATCH] =?UTF-8?q?=E7=BE=A4=E8=81=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/modules/room.ts | 6 +- src/components/chat/GroupChatPanel.vue | 390 +++++++++++++++++++- src/components/chat/GroupInfoPanel.vue | 83 ++++- src/components/chat/MessageBubble.vue | 4 +- src/components/chat/MessageInput.vue | 155 ++++++-- src/components/chat/MessageList.vue | 48 ++- src/components/chat/SelectContactsModal.vue | 61 +-- src/components/common/Toast.vue | 14 + src/composables/useWebRTC.ts | 2 +- src/stores/conversation.ts | 19 +- src/stores/toast.ts | 8 +- src/views/chat/ChatView.vue | 389 ++++++++++++++++++- 12 files changed, 1076 insertions(+), 103 deletions(-) diff --git a/src/api/modules/room.ts b/src/api/modules/room.ts index 62041e9..49b6853 100644 --- a/src/api/modules/room.ts +++ b/src/api/modules/room.ts @@ -58,8 +58,10 @@ export function getGroup(roomId: string) { } // 获取群成员列表 -export function getGroupMembers(roomId: string) { - return request.get(`/groups/${roomId}/members`) +export function getGroupMembers(roomId: string, keyword?: string) { + return request.get(`/groups/${roomId}/members`, { + params: keyword ? { keyword } : undefined + }) } // 邀请成员入群 diff --git a/src/components/chat/GroupChatPanel.vue b/src/components/chat/GroupChatPanel.vue index d629256..7f1f837 100644 --- a/src/components/chat/GroupChatPanel.vue +++ b/src/components/chat/GroupChatPanel.vue @@ -1,5 +1,52 @@ diff --git a/src/components/chat/GroupInfoPanel.vue b/src/components/chat/GroupInfoPanel.vue index 587acce..bba77c0 100644 --- a/src/components/chat/GroupInfoPanel.vue +++ b/src/components/chat/GroupInfoPanel.vue @@ -62,7 +62,7 @@ @@ -190,6 +190,40 @@ + + + + + + + @@ -202,6 +236,7 @@ import { useToastStore } from '@/stores/toast' import * as groupApi from '@/api/modules/room' import * as contactApi from '@/api/modules/contact' import Avatar from '@/components/common/Avatar.vue' +import ConfirmModal from '@/components/common/ConfirmModal.vue' interface Props { show: boolean @@ -228,6 +263,10 @@ const availableContacts = ref([]) const showInviteModal = ref(false) const showEditModal = ref(false) const editForm = ref({ name: '', avatar: '' }) +const showRemoveMemberConfirm = ref(false) +const showQuitConfirm = ref(false) +const showDissolveConfirm = ref(false) +const pendingMember = ref(null) // 当前用户角色 const currentUserRole = computed(() => { @@ -248,6 +287,12 @@ const canInvite = computed(() => isOwner.value || isAdmin.value) // 是否可以编辑群信息 const canEdit = computed(() => isOwner.value) +// 移除成员确认消息 +const removeMemberMessage = computed(() => { + const name = pendingMember.value?.user?.name || pendingMember.value?.nickname || '未知' + return `确定要将 ${name} 移出群聊吗?` +}) + // 加载群信息 async function loadGroupInfo() { if (!props.roomId) return @@ -295,14 +340,19 @@ function handleMemberClick(member: GroupMember) { } // 移除成员 -async function handleRemoveMember(member: GroupMember) { - if (!confirm(`确定要将 ${member.user?.name || member.nickname} 移出群聊吗?`)) { - return - } +function showRemoveMemberConfirmDialog(member: GroupMember) { + pendingMember.value = member + showRemoveMemberConfirm.value = true +} + +async function handleRemoveMember() { + if (!pendingMember.value) return try { - await groupApi.removeGroupMember(props.roomId, member.user_id) + await groupApi.removeGroupMember(props.roomId, pendingMember.value.user_id) toastStore.success('已移除成员') + showRemoveMemberConfirm.value = false + pendingMember.value = null await loadGroupInfo() emit('updated') } catch (error: any) { @@ -345,14 +395,15 @@ async function handleUpdateGroup() { } // 退出群聊 +function showQuitConfirmDialog() { + showQuitConfirm.value = true +} + async function handleQuit() { - if (!confirm('确定要退出该群聊吗?')) { - return - } - try { await groupApi.quitGroup(props.roomId) toastStore.success('已退出群聊') + showQuitConfirm.value = false emit('quit') emit('close') } catch (error: any) { @@ -361,11 +412,11 @@ async function handleQuit() { } // 解散群聊 +function showDissolveConfirmDialog() { + showDissolveConfirm.value = true +} + async function handleDissolve() { - if (!confirm('确定要解散该群聊吗?此操作不可恢复!')) { - return - } - try { await groupApi.dissolveGroup(props.roomId) toastStore.success('群聊已解散') diff --git a/src/components/chat/MessageBubble.vue b/src/components/chat/MessageBubble.vue index e46a999..eaf1208 100644 --- a/src/components/chat/MessageBubble.vue +++ b/src/components/chat/MessageBubble.vue @@ -1,7 +1,7 @@ @@ -129,13 +129,17 @@ import Avatar from '@/components/common/Avatar.vue' interface Props { show: boolean contacts: Contact[] + mode?: 'create' | 'invite' // 创建群聊或邀请好友 } -const props = defineProps() +const props = withDefaults(defineProps(), { + mode: 'create' +}) const emit = defineEmits<{ 'close': [] - 'create': [data: { member_ids: string[]; name: string; avatar?: string }] + 'create': [data: { member_ids: string[]; name?: string; avatar?: string }] + 'invite': [data: { member_ids: string[] }] }>() const searchQuery = ref('') @@ -174,20 +178,29 @@ function removeContact(contactId: string) { } } -// 创建群聊 +// 创建群聊或邀请好友 function handleCreate() { - if (selectedContacts.value.length < 1 || !groupName.value.trim()) { + if (selectedContacts.value.length < 1) { return } const memberIds = selectedContacts.value.map(c => c.user_id || c.id) - const avatar = groupName.value.charAt(0).toUpperCase() // 使用群名称首字母作为头像 - emit('create', { - member_ids: memberIds, - name: groupName.value.trim(), - avatar - }) + if (props.mode === 'invite') { + emit('invite', { + member_ids: memberIds + }) + } else { + if (!groupName.value.trim()) { + return + } + const avatar = groupName.value.charAt(0).toUpperCase() // 使用群名称首字母作为头像 + emit('create', { + member_ids: memberIds, + name: groupName.value.trim(), + avatar + }) + } // 重置状态 selectedContacts.value = [] diff --git a/src/components/common/Toast.vue b/src/components/common/Toast.vue index 88dfe50..496af88 100644 --- a/src/components/common/Toast.vue +++ b/src/components/common/Toast.vue @@ -41,6 +41,13 @@
{{ toast.description }}
+ @@ -65,6 +72,13 @@ const toasts = computed(() => toastStore.toasts) function removeToast(id: string) { toastStore.removeToast(id) } + +function handleAction(toast: Toast) { + if (toast.action) { + toast.action.handler() + removeToast(toast.id) + } +}