diff --git a/src/views/Community/components/DynamicNewsCard.js b/src/views/Community/components/DynamicNewsCard.js index da1d0013..4977b870 100644 --- a/src/views/Community/components/DynamicNewsCard.js +++ b/src/views/Community/components/DynamicNewsCard.js @@ -57,30 +57,21 @@ const usePagination = ({ allCachedEvents, total, cachedCount, dispatch, toast }) return allCachedEvents.slice(startIndex, endIndex).filter(event => event !== null); }, [allCachedEvents, currentPage, pageSize]); - // 翻页处理(智能预加载) - const handlePageChange = useCallback(async (newPage) => { - // 🔍 诊断日志 - 记录翻页开始状态 - console.log('[handlePageChange] 开始翻页', { - currentPage, - newPage, - pageSize, - totalPages, - hasMore, - total, - allCachedEventsLength: allCachedEvents.length, - cachedCount - }); - - // 0. 首先检查目标页数据是否已完整缓存 - const targetPageStartIndex = (newPage - 1) * pageSize; + /** + * 子函数1: 检查目标页缓存状态 + * @param {number} targetPage - 目标页码 + * @returns {Object} { isTargetPageCached, targetPageInfo } + */ + const checkTargetPageCache = useCallback((targetPage) => { + const targetPageStartIndex = (targetPage - 1) * pageSize; const targetPageEndIndex = targetPageStartIndex + pageSize; const targetPageData = allCachedEvents.slice(targetPageStartIndex, targetPageEndIndex); const validTargetData = targetPageData.filter(e => e !== null); const expectedCount = Math.min(pageSize, total - targetPageStartIndex); const isTargetPageCached = validTargetData.length >= expectedCount; - console.log('[handlePageChange] 目标页缓存检查', { - newPage, + console.log('[checkTargetPageCache] 目标页缓存检查', { + targetPage, targetPageStartIndex, targetPageEndIndex, targetPageDataLength: targetPageData.length, @@ -89,32 +80,63 @@ const usePagination = ({ allCachedEvents, total, cachedCount, dispatch, toast }) isTargetPageCached }); - // 1. 判断翻页类型:连续翻页(上一页/下一页)还是跳转翻页(点击页码/输入跳转) - const isSequentialNavigation = Math.abs(newPage - currentPage) === 1; + return { + isTargetPageCached, + targetPageInfo: { + startIndex: targetPageStartIndex, + endIndex: targetPageEndIndex, + validCount: validTargetData.length, + expectedCount + } + }; + }, [allCachedEvents, pageSize, total]); + + /** + * 子函数2: 计算预加载范围 + * @param {number} targetPage - 目标页码 + * @param {number} fromPage - 来源页码 + * @returns {Array} 预加载页码数组 + */ + const calculatePreloadRange = useCallback((targetPage, fromPage) => { + const isSequentialNavigation = Math.abs(targetPage - fromPage) === 1; - // 2. 计算预加载范围 let preloadRange; if (isSequentialNavigation) { // 连续翻页:前后各2页(共5页) - const start = Math.max(1, newPage - 2); - const end = Math.min(totalPages, newPage + 2); + const start = Math.max(1, targetPage - 2); + const end = Math.min(totalPages, targetPage + 2); preloadRange = Array.from( { length: end - start + 1 }, (_, i) => start + i ); } else { // 跳转翻页:只加载当前页 - preloadRange = [newPage]; + preloadRange = [targetPage]; } - // 3. 检查哪些页面的数据还未缓存(检查是否包含 null 或超出数组长度) + console.log('[calculatePreloadRange] 计算预加载范围', { + targetPage, + fromPage, + isSequentialNavigation, + preloadRange + }); + + return preloadRange; + }, [totalPages]); + + /** + * 子函数3: 查找缺失页面 + * @param {Array} preloadRange - 预加载范围 + * @returns {Array} 缺失页码数组 + */ + const findMissingPages = useCallback((preloadRange) => { const missingPages = preloadRange.filter(page => { const pageStartIndex = (page - 1) * pageSize; const pageEndIndex = pageStartIndex + pageSize; // 如果该页超出数组范围,说明未缓存 if (pageEndIndex > allCachedEvents.length) { - console.log(`[missingPages] 页面${page}超出数组范围`, { + console.log(`[findMissingPages] 页面${page}超出数组范围`, { pageStartIndex, pageEndIndex, allCachedEventsLength: allCachedEvents.length @@ -128,7 +150,7 @@ const usePagination = ({ allCachedEvents, total, cachedCount, dispatch, toast }) const expectedCount = Math.min(pageSize, total - pageStartIndex); const hasNullOrIncomplete = validData.length < expectedCount; - console.log(`[missingPages] 页面${page}检查`, { + console.log(`[findMissingPages] 页面${page}检查`, { pageStartIndex, pageEndIndex, pageDataLength: pageData.length, @@ -140,120 +162,137 @@ const usePagination = ({ allCachedEvents, total, cachedCount, dispatch, toast }) return hasNullOrIncomplete; }); - console.log('[handlePageChange] 缺失页面检测完成', { + console.log('[findMissingPages] 缺失页面检测完成', { preloadRange, missingPages, missingPagesCount: missingPages.length }); - // 4. 如果目标页已缓存,立即切换页码,然后在后台静默预加载其他页 - if (isTargetPageCached && missingPages.length > 0 && hasMore) { - console.log('[DynamicNewsCard] 目标页已缓存,立即切换', { - currentPage, - newPage, - 缺失页面: missingPages, - 目标页已缓存: true - }); + return missingPages; + }, [allCachedEvents, pageSize, total]); - // 立即切换页码(用户无感知延迟) - setCurrentPage(newPage); - - // 在后台静默预加载其他缺失页面(拆分为单页请求) - try { - console.log('[DynamicNewsCard] 开始后台预加载', { - 缺失页面: missingPages, - 每页数量: pageSize - }); - - // 拆分为单页请求,避免 per_page 动态值导致后端返回空数据 - for (const page of missingPages) { - await dispatch(fetchDynamicNews({ - page: page, - per_page: pageSize, // 固定值(5或10),不使用动态计算 - pageSize: pageSize, - clearCache: false - })).unwrap(); - - console.log(`[DynamicNewsCard] 后台预加载第 ${page} 页完成`); - } - - console.log('[DynamicNewsCard] 后台预加载全部完成', { - 预加载页面: missingPages - }); - } catch (error) { - console.error('[DynamicNewsCard] 后台预加载失败', error); - // 静默失败,不影响用户体验 - } - - return; // 提前返回,不执行下面的加载逻辑 + /** + * 子函数4: 加载页面数据 + * @param {Array} missingPages - 缺失页码数组 + * @param {number} targetPage - 目标页码 + * @param {boolean} silentMode - 静默模式(后台预加载) + * @returns {Promise} 是否加载成功 + */ + const loadPages = useCallback(async (missingPages, targetPage, silentMode = false) => { + if (!silentMode) { + // 显示 loading 状态 + setLoadingPage(targetPage); } - // 5. 如果目标页未缓存,显示 loading 并等待加载完成 - if (missingPages.length > 0 && hasMore) { - console.log('[DynamicNewsCard] 目标页未缓存,显示loading', { - currentPage, - newPage, - 翻页类型: isSequentialNavigation ? '连续翻页' : '跳转翻页', - 预加载范围: preloadRange, - 缺失页面: missingPages, - 每页数量: pageSize, - 目标页已缓存: false + try { + console.log(`[loadPages] 开始加载`, { + missingPages, + targetPage, + silentMode, + pageSize }); - try { - // 设置加载状态(显示"正在加载第X页...") - setLoadingPage(newPage); + // 拆分为单页请求,避免 per_page 动态值导致后端返回空数据 + for (const page of missingPages) { + console.log(`[loadPages] 开始加载第 ${page} 页`); - // 拆分为单页请求,避免 per_page 动态值导致后端返回空数据 - for (const page of missingPages) { - console.log(`[DynamicNewsCard] 开始加载第 ${page} 页`); + await dispatch(fetchDynamicNews({ + page: page, + per_page: pageSize, // 固定值(5或10),不使用动态计算 + pageSize: pageSize, + clearCache: false + })).unwrap(); - await dispatch(fetchDynamicNews({ - page: page, - per_page: pageSize, // 固定值(5或10),不使用动态计算 - pageSize: pageSize, // 传递原始 pageSize,用于正确计算索引 - clearCache: false - })).unwrap(); + console.log(`[loadPages] 第 ${page} 页加载完成`); + } - console.log(`[DynamicNewsCard] 第 ${page} 页加载完成`); - } + console.log('[loadPages] 所有页面加载完成', { + missingPages, + silentMode + }); - console.log('[DynamicNewsCard] 所有缺失页面加载完成', { - 缺失页面: missingPages - }); + return true; + } catch (error) { + console.error('[loadPages] 加载失败', error); - // 数据加载成功后才更新当前页码 - setCurrentPage(newPage); - } catch (error) { - console.error('[DynamicNewsCard] 翻页加载失败', error); - - // 显示错误提示 + if (!silentMode) { + // 非静默模式下显示错误提示 toast({ title: '加载失败', - description: `无法加载第 ${newPage} 页数据,请稍后重试`, + description: `无法加载第 ${targetPage} 页数据,请稍后重试`, status: 'error', duration: 3000, isClosable: true, position: 'top' }); + } - // 加载失败时不更新页码,保持在当前页 - } finally { + return false; + } finally { + if (!silentMode) { // 清除加载状态 setLoadingPage(null); } + } + }, [dispatch, pageSize, toast]); + + // 翻页处理(智能预加载)- 使用子函数重构 + const handlePageChange = useCallback(async (newPage) => { + // 🔍 诊断日志 - 记录翻页开始状态 + console.log('[handlePageChange] 开始翻页', { + currentPage, + newPage, + pageSize, + totalPages, + hasMore, + total, + allCachedEventsLength: allCachedEvents.length, + cachedCount + }); + + // 步骤1: 检查目标页缓存状态 + const { isTargetPageCached } = checkTargetPageCache(newPage); + + // 步骤2: 计算预加载范围 + const preloadRange = calculatePreloadRange(newPage, currentPage); + + // 步骤3: 查找缺失页面 + const missingPages = findMissingPages(preloadRange); + + // 步骤4: 根据情况加载数据 + if (isTargetPageCached && missingPages.length > 0 && hasMore) { + // 场景A: 目标页已缓存,立即切换,后台静默预加载其他页 + console.log('[handlePageChange] 目标页已缓存,立即切换 + 后台预加载', { + currentPage, + newPage, + 缺失页面: missingPages + }); + + setCurrentPage(newPage); + await loadPages(missingPages, newPage, true); // 静默模式 + } else if (missingPages.length > 0 && hasMore) { + // 场景B: 目标页未缓存,显示 loading 并等待加载完成 + console.log('[handlePageChange] 目标页未缓存,显示 loading', { + currentPage, + newPage, + 缺失页面: missingPages + }); + + const success = await loadPages(missingPages, newPage, false); // 非静默模式 + if (success) { + setCurrentPage(newPage); + } } else if (missingPages.length === 0) { - // 只有在确实不需要加载时才直接切换 + // 场景C: 所有页面均已缓存,直接切换 console.log('[handlePageChange] 无需加载,直接切换', { currentPage, newPage, - preloadRange, - missingPages, reason: '所有页面均已缓存' }); + setCurrentPage(newPage); } else { - // 理论上不应该到这里(missingPages.length > 0 但 hasMore=false) + // 场景D: 意外分支(有缺失页面但 hasMore=false) console.warn('[handlePageChange] 意外分支:有缺失页面但无法加载', { missingPages, hasMore, @@ -263,7 +302,6 @@ const usePagination = ({ allCachedEvents, total, cachedCount, dispatch, toast }) cachedCount }); - // 尝试切换页码,但可能会显示空数据 setCurrentPage(newPage); toast({ @@ -275,7 +313,20 @@ const usePagination = ({ allCachedEvents, total, cachedCount, dispatch, toast }) position: 'top' }); } - }, [currentPage, allCachedEvents, pageSize, totalPages, hasMore, dispatch, total, toast, cachedCount]); + }, [ + currentPage, + pageSize, + totalPages, + hasMore, + total, + allCachedEvents.length, + cachedCount, + checkTargetPageCache, + calculatePreloadRange, + findMissingPages, + loadPages, + toast + ]); // 模式切换处理 const handleModeToggle = useCallback((newMode) => {