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

feat: 食材推荐高频入口前置,合并页统一导航

- 首页快捷栏新增「🥗 食材」入口,1 击直达 food-query
- 成长 Tab 饮食推荐 / 食材戒备卡均改跳合并页 food-query
- 菌群报告页戒备入口改跳 food-query(带 reportId/memberId,进入默认戒备模式)
- food-query.vue 重写:支持成员切换、排序选择、红绿适合/戒备配色、家庭总览聚合
E2E Test Bot 13 часов назад
Родитель
Сommit
39605069ff

+ 484 - 54
cfc-frontend/pages/diet/food-query.vue

@@ -1,12 +1,43 @@
 <template>
 <template>
   <view class="food-query-page">
   <view class="food-query-page">
+    <!-- 成员切换条 -->
+    <view class="member-bar" v-if="memberList.length > 1 || familyMode">
+      <view class="member-tab"
+        :class="{'active': currentView === 'family'}"
+        @tap="switchView('family')">
+        <text class="member-name">👨‍👩‍👧 家庭总览</text>
+      </view>
+      <view class="member-tab"
+        :class="{'active': currentView === 'personal'}"
+        @tap="switchView('personal')">
+        <text class="member-name">👤 个人</text>
+      </view>
+      <view class="member-tab"
+        v-for="(m, mi) in memberList"
+        :key="m.userId"
+        :class="{'active': currentMemberId === m.userId}"
+        @tap="switchMember(m)">
+        <text class="member-name">{{ m.nickname || m.roleLabel || '成员' }}</text>
+      </view>
+    </view>
 
 
-    <view class="search-bar">
-      <input class="search-input" placeholder="搜索食材..." v-model="searchQuery" @confirm="searchFood" />
-      <view class="search-btn" @tap="searchFood">搜索</view>
+    <!-- 模式切换(仅家庭模式展示) -->
+    <view class="mode-bar" v-if="familyMode">
+      <view class="mode-btn" :class="{'active': sortType === 'desc'}" @tap="setSort('desc')">
+        <text>推荐值 ↓</text>
+      </view>
+      <view class="mode-btn" :class="{'active': sortType === 'asc'}" @tap="setSort('asc')">
+        <text>推荐值 ↑</text>
+      </view>
     </view>
     </view>
 
 
-    <scroll-view class="content" scroll-y>
+    <scroll-view class="content" scroll-y :refresher-enabled="true" :refresher-triggered="refreshing" @refresherrefresh="onRefresh">
+      <!-- 搜索栏 -->
+      <view class="search-bar">
+        <input class="search-input" placeholder="搜索食材..." v-model="searchQuery" @confirm="searchFood" />
+        <view class="search-btn" @tap="searchFood">搜索</view>
+      </view>
+
       <!-- 分类标签 -->
       <!-- 分类标签 -->
       <view class="category-tabs">
       <view class="category-tabs">
         <view class="tab-item" :class="{'tab-active': activeCategory === 'all'}" @tap="switchCategory('all')">
         <view class="tab-item" :class="{'tab-active': activeCategory === 'all'}" @tap="switchCategory('all')">
@@ -27,35 +58,82 @@
         <view class="tab-item" :class="{'tab-active': activeCategory === 'grain'}" @tap="switchCategory('grain')">
         <view class="tab-item" :class="{'tab-active': activeCategory === 'grain'}" @tap="switchCategory('grain')">
           <text>谷物</text>
           <text>谷物</text>
         </view>
         </view>
+        <view class="tab-item" :class="{'tab-active': activeCategory === 'dairy'}" @tap="switchCategory('dairy')">
+          <text>乳制品</text>
+        </view>
       </view>
       </view>
 
 
-      <!-- 筛选 -->
+      <!-- 筛选 -->
       <view class="filter-bar">
       <view class="filter-bar">
         <view class="filter-item" :class="{'filter-active': filterType === 'suitable'}" @tap="setFilter('suitable')">
         <view class="filter-item" :class="{'filter-active': filterType === 'suitable'}" @tap="setFilter('suitable')">
           <text>✅ 适合</text>
           <text>✅ 适合</text>
         </view>
         </view>
         <view class="filter-item" :class="{'filter-active': filterType === 'unsuitable'}" @tap="setFilter('unsuitable')">
         <view class="filter-item" :class="{'filter-active': filterType === 'unsuitable'}" @tap="setFilter('unsuitable')">
-          <text>❌ 不适合</text>
+          <text>❌ 戒备</text>
+        </view>
+        <view class="filter-item" :class="{'filter-active': filterType === 'all'}" @tap="setFilter('all')">
+          <text>📋 全部</text>
+        </view>
+      </view>
+
+      <!-- 家庭总览 -->
+      <view class="family-overview" v-if="currentView === 'family' && familyOverview && familyOverview.length > 0">
+        <view class="overview-title">
+          <text>🏠 家庭推荐概况(共 {{ familyMembers.length }} 人)</text>
+        </view>
+        <view class="overview-item" v-for="(item, idx) in familyOverview" :key="idx">
+          <view class="overview-header">
+            <text class="food-name">{{ item.name }}</text>
+            <view class="score-badge" :class="item.color">
+              <text>{{ item.avgScore }}</text>
+            </view>
+          </view>
+          <view class="overview-detail">
+            <text class="category-label">{{ item.category }}</text>
+            <view class="member-scores">
+              <text v-for="(s, mi) in item.memberScores" :key="mi"
+                class="member-score"
+                :class="s.score >= 0 ? 'score-green' : 'score-red'">
+                {{ s.name }}:{{ s.score >= 0 ? '+' : '' }}{{ s.score }}
+              </text>
+            </view>
+          </view>
         </view>
         </view>
       </view>
       </view>
 
 
       <!-- 食材列表 -->
       <!-- 食材列表 -->
-      <view class="food-list">
-        <view class="food-item" v-for="food in filteredFoods" :key="food.id">
-          <view class="food-info">
-            <text class="food-name">{{ food.name }}</text>
-            <text class="food-category">{{ food.category }}</text>
-            <text class="food-score" v-if="food.score">推荐分: {{ food.score }}</text>
+      <view class="food-list" v-if="currentView === 'personal'">
+        <view class="food-card" v-for="(item, idx) in filteredFoods" :key="item.id || idx">
+          <view class="food-left">
+            <view class="ingredient-badge" :class="item.scoreClass">
+              <text class="ingredient-badge-text">{{ idx + 1 }}</text>
+            </view>
+            <view class="food-info">
+              <text class="food-name">{{ item.name }}</text>
+              <view class="food-meta" v-if="item.category">
+                <text class="meta-item">{{ item.category }}</text>
+              </view>
+            </view>
           </view>
           </view>
-          <view class="food-status" :class="'status-' + food.status">
-            <text>{{ food.statusText }}</text>
+          <view class="food-right">
+            <text class="score-value" :class="item.scoreClass">{{ item.score || '--' }}</text>
+            <view class="status-badge" :class="'badge-' + item.status">
+              <text>{{ item.statusText }}</text>
+            </view>
           </view>
           </view>
         </view>
         </view>
       </view>
       </view>
 
 
-      <view class="empty-state" v-if="filteredFoods.length === 0">
-        <text>暂无食材数据</text>
+      <view class="empty-state" v-if="!loading && filteredFoods.length === 0 && currentView === 'personal'">
+        <text class="empty-icon">🥗</text>
+        <text class="empty-text">暂无匹配食材</text>
       </view>
       </view>
+
+      <view class="loading-state" v-if="loading">
+        <text class="loading-text">加载中...</text>
+      </view>
+
+      <view class="bottom-spacer"></view>
     </scroll-view>
     </scroll-view>
   </view>
   </view>
 </template>
 </template>
@@ -69,65 +147,214 @@ export default {
       searchQuery: '',
       searchQuery: '',
       activeCategory: 'all',
       activeCategory: 'all',
       filterType: 'suitable',
       filterType: 'suitable',
+      sortType: 'desc',
+      currentView: 'personal',
       foods: [],
       foods: [],
-      allFoods: []
+      allFoods: [],
+      familyData: null,
+      familyMembers: [],
+      familyOverview: [],
+      currentMemberId: null,
+      memberList: [],
+      loading: false,
+      refreshing: false,
+      reportId: null
     }
     }
   },
   },
   computed: {
   computed: {
     filteredFoods: function() {
     filteredFoods: function() {
       var self = this
       var self = this
-      var result = self.allFoods
-      
+      var result = self.currentView === 'family'
+        ? self.familyOverview
+        : self.allFoods
+
       // 分类筛选
       // 分类筛选
       if (self.activeCategory !== 'all') {
       if (self.activeCategory !== 'all') {
         result = result.filter(function(f) {
         result = result.filter(function(f) {
-          return f.category === self.activeCategory
+          return f.category && f.category.indexOf(self.activeCategory) !== -1
         })
         })
       }
       }
-      
+
       // 搜索筛选
       // 搜索筛选
       if (self.searchQuery) {
       if (self.searchQuery) {
         result = result.filter(function(f) {
         result = result.filter(function(f) {
-          return f.name.indexOf(self.searchQuery) >= 0
+          return (f.name && f.name.indexOf(self.searchQuery) !== -1) ||
+                 (f.category && f.category.indexOf(self.searchQuery) !== -1)
         })
         })
       }
       }
-      
-      // 适合/不适合筛选
+
+      // 适合/戒备筛选
       if (self.filterType === 'suitable') {
       if (self.filterType === 'suitable') {
         result = result.filter(function(f) {
         result = result.filter(function(f) {
           return f.score >= 50
           return f.score >= 50
         })
         })
-      } else {
+      } else if (self.filterType === 'unsuitable') {
         result = result.filter(function(f) {
         result = result.filter(function(f) {
           return f.score < 50
           return f.score < 50
         })
         })
       }
       }
-      
+
+      // 排序
+      if (self.sortType === 'asc') {
+        result = result.slice().sort(function(a, b) {
+          return (a.score || 0) - (b.score || 0)
+        })
+      } else {
+        result = result.slice().sort(function(a, b) {
+          return (b.score || 0) - (a.score || 0)
+        })
+      }
+
       return result
       return result
     }
     }
   },
   },
-  onLoad: function() {
+  onLoad: function(options) {
+    this.reportId = options.reportId ? parseInt(options.reportId) : null
+    this.currentMemberId = options.memberId ? parseInt(options.memberId) : null
+    
+    // 从报告入口进来时,默认显示戒备模式
+    if (this.reportId) {
+      this.filterType = 'unsuitable'
+    }
+    
+    this.loadMembers()
     this.loadFoods()
     this.loadFoods()
+    this.loadFamilyData()
   },
   },
   methods: {
   methods: {
     goBack: function() {
     goBack: function() {
       uni.navigateBack()
       uni.navigateBack()
     },
     },
+    loadMembers: function() {
+      var self = this
+      var token = uni.getStorageSync('token')
+      if (!token) return
+      
+      uni.request({
+        url: config.API_BASE_URL + '/api/food/recommendation/members',
+        method: 'POST',
+        header: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token },
+        success: function(res) {
+          if (res.data && res.data.code === 200 && res.data.data) {
+            self.memberList = res.data.data
+            if (!self.currentMemberId && self.memberList.length > 0) {
+              self.currentMemberId = self.memberList[0].userId
+            }
+          }
+        }
+      })
+    },
     loadFoods: function() {
     loadFoods: function() {
       var self = this
       var self = this
       var token = uni.getStorageSync('token')
       var token = uni.getStorageSync('token')
       if (!token) return
       if (!token) return
       
       
+      self.loading = true
+      var url = config.API_BASE_URL + '/api/health/foods'
+      var data = {}
+      if (self.reportId) data.reportId = self.reportId
+      if (self.currentMemberId) data.memberId = self.currentMemberId
+      
       uni.request({
       uni.request({
-        url: this.getApiUrl('/api/health/foods'),
+        url: url,
         method: 'POST',
         method: 'POST',
+        data: data,
         header: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token },
         header: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token },
         success: function(res) {
         success: function(res) {
+          self.loading = false
           if (res.data && res.data.code === 200 && res.data.data) {
           if (res.data && res.data.code === 200 && res.data.data) {
-            self.allFoods = res.data.data
+            self.allFoods = res.data.data.map(function(f) {
+              f.scoreClass = self.getScoreClass(f.score)
+              f.statusText = self.getStatusText(f.score)
+              f.status = f.score >= 50 ? 'suitable' : 'unsuitable'
+              return f
+            })
+          }
+        },
+        fail: function() {
+          self.loading = false
+        }
+      })
+    },
+    loadFamilyData: function() {
+      var self = this
+      var token = uni.getStorageSync('token')
+      if (!token) return
+      
+      var familyId = uni.getStorageSync('familyId')
+      if (!familyId) return
+      
+      uni.request({
+        url: config.API_BASE_URL + '/api/health/foods/by-family',
+        method: 'POST',
+        header: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token },
+        success: function(res) {
+          if (res.data && res.data.code === 200 && res.data.data) {
+            self.familyData = res.data.data
+            self.familyMembers = Object.keys(res.data.data.byMember || {}).map(function(key) {
+              return { userId: key, nickname: res.data.data.memberNames[key] || '成员' }
+            })
+            self.buildFamilyOverview(res.data.data.totalList, res.data.data.byMember, res.data.data.memberNames)
+          }
+        }
+      })
+    },
+    buildFamilyOverview: function(totalList, byMember, memberNames) {
+      var self = this
+      var overview = []
+      for (var i = 0; i < totalList.length; i++) {
+        var food = totalList[i]
+        var memberScores = []
+        var totalScore = 0
+        var count = 0
+        
+        for (var j = 0; j < self.familyMembers.length; j++) {
+          var member = self.familyMembers[j]
+          var memberFoods = byMember[member.userId]
+          if (memberFoods) {
+            for (var k = 0; k < memberFoods.length; k++) {
+              if (memberFoods[k].foodId === food.id) {
+                var score = memberFoods[k].indexScore || 0
+                memberScores.push({ name: member.nickname, score: score })
+                totalScore += score
+                count++
+                break
+              }
+            }
           }
           }
         }
         }
+        
+        if (memberScores.length > 0) {
+          var avgScore = Math.round(totalScore / memberScores.length)
+          overview.push({
+            id: food.id,
+            name: food.name,
+            category: food.category,
+            score: avgScore,
+            scoreClass: self.getScoreClass(avgScore),
+            statusText: self.getStatusText(avgScore),
+            status: avgScore >= 50 ? 'suitable' : 'unsuitable',
+            memberScores: memberScores
+          })
+        }
+      }
+      
+      // 按平均推荐值排序(从高到低)
+      overview.sort(function(a, b) {
+        return b.score - a.score
       })
       })
+      
+      self.familyOverview = overview
+    },
+    getScoreClass: function(score) {
+      if (score === undefined || score === null) return 'neutral'
+      if (score >= 50) return 'good'
+      return 'bad'
+    },
+    getStatusText: function(score) {
+      if (score === undefined || score === null) return '未知'
+      if (score >= 50) return '适合'
+      return '戒备'
     },
     },
     searchFood: function() {
     searchFood: function() {
       // 搜索由 computed 自动处理
       // 搜索由 computed 自动处理
@@ -138,8 +365,24 @@ export default {
     setFilter: function(type) {
     setFilter: function(type) {
       this.filterType = type
       this.filterType = type
     },
     },
-    getApiUrl: function(path) {
-      return config.api(path)
+    setSort: function(type) {
+      this.sortType = type
+    },
+    switchView: function(view) {
+      this.currentView = view
+    },
+    switchMember: function(member) {
+      this.currentMemberId = member.userId
+      this.currentView = 'personal'
+      this.loadFoods()
+    },
+    onRefresh: function() {
+      this.refreshing = true
+      this.loadFoods()
+      this.loadFamilyData()
+      setTimeout(function() {
+        this.refreshing = false
+      }.bind(this), 500)
     }
     }
   }
   }
 }
 }
@@ -152,9 +395,54 @@ export default {
   display: flex;
   display: flex;
   flex-direction: column;
   flex-direction: column;
 }
 }
-.nav-placeholder {
-  width: 80rpx;
+
+/* 成员切换条 */
+.member-bar {
+  display: flex;
+  overflow-x: auto;
+  padding: 16rpx 30rpx 8rpx;
+  background: #fff;
+  border-bottom: 1rpx solid #f0f0f0;
+  gap: 16rpx;
+  -webkit-overflow-scrolling: touch;
+}
+.member-tab {
+  flex-shrink: 0;
+  padding: 8rpx 20rpx;
+  border-radius: 20rpx;
+  background: #f5f5f5;
+  color: #666;
+  font-size: 26rpx;
+  font-weight: 500;
+}
+.member-tab.active {
+  background: linear-gradient(135deg, #FF8C42, #FFB366);
+  color: #fff;
+}
+
+/* 模式切换(排序) */
+.mode-bar {
+  display: flex;
+  padding: 16rpx 30rpx;
+  background: #fff;
+  border-bottom: 1rpx solid #f0f0f0;
+  gap: 20rpx;
+}
+.mode-btn {
+  flex: 1;
+  padding: 12rpx 0;
+  text-align: center;
+  border-radius: 20rpx;
+  background: #f5f5f5;
+  color: #666;
+  font-size: 26rpx;
 }
 }
+.mode-btn.active {
+  background: linear-gradient(135deg, #10B981, #34D399);
+  color: #fff;
+}
+
+/* 搜索栏 */
 .search-bar {
 .search-bar {
   display: flex;
   display: flex;
   padding: 20rpx 30rpx;
   padding: 20rpx 30rpx;
@@ -176,10 +464,13 @@ export default {
   border-radius: 30rpx;
   border-radius: 30rpx;
   font-size: 26rpx;
   font-size: 26rpx;
 }
 }
+
 .content {
 .content {
   flex: 1;
   flex: 1;
   padding: 20rpx 30rpx;
   padding: 20rpx 30rpx;
 }
 }
+
+/* 分类标签 */
 .category-tabs {
 .category-tabs {
   display: flex;
   display: flex;
   flex-wrap: wrap;
   flex-wrap: wrap;
@@ -199,6 +490,8 @@ export default {
   color: #FF8C42;
   color: #FF8C42;
   background-color: #FFF5EB;
   background-color: #FFF5EB;
 }
 }
+
+/* 筛选栏 */
 .filter-bar {
 .filter-bar {
   display: flex;
   display: flex;
   gap: 20rpx;
   gap: 20rpx;
@@ -217,55 +510,192 @@ export default {
   color: #4CAF50;
   color: #4CAF50;
   background-color: #E8F5E9;
   background-color: #E8F5E9;
 }
 }
+
+/* 家庭总览 */
+.family-overview {
+  background: #fff;
+  border-radius: 16rpx;
+  padding: 20rpx;
+  margin-bottom: 20rpx;
+}
+.overview-title {
+  font-size: 28rpx;
+  font-weight: 600;
+  color: #333;
+  margin-bottom: 16rpx;
+  padding-bottom: 16rpx;
+  border-bottom: 1rpx solid #f0f0f0;
+}
+.overview-item {
+  padding: 16rpx 0;
+  border-bottom: 1rpx solid #f5f5f5;
+}
+.overview-item:last-child {
+  border-bottom: none;
+}
+.overview-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-bottom: 8rpx;
+}
+.overview-detail {
+  display: flex;
+  flex-direction: column;
+  gap: 8rpx;
+}
+.category-label {
+  font-size: 22rpx;
+  color: #999;
+}
+.member-scores {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 12rpx;
+}
+.member-score {
+  font-size: 20rpx;
+  padding: 4rpx 12rpx;
+  border-radius: 12rpx;
+  background: #f5f5f5;
+}
+.score-green {
+  color: #10B981;
+  background: #E8F5E9;
+}
+.score-red {
+  color: #EF4444;
+  background: #FEE2E2;
+}
+
+/* 食材列表 */
 .food-list {
 .food-list {
   display: flex;
   display: flex;
   flex-direction: column;
   flex-direction: column;
   gap: 16rpx;
   gap: 16rpx;
 }
 }
-.food-item {
+.food-card {
+  background: #fff;
+  border-radius: 16rpx;
+  padding: 18rpx 20rpx;
+  box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
   display: flex;
   display: flex;
+  align-items: center;
   justify-content: space-between;
   justify-content: space-between;
+  width: 100%;
+  box-sizing: border-box;
+}
+.food-left {
+  display: flex;
   align-items: center;
   align-items: center;
-  background-color: #FFFFFF;
-  padding: 24rpx 30rpx;
-  border-radius: 16rpx;
-  box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.04);
+  flex: 1;
+  min-width: 0;
+}
+.ingredient-badge {
+  width: 36rpx;
+  height: 36rpx;
+  border-radius: 10rpx;
+  background: linear-gradient(135deg, #FF8C42, #FFB366);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-right: 16rpx;
+  flex-shrink: 0;
+}
+.ingredient-badge.good {
+  background: linear-gradient(135deg, #10B981, #34D399);
+}
+.ingredient-badge.bad {
+  background: linear-gradient(135deg, #EF4444, #DC2626);
+}
+.ingredient-badge-text {
+  font-size: 18rpx;
+  color: #FFFFFF;
+  font-weight: 600;
 }
 }
 .food-info {
 .food-info {
   display: flex;
   display: flex;
   flex-direction: column;
   flex-direction: column;
-  gap: 8rpx;
+  flex: 1;
+  min-width: 0;
+  overflow: hidden;
 }
 }
 .food-name {
 .food-name {
   font-size: 28rpx;
   font-size: 28rpx;
-  font-weight: 500;
-  color: #333;
+  font-weight: 600;
+  color: #1a1a1a;
+  display: block;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
 }
 }
-.food-category {
-  font-size: 22rpx;
-  color: #999;
+.food-meta {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8rpx;
+  margin-top: 6rpx;
 }
 }
-.food-score {
-  font-size: 22rpx;
-  color: #FF8C42;
+.meta-item {
+  flex-shrink: 0;
+  font-size: 20rpx;
+  color: #888;
+  background: #f5f5f5;
+  padding: 4rpx 14rpx;
+  border-radius: 8rpx;
 }
 }
-.food-status {
-  padding: 8rpx 16rpx;
+.food-right {
+  display: flex;
+  align-items: center;
+  gap: 12rpx;
+  flex-shrink: 0;
+}
+.score-value {
+  font-size: 28rpx;
+  font-weight: 700;
+  min-width: 60rpx;
+  text-align: right;
+}
+.score-value.good { color: #10B981; }
+.score-value.bad { color: #EF4444; }
+.status-badge {
+  padding: 6rpx 16rpx;
   border-radius: 20rpx;
   border-radius: 20rpx;
   font-size: 22rpx;
   font-size: 22rpx;
+  font-weight: 500;
 }
 }
-.status-suitable {
-  background-color: #E8F5E9;
+.badge-suitable {
+  background: #E8F5E9;
   color: #4CAF50;
   color: #4CAF50;
 }
 }
-.status-unsuitable {
-  background-color: #FFEBEE;
+.badge-unsuitable {
+  background: #FFEBEE;
   color: #F44336;
   color: #F44336;
 }
 }
+
+/* 空状态 */
 .empty-state {
 .empty-state {
   text-align: center;
   text-align: center;
-  padding: 60rpx 0;
+  padding: 100rpx 40rpx;
+}
+.empty-icon {
+  font-size: 80rpx;
+  display: block;
+  margin-bottom: 20rpx;
+}
+.empty-text {
+  font-size: 30rpx;
+  color: #333;
+  font-weight: 500;
+  display: block;
+  margin-bottom: 12rpx;
+}
+.loading-state {
+  text-align: center;
+  padding: 80rpx 0;
+}
+.loading-text {
   font-size: 26rpx;
   font-size: 26rpx;
   color: #999;
   color: #999;
 }
 }
+.bottom-spacer { height: 40rpx; }
 </style>
 </style>

+ 2 - 3
cfc-frontend/pages/growth-main/index.vue

@@ -312,11 +312,10 @@ export default {
     handleGuestFeatureTap: function() { uni.showToast({ title: '登录后即可体验该功能', icon: 'none' }) },
     handleGuestFeatureTap: function() { uni.showToast({ title: '登录后即可体验该功能', icon: 'none' }) },
     goToCaution: function() {
     goToCaution: function() {
       var mid = uni.getStorageSync('currentChildId') || uni.getStorageSync('currentMemberId') || ''
       var mid = uni.getStorageSync('currentChildId') || uni.getStorageSync('currentMemberId') || ''
-      uni.navigateTo({ url: '/pages/growth/caution-detail' + (mid ? '?memberId=' + mid : '') })
+      uni.navigateTo({ url: '/pages/diet/food-query' + (mid ? '?memberId=' + mid : '') })
     },
     },
     goToFoods: function() {
     goToFoods: function() {
-      // 饮食推荐入口回归原有饮食首页(今日共餐/菜篮/食谱推荐/共餐配置)
-      uni.navigateTo({ url: '/pages/diet/index' })
+      uni.navigateTo({ url: '/pages/diet/food-query' })
     },
     },
     goToArticle: function(article) { var id = article && article.id ? article.id : article; if (!id) return; uni.navigateTo({ url: '/pages/article-center/article-detail?id=' + id }) },
     goToArticle: function(article) { var id = article && article.id ? article.id : article; if (!id) return; uni.navigateTo({ url: '/pages/article-center/article-detail?id=' + id }) },
     goToProduct: function(prod) { var id = prod && prod.id ? prod.id : prod; if (!id) return; uni.navigateTo({ url: '/pages/discover-detail/product-detail/product-detail?id=' + id }) },
     goToProduct: function(prod) { var id = prod && prod.id ? prod.id : prod; if (!id) return; uni.navigateTo({ url: '/pages/discover-detail/product-detail/product-detail?id=' + id }) },

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

@@ -1094,7 +1094,7 @@ applyDraftPayload: function(payload) {
     goToCaution: function() {
     goToCaution: function() {
       // 报告详情入口:传 reportId(按报告快照返回戒备食材)+ memberId(报告绑定主体 subjectId)
       // 报告详情入口:传 reportId(按报告快照返回戒备食材)+ memberId(报告绑定主体 subjectId)
       var mid = (this.report && this.report.subjectId) || uni.getStorageSync('currentMemberId') || ''
       var mid = (this.report && this.report.subjectId) || uni.getStorageSync('currentMemberId') || ''
-      var url = '/pages/growth/caution-detail?reportId=' + this.reportId
+      var url = '/pages/diet/food-query?reportId=' + this.reportId
       if (mid) url += '&memberId=' + mid
       if (mid) url += '&memberId=' + mid
       uni.navigateTo({ url: url })
       uni.navigateTo({ url: url })
     },
     },

+ 4 - 0
cfc-frontend/pages/index-home/index.vue

@@ -146,6 +146,10 @@
           <view class="quick-icon" style="background:rgba(249,115,22,0.15)"><text>🛒</text></view>
           <view class="quick-icon" style="background:rgba(249,115,22,0.15)"><text>🛒</text></view>
           <text class="quick-label">商城</text>
           <text class="quick-label">商城</text>
         </view>
         </view>
+        <view class="quick-item" @click="navTo('/pages/diet/food-query')">
+          <view class="quick-icon" style="background:rgba(16,185,129,0.15)"><text>🥗</text></view>
+          <text class="quick-label">食材</text>
+        </view>
       </view>
       </view>
     </view>
     </view>