实现IM聊天群聊功能,包括群聊列表和创建群聊接口,优化消息处理和未读消息统计,增强用户体验。

This commit is contained in:
geht
2026-05-28 18:46:27 +08:00
parent a63cd6ad1a
commit 44a5868349
20 changed files with 1504 additions and 170 deletions

View File

@@ -9,7 +9,7 @@
<a-list-item-meta>
<template #title>
<div class="im-chat-msg-title">
<span class="im-chat-msg-name">{{ item.realname || item.username }}</span>
<span class="im-chat-msg-name">{{ item.displayName }}</span>
<a-badge :count="item.unreadCount" :overflow-count="99" />
</div>
</template>
@@ -18,9 +18,14 @@
</template>
<template #avatar>
<a-badge dot :offset="[-2, 2]">
<a-avatar :src="getAvatarUrl(item.avatar)">
{{ (item.realname || item.username || '?').slice(0, 1) }}
<!--update-begin---author:xsl ---date:20260528 forIM聊天群聊显示群图标单聊显示头像------------->
<a-avatar v-if="item.type === 'group'" :style="{ backgroundColor: '#1677ff' }">
<Icon icon="ant-design:team-outlined" />
</a-avatar>
<a-avatar v-else :src="getAvatarUrl(item.avatar)">
{{ (item.displayName || '?').slice(0, 1) }}
</a-avatar>
<!--update-end---author:xsl ---date:20260528 forIM聊天群聊显示群图标单聊显示头像----------->
</a-badge>
</template>
</a-list-item-meta>
@@ -37,11 +42,13 @@
import dayjs from 'dayjs';
import { useModal } from '/@/components/Modal';
import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
import { fetchDeptMembers } from '/@/views/system/im/im.api';
import { fetchDeptMembers, fetchGroups } from '/@/views/system/im/im.api';
import { formatImMessagePreview } from '/@/views/system/im/imMessageUtil';
import { syncImUnreadFromMembers } from '/@/views/system/im/useImUnread';
import { getCachedGroupUnreadItems, initGroupUnreadFromList } from '/@/views/system/im/imCache';
import ImChatModal from '/@/views/system/im/ImChatModal.vue';
import { openImChat } from '/@/views/system/im/imSession';
import { Icon } from '/@/components/Icon';
defineOptions({ name: 'SysImChatMessageList' });
@@ -49,26 +56,59 @@
(e: 'closeModal'): void;
}>();
interface ChatMemberItem {
//update-begin---author:xsl ---date:20260528 for【IM聊天】统一单聊与群聊展示类型-----------
type ItemType = 'single' | 'group';
interface UnreadItem {
type: ItemType;
/** 单聊userId群聊conversationId */
id: string;
username: string;
realname?: string;
displayName: string;
avatar?: string;
conversationId?: string;
lastContent?: string;
lastTime?: string;
unreadCount?: number;
}
//update-end---author:xsl ---date:20260528 for【IM聊天】统一单聊与群聊展示类型-----------
const loading = ref(false);
const members = ref<ChatMemberItem[]>([]);
const members = ref<any[]>([]);
const groups = ref<any[]>([]);
const [registerImChatModal, { openModal: openImChatModal }] = useModal();
const unreadList = computed(() =>
members.value
//update-begin---author:xsl ---date:20260528 for【IM聊天】合并单聊和群聊未读列表-----------
const unreadList = computed<UnreadItem[]>(() => {
const singleItems: UnreadItem[] = members.value
.filter((item) => (item.unreadCount || 0) > 0)
.sort((a, b) => dayjs(b.lastTime || 0).valueOf() - dayjs(a.lastTime || 0).valueOf()),
);
.map((item) => ({
type: 'single',
id: item.id,
displayName: item.realname || item.username || '',
avatar: item.avatar,
conversationId: item.conversationId,
lastContent: item.lastContent,
lastTime: item.lastTime,
unreadCount: item.unreadCount,
}));
const groupItems: UnreadItem[] = groups.value
.filter((item) => (item.unreadCount || 0) > 0)
.map((item) => ({
type: 'group',
id: item.conversationId,
displayName: item.groupName || '群聊',
conversationId: item.conversationId,
lastContent: item.lastContent,
lastTime: item.lastTime,
unreadCount: item.unreadCount,
}));
return [...singleItems, ...groupItems].sort(
(a, b) => dayjs(b.lastTime || 0).valueOf() - dayjs(a.lastTime || 0).valueOf(),
);
});
//update-end---author:xsl ---date:20260528 for【IM聊天】合并单聊和群聊未读列表-----------
const locale = computed(() => ({
emptyText: loading.value ? ' ' : '暂无未读聊天消息',
@@ -96,36 +136,52 @@
return d.format('MM-DD HH:mm');
}
//update-begin---author:xsl ---date:20260528 for【IM聊天】同时拉取单聊成员和群聊列表修复 syncImUnreadFromMembers 丢失群聊未读-----------
async function reload(silent = false) {
//update-begin---author:cursor ---date:20250528 for【IM聊天-OA】聊天消息列表静默刷新避免 spin 遮罩闪烁-----------
const showLoading = !silent && members.value.length === 0;
const showLoading = !silent && members.value.length === 0 && groups.value.length === 0;
if (showLoading) {
loading.value = true;
}
try {
members.value = ((await fetchDeptMembers()) || []) as ChatMemberItem[];
syncImUnreadFromMembers(members.value);
const [fetchedMembers, fetchedGroups] = await Promise.all([
fetchDeptMembers().catch(() => []),
fetchGroups().catch(() => []),
]);
members.value = (fetchedMembers || []) as any[];
groups.value = (fetchedGroups || []) as any[];
// 同步群聊未读到全局缓存
initGroupUnreadFromList(groups.value);
// 合并单聊 + 群聊未读数,防止 syncImUnreadFromMembers 只传成员数据导致群聊角标清零
syncImUnreadFromMembers([...members.value, ...getCachedGroupUnreadItems()]);
} catch {
if (!silent) {
members.value = [];
groups.value = [];
}
} finally {
if (showLoading) {
loading.value = false;
}
}
//update-end---author:cursor ---date:20250528 for【IM聊天-OA】聊天消息列表静默刷新避免 spin 遮罩闪烁-----------
}
//update-end---author:xsl ---date:20260528 for【IM聊天】同时拉取单聊成员和群聊列表修复 syncImUnreadFromMembers 丢失群聊未读-----------
async function handleOpenChat(item: ChatMemberItem) {
//update-begin---author:xsl ---date:20260528 for【IM聊天-OA】统一 IM 打开入口,全页优先-----------
const mode = await openImChat({ targetUserId: item.id, pageContext: null });
if (mode === 'modal') {
openImChatModal(true, { targetUserId: item.id, pageContext: null });
//update-begin---author:xsl ---date:20260528 for【IM聊天】点击群聊条目直接打开群聊会话-----------
async function handleOpenChat(item: UnreadItem) {
if (item.type === 'group') {
const mode = await openImChat({ conversationId: item.conversationId });
if (mode === 'modal') {
openImChatModal(true, { conversationId: item.conversationId });
}
} else {
const mode = await openImChat({ targetUserId: item.id, pageContext: null });
if (mode === 'modal') {
openImChatModal(true, { targetUserId: item.id, pageContext: null });
}
}
//update-end---author:xsl ---date:20260528 for【IM聊天-OA】统一 IM 打开入口,全页优先-----------
emit('closeModal');
}
//update-end---author:xsl ---date:20260528 for【IM聊天】点击群聊条目直接打开群聊会话-----------
defineExpose({ reload });
</script>