feat: channel efficiency table grouped by product_country

This commit is contained in:
王源
2026-09-09 21:01:36 +08:00
parent b8c7afc1d8
commit 1c53faa015
+28 -8
View File
@@ -179,14 +179,15 @@
</div>
</div>
<!-- 模块4:渠道效率指标表 -->
<!-- 模块4:渠道效率对比(按 产品_国家 分组,展开各渠道)-->
<div class="section">
<h2>渠道效率对比</h2>
<h2>渠道效率对比(产品_国家 × 渠道)</h2>
<p class="hint">每个产品_国家下列出其投放过的各渠道及效率指标,同组内可横向对比不同渠道表现。</p>
<div class="table-wrapper">
<table id="channelTable">
<thead>
<tr>
<th>渠道</th><th>展示</th><th>点击</th><th>转化</th>
<th>产品_国家</th><th>渠道</th><th>展示</th><th>点击</th><th>转化</th>
<th>花费</th><th>CTR</th><th>CVR</th><th>eCPM</th><th>CPC</th><th>CPA</th>
</tr>
</thead>
@@ -446,17 +447,36 @@
options:{ responsive:true, maintainAspectRatio:false, plugins:{legend:{position:'right',labels:{boxWidth:12,font:{size:11}}}} }
});
// ---- 模块4:渠道效率表 ----
document.querySelector('#channelTable tbody').innerHTML = chans.map(c => {
const cm = metrics(data.filter(x=>x.channel===c));
return `<tr>
// ---- 模块4:渠道效率对比(按 产品_国家 分组,展开各渠道)----
// 分组键 = 产品 + 国家
const groupMap = {};
data.forEach(x => {
const g = x.product + '_' + x.location;
(groupMap[g] = groupMap[g] || []).push(x);
});
// 分组按总展示降序;组内渠道也按展示降序
const groupKeys = Object.keys(groupMap).sort((a,b)=> sum(groupMap[b],'impression') - sum(groupMap[a],'impression'));
let rowsHtml = '';
groupKeys.forEach(g => {
const rows = groupMap[g];
const chansInG = [...new Set(rows.map(x=>x.channel))]
.sort((a,b)=> sum(rows.filter(x=>x.channel===b),'impression') - sum(rows.filter(x=>x.channel===a),'impression'));
chansInG.forEach((c, idx) => {
const cm = metrics(rows.filter(x=>x.channel===c));
const groupCell = idx===0
? `<td rowspan="${chansInG.length}" style="font-weight:600;vertical-align:top;background:#fafbfc;">${g}</td>`
: '';
rowsHtml += `<tr>
${groupCell}
<td><span class="tag" style="background:${(CH_COLORS[c]||'#999')}20;color:${CH_COLORS[c]||'#666'}">${c}</span></td>
<td class="num">${fmtNum(cm.imp)}</td><td class="num">${fmtNum(cm.clk)}</td><td class="num">${cm.cv}</td>
<td class="num">${fmtMoney(cm.sp)}</td><td class="num">${(cm.ctr*100).toFixed(2)}%</td>
<td class="num">${(cm.cvr*100).toFixed(2)}%</td><td class="num">$${cm.ecpm.toFixed(2)}</td>
<td class="num">$${cm.cpc.toFixed(3)}</td><td class="num">${cm.cpa?fmtMoney(cm.cpa):'-'}</td>
</tr>`;
}).join('') || `<tr><td colspan="10" class="empty">暂无数据</td></tr>`;
});
});
document.querySelector('#channelTable tbody').innerHTML = rowsHtml || `<tr><td colspan="11" class="empty">暂无数据</td></tr>`;
// ---- 模块5:产品表现排行(按花费降序)----
const prods = [...new Set(data.map(d=>d.product))];