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知行记'), ], }; }