feat(frontend): 构建前端 MVP,实现完整 UI 框架与 mock 数据层

- 安装 zustand + react-router-dom 依赖
- 修复 Vite 代理配置,添加 /auth 路由转发
- 实现 API 封装层(client.ts 统一 fetch、auth.ts 真实调用)
- 实现 mock 数据层(mock.ts 模拟 WebSocket 管线进度)
- 实现 4 个 zustand stores(auth/project/task/generation)
- 实现 5 个组件(StyleSelector/PromptEditor/GenerateForm/ProgressBar/AssetPreview)
- 实现 5 个页面(Login/Register/Project/Generate/Result)
- 实现路由含 ProtectedRoute 守卫
- 添加暗色主题全局样式
- 更新前端文档与 API 文档
This commit is contained in:
2026-05-24 14:18:39 +08:00
parent 302b9ba88e
commit a2736f722c
33 changed files with 2272 additions and 20 deletions
+111
View File
@@ -0,0 +1,111 @@
import { useEffect } from 'react'
import { Link, useNavigate, useParams } from 'react-router-dom'
import { useTaskStore } from '../stores/task'
import { useGenerationStore } from '../stores/generation'
import GenerateForm from '../components/GenerateForm'
import ProgressBar from '../components/ProgressBar'
export default function GeneratePage() {
const { projectId = 'proj-default' } = useParams()
const navigate = useNavigate()
const { prompt, assetType, reset: resetTask } = useTaskStore()
const {
status,
stage,
progress,
retryCount,
rejectReason,
taskId,
submit,
reset: resetGeneration,
} = useGenerationStore()
// 组件卸载时重置生成状态
useEffect(() => {
return () => resetGeneration()
}, [resetGeneration])
// 完成后自动跳转
useEffect(() => {
if (status === 'completed' && taskId) {
const timer = setTimeout(() => {
navigate(`/projects/${projectId}/tasks/${taskId}`)
}, 1500)
return () => clearTimeout(timer)
}
}, [status, taskId, projectId, navigate])
const handleSubmit = async () => {
await submit(projectId, prompt, assetType)
}
const handleReset = () => {
resetGeneration()
resetTask()
}
return (
<div className="container" style={{ paddingTop: 24, paddingBottom: 40 }}>
{/* 顶栏 */}
<div
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 32,
}}
>
<h1 style={{ fontSize: 24 }}>新建生成</h1>
<Link
to={`/projects/${projectId}`}
className="btn-secondary"
style={{
padding: '8px 20px',
fontSize: 13,
borderRadius: 'var(--radius)',
}}
>
返回工程
</Link>
</div>
{/* 生成表单 */}
{status === 'idle' || status === 'submitting' ? (
<GenerateForm onSubmit={handleSubmit} submitting={status === 'submitting'} />
) : (
<div style={{ display: 'flex', flexDirection: 'column', gap: 24 }}>
{/* 进度条 */}
<ProgressBar
stage={stage}
progress={progress}
status={status}
retryCount={retryCount}
rejectReason={rejectReason}
/>
{/* 状态提示 */}
{status === 'running' && (
<p style={{ textAlign: 'center', color: 'var(--text-secondary)' }}>
管线执行中,请稍候...
</p>
)}
{status === 'completed' && (
<p style={{ textAlign: 'center', color: 'var(--success)' }}>
✓ 生成完成,正在跳转到结果页...
</p>
)}
{status === 'failed' && (
<div style={{ textAlign: 'center' }}>
<p style={{ color: 'var(--error)', marginBottom: 16 }}>生成失败</p>
<button className="btn-primary" onClick={handleReset}>
重新开始
</button>
</div>
)}
</div>
)}
</div>
)
}