From a97b83e221852dadb5f3aeef524f22568d9d2fd0 Mon Sep 17 00:00:00 2001 From: JianFeeeee Date: Sun, 20 Sep 2026 12:07:32 +0800 Subject: [PATCH] =?UTF-8?q?=E8=B7=A8=E7=AB=AF:=20=E5=AE=BD=E5=B1=8F?= =?UTF-8?q?=E5=8F=B3=E6=A0=8F=E7=A9=BA=E6=80=81=EF=BC=88splitPlaceholder?= =?UTF-8?q?=EF=BC=89=E2=80=94=E2=80=94=20WebUI=20=E6=9C=89=E5=BC=95?= =?UTF-8?q?=E5=AF=BC=EF=BC=8C=E6=88=91=E4=BB=AC=E5=8E=9F=E6=9D=A5=E4=B8=80?= =?UTF-8?q?=E7=89=87=E7=A9=BA=E7=99=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 用户:「你自己看看跟 webui 相比,观感真的差很多」。同 1107vp 视口并排后, 差异确实明显,其中**最扎眼**的一条:宽屏两栏并排时,右栏是**一大片空白**。 WebUI 有引导(`MailView.tsx:121-129`): 信封图标 + 「选择一封邮件查看,或点击左侧「新建」写邮件」 我们什么都没渲染。 ★ 第一版放错位置(记下来,这个坑很隐蔽) 我把它写进 `NavDestination` 的 `else` 分支 —— 而 **`NavDestination` 只在 push 之后才挂载**,栈空时它根本不存在 ⇒ 那个 else **一次都不会显示** (实测:加上去之后右栏仍然全空)。 正确入口是 `splitPlaceholder(ComponentContent)`:系统给"右栏默认页"的专用 API (`navigation.d.ts`,API 20+,我们是 23),由 `Navigation` 在栈空时自己渲染。 ★ 三个 ArkTS 约束(都撞了才过) ① `splitPlaceholder` 要的 `wrapBuilder` 只接受**全局** `@Builder` 函数 —— 写成 struct 成员方法会报 "The wrapBuilder's parameter should be '@Builder' function"。 内容本身是静态的(不需要 `this`),所以全局正好。 ② `wrapBuilder` 是**全局声明**(`common.d.ts:27448`),**不在** `@kit.ArkUI` 里 —— 从 kit 引会报 "has no exported member"。 ③ `ComponentContent` 要从 `@kit.ArkUI` 引。 文案**逐字对齐 WebUI**(同一句话,不自己改写)—— 与本仓既有纪律一致 (`harmony-logic` 里「空态主句与 WebUI 逐字一致」那条是同源要求)。 设备验证:`选择一封邮件查看,或点击左侧「新建」写邮件` 已在右栏渲染(布局树可见)。 --- .../entry/src/main/ets/pages/MainPage.ets | 86 ++++++++++++++----- 1 file changed, 64 insertions(+), 22 deletions(-) diff --git a/client/harmony/entry/src/main/ets/pages/MainPage.ets b/client/harmony/entry/src/main/ets/pages/MainPage.ets index 118c24a..f28da46 100644 --- a/client/harmony/entry/src/main/ets/pages/MainPage.ets +++ b/client/harmony/entry/src/main/ets/pages/MainPage.ets @@ -76,7 +76,7 @@ import { PushService, PushRoute } from '../api/PushService'; */ import { CalendarPage } from './CalendarPage'; import { SettingsPane } from './SettingsPage'; -import { LengthMetrics } from '@kit.ArkUI'; +import { LengthMetrics, ComponentContent } from '@kit.ArkUI'; import { Insets, KEY_WINDOW_INSETS } from '../model/WindowInsets'; import { LIST_FADE_LENGTH, @@ -117,6 +117,42 @@ function compactMailTime(iso: string): string { * Navigation 目标页:按官方示例在 NavDestination.onReady 读取路径参数。 * 系统 Navigation 自动决定 Stack / Split;这里不读窗口宽度、不手搓分栏。 */ +/** + * 右栏占位(`Navigation` **split 模式**下、栈空时显示的那一块)。 + * + * ★★ 2026-09-20 补(用户:「你自己看看跟 webui 相比,观感真的差很多」)。 + * + * 宽屏下详情是**常驻右栏**;没选邮件时 WebUI 有引导(`MailView.tsx:121-129`): + * 信封图标 + 「选择一封邮件查看,或点击左侧「新建」写邮件」 + * 而我们什么都不渲染 ⇒ 两栏并排时右栏是**一大片空白** + * (实测同 1107vp 视口并排:WebUI 那栏中央有图标+文案,我们那栏全空)。 + * + * ★ 为什么用 `splitPlaceholder` 而不是"在 `NavDestination` 里加 `else` 分支": + * `NavDestination` **只在 push 之后才挂载**,栈空时它根本不存在 —— + * 我第一版就是写在它的 `else` 里,**一次都不会显示**(已撤销)。 + * `splitPlaceholder(ComponentContent)` 是系统给"右栏默认页"的专用入口 + * (`navigation.d.ts`,API 20+,我们是 23),由 `Navigation` 在栈空时自己渲染。 + * + * ★ 必须是**全局** `@Builder`:`splitPlaceholder` 要的是 + * `wrapBuilder<[]>(fn)`,而它只接受全局 @Builder 函数 + * (成员方法会报 "The wrapBuilder's parameter should be '@Builder' function")。 + * 内容本身是静态的,不需要 `this`,所以全局正好。 + * + * 文案**逐字对齐 WebUI**(同一句话,不自己改写)—— 本仓纪律: + * 两端对同一件事说同一句话(见 `harmony-logic` 里「空态主句逐字一致」那条)。 + */ +@Builder +function DetailPlaceholder() { + Column() { + AmIcon({ iconName: 'mail', iconSize: 40, iconColor: Theme.textSubtleFor() }) + Text('选择一封邮件查看,或点击左侧「新建」写邮件') + .fontSize(14).fontColor(Theme.textMuted) + .margin({ top: 12 }) + } + .width('100%').height('100%') + .justifyContent(FlexAlign.Center) +} + @Component struct MailDetailDestination { @State mailId: string = ''; @@ -1443,6 +1479,25 @@ struct CommPage { this.navPathStack.pushPath({ name: MAIL_DETAIL_ROUTE, param: params }); } + /** + * 右栏占位(`Navigation` **split 模式**下、栈空时显示的那一块)。 + * + * ★★ 2026-09-20 补(用户:「你自己看看跟 webui 相比,观感真的差很多」)。 + * + * 宽屏下详情是**常驻右栏**;没选邮件时 WebUI 有引导(`MailView.tsx:121-129`): + * 信封图标 + 「选择一封邮件查看,或点击左侧「新建」写邮件」 + * 而我们什么都不渲染 ⇒ 两栏并排时右栏是**一大片空白** + * (实测同 1107vp 视口并排:WebUI 那栏中央有图标+文案,我们那栏全空)。 + * + * ★ 为什么用 `splitPlaceholder` 而不是"在 `NavDestination` 里加 `else` 分支": + * `NavDestination` **只在 push 之后才挂载**,栈空时它根本不存在 —— + * 我第一版就是写在它的 `else` 里,**一次都不会显示**(已撤销)。 + * `splitPlaceholder(ComponentContent)` 是系统给"右栏默认页"的专用入口 + * (`navigation.d.ts`,API 20+,我们是 23),由 `Navigation` 在栈空时自己渲染。 + * + * 文案**逐字对齐 WebUI**(同一句话,不自己改写)—— 本仓纪律: + * 两端对同一件事说同一句话(见 `harmony-logic` 里「空态主句逐字一致」那条)。 + */ @Builder DestinationBuilder(name: string, param: Object) { if (name === MAIL_DETAIL_ROUTE) { @@ -1575,6 +1630,10 @@ struct CommPage { .minContentWidth(360) .hideTitleBar(true) .width('100%').height('100%') + /* 右栏占位:栈空时显示引导(否则宽屏两栏并排时右栏是一大片空白)。 + 用系统入口 `splitPlaceholder`,**不是** NavDestination 的 else —— 后者栈空时不挂载。 */ + .splitPlaceholder(new ComponentContent( + this.getUIContext(), wrapBuilder<[]>(DetailPlaceholder))) /* * ★★ 2026-09-19 修(用户:「你这玻璃也不透明啊」)。 * @@ -1787,27 +1846,6 @@ struct ContactsTab { DestinationBuilder(name: string, param: Object) { if (name === MAIL_DETAIL_ROUTE) { MailDetailDestination({ navReserve: this.navReserve, bgActive: this.bgActive }) - } else { - /* - * ★★ 2026-09-20 补(用户:「你自己看看跟 webui 相比,观感真的差很多」)。 - * - * 宽屏下详情是**常驻右栏**,没选邮件时这一栏该有**空态引导**。 - * WebUI 有(`MailView.tsx:121-129`): - * 信封图标 + 「选择一封邮件查看,或点击左侧「新建」写邮件」 - * 而我们这边什么都不渲染 ⇒ **一大片纯白/壁纸**,两栏并排时尤其空。 - * 实测(同 1107vp 视口并排):WebUI 右栏中央有图标+文案,我们那栏全空。 - * - * 文案**逐字对齐 WebUI**(同一句话,不自己改写)—— 本仓纪律: - * 两端对同一件事说同一句话(`harmony-logic` 里那条「空态主句逐字一致」同源)。 - */ - Column() { - AmIcon({ iconName: 'mail', iconSize: 40, iconColor: Theme.textSubtleFor() }) - Text('选择一封邮件查看,或点击左侧「新建」写邮件') - .fontSize(14).fontColor(Theme.textMuted) - .margin({ top: 12 }) - } - .width('100%').height('100%') - .justifyContent(FlexAlign.Center) } } @@ -1975,6 +2013,10 @@ struct ContactsTab { .minContentWidth(360) .hideTitleBar(true) .width('100%').height('100%') + /* 右栏占位:栈空时显示引导(否则宽屏两栏并排时右栏是一大片空白)。 + 用系统入口 `splitPlaceholder`,**不是** NavDestination 的 else —— 后者栈空时不挂载。 */ + .splitPlaceholder(new ComponentContent( + this.getUIContext(), wrapBuilder<[]>(DetailPlaceholder))) /* 同 CommPage 的 Navigation:不设背景时系统默认不透明白,会把里面 已经透明的窗格盖住。联系人页是同一形状,同一修法。 */ .attributeModifier(PaneModifier.of(this.bgActive))