From 4a0194e26c4561f12fedd8d37fd9bbc29fd4edb5 Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Thu, 30 Oct 2025 12:15:55 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=87=8D=E6=9E=84=E4=B8=BB=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=E2=94=82?= =?UTF-8?q?=20=E2=94=82=20=E2=94=82=20=E2=94=82=20-=20=E2=9D=8C=20?= =?UTF-8?q?=E7=A7=BB=E9=99=A4=20renderPriceChange=20=E5=87=BD=E6=95=B0?= =?UTF-8?q?=EF=BC=8860=E8=A1=8C=EF=BC=89=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=E2=94=82=20?= =?UTF-8?q?=E2=94=82=20=E2=94=82=20=E2=94=82=20-=20=E2=9D=8C=20=E7=A7=BB?= =?UTF-8?q?=E9=99=A4=20renderCompactEvent=20=E5=87=BD=E6=95=B0=EF=BC=88200?= =?UTF-8?q?=E8=A1=8C=EF=BC=89=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=E2=94=82=20=E2=94=82=20=E2=94=82?= =?UTF-8?q?=20=E2=94=82=20-=20=E2=9D=8C=20=E7=A7=BB=E9=99=A4=20renderDetai?= =?UTF-8?q?ledEvent=20=E5=87=BD=E6=95=B0=EF=BC=88300=E8=A1=8C=EF=BC=89=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=E2=94=82=20=E2=94=82=20=E2=94=82=20=E2=94=82=20-=20?= =?UTF-8?q?=E2=9D=8C=20=E7=A7=BB=E9=99=A4=20expandedDescriptions=20state?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=E2=94=82=20=E2=94=82=20=E2=94=82?= =?UTF-8?q?=20=E2=94=82=20-=20=E2=9D=8C=20=E7=B2=BE=E7=AE=80=20Chakra=20UI?= =?UTF-8?q?=20=E5=AF=BC=E5=85=A5=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=E2=94=82=20=E2=94=82=20=E2=94=82=20?= =?UTF-8?q?=E2=94=82=20-=20=E2=9C=85=20=E4=BD=BF=E7=94=A8=20EventCard=20?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E7=BB=9F=E4=B8=80=E6=B8=B2=E6=9F=93=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20=20?= =?UTF-8?q?=20=20=20=20=20=20=20=20=20=20=20=E2=94=82=20=E2=94=82=20?= =?UTF-8?q?=E2=94=82=20=E2=94=82=20-=20=E2=9C=85=20=E4=BF=9D=E7=95=99?= =?UTF-8?q?=E6=89=80=E6=9C=89=E4=B8=9A=E5=8A=A1=E9=80=BB=E8=BE=91=EF=BC=88?= =?UTF-8?q?WebSocket=E3=80=81=E9=80=9A=E7=9F=A5=E3=80=81=E5=85=B3=E6=B3=A8?= =?UTF-8?q?=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/views/Community/components/EventList.js | 634 +------------------- 1 file changed, 18 insertions(+), 616 deletions(-) diff --git a/src/views/Community/components/EventList.js b/src/views/Community/components/EventList.js index 9208c8de..2555548f 100644 --- a/src/views/Community/components/EventList.js +++ b/src/views/Community/components/EventList.js @@ -7,68 +7,28 @@ import { Text, Button, Badge, - Tag, - TagLabel, - TagLeftIcon, Flex, - Avatar, - Tooltip, - IconButton, - Divider, Container, useColorModeValue, - Circle, - Stat, - StatNumber, - StatHelpText, - StatArrow, - ButtonGroup, - Heading, - SimpleGrid, - Card, - CardBody, - Center, - Link, - Spacer, Switch, FormControl, FormLabel, useToast, + Center, } from '@chakra-ui/react'; -import { - ViewIcon, - ChatIcon, - StarIcon, - TimeIcon, - InfoIcon, - WarningIcon, - WarningTwoIcon, - CheckCircleIcon, - ArrowForwardIcon, - ExternalLinkIcon, - ViewOffIcon, -} from '@chakra-ui/icons'; +import { InfoIcon } from '@chakra-ui/icons'; 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'; -import { getImportanceConfig, getAllImportanceLevels } from '../../../constants/importanceLevels'; import { browserNotificationService } from '../../../services/browserNotificationService'; import { useNotification } from '../../../contexts/NotificationContext'; +import { getImportanceConfig } from '../../../constants/importanceLevels'; -// 导入价格相关工具函数 -import { - getPriceChangeColor, - getPriceChangeBg, - getPriceChangeBorderColor, - PriceArrow, -} from '../../../utils/priceFormatters'; - -// 导入动画定义 -import { pulseAnimation } from '../../../constants/animations'; +// 导入子组件 +import EventCard from './EventCard'; // ========== 主组件 ========== const EventList = ({ events, pagination, onPageChange, onEventClick, onViewDetail }) => { @@ -78,7 +38,6 @@ const EventList = ({ events, pagination, onPageChange, onEventClick, onViewDetai const [followingMap, setFollowingMap] = useState({}); const [followCountMap, setFollowCountMap] = useState({}); const [localEvents, setLocalEvents] = useState(events); // 用于实时更新的本地事件列表 - const [expandedDescriptions, setExpandedDescriptions] = useState({}); // 描述展开状态映射 // 从 NotificationContext 获取推送权限相关状态和方法 const { browserPermission, requestBrowserPermission } = useNotification(); @@ -265,67 +224,6 @@ const EventList = ({ events, pagination, onPageChange, onEventClick, onViewDetai const linkColor = useColorModeValue('blue.600', 'blue.400'); const hoverBg = useColorModeValue('gray.50', 'gray.700'); - const renderPriceChange = (value, label) => { - if (value === null || value === undefined) { - return ( - - {label}: -- - - ); - } - - const absValue = Math.abs(value); - const isPositive = value > 0; - - // 根据涨跌幅大小选择不同的颜色深浅 - let colorScheme = 'gray'; - let variant = 'solid'; - - if (isPositive) { - // 上涨用红色系 - if (absValue >= 3) { - colorScheme = 'red'; - variant = 'solid'; // 深色 - } else if (absValue >= 1) { - colorScheme = 'red'; - variant = 'subtle'; // 中等 - } else { - colorScheme = 'red'; - variant = 'outline'; // 浅色 - } - } else { - // 下跌用绿色系 - if (absValue >= 3) { - colorScheme = 'green'; - variant = 'solid'; // 深色 - } else if (absValue >= 1) { - colorScheme = 'green'; - variant = 'subtle'; // 中等 - } else { - colorScheme = 'green'; - variant = 'outline'; // 浅色 - } - } - - const Icon = isPositive ? TriangleUpIcon : TriangleDownIcon; - - return ( - - - - {label}: {isPositive ? '+' : ''}{value.toFixed(2)}% - - - ); - }; const handleTitleClick = (e, event) => { e.preventDefault(); @@ -349,511 +247,6 @@ const EventList = ({ events, pagination, onPageChange, onEventClick, onViewDetai }; }; - // 精简模式的事件渲染(优化版:标题2行+标签内联+按钮右侧) - const renderCompactEvent = (event, index) => { - const importance = getImportanceConfig(event.importance); - const isFollowing = !!followingMap[event.id]; - const followerCount = followCountMap[event.id] ?? (event.follower_count || 0); - const timelineStyle = getTimelineBoxStyle(); - - return ( - - {/* 左侧时间轴 - 动态样式 */} - - {/* 时间长方形卡片 */} - - {/* 日期 YYYY-MM-DD */} - - {moment(event.created_at).format('YYYY-MM-DD')} - - {/* 时间 HH:mm */} - - {moment(event.created_at).format('HH:mm')} - - - {/* 时间轴竖线 */} - - - - {/* 右侧内容卡片 */} - onEventClick(event)} - mb={2} - > - - - {/* 第一行:标题(2行)+ 标签(内联)+ 按钮(右侧) */} - - {/* 标题区域:标题+标签(内联) */} - - handleTitleClick(e, event)} - cursor="pointer" - > - {event.title} - - {' '} - {/* 重要性标签 - 内联 */} - - {event.importance || 'C'}级 - - {' '} - {/* 涨跌幅标签 - 内联 */} - {event.related_avg_chg != null && ( - - 0 ? 'red' : 'green'} - fontSize="xs" - px={2} - py={1} - borderRadius="md" - fontWeight="bold" - display="inline-flex" - alignItems="center" - gap={1} - verticalAlign="middle" - > - - {event.related_avg_chg > 0 ? '+' : ''}{event.related_avg_chg.toFixed(2)}% - - - )} - - - {/* 操作按钮 - 固定右侧 */} - - - - - - - {/* 第二行:统计数据(左) + 作者时间(右) */} - - {/* 左侧:统计数据 */} - - - - - {event.view_count || 0} - - - - - - - {event.post_count || 0} - - - - - - - {followerCount} - - - - - {/* 右侧:作者 + 时间(统一格式 YYYY-MM-DD HH:mm) */} - - @{event.creator?.username || 'Anonymous'} - - - {moment(event.created_at).format('YYYY-MM-DD HH:mm')} - - - - - - - - ); - }; - - // 详细模式的事件渲染(原有的渲染方式,但修复了箭头颜色) - const renderDetailedEvent = (event) => { - const importance = getImportanceConfig(event.importance); - const isFollowing = !!followingMap[event.id]; - const followerCount = followCountMap[event.id] ?? (event.follower_count || 0); - const timelineStyle = getTimelineBoxStyle(); - - return ( - - {/* 左侧时间轴 - 动态样式 */} - - {/* 时间长方形卡片 */} - - {/* 日期 YYYY-MM-DD */} - - {moment(event.created_at).format('YYYY-MM-DD')} - - {/* 时间 HH:mm */} - - {moment(event.created_at).format('HH:mm')} - - - {/* 时间轴竖线 */} - - - - {/* 事件卡片 */} - onEventClick(event)} - mb={3} - > - - - {/* 第一行:标题+优先级 | 统计+关注 */} - - {/* 左侧:标题 + 优先级标签 */} - - - handleTitleClick(e, event)} - cursor="pointer" - > - {event.title} - - - - - - 重要性等级说明 - - - {getAllImportanceLevels().map((level) => ( - - - - {level.level}级 - {level.description} - - - ))} - - } - placement="top" - hasArrow - bg="white" - color="gray.800" - fontSize="md" - p={3} - borderRadius="lg" - borderWidth="1px" - borderColor="gray.200" - boxShadow="lg" - > - - - {event.importance || 'C'}级 - - - - - {/* 右侧:统计数据 + 关注按钮 */} - - {/* 统计数据 */} - - - - - {event.view_count || 0} - - - - - - {event.post_count || 0} - - - - - - {followerCount} - - - - - {/* 关注按钮 */} - - - - - {/* 第二行:价格标签 | 时间+作者 */} - - {/* 左侧:价格标签 */} - - {/* 平均涨幅 - 始终显示,无数据时显示 -- */} - 0 ? 'red' : event.related_avg_chg < 0 ? 'green' : 'gray') - : 'gray'} - fontSize="xs" - px={2} - py={0.5} - borderRadius="md" - cursor="pointer" - _hover={{ transform: 'scale(1.05)', boxShadow: 'md' }} - transition="all 0.2s" - > - - 平均 - - {event.related_avg_chg != null - ? `${event.related_avg_chg > 0 ? '+' : ''}${event.related_avg_chg.toFixed(2)}%` - : '--'} - - - - - {/* 最大涨幅 - 始终显示,无数据时显示 -- */} - 0 ? 'red' : event.related_max_chg < 0 ? 'green' : 'gray') - : 'gray'} - fontSize="xs" - px={2} - py={0.5} - borderRadius="md" - cursor="pointer" - _hover={{ transform: 'scale(1.05)', boxShadow: 'md' }} - transition="all 0.2s" - > - - 最大 - - {event.related_max_chg != null - ? `${event.related_max_chg > 0 ? '+' : ''}${event.related_max_chg.toFixed(2)}%` - : '--'} - - - - - {/* 周涨幅 - 始终显示,无数据时显示 -- */} - 0 ? 'red' : event.related_week_chg < 0 ? 'green' : 'gray') - : 'gray'} - fontSize="xs" - px={2} - py={0.5} - borderRadius="md" - cursor="pointer" - _hover={{ transform: 'scale(1.05)', boxShadow: 'md' }} - transition="all 0.2s" - > - - - {event.related_week_chg != null && } - - {event.related_week_chg != null - ? `${event.related_week_chg > 0 ? '+' : ''}${event.related_week_chg.toFixed(2)}%` - : '--'} - - - - - - {/* 右侧:时间 + 作者 */} - - - {moment(event.created_at).format('YYYY-MM-DD HH:mm')} - - - @{event.creator?.username || 'Anonymous'} - - - - {/* 第三行:描述文字 + 展开/收起 */} - {event.description && ( - - - {event.description} - - {event.description.length > 120 && ( - - )} - - )} - - - - - ); - }; - // 分页组件 const Pagination = ({ current, total, pageSize, onChange }) => { const totalPages = Math.ceil(total / pageSize); @@ -1062,10 +455,19 @@ const EventList = ({ events, pagination, onPageChange, onEventClick, onViewDetai {localEvents.map((event, index) => ( - {isCompactMode - ? renderCompactEvent(event, index) - : renderDetailedEvent(event) - } + ))}