Files
vf_react/src/components/StockChart/StockChartModal.tsx

214 lines
5.8 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/components/StockChart/StockChartModal.tsx - 统一的股票图表组件KLineChart 实现)
import React, { useState } from 'react';
import {
Modal,
ModalOverlay,
ModalContent,
ModalHeader,
ModalCloseButton,
ModalBody,
Button,
ButtonGroup,
VStack,
HStack,
Text,
Badge,
Box,
Flex,
CircularProgress,
} from '@chakra-ui/react';
import RiskDisclaimer from '../RiskDisclaimer';
import { RelationDescription } from '../StockRelation';
import type { RelationDescType } from '../StockRelation';
import { useKLineChart, useKLineData, useEventMarker } from './hooks';
import { Alert, AlertIcon } from '@chakra-ui/react';
/**
* 图表类型
*/
type ChartType = 'timeline' | 'daily';
/**
* 股票信息
*/
interface StockInfo {
stock_code: string;
stock_name?: string;
relation_desc?: RelationDescType;
}
/**
* StockChartModal 组件 Props
*/
export interface StockChartModalProps {
/** 模态框是否打开 */
isOpen: boolean;
/** 关闭回调 */
onClose: () => void;
/** 股票信息 */
stock: StockInfo | null;
/** 事件时间 */
eventTime?: string | null;
/** 是否使用 Chakra UI保留字段当前未使用 */
isChakraUI?: boolean;
/** 模态框大小 */
size?: string;
/** 初始图表类型 */
initialChartType?: ChartType;
}
const StockChartModal: React.FC<StockChartModalProps> = ({
isOpen,
onClose,
stock,
eventTime,
isChakraUI = true,
size = '6xl',
initialChartType = 'timeline',
}) => {
// 状态管理
const [chartType, setChartType] = useState<ChartType>(initialChartType);
// KLineChart Hooks
const { chart, chartRef, isInitialized, error: chartError } = useKLineChart({
containerId: `kline-chart-${stock?.stock_code || 'default'}`,
height: 500,
autoResize: true,
chartType, // ✅ 传递 chartType让 Hook 根据类型应用不同样式
});
const { data, loading, error: dataError } = useKLineData({
chart,
stockCode: stock?.stock_code || '',
chartType,
eventTime: eventTime || undefined,
autoLoad: true, // 改为 true让 Hook 内部根据 stockCode 和 chart 判断是否加载
});
const { marker } = useEventMarker({
chart,
data,
eventTime: eventTime || undefined,
eventTitle: '事件发生',
autoCreate: true,
});
// 守卫子句
if (!stock) return null;
return (
<Modal isOpen={isOpen} onClose={onClose} size={size}>
<ModalOverlay />
<ModalContent maxW="90vw" maxH="90vh" overflow="hidden">
<ModalHeader pb={4} position="relative">
<VStack align="flex-start" spacing={2}>
<HStack>
<Text fontSize="lg" fontWeight="bold">
{stock.stock_name || stock.stock_code} ({stock.stock_code}) -
</Text>
{data.length > 0 && <Badge colorScheme="blue">: {data.length}</Badge>}
</HStack>
<ButtonGroup size="sm">
<Button
variant={chartType === 'timeline' ? 'solid' : 'outline'}
onClick={() => setChartType('timeline')}
colorScheme="blue"
>
线
</Button>
<Button
variant={chartType === 'daily' ? 'solid' : 'outline'}
onClick={() => setChartType('daily')}
colorScheme="blue"
>
K线
</Button>
</ButtonGroup>
</VStack>
{/* 重件发生标签 - 仅在有 eventTime 时显示 */}
{eventTime && (
<Badge
colorScheme="yellow"
fontSize="sm"
px={3}
py={1}
borderRadius="md"
position="absolute"
top="4"
right="12"
boxShadow="sm"
>
()
</Badge>
)}
</ModalHeader>
<ModalCloseButton />
<ModalBody p={0} overflowY="auto" maxH="calc(90vh - 120px)">
{/* 错误提示 */}
{(chartError || dataError) && (
<Alert status="error" mx={4} mt={4}>
<AlertIcon />
{chartError?.message || dataError?.message}
</Alert>
)}
{/* 图表区域 - 响应式高度 */}
<Box
h={{
base: "calc(60vh - 100px)", // 移动端60% 视口高度 - 100px
md: "calc(70vh - 150px)", // 平板70% 视口高度 - 150px
lg: "calc(80vh - 200px)" // 桌面80% 视口高度 - 200px
}}
minH="350px" // 最小高度:确保可用性
maxH="650px" // 最大高度:避免过大
w="100%"
position="relative"
>
{loading && (
<Flex
position="absolute"
top="0"
left="0"
right="0"
bottom="0"
bg="rgba(255, 255, 255, 0.7)"
zIndex="10"
alignItems="center"
justifyContent="center"
>
<VStack spacing={4}>
<CircularProgress isIndeterminate color="blue.300" />
<Text>...</Text>
</VStack>
</Flex>
)}
<div
ref={chartRef}
id={`kline-chart-${stock.stock_code}`}
style={{ width: '100%', height: '100%' }}
/>
</Box>
{/* 关联描述 */}
<RelationDescription relationDesc={stock?.relation_desc} />
{/* 风险提示 */}
<Box px={4} pb={4}>
<RiskDisclaimer text="" variant="default" sx={{}} />
</Box>
</ModalBody>
</ModalContent>
</Modal>
);
};
export default StockChartModal;