feat(harmony): P2b+P3 —— 「收件箱」改成「通信」(内部三栏 + 徽标 + 悬浮加号),发件箱与授权栏落地
用户:「收件发件授权改为一个导航项,通过内部导航区分,然后新建作为他们内部的一个悬浮的圆形加号」。 所以这一期不是"再加两个页面",而是对齐信息架构。 ## 鸿蒙侧 - 底部第一项 **收件箱 → 通信**(`CommPage`),内部三栏 收件箱 / 发件箱 / 授权; 页签下划线式(不是浮动白胶囊 —— WebUI 侧用户原话「通信页面的二级页面与其他位置极其割裂」)。 - **徽标**:收件箱红(未读)、授权橙(**待决策**)、发件箱无;0 不显示,>99 写 `99+`。 合并成一个导航项后,底部看不到"授权有 3 个在等我",这个信息不能丢 —— 它比未读更急。 - **悬浮圆形加号**挂到通信页这一层(三个栏都要能新建);`⚙` 也搬上来(否则切栏就够不到设置)。 - **收件箱不再混权限邮件**(`splitByPermission`),未读按筛后算 —— WebUI 实测过"一个会话 17 封权限邮件挤掉另外两个会话"。 - **发件箱**:`GET /me/mail/sent`,与收件箱同构(同一套折叠/行),行上主角是**收件人**; 空态有说明(主句与 WebUI 逐字一致「暂无邮件」+ 一句"这里放什么")。 - **授权栏**(P3 主体):`GET /permission/pending`(不从收件箱筛)+ `POST /permission/decide`。 拒绝**可填备注且备注真的送出**;请求**过期**时当场说清「审批不会让那次调用继续」。 - 顺手删掉死代码:收件箱里「写邮件」的 `bindSheet`(`composeVisible` 从未置 true,谁也打不开)。 ## 判据(harmony-logic 19 → 28 条) 页签键/顺序从 WebUI 源码抽取比对(`uiStore.ts` 的 `CommTab` + `CommTabs.tsx` 的 `TABS`); 页签状态机(键↔下标往返、脏键/越界/非整数 → 回收件箱);徽标规则(数字来源、0 不显示、 99+ 上限、红/橙与 WebUI 类名对应);分家语义(决策过的不再算待决策、空串与 null 同义); 三栏空态互不相同且主句与 WebUI 一致;接线(三个 pane 真的渲染、加号是圆形且在通信页、 "先分家再折叠"、接口路径与决策体三字段、拒绝传备注、过期分支)。 判据抓到一个**真 bug**:`commTabFromIndex` 只判范围,`1.5` → `COMM_TABS[1.5]` = `undefined` (表现"点哪都不亮")。已加 `Number.isInteger`。 变异验证(六种):授权徽标看未读 / 权限邮件不分出去 / 类型名拼错 / 决策过仍算待决策 / 收件箱不分家 / 发件箱空态去掉说明 —— 全部判红。 ## 排序说明 **日历暂时没有入口**:P6 的内容(网格 + 事件读写 + 滑动翻页)还没做, 先放一个点进去空着的入口比暂时没有更糟 —— 有意排序,记在 §7.15 以免被当成漏做。 ## 验证 / 未验 `hvigorw assembleHap` BUILD SUCCESSFUL;`npm test` 退出码 0(10 个判据文件全绿 + vitest 258/258)。 **未验**:页签/徽标/悬浮加号在真机上的观感与点击 —— 需设备或模拟器(模拟器要人在命令行启动)。
This commit is contained in:
@ -27,10 +27,19 @@ export interface MailLike {
|
||||
session_id: string;
|
||||
session_alias: string;
|
||||
from_name: string;
|
||||
/** 收件人显示名 —— 发件箱那一栏行上显示的是它(收件箱显示 from_name) */
|
||||
to_name: string;
|
||||
subject: string;
|
||||
body_preview: string;
|
||||
created_at: string;
|
||||
status: string;
|
||||
/**
|
||||
* 邮件类型:`permission_request` 是**待办**(等人点头),其余是要读的内容。
|
||||
* 与 WebUI 的 `Mail.mail_type` 同名同义 —— 收件箱与授权两栏就按它分家。
|
||||
*/
|
||||
mail_type: string;
|
||||
/** 决策结果(空串 = 还没人处理过)。后端用 COALESCE 归一成空串,所以空串与 null 同义。 */
|
||||
permission_result: string;
|
||||
permission_mode: string;
|
||||
source_account_id: string;
|
||||
source_account_name: string;
|
||||
@ -328,3 +337,48 @@ export function permissionChipText(mode: string, enforcement: string): string {
|
||||
}
|
||||
return label + ' ' + enforcementGlyph(enforcement);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ───────── 权限请求从普通邮件里分出来(对应 WebUI `mailGroups.ts` 的同名三个函数) ─────────
|
||||
*
|
||||
* 权限请求不是「一封信」而是「一件待办」:它的生命周期是「等人点头 → 决策完就作废」。
|
||||
* 混在收件箱里两者互相伤害:一次 Agent 任务能连着产生十几封权限请求,
|
||||
* 把真正需要阅读的来信挤到看不见的地方(WebUI 侧实测过:17 封权限邮件挤掉另外两个会话)。
|
||||
* 所以收件箱只放要读的,授权栏只放要批的。
|
||||
*/
|
||||
|
||||
/** 权限邮件且尚无决策结果。空串与 null 都算未决策。 */
|
||||
export function isPendingPermission(m: MailLike): boolean {
|
||||
return m.mail_type === 'permission_request' && !m.permission_result;
|
||||
}
|
||||
|
||||
/** 待决策的权限请求数 —— 授权栏徽标的数字,也是「有人被卡住、要人动手」的唯一信号。 */
|
||||
export function countPendingPermissions(mails: MailLike[]): number {
|
||||
let n: number = 0;
|
||||
for (let i = 0; i < mails.length; i++) {
|
||||
if (isPendingPermission(mails[i])) {
|
||||
n += 1;
|
||||
}
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
/** 分家结果(用 class 而不是匿名对象字面量:ArkTS 要求对象字面量处处有类型) */
|
||||
export class MailSplit {
|
||||
normal: MailLike[] = [];
|
||||
permissions: MailLike[] = [];
|
||||
}
|
||||
|
||||
export function splitByPermission(mails: MailLike[]): MailSplit {
|
||||
const out: MailSplit = new MailSplit();
|
||||
for (let i = 0; i < mails.length; i++) {
|
||||
const m: MailLike = mails[i];
|
||||
if (m.mail_type === 'permission_request') {
|
||||
out.permissions.push(m);
|
||||
} else {
|
||||
out.normal.push(m);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user