liuziting преди 7 месеца
родител
ревизия
3bd1571a88
променени са 4 файла, в които са добавени 339 реда и са изтрити 10 реда
  1. 30 9
      src/components/GlobalNavigation.vue
  2. 3 1
      src/main.ts
  3. 83 0
      src/services/aiService.ts
  4. 223 0
      src/views/HowToCook.vue

+ 30 - 9
src/components/GlobalNavigation.vue

@@ -1,6 +1,6 @@
 <template>
     <nav class="bg-white border-2 border-[#0A0910] max-w-7xl mx-auto rounded-lg mb-4 shadow-lg">
-        <div class="px-6 py-6">
+        <div class="px-4 py-6  md:px-6">
             <!-- 桌面端导航 -->
             <div class="hidden md:flex items-center justify-between">
                 <!-- Logo区域 -->
@@ -35,7 +35,7 @@
                         :class="$route.path === '/today-eat' ? 'bg-yellow-400 text-gray-800' : 'bg-gray-100 text-gray-700 hover:bg-gray-200'"
                     >
                         <span>🎲</span>
-                        <span>今天吃什么?</span>
+                        <span>今日吃啥</span>
                     </router-link>
                     <router-link
                         to="/table-design"
@@ -43,7 +43,15 @@
                         :class="$route.path === '/table-design' ? 'bg-yellow-400 text-gray-800' : 'bg-gray-100 text-gray-700 hover:bg-gray-200'"
                     >
                         <span>🍽️</span>
-                        <span>一桌菜设计</span>
+                        <span>一桌好菜</span>
+                    </router-link>
+                    <router-link
+                        to="/how-to-cook"
+                        class="flex items-center gap-2 px-4 py-2 rounded-lg font-bold border-2 border-[#0A0910] transition-all duration-200 transform hover:scale-105 text-sm"
+                        :class="$route.path === '/how-to-cook' ? 'bg-yellow-400 text-gray-800' : 'bg-gray-100 text-gray-700 hover:bg-gray-200'"
+                    >
+                        <span>🍳</span>
+                        <span>菜谱指南</span>
                     </router-link>
                     <router-link
                         to="/favorites"
@@ -67,7 +75,7 @@
                         :class="$route.path === '/about' ? 'bg-yellow-400 text-gray-800' : 'bg-gray-100 text-gray-700 hover:bg-gray-200'"
                     >
                         <span>📖</span>
-                        <span>简介</span>
+                        <span>关于我们</span>
                     </router-link>
                 </div>
             </div>
@@ -115,7 +123,7 @@
                         :class="$route.path === '/today-eat' ? 'bg-yellow-400 text-gray-800' : 'bg-gray-100 text-gray-700 hover:bg-gray-200'"
                     >
                         <span>🎲</span>
-                        <span>今天吃什么?</span>
+                        <span>今日吃啥</span>
                     </router-link>
                     <router-link
                         to="/table-design"
@@ -124,7 +132,16 @@
                         :class="$route.path === '/table-design' ? 'bg-yellow-400 text-gray-800' : 'bg-gray-100 text-gray-700 hover:bg-gray-200'"
                     >
                         <span>🍽️</span>
-                        <span>一桌菜设计</span>
+                        <span>一桌好菜</span>
+                    </router-link>
+                    <router-link
+                        to="/how-to-cook"
+                        @click="showMobileMenu = false"
+                        class="flex items-center gap-2 w-full px-3 py-2 rounded-lg font-bold border-2 border-[#0A0910] transition-all duration-200 text-sm"
+                        :class="$route.path === '/how-to-cook' ? 'bg-yellow-400 text-gray-800' : 'bg-gray-100 text-gray-700 hover:bg-gray-200'"
+                    >
+                        <span>🍳</span>
+                        <span>菜谱指南</span>
                     </router-link>
                     <router-link
                         to="/favorites"
@@ -151,7 +168,7 @@
                         :class="$route.path === '/about' ? 'bg-yellow-400 text-gray-800' : 'bg-gray-100 text-gray-700 hover:bg-gray-200'"
                     >
                         <span>📖</span>
-                        <span>简介</span>
+                        <span>关于我们</span>
                     </router-link>
                 </div>
             </div>
@@ -180,9 +197,11 @@ const pageTitle = computed(() => {
         case '/':
             return '一饭封神'
         case '/today-eat':
-            return '今天吃什么?'
+            return '今日吃啥'
         case '/table-design':
-            return '一桌菜设计师'
+            return '一桌好菜师'
+        case '/how-to-cook':
+            return '菜谱指南'
         case '/favorites':
             return '我的收藏'
         case '/gallery':
@@ -202,6 +221,8 @@ const pageSubtitle = computed(() => {
             return "盲盒美食:'绝了!' or '寄了!'"
         case '/table-design':
             return '让每顿饭,都有剧本!'
+        case '/how-to-cook':
+            return 'AI大师手把手教学!'
         case '/favorites':
             return '珍藏美味,随时回味!'
         case '/gallery':

+ 3 - 1
src/main.ts

@@ -7,6 +7,7 @@ import TodayEat from './views/TodayEat.vue'
 import TableDesign from './views/TableDesign.vue'
 import Favorites from './views/Favorites.vue'
 import Gallery from './views/Gallery.vue'
+import HowToCook from './views/HowToCook.vue'
 import './style.css'
 
 const routes = [
@@ -15,7 +16,8 @@ const routes = [
     { path: '/today-eat', component: TodayEat },
     { path: '/table-design', component: TableDesign },
     { path: '/favorites', component: Favorites },
-    { path: '/gallery', component: Gallery }
+    { path: '/gallery', component: Gallery },
+    { path: '/how-to-cook', component: HowToCook }
 ]
 
 const router = createRouter({

+ 83 - 0
src/services/aiService.ts

@@ -736,5 +736,88 @@ export const testAIConnection = async (): Promise<boolean> => {
     }
 }
 
+/**
+ * 根据菜名生成详细菜谱
+ * @param dishName 菜品名称
+ * @returns Promise<Recipe>
+ */
+export const generateDishRecipeByName = async (dishName: string): Promise<Recipe> => {
+    try {
+        const prompt = `请为"${dishName}"这道菜生成详细的制作教程。
+
+要求:
+1. 提供完整的食材清单(包括主料和调料)
+2. 详细的制作步骤,每个步骤要包含具体的时间和火候
+3. 实用的烹饪技巧和注意事项
+4. 如果是地方菜,请说明其特色和来源
+
+请按照以下JSON格式返回菜谱:
+{
+  "name": "菜品名称",
+  "ingredients": ["主料1 200g", "调料1 适量", "调料2 1勺"],
+  "steps": [
+    {
+      "step": 1,
+      "description": "详细的步骤描述,包含具体操作方法",
+      "time": 5,
+      "temperature": "中火/大火/小火"
+    }
+  ],
+  "cookingTime": 30,
+  "difficulty": "easy/medium/hard",
+  "tips": ["实用技巧1", "注意事项2", "口感调节3"]
+}`
+
+        const response = await aiClient.post('/chat/completions', {
+            model: AI_CONFIG.model,
+            messages: [
+                {
+                    role: 'system',
+                    content: '你是一位经验丰富的中华料理大师,精通各种菜系的制作方法。请根据用户提供的菜名,生成详细、实用的制作教程。请严格按照JSON格式返回,不要包含任何其他文字。请务必用中文回答。'
+                },
+                {
+                    role: 'user',
+                    content: prompt
+                }
+            ],
+            temperature: 0.7,
+            stream: false
+        })
+
+        // 解析AI响应
+        const aiResponse = response.data.choices[0].message.content
+        let cleanResponse = aiResponse.trim()
+        if (cleanResponse.startsWith('```json')) {
+            cleanResponse = cleanResponse.replace(/```json\s*/, '').replace(/```\s*$/, '')
+        } else if (cleanResponse.startsWith('```')) {
+            cleanResponse = cleanResponse.replace(/```\s*/, '').replace(/```\s*$/, '')
+        }
+
+        const recipeData = JSON.parse(cleanResponse)
+
+        // 构建完整的Recipe对象
+        const recipe: Recipe = {
+            id: `dish-search-${Date.now()}`,
+            name: recipeData.name || dishName,
+            cuisine: '传统菜谱',
+            ingredients: recipeData.ingredients || ['主要食材', '调料'],
+            steps: recipeData.steps || [
+                { step: 1, description: '准备所有食材', time: 5 },
+                { step: 2, description: '按照传统方法制作', time: 20 }
+            ],
+            cookingTime: recipeData.cookingTime || 25,
+            difficulty: recipeData.difficulty || 'medium',
+            tips: recipeData.tips || ['注意火候控制', '调味要适中'],
+            nutritionAnalysis: undefined,
+            winePairing: undefined
+        }
+
+        return recipe
+    } catch (error) {
+        console.error(`生成"${dishName}"菜谱失败:`, error)
+        throw new Error(`AI生成"${dishName}"菜谱失败,请稍后重试`)
+    }
+}
+
 // 导出配置更新函数,供外部使用
 export { AI_CONFIG }

+ 223 - 0
src/views/HowToCook.vue

@@ -0,0 +1,223 @@
+<template>
+    <div class="min-h-screen bg-yellow-400 px-2 md:px-4 py-6">
+        <!-- 全局导航 -->
+        <GlobalNavigation />
+
+        <div class="max-w-7xl mx-auto">
+            <!-- 输入区域 -->
+            <div class="mb-8">
+                <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>
+                </div>
+                <div class="bg-white border-2 border-[#0A0910] rounded-lg rounded-tl-none p-4 md:p-6">
+                    <div class="space-y-4">
+                        <!-- 菜名输入框 -->
+                        <div class="relative">
+                            <input
+                                v-model="dishName"
+                                @keyup.enter="searchRecipe"
+                                placeholder="例如:红烧肉、宫保鸡丁、麻婆豆腐..."
+                                class="w-full p-4 border-2 border-[#0A0910] rounded-lg text-md font-medium focus:outline-none focus:ring-2 focus:ring-pink-400"
+                            />
+                            <div class="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-400">
+                                <span class="text-2xl">🔍</span>
+                            </div>
+                        </div>
+
+
+
+                        <!-- 搜索按钮 -->
+                        <button
+                            @click="searchRecipe"
+                            :disabled="!dishName.trim() || isLoading"
+                            class="w-full bg-gradient-to-r from-pink-500 to-red-500 hover:from-pink-600 hover:to-red-600 disabled:from-gray-400 disabled:to-gray-400 text-white px-6 py-4 rounded-lg font-bold text-md border-2 border-[#0A0910] transition-all duration-300 transform  disabled:scale-100 disabled:cursor-not-allowed shadow-lg"
+                        >
+                            <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>AI大师思考中...</span>
+                                </template>
+                                <template v-else>
+                                    <span class="text-xl">🔍</span>
+                                    <span>开始学做菜</span>
+                                </template>
+                            </span>
+                        </button>
+                    </div>
+                </div>
+            </div>
+
+            <!-- 菜谱结果 -->
+            <div class="mb-8" data-results>
+                <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>
+                </div>
+                <div class="bg-white border-2 border-[#0A0910] rounded-lg rounded-tl-none p-4 md:p-6">
+                    <!-- 空状态提示 -->
+                    <div v-if="!recipe && !isLoading" class="text-center py-12">
+                        <div class="w-16 h-16 bg-gradient-to-br from-gray-300 to-gray-400 rounded-lg flex items-center justify-center mx-auto mb-4">
+                            <span class="text-white text-2xl">🍳</span>
+                        </div>
+                        <h3 class="text-lg font-bold text-gray-600 mb-3">等待您的菜名...</h3>
+                        <div class="space-y-2 text-sm text-gray-500 max-w-md mx-auto">
+                            <p class="flex items-center justify-center gap-2">
+                                <span>💡</span>
+                                <span>输入具体菜名效果更好,如"红烧肉"</span>
+                            </p>
+                            <p class="flex items-center justify-center gap-2">
+                                <span>🌟</span>
+                                <span>支持各种菜系:川菜、粤菜、湘菜等</span>
+                            </p>
+                            <p class="flex items-center justify-center gap-2">
+                                <span>📝</span>
+                                <span>包含详细步骤、用料和烹饪技巧</span>
+                            </p>
+                        </div>
+                    </div>
+
+                    <!-- 加载状态 -->
+                    <div v-if="isLoading && !recipe" class="text-center py-12">
+                        <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 animate-pulse">
+                            <span class="text-white text-2xl">👨‍🍳</span>
+                        </div>
+                        <h3 class="text-xl font-bold text-gray-700 mb-2">AI大师正在为您准备教程...</h3>
+                        <p class="text-gray-500">请稍等片刻,精彩内容即将呈现</p>
+                        <div class="mt-4">
+                            <div class="animate-spin w-8 h-8 border-4 border-pink-400 border-t-transparent rounded-full mx-auto"></div>
+                        </div>
+                    </div>
+
+                    <!-- 菜谱内容 -->
+                    <div v-if="recipe" class="max-w-2xl mx-auto border-2 border-[#333333] rounded-lg overflow-hidden">
+                        <RecipeCard :recipe="recipe" :show-actions="true" />
+                    </div>
+                </div>
+          
+            </div>
+
+            <!-- 历史记录 -->
+            <div v-if="searchHistory.length > 0" class="mb-8">
+                <div class="bg-blue-400 text-white px-4 py-2 rounded-t-lg border-2 border-[#0A0910] border-b-0 inline-block">
+                    <span class="font-bold">最近搜索</span>
+                </div>
+                <div class="bg-white border-2 border-[#0A0910] rounded-lg rounded-tl-none p-4">
+                    <div class="flex flex-wrap gap-2">
+                        <button
+                            v-for="historyItem in searchHistory.slice(0, 8)"
+                            :key="historyItem"
+                            @click="selectDish(historyItem)"
+                            class="px-3 py-2 text-sm bg-blue-100 text-blue-700 rounded-full border border-blue-300 hover:bg-blue-200 hover:border-blue-400 transition-all duration-200"
+                        >
+                            {{ historyItem }}
+                        </button>
+                        <button
+                            v-if="searchHistory.length > 0"
+                            @click="clearHistory"
+                            class="px-3 py-2 text-sm text-red-600 hover:text-red-700 underline"
+                        >
+                            清除历史
+                        </button>
+                    </div>
+                </div>
+            </div>
+
+
+        </div>
+
+        <!-- 全局底部 -->
+        <GlobalFooter />
+    </div>
+</template>
+
+<script setup lang="ts">
+import { ref, onMounted } from 'vue'
+import { generateDishRecipeByName } from '@/services/aiService'
+import type { Recipe } from '@/types'
+import RecipeCard from '@/components/RecipeCard.vue'
+import GlobalNavigation from '@/components/GlobalNavigation.vue'
+import GlobalFooter from '@/components/GlobalFooter.vue'
+
+// 响应式数据
+const dishName = ref('')
+const recipe = ref<Recipe | null>(null)
+const isLoading = ref(false)
+const searchHistory = ref<string[]>([])
+
+// 页面加载时恢复历史记录
+onMounted(() => {
+    const saved = localStorage.getItem('howToCook_history')
+    if (saved) {
+        try {
+            searchHistory.value = JSON.parse(saved)
+        } catch (e) {
+            console.error('恢复搜索历史失败:', e)
+        }
+    }
+})
+
+// 选择菜品
+const selectDish = (dish: string) => {
+    dishName.value = dish
+    searchRecipe()
+}
+
+// 搜索菜谱
+const searchRecipe = async () => {
+    if (!dishName.value.trim() || isLoading.value) return
+
+    const searchTerm = dishName.value.trim()
+    
+    // 添加到历史记录
+    if (!searchHistory.value.includes(searchTerm)) {
+        searchHistory.value.unshift(searchTerm)
+        if (searchHistory.value.length > 20) {
+            searchHistory.value = searchHistory.value.slice(0, 20)
+        }
+        localStorage.setItem('howToCook_history', JSON.stringify(searchHistory.value))
+    }
+
+    isLoading.value = true
+    recipe.value = null
+
+    try {
+        const result = await generateDishRecipeByName(searchTerm)
+        recipe.value = result
+        
+        // 滚动到结果区域
+        setTimeout(() => {
+            const resultsElement = document.querySelector('[data-results]')
+            if (resultsElement) {
+                resultsElement.scrollIntoView({ behavior: 'smooth', block: 'start' })
+            }
+        }, 100)
+    } catch (error) {
+        console.error('搜索菜谱失败:', error)
+        // 这里可以添加错误提示
+    } finally {
+        isLoading.value = false
+    }
+}
+
+// 清除历史记录
+const clearHistory = () => {
+    searchHistory.value = []
+    localStorage.removeItem('howToCook_history')
+}
+</script>
+
+<style scoped>
+@keyframes fade-in-up {
+    from {
+        opacity: 0;
+        transform: translateY(20px);
+    }
+    to {
+        opacity: 1;
+        transform: translateY(0);
+    }
+}
+
+.animate-fade-in-up {
+    animation: fade-in-up 0.6s ease-out;
+}
+</style>