import React, { useState } from "react"; import { Box, VStack, HStack, Text, Badge, Card, CardBody, CardHeader, Heading, SimpleGrid, Divider, Center, Alert, AlertIcon, Tabs, TabList, TabPanels, Tab, TabPanel, Button, Tag, TagLabel, Icon, Tooltip, Grid, GridItem, useToast, IconButton, Progress, Stat, StatLabel, StatNumber, StatHelpText, Accordion, AccordionItem, AccordionButton, AccordionPanel, AccordionIcon, Fade, ScaleFade, useDisclosure, Modal, ModalOverlay, ModalContent, ModalHeader, ModalFooter, ModalBody, ModalCloseButton, Circle, Spinner, } from "@chakra-ui/react"; import { FaBuilding, FaChartLine, FaLightbulb, FaRocket, FaNetworkWired, FaCog, FaTrophy, FaShieldAlt, FaChartPie, FaHistory, FaCheckCircle, FaExclamationCircle, FaArrowUp, FaArrowDown, FaArrowRight, FaArrowLeft, FaStar, FaUserTie, FaIndustry, FaDollarSign, FaBalanceScale, FaFlask, FaHandshake, FaUsers, FaCalendarAlt, FaExpandAlt, FaCompressAlt, } from "react-icons/fa"; import { ExternalLinkIcon } from "@chakra-ui/icons"; import ReactECharts from "echarts-for-react"; import { logger } from "@utils/logger"; import { getApiBase } from "@utils/apiConfig"; const API_BASE_URL = getApiBase(); // 格式化工具 const formatUtils = { formatCurrency: (value) => { if (!value && value !== 0) return "-"; const absValue = Math.abs(value); if (absValue >= 100000000) { return (value / 100000000).toFixed(2) + "亿元"; } else if (absValue >= 10000) { return (value / 10000).toFixed(2) + "万元"; } return value.toFixed(2) + "元"; }, formatBusinessRevenue: (value, unit) => { if (!value && value !== 0) return "-"; if (unit) { if (unit === "元") { const absValue = Math.abs(value); if (absValue >= 100000000) { return (value / 100000000).toFixed(2) + "亿元"; } else if (absValue >= 10000) { return (value / 10000).toFixed(2) + "万元"; } return value.toFixed(0) + "元"; } else if (unit === "万元") { const absValue = Math.abs(value); if (absValue >= 10000) { return (value / 10000).toFixed(2) + "亿元"; } return value.toFixed(2) + "万元"; } else if (unit === "亿元") { return value.toFixed(2) + "亿元"; } else { return value.toFixed(2) + unit; } } const absValue = Math.abs(value); if (absValue >= 100000000) { return (value / 100000000).toFixed(2) + "亿元"; } else if (absValue >= 10000) { return (value / 10000).toFixed(2) + "万元"; } return value.toFixed(2) + "元"; }, formatPercentage: (value) => { if (!value && value !== 0) return "-"; return value.toFixed(2) + "%"; }, }; // 免责声明组件 const DisclaimerBox = () => { return ( 免责声明 本内容由AI模型基于新闻、公告、研报等公开信息自动分析和生成,未经许可严禁转载。 所有内容仅供参考,不构成任何投资建议,请投资者注意风险,独立审慎决策。 ); }; // 评分进度条组件 const ScoreBar = ({ label, score, icon }) => { const percentage = (score / 100) * 100; const getColorScheme = () => { if (percentage >= 80) return "purple"; if (percentage >= 60) return "blue"; if (percentage >= 40) return "yellow"; return "orange"; }; return ( {icon && ( )} {label} {score || 0} ); }; // 业务结构树形图组件 const BusinessTreeItem = ({ business, depth = 0 }) => { const bgColor = "gray.50"; return ( 0 ? `4px solid` : "none"} borderLeftColor="blue.400" borderRadius="md" mb={2} _hover={{ shadow: "md" }} transition="all 0.2s" > {business.business_name} {business.financial_metrics?.revenue_ratio > 30 && ( 核心业务 )} 营收占比:{" "} {formatUtils.formatPercentage( business.financial_metrics?.revenue_ratio )} 毛利率:{" "} {formatUtils.formatPercentage( business.financial_metrics?.gross_margin )} {business.growth_metrics?.revenue_growth && ( 0 ? "red" : "green" } > 增长: {business.growth_metrics.revenue_growth > 0 ? "+" : ""} {formatUtils.formatPercentage( business.growth_metrics.revenue_growth )} )} {(() => { const revenue = business.revenue || business.financial_metrics?.revenue; const unit = business.revenue_unit; if (revenue || revenue === 0) { return formatUtils.formatBusinessRevenue(revenue, unit); } return "-"; })()} 营业收入 ); }; // 产业链节点卡片 const ValueChainNodeCard = ({ node, isCompany = false, level = 0 }) => { const { isOpen, onOpen, onClose } = useDisclosure(); const [relatedCompanies, setRelatedCompanies] = useState([]); const [loadingRelated, setLoadingRelated] = useState(false); const toast = useToast(); const getColorScheme = () => { if (isCompany) return "blue"; if (level < 0) return "orange"; if (level > 0) return "green"; return "gray"; }; const colorScheme = getColorScheme(); const bgColor = `${colorScheme}.50`; const borderColor = `${colorScheme}.200`; const getNodeTypeIcon = (type) => { const icons = { company: FaBuilding, supplier: FaHandshake, customer: FaUserTie, product: FaIndustry, service: FaCog, channel: FaNetworkWired, raw_material: FaFlask, }; return icons[type] || FaBuilding; }; const getImportanceColor = (score) => { if (score >= 80) return "red"; if (score >= 60) return "orange"; if (score >= 40) return "yellow"; return "green"; }; const fetchRelatedCompanies = async () => { setLoadingRelated(true); try { const response = await fetch( `${API_BASE_URL}/api/company/value-chain/related-companies?node_name=${encodeURIComponent( node.node_name )}` ); const data = await response.json(); if (data.success) { setRelatedCompanies(data.data || []); } else { toast({ title: "获取相关公司失败", description: data.message, status: "error", duration: 3000, isClosable: true, }); } } catch (error) { logger.error("ValueChainNodeCard", "fetchRelatedCompanies", error, { node_name: node.node_name, }); toast({ title: "获取相关公司失败", description: error.message, status: "error", duration: 3000, isClosable: true, }); } finally { setLoadingRelated(false); } }; const handleCardClick = () => { onOpen(); if (relatedCompanies.length === 0) { fetchRelatedCompanies(); } }; return ( <> {isCompany && ( 核心企业 )} {node.importance_score >= 70 && ( )} {node.node_name} {node.node_description && ( {node.node_description} )} {node.node_type} {node.market_share && ( 份额 {node.market_share}% )} {(node.importance_score || node.importance_score === 0) && ( 重要度 {node.importance_score} )} {node.node_name} {node.node_type} {isCompany && ( 核心企业 )} {node.node_description && ( 节点描述 {node.node_description} )} 重要度评分 {node.importance_score || 0} {node.market_share && ( 市场份额 {node.market_share}% )} {node.dependency_degree && ( 依赖程度 {node.dependency_degree}% 50 ? "orange" : "green" } borderRadius="full" /> )} 相关公司 {loadingRelated && } {loadingRelated ? (
) : relatedCompanies.length > 0 ? ( {relatedCompanies.map((company, idx) => { const getLevelLabel = (level) => { if (level < 0) return { text: "上游", color: "orange" }; if (level === 0) return { text: "核心", color: "blue" }; if (level > 0) return { text: "下游", color: "green" }; return { text: "未知", color: "gray" }; }; const levelInfo = getLevelLabel( company.node_info?.node_level ); return ( {company.stock_name} {company.stock_code} {levelInfo.text} {company.company_name && ( {company.company_name} )} } variant="ghost" colorScheme="blue" onClick={() => { window.location.href = `/company?stock_code=${company.stock_code}`; }} aria-label="查看公司详情" /> {company.node_info?.node_description && ( {company.node_info.node_description} )} {company.relationships && company.relationships.length > 0 && ( 产业链关系: {company.relationships.map( (rel, ridx) => ( {rel.role === "source" ? "流向" : "来自"} {rel.connected_node} ) )} )} ); })} ) : (
暂无相关公司
)}
); }; // 关键因素卡片 const KeyFactorCard = ({ factor }) => { const impactColor = { positive: "red", negative: "green", neutral: "gray", mixed: "yellow", }[factor.impact_direction] || "gray"; const bgColor = "white"; const borderColor = "gray.200"; return ( {factor.factor_name} {factor.impact_direction === "positive" ? "正面" : factor.impact_direction === "negative" ? "负面" : factor.impact_direction === "mixed" ? "混合" : "中性"} {factor.factor_value} {factor.factor_unit && ` ${factor.factor_unit}`} {factor.year_on_year && ( 0 ? "red" : "green"} > 0 ? FaArrowUp : FaArrowDown} mr={1} boxSize={3} /> {Math.abs(factor.year_on_year)}% )} {factor.factor_desc && ( {factor.factor_desc} )} 影响权重: {factor.impact_weight} {factor.report_period && ( {factor.report_period} )} ); }; // 时间线组件 const TimelineComponent = ({ events }) => { const [selectedEvent, setSelectedEvent] = useState(null); const { isOpen, onOpen, onClose } = useDisclosure(); // 背景颜色 const positiveBgColor = "red.50"; const negativeBgColor = "green.50"; const handleEventClick = (event) => { setSelectedEvent(event); onOpen(); }; return ( <> {events.map((event, idx) => { const isPositive = event.impact_metrics?.is_positive; const iconColor = isPositive ? "red.500" : "green.500"; const bgColor = isPositive ? positiveBgColor : negativeBgColor; return ( handleEventClick(event)} _hover={{ shadow: "lg", transform: "translateX(4px)" }} transition="all 0.3s ease" > {event.event_title} {event.event_date} {event.event_type} {event.event_desc} 影响度: 70 ? "red" : "orange" } borderRadius="full" /> {event.impact_metrics?.impact_score || 0} ); })} {selectedEvent && ( {selectedEvent.event_title} {selectedEvent.event_type} {selectedEvent.event_date} 事件详情 {selectedEvent.event_desc} {selectedEvent.related_info?.financial_impact && ( 财务影响 {selectedEvent.related_info.financial_impact} )} 影响评估 影响度 70 ? "red" : "orange" } hasStripe isAnimated /> {selectedEvent.impact_metrics?.impact_score || 0}/100 {selectedEvent.impact_metrics?.is_positive ? "正面影响" : "负面影响"} )} ); }; // 生成雷达图配置 const getRadarChartOption = (comprehensiveData) => { if (!comprehensiveData?.competitive_position?.scores) return null; const scores = comprehensiveData.competitive_position.scores; const indicators = [ { name: "市场地位", max: 100 }, { name: "技术实力", max: 100 }, { name: "品牌价值", max: 100 }, { name: "运营效率", max: 100 }, { name: "财务健康", max: 100 }, { name: "创新能力", max: 100 }, { name: "风险控制", max: 100 }, { name: "成长潜力", max: 100 }, ]; const data = [ scores.market_position || 0, scores.technology || 0, scores.brand || 0, scores.operation || 0, scores.finance || 0, scores.innovation || 0, scores.risk || 0, scores.growth || 0, ]; return { tooltip: { trigger: "item" }, radar: { indicator: indicators, shape: "polygon", splitNumber: 4, name: { textStyle: { color: "#666", fontSize: 12 } }, splitLine: { lineStyle: { color: ["#e8e8e8", "#e0e0e0", "#d0d0d0", "#c0c0c0"] }, }, splitArea: { show: true, areaStyle: { color: ["rgba(250,250,250,0.3)", "rgba(200,200,200,0.3)"], }, }, axisLine: { lineStyle: { color: "#ddd" } }, }, series: [ { name: "竞争力评分", type: "radar", data: [ { value: data, name: "当前评分", symbol: "circle", symbolSize: 5, lineStyle: { width: 2, color: "#3182ce" }, areaStyle: { color: "rgba(49, 130, 206, 0.3)" }, label: { show: true, formatter: (params) => params.value, color: "#3182ce", fontSize: 10, }, }, ], }, ], }; }; // 生成桑基图配置 const getSankeyChartOption = (valueChainData) => { if ( !valueChainData?.value_chain_flows || valueChainData.value_chain_flows.length === 0 ) return null; const nodes = new Set(); const links = []; valueChainData.value_chain_flows.forEach((flow) => { if (!flow?.source?.node_name || !flow?.target?.node_name) return; nodes.add(flow.source.node_name); nodes.add(flow.target.node_name); links.push({ source: flow.source.node_name, target: flow.target.node_name, value: parseFloat(flow.flow_metrics?.flow_ratio) || 1, lineStyle: { color: "source", opacity: 0.6 }, }); }); return { tooltip: { trigger: "item", triggerOn: "mousemove" }, series: [ { type: "sankey", layout: "none", emphasis: { focus: "adjacency" }, data: Array.from(nodes).map((name) => ({ name })), links: links, lineStyle: { color: "gradient", curveness: 0.5 }, label: { color: "#333", fontSize: 10 }, }, ], }; }; // 深度分析 Tab 主组件 const DeepAnalysisTab = ({ comprehensiveData, valueChainData, keyFactorsData, loading, cardBg, expandedSegments, onToggleSegment, }) => { const blueBg = "blue.50"; const greenBg = "green.50"; const purpleBg = "purple.50"; const orangeBg = "orange.50"; if (loading) { return (
加载深度分析数据...
); } return ( {/* 核心定位卡片 */} {comprehensiveData?.qualitative_analysis && ( 核心定位 {comprehensiveData.qualitative_analysis.core_positioning ?.one_line_intro && ( { comprehensiveData.qualitative_analysis.core_positioning .one_line_intro } )} 投资亮点 {comprehensiveData.qualitative_analysis.core_positioning ?.investment_highlights || "暂无数据"} 商业模式 {comprehensiveData.qualitative_analysis.core_positioning ?.business_model_desc || "暂无数据"} )} {/* 竞争地位分析 */} {comprehensiveData?.competitive_position && ( 竞争地位分析 {comprehensiveData.competitive_position.ranking && ( 行业排名{" "} {comprehensiveData.competitive_position.ranking.industry_rank} / { comprehensiveData.competitive_position.ranking .total_companies } )} {comprehensiveData.competitive_position.analysis ?.main_competitors && ( 主要竞争对手 {comprehensiveData.competitive_position.analysis.main_competitors .split(",") .map((competitor, idx) => ( {competitor.trim()} ))} )} {getRadarChartOption(comprehensiveData) && ( )} 竞争优势 {comprehensiveData.competitive_position.analysis ?.competitive_advantages || "暂无数据"} 竞争劣势 {comprehensiveData.competitive_position.analysis ?.competitive_disadvantages || "暂无数据"} )} {/* 业务结构分析 */} {comprehensiveData?.business_structure && comprehensiveData.business_structure.length > 0 && ( 业务结构分析 {comprehensiveData.business_structure[0]?.report_period} {comprehensiveData.business_structure.map((business, idx) => ( ))} )} {/* 产业链分析 */} {valueChainData && ( 产业链分析 上游 {valueChainData.analysis_summary?.upstream_nodes || 0} 核心 {valueChainData.analysis_summary?.company_nodes || 0} 下游 {valueChainData.analysis_summary?.downstream_nodes || 0} 层级视图 流向关系 {(valueChainData.value_chain_structure?.nodes_by_level?.[ "level_-2" ] || valueChainData.value_chain_structure?.nodes_by_level?.[ "level_-1" ]) && ( 上游供应链 原材料与供应商 {[ ...(valueChainData.value_chain_structure ?.nodes_by_level?.["level_-2"] || []), ...(valueChainData.value_chain_structure ?.nodes_by_level?.["level_-1"] || []), ].map((node, idx) => ( ))} )} {valueChainData.value_chain_structure?.nodes_by_level?.[ "level_0" ] && ( 核心企业 公司主体与产品 {valueChainData.value_chain_structure.nodes_by_level[ "level_0" ].map((node, idx) => ( ))} )} {(valueChainData.value_chain_structure?.nodes_by_level?.[ "level_1" ] || valueChainData.value_chain_structure?.nodes_by_level?.[ "level_2" ]) && ( 下游客户 客户与终端市场 {[ ...(valueChainData.value_chain_structure ?.nodes_by_level?.["level_1"] || []), ...(valueChainData.value_chain_structure ?.nodes_by_level?.["level_2"] || []), ].map((node, idx) => ( ))} )} {getSankeyChartOption(valueChainData) ? ( ) : (
暂无流向数据
)}
)} {/* 关键因素与发展时间线 */} {keyFactorsData?.key_factors && ( 关键因素 {keyFactorsData.key_factors.total_factors} 项 {keyFactorsData.key_factors.categories.map( (category, idx) => ( {category.category_name} {category.factors.length} {category.factors.map((factor, fidx) => ( ))} ) )} )} {keyFactorsData?.development_timeline && ( 发展时间线 正面{" "} {keyFactorsData.development_timeline.statistics ?.positive_events || 0} 负面{" "} {keyFactorsData.development_timeline.statistics ?.negative_events || 0} )} {/* 业务板块详情 */} {comprehensiveData?.business_segments && comprehensiveData.business_segments.length > 0 && ( 业务板块详情 {comprehensiveData.business_segments.length} 个板块 {comprehensiveData.business_segments.map((segment, idx) => { const isExpanded = expandedSegments[idx]; return ( {segment.segment_name} 业务描述 {segment.segment_description || "暂无描述"} 竞争地位 {segment.competitive_position || "暂无数据"} 未来潜力 {segment.future_potential || "暂无数据"} {isExpanded && segment.key_products && ( 主要产品 {segment.key_products} )} {isExpanded && segment.market_share && ( 市场份额 {segment.market_share}% )} {isExpanded && segment.revenue_contribution && ( 营收贡献 {segment.revenue_contribution}% )} ); })} )} {/* 战略分析 */} {comprehensiveData?.qualitative_analysis?.strategy && ( 战略分析 战略方向 {comprehensiveData.qualitative_analysis.strategy .strategy_description || "暂无数据"} 战略举措 {comprehensiveData.qualitative_analysis.strategy .strategic_initiatives || "暂无数据"} )}
); }; export default DeepAnalysisTab;