From 61c9e1acf2fa46cda5fa0aee9faea455c70b5c8f Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 18 Jun 2026 08:59:04 -0700 Subject: [PATCH] =?UTF-8?q?fix(im):=20=E4=BF=AE=E5=A4=8D=E7=BE=A4=E5=A4=87?= =?UTF-8?q?=E6=B3=A8=E9=A6=96=E5=B1=8F=E5=B1=95=E7=A4=BA=E5=92=8C=E8=81=8A?= =?UTF-8?q?=E5=A4=A9=E5=88=97=E8=A1=A8=E5=90=8D=E7=A7=B0=E8=A6=86=E7=9B=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 后端群 VO 返回当前用户维度的 groupRemark 和 silent - 群列表构建时通过成员关系回填个人群设置,并继续仅对有效成员回填置顶消息 - Vue3 群列表同步时以接口返回的个人群设置为准,只保留成员缓存 - 会话名写入入口统一使用 getGroupDisplayName,避免群备注被原群名覆盖 - 空群头像且成员未加载时异步预拉群成员,用于合成群头像 - 启用 IM Maven 模块和 yudao-server 对 IM 模块的依赖 --- src/api/im/group/index.ts | 2 ++ .../components/user/RecommendCardDialog.vue | 4 +-- .../im/home/composables/useMessagePuller.ts | 4 +-- .../conversation/ConversationPrivateSide.vue | 10 +++---- .../components/message/MessagePanel.vue | 4 ++- .../message/forward/MessageForwardDialog.vue | 4 +-- .../im/home/pages/conversation/index.vue | 3 ++- src/views/im/home/store/groupStore.ts | 26 ++++++++++++++++--- src/views/im/home/store/websocketStore.ts | 4 +-- 9 files changed, 42 insertions(+), 19 deletions(-) diff --git a/src/api/im/group/index.ts b/src/api/im/group/index.ts index 5d76639d8..d9b2f1f94 100644 --- a/src/api/im/group/index.ts +++ b/src/api/im/group/index.ts @@ -17,6 +17,8 @@ export interface ImGroupRespVO { createTime?: string // 创建时间 pinnedMessages?: ImGroupMessageRespVO[] // 群置顶消息列表(后端关联回填,仅当登录用户是群成员时非空) joinStatus?: number // 当前登录用户在该群的成员状态(参见 CommonStatusEnum:0 在群 / 1 已退群);历史退群群仍返回,供展示离线消息的群名 / 头像 + groupRemark?: string // 当前登录用户对该群的备注 + silent?: boolean // 当前登录用户是否免打扰 } // 群消息置顶 / 取消置顶 Request VO diff --git a/src/views/im/home/components/user/RecommendCardDialog.vue b/src/views/im/home/components/user/RecommendCardDialog.vue index 95a5ef24b..a2ce6645b 100644 --- a/src/views/im/home/components/user/RecommendCardDialog.vue +++ b/src/views/im/home/components/user/RecommendCardDialog.vue @@ -119,7 +119,7 @@ import { ImConversationType, ImContentType, isGroupConversation } from '../../.. import { getConversationKey } from '../../../utils/conversation' import { buildDefaultGroupName } from '../../../utils/group' import { serializeMessage, type CardTarget } from '../../../utils/message' -import { isGroupQuit } from '../../../utils/user' +import { getGroupDisplayName, isGroupQuit } from '../../../utils/user' import type { Conversation, FriendLite } from '../../types' defineOptions({ name: 'ImRecommendCardDialog' }) @@ -283,7 +283,7 @@ async function handleCreateGroupAndSend() { const newConversation: Conversation = { type: ImConversationType.GROUP, targetId: group.id, - name: group.name || name, + name: getGroupDisplayName(group) || name, avatar: group.avatar || '', unreadCount: 0, lastContent: '', diff --git a/src/views/im/home/composables/useMessagePuller.ts b/src/views/im/home/composables/useMessagePuller.ts index dbb8c55da..74a41b41b 100644 --- a/src/views/im/home/composables/useMessagePuller.ts +++ b/src/views/im/home/composables/useMessagePuller.ts @@ -3,7 +3,7 @@ import { useConversationStore } from '../store/conversationStore' import { useMessageStore, type PulledMessage } from '../store/messageStore' import { useImWebSocketStore } from '../store/websocketStore' import { useFriendStore } from '../store/friendStore' -import { getFriendDisplayName } from '../../utils/user' +import { getFriendDisplayName, getGroupDisplayName } from '../../utils/user' import { useGroupStore } from '../store/groupStore' import { useGroupRequestStore } from '../store/groupRequestStore' import { @@ -149,7 +149,7 @@ export const useMessagePuller = () => { return { type: ImConversationType.GROUP, targetId: message.groupId, - name: group?.name || String(message.groupId), + name: group ? getGroupDisplayName(group) : String(message.groupId), avatar: group?.avatar || '', silent: group?.silent } diff --git a/src/views/im/home/pages/conversation/components/conversation/ConversationPrivateSide.vue b/src/views/im/home/pages/conversation/components/conversation/ConversationPrivateSide.vue index 2d1577e51..e85b43e58 100644 --- a/src/views/im/home/pages/conversation/components/conversation/ConversationPrivateSide.vue +++ b/src/views/im/home/pages/conversation/components/conversation/ConversationPrivateSide.vue @@ -85,9 +85,9 @@ />
取消 - 保存 + + 保存 +
@@ -141,7 +141,7 @@ import { useMessage } from '@/hooks/web/useMessage' import { useConversationStore } from '@/views/im/home/store/conversationStore' import { useFriendStore } from '@/views/im/home/store/friendStore' import { useGroupStore } from '@/views/im/home/store/groupStore' -import { getFriendDisplayName } from '@/views/im/utils/user' +import { getFriendDisplayName, getGroupDisplayName } from '@/views/im/utils/user' import { ImConversationType } from '@/views/im/utils/constants' import type { Conversation, Friend } from '../../../../types' @@ -248,7 +248,7 @@ function handleGroupCreated(groupId: number) { conversationStore.openConversation( groupId, ImConversationType.GROUP, - group.name, + getGroupDisplayName(group), group.avatar || '', { silent: !!group.silent } ) diff --git a/src/views/im/home/pages/conversation/components/message/MessagePanel.vue b/src/views/im/home/pages/conversation/components/message/MessagePanel.vue index 61224a6ba..3ba0836bf 100644 --- a/src/views/im/home/pages/conversation/components/message/MessagePanel.vue +++ b/src/views/im/home/pages/conversation/components/message/MessagePanel.vue @@ -260,6 +260,7 @@ import { ImRtcCallMediaType, ImRtcCallStatus, ImConversationType } from '@/views import { resolveCallEndReasonText } from '@/views/im/utils/message' import { getClientConversationId } from '@/views/im/utils/db' import { getCurrentUserId } from '@/utils/auth' +import { getGroupDisplayName } from '@/views/im/utils/user' import { useRtcStore } from '../../../../store/rtcStore' import { useMessageStore } from '../../../../store/messageStore' @@ -405,10 +406,11 @@ const groupInfo = computed< } const group = groupStore.getGroup(conversation.targetId) const selfMember = group?.members?.find((member) => member.userId === getCurrentUserId()) + const showGroupName = group ? getGroupDisplayName(group) : conversation.name return { id: conversation.targetId, name: group?.name || conversation.name, - showGroupName: group?.name || conversation.name, + showGroupName, showImage: group?.avatar || conversation.avatar, notice: group?.notice, remarkNickName: selfMember?.displayUserName, diff --git a/src/views/im/home/pages/conversation/components/message/forward/MessageForwardDialog.vue b/src/views/im/home/pages/conversation/components/message/forward/MessageForwardDialog.vue index 440473768..7e5f2fc65 100644 --- a/src/views/im/home/pages/conversation/components/message/forward/MessageForwardDialog.vue +++ b/src/views/im/home/pages/conversation/components/message/forward/MessageForwardDialog.vue @@ -154,7 +154,7 @@ import FacePicker from '../../input/FacePicker.vue' import { useConversationStore } from '@/views/im/home/store/conversationStore' import { useFriendStore } from '@/views/im/home/store/friendStore' import { useGroupStore } from '@/views/im/home/store/groupStore' -import { isGroupQuit } from '@/views/im/utils/user' +import { getGroupDisplayName, isGroupQuit } from '@/views/im/utils/user' import { useMessageSender } from '@/views/im/home/composables/useMessageSender' import { useMessageMultiSelect } from '@/views/im/home/composables/useMessageMultiSelect' import { @@ -406,7 +406,7 @@ async function handleCreateGroupAndSend() { const newConversation: Conversation = { type: ImConversationType.GROUP, targetId: group.id, - name: group.name || name, + name: getGroupDisplayName(group) || name, avatar: group.avatar || '', unreadCount: 0, lastContent: '', diff --git a/src/views/im/home/pages/conversation/index.vue b/src/views/im/home/pages/conversation/index.vue index 623a92716..b0edc825a 100644 --- a/src/views/im/home/pages/conversation/index.vue +++ b/src/views/im/home/pages/conversation/index.vue @@ -104,6 +104,7 @@ import { useImUiStore } from '../../store/uiStore' import { ImConversationType } from '../../../utils/constants' import { StorageKeys } from '../../../utils/db' import { filterConversationsByKeyword, getConversationKey } from '../../../utils/conversation' +import { getGroupDisplayName } from '../../../utils/user' import type { Conversation } from '../../types' import ResizableAside from '../../components/ResizableAside.vue' import ConversationItem from './components/conversation/ConversationItem.vue' @@ -220,7 +221,7 @@ function handleGroupCreated(groupId: number) { conversationStore.openConversation( groupId, ImConversationType.GROUP, - group.name, + getGroupDisplayName(group), group.avatar || '', { silent: !!group.silent } ) diff --git a/src/views/im/home/store/groupStore.ts b/src/views/im/home/store/groupStore.ts index 4b131f60c..c535ac769 100644 --- a/src/views/im/home/store/groupStore.ts +++ b/src/views/im/home/store/groupStore.ts @@ -227,7 +227,7 @@ export const useGroupStore = defineStore('imGroupStore', { return } const fresh = (list || []).map((group) => convertGroup(group)) - // 合并而非全量替换:silent / groupRemark / 成员缓存这些字段不在 ImGroupRespVO 里,得从旧 group 保留 + // 合并而非全量替换:成员缓存只在成员列表接口维护,群个人设置以群列表接口为准 const groupMap = new Map(this.groups.map((group) => [group.id, group])) this.groups = fresh.map((group) => { const existing = groupMap.get(group.id) @@ -238,8 +238,6 @@ export const useGroupStore = defineStore('imGroupStore', { ...group, members: existing.members, memberCount: existing.memberCount ?? group.memberCount, - silent: existing.silent ?? group.silent, - groupRemark: existing.groupRemark, membersLoaded: existing.membersLoaded, membersExpired: existing.membersExpired } @@ -254,6 +252,24 @@ export const useGroupStore = defineStore('imGroupStore', { }) } this.saveGroupList() + this.preloadMembersForEmptyAvatarGroups() + }, + + /** 预加载空群头像的成员列表,供 GroupAvatar 异步合成群头像 */ + preloadMembersForEmptyAvatarGroups() { + for (const group of this.groups) { + if ( + group.avatar || + group.joinStatus === CommonStatusEnum.DISABLE || + (group.membersLoaded && !group.membersExpired && group.members?.length) + ) { + continue + } + const force = !!group.membersLoaded && !group.membersExpired && !group.members?.length + this.fetchGroupMemberList(group.id, force).catch((error) => { + console.warn('[IM groupStore] 预加载群头像成员失败', { groupId: group.id }, error) + }) + } }, /** 失效全部群成员缓存 */ @@ -892,7 +908,9 @@ function convertGroup(group: ImGroupRespVO): Group { mutedAll: group.mutedAll, banned: group.banned, joinApproval: group.joinApproval, - joinStatus: group.joinStatus + joinStatus: group.joinStatus, + groupRemark: group.groupRemark, + silent: group.silent } } diff --git a/src/views/im/home/store/websocketStore.ts b/src/views/im/home/store/websocketStore.ts index 3bf68edb8..012ca3a09 100644 --- a/src/views/im/home/store/websocketStore.ts +++ b/src/views/im/home/store/websocketStore.ts @@ -30,7 +30,7 @@ import { import { useConversationStore } from './conversationStore' import { useMessageStore } from './messageStore' import { useFriendStore, type FriendNotificationPayload } from './friendStore' -import { getFriendDisplayName } from '../../utils/user' +import { getFriendDisplayName, getGroupDisplayName } from '../../utils/user' import { useGroupStore } from './groupStore' import { useGroupRequestStore } from './groupRequestStore' import { @@ -732,7 +732,7 @@ export const useImWebSocketStore = defineStore('imWebSocketStore', { { type: ImConversationType.GROUP, targetId: websocketMessage.groupId, - name: group?.name || String(websocketMessage.groupId), + name: group ? getGroupDisplayName(group) : String(websocketMessage.groupId), avatar: group?.avatar || '', silent: group?.silent },