- 各投手本周期内投放花费: 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>
83 lines
6.1 KiB
HTML
83 lines
6.1 KiB
HTML
<!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> |