feat: sockt 弹窗功能添加

This commit is contained in:
zdl
2025-10-21 17:50:21 +08:00
parent c93f689954
commit 09c9273190
17 changed files with 3739 additions and 161 deletions

View File

@@ -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)
}