Просмотр исходного кода

fix(frontend): report-detail.vue 前端自动从 payload 组装展示块,适配不同报告类型

当后端 report_blocks 表无数据时,前端自动从 payload 数据组装 blocks(与后端 ReportBlockAssembler 逻辑一致),覆盖:评分圆环、疾病风险分组、指标分组、菌种详情、食物推荐、益生菌种、分类学列表(纲/目/科/属/种)。后端已有 blocks 时优先使用。编辑按钮始终显示,文案区分'编辑'/'查看完整数据'。
liaoxg 1 месяц назад
Родитель
Сommit
690a0c6077
1 измененных файлов с 194 добавлено и 25 удалено
  1. 194 25
      cfc-frontend/pages/health/report-detail.vue

+ 194 - 25
cfc-frontend/pages/health/report-detail.vue

@@ -14,30 +14,9 @@
       <!-- view mode -->
       <view v-if="pageMode==='view'">
         <report-blocks-renderer v-if="blocks && blocks.length > 0" :blocks="blocks" />
-        <view v-else-if="hasPayloadData" class="payload-fallback">
-          <view class="card" v-if="payload.summary && payload.summary.overallScore">
-            <view class="card-title">综合评分</view>
-            <text class="score-text">{{ payload.summary.overallScore }}</text>
-          </view>
-          <view class="card" v-if="payload.indicators && payload.indicators.length">
-            <view class="card-title">健康指标({{ payload.indicators.length }} 项)</view>
-            <view class="indicator-item" v-for="(ind, idx) in payload.indicators" :key="'fallback-' + idx">
-              <text class="ind-name">{{ ind.indicatorName || ind.name || '' }}</text>
-              <text class="ind-value">{{ ind.indicatorValue || ind.value || '' }}{{ ind.unit || '' }}</text>
-              <text class="ind-status" :class="'status-' + _cssClass(ind.status)">{{ statusText(ind.status) }}</text>
-            </view>
-          </view>
-          <view class="card" v-if="payload.diseaseRisks && payload.diseaseRisks.length">
-            <view class="card-title">疾病风险</view>
-            <view class="risk-item" v-for="(risk, idx) in payload.diseaseRisks" :key="'risk-fb-' + idx">
-              <text class="risk-name">{{ risk.diseaseName || '' }}</text>
-              <view class="risk-level" :class="'risk-' + (risk.riskLevel || 'low')">{{ riskLevelText(risk.riskLevel) }}</view>
-            </view>
-          </view>
-        </view>
         <view class="empty-hint" v-else>暂无报告内容</view>
         <view class="edit-btn" v-if="isGutFlora && pageMode==='view'">
-          <view class="nav-edit" @tap="enterEditMode"><text>{{ blocks && blocks.length > 0 ? '编辑' : '查看完整数据 ›' }}</text></view>
+          <view class="nav-edit" @tap="enterEditMode"><text>{{ backendBlocksExist ? '编辑' : '查看完整数据 ›' }}</text></view>
         </view>
       </view>
 
@@ -404,6 +383,7 @@ export default {
       memberId: null,
       familyId: null,
       blocks: [],
+      backendBlocksExist: false,
       headInfo: null,
       pageMode: 'view', // view / draft / edit
       parsing: false,
@@ -564,10 +544,21 @@ export default {
           head.childName = report.childName || ''
           head.reportDateText = report.reportDate ? parseDate(report.reportDate) : ''
           self.headInfo = head
-          self.blocks = (res.data && res.data.blocks) || report.blocks || []
+          var backendBlocks = (res.data && res.data.blocks) || report.blocks || []
           if (res.data.payload) {
             self.payload = Object.assign({}, self.defaultPayload(), res.data.payload)
           }
+          // 后端无 blocks 时从 payload 自动组装
+          if (backendBlocks.length > 0) {
+            self.blocks = backendBlocks
+            self.backendBlocksExist = true
+          } else if (self.hasPayloadData) {
+            self.blocks = self.assembleBlocksFromPayload()
+            self.backendBlocksExist = false
+          } else {
+            self.blocks = []
+            self.backendBlocksExist = false
+          }
           self.needBind = !!report.needBind
           self.selectedMemberId = report.subjectId || null
         } else {
@@ -871,6 +862,186 @@ export default {
         uni.hideLoading()
         uni.showToast({ title: '保存异常', icon: 'none' })
       })
+    },
+    /** 从 payload 数据自动组装展示块(后端无 blocks 时使用) */
+    assembleBlocksFromPayload: function() {
+      var blocks = []
+      var p = this.payload
+      if (!p) return blocks
+
+      // 1. 评分圆环块
+      var scoreItems = []
+      if (p.summary) {
+        this._addScoreItem(scoreItems, '综合', p.summary.overallScore)
+        this._addScoreItem(scoreItems, '菌群健康', p.summary.gutHealthScore)
+        this._addScoreItem(scoreItems, '慢病控制', p.summary.chronicDiseaseScore)
+        // 子项
+        if (p.summary.nutritionScore != null) { this._addSubScore(scoreItems, '综合', '营养均衡', p.summary.nutritionScore) }
+        if (p.summary.gutBalanceScore != null) { this._addSubScore(scoreItems, '菌群健康', '平衡', p.summary.gutBalanceScore) }
+        if (p.summary.gutDiversityScore != null) { this._addSubScore(scoreItems, '菌群健康', '多样性', p.summary.gutDiversityScore) }
+        if (p.summary.beneficialBacteriaScore != null) { this._addSubScore(scoreItems, '菌群健康', '有益菌', p.summary.beneficialBacteriaScore) }
+        if (p.summary.harmfulBacteriaScore != null) { this._addSubScore(scoreItems, '菌群健康', '有害菌', p.summary.harmfulBacteriaScore) }
+        if (p.summary.coreSpeciesScore != null) { this._addSubScore(scoreItems, '菌群健康', '核心菌属', p.summary.coreSpeciesScore) }
+        // 肠道年龄/肠型
+        if (p.summary.gutAge) { this._addScoreItem(scoreItems, '肠道年龄', p.summary.gutAge) }
+        if (p.summary.gutType) { this._addScoreItem(scoreItems, '肠型', p.summary.gutType) }
+      }
+      if (scoreItems.length > 0) {
+        blocks.push({ type: 'score', title: '健康评分', items: scoreItems })
+      }
+
+      // 2. 疾病风险分组块
+      var risks = p.diseaseRisks || []
+      if (risks.length > 0) {
+        var important = []
+        var normal = []
+        for (var ri = 0; ri < risks.length; ri++) {
+          var r = risks[ri]
+          var level = r.riskLevel || ''
+          var item = { name: r.diseaseName || '', value: r.riskValue || '', level: level }
+          if (level === '需注意' || level === '注意' || level === '高风险' || level === '异常') {
+            important.push(item)
+          } else {
+            normal.push(item)
+          }
+        }
+        if (important.length > 0) blocks.push({ type: 'risk_group', title: '重要风险', items: important })
+        if (normal.length > 0) blocks.push({ type: 'risk_group', title: '其它风险', items: normal })
+      }
+
+      // 3. 指标分组块
+      var inds = p.indicators || []
+      if (inds.length > 0) {
+        var byCat = {}
+        for (var ii = 0; ii < inds.length; ii++) {
+          var ind = inds[ii]
+          var cat = ind.category || '其他指标'
+          if (!byCat[cat]) byCat[cat] = []
+          byCat[cat].push({
+            name: ind.indicatorName || ind.name || '',
+            value: ind.indicatorValue || ind.value || '',
+            unit: ind.unit || '',
+            refRange: ind.refRange || '',
+            status: ind.status || ''
+          })
+        }
+        var catKeys = Object.keys(byCat)
+        for (var ck = 0; ck < catKeys.length; ck++) {
+          blocks.push({ type: 'indicator', title: catKeys[ck], items: byCat[catKeys[ck]] })
+        }
+      }
+
+      // 4. 菌种列表块
+      var floraItems = []
+      // 肠道菌群
+      var gf = p.gutFlora || []
+      for (var fi = 0; fi < gf.length; fi++) {
+        var f = gf[fi]
+        floraItems.push({
+          name: f.name || f.bacteriaName || '',
+          value: f.abundance != null ? f.abundance : (f.bacteriaValue || ''),
+          range: f.normalRange || '',
+          status: f.populationLevel || f.level || f.status || ''
+        })
+      }
+      // 病原菌
+      var pg = p.pathogenGenus || []
+      for (var pi = 0; pi < pg.length; pi++) {
+        var pgi = pg[pi]
+        floraItems.push({
+          name: pgi.name || pgi.bacteriaName || '',
+          value: pgi.abundance != null ? pgi.abundance : (pgi.bacteriaValue || ''),
+          range: pgi.normalRange || '',
+          status: pgi.populationLevel || pgi.level || pgi.status || ''
+        })
+      }
+      var pd = p.pathogenDetection || []
+      for (var pdi = 0; pdi < pd.length; pdi++) {
+        var pdiItem = pd[pdi]
+        floraItems.push({
+          name: pdiItem.name || pdiItem.bacteriaName || '',
+          value: pdiItem.abundance != null ? pdiItem.abundance : (pdiItem.bacteriaValue || ''),
+          range: pdiItem.normalRange || '',
+          status: pdiItem.populationLevel || pdiItem.level || pdiItem.status || ''
+        })
+      }
+      if (floraItems.length > 0) {
+        blocks.push({
+          type: 'list', title: '菌种详情',
+          columns: [{ key: 'name', label: '菌种' }, { key: 'value', label: '数值' }, { key: 'range', label: '正常范围' }, { key: 'status', label: '状态' }],
+          items: floraItems
+        })
+      }
+
+      // 5. 食物推荐列表
+      var foods = p.foods || []
+      if (foods.length > 0) {
+        var foodItems = []
+        for (var fdi = 0; fdi < foods.length; fdi++) {
+          var fd = foods[fdi]
+          foodItems.push({ name: fd.foodName || fd.name || '', category: fd.category || '', score: fd.score != null ? fd.score : '' })
+        }
+        blocks.push({
+          type: 'list', title: '食物推荐',
+          columns: [{ key: 'name', label: '食材' }, { key: 'category', label: '类别' }, { key: 'score', label: '推荐指数' }],
+          items: foodItems
+        })
+      }
+
+      // 6. 益生菌种列表
+      var ps = p.probioticSpecies || []
+      if (ps.length > 0) {
+        var psItems = []
+        for (var psi = 0; psi < ps.length; psi++) {
+          var psiItem = ps[psi]
+          psItems.push({
+            name: psiItem.name || psiItem.bacteriaName || '',
+            value: psiItem.abundance != null ? psiItem.abundance : (psiItem.bacteriaValue || ''),
+            range: psiItem.normalRange || '',
+            status: psiItem.populationLevel || psiItem.level || psiItem.status || ''
+          })
+        }
+        blocks.push({
+          type: 'list', title: '益生菌种',
+          columns: [{ key: 'name', label: '菌种' }, { key: 'value', label: '数值' }, { key: 'range', label: '正常范围' }, { key: 'status', label: '状态' }],
+          items: psItems
+        })
+      }
+
+      // 7. 分类学列表(纲/目/科/属/种)
+      var taxLabels = [
+        { key: 'taxonomyClass', label: '菌纲 (Class)' },
+        { key: 'taxonomyOrder', label: '菌目 (Order)' },
+        { key: 'taxonomyFamily', label: '菌科 (Family)' },
+        { key: 'taxonomyGenus', label: '菌属 (Genus)' },
+        { key: 'taxonomySpecies', label: '菌种 (Species)' }
+      ]
+      for (var tli = 0; tli < taxLabels.length; tli++) {
+        var tl = taxLabels[tli]
+        var taxList = p[tl.key] || []
+        if (taxList.length > 0) {
+          var taxItems = []
+          for (var txi = 0; txi < taxList.length; txi++) {
+            var tx = taxList[txi]
+            taxItems.push({ name: tx.name || '', value: tx.abundance != null ? tx.abundance : '' })
+          }
+          blocks.push({
+            type: 'list', title: tl.label,
+            columns: [{ key: 'name', label: '名称' }, { key: 'value', label: '丰度' }],
+            items: taxItems
+          })
+        }
+      }
+
+      return blocks
+    },
+    _addScoreItem: function(items, label, value) {
+      if (value == null || value === '') return
+      items.push({ label: label, value: value, max: 100 })
+    },
+    _addSubScore: function(items, parentLabel, label, value) {
+      if (value == null) return
+      items.push({ parentLabel: parentLabel, label: label, value: value, max: 100 })
     }
   }
 }
@@ -920,8 +1091,6 @@ export default {
   font-size: 26rpx;
   padding: 60rpx 0;
 }
-.payload-fallback { padding: 0; }
-.payload-fallback .score-text { font-size: 48rpx; font-weight: bold; color: #4A9BD7; text-align: center; display: block; padding: 20rpx 0; }
 .edit-btn {
   margin: 30rpx 0;
   display: flex;