From 3585967b405703bca260c657d853d775d54bfb04 Mon Sep 17 00:00:00 2001 From: JOJO <1498581755@qq.com> Date: Sat, 11 Jul 2026 20:27:07 +0800 Subject: [PATCH] =?UTF-8?q?fix(ui):=20=E6=9E=81=E7=AE=80=E6=A8=A1=E5=BC=8F?= =?UTF-8?q?=E5=B1=95=E5=BC=80=E5=8C=BA=E8=BF=BD=E5=BA=95=E6=94=B9=E4=B8=BA?= =?UTF-8?q?stick-to-bottom=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/src/components/chat/MinimalBlocks.vue | 30 ++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/static/src/components/chat/MinimalBlocks.vue b/static/src/components/chat/MinimalBlocks.vue index df3b2f7..7bf9665 100644 --- a/static/src/components/chat/MinimalBlocks.vue +++ b/static/src/components/chat/MinimalBlocks.vue @@ -216,7 +216,9 @@ const emit = defineEmits<{ const expandedGroups = ref(new Set()); const thinkingRefs = new Map(); const stepsWrapperRefs = new Map(); +const stepsWrapperScrollLocks = new Map(); const summaryLoaders = new Map(); +const STEPS_WRAPPER_BOTTOM_THRESHOLD = 20; const TOOL_REEL_ITEM_HEIGHT = 26; const TOOL_REEL_INTERVAL_MS = 1450; const TOOL_REEL_ROLL_MS = 520; @@ -811,17 +813,35 @@ const getSummarySteps = (actions: Action[]) => { })); }; +const isStepsWrapperNearBottom = (wrapper: HTMLElement) => { + const distance = wrapper.scrollHeight - wrapper.scrollTop - wrapper.clientHeight; + return distance <= STEPS_WRAPPER_BOTTOM_THRESHOLD; +}; + +const handleStepsWrapperScroll = (groupId: string, event: Event) => { + const wrapper = event.target as HTMLElement; + if (!wrapper) return; + const escaped = !isStepsWrapperNearBottom(wrapper); + stepsWrapperScrollLocks.set(groupId, escaped); +}; + const registerStepsWrapper = (groupId: string, el: Element | null) => { + const prev = stepsWrapperRefs.get(groupId); + if (prev) { + prev.removeEventListener('scroll', handleStepsWrapperScroll as EventListener); + } if (el instanceof HTMLElement) { stepsWrapperRefs.set(groupId, el); + el.addEventListener('scroll', (event) => handleStepsWrapperScroll(groupId, event)); } else { stepsWrapperRefs.delete(groupId); + stepsWrapperScrollLocks.delete(groupId); } }; const scrollStepsWrapperToBottom = (groupId: string) => { const wrapper = stepsWrapperRefs.get(groupId); - if (wrapper) { + if (wrapper && !stepsWrapperScrollLocks.get(groupId)) { wrapper.scrollTop = wrapper.scrollHeight; } }; @@ -853,6 +873,8 @@ const toggleExpand = (groupId: string) => { nextTick(() => { const group = blockGroups.value.find((g) => g.id === groupId); if (group && group.actions) { + // 展开时重置锁定:用户新展开的内容默认跟随到底部 + stepsWrapperScrollLocks.set(groupId, false); // 运行中展开时,将展开区滚动到底部 if (isSummaryRunning(group.actions, groupId)) { scrollStepsWrapperToBottom(groupId); @@ -980,7 +1002,7 @@ watch( () => props.actions, () => { syncToolReels(); - // 对展开的 running 摘要组,自动将展开区滚动到底部 + // 对展开的 running 摘要组,若用户未主动向上滚动,自动将展开区滚动到底部 nextTick(() => { blockGroups.value.forEach((group) => { if ( @@ -1006,7 +1028,11 @@ watch( onBeforeUnmount(() => { Object.keys(toolReelStates).forEach(clearToolReelTimers); + stepsWrapperRefs.forEach((el) => { + el.removeEventListener('scroll', handleStepsWrapperScroll as EventListener); + }); stepsWrapperRefs.clear(); + stepsWrapperScrollLocks.clear(); });