聊天室完成
This commit is contained in:
@@ -60,11 +60,11 @@ export const workcaseChatAPI = {
|
||||
|
||||
/**
|
||||
* 分页查询聊天室(含当前用户未读数)
|
||||
* @param pageRequest - 分页请求,filter.guestId用于过滤聊天室(guest用)
|
||||
* 注:userId从token中自动获取,无需传递
|
||||
*/
|
||||
async getChatRoomPage(pageRequest: PageRequest<TbChatRoomDTO>, userId: string): Promise<ResultDomain<ChatRoomVO>> {
|
||||
const response = await api.post<ChatRoomVO>(`${this.baseUrl}/room/page`, pageRequest, {
|
||||
params: { userId }
|
||||
})
|
||||
async getChatRoomPage(pageRequest: PageRequest<TbChatRoomDTO>): Promise<ResultDomain<ChatRoomVO>> {
|
||||
const response = await api.post<ChatRoomVO>(`${this.baseUrl}/room/page`, pageRequest)
|
||||
return response.data
|
||||
},
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ $brand-color-hover: #004488;
|
||||
|
||||
// ==================== 消息列表 ====================
|
||||
.messages-list {
|
||||
max-width: 900px;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
padding: 24px 16px;
|
||||
|
||||
|
||||
@@ -644,3 +644,46 @@ $brand-color-hover: #004488;
|
||||
padding: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== 聊天室包装容器 ====================
|
||||
.chat-room-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
// ==================== 自动填充加载遮罩 ====================
|
||||
.auto-fill-mask {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
gap: 16px;
|
||||
|
||||
.loading-spinner {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border: 3px solid #e2e8f0;
|
||||
border-top-color: $brand-color;
|
||||
border-radius: 50%;
|
||||
animation: spin 0.8s linear infinite;
|
||||
}
|
||||
|
||||
.loading-text {
|
||||
font-size: 14px;
|
||||
color: #64748b;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<button
|
||||
v-for="room in filteredRooms.slice(0, 8)"
|
||||
:key="room.roomId"
|
||||
@click="selectRoom(room.roomId!); toggleSidebar()"
|
||||
@click="selectRoom(room)"
|
||||
class="sidebar-icon-btn"
|
||||
:class="{ active: currentRoomId === room.roomId }"
|
||||
:title="room.roomName"
|
||||
@@ -55,7 +55,7 @@
|
||||
:key="room.roomId"
|
||||
class="room-item"
|
||||
:class="{ active: currentRoomId === room.roomId }"
|
||||
@click="selectRoom(room.roomId!)"
|
||||
@click="selectRoom(room)"
|
||||
>
|
||||
<!-- 头像 -->
|
||||
<div class="room-avatar">
|
||||
@@ -85,21 +85,28 @@
|
||||
<!-- 主聊天区域 -->
|
||||
<main class="chat-main" :class="{ 'sidebar-open': isSidebarOpen }">
|
||||
<template v-if="currentRoomId">
|
||||
<ChatRoom
|
||||
ref="chatRoomRef"
|
||||
:messages="messages"
|
||||
:current-user-id="loginDomain.user.userId"
|
||||
:room-name="currentRoom?.roomName"
|
||||
:meeting-url="currentMeetingUrl"
|
||||
:show-meeting="showMeetingIframe"
|
||||
:file-download-url="FILE_DOWNLOAD_URL"
|
||||
:has-more="hasMore"
|
||||
:loading-more="loadingMore"
|
||||
@send-message="handleSendMessage"
|
||||
@start-meeting="startMeeting"
|
||||
@download-file="downloadFile"
|
||||
@load-more="loadMoreMessages"
|
||||
>
|
||||
<div class="chat-room-wrapper">
|
||||
<!-- 自动填充加载遮罩 -->
|
||||
<div v-if="autoFilling" class="auto-fill-mask">
|
||||
<div class="loading-spinner"></div>
|
||||
<div class="loading-text">正在加载历史消息...</div>
|
||||
</div>
|
||||
|
||||
<ChatRoom
|
||||
ref="chatRoomRef"
|
||||
:messages="messages"
|
||||
:current-user-id="loginDomain.user.userId"
|
||||
:room-name="currentRoom?.roomName"
|
||||
:meeting-url="currentMeetingUrl"
|
||||
:show-meeting="showMeetingIframe"
|
||||
:file-download-url="FILE_DOWNLOAD_URL"
|
||||
:has-more="hasMore"
|
||||
:loading-more="loadingMore"
|
||||
@send-message="handleSendMessage"
|
||||
@start-meeting="startMeeting"
|
||||
@download-file="downloadFile"
|
||||
@load-more="loadMoreMessages"
|
||||
>
|
||||
<template #header>
|
||||
<div class="chat-room-header">
|
||||
<div class="header-left">
|
||||
@@ -123,6 +130,7 @@
|
||||
</ElButton>
|
||||
</template>
|
||||
</ChatRoom>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 空状态 -->
|
||||
@@ -193,6 +201,8 @@ const userType = true //web端固定这个
|
||||
const loading = ref(false)
|
||||
const messageLoading = ref(false)
|
||||
const loadingMore = ref(false)
|
||||
// 自动填充加载状态
|
||||
const autoFilling = ref(false)
|
||||
|
||||
// 分页状态
|
||||
const PAGE_SIZE = 5
|
||||
@@ -242,9 +252,11 @@ const fetchChatRooms = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const result = await workcaseChatAPI.getChatRoomPage({
|
||||
filter: { status: 'active' },
|
||||
filter: {
|
||||
status: 'active'
|
||||
},
|
||||
pageParam: { page: 1, pageSize: 100, total: 0 }
|
||||
}, loginDomain.user.userId)
|
||||
})
|
||||
if (result.success && result.pageDomain) {
|
||||
chatRooms.value = result.pageDomain.dataList || []
|
||||
}
|
||||
@@ -258,24 +270,25 @@ const fetchChatRooms = async () => {
|
||||
}
|
||||
|
||||
// 选择聊天室
|
||||
const selectRoom = async (roomId: string) => {
|
||||
currentRoomId.value = roomId
|
||||
const selectRoom = async (room: ChatRoomVO) => {
|
||||
currentRoomId.value = room.roomId!
|
||||
|
||||
// 自动加入聊天室成员表(如果不存在)
|
||||
try {
|
||||
const memberData: TbChatRoomMemberDTO = {
|
||||
roomId: roomId,
|
||||
roomId: room.roomId,
|
||||
userId: loginDomain.user.userId,
|
||||
userName: loginDomain.userInfo.username,
|
||||
userType: 'staff'
|
||||
}
|
||||
await workcaseChatAPI.addChatRoomMember(memberData)
|
||||
room.unreadCount = 0
|
||||
} catch (error) {
|
||||
// 已存在成员或其他错误,忽略
|
||||
console.debug('加入聊天室:', error)
|
||||
}
|
||||
|
||||
loadMessages(roomId)
|
||||
loadMessages(room.roomId!)
|
||||
}
|
||||
|
||||
// 加载消息(初始加载page1,后端降序返回)
|
||||
@@ -296,6 +309,9 @@ const loadMessages = async (roomId: string) => {
|
||||
// 后端降序返回,需要反转后显示(早的在上,新的在下)
|
||||
const dataList = result.pageDomain.dataList || []
|
||||
messages.value = [...dataList].reverse()
|
||||
|
||||
// 首次加载后自动填充消息直到出现滚动条
|
||||
await autoFillMessages(roomId)
|
||||
}
|
||||
// 加载完成后滚动到底部
|
||||
scrollToBottom()
|
||||
@@ -307,6 +323,85 @@ const loadMessages = async (roomId: string) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 自动填充消息直到出现滚动条
|
||||
const autoFillMessages = async (roomId: string) => {
|
||||
autoFilling.value = true
|
||||
console.log('[autoFill] 开始检查消息高度, hasMore:', hasMore.value, 'messages:', messages.value.length)
|
||||
|
||||
// 等待DOM渲染
|
||||
await nextTick()
|
||||
await new Promise(resolve => setTimeout(resolve, 300))
|
||||
|
||||
let attempts = 0
|
||||
const maxAttempts = 20
|
||||
|
||||
while (hasMore.value && attempts < maxAttempts) {
|
||||
attempts++
|
||||
|
||||
const container = chatRoomRef.value?.$el?.querySelector?.('.messages-container')
|
||||
const messagesList = chatRoomRef.value?.$el?.querySelector?.('.messages-list')
|
||||
|
||||
if (!container || !messagesList) {
|
||||
console.warn('[autoFill] 找不到容器或消息列表')
|
||||
break
|
||||
}
|
||||
|
||||
// 容器高度(可视区域)
|
||||
const containerHeight = container.clientHeight
|
||||
// 消息列表实际高度(内容)
|
||||
const listHeight = messagesList.offsetHeight
|
||||
const fillPercent = containerHeight > 0 ? Math.round(listHeight / containerHeight * 100) : 0
|
||||
|
||||
console.log(`[autoFill] 第${attempts}次检查 - 容器高度: ${containerHeight}px, 列表高度: ${listHeight}px, 填充率: ${fillPercent}%, 消息数: ${messages.value.length}`)
|
||||
|
||||
// 判断是否已经溢出(列表高度 >= 容器高度)
|
||||
if (containerHeight > 0 && listHeight >= containerHeight) {
|
||||
console.log(`[autoFill] ✓ 列表已溢出(${fillPercent}%),可以滚动!停止加载`)
|
||||
break
|
||||
}
|
||||
|
||||
// 内容不足,继续加载下一页
|
||||
console.log('[autoFill] → 内容不足,继续加载历史消息...')
|
||||
const nextPage = currentPage.value + 1
|
||||
const result = await workcaseChatAPI.getChatMessagePage({
|
||||
filter: { roomId },
|
||||
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)
|
||||
console.log(`[autoFill] ✓ 加载第${nextPage}页完成, 新增${dataList.length}条, 总消息数: ${messages.value.length}`)
|
||||
|
||||
await nextTick()
|
||||
await new Promise(resolve => setTimeout(resolve, 200))
|
||||
} else {
|
||||
hasMore.value = false
|
||||
break
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (attempts >= maxAttempts) {
|
||||
console.warn('[autoFill] ⚠ 达到最大尝试次数')
|
||||
}
|
||||
|
||||
console.log(`[autoFill] 自动填充结束 - 共尝试${attempts}次, 最终消息数: ${messages.value.length}, hasMore: ${hasMore.value}`)
|
||||
|
||||
autoFilling.value = false
|
||||
}
|
||||
|
||||
// 加载更多历史消息(滚动到顶部触发)
|
||||
const loadMoreMessages = async () => {
|
||||
if (!currentRoomId.value || loadingMore.value || !hasMore.value) return
|
||||
|
||||
Reference in New Issue
Block a user