'use client'; import { useState, useEffect, useCallback } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { MagnifyingGlassIcon, EyeIcon, HeartIcon, ArrowDownTrayIcon, SparklesIcon, FunnelIcon, ArrowsUpDownIcon } from '@heroicons/react/24/outline'; import { HeartIcon as HeartIconSolid } from '@heroicons/react/24/solid'; import SkinViewer from '@/components/SkinViewer'; import SkinDetailModal from '@/components/SkinDetailModal'; import { searchTextures, toggleFavorite, type Texture } from '@/lib/api'; import { useAuth } from '@/contexts/AuthContext'; export default function SkinsPage() { const [textures, setTextures] = useState([]); const [searchTerm, setSearchTerm] = useState(''); const [textureType, setTextureType] = useState<'SKIN' | 'CAPE' | 'ALL'>('ALL'); const [sortBy, setSortBy] = useState('最新'); const [isLoading, setIsLoading] = useState(true); const [page, setPage] = useState(1); const [total, setTotal] = useState(0); const [totalPages, setTotalPages] = useState(1); const [favoritedIds, setFavoritedIds] = useState>(new Set()); const [selectedTexture, setSelectedTexture] = useState(null); const [isDetailModalOpen, setIsDetailModalOpen] = useState(false); const { isAuthenticated } = useAuth(); const sortOptions = ['最新', '最热', '最多下载']; const pageSize = 20; // 加载材质数据 const loadTextures = useCallback(async () => { setIsLoading(true); try { console.log('开始加载材质数据,参数:', { searchTerm, textureType, sortBy, page, pageSize }); const response = await searchTextures({ keyword: searchTerm || undefined, type: textureType !== 'ALL' ? textureType : undefined, public_only: true, page, page_size: pageSize, }); console.log('API响应数据:', response); console.log('API响应code:', response.code); console.log('API响应data:', response.data); if (response.code === 200 && response.data) { // 安全地处理数据,避免未定义错误 const textureList = response.data.list || []; const totalCount = response.data.total || 0; const totalPagesCount = response.data.total_pages || 1; console.log('解析后的数据:', { textureList, totalCount, totalPagesCount }); console.log('材质列表长度:', textureList.length); if (textureList.length > 0) { console.log('第一个材质数据:', textureList[0]); console.log('第一个材质URL:', textureList[0].url); } let sortedList = [...textureList]; // 客户端排序 if (sortedList.length > 0) { switch (sortBy) { case '最热': sortedList = sortedList.sort((a, b) => (b.favorite_count || 0) - (a.favorite_count || 0)); break; case '最多下载': sortedList = sortedList.sort((a, b) => (b.download_count || 0) - (a.download_count || 0)); break; default: // 最新 sortedList = sortedList.sort((a, b) => { const dateA = new Date(a.created_at || 0).getTime(); const dateB = new Date(b.created_at || 0).getTime(); return dateB - dateA; }); } } setTextures(sortedList); setTotal(totalCount); setTotalPages(totalPagesCount); console.log('设置状态后的数据:', { sortedListLength: sortedList.length, totalCount, totalPagesCount }); } else { // API返回错误状态 console.warn('API返回错误:', response.message); console.warn('API完整响应:', response); setTextures([]); setTotal(0); setTotalPages(1); } } catch (error) { console.error('加载材质失败:', error); // 发生网络或其他错误时,显示空状态 setTextures([]); setTotal(0); setTotalPages(1); } finally { setIsLoading(false); console.log('加载完成,isLoading设置为false'); } }, [searchTerm, textureType, sortBy, page]); // 处理收藏 const handleFavorite = async (textureId: number) => { if (!isAuthenticated) { alert('请先登录'); return; } try { const response = await toggleFavorite(textureId); if (response.code === 200) { const newFavoritedIds = new Set(favoritedIds); if (favoritedIds.has(textureId)) { newFavoritedIds.delete(textureId); } else { newFavoritedIds.add(textureId); } setFavoritedIds(newFavoritedIds); } } catch (error) { console.error('收藏操作失败:', error); } }; // 处理详细预览 const handleDetailView = (texture: Texture) => { setSelectedTexture(texture); setIsDetailModalOpen(true); }; // 关闭详细预览 const handleCloseDetailModal = () => { setIsDetailModalOpen(false); setSelectedTexture(null); }; // 初始化和搜索 useEffect(() => { loadTextures(); }, [loadTextures]); return (

皮肤库

发现和分享精美的 Minecraft 皮肤和披风,支持 3D 预览和动画效果

{/* Search and Filter Section - 更紧凑的设计 */}
{/* 搜索框 - 更紧凑 */}
setSearchTerm(e.target.value)} onKeyPress={(e) => e.key === 'Enter' && loadTextures()} className="w-full pl-12 pr-4 py-3 border border-gray-200 dark:border-gray-600 rounded-xl focus:ring-2 focus:ring-orange-500 focus:border-orange-500 bg-white/80 dark:bg-gray-700/80 text-gray-900 dark:text-white transition-all duration-200 hover:border-gray-300 dark:hover:border-gray-500" />
{/* 类型筛选 - 更紧凑 */}
{/* 排序 - 更紧凑 */}
{/* 搜索按钮 - 更简洁 */} 搜索
{/* 结果统计 - 更简洁 */}

共找到 {total} 个材质

{totalPages > 1 && (
setPage(p => Math.max(1, p - 1))} disabled={page === 1} className="px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed hover:bg-gray-100 dark:hover:bg-gray-700 transition-all duration-200" whileHover={{ scale: page === 1 ? 1 : 1.05 }} whileTap={{ scale: page === 1 ? 1 : 0.95 }} > 上一页 {page} / {totalPages} setPage(p => Math.min(totalPages, p + 1))} disabled={page === totalPages} className="px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg disabled:opacity-50 disabled:cursor-not-allowed hover:bg-gray-100 dark:hover:bg-gray-700 transition-all duration-200" whileHover={{ scale: page === totalPages ? 1 : 1.05 }} whileTap={{ scale: page === totalPages ? 1 : 0.95 }} > 下一页
)}
{/* Loading State - 保持但简化 */} {isLoading && ( {Array.from({ length: 8 }).map((_, i) => (
))}
)}
{/* Results Grid - 更紧凑 */} {!isLoading && textures.length > 0 && ( {textures.map((texture, index) => { const isFavorited = favoritedIds.has(texture.id); return ( {/* 3D预览区域 - 更紧凑 */}
{/* 悬停操作按钮 */}
handleDetailView(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" whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.9 }} title="详细预览" > window.open(texture.url, '_blank')} className="bg-gradient-to-r from-blue-500 to-blue-600 hover:from-blue-600 hover:to-blue-700 text-white p-3 rounded-full shadow-lg transition-all duration-200" whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.9 }} title="查看原图" >
{/* 标签 */}
{texture.type === 'SKIN' ? '皮肤' : '披风'} {texture.is_slim && ( 细臂 )}
{/* Texture Info */}

{texture.name}

{texture.description && (

{texture.description}

)} {/* Stats */}
{texture.favorite_count} {texture.download_count}
{/* Action Buttons */}
handleDetailView(texture)} className="flex-1 bg-gradient-to-r from-orange-500 to-orange-600 hover:from-orange-600 hover:to-orange-700 text-white text-sm py-2 px-3 rounded-lg transition-all duration-200 font-medium shadow-md hover:shadow-lg flex items-center justify-center" whileHover={{ scale: 1.02 }} whileTap={{ scale: 0.98 }} > 详细预览 handleFavorite(texture.id)} className={`px-3 py-2 border rounded-lg transition-all duration-200 font-medium ${ isFavorited ? 'bg-gradient-to-r from-red-500 to-pink-500 border-transparent text-white shadow-md' : 'border-orange-500 text-orange-500 hover:bg-gradient-to-r hover:from-orange-500 hover:to-orange-600 hover:text-white hover:border-transparent hover:shadow-md' }`} whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }} > {isFavorited ? ( ) : ( )}
); })}
)}
{/* Empty State - 简化 */} {!isLoading && textures.length === 0 && (
🎨

暂无材质

没有找到符合条件的皮肤或披风,试试其他搜索条件吧

)}
{/* 详细预览对话框 */}
); }