2026-05-25 14:52:24 +08:00

164 lines
6.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client'
import { buttonStyles, buttonModifiers, inputStyles, cn } from '@/lib/ui-styles'
import type { TemplateForm, BuiltinTemplateConfig } from '../types'
interface TemplateBasicsFormProps {
form: TemplateForm
statusOptions: Array<{ value: string; label: string }>
materialTypeOptions: string[]
showPresetSelector: boolean
selectedPreset: string | null
presets: BuiltinTemplateConfig[]
onChange: (patch: Partial<TemplateForm>) => void
onSelectPreset: (presetKey: string) => void
}
export default function TemplateBasicsForm({
form,
statusOptions,
materialTypeOptions,
showPresetSelector,
selectedPreset,
presets,
onChange,
onSelectPreset,
}: TemplateBasicsFormProps) {
const inferMaterialKind = (materialType: string) => {
if (materialType === '视频') return 'VIDEO'
if (materialType === '音频') return 'AUDIO'
if (materialType === '其他') return 'OTHER'
return 'IMAGE'
}
return (
<div className="space-y-5">
<div>
<label className="block text-sm font-semibold text-slate-900 dark:text-white mb-2">
模板名称 <span className="text-red-500">*</span>
</label>
<input
type="text"
value={form.name}
onChange={(e) => onChange({ name: e.target.value })}
className={cn(inputStyles.base)}
placeholder="例如:产品素材模板"
/>
<p className="text-xs text-slate-500 dark:text-slate-400 mt-1.5">给模板起一个容易识别的名称</p>
</div>
<div>
<label className="block text-sm font-semibold text-slate-900 dark:text-white mb-2">模板描述</label>
<textarea
rows={2}
value={form.description}
onChange={(e) => onChange({ description: e.target.value })}
className={cn(inputStyles.base, 'resize-none')}
placeholder="简单描述这个模板的用途"
/>
</div>
<div>
<label className="block text-sm font-semibold text-slate-900 dark:text-white mb-2">模板状态</label>
<select
value={form.status}
onChange={(e) => onChange({ status: e.target.value })}
className={cn(inputStyles.base)}
>
{statusOptions.map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
<p className="text-xs text-slate-500 dark:text-slate-400 mt-1.5">草稿:仅保存,不生效;启用:用户可以使用</p>
</div>
<div className="space-y-3">
<div>
<label className="block text-sm font-semibold text-slate-900 dark:text-white mb-2">
素材类型 <span className="text-red-500">*</span>
</label>
<select
value={form.materialType}
onChange={(e) => onChange({ materialType: e.target.value, materialKind: inferMaterialKind(e.target.value) })}
className={cn(inputStyles.base)}
>
{materialTypeOptions.map((option) => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
<p className="text-xs text-slate-500 dark:text-slate-400 mt-1.5">用于展示的业务类型,例如“视频”、“PSD”等。</p>
</div>
<div>
<label className="block text-sm font-semibold text-slate-900 dark:text-white mb-2">
素材大类 <span className="text-red-500">*</span>
</label>
<div className="flex flex-wrap gap-2">
{[
{ label: '图片/文档', value: 'IMAGE', hint: '普通素材,走图片/文件流程' },
{ label: '音频', value: 'AUDIO', hint: '走音频上传与解析流程' },
{ label: '视频', value: 'VIDEO', hint: '走视频上传与转码流程' },
{ label: '其他', value: 'OTHER', hint: '暂不归类,可后续扩展' },
].map((option) => {
const active = (form.materialKind || 'IMAGE') === option.value
return (
<button
key={option.value}
type="button"
onClick={() => onChange({ materialKind: option.value })}
className={cn(
active ? buttonStyles.primary : buttonStyles.secondary,
buttonModifiers.pill,
'flex flex-col items-start gap-0.5 px-4 py-2 text-left min-w-[120px]',
)}
>
<span className="text-sm font-semibold">{option.label}</span>
<span className="text-[11px] text-current/70">{option.hint}</span>
</button>
)
})}
</div>
<p className="text-xs text-slate-500 dark:text-slate-400 mt-1.5">用于驱动上传/处理链路,音频模板请选择“音频”,视频模板请选择“视频”。</p>
</div>
</div>
{showPresetSelector && (
<div>
<label className="block text-sm font-semibold text-slate-900 dark:text-white mb-2">选择预设模板</label>
<div className="flex flex-wrap gap-2">
{presets.map((preset) => {
const active = selectedPreset === preset.key
return (
<button
key={preset.key}
type="button"
onClick={() => onSelectPreset(preset.key)}
className={cn(
active ? buttonStyles.primary : buttonStyles.secondary,
buttonModifiers.compactXs,
buttonModifiers.pill,
'flex items-center gap-1.5 shadow-none',
!active && 'border-dashed',
)}
>
{active && <span className="inline-block w-1.5 h-1.5 rounded-full bg-white" />}
<span>{preset.name}</span>
</button>
)
})}
</div>
<p className="text-xs text-slate-500 dark:text-slate-400 mt-1.5">
选择一个预设模板后,下方字段会自动填充,可在此基础上继续增删或调整。
</p>
</div>
)}
</div>
)
}