383 lines
19 KiB
HTML
383 lines
19 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>DSP Routing Daily 报告</title>
|
||
<style>
|
||
@import url('https://fonts.googleapis.com/css2?family=DM+Sans:opsz,wght@9..40,400;9..40,500;9..40,600;9..40,700&family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap');
|
||
/* roasbloom Brand System v1.0 */
|
||
:root{
|
||
--rose:#C4506A;--rose-50:#FDF5F6;--rose-100:#F9E0E5;--rose-200:#F0C2CC;--rose-600:#A83D55;--rose-700:#8C2D43;
|
||
--ink:#2D1B20;--ink-80:#57484C;--ink-60:#827578;--ink-40:#ADA2A4;--ink-20:#D8D0D2;--ink-10:#EBE7E8;--ink-5:#F5F3F4;
|
||
--success:#4A9E6B;--warning:#D4914A;--danger:#C4506A;
|
||
--font-brand:'DM Sans',sans-serif;--font-body:'Inter','PingFang SC','Microsoft YaHei',sans-serif;--font-mono:'JetBrains Mono',monospace;
|
||
--bg:#F5F3F4;--card:#FFFFFF;--line:#EBE7E8;--head:#4E1222;--sub:#827578;--accent:#C4506A;--accent-bg:#FDF5F6;
|
||
}
|
||
*{box-sizing:border-box;}
|
||
body{margin:0;background:var(--bg);color:var(--ink);font-family:var(--font-body);font-size:13px;}
|
||
.wrap{max-width:1500px;margin:0 auto;padding:24px 20px 60px;}
|
||
h1{font-family:var(--font-brand);font-size:20px;font-weight:600;margin:0 0 4px;color:var(--ink);}
|
||
.meta{color:var(--ink-60);font-size:12px;margin-bottom:20px;}
|
||
.logo{font-family:var(--font-brand);font-weight:700;font-size:22px;letter-spacing:-.5px;display:inline-flex;align-items:center;margin-bottom:2px;}
|
||
.logo .roas{color:var(--rose);} .logo .bloom{color:var(--ink);}
|
||
.logo .ring{display:inline-block;width:.6em;height:.6em;border:.18em solid var(--rose);border-radius:50%;margin:0 .02em;position:relative;top:.05em;}
|
||
.card{background:var(--card);border:1px solid var(--ink-10);border-radius:10px;padding:16px 18px;margin-bottom:22px;box-shadow:0 1px 2px rgba(45,27,32,.04);}
|
||
.card h2{font-family:var(--font-brand);font-size:15px;font-weight:600;margin:0 0 12px;display:flex;align-items:center;gap:8px;color:var(--ink);}
|
||
.card h2 .tag{font-family:var(--font-body);font-size:11px;font-weight:normal;color:var(--ink-60);background:var(--ink-5);border:1px solid var(--ink-10);padding:1px 8px;border-radius:20px;}
|
||
table{border-collapse:collapse;width:100%;}
|
||
th,td{border:1px solid var(--ink-10);padding:7px 10px;text-align:right;white-space:nowrap;}
|
||
td{font-family:var(--font-mono);font-variant-numeric:tabular-nums;}
|
||
td.txt{font-family:var(--font-body);}
|
||
th{background:var(--head);color:#fff;font-weight:600;text-align:center;position:sticky;top:0;cursor:default;font-family:var(--font-brand);}
|
||
td:first-child,th:first-child{text-align:center;}
|
||
.detail-scroll{max-height:620px;overflow:auto;border:1px solid var(--ink-10);border-radius:8px;}
|
||
.detail-scroll table th{top:0;}
|
||
tbody tr:nth-child(even){background:var(--ink-5);}
|
||
tbody tr:hover{background:var(--accent-bg);}
|
||
.txt{text-align:left;}
|
||
.filters{display:flex;flex-wrap:wrap;gap:14px;align-items:flex-end;margin-bottom:14px;}
|
||
.fld{display:flex;flex-direction:column;gap:4px;}
|
||
.fld label{font-size:11px;color:var(--ink-60);}
|
||
.fld input,.fld select{border:1px solid var(--ink-20);border-radius:6px;padding:6px 8px;font-size:13px;min-width:150px;background:#fff;font-family:var(--font-body);}
|
||
.fld input[type=date]{min-width:140px;}
|
||
.btn{border:1px solid var(--rose);background:var(--rose);color:#fff;border-radius:6px;padding:7px 16px;font-size:13px;cursor:pointer;font-family:var(--font-brand);font-weight:500;}
|
||
.btn.ghost{background:#fff;color:var(--rose);}
|
||
.count-note{color:var(--ink-60);font-size:12px;margin:6px 2px 0;}
|
||
.pos{color:var(--success);} .neg{color:var(--danger);}
|
||
.empty{padding:30px;text-align:center;color:var(--ink-60);}
|
||
.sum-metric{color:var(--rose);font-weight:600;}
|
||
.note-list{margin:0 0 14px;padding:10px 14px 10px 28px;background:var(--ink-5);border:1px solid var(--ink-10);border-radius:8px;color:var(--ink-80);font-size:12px;line-height:1.9;}
|
||
.note-list b{color:var(--ink);}
|
||
.total-row td{position:sticky;top:33px;background:var(--rose-50);font-weight:700;color:var(--ink);border-top:2px solid var(--rose-200);z-index:1;}
|
||
.total-row td.txt{text-align:left;}
|
||
th.sortable,th.fsortable{cursor:pointer;user-select:none;}
|
||
th.sortable:hover,th.fsortable:hover{background:var(--rose-700);}
|
||
th .arrow{display:inline-block;width:10px;margin-left:3px;font-size:10px;color:var(--rose-200);}
|
||
th.sort-asc .arrow::after{content:"\25B2";}
|
||
th.sort-desc .arrow::after{content:"\25BC";}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="wrap">
|
||
<div class="logo"><span class="roas">roas</span><span class="bloom">bl</span><span class="ring"></span><span class="bloom">m</span></div>
|
||
<h1>DSP Routing Daily 报告</h1>
|
||
<div class="meta" id="meta">加载中…</div>
|
||
|
||
<div class="card">
|
||
<h2>每日总表 <span class="tag">保留近3天</span></h2>
|
||
<ul class="note-list">
|
||
<li><b>日期</b>:查询当日,有点击订单等数据来自前一天</li>
|
||
<li><b>原始订单</b>:全部可融合订单【DSP Campaign(可融合)】</li>
|
||
<li><b>pkg</b>:原始订单按照 pkg+geo 去重后的订单数</li>
|
||
<li><b>中转订单</b>:已 link 订单【Order List】</li>
|
||
</ul>
|
||
<div style="overflow:auto;">
|
||
<table id="summaryTable">
|
||
<thead>
|
||
<tr>
|
||
<th>日期</th><th>原始订单</th><th>pkg</th><th>中转订单</th>
|
||
<th>有点击订单</th><th>无转化订单</th><th>无点击订单</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<h2>Kibana 监测明细表 <span class="tag">按 adping_id 聚合 · 近14天</span></h2>
|
||
<div class="filters">
|
||
<div class="fld"><label>dt 起</label><input type="date" id="fDtFrom"></div>
|
||
<div class="fld"><label>dt 止</label><input type="date" id="fDtTo"></div>
|
||
<div class="fld"><label>融合后campaign id (routing_campaign_id)</label><input type="text" id="fRc" placeholder="模糊匹配"></div>
|
||
<div class="fld"><label>融合前campaign id (campaign_id)</label><input type="text" id="fCid" placeholder="模糊匹配"></div>
|
||
<div class="fld"><label>pkg</label><input type="text" id="fPkg" placeholder="模糊匹配"></div>
|
||
<div class="fld"><label>second_channel</label><select id="fSc"><option value="">全部</option></select></div>
|
||
<div class="fld"><label>account</label><select id="fAcc"><option value="">全部</option></select></div>
|
||
<button class="btn" id="applyBtn">筛选</button>
|
||
<button class="btn ghost" id="resetBtn">重置</button>
|
||
</div>
|
||
<div class="count-note" id="detailNote"></div>
|
||
<div class="detail-scroll">
|
||
<table id="detailTable">
|
||
<thead>
|
||
<tr>
|
||
<th class="sortable" data-key="dt" data-type="dt">dt<span class="arrow"></span></th>
|
||
<th class="sortable" data-key="routing_campaign_id" data-type="str">融合后campaign id<span class="arrow"></span></th>
|
||
<th class="sortable" data-key="campaign_id" data-type="str">融合前campaign id<span class="arrow"></span></th>
|
||
<th class="sortable" data-key="adping_id" data-type="num">adping_id<span class="arrow"></span></th>
|
||
<th class="sortable" data-key="second_channel" data-type="str">second_channel<span class="arrow"></span></th>
|
||
<th class="sortable" data-key="account" data-type="str">account<span class="arrow"></span></th>
|
||
<th class="sortable" data-key="pkg" data-type="str">pkg<span class="arrow"></span></th>
|
||
<th class="sortable" data-key="clicks" data-type="num">clicks<span class="arrow"></span></th>
|
||
<th class="sortable" data-key="conversion" data-type="num">conversion<span class="arrow"></span></th>
|
||
<th class="sortable" data-key="cvr" data-type="num">cvr<span class="arrow"></span></th>
|
||
<th class="sortable" data-key="revenue" data-type="num">revenue<span class="arrow"></span></th>
|
||
<th class="sortable" data-key="cost" data-type="num">cost<span class="arrow"></span></th>
|
||
<th class="sortable" data-key="gp" data-type="num">GP<span class="arrow"></span></th>
|
||
<th class="sortable" data-key="ecpc" data-type="num">ecpc<span class="arrow"></span></th>
|
||
</tr>
|
||
<tr id="totalRow" class="total-row">
|
||
<td colspan="7" class="txt">Total</td>
|
||
<td id="tClicks"></td>
|
||
<td id="tConv"></td>
|
||
<td id="tCvr"></td>
|
||
<td id="tRev"></td>
|
||
<td id="tCost"></td>
|
||
<td id="tGp"></td>
|
||
<td id="tEcpc"></td>
|
||
</tr>
|
||
</thead>
|
||
<tbody></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<h2>骗量排行榜 <span class="tag">前一天 · 按 account · 占比降序</span></h2>
|
||
<ul class="note-list">
|
||
<li>按 <b>campaign_id</b> 重新聚合(同 campaign_id 的所有 adping_id 的 clicks、conversion 累加)</li>
|
||
<li><b>可疑 campaign</b>:clicks 总数 > 500 且 conversion = 0 的 campaign</li>
|
||
<li><b>骗量占比</b> = 可疑 campaign 数 / campaign 总数,占比越高越靠前</li>
|
||
</ul>
|
||
<div style="overflow:auto;">
|
||
<table id="fraudTable">
|
||
<thead>
|
||
<tr>
|
||
<th>排名</th>
|
||
<th class="txt fsortable" data-key="account" data-type="str">account<span class="arrow"></span></th>
|
||
<th class="fsortable" data-key="total" data-type="num">campaign 总数<span class="arrow"></span></th>
|
||
<th class="fsortable" data-key="suspicious" data-type="num">可疑 campaign 数<span class="arrow"></span><br><span style="font-weight:normal;font-size:11px;">(>500点击 & 0转化)</span></th>
|
||
<th class="fsortable" data-key="ratio" data-type="num">骗量占比<span class="arrow"></span></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody></tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
var ALL = []; // 全部明细
|
||
var SUMMARY = [];
|
||
|
||
function num(n){ return (n==null||n==='')?'':Number(n).toLocaleString('en-US'); }
|
||
function pct(n){ if(n==null||n===''||isNaN(n)) return ''; return (Number(n)*100).toFixed(2)+'%'; }
|
||
function money(n){ if(n==null||n===''||isNaN(n)) return ''; return Number(n).toLocaleString('en-US',{minimumFractionDigits:2,maximumFractionDigits:2}); }
|
||
function dec(n,d){ if(n==null||n===''||isNaN(n)) return ''; return Number(n).toFixed(d==null?4:d); }
|
||
function dtKey(s){ // "2026/9/9" -> 20260909 数值,便于比较
|
||
var p=String(s).split('/'); return parseInt(p[0])*10000+parseInt(p[1])*100+parseInt(p[2]);
|
||
}
|
||
function dtToInput(s){ var p=String(s).split('/'); return p[0]+'-'+String(p[1]).padStart(2,'0')+'-'+String(p[2]).padStart(2,'0'); }
|
||
function inputToKey(v){ if(!v) return null; var p=v.split('-'); return parseInt(p[0])*10000+parseInt(p[1])*100+parseInt(p[2]); }
|
||
|
||
function renderSummary(){
|
||
var tb=document.querySelector('#summaryTable tbody');
|
||
if(!SUMMARY.length){ tb.innerHTML='<tr><td colspan="7" class="empty">暂无数据</td></tr>'; return; }
|
||
tb.innerHTML = SUMMARY.slice().sort(function(a,b){return dtKey(b.date)-dtKey(a.date);}).map(function(r){
|
||
return '<tr><td>'+r.date+'</td>'
|
||
+'<td class="sum-metric">'+num(r.raw_orders)+'</td>'
|
||
+'<td>'+num(r.pkg)+'</td>'
|
||
+'<td class="sum-metric">'+num(r.relay_orders)+'</td>'
|
||
+'<td>'+num(r.has_click_orders)+'</td>'
|
||
+'<td>'+num(r.no_conv_orders)+'</td>'
|
||
+'<td>'+num(r.no_click_orders)+'</td></tr>';
|
||
}).join('');
|
||
}
|
||
|
||
function fillSelects(){
|
||
var scSet={}, accSet={};
|
||
ALL.forEach(function(d){ if(d.second_channel) scSet[d.second_channel]=1; if(d.account) accSet[d.account]=1; });
|
||
var scSel=document.getElementById('fSc'), accSel=document.getElementById('fAcc');
|
||
Object.keys(scSet).sort().forEach(function(v){ scSel.insertAdjacentHTML('beforeend','<option>'+v+'</option>'); });
|
||
Object.keys(accSet).sort().forEach(function(v){ accSel.insertAdjacentHTML('beforeend','<option>'+v+'</option>'); });
|
||
}
|
||
|
||
function currentFilter(d){
|
||
var from=inputToKey(document.getElementById('fDtFrom').value);
|
||
var to=inputToKey(document.getElementById('fDtTo').value);
|
||
var rc=document.getElementById('fRc').value.trim().toLowerCase();
|
||
var cid=document.getElementById('fCid').value.trim().toLowerCase();
|
||
var pkg=document.getElementById('fPkg').value.trim().toLowerCase();
|
||
var sc=document.getElementById('fSc').value;
|
||
var acc=document.getElementById('fAcc').value;
|
||
var k=dtKey(d.dt);
|
||
if(from!=null && k<from) return false;
|
||
if(to!=null && k>to) return false;
|
||
if(rc && String(d.routing_campaign_id).toLowerCase().indexOf(rc)<0) return false;
|
||
if(cid && String(d.campaign_id).toLowerCase().indexOf(cid)<0) return false;
|
||
if(pkg && String(d.pkg).toLowerCase().indexOf(pkg)<0) return false;
|
||
if(sc && d.second_channel!==sc) return false;
|
||
if(acc && d.account!==acc) return false;
|
||
return true;
|
||
}
|
||
|
||
var SORT_KEY='clicks', SORT_TYPE='num', SORT_DIR='desc';
|
||
|
||
function sortRows(rows){
|
||
var k=SORT_KEY, type=SORT_TYPE, dir=(SORT_DIR==='asc'?1:-1);
|
||
return rows.sort(function(a,b){
|
||
var va,vb;
|
||
if(type==='dt'){ va=dtKey(a.dt); vb=dtKey(b.dt); }
|
||
else if(type==='num'){ va=Number(a[k])||0; vb=Number(b[k])||0; }
|
||
else { va=String(a[k]||''); vb=String(b[k]||''); return dir*va.localeCompare(vb); }
|
||
if(va<vb) return -1*dir; if(va>vb) return 1*dir;
|
||
// 次级排序:dt 降序、clicks 降序,保持稳定观感
|
||
return dtKey(b.dt)-dtKey(a.dt) || (b.clicks-a.clicks);
|
||
});
|
||
}
|
||
|
||
function updateSortArrows(){
|
||
document.querySelectorAll('#detailTable th.sortable').forEach(function(th){
|
||
th.classList.remove('sort-asc','sort-desc');
|
||
if(th.getAttribute('data-key')===SORT_KEY){
|
||
th.classList.add(SORT_DIR==='asc'?'sort-asc':'sort-desc');
|
||
}
|
||
});
|
||
}
|
||
|
||
function renderDetail(){
|
||
var rows = sortRows(ALL.filter(currentFilter));
|
||
var tb=document.querySelector('#detailTable tbody');
|
||
if(!rows.length){ tb.innerHTML='<tr><td colspan="14" class="empty">无匹配数据</td></tr>'; }
|
||
else {
|
||
tb.innerHTML = rows.map(function(d){
|
||
var gpCls = (d.gp>=0)?'pos':'neg';
|
||
return '<tr>'
|
||
+'<td>'+d.dt+'</td>'
|
||
+'<td>'+d.routing_campaign_id+'</td>'
|
||
+'<td>'+d.campaign_id+'</td>'
|
||
+'<td>'+d.adping_id+'</td>'
|
||
+'<td class="txt">'+d.second_channel+'</td>'
|
||
+'<td class="txt">'+d.account+'</td>'
|
||
+'<td class="txt">'+d.pkg+'</td>'
|
||
+'<td>'+num(d.clicks)+'</td>'
|
||
+'<td>'+num(d.conversion)+'</td>'
|
||
+'<td>'+pct(d.cvr)+'</td>'
|
||
+'<td>'+money(d.revenue)+'</td>'
|
||
+'<td>'+money(d.cost)+'</td>'
|
||
+'<td class="'+gpCls+'">'+pct(d.gp)+'</td>'
|
||
+'<td>'+dec(d.ecpc,4)+'</td>'
|
||
+'</tr>';
|
||
}).join('');
|
||
}
|
||
document.getElementById('detailNote').textContent='显示 '+rows.length+' / '+ALL.length+' 行';
|
||
renderTotal(rows);
|
||
updateSortArrows();
|
||
}
|
||
|
||
var FRAUD = [];
|
||
var FSORT_KEY='ratio', FSORT_TYPE='num', FSORT_DIR='desc';
|
||
|
||
function computeFraud(){
|
||
FRAUD=[];
|
||
if(!ALL.length) return;
|
||
var maxDt = ALL.reduce(function(m,d){ return dtKey(d.dt)>dtKey(m)?d.dt:m; }, ALL[0].dt);
|
||
var rows = ALL.filter(function(d){ return d.dt===maxDt; });
|
||
var byAcc = {};
|
||
rows.forEach(function(d){
|
||
var acc=d.account||'未知', cid=String(d.campaign_id||'');
|
||
if(!byAcc[acc]) byAcc[acc]={};
|
||
if(!byAcc[acc][cid]) byAcc[acc][cid]={clicks:0,conv:0};
|
||
byAcc[acc][cid].clicks += (+d.clicks||0);
|
||
byAcc[acc][cid].conv += (+d.conversion||0);
|
||
});
|
||
FRAUD = Object.keys(byAcc).map(function(acc){
|
||
var camps = byAcc[acc], ids = Object.keys(camps);
|
||
var total = ids.length;
|
||
var suspicious = ids.filter(function(cid){ return camps[cid].clicks>500 && camps[cid].conv===0; }).length;
|
||
return { account:acc, total:total, suspicious:suspicious, ratio: total? suspicious/total : 0 };
|
||
});
|
||
}
|
||
|
||
function renderFraud(){
|
||
var dir=(FSORT_DIR==='asc'?1:-1), k=FSORT_KEY, type=FSORT_TYPE;
|
||
var list = FRAUD.slice().sort(function(a,b){
|
||
if(type==='str'){ return dir*String(a[k]).localeCompare(String(b[k])); }
|
||
var va=Number(a[k])||0, vb=Number(b[k])||0;
|
||
return (va<vb?-1:va>vb?1:0)*dir || b.ratio-a.ratio || b.suspicious-a.suspicious;
|
||
});
|
||
var tb=document.querySelector('#fraudTable tbody');
|
||
if(!list.length){ tb.innerHTML='<tr><td colspan="5" class="empty">暂无数据</td></tr>'; }
|
||
else {
|
||
tb.innerHTML = list.map(function(r,i){
|
||
var hot = r.ratio>=0.5 ? 'neg' : '';
|
||
return '<tr>'
|
||
+'<td>'+(i+1)+'</td>'
|
||
+'<td class="txt">'+r.account+'</td>'
|
||
+'<td>'+num(r.total)+'</td>'
|
||
+'<td>'+num(r.suspicious)+'</td>'
|
||
+'<td class="'+hot+'">'+pct(r.ratio)+'</td>'
|
||
+'</tr>';
|
||
}).join('');
|
||
}
|
||
document.querySelectorAll('#fraudTable th.fsortable').forEach(function(th){
|
||
th.classList.remove('sort-asc','sort-desc');
|
||
if(th.getAttribute('data-key')===FSORT_KEY) th.classList.add(FSORT_DIR==='asc'?'sort-asc':'sort-desc');
|
||
});
|
||
}
|
||
|
||
function renderTotal(rows){
|
||
var c=0,cv=0,rev=0,cost=0;
|
||
rows.forEach(function(d){ c+=(+d.clicks||0); cv+=(+d.conversion||0); rev+=(+d.revenue||0); cost+=(+d.cost||0); });
|
||
var cvr = c? cv/c : 0;
|
||
var gp = rev? 1-cost/rev : 0;
|
||
var ecpc= c? rev/c : 0;
|
||
document.getElementById('tClicks').textContent=num(c);
|
||
document.getElementById('tConv').textContent=num(cv);
|
||
document.getElementById('tCvr').textContent=pct(cvr);
|
||
document.getElementById('tRev').textContent=money(rev);
|
||
document.getElementById('tCost').textContent=money(cost);
|
||
var tGp=document.getElementById('tGp');
|
||
tGp.textContent=pct(gp); tGp.className=(gp>=0)?'pos':'neg';
|
||
document.getElementById('tEcpc').textContent=dec(ecpc,4);
|
||
}
|
||
|
||
document.querySelectorAll('#detailTable th.sortable').forEach(function(th){
|
||
th.addEventListener('click',function(){
|
||
var k=th.getAttribute('data-key'), type=th.getAttribute('data-type');
|
||
if(SORT_KEY===k){ SORT_DIR=(SORT_DIR==='asc'?'desc':'asc'); }
|
||
else { SORT_KEY=k; SORT_TYPE=type; SORT_DIR='desc'; }
|
||
renderDetail();
|
||
});
|
||
});
|
||
|
||
document.querySelectorAll('#fraudTable th.fsortable').forEach(function(th){
|
||
th.addEventListener('click',function(){
|
||
var key=th.getAttribute('data-key'), type=th.getAttribute('data-type');
|
||
if(FSORT_KEY===key){ FSORT_DIR=(FSORT_DIR==='asc'?'desc':'asc'); }
|
||
else { FSORT_KEY=key; FSORT_TYPE=type; FSORT_DIR='desc'; }
|
||
renderFraud();
|
||
});
|
||
});
|
||
|
||
document.getElementById('applyBtn').onclick=renderDetail;
|
||
document.getElementById('resetBtn').onclick=function(){
|
||
['fDtFrom','fDtTo','fRc','fCid','fPkg'].forEach(function(id){document.getElementById(id).value='';});
|
||
document.getElementById('fSc').value=''; document.getElementById('fAcc').value='';
|
||
renderDetail();
|
||
};
|
||
|
||
fetch('data.json?_='+Date.now()).then(function(r){return r.json();}).then(function(d){
|
||
SUMMARY = d.summary||[];
|
||
ALL = d.detail||[];
|
||
document.getElementById('meta').textContent='数据更新时间:'+(d.updated_at||'—')+' | 总表 '+SUMMARY.length+' 天 | 明细 '+ALL.length+' 行';
|
||
renderSummary();
|
||
computeFraud();
|
||
renderFraud();
|
||
fillSelects();
|
||
// 默认 dt 范围 = 数据里的最早~最新
|
||
if(ALL.length){
|
||
var ks=ALL.map(function(x){return x.dt;}).sort(function(a,b){return dtKey(a)-dtKey(b);});
|
||
document.getElementById('fDtFrom').value=dtToInput(ks[0]);
|
||
document.getElementById('fDtTo').value=dtToInput(ks[ks.length-1]);
|
||
}
|
||
renderDetail();
|
||
}).catch(function(e){
|
||
document.getElementById('meta').textContent='数据加载失败:'+e;
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|