From 4d077adcf2af4fd464991c03050e9e6f8a9420e2 Mon Sep 17 00:00:00 2001 From: zzlgreat Date: Sat, 10 Jan 2026 18:44:58 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BA=8B=E4=BB=B6=E4=B8=AD=E5=BF=83=E7=9A=84?= =?UTF-8?q?=E6=B6=A8=E5=81=9C=E5=8E=9F=E5=9B=A0=E9=87=8C=E9=9D=A2=E5=92=8C?= =?UTF-8?q?=E4=BA=8B=E4=BB=B6=E7=9B=B8=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../RelatedEvents/RelatedEventCard.tsx | 382 +++++++++--------- .../RelatedEvents/RelatedEventsModal.tsx | 233 ++++++----- .../components/RelatedEvents/index.ts | 4 +- .../components/SectorItem.tsx | 86 +++- 4 files changed, 389 insertions(+), 316 deletions(-) diff --git a/src/views/LimitAnalyse/components/UnifiedSectorCard/components/RelatedEvents/RelatedEventCard.tsx b/src/views/LimitAnalyse/components/UnifiedSectorCard/components/RelatedEvents/RelatedEventCard.tsx index 884a3d1a..7371eb7a 100644 --- a/src/views/LimitAnalyse/components/UnifiedSectorCard/components/RelatedEvents/RelatedEventCard.tsx +++ b/src/views/LimitAnalyse/components/UnifiedSectorCard/components/RelatedEvents/RelatedEventCard.tsx @@ -2,7 +2,7 @@ * RelatedEventCard - 关联事件卡片组件 * 在板块卡片内显示涨停归因的关联事件 */ -import React, { memo } from 'react'; +import React, { memo } from "react"; import { Box, HStack, @@ -12,10 +12,10 @@ import { Icon, Progress, Tooltip, -} from '@chakra-ui/react'; -import { Newspaper, TrendingUp, ExternalLink } from 'lucide-react'; -import type { RelatedEvent } from '@/types/limitAnalyse'; -import { textColors, bgColors } from '@/constants/limitAnalyseTheme'; +} from "@chakra-ui/react"; +import { Newspaper, TrendingUp, ExternalLink } from "lucide-react"; +import type { RelatedEvent } from "@/types/limitAnalyse"; +import { textColors, bgColors } from "@/constants/limitAnalyseTheme"; interface RelatedEventCardProps { /** 事件数据 */ @@ -30,210 +30,218 @@ interface RelatedEventCardProps { * 根据相关度分数获取颜色 */ const getRelevanceColor = (score: number): string => { - if (score >= 80) return '#10B981'; // 高相关 - 绿色 - if (score >= 60) return '#F59E0B'; // 中相关 - 橙色 - return '#6B7280'; // 低相关 - 灰色 + if (score >= 80) return "#10B981"; // 高相关 - 绿色 + if (score >= 60) return "#F59E0B"; // 中相关 - 橙色 + return "#6B7280"; // 低相关 - 灰色 }; /** * 根据相关度分数获取标签 */ const getRelevanceLabel = (score: number): string => { - if (score >= 80) return '高度相关'; - if (score >= 60) return '中度相关'; - return '参考'; + if (score >= 80) return "高度相关"; + if (score >= 60) return "中度相关"; + return "参考"; }; /** * RelatedEventCard 组件 */ -const RelatedEventCard: React.FC = memo(({ - event, - compact = false, - onClick -}) => { - const relevanceColor = getRelevanceColor(event.relevance_score || 0); - const relevanceLabel = getRelevanceLabel(event.relevance_score || 0); +const RelatedEventCard: React.FC = memo( + ({ event, compact = false, onClick }) => { + const relevanceColor = getRelevanceColor(event.relevance_score || 0); + const relevanceLabel = getRelevanceLabel(event.relevance_score || 0); - const handleClick = () => { - onClick?.(event); - }; + const handleClick = () => { + onClick?.(event); + }; - // 紧凑模式(板块卡片内摘要) - if (compact) { + // 紧凑模式(板块卡片内摘要) + if (compact) { + return ( + + + + + + {event.title} + + + + {relevanceLabel} + + {event.match_score && event.match_score > 1 && ( + + 匹配{event.match_score}个概念 + + )} + + + + + ); + } + + // 完整模式(弹窗列表) return ( - - - - - {event.title} - - - + {/* 标题和跳转图标 */} + + + + - {relevanceLabel} - - {event.match_score && event.match_score > 1 && ( - - 匹配{event.match_score}个概念 - - )} + {event.title} + - - + {onClick && ( + + )} + + + {/* 相关度分数 */} + + + + + 相关度 + + + + div": { + background: `linear-gradient(90deg, ${relevanceColor}80, ${relevanceColor})`, + }, + }} + /> + + + {event.relevance_score || 0}分 + + + + {/* 匹配的概念 */} + {event.matched_concepts && event.matched_concepts.length > 0 && ( + + + 匹配概念: + + + {event.matched_concepts.slice(0, 5).map((concept, idx) => ( + + {concept} + + ))} + {event.matched_concepts.length > 5 && ( + + +{event.matched_concepts.length - 5} + + )} + + + )} + + {/* 相关原因 */} + {event.relevance_reason && ( + + + {event.relevance_reason} + + + )} + ); } +); - // 完整模式(弹窗列表) - return ( - - - {/* 标题和跳转图标 */} - - - - - {event.title} - - - {onClick && ( - - )} - - - {/* 相关度分数 */} - - - - - 相关度 - - - - div': { - background: `linear-gradient(90deg, ${relevanceColor}80, ${relevanceColor})`, - }, - }} - /> - - - {event.relevance_score || 0}分 - - - - {/* 匹配的概念 */} - {event.matched_concepts && event.matched_concepts.length > 0 && ( - - - 匹配概念: - - - {event.matched_concepts.slice(0, 5).map((concept, idx) => ( - - {concept} - - ))} - {event.matched_concepts.length > 5 && ( - - +{event.matched_concepts.length - 5} - - )} - - - )} - - {/* 相关原因 */} - {event.relevance_reason && ( - - - {event.relevance_reason} - - - )} - - - ); -}); - -RelatedEventCard.displayName = 'RelatedEventCard'; +RelatedEventCard.displayName = "RelatedEventCard"; export default RelatedEventCard; diff --git a/src/views/LimitAnalyse/components/UnifiedSectorCard/components/RelatedEvents/RelatedEventsModal.tsx b/src/views/LimitAnalyse/components/UnifiedSectorCard/components/RelatedEvents/RelatedEventsModal.tsx index 429ee2bd..f0dcf017 100644 --- a/src/views/LimitAnalyse/components/UnifiedSectorCard/components/RelatedEvents/RelatedEventsModal.tsx +++ b/src/views/LimitAnalyse/components/UnifiedSectorCard/components/RelatedEvents/RelatedEventsModal.tsx @@ -2,7 +2,7 @@ * RelatedEventsModal - 关联事件弹窗 * 显示板块完整的关联事件列表 */ -import React, { memo, useCallback } from 'react'; +import React, { memo, useCallback } from "react"; import { Modal, ModalOverlay, @@ -17,13 +17,18 @@ import { Icon, Box, Divider, -} from '@chakra-ui/react'; -import { Newspaper, TrendingUp, Layers } from 'lucide-react'; -import { useNavigate } from 'react-router-dom'; -import type { RelatedEvent } from '@/types/limitAnalyse'; -import { textColors, bgColors, borderColors, goldColors } from '@/constants/limitAnalyseTheme'; -import { scrollbarStyles } from '@/styles/limitAnalyseStyles'; -import RelatedEventCard from './RelatedEventCard'; +} from "@chakra-ui/react"; +import { Newspaper, TrendingUp, Layers } from "lucide-react"; +import { useNavigate } from "react-router-dom"; +import type { RelatedEvent } from "@/types/limitAnalyse"; +import { + textColors, + bgColors, + borderColors, + goldColors, +} from "@/constants/limitAnalyseTheme"; +import { scrollbarStyles } from "@/styles/limitAnalyseStyles"; +import RelatedEventCard from "./RelatedEventCard"; interface RelatedEventsModalProps { /** 是否打开 */ @@ -41,110 +46,128 @@ interface RelatedEventsModalProps { /** * RelatedEventsModal 组件 */ -const RelatedEventsModal: React.FC = memo(({ - isOpen, - onClose, - sectorName, - events, - limitUpCount = 0, -}) => { - const navigate = useNavigate(); +const RelatedEventsModal: React.FC = memo( + ({ isOpen, onClose, sectorName, events, limitUpCount = 0 }) => { + const navigate = useNavigate(); - // 点击事件跳转到事件详情 - const handleEventClick = useCallback((event: RelatedEvent) => { - onClose(); - navigate(`/community?event_id=${event.event_id}`); - }, [navigate, onClose]); + // 点击事件跳转到事件详情 + const handleEventClick = useCallback( + (event: RelatedEvent) => { + onClose(); + navigate(`/community?event_id=${event.event_id}`); + }, + [navigate, onClose] + ); - // 按相关度排序 - const sortedEvents = [...events].sort( - (a, b) => (b.relevance_score || 0) - (a.relevance_score || 0) - ); + // 按相关度排序 + const sortedEvents = [...events].sort( + (a, b) => (b.relevance_score || 0) - (a.relevance_score || 0) + ); - // 高相关事件数量 - const highRelevanceCount = events.filter(e => (e.relevance_score || 0) >= 80).length; + // 高相关事件数量 + const highRelevanceCount = events.filter( + (e) => (e.relevance_score || 0) >= 80 + ).length; - return ( - - - - {/* 头部 */} - - - - - - {sectorName} - 涨停归因分析 - - - - - - - 涨停 {limitUpCount} 只 - - - - - - 关联事件 {events.length} 条 - - - {highRelevanceCount > 0 && ( - + + {/* 头部 */} + + + + + - 高度相关 {highRelevanceCount} - - )} - - - - - - - - - {/* 事件列表 */} - - {sortedEvents.length === 0 ? ( - - - 暂无关联事件 - - ) : ( - - {sortedEvents.map((event, index) => ( - - ))} + {sectorName} - 涨停归因分析 + + + + + + + 涨停{" "} + + {limitUpCount} + {" "} + 只 + + + + + + 关联事件{" "} + + {events.length} + {" "} + 条 + + + {highRelevanceCount > 0 && ( + + 高度相关 {highRelevanceCount} + + )} + - )} - - - - ); -}); + -RelatedEventsModal.displayName = 'RelatedEventsModal'; + + + + + {/* 事件列表 */} + + {sortedEvents.length === 0 ? ( + + + 暂无关联事件 + + ) : ( + + {sortedEvents.map((event, index) => ( + + ))} + + )} + + + + ); + } +); + +RelatedEventsModal.displayName = "RelatedEventsModal"; export default RelatedEventsModal; diff --git a/src/views/LimitAnalyse/components/UnifiedSectorCard/components/RelatedEvents/index.ts b/src/views/LimitAnalyse/components/UnifiedSectorCard/components/RelatedEvents/index.ts index 3666fb48..e98e353e 100644 --- a/src/views/LimitAnalyse/components/UnifiedSectorCard/components/RelatedEvents/index.ts +++ b/src/views/LimitAnalyse/components/UnifiedSectorCard/components/RelatedEvents/index.ts @@ -1,5 +1,5 @@ /** * RelatedEvents 组件导出 */ -export { default as RelatedEventCard } from './RelatedEventCard'; -export { default as RelatedEventsModal } from './RelatedEventsModal'; +export { default as RelatedEventCard } from "./RelatedEventCard"; +export { default as RelatedEventsModal } from "./RelatedEventsModal"; diff --git a/src/views/LimitAnalyse/components/UnifiedSectorCard/components/SectorItem.tsx b/src/views/LimitAnalyse/components/UnifiedSectorCard/components/SectorItem.tsx index 31cbe528..e65d101b 100644 --- a/src/views/LimitAnalyse/components/UnifiedSectorCard/components/SectorItem.tsx +++ b/src/views/LimitAnalyse/components/UnifiedSectorCard/components/SectorItem.tsx @@ -2,7 +2,7 @@ * 单个板块项组件 * 支持 accordion 和 card 两种显示模式 */ -import React, { memo, useCallback, useMemo, useState } from "react"; +import React, { memo, useCallback, useMemo } from "react"; import { Box, VStack, @@ -17,7 +17,6 @@ import { WrapItem, Tag, TagLabel, - Accordion, AccordionItem, AccordionButton, AccordionPanel, @@ -26,15 +25,22 @@ import { useDisclosure, Divider, } from "@chakra-ui/react"; -import { Star, Zap, ChevronDown, ChevronUp, Sparkles, Newspaper, ChevronRight } from "lucide-react"; -import type { SectorData, LimitUpStock } from "@/types/limitAnalyse"; -import { formatLimitUpTime, getSectorColor } from "@/utils/limitAnalyseUtils"; +import { + Star, + Zap, + ChevronDown, + ChevronUp, + Sparkles, + Newspaper, + ChevronRight, +} from "lucide-react"; +import type { SectorData } from "@/types/limitAnalyse"; +import { formatLimitUpTime } from "@/utils/limitAnalyseUtils"; import { goldColors, textColors, bgColors, marketColors, - SECTOR_COLORS, } from "@/constants/limitAnalyseTheme"; import StockListItem from "./StockListItem"; import { RelatedEventCard, RelatedEventsModal } from "./RelatedEvents"; @@ -112,11 +118,13 @@ const SectorItem = memo( const sectorColor = getSectorSingleColor(sector); // 关联事件弹窗控制 - const { isOpen: isEventsModalOpen, onOpen: onOpenEventsModal, onClose: onCloseEventsModal } = useDisclosure(); + const { + isOpen: isEventsModalOpen, + onOpen: onOpenEventsModal, + onClose: onCloseEventsModal, + } = useDisclosure(); - const firstLimitTime = leadingStock - ? formatLimitUpTime(leadingStock) - : "-"; + const firstLimitTime = leadingStock ? formatLimitUpTime(leadingStock) : "-"; // 按时间排序的股票 const sortedStocks = useMemo(() => { @@ -133,10 +141,13 @@ const SectorItem = memo( }, [relatedEvents]); // 点击查看更多事件 - const handleViewMoreEvents = useCallback((e: React.MouseEvent) => { - e.stopPropagation(); - onOpenEventsModal(); - }, [onOpenEventsModal]); + const handleViewMoreEvents = useCallback( + (e: React.MouseEvent) => { + e.stopPropagation(); + onOpenEventsModal(); + }, + [onOpenEventsModal] + ); // Card 模式 if (displayMode === "card") { @@ -185,11 +196,19 @@ const SectorItem = memo( - + {sector} {sector === "公告" && ( - + )} @@ -214,7 +233,11 @@ const SectorItem = memo( {leadingStock && ( - + {leadingStock.sname} ( {/* Body - 展开的个股明细 */} - + {/* 关联事件区域 */} {relatedEvents.length > 0 && ( - + - + 涨停归因 ( - + {sector} {sector === "公告" && ( @@ -399,7 +437,11 @@ const SectorItem = memo( - + 涨停归因