From 8abcd567a6229d876531036a745b68ae7df06dbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BB=9D=E5=B0=98?= <237809796@qq.com> Date: Wed, 17 Jun 2026 23:33:28 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D:=20=E8=99=9A=E6=8B=9F?= =?UTF-8?q?=E6=BB=9A=E5=8A=A8=20IO=20=E5=8D=B8=E8=BD=BD=E5=88=86=E6=94=AF?= =?UTF-8?q?=20height=3D0=20=E6=97=B6=20minHeight=20fallback=20=E9=98=B2=20?= =?UTF-8?q?slot=20=E5=A1=8C=200=20=E9=87=8D=E5=8F=A0(P0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/composables/ai/useAiVirtualScroll.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/composables/ai/useAiVirtualScroll.ts b/src/composables/ai/useAiVirtualScroll.ts index 54f3601..1610d62 100644 --- a/src/composables/ai/useAiVirtualScroll.ts +++ b/src/composables/ai/useAiVirtualScroll.ts @@ -69,9 +69,14 @@ export function useAiVirtualScroll(options: AiVirtualScrollOptions) { // 卸载前:把实测高度锁到 sentinel 的 minHeight,防内容移除后 sentinel 塌成 0 // → scrollHeight 不变 → isNearBottom/scrollToBottom/回到底部计算零感知。 // 内容重新挂载时 RO 会把真实高度回填,registerSentinel 复挂时清 minHeight 由真实高度接管。 + // 防御(P0):RO 首次回调前 item 已被 IO 判不可见时 meta.height===0,直接不设 minHeight + // → sentinel 塌 0 高 → 下条消息上浮重叠。此处兜底:height>0 用实测值, + // height===0 用 el.offsetWidth 量得的实际渲染高度(此时 el 仍在 DOM),仍<=0 则用固定 40px。 const meta = itemMeta.get(key) - if (meta && meta.height > 0 && meta.el.isConnected) { - meta.el.style.minHeight = `${meta.height}px` + if (meta && meta.el.isConnected) { + const measured = meta.height > 0 ? meta.height : (meta.el.offsetHeight || 0) + const placeholder = measured > 0 ? measured : 40 + meta.el.style.minHeight = `${placeholder}px` } next.delete(key); changed = true } @@ -98,9 +103,12 @@ export function useAiVirtualScroll(options: AiVirtualScrollOptions) { const prev = itemMeta.get(key) if (el instanceof HTMLElement) { el.dataset.vscrollKey = key - // 复用上次实测高度作为初始 inline 占位(再次挂载时先占位,内容挂载后 RO 再校准) + // 复用上次实测高度作为初始 inline 占位(再次挂载时先占位,内容挂载后 RO 再校准)。 + // 防御(P0):首次挂载(无 prev / RO 未回填)h<=0 时若不设占位,sentinel 首帧塌 0 高 → + // 列表首次渲染时下条上浮重叠。此处用保守默认 40px 占位,RO 回调后用真实高度覆盖。 const h = prev?.height ?? 0 - if (h > 0) el.style.minHeight = `${h}px` + const placeholder = h > 0 ? h : 40 + el.style.minHeight = `${placeholder}px` itemMeta.set(key, { el, height: h }) ensureObserver() io?.observe(el)