7.2 KiB
7.2 KiB
小程序自定义顶部栏 · 微信胶囊安全区适配(方案 B)
日期:2026-09-12
范围:miniprogram中navigationStyle: custom的页面
状态:待用户审阅后进入实现计划
1. 背景与问题
体验版中,从工作台进入地磅「仪表盘」等自定义导航页时,业务顶部栏(返回、标题、右侧用户名/操作)与微信小程序原生顶栏(状态栏 + 右上角胶囊「··· / 关闭」)重叠,尤其右侧关闭按钮区域会挡住业务内容。
根因:
- 多数业务顶栏仅用固定
padding(如.db-header { padding: 12px 16px }),未计入statusBarHeight。 - 仓库已有
utils/safeArea.js、components/app-safe-header.vue与 CSS 变量约定(--sq-safe-top等),但:applySafeAreaCssVars仅写入 H5 的document.documentElement,微信小程序端不生效;app-safe-header未被任何页面引用;- 地磅
.db-header、报销.topbar、壳层.mp-header未统一消费同一套量纲。
用户确认采用 方案 B:顶部加高并对齐胶囊垂直中心,右侧永久预留胶囊宽度,业务可点按钮不得放入胶囊区。
2. 目标
- 所有自定义导航页的顶栏内容(标题、返回、次要文案)落在胶囊下方/左侧安全区内,不被遮挡。
- 量纲由运行时 API 计算,适配不同机型(刘海、胶囊位置差异)。
- 视觉风格不变:白底列表顶栏与绿色渐变大头仍分别沿用现有 Token;只调整几何与占位。
- 最小改动:复用已有
safeArea基建,统一样式入口,避免每页手写高度。
3. 非目标
- 不改系统默认导航栏页面(如协议/隐私等非 custom 页)。
- 不调整底部 TabBar /
safe-area-inset-bottom(除非顺带发现明显缺陷且与本次无关则不做)。 - 不做顶栏视觉重设计(颜色、圆角、字号大改)。
- 不在右侧胶囊区放置业务「关闭」「更多」按钮。
4. 量纲与 CSS 变量
沿用并落实 miniprogram/utils/safeArea.js 的计算(uni.getSystemInfoSync + uni.getMenuButtonBoundingClientRect):
| 变量 | 含义 | 典型算法 |
|---|---|---|
--sq-safe-top |
状态栏高度 | statusBarHeight |
--sq-nav-height |
导航内容行高(与胶囊垂直居中) | (capsule.top - statusBarHeight) * 2 + capsule.height,下限 44 |
--sq-header-height |
顶栏总高 | safe-top + nav-height |
--sq-capsule-right-safe |
右侧预留宽 | capsule.width + (windowWidth - capsule.right) + 水平余量,无胶囊时为 0 |
启动时 App.onLaunch 已调用 initGlobalSafeArea();实现时须保证指标写入 小程序可消费的样式(见 §5),不能仅依赖 H5 document。
5. 布局规则(方案 B)
5.1 白底顶栏(地磅 / 报销)
结构:
┌─────────────────────────────────────────────┐
│ 状态栏占位 (--sq-safe-top) │
├──────────┬──────────────────┬───────────────┤
│ 返回+标题 │ 次要信息(可选) │ 胶囊右侧空白 │
│ │ │ (不可放按钮) │
└──────────┴──────────────────┴───────────────┘
←────────── --sq-nav-height ───────────────→
约束:
- 总高度 =
--sq-header-height;内容行垂直居中对齐胶囊。 - 网格或等价布局:
auto | minmax(0,1fr) | var(--sq-capsule-right-safe)。 - 标题省略号截断;右侧列为空占位。
5.2 渐变大头(工作台 / 首页 / 待办等 .mp-header)
padding-top:calc(var(--sq-safe-top) + 内容上边距)(替换仅env(safe-area-inset-top)或写死像素)。padding-right:calc(水平内边距 + var(--sq-capsule-right-safe)),保证副文案/用户信息不进入胶囊区。- 若该页在头部右侧放置「退出」等按钮:须落在胶囊左侧列,不得占用
--sq-capsule-right-safe区域(工作台现有右侧用户栈需按此收紧)。
5.3 变量下发(微信端)— 既定路径
采用 「SCSS 统一吃变量 + 顶栏根节点内联注入」,不强制先改完所有页面 DOM:
- 新增或复用 composable / 工具函数:基于
getGlobalSafeArea()返回四变量的 style 对象(px 字符串)。 .db-header/.bx-page .topbar/.mp-header(及必要变体)在 SCSS 中改用--sq-*;各页顶栏根节点绑定:style="safeAreaStyle"(或等价)。app-safe-header:补齐样式与插槽后,新页优先使用;存量地磅/报销可先改 SCSS + 内联变量,不必一次性替换 DOM。applySafeAreaCssVars:保留 H5 写入document;微信端以顶栏内联为准(不依赖 page 全局 CSS 变量穿透)。
禁止:仅增大固定 padding-top、不预留右侧(已否决的方案 C)。
6. 影响面
| 样式/组件 | 路径 | 动作 |
|---|---|---|
| safeArea 工具 | miniprogram/utils/safeArea.js |
确保微信端可消费;必要时补小程序注入 |
| 公共顶栏 | miniprogram/components/app-safe-header.vue |
完善后供白底页复用,或抽出等效 SCSS |
| 地磅顶栏 | miniprogram/styles/db-theme.scss + 各地磅页 |
.db-header 改为方案 B 几何 |
| 报销顶栏 | miniprogram/styles/bx-theme.scss |
.topbar 对齐同一变量(替换仅 env(safe-area-inset-top) + 写死 58px) |
| 壳层大头 | miniprogram/styles/mp-shell.scss(及 mp-biz 变体) |
.mp-header* 使用 safe-top + capsule-right |
| 既有大背景头 | miniprogram/styles/index.scss .sq-hero-header |
核对已引用变量是否在微信端生效;无效则一并修通 |
| UI 规范 | docs/miniprogram-ui-spec-sanqiang.md §3.2 |
补充「自定义 Header 须适配胶囊」SHALL |
页面枚举以 pages.json 中 navigationStyle: custom 为准,至少覆盖:工作台、首页、待办、地磅全链路、报销全链路、占位页。
7. 验收标准
- iPhone(刘海)与 Android 体验版:仪表盘返回/标题/用户名均不被胶囊遮挡。
- 地磅列表/详情、报销列表/表单顶栏同样不被遮挡。
- 工作台渐变头标题与右侧用户/退出不与胶囊重叠。
- H5 预览不出现异常巨大右侧空白或顶栏塌陷(无胶囊时
--sq-capsule-right-safe = 0)。 - 不引入新的业务按钮挤进胶囊区。
8. 风险与注意
getMenuButtonBoundingClientRect在部分端或过早调用可能为空:已有默认值(safe-top≈20、nav=44、右侧 0/96);实现时保持兜底。- 工作台当前
mp-header--workbench含额外40px偏移,改造时应用变量替换魔法数,避免双重叠加导致头部过高。 - 规范文档与 Demo HTML 可后置同步,但代码合入前至少更新
miniprogram-ui-spec-sanqiang.md中 Header 一小节。
9. 决策记录
- 方案 B(加高 + 右侧预留)— 用户确认 2026-09-12。
- 否决方案 C(只加高不预留右侧)。
- 视觉 Token 不换色;几何统一到
--sq-*变量。