feat: add telescope style
This commit is contained in:
+55
-24
@@ -13,7 +13,7 @@ const pageHTML = `<!doctype html>
|
||||
body { margin:0; font-family: -apple-system, "Segoe UI", Tahoma, sans-serif;
|
||||
background:#0E2347; color:#E8EEF7; }
|
||||
header { padding:14px 20px; background:#17345C; border-bottom:1px solid #1E5FA8;
|
||||
display:flex; align-items:center; gap:14px; position:sticky; top:0; }
|
||||
display:flex; align-items:center; gap:14px; position:sticky; top:0; z-index:2; }
|
||||
header h1 { font-size:17px; margin:0; font-weight:700; }
|
||||
header .dot { width:9px; height:9px; border-radius:50%; background:#4ade80; }
|
||||
header .meta { margin-inline-start:auto; font-size:12px; opacity:.7; }
|
||||
@@ -22,24 +22,34 @@ const pageHTML = `<!doctype html>
|
||||
.filters input, .filters select {
|
||||
background:#0b1c39; color:#E8EEF7; border:1px solid #1E5FA8;
|
||||
border-radius:8px; padding:7px 10px; font-size:13px; }
|
||||
.filters input { flex:1; min-width:160px; }
|
||||
.filters input { flex:1; min-width:140px; }
|
||||
.wrap { overflow-x:auto; }
|
||||
table { width:100%; border-collapse:collapse; font-size:13px; }
|
||||
th, td { padding:9px 12px; text-align:right; border-bottom:1px solid #16305a; white-space:nowrap; }
|
||||
th { position:sticky; top:52px; background:#122c52; font-weight:600; z-index:1; }
|
||||
tr:hover { background:#132b50; }
|
||||
th { background:#122c52; font-weight:600; border-bottom:2px solid #1E5FA8; }
|
||||
tr.row { cursor:pointer; }
|
||||
tr.row:hover { background:#132b50; }
|
||||
.m { font-weight:700; font-size:11px; padding:2px 7px; border-radius:6px; }
|
||||
.GET{background:#134e4a;color:#5eead4} .POST{background:#1e3a8a;color:#93c5fd}
|
||||
.PUT{background:#713f12;color:#fcd34d} .DELETE{background:#7f1d1d;color:#fca5a5}
|
||||
.st { font-weight:700; }
|
||||
.s2{color:#4ade80} .s3{color:#60a5fa} .s4{color:#fbbf24} .s5{color:#f87171}
|
||||
.path { font-family: ui-monospace, monospace; }
|
||||
.body { display:none; }
|
||||
tr.open .body { display:table-row; }
|
||||
.body td { background:#0b1c39; white-space:pre-wrap; font-family: ui-monospace, monospace;
|
||||
font-size:12px; color:#fca5a5; direction:ltr; text-align:left; }
|
||||
.phone { font-family: ui-monospace, monospace; color:#fcd34d; }
|
||||
.slow { color:#fbbf24; }
|
||||
.dim { opacity:.55; }
|
||||
.empty { padding:40px; text-align:center; opacity:.6; }
|
||||
.detail td { background:#0b1c39; padding:0; }
|
||||
.detail.hidden { display:none; }
|
||||
.panels { padding:12px 16px; display:grid; gap:12px; }
|
||||
.panel h4 { margin:0 0 6px; font-size:12px; color:#8ab4e8; font-weight:700;
|
||||
display:flex; align-items:center; gap:6px; }
|
||||
.panel pre { margin:0; background:#04101f; border:1px solid #16305a; border-radius:8px;
|
||||
padding:10px; font-family: ui-monospace, monospace; font-size:12px; line-height:1.6;
|
||||
color:#cbd5e1; white-space:pre-wrap; word-break:break-word; direction:ltr; text-align:left;
|
||||
max-height:280px; overflow:auto; }
|
||||
.kv { display:flex; gap:8px; flex-wrap:wrap; font-size:12px; margin-bottom:4px; }
|
||||
.kv b { color:#8ab4e8; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -51,6 +61,7 @@ const pageHTML = `<!doctype html>
|
||||
</header>
|
||||
<div class="filters">
|
||||
<input id="q" placeholder="جستجو در مسیر…">
|
||||
<input id="pf" placeholder="شماره موبایل…" inputmode="numeric">
|
||||
<select id="mf">
|
||||
<option value="">همه متدها</option>
|
||||
<option>GET</option><option>POST</option><option>PUT</option><option>DELETE</option>
|
||||
@@ -61,48 +72,68 @@ const pageHTML = `<!doctype html>
|
||||
<option value="4">4xx</option><option value="5">5xx</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="wrap">
|
||||
<table>
|
||||
<thead><tr>
|
||||
<th>زمان</th><th>متد</th><th>مسیر</th><th>وضعیت</th><th>مدت</th><th>حجم</th><th>IP</th>
|
||||
<th>زمان</th><th>متد</th><th>مسیر</th><th>وضعیت</th><th>مدت</th><th>موبایل</th><th>IP</th>
|
||||
</tr></thead>
|
||||
<tbody id="rows"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="empty" id="empty" style="display:none">درخواستی ثبت نشده است</div>
|
||||
<script>
|
||||
let data = [];
|
||||
let openId = null;
|
||||
const $ = s => document.querySelector(s);
|
||||
function fmtTime(t){ const d = new Date(t); return d.toLocaleTimeString('fa-IR'); }
|
||||
const esc = s => (s||'').replace(/&/g,'&').replace(/</g,'<');
|
||||
function fmtTime(t){ return new Date(t).toLocaleTimeString('fa-IR'); }
|
||||
function stClass(s){ return 's' + Math.floor(s/100); }
|
||||
|
||||
function detail(e){
|
||||
const sec = (title, body) => body
|
||||
? '<div class="panel"><h4>'+title+'</h4><pre>'+esc(body)+'</pre></div>' : '';
|
||||
const kv = '<div class="kv">'+
|
||||
(e.mobile ? '<span><b>موبایل:</b> '+esc(e.mobile)+'</span>' : '<span class="dim">کاربر مهمان</span>')+
|
||||
(e.user_id ? '<span><b>شناسه کاربر:</b> '+e.user_id+'</span>' : '')+
|
||||
'<span><b>حجم پاسخ:</b> '+e.bytes+' بایت</span>'+
|
||||
'<span><b>مدت:</b> '+e.duration_ms+'ms</span></div>';
|
||||
return '<div class="panels">'+kv+
|
||||
sec('هدرها', e.headers)+
|
||||
sec('Payload درخواست', e.req_body)+
|
||||
sec('پاسخ', e.resp_body)+
|
||||
'</div>';
|
||||
}
|
||||
|
||||
function render(){
|
||||
const q = $('#q').value.trim().toLowerCase();
|
||||
const pf = $('#pf').value.trim();
|
||||
const mf = $('#mf').value, sf = $('#sf').value;
|
||||
const rows = data.filter(e =>
|
||||
(!q || (e.path||'').toLowerCase().includes(q)) &&
|
||||
(!pf || (e.mobile||'').includes(pf)) &&
|
||||
(!mf || e.method === mf) &&
|
||||
(!sf || Math.floor((e.status||0)/100) == sf));
|
||||
const tb = $('#rows'); tb.innerHTML = '';
|
||||
$('#empty').style.display = rows.length ? 'none' : 'block';
|
||||
for(const e of rows){
|
||||
const tr = document.createElement('tr');
|
||||
tr.className = 'row';
|
||||
const slow = e.duration_ms > 500 ? ' slow' : '';
|
||||
tr.innerHTML =
|
||||
'<td class="dim">'+fmtTime(e.time)+'</td>'+
|
||||
'<td><span class="m '+e.method+'">'+e.method+'</span></td>'+
|
||||
'<td class="path">'+e.path+'</td>'+
|
||||
'<td class="path">'+esc(e.path)+'</td>'+
|
||||
'<td class="st '+stClass(e.status)+'">'+(e.status||'-')+'</td>'+
|
||||
'<td class="'+slow.trim()+'">'+e.duration_ms+'ms</td>'+
|
||||
'<td class="dim">'+e.bytes+'</td>'+
|
||||
'<td class="dim">'+(e.ip||'')+'</td>';
|
||||
if(e.body){
|
||||
tr.style.cursor='pointer';
|
||||
tr.onclick = () => { const b = tr.nextSibling; b.style.display = b.style.display==='table-row'?'none':'table-row'; };
|
||||
tb.appendChild(tr);
|
||||
const b = document.createElement('tr'); b.className='body';
|
||||
b.innerHTML = '<td colspan="7">'+e.body.replace(/</g,'<')+'</td>';
|
||||
b.style.display='none';
|
||||
tb.appendChild(b);
|
||||
} else {
|
||||
tb.appendChild(tr);
|
||||
'<td class="phone">'+esc(e.mobile||'')+'</td>'+
|
||||
'<td class="dim">'+esc(e.ip||'')+'</td>';
|
||||
tr.onclick = () => { openId = (openId===e.id ? null : e.id); render(); };
|
||||
tb.appendChild(tr);
|
||||
if(openId === e.id){
|
||||
const dr = document.createElement('tr');
|
||||
dr.className = 'detail';
|
||||
dr.innerHTML = '<td colspan="7">'+detail(e)+'</td>';
|
||||
tb.appendChild(dr);
|
||||
}
|
||||
}
|
||||
$('#meta').textContent = rows.length + ' / ' + data.length + ' درخواست';
|
||||
@@ -114,7 +145,7 @@ async function load(){
|
||||
render();
|
||||
}catch(e){ $('#meta').textContent = 'خطا در دریافت'; }
|
||||
}
|
||||
['q','mf','sf'].forEach(id => $('#'+id).addEventListener('input', render));
|
||||
['q','pf','mf','sf'].forEach(id => $('#'+id).addEventListener('input', render));
|
||||
load();
|
||||
setInterval(() => { if($('#live').checked) load(); }, 2000);
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user