修复动漫磁力翻页

This commit is contained in:
mtvpls
2025-12-28 16:15:42 +08:00
parent 20021dce19
commit dd6d2e802c

View File

@@ -2,7 +2,7 @@
'use client';
import { AlertCircle, Download, ExternalLink, Loader2 } from 'lucide-react';
import { useEffect, useState } from 'react';
import { useEffect, useState, useRef, useCallback } from 'react';
import Toast, { ToastProps } from '@/components/Toast';
@@ -35,17 +35,23 @@ export default function AcgSearch({
onError,
}: AcgSearchProps) {
const [loading, setLoading] = useState(false);
const [results, setResults] = useState<AcgSearchResult | null>(null);
const [allItems, setAllItems] = useState<AcgSearchItem[]>([]); // 所有加载的项目
const [error, setError] = useState<string | null>(null);
const [currentPage, setCurrentPage] = useState(1);
const [hasMore, setHasMore] = useState(true);
const [downloadingId, setDownloadingId] = useState<string | null>(null);
const [showNameDialog, setShowNameDialog] = useState(false);
const [selectedItem, setSelectedItem] = useState<AcgSearchItem | null>(null);
const [customName, setCustomName] = useState('');
const [toast, setToast] = useState<ToastProps | null>(null);
const loadMoreRef = useRef<HTMLDivElement>(null);
const isLoadingMoreRef = useRef(false);
// 执行搜索
const performSearch = async (page: number) => {
const performSearch = async (page: number, isLoadMore = false) => {
if (isLoadingMoreRef.current) return;
isLoadingMoreRef.current = true;
setLoading(true);
setError(null);
@@ -67,7 +73,19 @@ export default function AcgSearch({
}
const data: AcgSearchResult = await response.json();
setResults(data);
if (isLoadMore) {
// 追加新数据
setAllItems(prev => [...prev, ...data.items]);
// 如果当前页没有结果,说明没有更多了
setHasMore(data.items.length > 0);
} else {
// 新搜索,重置数据
setAllItems(data.items);
// 如果第一页有结果,假设可能还有更多
setHasMore(data.items.length > 0);
}
setCurrentPage(page);
} catch (err: any) {
const errorMsg = err.message || '搜索失败,请稍后重试';
@@ -75,6 +93,7 @@ export default function AcgSearch({
onError?.(errorMsg);
} finally {
setLoading(false);
isLoadingMoreRef.current = false;
}
};
@@ -89,14 +108,45 @@ export default function AcgSearch({
return;
}
performSearch(1);
// 重置状态并开始新搜索
setAllItems([]);
setCurrentPage(1);
setHasMore(true);
performSearch(1, false);
}, [triggerSearch]);
// 处理页码变化
const handlePageChange = (newPage: number) => {
if (newPage < 1 || loading) return;
performSearch(newPage);
};
// 加载更多数据
const loadMore = useCallback(() => {
if (!loading && hasMore && !isLoadingMoreRef.current) {
performSearch(currentPage + 1, true);
}
}, [loading, hasMore, currentPage]);
// 使用 Intersection Observer 监听滚动到底部
useEffect(() => {
const element = loadMoreRef.current;
if (!element) return;
const observer = new IntersectionObserver(
(entries) => {
const target = entries[0];
if (target.isIntersecting) {
loadMore();
}
},
{
root: null,
rootMargin: '100px',
threshold: 0.1,
}
);
observer.observe(element);
return () => {
observer.unobserve(element);
};
}, [loadMore]);
// 打开命名弹窗
const handleOpenDownloadDialog = (item: AcgSearchItem) => {
@@ -150,7 +200,7 @@ export default function AcgSearch({
}
};
if (loading && !results) {
if (loading && allItems.length === 0) {
return (
<div className='flex items-center justify-center py-12'>
<div className='text-center'>
@@ -174,7 +224,7 @@ export default function AcgSearch({
);
}
if (!results || results.total === 0) {
if (allItems.length === 0) {
return (
<div className='flex items-center justify-center py-12'>
<div className='text-center'>
@@ -189,35 +239,9 @@ export default function AcgSearch({
return (
<div className='space-y-6'>
{/* 搜索结果统计 */}
<div className='flex items-center justify-between'>
<div className='text-sm text-gray-600 dark:text-gray-400'>
<span className='font-semibold text-green-600 dark:text-green-400'>{currentPage}</span>
<span className='font-semibold text-green-600 dark:text-green-400'>{results.total}</span>
</div>
{/* 分页按钮 */}
<div className='flex gap-2'>
<button
onClick={() => handlePageChange(currentPage - 1)}
disabled={currentPage === 1 || loading}
className='px-4 py-2 rounded-lg text-sm font-medium bg-gray-100 text-gray-700 hover:bg-gray-200 disabled:opacity-50 disabled:cursor-not-allowed dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700'
>
</button>
<button
onClick={() => handlePageChange(currentPage + 1)}
disabled={loading || results.total === 0}
className='px-4 py-2 rounded-lg text-sm font-medium bg-gray-100 text-gray-700 hover:bg-gray-200 disabled:opacity-50 disabled:cursor-not-allowed dark:bg-gray-800 dark:text-gray-300 dark:hover:bg-gray-700'
>
</button>
</div>
</div>
{/* 结果列表 */}
<div className='space-y-3'>
{results.items.map((item) => (
{allItems.map((item) => (
<div
key={item.guid}
className='p-4 rounded-lg bg-gray-50 dark:bg-gray-800 border border-gray-200 dark:border-gray-700 hover:border-green-400 dark:hover:border-green-600 transition-colors'
@@ -282,6 +306,18 @@ export default function AcgSearch({
))}
</div>
{/* 加载更多指示器 */}
{hasMore && (
<div ref={loadMoreRef} className='flex items-center justify-center py-8'>
<div className='text-center'>
<Loader2 className='mx-auto h-6 w-6 animate-spin text-green-600 dark:text-green-400' />
<p className='mt-2 text-sm text-gray-600 dark:text-gray-400'>
...
</p>
</div>
</div>
)}
{/* 命名弹窗 */}
{showNameDialog && (
<div className='fixed inset-0 z-[1000] flex items-center justify-center bg-black/50'>