面向 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 边界内操作)。
# 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 完成。
diet/index.vue — 今日餐桌主视觉(替换原 page-header + 今日共餐卡片)文件: 修改 cfc-frontend/pages/diet/index.vue
将原 <view class="page-header">...</view> + 「今日共餐」<view class="card"> 合并为一个暖橙渐变主视觉大卡:
<!-- ===== 今日餐桌主视觉 ===== -->
<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>
<script> 增加 heroSubtitle / heroMeals / hasMealConfig 计算字段(不改接口)在 computed 中新增(methods 原有逻辑不动):
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> 顶部先定义页面令牌)
/* ===== 页面令牌(暖灶·家的味道) ===== */
.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
git add cfc-frontend/pages/diet/index.vue
git commit -m "feat(frontend): 饮食首页今日餐桌主视觉(暖灶主题)"
diet/index.vue — 今日菜篮 + 厨房导航 + 本周饮食文件: 修改 cfc-frontend/pages/diet/index.vue(承接任务 1)
template 头部改为(保留 action-bar 三个按钮的绑定与事件不动):
<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>
在 <style scoped> 中替换/新增:
.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);
/* 保留现有圆角方块结构与其余属性 */
}
替换 nav-icon-* 五个背景为暖调(可整体替换五个类名背景值,类名保留以免改 template):
.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:本周饮食卡暖化(分隔线浅橙 + 大数字橙字已具,改分隔线)
.trend-divider { background: #FDE8D0; }
[ ] 步骤 5:运行全局校验命令确认无禁止模式
[ ] 步骤 6:Commit
git add cfc-frontend/pages/diet/index.vue
git commit -m "feat(frontend): 饮食首页菜篮/导航/趋势暖灶主题化"
diet/meal-config.vue — 共餐配置页美化文件: 修改 cfc-frontend/pages/diet/meal-config.vue
template 的 date-tabs 保留结构,样式整体替换:
<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>
.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);
}
template 的 meal-header 增加已选人数:
<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。
.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; }
替换 member-avatar / member-selected 样式:
.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:提示条暖橙化 + 保存按钮渐变
.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
git add cfc-frontend/pages/diet/meal-config.vue
git commit -m "feat(frontend): 共餐配置页暖灶主题美化"
diet/recommendation.vue — 食谱推荐页美化文件: 修改 cfc-frontend/pages/diet/recommendation.vue
template 替换空态区块:
<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>
.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);
}
card-accent 改为橙渐变竖条、meal-header 增加渐变 icon 圆底(复用任务 3 的三餐配色类)、dish-item 改淡橙底块:
.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 内方法调用。
template 的 dish-actions 三个按钮改为「图标 + 文字」(保留 @tap 事件绑定):
<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>
.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:营养汇总卡浅暖底 + 宫格分隔浅橙
.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:底部操作(重新生成描边 / 标记完成渐变)
.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
git add cfc-frontend/pages/diet/recommendation.vue
git commit -m "feat(frontend): 食谱推荐页暖灶主题美化"
文件: 全部三个页面(只读复核)
[ ] 步骤 1:核对规格 §设计令牌 的色值在三个页面 scoped 样式中的使用是否统一(主橙 #FF8C42、深橙 #C2410C、米底 #FFF7ED、嫩绿 #10B981 出现位置与规格一致)
[ ] 步骤 2:复核业务逻辑零改动:git diff 三个文件,确认 methods/data/onLoad/onShow 仅新增 computed 字段与类映射字段,未改任何接口调用、参数、跳转路径
[ ] 步骤 3:跑一次全量校验命令(三文件同时)确认最终无禁止模式
[ ] 步骤 4:Commit(若有遗漏修正)
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): 饮食三页暖灶主题一致性复核修正"
meal.iconCls、member._avatarCls、heroMeals[].key 等字段在对应 computed 中定义后才在 template 使用