Files
2026-08-23 21:23:41 +08:00

248 lines
6.1 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 方木风格主题 —— 统一的配色、字体、坐标轴、水印
* 所有图表共用这一份配置,保证视觉一致性
*/
export const WATERMARK_TEXT = '@思考的Joey';
// 字体名用单引号:SVG 渲染时字符串会被塞进 style="..." 属性,
// 用双引号会提前闭合属性、生成非法 SVG(canvas 渲染看不出来,导出 SVG 时才炸)
export const FONT = "'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', sans-serif";
/** 调色板:低饱和、克制,接近财经媒体常用的莫兰迪系 */
export const PALETTE = {
brown: '#6b4a2b',
sand: '#d5c7b4',
coral: '#ec7b73',
green: '#2f8a5b',
orange: '#d98324',
purple: '#9b4fa8',
blue: '#3b6ea5',
navy: '#1f4e79',
red: '#b8342a',
sage: '#8aab7e',
ink: '#333333',
grey: '#8c8c8c',
gridLine: '#e8e8e8',
axisLine: '#d0d0d0',
};
/** 坐标轴通用样式:只留浅色横向网格,去掉轴线的存在感 */
export const axisCommon = {
axisLine: { show: false },
axisTick: { show: false },
axisLabel: {
color: PALETTE.grey,
fontSize: 13,
fontFamily: FONT,
},
splitLine: {
show: true,
lineStyle: { color: PALETTE.gridLine, width: 1 },
},
};
/** X 轴:显示轴线,不显示网格 */
export const categoryAxis = {
type: 'category',
boundaryGap: true,
axisLine: { show: true, lineStyle: { color: PALETTE.axisLine } },
axisTick: { show: false },
axisLabel: {
color: PALETTE.ink,
fontSize: 14,
fontFamily: FONT,
margin: 14,
},
splitLine: { show: false },
};
/**
* 期间标签简写:2026H1 → 26H12026H2 → 26全年。
* CSV 里保留四位年份(无歧义、便于核对),只在轴上简化显示。
* 非 20xx 开头的标签(如"年初")原样返回。
*/
export function shortPeriod(label) {
return String(label)
.replace(/^20(\d{2})H2$/, '$1全年') // H2 = 全年累计
.replace(/^20(\d{2})/, '$1'); // 其余去掉世纪前缀
}
/** X 轴(期间):在 categoryAxis 基础上套用简写 */
export const periodAxis = {
...categoryAxis,
axisLabel: {
...categoryAxis.axisLabel,
formatter: shortPeriod,
},
};
export const titleStyle = {
left: 'center',
top: 18,
textStyle: {
color: PALETTE.ink,
fontSize: 19,
fontWeight: 'bold',
fontFamily: FONT,
},
subtextStyle: {
color: PALETTE.grey,
fontSize: 13,
fontFamily: FONT,
lineHeight: 20,
},
};
/**
* 标准布局常量
* 经多图表调试验证的标题-图例-绘图区间距,确保视觉呼吸感一致
*/
export const LAYOUT = {
titleTop: 18, // 标题顶部边距(已内置在 titleStyle 中)
legendTop: 85, // 图例顶部边距(副标题到图例约 25px)
gridTop: 145, // 绘图区顶部边距(图例到绘图区约 60px)
};
export const legendStyle = {
top: 62, // ⚠️ 已废弃:请改用 LAYOUT.legendTop 或 standardLegend()
itemGap: 26,
itemWidth: 18,
// ❌ 删除 icon: 'roundRect'
// 原因:它会覆盖 ECharts 按系列类型自动生成的图例样式(折线图的"短线+空心圆"
// 现在让每个图表按需设置,或使用 standardLegend() 获得正确默认值
textStyle: {
color: PALETTE.ink,
fontSize: 14,
fontFamily: FONT,
},
};
/** 折线标注:带白底描边,避免压在网格线上看不清 */
export function labelStyle(color, position = 'top') {
return {
show: true,
position,
color,
fontSize: 13,
fontFamily: FONT,
fontWeight: 500,
distance: 8,
};
}
/**
* 标准图例配置
* 不设置 icon,让 ECharts 按系列类型自动生成(折线→短线+空心圆,柱状→矩形)
* 统一使用 LAYOUT.legendTop 保证间距一致
*/
export function standardLegend(data, options = {}) {
return {
top: LAYOUT.legendTop,
itemGap: options.itemGap ?? 28,
textStyle: {
color: PALETTE.ink,
fontSize: 14,
fontFamily: FONT,
},
data,
...options, // 允许覆盖任何字段
};
}
/**
* 空心圆点折线系列 —— 标准配置
* 白心 + 彩色边框,视觉统一(symbolSize: 9, borderWidth: 2.5
*/
export function lineSeries({ name, data, color, labelPosition = 'top', formatter, width = 3 }) {
return {
name,
type: 'line',
data,
smooth: false,
symbol: 'circle',
symbolSize: 9,
itemStyle: {
color: '#fff',
borderColor: color,
borderWidth: 2.5,
},
lineStyle: { color, width },
emphasis: { focus: 'series' },
label: {
...labelStyle(color, labelPosition),
formatter,
},
};
}
/**
* 柱状图系列标准配置
* 强制显示所有标签(overflow: 'none'),避免小柱子标签被 ECharts 自动隐藏
*/
export function barSeries({ name, data, color, barWidth, formatter, ...rest }) {
return {
name,
type: 'bar',
data,
barWidth,
itemStyle: { color },
label: {
show: true,
position: 'top',
fontFamily: FONT,
fontSize: 11,
color: PALETTE.ink,
formatter: formatter ?? ((p) => p.value),
overflow: 'none', // 关键:强制显示所有标签
},
...rest,
};
}
/** 底部注释 */
export function footnote(text) {
return {
type: 'text',
left: 'center',
bottom: 12,
style: {
text,
fill: PALETTE.grey,
fontSize: 12,
fontFamily: FONT,
lineHeight: 18,
},
};
}
/**
* 平铺水印。ECharts 没有原生水印,用 graphic 数组铺满画布。
* 旋转 -20°,透明度压到 0.08,不干扰读数。
*/
export function watermarks(width, height, text = WATERMARK_TEXT) {
const items = [];
const stepX = 300;
const stepY = 200;
for (let x = -100; x < width + stepX; x += stepX) {
for (let y = 40; y < height + stepY; y += stepY) {
const offset = (Math.floor(y / stepY) % 2) * (stepX / 2);
items.push({
type: 'text',
left: x + offset,
top: y,
silent: true,
rotation: 0.35,
style: {
text,
fill: 'rgba(120, 100, 80, 0.10)',
fontSize: 20,
fontWeight: 'bold',
fontFamily: FONT,
},
});
}
}
return items;
}