feat: add min hours-to-expiry filter on ops map (8-48h step 2)
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Vendored
+26
-3
@@ -34,7 +34,7 @@
|
||||
.seg button { appearance: none; border: 0; background: transparent; color: var(--muted); padding: 0.45rem 0.85rem; cursor: pointer; }
|
||||
.seg button.active { background: #1e2a3a; color: var(--text); }
|
||||
.date-field { color: var(--muted); font-size: 0.85rem; display: inline-flex; gap: 0.5rem; align-items: center; }
|
||||
.date-field input { background: var(--panel); border: 1px solid #243041; color: var(--text); border-radius: 6px; padding: 0.35rem 0.5rem; }
|
||||
.date-field input, .date-field select { background: var(--panel); border: 1px solid #243041; color: var(--text); border-radius: 6px; padding: 0.35rem 0.5rem; }
|
||||
.chart-wrap { background: var(--panel); border: 1px solid #243041; border-radius: 10px; padding: 0.75rem 0.5rem; margin-top: 0.5rem; }
|
||||
.chart-title { padding: 0 0.75rem 0.25rem; color: var(--muted); font-size: 0.85rem; }
|
||||
.chart-svg { width: 100%; height: auto; display: block; }
|
||||
@@ -123,6 +123,9 @@
|
||||
<button type="button" data-side="P">Put</button>
|
||||
</div>
|
||||
<label class="date-field">锚点日 <input type="date" id="anchorDate" /></label>
|
||||
<label class="date-field">距离到期 ≥
|
||||
<select id="minHours" aria-label="距离到期最小小时数"></select>
|
||||
</label>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="tile"><div class="label">范围</div><div class="value" id="opsRange" style="font-size:1.05rem">—</div><div class="hint" id="opsSamples">—</div></div>
|
||||
@@ -211,7 +214,7 @@
|
||||
</div>
|
||||
<script>
|
||||
const TOKEN_KEY = "mi_token";
|
||||
const state = { page: "dash", range: "day", side: "both", moveMode: "abs", lastMov: null, authRequired: false, loadedUsername: "admin" };
|
||||
const state = { page: "dash", range: "day", side: "both", moveMode: "abs", minHours: 12, lastMov: null, authRequired: false, loadedUsername: "admin" };
|
||||
function getToken() { try { return localStorage.getItem(TOKEN_KEY); } catch { return null; } }
|
||||
function setToken(t) { try { if (t) localStorage.setItem(TOKEN_KEY, t); else localStorage.removeItem(TOKEN_KEY); } catch {} }
|
||||
function authHeaders() {
|
||||
@@ -233,6 +236,16 @@
|
||||
return d.getFullYear() + "-" + String(d.getMonth()+1).padStart(2,"0") + "-" + String(d.getDate()).padStart(2,"0");
|
||||
}
|
||||
document.getElementById("anchorDate").value = todayYmd();
|
||||
(function initMinHours() {
|
||||
const sel = document.getElementById("minHours");
|
||||
for (let h = 8; h <= 48; h += 2) {
|
||||
const opt = document.createElement("option");
|
||||
opt.value = String(h);
|
||||
opt.textContent = h + "h";
|
||||
if (h === state.minHours) opt.selected = true;
|
||||
sel.appendChild(opt);
|
||||
}
|
||||
})();
|
||||
|
||||
function showLogin() {
|
||||
document.getElementById("page-login").classList.remove("hidden");
|
||||
@@ -540,6 +553,10 @@
|
||||
}
|
||||
}));
|
||||
document.getElementById("anchorDate").addEventListener("change", loadOps);
|
||||
document.getElementById("minHours").addEventListener("change", () => {
|
||||
state.minHours = Number(document.getElementById("minHours").value) || 12;
|
||||
loadOps();
|
||||
});
|
||||
|
||||
function drawChart(buckets, minLev) {
|
||||
const svg = document.getElementById("levChart");
|
||||
@@ -681,7 +698,13 @@
|
||||
|
||||
async function loadOps() {
|
||||
const date = document.getElementById("anchorDate").value;
|
||||
const q = new URLSearchParams({ range: state.range, side: state.side, bucket_minutes: "60", date });
|
||||
const q = new URLSearchParams({
|
||||
range: state.range,
|
||||
side: state.side,
|
||||
bucket_minutes: "60",
|
||||
date,
|
||||
min_hours: String(state.minHours || 12),
|
||||
});
|
||||
try {
|
||||
const d = await apiFetch("/api/stats/ops-map?" + q).then(r => {
|
||||
if (!r.ok) throw new Error("ops-map " + r.status);
|
||||
|
||||
Reference in New Issue
Block a user