feat(panel): customers stats strip with 3x-ui depletion semantics (round-2 #1)
GET /api/customers gains a summary object (total/online/depleted/ depletingSoon/disabled/enabled) computed server-side; the customers page renders it as a six-cell stats strip above the toolbar. Predicates aligned verbatim with 3x-ui client_paging.go: - depleted = (limit>0 && used>=limit) || (expiry set && expiry<=now), counted regardless of status; - depletingSoon = active && !depleted && (expiry within expireDiff days || remaining traffic < trafficDiff GB), thresholds from new settings expireDiff/trafficDiff with 3x-ui's factory default 0 (nobody counts until configured); - enabled = status active; disabled = manual suspensions only. Online reuses the existing 3-minute presence projection. Golden diff confined to the additive summary keys; summary_test.go covers every bucket with thresholds off (soon=0) and on (soon=2).
This commit is contained in:
+2
-1
@@ -210,6 +210,7 @@ tbody tr.drag-over td{border-top:2px solid var(--primary)}
|
||||
|
||||
/* ---------- 服务器页 ---------- */
|
||||
.stats{display:grid;grid-template-columns:repeat(3,1fr);background:var(--card);border:1px solid var(--border-soft);border-radius:var(--radius);margin-bottom:14px}
|
||||
.stats.six{grid-template-columns:repeat(6,1fr)}
|
||||
.stats .stat{padding:14px 22px;border:none;background:transparent}
|
||||
.stats .stat .lab{color:var(--text-2);font-size:12.5px;margin-bottom:3px}
|
||||
.stats .stat .val{display:flex;align-items:center;gap:8px;font-size:20px;font-weight:600;font-variant-numeric:tabular-nums}
|
||||
@@ -401,5 +402,5 @@ label>input:focus,label>select:focus,label>textarea:focus{border-color:var(--pri
|
||||
.toast{background:var(--pop-bg);border:1px solid var(--border);border-left:3px solid var(--green);color:var(--text);border-radius:8px;box-shadow:var(--pop-shadow);padding:9px 14px;font-size:13px;max-width:360px}
|
||||
.toast.error{border-left-color:var(--red)}
|
||||
|
||||
@media (max-width:1100px){.stat-grid{grid-template-columns:repeat(2,1fr)}.mid-grid{grid-template-columns:1fr}.info-bar{flex-direction:column;gap:14px}.info-bar .seg{padding:0}.info-bar .seg+.seg{border-left:none}.stats{grid-template-columns:repeat(2,1fr)}}
|
||||
@media (max-width:1100px){.stat-grid{grid-template-columns:repeat(2,1fr)}.mid-grid{grid-template-columns:1fr}.info-bar{flex-direction:column;gap:14px}.info-bar .seg{padding:0}.info-bar .seg+.seg{border-left:none}.stats{grid-template-columns:repeat(2,1fr)}.stats.six{grid-template-columns:repeat(3,1fr)}}
|
||||
@media (max-width:760px){.sider{display:none}.form-grid{grid-template-columns:1fr}.top-search{display:none}.content{padding:8px}}
|
||||
|
||||
Reference in New Issue
Block a user