+ {toasts.map((t) => (
+
+ {t.message}
+
+
+ ))}
+
+ )
+}
diff --git a/frontend/src/pages/GeneratePage.tsx b/frontend/src/pages/GeneratePage.tsx
index 9eee0aa..bb17ec7 100644
--- a/frontend/src/pages/GeneratePage.tsx
+++ b/frontend/src/pages/GeneratePage.tsx
@@ -2,6 +2,7 @@ import { useEffect } from 'react'
import { useNavigate, useParams } from 'react-router-dom'
import { useTaskStore } from '../stores/task'
import { useGenerationStore } from '../stores/generation'
+import { useToastStore } from '../stores/toast'
import GenerateForm from '../components/GenerateForm'
import ProgressBar from '../components/ProgressBar'
@@ -9,6 +10,7 @@ export default function GeneratePage() {
const { projectId = 'proj-default' } = useParams()
const navigate = useNavigate()
const { assetType, reset: resetTask } = useTaskStore()
+ const addToast = useToastStore(s => s.addToast)
const {
status,
stage,
@@ -25,6 +27,13 @@ export default function GeneratePage() {
return () => resetGeneration()
}, [resetGeneration])
+ // 失败时显示 toast
+ useEffect(() => {
+ if (status === 'failed') {
+ addToast({ type: 'error', message: '素材生成失败,请重试' })
+ }
+ }, [status, addToast])
+
// 完成后自动跳转
useEffect(() => {
if (status === 'completed' && taskId) {
diff --git a/frontend/src/pages/ProjectPage.tsx b/frontend/src/pages/ProjectPage.tsx
index 70c21e6..971c503 100644
--- a/frontend/src/pages/ProjectPage.tsx
+++ b/frontend/src/pages/ProjectPage.tsx
@@ -1,6 +1,7 @@
import { useEffect, useState } from 'react'
import { Link, useNavigate, useParams } from 'react-router-dom'
import { useProjectStore } from '../stores/project'
+import { useToastStore } from '../stores/toast'
import { getTasks } from '../api/project'
import type { Task } from '../api/types'
import StyleSelector from '../components/StyleSelector'
@@ -35,10 +36,15 @@ export default function ProjectPage() {
getTasks(projectId).then(setTasks)
}, [projectId, loadProject, loadStyle])
+ const addToast = useToastStore(s => s.addToast)
+
const handleSave = async () => {
setSaving(true)
try {
await saveStyle()
+ addToast({ type: 'success', message: '风格已保存' })
+ } catch {
+ addToast({ type: 'error', message: '保存失败,请重试' })
} finally {
setSaving(false)
}
diff --git a/frontend/src/stores/toast.ts b/frontend/src/stores/toast.ts
new file mode 100644
index 0000000..8a717b0
--- /dev/null
+++ b/frontend/src/stores/toast.ts
@@ -0,0 +1,34 @@
+import { create } from 'zustand'
+
+export type ToastType = 'success' | 'error' | 'info'
+
+export interface Toast {
+ id: string
+ type: ToastType
+ message: string
+ duration?: number
+}
+
+interface ToastState {
+ toasts: Toast[]
+ addToast: (t: Omit