diff --git a/src/app/page.tsx b/src/app/page.tsx index 64fa537..51aeec2 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -82,6 +82,21 @@ function HomeClient() { const [loading, setLoading] = useState(true); const { announcement } = useSite(); + // 分页状态管理 + const [moviePage, setMoviePage] = useState(0); + const [tvShowPage, setTvShowPage] = useState(0); + const [varietyShowPage, setVarietyShowPage] = useState(0); + const [loadingMore, setLoadingMore] = useState({ + movies: false, + tvShows: false, + varietyShows: false, + }); + const [hasMoreData, setHasMoreData] = useState({ + movies: true, + tvShows: true, + varietyShows: true, + }); + const [showAnnouncement, setShowAnnouncement] = useState(false); // 检查公告弹窗状态 @@ -148,6 +163,100 @@ function HomeClient() { fetchDoubanData(); }, []); + // 加载更多电影 + const loadMoreMovies = async () => { + if (loadingMore.movies || !hasMoreData.movies) return; + + setLoadingMore(prev => ({ ...prev, movies: true })); + try { + const nextPage = moviePage + 1; + const moviesData = await getDoubanCategories({ + kind: 'movie', + category: '热门', + type: '全部', + pageStart: nextPage * 20, + pageLimit: 20, + }); + + if (moviesData.code === 200 && moviesData.list.length > 0) { + setHotMovies(prev => [...prev, ...moviesData.list]); + setMoviePage(nextPage); + // 如果返回的数据少于请求的数量,说明没有更多数据了 + if (moviesData.list.length < 20) { + setHasMoreData(prev => ({ ...prev, movies: false })); + } + } else { + setHasMoreData(prev => ({ ...prev, movies: false })); + } + } catch (error) { + // 静默处理错误 + } finally { + setLoadingMore(prev => ({ ...prev, movies: false })); + } + }; + + // 加载更多剧集 + const loadMoreTvShows = async () => { + if (loadingMore.tvShows || !hasMoreData.tvShows) return; + + setLoadingMore(prev => ({ ...prev, tvShows: true })); + try { + const nextPage = tvShowPage + 1; + const tvShowsData = await getDoubanCategories({ + kind: 'tv', + category: 'tv', + type: 'tv', + pageStart: nextPage * 20, + pageLimit: 20, + }); + + if (tvShowsData.code === 200 && tvShowsData.list.length > 0) { + setHotTvShows(prev => [...prev, ...tvShowsData.list]); + setTvShowPage(nextPage); + if (tvShowsData.list.length < 20) { + setHasMoreData(prev => ({ ...prev, tvShows: false })); + } + } else { + setHasMoreData(prev => ({ ...prev, tvShows: false })); + } + } catch (error) { + // 静默处理错误 + } finally { + setLoadingMore(prev => ({ ...prev, tvShows: false })); + } + }; + + // 加载更多综艺 + const loadMoreVarietyShows = async () => { + if (loadingMore.varietyShows || !hasMoreData.varietyShows) return; + + setLoadingMore(prev => ({ ...prev, varietyShows: true })); + try { + const nextPage = varietyShowPage + 1; + const varietyShowsData = await getDoubanCategories({ + kind: 'tv', + category: 'show', + type: 'show', + pageStart: nextPage * 20, + pageLimit: 20, + }); + + if (varietyShowsData.code === 200 && varietyShowsData.list.length > 0) { + setHotVarietyShows(prev => [...prev, ...varietyShowsData.list]); + setVarietyShowPage(nextPage); + if (varietyShowsData.list.length < 20) { + setHasMoreData(prev => ({ ...prev, varietyShows: false })); + } + } else { + setHasMoreData(prev => ({ ...prev, varietyShows: false })); + } + } catch (error) { + // 静默处理错误 + } finally { + setLoadingMore(prev => ({ ...prev, varietyShows: false })); + } + }; + // 处理收藏数据更新的函数 const updateFavoriteItems = async (allFavorites: Record) => { const allPlayRecords = await getAllPlayRecords(); @@ -292,7 +401,12 @@ function HomeClient() { - + {loading ? // 加载状态显示灰色占位数据 (显示10个,2行x5列) Array.from({ length: 10 }).map((_, index) => ( @@ -340,7 +454,12 @@ function HomeClient() { - + {loading ? // 加载状态显示灰色占位数据 (显示10个,2行x5列) Array.from({ length: 10 }).map((_, index) => ( @@ -387,7 +506,12 @@ function HomeClient() { - + {loading ? // 加载状态显示灰色占位数据 (显示10个,2行x5列) Array.from({ length: 10 }).map((_, index) => ( diff --git a/src/components/PaginatedRow.tsx b/src/components/PaginatedRow.tsx index 8567663..3beb5f4 100644 --- a/src/components/PaginatedRow.tsx +++ b/src/components/PaginatedRow.tsx @@ -7,12 +7,18 @@ interface PaginatedRowProps { children: React.ReactNode[]; itemsPerPage?: number; className?: string; + onLoadMore?: () => Promise; // 新增:加载更多数据的回调函数 + hasMoreData?: boolean; // 新增:是否还有更多数据可加载 + isLoading?: boolean; // 新增:是否正在加载中 } export default function PaginatedRow({ children, itemsPerPage = 10, className = '', + onLoadMore, + hasMoreData = true, + isLoading = false, }: PaginatedRowProps) { const [startIndex, setStartIndex] = useState(0); const [isHovered, setIsHovered] = useState(false); @@ -40,19 +46,32 @@ export default function PaginatedRow({ }); }; - // 向后翻页 - 真正的无限浏览 - const handleNextPage = () => { - setStartIndex((prev) => { - const newIndex = prev + itemsPerPage; - // 当超出总长度时,从头开始,实现无限循环 - return newIndex >= children.length ? 0 : newIndex; - }); + // 向后翻页 - 支持动态加载更多数据 + const handleNextPage = async () => { + const newIndex = startIndex + itemsPerPage; + + // 如果即将超出当前数据范围,且有更多数据可加载,且有加载回调函数 + if (newIndex >= children.length && hasMoreData && onLoadMore && !isLoading) { + try { + await onLoadMore(); // 加载更多数据 + // 加载完成后,直接设置到下一页 + setStartIndex(newIndex); + } catch (error) { + // 静默处理加载错误,保持用户体验 + } + } else if (newIndex < children.length) { + // 如果还在当前数据范围内,直接翻页 + setStartIndex(newIndex); + } else { + // 如果没有更多数据可加载,循环回到第一页 + setStartIndex(0); + } }; // 检查是否可以向前翻页 const canGoPrev = startIndex > 0; - // 总是可以向后翻页(无限循环) - const canGoNext = children.length > itemsPerPage; + // 检查是否可以向后翻页:有更多数据或者当前不在最后一页 + const canGoNext = children.length > itemsPerPage && (startIndex + itemsPerPage < children.length || hasMoreData || startIndex + itemsPerPage >= children.length); // 如果没有足够的内容需要分页,就不显示按钮 const needsPagination = children.length > itemsPerPage; @@ -88,20 +107,25 @@ export default function PaginatedRow({ )} - {/* 右箭头按钮 - 总是显示,支持无限循环 */} + {/* 右箭头按钮 - 总是显示,支持动态加载 */} {canGoNext && ( )}