feat: 优化事件中心页面 重构后的文件结构
src/views/Community/
├── index.js (主组件,150行左右)
├── components/
│ ├── EventTimelineCard.js (新增)
│ ├── EventTimelineHeader.js (新增)
│ ├── EventListSection.js (新增)
│ ├── HotEventsSection.js (新增)
│ ├── EventModals.js (新增)
│ ├── UnifiedSearchBox.js (已有)
│ ├── EventList.js (已有)
│ └── ...
└── hooks/
├── useEventFilters.js (新增)
└── useEventData.js (新增)
This commit is contained in:
91
src/views/Community/hooks/useEventData.js
Normal file
91
src/views/Community/hooks/useEventData.js
Normal file
@@ -0,0 +1,91 @@
|
||||
// src/views/Community/hooks/useEventData.js
|
||||
// 事件数据加载逻辑 Hook
|
||||
|
||||
import { useState, useEffect, useRef, useCallback } from 'react';
|
||||
import { debounce } from 'lodash';
|
||||
import { eventService } from '../../../services/eventService';
|
||||
import { logger } from '../../../utils/logger';
|
||||
|
||||
/**
|
||||
* 事件数据加载 Hook
|
||||
* @param {Object} filters - 筛选条件
|
||||
* @param {number} pageSize - 每页数量
|
||||
* @returns {Object} 事件数据和加载状态
|
||||
*/
|
||||
export const useEventData = (filters, pageSize = 10) => {
|
||||
const [events, setEvents] = useState([]);
|
||||
const [pagination, setPagination] = useState({
|
||||
current: 1,
|
||||
pageSize: pageSize,
|
||||
total: 0
|
||||
});
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [lastUpdateTime, setLastUpdateTime] = useState(new Date());
|
||||
|
||||
// 加载事件列表
|
||||
const loadEvents = useCallback(async (page = 1) => {
|
||||
logger.debug('useEventData', 'loadEvents 被调用', { page });
|
||||
setLoading(true);
|
||||
|
||||
try {
|
||||
const response = await eventService.getEvents({
|
||||
...filters,
|
||||
page,
|
||||
per_page: pagination.pageSize
|
||||
});
|
||||
|
||||
if (response.success) {
|
||||
setEvents(response.data.events);
|
||||
setPagination({
|
||||
current: response.data.pagination.page,
|
||||
pageSize: response.data.pagination.per_page,
|
||||
total: response.data.pagination.total
|
||||
});
|
||||
setLastUpdateTime(new Date());
|
||||
|
||||
logger.debug('useEventData', 'loadEvents 成功', {
|
||||
count: response.data.events.length,
|
||||
total: response.data.pagination.total
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('useEventData', 'loadEvents', error, {
|
||||
page,
|
||||
filters
|
||||
});
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [filters, pagination.pageSize]);
|
||||
|
||||
// 创建防抖的 loadEvents 函数(500ms 防抖延迟)
|
||||
const debouncedLoadEvents = useRef(
|
||||
debounce((page) => {
|
||||
logger.debug('useEventData', '防抖后执行 loadEvents', { page });
|
||||
loadEvents(page);
|
||||
}, 500)
|
||||
).current;
|
||||
|
||||
// 监听 filters 变化,自动加载数据
|
||||
// 防抖优化:用户快速切换筛选条件时,只执行最后一次请求
|
||||
useEffect(() => {
|
||||
logger.debug('useEventData', 'useEffect 触发,filters 变化', { filters });
|
||||
|
||||
// 使用防抖加载事件
|
||||
debouncedLoadEvents(filters.page || 1);
|
||||
|
||||
// 组件卸载时取消防抖
|
||||
return () => {
|
||||
debouncedLoadEvents.cancel();
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [filters]); // 监听 filters 状态变化
|
||||
|
||||
return {
|
||||
events,
|
||||
pagination,
|
||||
loading,
|
||||
lastUpdateTime,
|
||||
loadEvents
|
||||
};
|
||||
};
|
||||
76
src/views/Community/hooks/useEventFilters.js
Normal file
76
src/views/Community/hooks/useEventFilters.js
Normal file
@@ -0,0 +1,76 @@
|
||||
// src/views/Community/hooks/useEventFilters.js
|
||||
// 事件筛选逻辑 Hook
|
||||
|
||||
import { useState, useCallback } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { logger } from '../../../utils/logger';
|
||||
|
||||
/**
|
||||
* 事件筛选逻辑 Hook
|
||||
* @param {Object} options - 配置选项
|
||||
* @param {Function} options.navigate - 路由导航函数
|
||||
* @param {Function} options.onEventClick - 事件点击回调
|
||||
* @param {Object} options.eventTimelineRef - 时间轴ref(用于滚动)
|
||||
* @returns {Object} 筛选状态和处理函数
|
||||
*/
|
||||
export const useEventFilters = ({ navigate, onEventClick, eventTimelineRef } = {}) => {
|
||||
const [searchParams] = useSearchParams();
|
||||
|
||||
// 筛选参数状态 - 初始化时从URL读取,之后只用本地状态
|
||||
const [filters, setFilters] = useState(() => {
|
||||
return {
|
||||
sort: searchParams.get('sort') || 'new',
|
||||
importance: searchParams.get('importance') || 'all',
|
||||
date_range: searchParams.get('date_range') || '',
|
||||
q: searchParams.get('q') || '',
|
||||
industry_classification: searchParams.get('industry_classification') || '',
|
||||
industry_code: searchParams.get('industry_code') || '',
|
||||
stock_code: searchParams.get('stock_code') || '',
|
||||
page: parseInt(searchParams.get('page') || '1', 10)
|
||||
};
|
||||
});
|
||||
|
||||
// 更新筛选参数 - 直接替换(由 UnifiedSearchBox 输出完整参数)
|
||||
const updateFilters = useCallback((newFilters) => {
|
||||
logger.debug('useEventFilters', 'updateFilters 接收到完整参数', newFilters);
|
||||
setFilters(newFilters);
|
||||
}, []);
|
||||
|
||||
// 处理分页变化
|
||||
const handlePageChange = useCallback((page) => {
|
||||
// 保持现有筛选条件,只更新页码
|
||||
updateFilters({ ...filters, page });
|
||||
|
||||
// 滚动到实时事件时间轴(平滑滚动)
|
||||
if (eventTimelineRef && eventTimelineRef.current) {
|
||||
setTimeout(() => {
|
||||
eventTimelineRef.current.scrollIntoView({
|
||||
behavior: 'smooth', // 平滑滚动
|
||||
block: 'start' // 滚动到元素顶部
|
||||
});
|
||||
}, 100); // 延迟100ms,确保DOM更新
|
||||
}
|
||||
}, [filters, updateFilters, eventTimelineRef]);
|
||||
|
||||
// 处理事件点击
|
||||
const handleEventClick = useCallback((event) => {
|
||||
if (onEventClick) {
|
||||
onEventClick(event);
|
||||
}
|
||||
}, [onEventClick]);
|
||||
|
||||
// 处理查看详情
|
||||
const handleViewDetail = useCallback((eventId) => {
|
||||
if (navigate) {
|
||||
navigate(`/event-detail/${eventId}`);
|
||||
}
|
||||
}, [navigate]);
|
||||
|
||||
return {
|
||||
filters,
|
||||
updateFilters,
|
||||
handlePageChange,
|
||||
handleEventClick,
|
||||
handleViewDetail
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user