diff --git a/entry/src/main/ets/common/Store.ets b/entry/src/main/ets/common/Store.ets index 3939c18..df9589c 100644 --- a/entry/src/main/ets/common/Store.ets +++ b/entry/src/main/ets/common/Store.ets @@ -24,6 +24,15 @@ export class FbAppState { /** 抽屉是否展开(手机端滑出层;宽屏端恒为常驻,不看这个值) */ @Trace drawerOpen: boolean = false; + /** + * 抽屉从哪一侧滑出:'left' | 'right'。 + * + * 🔴 用户要求「从左边缘向右滑 → 左边出抽屉;从右边缘向左滑 → 右边出抽屉」—— + * 滑动方向决定抽屉在哪边(用户 2026-10-12 选的是这套,不用握姿传感器)。 + * 这样左手握持时用左边缘、右手握持时用右边缘,单手持机都顺。 + */ + @Trace drawerSide: string = 'left'; + /** 当前所在模块 key(home / people / movies …) */ @Trace activeModule: string = 'home'; diff --git a/entry/src/main/ets/components/Drawer.ets b/entry/src/main/ets/components/Drawer.ets index a831111..2b21844 100644 --- a/entry/src/main/ets/components/Drawer.ets +++ b/entry/src/main/ets/components/Drawer.ets @@ -1,13 +1,14 @@ /** - * 家书 FamilyBook · 左侧抽屉(鸿蒙端) + * 家书 FamilyBook · 抽屉(鸿蒙端) * * 形态对齐 Web 端顶栏 `.nav` 那一排模块入口,但**改成抽屉**(用户建议): * Web 端导航条最多放 12 个模块,手机宽度下必然溢出;抽屉竖向排列, * 还能顺带放下「模块说明 + 主题色」这层信息,一眼看清每个模块是干什么的。 * - * 两条形态,同一个组件: + * 三条形态,同一个组件: * · **常驻侧栏**(宽屏:窗口短边 ≥ 600vp)—— 固定在左边,占 264vp; - * · **滑出层**(手机竖屏)—— 覆盖在内容上,点遮罩或选中项即收起。 + * · **左滑出层**(手机,从左边缘向右滑)—— 覆盖内容,点遮罩或选中项即收起; + * · **右滑出层**(手机,从右边缘向左滑)—— 同上,只是贴右边(右手单手持机顺手)。 * * ⚠️ 这里**不用 SideBarContainer**:它的控制按钮与「滑出」是内建的一套, * 样式(图标、位置、宽度动画)很难与家书的视觉统一,且它只能有一层侧栏 @@ -205,7 +206,10 @@ export struct FbDrawerShell { @BuilderParam content: () => void; build() { - Stack({ alignContent: Alignment.TopStart }) { + // 抽屉贴哪边由 drawerSide 决定 ⇒ Stack 的 alignment 也跟着换。 + Stack({ + alignContent: this.app.drawerSide === 'right' ? Alignment.TopEnd : Alignment.TopStart + }) { // ---- 宽屏:并排 ---- if (this.app.wideLayout) { Row() { @@ -222,7 +226,7 @@ export struct FbDrawerShell { .width('100%') .height('100%') } else { - // ---- 窄屏:内容铺满 + 抽屉浮层 ---- + // ---- 窄屏:内容铺满 + 抽屉浮层(可从左或右滑出)---- Column() { this.content() } @@ -240,6 +244,9 @@ export struct FbDrawerShell { }) .transition(TransitionEffect.OPACITY.animation({ duration: 180 })) + // 抽屉本体:由 drawerSide 决定贴左还是贴右 + // ⚠️ 用 Stack 的 alignment 而不是 margin 来定位: + // 动画是 translateX,用 alignment 铺位更稳(不参与测量)。 FbDrawer({ app: this.app, overlayMode: true, @@ -247,13 +254,50 @@ export struct FbDrawerShell { this.app.drawerOpen = false; } }) - .transition(TransitionEffect.translate({ x: -FbMetric.drawerW }) - .animation({ duration: 220, curve: Curve.FastOutSlowIn })) + .transition(TransitionEffect.translate({ + x: this.app.drawerSide === 'right' ? FbMetric.drawerW : -FbMetric.drawerW + }).animation({ duration: 220, curve: Curve.FastOutSlowIn })) } } } .width('100%') .height('100%') .backgroundColor(FbColors.paper) + // 🔴 横向滑动开抽屉(用户 2026-10-12 定的规则)。 + // + // ⚠️ **不要判断"起点是否贴屏幕边缘"** —— 用户实测反馈:屏幕边缘是 + // **系统手势区**(返回/侧边栏),从边缘往里滑的事件根本轮不到应用处理, + // 我们无法控制。所以判据只能是"**在应用内部水平滑动**": + // 向右滑 → 抽屉从左边出;向左滑 → 抽屉从右边出。 + // 这样左手持机时往右划、右手持机时往左划,单手都顺。 + // + // ⚠️ `offsetX` 的单位是 **vp**(SDK 原文 "in vp"),不要再除以 density —— + // 除一次会把位移缩到 1/3,导致阈值永远达不到、手势"看起来没生效"。 + // + // ⚠️ 用 `parallelGesture` 而不是 `gesture`:内容区自己还有竖向滚动 + // (以及纪念日/财务/任务的横向滚动条),用 gesture 会把它们抢掉。 + // 代价是横向滚动条扫得猛时也可能触发 —— 所以阈值给到 80vp, + // 轻扫(浏览横向列表)不会误开。 + .parallelGesture( + PanGesture({ direction: PanDirection.Horizontal, distance: 30 }) + .onActionEnd((e: GestureEvent) => { + // 宽屏下抽屉常驻,不需要手势 + if (this.app.wideLayout) { + return; + } + const dx: number = e.offsetX; // vp + // 向右滑 ⇒ 抽屉在左 + if (dx > 80) { + this.app.drawerSide = 'left'; + this.app.drawerOpen = true; + return; + } + // 向左滑 ⇒ 抽屉在右 + if (dx < -80) { + this.app.drawerSide = 'right'; + this.app.drawerOpen = true; + } + }) + ) } } \ No newline at end of file diff --git a/entry/src/main/ets/components/TopBar.ets b/entry/src/main/ets/components/TopBar.ets index b009729..5050073 100644 --- a/entry/src/main/ets/components/TopBar.ets +++ b/entry/src/main/ets/components/TopBar.ets @@ -7,10 +7,11 @@ * · 顶栏**并入页面滚动流**,向上滑动时跟着内容一起滑走 * (所以它本身不固定、也不自己处理状态栏留白)。 * - * 🔴 与状态栏的关系:顶栏是白色,而系统栏被涂成全透明、透出的是**根容器底色**。 - * 所以要让"通知栏与顶栏融为一体",根容器在顶栏这一段必须是白色 —— - * 由 `Index.ets` 在顶部铺一层与顶栏同高的白色块来实现(页面滚动时它不动, - * 保证通知栏区域永远是白的,不会滚出内容)。 + * 🔴 **顶栏不放汉堡按钮**(用户 2026-10-12 明确要求去掉): + * 开抽屉改由**屏幕边缘滑动**触发(见 `Drawer.ets` 的 parallelGesture)—— + * 从左边缘向右滑 ⇒ 抽屉在左;从右边缘向左滑 ⇒ 抽屉在右。 + * ⇒ 图标能一直贴到最左边,视觉上更好看。 + * ⚠️ 别再加回汉堡按钮;抽屉的其它入口是「顶栏头像」与「抽屉底部账号区」。 * * ⚠️ 顶栏**不放**「设置」—— 与 Web 端一致:设置收在抽屉底部的账号区里。 */ @@ -28,25 +29,7 @@ export struct FbTopBar { build() { Row({ space: 10 }) { - // ---- 左:窄屏显示抽屉按钮 ---- - if (!this.app.wideLayout) { - Button({ type: ButtonType.Circle }) { - Image(FbIcon.ui('menu')) - .width(18) - .height(18) - .renderMode(ImageRenderMode.Template) - .fillColor(FbColors.ink) - } - .width(36) - .height(36) - .backgroundColor(Color.Transparent) - .stateEffect(true) - .onClick(() => { - this.app.drawerOpen = true; - }) - } - - // ---- 应用图标 ---- + // ---- 应用图标(贴最左:抽屉改由边缘滑动触发,这里不再有汉堡按钮)---- // ⚠️ 必须给描边:logo 是「米色底(74%) + 砖红图案(24%)」的实心方图, // 放在白色/米色上时底板与背景几乎同色,只剩砖红图案发飘、看不清。 // 一层极浅的描边就能把底板"托"出来(本机范本 SyncCalendar 也给了描边)。 @@ -114,7 +97,8 @@ export struct FbTopBar { } .width('100%') .height(FbMetric.topBarH) - .padding({ left: 14, right: 10, top: 4, bottom: 4 }) + // 左内边距=pagePad(与下面各区块的左边线对齐);右侧 10(图标可略贴边) + .padding({ left: FbMetric.pagePad, right: 10, top: 4, bottom: 4 }) .alignItems(VerticalAlign.Center) // 顶栏底色 = 白色(与下面的个人信息卡同色) .backgroundColor(FbColors.surface) diff --git a/entry/src/main/ets/pages/calendar/FbCalendarPage.ets b/entry/src/main/ets/pages/calendar/FbCalendarPage.ets index c6d7bf6..f86b53a 100644 --- a/entry/src/main/ets/pages/calendar/FbCalendarPage.ets +++ b/entry/src/main/ets/pages/calendar/FbCalendarPage.ets @@ -147,7 +147,6 @@ export struct FbCalendarPage { .height(this.gridHeight()) .scrollBar(BarState.Off) } - .width('100%') .padding(FbMetric.cardPad) .backgroundColor(FbColors.surface) .borderRadius(FbMetric.radius) @@ -205,7 +204,6 @@ export struct FbCalendarPage { .width('100%') .padding({ left: FbMetric.cardPad, right: FbMetric.cardPad, bottom: 6 }) } - .width('100%') .backgroundColor(FbColors.surface) .borderRadius(FbMetric.radius) .margin({ left: FbMetric.pagePad, right: FbMetric.pagePad, top: 12 }) @@ -233,7 +231,6 @@ export struct FbCalendarPage { .width('100%') .padding({ left: FbMetric.cardPad, right: FbMetric.cardPad, bottom: 6 }) } - .width('100%') .backgroundColor(FbColors.surface) .borderRadius(FbMetric.radius) .margin({ left: FbMetric.pagePad, right: FbMetric.pagePad, top: 12, bottom: 28 }) diff --git a/entry/src/main/ets/pages/days/FbDaysPage.ets b/entry/src/main/ets/pages/days/FbDaysPage.ets index 08a97ba..d2efdb3 100644 --- a/entry/src/main/ets/pages/days/FbDaysPage.ets +++ b/entry/src/main/ets/pages/days/FbDaysPage.ets @@ -98,7 +98,6 @@ export struct FbDaysPage { .width('100%') } } - .width('100%') .padding(FbMetric.cardPad) .backgroundColor(FbColors.surface) .borderRadius(FbMetric.radius) @@ -127,7 +126,6 @@ export struct FbDaysPage { this.annRow(a, idx === this.filtered().length - 1) }, (a: FbAnniversaryItem): string => 'ann_' + a.id.toString()) } - .width('100%') .padding({ left: FbMetric.cardPad, right: FbMetric.cardPad, top: 4, bottom: 8 }) .backgroundColor(FbColors.surface) .borderRadius(FbMetric.radius) diff --git a/entry/src/main/ets/pages/home/FbHomePage.ets b/entry/src/main/ets/pages/home/FbHomePage.ets index a510839..0200175 100644 --- a/entry/src/main/ets/pages/home/FbHomePage.ets +++ b/entry/src/main/ets/pages/home/FbHomePage.ets @@ -171,9 +171,12 @@ export struct FbHomePage { cAlarm: FB_DEMO_WEEK_STAT.overdue > 0 }) } - .width('100%') + // 🔴 不要 `.width('100%')` —— 它会先把宽度占满,导致后面的 margin 失效, + // 卡片于是**顶到屏幕两侧边缘**(实测 x=0..1080),而别的区块是留边 16vp 的, + // 页面里就出现"有的满宽、有的有缝"的不统一(用户 2026-10-12 的反馈)。 + // 正确做法:**只给 margin**,宽度由 margin 自动撑出(左右各 16vp)。 .padding(FbMetric.cardPad) - // 白色卡片(用户要求):与下面「今天的日程」等块同一种卡片语言 + // 白色卡片:与下面「今天的日程」等块同一种卡片语言 .backgroundColor(FbColors.surface) .borderRadius(FbMetric.radius) .margin({ diff --git a/entry/src/main/ets/pages/ledger/FbLedgerPage.ets b/entry/src/main/ets/pages/ledger/FbLedgerPage.ets index 8c73e97..6a456e8 100644 --- a/entry/src/main/ets/pages/ledger/FbLedgerPage.ets +++ b/entry/src/main/ets/pages/ledger/FbLedgerPage.ets @@ -168,7 +168,6 @@ export struct FbLedgerPage { .width('100%') .alignItems(HorizontalAlign.Start) } - .width('100%') .padding(FbMetric.cardPad) .backgroundColor(FbColors.surface) .borderRadius(FbMetric.radius) @@ -219,7 +218,6 @@ export struct FbLedgerPage { } .width('100%') } - .width('100%') .padding(FbMetric.cardPad) .backgroundColor(FbColors.surface) .borderRadius(FbMetric.radius) diff --git a/entry/src/main/ets/pages/movies/FbMoviesPage.ets b/entry/src/main/ets/pages/movies/FbMoviesPage.ets index fa6111d..e3c0964 100644 --- a/entry/src/main/ets/pages/movies/FbMoviesPage.ets +++ b/entry/src/main/ets/pages/movies/FbMoviesPage.ets @@ -77,7 +77,6 @@ export struct FbMoviesPage { .layoutWeight(1) .maxLines(1) } - .width('100%') .height(36) .padding({ left: 12, right: 12 }) .backgroundColor(FbColors.inputBg) diff --git a/entry/src/main/ets/pages/people/FbPeoplePage.ets b/entry/src/main/ets/pages/people/FbPeoplePage.ets index 4be7f11..0e37553 100644 --- a/entry/src/main/ets/pages/people/FbPeoplePage.ets +++ b/entry/src/main/ets/pages/people/FbPeoplePage.ets @@ -293,7 +293,6 @@ export struct FbPeoplePage { } .width('100%') } - .width('100%') .padding(FbMetric.cardPad) .backgroundColor(FbColors.surface) .borderRadius(FbMetric.radius) @@ -361,7 +360,6 @@ export struct FbPeoplePage { .width('100%') } } - .width('100%') .padding(FbMetric.cardPad) .backgroundColor(FbColors.surface) .borderRadius(FbMetric.radius) diff --git a/entry/src/main/ets/pages/settings/FbSettingsPage.ets b/entry/src/main/ets/pages/settings/FbSettingsPage.ets index 499f86b..b9caadf 100644 --- a/entry/src/main/ets/pages/settings/FbSettingsPage.ets +++ b/entry/src/main/ets/pages/settings/FbSettingsPage.ets @@ -69,7 +69,6 @@ export struct FbSettingsPage { .renderMode(ImageRenderMode.Template) .fillColor(FbColors.faint) } - .width('100%') .padding(FbMetric.cardPad) .backgroundColor(FbColors.surface) .borderRadius(FbMetric.radius) @@ -82,7 +81,6 @@ export struct FbSettingsPage { this.switchRow('到期提醒', '日程开始前 10 分钟提醒', true) this.switchRow('逾期提醒', '任务逾期时提醒我', true) } - .width('100%') .padding(FbMetric.cardPad) .backgroundColor(FbColors.surface) .borderRadius(FbMetric.radius) @@ -113,7 +111,6 @@ export struct FbSettingsPage { this.moduleToggleRow(m) }, (m: FbModuleDef): string => 'set_' + m.key) } - .width('100%') .padding(FbMetric.cardPad) .backgroundColor(FbColors.surface) .borderRadius(FbMetric.radius) @@ -137,7 +134,6 @@ export struct FbSettingsPage { this.colorRow(m) }, (m: FbModuleDef): string => 'col_' + m.key) } - .width('100%') .padding(FbMetric.cardPad) .backgroundColor(FbColors.surface) .borderRadius(FbMetric.radius) @@ -151,7 +147,6 @@ export struct FbSettingsPage { this.plainRow('数据来源', '待接入 · 当前为演示数据') this.plainRow('服务器', '未配置') } - .width('100%') .padding(FbMetric.cardPad) .backgroundColor(FbColors.surface) .borderRadius(FbMetric.radius)