From 6eec7c6402409873028cec66c20a0a328e8802a7 Mon Sep 17 00:00:00 2001 From: zdl <3489966805@qq.com> Date: Fri, 19 Dec 2025 15:18:00 +0800 Subject: [PATCH] =?UTF-8?q?feat(ForecastReport):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E7=9B=88=E5=88=A9=E9=A2=84=E6=B5=8B=E9=AA=A8=E6=9E=B6=E5=B1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 创建 ForecastSkeleton 组件(图表卡片 + 表格) - 初始加载时显示骨架屏 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../components/UnifiedFinancialTable.tsx | 12 +- .../FinancialPanorama/tabs/UnifiedTabs.tsx | 264 ------------------ .../components/ForecastSkeleton.tsx | 79 ++++++ .../ForecastReport/components/index.ts | 1 + .../components/ForecastReport/index.tsx | 6 +- 5 files changed, 90 insertions(+), 272 deletions(-) delete mode 100644 src/views/Company/components/FinancialPanorama/tabs/UnifiedTabs.tsx create mode 100644 src/views/Company/components/ForecastReport/components/ForecastSkeleton.tsx diff --git a/src/views/Company/components/FinancialPanorama/components/UnifiedFinancialTable.tsx b/src/views/Company/components/FinancialPanorama/components/UnifiedFinancialTable.tsx index ed6a1fcc..6aebd60a 100644 --- a/src/views/Company/components/FinancialPanorama/components/UnifiedFinancialTable.tsx +++ b/src/views/Company/components/FinancialPanorama/components/UnifiedFinancialTable.tsx @@ -19,11 +19,14 @@ import type { MetricConfig, MetricSectionConfig } from '../types'; export type TableType = 'metrics' | 'statement'; +// 数据类型:必须有 period 字段 +export type FinancialDataItem = { period: string; [key: string]: unknown }; + export interface UnifiedFinancialTableProps { /** 表格类型: metrics=指标表格, statement=报表表格 */ type: TableType; /** 数据数组 */ - data: Array<{ period: string; [key: string]: unknown }>; + data: FinancialDataItem[]; /** metrics 类型: 指标分类 key */ categoryKey?: string; /** metrics 类型: 分类标题 */ @@ -35,7 +38,7 @@ export interface UnifiedFinancialTableProps { /** 是否隐藏汇总行的分组标题 */ hideTotalSectionTitle?: boolean; /** 点击行显示图表回调 */ - showMetricChart: (name: string, key: string, data: unknown[], path: string) => void; + showMetricChart: (name: string, key: string, data: FinancialDataItem[], path: string) => void; /** 是否为成长类指标(正负值着色) */ isGrowthCategory?: boolean; /** 核心指标 keys(用于 cashflow 表格) */ @@ -78,7 +81,6 @@ const extendedTableStyles = getTableStyles(TABLE_CLASS_NAME) + ` const UnifiedFinancialTableInner: React.FC = ({ type, data, - categoryKey, categoryTitle, metrics, sections, @@ -107,7 +109,7 @@ const UnifiedFinancialTableInner: React.FC = ({ } // 限制显示列数 - const maxColumns = type === 'metrics' ? 6 : (type === 'statement' ? 8 : 6); + const maxColumns = type === 'metrics' ? 6 : 8; const displayData = data.slice(0, Math.min(data.length, maxColumns)); // 构建表格数据 @@ -168,7 +170,7 @@ const UnifiedFinancialTableInner: React.FC = ({ } return rows; - }, [type, metrics, sections, displayData, data, hideTotalSectionTitle, coreMetricKeys]); + }, [type, metrics, sections, displayData, hideTotalSectionTitle, coreMetricKeys]); // 计算同比变化 const calcYoY = ( diff --git a/src/views/Company/components/FinancialPanorama/tabs/UnifiedTabs.tsx b/src/views/Company/components/FinancialPanorama/tabs/UnifiedTabs.tsx deleted file mode 100644 index 6a046f02..00000000 --- a/src/views/Company/components/FinancialPanorama/tabs/UnifiedTabs.tsx +++ /dev/null @@ -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(({ financialMetrics, loading, showMetricChart }) => { - const category = FINANCIAL_METRICS_CATEGORIES.profitability; - return ( - - ); -}); -ProfitabilityTab.displayName = 'ProfitabilityTab'; - -/** 每股指标 Tab */ -export const PerShareTab = memo(({ financialMetrics, loading, showMetricChart }) => { - const category = FINANCIAL_METRICS_CATEGORIES.perShare; - return ( - - ); -}); -PerShareTab.displayName = 'PerShareTab'; - -/** 成长能力 Tab */ -export const GrowthTab = memo(({ financialMetrics, loading, showMetricChart }) => { - const category = FINANCIAL_METRICS_CATEGORIES.growth; - return ( - - ); -}); -GrowthTab.displayName = 'GrowthTab'; - -/** 运营效率 Tab */ -export const OperationalTab = memo(({ financialMetrics, loading, showMetricChart }) => { - const category = FINANCIAL_METRICS_CATEGORIES.operational; - return ( - - ); -}); -OperationalTab.displayName = 'OperationalTab'; - -/** 偿债能力 Tab */ -export const SolvencyTab = memo(({ financialMetrics, loading, showMetricChart }) => { - const category = FINANCIAL_METRICS_CATEGORIES.solvency; - return ( - - ); -}); -SolvencyTab.displayName = 'SolvencyTab'; - -/** 费用率 Tab */ -export const ExpenseTab = memo(({ financialMetrics, loading, showMetricChart }) => { - const category = FINANCIAL_METRICS_CATEGORIES.expense; - return ( - - ); -}); -ExpenseTab.displayName = 'ExpenseTab'; - -/** 现金流指标 Tab */ -export const CashflowMetricsTab = memo(({ financialMetrics, loading, showMetricChart }) => { - const category = FINANCIAL_METRICS_CATEGORIES.cashflow; - return ( - - ); -}); -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(({ balanceSheet, loading, showMetricChart }) => ( - -)); -BalanceSheetTab.displayName = 'BalanceSheetTab'; - -/** 利润表 Tab */ -export const IncomeStatementTab = memo(({ incomeStatement, loading, showMetricChart }) => ( - -)); -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(({ cashflow, loading, showMetricChart }) => ( - -)); -CashflowTab.displayName = 'CashflowTab'; diff --git a/src/views/Company/components/ForecastReport/components/ForecastSkeleton.tsx b/src/views/Company/components/ForecastReport/components/ForecastSkeleton.tsx new file mode 100644 index 00000000..1c5f0dfe --- /dev/null +++ b/src/views/Company/components/ForecastReport/components/ForecastSkeleton.tsx @@ -0,0 +1,79 @@ +/** + * 盈利预测骨架屏组件 + */ + +import React, { memo } from 'react'; +import { + Box, + SimpleGrid, + Skeleton, + SkeletonText, + Card, + CardBody, + VStack, +} from '@chakra-ui/react'; + +// 黑金主题配色 +const SKELETON_COLORS = { + startColor: 'rgba(26, 32, 44, 0.6)', + endColor: 'rgba(212, 175, 55, 0.2)', +}; + +/** + * 图表卡片骨架屏 + */ +const ChartCardSkeleton: React.FC = memo(() => ( + + + + + + +)); + +ChartCardSkeleton.displayName = 'ChartCardSkeleton'; + +/** + * 表格骨架屏 + */ +const TableSkeleton: React.FC = memo(() => ( + + + + + {/* 表头 */} + + {/* 表格行 */} + {[1, 2, 3, 4, 5].map((i) => ( + + ))} + + + +)); + +TableSkeleton.displayName = 'TableSkeleton'; + +/** + * 盈利预测完整骨架屏 + */ +const ForecastSkeleton: React.FC = memo(() => ( + + {/* 图表区域 - 3列布局 */} + + + + + + + {/* 详细数据表格 */} + + + + +)); + +ForecastSkeleton.displayName = 'ForecastSkeleton'; + +export { ForecastSkeleton }; +export default ForecastSkeleton; diff --git a/src/views/Company/components/ForecastReport/components/index.ts b/src/views/Company/components/ForecastReport/components/index.ts index e31ad54b..2aa7f067 100644 --- a/src/views/Company/components/ForecastReport/components/index.ts +++ b/src/views/Company/components/ForecastReport/components/index.ts @@ -9,3 +9,4 @@ export { default as IncomeProfitGrowthChart } from './IncomeProfitGrowthChart'; export { default as EpsChart } from './EpsChart'; export { default as PePegChart } from './PePegChart'; export { default as DetailTable } from './DetailTable'; +export { ForecastSkeleton } from './ForecastSkeleton'; diff --git a/src/views/Company/components/ForecastReport/index.tsx b/src/views/Company/components/ForecastReport/index.tsx index 7d7db597..a8edc19a 100644 --- a/src/views/Company/components/ForecastReport/index.tsx +++ b/src/views/Company/components/ForecastReport/index.tsx @@ -11,16 +11,16 @@ import { EpsChart, PePegChart, DetailTable, + ForecastSkeleton, } from './components'; -import LoadingState from '../LoadingState'; import type { ForecastReportProps } from './types'; const ForecastReport: React.FC = ({ stockCode }) => { const { data, isLoading, error, refetch } = useForecastData(stockCode); - // 加载状态 + // 加载状态 - 显示骨架屏 if (isLoading && !data) { - return ; + return ; } // 错误状态