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

Merge remote-tracking branch 'origin/cfclub' into cfclub

E2E Test Bot 3 недель назад
Родитель
Сommit
04d44ed204

+ 497 - 0
cfc-frontend/docs/superpowers/plans/2026-08-28-diet-redesign.md

@@ -0,0 +1,497 @@
+# 共餐 & 食谱页面美化 实现计划
+
+> **面向 AI 代理的工作者:** 必需子技能:使用 superpowers:subagent-driven-development(推荐)或 superpowers:executing-plans 逐任务实现此计划。步骤使用复选框(`- [ ]`)语法来跟踪进度。
+
+**目标:** 按「🍳 暖灶 · 家的味道」设计重做三个活跃页面:`pages/diet/index.vue`(饮食首页)、`pages/diet/meal-config.vue`(共餐配置)、`pages/diet/recommendation.vue`(食谱推荐)。纯样式/布局/文案重做,零接口与业务逻辑改动。
+
+**架构:** 每个 `.vue` 在 scoped `<style>` 顶部定义页面级 CSS 令牌变量(主橙 `#FF8C42` 渐变、米底 `#FFF7ED`、深橙 `#C2410C`、嫩绿 `#10B981`),template 结构调整为新的视觉区块,methods/data 逻辑保持不变。
+
+**技术栈:** uni-app Vue 2(Options API),微信小程序目标;flexbox 布局;无新依赖。
+
+**规格文档:** `cfc-frontend/docs/superpowers/specs/2026-08-28-diet-redesign.md`(唯一视觉契约,本节不重复细节,务必对照实现)
+
+---
+
+## 文件结构
+
+| 文件 | 职责 | 变更类型 |
+|------|------|----------|
+| `cfc-frontend/pages/diet/index.vue` | 饮食首页:今日餐桌主视觉 + 今日菜篮 + 厨房导航 + 本周饮食 | 修改 template + style |
+| `cfc-frontend/pages/diet/meal-config.vue` | 共餐配置:胶囊 tabs + 渐变餐次卡 + 成员 chips | 修改 template + style |
+| `cfc-frontend/pages/diet/recommendation.vue` | 食谱推荐:空态插图 + 餐次菜单卡 + 营养汇总 | 修改 template + style |
+
+> 三个页面相互独立、无共享状态,可并行实现(同一文件多处改动时避免并发冲突,按任务顺序在各自 commit 边界内操作)。
+
+## 全局校验命令(每个任务提交前必跑)
+
+```bash
+# 1) 提取 .vue 的 <script> 块做 JS 语法校验(在 cfc-frontend 下)
+node -e "
+const fs=require('fs');
+const s=fs.readFileSync(process.argv[1],'utf8');
+const m=s.match(/<script>([\s\S]*?)<\/script>/);
+if(!m){console.log('no script');process.exit(0)}
+new Function(m[1].replace(/import\s[^;]+;/g,'').replace(/export\s+default/,'return'));
+console.log('script OK');
+" pages/diet/index.vue
+
+# 2) 禁止模式扫描(输出应为空)
+grep -nE '\?\.|display:\s*grid|:key="[^"]*(\|\||&&|\+)' pages/diet/index.vue pages/diet/meal-config.vue pages/diet/recommendation.vue
+
+# 3) 中文类名扫描(输出应为空)
+grep -nE 'class="[^"]*[\u4e00-\u9fa5]|\.\w*[\u4e00-\u9fa5]' pages/diet/index.vue pages/diet/meal-config.vue pages/diet/recommendation.vue
+```
+
+> 注意:代理禁止执行 `npm run build:mp-weixin` / `npm run dev:mp-weixin`(项目规范),打包由用户用 HBuilderX 完成。
+
+---
+
+## 任务 1:`diet/index.vue` — 今日餐桌主视觉(替换原 page-header + 今日共餐卡片)
+
+**文件:** 修改 `cfc-frontend/pages/diet/index.vue`
+
+- [ ] **步骤 1:重写页面顶部 template(保留数据绑定,改造区块)**
+
+将原 `<view class="page-header">...</view>` + 「今日共餐」`<view class="card">` 合并为一个暖橙渐变主视觉大卡:
+
+```html
+<!-- ===== 今日餐桌主视觉 ===== -->
+<view class="dining-hero">
+  <view class="hero-top">
+    <view class="hero-icon-wrap"><text class="hero-icon">🍳</text></view>
+    <view class="hero-text">
+      <text class="hero-title">今日共餐</text>
+      <text class="hero-subtitle">{{ heroSubtitle }}</text>
+    </view>
+  </view>
+  <view class="hero-meals" v-if="hasMealConfig">
+    <view class="hero-meal" v-for="m in heroMeals" :key="m.key">
+      <text class="hero-meal-emoji">{{ m.emoji }}</text>
+      <text class="hero-meal-label">{{ m.label }}</text>
+      <text class="hero-meal-count">{{ m.count || '—' }}</text>
+    </view>
+  </view>
+  <view class="hero-empty" v-else @tap="navTo('/pages/diet/meal-config')">
+    <text class="hero-empty-text">还没设置共餐?点这里安排家人一起吃饭</text>
+    <view class="hero-empty-btn">去设置共餐</view>
+  </view>
+</view>
+```
+
+- [ ] **步骤 2:在 `<script>` 增加 `heroSubtitle` / `heroMeals` / `hasMealConfig` 计算字段(不改接口)**
+
+在 `computed` 中新增(`methods` 原有逻辑不动):
+
+```js
+computed: {
+  heroSubtitle: function() {
+    var self = this
+    return (self.isWeekend(new Date()) ? '周末' : '工作日') + ' · 全家一起吃饭'
+  },
+  heroMeals: function() {
+    var defs = [
+      { key: 'breakfast', label: '早餐', emoji: '🌅' },
+      { key: 'lunch', label: '午餐', emoji: '☀️' },
+      { key: 'dinner', label: '晚餐', emoji: '🌙' }
+    ]
+    var self = this
+    return defs.map(function(d) {
+      return { key: d.key, label: d.label, emoji: d.emoji, count: self.mealSummary[d.key] || '' }
+    })
+  },
+  hasMealConfig: function() {
+    return !!(this.mealSummary.breakfast || this.mealSummary.lunch || this.mealSummary.dinner)
+  }
+}
+```
+
+- [ ] **步骤 3:新增今日餐桌主视觉样式(在 `<style scoped>` 顶部先定义页面令牌)**
+
+```css
+/* ===== 页面令牌(暖灶·家的味道) ===== */
+.diet-page {
+  min-height: 100vh;
+  background: linear-gradient(180deg, #FFF7ED 0%, #FFF3E6 100%);
+  overflow: hidden;
+}
+.dining-hero {
+  background: linear-gradient(135deg, #FF8C42, #FFB366);
+  border-radius: 32rpx;
+  padding: 36rpx 32rpx;
+  margin-bottom: 24rpx;
+  box-shadow: 0 8rpx 32rpx rgba(255, 140, 66, 0.25);
+}
+.hero-top { display: flex; align-items: center; margin-bottom: 24rpx; }
+.hero-icon-wrap {
+  width: 88rpx; height: 88rpx; border-radius: 50%;
+  background: rgba(255,255,255,0.22);
+  display: flex; align-items: center; justify-content: center;
+  margin-right: 20rpx; flex-shrink: 0;
+}
+.hero-icon { font-size: 44rpx; }
+.hero-title { font-size: 38rpx; font-weight: 700; color: #FFFFFF; display: block; }
+.hero-subtitle { font-size: 24rpx; color: rgba(255,255,255,0.85); margin-top: 6rpx; display: block; }
+.hero-meals { display: flex; }
+.hero-meal {
+  flex: 1; margin: 0 8rpx;
+  background: rgba(255,255,255,0.18);
+  border-radius: 20rpx; padding: 20rpx 8rpx;
+  display: flex; flex-direction: column; align-items: center;
+}
+.hero-meal:first-child { margin-left: 0; }
+.hero-meal:last-child { margin-right: 0; }
+.hero-meal-emoji { font-size: 40rpx; margin-bottom: 8rpx; }
+.hero-meal-label { font-size: 24rpx; color: #FFFFFF; }
+.hero-meal-count { font-size: 22rpx; color: rgba(255,255,255,0.9); margin-top: 6rpx; font-weight: 600; }
+.hero-empty { display: flex; flex-direction: column; align-items: center; padding: 12rpx 0; }
+.hero-empty-text { font-size: 26rpx; color: #FFFFFF; margin-bottom: 20rpx; }
+.hero-empty-btn {
+  background: #FFFFFF; color: #C2410C; font-size: 28rpx; font-weight: 600;
+  padding: 18rpx 44rpx; border-radius: 999rpx;
+}
+```
+
+- [ ] **步骤 4:运行全局校验命令(见上文「全局校验命令」)确认无脚本语法错误、无禁止模式**
+
+- [ ] **步骤 5:Commit**
+
+```bash
+git add cfc-frontend/pages/diet/index.vue
+git commit -m "feat(frontend): 饮食首页今日餐桌主视觉(暖灶主题)"
+```
+
+---
+
+## 任务 2:`diet/index.vue` — 今日菜篮 + 厨房导航 + 本周饮食
+
+**文件:** 修改 `cfc-frontend/pages/diet/index.vue`(承接任务 1)
+
+- [ ] **步骤 1:改造「今日食材推荐」卡为「今日菜篮」**
+
+template 头部改为(保留 `action-bar` 三个按钮的绑定与事件不动):
+
+```html
+<view class="card">
+  <view class="card-header">
+    <view class="card-basket-icon"><text class="card-basket-emoji">🧺</text></view>
+    <text class="card-title">今日菜篮</text>
+  </view>
+  <view class="card-body">
+    <!-- 三个按钮:class 更新,事件绑定原样 -->
+    <view class="action-bar">
+      <view class="action-btn" @tap="refreshIngredients">
+        <text class="action-btn-text">🔄 换一批</text>
+      </view>
+      <view class="action-btn action-btn-primary" @tap="showFoodPicker">
+        <text class="action-btn-text-primary">+ 添加食材</text>
+      </view>
+      <view class="action-btn action-btn-accent" @tap="generateRecipe"
+        :class="{'action-btn-disabled': selectedIngredients.length === 0}">
+        <text class="action-btn-text-accent">✨ 生成食谱</text>
+      </view>
+    </view>
+    <!-- 食材列表保留现有 v-for 结构 -->
+  </view>
+</view>
+```
+
+- [ ] **步骤 2:更新食材列表项与按钮样式(嫩绿生成按钮 + 淡橙选中态)**
+
+在 `<style scoped>` 中替换/新增:
+
+```css
+.card-basket-icon {
+  width: 64rpx; height: 64rpx; border-radius: 18rpx;
+  background: linear-gradient(135deg, #FF8C42, #FFB366);
+  display: flex; align-items: center; justify-content: center;
+  margin-right: 16rpx; flex-shrink: 0;
+}
+.card-basket-emoji { font-size: 32rpx; }
+.action-btn-accent { background: linear-gradient(135deg, #10B981, #34D399); }
+.action-btn-text-accent { color: #FFFFFF; }
+.ingredient-item:active { background: #FFF9F0; }
+.ingredient-badge {
+  background: linear-gradient(135deg, #FF8C42, #FFB366);
+  /* 保留现有圆角方块结构与其余属性 */
+}
+```
+
+- [ ] **步骤 3:厨房导航 5 格改暖橙系深浅渐变(保留 20% 网格与跳转)**
+
+替换 `nav-icon-*` 五个背景为暖调(可整体替换五个类名背景值,类名保留以免改 template):
+
+```css
+.nav-icon-green { background: linear-gradient(135deg, #FFF1E6, #FFE0C0); }
+.nav-icon-blue  { background: linear-gradient(135deg, #FFF7ED, #FFEEDD); }
+.nav-icon-orange{ background: linear-gradient(135deg, #FF8C42, #FFB366); }
+.nav-icon-purple{ background: linear-gradient(135deg, #FFE8D6, #FFD4B0); }
+.nav-icon-red   { background: linear-gradient(135deg, #E8F8F1, #C8F0E0); }
+.nav-item:active .nav-icon-wrap { transform: scale(0.92); }
+.nav-icon-wrap { transition: transform 0.15s ease; }
+```
+
+- [ ] **步骤 4:本周饮食卡暖化(分隔线浅橙 + 大数字橙字已具,改分隔线)**
+
+```css
+.trend-divider { background: #FDE8D0; }
+```
+
+- [ ] **步骤 5:运行全局校验命令确认无禁止模式**
+
+- [ ] **步骤 6:Commit**
+
+```bash
+git add cfc-frontend/pages/diet/index.vue
+git commit -m "feat(frontend): 饮食首页菜篮/导航/趋势暖灶主题化"
+```
+
+---
+
+## 任务 3:`diet/meal-config.vue` — 共餐配置页美化
+
+**文件:** 修改 `cfc-frontend/pages/diet/meal-config.vue`
+
+- [ ] **步骤 1:日期切换改两段式胶囊 tabs**
+
+template 的 `date-tabs` 保留结构,样式整体替换:
+
+```html
+<view class="date-tabs">
+  <view class="tab-item" :class="{'tab-active': dateType === 'weekday'}" @tap="switchDateType('weekday')">
+    <text class="tab-text">工作日</text>
+  </view>
+  <view class="tab-item" :class="{'tab-active': dateType === 'weekend'}" @tap="switchDateType('weekend')">
+    <text class="tab-text">周末</text>
+  </view>
+</view>
+```
+
+```css
+.date-tabs {
+  display: flex; background: #FFF1E6; border-radius: 999rpx;
+  padding: 6rpx; margin-bottom: 30rpx;
+}
+.tab-item {
+  flex: 1; text-align: center; padding: 18rpx 0;
+  border-radius: 999rpx; font-size: 28rpx; color: #C2410C;
+}
+.tab-active {
+  background: linear-gradient(135deg, #FF8C42, #FFB366);
+  color: #FFFFFF; font-weight: 600;
+  box-shadow: 0 4rpx 12rpx rgba(255, 140, 66, 0.3);
+}
+```
+
+- [ ] **步骤 2:餐次卡 icon 渐变圆底 + 已选人数角标**
+
+template 的 `meal-header` 增加已选人数:
+
+```html
+<view class="meal-header">
+  <view class="meal-icon-wrap" :class="'meal-icon-' + meal.value"><text class="meal-icon">{{ meal.icon }}</text></view>
+  <text class="meal-name">{{ meal.label }}</text>
+  <text class="meal-count" v-if="meal.selectedCount > 0">已选 {{ meal.selectedCount }} 人</text>
+</view>
+```
+
+同步在 `computed.mealGroups` 的返回对象中增加 `selectedCount: ids.length`。
+
+```css
+.meal-icon-wrap { width: 72rpx; height: 72rpx; border-radius: 22rpx; display: flex; align-items: center; justify-content: center; margin-right: 16rpx; }
+.meal-icon-breakfast { background: linear-gradient(135deg, #FFB366, #FFD48A); }
+.meal-icon-lunch     { background: linear-gradient(135deg, #FBBF24, #FCD34D); }
+.meal-icon-dinner    { background: linear-gradient(135deg, #A78BFA, #C4B5FD); }
+.meal-count { font-size: 24rpx; color: #C2410C; }
+```
+
+- [ ] **步骤 3:成员 chips 选中态 + 彩色渐变字母圈**
+
+替换 `member-avatar` / `member-selected` 样式:
+
+```css
+.member-item {
+  box-sizing: border-box; position: relative;
+  display: flex; flex-direction: column; align-items: center; justify-content: center;
+  padding: 16rpx 20rpx; border: 2rpx solid #E0E0E0; border-radius: 20rpx;
+  background: #FAFAFA; min-width: 132rpx;
+  transition: all 0.15s ease;
+}
+.member-selected {
+  border-color: #FF8C42; border-width: 3rpx;
+  background: #FFF5EB; box-shadow: 0 0 0 4rpx rgba(255, 140, 66, 0.18);
+}
+.member-avatar {
+  width: 60rpx; height: 60rpx; border-radius: 50%;
+  color: #FFFFFF; display: flex; align-items: center; justify-content: center;
+  font-size: 24rpx; font-weight: 600; margin-bottom: 8rpx;
+}
+/* 底色按 gender:需在 template 的成员对象上加 _avatarCls,methods 中根据 m.gender 映射 */
+.member-avatar-male   { background: linear-gradient(135deg, #5B9BD5, #8FC5E8); }
+.member-avatar-female { background: linear-gradient(135deg, #F472B6, #F9A8D4); }
+.member-avatar-unknown{ background: linear-gradient(135deg, #FF8C42, #FFB366); }
+```
+
+> 注意:`gender` 在 `meal.members` 数据中来自 `/api/family/member/list` 的 `member.gender`。在 `computed.mealGroups` 组装时给每个成员加 `_avatarCls`(用 `m.gender === 'male' ? 'member-avatar-male' : (m.gender === 'female' ? 'member-avatar-female' : 'member-avatar-unknown')`),template 用 `:class="member._avatarCls"`。**禁止在 template 内调用方法返回类名。**
+
+- [ ] **步骤 4:提示条暖橙化 + 保存按钮渐变**
+
+```css
+.tip-card { background: #FFF1E6; border-radius: 16rpx; padding: 24rpx 30rpx; margin-bottom: 30rpx; }
+.tip-text { font-size: 24rpx; color: #C2410C; line-height: 1.6; }
+.save-btn {
+  background: linear-gradient(135deg, #FF8C42, #FFB366);
+  color: #FFFFFF; text-align: center; padding: 28rpx 0; border-radius: 999rpx;
+  font-size: 30rpx; font-weight: 600;
+  box-shadow: 0 6rpx 20rpx rgba(255, 140, 66, 0.25);
+}
+```
+
+- [ ] **步骤 5:运行全局校验命令确认无禁止模式**
+
+- [ ] **步骤 6:Commit**
+
+```bash
+git add cfc-frontend/pages/diet/meal-config.vue
+git commit -m "feat(frontend): 共餐配置页暖灶主题美化"
+```
+
+---
+
+## 任务 4:`diet/recommendation.vue` — 食谱推荐页美化
+
+**文件:** 修改 `cfc-frontend/pages/diet/recommendation.vue`
+
+- [ ] **步骤 1:空态改插图式(暖橙渐变圆 + 主文案 + 生成按钮)**
+
+template 替换空态区块:
+
+```html
+<view class="empty-state" v-if="!recommendation">
+  <view class="empty-icon-wrap"><text class="empty-icon">🍽️</text></view>
+  <text class="empty-title">今日菜单空着</text>
+  <text class="empty-sub">点击生成今日三餐食谱</text>
+  <view class="generate-btn" @tap="generateRecipe">
+    <text>生成食谱</text>
+  </view>
+</view>
+```
+
+```css
+.empty-state { display: flex; flex-direction: column; align-items: center; padding: 120rpx 40rpx; }
+.empty-icon-wrap {
+  width: 160rpx; height: 160rpx; border-radius: 50%;
+  background: linear-gradient(135deg, #FF8C42, #FFB366);
+  display: flex; align-items: center; justify-content: center;
+  box-shadow: 0 10rpx 30rpx rgba(255, 140, 66, 0.3);
+  margin-bottom: 40rpx;
+}
+.empty-icon { font-size: 72rpx; }
+.empty-title { font-size: 34rpx; font-weight: 700; color: #333; display: block; margin-bottom: 16rpx; }
+.empty-sub { font-size: 26rpx; color: #999; display: block; margin-bottom: 48rpx; }
+.generate-btn {
+  background: linear-gradient(135deg, #FF8C42, #FFB366);
+  color: #FFFFFF; padding: 28rpx 60rpx; border-radius: 999rpx;
+  font-size: 30rpx; font-weight: 600;
+  box-shadow: 0 6rpx 20rpx rgba(255, 140, 66, 0.25);
+}
+```
+
+- [ ] **步骤 2:日期卡 + 状态徽章 + 餐次菜单卡(渐变 icon + 淡橙菜品块)**
+
+`card-accent` 改为橙渐变竖条、`meal-header` 增加渐变 icon 圆底(复用任务 3 的三餐配色类)、`dish-item` 改淡橙底块:
+
+```css
+.card-accent { width: 8rpx; background: linear-gradient(180deg, #FF8C42, #FFB366); flex-shrink: 0; }
+.status-pending { background: #FFF1E6; color: #C2410C; }
+.meal-header { display: flex; align-items: center; padding: 24rpx 30rpx; border-bottom: 1rpx solid #FDE8D0; }
+.meal-icon-wrap { width: 64rpx; height: 64rpx; border-radius: 20rpx; display: flex; align-items: center; justify-content: center; margin-right: 16rpx; }
+.meal-icon-breakfast { background: linear-gradient(135deg, #FFB366, #FFD48A); }
+.meal-icon-lunch     { background: linear-gradient(135deg, #FBBF24, #FCD34D); }
+.meal-icon-dinner    { background: linear-gradient(135deg, #A78BFA, #C4B5FD); }
+.dish-item { padding: 24rpx; border-radius: 20rpx; background: #FFF9F0; margin-bottom: 16rpx; }
+.dish-item:last-child { margin-bottom: 0; }
+.dish-name { font-size: 28rpx; font-weight: 600; color: #333; }
+.dish-cal { font-size: 24rpx; color: #C2410C; font-weight: 600; }
+```
+
+> 注意:餐次 icon 需要 `meal.type` 映射(`breakfast/lunch/dinner`)。在 `parseMenu()` 组装 `self.meals` 时给每个 meal 加 `iconCls` 字段(如 `'meal-icon-' + meal.type`),template 用 `:class="meal.iconCls"`。**禁止在 template 内方法调用。**
+
+- [ ] **步骤 3:操作按钮升级为图标+文字胶囊**
+
+template 的 `dish-actions` 三个按钮改为「图标 + 文字」(保留 `@tap` 事件绑定):
+
+```html
+<view class="dish-actions">
+  <view class="action-btn like-btn" @tap="likeDish(meal.name, dIndex)">👍 好吃</view>
+  <view class="action-btn swap-btn" @tap="swapDish(meal.name, dIndex)">🔄 替换</view>
+  <view class="action-btn skip-btn" @tap="skipDish(meal.name, dIndex)">✕ 跳过</view>
+</view>
+```
+
+```css
+.dish-actions { display: flex; gap: 16rpx; margin-top: 8rpx; }
+.action-btn { padding: 12rpx 26rpx; border-radius: 999rpx; font-size: 24rpx; }
+.like-btn { background: #E8F5E9; color: #4CAF50; }
+.swap-btn { background: #E8F1FB; color: #2196F3; }
+.skip-btn { background: #FFEBEE; color: #F44336; }
+```
+
+- [ ] **步骤 4:营养汇总卡浅暖底 + 宫格分隔浅橙**
+
+```css
+.nutrition-card .card-body { background: #FFF9F0; border-radius: 0 0 20rpx 20rpx; }
+.nutrition-value { font-size: 40rpx; font-weight: 700; color: #C2410C; display: block; }
+```
+
+- [ ] **步骤 5:底部操作(重新生成描边 / 标记完成渐变)**
+
+```css
+.btn-secondary {
+  flex: 1; padding: 28rpx 0; background: #FFFFFF;
+  border: 2rpx solid #FF8C42; border-radius: 999rpx;
+  text-align: center; font-size: 28rpx; color: #C2410C;
+}
+.btn-primary {
+  flex: 1; padding: 28rpx 0;
+  background: linear-gradient(135deg, #FF8C42, #FFB366);
+  border-radius: 999rpx; text-align: center;
+  font-size: 28rpx; color: #FFFFFF; font-weight: 600;
+  box-shadow: 0 6rpx 20rpx rgba(255, 140, 66, 0.25);
+}
+```
+
+- [ ] **步骤 6:运行全局校验命令确认无禁止模式**
+
+- [ ] **步骤 7:Commit**
+
+```bash
+git add cfc-frontend/pages/diet/recommendation.vue
+git commit -m "feat(frontend): 食谱推荐页暖灶主题美化"
+```
+
+---
+
+## 任务 5:跨页一致性复核 + 收尾
+
+**文件:** 全部三个页面(只读复核)
+
+- [ ] **步骤 1:核对规格 §设计令牌 的色值在三个页面 scoped 样式中的使用是否统一**(主橙 `#FF8C42`、深橙 `#C2410C`、米底 `#FFF7ED`、嫩绿 `#10B981` 出现位置与规格一致)
+
+- [ ] **步骤 2:复核业务逻辑零改动**:`git diff` 三个文件,确认 `methods`/`data`/`onLoad`/`onShow` 仅新增 computed 字段与类映射字段,未改任何接口调用、参数、跳转路径
+
+- [ ] **步骤 3:跑一次全量校验命令(三文件同时)确认最终无禁止模式**
+
+- [ ] **步骤 4:Commit(若有遗漏修正)**
+
+```bash
+git add cfc-frontend/pages/diet/index.vue cfc-frontend/pages/diet/meal-config.vue cfc-frontend/pages/diet/recommendation.vue
+git commit -m "fix(frontend): 饮食三页暖灶主题一致性复核修正"
+```
+
+---
+
+## 自检记录
+
+- [ ] **规格覆盖度**:规格 §1(index 四区块)→ 任务1+2;§2(meal-config 五小节)→ 任务3;§3(recommendation 五小节)→ 任务4;令牌/约束 → 各任务样式与校验
+- [ ] **占位符扫描**:无 TODO/待定;每个样式步骤含实际 CSS 代码
+- [ ] **类型一致性**:`meal.iconCls`、`member._avatarCls`、`heroMeals[].key` 等字段在对应 computed 中定义后才在 template 使用

+ 279 - 0
cfc-frontend/docs/superpowers/specs/2026-08-28-diet-redesign.md

@@ -0,0 +1,279 @@
+# 共餐 & 食谱页面 Redesign 设计文档
+
+- **日期**:2026-08-28
+- **主题**:饮食模块三页重新设计(共餐 + 食谱)
+- **涉及页面**:
+  - `pages/diet/index.vue`(饮食首页:今日共餐摘要 + 今日食材推荐 + 功能入口 + 本周趋势)
+  - `pages/diet/meal-config.vue`(共餐配置:工作日/周末切换 + 餐次×成员多选)
+  - `pages/diet/recommendation.vue`(食谱推荐:餐次菜单 + 菜品 + 营养汇总)
+- **方案**:方案一「🍳 暖灶 · 家的味道」(橙暖团聚主题)
+- **核心理念**:身维度属土 → 暖橙 + 大地色 + 嫩绿点缀,用「一起吃饭的餐桌」表达家庭共餐与科学膳食的温暖感。与财富页「💧 财富之泉(水元素主题)」构成一致的维度主题化策略。
+
+## 目标
+
+解决当前饮食模块的问题:
+
+1. **平铺无锚点**:`diet/index.vue` 结构是「标题 + 卡片 + 网格」的通用堆叠,没有视觉主视觉,与身/智/心详情页的视觉丰富度差距明显
+2. **配色硬编码**:多处使用 `#FF8C42` 但未抽象为令牌,页面间深浅不一(`#FF8C42` vs `#F97316` 混用)
+3. **共餐无温度**:共餐摘要只是 3 个灰底格子,没有「一家人一起吃饭」的氛围
+4. **配置页无辨识度**:`meal-config.vue` 纯白底 + 灰头像,餐次与成员选择的视觉反馈弱
+5. **食谱无食欲感**:`recommendation.vue` 卡片平淡,菜品信息缺乏层级,操作按钮用裸 emoji
+6. **空态无引导**:三页空态都是「图标 + 灰色文字」,缺乏转化引导
+
+## 设计原则
+
+- 沿用项目统一设计 Token(`uni.scss`:扁平圆角卡片、柔和暖色系、`--color-primary` 体系)
+- 身维度专属色系:主橙 `#FF8C42`、渐变 `#FF8C42 → #FFB366`、米底 `#FFF7ED`、深橙文字 `#C2410C`、嫩绿点缀 `#10B981`
+- **小程序限制(强制遵守)**:
+  - 禁止可选链 `?.`(用 `&&` 替代)
+  - 禁止 CSS Grid(用 flexbox)
+  - 禁止 `:key` 表达式(用方法调用代替)
+  - 禁止中文类名(用英文,数据状态映射 `_cssClass`)
+  - Vue 2 Options API,禁止 Composition API
+  - 禁止 `new Date(string)` 解析(统一 `utils/format.js` 的 `parseDate()`)
+- **图标策略**:保留食品 emoji(领域语义贴切 + 小程序惯例),统一放入柔和渐变圆底承载;禁止为纯装饰添加动画
+- **纯样式与布局重做**:不改任何接口、数据结构、业务逻辑、跳转路径
+
+## 设计令牌(Theme Tokens)
+
+| 令牌 | 值 | 用途 |
+|------|-----|------|
+| `--diet-primary` | `#FF8C42` | 主操作 / 强调 |
+| `--diet-primary-grad` | `linear-gradient(135deg, #FF8C42, #FFB366)` | 主按钮 / 主视觉底 |
+| `--diet-primary-deep` | `#C2410C` | 深橙文字(标题强调) |
+| `--diet-bg` | `#FFF7ED` | 页面底色 |
+| `--diet-bg-soft` | `#FFF1E6` | 浅卡底 / 选中态 |
+| `--diet-fresh` | `#10B981` | 嫩绿点缀(生成食谱 / 点赞) |
+| `--diet-sun` | `#FBBF24` | 正午黄(午餐) |
+| `--diet-dusk` | `#8B5CF6` | 暮色紫(晚餐 icon 底) |
+| `--diet-text` | `#333333` | 主文字 |
+| `--diet-text-2` | `#999999` | 次要文字 |
+| `--diet-card-shadow` | `0 4rpx 20rpx rgba(255, 140, 66, 0.10)` | 卡片暖影 |
+
+> 说明:令牌以页面 scoped CSS 变量形式在每个 `.vue` 的 `<style>` 顶部定义(小程序页面无全局注入 css var 的便捷手段,保持 scoped 内自足),不修改 `uni.scss`,避免影响其他页面。
+
+---
+
+## 第一节:饮食首页 `pages/diet/index.vue`
+
+### 信息架构
+
+```
+┌──────────────────────────────────────────────┐
+│  🍳 今日餐桌(顶部主视觉,暖橙渐变大卡)        │
+│    今日共餐 · 工作日/周末 · N 人共餐           │
+│    [🌅 早餐] [☀️ 午餐] [🌙 晚餐]  时间轴三格   │
+│    (未配置 → 引导「去设置共餐」)              │
+├──────────────────────────────────────────────┤
+│  🧺 今日菜篮(食材推荐)                      │
+│    [🔄 换一批] [+ 添加食材] [✨ 生成食谱]     │
+│    食材列表:序号徽章 + 名称 + 推荐理由        │
+├──────────────────────────────────────────────┤
+│  常用功能(厨房导航 5 格,暖橙系深浅渐变)      │
+├──────────────────────────────────────────────┤
+│  本周饮食(总摄入 kcal / 记录天数,橙大数字)    │
+└──────────────────────────────────────────────┘
+```
+
+### 1.1 今日餐桌主视觉(替换原 page-header + 今日共餐卡片)
+
+**结构**:页面顶部一张暖橙渐变圆角大卡(`#FF8C42 → #FFB366`),白字。
+
+```
+┌──────────────────────────────────┐
+│  🍳 今日餐桌                       │
+│  工作日 · 3 人共餐                 │
+│  ┌────────┐ ┌────────┐ ┌────────┐ │
+│  │ 🌅      │ │ ☀️      │ │ 🌙      │ │
+│  │ 早餐     │ │ 午餐     │ │ 晚餐     │ │
+│  │ 共 2 人  │ │ 共 3 人  │ │ 共 3 人  │ │
+│  └────────┘ └────────┘ └────────┘ │
+└──────────────────────────────────┘
+```
+
+**视觉细节**:
+- 卡片顶部:左侧大号 `🍳` 放入白色半透明圆底,右侧标题「今日共餐」+ 副标题「工作日 · N 人共餐」
+- 下方三格:白色半透明(`rgba(255,255,255,0.18)`)圆角块,内含餐次 emoji + 餐次名(白字)+ 参与人数(`共 N 人`,来自 `parseMembers` 返回的计数文案)
+- 副标题按 `dateType` 显示:`工作日` / `周末`;参与人数来自 `mealSummary.*`(`parseMembers` 返回值)
+- 未配置任何餐次 → 该卡显示暖橙引导文案 + 「去设置共餐」白底橙字按钮,点击 `navTo('/pages/diet/meal-config')`
+- 若已配置但某餐无人(`mealSummary.breakfast` 为空)→ 该格显示 `—` 占位
+
+**数据**:沿用 `mealSummary`(`getMealConfig` 三连请求)+ `isWeekend()`,不新增接口。
+**交互**:点击整卡或「去设置共餐」跳转 `meal-config`。
+
+### 1.2 今日菜篮(食材推荐卡)
+
+**结构**:
+```
+┌──────────────────────────────────┐
+│  🧺 今日菜篮                       │
+│  [🔄 换一批] [+ 添加食材] [✨ 生成食谱] │
+│  ┌────┐ 西蓝花                     │
+│  │ 01 │ 富含膳食纤维,适合晚餐搭配     │
+│  └────┘                            │
+│  ...                                │
+└──────────────────────────────────┘
+```
+
+**视觉细节**:
+- 卡片头部:左侧 `🧺` 放入橙渐变圆底,标题「今日菜篮」(替代原「今日食材推荐」文案)
+- 操作栏三按钮:
+  - `🔄 换一批`:灰底灰字胶囊
+  - `+ 添加食材`:主橙渐变底白字胶囊
+  - `✨ 生成食谱`:嫩绿渐变底白字胶囊;无食材时 `opacity: 0.4` 禁用态
+- 食材列表项:左侧序号徽章(橙渐变圆角方块,白字 01/02…)+ 名称(`#333` 加粗)+ 推荐理由(浅灰小字,`item.reason`)
+- 空态:`🥬` 大 emoji + 「暂无推荐食材」+ 引导副文案,保留现有上传报告引导弹窗逻辑不动
+
+**数据/逻辑**:沿用 `ingredients`、`loadIngredients`、`refreshIngredients`、`removeIngredient`、`showFoodPicker`、`generateRecipe` 全部逻辑,仅改样式与文案。
+
+### 1.3 厨房导航(快捷入口 5 格)
+
+**视觉细节**:
+- 保留 20% 宽 5 列网格(flexbox)
+- 图标底从「五种 pastel 渐变色」统一为**暖橙系深浅渐变**,保留 5 个入口语义色但收敛到暖调:
+  - 食材查询 🥗:浅橙 `#FFF1E6 → #FFE0C0`
+  - 饮食记录 📝:浅米 `#FFF7ED → #FFEEDD`
+  - 调研表 📋:中橙 `#FF8C42 → #FFB366`
+  - 共餐配置 👨👩👧:浅杏 `#FFE8D6 → #FFD4B0`
+  - 食谱推荐 🍽️:嫩绿点缀 `#E8F8F1 → #C8F0E0`
+- 图标保留 emoji(40rpx),标签 `#333` 22rpx
+- `hover-class` / `:active` 按压缩放反馈
+
+### 1.4 本周饮食(趋势卡)
+
+**视觉细节**:
+- 白卡 + 暖影,头部「本周饮食」沿用橙色竖条
+- 两个数据:`总摄入 kcal`(48rpx 橙色加粗数字 + 单位小字)+ `记录天数`(48rpx 橙色 + 天)
+- 中间分隔线浅橙 `#FDE8D0`
+- 无数据(`trendData.days === 0`)保持隐藏
+
+### 1.5 页面背景
+
+- 页底从 `linear-gradient(180deg, #FFF7ED, #FFF3E6, #F5F7FA)` 收敛为**纯暖调** `linear-gradient(180deg, #FFF7ED 0%, #FFF3E6 100%)`,避免页面尾部突然变冷蓝
+
+---
+
+## 第二节:共餐配置 `pages/diet/meal-config.vue`
+
+### 信息架构
+
+```
+┌──────────────────────────────────────────────┐
+│  [工作日]  [周末]        ← 两段式胶囊 tabs      │
+│  ┌ ─ ─ ─ ─ ─ ─ ─ ─ ┐                        │
+│  │  + 添加共餐人  ›  │  ← 橙虚线引导卡          │
+│  └ ─ ─ ─ ─ ─ ─ ─ ─ ┘                        │
+│  ┌──────────────────────┐                   │
+│  │ 🌅 早餐               │                   │
+│  │ [爸爸✓] [妈妈] [爷爷]   │  ← 成员 chips      │
+│  └──────────────────────┘                   │
+│  ☀️ 午餐 ...   🌙 晚餐 ...                   │
+│  💡 提示:选择与您在工作日共餐的家庭成员         │
+│  [保存配置](主橙渐变)                       │
+└──────────────────────────────────────────────┘
+```
+
+### 2.1 日期切换
+
+- 两段式胶囊(segmented control):容器白底圆角,两个等宽选项
+- 未选中:`#666` 灰字透明底;选中:橙渐变底白字加粗
+- 保留 `switchDateType('weekday'/'weekend')` 逻辑
+
+### 2.2 添加共餐人入口
+
+- 橙虚线圆角卡(`border: 2rpx dashed #FF8C42`),白底
+- 内容:`+` 橙色 + 「添加共餐人」橙字加粗 + `›` 右箭头
+- 保留跳转 `family-members` 逻辑
+
+### 2.3 餐次 × 成员配置
+
+- **餐次卡片**:白底圆角 + 暖影
+  - 卡片头:餐次 emoji 放入对应渐变圆底:
+    - 早餐 `🌅` → 日出橙渐变 `#FFB366 → #FFD48A`
+    - 午餐 `☀️` → 正午黄渐变 `#FBBF24 → #FCD34D`
+    - 晚餐 `🌙` → 暮色紫渐变 `#A78BFA → #C4B5FD`
+  - 餐次名 `#333` 加粗,右侧显示「已选 N 人」(仅 N>0 时显示,N 为该餐次 `participantMemberIds` 长度)
+- **成员 chips**(flexbox wrap):
+  - 未选:浅灰底(`#F5F5F5`)灰描边,首字字母圈用中性渐变
+  - 选中:橙描边(`2rpx`)+ 浅橙底(`#FFF5EB`)+ 右上角橙色 `✓` 圆形角标 + 首字圈变橙渐变
+  - 头像圈从纯灰 `#C8CDD4` 升级为**彩色渐变字母圈**(取成员昵称首字;底色按 `member.gender` 定:`male` → 暖蓝渐变 `#5B9BD5 → #8FC5E8`、`female` → 暖粉渐变 `#F472B6 → #F9A8D4`、未知 → 暖橙渐变 `#FF8C42 → #FFB366`)
+  - 空成员:`暂无可选的共餐人` 灰字,占满一行
+
+### 2.4 提示条
+
+- 从蓝底 `#E3F2FD` 改为暖橙浅底 `#FFF1E6`,文字深橙 `#C2410C`
+- 文案保留:`💡 提示:选择与您在{工作日/周末}共餐的家庭成员`
+
+### 2.5 保存按钮
+
+- 主橙渐变(`#FF8C42 → #FFB366`)白字,大圆角,底部留白加大
+- 保留 `saving` 态文案切换
+
+---
+
+## 第三节:食谱推荐 `pages/diet/recommendation.vue`
+
+### 3.1 空态(无推荐)
+
+**结构**:居中暖橙渐变圆底(120rpx)+ `🍽️` emoji + 主文案「今日菜单空着」+ 副文案 + 「生成食谱」主按钮。
+
+**视觉细节**:
+- emoji 承载圆底:`#FF8C42 → #FFB366` 渐变 + 白字大图标(或 emoji 60rpx)
+- 主文案 `#333` 32rpx 加粗「今日菜单空着」
+- 副文案 `#999`「点击生成今日三餐食谱」
+- 按钮:主橙渐变白字,`padding: 28rpx 60rpx` 大圆角
+- 保留 `generateRecipe()` 逻辑
+
+### 3.2 日期 + 状态卡
+
+- 白卡 + 暖影,左缘保留橙色竖条(`card-accent` 改为橙渐变竖条)
+- 日期 `#333` 加粗;状态徽章:
+  - `待确认`:浅橙底 `#FFF1E6` + 深橙字 `#C2410C`
+  - `已接受`:浅暖蓝 `#E8F1FB` + 蓝字(保留语义色,仅微调)
+  - `已完成`:浅绿底 `#E8F5E9` + 绿字
+
+### 3.3 餐次菜单卡
+
+- 每餐一张白卡 + 暖影
+- 餐次头:emoji 渐变圆底(同 2.3 三餐配色)+ 餐次名 `#333` 加粗 + 右侧 `N 人份` 灰字
+- **菜品项**:
+  - 淡橙底块(`#FFF9F0` 圆角)承载整条菜品信息,替代现有无底色分行
+  - 菜名 `#333` 26rpx 加粗 + 右侧 `NNN kcal` 橙字
+  - 食材标签:灰 chips(`#F5F5F5` 底 + `#666` 字,`名称 Xg`)
+  - 做法:`🍳 清蒸 20 分钟` 灰字小行
+  - 操作行三按钮升级为**圆角小胶囊(图标 + 文字)**:
+    - 👍 好吃:浅绿底 `#E8F5E9` + 绿字 `#4CAF50`
+    - 🔄 替换:浅暖蓝底 `#E8F1FB` + 蓝字
+    - ✕ 跳过:浅红底 `#FFEBEE` + 红字
+
+### 3.4 营养汇总卡
+
+- 白卡 + 暖影,标题「今日营养汇总」
+- 4 宫格(flexbox):热量 / 蛋白质 / 碳水 / 脂肪
+- 数值 36rpx 橙色加粗 + 单位小字(kcal/g)+ 标签灰字
+- 宫格间浅橙分隔,整体加浅暖底
+
+### 3.5 底部操作
+
+- `重新生成`:白底橙描边圆角按钮(替代现有灰描边)
+- `标记完成`:主橙渐变白字(`recommendation.status !== 'completed'` 时显示)
+- 保留 `regenerate()` / `completeRecipe()` 逻辑
+
+---
+
+## 验证方式
+
+- 小程序无法跑 Lighthouse,验证以**代码审查 + 微信开发者工具视觉确认**为主:
+  1. `mvn` 无涉(纯前端)
+  2. 三页 `<style>` 均 scoped,不污染全局
+  3. 无 `?.`、无 CSS Grid、无 `:key` 表达式、无中文类名、无 `new Date(string)`
+  4. 业务逻辑零改动:仅 template 结构 + style + 文案
+  5. 改完由用户在 HBuilderX 重新打包,微信开发者工具目检三页(含空态/已配置态)
+
+## 明确不做(Out of Scope)
+
+- 不动 `pages/meal/recommend.vue`(已废弃)、`pages/meal/recipe-detail.vue`(已废弃)
+- 不新增/修改任何后端接口与数据字段
+- 不修改 `uni.scss` 全局令牌(避免影响其他页面)
+- 不引入新依赖
+- 不新增动画(除保留现有 `transition: all 0.15s` chips 态切换)

+ 97 - 153
cfc-frontend/pages/diet/index.vue

@@ -1,54 +1,33 @@
 <template>
 <template>
   <view class="diet-page">
   <view class="diet-page">
     <scroll-view class="content" scroll-y>
     <scroll-view class="content" scroll-y>
-      <!-- 页面标题区 -->
-      <view class="page-header">
-        <view class="header-icon-wrap">
-          <text class="header-icon">🍽️</text>
-        </view>
-        <view class="header-text">
-          <text class="header-title">饮食推荐</text>
-          <text class="header-subtitle">科学膳食 · 健康生活</text>
-        </view>
-      </view>
-
-      <!-- 今日共餐摘要 -->
-      <view class="card">
-        <view class="card-header">
-          <view class="card-dot"></view>
-          <text class="card-title">今日共餐</text>
-        </view>
-        <view class="card-body">
-          <view class="meal-grid">
-            <view class="meal-item" v-if="mealSummary.breakfast">
-              <text class="meal-emoji">🌅</text>
-              <text class="meal-label">早餐</text>
-              <text class="meal-members">{{ mealSummary.breakfast }}</text>
-            </view>
-            <view class="meal-item" v-if="mealSummary.lunch">
-              <text class="meal-emoji">☀️</text>
-              <text class="meal-label">午餐</text>
-              <text class="meal-members">{{ mealSummary.lunch }}</text>
-            </view>
-            <view class="meal-item" v-if="mealSummary.dinner">
-              <text class="meal-emoji">🌙</text>
-              <text class="meal-label">晚餐</text>
-              <text class="meal-members">{{ mealSummary.dinner }}</text>
-            </view>
+      <!-- ===== 今日餐桌主视觉 ===== -->
+      <view class="dining-hero">
+        <view class="hero-top">
+          <view class="hero-icon-wrap"><text class="hero-icon">🍳</text></view>
+          <view class="hero-text">
+            <text class="hero-title">今日共餐</text>
+            <text class="hero-subtitle">{{ heroSubtitle }}</text>
           </view>
           </view>
-          <view class="empty-hint" v-if="!mealSummary.breakfast && !mealSummary.lunch && !mealSummary.dinner">
-            <text class="empty-icon">📋</text>
-            <text class="empty-text">尚未配置共餐</text>
-            <text class="empty-sub">点击下方「共餐配置」开始设置</text>
+        </view>
+        <view class="hero-meals" v-if="hasMealConfig">
+          <view class="hero-meal" v-for="m in heroMeals" :key="m.key">
+            <text class="hero-meal-emoji">{{ m.emoji }}</text>
+            <text class="hero-meal-label">{{ m.label }}</text>
+            <text class="hero-meal-count">{{ m.count || '—' }}</text>
           </view>
           </view>
         </view>
         </view>
+        <view class="hero-empty" v-else @tap="navTo('/pages/diet/meal-config')">
+          <text class="hero-empty-text">还没设置共餐?点这里安排家人一起吃饭</text>
+          <view class="hero-empty-btn">去设置共餐</view>
+        </view>
       </view>
       </view>
 
 
       <!-- 食材推荐器 -->
       <!-- 食材推荐器 -->
       <view class="card">
       <view class="card">
         <view class="card-header">
         <view class="card-header">
-          <view class="card-dot"></view>
-          <text class="card-title">今日食材推荐</text>
+          <view class="card-basket-icon"><text class="card-basket-emoji">🧺</text></view>
+          <text class="card-title">今日菜篮</text>
         </view>
         </view>
         <view class="card-body">
         <view class="card-body">
           <view class="action-bar">
           <view class="action-bar">
@@ -198,6 +177,28 @@ export default {
       uploadPromptShown: false
       uploadPromptShown: false
     }
     }
   },
   },
+  computed: {
+    heroSubtitle: function() {
+      var self = this
+      var parts = this.heroMeals.filter(function(m) { return m.count && m.count !== '' })
+      var count = parts.length > 0 ? (parts[0].count || '') : ''
+      return (self.isWeekend(new Date()) ? '周末' : '工作日') + ' · ' + (count ? count : '全家一起吃饭')
+    },
+    heroMeals: function() {
+      var defs = [
+        { key: 'breakfast', label: '早餐', emoji: '🌅' },
+        { key: 'lunch', label: '午餐', emoji: '☀️' },
+        { key: 'dinner', label: '晚餐', emoji: '🌙' }
+      ]
+      var self = this
+      return defs.map(function(d) {
+        return { key: d.key, label: d.label, emoji: d.emoji, count: self.mealSummary[d.key] || '' }
+      })
+    },
+    hasMealConfig: function() {
+      return !!(this.mealSummary.breakfast || this.mealSummary.lunch || this.mealSummary.dinner)
+    }
+  },
   onLoad: function() {
   onLoad: function() {
     this.familyId = uni.getStorageSync('familyId')
     this.familyId = uni.getStorageSync('familyId')
     this.familyMemberId = uni.getStorageSync('currentChildId') || uni.getStorageSync('currentMemberId') || uni.getStorageSync('userId')
     this.familyMemberId = uni.getStorageSync('currentChildId') || uni.getStorageSync('currentMemberId') || uni.getStorageSync('userId')
@@ -347,11 +348,47 @@ export default {
 </script>
 </script>
 
 
 <style scoped>
 <style scoped>
+/* ===== 页面令牌(暖灶·家的味道) ===== */
 .diet-page {
 .diet-page {
   min-height: 100vh;
   min-height: 100vh;
-  background: linear-gradient(180deg, #FFF7ED 0%, #FFF3E6 30%, #F5F7FA 100%);
+  background: linear-gradient(180deg, #FFF7ED 0%, #FFF3E6 100%);
   overflow: hidden;
   overflow: hidden;
 }
 }
+.dining-hero {
+  background: linear-gradient(135deg, #FF8C42, #FFB366);
+  border-radius: 32rpx;
+  padding: 36rpx 32rpx;
+  margin-bottom: 24rpx;
+  box-shadow: 0 8rpx 32rpx rgba(255, 140, 66, 0.25);
+}
+.hero-top { display: flex; align-items: center; margin-bottom: 24rpx; }
+.hero-icon-wrap {
+  width: 88rpx; height: 88rpx; border-radius: 50%;
+  background: rgba(255,255,255,0.22);
+  display: flex; align-items: center; justify-content: center;
+  margin-right: 20rpx; flex-shrink: 0;
+}
+.hero-icon { font-size: 44rpx; }
+.hero-title { font-size: 38rpx; font-weight: 700; color: #FFFFFF; display: block; }
+.hero-subtitle { font-size: 24rpx; color: rgba(255,255,255,0.85); margin-top: 6rpx; display: block; }
+.hero-meals { display: flex; }
+.hero-meal {
+  flex: 1; margin: 0 8rpx;
+  background: rgba(255,255,255,0.18);
+  border-radius: 20rpx; padding: 20rpx 8rpx;
+  display: flex; flex-direction: column; align-items: center;
+}
+.hero-meal:first-child { margin-left: 0; }
+.hero-meal:last-child { margin-right: 0; }
+.hero-meal-emoji { font-size: 40rpx; margin-bottom: 8rpx; }
+.hero-meal-label { font-size: 24rpx; color: #FFFFFF; }
+.hero-meal-count { font-size: 22rpx; color: rgba(255,255,255,0.9); margin-top: 6rpx; font-weight: 600; }
+.hero-empty { display: flex; flex-direction: column; align-items: center; padding: 12rpx 0; }
+.hero-empty-text { font-size: 26rpx; color: #FFFFFF; margin-bottom: 20rpx; }
+.hero-empty-btn {
+  background: #FFFFFF; color: #C2410C; font-size: 28rpx; font-weight: 600;
+  padding: 18rpx 44rpx; border-radius: 999rpx;
+}
 .content {
 .content {
   padding: 24rpx 32rpx;
   padding: 24rpx 32rpx;
   box-sizing: border-box;
   box-sizing: border-box;
@@ -359,44 +396,7 @@ export default {
   overflow: hidden;
   overflow: hidden;
 }
 }
 
 
-/* 页面头部 */
-.page-header {
-  display: flex;
-  align-items: center;
-  padding: 20rpx 0 32rpx;
-  box-sizing: border-box;
-  width: 100%;
-}
-.header-icon-wrap {
-  width: 80rpx;
-  height: 80rpx;
-  border-radius: 24rpx;
-  background: linear-gradient(135deg, #FF8C42, #FFB366);
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  margin-right: 24rpx;
-  flex-shrink: 0;
-}
-.header-icon {
-  font-size: 40rpx;
-}
-.header-text {
-  display: flex;
-  flex-direction: column;
-}
-.header-title {
-  font-size: 36rpx;
-  font-weight: 700;
-  color: #333;
-}
-.header-subtitle {
-  font-size: 24rpx;
-  color: #999;
-  margin-top: 4rpx;
-}
-
-/* 卡片通用 */
+/* 共餐格子 */
 .card {
 .card {
   background: #FFFFFF;
   background: #FFFFFF;
   border-radius: 24rpx;
   border-radius: 24rpx;
@@ -411,14 +411,13 @@ export default {
   align-items: center;
   align-items: center;
   padding: 28rpx 28rpx 0;
   padding: 28rpx 28rpx 0;
 }
 }
-.card-dot {
-  width: 8rpx;
-  height: 32rpx;
-  border-radius: 4rpx;
-  background: linear-gradient(180deg, #FF8C42, #FFB366);
-  margin-right: 16rpx;
-  flex-shrink: 0;
+.card-basket-icon {
+  width: 64rpx; height: 64rpx; border-radius: 18rpx;
+  background: linear-gradient(135deg, #FF8C42, #FFB366);
+  display: flex; align-items: center; justify-content: center;
+  margin-right: 16rpx; flex-shrink: 0;
 }
 }
+.card-basket-emoji { font-size: 32rpx; }
 .card-title {
 .card-title {
   font-size: 30rpx;
   font-size: 30rpx;
   font-weight: 600;
   font-weight: 600;
@@ -428,43 +427,6 @@ export default {
   padding: 24rpx 28rpx 28rpx;
   padding: 24rpx 28rpx 28rpx;
 }
 }
 
 
-/* 共餐格子 */
-.meal-grid {
-  display: flex;
-  justify-content: space-between;
-}
-.meal-item {
-  flex: 1;
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  background: #FFF8F0;
-  border-radius: 16rpx;
-  padding: 24rpx 12rpx;
-  margin-left: 12rpx;
-  margin-right: 12rpx;
-}
-.meal-item:first-child {
-  margin-left: 0;
-}
-.meal-item:last-child {
-  margin-right: 0;
-}
-.meal-emoji {
-  font-size: 36rpx;
-  margin-bottom: 8rpx;
-}
-.meal-label {
-  font-size: 24rpx;
-  color: #666;
-  margin-bottom: 4rpx;
-}
-.meal-members {
-  font-size: 24rpx;
-  color: #FF8C42;
-  font-weight: 600;
-}
-
 /* 操作按钮栏 */
 /* 操作按钮栏 */
 .action-bar {
 .action-bar {
   display: flex;
   display: flex;
@@ -484,9 +446,8 @@ export default {
 .action-btn-primary {
 .action-btn-primary {
   background: #FF8C42;
   background: #FF8C42;
 }
 }
-.action-btn-accent {
-  background: #FFF0E0;
-}
+.action-btn-accent { background: linear-gradient(135deg, #10B981, #34D399); }
+.action-btn-text-accent { font-size: 24rpx; color: #FFFFFF; }
 .action-btn-disabled {
 .action-btn-disabled {
   opacity: 0.4;
   opacity: 0.4;
 }
 }
@@ -498,15 +459,12 @@ export default {
   font-size: 24rpx;
   font-size: 24rpx;
   color: #FFFFFF;
   color: #FFFFFF;
 }
 }
-.action-btn-text-accent {
-  font-size: 24rpx;
-  color: #FF8C42;
-}
 
 
 /* 食材列表 */
 /* 食材列表 */
 .ingredient-list {
 .ingredient-list {
   margin-top: 8rpx;
   margin-top: 8rpx;
 }
 }
+.ingredient-item:active { background: #FFF9F0; }
 .ingredient-item {
 .ingredient-item {
   display: flex;
   display: flex;
   align-items: center;
   align-items: center;
@@ -601,22 +559,14 @@ export default {
   align-items: center;
   align-items: center;
   justify-content: center;
   justify-content: center;
   margin-bottom: 12rpx;
   margin-bottom: 12rpx;
-}
-.nav-icon-green {
-  background: linear-gradient(135deg, #E8F5E9, #C8E6C9);
-}
-.nav-icon-blue {
-  background: linear-gradient(135deg, #E3F2FD, #BBDEFB);
-}
-.nav-icon-orange {
-  background: linear-gradient(135deg, #FFF3E0, #FFE0B2);
-}
-.nav-icon-purple {
-  background: linear-gradient(135deg, #F3E5F5, #E1BEE7);
-}
-.nav-icon-red {
-  background: linear-gradient(135deg, #FBE9E7, #FFCCBC);
-}
+  transition: transform 0.15s ease;
+}
+.nav-icon-green { background: linear-gradient(135deg, #FFF1E6, #FFE0C0); }
+.nav-icon-blue  { background: linear-gradient(135deg, #FFF7ED, #FFEEDD); }
+.nav-icon-orange{ background: linear-gradient(135deg, #FF8C42, #FFB366); }
+.nav-icon-purple{ background: linear-gradient(135deg, #FFE8D6, #FFD4B0); }
+.nav-icon-red   { background: linear-gradient(135deg, #E8F8F1, #C8F0E0); }
+.nav-item:active .nav-icon-wrap { transform: scale(0.92); }
 .nav-icon {
 .nav-icon {
   font-size: 36rpx;
   font-size: 36rpx;
 }
 }
@@ -641,7 +591,7 @@ export default {
 .trend-divider {
 .trend-divider {
   width: 1rpx;
   width: 1rpx;
   height: 60rpx;
   height: 60rpx;
-  background: #F0F0F0;
+  background: #FDE8D0;
 }
 }
 .trend-num {
 .trend-num {
   font-size: 48rpx;
   font-size: 48rpx;
@@ -660,12 +610,6 @@ export default {
 }
 }
 
 
 /* 空状态 */
 /* 空状态 */
-.empty-hint {
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  padding: 20rpx 0;
-}
 .empty-state {
 .empty-state {
   display: flex;
   display: flex;
   flex-direction: column;
   flex-direction: column;

+ 39 - 74
cfc-frontend/pages/diet/meal-config.vue

@@ -5,10 +5,10 @@
       <!-- 日期类型切换 -->
       <!-- 日期类型切换 -->
       <view class="date-tabs">
       <view class="date-tabs">
         <view class="tab-item" :class="{'tab-active': dateType === 'weekday'}" @tap="switchDateType('weekday')">
         <view class="tab-item" :class="{'tab-active': dateType === 'weekday'}" @tap="switchDateType('weekday')">
-          <text>工作日</text>
+          <text class="tab-text">工作日</text>
         </view>
         </view>
         <view class="tab-item" :class="{'tab-active': dateType === 'weekend'}" @tap="switchDateType('weekend')">
         <view class="tab-item" :class="{'tab-active': dateType === 'weekend'}" @tap="switchDateType('weekend')">
-          <text>周末</text>
+          <text class="tab-text">周末</text>
         </view>
         </view>
       </view>
       </view>
 
 
@@ -22,15 +22,16 @@
       <!-- 餐次配置 -->
       <!-- 餐次配置 -->
       <view class="meal-section" v-for="meal in mealGroups" :key="meal.value">
       <view class="meal-section" v-for="meal in mealGroups" :key="meal.value">
         <view class="meal-header">
         <view class="meal-header">
-          <text class="meal-icon">{{ meal.icon }}</text>
+          <view class="meal-icon-wrap" :class="'meal-icon-' + meal.value"><text class="meal-icon">{{ meal.icon }}</text></view>
           <text class="meal-name">{{ meal.label }}</text>
           <text class="meal-name">{{ meal.label }}</text>
+          <text class="meal-count" v-if="meal.selectedCount > 0">已选 {{ meal.selectedCount }} 人</text>
         </view>
         </view>
         <view class="member-list">
         <view class="member-list">
           <view class="member-item" v-for="member in meal.members" :key="member.id"
           <view class="member-item" v-for="member in meal.members" :key="member.id"
             :class="{'member-selected': member.selected}"
             :class="{'member-selected': member.selected}"
             @tap="toggleMember(meal.value, member.id)">
             @tap="toggleMember(meal.value, member.id)">
             <view class="member-check" v-if="member.selected">✓</view>
             <view class="member-check" v-if="member.selected">✓</view>
-            <text class="member-avatar">{{ member.nickname ? member.nickname.substring(0, 1) : '?' }}</text>
+            <text class="member-avatar" :class="member._avatarCls">{{ member.nickname ? member.nickname.substring(0, 1) : '?' }}</text>
             <text class="member-name">{{ member.nickname || '未知' }}</text>
             <text class="member-name">{{ member.nickname || '未知' }}</text>
           </view>
           </view>
           <view class="member-empty" v-if="meal.members.length === 0">
           <view class="member-empty" v-if="meal.members.length === 0">
@@ -90,14 +91,16 @@ export default {
           return {
           return {
             id: m.id,
             id: m.id,
             nickname: m.nickname,
             nickname: m.nickname,
-            selected: ids.indexOf(m.id) >= 0
+            selected: ids.indexOf(m.id) >= 0,
+            _avatarCls: m.gender === 'male' ? 'member-avatar-male' : (m.gender === 'female' ? 'member-avatar-female' : 'member-avatar-unknown')
           }
           }
         })
         })
         return {
         return {
           value: meal.value,
           value: meal.value,
           label: meal.label,
           label: meal.label,
           icon: meal.icon,
           icon: meal.icon,
-          members: members
+          members: members,
+          selectedCount: ids.length
         }
         }
       })
       })
     }
     }
@@ -206,26 +209,17 @@ export default {
   padding: 30rpx;
   padding: 30rpx;
 }
 }
 .date-tabs {
 .date-tabs {
-  display: flex;
-  gap: 20rpx;
-  margin-bottom: 30rpx;
+  display: flex; background: #FFF1E6; border-radius: 999rpx;
+  padding: 6rpx; margin-bottom: 30rpx;
 }
 }
 .tab-item {
 .tab-item {
-  box-sizing: border-box;
-  flex: 1;
-  padding: 20rpx 0;
-  border: 1rpx solid #ddd;
-  border-radius: 30rpx;
-  text-align: center;
-  font-size: 26rpx;
-  color: #666;
-  background-color: #FFFFFF;
+  flex: 1; text-align: center; padding: 18rpx 0;
+  border-radius: 999rpx; font-size: 28rpx; color: #C2410C;
 }
 }
 .tab-active {
 .tab-active {
-  border-color: #FF8C42;
-  color: #FF8C42;
-  background-color: #FFF5EB;
-  font-weight: 600;
+  background: linear-gradient(135deg, #FF8C42, #FFB366);
+  color: #FFFFFF; font-weight: 600;
+  box-shadow: 0 4rpx 12rpx rgba(255, 140, 66, 0.3);
 }
 }
 .meal-section {
 .meal-section {
   background-color: #FFFFFF;
   background-color: #FFFFFF;
@@ -240,10 +234,11 @@ export default {
   padding: 24rpx 30rpx;
   padding: 24rpx 30rpx;
   border-bottom: 1rpx solid #f0f0f0;
   border-bottom: 1rpx solid #f0f0f0;
 }
 }
-.meal-icon {
-  font-size: 36rpx;
-  margin-right: 16rpx;
-}
+.meal-icon-wrap { width: 72rpx; height: 72rpx; border-radius: 22rpx; display: flex; align-items: center; justify-content: center; margin-right: 16rpx; }
+.meal-icon-breakfast { background: linear-gradient(135deg, #FFB366, #FFD48A); }
+.meal-icon-lunch     { background: linear-gradient(135deg, #FBBF24, #FCD34D); }
+.meal-icon-dinner    { background: linear-gradient(135deg, #A78BFA, #C4B5FD); }
+.meal-count { font-size: 24rpx; color: #C2410C; }
 .meal-name {
 .meal-name {
   font-size: 28rpx;
   font-size: 28rpx;
   font-weight: 600;
   font-weight: 600;
@@ -256,25 +251,15 @@ export default {
   padding: 24rpx 30rpx;
   padding: 24rpx 30rpx;
 }
 }
 .member-item {
 .member-item {
-  box-sizing: border-box;
-  position: relative;
-  display: flex;
-  flex-direction: column;
-  align-items: center;
-  justify-content: center;
-  padding: 16rpx 20rpx;
-  border: 2rpx solid #E0E0E0;
-  border-radius: 16rpx;
-  background-color: #FAFAFA;
-  min-width: 132rpx;
-  max-width: 100%;
+  box-sizing: border-box; position: relative;
+  display: flex; flex-direction: column; align-items: center; justify-content: center;
+  padding: 16rpx 20rpx; border: 2rpx solid #E0E0E0; border-radius: 20rpx;
+  background: #FAFAFA; min-width: 132rpx;
   transition: all 0.15s ease;
   transition: all 0.15s ease;
 }
 }
 .member-selected {
 .member-selected {
-  border-color: #FF8C42;
-  border-width: 3rpx;
-  background-color: #FFF5EB;
-  box-shadow: 0 0 0 4rpx rgba(255, 140, 66, 0.18);
+  border-color: #FF8C42; border-width: 3rpx;
+  background: #FFF5EB; box-shadow: 0 0 0 4rpx rgba(255, 140, 66, 0.18);
 }
 }
 .member-check {
 .member-check {
   position: absolute;
   position: absolute;
@@ -293,21 +278,13 @@ export default {
   z-index: 2;
   z-index: 2;
 }
 }
 .member-avatar {
 .member-avatar {
-  width: 60rpx;
-  height: 60rpx;
-  border-radius: 50%;
-  background-color: #C8CDD4;
-  color: #FFFFFF;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  font-size: 24rpx;
-  font-weight: 600;
-  margin-bottom: 8rpx;
-}
-.member-selected .member-avatar {
-  background-color: #FF8C42;
+  width: 60rpx; height: 60rpx; border-radius: 50%;
+  color: #FFFFFF; display: flex; align-items: center; justify-content: center;
+  font-size: 24rpx; font-weight: 600; margin-bottom: 8rpx;
 }
 }
+.member-avatar-male   { background: linear-gradient(135deg, #5B9BD5, #8FC5E8); }
+.member-avatar-female { background: linear-gradient(135deg, #F472B6, #F9A8D4); }
+.member-avatar-unknown{ background: linear-gradient(135deg, #FF8C42, #FFB366); }
 .member-name {
 .member-name {
   font-size: 22rpx;
   font-size: 22rpx;
   color: #666;
   color: #666;
@@ -354,27 +331,15 @@ export default {
   color: #FF8C42;
   color: #FF8C42;
   margin-left: 8rpx;
   margin-left: 8rpx;
 }
 }
-.tip-card {
-  background-color: #E3F2FD;
-  border-radius: 16rpx;
-  padding: 24rpx 30rpx;
-  margin-bottom: 30rpx;
-}
-.tip-text {
-  font-size: 24rpx;
-  color: #1976D2;
-  line-height: 1.6;
-}
+.tip-card { background: #FFF1E6; border-radius: 16rpx; padding: 24rpx 30rpx; margin-bottom: 30rpx; }
+.tip-text { font-size: 24rpx; color: #C2410C; line-height: 1.6; }
 .save-area {
 .save-area {
   padding: 40rpx 30rpx;
   padding: 40rpx 30rpx;
 }
 }
 .save-btn {
 .save-btn {
-  background-color: #FF8C42;
-  color: #FFFFFF;
-  text-align: center;
-  padding: 28rpx 0;
-  border-radius: 16rpx;
-  font-size: 30rpx;
-  font-weight: 600;
+  background: linear-gradient(135deg, #FF8C42, #FFB366);
+  color: #FFFFFF; text-align: center; padding: 28rpx 0; border-radius: 999rpx;
+  font-size: 30rpx; font-weight: 600;
+  box-shadow: 0 6rpx 20rpx rgba(255, 140, 66, 0.25);
 }
 }
 </style>
 </style>

+ 50 - 101
cfc-frontend/pages/diet/recommendation.vue

@@ -4,8 +4,9 @@
     <scroll-view class="content" scroll-y>
     <scroll-view class="content" scroll-y>
       <!-- 无推荐状态 -->
       <!-- 无推荐状态 -->
       <view class="empty-state" v-if="!recommendation">
       <view class="empty-state" v-if="!recommendation">
-        <text class="empty-icon">🍽️</text>
-        <text class="empty-text">暂无今日食谱推荐</text>
+        <view class="empty-icon-wrap"><text class="empty-icon">🍽️</text></view>
+        <text class="empty-title">今日菜单空着</text>
+        <text class="empty-sub">点击生成今日三餐食谱</text>
         <view class="generate-btn" @tap="generateRecipe">
         <view class="generate-btn" @tap="generateRecipe">
           <text>生成食谱</text>
           <text>生成食谱</text>
         </view>
         </view>
@@ -27,7 +28,9 @@
         <!-- 菜品列表 -->
         <!-- 菜品列表 -->
         <view class="meal-section" v-for="(meal, index) in meals" :key="index">
         <view class="meal-section" v-for="(meal, index) in meals" :key="index">
           <view class="meal-header">
           <view class="meal-header">
-            <text class="meal-icon">{{ meal.icon }}</text>
+            <view class="meal-icon-wrap" :class="'meal-icon-' + meal.type">
+              <text class="meal-icon">{{ meal.icon }}</text>
+            </view>
             <text class="meal-name">{{ meal.name }}</text>
             <text class="meal-name">{{ meal.name }}</text>
             <text class="meal-count">{{ meal.participants }} 人份</text>
             <text class="meal-count">{{ meal.participants }} 人份</text>
           </view>
           </view>
@@ -47,9 +50,9 @@
                 <text class="method-text">🍳 {{ dish.method }}</text>
                 <text class="method-text">🍳 {{ dish.method }}</text>
               </view>
               </view>
               <view class="dish-actions">
               <view class="dish-actions">
-                <view class="action-btn like-btn" @tap="likeDish(meal.name, dIndex)">👍</view>
-                <view class="action-btn swap-btn" @tap="swapDish(meal.name, dIndex)">🔄</view>
-                <view class="action-btn skip-btn" @tap="skipDish(meal.name, dIndex)">✕</view>
+                <view class="action-btn like-btn" @tap="likeDish(meal.name, dIndex)">👍 好吃</view>
+                <view class="action-btn swap-btn" @tap="swapDish(meal.name, dIndex)">🔄 替换</view>
+                <view class="action-btn skip-btn" @tap="skipDish(meal.name, dIndex)">✕ 跳过</view>
               </view>
               </view>
             </view>
             </view>
           </view>
           </view>
@@ -159,6 +162,7 @@ export default {
             self.meals.push({
             self.meals.push({
               name: config.name,
               name: config.name,
               icon: config.icon,
               icon: config.icon,
+              type: meal.type,
               participants: meal.participants || 1,
               participants: meal.participants || 1,
               dishes: meal.dishes || []
               dishes: meal.dishes || []
             })
             })
@@ -229,29 +233,22 @@ export default {
   flex: 1;
   flex: 1;
   padding: 30rpx;
   padding: 30rpx;
 }
 }
-.empty-state {
-  text-align: center;
-  padding: 100rpx 40rpx;
-}
-.empty-icon {
-  font-size: 120rpx;
-  display: block;
-  margin-bottom: 30rpx;
-}
-.empty-text {
-  font-size: 28rpx;
-  color: #999;
-  display: block;
+.empty-state { display: flex; flex-direction: column; align-items: center; padding: 120rpx 40rpx; }
+.empty-icon-wrap {
+  width: 160rpx; height: 160rpx; border-radius: 50%;
+  background: linear-gradient(135deg, #FF8C42, #FFB366);
+  display: flex; align-items: center; justify-content: center;
+  box-shadow: 0 10rpx 30rpx rgba(255, 140, 66, 0.3);
   margin-bottom: 40rpx;
   margin-bottom: 40rpx;
 }
 }
+.empty-icon { font-size: 72rpx; }
+.empty-title { font-size: 34rpx; font-weight: 700; color: #333; display: block; margin-bottom: 16rpx; }
+.empty-sub { font-size: 26rpx; color: #999; display: block; margin-bottom: 48rpx; }
 .generate-btn {
 .generate-btn {
-  background-color: #FF8C42;
-  color: #FFFFFF;
-  padding: 28rpx 60rpx;
-  border-radius: 30rpx;
-  font-size: 30rpx;
-  font-weight: 600;
-  display: inline-block;
+  background: linear-gradient(135deg, #FF8C42, #FFB366);
+  color: #FFFFFF; padding: 28rpx 60rpx; border-radius: 999rpx;
+  font-size: 30rpx; font-weight: 600;
+  box-shadow: 0 6rpx 20rpx rgba(255, 140, 66, 0.25);
 }
 }
 .card {
 .card {
   display: flex;
   display: flex;
@@ -261,11 +258,7 @@ export default {
   box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
   box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
   overflow: hidden;
   overflow: hidden;
 }
 }
-.card-accent {
-  width: 8rpx;
-  background-color: #FF8C42;
-  flex-shrink: 0;
-}
+.card-accent { width: 8rpx; background: linear-gradient(180deg, #FF8C42, #FFB366); flex-shrink: 0; }
 .card-body {
 .card-body {
   flex: 1;
   flex: 1;
   padding: 30rpx;
   padding: 30rpx;
@@ -292,12 +285,9 @@ export default {
   border-radius: 20rpx;
   border-radius: 20rpx;
   font-size: 24rpx;
   font-size: 24rpx;
 }
 }
-.status-pending {
-  background-color: #FFF3E0;
-  color: #FF9800;
-}
+.status-pending { background: #FFF1E6; color: #C2410C; }
 .status-accepted {
 .status-accepted {
-  background-color: #E3F2FD;
+  background: #E8F1FB;
   color: #2196F3;
   color: #2196F3;
 }
 }
 .status-completed {
 .status-completed {
@@ -311,12 +301,11 @@ export default {
   overflow: hidden;
   overflow: hidden;
   box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
   box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
 }
 }
-.meal-header {
-  display: flex;
-  align-items: center;
-  padding: 24rpx 30rpx;
-  border-bottom: 1rpx solid #f0f0f0;
-}
+.meal-header { display: flex; align-items: center; padding: 24rpx 30rpx; border-bottom: 1rpx solid #FDE8D0; }
+.meal-icon-wrap { width: 64rpx; height: 64rpx; border-radius: 20rpx; display: flex; align-items: center; justify-content: center; margin-right: 16rpx; }
+.meal-icon-breakfast { background: linear-gradient(135deg, #FFB366, #FFD48A); }
+.meal-icon-lunch     { background: linear-gradient(135deg, #FBBF24, #FCD34D); }
+.meal-icon-dinner    { background: linear-gradient(135deg, #A78BFA, #C4B5FD); }
 .meal-icon {
 .meal-icon {
   font-size: 36rpx;
   font-size: 36rpx;
   margin-right: 12rpx;
   margin-right: 12rpx;
@@ -334,27 +323,15 @@ export default {
 .dish-list {
 .dish-list {
   padding: 20rpx 30rpx;
   padding: 20rpx 30rpx;
 }
 }
-.dish-item {
-  padding: 24rpx 0;
-  border-bottom: 1rpx solid #f0f0f0;
-}
-.dish-item:last-child {
-  border-bottom: none;
-}
+.dish-item { padding: 24rpx; border-radius: 20rpx; background: #FFF9F0; margin-bottom: 16rpx; }
+.dish-item:last-child { margin-bottom: 0; }
 .dish-header {
 .dish-header {
   display: flex;
   display: flex;
   justify-content: space-between;
   justify-content: space-between;
   margin-bottom: 12rpx;
   margin-bottom: 12rpx;
 }
 }
-.dish-name {
-  font-size: 26rpx;
-  font-weight: 500;
-  color: #333;
-}
-.dish-cal {
-  font-size: 24rpx;
-  color: #FF8C42;
-}
+.dish-name { font-size: 28rpx; font-weight: 600; color: #333; }
+.dish-cal { font-size: 24rpx; color: #C2410C; font-weight: 600; }
 .dish-ingredients {
 .dish-ingredients {
   display: flex;
   display: flex;
   flex-wrap: wrap;
   flex-wrap: wrap;
@@ -375,27 +352,11 @@ export default {
   font-size: 24rpx;
   font-size: 24rpx;
   color: #999;
   color: #999;
 }
 }
-.dish-actions {
-  display: flex;
-  gap: 16rpx;
-}
-.action-btn {
-  padding: 12rpx 24rpx;
-  border-radius: 20rpx;
-  font-size: 24rpx;
-}
-.like-btn {
-  background-color: #E8F5E9;
-  color: #4CAF50;
-}
-.swap-btn {
-  background-color: #E3F2FD;
-  color: #2196F3;
-}
-.skip-btn {
-  background-color: #FFEBEE;
-  color: #F44336;
-}
+.dish-actions { display: flex; gap: 16rpx; margin-top: 8rpx; }
+.action-btn { padding: 12rpx 26rpx; border-radius: 999rpx; font-size: 24rpx; }
+.like-btn { background: #E8F5E9; color: #4CAF50; }
+.swap-btn { background: #E8F1FB; color: #2196F3; }
+.skip-btn { background: #FFEBEE; color: #F44336; }
 .nutrition-grid {
 .nutrition-grid {
   display: flex;
   display: flex;
   justify-content: space-around;
   justify-content: space-around;
@@ -403,12 +364,8 @@ export default {
 .nutrition-item {
 .nutrition-item {
   text-align: center;
   text-align: center;
 }
 }
-.nutrition-value {
-  font-size: 36rpx;
-  font-weight: 600;
-  color: #FF8C42;
-  display: block;
-}
+.nutrition-card .card-body { background: #FFF9F0; border-radius: 0 0 20rpx 20rpx; }
+.nutrition-value { font-size: 40rpx; font-weight: 700; color: #C2410C; display: block; }
 .nutrition-unit {
 .nutrition-unit {
   font-size: 22rpx;
   font-size: 22rpx;
   color: #999;
   color: #999;
@@ -427,23 +384,15 @@ export default {
   gap: 20rpx;
   gap: 20rpx;
 }
 }
 .btn-secondary {
 .btn-secondary {
-  flex: 1;
-  padding: 28rpx 0;
-  background-color: #FFFFFF;
-  border: 1rpx solid #ddd;
-  border-radius: 16rpx;
-  text-align: center;
-  font-size: 28rpx;
-  color: #666;
+  flex: 1; padding: 28rpx 0; background: #FFFFFF;
+  border: 2rpx solid #FF8C42; border-radius: 999rpx;
+  text-align: center; font-size: 28rpx; color: #C2410C;
 }
 }
 .btn-primary {
 .btn-primary {
-  flex: 1;
-  padding: 28rpx 0;
-  background-color: #FF8C42;
-  border-radius: 16rpx;
-  text-align: center;
-  font-size: 28rpx;
-  color: #FFFFFF;
-  font-weight: 600;
+  flex: 1; padding: 28rpx 0;
+  background: linear-gradient(135deg, #FF8C42, #FFB366);
+  border-radius: 999rpx; text-align: center;
+  font-size: 28rpx; color: #FFFFFF; font-weight: 600;
+  box-shadow: 0 6rpx 20rpx rgba(255, 140, 66, 0.25);
 }
 }
 </style>
 </style>

+ 1 - 1
cfc-web/.last_build_commit

@@ -1 +1 @@
-c8ece43ebb56699a69b0e4a50f484cffa56ac3a5
+3c072274d4797d6768b18ac16561d1ffc5d2598f

+ 2 - 2
cfc-web/package-lock.json

@@ -1,12 +1,12 @@
 {
 {
   "name": "cfc-web",
   "name": "cfc-web",
-  "version": "1.0.1235",
+  "version": "1.0.1236",
   "lockfileVersion": 3,
   "lockfileVersion": 3,
   "requires": true,
   "requires": true,
   "packages": {
   "packages": {
     "": {
     "": {
       "name": "cfc-web",
       "name": "cfc-web",
-      "version": "1.0.1235",
+      "version": "1.0.1236",
       "dependencies": {
       "dependencies": {
         "@wangeditor/editor": "^5.1.23",
         "@wangeditor/editor": "^5.1.23",
         "@wangeditor/editor-for-vue": "^1.0.2",
         "@wangeditor/editor-for-vue": "^1.0.2",

+ 1 - 1
cfc-web/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "cfc-web",
   "name": "cfc-web",
-  "version": "1.0.1236",
+  "version": "1.0.1237",
   "private": true,
   "private": true,
   "scripts": {
   "scripts": {
     "dev": "vue-cli-service serve",
     "dev": "vue-cli-service serve",

+ 20 - 0
cfc-web/public/CHANGELOG-v1.0.md

@@ -4,6 +4,26 @@
 
 
 ---
 ---
 
 
+## v1.0.1237 (2026-08-28)
+
+### Bug 修复
+- 修复最终审查3处Minor偏差(副标题人数/nav-icon-wrap重复/status-accepted暖蓝)
+- 移除食谱页未使用的 iconCls 死代码
+- 生成食谱按钮文字改白色(修复样式覆盖冲突)
+
+### 新功能
+- 食谱推荐页暖灶主题美化
+- 共餐配置页暖灶主题美化
+- 饮食首页菜篮/导航/趋势暖灶主题化
+- 饮食首页今日餐桌主视觉(暖灶主题)
+- add scrollbar to rich editor text containers
+
+### 文档
+- 共餐食谱美化实现计划(暖灶主题)
+- 共餐食谱设计文档自检修正(数据一致性/确定性)
+- 共餐&食谱页面美化设计文档(暖灶·家的味道)
+
+
 ## v1.0.1236 (2026-08-28)
 ## v1.0.1236 (2026-08-28)
 
 
 ### Bug 修复
 ### Bug 修复

+ 21 - 1
cfc-web/public/CHANGELOG.md

@@ -1,6 +1,6 @@
 # 更新日志
 # 更新日志
 
 
-> 当前版本: v1.0.1236
+> 当前版本: v1.0.1237
 
 
 ## 历史版本
 ## 历史版本
 
 
@@ -8,6 +8,26 @@
 
 
 ---
 ---
 
 
+## v1.0.1237 (2026-08-28)
+
+### Bug 修复
+- 修复最终审查3处Minor偏差(副标题人数/nav-icon-wrap重复/status-accepted暖蓝)
+- 移除食谱页未使用的 iconCls 死代码
+- 生成食谱按钮文字改白色(修复样式覆盖冲突)
+
+### 新功能
+- 食谱推荐页暖灶主题美化
+- 共餐配置页暖灶主题美化
+- 饮食首页菜篮/导航/趋势暖灶主题化
+- 饮食首页今日餐桌主视觉(暖灶主题)
+- add scrollbar to rich editor text containers
+
+### 文档
+- 共餐食谱美化实现计划(暖灶主题)
+- 共餐食谱设计文档自检修正(数据一致性/确定性)
+- 共餐&食谱页面美化设计文档(暖灶·家的味道)
+
+
 ## v1.0.1236 (2026-08-28)
 ## v1.0.1236 (2026-08-28)
 
 
 ### Bug 修复
 ### Bug 修复