增加历史记录功能
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
|
||||
export const getMessages = (params) => request.get('/messages', { params })
|
||||
|
||||
export const getHistoryMessages = (params) => request.get('/messages/history', { params })
|
||||
|
||||
export const sendMessage = (data) => request.post('/messages', data)
|
||||
|
||||
export const getUnreadCount = () => request.get('/messages/unreadCount')
|
||||
|
||||
@@ -78,7 +78,12 @@
|
||||
</div>
|
||||
|
||||
<!-- 消息列表 -->
|
||||
<div class="chat-messages" ref="messagesRef">
|
||||
<div class="chat-messages" ref="messagesRef" @scroll="handleScroll">
|
||||
<div class="history-loader" v-if="hasMoreHistory" @click="loadMoreHistory">
|
||||
<el-button link type="primary" :loading="loadingHistory">
|
||||
加载更多消息
|
||||
</el-button>
|
||||
</div>
|
||||
<div v-if="loadingMessages" class="messages-loading">
|
||||
<el-icon class="is-loading"><Loading /></el-icon> 加载中..
|
||||
</div>
|
||||
@@ -227,7 +232,7 @@ import { ref, computed, onMounted, onUnmounted, nextTick } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { ChatLineRound, User, Search, Loading, Picture, Paperclip, Download, ChatDotRound, Promotion, Delete } from '@element-plus/icons-vue'
|
||||
import { useUserStore } from '@/store/user'
|
||||
import { getUsers, getMessages, sendMessage as sendMessageApi, uploadChatFile, getUnreadList, deleteConversation } from '@/api/message'
|
||||
import { getUsers, getMessages, getHistoryMessages, sendMessage as sendMessageApi, uploadChatFile, getUnreadList, deleteConversation } from '@/api/message'
|
||||
import { chatService } from '@/services/chat'
|
||||
|
||||
const props = defineProps({ modelValue: Boolean })
|
||||
@@ -250,6 +255,8 @@ const currentUserNickname = ref('')
|
||||
const currentContact = ref(null)
|
||||
const inputText = ref('')
|
||||
const loadingMessages = ref(false)
|
||||
const loadingHistory = ref(false)
|
||||
const hasMoreHistory = ref(false)
|
||||
const emojiVisible = ref(false)
|
||||
const messagesRef = ref(null)
|
||||
const imageInputRef = ref(null)
|
||||
@@ -380,10 +387,11 @@ const loadUnreadChats = async () => {
|
||||
const selectContact = async (contact) => {
|
||||
currentContact.value = contact
|
||||
contact.unread = 0
|
||||
hasMoreHistory.value = false
|
||||
loadingMessages.value = true
|
||||
try {
|
||||
const res = await getMessages({ userId: contact.id })
|
||||
messages.value[contact.id] = (res.data || []).map(msg => {
|
||||
const list = (res.data || []).map(msg => {
|
||||
const isSelf = String(msg.fromUserId) === String(userStore.userId)
|
||||
return {
|
||||
...msg,
|
||||
@@ -393,6 +401,9 @@ const selectContact = async (contact) => {
|
||||
fromColor: colors[Math.abs(Number(isSelf ? userStore.userId : msg.fromUserId) % colors.length)]
|
||||
}
|
||||
})
|
||||
messages.value[contact.id] = list
|
||||
// 如果消息条数 >= 20,可能还有更早的历史
|
||||
hasMoreHistory.value = list.length >= 20
|
||||
updateRecentChat(contact, '')
|
||||
} catch (e) {
|
||||
ElMessage.error('加载消息失败')
|
||||
@@ -402,6 +413,51 @@ const selectContact = async (contact) => {
|
||||
}
|
||||
}
|
||||
|
||||
const loadMoreHistory = async () => {
|
||||
if (!currentContact.value || loadingHistory.value || !hasMoreHistory.value) return
|
||||
const list = messages.value[currentContact.value.id]
|
||||
if (!list || list.length === 0) return
|
||||
|
||||
loadingHistory.value = true
|
||||
// 记录当前滚动高度,加载后恢复
|
||||
const prevScrollHeight = messagesRef.value?.scrollHeight || 0
|
||||
try {
|
||||
const firstMsg = list[0]
|
||||
const beforeTime = firstMsg.createTime || firstMsg.id
|
||||
const res = await getHistoryMessages({ userId: currentContact.value.id, beforeTime, limit: 20 })
|
||||
const historyList = (res.data || []).map(msg => {
|
||||
const isSelf = String(msg.fromUserId) === String(userStore.userId)
|
||||
return {
|
||||
...msg,
|
||||
isSelf,
|
||||
sending: false,
|
||||
failed: false,
|
||||
fromColor: colors[Math.abs(Number(isSelf ? userStore.userId : msg.fromUserId) % colors.length)]
|
||||
}
|
||||
})
|
||||
|
||||
if (historyList.length === 0) {
|
||||
hasMoreHistory.value = false
|
||||
} else {
|
||||
// 插入到消息列表前面
|
||||
messages.value[currentContact.value.id] = [...historyList, ...list]
|
||||
// 如果返回条数少于 limit,没有更多了
|
||||
hasMoreHistory.value = historyList.length >= 20
|
||||
// 恢复滚动位置
|
||||
nextTick(() => {
|
||||
if (messagesRef.value) {
|
||||
const newScrollHeight = messagesRef.value.scrollHeight
|
||||
messagesRef.value.scrollTop = newScrollHeight - prevScrollHeight
|
||||
}
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error('加载历史消息失败')
|
||||
} finally {
|
||||
loadingHistory.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const updateRecentChat = (contact, lastMsg) => {
|
||||
let chat = recentChats.value.find(c => c.id === contact.id)
|
||||
if (!chat) {
|
||||
@@ -600,6 +656,16 @@ const scrollToBottom = () => {
|
||||
})
|
||||
}
|
||||
|
||||
// 滚动到顶部时加载更多历史消息
|
||||
const handleScroll = () => {
|
||||
if (!messagesRef.value || !currentContact.value) return
|
||||
const { scrollTop } = messagesRef.value
|
||||
// 滚动到顶部 50px 以内时触发
|
||||
if (scrollTop < 50 && hasMoreHistory.value && !loadingHistory.value) {
|
||||
loadMoreHistory()
|
||||
}
|
||||
}
|
||||
|
||||
// ======== WebSocket ========
|
||||
|
||||
const onDialogOpen = () => {
|
||||
@@ -683,7 +749,7 @@ onUnmounted(() => { if (unsubscribeWs) unsubscribeWs() })
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.chat-container { display: flex; height: 600px; gap: 12px; }
|
||||
.chat-container { display: flex; height: calc(70vh - 120px); gap: 12px; }
|
||||
.chat-sidebar { width: 280px; display: flex; flex-direction: column; border-right: 1px solid #e4e7ed; }
|
||||
.sidebar-tabs { display: flex; border-bottom: 1px solid #e4e7ed; }
|
||||
.sidebar-tab { flex: 1; padding: 12px; text-align: center; cursor: pointer; border-bottom: 2px solid transparent; transition: all 0.3s; display: flex; align-items: center; justify-content: center; gap: 6px; font-size: 13px; }
|
||||
@@ -782,5 +848,17 @@ onUnmounted(() => { if (unsubscribeWs) unsubscribeWs() })
|
||||
.chat-empty { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; color: #909399; gap: 12px; }
|
||||
.chat-empty p { margin: 0; font-size: 14px; }
|
||||
|
||||
.chat-dialog :deep(.el-dialog) { height: 720px; margin: auto !important; top: 50% !important; transform: translateY(-50%) !important; }
|
||||
.history-loader {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.chat-dialog :deep(.el-dialog) {
|
||||
height: 70vh;
|
||||
margin: auto !important;
|
||||
top: 50% !important;
|
||||
transform: translateY(-50%) !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user