Files
smart-fin-chart/js/charts/ppmt_stores_robot.js
T
R524809 2555453b79 feat(ppmt): 新增分地区收入占比与门店数量图表
- 新增分地区收入占比、零售门店数量、机器人商店数量三张图表及对应 CSV 数据
- 渠道收入图矮段标签外置并扇形排布,避免段内标签挤压
- IP 收入占比图改用 standardLegend 统一图例样式
- 折线标注增加白底描边,提升可读性
2026-08-31 16:31:44 +08:00

145 lines
4.7 KiB
JavaScript
Raw 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 {
CHANNEL_COLORS, PALETTE, FONT, axisCommon, standalonePeriodAxis, titleStyle,
standardLegend, financialViewLabel, lineSeries, footnote, watermarks, LAYOUT,
} from '../theme.js';
/**
* 泡泡玛特分地区机器人商店数量与同比增速。
* 堆叠柱为各期末机器人数,折线为全球合计数量的同比。
* 口径:2023年及以前"海外合计"含港澳台;2024年起港澳台单列并计入中国。
* 机器人商店仅披露内地/港澳台/海外三层,无亚太/美洲/欧洲分列。
*/
// 门店数是计数而非金额,保留整数。中国内地沿用渠道语义色(机器人商店=绿)。
const SEGMENT_COLORS = {
mainland: CHANNEL_COLORS.robotStore,
hkmtw: '#AECBE8',
overseas: '#C2726B',
};
const regions = [
{ key: 'mainland', name: '中国内地' },
{ key: 'hkmtw', name: '港澳台' },
{ key: 'overseas', name: '海外合计(含港澳台)' },
];
// 段内标签文字色:浅色底(港澳台)用深字,深色底(内地绿、海外玫红)用白字
const INSIDE_LABEL_COLORS = {
mainland: '#fff',
hkmtw: PALETTE.ink,
overseas: '#fff',
};
const num = (value) => (Number.isFinite(value) ? value : null);
// 绘图区高约 435px(grid top 145 / bottom 100,画布 680),轴上限 3000。
// 低于约 13px 的段放不下段内 10px 标签:柱顶段标到柱顶上方,
// 上方还有其它段的标到柱体右侧,避免与上段段内标签互相挤压。
const PX_PER_UNIT = 435 / 3000;
const MIN_INSIDE_PX = 13;
function segmentData(row, key) {
const value = num(row[key]);
if (value === null) return null;
if (value * PX_PER_UNIT >= MIN_INSIDE_PX) {
return { value, label: { position: 'inside', color: INSIDE_LABEL_COLORS[key] } };
}
const keyIndex = regions.findIndex((item) => item.key === key);
const hasSegmentAbove = regions
.slice(keyIndex + 1)
.some((item) => num(row[item.key]) !== null);
return {
value,
label: hasSegmentAbove
? { position: 'right', color: PALETTE.ink, distance: 4 }
: { position: 'top', color: PALETTE.ink },
};
}
export function storesRobotChart(rows, { width, height, view = 'all' }) {
return {
backgroundColor: '#fff',
title: {
...titleStyle,
text: `泡泡玛特分地区机器人商店数量与同比增速(${financialViewLabel(view)})`,
subtext: '柱状为各期末机器人商店数(堆叠);折线为全球合计数量同比',
},
legend: standardLegend([...regions.map((item) => item.name), '全球合计同比']),
grid: { left: 78, right: 82, top: LAYOUT.gridTop, bottom: 100 },
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
textStyle: { fontFamily: FONT },
},
xAxis: { ...standalonePeriodAxis, data: col(rows, 'period') },
yAxis: [
{
...axisCommon,
type: 'value',
name: '机器人商店数量(台)',
nameLocation: 'middle',
nameGap: 52,
nameTextStyle: { color: PALETTE.grey, fontSize: 13, fontFamily: FONT },
min: 0,
max: 3000,
interval: 500,
},
{
...axisCommon,
type: 'value',
name: '全球合计同比(%)',
nameLocation: 'middle',
nameGap: 60,
nameRotate: -90,
nameTextStyle: { color: PALETTE.grey, fontSize: 13, fontFamily: FONT },
min: 0,
max: 600,
interval: 100,
axisLabel: { ...axisCommon.axisLabel, formatter: '{value}%' },
splitLine: { show: false },
},
],
series: [
...regions.map((item) => ({
name: item.name,
type: 'bar',
stack: 'stores-robot',
data: rows.map((row) => segmentData(row, item.key)),
itemStyle: { color: SEGMENT_COLORS[item.key] },
label: {
show: true,
fontSize: 10,
fontWeight: 500,
fontFamily: FONT,
overflow: 'none',
},
// 相邻矮段标签贴近时沿 y 向微移避让
labelLayout: { moveOverlap: 'shiftY' },
emphasis: { focus: 'series' },
})),
{
...lineSeries({
name: '全球合计同比',
data: col(rows, 'global_yoy'),
color: '#8256B2',
formatter: (p) => `${p.value.toFixed(1)}%`,
}),
yAxisIndex: 1,
z: 10,
},
],
graphic: [
...watermarks(width, height),
footnote(
[
'数据来源:泡泡玛特历年公告;门店数为各期末时点数',
view === 'annual'
? '海外合计2023年及以前含港澳台、其后不含 | @思考的Joey'
: '海外合计2023年及以前含港澳台、其后不含;2024H1海外数量未披露 | @思考的Joey',
].join('\n'),
),
],
};
}