diff --git a/src/views/Community/components/DynamicNews/layouts/MainlineTimelineView.js b/src/views/Community/components/DynamicNews/layouts/MainlineTimelineView.js index c820fce2..0095b01f 100644 --- a/src/views/Community/components/DynamicNews/layouts/MainlineTimelineView.js +++ b/src/views/Community/components/DynamicNews/layouts/MainlineTimelineView.js @@ -1,5 +1,5 @@ // src/views/Community/components/DynamicNews/layouts/MainlineTimelineView.js -// 主线时间轴布局组件 - 按 lv2 概念分组展示事件 +// 主线时间轴布局组件 - 按 lv2 概念分组展示事件(卡片式布局) import React, { useState, @@ -28,9 +28,8 @@ import { ChevronDownIcon, ChevronRightIcon, RepeatIcon, - TimeIcon, } from "@chakra-ui/icons"; -import { FiTrendingUp, FiClock, FiZap } from "react-icons/fi"; +import { FiTrendingUp, FiZap } from "react-icons/fi"; import DynamicNewsEventCard from "../../EventCard/DynamicNewsEventCard"; import { getApiBase } from "@utils/apiConfig"; @@ -39,8 +38,8 @@ import { getApiBase } from "@utils/apiConfig"; * * 功能: * 1. 调用 /api/events/mainline 获取预分组数据 - * 2. 按 lv2 概念分组展示,时间轴样式 - * 3. 不使用无限滚动,一次性加载全部数据 + * 2. 按 lv2 概念分组展示,卡片式布局 + 内部时间轴 + * 3. 按事件数量从多到少排序 */ const MainlineTimelineViewComponent = forwardRef( ( @@ -62,14 +61,15 @@ const MainlineTimelineViewComponent = forwardRef( const [mainlineData, setMainlineData] = useState(null); const [expandedGroups, setExpandedGroups] = useState({}); - // 主题颜色 - const bgColor = useColorModeValue("white", "gray.800"); - const headerBg = useColorModeValue("gray.50", "gray.700"); - const headerHoverBg = useColorModeValue("gray.100", "gray.600"); - const textColor = useColorModeValue("gray.700", "gray.200"); - const secondaryTextColor = useColorModeValue("gray.500", "gray.400"); - const timelineBorderColor = useColorModeValue("gray.200", "gray.600"); - const timelineLineColor = useColorModeValue("blue.200", "blue.600"); + // 主题颜色 - 与列表模式统一的深色风格 + const cardBg = useColorModeValue("white", "gray.800"); + const cardBorderColor = useColorModeValue("gray.200", "gray.600"); + const headerBg = useColorModeValue("gray.50", "gray.750"); + const headerHoverBg = useColorModeValue("gray.100", "gray.700"); + const textColor = useColorModeValue("gray.800", "gray.100"); + const secondaryTextColor = useColorModeValue("gray.600", "gray.400"); + const timelineLineColor = useColorModeValue("blue.300", "blue.500"); + const timelineDotBg = useColorModeValue("blue.500", "blue.400"); const scrollbarTrackBg = useColorModeValue("#f1f1f1", "#2D3748"); const scrollbarThumbBg = useColorModeValue("#888", "#4A5568"); const scrollbarThumbHoverBg = useColorModeValue("#555", "#718096"); @@ -179,11 +179,19 @@ const MainlineTimelineViewComponent = forwardRef( }); if (result.success) { - setMainlineData(result.data); + // 按事件数量从多到少排序 + const sortedMainlines = [...(result.data.mainlines || [])].sort( + (a, b) => b.event_count - a.event_count + ); + + setMainlineData({ + ...result.data, + mainlines: sortedMainlines, + }); // 初始化展开状态(默认展开前5个) const initialExpanded = {}; - result.data.mainlines?.slice(0, 5).forEach((mainline) => { + sortedMainlines.slice(0, 5).forEach((mainline) => { initialExpanded[mainline.lv2_id] = true; }); setExpandedGroups(initialExpanded); @@ -208,7 +216,7 @@ const MainlineTimelineViewComponent = forwardRef( ref, () => ({ refresh: fetchMainlineData, - getScrollPosition: () => null, // 不使用滚动位置判断 + getScrollPosition: () => null, }), [fetchMainlineData] ); @@ -295,11 +303,10 @@ const MainlineTimelineViewComponent = forwardRef( - + {mainline_count} 条主线 - - - - {total_events} 个事件 - - + + {total_events} 个事件 + {ungrouped_count > 0 && ( - + ({ungrouped_count} 个未归类) )} - + } - size="sm" + size="xs" variant="ghost" onClick={() => toggleAll(true)} aria-label="全部展开" @@ -362,7 +365,7 @@ const MainlineTimelineViewComponent = forwardRef( } - size="sm" + size="xs" variant="ghost" onClick={() => toggleAll(false)} aria-label="全部折叠" @@ -371,7 +374,7 @@ const MainlineTimelineViewComponent = forwardRef( } - size="sm" + size="xs" variant="ghost" onClick={fetchMainlineData} aria-label="刷新" @@ -380,151 +383,144 @@ const MainlineTimelineViewComponent = forwardRef( - {/* 主线列表 - 时间轴样式 */} - - {/* 时间轴垂直线 */} - + {/* 主线卡片列表 */} + + {mainlines.map((mainline) => { + const colorScheme = getColorScheme(mainline.lv2_name); + const isExpanded = expandedGroups[mainline.lv2_id]; - - {mainlines.map((mainline, index) => { - const colorScheme = getColorScheme(mainline.lv2_name); - const isExpanded = expandedGroups[mainline.lv2_id]; - - return ( - - {/* 时间轴圆点 */} - - - {/* 分组头部 */} - toggleGroup(mainline.lv2_id)} - transition="all 0.2s" - boxShadow="sm" - > - - - - - - {mainline.lv2_name} - - - {mainline.event_count} 条 - - + return ( + + {/* 卡片头部 - 概念名称 */} + toggleGroup(mainline.lv2_id)} + transition="all 0.2s" + borderBottomWidth={isExpanded ? "1px" : "0"} + borderBottomColor={cardBorderColor} + > + + + + + + + {mainline.lv2_name || "其他"} + + + {mainline.event_count} 条 + + + {mainline.lv1_name && ( {mainline.lv1_name} - - + )} + + + - - - - #{index + 1} - - - - - {/* 分组内容 */} - + {/* 卡片内容 - 时间轴事件列表 */} + + + {/* 时间轴线 */} - - {mainline.events.map((event, eventIndex) => ( - - { - onEventSelect?.(clickedEvent); - }} - onTitleClick={(e) => { - e.preventDefault(); - e.stopPropagation(); - onEventSelect?.(event); - }} - onToggleFollow={() => onToggleFollow?.(event.id)} - borderColor={borderColor} - /> - - ))} - - - - - ); - })} - - + position="absolute" + left="24px" + top="12px" + bottom="12px" + w="2px" + bg={timelineLineColor} + opacity={0.4} + borderRadius="full" + /> + + {/* 事件列表 */} + + {mainline.events.map((event, eventIndex) => ( + + {/* 时间轴圆点 */} + + {/* 事件卡片 */} + { + onEventSelect?.(clickedEvent); + }} + onTitleClick={(e) => { + e.preventDefault(); + e.stopPropagation(); + onEventSelect?.(event); + }} + onToggleFollow={() => onToggleFollow?.(event.id)} + borderColor={borderColor} + /> + + ))} + + + + + ); + })} + ); }