diff --git a/src/views/Community/components/DynamicNewsCard.js b/src/views/Community/components/DynamicNewsCard.js
index c59e0078..16c2f9b4 100644
--- a/src/views/Community/components/DynamicNewsCard.js
+++ b/src/views/Community/components/DynamicNewsCard.js
@@ -107,7 +107,8 @@ const DynamicNewsCard = forwardRef(({
isAccumulateMode, // 新增:是否累积模式
handlePageChange,
handleModeToggle,
- loadNextPage // 新增:加载下一页
+ loadNextPage, // 新增:加载下一页
+ loadPrevPage // 新增:加载上一页
} = usePagination({
allCachedEvents,
total,
@@ -244,6 +245,7 @@ const DynamicNewsCard = forwardRef(({
displayEvents={displayEvents} // 新增:累积显示的事件列表
isAccumulateMode={isAccumulateMode} // 新增:是否累积模式
loadNextPage={loadNextPage} // 新增:加载下一页
+ loadPrevPage={loadPrevPage} // 新增:加载上一页
onFourRowEventClick={handleFourRowEventClick} // 新增:四排模式事件点击
selectedEvent={selectedEvent}
onEventSelect={setSelectedEvent}
diff --git a/src/views/Community/components/DynamicNewsCard/EventScrollList.js b/src/views/Community/components/DynamicNewsCard/EventScrollList.js
index d47db8c2..10a8cc44 100644
--- a/src/views/Community/components/DynamicNewsCard/EventScrollList.js
+++ b/src/views/Community/components/DynamicNewsCard/EventScrollList.js
@@ -46,6 +46,7 @@ const EventScrollList = ({
displayEvents, // 累积显示的事件列表(四排模式用)
isAccumulateMode, // 是否累积模式
loadNextPage, // 加载下一页(无限滚动)
+ loadPrevPage, // 加载上一页(双向无限滚动)
onFourRowEventClick, // 四排模式事件点击回调(打开弹窗)
selectedEvent,
onEventSelect,
@@ -270,7 +271,7 @@ const EventScrollList = ({
)}
- {/* 模式3: 四排网格模式 - 使用虚拟滚动 + 无限滚动 */}
+ {/* 模式3: 四排网格模式 - 使用虚拟滚动 + 双向无限滚动 */}
{mode === 'four-row' && (
@@ -289,7 +291,7 @@ const EventScrollList = ({
{/* 模式4: 纵向分栏模式 - 横向布局(时间在左,卡片在右) */}
{mode === 'vertical' && (
- {/* 左侧:事件列表 (33.3%) - 使用虚拟滚动 + 无限滚动 */}
+ {/* 左侧:事件列表 (33.3%) - 使用虚拟滚动 + 双向无限滚动 */}
diff --git a/src/views/Community/components/DynamicNewsCard/VirtualizedFourRowGrid.js b/src/views/Community/components/DynamicNewsCard/VirtualizedFourRowGrid.js
index 9b941c75..74bb6d74 100644
--- a/src/views/Community/components/DynamicNewsCard/VirtualizedFourRowGrid.js
+++ b/src/views/Community/components/DynamicNewsCard/VirtualizedFourRowGrid.js
@@ -34,11 +34,13 @@ const VirtualizedFourRowGrid = ({
getTimelineBoxStyle,
borderColor,
loadNextPage,
+ loadPrevPage, // 新增:加载上一页
hasMore,
loading,
}) => {
const parentRef = useRef(null);
const isLoadingMore = useRef(false); // 防止重复加载
+ const previousScrollHeight = useRef(0); // 记录加载前的滚动高度(用于位置保持)
// 滚动条颜色(主题适配)
const scrollbarTrackBg = useColorModeValue('#f1f1f1', '#2D3748');
@@ -62,30 +64,42 @@ const VirtualizedFourRowGrid = ({
overscan: 2, // 预加载2行(上下各1行)
});
- // 无限滚动逻辑 - 监听滚动事件,到达底部时加载下一页
+ // 双向无限滚动逻辑 - 监听滚动事件,到达底部加载下一页,到达顶部加载上一页
useEffect(() => {
const scrollElement = parentRef.current;
- if (!scrollElement || !loadNextPage) return;
+ if (!scrollElement) return;
const handleScroll = async () => {
// 防止重复触发
- if (isLoadingMore.current || !hasMore || loading) return;
+ if (isLoadingMore.current || loading) return;
const { scrollTop, scrollHeight, clientHeight } = scrollElement;
const scrollPercentage = (scrollTop + clientHeight) / scrollHeight;
- // 滚动到 60% 时开始加载下一页(降低阈值,更早触发)
- if (scrollPercentage > 0.6) {
- console.log('%c📜 [无限滚动] 到达底部,加载下一页', 'color: #8B5CF6; font-weight: bold;');
+ // 向下滚动:滚动到 60% 时开始加载下一页
+ if (loadNextPage && hasMore && scrollPercentage > 0.6) {
+ console.log('%c📜 [双向滚动] 到达底部,加载下一页', 'color: #8B5CF6; font-weight: bold;');
isLoadingMore.current = true;
await loadNextPage();
isLoadingMore.current = false;
}
+
+ // 向上滚动:滚动到顶部 10% 以内时加载上一页
+ if (loadPrevPage && scrollTop < clientHeight * 0.1) {
+ console.log('%c📜 [双向滚动] 到达顶部,加载上一页', 'color: #10B981; font-weight: bold;');
+ isLoadingMore.current = true;
+
+ // 记录加载前的滚动高度(用于位置保持)
+ previousScrollHeight.current = scrollHeight;
+
+ await loadPrevPage();
+ isLoadingMore.current = false;
+ }
};
scrollElement.addEventListener('scroll', handleScroll);
return () => scrollElement.removeEventListener('scroll', handleScroll);
- }, [loadNextPage, hasMore, loading]);
+ }, [loadNextPage, loadPrevPage, hasMore, loading]);
// 主动检测内容高度 - 如果内容不足以填满容器,主动加载下一页
useEffect(() => {
@@ -116,6 +130,36 @@ const VirtualizedFourRowGrid = ({
return () => clearTimeout(timer);
}, [events.length, hasMore, loading, loadNextPage]);
+ // 滚动位置保持 - 加载上一页后,调整 scrollTop 使用户看到的内容位置不变
+ useEffect(() => {
+ const scrollElement = parentRef.current;
+ if (!scrollElement || previousScrollHeight.current === 0) return;
+
+ // 延迟执行,确保虚拟滚动已重新渲染并测量了新高度
+ const timer = setTimeout(() => {
+ const currentScrollHeight = scrollElement.scrollHeight;
+ const heightDifference = currentScrollHeight - previousScrollHeight.current;
+
+ // 如果高度增加了(说明上一页数据已加载),调整滚动位置
+ if (heightDifference > 0) {
+ console.log('%c📜 [位置保持] 调整滚动位置', 'color: #10B981; font-weight: bold;', {
+ previousHeight: previousScrollHeight.current,
+ currentHeight: currentScrollHeight,
+ heightDifference,
+ newScrollTop: scrollElement.scrollTop + heightDifference
+ });
+
+ // 调整 scrollTop,使用户看到的内容位置不变
+ scrollElement.scrollTop += heightDifference;
+
+ // 重置记录
+ previousScrollHeight.current = 0;
+ }
+ }, 300);
+
+ return () => clearTimeout(timer);
+ }, [events.length]); // 监听 events 变化,加载上一页后会增加 events 数量
+
// 底部加载指示器
const renderLoadingIndicator = () => {
if (!hasMore) {
diff --git a/src/views/Community/components/DynamicNewsCard/hooks/usePagination.js b/src/views/Community/components/DynamicNewsCard/hooks/usePagination.js
index 7c734a6d..2ac070fb 100644
--- a/src/views/Community/components/DynamicNewsCard/hooks/usePagination.js
+++ b/src/views/Community/components/DynamicNewsCard/hooks/usePagination.js
@@ -53,8 +53,8 @@ export const usePagination = ({ allCachedEvents, total, cachedCount, dispatch, t
// 检查是否还有更多数据
const hasMore = cachedCount < total;
- // 判断是否使用累积模式(四排模式)
- const isAccumulateMode = mode === DISPLAY_MODES.FOUR_ROW;
+ // 判断是否使用累积模式(四排模式 + 纵向模式)
+ const isAccumulateMode = mode === DISPLAY_MODES.FOUR_ROW || mode === DISPLAY_MODES.VERTICAL;
// 从缓存中切片获取当前页数据(过滤 null 占位符)
const currentPageEvents = useMemo(() => {
@@ -282,6 +282,29 @@ export const usePagination = ({ allCachedEvents, total, cachedCount, dispatch, t
}
}, [currentPage, totalPages, loadingPage, handlePageChange]);
+ // 加载上一页(用于双向无限滚动)
+ const loadPrevPage = useCallback(async () => {
+ if (currentPage <= 1 || loadingPage !== null) {
+ logger.debug('DynamicNewsCard', '无法加载上一页', {
+ currentPage,
+ loadingPage,
+ reason: currentPage <= 1 ? '已是第一页' : '正在加载中'
+ });
+ return Promise.resolve(false); // 已经是第一页或正在加载
+ }
+
+ const prevPage = currentPage - 1;
+ logger.debug('DynamicNewsCard', '懒加载:加载上一页', { currentPage, prevPage });
+
+ try {
+ await handlePageChange(prevPage);
+ return true;
+ } catch (error) {
+ logger.error('DynamicNewsCard', '懒加载上一页失败', error, { prevPage });
+ return false;
+ }
+ }, [currentPage, loadingPage, handlePageChange]);
+
// 模式切换处理
const handleModeToggle = useCallback((newMode) => {
if (newMode === mode) return;
@@ -337,6 +360,7 @@ export const usePagination = ({ allCachedEvents, total, cachedCount, dispatch, t
// 方法
handlePageChange,
handleModeToggle,
- loadNextPage // 新增:加载下一页(用于无限滚动)
+ loadNextPage, // 新增:加载下一页(用于无限滚动)
+ loadPrevPage // 新增:加载上一页(用于双向无限滚动)
};
};