99 lines
3.4 KiB
TypeScript
99 lines
3.4 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="flex flex-col gap-3 min-w-[200px] w-full grow basis-0 group"
|
|||
|
|
onMouseEnter={() => onHover(true)}
|
|||
|
|
onMouseLeave={() => onHover(false)}
|
|||
|
|
>
|
|||
|
|
{/* 角色图片 */}
|
|||
|
|
<div
|
|||
|
|
className="pb-[133%] bg-center bg-cover bg-no-repeat rounded-lg relative overflow-hidden"
|
|||
|
|
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="absolute top-2 right-2 z-[1] bg-surface-element-dark-normal rounded-xs backdrop-blur-xs w-6 h-6 flex items-center justify-center">
|
|||
|
|
<i className={cn("!text-[12px] iconfont", {
|
|||
|
|
'icon-eye-off': isPrivate,
|
|||
|
|
'icon-eye-on': !isPrivate,
|
|||
|
|
})} />
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* 底部遮罩层 */}
|
|||
|
|
<div className={cn("absolute bottom-0 left-0 right-0 bg-gradient-to-b from-transparent to-black pb-3 px-3 flex flex-col justify-end", {
|
|||
|
|
'pt-6': !isHovered,
|
|||
|
|
'pt-12 h-full': isHovered,
|
|||
|
|
})}>
|
|||
|
|
{/* 描述文字 - hover时显示,带过渡动效 */}
|
|||
|
|
<div
|
|||
|
|
className={cn("text-white txt-body-m transition-all duration-300 ease-in-out transform line-clamp-6 overflow-hidden mb-2", {
|
|||
|
|
'max-h-0 opacity-0': !isHovered,
|
|||
|
|
'max-h-max h-full opacity-100': isHovered,
|
|||
|
|
})}
|
|||
|
|
>
|
|||
|
|
{character.introduction || ''}
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* 点赞数 - 暂时用固定值,实际应该从API获取 */}
|
|||
|
|
<div className="flex items-center gap-1 h-6 px-1 py-0.5">
|
|||
|
|
{/* <img src={likeIcon} alt="点赞" className="w-3 h-3" /> */}
|
|||
|
|
<i className="iconfont icon-Like-fill" />
|
|||
|
|
<span className="text-white text-xs font-medium">{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;
|