将 1,796 行单文件拆分为原子设计模式结构: **atoms/** - 原子组件 - DisclaimerBox: 免责声明警告框 - ScoreBar: 评分进度条 - BusinessTreeItem: 业务树形项 - KeyFactorCard: 关键因素卡片 **components/** - Card 容器组件 - CorePositioningCard: 核心定位 - CompetitiveAnalysisCard: 竞争地位分析(含雷达图) - BusinessStructureCard: 业务结构 - ValueChainCard: 产业链分析 - KeyFactorsCard: 关键因素 - TimelineCard: 发展时间线 - BusinessSegmentsCard: 业务板块详情 - StrategyAnalysisCard: 战略分析 **organisms/** - 复杂组件 - ValueChainNodeCard: 产业链节点(含 RelatedCompaniesModal) - TimelineComponent: 时间线(含 EventDetailModal) **utils/** - chartOptions.ts: ECharts 图表配置 优化效果:主文件从 1,796 行减少到 117 行(-93%) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
140 lines
3.6 KiB
TypeScript
140 lines
3.6 KiB
TypeScript
/**
|
||
* DeepAnalysisTab 图表配置工具
|
||
*
|
||
* 生成雷达图和桑基图的 ECharts 配置
|
||
*/
|
||
|
||
import type {
|
||
ComprehensiveData,
|
||
ValueChainData,
|
||
RadarChartOption,
|
||
SankeyChartOption,
|
||
} from '../types';
|
||
|
||
/**
|
||
* 生成竞争力雷达图配置
|
||
* @param comprehensiveData - 综合分析数据
|
||
* @returns ECharts 雷达图配置,或 null(数据不足时)
|
||
*/
|
||
export const getRadarChartOption = (
|
||
comprehensiveData?: ComprehensiveData
|
||
): RadarChartOption | null => {
|
||
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: { value: number }) => params.value,
|
||
color: '#3182ce',
|
||
fontSize: 10,
|
||
},
|
||
},
|
||
],
|
||
},
|
||
],
|
||
};
|
||
};
|
||
|
||
/**
|
||
* 生成产业链桑基图配置
|
||
* @param valueChainData - 产业链数据
|
||
* @returns ECharts 桑基图配置,或 null(数据不足时)
|
||
*/
|
||
export const getSankeyChartOption = (
|
||
valueChainData?: ValueChainData
|
||
): SankeyChartOption | null => {
|
||
if (
|
||
!valueChainData?.value_chain_flows ||
|
||
valueChainData.value_chain_flows.length === 0
|
||
) {
|
||
return null;
|
||
}
|
||
|
||
const nodes = new Set<string>();
|
||
const links: Array<{
|
||
source: string;
|
||
target: string;
|
||
value: number;
|
||
lineStyle: { color: string; opacity: number };
|
||
}> = [];
|
||
|
||
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') || 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 },
|
||
},
|
||
],
|
||
};
|
||
};
|