板块2/3互换(客户分类前移为2.x,PKG波动后移为3.x); 3.2客户明细改为多天并列; DoD说明移至标题下

This commit is contained in:
2026-09-11 17:36:22 +08:00
parent 0fc8764c97
commit 0ae1838399
+67 -50
View File
@@ -51,8 +51,8 @@
<div class="meta" id="meta">加载中…</div>
<div class="tabs">
<div class="tab active" data-tab="overview">总日报</div>
<div class="tab" data-tab="pkg">PKG 收入波动</div>
<div class="tab" data-tab="cust">客户分类</div>
<div class="tab" data-tab="pkg">PKG 收入波动</div>
</div>
<!-- ===== 总日报 ===== -->
@@ -74,40 +74,16 @@
</div>
<!-- ===== PKG 收入波动 ===== -->
<div id="tab-pkg" class="hide">
<div class="card">
<h2>2.1 PKG 收入波动 - Midas <span class="tag">保留14天</span></h2>
<div class="ctrl">
<label><input type="checkbox" class="m-pkg" data-t="midas" checked> 按 pkg</label>
<label><input type="checkbox" class="m-acc" data-t="midas"> 按 account</label>
<input type="text" class="f-pkg" data-t="midas" placeholder="筛选 pkg">
<input type="text" class="f-acc" data-t="midas" placeholder="筛选 account">
<span class="sub-note" style="margin:0;">|DoD|&gt;100 标注(+绿−红);表头点击排序;波动原因暂存本浏览器</span>
</div>
<div class="scroll"><table id="tPkgMidas"><thead></thead><tbody></tbody></table></div>
</div>
<div class="card">
<h2>2.2 PKG 收入波动 - MS <span class="tag">保留14天</span></h2>
<div class="ctrl">
<label><input type="checkbox" class="m-pkg" data-t="ms" checked> 按 pkg</label>
<label><input type="checkbox" class="m-acc" data-t="ms"> 按 account</label>
<input type="text" class="f-pkg" data-t="ms" placeholder="筛选 pkg">
<input type="text" class="f-acc" data-t="ms" placeholder="筛选 account">
<span class="sub-note" style="margin:0;">|DoD|&gt;100 标注(+绿−红);表头点击排序;波动原因暂存本浏览器</span>
</div>
<div class="scroll"><table id="tPkgMS"><thead></thead><tbody></tbody></table></div>
</div>
</div>
<!-- ===== 客户分类 ===== -->
<!-- ===== 客户分类(板块2) ===== -->
<div id="tab-cust" class="hide">
<div class="card">
<h2>3.1 客户分类日报</h2>
<div class="sub-note">DoD=T-1−T-2,DoD%=T-1/T-2−1。波动原因可编辑(暂存本浏览器)。</div>
<h2>2.1 客户分类日报</h2>
<div class="sub-note">日期列为各日收入;DoD=最新日−前一日,DoD%=最新日/前一日−1。波动原因可编辑(暂存本浏览器)。</div>
<div class="scroll"><table id="tCustDaily"><thead></thead><tbody></tbody></table></div>
</div>
<div class="card">
<h2>3.2 客户明细 <span class="tag">保留14天</span></h2>
<h2>2.2 客户明细 <span class="tag">保留14天</span></h2>
<div class="sub-note">日期列为各客户各日收入;DoD=最新日−前一日。</div>
<div class="ctrl">
<label>客户分类 <select id="deCat"><option value="">全部</option></select></label>
<input type="text" id="deAcc" placeholder="筛选客户">
@@ -115,6 +91,32 @@
<div class="scroll"><table id="tCustDetail"><thead></thead><tbody></tbody></table></div>
</div>
</div>
<!-- ===== PKG 收入波动(板块3) ===== -->
<div id="tab-pkg" class="hide">
<div class="card">
<h2>3.1 PKG 收入波动 - Midas <span class="tag">保留14天</span></h2>
<div class="sub-note">|DoD|&gt;100 标注(+绿−红);表头点击排序;波动原因暂存本浏览器。</div>
<div class="ctrl">
<label><input type="checkbox" class="m-pkg" data-t="midas" checked> 按 pkg</label>
<label><input type="checkbox" class="m-acc" data-t="midas"> 按 account</label>
<input type="text" class="f-pkg" data-t="midas" placeholder="筛选 pkg">
<input type="text" class="f-acc" data-t="midas" placeholder="筛选 account">
</div>
<div class="scroll"><table id="tPkgMidas"><thead></thead><tbody></tbody></table></div>
</div>
<div class="card">
<h2>3.2 PKG 收入波动 - MS <span class="tag">保留14天</span></h2>
<div class="sub-note">|DoD|&gt;100 标注(+绿−红);表头点击排序;波动原因暂存本浏览器。</div>
<div class="ctrl">
<label><input type="checkbox" class="m-pkg" data-t="ms" checked> 按 pkg</label>
<label><input type="checkbox" class="m-acc" data-t="ms"> 按 account</label>
<input type="text" class="f-pkg" data-t="ms" placeholder="筛选 pkg">
<input type="text" class="f-acc" data-t="ms" placeholder="筛选 account">
</div>
<div class="scroll"><table id="tPkgMS"><thead></thead><tbody></tbody></table></div>
</div>
</div>
</div>
<script>
@@ -381,44 +383,59 @@ function renderCustDaily(){
// ===== 3.2 客户明细 =====
var DE_SORT={k:null,ty:'num',dir:'desc'};
function renderCustDetail(){
var dts=sortedDates(D.cust_detail);
var t1=dts[dts.length-1], t2=dts[dts.length-2];
var t1list=D.cust_detail[t1]||[];
var t2map={};
(D.cust_detail[t2]||[]).forEach(function(x){ t2map[x.cat+'||'+x.acc]=x.t1; });
var dts=sortedDates(D.cust_detail).slice(-14); // 升序, 最近14天
var latest=dts[dts.length-1], prev=dts[dts.length-2];
// 按 (cat,acc) 聚合各天收入 + base
var map={}; // key -> {cat,acc,base,byDt:{}}
dts.forEach(function(dt){
(D.cust_detail[dt]||[]).forEach(function(x){
var k=x.cat+'||'+x.acc;
if(!map[k]) map[k]={cat:x.cat,acc:x.acc,base:x.base,byDt:{}};
map[k].byDt[dt]=x.t1;
if(x.base!=null) map[k].base=x.base;
});
});
var fCat=(document.getElementById('deCat')||{}).value||'';
var fAcc=((document.getElementById('deAcc')||{}).value||'').trim().toLowerCase();
var rows=t1list.map(function(r){
var prev=t2map[r.cat+'||'+r.acc];
return {cat:r.cat,acc:r.acc,base:r.base,t1:r.t1,dod:(prev!=null?r.t1-prev:null)};
var rows=Object.keys(map).map(function(k){
var r=map[k];
var lv=r.byDt[latest], pv=prev?r.byDt[prev]:null;
r.dod=(lv!=null&&pv!=null)?lv-pv:(lv!=null?lv:null);
r.latest=lv;
return r;
}).filter(function(r){
if(fCat && r.cat!==fCat) return false;
if(fAcc && String(r.acc).toLowerCase().indexOf(fAcc)<0) return false;
return true;
});
// 排序
if(DE_SORT.k){
var dir=DE_SORT.dir==='asc'?1:-1;
rows.sort(function(a,b){
if(DE_SORT.ty==='str') return dir*String(a[DE_SORT.k]).localeCompare(String(b[DE_SORT.k]));
var va=a[DE_SORT.k]==null?-Infinity:a[DE_SORT.k], vb=b[DE_SORT.k]==null?-Infinity:b[DE_SORT.k];
var va,vb;
if(DE_SORT.k==='cat'||DE_SORT.k==='acc'){ return dir*String(a[DE_SORT.k]).localeCompare(String(b[DE_SORT.k])); }
if(DE_SORT.k==='base'){ va=a.base||0; vb=b.base||0; }
else if(DE_SORT.k==='dod'){ va=a.dod==null?-Infinity:a.dod; vb=b.dod==null?-Infinity:b.dod; }
else { va=a.byDt[DE_SORT.k]==null?-Infinity:a.byDt[DE_SORT.k]; vb=b.byDt[DE_SORT.k]==null?-Infinity:b.byDt[DE_SORT.k]; } // 某日期列
return (va<vb?-1:va>vb?1:0)*dir;
});
} else {
rows.sort(function(a,b){ return a.cat<b.cat?-1:a.cat>b.cat?1:(b.t1-a.t1); });
rows.sort(function(a,b){ return a.cat<b.cat?-1:a.cat>b.cat?1:((b.latest||0)-(a.latest||0)); });
}
// 动态表头:用实际日期替代 T-1
// 动态表头:客户分类 | 客户 | base | 各日期列 | DoD
var thead=document.querySelector('#tCustDetail thead');
thead.innerHTML='<tr>'
+'<th class="txt de-sort" data-k="cat" data-ty="str">客户分类<span class="arw"></span></th>'
var th='<tr><th class="txt de-sort" data-k="cat" data-ty="str">客户分类<span class="arw"></span></th>'
+'<th class="txt de-sort" data-k="acc" data-ty="str">客户<span class="arw"></span></th>'
+'<th class="de-sort" data-k="base" data-ty="num">base收入<span class="arw"></span></th>'
+'<th class="de-sort" data-k="t1" data-ty="num">'+(t1||'—')+'收入<span class="arw"></span></th>'
+'<th class="de-sort" data-k="dod" data-ty="num">DoD('+(t1||'—')+'−'+(t2||'—')+')<span class="arw"></span></th>'
+'</tr>';
+'<th class="de-sort" data-k="base" data-ty="num">base收入<span class="arw"></span></th>';
dts.forEach(function(dt){ th+='<th class="de-sort" data-k="'+dt+'" data-ty="num">'+dt+'<span class="arw"></span></th>'; });
th+='<th class="de-sort" data-k="dod" data-ty="num">DoD<span class="arw"></span></th></tr>';
thead.innerHTML=th;
var body=rows.map(function(r){
var s='<tr><td class="txt">'+r.cat+'</td><td class="txt">'+r.acc+'</td><td>'+n2(r.base)+'</td>';
dts.forEach(function(dt){ var v=r.byDt[dt]; s+='<td>'+(v==null?'':n2(v))+'</td>'; });
var dc=r.dod==null?'':(r.dod>=0?'pos':'neg');
return '<tr><td class="txt">'+r.cat+'</td><td class="txt">'+r.acc+'</td><td>'+n2(r.base)+'</td>'
+'<td>'+n2(r.t1)+'</td><td class="'+dc+'">'+(r.dod==null?'':n2(r.dod))+'</td></tr>';
s+='<td class="'+dc+'">'+(r.dod==null?'':n2(r.dod))+'</td></tr>';
return s;
}).join('');
document.querySelector('#tCustDetail tbody').innerHTML=body;
document.querySelectorAll('#tCustDetail th.de-sort').forEach(function(th){