From 4d006f8e7316623e345db7191822427ec70587a8 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Tue, 5 May 2026 13:51:53 +0800 Subject: [PATCH] =?UTF-8?q?feat(im)=EF=BC=9A=E5=B0=86"=E5=85=8D=E6=89=93?= =?UTF-8?q?=E6=89=B0"=E5=AD=97=E6=AE=B5=E4=BB=8E=20muted=20=E5=85=A8?= =?UTF-8?q?=E9=87=8F=E9=87=8D=E5=91=BD=E5=90=8D=E4=B8=BA=20silent=EF=BC=88?= =?UTF-8?q?DO/VO/Service/Mapper/=E6=B5=8B=E8=AF=95/SQL=20+=20=E5=89=8D?= =?UTF-8?q?=E7=AB=AF=20types/store/=E7=BB=84=E4=BB=B6/=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E5=90=8E=E5=8F=B0=EF=BC=89=EF=BC=8C=E4=B8=BA=E5=90=8E=E7=BB=AD?= =?UTF-8?q?=20mute=20=E7=A6=81=E8=A8=80=E5=8A=9F=E8=83=BD=E8=85=BE?= =?UTF-8?q?=E5=87=BA=E8=AF=8D=E6=97=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/im/friend/index.ts | 4 +-- src/api/im/group/member/index.ts | 4 +-- src/api/im/manager/friend/index.ts | 2 +- src/api/im/manager/group/index.ts | 2 +- .../im/home/components/user/UserInfoCard.vue | 2 +- src/views/im/home/index.vue | 2 +- src/views/im/home/pages/contact/index.vue | 6 ++-- .../conversation/ConversationGroupSide.vue | 12 +++---- .../conversation/ConversationItem.vue | 20 +++++------ .../conversation/ConversationPrivateSide.vue | 10 +++--- .../im/home/pages/conversation/index.vue | 4 +-- src/views/im/home/store/conversationStore.ts | 34 +++++++++---------- src/views/im/home/store/friendStore.ts | 24 ++++++------- src/views/im/home/store/groupStore.ts | 34 +++++++++---------- src/views/im/home/store/websocketStore.ts | 12 +++---- src/views/im/home/types/index.ts | 6 ++-- src/views/im/manager/friend/index.vue | 10 +++--- src/views/im/manager/group/GroupDetail.vue | 4 +-- src/views/im/utils/constants.ts | 4 +-- src/views/im/utils/storage.ts | 2 +- 20 files changed, 99 insertions(+), 99 deletions(-) diff --git a/src/api/im/friend/index.ts b/src/api/im/friend/index.ts index 8f0d4613e..c16e478e8 100644 --- a/src/api/im/friend/index.ts +++ b/src/api/im/friend/index.ts @@ -4,7 +4,7 @@ import request from '@/config/axios' export interface ImFriendRespVO { id: number // 关系记录编号 friendUserId: number // 好友的用户编号 - muted?: boolean // 是否免打扰 + silent?: boolean // 是否免打扰 displayName?: string // 好友展示备注(仅自己可见) displayNamePinyin?: string // 备注的拼音(小写无空格,前端按首字母分桶 / 拼音搜索) addSource?: number // 添加来源;参见 ImFriendAddSourceEnum @@ -22,7 +22,7 @@ export interface ImFriendRespVO { // IM 好友更新 Request VO export interface ImFriendUpdateReqVO { friendUserId: number // 好友的用户编号 - muted?: boolean // 是否免打扰 + silent?: boolean // 是否免打扰 displayName?: string // 好友展示备注 pinned?: boolean // 是否置顶联系人 } diff --git a/src/api/im/group/member/index.ts b/src/api/im/group/member/index.ts index d3c0f247b..2c65016fb 100644 --- a/src/api/im/group/member/index.ts +++ b/src/api/im/group/member/index.ts @@ -7,7 +7,7 @@ export interface ImGroupMemberRespVO { userId: number // 用户编号 displayUserName?: string // 组内显示名(群主设置的备注) groupRemark?: string // 群备注(当前用户对群的备注) - muted?: boolean // 是否免打扰 + silent?: boolean // 是否免打扰 status?: number // 成员状态(0=在群,1=退群) role?: number // 成员角色,参见 ImGroupMemberRole 枚举 joinTime?: string // 入群时间 @@ -35,7 +35,7 @@ export interface ImGroupMemberUpdateReqVO { groupId: number // 群编号 displayUserName?: string // 群内昵称 groupRemark?: string // 群备注 - muted?: boolean // 是否免打扰 + silent?: boolean // 是否免打扰 } // 邀请用户加入群 diff --git a/src/api/im/manager/friend/index.ts b/src/api/im/manager/friend/index.ts index 20aa673fb..93bfc4c14 100644 --- a/src/api/im/manager/friend/index.ts +++ b/src/api/im/manager/friend/index.ts @@ -7,7 +7,7 @@ export interface ImManagerFriendVO { friendUserId: number friendNickname?: string displayName?: string - muted: boolean + silent: boolean status: number addTime?: Date deleteTime?: Date diff --git a/src/api/im/manager/group/index.ts b/src/api/im/manager/group/index.ts index e2beb5f38..8c8d493e6 100644 --- a/src/api/im/manager/group/index.ts +++ b/src/api/im/manager/group/index.ts @@ -22,7 +22,7 @@ export interface ImManagerGroupMemberVO { avatar?: string displayUserName?: string groupRemark?: string - muted?: boolean + silent?: boolean status: number role?: number // 成员角色,参见 ImGroupMemberRole 枚举 joinTime?: Date diff --git a/src/views/im/home/components/user/UserInfoCard.vue b/src/views/im/home/components/user/UserInfoCard.vue index a5da452fc..825417bc8 100644 --- a/src/views/im/home/components/user/UserInfoCard.vue +++ b/src/views/im/home/components/user/UserInfoCard.vue @@ -110,7 +110,7 @@ function handleSendMessage() { ImConversationType.PRIVATE, conversationName, user.value.avatar || '', - { muted: !!friend?.muted } + { silent: !!friend?.silent } ) // 跳转会话 Tab(如果不在的话),并且不管当前路由是啥都先切到会话列表页 if (router.currentRoute.value.name !== 'ImHomeConversation') { diff --git a/src/views/im/home/index.vue b/src/views/im/home/index.vue index f4a182cbb..deb623aad 100644 --- a/src/views/im/home/index.vue +++ b/src/views/im/home/index.vue @@ -113,7 +113,7 @@ function pickFirstVisibleConversation(sorted: Conversation[]): Conversation | un if (pinnedExpanded) { return sorted[0] } - return sorted.find((c) => !c.top || (!c.muted && (c.unreadCount || 0) > 0)) ?? sorted[0] + return sorted.find((c) => !c.top || (!c.silent && (c.unreadCount || 0) > 0)) ?? sorted[0] } /** 标签关闭前 flush 草稿队列;debounce 默认 trail-edge 触发,最后一次输入可能还压在队列里 */ diff --git a/src/views/im/home/pages/contact/index.vue b/src/views/im/home/pages/contact/index.vue index 51570a7de..2cd7da0cf 100644 --- a/src/views/im/home/pages/contact/index.vue +++ b/src/views/im/home/pages/contact/index.vue @@ -204,7 +204,7 @@ function handleChatPeer(peerUserId: number) { ImConversationType.PRIVATE, conversationName, friend?.avatar || '', - { muted: !!friend?.muted } + { silent: !!friend?.silent } ) router.push({ name: 'ImHomeConversation' }) } @@ -219,7 +219,7 @@ function handleChatFriend(friend: FriendLite) { ImConversationType.PRIVATE, conversationName, friend.avatar || '', - { muted: !!entry?.muted } + { silent: !!entry?.silent } ) router.push({ name: 'ImHomeConversation' }) } @@ -232,7 +232,7 @@ function handleChatGroup(group: GroupLite) { ImConversationType.GROUP, group.showGroupName || group.name || '', group.showImage || group.showImageThumb || '', - { muted: !!entry?.muted } + { silent: !!entry?.silent } ) router.push({ name: 'ImHomeConversation' }) } diff --git a/src/views/im/home/pages/conversation/components/conversation/ConversationGroupSide.vue b/src/views/im/home/pages/conversation/components/conversation/ConversationGroupSide.vue index 7022fbd9d..d12166c67 100644 --- a/src/views/im/home/pages/conversation/components/conversation/ConversationGroupSide.vue +++ b/src/views/im/home/pages/conversation/components/conversation/ConversationGroupSide.vue @@ -261,7 +261,7 @@
消息免打扰 - +
置顶聊天 @@ -549,7 +549,7 @@ async function saveRemark() { // ==================== 开关切换 ==================== /** - * 消息免打扰:本地 conversationStore 立即切;后端 /muted 异步同步,失败回滚本地 + * 消息免打扰:本地 conversationStore 立即切;后端 /silent 异步同步,失败回滚本地 * * 与 ConversationItem 右键菜单的"消息免打扰"语义一致;区别仅在 UI 入口 */ @@ -559,10 +559,10 @@ function onMutedChange(value: boolean | string | number) { } const next = !!value const { type, targetId } = props.conversation - conversationStore.setMuted(type, targetId, next) - groupStore.setMuted(targetId, next).catch((error) => { - console.error('[IM ConversationGroupSide] setMuted 失败', { targetId }, error) - conversationStore.setMuted(type, targetId, !next) + conversationStore.setSilent(type, targetId, next) + groupStore.setSilent(targetId, next).catch((error) => { + console.error('[IM ConversationGroupSide] setSilent 失败', { targetId }, error) + conversationStore.setSilent(type, targetId, !next) message.error('操作失败') }) } diff --git a/src/views/im/home/pages/conversation/components/conversation/ConversationItem.vue b/src/views/im/home/pages/conversation/components/conversation/ConversationItem.vue index 8ebdda8f2..b2bdf7bec 100644 --- a/src/views/im/home/pages/conversation/components/conversation/ConversationItem.vue +++ b/src/views/im/home/pages/conversation/components/conversation/ConversationItem.vue @@ -14,7 +14,7 @@ :clickable="false" /> {{ conversation.unreadCount > 99 ? '99+' : conversation.unreadCount }} @@ -58,10 +58,10 @@
@@ -192,17 +192,17 @@ function handleTop() { /** 切换免打扰:乐观 UI(先本地切换,菜单立即关;后端失败回滚 conversation 状态) */ function handleMuted() { - const next = !props.conversation.muted + const next = !props.conversation.silent const { type, targetId } = props.conversation - conversationStore.setMuted(type, targetId, next) + conversationStore.setSilent(type, targetId, next) const sync = type === ImConversationType.PRIVATE - ? friendStore.setMuted(targetId, next) - : groupStore.setMuted(targetId, next) + ? friendStore.setSilent(targetId, next) + : groupStore.setSilent(targetId, next) sync.catch((e) => { console.error('[IM] 切换免打扰失败', e) message.error('切换免打扰失败') - conversationStore.setMuted(type, targetId, !next) + conversationStore.setSilent(type, targetId, !next) }) } @@ -220,7 +220,7 @@ function handleContextMenu(e: MouseEvent) { { x: e.clientX, y: e.clientY }, [ { key: 'TOP', name: props.conversation.top ? '取消置顶' : '置顶' }, - { key: 'MUTED', name: props.conversation.muted ? '允许消息通知' : '消息免打扰' }, + { key: 'MUTED', name: props.conversation.silent ? '允许消息通知' : '消息免打扰' }, { key: 'DELETE', name: '删除', divided: true, danger: true } ], (item) => { @@ -276,7 +276,7 @@ function formatTime(timestamp: number): string { } /* el-icon 的全局 color:var(--color) 在暗色模式下会渲染成白色,这里用 :deep + !important 锁定 */ -.conversation-item__muted :deep(svg) { +.conversation-item__silent :deep(svg) { fill: currentColor !important; } 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 96cfff540..0315729c4 100644 --- a/src/views/im/home/pages/conversation/components/conversation/ConversationPrivateSide.vue +++ b/src/views/im/home/pages/conversation/components/conversation/ConversationPrivateSide.vue @@ -108,7 +108,7 @@
消息免打扰 - +
置顶聊天 @@ -217,14 +217,14 @@ function handleMutedChange(value: boolean | string | number) { } const next = !!value const { type, targetId } = props.conversation - conversationStore.setMuted(type, targetId, next) + conversationStore.setSilent(type, targetId, next) if (type !== ImConversationType.PRIVATE) { return } - friendStore.setMuted(targetId, next).catch((error) => { + friendStore.setSilent(targetId, next).catch((error) => { console.error('[IM ConversationPrivateSide] 切换免打扰失败', { targetId }, error) message.error('切换免打扰失败') - conversationStore.setMuted(type, targetId, !next) + conversationStore.setSilent(type, targetId, !next) }) } @@ -247,7 +247,7 @@ function handleGroupCreated(groupId: number) { ImConversationType.GROUP, group.name, group.avatar || '', - { muted: !!group.muted } + { silent: !!group.silent } ) visible.value = false } diff --git a/src/views/im/home/pages/conversation/index.vue b/src/views/im/home/pages/conversation/index.vue index ad52e1dfa..c1312ff99 100644 --- a/src/views/im/home/pages/conversation/index.vue +++ b/src/views/im/home/pages/conversation/index.vue @@ -186,7 +186,7 @@ const renderedPinnedConversations = computed(() => /** 与会话项右上角红点的可见条件保持一致:免打扰不亮,无未读不亮 */ function hasUnreadBadge(conversation: Conversation): boolean { - return !conversation.muted && (conversation.unreadCount || 0) > 0 + return !conversation.silent && (conversation.unreadCount || 0) > 0 } /** 是否为当前激活会话 */ @@ -227,7 +227,7 @@ function handleGroupCreated(groupId: number) { ImConversationType.GROUP, group.name, group.avatar || '', - { muted: !!group.muted } + { silent: !!group.silent } ) } diff --git a/src/views/im/home/store/conversationStore.ts b/src/views/im/home/store/conversationStore.ts index 7036dd37e..7e137d67d 100644 --- a/src/views/im/home/store/conversationStore.ts +++ b/src/views/im/home/store/conversationStore.ts @@ -97,7 +97,7 @@ export const useConversationStore = defineStore('imConversationStore', { /** 未读总数(免打扰会话不计入)—— 用于 ToolBar 红点 */ getTotalUnread(state): number { return state.conversations - .filter((c) => !c.deleted && !c.muted) + .filter((c) => !c.deleted && !c.silent) .reduce((sum, c) => sum + (c.unreadCount || 0), 0) }, /** 查找会话:按 (type, targetId) 组合主键 */ @@ -169,7 +169,7 @@ export const useConversationStore = defineStore('imConversationStore', { /** * 持久化到 IndexedDB(fire-and-forget;调用方无需 await) * - * - 不传 target:仅写 meta(适用于 top / muted / unread 等元数据变更) + * - 不传 target:仅写 meta(适用于 top / silent / unread 等元数据变更) * - 传单个 conversation:写 meta + 该会话的消息(单条消息变更走这里) * - 传数组:写 meta + 数组里所有未删除会话的消息(loading 完成后兜底 flush 用) * @@ -229,7 +229,7 @@ export const useConversationStore = defineStore('imConversationStore', { /** * 打开或创建一个会话,并设为激活 * - * 调用方应该把从 friendStore / groupStore 拿到的最新元数据(muted 等) + * 调用方应该把从 friendStore / groupStore 拿到的最新元数据(silent 等) * 通过 options 传进来,避免新建/复用的会话显示陈旧状态。 * 此处不在 conversationStore 里反向 import friendStore/groupStore,是为了避免循环依赖。 */ @@ -238,26 +238,26 @@ export const useConversationStore = defineStore('imConversationStore', { type: number, name: string, avatar: string, - options?: { muted?: boolean } + options?: { silent?: boolean } ): Conversation { // 按 (type, targetId) 查找已有会话,不存在则新建并插到列表头部 let conversation = this.getConversation(type, targetId) if (!conversation) { conversation = this.createEmptyConversation(type, targetId, name, avatar) - if (options?.muted !== undefined) { - conversation.muted = options.muted + if (options?.silent !== undefined) { + conversation.silent = options.silent } this.conversations.unshift(conversation) } else { - // 已存在会话:用最新元数据刷新 name / avatar / muted + // 已存在会话:用最新元数据刷新 name / avatar / silent if (name) { conversation.name = name } if (avatar) { conversation.avatar = avatar } - if (options?.muted !== undefined) { - conversation.muted = options.muted + if (options?.silent !== undefined) { + conversation.silent = options.silent } } this.setActiveConversation(conversation) @@ -294,7 +294,7 @@ export const useConversationStore = defineStore('imConversationStore', { messages: [], deleted: false, top: false, - muted: false, + silent: false, atMe: false, atAll: false } @@ -312,13 +312,13 @@ export const useConversationStore = defineStore('imConversationStore', { this.saveConversations() }, - /** 设置会话免打扰(本地状态;后端同步由 friendStore / groupStore + /muted API 负责) */ - setMuted(type: number, targetId: number, muted: boolean) { + /** 设置会话免打扰(本地状态;后端同步由 friendStore / groupStore + /silent API 负责) */ + setSilent(type: number, targetId: number, silent: boolean) { const conversation = this.getConversation(type, targetId) if (!conversation) { return } - conversation.muted = muted + conversation.silent = silent this.saveConversations() }, @@ -718,7 +718,7 @@ export const useConversationStore = defineStore('imConversationStore', { }, /** - * 同步会话的展示元数据(name / avatar / muted) + * 同步会话的展示元数据(name / avatar / silent) * * 调用方负责把好友 / 群的信息整理成 Conversation 视角的字段: * - 私聊:name = friend.nickname;avatar = friend.avatar @@ -727,7 +727,7 @@ export const useConversationStore = defineStore('imConversationStore', { updateConversation( type: number, targetId: number, - info: { name?: string; avatar?: string; muted?: boolean } + info: { name?: string; avatar?: string; silent?: boolean } ) { const conversation = this.getConversation(type, targetId) if (!conversation) { @@ -742,8 +742,8 @@ export const useConversationStore = defineStore('imConversationStore', { conversation.avatar = info.avatar || '' changed = true } - if (info.muted !== undefined && conversation.muted !== info.muted) { - conversation.muted = info.muted + if (info.silent !== undefined && conversation.silent !== info.silent) { + conversation.silent = info.silent changed = true } if (changed) { diff --git a/src/views/im/home/store/friendStore.ts b/src/views/im/home/store/friendStore.ts index f21d240b2..b59391f09 100644 --- a/src/views/im/home/store/friendStore.ts +++ b/src/views/im/home/store/friendStore.ts @@ -51,7 +51,7 @@ export interface FriendNotificationPayload { fromAvatar?: string // FRIEND_UPDATE:单边属性变更 displayName?: string - muted?: boolean + silent?: boolean pinned?: boolean // FRIEND_DELETE:是否级联清理本端相关数据(如私聊会话) clear?: boolean @@ -164,7 +164,7 @@ export const useFriendStore = defineStore('imFriendStore', { conversationStore.updateConversation(ImConversationType.PRIVATE, friend.friendUserId, { name: getFriendDisplayName(friend), avatar: friend.avatar, - muted: friend.muted + silent: friend.silent }) } this.saveFriends() @@ -303,14 +303,14 @@ export const useFriendStore = defineStore('imFriendStore', { this.removeFriend(friendUserId, clear) }, - /** 切换免打扰:同步会话的 muted 字段,避免会话列表 muted 图标等 1210 推到才更新 */ - async setMuted(friendUserId: number, muted: boolean) { - await apiUpdateFriend({ friendUserId, muted }) + /** 切换免打扰:同步会话的 silent 字段,避免会话列表 silent 图标等 1210 推到才更新 */ + async setSilent(friendUserId: number, silent: boolean) { + await apiUpdateFriend({ friendUserId, silent }) const friend = this.getFriend(friendUserId) if (friend) { - friend.muted = muted + friend.silent = silent const conversationStore = useConversationStore() - conversationStore.updateConversation(ImConversationType.PRIVATE, friendUserId, { muted }) + conversationStore.updateConversation(ImConversationType.PRIVATE, friendUserId, { silent }) this.saveFriends() } }, @@ -381,7 +381,7 @@ export const useFriendStore = defineStore('imFriendStore', { conversationStore.updateConversation(ImConversationType.PRIVATE, friend.friendUserId, { name: merged ? getFriendDisplayName(merged) : friend.nickname, avatar: friend.avatar, - muted: friend.muted + silent: friend.silent }) this.saveFriends() }, @@ -479,8 +479,8 @@ export const useFriendStore = defineStore('imFriendStore', { if (payload.displayName != null) { friend.displayName = payload.displayName } - if (payload.muted != null) { - friend.muted = payload.muted + if (payload.silent != null) { + friend.silent = payload.silent } if (payload.pinned != null) { friend.pinned = payload.pinned @@ -488,7 +488,7 @@ export const useFriendStore = defineStore('imFriendStore', { const conversationStore = useConversationStore() conversationStore.updateConversation(ImConversationType.PRIVATE, payload.friendUserId, { name: getFriendDisplayName(friend), - muted: friend.muted + silent: friend.silent }) this.saveFriends() }, @@ -510,7 +510,7 @@ function convertFriend(vo: ImFriendRespVO): Friend { nickname: vo.nickname || String(vo.friendUserId), nicknamePinyin: vo.nicknamePinyin, avatar: vo.avatar, - muted: !!vo.muted, + silent: !!vo.silent, displayName: vo.displayName || '', displayNamePinyin: vo.displayNamePinyin, addSource: vo.addSource, diff --git a/src/views/im/home/store/groupStore.ts b/src/views/im/home/store/groupStore.ts index 79a5eeb1a..241d2f3a1 100644 --- a/src/views/im/home/store/groupStore.ts +++ b/src/views/im/home/store/groupStore.ts @@ -35,7 +35,7 @@ const pendingMemberKey = (userId: number, groupId: number) => `${userId}:${group /** * fetchGroupMember 单成员并发去重表:同 (groupId, memberUserId) 同时进的请求共用一个 Promise * - * 跟整群表分开:单成员 fetch 跟整群 fetch 语义不同(单成员不回填 me 的 muted),不能互相代替 + * 跟整群表分开:单成员 fetch 跟整群 fetch 语义不同(单成员不回填 me 的 silent),不能互相代替 */ const pendingSingleMemberFetches = new Map>() @@ -177,7 +177,7 @@ export const useGroupStore = defineStore('imGroupStore', { // 拉取当前登录用户加入的所有群(不带成员;成员按需再走 fetchGroupMembers) const list = await apiGetMyGroupList() const fresh = (list || []).map(convertGroup) - // 合并而非全量替换:muted / groupRemark / 成员缓存这些字段不在 ImGroupRespVO 里,得从旧 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) @@ -188,7 +188,7 @@ export const useGroupStore = defineStore('imGroupStore', { ...group, members: existing.members, memberCount: existing.memberCount ?? group.memberCount, - muted: existing.muted ?? group.muted, + silent: existing.silent ?? group.silent, groupRemark: existing.groupRemark, membersLoaded: existing.membersLoaded } @@ -199,7 +199,7 @@ export const useGroupStore = defineStore('imGroupStore', { conversationStore.updateConversation(ImConversationType.GROUP, group.id, { name: getGroupDisplayName(group), avatar: group.avatar, - muted: group.muted + silent: group.silent }) } this.saveGroups() @@ -237,7 +237,7 @@ export const useGroupStore = defineStore('imGroupStore', { return inflight } const promise = (async () => { - // 拉接口 + 单 pass 转换:同时捕获 me 的原始 VO,给下面回填 user-per-group 字段(muted / groupRemark)用 + // 拉接口 + 单 pass 转换:同时捕获 me 的原始 VO,给下面回填 user-per-group 字段(silent / groupRemark)用 const list = await apiGetGroupMemberList(groupId) let meRaw: ImGroupMemberRespVO | undefined const members = (list || []).map((member) => { @@ -246,7 +246,7 @@ export const useGroupStore = defineStore('imGroupStore', { } return convertGroupMember(member, groupId) }) - const muted = !!meRaw?.muted + const silent = !!meRaw?.silent const groupRemark = meRaw?.groupRemark || '' // 必须 await 之后重新 getGroup,避免 fetchGroups 已并发写入真实 group 的 race @@ -261,7 +261,7 @@ export const useGroupStore = defineStore('imGroupStore', { name: '', members, memberCount: members.length, - muted, + silent, groupRemark, membersLoaded: true }) @@ -269,15 +269,15 @@ export const useGroupStore = defineStore('imGroupStore', { group.members = members group.memberCount = members.length group.membersLoaded = true - // muted / groupRemark 任一变化才同步到 conversation 和 IDB;groupRemark 变化要顺带刷会话名 - if (group.muted !== muted || group.groupRemark !== groupRemark) { - group.muted = muted + // silent / groupRemark 任一变化才同步到 conversation 和 IDB;groupRemark 变化要顺带刷会话名 + if (group.silent !== silent || group.groupRemark !== groupRemark) { + group.silent = silent group.groupRemark = groupRemark groupFieldsChanged = true const conversationStore = useConversationStore() conversationStore.updateConversation(ImConversationType.GROUP, groupId, { name: getGroupDisplayName(group), - muted + silent }) } } @@ -299,7 +299,7 @@ export const useGroupStore = defineStore('imGroupStore', { /** * 按 (groupId, memberUserId) 单成员补齐——deriveLastSenderDisplayName 兜底场景用 * - * 跟 fetchGroupMembers 区别:只拉这一个成员,不动 me 的 muted / groupRemark(不是 me 的话拿不到); + * 跟 fetchGroupMembers 区别:只拉这一个成员,不动 me 的 silent / groupRemark(不是 me 的话拿不到); * 命中时把成员 upsert 进 group.members 数组并落 IDB,让后续渲染能用 displayUserName */ fetchGroupMember(groupId: number, memberUserId: number): Promise { @@ -366,7 +366,7 @@ export const useGroupStore = defineStore('imGroupStore', { conversationStore.updateConversation(ImConversationType.GROUP, group.id, { name: getGroupDisplayName(merged), avatar: merged.avatar, - muted: merged.muted + silent: merged.silent }) // 持久化到 IDB(fire-and-forget) this.saveGroups() @@ -390,13 +390,13 @@ export const useGroupStore = defineStore('imGroupStore', { }, /** 切换免打扰:推后端 + 落本地 */ - async setMuted(id: number, muted: boolean) { - await apiUpdateGroupMember({ groupId: id, muted }) + async setSilent(id: number, silent: boolean) { + await apiUpdateGroupMember({ groupId: id, silent }) const group = this.getGroup(id) if (!group) { return } - group.muted = muted + group.silent = silent this.saveGroups() }, @@ -478,7 +478,7 @@ export const useGroupStore = defineStore('imGroupStore', { conversationStore.updateConversation(ImConversationType.GROUP, groupId, { name: getGroupDisplayName(group), avatar: group.avatar, - muted: group.muted + silent: group.silent }) this.saveGroups() }, diff --git a/src/views/im/home/store/websocketStore.ts b/src/views/im/home/store/websocketStore.ts index cb1b22dd4..42f050c9d 100644 --- a/src/views/im/home/store/websocketStore.ts +++ b/src/views/im/home/store/websocketStore.ts @@ -333,7 +333,7 @@ export const useImWebSocketStore = defineStore('imWebSocketStore', { apiReadPrivateMessages(peerId, websocketMessage.id).catch((e) => { console.warn('[IM WS] 自动已读上报失败', e) }) - } else if (!conversation?.muted && isNormalMessage(websocketMessage.type)) { + } else if (!conversation?.silent && isNormalMessage(websocketMessage.type)) { // 非当前会话且未免打扰:响一下提示音(带节流,详见 playAudioTip);TIP_TEXT 等系统提示不响 playAudioTip() } @@ -439,7 +439,7 @@ export const useImWebSocketStore = defineStore('imWebSocketStore', { apiReadGroupMessages(websocketMessage.groupId, websocketMessage.id).catch((e) => { console.warn('[IM WS] 自动已读上报失败', e) }) - } else if (!conversation?.muted && isNormalMessage(websocketMessage.type)) { + } else if (!conversation?.silent && isNormalMessage(websocketMessage.type)) { // GROUP_* 群广播事件 / TIP_TEXT 等系统提示不响提示音 playAudioTip() } @@ -521,13 +521,13 @@ export const useImWebSocketStore = defineStore('imWebSocketStore', { // ==================== 群关系事件(承载于群聊通道,按 inner type 分流) ==================== /** - * GROUP_MEMBER_SETTING_UPDATE:多端同步成员个人设置变更(muted / groupRemark) + * GROUP_MEMBER_SETTING_UPDATE:多端同步成员个人设置变更(silent / groupRemark) * * payload 携带变更字段,按非 null 字段直接局部更新;省一次 fetchGroupMembers 接口 */ handleGroupMemberSettingUpdate(websocketMessage: ImGroupMessageDTO) { // content 解析失败由外层 dispatchGroupFrame 的 try-catch 兜底(含 websocketMessage 打印),不重复 catch - const payload: { muted?: boolean; groupRemark?: string } = JSON.parse( + const payload: { silent?: boolean; groupRemark?: string } = JSON.parse( websocketMessage.content || '{}' ) const groupStore = useGroupStore() @@ -536,8 +536,8 @@ export const useImWebSocketStore = defineStore('imWebSocketStore', { return } const fields: Partial = {} - if (payload.muted != null) { - fields.muted = payload.muted + if (payload.silent != null) { + fields.silent = payload.silent } if (payload.groupRemark != null) { fields.groupRemark = payload.groupRemark diff --git a/src/views/im/home/types/index.ts b/src/views/im/home/types/index.ts index a3998f2c7..64f42f4b6 100644 --- a/src/views/im/home/types/index.ts +++ b/src/views/im/home/types/index.ts @@ -59,7 +59,7 @@ export interface Conversation { // ========== UI 状态 ========== deleted?: boolean // 是否已删除(软删标记,持久化时过滤) top?: boolean // 是否置顶(排序时优先) - muted?: boolean // 是否免打扰(不展示未读徽标 + 不响提示音) + silent?: boolean // 是否免打扰(不展示未读徽标 + 不响提示音) atMe?: boolean // 群聊:是否有人 @我 atAll?: boolean // 群聊:是否有人 @全体成员 } @@ -115,7 +115,7 @@ export interface Group { pinnedMessages?: Message[] // 群置顶消息列表 // ========== 前端扩展字段(user-per-group 维度) ========== - muted?: boolean // 是否免打扰。从当前用户的 GroupMember 回填 + silent?: boolean // 是否免打扰。从当前用户的 GroupMember 回填 groupRemark?: string // 群备注。从当前用户的 GroupMember 回填(当前用户对该群的自定义名) members?: GroupMember[] // 群成员缓存(按需懒加载) membersLoaded?: boolean // members 是否"完整加载"——只有整群 loadGroupMembers / fetchGroupMembers 命中时为 true;fetchGroupMember 单成员补齐不置位,避免 fetchGroupMembers(force=false) 命中缓存时误判整群已加载 @@ -148,7 +148,7 @@ export interface Friend { nickname: string // 好友昵称(对方真实昵称,永远不被备注覆盖;UI 显示走 displayName || nickname) nicknamePinyin?: string // 昵称的拼音(后端用 Pinyin4j 算好回填,小写无空格) avatar?: string // 好友头像 - muted?: boolean // 是否免打扰(不展示未读徽标 + 不响提示音) + silent?: boolean // 是否免打扰(不展示未读徽标 + 不响提示音) displayName?: string // 好友展示备注:仅自己可见的别名(单字段不歧义,不带 Friend 前缀) displayNamePinyin?: string // 备注的拼音(后端用 Pinyin4j 算好回填,小写无空格) status?: number // 好友状态,对齐 CommonStatusEnum(DISABLE = 已删除,软删保留记录) diff --git a/src/views/im/manager/friend/index.vue b/src/views/im/manager/friend/index.vue index 1a6b64ad5..e6b626b95 100644 --- a/src/views/im/manager/friend/index.vue +++ b/src/views/im/manager/friend/index.vue @@ -29,9 +29,9 @@ /> - + - + @@ -131,7 +131,7 @@ const queryParams = reactive({ userId: undefined as number | undefined, friendUserId: undefined as number | undefined, status: undefined as number | undefined, - muted: undefined as boolean | undefined, + silent: undefined as boolean | undefined, addTime: [] as string[] }) const queryFormRef = ref() // 搜索的表单 diff --git a/src/views/im/manager/group/GroupDetail.vue b/src/views/im/manager/group/GroupDetail.vue index 09f82a4d9..2678bd48f 100644 --- a/src/views/im/manager/group/GroupDetail.vue +++ b/src/views/im/manager/group/GroupDetail.vue @@ -62,9 +62,9 @@ > - + diff --git a/src/views/im/utils/constants.ts b/src/views/im/utils/constants.ts index ba589ee11..83c2b788f 100644 --- a/src/views/im/utils/constants.ts +++ b/src/views/im/utils/constants.ts @@ -19,7 +19,7 @@ export const ImMessageType = { FRIEND_BLOCK: 1207, // 加入黑名单 FRIEND_UNBLOCK: 1208, // 移出黑名单 FRIEND_INFO_UPDATED: 1209, // 好友资料变更(昵称 / 头像) - FRIEND_UPDATE: 1210, // 好友信息批量更新(muted / pinned) + FRIEND_UPDATE: 1210, // 好友信息批量更新(silent / pinned) // ========== 群事件(1501-1520 直接复用 OpenIM 段位编号;1530+ 自有扩展段) ========== GROUP_CREATE: 1501, // 群创建 GROUP_INFO_UPDATE: 1502, // 群信息变更(NAME / NOTICE 之外字段兜底) @@ -42,7 +42,7 @@ export const ImMessageType = { GROUP_NOTICE_UPDATE: 1519, // 群公告变更 GROUP_NAME_UPDATE: 1520, // 群名变更 // ========== 自有扩展段(1530+,OpenIM 1500-1520 段位无对应物) ========== - GROUP_MEMBER_SETTING_UPDATE: 1530, // 群成员个人设置变更:muted / groupRemark 个人多端同步 + GROUP_MEMBER_SETTING_UPDATE: 1530, // 群成员个人设置变更:silent / groupRemark 个人多端同步 GROUP_MESSAGE_PIN: 1531, // 群消息置顶(自有扩展,OpenIM 无) GROUP_MESSAGE_UNPIN: 1532 // 群消息取消置顶(自有扩展,OpenIM 无) } as const diff --git a/src/views/im/utils/storage.ts b/src/views/im/utils/storage.ts index bc962c4c1..83f7f4d77 100644 --- a/src/views/im/utils/storage.ts +++ b/src/views/im/utils/storage.ts @@ -31,7 +31,7 @@ export const StorageKeys = { /** * 会话索引:游标 + 会话元数据(不含 messages),对应 ConversationStoreMeta * - * 任何会话级元数据变更(top / muted / unread / 列表增删 / 排序)都会重写这一个 key;由于 messages 已剥离到独立 key,单次写体积稳定(仅元数据,量级 KB 级) + * 任何会话级元数据变更(top / silent / unread / 列表增删 / 排序)都会重写这一个 key;由于 messages 已剥离到独立 key,单次写体积稳定(仅元数据,量级 KB 级) */ conversationMeta: (userId: number | string) => `conversation:meta:${userId}`, /**