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

143 lines
4.4 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.
import { col } from '../csv.js';
import {
PALETTE, FONT, axisCommon, periodAxis, titleStyle, legendStyle,
footnote, watermarks, LAYOUT,
} from '../theme.js';
/**
* 核心利润及同比增速(2020H1-2026H1
* 核心利润 = 毛利 - 销售费用 - 管理费用,反映主业盈利能力去除营销和管理成本后的净额。
* 柱状图展示绝对值(亿元),折线图展示同比增速(%)。
*/
export function ppmtCoreProfitChart(rows, { width, height }) {
const periods = col(rows, 'period');
const core = col(rows, 'core_profit');
const yoy = col(rows, 'yoy');
return {
backgroundColor: '#fff',
title: {
...titleStyle,
text: '泡泡玛特核心利润及同比增速(20H1-26H1',
subtext: '核心利润 = 毛利 - 销售费用 - 管理费用,单位:亿元',
},
legend: {
top: LAYOUT.legendTop,
itemGap: 32,
textStyle: {
color: PALETTE.ink,
fontSize: 14,
fontFamily: FONT,
},
data: ['核心利润', '同比增速'],
},
grid: { left: 78, right: 128, top: LAYOUT.gridTop, bottom: 100 },
tooltip: {
trigger: 'axis',
axisPointer: { type: 'cross' },
textStyle: { fontFamily: FONT },
formatter: (params) => {
let result = `<b>${params[0].axisValue}</b><br/>`;
params.forEach((p) => {
if (p.seriesName === '核心利润') {
result += `${p.marker}${p.seriesName}: ${p.value.toFixed(1)}亿<br/>`;
} else if (p.seriesName === '同比增速' && p.value !== null) {
result += `${p.marker}${p.seriesName}: ${p.value > 0 ? '+' : ''}${p.value.toFixed(1)}%<br/>`;
}
});
return result;
},
},
xAxis: { ...periodAxis, data: periods },
yAxis: [
{
// 左轴:核心利润(亿元)
...axisCommon,
type: 'value',
name: '核心利润(亿元)',
nameLocation: 'middle',
nameGap: 48,
nameTextStyle: { color: PALETTE.grey, fontSize: 13, fontFamily: FONT },
min: 0,
axisLabel: { ...axisCommon.axisLabel, formatter: '{value}' },
},
{
// 右轴:同比增速(%
type: 'value',
name: '同比增速(%',
nameLocation: 'middle',
nameGap: 58,
nameTextStyle: { color: PALETTE.grey, fontSize: 13, fontFamily: FONT },
min: -100,
max: 500,
interval: 100,
axisLine: { show: false },
axisTick: { show: false },
splitLine: { show: false },
axisLabel: {
color: PALETTE.grey,
fontSize: 12,
fontFamily: FONT,
formatter: '{value}%',
},
},
],
series: [
{
name: '核心利润',
type: 'bar',
yAxisIndex: 0,
data: core,
barWidth: 32,
itemStyle: { color: PALETTE.navy },
label: {
show: true,
position: 'top',
fontFamily: FONT,
fontSize: 11,
color: PALETTE.ink,
formatter: (p) => p.value.toFixed(1),
// 强制显示所有标签,即使柱子很矮
overflow: 'none',
},
},
{
name: '同比增速',
type: 'line',
yAxisIndex: 1,
data: yoy,
lineStyle: { color: PALETTE.coral, width: 3.08 }, // 与拆解图毛利率线一致
// 空心圆点:白心 + 彩色描边。图例会自动继承这个样式,
// 渲染成「短线 + 空心圆」
itemStyle: {
color: '#fff',
borderColor: PALETTE.coral,
borderWidth: 2.5, // 与 theme.js lineSeries 一致
},
symbol: 'circle',
symbolSize: 9, // 与 theme.js lineSeries 一致
showSymbol: true,
label: {
show: true,
position: 'top',
fontFamily: FONT,
fontSize: 11,
color: PALETTE.coral,
fontWeight: '600',
formatter: (p) => {
if (p.value === null || p.value === '') return '';
return `${p.value > 0 ? '+' : ''}${p.value.toFixed(1)}%`;
},
},
},
],
graphic: [
...watermarks(width, height),
footnote(
'核心利润反映主业盈利能力(扣除营销与管理成本);' +
'2025H1/H2同比增速受益于费用率优化和规模效应 | @思考的Joey'
),
],
};
}