|
|
@@ -0,0 +1,717 @@
|
|
|
+# 代码 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
|
|
|
+<teacher-index v-else-if="currentRole === 'teacher'" />
|
|
|
+```
|
|
|
+
|
|
|
+修改后:
|
|
|
+```html
|
|
|
+<!-- teacher-index 已废弃,教师功能折入 parent-index -->
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 3: 未登录态商品列表改用 /api/product/list**
|
|
|
+
|
|
|
+修改前(第 200 行):
|
|
|
+```javascript
|
|
|
+productList(params).then(res => { ... })
|
|
|
+```
|
|
|
+
|
|
|
+修改后:
|
|
|
+```javascript
|
|
|
+// 调用 getProductsByDomain 或其他 api.js 中已封装的 /api/product/list
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 4: 未登录态商品隐藏价格(第 44-48 行)**
|
|
|
+
|
|
|
+修改前:
|
|
|
+```html
|
|
|
+<view class="product-price-row">
|
|
|
+ <text class="product-price">¥{{ item.price }}</text>
|
|
|
+ <text v-if="item.memberPrice" class="product-member">会员¥{{ item.memberPrice }}</text>
|
|
|
+</view>
|
|
|
+```
|
|
|
+
|
|
|
+修改后:
|
|
|
+```html
|
|
|
+<view class="product-price-row">
|
|
|
+ <!-- 游客不显示价格 -->
|
|
|
+ <text class="product-price-login">登录查看</text>
|
|
|
+</view>
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **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
|
|
|
+<!-- 五行能量沙盘(浠艾福 — 全家庭版) -->
|
|
|
+<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 下方增加:
|
|
|
+```html
|
|
|
+<!-- 家庭能量横条 -->
|
|
|
+<FamilyEnergyBar
|
|
|
+ v-if="hasFamilyData"
|
|
|
+ dimensionCode="parent"
|
|
|
+ :sandboxData="sandboxData" />
|
|
|
+```
|
|
|
+
|
|
|
+添加对应 import:
|
|
|
+```javascript
|
|
|
+import FamilyEnergyBar from '@/components/FamilyEnergyBar.vue'
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 3: 增加服务商快捷入口(替代废弃的 teacher-index)**
|
|
|
+
|
|
|
+在「待处理事项」区域增加服务商快捷卡片,当用户角色为 `teacher` 或 `role` 包含 `teacher` 时显示:
|
|
|
+
|
|
|
+```html
|
|
|
+<!-- ===== 服务商快捷入口(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: 增加维度商品/活动推荐区块**
|
|
|
+
|
|
|
+在「成长建议」后面增加:
|
|
|
+```html
|
|
|
+<!-- ===== 推荐活动 ===== -->
|
|
|
+<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**
|
|
|
+
|
|
|
+```html
|
|
|
+<!-- 家庭能量横条 -->
|
|
|
+<FamilyEnergyBar
|
|
|
+ v-if="hasChildren"
|
|
|
+ dimensionCode="child"
|
|
|
+ :sandboxData="sandboxData" />
|
|
|
+```
|
|
|
+
|
|
|
+添加 import:`import FamilyEnergyBar from '@/components/FamilyEnergyBar.vue'`
|
|
|
+
|
|
|
+- [ ] **Step 2: 增加维度商品/活动区块**
|
|
|
+
|
|
|
+在「最近能量变动」下方增加:
|
|
|
+```html
|
|
|
+<!-- ===== 推荐活动 ===== -->
|
|
|
+<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 行)**
|
|
|
+
|
|
|
+修改前:
|
|
|
+```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: 文件首部添加废弃标记**
|
|
|
+
|
|
|
+在 `<template>` 上方添加注释:
|
|
|
+```html
|
|
|
+<!--
|
|
|
+ ⚠️ 废弃说明(2026-06-15):
|
|
|
+ 此页面已按照设计决策废弃。教师首页功能已折入 parent-index。
|
|
|
+ 保留文件仅用于参考,不通过任何路由访问。
|
|
|
+-->
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 2: 模板替换为提示页**
|
|
|
+
|
|
|
+将整个 `<template>` 内容替换为简洁提示:
|
|
|
+```html
|
|
|
+<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 行):
|
|
|
+```html
|
|
|
+<DimensionActivities
|
|
|
+ v-if="isLoggedIn"
|
|
|
+ ...
|
|
|
+/>
|
|
|
+<DimensionProducts
|
|
|
+ v-if="isLoggedIn"
|
|
|
+ ...
|
|
|
+/>
|
|
|
+<DimensionTasks
|
|
|
+ v-if="isLoggedIn"
|
|
|
+ ...
|
|
|
+/>
|
|
|
+```
|
|
|
+
|
|
|
+修改后:
|
|
|
+```html
|
|
|
+<DimensionActivities
|
|
|
+ ...
|
|
|
+/>
|
|
|
+<DimensionProducts
|
|
|
+ ...
|
|
|
+/>
|
|
|
+<DimensionTasks
|
|
|
+ v-if="isLoggedIn"
|
|
|
+ ...
|
|
|
+/>
|
|
|
+```
|
|
|
+
|
|
|
+(任务仍需要登录,因为关联孩子;活动和商品游客可浏览)
|
|
|
+
|
|
|
+在 `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
|
|
|
+<FamilyEnergyBar
|
|
|
+ v-if="isLoggedIn"
|
|
|
+ dimensionCode="mind"
|
|
|
+ :sandboxData="sandboxData"
|
|
|
+ :dualDimension="dualDimension" />
|
|
|
+```
|
|
|
+
|
|
|
+`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
|
|
|
+<!-- 堆叠柱渲染 -->
|
|
|
+<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 增加:
|
|
|
+```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
|
|
|
+<DimensionTasks v-if="isLoggedIn" ... />
|
|
|
+<DimensionActivities v-if="isLoggedIn" ... />
|
|
|
+<DimensionProducts v-if="isLoggedIn" ... />
|
|
|
+```
|
|
|
+
|
|
|
+修改后:
|
|
|
+```html
|
|
|
+<DimensionTasks v-if="isLoggedIn" ... />
|
|
|
+<DimensionActivities ... />
|
|
|
+<DimensionProducts ... />
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Task 8: action/index.vue — 游客可见商品/活动 + 修复商城路径
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Modify: `cfc-frontend/pages/action/index.vue`
|
|
|
+
|
|
|
+- [ ] **Step 1: 游客可见维度商品/活动**
|
|
|
+
|
|
|
+修改前(第 37-56 行):
|
|
|
+```html
|
|
|
+<DimensionTasks v-if="isLoggedIn" ... />
|
|
|
+<DimensionActivities v-if="isLoggedIn" ... />
|
|
|
+<DimensionProducts v-if="isLoggedIn" ... />
|
|
|
+```
|
|
|
+
|
|
|
+修改后:
|
|
|
+```html
|
|
|
+<DimensionTasks v-if="isLoggedIn" ... />
|
|
|
+<DimensionActivities ... />
|
|
|
+<DimensionProducts ... />
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **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
|
|
|
+<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**
|
|
|
+
|
|
|
+```javascript
|
|
|
+props: {
|
|
|
+ // ... 现有 props
|
|
|
+ isLoggedIn: { type: Boolean, default: false }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 2: 空数据时区分游客和登录态**
|
|
|
+
|
|
|
+在空数据展示处:
|
|
|
+```html
|
|
|
+<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:
|
|
|
+
|
|
|
+```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` 显示废弃提示 |
|