跨端对齐:授权栏 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:
@ -41,7 +41,33 @@ const ATT_TS = join(HARMONY_ETS, 'model/Attachment.ts');
|
||||
const ATT = await import(pathToFileURL(ATT_TS).href);
|
||||
const C = await import(pathToFileURL(COMM_TS).href);
|
||||
|
||||
const page = code(join(HARMONY_ETS, 'pages/MainPage.ets'));
|
||||
/*
|
||||
* ★★ 2026-09-23 修:把**从 MainPage 抽出去的组件文件**也并进来。
|
||||
*
|
||||
* 用户要求「把鸿蒙的组件按页面封装,以便与 WebUI 一一对应」,
|
||||
* 于是 `PermissionTab` / `ContactsTab` / `NavDestinations` / `NavShared`
|
||||
* 都从 `MainPage.ets` 抽成了独立文件。而本文件的 `pageCode` 只看
|
||||
* `MainPage.ets` ⇒ 那些断言("卡片上要显示预算"、"切换按钮要走这条判据"…)
|
||||
* 全部落空变红 —— 它们问的是**渲染层有没有这个调用**,
|
||||
* 而那个渲染层已经搬家了。
|
||||
*
|
||||
* ⇒ 判据该问的是"这个应用里有没有",不是"它在哪个文件里"。
|
||||
* 把抽出去的那几个一起读进来。
|
||||
*
|
||||
* ★ 有意**不**做成"扫描 pages/ 下所有文件":那会让断言变得过宽
|
||||
* (任何文件里出现过一次就算数),而本仓反复在消的正是"作用域比声称的宽"。
|
||||
* 这份名单是显式的 —— 组件再搬家就一起改,与 `harmony-appearance` 里
|
||||
* 那份 `PANE_SOURCES` 同一个做法。
|
||||
*/
|
||||
const PAGE_SOURCES = [
|
||||
'pages/MainPage.ets',
|
||||
/* ★ 2026-09-23 抽出的组件(与 WebUI 的 ContactPanel / PermissionList 一一对应) */
|
||||
'pages/ContactsTab.ets',
|
||||
'pages/PermissionTab.ets',
|
||||
'pages/NavDestinations.ets',
|
||||
'pages/NavShared.ets'
|
||||
];
|
||||
const page = PAGE_SOURCES.map((f) => code(join(HARMONY_ETS, f))).join('\n');
|
||||
/**
|
||||
* 断言一律读**剥掉注释的源码**。
|
||||
*
|
||||
@ -663,12 +689,53 @@ test('发件箱与授权栏走的是与 WebUI 相同的接口(路径、字段
|
||||
assert.ok(new RegExp(`payload\\.${field} =`).test(decide), `决策体要带 ${field}(后端按这三个字段解析)`);
|
||||
}
|
||||
// 备注必须真的送出:拒绝路径要传 noteText(WebUI 踩过"界面能填、其实没发出去"的坑)
|
||||
const permTab = sendCode.slice(sendCode.indexOf('struct PermissionTab'));
|
||||
assert.match(permTab, /this\.decide\(req, 'deny', this\.noteText\)/, '拒绝要把备注送出去');
|
||||
assert.match(permTab, /this\.noteText = v;/, '备注框要真的收集输入');
|
||||
/*
|
||||
* ★★ 2026-09-23 修:`PermissionTab` **已从 `MainPage.ets` 抽成独立组件**
|
||||
* (`pages/PermissionTab.ets`)。
|
||||
*
|
||||
* 原来这里写的是 `sendCode.slice(sendCode.indexOf('struct PermissionTab'))`
|
||||
* —— 而 `sendCode` 是 `MainPage.ets` 的源码。抽出之后 `indexOf` 返回 -1,
|
||||
* `slice(-1)` 是**末一个字符**,于是下面四条断言全部落在空串上 ⇒
|
||||
* **整条判据静默失效**(不是红,是变成永远绿的空断言)。
|
||||
*
|
||||
* ★ 这正是本仓反复在消的形状:**判据盯着一个会移动的位置**。
|
||||
* 组件一旦搬家,它不会红,只会不再检查任何东西。
|
||||
* ⇒ 改成读**那个组件自己的文件**,并把"搬家"这件事也钉住
|
||||
* (下面先断言它在新位置,否则给一句能看懂的失败)。
|
||||
*/
|
||||
const permTabSrc = code(join(HARMONY_ETS, 'pages/PermissionTab.ets'));
|
||||
assert.ok(permTabSrc.includes('struct PermissionTab'),
|
||||
'PermissionTab 应在 pages/PermissionTab.ets —— 它若再次搬家,本条要一起改');
|
||||
/*
|
||||
* ★★★ 2026-09-23 重大修:`navigator_only` 后,**决策不在授权栏了**。
|
||||
*
|
||||
* 用户裁定授权栏走 WebUI 架构(纯导航器):点一条 → 详情页决策。
|
||||
* 于是原来断言在 `PermissionTab` 里的「拒绝要送备注 / 备注框收集输入 /
|
||||
* 过期要说清后果」全部搬到了详情页的 `PermissionPanel`(新组件)。
|
||||
*
|
||||
* ⇒ 判据对象从 `PermissionTab` 换成 `PermissionPanel`。
|
||||
* 断言**没变宽**:还是那三件必须如实做的事(备注送出、输入收集、过期说清)。
|
||||
* 只是位置跟组件一起搬了 —— 这正是本文件上面那条注释反复在消的形状。
|
||||
*/
|
||||
const panelSrc = code(join(HARMONY_ETS, 'pages/PermissionPanel.ets'));
|
||||
assert.ok(panelSrc.includes('struct PermissionPanel'),
|
||||
'PermissionPanel 应在 pages/PermissionPanel.ets —— 决策面板搬到详情页了');
|
||||
assert.match(panelSrc, /this\.submit\(decision, noteText\)|this\.submit\(label, this\.note\)/,
|
||||
'决策要带着备注一起送出(拒绝往往要说明理由,理由要真的到 Agent 手里)');
|
||||
assert.match(panelSrc, /this\.note = v;/, '备注框要真的收集输入');
|
||||
// 过期必须当场说清:审批不会让那次调用继续
|
||||
assert.match(permTab, /resp\.expired/, '过期分支要处理');
|
||||
assert.match(permTab, /不会让那次调用继续/, '过期时必须说清后果(否则人会以为 Agent 接着跑了)');
|
||||
assert.match(panelSrc, /resp\.expired/, '过期分支要处理');
|
||||
assert.match(panelSrc, /staleWarning|已超过等待窗口/, '过期时必须说清后果(否则人会以为 Agent 接着跑了)');
|
||||
/*
|
||||
* ★ `navigator_only` 的正形状:授权栏**不再有**内联决策,只导航。
|
||||
* 漏删一个按钮/备注框就是"一半改了"(用户裁定最恨的形状)。
|
||||
*/
|
||||
assert.ok(!/Button\('同意'\)/.test(permTabSrc),
|
||||
'授权栏不许再有内联「同意」按钮 —— 决策在详情页(navigator_only)');
|
||||
assert.ok(!/this\.decide\(/.test(permTabSrc),
|
||||
'授权栏不许再有 decide() 调用 —— 那是内联决策时代的残留');
|
||||
assert.match(permTabSrc, /this\.onOpenMail\(req\.mail_id, req\.source_account_id\)/,
|
||||
'卡片要能把点按变成导航(onOpenMail 带 mail_id + 来源账号)—— navigator_only 的形状');
|
||||
});
|
||||
|
||||
test('★ 判据自检:把状态机的默认页签改错必须判红', () => {
|
||||
|
||||
Reference in New Issue
Block a user