# TabBar 重构实施计划 > 基于 2026-08-08-tabbar-redesign.md 设计文档 > 状态:completed · 2026-08-08 执行完成 > 提交:db6c2d3c feat(frontend): TabBar 重构 - 4Tabs + 中间五维启动器 + 自定义TabBar ## 总体目标 将小程序底部 TabBar 从 5 个五维标签(行/心/身/智/富)重构为 4 个功能标签(首页/健康/成长/我的)+ 中间五维启动器按钮。 ## 阶段分解 ### 阶段 1:基础设施 **1.1 创建 `utils/nav.js` 统一导航工具** ```javascript /** * 统一导航工具 * TabBar 页面列表(4个新Tab) */ var TAB_BAR_PAGES = [ '/pages/index-home/index', '/pages/health/index', '/pages/growth/index', '/pages/profile/profile' ] /** * 跳转到维度页面(五维页已移出 TabBar,统一用 navigateTo) * 行 = index-home(仍是 TabBar 页,用 switchTab) * 心/身/智/富 = 分包页,用 navigateTo */ function goDimension(dimCode) { var dimMap = { action: '/pages/index-home/index', mind: '/pages/mind/index', body: '/pages/body/index', wisdom: '/pages/wisdom/index', wealth: '/pages/wealth/index' } var url = dimMap[dimCode] if (!url) return if (TAB_BAR_PAGES.indexOf(url) !== -1) { uni.switchTab({ url: url }) } else { uni.navigateTo({ url: url }) } } function goPage(url) { if (!url) return if (TAB_BAR_PAGES.indexOf(url) !== -1) { uni.switchTab({ url: url }) } else { uni.navigateTo({ url: url }) } } module.exports = { TAB_BAR_PAGES: TAB_BAR_PAGES, goDimension: goDimension, goPage: goPage } ``` **1.2 准备 TabBar 图标** - 新增:`static/tab-home.png` / `static/tab-home-active.png` - 新增:`static/tab-health.png` / `static/tab-health-active.png` - 复用:`static/tab-growth.png` / `static/tab-growth-active.png`(已有) - 复用:`static/tab-profile.png` / `static/tab-profile-active.png`(已有) ### 阶段 2:pages.json 重构 **2.1 修改主包 pages 数组**(5 页 → 4 页) ```json { "pages": [ { "path": "pages/index-home/index", "style": { "navigationBarTitleText": "首页" } }, { "path": "pages/health/index", "style": { "navigationBarTitleText": "健康" } }, { "path": "pages/growth/index", "style": { "navigationBarTitleText": "成长档案" } }, { "path": "pages/profile/profile", "style": { "navigationBarTitleText": "我的" } } ] } ``` **2.2 修改 tabBar.list** ```json "tabBar": { "color": "#7A7E83", "selectedColor": "#F97316", "borderStyle": "black", "backgroundColor": "#FFFFFF", "list": [ { "pagePath": "pages/index-home/index", "text": "首页", "iconPath": "static/tab-home.png", "selectedIconPath": "static/tab-home-active.png" }, { "pagePath": "pages/health/index", "text": "健康", "iconPath": "static/tab-health.png", "selectedIconPath": "static/tab-health-active.png" }, { "pagePath": "pages/growth/index", "text": "成长", "iconPath": "static/tab-growth.png", "selectedIconPath": "static/tab-growth-active.png" }, { "pagePath": "pages/profile/profile", "text": "我的", "iconPath": "static/tab-profile.png", "selectedIconPath": "static/tab-profile-active.png" } ] } ``` **2.3 分包调整** > ⚠️ 现状核对(2026-08-08 评审):`pages/mind-detail`、`pages/body-detail`、`pages/wisdom-detail` **已存在**为分包(分别含 11/6/4 页),不是新增;只有 `pages/wealth` 分包是真正需要新建的。`pages/health` 分包已存在含 18 页,主包新增 `pages/health/index` 与分包 `root: "pages/health"` 同目录共存 —— uni-app 允许(主包声明 `pages/health/index.vue`,分包声明其他页面),首次打包需验证无路径冲突。 | 操作 | 分包 | 说明 | |------|------|------| | 移除 | `pages/growth` | 删除 `{ "path": "index" }` 条目(index 移入主包),其余 5 页保留 | | 移除 | `pages/profile` | 删除 `{ "path": "profile" }` 条目(profile 移入主包),其余 3 页保留(index/report-management/report-detail) | | 加入 | `pages/mind-detail` | **已存在**,添加 `{ "path": "index", "style": { "navigationBarTitleText": "心" } }`(指向 `pages/mind/index.vue` 物理文件,从主包 pages 数组移除) | | 加入 | `pages/body-detail` | **已存在**,添加 `{ "path": "index", "style": { "navigationBarTitleText": "身" } }`(指向 `pages/body/index.vue`) | | 加入 | `pages/wisdom-detail` | **已存在**,添加 `{ "path": "index", "style": { "navigationBarTitleText": "智" } }`(指向 `pages/wisdom/index.vue`) | | 新建 | `pages/wealth` 分包 | **真正新建**,含 `{ "path": "index", "style": { "navigationBarTitleText": "富" } }`(指向 `pages/wealth/index.vue`) | **2.4 主包五维页移除**(与 2.3 配套) 从主包 `pages` 数组移除以下 4 个条目(物理文件不动,路径归属转移到对应分包): - `pages/mind/index` → `pages/mind-detail` 分包 - `pages/body/index` → `pages/body-detail` 分包 - `pages/wisdom/index` → `pages/wisdom-detail` 分包 - `pages/wealth/index` → `pages/wealth` 分包(新建) > 注意:分包页面路径 = `root + "/" + path`。加入 `pages/mind-detail` 的 `{ path: "index" }` 实际指向 `pages/mind-detail/index.vue`,因此**物理文件需要移动**:`pages/mind/index.vue` → `pages/mind-detail/index.vue`(同理 body/wisdom/wealth)。移动后原目录若为空则删除。 ### 阶段 3:替换 switchTab 引用 **3.1 index-home/index.vue** — onFuncClick 中改用 `nav.goPage()` **3.2 wisdom/index.vue** — onFuncClick 中改用 `nav.goPage()` **3.3 wisdom/self-quiz.vue** — `switchTab` → `navigateTo` **3.4 wisdom-detail/self-quiz.vue** — `switchTab` → `navigateTo` **3.5 全局 grep 兜底** ```bash grep -rn "switchTab.*/pages/\(mind\|body\|wisdom\|wealth\)" --include="*.vue" . ``` 预期:无输出 ### 阶段 4:自定义 TabBar 改造 **4.1 重构 `custom-tab-bar/index.vue`** 模板结构: - 普通用户:4 Tab(首页/健康/成长/我的)+ 中间⭐按钮 + 五维半屏浮层 - 规划师:5 Tab(不变,不走自定义) **4.2 五维半屏浮层** - 5 个维度卡片(行草木火金水对应色) - slideUp 动画 - 点击关闭浮层 → navigateTo/switchTab 对应页面 ### 阶段 5:新建健康页 **5.1 创建 `pages/health/index.vue`** 内容: - PageBanner(theme="body", tagline="科学测评·精准诊断") - FamilyMemberStrip - 健康概览卡片(综合评分、待处理报告、上次测评日期、连续打卡天数) - 功能入口网格(8 个:申请测评、健康报告、健康调研、体检报告、肠道菌群、体征记录、五维测评、心智评估) - 最近报告列表 - 诊断建议(AI 解读摘要) - 未登录态:LoginGuideCard ### 阶段 6:增强成长页 **6.1 改造 `pages/growth/index.vue`** 从"档案查看"扩展为"方案执行中心": 新增内容: - PageBanner(theme="action", tagline="方案·执行·成长") - FamilyMemberStrip - 今日待办卡片(打卡状态、待完成任务数、挑战数、饮食记录数) - 功能入口网格(8 个:每日打卡、日常任务、家庭挑战、饮食记录、运动记录、作息记录、成长档案、冥想练习) - 成长档案记录列表(原有) - 创建成长档案按钮 保留原有: - 孩子选择 → 成长档案记录列表 - 筛选 Tab(全部/手动/测评/上传/补充) ### 阶段 7:调整推荐分布 **推荐分布原则**: - 🏷️ 推荐商品 → 首页(消费发现) - 🎯 推荐活动 → 成长(活动是"可参与的行动",与执行主题一致) - 📖 推荐阅读-健康类 → 健康(诊断后延伸学习) - 📖 推荐阅读-成长类 → 成长(方案执行的知识补充) - 📖 推荐阅读-综合类 → 首页(信息流) **7.1 首页 `index-home/index.vue`** - 保留:商品推荐(DimensionProducts) - 保留:综合类文章推荐(ArticleBookshelf) - 移除:活动推荐(DimensionActivities → 移到成长页) **7.2 成长页 `growth/index.vue`** - 新增:活动推荐(DimensionActivities 组件,从首页移过来) - 新增:成长类文章推荐(如图文育儿知识) **7.3 健康页 `pages/health/index.vue`** - 新增:健康类文章推荐(科普知识:体检、营养、疾病预防) ### 阶段 8:调整我的页 **8.1 `profile/profile.vue`** - 将 `ProfileMenu` 的 `showServices` prop 改为 `true` ### 阶段 9:构建验证 **9.1 语法/结构校验**(cfc-frontend AGENTS.md 强制:Agent 禁止执行 `npm run build:mp-weixin`,打包必须用 HBuilderX) ```bash # 检查所有修改过的 .vue 文件的 script 块语法 for f in $(git diff --name-only -- '*.vue'); do node -e "var s=require('fs').readFileSync('$f','utf8');var m=s.match(/