web端聊天室优化
This commit is contained in:
@@ -10,6 +10,14 @@ $brand-color-hover: #004488;
|
|||||||
background: #fff;
|
background: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ==================== 加载更多 ====================
|
||||||
|
.loading-more {
|
||||||
|
text-align: center;
|
||||||
|
padding: 12px;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #94a3b8;
|
||||||
|
}
|
||||||
|
|
||||||
// ==================== 聊天室头部 ====================
|
// ==================== 聊天室头部 ====================
|
||||||
.chat-header {
|
.chat-header {
|
||||||
height: 64px;
|
height: 64px;
|
||||||
@@ -96,12 +104,28 @@ $brand-color-hover: #004488;
|
|||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
background: $brand-color-light;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
img {
|
img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.avatar-text {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $brand-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sender-name {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #64748b;
|
||||||
|
margin-bottom: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-content-wrapper {
|
.message-content-wrapper {
|
||||||
|
|||||||
@@ -10,7 +10,11 @@
|
|||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- 消息容器 -->
|
<!-- 消息容器 -->
|
||||||
<div ref="messagesRef" class="messages-container">
|
<div ref="messagesRef" class="messages-container" @scroll="handleScroll">
|
||||||
|
<!-- 加载更多提示 -->
|
||||||
|
<div v-if="loadingMore" class="loading-more">加载中...</div>
|
||||||
|
<div v-else-if="!hasMore" class="loading-more">没有更多消息了</div>
|
||||||
|
|
||||||
<!-- Jitsi Meet会议iframe -->
|
<!-- Jitsi Meet会议iframe -->
|
||||||
<div v-if="showMeeting && meetingUrl" class="meeting-container">
|
<div v-if="showMeeting && meetingUrl" class="meeting-container">
|
||||||
<IframeView :src="meetingUrl" />
|
<IframeView :src="meetingUrl" />
|
||||||
@@ -24,9 +28,13 @@
|
|||||||
class="message-row"
|
class="message-row"
|
||||||
:class="message.senderId === currentUserId ? 'is-me' : 'other'"
|
:class="message.senderId === currentUserId ? 'is-me' : 'other'"
|
||||||
>
|
>
|
||||||
<!-- 头像 -->
|
<div>
|
||||||
<div class="message-avatar">
|
<!-- 头像 -->
|
||||||
<img :src="FILE_DOWNLOAD_URL + message.senderAvatar" />
|
<div class="message-avatar">
|
||||||
|
<img v-if="message.senderAvatar" :src="FILE_DOWNLOAD_URL + message.senderAvatar" />
|
||||||
|
<span v-else class="avatar-text">{{ message.senderName?.charAt(0) || '?' }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="sender-name">{{ message.senderName || '未知用户' }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 消息内容 -->
|
<!-- 消息内容 -->
|
||||||
@@ -126,12 +134,16 @@ interface Props {
|
|||||||
meetingUrl?: string
|
meetingUrl?: string
|
||||||
showMeeting?: boolean
|
showMeeting?: boolean
|
||||||
fileDownloadUrl?: string
|
fileDownloadUrl?: string
|
||||||
|
hasMore?: boolean
|
||||||
|
loadingMore?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
roomName: '聊天室',
|
roomName: '聊天室',
|
||||||
showMeeting: false,
|
showMeeting: false,
|
||||||
fileDownloadUrl: ''
|
fileDownloadUrl: '',
|
||||||
|
hasMore: true,
|
||||||
|
loadingMore: false
|
||||||
})
|
})
|
||||||
|
|
||||||
const FILE_DOWNLOAD_URL = props.fileDownloadUrl
|
const FILE_DOWNLOAD_URL = props.fileDownloadUrl
|
||||||
@@ -140,8 +152,17 @@ const emit = defineEmits<{
|
|||||||
'send-message': [content: string, files: File[]]
|
'send-message': [content: string, files: File[]]
|
||||||
'start-meeting': []
|
'start-meeting': []
|
||||||
'download-file': [fileId: string]
|
'download-file': [fileId: string]
|
||||||
|
'load-more': []
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
// 滚动到顶部加载更多
|
||||||
|
const handleScroll = (e: Event) => {
|
||||||
|
const target = e.target as HTMLElement
|
||||||
|
if (target.scrollTop < 50 && props.hasMore && !props.loadingMore) {
|
||||||
|
emit('load-more')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
defineSlots<{
|
defineSlots<{
|
||||||
header?: () => any
|
header?: () => any
|
||||||
'action-area'?: () => any
|
'action-area'?: () => any
|
||||||
|
|||||||
@@ -12,15 +12,135 @@ $brand-color-hover: #004488;
|
|||||||
font-family: 'Inter', 'Noto Sans SC', sans-serif;
|
font-family: 'Inter', 'Noto Sans SC', sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== 聊天室列表侧边栏 ====================
|
// ==================== 折叠状态的侧边栏 ====================
|
||||||
.room-list-sidebar {
|
.sidebar-collapsed {
|
||||||
width: 320px;
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
width: 48px;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-right: 1px solid #e2e8f0;
|
border-right: 1px solid #e2e8f0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex-shrink: 0;
|
align-items: center;
|
||||||
|
padding: 16px 0;
|
||||||
|
z-index: 10;
|
||||||
|
|
||||||
|
.sidebar-toggle-btn {
|
||||||
|
padding: 8px;
|
||||||
|
color: #64748b;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $brand-color;
|
||||||
|
background: $brand-color-light;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-icons {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-icon-btn {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #fff;
|
||||||
|
background: linear-gradient(135deg, $brand-color 0%, $brand-color-hover 100%);
|
||||||
|
border: none;
|
||||||
|
border-radius: 50%;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
box-shadow: 0 0 0 2px $brand-color-light;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.expand-btn {
|
||||||
|
padding: 8px;
|
||||||
|
color: #94a3b8;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #64748b;
|
||||||
|
background: #f1f5f9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 展开时的关闭按钮 ====================
|
||||||
|
.sidebar-close-btn {
|
||||||
|
position: absolute;
|
||||||
|
left: 256px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
z-index: 20;
|
||||||
|
background: #fff;
|
||||||
|
border: 1px solid #e2e8f0;
|
||||||
|
border-left: none;
|
||||||
|
border-radius: 0 8px 8px 0;
|
||||||
|
padding: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.05);
|
||||||
|
transition: all 0.2s;
|
||||||
|
color: #64748b;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #f8fafc;
|
||||||
|
box-shadow: 2px 0 12px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 聊天室列表侧边栏 ====================
|
||||||
|
.room-list-sidebar {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
height: 100%;
|
||||||
|
width: 0;
|
||||||
|
background: #fff;
|
||||||
|
border-right: 1px solid #e2e8f0;
|
||||||
|
z-index: 10;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 4px 0 16px rgba(0, 0, 0, 0.08);
|
||||||
|
|
||||||
|
&.open {
|
||||||
|
width: 256px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-inner {
|
||||||
|
width: 256px;
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar-header {
|
.sidebar-header {
|
||||||
height: 56px;
|
height: 56px;
|
||||||
@@ -156,6 +276,12 @@ $brand-color-hover: #004488;
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
|
margin-left: 48px;
|
||||||
|
transition: margin-left 0.3s ease;
|
||||||
|
|
||||||
|
&.sidebar-open {
|
||||||
|
margin-left: 256px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== 聊天室头部 ====================
|
// ==================== 聊天室头部 ====================
|
||||||
|
|||||||
@@ -1,69 +1,102 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="chat-room-container">
|
<div class="chat-room-container">
|
||||||
<!-- 聊天室列表侧边栏 -->
|
<!-- 折叠状态的侧边栏 -->
|
||||||
<aside class="room-list-sidebar">
|
<div v-if="!isSidebarOpen" class="sidebar-collapsed">
|
||||||
<!-- 头部 -->
|
<button class="sidebar-toggle-btn" @click="toggleSidebar" title="展开聊天室列表">
|
||||||
<div class="sidebar-header">
|
<MessageCircle :size="20" />
|
||||||
<span class="title">聊天室</span>
|
</button>
|
||||||
</div>
|
<div class="sidebar-icons">
|
||||||
|
<button
|
||||||
<!-- 搜索框 -->
|
v-for="room in filteredRooms.slice(0, 8)"
|
||||||
<div class="search-box">
|
|
||||||
<ElInput
|
|
||||||
v-model="searchText"
|
|
||||||
placeholder="搜索工单号、来客姓名、电话..."
|
|
||||||
:prefix-icon="Search"
|
|
||||||
clearable
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- chatRoom列表 -->
|
|
||||||
<div class="room-list-container">
|
|
||||||
<div v-if="filteredRooms.length === 0" class="empty-tip">
|
|
||||||
暂无聊天室
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-for="room in filteredRooms"
|
|
||||||
:key="room.roomId"
|
:key="room.roomId"
|
||||||
class="room-item"
|
@click="selectRoom(room.roomId!); toggleSidebar()"
|
||||||
|
class="sidebar-icon-btn"
|
||||||
:class="{ active: currentRoomId === room.roomId }"
|
:class="{ active: currentRoomId === room.roomId }"
|
||||||
@click="selectRoom(room.roomId!)"
|
:title="room.roomName"
|
||||||
>
|
>
|
||||||
<!-- 头像 -->
|
{{ room.guestName?.substring(0, 1) || '?' }}
|
||||||
<div class="room-avatar">
|
</button>
|
||||||
{{ room.guestName?.substring(0, 1) || '?' }}
|
</div>
|
||||||
</div>
|
<button class="expand-btn" @click="toggleSidebar" title="展开聊天室列表">
|
||||||
|
<ChevronRight :size="18" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 信息 -->
|
<!-- 展开时的关闭按钮 -->
|
||||||
<div class="room-info">
|
<button v-if="isSidebarOpen" class="sidebar-close-btn" @click="toggleSidebar">
|
||||||
<div class="room-header">
|
<ChevronLeft :size="18" />
|
||||||
<div class="room-name">{{ room.roomName }}</div>
|
</button>
|
||||||
<div class="room-time">{{ formatTime(room.lastMessageTime) }}</div>
|
|
||||||
|
<!-- 聊天室列表侧边栏 -->
|
||||||
|
<aside class="room-list-sidebar" :class="{ open: isSidebarOpen }">
|
||||||
|
<div class="sidebar-inner">
|
||||||
|
<!-- 头部 -->
|
||||||
|
<div class="sidebar-header">
|
||||||
|
<span class="title">聊天室</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 搜索框 -->
|
||||||
|
<div class="search-box">
|
||||||
|
<ElInput
|
||||||
|
v-model="searchText"
|
||||||
|
placeholder="搜索工单号、来客姓名、电话..."
|
||||||
|
:prefix-icon="Search"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- chatRoom列表 -->
|
||||||
|
<div class="room-list-container">
|
||||||
|
<div v-if="filteredRooms.length === 0" class="empty-tip">
|
||||||
|
暂无聊天室
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="room in filteredRooms"
|
||||||
|
:key="room.roomId"
|
||||||
|
class="room-item"
|
||||||
|
:class="{ active: currentRoomId === room.roomId }"
|
||||||
|
@click="selectRoom(room.roomId!)"
|
||||||
|
>
|
||||||
|
<!-- 头像 -->
|
||||||
|
<div class="room-avatar">
|
||||||
|
{{ room.guestName?.substring(0, 1) || '?' }}
|
||||||
</div>
|
</div>
|
||||||
<div class="last-message">{{ room.lastMessage || '暂无消息' }}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 未读红点 -->
|
<!-- 信息 -->
|
||||||
<div v-if="(room.unreadCount ?? 0) > 0" class="unread-badge">
|
<div class="room-info">
|
||||||
{{ (room.unreadCount ?? 0) > 99 ? '99+' : room.unreadCount }}
|
<div class="room-header">
|
||||||
|
<div class="room-name">{{ room.roomName }}</div>
|
||||||
|
<div class="room-time">{{ formatTime(room.lastMessageTime) }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="last-message">{{ room.lastMessage || '暂无消息' }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 未读红点 -->
|
||||||
|
<div v-if="(room.unreadCount ?? 0) > 0" class="unread-badge">
|
||||||
|
{{ (room.unreadCount ?? 0) > 99 ? '99+' : room.unreadCount }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<!-- 主聊天区域 -->
|
<!-- 主聊天区域 -->
|
||||||
<main class="chat-main">
|
<main class="chat-main" :class="{ 'sidebar-open': isSidebarOpen }">
|
||||||
<template v-if="currentRoomId">
|
<template v-if="currentRoomId">
|
||||||
<ChatRoom
|
<ChatRoom
|
||||||
|
ref="chatRoomRef"
|
||||||
:messages="messages"
|
:messages="messages"
|
||||||
:current-user-id="userId"
|
:current-user-id="userId"
|
||||||
:room-name="currentRoom?.roomName"
|
:room-name="currentRoom?.roomName"
|
||||||
:meeting-url="currentMeetingUrl"
|
:meeting-url="currentMeetingUrl"
|
||||||
:show-meeting="showMeetingIframe"
|
:show-meeting="showMeetingIframe"
|
||||||
:file-download-url="FILE_DOWNLOAD_URL"
|
:file-download-url="FILE_DOWNLOAD_URL"
|
||||||
|
:has-more="hasMore"
|
||||||
|
:loading-more="loadingMore"
|
||||||
@send-message="handleSendMessage"
|
@send-message="handleSendMessage"
|
||||||
@start-meeting="startMeeting"
|
@start-meeting="startMeeting"
|
||||||
@download-file="downloadFile"
|
@download-file="downloadFile"
|
||||||
|
@load-more="loadMoreMessages"
|
||||||
>
|
>
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="chat-room-header">
|
<div class="chat-room-header">
|
||||||
@@ -113,10 +146,11 @@
|
|||||||
</ElDialog>
|
</ElDialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
|
import { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue'
|
||||||
import { ElButton, ElInput, ElDialog, ElMessage } from 'element-plus'
|
import { ElButton, ElInput, ElDialog, ElMessage } from 'element-plus'
|
||||||
import { Search, FileText, MessageSquare } from 'lucide-vue-next'
|
import { Search, FileText, MessageSquare, MessageCircle, ChevronLeft, ChevronRight } from 'lucide-vue-next'
|
||||||
import { ChatRoom } from '@/components/chatRoom'
|
import { ChatRoom } from '@/components/chatRoom'
|
||||||
import WorkcaseDetail from '@/views/public/workcase/WorkcaseDetail/WorkcaseDetail.vue'
|
import WorkcaseDetail from '@/views/public/workcase/WorkcaseDetail/WorkcaseDetail.vue'
|
||||||
import { workcaseChatAPI } from '@/api/workcase'
|
import { workcaseChatAPI } from '@/api/workcase'
|
||||||
@@ -143,12 +177,26 @@ let listSubscription: any = null
|
|||||||
// 当前用户ID(从登录状态获取)
|
// 当前用户ID(从登录状态获取)
|
||||||
const userId = ref(localStorage.getItem('userId') || '')
|
const userId = ref(localStorage.getItem('userId') || '')
|
||||||
|
|
||||||
|
// 侧边栏展开状态
|
||||||
|
const isSidebarOpen = ref(false)
|
||||||
|
|
||||||
|
// 切换侧边栏
|
||||||
|
const toggleSidebar = () => {
|
||||||
|
isSidebarOpen.value = !isSidebarOpen.value
|
||||||
|
}
|
||||||
|
|
||||||
// 搜索文本
|
// 搜索文本
|
||||||
const searchText = ref('')
|
const searchText = ref('')
|
||||||
const userType = true //web端固定这个
|
const userType = true //web端固定这个
|
||||||
// 加载状态
|
// 加载状态
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const messageLoading = ref(false)
|
const messageLoading = ref(false)
|
||||||
|
const loadingMore = ref(false)
|
||||||
|
|
||||||
|
// 分页状态
|
||||||
|
const PAGE_SIZE = 5
|
||||||
|
const currentPage = ref(1)
|
||||||
|
const hasMore = ref(true)
|
||||||
|
|
||||||
// 聊天室列表
|
// 聊天室列表
|
||||||
const chatRooms = ref<ChatRoomVO[]>([])
|
const chatRooms = ref<ChatRoomVO[]>([])
|
||||||
@@ -185,6 +233,9 @@ const showWorkcaseDetail = ref(false)
|
|||||||
const currentMeetingUrl = ref('')
|
const currentMeetingUrl = ref('')
|
||||||
const showMeetingIframe = ref(false)
|
const showMeetingIframe = ref(false)
|
||||||
|
|
||||||
|
// ChatRoom组件引用
|
||||||
|
const chatRoomRef = ref<InstanceType<typeof ChatRoom> | null>(null)
|
||||||
|
|
||||||
// 获取聊天室列表
|
// 获取聊天室列表
|
||||||
const fetchChatRooms = async () => {
|
const fetchChatRooms = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
@@ -196,6 +247,7 @@ const fetchChatRooms = async () => {
|
|||||||
if (result.success && result.pageDomain) {
|
if (result.success && result.pageDomain) {
|
||||||
chatRooms.value = result.pageDomain.dataList || []
|
chatRooms.value = result.pageDomain.dataList || []
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取聊天室列表失败:', error)
|
console.error('获取聊天室列表失败:', error)
|
||||||
ElMessage.error('获取聊天室列表失败')
|
ElMessage.error('获取聊天室列表失败')
|
||||||
@@ -210,17 +262,26 @@ const selectRoom = (roomId: string) => {
|
|||||||
loadMessages(roomId)
|
loadMessages(roomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 加载消息
|
// 加载消息(初始加载page1,后端降序返回)
|
||||||
const loadMessages = async (roomId: string) => {
|
const loadMessages = async (roomId: string) => {
|
||||||
messageLoading.value = true
|
messageLoading.value = true
|
||||||
|
currentPage.value = 1
|
||||||
|
hasMore.value = true
|
||||||
try {
|
try {
|
||||||
const result = await workcaseChatAPI.getChatMessagePage({
|
const result = await workcaseChatAPI.getChatMessagePage({
|
||||||
filter: { roomId },
|
filter: { roomId },
|
||||||
pageParam: { page: 1, pageSize: 100, total: 0 }
|
pageParam: { page: 1, pageSize: PAGE_SIZE }
|
||||||
})
|
})
|
||||||
if (result.success && result.pageDomain) {
|
if (result.success && result.pageDomain) {
|
||||||
messages.value = result.pageDomain.dataList || []
|
const pageInfo = result.pageDomain.pageParam
|
||||||
|
const actualTotalPages = pageInfo?.totalPages || 1
|
||||||
|
hasMore.value = actualTotalPages > currentPage.value
|
||||||
|
|
||||||
|
// 后端降序返回,需要反转后显示(早的在上,新的在下)
|
||||||
|
const dataList = result.pageDomain.dataList || []
|
||||||
|
messages.value = [...dataList].reverse()
|
||||||
}
|
}
|
||||||
|
// 加载完成后滚动到底部
|
||||||
scrollToBottom()
|
scrollToBottom()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('加载消息失败:', error)
|
console.error('加载消息失败:', error)
|
||||||
@@ -230,6 +291,39 @@ const loadMessages = async (roomId: string) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 加载更多历史消息(滚动到顶部触发)
|
||||||
|
const loadMoreMessages = async () => {
|
||||||
|
if (!currentRoomId.value || loadingMore.value || !hasMore.value) return
|
||||||
|
|
||||||
|
const nextPage = currentPage.value + 1
|
||||||
|
loadingMore.value = true
|
||||||
|
try {
|
||||||
|
const result = await workcaseChatAPI.getChatMessagePage({
|
||||||
|
filter: { roomId: currentRoomId.value },
|
||||||
|
pageParam: { page: nextPage, pageSize: PAGE_SIZE }
|
||||||
|
})
|
||||||
|
if (result.success && result.pageDomain) {
|
||||||
|
const pageInfo = result.pageDomain.pageParam
|
||||||
|
const actualTotalPages = pageInfo?.totalPages || 1
|
||||||
|
const dataList = result.pageDomain.dataList || []
|
||||||
|
|
||||||
|
if (dataList.length > 0) {
|
||||||
|
currentPage.value = nextPage
|
||||||
|
hasMore.value = actualTotalPages > currentPage.value
|
||||||
|
// 后端降序返回,反转后插入到列表前面
|
||||||
|
const reversedList = [...dataList].reverse()
|
||||||
|
messages.value.unshift(...reversedList)
|
||||||
|
} else {
|
||||||
|
hasMore.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载更多消息失败:', error)
|
||||||
|
} finally {
|
||||||
|
loadingMore.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 处理发送消息(从ChatRoom组件触发)
|
// 处理发送消息(从ChatRoom组件触发)
|
||||||
const handleSendMessage = async (content: string, files: File[]) => {
|
const handleSendMessage = async (content: string, files: File[]) => {
|
||||||
if (!currentRoomId.value) return
|
if (!currentRoomId.value) return
|
||||||
@@ -284,9 +378,11 @@ const startMeeting = async () => {
|
|||||||
showMeetingIframe.value = true
|
showMeetingIframe.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// 滚动到底部
|
// 滚动聊天消息到底部
|
||||||
const scrollToBottom = () => {
|
const scrollToBottom = () => {
|
||||||
// TODO: 滚动到ChatRoom组件底部
|
nextTick(() => {
|
||||||
|
chatRoomRef.value?.scrollToBottom()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 格式化时间(用于聊天室列表)
|
// 格式化时间(用于聊天室列表)
|
||||||
|
|||||||
Reference in New Issue
Block a user