// src/views/Community/index.js import React, { useState, useEffect, useCallback, useRef } from 'react'; import { useSearchParams, useNavigate } from 'react-router-dom'; import { useSelector, useDispatch } from 'react-redux'; import { debounce } from 'lodash'; import { fetchPopularKeywords, fetchHotEvents } from '../../store/slices/communityDataSlice'; import { Box, Container, Grid, GridItem, Card, CardBody, CardHeader, Button, Text, Heading, VStack, HStack, Badge, Spinner, Flex, Tag, TagLabel, TagCloseButton, IconButton, Wrap, WrapItem, Stat, StatLabel, StatNumber, StatHelpText, Modal, ModalOverlay, ModalContent, ModalHeader, ModalBody, ModalCloseButton, Drawer, DrawerBody, DrawerHeader, DrawerOverlay, DrawerContent, DrawerCloseButton, useDisclosure, Center, Image, Divider, useColorModeValue, } from '@chakra-ui/react'; import { RepeatIcon, TimeIcon, InfoIcon, SearchIcon, CalendarIcon, StarIcon, ChevronRightIcon, CloseIcon, } from '@chakra-ui/icons'; // 导入组件 import MidjourneyHeroSection from './components/MidjourneyHeroSection'; import EventList from './components/EventList'; import EventDetailModal from './components/EventDetailModal'; import StockDetailPanel from './components/StockDetailPanel'; import HotEvents from './components/HotEvents'; import UnifiedSearchBox from './components/UnifiedSearchBox'; // 已整合 SearchBox、PopularKeywords、IndustryCascader import { eventService } from '../../services/eventService'; import { logger } from '../../utils/logger'; import { useNotification } from '../../contexts/NotificationContext'; // 导航栏已由 MainLayout 提供,无需在此导入 // const { RangePicker } = DatePicker; // const { Option } = AntSelect; const filterLabelMap = { date_range: v => v ? `日期: ${v}` : '', sort: v => v ? `排序: ${v === 'new' ? '最新' : v === 'hot' ? '热门' : v === 'returns' ? '收益率' : v}` : '', importance: v => v && v !== 'all' ? `重要性: ${v}` : '', industry_classification: v => v ? `行业: ${v}` : '', industry_code: v => v ? `行业代码: ${v}` : '', q: v => v ? `关键词: ${v}` : '', }; const Community = () => { const [searchParams, setSearchParams] = useSearchParams(); const navigate = useNavigate(); const dispatch = useDispatch(); // Redux状态 const { popularKeywords, hotEvents } = useSelector(state => state.communityData); // Chakra UI hooks const bgColor = useColorModeValue('gray.50', 'gray.900'); const cardBg = useColorModeValue('white', 'gray.800'); const borderColor = useColorModeValue('gray.200', 'gray.700'); // Modal/Drawer控制 const { isOpen: isEventModalOpen, onOpen: onEventModalOpen, onClose: onEventModalClose } = useDisclosure(); const { isOpen: isStockDrawerOpen, onOpen: onStockDrawerOpen, onClose: onStockDrawerClose } = useDisclosure(); // Ref:用于滚动到实时事件时间轴 const eventTimelineRef = useRef(null); // ⚡ 通知权限引导 const { showCommunityGuide } = useNotification(); // 状态管理 const [events, setEvents] = useState([]); const [pagination, setPagination] = useState({ current: 1, pageSize: 10, total: 0 }); const [loading, setLoading] = useState(false); const [selectedEvent, setSelectedEvent] = useState(null); const [selectedEventForStock, setSelectedEventForStock] = useState(null); const [lastUpdateTime, setLastUpdateTime] = useState(new Date()); // 筛选参数状态 - 初始化时从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') || '', search_type: searchParams.get('search_type') || 'topic', industry_classification: searchParams.get('industry_classification') || '', industry_code: searchParams.get('industry_code') || '', page: parseInt(searchParams.get('page') || '1', 10) }; }); // 更新筛选参数 - 不再修改URL const updateFilters = useCallback((newFilters) => { setFilters(prev => ({ ...prev, ...newFilters })); }, []); // 加载事件列表 const loadEvents = useCallback(async (page = 1) => { logger.debug('Community', '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()); } } catch (error) { logger.error('Community', 'loadEvents', error, { page, filters }); } finally { setLoading(false); } }, [filters, pagination.pageSize]); // 处理筛选变化 const handleFilterChange = useCallback((filterType, value) => { updateFilters({ [filterType]: value, page: 1 }); }, [updateFilters]); // 处理分页变化 const handlePageChange = useCallback((page) => { updateFilters({ page }); // 滚动到实时事件时间轴(平滑滚动) setTimeout(() => { if (eventTimelineRef.current) { eventTimelineRef.current.scrollIntoView({ behavior: 'smooth', // 平滑滚动 block: 'start' // 滚动到元素顶部 }); } }, 100); // 延迟100ms,确保DOM更新 }, [updateFilters]); // 处理事件点击 const handleEventClick = useCallback((event) => { setSelectedEventForStock(event); onStockDrawerOpen(); }, [onStockDrawerOpen]); // 处理查看详情 const handleViewDetail = useCallback((eventId) => { navigate(`/event-detail/${eventId}`); }, [navigate]); // 处理关键词点击 const handleKeywordClick = useCallback((keyword) => { updateFilters({ q: keyword, page: 1 }); }, [updateFilters]); // 处理标签删除 const handleRemoveFilterTag = (key) => { let reset = ''; if (key === 'sort') reset = 'new'; if (key === 'importance') reset = 'all'; updateFilters({ [key]: reset, page: 1 }); }; // 获取筛选标签 const filterTags = Object.entries(filters) .filter(([key, value]) => { if (key === 'industry_code') return !!value; if (key === 'importance') return value && value !== 'all'; if (key === 'sort') return value && value !== 'new'; if (key === 'date_range') return !!value; if (key === 'q') return !!value; return false; }) .map(([key, value]) => { if (key === 'industry_code') return { key, label: `行业代码: ${value}` }; return { key, label: filterLabelMap[key] ? filterLabelMap[key](value) : `${key}: ${value}` }; }); // 创建防抖的 loadEvents 函数(500ms 防抖延迟) const debouncedLoadEvents = useRef( debounce((page) => { logger.debug('Community', '防抖后执行 loadEvents', { page }); loadEvents(page); }, 500) ).current; // 加载事件列表 - 监听 filters 状态变化 // 防抖优化:用户快速切换筛选条件时,只执行最后一次请求 useEffect(() => { logger.debug('Community', 'useEffect 触发,filters 变化', { filters }); // 使用防抖加载事件 debouncedLoadEvents(filters.page); // 组件卸载时取消防抖 return () => { debouncedLoadEvents.cancel(); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, [filters]); // 监听本地 filters 状态变化 // 加载热门关键词和热点事件(使用Redux,内部有缓存判断) useEffect(() => { dispatch(fetchPopularKeywords()); dispatch(fetchHotEvents()); }, [dispatch]); // ⚡ 首次访问社区时,延迟显示权限引导 useEffect(() => { if (showCommunityGuide) { const timer = setTimeout(() => { logger.info('Community', '显示社区权限引导'); showCommunityGuide(); }, 5000); // 延迟 5 秒,让用户先浏览页面 return () => clearTimeout(timer); } }, [showCommunityGuide]); // 只在组件挂载时执行一次 return ( {/* 导航栏已由 MainLayout 提供 */} {/* Midjourney风格英雄区域 */} {/* 主内容区域 */} {/* 左侧主要内容 */} {/* 筛选器 - 需要改造为Chakra UI版本 */} {/* 筛选标签 */} {filterTags.length > 0 && ( {filterTags.map(tag => ( {tag.label} handleRemoveFilterTag(tag.key)} /> ))} )} {/* 事件列表卡片 */} 实时事件时间轴 全网监控 智能捕获 深度分析 最后更新: {lastUpdateTime.toLocaleTimeString()} {loading ? (
正在加载最新事件...
) : events.length > 0 ? ( ) : (
暂无事件数据
)}
{/* 右侧侧边栏 */} {/* 搜索框 - 需要改造为Chakra UI版本 */} { updateUrlParams({ ...values, page: 1 }); }} /> {/* 投资日历 - 需要改造为Chakra UI版本 */} 投资日历 {/* 热门关键词 - 需要改造为Chakra UI版本 */} 热门关键词 {/* 重要性说明 - 需要改造为Chakra UI版本 */} 重要性说明
{/* 热点事件 - 需要改造为Chakra UI版本 */} {hotEvents.length > 0 && ( 🔥 热点事件 )}
{/* Footer区域 */} © 2024 价值前沿. 保留所有权利. 京公网安备11010802046286号 京ICP备2025107343号-1 {/* 事件详情模态框 - 使用Chakra UI Modal */} 事件详情 { setSelectedEvent(null); onEventModalClose(); }} /> {/* 股票详情抽屉 - 使用原组件自带的 Antd Drawer,避免与 Chakra Drawer 重叠导致空白 */} { setSelectedEventForStock(null); onStockDrawerClose(); }} />
); }; export default Community;