From b0e12f14fe35cedb6b956379a23d06da32e047a4 Mon Sep 17 00:00:00 2001 From: laoyang Date: Fri, 6 Nov 2026 10:02:45 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A6=96=E9=A1=B5/=E6=97=A5=E7=A8=8B=E9=A1=B5?= =?UTF-8?q?=EF=BC=9A=E5=BD=93=E5=89=8D=E6=97=B6=E9=97=B4=E7=BA=A2=E7=BA=BF?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E5=AE=9A=E6=97=B6=E5=99=A8=E9=A9=B1=E5=8A=A8?= =?UTF-8?q?=EF=BC=8C=E4=B8=8D=E5=86=8D=E5=86=BB=E7=BB=93=E5=9C=A8=E5=86=B7?= =?UTF-8?q?=E5=90=AF=E5=8A=A8=E6=97=B6=E5=88=BB=20(#81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 用户:首页今天的日程,那个红线,不会动态更新,只有刚打开应用的时候更新, 如果不清后台,就一直不动了。查一下这个更新机制,需要能够动态更新。 根因:nowMin: this.nowMinutes() 是**在 build 里直接调方法** —— ArkUI 不会因为时间流逝重算任何东西,那个方法只在首次 build 时求值一次, 红线永远停在打开 App 那一刻。与 pitfalls 里 ForEach(this.xxx(), ...) 追踪不到 是同一条规则:build 里只有字段(@Local/@Param)建依赖,方法调用不建。 改法:@Local nowClockMin + setInterval 30 秒一跳改它;传参改成读该字段。 - 30 秒而非 1 分钟:1 分钟在整分附近显得迟一拍,30 秒只改一个 number,成本极低; - 先立即刷一次,否则首屏红线停在默认 0(=00:00,跑到视窗外面); - aboutToAppear 起、aboutToDisappear 停 —— 首页切模块走 if/else 分发, 组件会销毁重建,成对钩子必然触发; - 绝不在 build() 里起定时器(build 可能多次调用,定时器会越叠越多)。 顺带修 FbCalendarPage 完全相同的两处(月视图 + 议程视图)—— 用户从首页切过去看到同样的死线。 验证(真机 5NC0225B27004718,红线 #FF3B30 像素扫描): 09:57:22 红线 y=1359.0,10:00:24 y=1366.5(下移 7.5px, 理论 3.03 分钟 = 6.9px,吻合); 切到别的模块再切回后仍在走:1368.5 → 90 秒后 1372.5(4px≈1.75 分钟,吻合), 说明 aboutToDisappear 清掉旧定时器、aboutToAppear 起了新的,无泄漏无停摆。 hvigorw assembleHap BUILD SUCCESSFUL。 --- .../ets/pages/calendar/FbCalendarPage.ets | 44 ++++++++++++- entry/src/main/ets/pages/home/FbHomePage.ets | 66 ++++++++++++++++++- 2 files changed, 107 insertions(+), 3 deletions(-) diff --git a/entry/src/main/ets/pages/calendar/FbCalendarPage.ets b/entry/src/main/ets/pages/calendar/FbCalendarPage.ets index c870c80..5be3675 100644 --- a/entry/src/main/ets/pages/calendar/FbCalendarPage.ets +++ b/entry/src/main/ets/pages/calendar/FbCalendarPage.ets @@ -205,6 +205,16 @@ export struct FbCalendarPage { @Local showMonth: number = new Date().getMonth() + 1; /** 选中的日期 `YYYY-MM-DD` */ @Local pickedIso: string = ''; + /** + * 当前时刻(当天第几分钟)—— 驱动时间轴的**当前时间红线**。 + * + * 🔴 2026-11-06:原来是 `nowMin: this.nowMinutes()`(build 里调方法), + * 不建立响应依赖 ⇒ 红线停在进页面那一刻。改成 @Local + 定时器。 + * 与首页 `FbHomePage.nowClockMin` 完全同一套做法。 + */ + @Local nowClockMin: number = 0; + /** 定时器句柄(0 = 没在跑) */ + private nowTimer: number = 0; // ---- 浮层状态 ---- /** 详情:正在看的条目(列表里那份) */ @@ -339,6 +349,8 @@ export struct FbCalendarPage { */ aboutToDisappear(): void { FB_BACK.unregister('calendar'); + /* 🔴 2026-11-06:停掉当前时间刷新(见 `startNowTicker()`)。 */ + this.stopNowTicker(); } private backOnce(): boolean { @@ -376,6 +388,11 @@ export struct FbCalendarPage { this.applyCardJump(); this.load(); this.loadBooks(); + /* 🔴 2026-11-06:当前时间红线要自己走。 + 用户报的是首页,但这里**完全同一个毛病** —— `nowMin: this.nowMinutes()` + 在 build 里调方法,不建立响应依赖 ⇒ 红线停在进页面那一刻。 + 既然修了就一起修,否则从首页切到日程页还是同一条死线。 */ + this.startNowTicker(); } /** @@ -1521,7 +1538,8 @@ export struct FbCalendarPage { events: this.eventsOf(this.pickedIso), style: this.style, isToday: this.pickedIso === this.todayIso(), - nowMin: this.nowMinutes(), + /* 🔴 2026-11-06:读 @Local(定时器驱动),不要调方法(不建立依赖) */ + nowMin: this.nowClockMin, onPick: (e: FbCalEvent): void => { this.openEvent(e); } @@ -1534,6 +1552,28 @@ export struct FbCalendarPage { return d.getHours() * 60 + d.getMinutes(); } + /** + * 启动「当前时间」刷新(与首页 `FbHomePage.startNowTicker` 同一套)。 + * + * · 30 秒一跳:红线是"现在"的指示,1 分钟一跳在整分附近会显得迟一拍; + * · 先立即刷一次,避免首屏红线停在默认值 0(= 00:00,跑到视窗外面); + * · 只在页面存在期间跑,`aboutToDisappear` 里清掉。 + */ + private startNowTicker(): void { + this.stopNowTicker(); + this.nowClockMin = this.nowMinutes(); + this.nowTimer = setInterval((): void => { + this.nowClockMin = this.nowMinutes(); + }, 30 * 1000); + } + + private stopNowTicker(): void { + if (this.nowTimer !== 0) { + clearInterval(this.nowTimer); + this.nowTimer = 0; + } + } + /** 周视图:7 列日期条 + 选中日日程 */ @Builder weekBody() { @@ -1661,7 +1701,7 @@ export struct FbCalendarPage { events: g.events, style: this.style, isToday: g.iso === this.todayIso(), - nowMin: this.nowMinutes(), + nowMin: this.nowClockMin, onPick: (e: FbCalEvent): void => { this.openEvent(e); } diff --git a/entry/src/main/ets/pages/home/FbHomePage.ets b/entry/src/main/ets/pages/home/FbHomePage.ets index f488670..7934571 100644 --- a/entry/src/main/ets/pages/home/FbHomePage.ets +++ b/entry/src/main/ets/pages/home/FbHomePage.ets @@ -337,6 +337,27 @@ export struct FbHomePage { @Local mvCinemas: FbCinemaItem[] = []; @Local mvLoading: boolean = false; + /** + * 当前时刻(当天第几分钟)—— 驱动「今天的日程」时间轴的**当前时间红线**。 + * + * 🔴 用户 2026-11-06:「首页今天的日程,那个红线,不会动态更新,只有刚打开 + * 应用的时候更新,如果不清后台,就一直不动了。」 + * + * 根因:原来是 `nowMin: this.nowMinutes()` —— **在 build 里直接调用方法**。 + * ArkUI 不会因为"时间流逝"重算任何东西,那个方法只在首次 build(以及 + * 别的状态变化引发重绘)时求值一次 ⇒ 红线永远停在打开 App 的那一刻。 + * + * ⇒ 改成 `@Local` 字段 + 定时器主动改它:字段一变,`FbDayEventPanel` 的 + * `@Param nowMin` 就更新,红线跟着走。这是项目里唯一能让时间自动刷新的 + * 办法(`@Builder` 里调方法不建立依赖,与 `pitfalls.md` 记的那条同源)。 + * + * ⚠️ 不叫 `nowMin` 而是 `nowClockMin`:`nowMin` 已经被 `nowMinutes()` + * 方法占着语义,两个同名字段/方法挨着放容易误改。 + */ + @Local nowClockMin: number = 0; + /** 红线的定时器句柄(0 = 没在跑) */ + private nowTimer: number = 0; + /** 「新增成员」表单开着 */ @Local pfCreating: boolean = false; /** 联系人卡要显示的人(本地库前 N 个) */ @@ -384,10 +405,16 @@ export struct FbHomePage { ⚠️ 必须**不 await**:本地库首次打开或库很大时读一次要几百毫秒, 串在上面会让首页首屏白等(而这两张卡是锦上添花)。 */ this.loadSideCards(); + /* 🔴 2026-11-06:当前时间红线要自己走 —— 见 `startNowTicker()` 的说明。 + ⚠️ 放在这里而不是 build 里:`build()` 可能被调用很多次, + 在里面起定时器会**越叠越多**(每次都新开一个,旧的没人清)。 */ + this.startNowTicker(); } aboutToDisappear(): void { FB_BACK.unregister('home'); + /* 页面销毁 ⇒ 停掉时间刷新,别让已销毁的页面继续收定时器回调(白耗电)。 */ + this.stopNowTicker(); } /** 返回手势:先关详情卡,再交给 `Index`(返回 true = 已消费) */ @@ -1389,6 +1416,40 @@ export struct FbHomePage { return d.getHours() * 60 + d.getMinutes(); } + /** + * 启动「当前时间」刷新。 + * + * 🔴 用户 2026-11-06:「红线不会动态更新,只有刚打开应用的时候更新, + * 如果不清后台,就一直不动了。」 + * + * 设计取舍: + * · **30 秒一跳**,不是 1 分钟:红线是"现在"的视觉指示,1 分钟一跳的话 + * 用户在整分时刻附近盯着看会觉得"迟了一拍";30 秒的成本极低 + * (只改一个 number 字段,且值没变时设成同值不会触发重绘)。 + * · **先立即刷一次**:`aboutToAppear` 里直接调一次,避免首屏红线 + * 停留在默认值 0(= 00:00,跑到视窗顶部外面去了)。 + * · **只在页面存在期间跑**(`aboutToDisappear` 里清掉): + * 首页切走(`Index` 换模块)会走 `aboutToDisappear`, + * 留着定时器就是纯粹的耗电,而且会让已销毁的页面继续收状态更新。 + * · **不判断"值没变就跳过"**:`@Local` 赋同值本来就不触发重绘, + * 多一句判断反而容易写错(比如把比较写成 `>=`)。 + */ + private startNowTicker(): void { + this.stopNowTicker(); + this.nowClockMin = this.nowMinutes(); + this.nowTimer = setInterval((): void => { + this.nowClockMin = this.nowMinutes(); + }, 30 * 1000); + } + + /** 停掉刷新(可重复调用) */ + private stopNowTicker(): void { + if (this.nowTimer !== 0) { + clearInterval(this.nowTimer); + this.nowTimer = 0; + } + } + /** * 今天 → `YYYY-MM-DD`(`FbEventEditSheet` 的 `presetDate` 要这个格式)。 * @@ -1615,7 +1676,10 @@ export struct FbHomePage { events: this.todayEvents, style: this.calStyle, isToday: true, - nowMin: this.nowMinutes(), + /* 🔴 2026-11-06:读 `@Local nowClockMin`(定时器驱动), + 不要写 `this.nowMinutes()` —— 在 build 里调方法不建立依赖, + 红线会停在打开 App 那一刻(用户报的就是这个)。 */ + nowMin: this.nowClockMin, showHead: false, emptyTitle: '今天没有安排', emptyHint: '过得轻松一点',