Parcourir la source

feat(health-plan): 增加健康问卷确认步骤

方案制定流程由3步改为4步:选人→提需求→健康问卷→确认方案

- 步骤条新增第3步「健康问卷」
- Step2「生成方案」改为「下一步」至健康问卷步骤
- Step3健康问卷:已填写则带出摘要确认,未填写引导去填写
- Step4确认方案:原Step3内容,regenerate返回Step3
- 新增healthStatusSummary computed解析健康数据摘要
- 新增parseLipidText/parseNameList/parseMedText/formatTime辅助方法
- 新增goFillQuestionnaire跳转健康问卷表单页

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)
Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
liaoxg il y a 1 mois
Parent
commit
d82058e19e
1 fichiers modifiés avec 158 ajouts et 10 suppressions
  1. 158 10
      cfc-frontend/pages/health/health-plan-summary.vue

+ 158 - 10
cfc-frontend/pages/health/health-plan-summary.vue

@@ -12,14 +12,19 @@
         <text class="step-label">提需求</text>
       </view>
       <view class="step-line" :class="{ done: step > 2 }"></view>
-      <view class="step" :class="{ active: step >= 3 }">
+      <view class="step" :class="{ active: step >= 3, done: step > 3 }">
         <text class="step-num">3</text>
+        <text class="step-label">健康问卷</text>
+      </view>
+      <view class="step-line" :class="{ done: step > 3 }"></view>
+      <view class="step" :class="{ active: step >= 4 }">
+        <text class="step-num">4</text>
         <text class="step-label">确认方案</text>
       </view>
     </view>
 
     <!-- Step 1: 选择家庭成员 -->
-    <view class="section" v-if="step <= 2">
+    <view class="section" v-if="step === 1">
       <view class="section-title">选择家庭成员</view>
       <view class="hint-text">选择方案针对的家庭成员(可多选)</view>
       <view class="member-list">
@@ -61,19 +66,54 @@
       <view class="goal-footer">
         <text class="goal-count">{{ userGoal.length }}/300</text>
         <button
-          class="btn-generate"
+          class="btn-next btn-step-next"
+          :disabled="!canGenerate"
+          @click="step = 3"
+        >
+          下一步
+        </button>
+      </view>
+      <view class="back-link" @click="step = 1">← 返回选择成员</view>
+    </view>
+
+    <!-- Step 3: 健康问卷确认 -->
+    <view class="section" v-if="step === 3">
+      <view class="section-title">健康问卷</view>
+      <view class="hint-text">确认健康现状,方案将参考这些信息更精准</view>
+
+      <!-- 已填写:带出内容确认 -->
+      <view v-if="healthStatus && healthStatusSummary.length > 0" class="q-summary">
+        <view v-for="(item, idx) in healthStatusSummary" :key="idx" class="q-row">
+          <text class="q-label">{{ item.label }}</text>
+          <text class="q-value">{{ item.value }}</text>
+        </view>
+        <view class="q-updated" v-if="healthStatus.updatedAt">更新于 {{ formatTime(healthStatus.updatedAt) }}</view>
+      </view>
+
+      <!-- 未填写 -->
+      <view v-else class="empty-state q-empty">
+        <text class="empty-icon">📋</text>
+        <text class="empty-text">尚未填写健康问卷</text>
+      </view>
+
+      <view class="q-actions">
+        <button class="btn-edit" @click="goFillQuestionnaire">
+          {{ healthStatus ? '修改健康问卷' : '去填写健康问卷' }}
+        </button>
+        <button
+          class="btn-confirm"
           :class="{ disabled: !canGenerate || loading }"
           :disabled="!canGenerate || loading"
           @click="generatePlan"
         >
-          {{ loading ? '正在生成方案...' : '生成方案' }}
+          {{ loading ? '正在生成方案...' : '确认并生成方案' }}
         </button>
       </view>
-      <view class="back-link" @click="step = 1">← 返回选择成员</view>
+      <view class="back-link" @click="step = 2">← 返回修改需求</view>
     </view>
 
-    <!-- Step 3: 方案 & 任务 -->
-    <view class="section task-section" v-if="step === 3 && parsedTasks.length > 0 && !taskSubmitted">
+    <!-- Step 4: 方案 & 任务 -->
+    <view class="section task-section" v-if="step === 4 && parsedTasks.length > 0 && !taskSubmitted">
       <view class="section-title">健康方案</view>
 
       <!-- 方案原文 -->
@@ -128,7 +168,7 @@
           确认并生成任务单
         </button>
       </view>
-      <view class="back-link" @click="step = 2">← 返回修改需求</view>
+      <view class="back-link" @click="step = 3">← 返回健康问卷</view>
     </view>
 
     <!-- 成功 -->
@@ -172,6 +212,22 @@ export default {
     },
     canConfirmTasks: function() {
       return this.parsedTasks.length > 0 && this.parsedTasks.every(function(t) { return t.executorId })
+    },
+    healthStatusSummary: function() {
+      var d = this.healthStatus || {}
+      var list = []
+      if (d.heightCm) list.push({ label: '身高', value: d.heightCm + ' cm' })
+      if (d.weightKg) list.push({ label: '体重', value: d.weightKg + ' kg' })
+      if (d.bloodPressure) list.push({ label: '血压', value: d.bloodPressure })
+      if (d.bloodGlucose) list.push({ label: '空腹血糖', value: d.bloodGlucose + ' mmol/L' })
+      var lipids = this.parseLipidText(d.bloodLipids)
+      if (lipids) list.push({ label: '血脂', value: lipids })
+      var diseases = this.parseNameList(d.diseaseHistory)
+      if (diseases) list.push({ label: '疾病史', value: diseases })
+      var meds = this.parseMedText(d.medications)
+      if (meds) list.push({ label: '在服药物', value: meds })
+      if (d.notes) list.push({ label: '其他补充', value: d.notes })
+      return list
     }
   },
   onLoad: function(options) {
@@ -227,6 +283,46 @@ export default {
       if (idx >= 0) this.selectedMemberIds.splice(idx, 1)
       else this.selectedMemberIds.push(memberId)
     },
+    parseLipidText: function(json) {
+      try {
+        var arr = JSON.parse(json || '[]')
+        if (arr instanceof Array) {
+          return arr.map(function(it) {
+            return (it.name || '') + (it.value ? ' ' + it.value + (it.unit || '') : '')
+          }).filter(function(s) { return s }).join('、')
+        }
+      } catch (e) {}
+      return ''
+    },
+    parseNameList: function(json) {
+      try {
+        var arr = JSON.parse(json || '[]')
+        if (arr instanceof Array) {
+          return arr.map(function(it) { return it.name || '' }).filter(function(s) { return s }).join('、')
+        }
+      } catch (e) {}
+      return ''
+    },
+    parseMedText: function(json) {
+      try {
+        var arr = JSON.parse(json || '[]')
+        if (arr instanceof Array) {
+          return arr.map(function(it) {
+            return (it.name || '') + (it.time ? '(' + it.time + ')' : '')
+          }).filter(function(s) { return s }).join('、')
+        }
+      } catch (e) {}
+      return ''
+    },
+    formatTime: function(iso) {
+      if (!iso) return ''
+      var s = String(iso)
+      if (s.length >= 19) s = s.substring(0, 19)
+      return s.replace('T', ' ')
+    },
+    goFillQuestionnaire: function() {
+      uni.navigateTo({ url: '/pages/health/health-status-form' })
+    },
     generatePlan: function() {
       var self = this
       if (!self.canGenerate) return
@@ -301,7 +397,7 @@ export default {
         if (answer) {
           self.planContent = answer
           self.parseTasksFromPlan(answer)
-          self.step = 3
+          self.step = 4
           // 自动出方案模式:方案生成后自动提交任务
           if (self.autoExecute && self.parsedTasks.length > 0) {
             setTimeout(function() { self.submitTasks() }, 500)
@@ -350,7 +446,7 @@ export default {
       self.parsedTasks = tasks
     },
     regenerate: function() {
-      this.step = 2
+      this.step = 3
       this.parsedTasks = []
       this.planContent = ''
     },
@@ -779,4 +875,56 @@ export default {
   border: none;
   text-align: center;
 }
+
+/* 健康问卷摘要 */
+.q-summary {
+  background: #FAFAFA;
+  border-radius: 12rpx;
+  padding: 20rpx 24rpx;
+  margin-bottom: 24rpx;
+}
+.q-row {
+  display: flex;
+  justify-content: space-between;
+  align-items: flex-start;
+  padding: 12rpx 0;
+  border-bottom: 1rpx solid #f0f0f0;
+}
+.q-row:last-child {
+  border-bottom: none;
+}
+.q-label {
+  font-size: 26rpx;
+  color: #999;
+  flex-shrink: 0;
+  margin-right: 20rpx;
+}
+.q-value {
+  font-size: 26rpx;
+  color: #333;
+  text-align: right;
+  word-break: break-all;
+}
+.q-updated {
+  font-size: 22rpx;
+  color: #bbb;
+  margin-top: 12rpx;
+}
+.q-empty {
+  padding: 40rpx 0;
+}
+.q-actions {
+  display: flex;
+  flex-direction: column;
+  gap: 16rpx;
+}
+.q-actions .btn-edit {
+  width: 100%;
+}
+.q-actions .btn-confirm {
+  width: 100%;
+}
+.btn-step-next {
+  width: 100%;
+}
 </style>