feat: sockt 弹窗功能添加
This commit is contained in:
@@ -33,6 +33,7 @@ import {
|
||||
Switch,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
useToast,
|
||||
} from '@chakra-ui/react';
|
||||
import {
|
||||
ViewIcon,
|
||||
@@ -53,6 +54,7 @@ import { useNavigate } from 'react-router-dom';
|
||||
import moment from 'moment';
|
||||
import { logger } from '../../../utils/logger';
|
||||
import { getApiBase } from '../../../utils/apiConfig';
|
||||
import { useEventNotifications } from '../../../hooks/useEventNotifications';
|
||||
|
||||
// ========== 工具函数定义在组件外部 ==========
|
||||
// 涨跌颜色配置(中国A股配色:红涨绿跌)- 分档次显示
|
||||
@@ -166,15 +168,55 @@ const PriceArrow = ({ value }) => {
|
||||
// ========== 主组件 ==========
|
||||
const EventList = ({ events, pagination, onPageChange, onEventClick, onViewDetail }) => {
|
||||
const navigate = useNavigate();
|
||||
const toast = useToast();
|
||||
const [isCompactMode, setIsCompactMode] = useState(false); // 新增:紧凑模式状态
|
||||
const [followingMap, setFollowingMap] = useState({});
|
||||
const [followCountMap, setFollowCountMap] = useState({});
|
||||
const [localEvents, setLocalEvents] = useState(events); // 用于实时更新的本地事件列表
|
||||
|
||||
// 实时事件推送集成
|
||||
const { isConnected } = useEventNotifications({
|
||||
eventType: 'all',
|
||||
importance: 'all',
|
||||
enabled: true,
|
||||
onNewEvent: (event) => {
|
||||
logger.info('EventList', '收到新事件推送', event);
|
||||
|
||||
// 显示 Toast 通知
|
||||
toast({
|
||||
title: '新事件发布',
|
||||
description: event.title,
|
||||
status: 'info',
|
||||
duration: 5000,
|
||||
isClosable: true,
|
||||
position: 'top-right',
|
||||
variant: 'left-accent',
|
||||
});
|
||||
|
||||
// 将新事件添加到列表顶部(防止重复)
|
||||
setLocalEvents((prevEvents) => {
|
||||
const exists = prevEvents.some(e => e.id === event.id);
|
||||
if (exists) {
|
||||
logger.debug('EventList', '事件已存在,跳过添加', { eventId: event.id });
|
||||
return prevEvents;
|
||||
}
|
||||
logger.info('EventList', '新事件添加到列表顶部', { eventId: event.id });
|
||||
// 添加到顶部,最多保留 100 个
|
||||
return [event, ...prevEvents].slice(0, 100);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 同步外部 events 到 localEvents
|
||||
useEffect(() => {
|
||||
setLocalEvents(events);
|
||||
}, [events]);
|
||||
|
||||
// 初始化关注状态与计数
|
||||
useEffect(() => {
|
||||
// 初始化计数映射
|
||||
const initCounts = {};
|
||||
events.forEach(ev => {
|
||||
localEvents.forEach(ev => {
|
||||
initCounts[ev.id] = ev.follower_count || 0;
|
||||
});
|
||||
setFollowCountMap(initCounts);
|
||||
@@ -197,8 +239,8 @@ const EventList = ({ events, pagination, onPageChange, onEventClick, onViewDetai
|
||||
}
|
||||
};
|
||||
loadFollowing();
|
||||
// 仅在 events 更新时重跑
|
||||
}, [events]);
|
||||
// 仅在 localEvents 更新时重跑
|
||||
}, [localEvents]);
|
||||
|
||||
const toggleFollow = async (eventId) => {
|
||||
try {
|
||||
@@ -766,8 +808,27 @@ const EventList = ({ events, pagination, onPageChange, onEventClick, onViewDetai
|
||||
return (
|
||||
<Box bg={bgColor} minH="100vh" py={8}>
|
||||
<Container maxW="container.xl">
|
||||
{/* 视图切换控制 */}
|
||||
<Flex justify="flex-end" mb={6}>
|
||||
{/* 顶部控制栏:连接状态 + 视图切换 */}
|
||||
<Flex justify="space-between" align="center" mb={6}>
|
||||
{/* WebSocket 连接状态指示器 */}
|
||||
<HStack spacing={2}>
|
||||
<Badge
|
||||
colorScheme={isConnected ? 'green' : 'red'}
|
||||
fontSize="sm"
|
||||
px={3}
|
||||
py={1}
|
||||
borderRadius="full"
|
||||
>
|
||||
{isConnected ? '🟢 实时推送已开启' : '🔴 实时推送未连接'}
|
||||
</Badge>
|
||||
{isConnected && (
|
||||
<Text fontSize="xs" color={mutedColor}>
|
||||
新事件将自动推送
|
||||
</Text>
|
||||
)}
|
||||
</HStack>
|
||||
|
||||
{/* 视图切换控制 */}
|
||||
<FormControl display="flex" alignItems="center" w="auto">
|
||||
<FormLabel htmlFor="compact-mode" mb="0" fontSize="sm" color={textColor}>
|
||||
精简模式
|
||||
@@ -781,11 +842,11 @@ const EventList = ({ events, pagination, onPageChange, onEventClick, onViewDetai
|
||||
</FormControl>
|
||||
</Flex>
|
||||
|
||||
{events.length > 0 ? (
|
||||
{localEvents.length > 0 ? (
|
||||
<VStack align="stretch" spacing={0}>
|
||||
{events.map((event, index) => (
|
||||
{localEvents.map((event, index) => (
|
||||
<Box key={event.id} position="relative">
|
||||
{isCompactMode
|
||||
{isCompactMode
|
||||
? renderCompactEvent(event)
|
||||
: renderDetailedEvent(event)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user