459 lines
11 KiB
TypeScript
459 lines
11 KiB
TypeScript
/**
|
||
* KLineChart 主题配置
|
||
*
|
||
* 适配 klinecharts@10.0.0-beta1
|
||
* 参考: https://github.com/klinecharts/KLineChart/blob/main/docs/en-US/guide/styles.md
|
||
*/
|
||
|
||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||
// ⚠️ 使用 any 类型绕过 KLineChart 类型定义的限制(beta 版本类型不完整)
|
||
import type { DeepPartial, Styles } from 'klinecharts';
|
||
|
||
/**
|
||
* 图表主题颜色配置
|
||
*/
|
||
export const CHART_COLORS = {
|
||
// 涨跌颜色(中国市场习惯:红涨绿跌)
|
||
up: '#ef5350', // 上涨红色
|
||
down: '#26a69a', // 下跌绿色
|
||
neutral: '#888888', // 平盘灰色
|
||
|
||
// 主题色(继承自 Argon Dashboard)
|
||
primary: '#1b3bbb', // Navy 500
|
||
secondary: '#728fea', // Navy 300
|
||
background: '#ffffff',
|
||
backgroundDark: '#1B254B',
|
||
|
||
// 文本颜色
|
||
text: '#333333',
|
||
textSecondary: '#888888',
|
||
textDark: '#ffffff',
|
||
|
||
// 网格颜色
|
||
grid: '#e0e0e0',
|
||
gridDark: '#2d3748',
|
||
|
||
// 边框颜色
|
||
border: '#e0e0e0',
|
||
borderDark: '#2d3748',
|
||
|
||
// 事件标记颜色
|
||
eventMarker: '#ff9800',
|
||
eventMarkerText: '#ffffff',
|
||
};
|
||
|
||
/**
|
||
* 浅色主题配置(默认)
|
||
*/
|
||
export const lightTheme: any = {
|
||
candle: {
|
||
type: 'candle_solid', // 实心蜡烛图
|
||
bar: {
|
||
upColor: CHART_COLORS.up,
|
||
downColor: CHART_COLORS.down,
|
||
noChangeColor: CHART_COLORS.neutral,
|
||
},
|
||
priceMark: {
|
||
show: true,
|
||
high: {
|
||
color: CHART_COLORS.up,
|
||
},
|
||
low: {
|
||
color: CHART_COLORS.down,
|
||
},
|
||
},
|
||
tooltip: {
|
||
showRule: 'always',
|
||
showType: 'standard',
|
||
// labels: ['时间: ', '开: ', '收: ', '高: ', '低: ', '成交量: '], // ❌ KLineChart 类型不支持自定义 labels
|
||
text: {
|
||
size: 12,
|
||
family: 'Helvetica, Arial, sans-serif',
|
||
weight: 'normal',
|
||
color: CHART_COLORS.text,
|
||
},
|
||
},
|
||
},
|
||
indicator: {
|
||
tooltip: {
|
||
showRule: 'always',
|
||
showType: 'standard',
|
||
text: {
|
||
size: 12,
|
||
family: 'Helvetica, Arial, sans-serif',
|
||
weight: 'normal',
|
||
color: CHART_COLORS.text,
|
||
},
|
||
},
|
||
},
|
||
xAxis: {
|
||
axisLine: {
|
||
show: true,
|
||
color: CHART_COLORS.border,
|
||
},
|
||
tickLine: {
|
||
show: true,
|
||
length: 3,
|
||
color: CHART_COLORS.border,
|
||
},
|
||
tickText: {
|
||
show: true,
|
||
color: CHART_COLORS.textSecondary,
|
||
family: 'Helvetica, Arial, sans-serif',
|
||
weight: 'normal',
|
||
size: 12,
|
||
},
|
||
},
|
||
yAxis: {
|
||
axisLine: {
|
||
show: true,
|
||
color: CHART_COLORS.border,
|
||
},
|
||
tickLine: {
|
||
show: true,
|
||
length: 3,
|
||
color: CHART_COLORS.border,
|
||
},
|
||
tickText: {
|
||
show: true,
|
||
color: CHART_COLORS.textSecondary,
|
||
family: 'Helvetica, Arial, sans-serif',
|
||
weight: 'normal',
|
||
size: 12,
|
||
},
|
||
type: 'normal', // 'normal' | 'percentage' | 'log'
|
||
},
|
||
grid: {
|
||
show: true,
|
||
horizontal: {
|
||
show: true,
|
||
size: 1,
|
||
color: CHART_COLORS.grid,
|
||
style: 'dashed',
|
||
},
|
||
vertical: {
|
||
show: false, // 垂直网格线通常关闭,避免过于密集
|
||
},
|
||
},
|
||
separator: {
|
||
size: 1,
|
||
color: CHART_COLORS.border,
|
||
},
|
||
crosshair: {
|
||
show: true,
|
||
horizontal: {
|
||
show: true,
|
||
line: {
|
||
show: true,
|
||
style: 'dashed',
|
||
dashedValue: [4, 2], // ✅ 修复: 使用 dashedValue 而非 dashValue
|
||
size: 1,
|
||
color: CHART_COLORS.primary,
|
||
},
|
||
text: {
|
||
show: true,
|
||
color: CHART_COLORS.textDark,
|
||
size: 12,
|
||
family: 'Helvetica, Arial, sans-serif',
|
||
weight: 'normal',
|
||
backgroundColor: CHART_COLORS.primary,
|
||
},
|
||
},
|
||
vertical: {
|
||
show: true,
|
||
line: {
|
||
show: true,
|
||
style: 'dashed',
|
||
dashedValue: [4, 2], // ✅ 修复: 使用 dashedValue 而非 dashValue
|
||
size: 1,
|
||
color: CHART_COLORS.primary,
|
||
},
|
||
text: {
|
||
show: true,
|
||
color: CHART_COLORS.textDark,
|
||
size: 12,
|
||
family: 'Helvetica, Arial, sans-serif',
|
||
weight: 'normal',
|
||
backgroundColor: CHART_COLORS.primary,
|
||
},
|
||
},
|
||
},
|
||
overlay: {
|
||
// 事件标记覆盖层样式
|
||
point: {
|
||
color: CHART_COLORS.eventMarker,
|
||
borderColor: CHART_COLORS.eventMarker,
|
||
borderSize: 1,
|
||
radius: 5,
|
||
activeColor: CHART_COLORS.eventMarker,
|
||
activeBorderColor: CHART_COLORS.eventMarker,
|
||
activeBorderSize: 2,
|
||
activeRadius: 6,
|
||
},
|
||
line: {
|
||
style: 'solid',
|
||
smooth: false,
|
||
color: CHART_COLORS.eventMarker,
|
||
size: 1,
|
||
dashedValue: [2, 2],
|
||
},
|
||
text: {
|
||
style: 'fill',
|
||
color: CHART_COLORS.eventMarkerText,
|
||
size: 12,
|
||
family: 'Helvetica, Arial, sans-serif',
|
||
weight: 'normal',
|
||
offset: [0, 0],
|
||
},
|
||
rect: {
|
||
style: 'fill',
|
||
color: CHART_COLORS.eventMarker,
|
||
borderColor: CHART_COLORS.eventMarker,
|
||
borderSize: 1,
|
||
borderRadius: 4,
|
||
borderStyle: 'solid',
|
||
borderDashedValue: [2, 2],
|
||
},
|
||
},
|
||
};
|
||
|
||
/**
|
||
* 深色主题配置
|
||
*/
|
||
export const darkTheme: any = {
|
||
...lightTheme,
|
||
candle: {
|
||
...lightTheme.candle,
|
||
tooltip: {
|
||
...lightTheme.candle?.tooltip,
|
||
text: {
|
||
...lightTheme.candle?.tooltip?.text,
|
||
color: CHART_COLORS.textDark,
|
||
},
|
||
},
|
||
},
|
||
indicator: {
|
||
...lightTheme.indicator,
|
||
tooltip: {
|
||
...lightTheme.indicator?.tooltip,
|
||
text: {
|
||
...lightTheme.indicator?.tooltip?.text,
|
||
color: CHART_COLORS.textDark,
|
||
},
|
||
},
|
||
},
|
||
xAxis: {
|
||
...lightTheme.xAxis,
|
||
axisLine: {
|
||
show: true,
|
||
color: CHART_COLORS.borderDark,
|
||
},
|
||
tickLine: {
|
||
show: true,
|
||
length: 3,
|
||
color: CHART_COLORS.borderDark,
|
||
},
|
||
tickText: {
|
||
...lightTheme.xAxis?.tickText,
|
||
color: CHART_COLORS.textDark,
|
||
},
|
||
},
|
||
yAxis: {
|
||
...lightTheme.yAxis,
|
||
axisLine: {
|
||
show: true,
|
||
color: CHART_COLORS.borderDark,
|
||
},
|
||
tickLine: {
|
||
show: true,
|
||
length: 3,
|
||
color: CHART_COLORS.borderDark,
|
||
},
|
||
tickText: {
|
||
...lightTheme.yAxis?.tickText,
|
||
color: CHART_COLORS.textDark,
|
||
},
|
||
},
|
||
grid: {
|
||
show: true,
|
||
horizontal: {
|
||
show: true,
|
||
size: 1,
|
||
color: CHART_COLORS.gridDark,
|
||
style: 'dashed',
|
||
},
|
||
vertical: {
|
||
show: false,
|
||
},
|
||
},
|
||
separator: {
|
||
size: 1,
|
||
color: CHART_COLORS.borderDark,
|
||
},
|
||
};
|
||
|
||
/**
|
||
* 分时图专用主题配置
|
||
* 特点:面积图样式、均价线、百分比Y轴
|
||
*/
|
||
export const timelineTheme: any = {
|
||
...lightTheme,
|
||
candle: {
|
||
type: 'area', // ✅ 面积图模式(分时线)
|
||
area: {
|
||
lineSize: 2,
|
||
lineColor: CHART_COLORS.up, // 默认红色,实际会根据涨跌动态调整
|
||
value: 'close',
|
||
backgroundColor: [
|
||
{
|
||
offset: 0,
|
||
color: 'rgba(239, 83, 80, 0.2)', // 红色半透明渐变(顶部)
|
||
},
|
||
{
|
||
offset: 1,
|
||
color: 'rgba(239, 83, 80, 0.01)', // 红色几乎透明(底部)
|
||
},
|
||
],
|
||
},
|
||
priceMark: {
|
||
show: true,
|
||
high: {
|
||
show: false, // 分时图不显示最高最低价标记
|
||
},
|
||
low: {
|
||
show: false,
|
||
},
|
||
last: {
|
||
show: true,
|
||
upColor: CHART_COLORS.up,
|
||
downColor: CHART_COLORS.down,
|
||
noChangeColor: CHART_COLORS.neutral,
|
||
line: {
|
||
show: true,
|
||
style: 'dashed',
|
||
dashedValue: [4, 2], // ✅ 修复: 使用 dashedValue 而非 dashValue
|
||
size: 1,
|
||
},
|
||
text: {
|
||
show: true,
|
||
size: 12,
|
||
paddingLeft: 4,
|
||
paddingTop: 2,
|
||
paddingRight: 4,
|
||
paddingBottom: 2,
|
||
borderRadius: 2,
|
||
},
|
||
},
|
||
},
|
||
tooltip: {
|
||
showRule: 'always',
|
||
showType: 'standard',
|
||
// ❌ KLineChart 类型不支持自定义 labels 和 formatter(需要在运行时通过 API 设置)
|
||
// labels: ['时间: ', '现价: ', '涨跌: ', '均价: ', '昨收: ', '成交量: '],
|
||
// formatter: (data: any, indicator: any) => { ... },
|
||
text: {
|
||
size: 12,
|
||
family: 'Helvetica, Arial, sans-serif',
|
||
weight: 'normal',
|
||
color: CHART_COLORS.text,
|
||
},
|
||
},
|
||
},
|
||
yAxis: {
|
||
...lightTheme.yAxis,
|
||
type: 'percentage', // ✅ 百分比模式
|
||
position: 'left', // Y轴在左侧
|
||
inside: false,
|
||
reverse: false,
|
||
tickText: {
|
||
...lightTheme.yAxis?.tickText,
|
||
// ❌ KLineChart 类型不支持自定义 formatter(需要在运行时通过 API 设置)
|
||
// formatter: (value: any) => {
|
||
// const percent = (value * 100).toFixed(2);
|
||
// if (Math.abs(value) < 0.0001) return '0.00%';
|
||
// return value > 0 ? `+${percent}%` : `${percent}%`;
|
||
// },
|
||
},
|
||
},
|
||
grid: {
|
||
show: true,
|
||
horizontal: {
|
||
show: true,
|
||
size: 1,
|
||
color: CHART_COLORS.grid,
|
||
style: 'solid', // 分时图使用实线网格
|
||
},
|
||
vertical: {
|
||
show: false,
|
||
},
|
||
},
|
||
};
|
||
|
||
/**
|
||
* 分时图深色主题
|
||
*/
|
||
export const timelineThemeDark: any = {
|
||
...timelineTheme,
|
||
...darkTheme,
|
||
candle: {
|
||
...timelineTheme.candle,
|
||
tooltip: {
|
||
...timelineTheme.candle?.tooltip,
|
||
text: {
|
||
...timelineTheme.candle?.tooltip?.text,
|
||
color: CHART_COLORS.textDark,
|
||
},
|
||
},
|
||
},
|
||
grid: {
|
||
...timelineTheme.grid,
|
||
horizontal: {
|
||
...timelineTheme.grid?.horizontal,
|
||
color: CHART_COLORS.gridDark,
|
||
},
|
||
},
|
||
};
|
||
|
||
/**
|
||
* 获取主题配置(根据 Chakra UI colorMode)
|
||
*/
|
||
export const getTheme = (colorMode: 'light' | 'dark' = 'light'): any => {
|
||
return colorMode === 'dark' ? darkTheme : lightTheme;
|
||
};
|
||
|
||
/**
|
||
* 获取分时图主题配置
|
||
*/
|
||
export const getTimelineTheme = (colorMode: 'light' | 'dark' = 'light'): any => {
|
||
const baseTheme = colorMode === 'dark' ? timelineThemeDark : timelineTheme;
|
||
|
||
// ✅ 添加成交量指标样式(蓝色渐变柱状图)+ 成交量单位格式化
|
||
return {
|
||
...baseTheme,
|
||
indicator: {
|
||
...baseTheme.indicator,
|
||
bars: [
|
||
{
|
||
upColor: 'rgba(59, 130, 246, 0.6)', // 蓝色(涨)
|
||
downColor: 'rgba(59, 130, 246, 0.6)', // 蓝色(跌)- 分时图成交量统一蓝色
|
||
noChangeColor: 'rgba(59, 130, 246, 0.6)',
|
||
}
|
||
],
|
||
// ❌ KLineChart 类型不支持自定义 formatter(需要在运行时通过 API 设置)
|
||
tooltip: {
|
||
...baseTheme.indicator?.tooltip,
|
||
// formatter: (params: any) => {
|
||
// if (params.name === 'VOL' && params.calcParamsText) {
|
||
// const volume = params.calcParamsText.match(/\d+/)?.[0];
|
||
// if (volume) {
|
||
// const hands = Math.floor(Number(volume) / 100);
|
||
// return `成交量: ${hands.toLocaleString()}手`;
|
||
// }
|
||
// }
|
||
// return params.calcParamsText || '';
|
||
// },
|
||
},
|
||
},
|
||
};
|
||
};
|