Better UX
This commit is contained in:
@@ -42,9 +42,9 @@ export const VoiceLibraryPage: React.FC = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-6 animate-in fade-in">
|
||||
<div className="space-y-6 animate-in fade-in py-4 pb-10">
|
||||
<div className="flex items-center justify-between">
|
||||
<h1 className="text-2xl font-bold tracking-tight">声音库</h1>
|
||||
<h1 className="text-2xl font-bold tracking-tight text-white">声音库</h1>
|
||||
<div className="flex space-x-3">
|
||||
<Button variant="primary" onClick={() => setIsAddModalOpen(true)} className="shadow-[0_0_15px_rgba(6,182,212,0.4)]">
|
||||
<Plus className="mr-2 h-4 w-4" /> 添加声音
|
||||
@@ -69,7 +69,7 @@ export const VoiceLibraryPage: React.FC = () => {
|
||||
<div className="flex items-center space-x-2">
|
||||
<Filter className="h-4 w-4 text-muted-foreground" />
|
||||
<select
|
||||
className="flex h-9 w-full rounded-md border-0 bg-white/5 px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/50 [&>option]:bg-card"
|
||||
className="flex h-9 w-full rounded-md border-0 bg-white/5 px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/50 [&>option]:bg-card text-foreground"
|
||||
value={vendorFilter}
|
||||
onChange={(e) => setVendorFilter(e.target.value as any)}
|
||||
>
|
||||
@@ -82,7 +82,7 @@ export const VoiceLibraryPage: React.FC = () => {
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<select
|
||||
className="flex h-9 w-full rounded-md border-0 bg-white/5 px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/50 [&>option]:bg-card"
|
||||
className="flex h-9 w-full rounded-md border-0 bg-white/5 px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/50 [&>option]:bg-card text-foreground"
|
||||
value={genderFilter}
|
||||
onChange={(e) => setGenderFilter(e.target.value as any)}
|
||||
>
|
||||
@@ -93,7 +93,7 @@ export const VoiceLibraryPage: React.FC = () => {
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<select
|
||||
className="flex h-9 w-full rounded-md border-0 bg-white/5 px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/50 [&>option]:bg-card"
|
||||
className="flex h-9 w-full rounded-md border-0 bg-white/5 px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/50 [&>option]:bg-card text-foreground"
|
||||
value={langFilter}
|
||||
onChange={(e) => setLangFilter(e.target.value as any)}
|
||||
>
|
||||
@@ -120,7 +120,7 @@ export const VoiceLibraryPage: React.FC = () => {
|
||||
<TableRow key={voice.id}>
|
||||
<TableCell className="font-medium">
|
||||
<div className="flex flex-col">
|
||||
<span className="flex items-center">
|
||||
<span className="flex items-center text-white">
|
||||
{voice.vendor === '硅基流动' && <Sparkles className="w-3 h-3 text-primary mr-1.5" />}
|
||||
{voice.name}
|
||||
</span>
|
||||
@@ -130,8 +130,8 @@ export const VoiceLibraryPage: React.FC = () => {
|
||||
<TableCell>
|
||||
<Badge variant={voice.vendor === '硅基流动' ? 'default' : 'outline'}>{voice.vendor}</Badge>
|
||||
</TableCell>
|
||||
<TableCell>{voice.gender === 'Male' ? '男' : '女'}</TableCell>
|
||||
<TableCell>{voice.language === 'zh' ? '中文' : 'English'}</TableCell>
|
||||
<TableCell className="text-muted-foreground">{voice.gender === 'Male' ? '男' : '女'}</TableCell>
|
||||
<TableCell className="text-muted-foreground">{voice.language === 'zh' ? '中文' : 'English'}</TableCell>
|
||||
<TableCell className="text-right">
|
||||
<Button
|
||||
variant="ghost"
|
||||
@@ -177,13 +177,11 @@ const AddVoiceModal: React.FC<{
|
||||
const [vendor, setVendor] = useState<'硅基流动' | 'Ali' | 'Volcano' | 'Minimax'>('硅基流动');
|
||||
const [name, setName] = useState('');
|
||||
|
||||
// SiliconFlow specific state
|
||||
const [sfModel, setSfModel] = useState('fishaudio/fish-speech-1.5');
|
||||
const [sfVoiceId, setSfVoiceId] = useState('fishaudio:amy');
|
||||
const [sfSpeed, setSfSpeed] = useState(1);
|
||||
const [sfGain, setSfGain] = useState(0);
|
||||
|
||||
// Common/Other state
|
||||
const [model, setModel] = useState('');
|
||||
const [voiceKey, setVoiceKey] = useState('');
|
||||
const [gender, setGender] = useState('Female');
|
||||
@@ -196,7 +194,6 @@ const AddVoiceModal: React.FC<{
|
||||
const handleAudition = () => {
|
||||
if (!testInput.trim()) return;
|
||||
setIsAuditioning(true);
|
||||
// Mocking API Call
|
||||
setTimeout(() => setIsAuditioning(false), 2000);
|
||||
};
|
||||
|
||||
@@ -213,7 +210,6 @@ const AddVoiceModal: React.FC<{
|
||||
};
|
||||
|
||||
onSuccess(newVoice);
|
||||
// Reset
|
||||
setName('');
|
||||
setVendor('硅基流动');
|
||||
setDescription('');
|
||||
@@ -232,7 +228,6 @@ const AddVoiceModal: React.FC<{
|
||||
}
|
||||
>
|
||||
<div className="space-y-4 max-h-[75vh] overflow-y-auto px-1 custom-scrollbar">
|
||||
{/* 1. Vendor Selection (Dropdown) */}
|
||||
<div className="space-y-1.5">
|
||||
<label className="text-[10px] font-black text-muted-foreground uppercase tracking-widest block">厂商 (Vendor)</label>
|
||||
<div className="relative">
|
||||
@@ -252,13 +247,11 @@ const AddVoiceModal: React.FC<{
|
||||
|
||||
<div className="h-px bg-white/5"></div>
|
||||
|
||||
{/* 2. Basic Info */}
|
||||
<div className="space-y-1.5">
|
||||
<label className="text-[10px] font-black text-muted-foreground uppercase tracking-widest block">声音名称</label>
|
||||
<Input value={name} onChange={e => setName(e.target.value)} placeholder="例如: 客服小美" />
|
||||
</div>
|
||||
|
||||
{/* 3. Dynamic Parameters based on Vendor */}
|
||||
{vendor === '硅基流动' ? (
|
||||
<div className="space-y-4 animate-in fade-in slide-in-from-top-1 duration-200">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
@@ -347,7 +340,6 @@ const AddVoiceModal: React.FC<{
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Audition Section */}
|
||||
<div className="p-4 rounded-xl border border-primary/20 bg-primary/5 space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<h4 className="text-[10px] font-black text-primary flex items-center tracking-widest uppercase">
|
||||
@@ -429,7 +421,7 @@ const CloneVoiceModal: React.FC<{
|
||||
>
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium">语音名称</label>
|
||||
<label className="text-sm font-medium text-white">语音名称</label>
|
||||
<Input
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
@@ -438,7 +430,7 @@ const CloneVoiceModal: React.FC<{
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium">上传音频文件 (参考音频)</label>
|
||||
<label className="text-sm font-medium text-white">上传音频文件 (参考音频)</label>
|
||||
<div
|
||||
className="flex flex-col items-center justify-center w-full h-32 rounded-lg border-2 border-dashed border-white/10 bg-white/5 hover:bg-white/10 transition-colors cursor-pointer"
|
||||
onClick={() => inputRef.current?.click()}
|
||||
@@ -465,7 +457,7 @@ const CloneVoiceModal: React.FC<{
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium">语音描述</label>
|
||||
<label className="text-sm font-medium text-white">语音描述</label>
|
||||
<textarea
|
||||
className="flex min-h-[80px] w-full rounded-md border-0 bg-white/5 px-3 py-2 text-sm shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-primary/50 text-white"
|
||||
value={description}
|
||||
|
||||
Reference in New Issue
Block a user