From 523f8c4cc880f4769c2956f41fb8a97c3f1ca80b Mon Sep 17 00:00:00 2001 From: jxxghp Date: Fri, 15 May 2026 17:47:50 +0800 Subject: [PATCH] feat: add force scroll option to intelligent message scrolling in ShortcutBar and MessageView --- src/layouts/components/ShortcutBar.vue | 13 ++++++++-- src/views/system/MessageView.vue | 33 +++++++++++++++----------- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/src/layouts/components/ShortcutBar.vue b/src/layouts/components/ShortcutBar.vue index 4893715b..5131c283 100644 --- a/src/layouts/components/ShortcutBar.vue +++ b/src/layouts/components/ShortcutBar.vue @@ -11,6 +11,10 @@ type MessageViewExpose = { refreshLatestMessages?: () => Promise | void } +type MessageScrollPayload = { + force?: boolean +} + // 国际化 const { t } = useI18n() @@ -165,14 +169,19 @@ async function openMessageDialog() { }) } -// 智能滚动到底部(只有用户在底部附近时才滚动) -function scrollMessageToEnd() { +// 智能滚动到底部:首次打开时允许强制滚动,后续实时消息尊重用户当前位置。 +function scrollMessageToEnd(payload?: MessageScrollPayload) { // 使用更长的延迟确保DOM已更新 setTimeout(() => { try { // 查找消息弹窗的滚动容器 const cardText = document.querySelector('.v-dialog .v-card-text') if (cardText) { + if (payload?.force) { + cardText.scrollTop = cardText.scrollHeight + return + } + const { scrollTop, scrollHeight, clientHeight } = cardText // 计算距离底部的距离 const distanceFromBottom = scrollHeight - scrollTop - clientHeight diff --git a/src/views/system/MessageView.vue b/src/views/system/MessageView.vue index d26171b5..13c42fae 100644 --- a/src/views/system/MessageView.vue +++ b/src/views/system/MessageView.vue @@ -9,8 +9,14 @@ import { useBackgroundOptimization } from '@/composables/useBackgroundOptimizati const { t } = useI18n() const { useSSE } = useBackgroundOptimization() +type ScrollPayload = { + force?: boolean +} + // 定义事件 -const emit = defineEmits(['scroll']) +const emit = defineEmits<{ + (e: 'scroll', payload?: ScrollPayload): void +}>() // 消息列表 const messages = ref([]) @@ -66,6 +72,13 @@ function updateLastTime(message: Message) { } } +// 请求父组件滚动,首屏历史消息需要强制到底,实时消息继续使用智能滚动。 +function requestScrollToEnd(force = false) { + nextTick(() => { + emit('scroll', { force }) + }) +} + // 合并消息到当前列表 function mergeMessages(items: Message[]) { let hasNewMessage = false @@ -95,9 +108,7 @@ function handleSSEMessage(event: MessageEvent) { if (message) { const object = JSON.parse(message) if (mergeMessages([object])) { - nextTick(() => { - emit('scroll') // 新消息到达时触发智能滚动 - }) + requestScrollToEnd() // 新消息到达时触发智能滚动 } } } @@ -137,9 +148,7 @@ async function loadMessages({ done }: { done: any }) { // 首次加载时滚动到底部 if (page.value === 1 && hasNewMessage) { - nextTick(() => { - emit('scroll') - }) + requestScrollToEnd(true) } // 页码+1 page.value++ @@ -168,9 +177,7 @@ async function refreshLatestMessages() { })) as Message[] if (mergeMessages(latestMessages)) { - nextTick(() => { - emit('scroll') - }) + requestScrollToEnd() } } catch (error) { console.error('刷新最新消息失败:', error) @@ -206,7 +213,7 @@ function compareTime(time1: string, time2: string) { // 图片加载完成时触发智能滚动 function handleImageLoad() { - emit('scroll') + requestScrollToEnd() } // 暂停SSE连接 @@ -236,9 +243,7 @@ defineExpose({ onMounted(() => { // 组件挂载后触发一次滚动事件 - nextTick(() => { - emit('scroll') - }) + requestScrollToEnd() })