fix(im): 对齐群备注展示并修复 IM 消息管理字典
聊天端: - 群 API 类型补充 groupRemark 和 silent - 群列表同步时以接口返回的个人群设置为准,只保留成员缓存 - 会话名写入入口统一使用 getGroupDisplayName,避免群备注被原群名覆盖 - 聊天标题、转发、推荐名片、新建群入口同步群展示名逻辑 - 空群头像且成员未加载时异步预拉群成员,用于合成群头像 - 通讯录和合并消息详情补充滚动容器 - 消息历史日期选择改用 antd Calendar 卡片模式并修正样式 管理端: - IM 字典常量统一为 im_content_type、im_message_status、im_message_receipt_status - 私聊 / 群聊消息列表和详情页切换到统一内容类型、消息状态、回执状态字典 - 私聊消息 API 和详情页补充 receiptStatus - 统计消息类型分布改用内容类型字典pull/367/head
parent
e61d0a5aa2
commit
5a4f8b4e2a
|
|
@ -19,6 +19,8 @@ export namespace ImGroupApi {
|
||||||
createTime?: string; // 创建时间
|
createTime?: string; // 创建时间
|
||||||
pinnedMessages?: ImGroupMessageApi.GroupMessageRespVO[]; // 群置顶消息列表(后端关联回填,仅当登录用户是群成员时非空)
|
pinnedMessages?: ImGroupMessageApi.GroupMessageRespVO[]; // 群置顶消息列表(后端关联回填,仅当登录用户是群成员时非空)
|
||||||
joinStatus?: number; // 当前登录用户在该群的成员状态(参见 CommonStatusEnum:0 在群 / 1 已退群);历史退群群仍返回,供展示离线消息的群名 / 头像
|
joinStatus?: number; // 当前登录用户在该群的成员状态(参见 CommonStatusEnum:0 在群 / 1 已退群);历史退群群仍返回,供展示离线消息的群名 / 头像
|
||||||
|
groupRemark?: string; // 当前登录用户对该群的备注
|
||||||
|
silent?: boolean; // 当前登录用户是否免打扰
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 群消息置顶 / 取消置顶 Request VO */
|
/** 群消息置顶 / 取消置顶 Request VO */
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ export namespace ImManagerPrivateMessageApi {
|
||||||
type: number;
|
type: number;
|
||||||
content: string;
|
content: string;
|
||||||
status: number;
|
status: number;
|
||||||
|
receiptStatus?: number;
|
||||||
sendTime: Date;
|
sendTime: Date;
|
||||||
createTime: Date;
|
createTime: Date;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import { ImContentType, ImConversationType, isGroupConversation } from '../../..
|
||||||
import { getConversationKey } from '../../../utils/conversation'
|
import { getConversationKey } from '../../../utils/conversation'
|
||||||
import { buildDefaultGroupName } from '../../../utils/group'
|
import { buildDefaultGroupName } from '../../../utils/group'
|
||||||
import { type CardTarget, serializeMessage } from '../../../utils/message'
|
import { type CardTarget, serializeMessage } from '../../../utils/message'
|
||||||
import { isGroupQuit } from '../../../utils/user'
|
import { getGroupDisplayName, isGroupQuit } from '../../../utils/user'
|
||||||
import { useMessageSender } from '../../composables/useMessageSender'
|
import { useMessageSender } from '../../composables/useMessageSender'
|
||||||
import { FacePicker } from '../../pages/conversation/components/input'
|
import { FacePicker } from '../../pages/conversation/components/input'
|
||||||
import { useConversationStore } from '../../store/conversationStore'
|
import { useConversationStore } from '../../store/conversationStore'
|
||||||
|
|
@ -181,7 +181,7 @@ async function handleCreateGroupAndSend() {
|
||||||
const newConversation: Conversation = {
|
const newConversation: Conversation = {
|
||||||
type: ImConversationType.GROUP,
|
type: ImConversationType.GROUP,
|
||||||
targetId: group.id,
|
targetId: group.id,
|
||||||
name: group.name || name,
|
name: getGroupDisplayName(group) || name,
|
||||||
avatar: group.avatar || '',
|
avatar: group.avatar || '',
|
||||||
unreadCount: 0,
|
unreadCount: 0,
|
||||||
lastContent: '',
|
lastContent: '',
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ import {
|
||||||
} from '../../utils/constants'
|
} from '../../utils/constants'
|
||||||
import { generateClientMessageId, getPrivateMessagePeerId } from '../../utils/message'
|
import { generateClientMessageId, getPrivateMessagePeerId } from '../../utils/message'
|
||||||
import { runMinIdPull } from '../../utils/pull'
|
import { runMinIdPull } from '../../utils/pull'
|
||||||
import { getFriendDisplayName } from '../../utils/user'
|
import { getFriendDisplayName, getGroupDisplayName } from '../../utils/user'
|
||||||
import { useConversationStore } from '../store/conversationStore'
|
import { useConversationStore } from '../store/conversationStore'
|
||||||
import { useFriendStore } from '../store/friendStore'
|
import { useFriendStore } from '../store/friendStore'
|
||||||
import { useGroupRequestStore } from '../store/groupRequestStore'
|
import { useGroupRequestStore } from '../store/groupRequestStore'
|
||||||
|
|
@ -146,7 +146,7 @@ export const useMessagePuller = () => {
|
||||||
return {
|
return {
|
||||||
type: ImConversationType.GROUP,
|
type: ImConversationType.GROUP,
|
||||||
targetId: message.groupId,
|
targetId: message.groupId,
|
||||||
name: group?.name || String(message.groupId),
|
name: group ? getGroupDisplayName(group) : String(message.groupId),
|
||||||
avatar: group?.avatar || '',
|
avatar: group?.avatar || '',
|
||||||
silent: group?.silent
|
silent: group?.silent
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -241,7 +241,8 @@ function onRemarkSaved(displayName: string) {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 列表主体:拆 FriendRequestList / GroupList / FriendList 三个子组件,各自管理折叠 + 过滤;本页只透传选中态 -->
|
<!-- 列表主体:拆 FriendRequestList / GroupList / FriendList 三个子组件,各自管理折叠 + 过滤;本页只透传选中态 -->
|
||||||
<div class="flex-1">
|
<!-- overflow-y-auto:联系人多时本列表可滚动(父 ResizableAside 不提供滚动),对齐 Vue3 的 el-scrollbar -->
|
||||||
|
<div class="flex-1 overflow-y-auto">
|
||||||
<FriendRequestList
|
<FriendRequestList
|
||||||
:requests="friendRequests"
|
:requests="friendRequests"
|
||||||
:active-id="selection?.type === 'request' ? selection.request.id : undefined"
|
:active-id="selection?.type === 'request' ? selection.request.id : undefined"
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import { useConversationStore } from '#/views/im/home/store/conversationStore'
|
||||||
import { useFriendStore } from '#/views/im/home/store/friendStore'
|
import { useFriendStore } from '#/views/im/home/store/friendStore'
|
||||||
import { useGroupStore } from '#/views/im/home/store/groupStore'
|
import { useGroupStore } from '#/views/im/home/store/groupStore'
|
||||||
import { ImConversationType } from '#/views/im/utils/constants'
|
import { ImConversationType } from '#/views/im/utils/constants'
|
||||||
import { getFriendDisplayName } from '#/views/im/utils/user'
|
import { getFriendDisplayName, getGroupDisplayName } from '#/views/im/utils/user'
|
||||||
|
|
||||||
import { GroupCreateDialog } from '../../../../components/group'
|
import { GroupCreateDialog } from '../../../../components/group'
|
||||||
import { UserAvatar } from '../../../../components/user'
|
import { UserAvatar } from '../../../../components/user'
|
||||||
|
|
@ -119,7 +119,7 @@ function handleGroupCreated(groupId: number) {
|
||||||
conversationStore.openConversation(
|
conversationStore.openConversation(
|
||||||
groupId,
|
groupId,
|
||||||
ImConversationType.GROUP,
|
ImConversationType.GROUP,
|
||||||
group.name,
|
getGroupDisplayName(group),
|
||||||
group.avatar || '',
|
group.avatar || '',
|
||||||
{ silent: !!group.silent }
|
{ silent: !!group.silent }
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ import {
|
||||||
removeQuotePayload,
|
removeQuotePayload,
|
||||||
serializeMessage
|
serializeMessage
|
||||||
} from '#/views/im/utils/message'
|
} from '#/views/im/utils/message'
|
||||||
import { isGroupQuit } from '#/views/im/utils/user'
|
import { getGroupDisplayName, isGroupQuit } from '#/views/im/utils/user'
|
||||||
|
|
||||||
import { FacePicker } from '../../input'
|
import { FacePicker } from '../../input'
|
||||||
|
|
||||||
|
|
@ -263,7 +263,7 @@ async function handleCreateGroupAndSend() {
|
||||||
const newConversation: Conversation = {
|
const newConversation: Conversation = {
|
||||||
type: ImConversationType.GROUP,
|
type: ImConversationType.GROUP,
|
||||||
targetId: group.id,
|
targetId: group.id,
|
||||||
name: group.name || name,
|
name: getGroupDisplayName(group) || name,
|
||||||
avatar: group.avatar || '',
|
avatar: group.avatar || '',
|
||||||
unreadCount: 0,
|
unreadCount: 0,
|
||||||
lastContent: '',
|
lastContent: '',
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,8 @@ function handleClose() {
|
||||||
>
|
>
|
||||||
以下是 {{ currentPayload.messages.length }} 条消息
|
以下是 {{ currentPayload.messages.length }} 条消息
|
||||||
</div>
|
</div>
|
||||||
<div class="flex-1">
|
<!-- overflow-y-auto:合并消息多时在定高弹窗内可滚动,对齐 Vue3 的 el-scrollbar -->
|
||||||
|
<div class="flex-1 overflow-y-auto">
|
||||||
<div
|
<div
|
||||||
v-for="(item, idx) in currentPayload.messages"
|
v-for="(item, idx) in currentPayload.messages"
|
||||||
:key="idx"
|
:key="idx"
|
||||||
|
|
|
||||||
|
|
@ -582,7 +582,11 @@ function locateMessage(messageId: number) {
|
||||||
<div class="px-2 pt-1 pb-2 text-13px font-medium text-[var(--ant-color-text)]">
|
<div class="px-2 pt-1 pb-2 text-13px font-medium text-[var(--ant-color-text)]">
|
||||||
选择发送日期
|
选择发送日期
|
||||||
</div>
|
</div>
|
||||||
<Calendar v-model:value="datePickerValue" class="im-message-history__calendar" />
|
<Calendar
|
||||||
|
v-model:value="datePickerValue"
|
||||||
|
:fullscreen="false"
|
||||||
|
class="im-message-history__calendar"
|
||||||
|
/>
|
||||||
<div class="flex gap-2 justify-end px-2 pt-2">
|
<div class="flex gap-2 justify-end px-2 pt-2">
|
||||||
<Button size="small" @click="datePopoverVisible = false">取消</Button>
|
<Button size="small" @click="datePopoverVisible = false">取消</Button>
|
||||||
<Button size="small" type="primary" @click="onDateConfirm">确定</Button>
|
<Button size="small" type="primary" @click="onDateConfirm">确定</Button>
|
||||||
|
|
@ -785,18 +789,19 @@ function locateMessage(messageId: number) {
|
||||||
border-radius: 1px;
|
border-radius: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* :deep 穿透 el-calendar 子组件 DOM;默认偏大压一压让它能塞进 320 popover */
|
/* :deep 穿透 antd Calendar(fullscreen=false 卡片模式)子 DOM,压一压塞进 320 popover */
|
||||||
.im-message-history__calendar :deep(.el-calendar) {
|
.im-message-history__calendar :deep(.ant-picker-calendar-header) {
|
||||||
40px: 36px;
|
|
||||||
}
|
|
||||||
.im-message-history__calendar :deep(.el-calendar__header) {
|
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
}
|
}
|
||||||
.im-message-history__calendar :deep(.el-calendar-table) {
|
.im-message-history__calendar :deep(.ant-picker-content) {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
.im-message-history__calendar :deep(.el-calendar-day) {
|
.im-message-history__calendar :deep(.ant-picker-cell) {
|
||||||
height: 36px;
|
padding: 1px 0;
|
||||||
padding: 4px;
|
}
|
||||||
|
.im-message-history__calendar :deep(.ant-picker-cell .ant-picker-calendar-date) {
|
||||||
|
height: 28px;
|
||||||
|
margin: 0 2px;
|
||||||
|
padding: 2px 4px 0;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import { getCurrentUserId } from '#/views/im/utils/auth'
|
||||||
import { ImConversationType, ImRtcCallMediaType, ImRtcCallStatus } from '#/views/im/utils/constants'
|
import { ImConversationType, ImRtcCallMediaType, ImRtcCallStatus } from '#/views/im/utils/constants'
|
||||||
import { getClientConversationId } from '#/views/im/utils/db'
|
import { getClientConversationId } from '#/views/im/utils/db'
|
||||||
import { resolveCallEndReasonText } from '#/views/im/utils/message'
|
import { resolveCallEndReasonText } from '#/views/im/utils/message'
|
||||||
import { getMemberDisplayName, isGroupQuit } from '#/views/im/utils/user'
|
import { getGroupDisplayName, getMemberDisplayName, isGroupQuit } from '#/views/im/utils/user'
|
||||||
|
|
||||||
import { GroupMuteMemberDialog } from '../../../../components/group'
|
import { GroupMuteMemberDialog } from '../../../../components/group'
|
||||||
import {
|
import {
|
||||||
|
|
@ -185,10 +185,11 @@ const groupInfo = computed<
|
||||||
}
|
}
|
||||||
const group = groupStore.getGroup(conversation.targetId)
|
const group = groupStore.getGroup(conversation.targetId)
|
||||||
const selfMember = group?.members?.find((member) => member.userId === getCurrentUserId())
|
const selfMember = group?.members?.find((member) => member.userId === getCurrentUserId())
|
||||||
|
const showGroupName = group ? getGroupDisplayName(group) : conversation.name
|
||||||
return {
|
return {
|
||||||
id: conversation.targetId,
|
id: conversation.targetId,
|
||||||
name: group?.name || conversation.name,
|
name: group?.name || conversation.name,
|
||||||
showGroupName: group?.name || conversation.name,
|
showGroupName,
|
||||||
showImage: group?.avatar || conversation.avatar,
|
showImage: group?.avatar || conversation.avatar,
|
||||||
notice: group?.notice,
|
notice: group?.notice,
|
||||||
remarkNickName: selfMember?.displayUserName,
|
remarkNickName: selfMember?.displayUserName,
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import { Button, Dropdown, Input, Menu } from 'ant-design-vue'
|
||||||
import { ImConversationType } from '../../../utils/constants'
|
import { ImConversationType } from '../../../utils/constants'
|
||||||
import { filterConversationsByKeyword, getConversationKey } from '../../../utils/conversation'
|
import { filterConversationsByKeyword, getConversationKey } from '../../../utils/conversation'
|
||||||
import { StorageKeys } from '../../../utils/db'
|
import { StorageKeys } from '../../../utils/db'
|
||||||
|
import { getGroupDisplayName } from '../../../utils/user'
|
||||||
import { ResizableAside } from '../../components'
|
import { ResizableAside } from '../../components'
|
||||||
import { FriendAddDialog } from '../../components/friend'
|
import { FriendAddDialog } from '../../components/friend'
|
||||||
import { GroupCreateDialog } from '../../components/group'
|
import { GroupCreateDialog } from '../../components/group'
|
||||||
|
|
@ -124,7 +125,7 @@ function handleGroupCreated(groupId: number) {
|
||||||
conversationStore.openConversation(
|
conversationStore.openConversation(
|
||||||
groupId,
|
groupId,
|
||||||
ImConversationType.GROUP,
|
ImConversationType.GROUP,
|
||||||
group.name,
|
getGroupDisplayName(group),
|
||||||
group.avatar || '',
|
group.avatar || '',
|
||||||
{ silent: !!group.silent }
|
{ silent: !!group.silent }
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -223,7 +223,7 @@ export const useGroupStore = defineStore('imGroupStore', {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const fresh = (list || []).map((group) => convertGroup(group))
|
const fresh = (list || []).map((group) => convertGroup(group))
|
||||||
// 合并而非全量替换:silent / groupRemark / 成员缓存这些字段不在 ImGroupApi.GroupRespVO 里,得从旧 group 保留
|
// 合并而非全量替换:成员缓存只在成员列表接口维护,群个人设置以群列表接口为准
|
||||||
const groupMap = new Map(this.groups.map((group) => [group.id, group]))
|
const groupMap = new Map(this.groups.map((group) => [group.id, group]))
|
||||||
this.groups = fresh.map((group) => {
|
this.groups = fresh.map((group) => {
|
||||||
const existing = groupMap.get(group.id)
|
const existing = groupMap.get(group.id)
|
||||||
|
|
@ -234,8 +234,6 @@ export const useGroupStore = defineStore('imGroupStore', {
|
||||||
...group,
|
...group,
|
||||||
members: existing.members,
|
members: existing.members,
|
||||||
memberCount: existing.memberCount ?? group.memberCount,
|
memberCount: existing.memberCount ?? group.memberCount,
|
||||||
silent: existing.silent ?? group.silent,
|
|
||||||
groupRemark: existing.groupRemark,
|
|
||||||
membersLoaded: existing.membersLoaded,
|
membersLoaded: existing.membersLoaded,
|
||||||
membersExpired: existing.membersExpired
|
membersExpired: existing.membersExpired
|
||||||
}
|
}
|
||||||
|
|
@ -250,6 +248,24 @@ export const useGroupStore = defineStore('imGroupStore', {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
this.saveGroupList()
|
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)
|
||||||
|
})
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/** 失效全部群成员缓存 */
|
/** 失效全部群成员缓存 */
|
||||||
|
|
@ -908,7 +924,9 @@ function convertGroup(group: ImGroupApi.GroupRespVO): Group {
|
||||||
mutedAll: group.mutedAll,
|
mutedAll: group.mutedAll,
|
||||||
banned: group.banned,
|
banned: group.banned,
|
||||||
joinApproval: group.joinApproval,
|
joinApproval: group.joinApproval,
|
||||||
joinStatus: group.joinStatus
|
joinStatus: group.joinStatus,
|
||||||
|
groupRemark: group.groupRemark,
|
||||||
|
silent: group.silent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ import {
|
||||||
playAudioTip,
|
playAudioTip,
|
||||||
resolveCallEndReasonText
|
resolveCallEndReasonText
|
||||||
} from '../../utils/message'
|
} from '../../utils/message'
|
||||||
import { getFriendDisplayName } from '../../utils/user'
|
import { getFriendDisplayName, getGroupDisplayName } from '../../utils/user'
|
||||||
import { useConversationStore } from './conversationStore'
|
import { useConversationStore } from './conversationStore'
|
||||||
import { type FriendNotificationPayload, useFriendStore } from './friendStore'
|
import { type FriendNotificationPayload, useFriendStore } from './friendStore'
|
||||||
import { useGroupRequestStore } from './groupRequestStore'
|
import { useGroupRequestStore } from './groupRequestStore'
|
||||||
|
|
@ -781,7 +781,7 @@ export const useImWebSocketStore = defineStore('imWebSocketStore', {
|
||||||
{
|
{
|
||||||
type: ImConversationType.GROUP,
|
type: ImConversationType.GROUP,
|
||||||
targetId: websocketMessage.groupId,
|
targetId: websocketMessage.groupId,
|
||||||
name: group?.name || String(websocketMessage.groupId),
|
name: group ? getGroupDisplayName(group) : String(websocketMessage.groupId),
|
||||||
avatar: group?.avatar || '',
|
avatar: group?.avatar || '',
|
||||||
silent: group?.silent
|
silent: group?.silent
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ export function usePrivateGridFormSchema(): VbenFormSchema[] {
|
||||||
component: 'Select',
|
component: 'Select',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
allowClear: true,
|
allowClear: true,
|
||||||
options: getDictOptions(DICT_TYPE.IM_MESSAGE_TYPE, 'number'),
|
options: getDictOptions(DICT_TYPE.IM_CONTENT_TYPE, 'number'),
|
||||||
placeholder: '请选择内容类型',
|
placeholder: '请选择内容类型',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -84,7 +84,7 @@ export function usePrivateGridColumns(showReadColumns: boolean): VxeTableGridOpt
|
||||||
width: 100,
|
width: 100,
|
||||||
cellRender: {
|
cellRender: {
|
||||||
name: 'CellDict',
|
name: 'CellDict',
|
||||||
props: { type: DICT_TYPE.IM_MESSAGE_TYPE },
|
props: { type: DICT_TYPE.IM_CONTENT_TYPE },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -101,7 +101,17 @@ export function usePrivateGridColumns(showReadColumns: boolean): VxeTableGridOpt
|
||||||
width: 100,
|
width: 100,
|
||||||
cellRender: {
|
cellRender: {
|
||||||
name: 'CellDict',
|
name: 'CellDict',
|
||||||
props: { type: DICT_TYPE.IM_PRIVATE_MESSAGE_STATUS },
|
props: { type: DICT_TYPE.IM_MESSAGE_STATUS },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'receiptStatus',
|
||||||
|
title: '回执',
|
||||||
|
width: 110,
|
||||||
|
cellRender: {
|
||||||
|
name: 'CellDict',
|
||||||
|
// 回执状态(私聊 / 群聊共用 im_message_receipt_status),与源端「回执」列对齐
|
||||||
|
props: { type: DICT_TYPE.IM_MESSAGE_RECEIPT_STATUS },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
@ -143,7 +153,7 @@ export function useGroupGridFormSchema(): VbenFormSchema[] {
|
||||||
component: 'Select',
|
component: 'Select',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
allowClear: true,
|
allowClear: true,
|
||||||
options: getDictOptions(DICT_TYPE.IM_MESSAGE_TYPE, 'number'),
|
options: getDictOptions(DICT_TYPE.IM_CONTENT_TYPE, 'number'),
|
||||||
placeholder: '请选择内容类型',
|
placeholder: '请选择内容类型',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -192,7 +202,7 @@ export function useGroupGridColumns(showReadColumns: boolean): VxeTableGridOptio
|
||||||
width: 100,
|
width: 100,
|
||||||
cellRender: {
|
cellRender: {
|
||||||
name: 'CellDict',
|
name: 'CellDict',
|
||||||
props: { type: DICT_TYPE.IM_MESSAGE_TYPE },
|
props: { type: DICT_TYPE.IM_CONTENT_TYPE },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -214,7 +224,7 @@ export function useGroupGridColumns(showReadColumns: boolean): VxeTableGridOptio
|
||||||
width: 100,
|
width: 100,
|
||||||
cellRender: {
|
cellRender: {
|
||||||
name: 'CellDict',
|
name: 'CellDict',
|
||||||
props: { type: DICT_TYPE.IM_GROUP_MESSAGE_STATUS },
|
props: { type: DICT_TYPE.IM_MESSAGE_STATUS },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -223,7 +233,7 @@ export function useGroupGridColumns(showReadColumns: boolean): VxeTableGridOptio
|
||||||
width: 110,
|
width: 110,
|
||||||
cellRender: {
|
cellRender: {
|
||||||
name: 'CellDict',
|
name: 'CellDict',
|
||||||
props: { type: DICT_TYPE.IM_GROUP_MESSAGE_RECEIPT_STATUS },
|
props: { type: DICT_TYPE.IM_MESSAGE_RECEIPT_STATUS },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -48,14 +48,14 @@ defineExpose({ open });
|
||||||
{{ formatUserLabel(detail.senderNickname, detail.senderId) }}
|
{{ formatUserLabel(detail.senderNickname, detail.senderId) }}
|
||||||
</DescriptionsItem>
|
</DescriptionsItem>
|
||||||
<DescriptionsItem label="类型">
|
<DescriptionsItem label="类型">
|
||||||
<DictTag :type="DICT_TYPE.IM_MESSAGE_TYPE" :value="detail.type" />
|
<DictTag :type="DICT_TYPE.IM_CONTENT_TYPE" :value="detail.type" />
|
||||||
</DescriptionsItem>
|
</DescriptionsItem>
|
||||||
<DescriptionsItem label="状态">
|
<DescriptionsItem label="状态">
|
||||||
<DictTag :type="DICT_TYPE.IM_GROUP_MESSAGE_STATUS" :value="detail.status" />
|
<DictTag :type="DICT_TYPE.IM_MESSAGE_STATUS" :value="detail.status" />
|
||||||
</DescriptionsItem>
|
</DescriptionsItem>
|
||||||
<DescriptionsItem v-if="MESSAGE_GROUP_READ_ENABLED" label="回执" :span="2">
|
<DescriptionsItem v-if="MESSAGE_GROUP_READ_ENABLED" label="回执" :span="2">
|
||||||
<DictTag
|
<DictTag
|
||||||
:type="DICT_TYPE.IM_GROUP_MESSAGE_RECEIPT_STATUS"
|
:type="DICT_TYPE.IM_MESSAGE_RECEIPT_STATUS"
|
||||||
:value="detail.receiptStatus"
|
:value="detail.receiptStatus"
|
||||||
/>
|
/>
|
||||||
</DescriptionsItem>
|
</DescriptionsItem>
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import {
|
||||||
formatJsonText,
|
formatJsonText,
|
||||||
formatUserLabel,
|
formatUserLabel,
|
||||||
} from '#/views/im/manager/utils/format';
|
} from '#/views/im/manager/utils/format';
|
||||||
|
import { MESSAGE_PRIVATE_READ_ENABLED } from '#/views/im/utils/config';
|
||||||
|
|
||||||
import { MessageContentPreview } from '../..';
|
import { MessageContentPreview } from '../..';
|
||||||
|
|
||||||
|
|
@ -44,10 +45,17 @@ defineExpose({ open });
|
||||||
{{ formatUserLabel(detail.receiverNickname, detail.receiverId) }}
|
{{ formatUserLabel(detail.receiverNickname, detail.receiverId) }}
|
||||||
</DescriptionsItem>
|
</DescriptionsItem>
|
||||||
<DescriptionsItem label="类型">
|
<DescriptionsItem label="类型">
|
||||||
<DictTag :type="DICT_TYPE.IM_MESSAGE_TYPE" :value="detail.type" />
|
<DictTag :type="DICT_TYPE.IM_CONTENT_TYPE" :value="detail.type" />
|
||||||
</DescriptionsItem>
|
</DescriptionsItem>
|
||||||
<DescriptionsItem label="状态">
|
<DescriptionsItem label="状态">
|
||||||
<DictTag :type="DICT_TYPE.IM_PRIVATE_MESSAGE_STATUS" :value="detail.status" />
|
<DictTag :type="DICT_TYPE.IM_MESSAGE_STATUS" :value="detail.status" />
|
||||||
|
</DescriptionsItem>
|
||||||
|
<DescriptionsItem v-if="MESSAGE_PRIVATE_READ_ENABLED" label="回执">
|
||||||
|
<!-- 回执状态(私聊 / 群聊共用 im_message_receipt_status),与源端私聊详情「回执」对齐 -->
|
||||||
|
<DictTag
|
||||||
|
:type="DICT_TYPE.IM_MESSAGE_RECEIPT_STATUS"
|
||||||
|
:value="detail.receiptStatus"
|
||||||
|
/>
|
||||||
</DescriptionsItem>
|
</DescriptionsItem>
|
||||||
<DescriptionsItem label="发送时间" :span="2">
|
<DescriptionsItem label="发送时间" :span="2">
|
||||||
{{ formatDateTimeText(detail.sendTime) }}
|
{{ formatDateTimeText(detail.sendTime) }}
|
||||||
|
|
|
||||||
|
|
@ -56,7 +56,7 @@ async function loadData() {
|
||||||
const data = await getMessageTypeDistribution();
|
const data = await getMessageTypeDistribution();
|
||||||
const items = data.map((item) => ({
|
const items = data.map((item) => ({
|
||||||
name:
|
name:
|
||||||
getDictObj(DICT_TYPE.IM_MESSAGE_TYPE, String(item.type))?.label ||
|
getDictObj(DICT_TYPE.IM_CONTENT_TYPE, String(item.type))?.label ||
|
||||||
`未知(${item.type})`,
|
`未知(${item.type})`,
|
||||||
value: item.value,
|
value: item.value,
|
||||||
}));
|
}));
|
||||||
|
|
|
||||||
|
|
@ -178,17 +178,16 @@ const IOT_DICT = {
|
||||||
/** ========== IM - 即时通讯模块 ========== */
|
/** ========== IM - 即时通讯模块 ========== */
|
||||||
const IM_DICT = {
|
const IM_DICT = {
|
||||||
IM_CHANNEL_MATERIAL_TYPE: 'im_channel_material_type', // IM 频道素材类型
|
IM_CHANNEL_MATERIAL_TYPE: 'im_channel_material_type', // IM 频道素材类型
|
||||||
|
IM_CONTENT_TYPE: 'im_content_type', // IM 消息内容类型
|
||||||
IM_FRIEND_ADD_SOURCE: 'im_friend_add_source', // IM 好友添加来源
|
IM_FRIEND_ADD_SOURCE: 'im_friend_add_source', // IM 好友添加来源
|
||||||
IM_FRIEND_REQUEST_HANDLE_RESULT: 'im_friend_request_handle_result', // IM 好友申请处理结果
|
IM_FRIEND_REQUEST_HANDLE_RESULT: 'im_friend_request_handle_result', // IM 好友申请处理结果
|
||||||
IM_FRIEND_STATUS: 'im_friend_status', // IM 好友状态
|
IM_FRIEND_STATUS: 'im_friend_status', // IM 好友状态
|
||||||
IM_GROUP_ADD_SOURCE: 'im_group_add_source', // IM 加群来源
|
IM_GROUP_ADD_SOURCE: 'im_group_add_source', // IM 加群来源
|
||||||
IM_GROUP_MEMBER_ROLE: 'im_group_member_role', // IM 群成员角色
|
IM_GROUP_MEMBER_ROLE: 'im_group_member_role', // IM 群成员角色
|
||||||
IM_GROUP_MESSAGE_RECEIPT_STATUS: 'im_group_message_receipt_status', // IM 群消息回执状态
|
|
||||||
IM_GROUP_MESSAGE_STATUS: 'im_group_message_status', // IM 群聊消息状态
|
|
||||||
IM_GROUP_REQUEST_HANDLE_RESULT: 'im_group_request_handle_result', // IM 加群申请处理结果
|
IM_GROUP_REQUEST_HANDLE_RESULT: 'im_group_request_handle_result', // IM 加群申请处理结果
|
||||||
IM_GROUP_STATUS: 'im_group_status', // IM 群状态
|
IM_GROUP_STATUS: 'im_group_status', // IM 群状态
|
||||||
IM_MESSAGE_TYPE: 'im_message_type', // IM 消息类型
|
IM_MESSAGE_RECEIPT_STATUS: 'im_message_receipt_status', // IM 消息回执状态(私聊 / 群聊共用)
|
||||||
IM_PRIVATE_MESSAGE_STATUS: 'im_private_message_status', // IM 私聊消息状态
|
IM_MESSAGE_STATUS: 'im_message_status', // IM 消息状态(私聊 / 群聊共用)
|
||||||
IM_RTC_CALL_CONVERSATION_TYPE: 'im_rtc_call_conversation_type', // IM 通话会话类型
|
IM_RTC_CALL_CONVERSATION_TYPE: 'im_rtc_call_conversation_type', // IM 通话会话类型
|
||||||
IM_RTC_CALL_END_REASON: 'im_rtc_call_end_reason', // IM 通话结束原因
|
IM_RTC_CALL_END_REASON: 'im_rtc_call_end_reason', // IM 通话结束原因
|
||||||
IM_RTC_CALL_MEDIA_TYPE: 'im_rtc_call_media_type', // IM 通话媒体类型
|
IM_RTC_CALL_MEDIA_TYPE: 'im_rtc_call_media_type', // IM 通话媒体类型
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue