web聊天室数据同步修改

This commit is contained in:
2025-12-24 15:02:23 +08:00
parent 1fd26dcf1a
commit 898da3a2c6
16 changed files with 691 additions and 53 deletions

View File

@@ -59,10 +59,12 @@ export const workcaseChatAPI = {
},
/**
* 分页查询聊天室
* 分页查询聊天室(含当前用户未读数)
*/
async getChatRoomPage(pageRequest: PageRequest<TbChatRoomDTO>): Promise<ResultDomain<ChatRoomVO>> {
const response = await api.post<ChatRoomVO>(`${this.baseUrl}/room/page`, pageRequest)
async getChatRoomPage(pageRequest: PageRequest<TbChatRoomDTO>, userId: string): Promise<ResultDomain<ChatRoomVO>> {
const response = await api.post<ChatRoomVO>(`${this.baseUrl}/room/page`, pageRequest, {
params: { userId }
})
return response.data
},
@@ -92,6 +94,16 @@ export const workcaseChatAPI = {
return response.data
},
/**
* 获取当前用户在指定聊天室的未读消息数
*/
async getUnreadCount(roomId: string, userId: string): Promise<ResultDomain<number>> {
const response = await api.get<number>(`${this.baseUrl}/room/${roomId}/unread`,
{ userId }
)
return response.data
},
// ====================== ChatRoom消息管理 ======================
/**

View File

@@ -52,7 +52,7 @@ declare module 'shared/api' {
import type { AxiosResponse, AxiosRequestConfig } from 'axios'
interface ApiInstance {
get<T = any>(url: string, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>
get<T = any>(url: string,data?: any, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>
post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>
put<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>
delete<T = any>(url: string, config?: AxiosRequestConfig): Promise<AxiosResponse<T>>

View File

@@ -235,7 +235,14 @@ $brand-color-hover: #004488;
flex-shrink: 0;
}
.last-message-row {
display: flex;
align-items: center;
gap: 8px;
}
.last-message {
flex: 1;
font-size: 13px;
color: #64748b;
white-space: nowrap;
@@ -244,9 +251,10 @@ $brand-color-hover: #004488;
}
.unread-badge {
position: absolute;
top: 10px;
right: 10px;
flex-shrink: 0;
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 18px;
height: 18px;
padding: 0 6px;
@@ -255,9 +263,6 @@ $brand-color-hover: #004488;
border-radius: 9px;
font-size: 11px;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
}
}

View File

@@ -68,13 +68,15 @@
<div class="room-name">{{ room.roomName }}</div>
<div class="room-time">{{ formatTime(room.lastMessageTime) }}</div>
</div>
<div class="last-message">{{ room.lastMessage || '暂无消息' }}</div>
<div class="last-message-row">
<div class="last-message">{{ room.lastMessage || '暂无消息' }}</div>
<!-- 未读红点 -->
<div v-if="(room.unreadCount ?? 0) > 0" class="unread-badge">
{{ (room.unreadCount ?? 0) > 99 ? '99+' : room.unreadCount }}
</div>
</div>
</div>
<!-- 未读红点 -->
<div v-if="(room.unreadCount ?? 0) > 0" class="unread-badge">
{{ (room.unreadCount ?? 0) > 99 ? '99+' : room.unreadCount }}
</div>
</div>
</div>
</div>
@@ -86,7 +88,7 @@
<ChatRoom
ref="chatRoomRef"
:messages="messages"
:current-user-id="userId"
:current-user-id="loginDomain.user.userId"
:room-name="currentRoom?.roomName"
:meeting-url="currentMeetingUrl"
:show-meeting="showMeetingIframe"
@@ -146,7 +148,6 @@
</ElDialog>
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted, watch, nextTick } from 'vue'
import { ElButton, ElInput, ElDialog, ElMessage } from 'element-plus'
@@ -156,7 +157,7 @@ import WorkcaseDetail from '@/views/public/workcase/WorkcaseDetail/WorkcaseDetai
import { workcaseChatAPI } from '@/api/workcase'
import { fileAPI } from 'shared/api/file'
import { FILE_DOWNLOAD_URL } from '@/config'
import type { ChatRoomVO, ChatRoomMessageVO, TbChatRoomMessageDTO } from '@/types/workcase'
import type { ChatRoomVO, ChatRoomMessageVO, TbChatRoomMessageDTO, TbChatRoomMemberDTO } from '@/types/workcase'
import SockJS from 'sockjs-client'
import { Client } from '@stomp/stompjs'
@@ -175,7 +176,7 @@ let roomSubscription: any = null
let listSubscription: any = null
// 当前用户ID从登录状态获取
const userId = ref(localStorage.getItem('userId') || '')
const loginDomain = JSON.parse(localStorage.getItem('loginDomain')!)
// 侧边栏展开状态
const isSidebarOpen = ref(false)
@@ -243,7 +244,7 @@ const fetchChatRooms = async () => {
const result = await workcaseChatAPI.getChatRoomPage({
filter: { status: 'active' },
pageParam: { page: 1, pageSize: 100, total: 0 }
})
}, loginDomain.user.userId)
if (result.success && result.pageDomain) {
chatRooms.value = result.pageDomain.dataList || []
}
@@ -257,8 +258,23 @@ const fetchChatRooms = async () => {
}
// 选择聊天室
const selectRoom = (roomId: string) => {
const selectRoom = async (roomId: string) => {
currentRoomId.value = roomId
// 自动加入聊天室成员表(如果不存在)
try {
const memberData: TbChatRoomMemberDTO = {
roomId: roomId,
userId: loginDomain.user.userId,
userName: loginDomain.userInfo.username,
userType: 'staff'
}
await workcaseChatAPI.addChatRoomMember(memberData)
} catch (error) {
// 已存在成员或其他错误,忽略
console.debug('加入聊天室:', error)
}
loadMessages(roomId)
}
@@ -341,7 +357,8 @@ const handleSendMessage = async (content: string, files: File[]) => {
// 构造消息
const messageData: TbChatRoomMessageDTO = {
roomId: currentRoomId.value,
senderId: userId.value,
senderId: loginDomain.user.userId,
senderName: loginDomain.userInfo.username,
senderType: 'agent',
content,
files: fileIds,
@@ -443,20 +460,40 @@ const initWebSocket = () => {
stompClient.activate()
}
// 订阅聊天室列表更新 (用于更新列表中的lastMessage)
// 订阅聊天室列表更新 (用于更新列表中的lastMessage和未读数)
const subscribeToListUpdate = () => {
if (!stompClient || !stompClient.connected) return
listSubscription = stompClient.subscribe('/topic/chat/list-update', (message: any) => {
listSubscription = stompClient.subscribe('/topic/chat/list-update', async (message: any) => {
const chatMessage = JSON.parse(message.body)
// 更新对应聊天室的lastMessage和lastMessageTime
const roomIndex = chatRooms.value.findIndex((r: ChatRoomVO) => r.roomId === chatMessage.roomId)
if (roomIndex !== -1) {
// 查询当前用户在该聊天室的未读数
let unreadCount = 0
try {
const unreadResult = await workcaseChatAPI.getUnreadCount(
chatMessage.roomId,
loginDomain.user.userId
)
if (unreadResult.success && unreadResult.data !== undefined) {
unreadCount = unreadResult.data
}
} catch (error) {
console.error('查询未读数失败:', error)
}
chatRooms.value[roomIndex] = {
...chatRooms.value[roomIndex],
lastMessage: chatMessage.content,
lastMessageTime: chatMessage.sendTime
lastMessageTime: chatMessage.sendTime,
unreadCount: unreadCount
}
// 将更新的聊天室移到列表顶部
const updatedRoom = chatRooms.value[roomIndex]
chatRooms.value.splice(roomIndex, 1)
chatRooms.value.unshift(updatedRoom)
}
})
}
@@ -474,7 +511,7 @@ const subscribeToRoom = (roomId: string) => {
roomSubscription = stompClient.subscribe(`/topic/chat/${roomId}`, (message: any) => {
const chatMessage = JSON.parse(message.body) as ChatRoomMessageVO
// 避免重复添加自己发送的消息
if (chatMessage.senderId !== userId.value) {
if (chatMessage.senderId !== loginDomain.user.userId) {
messages.value.push(chatMessage)
scrollToBottom()
}