feat(im): 群聊免打扰接入后端,完善免打扰失败回滚 + ContextMenu 微调

- groupStore.setMuted 改 async,调 /im/group-member/update 推后端
- GroupMember.muted 在类型层补齐;convertGroupMember 保留 muted;
  loadGroupMembers 拉完成员后用当前用户那条 member.muted 回填 group.muted
  与 conversation.muted,避免冷启动后服务端已免打扰的群在会话列表里仍显示为
  未免打扰
- ConversationItem.handleMuted 失败回滚:catch 后 ElMessage.error 并反向
  setMuted 把 conversationStore(已 saveConversations 落盘)拽回正确状态
- ContextMenu 分割线改用 h-[1px] + bg(UnoCSS 不带 border-style preflight,
  border-t 在空内容 div 上不显形),文案 text-center → text-left 贴近微信
- groupStore.setMuted 改 async 后,ConversationItem 里两路 setMuted 调用
  都用 void 显式 fire-and-forget,风格统一
im
YunaiV 2026-04-27 09:29:49 +08:00
parent 45a530e8c7
commit a0ed0d800c
2 changed files with 16 additions and 2 deletions

View File

@ -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))
// 后端只在成员维度返回当前用户的 mutedapiGetMyGroupList 不带),借这次拉成员一起回填
// 否则冷启动 / 清缓存后,服务端已免打扰的群在会话列表里仍显示为未免打扰
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
}
}

View File

@ -125,6 +125,7 @@ export interface GroupMember {
displayUserName?: string // 组内显示名(不与 nickname 合并,由消费方按需取舍)
displayGroupName?: string // 群显示备注(当前用户对该群的自定义名)
status?: number // 在群 / 退群状态,对齐 CommonStatusEnum
muted?: boolean // 当前成员对该群的免打扰开关loadGroupMembers 用它回填 Group.muted
// ========== 前端扩展字段 ==========
isOwner?: boolean // 是否群主(前端从 Group.ownerUserId 计算)