feat: 实现动态新闻筛选功能并优化虚拟滚动
## 主要改进
### 1. 修复筛选功能
- **问题**: 筛选触发了 API 请求但列表未更新
- **根因**: fetchDynamicNews 硬编码 sort: 'new',未支持筛选参数
- **解决**:
- Redux action 添加筛选参数支持 (sort, importance, q, date_range, industry_code)
- DynamicNewsCard 监听 filters 变化并重新请求数据
- 筛选时清空缓存并从第1页开始加载
### 2. 虚拟滚动优化
- 改造 VirtualizedFourRowGrid 支持多列布局
- 添加 columnsPerRow prop (默认4列,传1实现单列)
- 添加 CardComponent prop (支持不同卡片组件)
- 单列模式使用更小的 gap 间距
- 纵向模式使用虚拟滚动 + 无限滚动
- 左侧事件列表使用 VirtualizedFourRowGrid (columnsPerRow=1)
- 使用 HorizontalDynamicNewsEventCard 横向卡片
- 支持滚动到底部自动加载
### 3. UI 交互优化
- 默认模式改为纵向模式 (左侧列表 + 右侧详情)
- 四排/纵向模式不显示全局 loading 遮罩
- 四排模式弹窗在关闭时不渲染 (性能优化)
- 注释掉单排/双排按钮,只保留纵向和平铺模式
## 技术细节
**数据流**:
```
用户筛选 → updateFilters → filters state 更新
→ DynamicNewsCard useEffect 监听
→ dispatch(fetchDynamicNews({ ...filters, clearCache: true }))
→ API 请求(带筛选参数)
→ Redux state 更新 → 列表重新渲染
```
**虚拟滚动**:
- @tanstack/react-virtual 动态高度测量
- 80% 滚动深度触发无限加载
- 底部 loading 指示器(绝对定位)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -164,6 +164,11 @@ export const fetchHotEvents = createAsyncThunk(
|
|||||||
* @param {number} params.per_page - 每页数量
|
* @param {number} params.per_page - 每页数量
|
||||||
* @param {boolean} params.clearCache - 是否清空缓存(默认 false)
|
* @param {boolean} params.clearCache - 是否清空缓存(默认 false)
|
||||||
* @param {boolean} params.prependMode - 是否追加到头部(用于定时刷新,默认 false)
|
* @param {boolean} params.prependMode - 是否追加到头部(用于定时刷新,默认 false)
|
||||||
|
* @param {string} params.sort - 排序方式(new/hot)
|
||||||
|
* @param {string} params.importance - 重要性筛选(all/1/2/3/4/5)
|
||||||
|
* @param {string} params.q - 搜索关键词
|
||||||
|
* @param {string} params.date_range - 时间范围
|
||||||
|
* @param {string} params.industry_code - 行业代码
|
||||||
*/
|
*/
|
||||||
export const fetchDynamicNews = createAsyncThunk(
|
export const fetchDynamicNews = createAsyncThunk(
|
||||||
'communityData/fetchDynamicNews',
|
'communityData/fetchDynamicNews',
|
||||||
@@ -172,20 +177,34 @@ export const fetchDynamicNews = createAsyncThunk(
|
|||||||
per_page = 5,
|
per_page = 5,
|
||||||
pageSize = 5, // 每页实际显示的数据量(用于计算索引)
|
pageSize = 5, // 每页实际显示的数据量(用于计算索引)
|
||||||
clearCache = false,
|
clearCache = false,
|
||||||
prependMode = false
|
prependMode = false,
|
||||||
|
sort = 'new',
|
||||||
|
importance,
|
||||||
|
q,
|
||||||
|
date_range,
|
||||||
|
industry_code
|
||||||
} = {}, { rejectWithValue }) => {
|
} = {}, { rejectWithValue }) => {
|
||||||
try {
|
try {
|
||||||
|
// 构建筛选参数
|
||||||
|
const filters = {};
|
||||||
|
if (sort) filters.sort = sort;
|
||||||
|
if (importance && importance !== 'all') filters.importance = importance;
|
||||||
|
if (q) filters.q = q;
|
||||||
|
if (date_range) filters.date_range = date_range;
|
||||||
|
if (industry_code) filters.industry_code = industry_code;
|
||||||
|
|
||||||
logger.debug('CommunityData', '开始获取动态新闻', {
|
logger.debug('CommunityData', '开始获取动态新闻', {
|
||||||
page,
|
page,
|
||||||
per_page,
|
per_page,
|
||||||
clearCache,
|
clearCache,
|
||||||
prependMode
|
prependMode,
|
||||||
|
filters
|
||||||
});
|
});
|
||||||
|
|
||||||
const response = await eventService.getEvents({
|
const response = await eventService.getEvents({
|
||||||
page,
|
page,
|
||||||
per_page,
|
per_page,
|
||||||
sort: 'new'
|
...filters
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.success && response.data?.events) {
|
if (response.success && response.data?.events) {
|
||||||
|
|||||||
@@ -131,11 +131,36 @@ const DynamicNewsCard = forwardRef(({
|
|||||||
page: PAGINATION_CONFIG.INITIAL_PAGE,
|
page: PAGINATION_CONFIG.INITIAL_PAGE,
|
||||||
per_page: PAGINATION_CONFIG.CAROUSEL_PAGE_SIZE,
|
per_page: PAGINATION_CONFIG.CAROUSEL_PAGE_SIZE,
|
||||||
pageSize: PAGINATION_CONFIG.CAROUSEL_PAGE_SIZE, // 传递 pageSize 确保索引计算一致
|
pageSize: PAGINATION_CONFIG.CAROUSEL_PAGE_SIZE, // 传递 pageSize 确保索引计算一致
|
||||||
clearCache: true
|
clearCache: true,
|
||||||
|
...filters // 应用初始筛选条件
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}, [dispatch, allCachedEvents.length]);
|
}, [dispatch, allCachedEvents.length]);
|
||||||
|
|
||||||
|
// 监听筛选条件变化 - 清空缓存并重新请求数据
|
||||||
|
useEffect(() => {
|
||||||
|
// 跳过初始加载(由上面的 useEffect 处理)
|
||||||
|
if (!hasInitialized.current) return;
|
||||||
|
|
||||||
|
console.log('%c🔍 [筛选] 筛选条件改变,重新请求数据', 'color: #8B5CF6; font-weight: bold;', filters);
|
||||||
|
|
||||||
|
// 筛选条件改变时,清空缓存并从第1页开始加载
|
||||||
|
dispatch(fetchDynamicNews({
|
||||||
|
page: PAGINATION_CONFIG.INITIAL_PAGE,
|
||||||
|
per_page: pageSize,
|
||||||
|
pageSize: pageSize,
|
||||||
|
clearCache: true, // 清空缓存
|
||||||
|
...filters // 应用新的筛选条件
|
||||||
|
}));
|
||||||
|
}, [
|
||||||
|
filters.sort,
|
||||||
|
filters.importance,
|
||||||
|
filters.q,
|
||||||
|
filters.date_range,
|
||||||
|
filters.industry_code,
|
||||||
|
dispatch
|
||||||
|
]); // 只监听筛选参数的变化,不监听 page
|
||||||
|
|
||||||
// 自动选中逻辑 - 只在首次加载时自动选中第一个事件,翻页时不自动选中
|
// 自动选中逻辑 - 只在首次加载时自动选中第一个事件,翻页时不自动选中
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (currentPageEvents.length > 0) {
|
if (currentPageEvents.length > 0) {
|
||||||
|
|||||||
Reference in New Issue
Block a user