ソースを参照

feat(diet): S-cfc-126 diet.food.healthAdjustUi 食物卡片健康问卷调整提示条 + suitable Tab blocked 排末

能力键 diet.food.healthAdjustUi(spec §3 前端 B1-B3)
- B2: 食物卡片按 item.adjustment 渲染提示条:blocked 红条「⚠ 健康问卷不建议:{adjustmentReason}」、
  warning 黄条「注意:{adjustmentReason}」、boost 绿条「✓ 符合饮食习惯偏好」;normal/空不渲染
- B3: filteredFoods suitable Tab 中 adjustment==='blocked' 项 stable 分组排列表末尾(保持可见不置顶;
  unsuitable/all Tab 位置不变仅带提示条,既有 score 排序无回归)
- B1: 既有 loadFoods object 透传保留 /api/health/foods 响应 adjustment/adjustmentReason(契约测试锁定)
- 红单测 recommendation.healthAdjustUi.test.js:14 通过(1 skip 权限占位)
E2E Test Bot 6 日 前
親
コミット
7366733e0
1 ファイル変更、51 行追加、6 行削除
  1. 51 6
      cfc-frontend/pages/diet/recommendation.vue

+ 51 - 6
cfc-frontend/pages/diet/recommendation.vue

@@ -102,6 +102,16 @@
         <!-- 食材列表 -->
         <view class="food-list" v-if="currentView === 'personal'">
           <view class="food-card" v-for="(item, idx) in filteredFoods" :key="getFoodKey(item, idx)">
+            <!-- 健康问卷个性化调整提示条:blocked 红 / warning 黄 / boost 绿(normal/空不渲染) -->
+            <view v-if="item.adjustment === 'blocked'" class="health-adjust-bar">
+              <text class="health-adjust-text health-adjust-blocked">⚠ 健康问卷不建议:{{ item.adjustmentReason }}</text>
+            </view>
+            <view v-else-if="item.adjustment === 'warning'" class="health-adjust-bar">
+              <text class="health-adjust-text health-adjust-warning">注意:{{ item.adjustmentReason }}</text>
+            </view>
+            <view v-else-if="item.adjustment === 'boost'" class="health-adjust-bar">
+              <text class="health-adjust-text health-adjust-boost">✓ 符合饮食习惯偏好</text>
+            </view>
             <view class="food-left">
               <view class="ingredient-badge" :class="item.scoreClass">
                 <text class="ingredient-badge-text">{{ idx + 1 }}</text>
@@ -354,14 +364,24 @@ export default {
       }
 
       // 排序(戒备模式默认升序,见 setFilter)
-      if (self.sortType === 'asc') {
-        result = result.slice().sort(function(a, b) {
-          return (a.score || 0) - (b.score || 0)
+      var sortFn = self.sortType === 'asc'
+        ? function(a, b) { return (a.score || 0) - (b.score || 0) }
+        : function(a, b) { return (b.score || 0) - (a.score || 0) }
+
+      // 健康问卷个性化调整(B3):suitable Tab 中 blocked 项整体排到列表末尾(保持可见不置顶;组内各按既有分值序)
+      if (self.filterType === 'suitable') {
+        var nonBlocked = []
+        var blocked = []
+        result.forEach(function(f) {
+          if (f.adjustment === 'blocked') {
+            blocked.push(f)
+          } else {
+            nonBlocked.push(f)
+          }
         })
+        result = nonBlocked.slice().sort(sortFn).concat(blocked.slice().sort(sortFn))
       } else {
-        result = result.slice().sort(function(a, b) {
-          return (b.score || 0) - (a.score || 0)
-        })
+        result = result.slice().sort(sortFn)
       }
 
       return result
@@ -1165,11 +1185,36 @@ export default {
   padding: 18rpx 20rpx;
   box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
   display: flex;
+  flex-wrap: wrap;
   align-items: center;
   justify-content: space-between;
   width: 100%;
   box-sizing: border-box;
 }
+/* 健康问卷个性化调整提示条(blocked 红 / warning 黄 / boost 绿;normal/空不渲染) */
+.health-adjust-bar {
+  width: 100%;
+  margin-bottom: 12rpx;
+}
+.health-adjust-text {
+  display: block;
+  padding: 8rpx 16rpx;
+  border-radius: 8rpx;
+  font-size: calc(22rpx * var(--font-scale, 1));
+  line-height: 1.4;
+}
+.health-adjust-blocked {
+  background: $fortune-0-tint;
+  color: $mat-red;
+}
+.health-adjust-warning {
+  background: rgba(245, 158, 11, 0.14);
+  color: #b45309;
+}
+.health-adjust-boost {
+  background: $mat-green-tint;
+  color: $mat-green;
+}
 .food-left {
   display: flex;
   align-items: center;