feat: 构建泡泡玛特相关图表
This commit is contained in:
+99
@@ -0,0 +1,99 @@
|
||||
import { loadCSV } from './csv.js';
|
||||
import { inventoryChart } from './charts/inventory.js';
|
||||
import { marginChart } from './charts/margin.js';
|
||||
import { twoLineChart } from './charts/twoline.js';
|
||||
import { growthChart } from './charts/growth.js';
|
||||
import { fxChart } from './charts/fx.js';
|
||||
import { assetsChart } from './charts/ppmt_assets.js';
|
||||
import { profitChart } from './charts/ppmt_profit.js';
|
||||
import { ipChart } from './charts/ppmt_ip.js';
|
||||
import { categoryChart } from './charts/ppmt_category.js';
|
||||
import { sankeyChart } from './charts/ppmt_sankey.js';
|
||||
import { regionChannelYoyChart } from './charts/ppmt_region_yoy.js';
|
||||
import { ppmtGrowthChart } from './charts/ppmt_growth.js';
|
||||
import { ppmtMarginsChart } from './charts/ppmt_margins.js';
|
||||
import { ppmtBridgeChart } from './charts/ppmt_bridge.js';
|
||||
import { ppmtCoreProfitChart } from './charts/ppmt_core_profit.js';
|
||||
import { ppmtCoreMarginChart } from './charts/ppmt_core_margin.js';
|
||||
import { ppmtIpShareChart } from './charts/ppmt_ip_share.js';
|
||||
import { ppmtIpShareHistoryChart } from './charts/ppmt_ip_share_history.js';
|
||||
import { ppmtIpExMonstersChart } from './charts/ppmt_ip_ex_monsters.js';
|
||||
|
||||
/** 图表清单:加新图只需在这里加一行 */
|
||||
const CHARTS = [
|
||||
// 示例图表
|
||||
{ id: 'chart-inventory', csv: 'data/inventory.csv', build: inventoryChart, height: 620 },
|
||||
{ id: 'chart-margin', csv: 'data/margin.csv', build: marginChart, height: 620 },
|
||||
{ id: 'chart-twoline', csv: 'data/margin.csv', build: twoLineChart, height: 620 },
|
||||
{ id: 'chart-growth', csv: 'data/growth.csv', build: growthChart, height: 700 },
|
||||
{ id: 'chart-fx', csv: 'data/fx.csv', build: fxChart, height: 780 },
|
||||
// 泡泡玛特图表
|
||||
{ id: 'chart-ppmt-assets', csv: 'data/ppmt_assets.csv', build: assetsChart, height: 680 },
|
||||
{ id: 'chart-ppmt-profit', csv: 'data/ppmt_profit.csv', build: profitChart, height: 680 },
|
||||
{ id: 'chart-ppmt-ip', csv: 'data/ppmt_ip.csv', build: ipChart, height: 720 },
|
||||
{ id: 'chart-ppmt-category', csv: 'data/ppmt_category.csv', build: categoryChart, height: 680 },
|
||||
{ id: 'chart-ppmt-sankey', csv: 'data/ppmt_sankey_2026h1.csv', build: sankeyChart, height: 800 },
|
||||
{ id: 'chart-ppmt-region-yoy', csv: 'data/ppmt_region_channel_yoy.csv', build: regionChannelYoyChart, height: 620 },
|
||||
{ id: 'chart-ppmt-growth', csv: 'data/ppmt_growth.csv', build: ppmtGrowthChart, height: 700 },
|
||||
{ id: 'chart-ppmt-margins', csv: 'data/ppmt_margins.csv', build: ppmtMarginsChart, height: 680 },
|
||||
{ id: 'chart-ppmt-bridge', csv: 'data/ppmt_margin_bridge.csv', build: ppmtBridgeChart, height: 700 },
|
||||
{ id: 'chart-ppmt-core', csv: 'data/ppmt_core_profit.csv', build: ppmtCoreProfitChart, height: 720 },
|
||||
{ id: 'chart-ppmt-core-margin', csv: 'data/ppmt_core_margin.csv', build: ppmtCoreMarginChart, height: 680 },
|
||||
{ id: 'chart-ppmt-ip-share', csv: 'data/ppmt_ip_share_2026h1.csv', build: ppmtIpShareChart, height: 720 },
|
||||
{ id: 'chart-ppmt-ip-share-history', csv: 'data/ppmt_ip_share_history.csv', build: ppmtIpShareHistoryChart, height: 700 },
|
||||
{ id: 'chart-ppmt-ip-ex-monsters', csv: 'data/ppmt_ip_ex_monsters.csv', build: ppmtIpExMonstersChart, height: 700 },
|
||||
];
|
||||
|
||||
const instances = new Map();
|
||||
|
||||
async function render(spec) {
|
||||
const el = document.getElementById(spec.id);
|
||||
if (!el) return;
|
||||
|
||||
try {
|
||||
// 加时间戳绕过缓存,改完 CSV 刷新就能看到
|
||||
const rows = await loadCSV(`${spec.csv}?t=${Date.now()}`);
|
||||
const chart = instances.get(spec.id) || echarts.init(el, null, { renderer: 'canvas' });
|
||||
instances.set(spec.id, chart);
|
||||
|
||||
const { width, height } = el.getBoundingClientRect();
|
||||
chart.setOption(spec.build(rows, { width, height }), true);
|
||||
} catch (err) {
|
||||
el.innerHTML = `<div class="chart-error">${err.message}</div>`;
|
||||
console.error(spec.id, err);
|
||||
}
|
||||
}
|
||||
|
||||
async function renderAll() {
|
||||
await Promise.all(CHARTS.map(render));
|
||||
}
|
||||
|
||||
/** 导出当前图为 PNG,2 倍图,微信公众号里够清晰 */
|
||||
function exportPNG(id, filename) {
|
||||
const chart = instances.get(id);
|
||||
if (!chart) return;
|
||||
const url = chart.getDataURL({ type: 'png', pixelRatio: 2, backgroundColor: '#fff' });
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `${filename}.png`;
|
||||
a.click();
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
renderAll();
|
||||
|
||||
document.getElementById('btn-reload').addEventListener('click', renderAll);
|
||||
|
||||
document.querySelectorAll('[data-export]').forEach((btn) => {
|
||||
btn.addEventListener('click', () => {
|
||||
exportPNG(btn.dataset.export, btn.dataset.filename || btn.dataset.export);
|
||||
});
|
||||
});
|
||||
|
||||
let timer;
|
||||
window.addEventListener('resize', () => {
|
||||
clearTimeout(timer);
|
||||
// resize 后水印数量要跟着画布变,所以整体重绘而不是 chart.resize()
|
||||
timer = setTimeout(renderAll, 200);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user