|
|
@@ -0,0 +1,311 @@
|
|
|
+# 首页健康漏斗重构实现计划
|
|
|
+
|
|
|
+**日期:** 2026-08-18
|
|
|
+**状态:** pending(待用户审阅)
|
|
|
+**设计:** `specs/2026-08-18-homepage-health-funnel-design.md`(方案 A,已确认)
|
|
|
+**目标文件:** `cfc-frontend/pages/index-home/index.vue`
|
|
|
+**关联背景:** `specs/2026-07-22-active-health-six-stage-design.md`(主动健康六阶模型 P0-P3 已交付)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 设计说明(与设计文档的偏差修正)
|
|
|
+
|
|
|
+设计文档 `2026-08-18-homepage-health-funnel-design.md` 写于 commit `07d897a1`(全局移除 PageBanner)之后,但其中 ① 了解区仍引用 `PageBanner theme="action"`。**当前代码已无 PageBanner**,① 了解区只剩"自测大卡"一个元素,品牌头部不再渲染。本次实现先修正设计文档该处引用,再实现代码。
|
|
|
+
|
|
|
+**已有数据判定**:设计文档建议数据源 `getLatestHealthReport` / `getAssessmentLatestResult`。实现时**复用现有 `sandboxData` 状态**判定(`sandboxData` 存在 ⟺ 已完五维自检,沙盘解锁),避免首页新增 2 个 API 调用(YAGNI)。综合得分展示 `sandboxData.overallScore`。如后续需要更细的报告摘要,可再接入报告接口。
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 任务分解
|
|
|
+
|
|
|
+### 任务 1:修正设计文档(PageBanner 已全局移除)
|
|
|
+
|
|
|
+- **文件:** `docs/superpowers/specs/2026-08-18-homepage-health-funnel-design.md`
|
|
|
+- **改动 A(第 30 行):** `│ ① 了解区 │ 品牌头部 + 理念语 + 自测大卡` → `│ ① 了解区 │ 自测大卡(品牌头部已由 07d897a1 全局移除)`
|
|
|
+- **改动 B(第 42 行):** 删除 `- **品牌头部**:保留 \`PageBanner theme="action"\`` 一行
|
|
|
+- **改动 C(第 43 行):** `- **自测大卡(新增)**:置于品牌头部下方` → `- **自测大卡(新增,首屏首个元素)**:`
|
|
|
+- **改动 D(第 48-50 行条件渲染):** 数据源补充实现说明:`已有数据判定复用首页已加载的 sandboxData(沙盘解锁即有自检数据),得分摘要取 sandboxData.overallScore,避免新增 API 调用`
|
|
|
+- **验证:** 无(纯文档)
|
|
|
+- **提交:** `docs(specs): 首页漏斗设计修正 — PageBanner 已全局移除,了解区仅自测卡`
|
|
|
+
|
|
|
+### 任务 2:模板重排 + 新增自测大卡(index-home/index.vue 登录态容器)
|
|
|
+
|
|
|
+- **文件:** `cfc-frontend/pages/index-home/index.vue` 第 54-200 行(`<view v-if="currentRole" class="logged-in-container">` 整个容器)
|
|
|
+- **做法:** 用下方"目标模板"整块替换第 54-200 行原文。所有区块内容逐字保留(仅调整位置与注释),`@action`/`@click`/`v-if`/`v-for` 绑定全部不变。
|
|
|
+
|
|
|
+**当前顺序 → 目标顺序:**
|
|
|
+
|
|
|
+| 目标区块 | 内容 | 来源 |
|
|
|
+|---|---|---|
|
|
|
+| ① 了解区 | 自测大卡(新增) | 新增 |
|
|
|
+| ② 发现区 | GateLock/WuxingSandbox 能量沙盘 | 原第 2 位,原位保留 |
|
|
|
+| ② 发现区 | ReportUploadCard 上传报告 | 原第 10 位,上移 |
|
|
|
+| ③ 行动区 | JourneyCard 旅程卡 | 原第 1 位,下移 |
|
|
|
+| ③ 行动区 | DailyTaskCard 今日任务 | 原第 9 位,上移 |
|
|
|
+| ③ 行动区 | 我的任务(v-if hasPendingTasks) | 原第 6 位,原位 |
|
|
|
+| ③ 行动区 | 家庭挑战(FamilyChallengeCard) | 原第 7 位,原位 |
|
|
|
+| ③ 行动区 | 会员入口(v-if showMemberEntry) | 原第 4 位,下移 |
|
|
|
+| ④ 内容区 | 快捷栏(quick-bar) | 原第 5 位,下移 |
|
|
|
+| ④ 内容区 | FamilyFeed 家庭动态 | 原第 3 位,下移 |
|
|
|
+| ④ 内容区 | 推荐阅读(ArticleBookshelf) | 原第 11 位,原位 |
|
|
|
+| ④ 内容区 | 推荐商品(DimensionProducts) | 原第 12 位,原位(最底) |
|
|
|
+| — | 底部占位 bottom-spacer | 原位 |
|
|
|
+
|
|
|
+**目标模板(整块替换,含新增自测卡):**
|
|
|
+
|
|
|
+```html
|
|
|
+ <view v-if="currentRole" class="logged-in-container">
|
|
|
+ <!-- ① 了解区 — 自测大卡(首屏,引导"从了解开始") -->
|
|
|
+ <view class="self-test-card" @click="goSelfTest">
|
|
|
+ <view class="self-test-head">
|
|
|
+ <text class="self-test-icon">⚡</text>
|
|
|
+ <view class="self-test-info">
|
|
|
+ <text class="self-test-title">{{ selfTestTitle }}</text>
|
|
|
+ <text class="self-test-sub">{{ selfTestSub }}</text>
|
|
|
+ </view>
|
|
|
+ <text class="self-test-arrow">›</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- ② 发现区 — 能量沙盘 — Canvas 五行星图(未测评时显示引导卡) -->
|
|
|
+ <GateLock v-if="sandboxLocked && sandboxLockGate" :gate="sandboxLockGate" />
|
|
|
+ <WuxingSandbox
|
|
|
+ v-else-if="sandboxData"
|
|
|
+ mode="live"
|
|
|
+ :scores="sandboxScores"
|
|
|
+ :overallScore="sandboxData.overallScore || 0"
|
|
|
+ :members="sandboxData.members || []"
|
|
|
+ showDimensionBar
|
|
|
+ starHeight="480rpx" />
|
|
|
+
|
|
|
+ <!-- ② 发现区 — 上传报告 -->
|
|
|
+ <view class="section">
|
|
|
+ <ReportUploadCard
|
|
|
+ title="上传报告"
|
|
|
+ desc="体检报告 · 菌群检测 · 认知测评 · 心理测评 · 情绪打卡"
|
|
|
+ :tags="['体检报告', '菌群检测', '认知测评', '心理测评', '情绪打卡']"
|
|
|
+ @click="goToUploadReport" />
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- ③ 行动区 — 旅程状态卡 — 改版v1 需求驱动(A类·随便看看 不展示旅程卡) -->
|
|
|
+ <JourneyCard
|
|
|
+ v-if="userIntent && userIntent.intentType !== 'A'"
|
|
|
+ :intent="userIntent"
|
|
|
+ @action="handleJourneyAction" />
|
|
|
+
|
|
|
+ <!-- ③ 行动区 — 今日任务 -->
|
|
|
+ <DailyTaskCard />
|
|
|
+
|
|
|
+ <!-- ③ 行动区 — 我的任务(仅当有待完成时显示) -->
|
|
|
+ <view v-if="hasPendingTasks" class="section">
|
|
|
+ <view class="section-header">
|
|
|
+ <text class="section-title">📝 我的任务</text>
|
|
|
+ <text class="section-more" @click="navTo('/pages/tasks/tasks')">全部 ›</text>
|
|
|
+ </view>
|
|
|
+ <view class="task-quick-list">
|
|
|
+ <view
|
|
|
+ v-for="task in pendingTasks"
|
|
|
+ :key="task.id"
|
|
|
+ class="task-quick-item"
|
|
|
+ >
|
|
|
+ <view class="task-quick-info">
|
|
|
+ <text class="task-quick-title line-clamp-1">{{ task.title }}</text>
|
|
|
+ <text class="task-quick-meta">+{{ task.points }}分</text>
|
|
|
+ </view>
|
|
|
+ <button class="btn-quick-complete" @click="quickCompleteTask(task)">完成</button>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- ③ 行动区 — 家庭挑战任务 -->
|
|
|
+ <view class="section">
|
|
|
+ <view class="section-header">
|
|
|
+ <text class="section-title">🔥 家庭挑战</text>
|
|
|
+ <text class="section-more" @click="goChallengeManage">管理 ›</text>
|
|
|
+ </view>
|
|
|
+ <FamilyChallengeCard
|
|
|
+ v-for="ch in activeChallenges"
|
|
|
+ :key="ch.id"
|
|
|
+ :challenge="ch"
|
|
|
+ :showCheckin="showCheckinBtn"
|
|
|
+ @checkin="onChallengeCheckin"
|
|
|
+ @respond="onChallengeRespond" />
|
|
|
+ <view v-if="activeChallenges.length === 0" class="challenge-empty">
|
|
|
+ <text class="challenge-empty-icon">🏆</text>
|
|
|
+ <text class="challenge-empty-text">您还没有挑战,去添加吧</text>
|
|
|
+ <button class="challenge-add-btn" @click="goChallengeManage">去添加</button>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- ③ 行动区 — 会员入口:非会员显示开通 / 快到期显示续费 -->
|
|
|
+ <view v-if="showMemberEntry" class="member-entry" @click="goMembership">
|
|
|
+ <view class="member-entry-icon-wrap">
|
|
|
+ <text class="member-entry-icon">👑</text>
|
|
|
+ </view>
|
|
|
+ <view class="member-entry-texts">
|
|
|
+ <text class="member-entry-title">{{ memberEntryTitle }}</text>
|
|
|
+ <text class="member-entry-desc">{{ memberEntryDesc }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="member-entry-btn">
|
|
|
+ <text class="member-entry-btn-text">{{ memberEntryBtn }}</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- ④ 内容区 — 快捷功能入口(单排) -->
|
|
|
+ <view class="section">
|
|
|
+ <view class="quick-bar">
|
|
|
+ <view class="quick-item" @click="navTo('/pages/tasks/tasks')">
|
|
|
+ <view class="quick-icon" style="background:rgba(99,102,241,0.15)"><text>📋</text></view>
|
|
|
+ <text class="quick-label">任务</text>
|
|
|
+ </view>
|
|
|
+ <view class="quick-item" @click="navTo('/pages/action-detail/interaction-log')">
|
|
|
+ <view class="quick-icon" style="background:rgba(16,185,129,0.15)"><text>💬</text></view>
|
|
|
+ <text class="quick-label">互动</text>
|
|
|
+ </view>
|
|
|
+ <view class="quick-item" @click="navTo('/pages/mind-detail/family-dashboard')">
|
|
|
+ <view class="quick-icon" style="background:rgba(139,92,246,0.15)"><text>🏠</text></view>
|
|
|
+ <text class="quick-label">天盘</text>
|
|
|
+ </view>
|
|
|
+ <view class="quick-item" @click="navTo('/pages/profile-extra/family-members')">
|
|
|
+ <view class="quick-icon" style="background:rgba(245,158,11,0.15)"><text>👨👩👧👦</text></view>
|
|
|
+ <text class="quick-label">成员</text>
|
|
|
+ </view>
|
|
|
+ <view class="quick-item" @click="navTo('/pages/discover/circles')">
|
|
|
+ <view class="quick-icon" style="background:rgba(255,107,157,0.15)"><text>🤝</text></view>
|
|
|
+ <text class="quick-label">圈子</text>
|
|
|
+ </view>
|
|
|
+ <view class="quick-item" @click="navTo('/pages/shop/index/index')">
|
|
|
+ <view class="quick-icon" style="background:rgba(249,115,22,0.15)"><text>🛒</text></view>
|
|
|
+ <text class="quick-label">商城</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- ④ 内容区 — 家庭动态 -->
|
|
|
+ <FamilyFeed
|
|
|
+ :items="familyFeed"
|
|
|
+ :notices="notices"
|
|
|
+ :showHeader="true" />
|
|
|
+
|
|
|
+ <!-- ④ 内容区 — 推荐文章 -->
|
|
|
+ <view class="section">
|
|
|
+ <view class="section-header">
|
|
|
+ <text class="section-title">📖 推荐阅读</text>
|
|
|
+ <text class="section-more" @click="goMoreArticles">更多 ›</text>
|
|
|
+ </view>
|
|
|
+ <ArticleBookshelf
|
|
|
+ :articles="actionArticles"
|
|
|
+ :isLoggedIn="true"
|
|
|
+ @articleClick="goArticleDetail" />
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- ④ 内容区 — 推荐商品 -->
|
|
|
+ <view class="section">
|
|
|
+ <view class="section-header">
|
|
|
+ <text class="section-title">🛍️ 推荐商品</text>
|
|
|
+ <text class="section-more" @click="goMoreProducts">更多 ›</text>
|
|
|
+ </view>
|
|
|
+ <DimensionProducts
|
|
|
+ :isLoggedIn="true"
|
|
|
+ :showMore="false"
|
|
|
+ @productClick="goProductDetail" />
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 底部占位 -->
|
|
|
+ <view class="bottom-spacer" style="height: 40rpx;"></view>
|
|
|
+ </view>
|
|
|
+```
|
|
|
+
|
|
|
+- **验证:** 替换后运行模板标签平衡检查(`<view>` 开闭计数一致),确认无遗漏/多闭合
|
|
|
+
|
|
|
+### 任务 3:新增 goSelfTest 方法与 selfTestTitle/selfTestSub 计算属性
|
|
|
+
|
|
|
+- **文件:** `cfc-frontend/pages/index-home/index.vue`
|
|
|
+- **改动 A(methods,`goToUploadReport` 方法 ~847 行旁):** 新增方法:
|
|
|
+
|
|
|
+```js
|
|
|
+ goSelfTest: function() {
|
|
|
+ uni.navigateTo({ url: '/pages/health/survey-questionnaire?dim=all' })
|
|
|
+ },
|
|
|
+```
|
|
|
+
|
|
|
+- **改动 B(computed,`memberEntryBtn` 计算属性 ~481-484 行后):** 新增计算属性:
|
|
|
+
|
|
|
+```js
|
|
|
+ // 自测大卡:已有数据(沙盘解锁)显示"再次了解",否则显示引导文案
|
|
|
+ selfTestTitle: function() {
|
|
|
+ return this.sandboxData ? '再次了解 · 看看变化' : '先花 1 分钟,了解全家健康状况'
|
|
|
+ },
|
|
|
+ selfTestSub: function() {
|
|
|
+ if (this.sandboxData) {
|
|
|
+ return '最近综合得分 ' + (this.sandboxData.overallScore || 0) + ' 分 · 点击重新自检'
|
|
|
+ }
|
|
|
+ return '15 题五维自检 · 约 20 秒'
|
|
|
+ },
|
|
|
+```
|
|
|
+
|
|
|
+- **验证:** 提取 script 块 `node --check` 通过
|
|
|
+
|
|
|
+### 任务 4:新增 self-test-card 样式
|
|
|
+
|
|
|
+- **文件:** `cfc-frontend/pages/index-home/index.vue` `<style>` 块(1159 行起)
|
|
|
+- **做法:** 在 `.logged-in-container` 样式(1705 行)附近新增以下样式(从 health-main 复制,水平外边距对齐首页 `.section` 的 30rpx、底部对齐 16rpx):
|
|
|
+
|
|
|
+```css
|
|
|
+/* ===== ① 了解区 — 自测大卡(引导"从了解开始") ===== */
|
|
|
+.self-test-card {
|
|
|
+ margin: 20rpx 30rpx 16rpx;
|
|
|
+ background: linear-gradient(135deg, #667eea, #764ba2);
|
|
|
+ border-radius: 20rpx;
|
|
|
+ padding: 24rpx 28rpx;
|
|
|
+}
|
|
|
+.self-test-head { display: flex; align-items: center; }
|
|
|
+.self-test-icon { font-size: 40rpx; margin-right: 16rpx; }
|
|
|
+.self-test-info { flex: 1; }
|
|
|
+.self-test-title { font-size: 28rpx; font-weight: bold; color: #fff; display: block; }
|
|
|
+.self-test-sub { font-size: 22rpx; color: rgba(255,255,255,0.8); display: block; margin-top: 4rpx; }
|
|
|
+.self-test-arrow { font-size: 36rpx; color: rgba(255,255,255,0.7); }
|
|
|
+```
|
|
|
+
|
|
|
+- **验证:** 无 JS 变更,检查样式类名与模板引用一致(self-test-card / self-test-head / self-test-icon / self-test-info / self-test-title / self-test-sub / self-test-arrow 共 7 个类)
|
|
|
+
|
|
|
+### 任务 5:验证 + 提交推送
|
|
|
+
|
|
|
+- **验证步骤(全部通过才可提交):**
|
|
|
+ 1. 提取 script 块 `node --check`(复用此前验证脚本,0 错误)
|
|
|
+ 2. 模板 `<view>` 开闭标签平衡(0 差异)
|
|
|
+ 3. 确认模板中 `PageBanner` 引用为 0(设计文档已同步修正)
|
|
|
+ 4. 确认 `goSelfTest` 方法存在、`selfTestTitle`/`selfTestSub` 计算属性存在
|
|
|
+ 5. 未登录态(discover-container,207 行起)内容逐字未动(git diff 校验)
|
|
|
+- **提交:** `feat(home): 首页健康漏斗重构 — 了解区自测卡 + 四段式区块重排`(仅含 index-home/index.vue)
|
|
|
+- **推送:** `git push origin cfclub`
|
|
|
+- **最后一步(用户执行):** HBuilderX 重新打包 → 微信开发者工具查看效果
|
|
|
+
|
|
|
+### 任务 6:同步 PROJECT-OVERVIEW.md
|
|
|
+
|
|
|
+- **文件:** `docs/superpowers/PROJECT-OVERVIEW.md`
|
|
|
+- **改动:** 首页漏斗设计条目(2.4 表)状态改为"实现中/已完成",文档索引表补 `plans/2026-08-18-homepage-health-funnel.md` 条目
|
|
|
+- **提交:** `docs(overview): 同步首页健康漏斗实现计划`(与任务 5 同一提交或独立提交均可)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 验收标准(对应设计文档第五节)
|
|
|
+
|
|
|
+- [ ] 登录态首页从上到下:① 了解区(自测大卡)→ ② 发现区(沙盘 + 上传报告)→ ③ 行动区(旅程卡/今日任务/我的任务/家庭挑战/会员)→ ④ 内容区(快捷栏/动态/推荐阅读/推荐商品)
|
|
|
+- [ ] 首屏第一个元素是自测大卡,点击进入 `/pages/health/survey-questionnaire?dim=all`
|
|
|
+- [ ] 未测评用户显示引导文案;已测评(sandboxData 存在)显示"再次了解 · 最近综合得分 X 分"
|
|
|
+- [ ] `ReportUploadCard` 位于能量沙盘下方(发现区)
|
|
|
+- [ ] `DailyTaskCard` 位于行动区(旅程卡下方)
|
|
|
+- [ ] `JourneyCard` 从首屏移到行动区顶部,C 类意图显示 / A 类隐藏逻辑不变
|
|
|
+- [ ] 会员入口在行动区底部,`showMemberEntry` 逻辑不变
|
|
|
+- [ ] 快捷栏、家庭动态、推荐阅读、推荐商品依次在底部内容区
|
|
|
+- [ ] 未登录态首页(discover-container)无任何改动
|
|
|
+- [ ] 全部 JS 语法检查通过;HBuilderX 重新打包后显示正常
|
|
|
+
|
|
|
+## 明确不做(YAGNI,延续设计文档第六节)
|
|
|
+
|
|
|
+- ❌ 不做角色分流(家长/孩子首页不拆分)
|
|
|
+- ❌ 不新增后端接口(已有数据判定复用 sandboxData)
|
|
|
+- ❌ 不改未登录态
|
|
|
+- ❌ 不新增组件依赖,不抽取 self-test-card 为共享组件
|
|
|
+- ❌ 不做测评状态条(无聚合接口,暂缓)
|