Files
mediabuy-wh/index.html
T
FANGKKandClaude Opus 5 d980690ff2 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>
2026-08-27 14:23:36 +08:00

83 lines
6.1 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>武汉自投 · 投手投放花费报告</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
<style>
:root{--bg:#ffffff;--card:#FAF7F7;--line:#EBE7E8;--txt:#2D1B20;--sub:#827578;--accent:#C4506A;--accent2:#C4506A;}*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--txt);font-family:"Inter","PingFang TC","Apple LiGothic","STHeiti","Hiragino Sans GB",-apple-system,BlinkMacSystemFont,"Microsoft YaHei",sans-serif;line-height:1.5}
.wrap{max-width:1080px;margin:0 auto;padding:28px 20px 60px}
h1{font-size:24px;margin:0 0 4px;font-family:"DM Sans","Inter",sans-serif}.sub{color:var(--sub);font-size:13px;margin-bottom:22px}
.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:14px;margin-bottom:26px}
.card{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:16px 18px}
.card .k{color:var(--sub);font-size:12px}
.card .v{font-size:22px;font-weight:600;margin-top:6px}
.panel{background:var(--card);border:1px solid var(--line);border-radius:12px;padding:18px;margin-bottom:22px}
.panel h2{font-size:16px;margin:0 0 14px}
table{width:100%;border-collapse:collapse;font-size:13px}
th,td{padding:9px 10px;text-align:left;border-bottom:1px solid var(--line)}
th{color:var(--sub);font-weight:500;font-size:12px}
td.num,th.num{text-align:right;font-variant-numeric:tabular-nums;font-family:"JetBrains Mono","Menlo",monospace}.pkg{font-family:"JetBrains Mono","Menlo","Consolas",monospace;font-size:12px;color:#333a45}.buyer-name{font-weight:600}
details{margin-top:6px}
summary{cursor:pointer;color:var(--accent);font-size:12px}
.foot{color:var(--sub);font-size:12px;margin-top:30px;border-top:1px solid var(--line);padding-top:14px}
.badge{display:inline-block;background:#e6eefc;color:var(--sub);border-radius:6px;padding:2px 8px;font-size:11px;margin-left:8px}
.chartbox{position:relative;height:300px}
</style>
<script src="https://cdn.jsdelivr.net/npm/xlsx@0.18.5/dist/xlsx.full.min.js"></script>
</head>
<body>
<div class="wrap">
<h1>武汉自投 · 投手投放花费报告 <span class="badge" id="weekBadge"></span></h1>
<div class="sub" id="metaLine">加载中…</div>
<div class="cards" id="cards"></div>
<div class="panel"><h2>各投手本周期内投放花费</h2><div class="chartbox"><canvas id="spendChart"></canvas></div></div>
<div class="panel"><h2>各投手主要订单 Package(花费最高)</h2><div class="chartbox"><canvas id="pkgChart"></canvas></div></div>
<div class="panel"><h2>投手明细</h2><div id="tables"></div></div>
<div class="foot" id="foot"></div>
</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],['投放花费合计',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])}]},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">'+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;
}
</script>
</body>
</html>