crush-level-web/src/app/(main)/user/[userId]/components/AlbumItem.tsx

138 lines
4.6 KiB
TypeScript
Raw Normal View History

2025-11-13 08:38:25 +00:00
import { Tag } from "@/components/ui/tag";
import { cn, formatNumberToKMB } from "@/lib/utils";
import { IAlbumItem, LikedStatus, LockStatus } from "@/services/user";
import Image from "next/image";
import { useState } from "react";
import { useAIUser } from "../context/aiUser";
import { IconButton } from "@/components/ui/button";
import AlbumItemAction from "./AlbumItemAction";
import { formatFromCents } from "@/utils/number";
interface AlbumItemProps {
item: IAlbumItem;
onLike: (albumId: number, isLiked: boolean) => void;
onImageClick: () => void;
}
const AlbumItem = ({ item, onLike, onImageClick }: AlbumItemProps) => {
const [imageLoading, setImageLoading] = useState(true);
const { isOwner } = useAIUser();
const handleLike = () => {
onLike(item.albumId, item.likedStatus === LikedStatus.Liked);
};
const renderTag = () => {
if (item.isDefault) {
return null;
}
if (isOwner) {
if (item.lockStatus) {
return (
<Tag variant="dark" className="absolute top-2 right-2 p-[6px]" size="small">
<i className="iconfont icon-private !text-[12px] leading-none" />
</Tag>
)
}
}
if (item.lockStatus === LockStatus.Unlock) {
return (
<Tag variant="default" className="absolute top-2 right-2 p-[6px] bg-primary-gradient-normal" size="small">
<i className="iconfont icon-public !text-[12px] leading-none" />
</Tag>
);
}
return null;
};
const renderOverlay = () => {
// 如果是自己的相册,则不显示解锁按钮
if (isOwner) {
return null;
}
if (item.lockStatus === LockStatus.Lock) {
return (
<div
className="absolute inset-0 flex flex-col items-center justify-center gap-3 cursor-pointer"
onClick={(e) => {
// e.stopPropagation();
// handleUnlock();
}}
>
<i className="iconfont icon-private-border !text-[24px] leading-none" />
<div className="flex items-center gap-2">
<div className="flex items-center gap-1">
<div className="w-4 h-4 relative">
<Image src="/icons/diamond.svg" alt="diamond" fill className="object-contain" />
</div>
<span className="text-white text-sm font-semibold">{formatFromCents(item.unlockPrice || 0)}</span>
</div>
<div className="txt-label-m text-txt-primary-normal">Unlock</div>
</div>
</div>
);
}
return null;
};
const renderDefaultTag = () => {
if (item.isDefault) {
return (
<Tag variant="dark" className="absolute top-2 left-2" size="small">Default</Tag>
);
}
return null;
}
return (
<div className="relative pb-[134%] rounded-2xl overflow-hidden cursor-pointer group">
<div className="absolute inset-0" onClick={onImageClick}>
{/* 背景图片 */}
<div className="relative w-full h-full">
<Image
src={item.imgUrl || item.img1}
alt="Album image"
fill
className={cn(
"object-cover object-top transition-opacity duration-300",
imageLoading ? "opacity-0" : "opacity-100"
)}
onLoadingComplete={() => setImageLoading(false)}
sizes="(max-width: 768px) 50vw, 176px"
/>
{imageLoading && (
<div className="absolute inset-0 bg-surface-nest-normal animate-pulse" />
)}
</div>
{renderDefaultTag()}
{/* 标签 */}
{renderTag()}
{/* 付费内容遮罩 */}
{renderOverlay()}
{/* 底部操作区 */}
<div className="absolute bottom-2 left-2 right-2 flex items-center justify-between" onClick={(e) => e.stopPropagation()}>
{/* 点赞按钮 */}
{(item.lockStatus !== LockStatus.Lock || isOwner) && <div className="backdrop-blur-lg pr-1 bg-surface-element-dark-normal rounded-full flex items-center gap-[2px]">
<IconButton variant="ghost" size="xs" onClick={handleLike}>
{item.likedStatus === LikedStatus.Liked ? (
<i className="iconfont icon-Like-fill !text-[16px] leading-none !text-important-normal" />
) : (
<i className="iconfont icon-Like !text-[16px] leading-none" />
)}
</IconButton>
<span className="text-white txt-numMonotype-xs">{formatNumberToKMB(item.likedCount ?? 0)}</span>
</div>}
<AlbumItemAction data={item} />
</div>
</div>
</div>
);
};
export default AlbumItem;