跨端: 发件箱接上 MailStore(A 的剩余)+ 修 3 处把原始 ISO 印到界面上的时间
══ ① A 的剩余:发件箱走了 store(顺带撞出 store 里的一个真 bug)
`SentTab.load()` 原来把收件箱那 107 行**抄了一遍**(注释里还写着"同收件箱"
—— 抄的时候就知道是重复)。现在改成 3 行调用 `MailStore.loadSent`。
★ 接上之后**立刻炸出一个真 bug**:`MailStore.loadSent` 里写的是
snap.groups = [];
而发件箱的列表**就是按会话分组渲染的**(`ForEach(this.groups, …)`)
⇒ 接上 store 之后发件箱会**一片空白**,而"接口有返回、loaded > 0"
会让症状看起来像"数据没到"。
为什么会写成空数组:它是照收件箱那半抄的,而收件箱的 `groups` 来自
`splitByPermission` **筛完之后**的 `inboxMails`(授权邮件要挑出去单独成栏)。
抄的时候只看到"要赋值",没注意发件箱没有那一步筛选 ⇒ 把 `[]` 抄了过来。
发件箱也**不能**照搬那个筛选:那是为"授权待办栏"服务的,发件箱不显示那栏。
★ 这段经历本身值得记:**死代码不会自己暴露错误**。
`loadSent` 写完到今天我接上它之前,那行 `groups = []` 一直没人执行过。
"写完就搁着"和"接上一个调用点"是两件事 —— 后者才算验过。
══ ② 3 处把原始 ISO 直接印到界面上(看到屏幕才发现的)
发件箱那张卡的时间格显示的是
2026-09-19T02:55:33.10099Z
(23 个字符,把「致 homeagent」那行挤到换行)。
WebUI **三种卡片全部格式化**、一个没漏:`MailList.tsx:151/262`、
`PermissionList.tsx:143/251`,全是 `toLocaleString('zh-CN', {month,day,hour,minute})`
⇒ `MM/DD HH:mm`。鸿蒙的 `compactMailTime` 就是那个实现,收件箱一直在用 ——
所以这不是"要不要格式化"的分歧,是**三处漏调**(收件箱 / 发件箱 / 授权栏)。
══ ③ 补判据(形状而不是实例)
新判据:`Text(<x>.created_at)` 这个**形状**不许出现(`Text` 只负责画,
不做格式化)。为什么枚举形状而不是列举调用点:漏的三处分布在**三个不同 struct**,
按名字枚举一定会再漏第四个。
自检 + 变异验证都做了(把 SentRow 那处改回裸字段 → 判据转红;还原 → 绿)。
设备验证:发件箱正常渲染(会话组 + 条目,非空白),
时间显示 `09/19 10:55` 而非 ISO。
This commit is contained in:
@ -273,7 +273,24 @@ export class MailStore {
|
||||
}
|
||||
|
||||
snap.mails = merged;
|
||||
snap.groups = [];
|
||||
/*
|
||||
* ★★ 2026-09-20 修:这里原来写的是 `snap.groups = []`。
|
||||
*
|
||||
* 那是**错的**,而且错得很隐蔽:发件箱的列表**就是按会话分组渲染的**
|
||||
* (`SentTab` 里 `ForEach(this.groups, …)`),`groups` 恒为空
|
||||
* ⇒ 接上 store 之后发件箱会变成一片空白,而"接口有返回、loaded > 0"
|
||||
* 会让错误看起来像"数据没到"。
|
||||
*
|
||||
* 为什么会写成空数组:这个方法是照收件箱那半抄的,而收件箱的
|
||||
* `groups` 来自 `splitByPermission` **筛完之后**的 `inboxMails`
|
||||
* (授权邮件要挑出去单独成栏)。我抄的时候只看到"要赋值",
|
||||
* 没注意发件箱没有那一步筛选 ⇒ 直接把 `[]` 抄了过来。
|
||||
*
|
||||
* 发件箱也**不能**照搬 `splitByPermission`:那一筛是为"授权待办栏"服务的,
|
||||
* 发件箱不显示那个栏位。所以这里直接对全部 merged 分组 ——
|
||||
* 与 WebUI `mailStore.fetchSent` 同口径(那边也是直接 set 全量)。
|
||||
*/
|
||||
snap.groups = groupMailsBySession(merged);
|
||||
snap.loaded = merged.length;
|
||||
snap.unread = 0;
|
||||
snap.notice = '';
|
||||
|
||||
@ -915,45 +915,38 @@ struct SentTab {
|
||||
this.load();
|
||||
}
|
||||
|
||||
/**
|
||||
* 取数**交给 `MailStore`**(与收件箱同一份实现)。
|
||||
*
|
||||
* ★★ 2026-09-20 改。这一段原来是**抄了一遍**多账号聚合:
|
||||
* 遍历账号 → 每个建 `ApiClient` → `sent()` → 补 `source_account_*` 与
|
||||
* `cc_count` → 最后 `groupMailsBySession`。与收件箱里那 107 行是同源复制品
|
||||
* (注释里还写着"同收件箱"—— 说明抄的时候就知道是重复)。
|
||||
*
|
||||
* 用户要做的 **A** 就是这件事:把散在各处的取数收到 store。
|
||||
* 收件箱那半已经收了(`InboxTab.loadData` 现在 3 行),发件箱这片是剩下的。
|
||||
*
|
||||
* ★ 顺带修掉 `MailStore.loadSent` 里一个真 bug(`snap.groups = []`)——
|
||||
* 见那个方法里的注释:接上 store 之前它是死代码,接上之后
|
||||
* 发件箱会**一片空白**。这就是"写完没收口"的代价,也是为什么要
|
||||
* 先接一个调用点再算完 —— 死代码不会自己暴露错误。
|
||||
*/
|
||||
async load(): Promise<void> {
|
||||
const ctx = this.getUIContext().getHostContext();
|
||||
if (ctx === undefined) {
|
||||
return;
|
||||
}
|
||||
this.loading = true;
|
||||
this.error = '';
|
||||
try {
|
||||
const acctMgr: AccountManager = AccountManager.getInstance(ctx);
|
||||
await acctMgr.load();
|
||||
const accounts: AccountInfo[] = acctMgr.getAccounts();
|
||||
const all: MailSummary[] = [];
|
||||
for (let i = 0; i < accounts.length; i++) {
|
||||
const acct: AccountInfo = accounts[i];
|
||||
try {
|
||||
const c: ApiClient = new ApiClient(ctx);
|
||||
c.setBase(acct.server);
|
||||
c.setToken(acct.token);
|
||||
const resp: SentResponse = await new MailApi(c).sent();
|
||||
for (let j = 0; j < resp.mails.length; j++) {
|
||||
const mail: MailSummary = resp.mails[j];
|
||||
mail.source_account_id = acct.id;
|
||||
mail.source_account_name = acct.displayName;
|
||||
/* 同收件箱:派生行上的「抄送 N」(两处都要写 —— 理由见收件箱那处) */
|
||||
mail.cc_count = mail.cc_list.length;
|
||||
all.push(mail);
|
||||
}
|
||||
} catch (e) {
|
||||
// 单个账号拉不到不该让整栏空白(与收件箱同口径)
|
||||
}
|
||||
}
|
||||
this.groups = groupMailsBySession(all);
|
||||
this.loaded = all.length;
|
||||
} catch (e) {
|
||||
const ae = e as ApiError;
|
||||
this.error = ae.message.length > 0 ? ae.message : '加载失败';
|
||||
} finally {
|
||||
this.loading = false;
|
||||
}
|
||||
const store: MailStore = MailStore.getInstance();
|
||||
await store.loadSent(ctx, 'all');
|
||||
this.applyStoreSnapshot(store.snapshot);
|
||||
}
|
||||
|
||||
/** 把 store 快照搬到本组件的 `@State`(`InboxTab` 里同名同形) */
|
||||
private applyStoreSnapshot(snap: MailSnapshot): void {
|
||||
this.groups = snap.groups;
|
||||
this.loaded = snap.loaded;
|
||||
this.loading = snap.loading;
|
||||
this.error = snap.error;
|
||||
}
|
||||
|
||||
isExpanded(key: string): boolean {
|
||||
@ -989,7 +982,17 @@ struct SentTab {
|
||||
.fontSize(13).fontWeight(FontWeight.Medium).fontColor(Theme.textPrimary)
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.layoutWeight(1)
|
||||
Text(mail.created_at).fontSize(10).fontColor(Theme.textSubtleFor())
|
||||
/*
|
||||
* ★★ 2026-09-20 修:这里原来直接渲染 `mail.created_at` ——
|
||||
* 屏上印的是原始 ISO,实测:`2026-09-19T02:55:33.10099Z`
|
||||
* (23 个字符,把「致 homeagent」那一行挤到换行)。
|
||||
*
|
||||
* WebUI 三种卡片**全部都格式化**(`MailList.tsx:262/151`、
|
||||
* `PermissionList.tsx:251/143` 都是 `toLocaleString('zh-CN', {month,day,hour,minute})`
|
||||
* ⇒ `MM/DD HH:mm`)。鸿蒙的 `compactMailTime` 就是那个格式的实现,
|
||||
* 收件箱行(L813)一直在用,这三处漏了。
|
||||
*/
|
||||
Text(compactMailTime(mail.created_at)).fontSize(10).fontColor(Theme.textSubtleFor())
|
||||
}
|
||||
.width('100%')
|
||||
|
||||
@ -1289,7 +1292,8 @@ struct PermissionTab {
|
||||
.margin({ top: 4 })
|
||||
}
|
||||
|
||||
Text(req.created_at).fontSize(10).fontColor(Theme.textSubtleFor()).margin({ top: 6 })
|
||||
/* 同 ①:WebUI `PermissionList.tsx:251` 也格式化,不印 ISO */
|
||||
Text(compactMailTime(req.created_at)).fontSize(10).fontColor(Theme.textSubtleFor()).margin({ top: 6 })
|
||||
|
||||
if (this.noteFor === req.mail_id) {
|
||||
TextInput({ placeholder: '备注(会随决策一起送给 Agent)', text: this.noteText })
|
||||
|
||||
Reference in New Issue
Block a user