fix: restore analysis chart rendering

This commit is contained in:
OoO
2026-05-18 14:24:28 +08:00
parent 49f6b3ebd9
commit a6059b5377
6 changed files with 302 additions and 46 deletions

View File

@@ -91,6 +91,18 @@
: v.toLocaleString();
}
function formatMetric(v, label = '') {
if (typeof v !== 'number' || !Number.isFinite(v)) return v;
const name = String(label || '').toLowerCase();
if (name.includes('%') || name.includes('率') || name.includes('yoy') || name.includes('mom')) {
return `${v >= 0 ? '+' : ''}${v.toFixed(1)}%`;
}
if (name.includes('$') || name.includes('金額') || name.includes('營收') || name.includes('業績') || name.includes('毛利') || name.includes('客單') || name.includes('單價')) {
return `$${Math.round(v).toLocaleString()}`;
}
return formatNumber(v);
}
function tuneDataset(ds, idx, type) {
if (!ds || typeof ds !== 'object') return;
const palette = readPalette();
@@ -118,6 +130,8 @@
ds.pointBackgroundColor = ds.pointBackgroundColor || color;
ds.pointBorderColor = ds.pointBorderColor || theme.elevated;
ds.tension = ds.tension ?? (t === 'line' ? 0.32 : undefined);
ds.hoverBorderWidth = ds.hoverBorderWidth || (t === 'line' ? 3 : 1);
ds.maxBarThickness = ds.maxBarThickness || (t === 'bar' ? 34 : undefined);
}
function tuneChartConfig(config) {
@@ -136,6 +150,8 @@
const plugins = (config.options.plugins = config.options.plugins || {});
plugins.legend = {
...(plugins.legend || {}),
position: (plugins.legend || {}).position || (isCompact() ? 'bottom' : 'top'),
align: (plugins.legend || {}).align || 'start',
labels: {
color: theme.muted,
boxWidth: 10,
@@ -165,7 +181,7 @@
typeof ctx.parsed === 'object'
? ctx.parsed.y ?? ctx.parsed.x ?? ctx.raw
: ctx.parsed;
return `${lbl}${formatNumber(v)}`;
return `${lbl}${formatMetric(v, ctx.dataset && ctx.dataset.label)}`;
},
...((plugins.tooltip || {}).callbacks || {})
}
@@ -185,6 +201,7 @@
color: theme.muted,
maxRotation: isCompact() ? 0 : 30,
autoSkip: isCompact(),
maxTicksLimit: isCompact() ? 5 : 8,
font: {
family: theme.mono,
size: isCompact() ? 10 : 11,
@@ -252,6 +269,28 @@
if (!window.echarts || window.echarts.__ewooocThemed) return;
const ec = window.echarts;
const nativeInit = ec.init.bind(ec);
const tuneAxis = axis => {
if (!axis || typeof axis !== 'object') return axis;
return {
axisLine: { lineStyle: { color: theme.faint }, ...(axis.axisLine || {}) },
axisTick: { lineStyle: { color: theme.faint }, ...(axis.axisTick || {}) },
axisLabel: {
color: theme.muted,
fontFamily: theme.mono,
fontSize: isCompact() ? 10 : 11,
...(axis.axisLabel || {})
},
splitLine: {
lineStyle: { color: theme.faint, type: 'dashed' },
...(axis.splitLine || {})
},
...axis
};
};
const tuneAxisList = axes => {
if (Array.isArray(axes)) return axes.map(tuneAxis);
return tuneAxis(axes);
};
ec.init = function (dom, themeName, opts) {
const chart = nativeInit(dom, themeName || null, opts);
const setOpt = chart.setOption.bind(chart);
@@ -261,7 +300,33 @@
...opt,
color: palette,
backgroundColor: 'transparent',
textStyle: { color: theme.ink, fontFamily: theme.font }
textStyle: { color: theme.ink, fontFamily: theme.font, ...(opt.textStyle || {}) },
tooltip: {
trigger: 'axis',
confine: true,
backgroundColor: theme.elevated,
borderColor: theme.faint,
borderWidth: 1,
textStyle: { color: theme.ink, fontFamily: theme.font },
axisPointer: { type: 'cross', label: { backgroundColor: '#6f6256' } },
...(opt.tooltip || {})
},
legend: {
bottom: 0,
type: 'scroll',
textStyle: { color: theme.muted, fontFamily: theme.mono, fontSize: 11 },
...(opt.legend || {})
},
grid: {
containLabel: true,
left: isCompact() ? 12 : 28,
right: isCompact() ? 12 : 24,
top: isCompact() ? 24 : 36,
bottom: isCompact() ? 34 : 40,
...(opt.grid || {})
},
xAxis: tuneAxisList(opt.xAxis),
yAxis: tuneAxisList(opt.yAxis)
};
return setOpt(tuned, notMerge, lazy);
};