|
|
@@ -0,0 +1,868 @@
|
|
|
+# 家庭成员卡片 Implementation Plan
|
|
|
+
|
|
|
+> **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 (`- [ ]`) for tracking.
|
|
|
+
|
|
|
+**Goal:** Add horizontal-scrolling family member card strip to four tab pages (home/body/mind/action), with per-member data filtering and show_to_family visibility control.
|
|
|
+
|
|
|
+**Architecture:** Add `show_to_family` column to `children` and `users` tables via DatabaseInitializer. Create `FamilyMemberCard` + `FamilyMemberStrip` components. Two new API endpoints under `FamilyUserController` for visible members query and visibility toggle. Integrate strip into all four dimension pages above FamilyEnergyBar.
|
|
|
+
|
|
|
+**Tech Stack:** Spring Boot 2.7.18 / MyBatis-Plus / uni-app Vue 2 (Options API, no `?.` operator, no CSS Grid)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Task 1: Database migration — add show_to_family column
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Modify: `cfc-backend/src/main/java/com/etotem/cfc/config/DatabaseInitializer.java`
|
|
|
+
|
|
|
+- [ ] **Step 1: Add migration block after existing family_member_attributes migration (~line 1556)**
|
|
|
+
|
|
|
+```java
|
|
|
+// ===== 迁移: children + users 表添加 show_to_family 字段(v1.3 家庭成员可见性) =====
|
|
|
+try {
|
|
|
+ String checkChildCol = "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'children' AND COLUMN_NAME = 'show_to_family'";
|
|
|
+ if (jdbcTemplate.queryForObject(checkChildCol, Integer.class) == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE children ADD COLUMN show_to_family TINYINT DEFAULT 1 COMMENT '是否对家庭成员可见: 1=是, 0=否'");
|
|
|
+ log.info("已添加 show_to_family 列到 children 表");
|
|
|
+ } else {
|
|
|
+ log.info("children.show_to_family 已存在,跳过");
|
|
|
+ }
|
|
|
+} catch (Exception e) {
|
|
|
+ log.warn("添加 children.show_to_family 列失败: {}", e.getMessage());
|
|
|
+}
|
|
|
+
|
|
|
+try {
|
|
|
+ String checkUserCol = "SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'users' AND COLUMN_NAME = 'show_to_family'";
|
|
|
+ if (jdbcTemplate.queryForObject(checkUserCol, Integer.class) == 0) {
|
|
|
+ jdbcTemplate.execute("ALTER TABLE users ADD COLUMN show_to_family TINYINT DEFAULT 1 COMMENT '是否对家庭成员可见: 1=是, 0=否'");
|
|
|
+ log.info("已添加 show_to_family 列到 users 表");
|
|
|
+ } else {
|
|
|
+ log.info("users.show_to_family 已存在,跳过");
|
|
|
+ }
|
|
|
+} catch (Exception e) {
|
|
|
+ log.warn("添加 users.show_to_family 列失败: {}", e.getMessage());
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 2: Verify compilation**
|
|
|
+
|
|
|
+Run: `cd cfc-backend && mvn clean compile -q`
|
|
|
+Expected: BUILD SUCCESS
|
|
|
+
|
|
|
+- [ ] **Step 3: Commit**
|
|
|
+
|
|
|
+```bash
|
|
|
+git add cfc-backend/src/main/java/com/etotem/cfc/config/DatabaseInitializer.java
|
|
|
+git commit -m "feat: add show_to_family column migration to children and users tables"
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Task 2: Update DTOs — add showToFamily field
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Modify: `cfc-backend/src/main/java/com/etotem/cfc/dto/UpdateChildDTO.java`
|
|
|
+- Modify: `cfc-backend/src/main/java/com/etotem/cfc/dto/ChildInfoDTO.java`
|
|
|
+
|
|
|
+- [ ] **Step 1: Add `showToFamily` to UpdateChildDTO**
|
|
|
+
|
|
|
+```java
|
|
|
+// After line 17 (private String theme;)
|
|
|
+ private Integer showToFamily; // 是否对家庭成员可见: 1=是, 0=否
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 2: Add `showToFamily` to ChildInfoDTO**
|
|
|
+
|
|
|
+```java
|
|
|
+// After line 21 (private Integer focusRemaining;)
|
|
|
+ private Integer showToFamily; // 是否对家庭成员可见: 1=是, 0=否
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 3: Verify compilation**
|
|
|
+
|
|
|
+Run: `cd cfc-backend && mvn clean compile -q`
|
|
|
+Expected: BUILD SUCCESS
|
|
|
+
|
|
|
+- [ ] **Step 4: Commit**
|
|
|
+
|
|
|
+```bash
|
|
|
+git add cfc-backend/src/main/java/com/etotem/cfc/dto/UpdateChildDTO.java cfc-backend/src/main/java/com/etotem/cfc/dto/ChildInfoDTO.java
|
|
|
+git commit -m "feat: add showToFamily field to ChildInfoDTO and UpdateChildDTO"
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Task 3: UserService — add getVisibleMembers and updateVisibility methods
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Modify: `cfc-backend/src/main/java/com/etotem/cfc/service/UserService.java`
|
|
|
+- Create: `cfc-backend/src/main/java/com/etotem/cfc/dto/FamilyMemberVO.java`
|
|
|
+
|
|
|
+- [ ] **Step 1: Create FamilyMemberVO DTO**
|
|
|
+
|
|
|
+```java
|
|
|
+package com.etotem.cfc.dto;
|
|
|
+
|
|
|
+import lombok.Data;
|
|
|
+
|
|
|
+@Data
|
|
|
+public class FamilyMemberVO {
|
|
|
+ private Long id;
|
|
|
+ private String nickname;
|
|
|
+ private String avatar;
|
|
|
+ private String memberType; // 'child' 或 'parent'
|
|
|
+ private String roleLabel; // '孩子' 或 '家长'
|
|
|
+ private String danLevel; // 段位(仅孩子)
|
|
|
+ private Integer totalPoints;
|
|
|
+ private Integer showToFamily; // 1=可见, 0=不可见
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 2: Add `getVisibleMembers` method to UserService**
|
|
|
+
|
|
|
+Insert after `getChildren()` method (after line 749):
|
|
|
+
|
|
|
+```java
|
|
|
+public List<FamilyMemberVO> getVisibleMembers(Long userId) {
|
|
|
+ User user = userMapper.selectById(userId);
|
|
|
+ if (user == null || user.getFamilyId() == null) {
|
|
|
+ return Collections.emptyList();
|
|
|
+ }
|
|
|
+
|
|
|
+ Long familyId = user.getFamilyId();
|
|
|
+ List<FamilyMemberVO> members = new ArrayList<>();
|
|
|
+
|
|
|
+ // 查询可见的家长成员
|
|
|
+ List<User> parents = userMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<User>()
|
|
|
+ .eq(User::getFamilyId, familyId)
|
|
|
+ .eq(User::getRole, "parent")
|
|
|
+ .eq(User::getShowToFamily, 1)
|
|
|
+ );
|
|
|
+ for (User p : parents) {
|
|
|
+ FamilyMemberVO vo = new FamilyMemberVO();
|
|
|
+ vo.setId(p.getId());
|
|
|
+ vo.setNickname(p.getNickname());
|
|
|
+ vo.setAvatar(p.getAvatar());
|
|
|
+ vo.setMemberType("parent");
|
|
|
+ vo.setRoleLabel("家长");
|
|
|
+ vo.setTotalPoints(p.getTotalPoints());
|
|
|
+ vo.setShowToFamily(p.getShowToFamily());
|
|
|
+ members.add(vo);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 查询可见的孩子成员
|
|
|
+ List<Child> children = childMapper.selectList(
|
|
|
+ new LambdaQueryWrapper<Child>()
|
|
|
+ .eq(Child::getFamilyId, familyId)
|
|
|
+ .eq(Child::getShowToFamily, 1)
|
|
|
+ );
|
|
|
+ for (Child c : children) {
|
|
|
+ FamilyMemberVO vo = new FamilyMemberVO();
|
|
|
+ vo.setId(c.getId());
|
|
|
+ vo.setNickname(c.getNickname());
|
|
|
+ vo.setAvatar(null);
|
|
|
+ vo.setMemberType("child");
|
|
|
+ vo.setRoleLabel("孩子");
|
|
|
+ vo.setDanLevel(c.getDanLevel());
|
|
|
+ vo.setTotalPoints(c.getTotalPoints());
|
|
|
+ vo.setShowToFamily(c.getShowToFamily());
|
|
|
+ members.add(vo);
|
|
|
+ }
|
|
|
+
|
|
|
+ return members;
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+Add imports at top of file:
|
|
|
+```java
|
|
|
+import com.etotem.cfc.dto.FamilyMemberVO;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.Collections;
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 3: Add `updateChildVisibility` method to UserService**
|
|
|
+
|
|
|
+Insert after `getVisibleMembers`:
|
|
|
+
|
|
|
+```java
|
|
|
+public boolean updateVisibility(Long userId, Long memberId, String memberType, Integer showToFamily) {
|
|
|
+ User user = userMapper.selectById(userId);
|
|
|
+ if (user == null) return false;
|
|
|
+
|
|
|
+ if ("child".equals(memberType)) {
|
|
|
+ // 只有家长可以修改孩子的可见性
|
|
|
+ if (!"parent".equals(user.getRole())) return false;
|
|
|
+ Child child = childMapper.selectById(memberId);
|
|
|
+ if (child == null || !child.getFamilyId().equals(user.getFamilyId())) return false;
|
|
|
+ child.setShowToFamily(showToFamily);
|
|
|
+ child.setUpdatedAt(new Date());
|
|
|
+ childMapper.updateById(child);
|
|
|
+ return true;
|
|
|
+ } else if ("parent".equals(memberType)) {
|
|
|
+ // 用户只能修改自己的可见性
|
|
|
+ if (!memberId.equals(userId)) return false;
|
|
|
+ user.setShowToFamily(showToFamily);
|
|
|
+ user.setUpdatedAt(new Date());
|
|
|
+ userMapper.updateById(user);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 4: Update `toChildInfoDTO` to include showToFamily**
|
|
|
+
|
|
|
+In the existing `toChildInfoDTO` method (line 802+), add after line ~819:
|
|
|
+```java
|
|
|
+dto.setShowToFamily(child.getShowToFamily());
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 5: Update `updateChild` method to handle showToFamily**
|
|
|
+
|
|
|
+In `updateChild` method (line 751), add before `child.setUpdatedAt(new Date())`:
|
|
|
+```java
|
|
|
+if (dto.getShowToFamily() != null) {
|
|
|
+ child.setShowToFamily(dto.getShowToFamily());
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 6: Verify compilation**
|
|
|
+
|
|
|
+Run: `cd cfc-backend && mvn clean compile -q`
|
|
|
+Expected: BUILD SUCCESS
|
|
|
+
|
|
|
+- [ ] **Step 7: Commit**
|
|
|
+
|
|
|
+```bash
|
|
|
+git add cfc-backend/src/main/java/com/etotem/cfc/dto/FamilyMemberVO.java cfc-backend/src/main/java/com/etotem/cfc/service/UserService.java
|
|
|
+git commit -m "feat: add getVisibleMembers and updateVisibility to UserService"
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Task 4: FamilyUserController — add two new API endpoints
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Modify: `cfc-backend/src/main/java/com/etotem/cfc/controller/family/FamilyUserController.java`
|
|
|
+
|
|
|
+- [ ] **Step 1: Add `getVisibleFamilyMembers` endpoint**
|
|
|
+
|
|
|
+Insert before `getFamilyMembers` (before line 167):
|
|
|
+
|
|
|
+```java
|
|
|
+@Operation(summary = "获取对当前用户可见的家庭成员列表")
|
|
|
+@PostMapping("/members/visible")
|
|
|
+public Result<List<FamilyMemberVO>> getVisibleFamilyMembers(javax.servlet.http.HttpServletRequest request) {
|
|
|
+ Long userId = getUserId(request);
|
|
|
+ List<FamilyMemberVO> members = userService.getVisibleMembers(userId);
|
|
|
+ return Result.success(members);
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 2: Add `updateMemberVisibility` endpoint**
|
|
|
+
|
|
|
+Insert after the above:
|
|
|
+
|
|
|
+```java
|
|
|
+@Operation(summary = "更新家庭成员可见性")
|
|
|
+@PostMapping("/members/update-visibility")
|
|
|
+public Result<Boolean> updateMemberVisibility(javax.servlet.http.HttpServletRequest request,
|
|
|
+ @RequestBody Map<String, Object> body) {
|
|
|
+ Long userId = getUserId(request);
|
|
|
+ Long memberId = Long.valueOf(body.get("memberId").toString());
|
|
|
+ String memberType = (String) body.get("memberType");
|
|
|
+ Integer showToFamily = Integer.valueOf(body.get("showToFamily").toString());
|
|
|
+ boolean success = userService.updateVisibility(userId, memberId, memberType, showToFamily);
|
|
|
+ return Result.success(success);
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 3: Add missing import**
|
|
|
+
|
|
|
+Ensure `FamilyMemberVO` is imported via the existing `import com.etotem.cfc.dto.*;` on line 5 — it's already a wildcard import, so `FamilyMemberVO` will be auto-included.
|
|
|
+
|
|
|
+- [ ] **Step 4: Verify compilation**
|
|
|
+
|
|
|
+Run: `cd cfc-backend && mvn clean compile -q`
|
|
|
+Expected: BUILD SUCCESS
|
|
|
+
|
|
|
+- [ ] **Step 5: Commit**
|
|
|
+
|
|
|
+```bash
|
|
|
+git add cfc-backend/src/main/java/com/etotem/cfc/controller/family/FamilyUserController.java
|
|
|
+git commit -m "feat: add /family/user/members/visible and /members/update-visibility endpoints"
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Task 5: Frontend API — add new API functions
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Modify: `cfc-frontend/utils/api.js`
|
|
|
+
|
|
|
+- [ ] **Step 1: Add `getVisibleFamilyMembers` and `updateMemberVisibility` exports
|
|
|
+
|
|
|
+Insert after `getFamilyMembers` (after line 146):
|
|
|
+
|
|
|
+```javascript
|
|
|
+// 获取对当前用户可见的家庭成员列表(已过滤 show_to_family=0 的成员)
|
|
|
+export const getVisibleFamilyMembers = () => {
|
|
|
+ return request('/api/family/user/members/visible', 'POST')
|
|
|
+}
|
|
|
+
|
|
|
+// 更新成员可见性
|
|
|
+export const updateMemberVisibility = (memberId, memberType, showToFamily) => {
|
|
|
+ return request('/api/family/user/members/update-visibility', 'POST', { memberId, memberType, showToFamily })
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 2: Commit**
|
|
|
+
|
|
|
+```bash
|
|
|
+git add cfc-frontend/utils/api.js
|
|
|
+git commit -m "feat: add getVisibleFamilyMembers and updateMemberVisibility API functions"
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Task 6: FamilyMemberCard component
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Create: `cfc-frontend/components/FamilyMemberCard.vue`
|
|
|
+
|
|
|
+- [ ] **Step 1: Create component**
|
|
|
+
|
|
|
+```vue
|
|
|
+<template>
|
|
|
+ <view class="family-member-card" :class="{ 'is-active': isSelected }" @click="onSelect">
|
|
|
+ <view class="member-avatar-wrap">
|
|
|
+ <image v-if="member.avatar" class="member-avatar" :src="member.avatar" mode="aspectFill" />
|
|
|
+ <view v-else class="member-avatar-default">
|
|
|
+ <text class="avatar-letter">{{ displayLetter }}</text>
|
|
|
+ </view>
|
|
|
+ <text class="member-role-badge">{{ member.memberType === 'parent' ? '家长' : '孩子' }}</text>
|
|
|
+ </view>
|
|
|
+ <text class="member-name">{{ member.nickname }}</text>
|
|
|
+ <text class="member-dan" v-if="member.danLevel">{{ member.danLevel }}</text>
|
|
|
+ <view class="selected-check" v-if="isSelected">
|
|
|
+ <text class="check-icon">✓</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+export default {
|
|
|
+ props: {
|
|
|
+ member: { type: Object, required: true },
|
|
|
+ isSelected: { type: Boolean, default: false }
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ displayLetter: function() {
|
|
|
+ if (!this.member.nickname) return '?'
|
|
|
+ return this.member.nickname[0]
|
|
|
+ }
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ onSelect: function() {
|
|
|
+ this.$emit('select', this.member)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.family-member-card {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ padding: 16rpx 20rpx;
|
|
|
+ margin-right: 16rpx;
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 20rpx;
|
|
|
+ min-width: 130rpx;
|
|
|
+ position: relative;
|
|
|
+ box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
|
|
|
+ border: 3rpx solid transparent;
|
|
|
+ transition: all 0.2s;
|
|
|
+}
|
|
|
+.family-member-card.is-active {
|
|
|
+ border-color: #F97316;
|
|
|
+ background: #FFF7ED;
|
|
|
+}
|
|
|
+.member-avatar-wrap {
|
|
|
+ position: relative;
|
|
|
+ margin-bottom: 8rpx;
|
|
|
+}
|
|
|
+.member-avatar {
|
|
|
+ width: 72rpx;
|
|
|
+ height: 72rpx;
|
|
|
+ border-radius: 50%;
|
|
|
+}
|
|
|
+.member-avatar-default {
|
|
|
+ width: 72rpx;
|
|
|
+ height: 72rpx;
|
|
|
+ border-radius: 50%;
|
|
|
+ background: #FDE68A;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+}
|
|
|
+.avatar-letter {
|
|
|
+ font-size: 32rpx;
|
|
|
+ color: #92400E;
|
|
|
+ font-weight: bold;
|
|
|
+}
|
|
|
+.member-role-badge {
|
|
|
+ position: absolute;
|
|
|
+ bottom: -4rpx;
|
|
|
+ right: -8rpx;
|
|
|
+ font-size: 18rpx;
|
|
|
+ color: #fff;
|
|
|
+ background: #F97316;
|
|
|
+ padding: 2rpx 8rpx;
|
|
|
+ border-radius: 12rpx;
|
|
|
+ line-height: 1.4;
|
|
|
+ transform: scale(0.85);
|
|
|
+}
|
|
|
+.member-name {
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #333;
|
|
|
+ font-weight: 500;
|
|
|
+ text-align: center;
|
|
|
+ max-width: 100rpx;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+}
|
|
|
+.member-dan {
|
|
|
+ font-size: 18rpx;
|
|
|
+ color: #F97316;
|
|
|
+ margin-top: 2rpx;
|
|
|
+}
|
|
|
+.selected-check {
|
|
|
+ position: absolute;
|
|
|
+ top: 4rpx;
|
|
|
+ right: 4rpx;
|
|
|
+ width: 32rpx;
|
|
|
+ height: 32rpx;
|
|
|
+ background: #F97316;
|
|
|
+ border-radius: 50%;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+}
|
|
|
+.check-icon {
|
|
|
+ color: #fff;
|
|
|
+ font-size: 20rpx;
|
|
|
+ font-weight: bold;
|
|
|
+}
|
|
|
+</style>
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 2: Commit**
|
|
|
+
|
|
|
+```bash
|
|
|
+git add cfc-frontend/components/FamilyMemberCard.vue
|
|
|
+git commit -m "feat: add FamilyMemberCard component"
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Task 7: FamilyMemberStrip component
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Create: `cfc-frontend/components/FamilyMemberStrip.vue`
|
|
|
+
|
|
|
+- [ ] **Step 1: Create component**
|
|
|
+
|
|
|
+```vue
|
|
|
+<template>
|
|
|
+ <view class="member-strip" v-if="members && members.length > 0">
|
|
|
+ <scroll-view class="strip-scroll" scroll-x :show-scrollbar="false" :enhanced="true">
|
|
|
+ <view class="strip-inner">
|
|
|
+ <FamilyMemberCard
|
|
|
+ v-for="m in members"
|
|
|
+ :key="m.memberType + '-' + m.id"
|
|
|
+ :member="m"
|
|
|
+ :isSelected="m.id === selectedMemberId && m.memberType === selectedMemberType"
|
|
|
+ @select="onSelectMember"
|
|
|
+ />
|
|
|
+ </view>
|
|
|
+ </scroll-view>
|
|
|
+ </view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import FamilyMemberCard from './FamilyMemberCard.vue'
|
|
|
+
|
|
|
+export default {
|
|
|
+ components: { FamilyMemberCard },
|
|
|
+ props: {
|
|
|
+ members: { type: Array, default: function() { return [] } },
|
|
|
+ selectedMemberId: { type: Number, default: null },
|
|
|
+ selectedMemberType: { type: String, default: '' }
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ onSelectMember: function(member) {
|
|
|
+ this.$emit('select', member)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.member-strip {
|
|
|
+ margin: 0 30rpx 16rpx;
|
|
|
+}
|
|
|
+.strip-scroll {
|
|
|
+ width: 100%;
|
|
|
+ white-space: nowrap;
|
|
|
+}
|
|
|
+.strip-inner {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: row;
|
|
|
+ padding: 4rpx 0;
|
|
|
+}
|
|
|
+</style>
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 2: Commit**
|
|
|
+
|
|
|
+```bash
|
|
|
+git add cfc-frontend/components/FamilyMemberStrip.vue
|
|
|
+git commit -m "feat: add FamilyMemberStrip horizontal scrolling component"
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Task 8: Integrate FamilyMemberStrip into parent-index.vue
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Modify: `cfc-frontend/pages/index/parent-index.vue`
|
|
|
+
|
|
|
+- [ ] **Step 1: Add component import (after line ~361)**
|
|
|
+
|
|
|
+```javascript
|
|
|
+import FamilyMemberStrip from '../../components/FamilyMemberStrip.vue'
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 2: Add to components block (around line ~375)**
|
|
|
+
|
|
|
+```javascript
|
|
|
+FamilyMemberStrip,
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 3: Add data properties (in data() around line ~390)**
|
|
|
+
|
|
|
+```javascript
|
|
|
+familyMembersVisible: [],
|
|
|
+selectedMemberId: null,
|
|
|
+selectedMemberType: 'child',
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 4: Insert FamilyMemberStrip in template after PageBanner**
|
|
|
+
|
|
|
+Insert between `PageBanner` (line ~10) and the `children-row` section (line ~13):
|
|
|
+
|
|
|
+```html
|
|
|
+<!-- ===== 家庭成员卡片条 ===== -->
|
|
|
+<FamilyMemberStrip
|
|
|
+ v-if="isLoggedIn"
|
|
|
+ :members="familyMembersVisible"
|
|
|
+ :selectedMemberId="selectedMemberId"
|
|
|
+ :selectedMemberType="selectedMemberType"
|
|
|
+ @select="onFamilyMemberSelect" />
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 5: Add data loading in loadData() method**
|
|
|
+
|
|
|
+After the existing `getFamilyMembers()` call (~line 488), add:
|
|
|
+
|
|
|
+```javascript
|
|
|
+try {
|
|
|
+ const visibleRes = await getVisibleFamilyMembers()
|
|
|
+ const visibleMembers = visibleRes.data || []
|
|
|
+ this.familyMembersVisible = visibleMembers
|
|
|
+ // 默认选中第一个孩子
|
|
|
+ var firstChild = visibleMembers.find(function(m) { return m.memberType === 'child' })
|
|
|
+ if (firstChild) {
|
|
|
+ this.selectedMemberId = firstChild.id
|
|
|
+ this.selectedMemberType = firstChild.memberType
|
|
|
+ }
|
|
|
+} catch (e) {
|
|
|
+ console.log('获取可见家庭成员失败', e)
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 6: Add `onFamilyMemberSelect` method**
|
|
|
+
|
|
|
+```javascript
|
|
|
+onFamilyMemberSelect: function(member) {
|
|
|
+ this.selectedMemberId = member.id
|
|
|
+ this.selectedMemberType = member.memberType
|
|
|
+ // 切换选中孩子以刷新数据
|
|
|
+ if (member.memberType === 'child') {
|
|
|
+ this.selectedChild = { id: member.id, nickname: member.nickname }
|
|
|
+ this.loadChildData(member.id)
|
|
|
+ }
|
|
|
+ // 如果是家长,切换为家长视图
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 7: Add import for `getVisibleFamilyMembers`**
|
|
|
+
|
|
|
+Find the existing import line ~354:
|
|
|
+```javascript
|
|
|
+import { getFamilyMembers, getChildren, ... } from '../../utils/api.js'
|
|
|
+```
|
|
|
+Change to:
|
|
|
+```javascript
|
|
|
+import { getFamilyMembers, getChildren, getVisibleFamilyMembers, getPendingReviewTasks, ... } from '../../utils/api.js'
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 8: Commit**
|
|
|
+
|
|
|
+```bash
|
|
|
+git add cfc-frontend/pages/index/parent-index.vue
|
|
|
+git commit -m "feat: integrate FamilyMemberStrip into parent-index page"
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Task 9: Integrate FamilyMemberStrip into body/index.vue
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Modify: `cfc-frontend/pages/body/index.vue`
|
|
|
+
|
|
|
+- [ ] **Step 1: Add component import after existing imports (~line 1-5)**
|
|
|
+
|
|
|
+```javascript
|
|
|
+import FamilyMemberStrip from '../../components/FamilyMemberStrip.vue'
|
|
|
+import { getVisibleFamilyMembers } from '../../utils/api.js'
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 2: Add to components block**
|
|
|
+
|
|
|
+```javascript
|
|
|
+FamilyMemberStrip,
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 3: Add data properties (in data())**
|
|
|
+
|
|
|
+```javascript
|
|
|
+familyMembersVisible: [],
|
|
|
+selectedMemberId: null,
|
|
|
+selectedMemberType: '',
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 4: Insert FamilyMemberStrip in template after PageBanner (after line 6)**
|
|
|
+
|
|
|
+```html
|
|
|
+<!-- ===== 家庭成员卡片条 ===== -->
|
|
|
+<FamilyMemberStrip
|
|
|
+ v-if="isLoggedIn"
|
|
|
+ :members="familyMembersVisible"
|
|
|
+ :selectedMemberId="selectedMemberId"
|
|
|
+ :selectedMemberType="selectedMemberType"
|
|
|
+ @select="onFamilyMemberSelect" />
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 5: Add data loading and onFamilyMemberSelect method**
|
|
|
+
|
|
|
+In `onShow` or a dedicated `loadFamilyMembers` method:
|
|
|
+```javascript
|
|
|
+loadVisibleMembers: function() {
|
|
|
+ var self = this
|
|
|
+ getVisibleFamilyMembers().then(function(res) {
|
|
|
+ if (res && res.data) {
|
|
|
+ self.familyMembersVisible = res.data
|
|
|
+ }
|
|
|
+ }).catch(function(e) {
|
|
|
+ console.log('获取可见家庭成员失败', e)
|
|
|
+ })
|
|
|
+},
|
|
|
+onFamilyMemberSelect: function(member) {
|
|
|
+ this.selectedMemberId = member.id
|
|
|
+ this.selectedMemberType = member.memberType
|
|
|
+ // 切换该成员的健康数据
|
|
|
+ this.loadHealthData(member)
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 6: Commit**
|
|
|
+
|
|
|
+```bash
|
|
|
+git add cfc-frontend/pages/body/index.vue
|
|
|
+git commit -m "feat: integrate FamilyMemberStrip into body page"
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Task 10: Integrate FamilyMemberStrip into mind/index.vue
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Modify: `cfc-frontend/pages/mind/index.vue`
|
|
|
+
|
|
|
+- [ ] **Step 1: Add import + component + data + template insertion**
|
|
|
+
|
|
|
+Same pattern as Task 9 — insert `FamilyMemberStrip` after `PageBanner`, load `getVisibleFamilyMembers`, add `onFamilyMemberSelect`.
|
|
|
+
|
|
|
+Find the template block and add after `PageBanner` closing tag (after line ~6):
|
|
|
+```html
|
|
|
+<FamilyMemberStrip
|
|
|
+ v-if="isLoggedIn"
|
|
|
+ :members="familyMembersVisible"
|
|
|
+ :selectedMemberId="selectedMemberId"
|
|
|
+ :selectedMemberType="selectedMemberType"
|
|
|
+ @select="onFamilyMemberSelect" />
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 2: Commit**
|
|
|
+
|
|
|
+```bash
|
|
|
+git add cfc-frontend/pages/mind/index.vue
|
|
|
+git commit -m "feat: integrate FamilyMemberStrip into mind page"
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Task 11: Integrate FamilyMemberStrip into action/index.vue
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Modify: `cfc-frontend/pages/action/index.vue`
|
|
|
+
|
|
|
+- [ ] **Step 1: Same pattern as Task 9/10**
|
|
|
+
|
|
|
+Insert after `PageBanner` (~line 6):
|
|
|
+```html
|
|
|
+<FamilyMemberStrip
|
|
|
+ v-if="isLoggedIn"
|
|
|
+ :members="familyMembersVisible"
|
|
|
+ :selectedMemberId="selectedMemberId"
|
|
|
+ :selectedMemberType="selectedMemberType"
|
|
|
+ @select="onFamilyMemberSelect" />
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 2: Commit**
|
|
|
+
|
|
|
+```bash
|
|
|
+git add cfc-frontend/pages/action/index.vue
|
|
|
+git commit -m "feat: integrate FamilyMemberStrip into action page"
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Task 12: Visibility toggle UI — child edit page
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Modify: `cfc-frontend/pages/profile/edit-child.vue`
|
|
|
+
|
|
|
+- [ ] **Step 1: Add visibility toggle switch after the theme picker (after line ~65)**
|
|
|
+
|
|
|
+```html
|
|
|
+<!-- 对家庭成员可见 -->
|
|
|
+<view class="form-item">
|
|
|
+ <text class="label">对家庭成员可见</text>
|
|
|
+ <switch :checked="form.showToFamily !== 0" @change="onVisibilityChange" color="#F97316" />
|
|
|
+ <text class="hint">关闭后其他家庭成员看不到该孩子</text>
|
|
|
+</view>
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 2: Add change handler and form default**
|
|
|
+
|
|
|
+```javascript
|
|
|
+onVisibilityChange: function(e) {
|
|
|
+ this.form.showToFamily = e.detail.value ? 1 : 0
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+In `data()`, set form default:
|
|
|
+```javascript
|
|
|
+form: {
|
|
|
+ // ... existing fields
|
|
|
+ showToFamily: 1
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+In `onLoad`, when loading child data:
|
|
|
+```javascript
|
|
|
+// after setting form fields
|
|
|
+self.form.showToFamily = child.showToFamily !== undefined ? child.showToFamily : 1
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 3: Commit**
|
|
|
+
|
|
|
+```bash
|
|
|
+git add cfc-frontend/pages/profile/edit-child.vue
|
|
|
+git commit -m "feat: add showToFamily toggle to child edit page"
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Task 13: Visibility toggle UI — profile page (for self)
|
|
|
+
|
|
|
+**Files:**
|
|
|
+- Modify: `cfc-frontend/pages/profile/profile.vue`
|
|
|
+
|
|
|
+- [ ] **Step 1: Add visibility toggle in settings section**
|
|
|
+
|
|
|
+Find the profile settings area and add:
|
|
|
+```html
|
|
|
+<view class="setting-item">
|
|
|
+ <text class="setting-label">对家庭成员可见</text>
|
|
|
+ <switch :checked="showToFamily !== 0" @change="onSelfVisibilityChange" color="#F97316" />
|
|
|
+</view>
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 2: Add handler**
|
|
|
+
|
|
|
+```javascript
|
|
|
+onSelfVisibilityChange: function(e) {
|
|
|
+ var show = e.detail.value ? 1 : 0
|
|
|
+ var self = this
|
|
|
+ updateMemberVisibility(self.userId, 'parent', show).then(function(res) {
|
|
|
+ if (res && res.code === 200) {
|
|
|
+ uni.showToast({ title: '已更新', icon: 'success' })
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+- [ ] **Step 3: Commit**
|
|
|
+
|
|
|
+```bash
|
|
|
+git add cfc-frontend/pages/profile/profile.vue
|
|
|
+git commit -m "feat: add showToFamily toggle to profile page"
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+### Task 14: Final verification
|
|
|
+
|
|
|
+- [ ] **Step 1: Backend compilation**
|
|
|
+
|
|
|
+Run: `cd cfc-backend && mvn clean compile -q`
|
|
|
+Expected: BUILD SUCCESS
|
|
|
+
|
|
|
+- [ ] **Step 2: Quick LSP check on modified frontend files**
|
|
|
+
|
|
|
+Check for template syntax issues (no `?.`, no CSS Grid).
|
|
|
+
|
|
|
+- [ ] **Step 3: Verify file checklist**
|
|
|
+
|
|
|
+- [ ] `DatabaseInitializer.java` — migration added
|
|
|
+- [ ] `UpdateChildDTO.java` — showToFamily field
|
|
|
+- [ ] `ChildInfoDTO.java` — showToFamily field
|
|
|
+- [ ] `FamilyMemberVO.java` — new DTO
|
|
|
+- [ ] `UserService.java` — getVisibleMembers + updateVisibility
|
|
|
+- [ ] `FamilyUserController.java` — 2 new endpoints
|
|
|
+- [ ] `api.js` — 2 new API functions
|
|
|
+- [ ] `FamilyMemberCard.vue` — new component
|
|
|
+- [ ] `FamilyMemberStrip.vue` — new component
|
|
|
+- [ ] `parent-index.vue` — strip integrated
|
|
|
+- [ ] `body/index.vue` — strip integrated
|
|
|
+- [ ] `mind/index.vue` — strip integrated
|
|
|
+- [ ] `action/index.vue` — strip integrated
|
|
|
+- [ ] `edit-child.vue` — visibility toggle
|
|
|
+- [ ] `profile.vue` — visibility toggle (self)
|