删除相册相关的API、页面和样式文件,以简化代码结构并移除不再使用的功能。

This commit is contained in:
宇阳
2025-12-22 10:45:29 +08:00
parent c2b0d6c87f
commit 9cc462d4f4
6 changed files with 0 additions and 343 deletions

View File

@@ -1,9 +0,0 @@
import { Photo, Cate } from '@/types/app/album'
import Request from '@/utils/request'
// 分页获取相册列表
export const getAlbumCatePagingAPI = (page: number = 1, size: number = 10) => Request<Paginate<Cate[]>>('POST', `/album/cate/paging?page=${page}&size=${size}`)
// 获取指定相册中的所有照片
export const getImagesByAlbumIdAPI = (id: number, page: number = 1, size: number = 10) =>
Request<Paginate<Photo[]>>('GET', `/album/cate/${id}/images?page=${page}&size=${size}`)

View File

@@ -1,20 +0,0 @@
.masonry-grid {
display: flex;
width: auto;
margin-left: -16px;
}
.masonry-grid_column {
padding-left: 16px;
background-clip: padding-box;
}
/* 可选:添加响应式间距 */
@media (max-width: 700px) {
.masonry-grid {
margin-left: -8px;
}
.masonry-grid_column {
padding-left: 8px;
}
}

View File

@@ -1,214 +0,0 @@
'use client';
import { useEffect, useState, useCallback } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { IoChevronBack, IoChevronForward } from 'react-icons/io5';
import { BsCalendar } from 'react-icons/bs';
import { Photo } from '@/types/app/album';
import { getImagesByAlbumIdAPI } from '@/api/album';
import Masonry from 'react-masonry-css';
import Empty from '@/components/Empty';
import dayjs from 'dayjs';
import './page.scss';
const breakpointColumnsObj = {
default: 4,
1024: 3,
700: 2,
};
interface Props {
params: Promise<{ id: number }>;
searchParams: Promise<{ page: number; name: string }>;
}
export default function AlbumPage(props: Props) {
const [list, setList] = useState<Photo[]>([]);
const [currentPhotoIndex, setCurrentPhotoIndex] = useState<number | null>(null);
const [showModal, setShowModal] = useState(false);
const [isImageLoading, setIsImageLoading] = useState(false);
const [page, setPage] = useState(1);
const [hasMore, setHasMore] = useState(true);
const [loading, setLoading] = useState(false);
const [albumName, setAlbumName] = useState('');
const [albumId, setAlbumId] = useState<number>(0);
useEffect(() => {
const initData = async () => {
const searchParams = await props.searchParams;
const params = await props.params;
setAlbumName(searchParams.name);
setAlbumId(params.id);
await getImagesByAlbumId(params.id);
};
initData();
}, [props.searchParams, props.params]);
const getImagesByAlbumId = async (id: number, page: number = 1, isLoadMore: boolean = false) => {
try {
setLoading(true);
const response = await getImagesByAlbumIdAPI(id, page);
if (!response) return;
const { data } = response;
if (isLoadMore) {
setList((prev) => [...prev, ...data.result]);
} else {
setList(data.result);
}
setHasMore(data.result.length === 10);
} catch (error) {
console.error('Failed to fetch images:', error);
} finally {
setLoading(false);
}
};
const handleScroll = useCallback(() => {
if (loading || !hasMore) return;
const scrollHeight = document.documentElement.scrollHeight;
const scrollTop = document.documentElement.scrollTop;
const clientHeight = document.documentElement.clientHeight;
if (scrollHeight - scrollTop - clientHeight < 300) {
setPage((prev) => prev + 1);
getImagesByAlbumId(albumId, page + 1, true);
}
}, [loading, hasMore, page, albumId]);
useEffect(() => {
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, [handleScroll]);
const openPhoto = async (index: number) => {
setCurrentPhotoIndex(index);
setIsImageLoading(true);
const img = new Image();
img.src = list[index].image;
await new Promise((resolve) => {
img.onload = () => {
resolve(true);
};
});
setIsImageLoading(false);
setShowModal(true);
};
const closeModal = () => {
setShowModal(false);
setCurrentPhotoIndex(null);
};
const nextPhoto = () => {
if (currentPhotoIndex !== null) {
setCurrentPhotoIndex((currentPhotoIndex + 1) % list.length);
}
};
const prevPhoto = () => {
if (currentPhotoIndex !== null) {
setCurrentPhotoIndex((currentPhotoIndex - 1 + list.length) % list.length);
}
};
return (
<>
<title>{`📷 ${albumName} - 照片墙`}</title>
<meta name="description" content={`📷 ${albumName} - 照片墙`} />
<div className="container mx-auto px-4 py-8 pt-[90px]">
{/* 移除最大高度限制 */}
<div className="w-full">
{list?.length === 0 ? (
<Empty info="暂无照片" />
) : (
<>
<Masonry breakpointCols={breakpointColumnsObj} className="masonry-grid" columnClassName="masonry-grid_column">
{list?.map((photo, index) => (
<motion.div key={photo.id} initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.5, delay: index * 0.1 }} className="relative group overflow-hidden rounded-lg shadow-lg mb-6" onClick={() => openPhoto(index)}>
<div className="w-full cursor-pointer">
<img src={photo.image || 'https://images.unsplash.com/photo-1501785888041-af3ef285b470?ixlib=rb-1.2.1&auto=format&fit=crop&w=3840&q=100'} alt={photo.name} className="w-full h-auto object-cover transform transition-transform group-hover:scale-110" />
</div>
<div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/70 to-transparent p-4">
<h3 className="text-white font-medium text-lg">{photo.name}</h3>
</div>
</motion.div>
))}
</Masonry>
{loading && (
<div className="flex justify-center items-center py-4">
<div className="animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-gray-900"></div>
</div>
)}
{!hasMore && list.length > 0 && <div className="text-center text-gray-500 py-4"></div>}
</>
)}
</div>
{/* 照片查看模态框 */}
<AnimatePresence>
{showModal && currentPhotoIndex !== null && (
<motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }} className="fixed inset-0 backdrop-blur-md bg-black/30 flex items-center justify-center z-50" onClick={closeModal}>
<motion.div initial={{ opacity: 0, scale: 0.8 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0.8 }} transition={{ duration: 0.3, ease: 'easeInOut' }} className="relative max-w-4xl w-full mx-4" onClick={(e) => e.stopPropagation()}>
<div className="relative rounded-2xl overflow-hidden">
{isImageLoading && (
<div className="absolute inset-0 flex items-center justify-center bg-black/50">
<div className="animate-spin rounded-full h-12 w-12 border-t-2 border-b-2 border-white"></div>
</div>
)}
<motion.div key={currentPhotoIndex} initial={{ opacity: 0, scale: 0.9 }} animate={{ opacity: 1, scale: 1 }} exit={{ opacity: 0, scale: 0.9 }} transition={{ duration: 0.3, ease: 'easeInOut' }} className="relative">
<img src={list[currentPhotoIndex].image} alt={list[currentPhotoIndex].name} className="w-full h-auto max-h-[80vh] rounded-2xl object-cover" />
{/* 导航按钮 */}
<button
className="flex justify-center items-center absolute left-4 top-1/2 z-10 -translate-y-1/2 p-2 rounded-full bg-[#fff3] hover:bg-black/50 backdrop-blur-md duration-200"
onClick={(e) => {
e.stopPropagation();
prevPhoto();
}}
>
<IoChevronBack className="w-8 h-8 text-white" />
</button>
<button
className="flex justify-center items-center absolute right-4 top-1/2 z-10 -translate-y-1/2 p-2 rounded-full bg-[#fff3] hover:bg-black/10 backdrop-blur-md duration-200"
onClick={(e) => {
e.stopPropagation();
nextPhoto();
}}
>
<IoChevronForward className="w-8 h-8 text-white" />
</button>
{/* 照片信息 */}
<div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/70 to-transparent p-6">
<motion.div key={currentPhotoIndex} initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.3, delay: 0.2 }}>
<h3 className="text-white text-2xl font-medium mb-2">{list[currentPhotoIndex].name}</h3>
<p className="text-white/50 leading-relaxed mb-3">{list[currentPhotoIndex].description}</p>
<div className="flex items-center space-x-2 text-gray-400">
<BsCalendar className="w-4 h-4 text-gray-400" />
<p className="text-sm">{dayjs(+list[currentPhotoIndex].createTime).format('YYYY-MM-DD HH:mm')}</p>
</div>
</motion.div>
</div>
</motion.div>
</div>
</motion.div>
</motion.div>
)}
</AnimatePresence>
</div>
</>
);
}

View File

@@ -1,20 +0,0 @@
.masonry-grid {
display: flex;
width: auto;
margin-left: -16px;
}
.masonry-grid_column {
padding-left: 16px;
background-clip: padding-box;
}
/* 可选:添加响应式间距 */
@media (max-width: 700px) {
.masonry-grid {
margin-left: -8px;
}
.masonry-grid_column {
padding-left: 8px;
}
}

View File

@@ -1,64 +0,0 @@
'use client';
import { useEffect, useState } from 'react';
import { motion } from 'framer-motion';
import { useRouter } from 'next/navigation';
import { Cate } from '@/types/app/album';
import { getAlbumCatePagingAPI } from '@/api/album';
import Masonry from 'react-masonry-css';
import './page.scss';
const breakpointColumnsObj = {
default: 4,
1024: 3,
700: 2,
};
export default function AlbumPage() {
const router = useRouter();
const [list, setList] = useState<Cate[]>([]);
const getAlbumCatePaging = async () => {
const { data } = (await getAlbumCatePagingAPI(1, 9999)) || { data: {} as Paginate<Cate[]> };
setList(data.result);
};
useEffect(() => {
getAlbumCatePaging();
}, []);
const handleClick = (data: Cate) => {
router.push(`/album/${data.id}?name=${data.name}`);
};
return (
<>
<title>📷 </title>
<meta name="description" content="📷 照片墙" />
<div className="container mx-auto px-4 py-8 pt-[90px]">
<Masonry breakpointCols={breakpointColumnsObj} className="masonry-grid mb-12" columnClassName="masonry-grid_column">
{list.map((cate, index) => (
<motion.div key={cate.id} initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.5, delay: index * 0.1 }} className="mb-6">
<div className="relative group overflow-hidden rounded-lg shadow-lg cursor-pointer" onClick={() => handleClick(cate)}>
{/* 图片容器 */}
<div className="aspect-w-1 aspect-h-1 w-full">
<img src={cate.cover || 'https://images.unsplash.com/photo-1501785888041-af3ef285b470?ixlib=rb-1.2.1&auto=format&fit=crop&w=3840&q=100'} alt={cate.name} className="w-full h-full object-cover transform transition-transform group-hover:scale-110" />
</div>
{/* 分类标签 */}
<div className="absolute top-4 left-4 bg-black/20 backdrop-blur-md text-white px-3 py-1 rounded-full text-sm">{cate.name}</div>
{/* 标题遮罩 */}
<div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/70 to-transparent p-4">
<h3 className="text-white font-medium text-lg">{cate.name}</h3>
</div>
</div>
</motion.div>
))}
</Masonry>
</div>
</>
);
}

View File

@@ -1,16 +0,0 @@
// 定义照片类型
export interface Photo {
id?: number;
name: string;
description: string;
image: string;
cateId: number;
createTime: number;
}
interface Cate {
id?: number;
name: string;
cover: string;
images: string[];
}