diff --git a/client/harmony/entry/src/main/ets/common/BackgroundPicker.ets b/client/harmony/entry/src/main/ets/common/BackgroundPicker.ets index 561e4d1..5918845 100644 --- a/client/harmony/entry/src/main/ets/common/BackgroundPicker.ets +++ b/client/harmony/entry/src/main/ets/common/BackgroundPicker.ets @@ -406,7 +406,19 @@ async function packJpeg(pm: image.PixelMap, quality: number): Promise + * packToData(pm: PixelMap, options: PackingOption): Promise + * ⇒ 改名字就行,返回值/异常语义/调用方全部不变。 + */ + return await packer.packToData(pm, opt); } finally { // ★ `release()` 也是 `Promise`(SDK 两个重载:callback 版与 Promise 版)—— // 第 22 条判据把这一处抓出来了:不 await 的话它是一个"没人管的 promise", diff --git a/client/harmony/entry/src/main/ets/entryability/EntryAbility.ets b/client/harmony/entry/src/main/ets/entryability/EntryAbility.ets index c8e66a0..f405617 100644 --- a/client/harmony/entry/src/main/ets/entryability/EntryAbility.ets +++ b/client/harmony/entry/src/main/ets/entryability/EntryAbility.ets @@ -29,7 +29,7 @@ import { hilog } from '@kit.PerformanceAnalysisKit'; * 两个同名枚举撞在一起,是本条最费时的一步。 */ import { KeyboardAvoidMode, window } from '@kit.ArkUI'; -import { BusinessError } from '@kit.BasicServicesKit'; +import { BusinessError, deviceInfo } from '@kit.BasicServicesKit'; import { ApiClient } from '../api/ApiClient'; import { PushService } from '../api/PushService'; import { NotificationLedger } from '../model/PushContract'; @@ -225,10 +225,27 @@ export default class EntryAbility extends UIAbility { * 下面这条 `setWindowSystemBarEnable(['status'])` 是上一位留下的: * 它对状态栏仍有效,且留着不会更糟 —— 但它**不是**消黑边的手段 * (实测没能消掉底部那 60px + 20px)。一并保留,不再靠它。 + * + * ★★ 2026-09-21 加设备类型守卫(编译器告警:`This API is unavailable to 2in1.`)。 + * + * 查了 SDK 头文件确认不是误报。`api/@ohos.window.d.ts` 上该方法的注释原文: + * * From API version 12, + * this API does not take effect on 2-in-1 devices. + * ⇒ 在 2in1(PC/平板键盘态)上**从 API 12 起就不生效**,而我们是 23。 + * + * ── 为什么不是"反正有 catch,不管了" ── + * 当前它只是"发个不生效的请求"(catch 收个错、日志里多一行), + * 危害确实小。但这条告警会在每次完整重编时**重复出现**, + * 而告警列表里混着真实隐患(同一批里就有 `fill` 那个 SDK 26 的 API)⇒ + * 噪声会把真信号淹死。今天正是先 `touch` 重编、再逐条筛,才把 `fill` 挑出来。 + * + * ⇒ 按设备类型分流:2in1 上直接不调,其余平台行为一字不变。 */ - win.setWindowSystemBarEnable(['status']).catch((err: BusinessError) => { - hilog.error(DOMAIN, 'testTag', 'setWindowSystemBarEnable failed: %{public}s', JSON.stringify(err)); - }); + if (deviceInfo.deviceType !== '2in1') { + win.setWindowSystemBarEnable(['status']).catch((err: BusinessError) => { + hilog.error(DOMAIN, 'testTag', 'setWindowSystemBarEnable failed: %{public}s', JSON.stringify(err)); + }); + } /* ── ② 读避让 + 监听变化 ── */ this.publishInsets(win); try { diff --git a/client/harmony/entry/src/main/ets/pages/InboxPage.ets b/client/harmony/entry/src/main/ets/pages/InboxPage.ets index 718490b..eac5f4c 100644 --- a/client/harmony/entry/src/main/ets/pages/InboxPage.ets +++ b/client/harmony/entry/src/main/ets/pages/InboxPage.ets @@ -193,7 +193,30 @@ struct InboxPage { // 未读圆点 Column() { if (mail.status === 'unread') { - Circle({ width: 8, height: 8 }).fill(Theme.accent) + /* + * ★★ 2026-09-21 修:`Circle().fill()` → 普通容器 + 圆角。 + * + * 编译器告警(自有源码 2 条中的一条): + * The 'fill' API is supported since SDK version 26.0.0. + * However, the current compatible SDK version is 6.1.0(23). + * + * 查了 SDK 头文件确认这不是误报: + * component/circle.d.ts 的 `CircleAttribute.fill()` 标着 @since 26.0.0 + * 而基类 `CommonShapeMethod.fill()` 才是 @since 11。 + * 子类重载**遮蔽**了基类那个 ⇒ 当前编译目标(23)下它其实是**未来 API**。 + * + * 设备实测(宽屏 3184)黄点/蓝点**确实渲染出来了** —— + * 所以现在无害。但那是"碰巧实现里兼容",不是契约: + * 升 SDK 或换个渲染后端就可能变静默不出点。 + * + * ⇒ 换成与 `CalendarPage` 小圆点**同一形状**(本仓已有): + * 普通容器 + width/height + borderRadius = 半径 + * 只用 API 11 起的通用属性,不碰 `fill`。 + */ + Column() + .width(8).height(8) + .borderRadius(4) + .backgroundColor(Theme.accent) } } .width(20).height('100%') diff --git a/client/harmony/entry/src/main/ets/pages/MainPage.ets b/client/harmony/entry/src/main/ets/pages/MainPage.ets index a487866..80d6b3c 100644 --- a/client/harmony/entry/src/main/ets/pages/MainPage.ets +++ b/client/harmony/entry/src/main/ets/pages/MainPage.ets @@ -1008,7 +1008,14 @@ struct InboxTab { Row() { Column() { if (mail.status === 'unread') { - Circle({ width: 8, height: 8 }).fill(Theme.accent) + /* + * ★★ 2026-09-21 修:`Circle().fill()` 是 SDK 26 的 API + * (见 `InboxPage.MailItem` 那段完整说明)。换成通用容器画点。 + */ + Column() + .width(8).height(8) + .borderRadius(4) + .backgroundColor(Theme.accent) } } .width(20).height('100%').justifyContent(FlexAlign.Center) diff --git a/client/harmony/entry/src/main/ets/pages/SettingsPage.ets b/client/harmony/entry/src/main/ets/pages/SettingsPage.ets index c542f33..0f7a2a7 100644 --- a/client/harmony/entry/src/main/ets/pages/SettingsPage.ets +++ b/client/harmony/entry/src/main/ets/pages/SettingsPage.ets @@ -32,6 +32,37 @@ import { PushService } from '../api/PushService'; import { performLogout } from '../api/Logout'; import { PushTokenList } from '../model/PushContract'; +/** + * 内容列宽度上限(vp)—— 对齐 WebUI `AccountPage.tsx:104` 的 `max-w-3xl`。 + * + * tailwind 的 `max-w-3xl` = `48rem` = **768px**。 + * + * ★★ 2026-09-21 新增(用户:「「我的」页面显得非常挤」): + * 没有这个上限时,宽屏(3184px)下卡片铺满整个窗格, + * 而里面全是"标签 + 短值"的两列行 —— 文字只占左边一小块, + * 右边大片留白,整体读起来像一条被拉长的表格。 + * + * 为什么是 768 而不是"窗格宽度的百分之多少": + * WebUI 是固定值(响应式只改左右 padding),我们跟它一致; + * 按比例算会让"同一张卡在宽屏上又变宽",就治不了这个病。 + */ +const CONTENT_MAX_WIDTH: number = 768; + +/** + * 段与段之间的竖向间距(vp)—— 对齐 WebUI `AccountPage.tsx:104` 的 `space-y-6`。 + * + * tailwind `space-y-6` = **24px**。 + * + * ★★ 2026-09-21 新增(用户:「「我的」页面显得非常挤」): + * 原来每张卡各自写 `margin({ top: 8 })` / `({ top: 10 })` —— + * 散在 8 个 `@Builder` 的尾部,既不统一(8 与 10 混用) + * 也只有 WebUI 的 **1/3**。卡片贴得太近时,"段"的边界看不出来, + * 整页读起来就是一大块密集表单。 + * + * 收成一个常量:下次改间距只改这一处,不会再出现 6 个 8、1 个 10 的散开状态。 + */ +const SECTION_GAP: number = 24; + @Component export struct SettingsPane { @@ -632,9 +663,28 @@ export struct SettingsPane { * 没有任何办法滚到」。账号越多 / 屏越矮时越明显,而「退出登录」正好在最下面。 */ Scroll() { - Column() { - /* 基本资料 / 权限范围(对齐 WebUI 的前两个 section) */ - this.ProfileSection() + /* + * ★★ 2026-09-21 修(用户:「「我的」页面显得非常挤」)—— + * + * 原来内容列只有 `.width('100%')`:**没有任何宽度上限**。 + * 宽屏(3184px)下卡片直接铺满 ~1900px,而所有文字内容只有 ~200px 宽 + * 全挤在左边 —— 右边一大片空白,整体看着像"被拉长的一条"。 + * + * WebUI `AccountPage.tsx:104` 是: + *
+ * `max-w-3xl` = **48rem = 768px**,`mx-auto` 居中。 + * + * ⇒ 形状:`Scroll > Row(居中容器)> Column(带上限)`。 + * + * ★ 为什么不把居中写在 `Scroll` 上:**`Scroll` 没有 `.justifyContent()`** + * (官方 `ScrollAttribute` 里没有这个属性)。 + * 我第一版就是直接给 Scroll 挂 `justifyContent` —— 那是个编出来的属性。 + * ArkUI 没有 `mx-auto` 的直接对应物,"居中"必须靠一个容器节点。 + */ + Row() { + Column() { + /* 基本资料 / 权限范围(对齐 WebUI 的前两个 section) */ + this.ProfileSection() /* 修改密码 */ this.PasswordSection() @@ -673,7 +723,7 @@ export struct SettingsPane { } } .width('100%').alignItems(HorizontalAlign.Start) - .padding(16).margin({ top: 8 }) + .padding(16).margin({ top: SECTION_GAP }) .attributeModifier(GlassCardModifier.of(this.bgActive)) .borderRadius(Theme.radiusCard) @@ -699,8 +749,12 @@ export struct SettingsPane { this.AdminSection() Blank().height(24) + } + .constraintSize({ maxWidth: CONTENT_MAX_WIDTH }) + .width('100%') } .width('100%') + .justifyContent(FlexAlign.Center) } .width('100%').layoutWeight(1) .scrollBar(BarState.Off) @@ -827,6 +881,19 @@ export struct SettingsPane { if (this.profile === null) { Text('加载中…').fontSize(13).fontColor(Theme.textMuted).width('100%') } else { + /* + * ★★ 2026-09-21 补:「基本资料」这个**段标题**。 + * + * WebUI `AccountPage.tsx:107` 有 `

基本资料

`,而鸿蒙这边缺 —— + * `SectionTitle()` 定义了却**只被调用一次**('权限范围'), + * 于是基本资料那六行直接裸放在卡片里,与下面的「权限范围」 + * 靠一条 `Divider` 硬分 —— 没有标题就没有"这是另一段"的语义。 + * + * 用户说的「显得非常挤」,宽度是主因,这一条是次因: + * 段落边界只能靠线,读起来就是一整块。 + * WebUI 全文 5 个 h3,我们只有 1 个。 + */ + this.SectionTitle('基本资料') this.InfoRow('用户名', this.profile.username) this.InfoRow('显示名', this.profile.display_name) this.InfoRow('角色', this.profile.role === 'admin' ? '管理员' : '普通用户') @@ -854,7 +921,7 @@ export struct SettingsPane { } } .width('100%').alignItems(HorizontalAlign.Start) - .padding(16).margin({ top: 10 }) + .padding(16).margin({ top: SECTION_GAP }) .attributeModifier(GlassCardModifier.of(this.bgActive)) .borderRadius(Theme.radiusCard) } @@ -925,7 +992,7 @@ export struct SettingsPane { }) } .width('100%').alignItems(HorizontalAlign.Start) - .padding(16).margin({ top: 8 }) + .padding(16).margin({ top: SECTION_GAP }) .attributeModifier(GlassCardModifier.of(this.bgActive)) .borderRadius(Theme.radiusCard) } @@ -962,7 +1029,7 @@ export struct SettingsPane { .fontSize(11).fontColor(Theme.textSubtleFor()).margin({ top: 6 }) } .width('100%').alignItems(HorizontalAlign.Start) - .padding(16).margin({ top: 8 }) + .padding(16).margin({ top: SECTION_GAP }) .attributeModifier(GlassCardModifier.of(this.bgActive)) .borderRadius(Theme.radiusCard) } @@ -1047,7 +1114,7 @@ export struct SettingsPane { } } .width('100%').alignItems(HorizontalAlign.Start) - .padding(16).margin({ top: 8 }) + .padding(16).margin({ top: SECTION_GAP }) .attributeModifier(GlassCardModifier.of(this.bgActive)) .borderRadius(Theme.radiusCard) } @@ -1172,7 +1239,7 @@ export struct SettingsPane { } .width('100%').alignItems(HorizontalAlign.Start) - .padding(16).margin({ top: 8 }) + .padding(16).margin({ top: SECTION_GAP }) .attributeModifier(GlassCardModifier.of(this.bgActive)) .borderRadius(Theme.radiusCard) } @@ -1205,7 +1272,7 @@ export struct SettingsPane { .onClick(() => { this.doLogout(); }) } .width('100%').alignItems(HorizontalAlign.Start) - .padding(16).margin({ top: 8 }) + .padding(16).margin({ top: SECTION_GAP }) .attributeModifier(GlassCardModifier.of(this.bgActive)) .borderRadius(Theme.radiusCard) } @@ -1234,7 +1301,7 @@ export struct SettingsPane { .width('100%').margin({ top: 4 }) } .width('100%').alignItems(HorizontalAlign.Start) - .padding(16).margin({ top: 8 }) + .padding(16).margin({ top: SECTION_GAP }) .attributeModifier(GlassCardModifier.of(this.bgActive)) .borderRadius(Theme.radiusCard) .attributeModifier(PressEffectModifier.of()) diff --git a/client/harmony/entry/src/main/ets/pages/WideSidebar.ets b/client/harmony/entry/src/main/ets/pages/WideSidebar.ets index 0f44185..8067db2 100644 --- a/client/harmony/entry/src/main/ets/pages/WideSidebar.ets +++ b/client/harmony/entry/src/main/ets/pages/WideSidebar.ets @@ -253,9 +253,17 @@ export struct WideSidebar { .borderRadius(18) .backgroundColor(this.currentIndex === ME_PANE_INDEX ? Theme.navActiveBg : Theme.border) .textAlign(TextAlign.Center) - /* 连接状态点:不遮挡文字,贴在右下角(WebUI `-bottom-0.5 -right-0.5`) */ - Circle({ width: 8, height: 8 }) - .fill(this.sseColorOf(this.sseStatus)) + /* + * 连接状态点:不遮挡文字,贴在右下角(WebUI `-bottom-0.5 -right-0.5`)。 + * + * ★★ 2026-09-21 修:`Circle().fill()` 是 SDK 26 的 API + * (`circle.d.ts` 的 `fill` 标 @since 26.0.0,遮蔽了基类 API 11 那个)。 + * 实测能渲染,但那是碰巧 —— 换通用容器画,只用 API 11 起的属性。 + */ + Column() + .width(8).height(8) + .borderRadius(4) + .backgroundColor(this.sseColorOf(this.sseStatus)) .margin({ right: 1, bottom: 1 }) } .width(40).height(40)