feat: 构建泡泡玛特相关图表
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
import * as echarts from 'echarts';
|
||||
import fs from 'fs';
|
||||
// 项目根目录(从 test/regress.mjs 向上一层,或直接指定绝对路径)
|
||||
const P = '/Users/joey/sites/vest-tools/echart-demo/';
|
||||
const {parseCSV}=await import(P+'js/csv.js');
|
||||
const all=[
|
||||
['inventory','inventoryChart','data/inventory.csv'],
|
||||
['margin','marginChart','data/margin.csv'],
|
||||
['twoline','twoLineChart','data/margin.csv'],
|
||||
['growth','growthChart','data/growth.csv'],
|
||||
['fx','fxChart','data/fx.csv'],
|
||||
['ppmt_assets','assetsChart','data/ppmt_assets.csv'],
|
||||
['ppmt_profit','profitChart','data/ppmt_profit.csv'],
|
||||
['ppmt_ip','ipChart','data/ppmt_ip.csv'],
|
||||
['ppmt_category','categoryChart','data/ppmt_category.csv'],
|
||||
['ppmt_sankey','sankeyChart','data/ppmt_sankey_2026h1.csv'],
|
||||
['ppmt_region_yoy','regionChannelYoyChart','data/ppmt_region_channel_yoy.csv'],
|
||||
['ppmt_growth','ppmtGrowthChart','data/ppmt_growth.csv'],
|
||||
['ppmt_margins','ppmtMarginsChart','data/ppmt_margins.csv'],
|
||||
['ppmt_bridge','ppmtBridgeChart','data/ppmt_margin_bridge.csv'],
|
||||
['ppmt_core_profit','ppmtCoreProfitChart','data/ppmt_core_profit.csv'],
|
||||
['ppmt_core_margin','ppmtCoreMarginChart','data/ppmt_core_margin.csv'],
|
||||
['ppmt_ip_share','ppmtIpShareChart','data/ppmt_ip_share_2026h1.csv'],
|
||||
['ppmt_ip_share_history','ppmtIpShareHistoryChart','data/ppmt_ip_share_history.csv'],
|
||||
['ppmt_ip_ex_monsters','ppmtIpExMonstersChart','data/ppmt_ip_ex_monsters.csv'],
|
||||
];
|
||||
|
||||
// 可传图表名只跑指定的(改一张图不用全量回归):
|
||||
// node test/regress.mjs ppmt_ip_share ppmt_core_margin
|
||||
const only=process.argv.slice(2);
|
||||
const targets=only.length>0 ? all.filter(([f])=>only.includes(f)) : all;
|
||||
if(only.length>0){
|
||||
const missing=only.filter(f=>!all.some(([name])=>name===f));
|
||||
if(missing.length>0) console.log(`⚠ 未找到图表:${missing.join(', ')}`);
|
||||
}
|
||||
|
||||
// 样式规范断言:从核心利润/利润率拆解图总结的标准
|
||||
const STYLE_RULES={
|
||||
legendTop:85, // 固定值:副标题到图例约25px
|
||||
gridTop:145, // 固定值:图例到绘图区约60px
|
||||
lineSeries:{ // 折线标准样式(空心圆点)
|
||||
symbolSize:9,
|
||||
borderWidth:2.5,
|
||||
fill:'#fff', // 白心
|
||||
},
|
||||
};
|
||||
|
||||
let ok=0,fail=0,styleWarnings=[];
|
||||
for(const [f,fn,csv] of targets){
|
||||
try{
|
||||
const mod=await import(P+`js/charts/${f}.js`);
|
||||
const rows=parseCSV(fs.readFileSync(P+csv,'utf8'));
|
||||
const c=echarts.init(null,null,{renderer:'svg',ssr:true,width:1100,height:720});
|
||||
const opt=mod[fn](rows,{width:1100,height:720});
|
||||
c.setOption(opt);
|
||||
const svg=c.renderToSVGString();
|
||||
|
||||
// 1. SVG 格式检查(字体引号)
|
||||
const badAttr=/style="[^"]*"[^"=>\s][^>]*>/.test(svg.slice(0,3000));
|
||||
|
||||
// 2. 样式规范检查(跳过无图例的图表)
|
||||
if(opt.legend?.top!==undefined){
|
||||
if(opt.legend.top!==STYLE_RULES.legendTop){
|
||||
styleWarnings.push(`${f}: legend.top=${opt.legend.top} (期望${STYLE_RULES.legendTop})`);
|
||||
}
|
||||
}
|
||||
if(opt.grid?.top!==undefined && opt.legend?.top!==undefined){
|
||||
if(opt.grid.top!==STYLE_RULES.gridTop){
|
||||
styleWarnings.push(`${f}: grid.top=${opt.grid.top} (期望${STYLE_RULES.gridTop})`);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 折线圆点样式检查(在 SVG 里验证 symbolSize 和 borderWidth)
|
||||
const circles=[...svg.matchAll(/<path d="M1 0A1[^"]*" transform="matrix\(([\d.]+),0,0,[\d.]+,[^)]+\)" fill="#fff" stroke="[^"]*" stroke-width="([\d.]+)"/g)];
|
||||
if(circles.length>0){
|
||||
const scale=+circles[0][1], sw=+circles[0][2];
|
||||
const symbolSize=scale*2, borderWidth=sw*scale;
|
||||
if(Math.abs(symbolSize-STYLE_RULES.lineSeries.symbolSize)>0.1){
|
||||
styleWarnings.push(`${f}: 折线 symbolSize=${symbolSize.toFixed(1)} (期望${STYLE_RULES.lineSeries.symbolSize})`);
|
||||
}
|
||||
if(Math.abs(borderWidth-STYLE_RULES.lineSeries.borderWidth)>0.1){
|
||||
styleWarnings.push(`${f}: 折线 borderWidth=${borderWidth.toFixed(1)} (期望${STYLE_RULES.lineSeries.borderWidth})`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`${badAttr?'⚠':'✓'} ${f.padEnd(17)} ${String(svg.length).padStart(6)}B`);
|
||||
ok++; c.dispose();
|
||||
}catch(e){console.log(`✗ ${f.padEnd(17)} ${e.message}`);fail++;}
|
||||
}
|
||||
console.log(`\n${ok} 张成功 / ${fail} 张失败`);
|
||||
|
||||
if(styleWarnings.length>0){
|
||||
console.log(`\n⚠ ${styleWarnings.length} 处样式偏离规范:`);
|
||||
styleWarnings.forEach(w=>console.log(` ${w}`));
|
||||
}else{
|
||||
console.log('✓ 样式规范全部符合');
|
||||
}
|
||||
process.exit(fail>0?1:0);
|
||||
Reference in New Issue
Block a user