refactor(FinancialPanorama): 添加数据加载 Hook

useFinancialData Hook 功能:
- 9个财务API并行加载(Promise.all)
- 股票信息、资产负债表、利润表、现金流量表
- 财务指标、主营业务、业绩预告
- 行业排名、期间对比
- 支持期数选择(4/8/12/16期)
- 自动响应 stockCode 变化重新加载

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
zdl
2025-12-12 15:01:26 +08:00
parent fb42ef566b
commit d9106bf9f7
2 changed files with 192 additions and 0 deletions

View File

@@ -0,0 +1,6 @@
/**
* Hooks 统一导出
*/
export { useFinancialData } from './useFinancialData';
export type { default as UseFinancialDataReturn } from './useFinancialData';

View File

@@ -0,0 +1,186 @@
/**
* 财务数据加载 Hook
* 封装所有财务数据的加载逻辑
*/
import { useState, useEffect, useCallback } from 'react';
import { useToast } from '@chakra-ui/react';
import { logger } from '@utils/logger';
import { financialService } from '@services/financialService';
import type {
StockInfo,
BalanceSheetData,
IncomeStatementData,
CashflowData,
FinancialMetricsData,
MainBusinessData,
ForecastData,
IndustryRankData,
ComparisonData,
} from '../types';
interface UseFinancialDataOptions {
stockCode?: string;
periods?: number;
}
interface UseFinancialDataReturn {
// 数据状态
stockInfo: StockInfo | null;
balanceSheet: BalanceSheetData[];
incomeStatement: IncomeStatementData[];
cashflow: CashflowData[];
financialMetrics: FinancialMetricsData[];
mainBusiness: MainBusinessData | null;
forecast: ForecastData | null;
industryRank: IndustryRankData[];
comparison: ComparisonData[];
// 加载状态
loading: boolean;
error: string | null;
// 操作方法
refetch: () => Promise<void>;
setStockCode: (code: string) => void;
setSelectedPeriods: (periods: number) => void;
// 当前参数
currentStockCode: string;
selectedPeriods: number;
}
/**
* 财务数据加载 Hook
* @param options - 配置选项
* @returns 财务数据和操作方法
*/
export const useFinancialData = (
options: UseFinancialDataOptions = {}
): UseFinancialDataReturn => {
const { stockCode: initialStockCode = '600000', periods: initialPeriods = 8 } = options;
// 参数状态
const [stockCode, setStockCode] = useState(initialStockCode);
const [selectedPeriods, setSelectedPeriods] = useState(initialPeriods);
// 加载状态
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
// 财务数据状态
const [stockInfo, setStockInfo] = useState<StockInfo | null>(null);
const [balanceSheet, setBalanceSheet] = useState<BalanceSheetData[]>([]);
const [incomeStatement, setIncomeStatement] = useState<IncomeStatementData[]>([]);
const [cashflow, setCashflow] = useState<CashflowData[]>([]);
const [financialMetrics, setFinancialMetrics] = useState<FinancialMetricsData[]>([]);
const [mainBusiness, setMainBusiness] = useState<MainBusinessData | null>(null);
const [forecast, setForecast] = useState<ForecastData | null>(null);
const [industryRank, setIndustryRank] = useState<IndustryRankData[]>([]);
const [comparison, setComparison] = useState<ComparisonData[]>([]);
const toast = useToast();
// 加载所有财务数据
const loadFinancialData = useCallback(async () => {
if (!stockCode || stockCode.length !== 6) {
logger.warn('useFinancialData', '无效的股票代码', { stockCode });
toast({
title: '请输入有效的6位股票代码',
status: 'warning',
duration: 3000,
});
return;
}
logger.debug('useFinancialData', '开始加载财务数据', { stockCode, selectedPeriods });
setLoading(true);
setError(null);
try {
// 并行加载所有数据
const [
stockInfoRes,
balanceRes,
incomeRes,
cashflowRes,
metricsRes,
businessRes,
forecastRes,
rankRes,
comparisonRes,
] = await Promise.all([
financialService.getStockInfo(stockCode),
financialService.getBalanceSheet(stockCode, selectedPeriods),
financialService.getIncomeStatement(stockCode, selectedPeriods),
financialService.getCashflow(stockCode, selectedPeriods),
financialService.getFinancialMetrics(stockCode, selectedPeriods),
financialService.getMainBusiness(stockCode, 4),
financialService.getForecast(stockCode),
financialService.getIndustryRank(stockCode, 4),
financialService.getPeriodComparison(stockCode, selectedPeriods),
]);
// 设置数据
if (stockInfoRes.success) setStockInfo(stockInfoRes.data);
if (balanceRes.success) setBalanceSheet(balanceRes.data);
if (incomeRes.success) setIncomeStatement(incomeRes.data);
if (cashflowRes.success) setCashflow(cashflowRes.data);
if (metricsRes.success) setFinancialMetrics(metricsRes.data);
if (businessRes.success) setMainBusiness(businessRes.data);
if (forecastRes.success) setForecast(forecastRes.data);
if (rankRes.success) setIndustryRank(rankRes.data);
if (comparisonRes.success) setComparison(comparisonRes.data);
logger.info('useFinancialData', '财务数据加载成功', { stockCode });
} catch (err) {
const errorMessage = err instanceof Error ? err.message : '未知错误';
setError(errorMessage);
logger.error('useFinancialData', 'loadFinancialData', err, { stockCode, selectedPeriods });
} finally {
setLoading(false);
}
}, [stockCode, selectedPeriods, toast]);
// 监听 props 中的 stockCode 变化
useEffect(() => {
if (initialStockCode && initialStockCode !== stockCode) {
setStockCode(initialStockCode);
}
}, [initialStockCode]);
// 初始加载和参数变化时重新加载
useEffect(() => {
if (stockCode) {
loadFinancialData();
}
}, [stockCode, selectedPeriods, loadFinancialData]);
return {
// 数据状态
stockInfo,
balanceSheet,
incomeStatement,
cashflow,
financialMetrics,
mainBusiness,
forecast,
industryRank,
comparison,
// 加载状态
loading,
error,
// 操作方法
refetch: loadFinancialData,
setStockCode,
setSelectedPeriods,
// 当前参数
currentStockCode: stockCode,
selectedPeriods,
};
};
export default useFinancialData;