/** * StockChartKLineModal - K 线图表模态框组件 * * 使用 KLineChart 库实现的专业金融图表组件 * 替换原有的 ECharts 实现(StockChartAntdModal.js) */ import React, { useState, useCallback, useMemo } from 'react'; import { Modal, Button, Radio, Select, Space, Spin, Alert } from 'antd'; import type { RadioChangeEvent } from 'antd'; import { LineChartOutlined, BarChartOutlined, SettingOutlined, ReloadOutlined, } from '@ant-design/icons'; import { Box } from '@chakra-ui/react'; // 自定义 Hooks import { useKLineChart, useKLineData, useEventMarker } from './hooks'; // 类型定义 import type { ChartType, StockInfo } from './types'; // 配置常量 import { CHART_TYPE_CONFIG, CHART_HEIGHTS, INDICATORS, DEFAULT_SUB_INDICATORS, } from './config'; // 工具函数 import { createSubIndicators } from './utils'; // ==================== 组件 Props ==================== export interface StockChartKLineModalProps { /** 是否显示模态框 */ visible: boolean; /** 关闭模态框回调 */ onClose: () => void; /** 股票信息 */ stock: StockInfo; /** 事件时间(ISO 字符串,可选) */ eventTime?: string; /** 事件标题(用于标记标签,可选) */ eventTitle?: string; } // ==================== 主组件 ==================== const StockChartKLineModal: React.FC = ({ visible, onClose, stock, eventTime, eventTitle, }) => { // ==================== 状态管理 ==================== /** 图表类型(分时图/日K线) */ const [chartType, setChartType] = useState('daily'); /** 选中的副图指标 */ const [selectedIndicators, setSelectedIndicators] = useState( DEFAULT_SUB_INDICATORS ); // ==================== 自定义 Hooks ==================== /** 图表实例管理 */ const { chart, chartRef, isInitialized, error: chartError } = useKLineChart({ containerId: `kline-chart-${stock.stock_code}`, height: CHART_HEIGHTS.main, autoResize: true, }); /** 数据加载管理 */ const { data, loading: dataLoading, error: dataError, loadData, } = useKLineData({ chart, stockCode: stock.stock_code, chartType, eventTime, autoLoad: visible, // 模态框打开时自动加载 }); /** 事件标记管理 */ const { marker } = useEventMarker({ chart, data, eventTime, eventTitle, autoCreate: true, }); // ==================== 事件处理 ==================== /** * 切换图表类型(分时图 ↔ 日K线) */ const handleChartTypeChange = useCallback((e: RadioChangeEvent) => { const newType = e.target.value as ChartType; setChartType(newType); }, []); /** * 切换副图指标 */ const handleIndicatorChange = useCallback( (values: string[]) => { setSelectedIndicators(values); if (!chart) { return; } // 先移除所有副图指标(KLineChart 会自动移除) // 然后创建新的指标 createSubIndicators(chart, values); }, [chart] ); /** * 刷新数据 */ const handleRefresh = useCallback(() => { loadData(); }, [loadData]); // ==================== 计算属性 ==================== /** 是否有错误 */ const hasError = useMemo(() => { return !!chartError || !!dataError; }, [chartError, dataError]); /** 错误消息 */ const errorMessage = useMemo(() => { if (chartError) { return `图表初始化失败: ${chartError.message}`; } if (dataError) { return `数据加载失败: ${dataError.message}`; } return null; }, [chartError, dataError]); /** 模态框标题 */ const modalTitle = useMemo(() => { return `${stock.stock_name}(${stock.stock_code}) - ${CHART_TYPE_CONFIG[chartType].label}`; }, [stock, chartType]); /** 是否显示加载状态 */ const showLoading = useMemo(() => { return dataLoading || !isInitialized; }, [dataLoading, isInitialized]); // ==================== 副作用 ==================== // 无副作用,都在 Hooks 中管理 // ==================== 渲染 ==================== return ( {/* 工具栏 */} {/* 图表类型切换 */} 分时图 日K线 {/* 副图指标选择 */} {/* 刷新按钮 */} {/* 错误提示 */} {hasError && ( )} {/* 图表容器 */} {/* 加载遮罩 */} {showLoading && ( )} {/* KLineChart 容器 */}
{/* 数据信息(调试用,生产环境可移除) */} {process.env.NODE_ENV === 'development' && ( 数据点数: {data.length} 事件标记: {marker ? marker.label : '无'} 图表ID: {chart?.id || '未初始化'} )} ); }; export default StockChartKLineModal;