From eb4a0f6529ea1971cca98a1ac8d5de48c2207e01 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 10 Sep 2026 19:01:53 +0800 Subject: [PATCH] Show package KPI event name on metric card --- index.html | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index f9ab8ae..0f7329f 100644 --- a/index.html +++ b/index.html @@ -12,7 +12,7 @@ .filters{display:grid;grid-template-columns:repeat(8,minmax(125px,1fr));gap:12px;padding:16px;margin-bottom:16px}.field label{display:block;color:var(--muted);font-size:12px;margin-bottom:5px} select,input{width:100%;height:36px;border:1px solid #cfd7df;border-radius:7px;background:#fff;padding:0 9px;color:var(--text)} select[multiple]{height:72px;padding:4px 8px}.range-note{font-size:11px;color:var(--muted);margin-top:4px} - .cards{display:grid;grid-template-columns:repeat(9,1fr);gap:12px;margin-bottom:16px}.card{padding:16px}.card .label{color:var(--muted);font-size:12px}.card .value{font-size:23px;font-weight:650;margin-top:5px;font-variant-numeric:tabular-nums} + .cards{display:grid;grid-template-columns:repeat(9,1fr);gap:12px;margin-bottom:16px}.card{padding:16px;min-width:0}.card .label{color:var(--muted);font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.card .value{font-size:23px;font-weight:650;margin-top:5px;font-variant-numeric:tabular-nums} .grid{margin-bottom:16px}.chart{padding:18px;min-height:340px;position:relative}.title{font-size:16px;font-weight:650;margin-bottom:12px}.chart-head{display:flex;align-items:flex-start;justify-content:space-between;gap:16px}.chart-sub{font-size:12px;color:var(--muted);margin-top:-8px} svg{width:100%;height:270px;margin-top:8px}.axis{stroke:#d8dfe6}.gridline{stroke:#edf1f4}.spend-line{fill:none;stroke:var(--brand);stroke-width:2.5}.conv-line{fill:none;stroke:var(--good);stroke-width:2.5}.legend{display:flex;gap:8px}.legend button{border:1px solid var(--line);border-radius:16px;background:#fff;color:var(--muted);padding:5px 10px;cursor:pointer}.legend button.off{opacity:.4}.dot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:5px}.axis-label{fill:#74808c;font-size:11px}.axis-title{font-size:11px;font-weight:650}.point{stroke:#fff;stroke-width:2;cursor:crosshair}.hover-line{stroke:#9aa7b3;stroke-dasharray:3 3}.tooltip{position:absolute;display:none;pointer-events:none;background:#17212b;color:#fff;border-radius:7px;padding:8px 10px;font-size:12px;line-height:1.55;box-shadow:0 4px 14px #0003;z-index:2}.num{text-align:right;font-variant-numeric:tabular-nums} .table-panel{padding:18px}.table-head{display:flex;justify-content:space-between;align-items:center;margin-bottom:10px}.table-note{color:var(--muted);font-size:12px}.table-wrap{overflow:auto;max-height:640px;border:1px solid var(--line);border-radius:8px} @@ -52,8 +52,9 @@ function updateDateBounds(reset=false){const months=selected('month'),rows=ancho function updateAnchoredFilters(resetDates=false){const rows=anchorRows();multiOptions('month',rows.map(r=>r.date.slice(0,7)));singleOptions('geo',rows.map(r=>r.geo));singleOptions('source',rows.map(r=>r.source));multiOptions('group',rows.map(r=>r.group));updateDateBounds(resetDates)} function filtered(){const q=$('search').value.trim().toLowerCase(),months=selected('month'),groups=selected('group');return all.filter(r=>r.status==='Active'&&(!$('pkg').value||r.pkg===$('pkg').value)&&(!months.length||months.includes(r.date.slice(0,7)))&&(!$('geo').value||r.geo===$('geo').value)&&(!$('source').value||r.source===$('source').value)&&(!groups.length||groups.includes(r.group))&&(!$('start').value||r.date>=$('start').value)&&(!$('end').value||r.date<=$('end').value)&&(!q||(`${r.campaign} ${r.adpingId}`).toLowerCase().includes(q)))} const tracksEvent=r=>{const k=String(r.kpi||'').trim();return k&&k!=='-'}; +function eventLabel(rows){const names=[...new Set(rows.filter(tracksEvent).map(r=>String(r.kpi).trim()))];return names.length?names.join(' / '):'KPI Event'} function aggregate(rows,keys){const m=new Map;for(const r of rows){const k=keys.map(x=>r[x]).join('\u0001');if(!m.has(k))m.set(k,{...Object.fromEntries(keys.map(x=>[x,r[x]])),impression:0,click:0,conversions:0,spend:0,event:0,eventSpend:0,eventKnown:false,kpi:r.kpi||''});const o=m.get(k);for(const x of ['impression','click','conversions','spend'])o[x]+=+r[x]||0;if(r.event!==null&&r.event!==undefined&&r.event!==''){o.event+=+r.event||0;o.eventSpend+=+r.spend||0;o.eventKnown=true}}return [...m.values()]} -function cards(rows){const t=aggregate(rows,[])[0]||{impression:0,click:0,conversions:0,spend:0},e=aggregate(rows.filter(tracksEvent),[])[0]||{event:0,eventKnown:false,eventSpend:0};const vals=[['Spend',money(t.spend)],['Event',e.eventKnown?n(e.event):'—'],['CAC',e.eventKnown&&e.event?money(e.eventSpend/e.event):'—'],['Conversions',n(t.conversions)],['CPI',t.conversions?money(t.spend/t.conversions):'—'],['Impression',n(t.impression)],['Click',n(t.click)],['CTR',pct(rate(t.click,t.impression))],['CVR',pct(rate(t.conversions,t.click))]];$('cards').innerHTML=vals.map(x=>`
${x[0]}
${x[1]}
`).join('')} +function cards(rows){const t=aggregate(rows,[])[0]||{impression:0,click:0,conversions:0,spend:0},e=aggregate(rows.filter(tracksEvent),[])[0]||{event:0,eventKnown:false,eventSpend:0},eventName=eventLabel(rows);const vals=[['Spend',money(t.spend)], [eventName,e.eventKnown?n(e.event):'—'],['CAC',e.eventKnown&&e.event?money(e.eventSpend/e.event):'—'],['Conversions',n(t.conversions)],['CPI',t.conversions?money(t.spend/t.conversions):'—'],['Impression',n(t.impression)],['Click',n(t.click)],['CTR',pct(rate(t.click,t.impression))],['CVR',pct(rate(t.conversions,t.click))]];$('cards').innerHTML=vals.map(x=>`
${x[0]}
${x[1]}
`).join('')} function compact(v,prefix=''){return prefix+Intl.NumberFormat('en-US',{notation:'compact',maximumFractionDigits:1}).format(v)} function trend(rows){const d=aggregate(rows,['date']).sort((a,b)=>a.date.localeCompare(b.date)),svg=$('trend'),tip=$('trendTip');if(!d.length){svg.innerHTML='当前筛选没有数据';return}const W=900,H=270,L=62,R=66,T=20,B=38,maxS=Math.max(...d.map(x=>x.spend),1),maxC=Math.max(...d.map(x=>x.conversions),1),x=i=>L+(W-L-R)*(d.length===1?.5:i/(d.length-1)),y=(v,max)=>T+(H-T-B)*(1-v/max),path=(key,max)=>d.map((r,i)=>(i?'L':'M')+x(i).toFixed(1)+' '+y(r[key],max).toFixed(1)).join(' ');let parts=[];for(let i=0;i<=4;i++){const yy=T+(H-T-B)*i/4,ratio=1-i/4;parts.push(`${compact(maxS*ratio,'$')}${compact(maxC*ratio)}`)}const labelStep=Math.max(1,Math.ceil(d.length/8));parts.push(...d.map((r,i)=>(i%labelStep===0||i===d.length-1)?`${r.date.slice(5)}`:''));parts.push(`Spend (USD)Conversions`);if(trendVisible.spend)parts.push(``);if(trendVisible.conversions)parts.push(``);parts.push(...d.map((r,i)=>``));svg.innerHTML=parts.join('');svg.querySelectorAll('.trend-hit').forEach(el=>{el.addEventListener('mouseenter',()=>{const r=d[+el.dataset.index];tip.innerHTML=`${r.date}
Spend:${money(r.spend)}
Conversions:${n(r.conversions)}`;tip.style.display='block'});el.addEventListener('mousemove',e=>{const box=svg.getBoundingClientRect(),panel=svg.parentElement.getBoundingClientRect();tip.style.left=Math.min(e.clientX-panel.left+12,panel.width-tip.offsetWidth-12)+'px';tip.style.top=(e.clientY-panel.top-tip.offsetHeight-10)+'px'});el.addEventListener('mouseleave',()=>tip.style.display='none')})} function table(rows){const d=aggregate(rows,['date','group','campaign','kpi']).sort((a,b)=>b.date.localeCompare(a.date)||(a.group==='对照组'?-1:1)-(b.group==='对照组'?-1:1)||b.spend-a.spend);$('count').textContent=`${n(d.length)} 行`;const limited=d.slice(0,2000),dateCounts={};for(const r of limited)dateCounts[r.date]=(dateCounts[r.date]||0)+1;const seen=new Set;$('body').innerHTML=limited.map(r=>{const first=!seen.has(r.date),required=tracksEvent(r);seen.add(r.date);return`${first?`${r.date}`:''}${r.group}${r.campaign}${r.kpi||'-'}${required&&r.eventKnown?n(r.event):'—'}${money(r.spend)}${required&&r.eventKnown&&r.event?money(r.eventSpend/r.event):'—'}${n(r.impression)}${n(r.click)}${n(r.conversions)}${pct(rate(r.click,r.impression))}${pct(rate(r.conversions,r.click))}${r.conversions?money(r.spend/r.conversions):'—'}`}).join('')||'当前筛选没有数据'}