Просмотр исходного кода

feat(cfc-frontend): redesign family-members page with gear entry, detail modal, edit permissions

Complete redesign: header now has gear icon entry -> Settings modal (add/invite members), filter-tabs (all/parent/child), member row click -> detail modal with 3D scores display and action buttons (switch/edit/questionnaire/interaction/kick). Edit button only visible for non-login members (source=family_member). Questionnaire and interaction navigation pass memberId.

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
User 2 месяцев назад
Родитель
Сommit
32ba446247
1 измененных файлов с 408 добавлено и 251 удалено
  1. 408 251
      cfc-frontend/pages/profile/family-members.vue

+ 408 - 251
cfc-frontend/pages/profile/family-members.vue

@@ -1,109 +1,101 @@
 <template>
 <template>
   <view class="container">
   <view class="container">
-    <!-- 家庭信息头部 -->
+    <!-- ===== 家庭信息头部 (带齿轮入口) ===== -->
     <view class="family-header" v-if="familyName">
     <view class="family-header" v-if="familyName">
-      <view class="family-header-icon">🏠</view>
-      <text class="family-name">{{ familyName }}</text>
-      <text class="family-member-count">{{ members.length }} 位成员</text>
+      <view class="family-header-content">
+        <view class="family-header-icon">🏠</view>
+        <view class="family-header-text">
+          <text class="family-name">{{ familyName }}</text>
+          <text class="family-member-count">{{ members.length }} 位成员</text>
+        </view>
+        <view class="header-settings-btn" @click="showSettings = true">
+          <text class="settings-gear-icon">⚙️</text>
+        </view>
+      </view>
     </view>
     </view>
 
 
-    <!-- 操作按钮组 -->
-    <view class="action-bar">
-      <view class="action-btn add-btn" @click="onAddMember">
-        <text class="action-icon">+</text>
-        <text class="action-text">添加成员</text>
-      </view>
-      <view class="action-btn invite-btn" @click="onInvite">
-        <text class="action-icon">📨</text>
-        <text class="action-text">邀请成员</text>
-      </view>
+    <!-- ===== 关系标签筛选项 ===== -->
+    <view class="filter-tabs">
+      <view class="filter-tab" :class="{active: filterType === 'all'}" @click="filterType = 'all'">全部</view>
+      <view class="filter-tab" :class="{active: filterType === 'parent'}" @click="filterType = 'parent'">家长</view>
+      <view class="filter-tab" :class="{active: filterType === 'child'}" @click="filterType = 'child'">孩子</view>
     </view>
     </view>
 
 
-    <!-- 家长组 -->
-    <view class="section-title">家长 ({{ parentMembers.length }})</view>
+    <!-- ===== 成员列表(点击行→详情弹窗) ===== -->
     <view class="member-list">
     <view class="member-list">
-      <view class="member-item" v-for="m in parentMembers" :key="m.id">
+      <view class="member-item" v-for="m in filteredMembers" :key="m.id" @click="showMemberDetail(m)">
         <image class="member-avatar" :src="m.avatar || '/static/default-avatar.png'" mode="aspectFill"></image>
         <image class="member-avatar" :src="m.avatar || '/static/default-avatar.png'" mode="aspectFill"></image>
         <view class="member-detail">
         <view class="member-detail">
           <text class="member-name">{{ m.nickname || '未设置' }}</text>
           <text class="member-name">{{ m.nickname || '未设置' }}</text>
           <view class="member-tags">
           <view class="member-tags">
-            <text class="member-role-tag">{{ m.relativeLabel || m.roleLabel || '家长' }}</text>
+            <text class="member-role-tag">{{ m.relativeLabel || m.roleLabel || (m.effectiveRole === 'parent' ? '家长' : '孩子') }}</text>
             <text class="member-source-tag" v-if="m.source === 'family_member'">手动添加</text>
             <text class="member-source-tag" v-if="m.source === 'family_member'">手动添加</text>
+            <text class="member-extra" v-if="m.age"> {{ m.age }}岁</text>
+            <text class="member-extra" v-if="m.gender"> · {{ m.gender === 'male' ? '男' : '女' }}</text>
           </view>
           </view>
           <!-- 三维评分进度条 -->
           <!-- 三维评分进度条 -->
-          <view class="member-quality-scores" v-if="qualityScores[m.id]">
+          <view class="member-quality-scores" v-if="memberScores[m.id]">
             <view class="score-item">
             <view class="score-item">
               <text class="score-label">信任</text>
               <text class="score-label">信任</text>
               <view class="score-bar-bg">
               <view class="score-bar-bg">
-                <view class="score-bar-fill trust" :style="{width: (qualityScores[m.id].trustScore || 0) + '%'}"></view>
+                <view class="score-bar-fill trust" :style="{width: (memberScores[m.id].trustScore || 0) + '%'}"></view>
               </view>
               </view>
-              <text class="score-value">{{ Math.round(qualityScores[m.id].trustScore || 0) }}</text>
+              <text class="score-value">{{ Math.round(memberScores[m.id].trustScore || 0) }}</text>
             </view>
             </view>
             <view class="score-item">
             <view class="score-item">
               <text class="score-label">亲密</text>
               <text class="score-label">亲密</text>
               <view class="score-bar-bg">
               <view class="score-bar-bg">
-                <view class="score-bar-fill intimacy" :style="{width: (qualityScores[m.id].intimacyScore || 0) + '%'}"></view>
+                <view class="score-bar-fill intimacy" :style="{width: (memberScores[m.id].intimacyScore || 0) + '%'}"></view>
               </view>
               </view>
-              <text class="score-value">{{ Math.round(qualityScores[m.id].intimacyScore || 0) }}</text>
+              <text class="score-value">{{ Math.round(memberScores[m.id].intimacyScore || 0) }}</text>
             </view>
             </view>
             <view class="score-item">
             <view class="score-item">
               <text class="score-label">沟通</text>
               <text class="score-label">沟通</text>
               <view class="score-bar-bg">
               <view class="score-bar-bg">
-                <view class="score-bar-fill communication" :style="{width: (qualityScores[m.id].communicationScore || 0) + '%'}"></view>
+                <view class="score-bar-fill communication" :style="{width: (memberScores[m.id].communicationScore || 0) + '%'}"></view>
               </view>
               </view>
-              <text class="score-value">{{ Math.round(qualityScores[m.id].communicationScore || 0) }}</text>
+              <text class="score-value">{{ Math.round(memberScores[m.id].communicationScore || 0) }}</text>
             </view>
             </view>
           </view>
           </view>
         </view>
         </view>
-        <button class="btn-switch" v-if="m.source !== 'parent' || m.id !== userId" @click="onSwitchMember(m)">切换</button>
-        <button class="btn-edit" v-if="m.source === 'family_member'" @click="onEditRelation(m)">编辑</button>
-        <button class="btn-kick" v-if="isAdmin && m.source === 'family_member'" @click="confirmKick(m)">移除</button>
+        <text class="member-arrow">›</text>
       </view>
       </view>
-      <view v-if="parentMembers.length === 0" class="empty-row">
-        <text class="empty-text">暂无家长成员</text>
+      <view v-if="filteredMembers.length === 0" class="empty-row">
+        <text class="empty-text">暂无成员</text>
       </view>
       </view>
     </view>
     </view>
 
 
-    <!-- 孩子组 -->
-    <view class="section-title">
-      孩子 ({{ childMembers.length }})
-    </view>
-    <view class="member-list">
-      <view class="member-item" v-for="m in childMembers" :key="m.id">
-        <view class="member-avatar child-avatar">👶</view>
-        <view class="member-detail">
-          <text class="member-name">{{ m.nickname || '未设置' }}</text>
-          <view class="member-tags">
-            <text class="member-role-tag">{{ m.relativeLabel || m.roleLabel || '孩子' }}</text>
-            <text class="member-source-tag" v-if="m.source === 'family_member'">手动添加</text>
-            <text class="member-extra" v-if="m.age"> {{ m.age }}岁</text>
-            <text class="member-extra" v-if="m.gender"> · {{ m.gender === 'male' ? '男' : '女' }}</text>
+    <!-- ===== Settings 弹窗(齿轮入口) ===== -->
+    <view class="modal-mask" v-if="showSettings" @click="showSettings = false">
+      <view class="settings-card" @click.stop>
+        <view class="settings-header">
+          <text class="settings-title">成员管理</text>
+          <text class="settings-close" @click="showSettings = false">×</text>
+        </view>
+        <view class="settings-body">
+          <view class="settings-item" @click="onAddMember">
+            <text class="settings-item-icon">+</text>
+            <text class="settings-item-text">添加成员</text>
+          </view>
+          <view class="settings-item" @click="onInvite">
+            <text class="settings-item-icon">📨</text>
+            <text class="settings-item-text">邀请成员</text>
           </view>
           </view>
         </view>
         </view>
-        <button class="btn-switch" @click="onSwitchMember(m)">切换</button>
-        <button class="btn-edit" v-if="m.source === 'family_member'" @click="onEditRelation(m)">编辑</button>
-        <button class="btn-kick" v-if="isAdmin && m.source === 'family_member'" @click="confirmKick(m)">移除</button>
-      </view>
-      <view v-if="childMembers.length === 0" class="empty-row">
-        <text class="empty-text">暂无孩子成员</text>
       </view>
       </view>
     </view>
     </view>
 
 
-    <!-- 邀请卡片弹窗 -->
+    <!-- ===== 邀请卡片弹窗 ===== -->
     <view class="modal-mask" v-if="showInviteCard" @click="showInviteCard = false">
     <view class="modal-mask" v-if="showInviteCard" @click="showInviteCard = false">
       <view class="invite-card" @click.stop>
       <view class="invite-card" @click.stop>
         <text class="invite-card-title">邀请家庭成员</text>
         <text class="invite-card-title">邀请家庭成员</text>
         <text class="invite-card-family">{{ familyName }}</text>
         <text class="invite-card-family">{{ familyName }}</text>
-
-        <!-- 二维码 -->
         <view class="invite-qr-wrap" v-if="inviteQrCode">
         <view class="invite-qr-wrap" v-if="inviteQrCode">
           <image class="invite-qr" :src="inviteQrCode" mode="widthFix" />
           <image class="invite-qr" :src="inviteQrCode" mode="widthFix" />
         </view>
         </view>
         <view class="invite-qr-wrap loading-qr" v-else>
         <view class="invite-qr-wrap loading-qr" v-else>
           <text class="loading-text">生成二维码中...</text>
           <text class="loading-text">生成二维码中...</text>
         </view>
         </view>
-
-        <!-- 邀请码 -->
         <view class="invite-code-section">
         <view class="invite-code-section">
           <text class="invite-code-label">邀请码</text>
           <text class="invite-code-label">邀请码</text>
           <view class="invite-code-row">
           <view class="invite-code-row">
@@ -111,28 +103,68 @@
             <button class="copy-btn" @click="onCopyCode">复制</button>
             <button class="copy-btn" @click="onCopyCode">复制</button>
           </view>
           </view>
         </view>
         </view>
-
         <text class="invite-tip">让好友在「我的」页面输入邀请码加入家庭</text>
         <text class="invite-tip">让好友在「我的」页面输入邀请码加入家庭</text>
         <button class="close-btn" @click="showInviteCard = false">关闭</button>
         <button class="close-btn" @click="showInviteCard = false">关闭</button>
       </view>
       </view>
     </view>
     </view>
 
 
-    <!-- 编辑关系弹窗 -->
-    <view class="modal-mask" v-if="showEditRelation" @click="showEditRelation = false">
-      <view class="edit-relation-card" @click.stop>
-        <text class="edit-relation-title">编辑成员关系</text>
-        <view class="edit-relation-member">
-          <text class="edit-relation-name">{{ editTargetMember && editTargetMember.nickname || '' }}</text>
+    <!-- ===== 成员详情弹窗 ===== -->
+    <view class="modal-mask" v-if="showMemberDetailModal" @click="showMemberDetailModal = false">
+      <view class="member-detail-card" @click.stop>
+        <view class="detail-header">
+          <image class="detail-avatar" :src="detailMember && (detailMember.avatar || '/static/default-avatar.png')" mode="aspectFill"></image>
+          <view class="detail-info">
+            <text class="detail-name">{{ detailMember && detailMember.nickname || '' }}</text>
+            <text class="detail-relation">{{ detailMember && (detailMember.relativeLabel || detailMember.roleLabel || '') }}</text>
+          </view>
+          <text class="detail-close" @click="showMemberDetailModal = false">×</text>
         </view>
         </view>
-        <relationship-picker v-model="editRelationType" />
-        <view class="edit-relation-actions">
-          <button class="btn-cancel" @click="showEditRelation = false">取消</button>
-          <button class="btn-save" @click="onSaveRelation">保存</button>
+
+        <!-- 详情:性别/年龄/手机号 -->
+        <view class="detail-meta">
+          <text class="detail-meta-item" v-if="detailMember && detailMember.gender">性别: {{ detailMember.gender === 'male' ? '男' : '女' }}</text>
+          <text class="detail-meta-item" v-if="detailMember && detailMember.age">{{ detailMember.age }}岁</text>
+          <text class="detail-meta-item" v-if="detailMember && detailMember.phone">{{ detailMember.phone }}</text>
+        </view>
+
+        <!-- 三维评分展示 -->
+        <view class="detail-scores">
+          <text class="detail-section-title">关系质量</text>
+          <view class="detail-score-item">
+            <text class="detail-score-label">信任</text>
+            <view class="detail-score-bar-bg">
+              <view class="detail-score-bar-fill trust" :style="{width: (detailMemberScores.trustScore || 0) + '%'}"></view>
+            </view>
+            <text class="detail-score-value">{{ Math.round(detailMemberScores.trustScore || 0) }}</text>
+          </view>
+          <view class="detail-score-item">
+            <text class="detail-score-label">亲密</text>
+            <view class="detail-score-bar-bg">
+              <view class="detail-score-bar-fill intimacy" :style="{width: (detailMemberScores.intimacyScore || 0) + '%'}"></view>
+            </view>
+            <text class="detail-score-value">{{ Math.round(detailMemberScores.intimacyScore || 0) }}</text>
+          </view>
+          <view class="detail-score-item">
+            <text class="detail-score-label">沟通</text>
+            <view class="detail-score-bar-bg">
+              <view class="detail-score-bar-fill communication" :style="{width: (detailMemberScores.communicationScore || 0) + '%'}"></view>
+            </view>
+            <text class="detail-score-value">{{ Math.round(detailMemberScores.communicationScore || 0) }}</text>
+          </view>
+        </view>
+
+        <!-- 操作按钮 -->
+        <view class="detail-actions">
+          <button class="detail-action-btn action-switch" @click="onSwitchMember(detailMember)" v-if="!(detailMember.source === 'parent' && detailMember.userId === userId)">切换身份</button>
+          <button class="detail-action-btn action-edit" @click="onEditRelation(detailMember)" v-if="canEditMember(detailMember)">编辑资料</button>
+          <button class="detail-action-btn action-questionnaire" @click="doQuestionnaire(detailMember)">关系问卷</button>
+          <button class="detail-action-btn action-interaction" @click="recordInteraction(detailMember)">记录互动</button>
+          <button class="detail-action-btn action-kick" @click="confirmKick(detailMember)" v-if="isAdmin && detailMember.source === 'family_member'">移出家庭</button>
         </view>
         </view>
       </view>
       </view>
     </view>
     </view>
 
 
-    <!-- 加载状态 -->
+    <!-- ===== 加载状态 ===== -->
     <view v-if="loading" class="loading-wrap">
     <view v-if="loading" class="loading-wrap">
       <text class="loading-text">加载中...</text>
       <text class="loading-text">加载中...</text>
     </view>
     </view>
@@ -146,16 +178,11 @@ import {
   kickFamilyMemberById,
   kickFamilyMemberById,
   generateFamilyInviteQrCode,
   generateFamilyInviteQrCode,
   getFamilyMembers,
   getFamilyMembers,
-  updateFamilyMember,
-  getRelationshipScores,
-  generateQuestionnaire,
-  addInteraction
+  updateFamilyMember
 } from '../../utils/api.js'
 } from '../../utils/api.js'
 import store from '../../store/index.js'
 import store from '../../store/index.js'
-import RelationshipPicker from '../../components/RelationshipPicker.vue'
 
 
 export default {
 export default {
-  components: { RelationshipPicker },
   data() {
   data() {
     return {
     return {
       userId: 0,
       userId: 0,
@@ -167,22 +194,25 @@ export default {
       members: [],
       members: [],
       isAdmin: false,
       isAdmin: false,
       loading: false,
       loading: false,
+      filterType: 'all',
+      showSettings: false,
       showInviteCard: false,
       showInviteCard: false,
-      showEditRelation: false,
-      editTargetMember: null,
-      editRelationType: '',
-      qualityScores: {}
+      showMemberDetailModal: false,
+      detailMember: null,
+      memberScores: {}
     }
     }
   },
   },
   computed: {
   computed: {
-    parentMembers() {
-      return this.members.filter(function(m) { return m.effectiveRole === 'parent' })
-    },
-    childMembers() {
-      return this.members.filter(function(m) { return m.effectiveRole === 'child' })
+    filteredMembers() {
+      var self = this
+      if (this.filterType === 'all') return this.members
+      return this.members.filter(function(m) {
+        return m.effectiveRole === self.filterType
+      })
     },
     },
-    editableMembers() {
-      return this.members.filter(function(m) { return m.source === 'family_member' })
+    detailMemberScores() {
+      if (!this.detailMember) return {}
+      return this.memberScores[this.detailMember.id] || {}
     }
     }
   },
   },
   onShow() {
   onShow() {
@@ -197,8 +227,7 @@ export default {
         var res = await listFamilyMembers()
         var res = await listFamilyMembers()
         if (res.code === 200 && res.data) {
         if (res.code === 200 && res.data) {
           this.members = res.data || []
           this.members = res.data || []
-          // 从members中提取家庭信息(取第一个parent的familyId等)
-          // 仍需调用旧接口获取家庭名/邀请码/creatorId
+          this.buildScoreMap(res.data)
           this.loadFamilyInfo()
           this.loadFamilyInfo()
         }
         }
       } catch (e) {
       } catch (e) {
@@ -207,6 +236,20 @@ export default {
         this.loading = false
         this.loading = false
       }
       }
     },
     },
+    buildScoreMap(members) {
+      var map = {}
+      if (members && members.length) {
+        for (var i = 0; i < members.length; i++) {
+          var m = members[i]
+          map[m.id] = {
+            trustScore: m.trustScore || 0,
+            intimacyScore: m.intimacyScore || 0,
+            communicationScore: m.communicationScore || 0
+          }
+        }
+      }
+      this.memberScores = map
+    },
     async loadFamilyInfo() {
     async loadFamilyInfo() {
       try {
       try {
         var res = await getFamilyMembers()
         var res = await getFamilyMembers()
@@ -222,11 +265,21 @@ export default {
         // 静默处理
         // 静默处理
       }
       }
     },
     },
+    showMemberDetail(member) {
+      this.detailMember = member
+      this.showMemberDetailModal = true
+    },
+    canEditMember(member) {
+      // 可编辑规则:非登录用户(无userId关联)
+      if (member.userId) return false
+      return member.source === 'family_member'
+    },
     onAddMember() {
     onAddMember() {
+      this.showSettings = false
       uni.navigateTo({ url: '/pages/family/add-member' })
       uni.navigateTo({ url: '/pages/family/add-member' })
     },
     },
-    
     async onSwitchMember(member) {
     async onSwitchMember(member) {
+      this.showMemberDetailModal = false
       try {
       try {
         var res = await switchToFamilyMember(member.id, member.source)
         var res = await switchToFamilyMember(member.id, member.source)
         if (res.code === 200 && res.data) {
         if (res.code === 200 && res.data) {
@@ -258,6 +311,7 @@ export default {
       })
       })
     },
     },
     async onKickMember(member) {
     async onKickMember(member) {
+      this.showMemberDetailModal = false
       try {
       try {
         var res = await kickFamilyMemberById(member.id)
         var res = await kickFamilyMemberById(member.id)
         if (res.code === 200) {
         if (res.code === 200) {
@@ -271,6 +325,7 @@ export default {
       }
       }
     },
     },
     async onInvite() {
     async onInvite() {
+      this.showSettings = false
       this.showInviteCard = true
       this.showInviteCard = true
       this.inviteQrCode = ''
       this.inviteQrCode = ''
       try {
       try {
@@ -294,36 +349,16 @@ export default {
       })
       })
     },
     },
     onEditRelation(member) {
     onEditRelation(member) {
-      this.editTargetMember = member
-      this.editRelationType = member.relationshipType || ''
-      this.showEditRelation = true
+      this.showMemberDetailModal = false
+      uni.navigateTo({ url: '/pages/profile/user-edit?memberId=' + member.id + '&nickname=' + encodeURIComponent(member.nickname || '') })
     },
     },
     doQuestionnaire(member) {
     doQuestionnaire(member) {
+      this.showMemberDetailModal = false
       uni.navigateTo({ url: '/pages/action/relationship-questionnaire?memberId=' + member.id })
       uni.navigateTo({ url: '/pages/action/relationship-questionnaire?memberId=' + member.id })
     },
     },
     recordInteraction(member) {
     recordInteraction(member) {
+      this.showMemberDetailModal = false
       uni.navigateTo({ url: '/pages/action/interaction-log?memberId=' + member.id })
       uni.navigateTo({ url: '/pages/action/interaction-log?memberId=' + member.id })
-    },
-    async onSaveRelation() {
-      if (!this.editTargetMember || !this.editRelationType) {
-        uni.showToast({ title: '请选择关系类型', icon: 'none' })
-        return
-      }
-      try {
-        var res = await updateFamilyMember({
-          memberId: this.editTargetMember.id,
-          relationshipType: this.editRelationType
-        })
-        if (res.code === 200) {
-          uni.showToast({ title: '修改成功', icon: 'success' })
-          this.showEditRelation = false
-          this.loadMembers()
-        } else {
-          uni.showToast({ title: res.message || '修改失败', icon: 'none' })
-        }
-      } catch (e) {
-        uni.showToast({ title: '修改失败', icon: 'none' })
-      }
     }
     }
   }
   }
 }
 }
@@ -336,72 +371,76 @@ export default {
   background: #f5f7fa;
   background: #f5f7fa;
 }
 }
 
 
-/* 家庭信息头部 */
+/* ===== 家庭信息头部(带齿轮入口) ===== */
 .family-header {
 .family-header {
   background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
   background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
   border-radius: 20rpx;
   border-radius: 20rpx;
-  padding: 40rpx 30rpx;
-  margin-bottom: 24rpx;
-  text-align: center;
+  padding: 30rpx 30rpx;
+  margin-bottom: 20rpx;
   color: #fff;
   color: #fff;
 }
 }
+.family-header-content {
+  display: flex;
+  align-items: center;
+}
 .family-header-icon {
 .family-header-icon {
-  font-size: 56rpx;
-  margin-bottom: 12rpx;
+  font-size: 48rpx;
+  margin-right: 16rpx;
+}
+.family-header-text {
+  flex: 1;
 }
 }
 .family-name {
 .family-name {
-  font-size: 36rpx;
+  font-size: 34rpx;
   font-weight: bold;
   font-weight: bold;
   display: block;
   display: block;
 }
 }
 .family-member-count {
 .family-member-count {
-  font-size: 26rpx;
+  font-size: 24rpx;
   opacity: 0.85;
   opacity: 0.85;
-  margin-top: 8rpx;
+  margin-top: 4rpx;
   display: block;
   display: block;
 }
 }
-
-/* 操作按钮栏 */
-.action-bar {
-  display: flex;
-  gap: 20rpx;
-  margin-bottom: 24rpx;
-}
-.action-btn {
-  flex: 1;
+.header-settings-btn {
+  width: 64rpx;
+  height: 64rpx;
   display: flex;
   display: flex;
   align-items: center;
   align-items: center;
   justify-content: center;
   justify-content: center;
-  background: #fff;
-  border-radius: 16rpx;
-  padding: 24rpx 0;
-  box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
-}
-.action-btn:active {
-  opacity: 0.8;
+  border-radius: 50%;
+  background: rgba(255,255,255,0.2);
 }
 }
-.action-icon {
-  font-size: 32rpx;
-  margin-right: 8rpx;
+.header-settings-btn:active {
+  background: rgba(255,255,255,0.35);
 }
 }
-.action-text {
-  font-size: 28rpx;
-  font-weight: 500;
-  color: #333;
+.settings-gear-icon {
+  font-size: 36rpx;
 }
 }
 
 
-/* 分区标题 */
-.section-title {
-  font-size: 30rpx;
-  font-weight: bold;
-  margin: 20rpx 0 16rpx;
-  padding-left: 10rpx;
+/* ===== 筛选标签 ===== */
+.filter-tabs {
   display: flex;
   display: flex;
-  align-items: center;
-  justify-content: space-between;
+  gap: 12rpx;
+  margin-bottom: 20rpx;
+}
+.filter-tab {
+  padding: 8rpx 28rpx;
+  background: #fff;
+  border-radius: 30rpx;
+  font-size: 26rpx;
+  color: #666;
+  box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04);
+}
+.filter-tab.active {
+  background: #667eea;
+  color: #fff;
+  font-weight: 500;
+}
+.filter-tab:active {
+  opacity: 0.8;
 }
 }
 
 
-/* 成员列表 */
+/* ===== 成员列表 ===== */
 .member-list {
 .member-list {
   background: #fff;
   background: #fff;
   border-radius: 16rpx;
   border-radius: 16rpx;
@@ -417,6 +456,9 @@ export default {
 .member-item:last-child {
 .member-item:last-child {
   border-bottom: none;
   border-bottom: none;
 }
 }
+.member-item:active {
+  background: #f8f8ff;
+}
 .member-avatar {
 .member-avatar {
   width: 72rpx;
   width: 72rpx;
   height: 72rpx;
   height: 72rpx;
@@ -425,12 +467,6 @@ export default {
   background: #f0f0f0;
   background: #f0f0f0;
   flex-shrink: 0;
   flex-shrink: 0;
 }
 }
-.child-avatar {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  font-size: 40rpx;
-}
 .member-detail {
 .member-detail {
   flex: 1;
   flex: 1;
   min-width: 0;
   min-width: 0;
@@ -467,25 +503,9 @@ export default {
   margin-top: 4rpx;
   margin-top: 4rpx;
   display: inline;
   display: inline;
 }
 }
-.btn-switch {
-  font-size: 24rpx;
-  color: #0EA5E9;
-  background: #f0f9ff;
-  border: none;
-  padding: 8rpx 20rpx;
-  border-radius: 8rpx;
-  line-height: 1.5;
-  margin-left: 12rpx;
-  flex-shrink: 0;
-}
-.btn-kick {
-  font-size: 24rpx;
-  color: #f56c6c;
-  background: #fef0f0;
-  border: none;
-  padding: 8rpx 20rpx;
-  border-radius: 8rpx;
-  line-height: 1.5;
+.member-arrow {
+  font-size: 36rpx;
+  color: #ccc;
   margin-left: 12rpx;
   margin-left: 12rpx;
   flex-shrink: 0;
   flex-shrink: 0;
 }
 }
@@ -498,7 +518,45 @@ export default {
   color: #bbb;
   color: #bbb;
 }
 }
 
 
-/* 弹窗遮罩 */
+/* ===== 三维评分进度条(列表行内) ===== */
+.member-quality-scores {
+  display: flex;
+  flex-direction: column;
+  gap: 6rpx;
+  padding: 8rpx 0;
+}
+.score-item {
+  display: flex;
+  align-items: center;
+  gap: 8rpx;
+}
+.score-label {
+  width: 56rpx;
+  font-size: 20rpx;
+  color: #999;
+}
+.score-bar-bg {
+  flex: 1;
+  height: 10rpx;
+  background: #f0f0f0;
+  border-radius: 5rpx;
+  overflow: hidden;
+}
+.score-bar-fill {
+  height: 100%;
+  border-radius: 5rpx;
+}
+.score-bar-fill.trust { background: #10B981; }
+.score-bar-fill.intimacy { background: #FF6B9D; }
+.score-bar-fill.communication { background: #6366F1; }
+.score-value {
+  width: 44rpx;
+  font-size: 18rpx;
+  color: #999;
+  text-align: right;
+}
+
+/* ===== 弹窗遮罩 ===== */
 .modal-mask {
 .modal-mask {
   position: fixed;
   position: fixed;
   top: 0;
   top: 0;
@@ -512,7 +570,53 @@ export default {
   z-index: 100;
   z-index: 100;
 }
 }
 
 
-/* 邀请卡片 */
+/* ===== Settings 弹窗 ===== */
+.settings-card {
+  background: #fff;
+  border-radius: 24rpx;
+  width: 500rpx;
+  overflow: hidden;
+}
+.settings-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 30rpx 30rpx 0;
+}
+.settings-title {
+  font-size: 32rpx;
+  font-weight: bold;
+}
+.settings-close {
+  font-size: 44rpx;
+  color: #999;
+  padding: 4rpx 8rpx;
+}
+.settings-body {
+  padding: 20rpx 30rpx 30rpx;
+}
+.settings-item {
+  display: flex;
+  align-items: center;
+  padding: 22rpx 16rpx;
+  border-radius: 12rpx;
+  margin-bottom: 8rpx;
+}
+.settings-item:active {
+  background: #f5f5f5;
+}
+.settings-item-icon {
+  font-size: 32rpx;
+  margin-right: 16rpx;
+  width: 40rpx;
+  text-align: center;
+}
+.settings-item-text {
+  font-size: 28rpx;
+  color: #333;
+}
+
+/* ===== 邀请卡片 ===== */
 .invite-card {
 .invite-card {
   background: #fff;
   background: #fff;
   border-radius: 24rpx;
   border-radius: 24rpx;
@@ -593,105 +697,158 @@ export default {
   color: #666;
   color: #666;
 }
 }
 
 
-/* 加载 */
-.loading-wrap {
-  text-align: center;
-  padding: 60rpx 0;
+/* ===== 成员详情弹窗 ===== */
+.member-detail-card {
+  background: #fff;
+  border-radius: 24rpx;
+  width: 600rpx;
+  max-height: 80vh;
+  overflow-y: auto;
+  padding: 36rpx 32rpx 32rpx;
 }
 }
-.loading-text {
-  font-size: 28rpx;
-  color: #999;
+.detail-header {
+  display: flex;
+  align-items: center;
+  position: relative;
+  margin-bottom: 20rpx;
 }
 }
-.btn-edit {
-  font-size: 24rpx;
-  color: #67c23a;
-  background: #f0f9eb;
-  border: none;
-  padding: 8rpx 20rpx;
-  border-radius: 8rpx;
-  line-height: 1.5;
-  margin-left: 12rpx;
+.detail-avatar {
+  width: 100rpx;
+  height: 100rpx;
+  border-radius: 50%;
+  margin-right: 20rpx;
+  background: #f0f0f0;
   flex-shrink: 0;
   flex-shrink: 0;
 }
 }
-.edit-relation-card {
-  background: #fff;
-  border-radius: 24rpx;
-  padding: 40rpx;
-  width: 580rpx;
+.detail-info {
+  flex: 1;
 }
 }
-.edit-relation-title {
-  font-size: 32rpx;
+.detail-name {
+  font-size: 34rpx;
   font-weight: bold;
   font-weight: bold;
   display: block;
   display: block;
-  text-align: center;
-  margin-bottom: 20rpx;
 }
 }
-.edit-relation-member {
-  text-align: center;
-  margin-bottom: 24rpx;
+.detail-relation {
+  font-size: 26rpx;
+  color: #667eea;
+  margin-top: 4rpx;
+  display: block;
 }
 }
-.edit-relation-name {
-  font-size: 28rpx;
-  color: #333;
+.detail-close {
+  font-size: 44rpx;
+  color: #999;
+  padding: 4rpx 8rpx;
+  position: absolute;
+  top: -12rpx;
+  right: -8rpx;
 }
 }
-.edit-relation-actions {
+.detail-meta {
   display: flex;
   display: flex;
-  gap: 20rpx;
-  margin-top: 30rpx;
+  gap: 16rpx;
+  flex-wrap: wrap;
+  margin-bottom: 20rpx;
+  padding: 16rpx 0;
+  border-bottom: 1rpx solid #f0f0f0;
 }
 }
-.btn-cancel {
-  flex: 1;
-  background: #f5f5f5;
-  border: none;
-  border-radius: 12rpx;
-  padding: 18rpx 0;
-  font-size: 28rpx;
-  color: #666;
+.detail-meta-item {
+  font-size: 24rpx;
+  color: #888;
 }
 }
-.btn-save {
-  flex: 1;
-  background: #F97316;
-  border: none;
-  border-radius: 12rpx;
-  padding: 18rpx 0;
-  font-size: 28rpx;
-  color: #fff;
+
+/* 详情三维评分 */
+.detail-scores {
+  margin-bottom: 24rpx;
 }
 }
-/* 三维评分进度条 */
-.member-quality-scores {
-  display: flex;
-  flex-direction: column;
-  gap: 6rpx;
-  padding: 8rpx 0;
+.detail-section-title {
+  font-size: 28rpx;
+  font-weight: 600;
+  display: block;
+  margin-bottom: 12rpx;
+  padding-left: 12rpx;
+  border-left: 4rpx solid #667eea;
 }
 }
-.score-item {
+.detail-score-item {
   display: flex;
   display: flex;
   align-items: center;
   align-items: center;
-  gap: 8rpx;
+  gap: 10rpx;
+  margin-bottom: 8rpx;
 }
 }
-.score-label {
+.detail-score-label {
   width: 56rpx;
   width: 56rpx;
-  font-size: 20rpx;
-  color: #999;
+  font-size: 22rpx;
+  color: #888;
 }
 }
-.score-bar-bg {
+.detail-score-bar-bg {
   flex: 1;
   flex: 1;
-  height: 10rpx;
+  height: 12rpx;
   background: #f0f0f0;
   background: #f0f0f0;
-  border-radius: 5rpx;
+  border-radius: 6rpx;
   overflow: hidden;
   overflow: hidden;
 }
 }
-.score-bar-fill {
+.detail-score-bar-fill {
   height: 100%;
   height: 100%;
-  border-radius: 5rpx;
+  border-radius: 6rpx;
+  transition: width 0.3s ease;
 }
 }
-.score-bar-fill.trust { background: #10B981; }
-.score-bar-fill.intimacy { background: #FF6B9D; }
-.score-bar-fill.communication { background: #6366F1; }
-.score-value {
+.detail-score-bar-fill.trust { background: #10B981; }
+.detail-score-bar-fill.intimacy { background: #FF6B9D; }
+.detail-score-bar-fill.communication { background: #6366F1; }
+.detail-score-value {
   width: 44rpx;
   width: 44rpx;
-  font-size: 18rpx;
-  color: #999;
+  font-size: 22rpx;
+  font-weight: 600;
+  color: #555;
   text-align: right;
   text-align: right;
 }
 }
+
+/* ===== 操作按钮区(2列flex网格) ===== */
+.detail-actions {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 16rpx;
+  padding-top: 8rpx;
+  border-top: 1rpx solid #f0f0f0;
+}
+.detail-action-btn {
+  width: calc(50% - 8rpx);
+  border: none;
+  border-radius: 12rpx;
+  padding: 20rpx 0;
+  font-size: 26rpx;
+  text-align: center;
+  line-height: 1.4;
+}
+.detail-action-btn:active {
+  opacity: 0.8;
+}
+.action-switch {
+  background: #f0f9ff;
+  color: #0EA5E9;
+}
+.action-edit {
+  background: #f0f9eb;
+  color: #67c23a;
+}
+.action-questionnaire {
+  background: #f3e8ff;
+  color: #8b5cf6;
+}
+.action-interaction {
+  background: #fff7ed;
+  color: #F97316;
+}
+.action-kick {
+  background: #fef0f0;
+  color: #f56c6c;
+}
+
+/* ===== 加载 ===== */
+.loading-wrap {
+  text-align: center;
+  padding: 60rpx 0;
+}
+.loading-text {
+  font-size: 28rpx;
+  color: #999;
+}
 </style>
 </style>