3 Commits

Author SHA1 Message Date
R524809 44c6437b64 feat: 新增各渠道收入占比 2026-09-07 17:42:45 +08:00
R524809 9605228c3f Merge branch 'main' into feat-1.1.0 2026-09-07 14:54:38 +08:00
joey5628 b3c7c30a1f feat: 更新泡泡玛特图表,加入单店收入 2026-09-06 22:31:55 +08:00
28 changed files with 799 additions and 32 deletions
+8
View File
@@ -0,0 +1,8 @@
period,retail,online,robot_store,wholesale,total_revenue
2024H1,57.4,25.9,8.1,8.6,45.59
2024H2,49.0,35.2,5.4,10.4,84.78
2024FY,51.9,31.9,6.4,9.8,130.37
2025H1,49.8,39.6,5.9,4.7,138.75
2025H2,44.5,47.0,3.7,4.7,232.45
2025FY,46.5,44.3,4.6,4.7,371.20
2026H1,53.1,36.2,5.5,5.2,171.74
1 period retail online robot_store wholesale total_revenue
2 2024H1 57.4 25.9 8.1 8.6 45.59
3 2024H2 49.0 35.2 5.4 10.4 84.78
4 2024FY 51.9 31.9 6.4 9.8 130.37
5 2025H1 49.8 39.6 5.9 4.7 138.75
6 2025H2 44.5 47.0 3.7 4.7 232.45
7 2025FY 46.5 44.3 4.6 4.7 371.20
8 2026H1 53.1 36.2 5.5 5.2 171.74
+3
View File
@@ -0,0 +1,3 @@
period,retail,online,robot_store,wholesale,total_revenue
2024FY,51.9,31.9,6.4,9.8,130.37
2025FY,46.5,44.3,4.6,4.7,371.20
1 period retail online robot_store wholesale total_revenue
2 2024FY 51.9 31.9 6.4 9.8 130.37
3 2025FY 46.5 44.3 4.6 4.7 371.20
@@ -0,0 +1,6 @@
period,retail,online,robot_store,wholesale,total_revenue
2024H1,57.4,25.9,8.1,8.6,45.59
2024H2,49.0,35.2,5.4,10.4,84.78
2025H1,49.8,39.6,5.9,4.7,138.75
2025H2,44.5,47.0,3.7,4.7,232.45
2026H1,53.1,36.2,5.5,5.2,171.74
1 period retail online robot_store wholesale total_revenue
2 2024H1 57.4 25.9 8.1 8.6 45.59
3 2024H2 49.0 35.2 5.4 10.4 84.78
4 2025H1 49.8 39.6 5.9 4.7 138.75
5 2025H2 44.5 47.0 3.7 4.7 232.45
6 2026H1 53.1 36.2 5.5 5.2 171.74
+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
+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
+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 -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
+81
View File
@@ -0,0 +1,81 @@
region,channel,period,revenue
中国,零售店,2024H1,20.04
中国,零售店,2024FY,46.01
中国,零售店,2025H1,44.06
中国,零售店,2025FY,100.75
中国,零售店,2026H1,60.90
中国,机器人商店,2024H1,3.38
中国,机器人商店,2024FY,7.51
中国,机器人商店,2025H1,6.78
中国,机器人商店,2025FY,13.46
中国,机器人商店,2026H1,7.79
中国,线上渠道,2024H1,9.41
中国,线上渠道,2024FY,27.72
中国,线上渠道,2025H1,29.37
中国,线上渠道,2025FY,85.22
中国,线上渠道,2026H1,47.79
中国,批发及其他,2024H1,2.39
中国,批发及其他,2024FY,7.63
中国,批发及其他,2025H1,2.61
中国,批发及其他,2025FY,9.08
中国,批发及其他,2026H1,5.53
亚太,零售店,2024H1,4.85
亚太,零售店,2024FY,16.68
亚太,零售店,2025H1,14.93
亚太,零售店,2025FY,43.19
亚太,零售店,2026H1,17.16
亚太,机器人商店,2024H1,0.17
亚太,机器人商店,2024FY,0.40
亚太,机器人商店,2025H1,0.33
亚太,机器人商店,2025FY,0.95
亚太,机器人商店,2026H1,0.57
亚太,线上渠道,2024H1,1.65
亚太,线上渠道,2024FY,9.72
亚太,线上渠道,2025H1,10.69
亚太,线上渠道,2025FY,30.53
亚太,线上渠道,2026H1,6.44
亚太,批发及其他,2024H1,1.29
亚太,批发及其他,2024FY,4.30
亚太,批发及其他,2025H1,2.56
亚太,批发及其他,2025FY,5.44
亚太,批发及其他,2026H1,1.59
美洲,零售店,2024H1,0.87
美洲,零售店,2024FY,3.41
美洲,零售店,2025H1,7.37
美洲,零售店,2025FY,20.04
美洲,零售店,2026H1,9.03
美洲,机器人商店,2024H1,0.13
美洲,机器人商店,2024FY,0.38
美洲,机器人商店,2025H1,1.05
美洲,机器人商店,2025FY,2.32
美洲,机器人商店,2026H1,1.04
美洲,线上渠道,2024H1,0.64
美洲,线上渠道,2024FY,3.64
美洲,线上渠道,2025H1,13.27
美洲,线上渠道,2025FY,43.54
美洲,线上渠道,2026H1,7.22
美洲,批发及其他,2024H1,0.19
美洲,批发及其他,2024FY,0.58
美洲,批发及其他,2025H1,0.96
美洲,批发及其他,2025FY,2.17
美洲,批发及其他,2026H1,1.62
欧洲及其他,零售店,2024H1,0.41
欧洲及其他,零售店,2024FY,1.57
欧洲及其他,零售店,2025H1,2.72
欧洲及其他,零售店,2025FY,8.56
欧洲及其他,零售店,2026H1,4.03
欧洲及其他,机器人商店,2024H1,0.01
欧洲及其他,机器人商店,2024FY,0.02
欧洲及其他,机器人商店,2025H1,0.04
欧洲及其他,机器人商店,2025FY,0.17
欧洲及其他,机器人商店,2026H1,0.11
欧洲及其他,线上渠道,2024H1,0.11
欧洲及其他,线上渠道,2024FY,0.55
欧洲及其他,线上渠道,2025H1,1.65
欧洲及其他,线上渠道,2025FY,5.05
欧洲及其他,线上渠道,2026H1,0.68
欧洲及其他,批发及其他,2024H1,0.05
欧洲及其他,批发及其他,2024FY,0.25
欧洲及其他,批发及其他,2025H1,0.36
欧洲及其他,批发及其他,2025FY,0.73
欧洲及其他,批发及其他,2026H1,0.24
1 region channel period revenue
2 中国 零售店 2024H1 20.04
3 中国 零售店 2024FY 46.01
4 中国 零售店 2025H1 44.06
5 中国 零售店 2025FY 100.75
6 中国 零售店 2026H1 60.90
7 中国 机器人商店 2024H1 3.38
8 中国 机器人商店 2024FY 7.51
9 中国 机器人商店 2025H1 6.78
10 中国 机器人商店 2025FY 13.46
11 中国 机器人商店 2026H1 7.79
12 中国 线上渠道 2024H1 9.41
13 中国 线上渠道 2024FY 27.72
14 中国 线上渠道 2025H1 29.37
15 中国 线上渠道 2025FY 85.22
16 中国 线上渠道 2026H1 47.79
17 中国 批发及其他 2024H1 2.39
18 中国 批发及其他 2024FY 7.63
19 中国 批发及其他 2025H1 2.61
20 中国 批发及其他 2025FY 9.08
21 中国 批发及其他 2026H1 5.53
22 亚太 零售店 2024H1 4.85
23 亚太 零售店 2024FY 16.68
24 亚太 零售店 2025H1 14.93
25 亚太 零售店 2025FY 43.19
26 亚太 零售店 2026H1 17.16
27 亚太 机器人商店 2024H1 0.17
28 亚太 机器人商店 2024FY 0.40
29 亚太 机器人商店 2025H1 0.33
30 亚太 机器人商店 2025FY 0.95
31 亚太 机器人商店 2026H1 0.57
32 亚太 线上渠道 2024H1 1.65
33 亚太 线上渠道 2024FY 9.72
34 亚太 线上渠道 2025H1 10.69
35 亚太 线上渠道 2025FY 30.53
36 亚太 线上渠道 2026H1 6.44
37 亚太 批发及其他 2024H1 1.29
38 亚太 批发及其他 2024FY 4.30
39 亚太 批发及其他 2025H1 2.56
40 亚太 批发及其他 2025FY 5.44
41 亚太 批发及其他 2026H1 1.59
42 美洲 零售店 2024H1 0.87
43 美洲 零售店 2024FY 3.41
44 美洲 零售店 2025H1 7.37
45 美洲 零售店 2025FY 20.04
46 美洲 零售店 2026H1 9.03
47 美洲 机器人商店 2024H1 0.13
48 美洲 机器人商店 2024FY 0.38
49 美洲 机器人商店 2025H1 1.05
50 美洲 机器人商店 2025FY 2.32
51 美洲 机器人商店 2026H1 1.04
52 美洲 线上渠道 2024H1 0.64
53 美洲 线上渠道 2024FY 3.64
54 美洲 线上渠道 2025H1 13.27
55 美洲 线上渠道 2025FY 43.54
56 美洲 线上渠道 2026H1 7.22
57 美洲 批发及其他 2024H1 0.19
58 美洲 批发及其他 2024FY 0.58
59 美洲 批发及其他 2025H1 0.96
60 美洲 批发及其他 2025FY 2.17
61 美洲 批发及其他 2026H1 1.62
62 欧洲及其他 零售店 2024H1 0.41
63 欧洲及其他 零售店 2024FY 1.57
64 欧洲及其他 零售店 2025H1 2.72
65 欧洲及其他 零售店 2025FY 8.56
66 欧洲及其他 零售店 2026H1 4.03
67 欧洲及其他 机器人商店 2024H1 0.01
68 欧洲及其他 机器人商店 2024FY 0.02
69 欧洲及其他 机器人商店 2025H1 0.04
70 欧洲及其他 机器人商店 2025FY 0.17
71 欧洲及其他 机器人商店 2026H1 0.11
72 欧洲及其他 线上渠道 2024H1 0.11
73 欧洲及其他 线上渠道 2024FY 0.55
74 欧洲及其他 线上渠道 2025H1 1.65
75 欧洲及其他 线上渠道 2025FY 5.05
76 欧洲及其他 线上渠道 2026H1 0.68
77 欧洲及其他 批发及其他 2024H1 0.05
78 欧洲及其他 批发及其他 2024FY 0.25
79 欧洲及其他 批发及其他 2025H1 0.36
80 欧洲及其他 批发及其他 2025FY 0.73
81 欧洲及其他 批发及其他 2026H1 0.24
Binary file not shown.
+10
View File
@@ -25,9 +25,14 @@ 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 { channelShareChart } from './ppmt_channel_share.js';
import { storesRetailChart } from './ppmt_stores_retail.js'; import { storesRetailChart } from './ppmt_stores_retail.js';
import { storesRobotChart } from './ppmt_stores_robot.js'; import { storesRobotChart } from './ppmt_stores_robot.js';
import { regionShareChart } from './ppmt_region_share.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 = {
@@ -58,9 +63,14 @@ 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-channel-share': channelShareChart,
'ppmt-stores-retail': storesRetailChart, 'ppmt-stores-retail': storesRetailChart,
'ppmt-stores-robot': storesRobotChart, 'ppmt-stores-robot': storesRobotChart,
'ppmt-region-share': regionShareChart, '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 -2
View File
@@ -31,6 +31,10 @@ export function channelRevenueChart(rows, { width, height, view = 'all' }) {
const totalMax = Math.max(...rows.map((row) => num(row.total_revenue) || 0)); const totalMax = Math.max(...rows.map((row) => num(row.total_revenue) || 0));
const axisMax = NICE_MAX.find((max) => max >= totalMax * 1.02) const axisMax = NICE_MAX.find((max) => max >= totalMax * 1.02)
?? Math.ceil((totalMax * 1.2) / 100) * 100; ?? 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 // 绘图区像素高:画布高 − grid top 145 − grid bottom 90
const pxPerUnit = (height - LAYOUT.gridTop - 90) / axisMax; const pxPerUnit = (height - LAYOUT.gridTop - 90) / axisMax;
@@ -111,8 +115,8 @@ export function channelRevenueChart(rows, { width, height, view = 'all' }) {
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 },
+83
View File
@@ -0,0 +1,83 @@
import { col } from '../csv.js';
import {
CHANNEL_COLORS, PALETTE, FONT, axisCommon, standalonePeriodAxis, titleStyle, standardLegend,
financialViewLabel, barSeries, footnote, watermarks, LAYOUT,
} from '../theme.js';
const CHANNELS = [
{ key: 'retail', name: '零售店', color: CHANNEL_COLORS.retail, labelColor: '#fff' },
{ key: 'online', name: '线上渠道', color: CHANNEL_COLORS.online, labelColor: '#fff' },
{ key: 'robot_store', name: '机器人商店', color: CHANNEL_COLORS.robotStore, labelColor: '#fff' },
{ key: 'wholesale', name: '批发及其他', color: CHANNEL_COLORS.wholesale, labelColor: PALETTE.ink },
];
function sharePoint(value, labelColor) {
if (!Number.isFinite(value) || value === 0) return null;
// 3.5% 以下的段放不下 11px 段内标签;当前数据最小为 3.7%(2025H2 机器人商店)
return {
value,
label: value >= 3.5
? { position: 'inside', color: labelColor }
: { show: false },
};
}
/** 泡泡玛特全渠道收入占比:全部地区合并,每根柱按当期总收入归一化为 100%。 */
export function channelShareChart(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(CHANNELS.map((item) => item.name)),
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.total_revenue.toFixed(2)} 亿元<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: CHANNELS.map((item) => barSeries({
name: item.name,
data: rows.map((row) => sharePoint(row[item.key], item.labelColor)),
color: item.color,
barWidth,
stack: 'channel-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('数据来源:泡泡玛特分地区渠道收入披露(中国含港澳台);H2为单半年(全年−H1)\n线上渠道含抽盒机、天猫、抖音、自研APP及官网等 | @思考的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'),
],
};
}
+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'),
],
};
}
+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'),
],
};
}
+2 -2
View File
@@ -36,7 +36,7 @@ export function workingCapitalDaysChart(rows, { width, height }) {
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 },
@@ -78,7 +78,7 @@ export function workingCapitalDaysChart(rows, { width, height }) {
})), })),
graphic: [ graphic: [
...watermarks(width, height), ...watermarks(width, height),
footnote('数据来源:泡泡玛特财务指标(合并报表)| @思考的Joey'), footnote('数据来源:泡泡玛特财务指标(合并报表);原始180/360天口径已换算为181/365天 | @思考的Joey'),
], ],
}; };
} }
+8 -2
View File
@@ -12,6 +12,7 @@
"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": "利润率与费用率拆解", "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-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": "资产结构演变", "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-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-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": { "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 半年收入占比", "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-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半年收入占比" },
@@ -26,17 +27,22 @@
"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": "费用结构与费用率", "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-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": "渠道收入结构与增速", "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-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-channel-share": { "key": "ppmt-channel-share", "title": "渠道收入占比", "defaultView": "all", "views": { "all": "data/ppmt/ppmt_channel_share.csv", "annual": "data/ppmt/ppmt_channel_share_annual.csv", "halfyear": "data/ppmt/ppmt_channel_share_halfyear.csv" }, "height": 680, "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": { "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-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": "泡泡玛特分地区营收占比" } "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", "ppmt-stores-retail", "ppmt-stores-robot", "ppmt-region-share"] } { "id": "region-channel", "name": "区域渠道", "chartRefs": ["ppmt-region-channel", "ppmt-region-yoy", "ppmt-region-yoy-timeseries", "ppmt-channel-revenue", "ppmt-channel-share", "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"] }
] ]
}, },
{ {
+114 -13
View File
@@ -4,6 +4,7 @@ import { fileURLToPath } from 'node:url';
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const RAW_DIR = path.join(ROOT, 'data/ppmt/raw'); const RAW_DIR = path.join(ROOT, 'data/ppmt/raw');
const RAWDATA_DIR = path.join(ROOT, 'data/ppmt/rawData');
const OUTPUT_DIR = path.join(ROOT, 'data/ppmt'); const OUTPUT_DIR = path.join(ROOT, 'data/ppmt');
const FILES = { const FILES = {
@@ -184,13 +185,21 @@ const workingCapitalDaysRows = indicators.dates.map((date, index) => ({
})).filter((item) => ( })).filter((item) => (
item.date >= '2020-01-01' item.date >= '2020-01-01'
&& item.inventory !== null && item.receivable !== null && item.payable !== null && item.inventory !== null && item.receivable !== null && item.payable !== null
)).map((item) => [ )).map((item) => {
periodFor(item.date), const period = periodFor(item.date);
item.inventory.toFixed(1), // 原始指标表按半年 180 天、全年 360 天计算;此处统一换算为自然日 181/365 天。
item.receivable.toFixed(1), const factor = period.endsWith('H1') ? 181 / 180 : 365 / 360;
item.payable.toFixed(1), const inventory = item.inventory * factor;
(item.inventory + item.receivable - item.payable).toFixed(1), const receivable = item.receivable * factor;
]).reverse(); const payable = item.payable * factor;
return [
period,
inventory.toFixed(1),
receivable.toFixed(1),
payable.toFixed(1),
(inventory + receivable - payable).toFixed(1),
];
}).reverse();
writeCSV('ppmt_working_capital_days.csv', [ writeCSV('ppmt_working_capital_days.csv', [
'period', 'inventory_days', 'receivable_days', 'payable_days', 'cash_conversion_cycle', 'period', 'inventory_days', 'receivable_days', 'payable_days', 'cash_conversion_cycle',
@@ -341,9 +350,9 @@ function yoyFor(rows, key, outputKey = key) {
}); });
} }
function toOutputRows(rows, headers, digits = {}) { function toOutputRows(rows, headers, digits = {}, startYear = OUTPUT_START_YEAR) {
return rows return rows
.filter((row) => Number(row.period.slice(0, 4)) >= OUTPUT_START_YEAR) .filter((row) => Number(row.period.slice(0, 4)) >= startYear)
.map((row) => headers.map((header) => { .map((row) => headers.map((header) => {
if (header === 'period') return row.period; if (header === 'period') return row.period;
const value = row[header]; const value = row[header];
@@ -351,14 +360,14 @@ function toOutputRows(rows, headers, digits = {}) {
})); }));
} }
function writeViewFiles(base, headers, views, digits) { function writeViewFiles(base, headers, views, digits, startYear = OUTPUT_START_YEAR) {
const filenames = { const filenames = {
all: `${base}.csv`, all: `${base}.csv`,
annual: `${base}_annual.csv`, annual: `${base}_annual.csv`,
halfyear: `${base}_halfyear.csv`, halfyear: `${base}_halfyear.csv`,
}; };
Object.entries(filenames).forEach(([view, filename]) => { Object.entries(filenames).forEach(([view, filename]) => {
writeCSV(filename, headers, toOutputRows(views[view], headers, digits)); writeCSV(filename, headers, toOutputRows(views[view], headers, digits, startYear));
}); });
} }
@@ -557,8 +566,8 @@ writeViewFiles('ppmt_expense_structure', ['period', 'sales_expense', 'admin_expe
sales_expense: 2, admin_expense: 2, financial_expense: 2, four_expense_rate: 1, sales_expense: 2, admin_expense: 2, financial_expense: 2, four_expense_rate: 1,
}); });
function readRuntimeRows(filename) { function readCsvRows(dir, filename) {
const [header, ...lines] = fs.readFileSync(path.join(OUTPUT_DIR, filename), 'utf8').trim().split(/\r?\n/); const [header, ...lines] = fs.readFileSync(path.join(dir, filename), 'utf8').trim().split(/\r?\n/);
const headers = header.split(','); const headers = header.split(',');
return lines.map((line) => Object.fromEntries(line.split(',').map((cell, index) => { return lines.map((line) => Object.fromEntries(line.split(',').map((cell, index) => {
const value = Number(cell); const value = Number(cell);
@@ -566,6 +575,10 @@ function readRuntimeRows(filename) {
}))); })));
} }
function readRuntimeRows(filename) {
return readCsvRows(OUTPUT_DIR, filename);
}
function writeRuntimeViews(base, headers, views, digits = {}) { function writeRuntimeViews(base, headers, views, digits = {}) {
writeViewFiles(base, headers, views, digits); writeViewFiles(base, headers, views, digits);
} }
@@ -614,6 +627,31 @@ writeRuntimeViews('ppmt_category', ['period', 'plush', 'figure', 'mega', 'other'
plush: 2, figure: 2, mega: 2, other: 2, plush: 2, figure: 2, mega: 2, other: 2,
}); });
// 2024H1 起门店披露可将中国内地与港澳台合并,并按全球总数归一化为地区占比。
const retailStoreRows = readRuntimeRows('ppmt_stores_retail.csv');
const retailStoreShareViews = { all: [], annual: [], halfyear: [] };
retailStoreRows.filter((row) => (
Number(row.period.slice(0, 4)) >= 2024
&& ['mainland', 'hkmtw', 'apac', 'americas', 'europe', 'global'].every((key) => Number.isFinite(row[key]))
)).forEach((row) => {
const china = row.mainland + row.hkmtw;
const other = row.global - china - row.apac - row.americas - row.europe;
const shareRow = {
period: row.period,
global: row.global,
china: china / row.global * 100,
apac: row.apac / row.global * 100,
americas: row.americas / row.global * 100,
europe: row.europe / row.global * 100,
other: other / row.global * 100,
};
retailStoreShareViews.all.push(shareRow);
retailStoreShareViews[row.period.endsWith('FY') ? 'annual' : 'halfyear'].push(shareRow);
});
writeRuntimeViews('ppmt_stores_retail_share', ['period', 'global', 'china', 'apac', 'americas', 'europe', 'other'], retailStoreShareViews, {
global: 0, china: 1, apac: 1, americas: 1, europe: 1, other: 1,
});
// 资产负债表为时点指标:H2 是年末时点,而非“全年累计”。 // 资产负债表为时点指标:H2 是年末时点,而非“全年累计”。
function buildSnapshotViews(base, headers, digits) { function buildSnapshotViews(base, headers, digits) {
const source = readRuntimeRows(`${base}.csv`); const source = readRuntimeRows(`${base}.csv`);
@@ -643,4 +681,67 @@ buildSnapshotViews('ppmt_liquidity_working_capital', ['period', 'cash', 'total_l
cash: 2, total_liabilities: 2, net_working_capital: 2, cash: 2, total_liabilities: 2, net_working_capital: 2,
}); });
// 存货为资产负债表期末时点数:半年仅取中报期末,年度仅取年报期末。
// 同比严格按相同报告期(H1 对 H1、FY 对 FY)计算,不把中报与年报交叉比较。
const inventoryBalances = row(balance, '存货');
const inventoryViews = { all: [], annual: [], halfyear: [] };
balance.dates.map((date, index) => ({
date,
inventory: inventoryBalances[index],
})).filter(({ date, inventory }) => (
date >= '2017-01-01' && inventory > 0 && (date.endsWith('-06-30') || date.endsWith('-12-31'))
)).sort((a, b) => a.date.localeCompare(b.date)).forEach(({ date, inventory }) => {
const row = { period: periodFor(date), inventory: inventory / HUNDRED_MILLION };
inventoryViews.all.push(row);
inventoryViews[row.period.endsWith('FY') ? 'annual' : 'halfyear'].push(row);
});
Object.keys(inventoryViews).forEach((view) => {
inventoryViews[view] = yoyFor(inventoryViews[view], 'inventory', 'yoy');
});
writeViewFiles('ppmt_inventory', ['period', 'inventory', 'yoy'], inventoryViews, {
inventory: 2, yoy: 1,
}, 2017);
console.log('已生成第二批连续图表的全部、年度、半年 CSV。'); console.log('已生成第二批连续图表的全部、年度、半年 CSV。');
// 渠道收入占比:底表 data/ppmt/rawData/地区渠道收入.csv 整理自
// 泡泡玛特-rawdata.xlsx「各地区渠道收入」工作表(中国含港澳台的四地区 × 四渠道明细)。
// 全球合计 = 四地区加总;披露口径只有 H1 与全年,H2 单半年 = 全年 − H1;
// 占比在金额视图之后计算,按当期四渠道合计归一化,不可用占比直接相减。
const CHANNEL_NAME_TO_KEY = {
零售店: 'retail',
机器人商店: 'robot_store',
线上渠道: 'online',
批发及其他: 'wholesale',
};
const channelRevenueByPeriod = new Map();
readCsvRows(RAWDATA_DIR, '地区渠道收入.csv').forEach(({ region, channel, period, revenue }) => {
const key = CHANNEL_NAME_TO_KEY[channel];
if (!key) throw new Error(`地区渠道收入底表存在未知渠道:${channel}`);
if (!period || !Number.isFinite(revenue)) throw new Error(`地区渠道收入底表行不完整:${region} ${channel} ${period} ${revenue}`);
if (!channelRevenueByPeriod.has(period)) {
channelRevenueByPeriod.set(period, { retail: 0, online: 0, robot_store: 0, wholesale: 0 });
}
channelRevenueByPeriod.get(period)[key] += revenue;
});
const channelAmountViews = buildViews((year, type) => {
const bucket = channelRevenueByPeriod.get(type === 'H1' ? `${year}H1` : `${year}FY`);
return bucket ?? null;
});
const channelShareViews = Object.fromEntries(Object.entries(channelAmountViews).map(([view, rows]) => [
view,
rows.map(({ period, ...amounts }) => {
const total = Object.values(amounts).reduce((sum, value) => sum + value, 0);
return {
period,
...Object.fromEntries(Object.entries(amounts).map(([key, value]) => [key, value / total * 100])),
total_revenue: total,
};
}),
]));
writeRuntimeViews('ppmt_channel_share', ['period', 'retail', 'online', 'robot_store', 'wholesale', 'total_revenue'], channelShareViews, {
retail: 1, online: 1, robot_store: 1, wholesale: 1, total_revenue: 2,
});
console.log('已生成渠道收入占比的全部、年度、半年 CSV。');