feat: StockChartModal.tsx 替换 KLine 实现

This commit is contained in:
zdl
2025-11-24 13:59:29 +08:00
parent b4dcbd1db9
commit 2f125a9207
10 changed files with 768 additions and 630 deletions

View File

@@ -290,9 +290,193 @@ export const darkTheme: DeepPartial<Styles> = {
},
};
/**
* 分时图专用主题配置
* 特点面积图样式、均价线、百分比Y轴
*/
export const timelineTheme: DeepPartial<Styles> = {
...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',
dashValue: [4, 2],
size: 1,
},
text: {
show: true,
size: 12,
paddingLeft: 4,
paddingTop: 2,
paddingRight: 4,
paddingBottom: 2,
borderRadius: 2,
},
},
},
tooltip: {
showRule: 'always',
showType: 'standard',
// ✅ 自定义 Tooltip 标签和格式化
labels: ['时间: ', '现价: ', '涨跌: ', '均价: ', '昨收: ', '成交量: '],
// 自定义格式化函数(如果 KLineChart 支持)
formatter: (data: any, indicator: any) => {
if (!data) return [];
const { timestamp, close, volume, prev_close } = data;
const time = new Date(timestamp);
const timeStr = `${String(time.getHours()).padStart(2, '0')}:${String(time.getMinutes()).padStart(2, '0')}`;
// 计算涨跌幅
const changePercent = prev_close ? ((close - prev_close) / prev_close * 100).toFixed(2) : '0.00';
const changeValue = prev_close ? (close - prev_close).toFixed(2) : '0.00';
// 成交量转换为手1手 = 100股
const volumeHands = Math.floor(volume / 100);
return [
{ title: '时间', value: timeStr },
{ title: '现价', value: `¥${close?.toFixed(2) || '--'}` },
{ title: '涨跌', value: `${changeValue} (${changePercent}%)` },
{ title: '昨收', value: `¥${prev_close?.toFixed(2) || '--'}` },
{ title: '成交量', value: `${volumeHands.toLocaleString()}` },
];
},
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,
// ✅ 自定义 Y 轴格式化:显示百分比(如 "+2.50%", "-1.20%", "0.00%"
formatter: (value: any) => {
const percent = (value * 100).toFixed(2);
// 处理 0 值:显示 "0.00%" 而非 "-0.00%" 或 "+0.00%"
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: DeepPartial<Styles> = {
...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'): DeepPartial<Styles> => {
return colorMode === 'dark' ? darkTheme : lightTheme;
};
/**
* 获取分时图主题配置
*/
export const getTimelineTheme = (colorMode: 'light' | 'dark' = 'light'): DeepPartial<Styles> => {
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)',
}
],
// ✅ 自定义 Tooltip 格式化(显示完整信息)
tooltip: {
...baseTheme.indicator?.tooltip,
// 格式化成交量数值:显示为"手"1手 = 100股
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 || '';
},
},
},
};
};