From a0ed0d800c1fc9af171125763bfab0dc10ffff76 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Mon, 27 Apr 2026 09:29:49 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(im):=20=E7=BE=A4=E8=81=8A?= =?UTF-8?q?=E5=85=8D=E6=89=93=E6=89=B0=E6=8E=A5=E5=85=A5=E5=90=8E=E7=AB=AF?= =?UTF-8?q?=EF=BC=8C=E5=AE=8C=E5=96=84=E5=85=8D=E6=89=93=E6=89=B0=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=E5=9B=9E=E6=BB=9A=20+=20ContextMenu=20=E5=BE=AE?= =?UTF-8?q?=E8=B0=83=20-=20groupStore.setMuted=20=E6=94=B9=20async?= =?UTF-8?q?=EF=BC=8C=E8=B0=83=20/im/group-member/update=20=E6=8E=A8?= =?UTF-8?q?=E5=90=8E=E7=AB=AF=20-=20GroupMember.muted=20=E5=9C=A8=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E5=B1=82=E8=A1=A5=E9=BD=90=EF=BC=9BconvertGroupMember?= =?UTF-8?q?=20=E4=BF=9D=E7=95=99=20muted=EF=BC=9B=20=20=20loadGroupMembers?= =?UTF-8?q?=20=E6=8B=89=E5=AE=8C=E6=88=90=E5=91=98=E5=90=8E=E7=94=A8?= =?UTF-8?q?=E5=BD=93=E5=89=8D=E7=94=A8=E6=88=B7=E9=82=A3=E6=9D=A1=20member?= =?UTF-8?q?.muted=20=E5=9B=9E=E5=A1=AB=20group.muted=20=20=20=E4=B8=8E=20c?= =?UTF-8?q?onversation.muted=EF=BC=8C=E9=81=BF=E5=85=8D=E5=86=B7=E5=90=AF?= =?UTF-8?q?=E5=8A=A8=E5=90=8E=E6=9C=8D=E5=8A=A1=E7=AB=AF=E5=B7=B2=E5=85=8D?= =?UTF-8?q?=E6=89=93=E6=89=B0=E7=9A=84=E7=BE=A4=E5=9C=A8=E4=BC=9A=E8=AF=9D?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E9=87=8C=E4=BB=8D=E6=98=BE=E7=A4=BA=E4=B8=BA?= =?UTF-8?q?=20=20=20=E6=9C=AA=E5=85=8D=E6=89=93=E6=89=B0=20-=20Conversatio?= =?UTF-8?q?nItem.handleMuted=20=E5=A4=B1=E8=B4=A5=E5=9B=9E=E6=BB=9A?= =?UTF-8?q?=EF=BC=9Acatch=20=E5=90=8E=20ElMessage.error=20=E5=B9=B6?= =?UTF-8?q?=E5=8F=8D=E5=90=91=20=20=20setMuted=20=E6=8A=8A=20conversationS?= =?UTF-8?q?tore=EF=BC=88=E5=B7=B2=20saveConversations=20=E8=90=BD=E7=9B=98?= =?UTF-8?q?=EF=BC=89=E6=8B=BD=E5=9B=9E=E6=AD=A3=E7=A1=AE=E7=8A=B6=E6=80=81?= =?UTF-8?q?=20-=20ContextMenu=20=E5=88=86=E5=89=B2=E7=BA=BF=E6=94=B9?= =?UTF-8?q?=E7=94=A8=20h-[1px]=20+=20bg=EF=BC=88UnoCSS=20=E4=B8=8D?= =?UTF-8?q?=E5=B8=A6=20border-style=20preflight=EF=BC=8C=20=20=20border-t?= =?UTF-8?q?=20=E5=9C=A8=E7=A9=BA=E5=86=85=E5=AE=B9=20div=20=E4=B8=8A?= =?UTF-8?q?=E4=B8=8D=E6=98=BE=E5=BD=A2=EF=BC=89=EF=BC=8C=E6=96=87=E6=A1=88?= =?UTF-8?q?=20text-center=20=E2=86=92=20text-left=20=E8=B4=B4=E8=BF=91?= =?UTF-8?q?=E5=BE=AE=E4=BF=A1=20-=20groupStore.setMuted=20=E6=94=B9=20asyn?= =?UTF-8?q?c=20=E5=90=8E=EF=BC=8CConversationItem=20=E9=87=8C=E4=B8=A4?= =?UTF-8?q?=E8=B7=AF=20setMuted=20=E8=B0=83=E7=94=A8=20=20=20=E9=83=BD?= =?UTF-8?q?=E7=94=A8=20void=20=E6=98=BE=E5=BC=8F=20fire-and-forget?= =?UTF-8?q?=EF=BC=8C=E9=A3=8E=E6=A0=BC=E7=BB=9F=E4=B8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/im/home/store/groupStore.ts | 17 +++++++++++++++-- src/views/im/home/types/index.ts | 1 + 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/views/im/home/store/groupStore.ts b/src/views/im/home/store/groupStore.ts index a497ccbc7..dabd623c4 100644 --- a/src/views/im/home/store/groupStore.ts +++ b/src/views/im/home/store/groupStore.ts @@ -11,6 +11,7 @@ import { updateGroupMember as apiUpdateGroupMember, type ImGroupMemberRespVO } from '@/api/im/group/member' +import { useUserStore } from '@/store/modules/user' import { useConversationStore } from './conversationStore' import { ImConversationType } from '../../utils/constants' import type { Group, GroupMember } from '../types' @@ -81,17 +82,28 @@ export const useGroupStore = defineStore('imGroupStore', { // 拉取该群所有成员(聚合自 AdminUser,含 nickname / avatar / displayUserName) const list = await apiGetGroupMemberList(groupId) const members = (list || []).map((member) => convertGroupMember(member, groupId)) + // 后端只在成员维度返回当前用户的 muted(apiGetMyGroupList 不带),借这次拉成员一起回填 + // 否则冷启动 / 清缓存后,服务端已免打扰的群在会话列表里仍显示为未免打扰 + const userStore = useUserStore() + const currentUserId = Number(userStore.getUser?.id) || 0 + const me = members.find((m) => m.userId === currentUserId) + const muted = !!me?.muted // 成员列表可能在群列表之前触发,此时需要占位一个 group if (!group) { this.upsertGroup({ id: groupId, name: String(groupId), members, - memberCount: members.length + memberCount: members.length, + muted }) } else { group.members = members group.memberCount = members.length + group.muted = muted + // 已有 group 分支没走 upsertGroup,单独把 muted 推回 conversation 保证会话列表展示一致 + const conversationStore = useConversationStore() + conversationStore.updateConversation(ImConversationType.GROUP, groupId, { muted }) } return members }, @@ -158,7 +170,8 @@ function convertGroupMember(member: ImGroupMemberRespVO, groupId: number): Group avatar: member.avatar, displayUserName: member.displayUserName, displayGroupName: member.displayGroupName, - status: member.status + status: member.status, + muted: member.muted } } diff --git a/src/views/im/home/types/index.ts b/src/views/im/home/types/index.ts index d8fb63dc3..661140389 100644 --- a/src/views/im/home/types/index.ts +++ b/src/views/im/home/types/index.ts @@ -125,6 +125,7 @@ export interface GroupMember { displayUserName?: string // 组内显示名(不与 nickname 合并,由消费方按需取舍) displayGroupName?: string // 群显示备注(当前用户对该群的自定义名) status?: number // 在群 / 退群状态,对齐 CommonStatusEnum + muted?: boolean // 当前成员对该群的免打扰开关(loadGroupMembers 用它回填 Group.muted) // ========== 前端扩展字段 ========== isOwner?: boolean // 是否群主(前端从 Group.ownerUserId 计算)