Files
gen2d/frontend/src/components/GenerateForm.tsx
T

125 lines
3.9 KiB
TypeScript
Raw Normal View History

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>
)
}