201 lines
4.8 KiB
TypeScript
201 lines
4.8 KiB
TypeScript
/**
|
||
* useEventMarker Hook
|
||
*
|
||
* 管理事件标记的创建、更新和删除
|
||
*/
|
||
|
||
import { useEffect, useState, useCallback } from 'react';
|
||
import type { Chart } from 'klinecharts';
|
||
import type { EventMarker, KLineDataPoint } from '../types';
|
||
import {
|
||
createEventMarkerFromTime,
|
||
createEventMarkerOverlay,
|
||
createEventHighlightOverlay,
|
||
removeAllEventMarkers,
|
||
} from '../utils/eventMarkerUtils';
|
||
|
||
export interface UseEventMarkerOptions {
|
||
/** KLineChart 实例 */
|
||
chart: Chart | null;
|
||
/** K 线数据(用于定位标记) */
|
||
data: KLineDataPoint[];
|
||
/** 事件时间(ISO 字符串) */
|
||
eventTime?: string;
|
||
/** 事件标题(用于标记标签) */
|
||
eventTitle?: string;
|
||
/** 是否自动创建标记 */
|
||
autoCreate?: boolean;
|
||
}
|
||
|
||
export interface UseEventMarkerReturn {
|
||
/** 当前标记 */
|
||
marker: EventMarker | null;
|
||
/** 标记 ID(已添加到图表) */
|
||
markerId: string | null;
|
||
/** 创建标记 */
|
||
createMarker: (time: string, label: string, color?: string) => void;
|
||
/** 移除标记 */
|
||
removeMarker: () => void;
|
||
/** 移除所有标记 */
|
||
removeAllMarkers: () => void;
|
||
}
|
||
|
||
/**
|
||
* 事件标记管理 Hook
|
||
*
|
||
* @param options 配置选项
|
||
* @returns UseEventMarkerReturn
|
||
*
|
||
* @example
|
||
* const { marker, createMarker, removeMarker } = useEventMarker({
|
||
* chart,
|
||
* data,
|
||
* eventTime: '2024-01-01 10:00:00',
|
||
* eventTitle: '重大公告',
|
||
* autoCreate: true,
|
||
* });
|
||
*/
|
||
export const useEventMarker = (
|
||
options: UseEventMarkerOptions
|
||
): UseEventMarkerReturn => {
|
||
const {
|
||
chart,
|
||
data,
|
||
eventTime,
|
||
eventTitle = '事件发生',
|
||
autoCreate = true,
|
||
} = options;
|
||
|
||
const [marker, setMarker] = useState<EventMarker | null>(null);
|
||
const [markerId, setMarkerId] = useState<string | null>(null);
|
||
const [highlightId, setHighlightId] = useState<string | null>(null);
|
||
|
||
/**
|
||
* 创建事件标记
|
||
*/
|
||
const createMarker = useCallback(
|
||
(time: string, label: string, color?: string) => {
|
||
if (!chart || !data || data.length === 0) {
|
||
return;
|
||
}
|
||
|
||
try {
|
||
// 1. 创建事件标记配置
|
||
const eventMarker = createEventMarkerFromTime(time, label, color);
|
||
setMarker(eventMarker);
|
||
|
||
// 2. 创建 Overlay
|
||
const overlay = createEventMarkerOverlay(eventMarker, data);
|
||
|
||
if (!overlay) {
|
||
return;
|
||
}
|
||
|
||
// 3. 添加到图表
|
||
const id = chart.createOverlay(overlay);
|
||
|
||
if (!id || (Array.isArray(id) && id.length === 0)) {
|
||
return;
|
||
}
|
||
|
||
const actualId = Array.isArray(id) ? id[0] : id;
|
||
setMarkerId(actualId as string);
|
||
|
||
// 4. 创建黄色高亮背景(事件影响日)
|
||
const highlightOverlay = createEventHighlightOverlay(time, data);
|
||
if (highlightOverlay) {
|
||
const highlightResult = chart.createOverlay(highlightOverlay);
|
||
const actualHighlightId = Array.isArray(highlightResult) ? highlightResult[0] : highlightResult;
|
||
setHighlightId(actualHighlightId as string);
|
||
}
|
||
} catch (err) {
|
||
// 忽略创建标记时的错误
|
||
}
|
||
},
|
||
[chart, data]
|
||
);
|
||
|
||
/**
|
||
* 移除事件标记
|
||
*/
|
||
const removeMarker = useCallback(() => {
|
||
if (!chart) {
|
||
return;
|
||
}
|
||
|
||
try {
|
||
if (markerId) {
|
||
chart.removeOverlay({ id: markerId });
|
||
}
|
||
if (highlightId) {
|
||
chart.removeOverlay({ id: highlightId });
|
||
}
|
||
|
||
setMarker(null);
|
||
setMarkerId(null);
|
||
setHighlightId(null);
|
||
} catch (err) {
|
||
// 忽略移除标记时的错误
|
||
}
|
||
}, [chart, markerId, highlightId]);
|
||
|
||
/**
|
||
* 移除所有标记
|
||
*/
|
||
const removeAllMarkers = useCallback(() => {
|
||
if (!chart) {
|
||
return;
|
||
}
|
||
|
||
try {
|
||
removeAllEventMarkers(chart);
|
||
setMarker(null);
|
||
setMarkerId(null);
|
||
setHighlightId(null);
|
||
} catch (err) {
|
||
// 忽略移除所有标记时的错误
|
||
}
|
||
}, [chart]);
|
||
|
||
// 自动创建标记(当 eventTime 和数据都准备好时)
|
||
useEffect(() => {
|
||
if (
|
||
autoCreate &&
|
||
eventTime &&
|
||
chart &&
|
||
data &&
|
||
data.length > 0 &&
|
||
!markerId // 避免重复创建
|
||
) {
|
||
createMarker(eventTime, eventTitle);
|
||
}
|
||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||
}, [eventTime, chart, data, autoCreate]);
|
||
|
||
// 清理:组件卸载时移除所有标记
|
||
useEffect(() => {
|
||
return () => {
|
||
if (chart) {
|
||
try {
|
||
if (markerId) {
|
||
chart.removeOverlay({ id: markerId });
|
||
}
|
||
if (highlightId) {
|
||
chart.removeOverlay({ id: highlightId });
|
||
}
|
||
} catch (err) {
|
||
// 忽略清理时的错误
|
||
}
|
||
}
|
||
};
|
||
}, [chart, markerId, highlightId]);
|
||
|
||
return {
|
||
marker,
|
||
markerId,
|
||
createMarker,
|
||
removeMarker,
|
||
removeAllMarkers,
|
||
};
|
||
};
|