Browse Source

feat: report-confirm 被检测人选择整合进基本信息卡片(内联面板+移除独立卡片)

asus 1 month ago
parent
commit
ed99ddc7eb
1 changed files with 128 additions and 86 deletions
  1. 128 86
      cfc-frontend/pages/health/report-confirm.vue

+ 128 - 86
cfc-frontend/pages/health/report-confirm.vue

@@ -27,10 +27,83 @@
       <!-- 基本信息 -->
       <view class="card">
         <view class="card-title">基本信息</view>
-        <view class="field-row">
+        <view class="field-row member-select-row" @click="toggleMemberPanel">
           <text class="field-label">被检测人</text>
-          <text class="field-value" v-if="!needBind">{{ extractedName || '未识别' }}</text>
-          <text class="field-value binding-required" v-else>需手动绑定</text>
+          <text class="field-value" :class="{ 'binding-required': needBind && !selectedMemberObj }">{{ memberDisplayText }}</text>
+          <text class="member-select-arrow">{{ selectedMemberObj ? '修改 ›' : '选择 ›' }}</text>
+        </view>
+
+        <!-- 被检测人选择面板(内联展开) -->
+        <view class="member-panel" v-if="showMemberPanel">
+          <!-- 候选匹配列表(按置信度排序) -->
+          <view class="candidate-section" v-if="candidates.length > 0">
+            <text class="candidate-section-hint">以下成员与报告信息匹配,请选择:</text>
+            <view
+              class="candidate-item"
+              v-for="(cand, idx) in candidates"
+              :key="idx"
+              :class="{ 'candidate-selected': selectedMemberId === (cand.id || cand.userId) }"
+              @click="selectAndClose(cand)"
+            >
+              <view class="candidate-left">
+                <text class="candidate-avatar">{{ (cand.nickname || cand.name || '?').substring(0, 1) }}</text>
+                <view class="candidate-info">
+                  <text class="candidate-name">{{ cand.nickname || cand.name || '未知' }}</text>
+                  <text class="candidate-detail" v-if="cand.gender || cand.age">{{ genderText(cand.gender) }}{{ cand.age ? ' · ' + cand.age + '岁' : '' }}</text>
+                  <text class="candidate-fields" v-if="cand.matchFields">匹配: {{ cand.matchFields }}</text>
+                </view>
+              </view>
+              <view class="candidate-right">
+                <view class="score-bar-track">
+                  <view class="score-bar-fill" :style="'width:' + (cand.similarityScore * 100) + '%'"></view>
+                </view>
+                <text class="score-text">{{ Math.round(cand.similarityScore * 100) }}%</text>
+              </view>
+            </view>
+          </view>
+
+          <!-- 全量家庭成员列表(后备) -->
+          <view class="member-list" v-if="familyMembers.length > 0 && (candidates.length === 0 || !showCandidatesOnly)">
+            <view
+              class="member-item"
+              v-for="(member, mIdx) in familyMembers"
+              :key="mIdx"
+              :class="{ 'member-selected': selectedMemberId === (member.userId || member.id) }"
+              @click="selectAndClose(member)"
+            >
+              <text class="member-avatar">{{ (member.nickname || '?').substring(0, 1) }}</text>
+              <text class="member-name">{{ member.nickname || '未知' }}</text>
+            </view>
+          </view>
+
+          <!-- 新建成员入口 -->
+          <view class="create-member-entry" @click="toggleAutoCreate">
+            <text class="create-member-icon">+</text>
+            <text class="create-member-text">{{ showAutoCreateForm ? '收起新建表单' : '不在列表中?新建家庭成员' }}</text>
+          </view>
+
+          <!-- 新建成员表单(面板内嵌) -->
+          <view class="auto-create-inline" v-if="showAutoCreateForm">
+            <view class="form-item">
+              <text class="form-item-label">姓名</text>
+              <input class="form-item-input" v-model="newMember.name" placeholder="请输入姓名" />
+            </view>
+            <view class="form-item">
+              <text class="form-item-label">性别</text>
+              <view class="gender-selector">
+                <text class="gender-option" :class="{ 'gender-selected': newMember.gender === 'male' }" @click="newMember.gender = 'male'">男</text>
+                <text class="gender-option" :class="{ 'gender-selected': newMember.gender === 'female' }" @click="newMember.gender = 'female'">女</text>
+              </view>
+            </view>
+            <view class="form-item">
+              <text class="form-item-label">年龄</text>
+              <input class="form-item-input" type="number" v-model="newMember.age" placeholder="请输入年龄" />
+            </view>
+            <view class="btn-create-submit" :class="{ 'btn-disabled': creatingMember }" @click="doAutoCreate">
+              <text v-if="creatingMember">创建中...</text>
+              <text v-else>确认创建</text>
+            </view>
+          </view>
         </view>
         <view class="field-row" v-if="extractedGender">
           <text class="field-label">性别</text>
@@ -54,82 +127,6 @@
         </view>
       </view>
 
-      <!-- 家庭成员绑定(含候选推荐) -->
-      <view class="card" v-if="needBind">
-        <view class="card-title">选择家庭成员</view>
-
-        <!-- 候选匹配列表(按置信度排序) -->
-        <view class="candidate-section" v-if="candidates.length > 0">
-          <text class="candidate-section-hint">以下成员与报告信息匹配,请选择:</text>
-          <view
-            class="candidate-item"
-            v-for="(cand, idx) in candidates"
-            :key="idx"
-            :class="{ 'candidate-selected': selectedMemberId === (cand.id || cand.userId) }"
-            @click="selectCandidate(cand)"
-          >
-            <view class="candidate-left">
-              <text class="candidate-avatar">{{ (cand.nickname || cand.name || '?').substring(0, 1) }}</text>
-              <view class="candidate-info">
-                <text class="candidate-name">{{ cand.nickname || cand.name || '未知' }}</text>
-                <text class="candidate-detail" v-if="cand.gender || cand.age">{{ genderText(cand.gender) }}{{ cand.age ? ' · ' + cand.age + '岁' : '' }}</text>
-                <text class="candidate-fields" v-if="cand.matchFields">匹配: {{ cand.matchFields }}</text>
-              </view>
-            </view>
-            <view class="candidate-right">
-              <view class="score-bar-track">
-                <view class="score-bar-fill" :style="'width:' + (cand.similarityScore * 100) + '%'"></view>
-              </view>
-              <text class="score-text">{{ Math.round(cand.similarityScore * 100) }}%</text>
-            </view>
-          </view>
-        </view>
-
-        <!-- 全量家庭成员列表(后备) -->
-        <view class="member-list" v-if="familyMembers.length > 0 && (candidates.length === 0 || !showCandidatesOnly)">
-          <view
-            class="member-item"
-            v-for="(member, mIdx) in familyMembers"
-            :key="mIdx"
-            :class="{ 'member-selected': selectedMemberId === (member.userId || member.id) }"
-            @click="selectMember(member)"
-          >
-            <text class="member-avatar">{{ (member.nickname || '?').substring(0, 1) }}</text>
-            <text class="member-name">{{ member.nickname || '未知' }}</text>
-          </view>
-        </view>
-
-        <!-- 新建成员入口 -->
-        <view class="create-member-entry" @click="toggleAutoCreate">
-          <text class="create-member-icon">+</text>
-          <text class="create-member-text">{{ showAutoCreateForm ? '收起新建表单' : '不在列表中?新建家庭成员' }}</text>
-        </view>
-      </view>
-
-      <!-- 新建家庭成员表单 -->
-      <view class="card auto-create-card" v-if="showAutoCreateForm">
-        <view class="card-title">新建家庭成员</view>
-        <view class="form-item">
-          <text class="form-item-label">姓名</text>
-          <input class="form-item-input" v-model="newMember.name" placeholder="请输入姓名" />
-        </view>
-        <view class="form-item">
-          <text class="form-item-label">性别</text>
-          <view class="gender-selector">
-            <text class="gender-option" :class="{ 'gender-selected': newMember.gender === 'male' }" @click="newMember.gender = 'male'">男</text>
-            <text class="gender-option" :class="{ 'gender-selected': newMember.gender === 'female' }" @click="newMember.gender = 'female'">女</text>
-          </view>
-        </view>
-        <view class="form-item">
-          <text class="form-item-label">年龄</text>
-          <input class="form-item-input" type="number" v-model="newMember.age" placeholder="请输入年龄" />
-        </view>
-        <view class="btn-create-submit" :class="{ 'btn-disabled': creatingMember }" @click="doAutoCreate">
-          <text v-if="creatingMember">创建中...</text>
-          <text v-else>确认创建</text>
-        </view>
-      </view>
-
       <!-- 舌诊指标(只读展示) -->
       <view class="card" v-if="payload.type === 'tongue' && payload.indicators && payload.indicators.length">
         <view class="card-title">舌象指标</view>
@@ -306,6 +303,9 @@ export default {
       matchLabel: '',
       candidates: [],
       showCandidatesOnly: false,
+      // 被检测人选择面板
+      showMemberPanel: false,
+      selectedMemberObj: null,
       // 新建成员
       showAutoCreateForm: false,
       newMember: {
@@ -350,6 +350,12 @@ export default {
       if (this.matchLabel === 'no_match') return '无法匹配到现有家庭成员,可新建成员'
       return ''
     },
+    memberDisplayText() {
+      var m = this.selectedMemberObj
+      if (m && (m.nickname || m.name)) return m.nickname || m.name
+      if (this.needBind) return '需手动绑定'
+      return this.extractedName || '未识别'
+    },
     // 指标按类型(category)分组,保持解析顺序,统计需处理(非正常)数量
     indicatorGroups() {
       var groups = []
@@ -429,8 +435,10 @@ export default {
       this.showCandidatesOnly = true
     }
 
+    // 始终加载家庭成员(面板始终可点击,needBind=false 也允许改选)
+    this.loadFamilyMembers()
+
     if (this.needBind) {
-      this.loadFamilyMembers()
       // 高置信度且已匹配 → 直接选中
       if (this.matchedMemberId && this.matchConfidence >= 0.8) {
         this.selectedMemberId = this.matchedMemberId
@@ -450,13 +458,41 @@ export default {
           return { id: c.id, nickname: c.nickname, gender: c.gender, age: c.age, role: 'child' }
         })
         self.familyMembers = parents.concat(children)
+        self.syncSelectedMemberObj()
       }).catch(function(e) {
         console.log('加载家庭成员失败', e)
         self.familyMembers = []
       })
     },
-    selectMember(member) {
-      this.selectedMemberId = member.userId || member.id
+    toggleMemberPanel() {
+      this.showMemberPanel = !this.showMemberPanel
+      if (!this.showMemberPanel) {
+        this.showAutoCreateForm = false
+      }
+    },
+    selectAndClose(member) {
+      var memberId = member.id || member.userId
+      this.selectedMemberId = memberId
+      this.selectedMemberObj = member
+      this.extractedName = member.nickname || member.name || this.extractedName
+      this.extractedGender = member.gender || this.extractedGender
+      this.extractedAge = member.age || this.extractedAge
+      this.showAutoCreateForm = false
+      this.showMemberPanel = false
+    },
+    syncSelectedMemberObj() {
+      if (!this.selectedMemberId) {
+        this.selectedMemberObj = null
+        return
+      }
+      var all = this.familyMembers.concat(this.candidates)
+      for (var i = 0; i < all.length; i++) {
+        var m = all[i]
+        if ((m.id || m.userId) === this.selectedMemberId) {
+          this.selectedMemberObj = m
+          return
+        }
+      }
     },
     // === Phase 2: 候选匹配与新建成员 ===
     genderText(gender) {
@@ -464,10 +500,6 @@ export default {
       if (gender === 'female') return '女'
       return gender || ''
     },
-    selectCandidate(candidate) {
-      this.selectedMemberId = candidate.id || candidate.userId
-      this.showAutoCreateForm = false
-    },
     toggleAutoCreate() {
       this.showAutoCreateForm = !this.showAutoCreateForm
       if (this.showAutoCreateForm) {
@@ -497,7 +529,17 @@ export default {
           if (memberId) {
             self.createdMemberId = memberId
             self.selectedMemberId = memberId
+            self.selectedMemberObj = {
+              id: memberId,
+              nickname: self.newMember.name,
+              gender: self.newMember.gender,
+              age: self.newMember.age
+            }
+            self.extractedName = self.newMember.name
+            self.extractedGender = self.newMember.gender
+            self.extractedAge = self.newMember.age
             self.showAutoCreateForm = false
+            self.showMemberPanel = false
             uni.showToast({ title: '创建成功', icon: 'success' })
           }
         } else {