ソースを参照

docs: trim homepage plan — 9 tasks done by remote c1ade35, keep only Task 1

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
liaoxg 4 ヶ月 前
親
コミット
0b0b94bcda
1 ファイル変更、12 行追加、560 行削除
  1. 12 560
      docs/superpowers/plans/2026-06-19-homepage-risk-fix.md

+ 12 - 560
docs/superpowers/plans/2026-06-19-homepage-risk-fix.md

@@ -2,26 +2,11 @@
 
 > **For agentic workers:** 使用 superpowers:subagent-driven-development 或 superpowers:executing-plans 来执行此计划。
 
-**目标:** 修复小程序首页(parent-index / child-index)的 10 个实现问题
+**目标:** 修复小程序首页(parent-index / child-index)的实现问题
 
-**问题总览:**
+**已由远程 `c1ade35` 修复(无需操作):** Task 2/3/4/5/6/7/8/9/10 ✅
 
-| # | 严重度 | 问题 | 文件 | 影响 |
-|---|---|---|---|---|
-| A1 | 🔴高 | FamilyEnergyBar 硬编码 3 个未注册页面路径,点击成员行→404 | `FamilyEnergyBar.vue:134-139` | 运行时导航失败 |
-| A2 | 🟡中 | DimensionActivities 组件自加载 API,父组件 prop 数据被忽略 | `DimensionActivities.vue:47-65` | 重复 API 调用,prop 冗余 |
-| A3 | 🟡中 | DimensionProducts 组件自加载 API,父组件 prop 数据被忽略 | `DimensionProducts.vue:39-53` | 同 A2 |
-| A4 | 🟡中 | `onDomainClick` 导航 URL 缺少 `childId` 参数 | `child-index.vue:580-585` | 能量详情页无法加载孩子数据 |
-| A5 | 🟡中 | 成就徽章 3/4/5 无解锁触发逻辑 | `child-index.vue:499-504` | 运动/艺术/家务徽章永不解锁 |
-| A6 | 🟡中 | 模板残留孤立标签 `@moreProducts` | `parent-index.vue:229` | 微信 XML 解析警告,事件绑定丢失 |
-| A7 | 🟢低 | preview 模式每次 mount 重算随机值,视觉不稳定 | `wuxing-sandbox.vue:304-310` | 沙盘数值闪烁 |
-| A8 | 🟢低 | FamilyMemberCard `roleLabel` 依赖后端,可能未填充 | `FamilyMemberCard.vue:9` | 角色标签空白 |
-| A9 | 🟢低 | 降级路径 `!c.role` 条件混入非孩子角色 | `parent-index.vue:432-433` | 成员过滤不精确 |
-| A10 | 🟢低 | FamilyMemberStrip scroll-view 无固定高度 | `FamilyMemberStrip.vue:109-112` | 小屏设备布局风险 |
-
-**优先级排序:** A1 → A6 → A4 → A5 → A2 → A3 → A7 → A8 → A9 → A10
-
-**架构:** 10 个任务,每个只改一个文件。Task 1-6 为高/中优先级,Task 7-10 为低优先级可并行处理。
+**待处理(仅 Task 1):** 🔴
 
 ---
 
@@ -52,8 +37,6 @@ var detailPages = {
 
 **选项 B(保守):** 将 `onMemberClick` 方法中的跳转改为 `uni.showToast({ title: '功能即将上线', icon: 'none' })`,阻止 404。
 
-**选项 C:** 若这些页面存在但未注册,补全 `pages.json` 注册条目。
-
 - [ ] **Step 1: 确认目标页面文件是否存在于 `pages/` 目录**
 
 ```bash
@@ -104,531 +87,17 @@ grep -n "member-body-detail\|member-mind-detail\|member-action-detail" /root/cfc
 
 ---
 
-## Task 2: 修复 parent-index.vue 模板孤立标签(A6)
-
-**优先级:** 🟡 中
-
-**文件:** `cfc-frontend/pages/index/parent-index.vue`
-
-- [ ] **Step 1: 确认问题行**
-
-文件 L221–229 区域当前状态:
-
-```vue
-221:       <!-- ===== 推荐商品 ===== -->
-222:       <DimensionProducts
-223:         dimensionCode="all"
-224:         :products="recommendedProducts"
-225:         @productClick="goProductDetail" />   ← 自闭合,@moreProducts 丢失
-226:       <view class="error-state" v-if="errorProducts">
-227:         <text class="error-state-text">商品推荐加载失败</text>
-228:       </view>
-229:         @moreProducts="goMoreProducts" />    ← 孤立残留行,必须删除
-```
-
-- [ ] **Step 2: 将 `@moreProducts` 合并到 DimensionProducts 组件上,删除孤立行**
-
-将 L222–225 改为:
-
-```vue
-222:       <DimensionProducts
-223:         dimensionCode="all"
-224:         :products="recommendedProducts"
-225:         @productClick="goProductDetail"
-226:         @moreProducts="goMoreProducts" />
-```
-
-删除 L229(`@moreProducts="goMoreProducts" />`)。
-
-- [ ] **Step 3: 验证 `goMoreProducts` 方法存在**
-
-L687-688:
-
-```javascript
-goMoreProducts() {
-  uni.switchTab({ url: '/pages/discover/index' })
-},
-```
-
-已存在,无需修改。
-
-- [ ] **Step 4: lint 检查**
-
-```bash
-cd /root/cfc/cfc-frontend && npx eslint pages/index/parent-index.vue --no-error-on-unmatched-pattern 2>&1 | head -20
-```
-
----
-
-## Task 3: 修复 child-index.vue onDomainClick 缺少 childId(A4)
-
-**优先级:** 🟡 中
-
-**文件:** `cfc-frontend/pages/index/child-index.vue`
-
-- [ ] **Step 1: 确认当前错误实现**
-
-L580–585:
-
-```javascript
-onDomainClick(evt) {
-  var code = evt && evt.code
-  if (!code) return
-  uni.navigateTo({
-    url: '/pages/energy/detail?code=' + code   // ❌ 缺少 childId
-  })
-},
-```
-
-- [ ] **Step 2: 修复 onDomainClick,追加 childId**
-
-替换 L580–585 为:
-
-```javascript
-onDomainClick(evt) {
-  var code = evt && evt.code
-  if (!code || !this.childId) return
-  uni.navigateTo({
-    url: '/pages/energy/detail?childId=' + this.childId + '&code=' + code
-  })
-},
-```
-
-- [ ] **Step 3: 验证与 parent-index 逻辑一致**
-
-parent-index.vue L656–665 的 `goToDomain` 方法:
-
-```javascript
-goToDomain(code) {
-  var childId = this.currentChildId
-  if (!childId || !code) return
-  uni.navigateTo({
-    url: '/pages/energy/detail?childId=' + childId + '&code=' + code
-  })
-},
-```
-
-child-index 修复后行为与 parent-index 一致。
-
-- [ ] **Step 4: lint 检查**
-
-```bash
-cd /root/cfc/cfc-frontend && npx eslint pages/index/child-index.vue --no-error-on-unmatched-pattern 2>&1 | head -20
-```
-
----
-
-## Task 4: 补充成就徽章解锁触发逻辑(A5)
-
-**优先级:** 🟡 中
-
-**文件:** `cfc-frontend/pages/index/child-index.vue`
-
-### 徽章 → 解锁条件
-
-| 徽章 ID | 名称 | 解锁条件 |
-|---|---|---|
-| 1 | 🌟 早起鸟 | `streakDays >= 7` ✅ 已有 |
-| 2 | 📚 阅读小达人 | `totalPoints >= 1000` ✅ 已有 |
-| 3 | 🏃 运动健将 | 完成 `运动类` 任务 ≥ 5 次 |
-| 4 | 🎨 小艺术家 | 完成 `艺术类` 任务 ≥ 5 次 |
-| 5 | 🧹 家务小能手 | 完成 `家务类` 任务 ≥ 5 次 |
-
-- [ ] **Step 1: 确认任务数据位置**
-
-L492–496:
-
-```javascript
-const tasksRes = await getTodayTasks(this.childId)
-const rawTasks = tasksRes.data || []
-this.todayTasks = rawTasks
-this.completedCount = rawTasks.filter(function(t) { return t.status === 'completed' }).length
-this.totalTaskCount = rawTasks.length
-```
-
-- [ ] **Step 2: 替换 L498–504 解锁逻辑区域**
-
-当前代码(L498–504):
-
-```javascript
-// 解锁成就
-if (this.streakDays >= 7) {
-  this.unlockBadge(1)
-}
-if (this.totalPoints >= 1000) {
-  this.unlockBadge(2)
-}
-```
-
-替换为:
-
-```javascript
-// 解锁成就
-if (this.streakDays >= 7) {
-  this.unlockBadge(1)
-}
-if (this.totalPoints >= 1000) {
-  this.unlockBadge(2)
-}
-
-// 按类别统计已完成任务次数,解锁对应徽章
-var categoryCompletedCount = { '运动类': 0, '艺术类': 0, '家务类': 0 }
-for (var i = 0; i < rawTasks.length; i++) {
-  var task = rawTasks[i]
-  if (task.status === 'completed') {
-    var cat = task.category || '其他'
-    if (cat === '运动类') {
-      categoryCompletedCount['运动类']++
-    } else if (cat === '艺术类') {
-      categoryCompletedCount['艺术类']++
-    } else if (cat === '家务类') {
-      categoryCompletedCount['家务类']++
-    }
-  }
-}
-if (categoryCompletedCount['运动类'] >= 5) {
-  this.unlockBadge(3)
-}
-if (categoryCompletedCount['艺术类'] >= 5) {
-  this.unlockBadge(4)
-}
-if (categoryCompletedCount['家务类'] >= 5) {
-  this.unlockBadge(5)
-}
-```
-
-> **注意**:`task.status === 'completed'` — 与 L495 的过滤条件保持一致,不使用 `|| task.status === 'done'`。
-
-- [ ] **Step 3: lint 检查**
-
-```bash
-cd /root/cfc/cfc-frontend && npx eslint pages/index/child-index.vue --no-error-on-unmatched-pattern 2>&1 | head -20
-```
-
----
-
-## Task 5: 修复 DimensionActivities 重复 API 调用(A2)
-
-**优先级:** 🟡 中(架构问题)
-
-**文件:** `cfc-frontend/components/DimensionActivities.vue`
-
-### 问题分析
-
-**双重调用链路:**
-
-```
-parent-index / child-index:
-  loadData() → getActivityList() → dimensionActivities → :activities prop
-                              ↑
-               ↑(第一次调用,prop 数据被忽略)
-
-DimensionActivities 组件:
-  created() → getActivityList() → activitiesData → displayActivities
-                              ↑
-               ↑(第二次调用,实际生效)
-```
-
-`displayActivities` 优先级:`activitiesData.length > 0 ? activitiesData : activities`,组件自加载的 `activitiesData` 永远有值,父组件 prop 永远被忽略。
-
-### 修复方案
-
-**选项 A(推荐):** 移除组件 `created()` 中的自加载逻辑,改为完全依赖 prop 数据(消除重复调用)。
-
-**选项 B:** 父组件不调用 API,直接传空数组,组件自加载生效(保持现状但承认架构)。
-
-- [ ] **Step 1: 确认 DimensionActivities 的调用方传入了什么**
-
-parent-index L212-216:
-
-```vue
-<DimensionActivities
-  dimensionCode="all"
-  :activities="recommendedActivities"
-  @activityClick="goActivityDetail"
-  @moreActivities="goMoreActivities" />
-```
-
-child-index L269-274:
-
-```vue
-<DimensionActivities
-  dimensionCode="child"
-  :activities="dimensionActivities"
-  @activityClick="goActivityDetail"
-  @moreActivities="goMoreProducts" />
-```
-
-**两个调用方都已经在 loadData 中调用了 `getActivityList` 并通过 prop 传入**,组件自加载是冗余的。
-
-- [ ] **Step 2: 移除组件 `created()` 自加载逻辑**
-
-删除 `DimensionActivities.vue` L47-65 的 `created()` 钩子和 `loadActivities` 方法:
-
-删除:
-
-```javascript
-created: function() {
-  this.loadActivities()
-},
-methods: {
-  loadActivities: function() {
-    var self = this
-    import('@/utils/api.js').then(function(api) {
-      api.getActivityList({ dimensionCode: self.dimensionCode, page: 1, size: 6 }).then(function(res) {
-        var records = (res.data && res.data.records) ? res.data.records : []
-        self.activitiesData = records
-      }).catch(function() {
-        // silently fail
-      })
-    })
-  }
-}
-```
-
-同时删除 `data()` 中的 `activitiesData: []`(L38-39),因为不再需要内部状态。
-
-`displayActivities` computed 简化为:
-
-```javascript
-displayActivities: function() {
-  return this.activities
-}
-```
-
-- [ ] **Step 3: 验证修改后 dimensionCode prop 仍传递给父组件(事件链路不变)**
-
-`dimensionCode` 仅用于传给父组件的事件参数,不需要在组件内部使用。
-
-- [ ] **Step 4: lint 检查**
-
-```bash
-cd /root/cfc/cfc-frontend && npx eslint components/DimensionActivities.vue --no-error-on-unmatched-pattern 2>&1 | head -20
-```
-
----
-
-## Task 6: 修复 DimensionProducts 重复 API 调用(A3)
-
-**优先级:** 🟡 中(架构问题)
-
-**文件:** `cfc-frontend/components/DimensionProducts.vue`
-
-### 问题分析
-
-与 Task 5 完全相同:
-- 父组件在 `loadData` 中调用 `getProductsByDomain` 并通过 prop 传入
-- 组件 `created()` 自加载,`activitiesData` 优先级覆盖 prop
-- 造成重复 API 调用
-
-- [ ] **Step 1: 确认调用方传入数据**
-
-parent-index L222-225:
-
-```vue
-<DimensionProducts
-  dimensionCode="all"
-  :products="recommendedProducts"
-  @productClick="goProductDetail"
-  @moreProducts="goMoreProducts" />
-```
-
-child-index L277-282:
-
-```vue
-<DimensionProducts
-  dimensionCode="child"
-  :products="dimensionProducts"
-  @productClick="goProductDetail"
-  @moreProducts="goMoreProducts" />
-```
-
-- [ ] **Step 2: 移除组件自加载(删除 `created()`、`loadProducts` 方法、`productsData` 状态)**
-
-与 Task 5 完全对称的处理,删除 L39-53:
-
-```javascript
-data: function() {
-  return {
-    productsData: []   // 删除
-  }
-},
-created: function() {
-  this.loadProducts()  // 删除
-},
-methods: {
-  loadProducts: function() { ... }  // 删除
-}
-```
-
-`displayProducts` computed 简化为:
-
-```javascript
-displayProducts: function() {
-  return this.products
-}
-```
-
-- [ ] **Step 3: lint 检查**
-
-```bash
-cd /root/cfc/cfc-frontend && npx eslint components/DimensionProducts.vue --no-error-on-unmatched-pattern 2>&1 | head -20
-```
-
----
-
-## Task 7: 优化 wuxing-sandbox preview 模式随机值抖动(A7)
-
-**优先级:** 🟢 低
-
-**文件:** `cfc-frontend/components/wuxing-sandbox.vue`
-
-### 问题分析
-
-L304-310,每次组件重新 mount 时 `Math.random()` 生成新的随机分数,导致数值闪烁:
-
-```javascript
-if (this.isPreview) {
-  var scores = []
-  for (var i = 0; i < 5; i++) {
-    scores.push(Math.floor(Math.random() * 51) + 35)  // 35-85 随机
-  }
-  this.previewScores = scores   // ⚠️ 每次 mount 重算
-}
-```
-
-### 修复方案
-
-将预览数据持久化到 Storage,首次生成后缓存,后续 mount 读取缓存。
-
-- [ ] **Step 1: 在 mounted 中加入缓存逻辑**
-
-将 L304-310 替换为:
-
-```javascript
-if (this.isPreview) {
-  var cacheKey = '_wuxing_preview_scores'
-  var cached = uni.getStorageSync(cacheKey)
-  if (cached && Array.isArray(cached) && cached.length === 5) {
-    this.previewScores = cached
-  } else {
-    var scores = []
-    for (var i = 0; i < 5; i++) {
-      scores.push(Math.floor(Math.random() * 51) + 35)
-    }
-    this.previewScores = scores
-    uni.setStorageSync(cacheKey, scores)
-  }
-}
-```
-
-**权衡**:这意味着游客第一次访问生成的随机值会一直保持,直到清除缓存。若希望"仅在当前 session 稳定"而非"永久稳定",将 `uni.setStorageSync` 替换为 `uni.setStorageSync(cacheKey, scores)` 并在 `onUnload` 清除。
-
----
-
-## Task 8: 确认 FamilyMemberCard roleLabel 后端填充(A8)
-
-**优先级:** 🟢 低(需后端确认)
-
-**文件:** `cfc-frontend/components/FamilyMemberCard.vue`
+## 执行验证
 
-- [ ] **Step 1: 搜索后端 FamilyMemberVO 是否填充了 roleLabel**
+### Step 1: 确认目标页面文件是否存在
 
 ```bash
-grep -n "roleLabel" /root/cfc/cfc-backend/src/main/java/com/etotem/cfc/dto/FamilyMemberVO.java
-grep -n "roleLabel" /root/cfc/cfc-backend/src/main/java/com/etotem/cfc/service/UserService.java | head -20
-```
-
-- [ ] **Step 2: 根据搜索结果处理**
-
-**若后端已填充**:在 `FamilyMemberCard.vue` 增加兜底显示:
-
-L9:
-
-```vue
-<text class="member-card__role">{{ member.roleLabel || (member.memberType === 'child' ? '孩子' : '家长') }}</text>
-```
-
-**若后端未填充**:建议在后端 `UserService.getVisibleMembers()` 中填充 `roleLabel` 字段,此任务标记为"需后端配合"。
-
----
-
-## Task 9: 收紧 parent-index 降级路径成员过滤条件(A9)
-
-**优先级:** 🟢 低
-
-**文件:** `cfc-frontend/pages/index/parent-index.vue`
-
-### 问题分析
-
-L432-433:
-
-```javascript
-var filteredChildren = dashChildren.filter(function(c) {
-  return c && c.id && c.id !== currentUserId && (c.role === 'child' || c.memberType === 'child' || !c.role)
-})
-```
-
-`!c.role` 的意图是"若成员没有 role 字段也保留",但这可能导致非孩子角色被误包含。
-
-### 修复方案
-
-收紧条件,明确只允许孩子角色:
-
-- [ ] **Step 1: 替换 L432-433 过滤条件**
-
-```javascript
-var filteredChildren = dashChildren.filter(function(c) {
-  return c && c.id && c.id !== currentUserId && (c.role === 'child' || c.memberType === 'child')
-})
-```
-
-删除 `|| !c.role`。
-
----
-
-## Task 10: 修复 FamilyMemberStrip scroll-view 高度(A10)
-
-**优先级:** 🟢 低
-
-**文件:** `cfc-frontend/components/FamilyMemberStrip.vue`
-
-### 问题分析
-
-L109-112:
-
-```css
-.member-strip__scroll {
-  white-space: nowrap;
-  width: 100%;
-}
-```
-
-`scroll-view` 组件若无固定高度,在部分 Android 机型上可能无法正确滚动。
-
-### 修复方案
-
-- [ ] **Step 1: 在 scroll-view 上添加固定高度**
-
-L6-13 区域,将 `<scroll-view>` 标签添加 style:
-
-```vue
-<scroll-view
-  v-if="allMembers.length >= 4"
-  class="member-strip__scroll"
-  scroll-x
-  :scroll-with-animation="true"
-  :enhanced="true"
-  :show-scrollbar="false"
-  style="height: 160rpx;"
->
+ls /root/cfc/cfc-frontend/pages/body/member-body-detail* 2>/dev/null || echo "NOT FOUND"
+ls /root/cfc/cfc-frontend/pages/mind/member-mind-detail* 2>/dev/null || echo "NOT FOUND"
+ls /root/cfc/cfc-frontend/pages/action/member-action-detail* 2>/dev/null || echo "NOT FOUND"
 ```
 
-160rpx 基于成员卡片高度(约 104rpx avatar 72rpx + padding 16rpx + 名称行 22rpx × 2)估算,留出足够空间。
-
----
-
-## 执行验证
+### Step 2: 根据结果选择修复路径(见 Task 1 描述)
 
 ### 编译验证
 ```bash
@@ -637,30 +106,13 @@ cd /root/cfc/cfc-backend && mvn clean compile -q
 
 ### 前端语法检查
 ```bash
-cd /root/cfc/cfc-frontend && npm run build:mp-weixin 2>&1 | grep -E "ERROR|error|Warning" | head -30
+cd /root/cfc/cfc-frontend && npm run build:mp-weixin 2>&1 | grep -E "ERROR|error" | head -20
 ```
 
 ### 微信开发者工具验证
-
-| 操作 | 预期 |
-|---|---|
-| 家长账号 → 首页 → 点击能量横条成员行 | 不再 404(Toast 提示或正常跳转) |
-| 家长账号 → 首页 → 推荐商品 → 查看更多 | 跳转发现页 |
-| 孩子账号 → 首页 → 点击沙盘点 | 跳转能量详情页,正常显示孩子数据 |
-| 孩子完成 5 个运动类任务 → 返回首页 | 运动健将徽章解锁 |
-| 孩子完成 5 个艺术类任务 → 返回首页 | 小艺术家徽章解锁 |
-| 孩子完成 5 个家务类任务 → 返回首页 | 家务小能手徽章解锁 |
+- 家长账号 → 首页 → 点击能量横条成员行 → 不再 404
 
 ### 回退方案
-
 ```bash
-git checkout cfc-frontend/pages/index/parent-index.vue \
-           cfc-frontend/pages/index/child-index.vue \
-           cfc-frontend/components/FamilyEnergyBar.vue \
-           cfc-frontend/components/DimensionActivities.vue \
-           cfc-frontend/components/DimensionProducts.vue \
-           cfc-frontend/components/wuxing-sandbox.vue \
-           cfc-frontend/components/FamilyMemberCard.vue \
-           cfc-frontend/components/FamilyMemberStrip.vue \
-           cfc-frontend/pages.json
+git checkout cfc-frontend/components/FamilyEnergyBar.vue
 ```