5 Commits

Author SHA1 Message Date
joey5628 b3c7c30a1f feat: 更新泡泡玛特图表,加入单店收入 2026-09-06 22:31:55 +08:00
R524809 2555453b79 feat(ppmt): 新增分地区收入占比与门店数量图表
- 新增分地区收入占比、零售门店数量、机器人商店数量三张图表及对应 CSV 数据
- 渠道收入图矮段标签外置并扇形排布,避免段内标签挤压
- IP 收入占比图改用 standardLegend 统一图例样式
- 折线标注增加白底描边,提升可读性
2026-08-31 16:31:44 +08:00
joey5628 d51890a450 feat: 泡泡玛特增加年度、半年数据区分 2026-08-30 14:49:27 +08:00
R524809 d010b107aa 更新进度文档 2026-08-28 17:44:37 +08:00
R524809 e5c21c6148 Standardize financial chart colors and improve label placement 2026-08-28 17:41:48 +08:00
103 changed files with 2491 additions and 375 deletions
+10 -4
View File
@@ -6,8 +6,8 @@
```js ```js
import { import {
PALETTE, FONT, axisCommon, periodAxis, titleStyle, PALETTE, FINANCIAL_COLORS, BRIDGE_COLORS, CHANNEL_COLORS, FONT, axisCommon, periodAxis, titleStyle,
lineSeries, barSeries, standardLegend, footnote, watermarks, LAYOUT, lineSeries, lineDataWithSafeLabels, barSeries, standardLegend, footnote, watermarks, LAYOUT,
} from '../theme.js'; } from '../theme.js';
``` ```
@@ -27,13 +27,13 @@ pnpm test # 全量,只在改了 t
全量跑纯属浪费,而且十几张无关图表的输出会盖住本次改动的真实问题。不传参数时才是全量,所以 `pnpm test` 保持向后兼容。 全量跑纯属浪费,而且十几张无关图表的输出会盖住本次改动的真实问题。不传参数时才是全量,所以 `pnpm test` 保持向后兼容。
它会断言 `legend.top`、`grid.top`、折线 `symbolSize`/`borderWidth`,并检查 SVG 字体引号。加新图记得同步往 `regress.mjs` 顶部的 `all` 数组加一行,否则不会被测到。 它会断言 `legend.top`、`grid.top`、折线 `symbolSize`/`borderWidth`,并检查 SVG 字体引号。回归测试直接遍历 `manifest.json` 的 `chartCatalog`,新增图只要完成 registry 与 manifest 登记就会被测到。
**右侧纵向图例的图会报 `legend.top=middle (期望85)`,这是预期的,不要去"修"。** 那条断言的 85px 基准是给顶部横向图例定的,对 `orient: 'vertical'` + `top: 'middle'` 的图不适用(`ppmt_ip_share`、`ppmt_ip_share_history` 都会报)。 **右侧纵向图例的图会报 `legend.top=middle (期望85)`,这是预期的,不要去"修"。** 那条断言的 85px 基准是给顶部横向图例定的,对 `orient: 'vertical'` + `top: 'middle'` 的图不适用(`ppmt_ip_share`、`ppmt_ip_share_history` 都会报)。
## 踩过的坑 ## 踩过的坑
**不要用 `...legendStyle`。** 它遗留 `top: 62` 且曾带 `icon: 'roundRect'`——后者会盖掉 ECharts 按系列类型自动生成的图例(折线本该是「短线+空心圆」)。用 `standardLegend()`。 **优先用 `standardLegend()`,且不要手工设置 `legend.icon`。** ECharts 会按系列类型自动生成图例:柱状图为矩形,折线图为「短线 + 空心圆」。需要特殊位置时可扩展 `legendStyle`,但不得覆盖这一自动类型图标。
**`label.position` 不接受函数。** 写了会被静默忽略、退化成默认位置,实测确认过。要让同一系列内不同点朝向不同,只能逐点覆盖 `data`:`{ value, label: { position: 'top' } }`。 **`label.position` 不接受函数。** 写了会被静默忽略、退化成默认位置,实测确认过。要让同一系列内不同点朝向不同,只能逐点覆盖 `data`:`{ value, label: { position: 'top' } }`。
@@ -59,6 +59,12 @@ pnpm test # 全量,只在改了 t
## 配色 ## 配色
**正式方向:轻快消费。** 柱线组合图的现金流、利润与同比使用 `FINANCIAL_COLORS`,渠道结构使用 `CHANNEL_COLORS`,瀑布图使用 `BRIDGE_COLORS`(蓝色基准、青绿色正向、珊瑚红负向);不要在图表文件里为相同财务/渠道语义重写十六进制颜色。柱状系列负责分类,折线只保留一条高辨识度强调色。
**一张图只留一个视觉主角。** 辅助柱使用中等饱和度;避免红、橙、紫等多个强调色同时竞争。分类色要同时拉开色相和明度;折线除颜色外还使用线宽、实虚线、空心圆和标签优先级辅助区分。
**柱线组合的标签不能压在柱体端点。** 使用 `lineDataWithSafeLabels(rows, { lineKey, barKeys })`:正值标签在上、负值标签在下,接近柱值时自动增加距离,并配合 ECharts `labelLayout` 兜底;不要按某个年份写特判。
先分清两件事,别混用同一个门槛。**选色的功夫要和系列数成正比**——两个系列的图不该选半天。 先分清两件事,别混用同一个门槛。**选色的功夫要和系列数成正比**——两个系列的图不该选半天。
**① 同一张图内的系列要能互相区分。** 这条永远要满足,是可读性问题: **① 同一张图内的系列要能互相区分。** 这条永远要满足,是可读性问题:
+33 -5
View File
@@ -123,6 +123,9 @@ button {
.content { min-width: 0; } .content { min-width: 0; }
.tabs { .tabs {
position: sticky;
top: 20px;
z-index: 5;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
gap: 6px; gap: 6px;
@@ -142,7 +145,7 @@ button {
background: var(--surface); background: var(--surface);
border-radius: 10px; border-radius: 10px;
box-shadow: 0 1px 3px rgba(0, 0, 0, .06); box-shadow: 0 1px 3px rgba(0, 0, 0, .06);
scroll-margin-top: 20px; scroll-margin-top: 84px;
} }
.card-bar { .card-bar {
@@ -198,14 +201,39 @@ button {
} }
@media (max-width: 880px) { @media (max-width: 880px) {
.app-layout { display: block; padding: 16px 12px 40px; } .app-layout { display: block; padding: 0 12px 40px; }
.sidebar { position: static; margin-bottom: 12px; } .sidebar { margin-bottom: 12px; }
.company-sidebar { padding: 7px; } .company-sidebar {
position: sticky;
top: 0;
z-index: 7;
margin: 0 -12px;
padding: 7px 12px;
border-width: 0 0 1px;
border-radius: 0;
background: rgba(245, 244, 241, .96);
backdrop-filter: blur(10px);
}
.company-sidebar .sidebar-title { display: none; } .company-sidebar .sidebar-title { display: none; }
.company-list { display: flex; overflow-x: auto; } .company-list { display: flex; overflow-x: auto; }
.company-button { flex: 0 0 auto; width: auto; } .company-button { flex: 0 0 auto; width: auto; }
.tabs { overflow-x: auto; flex-wrap: nowrap; } .tabs {
top: 50px;
z-index: 6;
overflow-x: auto;
overflow-y: hidden;
flex-wrap: nowrap;
margin: 0 -12px 18px;
padding: 5px 12px;
border-width: 0 0 1px;
border-radius: 0;
background: rgba(245, 244, 241, .96);
backdrop-filter: blur(10px);
scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }
.tab-button { flex: 0 0 auto; } .tab-button { flex: 0 0 auto; }
.card { scroll-margin-top: 120px; }
.card-bar { align-items: flex-start; flex-wrap: wrap; } .card-bar { align-items: flex-start; flex-wrap: wrap; }
.export-button { margin-left: auto; } .export-button { margin-left: auto; }
.chart { min-width: 560px; } .chart { min-width: 560px; }
+9 -9
View File
@@ -1,13 +1,13 @@
period,total_assets,cash_like,receivables,investment,operating_assets period,total_assets,cash_like,receivables,investment,operating_assets
2020H1,17.89,8.22,0.41,0.46,8.8 2020H1,17.89,8.22,0.41,0.46,8.80
2020H2,69.71,56.8,0.78,0.67,11.45 2020H2,69.71,56.80,0.78,0.67,11.45
2021H1,76.41,57.67,1.35,1.78,15.6 2021H1,76.41,57.67,1.35,1.78,15.60
2021H2,83.24,52.65,1.71,4.11,24.77 2021H2,83.24,52.65,1.71,4.11,24.77
2022H1,85.9,51.63,1.32,5.53,27.42 2022H1,85.90,51.63,1.32,5.53,27.42
2022H2,85.8,50.42,1.94,5.55,27.89 2022H2,85.80,50.42,1.94,5.55,27.89
2023H1,90.65,55.4,2.09,5.75,27.42 2023H1,90.65,55.40,2.09,5.75,27.42
2023H2,99.69,59.63,3.21,5.87,30.97 2023H2,99.69,59.63,3.21,5.87,30.97
2024H1,110.17,70.1,2.64,5.5,31.93 2024H1,110.17,70.10,2.64,5.50,31.93
2024H2,148.71,96.2,4.78,5.6,42.13 2024H2,148.71,96.20,4.78,5.60,42.13
2025H1,213.57,137.66,9.72,5.4,60.79 2025H1,213.57,137.66,9.72,5.40,60.79
2025H2,321.01,172.25,9.21,4.95,132.04 2025H2,321.01,172.25,9.21,4.95,132.04
1 period total_assets cash_like receivables investment operating_assets
2 2020H1 17.89 8.22 0.41 0.46 8.8 8.80
3 2020H2 69.71 56.8 56.80 0.78 0.67 11.45
4 2021H1 76.41 57.67 1.35 1.78 15.6 15.60
5 2021H2 83.24 52.65 1.71 4.11 24.77
6 2022H1 85.9 85.90 51.63 1.32 5.53 27.42
7 2022H2 85.8 85.80 50.42 1.94 5.55 27.89
8 2023H1 90.65 55.4 55.40 2.09 5.75 27.42
9 2023H2 99.69 59.63 3.21 5.87 30.97
10 2024H1 110.17 70.1 70.10 2.64 5.5 5.50 31.93
11 2024H2 148.71 96.2 96.20 4.78 5.6 5.60 42.13
12 2025H1 213.57 137.66 9.72 5.4 5.40 60.79
13 2025H2 321.01 172.25 9.21 4.95 132.04
+7
View File
@@ -0,0 +1,7 @@
period,total_assets,cash_like,receivables,investment,operating_assets
2020FY,69.71,56.80,0.78,0.67,11.45
2021FY,83.24,52.65,1.71,4.11,24.77
2022FY,85.80,50.42,1.94,5.55,27.89
2023FY,99.69,59.63,3.21,5.87,30.97
2024FY,148.71,96.20,4.78,5.60,42.13
2025FY,321.01,172.25,9.21,4.95,132.04
1 period total_assets cash_like receivables investment operating_assets
2 2020FY 69.71 56.80 0.78 0.67 11.45
3 2021FY 83.24 52.65 1.71 4.11 24.77
4 2022FY 85.80 50.42 1.94 5.55 27.89
5 2023FY 99.69 59.63 3.21 5.87 30.97
6 2024FY 148.71 96.20 4.78 5.60 42.13
7 2025FY 321.01 172.25 9.21 4.95 132.04
+13
View File
@@ -0,0 +1,13 @@
period,total_assets,cash_like,receivables,investment,operating_assets
2020H1,17.89,8.22,0.41,0.46,8.80
2020H2,69.71,56.80,0.78,0.67,11.45
2021H1,76.41,57.67,1.35,1.78,15.60
2021H2,83.24,52.65,1.71,4.11,24.77
2022H1,85.90,51.63,1.32,5.53,27.42
2022H2,85.80,50.42,1.94,5.55,27.89
2023H1,90.65,55.40,2.09,5.75,27.42
2023H2,99.69,59.63,3.21,5.87,30.97
2024H1,110.17,70.10,2.64,5.50,31.93
2024H2,148.71,96.20,4.78,5.60,42.13
2025H1,213.57,137.66,9.72,5.40,60.79
2025H2,321.01,172.25,9.21,4.95,132.04
1 period total_assets cash_like receivables investment operating_assets
2 2020H1 17.89 8.22 0.41 0.46 8.80
3 2020H2 69.71 56.80 0.78 0.67 11.45
4 2021H1 76.41 57.67 1.35 1.78 15.60
5 2021H2 83.24 52.65 1.71 4.11 24.77
6 2022H1 85.90 51.63 1.32 5.53 27.42
7 2022H2 85.80 50.42 1.94 5.55 27.89
8 2023H1 90.65 55.40 2.09 5.75 27.42
9 2023H2 99.69 59.63 3.21 5.87 30.97
10 2024H1 110.17 70.10 2.64 5.50 31.93
11 2024H2 148.71 96.20 4.78 5.60 42.13
12 2025H1 213.57 137.66 9.72 5.40 60.79
13 2025H2 321.01 172.25 9.21 4.95 132.04
+12
View File
@@ -1,7 +1,19 @@
period,operating_cash_flow,net_profit,cash_conversion period,operating_cash_flow,net_profit,cash_conversion
2020H1,1.22,1.41,86.5
2020H2,5.81,3.82,152.1
2020FY,7.03,5.23,134.4 2020FY,7.03,5.23,134.4
2021H1,4.42,3.59,123.1
2021H2,3.37,4.96,68.1
2021FY,7.79,8.55,91.2 2021FY,7.79,8.55,91.2
2022H1,3.74,3.33,112.2
2022H2,5.17,1.43,362.2
2022FY,8.91,4.76,187.3 2022FY,8.91,4.76,187.3
2023H1,10.92,4.77,228.9
2023H2,8.98,6.12,146.9
2023FY,19.91,10.89,182.8 2023FY,19.91,10.89,182.8
2024H1,19.05,9.64,197.6
2024H2,30.49,23.44,130.1
2024FY,49.54,33.08,149.7 2024FY,49.54,33.08,149.7
2025H1,59.77,46.82,127.7
2025H2,48.88,83.30,58.7
2025FY,108.65,130.12,83.5 2025FY,108.65,130.12,83.5
1 period operating_cash_flow net_profit cash_conversion
2 2020H1 1.22 1.41 86.5
3 2020H2 5.81 3.82 152.1
4 2020FY 7.03 5.23 134.4
5 2021H1 4.42 3.59 123.1
6 2021H2 3.37 4.96 68.1
7 2021FY 7.79 8.55 91.2
8 2022H1 3.74 3.33 112.2
9 2022H2 5.17 1.43 362.2
10 2022FY 8.91 4.76 187.3
11 2023H1 10.92 4.77 228.9
12 2023H2 8.98 6.12 146.9
13 2023FY 19.91 10.89 182.8
14 2024H1 19.05 9.64 197.6
15 2024H2 30.49 23.44 130.1
16 2024FY 49.54 33.08 149.7
17 2025H1 59.77 46.82 127.7
18 2025H2 48.88 83.30 58.7
19 2025FY 108.65 130.12 83.5
@@ -0,0 +1,7 @@
period,operating_cash_flow,net_profit,cash_conversion
2020FY,7.03,5.23,134.4
2021FY,7.79,8.55,91.2
2022FY,8.91,4.76,187.3
2023FY,19.91,10.89,182.8
2024FY,49.54,33.08,149.7
2025FY,108.65,130.12,83.5
1 period operating_cash_flow net_profit cash_conversion
2 2020FY 7.03 5.23 134.4
3 2021FY 7.79 8.55 91.2
4 2022FY 8.91 4.76 187.3
5 2023FY 19.91 10.89 182.8
6 2024FY 49.54 33.08 149.7
7 2025FY 108.65 130.12 83.5
@@ -0,0 +1,13 @@
period,operating_cash_flow,net_profit,cash_conversion
2020H1,1.22,1.41,86.5
2020H2,5.81,3.82,152.1
2021H1,4.42,3.59,123.1
2021H2,3.37,4.96,68.1
2022H1,3.74,3.33,112.2
2022H2,5.17,1.43,362.2
2023H1,10.92,4.77,228.9
2023H2,8.98,6.12,146.9
2024H1,19.05,9.64,197.6
2024H2,30.49,23.44,130.1
2025H1,59.77,46.82,127.7
2025H2,48.88,83.30,58.7
1 period operating_cash_flow net_profit cash_conversion
2 2020H1 1.22 1.41 86.5
3 2020H2 5.81 3.82 152.1
4 2021H1 4.42 3.59 123.1
5 2021H2 3.37 4.96 68.1
6 2022H1 3.74 3.33 112.2
7 2022H2 5.17 1.43 362.2
8 2023H1 10.92 4.77 228.9
9 2023H2 8.98 6.12 146.9
10 2024H1 19.05 9.64 197.6
11 2024H2 30.49 23.44 130.1
12 2025H1 59.77 46.82 127.7
13 2025H2 48.88 83.30 58.7
+12
View File
@@ -1,7 +1,19 @@
period,operating_cash_flow,investing_cash_flow,financing_cash_flow,net_cash_increase period,operating_cash_flow,investing_cash_flow,financing_cash_flow,net_cash_increase
2020H1,1.22,-0.48,4.24,4.98
2020H2,5.81,-1.69,44.47,48.59
2020FY,7.03,-2.17,48.70,53.57 2020FY,7.03,-2.17,48.70,53.57
2021H1,4.42,-44.98,-1.21,-41.78
2021H2,3.37,38.50,-3.15,38.73
2021FY,7.79,-6.48,-4.36,-3.05 2021FY,7.79,-6.48,-4.36,-3.05
2022H1,3.74,-42.10,-4.81,-43.17
2022H2,5.17,-4.89,-6.97,-6.68
2022FY,8.91,-46.98,-11.77,-49.85 2022FY,8.91,-46.98,-11.77,-49.85
2023H1,10.92,0.82,-5.58,6.16
2023H2,8.98,1.52,-2.83,7.67
2023FY,19.91,2.34,-8.42,13.83 2023FY,19.91,2.34,-8.42,13.83
2024H1,19.05,3.34,-7.16,15.23
2024H2,30.49,-3.25,-2.43,24.82
2024FY,49.54,0.09,-9.59,40.05 2024FY,49.54,0.09,-9.59,40.05
2025H1,59.77,12.34,-14.05,58.07
2025H2,48.88,-22.23,-3.62,23.02
2025FY,108.65,-9.89,-17.67,81.09 2025FY,108.65,-9.89,-17.67,81.09
1 period operating_cash_flow investing_cash_flow financing_cash_flow net_cash_increase
2 2020H1 1.22 -0.48 4.24 4.98
3 2020H2 5.81 -1.69 44.47 48.59
4 2020FY 7.03 -2.17 48.70 53.57
5 2021H1 4.42 -44.98 -1.21 -41.78
6 2021H2 3.37 38.50 -3.15 38.73
7 2021FY 7.79 -6.48 -4.36 -3.05
8 2022H1 3.74 -42.10 -4.81 -43.17
9 2022H2 5.17 -4.89 -6.97 -6.68
10 2022FY 8.91 -46.98 -11.77 -49.85
11 2023H1 10.92 0.82 -5.58 6.16
12 2023H2 8.98 1.52 -2.83 7.67
13 2023FY 19.91 2.34 -8.42 13.83
14 2024H1 19.05 3.34 -7.16 15.23
15 2024H2 30.49 -3.25 -2.43 24.82
16 2024FY 49.54 0.09 -9.59 40.05
17 2025H1 59.77 12.34 -14.05 58.07
18 2025H2 48.88 -22.23 -3.62 23.02
19 2025FY 108.65 -9.89 -17.67 81.09
@@ -0,0 +1,7 @@
period,operating_cash_flow,investing_cash_flow,financing_cash_flow,net_cash_increase
2020FY,7.03,-2.17,48.70,53.57
2021FY,7.79,-6.48,-4.36,-3.05
2022FY,8.91,-46.98,-11.77,-49.85
2023FY,19.91,2.34,-8.42,13.83
2024FY,49.54,0.09,-9.59,40.05
2025FY,108.65,-9.89,-17.67,81.09
1 period operating_cash_flow investing_cash_flow financing_cash_flow net_cash_increase
2 2020FY 7.03 -2.17 48.70 53.57
3 2021FY 7.79 -6.48 -4.36 -3.05
4 2022FY 8.91 -46.98 -11.77 -49.85
5 2023FY 19.91 2.34 -8.42 13.83
6 2024FY 49.54 0.09 -9.59 40.05
7 2025FY 108.65 -9.89 -17.67 81.09
@@ -0,0 +1,13 @@
period,operating_cash_flow,investing_cash_flow,financing_cash_flow,net_cash_increase
2020H1,1.22,-0.48,4.24,4.98
2020H2,5.81,-1.69,44.47,48.59
2021H1,4.42,-44.98,-1.21,-41.78
2021H2,3.37,38.50,-3.15,38.73
2022H1,3.74,-42.10,-4.81,-43.17
2022H2,5.17,-4.89,-6.97,-6.68
2023H1,10.92,0.82,-5.58,6.16
2023H2,8.98,1.52,-2.83,7.67
2024H1,19.05,3.34,-7.16,15.23
2024H2,30.49,-3.25,-2.43,24.82
2025H1,59.77,12.34,-14.05,58.07
2025H2,48.88,-22.23,-3.62,23.02
1 period operating_cash_flow investing_cash_flow financing_cash_flow net_cash_increase
2 2020H1 1.22 -0.48 4.24 4.98
3 2020H2 5.81 -1.69 44.47 48.59
4 2021H1 4.42 -44.98 -1.21 -41.78
5 2021H2 3.37 38.50 -3.15 38.73
6 2022H1 3.74 -42.10 -4.81 -43.17
7 2022H2 5.17 -4.89 -6.97 -6.68
8 2023H1 10.92 0.82 -5.58 6.16
9 2023H2 8.98 1.52 -2.83 7.67
10 2024H1 19.05 3.34 -7.16 15.23
11 2024H2 30.49 -3.25 -2.43 24.82
12 2025H1 59.77 12.34 -14.05 58.07
13 2025H2 48.88 -22.23 -3.62 23.02
+6 -5
View File
@@ -1,6 +1,7 @@
period,plush,figure,mega,other period,plush,figure,mega,other
品类,毛绒,手办,MEGA,衍生品及其他 2024H1,4.46,26.57,5.86,8.69
2024H1,4.46105,26.56781,5.8613,8.68815 2024H2,23.86,42.79,10.98,7.17
2024H2,23.85973,42.79306,10.9765,7.16989 2024FY,28.32,69.36,16.84,15.86
2025H1,61.39236,51.75875,10.07147,15.54018 2025H1,61.39,51.76,10.07,15.54
2025H2,125.68838,68.46694,9.08935,29.19309 2025H2,125.69,68.47,9.09,29.19
2025FY,187.08,120.23,19.16,44.73
1 period plush figure mega other
2 品类 2024H1 毛绒 4.46 手办 26.57 MEGA 5.86 衍生品及其他 8.69
3 2024H1 2024H2 4.46105 23.86 26.56781 42.79 5.8613 10.98 8.68815 7.17
4 2024H2 2024FY 23.85973 28.32 42.79306 69.36 10.9765 16.84 7.16989 15.86
5 2025H1 61.39236 61.39 51.75875 51.76 10.07147 10.07 15.54018 15.54
6 2025H2 125.68838 125.69 68.46694 68.47 9.08935 9.09 29.19309 29.19
7 2025FY 187.08 120.23 19.16 44.73
+3
View File
@@ -0,0 +1,3 @@
period,plush,figure,mega,other
2024FY,28.32,69.36,16.84,15.86
2025FY,187.08,120.23,19.16,44.73
1 period plush figure mega other
2 2024FY 28.32 69.36 16.84 15.86
3 2025FY 187.08 120.23 19.16 44.73
+5
View File
@@ -0,0 +1,5 @@
period,plush,figure,mega,other
2024H1,4.46,26.57,5.86,8.69
2024H2,23.86,42.79,10.98,7.17
2025H1,61.39,51.76,10.07,15.54
2025H2,125.69,68.47,9.09,29.19
1 period plush figure mega other
2 2024H1 4.46 26.57 5.86 8.69
3 2024H2 23.86 42.79 10.98 7.17
4 2025H1 61.39 51.76 10.07 15.54
5 2025H2 125.69 68.47 9.09 29.19
+6
View File
@@ -1,14 +1,20 @@
period,retail,online,robot_store,wholesale,total_revenue,revenue_yoy period,retail,online,robot_store,wholesale,total_revenue,revenue_yoy
2020H1,3.13,3.34,1.05,0.64,8.18,50.5 2020H1,3.13,3.34,1.05,0.64,8.18,50.5
2020H2,6.89,6.18,2.23,1.44,16.96,48.7
2020FY,10.02,9.52,3.29,2.07,25.13,49.3 2020FY,10.02,9.52,3.29,2.07,25.13,49.3
2021H1,6.75,6.78,2.28,1.91,17.73,116.8 2021H1,6.75,6.78,2.28,1.91,17.73,116.8
2021H2,9.98,12.01,2.42,2.53,27.18,60.3
2021FY,16.73,18.79,4.70,4.45,44.91,78.7 2021FY,16.73,18.79,4.70,4.45,44.91,78.7
2022H1,9.26,10.13,2.08,2.12,23.59,33.1 2022H1,9.26,10.13,2.08,2.12,23.59,33.1
2022H2,9.00,9.07,1.86,2.65,22.59,-16.9
2022FY,18.27,19.20,3.94,4.77,46.17,2.8 2022FY,18.27,19.20,3.94,4.77,46.17,2.8
2023H1,13.44,8.74,2.96,2.99,28.14,19.3 2023H1,13.44,8.74,2.96,2.99,28.14,19.3
2023H2,17.18,9.92,3.10,4.68,34.87,54.4
2023FY,30.62,18.66,6.06,7.67,63.01,36.5 2023FY,30.62,18.66,6.06,7.67,63.01,36.5
2024H1,23.65,13.43,3.68,4.82,45.58,62.0 2024H1,23.65,13.43,3.68,4.82,45.58,62.0
2024H2,44.01,28.10,4.63,8.06,84.80,143.2
2024FY,67.66,41.53,8.31,12.88,130.38,106.9 2024FY,67.66,41.53,8.31,12.88,130.38,106.9
2025H1,69.08,54.98,8.21,6.50,138.76,204.4 2025H1,69.08,54.98,8.21,6.50,138.76,204.4
2025H2,103.46,109.36,8.69,10.93,232.44,174.1
2025FY,172.54,164.34,16.89,17.42,371.20,184.7 2025FY,172.54,164.34,16.89,17.42,371.20,184.7
2026H1,91.12,62.12,9.51,8.98,171.73,23.8 2026H1,91.12,62.12,9.51,8.98,171.73,23.8
1 period retail online robot_store wholesale total_revenue revenue_yoy
2 2020H1 3.13 3.34 1.05 0.64 8.18 50.5
3 2020H2 6.89 6.18 2.23 1.44 16.96 48.7
4 2020FY 10.02 9.52 3.29 2.07 25.13 49.3
5 2021H1 6.75 6.78 2.28 1.91 17.73 116.8
6 2021H2 9.98 12.01 2.42 2.53 27.18 60.3
7 2021FY 16.73 18.79 4.70 4.45 44.91 78.7
8 2022H1 9.26 10.13 2.08 2.12 23.59 33.1
9 2022H2 9.00 9.07 1.86 2.65 22.59 -16.9
10 2022FY 18.27 19.20 3.94 4.77 46.17 2.8
11 2023H1 13.44 8.74 2.96 2.99 28.14 19.3
12 2023H2 17.18 9.92 3.10 4.68 34.87 54.4
13 2023FY 30.62 18.66 6.06 7.67 63.01 36.5
14 2024H1 23.65 13.43 3.68 4.82 45.58 62.0
15 2024H2 44.01 28.10 4.63 8.06 84.80 143.2
16 2024FY 67.66 41.53 8.31 12.88 130.38 106.9
17 2025H1 69.08 54.98 8.21 6.50 138.76 204.4
18 2025H2 103.46 109.36 8.69 10.93 232.44 174.1
19 2025FY 172.54 164.34 16.89 17.42 371.20 184.7
20 2026H1 91.12 62.12 9.51 8.98 171.73 23.8
@@ -0,0 +1,7 @@
period,retail,online,robot_store,wholesale,total_revenue,revenue_yoy
2020FY,10.02,9.52,3.29,2.07,25.13,49.3
2021FY,16.73,18.79,4.70,4.45,44.91,78.7
2022FY,18.27,19.20,3.94,4.77,46.17,2.8
2023FY,30.62,18.66,6.06,7.67,63.01,36.5
2024FY,67.66,41.53,8.31,12.88,130.38,106.9
2025FY,172.54,164.34,16.89,17.42,371.20,184.7
1 period retail online robot_store wholesale total_revenue revenue_yoy
2 2020FY 10.02 9.52 3.29 2.07 25.13 49.3
3 2021FY 16.73 18.79 4.70 4.45 44.91 78.7
4 2022FY 18.27 19.20 3.94 4.77 46.17 2.8
5 2023FY 30.62 18.66 6.06 7.67 63.01 36.5
6 2024FY 67.66 41.53 8.31 12.88 130.38 106.9
7 2025FY 172.54 164.34 16.89 17.42 371.20 184.7
@@ -0,0 +1,14 @@
period,retail,online,robot_store,wholesale,total_revenue,revenue_yoy
2020H1,3.13,3.34,1.05,0.64,8.18,50.5
2020H2,6.89,6.18,2.23,1.44,16.96,48.7
2021H1,6.75,6.78,2.28,1.91,17.73,116.8
2021H2,9.98,12.01,2.42,2.53,27.18,60.3
2022H1,9.26,10.13,2.08,2.12,23.59,33.1
2022H2,9.00,9.07,1.86,2.65,22.59,-16.9
2023H1,13.44,8.74,2.96,2.99,28.14,19.3
2023H2,17.18,9.92,3.10,4.68,34.87,54.4
2024H1,23.65,13.43,3.68,4.82,45.58,62.0
2024H2,44.01,28.10,4.63,8.06,84.80,143.2
2025H1,69.08,54.98,8.21,6.50,138.76,204.4
2025H2,103.46,109.36,8.69,10.93,232.44,174.1
2026H1,91.12,62.12,9.51,8.98,171.73,23.8
1 period retail online robot_store wholesale total_revenue revenue_yoy
2 2020H1 3.13 3.34 1.05 0.64 8.18 50.5
3 2020H2 6.89 6.18 2.23 1.44 16.96 48.7
4 2021H1 6.75 6.78 2.28 1.91 17.73 116.8
5 2021H2 9.98 12.01 2.42 2.53 27.18 60.3
6 2022H1 9.26 10.13 2.08 2.12 23.59 33.1
7 2022H2 9.00 9.07 1.86 2.65 22.59 -16.9
8 2023H1 13.44 8.74 2.96 2.99 28.14 19.3
9 2023H2 17.18 9.92 3.10 4.68 34.87 54.4
10 2024H1 23.65 13.43 3.68 4.82 45.58 62.0
11 2024H2 44.01 28.10 4.63 8.06 84.80 143.2
12 2025H1 69.08 54.98 8.21 6.50 138.76 204.4
13 2025H2 103.46 109.36 8.69 10.93 232.44 174.1
14 2026H1 91.12 62.12 9.51 8.98 171.73 23.8
+12 -6
View File
@@ -1,14 +1,20 @@
period,core_margin period,core_margin
2020H1,22.6 2020H1,22.6
2020H2,27.2 2020H2,29.4
2020FY,27.2
2021H1,25.8 2021H1,25.8
2021H2,24.4 2021H2,23.5
2021FY,24.4
2022H1,15.0 2022H1,15.0
2022H2,10.8 2022H2,6.3
2022FY,10.8
2023H1,17.4 2023H1,17.4
2023H2,18.3 2023H2,19.0
2023FY,18.3
2024H1,24.8 2024H1,24.8
2024H2,31.5 2024H2,35.1
2024FY,31.5
2025H1,41.8 2025H1,41.8
2025H2,45.6 2025H2,47.8
2025FY,45.6
2026H1,41.4 2026H1,41.4
1 period core_margin
2 2020H1 22.6
3 2020H2 27.2 29.4
4 2020FY 27.2
5 2021H1 25.8
6 2021H2 24.4 23.5
7 2021FY 24.4
8 2022H1 15.0
9 2022H2 10.8 6.3
10 2022FY 10.8
11 2023H1 17.4
12 2023H2 18.3 19.0
13 2023FY 18.3
14 2024H1 24.8
15 2024H2 31.5 35.1
16 2024FY 31.5
17 2025H1 41.8
18 2025H2 45.6 47.8
19 2025FY 45.6
20 2026H1 41.4
+7
View File
@@ -0,0 +1,7 @@
period,core_margin
2020FY,27.2
2021FY,24.4
2022FY,10.8
2023FY,18.3
2024FY,31.5
2025FY,45.6
1 period core_margin
2 2020FY 27.2
3 2021FY 24.4
4 2022FY 10.8
5 2023FY 18.3
6 2024FY 31.5
7 2025FY 45.6
+14
View File
@@ -0,0 +1,14 @@
period,core_margin
2020H1,22.6
2020H2,29.4
2021H1,25.8
2021H2,23.5
2022H1,15.0
2022H2,6.3
2023H1,17.4
2023H2,19.0
2024H1,24.8
2024H2,35.1
2025H1,41.8
2025H2,47.8
2026H1,41.4
1 period core_margin
2 2020H1 22.6
3 2020H2 29.4
4 2021H1 25.8
5 2021H2 23.5
6 2022H1 15.0
7 2022H2 6.3
8 2023H1 17.4
9 2023H2 19.0
10 2024H1 24.8
11 2024H2 35.1
12 2025H1 41.8
13 2025H2 47.8
14 2026H1 41.4
+19 -13
View File
@@ -1,14 +1,20 @@
period,core_profit,yoy period,core_profit,yoy
2020H1,1.9,25.0 2020H1,1.85,25.2
2020H2,6.8,17.1 2020H2,4.99,14.4
2021H1,4.6,147.0 2020FY,6.84,17.1
2021H2,11.0,60.1 2021H1,4.58,147.3
2022H1,3.5,-22.5 2021H2,6.37,27.7
2022H2,5.0,-54.5 2021FY,10.95,60.1
2023H1,4.9,38.1 2022H1,3.54,-22.5
2023H2,11.5,131.3 2022H2,1.43,-77.6
2024H1,11.3,131.5 2022FY,4.98,-54.6
2024H2,41.1,256.9 2023H1,4.89,37.9
2025H1,58.0,412.2 2023H2,6.63,363.8
2025H2,169.1,311.4 2023FY,11.52,131.6
2026H1,71.1,22.6 2024H1,11.31,131.5
2024H2,29.79,349.1
2024FY,41.10,256.8
2025H1,57.98,412.4
2025H2,111.14,273.1
2025FY,169.12,311.5
2026H1,71.08,22.6
1 period core_profit yoy
2 2020H1 1.9 1.85 25.0 25.2
3 2020H2 6.8 4.99 17.1 14.4
4 2021H1 2020FY 4.6 6.84 147.0 17.1
5 2021H2 2021H1 11.0 4.58 60.1 147.3
6 2022H1 2021H2 3.5 6.37 -22.5 27.7
7 2022H2 2021FY 5.0 10.95 -54.5 60.1
8 2023H1 2022H1 4.9 3.54 38.1 -22.5
9 2023H2 2022H2 11.5 1.43 131.3 -77.6
10 2024H1 2022FY 11.3 4.98 131.5 -54.6
11 2024H2 2023H1 41.1 4.89 256.9 37.9
12 2025H1 2023H2 58.0 6.63 412.2 363.8
13 2025H2 2023FY 169.1 11.52 311.4 131.6
14 2026H1 2024H1 71.1 11.31 22.6 131.5
15 2024H2 29.79 349.1
16 2024FY 41.10 256.8
17 2025H1 57.98 412.4
18 2025H2 111.14 273.1
19 2025FY 169.12 311.5
20 2026H1 71.08 22.6
+7
View File
@@ -0,0 +1,7 @@
period,core_profit,yoy
2020FY,6.84,17.1
2021FY,10.95,60.1
2022FY,4.98,-54.6
2023FY,11.52,131.6
2024FY,41.10,256.8
2025FY,169.12,311.5
1 period core_profit yoy
2 2020FY 6.84 17.1
3 2021FY 10.95 60.1
4 2022FY 4.98 -54.6
5 2023FY 11.52 131.6
6 2024FY 41.10 256.8
7 2025FY 169.12 311.5
+14
View File
@@ -0,0 +1,14 @@
period,core_profit,yoy
2020H1,1.85,25.2
2020H2,4.99,14.4
2021H1,4.58,147.3
2021H2,6.37,27.7
2022H1,3.54,-22.5
2022H2,1.43,-77.6
2023H1,4.89,37.9
2023H2,6.63,363.8
2024H1,11.31,131.5
2024H2,29.79,349.1
2025H1,57.98,412.4
2025H2,111.14,273.1
2026H1,71.08,22.6
1 period core_profit yoy
2 2020H1 1.85 25.2
3 2020H2 4.99 14.4
4 2021H1 4.58 147.3
5 2021H2 6.37 27.7
6 2022H1 3.54 -22.5
7 2022H2 1.43 -77.6
8 2023H1 4.89 37.9
9 2023H2 6.63 363.8
10 2024H1 11.31 131.5
11 2024H2 29.79 349.1
12 2025H1 57.98 412.4
13 2025H2 111.14 273.1
14 2026H1 71.08 22.6
+13
View File
@@ -1,7 +1,20 @@
period,sales_expense,admin_expense,financial_expense,four_expense_rate period,sales_expense,admin_expense,financial_expense,four_expense_rate
2020H1,2.23,1.25,0.04,43.1
2020H2,4.07,1.55,0.05,33.4
2020FY,6.30,2.80,0.09,36.6 2020FY,6.30,2.80,0.09,36.6
2021H1,4.20,2.40,-0.06,36.9
2021H2,6.86,3.18,-0.02,36.9
2021FY,11.06,5.58,-0.07,36.9 2021FY,11.06,5.58,-0.07,36.9
2022H1,6.93,3.23,-0.07,42.8
2022H2,7.78,3.64,-0.22,49.5
2022FY,14.71,6.86,-0.29,46.1 2022FY,14.71,6.86,-0.29,46.1
2023H1,8.78,3.31,-0.63,40.7
2023H2,11.26,3.76,-0.88,40.5
2023FY,20.05,7.07,-1.52,40.6 2023FY,20.05,7.07,-1.52,40.6
2024H1,13.53,4.34,-0.84,37.4
2024H2,22.97,5.13,-0.80,32.2
2024FY,36.50,9.47,-1.63,34.0 2024FY,36.50,9.47,-1.63,34.0
2025H1,31.93,7.70,-0.66,28.1
2025H2,48.90,10.00,-0.11,25.3
2025FY,80.82,17.70,-0.76,26.3 2025FY,80.82,17.70,-0.76,26.3
2026H1,39.31,9.27,-0.11,28.2
1 period sales_expense admin_expense financial_expense four_expense_rate
2 2020H1 2.23 1.25 0.04 43.1
3 2020H2 4.07 1.55 0.05 33.4
4 2020FY 6.30 2.80 0.09 36.6
5 2021H1 4.20 2.40 -0.06 36.9
6 2021H2 6.86 3.18 -0.02 36.9
7 2021FY 11.06 5.58 -0.07 36.9
8 2022H1 6.93 3.23 -0.07 42.8
9 2022H2 7.78 3.64 -0.22 49.5
10 2022FY 14.71 6.86 -0.29 46.1
11 2023H1 8.78 3.31 -0.63 40.7
12 2023H2 11.26 3.76 -0.88 40.5
13 2023FY 20.05 7.07 -1.52 40.6
14 2024H1 13.53 4.34 -0.84 37.4
15 2024H2 22.97 5.13 -0.80 32.2
16 2024FY 36.50 9.47 -1.63 34.0
17 2025H1 31.93 7.70 -0.66 28.1
18 2025H2 48.90 10.00 -0.11 25.3
19 2025FY 80.82 17.70 -0.76 26.3
20 2026H1 39.31 9.27 -0.11 28.2
@@ -0,0 +1,7 @@
period,sales_expense,admin_expense,financial_expense,four_expense_rate
2020FY,6.30,2.80,0.09,36.6
2021FY,11.06,5.58,-0.07,36.9
2022FY,14.71,6.86,-0.29,46.1
2023FY,20.05,7.07,-1.52,40.6
2024FY,36.50,9.47,-1.63,34.0
2025FY,80.82,17.70,-0.76,26.3
1 period sales_expense admin_expense financial_expense four_expense_rate
2 2020FY 6.30 2.80 0.09 36.6
3 2021FY 11.06 5.58 -0.07 36.9
4 2022FY 14.71 6.86 -0.29 46.1
5 2023FY 20.05 7.07 -1.52 40.6
6 2024FY 36.50 9.47 -1.63 34.0
7 2025FY 80.82 17.70 -0.76 26.3
@@ -0,0 +1,14 @@
period,sales_expense,admin_expense,financial_expense,four_expense_rate
2020H1,2.23,1.25,0.04,43.1
2020H2,4.07,1.55,0.05,33.4
2021H1,4.20,2.40,-0.06,36.9
2021H2,6.86,3.18,-0.02,36.9
2022H1,6.93,3.23,-0.07,42.8
2022H2,7.78,3.64,-0.22,49.5
2023H1,8.78,3.31,-0.63,40.7
2023H2,11.26,3.76,-0.88,40.5
2024H1,13.53,4.34,-0.84,37.4
2024H2,22.97,5.13,-0.80,32.2
2025H1,31.93,7.70,-0.66,28.1
2025H2,48.90,10.00,-0.11,25.3
2026H1,39.31,9.27,-0.11,28.2
1 period sales_expense admin_expense financial_expense four_expense_rate
2 2020H1 2.23 1.25 0.04 43.1
3 2020H2 4.07 1.55 0.05 33.4
4 2021H1 4.20 2.40 -0.06 36.9
5 2021H2 6.86 3.18 -0.02 36.9
6 2022H1 6.93 3.23 -0.07 42.8
7 2022H2 7.78 3.64 -0.22 49.5
8 2023H1 8.78 3.31 -0.63 40.7
9 2023H2 11.26 3.76 -0.88 40.5
10 2024H1 13.53 4.34 -0.84 37.4
11 2024H2 22.97 5.13 -0.80 32.2
12 2025H1 31.93 7.70 -0.66 28.1
13 2025H2 48.90 10.00 -0.11 25.3
14 2026H1 39.31 9.27 -0.11 28.2
+17 -11
View File
@@ -1,14 +1,20 @@
period,non_gaap_net_profit,gross_profit,revenue,cost period,non_gaap_net_profit,gross_profit,revenue,cost
2020H1,23.7,61.0,50.6,33.3 2020H1,24.4,61.4,50.5,33.6
2020H2,16.2,46.2,49.3,55.0 2020H2,33.1,39.6,48.7,67.0
2021H1,154.6,109.6,116.7,131.0 2020FY,30.9,46.2,49.3,55.0
2021H2,69.5,73.1,78.7,88.5 2021H1,208.0,109.4,116.8,130.5
2022H1,-13.6,22.6,33.1,50.6 2021H2,26.1,54.8,60.3,69.5
2022H2,-42.7,-3.8,2.8,13.3 2021FY,69.6,73.1,78.7,88.4
2022H1,-13.5,22.7,33.1,50.8
2022H2,-65.2,-21.8,-16.9,-9.5
2022FY,-42.7,-3.8,2.8,13.3
2023H1,42.3,23.9,19.3,12.9 2023H1,42.3,23.9,19.3,12.9
2023H2,107.5,45.5,36.5,24.1 2023H2,232.1,68.7,54.4,35.6
2024H1,90.3,71.9,62.0,47.0 2023FY,107.6,45.6,36.5,24.2
2024H2,185.7,125.4,106.9,77.7 2024H1,90.1,71.9,62.0,46.9
2025H1,362.7,234.4,204.4,151.1 2024H2,264.1,167.3,143.2,103.7
2025H2,284.5,207.4,184.7,139.1 2024FY,185.9,125.4,106.9,77.7
2025H1,362.8,234.4,204.4,151.1
2025H2,251.0,193.7,174.1,131.9
2025FY,284.5,207.4,184.7,139.1
2026H1,9.5,22.6,23.8,26.5 2026H1,9.5,22.6,23.8,26.5
1 period non_gaap_net_profit gross_profit revenue cost
2 2020H1 23.7 24.4 61.0 61.4 50.6 50.5 33.3 33.6
3 2020H2 16.2 33.1 46.2 39.6 49.3 48.7 55.0 67.0
4 2021H1 2020FY 154.6 30.9 109.6 46.2 116.7 49.3 131.0 55.0
5 2021H2 2021H1 69.5 208.0 73.1 109.4 78.7 116.8 88.5 130.5
6 2022H1 2021H2 -13.6 26.1 22.6 54.8 33.1 60.3 50.6 69.5
7 2022H2 2021FY -42.7 69.6 -3.8 73.1 2.8 78.7 13.3 88.4
8 2022H1 -13.5 22.7 33.1 50.8
9 2022H2 -65.2 -21.8 -16.9 -9.5
10 2022FY -42.7 -3.8 2.8 13.3
11 2023H1 42.3 23.9 19.3 12.9
12 2023H2 107.5 232.1 45.5 68.7 36.5 54.4 24.1 35.6
13 2024H1 2023FY 90.3 107.6 71.9 45.6 62.0 36.5 47.0 24.2
14 2024H2 2024H1 185.7 90.1 125.4 71.9 106.9 62.0 77.7 46.9
15 2025H1 2024H2 362.7 264.1 234.4 167.3 204.4 143.2 151.1 103.7
16 2025H2 2024FY 284.5 185.9 207.4 125.4 184.7 106.9 139.1 77.7
17 2025H1 362.8 234.4 204.4 151.1
18 2025H2 251.0 193.7 174.1 131.9
19 2025FY 284.5 207.4 184.7 139.1
20 2026H1 9.5 22.6 23.8 26.5
+7
View File
@@ -0,0 +1,7 @@
period,non_gaap_net_profit,gross_profit,revenue,cost
2020FY,30.9,46.2,49.3,55.0
2021FY,69.6,73.1,78.7,88.4
2022FY,-42.7,-3.8,2.8,13.3
2023FY,107.6,45.6,36.5,24.2
2024FY,185.9,125.4,106.9,77.7
2025FY,284.5,207.4,184.7,139.1
1 period non_gaap_net_profit gross_profit revenue cost
2 2020FY 30.9 46.2 49.3 55.0
3 2021FY 69.6 73.1 78.7 88.4
4 2022FY -42.7 -3.8 2.8 13.3
5 2023FY 107.6 45.6 36.5 24.2
6 2024FY 185.9 125.4 106.9 77.7
7 2025FY 284.5 207.4 184.7 139.1
+14
View File
@@ -0,0 +1,14 @@
period,non_gaap_net_profit,gross_profit,revenue,cost
2020H1,24.4,61.4,50.5,33.6
2020H2,33.1,39.6,48.7,67.0
2021H1,208.0,109.4,116.8,130.5
2021H2,26.1,54.8,60.3,69.5
2022H1,-13.5,22.7,33.1,50.8
2022H2,-65.2,-21.8,-16.9,-9.5
2023H1,42.3,23.9,19.3,12.9
2023H2,232.1,68.7,54.4,35.6
2024H1,90.1,71.9,62.0,46.9
2024H2,264.1,167.3,143.2,103.7
2025H1,362.8,234.4,204.4,151.1
2025H2,251.0,193.7,174.1,131.9
2026H1,9.5,22.6,23.8,26.5
1 period non_gaap_net_profit gross_profit revenue cost
2 2020H1 24.4 61.4 50.5 33.6
3 2020H2 33.1 39.6 48.7 67.0
4 2021H1 208.0 109.4 116.8 130.5
5 2021H2 26.1 54.8 60.3 69.5
6 2022H1 -13.5 22.7 33.1 50.8
7 2022H2 -65.2 -21.8 -16.9 -9.5
8 2023H1 42.3 23.9 19.3 12.9
9 2023H2 232.1 68.7 54.4 35.6
10 2024H1 90.1 71.9 62.0 46.9
11 2024H2 264.1 167.3 143.2 103.7
12 2025H1 362.8 234.4 204.4 151.1
13 2025H2 251.0 193.7 174.1 131.9
14 2026H1 9.5 22.6 23.8 26.5
+17
View File
@@ -0,0 +1,17 @@
period,inventory,yoy
2017FY,0.16,
2018FY,0.29,87.0
2019FY,0.96,231.4
2020H1,2.24,
2020FY,2.25,134.0
2021H1,3.16,41.0
2021FY,7.89,250.0
2022H1,9.57,203.0
2022FY,8.67,9.9
2023H1,7.59,-20.8
2023FY,9.05,4.4
2024H1,9.17,20.8
2024FY,15.25,68.5
2025H1,22.74,148.0
2025FY,54.73,259.0
2026H1,61.02,168.4
1 period inventory yoy
2 2017FY 0.16
3 2018FY 0.29 87.0
4 2019FY 0.96 231.4
5 2020H1 2.24
6 2020FY 2.25 134.0
7 2021H1 3.16 41.0
8 2021FY 7.89 250.0
9 2022H1 9.57 203.0
10 2022FY 8.67 9.9
11 2023H1 7.59 -20.8
12 2023FY 9.05 4.4
13 2024H1 9.17 20.8
14 2024FY 15.25 68.5
15 2025H1 22.74 148.0
16 2025FY 54.73 259.0
17 2026H1 61.02 168.4
+10
View File
@@ -0,0 +1,10 @@
period,inventory,yoy
2017FY,0.16,
2018FY,0.29,87.0
2019FY,0.96,231.4
2020FY,2.25,134.0
2021FY,7.89,250.0
2022FY,8.67,9.9
2023FY,9.05,4.4
2024FY,15.25,68.5
2025FY,54.73,259.0
1 period inventory yoy
2 2017FY 0.16
3 2018FY 0.29 87.0
4 2019FY 0.96 231.4
5 2020FY 2.25 134.0
6 2021FY 7.89 250.0
7 2022FY 8.67 9.9
8 2023FY 9.05 4.4
9 2024FY 15.25 68.5
10 2025FY 54.73 259.0
+8
View File
@@ -0,0 +1,8 @@
period,inventory,yoy
2020H1,2.24,
2021H1,3.16,41.0
2022H1,9.57,203.0
2023H1,7.59,-20.8
2024H1,9.17,20.8
2025H1,22.74,148.0
2026H1,61.02,168.4
1 period inventory yoy
2 2020H1 2.24
3 2021H1 3.16 41.0
4 2022H1 9.57 203.0
5 2023H1 7.59 -20.8
6 2024H1 9.17 20.8
7 2025H1 22.74 148.0
8 2026H1 61.02 168.4
+9 -9
View File
@@ -1,9 +1,9 @@
ip,2021H1,2021H2,2022H1,2022H2,2023H1,2023H2,2024H1,2024H2,2025H1,2025H2 ip,2021H1,2021H2,2021FY,2022H1,2022H2,2022FY,2023H1,2023H2,2023FY,2024H1,2024H2,2024FY,2025H1,2025H2,2025FY
THE MONSTERS,1.45789,1.58642,1.34887,1.28115,1.59829,2.08042,6.26806,24.13858,48.14017,93.47047 THE MONSTERS,1.45789,1.58642,3.04431,1.34887,1.28115,2.63002,1.59829,2.08042,3.67871,6.26806,24.13858,30.406640000000003,48.14017,93.47047,141.61064
MOLLY,2.039,5.01198,4.04347,3.97808,4.11433,6.08872,7.8216,13.11012,13.57248,15.39836 MOLLY,2.039,5.01198,7.050980000000001,4.04347,3.97808,8.02155,4.11433,6.08872,10.203050000000001,7.8216,13.11012,20.93172,13.57248,15.39836,28.970840000000003
SKULLPANDA,1.83042,4.12214,4.61766,3.89845,5.26383,4.98405,5.74562,7.337,12.20504,23.19038 SKULLPANDA,1.83042,4.12214,5.95256,4.61766,3.89845,8.51611,5.26383,4.98405,10.247879999999999,5.74562,7.337,13.082619999999999,12.20504,23.19038,35.39542
CRYBABY,0,0,0,0,0.26603,0.44546,3.49382,8.15503,12.1817,17.10836 CRYBABY,0,0,0,0,0,0,0.26603,0.44546,0.71149,3.49382,8.15503,11.64885,12.1817,17.10836,29.29006
DIMOO,2.04993,3.61742,2.98442,2.79506,3.62032,3.75634,3.77838,5.31723,11.05135,16.7156 DIMOO,2.04993,3.61742,5.66735,2.98442,2.79506,5.7794799999999995,3.62032,3.75634,7.376659999999999,3.77838,5.31723,9.09561,11.05135,16.7156,27.766949999999998
HIRONO-小野,0,0,0.37538,1.0323,1.09352,2.41849,2.45247,4.81278,7.2842,10.06815 HIRONO-小野,0,0,0,0.37538,1.0323,1.40768,1.09352,2.41849,3.51201,2.45247,4.81278,7.26525,7.2842,10.06815,17.35235
HACIPUPU,0,0,0,0.42797,0.69171,0.82587,0.94991,1.29297,3.32122,5.15899 HACIPUPU,0,0,0,0,0.42797,0.42797,0.69171,0.82587,1.5175800000000002,0.94991,1.29297,2.24288,3.32122,5.15899,8.48021
星星人,0,0,0,0,0,0,0,0,3.89363,16.66217 星星人,0,0,0,0,0,0,0,0,0,0,0,0,3.89363,16.66217,20.555799999999998
1 ip 2021H1 2021H2 2021FY 2022H1 2022H2 2022FY 2023H1 2023H2 2023FY 2024H1 2024H2 2024FY 2025H1 2025H2 2025FY
2 THE MONSTERS 1.45789 1.58642 3.04431 1.34887 1.28115 2.63002 1.59829 2.08042 3.67871 6.26806 24.13858 30.406640000000003 48.14017 93.47047 141.61064
3 MOLLY 2.039 5.01198 7.050980000000001 4.04347 3.97808 8.02155 4.11433 6.08872 10.203050000000001 7.8216 13.11012 20.93172 13.57248 15.39836 28.970840000000003
4 SKULLPANDA 1.83042 4.12214 5.95256 4.61766 3.89845 8.51611 5.26383 4.98405 10.247879999999999 5.74562 7.337 13.082619999999999 12.20504 23.19038 35.39542
5 CRYBABY 0 0 0 0 0 0 0.26603 0.44546 0.71149 3.49382 8.15503 11.64885 12.1817 17.10836 29.29006
6 DIMOO 2.04993 3.61742 5.66735 2.98442 2.79506 5.7794799999999995 3.62032 3.75634 7.376659999999999 3.77838 5.31723 9.09561 11.05135 16.7156 27.766949999999998
7 HIRONO-小野 0 0 0 0.37538 1.0323 1.40768 1.09352 2.41849 3.51201 2.45247 4.81278 7.26525 7.2842 10.06815 17.35235
8 HACIPUPU 0 0 0 0 0.42797 0.42797 0.69171 0.82587 1.5175800000000002 0.94991 1.29297 2.24288 3.32122 5.15899 8.48021
9 星星人 0 0 0 0 0 0 0 0 0 0 0 0 3.89363 16.66217 20.555799999999998
+9
View File
@@ -0,0 +1,9 @@
ip,2021FY,2022FY,2023FY,2024FY,2025FY
THE MONSTERS,3.04431,2.63002,3.67871,30.406640000000003,141.61064
MOLLY,7.050980000000001,8.02155,10.203050000000001,20.93172,28.970840000000003
SKULLPANDA,5.95256,8.51611,10.247879999999999,13.082619999999999,35.39542
CRYBABY,0,0,0.71149,11.64885,29.29006
DIMOO,5.66735,5.7794799999999995,7.376659999999999,9.09561,27.766949999999998
HIRONO-小野,0,1.40768,3.51201,7.26525,17.35235
HACIPUPU,0,0.42797,1.5175800000000002,2.24288,8.48021
星星人,0,0,0,0,20.555799999999998
1 ip 2021FY 2022FY 2023FY 2024FY 2025FY
2 THE MONSTERS 3.04431 2.63002 3.67871 30.406640000000003 141.61064
3 MOLLY 7.050980000000001 8.02155 10.203050000000001 20.93172 28.970840000000003
4 SKULLPANDA 5.95256 8.51611 10.247879999999999 13.082619999999999 35.39542
5 CRYBABY 0 0 0.71149 11.64885 29.29006
6 DIMOO 5.66735 5.7794799999999995 7.376659999999999 9.09561 27.766949999999998
7 HIRONO-小野 0 1.40768 3.51201 7.26525 17.35235
8 HACIPUPU 0 0.42797 1.5175800000000002 2.24288 8.48021
9 星星人 0 0 0 0 20.555799999999998
+5
View File
@@ -1,12 +1,17 @@
period,revenue,yoy period,revenue,yoy
2021H1,16.27, 2021H1,16.27,
2021H2,25.59, 2021H2,25.59,
2021FY,41.86,
2022H1,22.24,36.7 2022H1,22.24,36.7
2022H2,21.30,-16.8 2022H2,21.30,-16.8
2022FY,43.54,4.0
2023H1,26.54,19.3 2023H1,26.54,19.3
2023H2,32.79,53.9 2023H2,32.79,53.9
2023FY,59.33,36.3
2024H1,39.31,48.1 2024H1,39.31,48.1
2024H2,60.66,85.0 2024H2,60.66,85.0
2024FY,99.97,68.5
2025H1,90.62,130.5 2025H1,90.62,130.5
2025H2,138.97,129.1 2025H2,138.97,129.1
2025FY,229.59,129.7
2026H1,127.19,40.4 2026H1,127.19,40.4
1 period revenue yoy
2 2021H1 16.27
3 2021H2 25.59
4 2021FY 41.86
5 2022H1 22.24 36.7
6 2022H2 21.30 -16.8
7 2022FY 43.54 4.0
8 2023H1 26.54 19.3
9 2023H2 32.79 53.9
10 2023FY 59.33 36.3
11 2024H1 39.31 48.1
12 2024H2 60.66 85.0
13 2024FY 99.97 68.5
14 2025H1 90.62 130.5
15 2025H2 138.97 129.1
16 2025FY 229.59 129.7
17 2026H1 127.19 40.4
+6
View File
@@ -0,0 +1,6 @@
period,revenue,yoy
2021FY,41.86,
2022FY,43.54,4.0
2023FY,59.33,36.3
2024FY,99.97,68.5
2025FY,229.59,129.7
1 period revenue yoy
2 2021FY 41.86
3 2022FY 43.54 4.0
4 2023FY 59.33 36.3
5 2024FY 99.97 68.5
6 2025FY 229.59 129.7
@@ -0,0 +1,12 @@
period,revenue,yoy
2021H1,16.27,
2021H2,25.59,
2022H1,22.24,36.7
2022H2,21.30,-16.8
2023H1,26.54,19.3
2023H2,32.79,53.9
2024H1,39.31,48.1
2024H2,60.66,85.0
2025H1,90.62,130.5
2025H2,138.97,129.1
2026H1,127.19,40.4
1 period revenue yoy
2 2021H1 16.27
3 2021H2 25.59
4 2022H1 22.24 36.7
5 2022H2 21.30 -16.8
6 2023H1 26.54 19.3
7 2023H2 32.79 53.9
8 2024H1 39.31 48.1
9 2024H2 60.66 85.0
10 2025H1 90.62 130.5
11 2025H2 138.97 129.1
12 2026H1 127.19 40.4
+9
View File
@@ -0,0 +1,9 @@
ip,2021H1,2021H2,2022H1,2022H2,2023H1,2023H2,2024H1,2024H2,2025H1,2025H2
THE MONSTERS,1.45789,1.58642,1.34887,1.28115,1.59829,2.08042,6.26806,24.13858,48.14017,93.47047
MOLLY,2.039,5.01198,4.04347,3.97808,4.11433,6.08872,7.8216,13.11012,13.57248,15.39836
SKULLPANDA,1.83042,4.12214,4.61766,3.89845,5.26383,4.98405,5.74562,7.337,12.20504,23.19038
CRYBABY,0,0,0,0,0.26603,0.44546,3.49382,8.15503,12.1817,17.10836
DIMOO,2.04993,3.61742,2.98442,2.79506,3.62032,3.75634,3.77838,5.31723,11.05135,16.7156
HIRONO-小野,0,0,0.37538,1.0323,1.09352,2.41849,2.45247,4.81278,7.2842,10.06815
HACIPUPU,0,0,0,0.42797,0.69171,0.82587,0.94991,1.29297,3.32122,5.15899
星星人,0,0,0,0,0,0,0,0,3.89363,16.66217
1 ip 2021H1 2021H2 2022H1 2022H2 2023H1 2023H2 2024H1 2024H2 2025H1 2025H2
2 THE MONSTERS 1.45789 1.58642 1.34887 1.28115 1.59829 2.08042 6.26806 24.13858 48.14017 93.47047
3 MOLLY 2.039 5.01198 4.04347 3.97808 4.11433 6.08872 7.8216 13.11012 13.57248 15.39836
4 SKULLPANDA 1.83042 4.12214 4.61766 3.89845 5.26383 4.98405 5.74562 7.337 12.20504 23.19038
5 CRYBABY 0 0 0 0 0.26603 0.44546 3.49382 8.15503 12.1817 17.10836
6 DIMOO 2.04993 3.61742 2.98442 2.79506 3.62032 3.75634 3.77838 5.31723 11.05135 16.7156
7 HIRONO-小野 0 0 0.37538 1.0323 1.09352 2.41849 2.45247 4.81278 7.2842 10.06815
8 HACIPUPU 0 0 0 0.42797 0.69171 0.82587 0.94991 1.29297 3.32122 5.15899
9 星星人 0 0 0 0 0 0 0 0 3.89363 16.66217
+12 -12
View File
@@ -1,12 +1,12 @@
ip,2021H1,2021H2,2022H1,2022H2,2023H1,2023H2,2024H1,2024H2,2025H1,2025H2,2026H1 ip,2021H1,2021H2,2021FY,2022H1,2022H2,2022FY,2023H1,2023H2,2023FY,2024H1,2024H2,2024FY,2025H1,2025H2,2025FY,2026H1
THE MONSTERS,1.46,1.59,1.35,1.28,1.60,2.08,6.27,24.14,48.14,93.47,44.54 THE MONSTERS,1.46,1.59,3.05,1.35,1.28,2.63,1.6,2.08,3.68,6.27,24.14,30.41,48.14,93.47,141.61,44.54
星星人,0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,3.89,16.66,26.50 星星人,0,0,0,0,0,0,0,0,0,0,0,0,3.89,16.66,20.55,26.5
授权IP(非独家),2.88,3.33,3.01,2.50,4.22,6.14,6.95,9.06,15.25,18.57,17.71 授权IP(非独家),2.88,3.33,6.21,3.01,2.5,5.51,4.22,6.14,10.36,6.95,9.06,16.01,15.25,18.57,33.82,17.71
CRYBABY,0.00,0.00,0.00,0.00,0.27,0.45,3.49,8.16,12.18,17.11,16.33 CRYBABY,0,0,0,0,0,0,0.27,0.45,0.72,3.49,8.16,11.65,12.18,17.11,29.29,16.33
DIMOO,2.05,3.62,2.98,2.80,3.62,3.76,3.78,5.32,11.05,16.72,16.19 DIMOO,2.05,3.62,5.67,2.98,2.8,5.779999999999999,3.62,3.76,7.38,3.78,5.32,9.1,11.05,16.72,27.77,16.19
SKULLPANDA,1.83,4.12,4.62,3.90,5.26,4.98,5.75,7.34,12.21,23.19,15.51 SKULLPANDA,1.83,4.12,5.95,4.62,3.9,8.52,5.26,4.98,10.24,5.75,7.34,13.09,12.21,23.19,35.400000000000006,15.51
其他艺术家IP,1.43,2.47,4.37,3.00,4.54,5.44,6.37,7.72,10.64,14.00,11.74 其他艺术家IP,1.43,2.47,3.9000000000000004,4.37,3,7.37,4.54,5.44,9.98,6.37,7.72,14.09,10.64,14,24.64,11.74
HIRONO-小野,0.00,0.00,0.38,1.03,1.09,2.42,2.45,4.81,7.28,10.07,10.09 HIRONO-小野,0,0,0,0.38,1.03,1.4100000000000001,1.09,2.42,3.51,2.45,4.81,7.26,7.28,10.07,17.35,10.09
MOLLY,2.04,5.01,4.04,3.98,4.11,6.09,7.82,13.11,13.57,15.40,9.01 MOLLY,2.04,5.01,7.05,4.04,3.98,8.02,4.11,6.09,10.2,7.82,13.11,20.93,13.57,15.4,28.97,9.01
其他IP(含已停单列),4.14,4.51,0.81,1.86,1.15,1.37,0.95,3.74,3.32,5.16,2.98 其他IP(含已停单列),4.14,4.51,8.649999999999999,0.81,1.86,2.67,1.15,1.37,2.52,0.95,3.74,4.69,3.32,5.16,8.48,2.98
外采及其他,1.90,2.53,2.03,2.24,2.28,2.15,1.75,1.41,1.23,2.09,1.12 外采及其他,1.9,2.53,4.43,2.03,2.24,4.27,2.28,2.15,4.43,1.75,1.41,3.16,1.23,2.09,3.32,1.12
1 ip 2021H1 2021H2 2021FY 2022H1 2022H2 2022FY 2023H1 2023H2 2023FY 2024H1 2024H2 2024FY 2025H1 2025H2 2025FY 2026H1
2 THE MONSTERS 1.46 1.59 3.05 1.35 1.28 2.63 1.60 1.6 2.08 3.68 6.27 24.14 30.41 48.14 93.47 141.61 44.54
3 星星人 0.00 0 0.00 0 0 0.00 0 0.00 0 0 0.00 0 0.00 0 0 0.00 0 0.00 0 0 3.89 16.66 20.55 26.50 26.5
4 授权IP(非独家) 2.88 3.33 6.21 3.01 2.50 2.5 5.51 4.22 6.14 10.36 6.95 9.06 16.01 15.25 18.57 33.82 17.71
5 CRYBABY 0.00 0 0.00 0 0 0.00 0 0.00 0 0 0.27 0.45 0.72 3.49 8.16 11.65 12.18 17.11 29.29 16.33
6 DIMOO 2.05 3.62 5.67 2.98 2.80 2.8 5.779999999999999 3.62 3.76 7.38 3.78 5.32 9.1 11.05 16.72 27.77 16.19
7 SKULLPANDA 1.83 4.12 5.95 4.62 3.90 3.9 8.52 5.26 4.98 10.24 5.75 7.34 13.09 12.21 23.19 35.400000000000006 15.51
8 其他艺术家IP 1.43 2.47 3.9000000000000004 4.37 3.00 3 7.37 4.54 5.44 9.98 6.37 7.72 14.09 10.64 14.00 14 24.64 11.74
9 HIRONO-小野 0.00 0 0.00 0 0 0.38 1.03 1.4100000000000001 1.09 2.42 3.51 2.45 4.81 7.26 7.28 10.07 17.35 10.09
10 MOLLY 2.04 5.01 7.05 4.04 3.98 8.02 4.11 6.09 10.2 7.82 13.11 20.93 13.57 15.40 15.4 28.97 9.01
11 其他IP(含已停单列) 4.14 4.51 8.649999999999999 0.81 1.86 2.67 1.15 1.37 2.52 0.95 3.74 4.69 3.32 5.16 8.48 2.98
12 外采及其他 1.90 1.9 2.53 4.43 2.03 2.24 4.27 2.28 2.15 4.43 1.75 1.41 3.16 1.23 2.09 3.32 1.12
@@ -0,0 +1,12 @@
ip,2021FY,2022FY,2023FY,2024FY,2025FY
THE MONSTERS,3.05,2.63,3.68,30.41,141.61
星星人,0,0,0,0,20.55
授权IP(非独家),6.21,5.51,10.36,16.01,33.82
CRYBABY,0,0,0.72,11.65,29.29
DIMOO,5.67,5.779999999999999,7.38,9.1,27.77
SKULLPANDA,5.95,8.52,10.24,13.09,35.400000000000006
其他艺术家IP,3.9000000000000004,7.37,9.98,14.09,24.64
HIRONO-小野,0,1.4100000000000001,3.51,7.26,17.35
MOLLY,7.05,8.02,10.2,20.93,28.97
其他IP(含已停单列),8.649999999999999,2.67,2.52,4.69,8.48
外采及其他,4.43,4.27,4.43,3.16,3.32
1 ip 2021FY 2022FY 2023FY 2024FY 2025FY
2 THE MONSTERS 3.05 2.63 3.68 30.41 141.61
3 星星人 0 0 0 0 20.55
4 授权IP(非独家) 6.21 5.51 10.36 16.01 33.82
5 CRYBABY 0 0 0.72 11.65 29.29
6 DIMOO 5.67 5.779999999999999 7.38 9.1 27.77
7 SKULLPANDA 5.95 8.52 10.24 13.09 35.400000000000006
8 其他艺术家IP 3.9000000000000004 7.37 9.98 14.09 24.64
9 HIRONO-小野 0 1.4100000000000001 3.51 7.26 17.35
10 MOLLY 7.05 8.02 10.2 20.93 28.97
11 其他IP(含已停单列) 8.649999999999999 2.67 2.52 4.69 8.48
12 外采及其他 4.43 4.27 4.43 3.16 3.32
@@ -0,0 +1,12 @@
ip,2021H1,2021H2,2022H1,2022H2,2023H1,2023H2,2024H1,2024H2,2025H1,2025H2,2026H1
THE MONSTERS,1.46,1.59,1.35,1.28,1.6,2.08,6.27,24.14,48.14,93.47,44.54
星星人,0,0,0,0,0,0,0,0,3.89,16.66,26.5
授权IP(非独家),2.88,3.33,3.01,2.5,4.22,6.14,6.95,9.06,15.25,18.57,17.71
CRYBABY,0,0,0,0,0.27,0.45,3.49,8.16,12.18,17.11,16.33
DIMOO,2.05,3.62,2.98,2.8,3.62,3.76,3.78,5.32,11.05,16.72,16.19
SKULLPANDA,1.83,4.12,4.62,3.9,5.26,4.98,5.75,7.34,12.21,23.19,15.51
其他艺术家IP,1.43,2.47,4.37,3,4.54,5.44,6.37,7.72,10.64,14,11.74
HIRONO-小野,0,0,0.38,1.03,1.09,2.42,2.45,4.81,7.28,10.07,10.09
MOLLY,2.04,5.01,4.04,3.98,4.11,6.09,7.82,13.11,13.57,15.4,9.01
其他IP(含已停单列),4.14,4.51,0.81,1.86,1.15,1.37,0.95,3.74,3.32,5.16,2.98
外采及其他,1.9,2.53,2.03,2.24,2.28,2.15,1.75,1.41,1.23,2.09,1.12
1 ip 2021H1 2021H2 2022H1 2022H2 2023H1 2023H2 2024H1 2024H2 2025H1 2025H2 2026H1
2 THE MONSTERS 1.46 1.59 1.35 1.28 1.6 2.08 6.27 24.14 48.14 93.47 44.54
3 星星人 0 0 0 0 0 0 0 0 3.89 16.66 26.5
4 授权IP(非独家) 2.88 3.33 3.01 2.5 4.22 6.14 6.95 9.06 15.25 18.57 17.71
5 CRYBABY 0 0 0 0 0.27 0.45 3.49 8.16 12.18 17.11 16.33
6 DIMOO 2.05 3.62 2.98 2.8 3.62 3.76 3.78 5.32 11.05 16.72 16.19
7 SKULLPANDA 1.83 4.12 4.62 3.9 5.26 4.98 5.75 7.34 12.21 23.19 15.51
8 其他艺术家IP 1.43 2.47 4.37 3 4.54 5.44 6.37 7.72 10.64 14 11.74
9 HIRONO-小野 0 0 0.38 1.03 1.09 2.42 2.45 4.81 7.28 10.07 10.09
10 MOLLY 2.04 5.01 4.04 3.98 4.11 6.09 7.82 13.11 13.57 15.4 9.01
11 其他IP(含已停单列) 4.14 4.51 0.81 1.86 1.15 1.37 0.95 3.74 3.32 5.16 2.98
12 外采及其他 1.9 2.53 2.03 2.24 2.28 2.15 1.75 1.41 1.23 2.09 1.12
+6 -6
View File
@@ -1,14 +1,14 @@
period,cash,total_liabilities,net_working_capital period,cash,total_liabilities,net_working_capital
2020H1,8.22,5.47,8.66 2020H1,8.22,5.47,8.66
2020FY,56.80,8.40,55.91 2020H2,56.80,8.40,55.91
2021H1,57.67,13.26,55.92 2021H1,57.67,13.26,55.92
2021FY,52.65,15.04,56.65 2021H2,52.65,15.04,56.65
2022H1,51.63,14.76,58.16 2022H1,51.63,14.76,58.16
2022FY,50.42,16.15,54.85 2022H2,50.42,16.15,54.85
2023H1,55.40,17.59,57.33 2023H1,55.40,17.59,57.33
2023FY,59.63,21.88,59.50 2023H2,59.63,21.88,59.50
2024H1,70.10,25.65,66.47 2024H1,70.10,25.65,66.47
2024FY,96.20,39.86,88.66 2024H2,96.20,39.86,88.66
2025H1,137.66,69.27,119.27 2025H1,137.66,69.27,119.27
2025FY,172.25,94.49,177.46 2025H2,172.25,94.49,177.46
2026H1,139.62,77.40,174.20 2026H1,139.62,77.40,174.20
1 period cash total_liabilities net_working_capital
2 2020H1 8.22 5.47 8.66
3 2020FY 2020H2 56.80 8.40 55.91
4 2021H1 57.67 13.26 55.92
5 2021FY 2021H2 52.65 15.04 56.65
6 2022H1 51.63 14.76 58.16
7 2022FY 2022H2 50.42 16.15 54.85
8 2023H1 55.40 17.59 57.33
9 2023FY 2023H2 59.63 21.88 59.50
10 2024H1 70.10 25.65 66.47
11 2024FY 2024H2 96.20 39.86 88.66
12 2025H1 137.66 69.27 119.27
13 2025FY 2025H2 172.25 94.49 177.46
14 2026H1 139.62 77.40 174.20
@@ -0,0 +1,7 @@
period,cash,total_liabilities,net_working_capital
2020FY,56.80,8.40,55.91
2021FY,52.65,15.04,56.65
2022FY,50.42,16.15,54.85
2023FY,59.63,21.88,59.50
2024FY,96.20,39.86,88.66
2025FY,172.25,94.49,177.46
1 period cash total_liabilities net_working_capital
2 2020FY 56.80 8.40 55.91
3 2021FY 52.65 15.04 56.65
4 2022FY 50.42 16.15 54.85
5 2023FY 59.63 21.88 59.50
6 2024FY 96.20 39.86 88.66
7 2025FY 172.25 94.49 177.46
@@ -0,0 +1,14 @@
period,cash,total_liabilities,net_working_capital
2020H1,8.22,5.47,8.66
2020H2,56.80,8.40,55.91
2021H1,57.67,13.26,55.92
2021H2,52.65,15.04,56.65
2022H1,51.63,14.76,58.16
2022H2,50.42,16.15,54.85
2023H1,55.40,17.59,57.33
2023H2,59.63,21.88,59.50
2024H1,70.10,25.65,66.47
2024H2,96.20,39.86,88.66
2025H1,137.66,69.27,119.27
2025H2,172.25,94.49,177.46
2026H1,139.62,77.40,174.20
1 period cash total_liabilities net_working_capital
2 2020H1 8.22 5.47 8.66
3 2020H2 56.80 8.40 55.91
4 2021H1 57.67 13.26 55.92
5 2021H2 52.65 15.04 56.65
6 2022H1 51.63 14.76 58.16
7 2022H2 50.42 16.15 54.85
8 2023H1 55.40 17.59 57.33
9 2023H2 59.63 21.88 59.50
10 2024H1 70.10 25.65 66.47
11 2024H2 96.20 39.86 88.66
12 2025H1 137.66 69.27 119.27
13 2025H2 172.25 94.49 177.46
14 2026H1 139.62 77.40 174.20
+17 -11
View File
@@ -1,14 +1,20 @@
period,gross_margin,sales_expense_rate,admin_expense_rate,non_gaap_net_margin period,gross_margin,sales_expense_rate,admin_expense_rate,non_gaap_net_margin
2020H1,65.23,27.27,15.33,17.24 2020H1,65.23,27.27,15.33,17.28
2020H2,63.42,25.07,11.14,23.52 2020H2,62.55,24.00,9.12,26.49
2021H1,63.02,23.68,13.52,24.53 2020FY,63.42,25.07,11.14,23.49
2021H2,61.43,24.63,12.41,22.31 2021H1,63.02,23.68,13.52,24.55
2022H1,58.1,29.39,13.68,15.94 2021H2,60.39,25.25,11.69,20.84
2022H2,57.49,31.85,14.86,12.43 2021FY,61.43,24.63,12.41,22.30
2023H1,60.36,31.21,11.77,19.01 2022H1,58.10,29.39,13.68,15.95
2023H2,61.32,31.82,11.23,18.9 2022H2,56.86,34.43,16.10,8.73
2022FY,57.49,31.85,14.86,12.42
2023H1,60.36,31.21,11.77,19.03
2023H2,62.10,32.30,10.78,18.79
2023FY,61.32,31.82,11.23,18.89
2024H1,64.05,29.69,9.53,22.33 2024H1,64.05,29.69,9.53,22.33
2024H2,66.79,28.0,7.26,26.1 2024H2,68.26,27.09,6.05,28.13
2024FY,66.79,28.00,7.26,26.10
2025H1,70.34,23.01,5.55,33.94 2025H1,70.34,23.01,5.55,33.94
2025H2,72.1,21.77,4.77,35.25 2025H2,73.15,21.04,4.30,36.03
2026H1,69.68,22.89,5.4,30.02 2025FY,72.10,21.77,4.77,35.25
2026H1,69.68,22.89,5.40,30.02
1 period gross_margin sales_expense_rate admin_expense_rate non_gaap_net_margin
2 2020H1 65.23 27.27 15.33 17.24 17.28
3 2020H2 63.42 62.55 25.07 24.00 11.14 9.12 23.52 26.49
4 2021H1 2020FY 63.02 63.42 23.68 25.07 13.52 11.14 24.53 23.49
5 2021H2 2021H1 61.43 63.02 24.63 23.68 12.41 13.52 22.31 24.55
6 2022H1 2021H2 58.1 60.39 29.39 25.25 13.68 11.69 15.94 20.84
7 2022H2 2021FY 57.49 61.43 31.85 24.63 14.86 12.41 12.43 22.30
8 2023H1 2022H1 60.36 58.10 31.21 29.39 11.77 13.68 19.01 15.95
9 2023H2 2022H2 61.32 56.86 31.82 34.43 11.23 16.10 18.9 8.73
10 2022FY 57.49 31.85 14.86 12.42
11 2023H1 60.36 31.21 11.77 19.03
12 2023H2 62.10 32.30 10.78 18.79
13 2023FY 61.32 31.82 11.23 18.89
14 2024H1 64.05 29.69 9.53 22.33
15 2024H2 66.79 68.26 28.0 27.09 7.26 6.05 26.1 28.13
16 2024FY 66.79 28.00 7.26 26.10
17 2025H1 70.34 23.01 5.55 33.94
18 2025H2 72.1 73.15 21.77 21.04 4.77 4.30 35.25 36.03
19 2026H1 2025FY 69.68 72.10 22.89 21.77 5.4 4.77 30.02 35.25
20 2026H1 69.68 22.89 5.40 30.02
+7
View File
@@ -0,0 +1,7 @@
period,gross_margin,sales_expense_rate,admin_expense_rate,non_gaap_net_margin
2020FY,63.42,25.07,11.14,23.49
2021FY,61.43,24.63,12.41,22.30
2022FY,57.49,31.85,14.86,12.42
2023FY,61.32,31.82,11.23,18.89
2024FY,66.79,28.00,7.26,26.10
2025FY,72.10,21.77,4.77,35.25
1 period gross_margin sales_expense_rate admin_expense_rate non_gaap_net_margin
2 2020FY 63.42 25.07 11.14 23.49
3 2021FY 61.43 24.63 12.41 22.30
4 2022FY 57.49 31.85 14.86 12.42
5 2023FY 61.32 31.82 11.23 18.89
6 2024FY 66.79 28.00 7.26 26.10
7 2025FY 72.10 21.77 4.77 35.25
+14
View File
@@ -0,0 +1,14 @@
period,gross_margin,sales_expense_rate,admin_expense_rate,non_gaap_net_margin
2020H1,65.23,27.27,15.33,17.28
2020H2,62.55,24.00,9.12,26.49
2021H1,63.02,23.68,13.52,24.55
2021H2,60.39,25.25,11.69,20.84
2022H1,58.10,29.39,13.68,15.95
2022H2,56.86,34.43,16.10,8.73
2023H1,60.36,31.21,11.77,19.03
2023H2,62.10,32.30,10.78,18.79
2024H1,64.05,29.69,9.53,22.33
2024H2,68.26,27.09,6.05,28.13
2025H1,70.34,23.01,5.55,33.94
2025H2,73.15,21.04,4.30,36.03
2026H1,69.68,22.89,5.40,30.02
1 period gross_margin sales_expense_rate admin_expense_rate non_gaap_net_margin
2 2020H1 65.23 27.27 15.33 17.28
3 2020H2 62.55 24.00 9.12 26.49
4 2021H1 63.02 23.68 13.52 24.55
5 2021H2 60.39 25.25 11.69 20.84
6 2022H1 58.10 29.39 13.68 15.95
7 2022H2 56.86 34.43 16.10 8.73
8 2023H1 60.36 31.21 11.77 19.03
9 2023H2 62.10 32.30 10.78 18.79
10 2024H1 64.05 29.69 9.53 22.33
11 2024H2 68.26 27.09 6.05 28.13
12 2025H1 70.34 23.01 5.55 33.94
13 2025H2 73.15 21.04 4.30 36.03
14 2026H1 69.68 22.89 5.40 30.02
+18 -16
View File
@@ -1,18 +1,20 @@
period,revenue,gross_profit,gross_margin,net_profit period,revenue,gross_profit,gross_margin,net_profit
2026H1,171.73,119.66,69.67999999999999,50.38
2025H2,371.2,267.65,72.1,127.76
2025H1,138.76,97.61,70.34,45.74
2024H2,130.38,87.08,66.79,31.25
2024H1,45.58,29.19,64.05,9.21
2023H2,63.01,38.64,61.31999999999999,10.82
2023H1,28.14,16.98,60.36,4.77
2022H2,46.17,26.55,57.489999999999995,4.76
2022H1,23.59,13.7,58.099999999999994,3.33
2021H2,44.91,27.59,61.42999999999999,8.54
2021H1,17.73,11.17,63.019999999999996,3.59
2020H2,25.13,15.94,63.42,5.24
2020H1,8.18,5.33,65.23,1.41 2020H1,8.18,5.33,65.23,1.41
2019H2,16.83,10.9,64.77000000000001,4.51 2020H2,16.96,10.61,62.55,3.82
2019H1,5.43,3.31,60.84,1.14 2020FY,25.13,15.94,63.42,5.23
2018H2,5.15,2.98,57.92,1 2021H1,17.73,11.17,63.02,3.59
2017H2,1.58,0.75,47.61,0.02 2021H2,27.18,16.42,60.39,4.96
2021FY,44.91,27.59,61.43,8.55
2022H1,23.59,13.70,58.10,3.33
2022H2,22.59,12.84,56.86,1.43
2022FY,46.17,26.55,57.49,4.76
2023H1,28.14,16.98,60.36,4.77
2023H2,34.87,21.66,62.10,6.12
2023FY,63.01,38.64,61.32,10.89
2024H1,45.58,29.19,64.05,9.64
2024H2,84.80,57.89,68.26,23.44
2024FY,130.38,87.08,66.79,33.08
2025H1,138.76,97.61,70.34,46.82
2025H2,232.44,170.04,73.15,83.30
2025FY,371.20,267.65,72.10,130.12
2026H1,171.73,119.66,69.68,51.00
1 period revenue gross_profit gross_margin net_profit
2026H1 171.73 119.66 69.67999999999999 50.38
2025H2 371.2 267.65 72.1 127.76
2025H1 138.76 97.61 70.34 45.74
2024H2 130.38 87.08 66.79 31.25
2024H1 45.58 29.19 64.05 9.21
2023H2 63.01 38.64 61.31999999999999 10.82
2023H1 28.14 16.98 60.36 4.77
2022H2 46.17 26.55 57.489999999999995 4.76
2022H1 23.59 13.7 58.099999999999994 3.33
2021H2 44.91 27.59 61.42999999999999 8.54
2021H1 17.73 11.17 63.019999999999996 3.59
2020H2 25.13 15.94 63.42 5.24
2 2020H1 8.18 5.33 65.23 1.41
3 2019H2 2020H2 16.83 16.96 10.9 10.61 64.77000000000001 62.55 4.51 3.82
4 2019H1 2020FY 5.43 25.13 3.31 15.94 60.84 63.42 1.14 5.23
5 2018H2 2021H1 5.15 17.73 2.98 11.17 57.92 63.02 1 3.59
6 2017H2 2021H2 1.58 27.18 0.75 16.42 47.61 60.39 0.02 4.96
7 2021FY 44.91 27.59 61.43 8.55
8 2022H1 23.59 13.70 58.10 3.33
9 2022H2 22.59 12.84 56.86 1.43
10 2022FY 46.17 26.55 57.49 4.76
11 2023H1 28.14 16.98 60.36 4.77
12 2023H2 34.87 21.66 62.10 6.12
13 2023FY 63.01 38.64 61.32 10.89
14 2024H1 45.58 29.19 64.05 9.64
15 2024H2 84.80 57.89 68.26 23.44
16 2024FY 130.38 87.08 66.79 33.08
17 2025H1 138.76 97.61 70.34 46.82
18 2025H2 232.44 170.04 73.15 83.30
19 2025FY 371.20 267.65 72.10 130.12
20 2026H1 171.73 119.66 69.68 51.00
+7
View File
@@ -0,0 +1,7 @@
period,revenue,gross_profit,gross_margin,net_profit
2020FY,25.13,15.94,63.42,5.23
2021FY,44.91,27.59,61.43,8.55
2022FY,46.17,26.55,57.49,4.76
2023FY,63.01,38.64,61.32,10.89
2024FY,130.38,87.08,66.79,33.08
2025FY,371.20,267.65,72.10,130.12
1 period revenue gross_profit gross_margin net_profit
2 2020FY 25.13 15.94 63.42 5.23
3 2021FY 44.91 27.59 61.43 8.55
4 2022FY 46.17 26.55 57.49 4.76
5 2023FY 63.01 38.64 61.32 10.89
6 2024FY 130.38 87.08 66.79 33.08
7 2025FY 371.20 267.65 72.10 130.12
+14
View File
@@ -0,0 +1,14 @@
period,revenue,gross_profit,gross_margin,net_profit
2020H1,8.18,5.33,65.23,1.41
2020H2,16.96,10.61,62.55,3.82
2021H1,17.73,11.17,63.02,3.59
2021H2,27.18,16.42,60.39,4.96
2022H1,23.59,13.70,58.10,3.33
2022H2,22.59,12.84,56.86,1.43
2023H1,28.14,16.98,60.36,4.77
2023H2,34.87,21.66,62.10,6.12
2024H1,45.58,29.19,64.05,9.64
2024H2,84.80,57.89,68.26,23.44
2025H1,138.76,97.61,70.34,46.82
2025H2,232.44,170.04,73.15,83.30
2026H1,171.73,119.66,69.68,51.00
1 period revenue gross_profit gross_margin net_profit
2 2020H1 8.18 5.33 65.23 1.41
3 2020H2 16.96 10.61 62.55 3.82
4 2021H1 17.73 11.17 63.02 3.59
5 2021H2 27.18 16.42 60.39 4.96
6 2022H1 23.59 13.70 58.10 3.33
7 2022H2 22.59 12.84 56.86 1.43
8 2023H1 28.14 16.98 60.36 4.77
9 2023H2 34.87 21.66 62.10 6.12
10 2024H1 45.58 29.19 64.05 9.64
11 2024H2 84.80 57.89 68.26 23.44
12 2025H1 138.76 97.61 70.34 46.82
13 2025H2 232.44 170.04 73.15 83.30
14 2026H1 171.73 119.66 69.68 51.00
+8
View File
@@ -0,0 +1,8 @@
period,china,apac,americas,europe,total
2024H1,35.22,7.97,1.82,0.58,45.59
2024H2,53.65,23.13,6.20,1.81,84.79
2024FY,88.86,31.10,8.02,2.39,130.37
2025H1,82.83,28.51,22.65,4.77,138.76
2025H2,125.69,51.60,45.41,9.74,232.44
2025FY,208.52,80.11,68.06,14.51,371.20
2026H1,122.01,25.75,18.92,5.06,171.74
1 period china apac americas europe total
2 2024H1 35.22 7.97 1.82 0.58 45.59
3 2024H2 53.65 23.13 6.20 1.81 84.79
4 2024FY 88.86 31.10 8.02 2.39 130.37
5 2025H1 82.83 28.51 22.65 4.77 138.76
6 2025H2 125.69 51.60 45.41 9.74 232.44
7 2025FY 208.52 80.11 68.06 14.51 371.20
8 2026H1 122.01 25.75 18.92 5.06 171.74
+3
View File
@@ -0,0 +1,3 @@
period,china,apac,americas,europe,total
2024FY,88.86,31.10,8.02,2.39,130.37
2025FY,208.52,80.11,68.06,14.51,371.20
1 period china apac americas europe total
2 2024FY 88.86 31.10 8.02 2.39 130.37
3 2025FY 208.52 80.11 68.06 14.51 371.20
+6
View File
@@ -0,0 +1,6 @@
period,china,apac,americas,europe,total
2024H1,35.22,7.97,1.82,0.58,45.59
2024H2,53.65,23.13,6.20,1.81,84.79
2025H1,82.83,28.51,22.65,4.77,138.76
2025H2,125.69,51.60,45.41,9.74,232.44
2026H1,122.01,25.75,18.92,5.06,171.74
1 period china apac americas europe total
2 2024H1 35.22 7.97 1.82 0.58 45.59
3 2024H2 53.65 23.13 6.20 1.81 84.79
4 2025H1 82.83 28.51 22.65 4.77 138.76
5 2025H2 125.69 51.60 45.41 9.74 232.44
6 2026H1 122.01 25.75 18.92 5.06 171.74
+8
View File
@@ -0,0 +1,8 @@
period,china_retail_revenue,china_average_stores,china_per_store,apac_retail_revenue,apac_average_stores,apac_per_store,americas_retail_revenue,americas_average_stores,americas_per_store,europe_other_retail_revenue,europe_other_average_stores,europe_other_per_store
2024H1,20.04,381.0,526.0,,,,,,,,,
2024H2,25.97,415.0,625.8,11.83,46.5,2544.1,2.54,16.0,1587.5,1.16,11.5,1008.7
2024FY,46.01,397.0,1158.9,,,,,,,,,
2025H1,44.06,437.0,1008.2,14.93,61.5,2427.6,7.37,31.5,2339.7,2.72,16.0,1700.0
2025H2,56.69,444.0,1276.8,28.26,77.0,3670.1,12.67,52.5,2413.3,5.84,27.0,2163.0
2025FY,100.75,438.0,2300.2,43.19,69.5,6214.4,20.04,43.0,4660.5,8.56,25.0,3424.0
2026H1,60.90,450.0,1353.3,17.16,87.5,1961.1,9.03,75.0,1204.0,4.03,40.5,995.1
1 period china_retail_revenue china_average_stores china_per_store apac_retail_revenue apac_average_stores apac_per_store americas_retail_revenue americas_average_stores americas_per_store europe_other_retail_revenue europe_other_average_stores europe_other_per_store
2 2024H1 20.04 381.0 526.0
3 2024H2 25.97 415.0 625.8 11.83 46.5 2544.1 2.54 16.0 1587.5 1.16 11.5 1008.7
4 2024FY 46.01 397.0 1158.9
5 2025H1 44.06 437.0 1008.2 14.93 61.5 2427.6 7.37 31.5 2339.7 2.72 16.0 1700.0
6 2025H2 56.69 444.0 1276.8 28.26 77.0 3670.1 12.67 52.5 2413.3 5.84 27.0 2163.0
7 2025FY 100.75 438.0 2300.2 43.19 69.5 6214.4 20.04 43.0 4660.5 8.56 25.0 3424.0
8 2026H1 60.90 450.0 1353.3 17.16 87.5 1961.1 9.03 75.0 1204.0 4.03 40.5 995.1
@@ -0,0 +1,3 @@
period,china_retail_revenue,china_average_stores,china_per_store,apac_retail_revenue,apac_average_stores,apac_per_store,americas_retail_revenue,americas_average_stores,americas_per_store,europe_other_retail_revenue,europe_other_average_stores,europe_other_per_store
2024FY,46.01,397.0,1158.9,,,,,,,,,
2025FY,100.75,438.0,2300.2,43.19,69.5,6214.4,20.04,43.0,4660.5,8.56,25.0,3424.0
1 period china_retail_revenue china_average_stores china_per_store apac_retail_revenue apac_average_stores apac_per_store americas_retail_revenue americas_average_stores americas_per_store europe_other_retail_revenue europe_other_average_stores europe_other_per_store
2 2024FY 46.01 397.0 1158.9
3 2025FY 100.75 438.0 2300.2 43.19 69.5 6214.4 20.04 43.0 4660.5 8.56 25.0 3424.0
@@ -0,0 +1,6 @@
period,china_retail_revenue,china_average_stores,china_per_store,apac_retail_revenue,apac_average_stores,apac_per_store,americas_retail_revenue,americas_average_stores,americas_per_store,europe_other_retail_revenue,europe_other_average_stores,europe_other_per_store
2024H1,20.04,381.0,526.0,,,,,,,,,
2024H2,25.97,415.0,625.8,11.83,46.5,2544.1,2.54,16.0,1587.5,1.16,11.5,1008.7
2025H1,44.06,437.0,1008.2,14.93,61.5,2427.6,7.37,31.5,2339.7,2.72,16.0,1700.0
2025H2,56.69,444.0,1276.8,28.26,77.0,3670.1,12.67,52.5,2413.3,5.84,27.0,2163.0
2026H1,60.90,450.0,1353.3,17.16,87.5,1961.1,9.03,75.0,1204.0,4.03,40.5,995.1
1 period china_retail_revenue china_average_stores china_per_store apac_retail_revenue apac_average_stores apac_per_store americas_retail_revenue americas_average_stores americas_per_store europe_other_retail_revenue europe_other_average_stores europe_other_per_store
2 2024H1 20.04 381.0 526.0
3 2024H2 25.97 415.0 625.8 11.83 46.5 2544.1 2.54 16.0 1587.5 1.16 11.5 1008.7
4 2025H1 44.06 437.0 1008.2 14.93 61.5 2427.6 7.37 31.5 2339.7 2.72 16.0 1700.0
5 2025H2 56.69 444.0 1276.8 28.26 77.0 3670.1 12.67 52.5 2413.3 5.84 27.0 2163.0
6 2026H1 60.90 450.0 1353.3 17.16 87.5 1961.1 9.03 75.0 1204.0 4.03 40.5 995.1
+8
View File
@@ -0,0 +1,8 @@
period,china_robot_revenue,china_average_stores,china_per_store,overseas_robot_revenue,overseas_average_stores,overseas_per_store
2024H1,3.38,2207.0,15.3,,,,
2024H2,4.13,2281.5,18.1,0.49,,
2024FY,7.51,2264.5,33.2,0.80,116.0,69.0
2025H1,6.78,2388.0,28.4,1.42,146.5,96.9
2025H2,6.68,2416.5,27.6,2.02,200.5,100.7
2025FY,13.46,2367.5,56.9,3.44,187.0,184.0
2026H1,7.79,2439.0,31.9,1.72,293.0,58.7
1 period,china_robot_revenue,china_average_stores,china_per_store,overseas_robot_revenue,overseas_average_stores,overseas_per_store
2 2024H1,3.38,2207.0,15.3,,,,
3 2024H2,4.13,2281.5,18.1,0.49,,
4 2024FY,7.51,2264.5,33.2,0.80,116.0,69.0
5 2025H1,6.78,2388.0,28.4,1.42,146.5,96.9
6 2025H2,6.68,2416.5,27.6,2.02,200.5,100.7
7 2025FY,13.46,2367.5,56.9,3.44,187.0,184.0
8 2026H1,7.79,2439.0,31.9,1.72,293.0,58.7
@@ -0,0 +1,3 @@
period,china_robot_revenue,china_average_stores,china_per_store,overseas_robot_revenue,overseas_average_stores,overseas_per_store
2024FY,7.51,2264.5,33.2,0.80,116.0,69.0
2025FY,13.46,2367.5,56.9,3.44,187.0,184.0
1 period china_robot_revenue china_average_stores china_per_store overseas_robot_revenue overseas_average_stores overseas_per_store
2 2024FY 7.51 2264.5 33.2 0.80 116.0 69.0
3 2025FY 13.46 2367.5 56.9 3.44 187.0 184.0
@@ -0,0 +1,6 @@
period,china_robot_revenue,china_average_stores,china_per_store,overseas_robot_revenue,overseas_average_stores,overseas_per_store
2024H1,3.38,2207.0,15.3,,,,
2024H2,4.13,2281.5,18.1,0.49,,
2025H1,6.78,2388.0,28.4,1.42,146.5,96.9
2025H2,6.68,2416.5,27.6,2.02,200.5,100.7
2026H1,7.79,2439.0,31.9,1.72,293.0,58.7
1 period,china_robot_revenue,china_average_stores,china_per_store,overseas_robot_revenue,overseas_average_stores,overseas_per_store
2 2024H1,3.38,2207.0,15.3,,,,
3 2024H2,4.13,2281.5,18.1,0.49,,
4 2025H1,6.78,2388.0,28.4,1.42,146.5,96.9
5 2025H2,6.68,2416.5,27.6,2.02,200.5,100.7
6 2026H1,7.79,2439.0,31.9,1.72,293.0,58.7
+13
View File
@@ -0,0 +1,13 @@
period,mainland,hkmtw,apac,americas,europe,overseas,global,global_yoy
2017FY,32,,,,,,32,
2018FY,63,,,,,,63,96.9
2019FY,114,,,,,,114,81.0
2020FY,187,,,,,,187,64.0
2021FY,288,,,,,7,295,57.8
2022FY,329,,,,,43,372,26.1
2023FY,363,,,,,70,433,16.4
2024H1,374,25,39,10,9,58,457,
2024FY,401,30,54,22,14,90,521,20.3
2025H1,409,34,69,41,18,128,571,24.9
2025FY,410,35,85,64,36,185,630,20.9
2026H1,419,36,90,86,45,221,676,18.4
1 period mainland hkmtw apac americas europe overseas global global_yoy
2 2017FY 32 32
3 2018FY 63 63 96.9
4 2019FY 114 114 81.0
5 2020FY 187 187 64.0
6 2021FY 288 7 295 57.8
7 2022FY 329 43 372 26.1
8 2023FY 363 70 433 16.4
9 2024H1 374 25 39 10 9 58 457
10 2024FY 401 30 54 22 14 90 521 20.3
11 2025H1 409 34 69 41 18 128 571 24.9
12 2025FY 410 35 85 64 36 185 630 20.9
13 2026H1 419 36 90 86 45 221 676 18.4
+10
View File
@@ -0,0 +1,10 @@
period,mainland,hkmtw,apac,americas,europe,overseas,global,global_yoy
2017FY,32,,,,,,32,
2018FY,63,,,,,,63,96.9
2019FY,114,,,,,,114,81.0
2020FY,187,,,,,,187,64.0
2021FY,288,,,,,7,295,57.8
2022FY,329,,,,,43,372,26.1
2023FY,363,,,,,70,433,16.4
2024FY,401,30,54,22,14,90,521,20.3
2025FY,410,35,85,64,36,185,630,20.9
1 period mainland hkmtw apac americas europe overseas global global_yoy
2 2017FY 32 32
3 2018FY 63 63 96.9
4 2019FY 114 114 81.0
5 2020FY 187 187 64.0
6 2021FY 288 7 295 57.8
7 2022FY 329 43 372 26.1
8 2023FY 363 70 433 16.4
9 2024FY 401 30 54 22 14 90 521 20.3
10 2025FY 410 35 85 64 36 185 630 20.9
@@ -0,0 +1,4 @@
period,mainland,hkmtw,apac,americas,europe,overseas,global,global_yoy
2024H1,374,25,39,10,9,58,457,
2025H1,409,34,69,41,18,128,571,24.9
2026H1,419,36,90,86,45,221,676,18.4
1 period mainland hkmtw apac americas europe overseas global global_yoy
2 2024H1 374 25 39 10 9 58 457
3 2025H1 409 34 69 41 18 128 571 24.9
4 2026H1 419 36 90 86 45 221 676 18.4
+6
View File
@@ -0,0 +1,6 @@
period,global,china,apac,americas,europe,other
2024H1,457,87.3,8.5,2.2,2.0,0.0
2024FY,521,82.7,10.4,4.2,2.7,0.0
2025H1,571,77.6,12.1,7.2,3.2,0.0
2025FY,630,70.6,13.5,10.2,5.7,0.0
2026H1,676,67.3,13.3,12.7,6.7,0.0
1 period global china apac americas europe other
2 2024H1 457 87.3 8.5 2.2 2.0 0.0
3 2024FY 521 82.7 10.4 4.2 2.7 0.0
4 2025H1 571 77.6 12.1 7.2 3.2 0.0
5 2025FY 630 70.6 13.5 10.2 5.7 0.0
6 2026H1 676 67.3 13.3 12.7 6.7 0.0
@@ -0,0 +1,3 @@
period,global,china,apac,americas,europe,other
2024FY,521,82.7,10.4,4.2,2.7,0.0
2025FY,630,70.6,13.5,10.2,5.7,0.0
1 period global china apac americas europe other
2 2024FY 521 82.7 10.4 4.2 2.7 0.0
3 2025FY 630 70.6 13.5 10.2 5.7 0.0
@@ -0,0 +1,4 @@
period,global,china,apac,americas,europe,other
2024H1,457,87.3,8.5,2.2,2.0,0.0
2025H1,571,77.6,12.1,7.2,3.2,0.0
2026H1,676,67.3,13.3,12.7,6.7,0.0
1 period global china apac americas europe other
2 2024H1 457 87.3 8.5 2.2 2.0 0.0
3 2025H1 571 77.6 12.1 7.2 3.2 0.0
4 2026H1 676 67.3 13.3 12.7 6.7 0.0
+13
View File
@@ -0,0 +1,13 @@
period,mainland,hkmtw,overseas,global,global_yoy
2017FY,43,,,43,
2018FY,260,,,260,504.7
2019FY,825,,,825,217.3
2020FY,1351,,,1351,63.8
2021FY,1861,,9,1870,38.4
2022FY,2067,,120,2187,17.0
2023FY,2190,,99,2289,4.7
2024H1,2189,35,,,
2024FY,2300,39,133,2472,8.0
2025H1,2396,41,160,2597,
2025FY,2350,46,241,2637,6.7
2026H1,2436,46,345,2827,8.9
1 period mainland hkmtw overseas global global_yoy
2 2017FY 43 43
3 2018FY 260 260 504.7
4 2019FY 825 825 217.3
5 2020FY 1351 1351 63.8
6 2021FY 1861 9 1870 38.4
7 2022FY 2067 120 2187 17.0
8 2023FY 2190 99 2289 4.7
9 2024H1 2189 35
10 2024FY 2300 39 133 2472 8.0
11 2025H1 2396 41 160 2597
12 2025FY 2350 46 241 2637 6.7
13 2026H1 2436 46 345 2827 8.9
+10
View File
@@ -0,0 +1,10 @@
period,mainland,hkmtw,overseas,global,global_yoy
2017FY,43,,,43,
2018FY,260,,,260,504.7
2019FY,825,,,825,217.3
2020FY,1351,,,1351,63.8
2021FY,1861,,9,1870,38.4
2022FY,2067,,120,2187,17.0
2023FY,2190,,99,2289,4.7
2024FY,2300,39,133,2472,8.0
2025FY,2350,46,241,2637,6.7
1 period mainland hkmtw overseas global global_yoy
2 2017FY 43 43
3 2018FY 260 260 504.7
4 2019FY 825 825 217.3
5 2020FY 1351 1351 63.8
6 2021FY 1861 9 1870 38.4
7 2022FY 2067 120 2187 17.0
8 2023FY 2190 99 2289 4.7
9 2024FY 2300 39 133 2472 8.0
10 2025FY 2350 46 241 2637 6.7
+4
View File
@@ -0,0 +1,4 @@
period,mainland,hkmtw,overseas,global,global_yoy
2024H1,2189,35,,,
2025H1,2396,41,160,2597,
2026H1,2436,46,345,2827,8.9
1 period mainland hkmtw overseas global global_yoy
2 2024H1 2189 35
3 2025H1 2396 41 160 2597
4 2026H1 2436 46 345 2827 8.9
+13 -13
View File
@@ -1,14 +1,14 @@
period,inventory_days,receivable_days,payable_days,cash_conversion_cycle period,inventory_days,receivable_days,payable_days,cash_conversion_cycle
2020H1,101.4,9.6,27.6,83.3 2020H1,102.0,9.6,27.8,83.8
2020FY,63.0,8.9,28.4,43.5 2020FY,63.9,9.0,28.8,44.1
2021H1,74.3,10.9,40.9,44.3 2021H1,74.7,10.9,41.1,44.6
2021FY,105.4,10.0,29.9,85.5 2021FY,106.9,10.1,30.4,86.6
2022H1,159.0,11.6,44.7,125.9 2022H1,159.9,11.7,44.9,126.6
2022FY,151.8,14.3,46.3,119.8 2022FY,154.0,14.5,47.0,121.5
2023H1,131.2,12.9,54.4,89.7 2023H1,131.9,13.0,54.7,90.2
2023FY,130.9,14.7,51.2,94.4 2023FY,132.7,14.9,51.9,95.7
2024H1,100.0,11.6,54.5,57.1 2024H1,100.6,11.6,54.8,57.4
2024FY,101.0,11.0,52.9,59.1 2024FY,102.4,11.2,53.6,59.9
2025H1,83.1,9.4,48.8,43.7 2025H1,83.5,9.5,49.1,43.9
2025FY,121.6,6.8,36.1,92.3 2025FY,123.3,6.9,36.6,93.6
2026H1,200.0,8.1,42.6,165.5 2026H1,201.2,8.2,42.9,166.5
1 period inventory_days receivable_days payable_days cash_conversion_cycle
2 2020H1 101.4 102.0 9.6 27.6 27.8 83.3 83.8
3 2020FY 63.0 63.9 8.9 9.0 28.4 28.8 43.5 44.1
4 2021H1 74.3 74.7 10.9 40.9 41.1 44.3 44.6
5 2021FY 105.4 106.9 10.0 10.1 29.9 30.4 85.5 86.6
6 2022H1 159.0 159.9 11.6 11.7 44.7 44.9 125.9 126.6
7 2022FY 151.8 154.0 14.3 14.5 46.3 47.0 119.8 121.5
8 2023H1 131.2 131.9 12.9 13.0 54.4 54.7 89.7 90.2
9 2023FY 130.9 132.7 14.7 14.9 51.2 51.9 94.4 95.7
10 2024H1 100.0 100.6 11.6 54.5 54.8 57.1 57.4
11 2024FY 101.0 102.4 11.0 11.2 52.9 53.6 59.1 59.9
12 2025H1 83.1 83.5 9.4 9.5 48.8 49.1 43.7 43.9
13 2025FY 121.6 123.3 6.8 6.9 36.1 36.6 92.3 93.6
14 2026H1 200.0 201.2 8.1 8.2 42.6 42.9 165.5 166.5
+138
View File
@@ -0,0 +1,138 @@
# 财报图表项目交接说明(2026-08-28)
## 1. 当前状态
项目已迁移到 **Vite + Vanilla JavaScript + ECharts**,本地开发地址为 `http://localhost:8899`。页面支持模块/CSS 热更新;修改 `manifest.json` 或 CSV 后会整页刷新。当前泡泡玛特已配置 **22 张图表**,另有 **5 张示例图表**;完整回归与生产构建均已通过。
当前页面结构:公司选择 → 分析分类 Tab → 当前分类图表卡片 → 右侧图表锚点导航。泡泡玛特的“成长性”已经并入“盈利能力”,不再单独保留该分类。
## 2. 启动与验证
```bash
cd /Users/joey-xd/sites/vest-tools/smart-fin-chart
pnpm install # 仅首次或依赖变化后执行
pnpm dev # 打开 http://localhost:8899
pnpm test # 27 张图表的 ECharts SSR 回归
pnpm build # 生产构建
```
项目根目录另有可双击执行的 [`start.command`](../start.command),会启动 Vite 并打开本地地址。
截至本次交接,`pnpm test` 的结果是 **27 张成功 / 0 张失败**。其中 `ppmt-ip-share` 与 `ppmt-ip-share-history` 会提示 `legend.top=middle`,这是两张右侧纵向图例的预期配置,不是故障。
## 3. 架构与单一数据源
### 图表声明
[`manifest.json`](../manifest.json) 是公司、分类、图表与 CSV 的唯一声明源:
- `company.chartCatalog`:每个图表只声明一次(图表 key、CSV、画布高度、导出文件名)。
- `company.tabs[].chartRefs`:分类只引用图表 ID;概览与详细分类可以引用同一个 ID,不复制图表代码或数据配置。
- `js/main.js`:读取 manifest、把 `chartRefs` 解析为视图实例;同一图在概览和详细分类中使用同一 chart builder。
- `js/charts/index.js`:chart key 与图表构建函数的注册表。
这项设计已经用于“概览”:概览展示的营收利润增速、盈利能力、IP 占比、区域渠道分布,在原分类中仍保留,且复用同一组件和同一 CSV。
### 新增图表的标准步骤
1. 在 `data/<company>/` 新增或更新运行时 CSV。
2. 在 `js/charts/` 新建 chart builder,并从 `js/theme.js` 导入公共主题与系列构造器。
3. 在 `js/charts/index.js` 注册 key。
4. 在 `manifest.json` 的 `chartCatalog` 中声明一次,再在所需 Tab 的 `chartRefs` 中引用。
5. 普通图表运行 `node test/regress.mjs <chart-id>`;若改了 `theme.js`、`csv.js`、manifest 等公共部分,运行 `pnpm test`,然后 `pnpm build`。
详细开发约定见 [`CLAUDE.md`](../CLAUDE.md),方案背景见 [`方案设计.md`](方案设计.md)。
## 4. 泡泡玛特当前分类与图表
| 分类 | 图表 |
| --- | --- |
| 概览 | 营收到利润的增速对比、盈利能力、主要 IP 收入占比、区域渠道分布 |
| 盈利能力 | 营收到利润的增速对比、核心利润及同比增速、核心利润率、净利率同比拆解、利润流向、经营现金流与净利润、三大现金流与现金净增加 |
| 费用率 | 利润率与费用率拆解、费用结构与费用率 |
| 资产负债 | 资产结构演变、现金/负债/净营运资本、营运资本效率 |
| IP 与品类 | 主要 IP 收入趋势、主要 IP 收入占比、各 IP 半年收入占比、除 THE MONSTERS 外 IP 收入及同比、品类收入结构 |
| 区域渠道 | 区域渠道分布、区域渠道同比增速、区域渠道同比增速趋势、渠道收入结构与增速 |
## 5. 数据状态与口径
### 原始数据
用户提供的 8 份 2026-08-28 原始 CSV 已原样保存到 [`data/ppmt/raw/`](../data/ppmt/raw/):资产负债表、利润表、现金流量表、财务指标、营收构成、员工、股本变动、分红分析。目录中也保留了早期 `ppmt.xlsx` 与 `2026H1.pdf`。
运行时图表 CSV 位于 [`data/ppmt/`](../data/ppmt/);原始文件不直接被浏览器读取。归一化脚本为 [`scripts/normalize_ppmt_financials.mjs`](../scripts/normalize_ppmt_financials.mjs),可由原始合并报表和财务指标生成以下衍生数据:
- `ppmt_cash_conversion.csv`
- `ppmt_liquidity_working_capital.csv`
- `ppmt_cashflow_composition.csv`
- `ppmt_working_capital_days.csv`
- `ppmt_expense_structure.csv`
- `ppmt_channel_revenue.csv`
### 必须注意的口径
- CSV 内的 `202xH2` 在利润表/部分财务表中代表**全年累计**,显示层会写成 `xx全年`;不可当作单独的下半年。
- `ppmt_ip_share_history.csv` 的 H2 则是**单半年**,所以该图使用自定义横轴格式,不能套 `periodAxis` 的“全年”转换。
- 非 GAAP 净利率是派生指标:非 GAAP 净利 ÷ 营业收入;2020H1/H2 缺失时暂用归母净利替代,脚注已标注。
- 净利率同比拆解为税盾换算后的百分点贡献;改动后应验证首尾闭合误差小于 0.05pct。
## 6. 近期完成的修改
### 页面与交互
- 使用 Vite 的 vanilla 模式,取代静态服务器,支持热更新。
- 页面通过 `localhost` 访问,避免 `file://` 读取 CSV 的 CORS 问题。
- 移除了宽而无信息价值的“财报图表”标题栏;“重新加载数据”改为固定位置的刷新图标按钮。
- 左侧菜单断点调整为 `880px`。
- 区域渠道同比增速趋势热力图:绘图区底部留白调整为 `155px`;控制器与横轴不重叠;高/低增长颜色的语义已调整为“红色更代表高增长”。
### 图表组织与数据
- 概览改为复用重要图表,不搬走原分类里的图表,避免两份图表代码逐渐不一致。
- “成长性”内容并入“盈利能力”,分类导航和方案文档已同步。
- 基于最新原始数据新增 6 张高优先级图表:经营现金流与净利润、现金/负债/净营运资本、三大现金流与现金净增加、营运资本效率、费用结构与费用率、渠道收入结构与增速。
### 图例与标签
- 图例图标统一交由 ECharts 按系列类型渲染:柱状图为矩形,折线图为“短线 + 空心圆”;统一图例图标宽度为 `20px`。
- `barSeries()` 默认强制展示柱状标签,避免小柱标签被自动隐藏。
- `lineDataWithSafeLabels()` 会根据折线值与柱值的接近程度调整标签距离;本次增加 `closePosition` 参数,可把接近柱顶的折线标签切到柱体另一侧。
- “营运资本效率”四条折线已全部显示数值,并按同期间相邻数值的间隔决定朝上/朝下,降低遮挡。
- “现金、负债与净营运资本”两组柱和净营运资本折线均显示数值。
### 配色:已确认方向为「轻快消费」
主题文件 [`js/theme.js`](../js/theme.js) 中的正式色板:
- `FINANCIAL_COLORS`:青绿经营现金流、蓝色投资/现金、琥珀融资/负债、珊瑚红净现金增加或关键压力项、暖橙净利润、紫色转化率/同比强调。
- `CHANNEL_COLORS`:渠道收入和增速的固定语义色。
- `BRIDGE_COLORS`:瀑布图固定为蓝色基准、青绿色正向贡献、珊瑚红负向拖累。
最近已按该方向调整:
- 净利率同比拆解:蓝色基准、青绿色正向、珊瑚红负向。
- 核心利润及同比增速:暖橙利润柱、紫色同比线。
- 经营现金流与净利润、三大现金流、渠道收入结构与增速已采用该色系。
- 现金/负债/净营运资本:蓝、琥珀、青绿。
- 营运资本效率:琥珀、蓝、紫、珊瑚红。
后续新增图表必须优先复用主题语义色,不要在单个图表文件里重复手写同语义的十六进制颜色。具体规则见 [`CLAUDE.md`](../CLAUDE.md) 的“配色”部分。
## 7. 下一步建议(尚未开始)
按开发优先级建议:
1. **人工视觉巡检**:重点确认资产负债页新增加的全量标签在桌面和窄屏下是否仍有重叠;当前已通过 SVG 回归,仍应以真实浏览器视觉为准。
2. **补齐数据链路**:当前员工、股本变动、分红分析原始数据已归档,但尚未生成对应运行时 CSV 或图表。可优先评估“人均营收/人均利润”“分红与派息率”“股本/期权稀释”等图表。
3. **更新对外 README**:README 的图表清单仍停在较早阶段,未完整列出新增的 6 张 PPMT 图,也仍写有“raw 仅 PDF/XLSX”的旧描述;这不影响运行,但应在对外使用前更新。
4. **通用财务模板后置**:只有至少两家公司需要同一图型、字段映射和口径都稳定时,再抽取 `template(rows, meta, { width, height }) => option`;当前保持“一图一文件”更稳妥。
## 8. 给下一位 AI 的工作约束
- 不要用 npm;使用 pnpm。
- 不要把概览图复制出第二套 chart 配置;复用 `manifest.json` 的同一 `chartRef`。
- 主题、图例、字体、轴和脚注均从 `js/theme.js` 复用;优先 `standardLegend()`、`lineSeries()`、`barSeries()`。
- 不要手工设置 `legend.icon`,否则会破坏柱/折线图例的统一样式。
- 修改公共模块(特别是 `theme.js`、`csv.js`、manifest)必须跑 `pnpm test` 与 `pnpm build`。
- 保留已有用户修改;不要使用 `git reset --hard` 或覆盖无关文件。
+6 -1
View File
@@ -147,7 +147,9 @@ js/main.js + js/ui.js ← 解析引用、渲染三栏、导出和
} }
``` ```
页面仅在声明多个视图时显示切换按钮,并将选择写入 URL query。当前泡泡玛特图表没有强制启用这一功能;基础能力已保留,等真实数据需要时再登记。 页面仅在声明多个视图时显示切换按钮,并将选择写入 URL query;尚未启用多视图的图表维持单一 CSV,不显示控制器。
泡泡玛特已在 15 张连续图启用该能力:盈利能力、营收到利润的增速对比、核心利润及同比增速、核心利润率、利润率与费用率拆解、资产结构演变、主要 IP 收入趋势、各 IP 收入占比、除 THE MONSTERS 外 IP 收入及同比、品类收入结构、经营现金流与净利润、现金/负债/净营运资本、三大现金流与现金净增加、费用结构与费用率、渠道收入结构与增速。默认显示“全部”,并提供“年度 / 半年”;其原始披露没有可靠 Q1/Q3 数据,故不展示“季度”。其中 `FY` 为全年、`H1/H2` 为单独半年,流量类指标的 H2 在数据归一化阶段由 `FY − H1` 生成,比率与同比也在数据层重算;资产负债表类则为期末余额,H2 对应年末时点而非累计金额。
--- ---
@@ -156,6 +158,9 @@ js/main.js + js/ui.js ← 解析引用、渲染三栏、导出和
新增图表必须复用 `theme.js` 的 `FONT`、`PALETTE`、`LAYOUT`、`periodAxis()`、`standardLegend()`、`lineSeries()`、`barSeries()`、`footnote()` 与 `watermarks()`,而不是在单个图表中重复手写基础样式。 新增图表必须复用 `theme.js` 的 `FONT`、`PALETTE`、`LAYOUT`、`periodAxis()`、`standardLegend()`、`lineSeries()`、`barSeries()`、`footnote()` 与 `watermarks()`,而不是在单个图表中重复手写基础样式。
- 财务含义相同的系列应使用固定语义色;IP、区域、品类等多分类数据使用统一分类色板。 - 财务含义相同的系列应使用固定语义色;IP、区域、品类等多分类数据使用统一分类色板。
- 柱线组合图采用已确认的“轻快消费”色板:柱状系列用不同色相承载分类,折线只保留一条高辨识度强调色。现金流、利润与同比使用 `FINANCIAL_COLORS`,渠道使用 `CHANNEL_COLORS`;瀑布图使用 `BRIDGE_COLORS`(蓝色基准、青绿色正向、珊瑚红负向)。新图不得在图表文件内散落同等语义的十六进制颜色。
- 一张图只保留一个视觉主角。辅助柱使用中等饱和度,避免多个红、橙、紫同时争夺注意力;分类图的相邻系列须同时拉开色相与明度。
- 颜色不是唯一编码:折线还须依靠线宽、实虚线、空心圆点和标签优先级;柱线混合图的折线标签使用 `lineDataWithSafeLabels()` 等通用避让规则,避免文字压在柱端。
- 新图的业务差异由 chart 文件表达;统一视觉由主题文件表达;展示位置与 CSV 由 manifest 表达。 - 新图的业务差异由 chart 文件表达;统一视觉由主题文件表达;展示位置与 CSV 由 manifest 表达。
- 新增或调整图表后,必须检查标签、图例、脚注和控制器是否重叠,以及颜色与文字是否有足够对比度。 - 新增或调整图表后,必须检查标签、图例、脚注和控制器是否重叠,以及颜色与文字是否有足够对比度。
+14
View File
@@ -25,6 +25,13 @@ import { cashflowCompositionChart } from './ppmt_cashflow_composition.js';
import { workingCapitalDaysChart } from './ppmt_working_capital_days.js'; import { workingCapitalDaysChart } from './ppmt_working_capital_days.js';
import { expenseStructureChart } from './ppmt_expense_structure.js'; import { expenseStructureChart } from './ppmt_expense_structure.js';
import { channelRevenueChart } from './ppmt_channel_revenue.js'; import { channelRevenueChart } from './ppmt_channel_revenue.js';
import { storesRetailChart } from './ppmt_stores_retail.js';
import { storesRobotChart } from './ppmt_stores_robot.js';
import { regionShareChart } from './ppmt_region_share.js';
import { inventoryBalanceChart } from './ppmt_inventory.js';
import { storesRetailShareChart } from './ppmt_stores_retail_share.js';
import { retailStoreRevenueChart } from './ppmt_retail_store_revenue.js';
import { robotStoreRevenueChart } from './ppmt_robot_store_revenue.js';
/** 图表构建函数注册表。manifest 只引用这里的 key,不直接引用 JS 函数。 */ /** 图表构建函数注册表。manifest 只引用这里的 key,不直接引用 JS 函数。 */
export const chartRegistry = { export const chartRegistry = {
@@ -55,6 +62,13 @@ export const chartRegistry = {
'ppmt-working-capital-days': workingCapitalDaysChart, 'ppmt-working-capital-days': workingCapitalDaysChart,
'ppmt-expense-structure': expenseStructureChart, 'ppmt-expense-structure': expenseStructureChart,
'ppmt-channel-revenue': channelRevenueChart, 'ppmt-channel-revenue': channelRevenueChart,
'ppmt-stores-retail': storesRetailChart,
'ppmt-stores-robot': storesRobotChart,
'ppmt-region-share': regionShareChart,
'ppmt-inventory': inventoryBalanceChart,
'ppmt-stores-retail-share': storesRetailShareChart,
'ppmt-retail-store-revenue': retailStoreRevenueChart,
'ppmt-robot-store-revenue': robotStoreRevenueChart,
}; };
export function getChartBuilder(key) { export function getChartBuilder(key) {
+6 -5
View File
@@ -1,6 +1,7 @@
import { col } from '../csv.js'; import { col } from '../csv.js';
import { import {
PALETTE, FONT, axisCommon, periodAxis, titleStyle, legendStyle, PALETTE, FONT, axisCommon, standalonePeriodAxis, titleStyle, legendStyle,
financialViewLabel,
lineSeries, footnote, watermarks, LAYOUT, lineSeries, footnote, watermarks, LAYOUT,
} from '../theme.js'; } from '../theme.js';
@@ -8,15 +9,15 @@ import {
* 泡泡玛特资产结构演变图 * 泡泡玛特资产结构演变图
* 堆叠柱状图展示资产配置,折线图展示总资产增长 * 堆叠柱状图展示资产配置,折线图展示总资产增长
*/ */
export function assetsChart(rows, { width, height }) { export function assetsChart(rows, { width, height, view = 'all' }) {
const periods = col(rows, 'period'); const periods = col(rows, 'period');
return { return {
backgroundColor: '#fff', backgroundColor: '#fff',
title: { title: {
...titleStyle, ...titleStyle,
text: '泡泡玛特资产结构演变(2020H1-2026H1)', text: `泡泡玛特资产结构演变(${financialViewLabel(view)})`,
subtext: '单位:亿元', subtext: '单位:亿元;资产负债表为期末余额,H2 为年末时点而非全年累计',
}, },
legend: { legend: {
...legendStyle, ...legendStyle,
@@ -29,7 +30,7 @@ export function assetsChart(rows, { width, height }) {
axisPointer: { type: 'shadow' }, axisPointer: { type: 'shadow' },
textStyle: { fontFamily: FONT }, textStyle: { fontFamily: FONT },
}, },
xAxis: { ...periodAxis, data: periods }, xAxis: { ...standalonePeriodAxis, data: periods },
yAxis: [ yAxis: [
{ {
...axisCommon, ...axisCommon,
+2 -8
View File
@@ -1,6 +1,6 @@
import { col } from '../csv.js'; import { col } from '../csv.js';
import { import {
PALETTE, FONT, axisCommon, titleStyle, footnote, watermarks, PALETTE, BRIDGE_COLORS, FONT, axisCommon, titleStyle, footnote, watermarks,
} from '../theme.js'; } from '../theme.js';
/** /**
@@ -13,12 +13,6 @@ import {
* 税率变化单独成项。恒等式 净利率 = 税前利润率 × (1-税率), * 税率变化单独成项。恒等式 净利率 = 税前利润率 × (1-税率),
* Δ净 = Δ税前×(1-t₁) − 税前₂×Δt,实测闭合误差 0.00pct。 * Δ净 = Δ税前×(1-t₁) − 税前₂×Δt,实测闭合误差 0.00pct。
*/ */
const COLOR = {
total: '#2F5C85', // 首尾锚点
neg: '#C0392B', // 拖累
pos: '#1A9E5C', // 利好
};
export function ppmtBridgeChart(rows, { width, height }) { export function ppmtBridgeChart(rows, { width, height }) {
const items = col(rows, 'item'); const items = col(rows, 'item');
const types = col(rows, 'type'); const types = col(rows, 'type');
@@ -92,7 +86,7 @@ export function ppmtBridgeChart(rows, { width, height }) {
barWidth: 51, barWidth: 51,
data: values.map((v, i) => ({ data: values.map((v, i) => ({
value: v, value: v,
itemStyle: { color: COLOR[types[i]] }, itemStyle: { color: BRIDGE_COLORS[types[i]] },
})), })),
label: { label: {
show: true, show: true,
+14 -10
View File
@@ -1,19 +1,23 @@
import { col } from '../csv.js'; import { col } from '../csv.js';
import { import {
FINANCIAL_COLORS, PALETTE, FONT, axisCommon, periodAxis, titleStyle, legendStyle, FINANCIAL_COLORS, PALETTE, FONT, axisCommon, standalonePeriodAxis, titleStyle, legendStyle,
financialViewDisclosure, financialViewLabel,
lineSeries, footnote, watermarks, LAYOUT, lineSeries, footnote, watermarks, LAYOUT,
} from '../theme.js'; } from '../theme.js';
/** 泡泡玛特经营现金流与净利润:观察利润的现金兑现能力。 */ /** 泡泡玛特经营现金流与净利润:观察利润的现金兑现能力。 */
export function cashConversionChart(rows, { width, height }) { export function cashConversionChart(rows, { width, height, view = 'all' }) {
const periods = col(rows, 'period'); const periods = col(rows, 'period');
const conversionMax = Math.max(...col(rows, 'cash_conversion').filter(Number.isFinite));
const conversionAxisMax = Math.max(200, Math.ceil((conversionMax * 1.1) / 100) * 100);
const barWidth = view === 'all' ? 14 : view === 'halfyear' ? 24.96 : 31.2;
return { return {
backgroundColor: '#fff', backgroundColor: '#fff',
title: { title: {
...titleStyle, ...titleStyle,
text: '泡泡玛特经营现金流与净利润(年度)', text: `泡泡玛特经营现金流与净利润(${financialViewLabel(view)})`,
subtext: '现金转换率 = 经营活动现金流净额 / 净利润;单位:亿元', subtext: ['现金转换率 = 经营活动现金流净额 / 净利润;单位:亿元', financialViewDisclosure(view)].filter(Boolean).join('\n'),
}, },
legend: { legend: {
...legendStyle, ...legendStyle,
@@ -27,7 +31,7 @@ export function cashConversionChart(rows, { width, height }) {
textStyle: { fontFamily: FONT }, textStyle: { fontFamily: FONT },
valueFormatter: (value) => `${value.toFixed(1)}`, valueFormatter: (value) => `${value.toFixed(1)}`,
}, },
xAxis: { ...periodAxis, data: periods }, xAxis: { ...standalonePeriodAxis, data: periods },
yAxis: [ yAxis: [
{ {
...axisCommon, ...axisCommon,
@@ -48,8 +52,8 @@ export function cashConversionChart(rows, { width, height }) {
nameRotate: -90, nameRotate: -90,
nameTextStyle: { color: PALETTE.grey, fontSize: 13, fontFamily: FONT }, nameTextStyle: { color: PALETTE.grey, fontSize: 13, fontFamily: FONT },
min: 0, min: 0,
max: 200, max: conversionAxisMax,
interval: 50, interval: conversionAxisMax / 4,
axisLabel: { ...axisCommon.axisLabel, formatter: '{value}%' }, axisLabel: { ...axisCommon.axisLabel, formatter: '{value}%' },
splitLine: { show: false }, splitLine: { show: false },
}, },
@@ -59,7 +63,7 @@ export function cashConversionChart(rows, { width, height }) {
name: '经营活动现金流净额', name: '经营活动现金流净额',
type: 'bar', type: 'bar',
data: col(rows, 'operating_cash_flow'), data: col(rows, 'operating_cash_flow'),
barWidth: 31.2, barWidth,
itemStyle: { color: FINANCIAL_COLORS.operatingCashFlow }, itemStyle: { color: FINANCIAL_COLORS.operatingCashFlow },
label: { show: true, position: 'top', color: PALETTE.ink, fontFamily: FONT, fontSize: 11, formatter: (p) => p.value.toFixed(1) }, label: { show: true, position: 'top', color: PALETTE.ink, fontFamily: FONT, fontSize: 11, formatter: (p) => p.value.toFixed(1) },
}, },
@@ -67,7 +71,7 @@ export function cashConversionChart(rows, { width, height }) {
name: '净利润', name: '净利润',
type: 'bar', type: 'bar',
data: col(rows, 'net_profit'), data: col(rows, 'net_profit'),
barWidth: 31.2, barWidth,
itemStyle: { color: FINANCIAL_COLORS.netProfit }, itemStyle: { color: FINANCIAL_COLORS.netProfit },
label: { show: true, position: 'top', color: PALETTE.ink, fontFamily: FONT, fontSize: 11, formatter: (p) => p.value.toFixed(1) }, label: { show: true, position: 'top', color: PALETTE.ink, fontFamily: FONT, fontSize: 11, formatter: (p) => p.value.toFixed(1) },
}, },
@@ -85,7 +89,7 @@ export function cashConversionChart(rows, { width, height }) {
], ],
graphic: [ graphic: [
...watermarks(width, height), ...watermarks(width, height),
footnote('数据来源:泡泡玛特合并现金流量表、利润表(年度口径)| @思考的Joey'), footnote('数据来源:泡泡玛特合并现金流量表、利润表;H2为单独半年 | @思考的Joey'),
], ],
}; };
} }
+11 -9
View File
@@ -1,12 +1,14 @@
import { col } from '../csv.js'; import { col } from '../csv.js';
import { import {
FINANCIAL_COLORS, PALETTE, FONT, axisCommon, periodAxis, titleStyle, standardLegend, FINANCIAL_COLORS, PALETTE, FONT, axisCommon, standalonePeriodAxis, titleStyle, standardLegend,
financialViewDisclosure, financialViewLabel,
lineDataWithSafeLabels, lineDataWithSafeLabels,
lineSeries, footnote, watermarks, LAYOUT, lineSeries, footnote, watermarks, LAYOUT,
} from '../theme.js'; } from '../theme.js';
/** 泡泡玛特三大现金流:展示经营造血、投资投入与筹资变化。 */ /** 泡泡玛特三大现金流:展示经营造血、投资投入与筹资变化。 */
export function cashflowCompositionChart(rows, { width, height }) { export function cashflowCompositionChart(rows, { width, height, view = 'all' }) {
const barWidth = view === 'all' ? 10 : view === 'halfyear' ? 18.2 : 26;
const netCashIncrease = lineDataWithSafeLabels(rows, { const netCashIncrease = lineDataWithSafeLabels(rows, {
lineKey: 'net_cash_increase', lineKey: 'net_cash_increase',
barKeys: ['operating_cash_flow', 'investing_cash_flow', 'financing_cash_flow'], barKeys: ['operating_cash_flow', 'investing_cash_flow', 'financing_cash_flow'],
@@ -16,8 +18,8 @@ export function cashflowCompositionChart(rows, { width, height }) {
backgroundColor: '#fff', backgroundColor: '#fff',
title: { title: {
...titleStyle, ...titleStyle,
text: '泡泡玛特三大现金流与现金净增加(年度)', text: `泡泡玛特三大现金流与现金净增加(${financialViewLabel(view)})`,
subtext: '单位:亿元;正值为流入,负值为流出', subtext: ['单位:亿元;正值为流入,负值为流出', financialViewDisclosure(view)].filter(Boolean).join('\n'),
}, },
// 不手工指定 icon,让 ECharts 按系列类型生成:柱为矩形、折线为短线 + 空心圆。 // 不手工指定 icon,让 ECharts 按系列类型生成:柱为矩形、折线为短线 + 空心圆。
legend: standardLegend(['经营活动现金流', '投资活动现金流', '筹资活动现金流', '现金净增加']), legend: standardLegend(['经营活动现金流', '投资活动现金流', '筹资活动现金流', '现金净增加']),
@@ -28,7 +30,7 @@ export function cashflowCompositionChart(rows, { width, height }) {
textStyle: { fontFamily: FONT }, textStyle: { fontFamily: FONT },
valueFormatter: (value) => `${value.toFixed(1)} 亿元`, valueFormatter: (value) => `${value.toFixed(1)} 亿元`,
}, },
xAxis: { ...periodAxis, data: col(rows, 'period') }, xAxis: { ...standalonePeriodAxis, data: col(rows, 'period') },
yAxis: { yAxis: {
...axisCommon, ...axisCommon,
type: 'value', type: 'value',
@@ -38,9 +40,9 @@ export function cashflowCompositionChart(rows, { width, height }) {
nameTextStyle: { color: PALETTE.grey, fontSize: 13, fontFamily: FONT }, nameTextStyle: { color: PALETTE.grey, fontSize: 13, fontFamily: FONT },
}, },
series: [ series: [
{ name: '经营活动现金流', type: 'bar', data: col(rows, 'operating_cash_flow'), barWidth: 26, itemStyle: { color: FINANCIAL_COLORS.operatingCashFlow } }, { name: '经营活动现金流', type: 'bar', data: col(rows, 'operating_cash_flow'), barWidth, itemStyle: { color: FINANCIAL_COLORS.operatingCashFlow } },
{ name: '投资活动现金流', type: 'bar', data: col(rows, 'investing_cash_flow'), barWidth: 26, itemStyle: { color: FINANCIAL_COLORS.investingCashFlow } }, { name: '投资活动现金流', type: 'bar', data: col(rows, 'investing_cash_flow'), barWidth, itemStyle: { color: FINANCIAL_COLORS.investingCashFlow } },
{ name: '筹资活动现金流', type: 'bar', data: col(rows, 'financing_cash_flow'), barWidth: 26, itemStyle: { color: FINANCIAL_COLORS.financingCashFlow } }, { name: '筹资活动现金流', type: 'bar', data: col(rows, 'financing_cash_flow'), barWidth, itemStyle: { color: FINANCIAL_COLORS.financingCashFlow } },
{ {
...lineSeries({ ...lineSeries({
name: '现金净增加', name: '现金净增加',
@@ -55,7 +57,7 @@ export function cashflowCompositionChart(rows, { width, height }) {
], ],
graphic: [ graphic: [
...watermarks(width, height), ...watermarks(width, height),
footnote('数据来源:泡泡玛特合并现金流量表(年度口径)| @思考的Joey'), footnote('数据来源:泡泡玛特合并现金流量表;H2为单独半年 | @思考的Joey'),
], ],
}; };
} }
+6 -12
View File
@@ -1,6 +1,7 @@
import { col } from '../csv.js'; import { col } from '../csv.js';
import { import {
PALETTE, FONT, axisCommon, categoryAxis, titleStyle, legendStyle, PALETTE, FONT, axisCommon, standalonePeriodAxis, titleStyle, legendStyle,
financialViewDisclosure, financialViewLabel,
footnote, watermarks, LAYOUT, footnote, watermarks, LAYOUT,
} from '../theme.js'; } from '../theme.js';
@@ -8,7 +9,7 @@ import {
* 泡泡玛特品类收入结构图 * 泡泡玛特品类收入结构图
* 堆叠柱状图展示各品类的收入占比和增长 * 堆叠柱状图展示各品类的收入占比和增长
*/ */
export function categoryChart(rows, { width, height }) { export function categoryChart(rows, { width, height, view = 'all' }) {
// 过滤掉表头行("品类"那一行) // 过滤掉表头行("品类"那一行)
const dataRows = rows.filter(r => r.period && r.period !== '品类'); const dataRows = rows.filter(r => r.period && r.period !== '品类');
const periods = col(dataRows, 'period'); const periods = col(dataRows, 'period');
@@ -17,8 +18,8 @@ export function categoryChart(rows, { width, height }) {
backgroundColor: '#fff', backgroundColor: '#fff',
title: { title: {
...titleStyle, ...titleStyle,
text: '泡泡玛特品类收入结构(2024H1-2025H2)', text: `泡泡玛特品类收入结构(${financialViewLabel(view)})`,
subtext: '单位:亿元', subtext: ['单位:亿元;H2为下半年单期', financialViewDisclosure(view)].filter(Boolean).join('\n'),
}, },
legend: { legend: {
...legendStyle, ...legendStyle,
@@ -41,14 +42,7 @@ export function categoryChart(rows, { width, height }) {
return result; return result;
}, },
}, },
xAxis: { xAxis: { ...standalonePeriodAxis, data: periods },
...categoryAxis,
data: periods,
axisLabel: {
...categoryAxis.axisLabel,
formatter: (value) => String(value).replace(/^20/, ''),
},
},
yAxis: { yAxis: {
...axisCommon, ...axisCommon,
type: 'value', type: 'value',
+100 -11
View File
@@ -1,24 +1,86 @@
import { col } from '../csv.js'; import { col } from '../csv.js';
import { import {
CHANNEL_COLORS, PALETTE, FONT, axisCommon, periodAxis, titleStyle, legendStyle, CHANNEL_COLORS, PALETTE, FONT, axisCommon, standalonePeriodAxis, titleStyle, legendStyle,
financialViewDisclosure, financialViewLabel,
lineSeries, footnote, watermarks, LAYOUT, lineSeries, footnote, watermarks, LAYOUT,
} from '../theme.js'; } from '../theme.js';
/** 泡泡玛特渠道收入结构与增速。 */ /** 泡泡玛特渠道收入结构与增速。 */
export function channelRevenueChart(rows, { width, height }) { const channels = [
const channels = [
{ key: 'retail', name: '零售店', color: CHANNEL_COLORS.retail }, { key: 'retail', name: '零售店', color: CHANNEL_COLORS.retail },
{ key: 'online', name: '线上', color: CHANNEL_COLORS.online }, { key: 'online', name: '线上', color: CHANNEL_COLORS.online },
{ key: 'robot_store', name: '机器人商店', color: CHANNEL_COLORS.robotStore }, { key: 'robot_store', name: '机器人商店', color: CHANNEL_COLORS.robotStore },
{ key: 'wholesale', name: '批发', color: CHANNEL_COLORS.wholesale }, { key: 'wholesale', name: '批发', color: CHANNEL_COLORS.wholesale },
]; ];
// 段内标签文字色:浅色底(批发金)用深字,其余深色底用白字
const INSIDE_LABEL_COLORS = {
retail: '#fff',
online: '#fff',
robot_store: '#fff',
wholesale: PALETTE.ink,
};
const num = (value) => (Number.isFinite(value) ? value : null);
// 轴上限取整齐刻度,柱高比例确定后,段标签的段内/外置可按像素判断
const NICE_MAX = [20, 25, 50, 60, 80, 100, 120, 150, 200, 250, 300, 400, 500, 600, 800, 1000];
const MIN_INSIDE_PX = 13; // 低于约 13px 的段放不下 10px 段内标签
export function channelRevenueChart(rows, { width, height, view = 'all' }) {
const totalMax = Math.max(...rows.map((row) => num(row.total_revenue) || 0));
const axisMax = NICE_MAX.find((max) => max >= totalMax * 1.02)
?? Math.ceil((totalMax * 1.2) / 100) * 100;
const yoyValues = col(rows, 'revenue_yoy').filter(Number.isFinite);
// 全部/半年视图存在 2022H2 的负增长,右轴需包含负区间,避免折线被 0 轴裁切。
const yoyMin = Math.min(0, Math.floor(Math.min(...yoyValues) / 50) * 50);
const yoyMax = Math.max(50, Math.ceil(Math.max(...yoyValues) / 50) * 50);
// 绘图区像素高:画布高 − grid top 145 − grid bottom 90
const pxPerUnit = (height - LAYOUT.gridTop - 90) / axisMax;
// 每根柱里放不下的矮段:标签外置到柱体右侧。同一柱内多个矮段标签
// 自下而上保持 12px 间距;整根柱都是矮段时,纵向扇形以柱身中部为中心,
// 避免标签整体飘离柱顶。返回 { 渠道序号: offset dy }(dy 为像素,正方向向下)。
const fanOutsideLabels = (row) => {
const values = channels.map((item) => num(row[item.key]));
const centers = [];
let cum = 0;
values.forEach((value) => {
centers.push((cum + value / 2) * pxPerUnit);
cum += value;
});
const barTopPx = cum * pxPerUnit;
const tinyIdx = channels
.map((_, i) => i)
.filter((i) => values[i] !== null && values[i] * pxPerUnit < MIN_INSIDE_PX);
if (tinyIdx.length === 0) return {};
const ys = tinyIdx.map((i) => centers[i]);
for (let k = 1; k < ys.length; k += 1) {
ys[k] = Math.max(ys[k], ys[k - 1] + 12);
}
if (tinyIdx.length === channels.length) {
const shift = barTopPx / 2 - (ys[0] + ys[ys.length - 1]) / 2;
for (let k = 0; k < ys.length; k += 1) ys[k] += shift;
if (ys[0] < 2) {
const lift = 2 - ys[0];
for (let k = 0; k < ys.length; k += 1) ys[k] += lift;
}
}
const offsets = {};
tinyIdx.forEach((i, k) => {
offsets[i] = centers[i] - ys[k];
});
return offsets;
};
const rowOffsets = rows.map(fanOutsideLabels);
return { return {
backgroundColor: '#fff', backgroundColor: '#fff',
title: { title: {
...titleStyle, ...titleStyle,
text: '泡泡玛特渠道收入结构与增速', text: `泡泡玛特渠道收入结构与增速(${financialViewLabel(view)})`,
subtext: '渠道收入为堆叠柱;折线为营业收入同比增速', subtext: ['渠道收入为堆叠柱,柱内及柱侧标注各渠道数值;折线为营业收入同比增速', financialViewDisclosure(view)].filter(Boolean).join('\n'),
}, },
legend: { legend: {
...legendStyle, ...legendStyle,
@@ -32,7 +94,7 @@ export function channelRevenueChart(rows, { width, height }) {
textStyle: { fontFamily: FONT }, textStyle: { fontFamily: FONT },
valueFormatter: (value) => value.toFixed(1), valueFormatter: (value) => value.toFixed(1),
}, },
xAxis: { ...periodAxis, data: col(rows, 'period') }, xAxis: { ...standalonePeriodAxis, data: col(rows, 'period') },
yAxis: [ yAxis: [
{ {
...axisCommon, ...axisCommon,
@@ -42,6 +104,8 @@ export function channelRevenueChart(rows, { width, height }) {
nameGap: 56, nameGap: 56,
nameTextStyle: { color: PALETTE.grey, fontSize: 13, fontFamily: FONT }, nameTextStyle: { color: PALETTE.grey, fontSize: 13, fontFamily: FONT },
min: 0, min: 0,
max: axisMax,
interval: axisMax % 4 === 0 ? axisMax / 4 : axisMax / 5,
}, },
{ {
...axisCommon, ...axisCommon,
@@ -51,20 +115,45 @@ export function channelRevenueChart(rows, { width, height }) {
nameGap: 60, nameGap: 60,
nameRotate: -90, nameRotate: -90,
nameTextStyle: { color: PALETTE.grey, fontSize: 13, fontFamily: FONT }, nameTextStyle: { color: PALETTE.grey, fontSize: 13, fontFamily: FONT },
min: 0, min: yoyMin,
max: 250, max: yoyMax,
interval: 50, interval: 50,
axisLabel: { ...axisCommon.axisLabel, formatter: '{value}%' }, axisLabel: { ...axisCommon.axisLabel, formatter: '{value}%' },
splitLine: { show: false }, splitLine: { show: false },
}, },
], ],
series: [ series: [
...channels.map((item) => ({ ...channels.map((item, ci) => ({
name: item.name, name: item.name,
type: 'bar', type: 'bar',
stack: 'channel-revenue', stack: 'channel-revenue',
data: col(rows, item.key), data: rows.map((row, ri) => {
const value = num(row[item.key]);
if (value === null) return null;
const inside = value * pxPerUnit >= MIN_INSIDE_PX;
return {
value,
label: inside
? { position: 'inside', color: INSIDE_LABEL_COLORS[item.key] }
: {
position: 'right',
color: PALETTE.ink,
distance: 3,
offset: [0, rowOffsets[ri][ci] || 0],
textBorderColor: '#fff',
textBorderWidth: 2,
},
};
}),
itemStyle: { color: item.color }, itemStyle: { color: item.color },
label: {
show: true,
fontSize: 10,
fontWeight: 500,
fontFamily: FONT,
formatter: (p) => Number(p.value).toFixed(1),
overflow: 'none',
},
emphasis: { focus: 'series' }, emphasis: { focus: 'series' },
})), })),
{ {
+6 -5
View File
@@ -1,6 +1,7 @@
import { col } from '../csv.js'; import { col } from '../csv.js';
import { import {
PALETTE, FONT, axisCommon, periodAxis, titleStyle, PALETTE, FONT, axisCommon, standalonePeriodAxis, titleStyle,
financialViewDisclosure, financialViewLabel,
lineSeries, standardLegend, footnote, watermarks, LAYOUT, lineSeries, standardLegend, footnote, watermarks, LAYOUT,
} from '../theme.js'; } from '../theme.js';
@@ -15,15 +16,15 @@ import {
*/ */
const LINE_COLOR = '#7B2D42'; const LINE_COLOR = '#7B2D42';
export function ppmtCoreMarginChart(rows, { width, height }) { export function ppmtCoreMarginChart(rows, { width, height, view = 'all' }) {
const pct = (p) => `${p.value.toFixed(1)}%`; const pct = (p) => `${p.value.toFixed(1)}%`;
return { return {
backgroundColor: '#fff', backgroundColor: '#fff',
title: { title: {
...titleStyle, ...titleStyle,
text: '泡泡玛特核心利润率(20H1-26H1)', text: `泡泡玛特核心利润率(${financialViewLabel(view)})`,
subtext: '核心利润率 = 毛利率 − 销售费用率 − 管理费用率,剔除汇兑与投资等非主业扰动', subtext: ['核心利润率 = 毛利率 − 销售费用率 − 管理费用率,剔除汇兑与投资等非主业扰动', financialViewDisclosure(view)].filter(Boolean).join('\n'),
}, },
legend: standardLegend(['核心利润率'], { itemGap: 32 }), legend: standardLegend(['核心利润率'], { itemGap: 32 }),
grid: { left: 78, right: 60, top: LAYOUT.gridTop, bottom: 90 }, grid: { left: 78, right: 60, top: LAYOUT.gridTop, bottom: 90 },
@@ -32,7 +33,7 @@ export function ppmtCoreMarginChart(rows, { width, height }) {
valueFormatter: (v) => `${v.toFixed(1)}%`, valueFormatter: (v) => `${v.toFixed(1)}%`,
textStyle: { fontFamily: FONT }, textStyle: { fontFamily: FONT },
}, },
xAxis: { ...periodAxis, data: col(rows, 'period') }, xAxis: { ...standalonePeriodAxis, data: col(rows, 'period') },
yAxis: { yAxis: {
...axisCommon, ...axisCommon,
type: 'value', type: 'value',
+10 -9
View File
@@ -1,6 +1,7 @@
import { col } from '../csv.js'; import { col } from '../csv.js';
import { import {
PALETTE, FONT, axisCommon, periodAxis, titleStyle, legendStyle, PALETTE, FINANCIAL_COLORS, FONT, axisCommon, standalonePeriodAxis, titleStyle, legendStyle,
financialViewDisclosure, financialViewLabel,
footnote, watermarks, LAYOUT, footnote, watermarks, LAYOUT,
} from '../theme.js'; } from '../theme.js';
@@ -9,7 +10,7 @@ import {
* 核心利润 = 毛利 - 销售费用 - 管理费用,反映主业盈利能力去除营销和管理成本后的净额。 * 核心利润 = 毛利 - 销售费用 - 管理费用,反映主业盈利能力去除营销和管理成本后的净额。
* 柱状图展示绝对值(亿元),折线图展示同比增速(%)。 * 柱状图展示绝对值(亿元),折线图展示同比增速(%)。
*/ */
export function ppmtCoreProfitChart(rows, { width, height }) { export function ppmtCoreProfitChart(rows, { width, height, view = 'all' }) {
const periods = col(rows, 'period'); const periods = col(rows, 'period');
const core = col(rows, 'core_profit'); const core = col(rows, 'core_profit');
const yoy = col(rows, 'yoy'); const yoy = col(rows, 'yoy');
@@ -18,8 +19,8 @@ export function ppmtCoreProfitChart(rows, { width, height }) {
backgroundColor: '#fff', backgroundColor: '#fff',
title: { title: {
...titleStyle, ...titleStyle,
text: '泡泡玛特核心利润及同比增速(20H1-26H1)', text: `泡泡玛特核心利润及同比增速(${financialViewLabel(view)})`,
subtext: '核心利润 = 毛利 - 销售费用 - 管理费用,单位:亿元', subtext: ['核心利润 = 毛利 - 销售费用 - 管理费用,单位:亿元', financialViewDisclosure(view)].filter(Boolean).join('\n'),
}, },
legend: { legend: {
...legendStyle, ...legendStyle,
@@ -49,7 +50,7 @@ export function ppmtCoreProfitChart(rows, { width, height }) {
return result; return result;
}, },
}, },
xAxis: { ...periodAxis, data: periods }, xAxis: { ...standalonePeriodAxis, data: periods },
yAxis: [ yAxis: [
{ {
// 左轴:核心利润(亿元) // 左轴:核心利润(亿元)
@@ -90,7 +91,7 @@ export function ppmtCoreProfitChart(rows, { width, height }) {
yAxisIndex: 0, yAxisIndex: 0,
data: core, data: core,
barWidth: 32, barWidth: 32,
itemStyle: { color: PALETTE.navy }, itemStyle: { color: FINANCIAL_COLORS.netProfit },
label: { label: {
show: true, show: true,
position: 'top', position: 'top',
@@ -107,12 +108,12 @@ export function ppmtCoreProfitChart(rows, { width, height }) {
type: 'line', type: 'line',
yAxisIndex: 1, yAxisIndex: 1,
data: yoy, data: yoy,
lineStyle: { color: PALETTE.coral, width: 3.08 }, // 与拆解图毛利率线一致 lineStyle: { color: FINANCIAL_COLORS.cashConversion, width: 3.08 },
// 空心圆点:白心 + 彩色描边。图例会自动继承这个样式, // 空心圆点:白心 + 彩色描边。图例会自动继承这个样式,
// 渲染成「短线 + 空心圆」 // 渲染成「短线 + 空心圆」
itemStyle: { itemStyle: {
color: '#fff', color: '#fff',
borderColor: PALETTE.coral, borderColor: FINANCIAL_COLORS.cashConversion,
borderWidth: 2.5, // 与 theme.js lineSeries 一致 borderWidth: 2.5, // 与 theme.js lineSeries 一致
}, },
symbol: 'circle', symbol: 'circle',
@@ -123,7 +124,7 @@ export function ppmtCoreProfitChart(rows, { width, height }) {
position: 'top', position: 'top',
fontFamily: FONT, fontFamily: FONT,
fontSize: 11, fontSize: 11,
color: PALETTE.coral, color: FINANCIAL_COLORS.cashConversion,
fontWeight: '600', fontWeight: '600',
formatter: (p) => { formatter: (p) => {
if (p.value === null || p.value === '') return ''; if (p.value === null || p.value === '') return '';
+7 -6
View File
@@ -1,17 +1,18 @@
import { col } from '../csv.js'; import { col } from '../csv.js';
import { import {
PALETTE, FONT, axisCommon, periodAxis, titleStyle, legendStyle, PALETTE, FONT, axisCommon, standalonePeriodAxis, titleStyle, legendStyle,
financialViewDisclosure, financialViewLabel,
lineSeries, footnote, watermarks, LAYOUT, lineSeries, footnote, watermarks, LAYOUT,
} from '../theme.js'; } from '../theme.js';
/** 泡泡玛特期间费用结构:观察销售投入、管理成本与费用率的规模效应。 */ /** 泡泡玛特期间费用结构:观察销售投入、管理成本与费用率的规模效应。 */
export function expenseStructureChart(rows, { width, height }) { export function expenseStructureChart(rows, { width, height, view = 'all' }) {
return { return {
backgroundColor: '#fff', backgroundColor: '#fff',
title: { title: {
...titleStyle, ...titleStyle,
text: '泡泡玛特费用结构与费用率(年度)', text: `泡泡玛特费用结构与费用率(${financialViewLabel(view)})`,
subtext: '销售、管理、财务费用;折线为四项费用率', subtext: ['销售、管理、财务费用;折线为四项费用率', financialViewDisclosure(view)].filter(Boolean).join('\n'),
}, },
legend: { legend: {
...legendStyle, ...legendStyle,
@@ -25,7 +26,7 @@ export function expenseStructureChart(rows, { width, height }) {
textStyle: { fontFamily: FONT }, textStyle: { fontFamily: FONT },
valueFormatter: (value) => value.toFixed(1), valueFormatter: (value) => value.toFixed(1),
}, },
xAxis: { ...periodAxis, data: col(rows, 'period') }, xAxis: { ...standalonePeriodAxis, data: col(rows, 'period') },
yAxis: [ yAxis: [
{ {
...axisCommon, ...axisCommon,
@@ -68,7 +69,7 @@ export function expenseStructureChart(rows, { width, height }) {
], ],
graphic: [ graphic: [
...watermarks(width, height), ...watermarks(width, height),
footnote('数据来源:泡泡玛特合并利润表(年度口径)| @思考的Joey'), footnote('数据来源:泡泡玛特合并利润表;H2为单独半年 | @思考的Joey'),
], ],
}; };
} }
+7 -6
View File
@@ -1,6 +1,7 @@
import { col } from '../csv.js'; import { col } from '../csv.js';
import { import {
PALETTE, FONT, axisCommon, periodAxis, titleStyle, standardLegend, PALETTE, FONT, axisCommon, standalonePeriodAxis, titleStyle, standardLegend,
financialViewDisclosure, financialViewLabel,
lineSeries, footnote, watermarks, LAYOUT, lineSeries, footnote, watermarks, LAYOUT,
} from '../theme.js'; } from '../theme.js';
@@ -43,15 +44,15 @@ function labeledData(rows, key) {
}); });
} }
export function ppmtGrowthChart(rows, { width, height }) { export function ppmtGrowthChart(rows, { width, height, view = 'all' }) {
const pct = (p) => `${p.value}%`; const pct = (p) => `${p.value}%`;
return { return {
backgroundColor: '#fff', backgroundColor: '#fff',
title: { title: {
...titleStyle, ...titleStyle,
text: '泡泡玛特营收到利润的增速对比(2020H1-2026H1)', text: `泡泡玛特营收到利润的增速对比(${financialViewLabel(view)})`,
subtext: '同比增速(%),净利增速显著高于营收增速体现利润弹性', subtext: ['同比增速(%),净利增速显著高于营收增速体现利润弹性', financialViewDisclosure(view)].filter(Boolean).join('\n'),
}, },
legend: standardLegend(['非GAAP归母净利', '毛利润', '营业收入', '营业成本'], { itemGap: 34 }), legend: standardLegend(['非GAAP归母净利', '毛利润', '营业收入', '营业成本'], { itemGap: 34 }),
grid: { left: 84, right: 56, top: LAYOUT.gridTop, bottom: 80 }, grid: { left: 84, right: 56, top: LAYOUT.gridTop, bottom: 80 },
@@ -60,7 +61,7 @@ export function ppmtGrowthChart(rows, { width, height }) {
valueFormatter: (v) => `${v}%`, valueFormatter: (v) => `${v}%`,
textStyle: { fontFamily: FONT }, textStyle: { fontFamily: FONT },
}, },
xAxis: { ...periodAxis, data: col(rows, 'period') }, xAxis: { ...standalonePeriodAxis, data: col(rows, 'period') },
yAxis: { yAxis: {
...axisCommon, ...axisCommon,
type: 'value', type: 'value',
@@ -105,7 +106,7 @@ export function ppmtGrowthChart(rows, { width, height }) {
})), })),
graphic: [ graphic: [
...watermarks(width, height), ...watermarks(width, height),
footnote('数据来源:泡泡玛特财报,2020H1/H2非GAAP净利用归母净利替代 | @思考的Joey'), footnote('数据来源:泡泡玛特财报;2020H1/H2非GAAP净利用归母净利替代 | @思考的Joey'),
], ],
}; };
} }
+82
View File
@@ -0,0 +1,82 @@
import { col } from '../csv.js';
import {
FINANCIAL_COLORS, PALETTE, FONT, axisCommon, standalonePeriodAxis, titleStyle, standardLegend,
financialViewLabel, barSeries, lineSeries, footnote, watermarks, LAYOUT,
} from '../theme.js';
/** 泡泡玛特存货余额与同报告期同比:存货为期末时点数。 */
export function inventoryBalanceChart(rows, { width, height, view = 'all' }) {
const yoy = col(rows, 'yoy').filter(Number.isFinite);
const yoyMin = Math.min(0, Math.floor(Math.min(...yoy) / 20) * 20);
const yoyMax = Math.max(50, Math.ceil(Math.max(...yoy) / 50) * 50);
const barWidth = view === 'all' ? 28.8 : 60.8;
return {
backgroundColor: '#fff',
title: {
...titleStyle,
text: `泡泡玛特存货余额及同比(${financialViewLabel(view)})`,
subtext: '柱状为报告期末存货余额;折线为同报告期同比(H1 对 H1、全年对全年)',
},
legend: standardLegend(['存货余额', '存货同比']),
grid: { left: 78, right: 82, top: LAYOUT.gridTop, bottom: 90 },
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
textStyle: { fontFamily: FONT },
valueFormatter: (value) => `${value.toFixed(1)}`,
},
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,
},
{
...axisCommon,
type: 'value',
name: '同比(%)',
nameLocation: 'middle',
nameGap: 58,
nameRotate: -90,
nameTextStyle: { color: PALETTE.grey, fontSize: 13, fontFamily: FONT },
min: yoyMin,
max: yoyMax,
interval: (yoyMax - yoyMin) / 5,
axisLabel: { ...axisCommon.axisLabel, formatter: '{value}%' },
splitLine: { show: false },
},
],
series: [
{
...barSeries({
name: '存货余额',
data: col(rows, 'inventory'),
color: FINANCIAL_COLORS.investingCashFlow,
barWidth,
formatter: (point) => point.value.toFixed(1),
}),
emphasis: { focus: 'series' },
},
{
...lineSeries({
name: '存货同比',
data: col(rows, 'yoy'),
color: FINANCIAL_COLORS.cashConversion,
formatter: (point) => `${point.value.toFixed(1)}%`,
}),
yAxisIndex: 1,
z: 10,
},
],
graphic: [
...watermarks(width, height),
footnote('数据来源:泡泡玛特历年合并资产负债表;存货为报告期末时点数 | @思考的Joey'),
],
};
}
+5 -4
View File
@@ -1,6 +1,7 @@
import { col } from '../csv.js'; import { col } from '../csv.js';
import { import {
PALETTE, FONT, axisCommon, categoryAxis, titleStyle, legendStyle, PALETTE, FONT, axisCommon, categoryAxis, titleStyle, legendStyle,
financialViewDisclosure, financialViewLabel, shortStandalonePeriod,
lineSeries, footnote, watermarks, LAYOUT, lineSeries, footnote, watermarks, LAYOUT,
} from '../theme.js'; } from '../theme.js';
@@ -8,7 +9,7 @@ import {
* 泡泡玛特主要IP收入趋势图 * 泡泡玛特主要IP收入趋势图
* 多条折线展示各IP的收入变化 * 多条折线展示各IP的收入变化
*/ */
export function ipChart(rows, { width, height }) { export function ipChart(rows, { width, height, view = 'all' }) {
// 提取时期(从第一行的列名) // 提取时期(从第一行的列名)
const periods = Object.keys(rows[0]).filter(k => k !== 'ip'); const periods = Object.keys(rows[0]).filter(k => k !== 'ip');
@@ -42,8 +43,8 @@ export function ipChart(rows, { width, height }) {
backgroundColor: '#fff', backgroundColor: '#fff',
title: { title: {
...titleStyle, ...titleStyle,
text: '泡泡玛特主要IP收入趋势(2021H1-2025H2)', text: `泡泡玛特主要IP收入趋势(${financialViewLabel(view)})`,
subtext: '单位:亿元', subtext: ['单位:亿元;H2为单独半年', financialViewDisclosure(view)].filter(Boolean).join('\n'),
}, },
legend: { legend: {
...legendStyle, ...legendStyle,
@@ -62,7 +63,7 @@ export function ipChart(rows, { width, height }) {
data: periods, data: periods,
axisLabel: { axisLabel: {
...categoryAxis.axisLabel, ...categoryAxis.axisLabel,
formatter: (value) => String(value).replace(/^20/, ''), formatter: shortStandalonePeriod,
}, },
}, },
yAxis: { yAxis: {
+10 -15
View File
@@ -1,6 +1,7 @@
import { col } from '../csv.js'; import { col } from '../csv.js';
import { import {
PALETTE, FONT, axisCommon, categoryAxis, titleStyle, labelStyle, PALETTE, FONT, axisCommon, standalonePeriodAxis, titleStyle, labelStyle,
financialViewDisclosure, financialViewLabel,
barSeries, lineSeries, standardLegend, footnote, watermarks, LAYOUT, barSeries, lineSeries, standardLegend, footnote, watermarks, LAYOUT,
} from '../theme.js'; } from '../theme.js';
@@ -34,13 +35,13 @@ const BAR_COLOR = '#176B52'; // 墨翡翠
const LINE_COLOR = '#7A3FE0'; // 鲜亮紫 const LINE_COLOR = '#7A3FE0'; // 鲜亮紫
const LINE_ON_BAR = '#DCC9FF'; // 压在翡翠柱面上时用的浅紫(对比度约 6:1) const LINE_ON_BAR = '#DCC9FF'; // 压在翡翠柱面上时用的浅紫(对比度约 6:1)
const Y_MAX = 160; // 左轴上限
const YOY_MIN = -50; // 右轴下限 const YOY_MIN = -50; // 右轴下限
const YOY_MAX = 150; // 右轴上限 const YOY_MAX = 150; // 右轴上限
export function ppmtIpExMonstersChart(rows, { width, height }) { export function ppmtIpExMonstersChart(rows, { width, height, view = 'all' }) {
const periods = col(rows, 'period'); const periods = col(rows, 'period');
const revenue = col(rows, 'revenue'); const revenue = col(rows, 'revenue');
const yMax = Math.max(...revenue) > 160 ? 300 : 160;
// 前两期无同比基数,CSV 里留空;parseCSV 会给出 '',统一转成 null // 前两期无同比基数,CSV 里留空;parseCSV 会给出 '',统一转成 null
const yoy = col(rows, 'yoy').map((v) => (v === '' ? null : v)); const yoy = col(rows, 'yoy').map((v) => (v === '' ? null : v));
@@ -50,7 +51,7 @@ export function ppmtIpExMonstersChart(rows, { width, height }) {
const labelGap = (8 + 13) / plotH; // labelStyle 的 distance + 字高,换成占比 const labelGap = (8 + 13) / plotH; // labelStyle 的 distance + 字高,换成占比
const yoyData = yoy.map((v, i) => { const yoyData = yoy.map((v, i) => {
if (v === null) return null; if (v === null) return null;
const barFrac = revenue[i] / Y_MAX; const barFrac = revenue[i] / yMax;
const lineFrac = (v - YOY_MIN) / (YOY_MAX - YOY_MIN); const lineFrac = (v - YOY_MIN) / (YOY_MAX - YOY_MIN);
// 标签整体(底边到顶边)都落在柱子高度以内 → 压在柱面上 // 标签整体(底边到顶边)都落在柱子高度以内 → 压在柱面上
const onBar = lineFrac + labelGap < barFrac; const onBar = lineFrac + labelGap < barFrac;
@@ -61,8 +62,8 @@ export function ppmtIpExMonstersChart(rows, { width, height }) {
backgroundColor: '#fff', backgroundColor: '#fff',
title: { title: {
...titleStyle, ...titleStyle,
text: '泡泡玛特除 THE MONSTERS 外的IP收入及同比(21H1-26H1)', text: `泡泡玛特除 THE MONSTERS 外的IP收入及同比(${financialViewLabel(view)})`,
subtext: 'IP收入合计 − THE MONSTERS,单位:亿元;H2 为下半年单期,非全年累计', subtext: ['IP收入合计 − THE MONSTERS,单位:亿元;H2为下半年单期,非全年累计', financialViewDisclosure(view)].filter(Boolean).join('\n'),
}, },
legend: standardLegend(['除MONSTERS外收入', '同比增速'], { itemGap: 32 }), legend: standardLegend(['除MONSTERS外收入', '同比增速'], { itemGap: 32 }),
grid: { left: 78, right: 128, top: LAYOUT.gridTop, bottom: 90 }, grid: { left: 78, right: 128, top: LAYOUT.gridTop, bottom: 90 },
@@ -82,12 +83,7 @@ export function ppmtIpExMonstersChart(rows, { width, height }) {
return out; return out;
}, },
}, },
xAxis: { xAxis: { ...standalonePeriodAxis, data: periods },
...categoryAxis,
data: periods,
// 只去世纪前缀:这张表的 H2 是单半年,不能简写成「全年」
axisLabel: { ...categoryAxis.axisLabel, formatter: (v) => v.replace(/^20/, '') },
},
yAxis: [ yAxis: [
{ {
...axisCommon, ...axisCommon,
@@ -97,9 +93,8 @@ export function ppmtIpExMonstersChart(rows, { width, height }) {
nameGap: 50, nameGap: 50,
nameTextStyle: { color: PALETTE.grey, fontSize: 13, fontFamily: FONT }, nameTextStyle: { color: PALETTE.grey, fontSize: 13, fontFamily: FONT },
min: 0, min: 0,
// 定死 160:留出顶部余量,最高柱(139.0)不会顶到绘图区上沿 max: yMax,
max: Y_MAX, interval: yMax === 160 ? 20 : 50,
interval: 20,
}, },
{ {
type: 'value', type: 'value',
+11 -27
View File
@@ -1,6 +1,7 @@
import { import {
PALETTE, FONT, axisCommon, categoryAxis, titleStyle, LEGEND_ITEM_WIDTH, PALETTE, FONT, axisCommon, categoryAxis, titleStyle,
barSeries, footnote, watermarks, LAYOUT, financialViewDisclosure, financialViewLabel, shortStandalonePeriod,
standardLegend, barSeries, footnote, watermarks, LAYOUT,
} from '../theme.js'; } from '../theme.js';
/** /**
@@ -35,10 +36,7 @@ const COLORS = {
'外采及其他': '#8C8C8C', '外采及其他': '#8C8C8C',
}; };
const LEGEND_WIDTH = 160; export function ppmtIpShareHistoryChart(rows, { width, height, view = 'all' }) {
const LEGEND_RIGHT = 20;
export function ppmtIpShareHistoryChart(rows, { width, height }) {
const periods = Object.keys(rows[0]).filter((k) => k !== 'ip'); const periods = Object.keys(rows[0]).filter((k) => k !== 'ip');
const names = rows.map((r) => r.ip); const names = rows.map((r) => r.ip);
const totals = periods.map((p) => rows.reduce((s, r) => s + r[p], 0)); const totals = periods.map((p) => rows.reduce((s, r) => s + r[p], 0));
@@ -49,20 +47,11 @@ export function ppmtIpShareHistoryChart(rows, { width, height }) {
backgroundColor: '#fff', backgroundColor: '#fff',
title: { title: {
...titleStyle, ...titleStyle,
text: '泡泡玛特各IP半年收入占比(21H1-26H1)', text: `泡泡玛特各IP收入占比(${financialViewLabel(view)})`,
subtext: '占当期IP收入合计的比重;H2 为下半年单期,非全年累计', subtext: ['占当期IP收入合计的比重;H2为下半年单期,非全年累计', financialViewDisclosure(view)].filter(Boolean).join('\n'),
},
legend: {
orient: 'vertical',
right: LEGEND_RIGHT,
width: LEGEND_WIDTH,
top: 'middle',
itemGap: 12,
itemWidth: LEGEND_ITEM_WIDTH,
itemHeight: 14,
data: names,
textStyle: { color: PALETTE.ink, fontSize: 12, fontFamily: FONT },
}, },
// 11 个系列较多:紧凑间距靠 ECharts 自动换行成多行
legend: standardLegend(names, { itemGap: 12, itemWidth: 14, itemHeight: 14 }),
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',
axisPointer: { type: 'shadow' }, axisPointer: { type: 'shadow' },
@@ -79,17 +68,12 @@ export function ppmtIpShareHistoryChart(rows, { width, height }) {
.join('<br/>'); .join('<br/>');
}, },
}, },
grid: { grid: { left: 78, right: 82, top: LAYOUT.gridTop, bottom: 70 },
left: 78,
right: LEGEND_RIGHT + LEGEND_WIDTH + 20,
top: LAYOUT.gridTop,
bottom: 70,
},
xAxis: { xAxis: {
...categoryAxis, ...categoryAxis,
data: periods, data: periods,
// 只去世纪前缀:这张表的 H2 是单半年,不能简写成「全年」 // 只去世纪前缀:这张表的 H2 是单半年,不能简写成「全年」
axisLabel: { ...categoryAxis.axisLabel, formatter: (v) => v.replace(/^20/, '') }, axisLabel: { ...categoryAxis.axisLabel, formatter: shortStandalonePeriod },
}, },
yAxis: { yAxis: {
...axisCommon, ...axisCommon,
@@ -113,7 +97,7 @@ export function ppmtIpShareHistoryChart(rows, { width, height }) {
fontFamily: FONT, fontFamily: FONT,
fontWeight: 500, fontWeight: 500,
// 低于 4% 的段塞不进字,留空靠 tooltip 读数 // 低于 4% 的段塞不进字,留空靠 tooltip 读数
formatter: (p) => (p.value >= 4 ? p.value.toFixed(1) : ''), formatter: (p) => (p.value >= 4 ? `${p.value.toFixed(1)}%` : ''),
}, },
emphasis: { focus: 'series' }, emphasis: { focus: 'series' },
})), })),
+22 -12
View File
@@ -1,19 +1,20 @@
import { col } from '../csv.js'; import { col } from '../csv.js';
import { import {
PALETTE, FONT, axisCommon, periodAxis, titleStyle, legendStyle, PALETTE, FINANCIAL_COLORS, FONT, axisCommon, standalonePeriodAxis, titleStyle, legendStyle,
lineSeries, footnote, watermarks, LAYOUT, financialViewLabel,
lineDataWithSafeLabels, lineSeries, barSeries, footnote, watermarks, LAYOUT,
} from '../theme.js'; } from '../theme.js';
/** 泡泡玛特现金、负债与净营运资本:观察流动性与资金占用。 */ /** 泡泡玛特现金、负债与净营运资本:观察流动性与资金占用。 */
export function liquidityWorkingCapitalChart(rows, { width, height }) { export function liquidityWorkingCapitalChart(rows, { width, height, view = 'all' }) {
const periods = col(rows, 'period'); const periods = col(rows, 'period');
return { return {
backgroundColor: '#fff', backgroundColor: '#fff',
title: { title: {
...titleStyle, ...titleStyle,
text: '泡泡玛特现金、负债与净营运资本', text: `泡泡玛特现金、负债与净营运资本(${financialViewLabel(view)})`,
subtext: '净营运资本 = 流动资产 − 流动负债;单位:亿元', subtext: '净营运资本 = 流动资产 − 流动负债;单位:亿元;H2 为年末时点',
}, },
legend: { legend: {
...legendStyle, ...legendStyle,
@@ -27,7 +28,7 @@ export function liquidityWorkingCapitalChart(rows, { width, height }) {
textStyle: { fontFamily: FONT }, textStyle: { fontFamily: FONT },
valueFormatter: (value) => `${value.toFixed(1)} 亿元`, valueFormatter: (value) => `${value.toFixed(1)} 亿元`,
}, },
xAxis: { ...periodAxis, data: periods }, xAxis: { ...standalonePeriodAxis, data: periods },
yAxis: { yAxis: {
...axisCommon, ...axisCommon,
type: 'value', type: 'value',
@@ -39,26 +40,35 @@ export function liquidityWorkingCapitalChart(rows, { width, height }) {
}, },
series: [ series: [
{ {
...barSeries({
name: '货币资金', name: '货币资金',
type: 'bar',
data: col(rows, 'cash'), data: col(rows, 'cash'),
color: FINANCIAL_COLORS.investingCashFlow,
barWidth: 18, barWidth: 18,
itemStyle: { color: PALETTE.blue }, formatter: (p) => p.value.toFixed(1),
}),
emphasis: { focus: 'series' }, emphasis: { focus: 'series' },
}, },
{ {
...barSeries({
name: '负债合计', name: '负债合计',
type: 'bar',
data: col(rows, 'total_liabilities'), data: col(rows, 'total_liabilities'),
color: FINANCIAL_COLORS.financingCashFlow,
barWidth: 18, barWidth: 18,
itemStyle: { color: PALETTE.orange }, formatter: (p) => p.value.toFixed(1),
}),
emphasis: { focus: 'series' }, emphasis: { focus: 'series' },
}, },
{ {
...lineSeries({ ...lineSeries({
name: '净营运资本', name: '净营运资本',
data: col(rows, 'net_working_capital'), data: lineDataWithSafeLabels(rows, {
color: PALETTE.red, lineKey: 'net_working_capital',
barKeys: ['cash', 'total_liabilities'],
// 净营运资本接近货币资金时,标签置于柱体另一侧。
closePosition: 'bottom',
}),
color: FINANCIAL_COLORS.operatingCashFlow,
formatter: (p) => p.value.toFixed(1), formatter: (p) => p.value.toFixed(1),
width: 3, width: 3,
}), }),
+6 -5
View File
@@ -1,6 +1,7 @@
import { col } from '../csv.js'; import { col } from '../csv.js';
import { import {
PALETTE, FONT, axisCommon, periodAxis, titleStyle, standardLegend, PALETTE, FONT, axisCommon, standalonePeriodAxis, titleStyle, standardLegend,
financialViewDisclosure, financialViewLabel,
lineSeries, footnote, watermarks, LAYOUT, lineSeries, footnote, watermarks, LAYOUT,
} from '../theme.js'; } from '../theme.js';
@@ -54,15 +55,15 @@ function labeledData(rows, key) {
}); });
} }
export function ppmtMarginsChart(rows, { width, height }) { export function ppmtMarginsChart(rows, { width, height, view = 'all' }) {
const pct = (p) => `${p.value}%`; const pct = (p) => `${p.value}%`;
return { return {
backgroundColor: '#fff', backgroundColor: '#fff',
title: { title: {
...titleStyle, ...titleStyle,
text: '泡泡玛特利润率与费用率拆解(2020H1-2026H1)', text: `泡泡玛特利润率与费用率拆解(${financialViewLabel(view)})`,
subtext: '毛利率抬升叠加费用率下降,共同推动净利率走高(H1为半年,H2为全年累计)', subtext: ['毛利率抬升叠加费用率下降,共同推动净利率走高', financialViewDisclosure(view)].filter(Boolean).join('\n'),
}, },
legend: standardLegend(SERIES.map((series) => series.name)), legend: standardLegend(SERIES.map((series) => series.name)),
grid: { left: 78, right: 56, top: LAYOUT.gridTop, bottom: 80 }, grid: { left: 78, right: 56, top: LAYOUT.gridTop, bottom: 80 },
@@ -71,7 +72,7 @@ export function ppmtMarginsChart(rows, { width, height }) {
valueFormatter: (v) => `${v}%`, valueFormatter: (v) => `${v}%`,
textStyle: { fontFamily: FONT }, textStyle: { fontFamily: FONT },
}, },
xAxis: { ...periodAxis, data: col(rows, 'period') }, xAxis: { ...standalonePeriodAxis, data: col(rows, 'period') },
yAxis: { yAxis: {
...axisCommon, ...axisCommon,
type: 'value', type: 'value',
+12 -11
View File
@@ -1,6 +1,7 @@
import { col } from '../csv.js'; import { col } from '../csv.js';
import { import {
PALETTE, FONT, axisCommon, periodAxis, titleStyle, legendStyle, PALETTE, FONT, axisCommon, standalonePeriodAxis, titleStyle, legendStyle,
financialViewDisclosure, financialViewLabel,
lineSeries, footnote, watermarks, LAYOUT, lineSeries, footnote, watermarks, LAYOUT,
} from '../theme.js'; } from '../theme.js';
@@ -8,19 +9,19 @@ import {
* 泡泡玛特盈利能力分析图 * 泡泡玛特盈利能力分析图
* 柱状图展示收入和利润,折线图展示毛利率 * 柱状图展示收入和利润,折线图展示毛利率
*/ */
export function profitChart(rows, { width, height }) { export function profitChart(rows, { width, height, view = 'all' }) {
const periods = col(rows, 'period').reverse(); // 按时间正序 const periods = col(rows, 'period');
const revenue = col(rows, 'revenue').reverse(); const revenue = col(rows, 'revenue');
const grossProfit = col(rows, 'gross_profit').reverse(); const grossProfit = col(rows, 'gross_profit');
const netProfit = col(rows, 'net_profit').reverse(); const netProfit = col(rows, 'net_profit');
const grossMargin = col(rows, 'gross_margin').reverse(); const grossMargin = col(rows, 'gross_margin');
return { return {
backgroundColor: '#fff', backgroundColor: '#fff',
title: { title: {
...titleStyle, ...titleStyle,
text: '泡泡玛特盈利能力分析(2022H2-2026H1)', text: `泡泡玛特盈利能力分析(${financialViewLabel(view)})`,
subtext: '营收、毛利、净利润及毛利率变化', subtext: ['营收、毛利、净利润及毛利率变化', financialViewDisclosure(view)].filter(Boolean).join('\n'),
}, },
legend: { legend: {
...legendStyle, ...legendStyle,
@@ -33,7 +34,7 @@ export function profitChart(rows, { width, height }) {
axisPointer: { type: 'shadow' }, axisPointer: { type: 'shadow' },
textStyle: { fontFamily: FONT }, textStyle: { fontFamily: FONT },
}, },
xAxis: { ...periodAxis, data: periods }, xAxis: { ...standalonePeriodAxis, data: periods },
yAxis: [ yAxis: [
{ {
...axisCommon, ...axisCommon,
@@ -122,7 +123,7 @@ export function profitChart(rows, { width, height }) {
], ],
graphic: [ graphic: [
...watermarks(width, height), ...watermarks(width, height),
footnote('数据来源:泡泡玛特财报(累计数据)| @思考的Joey'), footnote('数据来源:泡泡玛特财报;H2为单独半年 | @思考的Joey'),
], ],
}; };
} }
+111
View File
@@ -0,0 +1,111 @@
import {
PALETTE, FONT, axisCommon, categoryAxis, titleStyle, standardLegend,
financialViewDisclosure, financialViewLabel, standalonePeriodAxis,
barSeries, footnote, watermarks, LAYOUT,
} from '../theme.js';
/**
* 泡泡玛特分地区营收占比(2024H1 起)。
* 归一化堆叠柱状图:每根柱为当期营收合计 = 100%,看地区结构变化。
*
* 口径:中国含港澳台,海外 = 亚太 + 美洲 + 欧洲及其他(2024 年起的新披露口径)。
* H2 为单半年(当年披露全年 − 上半年还原),非全年累计,轴用 standalonePeriodAxis。
* 2024H1 的新口径分地区收入未直接披露,按 2025 年中报披露的分地区同比还原
* (2025H1 值 ÷ (1+同比)),还原后加总与当期总营收误差 < 0.1 亿。
*
* 地区配色与同 Tab 门店数量图(ppmt_stores_retail.js)保持一致。
*/
const REGIONS = [
{ key: 'china', name: '中国', color: '#3F7FBE' },
{ key: 'apac', name: '亚太', color: '#36A28F' },
{ key: 'americas', name: '美洲', color: '#E4A349' },
{ key: 'europe', name: '欧洲及其他', color: PALETTE.brown },
];
// 浅色底(美洲金)用深字,其余深色底用白字
const INSIDE_LABEL_COLORS = {
china: '#fff',
apac: '#fff',
americas: PALETTE.ink,
europe: '#fff',
};
// 绘图区像素高:画布高 − grid top 145 − grid bottom 100;轴固定 0-100%。
// 低于约 13px 的段放不下 11px 段内标签:是柱顶段的标到柱顶上方,
// 上方还有其它段的标到柱体右侧。
const MIN_INSIDE_PX = 13;
function segmentLabel(row, item, ri, totals, keyIndex, pxPerPct) {
const pct = ((row[item.key] || 0) / totals[ri]) * 100;
if (pct * pxPerPct >= MIN_INSIDE_PX) {
return { value: pct, label: { position: 'inside', color: INSIDE_LABEL_COLORS[item.key] } };
}
const hasSegmentAbove = REGIONS.slice(keyIndex + 1).some((r) => (row[r.key] || 0) > 0);
return {
value: pct,
label: hasSegmentAbove
? { position: 'right', color: PALETTE.ink, distance: 4, textBorderColor: '#fff', textBorderWidth: 2 }
: { position: 'top', color: PALETTE.ink, distance: 4 },
};
}
export function regionShareChart(rows, { width, height, view = 'all' }) {
const periods = rows.map((row) => row.period);
const totals = rows.map((row) => REGIONS.reduce((s, item) => s + (row[item.key] || 0), 0));
const pxPerPct = (height - LAYOUT.gridTop - 100) / 100;
return {
backgroundColor: '#fff',
title: {
...titleStyle,
text: `泡泡玛特分地区营收占比(${financialViewLabel(view)})`,
subtext: ['归一化堆叠,每根柱为当期营收合计=100%;H2为下半年单期', financialViewDisclosure(view)].filter(Boolean).join('\n'),
},
legend: standardLegend(REGIONS.map((item) => item.name)),
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
textStyle: { fontFamily: FONT },
formatter: (ps) => {
const i = periods.indexOf(ps[0].axisValue);
const head = `<b>${ps[0].axisValue}</b> 合计 ${totals[i].toFixed(1)} 亿元<br/>`;
return head + ps
.slice()
.reverse()
.map((p) => `${p.marker}${p.seriesName}: ${p.value.toFixed(1)}%`
+ `(${((p.value / 100) * totals[i]).toFixed(1)} 亿元)`)
.join('<br/>');
},
},
grid: { left: 78, right: 82, top: LAYOUT.gridTop, bottom: 100 },
xAxis: { ...standalonePeriodAxis, data: periods },
yAxis: {
...axisCommon,
type: 'value',
min: 0,
max: 100,
interval: 20,
axisLabel: { ...axisCommon.axisLabel, formatter: '{value}%' },
},
series: REGIONS.map((item, keyIndex) => barSeries({
name: item.name,
data: rows.map((row, ri) => segmentLabel(row, item, ri, totals, keyIndex, pxPerPct)),
color: item.color,
barWidth: 40,
stack: 'region-share',
label: {
show: true,
position: 'inside',
fontSize: 11,
fontFamily: FONT,
fontWeight: 500,
formatter: (p) => `${p.value.toFixed(1)}%`,
},
emphasis: { focus: 'series' },
})),
graphic: [
...watermarks(width, height),
footnote('数据来源:泡泡玛特财报;中国含港澳台\n2024H1为按2025年中报披露的分地区同比还原;H2为单半年 | @思考的Joey'),
],
};
}
+110
View File
@@ -0,0 +1,110 @@
import { col } from '../csv.js';
import {
PALETTE, FONT, axisCommon, standalonePeriodAxis, titleStyle, standardLegend,
financialViewLabel, barSeries, lineSeries, footnote, watermarks, LAYOUT,
} from '../theme.js';
const REGIONS = [
{ key: 'china', name: '中国(含港澳台)', color: '#3F7FBE' },
{ key: 'apac', name: '亚太', color: '#36A28F' },
{ key: 'americas', name: '美洲', color: '#E4A349' },
{ key: 'europe_other', name: '欧洲及其他', color: PALETTE.brown },
];
function yoyByPeriod(rows, key) {
const values = new Map(rows.map((row) => [row.period, row[key]]));
return rows.map((row) => {
const year = Number(row.period.slice(0, 4));
const prior = values.get(`${year - 1}${row.period.slice(4)}`);
return Number.isFinite(row[key]) && Number.isFinite(prior) && prior !== 0
? (row[key] / prior - 1) * 100
: null;
});
}
/** 各地区零售店单店收入:收入 ÷ 期初、期末平均零售店数。 */
export function retailStoreRevenueChart(rows, { width, height, view = 'all' }) {
const values = REGIONS.flatMap((region) => col(rows, `${region.key}_per_store`)).filter(Number.isFinite);
const yMax = Math.ceil(Math.max(...values) / 1000) * 1000;
const yoySeries = REGIONS.map((region) => ({ ...region, data: yoyByPeriod(rows, `${region.key}_per_store`) }));
const yoyValues = yoySeries.flatMap((region) => region.data).filter(Number.isFinite);
const yoyMin = Math.min(0, Math.floor(Math.min(...yoyValues) / 50) * 50);
const yoyMax = Math.max(50, Math.ceil(Math.max(...yoyValues) / 50) * 50);
const legendItems = REGIONS.flatMap((region) => [region.name, `${region.name}同比`]);
return {
backgroundColor: '#fff',
title: {
...titleStyle,
text: `泡泡玛特各地区零售店单店收入(${financialViewLabel(view)})`,
subtext: '柱状为单店收入;同色虚线为单店收入同比;H2为下半年单期',
},
legend: standardLegend(legendItems, { itemGap: 18 }),
grid: { left: 78, right: 42, top: LAYOUT.gridTop, bottom: 90 },
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
textStyle: { fontFamily: FONT },
formatter: (params) => `<b>${params[0].axisValue}</b><br/>${params
.filter((item) => Number.isFinite(item.value))
.map((item) => `${item.marker}${item.seriesName}: ${Number(item.value).toFixed(1)}${item.seriesName.endsWith('同比') ? '%' : ' 万元/店'}`)
.join('<br/>')}`,
},
xAxis: { ...standalonePeriodAxis, data: col(rows, 'period') },
yAxis: [
{
...axisCommon,
type: 'value',
name: '单店收入(万元/店)',
nameLocation: 'middle',
nameGap: 58,
nameTextStyle: { color: PALETTE.grey, fontSize: 13, fontFamily: FONT },
min: 0,
max: yMax,
interval: yMax / 5,
},
{
...axisCommon,
type: 'value',
name: '单店收入同比(%)',
nameLocation: 'middle',
nameGap: 60,
nameRotate: -90,
nameTextStyle: { color: PALETTE.grey, fontSize: 13, fontFamily: FONT },
min: yoyMin,
max: yoyMax,
interval: 50,
axisLabel: { ...axisCommon.axisLabel, formatter: '{value}%' },
splitLine: { show: false },
},
],
series: [
...REGIONS.map((region) => ({
...barSeries({
name: region.name,
data: col(rows, `${region.key}_per_store`),
color: region.color,
barWidth: view === 'all' ? 17 : 34,
formatter: (point) => point.value.toFixed(0),
}),
emphasis: { focus: 'series' },
})),
...yoySeries.map((region) => ({
...lineSeries({
name: `${region.name}同比`,
data: region.data,
color: region.color,
width: 2,
formatter: (point) => `${point.value.toFixed(0)}%`,
}),
yAxisIndex: 1,
lineStyle: { color: region.color, width: 2, type: 'dashed' },
z: 10,
})),
],
graphic: [
...watermarks(width, height),
footnote('数据来源:泡泡玛特历年公告;收入仅含“零售店”渠道;H2收入=全年−H1\n2024年海外各区域缺少期初店数,相关单店收入留空 | @思考的Joey'),
],
};
}
+108
View File
@@ -0,0 +1,108 @@
import { col } from '../csv.js';
import {
PALETTE, FONT, axisCommon, standalonePeriodAxis, titleStyle, standardLegend,
financialViewLabel, barSeries, lineSeries, footnote, watermarks, LAYOUT,
} from '../theme.js';
const REGIONS = [
{ key: 'china', name: '中国(含港澳台)', color: '#71AD81' },
{ key: 'overseas', name: '海外', color: PALETTE.brown },
];
function yoyByPeriod(rows, key) {
const values = new Map(rows.map((row) => [row.period, row[key]]));
return rows.map((row) => {
const year = Number(row.period.slice(0, 4));
const prior = values.get(`${year - 1}${row.period.slice(4)}`);
return Number.isFinite(row[key]) && Number.isFinite(prior) && prior !== 0
? (row[key] / prior - 1) * 100
: null;
});
}
/** 中国与海外机器人商店单店收入:收入 ÷ 期初、期末平均机器人商店数。 */
export function robotStoreRevenueChart(rows, { width, height, view = 'all' }) {
const values = REGIONS.flatMap((region) => col(rows, `${region.key}_per_store`)).filter(Number.isFinite);
const yMax = Math.ceil(Math.max(...values) / 50) * 50;
const yoySeries = REGIONS.map((region) => ({ ...region, data: yoyByPeriod(rows, `${region.key}_per_store`) }));
const yoyValues = yoySeries.flatMap((region) => region.data).filter(Number.isFinite);
const yoyMin = Math.min(0, Math.floor(Math.min(...yoyValues) / 50) * 50);
const yoyMax = Math.max(50, Math.ceil(Math.max(...yoyValues) / 50) * 50);
const legendItems = REGIONS.flatMap((region) => [region.name, `${region.name}同比`]);
return {
backgroundColor: '#fff',
title: {
...titleStyle,
text: `泡泡玛特机器人商店单店收入(${financialViewLabel(view)})`,
subtext: '柱状为单店收入;同色虚线为单店收入同比;H2为下半年单期',
},
legend: standardLegend(legendItems, { itemGap: 18 }),
grid: { left: 78, right: 42, top: LAYOUT.gridTop, bottom: 90 },
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
textStyle: { fontFamily: FONT },
formatter: (params) => `<b>${params[0].axisValue}</b><br/>${params
.filter((item) => Number.isFinite(item.value))
.map((item) => `${item.marker}${item.seriesName}: ${Number(item.value).toFixed(1)}${item.seriesName.endsWith('同比') ? '%' : ' 万元/店'}`)
.join('<br/>')}`,
},
xAxis: { ...standalonePeriodAxis, data: col(rows, 'period') },
yAxis: [
{
...axisCommon,
type: 'value',
name: '单店收入(万元/店)',
nameLocation: 'middle',
nameGap: 58,
nameTextStyle: { color: PALETTE.grey, fontSize: 13, fontFamily: FONT },
min: 0,
max: yMax,
interval: yMax / 5,
},
{
...axisCommon,
type: 'value',
name: '单店收入同比(%)',
nameLocation: 'middle',
nameGap: 60,
nameRotate: -90,
nameTextStyle: { color: PALETTE.grey, fontSize: 13, fontFamily: FONT },
min: yoyMin,
max: yoyMax,
interval: 50,
axisLabel: { ...axisCommon.axisLabel, formatter: '{value}%' },
splitLine: { show: false },
},
],
series: [
...REGIONS.map((region) => ({
...barSeries({
name: region.name,
data: col(rows, `${region.key}_per_store`),
color: region.color,
barWidth: view === 'all' ? 28 : 52,
formatter: (point) => point.value.toFixed(0),
}),
emphasis: { focus: 'series' },
})),
...yoySeries.map((region) => ({
...lineSeries({
name: `${region.name}同比`,
data: region.data,
color: region.color,
width: 2,
formatter: (point) => `${point.value.toFixed(0)}%`,
}),
yAxisIndex: 1,
lineStyle: { color: region.color, width: 2, type: 'dashed' },
z: 10,
})),
],
graphic: [
...watermarks(width, height),
footnote('数据来源:泡泡玛特历年公告;海外收入=亚太+美洲+欧洲及其他的机器人商店收入;H2收入=全年−H1\n2024H1海外期末机器人商店数未披露,相关单店收入留空 | @思考的Joey'),
],
};
}
+134
View File
@@ -0,0 +1,134 @@
import { col } from '../csv.js';
import {
PALETTE, FONT, axisCommon, standalonePeriodAxis, titleStyle, standardLegend,
financialViewLabel, lineSeries, footnote, watermarks, LAYOUT,
} from '../theme.js';
/**
* 泡泡玛特分地区零售门店数量与同比增速。
* 堆叠柱为各期末门店数,折线为全球合计数量的同比。
* 口径:2023年及以前"海外合计"含港澳台;2024年起港澳台单列并计入中国,
* 海外合计 = 亚太 + 美洲 + 欧洲。两段口径不可直接跨 2023/2024 对比海外。
*/
// 门店数是计数而非金额,保留整数(CLAUDE.md 一位小数规则针对金额/比率)。
const SEGMENT_COLORS = {
mainland: '#3F7FBE',
hkmtw: '#AECBE8',
apac: '#36A28F',
americas: '#E4A349',
europe: PALETTE.brown,
overseas: '#C2726B',
};
// 段内标签文字色:浅色底(港澳台、美洲)用深字,深色底用白字
const INSIDE_LABEL_COLORS = {
mainland: '#fff',
hkmtw: PALETTE.ink,
apac: '#fff',
americas: PALETTE.ink,
europe: '#fff',
overseas: '#fff',
};
const regions = [
{ key: 'mainland', name: '中国内地' },
{ key: 'hkmtw', name: '港澳台' },
{ key: 'apac', name: '亚太' },
{ key: 'americas', name: '美洲' },
{ key: 'europe', name: '欧洲' },
{ key: 'overseas', name: '海外合计(含港澳台)' },
];
const num = (value) => (Number.isFinite(value) ? value : null);
// 2024 起披露口径变为港澳台单列 + 亚太/美洲/欧洲分列;此前只有含港澳台的海外合计。
// 同一根柱内两种披露结构互斥出现,堆叠总和始终等于全球合计。
function stackValue(row, key) {
const split = Number.isFinite(row.apac);
if (key === 'overseas') return split ? null : num(row.overseas);
if (!split && key !== 'mainland') return null;
return num(row[key]);
}
export function storesRetailChart(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: 700,
interval: 100,
},
{
...axisCommon,
type: 'value',
name: '全球合计同比(%)',
nameLocation: 'middle',
nameGap: 60,
nameRotate: -90,
nameTextStyle: { color: PALETTE.grey, fontSize: 13, fontFamily: FONT },
min: 0,
max: 100,
interval: 20,
axisLabel: { ...axisCommon.axisLabel, formatter: '{value}%' },
splitLine: { show: false },
},
],
series: [
...regions.map((item) => ({
name: item.name,
type: 'bar',
stack: 'stores-retail',
data: rows.map((row) => stackValue(row, item.key)),
itemStyle: { color: SEGMENT_COLORS[item.key] },
// 每段标注门店数:浅色段用深字、深色段用白字,保证段内可读
label: {
show: true,
position: 'inside',
fontSize: 10,
fontWeight: 500,
fontFamily: FONT,
color: INSIDE_LABEL_COLORS[item.key],
overflow: 'none',
},
// 相邻矮段(如 2024H1 美洲/欧洲)标签贴得近时沿 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('数据来源:泡泡玛特历年公告;门店数为各期末时点数\n2023年及以前海外合计含港澳台;2024年起港澳台单列,海外=亚太+美洲+欧洲 | @思考的Joey'),
],
};
}
+83
View File
@@ -0,0 +1,83 @@
import { col } from '../csv.js';
import {
PALETTE, FONT, axisCommon, standalonePeriodAxis, titleStyle, standardLegend,
financialViewLabel, barSeries, footnote, watermarks, LAYOUT,
} from '../theme.js';
/** 泡泡玛特分地区零售门店占比:每根柱标准化为各期末全球门店数的 100%。 */
const REGIONS = [
{ key: 'china', name: '中国(含港澳台)', color: '#3F7FBE', labelColor: '#fff' },
{ key: 'apac', name: '亚太', color: '#36A28F', labelColor: '#fff' },
{ key: 'americas', name: '美洲', color: '#E4A349', labelColor: PALETTE.ink },
{ key: 'europe', name: '欧洲', color: PALETTE.brown, labelColor: '#fff' },
{ key: 'other', name: '其他', color: PALETTE.sand, labelColor: PALETTE.ink },
];
function sharePoint(value, labelColor) {
if (!Number.isFinite(value) || value === 0) return null;
return {
value,
label: value >= 5
? { position: 'inside', color: labelColor }
: { show: false },
};
}
export function storesRetailShareChart(rows, { width, height, view = 'all' }) {
const periods = col(rows, 'period');
const barWidth = view === 'all' ? 48 : 80;
return {
backgroundColor: '#fff',
title: {
...titleStyle,
text: `泡泡玛特分地区零售门店数量占比(${financialViewLabel(view)})`,
subtext: '归一化堆叠:每根柱为当期末全球零售门店数的 100%',
},
legend: standardLegend(REGIONS.map((item) => item.name), { itemGap: 20 }),
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' },
textStyle: { fontFamily: FONT },
formatter: (params) => {
const row = rows[periods.indexOf(params[0].axisValue)];
return `<b>${params[0].axisValue}</b> 全球合计 ${row.global} 家<br/>${params
.filter((item) => item.value !== null)
.slice()
.reverse()
.map((item) => `${item.marker}${item.seriesName}: ${item.value.toFixed(1)}%`)
.join('<br/>')}`;
},
},
grid: { left: 78, right: 42, top: LAYOUT.gridTop, bottom: 100 },
xAxis: { ...standalonePeriodAxis, data: periods },
yAxis: {
...axisCommon,
type: 'value',
min: 0,
max: 100,
interval: 20,
axisLabel: { ...axisCommon.axisLabel, formatter: '{value}%' },
},
series: REGIONS.map((item) => barSeries({
name: item.name,
data: rows.map((row) => sharePoint(row[item.key], item.labelColor)),
color: item.color,
barWidth,
stack: 'stores-retail-share',
label: {
show: true,
position: 'inside',
fontSize: 11,
fontFamily: FONT,
fontWeight: 500,
formatter: (point) => `${point.value.toFixed(1)}%`,
},
emphasis: { focus: 'series' },
})),
graphic: [
...watermarks(width, height),
footnote('数据来源:泡泡玛特历年公告;门店数为各报告期末时点数\n中国=中国内地+港澳台;其他=全球合计−中国−亚太−美洲−欧洲 | @思考的Joey'),
],
};
}
+144
View File
@@ -0,0 +1,144 @@
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'),
),
],
};
}
+42 -16
View File
@@ -1,26 +1,44 @@
import { col } from '../csv.js'; import { col } from '../csv.js';
import { import {
PALETTE, FONT, axisCommon, periodAxis, titleStyle, legendStyle, PALETTE, FINANCIAL_COLORS, FONT, axisCommon, periodAxis, titleStyle, legendStyle,
lineSeries, footnote, watermarks, LAYOUT, lineSeries, footnote, watermarks, LAYOUT,
} from '../theme.js'; } from '../theme.js';
/** 泡泡玛特营运资本效率:存货、应收、应付与现金转换周期。 */ /** 泡泡玛特营运资本效率:存货、应收、应付与现金转换周期。 */
export function workingCapitalDaysChart(rows, { width, height }) { const SERIES = [
const series = [ { key: 'inventory_days', name: '存货周转天数', color: FINANCIAL_COLORS.financingCashFlow, width: 2.4 },
{ key: 'inventory_days', name: '存货周转天数', color: PALETTE.orange, width: 2.4 }, { key: 'receivable_days', name: '应收周转天数', color: FINANCIAL_COLORS.investingCashFlow, width: 2.2 },
{ key: 'receivable_days', name: '应收周转天数', color: PALETTE.blue, width: 2.2 }, { key: 'payable_days', name: '应付周转天数', color: FINANCIAL_COLORS.cashConversion, width: 2.2 },
{ key: 'payable_days', name: '应付周转天数', color: PALETTE.purple, width: 2.2 }, { key: 'cash_conversion_cycle', name: '现金转换周期', color: FINANCIAL_COLORS.netCashIncrease, width: 3.2 },
{ key: 'cash_conversion_cycle', name: '现金转换周期', color: PALETTE.red, width: 3.2 }, ];
];
/**
* 同一期间的多条线将标签放到离最近相邻值更远的一侧,确保每个数值都可见。
*/
function labeledData(rows, key) {
return rows.map((row) => {
const value = row[key];
const ranked = SERIES.map((series) => row[series.key]).sort((a, b) => b - a);
const rank = ranked.indexOf(value);
const gapUp = rank > 0 ? ranked[rank - 1] - value : Infinity;
const gapDown = rank < ranked.length - 1 ? value - ranked[rank + 1] : Infinity;
return {
value,
label: { position: gapUp >= gapDown ? 'top' : 'bottom', distance: 9 },
};
});
}
export function workingCapitalDaysChart(rows, { width, height }) {
return { return {
backgroundColor: '#fff', backgroundColor: '#fff',
title: { title: {
...titleStyle, ...titleStyle,
text: '泡泡玛特营运资本效率', text: '泡泡玛特营运资本效率',
subtext: '现金转换周期 = 存货周转天数 + 应收周转天数 − 应付周转天数', subtext: '现金转换周期 = 存货周转天数 + 应收周转天数 − 应付周转天数(中报按181天、年报按365天)',
}, },
legend: { ...legendStyle, top: LAYOUT.legendTop, data: series.map((item) => item.name) }, legend: { ...legendStyle, top: LAYOUT.legendTop, data: SERIES.map((item) => item.name) },
grid: { left: 78, right: 42, top: LAYOUT.gridTop, bottom: 90 }, grid: { left: 78, right: 42, top: LAYOUT.gridTop, bottom: 90 },
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',
@@ -37,22 +55,30 @@ export function workingCapitalDaysChart(rows, { width, height }) {
nameTextStyle: { color: PALETTE.grey, fontSize: 13, fontFamily: FONT }, nameTextStyle: { color: PALETTE.grey, fontSize: 13, fontFamily: FONT },
min: 0, min: 0,
}, },
series: series.map((item) => ({ series: SERIES.map((item) => ({
...lineSeries({ ...lineSeries({
name: item.name, name: item.name,
data: col(rows, item.key), data: labeledData(rows, item.key),
color: item.color, color: item.color,
formatter: (p) => p.value.toFixed(0), formatter: (p) => p.value.toFixed(0),
width: item.width, width: item.width,
}), }),
label: item.key === 'cash_conversion_cycle' label: {
? { show: true, color: item.color, fontFamily: FONT, fontSize: 11, formatter: (p) => `${p.value.toFixed(0)}天` } show: true,
: { show: false }, color: item.color,
fontFamily: FONT,
fontSize: 11,
fontWeight: 500,
formatter: (p) => `${p.value.toFixed(0)}天`,
textBorderColor: '#fff',
textBorderWidth: 3,
},
labelLayout: { hideOverlap: true },
z: item.key === 'cash_conversion_cycle' ? 10 : 1, z: item.key === 'cash_conversion_cycle' ? 10 : 1,
})), })),
graphic: [ graphic: [
...watermarks(width, height), ...watermarks(width, height),
footnote('数据来源:泡泡玛特财务指标(合并报表)| @思考的Joey'), footnote('数据来源:泡泡玛特财务指标(合并报表);原始180/360天口径已换算为181/365天 | @思考的Joey'),
], ],
}; };
} }
+35 -5
View File
@@ -11,6 +11,7 @@ import {
const instances = new Map(); const instances = new Map();
const activeViews = new Map(); const activeViews = new Map();
const chartRenderVersions = new Map();
let manifest; let manifest;
let shell; let shell;
let state; let state;
@@ -106,25 +107,47 @@ function chartSource(chart) {
return chart.views[view]; return chart.views[view];
} }
async function renderChart(chart, version) { async function renderChart(chart, version, chartVersion) {
const el = document.getElementById(`chart-${chart.instanceId}`); const el = document.getElementById(`chart-${chart.instanceId}`);
if (!el) return; if (!el) return;
const source = chartSource(chart); const source = chartSource(chart);
const view = chart.views
? activeViews.get(chart.viewKey) || chart.defaultView
: undefined;
try { try {
const rows = await loadCSV(`${source}?t=${cacheToken}`); const rows = await loadCSV(`${source}?t=${cacheToken}`);
if (version !== renderVersion || !document.body.contains(el)) return; if (version !== renderVersion || chartVersion !== chartRenderVersions.get(chart.instanceId) || !document.body.contains(el)) return;
const instance = echarts.init(el, null, { renderer: 'canvas' }); const instance = echarts.init(el, null, { renderer: 'canvas' });
instances.set(chart.instanceId, instance); instances.set(chart.instanceId, instance);
const { width, height } = el.getBoundingClientRect(); const { width, height } = el.getBoundingClientRect();
instance.setOption(getChartBuilder(chart.key)(rows, { width, height }), true); instance.setOption(getChartBuilder(chart.key)(rows, { width, height, view }), true);
} catch (error) { } catch (error) {
if (version === renderVersion) showChartError(chart.instanceId, error.message); if (version === renderVersion) showChartError(chart.instanceId, error.message);
console.error(`渲染图表失败:${chart.id}`, error); console.error(`渲染图表失败:${chart.id}`, error);
} }
} }
function renderSingleChart(chart) {
const previous = instances.get(chart.instanceId);
previous?.dispose();
instances.delete(chart.instanceId);
const chartVersion = (chartRenderVersions.get(chart.instanceId) || 0) + 1;
chartRenderVersions.set(chart.instanceId, chartVersion);
renderChart(chart, renderVersion, chartVersion);
}
function updateViewControls(chart, view) {
const card = document.getElementById(`card-${chart.instanceId}`);
if (!card) return;
card.querySelectorAll('.view-button').forEach((button) => {
button.dataset.active = String(button.dataset.view === view);
});
const source = card.querySelector('.src');
if (source) source.textContent = chartSource(chart);
}
function exportPNG(chartId, filename) { function exportPNG(chartId, filename) {
const instance = instances.get(chartId); const instance = instances.get(chartId);
if (!instance) return; if (!instance) return;
@@ -167,12 +190,19 @@ function renderCurrent() {
renderCards(shell.cards, charts, activeViews, { renderCards(shell.cards, charts, activeViews, {
onExport: exportPNG, onExport: exportPNG,
onSelectView: (chart, view) => { onSelectView: (chart, view) => {
if (activeViews.get(chart.viewKey) === view) return;
activeViews.set(chart.viewKey, view); activeViews.set(chart.viewKey, view);
updateHash({ companyId: company.id, tabId: tab.id, chartId: chart.id, view }); state = normalizeState({ companyId: company.id, tabId: tab.id, chartId: chart.id, view });
history.pushState(null, '', serializeHash(state));
updateViewControls(chart, view);
renderSingleChart(chart);
}, },
}); });
renderAnchors(shell.chartNav, charts, selectAnchor); renderAnchors(shell.chartNav, charts, selectAnchor);
Promise.all(charts.map((chart) => renderChart(chart, version))); charts.forEach((chart) => {
chartRenderVersions.set(chart.instanceId, (chartRenderVersions.get(chart.instanceId) || 0) + 1);
});
Promise.all(charts.map((chart) => renderChart(chart, version, chartRenderVersions.get(chart.instanceId))));
} }
async function start() { async function start() {
+60 -17
View File
@@ -28,24 +28,34 @@ export const PALETTE = {
}; };
/** /**
* 试行语义色:柱线组合图中,柱采用低饱和辅助色,折线保留单一强调色。 * 正式色板:轻快消费。
* 待页面视觉确认后再固化为新增图表的正式规范。 * 柱线组合中,柱用不同色相承载分类,折线保留高辨识度的单一强调色。
*/ */
export const FINANCIAL_COLORS = { export const FINANCIAL_COLORS = {
operatingCashFlow: '#6E9A83', operatingCashFlow: '#36A28F',
investingCashFlow: '#7897B2', investingCashFlow: '#4D83D1',
financingCashFlow: '#B18A66', financingCashFlow: '#EEA64F',
netCashIncrease: '#A8465C', netCashIncrease: '#D95567',
netProfit: '#C9885C', netProfit: '#F08B5A',
cashConversion: '#6B4A6C', cashConversion: '#8256B2',
};
/**
* 瀑布图语义色:基准值、正向贡献、负向贡献。
* 复用轻快消费财务色板,避免利润率拆解图另起一套颜色。
*/
export const BRIDGE_COLORS = {
total: FINANCIAL_COLORS.investingCashFlow,
pos: FINANCIAL_COLORS.operatingCashFlow,
neg: FINANCIAL_COLORS.netCashIncrease,
}; };
export const CHANNEL_COLORS = { export const CHANNEL_COLORS = {
retail: '#597A9B', retail: '#3F7FBE',
online: '#C87368', online: '#E36D63',
robotStore: '#98A77A', robotStore: '#71AD81',
wholesale: '#BB9A5C', wholesale: '#E4A349',
revenueGrowth: '#775D8B', revenueGrowth: '#8256B2',
}; };
/** 坐标轴通用样式:只留浅色横向网格,去掉轴线的存在感 */ /** 坐标轴通用样式:只留浅色横向网格,去掉轴线的存在感 */
@@ -98,6 +108,32 @@ export const periodAxis = {
}, },
}; };
/**
* 独立期间轴:FY 为全年,H1/H2 为单独半年。
* 用于已拆分累计值的多时间维度图,避免把 H2 错写为全年。
*/
export function shortStandalonePeriod(label) {
return String(label)
.replace(/^20(\d{2})FY$/, '$1全年')
.replace(/^20(\d{2})/, '$1');
}
export const standalonePeriodAxis = {
...categoryAxis,
axisLabel: {
...categoryAxis.axisLabel,
formatter: shortStandalonePeriod,
},
};
export function financialViewLabel(view) {
return ({ all: '全部期间', annual: '年度', halfyear: '半年', quarter: '季度' })[view] || '全部期间';
}
export function financialViewDisclosure(view) {
return '';
}
export const titleStyle = { export const titleStyle = {
left: 'center', left: 'center',
top: 18, top: 18,
@@ -140,7 +176,7 @@ export const legendStyle = {
}, },
}; };
/** 折线标注:带白底描边,避免压在网格线上看不清 */ /** 折线标注:白底描边,避免标签被折线自身或柱体穿过时看不清 */
export function labelStyle(color, position = 'top') { export function labelStyle(color, position = 'top') {
return { return {
show: true, show: true,
@@ -150,6 +186,8 @@ export function labelStyle(color, position = 'top') {
fontFamily: FONT, fontFamily: FONT,
fontWeight: 500, fontWeight: 500,
distance: 8, distance: 8,
textBorderColor: '#fff',
textBorderWidth: 3,
}; };
} }
@@ -201,9 +239,12 @@ export function lineSeries({ name, data, color, labelPosition = 'top', formatter
/** /**
* 柱线组合图的折线标签:正值放在点上方、负值放在点下方; * 柱线组合图的折线标签:正值放在点上方、负值放在点下方;
* 若与同期间柱值接近,则加大距离,避免文字压在柱体端点附近。 * 若与同期间柱值接近,则加大距离;也可指定将标签放到柱体另一侧,
* 避免文字压在柱体端点附近。
*/ */
export function lineDataWithSafeLabels(rows, { lineKey, barKeys, distance = 8, closeDistance = 14 }) { export function lineDataWithSafeLabels(rows, {
lineKey, barKeys, distance = 8, closeDistance = 14, closePosition,
}) {
const values = rows.map((row) => row[lineKey]).filter(Number.isFinite); const values = rows.map((row) => row[lineKey]).filter(Number.isFinite);
const range = Math.max(...values) - Math.min(...values) || 1; const range = Math.max(...values) - Math.min(...values) || 1;
const threshold = range * 0.06; const threshold = range * 0.06;
@@ -217,7 +258,9 @@ export function lineDataWithSafeLabels(rows, { lineKey, barKeys, distance = 8, c
return { return {
value, value,
label: { label: {
position: value < 0 ? 'bottom' : 'top', position: nearestBarGap <= threshold && closePosition
? closePosition
: (value < 0 ? 'bottom' : 'top'),
distance: nearestBarGap <= threshold ? closeDistance : distance, distance: nearestBarGap <= threshold ? closeDistance : distance,
}, },
}; };
+5 -3
View File
@@ -79,13 +79,15 @@ export function renderCards(container, charts, activeViews, { onExport, onSelect
if (viewNames.length > 1) { if (viewNames.length > 1) {
const switcher = element('div', 'view-switcher'); const switcher = element('div', 'view-switcher');
const labels = { annual: '年度', halfyear: '半年', quarter: '季度' }; const labels = { all: '全部', annual: '年度', halfyear: '半年', quarter: '季度' };
viewNames.forEach((view) => { viewNames.forEach((view) => {
switcher.append(button(labels[view] || view, { const viewButton = button(labels[view] || view, {
active: view === activeView, active: view === activeView,
onClick: () => onSelectView(chart, view), onClick: () => onSelectView(chart, view),
className: 'view-button', className: 'view-button',
})); });
viewButton.dataset.view = view;
switcher.append(viewButton);
}); });
bar.append(switcher); bar.append(switcher);
} }
+25 -17
View File
@@ -4,36 +4,44 @@
"id": "ppmt", "id": "ppmt",
"name": "泡泡玛特", "name": "泡泡玛特",
"chartCatalog": { "chartCatalog": {
"ppmt-profit": { "key": "ppmt-profit", "title": "盈利能力", "csv": "data/ppmt/ppmt_profit.csv", "height": 680, "filename": "泡泡玛特盈利能力" }, "ppmt-profit": { "key": "ppmt-profit", "title": "盈利能力", "defaultView": "all", "views": { "all": "data/ppmt/ppmt_profit.csv", "annual": "data/ppmt/ppmt_profit_annual.csv", "halfyear": "data/ppmt/ppmt_profit_halfyear.csv" }, "height": 680, "filename": "泡泡玛特盈利能力" },
"ppmt-sankey": { "key": "ppmt-sankey", "title": "利润流向", "csv": "data/ppmt/ppmt_sankey_2026h1.csv", "height": 800, "filename": "泡泡玛特利润流向2026H1" }, "ppmt-sankey": { "key": "ppmt-sankey", "title": "利润流向", "csv": "data/ppmt/ppmt_sankey_2026h1.csv", "height": 800, "filename": "泡泡玛特利润流向2026H1" },
"ppmt-growth": { "key": "ppmt-growth", "title": "营收到利润的增速对比", "csv": "data/ppmt/ppmt_growth.csv", "height": 700, "filename": "泡泡玛特营收到利润增速对比" }, "ppmt-growth": { "key": "ppmt-growth", "title": "营收到利润的增速对比", "defaultView": "all", "views": { "all": "data/ppmt/ppmt_growth.csv", "annual": "data/ppmt/ppmt_growth_annual.csv", "halfyear": "data/ppmt/ppmt_growth_halfyear.csv" }, "height": 700, "filename": "泡泡玛特营收到利润增速对比" },
"ppmt-core-profit": { "key": "ppmt-core-profit", "title": "核心利润及同比增速", "csv": "data/ppmt/ppmt_core_profit.csv", "height": 720, "filename": "泡泡玛特核心利润及同比增速" }, "ppmt-core-profit": { "key": "ppmt-core-profit", "title": "核心利润及同比增速", "defaultView": "all", "views": { "all": "data/ppmt/ppmt_core_profit.csv", "annual": "data/ppmt/ppmt_core_profit_annual.csv", "halfyear": "data/ppmt/ppmt_core_profit_halfyear.csv" }, "height": 720, "filename": "泡泡玛特核心利润及同比增速" },
"ppmt-core-margin": { "key": "ppmt-core-margin", "title": "核心利润率", "csv": "data/ppmt/ppmt_core_margin.csv", "height": 680, "filename": "泡泡玛特核心利润率" }, "ppmt-core-margin": { "key": "ppmt-core-margin", "title": "核心利润率", "defaultView": "all", "views": { "all": "data/ppmt/ppmt_core_margin.csv", "annual": "data/ppmt/ppmt_core_margin_annual.csv", "halfyear": "data/ppmt/ppmt_core_margin_halfyear.csv" }, "height": 680, "filename": "泡泡玛特核心利润率" },
"ppmt-bridge": { "key": "ppmt-bridge", "title": "净利率同比拆解", "csv": "data/ppmt/ppmt_margin_bridge.csv", "height": 700, "filename": "泡泡玛特净利率同比拆解" }, "ppmt-bridge": { "key": "ppmt-bridge", "title": "净利率同比拆解", "csv": "data/ppmt/ppmt_margin_bridge.csv", "height": 700, "filename": "泡泡玛特净利率同比拆解" },
"ppmt-margins": { "key": "ppmt-margins", "title": "利润率与费用率拆解", "csv": "data/ppmt/ppmt_margins.csv", "height": 680, "filename": "泡泡玛特利润率与费用率拆解" }, "ppmt-margins": { "key": "ppmt-margins", "title": "利润率与费用率拆解", "defaultView": "all", "views": { "all": "data/ppmt/ppmt_margins.csv", "annual": "data/ppmt/ppmt_margins_annual.csv", "halfyear": "data/ppmt/ppmt_margins_halfyear.csv" }, "height": 680, "filename": "泡泡玛特利润率与费用率拆解" },
"ppmt-assets": { "key": "ppmt-assets", "title": "资产结构演变", "csv": "data/ppmt/ppmt_assets.csv", "height": 680, "filename": "泡泡玛特资产结构" }, "ppmt-assets": { "key": "ppmt-assets", "title": "资产结构演变", "defaultView": "all", "views": { "all": "data/ppmt/ppmt_assets.csv", "annual": "data/ppmt/ppmt_assets_annual.csv", "halfyear": "data/ppmt/ppmt_assets_halfyear.csv" }, "height": 680, "filename": "泡泡玛特资产结构" },
"ppmt-ip": { "key": "ppmt-ip", "title": "主要 IP 收入趋势", "csv": "data/ppmt/ppmt_ip.csv", "height": 720, "filename": "泡泡玛特IP收入趋势" }, "ppmt-inventory": { "key": "ppmt-inventory", "title": "存货余额及同比", "defaultView": "all", "views": { "all": "data/ppmt/ppmt_inventory.csv", "annual": "data/ppmt/ppmt_inventory_annual.csv", "halfyear": "data/ppmt/ppmt_inventory_halfyear.csv" }, "height": 680, "filename": "泡泡玛特存货余额及同比" },
"ppmt-ip": { "key": "ppmt-ip", "title": "主要 IP 收入趋势", "defaultView": "all", "views": { "all": "data/ppmt/ppmt_ip.csv", "annual": "data/ppmt/ppmt_ip_annual.csv", "halfyear": "data/ppmt/ppmt_ip_halfyear.csv" }, "height": 720, "filename": "泡泡玛特IP收入趋势" },
"ppmt-ip-share": { "key": "ppmt-ip-share", "title": "主要 IP 收入占比", "csv": "data/ppmt/ppmt_ip_share_2026h1.csv", "height": 720, "filename": "泡泡玛特IP收入占比2026H1" }, "ppmt-ip-share": { "key": "ppmt-ip-share", "title": "主要 IP 收入占比", "csv": "data/ppmt/ppmt_ip_share_2026h1.csv", "height": 720, "filename": "泡泡玛特IP收入占比2026H1" },
"ppmt-ip-share-history": { "key": "ppmt-ip-share-history", "title": "各 IP 半年收入占比", "csv": "data/ppmt/ppmt_ip_share_history.csv", "height": 700, "filename": "泡泡玛特各IP半年收入占比" }, "ppmt-ip-share-history": { "key": "ppmt-ip-share-history", "title": "各 IP 半年收入占比", "defaultView": "all", "views": { "all": "data/ppmt/ppmt_ip_share_history.csv", "annual": "data/ppmt/ppmt_ip_share_history_annual.csv", "halfyear": "data/ppmt/ppmt_ip_share_history_halfyear.csv" }, "height": 700, "filename": "泡泡玛特各IP半年收入占比" },
"ppmt-ip-ex-monsters": { "key": "ppmt-ip-ex-monsters", "title": "除 THE MONSTERS 外的 IP 收入及同比", "csv": "data/ppmt/ppmt_ip_ex_monsters.csv", "height": 700, "filename": "泡泡玛特除MONSTERS外IP收入及同比" }, "ppmt-ip-ex-monsters": { "key": "ppmt-ip-ex-monsters", "title": "除 THE MONSTERS 外的 IP 收入及同比", "defaultView": "all", "views": { "all": "data/ppmt/ppmt_ip_ex_monsters.csv", "annual": "data/ppmt/ppmt_ip_ex_monsters_annual.csv", "halfyear": "data/ppmt/ppmt_ip_ex_monsters_halfyear.csv" }, "height": 700, "filename": "泡泡玛特除MONSTERS外IP收入及同比" },
"ppmt-category": { "key": "ppmt-category", "title": "品类收入结构", "csv": "data/ppmt/ppmt_category.csv", "height": 680, "filename": "泡泡玛特品类结构" }, "ppmt-category": { "key": "ppmt-category", "title": "品类收入结构", "defaultView": "all", "views": { "all": "data/ppmt/ppmt_category.csv", "annual": "data/ppmt/ppmt_category_annual.csv", "halfyear": "data/ppmt/ppmt_category_halfyear.csv" }, "height": 680, "filename": "泡泡玛特品类结构" },
"ppmt-region-channel": { "key": "ppmt-region-channel", "title": "区域渠道分布", "csv": "data/ppmt/ppmt_region_channel_2026h1.csv", "height": 680, "filename": "泡泡玛特区域渠道分布2026H1" }, "ppmt-region-channel": { "key": "ppmt-region-channel", "title": "区域渠道分布", "csv": "data/ppmt/ppmt_region_channel_2026h1.csv", "height": 680, "filename": "泡泡玛特区域渠道分布2026H1" },
"ppmt-region-yoy": { "key": "ppmt-region-yoy", "title": "区域渠道同比增速", "csv": "data/ppmt/ppmt_region_channel_yoy.csv", "height": 620, "filename": "泡泡玛特区域渠道同比增速" }, "ppmt-region-yoy": { "key": "ppmt-region-yoy", "title": "区域渠道同比增速", "csv": "data/ppmt/ppmt_region_channel_yoy.csv", "height": 620, "filename": "泡泡玛特区域渠道同比增速" },
"ppmt-region-yoy-timeseries": { "key": "ppmt-region-yoy-timeseries", "title": "区域渠道同比增速趋势", "csv": "data/ppmt/ppmt_region_channel_yoy_timeseries.csv", "height": 760, "filename": "泡泡玛特区域渠道同比增速趋势" }, "ppmt-region-yoy-timeseries": { "key": "ppmt-region-yoy-timeseries", "title": "区域渠道同比增速趋势", "csv": "data/ppmt/ppmt_region_channel_yoy_timeseries.csv", "height": 760, "filename": "泡泡玛特区域渠道同比增速趋势" },
"ppmt-cash-conversion": { "key": "ppmt-cash-conversion", "title": "经营现金流与净利润(年度)", "csv": "data/ppmt/ppmt_cash_conversion.csv", "height": 700, "filename": "泡泡玛特经营现金流与净利润" }, "ppmt-cash-conversion": { "key": "ppmt-cash-conversion", "title": "经营现金流与净利润", "defaultView": "all", "views": { "all": "data/ppmt/ppmt_cash_conversion.csv", "annual": "data/ppmt/ppmt_cash_conversion_annual.csv", "halfyear": "data/ppmt/ppmt_cash_conversion_halfyear.csv" }, "height": 700, "filename": "泡泡玛特经营现金流与净利润" },
"ppmt-liquidity-working-capital": { "key": "ppmt-liquidity-working-capital", "title": "现金、负债与净营运资本", "csv": "data/ppmt/ppmt_liquidity_working_capital.csv", "height": 720, "filename": "泡泡玛特现金负债与净营运资本" }, "ppmt-liquidity-working-capital": { "key": "ppmt-liquidity-working-capital", "title": "现金、负债与净营运资本", "defaultView": "all", "views": { "all": "data/ppmt/ppmt_liquidity_working_capital.csv", "annual": "data/ppmt/ppmt_liquidity_working_capital_annual.csv", "halfyear": "data/ppmt/ppmt_liquidity_working_capital_halfyear.csv" }, "height": 720, "filename": "泡泡玛特现金负债与净营运资本" },
"ppmt-cashflow-composition": { "key": "ppmt-cashflow-composition", "title": "三大现金流与现金净增加(年度)", "csv": "data/ppmt/ppmt_cashflow_composition.csv", "height": 700, "filename": "泡泡玛特三大现金流与现金净增加" }, "ppmt-cashflow-composition": { "key": "ppmt-cashflow-composition", "title": "三大现金流与现金净增加", "defaultView": "all", "views": { "all": "data/ppmt/ppmt_cashflow_composition.csv", "annual": "data/ppmt/ppmt_cashflow_composition_annual.csv", "halfyear": "data/ppmt/ppmt_cashflow_composition_halfyear.csv" }, "height": 700, "filename": "泡泡玛特三大现金流与现金净增加" },
"ppmt-working-capital-days": { "key": "ppmt-working-capital-days", "title": "营运资本效率", "csv": "data/ppmt/ppmt_working_capital_days.csv", "height": 700, "filename": "泡泡玛特营运资本效率" }, "ppmt-working-capital-days": { "key": "ppmt-working-capital-days", "title": "营运资本效率", "csv": "data/ppmt/ppmt_working_capital_days.csv", "height": 700, "filename": "泡泡玛特营运资本效率" },
"ppmt-expense-structure": { "key": "ppmt-expense-structure", "title": "费用结构与费用率(年度)", "csv": "data/ppmt/ppmt_expense_structure.csv", "height": 700, "filename": "泡泡玛特费用结构与费用率" }, "ppmt-expense-structure": { "key": "ppmt-expense-structure", "title": "费用结构与费用率", "defaultView": "all", "views": { "all": "data/ppmt/ppmt_expense_structure.csv", "annual": "data/ppmt/ppmt_expense_structure_annual.csv", "halfyear": "data/ppmt/ppmt_expense_structure_halfyear.csv" }, "height": 700, "filename": "泡泡玛特费用结构与费用率" },
"ppmt-channel-revenue": { "key": "ppmt-channel-revenue", "title": "渠道收入结构与增速", "csv": "data/ppmt/ppmt_channel_revenue.csv", "height": 720, "filename": "泡泡玛特渠道收入结构与增速" } "ppmt-channel-revenue": { "key": "ppmt-channel-revenue", "title": "渠道收入结构与增速", "defaultView": "all", "views": { "all": "data/ppmt/ppmt_channel_revenue.csv", "annual": "data/ppmt/ppmt_channel_revenue_annual.csv", "halfyear": "data/ppmt/ppmt_channel_revenue_halfyear.csv" }, "height": 720, "filename": "泡泡玛特渠道收入结构与增速" },
"ppmt-stores-retail": { "key": "ppmt-stores-retail", "title": "分地区零售门店数量与同比增速", "defaultView": "all", "views": { "all": "data/ppmt/ppmt_stores_retail.csv", "annual": "data/ppmt/ppmt_stores_retail_annual.csv", "halfyear": "data/ppmt/ppmt_stores_retail_halfyear.csv" }, "height": 680, "filename": "泡泡玛特分地区零售门店数量与同比增速" },
"ppmt-stores-retail-share": { "key": "ppmt-stores-retail-share", "title": "分地区零售门店数量占比", "defaultView": "all", "views": { "all": "data/ppmt/ppmt_stores_retail_share.csv", "annual": "data/ppmt/ppmt_stores_retail_share_annual.csv", "halfyear": "data/ppmt/ppmt_stores_retail_share_halfyear.csv" }, "height": 680, "filename": "泡泡玛特分地区零售门店数量占比" },
"ppmt-retail-store-revenue": { "key": "ppmt-retail-store-revenue", "title": "各地区零售店单店收入", "defaultView": "all", "views": { "all": "data/ppmt/ppmt_retail_store_revenue.csv", "annual": "data/ppmt/ppmt_retail_store_revenue_annual.csv", "halfyear": "data/ppmt/ppmt_retail_store_revenue_halfyear.csv" }, "height": 680, "filename": "泡泡玛特各地区零售店单店收入" },
"ppmt-stores-robot": { "key": "ppmt-stores-robot", "title": "分地区机器人商店数量与同比增速", "defaultView": "all", "views": { "all": "data/ppmt/ppmt_stores_robot.csv", "annual": "data/ppmt/ppmt_stores_robot_annual.csv", "halfyear": "data/ppmt/ppmt_stores_robot_halfyear.csv" }, "height": 680, "filename": "泡泡玛特分地区机器人商店数量与同比增速" },
"ppmt-robot-store-revenue": { "key": "ppmt-robot-store-revenue", "title": "机器人商店单店收入", "defaultView": "all", "views": { "all": "data/ppmt/ppmt_robot_store_revenue.csv", "annual": "data/ppmt/ppmt_robot_store_revenue_annual.csv", "halfyear": "data/ppmt/ppmt_robot_store_revenue_halfyear.csv" }, "height": 680, "filename": "泡泡玛特机器人商店单店收入" },
"ppmt-region-share": { "key": "ppmt-region-share", "title": "分地区营收占比", "defaultView": "all", "views": { "all": "data/ppmt/ppmt_region_share.csv", "annual": "data/ppmt/ppmt_region_share_annual.csv", "halfyear": "data/ppmt/ppmt_region_share_halfyear.csv" }, "height": 680, "filename": "泡泡玛特分地区营收占比" }
}, },
"tabs": [ "tabs": [
{ "id": "overview", "name": "概览", "chartRefs": ["ppmt-growth", "ppmt-profit", "ppmt-ip-share", "ppmt-region-channel"] }, { "id": "overview", "name": "概览", "chartRefs": ["ppmt-growth", "ppmt-profit", "ppmt-ip-share", "ppmt-region-channel"] },
{ "id": "profitability", "name": "盈利能力", "chartRefs": ["ppmt-growth", "ppmt-core-profit", "ppmt-core-margin", "ppmt-bridge", "ppmt-sankey", "ppmt-cash-conversion", "ppmt-cashflow-composition"] }, { "id": "profitability", "name": "盈利能力", "chartRefs": ["ppmt-growth", "ppmt-core-profit", "ppmt-core-margin", "ppmt-bridge", "ppmt-sankey", "ppmt-cash-conversion", "ppmt-cashflow-composition"] },
{ "id": "expense", "name": "费用率", "chartRefs": ["ppmt-margins", "ppmt-expense-structure"] }, { "id": "expense", "name": "费用率", "chartRefs": ["ppmt-margins", "ppmt-expense-structure"] },
{ "id": "balance-sheet", "name": "资产负债", "chartRefs": ["ppmt-assets", "ppmt-liquidity-working-capital", "ppmt-working-capital-days"] }, { "id": "balance-sheet", "name": "资产负债", "chartRefs": ["ppmt-assets", "ppmt-inventory", "ppmt-liquidity-working-capital", "ppmt-working-capital-days"] },
{ "id": "ip-category", "name": "IP 与品类", "chartRefs": ["ppmt-ip", "ppmt-ip-share", "ppmt-ip-share-history", "ppmt-ip-ex-monsters", "ppmt-category"] }, { "id": "ip-category", "name": "IP 与品类", "chartRefs": ["ppmt-ip", "ppmt-ip-share", "ppmt-ip-share-history", "ppmt-ip-ex-monsters", "ppmt-category"] },
{ "id": "region-channel", "name": "区域渠道", "chartRefs": ["ppmt-region-channel", "ppmt-region-yoy", "ppmt-region-yoy-timeseries", "ppmt-channel-revenue"] } { "id": "region-channel", "name": "区域渠道", "chartRefs": ["ppmt-region-channel", "ppmt-region-yoy", "ppmt-region-yoy-timeseries", "ppmt-channel-revenue", "ppmt-stores-retail", "ppmt-stores-retail-share", "ppmt-stores-robot", "ppmt-region-share"] },
{ "id": "store-revenue", "name": "单店收入", "chartRefs": ["ppmt-retail-store-revenue", "ppmt-robot-store-revenue"] }
] ]
}, },
{ {

Some files were not shown because too many files have changed in this diff Show More