diff --git a/src/views/Company/components/FinancialPanorama/tabs/BalanceSheetTab.tsx b/src/views/Company/components/FinancialPanorama/tabs/BalanceSheetTab.tsx index a35e4022..13edf666 100644 --- a/src/views/Company/components/FinancialPanorama/tabs/BalanceSheetTab.tsx +++ b/src/views/Company/components/FinancialPanorama/tabs/BalanceSheetTab.tsx @@ -2,7 +2,7 @@ * 资产负债表 Tab */ -import React from 'react'; +import React, { memo } from 'react'; import { Box, VStack, HStack, Heading, Badge, Text, Spinner, Center } from '@chakra-ui/react'; import { BalanceSheetTable } from '../components'; import type { BalanceSheetData } from '../types'; @@ -19,7 +19,7 @@ export interface BalanceSheetTabProps { hoverBg: string; } -const BalanceSheetTab: React.FC = ({ +const BalanceSheetTabInner: React.FC = ({ balanceSheet, loading, showMetricChart, @@ -72,4 +72,5 @@ const BalanceSheetTab: React.FC = ({ ); }; +const BalanceSheetTab = memo(BalanceSheetTabInner); export default BalanceSheetTab; diff --git a/src/views/Company/components/FinancialPanorama/tabs/CashflowTab.tsx b/src/views/Company/components/FinancialPanorama/tabs/CashflowTab.tsx index 5d0cda91..ae10a888 100644 --- a/src/views/Company/components/FinancialPanorama/tabs/CashflowTab.tsx +++ b/src/views/Company/components/FinancialPanorama/tabs/CashflowTab.tsx @@ -2,7 +2,7 @@ * 现金流量表 Tab */ -import React from 'react'; +import React, { memo } from 'react'; import { Box, VStack, HStack, Heading, Badge, Text, Spinner, Center } from '@chakra-ui/react'; import { CashflowTable } from '../components'; import type { CashflowData } from '../types'; @@ -19,7 +19,7 @@ export interface CashflowTabProps { hoverBg: string; } -const CashflowTab: React.FC = ({ +const CashflowTabInner: React.FC = ({ cashflow, loading, showMetricChart, @@ -72,4 +72,5 @@ const CashflowTab: React.FC = ({ ); }; +const CashflowTab = memo(CashflowTabInner); export default CashflowTab; diff --git a/src/views/Company/components/FinancialPanorama/tabs/FinancialMetricsTab.tsx b/src/views/Company/components/FinancialPanorama/tabs/FinancialMetricsTab.tsx index e3ae7c17..bfb5e393 100644 --- a/src/views/Company/components/FinancialPanorama/tabs/FinancialMetricsTab.tsx +++ b/src/views/Company/components/FinancialPanorama/tabs/FinancialMetricsTab.tsx @@ -2,7 +2,7 @@ * 财务指标 Tab */ -import React from 'react'; +import React, { memo } from 'react'; import { Spinner, Center } from '@chakra-ui/react'; import { FinancialMetricsTable } from '../components'; import type { FinancialMetricsData } from '../types'; @@ -19,7 +19,7 @@ export interface FinancialMetricsTabProps { hoverBg: string; } -const FinancialMetricsTab: React.FC = ({ +const FinancialMetricsTabInner: React.FC = ({ financialMetrics, loading, showMetricChart, @@ -54,4 +54,5 @@ const FinancialMetricsTab: React.FC = ({ ); }; +const FinancialMetricsTab = memo(FinancialMetricsTabInner); export default FinancialMetricsTab; diff --git a/src/views/Company/components/FinancialPanorama/tabs/IncomeStatementTab.tsx b/src/views/Company/components/FinancialPanorama/tabs/IncomeStatementTab.tsx index b1af31b9..ce52e775 100644 --- a/src/views/Company/components/FinancialPanorama/tabs/IncomeStatementTab.tsx +++ b/src/views/Company/components/FinancialPanorama/tabs/IncomeStatementTab.tsx @@ -2,7 +2,7 @@ * 利润表 Tab */ -import React from 'react'; +import React, { memo } from 'react'; import { Box, VStack, HStack, Heading, Badge, Text, Spinner, Center } from '@chakra-ui/react'; import { IncomeStatementTable } from '../components'; import type { IncomeStatementData } from '../types'; @@ -19,7 +19,7 @@ export interface IncomeStatementTabProps { hoverBg: string; } -const IncomeStatementTab: React.FC = ({ +const IncomeStatementTabInner: React.FC = ({ incomeStatement, loading, showMetricChart, @@ -72,4 +72,5 @@ const IncomeStatementTab: React.FC = ({ ); }; +const IncomeStatementTab = memo(IncomeStatementTabInner); export default IncomeStatementTab; diff --git a/src/views/Company/components/FinancialPanorama/tabs/MetricsCategoryTab.tsx b/src/views/Company/components/FinancialPanorama/tabs/MetricsCategoryTab.tsx index 8b4d38d2..76754789 100644 --- a/src/views/Company/components/FinancialPanorama/tabs/MetricsCategoryTab.tsx +++ b/src/views/Company/components/FinancialPanorama/tabs/MetricsCategoryTab.tsx @@ -3,84 +3,26 @@ * 接受 categoryKey 显示单个分类的指标表格 */ -import React, { useMemo } from 'react'; +import React, { useMemo, memo } from 'react'; import { Box, Text, HStack, Badge as ChakraBadge, Spinner, Center } from '@chakra-ui/react'; import { Table, ConfigProvider, Tooltip } from 'antd'; import type { ColumnsType } from 'antd/es/table'; import { Eye } from 'lucide-react'; import { formatUtils } from '@services/financialService'; import { FINANCIAL_METRICS_CATEGORIES } from '../constants'; -import { getValueByPath, isNegativeIndicator } from '../utils'; +import { getValueByPath, isNegativeIndicator, BLACK_GOLD_TABLE_THEME, getTableStyles, calculateYoY } from '../utils'; import type { FinancialMetricsData } from '../types'; type CategoryKey = keyof typeof FINANCIAL_METRICS_CATEGORIES; -// Ant Design 黑金主题配置 -const BLACK_GOLD_THEME = { - token: { - colorBgContainer: 'transparent', - colorText: '#E2E8F0', - colorTextHeading: '#D4AF37', - colorBorderSecondary: 'rgba(212, 175, 55, 0.2)', - }, - components: { - Table: { - headerBg: 'rgba(26, 32, 44, 0.8)', - headerColor: '#D4AF37', - rowHoverBg: 'rgba(212, 175, 55, 0.1)', - borderColor: 'rgba(212, 175, 55, 0.15)', - cellPaddingBlock: 8, - cellPaddingInline: 12, - }, - }, -}; - -// 黑金主题CSS -const tableStyles = ` - .metrics-category-table .ant-table { - background: transparent !important; - } - .metrics-category-table .ant-table-thead > tr > th { - background: rgba(26, 32, 44, 0.8) !important; - color: #D4AF37 !important; - border-bottom: 1px solid rgba(212, 175, 55, 0.3) !important; - font-weight: 600; - font-size: 13px; - } - .metrics-category-table .ant-table-tbody > tr > td { - border-bottom: 1px solid rgba(212, 175, 55, 0.1) !important; - color: #E2E8F0; - font-size: 12px; - } - .metrics-category-table .ant-table-tbody > tr:hover > td { - background: rgba(212, 175, 55, 0.08) !important; - } - .metrics-category-table .ant-table-cell-fix-left, - .metrics-category-table .ant-table-cell-fix-right { - background: #1A202C !important; - } - .metrics-category-table .ant-table-tbody > tr:hover .ant-table-cell-fix-left, - .metrics-category-table .ant-table-tbody > tr:hover .ant-table-cell-fix-right { - background: rgba(26, 32, 44, 0.95) !important; - } - .metrics-category-table .positive-change { +const TABLE_CLASS_NAME = 'metrics-category-table'; +const tableStyles = getTableStyles(TABLE_CLASS_NAME) + ` + .${TABLE_CLASS_NAME} .positive-value { color: #E53E3E; } - .metrics-category-table .negative-change { + .${TABLE_CLASS_NAME} .negative-value { color: #48BB78; } - .metrics-category-table .positive-value { - color: #E53E3E; - } - .metrics-category-table .negative-value { - color: #48BB78; - } - .metrics-category-table .ant-table-placeholder { - background: transparent !important; - } - .metrics-category-table .ant-empty-description { - color: #A0AEC0; - } `; export interface MetricsCategoryTabProps { @@ -105,7 +47,7 @@ interface TableRowData { [period: string]: unknown; } -const MetricsCategoryTab: React.FC = ({ +const MetricsCategoryTabInner: React.FC = ({ categoryKey, financialMetrics, loading, @@ -162,29 +104,13 @@ const MetricsCategoryTab: React.FC = ({ }); }, [financialMetrics, displayData, category]); - // 计算同比变化 - const calculateYoY = ( + // 计算同比变化(使用共享函数) + const calcYoY = ( currentValue: number | undefined, currentPeriod: string, path: string ): number | null => { - if (currentValue === undefined || currentValue === null) return null; - - const currentDate = new Date(currentPeriod); - const lastYearPeriod = financialMetrics.find((item) => { - const date = new Date(item.period); - return ( - date.getFullYear() === currentDate.getFullYear() - 1 && - date.getMonth() === currentDate.getMonth() - ); - }); - - if (!lastYearPeriod) return null; - - const lastYearValue = getValueByPath(lastYearPeriod, path); - if (lastYearValue === undefined || lastYearValue === 0) return null; - - return ((currentValue - lastYearValue) / Math.abs(lastYearValue)) * 100; + return calculateYoY(financialMetrics, currentValue, currentPeriod, path, getValueByPath); }; // 构建列定义 @@ -219,7 +145,7 @@ const MetricsCategoryTab: React.FC = ({ width: 100, align: 'right' as const, render: (value: number | undefined, record: TableRowData) => { - const yoy = calculateYoY(value, item.period, record.path); + const yoy = calcYoY(value, item.period, record.path); const isNegative = isNegativeIndicator(record.key); // 对于负向指标,增加是坏事(绿色),减少是好事(红色) @@ -287,7 +213,7 @@ const MetricsCategoryTab: React.FC = ({ - + = ({ ); }; -// 为每个分类创建预配置的组件 -export const ProfitabilityTab: React.FC> = (props) => ( +const MetricsCategoryTab = memo(MetricsCategoryTabInner); + +// 为每个分类创建预配置的组件(使用 memo) +export const ProfitabilityTab = memo>((props) => ( -); +)); -export const PerShareTab: React.FC> = (props) => ( +export const PerShareTab = memo>((props) => ( -); +)); -export const GrowthTab: React.FC> = (props) => ( +export const GrowthTab = memo>((props) => ( -); +)); -export const OperationalTab: React.FC> = (props) => ( +export const OperationalTab = memo>((props) => ( -); +)); -export const SolvencyTab: React.FC> = (props) => ( +export const SolvencyTab = memo>((props) => ( -); +)); -export const ExpenseTab: React.FC> = (props) => ( +export const ExpenseTab = memo>((props) => ( -); +)); -export const CashflowMetricsTab: React.FC> = (props) => ( +export const CashflowMetricsTab = memo>((props) => ( -); +)); export default MetricsCategoryTab;