鸿蒙|修发件箱点不开(SentRow 缺 onClick)+ 补列表行判据
用户报的三件事,逐条实测后的结论:
## ① 发件箱内容点不开 —— 真 bug,本次修复
复现:点发件箱「致 pi」那一行
· 日志里**没有** `GET /mail/{id}`(收件箱同样操作是会发的)
· 右栏仍是占位(「选择一封邮件查看…」)
根因:点击挂在**外层** `Column` 上,而 `SentRow` 自己**一个 `.onClick` 都没有**。
更要命的是 `isFlatGroup(g)` 那条分支(单封不成组)直接调 `SentRow`,
**外层那层点击根本不经过它** —— 而用户的数据恰好是 1 封不成组 ⇒ 完全点不动。
收件箱的 `MailRow` 是挂在行自己身上的(`.clip(true)` 之后直接 `.onClick`),
所以它两条分支都通。这是本仓反复出现的形状:同一件事两处各写一遍,然后慢慢分叉。
修法:`.onClick` 移进 `SentRow` 自身(与 `MailRow` 同形),
并删掉外层那处(留着会变成"同一行两处都能点")。
修复后实测:发 `GET /mail/2d882e82…`,右栏渲染出详情 ✅
## ② 对话树点不开 —— 实测能点开(不是 bug)
代码里有完整的实现(`openThread()` + `bindSheet` + `tree` 图标按钮)。
点开路径:**先点详情页标题展开头部** → 出现「对话树」→ 点它
(`GET /mail/{id}/thread` 发出,弹层显示 `jianf → pi / 测试`)。
★ 折叠是**两端一致**的行为,不是鸿蒙漏做:
WebUI `CollapsibleHeader` 的 `useState(false)` 也是默认收起。
我实测确认了展开后才看得到该按钮。
## ③ 邮件本地缓存 —— 两端都没有(不是鸿蒙落后)
对着源码逐个数:
WebUI `stores/` 有 persist 的:accountStore(8) / appearanceSync / backgroundStore(15)
/ contactStore(5) / themeStore(3)
WebUI **没有** persist 的:`mailStore`(0) / sessionStore(0) / uiStore(0) / authStore(0)
⇒ **邮件与会话在 WebUI 也是不缓存的**,每次打开都从服务端拉。
鸿蒙侧:AppearanceStore / AccountManager 有 preferences 持久化,MailStore 没有
—— 与 WebUI 对齐,不是缺口。
## 判据
`harmony-logic` +1(35 条):**列表行必须把 onClick 挂在自己身上**。
钉的是不变式("行自带点击"),不是某个函数的写法:
`MailRow` / `SentRow` 的属性链里必须有自己的 `.onClick` 且调到 `openMail`。
变异:删掉 `SentRow` 的 `.onClick`(重演原 bug)→ 判据红;还原 → 绿。
套件:harmony-logic 35/35、harmony-nav 22/22、harmony-appearance 28/28、
harmony-contacts 5/5、animation-audit 15/15。
This commit is contained in:
@ -985,3 +985,48 @@ test('★ 附件区:空文件名要显式占位,不能留一条看起来坏
|
||||
assert.notEqual(empty.filename, '', '空文件名要有占位符,不能是空串');
|
||||
assert.equal(empty.size, '100 B', '空文件名不影响大小显示');
|
||||
});
|
||||
|
||||
/**
|
||||
* ★★ 2026-09-24 新增(用户:「发件箱内容也点不开」)。
|
||||
*
|
||||
* ── 实测复现 ──
|
||||
* 点发件箱里「致 pi」那一行:
|
||||
* · 日志里**没有** `GET /mail/{id}`(收件箱同样操作是会发的);
|
||||
* · 右栏仍是占位(「选择一封邮件查看…」)。
|
||||
*
|
||||
* ── 根因 ──
|
||||
* 点击挂在**外层** `Column` 上,而 `SentRow` 自己**一个 `.onClick` 都没有**。
|
||||
* 更要命的是:`isFlatGroup(g)` 那条分支(单封不成组)直接调 `SentRow`,
|
||||
* **外层那层点击根本不经过** ⇒ 用户的数据恰好是 1 封不成组 ⇒ 完全点不动。
|
||||
*
|
||||
* 收件箱的 `MailRow` 是挂在行自己身上的,所以它两条分支都通。
|
||||
* 这就是本仓反复出现的形状:**同一件事两处各写一遍,然后慢慢分叉**。
|
||||
*
|
||||
* 判据钉的是**不变式**(“列表行自带点击”),而不是某个函数的写法:
|
||||
* 每个能展开邮件的列表行 `@Builder`,它的属性链里必须有自己的 `.onClick`。
|
||||
*/
|
||||
test('★ 列表行必须把 onClick 挂在自己身上(发件箱就漏在这里)', () => {
|
||||
/*
|
||||
* 行 Builder 的名单:两栏各自的邮件行。
|
||||
* ★ 不扫全部 `@Builder`(那样会把组头、空态、徽标都算进来),
|
||||
* 只钉**真正代表“一封邮件”的那两个** —— 与 `PAGE_SOURCES` 同一条纪律:
|
||||
* 名单显式,不把作用域开得比声称的宽。
|
||||
*/
|
||||
const ROWS = ['MailRow', 'SentRow'];
|
||||
for (const name of ROWS) {
|
||||
const at = pageCode.indexOf(`${name}(mail: MailLike)`);
|
||||
assert.ok(at > 0, `${name} 要存在(列表行)`);
|
||||
/*
|
||||
* 取到下一个 `@Builder` 为止(下一个 Builder 就是下一个方法的开始)。
|
||||
* 比“取 3000 字符”可靠:方法长短会变,而 `@Builder` 是结构边界。
|
||||
*/
|
||||
const nextAt = pageCode.indexOf('@Builder', at);
|
||||
const body = pageCode.slice(at, nextAt > at ? nextAt : at + 4000);
|
||||
assert.match(body, /\.onClick\(/, [
|
||||
`${name} 的属性链里必须有自己的 \`.onClick\``,
|
||||
'—— 点击挂在外层容器时,扁平组分支(isFlatGroup)**不经过它**,',
|
||||
'那一栏就完全点不动(2026-09-24 发件箱实测就是这个形状)。',
|
||||
].join(''));
|
||||
assert.match(body, /openMail\(/, `${name} 的 onClick 要调到 openMail(而不是只写个空壳)`);
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user