crush-level-web/src/app/(main)/chat/[id]/Sider/VoiceActor.tsx

168 lines
5.1 KiB
TypeScript

'use client';
import { useChatStore } from '../store';
import { useState } from 'react';
import { Checkbox } from '@/components/ui/checkbox';
import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';
import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar';
type VoiceGender = 'all' | 'male' | 'female';
type VoiceActorItem = {
id: number;
name: string;
description: string;
avatarUrl: string;
gender: 'male' | 'female';
};
export default function VoiceActor() {
const setSideBar = useChatStore((store) => store.setSideBar);
// 语音演员列表(静态数据)
const voiceActors: VoiceActorItem[] = [
{
id: 1,
name: 'Voice Actor 1',
description: 'Have a role-playing conversation with AI',
avatarUrl: 'https://i.pravatar.cc/150?img=1',
gender: 'female',
},
{
id: 2,
name: 'Voice Actor 2',
description: 'Have a role-playing conversation with AI',
avatarUrl: 'https://i.pravatar.cc/150?img=2',
gender: 'female',
},
{
id: 3,
name: 'Voice Actor 3',
description: 'Have a role-playing conversation with AI',
avatarUrl: 'https://i.pravatar.cc/150?img=3',
gender: 'male',
},
{
id: 4,
name: 'Voice Actor 4',
description: 'Have a role-playing conversation with AI',
avatarUrl: 'https://i.pravatar.cc/150?img=4',
gender: 'female',
},
{
id: 5,
name: 'Voice Actor 5',
description: 'Have a role-playing conversation with AI',
avatarUrl: 'https://i.pravatar.cc/150?img=5',
gender: 'male',
},
{
id: 6,
name: 'Voice Actor 6',
description: 'Have a role-playing conversation with AI',
avatarUrl: 'https://i.pravatar.cc/150?img=6',
gender: 'female',
},
{
id: 7,
name: 'Voice Actor 7',
description: 'Have a role-playing conversation with AI',
avatarUrl: 'https://i.pravatar.cc/150?img=7',
gender: 'male',
},
];
const [selectedGender, setSelectedGender] = useState<VoiceGender>('all');
const [selectedActorId, setSelectedActorId] = useState(1);
const [loading, setLoading] = useState(false);
// 根据性别过滤演员列表
const filteredActors = voiceActors.filter((actor) => {
if (selectedGender === 'all') return true;
return actor.gender === selectedGender;
});
const handleConfirm = async () => {
setLoading(true);
try {
// TODO: 调用实际的 API 保存语音演员设置
// await updateVoiceActor({ voiceActorId: selectedActorId })
console.log('Selected voice actor:', selectedActorId);
// 模拟延迟
await new Promise((resolve) => setTimeout(resolve, 500));
setSideBar('profile');
} catch (error) {
console.error(error);
} finally {
setLoading(false);
}
};
return (
<div className="flex h-full flex-col">
{/* Gender Tabs */}
<div className="mb-6 flex gap-6">
{[
{ value: 'all' as const, label: 'All' },
{ value: 'male' as const, label: 'Male' },
{ value: 'female' as const, label: 'Female' },
].map((tab) => (
<button
key={tab.value}
className={cn(
'txt-title-s relative pb-2 transition-colors',
selectedGender === tab.value ? 'text-txt-primary-normal' : 'text-txt-secondary-normal'
)}
onClick={() => setSelectedGender(tab.value)}
>
{tab.label}
{selectedGender === tab.value && (
<div className="bg-primary-normal absolute bottom-0 left-0 right-0 h-0.5 rounded-full" />
)}
</button>
))}
</div>
{/* Voice Actor List */}
<div className="flex-1 overflow-y-auto">
<div className="flex flex-col gap-3">
{filteredActors.map((actor) => (
<div
key={actor.id}
className={cn(
'bg-surface-element-normal flex cursor-pointer items-center gap-3 rounded-lg p-4 transition-colors',
selectedActorId === actor.id && 'bg-surface-element-hover'
)}
onClick={() => setSelectedActorId(actor.id)}
>
<Avatar className="h-14 w-14">
<AvatarImage src={actor.avatarUrl} alt={actor.name} />
<AvatarFallback>{actor.name.charAt(0)}</AvatarFallback>
</Avatar>
<div className="flex-1">
<div className="txt-title-s text-txt-primary-normal">{actor.name}</div>
<div className="txt-body-s text-txt-secondary-normal mt-1">{actor.description}</div>
</div>
<Checkbox shape="round" checked={selectedActorId === actor.id} />
</div>
))}
</div>
</div>
{/* Footer Buttons */}
{/* <div className="mt-6 flex justify-end gap-3">
<Button variant="tertiary" size="large" onClick={() => setSideBar('profile')}>
Cancel
</Button>
<Button size="large" variant="primary" loading={loading} onClick={handleConfirm}>
Select
</Button>
</div> */}
</div>
);
}