2026-08-18-homepage-health-funnel.md 15 KB

首页健康漏斗重构实现计划

日期: 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 原位

目标模板(整块替换,含新增自测卡):

  <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 行旁): 新增方法:

    goSelfTest: function() {
      uni.navigateTo({ url: '/pages/health/survey-questionnaire?dim=all' })
    },
    
  • 改动 B(computed,memberEntryBtn 计算属性 ~481-484 行后): 新增计算属性:

    // 自测大卡:已有数据(沙盘解锁)显示"再次了解",否则显示引导文案
    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):

    /* ===== ① 了解区 — 自测大卡(引导"从了解开始") ===== */
    .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 为共享组件
  • ❌ 不做测评状态条(无聚合接口,暂缓)