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

feat(diet): 成长页跳转带 memberId + 食材页分类/排序/溢出修复

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
E2E Test Bot 1 месяц назад
Родитель
Сommit
1dbb9130e9

+ 4 - 1
cfc-frontend/pages/growth-main/index.vue

@@ -309,7 +309,10 @@ export default {
     goToTasks: function() { uni.navigateTo({ url: '/pages/child/tasks' }) },
     handleGuestFeatureTap: function() { uni.showToast({ title: '登录后即可体验该功能', icon: 'none' }) },
     goToCaution: function() { uni.navigateTo({ url: '/pages/growth/caution-detail' }) },
-    goToFoods: function() { uni.navigateTo({ url: '/pages/health/gut-flora-foods-detail' }) },
+    goToFoods: function() {
+      var mid = uni.getStorageSync('currentChildId') || uni.getStorageSync('currentMemberId') || ''
+      uni.navigateTo({ url: '/pages/health/gut-flora-foods-detail' + (mid ? '?memberId=' + mid : '') })
+    },
     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 }) },
     goMoreProducts: function() { uni.navigateTo({ url: '/pages/shop/index/index' }) },

+ 80 - 5
cfc-frontend/pages/health/gut-flora-foods-detail.vue

@@ -9,6 +9,33 @@
       </view>
     </view>
 
+    <!-- 分类筛选 + 排序 -->
+    <view class="filter-bar">
+      <scroll-view class="cat-scroll" scroll-x>
+        <view class="cat-list">
+          <view
+            class="cat-chip"
+            :class="{'cat-chip-active': activeCategory === 'all'}"
+            @tap="switchCategory('all')"
+          >
+            <text class="cat-chip-text">全部</text>
+          </view>
+          <view
+            class="cat-chip"
+            v-for="cat in categories"
+            :key="cat"
+            :class="{'cat-chip-active': activeCategory === cat}"
+            @tap="switchCategory(cat)"
+          >
+            <text class="cat-chip-text">{{ cat }}</text>
+          </view>
+        </view>
+      </scroll-view>
+      <view class="sort-btn" @tap="toggleSort">
+        <text class="sort-btn-text">{{ sortDesc ? '↓ 推荐值从高到低' : '↑ 推荐值从低到高' }}</text>
+      </view>
+    </view>
+
     <scroll-view class="content" scroll-y>
       <view
         class="food-card"
@@ -92,38 +119,74 @@ export default {
   data() {
     return {
       reportId: null,
+      memberId: null,
       reportData: null,
       foods: [],
       isEditing: false,
       editFoods: [],
       searchText: '',
+      activeCategory: 'all',
+      sortDesc: true,
       knowledgeVisible: false,
       knowledgeData: {},
       _kbCache: {}
     }
   },
   computed: {
+    categories: function() {
+      var seen = {}
+      var cats = []
+      var list = this.isEditing ? this.editFoods : this.foods
+      for (var i = 0; i < list.length; i++) {
+        var c = list[i].category || '其它'
+        if (!seen[c]) {
+          seen[c] = true
+          cats.push(c)
+        }
+      }
+      return cats
+    },
     filteredFoods: function() {
       var list = this.isEditing ? this.editFoods : this.foods
       var kw = (this.searchText || '').trim()
+      var self = this
       if (kw) {
         list = list.filter(function(item) {
           var name = item.foodName || item.name || ''
           return name.indexOf(kw) >= 0
         })
       }
+      if (this.activeCategory !== 'all') {
+        list = list.filter(function(item) {
+          return (item.category || '其它') === self.activeCategory
+        })
+      }
+      // 按推荐值排序(默认降序)
+      list = list.slice()
+      list.sort(function(a, b) {
+        var sa = parseFloat(a.score) || 0
+        var sb = parseFloat(b.score) || 0
+        return self.sortDesc ? sb - sa : sa - sb
+      })
       return list
     }
   },
   onLoad: function(options) {
     // 不传 reportId 时取当前用户最新报告的食材推荐
     this.reportId = options.reportId ? parseInt(options.reportId) : null
+    this.memberId = options.memberId ? parseInt(options.memberId) : null
     this.loadData()
   },
   methods: {
     goBack: function() {
       uni.navigateBack()
     },
+    switchCategory: function(cat) {
+      this.activeCategory = cat
+    },
+    toggleSort: function() {
+      this.sortDesc = !this.sortDesc
+    },
     /** 推荐值 → 背景色分级:<-30 红,>30 绿,中间每 10 换一档 */
     _scoreBand: function(score) {
       var s = parseFloat(score)
@@ -139,7 +202,7 @@ export default {
     },
     loadData: function() {
       var self = this
-      getHealthFoods(self.reportId).then(function(res) {
+      getHealthFoods(self.reportId, self.memberId).then(function(res) {
         if (res.code === 200 && res.data) {
           // 后端 Food 实体字段:name, category, score;兼容 foodName(旧 draft)
           self.foods = (Array.isArray(res.data) ? res.data : []).map(function(item) {
@@ -242,7 +305,7 @@ export default {
 .page { min-height: 100vh; background: #f8f8f8; }
 
 /* 搜索框 */
-.search-bar { padding: 20rpx 30rpx; background: #fff; }
+.search-bar { padding: 20rpx 30rpx 8rpx; background: #fff; }
 .search-input-wrap {
   display: flex;
   align-items: center;
@@ -254,7 +317,18 @@ export default {
 .search-input { flex: 1; font-size: 28rpx; }
 .search-clear { font-size: 28rpx; color: #999; padding: 0 8rpx; }
 
-.content { padding: 20rpx 30rpx; height: calc(100vh - 120rpx); }
+/* 分类筛选 + 排序 */
+.filter-bar { display: flex; align-items: center; background: #fff; padding: 8rpx 30rpx 16rpx; box-sizing: border-box; width: 100%; }
+.cat-scroll { flex: 1; white-space: nowrap; min-width: 0; }
+.cat-list { display: inline-flex; padding-right: 8rpx; }
+.cat-chip { display: inline-flex; align-items: center; padding: 10rpx 24rpx; margin-right: 12rpx; background: #f5f5f5; border-radius: 30rpx; flex-shrink: 0; }
+.cat-chip-active { background: #4A9BD7; }
+.cat-chip-text { font-size: 24rpx; color: #666; }
+.cat-chip-active .cat-chip-text { color: #fff; }
+.sort-btn { flex-shrink: 0; margin-left: 16rpx; padding: 10rpx 20rpx; background: #FFF1E6; border-radius: 30rpx; }
+.sort-btn-text { font-size: 24rpx; color: #C2410C; }
+
+.content { padding: 20rpx 30rpx; height: calc(100vh - 200rpx); }
 
 .food-card {
   border-radius: 16rpx;
@@ -274,8 +348,9 @@ export default {
 .score-band-null { background: #f0f0f0; }
 
 .food-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8rpx; }
-.food-name { font-size: 30rpx; font-weight: 600; color: #fff; flex: 1; }
-.food-cat-tag { padding: 4rpx 16rpx; background: rgba(255,255,255,0.3); border-radius: 20rpx; font-size: 22rpx; color: #fff; }
+.food-name { font-size: 30rpx; font-weight: 600; color: #fff; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+.food-cat-tag { padding: 4rpx 16rpx; background: rgba(255,255,255,0.3); border-radius: 20rpx; font-size: 22rpx; color: #fff; flex-shrink: 0; margin-left: 12rpx; max-width: 40%; box-sizing: border-box; }
+.food-cat-tag text { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
 
 .food-score-row { display: flex; align-items: baseline; gap: 12rpx; }
 .food-score-label { font-size: 22rpx; color: rgba(255,255,255,0.85); }

+ 6 - 1
cfc-frontend/utils/api.js

@@ -2722,7 +2722,12 @@ 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 })
+export const getHealthFoods = (reportId, memberId) => {
+  var data = {}
+  if (reportId) data.reportId = reportId
+  if (memberId) data.memberId = memberId
+  return request('/api/health/foods', 'POST', data)
+}
 
 // ===== 家庭平台积分(CF值)=====
 export const getFamilyPlatformBalance = () => request('/api/family-platform/balance', 'POST', {})