# 代码 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` | 修改 | 增加家庭能量横条(顶部);增加维度商品/活动区块;`reLaunch` 改 `switchTab` |
| `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 配置**
```json
// 在 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.png` 和 `static/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 行)**
修改前:
```javascript
import TeacherIndex from '../teacher/index.vue'
```
修改后:
```javascript
// TeacherIndex 已废弃,教师功能折入 parent-index
```
同时从 `components` 对象中移除 `TeacherIndex`。
- [ ] **Step 2: 移除 teacher 路由分支(第 97 行)**
修改前:
```html
```
修改后:
```html
```
- [ ] **Step 3: 未登录态商品列表改用 /api/product/list**
修改前(第 200 行):
```javascript
productList(params).then(res => { ... })
```
修改后:
```javascript
// 调用 getProductsByDomain 或其他 api.js 中已封装的 /api/product/list
```
- [ ] **Step 4: 未登录态商品隐藏价格(第 44-48 行)**
修改前:
```html
¥{{ item.price }}
会员¥{{ item.memberPrice }}
```
修改后:
```html
登录查看
```
- [ ] **Step 5: 未登录态商品点击弹登录提示(第 40 行)**
修改前:
```html
@click="goDetail(item.id)"
```
修改后:
```html
@click="handleLogin()"
```
并在 `handleLogin` 中:
```javascript
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()`,使游客首次进入能看到商品列表。
```javascript
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。
修改前:
```html
```
修改后:删除。
- [ ] **Step 2: 顶部增加家庭能量横条 FamilyEnergyBar**
在 Banner 下方增加:
```html
```
添加对应 import:
```javascript
import FamilyEnergyBar from '@/components/FamilyEnergyBar.vue'
```
- [ ] **Step 3: 增加服务商快捷入口(替代废弃的 teacher-index)**
在「待处理事项」区域增加服务商快捷卡片,当用户角色为 `teacher` 或 `role` 包含 `teacher` 时显示:
```html
🔧 服务商工具
👨👩👧
{{ serviceFamilyCount }}
服务家庭
📝
{{ pendingTaskCount }}
待办任务
💰
¥{{ todayIncome }}
今日收入
👥
{{ teamCount }}
团队
```
data 中增加字段:`isTeacher: false`, `serviceFamilyCount: 0`, `pendingTaskCount: 0`, `todayIncome: 0`, `teamCount: 0`
- [ ] **Step 4: 增加维度商品/活动推荐区块**
在「成长建议」后面增加:
```html
```
添加 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**
```html
```
添加 import:`import FamilyEnergyBar from '@/components/FamilyEnergyBar.vue'`
- [ ] **Step 2: 增加维度商品/活动区块**
在「最近能量变动」下方增加:
```html
```
添加对应 import,data 中增加 `dimensionActivities: [], dimensionProducts: []`,loadData 中调用对应加载方法。
- [ ] **Step 3: reLaunch 改 switchTab(第 432 行)**
修改前:
```javascript
uni.reLaunch({ url: '/pages/index/parent-index' })
```
修改后:
```javascript
uni.switchTab({ url: '/pages/index/index' })
```
---
### Task 5: teacher/index.vue — 软废弃
**Files:**
- Modify: `cfc-frontend/pages/teacher/index.vue`
- [ ] **Step 1: 文件首部添加废弃标记**
在 `` 上方添加注释:
```html
```
- [ ] **Step 2: 模板替换为提示页**
将整个 `` 内容替换为简洁提示:
```html
📋
服务商功能已整合
服务商工具和待办已迁移至家长首页
回到首页
```
保留 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 行):
```html
```
修改后:
```html
```
(任务仍需要登录,因为关联孩子;活动和商品游客可浏览)
在 `onShow` 中,游客也需要加载活动和商品数据:
```javascript
this.loadDimensionActivities()
this.loadDimensionProducts()
```
- [ ] **Step 2: 修复 BASE_URL 硬编码(第 150 行)**
修改前:
```javascript
const BASE_URL = 'http://localhost:8080'
```
修改后:
```javascript
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 行)**
修改前:
```html
```
`dualDimension` 当前只传了 mind 维度的数据。需要改为包含 mind + wisdom 两个维度的堆叠数据对象。
在 `loadEnergyData` 方法中修改(第 662-680 行):
```javascript
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 }` 时,能量柱渲染为上下堆叠:
```html
心 {{ dualDimension.primary.score }}
智 {{ dualDimension.secondary.score }}
```
computed 增加:
```javascript
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 行):
```html
```
修改后:
```html
```
---
### Task 8: action/index.vue — 游客可见商品/活动 + 修复商城路径
**Files:**
- Modify: `cfc-frontend/pages/action/index.vue`
- [ ] **Step 1: 游客可见维度商品/活动**
修改前(第 37-56 行):
```html
```
修改后:
```html
```
- [ ] **Step 2: 修复 funcList 商城路径(第 141 行)**
修改前:
```javascript
{ icon: '\u{1F6CD}', label: '商城', needLogin: false, page: '/pages/shop/index' }
```
修改后:
```javascript
{ 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**
```javascript
props: {
// ... 现有 props
isLoggedIn: { type: Boolean, default: false }
}
```
- [ ] **Step 2: 价格区域根据 isLoggedIn 切换显示**
在价格渲染位置:
```html
¥{{ item.price }}
会员¥{{ item.memberPrice }}
登录查看
```
在 body/mind/action 页使用时传入 `:isLoggedIn="isLoggedIn"`。
---
### Task 10: DimensionActivities 组件 — 支持游客空占位
**Files:**
- Modify: `cfc-frontend/components/DimensionActivities.vue`
- [ ] **Step 1: 增加 isLoggedIn prop**
```javascript
props: {
// ... 现有 props
isLoggedIn: { type: Boolean, default: false }
}
```
- [ ] **Step 2: 空数据时区分游客和登录态**
在空数据展示处:
```html
{{ isLoggedIn ? '暂无相关活动' : '登录后查看更多活动' }}
```
在 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:
```java
// 在返回前遍历商品列表
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 实体增加可见性字段**
```java
/** 可见性: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` 的列表查询中:
```java
// 游客:只返回 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: 后端编译**
```bash
cd cfc-backend && mvn clean compile -q
```
预期输出:`BUILD SUCCESS`
- [ ] **Step 3: 功能验收**
| 验收项 | 预期结果 |
|--------|---------|
| TabBar 第5个 Tab 显示"富沛" | 图标和文字正确 |
| 未登录首页商品列表显示 | 显示商品封面和名称,无价格,显示"登录查看" |
| 未登录点击商品 | 弹登录提示 |
| 游客进入身体/心智/行动页 | 看到活动/商品列表,无价格 |
| 家长首页无沙盘 | 五行沙盘不渲染 |
| 家长首页有服务商入口 | teacher 用户看到服务商工具卡片 |
| 心智页能量柱为堆叠图 | 一根柱上下显示心+智 |
| 教师首页废弃 | 直接跳转到 `/pages/teacher/index` 显示废弃提示 |