From 6515a47a4291bc535f24422f23a97b446a245a92 Mon Sep 17 00:00:00 2001 From: zzlgreat Date: Sun, 23 Nov 2025 12:31:34 +0800 Subject: [PATCH] update pay function --- src/views/Concept/ConceptTimelineModal.js | 681 ++++++++++++---------- 1 file changed, 364 insertions(+), 317 deletions(-) diff --git a/src/views/Concept/ConceptTimelineModal.js b/src/views/Concept/ConceptTimelineModal.js index 379a8a71..ef3e8e80 100644 --- a/src/views/Concept/ConceptTimelineModal.js +++ b/src/views/Concept/ConceptTimelineModal.js @@ -1,7 +1,12 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useMemo } from 'react'; import { logger } from '../../utils/logger'; import { useConceptTimelineEvents } from './hooks/useConceptTimelineEvents'; import RiskDisclaimer from '../../components/RiskDisclaimer'; +import FullCalendar from '@fullcalendar/react'; +import dayGridPlugin from '@fullcalendar/daygrid'; +import interactionPlugin from '@fullcalendar/interaction'; +import dayjs from 'dayjs'; +import 'dayjs/locale/zh-cn'; import { Modal, ModalOverlay, @@ -24,21 +29,28 @@ import { Divider, useToast, useDisclosure, + SimpleGrid, + Tooltip, } from '@chakra-ui/react'; import { ChevronDownIcon, ChevronRightIcon, ExternalLinkIcon, - ViewIcon + ViewIcon, + CalendarIcon, } from '@chakra-ui/icons'; import { FaChartLine, FaArrowUp, FaArrowDown, - FaHistory + FaHistory, + FaNewspaper, + FaFileAlt, } from 'react-icons/fa'; import { keyframes } from '@emotion/react'; +dayjs.locale('zh-cn'); + // 动画定义 const pulseAnimation = keyframes` 0% { transform: translate(-50%, -50%) scale(1); opacity: 0.5; } @@ -79,7 +91,11 @@ const ConceptTimelineModal = ({ const [timelineData, setTimelineData] = useState([]); const [loading, setLoading] = useState(true); - const [expandedDates, setExpandedDates] = useState({}); + const [selectedDate, setSelectedDate] = useState(null); + const [selectedDateData, setSelectedDateData] = useState(null); + + // 日期详情Modal + const { isOpen: isDateDetailOpen, onOpen: onDateDetailOpen, onClose: onDateDetailClose } = useDisclosure(); // 研报全文Modal相关状态 const [selectedReport, setSelectedReport] = useState(null); @@ -89,6 +105,71 @@ const ConceptTimelineModal = ({ const [selectedNews, setSelectedNews] = useState(null); const [isNewsModalOpen, setIsNewsModalOpen] = useState(false); + // 转换时间轴数据为日历事件格式 + const calendarEvents = useMemo(() => { + return timelineData.map(item => { + const priceInfo = getPriceInfo(item.price); + const hasEvents = item.events && item.events.length > 0; + const newsCount = item.events.filter(e => e.type === 'news').length; + const reportCount = item.events.filter(e => e.type === 'report').length; + + // 根据涨跌幅和事件确定颜色 + let backgroundColor = '#e2e8f0'; // 默认灰色(无数据) + if (hasEvents) { + backgroundColor = '#9F7AEA'; // 紫色(有事件) + } else if (item.price) { + if (priceInfo.color === 'red') { + backgroundColor = '#FC8181'; // 红色(上涨) + } else if (priceInfo.color === 'green') { + backgroundColor = '#68D391'; // 绿色(下跌) + } + } + + return { + id: item.date, + title: hasEvents + ? `📰${newsCount} 📊${reportCount}` + : (item.price ? priceInfo.text : ''), + date: item.date, + backgroundColor, + borderColor: backgroundColor, + extendedProps: { + ...item, + newsCount, + reportCount, + priceInfo, + } + }; + }); + }, [timelineData]); + + // 处理日期点击 + const handleDateClick = (info) => { + const clickedDate = info.dateStr; + const dateData = timelineData.find(item => item.date === clickedDate); + + if (dateData) { + setSelectedDate(clickedDate); + setSelectedDateData(dateData); + onDateDetailOpen(); + + // 追踪日期点击 + trackDateToggled(clickedDate, true); + } + }; + + // 处理事件点击 + const handleEventClick = (info) => { + const clickedDate = info.event.id; + const dateData = timelineData.find(item => item.date === clickedDate); + + if (dateData) { + setSelectedDate(clickedDate); + setSelectedDateData(dateData); + onDateDetailOpen(); + } + }; + // 获取时间轴数据 const fetchTimelineData = async () => { setLoading(true); @@ -535,321 +616,121 @@ const ConceptTimelineModal = ({ ) : timelineData.length > 0 ? ( - - - {timelineData.map((item, index) => { - const priceInfo = getPriceInfo(item.price); - const hasEvents = item.events.length > 0; - const isExpanded = expandedDates[item.date]; - const isLeft = index % 2 === 0; // 偶数项在左,奇数项在右 + + {/* 图例说明 */} + + + + 有新闻/研报 + + + + 上涨 + + + + 下跌 + + + + 无数据 + + - return ( - - - {/* 内容区域 */} - - - {/* 日期标签 */} - - - {formatDateDisplay(item.date)} - + {/* FullCalendar 日历组件 */} + + + - {item.price && ( - - - {priceInfo.icon && ( - - )} - {priceInfo.text} - - - )} - - - {/* 股票数量 */} - {item.price && item.price.stock_count && ( - - 📊 统计股票: {item.price.stock_count} 只 - - )} - - {/* 事件信息 */} - {hasEvents ? ( - - - - - - {item.events.map((event, eventIdx) => ( - - - - - {event.type === 'news' ? '新闻' : '研报'} - - {event.source && ( - {event.source} - )} - {event.publisher && ( - - {event.publisher} - - )} - {event.rating && ( - - {event.rating} - - )} - - - {event.title} - - - {event.content || '暂无内容'} - - - - - ))} - - - - ) : ( - - 📭 当日无相关资讯 - - )} - - - - {/* 时间轴节点 */} - - hasEvents && toggleDateExpand(item.date)} - _hover={hasEvents ? { - transform: 'scale(1.15)', - boxShadow: '0 6px 20px rgba(139, 92, 246, 0.4)' - } : {}} - transition="all 0.3s" - display="flex" - alignItems="center" - justifyContent="center" - > - {hasEvents && ( - <> - - {item.events.length} - - - - )} - - - {/* 连接线到下一个节点 */} - {index < timelineData.length - 1 && ( - - )} - - - - ); - })} - - - {/* 时间轴结束标记 */} - + {/* 底部说明 */} + )} + {/* 日期详情 Modal */} + {isDateDetailOpen && selectedDateData && ( + + + + + + + + {formatDateDisplay(selectedDate)} + + {selectedDateData.price && ( + + + + + {getPriceInfo(selectedDateData.price).text} + + + {selectedDateData.price.stock_count && ( + + 📊 统计股票: {selectedDateData.price.stock_count} 只 + + )} + + )} + + + + + + {selectedDateData.events && selectedDateData.events.length > 0 ? ( + + {selectedDateData.events.map((event, eventIdx) => ( + + + + + {event.type === 'news' ? '📰 新闻' : '📊 研报'} + + {event.source && ( + + {event.source} + + )} + {event.publisher && ( + + {event.publisher} + + )} + {event.rating && ( + + {event.rating} + + )} + {event.security_name && ( + + {event.security_name} + + )} + + + + {event.title} + + + + {event.content || '暂无内容'} + + + {event.time && ( + + 🕐 {formatDateTime(event.time)} + + )} + + + + + ))} + + ) : ( +
+ + + + 当日无新闻或研报 + + {selectedDateData.price && ( + + 仅有涨跌幅数据 + + )} + +
+ )} +
+ + + + +
+
+ )} + {/* 研报全文Modal */} {isReportModalOpen && (