diff --git a/src/views/Company/components/FinancialPanorama/components/FinancialOverviewPanel.tsx b/src/views/Company/components/FinancialPanorama/components/FinancialOverviewPanel.tsx new file mode 100644 index 00000000..27ba24da --- /dev/null +++ b/src/views/Company/components/FinancialPanorama/components/FinancialOverviewPanel.tsx @@ -0,0 +1,188 @@ +/** + * 财务全景面板组件 - 三列布局 + * 复用 MarketDataView 的 MetricCard 组件 + */ + +import React, { memo } from 'react'; +import { SimpleGrid, HStack, VStack, Text, Badge } from '@chakra-ui/react'; +import { TrendingUp, Coins, Shield, TrendingDown, Activity, PieChart } from 'lucide-react'; +import { formatUtils } from '@services/financialService'; + +// 复用 MarketDataView 的组件 +import MetricCard from '../../MarketDataView/components/StockSummaryCard/MetricCard'; +import { StatusTag } from '../../MarketDataView/components/StockSummaryCard/atoms'; +import { darkGoldTheme } from '../../MarketDataView/constants'; + +import type { StockInfo, FinancialMetricsData } from '../types'; + +export interface FinancialOverviewPanelProps { + stockInfo: StockInfo | null; + financialMetrics: FinancialMetricsData[]; +} + +/** + * 获取成长状态 + */ +const getGrowthStatus = (value: number | undefined): { text: string; color: string } => { + if (value === undefined || value === null) return { text: '-', color: darkGoldTheme.textMuted }; + if (value > 30) return { text: '高速增长', color: darkGoldTheme.green }; + if (value > 10) return { text: '稳健增长', color: darkGoldTheme.gold }; + if (value > 0) return { text: '低速增长', color: darkGoldTheme.orange }; + if (value > -10) return { text: '小幅下滑', color: darkGoldTheme.orange }; + return { text: '大幅下滑', color: darkGoldTheme.red }; +}; + +/** + * 获取 ROE 状态 + */ +const getROEStatus = (value: number | undefined): { text: string; color: string } => { + if (value === undefined || value === null) return { text: '-', color: darkGoldTheme.textMuted }; + if (value > 20) return { text: '优秀', color: darkGoldTheme.green }; + if (value > 15) return { text: '良好', color: darkGoldTheme.gold }; + if (value > 10) return { text: '一般', color: darkGoldTheme.orange }; + return { text: '较低', color: darkGoldTheme.red }; +}; + +/** + * 获取资产负债率状态 + */ +const getDebtStatus = (value: number | undefined): { text: string; color: string } => { + if (value === undefined || value === null) return { text: '-', color: darkGoldTheme.textMuted }; + if (value < 40) return { text: '安全', color: darkGoldTheme.green }; + if (value < 60) return { text: '适中', color: darkGoldTheme.gold }; + if (value < 70) return { text: '偏高', color: darkGoldTheme.orange }; + return { text: '风险', color: darkGoldTheme.red }; +}; + +/** + * 财务全景面板组件 + */ +export const FinancialOverviewPanel: React.FC = memo(({ + stockInfo, + financialMetrics, +}) => { + if (!stockInfo && (!financialMetrics || financialMetrics.length === 0)) { + return null; + } + + // 获取最新一期财务指标 + const latestMetrics = financialMetrics?.[0]; + + // 成长指标(来自 stockInfo) + const revenueGrowth = stockInfo?.growth_rates?.revenue_growth; + const profitGrowth = stockInfo?.growth_rates?.profit_growth; + const forecast = stockInfo?.latest_forecast; + + // 盈利指标(来自 financialMetrics) + const roe = latestMetrics?.profitability?.roe; + const netProfitMargin = latestMetrics?.profitability?.net_profit_margin; + const grossMargin = latestMetrics?.profitability?.gross_margin; + + // 风险与运营指标(来自 financialMetrics) + const assetLiabilityRatio = latestMetrics?.solvency?.asset_liability_ratio; + const currentRatio = latestMetrics?.solvency?.current_ratio; + const rdExpenseRatio = latestMetrics?.expense_ratios?.rd_expense_ratio; + + // 计算状态 + const growthStatus = getGrowthStatus(profitGrowth); + const roeStatus = getROEStatus(roe); + const debtStatus = getDebtStatus(assetLiabilityRatio); + + // 格式化涨跌显示 + const formatGrowth = (value: number | undefined) => { + if (value === undefined || value === null) return '-'; + const sign = value >= 0 ? '+' : ''; + return `${sign}${value.toFixed(2)}%`; + }; + + return ( + + {/* 卡片1: 成长能力 */} + } + rightIcon={} + mainLabel="利润增长" + mainValue={formatGrowth(profitGrowth)} + mainColor={profitGrowth !== undefined && profitGrowth >= 0 ? darkGoldTheme.green : darkGoldTheme.red} + subText={ + + + 营收增长 + = 0 ? darkGoldTheme.green : darkGoldTheme.red} + > + {formatGrowth(revenueGrowth)} + + + + {forecast && ( + + {forecast.forecast_type} {forecast.content} + + )} + + } + /> + + {/* 卡片2: 盈利与回报 */} + } + rightIcon={} + mainLabel="ROE" + mainValue={formatUtils.formatPercent(roe)} + mainColor={darkGoldTheme.orange} + subText={ + + + {roeStatus.text} + + + 净利率 {formatUtils.formatPercent(netProfitMargin)} + | + 毛利率 {formatUtils.formatPercent(grossMargin)} + + + } + /> + + {/* 卡片3: 风险与运营 */} + } + rightIcon={} + mainLabel="资产负债率" + mainValue={formatUtils.formatPercent(assetLiabilityRatio)} + mainColor={debtStatus.color} + subText={ + + + {debtStatus.text} + + + 流动比率 {currentRatio?.toFixed(2) ?? '-'} + | + 研发费用率 {formatUtils.formatPercent(rdExpenseRatio)} + + + } + /> + + ); +}); + +FinancialOverviewPanel.displayName = 'FinancialOverviewPanel'; + +export default FinancialOverviewPanel; diff --git a/src/views/Company/components/FinancialPanorama/components/index.ts b/src/views/Company/components/FinancialPanorama/components/index.ts index 334250d5..6bfaf227 100644 --- a/src/views/Company/components/FinancialPanorama/components/index.ts +++ b/src/views/Company/components/FinancialPanorama/components/index.ts @@ -3,6 +3,8 @@ */ export { PeriodSelector } from './PeriodSelector'; +export { FinancialOverviewPanel } from './FinancialOverviewPanel'; +// 保留旧组件导出(向后兼容) export { KeyMetricsOverview } from './KeyMetricsOverview'; export { StockInfoHeader } from './StockInfoHeader'; export { BalanceSheetTable } from './BalanceSheetTable';