Ver Fonte

fix(health): 菌群报告页圆环折行、quick-nav 显示关注数、饮食推荐走 /api/health/foods、菌群详情页标签折行

1) gut-flora-detail.vue: 圆环改为 flex-wrap 折行(去横向滚动);quick-nav 菌群详情显示 异常/总数,疾病风险显示 需关注/总数;删除菌群层级+全部指标两个卡片;foodCount 改用 /api/health/foods 接口。

2) gut-flora-foods-detail.vue: loadData 改用 getHealthFoods(reportId),不再依赖 report 详情里的 foods+KB 合并。

3) gut-flora-species-detail.vue: tabs 从 scroll-view scroll-x 改为 flex-wrap 折行(无横向滚动);默认 tab 从 core 改为 all(避免页面没内容)。

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
E2E Test Bot há 3 semanas atrás
pai
commit
6a66344305

+ 27 - 19
cfc-frontend/pages/health/gut-flora-detail.vue

@@ -23,37 +23,32 @@
         </view>
       </view>
 
-      <!-- 评分圆环 -->
-      <scroll-view class="scores-scroll" scroll-x>
+      <!-- 评分圆环(折行显示,无横向滚动) -->
+      <view class="scores-wrap">
         <view class="score-item" v-for="(score, idx) in scoreItems" :key="idx">
           <view class="score-ring" :style="'border-color: ' + score.color + '; color: ' + score.color">
             <text class="score-val">{{ score.value }}</text>
           </view>
           <text class="score-label">{{ score.label }}</text>
         </view>
-      </scroll-view>
+      </view>
 
       <!-- 快速跳转 -->
       <view class="quick-nav">
         <view class="nav-card" @tap="goToSpecies">
           <text class="nav-icon">🦠</text>
           <text class="nav-label">菌群详情</text>
-          <text class="nav-count">{{ floraCount }}种</text>
+          <text class="nav-count">{{ floraAbnormalCount }}/{{ floraCount }}种</text>
         </view>
         <view class="nav-card" @tap="goToPathogen">
           <text class="nav-icon">🔬</text>
           <text class="nav-label">病原菌</text>
           <text class="nav-count">{{ pathogenCount }}项</text>
         </view>
-        <view class="nav-card" @tap="goToTaxonomy">
-          <text class="nav-icon">📊</text>
-          <text class="nav-label">菌群层级</text>
-          <text class="nav-count">5级</text>
-        </view>
         <view class="nav-card" @tap="goToRisks">
           <text class="nav-icon">⚠️</text>
           <text class="nav-label">疾病风险</text>
-          <text class="nav-count">{{ riskCount }}项</text>
+          <text class="nav-count">{{ riskAttentionCount }}/{{ riskCount }}项</text>
         </view>
         <view class="nav-card" @tap="goToFoods">
           <text class="nav-icon">🍽️</text>
@@ -65,11 +60,6 @@
           <text class="nav-label">食材戒备</text>
           <text class="nav-count caution-count">{{ cautionCount }}项</text>
         </view>
-        <view class="nav-card" @tap="goToIndicators">
-          <text class="nav-icon">📋</text>
-          <text class="nav-label">全部指标</text>
-          <text class="nav-count">{{ indicatorCount }}项</text>
-        </view>
       </view>
 
       <!-- 菌群功能分析 -->
@@ -238,7 +228,7 @@
 </template>
 
 <script>
-import { getReportDetail, editHealthReport, queryKnowledgeBase, queryIndicatorKnowledge, getGutFloraAnalysis, confirmReportPreview, discardReportDraft, parseReportDraft } from '../../utils/api.js'
+import { getReportDetail, editHealthReport, queryKnowledgeBase, queryIndicatorKnowledge, getGutFloraAnalysis, confirmReportPreview, discardReportDraft, parseReportDraft, getHealthFoods, getFoodCautionList } from '../../utils/api.js'
 import HealthKnowledgePopup from '../../components/health-knowledge-popup.vue'
 import FeedbackPopup from '../../components/feedback-popup.vue'
 
@@ -267,7 +257,9 @@ export default {
       gutIndicators: [],
       nutritionGroups: [],
       floraCount: 0,
+      floraAbnormalCount: 0,
       riskCount: 0,
+      riskAttentionCount: 0,
       foodCount: 0,
       cautionCount: 0,
       indicatorCount: 0,
@@ -316,8 +308,24 @@ export default {
           self.buildScores(res.data)
           self.buildIndicators(res.data)
           self.floraCount = (res.data.gutFlora || []).length
+          // 菌群详情:统计异常/需关注的数量(状态非"正常"即视为需关注)
+          var flora = res.data.gutFlora || []
+          self.floraAbnormalCount = flora.filter(function(i) {
+            return i.status && i.status !== '正常'
+          }).length
           self.riskCount = (res.data.diseaseRisks || []).length
-          self.foodCount = (res.data.foods || []).length
+          // 疾病风险:统计需关注的数量(风险等级非"低风险"即视为需关注)
+          var risks = res.data.diseaseRisks || []
+          self.riskAttentionCount = risks.filter(function(r) {
+            return r.riskLevel && r.riskLevel !== '低风险'
+          }).length
+          // 饮食推荐:共用 /api/health/foods 接口获取数量
+          self.foodCount = 0
+          getHealthFoods(self.reportId).then(function(foodRes) {
+            if (foodRes && foodRes.code === 200 && foodRes.data) {
+              self.foodCount = (Array.isArray(foodRes.data) ? foodRes.data : []).length
+            }
+          }).catch(function() {})
           // 异步加载戒备食材数量
           var userId = uni.getStorageSync('userId')
           if (userId) {
@@ -651,8 +659,8 @@ export default {
 .badge-row { display: flex; gap: 16rpx; margin-top: 12rpx; }
 .badge { padding: 6rpx 20rpx; background: rgba(255,255,255,0.2); border-radius: 20rpx; font-size: 22rpx; }
 
-.scores-scroll { padding: 20rpx 0; white-space: nowrap; background: #fff; }
-.score-item { display: inline-flex; flex-direction: column; align-items: center; margin: 0 20rpx; }
+.scores-wrap { display: flex; flex-wrap: wrap; padding: 20rpx 10rpx; background: #fff; }
+.score-item { display: flex; flex-direction: column; align-items: center; margin: 12rpx 10rpx; width: calc(25% - 20rpx); }
 .score-ring { width: 100rpx; height: 100rpx; border-radius: 50%; border: 6rpx solid; display: flex; align-items: center; justify-content: center; font-size: 32rpx; font-weight: 700; margin-bottom: 8rpx; }
 .score-label { font-size: 22rpx; color: #666; }
 

+ 15 - 34
cfc-frontend/pages/health/gut-flora-foods-detail.vue

@@ -55,7 +55,7 @@
 </template>
 
 <script>
-import { getReportDetail, editHealthReport, getKnowledgeSection } from '../../utils/api.js'
+import { getReportDetail, editHealthReport, getHealthFoods } from '../../utils/api.js'
 
 export default {
   data() {
@@ -105,40 +105,21 @@ export default {
     },
     loadData: function() {
       var self = this
-      Promise.all([
-        getReportDetail(this.reportId),
-        getKnowledgeSection('食物库')
-      ]).then(function(results) {
-        var reportRes = results[0]
-        var kbRes = results[1]
-
-        // 构建 KB 食物 map
-        var kbFoodMap = {}
-        if (kbRes.code === 200 && kbRes.data && kbRes.data['数据']) {
-          var kbFoods = kbRes.data['数据']
-          for (var i = 0; i < kbFoods.length; i++) {
-            var f = kbFoods[i]
-            kbFoodMap[f['名称']] = f
-          }
-        }
-
-        if (reportRes.code === 200 && reportRes.data) {
-          self.reportData = reportRes.data
-          var reportFoods = reportRes.data.foods || []
-          // 合并 KB 营养数据(个人 JSON 只保存名称和推荐指数,营养数据从 KB 查)
-          self.foods = reportFoods.map(function(item) {
-            var name = item.foodName || item.name
-            var kb = kbFoodMap[name] || {}
+      getHealthFoods(this.reportId).then(function(res) {
+        if (res.code === 200 && res.data) {
+          // 后端 Food 实体字段:name, category, score, protein, fat, carbs, fiber, energyKj, status
+          // 兼容 foodName(旧 draft)和 name(实体),carb/carbs 双字段
+          self.foods = (res.data || []).map(function(item) {
             return {
-              foodName: name,
-              name: name,
-              score: item.score,
-              category: item.category || kb['分类'] || '其它',
-              protein: kb['蛋白g'] || item.protein,
-              fat: kb['脂肪g'] || item.fat,
-              carb: kb['碳水化合物g'] || item.carb,
-              fiber: kb['总膳食纤维g'] || item.fiber,
-              energy: kb['能量KJ'] || item.energy
+              foodName: item.name || item.foodName || item.name,
+              name: item.name || item.foodName || '',
+              score: item.score != null ? parseInt(item.score, 10) : 0,
+              category: item.category || '其它',
+              protein: item.protein != null ? parseFloat(item.protein) : 0,
+              fat: item.fat != null ? parseFloat(item.fat) : 0,
+              carb: item.carbs != null ? parseFloat(item.carbs) : (item.carb != null ? parseFloat(item.carb) : 0),
+              fiber: item.fiber != null ? parseFloat(item.fiber) : 0,
+              energy: item.energyKj != null ? parseFloat(item.energyKj) : (item.energy != null ? parseFloat(item.energy) : 0)
             }
           })
         }

+ 6 - 6
cfc-frontend/pages/health/gut-flora-species-detail.vue

@@ -2,13 +2,13 @@
   <view class="page">
 <text class="nav-edit" @tap="toggleEdit">{{ isEditing ? '完成' : '编辑' }}</text>
 
-    <scroll-view class="tabs" scroll-x>
+    <view class="tabs">
       <view class="tab-item" v-for="(tab, idx) in tabs" :key="idx"
             :class="'tab-' + (selectedTab === tab.key ? 'active' : '')"
             @tap="selectedTab = tab.key">
         <text>{{ tab.label }}</text>
       </view>
-    </scroll-view>
+    </view>
 
     <scroll-view class="content" scroll-y>
       <view class="flora-card" v-for="(item, idx) in filteredFlora" :key="idx">
@@ -61,7 +61,7 @@ export default {
       probioticSpecies: [],
       isEditing: false,
       editFlora: [],
-      selectedTab: 'core',
+      selectedTab: 'all',
       floraStatuses: ['正常', '偏低', '偏高', '缺乏', '不足', '过多'],
       knowledgeVisible: false,
       knowledgeData: {},
@@ -230,10 +230,10 @@ export default {
 <style scoped>
 .page { min-height: 100vh; background: #f8f8f8; }
 .nav-edit { font-size: 28rpx; color: #4A9BD7; padding: 10rpx 0; }
-.tabs { background: #fff; padding: 10rpx 20rpx; white-space: nowrap; border-bottom: 1rpx solid #eee; }
-.tab-item { display: inline-block; padding: 12rpx 24rpx; margin: 0 6rpx; border-radius: 30rpx; font-size: 24rpx; background: #f0f0f0; color: #666; }
+.tabs { background: #fff; padding: 10rpx 20rpx; display: flex; flex-wrap: wrap; border-bottom: 1rpx solid #eee; }
+.tab-item { display: inline-block; padding: 12rpx 24rpx; margin: 6rpx; border-radius: 30rpx; font-size: 24rpx; background: #f0f0f0; color: #666; }
 .tab-active { background: #4A9BD7; color: #fff; }
-.content { padding: 20rpx 30rpx; height: calc(100vh - 180rpx); }
+.content { padding: 20rpx 30rpx; height: calc(100vh - 200rpx); }
 .flora-card { background: #fff; border-radius: 16rpx; padding: 24rpx; margin-bottom: 16rpx; }
 .flora-header { display: flex; align-items: center; margin-bottom: 10rpx; }
 .flora-name { font-size: 28rpx; font-weight: 600; color: #333; flex: 1; }

+ 3 - 0
cfc-frontend/utils/api.js

@@ -2525,6 +2525,9 @@ export const getGrowthRecommendations = (category = 'health', limit = 3) =>
 // ===== 食材戒备匹配 =====
 export const getFoodCautionList = () => request('/api/food/recommendation/caution', 'POST', {})
 
+// ===== 健康报告食材推荐(共用 /api/health/foods)=====
+export const getHealthFoods = (reportId) => request('/api/health/foods', 'POST', { reportId })
+
 // ===== 家庭平台积分(CF值)=====
 export const getFamilyPlatformBalance = () => request('/api/family-platform/balance', 'POST', {})
 export const getFamilyPlatformLogs = (data) => request('/api/family-platform/logs', 'POST', data)