From 3ef1e6ea29913ff4c7921c0bd31091fde220ca17 Mon Sep 17 00:00:00 2001 From: zzlgreat Date: Mon, 22 Dec 2025 17:10:55 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0Company=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E7=9A=84UI=E4=B8=BAFUI=E9=A3=8E=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../layouts/MainlineTimelineView.js | 351 ++++++++++++++---- 1 file changed, 269 insertions(+), 82 deletions(-) diff --git a/src/views/Community/components/DynamicNews/layouts/MainlineTimelineView.js b/src/views/Community/components/DynamicNews/layouts/MainlineTimelineView.js index 6b2adf23..cf6a7ef6 100644 --- a/src/views/Community/components/DynamicNews/layouts/MainlineTimelineView.js +++ b/src/views/Community/components/DynamicNews/layouts/MainlineTimelineView.js @@ -24,7 +24,9 @@ import { Button, } from "@chakra-ui/react"; import { ChevronDownIcon, ChevronUpIcon, RepeatIcon } from "@chakra-ui/icons"; -import { FiTrendingUp, FiZap } from "react-icons/fi"; +import { FiTrendingUp, FiZap, FiClock } from "react-icons/fi"; +import { FireOutlined } from "@ant-design/icons"; +import dayjs from "dayjs"; import { Select } from "antd"; import MiniEventCard from "../../EventCard/MiniEventCard"; import { getApiBase } from "@utils/apiConfig"; @@ -47,6 +49,122 @@ const COLORS = { // 每次加载的事件数量 const EVENTS_PER_LOAD = 12; +/** + * 格式化时间显示 + */ +const formatEventTime = (dateStr) => { + if (!dateStr) return ""; + const date = dayjs(dateStr); + const now = dayjs(); + const isToday = date.isSame(now, "day"); + const isYesterday = date.isSame(now.subtract(1, "day"), "day"); + + if (isToday) { + return date.format("HH:mm"); + } else if (isYesterday) { + return `昨天 ${date.format("HH:mm")}`; + } else { + return date.format("MM-DD HH:mm"); + } +}; + +/** + * 单个事件项组件 - 带时间轴 + */ +const TimelineEventItem = React.memo(({ event, isSelected, onEventClick, isHot }) => { + const changePct = event.max_change_pct ?? event.change_pct; + const hasChange = changePct != null; + const isPositive = hasChange && changePct >= 0; + + return ( + onEventClick?.(event)} + _hover={{ bg: "rgba(255, 255, 255, 0.05)" }} + borderRadius="md" + transition="all 0.15s" + bg={isSelected ? "rgba(66, 153, 225, 0.15)" : "transparent"} + > + {/* 左侧时间轴 */} + + {/* 时间显示 */} + + {formatEventTime(event.created_at || event.event_time)} + + {/* 时间轴线 */} + + {/* 时间轴圆点 */} + + + + {/* 右侧内容 */} + + + {/* HOT 标签 */} + {isHot && ( + + + HOT + + )} + + {event.title} + + {/* 涨跌幅 */} + {hasChange && ( + + {isPositive ? "+" : ""} + {changePct.toFixed(1)}% + + )} + + + + ); +}); + +TimelineEventItem.displayName = "TimelineEventItem"; + /** * 单个主线卡片组件 - 支持懒加载 */ @@ -70,6 +188,23 @@ const MainlineCard = React.memo( } }, [isExpanded]); + // 找出涨幅最大的事件(HOT 事件) + const hotEvent = useMemo(() => { + if (!mainline.events || mainline.events.length === 0) return null; + let maxChange = -Infinity; + let hot = null; + mainline.events.forEach((event) => { + const change = event.max_change_pct ?? event.change_pct ?? -Infinity; + if (change > maxChange) { + maxChange = change; + hot = event; + } + }); + return hot; + }, [mainline.events]); + + const hotEventId = hotEvent?.id; + // 当前显示的事件 const displayedEvents = useMemo(() => { return mainline.events.slice(0, displayCount); @@ -114,77 +249,135 @@ const MainlineCard = React.memo( }} > {/* 卡片头部 */} - - - - - {mainline.group_name || mainline.lv2_name || mainline.lv1_name || "其他"} - - {/* 涨跌幅显示 - 在概念名称旁边 */} - {mainline.avg_change_pct != null && ( + + {/* 第一行:概念名称 + 涨跌幅 + 事件数 */} + + + = 0 ? "#fc8181" : "#68d391"} + fontSize="sm" + color={COLORS.textColor} + noOfLines={1} + flex={1} + > + {mainline.group_name || mainline.lv2_name || mainline.lv1_name || "其他"} + + {/* 涨跌幅显示 - 在概念名称旁边 */} + {mainline.avg_change_pct != null && ( + = 0 ? "#fc8181" : "#68d391"} + flexShrink={0} + > + {mainline.avg_change_pct >= 0 ? "+" : ""} + {mainline.avg_change_pct.toFixed(2)}% + + )} + - {mainline.avg_change_pct >= 0 ? "+" : ""} - {mainline.avg_change_pct.toFixed(2)}% + {mainline.event_count} + + + {/* 显示上级概念名称作为副标题 */} + {mainline.parent_name && ( + + {mainline.grandparent_name ? `${mainline.grandparent_name} > ` : ""} + {mainline.parent_name} )} - - {mainline.event_count} - - - {/* 显示上级概念名称作为副标题 */} - {mainline.parent_name && ( - - {mainline.grandparent_name ? `${mainline.grandparent_name} > ` : ""} - {mainline.parent_name} - - )} - - - + + + + + {/* HOT 事件展示区域 */} + {hotEvent && ( + { + e.stopPropagation(); + onEventSelect?.(hotEvent); + }} + _hover={{ bg: "rgba(245, 101, 101, 0.15)" }} + transition="all 0.15s" + > + + {/* HOT 标签 */} + + + HOT + + {/* HOT 事件标题 */} + + {hotEvent.title} + + {/* HOT 事件涨幅 */} + {(hotEvent.max_change_pct ?? hotEvent.change_pct) != null && ( + + +{(hotEvent.max_change_pct ?? hotEvent.change_pct).toFixed(1)}% + + )} + + + )} + {/* 事件列表区域 */} {isExpanded ? ( - {/* 事件列表 - 单列布局 */} - + {/* 事件列表 - 带时间轴 */} + {displayedEvents.map((event) => ( - ))} @@ -229,27 +423,20 @@ const MainlineCard = React.memo( )} ) : ( - /* 折叠时显示简要信息 */ - - + /* 折叠时显示简要信息 - 也带时间轴 */ + + {mainline.events.slice(0, 4).map((event) => ( - { - e.stopPropagation(); - onEventSelect?.(event); - }} - > - • {event.title} - + event={event} + isSelected={selectedEvent?.id === event.id} + onEventClick={onEventSelect} + isHot={event.id === hotEventId} + /> ))} {mainline.events.length > 4 && ( - + ... 还有 {mainline.events.length - 4} 条 )}