feat(im):将"免打扰"字段从 muted 全量重命名为 silent(DO/VO/Service/Mapper/测试/SQL + 前端 types/store/组件/管理后台),为后续 mute 禁言功能腾出词族

im
YunaiV 2026-05-05 13:51:53 +08:00
parent dd75c702db
commit 4d006f8e73
20 changed files with 99 additions and 99 deletions

View File

@ -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 // 是否置顶联系人
}

View File

@ -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 // 是否免打扰
}
// 邀请用户加入群

View File

@ -7,7 +7,7 @@ export interface ImManagerFriendVO {
friendUserId: number
friendNickname?: string
displayName?: string
muted: boolean
silent: boolean
status: number
addTime?: Date
deleteTime?: Date

View File

@ -22,7 +22,7 @@ export interface ImManagerGroupMemberVO {
avatar?: string
displayUserName?: string
groupRemark?: string
muted?: boolean
silent?: boolean
status: number
role?: number // 成员角色,参见 ImGroupMemberRole 枚举
joinTime?: Date

View File

@ -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') {

View File

@ -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 触发,最后一次输入可能还压在队列里 */

View File

@ -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' })
}

View File

@ -261,7 +261,7 @@
<div class="im-conversation-group-side__section">
<div class="im-conversation-group-side__row">
<span class="im-conversation-group-side__label">消息免打扰</span>
<el-switch :model-value="!!conversation?.muted" @change="onMutedChange" />
<el-switch :model-value="!!conversation?.silent" @change="onMutedChange" />
</div>
<div class="im-conversation-group-side__row">
<span class="im-conversation-group-side__label">置顶聊天</span>
@ -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('操作失败')
})
}

View File

@ -14,7 +14,7 @@
:clickable="false"
/>
<span
v-show="!conversation.muted && conversation.unreadCount > 0"
v-show="!conversation.silent && conversation.unreadCount > 0"
class="absolute -top-1.5 -right-1.5 min-w-[18px] h-[18px] px-1.5 text-11px leading-[18px] text-white text-center bg-[#f56c6c] border border-white dark:border-[var(--el-bg-color)] rounded-full box-border whitespace-nowrap"
>
{{ conversation.unreadCount > 99 ? '99+' : conversation.unreadCount }}
@ -58,10 +58,10 @@
</span>
<!-- 免打扰图标 -->
<Icon
v-if="conversation.muted"
v-if="conversation.silent"
icon="mdi:bell-off-outline"
:size="14"
class="conversation-item__muted flex-shrink-0 ml-1 text-[var(--el-text-color-disabled)]"
class="conversation-item__silent flex-shrink-0 ml-1 text-[var(--el-text-color-disabled)]"
title="消息免打扰"
/>
</div>
@ -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;
}
</style>

View File

@ -108,7 +108,7 @@
<div class="im-conversation-private-side__section">
<div class="im-conversation-private-side__row">
<span class="im-conversation-private-side__label">消息免打扰</span>
<el-switch :model-value="!!conversation?.muted" @change="handleMutedChange" />
<el-switch :model-value="!!conversation?.silent" @change="handleMutedChange" />
</div>
<div class="im-conversation-private-side__row">
<span class="im-conversation-private-side__label">置顶聊天</span>
@ -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
}

View File

@ -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 }
)
}
</script>

View File

@ -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', {
/**
* IndexedDBfire-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.nicknameavatar = 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) {

View File

@ -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,

View File

@ -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<string, Promise<GroupMember | null>>()
@ -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 和 IDBgroupRemark 变化要顺带刷会话名
if (group.muted !== muted || group.groupRemark !== groupRemark) {
group.muted = muted
// silent / groupRemark 任一变化才同步到 conversation 和 IDBgroupRemark 变化要顺带刷会话名
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<GroupMember | null> {
@ -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
})
// 持久化到 IDBfire-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()
},

View File

@ -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)) {
// 非当前会话且未免打扰:响一下提示音(带节流,详见 playAudioTipTIP_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_UPDATEmuted / groupRemark
* GROUP_MEMBER_SETTING_UPDATEsilent / 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<Group> = {}
if (payload.muted != null) {
fields.muted = payload.muted
if (payload.silent != null) {
fields.silent = payload.silent
}
if (payload.groupRemark != null) {
fields.groupRemark = payload.groupRemark

View File

@ -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 命中时为 truefetchGroupMember 单成员补齐不置位,避免 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 // 好友状态,对齐 CommonStatusEnumDISABLE = 已删除,软删保留记录)

View File

@ -29,9 +29,9 @@
/>
</el-select>
</el-form-item>
<el-form-item label="免打扰" prop="muted">
<el-form-item label="免打扰" prop="silent">
<el-select
v-model="queryParams.muted"
v-model="queryParams.silent"
placeholder="请选择免打扰状态"
clearable
class="!w-160px"
@ -79,9 +79,9 @@
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="displayName" width="120" />
<el-table-column label="免打扰" align="center" prop="muted" width="80">
<el-table-column label="免打扰" align="center" prop="silent" width="80">
<template #default="{ row }">
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.muted" />
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.silent" />
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status" width="100">
@ -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() //

View File

@ -62,9 +62,9 @@
>
<template #default="{ row }">{{ row.groupRemark || '-' }}</template>
</el-table-column>
<el-table-column label="免打扰" prop="muted" width="80" align="center">
<el-table-column label="免打扰" prop="silent" width="80" align="center">
<template #default="{ row }">
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.muted" />
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="row.silent" />
</template>
</el-table-column>
<el-table-column label="状态" prop="status" width="100" align="center">

View File

@ -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

View File

@ -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}`,
/**