feat: 现在创建配置文件。首先让我检查现有的主题配置,以保持样式一致性:

This commit is contained in:
zdl
2025-11-22 23:12:47 +08:00
parent 7b2f5a18bc
commit c391c4c980
5 changed files with 1110 additions and 0 deletions

View File

@@ -0,0 +1,305 @@
/**
* 事件标记工具函数
*
* 用于在 K 线图上创建、管理事件标记Overlay
*/
import dayjs from 'dayjs';
import type { OverlayCreate } from 'klinecharts';
import type { EventMarker, KLineDataPoint } from '../types';
import { EVENT_MARKER_CONFIG } from '../config';
import { findClosestDataPoint } from './dataAdapter';
import { logger } from '@utils/logger';
/**
* 创建事件标记 OverlayKLineChart 10.0 格式)
*
* @param marker 事件标记配置
* @param data K线数据用于定位标记位置
* @returns OverlayCreate | null Overlay 配置对象
*/
export const createEventMarkerOverlay = (
marker: EventMarker,
data: KLineDataPoint[]
): OverlayCreate | null => {
try {
// 查找最接近事件时间的数据点
const closestPoint = findClosestDataPoint(data, marker.timestamp);
if (!closestPoint) {
logger.warn('eventMarkerUtils', 'createEventMarkerOverlay', '未找到匹配的数据点', {
markerId: marker.id,
timestamp: marker.timestamp,
});
return null;
}
// 根据位置计算 Y 坐标
const yValue = calculateMarkerYPosition(closestPoint, marker.position);
// 创建 Overlay 配置KLineChart 10.0 规范)
const overlay: OverlayCreate = {
name: 'simpleAnnotation', // 使用内置的简单标注类型
id: marker.id,
points: [
{
timestamp: closestPoint.timestamp,
value: yValue,
},
],
styles: {
point: {
color: marker.color,
borderColor: marker.color,
borderSize: 2,
radius: EVENT_MARKER_CONFIG.size.point,
},
text: {
color: EVENT_MARKER_CONFIG.text.color,
size: EVENT_MARKER_CONFIG.text.fontSize,
family: EVENT_MARKER_CONFIG.text.fontFamily,
weight: 'bold',
},
rect: {
style: 'fill',
color: marker.color,
borderRadius: EVENT_MARKER_CONFIG.text.borderRadius,
paddingLeft: EVENT_MARKER_CONFIG.text.padding,
paddingRight: EVENT_MARKER_CONFIG.text.padding,
paddingTop: EVENT_MARKER_CONFIG.text.padding,
paddingBottom: EVENT_MARKER_CONFIG.text.padding,
},
},
// 标记文本内容
extendData: {
label: marker.label,
icon: marker.icon,
},
};
logger.debug('eventMarkerUtils', 'createEventMarkerOverlay', '创建事件标记', {
markerId: marker.id,
timestamp: closestPoint.timestamp,
label: marker.label,
});
return overlay;
} catch (error) {
logger.error('eventMarkerUtils', 'createEventMarkerOverlay', error as Error, {
markerId: marker.id,
});
return null;
}
};
/**
* 计算标记的 Y 轴位置
*
* @param dataPoint K线数据点
* @param position 标记位置top/middle/bottom
* @returns number Y轴数值
*/
const calculateMarkerYPosition = (
dataPoint: KLineDataPoint,
position: 'top' | 'middle' | 'bottom'
): number => {
switch (position) {
case 'top':
return dataPoint.high * 1.02; // 在最高价上方 2%
case 'bottom':
return dataPoint.low * 0.98; // 在最低价下方 2%
case 'middle':
default:
return (dataPoint.high + dataPoint.low) / 2; // 中间位置
}
};
/**
* 从事件时间创建标记配置
*
* @param eventTime 事件时间字符串ISO 格式)
* @param label 标记标签(可选,默认为"事件发生"
* @param color 标记颜色(可选,使用默认颜色)
* @returns EventMarker 事件标记配置
*/
export const createEventMarkerFromTime = (
eventTime: string,
label: string = '事件发生',
color: string = EVENT_MARKER_CONFIG.defaultColor
): EventMarker => {
const timestamp = dayjs(eventTime).valueOf();
return {
id: `event-${timestamp}`,
timestamp,
label,
position: EVENT_MARKER_CONFIG.defaultPosition,
color,
icon: EVENT_MARKER_CONFIG.defaultIcon,
draggable: false,
};
};
/**
* 批量创建事件标记 Overlays
*
* @param markers 事件标记配置数组
* @param data K线数据
* @returns OverlayCreate[] Overlay 配置数组
*/
export const createEventMarkerOverlays = (
markers: EventMarker[],
data: KLineDataPoint[]
): OverlayCreate[] => {
if (!markers || markers.length === 0) {
return [];
}
const overlays: OverlayCreate[] = [];
markers.forEach((marker) => {
const overlay = createEventMarkerOverlay(marker, data);
if (overlay) {
overlays.push(overlay);
}
});
logger.debug('eventMarkerUtils', 'createEventMarkerOverlays', '批量创建事件标记', {
totalMarkers: markers.length,
createdOverlays: overlays.length,
});
return overlays;
};
/**
* 移除事件标记
*
* @param chart KLineChart 实例
* @param markerId 标记 ID
*/
export const removeEventMarker = (chart: any, markerId: string): void => {
try {
chart.removeOverlay(markerId);
logger.debug('eventMarkerUtils', 'removeEventMarker', '移除事件标记', { markerId });
} catch (error) {
logger.error('eventMarkerUtils', 'removeEventMarker', error as Error, { markerId });
}
};
/**
* 移除所有事件标记
*
* @param chart KLineChart 实例
*/
export const removeAllEventMarkers = (chart: any): void => {
try {
// KLineChart 10.0 API: removeOverlay() 不传参数时移除所有 overlays
chart.removeOverlay();
logger.debug('eventMarkerUtils', 'removeAllEventMarkers', '移除所有事件标记');
} catch (error) {
logger.error('eventMarkerUtils', 'removeAllEventMarkers', error as Error);
}
};
/**
* 更新事件标记
*
* @param chart KLineChart 实例
* @param markerId 标记 ID
* @param updates 更新内容(部分字段)
*/
export const updateEventMarker = (
chart: any,
markerId: string,
updates: Partial<EventMarker>
): void => {
try {
// 先移除旧标记
removeEventMarker(chart, markerId);
// 重新创建标记KLineChart 10.0 不支持直接更新 overlay
// 注意:需要在调用方重新创建并添加 overlay
logger.debug('eventMarkerUtils', 'updateEventMarker', '更新事件标记', {
markerId,
updates,
});
} catch (error) {
logger.error('eventMarkerUtils', 'updateEventMarker', error as Error, { markerId });
}
};
/**
* 高亮事件标记(改变样式)
*
* @param chart KLineChart 实例
* @param markerId 标记 ID
* @param highlight 是否高亮
*/
export const highlightEventMarker = (
chart: any,
markerId: string,
highlight: boolean
): void => {
try {
// KLineChart 10.0: 通过 overrideOverlay 修改样式
chart.overrideOverlay({
id: markerId,
styles: {
point: {
activeRadius: highlight ? 10 : EVENT_MARKER_CONFIG.size.point,
activeBorderSize: highlight ? 3 : 2,
},
},
});
logger.debug('eventMarkerUtils', 'highlightEventMarker', '高亮事件标记', {
markerId,
highlight,
});
} catch (error) {
logger.error('eventMarkerUtils', 'highlightEventMarker', error as Error, { markerId });
}
};
/**
* 格式化事件标记标签
*
* @param eventTitle 事件标题
* @param maxLength 最大长度(默认 10
* @returns string 格式化后的标签
*/
export const formatEventMarkerLabel = (eventTitle: string, maxLength: number = 10): string => {
if (!eventTitle) {
return '事件';
}
if (eventTitle.length <= maxLength) {
return eventTitle;
}
return `${eventTitle.substring(0, maxLength)}...`;
};
/**
* 判断事件时间是否在数据范围内
*
* @param eventTime 事件时间戳
* @param data K线数据
* @returns boolean 是否在范围内
*/
export const isEventTimeInDataRange = (
eventTime: number,
data: KLineDataPoint[]
): boolean => {
if (!data || data.length === 0) {
return false;
}
const timestamps = data.map((item) => item.timestamp);
const minTime = Math.min(...timestamps);
const maxTime = Math.max(...timestamps);
return eventTime >= minTime && eventTime <= maxTime;
};