125 lines
3.9 KiB
TypeScript
125 lines
3.9 KiB
TypeScript
|
|
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>
|
||
|
|
)
|
||
|
|
}
|