2026-06-13-five-dimension-pages-redesign.md 61 KB

五维 TabBar 页面统一改造设计

Designer: 系统分析员 (System Analyst) Status: 设计稿第一版
Scope: 小程序端 3 个 TabBar 页(身体/心智/行动)+ 相关子页面 + 后端补充接口


1. 总体设计原则

根据用户要求的 6 条设计原则:

# 原则 实现方式
1 三个页面都包含商品、活动、任务 每页底部固定「推荐商品」「热门活动」「今日任务」三个通用区块,按当前维度筛选
2 文章都在心智里 只有「心智」页展示文章列表及阅读统计;身体页和行动页不出现文章板块
3 身体包含各种体检数据 利用已有 health_reports + health_indicators 展示健康报告摘要、指标解读
4 心智包括 DAN 成长测评+认知6个维度 保留原有心 · 智双 Tab 结构,融合 DAN 结果展示 + 六维雷达图
5 每页显示当前登录用户信息+任务/活动快捷入口 在能量柱状图下方统一展示用户身份 + 三个快捷按钮
6 每页顶部横放柱状图:家庭总能量 + 各成员能量值,可点击跳转 利用 /api/energy/sandbox 数据渲染,成员条可点击跳转到对应维度详情

统一页面布局

┌───────────────────────────────────────────────────────────┐
│ PageBanner (dimension 主题)                                │
├───────────────────────────────────────────────────────────┤
│ ① 家庭能量柱状图(横放)                                   │
│   「全家」 ████████████████████ 85                        │
│   「妈妈」 ████████████████ 72      ← 可点击跳转           │
│   「爸爸」 ████████████████████ 80   ← 可点击跳转           │
│   「小宝」 ██████████████ 65        ← 可点击跳转           │
├───────────────────────────────────────────────────────────┤
│ ② 当前用户信息 + 快捷入口                                  │
│   👤 当前:妈妈(家长)                                     │
│   [📋 今日任务] [🔥 相关活动] [🛍️ 推荐商品]              │
├───────────────────────────────────────────────────────────┤
│ ③ 维度专属内容 (见各页详述)                                 │
├───────────────────────────────────────────────────────────┤
│ ④ 推荐商品(按维度筛选)       ← 通用区块,3页皆有         │
│   4个商品卡片,2×2 网格                                     │
├───────────────────────────────────────────────────────────┤
│ ⑤ 热门活动(按维度筛选)       ← 通用区块,3页皆有         │
│   横向滚动活动卡片                                           │
├───────────────────────────────────────────────────────────┤
│ ⑥ 功能入口 Grid (4个)        ← 通用区块,3页各自不同       │
│   body: 运动/饮食/作息/菌群                                  │
│   mind: 阅读/小游戏/测评报告/训练中心                         │
│   action: 任务/活动/商城/课程                                │
└───────────────────────────────────────────────────────────┘

2. 数据流

┌──────────────┐
│  Page onShow  │
└──────┬───────┘
       │
       ▼
┌──────────────────────────────────────┐
│  POST /api/energy/sandbox            │ ← 家庭能量全景
│  → bodyScore/mindScore/wisdomScore/  │
│    actionScore/wealthScore           │
│  → members[].{bodyScore,mindScore,   │
│    wisdomScore,actionScore,wealthScore}│
└──────────────────┬───────────────────┘
       │
       ├── 提取当前维度评分 → 渲染柱状图
       ├── 成员列表 → 每成员柱状图 + 跳转
       │
       ▼ 并行加载(根据页面不同)
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 健康报告数据  │ │ DAN测评数据  │ │ 任务数据     │
│ POST /api/   │ │ POST /api/   │ │ POST /api/   │
│ health/...   │ │ cognitive/.. │ │ tasks/...    │
└──────────────┘ └──────────────┘ └──────────────┘
       │
       ▼ 并行加载(3页通用)
┌──────────────┐ ┌──────────────┐
│ 商品(按维度)  │ │ 活动(按维度) │
│ POST /api/   │ │ POST /api/   │
│ product/list │ │ activity/list│
└──────────────┘ └──────────────┘

2.1 按维度筛选

商品用 energy_dimension_id 筛选(需给 Product 新增该字段,若尚无) 任务用 categorydimension 字段筛选 活动用新建的 dimension 字段筛选 文章用 relatedDimensions 字段筛选(已存在)


3. 身体页面 pages/body/index.vue

3.1 维度专属内容

┌─ ③ 维度专属内容 ──────────────────────────────────────┐
│  ┌─ 家庭健康概况(有数据时) ─────────────────────────┐  │
│  │  最新体检报告: 2026-05-15    综合评分: 82/100      │  │
│  │  最新菌群检测: 2026-04-20    菌群健康: 76/100      │  │
│  │  [查看完整报告 →]                                   │  │
│  └──────────────────────────────────────────────────┘  │
│                                                        │
│  ┌─ 无报告时 ────────────────────────────────────────┐  │
│  │  还没有健康检测报告                                  │  │
│  │  [上传体检报告]  [菌群检测]                          │  │
│  └──────────────────────────────────────────────────┘  │
│                                                        │
│  ┌─ 健康指标解读(有报告时) ─────────────────────────┐  │
│  │  营养状况: 🟢 正常  疾病风险: 🟢 低                 │  │
│  │  肠道屏障: 🟡 注意  神经递质: 🟢 正常              │  │
│  │  [查看全部指标 →]                                   │  │
│  └──────────────────────────────────────────────────┘  │
│                                                        │
│  ┌─ 今日身体数据 ────────────────────────────────────┐  │
│  │  🚶 6,582步    💧 1.2L    😴 8.5h                 │  │
│  │  (对接智能设备后自动同步)                           │  │
│  └──────────────────────────────────────────────────┘  │
│                                                        │
│  ┌─ 健康打卡 ────────────────────────────────────────┐  │
│  │  今日: 运动✔ 喝水✔ 早睡✔ 冥想□   +⚡15能量        │  │
│  │  [去打卡 →]                                         │  │
│  └──────────────────────────────────────────────────┘  │
└──────────────────────────────────────────────────────────┘

3.2 功能入口

入口 图标 跳转
运动 🏃 pages/body/sports (已有或新增)
饮食 🍎 pages/body/diet
作息 😴 pages/body/sleep
菌群 🔬 pages/body/gut-flora (已有或跳转报告)

3.3 调用接口

数据 接口 状态
家庭能量柱状图 POST /api/energy/sandbox → bodyScore, members[].bodyScore ✅ 已有
最新健康报告 POST /api/health/report/latest ✅ 已有
报告列表 POST /api/health/report/list ✅ 已有
指标明细 POST /api/health/indicator/list ✅ 已有
身体维度详情 POST /api/health/body/detail ✅ 已有
健康打卡 POST /api/checkin/today ✨ 新增
按维度商品 POST /api/product/list + 参数 dimensionCode ✅ 已有改造
按维度活动 POST /api/activity/list + 参数 dimensionCode ✨ 新增

4. 心智页面 pages/mind/index.vue

4.1 维度专属内容

保留原有的 心(心理)/ 智(认知)双 Tab 结构,合并为统一心智页面:

【心】心理 Tab (emotion):

┌─ 心理能量概况 ─────────────────────────────────────────┐
│  ❤ 心能量: 82  |  💪 韧性: 78  |  😊 情绪: 85        │
└────────────────────────────────────────────────────────┘
┌─ EMI 心理四维 ─────────────────────────────────────────┐
│  情绪商数   ████████████████░░ 82  优秀                │
│  心理韧性   ██████████████░░░░ 76  良好                │
│  压力应对   █████████████████░░ 80  良好                │
│  自我认知   ██████████████████░ 85  优秀                │
│  [查看完整报告 →]  综合 EQ 分: 81                       │
└────────────────────────────────────────────────────────┘
┌─ 无评估数据占位引导 ────────────────────────────────┐
│  「完成 EMI 心理测评后查看详细分析报告」              │
│  [预约测评]                                           │
└────────────────────────────────────────────────────────┘

【智】认知 Tab (cognitive):

┌─ 综合认知评分 ─────────────────────────────────────────┐
│  综合得分: 88  |  上次: 2026-05-20              │
│  [查看完整报告 →]  [预约测评]                            │
└────────────────────────────────────────────────────────┘
┌─ 认知六维雷达图 ───────────────────────────────────────┐
│  家庭成员切换: [小宝 78] [大宝 85] [自己 92] ←横向滚动  │
│  ┌────────────────────────────────────────────────┐     │
│  │             六维雷达图 (RadarChart)             │     │
│  │       感知                                     │     │
│  │    专注     记忆                                │     │
│  │  逻辑      空间                                 │     │
│  │        加工速度                                 │     │
│  └────────────────────────────────────────────────┘     │
│  (点击维度标签进入对应能力训练)                          │
└────────────────────────────────────────────────────────┘
┌─ 认知训练 ─────────────────────────────────────────────┐
│  [🎯 舒尔特方格]  [🔢 猜数字]  [🧩 数独]            │
└────────────────────────────────────────────────────────┘
┌─ 本月阅读统计 ─────────────────────────────────────────┐
│  已读: 12篇  /  阅读时长: 156分钟                       │
│  ████████████░░░░ 60%  [去阅读 →]                      │
└────────────────────────────────────────────────────────┘

4.2 文章(全在心理#2)

心智页 心理 Tab 底部认知 Tab 底部都放推荐文章:

┌─ 推荐阅读 ─────────────────────────────────────────────┐
│  ┌────────────────────────────────────────────────┐    │
│  │ [情绪管理]  如何帮助孩子管理情绪               │    │
│  │             掌握3个简单技巧...                 │    │
│  │             阅读 2.3k                           │    │
│  ├────────────────────────────────────────────────┤    │
│  │ [认知发展]  提升逻辑思维的5个日常游戏          │    │
│  │             在家就能玩的思维训练...             │    │
│  │             阅读 1.8k                           │    │
│  └────────────────────────────────────────────────┘    │
│  [更多文章 →]                                          │
└────────────────────────────────────────────────────────┘

文章根据 relatedDimensions 筛选相关维度:心理 Tab 显示 relatedDimensions 包含 mind 的文章;认知 Tab 显示包含 wisdom 的文章。

4.3 功能入口

入口 图标 跳转
阅读 📖 pages/mind/articles (已有)
小游戏 🎮 pages/games/list (已有)
测评报告 📊 心理报告/认知报告 (已有)
训练中心 🎯 pages/mind/training (已有)

4.4 能量柱状图特殊处理 — 堆叠图

心智 Tab 合并了「心」和「智」两个维度。横放柱状图采用堆叠图(Stacked Bar)模式:每个成员一根柱,叠加显示心能量 + 智能量:

心智能量总览:
┌──────────────────────────────────────────────────────┐
│  全家 ████████████████████████████████████░░ 82+88=170│
│       └── 心 82 ──┘└─── 智 88 ───┘                   │
│                                                       │
│  妈妈 ████████████████████████████░░░░ 72+82=154       │
│       └── 心 72 ──┘└─── 智 82 ───┘                   │
│                                                       │
│  爸爸 ██████████████████████████████████░░ 78+86=164   │
│       └── 心 78 ──┘└─── 智 86 ───┘                   │
│                                                       │
│  小宝 ████████████████████████░░░░░░░ 65+78=143        │
│       └── 心 65 ──┘└─── 智 78 ───┘                   │
│                                                       │
│  (柱长 = 心分数 + 智分数,上限200)                     │
│  点击心色段 → 心理报告    点击智色段 → 认知报告        │
└──────────────────────────────────────────────────────┘

堆叠图设计要点:

属性
柱总长 mindScore + wisdomScore(上限 200,即 100+100)
心色段 暖色(如 #FF6B35 橙),居左
智色段 冷色(如 #667eea 蓝紫),居右
刻度标签 显示 心分数+智分数=总分(如 72+82=154
点击心段 跳转到心理报告页
点击智段 跳转到认知报告页
无数据时 柱显示为浅灰色占位,点击引导预约测评

数据改造: FamilyEnergyBar 组件新增 stackedDimensions prop,心智页传入 ['mind', 'wisdom'],组件内部渲染两段堆叠而非单段。

4.5 调用接口

数据 接口 状态
家庭能量 /api/energy/sandbox → mindScore/wisdomScore + members[].mindScore/wisdomScore ✅ 已有
EMI 心理报告 POST /api/assessment/emi/latest ✅ 已有
DAN 认知结果 POST /api/assessment/latest ✅ 已有
六维家族数据 POST /api/cognitive/family ✅ 已有
文章列表(按维度) POST /api/article/list?dimension=body/mind/wisdom ✅ 已有改造
阅读统计 POST /api/reading/stats ✅ 已有
按维度商品 POST /api/product/list?dimension=心智 ✨ 新增改造
按维度活动 POST /api/activity/list?dimension=心智 ✨ 新增

5. 行动页面 pages/action/index.vue

5.1 维度专属内容

┌─ ③ 维度专属内容 ──────────────────────────────────────┐
│  ┌─ 今日任务 ────────────────────────────────────────┐  │
│  │  📋 完成晨跑 30 分钟          +20分  ⏰ 今日截止  │  │
│  │  📋 整理书桌                  +10分  ⏰ 今日截止  │  │
│  │  📋 阅读打卡 15 分钟          +15分  ⏰ 明日截止  │  │
│  │  [查看全部任务 →]                                  │  │
│  └──────────────────────────────────────────────────┘  │
│                                                        │
│  ┌─ 任务完成统计 ────────────────────────────────────┐  │
│  │  本周完成: 12/15  ████████████░░ 80%              │  │
│  │  本月连续打卡: 8天  🔥                            │  │
│  └──────────────────────────────────────────────────┘  │
│                                                        │
│  ┌─ 进行中的活动 ────────────────────────────────────┐  │
│  │  🔥 21天亲子运动打卡营                            │  │
│  │     📅 5/1 ~ 5/21  |  进度: 12/21天              │  │
│  │     ██████████████░░░░░░                           │  │
│  └──────────────────────────────────────────────────┘  │
└──────────────────────────────────────────────────────────┘

5.2 功能入口

入口 图标 跳转
任务 📋 pages/tasks/tasks (已有 TabBar)
活动 🔥 pages/discover/index?type=activity (已有)
商城 🛍️ pages/shop/index (已有)
课程 🎓 pages/discover/index?type=course (已有)

5.3 调用接口

数据 接口 状态
家庭能量 /api/energy/sandbox → actionScore + members[].actionScore ✅ 已有
今日任务(按维度) POST /api/tasks/list?dimension=action ✅ 已有改造
任务统计 POST /api/tasks/stats?childId=X ✅ 已有
按维度商品 POST /api/product/list?dimension=行动 ✨ 新增改造
按维度活动 POST /api/activity/list?dimension=行动 ✨ 新增

6. 新增/改造接口总览

6.1 后端新增接口

端点 方法 说明 优先级
POST /api/activity/list 新增 活动列表(支持按 dimension 筛选) P1
POST /api/activity/detail 新增 活动详情 P1
POST /api/checkin/today 新增 今日健康打卡状态 P1 (身体页)
POST /api/checkin/do 新增 执行打卡 P1
POST /api/checkin/list 新增 打卡记录 P2
POST /api/product/list 改造 增加 dimension 筛选参数 P1
POST /api/tasks/list 改造 增加 dimension 筛选参数 P1

6.2 前端新增/改造页面

页面 操作 说明
pages/body/index.vue 🔄 改造 统一布局 + 能量柱状图 + 健康数据 + 通用区块
pages/mind/index.vue 🔄 改造 统一布局 + 能量柱状图 + 双Tab + 通用区块
pages/action/index.vue 🔄 改造 统一布局 + 能量柱状图 + 今日任务 + 通用区块
pages/body/health-report.vue ✨ 新增 健康报告详情 (已有设计)
pages/body/checkin.vue ✨ 新增 健康打卡 (已有设计)
pages/body/member-body-detail.vue ✨ 新增 成员身体维度详情(柱状图点击跳转目标)
pages/mind/member-mind-detail.vue ✨ 新增 成员心/智维度详情
pages/action/member-action-detail.vue ✨ 新增 成员行动维度详情

7. 成员维度详情页(柱状图跳转目标)

当用户点击能量柱状图中某个成员的能量条时,跳转到对应成员的该维度详情页。

通用设计

每个成员维度详情页展示该成员在某维度下的详细信息:

┌─ 返回 ────────────────────────────────────────────────┐
│                                                        │
│  成员名称:小宝                                         │
│  身体能量值:65/100                                     │
│                                                        │
│  ┌─ 能量构成 ────────────────────────────────────────┐  │
│  │  获得来源:                                        │  │
│  │  完成任务  +120                                   │  │
│  │  健康打卡  +45                                    │  │
│  │  商品购买  -30                                    │  │
│  │  当前余额: 135                                    │  │
│  └──────────────────────────────────────────────────┘  │
│                                                        │
│  ┌─ 能量流水(近7天) ───────────────────────────────┐  │
│  │  06/07  完成晨跑            +10  ⚡               │  │
│  │  06/07  健康打卡            +5   ⚡               │  │
│  │  06/06  购买运动用品        -15  ⚡               │  │
│  │  [查看更多 →]                                     │  │
│  └──────────────────────────────────────────────────┘  │
│                                                        │
│  [维度专属内容]                                        │
│  body: 健康报告/指标/身体数据                           │
│  mind: EMI心理结果/DAN认知结果                          │
│  action: 任务/活动参与记录                              │
└────────────────────────────────────────────────────────┘

8. 数据模型补充

8.1 Activity(活动实体)— 新增

@TableName("activities")
public class Activity {
    private Long id;
    private String title;
    private String description;
    private String coverImage;
    private String dimensionCode;   // body/mind/wisdom/action/wealth
    private String activityType;    // offline/online/campaign
    private String status;          // draft/published/ended
    private Date startTime;
    private Date endTime;
    private String location;
    private Integer maxParticipants;
    private Integer currentParticipants;
    private BigDecimal price;
    private Long vendorId;          // 活动方
    private Date createdAt;
}

8.2 Product 增加 dimension 筛选

Product 实体已有 domain 字段(来源域)和 externalSource(外部来源)。可以复用现有字段或新增 energyDimensionId(与 energy_dimension 表关联)。推荐新增 energyDimensionId 字段以统一维度筛选逻辑。


9. 实现任务分解

Task 1: 统一布局组件

文件:

  • New: cfc-frontend/components/FamilyEnergyBar.vue — 家庭能量横放柱状图组件
  • New: cfc-frontend/components/UserQuickEntry.vue — 用户信息+快捷入口组件

说明:

  • 柱状图组件接收 dimensionCode + sandboxData(来自 /api/energy/sandbox
  • 自动筛选出该维度的全家评分+每位成员评分
  • 成员条 @click 事件跳转到对应成员详情页
  • 快捷入口组件显示当前用户身份+三个快速导航按钮

Task 2: 通用区块组件

文件:

  • New: cfc-frontend/components/DimensionProducts.vue — 按维度商品展示
  • New: cfc-frontend/components/DimensionActivities.vue — 按维度活动展示
  • New: cfc-frontend/components/DimensionTasks.vue — 按维度任务展示

说明:

  • 每个组件接收 dimensionCode 参数
  • 内部调用相应 API 获取数据并渲染
  • 商品显示 2×2 网格,活动显示横向滚动卡片,任务显示列表

Task 3: 改造身体页面

文件:

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

改造点:

  1. 顶部插入 <FamilyEnergyBar dimensionCode="body" />
  2. 插入 <UserQuickEntry />
  3. 维度专属内容:健康报告摘要 + 指标解读 + 今日数据 + 健康打卡
  4. 通用区块:<DimensionProducts> + <DimensionActivities>
  5. 功能入口 Grid:运动/饮食/作息/菌群

Task 4: 改造心智页面

文件:

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

改造点:

  1. 顶部插入 <FamilyEnergyBar dimensionCode="mind" dualDimension="wisdom" />(双维度)
  2. 插入 <UserQuickEntry />
  3. 维度专属内容:保留现有 心理Tab / 认知Tab 结构
  4. 心理Tab底部 + 认知Tab底部加文章推荐
  5. 通用区块:<DimensionProducts> + <DimensionActivities>
  6. 功能入口 Grid:阅读/小游戏/测评报告/训练中心

Task 5: 改造行动页面

文件:

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

改造点:

  1. 顶部插入 <FamilyEnergyBar dimensionCode="action" />
  2. 插入 <UserQuickEntry />
  3. 维度专属内容:今日任务列表 + 完成统计
  4. 通用区块:<DimensionProducts> + <DimensionActivities>
  5. 功能入口 Grid:任务/活动/商城/课程(已有)

Task 6: 成员维度详情页

文件:

  • Create: cfc-frontend/pages/body/member-body-detail.vue
  • Create: cfc-frontend/pages/mind/member-mind-detail.vue
  • Create: cfc-frontend/pages/action/member-action-detail.vue

说明:

  • 跳转参数 childId + dimensionCode
  • 展示该成员在该维度的能量值、能量构成、近期流水
  • Body 额外展示健康报告摘要
  • Mind 额外展示 EMI/DAN 结果
  • Action 额外展示任务完成统计

Task 7: 后端接口

文件:

  • Create: ActivityController.java — 活动 CRUD
  • Create: ActivityService.java
  • Create: entity/Activity.java
  • Create: mapper/ActivityMapper.java

说明:

  • Activities 表设计(见 8.1)
  • 商品接口增加维度筛选:改造 ProductController 增加 dimensionCode 参数
  • 任务接口增加维度筛选:改造 TaskController 增加 dimensionCode 参数
  • 打卡接口已有设计(参见身体页面设计方案)

10. 设计约束

10.1 TabBar 不变

TabBar: 首页 | 身体 | 心智 | 行动 | 我的

10.2 小程序限制

  • 禁用可选链 ?.(用 && 代替)
  • 禁用 CSS Grid(用 flexbox 代替)
  • Vue 2 Options API,不引入 Composition API

10.3 现有 LSP 错误

项目已有预存的 LSP entity getter 缺失错误(entity 未生成 getter),这些是已有问题,不影响本次设计的内容。


11. 视觉参考

能量横放柱状图 (FamilyEnergyBar)

┌──────────────────────────────────────────────────────────┐
│  🏃 身体能量  ⚡ 85                                     │
│                                                        │
│  全家  ████████████████████████████████░░ 85           │
│  妈妈  ██████████████████████░░░░░░░░░░ 72  ←点击跳转  │
│  爸爸  ██████████████████████████░░░░░░ 80  ←点击跳转  │
│  小宝  ████████████████████░░░░░░░░░░░░ 65  ←点击跳转  │
│                                                        │
│  颜色渐变: #10B981 → #34D399 (body)                    │
│           #8B5CF6 → #A78BFA (mind)                     │
│           #F97316 → #FB923C (action)                   │
└──────────────────────────────────────────────────────────┘

Click member bar → uni.navigateTo 带参数:

uni.navigateTo({
  url: '/pages/body/member-body-detail?childId=' + member.memberId
})

用户快捷入口 (UserQuickEntry)

┌──────────────────────────────────────────────────────────┐
│  👤 当前用户: 妈妈(家长)  切换孩子: [小宝 ▼]          │
│                                                        │
│  ┌──────────┐ ┌──────────┐ ┌──────────┐               │
│  │ 📋 今日  │ │ 🔥 相关  │ │ 🛍️ 推荐  │               │
│  │   任务   │ │   活动   │ │   商品   │               │
│  └──────────┘ └──────────┘ └──────────┘               │
└──────────────────────────────────────────────────────────┘

三个按钮分别滚动页面到对应区块,或跳转到对应 TabBar 页面。


12. 文件变更清单

操作 文件 说明
cfc-frontend/components/FamilyEnergyBar.vue 能量横放柱状图组件
cfc-frontend/components/UserQuickEntry.vue 用户信息+快捷入口组件
cfc-frontend/components/DimensionProducts.vue 按维度商品展示组件
cfc-frontend/components/DimensionActivities.vue 按维度活动展示组件
cfc-frontend/components/DimensionTasks.vue 按维度任务展示组件
🔄 cfc-frontend/pages/body/index.vue 身体页统一布局改造
🔄 cfc-frontend/pages/mind/index.vue 心智页统一布局改造
🔄 cfc-frontend/pages/action/index.vue 行动页统一布局改造
cfc-frontend/pages/body/member-body-detail.vue 身体成员详情页
cfc-frontend/pages/mind/member-mind-detail.vue 心智成员详情页
cfc-frontend/pages/action/member-action-detail.vue 行动成员详情页
cfc-backend/.../entity/Activity.java 活动实体
cfc-backend/.../mapper/ActivityMapper.java 活动 Mapper
cfc-backend/.../controller/ActivityController.java 活动 CRUD
cfc-backend/.../service/ActivityService.java 活动服务
🔄 cfc-backend/.../controller/ProductController.java 增加维度筛选参数
🔄 cfc-backend/.../controller/TaskController.java 增加维度筛选参数
🔄 cfc-frontend/pages.json 注册新增子页面
🔄 cfc-frontend/utils/api.js 新增 API 封装

13. 设计决策记录

决策 选项 选择
能量值使用 balance值(整数) vs 评分(0-100) 评分(0-100) — 标准化,跨成员可比
心智双维度展示 分两组并列 vs 堆叠图 堆叠图 — 一根柱同时显示心+智,节省空间且直观对比两者比例,点击对应色段可分别跳转报告
商品按维度筛选 新字段 vs 复用 domain 新增 energyDimensionId — 与 energy_dimension 表统一关联
Activity 实现 新表 vs 用 Product 扩展 新表 — Activity 有独立生命周期和业务逻辑
成员详情页数量 3个独立页 vs 1个通用页+参数 3个独立页 — 每个维度详情内容差异大,独立页面更清晰
登录前价格 隐藏 vs 显示"登录查看" 隐藏数值,显示"登录查看价格" — 不暴露定价信息
私密内容过滤 前端过滤 vs 后端过滤 后端统一过滤 — 后端根据 auth 状态和角色返回不同数据集
私密活动可见范围 public/member/private 三态 visibility + visibleScope + visibleTo 三字段 — 支持 family/role/user 三层级,扩展性好
游客无数据占位 直接隐藏 vs 显示"登录后查看更多" 显示「🔒 登录后查看更多」占位 — 提示用户有更多内容需登录解锁
游客点击商品/活动 弹登录 vs 可直接看详情页(无价格) 弹登录提示 — 私密活动不能暴露,详情页也依赖登录态

14. 登录前(游客)模式设计

14.1 设计原则

# 原则 说明
A 商品/活动/文章登录前可浏览 三个页面的通用区块在未登录时可见
B 商品和活动不显示价格 价格数值隐藏,替换为"登录查看"提示
C 私密活动从列表即过滤 未授权用户完全看不到私密活动,不显示无权限条目
D 文章按可见性过滤 复用 Article.visibility + Article.visibleTo 字段
E 后端统一过滤 后端根据 @RequestAttribute("userId") 是否存在 + 角色决定返回什么数据
F 个性化板块变为引导 原本登录后的能量柱状图、健康数据、测评报告、今日任务 → 替换为登录引导卡
G 无数据时显示占位提示 游客模式下某维度无公开活动/商品时,显示「登录后查看更多」占位,而非直接隐藏区块

14.2 权限字段补充

Activity 新增可见性字段

支持 3 种可见范围(scope),每种对应不同的 visibleTo 存储格式:

visibility 含义 visibleTo 格式 说明
public 所有人可见 游客和登录用户均可浏览
member 登录用户可见 任意登录用户可浏览
restricted 限定范围可见 见下方 scope 说明 按家庭/角色/具体人过滤

restricted 模式下还需 visibleScope 字段说明限定类型:

visibleScope 含义 visibleTo 示例 匹配逻辑
family 特定家庭 "42""[42,55]" 用户的 familyId 匹配
role 特定角色 "[\"parent\",\"teacher\"]" 用户的 role 字段匹配
user 特定用户 "[\"101\",\"102\"]" 用户的 userId 在可见列表中(JSON_CONTAINS)
// 在 Activity 实体中新增
/** 可见性: public / member / restricted */
private String visibility;

/** 限定模式: family / role / user — 仅 visibility=restricted 时有效 */
private String visibleScope;

/** 可见范围标识: family_id(s) / role(s) / user_id(s) — JSON 格式 */
private String visibleTo;

设计决策理由: 选用 visibility + visibleScope + visibleTo 三字段组合而非单一 visibility 枚举,是因为 scope 隔离了不同类型的可见判断逻辑(SQL 条件不同),且支持扩展新 scope 类型(如将来增加 grade 年级可见)而不需改枚举。

Article 已有字段(复用)

private String visibility;   // public / member / private
private String visibleTo;    // 可见的user_id列表(JSON数组) 或 role列表
private String status;       // draft / published / archived

Product 不新增可见性字段(沿用现有 status + memberEligible

private String status;          // approved / rejected / pending
private String memberEligible;  // 是否仅会员可见(yes/no)

14.3 游客模式统一布局

┌───────────────────────────────────────────────────────────┐
│ PageBanner (dimension 主题,与登录后一致)                   │
│  + 副标题: 登录后展示个性化数据                               │
├───────────────────────────────────────────────────────────┤
│ ┌─ 登录引导横条 ──────────────────────────────────────┐   │
│ │  🔒 登录后查看家庭能量和个性化数据                      │   │
│ │  [立即登录 / 注册]                                    │   │
│ └──────────────────────────────────────────────────────┘   │
├───────────────────────────────────────────────────────────┤
│ ③ 维度专属内容(游客版) 看下文各页详述                      │
├───────────────────────────────────────────────────────────┤
│ ④ 推荐商品(按维度筛选)                                   │
│   有公开商品 → 商品卡片: 封面 + 名称 + [登录查看价格]        │
│   无公开商品 → 「🔒 登录后查看更多推荐商品」占位              │
├───────────────────────────────────────────────────────────┤
│ ⑤ 热门活动(按维度筛选)                                   │
│   有公开活动 → 活动卡片: 封面 + 名称 + 日期 + 地点          │
│   无公开活动 → 「🔒 登录后查看更多相关活动」占位              │
│   私密活动直接不返回(后端过滤)                            │
├───────────────────────────────────────────────────────────┤
│ ⑥ 功能入口 Grid                                           │
│   保留图标,点击部分跳转需要登录时弹登录提示                │
└───────────────────────────────────────────────────────────┘

商品/活动空占位设计:

┌─────────────────────────────────────┐
│ 🔒 登录后查看更多推荐商品             │  ← 灰色小字,不可点击
│     或                                │
│ 🔒 登录后查看更多相关活动             │
└─────────────────────────────────────┘

占位条件:游客模式下,对应维度无任何 visibility=public 的数据。点击占位无事件(仅为提示,不跳转登录)。

14.4 身体页 — 游客模式

页面元素               | 游客(未登录)               | 登录后(已有设计)
----------------------|----------------------------|----------------------------
PageBanner            | 显示,tagline 不变          | 显示
家庭能量柱状图         | ❌ 隐藏                   | ✅ 显示
用户快捷入口           | ❌ 隐藏                   | ✅ 显示
健康报告/指标          | ❌ 隐藏                   | ✅ 显示
今日身体数据           | ❌ 隐藏                   | ✅ 显示
健康打卡               | ❌ 隐藏                   | ✅ 显示
推荐商品               | ✅ 显示(无价格)/ 无商品→占位 | ✅ 显示(有价格)
热门活动               | ✅ 显示(无价格)/ 无活动→占位 | ✅ 显示(有价格)
登录引导卡             | ✅ 显示                   | ❌ 隐藏
健康小贴士(游客版)   | ✅ 从文章库取 body 类文章   | ❌ 隐藏(已有替代区块)

商品/活动空占位: 身体页 body 维度下无公开商品时显示「🔒 登录后查看更多推荐商品」,无公开活动时显示「🔒 登录后查看更多相关活动」。

登录引导卡设计:

┌──────────────────────────────────────────────────┐
│  🔒 登录后可以                                     │
│  · 查看全家的身体能量数据和健康报告                    │
│  · 记录每日健康打卡,获取身体能量                     │
│  · 查看专属健康商品和活动价格                         │
│  [立即登录]  [注册账号]                              │
└──────────────────────────────────────────────────┘

健康小贴士(游客专属):

身体页在游客模式下展示从 Article 表筛选的 body 相关健康文章,作为「健康小贴士」板块:

┌─ 健康小贴士(从 articles 表取) ─────────────────┐
│  📋 儿童每日运动指南                             │
│  🍎 五行饮食搭配法则                             │
│  😴 优质睡眠的5个习惯                            │
│  [阅读更多 →]                                   │
└──────────────────────────────────────────────────┘

筛选条件:visibility='public' AND status='published' AND (relatedDimensions LIKE '%body%' OR categoryId IN (身体类目))

14.5 心智页 — 游客模式

页面元素               | 游客(未登录)               | 登录后(已有设计)
----------------------|----------------------------|----------------------------
PageBanner            | 显示                       | 显示
家庭能量柱状图         | ❌ 隐藏                   | ✅ 显示
用户快捷入口           | ❌ 隐藏                   | ✅ 显示
心理 Tab(EMI)       | ❌ 隐藏(无数据)          | ✅ 显示
认知 Tab(DAN+雷达图) | ❌ 隐藏(无数据)          | ✅ 显示
认知训练(小游戏)     | ❌ 隐藏(需登录)          | ✅ 显示
阅读统计              | ❌ 隐藏                   | ✅ 显示
推荐阅读(文章)       | ✅ 公开文章               | ✅ 全部可见文章
推荐商品              | ✅ 显示(无价格)/ 无→占位 | ✅ 显示(有价格)
热门活动              | ✅ 显示(无价格)/ 无→占位 | ✅ 显示(有价格)
登录引导卡            | ✅ 显示(专属文案)         | ❌ 隐藏

心智页游客布局(双Tab依然保留,但内容不同):

心理 Tab(游客)

┌─ 登录引导卡 ───────────────────────────────────────┐
│  🔒 登录后查看 EMI 心理测评报告                      │
│  · 情绪商数、心理韧性、压力应对、自我认知四维分析       │
│  [立即登录]                                         │
└────────────────────────────────────────────────────┘
┌─ 推荐阅读(心理维度) ──────────────────────────────┐
│  从文章库筛选 relatedDimensions 含 mind 的公开文章   │
└────────────────────────────────────────────────────┘

认知 Tab(游客)

┌─ 登录引导卡 ───────────────────────────────────────┐
│  🔒 登录后查看 DAN 认知评估报告                      │
│  · 六维认知雷达图:感知/专注/记忆/逻辑/空间/加工速度   │
│  [立即登录]                                         │
└────────────────────────────────────────────────────┘
┌─ 推荐阅读(认知维度) ──────────────────────────────┐
│  从文章库筛选 relatedDimensions 含 wisdom 的公开文章 │
└────────────────────────────────────────────────────┘

14.6 行动页 — 游客模式

页面元素               | 游客(未登录)               | 登录后(已有设计)
----------------------|----------------------------|----------------------------
PageBanner            | 显示                       | 显示
家庭能量柱状图         | ❌ 隐藏                   | ✅ 显示
用户快捷入口           | ❌ 隐藏                   | ✅ 显示
今日任务              | ❌ 隐藏(无数据)          | ✅ 显示
推荐商品              | ✅ 显示(无价格)/ 无→占位 | ✅ 显示(有价格)
热门活动              | ✅ 显示(无价格)/ 无→占位 | ✅ 显示(有价格)
登录引导卡            | ✅ 显示(专属文案)         | ❌ 隐藏
功能入口              | ✅ 显示,部分需弹登录       | ✅ 全部可用

行动页游客布局:

┌─ 登录引导卡 ───────────────────────────────────────┐
│  🔒 登录后查看任务和活动参与记录                      │
│  · 查看今日任务,完成打卡获取行动能量                  │
│  · 报名参加热门活动,记录成长足迹                     │
│  [立即登录]                                         │
└────────────────────────────────────────────────────┘
┌─ 热门活动(按 action 维度筛选) ──────────────────┐
│  横向滚动卡片,全部公开活动,不显示价格               │
└────────────────────────────────────────────────────┘
┌─ 推荐商品(按 action 维度筛选) ──────────────────┐
│  2×2 网格商品卡片,不显示价格                       │
└────────────────────────────────────────────────────┘

14.7 后端过滤逻辑

所有列表接口根据请求中是否携带有效 JWT Token,返回不同数据集:

请求到达
│
├─ 有 JWT Token(已登录)
│   ├─ userId 存在 → 正常返回全部可见数据
│   ├─ 商品: status=approved + memberEligible 检查
│   ├─ 活动: visiblity IN (public, member, private) + visibleTo 匹配
│   ├─ 文章: visibility IN (public, member, private) + visibleTo 匹配
│   └─ 价格: 正常返回 price 字段
│
└─ 无 JWT Token(游客)
    ├─ userId = null
    ├─ 商品: status=approved AND memberEligible!='yes'(非会员专属)
    ├─ 活动: visibility='public'
    ├─ 文章: visibility='public'
    └─ 价格: 返回 price=null 或 0(前端据此显示"登录查看")

商品列表接口改造

@PostMapping("/list")
public Result<Page<ProductDTO>> list(@RequestBody Map<String, Object> params,
                                     @RequestAttribute(value = "userId", required = false) Long userId) {
    // ... 现有参数解析 ...
    
    LambdaQueryWrapper<Product> query = new LambdaQueryWrapper<Product>()
            .eq(Product::getStatus, "approved");
    
    // 游客过滤:排除会员专属商品
    if (userId == null) {
        query.ne(Product::getMemberEligible, "yes");
    }
    
    // 维度筛选
    String dimensionCode = (String) params.get("dimensionCode");
    if (dimensionCode != null && !dimensionCode.isEmpty()) {
        query.eq(Product::getEnergyDimensionId, getDimensionId(dimensionCode));
    }
    
    // 查询并转换DTO(游客不返回价格)
    Page<Product> pageResult = productService.page(new Page<>(page, size), query);
    Page<ProductDTO> dtoPage = toDTOPage(pageResult, userId == null);
    return Result.success(dtoPage);
}

活动列表接口改造

后端根据 visibility / visibleScope / visibleTo 三字段组合判断可见性:

@PostMapping("/list")
public Result<Page<ActivityDTO>> list(@RequestBody Map<String, Object> params,
                                      @RequestAttribute(value = "userId", required = false) Long userId,
                                      @RequestAttribute(value = "familyId", required = false) Long familyId,
                                      @RequestAttribute(value = "role", required = false) String role) {
    LambdaQueryWrapper<Activity> query = new LambdaQueryWrapper<Activity>()
            .eq(Activity::getStatus, "published");
    
    if (userId == null) {
        // 游客:只看公开活动
        query.eq(Activity::getVisibility, "public");
    } else {
        // 登录用户:public + member + restricted 中自己符合条件的
        query.and(w -> {
            w.eq(Activity::getVisibility, "public")
             .or().eq(Activity::getVisibility, "member");
            
            // restricted 模式:按 scope 类型分别匹配
            if (familyId != null) {
                // family 范围 → 匹配 familyId
                w.or().and(w2 -> w2.eq(Activity::getVisibility, "restricted")
                    .eq(Activity::getVisibleScope, "family")
                    .apply("JSON_CONTAINS(visible_to, {0})", "\"" + familyId + "\""));
            }
            
            // role 范围 → 匹配用户 role
            if (role != null) {
                w.or().and(w2 -> w2.eq(Activity::getVisibility, "restricted")
                    .eq(Activity::getVisibleScope, "role")
                    .apply("JSON_CONTAINS(visible_to, {0})", "\"" + role + "\""));
            }
            
            // user 范围 → 匹配具体 userId
            w.or().and(w2 -> w2.eq(Activity::getVisibility, "restricted")
                .eq(Activity::getVisibleScope, "user")
                .apply("JSON_CONTAINS(visible_to, {0})", "\"" + userId + "\""));
        });
    }
    
    String dimensionCode = (String) params.get("dimensionCode");
    if (dimensionCode != null && !dimensionCode.isEmpty()) {
        query.eq(Activity::getDimensionCode, dimensionCode);
    }
    
    Page<Activity> pageResult = activityService.page(new Page<>(page, size), query);
    Page<ActivityDTO> dtoPage = toDTOPage(pageResult, userId == null);
    return Result.success(dtoPage);
}

familyId 非必须(用户可能无家庭),用 null 安全防护避免 SQL 异常。

文章列表接口改造

@PostMapping("/list")
public Result<Page<ArticleDTO>> list(@RequestBody Map<String, Object> params,
                                     @RequestAttribute(value = "userId", required = false) Long userId) {
    LambdaQueryWrapper<Article> query = new LambdaQueryWrapper<Article>()
            .eq(Article::getStatus, "published");
    
    if (userId == null) {
        query.eq(Article::getVisibility, "public");
    } else {
        // 登录用户:public + member + 指定给自己的 private
        query.and(w -> w.eq(Article::getVisibility, "public")
            .or().eq(Article::getVisibility, "member")
            .or().and(w2 -> w2.eq(Article::getVisibility, "private")
                .apply("JSON_CONTAINS(visible_to, {0})", "\"" + userId + "\"")));
    }
    
    String dimension = (String) params.get("dimension");
    if (dimension != null && !dimension.isEmpty()) {
        query.like(Article::getRelatedDimensions, dimension);
    }
    
    Page<Article> pageResult = articleService.page(new Page<>(page, size), query);
    return Result.success(toDTOPage(pageResult, userId == null));
}

ProductDTO(游客版不返回价格)

@Data
public class ProductDTO {
    private Long id;
    private String name;
    private String description;
    private String coverImage;
    private BigDecimal price;         // 游客 = null
    private String priceLabel;        // 游客 = "登录查看价格",登录后 = "¥XXX"
    private String vendorName;
    private String productType;
    // 其他非敏感字段
}

ActivityDTO(游客版不返回价格)

@Data
public class ActivityDTO {
    private Long id;
    private String title;
    private String coverImage;
    private String startTime;
    private String endTime;
    private String location;
    private BigDecimal price;         // 游客 = null
    private String priceLabel;        // 游客 = "登录查看",登录后 = "¥XXX"
    private Integer maxParticipants;
    private Integer currentParticipants;
    private String vendorName;
    // 其他非敏感字段
}

14.8 前端游客模式判断逻辑

所有页面统一使用 isLoggedIn 变量控制渲染分支:

// 每个页面的 onShow 中
onShow() {
    var token = uni.getStorageSync('token')
    this.isLoggedIn = !!token
    
    if (this.isLoggedIn) {
        this.loadPersonalData()   // 能量、健康、测评、任务等
    }
    // 无论是否登录,都加载公共数据
    this.loadPublicData()         // 商品、活动、文章
}

// 组件内部根据 props hidePrice 控制价格显示
// 后端在未登录时返回 price=null, priceLabel="登录查看价格"

商品区块渲染逻辑(含占位):

<template>
  <view class="section">
    <view class="section-header">
      <text class="section-title">🛍️ 推荐商品</text>
      <text class="section-more" @click="$emit('moreProducts')">更多 ›</text>
    </view>
    <!-- 有商品时:卡片列表 -->
    <view v-if="products.length > 0" class="product-list">
      <view class="product-card" v-for="prod in products" :key="prod.id" @click="onProductClick(prod)">
        <image class="product-img" :src="prod.coverImage || '/static/default-product.png'" />
        <text class="product-name">{{ prod.name }}</text>
        <text v-if="!isLoggedIn" class="product-price-label">登录查看价格</text>
        <text v-else class="product-price">¥{{ prod.price }}</text>
      </view>
    </view>
    <!-- 游客无商品时:占位提示 -->
    <view v-else-if="!isLoggedIn" class="empty-placeholder">
      <text class="placeholder-text">🔒 登录后查看更多推荐商品</text>
    </view>
  </view>
</template>

活动区块渲染逻辑(含占位):

<template>
  <view class="section">
    <view class="section-header">
      <text class="section-title">🔥 相关活动</text>
      <text class="section-more" @click="$emit('moreActivities')">更多 ›</text>
    </view>
    <!-- 有活动时:卡片列表 -->
    <view v-if="activities.length > 0" class="activity-list">
      <view class="activity-card" v-for="act in activities" :key="act.id" @click="onActivityClick(act)">
        <image class="activity-img" :src="act.coverImage || '/static/default-activity.png'" />
        <view class="activity-info">
          <text class="activity-name">{{ act.title }}</text>
          <text class="activity-meta">{{ act.startTime }} · {{ act.location }}</text>
          <text v-if="!isLoggedIn" class="activity-price-label">登录查看</text>
          <text v-else class="activity-price">¥{{ act.price }}</text>
        </view>
      </view>
    </view>
    <!-- 游客无活动时:占位提示 -->
    <view v-else-if="!isLoggedIn" class="empty-placeholder">
      <text class="placeholder-text">🔒 登录后查看更多相关活动</text>
    </view>
  </view>
</template>

14.9 文件变更补充

在原有文件变更清单基础上新增:

操作 文件 说明
🔄 cfc-backend/.../entity/Activity.java 补充 visibility + visibleTo 字段
🔄 cfc-backend/.../dto/ProductDTO.java 新增 priceLabel 字段
cfc-backend/.../dto/ActivityDTO.java 新增 priceLabel 字段
🔄 cfc-backend/.../dto/ArticleDTO.java 若尚无,新增 DTO 类
🔄 cfc-backend/.../controller/ProductController.java list 接口增加 @RequestAttribute("userId") 和游客逻辑
🔄 cfc-backend/.../controller/ActivityController.java list 接口增加 userId 参数和可见性过滤
🔄 cfc-backend/.../controller/ArticleController.java list 接口增加 userId 参数和可见性过滤
🔄 cfc-frontend/pages/body/index.vue 增加游客分支渲染
🔄 cfc-frontend/pages/mind/index.vue 增加游客分支渲染
🔄 cfc-frontend/pages/action/index.vue 增加游客分支渲染