Kaynağa Gözat

refactor(health): 方案制定页 Step2 替换 ComprehensiveSurvey,旧向导集群标记 DEPRECATED(任务3/3)

iwt 1 hafta önce
ebeveyn
işleme
98450cd73

+ 1 - 0
cfc-frontend/pages/health/components/BasicVitalsStep.vue

@@ -1,3 +1,4 @@
+<!-- DEPRECATED: 已由 ComprehensiveSurvey.vue 替代,待确认无引用后清理(2026-10-16 标记) -->
 <template>
   <view class="vitals-step">
     <view class="step-title-row">

+ 27 - 3
cfc-frontend/pages/health/components/ComprehensiveSurvey.vue

@@ -564,6 +564,16 @@ var LIFESTYLE_KEYS = [
   'sleepDuration', 'bedtime', 'allergySymptoms',
   'mood', 'antibioticHistory'
 ]
+// lifestyle key → 所属调研模块(用于「主动清空」时判断是否允许下发 null)
+var LIFESTYLE_MODULE_MAP = {
+  currentStatus: 'status', menstrualCycle: 'status', specialPeriod: 'status',
+  growthDevelopment: 'status', pickyEating: 'status', osteoporosisRisk: 'status',
+  fallRisk: 'status', screenTime: 'status', teenMood: 'status',
+  bowelFrequency: 'bowel', bowelForm: 'bowel', exerciseFrequency: 'bowel', exerciseDuration: 'bowel',
+  sleepDuration: 'sleep', bedtime: 'sleep',
+  allergySymptoms: 'allergy',
+  mood: '', antibioticHistory: ''
+}
 
 export default {
   name: 'ComprehensiveSurvey',
@@ -585,6 +595,9 @@ export default {
       innerForm: {},
       innerDiet: {},
       lifestyle: {},
+      // 初始快照的疾病/用药数量(用于区分「主动清空」与「原本为空」)
+      _initDiseaseCount: 0,
+      _initMedCount: 0,
       currentMonth: '',
       monthEnd: '',
       expandedGroups: [],
@@ -731,6 +744,8 @@ export default {
         })
       }
       this.lifestyle = obj
+      this._initDiseaseCount = (this.innerForm.selectedDiseases || []).length
+      this._initMedCount = (this.innerForm.medRows || []).length
       // 快捷标签回显
       this.matchQuickLabels()
     },
@@ -836,6 +851,8 @@ export default {
     },
     // 结构化 form 副本(不含 lifestyle;JSON 转换由父页负责)
     formCopy: function() {
+      var diseases = this.innerForm.selectedDiseases || []
+      var meds = this.innerForm.medRows || []
       return {
         heightCm: this.innerForm.heightCm,
         weightKg: this.innerForm.weightKg,
@@ -843,9 +860,12 @@ export default {
         bloodGlucose: this.innerForm.bloodGlucose,
         notes: this.innerForm.notes,
         lipidCholesterol: this.innerForm.lipidCholesterol,
-        selectedDiseases: this.innerForm.selectedDiseases || [],
-        medRows: this.innerForm.medRows || [],
-        noMedication: !!this.innerForm.noMedication
+        selectedDiseases: diseases.slice(),
+        medRows: meds.slice(),
+        noMedication: !!this.innerForm.noMedication,
+        // 原本有数据但被用户删空 → 置位,父页下发 '[]' 落库(否则旧值回滚)
+        diseasesCleared: this._initDiseaseCount > 0 && diseases.length === 0,
+        medsCleared: this._initMedCount > 0 && meds.length === 0
       }
     },
     dietCopy: function() {
@@ -878,6 +898,10 @@ export default {
         if (a === b) return
         if (val instanceof Array) { if (val.length > 0) diff[k] = val.slice() }
         else if (val !== '') diff[k] = val
+        // 主动清空:值被删空且该 key 所属模块已启用 → 下发 null 让父页落库清除(未启用模块不清,保护旧数据)
+        else if (val === '' && orig[k] && self.activeModules.indexOf(LIFESTYLE_MODULE_MAP[k]) >= 0) {
+          diff[k] = null
+        }
       })
       return diff
     },

+ 1 - 0
cfc-frontend/pages/health/components/ConfirmStep.vue

@@ -1,3 +1,4 @@
+<!-- DEPRECATED: 已由 ComprehensiveSurvey.vue 替代,待确认无引用后清理(2026-10-16 标记) -->
 <template>
   <view class="confirm-step">
     <view class="step-title-row">

+ 1 - 0
cfc-frontend/pages/health/components/DietHabitsStep.vue

@@ -1,3 +1,4 @@
+<!-- DEPRECATED: 已由 ComprehensiveSurvey.vue 替代,待确认无引用后清理(2026-10-16 标记) -->
 <template>
   <view class="diet-step">
     <view class="step-title-row">

+ 1 - 0
cfc-frontend/pages/health/components/HealthQuestionnaireWizard.vue

@@ -1,3 +1,4 @@
+<!-- DEPRECATED: 已由 ComprehensiveSurvey.vue 替代,待确认无引用后清理(2026-10-16 标记) -->
 <template>
   <view class="wizard-wrap">
     <!-- 内部步骤指示器(4 步:基础体征/疾病史/饮食习惯/确认) -->

+ 1 - 0
cfc-frontend/pages/health/components/LifestyleStep.vue

@@ -1,3 +1,4 @@
+<!-- DEPRECATED: 已由 ComprehensiveSurvey.vue 替代,待确认无引用后清理(2026-10-16 标记) -->
 <template>
   <view class="lifestyle-step">
     <view class="step-title-row">

+ 1 - 0
cfc-frontend/pages/health/components/MedicalHistoryStep.vue

@@ -1,3 +1,4 @@
+<!-- DEPRECATED: 已由 ComprehensiveSurvey.vue 替代,待确认无引用后清理(2026-10-16 标记) -->
 <template>
   <view class="medical-step">
     <view class="step-title-row">

+ 89 - 45
cfc-frontend/pages/health/health-plan-summary.vue

@@ -51,19 +51,16 @@
         <text class="form-member-save">每个成员独立保存,切换后自动保留</text>
       </view>
 
-      <!-- 分步问卷向导(体征 / 疾病史 / 饮食习惯 / 确认) -->
-      <health-questionnaire-wizard
-        ref="wizard"
-        :member-form-map="memberFormMap"
-        :diet-map="memberDietMap"
-        :member-profile-map="memberProfileMap"
-        :active-form-member-id="activeFormMemberId"
+      <!-- 综合调研(8 模块 step:组件内部管理步骤;多成员 tab 由父页切换传当前成员快照) -->
+      <comprehensive-survey
+        :modules="surveyModules"
+        :form="currentMemberForm"
+        :diet="currentDiet"
+        :profile="currentMemberProfile"
         :submitting="loading"
-        @update-vitals="onWizardVitals"
-        @update-medical="onWizardMedical"
-        @update-lifestyle="onWizardLifestyle"
-        @update-diet="onWizardDiet"
-        @submit="onWizardSubmit"
+        @update-form="onSurveyForm"
+        @update-diet="onSurveyDiet"
+        @submit="onSurveySubmit"
       />
 
       <!-- 底部按钮 -->
@@ -187,10 +184,10 @@
 import { generateHealthPlan, regenerateSection, confirmPlan, saveHealthPlan, getHealthPlanDetail } from '../../utils/api'
 import { getHealthStatus, saveHealthStatus, getDietPreferences, saveDietPreferences, getMyMembership } from '../../utils/api'
 import MemberChipPicker from '@/pages/health/components/MemberChipPicker.vue'
-import HealthQuestionnaireWizard from '@/pages/health/components/HealthQuestionnaireWizard.vue'
+import ComprehensiveSurvey from '@/pages/health/components/ComprehensiveSurvey.vue'
 
 export default {
-  components: { MemberChipPicker, HealthQuestionnaireWizard },
+  components: { MemberChipPicker, ComprehensiveSurvey },
   data() {
     return {
       // 步骤控制
@@ -207,6 +204,8 @@ export default {
       // 多成员饮食习惯:memberId → dietPreferences 对象
       memberDietMap: {},
       activeFormMemberId: '',
+      // Step2 综合调研模块(方案制定前完整调研,全量 8 模块)
+      surveyModules: ['basic', 'status', 'diet', 'disease', 'bowel', 'sleep', 'med', 'allergy'],
       // 方案数据
       planData: {},
       // 多人方案结构化数据(common/individual),保存时原样写入 planJson
@@ -301,6 +300,38 @@ selectedMembers: function() {
         }
       })
       return out
+    },
+    // 当前填写成员的 health 快照(memberFormMap 项:form 子对象 + 结构化兄弟 key)
+    currentMemberSnapshot: function() {
+      return this.memberFormMap[String(this.activeFormMemberId || '__account')] || null
+    },
+    // 组件 form prop:扁平体征 + 结构化字段 + lifestyle 对象(与快照同构的合并视图)
+    currentMemberForm: function() {
+      var s = this.currentMemberSnapshot
+      if (!s) {
+        return {
+          heightCm: '', weightKg: '', bloodPressure: '', bloodGlucose: '', notes: '',
+          lipidCholesterol: '', selectedDiseases: [], medRows: [], noMedication: false,
+          lifestyle: {}
+        }
+      }
+      return Object.assign({}, s.form || {}, {
+        lipidCholesterol: s.lipidCholesterol || '',
+        selectedDiseases: Array.isArray(s.selectedDiseases) ? s.selectedDiseases : [],
+        medRows: Array.isArray(s.medRows) ? s.medRows : [],
+        noMedication: !!s.noMedication,
+        lifestyle: s.lifestyle || {}
+      })
+    },
+    // 当前成员的 diet 快照
+    currentDiet: function() {
+      var key = String(this.activeFormMemberId || '__account')
+      return this.memberDietMap[key] || this.normalizeDiet(null)
+    },
+    // 当前成员画像(复用 memberProfileMap,无数据时兜底空 → 条件渲染区隐藏)
+    currentMemberProfile: function() {
+      var key = String(this.activeFormMemberId || '__account')
+      return this.memberProfileMap[key] || { gender: '', age: '' }
     }
   },
   onLoad: function(options) {
@@ -510,45 +541,52 @@ selectedMembers: function() {
       return list
     },
     // ===== 向导事件:按当前成员写回快照 =====
-    onWizardVitals: function(flat) {
-      this.mergeWizardForm(flat)
-    },
-    onWizardMedical: function(flat) {
-      this.mergeWizardForm(flat)
-    },
-    onWizardLifestyle: function(lifestyle) {
-      var key = String(this.activeFormMemberId || '__account')
-      var prev = this.memberFormMap[key] || {}
-      this.$set(this.memberFormMap, key, Object.assign({}, prev, { lifestyle: lifestyle }))
-    },
-    onWizardDiet: function(diet) {
-      var key = String(this.activeFormMemberId || '__account')
-      this.$set(this.memberDietMap, key, diet)
-    },
-    onWizardSubmit: function() {
-      this.saveAndGenerate()
-    },
-    // 合并向导推送的扁平体征/疾病数据到当前成员快照
-    mergeWizardForm: function(flat) {
-      if (!flat) return
+    // ===== 综合调研事件:patch 合并到当前成员快照 =====
+    // patch 为组件推送的结构化字段副本(扁平体征 + selectedDiseases/medRows/noMedication + lipidCholesterol)
+    onSurveyForm: function(patch) {
+      if (!patch) return
       var key = String(this.activeFormMemberId || '__account')
       var prev = this.memberFormMap[key] || {}
       var pf = prev.form || {}
       this.$set(this.memberFormMap, key, {
         form: {
-          heightCm: flat.heightCm != null ? flat.heightCm : (pf.heightCm || ''),
-          weightKg: flat.weightKg != null ? flat.weightKg : (pf.weightKg || ''),
-          bloodPressure: flat.bloodPressure != null ? flat.bloodPressure : (pf.bloodPressure || ''),
-          bloodGlucose: flat.bloodGlucose != null ? flat.bloodGlucose : (pf.bloodGlucose || ''),
-          notes: flat.notes != null ? flat.notes : (pf.notes || '')
+          heightCm: patch.heightCm != null ? patch.heightCm : (pf.heightCm || ''),
+          weightKg: patch.weightKg != null ? patch.weightKg : (pf.weightKg || ''),
+          bloodPressure: patch.bloodPressure != null ? patch.bloodPressure : (pf.bloodPressure || ''),
+          bloodGlucose: patch.bloodGlucose != null ? patch.bloodGlucose : (pf.bloodGlucose || ''),
+          notes: patch.notes != null ? patch.notes : (pf.notes || '')
         },
-        lipidCholesterol: flat.lipidCholesterol != null ? flat.lipidCholesterol : (prev.lipidCholesterol || ''),
-        selectedDiseases: flat.selectedDiseases != null ? flat.selectedDiseases : (prev.selectedDiseases || []),
-        medRows: flat.medRows != null ? flat.medRows : (prev.medRows || []),
-        noMedication: flat.noMedication != null ? flat.noMedication : (prev.noMedication || false),
+        lipidCholesterol: patch.lipidCholesterol != null ? patch.lipidCholesterol : (prev.lipidCholesterol || ''),
+        selectedDiseases: patch.selectedDiseases != null ? patch.selectedDiseases : (prev.selectedDiseases || []),
+        medRows: patch.medRows != null ? patch.medRows : (prev.medRows || []),
+        noMedication: patch.noMedication != null ? patch.noMedication : (prev.noMedication || false),
+        // 用户主动清空全部疾病/用药时置位,保存时下发 '[]'(否则旧值会被回滚)
+        diseasesCleared: !!patch.diseasesCleared,
+        medsCleared: !!patch.medsCleared,
         lifestyle: prev.lifestyle || {}
       })
     },
+    onSurveyDiet: function(patch) {
+      if (!patch) return
+      var key = String(this.activeFormMemberId || '__account')
+      var prev = this.memberDietMap[key] || this.normalizeDiet(null)
+      this.$set(this.memberDietMap, key, Object.assign({}, prev, patch))
+    },
+    // lifestyle diff 合并进当前成员快照(整体回写,未涉及 key 保留旧值)
+    onSurveyLifestyle: function(diff) {
+      var key = String(this.activeFormMemberId || '__account')
+      var prev = this.memberFormMap[key] || {}
+      var merged = Object.assign({}, prev.lifestyle || {}, diff || {})
+      this.$set(this.memberFormMap, key, Object.assign({}, prev, { lifestyle: merged }))
+    },
+    // 提交:合并 lifestyle diff + form/diet 最终值 → 走原 saveAndGenerate
+    onSurveySubmit: function(payload) {
+      if (!payload) { this.saveAndGenerate(); return }
+      if (payload.lifestyleDiff) this.onSurveyLifestyle(payload.lifestyleDiff)
+      if (payload.form) this.onSurveyForm(payload.form)
+      if (payload.diet) this.onSurveyDiet(payload.diet)
+      this.saveAndGenerate()
+    },
     buildHealthPayload: function(snapshot) {
       var s = snapshot || {}
       var lipids = []
@@ -577,11 +615,14 @@ selectedMembers: function() {
       if (s.form.bloodPressure) payload.bloodPressure = s.form.bloodPressure
       if (s.form.bloodGlucose) payload.bloodGlucose = Number(s.form.bloodGlucose)
       if (lipids.length > 0) payload.bloodLipids = JSON.stringify(lipids)
+      // 用户主动清空时下发 '[]'(未清空且原本为空则不发,保留旧值)
       if (diseases.length > 0) payload.diseaseHistory = JSON.stringify(diseases)
+      else if (s.diseasesCleared) payload.diseaseHistory = '[]'
       if (meds.length > 0) payload.medications = JSON.stringify(meds)
+      else if (s.medsCleared) payload.medications = '[]'
       if (s.form.notes) payload.notes = s.form.notes
       var lifestyle = s.lifestyle || {}
-      var lifestyleKeys = ['mood', 'sleepDuration', 'bedtime', 'bowelFrequency', 'bowelForm', 'antibioticHistory', 'allergySymptoms', 'menstrualCycle', 'specialPeriod', 'growthDevelopment', 'pickyEating', 'osteoporosisRisk', 'fallRisk', 'screenTime', 'teenMood']
+      var lifestyleKeys = ['mood', 'sleepDuration', 'bedtime', 'bowelFrequency', 'bowelForm', 'antibioticHistory', 'allergySymptoms', 'menstrualCycle', 'specialPeriod', 'growthDevelopment', 'pickyEating', 'osteoporosisRisk', 'fallRisk', 'screenTime', 'teenMood', 'currentStatus', 'exerciseFrequency', 'exerciseDuration']
       var hasLifestyle = lifestyleKeys.some(function(k) {
         return lifestyle[k] && (Array.isArray(lifestyle[k]) ? lifestyle[k].length > 0 : true)
       })
@@ -624,6 +665,9 @@ selectedMembers: function() {
           obj.fallRisk = parsed.fallRisk || ''
           obj.screenTime = parsed.screenTime || ''
           obj.teenMood = parsed.teenMood || ''
+          obj.currentStatus = parsed.currentStatus || ''
+          obj.exerciseFrequency = parsed.exerciseFrequency || ''
+          obj.exerciseDuration = parsed.exerciseDuration || ''
         }
       } catch (e) {}
       return obj