feat(ux): 素材类型选择器改为图标卡片布局
每个素材类型使用内联 SVG 图标 + 文字垂直排列的卡片式选择器, 选中态高亮边框与背景色。
This commit is contained in:
@@ -5,6 +5,29 @@ import { mergeStyles } from '../utils/style'
|
||||
import StyleSelector from './StyleSelector'
|
||||
import PromptEditor from './PromptEditor'
|
||||
|
||||
const ASSET_TYPE_ICONS: Record<AssetType, JSX.Element> = {
|
||||
sprite: (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M12 2a3 3 0 0 1 3 3c0 1.1-.6 2.1-1.5 2.6L17 12h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-1.2l-1.4 5.5a1 1 0 0 1-1 .5H10.6a1 1 0 0 1-1-.5L8.2 16H7a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1h2l3.5-4.4A3 3 0 0 1 12 2z" />
|
||||
</svg>
|
||||
),
|
||||
background: (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M4 4h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2zm0 2v8.6l4.3-4.3a1.5 1.5 0 0 1 2.1 0L14 13.9l2.4-2.4a1.5 1.5 0 0 1 2.1 0L20 13V6H4zm0 12h16v-1.4l-3.5-3.5-2.4 2.4a1.5 1.5 0 0 1-2.1 0L6.4 12 4 14.4V18z" />
|
||||
</svg>
|
||||
),
|
||||
ui: (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M3 4h18a1 1 0 0 1 1 1v14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1zm0 2v12h18V6H3zm2 2h4a1 1 0 0 1 1 1v4a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1zm1 1v4h2V9H6zm7-1h5a1 1 0 0 1 0 2h-5a1 1 0 0 1 0-2zm0 4h3a1 1 0 0 1 0 2h-3a1 1 0 0 1 0-2z" />
|
||||
</svg>
|
||||
),
|
||||
animation: (
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M8 5v14l11-7L8 5zm-2 1.5L16 12 6 17.5V6.5z" />
|
||||
</svg>
|
||||
),
|
||||
}
|
||||
|
||||
const ASSET_TYPES: { value: AssetType; label: string }[] = [
|
||||
{ value: 'sprite', label: '精灵' },
|
||||
{ value: 'background', label: '背景' },
|
||||
@@ -58,18 +81,37 @@ export default function GenerateForm({ onSubmit, submitting }: GenerateFormProps
|
||||
<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 style={{ display: 'flex', gap: 12 }}>
|
||||
{ASSET_TYPES.map(t => {
|
||||
const isSelected = assetType === t.value
|
||||
return (
|
||||
<button
|
||||
key={t.value}
|
||||
type="button"
|
||||
onClick={() => setAssetType(t.value)}
|
||||
style={{
|
||||
width: 80,
|
||||
height: 72,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: 6,
|
||||
borderRadius: 'var(--radius-lg)',
|
||||
border: `2px solid ${isSelected ? 'var(--accent)' : 'var(--border)'}`,
|
||||
background: isSelected ? 'var(--accent-dim)' : 'transparent',
|
||||
color: isSelected ? 'var(--accent)' : 'var(--text-secondary)',
|
||||
cursor: 'pointer',
|
||||
transition: 'all 0.2s ease',
|
||||
fontSize: 13,
|
||||
fontWeight: isSelected ? 600 : 400,
|
||||
}}
|
||||
>
|
||||
{ASSET_TYPE_ICONS[t.value]}
|
||||
{t.label}
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user