import React, { useState, useRef } from 'react'; import { Search, Mic2, Play, Pause, Upload, X, Filter } from 'lucide-react'; import { Button, Input, TableHeader, TableRow, TableHead, TableCell, Dialog, Badge } from '../components/UI'; import { mockVoices } from '../services/mockData'; import { Voice } from '../types'; export const VoiceLibraryPage: React.FC = () => { const [voices, setVoices] = useState(mockVoices); const [searchTerm, setSearchTerm] = useState(''); const [vendorFilter, setVendorFilter] = useState<'all' | 'Ali' | 'Volcano' | 'Minimax'>('all'); const [genderFilter, setGenderFilter] = useState<'all' | 'Male' | 'Female'>('all'); const [langFilter, setLangFilter] = useState<'all' | 'zh' | 'en'>('all'); const [playingVoiceId, setPlayingVoiceId] = useState(null); const [isCloneModalOpen, setIsCloneModalOpen] = useState(false); const filteredVoices = voices.filter(voice => { const matchesSearch = voice.name.toLowerCase().includes(searchTerm.toLowerCase()); const matchesVendor = vendorFilter === 'all' || voice.vendor === vendorFilter; const matchesGender = genderFilter === 'all' || voice.gender === genderFilter; const matchesLang = langFilter === 'all' || voice.language === langFilter; return matchesSearch && matchesVendor && matchesGender && matchesLang; }); const handlePlayToggle = (id: string) => { if (playingVoiceId === id) { setPlayingVoiceId(null); } else { setPlayingVoiceId(id); // Mock auto-stop after 3 seconds setTimeout(() => { setPlayingVoiceId((current) => current === id ? null : current); }, 3000); } }; const handleCloneSuccess = (newVoice: Voice) => { setVoices([newVoice, ...voices]); setIsCloneModalOpen(false); }; return (

声音库

{/* Filter Bar */}
setSearchTerm(e.target.value)} />
声音名称 厂商 性别 语言 试听 {filteredVoices.map(voice => (
{voice.name} {voice.description && {voice.description}}
{voice.vendor} {voice.gender === 'Male' ? '男' : '女'} {voice.language === 'zh' ? '中文' : 'English'}
))} {filteredVoices.length === 0 && ( 暂无声音数据 )}
setIsCloneModalOpen(false)} onSuccess={handleCloneSuccess} />
); }; const CloneVoiceModal: React.FC<{ isOpen: boolean; onClose: () => void; onSuccess: (voice: Voice) => void }> = ({ isOpen, onClose, onSuccess }) => { const [name, setName] = useState(''); const [description, setDescription] = useState(''); const [file, setFile] = useState(null); const inputRef = useRef(null); const handleFileChange = (e: React.ChangeEvent) => { if (e.target.files && e.target.files[0]) { setFile(e.target.files[0]); } }; const handleSubmit = () => { if (!name || !file) { alert("请填写名称并上传音频文件"); return; } // Mock creation const newVoice: Voice = { id: `v-${Date.now()}`, name: name, vendor: 'Volcano', // Default for cloned voices gender: 'Female', // Mock default language: 'zh', description: description || 'User cloned voice' }; onSuccess(newVoice); // Reset setName(''); setDescription(''); setFile(null); }; return ( } >
setName(e.target.value)} placeholder="给新声音起个名字" />
inputRef.current?.click()} > {file ? (
{file.name}
) : ( <>

点击上传 WAV/MP3 文件

)}