## 真 bug(设备实测) 深色主题下,「我的」页**选中**的那张账号卡片仍是接近纯白的浅蓝 (实测像素 `(255,255,255)` 级别的浅底压在 `(32,34,36)` 的深色页上)。 根因:`Theme.accentSoft = '#EFF6FF'` 是**写死的浅色**,而它被当作 「选中态背景」用在 12 处(未读邮件行、选中账号、分段选中、登录页模式切换…)。 **为什么只有 WebUI 没这个问题**:它有 CSS 变量的**反转发**机制 —— `index.css:113` 的 `--c-blue-50: 239 246 255` 在 `.dark` 段(`:475`)被换成 `28 37 54`(深蓝黑)。ArkTS 的 `static readonly` **一个常量一个值**, 没有那层机制 ⇒ 静态常量必须自己提供两个取值。 ## 修法 1. `Theme.accentSoftDark = '#1C2536'`(对齐 WebUI `.dark --c-blue-50` 的 `28 37 54`) 2. `Theme.accentSoftFor(dark)` 作为**唯一入口** —— 不在页面里各自 `isDark ? a : b`:那样每处都会各写一遍,迟早漏一处 (WebUI 那条"由 test/theme.test.mjs 逐档断言"就是为防这个) 3. **深浅色从哪来**:只有 `MainPage` 算得出(它读 `resourceManager` 的 `colorMode`)。所以走 `AppStorage` 单向发布(与徽标、windowInsets 同一套): MainPage 算 → 写 `agentmail.appearance.isDark` → 各窗格 `@StorageProp` 读。 6 个文件、12 处,全部改用 `accentSoftFor(this.isDarkNow)`。 **设备实测**:深色下「我的」页账号卡片与收件箱未读行都变成深蓝底, 像素 `(32,34,36)` 与页面底一致(不再刺眼)。 ## 判据(这条是新加的,形状值得记) `cross-client-theme` 新增:**品牌浅底必须有深色变体**。断三件事: 1. 深色变体存在,且**取值从 WebUI 的 `.dark` 段反推**(不是随手挑一个深色); 2. 有按主题选值的**入口**(防"页面各自写三元"); 3. **用到它的地方真的走那个入口** —— 扫描所有 `backgroundColor(… accentSoft …)` 并排除 `accentSoftFor`,把漏改的位置**逐行报出来**。 第 3 条在我改到一半时**当场列出了剩下 9 处**(`CalendarPage:1172`、 `ComposePage:255`、`LoginPage:327`…)—— 这就是它该有的样子: 不是"断言存在某个常量",而是"断言没有一处漏改"。 ★ 写这条判据时踩了自己一次:JS 模板串里嵌了反引号包围的标识符 (`` `accentSoft` ``),直接 SyntaxError。改用字符串拼接。 ## 判据 `run-all.mjs` → `checks=506 pass=506 fail=0 skip=0 red=0 broken=0 unreported=0`。 `hvigorw assembleHap` 成功;前端重建。 **未验**:日历/登录页在深色下的观感(只逐处改了底色,没逐页截图)。
239 lines
8.4 KiB
Plaintext
239 lines
8.4 KiB
Plaintext
/*
|
||
* AgentMail 鸿蒙客户端 — 收件箱页
|
||
* GET /me/inbox 分页,列表展示邮件摘要
|
||
*/
|
||
import { ApiClient, ApiError } from '../api/ApiClient';
|
||
import { Theme } from '../common/Theme';
|
||
import { MailApi } from '../api/MailApi';
|
||
import { MailSummary, Me } from '../model/Models';
|
||
import { router } from '@kit.ArkUI';
|
||
import { AmIcon } from '../common/Icons';
|
||
import { LIST_FADE_LENGTH } from '../model/NavItems';
|
||
import { LengthMetrics } from '@kit.ArkUI';
|
||
import { Insets, KEY_WINDOW_INSETS, topInset } from '../model/WindowInsets';
|
||
|
||
@Entry
|
||
@Component
|
||
struct InboxPage {
|
||
/*
|
||
* 当前是否深色(`MainPage` 经 `AppStorage` 发布)—— 用来选品牌浅底的深浅变体。
|
||
*
|
||
* ★ 2026-09-19:深色主题下这些"浅蓝底"(未读行 / 选中项 / 分段选中)
|
||
* 在深色页面上刺眼。根因是 `Theme.accentSoft` 写死浅色
|
||
* (WebUI 靠 CSS 的 `.dark` 段反转发解决,ArkTS 静态常量没有那层机制)。
|
||
* 窗格自己算不出深浅色 ⇒ 从 AppStorage 读。
|
||
*/
|
||
@StorageProp('agentmail.appearance.isDark') isDarkNow: boolean = false;
|
||
|
||
@State mails: MailSummary[] = [];
|
||
/**
|
||
* 窗口避让区 —— 由 `EntryAbility.setupFullScreenWindow` 写进 AppStorage。
|
||
*
|
||
* ★ 推上来的页**也要**消费它:`setWindowLayoutFullScreen(true)` 是**窗口级**的,
|
||
* 一旦设上,这个窗口里**所有**用 `router.pushUrl` 推上来的页都从 y=0 开始画。
|
||
* 本页顶栏原来贴 y=0 ⇒ 全屏后「取消/发送」会与时钟、wifi/电量图标重叠
|
||
* (2026-09-18 实测截图硬证)。只给 `MainPage` 加避让就是把"黑边"换成
|
||
* "顶栏被状态栏压住",同属只做一半。
|
||
*/
|
||
@StorageLink(KEY_WINDOW_INSETS) windowInsets: Insets = new Insets();
|
||
|
||
@State loading: boolean = false;
|
||
@State limit: number = 20;
|
||
@State total: number = 0;
|
||
@State unread: number = 0;
|
||
@State me: Me = new Me();
|
||
@State error: string = '';
|
||
|
||
private client: ApiClient | null = null;
|
||
private mailApi: MailApi | null = null;
|
||
|
||
aboutToAppear(): void {
|
||
const ctx = this.getUIContext().getHostContext();
|
||
if (ctx !== undefined) {
|
||
const client: ApiClient = ApiClient.getInstance(ctx);
|
||
this.client = client;
|
||
this.mailApi = new MailApi(client);
|
||
this.loadInbox();
|
||
}
|
||
}
|
||
|
||
async loadInbox(): Promise<void> {
|
||
const c: ApiClient | null = this.client;
|
||
const m: MailApi | null = this.mailApi;
|
||
if (c === null || m === null) {
|
||
return;
|
||
}
|
||
this.loading = true;
|
||
this.error = '';
|
||
try {
|
||
const resp = await m.inbox('all', this.limit);
|
||
this.mails = resp.mails;
|
||
this.total = resp.total;
|
||
// 客户端统计未读(API 未提供顶层 unread)
|
||
let unreadCount: number = 0;
|
||
for (let i = 0; i < this.mails.length; i++) {
|
||
if (this.mails[i].status === 'unread') {
|
||
unreadCount++;
|
||
}
|
||
}
|
||
this.unread = unreadCount;
|
||
} catch (e) {
|
||
const ae = e as ApiError;
|
||
this.error = ae.message.length > 0 ? ae.message : '加载失败';
|
||
} finally {
|
||
this.loading = false;
|
||
}
|
||
}
|
||
|
||
build() {
|
||
Column() {
|
||
// 顶栏
|
||
Row() {
|
||
Text('收件箱')
|
||
.fontSize(20).fontWeight(FontWeight.Bold).fontColor(Theme.textPrimary)
|
||
Blank()
|
||
if (this.unread > 0) {
|
||
Text(this.unread + ' 未读')
|
||
.fontSize(13).fontColor(Theme.surface)
|
||
.backgroundColor(Theme.danger)
|
||
.borderRadius(10)
|
||
.padding({ left: 8, right: 8, top: 2, bottom: 2 })
|
||
}
|
||
}
|
||
/* 高度与 padding-top 一起加避让 —— 理由见 `ComposePage` 顶栏(同形状) */
|
||
.width('100%')
|
||
.height(56 + topInset(this.windowInsets))
|
||
.padding({ left: 16, right: 16, top: topInset(this.windowInsets) })
|
||
.backgroundColor(Theme.surface)
|
||
|
||
// 列表
|
||
if (this.loading) {
|
||
Column() {
|
||
LoadingProgress().width(40).height(40)
|
||
Text('加载中…').fontSize(14).fontColor(Theme.textSubtle).margin({ top: 8 })
|
||
}
|
||
.width('100%').height('80%')
|
||
.justifyContent(FlexAlign.Center)
|
||
} else if (this.error.length > 0) {
|
||
Column() {
|
||
Text(this.error).fontSize(14).fontColor(Theme.danger)
|
||
Button('重试').margin({ top: 12 }).onClick(() => { this.loadInbox(); })
|
||
}
|
||
.width('100%').height('80%')
|
||
.justifyContent(FlexAlign.Center)
|
||
} else if (this.mails.length === 0) {
|
||
Column() {
|
||
Text('收件箱为空').fontSize(16).fontColor(Theme.textSubtle)
|
||
}
|
||
.width('100%').height('80%')
|
||
.justifyContent(FlexAlign.Center)
|
||
} else {
|
||
List({ space: 1 }) {
|
||
ForEach(this.mails, (mail: MailSummary) => {
|
||
ListItem() {
|
||
this.MailItem(mail)
|
||
}
|
||
.height(80)
|
||
.backgroundColor(mail.status === 'unread' ? Theme.accentSoftFor(this.isDarkNow) : Theme.surface)
|
||
.onClick(() => {
|
||
this.getUIContext().getRouter().pushUrl({
|
||
url: 'pages/MailDetailPage',
|
||
params: { mail_id: mail.mail_id }
|
||
});
|
||
})
|
||
}, (mail: MailSummary) => mail.mail_id)
|
||
}
|
||
.width('100%')
|
||
.layoutWeight(1)
|
||
/* 上下边缘渐隐(2026-09-17 补:原先漏了,同一次滚动里只有部分是渐隐) */
|
||
.fadingEdge(true, { fadingEdgeLength: LengthMetrics.vp(LIST_FADE_LENGTH) })
|
||
.divider({ strokeWidth: 1, color: Theme.border, startMargin: 16, endMargin: 16 })
|
||
}
|
||
|
||
// 底部 Tab 栏(预留系统导航栏空间)
|
||
Column() {
|
||
Row() {
|
||
Column() {
|
||
AmIcon({ iconName: 'inbox', iconSize: 20, iconColor: Theme.accent })
|
||
Text('收件箱').fontSize(11).fontColor(Theme.accent)
|
||
}
|
||
.layoutWeight(1).height('100%')
|
||
.justifyContent(FlexAlign.Center)
|
||
.backgroundColor(Theme.accentSoftFor(this.isDarkNow))
|
||
|
||
Column() {
|
||
AmIcon({ iconName: 'chatBubble', iconSize: 20, iconColor: Theme.textMuted })
|
||
Text('会话').fontSize(11).fontColor(Theme.textMuted)
|
||
}
|
||
.layoutWeight(1).height('100%')
|
||
.justifyContent(FlexAlign.Center)
|
||
.onClick(() => {
|
||
this.getUIContext().getRouter().pushUrl({ url: 'pages/SessionsPage' });
|
||
})
|
||
|
||
Column() {
|
||
AmIcon({ iconName: 'contacts', iconSize: 20, iconColor: Theme.textMuted })
|
||
Text('联系人').fontSize(11).fontColor(Theme.textMuted)
|
||
}
|
||
.layoutWeight(1).height('100%')
|
||
.justifyContent(FlexAlign.Center)
|
||
}
|
||
.width('100%').height(56)
|
||
.backgroundColor(Theme.surface)
|
||
}
|
||
.width('100%')
|
||
}
|
||
.width('100%').height('100%')
|
||
.backgroundColor(Theme.pageBg)
|
||
}
|
||
|
||
@Builder
|
||
MailItem(mail: MailSummary) {
|
||
Row() {
|
||
// 未读圆点
|
||
Column() {
|
||
if (mail.status === 'unread') {
|
||
Circle({ width: 8, height: 8 }).fill(Theme.accent)
|
||
}
|
||
}
|
||
.width(20).height('100%')
|
||
.justifyContent(FlexAlign.Center)
|
||
|
||
// 正文
|
||
Column() {
|
||
Row() {
|
||
Text(mail.from_name).fontSize(14).fontWeight(mail.is_read ? FontWeight.Normal : FontWeight.Bold).fontColor(Theme.textPrimary)
|
||
Blank()
|
||
Text(mail.created_at).fontSize(11).fontColor(Theme.textSubtle)
|
||
}
|
||
.width('100%')
|
||
|
||
Text(mail.subject).fontSize(13).fontColor(Theme.textPrimary)
|
||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||
.margin({ top: 2 })
|
||
Text(mail.body_preview).fontSize(12).fontColor(Theme.textSubtle)
|
||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||
.margin({ top: 2 })
|
||
}
|
||
.layoutWeight(1).height('100%')
|
||
.alignItems(HorizontalAlign.Start)
|
||
.padding({ left: 8 })
|
||
|
||
// 附件图标
|
||
if (mail.has_attachments) {
|
||
AmIcon({ iconName: 'paperclip', iconSize: 14, iconColor: Theme.textMuted }).margin({ right: 8 })
|
||
}
|
||
|
||
// 权限档位徽标
|
||
if (mail.permission_mode.length > 0) {
|
||
Text(mail.permission_mode)
|
||
.fontSize(10).fontColor(Theme.textMuted)
|
||
.backgroundColor(Theme.border).borderRadius(4)
|
||
.padding({ left: 4, right: 4, top: 1, bottom: 1 })
|
||
}
|
||
}
|
||
.width('100%').height('100%')
|
||
.padding({ left: 12, right: 12 })
|
||
.alignItems(VerticalAlign.Center)
|
||
}
|
||
} |