diff --git a/src/components/InstantMessage.vue b/src/components/InstantMessage.vue index 49bbfa9..e8707d2 100644 --- a/src/components/InstantMessage.vue +++ b/src/components/InstantMessage.vue @@ -565,6 +565,10 @@ const selectedConversation = ref(null) const messageInput = ref('') const showEmojiPicker = ref(false) +// 滚动状态 +const isUserScrolling = ref(false) +const shouldAutoScroll = ref(true) + // 表情列表 const emojiList = ref([ '😀', '😃', '😄', '😁', '😆', '😅', '😂', '🤣', '😊', '😇', @@ -1284,9 +1288,9 @@ const selectConversation = async (conversation: Conversation) => { await loadGroupMembers(String(conversation.id)) } - // 滚动到底部 + // 滚动到底部(强制滚动) nextTick(() => { - scrollToBottom() + scrollToBottom(true) // 更新最后读取消息ID const latestMessageId = getLatestMessageId() @@ -1635,14 +1639,14 @@ const sendImageMessage = async (imageData: { file: File; url: string; name: stri // 更新对话列表中的最后消息 updateConversationLastMessage(String(selectedConversation.value!.id), newMessage) - // 滚动到底部 + // 滚动到底部(强制滚动) nextTick(() => { - scrollToBottom() + scrollToBottom(true) }) // 确保消息渲染完成后再滚动一次 setTimeout(() => { - scrollToBottom() + scrollToBottom(true) }, 200) // 更新最后读取消息ID @@ -1770,9 +1774,9 @@ const sendMessage = async () => { // 同时更新对话列表中的最后消息 updateConversationLastMessage(String(selectedConversation.value.id), newMessage) - // 滚动到底部 + // 滚动到底部(强制滚动) nextTick(() => { - scrollToBottom() + scrollToBottom(true) resetTextareaHeight() // 使用服务器返回的真实ID更新最后读取消息ID @@ -1841,8 +1845,8 @@ const shouldShowViewMore = computed(() => { }) // 滚动到底部 -const scrollToBottom = () => { - if (messagesContainer.value) { +const scrollToBottom = (force = false) => { + if (messagesContainer.value && (shouldAutoScroll.value || force)) { // 使用setTimeout确保DOM更新完成 setTimeout(() => { if (messagesContainer.value) { @@ -2034,10 +2038,21 @@ const getLatestMessageId = (): string | null => { // 防抖定时器 let scrollDebounceTimer: NodeJS.Timeout | null = null +// 检查是否在底部 +const isAtBottom = () => { + if (!messagesContainer.value) return true + const { scrollTop, scrollHeight, clientHeight } = messagesContainer.value + return scrollHeight - scrollTop - clientHeight < 50 // 50px的容差 +} + // 处理消息容器滚动事件 const handleMessagesScroll = () => { if (!messagesContainer.value || !selectedConversation.value) return + const atBottom = isAtBottom() + shouldAutoScroll.value = atBottom + isUserScrolling.value = !atBottom + // 滚动时不更新最后读取消息ID,因为使用的是历史消息ID // 只在消息加载完成和发送新消息时更新 } @@ -2239,7 +2254,7 @@ const pollForNewMessages = async () => { // 只添加新消息,而不是重新加载所有消息 await addNewMessages(messages) - // 滚动到底部显示新消息 + // 只有在用户没有手动滚动时才滚动到底部显示新消息 nextTick(() => { scrollToBottom() })