| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380 |
- <!-- DEPRECATED: 已由 ComprehensiveSurvey.vue 替代,待确认无引用后清理(2026-10-16 标记) -->
- <template>
- <view class="wizard-wrap">
- <!-- 内部步骤指示器(4 步:基础体征/疾病史/饮食习惯/确认) -->
- <view class="wizard-steps">
- <view
- v-for="(s, i) in wizardSteps"
- :key="i"
- class="wizard-step"
- :class="{ 'wizard-step-active': currentStep === i + 1, 'wizard-step-done': currentStep > i + 1 }"
- @click="goToStep(i + 1)"
- >
- <text class="wizard-step-num">{{ i + 1 }}</text>
- <text class="wizard-step-label">{{ s }}</text>
- </view>
- </view>
- <!-- 步骤内容 -->
- <view class="wizard-body">
- <!-- Step 1: 基础体征 -->
- <view v-if="currentStep === 1">
- <basic-vitals-step
- ref="stepRef"
- :value="currentMemberForm"
- @input="onVitalsInput"
- />
- </view>
- <!-- Step 2: 疾病史与用药 -->
- <view v-if="currentStep === 2">
- <medical-history-step
- ref="stepRef"
- :value="currentMemberForm"
- @input="onMedicalInput"
- />
- </view>
- <!-- Step 3: 生活方式与肠道健康 -->
- <view v-if="currentStep === 3">
- <lifestyle-step
- ref="stepRef"
- :value="currentMemberForm"
- :profile="currentMemberProfile"
- @input="onLifestyleInput"
- />
- </view>
- <!-- Step 4: 饮食习惯 -->
- <view v-if="currentStep === 4">
- <diet-habits-step
- ref="stepRef"
- :value="currentMemberDiet"
- @input="onDietInput"
- @skip-no-preference="onDietSkipNoPreference"
- />
- </view>
- <!-- Step 5: 确认 -->
- <view v-if="currentStep === 5">
- <confirm-step
- ref="stepRef"
- :health-status="currentMemberForm"
- :diet-preferences="currentMemberDiet"
- :submitting="submitting"
- @submit="handleSubmit"
- @back="prev"
- />
- </view>
- </view>
- <!-- 底部导航:上一步 / 下一步(最后一步为保存并生成) -->
- <view class="wizard-footer" v-if="currentStep < 5">
- <button class="btn-prev" :disabled="currentStep === 1" @click="prev">
- <text>上一步</text>
- </button>
- <button class="btn-next" :disabled="validating" @click="next">
- <text>{{ validating ? '校验中...' : '下一步' }}</text>
- </button>
- </view>
- </view>
- </template>
- <script>
- import BasicVitalsStep from './BasicVitalsStep.vue'
- import MedicalHistoryStep from './MedicalHistoryStep.vue'
- import LifestyleStep from './LifestyleStep.vue'
- import DietHabitsStep from './DietHabitsStep.vue'
- import ConfirmStep from './ConfirmStep.vue'
- export default {
- name: 'HealthQuestionnaireWizard',
- components: {
- BasicVitalsStep,
- MedicalHistoryStep,
- LifestyleStep,
- DietHabitsStep,
- ConfirmStep
- },
- props: {
- // 成员表单快照:memberId → { form, lipidCholesterol, selectedDiseases, medRows }
- memberFormMap: {
- type: Object,
- default: function() { return {} }
- },
- // 成员饮食习惯快照:memberId → dietPreferences 对象
- dietMap: {
- type: Object,
- default: function() { return {} }
- },
- // 成员基本信息:memberId → { gender, age }(问卷按 profile 动态调整问题)
- memberProfileMap: {
- type: Object,
- default: function() { return {} }
- },
- // 当前填写成员
- activeFormMemberId: {
- type: String,
- default: ''
- },
- // 提交中(由父组件 loading 驱动,失败后按钮可恢复)
- submitting: {
- type: Boolean,
- default: false
- }
- },
- data() {
- return {
- currentStep: 1,
- validating: false
- }
- },
- computed: {
- wizardSteps: function() {
- return ['基础体征', '疾病史', '生活方式', '饮食习惯', '确认']
- },
- // 当前成员的健康表单(来自父组件快照)
- currentMemberForm: function() {
- var map = this.memberFormMap || {}
- var key = this.activeFormMemberId || '__account'
- var snapshot = map[key] || {}
- return {
- heightCm: snapshot.form && snapshot.form.heightCm || '',
- weightKg: snapshot.form && snapshot.form.weightKg || '',
- bloodPressure: snapshot.form && snapshot.form.bloodPressure || '',
- bloodGlucose: snapshot.form && snapshot.form.bloodGlucose || '',
- notes: snapshot.form && snapshot.form.notes || '',
- lipidCholesterol: snapshot.lipidCholesterol || '',
- selectedDiseases: snapshot.selectedDiseases || [],
- medRows: snapshot.medRows || [],
- noMedication: !!snapshot.noMedication,
- mood: snapshot.lifestyle && snapshot.lifestyle.mood || '',
- sleepDuration: snapshot.lifestyle && snapshot.lifestyle.sleepDuration || '',
- bedtime: snapshot.lifestyle && snapshot.lifestyle.bedtime || '',
- bowelFrequency: snapshot.lifestyle && snapshot.lifestyle.bowelFrequency || '',
- bowelForm: snapshot.lifestyle && snapshot.lifestyle.bowelForm || '',
- antibioticHistory: snapshot.lifestyle && snapshot.lifestyle.antibioticHistory || '',
- allergySymptoms: snapshot.lifestyle && Array.isArray(snapshot.lifestyle.allergySymptoms) ? snapshot.lifestyle.allergySymptoms : [],
- menstrualCycle: snapshot.lifestyle && snapshot.lifestyle.menstrualCycle || '',
- specialPeriod: snapshot.lifestyle && snapshot.lifestyle.specialPeriod || '',
- growthDevelopment: snapshot.lifestyle && snapshot.lifestyle.growthDevelopment || '',
- pickyEating: snapshot.lifestyle && snapshot.lifestyle.pickyEating || '',
- osteoporosisRisk: snapshot.lifestyle && snapshot.lifestyle.osteoporosisRisk || '',
- fallRisk: snapshot.lifestyle && snapshot.lifestyle.fallRisk || '',
- screenTime: snapshot.lifestyle && snapshot.lifestyle.screenTime || '',
- teenMood: snapshot.lifestyle && snapshot.lifestyle.teenMood || ''
- }
- },
- // 当前成员的饮食习惯
- currentMemberDiet: function() {
- var map = this.dietMap || {}
- var key = this.activeFormMemberId || '__account'
- return map[key] || {}
- },
- // 当前成员的基本信息(性别/年龄),供 LifestyleStep 动态渲染
- currentMemberProfile: function() {
- var map = this.memberProfileMap || {}
- var key = this.activeFormMemberId || '__account'
- return map[key] || {}
- }
- },
- watch: {
- // 切换填写成员时回到第 1 步(各成员数据快照已保留,不影响已填内容)
- activeFormMemberId: function() {
- this.currentStep = 1
- }
- },
- methods: {
- // —— 步骤导航 ——
- goToStep: function(n) {
- if (n < 1 || n > 5) return
- if (n <= this.currentStep) {
- this.currentStep = n
- return
- }
- for (var s = this.currentStep; s < n; s++) {
- if (!this.validateStep(s)) return
- }
- this.currentStep = n
- },
- prev: function() {
- if (this.currentStep > 1) {
- this.currentStep -= 1
- }
- },
- next: function() {
- var pass = this.validateStep(this.currentStep)
- if (!pass) return
- if (this.currentStep < 5) {
- this.currentStep += 1
- }
- },
- validateStep: function(step) {
- var validatorMap = {
- 1: this.validateVitals,
- 2: this.validateMedical,
- 3: this.validateLifestyle,
- 4: this.validateDiet
- }
- var fn = validatorMap[step]
- if (fn) {
- var msg = fn.call(this)
- if (msg) {
- uni.showToast({ title: msg, icon: 'none' })
- return false
- }
- }
- return true
- },
- // Step1: 体征数值合法性
- validateVitals: function() {
- var form = this.currentMemberForm
- var height = String(form.heightCm || '').trim()
- var weight = String(form.weightKg || '').trim()
- var bp = String(form.bloodPressure || '').trim()
- var glucose = String(form.bloodGlucose || '').trim()
- if (height && !/^\d{2,3}(\.\d+)?$/.test(height)) {
- return '身高格式不正确'
- }
- if (weight && !/^\d{1,3}(\.\d+)?$/.test(weight)) {
- return '体重格式不正确'
- }
- if (bp && !/^\d{2,3}\/\d{2,3}$/.test(bp)) {
- return '血压格式应为 120/80'
- }
- if (glucose && !/^\d{1,2}(\.\d+)?$/.test(glucose)) {
- return '血糖格式不正确'
- }
- return ''
- },
- // Step2: 疾病史至少填写一项,或已标记"无长期用药"
- validateMedical: function() {
- var snapshot = this.currentMemberForm
- if (snapshot.noMedication) return ''
- var diseases = snapshot.selectedDiseases || []
- var meds = snapshot.medRows || []
- var hasMed = meds.some(function(m) { return m && m.name && String(m.name).trim() })
- if (diseases.length === 0 && !hasMed) {
- return '请至少填写一项疾病史或药物,如无请勾选"无长期用药"'
- }
- return ''
- },
- // Step3: 生活方式 — 心情/睡眠/排便/抗生素/过敏,所有字段可选,无强制校验
- validateLifestyle: function() {
- return ''
- },
- // Step4: 饮食习惯至少填写一项(忌口/偏好/必吃/菜系/烹饪/辣度/宗教饮食/目标/饮食结构任一)
- validateDiet: function() {
- var diet = this.currentMemberDiet || {}
- if (diet.noPreference) return ''
- var arrays = [diet.allergies, diet.absoluteAvoid, diet.mustEat, diet.flavorPref, diet.cuisinePref, diet.cookingMethods, diet.healthGoals]
- var hasAny = arrays.some(function(a) { return a && a.length > 0 })
- if (diet.vegetableFruit || diet.dairy || diet.sugaryDrinks) hasAny = true
- if (!hasAny && !(diet.spiceLevel && Number(diet.spiceLevel) > 0) && !(diet.religiousDiet && diet.religiousDiet !== 'none')) {
- return '请至少选择一项饮食习惯偏好或填写饮食结构'
- }
- return ''
- },
- onVitalsInput: function(payload) {
- this.$emit('update-vitals', payload)
- },
- onMedicalInput: function(payload) {
- this.$emit('update-medical', payload)
- },
- onLifestyleInput: function(payload) {
- this.$emit('update-lifestyle', payload)
- },
- onDietInput: function(payload) {
- this.$emit('update-diet', payload)
- },
- // 第4步「无特殊偏好」:跳过校验直接进入确认步
- onDietSkipNoPreference: function() {
- this.currentStep = 5
- },
- // —— 确认步骤提交:聚合所有成员数据后交给父组件统一保存+生成 ——
- handleSubmit: function() {
- var self = this
- this.$emit('submit', {
- memberFormMap: self.memberFormMap,
- dietMap: self.dietMap,
- activeFormMemberId: self.activeFormMemberId
- })
- }
- }
- }
- </script>
- <style scoped>
- .wizard-wrap {
- display: flex;
- flex-direction: column;
- min-height: 600rpx;
- }
- .wizard-steps {
- display: flex;
- justify-content: space-between;
- padding: 20rpx 10rpx 10rpx;
- background: #fff;
- }
- .wizard-step {
- display: flex;
- flex-direction: column;
- align-items: center;
- flex: 1;
- }
- .wizard-step-num {
- width: 44rpx;
- height: 44rpx;
- border-radius: 22rpx;
- background: #e0e0e0;
- color: #999;
- font-size: 24rpx;
- font-weight: bold;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .wizard-step-active .wizard-step-num {
- background: #F97316;
- color: #fff;
- }
- .wizard-step-done .wizard-step-num {
- background: #4CAF50;
- color: #fff;
- }
- .wizard-step-label {
- font-size: 20rpx;
- color: #999;
- white-space: nowrap;
- margin-top: 6rpx;
- }
- .wizard-step-active .wizard-step-label,
- .wizard-step-done .wizard-step-label {
- color: #333;
- font-weight: 500;
- }
- .wizard-body {
- flex: 1;
- padding: 10rpx 20rpx;
- }
- .wizard-footer {
- display: flex;
- padding: 20rpx 20rpx 30rpx;
- }
- .btn-prev,
- .btn-next {
- flex: 1;
- margin: 0 10rpx;
- border-radius: 999rpx;
- font-size: 30rpx;
- font-weight: 600;
- }
- .btn-prev {
- background: #F1F5F9;
- color: #334155;
- }
- .btn-next {
- background: linear-gradient(135deg, #F97316, #FB923C);
- color: #fff;
- }
- </style>
|