Add spend labels on bars and $ currency symbols
- 各投手本周期内投放花费: draw value labels at each bar end via a small inline Chart.js plugin (no extra CDN dependency); right padding added so labels stay inside the panel. - Prefix all spend figures with $: summary card, both chart axes and tooltips, buyer subtotals, and package detail tables. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+26
-5
@@ -40,20 +40,41 @@ summary{cursor:pointer;color:var(--accent);font-size:12px}
|
||||
</div>
|
||||
<script>
|
||||
const fmt=n=>n.toLocaleString('en-US',{minimumFractionDigits:2,maximumFractionDigits:2});
|
||||
const usd=n=>'$'+fmt(n);
|
||||
const axisUsd=v=>'$'+Number(v).toLocaleString('en-US');
|
||||
// 柱状条末端花费标签(内联插件,避免额外引入 chartjs-plugin-datalabels)
|
||||
const barLabels={id:'barLabels',afterDatasetsDraw(chart){
|
||||
const ctx=chart.ctx;
|
||||
chart.data.datasets.forEach((ds,di)=>{
|
||||
const meta=chart.getDatasetMeta(di);
|
||||
if(meta.hidden)return;
|
||||
meta.data.forEach((el,i)=>{
|
||||
const v=ds.data[i];
|
||||
if(v==null)return;
|
||||
ctx.save();
|
||||
ctx.font='600 11px "JetBrains Mono","Menlo",monospace';
|
||||
ctx.fillStyle='#2D1B20';
|
||||
ctx.textBaseline='middle';
|
||||
ctx.textAlign='left';
|
||||
ctx.fillText(usd(v),el.x+6,el.y);
|
||||
ctx.restore();
|
||||
});
|
||||
});
|
||||
}};
|
||||
// 直接使用 data.json,Excel 支持由后续更新处理
|
||||
fetch('data.json?t='+Date.now()).then(r=>r.json()).then(d=>render(d)).catch(e=>{document.getElementById('metaLine').textContent='数据加载失败:'+e.message;});
|
||||
function render(d){
|
||||
document.getElementById('weekBadge').textContent=d.meta.week||'';
|
||||
document.getElementById('metaLine').textContent='数据周期:'+d.meta.date_range+' · 数据来源:'+d.meta.source+' · 生成时间:'+new Date(d.meta.generated_at).toLocaleString('zh-CN');
|
||||
const buyers=d.buyers.slice().sort((a,b)=>b.total-a.total);
|
||||
const cards=[['投手数',buyers.length],['投放花费合计',fmt(d.grand_total)],['覆盖 Adpingid 数',d.week34_id_count],['花费最高投手',buyers[0].name]];
|
||||
const cards=[['投手数',buyers.length],['投放花费合计',usd(d.grand_total)],['覆盖 Adpingid 数',d.week34_id_count],['花费最高投手',buyers[0].name]];
|
||||
document.getElementById('cards').innerHTML=cards.map(c=>'<div class="card"><div class="k">'+c[0]+'</div><div class="v">'+c[1]+'</div></div>').join('');
|
||||
const palette=['#5b8cff','#ff9f5b','#4dd6a8','#c78bff','#ff6b81','#f5c451'];
|
||||
new Chart(document.getElementById('spendChart'),{type:'bar',data:{labels:buyers.map(b=>b.name),datasets:[{label:'投放花费',data:buyers.map(b=>b.total),backgroundColor:buyers.map((b,i)=>palette[i%palette.length])}]},options:{indexAxis:'y',plugins:{legend:{display:false}},scales:{x:{ticks:{color:'#5b6472'},grid:{color:'#d6e2f5'}},y:{ticks:{color:'#111111'},grid:{display:false}}}}});
|
||||
new Chart(document.getElementById('pkgChart'),{type:'bar',data:{labels:buyers.map(b=>b.name),datasets:[{label:'主要Package花费',data:buyers.map(b=>b.pkgs[0].spend),backgroundColor:'#4dd6a8'}]},options:{plugins:{legend:{display:false},tooltip:{callbacks:{afterLabel:(ctx)=>buyers[ctx.dataIndex].pkgs[0].pkg}}},scales:{x:{ticks:{color:'#111111'},grid:{display:false}},y:{ticks:{color:'#5b6472'},grid:{color:'#d6e2f5'}}}}});
|
||||
new Chart(document.getElementById('spendChart'),{type:'bar',data:{labels:buyers.map(b=>b.name),datasets:[{label:'投放花费',data:buyers.map(b=>b.total),backgroundColor:buyers.map((b,i)=>palette[i%palette.length])}]},plugins:[barLabels],options:{indexAxis:'y',layout:{padding:{right:76}},plugins:{legend:{display:false},tooltip:{callbacks:{label:(c)=>usd(c.parsed.x)}}},scales:{x:{ticks:{color:'#5b6472',callback:axisUsd},grid:{color:'#d6e2f5'}},y:{ticks:{color:'#111111'},grid:{display:false}}}}});
|
||||
new Chart(document.getElementById('pkgChart'),{type:'bar',data:{labels:buyers.map(b=>b.name),datasets:[{label:'主要Package花费',data:buyers.map(b=>b.pkgs[0].spend),backgroundColor:'#4dd6a8'}]},options:{plugins:{legend:{display:false},tooltip:{callbacks:{label:(c)=>usd(c.parsed.y),afterLabel:(ctx)=>buyers[ctx.dataIndex].pkgs[0].pkg}}},scales:{x:{ticks:{color:'#111111'},grid:{display:false}},y:{ticks:{color:'#5b6472',callback:axisUsd},grid:{color:'#d6e2f5'}}}}});
|
||||
document.getElementById('tables').innerHTML=buyers.map(b=>{
|
||||
const rows=b.pkgs.map(p=>'<tr><td class="pkg">'+p.pkg+'</td><td class="num">'+fmt(p.spend)+'</td></tr>').join('');
|
||||
return '<div style="margin-bottom:18px"><div class="buyer-name">'+b.name+' <span style="color:var(--sub);font-weight:400">合计 '+fmt(b.total)+' · 主要订单 '+b.pkgs[0].pkg+'</span></div><details><summary>展开 '+b.pkgs.length+' 个 Package 明细</summary><table><thead><tr><th>Package</th><th class="num">花费</th></tr></thead><tbody>'+rows+'</tbody></table></details></div>';
|
||||
const rows=b.pkgs.map(p=>'<tr><td class="pkg">'+p.pkg+'</td><td class="num">'+usd(p.spend)+'</td></tr>').join('');
|
||||
return '<div style="margin-bottom:18px"><div class="buyer-name">'+b.name+' <span style="color:var(--sub);font-weight:400">合计 '+usd(b.total)+' · 主要订单 '+b.pkgs[0].pkg+'</span></div><details><summary>展开 '+b.pkgs.length+' 个 Package 明细</summary><table><thead><tr><th>Package</th><th class="num">花费</th></tr></thead><tbody>'+rows+'</tbody></table></details></div>';
|
||||
}).join('');
|
||||
document.getElementById('foot').textContent=d.meta.note;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user