2026-08-05-family-relationship-strip-design.md 10.0 KB

家庭成员关系条(FamilyMemberStrip)关系展示增强设计

日期: 2026-08-05 状态: 已确认 关联组件: cfc-frontend/components/FamilyMemberStrip.vue, cfc-frontend/components/FamilyMemberCard.vue

1. 背景与目标

1.1 现状问题

FamilyMemberStrip 是水平成员选择条(8 个页面共用),目前每个成员卡片只显示:

  • 头像 + 关系徽章(关系类型文本,如"爸爸"/"妈妈"/"儿子")
  • 昵称、辈分、电话、能量分、配偶标记

缺失:关系质量信息(信任度/亲密度/沟通质量)完全没有呈现。

1.2 数据现状(关键约束)

数据 现状 位置
关系类型(pairwise) ✅ 已有 family_relationships 表:from→to + generation_value + relationship_key,双向各一条
信任/亲密度/沟通评分 ⚠️ 仅单成员维度 family_members.trust_score/intimacy_score/communication_score——本质是问卷答卷人(当前用户)对该成员的评分(RelationshipQuestionnaireService.updateMemberScores 写入)
问卷答卷 ✅ pairwise 天然成立 relationship_questionnaire_responses:respondent_id → snapshot_id → family_member_id

1.3 目标

  1. 卡片上直接呈现"当前用户与该成员"的关系类型 + 关系现状(信任/亲密度/沟通),无需交互
  2. 成员详情页以任意成员为中心,复用本组件展示该成员与所有其他成员的关系
  3. 长按操作菜单升级为带关系现状区块的 Action Sheet,视觉更精致
  4. 未创建家庭时显示引导(提示 + 创建家庭按钮);当前用户菜单增加"添加成员/邀请成员"入口

1.4 非目标

  • 不改动 FamilyRelationGraph(力导向关系图保持现状,作为可选全景视图)
  • 不在卡片间画连线(成员多时视觉混乱,经确认放弃连线方案)
  • 不改变 8 处现有 FamilyMemberStrip 调用方式(默认视角零改动)

2. 数据层设计(后端)

2.1 family_relationships 表扩展评分列

family_members 单成员评分之外,将评分落库到 pairwise 关系表,支撑"以任意成员为中心"的视角。

ALTER TABLE family_relationships
  ADD COLUMN trust_score DECIMAL(5,2) DEFAULT 0 COMMENT '信任度(答卷人视角)',
  ADD COLUMN intimacy_score DECIMAL(5,2) DEFAULT 0 COMMENT '亲密度(答卷人视角)',
  ADD COLUMN communication_score DECIMAL(5,2) DEFAULT 0 COMMENT '沟通质量(答卷人视角)';
  • 迁移入口: DatabaseInitializer.runMigrations() 使用 ensureColumn(),幂等
  • 同步: schema.sqlfamily_relationships CREATE TABLE 补三列(编号递增,搜索 // 迁移 确认最新号)
  • 实体: FamilyRelationship.java 增加三个 BigDecimal 字段

2.2 问卷写入同步到 pairwise 表

RelationshipQuestionnaireService.updateMemberScores(Long memberId, BigDecimal[] scores) 增加逻辑:

  1. 通过 memberId 找到被评成员(to_member_id)
  2. 通过答卷人(respondent)定位其 family_member_id(from_member_id);若答卷人无 family_member 记录则跳过(不阻塞主流程)
  3. 存在 family_relationships 行则更新三列评分;不存在则插入新行(generation_value 从双方成员 generation 计算)
  4. 保持事务内,失败仅记日志不阻断问卷保存

2.3 新增 pairwise 接口

POST /api/family/relationship/pairwise
入参: { "centerMemberId": 123 }   // 可选,空 = 当前用户视角
出参: Result<List<FamilyMemberVO>>  // 以 centerMember 为中心的关系列表
  • Controller: RelationshipQualityController 新增 pairwise 方法(@PostMapping
  • Service: RelationshipQualityService 新增 getPairwiseRelationships(Long centerMemberId, Long familyId)
    • 查 family_relationships 中 from_member_id = centerMemberId 的所有行
    • join family_members 取目标成员资料(昵称/头像/性别/手机号/generation)
    • 关系标签:优先 relationship_key → 否则用 FamilyRelationshipService.computeRelationshipLabel(generationValue, gender)
    • 评分:取该行的 trust/intimacy/communication(无记录默认 0)
    • 复用 FamilyMemberVO 结构返回(含 isSelf 标记 centerMember 自身)

2.4 家庭是否存在判定

前端通过 getVisibleFamilyMembers 的返回判定:接口返回空数组 = 无家庭或家庭成员不可见 → 显示"创建家庭"引导。

3. 前端设计

3.1 FamilyMemberCard 徽章区(无需交互,直接可见)

关系徽章下方增加三个迷你圆点(关系现状指示):

[ 爸爸 ]        ← 关系类型徽章(现有,配色保留)
● ● ●          ← 三个圆点:信任/亲密度/沟通
  • 颜色分级:绿 #10B981(≥70)/ 黄 #F59E0B(40-69)/ 红 #EF4444(<40)
  • 三圆点含义需可辨识 → 增加极小的下划线/序号辅助?不做(空间有限,色点语义在长按菜单中解释)
  • 空数据(全 0 或未测评)→ 显示灰色 · · ·
  • 数据源:member.trustScore/intimacyScore/communicationScore(FamilyMemberVO 已有字段,当前用户视角)
  • 卡片宽度 140rpx 不变,色点放在徽章正下方 2rpx 处,直径 12rpx

3.2 FamilyMemberStrip 空状态引导

当前模板 v-if="members && members.length > 0" 空时整条不渲染 → 增加 v-else 分支:

┌─────────────────────────────┐
│  🏠 还未创建家庭             │
│  创建家庭,开启全家幸福之旅    │
│  [+ 创建家庭]               │
└─────────────────────────────┘
  • 按钮 @clickcreateFamily(nickname + '的家庭')(复用 utils/api.jscreateFamily,参照 user-edit.vuecreateFamilyAction
  • 创建成功后 $emit('created'),父组件刷新成员列表

3.3 长按操作菜单(升级版 Action Sheet)

当前用户member.isSelf === true)与其他成员菜单不同:

┌─────────────────────────────────┐
│  (头像) 小明                     │
│         儿子 · 8岁               │
│  ── 关系现状 ──                  │
│  信任度   ████████ 82            │
│  亲密度   ██████   60            │
│  沟通质量 █████████ 91           │
│  ───────────────                 │
│  ➕ 添加成员                     │   ← 仅当前用户
│  📨 邀请成员                     │   ← 仅当前用户
│  🔄 切换到该成员视角              │   ← 非本人且有 userId
│  ✏️ 编辑资料                     │   ← isParent 且可编辑
│  📋 关系问卷  → 上次填写 3天前    │   ← 显示上次填写时间
│  💬 记录互动                     │
│  📞 拨打电话                     │   ← 有 phone
│  🗑️ 移除成员                    │   ← isParent 且非本人
└─────────────────────────────────┘
  • 头部: 头像 + 昵称 + 关系标签(relationship || relativeLabel || roleLabel
  • 关系现状区块: 三条迷你进度条(背景 #F3F4F6,填充色按分级),右侧数值;无数据时显示"未测评"
  • 当前用户专属: "添加成员"(跳 /pages/family/add-member)、"邀请成员"(跳邀请二维码页,参照 family-members.vue 的 📨 邀请成员逻辑)
  • 权限控制: 保留现有 canSwitch/canEdit/canRemove computed,新增 isSelf 判断
  • 视觉: 圆角 24rpx、柔和阴影、分区标题(12px 灰)、图标统一 emoji、操作项 44rpx 行高、底部取消按钮

3.4 FamilyMemberStrip 新增 centerMemberId prop

  • centerMemberId: { type: Number, default: null }
  • 不传(默认): 现有逻辑不变——父组件传 members 数组(当前用户视角)
  • 传入: 组件内部拉取 pairwise 接口,members 由组件自身组装(以 centerMember 为中心)

3.5 成员详情页接入

pages/home-pages/member-home-detail.vue 增加 FamilyMemberStrip 区块:

  • 传入 :center-member-id="memberId",展示"以该成员为中心"的关系条
  • 复用 3.3 的 Action Sheet(该页面的长按菜单沿用权限控制)

4. 交互流程

  1. 首页/维度页加载getVisibleFamilyMembers 返回空 → 显示创建家庭引导卡
  2. 有成员 → 卡片行展示,关系徽章 + 三色点
  3. 点卡片 → 选中(selectedMemberId)或切换视角(沿用现有逻辑)
  4. 长按卡片 → 弹出 Action Sheet:关系现状 + 操作项
  5. 成员详情页 → 以该成员为中心的关系条(pairwise)

5. 错误处理与边界

场景 处理
无家庭/无成员 空状态引导卡 + 创建家庭按钮
未填写关系问卷 色点灰色 · · ·,菜单关系区块"未测评"
pairwise 接口失败 组件 fallback 为传入的 members 渲染(不阻塞页面)
答卷人无 family_member 记录 问卷保存不阻塞,日志记录
长按非本人卡片 不显示添加/邀请成员
非家长长按 不显示编辑/移除(现有 canEdit/canRemove 逻辑保留)

6. 验证

  • 后端: mvn clean compile
  • 前端: npm run build:mp-weixin(微信小程序编译通过)
  • 手动验证场景:
    1. 未创建家庭用户 → 看到创建家庭引导
    2. 已有家庭 → 卡片显示关系徽章 + 三色点
    3. 长按自己 → 菜单含添加/邀请成员 + 关系现状
    4. 长按其他成员 → 菜单不含添加/邀请,含切换/编辑/问卷/互动/电话/移除(按权限)
    5. 成员详情页 → 以该成员为中心的关系条

7. 不做的事(YAGNI)

  • 卡片间连线(已放弃)
  • hover 交互(小程序无 hover 事件)
  • FamilyRelationGraph 改动
  • 复杂评分算法(沿用问卷简化评分)