refactor(ForecastReport): 架构优化与性能提升

阶段一 - 核心优化:
- 所有子组件添加 React.memo 防止不必要重渲染
- 图表组件统一使用 EChartsWrapper 替代 ReactECharts
- 提取 isForecastYear、IMPORTANT_METRICS 到 constants.ts
- DetailTable 样式提取为 DETAIL_TABLE_STYLES 常量

阶段二 - 架构优化:
- 新增 hooks/useForecastData.ts:数据获取 + Map 缓存 + AbortController
- 新增 services/forecastService.ts:API 封装层
- 新增 utils/chartFormatters.ts:图表格式化工具函数
- 主组件精简:79行 → 63行,添加错误处理和重试功能

优化效果:
- 消除 4 处 isForecastYear 重复定义
- 样式从每次渲染重建改为常量复用
- 添加请求缓存,避免频繁切换时重复请求

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
zdl
2025-12-19 14:17:21 +08:00
parent ec9c5af0be
commit 00cd098968
13 changed files with 313 additions and 166 deletions

View File

@@ -0,0 +1,43 @@
/**
* 图表格式化工具函数
*/
// 从 constants 重新导出工具函数
export { isForecastYear, IMPORTANT_METRICS } from '../constants';
/**
* 格式化 Tooltip 值
* @param value 数值
* @param type 类型:金额、百分比、比率
*/
export const formatTooltipValue = (
value: number,
type: 'amount' | 'percent' | 'ratio'
): string => {
if (type === 'amount') {
return `${(value / 100000000).toFixed(2)}亿`;
}
if (type === 'percent') {
return `${value.toFixed(2)}%`;
}
return value.toFixed(2);
};
/**
* 查找预测年份起始索引
* @param years 年份数组
*/
export const findForecastStartIndex = (years: string[]): number => {
return years.findIndex(year => year.includes('E'));
};
/**
* 格式化金额(百万 → 亿)
* @param value 金额(百万元)
*/
export const formatAmount = (value: number): string => {
if (Math.abs(value) >= 1000) {
return `${(value / 1000).toFixed(1)}k`;
}
return value.toFixed(0);
};

View File

@@ -0,0 +1,7 @@
export {
isForecastYear,
IMPORTANT_METRICS,
formatTooltipValue,
findForecastStartIndex,
formatAmount,
} from './chartFormatters';