跨端对齐:授权栏 navigator_only + 组件按页拆分 + 服务器补 permission_options
用户两项裁定落地(均为 ask_user 明确选择):
① 授权栏口径 = navigator_only(照 WebUI 架构)
· 新建 pages/PermissionPanel.ets —— 详情页的决策面板,
对应 MailView.tsx:693 的 PermissionPanel(审批型 / 主动提问 / 已处理 三态)
· 决策入口从授权栏移到 MailDetailPage;MailDetailPage 原来只显示一个
「权限请求」小标签、根本没有决策入口(比 WebUI 少一整块,且反了:
栏里能决策、点进详情反而不能)
· PermissionTab 删掉内联「同意/拒绝」+ 备注框 + decide():
整卡可点 → onOpenMail(对齐 WebUI PermissionList.tsx:81 的 pick())
· PermissionRequest 补 source_account_id(客户端侧记来源,跳详情要定位网关)
② 服务器补 permission_options —— 修一条真实的、跨端共有的缺口
· mails.permission_options 从 INSERT 起就写进去,但**从来没有任何读路径
选过它** ⇒ 详情端点永远返回空。WebUI 的决策面板读 mail.permission_options,
所以提问型的预设选项**两端全部落空**(审批型靠 ['同意','拒绝'] 兜底蒙混)
· GetMailByID 补选该列 + JSON 反序列化(与 cc_list 同款)
③ 组件按页封装(用户要求「以便与 WebUI 一一对应」)
MainPage.ets 4592 → 3192 行
· pages/PermissionTab.ets 720 行 ↔ PermissionList.tsx
· pages/ContactsTab.ets 796 行 ↔ ContactPanel.tsx
· pages/NavDestinations.ets 181 行 ↔ Navigation 壳
· pages/NavShared.ets 65 行 ↔ 跨栏共用件
④ 判据跟着组件搬家(否则静默失效,不是红)
harmony-logic 的 pageCode / harmony-nav 的 navSrc 改为显式文件名单;
harmony-appearance 的 PANE_SOURCES 补 ContactsTab;harmony-contacts 三个
test 并入 ContactsTab;harmony-logic 的决策断言改指 PermissionPanel,
并新增「授权栏不许再有内联决策」两条(navigator_only 的正形状)。
animation-audit:共享元素转场判据从「同文件共址」改为「按 id 找驱动」。
旧形状把 in/out 端必须在同一文件当成代理,而两端**天然在两处**;
抽出写信页(NavDestinations 持有 in 端)后误报。新判据仍要求每个 id
都有 Motion.morph 驱动 —— 变异实测:把驱动换成裸 animateTo 仍判红。
判据:files=34 checks=556 red=1(仅 build-stamp,产物待重构建)
This commit is contained in:
@ -936,8 +936,31 @@ test('遮罩:交给系统的遮罩语义色("随主题换向"这件事现在
|
||||
* 由那个方法自己的使用点担保)。
|
||||
*/
|
||||
const themeSrc = code(themePath);
|
||||
const declared = [...themeSrc.matchAll(/static readonly (\w+)\s*[:=]/g)].map(m => m[1]);
|
||||
assert.ok(declared.length > 20, `要从 Theme.ets 里读到令牌清单(读到 ${declared.length} 个)`);
|
||||
/*
|
||||
* ★★ 2026-09-21 补:清单从"只有 `static readonly` 字段"扩到 **`static` 方法**。
|
||||
*
|
||||
* ── 怎么发现的 ──
|
||||
* 我把两个账号选择器换成官方 `bindMenu`(系统自带转场)之后,
|
||||
* `Theme.menuIn()` 就**没有任何调用点了** —— 它是个 `static` 方法,
|
||||
* 而这份清单当时只匹配 `static readonly NAME`,于是它**从来不在这份清单里**,
|
||||
* 这条判据一路绿着看它变成死代码。
|
||||
*
|
||||
* 讽刺的是本条判据的出身正是同一个形状:pi 2026-09-15 那一刀是为了
|
||||
* "`navMaterial` 没人用了却没被抓"(一个**字段**)。今天同一个洞
|
||||
* 长在**方法**上 —— 覆盖面按"声明种类"划,就必然漏掉另一种声明。
|
||||
* ⇒ 清单改成"字段 + 方法",两者用同一套可达性规则。
|
||||
*
|
||||
* ★ 方法名不带 `()`:比对用的是 `Theme.<name>\b`,
|
||||
* 而调用点写 `Theme.menuIn()` —— `\b` 在 `n` 与 `(` 之间成立,匹配得上。
|
||||
*/
|
||||
const fieldTokens = [...themeSrc.matchAll(/static readonly (\w+)\s*[:=]/g)].map(m => m[1]);
|
||||
const methodTokens = [...themeSrc.matchAll(/^\s*static (\w+)\(/gm)].map(m => m[1]);
|
||||
const declared = [...new Set([...fieldTokens, ...methodTokens])];
|
||||
assert.ok(fieldTokens.length > 20,
|
||||
`要从 Theme.ets 里读到令牌清单(读到 ${fieldTokens.length} 个)`);
|
||||
assert.ok(methodTokens.length > 10,
|
||||
`也要读到 static 方法清单(读到 ${methodTokens.length} 个)—— ` +
|
||||
'少了它,`menuIn` 那种"方法型的死代码"就永远不在射程内');
|
||||
const others = collectEts(etsRoot).filter(f => f !== themePath);
|
||||
const othersSrc = others.map(f => ({ f: f.slice(etsRoot.length + 1), src: code(f) }));
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user