Files
vf_react/src/views/Company/components/MarketDataView/index.tsx
zdl bea4c7fe81 perf(MarketDataView): 优化数据映射性能和请求管理
- useMarketData: 使用 Map 替代 findIndex,O(n*m) → O(n+m) 性能优化
- useMarketData: 修复 React StrictMode 下请求被意外取消的问题
- config.ts: 添加 CompanyOverview 和 DynamicTracking 的骨架屏 fallback

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-19 18:55:06 +08:00

182 lines
5.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// src/views/Company/components/MarketDataView/index.tsx
// MarketDataView 主组件 - 股票市场数据综合展示
import React, { useState, useEffect, ReactNode, useMemo, useCallback, memo } from 'react';
import {
Box,
Card,
CardBody,
Container,
VStack,
useDisclosure,
} from '@chakra-ui/react';
import { Unlock, ArrowUp, Star, Lock } from 'lucide-react';
// 通用组件
import SubTabContainer from '@components/SubTabContainer';
import type { SubTabConfig } from '@components/SubTabContainer';
// 内部模块导入
import { themes, DEFAULT_PERIOD } from './constants';
import { useMarketData } from './hooks/useMarketData';
import {
ThemedCard,
StockSummaryCard,
AnalysisModal,
AnalysisContent,
} from './components';
import {
TradeDataPanel,
FundingPanel,
BigDealPanel,
UnusualPanel,
PledgePanel,
} from './components/panels';
import type { MarketDataViewProps } from './types';
/**
* MarketDataView 主组件
* 展示股票的市场数据:交易数据、融资融券、大宗交易、龙虎榜、股权质押
*/
const MarketDataView: React.FC<MarketDataViewProps> = ({ stockCode: propStockCode }) => {
const { isOpen, onOpen, onClose } = useDisclosure();
const [modalContent, setModalContent] = useState<ReactNode>(null);
// 获取当前主题
const theme = themes.light;
// 状态管理
const [stockCode, setStockCode] = useState(propStockCode || '600000');
const [activeTab, setActiveTab] = useState(0);
const [selectedPeriod, setSelectedPeriod] = useState(DEFAULT_PERIOD);
// 使用自定义 Hook 获取数据
const {
loading,
summary,
tradeData,
fundingData,
bigDealData,
unusualData,
pledgeData,
minuteData,
minuteLoading,
analysisMap,
loadMinuteData,
loadDataByType,
} = useMarketData(stockCode, selectedPeriod);
// Tab 切换时按需加载数据
const handleTabChange = useCallback((index: number) => {
setActiveTab(index);
// 根据 tab index 加载对应数据
const tabDataMap: Record<number, 'funding' | 'bigDeal' | 'unusual' | 'pledge'> = {
0: 'funding',
1: 'bigDeal',
2: 'unusual',
3: 'pledge',
};
const dataType = tabDataMap[index];
if (dataType) {
loadDataByType(dataType);
}
}, [loadDataByType]);
// 监听 props 中的 stockCode 变化
useEffect(() => {
if (propStockCode && propStockCode !== stockCode) {
setStockCode(propStockCode);
}
}, [propStockCode, stockCode]);
// 首次渲染时加载默认 Tab融资融券的数据
useEffect(() => {
// 默认 Tab 是融资融券index 0
if (activeTab === 0) {
loadDataByType('funding');
}
}, [loadDataByType, activeTab]);
// 处理图表点击事件
const handleChartClick = useCallback(
(params: { seriesName?: string; data?: [number, number] }) => {
if (params.seriesName === '涨幅分析' && params.data) {
const dataIndex = params.data[0];
const analysis = analysisMap[dataIndex];
if (analysis) {
setModalContent(<AnalysisContent analysis={analysis} theme={theme} />);
onOpen();
}
}
},
[analysisMap, theme, onOpen]
);
// Tab 配置 - 使用通用 SubTabContainer不含交易数据交易数据单独显示在上方
const tabConfigs: SubTabConfig[] = [
{ key: 'funding', name: '融资融券', icon: Unlock, component: FundingPanel },
{ key: 'bigDeal', name: '大宗交易', icon: ArrowUp, component: BigDealPanel },
{ key: 'unusual', name: '龙虎榜', icon: Star, component: UnusualPanel },
{ key: 'pledge', name: '股权质押', icon: Lock, component: PledgePanel },
];
// 传递给 Tab 组件的 props - 只传递各 Tab 需要的数据
const componentProps = useMemo(
() => ({
// 各 Tab 只使用自己需要的数据
fundingData,
bigDealData,
unusualData,
pledgeData,
}),
[fundingData, bigDealData, unusualData, pledgeData]
);
return (
<Box bg={'#1A202C'} minH="100vh" color={theme.textPrimary}>
<Container maxW="container.xl" py={4}>
<VStack align="stretch" spacing={4}>
{/* 股票概览 */}
{summary && <StockSummaryCard summary={summary} theme={theme} />}
{/* 交易数据 - 日K/分钟K线独立显示在 Tab 上方) */}
<TradeDataPanel
theme={theme}
tradeData={tradeData}
minuteData={minuteData}
minuteLoading={minuteLoading}
analysisMap={analysisMap}
onLoadMinuteData={loadMinuteData}
onChartClick={handleChartClick}
selectedPeriod={selectedPeriod}
onPeriodChange={setSelectedPeriod}
stockCode={stockCode}
loading={loading}
/>
{/* 主要内容区域 - Tab */}
<Card bg="gray.900" shadow="md" border="1px solid" borderColor="rgba(212, 175, 55, 0.3)">
<CardBody p={0}>
<SubTabContainer
tabs={tabConfigs}
componentProps={componentProps}
themePreset="blackGold"
index={activeTab}
onTabChange={handleTabChange}
isLazy
size="sm"
/>
</CardBody>
</Card>
</VStack>
</Container>
{/* 涨幅分析模态框 */}
<AnalysisModal isOpen={isOpen} onClose={onClose} content={modalContent} theme={theme} />
</Box>
);
};
export default memo(MarketDataView);