mirror of
https://gitcode.com/JianFeeeee/ModelRouter.git
synced 2026-09-20 00:48:00 +00:00
fix(ui): active request card -> real-time QPS-style sparkline; remove pie slice outline
- drawActiveChart: rolling request-rate buffer (window.qpsSeries) sampled ~every 2s from records timestamps, drawn as an area+fine; header shows current active requests + 'reqs / 3s' subtitle; refreshes on the existing 3s paintStats poll - status pie: remove the hard slice outline (no stroke), clean seamless slices - active card now consistent with others: big value + subtitle + chart - deployed (bak .bak.20260811n), server active
This commit is contained in:
@ -767,7 +767,9 @@ async function paintStats() {
|
||||
const kpi = $('#kpi-row');
|
||||
if (kpi) kpi.innerHTML = `
|
||||
<div class="kpi${statsKeyF ? ' kpi-exit' : ''}" title="${statsKeyF ? t('exitKeyView') : ''}" onclick="${statsKeyF ? `renderKeyF('${escAttr(statsKeyF)}')` : ''}">
|
||||
<div class="k-hdr"><span class="k-lab">${t('kpiActive')} <span class="dot"></span></span><span class="okc k-sub">${st.active || 0}</span></div>
|
||||
<div class="k-hdr"><span class="k-lab">${t('kpiActive')} <span class="dot"></span></span></div>
|
||||
<div class="k-val">${st.active || 0}</div>
|
||||
<div class="k-sub">reqs / 3s</div>
|
||||
<div class="k-chart"><canvas id="cv-active"></canvas></div>
|
||||
</div>
|
||||
<div class="kpi">
|
||||
@ -871,17 +873,37 @@ function dailyBars(ctx, counts, w, h, color) {
|
||||
});
|
||||
}
|
||||
|
||||
// rolling real-time request-rate buffer (QPS-style sparkline)
|
||||
window.qpsSeries = window.qpsSeries || { last: Date.now(), data: [] };
|
||||
function drawActiveChart(st) {
|
||||
const cv = document.getElementById('cv-active'); if (!cv) return;
|
||||
const ctx = cv.getContext('2d');
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
const parent = cv.parentElement, rect = parent.getBoundingClientRect();
|
||||
cv.width = rect.width * dpr; cv.height = 86 * dpr; ctx.scale(dpr, dpr);
|
||||
const H = 86;
|
||||
cv.width = rect.width * dpr; cv.height = H * dpr; ctx.scale(dpr, dpr);
|
||||
ctx.clearRect(0, 0, cv.width, cv.height);
|
||||
// active count across records window (bucketed per minute) as a sparkline
|
||||
// sample: requests started in the last 3s (approx QPS). Records carry unix-ms time.
|
||||
const qs = window.qpsSeries;
|
||||
const now = Date.now();
|
||||
const recs = st.records || [];
|
||||
const col = window.charts && window.charts.active || [];
|
||||
sparkline(ctx, col, rect.width, 86, '#4fa3d8');
|
||||
const rate = recs.filter(r => r.time >= now - 3000).length;
|
||||
if (now - qs.last >= 2000) { qs.last = now; qs.data.push(Math.max(rate, 0)); }
|
||||
if (qs.data.length > 40) qs.data.shift();
|
||||
// grid baseline
|
||||
ctx.strokeStyle = 'var(--line)'; ctx.lineWidth = 1; ctx.beginPath();
|
||||
ctx.moveTo(0, H - 1); ctx.lineTo(rect.width, H - 1); ctx.stroke();
|
||||
// area fill under the line
|
||||
if (qs.data.length > 1) {
|
||||
const max = Math.max.apply(null, qs.data) || 1;
|
||||
const px = i => i / (qs.data.length - 1) * rect.width;
|
||||
const py = v => 2 + (1 - v / max) * (H - 4);
|
||||
ctx.beginPath();
|
||||
qs.data.forEach((v, i) => i ? ctx.lineTo(px(i), py(v)) : ctx.moveTo(px(i), py(v)));
|
||||
ctx.lineTo(rect.width, H); ctx.lineTo(px(0), H); ctx.closePath();
|
||||
ctx.fillStyle = 'rgba(79,163,216,.18)'; ctx.fill();
|
||||
sparkline(ctx, qs.data, rect.width, H, '#4fa3d8');
|
||||
}
|
||||
}
|
||||
function drawReqsChart(st) {
|
||||
const cv = document.getElementById('cv-reqs'); if (!cv) return;
|
||||
@ -955,7 +977,6 @@ function drawStatusChart(rows) {
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = palette[i % palette.length];
|
||||
ctx.fill();
|
||||
ctx.strokeStyle = 'var(--card)'; ctx.lineWidth = 1.5; ctx.stroke();
|
||||
a0 += sweep;
|
||||
});
|
||||
// center label = total
|
||||
|
||||
Reference in New Issue
Block a user