fix(FinancialPanorama): 优化 loading 状态,Tabs 立即显示

- 移除 SubTabContainer 的 loading 条件渲染,Tabs 始终可见
- 各 Tab 组件内部处理 loading 状态,显示 Spinner
- 传递 loading 和 loadingTab 到 componentProps
- 修改 BalanceSheetTab、IncomeStatementTab、CashflowTab、
  FinancialMetricsTab、MetricsCategoryTab 支持 loading 属性

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
zdl
2025-12-18 18:15:30 +08:00
parent ec2270ca8e
commit 997724e0b1
6 changed files with 84 additions and 25 deletions

View File

@@ -250,6 +250,9 @@ const FinancialPanorama: React.FC<FinancialPanoramaProps> = ({ stockCode: propSt
incomeStatement, incomeStatement,
cashflow, cashflow,
financialMetrics, financialMetrics,
// 加载状态
loading,
loadingTab,
// 工具函数 // 工具函数
showMetricChart, showMetricChart,
calculateYoYChange, calculateYoYChange,
@@ -265,6 +268,8 @@ const FinancialPanorama: React.FC<FinancialPanoramaProps> = ({ stockCode: propSt
incomeStatement, incomeStatement,
cashflow, cashflow,
financialMetrics, financialMetrics,
loading,
loadingTab,
showMetricChart, showMetricChart,
positiveColor, positiveColor,
negativeColor, negativeColor,
@@ -302,27 +307,25 @@ const FinancialPanorama: React.FC<FinancialPanoramaProps> = ({ stockCode: propSt
)} )}
{/* 三大财务报表 - 使用 SubTabContainer 二级导航 */} {/* 三大财务报表 - 使用 SubTabContainer 二级导航 */}
{!loading && stockInfo && ( <Card bg="gray.900" shadow="md" border="1px solid" borderColor="rgba(212, 175, 55, 0.3)">
<Card bg="gray.900" shadow="md" border="1px solid" borderColor="rgba(212, 175, 55, 0.3)"> <CardBody p={0}>
<CardBody p={0}> <SubTabContainer
<SubTabContainer tabs={tabConfigs}
tabs={tabConfigs} componentProps={componentProps}
componentProps={componentProps} themePreset="blackGold"
themePreset="blackGold" isLazy
isLazy onTabChange={handleTabChange}
onTabChange={handleTabChange} rightElement={
rightElement={ <PeriodSelector
<PeriodSelector selectedPeriods={selectedPeriods}
selectedPeriods={selectedPeriods} onPeriodsChange={setSelectedPeriods}
onPeriodsChange={setSelectedPeriods} onRefresh={handleRefresh}
onRefresh={handleRefresh} isLoading={loadingTab !== null || loading}
isLoading={loadingTab !== null} />
/> }
} />
/> </CardBody>
</CardBody> </Card>
</Card>
)}
{/* 错误提示 */} {/* 错误提示 */}
{error && ( {error && (

View File

@@ -3,12 +3,13 @@
*/ */
import React from 'react'; import React from 'react';
import { Box, VStack, HStack, Heading, Badge, Text } from '@chakra-ui/react'; import { Box, VStack, HStack, Heading, Badge, Text, Spinner, Center } from '@chakra-ui/react';
import { BalanceSheetTable } from '../components'; import { BalanceSheetTable } from '../components';
import type { BalanceSheetData } from '../types'; import type { BalanceSheetData } from '../types';
export interface BalanceSheetTabProps { export interface BalanceSheetTabProps {
balanceSheet: BalanceSheetData[]; balanceSheet: BalanceSheetData[];
loading?: boolean;
showMetricChart: (name: string, key: string, data: unknown[], path: string) => void; showMetricChart: (name: string, key: string, data: unknown[], path: string) => void;
calculateYoYChange: (value: number, period: string, data: unknown[], path: string) => { change: number; intensity: number }; calculateYoYChange: (value: number, period: string, data: unknown[], path: string) => { change: number; intensity: number };
getCellBackground: (change: number, intensity: number) => string; getCellBackground: (change: number, intensity: number) => string;
@@ -20,6 +21,7 @@ export interface BalanceSheetTabProps {
const BalanceSheetTab: React.FC<BalanceSheetTabProps> = ({ const BalanceSheetTab: React.FC<BalanceSheetTabProps> = ({
balanceSheet, balanceSheet,
loading,
showMetricChart, showMetricChart,
calculateYoYChange, calculateYoYChange,
getCellBackground, getCellBackground,
@@ -28,6 +30,15 @@ const BalanceSheetTab: React.FC<BalanceSheetTabProps> = ({
bgColor, bgColor,
hoverBg, hoverBg,
}) => { }) => {
// 加载中状态
if (loading && (!Array.isArray(balanceSheet) || balanceSheet.length === 0)) {
return (
<Center py={12}>
<Spinner size="lg" color="#D4AF37" thickness="3px" />
</Center>
);
}
const tableProps = { const tableProps = {
showMetricChart, showMetricChart,
calculateYoYChange, calculateYoYChange,

View File

@@ -3,12 +3,13 @@
*/ */
import React from 'react'; import React from 'react';
import { Box, VStack, HStack, Heading, Badge, Text } from '@chakra-ui/react'; import { Box, VStack, HStack, Heading, Badge, Text, Spinner, Center } from '@chakra-ui/react';
import { CashflowTable } from '../components'; import { CashflowTable } from '../components';
import type { CashflowData } from '../types'; import type { CashflowData } from '../types';
export interface CashflowTabProps { export interface CashflowTabProps {
cashflow: CashflowData[]; cashflow: CashflowData[];
loading?: boolean;
showMetricChart: (name: string, key: string, data: unknown[], path: string) => void; showMetricChart: (name: string, key: string, data: unknown[], path: string) => void;
calculateYoYChange: (value: number, period: string, data: unknown[], path: string) => { change: number; intensity: number }; calculateYoYChange: (value: number, period: string, data: unknown[], path: string) => { change: number; intensity: number };
getCellBackground: (change: number, intensity: number) => string; getCellBackground: (change: number, intensity: number) => string;
@@ -20,6 +21,7 @@ export interface CashflowTabProps {
const CashflowTab: React.FC<CashflowTabProps> = ({ const CashflowTab: React.FC<CashflowTabProps> = ({
cashflow, cashflow,
loading,
showMetricChart, showMetricChart,
calculateYoYChange, calculateYoYChange,
getCellBackground, getCellBackground,
@@ -28,6 +30,15 @@ const CashflowTab: React.FC<CashflowTabProps> = ({
bgColor, bgColor,
hoverBg, hoverBg,
}) => { }) => {
// 加载中状态
if (loading && (!Array.isArray(cashflow) || cashflow.length === 0)) {
return (
<Center py={12}>
<Spinner size="lg" color="#D4AF37" thickness="3px" />
</Center>
);
}
const tableProps = { const tableProps = {
showMetricChart, showMetricChart,
calculateYoYChange, calculateYoYChange,

View File

@@ -3,11 +3,13 @@
*/ */
import React from 'react'; import React from 'react';
import { Spinner, Center } from '@chakra-ui/react';
import { FinancialMetricsTable } from '../components'; import { FinancialMetricsTable } from '../components';
import type { FinancialMetricsData } from '../types'; import type { FinancialMetricsData } from '../types';
export interface FinancialMetricsTabProps { export interface FinancialMetricsTabProps {
financialMetrics: FinancialMetricsData[]; financialMetrics: FinancialMetricsData[];
loading?: boolean;
showMetricChart: (name: string, key: string, data: unknown[], path: string) => void; showMetricChart: (name: string, key: string, data: unknown[], path: string) => void;
calculateYoYChange: (value: number, period: string, data: unknown[], path: string) => { change: number; intensity: number }; calculateYoYChange: (value: number, period: string, data: unknown[], path: string) => { change: number; intensity: number };
getCellBackground: (change: number, intensity: number) => string; getCellBackground: (change: number, intensity: number) => string;
@@ -19,6 +21,7 @@ export interface FinancialMetricsTabProps {
const FinancialMetricsTab: React.FC<FinancialMetricsTabProps> = ({ const FinancialMetricsTab: React.FC<FinancialMetricsTabProps> = ({
financialMetrics, financialMetrics,
loading,
showMetricChart, showMetricChart,
calculateYoYChange, calculateYoYChange,
getCellBackground, getCellBackground,
@@ -27,6 +30,15 @@ const FinancialMetricsTab: React.FC<FinancialMetricsTabProps> = ({
bgColor, bgColor,
hoverBg, hoverBg,
}) => { }) => {
// 加载中状态
if (loading && (!Array.isArray(financialMetrics) || financialMetrics.length === 0)) {
return (
<Center py={12}>
<Spinner size="lg" color="#D4AF37" thickness="3px" />
</Center>
);
}
const tableProps = { const tableProps = {
showMetricChart, showMetricChart,
calculateYoYChange, calculateYoYChange,

View File

@@ -3,12 +3,13 @@
*/ */
import React from 'react'; import React from 'react';
import { Box, VStack, HStack, Heading, Badge, Text } from '@chakra-ui/react'; import { Box, VStack, HStack, Heading, Badge, Text, Spinner, Center } from '@chakra-ui/react';
import { IncomeStatementTable } from '../components'; import { IncomeStatementTable } from '../components';
import type { IncomeStatementData } from '../types'; import type { IncomeStatementData } from '../types';
export interface IncomeStatementTabProps { export interface IncomeStatementTabProps {
incomeStatement: IncomeStatementData[]; incomeStatement: IncomeStatementData[];
loading?: boolean;
showMetricChart: (name: string, key: string, data: unknown[], path: string) => void; showMetricChart: (name: string, key: string, data: unknown[], path: string) => void;
calculateYoYChange: (value: number, period: string, data: unknown[], path: string) => { change: number; intensity: number }; calculateYoYChange: (value: number, period: string, data: unknown[], path: string) => { change: number; intensity: number };
getCellBackground: (change: number, intensity: number) => string; getCellBackground: (change: number, intensity: number) => string;
@@ -20,6 +21,7 @@ export interface IncomeStatementTabProps {
const IncomeStatementTab: React.FC<IncomeStatementTabProps> = ({ const IncomeStatementTab: React.FC<IncomeStatementTabProps> = ({
incomeStatement, incomeStatement,
loading,
showMetricChart, showMetricChart,
calculateYoYChange, calculateYoYChange,
getCellBackground, getCellBackground,
@@ -28,6 +30,15 @@ const IncomeStatementTab: React.FC<IncomeStatementTabProps> = ({
bgColor, bgColor,
hoverBg, hoverBg,
}) => { }) => {
// 加载中状态
if (loading && (!Array.isArray(incomeStatement) || incomeStatement.length === 0)) {
return (
<Center py={12}>
<Spinner size="lg" color="#D4AF37" thickness="3px" />
</Center>
);
}
const tableProps = { const tableProps = {
showMetricChart, showMetricChart,
calculateYoYChange, calculateYoYChange,

View File

@@ -4,7 +4,7 @@
*/ */
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { Box, Text, HStack, Badge as ChakraBadge } from '@chakra-ui/react'; import { Box, Text, HStack, Badge as ChakraBadge, Spinner, Center } from '@chakra-ui/react';
import { Table, ConfigProvider, Tooltip } from 'antd'; import { Table, ConfigProvider, Tooltip } from 'antd';
import type { ColumnsType } from 'antd/es/table'; import type { ColumnsType } from 'antd/es/table';
import { Eye } from 'lucide-react'; import { Eye } from 'lucide-react';
@@ -86,6 +86,7 @@ const tableStyles = `
export interface MetricsCategoryTabProps { export interface MetricsCategoryTabProps {
categoryKey: CategoryKey; categoryKey: CategoryKey;
financialMetrics: FinancialMetricsData[]; financialMetrics: FinancialMetricsData[];
loading?: boolean;
showMetricChart: (name: string, key: string, data: unknown[], path: string) => void; showMetricChart: (name: string, key: string, data: unknown[], path: string) => void;
calculateYoYChange: (value: number, period: string, data: unknown[], path: string) => { change: number; intensity: number }; calculateYoYChange: (value: number, period: string, data: unknown[], path: string) => { change: number; intensity: number };
getCellBackground: (change: number, intensity: number) => string; getCellBackground: (change: number, intensity: number) => string;
@@ -107,9 +108,19 @@ interface TableRowData {
const MetricsCategoryTab: React.FC<MetricsCategoryTabProps> = ({ const MetricsCategoryTab: React.FC<MetricsCategoryTabProps> = ({
categoryKey, categoryKey,
financialMetrics, financialMetrics,
loading,
showMetricChart, showMetricChart,
calculateYoYChange, calculateYoYChange,
}) => { }) => {
// 加载中状态
if (loading && (!Array.isArray(financialMetrics) || financialMetrics.length === 0)) {
return (
<Center py={12}>
<Spinner size="lg" color="#D4AF37" thickness="3px" />
</Center>
);
}
// 数组安全检查 // 数组安全检查
if (!Array.isArray(financialMetrics) || financialMetrics.length === 0) { if (!Array.isArray(financialMetrics) || financialMetrics.length === 0) {
return ( return (