Files
2026-09-10 17:44:09 +08:00

131 lines
4.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.
import { col } from '../csv.js';
import {
PALETTE, FONT, axisCommon, standalonePeriodAxis, titleStyle, legendStyle,
financialViewLabel, niceAxisMax, fanTinyLabelOffsets, stackedSharePoint,
lineSeries, footnote, watermarks, LAYOUT,
} from '../theme.js';
const KEYS = ['cash_like', 'receivables', 'investment', 'operating_assets'];
const NAMES = {
cash_like: '类现金',
receivables: '应收账款',
investment: '投资资产',
operating_assets: '经营类资产',
};
const COLORS = {
cash_like: PALETTE.blue,
receivables: PALETTE.orange,
investment: PALETTE.purple,
operating_assets: PALETTE.sage,
};
// 段内标签文字色:浅色段(经营类资产灰绿)用深字,深色段用白字
const INSIDE_LABEL_COLORS = {
cash_like: '#fff',
receivables: '#fff',
investment: '#fff',
operating_assets: PALETTE.ink,
};
/**
* 泡泡玛特资产结构演变图
* 堆叠柱状图展示资产配置(每段标数值),折线图展示总资产增长
*/
export function assetsChart(rows, { width, height, view = 'all' }) {
const periods = col(rows, 'period');
const totalMax = Math.max(...rows.map((r) => r.total_assets));
// 轴上限随数据走(2025H2 总资产已 321 亿),矮段像素判定才有确定基准
const axisMax = niceAxisMax(totalMax);
const interval = axisMax % 7 === 0 ? axisMax / 7 : axisMax / 5;
// 绘图区像素高:画布高 − grid top 145 − grid bottom 90
const pxPerUnit = (height - LAYOUT.gridTop - 90) / axisMax;
// 早期年份应收/投资不足 1 亿,矮段标签外置到柱体右侧扇形排开
const rowOffsets = fanTinyLabelOffsets(rows, KEYS, pxPerUnit);
return {
backgroundColor: '#fff',
title: {
...titleStyle,
text: `泡泡玛特资产结构演变(${financialViewLabel(view)})`,
subtext: '单位:亿元;资产负债表为期末余额,H2 为年末时点而非全年累计',
},
legend: {
...legendStyle,
top: LAYOUT.legendTop,
data: [...KEYS.map((key) => NAMES[key]), '资产总计'],
},
grid: { left: 78, right: 82, top: LAYOUT.gridTop, bottom: 90 },
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
textStyle: { fontFamily: FONT },
},
xAxis: { ...standalonePeriodAxis, data: periods },
yAxis: [
{
...axisCommon,
type: 'value',
name: '金额(亿元)',
nameLocation: 'middle',
nameGap: 52,
nameTextStyle: { color: PALETTE.grey, fontSize: 13, fontFamily: FONT },
min: 0,
max: axisMax,
interval,
},
{
...axisCommon,
type: 'value',
name: '资产总计(亿元)',
nameLocation: 'middle',
nameGap: 58,
nameRotate: -90,
nameTextStyle: { color: PALETTE.grey, fontSize: 13, fontFamily: FONT },
min: 0,
max: axisMax,
interval,
splitLine: { show: false },
},
],
series: [
...KEYS.map((key, ci) => ({
name: NAMES[key],
type: 'bar',
stack: 'assets',
// 每段都标数值:矮段塞不进字的,外置到柱体右侧(机制同渠道金额图)
data: rows.map((row, ri) => stackedSharePoint(row[key], {
pxPerUnit,
labelColor: INSIDE_LABEL_COLORS[key],
dy: rowOffsets[ri][ci] ?? 0,
})),
itemStyle: { color: COLORS[key] },
emphasis: { focus: 'series' },
label: {
show: true,
position: 'inside',
color: INSIDE_LABEL_COLORS[key],
fontSize: 11,
fontFamily: FONT,
overflow: 'none',
formatter: (p) => `${p.value.toFixed(1)}`,
},
})),
{
...lineSeries({
name: '资产总计',
data: col(rows, 'total_assets'),
color: PALETTE.red,
labelPosition: 'top',
formatter: (p) => p.value.toFixed(1),
width: 3,
}),
yAxisIndex: 1,
z: 10,
},
],
graphic: [
...watermarks(width, height),
footnote('数据来源:泡泡玛特财报 | @Joey知行记'),
],
};
}