refactor(TradeDataPanel): 合并 KLineChart 和 MinuteKLineSection 为 KLineModule
- 新增 KLineModule 组件,整合日K线和分钟K线功能 - 右上角 ButtonGroup 切换「日K」/「分钟」模式 - 刷新按钮置于切换按钮组前方 - 切换到分钟模式时自动加载数据 - 删除旧的 KLineChart.tsx 和 MinuteKLineSection.tsx - 更新 panels/index.ts 导出 - 更新 types.ts,合并类型定义为 KLineModuleProps 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,43 +0,0 @@
|
||||
// src/views/Company/components/MarketDataView/components/panels/TradeDataPanel/KLineChart.tsx
|
||||
// 日K线图组件
|
||||
|
||||
import React from 'react';
|
||||
import { Box, CardBody } from '@chakra-ui/react';
|
||||
import ReactECharts from 'echarts-for-react';
|
||||
|
||||
import ThemedCard from '../../ThemedCard';
|
||||
import { getKLineOption } from '../../../utils/chartOptions';
|
||||
import type { Theme, TradeDayData, RiseAnalysis } from '../../../types';
|
||||
|
||||
export interface KLineChartProps {
|
||||
theme: Theme;
|
||||
tradeData: TradeDayData[];
|
||||
analysisMap: Record<number, RiseAnalysis>;
|
||||
onChartClick: (params: { seriesName?: string; data?: [number, number] }) => void;
|
||||
}
|
||||
|
||||
const KLineChart: React.FC<KLineChartProps> = ({
|
||||
theme,
|
||||
tradeData,
|
||||
analysisMap,
|
||||
onChartClick,
|
||||
}) => {
|
||||
return (
|
||||
<ThemedCard theme={theme}>
|
||||
<CardBody>
|
||||
{tradeData.length > 0 && (
|
||||
<Box h="600px">
|
||||
<ReactECharts
|
||||
option={getKLineOption(theme, tradeData, analysisMap)}
|
||||
style={{ height: '100%', width: '100%' }}
|
||||
theme="light"
|
||||
onEvents={{ click: onChartClick }}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</CardBody>
|
||||
</ThemedCard>
|
||||
);
|
||||
};
|
||||
|
||||
export default KLineChart;
|
||||
@@ -0,0 +1,168 @@
|
||||
// src/views/Company/components/MarketDataView/components/panels/TradeDataPanel/KLineModule.tsx
|
||||
// K线模块 - 日K线/分钟K线切换展示
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Text,
|
||||
VStack,
|
||||
HStack,
|
||||
Button,
|
||||
ButtonGroup,
|
||||
Badge,
|
||||
Center,
|
||||
Spinner,
|
||||
CardBody,
|
||||
CardHeader,
|
||||
Heading,
|
||||
} from '@chakra-ui/react';
|
||||
import { RepeatIcon } from '@chakra-ui/icons';
|
||||
import { BarChart2, Clock } from 'lucide-react';
|
||||
import ReactECharts from 'echarts-for-react';
|
||||
|
||||
import ThemedCard from '../../ThemedCard';
|
||||
import { getKLineOption, getMinuteKLineOption } from '../../../utils/chartOptions';
|
||||
import { MinuteStats, TradeAnalysis, EmptyState } from './atoms';
|
||||
import type { KLineModuleProps } from '../../../types';
|
||||
|
||||
// 重新导出类型供外部使用
|
||||
export type { KLineModuleProps } from '../../../types';
|
||||
|
||||
type ChartMode = 'daily' | 'minute';
|
||||
|
||||
const KLineModule: React.FC<KLineModuleProps> = ({
|
||||
theme,
|
||||
tradeData,
|
||||
minuteData,
|
||||
minuteLoading,
|
||||
analysisMap,
|
||||
onLoadMinuteData,
|
||||
onChartClick,
|
||||
}) => {
|
||||
const [mode, setMode] = useState<ChartMode>('daily');
|
||||
const hasMinuteData = minuteData && minuteData.data && minuteData.data.length > 0;
|
||||
|
||||
// 切换到分钟模式时自动加载数据
|
||||
const handleModeChange = (newMode: ChartMode) => {
|
||||
setMode(newMode);
|
||||
if (newMode === 'minute' && !hasMinuteData && !minuteLoading) {
|
||||
onLoadMinuteData();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ThemedCard theme={theme}>
|
||||
<CardHeader>
|
||||
<HStack justify="space-between" align="center">
|
||||
<HStack spacing={3}>
|
||||
<Heading size="md" color={theme.textSecondary}>
|
||||
{mode === 'daily' ? '日K线图' : '分钟K线图'}
|
||||
</Heading>
|
||||
{mode === 'minute' && minuteData?.trade_date && (
|
||||
<Badge colorScheme="blue" fontSize="xs">
|
||||
{minuteData.trade_date}
|
||||
</Badge>
|
||||
)}
|
||||
</HStack>
|
||||
|
||||
<HStack spacing={3}>
|
||||
{/* 分钟模式下的刷新按钮 */}
|
||||
{mode === 'minute' && (
|
||||
<Button
|
||||
leftIcon={<RepeatIcon />}
|
||||
size="sm"
|
||||
variant="outline"
|
||||
colorScheme="blue"
|
||||
onClick={onLoadMinuteData}
|
||||
isLoading={minuteLoading}
|
||||
loadingText="获取中"
|
||||
>
|
||||
刷新
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{/* 模式切换按钮组 */}
|
||||
<ButtonGroup size="sm" isAttached variant="outline">
|
||||
<Button
|
||||
leftIcon={<BarChart2 size={16} />}
|
||||
colorScheme={mode === 'daily' ? 'blue' : 'gray'}
|
||||
variant={mode === 'daily' ? 'solid' : 'outline'}
|
||||
onClick={() => handleModeChange('daily')}
|
||||
>
|
||||
日K
|
||||
</Button>
|
||||
<Button
|
||||
leftIcon={<Clock size={16} />}
|
||||
colorScheme={mode === 'minute' ? 'blue' : 'gray'}
|
||||
variant={mode === 'minute' ? 'solid' : 'outline'}
|
||||
onClick={() => handleModeChange('minute')}
|
||||
>
|
||||
分钟
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</HStack>
|
||||
</HStack>
|
||||
</CardHeader>
|
||||
|
||||
<CardBody>
|
||||
{mode === 'daily' ? (
|
||||
// 日K线图
|
||||
tradeData.length > 0 ? (
|
||||
<Box h="600px">
|
||||
<ReactECharts
|
||||
option={getKLineOption(theme, tradeData, analysisMap)}
|
||||
style={{ height: '100%', width: '100%' }}
|
||||
theme="light"
|
||||
onEvents={{ click: onChartClick }}
|
||||
/>
|
||||
</Box>
|
||||
) : (
|
||||
<EmptyState
|
||||
theme={theme}
|
||||
title="暂无日K线数据"
|
||||
description="该股票暂无交易数据"
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
// 分钟K线图
|
||||
minuteLoading ? (
|
||||
<Center h="500px">
|
||||
<VStack spacing={4}>
|
||||
<Spinner
|
||||
thickness="4px"
|
||||
speed="0.65s"
|
||||
emptyColor={theme.bgDark}
|
||||
color={theme.primary}
|
||||
size="lg"
|
||||
/>
|
||||
<Text color={theme.textMuted} fontSize="sm">
|
||||
加载分钟频数据中...
|
||||
</Text>
|
||||
</VStack>
|
||||
</Center>
|
||||
) : hasMinuteData ? (
|
||||
<VStack spacing={6} align="stretch">
|
||||
<Box h="500px">
|
||||
<ReactECharts
|
||||
option={getMinuteKLineOption(theme, minuteData)}
|
||||
style={{ height: '100%', width: '100%' }}
|
||||
theme="light"
|
||||
/>
|
||||
</Box>
|
||||
<MinuteStats theme={theme} data={minuteData.data} />
|
||||
<TradeAnalysis theme={theme} data={minuteData.data} />
|
||||
</VStack>
|
||||
) : (
|
||||
<EmptyState
|
||||
theme={theme}
|
||||
title="暂无分钟数据"
|
||||
description="点击右上角刷新按钮获取当日分钟频数据"
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</CardBody>
|
||||
</ThemedCard>
|
||||
);
|
||||
};
|
||||
|
||||
export default KLineModule;
|
||||
@@ -1,107 +0,0 @@
|
||||
// src/views/Company/components/MarketDataView/components/panels/TradeDataPanel/MinuteKLineSection.tsx
|
||||
// 分钟K线数据区域组件
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
Box,
|
||||
Text,
|
||||
VStack,
|
||||
HStack,
|
||||
Button,
|
||||
Badge,
|
||||
Center,
|
||||
Spinner,
|
||||
CardBody,
|
||||
CardHeader,
|
||||
Icon,
|
||||
Heading,
|
||||
} from '@chakra-ui/react';
|
||||
import { TimeIcon, RepeatIcon } from '@chakra-ui/icons';
|
||||
import ReactECharts from 'echarts-for-react';
|
||||
|
||||
import ThemedCard from '../../ThemedCard';
|
||||
import { getMinuteKLineOption } from '../../../utils/chartOptions';
|
||||
import { MinuteStats, TradeAnalysis, EmptyState } from './atoms';
|
||||
import type { Theme, MinuteData } from '../../../types';
|
||||
|
||||
export interface MinuteKLineSectionProps {
|
||||
theme: Theme;
|
||||
minuteData: MinuteData | null;
|
||||
loading: boolean;
|
||||
onLoadMinuteData: () => void;
|
||||
}
|
||||
|
||||
const MinuteKLineSection: React.FC<MinuteKLineSectionProps> = ({
|
||||
theme,
|
||||
minuteData,
|
||||
loading,
|
||||
onLoadMinuteData,
|
||||
}) => {
|
||||
const hasData = minuteData && minuteData.data && minuteData.data.length > 0;
|
||||
|
||||
return (
|
||||
<ThemedCard theme={theme}>
|
||||
<CardHeader>
|
||||
<HStack justify="space-between" align="center">
|
||||
<HStack spacing={3}>
|
||||
<Icon as={TimeIcon} color={theme.primary} boxSize={5} />
|
||||
<Heading size="md" color={theme.textSecondary}>
|
||||
当日分钟频数据
|
||||
</Heading>
|
||||
{minuteData?.trade_date && (
|
||||
<Badge colorScheme="blue" fontSize="xs">
|
||||
{minuteData.trade_date}
|
||||
</Badge>
|
||||
)}
|
||||
</HStack>
|
||||
<Button
|
||||
leftIcon={<RepeatIcon />}
|
||||
size="sm"
|
||||
variant="outline"
|
||||
colorScheme="blue"
|
||||
onClick={onLoadMinuteData}
|
||||
isLoading={loading}
|
||||
loadingText="获取中"
|
||||
>
|
||||
获取分钟数据
|
||||
</Button>
|
||||
</HStack>
|
||||
</CardHeader>
|
||||
|
||||
<CardBody>
|
||||
{loading ? (
|
||||
<Center h="400px">
|
||||
<VStack spacing={4}>
|
||||
<Spinner
|
||||
thickness="4px"
|
||||
speed="0.65s"
|
||||
emptyColor={theme.bgDark}
|
||||
color={theme.primary}
|
||||
size="lg"
|
||||
/>
|
||||
<Text color={theme.textMuted} fontSize="sm">
|
||||
加载分钟频数据中...
|
||||
</Text>
|
||||
</VStack>
|
||||
</Center>
|
||||
) : hasData ? (
|
||||
<VStack spacing={6} align="stretch">
|
||||
<Box h="500px">
|
||||
<ReactECharts
|
||||
option={getMinuteKLineOption(theme, minuteData)}
|
||||
style={{ height: '100%', width: '100%' }}
|
||||
theme="light"
|
||||
/>
|
||||
</Box>
|
||||
<MinuteStats theme={theme} data={minuteData.data} />
|
||||
<TradeAnalysis theme={theme} data={minuteData.data} />
|
||||
</VStack>
|
||||
) : (
|
||||
<EmptyState theme={theme} />
|
||||
)}
|
||||
</CardBody>
|
||||
</ThemedCard>
|
||||
);
|
||||
};
|
||||
|
||||
export default MinuteKLineSection;
|
||||
@@ -1,11 +1,10 @@
|
||||
// src/views/Company/components/MarketDataView/components/panels/TradeDataPanel/index.tsx
|
||||
// 交易数据面板 - K线图、分钟图、交易明细表格
|
||||
// 交易数据面板 - K线模块(日K/分钟切换)、交易明细表格
|
||||
|
||||
import React from 'react';
|
||||
import { VStack } from '@chakra-ui/react';
|
||||
|
||||
import KLineChart from './KLineChart';
|
||||
import MinuteKLineSection from './MinuteKLineSection';
|
||||
import KLineModule from './KLineModule';
|
||||
import TradeTable from './TradeTable';
|
||||
import type { Theme, TradeDayData, MinuteData, RiseAnalysis } from '../../../types';
|
||||
|
||||
@@ -30,18 +29,14 @@ const TradeDataPanel: React.FC<TradeDataPanelProps> = ({
|
||||
}) => {
|
||||
return (
|
||||
<VStack spacing={6} align="stretch">
|
||||
<KLineChart
|
||||
<KLineModule
|
||||
theme={theme}
|
||||
tradeData={tradeData}
|
||||
analysisMap={analysisMap}
|
||||
onChartClick={onChartClick}
|
||||
/>
|
||||
|
||||
<MinuteKLineSection
|
||||
theme={theme}
|
||||
minuteData={minuteData}
|
||||
loading={minuteLoading}
|
||||
minuteLoading={minuteLoading}
|
||||
analysisMap={analysisMap}
|
||||
onLoadMinuteData={onLoadMinuteData}
|
||||
onChartClick={onChartClick}
|
||||
/>
|
||||
|
||||
<TradeTable theme={theme} tradeData={tradeData} />
|
||||
@@ -52,7 +47,7 @@ const TradeDataPanel: React.FC<TradeDataPanelProps> = ({
|
||||
export default TradeDataPanel;
|
||||
|
||||
// 导出子组件供外部按需使用
|
||||
export { KLineChart, MinuteKLineSection, TradeTable };
|
||||
export type { KLineChartProps } from './KLineChart';
|
||||
export type { MinuteKLineSectionProps } from './MinuteKLineSection';
|
||||
export { default as KLineModule } from './KLineModule';
|
||||
export { default as TradeTable } from './TradeTable';
|
||||
export type { KLineModuleProps } from './KLineModule';
|
||||
export type { TradeTableProps } from './TradeTable';
|
||||
|
||||
@@ -15,13 +15,5 @@ export type { UnusualPanelProps } from './UnusualPanel';
|
||||
export type { PledgePanelProps } from './PledgePanel';
|
||||
|
||||
// 导出 TradeDataPanel 子组件
|
||||
export {
|
||||
KLineChart,
|
||||
MinuteKLineSection,
|
||||
TradeTable,
|
||||
} from './TradeDataPanel';
|
||||
export type {
|
||||
KLineChartProps,
|
||||
MinuteKLineSectionProps,
|
||||
TradeTableProps,
|
||||
} from './TradeDataPanel';
|
||||
export { KLineModule, TradeTable } from './TradeDataPanel';
|
||||
export type { KLineModuleProps, TradeTableProps } from './TradeDataPanel';
|
||||
|
||||
@@ -287,23 +287,16 @@ export interface TradeDataTabProps {
|
||||
}
|
||||
|
||||
/**
|
||||
* KLineChart 组件 Props
|
||||
* KLineModule 组件 Props(日K/分钟K线切换模块)
|
||||
*/
|
||||
export interface KLineChartProps {
|
||||
export interface KLineModuleProps {
|
||||
theme: Theme;
|
||||
tradeData: TradeDayData[];
|
||||
analysisMap: Record<number, RiseAnalysis>;
|
||||
onAnalysisClick: (analysis: RiseAnalysis) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* MinuteKLineChart 组件 Props
|
||||
*/
|
||||
export interface MinuteKLineChartProps {
|
||||
theme: Theme;
|
||||
minuteData: MinuteData | null;
|
||||
loading: boolean;
|
||||
onRefresh: () => void;
|
||||
minuteLoading: boolean;
|
||||
analysisMap: Record<number, RiseAnalysis>;
|
||||
onLoadMinuteData: () => void;
|
||||
onChartClick: (params: { seriesName?: string; data?: [number, number] }) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user