107 lines
3.5 KiB
TypeScript
107 lines
3.5 KiB
TypeScript
import { Tag } from '@/components/ui/tag'
|
||
import { cn, formatNumberToKMB } from '@/lib/utils'
|
||
import { AIPermission, AiUserBaseListOutput } from '@/services/create'
|
||
import Image from 'next/image'
|
||
import Link from 'next/link'
|
||
|
||
const CharacterCard = ({
|
||
character,
|
||
isHovered,
|
||
onHover,
|
||
}: {
|
||
character: AiUserBaseListOutput
|
||
isHovered: boolean
|
||
onHover: (hovered: boolean) => void
|
||
}) => {
|
||
// 根据权限判断是否私密
|
||
const isPrivate = character.permission === AIPermission.Private
|
||
|
||
return (
|
||
<Link href={`/@${character.aiId}`}>
|
||
<div
|
||
className="group flex w-full min-w-[200px] grow basis-0 flex-col gap-3"
|
||
onMouseEnter={() => onHover(true)}
|
||
onMouseLeave={() => onHover(false)}
|
||
>
|
||
{/* 角色图片 */}
|
||
<div
|
||
className="relative overflow-hidden rounded-lg bg-cover bg-center bg-no-repeat pb-[133%]"
|
||
style={
|
||
{
|
||
// backgroundImage: character.homeImageUrl ? `url('${character.homeImageUrl}')` : 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)'
|
||
}
|
||
}
|
||
>
|
||
<Image
|
||
src={character.homeImageUrl ?? ''}
|
||
alt={character.nickname ?? ''}
|
||
fill
|
||
className="object-cover object-top"
|
||
sizes="100%"
|
||
/>
|
||
|
||
{/* 私密标识 */}
|
||
<div className="bg-surface-element-dark-normal absolute top-2 right-2 z-[1] flex h-6 w-6 items-center justify-center rounded-xs backdrop-blur-xs">
|
||
<i
|
||
className={cn('iconfont !text-[12px]', {
|
||
'icon-eye-off': isPrivate,
|
||
'icon-eye-on': !isPrivate,
|
||
})}
|
||
/>
|
||
</div>
|
||
|
||
{/* 底部遮罩层 */}
|
||
<div
|
||
className={cn(
|
||
'absolute right-0 bottom-0 left-0 flex flex-col justify-end bg-gradient-to-b from-transparent to-black px-3 pb-3',
|
||
{
|
||
'pt-6': !isHovered,
|
||
'h-full pt-12': isHovered,
|
||
}
|
||
)}
|
||
>
|
||
{/* 描述文字 - hover时显示,带过渡动效 */}
|
||
<div
|
||
className={cn(
|
||
'txt-body-m mb-2 line-clamp-6 transform overflow-hidden text-white transition-all duration-300 ease-in-out',
|
||
{
|
||
'max-h-0 opacity-0': !isHovered,
|
||
'h-full max-h-max opacity-100': isHovered,
|
||
}
|
||
)}
|
||
>
|
||
{character.introduction || ''}
|
||
</div>
|
||
|
||
{/* 点赞数 - 暂时用固定值,实际应该从API获取 */}
|
||
<div className="flex h-6 items-center gap-1 px-1 py-0.5">
|
||
{/* <img src={likeIcon} alt="点赞" className="w-3 h-3" /> */}
|
||
<i className="iconfont icon-Like-fill" />
|
||
<span className="text-xs font-medium text-white">
|
||
{formatNumberToKMB(character.likedNum ?? 0)}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 角色信息 */}
|
||
<div className="flex flex-col gap-2">
|
||
{/* 角色名称 */}
|
||
<div className="txt-title-m truncate">{character.nickname}</div>
|
||
|
||
{/* 标签 */}
|
||
<div className="flex flex-wrap gap-1">
|
||
{/* 性格标签 */}
|
||
{character.characterName && <Tag size="small">{character.characterName}</Tag>}
|
||
|
||
{/* 标签 */}
|
||
{character.tagName && <Tag size="small">{character.tagName}</Tag>}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</Link>
|
||
)
|
||
}
|
||
|
||
export default CharacterCard
|