From 7920c0bba276e79a98dbad2aa20114e21946a2f9 Mon Sep 17 00:00:00 2001 From: toor Date: Wed, 21 Oct 2026 12:43:16 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E5=88=A0=E9=99=A4=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E5=8E=9F=E5=9E=8B=E7=9B=AE=E5=BD=95=EF=BC=8C=E9=94=81=E5=AE=9A?= =?UTF-8?q?=20Node=2024?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 移除 prototype/ 下 cook-desktop 与 cook-redesign 两套静态原型稿(37 个文件), 它们是设计过程产物,不是产品源码;eslint 的对应 ignore 规则一并撤掉。 engines.node 收紧为 >=22.0.0 <25:Node 26 下 ESLint worker 会原生崩溃 (退出码 0xC0000005),实测 Node 24 下连续 6 次 lint 全部正常。 --- CLAUDE.md | 2 +- apps/web/app/constants/index.ts | 2 +- docs/guide/getting-started.md | 2 +- eslint.config.js | 2 - package.json | 2 +- prototype/cook-desktop/_pages/about.html | 79 - prototype/cook-desktop/_pages/cook.html | 154 -- prototype/cook-desktop/_pages/discover.html | 64 - prototype/cook-desktop/_pages/help.html | 119 - prototype/cook-desktop/_pages/library.html | 82 - prototype/cook-desktop/_pages/me.html | 144 -- prototype/cook-desktop/_pages/recipe.html | 87 - prototype/cook-desktop/_pages/settings.html | 138 -- prototype/cook-desktop/about.html | 217 -- prototype/cook-desktop/api.js | 178 -- prototype/cook-desktop/app.js | 232 -- prototype/cook-desktop/build.mjs | 180 -- prototype/cook-desktop/discover.html | 202 -- prototype/cook-desktop/help.html | 257 -- prototype/cook-desktop/icons.js | 73 - prototype/cook-desktop/index.html | 292 --- prototype/cook-desktop/library.html | 220 -- prototype/cook-desktop/me.html | 282 --- prototype/cook-desktop/mock.js | 463 ---- prototype/cook-desktop/page-about.js | 11 - prototype/cook-desktop/page-cook.js | 609 ----- prototype/cook-desktop/page-discover.js | 189 -- prototype/cook-desktop/page-help.js | 32 - prototype/cook-desktop/page-library.js | 281 --- prototype/cook-desktop/page-me.js | 172 -- prototype/cook-desktop/page-recipe.js | 213 -- prototype/cook-desktop/page-settings.js | 87 - prototype/cook-desktop/recipe.html | 225 -- prototype/cook-desktop/settings.html | 276 --- prototype/cook-desktop/state.js | 230 -- prototype/cook-desktop/styles.css | 2474 ------------------- prototype/cook-redesign/app.js | 678 ----- prototype/cook-redesign/icons.js | 55 - prototype/cook-redesign/index.html | 315 --- prototype/cook-redesign/mock.js | 284 --- prototype/cook-redesign/styles.css | 1438 ----------- 41 files changed, 4 insertions(+), 11038 deletions(-) delete mode 100644 prototype/cook-desktop/_pages/about.html delete mode 100644 prototype/cook-desktop/_pages/cook.html delete mode 100644 prototype/cook-desktop/_pages/discover.html delete mode 100644 prototype/cook-desktop/_pages/help.html delete mode 100644 prototype/cook-desktop/_pages/library.html delete mode 100644 prototype/cook-desktop/_pages/me.html delete mode 100644 prototype/cook-desktop/_pages/recipe.html delete mode 100644 prototype/cook-desktop/_pages/settings.html delete mode 100644 prototype/cook-desktop/about.html delete mode 100644 prototype/cook-desktop/api.js delete mode 100644 prototype/cook-desktop/app.js delete mode 100644 prototype/cook-desktop/build.mjs delete mode 100644 prototype/cook-desktop/discover.html delete mode 100644 prototype/cook-desktop/help.html delete mode 100644 prototype/cook-desktop/icons.js delete mode 100644 prototype/cook-desktop/index.html delete mode 100644 prototype/cook-desktop/library.html delete mode 100644 prototype/cook-desktop/me.html delete mode 100644 prototype/cook-desktop/mock.js delete mode 100644 prototype/cook-desktop/page-about.js delete mode 100644 prototype/cook-desktop/page-cook.js delete mode 100644 prototype/cook-desktop/page-discover.js delete mode 100644 prototype/cook-desktop/page-help.js delete mode 100644 prototype/cook-desktop/page-library.js delete mode 100644 prototype/cook-desktop/page-me.js delete mode 100644 prototype/cook-desktop/page-recipe.js delete mode 100644 prototype/cook-desktop/page-settings.js delete mode 100644 prototype/cook-desktop/recipe.html delete mode 100644 prototype/cook-desktop/settings.html delete mode 100644 prototype/cook-desktop/state.js delete mode 100644 prototype/cook-desktop/styles.css delete mode 100644 prototype/cook-redesign/app.js delete mode 100644 prototype/cook-redesign/icons.js delete mode 100644 prototype/cook-redesign/index.html delete mode 100644 prototype/cook-redesign/mock.js delete mode 100644 prototype/cook-redesign/styles.css diff --git a/CLAUDE.md b/CLAUDE.md index 69c9338..2fb209b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -86,5 +86,5 @@ See `.env.example`: ## Node/Package Manager -- Node.js: 22 or newer +- Node.js: 24 (pinned via `.nvmrc`; Node 26 crashes ESLint natively) - pnpm: 11.21.0 (enforced via `packageManager` field) diff --git a/apps/web/app/constants/index.ts b/apps/web/app/constants/index.ts index f2a9c1b..49abc28 100644 --- a/apps/web/app/constants/index.ts +++ b/apps/web/app/constants/index.ts @@ -2,7 +2,7 @@ export const appName = '食用手册' export const appDescription = '好的,今天我们来做菜!' export const namespace = 'cook' -export const lastDbUpdated = '2026-10-21 12:26:30' +export const lastDbUpdated = '2026-10-21 12:41:15' export const icp = '苏ICP备17038157号' diff --git a/docs/guide/getting-started.md b/docs/guide/getting-started.md index 680f617..0af11a9 100644 --- a/docs/guide/getting-started.md +++ b/docs/guide/getting-started.md @@ -6,7 +6,7 @@ ### 系统要求 -- Node.js >= 22 +- Node.js 24(`.nvmrc` 锁定;Node 26 上 ESLint 会原生崩溃,暂不支持) - pnpm >= 11.21 ### 安装依赖 diff --git a/eslint.config.js b/eslint.config.js index ea72e7a..05f4a64 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -11,8 +11,6 @@ export default nuxt( { ignores: [ 'packages/data/**/*.json', - // 设计原型静态稿,不是产品源码。 - 'prototype/**/*', 'apps/web/public/**/*', 'node_modules/**/*', '**/.nuxt/**/*', diff --git a/package.json b/package.json index 87d28e5..a943c3e 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "private": true, "packageManager": "pnpm@11.21.0", "engines": { - "node": ">=22.0.0", + "node": ">=22.0.0 <25", "pnpm": "11.21.0" }, "scripts": { diff --git a/prototype/cook-desktop/_pages/about.html b/prototype/cook-desktop/_pages/about.html deleted file mode 100644 index 66ed099..0000000 --- a/prototype/cook-desktop/_pages/about.html +++ /dev/null @@ -1,79 +0,0 @@ -
-
-
-

关于

-

这个网站从哪来,能干什么

-
-
- - - -

- 食用手册 · 静态原型 · 数据来自 packages/data -

-
diff --git a/prototype/cook-desktop/_pages/cook.html b/prototype/cook-desktop/_pages/cook.html deleted file mode 100644 index 9b38071..0000000 --- a/prototype/cook-desktop/_pages/cook.html +++ /dev/null @@ -1,154 +0,0 @@ -
-
-
-

今天做点什么

-

选好食材,看看能做什么

-
-
- -
-
- -
- -
-
-
-

我的食材

- -
-
- 还没选食材 —— 下面挑几样,或者直接搜名字 -
-
- -
- -
-
- -
-
-

蔬菜

- -
-
-
- -
-
-

肉和蛋

- -
-
-
- -
-
-

主食

- -
-
-
- -
-
-

厨具

- 只看用它做的菜 -
-
-
- -
-
- -
-
-

做法

-
-
-
-

菜系

-
-
-
-

难度

-
-
-
-
-
-
- -
-
-

调料和忌口

- 常备调料算作已有 -
-
-
-
-

家里常备

-
-
-
-

忌口

-
-
-
- 不吃辣 - -
-
-
-
-
- - -
-
-

选好食材,看看能做什么

-

厨具可选,不选也能找菜谱

-
-
- - - -
- - -
-
- -
-
-
-
- -

只核对菜谱里记录的食材,西红柿算番茄、鸡翅算鸡肉;常备调料只在菜谱写明时算作已有。用量和调味以视频为准。

-
-
-
- - -
-
- 选好食材,看看能做什么 - 厨具可选,不选也能找菜谱 -
- -
diff --git a/prototype/cook-desktop/_pages/discover.html b/prototype/cook-desktop/_pages/discover.html deleted file mode 100644 index b901a5f..0000000 --- a/prototype/cook-desktop/_pages/discover.html +++ /dev/null @@ -1,64 +0,0 @@ -
-
-
-

发现

-

不知道做什么的时候,从这里翻翻

-
-
- -
-
- -
-
- 随机推荐 -

正在抽一道菜…

-

别纠结了,就它了

-
- - -
-
-
- -
-
-

按菜系逛

- -
-
-
- -
-
-

按厨具找

- 家里有什么锅,就做什么菜 -
-
-
- -
-
-

热门菜谱

- 有点播放量的都在这儿 -
-
-
- -
-
-

一日三餐

- 按早中晚分好的常见组合 -
- -
-
diff --git a/prototype/cook-desktop/_pages/help.html b/prototype/cook-desktop/_pages/help.html deleted file mode 100644 index 087c09c..0000000 --- a/prototype/cook-desktop/_pages/help.html +++ /dev/null @@ -1,119 +0,0 @@ -
-
-
-

帮助

-

用之前想知道的几件事

-
-
- - -
diff --git a/prototype/cook-desktop/_pages/library.html b/prototype/cook-desktop/_pages/library.html deleted file mode 100644 index 8ef10a7..0000000 --- a/prototype/cook-desktop/_pages/library.html +++ /dev/null @@ -1,82 +0,0 @@ -
-
-
-

菜谱库

-

按菜名、食材或厨具翻找所有菜谱

-
-
- - -
-
- -
- -
- - - - -
- 共 0 道 -
- - -
- - - - - - - - - - - - -
菜名食材厨具做法难度
-
- - -
- -
- - - -
diff --git a/prototype/cook-desktop/_pages/me.html b/prototype/cook-desktop/_pages/me.html deleted file mode 100644 index 4a37982..0000000 --- a/prototype/cook-desktop/_pages/me.html +++ /dev/null @@ -1,144 +0,0 @@ -
-
-
-

我的

-

收藏、历史和自己记的菜谱都在这儿

-
-
- -
-
- -
- -
- -
-
-
- 收藏 - 0 -
-
- 浏览历史 - 0 -
-
- 我的菜谱 - 0 -
-
- 上次同步 - — -
-
-
- - - - -
diff --git a/prototype/cook-desktop/_pages/recipe.html b/prototype/cook-desktop/_pages/recipe.html deleted file mode 100644 index 3f3bc58..0000000 --- a/prototype/cook-desktop/_pages/recipe.html +++ /dev/null @@ -1,87 +0,0 @@ -
-
-
-

菜谱详情

-

从菜名、食材和厨具看这道菜

-
- -
- -
-
-
- -
-

加载中…

-

-
-
- -
- - - - -
- -
- -
-
-

需要准备

- -
-
-
-
-
- -
-
-

做法与标签

-
-
-
-
-
-
- - -
-
diff --git a/prototype/cook-desktop/_pages/settings.html b/prototype/cook-desktop/_pages/settings.html deleted file mode 100644 index bc49050..0000000 --- a/prototype/cook-desktop/_pages/settings.html +++ /dev/null @@ -1,138 +0,0 @@ -
-
-
-

设置

-

推荐规则、外观和数据都在这儿调

-
-
- - - -
-
-

数据来源

-
-
-
-

菜谱来自仓库里的 packages/data/recipe.json,共 0 条记录;食材、厨具、别名和相克数据来自同目录下的 food.json、aliases.json、incompatible-foods.json。

-

推荐规则与正式应用的 packages/cook/src/utils/recommend.ts 保持一致:缺料少的排前面,大杂烩排同组末尾。

-
-
-
-
diff --git a/prototype/cook-desktop/about.html b/prototype/cook-desktop/about.html deleted file mode 100644 index db63010..0000000 --- a/prototype/cook-desktop/about.html +++ /dev/null @@ -1,217 +0,0 @@ - - - - - - 关于 · 食用手册 - - - - - - - - - - -
- - - 食用手册 - - - -
- -
-
-
-
-

关于

-

这个网站从哪来,能干什么

-
-
- - - -

- 食用手册 · 静态原型 · 数据来自 packages/data -

-
-
- - - - - - - -
- - - - - - - - - diff --git a/prototype/cook-desktop/api.js b/prototype/cook-desktop/api.js deleted file mode 100644 index 3c195f7..0000000 --- a/prototype/cook-desktop/api.js +++ /dev/null @@ -1,178 +0,0 @@ -/** - * API 桩层 —— 函数签名、延迟与响应形状即未来真实 API。 - * 每个函数头部注明 HTTP method + path 与请求/响应形状; - * 接后端时只替换函数体,调用方不用改。 - * - * 说明:本原型不接真实后端、不建库、不做真实鉴权; - * 登录用前端假校验(见 signIn)。 - */ -;(function () { - const M = window.Mock - - /** 模拟网络延迟,让 loading / 骨架屏状态真实可见 */ - function delay(ms = 320) { - return new Promise(resolve => setTimeout(resolve, ms)) - } - - function ok(data) { - return { code: 0, data } - } - - // --------------------------------------------------------------------------- - // GET /api/recipes/search - // query: { stuff[], pantry[], tool, methodGroups[], cuisines[], difficulty[], - // noSpicy, avoid[], keyword, mode } - // resp: { code:0, data:{ list: Match[], total:number } } - // --------------------------------------------------------------------------- - async function searchRecipes(query) { - await delay(280) - const list = M.findRecipes(query) - return ok({ list, total: list.length }) - } - - // --------------------------------------------------------------------------- - // GET /api/recipes?keyword=&view= view: all | fav | history | mine - // resp: { code:0, data:{ list: Recipe[], total:number } } - // --------------------------------------------------------------------------- - async function listRecipes({ keyword = '', view = 'all' } = {}) { - await delay(260) - const state = window.Store.state - // 自定义菜谱和内置菜谱一起参与检索 - const all = [...M.RECIPES, ...state.customRecipes] - let list = all.slice() - if (view === 'fav') - list = list.filter(r => state.favorites.has(r.id)) - if (view === 'history') - list = state.history.map(id => all.find(r => r.id === id)).filter(Boolean) - if (view === 'mine') - list = state.customRecipes.slice() - const text = keyword.trim() - if (text) - list = list.filter(r => r.name.includes(text)) - return ok({ list, total: list.length }) - } - - // --------------------------------------------------------------------------- - // GET /api/recipes/:id - // resp: { code:0, data: Recipe | null } - // --------------------------------------------------------------------------- - async function getRecipe(id) { - await delay(180) - const state = window.Store.state - const recipe = M.RECIPES.find(r => r.id === Number(id)) || state.customRecipes.find(r => r.id === Number(id)) || null - return ok(recipe) - } - - // --------------------------------------------------------------------------- - // GET /api/recipes/random - // resp: { code:0, data: Recipe } - // --------------------------------------------------------------------------- - async function getRandomRecipe() { - await delay(220) - return ok(M.guessRecipe()) - } - - // --------------------------------------------------------------------------- - // GET /api/foods?q= - // resp: { code:0, data: Food[] } - // --------------------------------------------------------------------------- - async function searchFoods(q) { - await delay(120) - return ok(M.searchFoods(q)) - } - - // --------------------------------------------------------------------------- - // GET /api/foods/check?stuff= - // resp: { code:0, data:{ hasWarning:boolean, message:string } } - // --------------------------------------------------------------------------- - async function checkIncompatible(stuff) { - await delay(160) - return ok(M.checkIncompatible(stuff)) - } - - // --------------------------------------------------------------------------- - // GET /api/recipes/suggestions?stuff= - // resp: { code:0, data: { name:string, count:number }[] } - // --------------------------------------------------------------------------- - async function getUnlockSuggestions(stuff) { - await delay(200) - return ok(M.unlockSuggestions(stuff)) - } - - // --------------------------------------------------------------------------- - // GET /api/recipes/cuisines - // resp: { code:0, data: { label:string, count:number }[] } - // --------------------------------------------------------------------------- - async function listCuisines() { - await delay(200) - return ok(M.CUISINE_GROUPS.map(g => ({ - label: g.label, - count: M.recipesByCuisine(g.label).length, - }))) - } - - // --------------------------------------------------------------------------- - // GET /api/recipes/hot - // resp: { code:0, data: Recipe[] } - // --------------------------------------------------------------------------- - async function listHotRecipes() { - await delay(240) - return ok(M.RECIPES.filter(r => r.bv).slice(0, 12)) - } - - // --------------------------------------------------------------------------- - // GET /api/user/profile - // resp: { code:0, data:{ displayName, email, avatarUrl, syncedAt } } - // --------------------------------------------------------------------------- - async function getProfile() { - await delay(240) - return ok(M.USER) - } - - // --------------------------------------------------------------------------- - // POST /api/auth/signin body: { account:string } - // resp: { code:0, data:{ displayName } } | { code:401, message:string } - // --------------------------------------------------------------------------- - async function signIn(account) { - await delay(600) - const name = String(account || '').trim() - if (!name) - return { code: 400, message: '请填写账号' } - return ok({ displayName: name }) - } - - // --------------------------------------------------------------------------- - // POST /api/user/sync - // resp: { code:0, data:{ syncedAt:string } } - // --------------------------------------------------------------------------- - async function syncUserData() { - await delay(900) - return ok({ syncedAt: new Date().toISOString().slice(0, 16).replace('T', ' ') }) - } - - // --------------------------------------------------------------------------- - // POST /api/feedback body: { recipeId?, recipeName, kind, note? } - // resp: { code:0, data:{ accepted:true } } - // --------------------------------------------------------------------------- - async function submitFeedback(payload) { - await delay(700) - return ok({ accepted: true, payload }) - } - - window.Api = { - delay, - searchRecipes, - listRecipes, - getRecipe, - getRandomRecipe, - searchFoods, - checkIncompatible, - getUnlockSuggestions, - listCuisines, - listHotRecipes, - getProfile, - signIn, - syncUserData, - submitFeedback, - } -})() diff --git a/prototype/cook-desktop/app.js b/prototype/cook-desktop/app.js deleted file mode 100644 index 871f37d..0000000 --- a/prototype/cook-desktop/app.js +++ /dev/null @@ -1,232 +0,0 @@ -/** - * 全站共享行为:图标渲染、导航激活态、主题切换、Toast、菜谱详情弹层。 - * 每个页面都引入本文件;页面自己的逻辑放在各自的页面脚本里。 - */ -;(function () { - const { icon } = window.Icons - const Store = window.Store - - // ---------- 图标渲染 ---------- - function paintIcons(root = document) { - root.querySelectorAll('[data-icon]').forEach((el) => { - if (el.dataset.painted) - return - el.innerHTML = icon(el.dataset.icon) - el.dataset.painted = '1' - }) - } - - // ---------- Toast ---------- - let toastTimer = 0 - function toast(message) { - const el = document.getElementById('toast') - if (!el) - return - el.textContent = message - el.classList.add('is-on') - clearTimeout(toastTimer) - toastTimer = setTimeout(() => el.classList.remove('is-on'), 1800) - } - - // ---------- 导航激活态 ---------- - // 视觉状态由 CSS 依据 body[data-page] 匹配 [data-nav] 决定(唯一机制); - // 这里只补 aria-current,保证无障碍语义正确。 - function markActiveNav() { - const page = document.body.dataset.page - // 菜谱详情属于「菜谱」这一组 - const group = page === 'recipe' ? 'library' : page - document.querySelectorAll('[data-nav]').forEach((el) => { - if (el.dataset.nav === group) - el.setAttribute('aria-current', 'page') - else - el.removeAttribute('aria-current') - }) - } - - // ---------- 主题切换 ---------- - function setupTheme() { - const buttons = document.querySelectorAll('[data-theme-toggle]') - buttons.forEach((btn) => { - btn.addEventListener('click', () => { - const dark = document.documentElement.getAttribute('data-theme') === 'dark' - Store.setTheme(dark ? 'light' : 'dark') - Store.applyTheme() - updateThemeButtons() - }) - }) - updateThemeButtons() - } - - function updateThemeButtons() { - const dark = document.documentElement.getAttribute('data-theme') === 'dark' - document.querySelectorAll('[data-theme-toggle]').forEach((btn) => { - const label = btn.querySelector('[data-theme-label]') - if (label) - label.textContent = dark ? '浅色模式' : '深色模式' - const ico = btn.querySelector('[data-theme-icon]') - if (ico) { - // 主题图标要跟着切换,直接替换内容,不能只改 data-icon(已绘制过不会重绘) - ico.innerHTML = icon(dark ? 'sun' : 'moon') - } - }) - } - - // ---------- 菜谱详情弹层 ---------- - const M = window.Mock - - function openRecipe(id) { - const modal = document.getElementById('recipe-modal') - if (!modal) - return - const recipe = M.RECIPES.find(r => r.id === Number(id)) || Store.state.customRecipes.find(r => r.id === Number(id)) - if (!recipe) - return - - const cuisine = M.recipeCuisine(recipe) - const tool = M.recipeToolLabel(recipe) - const selected = new Set(Store.state.selected) - const required = M.uniqueStuff(recipe.stuff) - const have = required.filter(s => selected.has(s)) - const miss = required.filter(s => !selected.has(s)) - - const groups = [] - if (have.length) - groups.push({ label: '已有', items: have, tone: 'have' }) - if (miss.length) - groups.push({ label: '还缺', items: miss, tone: 'missing' }) - if (!have.length && !miss.length) - groups.push({ label: '食材', items: required, tone: 'neutral' }) - - const thumb = document.getElementById('recipe-thumb') - const title = document.getElementById('recipe-title') - const meta = document.getElementById('recipe-meta') - const groupsBox = document.getElementById('recipe-groups') - const favBtn = document.getElementById('recipe-fav') - const videoBtn = document.getElementById('recipe-video') - - thumb.textContent = M.recipeEmoji(recipe) - title.textContent = recipe.name - meta.textContent = [tool, recipe.difficulty, cuisine].filter(Boolean).join(' · ') - - groupsBox.innerHTML = groups.map(g => ` -
-

${g.label} · ${g.items.length} 种

-
- ${g.items.map(i => `${escapeHtml(i)}`).join('')} -
-
`).join('') - - const favorited = Store.isFavorite(recipe.id) - favBtn.classList.toggle('is-on', favorited) - favBtn.setAttribute('aria-pressed', String(favorited)) - favBtn.setAttribute('aria-label', favorited ? '取消收藏' : '收藏') - favBtn.innerHTML = icon(favorited ? 'star' : 'star') - favBtn.dataset.id = String(recipe.id) - - videoBtn.hidden = !recipe.bv - videoBtn.dataset.id = String(recipe.id) - - modal.dataset.id = String(recipe.id) - modal.classList.add('is-open') - document.body.style.overflow = 'hidden' - } - - function closeRecipe() { - const modal = document.getElementById('recipe-modal') - if (!modal) - return - modal.classList.remove('is-open') - document.body.style.overflow = '' - } - - function escapeHtml(text) { - return String(text ?? '').replace(/[&<>"']/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[c]) - } - - function setupRecipeModal() { - const modal = document.getElementById('recipe-modal') - if (!modal) - return - - modal.addEventListener('click', (e) => { - if (e.target === modal) - closeRecipe() - }) - - const searchBtn = document.getElementById('recipe-search') - if (searchBtn) { - searchBtn.addEventListener('click', () => { - const id = Number(modal.dataset.id) - const recipe = M.RECIPES.find(r => r.id === id) || Store.state.customRecipes.find(r => r.id === id) - if (!recipe) - return - Store.addHistory(recipe.id) - window.open(`https://search.bilibili.com/all?keyword=${encodeURIComponent(recipe.name)}`, '_blank') - }) - } - - const videoBtn = document.getElementById('recipe-video') - if (videoBtn) { - videoBtn.addEventListener('click', () => { - const id = Number(videoBtn.dataset.id) - const recipe = M.RECIPES.find(r => r.id === id) - if (!recipe || !recipe.bv) - return - Store.addHistory(recipe.id) - window.open(`https://www.bilibili.com/video/${recipe.bv}`, '_blank') - }) - } - - const favBtn = document.getElementById('recipe-fav') - if (favBtn) { - favBtn.addEventListener('click', () => { - const id = Number(favBtn.dataset.id) - const recipe = M.RECIPES.find(r => r.id === id) || Store.state.customRecipes.find(r => r.id === id) - if (!recipe) - return - const favorited = Store.toggleFavorite(recipe) - favBtn.classList.toggle('is-on', favorited) - favBtn.setAttribute('aria-pressed', String(favorited)) - favBtn.setAttribute('aria-label', favorited ? '取消收藏' : '收藏') - favBtn.innerHTML = icon(favorited ? 'star' : 'star') - toast(favorited ? '已添加到收藏' : '已从收藏移除') - document.dispatchEvent(new CustomEvent('favorite:change', { detail: { id: recipe.id, favorited } })) - }) - } - - document.addEventListener('keydown', (e) => { - if (e.key === 'Escape') - closeRecipe() - }) - } - - // ---------- 启动 ---------- - document.addEventListener('DOMContentLoaded', () => { - paintIcons() - markActiveNav() - setupTheme() - setupRecipeModal() - - // 侧边栏的菜谱总数 - document.querySelectorAll('[data-recipe-count]').forEach((el) => { - el.textContent = String(M.RECIPES.length) - }) - - // 系统主题变化时,auto 模式下跟着变 - window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { - if (Store.state.theme === 'auto') { - Store.applyTheme() - updateThemeButtons() - } - }) - }) - - window.App = { - paintIcons, - toast, - openRecipe, - closeRecipe, - escapeHtml, - markActiveNav, - } -})() diff --git a/prototype/cook-desktop/build.mjs b/prototype/cook-desktop/build.mjs deleted file mode 100644 index 6e99824..0000000 --- a/prototype/cook-desktop/build.mjs +++ /dev/null @@ -1,180 +0,0 @@ -/** - * 生成静态页面。 - * - * App Shell(侧边栏 / 顶栏 / 底部 tab / 详情弹层)只在这里写一次, - * 每个页面都套同一份外壳,导航不会在页面之间漂移。 - * 页面内容放在 _pages/.html,页面脚本放在 _pages/.js。 - * - * 运行: node build.mjs - */ -import { readFileSync, writeFileSync, readdirSync } from 'node:fs' -import { join, dirname } from 'node:path' -import { fileURLToPath } from 'node:url' - -const ROOT = dirname(fileURLToPath(import.meta.url)) -const PAGES_DIR = join(ROOT, '_pages') - -/** 冻结的 App Shell —— 所有页面逐字相同 */ -const SHELL = ` - - - - - __TITLE__ - - - - - - - - - - -
- - - 食用手册 - - - -
- -
-__BODY__ -
- - - - - - - -
- - - - - - - - - -` - -/** 页面清单:输出文件名 / data-page / 标题 / 页面内容文件 / 页面脚本 */ -const PAGES = [ - { out: 'index.html', page: 'cook', title: '做菜 · 食用手册', body: 'cook.html', script: 'page-cook.js' }, - { out: 'library.html', page: 'library', title: '菜谱库 · 食用手册', body: 'library.html', script: 'page-library.js' }, - { out: 'discover.html', page: 'discover', title: '发现 · 食用手册', body: 'discover.html', script: 'page-discover.js' }, - { out: 'me.html', page: 'me', title: '我的 · 食用手册', body: 'me.html', script: 'page-me.js' }, - { out: 'recipe.html', page: 'recipe', title: '菜谱详情 · 食用手册', body: 'recipe.html', script: 'page-recipe.js' }, - { out: 'settings.html', page: 'settings', title: '设置 · 食用手册', body: 'settings.html', script: 'page-settings.js' }, - { out: 'help.html', page: 'help', title: '帮助 · 食用手册', body: 'help.html', script: 'page-help.js' }, - { out: 'about.html', page: 'about', title: '关于 · 食用手册', body: 'about.html', script: 'page-about.js' }, -] - -for (const page of PAGES) { - const body = readFileSync(join(PAGES_DIR, page.body), 'utf8').trim() - const html = SHELL - .replace('__TITLE__', page.title) - .replace('__PAGE__', page.page) - .replace('__BODY__', body) - .replace('__SCRIPT__', page.script) - writeFileSync(join(ROOT, page.out), html, 'utf8') - console.log('built', page.out) -} diff --git a/prototype/cook-desktop/discover.html b/prototype/cook-desktop/discover.html deleted file mode 100644 index ca58a1f..0000000 --- a/prototype/cook-desktop/discover.html +++ /dev/null @@ -1,202 +0,0 @@ - - - - - - 发现 · 食用手册 - - - - - - - - - - -
- - - 食用手册 - - - -
- -
-
-
-
-

发现

-

不知道做什么的时候,从这里翻翻

-
-
- -
-
- -
-
- 随机推荐 -

正在抽一道菜…

-

别纠结了,就它了

-
- - -
-
-
- -
-
-

按菜系逛

- -
-
-
- -
-
-

按厨具找

- 家里有什么锅,就做什么菜 -
-
-
- -
-
-

热门菜谱

- 有点播放量的都在这儿 -
-
-
- -
-
-

一日三餐

- 按早中晚分好的常见组合 -
- -
-
-
- - - - - - - -
- - - - - - - - - diff --git a/prototype/cook-desktop/help.html b/prototype/cook-desktop/help.html deleted file mode 100644 index 89b744b..0000000 --- a/prototype/cook-desktop/help.html +++ /dev/null @@ -1,257 +0,0 @@ - - - - - - 帮助 · 食用手册 - - - - - - - - - - -
- - - 食用手册 - - - -
- -
-
-
-
-

帮助

-

用之前想知道的几件事

-
-
- - -
-
- - - - - - - -
- - - - - - - - - diff --git a/prototype/cook-desktop/icons.js b/prototype/cook-desktop/icons.js deleted file mode 100644 index 8b0ec39..0000000 --- a/prototype/cook-desktop/icons.js +++ /dev/null @@ -1,73 +0,0 @@ -/** - * 内联 SVG 图标集(Lucide 路径风格,零外部依赖,离线可用) - * 统一规范:viewBox 0 0 24 24,stroke-width 1.7,currentColor - * 菜品缩略图使用 emoji 作为内容语义,不占用图标位 - */ -const ICONS = { - // 导航 - chefHat: '', - book: '', - compass: '', - user: '', - - // 通用 - search: '', - plus: '', - x: '', - star: '', - play: '', - chevronDown: '', - chevronRight: '', - chevronLeft: '', - sliders: '', - refresh: '', - clock: '', - history: '', - settings: '', - info: '', - message: '', - mail: '', - fileText: '', - helpCircle: '', - logIn: '', - logOut: '', - cloud: '', - utensils: '', - trash: '', - edit: '', - share: '', - flame: '', - bowl: '', - grid: '', - sparkle: '', - layers: '', - wallet: '', - external: '', - sun: '', - moon: '', - filter: '', - check: '', - alert: '', - table: '', - rows: '', - pot: '', - list: '', - arrowLeft: '', - tag: '', - heart: '', - - // 厨具 - oven: '', - fryer: '', - microwave: '', - riceCooker: '', - wok: '', - clayPot: '', -} - -function icon(name, cls = '') { - const path = ICONS[name] || '' - return `` -} - -window.Icons = { icon, ICONS } diff --git a/prototype/cook-desktop/index.html b/prototype/cook-desktop/index.html deleted file mode 100644 index 6e0d55f..0000000 --- a/prototype/cook-desktop/index.html +++ /dev/null @@ -1,292 +0,0 @@ - - - - - - 做菜 · 食用手册 - - - - - - - - - - -
- - - 食用手册 - - - -
- -
-
-
-
-

今天做点什么

-

选好食材,看看能做什么

-
-
- -
-
- -
- -
-
-
-

我的食材

- -
-
- 还没选食材 —— 下面挑几样,或者直接搜名字 -
-
- -
- -
-
- -
-
-

蔬菜

- -
-
-
- -
-
-

肉和蛋

- -
-
-
- -
-
-

主食

- -
-
-
- -
-
-

厨具

- 只看用它做的菜 -
-
-
- -
-
- -
-
-

做法

-
-
-
-

菜系

-
-
-
-

难度

-
-
-
-
-
-
- -
-
-

调料和忌口

- 常备调料算作已有 -
-
-
-
-

家里常备

-
-
-
-

忌口

-
-
-
- 不吃辣 - -
-
-
-
-
- - -
-
-

选好食材,看看能做什么

-

厨具可选,不选也能找菜谱

-
-
- - - -
- - -
-
- -
-
-
-
- -

只核对菜谱里记录的食材,西红柿算番茄、鸡翅算鸡肉;常备调料只在菜谱写明时算作已有。用量和调味以视频为准。

-
-
-
- - -
-
- 选好食材,看看能做什么 - 厨具可选,不选也能找菜谱 -
- -
-
- - - - - - - -
- - - - - - - - - diff --git a/prototype/cook-desktop/library.html b/prototype/cook-desktop/library.html deleted file mode 100644 index 257b7ee..0000000 --- a/prototype/cook-desktop/library.html +++ /dev/null @@ -1,220 +0,0 @@ - - - - - - 菜谱库 · 食用手册 - - - - - - - - - - -
- - - 食用手册 - - - -
- -
-
-
-
-

菜谱库

-

按菜名、食材或厨具翻找所有菜谱

-
-
- - -
-
- -
- -
- - - - -
- 共 0 道 -
- - -
- - - - - - - - - - - - -
菜名食材厨具做法难度
-
- - -
- -
- - - -
-
- - - - - - - -
- - - - - - - - - diff --git a/prototype/cook-desktop/me.html b/prototype/cook-desktop/me.html deleted file mode 100644 index 4f8b9ea..0000000 --- a/prototype/cook-desktop/me.html +++ /dev/null @@ -1,282 +0,0 @@ - - - - - - 我的 · 食用手册 - - - - - - - - - - -
- - - 食用手册 - - - -
- -
-
-
-
-

我的

-

收藏、历史和自己记的菜谱都在这儿

-
-
- -
-
- -
- -
- -
-
-
- 收藏 - 0 -
-
- 浏览历史 - 0 -
-
- 我的菜谱 - 0 -
-
- 上次同步 - — -
-
-
- - - - -
-
- - - - - - - -
- - - - - - - - - diff --git a/prototype/cook-desktop/mock.js b/prototype/cook-desktop/mock.js deleted file mode 100644 index cdb3114..0000000 --- a/prototype/cook-desktop/mock.js +++ /dev/null @@ -1,463 +0,0 @@ -/** - * 原型 mock 数据层(由 packages/data 真实数据生成,勿手改) - * 生成脚本: scripts/gen-prototype-mock.mjs - * - * 食材与厨具词典: packages/data/food.json - * 菜谱: packages/data/recipe.json(全量 637 条) - * 常备调料: packages/data/pantry.json - * 食材别名: packages/data/aliases.json - * 食物相克: packages/data/incompatible-foods.json - */ -;(function () { - const FOOD = { - "vegetable": [ - { - "name": "土豆", - "emoji": "🥔" - }, - { - "name": "胡萝卜", - "emoji": "🥕" - }, - { - "name": "花菜", - "emoji": "🥦" - }, - { - "name": "白萝卜", - "emoji": "🥣" - }, - { - "name": "西葫芦", - "emoji": "🥒" - }, - { - "name": "番茄", - "emoji": "🍅", - "alias": "西红柿" - }, - { - "name": "芹菜", - "emoji": "🥬" - }, - { - "name": "黄瓜", - "emoji": "🥒" - }, - { - "name": "洋葱", - "emoji": "🧅" - }, - { - "name": "莴笋", - "emoji": "🎍" - }, - { - "name": "菌菇", - "emoji": "🍄" - }, - { - "name": "茄子", - "emoji": "🍆" - }, - { - "name": "豆腐", - "emoji": "🍲" - }, - { - "name": "包菜", - "emoji": "🥗" - }, - { - "name": "白菜", - "emoji": "🥬" - }, - { - "name": "青椒", - "emoji": "🫑", - "alias": "青辣椒" - }, - { - "name": "韭菜", - "emoji": "🌿" - }, - { - "name": "南瓜", - "emoji": "🎃" - } - ], - "meat": [ - { - "name": "午餐肉", - "emoji": "🥓" - }, - { - "name": "香肠", - "emoji": "🌭" - }, - { - "name": "腊肠", - "emoji": "🌭" - }, - { - "name": "鸡肉", - "emoji": "🐤" - }, - { - "name": "猪肉", - "emoji": "🐷" - }, - { - "name": "鸡蛋", - "emoji": "🥚" - }, - { - "name": "虾", - "emoji": "🦐" - }, - { - "name": "牛肉", - "emoji": "🐮" - }, - { - "name": "骨头", - "emoji": "🦴" - }, - { - "name": "鱼", - "emoji": "🐟" - }, - { - "name": "蟹", - "emoji": "🦀" - } - ], - "staple": [ - { - "name": "面食", - "emoji": "🍝" - }, - { - "name": "面包", - "emoji": "🍞" - }, - { - "name": "米", - "emoji": "🍚" - }, - { - "name": "方便面", - "emoji": "🍜" - }, - { - "name": "玉米", - "emoji": "🌽", - "alias": "苞米" - }, - { - "name": "红薯", - "emoji": "🍠", - "alias": "地瓜" - } - ], - "tools": [ - { - "name": "烤箱", - "emoji": "", - "icon": "i-mdi-toaster-oven" - }, - { - "name": "空气炸锅", - "emoji": "", - "icon": "i-fe-frying-pan" - }, - { - "name": "微波炉", - "emoji": "", - "icon": "i-ic-outline-microwave" - }, - { - "name": "电饭煲", - "emoji": "", - "icon": "i-gg-smart-home-cooker" - }, - { - "label": "一口能炒又能煮的大锅", - "name": "一口大锅", - "emoji": "", - "icon": "i-mdi-pot-steam-outline" - }, - { - "name": "砂锅", - "emoji": "", - "icon": "i-mdi-pot-mix" - } - ] - } - - const RECIPES = [{"id":0,"name":"电饭煲版广式腊肠煲饭","stuff":["腊肠","米"],"bv":"BV1NE411Q7Jj","difficulty":"简单","tags":["广式"],"methods":["煲"],"tools":["电饭煲"]},{"id":1,"name":"电饭煲版烧鸡","stuff":["鸡肉","洋葱","菌菇"],"bv":"BV1T54y1U7Cu","difficulty":"简单","tags":["好吃"],"methods":["煲"],"tools":["电饭煲"]},{"id":2,"name":"电饭煲焖面","stuff":["猪肉","面食"],"bv":"BV14b411q7rM","difficulty":"简单","tags":["筋道"],"methods":["焖","煲"],"tools":["电饭煲"]},{"id":3,"name":"电饭煲版番茄牛腩焖饭","stuff":["牛肉","番茄","米"],"bv":"BV1Bv411C7X3","difficulty":"简单","tags":["懒人"],"methods":["焖","煲"],"tools":["电饭煲"]},{"id":4,"name":"电饭煲版蜜汁鸡翅","stuff":["鸡肉"],"bv":"BV1dj411f7sR","difficulty":"简单","tags":["懒人"],"methods":["煲"],"tools":["电饭煲"]},{"id":5,"name":"电饭煲版南瓜鸡腿焖饭","stuff":["鸡肉","洋葱","菌菇","米"],"bv":"BV1Bv411C7X3","difficulty":"简单","tags":["懒人"],"methods":["焖","煲"],"tools":["电饭煲"]},{"id":6,"name":"电饭煲版土豆排骨焖饭","stuff":["猪肉","土豆","米","腊肠"],"bv":"BV1Bv411C7X3","difficulty":"简单","tags":["懒人"],"methods":["焖","煲"],"tools":["电饭煲"]},{"id":7,"name":"电饭煲版香菇腊肠焖饭","stuff":["腊肠","菌菇","洋葱","米","土豆"],"bv":"BV1Bv411C7X3","difficulty":"简单","tags":["懒人"],"methods":["焖","煲"],"tools":["电饭煲"]},{"id":8,"name":"电饭煲版香卤牛肉","stuff":["牛肉","米"],"bv":"BV1dR4y1F7H2","difficulty":"简单","tags":["卤味"],"methods":["煮"],"tools":["电饭煲"]},{"id":9,"name":"电饭煲叉烧排骨","stuff":["猪肉","洋葱"],"bv":"BV1Ab4y1p7dw","difficulty":"简单","tags":["下饭"],"methods":["煲","烤"],"tools":["电饭煲"]},{"id":10,"name":"电饭煲版红烧肉","stuff":["猪肉"],"bv":"BV1SW411t7AX","difficulty":"","tags":[],"methods":["煲","烧"],"tools":["电饭煲"]},{"id":11,"name":"电饭煲版可乐鸡翅","stuff":["鸡肉"],"bv":"BV16J411U79k","difficulty":"","tags":[],"methods":["烧"],"tools":["电饭煲"]},{"id":12,"name":"电饭煲版蛋糕(废手版)","stuff":["鸡蛋","面食"],"bv":"BV1H7411V7Pz","difficulty":"简单","tags":["香软"],"methods":["煲"],"tools":["电饭煲"]},{"id":13,"name":"电饭煲卤菜(开店级别)","stuff":["鸡肉","鸡蛋","米"],"bv":"BV1ZA411E7KT","difficulty":"简单","tags":["小吃"],"methods":["卤","煲"],"tools":["电饭煲"]},{"id":14,"name":"电饭煲卤肉蛋豆腐","stuff":["鸡肉","鸡蛋","豆腐","米"],"bv":"BV1aM4y1L7QR","difficulty":"简单","tags":["小吃"],"methods":["卤","煲"],"tools":["电饭煲"]},{"id":15,"name":"电饭煲版罗宋汤","stuff":["牛肉","番茄","洋葱","芹菜","胡萝卜","土豆","包菜","香肠"],"bv":"BV16Q4y1m7nU","difficulty":"简单","tags":["杂烩","汤"],"methods":["煲","煮"],"tools":["电饭煲"]},{"id":16,"name":"电饭煲版一只番茄饭","stuff":["土豆","胡萝卜","香肠","番茄","鸡蛋","米"],"bv":"BV1dj411f7sR","difficulty":"简单","tags":["杂烩"],"methods":["煲"],"tools":["电饭煲"]},{"id":17,"name":"电饭煲茄子烧肉焖饭","stuff":["猪肉","茄子","番茄","土豆","洋葱","米"],"bv":"BV18T4y1c7s1","difficulty":"普通","tags":["杂烩"],"methods":["焖","煲","烧"],"tools":["电饭煲"]},{"id":18,"name":"电饭煲版茶叶蛋","stuff":["鸡蛋"],"bv":"BV12W411R7LA","difficulty":"","tags":[],"methods":["煲"],"tools":["电饭煲"]},{"id":19,"name":"电饭煲版番茄炖排骨","stuff":["番茄","猪肉","米"],"bv":"BV1av411r7mL","difficulty":"","tags":[],"methods":["炖","煲"],"tools":["电饭煲"]},{"id":20,"name":"电饭煲版番茄牛腩","stuff":["番茄","牛肉","米"],"bv":"BV18i4y1g7sp","difficulty":"","tags":[],"methods":["煲"],"tools":["电饭煲"]},{"id":21,"name":"电饭煲版干妈炒泡面","stuff":["方便面","鸡蛋"],"bv":"BV1kW411r7Py","difficulty":"简单","tags":["辣"],"methods":["炒","煲"],"tools":["电饭煲"]},{"id":22,"name":"电饭煲版红烧肉","stuff":["猪肉","米"],"bv":"BV1SW411t7AX","difficulty":"","tags":[],"methods":["煲","烧"],"tools":["电饭煲"]},{"id":23,"name":"电饭煲版胡萝卜腊肠饭","stuff":["胡萝卜","腊肠","米"],"bv":"BV1tW411b7LS","difficulty":"","tags":[],"methods":["煲"],"tools":["电饭煲"]},{"id":24,"name":"电饭煲版卤鸡翅","stuff":["鸡肉"],"bv":"BV1UK4y1Y7aC","difficulty":"简单","tags":[],"methods":["卤"],"tools":["电饭煲"]},{"id":25,"name":"电饭煲版焖鸡腿土豆","stuff":["鸡肉","土豆"],"bv":"BV1tR4y1g7kv","difficulty":"简单","tags":[],"methods":["焖","煲"],"tools":["电饭煲"]},{"id":26,"name":"电饭煲版蜜汁叉烧肉","stuff":["猪肉","米"],"bv":"BV1ci4y1j7Rz","difficulty":"简单","tags":[],"methods":["煲","烤"],"tools":["电饭煲"]},{"id":27,"name":"电饭煲版蜜汁鸡腿","stuff":["鸡肉"],"bv":"BV1A4411k7iz","difficulty":"普通","tags":[],"methods":["煲"],"tools":["电饭煲"]},{"id":28,"name":"电饭煲版牛腩萝卜清汤","stuff":["牛肉","白萝卜"],"bv":"BV16Q4y1m7nU","difficulty":"","tags":["汤"],"methods":["煲","煮"],"tools":["电饭煲"]},{"id":29,"name":"电饭煲版茄子猪肉焖面","stuff":["茄子","猪肉","面食"],"bv":"BV1ib41137ig","difficulty":"困难","tags":[],"methods":["焖","煲"],"tools":["电饭煲"]},{"id":30,"name":"电饭煲版肉饼蛋","stuff":["猪肉","鸡蛋"],"bv":"BV13W411a7Ax","difficulty":"普通","tags":[],"methods":["煲"],"tools":["电饭煲"]},{"id":31,"name":"电饭煲版上海菜饭","stuff":["猪肉","腊肠","米","包菜","莴笋"],"bv":"BV1mW411x7F7","difficulty":"","tags":[],"methods":["煲"],"tools":["电饭煲"]},{"id":32,"name":"电饭煲版蒜蓉排骨","stuff":["猪肉","米"],"bv":"BV1Dv411T7bD","difficulty":"","tags":[],"methods":["煲"],"tools":["电饭煲"]},{"id":33,"name":"电饭煲版土豆香肠焖饭","stuff":["土豆","香肠","米"],"bv":"BV1Kv411q7BM","difficulty":"简单","tags":[],"methods":["焖","煲"],"tools":["电饭煲"]},{"id":34,"name":"电饭煲版吐司(尽量不做,废手)","stuff":["鸡蛋","面包"],"bv":"BV1vY41137F1","difficulty":"困难","tags":[],"methods":["煲"],"tools":["电饭煲"]},{"id":35,"name":"电饭煲叉烧肉(叉烧酱版)","stuff":["猪肉","米"],"bv":"BV127411W75D","difficulty":"普通","tags":[],"methods":["煲","烤"],"tools":["电饭煲"]},{"id":36,"name":"电饭煲温泉蛋","stuff":["鸡蛋"],"bv":"BV1EJ411Y7fU","difficulty":"","tags":[],"methods":["煲"],"tools":["电饭煲"]},{"id":37,"name":"电饭煲窝蛋牛肉饭","stuff":["牛肉","鸡蛋","米"],"bv":"BV1aA411q7b9","difficulty":"简单","tags":[],"methods":["煲"],"tools":["电饭煲"]},{"id":38,"name":"电饭锅酱牛肉","stuff":["牛肉","米"],"bv":"BV1sv411C7qW","difficulty":"","tags":[],"methods":["卤"],"tools":["电饭煲"]},{"id":39,"name":"烤箱版M记同款薯条","stuff":["土豆"],"bv":"BV1nA411P7aX","difficulty":"","tags":["同款"],"methods":["炸"],"tools":["烤箱"]},{"id":40,"name":"空心小薯片","stuff":["土豆","鸡蛋"],"bv":"BV1N5411D79P","difficulty":"","tags":["零食"],"methods":["烘"],"tools":["烤箱"]},{"id":41,"name":"气泡小土豆","stuff":["土豆"],"bv":"BV1sL41137jN","difficulty":"简单","tags":["零食"],"methods":["烘"],"tools":["烤箱"]},{"id":42,"name":"烤箱版M记同款麦乐鸡","stuff":["土豆","鸡肉","鸡蛋"],"bv":"BV19v411J7MG","difficulty":"","tags":["同款"],"methods":["炸"],"tools":["烤箱"]},{"id":43,"name":"烤箱版KFC同款香辣鸡翅","stuff":["鸡肉"],"bv":"BV1jA4y197RR","difficulty":"","tags":["同款","辣"],"methods":["烤"],"tools":["烤箱"]},{"id":44,"name":"烤箱版M记同款薯饼","stuff":["土豆"],"bv":"BV1qy4y1y7Mn","difficulty":"","tags":["同款"],"methods":["烤"],"tools":["烤箱"]},{"id":45,"name":"烤箱烧烤","stuff":["土豆","胡萝卜","花菜","西葫芦","芹菜","洋葱","莴笋","菌菇","茄子","包菜","午餐肉","香肠","鸡肉","猪肉","虾","牛肉","鸡肉"],"bv":"BV17P4y1W7Ze","difficulty":"","tags":["杂烩"],"methods":["烤"],"tools":["烤箱"]},{"id":46,"name":"烤箱迎客菜","stuff":["牛肉","茄子","鸡肉","鸡蛋","豆腐","菌菇","番茄"],"bv":"BV1ug4y1B7CR","difficulty":"","tags":["杂烩"],"methods":["烤"],"tools":["烤箱"]},{"id":47,"name":"爆浆鸡腿","stuff":["鸡肉"],"bv":"BV1ju411X7mx","difficulty":"困难","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":48,"name":"BBQ烤鸡腿","stuff":["鸡肉"],"bv":"BV1Zr4y1B7UQ","difficulty":"简单","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":49,"name":"BBQ烟熏手撕猪肉","stuff":["猪肉"],"bv":"BV1DV411x7SH","difficulty":"困难","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":50,"name":"馋嘴烤箱版烧烤","stuff":["土豆","鸡肉"],"bv":"BV1wv411h7d6","difficulty":"普通","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":51,"name":"脆皮五花肉和蜜汁叉烧","stuff":["猪肉"],"bv":"BV17u411i7jF","difficulty":"普通","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":52,"name":"法棍","stuff":["面包"],"bv":"BV1Kc411h7eJ","difficulty":"普通","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":53,"name":"汉堡面包","stuff":["鸡蛋","面包"],"bv":"BV1ga411h7cw","difficulty":"普通","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":54,"name":"火山熔岩蛋","stuff":["土豆","鸡蛋"],"bv":"BV1Xy4y1L7ok","difficulty":"困难","tags":[],"methods":["烘"],"tools":["烤箱"]},{"id":55,"name":"烤布丁","stuff":["鸡蛋"],"bv":"BV1TT4y127vu","difficulty":"","tags":[],"methods":["烘"],"tools":["烤箱"]},{"id":56,"name":"烤箱版爆浆鸡腿","stuff":["鸡肉"],"bv":"BV1ju411X7mx","difficulty":"困难","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":57,"name":"烤箱版橙香鸡翅","stuff":["鸡肉"],"bv":"BV1Mb4y1Q7DU","difficulty":"简单","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":58,"name":"烤箱版脆皮五花肉","stuff":["猪肉"],"bv":"BV1VS4y137dE","difficulty":"普通","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":59,"name":"烤箱版黑椒牛肉土豆焖饭","stuff":["牛肉","土豆","胡萝卜","米","鸡蛋"],"bv":"BV14x411D7uN","difficulty":"","tags":[],"methods":["烤","焖"],"tools":["烤箱"]},{"id":60,"name":"烤箱版烤串","stuff":["猪肉","花菜"],"bv":"BV17P4y1W7Ze","difficulty":"简单","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":61,"name":"烤箱版烤花菜","stuff":["花菜"],"bv":"BV1Nv411Y7kV","difficulty":"简单","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":62,"name":"烤箱版烤西葫芦","stuff":["西葫芦"],"bv":"BV1Nv411Y7kV","difficulty":"简单","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":63,"name":"烤箱版烤洋葱","stuff":["洋葱"],"bv":"BV1Nv411Y7kV","difficulty":"简单","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":64,"name":"烤箱版麻薯","stuff":["鸡蛋"],"bv":"BV1CR4y1t7CM","difficulty":"普通","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":65,"name":"烤箱版气泡土豆","stuff":["土豆"],"bv":"BV1mR4y1E7VH","difficulty":"普通","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":66,"name":"烤箱版烧烤","stuff":["鸡肉","番茄","土豆"],"bv":"BV1Ra4y1x7Ua","difficulty":"简单","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":67,"name":"烤箱版薯条","stuff":["土豆"],"bv":"BV1nA411P7aX","difficulty":"普通","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":68,"name":"烤箱版吮指鸡翅","stuff":["鸡肉","土豆"],"bv":"BV12W411A7w8","difficulty":"普通","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":69,"name":"烤箱版蒜蓉烤茄子","stuff":["茄子"],"bv":"BV1Ff4y1P7ng","difficulty":"简单","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":70,"name":"烤箱鸡翅根","stuff":["鸡肉"],"bv":"BV1et411R7kG","difficulty":"普通","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":71,"name":"烤箱鸡蛋","stuff":["鸡蛋"],"bv":"BV1Vp4y167P4","difficulty":"简单","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":72,"name":"烤箱腊肠焖饭","stuff":["腊肠","花菜","米"],"bv":"BV14x411D7uN","difficulty":"","tags":[],"methods":["烤","焖"],"tools":["烤箱"]},{"id":73,"name":"烤箱蜜汁鸡腿","stuff":["鸡肉"],"bv":"BV1WW411S72C","difficulty":"普通","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":74,"name":"烤箱面包","stuff":["面包"],"bv":"BV1wJ411E7YS","difficulty":"简单","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":75,"name":"烤箱牛肉","stuff":["牛肉"],"bv":"BV1L64y1r7bk","difficulty":"困难","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":76,"name":"烤箱茄子","stuff":["茄子"],"bv":"BV165411N7fQ","difficulty":"简单","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":77,"name":"烤箱炸鸡米花","stuff":["鸡肉"],"bv":"BV1HE411L7T2","difficulty":"普通","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":78,"name":"炼猪油","stuff":["猪肉"],"bv":"BV12F411v74c","difficulty":"简单","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":79,"name":"流心培根吐司","stuff":["面包","鸡蛋","猪肉"],"bv":"BV1aZ4y1R7SL","difficulty":"普通","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":80,"name":"冒油牛排","stuff":["牛肉","土豆"],"bv":"BV1754y1E7Mj","difficulty":"普通","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":81,"name":"年轮蛋糕(难度max)","stuff":["面包","鸡蛋"],"bv":"BV1WQ4y1q7iz","difficulty":"困难","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":82,"name":"牛奶烤土司","stuff":["鸡蛋","面包"],"bv":"BV1KF411Y7WP","difficulty":"普通","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":83,"name":"千层土豆烘蛋","stuff":["土豆","鸡蛋"],"bv":"BV1fY41187Xo","difficulty":"困难","tags":[],"methods":["烘"],"tools":["烤箱"]},{"id":84,"name":"蔬菜团子","stuff":["包菜"],"bv":"BV1S44y1P7Qp","difficulty":"简单","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":85,"name":"蒜香芝士面包虾","stuff":["面包","虾"],"bv":"BV1gB4y1c7GY","difficulty":"困难","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":86,"name":"土豆面包","stuff":["土豆","面包","鸡蛋"],"bv":"BV1H4411572T","difficulty":"普通","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":87,"name":"土豆挞","stuff":["土豆"],"bv":"BV1fY41187Xo","difficulty":"普通","tags":[],"methods":["烘"],"tools":["烤箱"]},{"id":88,"name":"土豆芝士棒","stuff":["土豆","香肠"],"bv":"BV1DL4y1j7Nu","difficulty":"困难","tags":[],"methods":["烘"],"tools":["烤箱"]},{"id":89,"name":"早餐白面包(无鸡蛋版)","stuff":["面包"],"bv":"BV1Xg4y187BQ","difficulty":"普通","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":90,"name":"猪肉脯","stuff":["猪肉"],"bv":"BV174411Z7ar","difficulty":"普通","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":91,"name":"欧洲家庭版欧包","stuff":["鸡蛋","面包"],"bv":"BV1SJ411L7dG","difficulty":"普通","tags":[],"methods":["烤"],"tools":["烤箱","电饭煲"]},{"id":92,"name":"土豆杂蔬烘蛋","stuff":["土豆","番茄","胡萝卜","香肠"],"bv":"BV1Y5411S7mu","difficulty":"简单","tags":["早餐"],"methods":["烘"],"tools":["烤箱","空气炸锅"]},{"id":93,"name":"午餐肉串","stuff":["午餐肉"],"bv":"BV1MF411t7Ba","difficulty":"简单","tags":["零食"],"methods":["炸"],"tools":["空气炸锅"]},{"id":94,"name":"午餐肉薯条","stuff":["午餐肉","鸡蛋","面包"],"bv":"BV1ti4y1f7nE","difficulty":"简单","tags":["零食"],"methods":["炸"],"tools":["空气炸锅"]},{"id":95,"name":"空气炸锅蒜蓉茄子","stuff":["茄子"],"bv":"BV1aQ4y1D77r","difficulty":"简单","tags":["深夜美食"],"methods":["炸"],"tools":["空气炸锅"]},{"id":96,"name":"空气炸锅版M记同款薯条","stuff":["土豆"],"bv":"BV1tq4y1j72x","difficulty":"","tags":["同款"],"methods":["炸"],"tools":["空气炸锅"]},{"id":97,"name":"空气炸锅版M记同款薯饼","stuff":["土豆"],"bv":"BV1Cb4y167zK","difficulty":"","tags":["同款"],"methods":["炸"],"tools":["空气炸锅"]},{"id":98,"name":"空气炸锅版KFC同款奥尔良烤翅","stuff":["鸡肉"],"bv":"BV1EQ4y1a7C9","difficulty":"","tags":["同款"],"methods":["炸","烤"],"tools":["空气炸锅"]},{"id":99,"name":"空气炸锅炸包菜","stuff":["包菜"],"bv":"BV1eR4y1j715","difficulty":"简单","tags":["童年风味"],"methods":["炸"],"tools":["空气炸锅"]},{"id":100,"name":"空气炸锅版可乐鸡翅","stuff":["鸡肉"],"bv":"BV1eT4y1S7Jn","difficulty":"","tags":[],"methods":["烧"],"tools":["空气炸锅"]},{"id":101,"name":"空气炸锅烤时蔬","stuff":["菌菇","花菜","胡萝卜","西葫芦","包菜","土豆","芹菜","莴笋"],"bv":"BV1a5411u7TH","difficulty":"简单","tags":["杂烩"],"methods":["炸"],"tools":["空气炸锅"]},{"id":102,"name":"空气炸锅炸万物","stuff":["土豆","胡萝卜","花菜","西葫芦","芹菜","洋葱","莴笋","菌菇","茄子","包菜","午餐肉","香肠","鸡肉","猪肉","虾","牛肉","鸡肉","番茄","豆腐","面包"],"bv":"BV1wL411F7Cd","difficulty":"简单","tags":["杂烩"],"methods":["炸"],"tools":["空气炸锅"]},{"id":103,"name":"空气炸锅炸包菜串","stuff":["包菜"],"bv":"BV1DL4y137CD","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":104,"name":"空气炸锅炸馒头","stuff":["面食"],"bv":"BV1DL4y137CD","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":105,"name":"空气炸锅炸香肠串","stuff":["香肠"],"bv":"BV1DL4y137CD","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":106,"name":"虾肉茄子卷","stuff":["虾","茄子","胡萝卜","鸡蛋"],"bv":"BV1aB4y1U7zQ","difficulty":"普通","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":107,"name":"家庭版麦乐鸡块","stuff":["鸡肉","鸡蛋","土豆","洋葱"],"bv":"BV1uu411v7Qf","difficulty":"普通","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":108,"name":"焦糖吐司布丁","stuff":["鸡蛋","面包","豆腐"],"bv":"BV1sq4y197TK","difficulty":"","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":109,"name":"空气炸锅奥尔良炸鸡腿","stuff":["鸡肉"],"bv":"BV1JU4y1G7hq","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":110,"name":"空气炸锅爆浆巴斯克芝士蛋糕","stuff":["鸡蛋"],"bv":"BV1Ja411v7DK","difficulty":"","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":111,"name":"空气炸锅炒饭","stuff":["米","胡萝卜","香肠","鸡蛋"],"bv":"BV1oL4y1T7BX","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":112,"name":"空气炸锅炒面","stuff":["方便面"],"bv":"BV11P4y1u7R1","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":113,"name":"空气炸锅炒牛河","stuff":["面食","牛肉"],"bv":"BV1k44y1P7VY","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":114,"name":"空气炸锅脆皮烤肉","stuff":["猪肉"],"bv":"BV1qU4y1f7G1","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":115,"name":"空气炸锅蛋黄酥","stuff":["虾","土豆"],"bv":"BV1yS4y1u7M6","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":116,"name":"空气炸锅地三鲜","stuff":["土豆","茄子"],"bv":"BV1pf4y1T7bx","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":117,"name":"空气炸锅法式吐司","stuff":["面包","鸡蛋"],"bv":"BV1Fh41187GB","difficulty":"","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":118,"name":"空气炸锅蜂蜜小面包","stuff":["鸡蛋"],"bv":"BV1Ja411v7DK","difficulty":"","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":119,"name":"空气炸锅干锅虾烤肉串","stuff":["虾","猪肉"],"bv":"BV1Ka41117mc","difficulty":"","tags":["辣"],"methods":["炸","烤"],"tools":["空气炸锅"]},{"id":120,"name":"空气炸锅虾球","stuff":["土豆","虾"],"bv":"BV1L3411573m","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":121,"name":"空气炸锅虎皮凤爪","stuff":["鸡肉"],"bv":"BV1VR4y1s7XP","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":122,"name":"空气炸锅鸡蛋豆腐羹","stuff":["鸡蛋","豆腐","猪肉"],"bv":"BV1Hq4y137Vu","difficulty":"","tags":["汤"],"methods":["炸","煮"],"tools":["空气炸锅"]},{"id":123,"name":"空气炸锅减脂餐","stuff":["菌菇","虾"],"bv":"BV1CY4y1p7qP","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":124,"name":"空气炸锅减脂餐","stuff":["包菜","菌菇","鸡肉"],"bv":"BV1w44y1V7S5","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":125,"name":"空气炸锅烤豆腐","stuff":["豆腐"],"bv":"BV19L411x7bB","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":126,"name":"空气炸锅烤鸡腿","stuff":["鸡肉"],"bv":"BV1Zr4y1B7UQ","difficulty":"普通","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":127,"name":"空气炸锅烤鸡胸肉","stuff":["鸡肉"],"bv":"BV1dF411q7of","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":128,"name":"空气炸锅烤面包","stuff":["面包"],"bv":"BV1T34y1b7FS","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":129,"name":"空气炸锅烤蘑菇","stuff":["菌菇"],"bv":"BV1Jq4y147oM","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":130,"name":"空气炸锅烤牛奶","stuff":["鸡蛋"],"bv":"BV1ub4y1C7bp","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":131,"name":"空气炸锅烤排骨","stuff":["猪肉"],"bv":"BV1H3411J7TH","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":132,"name":"空气炸锅烤茄子","stuff":["茄子"],"bv":"BV1gP4y1s7GJ","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":133,"name":"空气炸锅烤肉串","stuff":["猪肉"],"bv":"BV1WY411G7aX","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":134,"name":"空气炸锅烤土豆","stuff":["土豆"],"bv":"BV1E5411D7RE","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":135,"name":"空气炸锅烤五花肉","stuff":["猪肉"],"bv":"BV1Jq4y147oM","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":136,"name":"空气炸锅烤猪蹄","stuff":["猪肉"],"bv":"BV11q4y1Y7hW","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":137,"name":"空气炸锅辣子鸡","stuff":["鸡肉"],"bv":"BV1yL4y1L71E","difficulty":"简单","tags":["辣"],"methods":["炸"],"tools":["空气炸锅"]},{"id":138,"name":"空气炸锅嫩鸡排","stuff":["鸡肉"],"bv":"BV1L3411W7Nr","difficulty":"","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":139,"name":"空气炸锅气泡面包","stuff":["面包"],"bv":"BV1Lu411z7FK","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":140,"name":"空气炸锅全麦面包","stuff":["面包"],"bv":"BV1VY411E74H","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":141,"name":"空气炸锅肉末豆腐","stuff":["豆腐"],"bv":"BV1Ja411C75G","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":142,"name":"空气炸锅酸奶蛋糕","stuff":["鸡蛋"],"bv":"BV1HL4y177B3","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":143,"name":"空气炸锅蒜香排骨","stuff":["猪肉"],"bv":"BV1v54y1B7cA","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":144,"name":"空气炸锅糖醋里脊","stuff":["猪肉"],"bv":"BV1rS4y137gC","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":145,"name":"空气炸锅甜点(没酵母别选)","stuff":["鸡蛋"],"bv":"BV1Ja411v7DK","difficulty":"","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":146,"name":"空气炸锅新年硬菜","stuff":["虾","猪肉"],"bv":"BV1Mq4y1h78M","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":147,"name":"空气炸锅早餐饼","stuff":["鸡蛋","西葫芦","胡萝卜"],"bv":"BV1mu411q715","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":148,"name":"空气炸锅炸包菜","stuff":["包菜"],"bv":"BV1NB4y1N7SH","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":149,"name":"空气炸锅炸低卡食材","stuff":["包菜","菌菇"],"bv":"BV1cF411s7QH","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":150,"name":"空气炸锅炸豆腐","stuff":["豆腐"],"bv":"BV1eR4y1j715","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":151,"name":"空气炸锅炸个荤素搭配","stuff":["土豆","香肠","鸡肉","虾","鸡蛋","花菜"],"bv":"BV18q4y117Cm","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":152,"name":"空气炸锅炸鸡","stuff":["鸡肉"],"bv":"BV1S44y1j7YM","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":153,"name":"空气炸锅炸鸡翅","stuff":["鸡肉"],"bv":"BV14b4y1p7XS","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":154,"name":"空气炸锅炸家常蔬菜","stuff":["花菜","豆腐","鸡肉","菌菇"],"bv":"BV1FU4y1Z7T1","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":155,"name":"空气炸锅炸金针菇","stuff":["菌菇"],"bv":"BV1x4411V7at","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":156,"name":"空气炸锅炸茄盒","stuff":["茄子","猪肉"],"bv":"BV1xP4y1J7eJ","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":157,"name":"空气炸锅炸蔬菜","stuff":["鸡蛋","黄瓜"],"bv":"BV1qr4y1p7BX","difficulty":"普通","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":158,"name":"空气炸锅炸薯角","stuff":["土豆"],"bv":"BV1eR4y1j715","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":159,"name":"空气炸锅炸薯条","stuff":["土豆"],"bv":"BV1tq4y1j72x","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":160,"name":"空气炸锅炸素菜","stuff":["豆腐","洋葱","茄子","菌菇","鸡蛋","米"],"bv":"BV1Ap4y1b7a6","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":161,"name":"空气炸锅炸酸奶","stuff":["鸡蛋"],"bv":"BV1xL411A7xG","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":162,"name":"空气炸锅炸土豆豆腐","stuff":["豆腐","虾","土豆","鸡蛋"],"bv":"BV15Z4y1D73J","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":163,"name":"空气炸锅炸吐司","stuff":["面包","鸡蛋"],"bv":"BV1aQ4y1D77r","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":164,"name":"空气炸锅炸五花肉花菜","stuff":["猪肉","花菜"],"bv":"BV1Jq4y147oM","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":165,"name":"空气炸锅炸鲜奶","stuff":["鸡蛋"],"bv":"BV1AZ4y1R7T2","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":166,"name":"空气炸锅炸香肠鸡翅","stuff":["鸡蛋","香肠","鸡肉"],"bv":"BV15r4y1W7vA","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":167,"name":"空气炸锅炸小吃一条街","stuff":["包菜","香肠","鸡蛋"],"bv":"BV1DL4y137CD","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":168,"name":"空气炸锅做蛋挞","stuff":["鸡蛋"],"bv":"BV1A44y1P7JY","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":169,"name":"蜜汁脆皮烤鸡","stuff":["鸡肉"],"bv":"av895289430","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":170,"name":"炸锅版吮指原味鸡","stuff":["鸡肉"],"bv":"BV155411S7Q1","difficulty":"简单","tags":[],"methods":["炸"],"tools":["空气炸锅"]},{"id":171,"name":"微波炉版番茄焖饭","stuff":["香肠","番茄"],"bv":"BV193411W7nb","difficulty":"普通","tags":["减脂餐"],"methods":["微波加热"],"tools":["微波炉"]},{"id":172,"name":"微波炉版菌菇鸡肉焖饭","stuff":["鸡肉","胡萝卜","菌菇","米"],"bv":"BV193411W7nb","difficulty":"普通","tags":["减脂餐"],"methods":["微波加热"],"tools":["微波炉"]},{"id":173,"name":"微波炉版盐烤土豆","stuff":["土豆"],"bv":"BV1Cf4y1R7pe","difficulty":"","tags":["零食"],"methods":["微波加热"],"tools":["微波炉"]},{"id":174,"name":"微波炉版荷包蛋方便面","stuff":["鸡蛋","方便面","香肠"],"bv":"BV18v411i7JM","difficulty":"简单","tags":["速食"],"methods":["微波加热"],"tools":["微波炉"]},{"id":175,"name":"微波炉版KFC同款土豆泥","stuff":["土豆"],"bv":"BV1Ji4y1773B","difficulty":"简单","tags":["同款"],"methods":["微波加热"],"tools":["微波炉"]},{"id":176,"name":"微波炉版乐事同款薯片","stuff":["土豆"],"bv":"BV1Ms411D7Qg","difficulty":"","tags":["同款"],"methods":["微波加热"],"tools":["微波炉"]},{"id":177,"name":"微波炉版FKC同款嫩牛五方","stuff":["牛肉","番茄","面食"],"bv":"BV1Ji4y1773B","difficulty":"","tags":["同款"],"methods":["微波加热"],"tools":["微波炉"]},{"id":178,"name":"微波炉版可乐鸡翅","stuff":["鸡肉","洋葱"],"bv":"BV1jJ41127ML","difficulty":"","tags":[],"methods":["烧"],"tools":["微波炉"]},{"id":179,"name":"微波炉版蔬菜脆片","stuff":["土豆","西葫芦","黄瓜"],"bv":"BV1n441187r3","difficulty":"","tags":["杂烩"],"methods":["微波加热"],"tools":["微波炉"]},{"id":180,"name":"微波炉火锅","stuff":["菌菇","牛肉","方便面","虾","鸡肉","猪肉","午餐肉","莴笋","豆腐","土豆"],"bv":"BV1o34y1R7BC","difficulty":"","tags":["杂烩"],"methods":["微波加热"],"tools":["微波炉"]},{"id":181,"name":"微波炉版鸡蛋羹","stuff":["鸡蛋"],"bv":"BV1HL411E7MM","difficulty":"简单","tags":["早饭"],"methods":["微波加热"],"tools":["微波炉"]},{"id":182,"name":"微波炉版土豆浓汤","stuff":["土豆","胡萝卜"],"bv":"BV1HL411E7MM","difficulty":"简单","tags":["早饭","汤"],"methods":["微波加热"],"tools":["微波炉"]},{"id":183,"name":"微波炉版煲仔饭","stuff":["米","香肠","鸡蛋"],"bv":"BV145411f7R4","difficulty":"简单","tags":["主食"],"methods":["微波加热"],"tools":["微波炉"]},{"id":184,"name":"微波炉版燕麦粥","stuff":["米"],"bv":"BV1cF411Y7pd","difficulty":"","tags":["主食"],"methods":["微波加热"],"tools":["微波炉"]},{"id":185,"name":"微波炉版叉烧肉","stuff":["猪肉","米"],"bv":"BV1Hi4y1s7Sc","difficulty":"","tags":[],"methods":["微波加热"],"tools":["微波炉"]},{"id":186,"name":"微波炉版蛋炒饭","stuff":["米","鸡蛋","香肠"],"bv":"BV1T34y147qU","difficulty":"简单","tags":[],"methods":["微波加热"],"tools":["微波炉"]},{"id":187,"name":"微波炉版番茄鸡蛋汤","stuff":["番茄","鸡蛋"],"bv":"BV1qx411n7QF","difficulty":"","tags":["汤"],"methods":["微波加热"],"tools":["微波炉"]},{"id":188,"name":"微波炉版番茄肉盒","stuff":["番茄","猪肉"],"bv":"BV194411R7aH","difficulty":"","tags":[],"methods":["微波加热"],"tools":["微波炉"]},{"id":189,"name":"微波炉版肥牛饭","stuff":["牛肉","洋葱","鸡蛋","米"],"bv":"BV1gh411Y7TU","difficulty":"","tags":[],"methods":["微波加热"],"tools":["微波炉"]},{"id":190,"name":"微波炉版干脆面","stuff":["面食"],"bv":"BV1pL4y1j7oe","difficulty":"","tags":[],"methods":["微波加热"],"tools":["微波炉"]},{"id":191,"name":"微波炉版虾吃豆腐","stuff":["虾","豆腐"],"bv":"BV1VW41157uy","difficulty":"","tags":[],"methods":["微波加热"],"tools":["微波炉"]},{"id":192,"name":"微波炉版红烧茄子","stuff":["茄子","洋葱","米"],"bv":"BV17Q4y1y7NC","difficulty":"普通","tags":[],"methods":["微波加热"],"tools":["微波炉"]},{"id":193,"name":"微波炉版厚蛋烧","stuff":["鸡蛋","包菜"],"bv":"BV1bT4y1o7dv","difficulty":"普通","tags":[],"methods":["微波加热"],"tools":["微波炉"]},{"id":194,"name":"微波炉版厚蛋烧(没芝士就别选了)","stuff":["鸡蛋","包菜","香肠"],"bv":"BV1bT4y1o7dv","difficulty":"","tags":[],"methods":["微波加热"],"tools":["微波炉"]},{"id":195,"name":"微波炉版胡萝卜蒸菜","stuff":["胡萝卜","面食"],"bv":"BV1A94y1o7aW","difficulty":"简单","tags":[],"methods":["微波加热"],"tools":["微波炉"]},{"id":196,"name":"微波炉版鸡胸肉脆片","stuff":["鸡肉"],"bv":"BV1xu41197tU","difficulty":"","tags":[],"methods":["微波加热"],"tools":["微波炉"]},{"id":197,"name":"微波炉版鸡胸肉丸","stuff":["鸡肉"],"bv":"BV1xu41197tU","difficulty":"","tags":[],"methods":["微波加热"],"tools":["微波炉"]},{"id":198,"name":"微波炉版烤鸡翅","stuff":["鸡肉"],"bv":"BV1bT4y1o7dv","difficulty":"","tags":[],"methods":["微波加热"],"tools":["微波炉"]},{"id":199,"name":"微波炉版辣烤豆腐","stuff":["豆腐","虾"],"bv":"BV17Q4y1y7NC","difficulty":"普通","tags":["辣"],"methods":["微波加热"],"tools":["微波炉"]},{"id":200,"name":"微波炉版麻辣排骨","stuff":["猪肉"],"bv":"BV1c44y1T79A","difficulty":"简单","tags":["辣"],"methods":["微波加热"],"tools":["微波炉"]},{"id":201,"name":"微波炉版蘑菇","stuff":["菌菇"],"bv":"BV1Nq4y1i72u","difficulty":"简单","tags":[],"methods":["微波加热"],"tools":["微波炉"]},{"id":202,"name":"微波炉版嫩豆腐","stuff":["豆腐"],"bv":"BV1DZ4y1o7ZM","difficulty":"简单","tags":[],"methods":["微波加热"],"tools":["微波炉"]},{"id":203,"name":"微波炉版牛奶炖饭","stuff":["米"],"bv":"BV1W7411c7As","difficulty":"简单","tags":[],"methods":["微波加热"],"tools":["微波炉"]},{"id":204,"name":"微波炉版日式嫩蒸鸡胸肉","stuff":["鸡肉"],"bv":"BV1xu41197tU","difficulty":"","tags":[],"methods":["微波加热"],"tools":["微波炉"]},{"id":205,"name":"微波炉版烧烤茄子","stuff":["茄子"],"bv":"BV19x411H7ig","difficulty":"","tags":[],"methods":["微波加热"],"tools":["微波炉"]},{"id":206,"name":"微波炉版蒜香烤茄子","stuff":["茄子"],"bv":"BV19x411H7ig","difficulty":"简单","tags":[],"methods":["微波加热"],"tools":["微波炉"]},{"id":207,"name":"微波炉版蒜香琵琶腿","stuff":["鸡肉"],"bv":"BV1qx411777p","difficulty":"","tags":[],"methods":["微波加热"],"tools":["微波炉"]},{"id":208,"name":"微波炉版吐司杯","stuff":["鸡蛋","面包"],"bv":"BV1CS4y1k7Xi","difficulty":"","tags":[],"methods":["微波加热"],"tools":["微波炉"]},{"id":209,"name":"微波炉版五花肉","stuff":["猪肉","米"],"bv":"BV1hL4y137Zk","difficulty":"","tags":[],"methods":["微波加热"],"tools":["微波炉"]},{"id":210,"name":"微波炉版香干","stuff":["豆腐"],"bv":"BV1F44y1T7sQ","difficulty":"","tags":[],"methods":["微波加热"],"tools":["微波炉"]},{"id":211,"name":"微波炉版香辣豆腐","stuff":["豆腐","米"],"bv":"BV1bT4y1o7dv","difficulty":"","tags":["辣"],"methods":["微波加热"],"tools":["微波炉"]},{"id":212,"name":"微波炉版香嫩鸡胸肉","stuff":["鸡肉"],"bv":"BV1xu41197tU","difficulty":"","tags":[],"methods":["微波加热"],"tools":["微波炉"]},{"id":213,"name":"微波炉版一个番茄焖饭","stuff":["番茄","米","胡萝卜","香肠","腊肠","土豆"],"bv":"BV193411W7nb","difficulty":"","tags":[],"methods":["微波加热"],"tools":["微波炉"]},{"id":214,"name":"微波炉版鱼香肉块","stuff":["胡萝卜","鸡肉"],"bv":"BV1qt411X7Do","difficulty":"","tags":["辣"],"methods":["微波加热"],"tools":["微波炉"]},{"id":215,"name":"微波炉版照烧鸡腿饭","stuff":["米","鸡肉","胡萝卜","花菜"],"bv":"BV16Z4y1V774","difficulty":"","tags":[],"methods":["微波加热"],"tools":["微波炉"]},{"id":216,"name":"微波炉版蒸蛋羹","stuff":["鸡蛋"],"bv":"BV19T4y1D7Zd","difficulty":"","tags":[],"methods":["微波加热"],"tools":["微波炉"]},{"id":217,"name":"微波炉版煮米饭","stuff":["米"],"bv":"BV193411W7nb","difficulty":"","tags":[],"methods":["微波加热"],"tools":["微波炉"]},{"id":218,"name":"水煮肉片","stuff":["猪肉","芹菜","莴笋"],"bv":"BV1ZZ4y1379N","difficulty":"普通","tags":["川菜","辣"],"methods":["炒"],"tools":["一口大锅"]},{"id":219,"name":"脆口黄瓜","stuff":["黄瓜"],"bv":"BV1Tb4y1X7ow","difficulty":"简单","tags":["脆口"],"methods":["凉拌"],"tools":["一口大锅"]},{"id":220,"name":"白萝卜汤","stuff":["白萝卜"],"bv":"BV1HJ411L7xA","difficulty":"简单","tags":["单一食材","汤"],"methods":["煮"],"tools":["一口大锅"]},{"id":221,"name":"白灼大虾","stuff":["虾"],"bv":"BV1Sr4y1m7zF","difficulty":"普通","tags":["单一食材"],"methods":["灼"],"tools":["一口大锅"]},{"id":222,"name":"炒包菜","stuff":["包菜"],"bv":"BV1K5411L7Xn","difficulty":"简单","tags":["单一食材"],"methods":["炒"],"tools":["一口大锅"]},{"id":223,"name":"番茄酱","stuff":["番茄","面食","米"],"bv":"BV1h4411c7TV","difficulty":"简单","tags":["单一食材"],"methods":["煮"],"tools":["一口大锅"]},{"id":224,"name":"黑椒土豆泥","stuff":["土豆"],"bv":"BV1q7411K7gr","difficulty":"简单","tags":["单一食材"],"methods":["蒸"],"tools":["一口大锅"]},{"id":225,"name":"红烧茄子","stuff":["茄子"],"bv":"BV1ot4y1S7Hf","difficulty":"普通","tags":["单一食材"],"methods":["烧"],"tools":["一口大锅"]},{"id":226,"name":"酱筒骨","stuff":["骨头"],"bv":"BV1Ai4y187KE","difficulty":"普通","tags":["单一食材"],"methods":["炖"],"tools":["一口大锅"]},{"id":227,"name":"酱土豆","stuff":["土豆"],"bv":"BV1YD4y1c7ip","difficulty":"简单","tags":["单一食材"],"methods":["炒"],"tools":["一口大锅"]},{"id":228,"name":"酱油炖白萝卜","stuff":["白萝卜"],"bv":"BV1us411h7E8","difficulty":"简单","tags":["单一食材"],"methods":["炖"],"tools":["一口大锅"]},{"id":229,"name":"椒盐炸蘑菇","stuff":["菌菇","鸡蛋"],"bv":"BV1P54y1L7FS","difficulty":"简单","tags":["单一食材"],"methods":["炸"],"tools":["一口大锅"]},{"id":230,"name":"浇汁西兰花","stuff":["花菜"],"bv":"BV1jq4y1M7jR","difficulty":"简单","tags":["单一食材"],"methods":["煮"],"tools":["一口大锅"]},{"id":231,"name":"金汤虾片","stuff":["虾"],"bv":"BV1mQ4y1e7T8","difficulty":"困难","tags":["单一食材","汤"],"methods":["煮"],"tools":["一口大锅"]},{"id":232,"name":"老奶洋芋","stuff":["土豆"],"bv":"BV1fY41187Xo","difficulty":"简单","tags":["单一食材"],"methods":["炒"],"tools":["一口大锅"]},{"id":233,"name":"麻辣土豆片","stuff":["土豆"],"bv":"BV1fY41187Xo","difficulty":"简单","tags":["单一食材","辣"],"methods":["煮"],"tools":["一口大锅"]},{"id":234,"name":"茄子焖饭","stuff":["茄子"],"bv":"BV1k7411m7nv","difficulty":"简单","tags":["单一食材"],"methods":["烧"],"tools":["一口大锅"]},{"id":235,"name":"清炖肉骨头","stuff":["骨头"],"bv":"BV1SQ4y1m73v","difficulty":"普通","tags":["单一食材"],"methods":["炖"],"tools":["一口大锅"]},{"id":236,"name":"薯球","stuff":["土豆"],"bv":"BV1bY4y1H7L9","difficulty":"普通","tags":["单一食材"],"methods":["炸"],"tools":["一口大锅"]},{"id":237,"name":"蒜蓉西兰花","stuff":["花菜"],"bv":"BV1fy4y1W7z5","difficulty":"简单","tags":["单一食材"],"methods":["炒"],"tools":["一口大锅"]},{"id":238,"name":"土豆派","stuff":["土豆"],"bv":"BV1bY4y1H7L9","difficulty":"简单","tags":["单一食材"],"methods":["烘"],"tools":["一口大锅"]},{"id":239,"name":"炸厚土豆片","stuff":["土豆"],"bv":"BV1mt4y1i7Zr","difficulty":"简单","tags":["单一食材"],"methods":["煎"],"tools":["一口大锅"]},{"id":240,"name":"蒸腊肠","stuff":["腊肠","米"],"bv":"BV1YK4y1e7bM","difficulty":"简单","tags":["单一食材"],"methods":["蒸"],"tools":["一口大锅"]},{"id":241,"name":"蒜香牛奶焗胡萝卜","stuff":["胡萝卜","洋葱","鸡蛋"],"bv":"BV1yF411u7Me","difficulty":"普通","tags":["法餐"],"methods":["焗"],"tools":["一口大锅"]},{"id":242,"name":"胡萝卜炖牛肉","stuff":["胡萝卜","牛肉","洋葱"],"bv":"BV1UR4y1V7nV","difficulty":"困难","tags":["法式"],"methods":["炖"],"tools":["一口大锅"]},{"id":243,"name":"葱油黄瓜","stuff":["黄瓜"],"bv":"BV1954y1b7dv","difficulty":"简单","tags":["国宴菜"],"methods":["拌"],"tools":["一口大锅"]},{"id":244,"name":"黄瓜翡翠羹","stuff":["黄瓜","鸡肉"],"bv":"BV1y3411W7Mz","difficulty":"困难","tags":["国宴菜","汤"],"methods":["煮"],"tools":["一口大锅"]},{"id":245,"name":"烧汁牛肉粒","stuff":["牛肉","洋葱","菌菇"],"bv":"BV1NU4y1d7ot","difficulty":"普通","tags":["国宴菜"],"methods":["炒"],"tools":["一口大锅"]},{"id":246,"name":"火烧云油焖鸡","stuff":["土豆","番茄","鸡肉"],"bv":"BV1fY41187Xo","difficulty":"普通","tags":["好吃"],"methods":["煮"],"tools":["一口大锅"]},{"id":247,"name":"蒜香口蘑牛肉粒","stuff":["菌菇","牛肉"],"bv":"BV1Nm4y197ev","difficulty":"简单","tags":["荤素搭配"],"methods":["炒"],"tools":["一口大锅"]},{"id":248,"name":"6阶番茄炒蛋","stuff":["番茄","鸡蛋"],"bv":"BV1Py4y1S7EF","difficulty":"困难","tags":["家常菜"],"methods":["炒"],"tools":["一口大锅"]},{"id":249,"name":"白萝卜烧鸡块","stuff":["白萝卜","鸡肉"],"bv":"BV1U3411e7Jv","difficulty":"普通","tags":["家常菜"],"methods":["烧"],"tools":["一口大锅"]},{"id":250,"name":"番茄炒蛋","stuff":["番茄","鸡蛋"],"bv":"BV1rf4y1872R","difficulty":"普通","tags":["家常菜"],"methods":["炒"],"tools":["一口大锅"]},{"id":251,"name":"番茄鸡蛋","stuff":["番茄","鸡蛋","米"],"bv":"BV11y4y167Wa","difficulty":"简单","tags":["家常菜"],"methods":["炒"],"tools":["一口大锅"]},{"id":252,"name":"番茄土豆炖牛腩","stuff":["番茄","土豆","牛肉"],"bv":"BV1344y1n73V","difficulty":"普通","tags":["家常菜"],"methods":["炖"],"tools":["一口大锅"]},{"id":253,"name":"回锅肉炒包菜","stuff":["包菜","猪肉"],"bv":"BV1EK411s75a","difficulty":"普通","tags":["家常菜","川菜"],"methods":["炒"],"tools":["一口大锅"]},{"id":254,"name":"腊肠蒸蛋","stuff":["腊肠","鸡蛋"],"bv":"BV1Zp4y1Q7wu","difficulty":"简单","tags":["家常菜"],"methods":["蒸"],"tools":["一口大锅"]},{"id":255,"name":"拍辣椒炒午餐肉","stuff":["午餐肉"],"bv":"BV1K4411Z7do","difficulty":"简单","tags":["家常菜","辣"],"methods":["炒"],"tools":["一口大锅"]},{"id":256,"name":"青椒炒腊肠","stuff":["腊肠","米"],"bv":"BV1ZK4y1L7pp","difficulty":"普通","tags":["家常菜"],"methods":["炒"],"tools":["一口大锅"]},{"id":257,"name":"热炒莴笋叶","stuff":["莴笋"],"bv":"BV1xJ411M7bc","difficulty":"简单","tags":["家常菜"],"methods":["炒"],"tools":["一口大锅"]},{"id":258,"name":"素蟹粉","stuff":["胡萝卜","土豆"],"bv":"BV1Zu411e7fp","difficulty":"简单","tags":["家常菜"],"methods":["炒"],"tools":["一口大锅"]},{"id":259,"name":"酸辣土豆丝","stuff":["土豆"],"bv":"BV1wq4y1W7QA","difficulty":"普通","tags":["家常菜","辣"],"methods":["炒"],"tools":["一口大锅"]},{"id":260,"name":"蒜蓉开背虾","stuff":["虾"],"bv":"BV1DR4y1p7j4","difficulty":"普通","tags":["家常菜"],"methods":["炒"],"tools":["一口大锅"]},{"id":261,"name":"土豆烧包菜","stuff":["包菜","土豆"],"bv":"BV1Ug4y1B7Uj","difficulty":"普通","tags":["家常菜"],"methods":["烧"],"tools":["一口大锅"]},{"id":262,"name":"土豆烧红肉","stuff":["土豆","猪肉"],"bv":"BV1fY41187Xo","difficulty":"普通","tags":["家常菜"],"methods":["炖"],"tools":["一口大锅"]},{"id":263,"name":"莴笋炒蛋","stuff":["莴笋","鸡蛋","胡萝卜"],"bv":"BV1C7411J7cb","difficulty":"普通","tags":["家常菜"],"methods":["炒"],"tools":["一口大锅"]},{"id":264,"name":"油焖大虾","stuff":["虾","米"],"bv":"BV1CW411c7kP","difficulty":"普通","tags":["家常菜"],"methods":["焖"],"tools":["一口大锅"]},{"id":265,"name":"陈氏醋卤面","stuff":["面食","猪肉"],"bv":"BV1tQ4y1M76f","difficulty":"普通","tags":["家传秘方"],"methods":["拌"],"tools":["一口大锅"]},{"id":266,"name":"白灼西兰花","stuff":["花菜"],"bv":"BV1o4411A7dC","difficulty":"简单","tags":["健康"],"methods":["煮"],"tools":["一口大锅"]},{"id":267,"name":"芹菜牛肉卷","stuff":["芹菜","牛肉"],"bv":"BV1Tv411L7V9","difficulty":"","tags":["健康餐"],"methods":["煎"],"tools":["一口大锅"]},{"id":268,"name":"减脂胡萝卜丝","stuff":["胡萝卜"],"bv":"BV1UQ4y1i7JY","difficulty":"普通","tags":["减脂"],"methods":["蒸"],"tools":["一口大锅"]},{"id":269,"name":"醋溜西葫芦","stuff":["西葫芦"],"bv":"BV1Rv411G7fD","difficulty":"普通","tags":["开胃"],"methods":["炒"],"tools":["一口大锅"]},{"id":270,"name":"餐蛋面","stuff":["午餐肉","鸡蛋","方便面"],"bv":"BV1pT4y1F7p5","difficulty":"简单","tags":["快手菜"],"methods":["煮"],"tools":["一口大锅"]},{"id":271,"name":"鸡蛋煎午餐肉","stuff":["午餐肉","鸡蛋"],"bv":"BV1aK4y177Xm","difficulty":"简单","tags":["快手菜"],"methods":["煎"],"tools":["一口大锅"]},{"id":272,"name":"金针菇菜卷","stuff":["菌菇","胡萝卜","包菜"],"bv":"BV1aW411H7fZ","difficulty":"简单","tags":["快手菜"],"methods":[],"tools":["一口大锅"]},{"id":273,"name":"口蘑鸡胸肉","stuff":["菌菇","鸡肉"],"bv":"BV1AP4y1w7y6","difficulty":"简单","tags":["快手菜"],"methods":["煎"],"tools":["一口大锅"]},{"id":274,"name":"茄汁西葫芦","stuff":["西葫芦","番茄"],"bv":"BV17W411g71Q","difficulty":"普通","tags":["快手菜"],"methods":["炒"],"tools":["一口大锅"]},{"id":275,"name":"番茄浓汤面","stuff":["面食","番茄","鸡蛋"],"bv":"BV1P54y1H7oG","difficulty":"简单","tags":["懒人","汤"],"methods":["煮"],"tools":["一口大锅"]},{"id":276,"name":"茄汁汤面","stuff":["面食","番茄","鸡蛋"],"bv":"BV1Z7411G7AW","difficulty":"简单","tags":["懒人","汤"],"methods":["煮"],"tools":["一口大锅"]},{"id":277,"name":"清汤面","stuff":["面食","鸡蛋"],"bv":"BV1Yb411T7ED","difficulty":"简单","tags":["懒人","汤"],"methods":["煮"],"tools":["一口大锅"]},{"id":278,"name":"鸡蛋拌包菜","stuff":["包菜","鸡蛋"],"bv":"BV1Q64y1B7eq","difficulty":"简单","tags":["凉菜"],"methods":["拌"],"tools":["一口大锅"]},{"id":279,"name":"酱萝卜","stuff":["白萝卜"],"bv":"BV1BK4y1Z7rF","difficulty":"简单","tags":["凉菜"],"methods":["腌"],"tools":["一口大锅"]},{"id":280,"name":"凉拌包菜","stuff":["包菜"],"bv":"BV1HY411n7dV","difficulty":"简单","tags":["凉菜"],"methods":["拌"],"tools":["一口大锅"]},{"id":281,"name":"凉拌芹菜叶","stuff":["芹菜"],"bv":"av976520411","difficulty":"简单","tags":["凉菜"],"methods":["拌"],"tools":["一口大锅"]},{"id":282,"name":"凉拌莴笋丝","stuff":["莴笋"],"bv":"BV1Es411j7oy","difficulty":"简单","tags":["凉菜"],"methods":["凉拌"],"tools":["一口大锅"]},{"id":283,"name":"凉拌西葫芦","stuff":["西葫芦"],"bv":"BV175411U7LU","difficulty":"简单","tags":["凉菜"],"methods":["凉拌"],"tools":["一口大锅"]},{"id":284,"name":"麻酱拌西葫芦丝","stuff":["西葫芦"],"bv":"BV1Th411U7nW","difficulty":"简单","tags":["凉菜"],"methods":["凉拌"],"tools":["一口大锅"]},{"id":285,"name":"炝拌芹菜","stuff":["芹菜"],"bv":"av845641799","difficulty":"简单","tags":["凉菜"],"methods":["拌"],"tools":["一口大锅"]},{"id":286,"name":"土豆拌茄子","stuff":["土豆","茄子"],"bv":"BV1VT4y1J7Y8","difficulty":"简单","tags":["凉菜"],"methods":["蒸"],"tools":["一口大锅"]},{"id":287,"name":"香菜拌牛肉","stuff":["牛肉"],"bv":"BV1144y1n7ZV","difficulty":"简单","tags":["凉菜"],"methods":["凉拌"],"tools":["一口大锅"]},{"id":288,"name":"小菜炝拌萝卜条","stuff":["白萝卜"],"bv":"BV1nS4y1X7tM","difficulty":"简单","tags":["凉菜"],"methods":["拌"],"tools":["一口大锅"]},{"id":289,"name":"海苔虾饼","stuff":["虾","胡萝卜"],"bv":"BV16T4y1i78D","difficulty":"困难","tags":["零食"],"methods":["炸","煎"],"tools":["一口大锅"]},{"id":290,"name":"灵魂土豆丸子","stuff":["土豆"],"bv":"BV1Z64y1674r","difficulty":"","tags":["零食"],"methods":["蒸"],"tools":["一口大锅"]},{"id":291,"name":"薯塔","stuff":["土豆"],"bv":"BV1uF411v7SQ","difficulty":"普通","tags":["零食"],"methods":["炸"],"tools":["一口大锅"]},{"id":292,"name":"盖码莴笋","stuff":["莴笋","猪肉"],"bv":"BV1aV411v7xw","difficulty":"简单","tags":["清爽"],"methods":["盖浇"],"tools":["一口大锅"]},{"id":293,"name":"大根烧","stuff":["白萝卜"],"bv":"BV1TW411e7Ln","difficulty":"普通","tags":["日式"],"methods":["烧"],"tools":["一口大锅"]},{"id":294,"name":"名古屋鸡翅","stuff":["鸡肉"],"bv":"BV1ET4y1A7Xd","difficulty":"普通","tags":["日式"],"methods":["炸"],"tools":["一口大锅"]},{"id":295,"name":"日式炖白萝","stuff":["白萝卜"],"bv":"BV17b411B7H1","difficulty":"简单","tags":["日式"],"methods":["炖"],"tools":["一口大锅"]},{"id":296,"name":"炸虾天妇罗","stuff":["虾"],"bv":"BV1e5411t7LY","difficulty":"困难","tags":["日式"],"methods":["炸"],"tools":["一口大锅"]},{"id":297,"name":"可乐饼","stuff":["土豆","洋葱","猪肉","鸡蛋"],"bv":"BV17x411U75q","difficulty":"普通","tags":["日式菜"],"methods":["炸"],"tools":["一口大锅"]},{"id":298,"name":"清炒莴笋丝","stuff":["莴笋","胡萝卜"],"bv":"BV1qK411H7RL","difficulty":"简单","tags":["爽口"],"methods":["炒"],"tools":["一口大锅"]},{"id":299,"name":"莴笋泡菜","stuff":["莴笋"],"bv":"BV1h741127rS","difficulty":"简单","tags":["爽口"],"methods":["泡菜"],"tools":["一口大锅"]},{"id":300,"name":"口蘑汤","stuff":["菌菇"],"bv":"BV1e64y1h776","difficulty":"简单","tags":["汤"],"methods":["煎","炖"],"tools":["一口大锅"]},{"id":301,"name":"蘑菇浓汤","stuff":["菌菇","洋葱","芹菜"],"bv":"BV1Ut411C7Cu","difficulty":"普通","tags":["汤"],"methods":["炖"],"tools":["一口大锅"]},{"id":302,"name":"KFC同款香辣鸡翅","stuff":["鸡肉"],"bv":"BV1A7411u7Ji","difficulty":"普通","tags":["同款","辣"],"methods":["炸"],"tools":["一口大锅"]},{"id":303,"name":"M记同款薯条","stuff":["土豆"],"bv":"BV1Jx411x7iU","difficulty":"","tags":["同款"],"methods":["炸"],"tools":["一口大锅"]},{"id":304,"name":"乐事同款原味薯片","stuff":["土豆","黄瓜"],"bv":"BV1vR4y1N7fN","difficulty":"简单","tags":["同款"],"methods":["炸"],"tools":["一口大锅"]},{"id":305,"name":"M记同款麦乐鸡","stuff":["鸡肉","鸡蛋"],"bv":"BV1KA411e7Gr","difficulty":"","tags":["同款"],"methods":["炸"],"tools":["一口大锅"]},{"id":306,"name":"洋芋擦擦","stuff":["土豆","胡萝卜"],"bv":"BV1fY41187Xo","difficulty":"普通","tags":["西北特色"],"methods":["蒸"],"tools":["一口大锅"]},{"id":307,"name":"白萝卜炒肉片","stuff":["白萝卜","猪肉"],"bv":"BV1qL4y1i7xD","difficulty":"普通","tags":["下饭"],"methods":["炒"],"tools":["一口大锅"]},{"id":308,"name":"爆汁茄子","stuff":["茄子","米"],"bv":"BV1rL4y1L751","difficulty":"简单","tags":["下饭"],"methods":["烧"],"tools":["一口大锅"]},{"id":309,"name":"番茄肉酱","stuff":["番茄","芹菜","洋葱","猪肉","米","面食"],"bv":"BV1hF411t7hS","difficulty":"普通","tags":["下饭"],"methods":["烧"],"tools":["一口大锅"]},{"id":310,"name":"番茄土豆炖牛腩","stuff":["牛肉","土豆","番茄","洋葱"],"bv":"BV1344y1n73V","difficulty":"普通","tags":["下饭"],"methods":["煮"],"tools":["一口大锅"]},{"id":311,"name":"干锅土豆片","stuff":["土豆","猪肉","米"],"bv":"BV1ZZ4y1A7HK","difficulty":"简单","tags":["下饭","辣"],"methods":["炒"],"tools":["一口大锅"]},{"id":312,"name":"干锅土豆五花肉","stuff":["猪肉","土豆","洋葱"],"bv":"BV1CV411W7AD","difficulty":"简单","tags":["下饭","辣"],"methods":["炒"],"tools":["一口大锅"]},{"id":313,"name":"红烧肉","stuff":["猪肉"],"bv":"BV1vk4y127ZB","difficulty":"简单","tags":["下饭"],"methods":["煮"],"tools":["一口大锅"]},{"id":314,"name":"胡萝卜炒里脊肉","stuff":["胡萝卜","猪肉","米"],"bv":"BV1Ff4y1x7FX","difficulty":"普通","tags":["下饭"],"methods":["炒"],"tools":["一口大锅"]},{"id":315,"name":"胡萝卜炒肉","stuff":["猪肉","胡萝卜"],"bv":"BV1Ff4y1x7FX","difficulty":"简单","tags":["下饭"],"methods":["炒"],"tools":["一口大锅"]},{"id":316,"name":"胡萝卜炒五花肉","stuff":["胡萝卜","猪肉","米"],"bv":"BV1jW411Z7yd","difficulty":"普通","tags":["下饭"],"methods":["炒"],"tools":["一口大锅"]},{"id":317,"name":"红烧肉","stuff":["猪肉","鸡肉"],"bv":"BV1MJ411t7pT","difficulty":"","tags":[],"methods":["烧"],"tools":["一口大锅"]},{"id":318,"name":"可乐鸡翅","stuff":["鸡肉"],"bv":"BV1pJ411v7S9","difficulty":"","tags":[],"methods":["烧"],"tools":["一口大锅"]},{"id":319,"name":"可乐鸡翅包虾滑","stuff":["虾","鸡肉","米"],"bv":"BV1Pb4y1p7mi","difficulty":"困难","tags":["下饭"],"methods":["煎"],"tools":["一口大锅"]},{"id":320,"name":"腊肠烧茄子","stuff":["腊肠","茄子","米"],"bv":"BV1bW41127MW","difficulty":"普通","tags":["下饭"],"methods":["烧"],"tools":["一口大锅"]},{"id":321,"name":"腊肠烧土豆","stuff":["腊肠","土豆","米"],"bv":"BV1TL411V7Cp","difficulty":"普通","tags":["下饭"],"methods":["烧"],"tools":["一口大锅"]},{"id":322,"name":"萝卜泡菜","stuff":["白萝卜"],"bv":"BV1VE411y7dr","difficulty":"普通","tags":["下饭"],"methods":["腌"],"tools":["一口大锅"]},{"id":323,"name":"麻辣凉拌包菜","stuff":["包菜"],"bv":"BV18f4y1e7cy","difficulty":"简单","tags":["下饭","辣"],"methods":["拌"],"tools":["一口大锅"]},{"id":324,"name":"炝炒西葫芦","stuff":["西葫芦","番茄"],"bv":"BV1QU4y1f7sY","difficulty":"普通","tags":["下饭"],"methods":["炒"],"tools":["一口大锅"]},{"id":325,"name":"芹菜炒肉","stuff":["猪肉","芹菜"],"bv":"BV1v7411T7is","difficulty":"简单","tags":["下饭"],"methods":["炒"],"tools":["一口大锅"]},{"id":326,"name":"芹菜肥牛","stuff":["芹菜","牛肉"],"bv":"BV1ML4y1G7ba","difficulty":"","tags":["下饭"],"methods":["炒"],"tools":["一口大锅"]},{"id":327,"name":"芹菜粉","stuff":["芹菜","面食"],"bv":"BV1Eh411Y7Y3","difficulty":"","tags":["下饭"],"methods":[],"tools":["一口大锅"]},{"id":328,"name":"芹菜鸡翅焖锅","stuff":["芹菜","鸡肉"],"bv":"av761877317","difficulty":"普通","tags":["下饭"],"methods":["烧"],"tools":["一口大锅"]},{"id":329,"name":"芹菜木耳炒蛋","stuff":["芹菜","鸡蛋"],"bv":"BV1Dt411o7hq","difficulty":"","tags":["下饭"],"methods":["炒"],"tools":["一口大锅"]},{"id":330,"name":"芹菜油豆腐","stuff":["芹菜","豆腐"],"bv":"BV15J411G7Dx","difficulty":"","tags":["下饭"],"methods":["炒"],"tools":["一口大锅"]},{"id":331,"name":"肉末土豆","stuff":["土豆","猪肉","米"],"bv":"BV1ZS4y127wm","difficulty":"","tags":["下饭"],"methods":["炒"],"tools":["一口大锅"]},{"id":332,"name":"莴笋炒肉","stuff":["莴笋","猪肉"],"bv":"BV1d3411H7G5","difficulty":"普通","tags":["下饭"],"methods":["炒"],"tools":["一口大锅"]},{"id":333,"name":"西葫芦炒蛋","stuff":["西葫芦","鸡蛋","番茄"],"bv":"BV1wT4y1Q7Db","difficulty":"简单","tags":["下饭"],"methods":["炒"],"tools":["一口大锅"]},{"id":334,"name":"西葫芦炒五花肉","stuff":["西葫芦","猪肉"],"bv":"BV1TL411T7eX","difficulty":"普通","tags":["下饭"],"methods":["炒"],"tools":["一口大锅"]},{"id":335,"name":"香辣土豆片","stuff":["土豆","米"],"bv":"BV1Sq4y137uw","difficulty":"","tags":["下饭","辣"],"methods":["煮"],"tools":["一口大锅"]},{"id":336,"name":"小炒拆骨肉","stuff":["骨头","米"],"bv":"BV14p411o7zm","difficulty":"困难","tags":["下饭"],"methods":["炒"],"tools":["一口大锅"]},{"id":337,"name":"孜然土豆火腿","stuff":["土豆","香肠","米"],"bv":"BV1k44y147kM","difficulty":"简单","tags":["下饭"],"methods":["炸"],"tools":["一口大锅"]},{"id":338,"name":"芹菜猪肉馅料","stuff":["芹菜","猪肉"],"bv":"BV18b411r7CP","difficulty":"","tags":["馅料"],"methods":[],"tools":["一口大锅"]},{"id":339,"name":"包菜厚蛋烧","stuff":["包菜","鸡蛋"],"bv":"BV11g411A7QE","difficulty":"普通","tags":["小吃"],"methods":["烧"],"tools":["一口大锅"]},{"id":340,"name":"风味茄子","stuff":["茄子"],"bv":"BV1tZ4y1U7iN","difficulty":"普通","tags":["小吃"],"methods":["炸"],"tools":["一口大锅"]},{"id":341,"name":"油炸白萝卜","stuff":["白萝卜"],"bv":"BV1J54y1x7ie","difficulty":"简单","tags":["小吃"],"methods":["炸"],"tools":["一口大锅"]},{"id":342,"name":"炸包菜串","stuff":["包菜"],"bv":"BV1Ts411F7q3","difficulty":"简单","tags":["小吃"],"methods":["炸"],"tools":["一口大锅"]},{"id":343,"name":"日式炒胡萝卜丝","stuff":["胡萝卜"],"bv":"BV1mY411774Z","difficulty":"简单","tags":["消耗囤货","日式"],"methods":["炒"],"tools":["一口大锅"]},{"id":344,"name":"番茄肉酱面","stuff":["番茄","猪肉","面食"],"bv":"BV1Cm4y1Q7RS","difficulty":"简单","tags":["意式"],"methods":["烧"],"tools":["一口大锅"]},{"id":345,"name":"胡萝卜炒蛋","stuff":["胡萝卜","鸡蛋"],"bv":"BV11E411Q7WB","difficulty":"简单","tags":["营养"],"methods":["炒"],"tools":["一口大锅"]},{"id":346,"name":"番茄酸汤火锅","stuff":["番茄","胡萝卜","菌菇","洋葱","豆腐","面食","白萝卜","土豆","白菜","猪肉","牛肉","午餐肉"],"bv":"BV1yz4y1C7Qu","difficulty":"困难","tags":["杂烩","汤"],"methods":["煮"],"tools":["一口大锅"]},{"id":347,"name":"骨头汤火锅锅底做法(全鸡版)","stuff":["骨头","土豆","胡萝卜","花菜","白萝卜","西葫芦","芹菜","菌菇","豆腐","包菜","白菜","午餐肉","鸡肉","猪肉","虾","牛肉","面食","方便面"],"bv":"BV1bi4y187ro","difficulty":"困难","tags":["杂烩","汤"],"methods":["煮"],"tools":["一口大锅"]},{"id":348,"name":"乱炖鸡胸","stuff":["鸡肉","洋葱","菌菇","胡萝卜","土豆"],"bv":"BV1Qb411u7Vu","difficulty":"普通","tags":["杂烩"],"methods":["炖"],"tools":["一口大锅"]},{"id":349,"name":"清汤锅万能高汤做法(鸡蛋+猪肉)","stuff":["猪肉","鸡蛋","土豆","胡萝卜","花菜","白萝卜","西葫芦","芹菜","菌菇","豆腐","包菜","白菜","午餐肉","鸡肉","虾","牛肉","面食","方便面"],"bv":"BV1zD4y197Us","difficulty":"困难","tags":["杂烩","汤"],"methods":["煮"],"tools":["一口大锅"]},{"id":350,"name":"无米炒饭","stuff":["花菜","黄瓜","胡萝卜","鸡蛋","豆腐","虾"],"bv":"BV1Cf4y1V7sv","difficulty":"","tags":["杂烩"],"methods":["炒"],"tools":["一口大锅"]},{"id":351,"name":"胡萝卜蛋饼","stuff":["胡萝卜","鸡蛋"],"bv":"BV1Nh411Z7hZ","difficulty":"简单","tags":["早餐"],"methods":["煎"],"tools":["一口大锅"]},{"id":352,"name":"土豆丝饼","stuff":["土豆"],"bv":"BV1fY41187Xo","difficulty":"简单","tags":["早餐"],"methods":["煎"],"tools":["一口大锅"]},{"id":353,"name":"午餐肉蛋饼","stuff":["午餐肉","鸡蛋","面包"],"bv":"BV1d34y1X7A5","difficulty":"简单","tags":["早餐"],"methods":["煎"],"tools":["一口大锅"]},{"id":354,"name":"午餐肉饭团","stuff":["午餐肉","米"],"bv":"BV1v7411t7uS","difficulty":"简单","tags":["早餐"],"methods":["煎"],"tools":["一口大锅"]},{"id":355,"name":"午餐肉米汉堡","stuff":["午餐肉","米","鸡蛋","包菜"],"bv":"BV1n5411U78R","difficulty":"简单","tags":["早餐"],"methods":["煎"],"tools":["一口大锅"]},{"id":356,"name":"萝卜砂锅粥","stuff":["白萝卜","米"],"bv":"BV1mz4y1C7H9","difficulty":"普通","tags":["早饭"],"methods":["煮"],"tools":["一口大锅","砂锅"]},{"id":357,"name":"麻辣豆腐包","stuff":["面食","豆腐"],"bv":"BV1y64y1U7jY","difficulty":"普通","tags":["早饭","辣"],"methods":["蒸"],"tools":["一口大锅"]},{"id":358,"name":"棉花馒头","stuff":["面食"],"bv":"BV1SK4y1r7dr","difficulty":"普通","tags":["早饭"],"methods":["蒸"],"tools":["一口大锅"]},{"id":359,"name":"奶黄包","stuff":["面食","鸡蛋"],"bv":"BV1et4y127jQ","difficulty":"普通","tags":["早饭"],"methods":["蒸"],"tools":["一口大锅"]},{"id":360,"name":"蒸花卷","stuff":["面食"],"bv":"BV1w64y1X7cT","difficulty":"困难","tags":["早饭"],"methods":["蒸"],"tools":["一口大锅"]},{"id":361,"name":"茄子焖面","stuff":["茄子","番茄","面食"],"bv":"BV1BA411H771","difficulty":"简单","tags":["主食"],"methods":["烧"],"tools":["一口大锅"]},{"id":362,"name":"芹菜肉饺子","stuff":["芹菜","猪肉","面食"],"bv":"av546464255","difficulty":"普通","tags":["主食"],"methods":["蒸"],"tools":["一口大锅"]},{"id":363,"name":"芹菜蒸面卷","stuff":["芹菜","胡萝卜","面食"],"bv":"BV1JZ4y1s7Sh","difficulty":"","tags":["主食"],"methods":["蒸"],"tools":["一口大锅"]},{"id":364,"name":"蒸芹菜叶","stuff":["芹菜","面食"],"bv":"BV1Rg411c72w","difficulty":"","tags":["主食"],"methods":["蒸"],"tools":["一口大锅"]},{"id":365,"name":"白菜炒土豆片","stuff":["白菜","土豆"],"bv":"BV17y4y1e76x","difficulty":"普通","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":366,"name":"白菜炒香菇","stuff":["白菜","菌菇"],"bv":"BV1MS4y117Kp","difficulty":"普通","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":367,"name":"白菜荷包蛋","stuff":["白菜","鸡蛋","猪肉"],"bv":"BV16W411a7V8","difficulty":"","tags":[],"methods":["煎","煮"],"tools":["一口大锅"]},{"id":368,"name":"白菜鸡蛋烙","stuff":["白菜","鸡蛋"],"bv":"BV1rb411F7en","difficulty":"","tags":[],"methods":["烙"],"tools":["一口大锅"]},{"id":369,"name":"白菜鸡胸肉","stuff":["白菜","鸡肉"],"bv":"BV1nf4y127Cx","difficulty":"","tags":[],"methods":[],"tools":["一口大锅"]},{"id":370,"name":"白菜鸡胸肉卷","stuff":["白菜","鸡肉","鸡蛋"],"bv":"BV1nf4y127Cx","difficulty":"","tags":[],"methods":["蒸"],"tools":["一口大锅"]},{"id":371,"name":"白菜卷火锅","stuff":["白菜","菌菇","胡萝卜","香肠"],"bv":"BV1wY411n7Vk","difficulty":"","tags":[],"methods":["煮"],"tools":["一口大锅"]},{"id":372,"name":"白菜酿肉","stuff":["白菜","猪肉","胡萝卜","菌菇"],"bv":"BV1Q5411J7oT","difficulty":"","tags":[],"methods":["蒸"],"tools":["一口大锅"]},{"id":373,"name":"白菜肉卷","stuff":["白菜","番茄","洋葱","猪肉","米","鸡蛋"],"bv":"BV1Ru411C7Hn","difficulty":"困难","tags":[],"methods":["蒸"],"tools":["一口大锅"]},{"id":374,"name":"白菜三丝饼","stuff":["白菜","胡萝卜","西葫芦","鸡蛋"],"bv":"BV1oF411B7Ys","difficulty":"普通","tags":[],"methods":["煎"],"tools":["一口大锅"]},{"id":375,"name":"白萝卜煲","stuff":["白萝卜","骨头"],"bv":"BV1dU4y1u7eb","difficulty":"普通","tags":[],"methods":["煲"],"tools":["一口大锅","砂锅"]},{"id":376,"name":"白萝卜炒鸡蛋","stuff":["白萝卜","鸡蛋"],"bv":"BV1PK4y157u2","difficulty":"普通","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":377,"name":"白萝卜大骨头汤","stuff":["骨头","白萝卜"],"bv":"BV1HE41137ZY","difficulty":"普通","tags":["汤"],"methods":["炖"],"tools":["一口大锅"]},{"id":378,"name":"白萝卜炖猪脚","stuff":["白萝卜","猪肉"],"bv":"BV1m7411c7pD","difficulty":"困难","tags":[],"methods":["炖"],"tools":["一口大锅"]},{"id":379,"name":"白萝卜骨头汤","stuff":["白萝卜","骨头"],"bv":"BV1gf4y1u7ap","difficulty":"普通","tags":["汤"],"methods":["煮"],"tools":["一口大锅"]},{"id":380,"name":"白萝卜焖鸡翅","stuff":["白萝卜","鸡肉"],"bv":"BV1fs41187XG","difficulty":"普通","tags":[],"methods":["焖"],"tools":["一口大锅"]},{"id":381,"name":"白萝卜烧肉","stuff":["白萝卜","猪肉"],"bv":"BV1x54y147mz","difficulty":"普通","tags":[],"methods":["烧"],"tools":["一口大锅"]},{"id":382,"name":"白切肉","stuff":["猪肉"],"bv":"BV15h411s7Ze","difficulty":"","tags":[],"methods":["煮"],"tools":["一口大锅"]},{"id":383,"name":"班尼迪克蛋","stuff":["鸡蛋","面包"],"bv":"BV1TT4y127vu","difficulty":"","tags":["西式"],"methods":["煮"],"tools":["一口大锅"]},{"id":384,"name":"包菜炒蛋","stuff":["包菜","鸡蛋"],"bv":"BV1w34y1k7wu","difficulty":"简单","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":385,"name":"包菜炒香肠","stuff":["包菜","腊肠"],"bv":"BV1AW411m77S","difficulty":"普通","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":386,"name":"包菜大阪烧","stuff":["包菜","鸡蛋"],"bv":"BV1tA411F7Eq","difficulty":"普通","tags":["日式"],"methods":["煎"],"tools":["一口大锅"]},{"id":387,"name":"包菜豆腐煲","stuff":["包菜","豆腐"],"bv":"BV1Cq4y157s1","difficulty":"普通","tags":[],"methods":["煲"],"tools":["一口大锅","砂锅"]},{"id":388,"name":"包菜烧肉","stuff":["包菜","猪肉"],"bv":"BV1Wp4y1a7YR","difficulty":"普通","tags":[],"methods":["烧"],"tools":["一口大锅"]},{"id":389,"name":"包菜五花肉","stuff":["猪肉","包菜"],"bv":"BV1La411F7H4","difficulty":"普通","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":390,"name":"抱蛋肥牛饭","stuff":["洋葱","牛肉","鸡蛋","米"],"bv":"BV1Kb4y147m2","difficulty":"","tags":[],"methods":["煎"],"tools":["一口大锅"]},{"id":391,"name":"爆汁肉饼","stuff":["猪肉","鸡蛋"],"bv":"BV1Cm4y1X7ej","difficulty":"","tags":[],"methods":["煎"],"tools":["一口大锅"]},{"id":392,"name":"爆汁香脆蛋","stuff":["鸡蛋"],"bv":"BV1yR4y1575Y","difficulty":"","tags":[],"methods":[],"tools":["一口大锅"]},{"id":393,"name":"biangbiang面","stuff":["面食"],"bv":"BV1844y157GL","difficulty":"简单","tags":["西北特色"],"methods":["油泼"],"tools":["一口大锅"]},{"id":394,"name":"菜包鸡胸","stuff":["鸡肉"],"bv":"BV1h54y1Q7pf","difficulty":"","tags":[],"methods":[],"tools":["一口大锅"]},{"id":395,"name":"茶叶蛋","stuff":["鸡蛋"],"bv":"BV1TT4y127vu","difficulty":"","tags":[],"methods":["卤"],"tools":["一口大锅"]},{"id":396,"name":"娼妇意面","stuff":["番茄","洋葱"],"bv":"BV1hF411t7hS","difficulty":"普通","tags":["意式"],"methods":["烧"],"tools":["一口大锅"]},{"id":397,"name":"炒白萝卜丝","stuff":["白萝卜"],"bv":"BV1ph411p7Pm","difficulty":"简单","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":398,"name":"炒方便面","stuff":["方便面","鸡蛋","香肠"],"bv":"BV1Q54y1p7n8","difficulty":"普通","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":399,"name":"潮汕杂菇煲","stuff":["菌菇","猪肉"],"bv":"BV15E411K7B5","difficulty":"简单","tags":["粤菜"],"methods":["炒"],"tools":["一口大锅"]},{"id":400,"name":"川味泡面","stuff":["方便面","鸡蛋"],"bv":"BV1G7411f7FA","difficulty":"简单","tags":["辣","川菜"],"methods":["煮"],"tools":["一口大锅"]},{"id":401,"name":"葱油拌面","stuff":["面食"],"bv":"BV1H7411c7Xe","difficulty":"简单","tags":[],"methods":["拌"],"tools":["一口大锅"]},{"id":402,"name":"葱油饼","stuff":["面食"],"bv":"BV1gb411J7Xe","difficulty":"普通","tags":[],"methods":["烙"],"tools":["一口大锅"]},{"id":403,"name":"葱油焖鸡翅","stuff":["鸡肉","洋葱","菌菇"],"bv":"BV1uP4y1P7oQ","difficulty":"普通","tags":[],"methods":["煎"],"tools":["一口大锅"]},{"id":404,"name":"醋溜白菜","stuff":["白菜","米"],"bv":"BV12K4y157im","difficulty":"","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":405,"name":"醋卤面","stuff":["面食","猪肉"],"bv":"BV1XC4y1p7Yn","difficulty":"普通","tags":[],"methods":["拌"],"tools":["一口大锅"]},{"id":406,"name":"脆皮糖醋豆腐","stuff":["豆腐"],"bv":"BV1dU4y1d7hz","difficulty":"普通","tags":[],"methods":["炸"],"tools":["一口大锅"]},{"id":407,"name":"脆皮土豆虾","stuff":["虾","土豆"],"bv":"BV1Kq4y1q7PM","difficulty":"普通","tags":[],"methods":["炸"],"tools":["一口大锅"]},{"id":408,"name":"大餐口味方便面","stuff":["方便面"],"bv":"BV1s34y1X74a","difficulty":"","tags":[],"methods":[],"tools":["一口大锅"]},{"id":409,"name":"大虾烧白菜","stuff":["白菜","虾"],"bv":"BV1yT4y157x5","difficulty":"","tags":[],"methods":["烧"],"tools":["一口大锅"]},{"id":410,"name":"地三鲜","stuff":["土豆","茄子"],"bv":"BV1st4y1Y73H","difficulty":"","tags":["东北菜"],"methods":["炸","炒"],"tools":["一口大锅"]},{"id":411,"name":"东北鸡蛋酱","stuff":["鸡蛋"],"bv":"BV1TT4y127vu","difficulty":"","tags":["东北菜"],"methods":["炒"],"tools":["一口大锅"]},{"id":412,"name":"豆腐饭(蛋炒饭)","stuff":["豆腐","鸡蛋","米"],"bv":"BV1dU4y1d7hz","difficulty":"普通","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":413,"name":"法式炒蛋","stuff":["鸡蛋"],"bv":"BV1TT4y127vu","difficulty":"","tags":["法式"],"methods":["炒"],"tools":["一口大锅"]},{"id":414,"name":"法式胡萝卜炖牛肉","stuff":["胡萝卜","牛肉","洋葱"],"bv":"BV1UR4y1V7nV","difficulty":"困难","tags":["法式"],"methods":["炖"],"tools":["一口大锅"]},{"id":415,"name":"番茄肥牛","stuff":["番茄","牛肉"],"bv":"BV1hF411t7hS","difficulty":"普通","tags":[],"methods":["烧"],"tools":["一口大锅"]},{"id":416,"name":"番茄肥牛面","stuff":["番茄","牛肉","面食"],"bv":"BV1xb4y167LH","difficulty":"普通","tags":[],"methods":["煮"],"tools":["一口大锅"]},{"id":417,"name":"番茄虾滑汤","stuff":["番茄","虾","面食"],"bv":"BV1eZ4y1o7TK","difficulty":"困难","tags":["汤"],"methods":["煮"],"tools":["一口大锅"]},{"id":418,"name":"番茄鸡翅焖锅","stuff":["鸡肉","胡萝卜","土豆","洋葱"],"bv":"BV1Ap4y147Ac","difficulty":"普通","tags":[],"methods":["煮"],"tools":["一口大锅"]},{"id":419,"name":"番茄鸡蛋面","stuff":["方便面","番茄","鸡蛋"],"bv":"BV1tL4y1b7SM","difficulty":"简单","tags":[],"methods":["煮"],"tools":["一口大锅"]},{"id":420,"name":"番茄鸡蛋油泼面","stuff":["番茄","鸡蛋","面食"],"bv":"BV1hF411t7hS","difficulty":"普通","tags":[],"methods":["煮"],"tools":["一口大锅"]},{"id":421,"name":"番茄焖面","stuff":["番茄","面食","香肠","鸡蛋"],"bv":"BV1NL4y1571X","difficulty":"","tags":[],"methods":["焖"],"tools":["一口大锅"]},{"id":422,"name":"番茄排骨汤","stuff":["番茄","骨头"],"bv":"BV11y4y167Wa","difficulty":"普通","tags":["汤"],"methods":["煮"],"tools":["一口大锅"]},{"id":423,"name":"番茄烧茄子","stuff":["茄子","番茄"],"bv":"BV11b4y1i7ck","difficulty":"普通","tags":[],"methods":["烧"],"tools":["一口大锅"]},{"id":424,"name":"番茄土豆牛腩饭","stuff":["牛肉","土豆","番茄","胡萝卜","米"],"bv":"BV1La411i73b","difficulty":"普通","tags":[],"methods":["煮"],"tools":["一口大锅"]},{"id":425,"name":"番茄芝士蛋饼","stuff":["番茄","鸡蛋"],"bv":"BV1PQ4y1d7ew","difficulty":"普通","tags":[],"methods":["煎"],"tools":["一口大锅"]},{"id":426,"name":"方便面炒饭","stuff":["方便面","米","香肠","鸡蛋"],"bv":"BV1Uf4y157RG","difficulty":"简单","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":427,"name":"翡翠玉带虾仁","stuff":["黄瓜","虾","鸡蛋"],"bv":"BV1E4411w7wC","difficulty":"困难","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":428,"name":"富贵金钱蛋(湖南口味辣)","stuff":["鸡蛋"],"bv":"BV15q4y1A784","difficulty":"","tags":["辣","湘菜"],"methods":["煎"],"tools":["一口大锅"]},{"id":429,"name":"干锅花菜","stuff":["花菜","洋葱","米"],"bv":"BV1Rb4y147bZ","difficulty":"普通","tags":["辣"],"methods":["炒"],"tools":["一口大锅"]},{"id":430,"name":"干锅莴笋","stuff":["莴笋"],"bv":"BV1TC4y1W76c","difficulty":"","tags":["辣"],"methods":["炒"],"tools":["一口大锅"]},{"id":431,"name":"干锅西葫芦","stuff":["西葫芦","洋葱"],"bv":"BV1eP4y1T76T","difficulty":"普通","tags":["辣"],"methods":["炒"],"tools":["一口大锅"]},{"id":432,"name":"干锅西葫芦","stuff":["西葫芦","猪肉"],"bv":"BV1Vu411B7o6","difficulty":"","tags":["辣"],"methods":["炒"],"tools":["一口大锅"]},{"id":433,"name":"宫保鸡丁","stuff":["鸡肉","黄瓜"],"bv":"BV1wE411T7X5","difficulty":"","tags":["辣","川菜"],"methods":["炒"],"tools":["一口大锅"]},{"id":434,"name":"骨汤面","stuff":["方便面","鸡蛋","牛肉"],"bv":"BV1Uf4y157RG","difficulty":"简单","tags":["汤"],"methods":["煮"],"tools":["一口大锅"]},{"id":435,"name":"骨头炖肉","stuff":["骨头","猪肉"],"bv":"BV1bS4y1K7BD","difficulty":"普通","tags":[],"methods":["炖"],"tools":["一口大锅"]},{"id":436,"name":"海鲜泡面","stuff":["方便面","虾","鸡蛋"],"bv":"BV1G7411f7FA","difficulty":"简单","tags":[],"methods":["煮"],"tools":["一口大锅"]},{"id":437,"name":"黑椒鸡胸肉","stuff":["鸡肉"],"bv":"BV1PX4y1V7S3","difficulty":"","tags":[],"methods":["煎"],"tools":["一口大锅"]},{"id":438,"name":"红烧大骨头","stuff":["骨头"],"bv":"BV1hg4y1i7Ss","difficulty":"普通","tags":[],"methods":["烧"],"tools":["一口大锅"]},{"id":439,"name":"厚蛋烧","stuff":["鸡蛋"],"bv":"BV1TT4y127vu","difficulty":"","tags":["日式"],"methods":["煎"],"tools":["一口大锅"]},{"id":440,"name":"胡辣莴笋","stuff":["莴笋"],"bv":"BV1NW411T7Xb","difficulty":"","tags":["辣"],"methods":["炒"],"tools":["一口大锅"]},{"id":441,"name":"花菜虾仁炒饭","stuff":["花菜","虾","米","洋葱","胡萝卜"],"bv":"BV1z4411a7dy","difficulty":"普通","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":442,"name":"滑蛋牛肉","stuff":["牛肉","鸡蛋"],"bv":"BV12u411Z72s","difficulty":"简单","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":443,"name":"黄瓜炒鸡蛋","stuff":["黄瓜","鸡蛋"],"bv":"BV1Fi4y1E7pt","difficulty":"简单","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":444,"name":"黄瓜鸡蛋卷","stuff":["黄瓜","鸡蛋"],"bv":"BV1GW411L7Sb","difficulty":"普通","tags":[],"methods":["煎"],"tools":["一口大锅"]},{"id":445,"name":"黄瓜鸡蛋汤","stuff":["黄瓜","鸡蛋"],"bv":"BV1zi4y1w7C8","difficulty":"简单","tags":["汤"],"methods":["煮"],"tools":["一口大锅"]},{"id":446,"name":"黄瓜鸡胸肉","stuff":["鸡肉","黄瓜","胡萝卜"],"bv":"BV1BF41137JV","difficulty":"普通","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":447,"name":"黄瓜钱炒肉","stuff":["黄瓜","猪肉"],"bv":"BV1WS4y1r7Tx","difficulty":"普通","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":448,"name":"火腿白菜豆腐煲","stuff":["白菜","豆腐","香肠"],"bv":"BV1o44y1r7hX","difficulty":"","tags":[],"methods":["煲"],"tools":["一口大锅","砂锅"]},{"id":449,"name":"鸡蛋饼","stuff":["鸡蛋"],"bv":"BV1a34y117iz","difficulty":"","tags":[],"methods":["煎"],"tools":["一口大锅"]},{"id":450,"name":"鸡蛋番茄拌面","stuff":["方便面","鸡蛋","番茄"],"bv":"BV1SS4y1G7fp","difficulty":"普通","tags":[],"methods":["拌"],"tools":["一口大锅"]},{"id":451,"name":"鸡蛋焖面","stuff":["面食","鸡蛋"],"bv":"BV1SS4y1G7fp","difficulty":"普通","tags":[],"methods":["焖"],"tools":["一口大锅"]},{"id":452,"name":"鸡蛋蒜","stuff":["鸡蛋"],"bv":"BV1TT4y127vu","difficulty":"","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":453,"name":"鸡蛋西兰花豆腐羹","stuff":["花菜","豆腐","鸡蛋"],"bv":"BV164411g77n","difficulty":"简单","tags":["汤"],"methods":["炖"],"tools":["一口大锅"]},{"id":454,"name":"鸡蛋洋葱饭","stuff":["洋葱","鸡蛋","米"],"bv":"BV1TU4y1H7Cr","difficulty":"","tags":[],"methods":[],"tools":["一口大锅"]},{"id":455,"name":"鸡蛋芝士泡面","stuff":["方便面","鸡蛋","香肠"],"bv":"BV1SS4y1G7fp","difficulty":"普通","tags":[],"methods":["煮"],"tools":["一口大锅"]},{"id":456,"name":"鸡蛋芝士培根吐司","stuff":["鸡蛋","面包"],"bv":"BV1hv411E7RW","difficulty":"","tags":[],"methods":["煎"],"tools":["一口大锅"]},{"id":457,"name":"鸡胸肉蔬菜小饼","stuff":["鸡肉","胡萝卜","花菜"],"bv":"BV1i7411d7Rd","difficulty":"","tags":[],"methods":["煎"],"tools":["一口大锅"]},{"id":458,"name":"家常黄焖鸡(多调料版)","stuff":["鸡肉","菌菇","土豆","胡萝卜"],"bv":"BV17u411X7rR","difficulty":"","tags":[],"methods":["焖"],"tools":["一口大锅"]},{"id":459,"name":"煎蛋方便面","stuff":["方便面"],"bv":"BV1s34y1X74a","difficulty":"","tags":[],"methods":["煎"],"tools":["一口大锅"]},{"id":460,"name":"煎蛋焖面","stuff":["面食","鸡蛋"],"bv":"BV1d54y1v7JE","difficulty":"简单","tags":[],"methods":["煮"],"tools":["一口大锅"]},{"id":461,"name":"煎方便面饼","stuff":["方便面","鸡蛋","胡萝卜"],"bv":"BV1Ey4y1g7p8","difficulty":"普通","tags":[],"methods":["煎"],"tools":["一口大锅"]},{"id":462,"name":"煎土豆","stuff":["土豆"],"bv":"BV15s411A7GB","difficulty":"","tags":[],"methods":["炸"],"tools":["一口大锅"]},{"id":463,"name":"酱爆西葫芦","stuff":["西葫芦"],"bv":"BV175411U7LU","difficulty":"简单","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":464,"name":"酱黄瓜","stuff":["黄瓜"],"bv":"BV1y4411T78H","difficulty":"简单","tags":[],"methods":["腌制"],"tools":["一口大锅"]},{"id":465,"name":"酱鸡蛋","stuff":["鸡蛋"],"bv":"BV19L411u7H4","difficulty":"","tags":[],"methods":["卤"],"tools":["一口大锅"]},{"id":466,"name":"椒盐鸡翅","stuff":["鸡肉"],"bv":"BV1HK4y1o77T","difficulty":"简单","tags":[],"methods":["煎"],"tools":["一口大锅"]},{"id":467,"name":"金丝午餐肉","stuff":["土豆","午餐肉"],"bv":"BV1W34y1k7Bg","difficulty":"普通","tags":[],"methods":["炸"],"tools":["一口大锅"]},{"id":468,"name":"开胃莴笋叶","stuff":["莴笋"],"bv":"BV16441147Dc","difficulty":"","tags":[],"methods":["拌"],"tools":["一口大锅"]},{"id":469,"name":"空气土豆","stuff":["土豆"],"bv":"BV1RL411E7Uv","difficulty":"困难","tags":[],"methods":["炸"],"tools":["一口大锅"]},{"id":470,"name":"腊肠炒黄瓜","stuff":["腊肠","黄瓜","米"],"bv":"BV1zA411x7oK","difficulty":"普通","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":471,"name":"腊肠炒鸡蛋","stuff":["腊肠","鸡蛋"],"bv":"BV1x54y127CC","difficulty":"普通","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":472,"name":"腊肠豆腐煲","stuff":["腊肠","豆腐","米"],"bv":"BV1Sf4y1r7mS","difficulty":"普通","tags":[],"methods":["煲"],"tools":["一口大锅","砂锅"]},{"id":473,"name":"腊肠茄子煲","stuff":["腊肠","茄子","米"],"bv":"BV1vW411g7td","difficulty":"普通","tags":[],"methods":["煲"],"tools":["一口大锅","砂锅"]},{"id":474,"name":"腊肠烧麦","stuff":["腊肠","面食"],"bv":"BV1VV411C7o2","difficulty":"普通","tags":[],"methods":["烧"],"tools":["一口大锅"]},{"id":475,"name":"辣味牛肉凉拌小黄瓜","stuff":["黄瓜","牛肉","洋葱"],"bv":"BV1qr4y1Y792","difficulty":"简单","tags":["辣"],"methods":["凉拌"],"tools":["一口大锅"]},{"id":476,"name":"辣子鸡丁","stuff":["鸡肉"],"bv":"BV1Ma4y147MA","difficulty":"","tags":["辣","川菜"],"methods":["炸","炒"],"tools":["一口大锅"]},{"id":477,"name":"烂糊白菜","stuff":["白菜","鸡蛋"],"bv":"BV1x3411y7eN","difficulty":"","tags":[],"methods":["烧"],"tools":["一口大锅"]},{"id":478,"name":"莲花洋葱(消耗洋葱!)","stuff":["洋葱","鸡蛋"],"bv":"BV1MW411T7Cv","difficulty":"","tags":[],"methods":["炸"],"tools":["一口大锅"]},{"id":479,"name":"凉拌黄瓜虾滑","stuff":["黄瓜","虾"],"bv":"BV1RF411W7v7","difficulty":"困难","tags":[],"methods":["凉拌"],"tools":["一口大锅"]},{"id":480,"name":"凉拌莴苣","stuff":["莴笋"],"bv":"BV1z3411Y7Bs","difficulty":"","tags":[],"methods":["凉拌"],"tools":["一口大锅"]},{"id":481,"name":"凉拌胸丝","stuff":["鸡肉"],"bv":"BV1h54y1Q7pf","difficulty":"","tags":[],"methods":["凉拌"],"tools":["一口大锅"]},{"id":482,"name":"凉拌洋葱","stuff":["洋葱"],"bv":"BV1BS4y1G7XD","difficulty":"","tags":[],"methods":["凉拌"],"tools":["一口大锅"]},{"id":483,"name":"流心蛋","stuff":["鸡蛋"],"bv":"BV1Yt411G7Ut","difficulty":"","tags":[],"methods":["煮"],"tools":["一口大锅"]},{"id":484,"name":"萝卜牛肉煲","stuff":["白萝卜","牛肉"],"bv":"BV1a7411L733","difficulty":"困难","tags":[],"methods":["煲"],"tools":["一口大锅","砂锅"]},{"id":485,"name":"萝卜烧牛腩","stuff":["白萝卜","牛肉"],"bv":"BV1nK41137Lb","difficulty":"普通","tags":[],"methods":["烧"],"tools":["一口大锅"]},{"id":486,"name":"萝卜烧排骨","stuff":["白萝卜","骨头"],"bv":"BV1YV411f7L8","difficulty":"普通","tags":[],"methods":["烧"],"tools":["一口大锅"]},{"id":487,"name":"萝卜丝炒牛肉","stuff":["白萝卜","牛肉"],"bv":"BV1jA411P7LJ","difficulty":"普通","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":488,"name":"萝卜鲜虾煲","stuff":["白萝卜","虾"],"bv":"BV1Mp4y1z7Yr","difficulty":"困难","tags":[],"methods":["煲"],"tools":["一口大锅","砂锅"]},{"id":489,"name":"罗宋汤","stuff":["胡萝卜","土豆","番茄","牛肉","包菜"],"bv":"BV1394y1d7f6","difficulty":"普通","tags":["汤"],"methods":["煮"],"tools":["一口大锅"]},{"id":490,"name":"马拉糕","stuff":["面食"],"bv":"BV1fa4y1a7Xy","difficulty":"困难","tags":[],"methods":["蒸"],"tools":["一口大锅"]},{"id":491,"name":"麻辣烫方便面","stuff":["方便面","香肠","鸡蛋","包菜"],"bv":"BV1Yp411o7MB","difficulty":"简单","tags":["辣"],"methods":["煮"],"tools":["一口大锅"]},{"id":492,"name":"焖泡面","stuff":["方便面","猪肉","茄子"],"bv":"BV1s34y1X74a","difficulty":"","tags":[],"methods":["焖"],"tools":["一口大锅"]},{"id":493,"name":"秘制洋葱炒肥牛","stuff":["洋葱","牛肉"],"bv":"BV1vT4y1E7fZ","difficulty":"","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":494,"name":"牧羊人派","stuff":["土豆","猪肉"],"bv":"BV1fY41187Xo","difficulty":"普通","tags":["西式"],"methods":["烤"],"tools":["一口大锅"]},{"id":495,"name":"奶汤方便面","stuff":["方便面"],"bv":"BV1s34y1X74a","difficulty":"","tags":["汤"],"methods":["煮"],"tools":["一口大锅"]},{"id":496,"name":"牛肉蒸黄瓜条","stuff":["黄瓜","牛肉","鸡蛋"],"bv":"BV1V5411S7Uv","difficulty":"普通","tags":[],"methods":["蒸"],"tools":["一口大锅"]},{"id":497,"name":"拍黄瓜","stuff":["黄瓜"],"bv":"BV1RV41167WV","difficulty":"简单","tags":[],"methods":["凉拌"],"tools":["一口大锅"]},{"id":498,"name":"铺盖打蛋","stuff":["鸡蛋"],"bv":"BV1TT4y127vu","difficulty":"","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":499,"name":"蒲烧茄子","stuff":["茄子"],"bv":"BV1q44y1n78y","difficulty":"普通","tags":[],"methods":["烧"],"tools":["一口大锅"]},{"id":500,"name":"千层土豆","stuff":["土豆"],"bv":"BV1nb4y147D6","difficulty":"困难","tags":[],"methods":["炸"],"tools":["一口大锅"]},{"id":501,"name":"茄汁大虾","stuff":["虾","番茄","米","面食"],"bv":"BV1aJ411K75X","difficulty":"简单","tags":[],"methods":["烧"],"tools":["一口大锅"]},{"id":502,"name":"茄汁蛋黄浓汤面","stuff":["方便面","番茄","鸡蛋"],"bv":"BV1s34y1X74a","difficulty":"","tags":["汤"],"methods":["煮"],"tools":["一口大锅"]},{"id":503,"name":"茄汁豆腐","stuff":["番茄","豆腐"],"bv":"BV1Xb4y1p7mv","difficulty":"简单","tags":[],"methods":["烧"],"tools":["一口大锅"]},{"id":504,"name":"茄汁花菜","stuff":["花菜","番茄"],"bv":"BV1J541187QQ","difficulty":"普通","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":505,"name":"茄汁鸡胸肉","stuff":["鸡肉","番茄"],"bv":"BV1L54y1b7Xq","difficulty":"","tags":[],"methods":["煎"],"tools":["一口大锅"]},{"id":506,"name":"茄子卷","stuff":["茄子","猪肉"],"bv":"BV1L3411H7EK","difficulty":"普通","tags":[],"methods":["炸","煎"],"tools":["一口大锅"]},{"id":507,"name":"芹菜炒腊肉","stuff":["芹菜","猪肉"],"bv":"av209779329","difficulty":"普通","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":508,"name":"芹菜炒肉丝","stuff":["芹菜","猪肉"],"bv":"av806554209","difficulty":"普通","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":509,"name":"芹菜炒香干","stuff":["芹菜"],"bv":"av671691480","difficulty":"普通","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":510,"name":"芹菜煎饼","stuff":["芹菜","鸡蛋"],"bv":"av247131174","difficulty":"简单","tags":[],"methods":["煎"],"tools":["一口大锅"]},{"id":511,"name":"清炖牛肉","stuff":["牛肉","白萝卜"],"bv":"BV1CL4y1n76E","difficulty":"普通","tags":[],"methods":["炖"],"tools":["一口大锅"]},{"id":512,"name":"青瓜炒火腿肠","stuff":["黄瓜","香肠"],"bv":"BV1u7411Z79s","difficulty":"简单","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":513,"name":"青椒炒荷包蛋","stuff":["鸡蛋"],"bv":"BV1wJ41167uH","difficulty":"","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":514,"name":"热拌莴笋叶","stuff":["莴笋"],"bv":"BV1xJ411M7bc","difficulty":"","tags":[],"methods":["拌"],"tools":["一口大锅"]},{"id":515,"name":"日式汉堡排(废手)","stuff":["面包","洋葱","猪肉","牛肉","鸡蛋"],"bv":"BV1UV411Y7HM","difficulty":"","tags":["日式"],"methods":["煎"],"tools":["一口大锅"]},{"id":516,"name":"肉炒莴苣","stuff":["莴笋","猪肉"],"bv":"BV14E411t7wh","difficulty":"","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":517,"name":"肉末土豆茄子煲","stuff":["茄子","土豆","猪肉"],"bv":"BV1Fu411S78s","difficulty":"普通","tags":[],"methods":["煲"],"tools":["一口大锅","砂锅"]},{"id":518,"name":"肉丸萝卜煲","stuff":["白萝卜","虾","猪肉"],"bv":"BV1gT4y1m7UK","difficulty":"普通","tags":[],"methods":["煲"],"tools":["一口大锅","砂锅"]},{"id":519,"name":"赛螃蟹","stuff":["鸡蛋"],"bv":"BV1ab4y1s7wk","difficulty":"","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":520,"name":"三杯鸡(无九层塔版)","stuff":["鸡肉"],"bv":"BV1dp4y1e73R","difficulty":"","tags":[],"methods":["烧"],"tools":["一口大锅"]},{"id":521,"name":"生煎白萝卜","stuff":["白萝卜"],"bv":"BV1Yu41127Fa","difficulty":"简单","tags":[],"methods":["煎"],"tools":["一口大锅"]},{"id":522,"name":"薯条","stuff":["土豆"],"bv":"BV1fY41187Xo","difficulty":"普通","tags":[],"methods":["炸"],"tools":["一口大锅"]},{"id":523,"name":"爽口腌莴笋","stuff":["莴笋"],"bv":"BV1Vv411V7SE","difficulty":"","tags":[],"methods":["腌"],"tools":["一口大锅"]},{"id":524,"name":"水波蛋","stuff":["鸡蛋"],"bv":"BV1TT4y127vu","difficulty":"简单","tags":[],"methods":["煮"],"tools":["一口大锅"]},{"id":525,"name":"水煮牛肉","stuff":["牛肉","菌菇","莴笋","豆腐"],"bv":"BV11q4y1z7dD","difficulty":"困难","tags":["辣","川菜"],"methods":["煮"],"tools":["一口大锅"]},{"id":526,"name":"苏格兰蛋","stuff":["鸡蛋","猪肉"],"bv":"BV1TT4y127vu","difficulty":"","tags":["西式"],"methods":["炸"],"tools":["一口大锅"]},{"id":527,"name":"素烧白萝卜","stuff":["白萝卜"],"bv":"BV1Z7411b7xo","difficulty":"简单","tags":[],"methods":["烧"],"tools":["一口大锅"]},{"id":528,"name":"蒜香蜂胸扒","stuff":["鸡肉"],"bv":"BV1h54y1Q7pf","difficulty":"","tags":[],"methods":["煎"],"tools":["一口大锅"]},{"id":529,"name":"蒜香鸡翅","stuff":["鸡肉"],"bv":"BV1sy4y1273W","difficulty":"普通","tags":[],"methods":["煎"],"tools":["一口大锅"]},{"id":530,"name":"蒜香鸡胸肉","stuff":["鸡肉"],"bv":"BV1tf4y1A7xR","difficulty":"","tags":[],"methods":["煎"],"tools":["一口大锅"]},{"id":531,"name":"蓑衣黄瓜","stuff":["黄瓜"],"bv":"BV1Wa411C7Gc","difficulty":"","tags":[],"methods":["拌"],"tools":["一口大锅"]},{"id":532,"name":"泰式卤蛋","stuff":["鸡蛋"],"bv":"BV1TT4y127vu","difficulty":"","tags":[],"methods":["卤"],"tools":["一口大锅"]},{"id":533,"name":"糖醋荷包蛋","stuff":["鸡蛋"],"bv":"BV1TT4y127vu","difficulty":"","tags":[],"methods":["煎"],"tools":["一口大锅"]},{"id":534,"name":"糖醋鸡胸肉","stuff":["鸡肉"],"bv":"BV1h7411C7PZ","difficulty":"","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":535,"name":"糖醋里脊","stuff":["猪肉"],"bv":"BV1Dt411B78h","difficulty":"普通","tags":[],"methods":["炸"],"tools":["一口大锅"]},{"id":536,"name":"糖醋茄子","stuff":["茄子","米"],"bv":"BV1TE411L7Gp","difficulty":"普通","tags":[],"methods":["烧"],"tools":["一口大锅"]},{"id":537,"name":"铁板豆腐","stuff":["豆腐"],"bv":"BV1dU4y1d7hz","difficulty":"简单","tags":[],"methods":["煎"],"tools":["一口大锅"]},{"id":538,"name":"土豆拌饭","stuff":["土豆","香肠","米"],"bv":"BV1YD4y1c7ip","difficulty":"简单","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":539,"name":"土豆炖茄子","stuff":["茄子","土豆"],"bv":"BV1uP4y1M7rt","difficulty":"普通","tags":[],"methods":["炖"],"tools":["一口大锅"]},{"id":540,"name":"土豆胡萝卜炖牛腩","stuff":["牛肉","胡萝卜","土豆"],"bv":"BV1R54y1E7JX","difficulty":"","tags":[],"methods":["炖"],"tools":["一口大锅"]},{"id":541,"name":"土豆腊肠煲饭","stuff":["腊肠","土豆","米"],"bv":"BV1wV411o7Sq","difficulty":"困难","tags":[],"methods":["煲"],"tools":["一口大锅"]},{"id":542,"name":"土豆泥厚蛋烧","stuff":["土豆","鸡蛋"],"bv":"BV1DL4y1j7Nu","difficulty":"困难","tags":[],"methods":["煎"],"tools":["一口大锅"]},{"id":543,"name":"土豆烧牛腩","stuff":["土豆","牛肉"],"bv":"BV1qs411u7gj","difficulty":"普通","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":544,"name":"吐司进阶吃法","stuff":["面包","鸡蛋","虾","包菜","香肠"],"bv":"BV1Tq4y1t7DC","difficulty":"简单","tags":[],"methods":[],"tools":["一口大锅"]},{"id":545,"name":"丸子面","stuff":["方便面","猪肉","牛肉"],"bv":"BV1s34y1X74a","difficulty":"","tags":[],"methods":["煮"],"tools":["一口大锅"]},{"id":546,"name":"莴笋菜饭","stuff":["莴笋"],"bv":"BV1wC4y1Y7iY","difficulty":"","tags":[],"methods":[],"tools":["一口大锅"]},{"id":547,"name":"莴笋炒虾仁","stuff":["莴笋","虾","胡萝卜"],"bv":"BV1ZT4y1g7Y2","difficulty":"简单","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":548,"name":"莴笋炒鸡丁","stuff":["莴笋","鸡肉"],"bv":"BV1hb411i7X2","difficulty":"普通","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":549,"name":"莴笋炒腊肉","stuff":["莴笋","香肠"],"bv":"BV1Ms411F7da","difficulty":"简单","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":550,"name":"莴笋胡萝卜酿肉","stuff":["莴笋","胡萝卜"],"bv":"BV1754y1y7eV","difficulty":"","tags":[],"methods":[],"tools":["一口大锅"]},{"id":551,"name":"莴笋三吃","stuff":["莴笋"],"bv":"BV1Bf4y1E7BR","difficulty":"","tags":[],"methods":[],"tools":["一口大锅"]},{"id":552,"name":"午餐肉蛋炒饭","stuff":["午餐肉","鸡蛋","米"],"bv":"BV1HE411X7xR","difficulty":"普通","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":553,"name":"午餐肉土豆锅","stuff":["午餐肉","土豆","胡萝卜"],"bv":"BV1gT4y1Q7R8","difficulty":"普通","tags":[],"methods":["煮"],"tools":["一口大锅"]},{"id":554,"name":"西班牙土豆鸡蛋饼","stuff":["鸡蛋","土豆","洋葱"],"bv":"BV1Vz4y1D7nF","difficulty":"","tags":[],"methods":["煎"],"tools":["一口大锅"]},{"id":555,"name":"西葫芦鸡蛋饼","stuff":["西葫芦","鸡蛋"],"bv":"BV1Mt411T7pF","difficulty":"普通","tags":[],"methods":["煎"],"tools":["一口大锅"]},{"id":556,"name":"西葫芦蒸蛋","stuff":["西葫芦","鸡蛋"],"bv":"BV1R94y1d7dd","difficulty":"普通","tags":[],"methods":["蒸"],"tools":["一口大锅"]},{"id":557,"name":"西兰花炒虾仁","stuff":["花菜","虾"],"bv":"BV1nv411B78i","difficulty":"简单","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":558,"name":"香菇白菜汤","stuff":["白菜","菌菇"],"bv":"BV1Cs411G7bw","difficulty":"普通","tags":["汤"],"methods":["煮"],"tools":["一口大锅"]},{"id":559,"name":"香煎鸡胸肉","stuff":["鸡肉"],"bv":"BV1W4411j71M","difficulty":"","tags":[],"methods":["煎"],"tools":["一口大锅"]},{"id":560,"name":"香辣鸡胸肉","stuff":["鸡肉"],"bv":"BV1M34y187r2","difficulty":"","tags":["辣"],"methods":["炒"],"tools":["一口大锅"]},{"id":561,"name":"小炒鸡胸肉","stuff":["鸡肉"],"bv":"BV1pF411a7nW","difficulty":"","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":562,"name":"小炒牛肉","stuff":["黄瓜","牛肉","米"],"bv":"BV1MK411T7NX","difficulty":"普通","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":563,"name":"雪碧拌面(要雪碧+老干妈)","stuff":["方便面","黄瓜"],"bv":"BV1az4y117d1","difficulty":"","tags":["辣"],"methods":["拌"],"tools":["一口大锅"]},{"id":564,"name":"阳春面","stuff":["面食"],"bv":"BV11E411C7e7","difficulty":"简单","tags":[],"methods":["煮"],"tools":["一口大锅"]},{"id":565,"name":"洋葱拌鸡丝","stuff":["洋葱","鸡肉"],"bv":"BV1Ki4y1P7tN","difficulty":"","tags":[],"methods":["拌"],"tools":["一口大锅"]},{"id":566,"name":"洋葱拌木耳","stuff":["洋葱","菌菇"],"bv":"BV1n5411H7Yo","difficulty":"","tags":[],"methods":["拌"],"tools":["一口大锅"]},{"id":567,"name":"洋葱炒鸡蛋","stuff":["洋葱","鸡蛋"],"bv":"BV1Dp4y1k7Mu","difficulty":"","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":568,"name":"洋葱炒肉","stuff":["洋葱","鸡肉"],"bv":"BV1QV41127ES","difficulty":"","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":569,"name":"洋葱炒土豆","stuff":["土豆","洋葱"],"bv":"BV1mg4y1b7z3","difficulty":"","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":570,"name":"洋葱虾仁滑蛋","stuff":["鸡蛋","牛肉","洋葱","虾"],"bv":"BV1uE411N7AN","difficulty":"","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":571,"name":"洋葱排骨","stuff":["洋葱","猪肉"],"bv":"BV1Q3411j7p6","difficulty":"","tags":[],"methods":["烧"],"tools":["一口大锅"]},{"id":572,"name":"洋葱烧鸡肉","stuff":["洋葱","鸡肉"],"bv":"BV1hW411y7Dp","difficulty":"","tags":[],"methods":["烧"],"tools":["一口大锅"]},{"id":573,"name":"一口酥豆腐","stuff":["豆腐"],"bv":"BV1dU4y1d7hz","difficulty":"普通","tags":[],"methods":["炸"],"tools":["一口大锅"]},{"id":574,"name":"一只鸡蛋糕","stuff":["鸡蛋"],"bv":"BV1X7411a7EQ","difficulty":"","tags":[],"methods":[],"tools":["一口大锅"]},{"id":575,"name":"英式炒蛋","stuff":["鸡蛋"],"bv":"BV1TT4y127vu","difficulty":"","tags":["西式"],"methods":["炒"],"tools":["一口大锅"]},{"id":576,"name":"油墩子/腰子饼","stuff":["白萝卜","芹菜","香肠","面食","鸡蛋"],"bv":"BV12b4y1i7DP","difficulty":"","tags":[],"methods":["炸"],"tools":["一口大锅"]},{"id":577,"name":"鱼香茄子","stuff":["茄子","鸡肉"],"bv":"BV1yL411E7oP","difficulty":"普通","tags":["辣","川菜"],"methods":["烧"],"tools":["一口大锅"]},{"id":578,"name":"杂粮蒸糕","stuff":["面食"],"bv":"BV1vf4y1T7gY","difficulty":"普通","tags":[],"methods":["蒸"],"tools":["一口大锅"]},{"id":579,"name":"炸蛋","stuff":["鸡蛋"],"bv":"BV1TT4y127vu","difficulty":"简单","tags":[],"methods":["炸"],"tools":["一口大锅"]},{"id":580,"name":"炸胡萝卜块","stuff":["胡萝卜","鸡蛋"],"bv":"BV11a411i7f4","difficulty":"普通","tags":[],"methods":["炸"],"tools":["一口大锅"]},{"id":581,"name":"炸胡萝卜丸","stuff":["胡萝卜","鸡蛋","香肠"],"bv":"BV12r4y1i7ud","difficulty":"普通","tags":[],"methods":["炸"],"tools":["一口大锅"]},{"id":582,"name":"炸茄盒","stuff":["茄子"],"bv":"BV1df4y177NB","difficulty":"普通","tags":[],"methods":["炸"],"tools":["一口大锅"]},{"id":583,"name":"炸薯卷","stuff":["土豆","香肠"],"bv":"BV1Na4y1a7LA","difficulty":"简单","tags":[],"methods":["炸"],"tools":["一口大锅"]},{"id":584,"name":"炸洋葱","stuff":["洋葱"],"bv":"BV1ib411V7H2","difficulty":"","tags":[],"methods":["炸"],"tools":["一口大锅"]},{"id":585,"name":"照烧鸡胸肉","stuff":["鸡肉"],"bv":"BV12Q4y1K7CA","difficulty":"","tags":["日式"],"methods":["煎"],"tools":["一口大锅"]},{"id":586,"name":"照烧牛肉","stuff":["牛肉"],"bv":"BV1mq4y1x7o3","difficulty":"普通","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":587,"name":"朝鲜冷面(方便面版)","stuff":["方便面"],"bv":"BV1s34y1X74a","difficulty":"","tags":[],"methods":["拌"],"tools":["一口大锅"]},{"id":588,"name":"蒸蛋羹","stuff":["鸡蛋"],"bv":"BV1Wt411Z7td","difficulty":"","tags":[],"methods":["蒸","煮"],"tools":["一口大锅"]},{"id":589,"name":"蒸蛋羹(硬核0失败版)","stuff":["鸡蛋"],"bv":"BV1x441117r4","difficulty":"","tags":[],"methods":["蒸","煮"],"tools":["一口大锅"]},{"id":590,"name":"蒸卤面","stuff":["面食"],"bv":"BV1sZ4y137QG","difficulty":"困难","tags":[],"methods":["蒸"],"tools":["一口大锅"]},{"id":591,"name":"蒸芹菜饼","stuff":["芹菜","鸡蛋"],"bv":"av809152626","difficulty":"普通","tags":[],"methods":["蒸"],"tools":["一口大锅"]},{"id":592,"name":"正确煮方便面","stuff":["方便面"],"bv":"BV1s34y1X74a","difficulty":"","tags":[],"methods":["煮"],"tools":["一口大锅"]},{"id":593,"name":"孜然土豆牛肉","stuff":["牛肉","土豆"],"bv":"BV1qX4y1T7ax","difficulty":"普通","tags":[],"methods":["炒"],"tools":["一口大锅"]},{"id":594,"name":"自制牛肉干","stuff":["牛肉"],"bv":"BV1Ht411d71Q","difficulty":"困难","tags":[],"methods":["烤"],"tools":["一口大锅"]},{"id":595,"name":"电饭煲番茄牛肉焖饭","stuff":["番茄","牛肉","米"],"bv":"BV1Bv411C7X3","difficulty":"普通","tags":[],"methods":["焖","煲"],"tools":["一口大锅","电饭煲"]},{"id":596,"name":"电饭煲排骨土豆焖饭","stuff":["猪肉","土豆","米"],"bv":"BV1Bv411C7X3","difficulty":"普通","tags":[],"methods":["焖","煲"],"tools":["一口大锅","电饭煲"]},{"id":597,"name":"米布丁","stuff":["米","鸡蛋"],"bv":"BV1hr4y1k7A5","difficulty":"简单","tags":[],"methods":["煲"],"tools":["一口大锅","电饭煲"]},{"id":598,"name":"麻婆豆腐","stuff":["豆腐"],"bv":"BV1it4y1X75m","difficulty":"简单","tags":["辣","川菜"],"methods":["烧"],"tools":["一口大锅"]},{"id":599,"name":"清蒸鲈鱼","stuff":["鱼"],"bv":"BV1QU4y1j7T4","difficulty":"普通","tags":[],"methods":["蒸"],"tools":["一口大锅"]},{"id":600,"name":"葱油鲈鱼","stuff":["鱼"],"bv":"BV1eQYe63EYi","difficulty":"简单","tags":[],"methods":["蒸"],"tools":["一口大锅"]},{"id":601,"name":"红烧鱼","stuff":["鱼"],"bv":"BV1vy421v76q","difficulty":"普通","tags":[],"methods":["烧"],"tools":["一口大锅"]},{"id":602,"name":"糖醋鱼","stuff":["鱼"],"bv":"BV1dE411L7uj","difficulty":"普通","tags":[],"methods":["炸","烧"],"tools":["一口大锅"]},{"id":603,"name":"酸菜鱼","stuff":["鱼"],"bv":"BV1mK411F7oM","difficulty":"困难","tags":["川菜"],"methods":["煮"],"tools":["一口大锅"]},{"id":604,"name":"巴沙鱼酸菜鱼","stuff":["鱼"],"bv":"BV1Fv411p7zg","difficulty":"普通","tags":[],"methods":["煮"],"tools":["一口大锅"]},{"id":605,"name":"番茄鱼","stuff":["鱼","番茄"],"bv":"BV1Km4y1P7W7","difficulty":"普通","tags":[],"methods":["煮"],"tools":["一口大锅"]},{"id":606,"name":"鲫鱼豆腐汤","stuff":["鱼","豆腐"],"bv":"BV1zV411m7Rf","difficulty":"普通","tags":["汤"],"methods":["炖"],"tools":["一口大锅"]},{"id":607,"name":"红烧带鱼","stuff":["鱼"],"bv":"BV1EW411t7Bg","difficulty":"普通","tags":[],"methods":["烧"],"tools":["一口大锅"]},{"id":608,"name":"剁椒鱼头","stuff":["鱼"],"bv":"BV1ZK4y1f7hZ","difficulty":"普通","tags":["湘菜","辣"],"methods":["蒸"],"tools":["一口大锅"]},{"id":609,"name":"水煮鱼","stuff":["鱼"],"bv":"BV16e4y1C7iB","difficulty":"困难","tags":["川菜","辣"],"methods":["煮"],"tools":["一口大锅"]},{"id":610,"name":"红烧鲟鱼","stuff":["鱼"],"bv":"BV1b4411j7aM","difficulty":"普通","tags":[],"methods":["烧"],"tools":["一口大锅"]},{"id":611,"name":"空气炸锅秋刀鱼","stuff":["鱼"],"bv":"BV1Cg411r7iQ","difficulty":"简单","tags":[],"methods":["烤"],"tools":["空气炸锅"]},{"id":612,"name":"空气炸锅金枪鱼球","stuff":["鱼","鸡蛋"],"bv":"BV12c3462E47","difficulty":"简单","tags":[],"methods":["烤"],"tools":["空气炸锅"]},{"id":613,"name":"微波炉蒸鱼","stuff":["鱼"],"bv":"BV1Mf4y1E7tr","difficulty":"简单","tags":["懒人"],"methods":["蒸"],"tools":["微波炉"]},{"id":614,"name":"微波炉三文鱼简餐","stuff":["鱼"],"bv":"BV18y4y1s7ze","difficulty":"简单","tags":["懒人"],"methods":["微波加热"],"tools":["微波炉"]},{"id":615,"name":"烤箱烤鱼","stuff":["鱼"],"bv":"BV1Db411k7gM","difficulty":"普通","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":616,"name":"锡纸烤黄鱼","stuff":["鱼"],"bv":"BV1Nf4y127a7","difficulty":"简单","tags":[],"methods":["烤"],"tools":["烤箱"]},{"id":617,"name":"广式清蒸鱼","stuff":["鱼"],"bv":"BV12m421V7Nh","difficulty":"简单","tags":["粤菜"],"methods":["蒸"],"tools":["一口大锅"]},{"id":618,"name":"香酥小黄鱼","stuff":["鱼"],"bv":"BV1it411Y7no","difficulty":"普通","tags":[],"methods":["炸"],"tools":["一口大锅"]},{"id":619,"name":"干煎带鱼","stuff":["鱼"],"bv":"BV1m441177A8","difficulty":"普通","tags":[],"methods":["煎"],"tools":["一口大锅"]},{"id":620,"name":"红烧鲫鱼","stuff":["鱼"],"bv":"BV1mY411V7sk","difficulty":"普通","tags":[],"methods":["烧"],"tools":["一口大锅"]},{"id":621,"name":"松鼠鳜鱼","stuff":["鱼"],"bv":"BV14W411p7vp","difficulty":"困难","tags":["苏菜"],"methods":["炸"],"tools":["一口大锅"]},{"id":622,"name":"青椒金汤鱼","stuff":["鱼"],"bv":"BV1EE411o7ta","difficulty":"普通","tags":["汤"],"methods":["煮"],"tools":["一口大锅"]},{"id":623,"name":"空气炸锅烤带鱼","stuff":["鱼"],"bv":"BV1xU4y127Z3","difficulty":"简单","tags":[],"methods":["烤"],"tools":["空气炸锅"]},{"id":624,"name":"奶白鱼汤","stuff":["鱼"],"bv":"BV1af4y1W7WL","difficulty":"普通","tags":["汤"],"methods":["煮"],"tools":["一口大锅"]},{"id":625,"name":"豆腐炖鱼","stuff":["鱼","豆腐"],"bv":"BV1kf4y147ek","difficulty":"普通","tags":[],"methods":["炖"],"tools":["一口大锅"]},{"id":626,"name":"鱼头豆腐汤","stuff":["鱼","豆腐"],"bv":"BV1Cs411N7RA","difficulty":"普通","tags":["汤"],"methods":["炖"],"tools":["一口大锅"]},{"id":627,"name":"红烧瓦块鱼","stuff":["鱼"],"bv":"BV1oX4y127ZA","difficulty":"普通","tags":[],"methods":["烧"],"tools":["一口大锅"]},{"id":628,"name":"红焖鱼头","stuff":["鱼"],"bv":"BV1Zp421D76M","difficulty":"普通","tags":[],"methods":["炖"],"tools":["一口大锅"]},{"id":629,"name":"青椒炒肉","stuff":["青椒","猪肉"],"bv":"","difficulty":"普通","tags":["家常菜"],"methods":["炒"],"tools":["一口大锅"]},{"id":630,"name":"玉米烙","stuff":["玉米"],"bv":"","difficulty":"简单","tags":["小吃"],"methods":["炸"],"tools":["一口大锅"]},{"id":631,"name":"韭菜盒子","stuff":["韭菜","面食"],"bv":"","difficulty":"普通","tags":["主食"],"methods":["煎"],"tools":["一口大锅"]},{"id":632,"name":"烤红薯","stuff":["红薯"],"bv":"","difficulty":"简单","tags":["单一食材"],"methods":["烤"],"tools":["烤箱"]},{"id":633,"name":"南瓜饼","stuff":["南瓜","面食"],"bv":"","difficulty":"普通","tags":["小吃"],"methods":["煎"],"tools":["一口大锅"]},{"id":634,"name":"玉米排骨汤","stuff":["玉米","骨头"],"bv":"","difficulty":"普通","tags":["汤"],"methods":["煲"],"tools":["一口大锅"]},{"id":635,"name":"清蒸大闸蟹","stuff":["蟹"],"bv":"","difficulty":"困难","tags":[],"methods":["蒸"],"tools":["一口大锅"]},{"id":636,"name":"韭菜炒虾仁","stuff":["韭菜","虾"],"bv":"","difficulty":"普通","tags":["家常菜"],"methods":["炒"],"tools":["一口大锅"]}] - - const PANTRY_OPTIONS = ["油","盐","糖","生抽","老抽","醋","料酒","蚝油","胡椒粉","淀粉","葱","姜","蒜"] - const PANTRY_DEFAULTS = ["油","盐"] - - const ALIASES = {"番茄":["西红柿"],"土豆":["土豆丝","洋芋","马铃薯"],"花菜":["西兰花"],"菌菇":["蘑菇","香菇","金针菇","口蘑"],"鸡肉":["鸡翅","鸡腿","鸡胸肉"],"猪肉":["排骨","五花肉","里脊"],"牛肉":["肥牛","牛腩"],"虾":["虾仁"],"鱼":["鱼片","鱼头","鲈鱼","鲫鱼","草鱼","鲤鱼","鲢鱼","带鱼","黄鱼","黄花鱼","巴沙鱼","龙利鱼","三文鱼","秋刀鱼","金枪鱼"],"白菜":["大白菜"],"包菜":["卷心菜"],"方便面":["泡面"],"面食":["挂面","面条"],"米":["米饭","大米"],"青椒":["青辣椒","柿子椒"],"玉米":["苞米"],"红薯":["地瓜","番薯"]} - - const INCOMPATIBLE = [{"foodA":"牛奶","foodB":"韭菜","reason":"牛奶与韭菜同食会影响钙的吸收,降低营养价值。"}] - - // ---------- 分组常量(与 packages/cook/src/utils/recommend.ts 保持一致) ---------- - const METHOD_GROUPS = [ - { label: '炒菜', methods: ['炒'] }, - { label: '汤羹', methods: [], tags: ['汤'] }, - { label: '炖煮', methods: ['炖', '煮', '煲', '卤', '焖', '烧', '灼'] }, - { label: '蒸', methods: ['蒸'] }, - { label: '炸煎', methods: ['炸', '煎', '烙'] }, - { label: '烤', methods: ['烤', '烘', '焗'] }, - { label: '凉拌', methods: ['拌', '凉拌', '腌', '腌制', '泡菜', '油泼'] }, - { label: '微波', methods: ['微波加热'] }, - { label: '火锅', methods: [], tags: ['杂烩'] }, - ] - - const CUISINE_GROUPS = [ - { label: '川菜', tags: ['川菜'] }, - { label: '湘菜', tags: ['湘菜'] }, - { label: '粤菜', tags: ['粤菜', '广式'] }, - { label: '苏菜', tags: ['苏菜'] }, - { label: '东北菜', tags: ['东北菜'] }, - { label: '西北菜', tags: ['西北特色'] }, - { label: '日式', tags: ['日式', '日式菜'] }, - { label: '意式', tags: ['意式'] }, - { label: '法式', tags: ['法式', '法餐'] }, - { label: '西式', tags: ['西式'] }, - { label: '国宴菜', tags: ['国宴菜'] }, - ] - - const DIFFICULTY_LEVELS = ['简单', '普通', '困难'] - const SPICY_TAG = '辣' - /** 大杂烩(火锅类)列的是「能放什么」而不是「必须有什么」 */ - const CATCH_ALL_MIN_STUFF = 10 - const CATCH_ALL_MIN_MATCH = 2 - - // ---------- 工具函数 ---------- - const EMOJI_MAP = (() => { - const map = {} - for (const group of ['vegetable', 'meat', 'staple']) { - for (const item of FOOD[group]) map[item.name] = item.emoji - } - return map - })() - - const TOOL_MAP = Object.fromEntries(FOOD.tools.map(t => [t.name, t])) - const ALL_FOODS = [...FOOD.vegetable, ...FOOD.meat, ...FOOD.staple] - const TOOL_LABEL = { '一口大锅': '炒锅' } - - /** 拆分记录里的分隔符并去重 */ - function uniqueStuff(stuff) { - return [...new Set((stuff || []).flatMap(s => String(s).split(/[,,、;;\n]/)).map(s => s.trim()).filter(Boolean))] - } - - function recipeMethods(recipe) { - return (recipe.methods || []).flatMap(m => String(m).split(/[、/]/)).filter(Boolean) - } - - function recipeEmoji(recipe) { - if ((recipe.tags || []).includes('杂烩')) return '🍲' - for (const s of recipe.stuff || []) { - if (EMOJI_MAP[s]) return EMOJI_MAP[s] - } - return '🍽️' - } - - function recipeCuisine(recipe) { - for (const group of CUISINE_GROUPS) { - if (group.tags.some(t => (recipe.tags || []).includes(t))) return group.label - } - return '' - } - - function recipeToolLabel(recipe) { - return (recipe.tools || []).map(t => TOOL_LABEL[t] || t).join(' / ') - } - - function matchesTaste(recipe, options) { - const { methodGroups = [], cuisines = [], noSpicy = false, difficulty = [] } = options - if (noSpicy && (recipe.tags || []).includes(SPICY_TAG)) return false - if (difficulty.length && !(recipe.difficulty && difficulty.includes(recipe.difficulty))) return false - if (cuisines.length) { - const tags = CUISINE_GROUPS.filter(g => cuisines.includes(g.label)).flatMap(g => g.tags) - if (!tags.some(t => (recipe.tags || []).includes(t))) return false - } - const groups = METHOD_GROUPS.filter(g => methodGroups.includes(g.label)) - if (!groups.length) return true - const methods = recipeMethods(recipe) - return groups.some(g => g.methods.some(m => methods.includes(m)) || (g.tags || []).some(t => (recipe.tags || []).includes(t))) - } - - function hasAvoidedStuff(recipe, avoid) { - if (!avoid.length) return false - const required = uniqueStuff(recipe.stuff) - if (required.length >= CATCH_ALL_MIN_STUFF) return false - const avoided = new Set(uniqueStuff(avoid)) - return required.some(item => avoided.has(item)) - } - - /** - * 复刻 packages/cook/src/utils/recommend.ts 的推荐算法: - * 缺料少的排前面,大杂烩排同组末尾 - */ - function findRecipes(options) { - const { stuff = [], pantry = [], tool = '', methodGroups = [], cuisines = [], difficulty = [], keyword = '', mode = 'loose', avoid = [] } = options - const selected = new Set(uniqueStuff(stuff)) - const pantrySet = new Set(uniqueStuff(pantry)) - const avoided = new Set(uniqueStuff(avoid)) - const kw = keyword.trim() - // 没选食材、只选了条件(厨具/做法/菜系/难度):当成「按条件翻找」,把符合条件的菜全列出来 - const browseOnly = !selected.size - && Boolean(tool || methodGroups.length || cuisines.length || difficulty.length) - if (!selected.size && !browseOnly) return [] - - const out = [] - RECIPES.forEach((recipe, sourceIndex) => { - const required = uniqueStuff(recipe.stuff) - if (tool && !(recipe.tools || []).includes(tool)) return - if (kw && !recipe.name.includes(kw)) return - const catchAll = required.length >= CATCH_ALL_MIN_STUFF - if (!catchAll && required.some(item => avoided.has(item))) return - if (!matchesTaste(recipe, { methodGroups, cuisines, noSpicy: options.noSpicy, difficulty })) return - - const matchedStuff = required.filter(i => selected.has(i)) - const pantryStuff = required.filter(i => !selected.has(i) && pantrySet.has(i)) - const unowned = required.filter(i => !selected.has(i) && !pantrySet.has(i)) - const missingStuff = catchAll ? [] : unowned - const optionalStuff = catchAll ? unowned.filter(i => !avoided.has(i)) : [] - - if (browseOnly) { - out.push({ recipe, sourceIndex, requiredStuff: required, matchedStuff: [], pantryStuff: [], missingStuff: required, optionalStuff: [] }) - return - } - - if (!matchedStuff.length || missingStuff.length > 2) return - if (catchAll && matchedStuff.length < CATCH_ALL_MIN_MATCH) return - if (mode === 'strict' && ![...selected].every(i => required.includes(i))) return - if (mode === 'survival' && missingStuff.length) return - - out.push({ recipe, sourceIndex, requiredStuff: required, matchedStuff, pantryStuff, missingStuff, optionalStuff }) - }) - - if (browseOnly) { - // 按条件翻找时,食材少的菜排前面 - return out.sort((a, b) => - a.requiredStuff.length - b.requiredStuff.length - || a.sourceIndex - b.sourceIndex, - ) - } - - return out.sort((a, b) => - a.missingStuff.length - b.missingStuff.length - || Number(a.requiredStuff.length >= CATCH_ALL_MIN_STUFF) - Number(b.requiredStuff.length >= CATCH_ALL_MIN_STUFF) - || b.matchedStuff.length - a.matchedStuff.length - || a.sourceIndex - b.sourceIndex, - ) - } - - function recipesByCuisine(label) { - const group = CUISINE_GROUPS.find(g => g.label === label) - if (!group) return [] - return RECIPES.filter(r => group.tags.some(t => (r.tags || []).includes(t))) - } - - function recipesByTool(tool) { - return RECIPES.filter(r => (r.tools || []).includes(tool)) - } - - function guessRecipe() { - const pool = RECIPES.filter(r => r.bv) - return pool[Math.floor(Math.random() * pool.length)] - } - - /** 统计每种食材能多做几道菜,用于「再补一样」提示 */ - function unlockSuggestions(stuff) { - const selected = new Set(uniqueStuff(stuff)) - const counts = {} - for (const recipe of RECIPES) { - const required = uniqueStuff(recipe.stuff) - if (required.length > 6) continue - for (const item of required) { - if (selected.has(item)) continue - const matched = required.filter(i => selected.has(i)).length - if (!matched) continue - counts[item] = (counts[item] || 0) + 1 - } - } - return Object.entries(counts) - .filter(([, count]) => count >= 2) - .sort((a, b) => b[1] - a[1]) - .slice(0, 4) - .map(([name, count]) => ({ name, count })) - } - - /** 搜索食材:标准名或别名都能命中 */ - function searchFoods(query) { - const text = query.trim() - if (!text) return [] - const aliasHit = Object.keys(ALIASES).find(std => ALIASES[std].some(a => a.includes(text))) - return ALL_FOODS.filter(item => - !selectedSet.has(item.name) - && (item.name.includes(text) || (ALIASES[item.name] || []).some(a => a.includes(text)) || (aliasHit && item.name === aliasHit)), - ).slice(0, 6) - } - - let selectedSet = new Set() - - function setSearchScope(selected) { - selectedSet = new Set(selected) - } - - /** 食物相克检测 */ - function checkIncompatible(stuff) { - const list = uniqueStuff(stuff) - const hits = INCOMPATIBLE.filter(pair => list.includes(pair.foodA) && list.includes(pair.foodB)) - if (!hits.length) return { hasWarning: false, message: '' } - const message = hits.map(h => `${h.foodA}和${h.foodB}一起吃不太好:${h.reason}`).join('\n') - return { hasWarning: true, message } - } - - const USER = { - displayName: '云乐坊用户', - email: 'cook@example.com', - avatarUrl: '', - syncedAt: '2026-10-21 09:24', - } - - window.Mock = { - FOOD, - RECIPES, - PANTRY_OPTIONS, - PANTRY_DEFAULTS, - ALIASES, - INCOMPATIBLE, - METHOD_GROUPS, - CUISINE_GROUPS, - DIFFICULTY_LEVELS, - SPICY_TAG, - uniqueStuff, - recipeMethods, - recipeEmoji, - recipeCuisine, - recipeToolLabel, - matchesTaste, - hasAvoidedStuff, - findRecipes, - recipesByCuisine, - recipesByTool, - guessRecipe, - unlockSuggestions, - searchFoods, - setSearchScope, - checkIncompatible, - EMOJI_MAP, - TOOL_MAP, - TOOL_LABEL, - ALL_FOODS, - USER, - } -})() diff --git a/prototype/cook-desktop/page-about.js b/prototype/cook-desktop/page-about.js deleted file mode 100644 index c2125de..0000000 --- a/prototype/cook-desktop/page-about.js +++ /dev/null @@ -1,11 +0,0 @@ -/** - * 关于页:项目与数据来源说明。 - */ -;(function () { - const M = window.Mock - - document.addEventListener('DOMContentLoaded', () => { - document.getElementById('about-total').textContent = String(M.RECIPES.length) - document.getElementById('about-count').textContent = `${M.RECIPES.length} 道菜` - }) -})() diff --git a/prototype/cook-desktop/page-cook.js b/prototype/cook-desktop/page-cook.js deleted file mode 100644 index 094aa31..0000000 --- a/prototype/cook-desktop/page-cook.js +++ /dev/null @@ -1,609 +0,0 @@ -/** - * 做菜页:左边选食材,右边常驻结果。 - * 所有数据都走 api.js 的桩函数,页面不直接读 mock。 - */ -;(function () { - const M = window.Mock - const Api = window.Api - const Store = window.Store - const App = window.App - - const $ = sel => document.querySelector(sel) - const $$ = sel => Array.from(document.querySelectorAll(sel)) - - const state = { - activeGroup: 0, - groupLocked: false, - loading: false, - error: '', - matches: [], - unlock: [], - } - - // ---------- 渲染:已选食材托盘 ---------- - function renderTray() { - const tray = $('#tray') - const hint = $('#tray-hint') - const selected = Store.state.selected - - if (!selected.length) { - tray.innerHTML = '还没选食材 —— 下面挑几样,或者直接搜名字' - hint.textContent = '' - hint.classList.remove('on') - return - } - - tray.innerHTML = selected.map((name) => { - const emoji = M.EMOJI_MAP[name] || '' - return `` - }).join('') - App.paintIcons(tray) - - hint.textContent = `已选 ${selected.length} 种` - hint.classList.add('on') - } - - // ---------- 渲染:食材分组 ---------- - function renderFoodChips() { - $$('[data-foods]').forEach((box) => { - const group = box.dataset.foods - box.innerHTML = M.FOOD[group].map((item) => { - const on = Store.hasStuff(item.name) - return `` - }).join('') - }) - - $$('[data-count]').forEach((el) => { - const group = el.dataset.count - const n = M.FOOD[group].filter(i => Store.hasStuff(i.name)).length - el.textContent = n ? `已选 ${n}` : '' - el.classList.toggle('on', n > 0) - }) - } - - // ---------- 渲染:搜索建议 ---------- - function renderSuggestions(list) { - const box = $('#search-suggest') - if (!list.length) { - box.innerHTML = '' - return - } - box.innerHTML = list.map((item) => ``).join('') - } - - // ---------- 渲染:厨具 ---------- - function renderTools() { - $('#tool-grid').innerHTML = M.FOOD.tools.map((tool) => { - const on = Store.state.tool === tool.name - const label = M.TOOL_LABEL[tool.name] || tool.label || tool.name - return `` - }).join('') - App.paintIcons($('#tool-grid')) - $('#tool-hint').textContent = Store.state.tool - ? `只看用${M.TOOL_LABEL[Store.state.tool] || Store.state.tool}做的菜` - : '只看用它做的菜' - } - - /** food.json 里的 icon 是 UnoCSS 类名,映射到内联 SVG 图标集 */ - function toolIcon(tool) { - const map = { - 'i-mdi-toaster-oven': 'oven', - 'i-fe-frying-pan': 'fryer', - 'i-ic-outline-microwave': 'microwave', - 'i-gg-smart-home-cooker': 'riceCooker', - 'i-mdi-pot-steam-outline': 'wok', - 'i-mdi-pot-mix': 'clayPot', - } - return map[tool.icon] || 'pot' - } - - // ---------- 渲染:筛选 ---------- - /** 记录上一轮的条件数,用来判断是不是刚加上的条件 */ - let lastFilterCount = 0 - - function renderFilterChips() { - $('#chips-method').innerHTML = M.METHOD_GROUPS.map((group) => { - const on = Store.state.methodGroups.includes(group.label) - return `` - }).join('') - - $('#chips-cuisine').innerHTML = M.CUISINE_GROUPS.map((group) => { - const on = Store.state.cuisines.includes(group.label) - return `` - }).join('') - - $('#chips-difficulty').innerHTML = M.DIFFICULTY_LEVELS.map((level) => { - const on = Store.state.difficulty.includes(level) - return `` - }).join('') - } - - function renderActiveFilters() { - const tags = [] - if (Store.state.tool) - tags.push({ key: 'tool', value: Store.state.tool, label: `厨具:${M.TOOL_LABEL[Store.state.tool] || Store.state.tool}` }) - Store.state.methodGroups.forEach(label => tags.push({ key: 'method', value: label, label: `做法:${label}` })) - Store.state.cuisines.forEach(label => tags.push({ key: 'cuisine', value: label, label: `菜系:${label}` })) - Store.state.difficulty.forEach(level => tags.push({ key: 'difficulty', value: level, label: `难度:${level}` })) - - $('#active-filters').innerHTML = tags.map(t => ``).join('') - App.paintIcons($('#active-filters')) - - const count = tags.length - const badge = $('#filter-count') - badge.textContent = String(count) - badge.hidden = count === 0 - $('#filter-title').textContent = count ? '筛选条件' : '筛选条件(做法 / 菜系 / 难度)' - - // 刚加上的条件要看得见,否则「按条件翻找」的结果没有来由 - if (count > 0 && lastFilterCount === 0) { - $('#filters').classList.add('is-open') - $('#filter-toggle').setAttribute('aria-expanded', 'true') - } - lastFilterCount = count - } - - // ---------- 渲染:调料与忌口 ---------- - function renderPantry() { - $('#chips-pantry').innerHTML = M.PANTRY_OPTIONS.map((name) => { - const on = Store.state.pantry.includes(name) - return `` - }).join('') - - const avoidPool = M.ALL_FOODS.slice(0, 12) - $('#chips-avoid').innerHTML = avoidPool.map((item) => { - const on = Store.state.avoid.includes(item.name) - return `` - }).join('') - - const spicy = $('#switch-spicy') - spicy.classList.toggle('is-on', Store.state.noSpicy) - spicy.setAttribute('aria-checked', String(Store.state.noSpicy)) - } - - /** 匹配范围下拉要跟着状态走(设置页改过也要回显) */ - function renderModeSelect() { - $('#mode-select').value = Store.state.mode - } - - /** 当前生效的条件,用来给结果区起标题 */ - function browseLabel() { - const parts = [] - if (Store.state.tool) - parts.push(`用${M.TOOL_LABEL[Store.state.tool] || Store.state.tool}`) - if (Store.state.cuisines.length) - parts.push(Store.state.cuisines.join('、')) - if (Store.state.methodGroups.length) - parts.push(Store.state.methodGroups.join('、')) - if (Store.state.difficulty.length) - parts.push(`${Store.state.difficulty.join('、')}难度`) - return parts.length ? parts.join(' · ') : '当前条件' - } - - // ---------- 渲染:结果 ---------- - function groupCounts() { - return [0, 1, 2].map(missing => state.matches.filter(m => m.missingStuff.length === missing).length) - } - - /** 写入结果区并把里面的图标画出来 */ - function setList(html) { - const list = $('#results-list') - list.innerHTML = html - App.paintIcons(list) - } - - function renderResults() { - const title = $('#results-title') - const sub = $('#results-sub') - const list = $('#results-list') - const count = $('#results-count') - const segment = $('#group-segment') - const hasSelection = Store.state.selected.length > 0 || Boolean(Store.state.tool) - // 没选食材、只选了条件:按条件翻找,缺料分组不适用 - const browseOnly = Store.state.selected.length === 0 - && Boolean(Store.state.tool || Store.state.methodGroups.length || Store.state.cuisines.length || Store.state.difficulty.length) - - // 没有选择时给一句引导 - if (!hasSelection) { - title.textContent = '选好食材,看看能做什么' - sub.textContent = '厨具可选,不选也能找菜谱' - count.textContent = '' - $('#unlock-box').innerHTML = '' - segment.hidden = false - setList(emptyHtml('bowl', '还没有开始选食材', '左边挑几样常见的,或者直接搜名字')) - setSegmentCounts([0, 0, 0], 0) - updateMobileBar('选好食材,看看能做什么', '厨具可选,不选也能找菜谱', '试试番茄+鸡蛋') - return - } - - const counts = groupCounts() - const total = state.matches.length - - title.textContent = total ? `可以做 ${total} 道菜` : '暂时没有匹配的菜' - sub.textContent = total - ? '缺料少的排在前面,点一道看做法' - : '换个食材,或放宽筛选条件' - count.textContent = total ? `${total} 道` : '' - - // 当前分组可能因为改筛选而变空,回退到第一个有内容的分组;手动点选的分组保持不动 - let active = state.activeGroup - if (!state.groupLocked && !counts[active]) { - const fallback = counts.findIndex(c => c > 0) - active = fallback === -1 ? 0 : fallback - state.activeGroup = active - } - setSegmentCounts(counts, active) - - // 加载中 - if (state.loading) { - setList(Array.from({ length: 6 }, () => ` -
-
-
-
-
-
-
`).join('')) - updateMobileBar('正在准备菜谱…', '稍等一下', '加载中') - return - } - - // 出错 - if (state.error) { - setList(`
-

菜谱没有加载成功

-

${App.escapeHtml(state.error)}

- -
`) - $('#btn-retry').addEventListener('click', () => refresh()) - updateMobileBar('菜谱没有加载成功', '点一下重试', '重试') - return - } - - // 按条件翻找:不分缺料组,整列平铺 - if (browseOnly) { - const label = browseLabel() - segment.hidden = true - $('#unlock-box').innerHTML = '' - title.textContent = total ? `${label} · ${total} 道菜` : `${label} · 暂时没有菜` - sub.textContent = total - ? '还没选食材 —— 点一道看看要准备什么' - : '换个条件,或直接选食材' - count.textContent = total ? `${total} 道` : '' - - if (!total) { - setList(emptyHtml('bowl', '这个条件还没有菜谱', '换个条件,或者直接选食材')) - updateMobileBar(`${label} · 暂时没有菜`, '换个条件看看', '换个条件') - return - } - - const rows = state.matches - const shown = rows.slice(0, 30) - setList(shown.map((match, i) => { - const recipe = match.recipe - const cuisine = M.recipeCuisine(recipe) - return `` - }).join('') + (rows.length > shown.length - ? `
还有 ${rows.length - shown.length} 道,去「菜谱」翻找
` - : '')) - updateMobileBar(`${label} · ${total} 道菜`, '点一道看看要准备什么', '查看菜谱') - return - } - segment.hidden = false - - // 「再补一样」提示 - $('#unlock-box').innerHTML = state.unlock.length - ? `
- 再补一样 -
- ${state.unlock.map(item => ``).join('')} -
-
` - : '' - - if (!total) { - setList(emptyHtml('bowl', '这一组暂时没有菜', '换个食材,或者清除筛选条件')) - updateMobileBar('暂时没有匹配的菜', '换个食材,或放宽筛选', '调整筛选') - return - } - - const rows = state.matches.filter(m => m.missingStuff.length === state.activeGroup) - const shown = rows.slice(0, 30) - - if (!shown.length) { - setList(emptyHtml('bowl', '这一组暂时是空的', '换一组看看,或者放宽筛选条件')) - updateMobileBar('这一组暂时是空的', '换一组看看', '换一组') - return - } - - setList(shown.map((match, i) => { - const recipe = match.recipe - const cuisine = M.recipeCuisine(recipe) - const have = match.matchedStuff - return `` - }).join('') + (rows.length > shown.length - ? `
还有 ${rows.length - shown.length} 道没显示,用「全部推荐」或换个分组看看
` - : '')) - - updateMobileBar( - total ? `可以做 ${total} 道菜` : '暂时没有匹配的菜', - Store.state.selected.length ? `已选 ${Store.state.selected.join('、')}` : '按厨具找菜', - '查看菜谱', - ) - } - - function setSegmentCounts(counts, active) { - const buttons = $$('#group-segment button') - buttons.forEach((btn, i) => { - btn.querySelector('.n').textContent = String(counts[i]) - btn.classList.toggle('is-on', i === active) - }) - } - - function updateMobileBar(title, sub, action) { - $('#mobile-title').textContent = title - $('#mobile-sub').textContent = sub - $('#mobile-action').textContent = action - } - - function emptyHtml(iconName, heading, text) { - return `
- -

${App.escapeHtml(heading)}

-

${App.escapeHtml(text)}

-
` - } - - // ---------- 数据刷新 ---------- - let refreshToken = 0 - - async function refresh() { - const token = ++refreshToken - const hasSelection = Store.state.selected.length > 0 || Boolean(Store.state.tool) - - if (!hasSelection) { - state.matches = [] - state.unlock = [] - state.loading = false - state.error = '' - renderResults() - return - } - - state.loading = true - state.error = '' - renderResults() - - try { - const query = { - stuff: Store.state.selected, - pantry: Store.state.pantry, - tool: Store.state.tool, - methodGroups: Store.state.methodGroups, - cuisines: Store.state.cuisines, - difficulty: Store.state.difficulty, - noSpicy: Store.state.noSpicy, - avoid: Store.state.avoid, - keyword: Store.state.keyword, - mode: Store.state.mode, - } - const [res, unlock] = await Promise.all([ - Api.searchRecipes(query), - Api.getUnlockSuggestions(Store.state.selected), - ]) - if (token !== refreshToken) - return - state.matches = res.data.list - state.unlock = unlock.data - } - catch (error) { - if (token !== refreshToken) - return - state.matches = [] - state.error = error && error.message ? error.message : '请检查网络后重试' - } - finally { - if (token === refreshToken) { - state.loading = false - renderResults() - } - } - } - - // ---------- 事件 ---------- - function bind() { - // 食材相关 - document.addEventListener('click', (e) => { - const add = e.target.closest('[data-add]') - if (add) { - Store.addStuff(add.dataset.add) - // 加完就把搜索框和候选清掉,避免留一条已经选中的候选 - $('#stuff-search').value = '' - renderSuggestions([]) - return - } - const remove = e.target.closest('[data-remove]') - if (remove) { - Store.removeStuff(remove.dataset.remove) - return - } - const stuff = e.target.closest('[data-stuff]') - if (stuff) { - Store.toggleStuff(stuff.dataset.stuff) - return - } - const tool = e.target.closest('[data-tool]') - if (tool) { - Store.toggleTool(tool.dataset.tool) - return - } - const method = e.target.closest('[data-method]') - if (method) { - Store.toggleMethodGroup(method.dataset.method) - return - } - const cuisine = e.target.closest('[data-cuisine]') - if (cuisine) { - Store.toggleCuisine(cuisine.dataset.cuisine) - return - } - const difficulty = e.target.closest('[data-difficulty]') - if (difficulty) { - Store.toggleDifficulty(difficulty.dataset.difficulty) - return - } - const pantry = e.target.closest('[data-pantry]') - if (pantry) { - Store.togglePantry(pantry.dataset.pantry) - return - } - const avoid = e.target.closest('[data-avoid]') - if (avoid) { - Store.toggleAvoid(avoid.dataset.avoid) - return - } - const clear = e.target.closest('[data-clear]') - if (clear) { - const key = clear.dataset.clear - const value = clear.dataset.value - if (key === 'tool') - Store.toggleTool(value) - if (key === 'method') - Store.toggleMethodGroup(value) - if (key === 'cuisine') - Store.toggleCuisine(value) - if (key === 'difficulty') - Store.toggleDifficulty(value) - return - } - const dish = e.target.closest('[data-dish]') - if (dish) { - App.openRecipe(dish.dataset.dish) - return - } - }) - - // 搜索 - let searchTimer = 0 - $('#stuff-search').addEventListener('input', (e) => { - const text = e.target.value - clearTimeout(searchTimer) - searchTimer = setTimeout(async () => { - if (!text.trim()) { - renderSuggestions([]) - return - } - const res = await Api.searchFoods(text) - renderSuggestions(res.data) - }, 140) - }) - - // 筛选折叠 - $('#filter-toggle').addEventListener('click', () => { - const box = $('#filters') - const open = box.classList.toggle('is-open') - $('#filter-toggle').setAttribute('aria-expanded', String(open)) - }) - - // 清空 - $('#btn-clear').addEventListener('click', () => { - Store.resetAll() - $('#stuff-search').value = '' - renderSuggestions([]) - App.toast('已清空') - }) - - // 不吃辣 - $('#switch-spicy').addEventListener('click', () => { - Store.setNoSpicy(!Store.state.noSpicy) - }) - - // 匹配范围 - $('#mode-select').addEventListener('change', (e) => { - Store.setMode(e.target.value) - }) - - // 结果分组 - $('#group-segment').addEventListener('click', (e) => { - const btn = e.target.closest('[data-group]') - if (!btn) - return - state.activeGroup = Number(btn.dataset.group) - state.groupLocked = true - renderResults() - }) - - // 移动端概览条 - $('#mobile-action').addEventListener('click', () => { - if (!Store.state.selected.length && !Store.state.tool) { - Store.addStuff('番茄') - Store.addStuff('鸡蛋') - return - } - const panel = $('#results-panel') - panel.scrollIntoView({ behavior: 'smooth', block: 'start' }) - }) - - // 状态变化 → 重画 - document.addEventListener('store:change', () => { - renderTray() - renderFoodChips() - renderTools() - renderFilterChips() - renderActiveFilters() - renderPantry() - renderModeSelect() - refresh() - }) - } - - // ---------- 启动 ---------- - document.addEventListener('DOMContentLoaded', () => { - renderTray() - renderFoodChips() - renderTools() - renderFilterChips() - renderActiveFilters() - renderPantry() - renderModeSelect() - renderResults() - bind() - - // 同步一次外部可能已经改过的状态 - refresh() - }) -})() diff --git a/prototype/cook-desktop/page-discover.js b/prototype/cook-desktop/page-discover.js deleted file mode 100644 index 333a54e..0000000 --- a/prototype/cook-desktop/page-discover.js +++ /dev/null @@ -1,189 +0,0 @@ -/** - * 发现页:随机推荐 + 菜系 / 厨具入口 + 热门菜谱 + 一日三餐。 - * 数据全部来自 api.js 的桩函数。 - */ -;(function () { - const M = window.Mock - const Api = window.Api - const Store = window.Store - const App = window.App - - const $ = sel => document.querySelector(sel) - const $$ = sel => Array.from(document.querySelectorAll(sel)) - - const state = { - random: null, - hot: [], - cuisines: [], - } - - const MEALS = [ - { - label: '早餐', - icon: 'sun', - stuff: ['鸡蛋', '面食', '米'], - note: '十分钟内能端上桌的', - }, - { - label: '午餐', - icon: 'utensils', - stuff: ['米', '猪肉', '包菜'], - note: '有菜有肉有主食', - }, - { - label: '晚餐', - icon: 'moon', - stuff: ['鸡肉', '土豆', '番茄'], - note: '汤汤水水收尾', - }, - { - label: '夜宵', - icon: 'flame', - stuff: ['方便面', '午餐肉', '鸡蛋'], - note: '深夜食堂专用', - }, - ] - - // ---------- 随机推荐 ---------- - function renderRandom() { - const recipe = state.random - if (!recipe) - return - const cuisine = M.recipeCuisine(recipe) - const stuff = M.uniqueStuff(recipe.stuff) - const brief = [cuisine, M.recipeToolLabel(recipe), recipe.difficulty].filter(Boolean).join(' · ') - $('#random-name').textContent = recipe.name - $('#random-meta').textContent = brief ? `${brief} 需要:${stuff.join('、')}` : `需要:${stuff.join('、')}` - $('#random-card').dataset.id = String(recipe.id) - $('#btn-random-open').dataset.id = String(recipe.id) - } - - async function loadRandom() { - const res = await Api.getRandomRecipe() - state.random = res.data - renderRandom() - } - - // ---------- 菜系 ---------- - function renderCuisines() { - $('#cuisine-grid').innerHTML = state.cuisines.map((item) => ` - `).join('') - $('#cuisine-count').textContent = `${state.cuisines.length} 个菜系` - } - - // ---------- 厨具 ---------- - function renderTools() { - $('#discover-tools').innerHTML = M.FOOD.tools.map((tool) => { - const label = M.TOOL_LABEL[tool.name] || tool.label || tool.name - const count = M.recipesByTool(tool.name).length - return `` - }).join('') - App.paintIcons($('#discover-tools')) - } - - function toolIcon(tool) { - const map = { - 'i-mdi-toaster-oven': 'oven', - 'i-fe-frying-pan': 'fryer', - 'i-ic-outline-microwave': 'microwave', - 'i-gg-smart-home-cooker': 'riceCooker', - 'i-mdi-pot-steam-outline': 'wok', - 'i-mdi-pot-mix': 'clayPot', - } - return map[tool.icon] || 'pot' - } - - // ---------- 热门 ---------- - function renderHot() { - $('#hot-list').innerHTML = state.hot.map(recipe => ` - `).join('') - App.paintIcons($('#hot-list')) - } - - // ---------- 一日三餐 ---------- - function renderMeals() { - $('#meal-grid').innerHTML = MEALS.map((meal) => ` -
-
- - ${meal.label} -
-

${meal.note}

-
- ${meal.stuff.map(s => ``).join('')} -
-
`).join('') - App.paintIcons($('#meal-grid')) - } - - // ---------- 事件 ---------- - function bind() { - // 随机卡片 - $('#btn-random-open').addEventListener('click', (e) => { - const id = e.currentTarget.dataset.id - if (id) - App.openRecipe(id) - }) - $('#btn-random-again').addEventListener('click', () => loadRandom()) - $('#btn-shuffle').addEventListener('click', () => loadRandom()) - - // 跳到做菜页并带上条件 - document.addEventListener('click', (e) => { - const cuisine = e.target.closest('[data-cuisine]') - if (cuisine) { - Store.state.cuisines = [cuisine.dataset.cuisine] - Store.emit() - location.href = 'index.html' - return - } - const tool = e.target.closest('[data-tool]') - if (tool) { - Store.state.tool = tool.dataset.tool - Store.emit() - location.href = 'index.html' - return - } - const meal = e.target.closest('[data-meal]') - if (meal) { - Store.state.selected = [...Store.state.selected, meal.dataset.meal] - Store.emit() - location.href = 'index.html' - return - } - const dish = e.target.closest('[data-dish]') - if (dish) - App.openRecipe(dish.dataset.dish) - }) - } - - // ---------- 启动 ---------- - document.addEventListener('DOMContentLoaded', () => { - bind() - renderTools() - renderMeals() - - loadRandom() - Api.listCuisines().then((res) => { - state.cuisines = res.data.filter(c => c.count > 0) - renderCuisines() - }) - Api.listHotRecipes().then((res) => { - state.hot = res.data - renderHot() - }) - }) -})() diff --git a/prototype/cook-desktop/page-help.js b/prototype/cook-desktop/page-help.js deleted file mode 100644 index 66b4c28..0000000 --- a/prototype/cook-desktop/page-help.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - * 帮助页:FAQ 折叠 + 反馈入口。 - */ -;(function () { - const Api = window.Api - const App = window.App - - const $ = sel => document.querySelector(sel) - - function bind() { - document.addEventListener('click', (e) => { - const q = e.target.closest('.faq-q') - if (q) { - const item = q.closest('.faq-item') - const open = item.classList.contains('is-open') - item.classList.toggle('is-open', !open) - q.setAttribute('aria-expanded', String(!open)) - return - } - }) - - $('#btn-help-feedback').addEventListener('click', async () => { - const note = window.prompt('说说你遇到的问题(原型不会真的发出去)', '') - if (note === null || !note.trim()) - return - await Api.submitFeedback({ recipeName: '', kind: 'general', note }) - App.toast('收到,谢谢反馈') - }) - } - - document.addEventListener('DOMContentLoaded', bind) -})() diff --git a/prototype/cook-desktop/page-library.js b/prototype/cook-desktop/page-library.js deleted file mode 100644 index 4d691b5..0000000 --- a/prototype/cook-desktop/page-library.js +++ /dev/null @@ -1,281 +0,0 @@ -/** - * 菜谱库:桌面看表格,手机看列表。 - * 搜索、分页、视图切换都走 api.js 的桩函数。 - */ -;(function () { - const M = window.Mock - const Api = window.Api - const Store = window.Store - const App = window.App - - const $ = sel => document.querySelector(sel) - const $$ = sel => Array.from(document.querySelectorAll(sel)) - - const PAGE_SIZE = 40 - - const state = { - view: 'all', - keyword: '', - list: [], - shown: PAGE_SIZE, - loading: false, - error: '', - } - - // ---------- 渲染:表格 ---------- - function renderTable() { - const tbody = $('#lib-tbody') - const rows = state.list.slice(0, state.shown) - tbody.innerHTML = rows.map(recipe => { - const cuisine = M.recipeCuisine(recipe) - const methods = M.recipeMethods(recipe) - const stuff = M.uniqueStuff(recipe.stuff) - const favorited = Store.isFavorite(recipe.id) - return ` - -
- - ${App.escapeHtml(recipe.name)} -
- - ${App.escapeHtml(stuff.join('、'))} - ${App.escapeHtml(M.recipeToolLabel(recipe) || '—')} - ${App.escapeHtml(methods.join('、') || '—')} - - ${App.escapeHtml(cuisine || recipe.difficulty || '—')} - - - - - ` - }).join('') - App.paintIcons(tbody) - } - - // ---------- 渲染:移动列表 ---------- - function renderList() { - const list = $('#lib-list') - const rows = state.list.slice(0, state.shown) - list.innerHTML = rows.map(recipe => { - const cuisine = M.recipeCuisine(recipe) - const favorited = Store.isFavorite(recipe.id) - return `
- - -
` - }).join('') - App.paintIcons(list) - } - - // ---------- 渲染:加载更多 ---------- - function renderMore() { - const box = $('#lib-more') - const rest = state.list.length - state.shown - if (rest <= 0) { - box.innerHTML = '' - return - } - box.innerHTML = `
- -
` - } - - function renderEmpty() { - const tips = { - all: '换个关键词试试', - fav: '在菜谱详情里点星标就会收进来', - history: '去 B 站看过的菜会记在这里', - mine: '点右上角「新建」记一条自己的菜谱', - } - const iconName = state.view === 'fav' ? 'star' : state.view === 'history' ? 'history' : 'book' - const html = `
- -

${state.view === 'all' ? '没有匹配的菜谱' : '这里还是空的'}

-

${tips[state.view]}

-
` - $('#lib-tbody').innerHTML = `${html}` - $('#lib-list').innerHTML = html - $('#lib-more').innerHTML = '' - App.paintIcons(document.querySelector('#lib-tbody')) - App.paintIcons($('#lib-list')) - } - - function renderAll() { - $('#lib-total').textContent = String(state.list.length) - const viewLabel = { all: '全部菜谱', fav: '我的收藏', history: '浏览历史', mine: '我的菜谱' } - $('#lib-sub').textContent = state.view === 'all' - ? `按菜名、食材或厨具翻找所有菜谱,共 ${state.list.length} 道` - : `${viewLabel[state.view]} · ${state.list.length} 道` - if (!state.list.length) { - renderEmpty() - return - } - renderTable() - renderList() - renderMore() - } - - // ---------- 数据 ---------- - let token = 0 - async function load() { - const current = ++token - state.loading = true - state.error = '' - try { - const res = await Api.listRecipes({ keyword: state.keyword, view: state.view }) - if (current !== token) - return - state.list = res.data.list - state.shown = PAGE_SIZE - } - catch (error) { - if (current !== token) - return - state.list = [] - state.error = error && error.message ? error.message : '加载失败,请重试' - } - finally { - if (current === token) { - state.loading = false - renderAll() - } - } - } - - // ---------- 事件 ---------- - function bind() { - // 视图切换 - $('#lib-segment').addEventListener('click', (e) => { - const btn = e.target.closest('[data-view]') - if (!btn) - return - state.view = btn.dataset.view - $$('#lib-segment button').forEach(b => b.classList.toggle('is-on', b === btn)) - load() - }) - - // 搜索 - let timer = 0 - $('#lib-search').addEventListener('input', (e) => { - state.keyword = e.target.value - clearTimeout(timer) - timer = setTimeout(load, 200) - }) - - // 表格 / 列表视图(桌面端) - $('#btn-view').addEventListener('click', () => { - const root = document.documentElement - const toList = root.dataset.libView !== 'list' - if (toList) - root.dataset.libView = 'list' - else - delete root.dataset.libView - App.toast(toList ? '已切换为列表视图' : '已切换为表格视图') - }) - - // 加载更多 - document.addEventListener('click', (e) => { - if (e.target.closest('#btn-load-more')) { - state.shown += PAGE_SIZE - renderTable() - renderList() - renderMore() - } - }) - - // 收藏 - document.addEventListener('click', (e) => { - const fav = e.target.closest('[data-fav]') - if (fav) { - e.stopPropagation() - const id = Number(fav.dataset.fav) - const recipe = M.RECIPES.find(r => r.id === id) || Store.state.customRecipes.find(r => r.id === id) - if (!recipe) - return - const on = Store.toggleFavorite(recipe) - App.toast(on ? '已添加到收藏' : '已从收藏移除') - if (state.view === 'fav' && !on) - load() - else - renderAll() - } - }) - - // 打开详情 - document.addEventListener('click', (e) => { - const dish = e.target.closest('[data-dish]') - if (dish && !e.target.closest('[data-fav]')) - App.openRecipe(dish.dataset.dish) - }) - - // 新建菜谱 - $('#btn-new').addEventListener('click', () => { - const panel = $('#new-panel') - panel.hidden = !panel.hidden - if (!panel.hidden) - $('#new-name').focus() - }) - $('#new-cancel').addEventListener('click', () => { - $('#new-panel').hidden = true - }) - $('#new-form').addEventListener('submit', (e) => { - e.preventDefault() - const name = $('#new-name').value.trim() - if (!name) { - App.toast('给这道菜起个名字') - return - } - const stuff = $('#new-stuff').value.split(/[,,、;;\n]/).map(s => s.trim()).filter(Boolean) - const method = $('#new-method').value.trim() - Store.addCustomRecipe({ - // 用 900000+ 的数字 id,避开内置菜谱的 0~636 - id: 900000 + (Number(String(Date.now()).slice(-4)) % 10000), - name, - stuff, - methods: method ? [method] : [], - tags: [], - tools: [], - difficulty: '', - bv: '', - custom: true, - }) - $('#new-form').reset() - $('#new-panel').hidden = true - App.toast('已保存到「我的菜谱」') - state.view = 'mine' - $$('#lib-segment button').forEach(b => b.classList.toggle('is-on', b.dataset.view === 'mine')) - load() - }) - - // 收藏变化后同步 - document.addEventListener('favorite:change', () => { - if (state.view === 'fav') - load() - else - renderAll() - }) - } - - // ---------- 启动 ---------- - document.addEventListener('DOMContentLoaded', () => { - // 从「我的」页跳过来时可以带上视图 - const wanted = new URLSearchParams(location.search).get('view') - if (wanted && ['all', 'fav', 'history', 'mine'].includes(wanted)) { - state.view = wanted - $$('#lib-segment button').forEach(b => b.classList.toggle('is-on', b.dataset.view === wanted)) - } - - bind() - load() - }) -})() diff --git a/prototype/cook-desktop/page-me.js b/prototype/cook-desktop/page-me.js deleted file mode 100644 index 8e979c7..0000000 --- a/prototype/cook-desktop/page-me.js +++ /dev/null @@ -1,172 +0,0 @@ -/** - * 我的页:账户、统计、偏好与跳转入口。 - * 登录 / 同步都走 api.js 的桩函数,状态写在 state.js。 - */ -;(function () { - const M = window.Mock - const Api = window.Api - const Store = window.Store - const App = window.App - - const $ = sel => document.querySelector(sel) - const $$ = sel => Array.from(document.querySelectorAll(sel)) - - const state = { - profile: null, - prefPanel: '', - } - - // ---------- 渲染 ---------- - function renderStats() { - const favCount = Store.state.favorites.size - const historyCount = Store.state.history.length - const mineCount = Store.state.customRecipes.length - - $('#stat-fav').textContent = String(favCount) - $('#stat-history').textContent = String(historyCount) - $('#stat-mine').textContent = String(mineCount) - $('#menu-fav').textContent = `${favCount} 道` - $('#menu-history').textContent = `${historyCount} 道` - $('#menu-mine').textContent = `${mineCount} 道` - - $('#value-pantry').textContent = Store.state.pantry.length - ? Store.state.pantry.join('、') - : '无' - $('#value-avoid').textContent = Store.state.avoid.length - ? Store.state.avoid.join('、') - : '无' - $('#value-nospicy').textContent = Store.state.noSpicy ? '开' : '关' - } - - function renderAccount() { - const signedIn = Store.state.signedIn - const profile = state.profile - $('#me-name').textContent = signedIn && profile ? profile.displayName : '未登录' - $('#me-email').textContent = signedIn && profile - ? profile.email - : '登录后可以同步收藏和历史' - $('#signin-label').textContent = signedIn ? '退出' : '登录' - // 图标要跟着切换,已绘制过的节点只改 data-icon 不会重绘 - $('#btn-signin').querySelector('[data-icon]').innerHTML = window.Icons.icon(signedIn ? 'logOut' : 'logIn') - $('#stat-synced').textContent = signedIn && profile ? profile.syncedAt : '—' - } - - // ---------- 偏好面板 ---------- - function openPref(kind) { - state.prefPanel = kind - const panel = $('#pref-panel') - const body = $('#pref-body') - panel.hidden = false - - if (kind === 'pantry') { - $('#pref-title').textContent = '常备调料' - body.innerHTML = `
- ${M.PANTRY_OPTIONS.map(name => ``).join('')} -
-

勾上的调料在推荐时算作「家里已有」。

` - } - else if (kind === 'avoid') { - $('#pref-title').textContent = '忌口' - body.innerHTML = `
- ${M.ALL_FOODS.map(item => ``).join('')} -
-

含忌口食材的菜谱不会出现在推荐里。

` - } - else { - $('#pref-title').textContent = '不吃辣' - body.innerHTML = `
- 推荐时排除带「辣」的菜 - -
` - $('#pref-spicy').addEventListener('click', () => { - Store.setNoSpicy(!Store.state.noSpicy) - }) - } - } - - function closePref() { - state.prefPanel = '' - $('#pref-panel').hidden = true - } - - // ---------- 事件 ---------- - function bind() { - $('#btn-signin').addEventListener('click', async () => { - if (Store.state.signedIn) { - Store.setSignedIn(false) - App.toast('已退出登录') - return - } - const account = window.prompt('输入一个账号名(原型不做真实鉴权)', 'cook@example.com') - if (account === null) - return - const res = await Api.signIn(account) - if (res.code !== 0) { - App.toast(res.message || '登录失败') - return - } - Store.setSignedIn(true) - App.toast(`欢迎回来,${res.data.displayName}`) - }) - - $('#btn-sync').addEventListener('click', async () => { - if (!Store.state.signedIn) { - App.toast('先登录再同步') - return - } - const btn = $('#btn-sync') - btn.disabled = true - const res = await Api.syncUserData() - btn.disabled = false - if (state.profile) - state.profile.syncedAt = res.data.syncedAt - renderAccount() - App.toast(`已同步 · ${res.data.syncedAt}`) - }) - - $('#menu-pantry').addEventListener('click', () => openPref('pantry')) - $('#menu-avoid').addEventListener('click', () => openPref('avoid')) - $('#menu-nospicy').addEventListener('click', () => openPref('spicy')) - $('#pref-close').addEventListener('click', closePref) - - // 偏好面板里的开关 - document.addEventListener('click', (e) => { - const pantry = e.target.closest('[data-pantry]') - if (pantry) { - Store.togglePantry(pantry.dataset.pantry) - return - } - const avoid = e.target.closest('[data-avoid]') - if (avoid) { - Store.toggleAvoid(avoid.dataset.avoid) - return - } - }) - - $('#btn-feedback').addEventListener('click', async () => { - const note = window.prompt('说说你遇到的问题(原型不会真的发出去)', '') - if (note === null || !note.trim()) - return - await Api.submitFeedback({ recipeName: '', kind: 'general', note }) - App.toast('收到,谢谢反馈') - }) - - document.addEventListener('store:change', () => { - renderStats() - renderAccount() - if (state.prefPanel) - openPref(state.prefPanel) - }) - } - - // ---------- 启动 ---------- - document.addEventListener('DOMContentLoaded', () => { - bind() - renderStats() - renderAccount() - Api.getProfile().then((res) => { - state.profile = res.data - renderAccount() - }) - }) -})() diff --git a/prototype/cook-desktop/page-recipe.js b/prototype/cook-desktop/page-recipe.js deleted file mode 100644 index 8360343..0000000 --- a/prototype/cook-desktop/page-recipe.js +++ /dev/null @@ -1,213 +0,0 @@ -/** - * 菜谱详情页(独立页面版)。 - * 从菜谱库、发现页或做菜页带 ?id= 跳过来。 - */ -;(function () { - const M = window.Mock - const Api = window.Api - const Store = window.Store - const App = window.App - - const $ = sel => document.querySelector(sel) - const $$ = sel => Array.from(document.querySelectorAll(sel)) - - const state = { - recipe: null, - related: [], - } - - function currentId() { - return Number(new URLSearchParams(location.search).get('id')) || 0 - } - - // ---------- 渲染 ---------- - function renderHead() { - const r = state.recipe - const cuisine = M.recipeCuisine(r) - const tool = M.recipeToolLabel(r) - $('#rp-thumb').textContent = M.recipeEmoji(r) - $('#rp-title').textContent = r.name - $('#rp-name').textContent = r.name - $('#rp-sub').textContent = [cuisine, tool, r.difficulty].filter(Boolean).join(' · ') - $('#rp-meta').textContent = [cuisine, tool, r.difficulty].filter(Boolean).join(' · ') || '家常做法' - } - - function renderFacts() { - const r = state.recipe - const stuff = M.uniqueStuff(r.stuff) - const methods = M.recipeMethods(r) - $('#rp-facts').innerHTML = [ - { label: '食材', value: `${stuff.length} 种` }, - { label: '厨具', value: M.recipeToolLabel(r) || '不挑锅' }, - { label: '做法', value: methods.join('、') || '未记录' }, - { label: '难度', value: r.difficulty || '未标注' }, - ].map(f => `
${f.label}${App.escapeHtml(f.value)}
`).join('') - } - - function renderStuff() { - const r = state.recipe - const required = M.uniqueStuff(r.stuff) - const selected = new Set(Store.state.selected) - const pantry = new Set(Store.state.pantry) - - const have = required.filter(s => selected.has(s)) - const fromPantry = required.filter(s => !selected.has(s) && pantry.has(s)) - const miss = required.filter(s => !selected.has(s) && !pantry.has(s)) - - const groups = [] - if (have.length) - groups.push({ label: '你已有的', items: have, tone: 'have' }) - if (fromPantry.length) - groups.push({ label: '常备调料', items: fromPantry, tone: 'pantry' }) - if (miss.length) - groups.push({ label: '还要买', items: miss, tone: 'missing' }) - if (!groups.length) - groups.push({ label: '食材', items: required, tone: 'neutral' }) - - $('#rp-stuff-hint').textContent = have.length || fromPantry.length - ? `还差 ${miss.length} 种` - : `共 ${required.length} 种` - - $('#rp-stuff').innerHTML = groups.map(g => ` -
-

${g.label} · ${g.items.length} 种

-
- ${g.items.map(i => ``).join('')} -
-
`).join('') - } - - function renderTags() { - const r = state.recipe - const tags = (r.tags || []).filter(t => t !== '杂烩') - const methods = M.recipeMethods(r) - $('#rp-tags').innerHTML = ` -
-

做法

-
- ${methods.length - ? methods.map(m => `${App.escapeHtml(m)}`).join('') - : '这条记录没写做法'} -
-
-
-

标签

-
- ${tags.length - ? tags.map(t => `${App.escapeHtml(t)}`).join('') - : '没有标签'} -
-
` - } - - function renderRelated() { - const r = state.recipe - const stuff = new Set(M.uniqueStuff(r.stuff)) - const cuisine = M.recipeCuisine(r) - - const scored = M.RECIPES - .filter(x => x.id !== r.id) - .map(x => { - const xs = new Set(M.uniqueStuff(x.stuff)) - let hit = 0 - xs.forEach(s => { - if (stuff.has(s)) - hit++ - }) - const sameCuisine = cuisine && M.recipeCuisine(x) === cuisine - return { recipe: x, score: hit + (sameCuisine ? 1 : 0) } - }) - .filter(x => x.score > 0) - .sort((a, b) => b.score - a.score) - .slice(0, 10) - - state.related = scored.map(x => x.recipe) - $('#rp-related-count').textContent = `${state.related.length} 道` - $('#rp-related').innerHTML = state.related.map(x => ` - `).join('') - App.paintIcons($('#rp-related')) - } - - function renderFav() { - const on = Store.isFavorite(state.recipe.id) - const btn = $('#rp-fav') - btn.classList.toggle('is-ghost', !on) - btn.innerHTML = `${on ? '已收藏' : '收藏'}` - App.paintIcons(btn) - } - - // ---------- 事件 ---------- - function bind() { - $('#rp-search').addEventListener('click', () => { - const r = state.recipe - Store.addHistory(r.id) - window.open(`https://search.bilibili.com/all?keyword=${encodeURIComponent(r.name)}`, '_blank') - }) - - $('#rp-video').addEventListener('click', () => { - const r = state.recipe - if (!r.bv) { - App.toast('这条记录没有原视频') - return - } - Store.addHistory(r.id) - window.open(`https://www.bilibili.com/video/${r.bv}`, '_blank') - }) - - $('#rp-fav').addEventListener('click', () => { - const on = Store.toggleFavorite(state.recipe) - renderFav() - App.toast(on ? '已添加到收藏' : '已从收藏移除') - }) - - $('#rp-cook').addEventListener('click', () => { - const stuff = M.uniqueStuff(state.recipe.stuff) - Store.state.selected = stuff - Store.emit() - location.href = 'index.html' - }) - - document.addEventListener('click', (e) => { - const stuff = e.target.closest('[data-stuff]') - if (stuff) { - const name = stuff.dataset.stuff - Store.hasStuff(name) ? Store.removeStuff(name) : Store.addStuff(name) - return - } - const goto = e.target.closest('[data-goto]') - if (goto) { - location.href = `recipe.html?id=${goto.dataset.goto}` - } - }) - } - - // ---------- 启动 ---------- - document.addEventListener('DOMContentLoaded', async () => { - bind() - - const id = currentId() - const res = await Api.getRecipe(id) - const recipe = res.data || M.RECIPES[0] - if (!recipe) { - $('#rp-title').textContent = '没有找到这道菜' - return - } - state.recipe = recipe - document.title = `${recipe.name} · 食用手册` - - renderHead() - renderFacts() - renderStuff() - renderTags() - renderRelated() - renderFav() - - $('#rp-video').hidden = !recipe.bv - }) -})() diff --git a/prototype/cook-desktop/page-settings.js b/prototype/cook-desktop/page-settings.js deleted file mode 100644 index 5924d6c..0000000 --- a/prototype/cook-desktop/page-settings.js +++ /dev/null @@ -1,87 +0,0 @@ -/** - * 设置页:推荐规则、外观、数据清理。 - * 所有改动立刻写回 state.js 的本地存储。 - */ -;(function () { - const M = window.Mock - const Store = window.Store - const App = window.App - - const $ = sel => document.querySelector(sel) - - function render() { - $('#set-mode').value = Store.state.mode - $('#set-theme').value = Store.state.theme - - const spicy = $('#set-spicy') - spicy.classList.toggle('is-on', Store.state.noSpicy) - spicy.setAttribute('aria-checked', String(Store.state.noSpicy)) - - $('#set-pantry').textContent = Store.state.pantry.length ? Store.state.pantry.join('、') : '无' - $('#set-avoid').textContent = Store.state.avoid.length ? Store.state.avoid.join('、') : '无' - - $('#set-fav-count').textContent = `${Store.state.favorites.size} 道菜谱` - $('#set-history-count').textContent = `${Store.state.history.length} 条记录` - $('#set-mine-count').textContent = `${Store.state.customRecipes.length} 条记录` - $('#set-total').textContent = String(M.RECIPES.length) - } - - function bind() { - $('#set-mode').addEventListener('change', (e) => { - Store.setMode(e.target.value) - }) - - $('#set-spicy').addEventListener('click', () => { - Store.setNoSpicy(!Store.state.noSpicy) - }) - - $('#set-theme').addEventListener('change', (e) => { - Store.setTheme(e.target.value) - Store.applyTheme() - }) - - $('#btn-clear-fav').addEventListener('click', () => { - if (!Store.state.favorites.size) { - App.toast('收藏已经是空的') - return - } - Store.state.favorites.clear() - Store.emit() - App.toast('已清空收藏') - }) - - $('#btn-clear-history').addEventListener('click', () => { - if (!Store.state.history.length) { - App.toast('历史已经是空的') - return - } - Store.state.history = [] - Store.emit() - App.toast('已清空历史') - }) - - $('#btn-clear-mine').addEventListener('click', () => { - if (!Store.state.customRecipes.length) { - App.toast('还没有自己的菜谱') - return - } - Store.state.customRecipes = [] - Store.emit() - App.toast('已清空我的菜谱') - }) - - $('#btn-reset-all').addEventListener('click', () => { - Store.resetAll() - Store.state.favorites.clear() - Store.state.history = [] - Store.state.customRecipes = [] - Store.emit() - App.toast('已重置全部') - }) - } - - document.addEventListener('DOMContentLoaded', () => { - bind() - render() - }) -})() diff --git a/prototype/cook-desktop/recipe.html b/prototype/cook-desktop/recipe.html deleted file mode 100644 index 6ca3d50..0000000 --- a/prototype/cook-desktop/recipe.html +++ /dev/null @@ -1,225 +0,0 @@ - - - - - - 菜谱详情 · 食用手册 - - - - - - - - - - -
- - - 食用手册 - - - -
- -
-
-
-
-

菜谱详情

-

从菜名、食材和厨具看这道菜

-
- -
- -
-
-
- -
-

加载中…

-

-
-
- -
- - - - -
- -
- -
-
-

需要准备

- -
-
-
-
-
- -
-
-

做法与标签

-
-
-
-
-
-
- - -
-
-
- - - - - - - -
- - - - - - - - - diff --git a/prototype/cook-desktop/settings.html b/prototype/cook-desktop/settings.html deleted file mode 100644 index 72ca946..0000000 --- a/prototype/cook-desktop/settings.html +++ /dev/null @@ -1,276 +0,0 @@ - - - - - - 设置 · 食用手册 - - - - - - - - - - -
- - - 食用手册 - - - -
- -
-
-
-
-

设置

-

推荐规则、外观和数据都在这儿调

-
-
- - - -
-
-

数据来源

-
-
-
-

菜谱来自仓库里的 packages/data/recipe.json,共 0 条记录;食材、厨具、别名和相克数据来自同目录下的 food.json、aliases.json、incompatible-foods.json。

-

推荐规则与正式应用的 packages/cook/src/utils/recommend.ts 保持一致:缺料少的排前面,大杂烩排同组末尾。

-
-
-
-
-
- - - - - - - -
- - - - - - - - - diff --git a/prototype/cook-desktop/state.js b/prototype/cook-desktop/state.js deleted file mode 100644 index b81408f..0000000 --- a/prototype/cook-desktop/state.js +++ /dev/null @@ -1,230 +0,0 @@ -/** - * 全局状态层:选中的食材、筛选条件、收藏、历史、自定义菜谱。 - * 收藏 / 历史 / 主题写入 localStorage,刷新后仍在(对应正式应用的 IndexedDB 行为)。 - */ -;(function () { - const KEY = 'cook-desktop-state' - - const defaults = { - selected: [], - tool: '', - methodGroups: [], - cuisines: [], - difficulty: [], - pantry: ['油', '盐'], - avoid: [], - noSpicy: false, - mode: 'loose', - keyword: '', - favorites: [], - history: [], - customRecipes: [], - theme: 'auto', - signedIn: false, - } - - function load() { - try { - const raw = localStorage.getItem(KEY) - if (!raw) - return { ...defaults } - const saved = JSON.parse(raw) - return { ...defaults, ...saved } - } - catch { - return { ...defaults } - } - } - - const persisted = load() - const state = { - ...persisted, - favorites: new Set(persisted.favorites), - history: persisted.history.slice(), - customRecipes: persisted.customRecipes.slice(), - } - - function save() { - try { - localStorage.setItem(KEY, JSON.stringify({ - selected: state.selected, - tool: state.tool, - methodGroups: state.methodGroups, - cuisines: state.cuisines, - difficulty: state.difficulty, - pantry: state.pantry, - avoid: state.avoid, - noSpicy: state.noSpicy, - mode: state.mode, - keyword: state.keyword, - favorites: [...state.favorites], - history: state.history, - customRecipes: state.customRecipes, - theme: state.theme, - signedIn: state.signedIn, - })) - } - catch { - // 隐私模式下 localStorage 不可写,忽略即可,状态仍在内存里 - } - } - - function emit() { - save() - document.dispatchEvent(new CustomEvent('store:change', { detail: { state } })) - } - - const Store = { - state, - - // ---------- 食材选择 ---------- - hasStuff(name) { - return state.selected.includes(name) - }, - toggleStuff(name) { - state.selected = state.selected.includes(name) - ? state.selected.filter(n => n !== name) - : [...state.selected, name] - emit() - }, - addStuff(name) { - if (!state.selected.includes(name)) { - state.selected = [...state.selected, name] - emit() - } - }, - removeStuff(name) { - state.selected = state.selected.filter(n => n !== name) - emit() - }, - clearStuff() { - state.selected = [] - emit() - }, - - // ---------- 筛选 ---------- - toggleTool(name) { - state.tool = state.tool === name ? '' : name - emit() - }, - toggleMethodGroup(label) { - state.methodGroups = state.methodGroups.includes(label) - ? state.methodGroups.filter(m => m !== label) - : [...state.methodGroups, label] - emit() - }, - toggleCuisine(label) { - state.cuisines = state.cuisines.includes(label) - ? state.cuisines.filter(c => c !== label) - : [...state.cuisines, label] - emit() - }, - toggleDifficulty(level) { - state.difficulty = state.difficulty.includes(level) - ? state.difficulty.filter(d => d !== level) - : [...state.difficulty, level] - emit() - }, - togglePantry(name) { - state.pantry = state.pantry.includes(name) - ? state.pantry.filter(p => p !== name) - : [...state.pantry, name] - emit() - }, - toggleAvoid(name) { - state.avoid = state.avoid.includes(name) - ? state.avoid.filter(a => a !== name) - : [...state.avoid, name] - emit() - }, - setNoSpicy(value) { - state.noSpicy = value - emit() - }, - setMode(mode) { - state.mode = mode - emit() - }, - setKeyword(value) { - state.keyword = value - emit() - }, - /** 已选筛选条件的数量,用于筛选区角标 */ - filterCount() { - return (state.tool ? 1 : 0) + state.methodGroups.length + state.cuisines.length + state.difficulty.length - }, - clearFilters() { - state.tool = '' - state.methodGroups = [] - state.cuisines = [] - state.difficulty = [] - emit() - }, - resetAll() { - state.selected = [] - state.tool = '' - state.methodGroups = [] - state.cuisines = [] - state.difficulty = [] - state.keyword = '' - state.noSpicy = false - state.avoid = [] - emit() - }, - - // ---------- 收藏 ---------- - isFavorite(id) { - return state.favorites.has(id) - }, - toggleFavorite(recipe) { - if (state.favorites.has(recipe.id)) - state.favorites.delete(recipe.id) - else - state.favorites.add(recipe.id) - emit() - return state.favorites.has(recipe.id) - }, - - // ---------- 历史 ---------- - addHistory(id) { - state.history = [id, ...state.history.filter(x => x !== id)].slice(0, 50) - emit() - }, - - // ---------- 自定义菜谱 ---------- - addCustomRecipe(recipe) { - state.customRecipes = [...state.customRecipes, recipe] - emit() - }, - removeCustomRecipe(id) { - state.customRecipes = state.customRecipes.filter(r => r.id !== id) - emit() - }, - - // ---------- 主题 ---------- - setTheme(theme) { - state.theme = theme - emit() - }, - applyTheme() { - const root = document.documentElement - const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches - const dark = state.theme === 'dark' || (state.theme === 'auto' && prefersDark) - if (dark) - root.setAttribute('data-theme', 'dark') - else - root.removeAttribute('data-theme') - }, - - // ---------- 账户 ---------- - setSignedIn(value) { - state.signedIn = value - emit() - }, - - emit, - } - - window.Store = Store - Store.applyTheme() -})() diff --git a/prototype/cook-desktop/styles.css b/prototype/cook-desktop/styles.css deleted file mode 100644 index a92bbd5..0000000 --- a/prototype/cook-desktop/styles.css +++ /dev/null @@ -1,2474 +0,0 @@ -/* ========================================================================== - 食用手册 · 电脑浏览器版 - Design Contract: Warm Kitchen Ledger (desktop-first) - tokens → layout → components → responsive - ========================================================================== */ - -/* ========================================================================== - 1. Tokens - ========================================================================== */ -:root { - /* 主色 */ - --primary: #1d7a45; - --primary-hover: #348758; - --primary-soft: #e8f4ec; - --primary-ink: #14522f; - --primary-tint: #348758; - - /* 强调色 */ - --accent: #c2410c; - --accent-soft: #fdf0e6; - - /* 中性 */ - --bg: #faf7f2; - --bg-alt: #f2ece1; - --rail: #f3ede2; - --surface: #ffffff; - --surface-sunken: #fdfbf7; - --text: #23201c; - --text-sub: #6b635a; - --text-mute: #9a9186; - --border: #e8e0d4; - --border-strong: #d8ccbb; - - /* 语义色 */ - --success: #3f7d3f; - --success-soft: #e9f3e9; - --warning: #b45309; - --warning-soft: #fef3c7; - --danger: #b91c1c; - - /* 字体 */ - --font-display: "Noto Serif SC", "Songti SC", "STSong", "SimSun", "Source Han Serif SC", serif; - --font-body: -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", "Noto Sans SC", "Segoe UI", sans-serif; - --font-mono: "SF Mono", ui-monospace, Consolas, monospace; - - /* 字号 */ - --t-11: 11px; - --t-12: 12px; - --t-13: 13px; - --t-14: 14px; - --t-15: 15px; - --t-16: 16px; - --t-18: 18px; - --t-22: 22px; - --t-28: 28px; - --t-34: 34px; - --t-42: 42px; - - /* 圆角 */ - --r-sm: 8px; - --r-md: 12px; - --r-lg: 18px; - --r-xl: 24px; - --r-pill: 999px; - - /* 阴影 */ - --sh-sm: 0 1px 2px rgba(35, 32, 28, 0.05); - --sh-md: 0 2px 8px rgba(35, 32, 28, 0.06), 0 1px 2px rgba(35, 32, 28, 0.04); - --sh-lg: 0 8px 28px rgba(35, 32, 28, 0.12); - - /* 间距 */ - --s-4: 4px; - --s-8: 8px; - --s-12: 12px; - --s-16: 16px; - --s-20: 20px; - --s-24: 24px; - --s-32: 32px; - --s-48: 48px; - - /* 布局 */ - --rail-w: 240px; - --tabbar-h: 60px; - --appbar-h: 54px; - --content-max: 1180px; - --content-pad: 32px; - - color-scheme: light; -} - -html[data-theme="dark"] { - --primary: #56b478; - --primary-hover: #6ac48a; - --primary-soft: #1d3a28; - --primary-ink: #9ad9b0; - --primary-tint: #6ac48a; - - --accent: #e8843c; - --accent-soft: #3a2214; - - --bg: #17140f; - --bg-alt: #211c15; - --rail: #1b1711; - --surface: #221d16; - --surface-sunken: #1a1610; - --text: #f2ede4; - --text-sub: #b3a898; - --text-mute: #8a7f6f; - --border: #373026; - --border-strong: #4a4234; - - --success: #6fbf6f; - --success-soft: #1e3320; - --warning: #e0a458; - --warning-soft: #3a2c15; - --danger: #e0736e; - - --sh-sm: 0 1px 2px rgba(0, 0, 0, 0.32); - --sh-md: 0 2px 8px rgba(0, 0, 0, 0.36), 0 1px 2px rgba(0, 0, 0, 0.28); - --sh-lg: 0 8px 28px rgba(0, 0, 0, 0.5); - - color-scheme: dark; -} - -/* ========================================================================== - 2. Reset & Base - ========================================================================== */ -*, -*::before, -*::after { - box-sizing: border-box; -} - -/* hidden 属性必须能压过组件自己的 display */ -[hidden] { - display: none !important; -} - -html, -body { - height: 100%; - margin: 0; - padding: 0; -} - -html { - -webkit-text-size-adjust: 100%; -} - -body { - background: var(--bg); - color: var(--text); - font: 400 var(--t-15) / 1.6 var(--font-body); - -webkit-font-smoothing: antialiased; - -webkit-tap-highlight-color: transparent; - overflow-x: hidden; -} - -/* 纸纹 + 暖光 */ -body::before { - position: fixed; - inset: 0; - z-index: 0; - background: - radial-gradient(120% 55% at 50% 0%, rgba(255, 250, 240, 0.85), transparent 60%), - radial-gradient(85% 45% at 100% 100%, rgba(194, 65, 12, 0.045), transparent 58%); - content: ""; - pointer-events: none; -} - -html[data-theme="dark"] body::before { - background: - radial-gradient(120% 55% at 50% 0%, rgba(86, 180, 120, 0.07), transparent 60%), - radial-gradient(85% 45% at 100% 100%, rgba(232, 132, 60, 0.05), transparent 58%); -} - -body::after { - position: fixed; - inset: 0; - z-index: 0; - opacity: 0.4; - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.035'/%3E%3C/svg%3E"); - content: ""; - pointer-events: none; -} - -a { - color: inherit; -} - -button, -input, -select, -textarea { - font: inherit; - color: inherit; -} - -:focus-visible { - outline: 2px solid var(--primary); - outline-offset: 2px; -} - -.sr-only { - position: absolute; - width: 1px; - height: 1px; - margin: -1px; - padding: 0; - overflow: hidden; - clip: rect(0 0 0 0); - white-space: nowrap; - border: 0; -} - -.skip-link { - position: absolute; - top: -100px; - left: 12px; - z-index: 200; - padding: 10px 16px; - border-radius: var(--r-sm); - background: var(--primary); - color: #fff; - text-decoration: none; - transition: top 0.16s ease; -} - -.skip-link:focus { - top: 12px; -} - -/* ========================================================================== - 3. App Shell — 桌面左侧固定导航栏 - ========================================================================== */ -.app-rail { - position: fixed; - inset: 0 auto 0 0; - z-index: 60; - display: none; - width: var(--rail-w); - flex-direction: column; - padding: var(--s-24) var(--s-12) var(--s-16); - border-right: 1px solid var(--border); - background: var(--rail); -} - -.rail-brand { - display: flex; - align-items: center; - gap: var(--s-12); - padding: 0 var(--s-12) var(--s-24); -} - -.rail-mark { - display: inline-flex; - align-items: center; - justify-content: center; - width: 38px; - height: 38px; - flex: none; - border-radius: var(--r-md); - background: var(--primary); - color: #fff; - box-shadow: var(--sh-sm); -} - -.rail-mark svg { - width: 21px; - height: 21px; -} - -.rail-brand-text b { - display: block; - font: 600 var(--t-18) / 1.25 var(--font-display); - letter-spacing: 0.02em; -} - -.rail-brand-text small { - display: block; - margin-top: 2px; - color: var(--text-mute); - font-size: var(--t-11); -} - -.rail-nav { - display: flex; - flex-direction: column; - gap: 2px; -} - -.rail-nav + .rail-nav { - margin-top: var(--s-20); - padding-top: var(--s-16); - border-top: 1px solid var(--border); -} - -.rail-label { - padding: 0 var(--s-12) var(--s-8); - color: var(--text-mute); - font-size: var(--t-11); - letter-spacing: 0.08em; -} - -.rail-link { - position: relative; - display: flex; - align-items: center; - gap: var(--s-12); - min-height: 40px; - padding: 0 var(--s-12); - border-radius: 10px; - color: var(--text-sub); - font-size: var(--t-14); - text-decoration: none; - transition: background-color 0.16s ease, color 0.16s ease; -} - -.rail-link svg { - width: 19px; - height: 19px; - flex: none; - stroke-width: 1.7; -} - -.rail-link:hover { - background: var(--bg-alt); - color: var(--text); -} - -/* 导航激活态:唯一机制 —— body[data-page] 与 [data-nav] 匹配 */ -body[data-page="cook"] .rail-link[data-nav="cook"], -body[data-page="library"] .rail-link[data-nav="library"], -body[data-page="recipe"] .rail-link[data-nav="library"], -body[data-page="discover"] .rail-link[data-nav="discover"], -body[data-page="me"] .rail-link[data-nav="me"], -body[data-page="settings"] .rail-link[data-nav="settings"], -body[data-page="help"] .rail-link[data-nav="help"], -body[data-page="about"] .rail-link[data-nav="about"] { - background: var(--primary-soft); - color: var(--primary-ink); - font-weight: 600; -} - -body[data-page="cook"] .rail-link[data-nav="cook"]::before, -body[data-page="library"] .rail-link[data-nav="library"]::before, -body[data-page="recipe"] .rail-link[data-nav="library"]::before, -body[data-page="discover"] .rail-link[data-nav="discover"]::before, -body[data-page="me"] .rail-link[data-nav="me"]::before, -body[data-page="settings"] .rail-link[data-nav="settings"]::before, -body[data-page="help"] .rail-link[data-nav="help"]::before, -body[data-page="about"] .rail-link[data-nav="about"]::before { - position: absolute; - left: 0; - width: 3px; - height: 18px; - border-radius: 0 3px 3px 0; - background: var(--primary); - content: ""; -} - -.rail-foot { - margin-top: auto; - padding-top: var(--s-16); - border-top: 1px solid var(--border); -} - -.rail-theme { - display: flex; - align-items: center; - gap: var(--s-12); - width: 100%; - min-height: 38px; - padding: 0 var(--s-12); - border: 0; - border-radius: 10px; - background: transparent; - color: var(--text-sub); - font-size: var(--t-13); - text-align: left; - cursor: pointer; - transition: background-color 0.16s ease, color 0.16s ease; -} - -.rail-theme:hover { - background: var(--bg-alt); - color: var(--text); -} - -.rail-theme svg { - width: 17px; - height: 17px; - flex: none; -} - -.rail-stat { - padding: var(--s-12) var(--s-12) 0; - color: var(--text-mute); - font-size: var(--t-11); - line-height: 1.6; -} - -.rail-stat b { - color: var(--text-sub); - font-family: var(--font-mono); - font-variant-numeric: tabular-nums; -} - -/* ========================================================================== - 4. App Shell — 移动端顶栏 / 底部 tab - ========================================================================== */ -.app-bar { - position: fixed; - inset: 0 0 auto 0; - z-index: 60; - display: flex; - align-items: center; - gap: var(--s-12); - height: calc(var(--appbar-h) + env(safe-area-inset-top)); - padding: env(safe-area-inset-top) var(--s-16) 0; - border-bottom: 1px solid var(--border); - background: color-mix(in srgb, var(--bg) 88%, transparent); - backdrop-filter: blur(14px); -} - -.app-bar-brand { - display: flex; - align-items: center; - gap: var(--s-8); - font: 600 var(--t-16) / 1 var(--font-display); -} - -.app-bar-brand svg { - width: 19px; - height: 19px; - color: var(--primary); -} - -.app-bar-spacer { - flex: 1; -} - -.app-bar-btn { - display: inline-flex; - align-items: center; - justify-content: center; - width: 36px; - height: 36px; - border: 1px solid var(--border); - border-radius: var(--r-pill); - background: var(--surface); - color: var(--text-sub); - cursor: pointer; - transition: color 0.16s ease, border-color 0.16s ease; -} - -.app-bar-btn:hover { - border-color: var(--border-strong); - color: var(--primary); -} - -.app-bar-btn svg { - width: 18px; - height: 18px; -} - -.tabbar { - position: fixed; - inset: auto 0 0 0; - z-index: 60; - display: flex; - padding-bottom: env(safe-area-inset-bottom); - border-top: 1px solid var(--border); - background: color-mix(in srgb, var(--bg) 90%, transparent); - backdrop-filter: blur(14px); -} - -.tabbar a { - position: relative; - display: flex; - flex: 1; - flex-direction: column; - align-items: center; - gap: 3px; - height: var(--tabbar-h); - padding-top: 9px; - color: var(--text-mute); - text-decoration: none; - transition: color 0.18s ease; -} - -.tabbar a svg { - width: 22px; - height: 22px; - stroke-width: 1.7; -} - -.tabbar a span { - font-size: var(--t-11); -} - -body[data-page="cook"] .tabbar a[data-nav="cook"], -body[data-page="library"] .tabbar a[data-nav="library"], -body[data-page="recipe"] .tabbar a[data-nav="library"], -body[data-page="discover"] .tabbar a[data-nav="discover"], -body[data-page="me"] .tabbar a[data-nav="me"] { - color: var(--primary); -} - -body[data-page="cook"] .tabbar a[data-nav="cook"] svg, -body[data-page="library"] .tabbar a[data-nav="library"] svg, -body[data-page="recipe"] .tabbar a[data-nav="library"] svg, -body[data-page="discover"] .tabbar a[data-nav="discover"] svg, -body[data-page="me"] .tabbar a[data-nav="me"] svg { - stroke-width: 2.1; -} - -body[data-page="cook"] .tabbar a[data-nav="cook"]::before, -body[data-page="library"] .tabbar a[data-nav="library"]::before, -body[data-page="recipe"] .tabbar a[data-nav="library"]::before, -body[data-page="discover"] .tabbar a[data-nav="discover"]::before, -body[data-page="me"] .tabbar a[data-nav="me"]::before { - position: absolute; - top: 0; - width: 20px; - height: 2.5px; - border-radius: 0 0 3px 3px; - background: var(--primary); - content: ""; -} - -/* ========================================================================== - 5. 主内容区 - ========================================================================== */ -.app-main { - position: relative; - z-index: 1; - min-height: 100%; - padding: var(--s-24) var(--content-pad) calc(var(--tabbar-h) + env(safe-area-inset-bottom) + var(--s-16)); -} - -.page { - max-width: var(--content-max); - margin: 0 auto; -} - -.page-head { - display: flex; - align-items: flex-end; - gap: var(--s-16); - margin-bottom: var(--s-24); -} - -.page-head-titles { - flex: 1; - min-width: 0; -} - -.page-title { - margin: 0; - font: 600 var(--t-28) / 1.25 var(--font-display); - letter-spacing: 0.01em; -} - -.page-sub { - margin: 5px 0 0; - color: var(--text-mute); - font-size: var(--t-13); -} - -.page-actions { - display: flex; - align-items: center; - gap: var(--s-8); - flex: none; -} - -.section { - margin-bottom: var(--s-24); - animation: rise 0.4s cubic-bezier(0.22, 0.9, 0.3, 1) both; -} - -.section:nth-of-type(2) { animation-delay: 0.04s; } -.section:nth-of-type(3) { animation-delay: 0.08s; } -.section:nth-of-type(4) { animation-delay: 0.12s; } -.section:nth-of-type(5) { animation-delay: 0.16s; } - -@keyframes rise { - from { - opacity: 0; - transform: translateY(10px); - } -} - -.section-head { - display: flex; - align-items: baseline; - justify-content: space-between; - gap: var(--s-12); - margin: 0 0 var(--s-12); -} - -.section-head h2 { - margin: 0; - font: 600 var(--t-18) / 1.3 var(--font-display); -} - -.section-head .hint { - color: var(--text-mute); - font-size: var(--t-13); -} - -.section-head .hint.on { - color: var(--primary); - font-weight: 500; -} - -.card { - border: 1px solid var(--border); - border-radius: var(--r-lg); - background: var(--surface); - box-shadow: var(--sh-sm); -} - -.card-pad { - padding: var(--s-20); -} - -/* ========================================================================== - 6. 按钮 - ========================================================================== */ -.btn { - display: inline-flex; - align-items: center; - justify-content: center; - gap: 6px; - min-height: 44px; - padding: 0 20px; - border: 1px solid transparent; - border-radius: var(--r-pill); - background: var(--primary); - color: #fff; - font: 500 var(--t-15) / 1 var(--font-body); - text-decoration: none; - white-space: nowrap; - cursor: pointer; - transition: background-color 0.16s ease, transform 0.16s ease; -} - -.btn:hover { - background: var(--primary-hover); -} - -.btn:active { - transform: scale(0.97); -} - -.btn:disabled { - opacity: 0.45; - cursor: not-allowed; - transform: none; -} - -.btn svg { - width: 17px; - height: 17px; -} - -.btn.is-ghost { - border-color: var(--border); - background: var(--surface); - color: var(--text-sub); -} - -.btn.is-ghost:hover { - border-color: var(--border-strong); - background: var(--bg); - color: var(--text); -} - -.btn.is-block { - width: 100%; -} - -.btn.is-sm { - min-height: 34px; - padding: 0 14px; - font-size: var(--t-13); -} - -.icon-btn { - display: inline-flex; - align-items: center; - justify-content: center; - width: 38px; - height: 38px; - flex: none; - border: 1px solid var(--border); - border-radius: var(--r-pill); - background: var(--surface); - color: var(--text-sub); - box-shadow: var(--sh-sm); - cursor: pointer; - transition: transform 0.16s ease, border-color 0.16s ease, color 0.16s ease; -} - -.icon-btn:hover { - border-color: var(--border-strong); - color: var(--primary); -} - -.icon-btn:active { - transform: scale(0.94); -} - -.icon-btn.is-on { - border-color: var(--primary); - background: var(--primary); - color: #fff; -} - -.icon-btn svg { - width: 19px; - height: 19px; -} - -/* ========================================================================== - 7. 食材 chip / token - ========================================================================== */ -.chips { - display: flex; - flex-wrap: wrap; - gap: var(--s-8); -} - -.chip { - display: inline-flex; - align-items: center; - gap: 6px; - min-height: 38px; - padding: 0 14px; - border: 1px solid var(--border); - border-radius: var(--r-pill); - background: var(--surface); - color: var(--text); - font: 400 var(--t-15) / 1 var(--font-body); - cursor: pointer; - user-select: none; - transition: transform 0.16s ease, background-color 0.16s ease, border-color 0.16s ease, color 0.16s ease; -} - -.chip .emoji { - font-size: 15px; - line-height: 1; -} - -.chip:hover { - border-color: var(--border-strong); -} - -.chip:active { - transform: scale(0.96); -} - -.chip.is-on { - border-color: var(--primary); - background: var(--primary); - color: #fff; - font-weight: 500; -} - -.chip:focus-visible { - outline: 2px solid var(--primary); - outline-offset: 2px; -} - -.chip.chip-sm { - min-height: 30px; - padding: 0 11px; - font-size: var(--t-13); -} - -.chip.chip-icon .icon { - width: 16px; - height: 16px; - opacity: 0.85; -} - -.chip.chip-icon.is-on .icon { - opacity: 1; -} - -/* 只读展示用的 chip */ -.chip.is-plain { - border-color: var(--border); - background: var(--bg-alt); - color: var(--text-sub); - cursor: default; -} - -.chip.is-plain:hover { - border-color: var(--border); -} - -/* 缺料 */ -.chip.is-missing { - border-color: var(--accent); - color: var(--accent); -} - -.tray { - display: flex; - flex-wrap: wrap; - gap: var(--s-8); - min-height: 62px; - align-items: center; - padding: var(--s-12); - border: 1px solid var(--border); - border-radius: var(--r-lg); - background: var(--surface); - box-shadow: var(--sh-sm); -} - -.tray-empty { - color: var(--text-mute); - font-size: var(--t-13); -} - -.token { - display: inline-flex; - align-items: center; - gap: 5px; - height: 34px; - padding: 0 8px 0 12px; - border: 0; - border-radius: var(--r-pill); - background: var(--primary-soft); - color: var(--primary-ink); - font-size: var(--t-14); - font-weight: 500; - cursor: pointer; - animation: token-in 0.24s cubic-bezier(0.34, 1.4, 0.64, 1) both; -} - -@keyframes token-in { - from { - opacity: 0; - transform: scale(0.82); - } -} - -.token svg { - width: 15px; - height: 15px; - opacity: 0.6; -} - -.token:hover svg { - opacity: 1; -} - -.token:active { - opacity: 0.65; -} - -/* ========================================================================== - 8. 搜索框 - ========================================================================== */ -.search { - position: relative; - display: flex; - align-items: center; -} - -.search svg { - position: absolute; - left: 14px; - width: 17px; - height: 17px; - color: var(--text-mute); - pointer-events: none; -} - -.search input { - width: 100%; - height: 44px; - padding: 0 14px 0 40px; - border: 1px solid var(--border); - border-radius: var(--r-pill); - background: var(--surface); - color: var(--text); - font: 400 var(--t-15) / 44px var(--font-body); - outline: none; - transition: border-color 0.16s ease, box-shadow 0.16s ease; -} - -.search input::placeholder { - color: var(--text-mute); -} - -.search input:focus { - border-color: var(--primary-tint); - box-shadow: 0 0 0 3px rgba(29, 122, 69, 0.12); -} - -html[data-theme="dark"] .search input:focus { - box-shadow: 0 0 0 3px rgba(86, 180, 120, 0.18); -} - -.search-lg input { - height: 48px; - font-size: var(--t-16); - line-height: 48px; -} - -/* ========================================================================== - 9. 筛选区 - ========================================================================== */ -.filters { - border: 1px solid var(--border); - border-radius: var(--r-lg); - background: var(--surface); - box-shadow: var(--sh-sm); - overflow: hidden; -} - -.filter-bar { - display: flex; - align-items: center; - gap: var(--s-8); - width: 100%; - min-height: 48px; - padding: 0 var(--s-16); - border: 0; - background: transparent; - color: var(--text); - font: 500 var(--t-15) / 1 var(--font-body); - text-align: left; - cursor: pointer; -} - -.filter-bar .badge-count { - display: inline-flex; - align-items: center; - justify-content: center; - min-width: 20px; - height: 20px; - padding: 0 6px; - border-radius: var(--r-pill); - background: var(--primary); - color: #fff; - font-size: var(--t-11); - font-weight: 600; - font-family: var(--font-mono); -} - -.filter-bar .caret { - width: 18px; - height: 18px; - margin-left: auto; - color: var(--text-mute); - transition: transform 0.22s ease; -} - -.filters.is-open .filter-bar .caret { - transform: rotate(180deg); -} - -.filter-body { - display: none; - padding: 0 var(--s-16) var(--s-16); -} - -.filters.is-open .filter-body { - display: block; -} - -.filter-group + .filter-group { - margin-top: var(--s-16); -} - -.filter-group h3 { - margin: 0 0 var(--s-8); - color: var(--text-sub); - font: 500 var(--t-13) / 1.3 var(--font-body); -} - -.active-filters { - display: flex; - flex-wrap: wrap; - gap: 6px; - margin-top: var(--s-12); - padding-top: var(--s-12); - border-top: 1px dashed var(--border); -} - -.tag-chip { - display: inline-flex; - align-items: center; - gap: 4px; - height: 28px; - padding: 0 8px 0 11px; - border: 0; - border-radius: var(--r-pill); - background: var(--primary-soft); - color: var(--primary-ink); - font: 500 var(--t-13) / 1 var(--font-body); - cursor: pointer; -} - -.tag-chip svg { - width: 13px; - height: 13px; - opacity: 0.65; -} - -.tag-chip.is-accent { - background: var(--accent-soft); - color: var(--accent); -} - -/* ========================================================================== - 10. 分段控件 - ========================================================================== */ -.segment { - display: inline-flex; - gap: 2px; - padding: 3px; - border-radius: var(--r-pill); - background: var(--bg-alt); -} - -.segment button { - min-height: 30px; - padding: 0 14px; - border: 0; - border-radius: var(--r-pill); - background: transparent; - color: var(--text-sub); - font: 500 var(--t-13) / 1 var(--font-body); - white-space: nowrap; - cursor: pointer; - transition: background-color 0.18s ease, color 0.18s ease, box-shadow 0.18s ease; -} - -.segment button:hover { - color: var(--text); -} - -.segment button.is-on { - background: var(--surface); - color: var(--primary); - box-shadow: var(--sh-sm); -} - -.segment button .n { - margin-left: 4px; - opacity: 0.65; - font-family: var(--font-mono); - font-variant-numeric: tabular-nums; -} - -/* ========================================================================== - 11. 列表行 / 表格 - ========================================================================== */ -.list { - border: 1px solid var(--border); - border-radius: var(--r-lg); - background: var(--surface); - box-shadow: var(--sh-sm); - overflow: hidden; -} - -.list-row { - display: flex; - align-items: center; - gap: var(--s-12); - width: 100%; - min-height: 56px; - padding: var(--s-8) var(--s-16); - border: 0; - border-bottom: 1px solid var(--border); - background: transparent; - color: inherit; - text-align: left; - text-decoration: none; - cursor: pointer; - transition: background-color 0.14s ease; -} - -.list-row:last-child { - border-bottom: 0; -} - -.list-row:hover { - background: var(--surface-sunken); -} - -.list-row .thumb { - display: inline-flex; - align-items: center; - justify-content: center; - width: 40px; - height: 40px; - flex: none; - border-radius: var(--r-sm); - background: var(--bg-alt); - font-size: 20px; -} - -.list-row .name { - flex: 1; - min-width: 0; - font: 500 var(--t-15) / 1.4 var(--font-body); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.list-row .sub { - display: block; - margin-top: 1px; - color: var(--text-mute); - font-size: var(--t-12); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.star { - display: inline-flex; - align-items: center; - justify-content: center; - width: 34px; - height: 34px; - flex: none; - border: 0; - border-radius: var(--r-pill); - background: transparent; - color: var(--border-strong); - cursor: pointer; - transition: color 0.16s ease, background-color 0.16s ease; -} - -.star:hover { - background: var(--bg-alt); -} - -.star.is-on { - color: #d97706; -} - -.star svg { - width: 19px; - height: 19px; -} - -/* 桌面端菜谱表 */ -.table-wrap { - border: 1px solid var(--border); - border-radius: var(--r-lg); - background: var(--surface); - box-shadow: var(--sh-sm); - overflow: auto; -} - -.recipe-table { - width: 100%; - border-collapse: collapse; - font-size: var(--t-14); -} - -.recipe-table th { - position: sticky; - top: 0; - z-index: 1; - padding: var(--s-12) var(--s-16); - background: var(--bg-alt); - color: var(--text-sub); - font: 600 var(--t-12) / 1.3 var(--font-body); - letter-spacing: 0.04em; - text-align: left; - white-space: nowrap; -} - -.recipe-table td { - padding: var(--s-10, 10px) var(--s-16); - border-top: 1px solid var(--border); - vertical-align: middle; -} - -.recipe-table tbody tr { - transition: background-color 0.14s ease; -} - -.recipe-table tbody tr:hover { - background: var(--surface-sunken); -} - -.table-dish { - display: flex; - align-items: center; - gap: var(--s-12); -} - -.table-dish .thumb { - display: inline-flex; - align-items: center; - justify-content: center; - width: 34px; - height: 34px; - flex: none; - border-radius: var(--r-sm); - background: var(--bg-alt); - font-size: 18px; -} - -.table-dish b { - font: 500 var(--t-14) / 1.4 var(--font-body); -} - -.table-stuff { - color: var(--text-sub); - font-size: var(--t-13); -} - -.table-num { - color: var(--text-mute); - font-family: var(--font-mono); - font-variant-numeric: tabular-nums; -} - -/* ========================================================================== - 12. 结果行(做菜页) - ========================================================================== */ -.dish-row { - display: flex; - align-items: center; - gap: var(--s-12); - width: 100%; - min-height: 60px; - padding: var(--s-8) var(--s-16); - border: 0; - border-bottom: 1px solid var(--border); - background: transparent; - color: inherit; - text-align: left; - cursor: pointer; - animation: row-in 0.32s cubic-bezier(0.2, 0.8, 0.2, 1) both; - animation-delay: calc(var(--i, 0) * 24ms); - transition: background-color 0.14s ease; -} - -@keyframes row-in { - from { - opacity: 0; - transform: translateY(8px); - } -} - -.dish-row:last-child { - border-bottom: 0; -} - -.dish-row:hover { - background: var(--surface-sunken); -} - -.dish-row:hover .dish-name { - color: var(--primary); -} - -.dish-thumb { - display: inline-flex; - align-items: center; - justify-content: center; - width: 44px; - height: 44px; - flex: none; - border-radius: var(--r-md); - background: var(--bg-alt); - font-size: 22px; -} - -.dish-main { - flex: 1; - min-width: 0; -} - -.dish-name { - display: block; - font: 500 var(--t-15) / 1.35 var(--font-body); - transition: color 0.16s ease; -} - -.dish-meta { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 6px; - margin-top: 3px; - color: var(--text-mute); - font-size: var(--t-12); -} - -.dish-meta .dot::before { - margin-right: 6px; - content: "·"; -} - -.miss { - color: var(--accent); - font-weight: 500; -} - -.cuisine-tag { - display: inline-flex; - align-items: center; - height: 20px; - padding: 0 8px; - border-radius: var(--r-pill); - background: var(--primary-soft); - color: var(--primary-ink); - font-size: var(--t-11); - font-weight: 500; -} - -.cuisine-tag.is-plain { - background: var(--bg-alt); - color: var(--text-sub); -} - -/* ========================================================================== - 13. 空状态 / 骨架屏 - ========================================================================== */ -.empty { - padding: var(--s-48) var(--s-24); - text-align: center; -} - -.empty svg { - width: 68px; - height: 68px; - margin-bottom: var(--s-16); - color: var(--border-strong); -} - -.empty h3 { - margin: 0 0 6px; - font: 600 var(--t-18) / 1.4 var(--font-display); -} - -.empty p { - margin: 0 0 var(--s-20); - color: var(--text-sub); - font-size: var(--t-13); -} - -.skeleton { - height: 16px; - border-radius: var(--r-sm); - background: linear-gradient(90deg, var(--bg-alt) 25%, var(--surface-sunken) 37%, var(--bg-alt) 63%); - background-size: 400% 100%; - animation: shimmer 1.3s ease infinite; -} - -@keyframes shimmer { - from { - background-position: 100% 0; - } - to { - background-position: 0 0; - } -} - -.skeleton-row { - display: flex; - align-items: center; - gap: var(--s-12); - min-height: 60px; - padding: var(--s-8) var(--s-16); - border-bottom: 1px solid var(--border); -} - -/* ========================================================================== - 14. 弹层 - ========================================================================== */ -.modal { - position: fixed; - inset: 0; - z-index: 80; - display: none; - align-items: center; - justify-content: center; - padding: var(--s-24); - background: rgba(35, 32, 28, 0.42); - backdrop-filter: blur(3px); -} - -.modal.is-open { - display: flex; - animation: fade-in 0.24s ease both; -} - -@keyframes fade-in { - from { - opacity: 0; - } -} - -.modal-panel { - width: 100%; - max-width: 720px; - max-height: 86vh; - overflow-y: auto; - border: 1px solid var(--border); - border-radius: var(--r-xl); - background: var(--bg); - box-shadow: var(--sh-lg); - animation: modal-in 0.32s cubic-bezier(0.22, 0.9, 0.3, 1) both; -} - -@keyframes modal-in { - from { - opacity: 0; - transform: translateY(20px) scale(0.98); - } -} - -.modal-hero { - display: flex; - align-items: center; - gap: var(--s-20); - padding: var(--s-24) var(--s-24) var(--s-16); -} - -.modal-hero .thumb { - display: inline-flex; - align-items: center; - justify-content: center; - width: 76px; - height: 76px; - flex: none; - border-radius: var(--r-xl); - background: var(--surface); - box-shadow: var(--sh-md); - font-size: 40px; - animation: pop 0.42s cubic-bezier(0.34, 1.4, 0.64, 1) both; -} - -@keyframes pop { - from { - transform: scale(0.76); - } -} - -.modal-hero h2 { - margin: 0 0 4px; - font: 600 var(--t-22) / 1.35 var(--font-display); -} - -.modal-hero p { - margin: 0; - color: var(--text-mute); - font-size: var(--t-13); -} - -.modal-actions { - display: flex; - gap: var(--s-8); - padding: 0 var(--s-24) var(--s-20); -} - -.modal-actions .btn { - flex: 1; -} - -.modal-actions .btn.is-ghost { - flex: none; - width: 48px; - padding: 0; -} - -.detail-group { - margin: 0 var(--s-24) var(--s-16); - padding: var(--s-16); - border: 1px solid var(--border); - border-radius: var(--r-lg); - background: var(--surface); -} - -.detail-group h3 { - margin: 0 0 var(--s-8); - color: var(--text-sub); - font: 500 var(--t-13) / 1.3 var(--font-body); -} - -.detail-note { - margin: 0 var(--s-24) var(--s-24); - color: var(--text-mute); - font-size: var(--t-12); - line-height: 1.7; - text-align: center; -} - -/* ========================================================================== - 15. Toast - ========================================================================== */ -.toast { - position: fixed; - bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom) + 20px); - left: 50%; - z-index: 90; - max-width: min(90vw, 420px); - padding: 10px 18px; - border-radius: var(--r-md); - background: var(--text); - color: var(--bg); - font-size: var(--t-13); - box-shadow: var(--sh-lg); - opacity: 0; - pointer-events: none; - transform: translate(-50%, 10px); - transition: opacity 0.22s ease, transform 0.22s ease; -} - -.toast.is-on { - opacity: 1; - transform: translate(-50%, 0); -} - -/* ========================================================================== - 16. 英雄卡 / 菜系网格 - ========================================================================== */ -.hero-card { - position: relative; - overflow: hidden; - padding: var(--s-32); - border-radius: var(--r-xl); - background: linear-gradient(135deg, #1d7a45, #14522f); - color: #fff; - box-shadow: var(--sh-md); -} - -.hero-card::after { - position: absolute; - top: -50px; - right: -30px; - width: 170px; - height: 170px; - border-radius: 50%; - background: rgba(255, 255, 255, 0.08); - content: ""; -} - -.hero-card .kicker { - font-size: var(--t-13); - opacity: 0.82; -} - -.hero-card h2 { - position: relative; - margin: var(--s-8) 0 var(--s-4); - font: 600 var(--t-34) / 1.25 var(--font-display); -} - -.hero-card p { - position: relative; - margin: 0 0 var(--s-20); - font-size: var(--t-13); - opacity: 0.85; -} - -.hero-card .btn { - position: relative; - background: #fff; - color: var(--primary-ink); -} - -.hero-card .btn:hover { - background: var(--primary-soft); -} - -.cuisine-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); - gap: var(--s-12); -} - -.cuisine-card { - display: flex; - align-items: center; - gap: var(--s-12); - padding: var(--s-16); - border: 1px solid var(--border); - border-radius: var(--r-md); - background: var(--surface); - box-shadow: var(--sh-sm); - color: inherit; - text-align: left; - text-decoration: none; - cursor: pointer; - transition: transform 0.16s ease, border-color 0.16s ease, box-shadow 0.16s ease; -} - -.cuisine-card:hover { - border-color: var(--border-strong); - box-shadow: var(--sh-md); - transform: translateY(-1px); -} - -.cuisine-card .dot { - width: 8px; - height: 8px; - flex: none; - border-radius: 50%; - background: var(--primary); -} - -.cuisine-card b { - flex: 1; - font: 500 var(--t-15) / 1.3 var(--font-body); -} - -.cuisine-card small { - color: var(--text-mute); - font-size: var(--t-12); - font-family: var(--font-mono); - font-variant-numeric: tabular-nums; -} - -/* ========================================================================== - 17. 我的页 - ========================================================================== */ -.account { - display: flex; - align-items: center; - gap: var(--s-16); - padding: var(--s-20); - border: 1px solid var(--border); - border-radius: var(--r-lg); - background: var(--surface); - box-shadow: var(--sh-sm); -} - -.account .avatar { - display: inline-flex; - align-items: center; - justify-content: center; - width: 52px; - height: 52px; - flex: none; - border-radius: 50%; - background: var(--primary-soft); - color: var(--primary-ink); -} - -.account .avatar svg { - width: 26px; - height: 26px; -} - -.account b { - display: block; - font: 600 var(--t-16) / 1.4 var(--font-body); -} - -.account small { - color: var(--text-mute); - font-size: var(--t-13); -} - -.menu { - border: 1px solid var(--border); - border-radius: var(--r-lg); - background: var(--surface); - box-shadow: var(--sh-sm); - overflow: hidden; -} - -.menu a, -.menu button { - display: flex; - align-items: center; - gap: var(--s-12); - width: 100%; - min-height: 50px; - padding: var(--s-12) var(--s-16); - border: 0; - border-bottom: 1px solid var(--border); - background: transparent; - color: inherit; - font: 400 var(--t-15) / 1.4 var(--font-body); - text-align: left; - text-decoration: none; - cursor: pointer; - transition: background-color 0.14s ease; -} - -.menu a:last-child, -.menu button:last-child { - border-bottom: 0; -} - -.menu a:hover, -.menu button:hover { - background: var(--surface-sunken); -} - -.menu .ico { - display: inline-flex; - align-items: center; - justify-content: center; - width: 32px; - height: 32px; - flex: none; - border-radius: var(--r-sm); - background: var(--bg-alt); - color: var(--text-sub); -} - -.menu .ico svg { - width: 17px; - height: 17px; -} - -.menu .label { - flex: 1; -} - -.menu .value { - color: var(--text-mute); - font-size: var(--t-13); -} - -.menu .chev { - width: 16px; - height: 16px; - flex: none; - color: var(--border-strong); -} - -.menu-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); - gap: var(--s-16); - align-items: start; -} - -/* ========================================================================== - 18. 设置 / FAQ / 关于 - ========================================================================== */ -.setting-row { - display: flex; - align-items: center; - gap: var(--s-16); - min-height: 58px; - padding: var(--s-12) var(--s-20); - border-bottom: 1px solid var(--border); -} - -.setting-row:last-child { - border-bottom: 0; -} - -.setting-row .label { - flex: 1; -} - -.setting-row .label b { - display: block; - font: 500 var(--t-15) / 1.4 var(--font-body); -} - -.setting-row .label small { - color: var(--text-mute); - font-size: var(--t-12); -} - -.switch { - position: relative; - width: 46px; - height: 28px; - flex: none; - border: 0; - border-radius: var(--r-pill); - background: var(--border-strong); - cursor: pointer; - transition: background-color 0.2s ease; -} - -.switch::after { - position: absolute; - top: 3px; - left: 3px; - width: 22px; - height: 22px; - border-radius: 50%; - background: #fff; - box-shadow: var(--sh-sm); - content: ""; - transition: transform 0.2s cubic-bezier(0.32, 0.72, 0, 1); -} - -.switch.is-on { - background: var(--primary); -} - -.switch.is-on::after { - transform: translateX(18px); -} - -.faq-item { - border-bottom: 1px solid var(--border); -} - -.faq-item:last-child { - border-bottom: 0; -} - -.faq-q { - display: flex; - align-items: center; - gap: var(--s-12); - width: 100%; - min-height: 54px; - padding: var(--s-12) var(--s-20); - border: 0; - background: transparent; - color: inherit; - font: 500 var(--t-15) / 1.5 var(--font-body); - text-align: left; - cursor: pointer; -} - -.faq-q .chev { - width: 16px; - height: 16px; - margin-left: auto; - flex: none; - color: var(--text-mute); - transition: transform 0.22s ease; -} - -.faq-item.is-open .faq-q .chev { - transform: rotate(180deg); -} - -.faq-a { - display: none; - padding: 0 var(--s-20) var(--s-20) calc(var(--s-20) + 28px); - color: var(--text-sub); - font-size: var(--t-14); - line-height: 1.75; -} - -.faq-item.is-open .faq-a { - display: block; -} - -.prose { - color: var(--text-sub); - font-size: var(--t-14); - line-height: 1.85; -} - -.prose p { - margin: 0 0 var(--s-12); -} - -.prose strong { - color: var(--text); - font-weight: 600; -} - -.about-mark { - display: flex; - align-items: center; - gap: var(--s-16); - margin-bottom: var(--s-20); -} - -.about-mark .mark { - display: inline-flex; - align-items: center; - justify-content: center; - width: 56px; - height: 56px; - flex: none; - border-radius: var(--r-lg); - background: var(--primary); - color: #fff; -} - -.about-mark .mark svg { - width: 30px; - height: 30px; -} - -.about-mark h2 { - margin: 0; - font: 600 var(--t-22) / 1.3 var(--font-display); -} - -.about-mark p { - margin: 4px 0 0; - color: var(--text-mute); - font-size: var(--t-13); -} - -/* ========================================================================== - 19. 工具类 - ========================================================================== */ -.stack-8 > * + * { margin-top: var(--s-8); } -.stack-12 > * + * { margin-top: var(--s-12); } -.stack-16 > * + * { margin-top: var(--s-16); } -.stack-24 > * + * { margin-top: var(--s-24); } - -.row-flex { - display: flex; - align-items: center; - gap: var(--s-8); - flex-wrap: wrap; -} - -.grid-2 { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); - gap: var(--s-20); - align-items: start; -} - -.mono { - font-family: var(--font-mono); - font-variant-numeric: tabular-nums; -} - -.text-mute { - color: var(--text-mute); -} - -.text-sub { - color: var(--text-sub); -} - -.divider { - height: 1px; - margin: var(--s-20) 0; - background: var(--border); -} - -/* ========================================================================== - 19.5 做菜页:左选菜 / 右结果(桌面双栏) - ========================================================================== */ -.cook-split { - display: grid; - gap: var(--s-24); - align-items: start; -} - -.cook-side { - min-width: 0; -} - -.cook-results { - position: sticky; - top: var(--s-32); - display: flex; - flex-direction: column; - min-width: 0; - max-height: calc(100vh - var(--s-64)); - border: 1px solid var(--border); - border-radius: var(--r-lg); - background: var(--surface); - box-shadow: var(--sh-sm); - overflow: hidden; -} - -.results-head { - flex: none; - padding: var(--s-20) var(--s-20) var(--s-16); - border-bottom: 1px solid var(--border); -} - -.results-title { - margin: 0; - font: 600 var(--t-22) / 1.3 var(--font-display); -} - -.results-sub { - margin: 4px 0 0; - color: var(--text-mute); - font-size: var(--t-13); -} - -.results-toolbar { - display: flex; - align-items: center; - gap: var(--s-8); - flex-wrap: wrap; - margin-top: var(--s-12); -} - -.results-count { - margin-left: auto; - color: var(--text-mute); - font-size: var(--t-12); - font-family: var(--font-mono); - font-variant-numeric: tabular-nums; -} - -.results-scroll { - flex: 1; - min-height: 0; - overflow-y: auto; - overscroll-behavior: contain; -} - -.results-scroll .list { - border: 0; - border-radius: 0; - box-shadow: none; -} - -.results-foot { - flex: none; - padding: var(--s-12) var(--s-20); - border-top: 1px solid var(--border); - background: var(--surface-sunken); - color: var(--text-mute); - font-size: var(--t-12); - line-height: 1.6; -} - -/* 结果区里的「再补一样」提示 */ -.unlock { - display: flex; - align-items: flex-start; - gap: var(--s-12); - padding: var(--s-12) var(--s-20); - border-bottom: 1px solid var(--border); - background: var(--primary-soft); -} - -.unlock-label { - flex: none; - padding-top: 7px; - color: var(--primary-ink); - font-size: var(--t-12); - font-weight: 600; -} - -.unlock-row { - display: flex; - flex-wrap: wrap; - gap: 6px; -} - -/* 工具选择(厨具) */ -.tool-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); - gap: var(--s-8); -} - -/* 菜谱库:工具栏 */ -.lib-toolbar { - display: flex; - align-items: center; - gap: var(--s-12); - flex-wrap: wrap; - margin-bottom: var(--s-16); -} - -.lib-toolbar .search { - flex: 1; - min-width: 240px; - max-width: 420px; -} - -.lib-count { - margin-left: auto; - color: var(--text-mute); - font-size: var(--t-13); -} - -.lib-count b { - color: var(--text); - font-family: var(--font-mono); - font-variant-numeric: tabular-nums; -} - -/* 详情页主从布局 */ -.detail-layout { - display: grid; - gap: var(--s-24); - align-items: start; -} - -.detail-main { - min-width: 0; -} - -.detail-side { - min-width: 0; -} - -.detail-hero { - display: flex; - align-items: center; - gap: var(--s-20); - padding: var(--s-24); - border: 1px solid var(--border); - border-radius: var(--r-lg); - background: var(--surface); - box-shadow: var(--sh-sm); -} - -.detail-hero .thumb { - display: inline-flex; - align-items: center; - justify-content: center; - width: 88px; - height: 88px; - flex: none; - border-radius: var(--r-xl); - background: var(--bg-alt); - font-size: 46px; -} - -.detail-hero h1 { - margin: 0 0 6px; - font: 600 var(--t-28) / 1.25 var(--font-display); -} - -.detail-hero p { - margin: 0; - color: var(--text-mute); - font-size: var(--t-13); -} - -.detail-actions { - display: flex; - gap: var(--s-8); - flex-wrap: wrap; - margin-top: var(--s-16); -} - -.detail-facts { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); - gap: var(--s-12); - margin-top: var(--s-16); -} - -.fact { - padding: var(--s-12) var(--s-16); - border: 1px solid var(--border); - border-radius: var(--r-md); - background: var(--surface); -} - -.fact span { - display: block; - color: var(--text-mute); - font-size: var(--t-11); - letter-spacing: 0.04em; -} - -.fact b { - display: block; - margin-top: 3px; - font: 500 var(--t-15) / 1.4 var(--font-body); -} - -/* 移动端底部的结果概览条(桌面隐藏) */ -.cook-mobile-bar { - position: fixed; - inset: auto 0 calc(var(--tabbar-h) + env(safe-area-inset-bottom)) 0; - z-index: 55; - display: none; - align-items: center; - gap: var(--s-12); - padding: var(--s-10, 10px) var(--s-16); - border-top: 1px solid var(--border); - background: color-mix(in srgb, var(--surface) 92%, transparent); - backdrop-filter: blur(12px); - box-shadow: 0 -4px 18px rgba(35, 32, 28, 0.07); -} - -.cook-mobile-bar strong { - display: block; - font: 600 var(--t-15) / 1.3 var(--font-body); -} - -.cook-mobile-bar span { - display: block; - color: var(--text-mute); - font-size: var(--t-12); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.cook-mobile-copy { - flex: 1; - min-width: 0; -} - -/* 下拉选择控件 */ -.select-pill { - min-height: 30px; - padding: 0 26px 0 11px; - border: 1px solid var(--border); - border-radius: var(--r-pill); - background: var(--surface); - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%239a9186' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E"); - background-repeat: no-repeat; - background-position: right 9px center; - color: var(--text); - font: 500 var(--t-13) / 1 var(--font-body); - appearance: none; - cursor: pointer; -} - -.select-pill:hover { - border-color: var(--border-strong); -} - -/* 调料 / 忌口 里的开关行 */ -.toggle-row { - display: flex; - align-items: center; - gap: var(--s-12); - min-height: 40px; -} - -.toggle-row .label { - flex: 1; - font-size: var(--t-14); -} - -/* 仅桌面 / 仅移动 */ -.only-desktop { - display: none; -} - -/* 菜谱库:桌面默认表格、手机默认列表,可手动切换 */ -.lib-list { - display: none; -} - -@media (max-width: 1023px) { - .lib-table { - display: none; - } - - .lib-list { - display: block; - } -} - -@media (min-width: 1024px) { - html[data-lib-view="list"] .lib-table { - display: none; - } - - html[data-lib-view="list"] .lib-list { - display: block; - } -} - -/* 列表行里的可点击主体(避免在 button 里再套 button) */ -.list-row-main { - display: flex; - align-items: center; - gap: var(--s-12); - flex: 1; - min-width: 0; - padding: 0; - border: 0; - background: transparent; - color: inherit; - font: inherit; - text-align: left; - cursor: pointer; -} - -/* 卡片里的小图标底座 */ -.ico { - display: inline-flex; - align-items: center; - justify-content: center; - width: 32px; - height: 32px; - flex: none; - border-radius: var(--r-sm); - background: var(--bg-alt); - color: var(--text-sub); -} - -.ico svg { - width: 17px; - height: 17px; -} - -/* ========================================================================== - 20. 响应式 - ========================================================================== */ - -/* 移动端(< 1024px):侧边栏收起,顶栏 + 底部 tab */ -@media (max-width: 1023px) { - .app-rail { - display: none !important; - } - - .app-main { - padding-top: calc(var(--appbar-h) + env(safe-area-inset-top) + var(--s-16)); - } - - .page-title { - font-size: var(--t-22); - } - - .page-head { - align-items: flex-start; - } - - .hero-card { - padding: var(--s-24); - } - - .hero-card h2 { - font-size: var(--t-28); - } - - .menu-grid { - grid-template-columns: 1fr; - } - - /* 移动端:结果区回到常规流,底部固定一条概览条 */ - .cook-results { - position: static; - max-height: none; - } - - .cook-results .results-scroll { - max-height: none; - overflow-y: visible; - } - - .cook-mobile-bar { - display: flex; - } - - .modal { - align-items: flex-end; - padding: 0; - } - - .modal-panel { - max-width: 100%; - max-height: 88vh; - border-radius: var(--r-xl) var(--r-xl) 0 0; - animation: sheet-up 0.32s cubic-bezier(0.22, 0.9, 0.3, 1) both; - } - - @keyframes sheet-up { - from { - transform: translateY(28px); - opacity: 0; - } - } - - .modal-hero { - flex-direction: column; - align-items: flex-start; - gap: var(--s-12); - text-align: left; - } - - .modal-actions { - flex-wrap: wrap; - } - - .modal-actions .btn { - min-width: 44px; - } -} - -/* 桌面(≥ 1024px):左侧导航栏 + 内容区 */ -@media (min-width: 1024px) { - .app-rail { - display: flex; - } - - .app-bar { - display: none; - } - - .tabbar { - display: none; - } - - .app-main { - margin-left: var(--rail-w); - padding: var(--s-32) var(--content-pad) var(--s-48); - } - - .toast { - bottom: 24px; - } - - /* 做菜页:左侧选菜,右侧结果常驻 */ - .cook-split { - grid-template-columns: minmax(340px, 400px) minmax(0, 1fr); - } - - .cook-side { - position: sticky; - top: var(--s-32); - max-height: calc(100vh - var(--s-64)); - overflow-y: auto; - padding-right: var(--s-8); - overscroll-behavior: contain; - } - - .cook-side::-webkit-scrollbar { - width: 6px; - } - - .cook-side::-webkit-scrollbar-thumb { - border-radius: var(--r-pill); - background: var(--border-strong); - } - - /* 详情页:主内容 + 侧栏 */ - .detail-layout { - grid-template-columns: minmax(0, 1fr) 300px; - } - - .only-desktop { - display: revert; - } -} - -/* 大屏留白 */ -@media (min-width: 1600px) { - :root { - --content-max: 1280px; - } -} - -@media (max-width: 420px) { - .chip { - min-height: 36px; - padding: 0 12px; - font-size: var(--t-14); - } - - .cuisine-grid { - grid-template-columns: 1fr 1fr; - } -} - -@media (prefers-reduced-motion: reduce) { - *, - *::before, - *::after { - animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; - transition-duration: 0.01ms !important; - } -} diff --git a/prototype/cook-redesign/app.js b/prototype/cook-redesign/app.js deleted file mode 100644 index 3198d66..0000000 --- a/prototype/cook-redesign/app.js +++ /dev/null @@ -1,678 +0,0 @@ -/** - * 食用手册 · 原型交互逻辑 - * 纯前端,状态存在内存,刷新即重置 - */ -;(function () { - const { icon } = window.Icons - const M = window.Mock - - // ---------- 状态 ---------- - const state = { - tab: 'cook', - selected: [], - tool: '', - methodGroups: [], - cuisines: [], - difficulty: [], - keyword: '', - mode: 'loose', - sheet: 'collapsed', - activeGroup: 0, - groupLocked: false, - libraryView: 'all', - libraryKeyword: '', - favorites: [], - cuisineFocus: '', - guessId: null, - } - - const $ = sel => document.querySelector(sel) - const $$ = sel => Array.from(document.querySelectorAll(sel)) - - function esc(text) { - return String(text ?? '').replace(/[&<>"']/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' })[c]) - } - - function toast(message) { - const el = $('#toast') - el.textContent = message - el.classList.add('is-on') - clearTimeout(toast._t) - toast._t = setTimeout(() => el.classList.remove('is-on'), 1800) - } - - // ---------- 渲染图标 ---------- - function paintIcons(root = document) { - root.querySelectorAll('[data-icon]').forEach(el => { - if (el.dataset.painted) return - el.innerHTML = icon(el.dataset.icon) - el.dataset.painted = '1' - }) - } - - // ---------- 做菜页 ---------- - function renderTray() { - const tray = $('#tray') - const hint = $('#tray-hint') - if (!state.selected.length) { - tray.innerHTML = '还没选食材 —— 下面挑几样,或者直接搜名字' - hint.textContent = '' - hint.classList.remove('on') - return - } - tray.innerHTML = state.selected.map(name => { - const emoji = M.EMOJI_MAP[name] || '' - return `` - }).join('') - hint.textContent = `已选 ${state.selected.length} 种` - hint.classList.add('on') - } - - function renderFoodChips() { - $$('[data-foods]').forEach(box => { - const group = box.dataset.foods - box.innerHTML = M.FOOD[group].map(item => { - const on = state.selected.includes(item.name) - return `` - }).join('') - }) - $$('[data-count]').forEach(el => { - const group = el.dataset.count - const n = M.FOOD[group].filter(i => state.selected.includes(i.name)).length - el.textContent = n ? `已选 ${n}` : '' - el.classList.toggle('on', n > 0) - }) - } - - function renderSuggestions(query) { - const box = $('#search-suggest') - const text = query.trim() - if (!text) { - box.innerHTML = '' - return - } - const hits = M.ALL_FOODS - .filter(i => !state.selected.includes(i.name) && (i.name.includes(text) || (i.alias || '').includes(text))) - .slice(0, 6) - box.innerHTML = hits.map(i => ``).join('') - } - - function renderFilterChips() { - $('#chips-tool').innerHTML = M.FOOD.tools.map(t => { - const on = state.tool === t.name - const label = t.name === '一口大锅' ? '炒锅' : t.name - return `` - }).join('') - - $('#chips-method').innerHTML = M.METHOD_GROUPS.map(g => { - const on = state.methodGroups.includes(g.label) - return `` - }).join('') - - $('#chips-cuisine').innerHTML = M.CUISINE_GROUPS.map(g => { - const on = state.cuisines.includes(g.label) - return `` - }).join('') - - $('#chips-difficulty').innerHTML = M.DIFFICULTY_LEVELS.map(d => { - const on = state.difficulty.includes(d) - return `` - }).join('') - - paintIcons($('#chips-tool')) - } - - function renderActiveFilters() { - const tags = [] - if (state.tool) { - tags.push({ key: 'tool', value: state.tool, label: `厨具:${state.tool === '一口大锅' ? '炒锅' : state.tool}` }) - } - state.methodGroups.forEach(label => tags.push({ key: 'method', value: label, label: `做法:${label}` })) - state.cuisines.forEach(label => tags.push({ key: 'cuisine', value: label, label: `菜系:${label}` })) - state.difficulty.forEach(label => tags.push({ key: 'difficulty', value: label, label: `难度:${label}` })) - - const box = $('#active-filters') - box.innerHTML = tags.map(t => ``).join('') - - const count = tags.length - const badge = $('#filter-count') - badge.textContent = String(count) - badge.hidden = count === 0 - $('#filter-title').textContent = count ? '筛选条件' : '筛选条件(厨具 / 做法 / 菜系 / 难度)' - } - - function currentMatches() { - return M.findRecipes({ - stuff: state.selected, - tool: state.tool, - methodGroups: state.methodGroups, - cuisines: state.cuisines, - difficulty: state.difficulty, - keyword: state.keyword, - mode: state.mode, - }) - } - - function renderSheet() { - const matches = currentMatches() - const hasSelection = state.selected.length > 0 || Boolean(state.tool) - const title = $('#sheet-title') - const sub = $('#sheet-sub') - const action = $('#sheet-action') - const peek = $('#sheet-peek') - const list = $('#sheet-list') - const toolbar = $('#sheet-toolbar') - - if (!hasSelection) { - title.textContent = '选好食材,看看能做什么' - sub.textContent = '厨具可选,不选也能找菜谱' - action.textContent = '试试番茄+鸡蛋' - peek.innerHTML = '' - list.innerHTML = '' - toolbar.innerHTML = '' - $('#cook-sub').textContent = '选好食材,看看能做什么' - return - } - - const groups = [ - { label: '齐全', missing: 0, hint: '食材都有' }, - { label: '差一种', missing: 1, hint: '补一种就能做' }, - { label: '差两种', missing: 2, hint: '补两种就能做' }, - ] - const counts = groups.map(g => matches.filter(m => m.missingStuff.length === g.missing).length) - const total = matches.length - - // 当前分组可能因为改了筛选而变空,回退到第一个有内容的分组; - // 但用户手动点选的分组保持不动(即使为空,列表里给出空状态提示) - let active = state.activeGroup - if (!state.groupLocked && !counts[active]) { - const fallback = counts.findIndex(c => c > 0) - active = fallback === -1 ? 0 : fallback - state.activeGroup = active - } - - title.textContent = total ? `可以做 ${total} 道菜` : '暂时没有匹配的菜' - sub.textContent = total ? '拖动这里展开,挑一道喜欢的' : '换个食材,或放宽筛选条件' - action.textContent = total ? '查看菜谱' : '调整筛选' - $('#cook-sub').textContent = state.selected.length ? `已选 ${state.selected.join('、')}` : '按厨具找菜' - - // 收起态:横向预览前几张 - peek.innerHTML = matches.slice(0, 6).map(m => { - const r = m.recipe - return `` - }).join('') - - // 展开态:工具条 + 列表 - toolbar.innerHTML = ` -
- ${groups.map((g, i) => ``).join('')} -
- - ` - - if (!total) { - const tips = M.unlockSuggestions(state.selected) - list.innerHTML = `
- ${icon('bowl')} -

这一组暂时没有菜

-

${tips.length ? '再补一样试试?' : '换个食材,或者清除筛选条件'}

- ${tips.length ? `
${tips.map(t => ``).join('')}
` : ''} - ${state.tool || state.methodGroups.length || state.cuisines.length || state.difficulty.length ? '' : ''} -
` - return - } - - const rows = matches.filter(m => m.missingStuff.length === active) - const shown = rows.slice(0, 12) - if (!shown.length) { - list.innerHTML = `
${icon('bowl')}

这一组暂时是空的

换一组看看,或者放宽筛选条件

` - return - } - list.innerHTML = shown.map((m, i) => { - const r = m.recipe - const cuisine = M.recipeCuisine(r) - const have = m.matchedStuff - return `` - }).join('') + (rows.length > shown.length ? `` : '') - } - function renderAll() { - renderTray() - renderFoodChips() - renderFilterChips() - renderActiveFilters() - renderSheet() - renderLibrary() - renderDiscover() - } - - // ---------- 菜谱页 ---------- - function renderLibrary() { - const keyword = state.libraryKeyword.trim() - let list = M.RECIPES.slice() - if (state.libraryView === 'fav') list = list.filter(r => state.favorites.includes(r.id)) - if (keyword) list = list.filter(r => r.name.includes(keyword)) - - $('#library-sub').textContent = `共 ${M.RECIPES.length} 道菜 · 收藏 ${state.favorites.length} 道` - const seg = $('#library-segment') - seg.children[0].querySelector('.n').textContent = String(M.RECIPES.length) - seg.children[1].querySelector('.n').textContent = String(state.favorites.length) - seg.children[0].classList.toggle('is-on', state.libraryView === 'all') - seg.children[1].classList.toggle('is-on', state.libraryView === 'fav') - - const box = $('#library-list') - if (!list.length) { - box.innerHTML = `
${icon('book')}

${keyword ? '没有找到相关菜谱' : '还没有收藏的菜谱'}

${keyword ? '换个关键词试试' : '点右侧星标即可收藏'}

` - return - } - box.innerHTML = list.map(r => { - const cuisine = M.recipeCuisine(r) - const fav = state.favorites.includes(r.id) - return `
- - - ${esc(r.name)} - ${esc(r.stuff.join('·'))}${cuisine ? ` · ${esc(cuisine)}` : ''} - - -
` - }).join('') - } - - // ---------- 发现页 ---------- - function renderDiscover() { - const cuisine = state.cuisineFocus - if (cuisine) { - const list = M.recipesByCuisine(cuisine) - $('#hot-title').textContent = `${cuisine}菜谱` - $('#hot-hint').textContent = `${list.length} 道` - $('#hot-list').innerHTML = list.length - ? list.map(r => `
- - - ${esc(r.name)} - ${esc(r.stuff.join('·'))} - -
`).join('') - : `
${icon('utensils')}

${esc(cuisine)}还没有菜谱

投稿帮我们补上这一道

` - } - else { - const hot = M.RECIPES.filter(r => r.bv).slice(0, 8) - $('#hot-title').textContent = '热门菜谱' - $('#hot-hint').textContent = '最多人做' - $('#hot-list').innerHTML = hot.map(r => `
- - - ${esc(r.name)} - ${esc(r.stuff.join('·'))}${M.recipeCuisine(r) ? ` · ${esc(M.recipeCuisine(r))}` : ''} - -
`).join('') - } - - const grid = $('#cuisine-grid') - grid.innerHTML = M.CUISINE_GROUPS.map(g => { - const n = M.recipesByCuisine(g.label).length - const on = state.cuisineFocus === g.label - return `` - }).join('') - - // 随机菜只在首次或主动换一道时重摇,不随其它交互变化 - if (!state.guessId || !M.RECIPES.some(r => r.id === state.guessId)) { - const guess = M.guessRecipe() - if (guess) { - state.guessId = guess.id - $('#discover-name').textContent = guess.name - $('#discover-meta').textContent = `${guess.stuff.join(' · ')} · ${guess.methods.join('/')}` - } - } - } - - // ---------- 详情弹层 ---------- - function openDish(id) { - const r = M.RECIPES.find(x => x.id === id) - if (!r) return - const cuisine = M.recipeCuisine(r) - const tool = (r.tools || []).map(t => (t === '一口大锅' ? '炒锅' : t)).join(' / ') - - $('#modal-thumb').textContent = M.recipeEmoji(r) - $('#modal-title').textContent = r.name - $('#modal-meta').textContent = [tool, r.difficulty, cuisine].filter(Boolean).join(' · ') - - const selected = new Set(state.selected) - const have = r.stuff.filter(s => selected.has(s)) - const miss = r.stuff.filter(s => !selected.has(s)) - const groups = [] - if (have.length) groups.push({ label: '已有', items: have, tone: 'have' }) - if (miss.length) groups.push({ label: '还缺', items: miss, tone: 'missing' }) - if (!have.length && !miss.length) groups.push({ label: '食材', items: r.stuff, tone: 'neutral' }) - - $('#modal-groups').innerHTML = groups.map(g => ` -
-

${g.label} · ${g.items.length} 种

-
- ${g.items.map(i => `${esc(i)}`).join('')} -
-
`).join('') - - const fav = state.favorites.includes(r.id) - $('#modal-fav').classList.toggle('is-on', fav) - $('#modal-video').style.display = r.bv ? '' : 'none' - $('#modal').dataset.id = String(r.id) - $('#modal').classList.add('is-open') - } - - function closeDish() { - $('#modal').classList.remove('is-open') - } - - // ---------- 事件绑定 ---------- - function bind() { - // 顶部图标按钮 - $('#btn-clear').innerHTML = icon('refresh') - $('#btn-new').innerHTML = icon('plus') - $('#filter-caret').innerHTML = icon('chevronDown') - $('#account-avatar').innerHTML = icon('user') - $('#btn-guess').insertAdjacentHTML('afterbegin', icon('sparkle')) - - // Tab 切换 - $('#tabbar').addEventListener('click', e => { - const a = e.target.closest('a[data-nav]') - if (!a) return - e.preventDefault() - go(a.dataset.nav) - }) - - // 食材交互 - document.addEventListener('click', e => { - const add = e.target.closest('[data-add]') - if (add) { - const name = add.dataset.add - if (!state.selected.includes(name)) state.selected.push(name) - renderAll() - return - } - const remove = e.target.closest('[data-remove]') - if (remove) { - state.selected = state.selected.filter(n => n !== remove.dataset.remove) - renderAll() - return - } - const stuff = e.target.closest('[data-stuff]') - if (stuff) { - const name = stuff.dataset.stuff - state.selected = state.selected.includes(name) - ? state.selected.filter(n => n !== name) - : [...state.selected, name] - renderAll() - return - } - const tool = e.target.closest('[data-tool]') - if (tool) { - state.tool = state.tool === tool.dataset.tool ? '' : tool.dataset.tool - renderAll() - return - } - const method = e.target.closest('[data-method]') - if (method) { - const label = method.dataset.method - state.methodGroups = state.methodGroups.includes(label) - ? state.methodGroups.filter(m => m !== label) - : [...state.methodGroups, label] - renderAll() - return - } - const cuisine = e.target.closest('[data-cuisine]') - if (cuisine) { - const label = cuisine.dataset.cuisine - state.cuisines = state.cuisines.includes(label) - ? state.cuisines.filter(c => c !== label) - : [...state.cuisines, label] - renderAll() - return - } - const diff = e.target.closest('[data-difficulty]') - if (diff) { - const label = diff.dataset.difficulty - state.difficulty = state.difficulty.includes(label) - ? state.difficulty.filter(d => d !== label) - : [...state.difficulty, label] - renderAll() - return - } - const clear = e.target.closest('[data-clear]') - if (clear) { - const key = clear.dataset.clear - const value = clear.dataset.value - if (key === 'tool') state.tool = '' - if (key === 'method') state.methodGroups = state.methodGroups.filter(m => m !== value) - if (key === 'cuisine') state.cuisines = state.cuisines.filter(c => c !== value) - if (key === 'difficulty') state.difficulty = state.difficulty.filter(d => d !== value) - renderAll() - return - } - if (e.target.closest('[data-clear-all]')) { - state.tool = '' - state.methodGroups = [] - state.cuisines = [] - state.difficulty = [] - renderAll() - return - } - const dish = e.target.closest('[data-dish]') - if (dish && !e.target.closest('[data-fav]')) { - openDish(Number(dish.dataset.dish)) - return - } - const fav = e.target.closest('[data-fav]') - if (fav) { - e.stopPropagation() - const id = Number(fav.dataset.fav) - const inList = state.favorites.includes(id) - state.favorites = inList ? state.favorites.filter(f => f !== id) : [...state.favorites, id] - toast(inList ? '已从收藏移除' : '已添加到收藏') - renderAll() - return - } - const group = e.target.closest('[data-group]') - if (group) { - state.activeGroup = Number(group.dataset.group) - state.groupLocked = true - renderSheet() - return - } - const cuisineOpen = e.target.closest('[data-cuisine-open]') - if (cuisineOpen) { - state.cuisineFocus = state.cuisineFocus === cuisineOpen.dataset.cuisineOpen ? '' : cuisineOpen.dataset.cuisineOpen - renderDiscover() - toast(state.cuisineFocus ? `正在看${state.cuisineFocus}` : '已回到热门菜谱') - return - } - const menu = e.target.closest('[data-me]') - if (menu) { - toast('原型演示:这里接原来的页面') - } - }) - - // 搜索 - $('#stuff-search').addEventListener('input', e => { - renderSuggestions(e.target.value) - }) - $('#library-search').addEventListener('input', e => { - state.libraryKeyword = e.target.value - renderLibrary() - }) - - // 筛选折叠 - $('#filter-toggle').addEventListener('click', () => { - const box = $('#filters') - const open = box.classList.toggle('is-open') - $('#filter-toggle').setAttribute('aria-expanded', String(open)) - }) - - // 清空 - $('#btn-clear').addEventListener('click', () => { - state.selected = [] - state.tool = '' - state.methodGroups = [] - state.cuisines = [] - state.difficulty = [] - state.keyword = '' - renderAll() - toast('已清空') - }) - - // 菜谱页分段 - $('#library-segment').addEventListener('click', e => { - const btn = e.target.closest('[data-view-mode]') - if (!btn) return - state.libraryView = btn.dataset.viewMode - renderLibrary() - }) - - // 新建 - $('#btn-new').addEventListener('click', () => toast('原型演示:新建菜谱页')) - - // 换一道 - $('#btn-guess').addEventListener('click', () => { - state.guessId = null - renderDiscover() - toast('换了一道') - }) - - // 模式选择(事件委托到 sheet 上,toolbar 每次重绘都会替换节点) - $('#sheet').addEventListener('change', e => { - if (e.target.id === 'mode-select') { - state.mode = e.target.value - renderSheet() - } - }) - - // 结果面板:三档切换 + 拖动 - const sheet = $('#sheet') - function setSheet(next) { - state.sheet = next - sheet.classList.remove('is-collapsed', 'is-half', 'is-full') - sheet.classList.add(`is-${next}`) - } - $('#sheet-grab').addEventListener('click', () => { - setSheet(state.sheet === 'full' ? 'collapsed' : 'full') - }) - $('#sheet-action').addEventListener('click', () => { - if (!state.selected.length && !state.tool) { - state.selected = ['番茄', '鸡蛋'] - renderAll() - setSheet('half') - return - } - setSheet(state.sheet === 'full' ? 'collapsed' : 'full') - }) - - // 拖拽把手 - let startY = 0 - let startOffset = 0 - let dragging = false - const grab = $('#sheet-grab') - grab.addEventListener('pointerdown', e => { - dragging = true - startY = e.clientY - startOffset = 0 - grab.setPointerCapture(e.pointerId) - }) - grab.addEventListener('pointermove', e => { - if (!dragging) return - const dy = e.clientY - startY - if (Math.abs(dy) < 4) return - const h = sheet.offsetHeight - if (dy < -40 && state.sheet !== 'full') setSheet('full') - if (dy > 40 && state.sheet === 'full') setSheet('half') - if (dy > 120 && state.sheet === 'half') setSheet('collapsed') - }) - grab.addEventListener('pointerup', () => { - dragging = false - }) - - // 弹层 - $('#modal').addEventListener('click', e => { - if (e.target.id === 'modal') closeDish() - }) - $('#modal-search').addEventListener('click', () => { - const id = Number($('#modal').dataset.id) - const r = M.RECIPES.find(x => x.id === id) - if (!r) return - window.open(`https://search.bilibili.com/all?keyword=${encodeURIComponent(r.name)}`, '_blank') - }) - $('#modal-video').addEventListener('click', () => { - const id = Number($('#modal').dataset.id) - const r = M.RECIPES.find(x => x.id === id) - if (!r || !r.bv) return - window.open(`https://www.bilibili.com/video/${r.bv}`, '_blank') - }) - $('#modal-fav').addEventListener('click', () => { - const id = Number($('#modal').dataset.id) - const inList = state.favorites.includes(id) - state.favorites = inList ? state.favorites.filter(f => f !== id) : [...state.favorites, id] - $('#modal-fav').classList.toggle('is-on', !inList) - toast(inList ? '已从收藏移除' : '已添加到收藏') - renderLibrary() - }) - - // 键盘:Esc 关弹层 - document.addEventListener('keydown', e => { - if (e.key === 'Escape') closeDish() - }) - } - - // ---------- 路由 ---------- - function go(tab) { - state.tab = tab - $$('.view').forEach(v => v.classList.toggle('is-active', v.dataset.view === tab)) - $$('#tabbar a').forEach(a => { - const on = a.dataset.nav === tab - a.classList.toggle('is-active', on) - if (on) a.setAttribute('aria-current', 'page') - else a.removeAttribute('aria-current') - }) - window.scrollTo({ top: 0, behavior: 'smooth' }) - } - - function boot() { - paintIcons() - bind() - renderAll() - const hash = location.hash.replace('#/', '') - if (['cook', 'library', 'discover', 'me'].includes(hash)) go(hash) - } - - document.addEventListener('DOMContentLoaded', boot) -})() diff --git a/prototype/cook-redesign/icons.js b/prototype/cook-redesign/icons.js deleted file mode 100644 index 1b43ddb..0000000 --- a/prototype/cook-redesign/icons.js +++ /dev/null @@ -1,55 +0,0 @@ -/** - * 内联 SVG 图标集(Lucide 路径风格,零外部依赖,离线可用) - * 只提供 UI 图标;菜品缩略图使用 emoji 作为内容语义 - */ -const ICONS = { - search: '', - plus: '', - x: '', - chefHat: '', - book: '', - compass: '', - user: '', - star: '', - play: '', - chevronDown: '', - chevronRight: '', - sliders: '', - refresh: '', - clock: '', - history: '', - settings: '', - info: '', - message: '', - mail: '', - fileText: '', - helpCircle: '', - logIn: '', - cloud: '', - utensils: '', - trash: '', - edit: '', - share: '', - flame: '', - bowl: '', - grid: '', - sparkle: '', - layers: '', - wallet: '', - external: '', - - // 厨具 - oven: '', - fryer: '', - microwave: '', - riceCooker: '', - wok: '', - clayPot: '', -} - -function icon(name, cls = '') { - const path = ICONS[name] || '' - return `` -} - -window.Icons = { icon, ICONS } diff --git a/prototype/cook-redesign/index.html b/prototype/cook-redesign/index.html deleted file mode 100644 index edd0f7f..0000000 --- a/prototype/cook-redesign/index.html +++ /dev/null @@ -1,315 +0,0 @@ - - - - - - 食用手册 · 界面重新设计 - - - -
-
- - -
-
-
-

今天做点什么

-

选好食材,看看能做什么

-
- -
- -
- -
-
-

我的食材

- -
-
- 还没选食材 —— 下面挑几样,或者直接搜名字 -
-
- - -
- -
-
- - -
-
-

蔬菜

- -
-
-
- -
-
-

肉和蛋

- -
-
-
- -
-
-

主食

- -
-
-
- - -
-
- -
-
-

厨具

-
-
-
-

做法

-
-
-
-

菜系

-
-
-
-

难度

-
-
-
-
-
-
-
-
- - -
-
-
-

菜谱库

-

-
- -
-
-
- -
-
-
- - -
-
-
-
-
-
-
- - -
-
-
-

发现

-

懒得想吃什么的时候

-
-
-
-
-
-

今天吃什么

-

番茄炒蛋

-

番茄 · 鸡蛋 · 炒

- -
-
- -
-
-

按菜系逛

- 点进去看看 -
-
-
- -
-
-

热门菜谱

- 最多人做 -
-
-
-
-
- - -
-
-
-

我的

-

收藏、历史和设置

-
-
-
-
- -
- -
- -
- -
- -
- -
- -
-
-
-
- - - - - - - - - - -
-
- - - - - - diff --git a/prototype/cook-redesign/mock.js b/prototype/cook-redesign/mock.js deleted file mode 100644 index 5343f53..0000000 --- a/prototype/cook-redesign/mock.js +++ /dev/null @@ -1,284 +0,0 @@ -/** - * 原型 mock 数据层 - * 菜谱来自 packages/data/recipe.json 真实数据,食材/厨具词典来自 packages/data/food.json - * 所有接口都是本地函数,不发真实请求 - */ - -// ---------- 食材与厨具词典 ---------- -const FOOD = { - vegetable: [ - { name: '土豆', emoji: '🥔' }, - { name: '胡萝卜', emoji: '🥕' }, - { name: '花菜', emoji: '🥦' }, - { name: '白萝卜', emoji: '🥣' }, - { name: '西葫芦', emoji: '🥒' }, - { name: '番茄', emoji: '🍅', alias: '西红柿' }, - { name: '芹菜', emoji: '🥬' }, - { name: '黄瓜', emoji: '🥒' }, - { name: '洋葱', emoji: '🧅' }, - { name: '莴笋', emoji: '🎍' }, - { name: '菌菇', emoji: '🍄' }, - { name: '茄子', emoji: '🍆' }, - { name: '豆腐', emoji: '🍲' }, - { name: '包菜', emoji: '🥗' }, - { name: '白菜', emoji: '🥬' }, - { name: '青椒', emoji: '🫑', alias: '青辣椒' }, - { name: '韭菜', emoji: '🌿' }, - { name: '南瓜', emoji: '🎃' }, - ], - meat: [ - { name: '午餐肉', emoji: '🥓' }, - { name: '香肠', emoji: '🌭' }, - { name: '腊肠', emoji: '🌭' }, - { name: '鸡肉', emoji: '🐤' }, - { name: '猪肉', emoji: '🐷' }, - { name: '鸡蛋', emoji: '🥚' }, - { name: '虾', emoji: '🦐' }, - { name: '牛肉', emoji: '🐮' }, - { name: '骨头', emoji: '🦴' }, - { name: '鱼', emoji: '🐟' }, - { name: '蟹', emoji: '🦀' }, - ], - staple: [ - { name: '面食', emoji: '🍝' }, - { name: '面包', emoji: '🍞' }, - { name: '米', emoji: '🍚' }, - { name: '方便面', emoji: '🍜' }, - { name: '玉米', emoji: '🌽', alias: '苞米' }, - { name: '红薯', emoji: '🍠', alias: '地瓜' }, - ], - tools: [ - { name: '烤箱', emoji: '', icon: 'oven' }, - { name: '空气炸锅', emoji: '', icon: 'fryer' }, - { name: '微波炉', emoji: '', icon: 'microwave' }, - { name: '电饭煲', emoji: '', icon: 'riceCooker' }, - { name: '一口大锅', emoji: '', icon: 'wok', label: '一口能炒又能煮的大锅' }, - { name: '砂锅', emoji: '', icon: 'clayPot' }, - ], -} - -// ---------- 菜谱(真实数据抽样) ---------- -const RECIPES = [ - { id: 598, name: '麻婆豆腐', stuff: ['豆腐'], bv: 'BV1it4y1X75m', difficulty: '简单', tags: ['辣', '川菜'], methods: ['烧'], tools: ['一口大锅'] }, - { id: 433, name: '宫保鸡丁', stuff: ['鸡肉', '黄瓜'], bv: 'BV1wE411T7X5', difficulty: '', tags: ['辣', '川菜'], methods: ['炒'], tools: ['一口大锅'] }, - { id: 253, name: '回锅肉炒包菜', stuff: ['包菜', '猪肉'], bv: 'BV1EK411s75a', difficulty: '普通', tags: ['家常菜', '川菜'], methods: ['炒'], tools: ['一口大锅'] }, - { id: 218, name: '水煮肉片', stuff: ['猪肉', '芹菜', '莴笋'], bv: 'BV1ZZ4y1379N', difficulty: '普通', tags: ['川菜', '辣'], methods: ['炒'], tools: ['一口大锅'] }, - { id: 577, name: '鱼香茄子', stuff: ['茄子', '鸡肉'], bv: 'BV1yL411E7oP', difficulty: '普通', tags: ['辣', '川菜'], methods: ['烧'], tools: ['一口大锅'] }, - { id: 476, name: '辣子鸡丁', stuff: ['鸡肉'], bv: 'BV1Ma4y147MA', difficulty: '', tags: ['辣', '川菜'], methods: ['炸', '炒'], tools: ['一口大锅'] }, - { id: 525, name: '水煮牛肉', stuff: ['牛肉', '菌菇', '莴笋', '豆腐'], bv: 'BV11q4y1z7dD', difficulty: '困难', tags: ['辣', '川菜'], methods: ['煮'], tools: ['一口大锅'] }, - { id: 400, name: '川味泡面', stuff: ['方便面', '鸡蛋'], bv: 'BV1G7411f7FA', difficulty: '简单', tags: ['辣', '川菜'], methods: ['煮'], tools: ['一口大锅'] }, - { id: 429, name: '干锅花菜', stuff: ['花菜', '洋葱', '米'], bv: 'BV1Rb4y147bZ', difficulty: '普通', tags: ['辣'], methods: ['炒'], tools: ['一口大锅'] }, - { id: 399, name: '潮汕杂菇煲', stuff: ['菌菇', '猪肉'], bv: 'BV15E411K7B5', difficulty: '简单', tags: ['粤菜'], methods: ['炒'], tools: ['一口大锅'] }, - { id: 617, name: '广式清蒸鱼', stuff: ['鱼'], bv: 'BV12m421V7Nh', difficulty: '简单', tags: ['粤菜'], methods: ['蒸'], tools: ['一口大锅'] }, - { id: 254, name: '腊肠蒸蛋', stuff: ['腊肠', '鸡蛋'], bv: 'BV1Zp4y1Q7wu', difficulty: '简单', tags: ['家常菜'], methods: ['蒸'], tools: ['一口大锅'] }, - { id: 221, name: '白灼大虾', stuff: ['虾'], bv: 'BV1Sr4y1m7zF', difficulty: '普通', tags: ['单一食材'], methods: ['灼'], tools: ['一口大锅'] }, - { id: 599, name: '清蒸鲈鱼', stuff: ['鱼'], bv: 'BV1QU4y1j7T4', difficulty: '普通', tags: [], methods: ['蒸'], tools: ['一口大锅'] }, - { id: 382, name: '白切肉', stuff: ['猪肉'], bv: 'BV15h411s7Ze', difficulty: '', tags: [], methods: ['煮'], tools: ['一口大锅'] }, - { id: 428, name: '富贵金钱蛋(湖南口味辣)', stuff: ['鸡蛋'], bv: 'BV15q4y1A784', difficulty: '', tags: ['辣', '湘菜'], methods: ['煎'], tools: ['一口大锅'] }, - { id: 410, name: '地三鲜', stuff: ['土豆', '茄子'], bv: 'BV1st4y1Y73H', difficulty: '', tags: ['东北菜'], methods: ['炸', '炒'], tools: ['一口大锅'] }, - { id: 411, name: '东北鸡蛋酱', stuff: ['鸡蛋'], bv: 'BV1TT4y127vu', difficulty: '', tags: ['东北菜'], methods: ['炒'], tools: ['一口大锅'] }, - { id: 393, name: 'biangbiang面', stuff: ['面食'], bv: 'BV1844y157GL', difficulty: '简单', tags: ['西北特色'], methods: ['油泼'], tools: ['一口大锅'] }, - { id: 439, name: '厚蛋烧', stuff: ['鸡蛋'], bv: 'BV1TT4y127vu', difficulty: '', tags: ['日式'], methods: ['煎'], tools: ['一口大锅'] }, - { id: 343, name: '日式炒胡萝卜丝', stuff: ['胡萝卜'], bv: 'BV1mY411774Z', difficulty: '简单', tags: ['消耗囤货', '日式'], methods: ['炒'], tools: ['一口大锅'] }, - { id: 386, name: '包菜大阪烧', stuff: ['包菜', '鸡蛋'], bv: 'BV1tA411F7Eq', difficulty: '普通', tags: ['日式'], methods: ['煎'], tools: ['一口大锅'] }, - { id: 585, name: '照烧鸡胸肉', stuff: ['鸡肉'], bv: 'BV12Q4y1K7CA', difficulty: '', tags: ['日式'], methods: ['煎'], tools: ['一口大锅'] }, - { id: 296, name: '炸虾天妇罗', stuff: ['虾'], bv: 'BV1e5411t7LY', difficulty: '困难', tags: ['日式'], methods: ['炸'], tools: ['一口大锅'] }, - { id: 515, name: '日式汉堡排(废手)', stuff: ['面包', '洋葱', '猪肉', '牛肉', '鸡蛋'], bv: 'BV1UV411Y7HM', difficulty: '', tags: ['日式'], methods: ['煎'], tools: ['一口大锅'] }, - { id: 294, name: '名古屋鸡翅', stuff: ['鸡肉'], bv: 'BV1ET4y1A7Xd', difficulty: '普通', tags: ['日式'], methods: ['炸'], tools: ['一口大锅'] }, - { id: 396, name: '娼妇意面', stuff: ['番茄', '洋葱'], bv: 'BV1hF411t7hS', difficulty: '普通', tags: ['意式'], methods: ['烧'], tools: ['一口大锅'] }, - { id: 344, name: '番茄肉酱面', stuff: ['番茄', '猪肉', '面食'], bv: 'BV1Cm4y1Q7RS', difficulty: '简单', tags: ['意式'], methods: ['烧'], tools: ['一口大锅'] }, - { id: 413, name: '法式炒蛋', stuff: ['鸡蛋'], bv: 'BV1TT4y127vu', difficulty: '', tags: ['法式'], methods: ['炒'], tools: ['一口大锅'] }, - { id: 414, name: '法式胡萝卜炖牛肉', stuff: ['胡萝卜', '牛肉', '洋葱'], bv: 'BV1UR4y1V7nV', difficulty: '困难', tags: ['法式'], methods: ['炖'], tools: ['一口大锅'] }, - { id: 383, name: '班尼迪克蛋', stuff: ['鸡蛋', '面包'], bv: 'BV1TT4y127vu', difficulty: '', tags: ['西式'], methods: ['煮'], tools: ['一口大锅'] }, - { id: 494, name: '牧羊人派', stuff: ['土豆', '猪肉'], bv: 'BV1fY41187Xo', difficulty: '普通', tags: ['西式'], methods: ['烤'], tools: ['一口大锅'] }, - { id: 575, name: '英式炒蛋', stuff: ['鸡蛋'], bv: 'BV1TT4y127vu', difficulty: '', tags: ['西式'], methods: ['炒'], tools: ['一口大锅'] }, - { id: 526, name: '苏格兰蛋', stuff: ['鸡蛋', '猪肉'], bv: 'BV1TT4y127vu', difficulty: '', tags: ['西式'], methods: ['炸'], tools: ['一口大锅'] }, - { id: 250, name: '番茄炒蛋', stuff: ['番茄', '鸡蛋'], bv: 'BV1rf4y1872R', difficulty: '普通', tags: ['家常菜'], methods: ['炒'], tools: ['一口大锅'] }, - { id: 629, name: '青椒炒肉', stuff: ['青椒', '猪肉'], bv: '', difficulty: '普通', tags: ['家常菜'], methods: ['炒'], tools: ['一口大锅'] }, - { id: 259, name: '酸辣土豆丝', stuff: ['土豆'], bv: 'BV1wq4y1W7QA', difficulty: '普通', tags: ['家常菜', '辣'], methods: ['炒'], tools: ['一口大锅'] }, - { id: 237, name: '蒜蓉西兰花', stuff: ['花菜'], bv: 'BV1fy4y1W7z5', difficulty: '简单', tags: ['单一食材'], methods: ['炒'], tools: ['一口大锅'] }, - { id: 317, name: '红烧肉', stuff: ['猪肉', '鸡肉'], bv: 'BV1MJ411t7pT', difficulty: '', tags: [], methods: ['烧'], tools: ['一口大锅'] }, - { id: 318, name: '可乐鸡翅', stuff: ['鸡肉'], bv: 'BV1pJ411v7S9', difficulty: '', tags: [], methods: ['烧'], tools: ['一口大锅'] }, - { id: 497, name: '拍黄瓜', stuff: ['黄瓜'], bv: 'BV1RV41167WV', difficulty: '简单', tags: [], methods: ['凉拌'], tools: ['一口大锅'] }, - { id: 220, name: '白萝卜汤', stuff: ['白萝卜'], bv: 'BV1HJ411L7xA', difficulty: '简单', tags: ['单一食材', '汤'], methods: ['煮'], tools: ['一口大锅'] }, - { id: 489, name: '罗宋汤', stuff: ['胡萝卜', '土豆', '番茄', '牛肉', '包菜'], bv: 'BV1394y1d7f6', difficulty: '普通', tags: ['汤'], methods: ['煮'], tools: ['一口大锅'] }, - { id: 588, name: '蒸蛋羹', stuff: ['鸡蛋'], bv: 'BV1Wt411Z7td', difficulty: '', tags: [], methods: ['蒸', '煮'], tools: ['一口大锅'] }, - { id: 352, name: '土豆丝饼', stuff: ['土豆'], bv: 'BV1fY41187Xo', difficulty: '简单', tags: ['早餐'], methods: ['煎'], tools: ['一口大锅'] }, - { id: 634, name: '玉米排骨汤', stuff: ['玉米', '骨头'], bv: '', difficulty: '普通', tags: ['汤'], methods: ['煲'], tools: ['一口大锅'] }, - { id: 375, name: '白萝卜煲', stuff: ['白萝卜', '骨头'], bv: 'BV1dU4y1u7eb', difficulty: '普通', tags: [], methods: ['煲'], tools: ['一口大锅', '砂锅'] }, - { id: 356, name: '萝卜砂锅粥', stuff: ['白萝卜', '米'], bv: 'BV1mz4y1C7H9', difficulty: '普通', tags: ['早饭'], methods: ['煮'], tools: ['一口大锅', '砂锅'] }, - { id: 472, name: '腊肠豆腐煲', stuff: ['腊肠', '豆腐', '米'], bv: 'BV1Sf4y1r7mS', difficulty: '普通', tags: [], methods: ['煲'], tools: ['一口大锅', '砂锅'] }, - { id: 517, name: '肉末土豆茄子煲', stuff: ['茄子', '土豆', '猪肉'], bv: 'BV1Fu411S78s', difficulty: '普通', tags: [], methods: ['煲'], tools: ['一口大锅', '砂锅'] }, - { id: 116, name: '空气炸锅地三鲜', stuff: ['土豆', '茄子'], bv: 'BV1pf4y1T7bx', difficulty: '简单', tags: [], methods: ['炸'], tools: ['空气炸锅'] }, - { id: 100, name: '空气炸锅版可乐鸡翅', stuff: ['鸡肉'], bv: 'BV1eT4y1S7Jn', difficulty: '', tags: [], methods: ['烧'], tools: ['空气炸锅'] }, - { id: 126, name: '空气炸锅烤鸡腿', stuff: ['鸡肉'], bv: 'BV1Zr4y1B7UQ', difficulty: '普通', tags: [], methods: ['炸'], tools: ['空气炸锅'] }, - { id: 0, name: '电饭煲版广式腊肠煲饭', stuff: ['腊肠', '米'], bv: 'BV1NE411Q7Jj', difficulty: '简单', tags: ['广式'], methods: ['煲'], tools: ['电饭煲'] }, - { id: 6, name: '电饭煲版土豆排骨焖饭', stuff: ['猪肉', '土豆', '米', '腊肠'], bv: 'BV1Bv411C7X3', difficulty: '简单', tags: ['懒人'], methods: ['焖', '煲'], tools: ['电饭煲'] }, - { id: 171, name: '微波炉版番茄焖饭', stuff: ['香肠', '番茄'], bv: 'BV193411W7nb', difficulty: '普通', tags: ['减脂餐'], methods: ['微波加热'], tools: ['微波炉'] }, - { id: 181, name: '微波炉版鸡蛋羹', stuff: ['鸡蛋'], bv: 'BV1HL411E7MM', difficulty: '简单', tags: ['早饭'], methods: ['微波加热'], tools: ['微波炉'] }, - { id: 216, name: '微波炉版蒸蛋羹', stuff: ['鸡蛋'], bv: 'BV19T4y1D7Zd', difficulty: '', tags: [], methods: ['微波加热'], tools: ['微波炉'] }, - { id: 61, name: '烤箱版烤花菜', stuff: ['花菜'], bv: 'BV1Nv411Y7kV', difficulty: '简单', tags: [], methods: ['烤'], tools: ['烤箱'] }, - { id: 39, name: '烤箱版M记同款薯条', stuff: ['土豆'], bv: 'BV1nA411P7aX', difficulty: '', tags: ['同款'], methods: ['炸'], tools: ['烤箱'] }, - { id: 401, name: '葱油拌面', stuff: ['面食'], bv: 'BV1H7411c7Xe', difficulty: '简单', tags: [], methods: ['拌'], tools: ['一口大锅'] }, - { id: 277, name: '清汤面', stuff: ['面食', '鸡蛋'], bv: 'BV1Yb411T7ED', difficulty: '简单', tags: ['懒人', '汤'], methods: ['煮'], tools: ['一口大锅'] }, - { id: 564, name: '阳春面', stuff: ['面食'], bv: 'BV11E411C7e7', difficulty: '简单', tags: [], methods: ['煮'], tools: ['一口大锅'] }, - { id: 427, name: '翡翠玉带虾仁', stuff: ['黄瓜', '虾', '鸡蛋'], bv: 'BV1E4411w7wC', difficulty: '困难', tags: [], methods: ['炒'], tools: ['一口大锅'] }, - { id: 222, name: '炒包菜', stuff: ['包菜'], bv: 'BV1K5411L7Xn', difficulty: '简单', tags: ['单一食材'], methods: ['炒'], tools: ['一口大锅'] }, - { id: 269, name: '醋溜西葫芦', stuff: ['西葫芦'], bv: 'BV1Rv411G7fD', difficulty: '普通', tags: ['开胃'], methods: ['炒'], tools: ['一口大锅'] }, -] - -// ---------- 分组常量(与 packages/cook/src/utils/recommend.ts 保持一致) ---------- -const METHOD_GROUPS = [ - { label: '炒菜', methods: ['炒'] }, - { label: '汤羹', methods: [], tags: ['汤'] }, - { label: '炖煮', methods: ['炖', '煮', '煲', '卤', '焖', '烧', '灼'] }, - { label: '蒸', methods: ['蒸'] }, - { label: '炸煎', methods: ['炸', '煎', '烙'] }, - { label: '烤', methods: ['烤', '烘', '焗'] }, - { label: '凉拌', methods: ['拌', '凉拌', '腌', '腌制', '泡菜', '油泼'] }, - { label: '微波', methods: ['微波加热'] }, - { label: '火锅', methods: [], tags: ['杂烩'] }, -] - -const CUISINE_GROUPS = [ - { label: '川菜', tags: ['川菜'] }, - { label: '湘菜', tags: ['湘菜'] }, - { label: '粤菜', tags: ['粤菜', '广式'] }, - { label: '苏菜', tags: ['苏菜'] }, - { label: '东北菜', tags: ['东北菜'] }, - { label: '西北菜', tags: ['西北特色'] }, - { label: '日式', tags: ['日式', '日式菜'] }, - { label: '意式', tags: ['意式'] }, - { label: '法式', tags: ['法式', '法餐'] }, - { label: '西式', tags: ['西式'] }, - { label: '国宴菜', tags: ['国宴菜'] }, -] - -const DIFFICULTY_LEVELS = ['简单', '普通', '困难'] -const SPICY_TAG = '辣' - -// ---------- 工具函数 ---------- -const EMOJI_MAP = (() => { - const map = {} - for (const group of ['vegetable', 'meat', 'staple']) { - for (const item of FOOD[group]) map[item.name] = item.emoji - } - return map -})() - -const TOOL_MAP = Object.fromEntries(FOOD.tools.map(t => [t.name, t])) -const ALL_FOODS = [...FOOD.vegetable, ...FOOD.meat, ...FOOD.staple] - -function uniqueStuff(stuff) { - return [...new Set((stuff || []).flatMap(s => String(s).split(/[,,、;;\n]/)).map(s => s.trim()).filter(Boolean))] -} - -function recipeMethods(recipe) { - return (recipe.methods || []).flatMap(m => String(m).split(/[、/]/)).filter(Boolean) -} - -function recipeEmoji(recipe) { - if (recipe.tags && recipe.tags.includes('杂烩')) return '🍲' - for (const s of recipe.stuff || []) { - if (EMOJI_MAP[s]) return EMOJI_MAP[s] - } - return '🍽️' -} - -function recipeCuisine(recipe) { - for (const group of CUISINE_GROUPS) { - if (group.tags.some(t => (recipe.tags || []).includes(t))) return group.label - } - return '' -} - -function matchesTaste(recipe, options) { - const { methodGroups = [], cuisines = [], noSpicy = false, difficulty = [] } = options - if (noSpicy && (recipe.tags || []).includes(SPICY_TAG)) return false - if (difficulty.length && !(recipe.difficulty && difficulty.includes(recipe.difficulty))) return false - if (cuisines.length) { - const tags = CUISINE_GROUPS.filter(g => cuisines.includes(g.label)).flatMap(g => g.tags) - if (!tags.some(t => (recipe.tags || []).includes(t))) return false - } - const groups = METHOD_GROUPS.filter(g => methodGroups.includes(g.label)) - if (!groups.length) return true - const methods = recipeMethods(recipe) - return groups.some(g => g.methods.some(m => methods.includes(m)) || (g.tags || []).some(t => (recipe.tags || []).includes(t))) -} - -/** 复刻真实推荐算法:缺料少的排前面 */ -function findRecipes(options) { - const { stuff = [], tool = '', methodGroups = [], cuisines = [], difficulty = [], keyword = '', mode = 'loose' } = options - const selected = new Set(uniqueStuff(stuff)) - if (!selected.size) return [] - - return RECIPES - .map((recipe, sourceIndex) => { - const required = uniqueStuff(recipe.stuff) - if (tool && !(recipe.tools || []).includes(tool)) return null - if (keyword && !recipe.name.includes(keyword)) return null - if (!matchesTaste(recipe, { methodGroups, cuisines, difficulty })) return null - - const matched = required.filter(i => selected.has(i)) - const missing = required.filter(i => !selected.has(i)) - if (!matched.length || missing.length > 2) return null - if (mode === 'strict' && ![...selected].every(i => required.includes(i))) return null - if (mode === 'survival' && missing.length) return null - - return { recipe, sourceIndex, requiredStuff: required, matchedStuff: matched, missingStuff: missing } - }) - .filter(Boolean) - .sort((a, b) => a.missingStuff.length - b.missingStuff.length || b.matchedStuff.length - a.matchedStuff.length || a.sourceIndex - b.sourceIndex) -} - -/** 按菜系取菜谱 */ -function recipesByCuisine(label) { - const group = CUISINE_GROUPS.find(g => g.label === label) - if (!group) return [] - return RECIPES.filter(r => group.tags.some(t => (r.tags || []).includes(t))) -} - -/** 猜一道菜 */ -function guessRecipe() { - const pool = RECIPES.filter(r => r.bv) - return pool[Math.floor(Math.random() * pool.length)] -} - -/** 统计每种食材能多做几道菜,用于"再补一样"提示 */ -function unlockSuggestions(stuff) { - const selected = new Set(uniqueStuff(stuff)) - const counts = {} - for (const recipe of RECIPES) { - const required = uniqueStuff(recipe.stuff) - if (required.length > 6) continue - for (const item of required) { - if (selected.has(item)) continue - const matched = required.filter(i => selected.has(i)).length - if (!matched) continue - counts[item] = (counts[item] || 0) + 1 - } - } - return Object.entries(counts) - .filter(([, count]) => count >= 2) - .sort((a, b) => b[1] - a[1]) - .slice(0, 4) - .map(([name, count]) => ({ name, count })) -} - -window.Mock = { - FOOD, - RECIPES, - METHOD_GROUPS, - CUISINE_GROUPS, - DIFFICULTY_LEVELS, - uniqueStuff, - recipeEmoji, - recipeCuisine, - findRecipes, - recipesByCuisine, - guessRecipe, - unlockSuggestions, - EMOJI_MAP, - TOOL_MAP, - ALL_FOODS, -} diff --git a/prototype/cook-redesign/styles.css b/prototype/cook-redesign/styles.css deleted file mode 100644 index 138a0f9..0000000 --- a/prototype/cook-redesign/styles.css +++ /dev/null @@ -1,1438 +0,0 @@ -/* ========================================================================== - 食用手册 · 温暖厨房手账风格 - Design Contract: Warm Kitchen Ledger - ========================================================================== */ - -:root { - /* 主色 */ - --primary: #1d7a45; - --primary-soft: #e8f4ec; - --primary-ink: #14522f; - --primary-tint: #348758; - - /* 强调色 */ - --accent: #c2410c; - --accent-soft: #fdf0e6; - - /* 中性 */ - --bg: #faf7f2; - --bg-alt: #f2ece1; - --surface: #ffffff; - --text: #23201c; - --text-sub: #6b635a; - --text-mute: #9a9186; - --border: #e8e0d4; - --border-strong: #d8ccbb; - - /* 语义色 */ - --success: #3f7d3f; - --success-soft: #e9f3e9; - --warning: #b45309; - --warning-soft: #fef3c7; - --danger: #b91c1c; - - /* 字体 */ - --font-display: "Noto Serif SC", "Songti SC", "STSong", "SimSun", "Source Han Serif SC", serif; - --font-body: -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", "Noto Sans SC", "Segoe UI", sans-serif; - --font-mono: "SF Mono", ui-monospace, Consolas, monospace; - - /* 字号 */ - --t-11: 11px; - --t-13: 13px; - --t-15: 15px; - --t-17: 17px; - --t-20: 20px; - --t-26: 26px; - --t-32: 32px; - - /* 圆角 */ - --r-sm: 8px; - --r-md: 12px; - --r-lg: 18px; - --r-xl: 24px; - --r-pill: 999px; - - /* 阴影 */ - --sh-sm: 0 1px 2px rgba(35, 32, 28, 0.05); - --sh-md: 0 2px 8px rgba(35, 32, 28, 0.06), 0 1px 2px rgba(35, 32, 28, 0.04); - --sh-lg: 0 8px 28px rgba(35, 32, 28, 0.12); - - /* 间距 */ - --s-4: 4px; - --s-8: 8px; - --s-12: 12px; - --s-16: 16px; - --s-20: 20px; - --s-24: 24px; - --s-32: 32px; - --s-48: 48px; - - /* 布局 */ - --shell-w: 430px; - --tabbar-h: 58px; - - color-scheme: light; -} - -*, -*::before, -*::after { - box-sizing: border-box; -} - -html, -body { - height: 100%; - margin: 0; - padding: 0; -} - -body { - background: #e9e2d6; - color: var(--text); - font: 400 var(--t-15) / 1.5 var(--font-body); - -webkit-font-smoothing: antialiased; - -webkit-tap-highlight-color: transparent; -} - -/* 桌面预览:手机壳 */ -.shell { - position: relative; - width: 100%; - max-width: var(--shell-w); - min-height: 100%; - margin: 0 auto; - background: var(--bg); - overflow-x: hidden; -} - -@media (min-width: 480px) { - body { - display: flex; - align-items: flex-start; - justify-content: center; - padding: var(--s-24) 0; - } - - .shell { - min-height: calc(100vh - 48px); - max-height: calc(100vh - 48px); - border-radius: 28px; - box-shadow: 0 24px 64px rgba(35, 32, 28, 0.18), 0 0 0 8px #2b2723; - overflow: hidden; - } -} - -/* 纸纹 + 暖光 */ -.shell::before { - position: fixed; - inset: 0; - z-index: 0; - background: - radial-gradient(120% 60% at 50% 0%, rgba(255, 250, 240, 0.9), transparent 62%), - radial-gradient(90% 50% at 100% 100%, rgba(194, 65, 12, 0.05), transparent 60%); - content: ""; - pointer-events: none; -} - -.shell::after { - position: fixed; - inset: 0; - z-index: 0; - opacity: 0.4; - background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.035'/%3E%3C/svg%3E"); - content: ""; - pointer-events: none; -} - -@media (max-width: 479px) { - .shell::before, - .shell::after { - position: absolute; - } -} - -/* ============ 视图容器 ============ */ -.views { - position: relative; - z-index: 1; -} - -.view { - display: none; - padding-bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom) + var(--s-16)); - animation: view-in 0.32s cubic-bezier(0.22, 0.9, 0.3, 1) both; -} - -.view.is-active { - display: block; -} - -@keyframes view-in { - from { - opacity: 0; - transform: translateY(8px); - } -} - -.page { - padding: 0 var(--s-16); -} - -/* ============ 顶栏 ============ */ -.topbar { - position: sticky; - top: 0; - z-index: 30; - display: flex; - align-items: center; - gap: var(--s-12); - padding: var(--s-16) var(--s-16) var(--s-12); - background: linear-gradient(to bottom, var(--bg) 72%, rgba(250, 247, 242, 0)); - backdrop-filter: blur(10px); -} - -.topbar-titles { - flex: 1; - min-width: 0; -} - -.topbar h1 { - margin: 0; - font: 600 var(--t-26) / 1.25 var(--font-display); - letter-spacing: 0.01em; -} - -.topbar .sub { - margin: 3px 0 0; - color: var(--text-mute); - font-size: var(--t-13); -} - -.icon-btn { - display: inline-flex; - align-items: center; - justify-content: center; - width: 38px; - height: 38px; - flex: none; - border: 1px solid var(--border); - border-radius: var(--r-pill); - background: var(--surface); - color: var(--text-sub); - box-shadow: var(--sh-sm); - cursor: pointer; - transition: transform 0.16s ease, border-color 0.16s ease, color 0.16s ease; -} - -.icon-btn:hover { - border-color: var(--border-strong); - color: var(--primary); -} - -.icon-btn:active { - transform: scale(0.94); -} - -.icon-btn.is-on { - border-color: var(--primary); - background: var(--primary); - color: #fff; -} - -.icon-btn svg { - width: 20px; - height: 20px; -} - -.icon-btn.is-plain { - width: auto; - height: 34px; - padding: 0 14px; - border-radius: var(--r-pill); - font: 500 var(--t-13) / 1 var(--font-body); -} - -/* ============ 卡片 ============ */ -.card { - border: 1px solid #ece5d8; - border-radius: var(--r-lg); - background: var(--surface); - box-shadow: var(--sh-sm); -} - -.section { - margin-bottom: var(--s-24); - animation: rise 0.4s cubic-bezier(0.22, 0.9, 0.3, 1) both; -} - -@keyframes rise { - from { - opacity: 0; - transform: translateY(10px); - } -} - -.section-head { - display: flex; - align-items: baseline; - justify-content: space-between; - gap: var(--s-12); - margin: 0 0 var(--s-8); - padding: 0 2px; -} - -.section-head h2 { - margin: 0; - font: 600 var(--t-17) / 1.3 var(--font-display); -} - -.section-head .hint { - color: var(--text-mute); - font-size: var(--t-13); -} - -.section-head .hint.on { - color: var(--primary); - font-weight: 500; -} - -/* ============ 食材 chip ============ */ -.chips { - display: flex; - flex-wrap: wrap; - gap: var(--s-8); -} - -.chip { - display: inline-flex; - align-items: center; - gap: 6px; - min-height: 38px; - padding: 0 14px; - border: 1px solid var(--border); - border-radius: var(--r-pill); - background: var(--surface); - color: var(--text); - font: 400 var(--t-15) / 1 var(--font-body); - cursor: pointer; - user-select: none; - transition: transform 0.16s ease, background-color 0.16s ease, border-color 0.16s ease, color 0.16s ease; -} - -.chip .emoji { - font-size: 15px; - line-height: 1; -} - -.chip:hover { - border-color: var(--border-strong); -} - -.chip:active { - transform: scale(0.96); -} - -.chip.is-on { - border-color: var(--primary); - background: var(--primary); - color: #fff; - font-weight: 500; -} - -.chip.is-on .emoji { - filter: saturate(1.15); -} - -.chip:focus-visible { - outline: 2px solid var(--primary); - outline-offset: 2px; -} - -.chip.chip-sm { - min-height: 30px; - padding: 0 11px; - font-size: var(--t-13); -} - -.chip.chip-icon .icon { - width: 16px; - height: 16px; - opacity: 0.85; -} - -/* ============ 已选食材托盘 ============ */ -.tray { - display: flex; - flex-wrap: wrap; - gap: var(--s-8); - padding: var(--s-12); - border: 1px solid #ece5d8; - border-radius: var(--r-lg); - background: var(--surface); - box-shadow: var(--sh-sm); -} - -.tray-empty { - color: var(--text-mute); - font-size: var(--t-13); -} - -.token { - display: inline-flex; - align-items: center; - gap: 5px; - height: 32px; - padding: 0 8px 0 12px; - border-radius: var(--r-pill); - background: var(--primary-soft); - color: var(--primary-ink); - font-size: var(--t-15); - font-weight: 500; - cursor: pointer; - animation: token-in 0.24s cubic-bezier(0.34, 1.4, 0.64, 1) both; -} - -@keyframes token-in { - from { - opacity: 0; - transform: scale(0.82); - } -} - -.token svg { - width: 15px; - height: 15px; - opacity: 0.6; -} - -.token:active { - opacity: 0.65; -} - -/* ============ 搜索框 ============ */ -.search { - position: relative; - display: flex; - align-items: center; -} - -.search svg { - position: absolute; - left: 14px; - width: 17px; - height: 17px; - color: var(--text-mute); - pointer-events: none; -} - -.search input { - width: 100%; - height: 44px; - padding: 0 14px 0 40px; - border: 1px solid var(--border); - border-radius: var(--r-pill); - background: var(--surface); - color: var(--text); - font: 400 var(--t-15) / 44px var(--font-body); - outline: none; - transition: border-color 0.16s ease, box-shadow 0.16s ease; -} - -.search input::placeholder { - color: var(--text-mute); -} - -.search input:focus { - border-color: var(--primary-tint); - box-shadow: 0 0 0 3px rgba(29, 122, 69, 0.12); -} - -/* ============ 折叠筛选 ============ */ -.filters { - border: 1px solid #ece5d8; - border-radius: var(--r-lg); - background: var(--surface); - box-shadow: var(--sh-sm); - overflow: hidden; -} - -.filter-bar { - display: flex; - align-items: center; - gap: var(--s-8); - width: 100%; - padding: var(--s-12) var(--s-14); - border: 0; - background: transparent; - font: 500 var(--t-15) / 1 var(--font-body); - color: var(--text); - text-align: left; - cursor: pointer; -} - -.filter-bar .badge-count { - display: inline-flex; - align-items: center; - justify-content: center; - min-width: 20px; - height: 20px; - padding: 0 6px; - border-radius: var(--r-pill); - background: var(--primary); - color: #fff; - font-size: var(--t-11); - font-weight: 600; -} - -.filter-bar .caret { - width: 18px; - height: 18px; - margin-left: auto; - color: var(--text-mute); - transition: transform 0.22s ease; -} - -.filters.is-open .filter-bar .caret { - transform: rotate(180deg); -} - -.filter-body { - display: none; - padding: 0 var(--s-14) var(--s-14); -} - -.filters.is-open .filter-body { - display: block; -} - -.filter-group + .filter-group { - margin-top: var(--s-16); -} - -.filter-group h3 { - margin: 0 0 var(--s-8); - color: var(--text-sub); - font: 500 var(--t-13) / 1.3 var(--font-body); -} - -.active-filters { - display: flex; - flex-wrap: wrap; - gap: 6px; - margin-top: var(--s-12); -} - -.tag-chip { - display: inline-flex; - align-items: center; - gap: 4px; - height: 28px; - padding: 0 8px 0 11px; - border: 0; - border-radius: var(--r-pill); - background: var(--primary-soft); - color: var(--primary-ink); - font: 500 var(--t-13) / 1 var(--font-body); - cursor: pointer; -} - -.tag-chip svg { - width: 13px; - height: 13px; - opacity: 0.65; -} - -.tag-chip.is-accent { - background: var(--accent-soft); - color: var(--accent); -} - -/* ============ 分段控件 ============ */ -.segment { - display: inline-flex; - gap: 2px; - padding: 3px; - border-radius: var(--r-pill); - background: var(--bg-alt); -} - -.segment button { - min-height: 30px; - padding: 0 14px; - border: 0; - border-radius: var(--r-pill); - background: transparent; - color: var(--text-sub); - font: 500 var(--t-13) / 1 var(--font-body); - cursor: pointer; - transition: background-color 0.18s ease, color 0.18s ease, box-shadow 0.18s ease; -} - -.segment button.is-on { - background: var(--surface); - color: var(--primary); - box-shadow: var(--sh-sm); -} - -.segment button .n { - margin-left: 4px; - opacity: 0.6; - font-variant-numeric: tabular-nums; -} - -/* ============ 底部结果面板 ============ */ -.sheet { - position: fixed; - right: 0; - bottom: 0; - left: 0; - z-index: 45; - display: flex; - flex-direction: column; - max-width: var(--shell-w); - margin: 0 auto; - border-radius: var(--r-xl) var(--r-xl) 0 0; - background: var(--surface); - box-shadow: var(--sh-lg); - transition: transform 0.4s cubic-bezier(0.32, 0.72, 0, 1), bottom 0.4s cubic-bezier(0.32, 0.72, 0, 1); -} - -/* 收起时面板停在底栏之上,避免"查看菜谱"按钮被 tabbar 盖住点不到 */ -.sheet.is-collapsed { - bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom)); -} - -@media (min-width: 480px) { - .sheet { - bottom: 24px; - border-radius: var(--r-xl); - } - - .sheet.is-collapsed { - bottom: calc(24px + var(--tabbar-h)); - } -} - -.sheet.is-collapsed { - transform: translateY(calc(100% - var(--peek-h, 104px))); -} - -.sheet.is-half { - transform: translateY(38%); -} - -.sheet.is-full { - transform: translateY(0); - z-index: 60; -} - -.sheet-grab { - display: flex; - justify-content: center; - padding: 10px 0 6px; - cursor: grab; -} - -.sheet-grab span { - width: 38px; - height: 4px; - border-radius: var(--r-pill); - background: var(--border-strong); -} - -.sheet-head { - padding: 0 var(--s-16) var(--s-12); -} - -.sheet-summary { - display: flex; - align-items: center; - gap: var(--s-12); -} - -.sheet-summary .count { - flex: 1; - min-width: 0; -} - -.sheet-summary strong { - display: block; - font: 600 var(--t-20) / 1.3 var(--font-display); -} - -.sheet-summary span { - display: block; - margin-top: 2px; - color: var(--text-mute); - font-size: var(--t-13); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.sheet-peek { - display: flex; - gap: var(--s-8); - padding: 0 var(--s-16) var(--s-12); - overflow-x: auto; - scrollbar-width: none; -} - -.sheet-peek::-webkit-scrollbar { - display: none; -} - -.peek-card { - flex: none; - display: flex; - align-items: center; - gap: var(--s-8); - min-width: 150px; - padding: var(--s-8) var(--s-12); - border: 1px solid var(--border); - border-radius: var(--r-md); - background: var(--bg); - cursor: pointer; - transition: border-color 0.16s ease, transform 0.16s ease; -} - -.peek-card:hover { - border-color: var(--border-strong); -} - -.peek-card:active { - transform: scale(0.97); -} - -.peek-card .thumb { - display: inline-flex; - align-items: center; - justify-content: center; - width: 32px; - height: 32px; - flex: none; - border-radius: var(--r-sm); - background: var(--surface); - font-size: 17px; -} - -.peek-card b { - display: block; - font: 500 var(--t-13) / 1.35 var(--font-body); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.peek-card small { - display: block; - color: var(--text-mute); - font-size: var(--t-11); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.sheet-body { - display: none; - flex: 1; - min-height: 0; - flex-direction: column; -} - -.sheet.is-half .sheet-body, -.sheet.is-full .sheet-body { - display: flex; -} - -.sheet-toolbar { - display: flex; - align-items: center; - gap: var(--s-8); - padding: 0 var(--s-16) var(--s-12); - overflow-x: auto; - scrollbar-width: none; -} - -.sheet-toolbar::-webkit-scrollbar { - display: none; -} - -.sheet-list { - flex: 1; - min-height: 0; - overflow-y: auto; - padding: 0 var(--s-16) var(--s-16); - overscroll-behavior: contain; -} - -.sheet.is-full .sheet-list { - max-height: 52vh; -} - -.dish-row { - display: flex; - align-items: center; - gap: var(--s-12); - width: 100%; - padding: var(--s-12) 0; - border: 0; - border-bottom: 1px solid #f2ece2; - background: transparent; - text-align: left; - cursor: pointer; - animation: row-in 0.32s cubic-bezier(0.2, 0.8, 0.2, 1) both; - animation-delay: calc(var(--i, 0) * 30ms); -} - -@keyframes row-in { - from { - opacity: 0; - transform: translateY(8px); - } -} - -.dish-row:last-child { - border-bottom: 0; -} - -.dish-row:hover .dish-name { - color: var(--primary); -} - -.dish-thumb { - display: inline-flex; - align-items: center; - justify-content: center; - width: 44px; - height: 44px; - flex: none; - border-radius: var(--r-md); - background: var(--bg-alt); - font-size: 22px; -} - -.dish-main { - flex: 1; - min-width: 0; -} - -.dish-name { - font: 500 var(--t-15) / 1.35 var(--font-body); - transition: color 0.16s ease; -} - -.dish-meta { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 6px; - margin-top: 3px; - color: var(--text-mute); - font-size: var(--t-13); -} - -.dish-meta .dot::before { - margin-right: 6px; - content: "·"; -} - -.miss { - color: var(--accent); - font-weight: 500; -} - -.cuisine-tag { - display: inline-flex; - align-items: center; - height: 20px; - padding: 0 8px; - border-radius: var(--r-pill); - background: var(--primary-soft); - color: var(--primary-ink); - font-size: var(--t-11); - font-weight: 500; -} - -.cuisine-tag.is-plain { - background: var(--bg-alt); - color: var(--text-sub); -} - -/* ============ 空状态 ============ */ -.empty { - padding: var(--s-48) var(--s-24); - text-align: center; -} - -.empty svg { - width: 72px; - height: 72px; - margin-bottom: var(--s-16); - color: var(--border-strong); -} - -.empty h3 { - margin: 0 0 6px; - font: 600 var(--t-17) / 1.4 var(--font-display); -} - -.empty p { - margin: 0 0 var(--s-20); - color: var(--text-sub); - font-size: var(--t-13); -} - -/* ============ 按钮 ============ */ -.btn { - display: inline-flex; - align-items: center; - justify-content: center; - gap: 6px; - min-height: 44px; - padding: 0 20px; - border: 1px solid transparent; - border-radius: var(--r-pill); - background: var(--primary); - color: #fff; - font: 500 var(--t-15) / 1 var(--font-body); - cursor: pointer; - transition: transform 0.16s ease, background-color 0.16s ease; -} - -.btn:hover { - background: var(--primary-tint); -} - -.btn:active { - transform: scale(0.97); -} - -.btn svg { - width: 17px; - height: 17px; -} - -.btn.is-ghost { - border-color: var(--border); - background: var(--surface); - color: var(--text-sub); -} - -.btn.is-ghost:hover { - border-color: var(--border-strong); - background: var(--bg); -} - -.btn.is-block { - width: 100%; -} - -.btn.is-sm { - min-height: 34px; - padding: 0 14px; - font-size: var(--t-13); -} - -/* ============ 菜谱列表行 ============ */ -.list { - border: 1px solid #ece5d8; - border-radius: var(--r-lg); - background: var(--surface); - box-shadow: var(--sh-sm); - overflow: hidden; -} - -.list-row { - display: flex; - align-items: center; - gap: var(--s-12); - width: 100%; - padding: var(--s-12) var(--s-14); - border: 0; - border-bottom: 1px solid #f2ece2; - background: transparent; - text-align: left; - cursor: pointer; - transition: background-color 0.14s ease; -} - -.list-row:last-child { - border-bottom: 0; -} - -.list-row:hover { - background: #fdfbf7; -} - -.list-row .thumb { - display: inline-flex; - align-items: center; - justify-content: center; - width: 38px; - height: 38px; - flex: none; - border-radius: var(--r-sm); - background: var(--bg-alt); - font-size: 19px; -} - -.list-row .name { - flex: 1; - min-width: 0; - font: 500 var(--t-15) / 1.4 var(--font-body); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.list-row .sub { - display: block; - margin-top: 1px; - color: var(--text-mute); - font-size: var(--t-13); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.star { - display: inline-flex; - align-items: center; - justify-content: center; - width: 34px; - height: 34px; - flex: none; - border: 0; - border-radius: var(--r-pill); - background: transparent; - color: var(--border-strong); - cursor: pointer; - transition: color 0.16s ease, background-color 0.16s ease; -} - -.star:hover { - background: var(--bg-alt); -} - -.star.is-on { - color: #d97706; -} - -.star svg { - width: 19px; - height: 19px; -} - -/* ============ 详情弹层 ============ */ -.modal { - position: fixed; - inset: 0; - z-index: 70; - display: none; - align-items: flex-end; - justify-content: center; - background: rgba(35, 32, 28, 0.42); - backdrop-filter: blur(3px); -} - -.modal.is-open { - display: flex; - animation: fade-in 0.24s ease both; -} - -@keyframes fade-in { - from { - opacity: 0; - } -} - -.modal-panel { - width: 100%; - max-width: var(--shell-w); - max-height: 88%; - overflow-y: auto; - border-radius: var(--r-xl) var(--r-xl) 0 0; - background: var(--bg); - box-shadow: var(--sh-lg); - animation: sheet-up 0.32s cubic-bezier(0.22, 0.9, 0.3, 1) both; -} - -@keyframes sheet-up { - from { - transform: translateY(28px); - opacity: 0; - } -} - -.modal-hero { - padding: var(--s-24) var(--s-20) var(--s-16); - text-align: center; -} - -.modal-hero .thumb { - display: inline-flex; - align-items: center; - justify-content: center; - width: 76px; - height: 76px; - border-radius: var(--r-xl); - background: var(--surface); - box-shadow: var(--sh-md); - font-size: 40px; - animation: pop 0.42s cubic-bezier(0.34, 1.4, 0.64, 1) both; -} - -@keyframes pop { - from { - transform: scale(0.76); - } -} - -.modal-hero h2 { - margin: var(--s-12) 0 4px; - font: 600 var(--t-20) / 1.35 var(--font-display); -} - -.modal-hero p { - margin: 0; - color: var(--text-mute); - font-size: var(--t-13); -} - -.modal-actions { - display: flex; - gap: var(--s-8); - padding: 0 var(--s-20) var(--s-20); -} - -.modal-actions .btn { - flex: 1; -} - -.detail-group { - margin: 0 var(--s-16) var(--s-16); - padding: var(--s-14); - border: 1px solid #ece5d8; - border-radius: var(--r-lg); - background: var(--surface); -} - -.detail-group h3 { - margin: 0 0 var(--s-8); - color: var(--text-sub); - font: 500 var(--t-13) / 1.3 var(--font-body); -} - -.detail-note { - margin: 0 var(--s-16) var(--s-32); - color: var(--text-mute); - font-size: var(--t-13); - line-height: 1.6; - text-align: center; -} - -/* ============ 底部 tab 栏 ============ */ -.tabbar { - position: fixed; - right: 0; - bottom: 0; - left: 0; - z-index: 50; - display: flex; - max-width: var(--shell-w); - margin: 0 auto; - padding-bottom: env(safe-area-inset-bottom); - border-top: 1px solid var(--border); - background: rgba(250, 247, 242, 0.88); - backdrop-filter: blur(14px); -} - -@media (min-width: 480px) { - .tabbar { - bottom: 24px; - border: 0; - border-radius: 0 0 28px 28px; - } -} - -.tabbar a { - position: relative; - display: flex; - flex: 1; - flex-direction: column; - align-items: center; - gap: 3px; - padding: 8px 0 10px; - color: var(--text-mute); - text-decoration: none; - transition: color 0.18s ease; -} - -.tabbar a svg { - width: 23px; - height: 23px; - stroke-width: 1.7; -} - -.tabbar a span { - font-size: var(--t-11); -} - -.tabbar a.is-active { - color: var(--primary); -} - -.tabbar a.is-active svg { - stroke-width: 2.2; -} - -.tabbar a.is-active::before { - position: absolute; - top: 0; - width: 22px; - height: 2.5px; - border-radius: 0 0 3px 3px; - background: var(--primary); - content: ""; -} - -/* ============ 发现页 ============ */ -.hero-card { - position: relative; - overflow: hidden; - padding: var(--s-24); - border-radius: var(--r-xl); - background: linear-gradient(135deg, #1d7a45, #14522f); - color: #fff; - box-shadow: var(--sh-md); -} - -.hero-card::after { - position: absolute; - top: -40px; - right: -30px; - width: 150px; - height: 150px; - border-radius: 50%; - background: rgba(255, 255, 255, 0.08); - content: ""; -} - -.hero-card .kicker { - font-size: var(--t-13); - opacity: 0.8; -} - -.hero-card h2 { - position: relative; - margin: var(--s-8) 0 var(--s-4); - font: 600 var(--t-26) / 1.3 var(--font-display); -} - -.hero-card p { - position: relative; - margin: 0 0 var(--s-16); - font-size: var(--t-13); - opacity: 0.85; -} - -.hero-card .btn { - position: relative; - background: #fff; - color: var(--primary-ink); -} - -.cuisine-grid { - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: var(--s-8); -} - -.cuisine-card { - display: flex; - align-items: center; - gap: var(--s-8); - padding: var(--s-12); - border: 1px solid #ece5d8; - border-radius: var(--r-md); - background: var(--surface); - box-shadow: var(--sh-sm); - cursor: pointer; - text-align: left; - transition: transform 0.16s ease, border-color 0.16s ease; -} - -.cuisine-card:hover { - border-color: var(--border-strong); -} - -.cuisine-card:active { - transform: scale(0.97); -} - -.cuisine-card .dot { - width: 8px; - height: 8px; - flex: none; - border-radius: 50%; - background: var(--primary); -} - -.cuisine-card b { - flex: 1; - font: 500 var(--t-15) / 1.3 var(--font-body); -} - -.cuisine-card small { - color: var(--text-mute); - font-size: var(--t-13); - font-variant-numeric: tabular-nums; -} - -/* ============ 我的页 ============ */ -.account { - display: flex; - align-items: center; - gap: var(--s-12); - padding: var(--s-16); - border-radius: var(--r-lg); - background: var(--surface); - border: 1px solid #ece5d8; - box-shadow: var(--sh-sm); -} - -.account .avatar { - display: inline-flex; - align-items: center; - justify-content: center; - width: 46px; - height: 46px; - flex: none; - border-radius: 50%; - background: var(--primary-soft); - color: var(--primary-ink); -} - -.account .avatar svg { - width: 24px; - height: 24px; -} - -.account b { - display: block; - font: 500 var(--t-15) / 1.4 var(--font-body); -} - -.account small { - color: var(--text-mute); - font-size: var(--t-13); -} - -.menu { - border: 1px solid #ece5d8; - border-radius: var(--r-lg); - background: var(--surface); - box-shadow: var(--sh-sm); - overflow: hidden; -} - -.menu a, -.menu button { - display: flex; - align-items: center; - gap: var(--s-12); - width: 100%; - padding: var(--s-14); - border: 0; - border-bottom: 1px solid #f2ece2; - background: transparent; - color: var(--text); - font: 400 var(--t-15) / 1.4 var(--font-body); - text-align: left; - text-decoration: none; - cursor: pointer; - transition: background-color 0.14s ease; -} - -.menu a:last-child, -.menu button:last-child { - border-bottom: 0; -} - -.menu a:hover, -.menu button:hover { - background: #fdfbf7; -} - -.menu .ico { - display: inline-flex; - align-items: center; - justify-content: center; - width: 32px; - height: 32px; - flex: none; - border-radius: var(--r-sm); - background: var(--bg-alt); - color: var(--text-sub); -} - -.menu .ico svg { - width: 17px; - height: 17px; -} - -.menu .label { - flex: 1; -} - -.menu .chev { - width: 16px; - height: 16px; - color: var(--border-strong); -} - -/* ============ Toast ============ */ -.toast { - position: fixed; - bottom: calc(var(--tabbar-h) + env(safe-area-inset-bottom) + 20px); - left: 50%; - z-index: 80; - padding: 10px 18px; - border-radius: var(--r-md); - background: var(--text); - color: #fff; - font-size: var(--t-13); - box-shadow: var(--sh-lg); - opacity: 0; - pointer-events: none; - transform: translate(-50%, 10px); - transition: opacity 0.22s ease, transform 0.22s ease; -} - -.toast.is-on { - opacity: 1; - transform: translate(-50%, 0); -} - -/* ============ 响应式 ============ */ -@media (max-width: 360px) { - .topbar h1 { - font-size: var(--t-20); - } - - .sheet-peek .peek-card { - min-width: 132px; - } -} - -@media (prefers-reduced-motion: reduce) { - *, - *::before, - *::after { - animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; - transition-duration: 0.01ms !important; - } -}