|
|
@@ -0,0 +1,313 @@
|
|
|
+<template>
|
|
|
+ <view class="survey-page">
|
|
|
+ <!-- 加载中 -->
|
|
|
+ <view v-if="loading" class="loading-state">
|
|
|
+ <text class="loading-text">加载中...</text>
|
|
|
+ </view>
|
|
|
+ <!-- 调研组件(会员门槛未过时由 checkGateAndLoad 弹窗拦截,此处不渲染) -->
|
|
|
+ <comprehensive-survey
|
|
|
+ v-else
|
|
|
+ :modules="activeModules"
|
|
|
+ :form="formSnapshot"
|
|
|
+ :diet="dietSnapshot"
|
|
|
+ :profile="memberProfile"
|
|
|
+ :submitting="submitting"
|
|
|
+ @update-form="onUpdateForm"
|
|
|
+ @update-diet="onUpdateDiet"
|
|
|
+ @submit="onSubmit"
|
|
|
+ />
|
|
|
+ </view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import { getHealthStatus, saveHealthStatus, getDietPreferences, saveDietPreferences, getMyMembership, getFamilyMemberList } from '../../utils/api'
|
|
|
+import ComprehensiveSurvey from '@/pages/health/components/ComprehensiveSurvey.vue'
|
|
|
+
|
|
|
+var ALL_MODULES = ['basic', 'status', 'diet', 'disease', 'bowel', 'sleep', 'med', 'allergy']
|
|
|
+
|
|
|
+// 本地解析工具(与 health-plan-summary.vue 的同名方法保持一致)
|
|
|
+function parseLipidCholesterol(json) {
|
|
|
+ try {
|
|
|
+ var arr = JSON.parse(json || '[]')
|
|
|
+ if (arr instanceof Array && arr.length > 0 && arr[0].value) return String(arr[0].value)
|
|
|
+ } catch (e) {}
|
|
|
+ return ''
|
|
|
+}
|
|
|
+function parseDiseases(json) {
|
|
|
+ var list = []
|
|
|
+ try {
|
|
|
+ var arr = JSON.parse(json || '[]')
|
|
|
+ if (arr instanceof Array) {
|
|
|
+ arr.forEach(function(item) {
|
|
|
+ if (item.name) list.push({ name: item.name, diagnosedAt: item.diagnosedAt || '', custom: false })
|
|
|
+ })
|
|
|
+ }
|
|
|
+ } catch (e) {}
|
|
|
+ return list
|
|
|
+}
|
|
|
+function parseMeds(json) {
|
|
|
+ var list = []
|
|
|
+ try {
|
|
|
+ var arr = JSON.parse(json || '[]')
|
|
|
+ if (arr instanceof Array) {
|
|
|
+ arr.forEach(function(item) {
|
|
|
+ list.push({ name: item.name || '', startDate: item.startDate || '', durationText: item.durationText || '', time: item.time || '' })
|
|
|
+ })
|
|
|
+ }
|
|
|
+ } catch (e) {}
|
|
|
+ return list
|
|
|
+}
|
|
|
+function parseLifestyle(json) {
|
|
|
+ var obj = {}
|
|
|
+ try {
|
|
|
+ var parsed = JSON.parse(json)
|
|
|
+ if (parsed && typeof parsed === 'object') {
|
|
|
+ var keys = ['mood', 'sleepDuration', 'bedtime', 'bowelFrequency', 'bowelForm', 'antibioticHistory',
|
|
|
+ 'currentStatus', 'exerciseFrequency', 'exerciseDuration',
|
|
|
+ 'allergySymptoms', 'menstrualCycle', 'specialPeriod', 'growthDevelopment',
|
|
|
+ 'pickyEating', 'osteoporosisRisk', 'fallRisk', 'screenTime', 'teenMood']
|
|
|
+ keys.forEach(function(k) {
|
|
|
+ obj[k] = parsed[k] != null ? parsed[k] : (k === 'allergySymptoms' ? [] : '')
|
|
|
+ })
|
|
|
+ }
|
|
|
+ } catch (e) {}
|
|
|
+ return obj
|
|
|
+}
|
|
|
+function normalizeDiet(d) {
|
|
|
+ return {
|
|
|
+ allergies: (d && d.allergies) || [],
|
|
|
+ absoluteAvoid: (d && d.absoluteAvoid) || [],
|
|
|
+ mustEat: (d && d.mustEat) || [],
|
|
|
+ religiousDiet: (d && d.religiousDiet) || 'none',
|
|
|
+ spiceLevel: (d && d.spiceLevel) || 0,
|
|
|
+ flavorPref: (d && d.flavorPref) || [],
|
|
|
+ cuisinePref: (d && d.cuisinePref) || [],
|
|
|
+ cookingMethods: (d && d.cookingMethods) || [],
|
|
|
+ healthGoals: (d && d.healthGoals) || [],
|
|
|
+ noPreference: !!(d && d.noPreference),
|
|
|
+ vegetableFruit: (d && d.vegetableFruit) || '',
|
|
|
+ dairy: (d && d.dairy) || '',
|
|
|
+ sugaryDrinks: (d && d.sugaryDrinks) || ''
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+export default {
|
|
|
+ components: { ComprehensiveSurvey },
|
|
|
+ data: function() {
|
|
|
+ return {
|
|
|
+ loading: true,
|
|
|
+ submitting: false,
|
|
|
+ activeModules: ALL_MODULES.slice(),
|
|
|
+ familyMemberId: '',
|
|
|
+ formSnapshot: {
|
|
|
+ heightCm: '', weightKg: '', bloodPressure: '', bloodGlucose: '', notes: '',
|
|
|
+ lipidCholesterol: '', selectedDiseases: [], medRows: [], noMedication: false,
|
|
|
+ lifestyle: {}
|
|
|
+ },
|
|
|
+ dietSnapshot: normalizeDiet(null),
|
|
|
+ memberProfile: {},
|
|
|
+ _loaded: false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onLoad: function(options) {
|
|
|
+ // 1. 解析 modules(缺省/空串/全非法 → 全量 8 模块)
|
|
|
+ if (options && options.modules) {
|
|
|
+ var list = String(options.modules).split(',').filter(function(m) { return ALL_MODULES.indexOf(m) >= 0 })
|
|
|
+ if (list.length > 0) this.activeModules = list
|
|
|
+ }
|
|
|
+ // 2. 当前成员快照(与 preferences.vue 一致)
|
|
|
+ this.familyMemberId = uni.getStorageSync('currentChildId') || uni.getStorageSync('currentMemberId') || uni.getStorageSync('userId') || ''
|
|
|
+ // 3. 会员门槛 + 加载
|
|
|
+ this.checkGateAndLoad()
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ // 会员门槛(语义与 health-plan-summary.checkQuestionnaireGate 一致)
|
|
|
+ checkGateAndLoad: function() {
|
|
|
+ var self = this
|
|
|
+ getMyMembership().then(function(res) {
|
|
|
+ var m = (res && res.data) || {}
|
|
|
+ var level = m.memberLevel || m.levelCode || ''
|
|
|
+ if (level && level !== 'FREE') {
|
|
|
+ self.loadData()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ uni.showModal({
|
|
|
+ title: '开通 9.9 元/月体验会员',
|
|
|
+ content: '填写健康调研需先开通体验会员(9.9 元/月),开通后可填写并保存。',
|
|
|
+ confirmText: '去开通',
|
|
|
+ cancelText: '暂不',
|
|
|
+ success: function(r) {
|
|
|
+ if (r.confirm) {
|
|
|
+ uni.setStorageSync('SURVEY_RESUME_AFTER_PURCHASE', '1')
|
|
|
+ uni.navigateTo({ url: '/pages/membership/pay?levelCode=BASIC&period=monthly' })
|
|
|
+ } else {
|
|
|
+ uni.navigateBack({ delta: 1 })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }).catch(function() {
|
|
|
+ // 网络失败放行(保存时由后端兜底校验)
|
|
|
+ self.loadData()
|
|
|
+ })
|
|
|
+ },
|
|
|
+ // 购买返回:重新走门槛(未开通仍会再弹,开通后放行加载)
|
|
|
+ resumeAfterPurchase: function() {
|
|
|
+ this.checkGateAndLoad()
|
|
|
+ },
|
|
|
+ // 并行加载:health_status + diet_preferences + 家庭成员(取当前成员性别/年龄供条件渲染)
|
|
|
+ loadData: function() {
|
|
|
+ var self = this
|
|
|
+ self.loading = true
|
|
|
+ var memberId = self.familyMemberId || ''
|
|
|
+ var tasks = [
|
|
|
+ getHealthStatus(memberId || undefined).then(function(res) {
|
|
|
+ var d = (res && res.code === 200 && res.data) ? res.data : null
|
|
|
+ if (d) {
|
|
|
+ self.$set(self.formSnapshot, 'heightCm', d.heightCm || '')
|
|
|
+ self.$set(self.formSnapshot, 'weightKg', d.weightKg || '')
|
|
|
+ self.$set(self.formSnapshot, 'bloodPressure', d.bloodPressure || '')
|
|
|
+ self.$set(self.formSnapshot, 'bloodGlucose', d.bloodGlucose || '')
|
|
|
+ self.$set(self.formSnapshot, 'notes', d.notes || '')
|
|
|
+ self.$set(self.formSnapshot, 'lipidCholesterol', parseLipidCholesterol(d.bloodLipids))
|
|
|
+ self.$set(self.formSnapshot, 'selectedDiseases', parseDiseases(d.diseaseHistory))
|
|
|
+ self.$set(self.formSnapshot, 'medRows', parseMeds(d.medications))
|
|
|
+ self.$set(self.formSnapshot, 'lifestyle', d.lifestyle ? parseLifestyle(d.lifestyle) : {})
|
|
|
+ }
|
|
|
+ }).catch(function() {}),
|
|
|
+ getDietPreferences({ memberId: memberId }).then(function(res) {
|
|
|
+ var d = (res && res.code === 200 && res.data) ? res.data : null
|
|
|
+ self.dietSnapshot = normalizeDiet(d)
|
|
|
+ }).catch(function() {
|
|
|
+ self.dietSnapshot = normalizeDiet(null)
|
|
|
+ }),
|
|
|
+ getFamilyMemberList({}).then(function(res) {
|
|
|
+ var list = (res && res.code === 200 && res.data) ? res.data : []
|
|
|
+ var arr = list instanceof Array ? list : []
|
|
|
+ var found = null
|
|
|
+ for (var i = 0; i < arr.length; i++) {
|
|
|
+ var mid = arr[i].memberId || arr[i].id
|
|
|
+ if (memberId && String(mid) === String(memberId)) { found = arr[i]; break }
|
|
|
+ }
|
|
|
+ // 未指定成员(账号级)时取 isSelf 成员兜底
|
|
|
+ if (!found) {
|
|
|
+ for (var j = 0; j < arr.length; j++) {
|
|
|
+ if (arr[j].isSelf) { found = arr[j]; break }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (found) {
|
|
|
+ self.memberProfile = { gender: found.gender || '', age: found.age != null ? found.age : '' }
|
|
|
+ }
|
|
|
+ }).catch(function() {})
|
|
|
+ ]
|
|
|
+ Promise.all(tasks).then(function() {
|
|
|
+ self.loading = false
|
|
|
+ self._loaded = true
|
|
|
+ })
|
|
|
+ },
|
|
|
+ // 组件 patch → 合并到快照(patch 为结构化字段副本,不含 lifestyle)
|
|
|
+ onUpdateForm: function(patch) {
|
|
|
+ var self = this
|
|
|
+ Object.keys(patch || {}).forEach(function(k) {
|
|
|
+ self.$set(self.formSnapshot, k, patch[k])
|
|
|
+ })
|
|
|
+ },
|
|
|
+ onUpdateDiet: function(patch) {
|
|
|
+ var self = this
|
|
|
+ Object.keys(patch || {}).forEach(function(k) {
|
|
|
+ self.$set(self.dietSnapshot, k, patch[k])
|
|
|
+ })
|
|
|
+ },
|
|
|
+ // 提交:health_status 侧组装 + lifestyle merge + diet_preferences 侧组装(合并式增量)
|
|
|
+ onSubmit: function(payload) {
|
|
|
+ var self = this
|
|
|
+ if (self.submitting) return
|
|
|
+ var form = payload.form || {}
|
|
|
+ // 血压格式兜底
|
|
|
+ if (form.bloodPressure && !/^\d{2,3}\/\d{2,3}$/.test(form.bloodPressure)) {
|
|
|
+ uni.showToast({ title: '血压格式应为 120/80', icon: 'none' })
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 1) health_status payload:扁平体征 + JSON 列(疾病史/用药/血脂)+ lifestyle merge
|
|
|
+ var hsPayload = {}
|
|
|
+ var enabled = self.activeModules
|
|
|
+ if (enabled.indexOf('basic') >= 0) {
|
|
|
+ if (form.heightCm != null && form.heightCm !== '') hsPayload.heightCm = Number(form.heightCm)
|
|
|
+ if (form.weightKg != null && form.weightKg !== '') hsPayload.weightKg = Number(form.weightKg)
|
|
|
+ if (form.bloodPressure != null && form.bloodPressure !== '') hsPayload.bloodPressure = form.bloodPressure
|
|
|
+ if (form.bloodGlucose != null && form.bloodGlucose !== '') hsPayload.bloodGlucose = Number(form.bloodGlucose)
|
|
|
+ if (form.notes != null && form.notes !== '') hsPayload.notes = form.notes
|
|
|
+ if (form.lipidCholesterol != null && form.lipidCholesterol !== '') {
|
|
|
+ hsPayload.bloodLipids = JSON.stringify([{ name: '总胆固醇', value: parseFloat(form.lipidCholesterol), unit: 'mmol/L', note: '' }])
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (enabled.indexOf('disease') >= 0) {
|
|
|
+ var diseases = (form.selectedDiseases || []).map(function(item) {
|
|
|
+ var d = { name: item.name }
|
|
|
+ if (item.diagnosedAt) d.diagnosedAt = item.diagnosedAt
|
|
|
+ return d
|
|
|
+ })
|
|
|
+ hsPayload.diseaseHistory = JSON.stringify(diseases)
|
|
|
+ }
|
|
|
+ if (enabled.indexOf('med') >= 0) {
|
|
|
+ var meds = (form.medRows || []).filter(function(item) { return item.name }).map(function(item) {
|
|
|
+ var m = { name: item.name }
|
|
|
+ if (item.startDate) m.startDate = item.startDate
|
|
|
+ if (item.durationText) m.durationText = item.durationText
|
|
|
+ if (item.time) m.time = item.time
|
|
|
+ return m
|
|
|
+ })
|
|
|
+ hsPayload.medications = JSON.stringify(meds)
|
|
|
+ }
|
|
|
+ // lifestyle merge:现有 lifestyle + 本次 diff(新 key 合并,未调研模块 key 不动)
|
|
|
+ var existing = self.formSnapshot.lifestyle || {}
|
|
|
+ var merged = Object.assign({}, existing)
|
|
|
+ Object.keys(payload.lifestyleDiff || {}).forEach(function(k) { merged[k] = payload.lifestyleDiff[k] })
|
|
|
+ if (Object.keys(merged).length > 0) hsPayload.lifestyle = JSON.stringify(merged)
|
|
|
+ // 2) diet payload:仅启用模块涉及字段(allergy 模块的 allergies 双写 diet_preferences)
|
|
|
+ var diet = payload.diet || {}
|
|
|
+ var dietModules = { allergies: 'allergy', absoluteAvoid: 'diet', mustEat: 'diet', flavorPref: 'diet', cuisinePref: 'diet', cookingMethods: 'diet', healthGoals: 'diet', religiousDiet: 'diet', spiceLevel: 'diet', vegetableFruit: 'diet', dairy: 'diet', sugaryDrinks: 'diet' }
|
|
|
+ var dietPayload = {}
|
|
|
+ Object.keys(dietModules).forEach(function(k) {
|
|
|
+ if (enabled.indexOf(dietModules[k]) < 0) return
|
|
|
+ var v = diet[k]
|
|
|
+ if (v == null) return
|
|
|
+ if (v instanceof Array) { if (v.length > 0) dietPayload[k] = v.slice() }
|
|
|
+ else if (v !== '' && v !== 'none') dietPayload[k] = v
|
|
|
+ })
|
|
|
+ if (diet.noPreference) dietPayload.noPreference = true
|
|
|
+ // 3. 组装保存任务
|
|
|
+ var saveTasks = []
|
|
|
+ if (Object.keys(hsPayload).length > 0) {
|
|
|
+ hsPayload.memberId = self.familyMemberId
|
|
|
+ saveTasks.push(saveHealthStatus(hsPayload))
|
|
|
+ }
|
|
|
+ if (Object.keys(dietPayload).length > 0) {
|
|
|
+ dietPayload.familyMemberId = self.familyMemberId
|
|
|
+ saveTasks.push(saveDietPreferences(dietPayload))
|
|
|
+ }
|
|
|
+ self.submitting = true
|
|
|
+ Promise.all(saveTasks).then(function() {
|
|
|
+ uni.showToast({ title: '已保存', icon: 'success' })
|
|
|
+ self.submitting = false
|
|
|
+ setTimeout(function() { uni.navigateBack() }, 800)
|
|
|
+ }).catch(function(err) {
|
|
|
+ self.submitting = false
|
|
|
+ uni.showToast({ title: (err && err.message) || '保存失败,请重试', icon: 'none' })
|
|
|
+ })
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onShow: function() {
|
|
|
+ // 从购买页返回:重新走会员门槛(首次 onLoad 已加载时跳过)
|
|
|
+ if (uni.getStorageSync('SURVEY_RESUME_AFTER_PURCHASE') === '1') {
|
|
|
+ uni.removeStorageSync('SURVEY_RESUME_AFTER_PURCHASE')
|
|
|
+ if (!this._loaded) this.resumeAfterPurchase()
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</script>
|
|
|
+
|
|
|
+<style>
|
|
|
+.survey-page { min-height: 100vh; background: linear-gradient(180deg, #FFF7ED, #FFF3E6); }
|
|
|
+.loading-state { padding: 200rpx 0; text-align: center; }
|
|
|
+.loading-text { font-size: 28rpx; color: #B08968; }
|
|
|
+</style>
|