From 3af1a19dce880c23003a5b018d22d06609dd09e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B4=AE=E7=94=9F=EF=BC=88=E5=AD=90=E8=99=9A=EF=BC=89?= <2234839456@qq.com> Date: Thu, 30 Jul 2026 17:33:20 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20StatsPanel=20=E5=8D=A1=E7=89=87=E4=B8=8D?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=20=E2=80=94=20IntersectionObserver=20?= =?UTF-8?q?=E6=AD=BB=E9=94=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 根因: v-if=data 和 ref=rootRef 在同一元素,data 为 null 时 元素不存在 → observer 无法 observe → data 永远不会加载。 修复: initialLoad() 在 onMounted 时立即调用,不依赖 observer。 IntersectionObserver 仅负责后续轮询的暂停/恢复。 同时修复 formatUptime 中文分支的秒数计算 bug。 --- src/StatsPanel.vue | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/StatsPanel.vue b/src/StatsPanel.vue index d69a2c7..3ebb569 100644 --- a/src/StatsPanel.vue +++ b/src/StatsPanel.vue @@ -18,7 +18,7 @@ function formatUptime(seconds: number): string { if (seconds < 3600) return `${Math.floor(seconds / 60)}分${seconds % 60}秒`; const h = Math.floor(seconds / 3600); const m = Math.floor((seconds % 3600) / 60); - const s = seconds % 3600 % 60; + const s = seconds % 60; if (h < 24) return `${h}时${m}分${s}秒`; const d = Math.floor(h / 24); return `${d}天${h % 24}时${m}分`; @@ -36,6 +36,15 @@ let inViewport = false; /** 轮询周期(毫秒),与进度条 animation-duration 保持一致 */ const POLL_INTERVAL = 10_000; +/** 首次加载数据(不受视口/可见性限制,mount 时立即调用) */ +async function initialLoad() { + const s = await fetchStats().catch(() => null); + if (s) { + data.value = s; + progressKey.value++; + } +} + async function load() { const s = await fetchStats().catch(() => null); if (s) { @@ -73,6 +82,9 @@ function onVisibilityChange() { let intersectionObserver: IntersectionObserver | null = null; onMounted(() => { + /** 立即首次加载,确保卡片尽快出现 */ + initialLoad(); + /** 监听页面标签可见性 */ document.addEventListener("visibilitychange", onVisibilityChange); @@ -99,7 +111,8 @@ onUnmounted(() => {