2026-06-15-code-design-discrepancy-fix.md 21 KB

代码 vs 设计差异修复计划

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: 修复小程序代码与设计文档(v2.0 + 五维页面重设计)之间的所有差异,使实现完全对齐设计

架构: 前端修复为主,涉及 7 个页面/组件和 1 个配置文件。后端补充 Activity 可见性三字段和 Product 游客价格逻辑。分批并行修改:先改 TabBar 和路由等基础设施,再改每个 Tab 页的逻辑

Tech Stack: uni-app Vue 2, Spring Boot 2.7

优先级: P0(阻塞) > P1(重要) > P2(一般) > P3(优化)


文件改动清单

文件 操作 职责
cfc-frontend/pages.json 修改 TabBar 第5个 Tab 改名为"富沛",路径改为 pages/wealth/index
cfc-frontend/pages/index/index.vue 修改 移除 teacher-index 路由分支;移除 TeacherIndex import;未登录态商品/活动/文章统一改为 /api/product/list;隐藏价格;弹登录提示
cfc-frontend/pages/index/parent-index.vue 修改 移除五行沙盘;改为服务商入口+代办;增加家庭能量横条(顶部);增加维度商品/活动区块
cfc-frontend/pages/index/child-index.vue 修改 增加家庭能量横条(顶部);增加维度商品/活动区块;reLaunchswitchTab
cfc-frontend/pages/teacher/index.vue 废弃 整个页面废弃,但保留文件做软删除(用注释标记废弃 + 路由不直达)
cfc-frontend/pages/body/index.vue 修改 游客可见商品/活动(无价格);BASE_URL 硬编码改 config
cfc-frontend/pages/mind/index.vue 修改 FamilyEnergyBar 传 dualDimension 双维度参数实现堆叠图;游客可见商品/活动(无价格)
cfc-frontend/pages/action/index.vue 修改 游客可见商品/活动(无价格);funcList 商城路径修复
cfc-frontend/components/FamilyEnergyBar.vue 修改 支持 dualDimension 属性,当传入时渲染堆叠柱(心+智叠加)
cfc-frontend/utils/api.js 修改 首页/发现页商品接口从 DANSHOP_BASE 改为 /api/product/list
cfc-backend/.../controller/product/ProductController.java 修改 游客模式:无 token 请求时 price/memberPrice 返回 null
cfc-backend/.../entity/Activity.java 修改 增加 visibility/visibleScope/visibleTo 三字段

Task 1: TabBar 第5个 Tab 改为「富沛」

Files:

  • Modify: cfc-frontend/pages.json:589-626

  • [ ] Step 1: 修改 pages.json TabBar 配置

    // 在 pages.json 中找到 tabBar.list 第5项,改为:
    {
    "pagePath": "pages/wealth/index",
    "text": "富沛",
    "iconPath": "static/tab-wealth.png",
    "selectedIconPath": "static/tab-wealth-active.png"
    }
    

同时确认 pages/wealth/index 已在 subPackages 中正确注册(第 560-581 行已存在 pages/wealth 子包)。

  • Step 2: 验证

确认 pages.json wealth 子包已包含 pages/wealth/index 页面,确认 static/tab-wealth.pngstatic/tab-wealth-active.png 图标文件存在。如果不存在,创建 48x48px 的占位图标。

  • Step 3: 清理旧 Tab 图片引用

static/ 目录检查是否有 tab-profile.png / tab-profile-active.png,确认不再被引用。


Task 2: 首页 index.vue — 移除教师路由 + 未登录态商品接口改造

Files:

  • Modify: cfc-frontend/pages/index/index.vue

  • [ ] Step 1: 移除 TeacherIndex 引入(第 108 行)

修改前:

import TeacherIndex from '../teacher/index.vue'

修改后:

// TeacherIndex 已废弃,教师功能折入 parent-index

同时从 components 对象中移除 TeacherIndex

  • Step 2: 移除 teacher 路由分支(第 97 行)

修改前:

<teacher-index v-else-if="currentRole === 'teacher'" />

修改后:

<!-- teacher-index 已废弃,教师功能折入 parent-index -->
  • Step 3: 未登录态商品列表改用 /api/product/list

修改前(第 200 行):

productList(params).then(res => { ... })

修改后:

// 调用 getProductsByDomain 或其他 api.js 中已封装的 /api/product/list
  • Step 4: 未登录态商品隐藏价格(第 44-48 行)

修改前:

<view class="product-price-row">
  <text class="product-price">¥{{ item.price }}</text>
  <text v-if="item.memberPrice" class="product-member">会员¥{{ item.memberPrice }}</text>
</view>

修改后:

<view class="product-price-row">
  <!-- 游客不显示价格 -->
  <text class="product-price-login">登录查看</text>
</view>
  • Step 5: 未登录态商品点击弹登录提示(第 40 行)

修改前:

@click="goDetail(item.id)"

修改后:

@click="handleLogin()"

并在 handleLogin 中:

handleLogin() {
  uni.showModal({
    title: '提示',
    content: '请先登录后查看详情',
    success: function(res) {
      if (res.confirm) uni.navigateTo({ url: '/pages/login/login?redirect=' + encodeURIComponent('/pages/index/index') })
    }
  })
}
  • Step 6: 确保 onShow 中调用 loadProducts()

确认在 onShow 的未登录分支中调用 this.loadProducts(),使游客首次进入能看到商品列表。

onShow() {
  if (!this.isLoggedIn()) {
    this.currentRole = ''
    this.loadProducts() // 游客加载商品
    return
  }
  this.syncRoleAndTabBar()
}

Task 3: parent-index.vue — 移除沙盘 + 增加服务商入口 + 家庭能量横条

Files:

  • Modify: cfc-frontend/pages/index/parent-index.vue

  • [ ] Step 1: 移除五行沙盘(第 74-84 行)

删除整个 wuxing-sandbox 组件的实例化和 import。

修改前:

<!-- 五行能量沙盘(浠艾福 — 全家庭版) -->
<view class="animate-fade-in animate-stagger-3">
<wuxing-sandbox mode="live"
  :scores="energyScores"
  :overall-score="energyOverall"
  :members="energyMembers"
  :dimensions="energyDimensions"
  :total-energy="totalEnergy"
  :total-health-index="totalHealthIndex"
  @point-click="goToDomain" />
</view>

修改后:删除。

  • Step 2: 顶部增加家庭能量横条 FamilyEnergyBar

在 Banner 下方增加:

<!-- 家庭能量横条 -->
<FamilyEnergyBar
  v-if="hasFamilyData"
  dimensionCode="parent"
  :sandboxData="sandboxData" />

添加对应 import:

import FamilyEnergyBar from '@/components/FamilyEnergyBar.vue'
  • Step 3: 增加服务商快捷入口(替代废弃的 teacher-index)

在「待处理事项」区域增加服务商快捷卡片,当用户角色为 teacherrole 包含 teacher 时显示:

<!-- ===== 服务商快捷入口(teacher 折入) ===== -->
<view class="teacher-quick-section" v-if="isTeacher">
  <view class="section-title">🔧 服务商工具</view>
  <view class="pending-grid">
    <view class="pending-card" @click="goToTeacherFamilies">
      <view class="pending-icon pending-icon--blue">
        <text class="pending-emoji">👨‍👩‍👧</text>
      </view>
      <view class="pending-info">
        <text class="pending-count accent-color">{{ serviceFamilyCount }}</text>
        <text class="pending-label">服务家庭</text>
      </view>
    </view>
    <view class="pending-card" @click="goToAssignTask">
      <view class="pending-icon pending-icon--green">
        <text class="pending-emoji">📝</text>
      </view>
      <view class="pending-info">
        <text class="pending-count">{{ pendingTaskCount }}</text>
        <text class="pending-label">待办任务</text>
      </view>
    </view>
    <view class="pending-card" @click="goToIncome">
      <view class="pending-icon pending-icon--gold">
        <text class="pending-emoji">💰</text>
      </view>
      <view class="pending-info">
        <text class="pending-count">¥{{ todayIncome }}</text>
        <text class="pending-label">今日收入</text>
      </view>
    </view>
    <view class="pending-card" @click="goToTeam">
      <view class="pending-icon pending-icon--purple">
        <text class="pending-emoji">👥</text>
      </view>
      <view class="pending-info">
        <text class="pending-count">{{ teamCount }}</text>
        <text class="pending-label">团队</text>
      </view>
    </view>
  </view>
</view>

data 中增加字段:isTeacher: false, serviceFamilyCount: 0, pendingTaskCount: 0, todayIncome: 0, teamCount: 0

  • Step 4: 增加维度商品/活动推荐区块

在「成长建议」后面增加:

<!-- ===== 推荐活动 ===== -->
<DimensionActivities
  dimensionCode="all"
  :activities="recommendedActivities"
  @activityClick="goActivityDetail"
  @moreActivities="goMoreActivities" />

<!-- ===== 推荐商品 ===== -->
<DimensionProducts
  dimensionCode="all"
  :products="recommendedProducts"
  @productClick="goProductDetail"
  @moreProducts="goMoreProducts" />

添加 import:import DimensionActivities from '@/components/DimensionActivities.vue'import DimensionProducts from '@/components/DimensionProducts.vue'


Task 4: child-index.vue — 增加家庭能量横条 + 维度商品/活动

Files:

  • Modify: cfc-frontend/pages/index/child-index.vue

  • [ ] Step 1: 在 Banner 下方增加 FamilyEnergyBar

    <!-- 家庭能量横条 -->
    <FamilyEnergyBar
    v-if="hasChildren"
    dimensionCode="child"
    :sandboxData="sandboxData" />
    

添加 import:import FamilyEnergyBar from '@/components/FamilyEnergyBar.vue'

  • Step 2: 增加维度商品/活动区块

在「最近能量变动」下方增加:

<!-- ===== 推荐活动 ===== -->
<DimensionActivities
  v-if="hasChildren"
  dimensionCode="child"
  :activities="dimensionActivities"
  @activityClick="goActivityDetail"
  @moreActivities="goMoreActivities" />

<!-- ===== 推荐商品 ===== -->
<DimensionProducts
  v-if="hasChildren"
  dimensionCode="child"
  :products="dimensionProducts"
  @productClick="goProductDetail"
  @moreProducts="goMoreProducts" />

添加对应 import,data 中增加 dimensionActivities: [], dimensionProducts: [],loadData 中调用对应加载方法。

  • Step 3: reLaunch 改 switchTab(第 432 行)

修改前:

uni.reLaunch({ url: '/pages/index/parent-index' })

修改后:

uni.switchTab({ url: '/pages/index/index' })

Task 5: teacher/index.vue — 软废弃

Files:

  • Modify: cfc-frontend/pages/teacher/index.vue

  • [ ] Step 1: 文件首部添加废弃标记

<template> 上方添加注释:

<!--
  ⚠️ 废弃说明(2026-06-15):
  此页面已按照设计决策废弃。教师首页功能已折入 parent-index。
  保留文件仅用于参考,不通过任何路由访问。
-->
  • Step 2: 模板替换为提示页

将整个 <template> 内容替换为简洁提示:

<template>
  <view class="deprecated-container">
    <text class="deprecated-icon">📋</text>
    <text class="deprecated-title">服务商功能已整合</text>
    <text class="deprecated-desc">服务商工具和待办已迁移至家长首页</text>
    <view class="deprecated-btn" @click="goToHome">回到首页</view>
  </view>
</template>

保留 script 中 goToHome 方法(uni.switchTab({ url: '/pages/index/index' }))。

  • Step 3: 确认 pages.json 中 teacher 子包保留

teacher 子包页面(families/team/messages/orders 等)作为功能页面仍可通过导航访问,仅首页废弃。无需移除 subPackage 注册。


Task 6: body/index.vue — 游客可见商品/活动 + 修复 BASE_URL

Files:

  • Modify: cfc-frontend/pages/body/index.vue

  • [ ] Step 1: 维度商品/活动/任务对游客可见(不隐藏,仅无价格)

修改前(第 94-107 行):

<DimensionActivities
  v-if="isLoggedIn"
  ...
/>
<DimensionProducts
  v-if="isLoggedIn"
  ...
/>
<DimensionTasks
  v-if="isLoggedIn"
  ...
/>

修改后:

<DimensionActivities
  ...
/>
<DimensionProducts
  ...
/>
<DimensionTasks
  v-if="isLoggedIn"
  ...
/>

(任务仍需要登录,因为关联孩子;活动和商品游客可浏览)

onShow 中,游客也需要加载活动和商品数据:

this.loadDimensionActivities()
this.loadDimensionProducts()
  • Step 2: 修复 BASE_URL 硬编码(第 150 行)

修改前:

const BASE_URL = 'http://localhost:8080'

修改后:

import config from '../../config.js'
const BASE_URL = config.api('')

Task 7: mind/index.vue — 堆叠图 + 游客可见商品/活动

Files:

  • Modify: cfc-frontend/pages/mind/index.vue
  • Modify: cfc-frontend/components/FamilyEnergyBar.vue

  • [ ] Step 1: mind 页 FamilyEnergyBar 传入 dualDimension 参数(第 20-24 行)

修改前:

<FamilyEnergyBar
  v-if="isLoggedIn"
  dimensionCode="mind"
  :sandboxData="sandboxData"
  :dualDimension="dualDimension" />

dualDimension 当前只传了 mind 维度的数据。需要改为包含 mind + wisdom 两个维度的堆叠数据对象。

loadEnergyData 方法中修改(第 662-680 行):

loadEnergyData: function() {
  var self = this
  if (!this.currentChildId) return
  getEnergyOverview(this.currentChildId).then(function(res) {
    if (res && res.data) {
      var dims = res.data.dimensions
      if (dims && dims.length > 0) {
        var mindDim = null
        var wisdomDim = null
        for (var i = 0; i < dims.length; i++) {
          if (dims[i].code === 'mind') mindDim = dims[i]
          if (dims[i].code === 'wisdom') wisdomDim = dims[i]
        }
        // dualDimension 格式:{ mind: { score, label, color }, wisdom: { score, label, color } }
        self.dualDimension = {
          primary: mindDim || { code: 'mind', score: 0, label: '心理' },
          secondary: wisdomDim || { code: 'wisdom', score: 0, label: '认知' }
        }
      }
    }
  }).catch(function(e) {
    console.log('获取能量概览失败', e)
  })
}
  • Step 2: FamilyEnergyBar 支持 dualDimension 堆叠渲染

cfc-frontend/components/FamilyEnergyBar.vue 中,当 dualDimension 为对象且包含 { primary, secondary } 时,能量柱渲染为上下堆叠:

<!-- 堆叠柱渲染 -->
<view class="stacked-bar" v-if="dualDimension && dualDimension.primary">
  <view class="stacked-bar-segment stacked-bar--primary"
    :style="{ height: primaryPercent + '%', background: '#FF6B35' }"
    @click="onSegmentClick('mind')">
    <text class="segment-label">心 {{ dualDimension.primary.score }}</text>
  </view>
  <view class="stacked-bar-segment stacked-bar--secondary"
    :style="{ height: secondaryPercent + '%', background: '#8B5CF6' }"
    @click="onSegmentClick('wisdom')">
    <text class="segment-label">智 {{ dualDimension.secondary.score }}</text>
  </view>
</view>

computed 增加:

primaryPercent() {
  if (!this.dualDimension || !this.dualDimension.primary) return 0
  return Math.min(100, this.dualDimension.primary.score || 0)
},
secondaryPercent() {
  if (!this.dualDimension || !this.dualDimension.secondary) return 0
  return Math.min(100, this.dualDimension.secondary.score || 0)
}
  • Step 3: 游客可见维度商品/活动(同 Task 6 Step 1)

修改前(第 261-280 行):

<DimensionTasks v-if="isLoggedIn" ... />
<DimensionActivities v-if="isLoggedIn" ... />
<DimensionProducts v-if="isLoggedIn" ... />

修改后:

<DimensionTasks v-if="isLoggedIn" ... />
<DimensionActivities ... />
<DimensionProducts ... />

Task 8: action/index.vue — 游客可见商品/活动 + 修复商城路径

Files:

  • Modify: cfc-frontend/pages/action/index.vue

  • [ ] Step 1: 游客可见维度商品/活动

修改前(第 37-56 行):

<DimensionTasks v-if="isLoggedIn" ... />
<DimensionActivities v-if="isLoggedIn" ... />
<DimensionProducts v-if="isLoggedIn" ... />

修改后:

<DimensionTasks v-if="isLoggedIn" ... />
<DimensionActivities ... />
<DimensionProducts ... />
  • Step 2: 修复 funcList 商城路径(第 141 行)

修改前:

{ icon: '\u{1F6CD}', label: '商城', needLogin: false, page: '/pages/shop/index' }

修改后:

{ icon: '\u{1F6CD}', label: '商城', needLogin: false, page: '/pages/shop/detail/detail' }

确认实际商城页面路径(pages.json 中 shop 子包入口页为 detail/detail,或者改为 '/pages/discover/index?type=product')。


Task 9: DimensionProducts 组件 — 支持游客模式隐藏价格

Files:

  • Modify: cfc-frontend/components/DimensionProducts.vue

  • [ ] Step 1: 增加 isLoggedIn prop

    props: {
    // ... 现有 props
    isLoggedIn: { type: Boolean, default: false }
    }
    
  • [ ] Step 2: 价格区域根据 isLoggedIn 切换显示

在价格渲染位置:

<view class="product-price-row" v-if="isLoggedIn">
  <text class="product-price">¥{{ item.price }}</text>
  <text v-if="item.memberPrice" class="product-member">会员¥{{ item.memberPrice }}</text>
</view>
<view class="product-price-row" v-else>
  <text class="product-login-tip">登录查看</text>
</view>

在 body/mind/action 页使用时传入 :isLoggedIn="isLoggedIn"


Task 10: DimensionActivities 组件 — 支持游客空占位

Files:

  • Modify: cfc-frontend/components/DimensionActivities.vue

  • [ ] Step 1: 增加 isLoggedIn prop

    props: {
    // ... 现有 props
    isLoggedIn: { type: Boolean, default: false }
    }
    
  • [ ] Step 2: 空数据时区分游客和登录态

在空数据展示处:

<view class="activities-empty" v-if="activities.length === 0">
  <text class="empty-tip">{{ isLoggedIn ? '暂无相关活动' : '登录后查看更多活动' }}</text>
</view>

在 body/mind/action 页使用时传入 :isLoggedIn="isLoggedIn"


Task 11: 后端 — 商品游客价格返回 null

Files:

  • Modify: cfc-backend/src/main/java/com/etotem/cfc/controller/product/ProductController.java

  • [ ] Step 1: 商品列表接口:无 token 时 price/memberPrice 返回 null

ProductController 的商品查询方法中,检查 @RequestAttribute("userId") 是否为 null。若无 userId(游客),将商品 DTO 中的 price/memberPrice 字段置为 null:

// 在返回前遍历商品列表
if (userId == null) {
    // 游客模式:隐藏价格
    productDTO.setPrice(null);
    productDTO.setMemberPrice(null);
}

Task 12: 后端 — Activity 可见性三字段

Files:

  • Modify: cfc-backend/src/main/java/com/etotem/cfc/entity/Activity.java
  • Modify: cfc-backend/src/main/java/com/etotem/cfc/controller/sncp/ActivityController.java

  • [ ] Step 1: Activity 实体增加可见性字段

    /** 可见性:public(公开) / restricted(私密) */
    private String visibility;
    
    /** 可见范围:family(家庭) / role(角色) / user(用户) */
    private String visibleScope;
    
    /** 可见对象标识:家庭ID/角色名/用户ID 的 JSON 数组,如 "["family_1","family_2"]" */
    private String visibleTo;
    

对应增加 getter/setter(可用 Lombok @Data)。

  • Step 2: 活动列表接口增加可见性过滤

ActivityController 的列表查询中:

// 游客:只返回 public 活动
if (userId == null) {
    queryWrapper.eq("visibility", "public");
} else {
    // 登录用户:public + 自己有权限的 restricted 活动
    queryWrapper.and(w -> 
        w.eq("visibility", "public")
        .or(w2 -> w2.eq("visibility", "restricted")
            .and(w3 -> {
                // visibleScope = family → familyId 在 visibleTo 中
                // visibleScope = role → role 在 visibleTo 中
                // visibleScope = user → userId 在 visibleTo 中
                ...
            })
        )
    );
}

Task 13: 验证清单

  • Step 1: LSP 诊断

对每个修改过的前端文件运行 lsp_diagnostics,确保无错误。

  • [ ] Step 2: 后端编译

    cd cfc-backend && mvn clean compile -q
    

预期输出:BUILD SUCCESS

  • Step 3: 功能验收
验收项 预期结果
TabBar 第5个 Tab 显示"富沛" 图标和文字正确
未登录首页商品列表显示 显示商品封面和名称,无价格,显示"登录查看"
未登录点击商品 弹登录提示
游客进入身体/心智/行动页 看到活动/商品列表,无价格
家长首页无沙盘 五行沙盘不渲染
家长首页有服务商入口 teacher 用户看到服务商工具卡片
心智页能量柱为堆叠图 一根柱上下显示心+智
教师首页废弃 直接跳转到 /pages/teacher/index 显示废弃提示