Files
qhmes/jeecgboot-vue3/src/views/system/message/components/SysImChatMessageList.vue

224 lines
7.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<a-spin :spinning="loading">
<a-list item-layout="horizontal" :data-source="unreadList" :locale="locale">
<template #renderItem="{ item }">
<a-list-item class="im-chat-msg-item" @click="handleOpenChat(item)">
<template #actions>
<span class="im-chat-msg-time">{{ formatTime(item.lastTime) }}</span>
</template>
<a-list-item-meta>
<template #title>
<div class="im-chat-msg-title">
<span class="im-chat-msg-name">{{ item.displayName }}</span>
<a-badge :count="item.unreadCount" :overflow-count="99" />
</div>
</template>
<template #description>
<div class="im-chat-msg-preview">{{ formatPreview(item.lastContent) }}</div>
</template>
<template #avatar>
<a-badge dot :offset="[-2, 2]">
<!--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>
</a-list-item>
</template>
</a-list>
</a-spin>
<ImChatModal @register="registerImChatModal" />
</template>
<script lang="ts" setup>
import { computed, ref } from 'vue';
import dayjs from 'dayjs';
import { useModal } from '/@/components/Modal';
import { getFileAccessHttpUrl } from '/@/utils/common/compUtils';
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' });
const emit = defineEmits<{
(e: 'closeModal'): void;
}>();
//update-begin---author:xsl ---date:20260528 for【IM聊天】统一单聊与群聊展示类型-----------
type ItemType = 'single' | 'group';
interface UnreadItem {
type: ItemType;
/** 单聊userId群聊conversationId */
id: 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<any[]>([]);
const groups = ref<any[]>([]);
const [registerImChatModal, { openModal: openImChatModal }] = useModal();
//update-begin---author:xsl ---date:20260528 for【IM聊天】合并单聊和群聊未读列表-----------
const unreadList = computed<UnreadItem[]>(() => {
const singleItems: UnreadItem[] = members.value
.filter((item) => (item.unreadCount || 0) > 0)
.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 ? ' ' : '暂无未读聊天消息',
}));
function getAvatarUrl(avatar?: string) {
return avatar ? getFileAccessHttpUrl(avatar) : '';
}
function formatPreview(content?: string) {
return formatImMessagePreview(content) || '发来一条新消息';
}
function formatTime(value?: string) {
if (!value) {
return '';
}
const d = dayjs(value);
if (!d.isValid()) {
return '';
}
if (d.isSame(dayjs(), 'day')) {
return d.format('HH:mm');
}
return d.format('MM-DD HH:mm');
}
//update-begin---author:xsl ---date:20260528 for【IM聊天】同时拉取单聊成员和群聊列表修复 syncImUnreadFromMembers 丢失群聊未读-----------
async function reload(silent = false) {
const showLoading = !silent && members.value.length === 0 && groups.value.length === 0;
if (showLoading) {
loading.value = true;
}
try {
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:xsl ---date:20260528 for【IM聊天】同时拉取单聊成员和群聊列表修复 syncImUnreadFromMembers 丢失群聊未读-----------
//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 });
}
}
emit('closeModal');
}
//update-end---author:xsl ---date:20260528 for【IM聊天】点击群聊条目直接打开群聊会话-----------
defineExpose({ reload });
</script>
<style lang="less" scoped>
.im-chat-msg-item {
cursor: pointer;
transition: background-color 0.15s;
&:hover {
background: #f5f7fa;
}
}
.im-chat-msg-title {
display: flex;
align-items: center;
gap: 8px;
}
.im-chat-msg-name {
font-weight: 500;
color: rgba(0, 0, 0, 0.88);
}
.im-chat-msg-preview {
color: #666;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 520px;
}
.im-chat-msg-time {
color: #999;
font-size: 12px;
white-space: nowrap;
}
</style>