Pārlūkot izejas kodu

新增菜谱400出错提示

liuziting 6 mēneši atpakaļ
vecāks
revīzija
3ce168c62f

+ 1 - 1
src/components/RecipeCard.vue

@@ -343,7 +343,7 @@ const generateImage = async () => {
         GalleryService.addToGallery(props.recipe, image.url, image.id, prompt)
     } catch (error) {
         console.error('生成图片失败:', error)
-        imageError.value = '生成图片失败,请稍后重试'
+        imageError.value = 'AI画师表示这道菜太有艺术挑战性了,哈哈!'
     } finally {
         isGeneratingImage.value = false
         if (imageLoadingInterval) {

+ 26 - 9
src/services/aiService.ts

@@ -124,8 +124,16 @@ export const generateRecipe = async (ingredients: string[], cuisine: CuisineType
     } catch (error) {
         console.error(`生成${cuisine.name}菜谱失败:`, error)
 
-        // 抛出错误,让上层处理
-        throw new Error(`AI生成${cuisine.name}菜谱失败,请稍后重试`)
+        // 检查是否是400错误或其他特定错误
+        if (error && typeof error === 'object' && 'response' in error) {
+            const axiosError = error as any
+            if (axiosError.response?.status === 400) {
+                throw new Error(`${cuisine.name}表示这个食材搭配太有挑战性了`)
+            }
+        }
+
+        // 抛出友好的错误信息
+        throw new Error(`${cuisine.name}暂时学不会这道菜`)
     }
 }
 
@@ -261,7 +269,7 @@ export const generateTableMenu = async (config: {
         return menuData.dishes || []
     } catch (error) {
         console.error('生成一桌菜菜单失败:', error)
-        throw new Error('AI生成菜单失败,请稍后重试')
+        throw new Error('大厨表示这个菜单搭配太有挑战性了,请稍后重试')
     }
 }
 
@@ -340,7 +348,7 @@ export const generateDishRecipe = async (dishName: string, dishDescription: stri
         return recipe
     } catch (error) {
         console.error('生成菜品菜谱失败:', error)
-        throw new Error('AI生成菜谱失败,请稍后重试')
+        throw new Error('大厨表示这道菜太有挑战性了,请稍后重试')
     }
 }
 
@@ -416,7 +424,7 @@ export const generateCustomRecipe = async (ingredients: string[], customPrompt:
         return recipe
     } catch (error) {
         console.error('生成自定义菜谱失败:', error)
-        throw new Error('AI生成自定义菜谱失败,请稍后重试')
+        throw new Error('大厨表示这个自定义要求太有挑战性了,请稍后重试')
     }
 }
 
@@ -425,6 +433,7 @@ export const generateMultipleRecipesStream = async (
     ingredients: string[],
     cuisines: CuisineType[],
     onRecipeGenerated: (recipe: Recipe, index: number, total: number) => void,
+    onRecipeError?: (error: Error, index: number, cuisine: CuisineType, total: number) => void,
     customPrompt?: string
 ): Promise<void> => {
     const total = cuisines.length
@@ -444,6 +453,14 @@ export const generateMultipleRecipesStream = async (
             onRecipeGenerated(recipe, index, total)
         } catch (error) {
             console.error(`生成${cuisine.name}菜谱失败:`, error)
+            
+            // 调用错误回调,让前端可以显示友好的错误信息
+            if (onRecipeError) {
+                const errorMessage = error instanceof Error ? error.message : `${cuisine.name}生成失败`
+                const friendlyError = new Error(`${cuisine.name}不会这道菜,哈哈`)
+                onRecipeError(friendlyError, index, cuisine, total)
+            }
+            
             // 即使某个菜系失败,也继续生成其他菜系
             continue
         }
@@ -827,7 +844,7 @@ export const generateDishRecipeByName = async (dishName: string): Promise<Recipe
         return recipe
     } catch (error) {
         console.error(`生成"${dishName}"菜谱失败:`, error)
-        throw new Error(`AI生成"${dishName}"菜谱失败,请稍后重试`)
+        throw new Error(`大厨表示"${dishName}"这道菜太有挑战性了,请稍后重试`)
     }
 }
 
@@ -935,7 +952,7 @@ export const generateSauceRecipe = async (sauceName: string): Promise<SauceRecip
         return sauceRecipe
     } catch (error) {
         console.error(`生成"${sauceName}"酱料配方失败:`, error)
-        throw new Error(`AI生成"${sauceName}"酱料配方失败,请稍后重试`)
+        throw new Error(`酱料大师表示"${sauceName}"这个配方太有挑战性了,请稍后重试`)
     }
 }
 
@@ -1006,7 +1023,7 @@ export const recommendSauces = async (preferences: SaucePreference): Promise<str
         return recommendationData.recommendations || []
     } catch (error) {
         console.error('获取酱料推荐失败:', error)
-        throw new Error('AI推荐酱料失败,请稍后重试')
+        throw new Error('酱料大师暂时想不出好的推荐,请稍后重试')
     }
 }
 
@@ -1130,7 +1147,7 @@ ${request.customRequirements ? `- 特殊要求:${request.customRequirements}`
         return customSauce
     } catch (error) {
         console.error('创建自定义酱料失败:', error)
-        throw new Error('AI创建自定义酱料失败,请稍后重试')
+        throw new Error('酱料大师表示这个自定义配方太有挑战性了,请稍后重试')
     }
 }
 

+ 137 - 4
src/views/Home.vue

@@ -378,6 +378,63 @@
                                 <!-- 如果菜谱已生成,显示菜谱卡片 -->
                                 <RecipeCard v-if="cuisineInfo.recipe" :recipe="cuisineInfo.recipe" />
 
+                                <!-- 如果菜谱生成失败,显示友好错误信息 -->
+                                <div v-else-if="cuisineInfo.error" class="bg-white error-card">
+                                    <!-- 错误头部 -->
+                                    <div class="bg-gradient-to-r from-red-400 to-orange-400 text-white p-4 md:p-6 border-b-2 border-black">
+                                        <div class="flex items-center justify-between">
+                                            <div class="flex-1">
+                                                <h3 class="text-lg font-bold mb-1 flex items-center gap-2">
+                                                    <span class="animate-bounce">😅</span>
+                                                    {{ cuisineInfo.name }}不会这道菜,哈哈
+                                                </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>
+                                                        技能点不够
+                                                    </span>
+                                                    <span>🎯 换个大师试试</span>
+                                                </div>
+                                            </div>
+                                            <div class="text-2xl ml-2">🤷‍♂️</div>
+                                        </div>
+                                    </div>
+
+                                    <!-- 错误内容区域 -->
+                                    <div class="p-4 md:p-6 text-center">
+                                        <div class="mb-4">
+                                            <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>
+                                        </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>
+                                            <div class="text-xs text-yellow-700 space-y-1">
+                                                <p>• 试试其他菜系大师,他们可能有不同的想法</p>
+                                                <p>• 调整一下食材搭配,或许会有惊喜</p>
+                                                <p>• 使用自定义要求,给大师一些灵感</p>
+                                            </div>
+                                        </div>
+
+                                        <!-- 重试按钮 -->
+                                        <button
+                                            @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"
+                                        >
+                                            🔄 再试一次
+                                        </button>
+                                    </div>
+                                </div>
+
                                 <!-- 如果菜谱还在生成中,显示加载状态 -->
                                 <div v-else class="bg-white loading-card">
                                     <!-- 菜系头部 -->
@@ -561,7 +618,7 @@ import { ingredientCategories } from '@/config/ingredients'
 import RecipeCard from '@/components/RecipeCard.vue'
 import GlobalNavigation from '@/components/GlobalNavigation.vue'
 import GlobalFooter from '@/components/GlobalFooter.vue'
-import { generateMultipleRecipes, generateCustomRecipe, generateMultipleRecipesStream } from '@/services/aiService'
+import { generateMultipleRecipes, generateCustomRecipe, generateMultipleRecipesStream, generateRecipe } from '@/services/aiService'
 import type { Recipe, CuisineType, NutritionAnalysis } from '@/types'
 
 // 响应式数据
@@ -585,6 +642,8 @@ interface CuisineSlot {
     recipe?: Recipe
     loadingText: string
     progress: number
+    error?: boolean
+    errorMessage?: string
 }
 const cuisineSlots = ref<CuisineSlot[]>([])
 
@@ -866,10 +925,11 @@ const generateRecipes = async () => {
                     recipes.value.push(recipe)
 
                     // 更新全局加载文字,显示进度
-                    loadingText.value = `已完成 ${recipes.value.length}/${total} 道菜谱...`
+                    const completedCount = recipes.value.length + cuisineSlots.value.filter(slot => slot.error).length
+                    loadingText.value = `已完成 ${completedCount}/${total} 道菜谱...`
 
-                    // 如果是最后一个菜谱,停止加载状态
-                    if (recipes.value.length === total) {
+                    // 检查是否所有菜系都已处理完成(成功或失败)
+                    if (completedCount === total) {
                         isLoading.value = false
                         // 清理所有进度定时器
                         progressIntervals.forEach(interval => clearInterval(interval))
@@ -880,6 +940,28 @@ const generateRecipes = async () => {
                         }, 1000)
                     }
                 },
+                (error: Error, index: number, cuisine: CuisineType, total: number) => {
+                    // 处理菜谱生成失败
+                    const targetSlot = cuisineSlots.value.find(slot => selectedCuisineObjects[index] && slot.id === selectedCuisineObjects[index].id)
+
+                    if (targetSlot) {
+                        targetSlot.error = true
+                        targetSlot.errorMessage = error.message
+                        targetSlot.progress = 0
+                        targetSlot.loadingText = '生成失败'
+                    }
+
+                    // 更新全局加载文字,显示进度
+                    const completedCount = recipes.value.length + cuisineSlots.value.filter(slot => slot.error).length
+                    loadingText.value = `已完成 ${completedCount}/${total} 道菜谱...`
+
+                    // 检查是否所有菜系都已处理完成(成功或失败)
+                    if (completedCount === total) {
+                        isLoading.value = false
+                        // 清理所有进度定时器
+                        progressIntervals.forEach(interval => clearInterval(interval))
+                    }
+                },
                 customPrompt.value.trim() || undefined
             )
 
@@ -902,6 +984,57 @@ const generateRecipes = async () => {
     }
 }
 
+// 重试失败的菜系
+const retryFailedCuisine = async (failedSlot: CuisineSlot) => {
+    // 重置错误状态
+    failedSlot.error = false
+    failedSlot.errorMessage = undefined
+    failedSlot.progress = 0
+    failedSlot.loadingText = '大师重新思考中...'
+
+    // 找到对应的菜系信息
+    const cuisine = cuisines.find(c => c.id === failedSlot.id)
+    if (!cuisine) return
+
+    // 开始进度动画
+    const progressInterval = setInterval(() => {
+        if (!failedSlot.recipe && !failedSlot.error) {
+            failedSlot.progress = Math.min(failedSlot.progress + Math.random() * 10, 85)
+        }
+    }, 500)
+
+    try {
+        // 添加随机延迟
+        const delay = 1000 + Math.random() * 2000
+        await new Promise(resolve => setTimeout(resolve, delay))
+
+        // 重新生成菜谱
+        const recipe = customPrompt.value.trim() 
+            ? await generateCustomRecipe(ingredients.value, customPrompt.value.trim())
+            : await generateRecipe(ingredients.value, cuisine, customPrompt.value.trim() || undefined)
+
+        // 成功生成,更新槽位
+        failedSlot.recipe = recipe
+        failedSlot.progress = 100
+        failedSlot.loadingText = '重新创作完成!'
+
+        // 添加到菜谱列表
+        recipes.value.push(recipe)
+
+        clearInterval(progressInterval)
+    } catch (error) {
+        console.error(`重试${cuisine.name}菜谱失败:`, error)
+        
+        // 重新设置错误状态
+        failedSlot.error = true
+        failedSlot.errorMessage = error instanceof Error ? error.message : `${cuisine.name}还是不会这道菜`
+        failedSlot.progress = 0
+        failedSlot.loadingText = '重试失败'
+
+        clearInterval(progressInterval)
+    }
+}
+
 // 模拟AI调用(后续替换为真实接口)
 const simulateAICall = async () => {
     return new Promise(resolve => {

+ 2 - 2
src/views/SauceDesign.vue

@@ -398,7 +398,7 @@ const searchSauce = async () => {
         }, 100)
     } catch (error) {
         console.error('搜索酱料失败:', error)
-        showErrorMessage('生成酱料配方失败,请稍后重试')
+        showErrorMessage('酱料大师表示这个配方太有挑战性了,哈哈!换个搭配试试吧~')
     } finally {
         isLoadingSearch.value = false
     }
@@ -424,7 +424,7 @@ const selectRecommendedSauce = async (sauceName: string) => {
         }, 100)
     } catch (error) {
         console.error('获取酱料配方失败:', error)
-        showErrorMessage('生成酱料配方失败,请稍后重试')
+        showErrorMessage('酱料大师挠了挠头说:"这个配方我还没学会呢!"换个试试吧~')
     } finally {
         isLoadingSearch.value = false
     }

+ 5 - 5
src/views/TableDesign.vue

@@ -622,10 +622,10 @@ const testConnection = async () => {
         if (isConnected) {
             alert('AI连接测试成功!')
         } else {
-            alert('AI连接测试失败,请检查配置')
+            alert('大厨暂时不在厨房,请稍后再试~')
         }
     } catch (error) {
-        alert('AI连接测试失败:' + error)
+        alert('大厨暂时不在厨房:' + error)
     }
 }
 
@@ -651,7 +651,7 @@ const generateTableMenuAction = async () => {
     } catch (error) {
         console.error('生成菜单失败:', error)
         // 显示错误提示
-        alert('AI生成菜单失败,请检查网络连接或稍后重试')
+        alert('大厨表示这个菜单搭配太有挑战性了,哈哈!调整一下要求试试吧~')
     } finally {
         isGenerating.value = false
     }
@@ -687,8 +687,8 @@ const generateDishRecipeAction = async (dish: DishInfo, index: number) => {
         // disableBodyScroll()
     } catch (error) {
         console.error('生成菜谱失败:', error)
-        // 显示错误提示
-        alert(`生成${dish.name}菜谱失败,请稍后重试`)
+        // 显示友好的错误提示
+        alert(`大厨表示"${dish.name}"这道菜太有挑战性了,哈哈!换个菜试试吧~`)
     } finally {
         dish.isGeneratingRecipe = false
     }

+ 5 - 1
src/views/TodayEat.vue

@@ -369,7 +369,11 @@ const generateRecipeFromSelection = async () => {
         showToast('菜谱生成成功', 'success')
     } catch (error) {
         console.error('生成菜谱失败:', error)
-        showToast('生成菜谱失败,请重试', 'error')
+        
+        // 显示友好的错误信息
+        const masterName = selectedMaster.value?.name || '大师'
+        const friendlyMessage = `${masterName}不会这道菜,哈哈!换个搭配试试吧~`
+        showToast(friendlyMessage, 'error')
     } finally {
         clearInterval(textInterval)
         isGenerating.value = false