From aa8c1737ad0aece9700205116ebecfe6c91bc567 Mon Sep 17 00:00:00 2001 From: zoujing Date: Wed, 21 Jan 2026 21:21:30 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A4=84=E7=90=86=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E5=8F=91=E9=80=81=E4=B9=8B=E5=90=8E=E7=9A=84=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E6=BB=9A=E5=8A=A8=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/renderer/views/home/ChatBox.vue | 49 ++++++++++++++++++++++------- 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/src/renderer/views/home/ChatBox.vue b/src/renderer/views/home/ChatBox.vue index 9c9a3b3..1338c7e 100644 --- a/src/renderer/views/home/ChatBox.vue +++ b/src/renderer/views/home/ChatBox.vue @@ -83,8 +83,8 @@ import userAvatar from '@assets/images/login/user_icon.png'; import aiAvatar from '@assets/images/login/blue_logo.png'; -///(控制滚动位置) -const scrollTop = ref(99999); +// 列表滚动容器引用 +const listRef = ref(null); /// 会话列表 const chatMsgList = ref([]); @@ -93,8 +93,8 @@ const inputMessage = ref(""); /// 发送消息中标志 const isSendingMessage = ref(false); -/// agentId 首页接口中获取 -const agentId = ref("1953462165250859010"); +/// agentId 首页接口中获取 1953462165250859010 +const agentId = ref("1"); /// 会话ID 历史数据接口中获取 const conversationId = ref(""); // 会话进行中标志 @@ -123,15 +123,39 @@ const sleep = (ms: number) => new Promise((res) => setTimeout(res, ms)); // 当前会话的消息ID,用于保持发送和终止的messageId一致 let currentSessionMessageId: string | null = null; -// 滚动到底部 - 优化版本,确保打字机效果始终可见 -const scrollToBottom = () => { +// 滚动到底部 - 精确计算 last 元素位置并使用双 rAF 保证布局稳定 +const scrollToBottom = (smooth = false) => { nextTick(() => { - // 使用更大的值确保滚动到真正的底部 - scrollTop.value = 99999; - // 强制触发滚动更新,增加延迟确保DOM更新完成 - setTimeout(() => { - scrollTop.value = scrollTop.value + Math.random(); - }, 10); + const el = listRef.value; + if (!el) return; + + const doScroll = () => { + const last = el.lastElementChild as HTMLElement | null; + // 计算容器 style padding-bottom,保证滚动到真正可视底部 + const style = window.getComputedStyle(el); + const paddingBottom = parseFloat(style.paddingBottom || '0') || 0; + + if (last) { + const lastOffset = last.offsetTop + last.offsetHeight; + const target = lastOffset + paddingBottom - el.clientHeight; + const top = Math.max(0, Math.ceil(target)); + if (smooth && typeof el.scrollTo === 'function') { + el.scrollTo({ top, behavior: 'smooth' }); + } else { + el.scrollTop = top; + } + return; + } + + // 兜底:滚到底 + if (smooth && typeof el.scrollTo === 'function') { + el.scrollTo({ top: el.scrollHeight, behavior: 'smooth' }); + } else { + el.scrollTop = el.scrollHeight; + } + }; + // 使用两次 requestAnimationFrame 增强在复杂渲染/打字机更新场景的可靠性 + requestAnimationFrame(() => requestAnimationFrame(doScroll)); }); }; @@ -388,6 +412,7 @@ const handleWebSocketMessage = (data: any) => { isSessionActive.value = false; // 清理当前会话的 messageId,避免保留陈旧 id resetMessageState(); + setTimeoutScrollToBottom(); } };