victor.zhou 4 сар өмнө
parent
commit
ea1aaa3950

+ 12 - 0
.editorconfig

@@ -0,0 +1,12 @@
+root = true
+
+[*]
+indent_style = space
+indent_size = 2
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+trim_trailing_whitespace = false

+ 15 - 0
.env.development

@@ -0,0 +1,15 @@
+# 菜谱生成模型配置(文本生成)
+VITE_TEXT_GENERATION_BASE_URL=https://api.302ai.cn/v1/
+VITE_TEXT_GENERATION_API_KEY=sk-K9Y5BnIenLkn3nN11VF4WXtw9W4f7RzDyijmICq5e9R0PIr4
+VITE_TEXT_GENERATION_MODEL=doubao-1.5-pro-32k
+VITE_TEXT_GENERATION_TEMPERATURE=0.7
+VITE_TEXT_GENERATION_TIMEOUT=300000
+
+
+# 图片生成模型配置
+VITE_IMAGE_GENERATION_BASE_URL=https://api.302ai.cn/bigmodel/api/paas/v4/images/generations
+VITE_IMAGE_GENERATION_API_KEY=sk-K9Y5BnIenLkn3nN11VF4WXtw9W4f7RzDyijmICq5e9R0PIr4
+VITE_IMAGE_GENERATION_MODEL=cogview-4
+
+# Java后台服务器地址(本地开发使用代理)
+VITE_API_BASE_URL=/api

+ 15 - 0
.env.production

@@ -0,0 +1,15 @@
+# 菜谱生成模型配置(文本生成)
+VITE_TEXT_GENERATION_BASE_URL=https://api.302ai.cn/v1/
+VITE_TEXT_GENERATION_API_KEY=sk-K9Y5BnIenLkn3nN11VF4WXtw9W4f7RzDyijmICq5e9R0PIr4
+VITE_TEXT_GENERATION_MODEL=doubao-1.5-pro-32k
+VITE_TEXT_GENERATION_TEMPERATURE=0.7
+VITE_TEXT_GENERATION_TIMEOUT=300000
+
+
+# 图片生成模型配置
+VITE_IMAGE_GENERATION_BASE_URL=https://api.302ai.cn/bigmodel/api/paas/v4/images/generations
+VITE_IMAGE_GENERATION_API_KEY=sk-K9Y5BnIenLkn3nN11VF4WXtw9W4f7RzDyijmICq5e9R0PIr4
+VITE_IMAGE_GENERATION_MODEL=cogview-4
+
+# Java后台服务器地址(生产环境使用完整URL)
+VITE_API_BASE_URL=https://recipe-muse.com

+ 6 - 239
README.md

@@ -1,60 +1,12 @@
-# 🍳 一饭封神
+api 文档: https://www.yuque.com/kolo7/ly4p08/koe2e766ehtb7gai?singleDoc# 《PAYMENT_API》
+https://www.yuque.com/kolo7/ly4p08/yps2h45o8l3cktgb?singleDoc# 《AUTH_API》
+https://www.yuque.com/kolo7/ly4p08/rz9ylvxih1ibv476?singleDoc# 《APPLE_AUTH_API》
+设计文档:https://www.yuque.com/kolo7/ly4p08/lgz7bru6f2gvrciq?singleDoc# 《统一登录充值平台》
+https://www.yuque.com/kolo7/ly4p08/uuwa89hedxi4qtds?singleDoc# 《google Pay》
 
-> 🚀 **Vibe Coding**  
-> 通过 Kiro 编辑器,实现了从需求分析、架构设计到代码实现的全流程开发。
-> [English](./README_EN.md) | 中文
+包名:com.ipeaking.recipemuse
 
-基于 AI 的智能菜谱生成平台,支持中华八大菜系 + 国际料理,提供营养分析、酒水推荐、菜谱效果图生成等功能。
 
-[![Live Demo](https://img.shields.io/badge/🌐_Live_Demo-一饭封神-yellow?style=for-the-badge)](https://eat.lz-t.top/)
-[![Vercel](https://img.shields.io/badge/🚀_Vercel-yffs.vercel.app-black?style=for-the-badge&logo=vercel)](https://yffs.vercel.app/)
-[![Netlify](https://img.shields.io/badge/🌐_Netlify-whattoeatai.netlify.app-00C7B7?style=for-the-badge&logo=netlify)](https://whattoeatai.netlify.app/)
-[![GitHub](https://img.shields.io/badge/GitHub-liu--ziting/what--to--eat-black?style=for-the-badge&logo=github)](https://github.com/liu-ziting/what-to-eat)
-[![Config System](https://img.shields.io/badge/⚙️_Dynamic_Config-Real--time_AI_Settings-blue?style=for-the-badge)](https://github.com/liu-ziting/what-to-eat#%EF%B8%8F-动态配置系统)
-
-## 🚀 核心功能
-
--   **智能菜谱生成** - 基于食材和菜系偏好生成专业菜谱
--   **营养分析** - 详细营养成分分析和健康评分
--   **AI 效果图** - 一键生成精美菜品图片
--   **酒水搭配** - 专业侍酒师推荐
--   **酱汁设计** - 定制化调料配方
--   **收藏管理** - 保存和管理喜爱的菜谱
--   **料理占卜** - 趣味性饮食运势
--   **配置管理** - 动态配置 AI 模型参数,支持多服务商切换
-
-## 🛠️ 技术栈
-
--   **前端框架:** Vue 3.4 + Composition API + TypeScript 5.3+
--   **样式方案:** Tailwind CSS 3.4+
--   **构建工具:** Vite 5.0+
--   **AI 服务:** 302.AI
--   **部署平台:** Vercel + Netlify
-
-## ⚡ AI 服务推荐
-
-> 🤖 **本项目 AI 服务提供商**
-
-### 🚀 [302.AI](https://share.302.ai/DymMSI) - 官方合作伙伴
-
-**302.AI** 是一个按用量付费的企业级 AI 资源平台,提供市场上最新、最全面的 AI 模型和 API,以及多种开箱即用的在线 AI 应用。
-
-**为什么选择 302.AI?**
-
--   🤖 **多模型支持** - 支持最新的 AI 大模型
--   💰 **价格优惠** - 按用量付费,成本可控
--   ⚡ **响应快速** - 高性能 API 服务
--   🛡️ **服务稳定** - 企业级可靠性保障
-
-[![访问 302.AI](https://img.shields.io/badge/🚀_访问_302.AI-官方合作伙伴-blue?style=for-the-badge)](https://share.302.ai/DymMSI)
-
-## 🚀 快速开始
-
-### 环境要求
-
--   Node.js 18+
-
-### 本地开发
 
 ```bash
 # 克隆项目
@@ -74,188 +26,3 @@ npm run dev
 # 🎯 首次启动后,点击导航栏的 ⚙️ 图标可以动态配置AI模型参数
 ```
 
-### 构建部署
-
-```bash
-# 类型检查 + 构建
-npm run build
-
-# Netlify 构建
-npm run build:netlify
-
-# 预览构建结果
-npm run preview
-```
-
-## 🚀 一键部署
-
-### Vercel 部署
-
-[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/liu-ziting/what-to-eat&env=VITE_TEXT_GENERATION_BASE_URL,VITE_TEXT_GENERATION_API_KEY,VITE_TEXT_GENERATION_MODEL,VITE_IMAGE_GENERATION_BASE_URL,VITE_IMAGE_GENERATION_API_KEY,VITE_IMAGE_GENERATION_MODEL&envDescription=AI%20API%20配置&envLink=https://github.com/liu-ziting/what-to-eat%23环境变量配置)
-
-### Netlify 部署
-
-[![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/liu-ziting/what-to-eat)
-
-> 📖 详细部署指南请参考:[DEPLOYMENT.md](./DEPLOYMENT.md)
-
-### 环境变量配置
-
-#### 你可以切换任何符合 OpenAI 标准的请求地址和模型
-
-> **🌟 模型推荐**: 建议使用高质量 AI 大模型获得更好的菜谱生成效果!不同模型的创意风格和专业程度差异显著。
-
-```env
-# 菜谱生成模型配置(文本生成)
-VITE_TEXT_GENERATION_BASE_URL=https://api.302ai.cn/v1/
-VITE_TEXT_GENERATION_API_KEY=************
-VITE_TEXT_GENERATION_MODEL=doubao-1.5-pro-32k
-VITE_TEXT_GENERATION_TEMPERATURE=0.7
-VITE_TEXT_GENERATION_TIMEOUT=300000
-
-# 图片生成模型配置
-VITE_IMAGE_GENERATION_BASE_URL=https://open.bigmodel.cn/api/paas/v4/images/generations
-VITE_IMAGE_GENERATION_API_KEY=******************
-VITE_IMAGE_GENERATION_MODEL=cogview-3-flash
-
-```
-
-### ⚙️ 动态配置系统
-
-应用内置了强大的配置管理系统,支持运行时动态修改 AI 模型配置:
-
-#### 🎯 功能特性
-
--   **实时配置** - 无需重启应用,配置修改立即生效
--   **持久化存储** - 用户配置自动保存到本地
--   **分离管理** - 菜谱生成和图片生成模型独立配置
--   **配置验证** - 内置 API 连接测试功能
--   **一键恢复** - 支持恢复环境变量默认配置
-
-#### 🚀 使用方法
-
-1. 点击导航栏右侧的 ⚙️ 设置按钮
-2. 在弹窗中修改 API 地址、密钥、模型等参数
-3. 点击"保存设置"立即应用配置
-4. 使用"测试配置"验证设置是否正确
-
-#### 🎯 模型效果说明
-
-> **💡 重要提示**: 不同 AI 模型生成的菜谱质量和风格差异很大!
->
-> -   **推荐使用高质量大模型** - 如 GPT、Claude、DeepSeek 等
-> -   **菜谱专业度** - 优质模型能生成更专业、详细的制作步骤
-> -   **创意程度** - 不同模型的创意风格和口味搭配各有特色
-> -   **营养分析** - 高端模型提供更准确的营养成分分析
->
-> 🔄 **建议**: 尝试切换不同的 AI 模型接口,体验各种风格的菜谱生成效果!
-
-#### 📋 支持的配置项
-
--   **API 地址** - 支持任何 OpenAI 兼容的 API 服务
--   **API 密钥** - 安全的密码形式输入
--   **模型名称** - 自定义使用的 AI 模型
--   **温度参数** - 控制生成内容的创造性(0-1)
--   **超时设置** - 自定义 API 请求超时时间
-
-> 💡 **提示**: 访问 `/settings-demo` 查看完整的配置系统演示
-
-## 📁 项目结构
-
-```
-src/
-├── components/          # 通用组件
-│   ├── ConfirmModal.vue      # 确认对话框
-│   ├── CookingLoader.vue     # 烹饪加载动画
-│   ├── FavoriteButton.vue    # 收藏按钮
-│   ├── GlobalNavigation.vue  # 全局导航
-│   ├── RecipeCard.vue        # 菜谱卡片
-│   ├── NutritionAnalysis.vue # 营养分析
-│   ├── SettingsModal.vue     # 设置弹窗 🆕
-│   ├── SettingsButton.vue    # 设置按钮 🆕
-│   ├── ConfigTest.vue        # 配置测试 🆕
-│   └── ...
-├── config/              # 配置文件
-│   ├── ai.ts                 # AI 模型配置
-│   ├── cuisines.ts           # 菜系配置
-│   ├── ingredients.ts        # 食材配置
-│   └── ...
-├── services/            # 服务层
-│   ├── aiService.ts          # AI 接口服务
-│   ├── favoriteService.ts    # 收藏服务
-│   ├── imageService.ts       # 图片服务
-│   └── ...
-├── stores/              # 状态管理 🆕
-│   └── settings.js           # 配置状态管理 🆕
-├── utils/               # 工具函数
-│   ├── apiConfig.js          # API配置工具 🆕
-│   └── ...
-├── views/               # 页面组件
-│   ├── Home.vue              # 首页
-│   ├── Favorites.vue         # 收藏页
-│   ├── SauceDesign.vue       # 酱汁设计
-│   ├── SettingsDemo.vue      # 配置演示页 🆕
-│   └── ...
-├── types/               # TypeScript 类型定义
-└── router/              # 路由配置
-```
-
-## 🎯 开发指南
-
-### 添加新菜系
-
-1. 在 `src/config/cuisines.ts` 中添加菜系配置
-2. 为菜系大师编写专业的 AI Prompt
-3. 添加对应的图标和描述
-
-### 扩展功能模块
-
-1. 在 `src/views/` 中创建新页面组件
-2. 在 `src/router/` 中添加路由配置
-3. 在导航组件中添加入口
-
-### AI 服务集成
-
--   **文本生成**:`src/services/aiService.ts` - 支持动态配置切换
--   **图片生成**:`src/services/imageService.ts` - 多服务商支持
--   **配置管理**:`src/stores/settings.js` - 实时配置管理
--   **API 工具**:`src/utils/apiConfig.js` - 统一配置接口
-
-### 配置系统开发
-
-#### 添加新的配置项
-
-```javascript
-// 在 settings.js 中扩展配置结构
-const defaultSettings = {
-    textGeneration: {
-        // 现有配置...
-        newParam: 'default_value' // 新增配置
-    }
-}
-```
-
-#### 使用动态配置
-
-```javascript
-import { useSettingsStore } from '@/stores/settings'
-import { createTextGenerationRequest } from '@/utils/apiConfig'
-
-// 获取当前配置
-const settingsStore = useSettingsStore()
-const config = settingsStore.getTextGenerationConfig()
-
-// 创建API请求
-const requestConfig = createTextGenerationRequest(messages)
-```
-
-## 📈 Star History
-
-[![Star History Chart](https://api.star-history.com/svg?repos=liu-ziting/what-to-eat&type=Date)](https://www.star-history.com/#liu-ziting/what-to-eat&Date)
-
-## 🙏 致谢
-
--   [302.AI](https://share.302.ai/DymMSI) -菜谱生成 API
--   [智谱 AI](https://open.bigmodel.cn/) - 图片生成 API
--   [Vue.js](https://vuejs.org/) - 渐进式 JavaScript 框架
--   [Tailwind CSS](https://tailwindcss.com/) - 实用优先的 CSS 框架

+ 1 - 1
SETTINGS_USAGE.md

@@ -116,7 +116,7 @@ src/
 ## 配置存储
 
 -   **环境变量**: `.env` 文件中的默认配置
--   **用户配置**: localStorage 中的 `yifan-fengshen-settings` 键
+-   **用户配置**: localStorage 中的 `recipe-muse-settings` 键
 -   **优先级**: 用户配置 > 环境变量默认值
 
 ## 注意事项

+ 7 - 4
index.html

@@ -6,10 +6,10 @@
         <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 
         <!-- 基础SEO -->
-        <title>一饭封神 - AI智能菜谱生成平台</title>
+        <title>recipeMuse</title>
         <meta
             name="description"
-            content="一饭封神是专业的AI智能菜谱生成平台,汇聚中华八大菜系厨艺大师,根据您的食材智能生成个性化菜谱。支持营养分析、效果图生成,让烹饪变得简单有趣。"
+            content="配方缪斯是专业的AI智能菜谱生成平台,汇聚中华八大菜系厨艺大师,根据您的食材智能生成个性化菜谱。支持营养分析、效果图生成,让烹饪变得简单有趣。"
         />
         <meta name="keywords" content="AI菜谱,智能菜谱生成,八大菜系,川菜,粤菜,苏菜,鲁菜,浙菜,湘菜,闽菜,徽菜,营养分析,烹饪指导,食材搭配,美食推荐" />
         <meta name="author" content="liuziting" />
@@ -21,8 +21,8 @@
         <meta name="mobile-web-app-capable" content="yes" />
         <meta name="apple-mobile-web-app-capable" content="yes" />
         <meta name="apple-mobile-web-app-status-bar-style" content="default" />
-        <meta name="apple-mobile-web-app-title" content="一饭封神" />
-        <meta name="application-name" content="一饭封神" />
+        <meta name="apple-mobile-web-app-title" content="recipeMuse" />
+        <meta name="application-name" content="recipeMuse" />
         <meta name="theme-color" content="#fbbf24" />
         <meta name="msapplication-TileColor" content="#fbbf24" />
         <meta name="msapplication-navbutton-color" content="#fbbf24" />
@@ -35,7 +35,10 @@
         <link rel="preconnect" href="https://open.bigmodel.cn" />
         <link rel="dns-prefetch" href="https://fonts.googleapis.com" />
         <link rel="dns-prefetch" href="https://www.clarity.ms" />
+
+        <script src="https://cdn.jsdelivr.net/npm/eruda"></script>
     </head>
+    <script>eruda.init();</script>
     <body>
         <div id="app"></div>
         <script type="module" src="/src/main.ts"></script>

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 565 - 1285
package-lock.json


+ 7 - 5
package.json

@@ -1,12 +1,12 @@
 {
-    "name": "yifan-fengshen",
+    "name": "recipe-muse",
     "version": "1.0.0",
     "description": "一饭封神 - AI厨艺大师指导平台",
     "scripts": {
-        "dev": "vite",
-        "build": "vue-tsc --noEmit && vite build",
-        "build:netlify": "vite build --config vite.config.prod.ts",
-        "build:simple": "vite build --config vite.config.prod.ts",
+        "dev": "vite --mode development",
+        "build": "vue-tsc --noEmit && vite build --mode production",
+        "build:netlify": "vite build --config vite.config.prod.ts --mode production",
+        "build:simple": "vite build --config vite.config.prod.ts --mode production",
         "preview": "vite preview",
         "type-check": "vue-tsc --noEmit"
     },
@@ -15,7 +15,9 @@
         "axios": "^1.6.0",
         "markdown-it": "^14.1.0",
         "party-js": "^2.2.0",
+        "vant": "^4.9.21",
         "vue": "^3.4.0",
+        "vue-i18n": "^9.14.5",
         "vue-router": "^4.2.0"
     },
     "devDependencies": {

+ 1847 - 0
pnpm-lock.yaml

@@ -0,0 +1,1847 @@
+lockfileVersion: '6.0'
+
+settings:
+  autoInstallPeers: true
+  excludeLinksFromLockfile: false
+
+dependencies:
+  '@vueuse/core':
+    specifier: ^10.7.0
+    version: 10.11.1(vue@3.5.22)
+  axios:
+    specifier: ^1.6.0
+    version: 1.12.2
+  markdown-it:
+    specifier: ^14.1.0
+    version: 14.1.0
+  party-js:
+    specifier: ^2.2.0
+    version: 2.2.0
+  vant:
+    specifier: ^4.9.21
+    version: 4.9.21(vue@3.5.22)
+  vue:
+    specifier: ^3.4.0
+    version: 3.5.22(typescript@5.9.3)
+  vue-i18n:
+    specifier: ^9.14.5
+    version: 9.14.5(vue@3.5.22)
+  vue-router:
+    specifier: ^4.2.0
+    version: 4.6.3(vue@3.5.22)
+
+devDependencies:
+  '@types/node':
+    specifier: ^20.19.11
+    version: 20.19.22
+  '@vitejs/plugin-vue':
+    specifier: ^5.0.0
+    version: 5.2.4(vite@5.4.21)(vue@3.5.22)
+  autoprefixer:
+    specifier: ^10.4.0
+    version: 10.4.21(postcss@8.5.6)
+  postcss:
+    specifier: ^8.4.0
+    version: 8.5.6
+  tailwindcss:
+    specifier: ^3.4.0
+    version: 3.4.18
+  typescript:
+    specifier: ^5.3.0
+    version: 5.9.3
+  vite:
+    specifier: ^5.0.0
+    version: 5.4.21(@types/node@20.19.22)
+  vue-tsc:
+    specifier: ^3.0.6
+    version: 3.1.1(typescript@5.9.3)
+
+packages:
+
+  /@alloc/quick-lru@5.2.0:
+    resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /@babel/helper-string-parser@7.27.1:
+    resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
+    engines: {node: '>=6.9.0'}
+
+  /@babel/helper-validator-identifier@7.27.1:
+    resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==}
+    engines: {node: '>=6.9.0'}
+
+  /@babel/parser@7.28.4:
+    resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==}
+    engines: {node: '>=6.0.0'}
+    hasBin: true
+    dependencies:
+      '@babel/types': 7.28.4
+
+  /@babel/types@7.28.4:
+    resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/helper-string-parser': 7.27.1
+      '@babel/helper-validator-identifier': 7.27.1
+
+  /@esbuild/aix-ppc64@0.21.5:
+    resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==}
+    engines: {node: '>=12'}
+    cpu: [ppc64]
+    os: [aix]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/android-arm64@0.21.5:
+    resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [android]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/android-arm@0.21.5:
+    resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [android]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/android-x64@0.21.5:
+    resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [android]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/darwin-arm64@0.21.5:
+    resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [darwin]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/darwin-x64@0.21.5:
+    resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [darwin]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/freebsd-arm64@0.21.5:
+    resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [freebsd]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/freebsd-x64@0.21.5:
+    resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [freebsd]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-arm64@0.21.5:
+    resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-arm@0.21.5:
+    resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-ia32@0.21.5:
+    resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-loong64@0.21.5:
+    resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==}
+    engines: {node: '>=12'}
+    cpu: [loong64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-mips64el@0.21.5:
+    resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==}
+    engines: {node: '>=12'}
+    cpu: [mips64el]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-ppc64@0.21.5:
+    resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==}
+    engines: {node: '>=12'}
+    cpu: [ppc64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-riscv64@0.21.5:
+    resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==}
+    engines: {node: '>=12'}
+    cpu: [riscv64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-s390x@0.21.5:
+    resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==}
+    engines: {node: '>=12'}
+    cpu: [s390x]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/linux-x64@0.21.5:
+    resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [linux]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/netbsd-x64@0.21.5:
+    resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [netbsd]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/openbsd-x64@0.21.5:
+    resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [openbsd]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/sunos-x64@0.21.5:
+    resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [sunos]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/win32-arm64@0.21.5:
+    resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [win32]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/win32-ia32@0.21.5:
+    resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [win32]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@esbuild/win32-x64@0.21.5:
+    resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [win32]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@intlify/core-base@9.14.5:
+    resolution: {integrity: sha512-5ah5FqZG4pOoHjkvs8mjtv+gPKYU0zCISaYNjBNNqYiaITxW8ZtVih3GS/oTOqN8d9/mDLyrjD46GBApNxmlsA==}
+    engines: {node: '>= 16'}
+    dependencies:
+      '@intlify/message-compiler': 9.14.5
+      '@intlify/shared': 9.14.5
+    dev: false
+
+  /@intlify/message-compiler@9.14.5:
+    resolution: {integrity: sha512-IHzgEu61/YIpQV5Pc3aRWScDcnFKWvQA9kigcINcCBXN8mbW+vk9SK+lDxA6STzKQsVJxUPg9ACC52pKKo3SVQ==}
+    engines: {node: '>= 16'}
+    dependencies:
+      '@intlify/shared': 9.14.5
+      source-map-js: 1.2.1
+    dev: false
+
+  /@intlify/shared@9.14.5:
+    resolution: {integrity: sha512-9gB+E53BYuAEMhbCAxVgG38EZrk59sxBtv3jSizNL2hEWlgjBjAw1AwpLHtNaeda12pe6W20OGEa0TwuMSRbyQ==}
+    engines: {node: '>= 16'}
+    dev: false
+
+  /@isaacs/cliui@8.0.2:
+    resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
+    engines: {node: '>=12'}
+    dependencies:
+      string-width: 5.1.2
+      string-width-cjs: /string-width@4.2.3
+      strip-ansi: 7.1.2
+      strip-ansi-cjs: /strip-ansi@6.0.1
+      wrap-ansi: 8.1.0
+      wrap-ansi-cjs: /wrap-ansi@7.0.0
+    dev: true
+
+  /@jridgewell/gen-mapping@0.3.13:
+    resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
+    dependencies:
+      '@jridgewell/sourcemap-codec': 1.5.5
+      '@jridgewell/trace-mapping': 0.3.31
+    dev: true
+
+  /@jridgewell/resolve-uri@3.1.2:
+    resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+    engines: {node: '>=6.0.0'}
+    dev: true
+
+  /@jridgewell/sourcemap-codec@1.5.5:
+    resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
+
+  /@jridgewell/trace-mapping@0.3.31:
+    resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+    dependencies:
+      '@jridgewell/resolve-uri': 3.1.2
+      '@jridgewell/sourcemap-codec': 1.5.5
+    dev: true
+
+  /@nodelib/fs.scandir@2.1.5:
+    resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
+    engines: {node: '>= 8'}
+    dependencies:
+      '@nodelib/fs.stat': 2.0.5
+      run-parallel: 1.2.0
+    dev: true
+
+  /@nodelib/fs.stat@2.0.5:
+    resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
+    engines: {node: '>= 8'}
+    dev: true
+
+  /@nodelib/fs.walk@1.2.8:
+    resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
+    engines: {node: '>= 8'}
+    dependencies:
+      '@nodelib/fs.scandir': 2.1.5
+      fastq: 1.19.1
+    dev: true
+
+  /@pkgjs/parseargs@0.11.0:
+    resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
+    engines: {node: '>=14'}
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-android-arm-eabi@4.52.5:
+    resolution: {integrity: sha512-8c1vW4ocv3UOMp9K+gToY5zL2XiiVw3k7f1ksf4yO1FlDFQ1C2u72iACFnSOceJFsWskc2WZNqeRhFRPzv+wtQ==}
+    cpu: [arm]
+    os: [android]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-android-arm64@4.52.5:
+    resolution: {integrity: sha512-mQGfsIEFcu21mvqkEKKu2dYmtuSZOBMmAl5CFlPGLY94Vlcm+zWApK7F/eocsNzp8tKmbeBP8yXyAbx0XHsFNA==}
+    cpu: [arm64]
+    os: [android]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-darwin-arm64@4.52.5:
+    resolution: {integrity: sha512-takF3CR71mCAGA+v794QUZ0b6ZSrgJkArC+gUiG6LB6TQty9T0Mqh3m2ImRBOxS2IeYBo4lKWIieSvnEk2OQWA==}
+    cpu: [arm64]
+    os: [darwin]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-darwin-x64@4.52.5:
+    resolution: {integrity: sha512-W901Pla8Ya95WpxDn//VF9K9u2JbocwV/v75TE0YIHNTbhqUTv9w4VuQ9MaWlNOkkEfFwkdNhXgcLqPSmHy0fA==}
+    cpu: [x64]
+    os: [darwin]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-freebsd-arm64@4.52.5:
+    resolution: {integrity: sha512-QofO7i7JycsYOWxe0GFqhLmF6l1TqBswJMvICnRUjqCx8b47MTo46W8AoeQwiokAx3zVryVnxtBMcGcnX12LvA==}
+    cpu: [arm64]
+    os: [freebsd]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-freebsd-x64@4.52.5:
+    resolution: {integrity: sha512-jr21b/99ew8ujZubPo9skbrItHEIE50WdV86cdSoRkKtmWa+DDr6fu2c/xyRT0F/WazZpam6kk7IHBerSL7LDQ==}
+    cpu: [x64]
+    os: [freebsd]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-linux-arm-gnueabihf@4.52.5:
+    resolution: {integrity: sha512-PsNAbcyv9CcecAUagQefwX8fQn9LQ4nZkpDboBOttmyffnInRy8R8dSg6hxxl2Re5QhHBf6FYIDhIj5v982ATQ==}
+    cpu: [arm]
+    os: [linux]
+    libc: [glibc]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-linux-arm-musleabihf@4.52.5:
+    resolution: {integrity: sha512-Fw4tysRutyQc/wwkmcyoqFtJhh0u31K+Q6jYjeicsGJJ7bbEq8LwPWV/w0cnzOqR2m694/Af6hpFayLJZkG2VQ==}
+    cpu: [arm]
+    os: [linux]
+    libc: [musl]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-linux-arm64-gnu@4.52.5:
+    resolution: {integrity: sha512-a+3wVnAYdQClOTlyapKmyI6BLPAFYs0JM8HRpgYZQO02rMR09ZcV9LbQB+NL6sljzG38869YqThrRnfPMCDtZg==}
+    cpu: [arm64]
+    os: [linux]
+    libc: [glibc]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-linux-arm64-musl@4.52.5:
+    resolution: {integrity: sha512-AvttBOMwO9Pcuuf7m9PkC1PUIKsfaAJ4AYhy944qeTJgQOqJYJ9oVl2nYgY7Rk0mkbsuOpCAYSs6wLYB2Xiw0Q==}
+    cpu: [arm64]
+    os: [linux]
+    libc: [musl]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-linux-loong64-gnu@4.52.5:
+    resolution: {integrity: sha512-DkDk8pmXQV2wVrF6oq5tONK6UHLz/XcEVow4JTTerdeV1uqPeHxwcg7aFsfnSm9L+OO8WJsWotKM2JJPMWrQtA==}
+    cpu: [loong64]
+    os: [linux]
+    libc: [glibc]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-linux-ppc64-gnu@4.52.5:
+    resolution: {integrity: sha512-W/b9ZN/U9+hPQVvlGwjzi+Wy4xdoH2I8EjaCkMvzpI7wJUs8sWJ03Rq96jRnHkSrcHTpQe8h5Tg3ZzUPGauvAw==}
+    cpu: [ppc64]
+    os: [linux]
+    libc: [glibc]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-linux-riscv64-gnu@4.52.5:
+    resolution: {integrity: sha512-sjQLr9BW7R/ZiXnQiWPkErNfLMkkWIoCz7YMn27HldKsADEKa5WYdobaa1hmN6slu9oWQbB6/jFpJ+P2IkVrmw==}
+    cpu: [riscv64]
+    os: [linux]
+    libc: [glibc]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-linux-riscv64-musl@4.52.5:
+    resolution: {integrity: sha512-hq3jU/kGyjXWTvAh2awn8oHroCbrPm8JqM7RUpKjalIRWWXE01CQOf/tUNWNHjmbMHg/hmNCwc/Pz3k1T/j/Lg==}
+    cpu: [riscv64]
+    os: [linux]
+    libc: [musl]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-linux-s390x-gnu@4.52.5:
+    resolution: {integrity: sha512-gn8kHOrku8D4NGHMK1Y7NA7INQTRdVOntt1OCYypZPRt6skGbddska44K8iocdpxHTMMNui5oH4elPH4QOLrFQ==}
+    cpu: [s390x]
+    os: [linux]
+    libc: [glibc]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-linux-x64-gnu@4.52.5:
+    resolution: {integrity: sha512-hXGLYpdhiNElzN770+H2nlx+jRog8TyynpTVzdlc6bndktjKWyZyiCsuDAlpd+j+W+WNqfcyAWz9HxxIGfZm1Q==}
+    cpu: [x64]
+    os: [linux]
+    libc: [glibc]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-linux-x64-musl@4.52.5:
+    resolution: {integrity: sha512-arCGIcuNKjBoKAXD+y7XomR9gY6Mw7HnFBv5Rw7wQRvwYLR7gBAgV7Mb2QTyjXfTveBNFAtPt46/36vV9STLNg==}
+    cpu: [x64]
+    os: [linux]
+    libc: [musl]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-openharmony-arm64@4.52.5:
+    resolution: {integrity: sha512-QoFqB6+/9Rly/RiPjaomPLmR/13cgkIGfA40LHly9zcH1S0bN2HVFYk3a1eAyHQyjs3ZJYlXvIGtcCs5tko9Cw==}
+    cpu: [arm64]
+    os: [openharmony]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-win32-arm64-msvc@4.52.5:
+    resolution: {integrity: sha512-w0cDWVR6MlTstla1cIfOGyl8+qb93FlAVutcor14Gf5Md5ap5ySfQ7R9S/NjNaMLSFdUnKGEasmVnu3lCMqB7w==}
+    cpu: [arm64]
+    os: [win32]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-win32-ia32-msvc@4.52.5:
+    resolution: {integrity: sha512-Aufdpzp7DpOTULJCuvzqcItSGDH73pF3ko/f+ckJhxQyHtp67rHw3HMNxoIdDMUITJESNE6a8uh4Lo4SLouOUg==}
+    cpu: [ia32]
+    os: [win32]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-win32-x64-gnu@4.52.5:
+    resolution: {integrity: sha512-UGBUGPFp1vkj6p8wCRraqNhqwX/4kNQPS57BCFc8wYh0g94iVIW33wJtQAx3G7vrjjNtRaxiMUylM0ktp/TRSQ==}
+    cpu: [x64]
+    os: [win32]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@rollup/rollup-win32-x64-msvc@4.52.5:
+    resolution: {integrity: sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg==}
+    cpu: [x64]
+    os: [win32]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /@types/estree@1.0.8:
+    resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
+    dev: true
+
+  /@types/node@20.19.22:
+    resolution: {integrity: sha512-hRnu+5qggKDSyWHlnmThnUqg62l29Aj/6vcYgUaSFL9oc7DVjeWEQN3PRgdSc6F8d9QRMWkf36CLMch1Do/+RQ==}
+    dependencies:
+      undici-types: 6.21.0
+    dev: true
+
+  /@types/web-bluetooth@0.0.20:
+    resolution: {integrity: sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow==}
+    dev: false
+
+  /@vant/popperjs@1.3.0:
+    resolution: {integrity: sha512-hB+czUG+aHtjhaEmCJDuXOep0YTZjdlRR+4MSmIFnkCQIxJaXLQdSsR90XWvAI2yvKUI7TCGqR8pQg2RtvkMHw==}
+    dev: false
+
+  /@vant/use@1.6.0(vue@3.5.22):
+    resolution: {integrity: sha512-PHHxeAASgiOpSmMjceweIrv2AxDZIkWXyaczksMoWvKV2YAYEhoizRuk/xFnKF+emUIi46TsQ+rvlm/t2BBCfA==}
+    peerDependencies:
+      vue: ^3.0.0
+    dependencies:
+      vue: 3.5.22(typescript@5.9.3)
+    dev: false
+
+  /@vitejs/plugin-vue@5.2.4(vite@5.4.21)(vue@3.5.22):
+    resolution: {integrity: sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==}
+    engines: {node: ^18.0.0 || >=20.0.0}
+    peerDependencies:
+      vite: ^5.0.0 || ^6.0.0
+      vue: ^3.2.25
+    dependencies:
+      vite: 5.4.21(@types/node@20.19.22)
+      vue: 3.5.22(typescript@5.9.3)
+    dev: true
+
+  /@volar/language-core@2.4.23:
+    resolution: {integrity: sha512-hEEd5ET/oSmBC6pi1j6NaNYRWoAiDhINbT8rmwtINugR39loROSlufGdYMF9TaKGfz+ViGs1Idi3mAhnuPcoGQ==}
+    dependencies:
+      '@volar/source-map': 2.4.23
+    dev: true
+
+  /@volar/source-map@2.4.23:
+    resolution: {integrity: sha512-Z1Uc8IB57Lm6k7q6KIDu/p+JWtf3xsXJqAX/5r18hYOTpJyBn0KXUR8oTJ4WFYOcDzWC9n3IflGgHowx6U6z9Q==}
+    dev: true
+
+  /@volar/typescript@2.4.23:
+    resolution: {integrity: sha512-lAB5zJghWxVPqfcStmAP1ZqQacMpe90UrP5RJ3arDyrhy4aCUQqmxPPLB2PWDKugvylmO41ljK7vZ+t6INMTag==}
+    dependencies:
+      '@volar/language-core': 2.4.23
+      path-browserify: 1.0.1
+      vscode-uri: 3.1.0
+    dev: true
+
+  /@vue/compiler-core@3.5.22:
+    resolution: {integrity: sha512-jQ0pFPmZwTEiRNSb+i9Ow/I/cHv2tXYqsnHKKyCQ08irI2kdF5qmYedmF8si8mA7zepUFmJ2hqzS8CQmNOWOkQ==}
+    dependencies:
+      '@babel/parser': 7.28.4
+      '@vue/shared': 3.5.22
+      entities: 4.5.0
+      estree-walker: 2.0.2
+      source-map-js: 1.2.1
+
+  /@vue/compiler-dom@3.5.22:
+    resolution: {integrity: sha512-W8RknzUM1BLkypvdz10OVsGxnMAuSIZs9Wdx1vzA3mL5fNMN15rhrSCLiTm6blWeACwUwizzPVqGJgOGBEN/hA==}
+    dependencies:
+      '@vue/compiler-core': 3.5.22
+      '@vue/shared': 3.5.22
+
+  /@vue/compiler-sfc@3.5.22:
+    resolution: {integrity: sha512-tbTR1zKGce4Lj+JLzFXDq36K4vcSZbJ1RBu8FxcDv1IGRz//Dh2EBqksyGVypz3kXpshIfWKGOCcqpSbyGWRJQ==}
+    dependencies:
+      '@babel/parser': 7.28.4
+      '@vue/compiler-core': 3.5.22
+      '@vue/compiler-dom': 3.5.22
+      '@vue/compiler-ssr': 3.5.22
+      '@vue/shared': 3.5.22
+      estree-walker: 2.0.2
+      magic-string: 0.30.19
+      postcss: 8.5.6
+      source-map-js: 1.2.1
+
+  /@vue/compiler-ssr@3.5.22:
+    resolution: {integrity: sha512-GdgyLvg4R+7T8Nk2Mlighx7XGxq/fJf9jaVofc3IL0EPesTE86cP/8DD1lT3h1JeZr2ySBvyqKQJgbS54IX1Ww==}
+    dependencies:
+      '@vue/compiler-dom': 3.5.22
+      '@vue/shared': 3.5.22
+
+  /@vue/devtools-api@6.6.4:
+    resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==}
+    dev: false
+
+  /@vue/language-core@3.1.1(typescript@5.9.3):
+    resolution: {integrity: sha512-qjMY3Q+hUCjdH+jLrQapqgpsJ0rd/2mAY02lZoHG3VFJZZZKLjAlV+Oo9QmWIT4jh8+Rx8RUGUi++d7T9Wb6Mw==}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+    dependencies:
+      '@volar/language-core': 2.4.23
+      '@vue/compiler-dom': 3.5.22
+      '@vue/shared': 3.5.22
+      alien-signals: 3.0.3
+      muggle-string: 0.4.1
+      path-browserify: 1.0.1
+      picomatch: 4.0.3
+      typescript: 5.9.3
+    dev: true
+
+  /@vue/reactivity@3.5.22:
+    resolution: {integrity: sha512-f2Wux4v/Z2pqc9+4SmgZC1p73Z53fyD90NFWXiX9AKVnVBEvLFOWCEgJD3GdGnlxPZt01PSlfmLqbLYzY/Fw4A==}
+    dependencies:
+      '@vue/shared': 3.5.22
+
+  /@vue/runtime-core@3.5.22:
+    resolution: {integrity: sha512-EHo4W/eiYeAzRTN5PCextDUZ0dMs9I8mQ2Fy+OkzvRPUYQEyK9yAjbasrMCXbLNhF7P0OUyivLjIy0yc6VrLJQ==}
+    dependencies:
+      '@vue/reactivity': 3.5.22
+      '@vue/shared': 3.5.22
+
+  /@vue/runtime-dom@3.5.22:
+    resolution: {integrity: sha512-Av60jsryAkI023PlN7LsqrfPvwfxOd2yAwtReCjeuugTJTkgrksYJJstg1e12qle0NarkfhfFu1ox2D+cQotww==}
+    dependencies:
+      '@vue/reactivity': 3.5.22
+      '@vue/runtime-core': 3.5.22
+      '@vue/shared': 3.5.22
+      csstype: 3.1.3
+
+  /@vue/server-renderer@3.5.22(vue@3.5.22):
+    resolution: {integrity: sha512-gXjo+ao0oHYTSswF+a3KRHZ1WszxIqO7u6XwNHqcqb9JfyIL/pbWrrh/xLv7jeDqla9u+LK7yfZKHih1e1RKAQ==}
+    peerDependencies:
+      vue: 3.5.22
+    dependencies:
+      '@vue/compiler-ssr': 3.5.22
+      '@vue/shared': 3.5.22
+      vue: 3.5.22(typescript@5.9.3)
+
+  /@vue/shared@3.5.22:
+    resolution: {integrity: sha512-F4yc6palwq3TT0u+FYf0Ns4Tfl9GRFURDN2gWG7L1ecIaS/4fCIuFOjMTnCyjsu/OK6vaDKLCrGAa+KvvH+h4w==}
+
+  /@vueuse/core@10.11.1(vue@3.5.22):
+    resolution: {integrity: sha512-guoy26JQktXPcz+0n3GukWIy/JDNKti9v6VEMu6kV2sYBsWuGiTU8OWdg+ADfUbHg3/3DlqySDe7JmdHrktiww==}
+    dependencies:
+      '@types/web-bluetooth': 0.0.20
+      '@vueuse/metadata': 10.11.1
+      '@vueuse/shared': 10.11.1(vue@3.5.22)
+      vue-demi: 0.14.10(vue@3.5.22)
+    transitivePeerDependencies:
+      - '@vue/composition-api'
+      - vue
+    dev: false
+
+  /@vueuse/metadata@10.11.1:
+    resolution: {integrity: sha512-IGa5FXd003Ug1qAZmyE8wF3sJ81xGLSqTqtQ6jaVfkeZ4i5kS2mwQF61yhVqojRnenVew5PldLyRgvdl4YYuSw==}
+    dev: false
+
+  /@vueuse/shared@10.11.1(vue@3.5.22):
+    resolution: {integrity: sha512-LHpC8711VFZlDaYUXEBbFBCQ7GS3dVU9mjOhhMhXP6txTV4EhYQg/KGnQuvt/sPAtoUKq7VVUnL6mVtFoL42sA==}
+    dependencies:
+      vue-demi: 0.14.10(vue@3.5.22)
+    transitivePeerDependencies:
+      - '@vue/composition-api'
+      - vue
+    dev: false
+
+  /alien-signals@3.0.3:
+    resolution: {integrity: sha512-2JXjom6R7ZwrISpUphLhf4htUq1aKRCennTJ6u9kFfr3sLmC9+I4CxxVi+McoFnIg+p1HnVrfLT/iCt4Dlz//Q==}
+    dev: true
+
+  /ansi-regex@5.0.1:
+    resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /ansi-regex@6.2.2:
+    resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==}
+    engines: {node: '>=12'}
+    dev: true
+
+  /ansi-styles@4.3.0:
+    resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+    engines: {node: '>=8'}
+    dependencies:
+      color-convert: 2.0.1
+    dev: true
+
+  /ansi-styles@6.2.3:
+    resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==}
+    engines: {node: '>=12'}
+    dev: true
+
+  /any-promise@1.3.0:
+    resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
+    dev: true
+
+  /anymatch@3.1.3:
+    resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
+    engines: {node: '>= 8'}
+    dependencies:
+      normalize-path: 3.0.0
+      picomatch: 2.3.1
+    dev: true
+
+  /arg@5.0.2:
+    resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
+    dev: true
+
+  /argparse@2.0.1:
+    resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+    dev: false
+
+  /asynckit@0.4.0:
+    resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
+    dev: false
+
+  /autoprefixer@10.4.21(postcss@8.5.6):
+    resolution: {integrity: sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==}
+    engines: {node: ^10 || ^12 || >=14}
+    hasBin: true
+    peerDependencies:
+      postcss: ^8.1.0
+    dependencies:
+      browserslist: 4.26.3
+      caniuse-lite: 1.0.30001751
+      fraction.js: 4.3.7
+      normalize-range: 0.1.2
+      picocolors: 1.1.1
+      postcss: 8.5.6
+      postcss-value-parser: 4.2.0
+    dev: true
+
+  /axios@1.12.2:
+    resolution: {integrity: sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==}
+    dependencies:
+      follow-redirects: 1.15.11
+      form-data: 4.0.4
+      proxy-from-env: 1.1.0
+    transitivePeerDependencies:
+      - debug
+    dev: false
+
+  /balanced-match@1.0.2:
+    resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+    dev: true
+
+  /baseline-browser-mapping@2.8.18:
+    resolution: {integrity: sha512-UYmTpOBwgPScZpS4A+YbapwWuBwasxvO/2IOHArSsAhL/+ZdmATBXTex3t+l2hXwLVYK382ibr/nKoY9GKe86w==}
+    hasBin: true
+    dev: true
+
+  /binary-extensions@2.3.0:
+    resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /brace-expansion@2.0.2:
+    resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
+    dependencies:
+      balanced-match: 1.0.2
+    dev: true
+
+  /braces@3.0.3:
+    resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
+    engines: {node: '>=8'}
+    dependencies:
+      fill-range: 7.1.1
+    dev: true
+
+  /browserslist@4.26.3:
+    resolution: {integrity: sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==}
+    engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+    hasBin: true
+    dependencies:
+      baseline-browser-mapping: 2.8.18
+      caniuse-lite: 1.0.30001751
+      electron-to-chromium: 1.5.237
+      node-releases: 2.0.25
+      update-browserslist-db: 1.1.3(browserslist@4.26.3)
+    dev: true
+
+  /call-bind-apply-helpers@1.0.2:
+    resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      es-errors: 1.3.0
+      function-bind: 1.1.2
+    dev: false
+
+  /camelcase-css@2.0.1:
+    resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
+    engines: {node: '>= 6'}
+    dev: true
+
+  /caniuse-lite@1.0.30001751:
+    resolution: {integrity: sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw==}
+    dev: true
+
+  /chokidar@3.6.0:
+    resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
+    engines: {node: '>= 8.10.0'}
+    dependencies:
+      anymatch: 3.1.3
+      braces: 3.0.3
+      glob-parent: 5.1.2
+      is-binary-path: 2.1.0
+      is-glob: 4.0.3
+      normalize-path: 3.0.0
+      readdirp: 3.6.0
+    optionalDependencies:
+      fsevents: 2.3.3
+    dev: true
+
+  /color-convert@2.0.1:
+    resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+    engines: {node: '>=7.0.0'}
+    dependencies:
+      color-name: 1.1.4
+    dev: true
+
+  /color-name@1.1.4:
+    resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+    dev: true
+
+  /combined-stream@1.0.8:
+    resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
+    engines: {node: '>= 0.8'}
+    dependencies:
+      delayed-stream: 1.0.0
+    dev: false
+
+  /commander@4.1.1:
+    resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
+    engines: {node: '>= 6'}
+    dev: true
+
+  /cross-spawn@7.0.6:
+    resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
+    engines: {node: '>= 8'}
+    dependencies:
+      path-key: 3.1.1
+      shebang-command: 2.0.0
+      which: 2.0.2
+    dev: true
+
+  /cssesc@3.0.0:
+    resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
+    engines: {node: '>=4'}
+    hasBin: true
+    dev: true
+
+  /csstype@3.1.3:
+    resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+
+  /delayed-stream@1.0.0:
+    resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
+    engines: {node: '>=0.4.0'}
+    dev: false
+
+  /didyoumean@1.2.2:
+    resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
+    dev: true
+
+  /dlv@1.1.3:
+    resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
+    dev: true
+
+  /dunder-proto@1.0.1:
+    resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      call-bind-apply-helpers: 1.0.2
+      es-errors: 1.3.0
+      gopd: 1.2.0
+    dev: false
+
+  /eastasianwidth@0.2.0:
+    resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
+    dev: true
+
+  /electron-to-chromium@1.5.237:
+    resolution: {integrity: sha512-icUt1NvfhGLar5lSWH3tHNzablaA5js3HVHacQimfP8ViEBOQv+L7DKEuHdbTZ0SKCO1ogTJTIL1Gwk9S6Qvcg==}
+    dev: true
+
+  /emoji-regex@8.0.0:
+    resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
+    dev: true
+
+  /emoji-regex@9.2.2:
+    resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
+    dev: true
+
+  /entities@4.5.0:
+    resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
+    engines: {node: '>=0.12'}
+
+  /es-define-property@1.0.1:
+    resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
+    engines: {node: '>= 0.4'}
+    dev: false
+
+  /es-errors@1.3.0:
+    resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
+    engines: {node: '>= 0.4'}
+    dev: false
+
+  /es-object-atoms@1.1.1:
+    resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      es-errors: 1.3.0
+    dev: false
+
+  /es-set-tostringtag@2.1.0:
+    resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      es-errors: 1.3.0
+      get-intrinsic: 1.3.0
+      has-tostringtag: 1.0.2
+      hasown: 2.0.2
+    dev: false
+
+  /esbuild@0.21.5:
+    resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==}
+    engines: {node: '>=12'}
+    hasBin: true
+    requiresBuild: true
+    optionalDependencies:
+      '@esbuild/aix-ppc64': 0.21.5
+      '@esbuild/android-arm': 0.21.5
+      '@esbuild/android-arm64': 0.21.5
+      '@esbuild/android-x64': 0.21.5
+      '@esbuild/darwin-arm64': 0.21.5
+      '@esbuild/darwin-x64': 0.21.5
+      '@esbuild/freebsd-arm64': 0.21.5
+      '@esbuild/freebsd-x64': 0.21.5
+      '@esbuild/linux-arm': 0.21.5
+      '@esbuild/linux-arm64': 0.21.5
+      '@esbuild/linux-ia32': 0.21.5
+      '@esbuild/linux-loong64': 0.21.5
+      '@esbuild/linux-mips64el': 0.21.5
+      '@esbuild/linux-ppc64': 0.21.5
+      '@esbuild/linux-riscv64': 0.21.5
+      '@esbuild/linux-s390x': 0.21.5
+      '@esbuild/linux-x64': 0.21.5
+      '@esbuild/netbsd-x64': 0.21.5
+      '@esbuild/openbsd-x64': 0.21.5
+      '@esbuild/sunos-x64': 0.21.5
+      '@esbuild/win32-arm64': 0.21.5
+      '@esbuild/win32-ia32': 0.21.5
+      '@esbuild/win32-x64': 0.21.5
+    dev: true
+
+  /escalade@3.2.0:
+    resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /estree-walker@2.0.2:
+    resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
+
+  /fast-glob@3.3.3:
+    resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==}
+    engines: {node: '>=8.6.0'}
+    dependencies:
+      '@nodelib/fs.stat': 2.0.5
+      '@nodelib/fs.walk': 1.2.8
+      glob-parent: 5.1.2
+      merge2: 1.4.1
+      micromatch: 4.0.8
+    dev: true
+
+  /fastq@1.19.1:
+    resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==}
+    dependencies:
+      reusify: 1.1.0
+    dev: true
+
+  /fill-range@7.1.1:
+    resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
+    engines: {node: '>=8'}
+    dependencies:
+      to-regex-range: 5.0.1
+    dev: true
+
+  /follow-redirects@1.15.11:
+    resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==}
+    engines: {node: '>=4.0'}
+    peerDependencies:
+      debug: '*'
+    peerDependenciesMeta:
+      debug:
+        optional: true
+    dev: false
+
+  /foreground-child@3.3.1:
+    resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==}
+    engines: {node: '>=14'}
+    dependencies:
+      cross-spawn: 7.0.6
+      signal-exit: 4.1.0
+    dev: true
+
+  /form-data@4.0.4:
+    resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==}
+    engines: {node: '>= 6'}
+    dependencies:
+      asynckit: 0.4.0
+      combined-stream: 1.0.8
+      es-set-tostringtag: 2.1.0
+      hasown: 2.0.2
+      mime-types: 2.1.35
+    dev: false
+
+  /fraction.js@4.3.7:
+    resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
+    dev: true
+
+  /fsevents@2.3.3:
+    resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+    engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+    os: [darwin]
+    requiresBuild: true
+    dev: true
+    optional: true
+
+  /function-bind@1.1.2:
+    resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+
+  /get-intrinsic@1.3.0:
+    resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      call-bind-apply-helpers: 1.0.2
+      es-define-property: 1.0.1
+      es-errors: 1.3.0
+      es-object-atoms: 1.1.1
+      function-bind: 1.1.2
+      get-proto: 1.0.1
+      gopd: 1.2.0
+      has-symbols: 1.1.0
+      hasown: 2.0.2
+      math-intrinsics: 1.1.0
+    dev: false
+
+  /get-proto@1.0.1:
+    resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      dunder-proto: 1.0.1
+      es-object-atoms: 1.1.1
+    dev: false
+
+  /glob-parent@5.1.2:
+    resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+    engines: {node: '>= 6'}
+    dependencies:
+      is-glob: 4.0.3
+    dev: true
+
+  /glob-parent@6.0.2:
+    resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
+    engines: {node: '>=10.13.0'}
+    dependencies:
+      is-glob: 4.0.3
+    dev: true
+
+  /glob@10.4.5:
+    resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
+    hasBin: true
+    dependencies:
+      foreground-child: 3.3.1
+      jackspeak: 3.4.3
+      minimatch: 9.0.5
+      minipass: 7.1.2
+      package-json-from-dist: 1.0.1
+      path-scurry: 1.11.1
+    dev: true
+
+  /gopd@1.2.0:
+    resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
+    engines: {node: '>= 0.4'}
+    dev: false
+
+  /has-symbols@1.1.0:
+    resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
+    engines: {node: '>= 0.4'}
+    dev: false
+
+  /has-tostringtag@1.0.2:
+    resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      has-symbols: 1.1.0
+    dev: false
+
+  /hasown@2.0.2:
+    resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      function-bind: 1.1.2
+
+  /is-binary-path@2.1.0:
+    resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
+    engines: {node: '>=8'}
+    dependencies:
+      binary-extensions: 2.3.0
+    dev: true
+
+  /is-core-module@2.16.1:
+    resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
+    engines: {node: '>= 0.4'}
+    dependencies:
+      hasown: 2.0.2
+    dev: true
+
+  /is-extglob@2.1.1:
+    resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /is-fullwidth-code-point@3.0.0:
+    resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /is-glob@4.0.3:
+    resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      is-extglob: 2.1.1
+    dev: true
+
+  /is-number@7.0.0:
+    resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+    engines: {node: '>=0.12.0'}
+    dev: true
+
+  /isexe@2.0.0:
+    resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+    dev: true
+
+  /jackspeak@3.4.3:
+    resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
+    dependencies:
+      '@isaacs/cliui': 8.0.2
+    optionalDependencies:
+      '@pkgjs/parseargs': 0.11.0
+    dev: true
+
+  /jiti@1.21.7:
+    resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==}
+    hasBin: true
+    dev: true
+
+  /lilconfig@3.1.3:
+    resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==}
+    engines: {node: '>=14'}
+    dev: true
+
+  /lines-and-columns@1.2.4:
+    resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
+    dev: true
+
+  /linkify-it@5.0.0:
+    resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==}
+    dependencies:
+      uc.micro: 2.1.0
+    dev: false
+
+  /lru-cache@10.4.3:
+    resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
+    dev: true
+
+  /magic-string@0.30.19:
+    resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==}
+    dependencies:
+      '@jridgewell/sourcemap-codec': 1.5.5
+
+  /markdown-it@14.1.0:
+    resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==}
+    hasBin: true
+    dependencies:
+      argparse: 2.0.1
+      entities: 4.5.0
+      linkify-it: 5.0.0
+      mdurl: 2.0.0
+      punycode.js: 2.3.1
+      uc.micro: 2.1.0
+    dev: false
+
+  /math-intrinsics@1.1.0:
+    resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
+    engines: {node: '>= 0.4'}
+    dev: false
+
+  /mdurl@2.0.0:
+    resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==}
+    dev: false
+
+  /merge2@1.4.1:
+    resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
+    engines: {node: '>= 8'}
+    dev: true
+
+  /micromatch@4.0.8:
+    resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
+    engines: {node: '>=8.6'}
+    dependencies:
+      braces: 3.0.3
+      picomatch: 2.3.1
+    dev: true
+
+  /mime-db@1.52.0:
+    resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
+    engines: {node: '>= 0.6'}
+    dev: false
+
+  /mime-types@2.1.35:
+    resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
+    engines: {node: '>= 0.6'}
+    dependencies:
+      mime-db: 1.52.0
+    dev: false
+
+  /minimatch@9.0.5:
+    resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==}
+    engines: {node: '>=16 || 14 >=14.17'}
+    dependencies:
+      brace-expansion: 2.0.2
+    dev: true
+
+  /minipass@7.1.2:
+    resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
+    engines: {node: '>=16 || 14 >=14.17'}
+    dev: true
+
+  /muggle-string@0.4.1:
+    resolution: {integrity: sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==}
+    dev: true
+
+  /mz@2.7.0:
+    resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
+    dependencies:
+      any-promise: 1.3.0
+      object-assign: 4.1.1
+      thenify-all: 1.6.0
+    dev: true
+
+  /nanoid@3.3.11:
+    resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==}
+    engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+    hasBin: true
+
+  /node-releases@2.0.25:
+    resolution: {integrity: sha512-4auku8B/vw5psvTiiN9j1dAOsXvMoGqJuKJcR+dTdqiXEK20mMTk1UEo3HS16LeGQsVG6+qKTPM9u/qQ2LqATA==}
+    dev: true
+
+  /normalize-path@3.0.0:
+    resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /normalize-range@0.1.2:
+    resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /object-assign@4.1.1:
+    resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /object-hash@3.0.0:
+    resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
+    engines: {node: '>= 6'}
+    dev: true
+
+  /package-json-from-dist@1.0.1:
+    resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==}
+    dev: true
+
+  /party-js@2.2.0:
+    resolution: {integrity: sha512-50hGuALCpvDTrQLPQ1fgUgxKIWAH28ShVkmeK/3zhO0YJyCqkhrZhQEkWPxDYLvbFJ7YAXyROmFEu35gKpZLtQ==}
+    dev: false
+
+  /path-browserify@1.0.1:
+    resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
+    dev: true
+
+  /path-key@3.1.1:
+    resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /path-parse@1.0.7:
+    resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+    dev: true
+
+  /path-scurry@1.11.1:
+    resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==}
+    engines: {node: '>=16 || 14 >=14.18'}
+    dependencies:
+      lru-cache: 10.4.3
+      minipass: 7.1.2
+    dev: true
+
+  /picocolors@1.1.1:
+    resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+  /picomatch@2.3.1:
+    resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+    engines: {node: '>=8.6'}
+    dev: true
+
+  /picomatch@4.0.3:
+    resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
+    engines: {node: '>=12'}
+    dev: true
+
+  /pify@2.3.0:
+    resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /pirates@4.0.7:
+    resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==}
+    engines: {node: '>= 6'}
+    dev: true
+
+  /postcss-import@15.1.0(postcss@8.5.6):
+    resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}
+    engines: {node: '>=14.0.0'}
+    peerDependencies:
+      postcss: ^8.0.0
+    dependencies:
+      postcss: 8.5.6
+      postcss-value-parser: 4.2.0
+      read-cache: 1.0.0
+      resolve: 1.22.10
+    dev: true
+
+  /postcss-js@4.1.0(postcss@8.5.6):
+    resolution: {integrity: sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==}
+    engines: {node: ^12 || ^14 || >= 16}
+    peerDependencies:
+      postcss: ^8.4.21
+    dependencies:
+      camelcase-css: 2.0.1
+      postcss: 8.5.6
+    dev: true
+
+  /postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.6):
+    resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==}
+    engines: {node: '>= 18'}
+    peerDependencies:
+      jiti: '>=1.21.0'
+      postcss: '>=8.0.9'
+      tsx: ^4.8.1
+      yaml: ^2.4.2
+    peerDependenciesMeta:
+      jiti:
+        optional: true
+      postcss:
+        optional: true
+      tsx:
+        optional: true
+      yaml:
+        optional: true
+    dependencies:
+      jiti: 1.21.7
+      lilconfig: 3.1.3
+      postcss: 8.5.6
+    dev: true
+
+  /postcss-nested@6.2.0(postcss@8.5.6):
+    resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==}
+    engines: {node: '>=12.0'}
+    peerDependencies:
+      postcss: ^8.2.14
+    dependencies:
+      postcss: 8.5.6
+      postcss-selector-parser: 6.1.2
+    dev: true
+
+  /postcss-selector-parser@6.1.2:
+    resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==}
+    engines: {node: '>=4'}
+    dependencies:
+      cssesc: 3.0.0
+      util-deprecate: 1.0.2
+    dev: true
+
+  /postcss-value-parser@4.2.0:
+    resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
+    dev: true
+
+  /postcss@8.5.6:
+    resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==}
+    engines: {node: ^10 || ^12 || >=14}
+    dependencies:
+      nanoid: 3.3.11
+      picocolors: 1.1.1
+      source-map-js: 1.2.1
+
+  /proxy-from-env@1.1.0:
+    resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
+    dev: false
+
+  /punycode.js@2.3.1:
+    resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==}
+    engines: {node: '>=6'}
+    dev: false
+
+  /queue-microtask@1.2.3:
+    resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+    dev: true
+
+  /read-cache@1.0.0:
+    resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
+    dependencies:
+      pify: 2.3.0
+    dev: true
+
+  /readdirp@3.6.0:
+    resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
+    engines: {node: '>=8.10.0'}
+    dependencies:
+      picomatch: 2.3.1
+    dev: true
+
+  /resolve@1.22.10:
+    resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==}
+    engines: {node: '>= 0.4'}
+    hasBin: true
+    dependencies:
+      is-core-module: 2.16.1
+      path-parse: 1.0.7
+      supports-preserve-symlinks-flag: 1.0.0
+    dev: true
+
+  /reusify@1.1.0:
+    resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
+    engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+    dev: true
+
+  /rollup@4.52.5:
+    resolution: {integrity: sha512-3GuObel8h7Kqdjt0gxkEzaifHTqLVW56Y/bjN7PSQtkKr0w3V/QYSdt6QWYtd7A1xUtYQigtdUfgj1RvWVtorw==}
+    engines: {node: '>=18.0.0', npm: '>=8.0.0'}
+    hasBin: true
+    dependencies:
+      '@types/estree': 1.0.8
+    optionalDependencies:
+      '@rollup/rollup-android-arm-eabi': 4.52.5
+      '@rollup/rollup-android-arm64': 4.52.5
+      '@rollup/rollup-darwin-arm64': 4.52.5
+      '@rollup/rollup-darwin-x64': 4.52.5
+      '@rollup/rollup-freebsd-arm64': 4.52.5
+      '@rollup/rollup-freebsd-x64': 4.52.5
+      '@rollup/rollup-linux-arm-gnueabihf': 4.52.5
+      '@rollup/rollup-linux-arm-musleabihf': 4.52.5
+      '@rollup/rollup-linux-arm64-gnu': 4.52.5
+      '@rollup/rollup-linux-arm64-musl': 4.52.5
+      '@rollup/rollup-linux-loong64-gnu': 4.52.5
+      '@rollup/rollup-linux-ppc64-gnu': 4.52.5
+      '@rollup/rollup-linux-riscv64-gnu': 4.52.5
+      '@rollup/rollup-linux-riscv64-musl': 4.52.5
+      '@rollup/rollup-linux-s390x-gnu': 4.52.5
+      '@rollup/rollup-linux-x64-gnu': 4.52.5
+      '@rollup/rollup-linux-x64-musl': 4.52.5
+      '@rollup/rollup-openharmony-arm64': 4.52.5
+      '@rollup/rollup-win32-arm64-msvc': 4.52.5
+      '@rollup/rollup-win32-ia32-msvc': 4.52.5
+      '@rollup/rollup-win32-x64-gnu': 4.52.5
+      '@rollup/rollup-win32-x64-msvc': 4.52.5
+      fsevents: 2.3.3
+    dev: true
+
+  /run-parallel@1.2.0:
+    resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+    dependencies:
+      queue-microtask: 1.2.3
+    dev: true
+
+  /shebang-command@2.0.0:
+    resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+    engines: {node: '>=8'}
+    dependencies:
+      shebang-regex: 3.0.0
+    dev: true
+
+  /shebang-regex@3.0.0:
+    resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /signal-exit@4.1.0:
+    resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
+    engines: {node: '>=14'}
+    dev: true
+
+  /source-map-js@1.2.1:
+    resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+    engines: {node: '>=0.10.0'}
+
+  /string-width@4.2.3:
+    resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
+    engines: {node: '>=8'}
+    dependencies:
+      emoji-regex: 8.0.0
+      is-fullwidth-code-point: 3.0.0
+      strip-ansi: 6.0.1
+    dev: true
+
+  /string-width@5.1.2:
+    resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
+    engines: {node: '>=12'}
+    dependencies:
+      eastasianwidth: 0.2.0
+      emoji-regex: 9.2.2
+      strip-ansi: 7.1.2
+    dev: true
+
+  /strip-ansi@6.0.1:
+    resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
+    engines: {node: '>=8'}
+    dependencies:
+      ansi-regex: 5.0.1
+    dev: true
+
+  /strip-ansi@7.1.2:
+    resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==}
+    engines: {node: '>=12'}
+    dependencies:
+      ansi-regex: 6.2.2
+    dev: true
+
+  /sucrase@3.35.0:
+    resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==}
+    engines: {node: '>=16 || 14 >=14.17'}
+    hasBin: true
+    dependencies:
+      '@jridgewell/gen-mapping': 0.3.13
+      commander: 4.1.1
+      glob: 10.4.5
+      lines-and-columns: 1.2.4
+      mz: 2.7.0
+      pirates: 4.0.7
+      ts-interface-checker: 0.1.13
+    dev: true
+
+  /supports-preserve-symlinks-flag@1.0.0:
+    resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
+    engines: {node: '>= 0.4'}
+    dev: true
+
+  /tailwindcss@3.4.18:
+    resolution: {integrity: sha512-6A2rnmW5xZMdw11LYjhcI5846rt9pbLSabY5XPxo+XWdxwZaFEn47Go4NzFiHu9sNNmr/kXivP1vStfvMaK1GQ==}
+    engines: {node: '>=14.0.0'}
+    hasBin: true
+    dependencies:
+      '@alloc/quick-lru': 5.2.0
+      arg: 5.0.2
+      chokidar: 3.6.0
+      didyoumean: 1.2.2
+      dlv: 1.1.3
+      fast-glob: 3.3.3
+      glob-parent: 6.0.2
+      is-glob: 4.0.3
+      jiti: 1.21.7
+      lilconfig: 3.1.3
+      micromatch: 4.0.8
+      normalize-path: 3.0.0
+      object-hash: 3.0.0
+      picocolors: 1.1.1
+      postcss: 8.5.6
+      postcss-import: 15.1.0(postcss@8.5.6)
+      postcss-js: 4.1.0(postcss@8.5.6)
+      postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.6)
+      postcss-nested: 6.2.0(postcss@8.5.6)
+      postcss-selector-parser: 6.1.2
+      resolve: 1.22.10
+      sucrase: 3.35.0
+    transitivePeerDependencies:
+      - tsx
+      - yaml
+    dev: true
+
+  /thenify-all@1.6.0:
+    resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
+    engines: {node: '>=0.8'}
+    dependencies:
+      thenify: 3.3.1
+    dev: true
+
+  /thenify@3.3.1:
+    resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
+    dependencies:
+      any-promise: 1.3.0
+    dev: true
+
+  /to-regex-range@5.0.1:
+    resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+    engines: {node: '>=8.0'}
+    dependencies:
+      is-number: 7.0.0
+    dev: true
+
+  /ts-interface-checker@0.1.13:
+    resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
+    dev: true
+
+  /typescript@5.9.3:
+    resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
+    engines: {node: '>=14.17'}
+    hasBin: true
+
+  /uc.micro@2.1.0:
+    resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==}
+    dev: false
+
+  /undici-types@6.21.0:
+    resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
+    dev: true
+
+  /update-browserslist-db@1.1.3(browserslist@4.26.3):
+    resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==}
+    hasBin: true
+    peerDependencies:
+      browserslist: '>= 4.21.0'
+    dependencies:
+      browserslist: 4.26.3
+      escalade: 3.2.0
+      picocolors: 1.1.1
+    dev: true
+
+  /util-deprecate@1.0.2:
+    resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+    dev: true
+
+  /vant@4.9.21(vue@3.5.22):
+    resolution: {integrity: sha512-hXUoZMrLLjykimFRLDlGNd+K2iYSRh9YwLMKnsVdVZ+9inUKxpqnjhOqlZwocbnYkvJlS+febf9u9aJpDol4Pw==}
+    peerDependencies:
+      vue: ^3.0.0
+    dependencies:
+      '@vant/popperjs': 1.3.0
+      '@vant/use': 1.6.0(vue@3.5.22)
+      '@vue/shared': 3.5.22
+      vue: 3.5.22(typescript@5.9.3)
+    dev: false
+
+  /vite@5.4.21(@types/node@20.19.22):
+    resolution: {integrity: sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==}
+    engines: {node: ^18.0.0 || >=20.0.0}
+    hasBin: true
+    peerDependencies:
+      '@types/node': ^18.0.0 || >=20.0.0
+      less: '*'
+      lightningcss: ^1.21.0
+      sass: '*'
+      sass-embedded: '*'
+      stylus: '*'
+      sugarss: '*'
+      terser: ^5.4.0
+    peerDependenciesMeta:
+      '@types/node':
+        optional: true
+      less:
+        optional: true
+      lightningcss:
+        optional: true
+      sass:
+        optional: true
+      sass-embedded:
+        optional: true
+      stylus:
+        optional: true
+      sugarss:
+        optional: true
+      terser:
+        optional: true
+    dependencies:
+      '@types/node': 20.19.22
+      esbuild: 0.21.5
+      postcss: 8.5.6
+      rollup: 4.52.5
+    optionalDependencies:
+      fsevents: 2.3.3
+    dev: true
+
+  /vscode-uri@3.1.0:
+    resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
+    dev: true
+
+  /vue-demi@0.14.10(vue@3.5.22):
+    resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==}
+    engines: {node: '>=12'}
+    hasBin: true
+    requiresBuild: true
+    peerDependencies:
+      '@vue/composition-api': ^1.0.0-rc.1
+      vue: ^3.0.0-0 || ^2.6.0
+    peerDependenciesMeta:
+      '@vue/composition-api':
+        optional: true
+    dependencies:
+      vue: 3.5.22(typescript@5.9.3)
+    dev: false
+
+  /vue-i18n@9.14.5(vue@3.5.22):
+    resolution: {integrity: sha512-0jQ9Em3ymWngyiIkj0+c/k7WgaPO+TNzjKSNq9BvBQaKJECqn9cd9fL4tkDhB5G1QBskGl9YxxbDAhgbFtpe2g==}
+    engines: {node: '>= 16'}
+    peerDependencies:
+      vue: ^3.0.0
+    dependencies:
+      '@intlify/core-base': 9.14.5
+      '@intlify/shared': 9.14.5
+      '@vue/devtools-api': 6.6.4
+      vue: 3.5.22(typescript@5.9.3)
+    dev: false
+
+  /vue-router@4.6.3(vue@3.5.22):
+    resolution: {integrity: sha512-ARBedLm9YlbvQomnmq91Os7ck6efydTSpRP3nuOKCvgJOHNrhRoJDSKtee8kcL1Vf7nz6U+PMBL+hTvR3bTVQg==}
+    peerDependencies:
+      vue: ^3.5.0
+    dependencies:
+      '@vue/devtools-api': 6.6.4
+      vue: 3.5.22(typescript@5.9.3)
+    dev: false
+
+  /vue-tsc@3.1.1(typescript@5.9.3):
+    resolution: {integrity: sha512-fyixKxFniOVgn+L/4+g8zCG6dflLLt01Agz9jl3TO45Bgk87NZJRmJVPsiK+ouq3LB91jJCbOV+pDkzYTxbI7A==}
+    hasBin: true
+    peerDependencies:
+      typescript: '>=5.0.0'
+    dependencies:
+      '@volar/typescript': 2.4.23
+      '@vue/language-core': 3.1.1(typescript@5.9.3)
+      typescript: 5.9.3
+    dev: true
+
+  /vue@3.5.22(typescript@5.9.3):
+    resolution: {integrity: sha512-toaZjQ3a/G/mYaLSbV+QsQhIdMo9x5rrqIpYRObsJ6T/J+RyCSFwN2LHNVH9v8uIcljDNa3QzPVdv3Y6b9hAJQ==}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+    dependencies:
+      '@vue/compiler-dom': 3.5.22
+      '@vue/compiler-sfc': 3.5.22
+      '@vue/runtime-dom': 3.5.22
+      '@vue/server-renderer': 3.5.22(vue@3.5.22)
+      '@vue/shared': 3.5.22
+      typescript: 5.9.3
+
+  /which@2.0.2:
+    resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+    engines: {node: '>= 8'}
+    hasBin: true
+    dependencies:
+      isexe: 2.0.0
+    dev: true
+
+  /wrap-ansi@7.0.0:
+    resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
+    engines: {node: '>=10'}
+    dependencies:
+      ansi-styles: 4.3.0
+      string-width: 4.2.3
+      strip-ansi: 6.0.1
+    dev: true
+
+  /wrap-ansi@8.1.0:
+    resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
+    engines: {node: '>=12'}
+    dependencies:
+      ansi-styles: 6.2.3
+      string-width: 5.1.2
+      strip-ansi: 7.1.2
+    dev: true

+ 41 - 0
src/api/index.ts

@@ -0,0 +1,41 @@
+import request from '@/utils/request'
+
+export function appleLogin(data: { code: any; id_token: any; user?: string }) {
+  return request({
+    url: '/api/v1/auth/apple',
+    method: 'post',
+    data,
+  })
+}
+
+export function googleLogin(data: { id_token: any;}) {
+  return request({
+    url: '/api/v1/auth/google',
+    method: 'post',
+    data,
+  })
+}
+
+// 刷新 Token(需认证)
+export function refreshToken() {
+  return request({
+    url: '/api/v1/auth/refresh',
+    method: 'post',
+  })
+}
+
+// 获取用户信息(需认证)
+export function getProfile() {
+  return request({
+    url: '/api/v1/auth/profile',
+    method: 'get',
+  })
+}
+
+// 登出(需认证)
+export function logout() {
+  return request({
+    url: '/api/v1/auth/logout',
+    method: 'post',
+  })
+}

+ 2 - 3
src/components/GlobalFooter.vue

@@ -1,7 +1,6 @@
 <template>
-    <footer class="bg-white border-2 border-[#0A0910] max-w-7xl mx-auto px-2 rounded-lg p-4 text-center mt-8">
+    <!-- <footer class="bg-white border-2 border-[#0A0910] max-w-7xl mx-auto px-2 rounded-lg p-4 text-center mt-8">
         <div class="space-y-3">
-            <!-- 主要信息 -->
             <div class="flex flex-col md:flex-row items-center justify-center gap-2 md:gap-4">
                 <p class="text-sm text-gray-600">
                     © 2025 一饭封神 |
@@ -13,7 +12,7 @@
                 <p class="text-xs text-gray-500">🚀 Vibe Coding with Kiro AI</p>
             </div>
         </div>
-    </footer>
+    </footer> -->
 </template>
 
 <script setup lang="ts">

+ 7 - 3
src/components/GlobalNavigation.vue

@@ -21,8 +21,9 @@
 
                 <!-- 导航菜单 -->
                 <div class="flex items-center gap-2">
+                    <LoginButton />
                     <!-- 设置按钮 -->
-                    <SettingsButton />
+                    <!-- <SettingsButton /> -->
                     <!-- 主要功能 -->
                     <router-link
                         to="/"
@@ -136,8 +137,10 @@
                         </div>
                     </router-link>
                     <div class="flex items-center gap-2">
+                    
+                        <LoginButton />
                         <!-- 移动端设置按钮 -->
-                        <SettingsButton />
+                        <!-- <SettingsButton /> -->
                         <button @click="showMobileMenu = !showMobileMenu" class="p-2 bg-gray-100 hover:bg-gray-200 rounded-lg border-2 border-[#0A0910] transition-colors">
                             <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                                 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
@@ -237,7 +240,8 @@
 <script setup lang="ts">
 import { ref, computed } from 'vue'
 import { useRoute } from 'vue-router'
-import SettingsButton from './SettingsButton.vue'
+// import SettingsButton from './SettingsButton.vue'
+import LoginButton from './LoginButton.vue'
 
 const showMobileMenu = ref(false)
 const showMoreMenu = ref(false)

+ 28 - 0
src/components/LoginButton.vue

@@ -0,0 +1,28 @@
+<template>
+    <div>
+      <button @click="login" class="p-2 text-gray-600 hover:text-gray-800 hover:bg-gray-100 rounded-lg transition-colors" title="系统设置">
+        <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
+              <path
+                  stroke-linecap="round"
+                  stroke-linejoin="round"
+                  stroke-width="2"
+                  d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
+              ></path>
+          </svg>
+      </button>
+    </div>
+</template>
+
+<script setup>
+import { ref } from 'vue'
+
+import { triggerNativeLogin } from '@/hooks/useLogin'
+
+const showSettings = ref(false)
+
+
+const login = () => {
+  triggerNativeLogin()
+}
+
+</script>

+ 3 - 18
src/config/ai.ts

@@ -10,21 +10,6 @@ export interface AIConfig {
 
 // 菜系提示词模板
 export const CUISINE_PROMPTS = {
-    system: '你是一位专业的厨师,请根据用户提供的食材和菜系要求,生成详细的菜谱。',
-    responseFormat: `请按照以下JSON格式返回菜谱:
-{
-  "name": "菜品名称",
-  "ingredients": ["食材1", "食材2"],
-  "steps": [
-    {
-      "step": 1,
-      "description": "步骤描述",
-      "time": 5,
-      "temperature": "中火"
-    }
-  ],
-  "cookingTime": 30,
-  "difficulty": "medium",
-  "tips": ["技巧1", "技巧2"]
-}`
-}
+    system: 'ai.systemPrompt',
+    responseFormat: 'ai.responseFormat'
+}

+ 57 - 46
src/config/cuisines.ts

@@ -1,125 +1,136 @@
 import type { CuisineType } from '@/types'
 
-export const cuisines: CuisineType[] = [
+const cuisines: CuisineType[] = [
     {
         id: 'su',
-        name: '苏菜大师',
-        description: '江南水乡的精致美味',
+        name: 'cuisines.su.name',
+        description: 'cuisines.su.description',
         avatar: '🦐',
-        specialty: '清淡鲜美,刀工精细',
+        specialty: 'cuisines.su.specialty',
         prompt: `作为苏菜传承人,你精通淮扬菜系精髓。苏菜以清鲜雅致、刀工精湛、造型玲珑著称。请基于用户食材设计一道正统苏菜,突出食材本味与养生搭配。回答需包含:创意菜名、分步烹饪流程、刀工技法解析、营养平衡说明。`
     },
     {
         id: 'lu',
-        name: '鲁菜大师',
-        description: '齐鲁大地的豪放风味',
+        name: 'cuisines.lu.name',
+        description: 'cuisines.lu.description',
         avatar: '🐟',
-        specialty: '咸鲜为主,火候精准',
+        specialty: 'cuisines.lu.specialty',
         prompt: `身为鲁菜宗师,你深谙孔府宫廷菜真谛。鲁菜讲究咸鲜纯正、火功严谨、礼仪考究。请依据用户食材创作经典鲁菜,强调火候层次与五味调和。回答需包含:传统菜名、分步烹饪图解、关键火候节点、宫廷技法溯源。`
     },
     {
         id: 'chuan',
-        name: '川菜大师',
-        description: '巴蜀之地的麻辣传奇',
+        name: 'cuisines.chuan.name',
+        description: 'cuisines.chuan.description',
         avatar: '🌶️',
-        specialty: '麻辣鲜香,变化多端',
+        specialty: 'cuisines.chuan.specialty',
         prompt: `作为川味掌门,你掌握二十三味型精髓。川菜擅长麻辣平衡、复合调味、一菜一格。请针对用户食材设计地道川味,突出口感层次与红油运用。回答需包含:特色菜名、七步烹饪法、秘制调料配方、味型创新解析。`
     },
     {
         id: 'yue',
-        name: '粤菜大师',
-        description: '岭南文化的鲜美诠释',
+        name: 'cuisines.yue.name',
+        description: 'cuisines.yue.description',
         avatar: '🦆',
-        specialty: '清淡鲜美,原汁原味',
+        specialty: 'cuisines.yue.specialty',
         prompt: `身为粤菜泰斗,你崇尚清中求鲜理念。粤菜注重时令本味、镬气逼人、养生之道。请根据用户食材构思广府佳肴,凸显生猛鲜香与少油烹饪。回答需包含:意境菜名、精准火候时序、锁鲜技巧、药膳融合建议。`
     },
     {
         id: 'zhe',
-        name: '浙菜大师',
-        description: '江南水乡的清雅之味',
+        name: 'cuisines.zhe.name',
+        description: 'cuisines.zhe.description',
         avatar: '🐠',
-        specialty: '清香淡雅,鲜嫩爽滑',
+        specialty: 'cuisines.zhe.specialty',
         prompt: `作为浙菜传人,你深得南宋遗风真传。浙菜追求清雅时鲜、南料北烹、滑嫩见长。请基于用户食材创作江南风韵菜,突出时令搭配与脆嫩口感。回答需包含:诗意菜名、分步滑炒技法、时令食材解析、勾芡要诀。`
     },
     {
         id: 'xiang',
-        name: '湘菜大师',
-        description: '湖湘文化的辣味人生',
+        name: 'cuisines.xiang.name',
+        description: 'cuisines.xiang.description',
         avatar: '🔥',
-        specialty: '香辣浓郁,口味厚重',
+        specialty: 'cuisines.xiang.specialty',
         prompt: `身为湘味宗师,你精通腊熏剁椒秘技。湘菜讲究酸辣透味、油重色浓、乡野本真。请针对用户食材设计火辣湘肴,突出发酵辣味与油色融合。回答需包含:霸气菜名、三重辣味调制法、腊味处理秘笈、油色控制要诀。`
     },
     {
         id: 'min',
-        name: '闽菜大师',
-        description: '八闽大地的海鲜盛宴',
+        name: 'cuisines.min.name',
+        description: 'cuisines.min.description',
         avatar: '🦀',
-        specialty: '鲜香清淡,汤鲜味美',
+        specialty: 'cuisines.min.specialty',
         prompt: `作为闽菜大家,你传承佛跳墙精髓。闽菜擅长汤醇味隽、糟香四溢、山珍海味。请依据用户食材创作闽派珍馐,突出红糟提鲜与汤品层次。回答需包含:典故菜名、吊汤八法详解、海鲜保鲜术、糟汁调配比例。`
     },
     {
         id: 'hui',
-        name: '徽菜大师',
-        description: '徽州文化的朴实醇香',
+        name: 'cuisines.hui.name',
+        description: 'cuisines.hui.description',
         avatar: '🐷',
-        specialty: '重油重色,醇厚朴实',
+        specialty: 'cuisines.hui.specialty',
         prompt: `身为徽菜掌门,你掌握文火炖焖绝技。徽菜强调重油保色、火腿提鲜、山野本味。请针对用户食材设计徽州古韵菜,突出炭火慢炖与油色控制。回答需包含:徽派菜名、三阶段火功法、火腿吊味技巧、收汁成色要诀。`
     },
     // 国际菜系
     {
         id: 'japanese',
-        name: '日式料理大师',
-        description: '和食之道的极致美学',
+        name: 'cuisines.japanese.name',
+        description: 'cuisines.japanese.description',
         avatar: '🍣',
-        specialty: '清淡本味,季节感强',
+        specialty: 'cuisines.japanese.specialty',
         prompt: `作为和食职人,你深谙日本料理的精髓。日式料理追求食材本味、季节感、视觉美感和营养平衡。请基于用户食材创作正宗和食,突出umami鲜味与刀工技艺。回答需包含:雅致菜名、传统制作工艺、季节搭配理念、摆盘美学指导。请务必用中文回答,包括菜名也要翻译成中文。`
     },
     {
         id: 'korean',
-        name: '韩式料理大师',
-        description: '韩半岛的发酵智慧',
+        name: 'cuisines.korean.name',
+        description: 'cuisines.korean.description',
         avatar: '🥢',
-        specialty: '发酵调味,营养均衡',
+        specialty: 'cuisines.korean.specialty',
         prompt: `身为韩食专家,你精通发酵调味精髓。韩式料理讲究发酵食品、营养搭配、辣椒调味和banchan小菜文化。请依据用户食材设计地道韩食,突出发酵风味与营养平衡。回答需包含:韩式菜名、发酵调料运用、营养搭配原理、banchan配菜建议。请务必用中文回答,包括菜名也要翻译成中文。`
     },
     {
         id: 'italian',
-        name: '意式料理大师',
-        description: '地中海的阳光味道',
+        name: 'cuisines.italian.name',
+        description: 'cuisines.italian.description',
         avatar: '🍝',
-        specialty: '简约精致,橄榄油香',
+        specialty: 'cuisines.italian.specialty',
         prompt: `作为意大利厨师,你传承地中海饮食传统。意式料理崇尚简约、优质食材、橄榄油运用和区域特色。请根据用户食材创作正宗意式菜,突出食材品质与地域风味。回答需包含:意式菜名、橄榄油使用技巧、区域特色解析、意面/烩饭制作要点。请务必用中文回答,包括菜名也要翻译成中文。`
     },
     {
         id: 'french',
-        name: '法式料理大师',
-        description: '高卢雄鸡的优雅艺术',
+        name: 'cuisines.french.name',
+        description: 'cuisines.french.description',
         avatar: '🥖',
-        specialty: '精致优雅,酱汁丰富',
+        specialty: 'cuisines.french.specialty',
         prompt: `身为法餐主厨,你掌握经典法式烹饪技法。法式料理注重技法精湛、酱汁层次、食材搭配和摆盘艺术。请基于用户食材设计经典法菜,突出烹饪技法与酱汁调制。回答需包含:法式菜名、经典技法运用、酱汁制作秘诀、摆盘艺术指导。请务必用中文回答,包括菜名也要翻译成中文。`
     },
     {
         id: 'indian',
-        name: '印度料理大师',
-        description: '香料王国的神秘魅力',
+        name: 'cuisines.indian.name',
+        description: 'cuisines.indian.description',
         avatar: '🍛',
-        specialty: '香料丰富,层次复杂',
+        specialty: 'cuisines.indian.specialty',
         prompt: `作为印度香料大师,你精通香料调配艺术。印度料理以香料复合、层次丰富、素食友好和阿育吠陀养生为特色。请针对用户食材创作正宗印度菜,突出香料平衡与健康理念。回答需包含:印式菜名、香料配比秘方、烹饪技法详解、阿育吠陀养生原理。请务必用中文回答,包括菜名也要翻译成中文。`
     },
     {
         id: 'thai',
-        name: '泰式料理大师',
-        description: '暹罗王国的酸甜平衡',
+        name: 'cuisines.thai.name',
+        description: 'cuisines.thai.description',
         avatar: '🌶️',
-        specialty: '酸甜辣鲜,香草丰富',
+        specialty: 'cuisines.thai.specialty',
         prompt: `身为泰菜专家,你深谙泰式风味平衡之道。泰式料理追求酸甜辣咸的完美平衡、新鲜香草运用和椰浆调味。请依据用户食材设计正宗泰菜,突出风味层次与香草搭配。回答需包含:泰式菜名、四味平衡技巧、香草使用指南、椰浆调味要诀。请务必用中文回答,包括菜名也要翻译成中文。`
     },
     {
         id: 'mexican',
-        name: '墨西哥料理大师',
-        description: '阿兹特克的火辣传承',
+        name: 'cuisines.mexican.name',
+        description: 'cuisines.mexican.description',
         avatar: '🌮',
-        specialty: '辣椒丰富,玉米文化',
+        specialty: 'cuisines.mexican.specialty',
         prompt: `作为墨西哥厨师,你传承古老的阿兹特克烹饪智慧。墨西哥料理以辣椒品种丰富、玉米文化、豆类蛋白和特色酱料著称。请根据用户食材创作正宗墨西哥菜,突出辣椒运用与传统技法。回答需包含:墨式菜名、辣椒品种选择、玉米制品技巧、特色酱料调制方法。请务必用中文回答,包括菜名也要翻译成中文。`
     }
 ]
+
+export function getCuisines (t: (key: string) => string): CuisineType[] {
+  return cuisines.map(cuisine => ({
+      id: cuisine.id,
+      name: t(`cuisines.${cuisine.id}.name`),
+      description: t(`cuisines.${cuisine.id}.description`),
+      avatar: cuisine.avatar,
+      specialty: t(`cuisines.${cuisine.id}.specialty`),
+      prompt: cuisine.prompt
+  }))
+}

+ 365 - 169
src/config/fortune.ts

@@ -4,111 +4,207 @@ import type { ZodiacConfig, AnimalConfig, MoodConfig, FortuneTeller } from '@/ty
 export const zodiacConfigs: ZodiacConfig[] = [
     {
         id: 'aries',
-        name: '白羊座',
+        name: 'fortune.zodiac.aries.name',
         symbol: '♈',
-        element: '火',
-        traits: ['热情', '冲动', '勇敢', '直率'],
-        luckyColors: ['红色', '橙色'],
-        dates: '3.21-4.19'
+        element: 'fortune.zodiac.aries.element',
+        traits: [
+            'fortune.zodiac.aries.traits.0',
+            'fortune.zodiac.aries.traits.1',
+            'fortune.zodiac.aries.traits.2',
+            'fortune.zodiac.aries.traits.3'
+        ],
+        luckyColors: [
+            'fortune.zodiac.aries.luckyColors.0',
+            'fortune.zodiac.aries.luckyColors.1'
+        ],
+        dates: 'fortune.zodiac.aries.dates'
     },
     {
         id: 'taurus',
-        name: '金牛座',
+        name: 'fortune.zodiac.taurus.name',
         symbol: '♉',
-        element: '土',
-        traits: ['稳重', '固执', '实际', '美食家'],
-        luckyColors: ['绿色', '粉色'],
-        dates: '4.20-5.20'
+        element: 'fortune.zodiac.taurus.element',
+        traits: [
+            'fortune.zodiac.taurus.traits.0',
+            'fortune.zodiac.taurus.traits.1',
+            'fortune.zodiac.taurus.traits.2',
+            'fortune.zodiac.taurus.traits.3'
+        ],
+        luckyColors: [
+            'fortune.zodiac.taurus.luckyColors.0',
+            'fortune.zodiac.taurus.luckyColors.1'
+        ],
+        dates: 'fortune.zodiac.taurus.dates'
     },
     {
         id: 'gemini',
-        name: '双子座',
+        name: 'fortune.zodiac.gemini.name',
         symbol: '♊',
-        element: '风',
-        traits: ['机智', '多变', '好奇', '善交际'],
-        luckyColors: ['黄色', '银色'],
-        dates: '5.21-6.21'
+        element: 'fortune.zodiac.gemini.element',
+        traits: [
+            'fortune.zodiac.gemini.traits.0',
+            'fortune.zodiac.gemini.traits.1',
+            'fortune.zodiac.gemini.traits.2',
+            'fortune.zodiac.gemini.traits.3'
+        ],
+        luckyColors: [
+            'fortune.zodiac.gemini.luckyColors.0',
+            'fortune.zodiac.gemini.luckyColors.1'
+        ],
+        dates: 'fortune.zodiac.gemini.dates'
     },
     {
         id: 'cancer',
-        name: '巨蟹座',
+        name: 'fortune.zodiac.cancer.name',
         symbol: '♋',
-        element: '水',
-        traits: ['温柔', '顾家', '敏感', '直觉强'],
-        luckyColors: ['白色', '银色'],
-        dates: '6.22-7.22'
+        element: 'fortune.zodiac.cancer.element',
+        traits: [
+            'fortune.zodiac.cancer.traits.0',
+            'fortune.zodiac.cancer.traits.1',
+            'fortune.zodiac.cancer.traits.2',
+            'fortune.zodiac.cancer.traits.3'
+        ],
+        luckyColors: [
+            'fortune.zodiac.cancer.luckyColors.0',
+            'fortune.zodiac.cancer.luckyColors.1'
+        ],
+        dates: 'fortune.zodiac.cancer.dates'
     },
     {
         id: 'leo',
-        name: '狮子座',
+        name: 'fortune.zodiac.leo.name',
         symbol: '♌',
-        element: '火',
-        traits: ['自信', '慷慨', '领导力', '戏剧性'],
-        luckyColors: ['金色', '橙色'],
-        dates: '7.23-8.22'
+        element: 'fortune.zodiac.leo.element',
+        traits: [
+            'fortune.zodiac.leo.traits.0',
+            'fortune.zodiac.leo.traits.1',
+            'fortune.zodiac.leo.traits.2',
+            'fortune.zodiac.leo.traits.3'
+        ],
+        luckyColors: [
+            'fortune.zodiac.leo.luckyColors.0',
+            'fortune.zodiac.leo.luckyColors.1'
+        ],
+        dates: 'fortune.zodiac.leo.dates'
     },
     {
         id: 'virgo',
-        name: '处女座',
+        name: 'fortune.zodiac.virgo.name',
         symbol: '♍',
-        element: '土',
-        traits: ['完美主义', '细心', '实用', '分析力强'],
-        luckyColors: ['深蓝', '灰色'],
-        dates: '8.23-9.22'
+        element: 'fortune.zodiac.virgo.element',
+        traits: [
+            'fortune.zodiac.virgo.traits.0',
+            'fortune.zodiac.virgo.traits.1',
+            'fortune.zodiac.virgo.traits.2',
+            'fortune.zodiac.virgo.traits.3'
+        ],
+        luckyColors: [
+            'fortune.zodiac.virgo.luckyColors.0',
+            'fortune.zodiac.virgo.luckyColors.1'
+        ],
+        dates: 'fortune.zodiac.virgo.dates'
     },
     {
         id: 'libra',
-        name: '天秤座',
+        name: 'fortune.zodiac.libra.name',
         symbol: '♎',
-        element: '风',
-        traits: ['和谐', '优雅', '犹豫', '社交'],
-        luckyColors: ['粉色', '浅蓝'],
-        dates: '9.23-10.23'
+        element: 'fortune.zodiac.libra.element',
+        traits: [
+            'fortune.zodiac.libra.traits.0',
+            'fortune.zodiac.libra.traits.1',
+            'fortune.zodiac.libra.traits.2',
+            'fortune.zodiac.libra.traits.3'
+        ],
+        luckyColors: [
+            'fortune.zodiac.libra.luckyColors.0',
+            'fortune.zodiac.libra.luckyColors.1'
+        ],
+        dates: 'fortune.zodiac.libra.dates'
     },
     {
         id: 'scorpio',
-        name: '天蝎座',
+        name: 'fortune.zodiac.scorpio.name',
         symbol: '♏',
-        element: '水',
-        traits: ['神秘', '专注', '激情', '直觉'],
-        luckyColors: ['深红', '黑色'],
-        dates: '10.24-11.22'
+        element: 'fortune.zodiac.scorpio.element',
+        traits: [
+            'fortune.zodiac.scorpio.traits.0',
+            'fortune.zodiac.scorpio.traits.1',
+            'fortune.zodiac.scorpio.traits.2',
+            'fortune.zodiac.scorpio.traits.3'
+        ],
+        luckyColors: [
+            'fortune.zodiac.scorpio.luckyColors.0',
+            'fortune.zodiac.scorpio.luckyColors.1'
+        ],
+        dates: 'fortune.zodiac.scorpio.dates'
     },
     {
         id: 'sagittarius',
-        name: '射手座',
+        name: 'fortune.zodiac.sagittarius.name',
         symbol: '♐',
-        element: '火',
-        traits: ['自由', '乐观', '冒险', '哲学'],
-        luckyColors: ['紫色', '深蓝'],
-        dates: '11.23-12.21'
+        element: 'fortune.zodiac.sagittarius.element',
+        traits: [
+            'fortune.zodiac.sagittarius.traits.0',
+            'fortune.zodiac.sagittarius.traits.1',
+            'fortune.zodiac.sagittarius.traits.2',
+            'fortune.zodiac.sagittarius.traits.3'
+        ],
+        luckyColors: [
+            'fortune.zodiac.sagittarius.luckyColors.0',
+            'fortune.zodiac.sagittarius.luckyColors.1'
+        ],
+        dates: 'fortune.zodiac.sagittarius.dates'
     },
     {
         id: 'capricorn',
-        name: '摩羯座',
+        name: 'fortune.zodiac.capricorn.name',
         symbol: '♑',
-        element: '土',
-        traits: ['务实', '有野心', '保守', '负责'],
-        luckyColors: ['黑色', '深绿'],
-        dates: '12.22-1.19'
+        element: 'fortune.zodiac.capricorn.element',
+        traits: [
+            'fortune.zodiac.capricorn.traits.0',
+            'fortune.zodiac.capricorn.traits.1',
+            'fortune.zodiac.capricorn.traits.2',
+            'fortune.zodiac.capricorn.traits.3'
+        ],
+        luckyColors: [
+            'fortune.zodiac.capricorn.luckyColors.0',
+            'fortune.zodiac.capricorn.luckyColors.1'
+        ],
+        dates: 'fortune.zodiac.capricorn.dates'
     },
     {
         id: 'aquarius',
-        name: '水瓶座',
+        name: 'fortune.zodiac.aquarius.name',
         symbol: '♒',
-        element: '风',
-        traits: ['独立', '创新', '人道主义', '理想'],
-        luckyColors: ['蓝色', '银色'],
-        dates: '1.20-2.18'
+        element: 'fortune.zodiac.aquarius.element',
+        traits: [
+            'fortune.zodiac.aquarius.traits.0',
+            'fortune.zodiac.aquarius.traits.1',
+            'fortune.zodiac.aquarius.traits.2',
+            'fortune.zodiac.aquarius.traits.3'
+        ],
+        luckyColors: [
+            'fortune.zodiac.aquarius.luckyColors.0',
+            'fortune.zodiac.aquarius.luckyColors.1'
+        ],
+        dates: 'fortune.zodiac.aquarius.dates'
     },
     {
         id: 'pisces',
-        name: '双鱼座',
+        name: 'fortune.zodiac.pisces.name',
         symbol: '♓',
-        element: '水',
-        traits: ['梦幻', '同情心', '艺术', '直觉'],
-        luckyColors: ['海蓝', '紫色'],
-        dates: '2.19-3.20'
+        element: 'fortune.zodiac.pisces.element',
+        traits: [
+            'fortune.zodiac.pisces.traits.0',
+            'fortune.zodiac.pisces.traits.1',
+            'fortune.zodiac.pisces.traits.2',
+            'fortune.zodiac.pisces.traits.3'
+        ],
+        luckyColors: [
+            'fortune.zodiac.pisces.luckyColors.0',
+            'fortune.zodiac.pisces.luckyColors.1'
+        ],
+        dates: 'fortune.zodiac.pisces.dates'
     }
 ]
 
@@ -116,109 +212,169 @@ export const zodiacConfigs: ZodiacConfig[] = [
 export const animalConfigs: AnimalConfig[] = [
     {
         id: 'rat',
-        name: '',
+        name: 'fortune.animals.rat.name',
         symbol: '🐭',
-        element: '水',
-        traits: ['机智', '灵活', '适应力强', '节俭'],
+        element: 'fortune.animals.rat.element',
+        traits: [
+            'fortune.animals.rat.traits.0',
+            'fortune.animals.rat.traits.1',
+            'fortune.animals.rat.traits.2',
+            'fortune.animals.rat.traits.3'
+        ],
         luckyNumbers: [2, 3],
         years: [2020, 2008, 1996, 1984, 1972, 1960]
     },
     {
         id: 'ox',
-        name: '',
+        name: 'fortune.animals.ox.name',
         symbol: '🐮',
-        element: '土',
-        traits: ['勤劳', '稳重', '诚实', '固执'],
+        element: 'fortune.animals.ox.element',
+        traits: [
+            'fortune.animals.ox.traits.0',
+            'fortune.animals.ox.traits.1',
+            'fortune.animals.ox.traits.2',
+            'fortune.animals.ox.traits.3'
+        ],
         luckyNumbers: [1, 9],
         years: [2021, 2009, 1997, 1985, 1973, 1961]
     },
     {
         id: 'tiger',
-        name: '',
+        name: 'fortune.animals.tiger.name',
         symbol: '🐯',
-        element: '木',
-        traits: ['勇敢', '自信', '竞争', '冲动'],
+        element: 'fortune.animals.tiger.element',
+        traits: [
+            'fortune.animals.tiger.traits.0',
+            'fortune.animals.tiger.traits.1',
+            'fortune.animals.tiger.traits.2',
+            'fortune.animals.tiger.traits.3'
+        ],
         luckyNumbers: [1, 3, 4],
         years: [2022, 2010, 1998, 1986, 1974, 1962]
     },
     {
         id: 'rabbit',
-        name: '',
+        name: 'fortune.animals.rabbit.name',
         symbol: '🐰',
-        element: '木',
-        traits: ['温和', '谨慎', '优雅', '善良'],
+        element: 'fortune.animals.rabbit.element',
+        traits: [
+            'fortune.animals.rabbit.traits.0',
+            'fortune.animals.rabbit.traits.1',
+            'fortune.animals.rabbit.traits.2',
+            'fortune.animals.rabbit.traits.3'
+        ],
         luckyNumbers: [3, 4, 6],
         years: [2023, 2011, 1999, 1987, 1975, 1963]
     },
     {
         id: 'dragon',
-        name: '',
+        name: 'fortune.animals.dragon.name',
         symbol: '🐲',
-        element: '土',
-        traits: ['威严', '热情', '创新', '领导'],
+        element: 'fortune.animals.dragon.element',
+        traits: [
+            'fortune.animals.dragon.traits.0',
+            'fortune.animals.dragon.traits.1',
+            'fortune.animals.dragon.traits.2',
+            'fortune.animals.dragon.traits.3'
+        ],
         luckyNumbers: [1, 6, 7],
         years: [2024, 2012, 2000, 1988, 1976, 1964]
     },
     {
         id: 'snake',
-        name: '',
+        name: 'fortune.animals.snake.name',
         symbol: '🐍',
-        element: '火',
-        traits: ['智慧', '神秘', '直觉', '优雅'],
+        element: 'fortune.animals.snake.element',
+        traits: [
+            'fortune.animals.snake.traits.0',
+            'fortune.animals.snake.traits.1',
+            'fortune.animals.snake.traits.2',
+            'fortune.animals.snake.traits.3'
+        ],
         luckyNumbers: [2, 8, 9],
         years: [2025, 2013, 2001, 1989, 1977, 1965]
     },
     {
         id: 'horse',
-        name: '',
+        name: 'fortune.animals.horse.name',
         symbol: '🐴',
-        element: '火',
-        traits: ['自由', '热情', '独立', '冒险'],
+        element: 'fortune.animals.horse.element',
+        traits: [
+            'fortune.animals.horse.traits.0',
+            'fortune.animals.horse.traits.1',
+            'fortune.animals.horse.traits.2',
+            'fortune.animals.horse.traits.3'
+        ],
         luckyNumbers: [2, 3, 7],
         years: [2026, 2014, 2002, 1990, 1978, 1966]
     },
     {
         id: 'goat',
-        name: '',
+        name: 'fortune.animals.goat.name',
         symbol: '🐐',
-        element: '土',
-        traits: ['温柔', '艺术', '同情', '和平'],
+        element: 'fortune.animals.goat.element',
+        traits: [
+            'fortune.animals.goat.traits.0',
+            'fortune.animals.goat.traits.1',
+            'fortune.animals.goat.traits.2',
+            'fortune.animals.goat.traits.3'
+        ],
         luckyNumbers: [3, 4, 5],
         years: [2027, 2015, 2003, 1991, 1979, 1967]
     },
     {
         id: 'monkey',
-        name: '',
+        name: 'fortune.animals.monkey.name',
         symbol: '🐵',
-        element: '金',
-        traits: ['聪明', '机智', '活泼', '好奇'],
+        element: 'fortune.animals.monkey.element',
+        traits: [
+            'fortune.animals.monkey.traits.0',
+            'fortune.animals.monkey.traits.1',
+            'fortune.animals.monkey.traits.2',
+            'fortune.animals.monkey.traits.3'
+        ],
         luckyNumbers: [1, 7, 8],
         years: [2028, 2016, 2004, 1992, 1980, 1968]
     },
     {
         id: 'rooster',
-        name: '',
+        name: 'fortune.animals.rooster.name',
         symbol: '🐓',
-        element: '金',
-        traits: ['勤奋', '准时', '诚实', '自信'],
+        element: 'fortune.animals.rooster.element',
+        traits: [
+            'fortune.animals.rooster.traits.0',
+            'fortune.animals.rooster.traits.1',
+            'fortune.animals.rooster.traits.2',
+            'fortune.animals.rooster.traits.3'
+        ],
         luckyNumbers: [5, 7, 8],
         years: [2029, 2017, 2005, 1993, 1981, 1969]
     },
     {
         id: 'dog',
-        name: '',
+        name: 'fortune.animals.dog.name',
         symbol: '🐕',
-        element: '土',
-        traits: ['忠诚', '诚实', '负责', '公正'],
+        element: 'fortune.animals.dog.element',
+        traits: [
+            'fortune.animals.dog.traits.0',
+            'fortune.animals.dog.traits.1',
+            'fortune.animals.dog.traits.2',
+            'fortune.animals.dog.traits.3'
+        ],
         luckyNumbers: [3, 4, 9],
         years: [2030, 2018, 2006, 1994, 1982, 1970]
     },
     {
         id: 'pig',
-        name: '',
+        name: 'fortune.animals.pig.name',
         symbol: '🐷',
-        element: '水',
-        traits: ['善良', '慷慨', '诚实', '乐观'],
+        element: 'fortune.animals.pig.element',
+        traits: [
+            'fortune.animals.pig.traits.0',
+            'fortune.animals.pig.traits.1',
+            'fortune.animals.pig.traits.2',
+            'fortune.animals.pig.traits.3'
+        ],
         luckyNumbers: [2, 5, 8],
         years: [2031, 2019, 2007, 1995, 1983, 1971]
     }
@@ -228,159 +384,199 @@ export const animalConfigs: AnimalConfig[] = [
 export const moodConfigs: MoodConfig[] = [
     {
         id: 'happy',
-        name: '开心',
+        name: 'fortune.moods.happy.name',
         emoji: '😊',
         color: 'text-yellow-500',
-        cookingStyle: ['甜品', '色彩丰富', '庆祝菜品', '轻松制作'],
-        description: '心情愉悦,适合制作色彩缤纷的美食'
+        cookingStyle: [
+            'fortune.moods.happy.cookingStyle.0',
+            'fortune.moods.happy.cookingStyle.1',
+            'fortune.moods.happy.cookingStyle.2',
+            'fortune.moods.happy.cookingStyle.3'
+        ],
+        description: 'fortune.moods.happy.description'
     },
     {
         id: 'sad',
-        name: '难过',
+        name: 'fortune.moods.sad.name',
         emoji: '😢',
         color: 'text-blue-500',
-        cookingStyle: ['温暖汤品', '治愈系', '家常菜', '慢炖'],
-        description: '需要温暖治愈的食物来抚慰心灵'
+        cookingStyle: [
+            'fortune.moods.sad.cookingStyle.0',
+            'fortune.moods.sad.cookingStyle.1',
+            'fortune.moods.sad.cookingStyle.2',
+            'fortune.moods.sad.cookingStyle.3'
+        ],
+        description: 'fortune.moods.sad.description'
     },
     {
         id: 'anxious',
-        name: '焦虑',
+        name: 'fortune.moods.anxious.name',
         emoji: '😰',
         color: 'text-orange-500',
-        cookingStyle: ['清淡菜品', '舒缓茶饮', '简单制作', '健康'],
-        description: '选择简单清淡的食物,避免复杂制作'
+        cookingStyle: [
+            'fortune.moods.anxious.cookingStyle.0',
+            'fortune.moods.anxious.cookingStyle.1',
+            'fortune.moods.anxious.cookingStyle.2',
+            'fortune.moods.anxious.cookingStyle.3'
+        ],
+        description: 'fortune.moods.anxious.description'
     },
     {
         id: 'tired',
-        name: '疲惫',
+        name: 'fortune.moods.tired.name',
         emoji: '😴',
         color: 'text-gray-500',
-        cookingStyle: ['营养补充', '快手菜', '能量食物', '简便'],
-        description: '需要快速补充能量的营养食物'
+        cookingStyle: [
+            'fortune.moods.tired.cookingStyle.0',
+            'fortune.moods.tired.cookingStyle.1',
+            'fortune.moods.tired.cookingStyle.2',
+            'fortune.moods.tired.cookingStyle.3'
+        ],
+        description: 'fortune.moods.tired.description'
     },
     {
         id: 'excited',
-        name: '兴奋',
+        name: 'fortune.moods.excited.name',
         emoji: '🤩',
         color: 'text-red-500',
-        cookingStyle: ['挑战菜品', '创新料理', '复杂制作', '实验'],
-        description: '精力充沛,适合尝试有挑战性的菜品'
+        cookingStyle: [
+            'fortune.moods.excited.cookingStyle.0',
+            'fortune.moods.excited.cookingStyle.1',
+            'fortune.moods.excited.cookingStyle.2',
+            'fortune.moods.excited.cookingStyle.3'
+        ],
+        description: 'fortune.moods.excited.description'
     },
     {
         id: 'calm',
-        name: '平静',
+        name: 'fortune.moods.calm.name',
         emoji: '😌',
         color: 'text-green-500',
-        cookingStyle: ['素食', '清淡', '禅意料理', '慢节奏'],
-        description: '心境平和,适合制作清淡素雅的菜品'
+        cookingStyle: [
+            'fortune.moods.calm.cookingStyle.0',
+            'fortune.moods.calm.cookingStyle.1',
+            'fortune.moods.calm.cookingStyle.2',
+            'fortune.moods.calm.cookingStyle.3'
+        ],
+        description: 'fortune.moods.calm.description'
     },
     {
         id: 'angry',
-        name: '愤怒',
+        name: 'fortune.moods.angry.name',
         emoji: '😠',
         color: 'text-red-600',
-        cookingStyle: ['辛辣菜品', '重口味', '发泄式烹饪', '刺激'],
-        description: '通过制作重口味食物来释放情绪'
+        cookingStyle: [
+            'fortune.moods.angry.cookingStyle.0',
+            'fortune.moods.angry.cookingStyle.1',
+            'fortune.moods.angry.cookingStyle.2',
+            'fortune.moods.angry.cookingStyle.3'
+        ],
+        description: 'fortune.moods.angry.description'
     },
     {
         id: 'nostalgic',
-        name: '思念',
+        name: 'fortune.moods.nostalgic.name',
         emoji: '🥺',
         color: 'text-purple-500',
-        cookingStyle: ['怀旧菜品', '家乡味', '传统料理', '回忆'],
-        description: '制作充满回忆的传统家乡菜'
+        cookingStyle: [
+            'fortune.moods.nostalgic.cookingStyle.0',
+            'fortune.moods.nostalgic.cookingStyle.1',
+            'fortune.moods.nostalgic.cookingStyle.2',
+            'fortune.moods.nostalgic.cookingStyle.3'
+        ],
+        description: 'fortune.moods.nostalgic.description'
     }
 ]
 
 // 占卜师角色配置
 export const fortuneTeller: FortuneTeller = {
-    name: '星月占卜师',
+    name: 'fortune.fortuneTeller.name',
     avatar: '🔮',
     greetings: [
-        '欢迎来到神秘的料理占卜殿堂...',
-        '星辰指引着美食的方向,让我为你占卜...',
-        '水晶球中浮现出美味的预言...',
-        '命运之轮正在转动,你的专属料理即将揭晓...'
+        'fortune.fortuneTeller.greetings.0',
+        'fortune.fortuneTeller.greetings.1',
+        'fortune.fortuneTeller.greetings.2',
+        'fortune.fortuneTeller.greetings.3'
     ],
     phrases: {
         daily: {
             opening: [
-                '让我感应今日的星象能量...',
-                '星座与生肖的力量正在汇聚...',
-                '今日的宇宙能量将指引你的味蕾...'
+                'fortune.fortuneTeller.phrases.daily.opening.0',
+                'fortune.fortuneTeller.phrases.daily.opening.1',
+                'fortune.fortuneTeller.phrases.daily.opening.2'
             ],
             closing: [
-                '愿这道幸运料理为你带来好运...',
-                '星辰已为你选定了今日的美味...',
-                '按照星象的指引,享受这份美食吧...'
+                'fortune.fortuneTeller.phrases.daily.closing.0',
+                'fortune.fortuneTeller.phrases.daily.closing.1',
+                'fortune.fortuneTeller.phrases.daily.closing.2'
             ],
             processing: [
-                '正在解读星座的秘密...',
-                '生肖的智慧正在显现...',
-                '宇宙的能量正在汇聚...'
+                'fortune.fortuneTeller.phrases.daily.processing.0',
+                'fortune.fortuneTeller.phrases.daily.processing.1',
+                'fortune.fortuneTeller.phrases.daily.processing.2'
             ]
         },
         mood: {
             opening: [
-                '让我感受你内心的情感波动...',
-                '心灵的频率正在与美食共鸣...',
-                '情感的色彩将决定你的料理...'
+                'fortune.fortuneTeller.phrases.mood.opening.0',
+                'fortune.fortuneTeller.phrases.mood.opening.1',
+                'fortune.fortuneTeller.phrases.mood.opening.2'
             ],
             closing: [
-                '愿这道治愈料理抚慰你的心灵...',
-                '美食是最好的情感治愈师...',
-                '让味蕾带走你的烦恼...'
+                'fortune.fortuneTeller.phrases.mood.closing.0',
+                'fortune.fortuneTeller.phrases.mood.closing.1',
+                'fortune.fortuneTeller.phrases.mood.closing.2'
             ],
             processing: [
-                '正在分析你的情感能量...',
-                '心情的颜色正在显现...',
-                '治愈的力量正在汇聚...'
+                'fortune.fortuneTeller.phrases.mood.processing.0',
+                'fortune.fortuneTeller.phrases.mood.processing.1',
+                'fortune.fortuneTeller.phrases.mood.processing.2'
             ]
         },
         couple: {
             opening: [
-                '让我感应你们之间的缘分...',
-                '两颗心的频率正在共鸣...',
-                '默契的火花即将点燃美食...'
+                'fortune.fortuneTeller.phrases.couple.opening.0',
+                'fortune.fortuneTeller.phrases.couple.opening.1',
+                'fortune.fortuneTeller.phrases.couple.opening.2'
             ],
             closing: [
-                '愿这道缘分料理增进你们的感情...',
-                '在共同烹饪中发现彼此的美好...',
-                '美食见证你们的默契...'
+                'fortune.fortuneTeller.phrases.couple.closing.0',
+                'fortune.fortuneTeller.phrases.couple.closing.1',
+                'fortune.fortuneTeller.phrases.couple.closing.2'
             ],
             processing: [
-                '正在分析你们的星座配对...',
-                '缘分的丝线正在编织...',
-                '默契指数正在计算...'
+                'fortune.fortuneTeller.phrases.couple.processing.0',
+                'fortune.fortuneTeller.phrases.couple.processing.1',
+                'fortune.fortuneTeller.phrases.couple.processing.2'
             ]
         },
         number: {
             opening: [
-                '数字是宇宙的语言...',
-                '让我解读这个神秘数字的含义...',
-                '数字中隐藏着美食的秘密...'
+                'fortune.fortuneTeller.phrases.number.opening.0',
+                'fortune.fortuneTeller.phrases.number.opening.1',
+                'fortune.fortuneTeller.phrases.number.opening.2'
             ],
             closing: [
-                '数字的力量将为你带来幸运...',
-                '在这个神秘数字的指引下享受美食...',
-                '数字与美味的完美结合...'
+                'fortune.fortuneTeller.phrases.number.closing.0',
+                'fortune.fortuneTeller.phrases.number.closing.1',
+                'fortune.fortuneTeller.phrases.number.closing.2'
             ],
             processing: [
-                '正在解析数字的神秘力量...',
-                '数字的能量正在显现...',
-                '宇宙密码正在解锁...'
+                'fortune.fortuneTeller.phrases.number.processing.0',
+                'fortune.fortuneTeller.phrases.number.processing.1',
+                'fortune.fortuneTeller.phrases.number.processing.2'
             ]
         }
     },
     mysticalWords: [
-        '星辰不会说谎,美食不会背叛...',
-        '在料理中寻找生活的真谛...',
-        '每一道菜都是命运的安排...',
-        '味蕾是通往心灵的桥梁...',
-        '美食中蕴含着宇宙的智慧...',
-        '烹饪是一种神圣的仪式...',
-        '在锅碗瓢盆中感受生活的节拍...',
-        '每一次品尝都是一次心灵的旅行...'
+        'fortune.fortuneTeller.mysticalWords.0',
+        'fortune.fortuneTeller.mysticalWords.1',
+        'fortune.fortuneTeller.mysticalWords.2',
+        'fortune.fortuneTeller.mysticalWords.3',
+        'fortune.fortuneTeller.mysticalWords.4',
+        'fortune.fortuneTeller.mysticalWords.5',
+        'fortune.fortuneTeller.mysticalWords.6',
+        'fortune.fortuneTeller.mysticalWords.7'
     ]
 }
 

+ 110 - 290
src/config/ingredients.ts

@@ -7,344 +7,164 @@ export interface IngredientCategory {
     items: string[]
 }
 
-export const ingredientCategories: IngredientCategory[] = [
+// 食材分类配置模板(不包含国际化文案)
+const ingredientCategoriesConfig = [
     {
         id: 'meat',
-        name: '荤菜',
         icon: '🥩',
         color: 'bg-red-100 border-red-300 text-red-800',
-        items: [
-            '猪肉',
-            '牛肉',
-            '羊肉',
-            '鸡肉',
-            '鸭肉',
-            '鹅肉',
-            '兔肉',
-            '驴肉',
-            '猪排骨',
-            '牛排',
-            '羊排',
-            '鸡翅',
-            '鸡腿',
-            '鸡胸肉',
-            '鸡爪',
-            '鸭腿',
-            '五花肉',
-            '瘦肉',
-            '肉丝',
-            '肉片',
-            '肉丁',
-            '肉馅',
-            '里脊肉',
-            '梅花肉',
-            '腊肉',
-            '香肠',
-            '火腿',
-            '培根',
-            '腊肠',
-            '咸肉',
-            '风干肉',
-            '牛肉干',
-            '猪蹄',
-            '猪肚',
-            '猪肝',
-            '猪心',
-            '猪肺',
-            '猪腰',
-            '牛肚',
-            '羊肚',
-            '鸡心',
-            '鸡肝',
-            '鸡胗',
-            '鸭血',
-            '猪血'
+        itemKeys: [
+            'pork', 'beef', 'lamb', 'chicken', 'duck', 'goose', 'rabbit', 'donkey',
+            'porkRibs', 'beefSteak', 'lambChops', 'chickenWings', 'chickenLegs', 'chickenBreast', 'chickenFeet', 'duckLegs',
+            'porkBelly', 'leanMeat', 'shreddedMeat', 'slicedMeat', 'dicedMeat', 'mincedMeat',
+            'tenderloin', 'porkShoulder', 'curedMeat', 'sausage', 'ham', 'bacon', 'chineseSausage',
+            'saltedMeat', 'airDriedMeat', 'beefJerky', 'porkTrotters', 'porkTripe', 'porkLiver',
+            'porkHeart', 'porkLung', 'porkKidney', 'beefTripe', 'lambTripe', 'chickenHeart',
+            'chickenLiver', 'chickenGizzard', 'duckBlood', 'pigBlood'
         ]
     },
     {
         id: 'seafood',
-        name: '海鲜',
         icon: '🦀',
         color: 'bg-blue-100 border-blue-300 text-blue-800',
-        items: [
-            '鲈鱼',
-            '鲫鱼',
-            '草鱼',
-            '鲤鱼',
-            '带鱼',
-            '黄鱼',
-            '鳕鱼',
-            '三文鱼',
-            '金枪鱼',
-            '鲳鱼',
-            '石斑鱼',
-            '桂鱼',
-            '鲢鱼',
-            '青鱼',
-            '武昌鱼',
-            '比目鱼',
-            '鳗鱼',
-            '刀鱼',
-            '大虾',
-            '基围虾',
-            '龙虾',
-            '白虾',
-            '河虾',
-            '明虾',
-            '对虾',
-            '皮皮虾',
-            '螃蟹',
-            '大闸蟹',
-            '梭子蟹',
-            '青蟹',
-            '毛蟹',
-            '扇贝',
-            '蛤蜊',
-            '生蚝',
-            '牡蛎',
-            '花甲',
-            '蚬子',
-            '海螺',
-            '田螺',
-            '鱿鱼',
-            '章鱼',
-            '墨鱼',
-            '海参',
-            '鲍鱼',
-            '海带',
-            '紫菜',
-            '裙带菜'
+        itemKeys: [
+            'seaBass', 'crucianCarp', 'grassCarp', 'carp', 'hairtail', 'yellowCroaker', 'cod', 'salmon',
+            'tuna', 'pomfret', 'grouper', 'mandarinFish', 'silverCarp', 'blackCarp', 'wuchangFish', 'flounder',
+            'eel', 'swordfish', 'largeShrimp', 'kurumaShrimp', 'lobster', 'whiteShrimp', 'riverShrimp',
+            'prawn', 'pairedShrimp', 'mantisShrimp', 'crab', 'hairyCrab', 'swimmingCrab', 'greenCrab',
+            'hairyCrab2', 'scallop', 'clam', 'oyster', 'oyster2', 'clam2', 'clam3', 'conch', 'snail',
+            'squid', 'octopus', 'cuttlefish', 'seaCucumber', 'abalone', 'kelp', 'nori', 'wakame'
         ]
     },
     {
         id: 'vegetables',
-        name: '蔬菜',
         icon: '🥬',
         color: 'bg-green-100 border-green-300 text-green-800',
-        items: [
-            '白菜',
-            '大白菜',
-            '小白菜',
-            '娃娃菜',
-            '菠菜',
-            '韭菜',
-            '韭黄',
-            '芹菜',
-            '西芹',
-            '生菜',
-            '油菜',
-            '小油菜',
-            '菜心',
-            '芥菜',
-            '空心菜',
-            '苋菜',
-            '茼蒿',
-            '西红柿',
-            '樱桃番茄',
-            '黄瓜',
-            '小黄瓜',
-            '茄子',
-            '长茄子',
-            '圆茄子',
-            '豆角',
-            '四季豆',
-            '扁豆',
-            '豇豆',
-            '荷兰豆',
-            '豌豆',
-            '毛豆',
-            '青椒',
-            '红椒',
-            '黄椒',
-            '彩椒',
-            '尖椒',
-            '朝天椒',
-            '小米椒',
-            '泡椒',
-            '土豆',
-            '红薯',
-            '紫薯',
-            '山药',
-            '芋头',
-            '莲藕',
-            '荸荠',
-            '慈姑',
-            '萝卜',
-            '白萝卜',
-            '胡萝卜',
-            '青萝卜',
-            '心里美萝卜',
-            '樱桃萝卜',
-            '洋葱',
-            '大葱',
-            '小葱',
-            '香葱',
-            '蒜苗',
-            '蒜黄',
-            '大蒜',
-            '生姜',
-            '冬瓜',
-            '南瓜',
-            '丝瓜',
-            '苦瓜',
-            '黄瓜',
-            '西葫芦',
-            '佛手瓜',
-            '蛇瓜',
-            '豆芽',
-            '绿豆芽',
-            '黄豆芽',
-            '豆苗',
-            '萝卜苗',
-            '香椿',
-            '蕨菜',
-            '马齿苋',
-            '菜花',
-            '西兰花',
-            '包菜',
-            '紫甘蓝',
-            '芥蓝',
-            '花椰菜'
+        itemKeys: [
+            'cabbage', 'chineseCabbage', 'babyCabbage', 'babyBokChoy', 'spinach', 'chineseChives', 'yellowChives',
+            'celery', 'westernCelery', 'lettuce', 'rapeseed', 'babyRapeseed', 'babyChoySum', 'mustardGreens',
+            'waterSpinach', 'amaranth', 'crownDaisy', 'tomato', 'cherryTomato', 'cucumber', 'babyCucumber',
+            'eggplant', 'longEggplant', 'roundEggplant', 'greenBeans', 'snapBeans', 'hyacinthBeans', 'cowpeas',
+            'snowPeas', 'peas', 'edamame', 'greenPepper', 'redPepper', 'yellowPepper', 'bellPepper',
+            'hotPepper', 'heavenFacingPepper', 'birdsEyeChili', 'pickledPepper', 'potato', 'sweetPotato',
+            'purpleSweetPotato', 'chineseYam', 'taro', 'lotusRoot', 'waterChestnut', 'arrowhead', 'radish',
+            'whiteRadish', 'carrot', 'greenRadish', 'redRadish', 'cherryRadish', 'onion', 'scallion',
+            'greenOnion', 'chive', 'garlicSprouts', 'yellowGarlicSprouts', 'garlic', 'ginger', 'winterMelon',
+            'pumpkin', 'luffa', 'bitterMelon', 'cucumber2', 'zucchini', 'buddhasHand', 'snakeGourd',
+            'beanSprouts', 'mungBeanSprouts', 'soybeanSprouts', 'peaShoots', 'radishSprouts', 'chineseToon',
+            'bracken', 'purslane', 'cauliflower', 'broccoli', 'cabbage2', 'purpleCabbage', 'chineseBroccoli', 'cauliflower2'
         ]
     },
     {
         id: 'mushrooms',
-        name: '菌菇',
         icon: '🍄',
         color: 'bg-yellow-100 border-yellow-300 text-yellow-800',
-        items: [
-            '香菇',
-            '花菇',
-            '平菇',
-            '金针菇',
-            '杏鲍菇',
-            '茶树菇',
-            '草菇',
-            '口蘑',
-            '蟹味菇',
-            '白玉菇',
-            '海鲜菇',
-            '鸡腿菇',
-            '滑子菇',
-            '秀珍菇',
-            '木耳',
-            '黑木耳',
-            '银耳',
-            '雪耳',
-            '毛木耳',
-            '竹荪',
-            '猴头菇',
-            '松茸',
-            '牛肝菌',
-            '羊肚菌',
-            '鸡油菌',
-            '榛蘑',
-            '元蘑'
+        itemKeys: [
+            'shiitake', 'flowerMushroom', 'oysterMushroom', 'enokiMushroom', 'kingOysterMushroom', 'teaTreeMushroom',
+            'strawMushroom', 'buttonMushroom', 'crabMushroom', 'whiteJadeMushroom', 'seafoodMushroom', 'chickenLegMushroom',
+            'slipperyMushroom', 'xiuZhenMushroom', 'woodEar', 'blackWoodEar', 'silverEar', 'snowEar', 'hairyWoodEar',
+            'bambooFungus', 'lionsMane', 'matsutake', 'porcini', 'morel', 'chanterelle', 'hazelMushroom', 'yuanMushroom'
         ]
     },
     {
         id: 'beans',
-        name: '豆类',
         icon: '🫘',
         color: 'bg-orange-100 border-orange-300 text-orange-800',
-        items: [
-            '豆腐',
-            '嫩豆腐',
-            '老豆腐',
-            '内酯豆腐',
-            '豆腐干',
-            '香干',
-            '白干',
-            '豆皮',
-            '腐竹',
-            '千张',
-            '豆泡',
-            '油豆腐',
-            '冻豆腐',
-            '臭豆腐',
-            '毛豆',
-            '豌豆',
-            '蚕豆',
-            '绿豆',
-            '红豆',
-            '黑豆',
-            '黄豆',
-            '芸豆',
-            '豆芽',
-            '豆苗',
-            '豆角',
-            '四季豆',
-            '扁豆',
-            '豇豆',
-            '荷兰豆',
-            '腐乳',
-            '豆豉',
-            '纳豆',
-            '豆浆',
-            '豆花'
+        itemKeys: [
+            'tofu', 'softTofu', 'firmTofu', 'silkenTofu', 'driedTofu', 'fragrantTofu', 'whiteDriedTofu', 'tofuSkin',
+            'driedTofuSkin', 'thousandSheets', 'tofuPuffs', 'friedTofu', 'frozenTofu', 'stinkyTofu', 'edamame2',
+            'peas2', 'broadBeans', 'mungBeans', 'redBeans', 'blackBeans', 'soybeans', 'kidneyBeans', 'beanSprouts2',
+            'peaShoots2', 'greenBeans2', 'snapBeans2', 'hyacinthBeans2', 'cowpeas2', 'snowPeas2', 'fermentedTofu',
+            'blackBeanSauce', 'natto', 'soyMilk', 'tofuPudding'
         ]
     },
     {
         id: 'eggs',
-        name: '蛋类',
         icon: '🥚',
         color: 'bg-amber-100 border-amber-300 text-amber-800',
-        items: ['鸡蛋', '土鸡蛋', '鸭蛋', '鹅蛋', '鹌鹑蛋', '鸽子蛋', '咸鸭蛋', '松花蛋', '皮蛋', '茶叶蛋', '卤蛋', '溏心蛋']
+        itemKeys: [
+            'egg', 'freeRangeEgg', 'duckEgg', 'gooseEgg', 'quailEgg', 'pigeonEgg', 'saltedDuckEgg',
+            'preservedEgg', 'centuryEgg', 'teaEgg', 'marinatedEgg', 'softBoiledEgg'
+        ]
     },
-
     {
         id: 'fruits',
-        name: '水果',
         icon: '🍎',
         color: 'bg-pink-100 border-pink-300 text-pink-800',
-        items: [
-            '苹果',
-            '梨',
-            '桃子',
-            '李子',
-            '杏',
-            '樱桃',
-            '葡萄',
-            '草莓',
-            '蓝莓',
-            '香蕉',
-            '橙子',
-            '柚子',
-            '柠檬',
-            '橘子',
-            '桔子',
-            '金桔',
-            '柑橘',
-            '西瓜',
-            '哈密瓜',
-            '甜瓜',
-            '香瓜',
-            '木瓜',
-            '芒果',
-            '菠萝',
-            '猕猴桃',
-            '火龙果',
-            '百香果',
-            '荔枝',
-            '龙眼',
-            '枣',
-            '柿子',
-            '石榴',
-            '山楂'
+        itemKeys: [
+            'apple', 'pear', 'peach', 'plum', 'apricot', 'cherry', 'grape', 'strawberry', 'blueberry',
+            'banana', 'orange', 'pomelo', 'lemon', 'tangerine', 'mandarin', 'kumquat', 'citrus',
+            'watermelon', 'hamiMelon', 'melon', 'muskmelon', 'papaya', 'mango', 'pineapple', 'kiwi',
+            'dragonFruit', 'passionFruit', 'lychee', 'longan', 'jujube', 'persimmon', 'pomegranate', 'hawthorn'
         ]
     },
     {
         id: 'nuts',
-        name: '坚果',
         icon: '🥜',
         color: 'bg-amber-100 border-amber-300 text-amber-800',
-        items: ['花生', '核桃', '杏仁', '腰果', '开心果', '松子', '榛子', '板栗', '瓜子', '葵花籽', '南瓜子', '西瓜子', '芝麻', '黑芝麻', '白芝麻']
+        itemKeys: [
+            'peanut', 'walnut', 'almond', 'cashew', 'pistachio', 'pineNut', 'hazelnut', 'chestnut',
+            'sunflowerSeeds', 'sunflowerSeeds2', 'pumpkinSeeds', 'watermelonSeeds', 'sesame', 'blackSesame', 'whiteSesame'
+        ]
     },
     {
         id: 'dairy',
-        name: '奶制品',
         icon: '🥛',
         color: 'bg-blue-50 border-blue-200 text-blue-700',
-        items: ['牛奶', '酸奶', '奶酪', '芝士', '奶油', '黄油', '炼乳', '奶粉', '马苏里拉奶酪', '车达奶酪', '帕玛森奶酪', '奶昔']
+        itemKeys: [
+            'milk', 'yogurt', 'cheese', 'cheese2', 'cream', 'butter', 'condensedMilk', 'milkPowder',
+            'mozzarella', 'cheddar', 'parmesan', 'milkshake'
+        ]
     }
-]
+] as const
+
+// 获取国际化后的食材分类配置
+export function getIngredientCategories(t: (key: string) => string): IngredientCategory[] {
+    return ingredientCategoriesConfig.map(category => ({
+        id: category.id,
+        name: t(`ingredients.categories.${category.id}`),
+        icon: category.icon,
+        color: category.color,
+        items: category.itemKeys.map((_, index) => {
+            // 使用索引来获取翻译,因为翻译文件是按数组顺序排列的
+            return t(`ingredients.items.${category.id}.${index}`)
+        })
+    }))
+}
+
+// 为了向后兼容,导出一个默认的配置(使用中文)
+// 注意:这个导出在组件中应该使用 getIngredientCategories 来替代
+// export const ingredientCategories: IngredientCategory[] = getIngredientCategories((key: string) => {
+//     // 默认返回中文翻译
+//     const translations: Record<string, string> = {
+//         'ingredients.categories.meat': '荤菜',
+//         'ingredients.categories.seafood': '海鲜',
+//         'ingredients.categories.vegetables': '蔬菜',
+//         'ingredients.categories.mushrooms': '菌菇',
+//         'ingredients.categories.beans': '豆类',
+//         'ingredients.categories.eggs': '蛋类',
+//         'ingredients.categories.fruits': '水果',
+//         'ingredients.categories.nuts': '坚果',
+//         'ingredients.categories.dairy': '奶制品',
+//     }
+    
+//     // 如果包含索引,说明是食材项
+//     const match = key.match(/ingredients\.items\.(\w+)\.(\d+)/)
+//     if (match) {
+//         const [, categoryId, index] = match
+//         const defaultItems: Record<string, string[]> = {
+//             meat: ['猪肉', '牛肉', '羊肉', '鸡肉', '鸭肉', '鹅肉', '兔肉', '驴肉', '猪排骨', '牛排', '羊排', '鸡翅', '鸡腿', '鸡胸肉', '鸡爪', '鸭腿', '五花肉', '瘦肉', '肉丝', '肉片', '肉丁', '肉馅', '里脊肉', '梅花肉', '腊肉', '香肠', '火腿', '培根', '腊肠', '咸肉', '风干肉', '牛肉干', '猪蹄', '猪肚', '猪肝', '猪心', '猪肺', '猪腰', '牛肚', '羊肚', '鸡心', '鸡肝', '鸡胗', '鸭血', '猪血'],
+//             seafood: ['鲈鱼', '鲫鱼', '草鱼', '鲤鱼', '带鱼', '黄鱼', '鳕鱼', '三文鱼', '金枪鱼', '鲳鱼', '石斑鱼', '桂鱼', '鲢鱼', '青鱼', '武昌鱼', '比目鱼', '鳗鱼', '刀鱼', '大虾', '基围虾', '龙虾', '白虾', '河虾', '明虾', '对虾', '皮皮虾', '螃蟹', '大闸蟹', '梭子蟹', '青蟹', '毛蟹', '扇贝', '蛤蜊', '生蚝', '牡蛎', '花甲', '蚬子', '海螺', '田螺', '鱿鱼', '章鱼', '墨鱼', '海参', '鲍鱼', '海带', '紫菜', '裙带菜'],
+//             vegetables: ['白菜', '大白菜', '小白菜', '娃娃菜', '菠菜', '韭菜', '韭黄', '芹菜', '西芹', '生菜', '油菜', '小油菜', '菜心', '芥菜', '空心菜', '苋菜', '茼蒿', '西红柿', '樱桃番茄', '黄瓜', '小黄瓜', '茄子', '长茄子', '圆茄子', '豆角', '四季豆', '扁豆', '豇豆', '荷兰豆', '豌豆', '毛豆', '青椒', '红椒', '黄椒', '彩椒', '尖椒', '朝天椒', '小米椒', '泡椒', '土豆', '红薯', '紫薯', '山药', '芋头', '莲藕', '荸荠', '慈姑', '萝卜', '白萝卜', '胡萝卜', '青萝卜', '心里美萝卜', '樱桃萝卜', '洋葱', '大葱', '小葱', '香葱', '蒜苗', '蒜黄', '大蒜', '生姜', '冬瓜', '南瓜', '丝瓜', '苦瓜', '黄瓜', '西葫芦', '佛手瓜', '蛇瓜', '豆芽', '绿豆芽', '黄豆芽', '豆苗', '萝卜苗', '香椿', '蕨菜', '马齿苋', '菜花', '西兰花', '包菜', '紫甘蓝', '芥蓝', '花椰菜'],
+//             mushrooms: ['香菇', '花菇', '平菇', '金针菇', '杏鲍菇', '茶树菇', '草菇', '口蘑', '蟹味菇', '白玉菇', '海鲜菇', '鸡腿菇', '滑子菇', '秀珍菇', '木耳', '黑木耳', '银耳', '雪耳', '毛木耳', '竹荪', '猴头菇', '松茸', '牛肝菌', '羊肚菌', '鸡油菌', '榛蘑', '元蘑'],
+//             beans: ['豆腐', '嫩豆腐', '老豆腐', '内酯豆腐', '豆腐干', '香干', '白干', '豆皮', '腐竹', '千张', '豆泡', '油豆腐', '冻豆腐', '臭豆腐', '毛豆', '豌豆', '蚕豆', '绿豆', '红豆', '黑豆', '黄豆', '芸豆', '豆芽', '豆苗', '豆角', '四季豆', '扁豆', '豇豆', '荷兰豆', '腐乳', '豆豉', '纳豆', '豆浆', '豆花'],
+//             eggs: ['鸡蛋', '土鸡蛋', '鸭蛋', '鹅蛋', '鹌鹑蛋', '鸽子蛋', '咸鸭蛋', '松花蛋', '皮蛋', '茶叶蛋', '卤蛋', '溏心蛋'],
+//             fruits: ['苹果', '梨', '桃子', '李子', '杏', '樱桃', '葡萄', '草莓', '蓝莓', '香蕉', '橙子', '柚子', '柠檬', '橘子', '桔子', '金桔', '柑橘', '西瓜', '哈密瓜', '甜瓜', '香瓜', '木瓜', '芒果', '菠萝', '猕猴桃', '火龙果', '百香果', '荔枝', '龙眼', '枣', '柿子', '石榴', '山楂'],
+//             nuts: ['花生', '核桃', '杏仁', '腰果', '开心果', '松子', '榛子', '板栗', '瓜子', '葵花籽', '南瓜子', '西瓜子', '芝麻', '黑芝麻', '白芝麻'],
+//             dairy: ['牛奶', '酸奶', '奶酪', '芝士', '奶油', '黄油', '炼乳', '奶粉', '马苏里拉奶酪', '车达奶酪', '帕玛森奶酪', '奶昔']
+//         }
+//         return defaultItems[categoryId]?.[parseInt(index)] || ''
+//     }
+    
+//     return translations[key] || key
+// })

+ 72 - 36
src/config/sauces.ts

@@ -4,48 +4,48 @@ import type { SauceCategoryConfig, SauceCategory } from '@/types'
 export const sauceCategories: SauceCategoryConfig[] = [
     {
         id: 'spicy',
-        name: '辣味酱料',
-        description: '各种辣椒酱、辣油、麻辣酱料',
+        name: 'sauces.categories.spicy.name',
+        description: 'sauces.categories.spicy.description',
         icon: '🌶️',
         color: 'bg-red-500',
         examples: ['辣椒酱', '蒜蓉辣椒酱', '麻辣油', '韩式辣椒酱', '泰式甜辣酱']
     },
     {
         id: 'garlic',
-        name: '蒜香酱料',
-        description: '以大蒜为主的香味酱料',
+        name: 'sauces.categories.garlic.name',
+        description: 'sauces.categories.garlic.description',
         icon: '🧄',
         color: 'bg-green-500',
         examples: ['蒜蓉酱', '蒜泥白肉酱', '蒜香辣椒油', '黑蒜酱', '蒜蓉豆豉酱']
     },
     {
         id: 'sweet',
-        name: '甜味酱料',
-        description: '甜口酱料和果酱类',
+        name: 'sauces.categories.sweet.name',
+        description: 'sauces.categories.sweet.description',
         icon: '🍯',
         color: 'bg-yellow-500',
         examples: ['甜面酱', '海鲜酱', '糖醋酱', '蜂蜜芥末酱', '果酱']
     },
     {
         id: 'complex',
-        name: '复合调味酱',
-        description: '多种调料混合的复合酱料',
+        name: 'sauces.categories.complex.name',
+        description: 'sauces.categories.complex.description',
         icon: '🥄',
         color: 'bg-purple-500',
         examples: ['XO酱', '沙茶酱', '黑椒酱', '蘑菇酱', '咖喱酱']
     },
     {
         id: 'regional',
-        name: '地方特色酱',
-        description: '各地传统特色酱料',
+        name: 'sauces.categories.regional.name',
+        description: 'sauces.categories.regional.description',
         icon: '🏮',
         color: 'bg-orange-500',
         examples: ['郫县豆瓣酱', '老干妈', '柱候酱', '海天黄豆酱', '东北大酱']
     },
     {
         id: 'fusion',
-        name: '创新融合酱',
-        description: '现代创新和中西融合酱料',
+        name: 'sauces.categories.fusion.name',
+        description: 'sauces.categories.fusion.description',
         icon: '✨',
         color: 'bg-pink-500',
         examples: ['芝士酱', '牛油果酱', '芝麻酱', '花生酱', '创意调味酱']
@@ -54,64 +54,64 @@ export const sauceCategories: SauceCategoryConfig[] = [
 
 // 使用场景配置
 export const useCaseOptions = [
-    { id: 'noodles', name: '拌面', icon: '🍜' },
-    { id: 'dipping', name: '蘸菜', icon: '🥢' },
-    { id: 'cooking', name: '炒菜', icon: '🍳' },
-    { id: 'bbq', name: '烧烤', icon: '🔥' },
-    { id: 'hotpot', name: '火锅', icon: '🍲' }
+    { id: 'noodles', name: 'sauces.useCases.noodles', icon: '🍜' },
+    { id: 'dipping', name: 'sauces.useCases.dipping', icon: '🥢' },
+    { id: 'cooking', name: 'sauces.useCases.cooking', icon: '🍳' },
+    { id: 'bbq', name: 'sauces.useCases.bbq', icon: '🔥' },
+    { id: 'hotpot', name: 'sauces.useCases.hotpot', icon: '🍲' }
 ]
 
 // 预设酱料模板
 export const sauceTemplates = [
     {
-        name: '蒜蓉辣椒酱',
+        name: 'sauces.templates.garlicChiliSauce.name',
         category: 'spicy' as SauceCategory,
-        tags: ['经典', '下饭', '万能'],
+        tags: ['sauces.templates.garlicChiliSauce.tags.0', 'sauces.templates.garlicChiliSauce.tags.1', 'sauces.templates.garlicChiliSauce.tags.2'],
         spiceLevel: 4,
         sweetLevel: 1,
         saltLevel: 3,
         sourLevel: 1
     },
     {
-        name: '甜面酱',
+        name: 'sauces.templates.sweetSauce.name',
         category: 'sweet' as SauceCategory,
-        tags: ['传统', '烤鸭', '甜口'],
+        tags: ['sauces.templates.sweetSauce.tags.0', 'sauces.templates.sweetSauce.tags.1', 'sauces.templates.sweetSauce.tags.2'],
         spiceLevel: 0,
         sweetLevel: 4,
         saltLevel: 2,
         sourLevel: 0
     },
     {
-        name: 'XO酱',
+        name: 'sauces.templates.xoSauce.name',
         category: 'complex' as SauceCategory,
-        tags: ['高级', '海鲜', '港式'],
+        tags: ['sauces.templates.xoSauce.tags.0', 'sauces.templates.xoSauce.tags.1', 'sauces.templates.xoSauce.tags.2'],
         spiceLevel: 2,
         sweetLevel: 2,
         saltLevel: 4,
         sourLevel: 1
     },
     {
-        name: '郫县豆瓣酱',
+        name: 'sauces.templates.doubanjiang.name',
         category: 'regional' as SauceCategory,
-        tags: ['川菜', '传统', '发酵'],
+        tags: ['sauces.templates.doubanjiang.tags.0', 'sauces.templates.doubanjiang.tags.1', 'sauces.templates.doubanjiang.tags.2'],
         spiceLevel: 3,
         sweetLevel: 1,
         saltLevel: 4,
         sourLevel: 2
     },
     {
-        name: '蒜泥白肉酱',
+        name: 'sauces.templates.whiteMeatSauce.name',
         category: 'garlic' as SauceCategory,
-        tags: ['川菜', '蒜香', '清爽'],
+        tags: ['sauces.templates.whiteMeatSauce.tags.0', 'sauces.templates.whiteMeatSauce.tags.1', 'sauces.templates.whiteMeatSauce.tags.2'],
         spiceLevel: 1,
         sweetLevel: 1,
         saltLevel: 3,
         sourLevel: 3
     },
     {
-        name: '芝麻花生酱',
+        name: 'sauces.templates.sesamePeanutSauce.name',
         category: 'fusion' as SauceCategory,
-        tags: ['创新', '香浓', '营养'],
+        tags: ['sauces.templates.sesamePeanutSauce.tags.0', 'sauces.templates.sesamePeanutSauce.tags.1', 'sauces.templates.sesamePeanutSauce.tags.2'],
         spiceLevel: 0,
         sweetLevel: 2,
         saltLevel: 2,
@@ -121,17 +121,53 @@ export const sauceTemplates = [
 
 // 口味等级描述
 export const tasteDescriptions = {
-    spice: ['不辣', '微辣', '中辣', '很辣', '超辣'],
-    sweet: ['不甜', '微甜', '中甜', '很甜', '超甜'],
-    salt: ['不咸', '微咸', '中咸', '很咸', '超咸'],
-    sour: ['不酸', '微酸', '中酸', '很酸', '超酸']
+    spice: [
+        'sauces.tasteLevels.spice.0',
+        'sauces.tasteLevels.spice.1',
+        'sauces.tasteLevels.spice.2',
+        'sauces.tasteLevels.spice.3',
+        'sauces.tasteLevels.spice.4'
+    ],
+    sweet: [
+        'sauces.tasteLevels.sweet.0',
+        'sauces.tasteLevels.sweet.1',
+        'sauces.tasteLevels.sweet.2',
+        'sauces.tasteLevels.sweet.3',
+        'sauces.tasteLevels.sweet.4'
+    ],
+    salt: [
+        'sauces.tasteLevels.salt.0',
+        'sauces.tasteLevels.salt.1',
+        'sauces.tasteLevels.salt.2',
+        'sauces.tasteLevels.salt.3',
+        'sauces.tasteLevels.salt.4'
+    ],
+    sour: [
+        'sauces.tasteLevels.sour.0',
+        'sauces.tasteLevels.sour.1',
+        'sauces.tasteLevels.sour.2',
+        'sauces.tasteLevels.sour.3',
+        'sauces.tasteLevels.sour.4'
+    ]
 }
 
 // 难度等级描述
 export const difficultyDescriptions = {
-    easy: { name: '简单', color: 'text-green-600', description: '新手友好,步骤简单' },
-    medium: { name: '中等', color: 'text-yellow-600', description: '需要一定经验' },
-    hard: { name: '困难', color: 'text-red-600', description: '需要丰富经验和技巧' }
+    easy: { 
+        name: 'sauces.difficulty.easy.name', 
+        color: 'text-green-600', 
+        description: 'sauces.difficulty.easy.description' 
+    },
+    medium: { 
+        name: 'sauces.difficulty.medium.name', 
+        color: 'text-yellow-600', 
+        description: 'sauces.difficulty.medium.description' 
+    },
+    hard: { 
+        name: 'sauces.difficulty.hard.name', 
+        color: 'text-red-600', 
+        description: 'sauces.difficulty.hard.description' 
+    }
 }
 
 // 获取分类配置

+ 36 - 0
src/hooks/useLogin.ts

@@ -0,0 +1,36 @@
+import { appleLogin, googleLogin } from "@/api";
+import { setToken } from "@/utils/auth";
+
+// 触发原生登录的函数
+export function triggerNativeLogin() {
+  // 在 React Native 应用中使用 react-native-webview 的 WebView 组件时,该库会自动在 WebView 加载的网页中注入 window.ReactNativeWebView 对象
+  // @ts-ignore
+  if (window.ReactNativeWebView) {
+    // 向原生应用发送登录请求
+    // @ts-ignore
+    console.log('h5: 发送登录请求给原生')
+    window.ReactNativeWebView.postMessage('NATIVE_LOGIN_REQUEST');
+  } else {
+    console.warn('不在 React Native 环境中');
+  }
+}
+
+
+
+// 示例:在按钮点击时触发登录
+// document.getElementById('loginButton').addEventListener('click', triggerNativeLogin);
+export function appleLoginHandler(data) {
+  const params = {
+    code: data.authorizationCode,
+    id_token: data.identityToken,
+  };
+  // console.log('appleLoginHandler params', params);
+  return appleLogin(params)
+}
+
+export function androidLoginHandler(data) {
+  const params = {
+    id_token: data.identityToken,
+  };
+  return googleLogin(params)
+}

+ 949 - 0
src/i18n/Translation.md

@@ -0,0 +1,949 @@
+一饭封神(英文版):“Legendary with just one meal”English Version
+灶间有AI,顿顿米其林!”With an AI chef in the kitchen, every meal is made with love and Michelin-level quality!”
+
+括号内
+主页:Home
+美食盲盒:Culinary Mystery Box
+满汉全席:Feast Mode
+玄学厨房:Kitchen Magic
+酱料大师:The Sauce Master
+更多:Explore More
+
+(一)我的收藏: My Favorites
+(二)封神图鉴:The Pantheon Guide
+(三)关于我们:About Us
+
+一、首页:Home
+1.输入食材:Enter Ingredients​
+(一)添加食材:Add Ingredients​
+(二)输入你现有的食材,按回车添加:Type in your available ingredients and press Enter to add them.​(输入框外)
+(三)输入食材名称,按回车添加.Type an ingredient name and press Enter to add...(输入框内)
+(四)支持蔬菜、肉类、调料等 (最多10种):Supports vegetables,meats,seasonings,etc.
+(五)快速选择食材:Quick Pick​(框内选项)
+
+(1)荤菜 | Meat:
+猪肉 Pork
+牛肉 Beef
+羊肉 Lamb
+鸡肉 Chicken
+鸭肉 Duck
+鹅肉 Goose
+兔肉 Rabbit
+驴肉 Donkey Meat
+猪排骨 Pork Ribs
+牛排 Steak / Beef Steak
+羊排 Lamb Chops
+鸡翅 Chicken Wings
+鸡腿 Chicken Thighs / Drumsticks
+鸡胸肉 Chicken Breast
+鸡爪 Chicken Feet
+鸭腿 Duck Leg
+五花肉 Pork Belly
+瘦肉 Lean Meat
+肉丝 Shredded Meat
+肉片 Sliced Meat
+肉丁 Diced Meat
+肉馅 Minced Meat / Ground Meat
+里脊肉 Tenderloin 
+梅花肉 Pork Collar / Boston Butt
+腊肉 Cured Pork / Chinese Bacon
+香肠 Sausage
+火腿 Ham
+培根 Bacon
+腊肠 Chinese Sausage
+咸肉 Salted Meat
+风干肉 Air-Dried Meat / Jerky
+牛肉干 Beef Jerky
+猪蹄 Pig Trotters
+猪肚 Pork Tripe
+猪肝 Pork Liver
+猪心 Pork Heart
+猪肺 Pork Lungs
+猪腰 Pork Kidney
+牛肚 Beef Tripe
+羊肚 Lamb Tripe
+鸡心 Chicken Hearts
+鸡肝 Chicken Livers
+鸡胗 Chicken Gizzards
+鸭血 Duck Blood Curd
+猪血 Pork Blood Curd
+
+(2)海鲜 | Seafood:​
+鲈鱼 Perch
+鲫鱼 Crucian Carp
+草鱼 Grass Carp
+鲤鱼 Carp
+带鱼 Ribbon Fish / Hairtail
+黄鱼 Yellow Croaker
+鳕鱼 Cod
+三文鱼 Salmon
+金枪鱼 Tuna
+鲳鱼 Pomfret
+石斑鱼 Grouper
+桂鱼 Mandarin Fish
+鲢鱼 Silver Carp
+青鱼 Black Carp
+武昌鱼 Wuchang Bream
+比目鱼 Flounder / Halibut
+鳗鱼 Eel
+刀鱼 Swordfish
+大虾 Prawns / Large Shrimp
+基围虾 Kuruma Shrimp / Tiger Prawns
+龙虾 Lobster
+白虾 Whiteleg Shrimp
+河虾 Freshwater Shrimp
+明虾 Chinese White Shrimp
+对虾 King Prawns
+皮皮虾 Mantis Shrimp
+螃蟹 Crab
+大闸蟹 Hairy Crab
+梭子蟹 Swimming Crab
+青蟹 Green Crab / Mud Crab
+毛蟹 Hairy Crab
+扇贝 Scallops
+蛤蜊 Clams
+生蚝 Oysters
+牡蛎 Oysters
+花甲 Shortneck Clams
+蚬子 Corbicula Clams
+海螺 Sea Snail / Conch
+田螺 River Snails
+鱿鱼 Squid
+章鱼 Octopus
+墨鱼 Cuttlefish
+海参 Sea Cucumber
+鲍鱼 Abalone
+海带 Kelp
+紫菜 Nori / Laver
+裙带菜 Wakame
+
+(3)蔬菜 | Vegetables:​
+
+白菜 Napa Cabbage / Chinese Cabbage
+
+大白菜 Napa Cabbage
+
+小白菜 Bok Choy / Shanghai Greens
+
+娃娃菜 Baby Napa Cabbage
+
+菠菜 Spinach
+
+韭菜 Garlic Chives
+
+韭黄 Yellow Garlic Chives
+
+芹菜 Celery
+
+西芹 Celery (通常与芹菜同)
+
+生菜 Lettuce
+
+油菜 Rapeseed Greens / Choy Sum
+
+小油菜 Baby Bok Choy
+
+菜心 Flowering Cabbage / Choy Sum
+
+芥菜 Mustard Greens
+
+空心菜 Water Spinach
+
+苋菜 Amaranth Greens
+
+茼蒿 Garland Chrysanthemum / Crown Daisy
+
+西红柿 Tomato
+
+樱桃番茄 Cherry Tomato
+
+黄瓜 Cucumber
+
+小黄瓜 Gherkin / Small Cucumber
+
+茄子 Eggplant
+
+长茄子 Chinese Eggplant / Long Eggplant
+
+圆茄子 Globe Eggplant
+
+豆角 Yardlong Beans
+
+四季豆 Green Beans
+
+扁豆 Hyacinth Beans / Lablab Beans
+
+豇豆 Cowpeas / Black-Eyed Peas
+
+荷兰豆 Snow Peas / Mangetout
+
+豌豆 Peas
+
+毛豆 Edamame (Young Soybeans)
+
+青椒 Green Bell Pepper
+
+红椒 Red Bell Pepper
+
+黄椒 Yellow Bell Pepper
+
+彩椒 Bell Pepper / Capsicum
+
+尖椒 Chili Pepper
+
+朝天椒 Facing Heaven Chili
+
+小米椒 Bird's Eye Chili
+
+泡椒 Pickled Chili
+
+土豆 Potato
+
+红薯 Sweet Potato
+
+紫薯 Purple Sweet Potato
+
+山药 Chinese Yam / Nagaimo
+
+芋头 Taro
+
+莲藕 Lotus Root
+
+荸荠 Water Chestnut
+
+慈姑 Arrowhead
+
+萝卜 Daikon / Radish
+
+白萝卜 Daikon / White Radish
+
+胡萝卜 Carrot
+
+青萝卜 Green Radish
+
+心里美萝卜 Watermelon Radish
+
+樱桃萝卜 Cherry Radish / Radish
+
+洋葱 Onion
+
+大葱 Scallion / Spring Onion 
+小葱 Scallion / Spring Onion
+
+香葱 Chives
+
+蒜苗 Garlic Sprouts / Green Garlic
+
+蒜黄 Yellow Garlic Sprouts
+
+大蒜 Garlic
+
+生姜 Ginger
+
+冬瓜 Winter Melon
+
+南瓜 Pumpkin
+
+丝瓜 Luffa / Sponge Gourd
+
+苦瓜 Bitter Melon
+
+黄瓜 Cucumber
+
+西葫芦 Zucchini / Courgette
+
+佛手瓜 Chayote
+
+蛇瓜 Snake Gourd
+
+豆芽 Bean Sprouts
+
+绿豆芽 Mung Bean Sprouts
+
+黄豆芽 Soybean Sprouts
+
+豆苗 Pea Shoots
+
+萝卜苗 Radish Sprouts
+
+香椿 Chinese Toon / Cedar Sprouts
+
+蕨菜 Fiddlehead Ferns
+
+马齿苋 Purslane
+
+菜花 Cauliflower
+
+西兰花 Broccoli
+
+包菜 Cabbage
+
+紫甘蓝 Red Cabbage / Purple Cabbage
+
+芥蓝 Chinese Broccoli / Gai Lan
+
+花椰菜 Cauliflower
+
+(4)菌菇 | Mushrooms:​
+
+香菇 Shiitake Mushroom
+花菇 Flower Shiitake 
+平菇 Oyster Mushroom
+金针菇 Enoki Mushroom
+杏鲍菇 King Oyster Mushroom
+
+茶树菇 Tea Tree Mushroom
+
+草菇 Straw Mushroom
+
+口蘑 Button Mushroom / White Mushroom
+
+蟹味菇 Crab Flavor Mushroom 
+白玉菇 White Beech Mushroom
+
+海鲜菇 Seafood Mushroom 
+鸡腿菇 King Oyster Mushroom 
+滑子菇 Nameko Mushroom
+
+秀珍菇 Oyster Mushroom 
+木耳 Wood Ear Fungus
+
+黑木耳 Black Fungus / Wood Ear
+
+银耳 Snow Fungus / Tremella
+
+雪耳 Snow Fungus
+
+毛木耳 Jelly Ear / Auricularia
+
+竹荪 Bamboo Fungus / Veiled Lady Mushroom
+
+猴头菇 Lion's Mane Mushroom / Monkey Head Mushroom
+
+松茸 Matsutake Mushroom
+
+牛肝菌 Porcini / Bolete Mushroom
+
+羊肚菌 Morel Mushroom
+
+鸡油菌 Chanterelle Mushroom
+
+榛蘑 Honey Mushroom
+
+元蘑 Yuan Mushroom
+
+
+(5)豆类 | Beans & Tofu:​
+
+豆腐 Tofu
+
+嫩豆腐 Soft Tofu / Silken Tofu
+
+老豆腐 Firm Tofu
+
+内酯豆腐 Silken Tofu 
+豆腐干 Dried Tofu / Tofu Gan
+
+香干 Seasoned Dried Tofu
+
+白干 Plain Dried Tofu
+
+豆皮 Tofu Skin / Yuba
+
+腐竹 Dried Tofu Skin Sticks
+
+千张 Tofu Sheets / Thousand-Layer Tofu
+
+豆泡 Fried Tofu Puff
+
+油豆腐 Fried Tofu Puff
+
+冻豆腐 Frozen Tofu
+
+臭豆腐 Stinky Tofu
+
+毛豆 Edamame
+
+豌豆 Peas
+
+蚕豆 Fava Beans / Broad Beans
+
+绿豆 Mung Beans
+
+红豆 Red Beans / Adzuki Beans
+
+黑豆 Black Beans
+
+黄豆 Soybeans
+
+芸豆 Kidney Beans
+
+豆芽 Bean Sprouts
+
+豆苗 Pea Shoots
+
+豆角 Yardlong Beans
+
+四季豆 Green Beans
+
+扁豆 Hyacinth Beans
+
+豇豆 Cowpeas
+
+荷兰豆 Snow Peas
+
+腐乳 Fermented Tofu
+
+豆豉 Fermented Black Beans 
+
+纳豆 Natto
+
+豆浆 Soy Milk
+
+豆花 Tofu Pudding 
+
+
+(6)蛋类 | Eggs​
+
+鸡蛋 Eggs 
+
+土鸡蛋 Free-Range Eggs
+
+鸭蛋 Duck Eggs
+
+鹅蛋 Goose Eggs
+
+鹌鹑蛋 Quail Eggs
+
+鸽子蛋 Pigeon Eggs
+
+咸鸭蛋 Salted Duck Eggs
+
+松花蛋 Century Egg  
+
+皮蛋 Preserved Egg
+
+茶叶蛋 Tea Eggs
+
+卤蛋 Marinated Eggs
+
+溏心蛋 Soft-Boiled Eggs / Runny Eggs
+
+
+(7)水果 | Fruits​
+
+苹果 Apple
+
+梨 Pear
+
+桃子 Peach
+
+李子 Plum
+
+杏 Apricot
+
+樱桃 Cherry
+
+葡萄 Grape
+
+草莓 Strawberry
+
+蓝莓 Blueberry
+
+香蕉 Banana
+
+橙子 Orange
+
+柚子 Pomelo
+
+柠檬 Lemon
+
+橘子 Tangerine
+
+桔子 Mandarin Orange 
+
+金桔 Kumquat
+
+柑橘 Citrus
+
+西瓜 Watermelon
+
+哈密瓜 Hami Melon
+
+甜瓜 Muskmelon
+
+香瓜 Muskmelon 
+
+木瓜 Papaya
+
+芒果 Mango
+
+菠萝 Pineapple
+
+猕猴桃 Kiwifruit
+
+火龙果 Dragon Fruit / Pitaya
+
+百香果 Passion Fruit
+
+荔枝 Lychee
+
+龙眼 Longan
+
+枣 Jujube / Chinese Date
+
+柿子 Persimmon
+
+石榴 Pomegranate
+
+山楂 Hawthorn
+
+
+(8)坚果 | Nuts & Seeds​
+
+花生 Peanut
+
+核桃 Walnut
+
+杏仁 Almond
+
+腰果 Cashew
+
+开心果 Pistachio
+
+松子 Pine Nut
+
+榛子 Hazelnut
+
+板栗 Chestnut
+
+瓜子 Sunflower Seeds 
+
+葵花籽 Sunflower Seeds
+
+南瓜子 Pumpkin Seeds
+
+西瓜子 Watermelon Seeds
+
+芝麻 Sesame Seeds
+
+黑芝麻 Black Sesame Seeds
+
+白芝麻 White Sesame Seeds
+
+
+(9)奶制品 | Dairy​
+
+牛奶 Milk
+
+酸奶 Yogurt
+
+奶酪 Cheese
+
+芝士 Cheese 
+
+奶油 Cream
+
+黄油 Butter
+
+炼乳 Condensed Milk
+
+奶粉 Milk Powder
+
+马苏里拉奶酪 Mozzarella Cheese
+
+车达奶酪 Cheddar Cheese
+
+帕玛森奶酪 Parmesan Cheese
+
+奶昔 Milkshake
+点击食材快速添加到列表:Tap/Click on an ingredient to quickly add it to your list.​
+已选择 0/10:  Selected 0/10
+
+
+2.选择菜系: Select Cuisine​
+
+CN中华八大菜系 | CN Chinese Cuisines​
+
+苏菜 | Jiangsu
+
+鲁菜 | Shandong
+
+川菜 | Sichuan
+
+粤菜 | Cantonese
+
+浙菜 | Zhejiang
+
+湘菜 | Hunan
+
+闽菜 | Fujian
+
+徽菜 | Anhui
+
+国际菜系 | International Cuisines​
+
+日式 | Japanese
+
+韩式 | Korean
+
+意式 | Italian
+
+法式 | French
+
+印度 | Indian
+
+泰式 | Thai
+
+墨西哥 | Mexican
+
+或自定义要求 | Or Custom Request
+
+
+快速预设 Quick Presets
+自由描述:Free Description:​
+例如:做一道清淡的汤,适合老人食用,不要太咸.../ Example: Make a light soup, suitable for the elderly, not too salty...
+ 随机灵感 Random Inspiration
+ 提示:越具体越好!Tip: The more specific, the better!
+
+
+3.交给大师: To the Master Chef
+
+准备开始烹饪 | Ready to Start Cooking​
+
+大师已准备就绪,点击按钮开始创作美味佳肴 | The Master Chef is ready! Click the button below to begin creating your delicious dish.​​
+
+​当前配置 | Current Selections​
+
+​食材 (0):未添加食材 | Ingredients (0): None added​
+
+​菜系大师 (0):未选择大师 | Culinary Masters (0): None selected​
+
+​交给大师 | To the Master Chef!​
+
+​大师将为您精心设计菜谱流程 | The Master Chef will meticulously design the recipe process for you.​​
+
+4.菜谱结果:Recipe Results
+
+等待魔法发生...:Waiting for the magic to happen...​
+添加食材并选择菜系开始创作:Add ingredients and select a cuisine to start creating.​
+
+
+二,美食盲盒:Culinary Mystery Box
+
+今日吃啥:What to Eat Today?​
+盲盒美食:'绝了!' or '寄了!':Surprise Dish: 'Nailed it!' or 'Failed it!'
+​                                                                                                                                   
+准备好了吗?Ready to Explore?​
+开始随机选择:Start Random Selection
+偏好设置:Your Preferences
+1.荤菜多 More Meat
+2.素菜多 More Vegetables
+3. 纯素 Vegan Only
+4. 纯荤 Meat Only
+三,满汉全席:​Feast Mode
+
+
+
+
+一桌好菜师:​A-table-of-masterpieces Director
+让每顿饭,都有剧本!Every meal, scripted to perfection!​
+1.菜品配置 | Dish Configuration
+选择生成模式 | Select Generation Mode
+(1)固定数量模式 | Fixed Quantity Mode,指定确切菜品数量 | Specify the exact number of dishes.
+① 菜品数量 | Number of Dishes
+② 2道 | 2
+③ 4道 | 4
+④ 6道 | 6
+⑤ 8道 | 8
+⑥ 自定义 | Custom
+⑦ 指定菜品(可选)| Specific Dish Requests (Optional)​
+⑧ 输入菜品名称,按回车添加…… | Enter a dish name and press Enter to add...​​
+⑨ ​添加 | Add​
+⑩ ​例如:红烧肉、清蒸鱼 | e.g., Braised Pork Belly, Steamed Fish
+(2)智能搭配模式 | Smart Pairing Mode,AI智能决定数量和搭配 | AI determines the quantity and pairing intelligently.
+① 输入想要的菜品 | Enter your desired dish(es).
+② 智能搭配模式需要您先输入至少一道菜品 | Smart Pairing Mode requires at least one dish as a starting point.
+③ 输入菜品名称,按回车添加…… | Enter dish name, press Enter to add...​​
+④ ​添加 | Add​
+⑤ ​例如:包菜、娃娃菜、土豆 | e.g., cabbage, baby napa cabbage, potato
+2. 偏好设置(可选) | Preferences (Optional),
+可选配置:以下设置为可选项,不设置也能生成精彩菜单。 | Optional Configuration: The settings below are optional. You can still generate a great menu without configuring them.
+(1)口味和风格设置 | Taste & Style Settings
+口味偏好 | Flavor Preferences​
+① 麻辣 | Spicy & Numbing 
+② 甜味 | Sweet
+③ 酸味 | Sour / Tangy
+④ 咸鲜 | Savory & Umami
+⑤ 清淡 | Light
+⑥ 浓郁 | Rich / Intense
+菜系风格 | Cuisine Style​
+⑦ 混合菜系 | Mixed Cuisines
+⑧ 中式为主 | Chinese Focused
+⑨ 西式为主 | Western Focused
+⑩ 日式为主 | Japanese Focused
+用餐场景 | Dining Occasion​
+⑪ 家庭聚餐 | Family Gathering
+⑫ 朋友聚会 | Friends Get-Together
+⑬ 浪漫晚餐 | Romantic Dinner
+⑭ 商务宴请 | Business Banquet
+⑮ 节日庆祝 | Holiday Celebration
+⑯ 日常用餐 | Everyday Meal
+(2)营养和特殊要求 | Nutrition & Special Requests
+营养搭配 | Nutritional Focus​
+① 营养均衡 | Balanced Nutrition
+② 高蛋白 | High-Protein
+③ 素食为主 | Vegetarian Focused
+④ 低脂健康 | Low-Fat & Healthy
+⑤ 滋补养生 | Nourishing & Tonic
+特殊要求 | Special Instructions​
+⑥ 例如:不要太油腻,适合老人小孩,有一道汤…… | e.g., Not too greasy, suitable for the elderly and children, including one soup...
+当前配置 | Current Configuration​
+⑦ 生成模式:智能搭配 | Generation Mode:  Smart Pairing
+⑧ 口味:浓郁、酸味 | Flavors:  Rich, Sour
+3.生成一桌菜 | Generate a Full Meal
+(1)准备生成一桌菜 | Ready to Generate a Full Meal​
+(2)AI大师已准备就绪,点击按钮开始设计您的专属菜单 | The AI Master is ready! Click the button to start designing your personalized menu.​​
+(3)交给大师 | To the Master!​
+(4)请先在步骤1中输入至少一道菜品 | Please enter at least one dish in Step 1 first.​​
+四,玄学厨房:Kitchen Magic,
+星辰指引美食,占卜预见美味!Stars align to reveal culinary fates; divination whispers of flavors yet to come.​
+
+
+
+
+
+1.选择占卜类型:Select Divination Type
+(1)今日幸运菜:Lucky Dish of the Day,根据星座生肖推荐幸运菜品:Recommends auspicious dishes based on your zodiac sign and Chinese zodiac animal.
+(2)配置占卜参数:Divination Settings
+① 选择你的星座 | Select Your Zodiac Sign
+白羊座 Aries
+金牛座 Taurus
+双子座 Gemini
+巨蟹座 Cancer
+狮子座 Leo
+处女座 Virgo
+天秤座 Libra
+天蝎座 Scorpio
+射手座 Sagittarius
+摩羯座 Capricorn
+水瓶座 Aquarius
+双鱼座 Pisces
+
+② 选择你的生肖 | Select Your Chinese Zodiac Animal​
+鼠 Rat
+牛 Ox
+虎 Tiger
+兔 Rabbit
+龙 Dragon
+蛇 Snake
+马 Horse
+羊 Goat
+猴 Monkey
+鸡 Rooster
+狗 Dog
+猪 Pig
+③ 开始占卜 | Start Divination
+(1)心情料理师:Mood Chef,根据心情推荐治愈菜品:Suggests comforting dishes tailored to your current mood.
+(2)配置占卜参数:Divination Settings,选择你的心情(可多选) | Select Your Mood (Multiple Selection)​
+① 开心 | Happy
+② 难过 | Sad
+③ 焦虑 | Anxious
+④ 疲惫 | Tired / Exhausted
+⑤ 兴奋 | Excited
+⑥ 平静 | Calm / Peaceful
+⑦ 愤怒 | Angry
+⑧ 思念 | Nostalgic / Missing Someone
+⑨ ​情绪强度:一般 | Emotional Intensity: Medium​ (可使用滑块,标注 Low - Medium - High)
+⑩ 开始占卜 | Start the Reading
+(1)幸运数字菜/通过数字占卜推荐菜品:Lucky Number Dish/Selects a dish through numerology for a unique culinary experience.
+(2)配置占卜参数:Divination Settings
+① 选择你的幸运数字 | Choose Your Lucky Number
+② ​随机生成 | Generate Randomly​
+③ ​开始占卜 | Start the Divination​
+
+五,酱料大师:The Sauce Master
+酱料设计大师:Sauce Alchemist
+专业酱料制作,调味灵魂升华!Professional crafting that elevates the soul of flavor.​
+
+
+1.智能推荐:Smart Recommendations
+(1)口味偏好:Flavor Profile
+辣度 | Spiciness
+甜度 | Sweetness
+咸度 | Saltiness
+酸度 | Sourness
+
+(2)使用场景:Usage Scenario
+拌面 | For Noodles
+蘸菜 | For Dipping
+炒菜 | For Stir-frying
+烧烤 | For BBQ / Grilling
+火锅 | For Hot Pot
+
+(3)现有食材:Available Ingredients
+入食材名称,回车添加…… :Enter ingredient name, press Enter to add...​​
+获取智能推荐:Get Smart Recommendations
+
+2.酱料搜索:Sauce Search
+(1)输入酱料名称:Enter sauce name
+(2)搜索:Search
+3.制作教程:  Making Tutorials
+(1)开始您的酱料之旅:Start Your Sauce Making Journey
+(2)配置口味偏好,获取个性化推荐:Set your flavor preferences to get personalized recommendations.​
+(3)直接搜索想要制作的酱料:Directly search for a sauce to make
+
+...更多:  Explore More
+六, 我的收藏:My Favorites​
+我的收藏:My Favorites
+珍藏美味,随时回味!Treasured flavors, to be savored again and again.
+1.我的收藏:My Favorites
+2.收藏菜谱 | Saved Recipes​
+3.共收藏了 0 道菜谱 | Total: 0 Recipes​
+4.还没有收藏任何菜谱 | You haven't saved any recipes yet.
+5.去生成一些美味的菜谱,然后收藏起来吧! | Go generate some delicious recipes and save them here!​​
+6.开始生成菜谱 | Start Generating Recipes
+七, 封神图鉴:
+封神图鉴:The Pantheon Guide
+每一帧都是厨艺的封神时刻!Every frame, a legendary moment.​
+
+1.AI厨艺的视觉宝典 | The Visual Bible of AI Cuisine​
+2.共生成了 0 张菜品图片 | 0 AI Dishes Generated​
+3.图库还是空的 | Your gallery is empty.​​
+4.去生成一些菜品效果图,让图库丰富起来吧! | Go generate some stunning dish visuals to fill your gallery!​​
+5.开始生成菜谱 | Start Generating
+
+八,关于我们:About Us
+关于一饭封神:bout One Meal, Legendary Status
+算法烹万物,一键即封神!Algorithm cooks all, one click to legend.
+
+
+
+
+
+
+
+(一)API服务商:API Service Provider
+本项目AI服务提供商: AI Service Provider for This Project
+为您推荐优质的AI API服务: Recommending premium AI API services for you
+302.AI 官方合作伙伴: 302.AI Official Partner
+302.AI是一个按用量付费的企业级AI资源平台,提供市场上最新、最全面的AI模型和API,以及多种开箱即用的在线AI应用: 302.AI is a pay-as-you-go enterprise AI resource platform, offering the market's newest and most comprehensive AI models and APIs, along with various ready-to-use online AI applications.
+Key Features
+多模型支持:Multi-model Support
+价格优惠:Cost-Effective Pricing
+响应快速:Rapid Response​
+服务稳定:Stable Service
+访问302.AI :[Visit 302.AI]​​
+正在为本项目提供AI服务:Currently providing AI services for this project
+
+(二)关于作者:About the Author
+liuziting​
+AI降临派:AI Adherent
+Vibe Coding通过Kiro编辑器,实现了从需求分析、架构设计到代码实现的全流程开发。致力于探索AI与传统开发的完美结合,让编程变得更加高效和有趣。:Vibe Coding leverages the Kiro Editor to achieve full-cycle development—from requirements analysis and architectural design to code implementation. Dedicated to exploring the perfect integration of AI and traditional development, making programming more efficient and enjoyable.
+开源地址:Open Source Address
+开发工具:Development Tool, 
+Kiro编辑器- AI驱动的全流程开发:Kiro Editor - AI-Powered Full-Cycle Development
+支持开发者:Support the Developer
+AI需要成本,您的支持让项目走得更远:AI incurs costs. Your support helps the project go further.
+每一份支持都是对开源精神的鼓励:Every contribution encourages the spirit of open source:
+维持AI服务运行:Maintain AI service operations
+持续功能更新:Continue feature updates
+开源项目维护:Maintain the open-source project
+扫码支持开发者:Scan the QR Code to Support the Developer 
+(三)项目介绍:About This Project
+一饭封神 - AI厨艺大师指导平台:One Meal, Legendary Status - AI Culinary Master Guidance Platform
+一饭封神是一个创新的AI厨艺指导平台,汇聚了中华八大菜系和国际料理的精华。通过先进的人工智能技术,我们为每一位用户提供个性化的烹饪指导,让每一餐都能达到"封神"级别的美味体验。:One Meal, Legendary Status is an innovative AI-powered culinary guidance platform that brings together the essence of China's Eight Great Cuisines and international dishes. Leveraging advanced artificial intelligence, we provide personalized cooking guidance for every user, ensuring each meal can achieve a legendary level of deliciousness.
+智能食材搭配推荐:Intelligent Ingredient Pairing Recommendations
+多菜系大师级指导:Master-Level Guidance Across Multiple Cuisines
+个性化烹饪方案:Personalized Cooking Plans
+详细步骤指导:Detailed Step-by-Step Instructions
+平台特色:Platform Features
+AI智能推荐:AI-Powered Recommendations
+基于食材智能匹配最适合的菜谱和烹饪方法:Intelligently matches your available ingredients to the most suitable recipes and cooking methods.
+大师级指导:Master-Level Guidance
+汇聚各菜系大师的烹饪精髓和独门秘籍:Incorporates the culinary essence and secret techniques of masters of various cuisines.
+个性化定制:Personalized Customization
+根据个人口味偏好和饮食需求定制专属菜谱:Tailors unique recipes based on your personal taste preferences and dietary needs.
+(四)大师介绍:Meet the Master
+中华八大菜系:The Eight Great Traditions of Chinese Cuisine
+中华料理博大精深,八大菜系各具特色,代表了中国烹饪艺术的最高水准。:Chinese cuisine is vast and profound. The eight major culinary traditions, each with its distinct characteristics, represent the pinnacle of Chinese culinary art.
+1.川菜大师:Sichuan Master 
+(1)特色:麻辣鲜香,口味浓重:Features: Numbing, spicy, fresh, and fragrant with bold flavors. 
+(2)代表菜:麻婆豆腐、宫保鸡丁、水煮鱼​Signature Dishes: Mapo Tofu, Kung Pao Chicken, Boiled Fish in Chili Oil
+(3)口味:麻辣、鲜香、回甜:Flavor Profile: Numbing spice, savory aroma, with a hint of sweetness.
+2.粤菜大师:Cantonese (Guangdong) Master
+(1)特色:清淡鲜美,注重原味:Features: Light, fresh, and delicate, emphasizing original flavors.
+(2)代表菜:白切鸡、蒸蛋羹、广式点心:Signature Dishes: White Cut Chicken, Steamed Egg Custard, Dim Sum
+(3)口味:清淡、鲜美、嫩滑:Flavor Profile: Mild, umami, and tender.
+3.鲁菜大师:Shandong Master
+(1)特色:咸鲜为主,突出本味:Features: Salty and umami-forward, highlighting natural tastes.
+(2)代表菜:糖醋鲤鱼、九转大肠、葱爆海参:Signature Dishes: Sweet and Sour Carp, Nine-Turn Large Intestine, Scallion-Fried Sea Cucumber
+(3)口味:咸鲜、醇厚、清香:Flavor Profile: Salty umami, robust, and aromatic.
+4.苏菜大师:Jiangsu Master
+(1)特色:清淡微甜,精工细作:Features: Lightly sweet, meticulously prepared with fine craftsmanship.
+(2)代表菜:松鼠桂鱼、蟹粉狮子头、盐水鸭:Signature Dishes: Squirrel-Shaped Mandarin Fish, Crab Meat Lion's Head, Brined Duck
+(3)口味:清淡、微甜、鲜美:Flavor Profile: Delicate, subtly sweet, and fresh.
+5,浙菜大师:Zhejiang Master
+(1)特色:清香嫩滑,鲜美爽口:Features: Fresh aroma, tender texture, and refreshing taste.
+(2)代表菜:西湖醋鱼、龙井虾仁、东坡肉:Signature Dishes: West Lake Vinegar Fish, Longjing Tea Shrimp, Dongpo Pork
+(3)口味:清香、嫩滑、鲜美:Flavor Profile: Lightly fragrant, tender, and delicious.
+6.闽菜大师:Fujian Master​
+(1)特色:鲜香清淡,重汤轻油:Features: Fresh and aromatic broths, light on oil
+(2)代表菜:佛跳墙、荔枝肉、沙茶面:Signature Dishes: Buddha Jumps Over the Wall, Lychee Pork, Satay Noodles
+(3)口味:鲜香、清淡、醇厚:Flavor Profile: Savory aroma, light, and rich.
+7.湘菜大师:Hunan Master
+(1)特色:香辣鲜浓,口味厚重:Features: Fragrantly spicy, intensely savory, with hearty flavors.
+(2)代表菜:剁椒鱼头、麻辣子鸡、口味虾:Signature Dishes: Chopped Chili Fish Head, Spicy Chicken, Flavorful Shrimp
+(3)口味:香辣、鲜浓、酸辣:Flavor Profile: Aromatic heat, rich umami, and sour-spicy.
+8.徽菜大师:Anhui Master
+(1)特色:重油重色,朴实醇厚:Features: Rich in oil and color, rustic yet profound.
+(2)代表菜:红烧肉、毛豆腐、臭鳜鱼:Signature Dishes: Braised Pork Belly, Hairy Tofu, Fermented Mandarin Fish
+(3)口味:醇厚、鲜美、微甜:Flavor Profile: Deeply savory, delicious, and slightly sweet.
+国际菜系:International Cuisines
+融汇世界各地的经典料理,为您带来国际化的美食体验。:Featuring classic dishes from around the world, bringing you a global culinary experience.
+1.日式料理大师:Japanese Cuisine Master
+(1)特色:新鲜食材,精致摆盘:Features: Fresh ingredients, exquisite presentation
+(2)代表菜:寿司、拉面、天妇罗:Signature Dishes: Sushi, Ramen, Tempura
+(3)风格:清淡、精致、原味:Style: Light, refined, authentic flavors
+2.韩式料理大师:Korean Cuisine Master
+(1)特色:发酵调味,营养均衡:Features: Fermented seasonings, balanced nutrition
+(2)代表菜:泡菜、烤肉、石锅拌饭:Signature Dishes: Kimchi, Korean BBQ, Bibimbap
+(3)风格:酸辣、发酵、健康:Style: Spicy-sour, fermented, healthy
+3.泰式料理大师:Thai Cuisine Master
+(1)特色:酸甜辣香,层次丰富:Features: Sweet-sour-spicy-aromatic, complex layers
+(2)代表菜:冬阴功汤、绿咖喱、芒果糯米饭:Signature Dishes: Tom Yum Goong, Green Curry, Mango Sticky Rice
+(3)风格:酸甜、香辣、清爽:Style: Sweet-sour, aromatic spicy, refreshing
+4. 意式料理大师:Italian Cuisine Master
+(1)特色:简单食材,经典搭配:Features: Simple ingredients, classic combinations
+(2)代表菜:意大利面、披萨、提拉米苏:Signature Dishes: Pasta, Pizza, Tiramisu
+(3)风格:简约、经典、浓郁:Style: Simple, classic, rich
+5. 法式料理大师:French Cuisine Master
+(1)特色:精致工艺,浪漫情调:Features: Exquisite techniques, romantic ambiance
+(2)代表菜:法式焗蜗牛、鹅肝、马卡龙:Signature Dishes: Escargots, Foie Gras, Macarons
+(3)风格:精致、浪漫、优雅:Style: Refined, romantic, elegant
+6. 美式料理大师:American Cuisine Master
+(1)特色:分量丰富,口味浓郁:Features: Generous portions, bold flavors
+(2)代表菜:汉堡、炸鸡、牛排:Signature Dishes: Burgers, Fried Chicken, Steak
+(3)风格:丰富、浓郁、豪放: Style: Hearty, rich, unpretentious
+(五)其他项目:Explore More Projects
+AI 驱动的创新工具集:AI-Powered Innovation Toolkit
+以下项目均由Kiro开发完成:The following projects are all developed with Kiro
+
+1.小红书智能解析工具:Xiaohongshu Smart Parser
+2.集成AI投流建议的小红书无水印媒体解析工具。不仅能解析高清内容,更能为您的内容运营提供专业的投流策略建议,助力内容创作者提升传播效果:An ad-spending suggestion integrated, watermark-free media parser for Xiaohongshu. Not only extracts high-quality content but also provides professional advertising strategy recommendations to help creators enhance their reach.
+3.在线访问:Online Access
+
+1.图片转 AI 提示词工具:Image to AI Prompt Tool
+2.智能图像分析工具,可将上传的图片转换为高质量的描述性文本提示词。支持多种输出风格,为AI绘画、内容创作提供精准的文本描述。:An intelligent image analysis tool that converts uploaded images into high-quality descriptive text prompts. Supports multiple output styles, providing precise textual descriptions for AI painting and content creation.
+3.在线访问:Online Access
+
+1.复古风格 AI 智能对话助手:Retro-Style AI Chat Assistant
+2.独特的复古像素风格AI聊天应用,支持多种先进AI模型。提供沉浸式的复古游戏界面体验,让AI对话充满怀旧魅力。:A unique retro pixel-style AI chat application supporting multiple advanced AI models. Delivers an immersive retro gaming interface experience, making AI conversations full of nostalgic charm.
+3.在线访问:Online Access
+
+持续更新中:More Updates Coming
+更多AI工具正在开发,敬请期待!:More AI tools are under development. Stay tuned!
+喜欢这些项目?:Like These Projects?
+您的支持是最大的动力:Your Support is the Greatest Motivation
+支持开发者:Support the Developer
+

+ 19 - 0
src/i18n/index.ts

@@ -0,0 +1,19 @@
+import { createI18n } from 'vue-i18n'
+
+// 导入语言包
+import zh from './locales/zh.json'
+import en from './locales/en.json'
+
+const messages = {
+  zh,
+  en
+}
+
+const i18n = createI18n({
+  legacy: false, // 使用Composition API模式
+  locale: localStorage.getItem('locale') || 'zh', // 默认语言
+  fallbackLocale: 'en', // 回退语言
+  messages
+})
+
+export default i18n

+ 796 - 0
src/i18n/locales/en.json

@@ -0,0 +1,796 @@
+{
+  "home": {
+    "pageTitle": "Home",
+    "step1": {
+      "title": "1. Enter Ingredients",
+      "addIngredients": {
+        "title": "Add Ingredients",
+        "description": "Type in your available ingredients and press Enter to add them.",
+        "hint": "Supports vegetables, meats, seasonings, etc. (up to 10 items)",
+        "placeholder": "Type an ingredient name and press Enter to add...",
+        "quickPick": "Quick Pick",
+        "quickPickHint": "Tap/Click on an ingredient to quickly add it to your list.",
+        "selectedCount": "Selected {count}/10"
+      }
+    },
+    "step2": {
+      "title": "2. Select Cuisine",
+      "cuisines": {
+        "chinese": "CN Chinese Cuisines",
+        "international": "International Cuisines",
+        "masterSuffix": "Master",
+        "internationalSuffix1": "Japanese"
+      },
+      "customRequest": {
+        "title": "Or Custom Request",
+        "quickPresets": "Quick Presets",
+        "freeDescription": "Free Description:",
+        "placeholder": "Example: Make a light soup, suitable for the elderly, not too salty...",
+        "randomInspiration": "Random Inspiration",
+        "tip": "Tip: The more specific, the better!",
+        "set": "Set",
+        "clear": "Clear",
+        "clearButton": "Clear",
+        "scenePresets": "Scene Presets",
+        "tastePresets": "Taste Presets"
+      }
+    },
+    "step3": {
+      "title": "3. To the Master Chef",
+      "readyToCook": "Ready to Start Cooking",
+      "masterReady": "The Master Chef is ready! Click the button below to begin creating your delicious dish.",
+      "currentConfig": "Current Selections",
+      "ingredientsLabel": "Ingredients",
+      "cuisinesLabel": "Culinary Masters",
+      "customRequestLabel": "Custom Request",
+      "noIngredients": "None added",
+      "noCuisines": "None selected",
+      "usingCustomRequest": "Using custom request",
+      "generateButton": {
+        "default": "To the Master Chef!",
+        "withCustom": "Generate Based on Request",
+        "loading": "Generating..."
+      },
+      "hints": {
+        "withCustom": "Will generate recipes based on your custom requirements",
+        "withCuisines": "Will generate recipes for {count} cuisine(s)",
+        "default": "The Master Chef will meticulously design the recipe process for you."
+      }
+    },
+    "step4": {
+      "title": "4. Recipe Results",
+      "emptyState": {
+        "title": "Waiting for the magic to happen...",
+        "description": "Add ingredients and select a cuisine to start creating."
+      },
+      "loading": {
+        "masterCreating": "{name} is creating...",
+        "masterThinking": "{name} is thinking...",
+        "masterPreparing": "{name} is preparing...",
+        "masterCooking": "{name} is cooking...",
+        "masterPlating": "{name} is plating...",
+        "masterFinishing": "{name} is almost done...",
+        "progress": "Completed {current}/{total} recipe(s)...",
+        "carefullyCrafted": "Carefully crafting",
+        "estimatedTime": "Estimated time",
+        "usedIngredients": "Used Ingredients",
+        "stepsPreview": "Steps Preview",
+        "completed": "Completed"
+      },
+      "error": {
+        "title": "The Master Chef is puzzled",
+        "description": "{name} looked at your ingredients, scratched his head and said: \"I haven't learned this combination yet!\"",
+        "suggestionsTitle": "Master's Suggestions",
+        "suggestion1": "Try other cuisine masters, they may have different ideas",
+        "suggestion2": "Adjust the ingredient combination, there might be surprises",
+        "suggestion3": "Use custom requests to give the master some inspiration",
+        "retryButton": "Try Again",
+        "generalTitle": "Generation Failed",
+        "skillIssue": "Skill Issue",
+        "slacking": "Slacking off"
+      }
+    }
+  },
+  "ingredients": {
+    "categories": {
+      "meat": "Meat",
+      "seafood": "Seafood",
+      "vegetables": "Vegetables",
+      "mushrooms": "Mushrooms",
+      "beans": "Beans & Tofu",
+      "eggs": "Eggs",
+      "fruits": "Fruits",
+      "nuts": "Nuts & Seeds",
+      "dairy": "Dairy"
+    },
+    "items": {
+      "meat": [
+        "Pork",
+        "Beef",
+        "Lamb",
+        "Chicken",
+        "Duck",
+        "Goose",
+        "Rabbit",
+        "Donkey Meat",
+        "Pork Ribs",
+        "Beef Steak",
+        "Lamb Chops",
+        "Chicken Wings",
+        "Chicken Thighs / Drumsticks",
+        "Chicken Breast",
+        "Chicken Feet",
+        "Duck Leg",
+        "Pork Belly",
+        "Lean Meat",
+        "Shredded Meat",
+        "Sliced Meat",
+        "Diced Meat",
+        "Minced Meat",
+        "Tenderloin",
+        "Pork Shoulder",
+        "Cured Meat",
+        "Sausage",
+        "Ham",
+        "Bacon",
+        "Chinese Sausage",
+        "Salted Meat",
+        "Air-Dried Meat",
+        "Beef Jerky",
+        "Pork Trotters",
+        "Pork Tripe",
+        "Pork Liver",
+        "Pork Heart",
+        "Pork Lung",
+        "Pork Kidney",
+        "Beef Tripe",
+        "Lamb Tripe",
+        "Chicken Heart",
+        "Chicken Liver",
+        "Chicken Gizzard",
+        "Duck Blood",
+        "Pig Blood"
+      ],
+      "seafood": [
+        "Sea Bass",
+        "Crucian Carp",
+        "Grass Carp",
+        "Carp",
+        "Hairtail",
+        "Yellow Croaker",
+        "Cod",
+        "Salmon",
+        "Tuna",
+        "Pomfret",
+        "Grouper",
+        "Mandarin Fish",
+        "Silver Carp",
+        "Black Carp",
+        "Wuchang Fish",
+        "Flounder",
+        "Eel",
+        "Swordfish",
+        "Large Shrimp",
+        "Kuruma Shrimp",
+        "Lobster",
+        "White Shrimp",
+        "River Shrimp",
+        "Prawn",
+        "Paired Shrimp",
+        "Mantis Shrimp",
+        "Crab",
+        "Hairy Crab",
+        "Swimming Crab",
+        "Green Crab",
+        "Hairy Crab",
+        "Scallop",
+        "Clam",
+        "Oyster",
+        "Oyster",
+        "Clam",
+        "Clam",
+        "Conch",
+        "Snail",
+        "Squid",
+        "Octopus",
+        "Cuttlefish",
+        "Sea Cucumber",
+        "Abalone",
+        "Kelp",
+        "Nori",
+        "Wakame"
+      ],
+      "vegetables": [
+        "Cabbage",
+        "Chinese Cabbage",
+        "Baby Cabbage",
+        "Baby Bok Choy",
+        "Spinach",
+        "Chinese Chives",
+        "Yellow Chives",
+        "Celery",
+        "Western Celery",
+        "Lettuce",
+        "Rapeseed",
+        "Baby Rapeseed",
+        "Baby Choy Sum",
+        "Mustard Greens",
+        "Water Spinach",
+        "Amaranth",
+        "Crown Daisy",
+        "Tomato",
+        "Cherry Tomato",
+        "Cucumber",
+        "Baby Cucumber",
+        "Eggplant",
+        "Long Eggplant",
+        "Round Eggplant",
+        "Green Beans",
+        "Snap Beans",
+        "Hyacinth Beans",
+        "Cowpeas",
+        "Snow Peas",
+        "Peas",
+        "Edamame",
+        "Green Pepper",
+        "Red Pepper",
+        "Yellow Pepper",
+        "Bell Pepper",
+        "Hot Pepper",
+        "Heaven Facing Pepper",
+        "Bird's Eye Chili",
+        "Pickled Pepper",
+        "Potato",
+        "Sweet Potato",
+        "Purple Sweet Potato",
+        "Chinese Yam",
+        "Taro",
+        "Lotus Root",
+        "Water Chestnut",
+        "Arrowhead",
+        "Radish",
+        "White Radish",
+        "Carrot",
+        "Green Radish",
+        "Red Radish",
+        "Cherry Radish",
+        "Onion",
+        "Scallion",
+        "Green Onion",
+        "Chive",
+        "Garlic Sprouts",
+        "Yellow Garlic Sprouts",
+        "Garlic",
+        "Ginger",
+        "Winter Melon",
+        "Pumpkin",
+        "Luffa",
+        "Bitter Melon",
+        "Cucumber",
+        "Zucchini",
+        "Buddha's Hand",
+        "Snake Gourd",
+        "Bean Sprouts",
+        "Mung Bean Sprouts",
+        "Soybean Sprouts",
+        "Pea Shoots",
+        "Radish Sprouts",
+        "Chinese Toon",
+        "Bracken",
+        "Purslane",
+        "Cauliflower",
+        "Broccoli",
+        "Cabbage",
+        "Purple Cabbage",
+        "Chinese Broccoli",
+        "Cauliflower"
+      ],
+      "mushrooms": [
+        "Shiitake",
+        "Flower Mushroom",
+        "Oyster Mushroom",
+        "Enoki Mushroom",
+        "King Oyster Mushroom",
+        "Tea Tree Mushroom",
+        "Straw Mushroom",
+        "Button Mushroom",
+        "Crab Mushroom",
+        "White Jade Mushroom",
+        "Seafood Mushroom",
+        "Chicken Leg Mushroom",
+        "Slippery Mushroom",
+        "Xiu Zhen Mushroom",
+        "Wood Ear",
+        "Black Wood Ear",
+        "Silver Ear",
+        "Snow Ear",
+        "Hairy Wood Ear",
+        "Bamboo Fungus",
+        "Lion's Mane",
+        "Matsutake",
+        "Porcini",
+        "Morel",
+        "Chanterelle",
+        "Hazel Mushroom",
+        "Yuan Mushroom"
+      ],
+      "beans": [
+        "Tofu",
+        "Soft Tofu",
+        "Firm Tofu",
+        "Silken Tofu",
+        "Dried Tofu",
+        "Fragrant Tofu",
+        "White Dried Tofu",
+        "Tofu Skin",
+        "Dried Tofu Skin",
+        "Thousand Sheets",
+        "Tofu Puffs",
+        "Fried Tofu",
+        "Frozen Tofu",
+        "Stinky Tofu",
+        "Edamame",
+        "Peas",
+        "Broad Beans",
+        "Mung Beans",
+        "Red Beans",
+        "Black Beans",
+        "Soybeans",
+        "Kidney Beans",
+        "Bean Sprouts",
+        "Pea Shoots",
+        "Green Beans",
+        "Snap Beans",
+        "Hyacinth Beans",
+        "Cowpeas",
+        "Snow Peas",
+        "Fermented Tofu",
+        "Black Bean Sauce",
+        "Natto",
+        "Soy Milk",
+        "Tofu Pudding"
+      ],
+      "eggs": [
+        "Egg",
+        "Free-Range Egg",
+        "Duck Egg",
+        "Goose Egg",
+        "Quail Egg",
+        "Pigeon Egg",
+        "Salted Duck Egg",
+        "Preserved Egg",
+        "Century Egg",
+        "Tea Egg",
+        "Marinated Egg",
+        "Soft-Boiled Egg"
+      ],
+      "fruits": [
+        "Apple",
+        "Pear",
+        "Peach",
+        "Plum",
+        "Apricot",
+        "Cherry",
+        "Grape",
+        "Strawberry",
+        "Blueberry",
+        "Banana",
+        "Orange",
+        "Pomelo",
+        "Lemon",
+        "Tangerine",
+        "Mandarin",
+        "Kumquat",
+        "Citrus",
+        "Watermelon",
+        "Hami Melon",
+        "Melon",
+        "Muskmelon",
+        "Papaya",
+        "Mango",
+        "Pineapple",
+        "Kiwi",
+        "Dragon Fruit",
+        "Passion Fruit",
+        "Lychee",
+        "Longan",
+        "Jujube",
+        "Persimmon",
+        "Pomegranate",
+        "Hawthorn"
+      ],
+      "nuts": [
+        "Peanut",
+        "Walnut",
+        "Almond",
+        "Cashew",
+        "Pistachio",
+        "Pine Nut",
+        "Hazelnut",
+        "Chestnut",
+        "Sunflower Seeds",
+        "Sunflower Seeds",
+        "Pumpkin Seeds",
+        "Watermelon Seeds",
+        "Sesame",
+        "Black Sesame",
+        "White Sesame"
+      ],
+      "dairy": [
+        "Milk",
+        "Yogurt",
+        "Cheese",
+        "Cheese",
+        "Cream",
+        "Butter",
+        "Condensed Milk",
+        "Milk Powder",
+        "Mozzarella",
+        "Cheddar",
+        "Parmesan",
+        "Milkshake"
+      ]
+    }
+  },
+  "cuisines": {
+    "su": {
+      "name": "Jiangsu Master",
+      "description": "Delicate flavors from the water towns of Jiangnan",
+      "specialty": "Light and fresh, exquisite knife work"
+    },
+    "lu": {
+      "name": "Shandong Master",
+      "description": "Bold flavors from Qilu land",
+      "specialty": "Salty and umami, precise heat control"
+    },
+    "chuan": {
+      "name": "Sichuan Master",
+      "description": "Spicy legend from Bashu land",
+      "specialty": "Numbing spicy, fresh and fragrant, versatile"
+    },
+    "yue": {
+      "name": "Cantonese Master",
+      "description": "Fresh and delicious interpretation of Lingnan culture",
+      "specialty": "Light and fresh, original flavor"
+    },
+    "zhe": {
+      "name": "Zhejiang Master",
+      "description": "Elegant taste from Jiangnan water towns",
+      "specialty": "Fresh and elegant, tender and smooth"
+    },
+    "xiang": {
+      "name": "Hunan Master",
+      "description": "Spicy life of Hunan culture",
+      "specialty": "Fragrant spicy, rich and heavy flavor"
+    },
+    "min": {
+      "name": "Fujian Master",
+      "description": "Seafood feast from Bamin land",
+      "specialty": "Fresh and light, delicious soup"
+    },
+    "hui": {
+      "name": "Anhui Master",
+      "description": "Simple and mellow Huizhou culture",
+      "specialty": "Heavy oil and color, mellow and simple"
+    },
+    "japanese": {
+      "name": "Japanese Cuisine Master",
+      "description": "Ultimate aesthetics of Washoku",
+      "specialty": "Light original flavor, strong seasonal sense"
+    },
+    "korean": {
+      "name": "Korean Cuisine Master",
+      "description": "Fermentation wisdom from Korean Peninsula",
+      "specialty": "Fermented seasoning, balanced nutrition"
+    },
+    "italian": {
+      "name": "Italian Cuisine Master",
+      "description": "Sunny flavors of the Mediterranean",
+      "specialty": "Simple and refined, olive oil aroma"
+    },
+    "french": {
+      "name": "French Cuisine Master",
+      "description": "Elegant art of the Gallic rooster",
+      "specialty": "Exquisite and elegant, rich sauces"
+    },
+    "indian": {
+      "name": "Indian Cuisine Master",
+      "description": "Mysterious charm of the spice kingdom",
+      "specialty": "Rich spices, complex layers"
+    },
+    "thai": {
+      "name": "Thai Cuisine Master",
+      "description": "Sweet-sour balance of Siam",
+      "specialty": "Sweet-sour-spicy-fresh, rich herbs"
+    },
+    "mexican": {
+      "name": "Mexican Cuisine Master",
+      "description": "Spicy heritage of the Aztecs",
+      "specialty": "Rich peppers, corn culture"
+    }
+  },
+  "fortune": {
+    "zodiac": {
+      "aries": "Aries",
+      "taurus": "Taurus",
+      "gemini": "Gemini",
+      "cancer": "Cancer",
+      "leo": "Leo",
+      "virgo": "Virgo",
+      "libra": "Libra",
+      "scorpio": "Scorpio",
+      "sagittarius": "Sagittarius",
+      "capricorn": "Capricorn",
+      "aquarius": "Aquarius",
+      "pisces": "Pisces"
+    },
+    "animals": {
+      "rat": "Rat",
+      "ox": "Ox",
+      "tiger": "Tiger",
+      "rabbit": "Rabbit",
+      "dragon": "Dragon",
+      "snake": "Snake",
+      "horse": "Horse",
+      "goat": "Goat",
+      "monkey": "Monkey",
+      "rooster": "Rooster",
+      "dog": "Dog",
+      "pig": "Pig"
+    },
+    "moods": {
+      "happy": {
+        "name": "Happy",
+        "description": "Feeling joyful, perfect for making colorful dishes",
+        "cookingStyle": {
+          "0": "Desserts",
+          "1": "Colorful",
+          "2": "Celebration dishes",
+          "3": "Easy to make"
+        }
+      },
+      "sad": {
+        "name": "Sad",
+        "description": "Need warm and comforting food to soothe the soul",
+        "cookingStyle": {
+          "0": "Warm soups",
+          "1": "Comforting",
+          "2": "Home-style dishes",
+          "3": "Slow-cooked"
+        }
+      },
+      "anxious": {
+        "name": "Anxious",
+        "description": "Choose simple and light food, avoid complex preparation",
+        "cookingStyle": {
+          "0": "Light dishes",
+          "1": "Soothing tea",
+          "2": "Simple preparation",
+          "3": "Healthy"
+        }
+      },
+      "tired": {
+        "name": "Tired",
+        "description": "Need quick energy-boosting nutritious food",
+        "cookingStyle": {
+          "0": "Nutritional supplement",
+          "1": "Quick dishes",
+          "2": "Energy food",
+          "3": "Simple"
+        }
+      },
+      "excited": {
+        "name": "Excited",
+        "description": "Full of energy, perfect for trying challenging dishes",
+        "cookingStyle": {
+          "0": "Challenging dishes",
+          "1": "Innovative cuisine",
+          "2": "Complex preparation",
+          "3": "Experimental"
+        }
+      },
+      "calm": {
+        "name": "Calm",
+        "description": "Peaceful mind, perfect for making light and elegant dishes",
+        "cookingStyle": {
+          "0": "Vegetarian",
+          "1": "Light",
+          "2": "Zen cuisine",
+          "3": "Slow-paced"
+        }
+      },
+      "angry": {
+        "name": "Angry",
+        "description": "Release emotions by making bold-flavored food",
+        "cookingStyle": {
+          "0": "Spicy dishes",
+          "1": "Bold flavors",
+          "2": "Vent cooking",
+          "3": "Stimulating"
+        }
+      },
+      "nostalgic": {
+        "name": "Nostalgic",
+        "description": "Make traditional hometown dishes full of memories",
+        "cookingStyle": {
+          "0": "Nostalgic dishes",
+          "1": "Hometown flavor",
+          "2": "Traditional cuisine",
+          "3": "Memories"
+        }
+      }
+    },
+    "fortuneTeller": {
+      "name": "Star-Moon Fortune Teller",
+      "greetings": {
+        "0": "Welcome to the mysterious culinary divination hall...",
+        "1": "The stars guide the direction of food, let me divine for you...",
+        "2": "Delicious prophecies appear in the crystal ball...",
+        "3": "The wheel of fate is turning, your exclusive dish is about to be revealed..."
+      },
+      "phrases": {
+        "daily": {
+          "opening": {
+            "0": "Let me sense today's astrological energy...",
+            "1": "The power of zodiac and Chinese zodiac is gathering...",
+            "2": "Today's cosmic energy will guide your taste buds..."
+          },
+          "closing": {
+            "0": "May this lucky dish bring you good fortune...",
+            "1": "The stars have chosen today's deliciousness for you...",
+            "2": "Follow the guidance of the stars, enjoy this food..."
+          },
+          "processing": {
+            "0": "Reading the secrets of the zodiac...",
+            "1": "The wisdom of the Chinese zodiac is emerging...",
+            "2": "The energy of the universe is gathering..."
+          }
+        },
+        "mood": {
+          "opening": {
+            "0": "Let me feel the emotional fluctuations in your heart...",
+            "1": "The frequency of the soul is resonating with food...",
+            "2": "The color of emotions will determine your dish..."
+          },
+          "closing": {
+            "0": "May this healing dish soothe your soul...",
+            "1": "Food is the best emotional healer...",
+            "2": "Let your taste buds take away your worries..."
+          },
+          "processing": {
+            "0": "Analyzing your emotional energy...",
+            "1": "The color of your mood is emerging...",
+            "2": "The power of healing is gathering..."
+          }
+        },
+        "couple": {
+          "opening": {
+            "0": "Let me sense the fate between you...",
+            "1": "The frequencies of two hearts are resonating...",
+            "2": "The spark of understanding is about to ignite the food..."
+          },
+          "closing": {
+            "0": "May this fate dish enhance your relationship...",
+            "1": "Discover each other's beauty in cooking together...",
+            "2": "Food witnesses your understanding..."
+          },
+          "processing": {
+            "0": "Analyzing your zodiac compatibility...",
+            "1": "The threads of fate are being woven...",
+            "2": "The compatibility index is being calculated..."
+          }
+        },
+        "number": {
+          "opening": {
+            "0": "Numbers are the language of the universe...",
+            "1": "Let me interpret the meaning of this mysterious number...",
+            "2": "The secrets of food are hidden in numbers..."
+          },
+          "closing": {
+            "0": "The power of numbers will bring you luck...",
+            "1": "Enjoy food under the guidance of this mysterious number...",
+            "2": "The perfect combination of numbers and deliciousness..."
+          },
+          "processing": {
+            "0": "Parsing the mysterious power of numbers...",
+            "1": "The energy of numbers is emerging...",
+            "2": "The cosmic code is being unlocked..."
+          }
+        }
+      },
+      "mysticalWords": {
+        "0": "Stars never lie, food never betrays...",
+        "1": "Finding the true meaning of life in cooking...",
+        "2": "Every dish is arranged by fate...",
+        "3": "Taste buds are the bridge to the soul...",
+        "4": "Food contains the wisdom of the universe...",
+        "5": "Cooking is a sacred ritual...",
+        "6": "Feel the rhythm of life in pots and pans...",
+        "7": "Every taste is a journey of the soul..."
+      }
+    }
+  },
+  "sauces": {
+    "categories": {
+      "spicy": {
+        "name": "Spicy Sauces",
+        "description": "Various chili sauces, chili oil, and spicy sauces"
+      },
+      "garlic": {
+        "name": "Garlic Sauces",
+        "description": "Aromatic sauces mainly based on garlic"
+      },
+      "sweet": {
+        "name": "Sweet Sauces",
+        "description": "Sweet sauces and jams"
+      },
+      "complex": {
+        "name": "Complex Seasoning Sauces",
+        "description": "Compound sauces with mixed seasonings"
+      },
+      "regional": {
+        "name": "Regional Specialty Sauces",
+        "description": "Traditional specialty sauces from various regions"
+      },
+      "fusion": {
+        "name": "Innovative Fusion Sauces",
+        "description": "Modern innovative and East-West fusion sauces"
+      }
+    },
+    "useCases": {
+      "noodles": "For Noodles",
+      "dipping": "For Dipping",
+      "cooking": "For Stir-frying",
+      "bbq": "For BBQ / Grilling",
+      "hotpot": "For Hot Pot"
+    },
+    "tasteLevels": {
+      "spice": {
+        "0": "Not Spicy",
+        "1": "Mildly Spicy",
+        "2": "Moderately Spicy",
+        "3": "Very Spicy",
+        "4": "Extremely Spicy"
+      },
+      "sweet": {
+        "0": "Not Sweet",
+        "1": "Mildly Sweet",
+        "2": "Moderately Sweet",
+        "3": "Very Sweet",
+        "4": "Extremely Sweet"
+      },
+      "salt": {
+        "0": "Not Salty",
+        "1": "Mildly Salty",
+        "2": "Moderately Salty",
+        "3": "Very Salty",
+        "4": "Extremely Salty"
+      },
+      "sour": {
+        "0": "Not Sour",
+        "1": "Mildly Sour",
+        "2": "Moderately Sour",
+        "3": "Very Sour",
+        "4": "Extremely Sour"
+      }
+    },
+    "difficulty": {
+      "easy": {
+        "name": "Easy",
+        "description": "Beginner-friendly, simple steps"
+      },
+      "medium": {
+        "name": "Medium",
+        "description": "Requires some experience"
+      },
+      "hard": {
+        "name": "Hard",
+        "description": "Requires rich experience and skills"
+      }
+    }
+  },
+  "ai": {
+    "systemPrompt": "You are a professional chef. Please generate detailed recipes based on the ingredients and cuisine requirements provided by the user.",
+    "responseFormat": "Please return the recipe in the following JSON format:\n{\n  \"name\": \"Dish Name\",\n  \"ingredients\": [\"Ingredient 1\", \"Ingredient 2\"],\n  \"steps\": [\n    {\n      \"step\": 1,\n      \"description\": \"Step description\",\n      \"time\": 5,\n      \"temperature\": \"Medium heat\"\n    }\n  ],\n  \"cookingTime\": 30,\n  \"difficulty\": \"medium\",\n  \"tips\": [\"Tip 1\", \"Tip 2\"]\n}"
+  }
+}

+ 796 - 0
src/i18n/locales/zh.json

@@ -0,0 +1,796 @@
+{
+  "home": {
+    "pageTitle": "首页",
+    "step1": {
+      "title": "1. 输入食材",
+      "addIngredients": {
+        "title": "添加食材",
+        "description": "输入你现有的食材,按回车添加",
+        "hint": "支持蔬菜、肉类、调料等 (最多10种)",
+        "placeholder": "输入食材名称,按回车添加...",
+        "quickPick": "快速选择食材",
+        "quickPickHint": "点击食材快速添加到列表",
+        "selectedCount": "已选择 {count}/10"
+      }
+    },
+    "step2": {
+      "title": "2. 选择菜系",
+      "cuisines": {
+        "chinese": "中华八大菜系",
+        "international": "国际菜系",
+        "masterSuffix": "大师",
+        "internationalSuffix1": "日式"
+      },
+      "customRequest": {
+        "title": "或自定义要求",
+        "quickPresets": "快速预设",
+        "freeDescription": "自由描述:",
+        "placeholder": "例如:做一道清淡的汤,适合老人食用,不要太咸...",
+        "randomInspiration": "随机灵感",
+        "tip": "提示:越具体越好!",
+        "set": "已设置",
+        "clear": "清除",
+        "clearButton": "清除",
+        "scenePresets": "场景预设",
+        "tastePresets": "口味预设"
+      }
+    },
+    "step3": {
+      "title": "3. 交给大师",
+      "readyToCook": "准备开始烹饪",
+      "masterReady": "大师已准备就绪,点击按钮开始创作美味佳肴",
+      "currentConfig": "当前配置",
+      "ingredientsLabel": "食材",
+      "cuisinesLabel": "菜系大师",
+      "customRequestLabel": "自定义要求",
+      "noIngredients": "未添加食材",
+      "noCuisines": "未选择大师",
+      "usingCustomRequest": "使用自定义要求",
+      "generateButton": {
+        "default": "交给大师",
+        "withCustom": "按要求生成",
+        "loading": "生成中..."
+      },
+      "hints": {
+        "withCustom": "将根据您的自定义要求生成菜谱",
+        "withCuisines": "将生成 {count} 个菜系的菜谱",
+        "default": "大师将为您精心设计菜谱流程"
+      }
+    },
+    "step4": {
+      "title": "4. 菜谱结果",
+      "emptyState": {
+        "title": "等待魔法发生...",
+        "description": "添加食材并选择菜系开始创作"
+      },
+      "loading": {
+        "masterCreating": "{name}正在精心创作...",
+        "masterThinking": "{name}正在思考中...",
+        "masterPreparing": "{name}正在准备中...",
+        "masterCooking": "{name}正在烹饪中...",
+        "masterPlating": "{name}正在摆盘中...",
+        "masterFinishing": "{name}即将完成...",
+        "progress": "已完成 {current}/{total} 道菜谱...",
+        "carefullyCrafted": "精心制作中",
+        "estimatedTime": "预计时间",
+        "usedIngredients": "使用食材",
+        "stepsPreview": "步骤预览",
+        "completed": "完成"
+      },
+      "error": {
+        "title": "大师表示很为难",
+        "description": "{name}看了看你的食材,挠了挠头说:\"这个组合我还没学会呢!\"",
+        "suggestionsTitle": "大师的建议",
+        "suggestion1": "试试其他菜系大师,他们可能有不同的想法",
+        "suggestion2": "调整一下食材搭配,或许会有惊喜",
+        "suggestion3": "使用自定义要求,给大师一些灵感",
+        "retryButton": "再试一次",
+        "generalTitle": "生成失败",
+        "skillIssue": "技能问题",
+        "slacking": "摸鱼中"
+      }
+    }
+  },
+  "ingredients": {
+    "categories": {
+      "meat": "荤菜",
+      "seafood": "海鲜",
+      "vegetables": "蔬菜",
+      "mushrooms": "菌菇",
+      "beans": "豆类",
+      "eggs": "蛋类",
+      "fruits": "水果",
+      "nuts": "坚果",
+      "dairy": "奶制品"
+    },
+    "items": {
+      "meat": [
+        "猪肉",
+        "牛肉",
+        "羊肉",
+        "鸡肉",
+        "鸭肉",
+        "鹅肉",
+        "兔肉",
+        "驴肉",
+        "猪排骨",
+        "牛排",
+        "羊排",
+        "鸡翅",
+        "鸡腿",
+        "鸡胸肉",
+        "鸡爪",
+        "鸭腿",
+        "五花肉",
+        "瘦肉",
+        "肉丝",
+        "肉片",
+        "肉丁",
+        "肉馅",
+        "里脊肉",
+        "梅花肉",
+        "腊肉",
+        "香肠",
+        "火腿",
+        "培根",
+        "腊肠",
+        "咸肉",
+        "风干肉",
+        "牛肉干",
+        "猪蹄",
+        "猪肚",
+        "猪肝",
+        "猪心",
+        "猪肺",
+        "猪腰",
+        "牛肚",
+        "羊肚",
+        "鸡心",
+        "鸡肝",
+        "鸡胗",
+        "鸭血",
+        "猪血"
+      ],
+      "seafood": [
+        "鲈鱼",
+        "鲫鱼",
+        "草鱼",
+        "鲤鱼",
+        "带鱼",
+        "黄鱼",
+        "鳕鱼",
+        "三文鱼",
+        "金枪鱼",
+        "鲳鱼",
+        "石斑鱼",
+        "桂鱼",
+        "鲢鱼",
+        "青鱼",
+        "武昌鱼",
+        "比目鱼",
+        "鳗鱼",
+        "刀鱼",
+        "大虾",
+        "基围虾",
+        "龙虾",
+        "白虾",
+        "河虾",
+        "明虾",
+        "对虾",
+        "皮皮虾",
+        "螃蟹",
+        "大闸蟹",
+        "梭子蟹",
+        "青蟹",
+        "毛蟹",
+        "扇贝",
+        "蛤蜊",
+        "生蚝",
+        "牡蛎",
+        "花甲",
+        "蚬子",
+        "海螺",
+        "田螺",
+        "鱿鱼",
+        "章鱼",
+        "墨鱼",
+        "海参",
+        "鲍鱼",
+        "海带",
+        "紫菜",
+        "裙带菜"
+      ],
+      "vegetables": [
+        "白菜",
+        "大白菜",
+        "小白菜",
+        "娃娃菜",
+        "菠菜",
+        "韭菜",
+        "韭黄",
+        "芹菜",
+        "西芹",
+        "生菜",
+        "油菜",
+        "小油菜",
+        "菜心",
+        "芥菜",
+        "空心菜",
+        "苋菜",
+        "茼蒿",
+        "西红柿",
+        "樱桃番茄",
+        "黄瓜",
+        "小黄瓜",
+        "茄子",
+        "长茄子",
+        "圆茄子",
+        "豆角",
+        "四季豆",
+        "扁豆",
+        "豇豆",
+        "荷兰豆",
+        "豌豆",
+        "毛豆",
+        "青椒",
+        "红椒",
+        "黄椒",
+        "彩椒",
+        "尖椒",
+        "朝天椒",
+        "小米椒",
+        "泡椒",
+        "土豆",
+        "红薯",
+        "紫薯",
+        "山药",
+        "芋头",
+        "莲藕",
+        "荸荠",
+        "慈姑",
+        "萝卜",
+        "白萝卜",
+        "胡萝卜",
+        "青萝卜",
+        "心里美萝卜",
+        "樱桃萝卜",
+        "洋葱",
+        "大葱",
+        "小葱",
+        "香葱",
+        "蒜苗",
+        "蒜黄",
+        "大蒜",
+        "生姜",
+        "冬瓜",
+        "南瓜",
+        "丝瓜",
+        "苦瓜",
+        "黄瓜",
+        "西葫芦",
+        "佛手瓜",
+        "蛇瓜",
+        "豆芽",
+        "绿豆芽",
+        "黄豆芽",
+        "豆苗",
+        "萝卜苗",
+        "香椿",
+        "蕨菜",
+        "马齿苋",
+        "菜花",
+        "西兰花",
+        "包菜",
+        "紫甘蓝",
+        "芥蓝",
+        "花椰菜"
+      ],
+      "mushrooms": [
+        "香菇",
+        "花菇",
+        "平菇",
+        "金针菇",
+        "杏鲍菇",
+        "茶树菇",
+        "草菇",
+        "口蘑",
+        "蟹味菇",
+        "白玉菇",
+        "海鲜菇",
+        "鸡腿菇",
+        "滑子菇",
+        "秀珍菇",
+        "木耳",
+        "黑木耳",
+        "银耳",
+        "雪耳",
+        "毛木耳",
+        "竹荪",
+        "猴头菇",
+        "松茸",
+        "牛肝菌",
+        "羊肚菌",
+        "鸡油菌",
+        "榛蘑",
+        "元蘑"
+      ],
+      "beans": [
+        "豆腐",
+        "嫩豆腐",
+        "老豆腐",
+        "内酯豆腐",
+        "豆腐干",
+        "香干",
+        "白干",
+        "豆皮",
+        "腐竹",
+        "千张",
+        "豆泡",
+        "油豆腐",
+        "冻豆腐",
+        "臭豆腐",
+        "毛豆",
+        "豌豆",
+        "蚕豆",
+        "绿豆",
+        "红豆",
+        "黑豆",
+        "黄豆",
+        "芸豆",
+        "豆芽",
+        "豆苗",
+        "豆角",
+        "四季豆",
+        "扁豆",
+        "豇豆",
+        "荷兰豆",
+        "腐乳",
+        "豆豉",
+        "纳豆",
+        "豆浆",
+        "豆花"
+      ],
+      "eggs": [
+        "鸡蛋",
+        "土鸡蛋",
+        "鸭蛋",
+        "鹅蛋",
+        "鹌鹑蛋",
+        "鸽子蛋",
+        "咸鸭蛋",
+        "松花蛋",
+        "皮蛋",
+        "茶叶蛋",
+        "卤蛋",
+        "溏心蛋"
+      ],
+      "fruits": [
+        "苹果",
+        "梨",
+        "桃子",
+        "李子",
+        "杏",
+        "樱桃",
+        "葡萄",
+        "草莓",
+        "蓝莓",
+        "香蕉",
+        "橙子",
+        "柚子",
+        "柠檬",
+        "橘子",
+        "桔子",
+        "金桔",
+        "柑橘",
+        "西瓜",
+        "哈密瓜",
+        "甜瓜",
+        "香瓜",
+        "木瓜",
+        "芒果",
+        "菠萝",
+        "猕猴桃",
+        "火龙果",
+        "百香果",
+        "荔枝",
+        "龙眼",
+        "枣",
+        "柿子",
+        "石榴",
+        "山楂"
+      ],
+      "nuts": [
+        "花生",
+        "核桃",
+        "杏仁",
+        "腰果",
+        "开心果",
+        "松子",
+        "榛子",
+        "板栗",
+        "瓜子",
+        "葵花籽",
+        "南瓜子",
+        "西瓜子",
+        "芝麻",
+        "黑芝麻",
+        "白芝麻"
+      ],
+      "dairy": [
+        "牛奶",
+        "酸奶",
+        "奶酪",
+        "芝士",
+        "奶油",
+        "黄油",
+        "炼乳",
+        "奶粉",
+        "马苏里拉奶酪",
+        "车达奶酪",
+        "帕玛森奶酪",
+        "奶昔"
+      ]
+    }
+  },
+  "cuisines": {
+    "su": {
+      "name": "苏菜大师",
+      "description": "江南水乡的精致美味",
+      "specialty": "清淡鲜美,刀工精细"
+    },
+    "lu": {
+      "name": "鲁菜大师",
+      "description": "齐鲁大地的豪放风味",
+      "specialty": "咸鲜为主,火候精准"
+    },
+    "chuan": {
+      "name": "川菜大师",
+      "description": "巴蜀之地的麻辣传奇",
+      "specialty": "麻辣鲜香,变化多端"
+    },
+    "yue": {
+      "name": "粤菜大师",
+      "description": "岭南文化的鲜美诠释",
+      "specialty": "清淡鲜美,原汁原味"
+    },
+    "zhe": {
+      "name": "浙菜大师",
+      "description": "江南水乡的清雅之味",
+      "specialty": "清香淡雅,鲜嫩爽滑"
+    },
+    "xiang": {
+      "name": "湘菜大师",
+      "description": "湖湘文化的辣味人生",
+      "specialty": "香辣浓郁,口味厚重"
+    },
+    "min": {
+      "name": "闽菜大师",
+      "description": "八闽大地的海鲜盛宴",
+      "specialty": "鲜香清淡,汤鲜味美"
+    },
+    "hui": {
+      "name": "徽菜大师",
+      "description": "徽州文化的朴实醇香",
+      "specialty": "重油重色,醇厚朴实"
+    },
+    "japanese": {
+      "name": "日式料理大师",
+      "description": "和食之道的极致美学",
+      "specialty": "清淡本味,季节感强"
+    },
+    "korean": {
+      "name": "韩式料理大师",
+      "description": "韩半岛的发酵智慧",
+      "specialty": "发酵调味,营养均衡"
+    },
+    "italian": {
+      "name": "意式料理大师",
+      "description": "地中海的阳光味道",
+      "specialty": "简约精致,橄榄油香"
+    },
+    "french": {
+      "name": "法式料理大师",
+      "description": "高卢雄鸡的优雅艺术",
+      "specialty": "精致优雅,酱汁丰富"
+    },
+    "indian": {
+      "name": "印度料理大师",
+      "description": "香料王国的神秘魅力",
+      "specialty": "香料丰富,层次复杂"
+    },
+    "thai": {
+      "name": "泰式料理大师",
+      "description": "暹罗王国的酸甜平衡",
+      "specialty": "酸甜辣鲜,香草丰富"
+    },
+    "mexican": {
+      "name": "墨西哥料理大师",
+      "description": "阿兹特克的火辣传承",
+      "specialty": "辣椒丰富,玉米文化"
+    }
+  },
+  "fortune": {
+    "zodiac": {
+      "aries": "白羊座",
+      "taurus": "金牛座",
+      "gemini": "双子座",
+      "cancer": "巨蟹座",
+      "leo": "狮子座",
+      "virgo": "处女座",
+      "libra": "天秤座",
+      "scorpio": "天蝎座",
+      "sagittarius": "射手座",
+      "capricorn": "摩羯座",
+      "aquarius": "水瓶座",
+      "pisces": "双鱼座"
+    },
+    "animals": {
+      "rat": "鼠",
+      "ox": "牛",
+      "tiger": "虎",
+      "rabbit": "兔",
+      "dragon": "龙",
+      "snake": "蛇",
+      "horse": "马",
+      "goat": "羊",
+      "monkey": "猴",
+      "rooster": "鸡",
+      "dog": "狗",
+      "pig": "猪"
+    },
+    "moods": {
+      "happy": {
+        "name": "开心",
+        "description": "心情愉悦,适合制作色彩缤纷的美食",
+        "cookingStyle": {
+          "0": "甜品",
+          "1": "色彩丰富",
+          "2": "庆祝菜品",
+          "3": "轻松制作"
+        }
+      },
+      "sad": {
+        "name": "难过",
+        "description": "需要温暖治愈的食物来抚慰心灵",
+        "cookingStyle": {
+          "0": "温暖汤品",
+          "1": "治愈系",
+          "2": "家常菜",
+          "3": "慢炖"
+        }
+      },
+      "anxious": {
+        "name": "焦虑",
+        "description": "选择简单清淡的食物,避免复杂制作",
+        "cookingStyle": {
+          "0": "清淡菜品",
+          "1": "舒缓茶饮",
+          "2": "简单制作",
+          "3": "健康"
+        }
+      },
+      "tired": {
+        "name": "疲惫",
+        "description": "需要快速补充能量的营养食物",
+        "cookingStyle": {
+          "0": "营养补充",
+          "1": "快手菜",
+          "2": "能量食物",
+          "3": "简便"
+        }
+      },
+      "excited": {
+        "name": "兴奋",
+        "description": "精力充沛,适合尝试有挑战性的菜品",
+        "cookingStyle": {
+          "0": "挑战菜品",
+          "1": "创新料理",
+          "2": "复杂制作",
+          "3": "实验"
+        }
+      },
+      "calm": {
+        "name": "平静",
+        "description": "心境平和,适合制作清淡素雅的菜品",
+        "cookingStyle": {
+          "0": "素食",
+          "1": "清淡",
+          "2": "禅意料理",
+          "3": "慢节奏"
+        }
+      },
+      "angry": {
+        "name": "愤怒",
+        "description": "通过制作重口味食物来释放情绪",
+        "cookingStyle": {
+          "0": "辛辣菜品",
+          "1": "重口味",
+          "2": "发泄式烹饪",
+          "3": "刺激"
+        }
+      },
+      "nostalgic": {
+        "name": "思念",
+        "description": "制作充满回忆的传统家乡菜",
+        "cookingStyle": {
+          "0": "怀旧菜品",
+          "1": "家乡味",
+          "2": "传统料理",
+          "3": "回忆"
+        }
+      }
+    },
+    "fortuneTeller": {
+      "name": "星月占卜师",
+      "greetings": {
+        "0": "欢迎来到神秘的料理占卜殿堂...",
+        "1": "星辰指引着美食的方向,让我为你占卜...",
+        "2": "水晶球中浮现出美味的预言...",
+        "3": "命运之轮正在转动,你的专属料理即将揭晓..."
+      },
+      "phrases": {
+        "daily": {
+          "opening": {
+            "0": "让我感应今日的星象能量...",
+            "1": "星座与生肖的力量正在汇聚...",
+            "2": "今日的宇宙能量将指引你的味蕾..."
+          },
+          "closing": {
+            "0": "愿这道幸运料理为你带来好运...",
+            "1": "星辰已为你选定了今日的美味...",
+            "2": "按照星象的指引,享受这份美食吧..."
+          },
+          "processing": {
+            "0": "正在解读星座的秘密...",
+            "1": "生肖的智慧正在显现...",
+            "2": "宇宙的能量正在汇聚..."
+          }
+        },
+        "mood": {
+          "opening": {
+            "0": "让我感受你内心的情感波动...",
+            "1": "心灵的频率正在与美食共鸣...",
+            "2": "情感的色彩将决定你的料理..."
+          },
+          "closing": {
+            "0": "愿这道治愈料理抚慰你的心灵...",
+            "1": "美食是最好的情感治愈师...",
+            "2": "让味蕾带走你的烦恼..."
+          },
+          "processing": {
+            "0": "正在分析你的情感能量...",
+            "1": "心情的颜色正在显现...",
+            "2": "治愈的力量正在汇聚..."
+          }
+        },
+        "couple": {
+          "opening": {
+            "0": "让我感应你们之间的缘分...",
+            "1": "两颗心的频率正在共鸣...",
+            "2": "默契的火花即将点燃美食..."
+          },
+          "closing": {
+            "0": "愿这道缘分料理增进你们的感情...",
+            "1": "在共同烹饪中发现彼此的美好...",
+            "2": "美食见证你们的默契..."
+          },
+          "processing": {
+            "0": "正在分析你们的星座配对...",
+            "1": "缘分的丝线正在编织...",
+            "2": "默契指数正在计算..."
+          }
+        },
+        "number": {
+          "opening": {
+            "0": "数字是宇宙的语言...",
+            "1": "让我解读这个神秘数字的含义...",
+            "2": "数字中隐藏着美食的秘密..."
+          },
+          "closing": {
+            "0": "数字的力量将为你带来幸运...",
+            "1": "在这个神秘数字的指引下享受美食...",
+            "2": "数字与美味的完美结合..."
+          },
+          "processing": {
+            "0": "正在解析数字的神秘力量...",
+            "1": "数字的能量正在显现...",
+            "2": "宇宙密码正在解锁..."
+          }
+        }
+      },
+      "mysticalWords": {
+        "0": "星辰不会说谎,美食不会背叛...",
+        "1": "在料理中寻找生活的真谛...",
+        "2": "每一道菜都是命运的安排...",
+        "3": "味蕾是通往心灵的桥梁...",
+        "4": "美食中蕴含着宇宙的智慧...",
+        "5": "烹饪是一种神圣的仪式...",
+        "6": "在锅碗瓢盆中感受生活的节拍...",
+        "7": "每一次品尝都是一次心灵的旅行..."
+      }
+    }
+  },
+  "sauces": {
+    "categories": {
+      "spicy": {
+        "name": "辣味酱料",
+        "description": "各种辣椒酱、辣油、麻辣酱料"
+      },
+      "garlic": {
+        "name": "蒜香酱料",
+        "description": "以大蒜为主的香味酱料"
+      },
+      "sweet": {
+        "name": "甜味酱料",
+        "description": "甜口酱料和果酱类"
+      },
+      "complex": {
+        "name": "复合调味酱",
+        "description": "多种调料混合的复合酱料"
+      },
+      "regional": {
+        "name": "地方特色酱",
+        "description": "各地传统特色酱料"
+      },
+      "fusion": {
+        "name": "创新融合酱",
+        "description": "现代创新和中西融合酱料"
+      }
+    },
+    "useCases": {
+      "noodles": "拌面",
+      "dipping": "蘸菜",
+      "cooking": "炒菜",
+      "bbq": "烧烤",
+      "hotpot": "火锅"
+    },
+    "tasteLevels": {
+      "spice": {
+        "0": "不辣",
+        "1": "微辣",
+        "2": "中辣",
+        "3": "很辣",
+        "4": "超辣"
+      },
+      "sweet": {
+        "0": "不甜",
+        "1": "微甜",
+        "2": "中甜",
+        "3": "很甜",
+        "4": "超甜"
+      },
+      "salt": {
+        "0": "不咸",
+        "1": "微咸",
+        "2": "中咸",
+        "3": "很咸",
+        "4": "超咸"
+      },
+      "sour": {
+        "0": "不酸",
+        "1": "微酸",
+        "2": "中酸",
+        "3": "很酸",
+        "4": "超酸"
+      }
+    },
+    "difficulty": {
+      "easy": {
+        "name": "简单",
+        "description": "新手友好,步骤简单"
+      },
+      "medium": {
+        "name": "中等",
+        "description": "需要一定经验"
+      },
+      "hard": {
+        "name": "困难",
+        "description": "需要丰富经验和技巧"
+      }
+    }
+  },
+  "ai": {
+    "systemPrompt": "你是一位专业的厨师,请根据用户提供的食材和菜系要求,生成详细的菜谱。",
+    "responseFormat": "请按照以下JSON格式返回菜谱:\n{\n  \"name\": \"菜品名称\",\n  \"ingredients\": [\"食材1\", \"食材2\"],\n  \"steps\": [\n    {\n      \"step\": 1,\n      \"description\": \"步骤描述\",\n      \"time\": 5,\n      \"temperature\": \"中火\"\n    }\n  ],\n  \"cookingTime\": 30,\n  \"difficulty\": \"medium\",\n  \"tips\": [\"技巧1\", \"技巧2\"]\n}"
+  }
+}

+ 9 - 0
src/main.ts

@@ -14,6 +14,11 @@ import SettingsDemo from './views/SettingsDemo.vue'
 import { autoRefreshEnvSettings } from './utils/envWatcher'
 import './style.css'
 
+import { Toast, setToastDefaultOptions } from 'vant';
+import 'vant/lib/index.css';
+
+import i18n from './i18n'
+
 const routes = [
     { path: '/', component: Home },
     { path: '/about', component: About },
@@ -35,6 +40,10 @@ const router = createRouter({
 // 初始化应用
 const app = createApp(App).use(router)
 
+app.use(i18n)
+app.use(Toast)
+setToastDefaultOptions({ duration: 3000 })
+
 // 在应用挂载前检查环境变量变化并自动刷新
 autoRefreshEnvSettings()
 

+ 1 - 1
src/services/favoriteService.ts

@@ -1,6 +1,6 @@
 import type { Recipe, FavoriteRecipe } from '@/types'
 
-const FAVORITES_KEY = 'yifan-fengshen-favorites'
+const FAVORITES_KEY = 'recipe-muse-favorites'
 
 /**
  * 收藏服务 - 管理本地收藏的菜谱

+ 1 - 1
src/stores/settings.js

@@ -36,7 +36,7 @@ const getLatestEnvSettings = () => {
 }
 
 // 存储键名
-const STORAGE_KEY = 'yifan-fengshen-settings'
+const STORAGE_KEY = 'recipe-muse-settings'
 
 // 全局设置状态
 let settingsInstance = null

+ 37 - 0
src/utils/auth.ts

@@ -0,0 +1,37 @@
+const TokenKey = 'Recipe-Muse-Token'
+
+const ExpiresInKey = 'Recipe-Muse-Expires-In'
+
+const UserKey = 'Recipe-Muse-User'
+
+export function setUserInfo(user: any) {
+  return user && localStorage.setItem(UserKey, JSON.stringify(user))
+}
+
+export function getUserInfo() {
+  return localStorage.getItem(UserKey)
+}
+
+export function getToken() {
+  return localStorage.getItem(TokenKey)
+}
+
+export function setToken(token: string) {
+  return token && localStorage.setItem(TokenKey, token)
+}
+
+export function removeToken() {
+  return localStorage.removeItem(TokenKey)
+}
+
+export function getExpiresIn() {
+  return localStorage.getItem(ExpiresInKey) || -1
+}
+
+export function setExpiresIn(time) {
+  return time && localStorage.setItem(ExpiresInKey, time)
+}
+
+export function removeExpiresIn() {
+  return localStorage.removeItem(ExpiresInKey)
+}

+ 3 - 3
src/utils/envWatcher.js

@@ -33,7 +33,7 @@ export const initEnvWatcher = () => {
     const currentSnapshot = getCurrentEnvSnapshot()
 
     // 在localStorage中存储环境变量快照
-    const storageKey = 'yifan-fengshen-env-snapshot'
+    const storageKey = 'recipe-muse-env-snapshot'
     const storedSnapshot = localStorage.getItem(storageKey)
 
     if (storedSnapshot) {
@@ -67,7 +67,7 @@ export const initEnvWatcher = () => {
 // 手动检查环境变量变化
 export const checkEnvChanges = () => {
     const currentSnapshot = getCurrentEnvSnapshot()
-    const storageKey = 'yifan-fengshen-env-snapshot'
+    const storageKey = 'recipe-muse-env-snapshot'
     const storedSnapshot = localStorage.getItem(storageKey)
 
     let hasChanged = false
@@ -102,7 +102,7 @@ export const autoRefreshEnvSettings = () => {
         console.log('检测到环境变量变化,自动刷新配置')
 
         // 清除localStorage中的设置缓存,让应用重新读取环境变量
-        localStorage.removeItem('yifan-fengshen-settings')
+        localStorage.removeItem('recipe-muse-settings')
 
         // 自动刷新页面以应用最新配置
         window.location.reload()

+ 73 - 0
src/utils/idleTimer.ts

@@ -0,0 +1,73 @@
+let options = {};
+
+function idleTimer() {
+  // 用户操作监听中
+  let isActive = false
+  let timer
+
+  // 外部会进行初始化调用: store.dispatch('RefreshToken')
+  // start()
+
+  function addOrRemoveEvents(addOrRemove) {
+    // 移动端触摸事件
+    document[addOrRemove]('touchstart', check)
+    document[addOrRemove]('touchmove', check)
+    document[addOrRemove]('touchend', check)
+    // 通用事件
+    document[addOrRemove]('scroll', check)
+    document[addOrRemove]('click', check)
+    // PC端兼容
+    document[addOrRemove]('mousemove', check)
+    document[addOrRemove]('keypress', check)
+  }
+
+  // 启动用户监听
+  function enableListener() {
+    if (isActive) return
+    destroy()
+    const time = options.idleTime
+    console.log('idleTime 时间后启动监听', time);
+    timer = setTimeout(active, time)
+  }
+
+  function active() {
+    isActive = true
+    addOrRemoveEvents('addEventListener')
+  }
+
+  function check() {
+    console.log('idleTime check 被调用', isActive, options.idleTime)
+    if (isActive) {
+      // 需要刷新token
+      isActive = false
+      options.callback?.()
+    }
+  }
+
+  function start() {
+    isActive = false
+    enableListener()
+  }
+
+  function destroy() {
+    clearTimeout(timer)
+    addOrRemoveEvents('removeEventListener')
+  }
+
+  return {
+    start: start,
+    destroy: destroy,
+  }
+}
+
+const { start, destroy } = idleTimer()
+
+function setOptions(params = {}) {
+  Object.assign(options, params)
+  return start
+}
+
+export {
+  setOptions,
+  destroy,
+}

+ 122 - 0
src/utils/request.ts

@@ -0,0 +1,122 @@
+import axios from 'axios'
+import { showFailToast, showToast } from 'vant'
+import {getToken} from '@/utils/auth'
+import { triggerNativeLogin } from '@/hooks/useLogin'
+
+
+const errorCode: any = {
+  '401': '认证失败,无法访问系统资源',
+  '403': '当前操作没有权限',
+  '404': '访问资源不存在',
+  'default': '系统未知错误,请反馈给管理员',
+}
+
+const tokenFailureHandler = () => {
+  triggerNativeLogin()
+}
+
+
+axios.defaults.headers['Content-Type'] = 'application/json;'
+// axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
+// 创建axios实例
+const service = axios.create({
+  baseURL: import.meta.env.VITE_API_BASE_URL,
+  // 超时
+  timeout: 3 * 60 * 1000,
+})
+
+// request拦截器
+service.interceptors.request.use(
+  (config) => {
+    // 是否需要设置 token
+    const isToken = (config.headers || {}).isToken === false
+    const token = getToken()
+    if (token && !isToken) {
+      config.headers['Authorization'] = 'Bearer ' + token // 让每个请求携带自定义token 请根据实际情况自行修改
+    }
+    if (config.method === 'get' && config.params) {
+      let url = config.url + '?'
+      for (const propName of Object.keys(config.params)) {
+        const value = config.params[propName]
+        var part = encodeURIComponent(propName) + '='
+        if (value !== null && typeof value !== 'undefined') {
+          if (typeof value === 'object') {
+            for (const key of Object.keys(value)) {
+              let params = propName + '[' + key + ']'
+              var subPart = encodeURIComponent(params) + '='
+              url += subPart + encodeURIComponent(value[key]) + '&'
+            }
+          } else {
+            url += part + encodeURIComponent(value) + '&'
+          }
+        }
+      }
+      url = url.slice(0, -1)
+      config.params = {}
+      config.url = url
+    }
+    return config
+  },
+  (error) => {
+    console.log(error)
+    Promise.reject(error)
+  }
+)
+
+// 响应拦截器
+service.interceptors.response.use(
+  (res) => {
+    // 未设置状态码则默认成功状态
+    const code = res.data.code || 200
+    // 获取错误信息
+    const msg = errorCode[code] || res.data.msg || errorCode['default']
+    if (code === 401) {
+      tokenFailureHandler()
+    } else if (code === 500) {
+      showFailToast(msg)
+      return Promise.reject(new Error(msg))
+    } else if (code !== 200) {
+      showFailToast(msg)
+      return Promise.reject('error')
+    } else {
+      return res.data
+    }
+  },
+  (error) => {
+    const data = error.response.data
+    let { message } = data
+    
+
+    const codeStart = String(data.code).substring(0, 2)
+
+    if (codeStart === '10') {
+      // Token 认证错误,需要重新登录获取token
+      // TODO 
+      tokenFailureHandler()
+    }
+    
+
+    if (message == 'Network Error') {
+      message = 'Back end interface connection exception'
+    } else if (message.includes('timeout')) {
+      message = 'System interface request timeout'
+    } else if (message.includes('Request failed with status code')) {
+      message = `System interface: ${message.substr(
+        message.length - 3
+      )};`
+    }
+    
+    setTimeout(() => {
+      showToast({
+        message,
+        duration: 3000,
+        // wordBreak: 'break-word',
+        position: 'top',
+      })      
+    })
+    return Promise.reject(data)
+  }
+)
+
+
+export default service

+ 182 - 68
src/views/Home.vue

@@ -7,16 +7,16 @@
             <!-- 步骤1: 输入食材 -->
             <div class="mb-6">
                 <div class="bg-pink-400 text-white px-4 py-2 rounded-t-lg border-2 border-[#0A0910] border-b-0 inline-block">
-                    <span class="font-bold">1. 输入食材</span>
+                    <span class="font-bold">{{ $t('home.step1.title') }}</span>
                 </div>
                 <div class="bg-white border-2 border-[#0A0910] rounded-lg rounded-tl-none p-4 md:p-6 md:pb-10">
                     <div class="text-center mb-6">
                         <div class="w-16 h-16 bg-black rounded-lg flex items-center justify-center mx-auto mb-4">
                             <span class="text-white text-2xl">🥬</span>
                         </div>
-                        <h2 class="text-2xl font-bold text-dark-800 mb-2">添加食材</h2>
-                        <p class="text-gray-600">输入你现有的食材,按回车添加</p>
-                        <p class="text-xs text-gray-500 mt-1">支持蔬菜、肉类、调料等 (最多10种)</p>
+                        <h2 class="text-2xl font-bold text-dark-800 mb-2">{{ $t('home.step1.addIngredients.title') }}</h2>
+                        <p class="text-gray-600">{{ $t('home.step1.addIngredients.description') }}</p>
+                        <p class="text-xs text-gray-500 mt-1">{{ $t('home.step1.addIngredients.hint') }}</p>
                     </div>
 
                     <!-- 食材输入区域 -->
@@ -40,7 +40,7 @@
                             <input
                                 v-model="currentIngredient"
                                 @keyup.enter="addIngredient"
-                                placeholder="输入食材名称,按回车添加..."
+                                :placeholder="$t('home.step1.addIngredients.placeholder')"
                                 class="w-full p-3 md:p-4 border-2 border-[#0A0910] rounded-lg text-sm md:text-lg font-medium focus:outline-none focus:ring-2 focus:ring-pink-400"
                             />
                         </div>
@@ -53,7 +53,7 @@
                             >
                                 <span class="flex items-center gap-2">
                                     <span class="text-base">🥬</span>
-                                    <span class="font-medium">快速选择食材</span>
+                                    <span class="font-medium">{{ $t('home.step1.addIngredients.quickPick') }}</span>
                                 </span>
                                 <span class="transform transition-transform duration-200 text-gray-400" :class="{ 'rotate-180': showIngredientPicker }">
                                     <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -96,8 +96,8 @@
 
                                 <!-- 底部状态栏 -->
                                 <div class="px-3 py-2 bg-gray-50 border-t border-gray-200 text-xs text-gray-500 flex justify-between items-center">
-                                    <span>点击食材快速添加到列表</span>
-                                    <span class="font-medium">已选择 {{ ingredients.length }}/10</span>
+                                    <span>{{ $t('home.step1.addIngredients.quickPickHint') }}</span>
+                                    <span class="font-medium">{{ $t('home.step1.addIngredients.selectedCount', { count: ingredients.length }) }}</span>
                                 </div>
                             </div>
                         </div>
@@ -110,19 +110,19 @@
                 <!-- 步骤2: 选择菜系 -->
                 <div>
                     <div class="bg-green-400 text-white px-4 py-2 rounded-t-lg border-2 border-[#0A0910] border-b-0 inline-block">
-                        <span class="font-bold">2. 选择菜系</span>
+                        <span class="font-bold">{{ $t('home.step2.title') }}</span>
                     </div>
                     <div class="bg-white border-2 border-[#0A0910] rounded-lg rounded-tl-none p-4 md:p-6 h-full">
                         <div>
                             <!-- 自定义要求提示 -->
                             <div v-if="customPrompt.trim()" class="mb-4 p-3 bg-blue-50 border-2 border-blue-200 rounded-lg text-center">
-                                <p class="text-sm text-blue-700 mb-2">✓ 已设置自定义要求,将优先使用自定义要求生成菜谱</p>
-                                <button @click="clearCustomPrompt" class="text-blue-600 hover:text-blue-700 underline text-sm">清除自定义要求以选择菜系</button>
+                                <p class="text-sm text-blue-700 mb-2">✓ {{ $t('home.step3.hints.withCustom') }}</p>
+                                <button @click="clearCustomPrompt" class="text-blue-600 hover:text-blue-700 underline text-sm">{{ $t('home.step2.customRequest.clearButton') }}</button>
                             </div>
 
                             <!-- 中华八大菜系 -->
                             <div class="mb-4" :class="{ 'opacity-50': customPrompt.trim() }">
-                                <h5 class="text-xs font-bold text-gray-700 mb-2 flex items-center gap-1">🇨🇳 中华八大菜系</h5>
+                                <h5 class="text-xs font-bold text-gray-700 mb-2 flex items-center gap-1">🇨🇳 {{ $t('home.step2.cuisines.chinese') }}</h5>
                                 <div class="grid grid-cols-3 gap-2">
                                     <button
                                         v-for="cuisine in cuisines.slice(0, 8)"
@@ -134,14 +134,14 @@
                                         ]"
                                     >
                                         <span>{{ cuisine.avatar }}</span>
-                                        <span>{{ cuisine.name.replace('大师', '') }}</span>
+                                        <span>{{ cuisine.name.replace($t('home.step2.cuisines.masterSuffix'), '') }}</span>
                                     </button>
                                 </div>
                             </div>
 
                             <!-- 国际菜系 -->
                             <div class="mb-6" :class="{ 'opacity-50': customPrompt.trim() }">
-                                <h5 class="text-xs font-bold text-gray-700 mb-2 flex items-center gap-1">🌍 国际菜系</h5>
+                                <h5 class="text-xs font-bold text-gray-700 mb-2 flex items-center gap-1">🌍 {{ $t('home.step2.cuisines.international') }}</h5>
                                 <div class="grid grid-cols-3 gap-2">
                                     <button
                                         v-for="cuisine in cuisines.slice(8)"
@@ -153,7 +153,7 @@
                                         ]"
                                     >
                                         <span>{{ cuisine.avatar }}</span>
-                                        <span>{{ cuisine.name.replace('料理大师', '').replace('大师', '') }}</span>
+                                        <span>{{ cuisine.name.replace($t('home.step2.cuisines.internationalSuffix1'), '').replace($t('home.step2.cuisines.masterSuffix'), '') }}</span>
                                     </button>
                                 </div>
                             </div>
@@ -168,8 +168,8 @@
                                 >
                                     <span class="flex items-center gap-2">
                                         <span class="text-base">💭</span>
-                                        <span class="font-medium">或自定义要求</span>
-                                        <span v-if="customPrompt.trim()" class="text-xs bg-blue-500 text-white px-2 py-1 rounded-full">已设置</span>
+                                        <span class="font-medium">{{ $t('home.step2.customRequest.title') }}</span>
+                                        <span v-if="customPrompt.trim()" class="text-xs bg-blue-500 text-white px-2 py-1 rounded-full">{{ $t('home.step2.customRequest.set') }}</span>
                                     </span>
                                     <span class="transform transition-transform duration-200 text-gray-400" :class="{ 'rotate-180': showCustomPrompt }">
                                         <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -188,7 +188,7 @@
                                         >
                                             <span class="flex items-center gap-2">
                                                 <span class="text-base">⚡</span>
-                                                <span class="font-medium">快速预设</span>
+                                                <span class="font-medium">{{ $t('home.step2.customRequest.quickPresets') }}</span>
                                             </span>
                                             <span class="transform transition-transform duration-200 text-gray-400" :class="{ 'rotate-180': showPresetPicker }">
                                                 <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -200,7 +200,7 @@
                                         <div v-if="showPresetPicker" class="space-y-2 mb-3 p-2 bg-white/70 rounded-lg border border-blue-200 shadow-sm max-h-40 overflow-y-auto">
                                             <!-- 场景预设 -->
                                             <div>
-                                                <h6 class="text-xs font-medium text-gray-700 mb-1 flex items-center gap-1">🎯 场景需求</h6>
+                                                <h6 class="text-xs font-medium text-gray-700 mb-1 flex items-center gap-1">🎯 {{ $t('home.step2.customRequest.scenePresets') }}</h6>
                                                 <div class="flex flex-wrap gap-1">
                                                     <button
                                                         v-for="preset in scenePresets"
@@ -215,7 +215,7 @@
 
                                             <!-- 口味偏好 -->
                                             <div>
-                                                <h6 class="text-xs font-medium text-gray-700 mb-1 flex items-center gap-1">👅 口味偏好</h6>
+                                                <h6 class="text-xs font-medium text-gray-700 mb-1 flex items-center gap-1">👅 {{ $t('home.step2.customRequest.tastePresets') }}</h6>
                                                 <div class="flex flex-wrap gap-1">
                                                     <button
                                                         v-for="preset in tastePresets"
@@ -232,17 +232,17 @@
 
                                     <!-- 自定义输入区域 -->
                                     <div>
-                                        <label class="block text-sm font-bold text-blue-800 mb-2">自由描述:</label>
+                                        <label class="block text-sm font-bold text-blue-800 mb-2">{{ $t('home.step2.customRequest.freeDescription') }}</label>
                                         <textarea
                                             v-model="customPrompt"
                                             @input="limitCustomPrompt"
-                                            placeholder="例如:做一道清淡的汤,适合老人食用,不要太咸..."
+                                            :placeholder="$t('home.step2.customRequest.placeholder')"
                                             class="w-full p-2 border-2 border-blue-300 rounded-lg text-sm resize-none focus:outline-none focus:border-blue-500 h-20"
                                             maxlength="200"
                                         ></textarea>
                                         <div v-if="customPrompt.trim()" class="mt-1 flex justify-between items-center">
-                                            <span class="text-xs text-green-600">✓ 已设置自定义要求</span>
-                                            <button @click="customPrompt = ''" class="text-xs text-red-600 hover:text-red-700 underline">清除</button>
+                                            <span class="text-xs text-green-600">✓ {{ $t('home.step2.customRequest.set') }}</span>
+                                            <button @click="customPrompt = ''" class="text-xs text-red-600 hover:text-red-700 underline">{{ $t('home.step2.customRequest.clear') }}</button>
                                         </div>
                                     </div>
 
@@ -252,14 +252,14 @@
                                             @click="getRandomInspiration"
                                             class="w-full py-1.5 px-2 bg-gradient-to-r from-purple-400 to-pink-400 hover:from-purple-500 hover:to-pink-500 text-white text-sm font-medium rounded-lg border-2 border-[#0A0910] transition-all duration-200 transform"
                                         >
-                                            ✨ 随机灵感
+                                            ✨ {{ $t('home.step2.customRequest.randomInspiration') }}
                                         </button>
                                     </div>
 
                                     <!-- 底部提示 -->
                                     <div class="mt-2 pt-2 border-t border-blue-200">
                                         <div class="flex items-center justify-between text-xs text-blue-600">
-                                            <span>💡 提示:越具体越好!</span>
+                                            <span>💡 {{ $t('home.step2.customRequest.tip') }}</span>
                                             <span :class="{ 'text-red-500': customPrompt.length > 180 }">{{ customPrompt.length }}/200</span>
                                         </div>
                                     </div>
@@ -272,37 +272,37 @@
                 <!-- 步骤3: 交给大师 -->
                 <div class="max-sm:mt-10">
                     <div class="bg-orange-400 text-white px-4 py-2 rounded-t-lg border-2 border-[#0A0910] border-b-0 inline-block">
-                        <span class="font-bold">3. 交给大师</span>
+                        <span class="font-bold">{{ $t('home.step3.title') }}</span>
                     </div>
                     <div class="bg-white border-2 border-[#0A0910] rounded-lg rounded-tl-none p-4 md:p-6 h-full">
                         <div class="text-center h-full flex flex-col">
                             <div class="w-16 h-16 bg-gradient-to-br from-orange-400 to-red-500 rounded-lg flex items-center justify-center mx-auto mb-4">
                                 <span class="text-white text-2xl">👨‍🍳</span>
                             </div>
-                            <h2 class="text-xl font-bold text-dark-800 mb-2">准备开始烹饪</h2>
-                            <p class="text-gray-600 mb-4 text-sm">大师已准备就绪,点击按钮开始创作美味佳肴</p>
+                            <h2 class="text-xl font-bold text-dark-800 mb-2">{{ $t('home.step3.readyToCook') }}</h2>
+                            <p class="text-gray-600 mb-4 text-sm">{{ $t('home.step3.masterReady') }}</p>
 
                             <!-- 当前配置预览 -->
                             <div class="bg-gray-50 rounded-lg p-3 mb-4 text-left flex-1">
                                 <h3 class="font-bold text-sm text-gray-700 mb-2 flex items-center gap-2">
                                     <span>📋</span>
-                                    <span>当前配置</span>
+                                    <span>{{ $t('home.step3.currentConfig') }}</span>
                                 </h3>
 
                                 <!-- 食材列表 -->
                                 <div class="mb-2">
-                                    <span class="text-xs font-medium text-gray-600">食材 ({{ ingredients.length }}):</span>
+                                    <span class="text-xs font-medium text-gray-600">{{ $t('home.step3.ingredientsLabel') }} ({{ ingredients.length }}):</span>
                                     <div v-if="ingredients.length > 0" class="flex flex-wrap gap-1 mt-1">
                                         <span v-for="ingredient in ingredients" :key="ingredient" class="inline-block bg-yellow-200 text-yellow-800 px-2 py-1 rounded text-xs">
                                             {{ ingredient }}
                                         </span>
                                     </div>
-                                    <span v-else class="text-xs text-gray-400">未添加食材</span>
+                                    <span v-else class="text-xs text-gray-400">{{ $t('home.step3.noIngredients') }}</span>
                                 </div>
 
                                 <!-- 菜系和大师选择 -->
                                 <div class="mb-2">
-                                    <span class="text-xs font-medium text-gray-600">菜系大师 ({{ selectedCuisines.length }}):</span>
+                                    <span class="text-xs font-medium text-gray-600">{{ $t('home.step3.cuisinesLabel') }} ({{ selectedCuisines.length }}):</span>
                                     <div v-if="selectedCuisines.length > 0 && !customPrompt.trim()" class="mt-1">
                                         <div
                                             v-for="cuisineId in selectedCuisines"
@@ -313,13 +313,13 @@
                                             <span>{{ cuisines.find(c => c.id === cuisineId)?.name }}</span>
                                         </div>
                                     </div>
-                                    <span v-else-if="!customPrompt.trim()" class="text-xs text-gray-400">未选择大师</span>
-                                    <span v-else class="text-xs text-blue-600">使用自定义要求</span>
+                                    <span v-else-if="!customPrompt.trim()" class="text-xs text-gray-400">{{ $t('home.step3.noCuisines') }}</span>
+                                    <span v-else class="text-xs text-blue-600">{{ $t('home.step3.usingCustomRequest') }}</span>
                                 </div>
 
                                 <!-- 自定义要求 -->
                                 <div v-if="customPrompt.trim()">
-                                    <span class="text-xs font-medium text-gray-600">自定义要求:</span>
+                                    <span class="text-xs font-medium text-gray-600">{{ $t('home.step3.customRequestLabel') }}:</span>
                                     <p class="text-xs text-blue-700 mt-1 bg-blue-50 p-2 rounded">
                                         {{ customPrompt.length > 50 ? customPrompt.substring(0, 50) + '...' : customPrompt }}
                                     </p>
@@ -335,21 +335,21 @@
                                 <span class="flex items-center gap-2 justify-center">
                                     <template v-if="isLoading">
                                         <div class="animate-spin w-5 h-5 border-2 border-white border-t-transparent rounded-full"></div>
-                                        <span v-if="recipes.length === 0">生成中...</span>
+                                        <span v-if="recipes.length === 0">{{ $t('home.step3.generateButton.loading') }}</span>
                                         <span v-else>{{ loadingText }}</span>
                                     </template>
                                     <template v-else>
                                         <span class="text-xl">✨</span>
-                                        <span>{{ customPrompt.trim() ? '按要求生成' : '交给大师' }}</span>
+                                        <span>{{ customPrompt.trim() ? $t('home.step3.generateButton.withCustom') : $t('home.step3.generateButton.default') }}</span>
                                     </template>
                                 </span>
                             </button>
 
                             <!-- 提示信息 -->
                             <div class="text-sm">
-                                <p v-if="customPrompt.trim()" class="text-blue-600">🎯 将根据您的自定义要求生成菜谱</p>
-                                <p v-else-if="selectedCuisines.length > 0" class="text-green-600">🍽️ 将生成 {{ selectedCuisines.length }} 个菜系的菜谱</p>
-                                <p class="text-xs text-gray-500 mt-1">大师将为您精心设计菜谱流程</p>
+                                <p v-if="customPrompt.trim()" class="text-blue-600">🎯 {{ $t('home.step3.hints.withCustom') }}</p>
+                                <p v-else-if="selectedCuisines.length > 0" class="text-green-600">🍽️ {{ $t('home.step3.hints.withCuisines', { count: selectedCuisines.length }) }}</p>
+                                <p class="text-xs text-gray-500 mt-1">{{ $t('home.step3.hints.default') }}</p>
                             </div>
                         </div>
                     </div>
@@ -359,7 +359,7 @@
             <!-- 步骤4: 菜谱结果 -->
             <div ref="resultsSection" class="mt-16">
                 <div class="bg-dark-800 text-white px-4 py-2 rounded-t-lg border-2 border-[#0A0910] border-b-0 inline-block">
-                    <span class="font-bold">4. 菜谱结果</span>
+                    <span class="font-bold">{{ $t('home.step4.title') }}</span>
                 </div>
                 <div class="bg-white border-2 border-[#0A0910] rounded-lg rounded-tl-none p-2 md:p-6">
                     <!-- 移除这个整体加载状态,因为我们现在使用菜系模块加载 -->
@@ -386,15 +386,15 @@
                                             <div class="flex-1">
                                                 <h3 class="text-lg font-bold mb-1 flex items-center gap-2">
                                                     <span class="animate-bounce">😅</span>
-                                                    {{ cuisineInfo.name }}不会这道菜,哈哈
+                                                    {{ $t('home.step4.error.title') }}
                                                 </h3>
                                                 <div class="flex items-center gap-3 text-sm">
                                                     <span class="bg-white/20 px-2 py-1 rounded text-xs">{{ cuisineInfo.name }}</span>
                                                     <span class="flex items-center gap-1">
                                                         <span>😓</span>
-                                                        技能点不够
+                                                        {{ $t('home.step4.error.skillIssue') }}
                                                     </span>
-                                                    <span>🎯 开小差了</span>
+                                                    <span>🎯 {{ $t('home.step4.error.slacking') }}</span>
                                                 </div>
                                             </div>
                                             <div class="text-2xl ml-2">🤷‍♂️</div>
@@ -407,17 +407,17 @@
                                             <div class="w-16 h-16 bg-orange-100 rounded-lg flex items-center justify-center mx-auto mb-4">
                                                 <span class="text-orange-500 text-2xl">🤔</span>
                                             </div>
-                                            <h4 class="text-lg font-bold text-gray-800 mb-2">大师表示很为难</h4>
-                                            <p class="text-gray-600 text-sm mb-4">{{ cuisineInfo.name }}看了看你的食材,挠了挠头说:"这个组合我还没学会呢!"</p>
+                                            <h4 class="text-lg font-bold text-gray-800 mb-2">{{ $t('home.step4.error.title') }}</h4>
+                                            <p class="text-gray-600 text-sm mb-4">{{ $t('home.step4.error.description', { name: cuisineInfo.name }) }}</p>
                                         </div>
 
                                         <!-- 建议区域 -->
                                         <div class="bg-yellow-50 border-2 border-yellow-200 rounded-lg p-4 mb-4">
-                                            <h5 class="text-sm font-bold text-yellow-800 mb-2 flex items-center gap-1 justify-center">💡 大师的建议</h5>
+                                            <h5 class="text-sm font-bold text-yellow-800 mb-2 flex items-center gap-1 justify-center">💡 {{ $t('home.step4.error.suggestionsTitle') }}</h5>
                                             <div class="text-xs text-yellow-700 space-y-1">
-                                                <p>• 试试其他菜系大师,他们可能有不同的想法</p>
-                                                <p>• 调整一下食材搭配,或许会有惊喜</p>
-                                                <p>• 使用自定义要求,给大师一些灵感</p>
+                                                <p>• {{ $t('home.step4.error.suggestion1') }}</p>
+                                                <p>• {{ $t('home.step4.error.suggestion2') }}</p>
+                                                <p>• {{ $t('home.step4.error.suggestion3') }}</p>
                                             </div>
                                         </div>
 
@@ -426,7 +426,7 @@
                                             @click="retryFailedCuisine(cuisineInfo)"
                                             class="bg-gradient-to-r from-orange-500 to-red-500 hover:from-orange-600 hover:to-red-600 text-white px-4 py-2 rounded-lg font-medium text-sm border-2 border-[#0A0910] transition-all duration-200 transform hover:scale-105"
                                         >
-                                            🔄 再试一次
+                                            🔄 {{ $t('home.step4.error.retryButton') }}
                                         </button>
                                     </div>
                                 </div>
@@ -439,17 +439,17 @@
                                             <div class="flex-1">
                                                 <h3 class="text-lg font-bold mb-1 flex items-center gap-2">
                                                     <span class="animate-pulse">👨‍🍳</span>
-                                                    {{ cuisineInfo.name }}创作中...
+                                                    {{ $t('home.step4.loading.masterCreating', { name: cuisineInfo.name }) }}
                                                 </h3>
                                                 <!-- 移动端:垂直布局 -->
                                                 <div class="flex flex-col gap-1 md:hidden">
                                                     <div class="flex items-center gap-2">
                                                         <span class="bg-white/20 px-2 py-1 rounded text-xs animate-pulse">{{ cuisineInfo.name }}</span>
-                                                        <span class="text-xs">📊 精心制作</span>
+                                                        <span class="text-xs">📊 {{ $t('home.step4.loading.carefullyCrafted') }}</span>
                                                     </div>
                                                     <div class="flex items-center gap-1 text-xs">
                                                         <span class="animate-spin">⏱️</span>
-                                                        <span>预计10-20秒</span>
+                                                        <span>{{ $t('home.step4.loading.estimatedTime') }}</span>
                                                     </div>
                                                 </div>
                                                 <!-- 桌面端:水平布局 -->
@@ -457,9 +457,9 @@
                                                     <span class="bg-white/20 px-2 py-1 rounded text-xs animate-pulse">{{ cuisineInfo.name }}</span>
                                                     <span class="flex items-center gap-1">
                                                         <span class="animate-spin">⏱️</span>
-                                                        预计10-20秒
+                                                        {{ $t('home.step4.loading.estimatedTime') }}
                                                     </span>
-                                                    <span>📊 精心制作</span>
+                                                    <span>📊 {{ $t('home.step4.loading.carefullyCrafted') }}</span>
                                                 </div>
                                             </div>
                                             <div class="text-2xl ml-2 animate-bounce">⏳</div>
@@ -470,7 +470,7 @@
                                     <div class="p-4 md:p-6">
                                         <!-- 食材预览 -->
                                         <div class="mb-4">
-                                            <h4 class="text-sm font-bold text-dark-800 mb-2 flex items-center gap-1">🥬 使用食材</h4>
+                                            <h4 class="text-sm font-bold text-dark-800 mb-2 flex items-center gap-1">🥬 {{ $t('home.step4.loading.usedIngredients') }}</h4>
                                             <div class="flex flex-wrap gap-1">
                                                 <span
                                                     v-for="ingredient in ingredients"
@@ -484,7 +484,7 @@
 
                                         <!-- 步骤预览骨架 -->
                                         <div class="mb-4">
-                                            <h4 class="text-sm font-bold text-dark-800 mb-2 flex items-center gap-1">📝 制作步骤</h4>
+                                            <h4 class="text-sm font-bold text-dark-800 mb-2 flex items-center gap-1">📝 {{ $t('home.step4.loading.stepsPreview') }}</h4>
                                             <div class="space-y-2">
                                                 <div v-for="i in 3" :key="i" class="flex gap-2 p-2 bg-gray-50 rounded border border-gray-200">
                                                     <div class="flex-shrink-0 w-5 h-5 bg-gray-300 rounded shimmer-effect"></div>
@@ -499,7 +499,7 @@
                                         <!-- 生成状态 -->
                                         <div class="text-center py-6 bg-gradient-to-br from-orange-50 to-yellow-50 rounded-lg border-2 border-dashed border-orange-200">
                                             <div class="w-16 h-16 border-4 border-orange-200 border-t-orange-500 rounded-full animate-spin mx-auto mb-4"></div>
-                                            <h3 class="text-lg font-bold text-dark-800 mb-2">{{ cuisineInfo.name }}正在创作中...</h3>
+                                            <h3 class="text-lg font-bold text-dark-800 mb-2">{{ $t('home.step4.loading.masterCreating', { name: cuisineInfo.name }) }}</h3>
                                             <p class="text-gray-600 text-sm mb-3">{{ cuisineInfo.loadingText || loadingText }}</p>
 
                                             <!-- 进度条 -->
@@ -512,7 +512,7 @@
                                                         <div class="absolute inset-0 bg-white/30 animate-pulse"></div>
                                                     </div>
                                                 </div>
-                                                <p class="text-xs text-gray-500 mt-2">{{ Math.round(cuisineInfo.progress) }}% 完成</p>
+                                                <p class="text-xs text-gray-500 mt-2">{{ Math.round(cuisineInfo.progress) }}% {{ $t('home.step4.loading.completed') }}</p>
                                             </div>
 
                                             <div class="mt-4 flex justify-center items-center gap-1 text-xs text-gray-500">
@@ -532,14 +532,14 @@
                         <div class="w-16 h-16 bg-red-100 rounded-lg flex items-center justify-center mx-auto mb-4">
                             <span class="text-red-500 text-2xl">⚠️</span>
                         </div>
-                        <h3 class="text-xl font-bold text-red-600 mb-2">生成失败</h3>
+                        <h3 class="text-xl font-bold text-red-600 mb-2">{{ $t('home.step4.error.generalTitle') }}</h3>
                         <p class="text-red-500 mb-4">{{ errorMessage }}</p>
                         <button
                             @click="generateRecipes"
                             :disabled="ingredients.length === 0"
                             class="bg-red-500 hover:bg-red-600 disabled:bg-gray-400 text-white px-6 py-2 rounded-lg font-medium border-2 border-[#0A0910] transition-all duration-200 disabled:cursor-not-allowed"
                         >
-                            🔄 重新生成
+                            🔄 {{ $t('home.step4.error.retryButton') }}
                         </button>
                     </div>
 
@@ -548,8 +548,8 @@
                         <div class="w-16 h-16 bg-gray-200 rounded-lg flex items-center justify-center mx-auto mb-4">
                             <span class="text-gray-400 text-2xl">⭐</span>
                         </div>
-                        <h3 class="text-xl font-bold text-gray-400 mb-2">等待魔法发生...</h3>
-                        <p class="text-gray-500">添加食材并选择菜系开始创作</p>
+                        <h3 class="text-xl font-bold text-gray-400 mb-2">{{ $t('home.step4.emptyState.title') }}</h3>
+                        <p class="text-gray-500">{{ $t('home.step4.emptyState.description') }}</p>
                     </div>
 
                     <!-- 传统菜谱结果显示 - 只在没有使用槽位系统时显示 -->
@@ -620,14 +620,26 @@
 </style>
 
 <script setup lang="ts">
-import { ref, onUnmounted } from 'vue'
-import { cuisines } from '@/config/cuisines'
-import { ingredientCategories } from '@/config/ingredients'
+import { ref, computed, onUnmounted, onMounted } from 'vue'
+import { useI18n } from 'vue-i18n'
+import { getCuisines } from '@/config/cuisines'
+import { getIngredientCategories } from '@/config/ingredients'
 import RecipeCard from '@/components/RecipeCard.vue'
 import GlobalNavigation from '@/components/GlobalNavigation.vue'
 import GlobalFooter from '@/components/GlobalFooter.vue'
 import { generateCustomRecipe, generateMultipleRecipesStream, generateRecipe } from '@/services/aiService'
 import type { Recipe, CuisineType } from '@/types'
+import { androidLoginHandler, appleLoginHandler } from '@/hooks/useLogin'
+import { showToast } from 'vant'
+import { getExpiresIn, setExpiresIn, setToken, setUserInfo } from '@/utils/auth'
+import { setOptions, destroy as idleTimerDestroy } from '@/utils/idleTimer'
+import { getProfile, refreshToken } from '@/api'
+
+const { t } = useI18n()
+
+// 使用国际化获取食材分类
+const ingredientCategories = computed(() => getIngredientCategories(t))
+const cuisines = computed(() => getCuisines(t))
 
 // 响应式数据
 const ingredients = ref<string[]>([])
@@ -707,6 +719,108 @@ const tastePresets = [
 //     { id: 'grill', name: '烧烤', prompt: '烧烤方式制作,香气四溢,口感独特' }
 // ]
 
+const eventHandler = (event: any) => {
+    try {
+      const data = typeof event.data === 'string' ? JSON.parse(event.data) : event.data;
+      
+      const type = data?.type
+      if (['LOGIN_SUCCESS_ANDROID', 'LOGIN_SUCCESS_IOS'].includes(type)) {
+        loginHandler(data)
+      }
+      
+    } catch (error) {
+      console.error('解析消息时出错:', error);
+    }
+}
+// 空闲监听
+let idleTimer: any
+const loginHandler = (data: any) => {
+    const type = data?.type
+    console.log('原生登录成功, h5收到请求数', data.data);
+    let api
+    if (type === 'LOGIN_SUCCESS_ANDROID') {
+      
+      api = androidLoginHandler
+      } else if (type === 'LOGIN_SUCCESS_IOS') {
+      api = appleLoginHandler
+    }
+    if (api) {
+      api(data.data).then(res => getTokenHandler(res, true)).then()
+    }
+}
+
+function getTokenHandler(resData: any, showTips?: boolean) {
+    console.log('getTokenHandler', resData)
+    const { token, user } = resData
+
+    idleTimer && idleTimerDestroy()
+
+    setToken(token)
+    // 真实失有效时间时24小时,提前一小时失效
+    const hours = 60 * 60 * 1000
+    const expiresTime = Date.now() + 23 * hours
+    setExpiresIn(expiresTime)
+    setUserInfo(user)
+    // setOptions 返回 start 函数,需要调用才能启动空闲检测
+    idleTimer = setOptions(
+        { 
+          idleTime: hours * 8,
+          callback: () => {
+            console.log('用户空闲8小时')
+            refreshTokenHandler()
+          }
+      },
+    )
+    idleTimer()  // 启动空闲检测
+    showTips && showToast('登录成功')
+}
+
+const refreshTokenHandler = () => {
+    refreshToken().then(res => getTokenHandler(res)).catch(err => {
+      console.error('刷新令牌失败:', err)
+    })
+}
+
+const addEventListener = () => {
+  // 在 H5 页面中添加以下代码
+  window.addEventListener('message', eventHandler);
+}
+
+onMounted(() => {
+    checkToken()
+    addEventListener()
+    mockLogin()
+})
+
+const checkToken = () => {
+  const expires = getExpiresIn()
+  if (expires && Date.now() < expires) {
+    refreshTokenHandler()
+  }
+}
+
+const mockLogin = () => {
+    const mock = { user: '001746.b066d194b82f46eaac8f3f26b9a915fc.0901',
+  realUserStatus: 1,
+  authorizedScopes: [],
+  identityToken: 'eyJraWQiOiJiRnd6bGVSOHRmIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL2FwcGxlaWQuYXBwbGUuY29tIiwiYXVkIjoiY29tLmlwZWFraW5nLnJlY2lwZW11c2UiLCJleHAiOjE3NjI1OTQxMDQsImlhdCI6MTc2MjUwNzcwNCwic3ViIjoiMDAxNzQ2LmIwNjZkMTk0YjgyZjQ2ZWFhYzhmM2YyNmI5YTkxNWZjLjA5MDEiLCJub25jZSI6ImUxOWFhMWFkM2IxNzk0OGFhOTkyMzIzMmYwYzY4ZmI3ZDY5NWY4NWVlM2YyMWJjZTA0YWY5OTMyMjI0ZmM4NzEiLCJjX2hhc2giOiJrWGxDWlJDSFBLbW5xZlNuTERxd01RIiwiZW1haWwiOiIyNzYyMDM4NzZAcXEuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImF1dGhfdGltZSI6MTc2MjUwNzcwNCwibm9uY2Vfc3VwcG9ydGVkIjp0cnVlfQ.OFZqwTExm4N_D-CAg-mphRQ7qWLdTwlOCma7BGLV_NW01vrh3LmgxHC3Um4yH7MH_w8mTBXRijG3RNAtMHcxroTbydJhpj0lyHSgdNy_Sksxnj9NgZTajvx0090stP2YnIPlBNWri09qP3dRWwo-YhvKyJRA2w5kxd3pyAJ0HtDyhV3PtDVvWCcrPGQ7CnnhVLM461ea-bs_xyKvIIGNCsLb9qnE_Dqwq1hRq7e_u-p4mHDob-jLDRk4P1fy290DjKfQWhM115ii4T3zID40bFoylBZ49EnuaWLdxYTYudysIi39OSDWCwrstgMWZo2bOgaaLvaqEZOth-gEkCkdIA',
+  authorizationCode: 'cc6c6972bec414c01b04732d9677d37c3.0.prxuw.ePi8C2SuDjDZ4jrvyebo2g',
+  fullName: 
+   { namePrefix: null,
+     givenName: null,
+     nameSuffix: null,
+     middleName: null,
+     familyName: null,
+     nickname: null },
+  email: null,
+  state: null,
+  nonce: '2fZOABYScdFd7GyZysVJHCekXTEEXFP6' }
+    loginHandler({
+        type: 'LOGIN_SUCCESS_IOS',
+        data: mock
+    })
+}
+
 // 添加食材
 const addIngredient = () => {
     const ingredient = currentIngredient.value.trim()

+ 14 - 7
src/views/TodayEat.vue

@@ -171,14 +171,21 @@
 
 <script setup lang="ts">
 import { ref, onMounted, onUnmounted, computed } from 'vue'
-import { cuisines } from '@/config/cuisines'
-import { ingredientCategories } from '@/config/ingredients'
+import { useI18n } from 'vue-i18n'
+import { getCuisines } from '@/config/cuisines'
+import { getIngredientCategories } from '@/config/ingredients'
 import type { Recipe, CuisineType } from '@/types'
 import { generateRecipe } from '@/services/aiService'
 import RecipeCard from '@/components/RecipeCard.vue'
 import GlobalNavigation from '@/components/GlobalNavigation.vue'
 import GlobalFooter from '@/components/GlobalFooter.vue'
 
+const { t } = useI18n()
+
+// 使用国际化获取食材分类
+const ingredientCategories = computed(() => getIngredientCategories(t))
+const cuisines = computed(() => getCuisines(t))
+
 // 状态管理
 const isSelecting = ref(false)
 const isGenerating = ref(false)
@@ -231,7 +238,7 @@ const allDishes = ref<string[]>([])
 
 // 初始化
 onMounted(() => {
-    allDishes.value = ingredientCategories.flatMap(category => category.items)
+    allDishes.value = ingredientCategories.value.flatMap(category => category.items)
     randomDice.value = diceEmojis[Math.floor(Math.random() * diceEmojis.length)]
 })
 
@@ -271,13 +278,13 @@ const selectRandomDishes = async () => {
         const vegCategories = ['vegetables', 'mushrooms', 'beans']
 
         if (preference.value === 'meat-heavy') {
-            filteredDishes = filteredDishes.filter(dish => ingredientCategories.some(cat => meatCategories.includes(cat.id) && cat.items.includes(dish)))
+            filteredDishes = filteredDishes.filter(dish => ingredientCategories.value.some(cat => meatCategories.includes(cat.id) && cat.items.includes(dish)))
         } else if (preference.value === 'veg-heavy') {
-            filteredDishes = filteredDishes.filter(dish => ingredientCategories.some(cat => vegCategories.includes(cat.id) && cat.items.includes(dish)))
+            filteredDishes = filteredDishes.filter(dish => ingredientCategories.value.some(cat => vegCategories.includes(cat.id) && cat.items.includes(dish)))
         } else if (preference.value === 'meat-only') {
-            filteredDishes = filteredDishes.filter(dish => ingredientCategories.some(cat => meatCategories.includes(cat.id) && cat.items.includes(dish)))
+            filteredDishes = filteredDishes.filter(dish => ingredientCategories.value.some(cat => meatCategories.includes(cat.id) && cat.items.includes(dish)))
         } else if (preference.value === 'veg-only') {
-            filteredDishes = filteredDishes.filter(dish => ingredientCategories.some(cat => vegCategories.includes(cat.id) && cat.items.includes(dish)))
+            filteredDishes = filteredDishes.filter(dish => ingredientCategories.value.some(cat => vegCategories.includes(cat.id) && cat.items.includes(dish)))
         }
     }
 

+ 8 - 1
vite.config.ts

@@ -10,7 +10,14 @@ export default defineConfig({
         }
     },
     server: {
-        host: '0.0.0.0'
+        host: '0.0.0.0',
+        proxy: {
+            '/api': {
+                target: 'https://recipe-muse.com:8443',
+                changeOrigin: true,
+                rewrite: (path) => path.replace(/^\/api/, '')
+            }
+        }
     },
     build: {
         outDir: 'dist',

BIN
原型页面/.~需求分析.docx


BIN
原型页面/一饭封神英文版.docx


+ 30 - 0
原型页面/方案设计.md

@@ -0,0 +1,30 @@
+
+## 登录
+
+### 接口
+
+/api/v1/auth/apple/login
+
+
+/api/v1/auth/refresh
+
+获取当前用户信息(需认证)
+/api/v1/auth/profile
+
+/api/v1/auth/logout
+
+
+### 逻辑
+
+需要
+
+在每次app每次加载的时候,刷新下token,在有效时间1/3处启动监听(时区不同不能用接口返回的过期时间)
+
+启动监听时间点:
+const idleTime = parseInt(res.data.expTime / 3)
+setOptions({ idleTime })
+
+失效时间点:
+将失效时间提前10分钟
+const expireTime = parseInt(res.data.expTime - 10 * 1000 * 6)
+通过对比超时时间来判断是否需要刷新token, 还是重新登录

BIN
原型页面/需求分析.docx


Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно