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:
@@ -0,0 +1,124 @@
|
||||
import type { AssetType } from '../api/types'
|
||||
import { useProjectStore } from '../stores/project'
|
||||
import { useTaskStore } from '../stores/task'
|
||||
import { mergeStyles } from '../utils/style'
|
||||
import StyleSelector from './StyleSelector'
|
||||
import PromptEditor from './PromptEditor'
|
||||
|
||||
const ASSET_TYPES: { value: AssetType; label: string }[] = [
|
||||
{ value: 'sprite', label: '精灵' },
|
||||
{ value: 'background', label: '背景' },
|
||||
{ value: 'ui', label: 'UI' },
|
||||
{ value: 'animation', label: '动画' },
|
||||
]
|
||||
|
||||
interface GenerateFormProps {
|
||||
onSubmit: () => void
|
||||
submitting: boolean
|
||||
}
|
||||
|
||||
export default function GenerateForm({ onSubmit, submitting }: GenerateFormProps) {
|
||||
const { style: projectStyle } = useProjectStore()
|
||||
const {
|
||||
prompt,
|
||||
assetType,
|
||||
taskStyle,
|
||||
params,
|
||||
setPrompt,
|
||||
setAssetType,
|
||||
toggleTaskStyle,
|
||||
setParams,
|
||||
} = useTaskStore()
|
||||
|
||||
const mergedStyle = mergeStyles(projectStyle, taskStyle)
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 24 }}>
|
||||
{/* 素材类型 */}
|
||||
<div>
|
||||
<h3 style={{ marginBottom: 12, fontSize: 14, color: 'var(--text-secondary)' }}>
|
||||
素材类型
|
||||
</h3>
|
||||
<div style={{ display: 'flex', gap: 8 }}>
|
||||
{ASSET_TYPES.map(t => (
|
||||
<button
|
||||
key={t.value}
|
||||
type="button"
|
||||
className={assetType === t.value ? 'btn-primary' : 'btn-secondary'}
|
||||
onClick={() => setAssetType(t.value)}
|
||||
style={{ padding: '8px 20px' }}
|
||||
>
|
||||
{t.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 任务风格覆盖 */}
|
||||
<div>
|
||||
<h3 style={{ marginBottom: 12, fontSize: 14, color: 'var(--text-secondary)' }}>
|
||||
风格覆盖(可选)
|
||||
</h3>
|
||||
<StyleSelector value={taskStyle} onChange={toggleTaskStyle} compact />
|
||||
</div>
|
||||
|
||||
{/* 提示词 */}
|
||||
<div>
|
||||
<h3 style={{ marginBottom: 12, fontSize: 14, color: 'var(--text-secondary)' }}>
|
||||
提示词
|
||||
</h3>
|
||||
<PromptEditor
|
||||
prompt={prompt}
|
||||
onPromptChange={setPrompt}
|
||||
style={mergedStyle}
|
||||
params={{ resolution: params.resolution, format: params.format }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* 参数 */}
|
||||
<div>
|
||||
<h3 style={{ marginBottom: 12, fontSize: 14, color: 'var(--text-secondary)' }}>
|
||||
参数
|
||||
</h3>
|
||||
<div style={{ display: 'flex', gap: 24, alignItems: 'center' }}>
|
||||
<label style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||
<span style={{ fontSize: 13, color: 'var(--text-secondary)' }}>分辨率</span>
|
||||
<input
|
||||
type="number"
|
||||
value={params.resolution}
|
||||
onChange={e => setParams({ resolution: Number(e.target.value) })}
|
||||
min={32}
|
||||
max={1024}
|
||||
step={32}
|
||||
style={{ width: 100 }}
|
||||
/>
|
||||
<span style={{ fontSize: 13, color: 'var(--text-muted)' }}>px</span>
|
||||
</label>
|
||||
<label style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||
<span style={{ fontSize: 13, color: 'var(--text-secondary)' }}>格式</span>
|
||||
<select
|
||||
value={params.format}
|
||||
onChange={e =>
|
||||
setParams({ format: e.target.value as 'spritesheet' | 'individual' })
|
||||
}
|
||||
>
|
||||
<option value="spritesheet">Spritesheet</option>
|
||||
<option value="individual">单帧</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 提交 */}
|
||||
<button
|
||||
type="button"
|
||||
className="btn-primary"
|
||||
onClick={onSubmit}
|
||||
disabled={submitting || !prompt.trim()}
|
||||
style={{ alignSelf: 'flex-start', padding: '12px 32px', fontSize: 16 }}
|
||||
>
|
||||
{submitting ? '提交中...' : '开始生成'}
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user