feat: 实现 Socket 触发的智能列表自动刷新功能(带防抖)

核心改动:
- 扩展 NotificationContext,添加事件更新回调注册机制
- VirtualizedFourRowGrid 添加 forwardRef 暴露 getScrollPosition 方法
- DynamicNewsCard 实现智能刷新逻辑(根据模式和滚动位置判断是否刷新)
- Community 页面注册 Socket 回调自动触发刷新
- 创建 TypeScript 通用防抖工具函数(debounce.ts)
- 集成防抖机制(2秒延迟),避免短时间内频繁请求

智能刷新策略:
- 纵向模式 + 第1页:自动刷新列表
- 纵向模式 + 其他页:不刷新(避免打断用户)
- 平铺模式 + 滚动在顶部:自动刷新列表
- 平铺模式 + 滚动不在顶部:仅显示 Toast 提示

防抖效果:
- 短时间内收到多个新事件,只执行最后一次刷新
- 减少服务器压力,提升用户体验

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
zdl
2025-11-14 19:04:00 +08:00
parent 9fd618c087
commit ddd6b2d4af
7 changed files with 382 additions and 9 deletions

View File

@@ -1,7 +1,7 @@
// src/views/Community/components/DynamicNewsCard/VirtualizedFourRowGrid.js
// 虚拟化网格组件(支持多列布局 + 纵向滚动 + 无限滚动)
import React, { useRef, useMemo, useEffect } from 'react';
import React, { useRef, useMemo, useEffect, forwardRef, useImperativeHandle } from 'react';
import { useVirtualizer } from '@tanstack/react-virtual';
import { Box, Grid, Spinner, Text, VStack, Center, HStack, IconButton, useBreakpointValue } from '@chakra-ui/react';
import { RepeatIcon } from '@chakra-ui/icons';
@@ -25,7 +25,7 @@ import DynamicNewsEventCard from '../EventCard/DynamicNewsEventCard';
* @param {boolean} props.hasMore - 是否还有更多数据
* @param {boolean} props.loading - 加载状态
*/
const VirtualizedFourRowGrid = ({
const VirtualizedFourRowGrid = forwardRef(({
display = 'block',
events,
columnsPerRow = 4,
@@ -42,7 +42,7 @@ const VirtualizedFourRowGrid = ({
loading,
error, // 新增:错误状态
onRetry, // 新增:重试回调
}) => {
}, ref) => {
const parentRef = useRef(null);
const isLoadingMore = useRef(false); // 防止重复加载
const lastRefreshTime = useRef(0); // 记录上次刷新时间用于30秒防抖
@@ -81,6 +81,31 @@ const VirtualizedFourRowGrid = ({
overscan: 2, // 预加载2行上下各1行
});
/**
* ⚡ 暴露方法给父组件(用于 Socket 刷新判断)
*/
useImperativeHandle(ref, () => ({
/**
* 获取当前滚动位置信息
* @returns {Object|null} 滚动位置信息
*/
getScrollPosition: () => {
const scrollElement = parentRef.current;
if (!scrollElement) return null;
const { scrollTop, scrollHeight, clientHeight } = scrollElement;
const isNearTop = scrollTop < clientHeight * 0.1; // 顶部 10% 区域
return {
scrollTop,
scrollHeight,
clientHeight,
isNearTop,
scrollPercentage: ((scrollTop + clientHeight) / scrollHeight) * 100,
};
},
}), []);
/**
* 【核心逻辑1】无限滚动 + 顶部刷新 - 监听滚动事件,根据滚动位置自动加载数据或刷新
*
@@ -360,6 +385,8 @@ const VirtualizedFourRowGrid = ({
</Box>
</Box>
);
};
});
VirtualizedFourRowGrid.displayName = 'VirtualizedFourRowGrid';
export default VirtualizedFourRowGrid;