fix(月视图): 翻月后选中态丢失,整月没有一天被选中 #6

用户反馈:月视图滑动切换月份后会"跳一下",整月没有任何一天被选中。
期望:切到下个月后,**同样的日号**保持选中(如 11/2 → 12/2)。

## 根因
handleSwiperChange 只改了 displayYear/displayMonth,**完全没动 selectedDate**。
于是 selectedDate 还停在上个月,而新月份所有格子的 dateMs 都变了,
`cell.dateMs === this.selectedDate` 永远为 false ⇒ 整月无选中。

## 修法
翻月后把 selectedDate 迁到新月份的同一天;该月没有这天时钳到月底
(1/31 → 2/28,与 incForFreq 的钳制策略一致)。

## 顺带修掉日号漂移
单纯的"钳制"有个副作用:1/31 → 2/28 → 3/28 → 4/28 …
日号被永久改成 28,翻到有 31 天的月份也回不去了。

加 `preferredDay` 记录**用户主动选中的日号**(点日期时更新,翻月时不更新):
  偏好31号:1/31 → 2/28(钳制显示) → 3/31(回到偏好) → 4/30 → 5/31
"月末"的语义得以保留。

## 验证
脚本 18 项全通过(跨年、闰年 2/29、大月小月、连续翻月、往前往后)。
真机实测:
  11月2日 → 左滑 → 12月,选中圆存在(144×144)
  → 再左滑 → 2027年1月,圆内日期仍是"2"

## 教训
"只改显示状态、不改选中状态"是日历类视图的高频 bug。
翻页/翻月时,凡是与"当前选中项"绑定的视觉(高亮、背景),
都要检查选中项本身有没有跟着迁移 —— 否则 diff 时匹配不上就全空白。
This commit is contained in:
2026-11-02 22:16:08 +08:00
parent d8ee2aa2be
commit 6305b4a7f0
2 changed files with 79 additions and 0 deletions
+14
View File
@@ -89,6 +89,10 @@ struct Index {
* 连 itemGenerator 都不重新执行 ⇒ 内层永远拿到旧的 cells(hasEvent 全 false)。
* 只改内层 key 是没用的,必须让外层 key 变。 */
@State monthDataVersion: number = 0;
/** ⭐ 用户**主动选中**的日号(1~31)。翻月时用它算目标日,
* 避免"1/31 → 2/28 → 3/28"这种日号被永久钳死的问题:
* 有了它,2 月显示 28 号,翻到 3 月能回到 31 号。 */
@State preferredDay: number = new Date().getDate();
@State syncing: boolean = false;
@State loading: boolean = true;
@State menuOpen: boolean = false; // 顶部 ≡ 下拉菜单
@@ -633,6 +637,14 @@ struct Index {
const nm = this.addMonths(this.displayYear, this.displayMonth, delta);
this.displayYear = nm[0];
this.displayMonth = nm[1];
// ⭐ KEEP_SELECTED_DAY 翻月后必须把选中日**迁到新月份的同一天**。
// 原来这里只改 displayMonth、不动 selectedDate,
// 导致 selectedDate 还停在上个月 ⇒ 新月份所有格子都匹配不上,
// 整个月没有任何一天是选中态(用户看到的"乱跳/没有选中")。
// ⭐ 用 preferredDay(用户原本选的日号)而不是 selectedDate 的日号:
// 否则 1/31 → 2/28 之后,再翻月就一直是 28 号,回不到 31。
const lastDay: number = new Date(nm[0], nm[1] + 1, 0).getDate();
this.selectedDate = new Date(nm[0], nm[1], Math.min(this.preferredDay, lastDay)).getTime();
this.reloadEvents();
this.swiperGuard = true;
this.swiperController.changeIndex(1, false);
@@ -3389,6 +3401,8 @@ struct Index {
.justifyContent(FlexAlign.Center)
.onClick(() => {
this.selectedDate = cell.dateMs;
// 用户主动点了某天 ⇒ 记住这个日号,作为后续翻月的基准
this.preferredDay = new Date(cell.dateMs).getDate();
if (!cell.inMonth) {
const d = new Date(cell.dateMs);
this.displayYear = d.getFullYear();