harmony(ui): 登录页 1:1 对齐 WebUI 卡片规格 + 通信页改用系统 Navigation Auto
登录页(LoginPage.ets):对照 WebUI LoginPage.tsx 同规格重写 build() - maxWidth 380vp 居中卡片(与 WebUI 380px 同档) - 14vp 圆角(Theme.glassRadius) - 48vp 品牌图标(Envelope 浅蓝底 accentSoft) - 显式字段标签(服务器/用户名/密码/用户密钥) - 40vp 输入框与登录按钮(WebUI 控件同高) - 分段式登录方式切换(账号密码 / 用户密钥) - 窄屏安全边距 16vp + Scroll 可滚 通信页(MainPage.ets CommPage):改用鸿蒙系统 Navigation 组件 - Navigation(navPathStack) 包住列表,navDestination 注册邮件详情目标页 - mode(NavigationMode.Auto):phone 自动 Stack、tablet/2in1 自动 Split - navBarWidth(320) 对齐 WebUI MailList 320px;navBarWidthRange([280,360]) - minContentWidth(360) 参与 Auto 断点计算 - Inbox/Sent 选中邮件改走 navPathStack.pushPath,不再 router.pushUrl - 切收件箱/发件箱/授权时清空详情栈避免跨栏残留 详情页(MailDetailPage.ets):抽出可复用 MailDetailView - 既有详情实现导出为 @Component struct MailDetailView - 新增 initialMailId/initialAccountId/embedded/onBack 输入 - 无 initialMailId 时才读路由参数(兼容旧 router 入口) - embedded=true 时返回走 onBack 回调(NavPathStack.pop) - 薄壳 @Entry MailDetailPage 用 Column 包裹(@Entry 根节点须容器) module.json5:deviceTypes 扩展为 phone/tablet/2in1 三形态 判据:harmony-widescreen.test.mjs 新增 ⑥ 钉住 Navigation Auto 不许退回手搓分栏 验证:ArkTS 编译通过;harmony-arkts/widescreen/nav/cross-client-theme 35/35 通过
This commit is contained in:
@ -10,6 +10,7 @@ import { ApiClient, ApiError } from '../api/ApiClient';
|
||||
import { Theme } from '../common/Theme';
|
||||
import { AmIcon } from '../common/Icons';
|
||||
import { WideSidebar } from './WideSidebar';
|
||||
import { MailDetailView } from './MailDetailPage';
|
||||
import { MailApi, InboxResponse } from '../api/MailApi';
|
||||
import { AccountManager, AccountInfo } from '../api/AccountManager';
|
||||
import { SseService, SseEvent } from '../api/SseService';
|
||||
@ -81,11 +82,52 @@ import {
|
||||
|
||||
/** 一页取多少封。取满了就要如实提示"可能还有更多"(服务端 total 是未读数,不是总封数)。 */
|
||||
const INBOX_PAGE_SIZE: number = 50;
|
||||
const MAIL_DETAIL_ROUTE: string = 'mail-detail';
|
||||
|
||||
/**
|
||||
* Navigation 目标页:按官方示例在 NavDestination.onReady 读取路径参数。
|
||||
* 系统 Navigation 自动决定 Stack / Split;这里不读窗口宽度、不手搓分栏。
|
||||
*/
|
||||
@Component
|
||||
struct MailDetailDestination {
|
||||
@State mailId: string = '';
|
||||
@State accountId: string = '';
|
||||
private pathStack: NavPathStack = new NavPathStack();
|
||||
|
||||
handleReady(ctx: NavDestinationContext): void {
|
||||
const params: MailDetailParams = ctx.pathInfo.param as MailDetailParams;
|
||||
this.mailId = params?.mail_id ?? '';
|
||||
this.accountId = params?.account_id ?? '';
|
||||
this.pathStack = ctx.pathStack;
|
||||
}
|
||||
|
||||
build() {
|
||||
NavDestination() {
|
||||
if (this.mailId.length > 0) {
|
||||
MailDetailView({
|
||||
initialMailId: this.mailId,
|
||||
initialAccountId: this.accountId,
|
||||
embedded: true,
|
||||
onBack: (): void => { this.pathStack.pop(); }
|
||||
})
|
||||
} else {
|
||||
Column() {
|
||||
LoadingProgress().width(32).height(32)
|
||||
}
|
||||
.width('100%').height('100%').justifyContent(FlexAlign.Center)
|
||||
}
|
||||
}
|
||||
.hideTitleBar(true)
|
||||
.onReady((ctx: NavDestinationContext) => { this.handleReady(ctx); })
|
||||
}
|
||||
}
|
||||
|
||||
@Component
|
||||
struct InboxTab {
|
||||
/** 背景是否开启:开着就让出页面底(WebUI 的做法是页面底完全透明) */
|
||||
@Prop bgActive: boolean = false;
|
||||
/** 选中邮件回调:宽屏内联到 Navigation 右栏,窄屏 push 到目标页 */
|
||||
onOpenMail: (mailId: string, accountId: string) => void = (): void => {};
|
||||
@State mails: MailSummary[] = [];
|
||||
/** 按会话折叠后的列表(单封的组平铺渲染) */
|
||||
@State groups: SessionGroup[] = [];
|
||||
@ -273,14 +315,7 @@ struct InboxTab {
|
||||
}
|
||||
|
||||
openMail(mail: MailLike): void {
|
||||
const params: MailDetailParams = {
|
||||
mail_id: mail.mail_id,
|
||||
account_id: mail.source_account_id
|
||||
};
|
||||
this.getUIContext().getRouter().pushUrl({
|
||||
url: 'pages/MailDetailPage',
|
||||
params: params
|
||||
});
|
||||
this.onOpenMail(mail.mail_id, mail.source_account_id);
|
||||
}
|
||||
|
||||
openCompose(): void {
|
||||
@ -611,6 +646,8 @@ struct InboxTab {
|
||||
struct SentTab {
|
||||
/** 背景是否开启:开着就让出页面底(WebUI 的做法是页面底完全透明) */
|
||||
@Prop bgActive: boolean = false;
|
||||
/** 选中邮件回调 */
|
||||
onOpenMail: (mailId: string, accountId: string) => void = (): void => {};
|
||||
@State groups: SessionGroup[] = [];
|
||||
@State expandedKeys: string[] = [];
|
||||
@State loading: boolean = false;
|
||||
@ -681,11 +718,7 @@ struct SentTab {
|
||||
}
|
||||
|
||||
openMail(mail: MailLike): void {
|
||||
const params: MailDetailParams = {
|
||||
mail_id: mail.mail_id,
|
||||
account_id: mail.source_account_id
|
||||
};
|
||||
this.getUIContext().getRouter().pushUrl({ url: 'pages/MailDetailPage', params: params });
|
||||
this.onOpenMail(mail.mail_id, mail.source_account_id);
|
||||
}
|
||||
|
||||
@Builder
|
||||
@ -1074,6 +1107,8 @@ struct CommPage {
|
||||
@State commTab: string = 'inbox';
|
||||
@State unreadCount: number = 0;
|
||||
@State pendingCount: number = 0;
|
||||
/** Navigation 路由栈:宽屏 Split 时列表+详情并排,窄屏 Stack 时详情 push 覆盖 */
|
||||
private navPathStack: NavPathStack = new NavPathStack();
|
||||
|
||||
aboutToAppear(): void {
|
||||
this.refreshCounts();
|
||||
@ -1177,6 +1212,7 @@ struct CommPage {
|
||||
.border({ width: { bottom: this.commTab === key ? 2 : 0 }, color: Theme.accent })
|
||||
.onClick(() => {
|
||||
this.commTab = normalizeCommTab(key);
|
||||
this.navPathStack.clear();
|
||||
this.refreshCounts();
|
||||
})
|
||||
}, (key: string) => key)
|
||||
@ -1185,37 +1221,68 @@ struct CommPage {
|
||||
.backgroundColor(Theme.surface)
|
||||
}
|
||||
|
||||
openMail(mailId: string, accountId: string): void {
|
||||
const params: MailDetailParams = {
|
||||
mail_id: mailId,
|
||||
account_id: accountId
|
||||
};
|
||||
this.navPathStack.pushPath({ name: MAIL_DETAIL_ROUTE, param: params });
|
||||
}
|
||||
|
||||
@Builder
|
||||
DestinationBuilder(name: string, param: Object) {
|
||||
if (name === MAIL_DETAIL_ROUTE) {
|
||||
MailDetailDestination()
|
||||
}
|
||||
}
|
||||
|
||||
build() {
|
||||
Stack({ alignContent: Alignment.BottomEnd }) {
|
||||
Column() {
|
||||
Row() {
|
||||
Text('通信').fontSize(20).fontWeight(FontWeight.Bold).fontColor(Theme.textPrimary)
|
||||
Blank()
|
||||
/*
|
||||
* 设置入口。
|
||||
* 图标用 `person`:与 WebUI 的「我的」(`NarrowNav.tsx` 的 `PersonIcon`)同一枚,
|
||||
* 而设置页里正是账号 / 外观 / 密钥这些“属于这个人的东西”。
|
||||
* 2026-09-15 之前这里是 emoji `⚙`(理由与替换过程见 `model/NavItems.ts`)。
|
||||
*/
|
||||
AmIcon({ iconName: 'person', iconSize: 20, iconColor: Theme.textMuted })
|
||||
.width(36).height(36)
|
||||
.onClick(() => {
|
||||
this.getUIContext().getRouter().pushUrl({ url: 'pages/SettingsPage' });
|
||||
})
|
||||
}
|
||||
.width('100%').height(52).padding({ left: 16, right: 8 })
|
||||
.backgroundColor(Theme.surface)
|
||||
Navigation(this.navPathStack) {
|
||||
Column() {
|
||||
Row() {
|
||||
Text('通信').fontSize(20).fontWeight(FontWeight.Bold).fontColor(Theme.textPrimary)
|
||||
Blank()
|
||||
/*
|
||||
* 设置入口。
|
||||
* 图标用 `person`:与 WebUI 的「我的」(`NarrowNav.tsx` 的 `PersonIcon`)同一枚,
|
||||
* 而设置页里正是账号 / 外观 / 密钥这些“属于这个人的东西”。
|
||||
* 2026-09-15 之前这里是 emoji `⚙`(理由与替换过程见 `model/NavItems.ts`)。
|
||||
*/
|
||||
AmIcon({ iconName: 'person', iconSize: 20, iconColor: Theme.textMuted })
|
||||
.width(36).height(36)
|
||||
.onClick(() => {
|
||||
this.getUIContext().getRouter().pushUrl({ url: 'pages/SettingsPage' });
|
||||
})
|
||||
}
|
||||
.width('100%').height(52).padding({ left: 16, right: 8 })
|
||||
.backgroundColor(Theme.surface)
|
||||
|
||||
this.CommTabBar()
|
||||
this.CommTabBar()
|
||||
|
||||
if (this.commTab === 'sent') {
|
||||
SentTab({ bgActive: this.bgActive })
|
||||
} else if (this.commTab === 'permissions') {
|
||||
PermissionTab({ bgActive: this.bgActive })
|
||||
} else {
|
||||
InboxTab({ bgActive: this.bgActive })
|
||||
if (this.commTab === 'sent') {
|
||||
SentTab({
|
||||
bgActive: this.bgActive,
|
||||
onOpenMail: (mailId: string, accountId: string): void => { this.openMail(mailId, accountId); }
|
||||
})
|
||||
} else if (this.commTab === 'permissions') {
|
||||
PermissionTab({ bgActive: this.bgActive })
|
||||
} else {
|
||||
InboxTab({
|
||||
bgActive: this.bgActive,
|
||||
onOpenMail: (mailId: string, accountId: string): void => { this.openMail(mailId, accountId); }
|
||||
})
|
||||
}
|
||||
}
|
||||
.width('100%').height('100%')
|
||||
}
|
||||
.navDestination(this.DestinationBuilder)
|
||||
.mode(NavigationMode.Auto)
|
||||
/* WebUI 邮件列表栏固定约 320px;Auto 的断点 = 列表最小宽 + 详情最小宽。 */
|
||||
.navBarWidth(320)
|
||||
.navBarWidthRange([280, 360])
|
||||
.minContentWidth(360)
|
||||
.hideTitleBar(true)
|
||||
.width('100%').height('100%')
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user