feat(harmony)★★: 背景档与预设改用官方 Chip,删掉重复的同步指示器
承接 2026-09-29 的反馈,本批改动是那一轮工作的收尾。
## 1. 自绘 Text → 官方 `Chip`
背景档与预设两排原来是**自绘 `Text`**,而且与设置页主题那排
**圆角/内边距不一致**(预设那排 `borderRadius(4)`,另两处是
`radiusControl`)。改用 `import { Chip, AccessibilitySelectedType,
LengthMetrics } from '@kit.ArkUI'`。
## 2. 删掉 `@Prop statusText`(一个数两个指示器)
它原来是「外观」卡右上角那个「已同步」的**第二份**:页面把同一个
`statusLabel(this.appearanceStatus)` 既画在卡片级标题行、又透传进来
画在「背景」小标题右边 ⇒ 同一屏两个「已同步」,永远同值。
删掉透传这一份,值只从页面进、只画一处。
## 3. Surface.ets(32 行)/ Theme.ets(51 行)/ ContactsTab.ets(21 行)
配套:Chip 的尺寸档、主题色取用、联系人页的调用点。
## 判据
- `test/harmony-appearance.test.mjs`:外观判据随实现更新
- `test/mutants/baseline.sha`:变异体自证底本同步
### ⚠️ 设备端判据本机跑不了(不是代码问题)
node --test test/harmony-appearance.test.mjs
pass 26 / fail 3
失败的 3 条**全部**是设备端断言:
★ 设备:打开背景开关后,壁纸上真的出现了
AssertionError: 要能拉起我们的应用并等到它到前台
★ 设备:窗格内容不得超出屏幕
AssertionError: 要能拉起应用
★ 设备:切换主题后界面真的整体换色了
AssertionError: 设备连续 10 轮拉不起应用(本条已连续 3 轮没真跑)
即**本机没有接 Harmony 设备**,不是断言挂在代码上。
判据文本本身断言「设备连续 10 轮拉不起」——它自己在如实报告
「这条从未在真机上跑过」。**需要真机补验**,这里不假装它通过了。
## docs
API.md / GUI-PLAN-HARMONY.md / PLAN.md 同步上述改动。
This commit is contained in:
39
docs/API.md
39
docs/API.md
@ -88,7 +88,7 @@ POST /me/mail/send 发信
|
||||
GET /me/mail/inbox 收件箱(?status=unread|all&limit=N)
|
||||
GET /me/mail/sent 发件箱
|
||||
GET /mail/{id} 单封详情(含附件列表)
|
||||
GET /mail/{id}/thread 对话树(分块加载,见下)
|
||||
GET /mail/{id}/thread 对话树(?offset=N&limit=N 自线索根分块加载,见下)
|
||||
POST /mail/{id}/read 标记已读
|
||||
POST /me/mail/{id}/forward 转发(引用原文 + 附件随行)
|
||||
```
|
||||
@ -119,22 +119,28 @@ POST /me/mail/{id}/forward 转发(引用原文 + 附件随行)
|
||||
### 对话树
|
||||
|
||||
```
|
||||
GET /mail/{id}/thread?dir=around&limit=40 首屏:锚点 + 部分祖先 + 部分子孙
|
||||
GET /mail/{id}/thread?dir=up&offset=20&limit=40 继续往上(上滑加载)
|
||||
GET /mail/{id}/thread?dir=down&offset=40&limit=40 继续往下
|
||||
GET /mail/{id}/thread?limit=60 首屏:自线索根起的头一块
|
||||
GET /mail/{id}/thread?offset=60&limit=60 继续往后取(offset 传上次 next_offset)
|
||||
```
|
||||
|
||||
树由 `parent_mail_id` 编码:回复指向来信,转发指向被转发的原件。
|
||||
因此**树可以跨会话** —— 转发把线索引到新会话,却仍属同一条线索。
|
||||
|
||||
**从线索根整树 BFS,而不是「锚点的祖先链 + 锚点的子树」**:后者会让兄弟分支
|
||||
整条落在盲区里 —— 一封抄送给两个 Agent 的邮件会收到两个回复,二者互为兄弟,
|
||||
从其中一个看树看不到另一个;挂在原件上的转发分支同理。先上溯到根
|
||||
(`ThreadRootOf`)再整棵展开,兄弟、抄送产生的平行回复、转发分支都是根的子孙,
|
||||
一次覆盖。
|
||||
|
||||
```json
|
||||
{
|
||||
"anchor_mail_id": "...",
|
||||
"dir": "around",
|
||||
"root_mail_id": "...",
|
||||
"anchor_depth": 3,
|
||||
"nodes": [
|
||||
{
|
||||
"mail_id": "...", "parent_mail_id": "...",
|
||||
"depth": -3,
|
||||
"depth": 0,
|
||||
"from_name": "admin", "to_name": "pi",
|
||||
"subject": "...", "body_preview": "正文前 240 字节…",
|
||||
"attachment_count": 2,
|
||||
@ -142,20 +148,25 @@ GET /mail/{id}/thread?dir=down&offset=40&limit=40 继续往下
|
||||
}
|
||||
],
|
||||
"total": 21, "hidden": 4,
|
||||
"has_more_up": true, "has_more_down": false,
|
||||
"next_up": 20, "next_down": 20
|
||||
"has_more": true, "next_offset": 60
|
||||
}
|
||||
```
|
||||
|
||||
- `depth` 是**相对锚点**的层级:0 = 锚点,负数 = 祖先,正数 = 子孙。
|
||||
分块加载时根可能还没取到,所以不用「距根深度」
|
||||
- `offset` 是相对锚点的偏移:`up` 按层数,`down` 按节点数。把 `next_up`/`next_down`
|
||||
原样回传即可,不必自己算已加载数量
|
||||
- `depth` 是**距线索根**的层级:0 = 根,1 = 它的直接回复。**恒 ≥ 0**。
|
||||
从根展开后根一定在结果里,绝对深度因此总是可知的
|
||||
- `offset` 是自根起的**节点数**偏移(不是层数)。从根 BFS 后只剩一个方向,
|
||||
游标就是「已取到的节点数」;把 `next_offset` 原样回传即可,不必自己算
|
||||
- `root_mail_id` / `anchor_depth` 是服务端算出的根与锚点深度,供客户端定位
|
||||
- 节点只带 `body_preview`(240 字节,按 UTF-8 边界截断),全文用 `GET /mail/{id}` 单取
|
||||
- `hidden` = 本页因权限被过滤掉的节点数
|
||||
- `detached` = 父邮件不在当前已加载集合里;`parent_hidden` 进一步区分
|
||||
「确实无权查看」(永久)与「尚未加载」(随上滑补齐)
|
||||
- `limit` 夹到 [1, 200],非法值回落默认 40
|
||||
「确实无权查看」(永久)与「尚未加载」(往下翻会补上)
|
||||
- `limit` 夹到 [1, 200],非法值回落默认 **60**
|
||||
|
||||
**锚点一定可见**:巨型线索里锚点可能没落在 BFS 首页,此时服务端额外把
|
||||
「根 → 锚点」这条路径补进第一页,锚点自己单取。但该路径的回填**上限 60 层**
|
||||
(`anchorPathBudget`)—— 超过 60 层的链条,锚点之上会出现断点。
|
||||
递归 CTE 本身另有 `descendantDepthCap = 10000` 的兜底,仅防数据损坏成环。
|
||||
|
||||
**鉴权按会话逐个进行**:A 转发给 B 之后,B 与 C 在新会话里的往来不会回流给 A。
|
||||
拿一个自己无权访问的 `mail_id` 当锚点直接返回 403。
|
||||
|
||||
Reference in New Issue
Block a user