## ① 沉浸式:改用本机 SyncCalendar 的方案(用户指定参考) 用户原话:「上面那一块的颜色和通知栏的颜色完全融为一体; 紧接着就是应用图标和标题,尽量靠上」。 三件套(缺一不可): 1. 系统栏涂**全透明** `#00000000`; 2. `setWindowBackgroundColor(页面底色)` 供透明系统栏透出; 3. **紧邻系统栏的元素不设自己的背景色** + 根容器 `expandSafeArea`。 🔴 第三件是最关键、也是我先前漏掉的一环: 我原来给顶栏设了白色 `backgroundColor`,于是状态栏(透出纸色)与顶栏(白) 仍上下拼接。读 SyncCalendar 源码发现它的 `header()` **根本没有 backgroundColor** —— 这才是"融为一体"的真正机制。已在代码里写明原因,防止回退。 配套简化:删掉自己算安全区的那套(`statusBarH`/`bottomBarH`/ `loadWindowInsets`/`applyAvoidAreas`,共约 110 行)—— 不再手算,也就不会算错。 ## ② 顶栏改「图标 + 标题」 按用户要求:应用图标 + 「家书」标题(下行小字显示当前模块)贴左上, 右侧铃铛与头像。顶栏高度 56 → 48vp,上下内边距压到 4,内容整体上移。 ## ③ 个人信息卡恢复为白色卡片 用户明确要求("还是得用一个白色的块,这样显得好看")。 曾一度做成"不套卡片的头部"(理由是省首屏高度),现按用户观感改回白卡。 ⚠️ 与顶栏的区别:顶栏**不能**有背景色(否则与状态栏割裂), 而这张卡在顶栏下方、与状态栏不相邻,套白卡没有这个问题。 ## 验证(全部基于真机) 逐行像素采样(`_tooling/familybook_immersion_check.py`, 模型读不了图 ⇒ 用数字裁定,不靠目测): · `y=0..271` 全为 `#F7F3EA` ⇒ **状态栏与顶栏同色,融为一体** ✓ · 底部 `y=2224..2444` 为纸色,无异常条 ✓ 真机界面遍历(`_tooling/familybook_harmonyos_probe.py`): · A 组抽屉 11 个模块 **11/11** · B 组家谱 **1/1**;C 组通知/设置 **2/2** · D 组深验(特征文本)**12/12** 合计 **14 个页面全通过**。 ⚠️ 顺带修掉探针自身两个坑(都记进注释了): · `nodes_of` 原来只收集**有文字**的节点 ⇒ 找不到纯图标的汉堡按钮, 导致"抽屉打不开"、10 个模块连锁失败(看着像应用坏了,其实是探针瞎了); · 汉堡按钮坐标写死 ⇒ 顶栏变矮后点空。改为在界面树里动态探测、 并**排除系统状态栏区域**(那里也有可点击元素,曾误取到 (103,56))。 ## 编译 0 error(保留 1 条 `setWindowBackgroundColor` 的 unavailable 告警,它是透明方案的必需组成)
188 lines
9.5 KiB
Markdown
188 lines
9.5 KiB
Markdown
# FamilyBook_harmonyos · 家书鸿蒙客户端
|
||
|
||
**家书**([FamilyBook](https://code.yangyq.net/laoyang/FamilyBook))的 HarmonyOS 客户端。
|
||
服务端是那套「自托管的家庭生活系统」,这个仓库是它的手机 / 平板端。
|
||
|
||
> 把一个家的「人」「日子」「去过的地方、看过的电影」收进同一本书里。
|
||
|
||
---
|
||
|
||
## 当前状态
|
||
|
||
**界面框架阶段(v0.1.0)** —— 骨架与全部模块页面已就位,**数据全部是演示数据**。
|
||
服务端还没有客户端用的接口,所以这一轮只做「长什么样」,不做任何读写。
|
||
|
||
| 已完成 | 说明 |
|
||
| --- | --- |
|
||
| 工程骨架 | Stage 模型 + ArkTS / ArkUI,targetSdk 26.0.0 / compatibleSdk 6.1.1(24) |
|
||
| 沉浸式 | 照本机 SyncCalendar 的方案:系统栏全透明 + 根容器 `expandSafeArea`,通知栏与页面**完全同色** |
|
||
| 左侧抽屉 | 宽屏常驻侧栏 / 窄屏滑出层,两套形态同一组件 |
|
||
| 12 个模块页面 | 首页、名册、观影、旅程、日程、资源、任务、纪念日、财务、读书、笔记(+ 家谱) |
|
||
| 附加页面 | 通知消息、个人设置 |
|
||
| 主题体系 | 宣纸米 / 砖红配色,深浅色两套资源,深浅自动跟随系统 |
|
||
| 模块主题色 | 与 Web 端 `Module::PRESET_COLORS` 同一套 12 色预设 |
|
||
| 编译验证 | `hvigorw assembleHap` **0 error / 0 warning** |
|
||
| 真机验证 | 装机成功;15 个页面逐个点开、断言渲染内容,**15/15 通过** |
|
||
|
||
| 待办(下一轮) | 说明 |
|
||
| --- | --- |
|
||
| 接口对接 | 服务端需要提供客户端用的 JSON 接口(登录、各模块列表与详情) |
|
||
| 登录态 | 会话持久化、令牌刷新、启动时的登录门禁 |
|
||
| 交互实现 | 新建 / 编辑 / 删除、搜索、筛选、拖拽排序 |
|
||
| 日程周/天视图 | 按半小时定位的时间轴 + 手势(本轮只做了月视图) |
|
||
| 附件能力 | 上传、图片/视频预览、裁剪 |
|
||
| 财务折线图 | 净资产与各账目的盘点变化曲线 |
|
||
|
||
---
|
||
|
||
## 与 Web 端的取舍
|
||
|
||
数据看 Web 端,**排版按手机重做** —— 不照搬电脑端版面。
|
||
|
||
### 模块
|
||
|
||
**不做「导航」模块**(Web 端有)。用户原话:「导航是专门为电脑端开发的,都是网站,
|
||
主要用在浏览器的主页,所以不需要」。手机上每个网站都有 App,收藏夹没有使用场景。
|
||
⇒ 本工程的模块清单与 Web 端 `Module::LIST` **有意不等**,别"顺手补回去"。
|
||
|
||
**「家谱」不在抽屉里**(与 Web 端一致):它默认不上导航条(用户说过
|
||
「不需要每天都查看家谱」),走「名册 → 家谱」的分段切换进入。
|
||
|
||
### 版面
|
||
|
||
| 模块 | Web 端 | 鸿蒙端 | 为什么 |
|
||
| --- | --- | --- | --- |
|
||
| 首页 | hero 招牌 + 信息面板 + 日程/任务并排 + 我的家人 + 未来七天 + 模块卡 | 去掉 hero;信息面板作**无卡片的页面头部**;其余保持顺序上下堆叠 | hero 只重复信息面板已有的信息(家庭名 × 问候),手机首屏高度宝贵 |
|
||
| 观影 | 瀑布流海报墙 | 两列定尺寸网格 | 瀑布流列宽随图高浮动,手机上很难扫 |
|
||
| 任务 | 多列看板(拖动改状态) | 按状态分组的三段列表 | 手机宽度放不下多列;用户关心的是"做到哪一步" |
|
||
| 笔记 | 定尺寸卡片网格 | 两列定高卡片网格 | 与 Web 端一致(用户要求过"块大小要确定") |
|
||
| 家谱 | HTML 折线树 | 「以我为中心的圈层」(直系 / 其他亲属) | 折线树在窄屏排不下 |
|
||
| 财务 | 概览 + 折线图 + 账目表 | 概览(图占位)+ 按类别分组的账目 | 折线图需要图表能力,留到第二轮 |
|
||
|
||
---
|
||
|
||
## 目录结构
|
||
|
||
```
|
||
entry/src/main/ets/
|
||
entryability/EntryAbility.ets 应用入口:主题跟随、系统栏配色
|
||
entrybackupability/ 备份扩展
|
||
common/
|
||
Theme.ets 设计令牌(颜色 / 间距 / 字号)+ 模块调色板
|
||
Store.ets 全局状态(V2:@ObservedV2 + @Trace)
|
||
model/
|
||
Modules.ets 模块注册中心(对应 Web 端 App\Module)
|
||
Types.ets 全部数据类型(对应各模板渲染的字段)
|
||
DemoData.ets 演示数据(接上接口后可整个删除)
|
||
components/
|
||
Widgets.ets 共享展示件(头像 / 标签 / 空态 / 圆点…)
|
||
Drawer.ets 左侧抽屉 + 骨架(并排 / 浮层两形态)
|
||
TopBar.ets 顶栏
|
||
pages/
|
||
Index.ets 主界面:模块分发
|
||
home/ movies/ trips/ calendar/ tasks/ people/
|
||
resources/ days/ ledger/ books/ misc/ settings/
|
||
```
|
||
|
||
---
|
||
|
||
## 设计说明
|
||
|
||
### 沉浸式(三件套,缺一不可)
|
||
|
||
参考本机另一个应用 **SyncCalendar**(用户指定)的做法:
|
||
|
||
1. **系统栏涂成全透明** —— `statusBarColor: '#00000000'`、`navigationBarColor: '#00000000'`;
|
||
2. **窗口底色 = 页面底色** —— `win.setWindowBackgroundColor(pageBg)`,供透明系统栏透出;
|
||
3. **紧邻系统栏的元素不设自己的背景色** —— 顶栏**不能**有 `backgroundColor`,
|
||
让它透出根容器底色;根容器再 `expandSafeArea([SYSTEM], [TOP, BOTTOM])`
|
||
把背景铺到状态栏区域。
|
||
|
||
原理:透明系统栏透出的是**窗口底色**,而顶栏也透出**同一个**底色 ⇒ 天然同色、
|
||
「通知栏与上方完全融为一体」。内容仍在安全区内,**不需要手算状态栏高度**。
|
||
|
||
⚠️ 常见的两个坑(都踩过):
|
||
- 调 `setWindowLayoutFullScreen(true)` 后子组件会钻到状态栏底下,页面得自己让位,
|
||
极易算错(曾出现顶部重复避让 156px);
|
||
- 给状态栏或顶栏**写死颜色**必然与相邻层割裂(曾出现「米色状态栏 + 白色顶栏」的拼接感)。
|
||
|
||
验证方式:**逐行像素采样**(`_tooling/familybook_immersion_check.py`)。
|
||
判据是「状态栏区颜色 == 顶栏区颜色」,用数字裁定而不是目测 ——
|
||
最终实测 `y=0..271` 全为 `#F7F3EA`(纸色),达标。
|
||
|
||
Web 端顶栏是一排横向模块入口,最多放 12 个模块 —— 手机宽度下必然溢出。
|
||
改成左侧抽屉后,竖向排列不受数量限制,还能顺带放下「模块说明 + 主题色」
|
||
这层信息,一眼看清每个模块是干什么的。
|
||
|
||
抽屉有**两套形态**,由窗口短边决定(≥ 600vp 为宽屏):
|
||
|
||
- **宽屏**(平板 / 折叠屏展开 / 横屏手机):抽屉常驻,与内容并排;
|
||
- **窄屏**:内容铺满,抽屉从左侧滑出,点遮罩或选中模块即收起。
|
||
|
||
判据用「短边」而不是「宽度」—— 短边在旋转时不变,所以比例接近 1:1 的设备
|
||
横竖屏布局保持一致。
|
||
|
||
### 为什么是抽屉
|
||
|
||
Web 端顶栏是一排横向模块入口,最多放 12 个模块 —— 手机宽度下必然溢出。
|
||
改成左侧抽屉后,竖向排列不受数量限制,还能顺带放下「模块说明 + 主题色」
|
||
这层信息,一眼看清每个模块是干什么的。
|
||
|
||
抽屉有**两套形态**,由窗口短边决定(≥ 600vp 为宽屏):
|
||
|
||
- **宽屏**(平板 / 折叠屏展开 / 横屏手机):抽屉常驻,与内容并排;
|
||
- **窄屏**:内容铺满,抽屉从左侧滑出,点遮罩或选中模块即收起。
|
||
|
||
判据用「短边」而不是「宽度」—— 短边在旋转时不变,所以比例接近 1:1 的设备
|
||
横竖屏布局保持一致。
|
||
|
||
### 模块注册中心
|
||
|
||
模块清单只有**一个来源**:`model/Modules.ets` 的 `FB_MODULES`,
|
||
与 Web 端 `app/Module.php` 的 `Module::LIST` 一一对应(key 必须等于路由名)。
|
||
|
||
加一个模块只改那一处 —— 抽屉、设置页的显隐开关、主题色选择全部自动出现。
|
||
这是 Web 端踩过的坑(漏改一处就「有页面但导航里找不到入口」),端上照搬这套结构。
|
||
|
||
### 主题色
|
||
|
||
每个模块一个主题色,三档(按钮底 / hover / 文字色)由主色按 HSL 压明度算出来,
|
||
保证三档永远同色系。算法与 Web 端 `Module::cssVars()` 共用同一套公式。
|
||
|
||
预设色板 `FB_PRESET_COLORS` 也与 Web 端 `Module::PRESET_COLORS` **逐值一致** ——
|
||
否则同一个人在手机和电脑上选出的「观影紫」会是两种紫。
|
||
|
||
### 编译期约束(踩过的坑)
|
||
|
||
| 坑 | 处理 |
|
||
| --- | --- |
|
||
| `Circle().fill()` 是 API 26 才有的重载 | compatibleSdk 是 24,改用「方块 + 圆角」画圆(`components/Widgets.ets::FbDot`),零版本风险 |
|
||
| `setWindowBackgroundColor` 对 phone/tablet 不可用 | 去掉,页面自身已用同色铺满整屏 |
|
||
| `window.getLastWindow()` 只有异步版 | 由 `EntryAbility` 用 `getMainWindowSync()` 取一次存进 AppStorage,页面侧同步读 |
|
||
| 跨文件顶层名冲突(`arkts-no-decl-merging`) | 所有导出名统一加 `Fb` 前缀 |
|
||
| `Button('label') { ... }` 报 10905202 | 一律用 `Button() { Row() { ... } }` |
|
||
| `@Param` 类型写成 `string` 导致传 `Resource` 报错 | 颜色类入参统一声明 `ResourceColor` |
|
||
| 嵌套目录下 import 相对路径层级 | 页面在 `pages/<模块>/`,公共件在 `components/`、`model/`,相对路径要多退一级 |
|
||
|
||
---
|
||
|
||
## 构建
|
||
|
||
需要 HarmonyOS SDK(DevEco Studio 自带)。本机开发时:
|
||
|
||
```bash
|
||
export NODE_HOME="<DevEco>/tools/node"
|
||
export DEVECO_SDK_HOME="<DevEco>/sdk"
|
||
<DevEco>/tools/ohpm/bin/ohpm install --all
|
||
<DevEco>/tools/hvigor/bin/hvigorw assembleHap --mode module -p product=default
|
||
```
|
||
|
||
产物在 `entry/build/default/outputs/default/entry-default-unsigned.hap`。
|
||
|
||
`local.properties` 里写本机 SDK 路径,**不入库**(见 `.gitignore`)。
|
||
|
||
---
|
||
|
||
## 开源协议
|
||
|
||
[Apache License 2.0](LICENSE) |