2026-07-31-family-members-redesign.md 11 KB

家庭成员页面重新设计 — 实施计划

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: 重新设计家庭管理员页面,改为温暖家庭风的卡片式布局,"添加"和"邀请"改为底部固定按钮,邀请直接弹出海报。

Architecture: 单页面重写 pages/profile-extra/family-members.vue,移除齿轮设置弹窗、筛选标签、行内三维评分条,新增暖橙渐变头部、卡片式成员列表、底部固定操作栏,邀请流程直接调用 generateFamilyInviteQrCode 后弹出 SharePoster。

Tech Stack: uni-app Vue 2 Options API,微信小程序

参考设计文档: docs/superpowers/specs/2026-07-31-family-members-redesign.md

Global Constraints

  • 禁止可选链 ?.(用 && 替代)
  • 禁止 CSS Grid(用 flexbox)
  • 禁止 :key 表达式(用方法调用替代)
  • 使用 Vue 2 Options API
  • 品牌色:暖橙 #F97316 / 米白 #FFFCF5 / 暖灰 #F5F0EB

文件结构

文件 操作 说明
pages/profile-extra/family-members.vue 重写 完整替换模板/脚本/样式
components/SharePoster.vue 无变更 保持现有,直接调用

Task 1: 重写页面模板 — 头部 + 成员列表 + 空状态

Files:

  • Modify: pages/profile-extra/family-members.vue (template section)

Interfaces:

  • Consumes: members (Array), filteredMembers (computed→直接返回 members), familyName (String), loading (Boolean)
  • Produces: 渲染完整的页面结构(头部、成员列表、空状态、底部栏)

  • [ ] Step 1: 替换 <template> 头部

替换现有头部(紫色渐变 + 齿轮图标)为暖橙渐变头部:

<!-- ===== 家庭信息头部 ===== -->
<view class="family-header">
  <view class="family-header-content">
    <text class="family-header-icon">🏠</text>
    <view class="family-header-text">
      <text class="family-name">{{ familyName || '我的家庭' }}</text>
      <text class="family-member-count">{{ members.length }} 位成员</text>
    </view>
  </view>
</view>
  • Step 2: 替换成员列表为卡片式布局

移除筛选标签和行内三维评分条,改为纯卡片:

<!-- ===== 成员卡片列表 ===== -->
<view class="member-list">
  <view class="member-card" v-for="(m, i) in filteredMembers" :key="getMemberKey(m, i)" @click="showMemberDetail(m)">
    <image class="member-avatar" :src="m.avatar || '/static/default-avatar.png'" mode="aspectFill"></image>
    <view class="member-info">
      <text class="member-name">{{ m.nickname || '未设置' }}</text>
      <text class="member-role-tag">{{ m.relativeLabel || m.roleLabel || (m.effectiveRole === 'parent' ? '家长' : '孩子') }}</text>
    </view>
    <text class="member-arrow">›</text>
  </view>
</view>
  • [ ] Step 3: 替换空状态

    <!-- ===== 空状态 ===== -->
    <view v-if="members.length === 0" class="empty-state">
    <text class="empty-icon">👨‍👩‍👧‍👦</text>
    <text class="empty-title">还没有家庭成员</text>
    <text class="empty-desc">邀请家人加入,一起记录成长</text>
    <button class="empty-invite-btn" @click="onInvite">📨 邀请家人</button>
    </view>
    
  • [ ] Step 4: 添加底部固定栏

    <!-- ===== 底部操作栏 ===== -->
    <view class="bottom-bar">
    <button class="bottom-btn btn-add" @click="onAddMember">+ 添加成员</button>
    <button class="bottom-btn btn-invite" @click="onInvite">📨 邀请成员</button>
    </view>
    
  • [ ] Step 5: 保留成员详情弹窗模板(简化关闭按钮)

保留 member-detail-card 弹窗,仅微调视觉(去掉原来紫色相关样式,改为暖橙风格)。移除 showSettings 和 showInviteCard 相关弹窗。

  • [ ] Step 6: 保留 SharePoster 引用

    <SharePoster :show="showPoster" :referralCode="inviteCode" :qrCodeBase64="inviteQrCode" :nickname="userNickname" :userAvatar="userAvatar" @close="showPoster = false" />
    

Task 2: 重写页面脚本

Files:

  • Modify: pages/profile-extra/family-members.vue (script section)

Interfaces:

  • Consumes: listFamilyMembers, switchToFamilyMember, kickFamilyMemberById, generateFamilyInviteQrCode, getFamilyMembers, updateFamilyMember from api.js
  • Produces: 所有页面交互逻辑

  • [ ] Step 1: 精简 imports 和 data

移除 showSettings, showInviteCard 相关字段。保留 showPoster, showMemberDetailModal, detailMember, memberScores 等。

import {
  listFamilyMembers,
  switchToFamilyMember,
  kickFamilyMemberById,
  generateFamilyInviteQrCode,
  getFamilyMembers,
  updateFamilyMember
} from '../../utils/api.js'
import store from '../../store/index.js'
import SharePoster from '@/components/SharePoster.vue'

export default {
  components: { SharePoster },
  data() {
    return {
      userId: 0, familyId: 0, familyName: '', inviteCode: '', inviteQrCode: '',
      creatorId: null, members: [], isAdmin: false, loading: false,
      showPoster: false, showMemberDetailModal: false, detailMember: null,
      memberScores: {}, userNickname: '', userAvatar: ''
    }
  },
  • Step 2: 简化 computed

移除 filteredMembers 的筛选逻辑(直接返回 members),移除 detailMemberScores(保留,因为详情弹窗仍显示评分)。

computed: {
  filteredMembers() {
    return this.members  // 无筛选标签,直接返回全部
  },
  detailMemberScores() {
    if (!this.detailMember) return {}
    return this.memberScores[this.detailMember.id] || {}
  }
},
  • [ ] Step 3: 重写 onInvite 方法 — 直接弹出海报

    async onInvite() {
    this.inviteQrCode = ''
    this.inviteCode = ''
    uni.showLoading({ title: '生成邀请...' })
    try {
    var res = await generateFamilyInviteQrCode()
    if (res.code === 200 && res.data) {
      this.inviteCode = res.data.inviteCode
      this.inviteQrCode = res.data.qrCodeBase64
      this.showPoster = true
    } else {
      uni.showToast({ title: res.message || '生成失败', icon: 'none' })
    }
    } catch (e) {
    uni.showToast({ title: '生成失败', icon: 'none' })
    } finally {
    uni.hideLoading()
    }
    },
    
  • [ ] Step 4: 移除 onAddMember 中的 showSettings = false

    onAddMember() {
    uni.navigateTo({ url: '/pages/family/add-member' })
    },
    
  • [ ] Step 5: 保留其他方法不变

保留:loadMembers, buildScoreMap, loadFamilyInfo, showMemberDetail, canEditMember, onSwitchMember, confirmKick, onKickMember, onCopyCode, shareInviteCard, onEditRelation, doQuestionnaire, recordInteraction

移除:onInvite 旧实现(已替换)


Task 3: 重写页面样式

Files:

  • Modify: pages/profile-extra/family-members.vue (style section)

Interfaces:

  • Produces: 温暖家庭风视觉风格

  • [ ] Step 1: 重写全局容器样式

    .container {
    padding: 30rpx 30rpx 140rpx;
    min-height: 100vh;
    background: #F5F0EB;
    }
    
  • [ ] Step 2: 暖橙渐变头部

    .family-header {
    background: linear-gradient(135deg, #F97316, #EA580C);
    border-radius: 20rpx;
    padding: 36rpx 30rpx;
    margin-bottom: 24rpx;
    color: #fff;
    text-align: center;
    }
    .family-header-content { display: flex; align-items: center; justify-content: center; flex-direction: column; }
    .family-header-icon { font-size: 48rpx; margin-bottom: 8rpx; }
    .family-header-text { text-align: center; }
    .family-name { font-size: 36rpx; font-weight: bold; display: block; }
    .family-member-count { font-size: 24rpx; opacity: 0.85; margin-top: 4rpx; display: block; }
    
  • [ ] Step 3: 成员卡片样式

    .member-card {
    display: flex;
    align-items: center;
    background: #fff;
    border-radius: 16rpx;
    padding: 24rpx 28rpx;
    margin-bottom: 16rpx;
    box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
    }
    .member-card:active { background: #FFFCF5; }
    .member-avatar { width: 80rpx; height: 80rpx; border-radius: 50%; margin-right: 20rpx; background: #f0f0f0; flex-shrink: 0; }
    .member-info { flex: 1; min-width: 0; }
    .member-name { font-size: 30rpx; font-weight: 600; display: block; color: #333; }
    .member-role-tag { display: inline-block; font-size: 22rpx; color: #F97316; background: #FFF7ED; padding: 4rpx 14rpx; border-radius: 8rpx; margin-top: 6rpx; }
    .member-arrow { font-size: 36rpx; color: #ccc; margin-left: 12rpx; flex-shrink: 0; }
    
  • [ ] Step 4: 空状态样式

    .empty-state { text-align: center; padding: 100rpx 30rpx; }
    .empty-icon { font-size: 100rpx; display: block; margin-bottom: 20rpx; }
    .empty-title { font-size: 32rpx; color: #666; display: block; margin-bottom: 12rpx; }
    .empty-desc { font-size: 26rpx; color: #999; display: block; margin-bottom: 40rpx; }
    .empty-invite-btn { background: linear-gradient(135deg, #F97316, #EA580C); color: #fff; border: none; border-radius: 40rpx; padding: 20rpx 50rpx; font-size: 28rpx; }
    
  • [ ] Step 5: 底部固定栏样式

    .bottom-bar {
    position: fixed;
    left: 0; right: 0; bottom: 0;
    padding: 16rpx 30rpx calc(16rpx + constant(safe-area-inset-bottom));
    padding: 16rpx 30rpx calc(16rpx + env(safe-area-inset-bottom));
    background: #fff;
    display: flex;
    gap: 20rpx;
    box-shadow: 0 -2rpx 12rpx rgba(0,0,0,0.06);
    z-index: 50;
    }
    .bottom-btn {
    flex: 1;
    height: 80rpx;
    line-height: 80rpx;
    border-radius: 40rpx;
    font-size: 28rpx;
    text-align: center;
    border: none;
    }
    .bottom-btn:active { opacity: 0.85; }
    .btn-add { background: #fff; color: #F97316; border: 2rpx solid #F97316; }
    .btn-invite { background: linear-gradient(135deg, #F97316, #EA580C); color: #fff; }
    
  • [ ] Step 6: 更新成员详情弹窗样式暖色风

将详情弹窗中紫色 #667eea 替换为暖橙 #F97316,保留原有布局和功能按钮样式,将 .action-switch 等按钮颜色微调为暖色调。

  • Step 7: 移除旧样式

移除不再使用的样式:.filter-tabs, .filter-tab, .settings-card, .settings-header, .settings-body, .settings-item, .invite-card, .invite-qr-wrap, .invite-code-section, .invite-actions, .member-quality-scores, .score-item, .member-source-tag, .member-extra


Task 4: 验证

Files:

  • Verify: pages/profile-extra/family-members.vue

  • [ ] Step 1: 编译验证

    cd /app/cfc/cfc-frontend && npm run build:mp-weixin 2>&1 | tail -20
    
  • [ ] Step 2: 最终检查

检查是否满足所有需求:

  • 暖橙渐变头部 ✓
  • 卡片式成员列表(头像 + 昵称 + 关系标签)✓
  • 无筛选标签 ✓
  • 无行内三维评分 ✓
  • 底部固定栏(添加 + 邀请)✓
  • 邀请直接弹出海报 ✓
  • 无齿轮设置弹窗 ✓
  • 无邀请码卡片弹窗 ✓
  • 成员详情弹窗保留 ✓