'use client'; import { motion, AnimatePresence } from 'framer-motion'; import { useState } from 'react'; import { EyeIcon, ArrowDownTrayIcon, HeartIcon } from '@heroicons/react/24/outline'; import { HeartIcon as HeartIconSolid } from '@heroicons/react/24/solid'; import SkinViewer from './SkinViewer'; import type { Texture } from '@/lib/api'; interface SkinCardProps { texture: Texture; isFavorited?: boolean; onViewDetails: (texture: Texture) => void; onToggleFavorite?: (textureId: number) => void; onDownload?: (texture: Texture) => void; showVisibilityBadge?: boolean; showActions?: boolean; customActions?: React.ReactNode; index?: number; } export default function SkinCard({ texture, isFavorited = false, onViewDetails, onToggleFavorite, onDownload, showVisibilityBadge = true, showActions = true, customActions, index = 0 }: SkinCardProps) { const [isHovered, setIsHovered] = useState(false); const [imageLoaded, setImageLoaded] = useState(false); const [isDownloading, setIsDownloading] = useState(false); const [isFavoriting, setIsFavoriting] = useState(false); const handleDownload = async () => { if (isDownloading) return; setIsDownloading(true); // 模拟下载延迟 setTimeout(() => { if (onDownload) { onDownload(texture); } else { window.open(texture.url, '_blank'); } setIsDownloading(false); }, 500); }; const handleToggleFavorite = async () => { if (isFavoriting || !onToggleFavorite) return; setIsFavoriting(true); // 模拟收藏操作延迟 setTimeout(() => { onToggleFavorite(texture.id); setIsFavoriting(false); }, 300); }; const getCardVariants = () => ({ hidden: { opacity: 0, y: 50, scale: 0.9, rotateX: -15 }, visible: { opacity: 1, y: 0, scale: 1, rotateX: 0, transition: { duration: 0.6, delay: index * 0.1, type: "spring" as const, stiffness: 100, damping: 15 } }, hover: { scale: 1.03, y: -8, rotateX: 5, transition: { duration: 0.3, } } }); const getActionButtonVariants = () => ({ initial: { scale: 0, opacity: 0 }, hover: { scale: 1, opacity: 1, transition: { duration: 0.2, delay: 0.1, type: "spring" as const , stiffness: 300, damping: 20 } }, tap: { scale: 0.9, transition: { duration: 0.1 } } }); const getTagVariants = () => ({ initial: { scale: 0.8, opacity: 0 }, animate: { scale: 1, opacity: 1, transition: { duration: 0.3, delay: 0.2 + index * 0.05, type: "spring" as const, stiffness: 200, damping: 15 } }, hover: { scale: 1.05, transition: { duration: 0.2 } } }); return ( setIsHovered(true)} onHoverEnd={() => setIsHovered(false)} className="group relative bg-white/80 dark:bg-gray-800/80 backdrop-blur-sm rounded-xl shadow-lg hover:shadow-2xl transition-all duration-300 overflow-hidden border border-white/20 dark:border-gray-700/30" style={{ perspective: '1000px', transformStyle: 'preserve-3d' }} > {/* 3D预览区域 */}
{/* 加载状态 */} {!imageLoaded && ( )} {texture.type === 'SKIN' ? ( setImageLoaded(true)} /> ) : (
🧥

披风

)} {/* 悬停操作按钮 */} {showActions && (
onViewDetails(texture)} className="bg-gradient-to-r from-orange-500 to-orange-600 hover:from-orange-600 hover:to-orange-700 text-white p-3 rounded-full shadow-lg transition-all duration-200 backdrop-blur-sm" title="详细预览" > {onDownload && ( {isDownloading ? ( ) : ( )} )} {onToggleFavorite && ( {isFavoriting ? ( ) : isFavorited ? ( ) : ( )} )}
)} {/* 标签 */}
{texture.type === 'SKIN' ? '皮肤' : '披风'} {texture.is_slim && ( 细臂 )} {showVisibilityBadge && !texture.is_public && ( 私密 )}
{/* 悬停时的光效 */} {isHovered && ( )}
{/* Texture Info */}
{texture.name} {texture.description && ( {texture.description} )} {/* Stats */}
{onToggleFavorite && ( {texture.favorite_count || 0} )} {texture.download_count || 0}
{texture.uploader_id && ( by User #{texture.uploader_id} )}
{/* Custom Actions */} {customActions && ( {customActions} )}
{/* 底部装饰条 */} ); }