refactor(ForecastReport): 合并营收/利润趋势与增长率图表

- 新增 IncomeProfitGrowthChart 合并组件
- 柱状图显示营业收入(左Y轴)
- 折线图显示净利润(左Y轴,渐变填充)
- 虚线显示增长率(右Y轴,红涨绿跌)
- 布局调整:合并图表独占一行,EPS/PE-PEG 两列

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
zdl
2025-12-16 20:37:24 +08:00
parent b52b54347d
commit 515b538c84
3 changed files with 160 additions and 8 deletions

View File

@@ -0,0 +1,144 @@
/**
* 营业收入、净利润趋势与增长率分析 - 合并图表
*/
import React, { useMemo } from 'react';
import ReactECharts from 'echarts-for-react';
import ChartCard from './ChartCard';
import { CHART_COLORS, BASE_CHART_CONFIG, THEME } from '../constants';
import type { IncomeProfitTrend, GrowthBars } from '../types';
interface IncomeProfitGrowthChartProps {
incomeProfitData: IncomeProfitTrend;
growthData: GrowthBars;
}
const IncomeProfitGrowthChart: React.FC<IncomeProfitGrowthChartProps> = ({
incomeProfitData,
growthData,
}) => {
const option = useMemo(() => ({
...BASE_CHART_CONFIG,
tooltip: {
...BASE_CHART_CONFIG.tooltip,
trigger: 'axis',
axisPointer: {
type: 'cross',
crossStyle: {
color: 'rgba(212, 175, 55, 0.5)',
},
},
},
legend: {
...BASE_CHART_CONFIG.legend,
data: ['营业总收入', '归母净利润', '营收增长率'],
bottom: 0,
},
grid: {
left: 60,
right: 60,
bottom: 50,
top: 40,
containLabel: false,
},
xAxis: {
...BASE_CHART_CONFIG.xAxis,
type: 'category',
data: incomeProfitData.years,
},
yAxis: [
{
...BASE_CHART_CONFIG.yAxis,
type: 'value',
name: '金额(百万元)',
position: 'left',
nameTextStyle: { color: THEME.textSecondary },
axisLabel: {
color: THEME.textSecondary,
formatter: (value: number) => {
if (Math.abs(value) >= 1000) {
return (value / 1000).toFixed(0) + 'k';
}
return value.toFixed(0);
},
},
},
{
...BASE_CHART_CONFIG.yAxis,
type: 'value',
name: '增长率(%)',
position: 'right',
nameTextStyle: { color: THEME.textSecondary },
axisLabel: {
color: THEME.textSecondary,
formatter: '{value}%',
},
splitLine: {
show: false,
},
},
],
series: [
{
name: '营业总收入',
type: 'bar',
data: incomeProfitData.income,
itemStyle: {
color: CHART_COLORS.income,
},
barMaxWidth: 30,
},
{
name: '归母净利润',
type: 'line',
data: incomeProfitData.profit,
smooth: true,
lineStyle: { width: 2, color: CHART_COLORS.profit },
itemStyle: { color: CHART_COLORS.profit },
areaStyle: {
color: {
type: 'linear',
x: 0,
y: 0,
x2: 0,
y2: 1,
colorStops: [
{ offset: 0, color: 'rgba(246, 173, 85, 0.3)' },
{ offset: 1, color: 'rgba(246, 173, 85, 0.05)' },
],
},
},
},
{
name: '营收增长率',
type: 'line',
yAxisIndex: 1,
data: growthData.revenue_growth_pct,
smooth: true,
lineStyle: { width: 2, type: 'dashed', color: '#10B981' },
itemStyle: {
color: (params: { value: number }) =>
params.value >= 0 ? THEME.positive : THEME.negative,
},
label: {
show: true,
position: 'top',
color: THEME.textSecondary,
fontSize: 10,
formatter: (params: { value: number }) =>
params.value !== null && params.value !== undefined
? `${params.value.toFixed(1)}%`
: '',
},
},
],
}), [incomeProfitData, growthData]);
return (
<ChartCard title="营业收入与净利润趋势 · 增长率分析" height={320}>
<ReactECharts option={option} style={{ height: 320 }} />
</ChartCard>
);
};
export default IncomeProfitGrowthChart;

View File

@@ -5,6 +5,7 @@
export { default as ChartCard } from './ChartCard';
export { default as IncomeProfitChart } from './IncomeProfitChart';
export { default as GrowthChart } from './GrowthChart';
export { default as IncomeProfitGrowthChart } from './IncomeProfitGrowthChart';
export { default as EpsChart } from './EpsChart';
export { default as PePegChart } from './PePegChart';
export { default as DetailTable } from './DetailTable';

View File

@@ -7,8 +7,7 @@ import { Box, SimpleGrid, HStack, Heading, Skeleton, IconButton } from '@chakra-
import { RefreshCw } from 'lucide-react';
import { stockService } from '@services/eventService';
import {
IncomeProfitChart,
GrowthChart,
IncomeProfitGrowthChart,
EpsChart,
PePegChart,
DetailTable,
@@ -94,12 +93,20 @@ const ForecastReport: React.FC<ForecastReportProps> = ({ stockCode: propStockCod
{/* 图表区域 */}
{data && (
<SimpleGrid columns={{ base: 1, md: 2 }} spacing={4}>
<IncomeProfitChart data={data.income_profit_trend} />
<GrowthChart data={data.growth_bars} />
<EpsChart data={data.eps_trend} />
<PePegChart data={data.pe_peg_axes} />
</SimpleGrid>
<>
{/* 合并图表:营收/利润/增长率 */}
<Box mb={4}>
<IncomeProfitGrowthChart
incomeProfitData={data.income_profit_trend}
growthData={data.growth_bars}
/>
</Box>
{/* EPS 和 PE/PEG */}
<SimpleGrid columns={{ base: 1, md: 2 }} spacing={4}>
<EpsChart data={data.eps_trend} />
<PePegChart data={data.pe_peg_axes} />
</SimpleGrid>
</>
)}
{/* 详细数据表格 */}