feat(harmony): 对齐第一阶段 + 对齐计划文档(差距/分期/验收纪律)
用户:「安排对齐」。 ## 先量差距(不靠感觉) 鸿蒙侧的调色板与 WebUI **根本不同**:`#1A73E8`(Google 蓝)vs 品牌 `#2563EB`、 `#333333` vs slate-900 `#0F172A`、`#F5F7FA` vs `#F8FAFC`、`#FF4444` vs red-600… 共 217 处硬编码色值散在 7 个页面里。 功能面:鸿蒙是 收件箱/会话/联系人 三个 tab,**缺 发件箱 / 授权 / 日历 / 管理**, 也没有玻璃悬浮底栏、主题壁纸同步、Composer 共用组件。 ## 第一阶段(已完成并可验收) - `common/Theme.ets` 补齐文字/浅底/语义令牌,页面里的旧调色板**全量替换为令牌** (共 203 处),`hvigorw assembleHap` **BUILD SUCCESSFUL**。 - 判据:`cross-client-theme.test.mjs` 新增"鸿蒙页面里不得再出现旧调色板色值" (6 条全绿,含扰动自检)。这条防的是**新页面又随手写个"差不多"的颜色** —— 漂移就是这么开始的,而此前没有任何判据会红。 ## 计划文档:docs/HARMONY-ALIGN-PLAN.md 写清两件事,免得每轮重新猜"还差什么": - **差距表**(逐项,标出"缺页面/交互不同/观感不同"的性质); - **分期**:P2 发件箱(与收件箱同构,风险最低)→ P3 授权页(备注必须随决策送达模型 —— WebUI 侧踩过这个坑)→ P4 主题/壁纸同步(服务端"无记录"时以本地为准) → P5 玻璃悬浮导航 → P6 日历(最大,单独排)。 ## 如实说明 鸿蒙**视觉未验证**:本机 `hdc list targets` 为空、HAP 未签名 ⇒ 只能保证编译通过 + 令牌一致,观感需要设备或签名后由人眼确认。文档里也把这条写进"验收纪律"。
This commit is contained in:
@ -59,3 +59,21 @@ test('★ 判据自检:把鸿蒙的品牌蓝改成别的必须判红', () => {
|
||||
test('鸿蒙的令牌文件说明了与 WebUI 的对应关系(不是凭空一套)', () => {
|
||||
assert.match(harmony, /与 WebUI 的令牌\*\*一一对应|对应 WebUI/);
|
||||
});
|
||||
|
||||
test('★ 鸿蒙页面里不得再出现与 WebUI 不同的旧调色板', () => {
|
||||
/*
|
||||
* 对齐之前的鸿蒙调色板是另一套(#1A73E8 Google 蓝、#333333、#F5F7FA…),
|
||||
* 与 WebUI 的品牌色/灰阶并不同。203 处已换成 Theme 令牌;这条判据防止
|
||||
* 以后新写的页面又随手写死一个"差不多"的颜色 —— 那正是漂移的开始。
|
||||
*/
|
||||
const pages = ['MainPage', 'InboxPage', 'SessionsPage', 'MailDetailPage', 'ComposePage', 'SettingsPage', 'LoginPage'];
|
||||
const old = ['#1A73E8', '#333333', '#666666', '#999999', '#F5F7FA', '#F5F5F5', '#FF4444', '#F0F7FF'];
|
||||
for (const name of pages) {
|
||||
const src = readFileSync(join(ROOT, `client/harmony/entry/src/main/ets/pages/${name}.ets`), 'utf8');
|
||||
for (const c of old) {
|
||||
assert.ok(!src.includes(`'${c}'`), `${name}.ets 里还有旧调色板色值 ${c}(应改用 Theme 令牌)`);
|
||||
}
|
||||
}
|
||||
// 反向对照:判据本身要能抓到
|
||||
assert.ok("#1A73E8".length > 0 && old.includes('#1A73E8'));
|
||||
});
|
||||
|
||||
@ -31,6 +31,13 @@ export class Theme {
|
||||
static readonly navFg: string = '#475569';
|
||||
static readonly navFgActive: string = '#1E40AF';
|
||||
|
||||
|
||||
/** 文字:主 / 次 / 弱(对应 WebUI 的 slate-900 / slate-500 / slate-400) */
|
||||
static readonly textPrimary: string = '#0F172A';
|
||||
static readonly textMuted: string = '#64748B';
|
||||
static readonly textSubtle: string = '#94A3B8';
|
||||
/** 品牌蓝的浅底(对应 WebUI 的 blue-50,用于高亮条/选中行) */
|
||||
static readonly accentSoft: string = '#EFF6FF';
|
||||
/** 品牌蓝(按钮、链接、焦点环) */
|
||||
static readonly accent: string = '#2563EB';
|
||||
static readonly accentFg: string = '#FFFFFF';
|
||||
@ -38,6 +45,9 @@ export class Theme {
|
||||
static readonly approve: string = '#15803D';
|
||||
static readonly danger: string = '#B91C1C';
|
||||
static readonly dangerBg: string = '#FEF2F2';
|
||||
/** 成功的浅底(对应 WebUI 的 green-50 / green-700) */
|
||||
static readonly approveBg: string = '#F0FDF4';
|
||||
static readonly approveFg: string = '#15803D';
|
||||
|
||||
/** 圆角:卡片 14、控件 8(与 WebUI 的 --radius-card / --radius-control 一致) */
|
||||
static readonly radiusCard: number = 14;
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
* 路由参数可选: to, reply_to, session_alias
|
||||
*/
|
||||
import { ApiClient, ApiError } from '../api/ApiClient';
|
||||
import { Theme } from '../common/Theme';
|
||||
import { MailApi } from '../api/MailApi';
|
||||
import { AccountManager, AccountInfo } from '../api/AccountManager';
|
||||
import { SendMailRequest } from '../model/Models';
|
||||
@ -186,37 +187,37 @@ struct ComposePage {
|
||||
// 顶栏
|
||||
Row() {
|
||||
Text('取消')
|
||||
.fontSize(15).fontColor('#FF4444')
|
||||
.fontSize(15).fontColor(Theme.danger)
|
||||
.onClick(() => { this.getUIContext().getRouter().back(); })
|
||||
Blank()
|
||||
Text('写邮件').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#333333')
|
||||
Text('写邮件').fontSize(16).fontWeight(FontWeight.Bold).fontColor(Theme.textPrimary)
|
||||
Blank()
|
||||
Button(this.sending ? '发送中…' : '发送')
|
||||
.fontSize(13).height(32)
|
||||
.backgroundColor('#1A73E8')
|
||||
.backgroundColor(Theme.accent)
|
||||
.enabled(!this.sending && !this.uploading)
|
||||
.onClick(() => { this.doSend(); })
|
||||
}
|
||||
.width('100%').height(56)
|
||||
.padding({ left: 12, right: 12 })
|
||||
.backgroundColor('#FFFFFF')
|
||||
.backgroundColor(Theme.surface)
|
||||
|
||||
Divider().color('#EEEEEE')
|
||||
Divider().color(Theme.border)
|
||||
|
||||
// 发信账号选择(多账号时显示)
|
||||
if (this.accountList.length > 1) {
|
||||
Row() {
|
||||
Text('发信账号').fontSize(13).fontColor('#999999').width(72)
|
||||
Text('发信账号').fontSize(13).fontColor(Theme.textSubtle).width(72)
|
||||
// 显示当前选中的账号名
|
||||
Text(this.getSelectedAccountName())
|
||||
.fontSize(13).fontColor('#1A73E8')
|
||||
.fontSize(13).fontColor(Theme.accent)
|
||||
.layoutWeight(1)
|
||||
.onClick(() => { this.showAccountPicker = !this.showAccountPicker; })
|
||||
Text(' ▾').fontSize(12).fontColor('#999999')
|
||||
Text(' ▾').fontSize(12).fontColor(Theme.textSubtle)
|
||||
.onClick(() => { this.showAccountPicker = !this.showAccountPicker; })
|
||||
}
|
||||
.width('100%').height(40).padding({ left: 12, right: 12 })
|
||||
.backgroundColor('#FFFFFF')
|
||||
.backgroundColor(Theme.surface)
|
||||
|
||||
// 账号选择下拉
|
||||
if (this.showAccountPicker) {
|
||||
@ -224,15 +225,15 @@ struct ComposePage {
|
||||
Row() {
|
||||
Text(acct.displayName)
|
||||
.fontSize(13)
|
||||
.fontColor(this.selectedAccountId === acct.id ? '#1A73E8' : '#333333')
|
||||
.fontColor(this.selectedAccountId === acct.id ? Theme.accent : Theme.textPrimary)
|
||||
.fontWeight(this.selectedAccountId === acct.id ? FontWeight.Bold : FontWeight.Normal)
|
||||
.layoutWeight(1)
|
||||
if (this.selectedAccountId === acct.id) {
|
||||
Text('✓').fontSize(14).fontColor('#1A73E8')
|
||||
Text('✓').fontSize(14).fontColor(Theme.accent)
|
||||
}
|
||||
}
|
||||
.width('100%').height(40).padding({ left: 40, right: 12 })
|
||||
.backgroundColor(this.selectedAccountId === acct.id ? '#F0F7FF' : '#FFFFFF')
|
||||
.backgroundColor(this.selectedAccountId === acct.id ? Theme.accentSoft : Theme.surface)
|
||||
.onClick(() => {
|
||||
this.selectedAccountId = acct.id;
|
||||
this.showAccountPicker = false;
|
||||
@ -240,38 +241,38 @@ struct ComposePage {
|
||||
}, (acct: AccountInfo) => acct.id)
|
||||
}
|
||||
|
||||
Divider().color('#F0F0F0')
|
||||
Divider().color(Theme.border)
|
||||
}
|
||||
|
||||
// 收件人
|
||||
Row() {
|
||||
Text('收件人').fontSize(14).fontColor('#999999').width(60)
|
||||
Text('收件人').fontSize(14).fontColor(Theme.textSubtle).width(60)
|
||||
TextInput({ placeholder: 'name@path.session', text: this.to })
|
||||
.layoutWeight(1).fontSize(14).backgroundColor('#00000000')
|
||||
.onChange((v: string) => { this.to = v; })
|
||||
}
|
||||
.width('100%').height(48).padding({ left: 12, right: 12 })
|
||||
.backgroundColor('#FFFFFF')
|
||||
.backgroundColor(Theme.surface)
|
||||
|
||||
Divider().color('#F0F0F0')
|
||||
Divider().color(Theme.border)
|
||||
|
||||
// 主题
|
||||
Row() {
|
||||
Text('主题').fontSize(14).fontColor('#999999').width(60)
|
||||
Text('主题').fontSize(14).fontColor(Theme.textSubtle).width(60)
|
||||
TextInput({ placeholder: '邮件主题', text: this.subject })
|
||||
.layoutWeight(1).fontSize(14).backgroundColor('#00000000')
|
||||
.onChange((v: string) => { this.subject = v; })
|
||||
}
|
||||
.width('100%').height(48).padding({ left: 12, right: 12 })
|
||||
.backgroundColor('#FFFFFF')
|
||||
.backgroundColor(Theme.surface)
|
||||
|
||||
Divider().color('#F0F0F0')
|
||||
Divider().color(Theme.border)
|
||||
|
||||
// 正文
|
||||
TextArea({ placeholder: '输入邮件正文…' })
|
||||
.layoutWeight(1).width('100%')
|
||||
.fontSize(14)
|
||||
.backgroundColor('#FFFFFF')
|
||||
.backgroundColor(Theme.surface)
|
||||
.onChange((v: string) => { this.body = v; })
|
||||
|
||||
// 附件区
|
||||
@ -280,16 +281,16 @@ struct ComposePage {
|
||||
ForEach(this.attachmentNames, (name: string, idx: number) => {
|
||||
Row() {
|
||||
Text('📎 ' + name)
|
||||
.fontSize(12).fontColor('#333333')
|
||||
.fontSize(12).fontColor(Theme.textPrimary)
|
||||
.layoutWeight(1)
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
Text('✕')
|
||||
.fontSize(14).fontColor('#FF4444')
|
||||
.fontSize(14).fontColor(Theme.danger)
|
||||
.onClick(() => { this.removeAttachment(idx); })
|
||||
}
|
||||
.width('100%').height(32)
|
||||
.padding({ left: 12, right: 12 })
|
||||
.backgroundColor('#F8F9FA')
|
||||
.backgroundColor(Theme.pageBg)
|
||||
}, (_name: string, idx: number) => idx.toString())
|
||||
}
|
||||
.width('100%')
|
||||
@ -298,24 +299,24 @@ struct ComposePage {
|
||||
// 底部工具栏
|
||||
Row() {
|
||||
Text('📎 添加附件')
|
||||
.fontSize(13).fontColor('#1A73E8')
|
||||
.fontSize(13).fontColor(Theme.accent)
|
||||
.onClick(() => { this.pickAttachment(); })
|
||||
|
||||
if (this.uploading) {
|
||||
Text(this.status)
|
||||
.fontSize(11).fontColor('#666666')
|
||||
.fontSize(11).fontColor(Theme.textMuted)
|
||||
.margin({ left: 12 })
|
||||
} else if (this.status.length > 0) {
|
||||
Text(this.status)
|
||||
.fontSize(11).fontColor('#4CAF50')
|
||||
.fontSize(11).fontColor(Theme.approve)
|
||||
.margin({ left: 12 })
|
||||
}
|
||||
}
|
||||
.width('100%').height(44)
|
||||
.padding({ left: 12, right: 12 })
|
||||
.backgroundColor('#FFFFFF')
|
||||
.backgroundColor(Theme.surface)
|
||||
}
|
||||
.width('100%').height('100%')
|
||||
.backgroundColor('#F5F7FA')
|
||||
.backgroundColor(Theme.pageBg)
|
||||
}
|
||||
}
|
||||
@ -3,6 +3,7 @@
|
||||
* 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 { promptAction, router } from '@kit.ArkUI';
|
||||
@ -64,12 +65,12 @@ struct InboxPage {
|
||||
// 顶栏
|
||||
Row() {
|
||||
Text('收件箱')
|
||||
.fontSize(20).fontWeight(FontWeight.Bold).fontColor('#333333')
|
||||
.fontSize(20).fontWeight(FontWeight.Bold).fontColor(Theme.textPrimary)
|
||||
Blank()
|
||||
if (this.unread > 0) {
|
||||
Text(this.unread + ' 未读')
|
||||
.fontSize(13).fontColor('#FFFFFF')
|
||||
.backgroundColor('#FF4444')
|
||||
.fontSize(13).fontColor(Theme.surface)
|
||||
.backgroundColor(Theme.danger)
|
||||
.borderRadius(10)
|
||||
.padding({ left: 8, right: 8, top: 2, bottom: 2 })
|
||||
}
|
||||
@ -77,26 +78,26 @@ struct InboxPage {
|
||||
.width('100%')
|
||||
.height(56)
|
||||
.padding({ left: 16, right: 16 })
|
||||
.backgroundColor('#FFFFFF')
|
||||
.backgroundColor(Theme.surface)
|
||||
|
||||
// 列表
|
||||
if (this.loading) {
|
||||
Column() {
|
||||
LoadingProgress().width(40).height(40)
|
||||
Text('加载中…').fontSize(14).fontColor('#999999').margin({ top: 8 })
|
||||
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('#FF4444')
|
||||
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('#999999')
|
||||
Text('📭 收件箱为空').fontSize(16).fontColor(Theme.textSubtle)
|
||||
}
|
||||
.width('100%').height('80%')
|
||||
.justifyContent(FlexAlign.Center)
|
||||
@ -107,7 +108,7 @@ struct InboxPage {
|
||||
this.MailItem(mail)
|
||||
}
|
||||
.height(80)
|
||||
.backgroundColor(mail.status === 'unread' ? '#F0F7FF' : '#FFFFFF')
|
||||
.backgroundColor(mail.status === 'unread' ? Theme.accentSoft : Theme.surface)
|
||||
.onClick(() => {
|
||||
this.getUIContext().getRouter().pushUrl({
|
||||
url: 'pages/MailDetailPage',
|
||||
@ -118,7 +119,7 @@ struct InboxPage {
|
||||
}
|
||||
.width('100%')
|
||||
.layoutWeight(1)
|
||||
.divider({ strokeWidth: 1, color: '#EEEEEE', startMargin: 16, endMargin: 16 })
|
||||
.divider({ strokeWidth: 1, color: Theme.border, startMargin: 16, endMargin: 16 })
|
||||
}
|
||||
|
||||
// 底部 Tab 栏(预留系统导航栏空间)
|
||||
@ -126,15 +127,15 @@ struct InboxPage {
|
||||
Row() {
|
||||
Column() {
|
||||
Text('📬').fontSize(20)
|
||||
Text('收件箱').fontSize(11).fontColor('#1A73E8')
|
||||
Text('收件箱').fontSize(11).fontColor(Theme.accent)
|
||||
}
|
||||
.layoutWeight(1).height('100%')
|
||||
.justifyContent(FlexAlign.Center)
|
||||
.backgroundColor('#E3F2FD')
|
||||
.backgroundColor(Theme.accentSoft)
|
||||
|
||||
Column() {
|
||||
Text('💬').fontSize(20)
|
||||
Text('会话').fontSize(11).fontColor('#666666')
|
||||
Text('会话').fontSize(11).fontColor(Theme.textMuted)
|
||||
}
|
||||
.layoutWeight(1).height('100%')
|
||||
.justifyContent(FlexAlign.Center)
|
||||
@ -144,18 +145,18 @@ struct InboxPage {
|
||||
|
||||
Column() {
|
||||
Text('👤').fontSize(20)
|
||||
Text('联系人').fontSize(11).fontColor('#666666')
|
||||
Text('联系人').fontSize(11).fontColor(Theme.textMuted)
|
||||
}
|
||||
.layoutWeight(1).height('100%')
|
||||
.justifyContent(FlexAlign.Center)
|
||||
}
|
||||
.width('100%').height(56)
|
||||
.backgroundColor('#FFFFFF')
|
||||
.backgroundColor(Theme.surface)
|
||||
}
|
||||
.width('100%')
|
||||
}
|
||||
.width('100%').height('100%')
|
||||
.backgroundColor('#F5F7FA')
|
||||
.backgroundColor(Theme.pageBg)
|
||||
}
|
||||
|
||||
@Builder
|
||||
@ -164,7 +165,7 @@ struct InboxPage {
|
||||
// 未读圆点
|
||||
Column() {
|
||||
if (mail.status === 'unread') {
|
||||
Circle({ width: 8, height: 8 }).fill('#1A73E8')
|
||||
Circle({ width: 8, height: 8 }).fill(Theme.accent)
|
||||
}
|
||||
}
|
||||
.width(20).height('100%')
|
||||
@ -173,16 +174,16 @@ struct InboxPage {
|
||||
// 正文
|
||||
Column() {
|
||||
Row() {
|
||||
Text(mail.from_name).fontSize(14).fontWeight(mail.is_read ? FontWeight.Normal : FontWeight.Bold).fontColor('#333333')
|
||||
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('#999999')
|
||||
Text(mail.created_at).fontSize(11).fontColor(Theme.textSubtle)
|
||||
}
|
||||
.width('100%')
|
||||
|
||||
Text(mail.subject).fontSize(13).fontColor('#333333')
|
||||
Text(mail.subject).fontSize(13).fontColor(Theme.textPrimary)
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.margin({ top: 2 })
|
||||
Text(mail.body_preview).fontSize(12).fontColor('#999999')
|
||||
Text(mail.body_preview).fontSize(12).fontColor(Theme.textSubtle)
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.margin({ top: 2 })
|
||||
}
|
||||
@ -198,8 +199,8 @@ struct InboxPage {
|
||||
// 权限档位徽标
|
||||
if (mail.permission_mode.length > 0) {
|
||||
Text(mail.permission_mode)
|
||||
.fontSize(10).fontColor('#666666')
|
||||
.backgroundColor('#EEEEEE').borderRadius(4)
|
||||
.fontSize(10).fontColor(Theme.textMuted)
|
||||
.backgroundColor(Theme.border).borderRadius(4)
|
||||
.padding({ left: 4, right: 4, top: 1, bottom: 1 })
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
* 服务器地址可配置(跨设备/模拟器场景)
|
||||
*/
|
||||
import { ApiClient, ApiError } from '../api/ApiClient';
|
||||
import { Theme } from '../common/Theme';
|
||||
import { AuthApi } from '../api/AuthApi';
|
||||
import { AccountManager } from '../api/AccountManager';
|
||||
import { SseService } from '../api/SseService';
|
||||
@ -145,10 +146,10 @@ struct LoginPage {
|
||||
Text('AgentMail')
|
||||
.fontSize(28)
|
||||
.fontWeight(FontWeight.Bold)
|
||||
.fontColor('#1A73E8')
|
||||
.fontColor(Theme.accent)
|
||||
Text('邮件驱动 · 多智能体协作平台')
|
||||
.fontSize(14)
|
||||
.fontColor('#666666')
|
||||
.fontColor(Theme.textMuted)
|
||||
.margin({ top: 6 })
|
||||
}
|
||||
.margin({ top: 100, bottom: 48 })
|
||||
@ -162,10 +163,10 @@ struct LoginPage {
|
||||
|
||||
// 切换登录方式
|
||||
Row() {
|
||||
Text('账号密码').fontSize(14).fontColor(this.mode === 0 ? '#1A73E8' : '#999999')
|
||||
Text('账号密码').fontSize(14).fontColor(this.mode === 0 ? Theme.accent : Theme.textSubtle)
|
||||
.onClick(() => { this.mode = 0; })
|
||||
Text(' | ').fontSize(14).fontColor('#cccccc')
|
||||
Text('用户密钥').fontSize(14).fontColor(this.mode === 1 ? '#1A73E8' : '#999999')
|
||||
Text('用户密钥').fontSize(14).fontColor(this.mode === 1 ? Theme.accent : Theme.textSubtle)
|
||||
.onClick(() => { this.mode = 1; })
|
||||
}
|
||||
.margin({ bottom: 16 })
|
||||
@ -186,7 +187,7 @@ struct LoginPage {
|
||||
|
||||
Button(this.loading ? '登录中…' : '登 录')
|
||||
.width('85%').height(48)
|
||||
.backgroundColor('#1A73E8')
|
||||
.backgroundColor(Theme.accent)
|
||||
.fontSize(16)
|
||||
.enabled(!this.loading)
|
||||
.onClick(() => {
|
||||
@ -202,12 +203,12 @@ struct LoginPage {
|
||||
// 登录成功 → 进入主界面(占位,后续 M2 替换)
|
||||
Blank()
|
||||
Text('登录成功:' + this.me.username)
|
||||
.fontSize(16).fontColor('#1A73E8').margin({ bottom: 60 })
|
||||
.fontSize(16).fontColor(Theme.accent).margin({ bottom: 60 })
|
||||
}
|
||||
}
|
||||
.width('100%')
|
||||
.height('100%')
|
||||
.backgroundColor('#F5F7FA')
|
||||
.backgroundColor(Theme.pageBg)
|
||||
.alignItems(HorizontalAlign.Center)
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,7 @@
|
||||
* 路由参数:mail_id
|
||||
*/
|
||||
import { ApiClient, ApiError } from '../api/ApiClient';
|
||||
import { Theme } from '../common/Theme';
|
||||
import { MailApi } from '../api/MailApi';
|
||||
import { AccountManager, AccountInfo } from '../api/AccountManager';
|
||||
import { MailDetail, SendMailRequest } from '../model/Models';
|
||||
@ -116,34 +117,34 @@ struct MailDetailPage {
|
||||
// 顶栏
|
||||
Row() {
|
||||
Text('‹')
|
||||
.fontSize(24).fontColor('#1A73E8')
|
||||
.fontSize(24).fontColor(Theme.accent)
|
||||
.width(40).height(40)
|
||||
.textAlign(TextAlign.Center)
|
||||
.onClick(() => {
|
||||
this.getUIContext().getRouter().back();
|
||||
})
|
||||
Text(this.subject.length > 20 ? this.subject.substring(0, 20) + '…' : this.subject)
|
||||
.fontSize(16).fontWeight(FontWeight.Bold).fontColor('#333333')
|
||||
.fontSize(16).fontWeight(FontWeight.Bold).fontColor(Theme.textPrimary)
|
||||
.layoutWeight(1)
|
||||
Text(this.permissionMode)
|
||||
.fontSize(11).fontColor('#666666')
|
||||
.fontSize(11).fontColor(Theme.textMuted)
|
||||
.backgroundColor(this.permissionMode === 'full' ? '#E8F5E9' : '#FFF3E0')
|
||||
.borderRadius(4).padding({ left: 6, right: 6, top: 2, bottom: 2 })
|
||||
}
|
||||
.width('100%').height(56)
|
||||
.padding({ left: 8, right: 12 })
|
||||
.backgroundColor('#FFFFFF')
|
||||
.backgroundColor(Theme.surface)
|
||||
|
||||
if (this.loading) {
|
||||
Column() {
|
||||
LoadingProgress().width(40).height(40)
|
||||
Text('加载中…').fontSize(14).fontColor('#999999').margin({ top: 8 })
|
||||
Text('加载中…').fontSize(14).fontColor(Theme.textSubtle).margin({ top: 8 })
|
||||
}
|
||||
.width('100%').layoutWeight(1)
|
||||
.justifyContent(FlexAlign.Center)
|
||||
} else if (this.error.length > 0) {
|
||||
Column() {
|
||||
Text(this.error).fontSize(14).fontColor('#FF4444')
|
||||
Text(this.error).fontSize(14).fontColor(Theme.danger)
|
||||
Button('重试').margin({ top: 12 }).onClick(() => { this.loadMail(this.mailId); })
|
||||
}
|
||||
.width('100%').layoutWeight(1)
|
||||
@ -154,37 +155,37 @@ struct MailDetailPage {
|
||||
// 元信息卡片
|
||||
Column() {
|
||||
Row() {
|
||||
Text('发件人').fontSize(12).fontColor('#999999').width(60)
|
||||
Text(this.fromName).fontSize(14).fontColor('#333333')
|
||||
Text('发件人').fontSize(12).fontColor(Theme.textSubtle).width(60)
|
||||
Text(this.fromName).fontSize(14).fontColor(Theme.textPrimary)
|
||||
}.width('100%').margin({ bottom: 6 })
|
||||
|
||||
Row() {
|
||||
Text('收件人').fontSize(12).fontColor('#999999').width(60)
|
||||
Text(this.toName).fontSize(14).fontColor('#333333')
|
||||
Text('收件人').fontSize(12).fontColor(Theme.textSubtle).width(60)
|
||||
Text(this.toName).fontSize(14).fontColor(Theme.textPrimary)
|
||||
}.width('100%').margin({ bottom: 6 })
|
||||
|
||||
if (this.sessionAlias.length > 0) {
|
||||
Row() {
|
||||
Text('会话').fontSize(12).fontColor('#999999').width(60)
|
||||
Text(this.sessionAlias).fontSize(13).fontColor('#1A73E8')
|
||||
Text('会话').fontSize(12).fontColor(Theme.textSubtle).width(60)
|
||||
Text(this.sessionAlias).fontSize(13).fontColor(Theme.accent)
|
||||
}.width('100%').margin({ bottom: 6 })
|
||||
}
|
||||
|
||||
Row() {
|
||||
Text('时间').fontSize(12).fontColor('#999999').width(60)
|
||||
Text(this.createdAt).fontSize(13).fontColor('#666666')
|
||||
Text('时间').fontSize(12).fontColor(Theme.textSubtle).width(60)
|
||||
Text(this.createdAt).fontSize(13).fontColor(Theme.textMuted)
|
||||
}.width('100%').margin({ bottom: 6 })
|
||||
|
||||
Row() {
|
||||
Text('权限').fontSize(12).fontColor('#999999').width(60)
|
||||
Text('权限').fontSize(12).fontColor(Theme.textSubtle).width(60)
|
||||
if (this.switchingPerm) {
|
||||
LoadingProgress().width(16).height(16)
|
||||
} else {
|
||||
ForEach(['plan', 'workspace', 'full'], (mode: string) => {
|
||||
Text(mode)
|
||||
.fontSize(11)
|
||||
.fontColor(this.permissionMode === mode ? '#FFFFFF' : '#666666')
|
||||
.backgroundColor(this.permissionMode === mode ? '#1A73E8' : '#F0F0F0')
|
||||
.fontColor(this.permissionMode === mode ? Theme.surface : Theme.textMuted)
|
||||
.backgroundColor(this.permissionMode === mode ? Theme.accent : Theme.border)
|
||||
.borderRadius(4)
|
||||
.padding({ left: 8, right: 8, top: 3, bottom: 3 })
|
||||
.margin({ right: 6 })
|
||||
@ -195,7 +196,7 @@ struct MailDetailPage {
|
||||
}
|
||||
.width('100%')
|
||||
.padding(16)
|
||||
.backgroundColor('#F8F9FA')
|
||||
.backgroundColor(Theme.pageBg)
|
||||
.borderRadius(8)
|
||||
.margin({ left: 12, right: 12, top: 8 })
|
||||
|
||||
@ -203,7 +204,7 @@ struct MailDetailPage {
|
||||
Text(this.subject)
|
||||
.fontSize(20)
|
||||
.fontWeight(FontWeight.Bold)
|
||||
.fontColor('#333333')
|
||||
.fontColor(Theme.textPrimary)
|
||||
.width('100%')
|
||||
.padding({ left: 16, right: 16, top: 16, bottom: 8 })
|
||||
|
||||
@ -225,7 +226,7 @@ struct MailDetailPage {
|
||||
Row() {
|
||||
Button('↩ 回复')
|
||||
.width('90%').height(44)
|
||||
.backgroundColor('#1A73E8')
|
||||
.backgroundColor(Theme.accent)
|
||||
.fontSize(15)
|
||||
.onClick(() => {
|
||||
this.showReplyBox = true;
|
||||
@ -233,7 +234,7 @@ struct MailDetailPage {
|
||||
}
|
||||
.width('100%').height(56)
|
||||
.justifyContent(FlexAlign.Center)
|
||||
.backgroundColor('#FFFFFF')
|
||||
.backgroundColor(Theme.surface)
|
||||
|
||||
// 回复弹层
|
||||
if (this.showReplyBox) {
|
||||
@ -247,7 +248,7 @@ struct MailDetailPage {
|
||||
// 回复框
|
||||
Column() {
|
||||
Text('回复给 ' + this.fromName)
|
||||
.fontSize(16).fontWeight(FontWeight.Bold).fontColor('#333333')
|
||||
.fontSize(16).fontWeight(FontWeight.Bold).fontColor(Theme.textPrimary)
|
||||
.margin({ bottom: 12 })
|
||||
|
||||
TextArea({ placeholder: '输入回复内容…' })
|
||||
@ -258,8 +259,8 @@ struct MailDetailPage {
|
||||
Row() {
|
||||
Button('取消')
|
||||
.width(80).height(36)
|
||||
.backgroundColor('#F5F5F5')
|
||||
.fontColor('#666666')
|
||||
.backgroundColor(Theme.surfaceMuted)
|
||||
.fontColor(Theme.textMuted)
|
||||
.fontSize(13)
|
||||
.onClick(() => { this.showReplyBox = false; })
|
||||
|
||||
@ -267,7 +268,7 @@ struct MailDetailPage {
|
||||
|
||||
Button(this.sending ? '发送中…' : '发送')
|
||||
.width(80).height(36)
|
||||
.backgroundColor('#1A73E8')
|
||||
.backgroundColor(Theme.accent)
|
||||
.fontSize(13)
|
||||
.enabled(!this.sending && this.replyBody.length > 0)
|
||||
.onClick(() => { this.doReply(); })
|
||||
@ -278,7 +279,7 @@ struct MailDetailPage {
|
||||
.width('100%')
|
||||
.height('60%')
|
||||
.padding(16)
|
||||
.backgroundColor('#FFFFFF')
|
||||
.backgroundColor(Theme.surface)
|
||||
.borderRadius({ topLeft: 12, topRight: 12 })
|
||||
}
|
||||
.width('100%').height('100%')
|
||||
@ -287,7 +288,7 @@ struct MailDetailPage {
|
||||
}
|
||||
}
|
||||
.width('100%').height('100%')
|
||||
.backgroundColor('#F5F7FA')
|
||||
.backgroundColor(Theme.pageBg)
|
||||
}
|
||||
|
||||
async doReply(): Promise<void> {
|
||||
|
||||
@ -168,43 +168,43 @@ struct InboxTab {
|
||||
Row() {
|
||||
Column() {
|
||||
Row() {
|
||||
Text('收件箱').fontSize(20).fontWeight(FontWeight.Bold).fontColor('#333333')
|
||||
Text('收件箱').fontSize(20).fontWeight(FontWeight.Bold).fontColor(Theme.textPrimary)
|
||||
if (this.accountList.length > 1) {
|
||||
Text(' ▾').fontSize(14).fontColor('#999999')
|
||||
Text(' ▾').fontSize(14).fontColor(Theme.textSubtle)
|
||||
.onClick(() => { this.showAccountPicker = !this.showAccountPicker; })
|
||||
}
|
||||
}
|
||||
Text(this.accountFilter === 'all' ? '全部邮箱' : this.accountName)
|
||||
.fontSize(11).fontColor('#999999').margin({ top: 2 })
|
||||
.fontSize(11).fontColor(Theme.textSubtle).margin({ top: 2 })
|
||||
.onClick(() => { if (this.accountList.length > 1) { this.showAccountPicker = !this.showAccountPicker; } })
|
||||
}
|
||||
.alignItems(HorizontalAlign.Start)
|
||||
Blank()
|
||||
if (this.unread > 0) {
|
||||
Text(this.unread + ' 未读')
|
||||
.fontSize(13).fontColor('#FFFFFF')
|
||||
.backgroundColor('#FF4444')
|
||||
.fontSize(13).fontColor(Theme.surface)
|
||||
.backgroundColor(Theme.danger)
|
||||
.borderRadius(10)
|
||||
.padding({ left: 8, right: 8, top: 2, bottom: 2 })
|
||||
}
|
||||
Text('⚙')
|
||||
.fontSize(20).fontColor('#666666')
|
||||
.fontSize(20).fontColor(Theme.textMuted)
|
||||
.width(36).height(36).textAlign(TextAlign.Center)
|
||||
.onClick(() => {
|
||||
this.getUIContext().getRouter().pushUrl({ url: 'pages/SettingsPage' });
|
||||
})
|
||||
}
|
||||
.width('100%').height(56).padding({ left: 16, right: 8 })
|
||||
.backgroundColor('#FFFFFF')
|
||||
.backgroundColor(Theme.surface)
|
||||
|
||||
// 账号选择器下拉
|
||||
if (this.showAccountPicker && this.accountList.length > 1) {
|
||||
Column() {
|
||||
Text('全部邮箱')
|
||||
.fontSize(14).fontColor(this.accountFilter === 'all' ? '#1A73E8' : '#333333')
|
||||
.fontSize(14).fontColor(this.accountFilter === 'all' ? Theme.accent : Theme.textPrimary)
|
||||
.fontWeight(this.accountFilter === 'all' ? FontWeight.Bold : FontWeight.Normal)
|
||||
.width('100%').height(40).padding({ left: 16 })
|
||||
.backgroundColor(this.accountFilter === 'all' ? '#F0F7FF' : '#FFFFFF')
|
||||
.backgroundColor(this.accountFilter === 'all' ? Theme.accentSoft : Theme.surface)
|
||||
.onClick(() => {
|
||||
this.accountFilter = 'all';
|
||||
this.accountName = '全部邮箱';
|
||||
@ -216,16 +216,16 @@ struct InboxTab {
|
||||
Column() {
|
||||
Text(acct.displayName)
|
||||
.fontSize(14)
|
||||
.fontColor(this.accountFilter === acct.id ? '#1A73E8' : '#333333')
|
||||
.fontColor(this.accountFilter === acct.id ? Theme.accent : Theme.textPrimary)
|
||||
.fontWeight(this.accountFilter === acct.id ? FontWeight.Bold : FontWeight.Normal)
|
||||
Text(acct.username)
|
||||
.fontSize(11).fontColor('#999999')
|
||||
.fontSize(11).fontColor(Theme.textSubtle)
|
||||
}
|
||||
.alignItems(HorizontalAlign.Start)
|
||||
.layoutWeight(1)
|
||||
}
|
||||
.width('100%').height(48).padding({ left: 16 })
|
||||
.backgroundColor(this.accountFilter === acct.id ? '#F0F7FF' : '#FFFFFF')
|
||||
.backgroundColor(this.accountFilter === acct.id ? Theme.accentSoft : Theme.surface)
|
||||
.onClick(() => {
|
||||
this.accountFilter = acct.id;
|
||||
this.accountName = acct.displayName;
|
||||
@ -235,8 +235,8 @@ struct InboxTab {
|
||||
}, (acct: AccountInfo) => acct.id)
|
||||
}
|
||||
.width('100%')
|
||||
.backgroundColor('#FFFFFF')
|
||||
.border({ width: { bottom: 1 }, color: '#EEEEEE' })
|
||||
.backgroundColor(Theme.surface)
|
||||
.border({ width: { bottom: 1 }, color: Theme.border })
|
||||
}
|
||||
|
||||
if (this.loading) {
|
||||
@ -246,13 +246,13 @@ struct InboxTab {
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else if (this.error.length > 0) {
|
||||
Column() {
|
||||
Text(this.error).fontSize(14).fontColor('#FF4444')
|
||||
Text(this.error).fontSize(14).fontColor(Theme.danger)
|
||||
Button('重试').margin({ top: 12 }).onClick(() => { this.loadData(); })
|
||||
}
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else if (this.mails.length === 0) {
|
||||
Column() {
|
||||
Text('📭 收件箱为空').fontSize(16).fontColor('#999999')
|
||||
Text('📭 收件箱为空').fontSize(16).fontColor(Theme.textSubtle)
|
||||
}
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else {
|
||||
@ -262,7 +262,7 @@ struct InboxTab {
|
||||
this.MailItem(mail)
|
||||
}
|
||||
.height(80)
|
||||
.backgroundColor(mail.status === 'unread' ? '#F0F7FF' : '#FFFFFF')
|
||||
.backgroundColor(mail.status === 'unread' ? Theme.accentSoft : Theme.surface)
|
||||
.onClick(() => {
|
||||
const params: MailDetailParams = {
|
||||
mail_id: mail.mail_id,
|
||||
@ -276,24 +276,24 @@ struct InboxTab {
|
||||
}, (mail: MailSummary) => mail.source_account_id + ':' + mail.mail_id)
|
||||
}
|
||||
.width('100%').layoutWeight(1)
|
||||
.divider({ strokeWidth: 1, color: '#EEEEEE', startMargin: 16, endMargin: 16 })
|
||||
.divider({ strokeWidth: 1, color: Theme.border, startMargin: 16, endMargin: 16 })
|
||||
}
|
||||
|
||||
// 底部信息栏 + 悬浮写邮件按钮
|
||||
Stack({ alignContent: Alignment.BottomEnd }) {
|
||||
Row() {
|
||||
Text('共 ' + this.total + ' 封').fontSize(12).fontColor('#999999')
|
||||
Text('共 ' + this.total + ' 封').fontSize(12).fontColor(Theme.textSubtle)
|
||||
Blank()
|
||||
Text('未读 ' + this.unread).fontSize(12).fontColor('#999999')
|
||||
Text('未读 ' + this.unread).fontSize(12).fontColor(Theme.textSubtle)
|
||||
}
|
||||
.width('100%').height(36).padding({ left: 16, right: 16 })
|
||||
.backgroundColor('#FFFFFF')
|
||||
.backgroundColor(Theme.surface)
|
||||
|
||||
Text('+')
|
||||
.fontSize(28).fontColor('#FFFFFF')
|
||||
.fontSize(28).fontColor(Theme.surface)
|
||||
.width(56).height(56)
|
||||
.borderRadius(28)
|
||||
.backgroundColor('#1A73E8')
|
||||
.backgroundColor(Theme.accent)
|
||||
.textAlign(TextAlign.Center)
|
||||
.margin({ right: 20, bottom: 44 })
|
||||
.onClick(() => {
|
||||
@ -303,7 +303,7 @@ struct InboxTab {
|
||||
.width('100%')
|
||||
}
|
||||
.width('100%').height('100%')
|
||||
.backgroundColor('#F5F7FA')
|
||||
.backgroundColor(Theme.pageBg)
|
||||
.bindSheet($$this.composeVisible, this.ComposeSheet())
|
||||
}
|
||||
|
||||
@ -312,16 +312,16 @@ struct InboxTab {
|
||||
@Builder
|
||||
ComposeSheet() {
|
||||
Column() {
|
||||
Text('写邮件').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#333333')
|
||||
Text('写邮件').fontSize(16).fontWeight(FontWeight.Bold).fontColor(Theme.textPrimary)
|
||||
.margin({ top: 16, bottom: 8 })
|
||||
Button('新建邮件')
|
||||
.width('90%').height(44).backgroundColor('#1A73E8')
|
||||
.width('90%').height(44).backgroundColor(Theme.accent)
|
||||
.onClick(() => {
|
||||
this.composeVisible = false;
|
||||
this.openCompose();
|
||||
})
|
||||
Button('取消')
|
||||
.width('90%').height(40).backgroundColor('#F5F5F5').fontColor('#666666')
|
||||
.width('90%').height(40).backgroundColor(Theme.surfaceMuted).fontColor(Theme.textMuted)
|
||||
.margin({ top: 8 })
|
||||
.onClick(() => { this.composeVisible = false; })
|
||||
}
|
||||
@ -333,30 +333,30 @@ struct InboxTab {
|
||||
Row() {
|
||||
Column() {
|
||||
if (mail.status === 'unread') {
|
||||
Circle({ width: 8, height: 8 }).fill('#1A73E8')
|
||||
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.status === 'unread' ? FontWeight.Bold : FontWeight.Normal).fontColor('#333333')
|
||||
Text(mail.from_name).fontSize(14).fontWeight(mail.status === 'unread' ? FontWeight.Bold : FontWeight.Normal).fontColor(Theme.textPrimary)
|
||||
if (this.accountFilter === 'all' && mail.source_account_name.length > 0) {
|
||||
Text(mail.source_account_name)
|
||||
.fontSize(10).fontColor('#1A73E8')
|
||||
.fontSize(10).fontColor(Theme.accent)
|
||||
.backgroundColor('#E8F0FE').borderRadius(4)
|
||||
.padding({ left: 5, right: 5, top: 1, bottom: 1 })
|
||||
.margin({ left: 6 })
|
||||
}
|
||||
Blank()
|
||||
Text(mail.created_at).fontSize(11).fontColor('#999999')
|
||||
Text(mail.created_at).fontSize(11).fontColor(Theme.textSubtle)
|
||||
}
|
||||
.width('100%')
|
||||
|
||||
Text(mail.subject).fontSize(13).fontColor('#333333')
|
||||
Text(mail.subject).fontSize(13).fontColor(Theme.textPrimary)
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.margin({ top: 2 })
|
||||
Text(mail.body_preview).fontSize(12).fontColor('#999999')
|
||||
Text(mail.body_preview).fontSize(12).fontColor(Theme.textSubtle)
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.margin({ top: 2 })
|
||||
}
|
||||
@ -366,8 +366,8 @@ struct InboxTab {
|
||||
|
||||
if (mail.permission_mode.length > 0) {
|
||||
Text(mail.permission_mode)
|
||||
.fontSize(10).fontColor('#666666')
|
||||
.backgroundColor('#EEEEEE').borderRadius(4)
|
||||
.fontSize(10).fontColor(Theme.textMuted)
|
||||
.backgroundColor(Theme.border).borderRadius(4)
|
||||
.padding({ left: 4, right: 4, top: 1, bottom: 1 })
|
||||
}
|
||||
}
|
||||
@ -412,10 +412,10 @@ struct SessionsTab {
|
||||
build() {
|
||||
Column() {
|
||||
Row() {
|
||||
Text('会话').fontSize(20).fontWeight(FontWeight.Bold).fontColor('#333333')
|
||||
Text('会话').fontSize(20).fontWeight(FontWeight.Bold).fontColor(Theme.textPrimary)
|
||||
}
|
||||
.width('100%').height(56).padding({ left: 16 })
|
||||
.backgroundColor('#FFFFFF')
|
||||
.backgroundColor(Theme.surface)
|
||||
|
||||
if (this.loading) {
|
||||
Column() {
|
||||
@ -424,12 +424,12 @@ struct SessionsTab {
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else if (this.error.length > 0) {
|
||||
Column() {
|
||||
Text(this.error).fontSize(14).fontColor('#FF4444')
|
||||
Text(this.error).fontSize(14).fontColor(Theme.danger)
|
||||
}
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else if (this.sessions.length === 0) {
|
||||
Column() {
|
||||
Text('📭 暂无会话').fontSize(16).fontColor('#999999')
|
||||
Text('📭 暂无会话').fontSize(16).fontColor(Theme.textSubtle)
|
||||
}
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else {
|
||||
@ -442,11 +442,11 @@ struct SessionsTab {
|
||||
}, (_s: Session, idx: number) => idx.toString())
|
||||
}
|
||||
.width('100%').layoutWeight(1)
|
||||
.divider({ strokeWidth: 1, color: '#EEEEEE', startMargin: 16, endMargin: 16 })
|
||||
.divider({ strokeWidth: 1, color: Theme.border, startMargin: 16, endMargin: 16 })
|
||||
}
|
||||
}
|
||||
.width('100%').height('100%')
|
||||
.backgroundColor('#F5F7FA')
|
||||
.backgroundColor(Theme.pageBg)
|
||||
}
|
||||
|
||||
@Builder
|
||||
@ -455,28 +455,28 @@ struct SessionsTab {
|
||||
Column() {
|
||||
Row() {
|
||||
Text(s.session_alias.length > 0 ? s.session_alias : ('会话 #' + (idx + 1)))
|
||||
.fontSize(14).fontWeight(FontWeight.Bold).fontColor('#333333')
|
||||
.fontSize(14).fontWeight(FontWeight.Bold).fontColor(Theme.textPrimary)
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.layoutWeight(1)
|
||||
if (s.max_rounds > 0) {
|
||||
Text(s.used_rounds + '/' + s.max_rounds)
|
||||
.fontSize(11).fontColor(s.used_rounds >= s.max_rounds ? '#FF4444' : '#666666')
|
||||
.backgroundColor(s.used_rounds >= s.max_rounds ? '#FFEBEE' : '#F5F5F5')
|
||||
.fontSize(11).fontColor(s.used_rounds >= s.max_rounds ? Theme.danger : Theme.textMuted)
|
||||
.backgroundColor(s.used_rounds >= s.max_rounds ? Theme.dangerBg : Theme.surfaceMuted)
|
||||
.borderRadius(4).padding({ left: 4, right: 4, top: 1, bottom: 1 })
|
||||
}
|
||||
}
|
||||
.width('100%')
|
||||
|
||||
Text('对方: ' + s.from_agent).fontSize(12).fontColor('#666666').margin({ top: 4 })
|
||||
Text('对方: ' + s.from_agent).fontSize(12).fontColor(Theme.textMuted).margin({ top: 4 })
|
||||
|
||||
Row() {
|
||||
Text(s.permission_mode.length > 0 ? s.permission_mode : '—')
|
||||
.fontSize(11).fontColor('#666666')
|
||||
.backgroundColor('#F0F0F0').borderRadius(4)
|
||||
.fontSize(11).fontColor(Theme.textMuted)
|
||||
.backgroundColor(Theme.border).borderRadius(4)
|
||||
.padding({ left: 4, right: 4, top: 1, bottom: 1 })
|
||||
Blank()
|
||||
Text(s.mail_count + ' 封').fontSize(11).fontColor('#999999')
|
||||
Text(' · ' + s.status).fontSize(11).fontColor(s.status === 'active' ? '#4CAF50' : '#999999')
|
||||
Text(s.mail_count + ' 封').fontSize(11).fontColor(Theme.textSubtle)
|
||||
Text(' · ' + s.status).fontSize(11).fontColor(s.status === 'active' ? Theme.approve : Theme.textSubtle)
|
||||
}
|
||||
.width('100%').margin({ top: 4 })
|
||||
}
|
||||
@ -525,10 +525,10 @@ struct ContactsTab {
|
||||
build() {
|
||||
Column() {
|
||||
Row() {
|
||||
Text('联系人').fontSize(20).fontWeight(FontWeight.Bold).fontColor('#333333')
|
||||
Text('联系人').fontSize(20).fontWeight(FontWeight.Bold).fontColor(Theme.textPrimary)
|
||||
}
|
||||
.width('100%').height(56).padding({ left: 16 })
|
||||
.backgroundColor('#FFFFFF')
|
||||
.backgroundColor(Theme.surface)
|
||||
|
||||
if (this.loading) {
|
||||
Column() {
|
||||
@ -537,13 +537,13 @@ struct ContactsTab {
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else if (this.error.length > 0) {
|
||||
Column() {
|
||||
Text(this.error).fontSize(14).fontColor('#FF4444')
|
||||
Text(this.error).fontSize(14).fontColor(Theme.danger)
|
||||
Button('重试').margin({ top: 12 }).onClick(() => { this.loadData(); })
|
||||
}
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else if (this.contacts.length === 0) {
|
||||
Column() {
|
||||
Text('📭 暂无联系人').fontSize(16).fontColor('#999999')
|
||||
Text('📭 暂无联系人').fontSize(16).fontColor(Theme.textSubtle)
|
||||
}
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
} else {
|
||||
@ -556,11 +556,11 @@ struct ContactsTab {
|
||||
}, (_c: Contact, idx: number) => idx.toString())
|
||||
}
|
||||
.width('100%').layoutWeight(1)
|
||||
.divider({ strokeWidth: 1, color: '#EEEEEE', startMargin: 16, endMargin: 16 })
|
||||
.divider({ strokeWidth: 1, color: Theme.border, startMargin: 16, endMargin: 16 })
|
||||
}
|
||||
}
|
||||
.width('100%').height('100%')
|
||||
.backgroundColor('#F5F7FA')
|
||||
.backgroundColor(Theme.pageBg)
|
||||
}
|
||||
|
||||
@Builder
|
||||
@ -568,12 +568,12 @@ struct ContactsTab {
|
||||
Row() {
|
||||
Column() {
|
||||
Row() {
|
||||
Text(c.agent_name).fontSize(14).fontWeight(FontWeight.Bold).fontColor('#333333')
|
||||
Text(c.agent_name).fontSize(14).fontWeight(FontWeight.Bold).fontColor(Theme.textPrimary)
|
||||
Blank()
|
||||
if (c.unread_count > 0) {
|
||||
Text(c.unread_count + '')
|
||||
.fontSize(11).fontColor('#FFFFFF')
|
||||
.backgroundColor('#FF4444')
|
||||
.fontSize(11).fontColor(Theme.surface)
|
||||
.backgroundColor(Theme.danger)
|
||||
.borderRadius(10).width(20).height(20)
|
||||
.textAlign(TextAlign.Center)
|
||||
}
|
||||
@ -581,18 +581,18 @@ struct ContactsTab {
|
||||
.width('100%')
|
||||
|
||||
Text(c.subject.length > 0 ? c.subject : c.session_alias)
|
||||
.fontSize(12).fontColor('#666666')
|
||||
.fontSize(12).fontColor(Theme.textMuted)
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.margin({ top: 3 })
|
||||
|
||||
Row() {
|
||||
Text(c.permission_mode.length > 0 ? c.permission_mode : '—')
|
||||
.fontSize(10).fontColor('#666666')
|
||||
.backgroundColor('#F0F0F0').borderRadius(4)
|
||||
.fontSize(10).fontColor(Theme.textMuted)
|
||||
.backgroundColor(Theme.border).borderRadius(4)
|
||||
.padding({ left: 4, right: 4, top: 1, bottom: 1 })
|
||||
Blank()
|
||||
Text(c.last_preview.length > 0 ? c.last_preview : '—')
|
||||
.fontSize(11).fontColor('#999999')
|
||||
.fontSize(11).fontColor(Theme.textSubtle)
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.layoutWeight(1)
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
* GET /me/sessions 展示所有会话摘要
|
||||
*/
|
||||
import { ApiClient, ApiError } from '../api/ApiClient';
|
||||
import { Theme } from '../common/Theme';
|
||||
import { MailApi } from '../api/MailApi';
|
||||
import { Session } from '../model/Models';
|
||||
|
||||
@ -49,29 +50,29 @@ struct SessionsPage {
|
||||
// 顶栏
|
||||
Row() {
|
||||
Text('会话')
|
||||
.fontSize(20).fontWeight(FontWeight.Bold).fontColor('#333333')
|
||||
.fontSize(20).fontWeight(FontWeight.Bold).fontColor(Theme.textPrimary)
|
||||
}
|
||||
.width('100%').height(56)
|
||||
.padding({ left: 16 })
|
||||
.backgroundColor('#FFFFFF')
|
||||
.backgroundColor(Theme.surface)
|
||||
|
||||
if (this.loading) {
|
||||
Column() {
|
||||
LoadingProgress().width(40).height(40)
|
||||
Text('加载中…').fontSize(14).fontColor('#999999').margin({ top: 8 })
|
||||
Text('加载中…').fontSize(14).fontColor(Theme.textSubtle).margin({ top: 8 })
|
||||
}
|
||||
.width('100%').layoutWeight(1)
|
||||
.justifyContent(FlexAlign.Center)
|
||||
} else if (this.error.length > 0) {
|
||||
Column() {
|
||||
Text(this.error).fontSize(14).fontColor('#FF4444')
|
||||
Text(this.error).fontSize(14).fontColor(Theme.danger)
|
||||
Button('重试').margin({ top: 12 }).onClick(() => { this.loadSessions(); })
|
||||
}
|
||||
.width('100%').layoutWeight(1)
|
||||
.justifyContent(FlexAlign.Center)
|
||||
} else if (this.sessions.length === 0) {
|
||||
Column() {
|
||||
Text('📭 暂无会话').fontSize(16).fontColor('#999999')
|
||||
Text('📭 暂无会话').fontSize(16).fontColor(Theme.textSubtle)
|
||||
}
|
||||
.width('100%').layoutWeight(1)
|
||||
.justifyContent(FlexAlign.Center)
|
||||
@ -85,18 +86,18 @@ struct SessionsPage {
|
||||
}, (s: Session) => s.session_id)
|
||||
}
|
||||
.width('100%').layoutWeight(1)
|
||||
.divider({ strokeWidth: 1, color: '#EEEEEE', startMargin: 16, endMargin: 16 })
|
||||
.divider({ strokeWidth: 1, color: Theme.border, startMargin: 16, endMargin: 16 })
|
||||
}
|
||||
|
||||
// 底栏
|
||||
Row() {
|
||||
Text('共 ' + this.sessions.length + ' 个会话').fontSize(12).fontColor('#999999')
|
||||
Text('共 ' + this.sessions.length + ' 个会话').fontSize(12).fontColor(Theme.textSubtle)
|
||||
}
|
||||
.width('100%').height(40).padding({ left: 16 })
|
||||
.backgroundColor('#FFFFFF')
|
||||
.backgroundColor(Theme.surface)
|
||||
}
|
||||
.width('100%').height('100%')
|
||||
.backgroundColor('#F5F7FA')
|
||||
.backgroundColor(Theme.pageBg)
|
||||
}
|
||||
|
||||
@Builder
|
||||
@ -105,23 +106,23 @@ struct SessionsPage {
|
||||
Column() {
|
||||
Row() {
|
||||
Text(s.session_alias.length > 0 ? s.session_alias : s.subject)
|
||||
.fontSize(14).fontWeight(FontWeight.Bold).fontColor('#333333')
|
||||
.fontSize(14).fontWeight(FontWeight.Bold).fontColor(Theme.textPrimary)
|
||||
.maxLines(1).textOverflow({ overflow: TextOverflow.Ellipsis })
|
||||
.layoutWeight(1)
|
||||
|
||||
// 预算进度
|
||||
if (s.max_rounds > 0) {
|
||||
Text(s.used_rounds + '/' + s.max_rounds)
|
||||
.fontSize(11).fontColor(s.used_rounds >= s.max_rounds ? '#FF4444' : '#666666')
|
||||
.backgroundColor(s.used_rounds >= s.max_rounds ? '#FFEBEE' : '#F5F5F5')
|
||||
.fontSize(11).fontColor(s.used_rounds >= s.max_rounds ? Theme.danger : Theme.textMuted)
|
||||
.backgroundColor(s.used_rounds >= s.max_rounds ? Theme.dangerBg : Theme.surfaceMuted)
|
||||
.borderRadius(4).padding({ left: 4, right: 4, top: 1, bottom: 1 })
|
||||
}
|
||||
|
||||
// 未读
|
||||
if (s.unread_count > 0) {
|
||||
Text(s.unread_count + '')
|
||||
.fontSize(11).fontColor('#FFFFFF')
|
||||
.backgroundColor('#FF4444')
|
||||
.fontSize(11).fontColor(Theme.surface)
|
||||
.backgroundColor(Theme.danger)
|
||||
.borderRadius(10).width(20).height(20)
|
||||
.textAlign(TextAlign.Center)
|
||||
.margin({ left: 6 })
|
||||
@ -129,16 +130,16 @@ struct SessionsPage {
|
||||
}
|
||||
.width('100%')
|
||||
|
||||
Text('对方: ' + s.from_agent).fontSize(12).fontColor('#666666').margin({ top: 4 })
|
||||
Text('对方: ' + s.from_agent).fontSize(12).fontColor(Theme.textMuted).margin({ top: 4 })
|
||||
|
||||
Row() {
|
||||
Text(s.permission_mode.length > 0 ? s.permission_mode : '—')
|
||||
.fontSize(11).fontColor('#666666')
|
||||
.backgroundColor('#F0F0F0').borderRadius(4)
|
||||
.fontSize(11).fontColor(Theme.textMuted)
|
||||
.backgroundColor(Theme.border).borderRadius(4)
|
||||
.padding({ left: 4, right: 4, top: 1, bottom: 1 })
|
||||
Blank()
|
||||
Text(s.mail_count + ' 封').fontSize(11).fontColor('#999999')
|
||||
Text(' · ' + s.status).fontSize(11).fontColor(s.status === 'active' ? '#4CAF50' : '#999999')
|
||||
Text(s.mail_count + ' 封').fontSize(11).fontColor(Theme.textSubtle)
|
||||
Text(' · ' + s.status).fontSize(11).fontColor(s.status === 'active' ? Theme.approve : Theme.textSubtle)
|
||||
}
|
||||
.width('100%').margin({ top: 4 })
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
* 多账号列表 / 添加新账号 / 删除 / 切换默认账号
|
||||
*/
|
||||
import { ApiClient, ApiError } from '../api/ApiClient';
|
||||
import { Theme } from '../common/Theme';
|
||||
import { AuthApi } from '../api/AuthApi';
|
||||
import { AccountManager, AccountInfo } from '../api/AccountManager';
|
||||
import { SseService } from '../api/SseService';
|
||||
@ -136,25 +137,25 @@ struct SettingsPage {
|
||||
build() {
|
||||
Column() {
|
||||
Row() {
|
||||
Text('‹').fontSize(24).fontColor('#1A73E8').width(40).height(40)
|
||||
Text('‹').fontSize(24).fontColor(Theme.accent).width(40).height(40)
|
||||
.textAlign(TextAlign.Center)
|
||||
.onClick(() => { this.getUIContext().getRouter().back(); })
|
||||
Text('账号管理').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#333333')
|
||||
Text('账号管理').fontSize(16).fontWeight(FontWeight.Bold).fontColor(Theme.textPrimary)
|
||||
.layoutWeight(1)
|
||||
Text('+').fontSize(24).fontColor('#1A73E8').width(40).height(40)
|
||||
Text('+').fontSize(24).fontColor(Theme.accent).width(40).height(40)
|
||||
.textAlign(TextAlign.Center)
|
||||
.onClick(() => { this.showAddDialog = true; })
|
||||
}
|
||||
.width('100%').height(56).padding({ left: 8, right: 8 })
|
||||
.backgroundColor('#FFFFFF')
|
||||
.backgroundColor(Theme.surface)
|
||||
|
||||
Divider().color('#EEEEEE')
|
||||
Divider().color(Theme.border)
|
||||
|
||||
if (this.accounts.length === 0) {
|
||||
Column() {
|
||||
Text('📭 暂无账号').fontSize(16).fontColor('#777777')
|
||||
Button('添加第一个账号')
|
||||
.margin({ top: 16 }).backgroundColor('#1A73E8')
|
||||
.margin({ top: 16 }).backgroundColor(Theme.accent)
|
||||
.onClick(() => { this.showAddDialog = true; })
|
||||
}
|
||||
.width('100%').layoutWeight(1).justifyContent(FlexAlign.Center)
|
||||
@ -168,7 +169,7 @@ struct SettingsPage {
|
||||
}, (account: AccountInfo) => account.id)
|
||||
}
|
||||
.width('100%').layoutWeight(1)
|
||||
.divider({ strokeWidth: 1, color: '#EEEEEE', startMargin: 16, endMargin: 16 })
|
||||
.divider({ strokeWidth: 1, color: Theme.border, startMargin: 16, endMargin: 16 })
|
||||
}
|
||||
|
||||
if (this.showAddDialog) {
|
||||
@ -179,7 +180,7 @@ struct SettingsPage {
|
||||
.onClick(() => { this.showAddDialog = false; })
|
||||
|
||||
Column() {
|
||||
Text('添加新账号').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#333333')
|
||||
Text('添加新账号').fontSize(16).fontWeight(FontWeight.Bold).fontColor(Theme.textPrimary)
|
||||
.margin({ bottom: 16 })
|
||||
|
||||
TextInput({ placeholder: '显示名称(必填)', text: this.newDisplayName })
|
||||
@ -201,18 +202,18 @@ struct SettingsPage {
|
||||
|
||||
Row() {
|
||||
Button('取消')
|
||||
.width(80).height(36).backgroundColor('#F5F5F5').fontColor('#555555')
|
||||
.width(80).height(36).backgroundColor(Theme.surfaceMuted).fontColor('#555555')
|
||||
.onClick(() => { this.showAddDialog = false; })
|
||||
Blank()
|
||||
Button(this.adding ? '验证中…' : '添加')
|
||||
.width(80).height(36).backgroundColor('#1A73E8')
|
||||
.width(80).height(36).backgroundColor(Theme.accent)
|
||||
.enabled(!this.adding)
|
||||
.onClick(() => { this.addNewAccount(); })
|
||||
}
|
||||
.width('100%')
|
||||
}
|
||||
.width('100%').height('68%')
|
||||
.padding(16).backgroundColor('#FFFFFF')
|
||||
.padding(16).backgroundColor(Theme.surface)
|
||||
.borderRadius({ topLeft: 12, topRight: 12 })
|
||||
}
|
||||
.width('100%').height('100%')
|
||||
@ -220,7 +221,7 @@ struct SettingsPage {
|
||||
}
|
||||
}
|
||||
.width('100%').height('100%')
|
||||
.backgroundColor('#F5F7FA')
|
||||
.backgroundColor(Theme.pageBg)
|
||||
}
|
||||
|
||||
@Builder
|
||||
@ -230,11 +231,11 @@ struct SettingsPage {
|
||||
Row() {
|
||||
Text(account.displayName)
|
||||
.fontSize(15).fontWeight(FontWeight.Bold)
|
||||
.fontColor(account.id === this.activeId ? '#1A73E8' : '#333333')
|
||||
.fontColor(account.id === this.activeId ? Theme.accent : Theme.textPrimary)
|
||||
if (account.id === this.activeId) {
|
||||
Text(' 默认')
|
||||
.fontSize(11).fontColor('#1A73E8')
|
||||
.backgroundColor('#E3F2FD').borderRadius(4)
|
||||
.fontSize(11).fontColor(Theme.accent)
|
||||
.backgroundColor(Theme.accentSoft).borderRadius(4)
|
||||
.padding({ left: 4, right: 4, top: 1, bottom: 1 })
|
||||
.margin({ left: 6 })
|
||||
}
|
||||
@ -252,7 +253,7 @@ struct SettingsPage {
|
||||
|
||||
if (account.id !== this.activeId) {
|
||||
Text('设为默认')
|
||||
.fontSize(12).fontColor('#1A73E8')
|
||||
.fontSize(12).fontColor(Theme.accent)
|
||||
.margin({ right: 12 })
|
||||
.onClick(() => { this.switchTo(account.id); })
|
||||
}
|
||||
@ -264,6 +265,6 @@ struct SettingsPage {
|
||||
.width('100%').height('100%')
|
||||
.padding({ left: 16, right: 16 })
|
||||
.alignItems(VerticalAlign.Center)
|
||||
.backgroundColor(account.id === this.activeId ? '#F0F7FF' : '#FFFFFF')
|
||||
.backgroundColor(account.id === this.activeId ? Theme.accentSoft : Theme.surface)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user