feat: 优化行情展示代码
This commit is contained in:
@@ -34,74 +34,163 @@
|
||||
'2025-01-08': [112, 118, 110, 121],
|
||||
'2025-01-09': [118, 121, 117, 123],
|
||||
'2025-01-10': [121, 119, 118, 122],
|
||||
};
|
||||
|
||||
// Derive arrays for the chart from the KV map
|
||||
const dates = Object.keys(data).sort();
|
||||
// For custom rendering, augment with category index: [idx, open, close, low, high]
|
||||
const ohlcData = dates.map((d, i) => [i, ...data[d]]);
|
||||
|
||||
// Styling and helpers
|
||||
const UP_COLOR = '#000000'; // up: black
|
||||
const DOWN_COLOR = '#9e9e9e'; // down: gray
|
||||
|
||||
function renderOHLCMinimal(params, api) {
|
||||
var idx = api.value(0);
|
||||
var open = api.value(1);
|
||||
var close = api.value(2);
|
||||
var low = api.value(3);
|
||||
var high = api.value(4);
|
||||
var up = close >= open;
|
||||
|
||||
var x = api.coord([idx, 0])[0];
|
||||
var highPoint = api.coord([idx, high]);
|
||||
var lowPoint = api.coord([idx, low]);
|
||||
var openPoint = api.coord([idx, open]);
|
||||
var closePoint = api.coord([idx, close]);
|
||||
|
||||
var band = api.size([1, 0])[0];
|
||||
// Keep dash length unchanged from the previous baseline
|
||||
var tick = Math.max(4, Math.min(10, band * 0.4));
|
||||
var color = up ? UP_COLOR : DOWN_COLOR;
|
||||
|
||||
return {
|
||||
type: 'group',
|
||||
children: [
|
||||
{
|
||||
type: 'line',
|
||||
shape: { x1: x, y1: highPoint[1], x2: x, y2: lowPoint[1] },
|
||||
style: { stroke: color, lineWidth: 1, opacity: 0.9 },
|
||||
},
|
||||
{
|
||||
type: 'line',
|
||||
shape: { x1: x - tick, y1: openPoint[1], x2: x, y2: openPoint[1] },
|
||||
style: { stroke: color, lineWidth: 1.2, opacity: 0.95 },
|
||||
},
|
||||
{
|
||||
type: 'line',
|
||||
shape: { x1: x, y1: closePoint[1], x2: x + tick, y2: closePoint[1] },
|
||||
style: { stroke: color, lineWidth: 1.6, opacity: 0.95 },
|
||||
},
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
function formatTooltip(params) {
|
||||
var p = Array.isArray(params) ? params[0] : params;
|
||||
var v = p.data; // [idx, open, close, low, high]
|
||||
var d = dates[v[0]];
|
||||
var o = v[1], c = v[2], l = v[3], h = v[4];
|
||||
var chg = (c - o);
|
||||
var chgPct = o ? (chg / o * 100) : 0;
|
||||
var sign = chg >= 0 ? '+' : '';
|
||||
return [
|
||||
d,
|
||||
'O: ' + o,
|
||||
'C: ' + c,
|
||||
'H: ' + h,
|
||||
'L: ' + l,
|
||||
'Chg: ' + sign + chg.toFixed(2) + ' (' + sign + chgPct.toFixed(2) + '%)'
|
||||
].join('<br/>');
|
||||
// 全局配置(颜色、尺寸、间距等),集中管理,便于统一调整
|
||||
const CONFIG = {
|
||||
colors: {up: '#000000FF', down: '#00000045'},
|
||||
grid: {left: '2%', right: '2%', top: 40, bottom: 110},
|
||||
zoom: {bottom: 16, height: 50},
|
||||
linewidth: {stem: 1.5, openTick: 1.2, closeTick: 1.6, closeLine: 1.5},
|
||||
tick: {min: 4, max: 10, scale: 0.4},
|
||||
}
|
||||
|
||||
// 通用 tooltip 格式化(按索引回读原始 O/H/L/C)
|
||||
function makeTooltipFormatter(dataMap, dates) {
|
||||
return function (params) {
|
||||
let p = Array.isArray(params) ? params[0] : params
|
||||
let idx = p.dataIndex
|
||||
let d = dates[idx]
|
||||
let ohlc = dataMap[d] || []
|
||||
let o = ohlc[0], c = ohlc[1], l = ohlc[2], h = ohlc[3]
|
||||
let chg = (c - o)
|
||||
let chgPct = o ? (chg / o * 100) : 0
|
||||
let sign = chg >= 0 ? '+' : ''
|
||||
return [
|
||||
d,
|
||||
'O: ' + o,
|
||||
'C: ' + c,
|
||||
'H: ' + h,
|
||||
'L: ' + l,
|
||||
'Chg: ' + sign + chg.toFixed(2) + ' (' + sign + chgPct.toFixed(2) + '%)',
|
||||
].join('<br/>')
|
||||
}
|
||||
}
|
||||
|
||||
// 通用基础配置构建(legend/tooltip/grid/xAxis/yAxis/dataZoom)
|
||||
function buildBaseOption(dates, series, formatter) {
|
||||
return {
|
||||
animation: false,
|
||||
legend: {show: false},
|
||||
tooltip: {trigger: 'axis', axisPointer: {type: 'cross'}, formatter},
|
||||
grid: CONFIG.grid,
|
||||
xAxis: {type: 'category', data: dates, boundaryGap: true, axisLine: {onZero: false}},
|
||||
yAxis: {scale: true},
|
||||
dataZoom: [
|
||||
{type: 'inside', xAxisIndex: 0, start: 0, end: 100},
|
||||
{
|
||||
show: true,
|
||||
type: 'slider',
|
||||
xAxisIndex: 0,
|
||||
bottom: CONFIG.zoom.bottom,
|
||||
height: CONFIG.zoom.height,
|
||||
start: 0,
|
||||
end: 100,
|
||||
},
|
||||
],
|
||||
series,
|
||||
}
|
||||
}
|
||||
|
||||
// Range Band + Close Line(高低区间带 + 收盘线):趋势与波动范围直观
|
||||
function buildRangeCloseOption(dataMap) {
|
||||
const dates = Object.keys(dataMap).sort()
|
||||
const lowArr = dates.map(d => dataMap[d][2])
|
||||
const highArr = dates.map(d => dataMap[d][3])
|
||||
const closeArr = dates.map(d => dataMap[d][1])
|
||||
const rangeArr = highArr.map((h, i) => h - lowArr[i])
|
||||
|
||||
const series = [
|
||||
{
|
||||
name: 'Low',
|
||||
type: 'line',
|
||||
data: lowArr,
|
||||
stack: 'range',
|
||||
symbol: 'none',
|
||||
lineStyle: {width: 0},
|
||||
emphasis: {disabled: true},
|
||||
},
|
||||
{
|
||||
name: 'Range',
|
||||
type: 'line',
|
||||
data: rangeArr,
|
||||
stack: 'range',
|
||||
symbol: 'none',
|
||||
lineStyle: {width: 0},
|
||||
areaStyle: {color: CONFIG.colors.down, opacity: 0.6},
|
||||
z: 2,
|
||||
},
|
||||
{
|
||||
name: 'Close',
|
||||
type: 'line',
|
||||
data: closeArr,
|
||||
symbol: 'none',
|
||||
lineStyle: {color: CONFIG.colors.up, width: CONFIG.linewidth.closeLine},
|
||||
z: 3,
|
||||
},
|
||||
]
|
||||
|
||||
return buildBaseOption(dates, series, makeTooltipFormatter(dataMap, dates))
|
||||
}
|
||||
|
||||
// Minimal OHLC(竖线 + 左/右短横):信息等价于K线但形态简洁
|
||||
function buildOHLCMinimalOption(dataMap) {
|
||||
const dates = Object.keys(dataMap).sort()
|
||||
const ohlcData = dates.map((d, i) => [i, ...dataMap[d]])
|
||||
|
||||
// 自定义渲染:竖线=高低区间;左短横=开盘;右短横=收盘;颜色=涨跌
|
||||
function renderOHLCMinimal(params, api) {
|
||||
let idx = api.value(0)
|
||||
let open = api.value(1)
|
||||
let close = api.value(2)
|
||||
let low = api.value(3)
|
||||
let high = api.value(4)
|
||||
let up = close >= open
|
||||
|
||||
let x = api.coord([idx, 0])[0]
|
||||
let highPoint = api.coord([idx, high])
|
||||
let lowPoint = api.coord([idx, low])
|
||||
let openPoint = api.coord([idx, open])
|
||||
let closePoint = api.coord([idx, close])
|
||||
|
||||
let band = api.size([1, 0])[0]
|
||||
let tick = Math.max(CONFIG.tick.min, Math.min(CONFIG.tick.max, band * CONFIG.tick.scale))
|
||||
let color = up ? CONFIG.colors.up : CONFIG.colors.down
|
||||
|
||||
return {
|
||||
type: 'group',
|
||||
children: [
|
||||
{
|
||||
type: 'line',
|
||||
shape: {x1: x, y1: highPoint[1], x2: x, y2: lowPoint[1]},
|
||||
style: {stroke: color, lineWidth: CONFIG.linewidth.stem, opacity: 0.9},
|
||||
},
|
||||
{
|
||||
type: 'line',
|
||||
shape: {x1: x - tick, y1: openPoint[1], x2: x, y2: openPoint[1]},
|
||||
style: {stroke: color, lineWidth: CONFIG.linewidth.openTick, opacity: 0.95},
|
||||
},
|
||||
{
|
||||
type: 'line',
|
||||
shape: {x1: x, y1: closePoint[1], x2: x + tick, y2: closePoint[1]},
|
||||
style: {stroke: color, lineWidth: CONFIG.linewidth.closeTick, opacity: 0.95},
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
const series = [
|
||||
{
|
||||
name: 'ohlc',
|
||||
type: 'custom',
|
||||
renderItem: renderOHLCMinimal,
|
||||
encode: {x: 0, y: [1, 2, 3, 4]},
|
||||
data: ohlcData,
|
||||
z: 10,
|
||||
},
|
||||
]
|
||||
|
||||
return buildBaseOption(dates, series, makeTooltipFormatter(dataMap, dates))
|
||||
}
|
||||
|
||||
(function () {
|
||||
@@ -112,49 +201,22 @@
|
||||
body: {
|
||||
type: 'tabs',
|
||||
tabsMode: 'vertical',
|
||||
// amis expects `tabs` to be an array. Using an object causes `.map` errors at runtime.
|
||||
tabs: [
|
||||
{
|
||||
title: '测试',
|
||||
body: {
|
||||
type: 'chart',
|
||||
height: 800,
|
||||
// ECharts candlestick example
|
||||
config: {
|
||||
animation: false,
|
||||
// Minimal style: hide legend for a cleaner look
|
||||
legend: { show: false },
|
||||
tooltip: { trigger: 'axis', axisPointer: { type: 'cross' }, formatter: formatTooltip },
|
||||
grid: { left: '10%', right: '8%', top: 50, bottom: 80 },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
// Use computed arrays directly to avoid template interpolation issues
|
||||
data: dates,
|
||||
boundaryGap: true,
|
||||
axisLine: { onZero: false },
|
||||
title: 'Charts',
|
||||
// 在一个 Tab 中展示两张图,便于同屏对比
|
||||
body: [
|
||||
{
|
||||
type: 'chart',
|
||||
height: 500,
|
||||
config: buildRangeCloseOption(data),
|
||||
},
|
||||
yAxis: {
|
||||
scale: true,
|
||||
{
|
||||
type: 'chart',
|
||||
height: 500,
|
||||
config: buildOHLCMinimalOption(data),
|
||||
},
|
||||
dataZoom: [
|
||||
// Inside zoom for wheel/gesture; default selects all
|
||||
{ type: 'inside', xAxisIndex: 0, start: 0, end: 100 },
|
||||
// Slider placed under the x-axis; default selects all
|
||||
{ show: true, type: 'slider', xAxisIndex: 0, bottom: 16, height: 26, start: 0, end: 100 },
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: 'ohlc',
|
||||
type: 'custom',
|
||||
// Minimal OHLC: neutral stem (high-low) + short dashes at open/close
|
||||
renderItem: renderOHLCMinimal,
|
||||
encode: { x: 0, y: [1, 2, 3, 4] },
|
||||
data: ohlcData,
|
||||
z: 10,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user