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}%
)}
相关公司
{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}
影响度:
);
})}
{selectedEvent && (
{selectedEvent.event_title}
{selectedEvent.event_type}
{selectedEvent.event_date}
事件详情
{selectedEvent.event_desc}
{selectedEvent.related_info?.financial_impact && (
财务影响
{selectedEvent.related_info.financial_impact}
)}
影响评估
影响度
{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}
}
onClick={() => onToggleSegment(idx)}
colorScheme="blue"
>
{isExpanded ? "折叠" : "展开"}
业务描述
{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;