"use client" import { useState, useEffect, useCallback, useRef } from "react" import Image from "next/image" import { Card } from "@/components/ui/card" import { Badge } from "@/components/ui/badge" import { Button } from "@/components/ui/button" import { Calendar, MapPin, Grid, List, Eye, Heart } from "lucide-react" interface Photo { id: number src: string title: string description: string category: string tags: string[] date: string exif: { camera: string lens: string settings: string location: string } } interface PhotoGalleryProps { photos: Photo[] onPhotoClick: (photo: Photo) => void } type ViewMode = 'grid' | 'masonry' | 'list' export function PhotoGallery({ photos, onPhotoClick }: PhotoGalleryProps) { const [loadedImages, setLoadedImages] = useState>(new Set()) const [isHydrated, setIsHydrated] = useState(false) const [viewMode, setViewMode] = useState('grid') const [page, setPage] = useState(1) const [displayedPhotos, setDisplayedPhotos] = useState([]) const [isLoading, setIsLoading] = useState(false) const [hasMore, setHasMore] = useState(true) const loaderRef = useRef(null) const photosPerPage = 12 useEffect(() => { setIsHydrated(true) }, []) // 初始化显示的照片 useEffect(() => { if (photos.length > 0) { setDisplayedPhotos(photos.slice(0, photosPerPage)) setHasMore(photos.length > photosPerPage) setPage(1) } }, [photos]) // 加载更多照片 const loadMorePhotos = useCallback(() => { if (isLoading || !hasMore) return setIsLoading(true) // 模拟API加载延迟 setTimeout(() => { const nextPage = page + 1 const startIndex = (nextPage - 1) * photosPerPage const endIndex = startIndex + photosPerPage const newPhotos = photos.slice(startIndex, endIndex) if (newPhotos.length > 0) { setDisplayedPhotos(prev => [...prev, ...newPhotos]) setPage(nextPage) setHasMore(endIndex < photos.length) } else { setHasMore(false) } setIsLoading(false) }, 500) }, [page, photos, isLoading, hasMore, photosPerPage]) // 滚动检测 useEffect(() => { const observer = new IntersectionObserver( ([entry]) => { if (entry.isIntersecting && hasMore && !isLoading) { loadMorePhotos() } }, { threshold: 0.1 } ) if (loaderRef.current) { observer.observe(loaderRef.current) } return () => observer.disconnect() }, [hasMore, isLoading, loadMorePhotos]) const handleImageLoad = (photoId: number) => { setLoadedImages((prev) => new Set(prev).add(photoId)) } const getGridClasses = () => { switch (viewMode) { case 'grid': return 'grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6' case 'masonry': return 'columns-1 md:columns-2 lg:columns-3 xl:columns-4 gap-6 space-y-6' case 'list': return 'space-y-6' default: return 'grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6' } } const renderPhotoCard = (photo: Photo) => { const isListView = viewMode === 'list' return ( onPhotoClick(photo)} >
{isHydrated && !loadedImages.has(photo.id) && (
)} {photo.title} handleImageLoad(photo.id)} />
{/* Hover overlay */}
点击查看
{/* Card content */}

{photo.title}

{isListView && (

{photo.description}

)}
{photo.tags.slice(0, isListView ? 4 : 2).map((tag) => ( {tag} ))}
{photo.date}
{photo.exif.location}
{photo.exif.camera} • {photo.exif.settings}
) } return (
{/* 视图模式切换 */}
显示 {displayedPhotos.length} / {photos.length} 张照片
{/* 照片网格 */}
{displayedPhotos.map(renderPhotoCard)}
{/* 加载更多 */} {hasMore && (
{isLoading ? (
正在加载更多...
) : ( )}
)} {/* 无更多照片 */} {!hasMore && displayedPhotos.length > photosPerPage && (
已显示全部 {photos.length} 张照片
)}
) }