feat(ForecastReport): 添加盈利预测骨架屏

- 创建 ForecastSkeleton 组件(图表卡片 + 表格)
- 初始加载时显示骨架屏

🤖 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 15:18:00 +08:00
parent 27b0e9375a
commit 6eec7c6402
5 changed files with 90 additions and 272 deletions

View File

@@ -1,264 +0,0 @@
/**
* 统一的财务 Tab 组件
*
* 使用 UnifiedFinancialTable 实现所有 10 个财务表格:
* - 7 个财务指标分类 Tab
* - 3 个财务报表 Tab
*/
import React, { memo } from 'react';
import { UnifiedFinancialTable } from '../components/UnifiedFinancialTable';
import {
FINANCIAL_METRICS_CATEGORIES,
CURRENT_ASSETS_METRICS,
NON_CURRENT_ASSETS_METRICS,
TOTAL_ASSETS_METRICS,
CURRENT_LIABILITIES_METRICS,
NON_CURRENT_LIABILITIES_METRICS,
TOTAL_LIABILITIES_METRICS,
EQUITY_METRICS,
INCOME_STATEMENT_SECTIONS,
CASHFLOW_METRICS,
} from '../constants';
import type { FinancialMetricsData, BalanceSheetData, IncomeStatementData, CashflowData } from '../types';
// ==================== 通用 Props 类型 ====================
/** 财务指标 Tab Props */
export interface MetricsTabProps {
financialMetrics: FinancialMetricsData[];
loading?: boolean;
loadingTab?: string | null;
showMetricChart: (name: string, key: string, data: unknown[], path: string) => void;
calculateYoYChange: (value: number, period: string, data: unknown[], path: string) => { change: number; intensity: number };
getCellBackground: (change: number, intensity: number) => string;
positiveColor: string;
negativeColor: string;
bgColor: string;
hoverBg: string;
}
/** 资产负债表 Tab Props */
export interface BalanceSheetTabProps {
balanceSheet: BalanceSheetData[];
loading?: boolean;
loadingTab?: string | null;
showMetricChart: (name: string, key: string, data: unknown[], path: string) => void;
calculateYoYChange: (value: number, period: string, data: unknown[], path: string) => { change: number; intensity: number };
getCellBackground: (change: number, intensity: number) => string;
positiveColor: string;
negativeColor: string;
bgColor: string;
hoverBg: string;
}
/** 利润表 Tab Props */
export interface IncomeStatementTabProps {
incomeStatement: IncomeStatementData[];
loading?: boolean;
loadingTab?: string | null;
showMetricChart: (name: string, key: string, data: unknown[], path: string) => void;
calculateYoYChange: (value: number, period: string, data: unknown[], path: string) => { change: number; intensity: number };
getCellBackground: (change: number, intensity: number) => string;
positiveColor: string;
negativeColor: string;
bgColor: string;
hoverBg: string;
}
/** 现金流量表 Tab Props */
export interface CashflowTabProps {
cashflow: CashflowData[];
loading?: boolean;
loadingTab?: string | null;
showMetricChart: (name: string, key: string, data: unknown[], path: string) => void;
calculateYoYChange: (value: number, period: string, data: unknown[], path: string) => { change: number; intensity: number };
getCellBackground: (change: number, intensity: number) => string;
positiveColor: string;
negativeColor: string;
bgColor: string;
hoverBg: string;
}
// ==================== 财务指标 Tab (7个) ====================
/** 盈利能力 Tab */
export const ProfitabilityTab = memo<MetricsTabProps>(({ financialMetrics, loading, showMetricChart }) => {
const category = FINANCIAL_METRICS_CATEGORIES.profitability;
return (
<UnifiedFinancialTable
type="metrics"
data={financialMetrics}
categoryKey="profitability"
categoryTitle={category.title}
metrics={category.metrics}
showMetricChart={showMetricChart}
loading={loading}
/>
);
});
ProfitabilityTab.displayName = 'ProfitabilityTab';
/** 每股指标 Tab */
export const PerShareTab = memo<MetricsTabProps>(({ financialMetrics, loading, showMetricChart }) => {
const category = FINANCIAL_METRICS_CATEGORIES.perShare;
return (
<UnifiedFinancialTable
type="metrics"
data={financialMetrics}
categoryKey="perShare"
categoryTitle={category.title}
metrics={category.metrics}
showMetricChart={showMetricChart}
loading={loading}
/>
);
});
PerShareTab.displayName = 'PerShareTab';
/** 成长能力 Tab */
export const GrowthTab = memo<MetricsTabProps>(({ financialMetrics, loading, showMetricChart }) => {
const category = FINANCIAL_METRICS_CATEGORIES.growth;
return (
<UnifiedFinancialTable
type="metrics"
data={financialMetrics}
categoryKey="growth"
categoryTitle={category.title}
metrics={category.metrics}
showMetricChart={showMetricChart}
loading={loading}
isGrowthCategory
/>
);
});
GrowthTab.displayName = 'GrowthTab';
/** 运营效率 Tab */
export const OperationalTab = memo<MetricsTabProps>(({ financialMetrics, loading, showMetricChart }) => {
const category = FINANCIAL_METRICS_CATEGORIES.operational;
return (
<UnifiedFinancialTable
type="metrics"
data={financialMetrics}
categoryKey="operational"
categoryTitle={category.title}
metrics={category.metrics}
showMetricChart={showMetricChart}
loading={loading}
/>
);
});
OperationalTab.displayName = 'OperationalTab';
/** 偿债能力 Tab */
export const SolvencyTab = memo<MetricsTabProps>(({ financialMetrics, loading, showMetricChart }) => {
const category = FINANCIAL_METRICS_CATEGORIES.solvency;
return (
<UnifiedFinancialTable
type="metrics"
data={financialMetrics}
categoryKey="solvency"
categoryTitle={category.title}
metrics={category.metrics}
showMetricChart={showMetricChart}
loading={loading}
/>
);
});
SolvencyTab.displayName = 'SolvencyTab';
/** 费用率 Tab */
export const ExpenseTab = memo<MetricsTabProps>(({ financialMetrics, loading, showMetricChart }) => {
const category = FINANCIAL_METRICS_CATEGORIES.expense;
return (
<UnifiedFinancialTable
type="metrics"
data={financialMetrics}
categoryKey="expense"
categoryTitle={category.title}
metrics={category.metrics}
showMetricChart={showMetricChart}
loading={loading}
/>
);
});
ExpenseTab.displayName = 'ExpenseTab';
/** 现金流指标 Tab */
export const CashflowMetricsTab = memo<MetricsTabProps>(({ financialMetrics, loading, showMetricChart }) => {
const category = FINANCIAL_METRICS_CATEGORIES.cashflow;
return (
<UnifiedFinancialTable
type="metrics"
data={financialMetrics}
categoryKey="cashflow"
categoryTitle={category.title}
metrics={category.metrics}
showMetricChart={showMetricChart}
loading={loading}
/>
);
});
CashflowMetricsTab.displayName = 'CashflowMetricsTab';
// ==================== 财务报表 Tab (3个) ====================
// 资产负债表分组配置
const BALANCE_SHEET_SECTIONS = [
CURRENT_ASSETS_METRICS,
NON_CURRENT_ASSETS_METRICS,
TOTAL_ASSETS_METRICS,
CURRENT_LIABILITIES_METRICS,
NON_CURRENT_LIABILITIES_METRICS,
TOTAL_LIABILITIES_METRICS,
EQUITY_METRICS,
];
/** 资产负债表 Tab */
export const BalanceSheetTab = memo<BalanceSheetTabProps>(({ balanceSheet, loading, showMetricChart }) => (
<UnifiedFinancialTable
type="statement"
data={balanceSheet}
sections={BALANCE_SHEET_SECTIONS}
showMetricChart={showMetricChart}
loading={loading}
/>
));
BalanceSheetTab.displayName = 'BalanceSheetTab';
/** 利润表 Tab */
export const IncomeStatementTab = memo<IncomeStatementTabProps>(({ incomeStatement, loading, showMetricChart }) => (
<UnifiedFinancialTable
type="statement"
data={incomeStatement}
sections={INCOME_STATEMENT_SECTIONS}
hideTotalSectionTitle={false}
showMetricChart={showMetricChart}
loading={loading}
/>
));
IncomeStatementTab.displayName = 'IncomeStatementTab';
// 现金流量表配置(转换为 sections 格式)
const CASHFLOW_SECTIONS = [{
title: '现金流量',
key: 'cashflow',
metrics: CASHFLOW_METRICS.map(m => ({
...m,
isCore: ['operating_net', 'free_cash_flow'].includes(m.key),
})),
}];
/** 现金流量表 Tab */
export const CashflowTab = memo<CashflowTabProps>(({ cashflow, loading, showMetricChart }) => (
<UnifiedFinancialTable
type="statement"
data={cashflow}
sections={CASHFLOW_SECTIONS}
hideTotalSectionTitle
showMetricChart={showMetricChart}
loading={loading}
/>
));
CashflowTab.displayName = 'CashflowTab';