中控总览改为后台常驻并行拉取热缓存,浏览器只读缓存并经 SSE 更新。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+88
-12
@@ -581,7 +581,7 @@
|
||||
if (dd.submitted_sheets != null) msg += " · " + dd.submitted_sheets + " 张";
|
||||
if (dd.fully_closed) msg += " · 已全平";
|
||||
alert(msg);
|
||||
await refresh();
|
||||
await requestOverviewRefresh();
|
||||
} else {
|
||||
alert((dd && dd.msg) || "平仓失败");
|
||||
btn.disabled = false;
|
||||
@@ -759,21 +759,34 @@
|
||||
return best;
|
||||
}
|
||||
|
||||
async function refresh() {
|
||||
let overviewVersion = 0;
|
||||
let overviewLoading = false;
|
||||
let overviewEventSource = null;
|
||||
let overviewReconnectTimer = null;
|
||||
|
||||
async function refresh(opts) {
|
||||
opts = opts || {};
|
||||
const silent = !!opts.silent;
|
||||
const status = document.getElementById("overview-status");
|
||||
const grid = document.getElementById("overview-grid");
|
||||
if (!grid) return;
|
||||
if (status) status.textContent = "刷新中…";
|
||||
if (overviewLoading) return;
|
||||
overviewLoading = true;
|
||||
if (status && !silent) status.textContent = "读取缓存…";
|
||||
try {
|
||||
const r = await fetch("/api/overview");
|
||||
// 只读中控热缓存;后台常驻并行 pull,不在此请求内打实例
|
||||
const r = await fetch("/api/overview", { credentials: "same-origin" });
|
||||
const d = await r.json();
|
||||
if (!d.ok) throw new Error(d.msg || "失败");
|
||||
if (!d.ok) throw new Error(d.msg || d.error || "失败");
|
||||
overviewVersion = Number(d.overview_version) || overviewVersion;
|
||||
const list = sortInstancesByMode(d.instances || []);
|
||||
renderOverviewSummary(list);
|
||||
if (global.HubQuotes && HubQuotes.paint) HubQuotes.paint(list);
|
||||
if (!list.length) {
|
||||
grid.innerHTML =
|
||||
'<p class="muted">暂无启用实例,请先到「实例」添加.</p>';
|
||||
d.pulling || !d.updated_at
|
||||
? '<p class="muted">后台首次拉取中…</p>'
|
||||
: '<p class="muted">暂无启用实例,请先到「实例」添加.</p>';
|
||||
} else if (isHubPhone()) {
|
||||
grid.innerHTML =
|
||||
'<div class="hub-phone-cards">' + list.map(renderPhoneCard).join("") + "</div>";
|
||||
@@ -790,15 +803,20 @@
|
||||
}
|
||||
if (status) {
|
||||
const s = summarizeOverview(list);
|
||||
const pulled = latestPulledAt(list);
|
||||
const pulled = latestPulledAt(list) || d.updated_at || "";
|
||||
const sec = Number(d.poll_interval_sec) || 10;
|
||||
const live = overviewEventSource && overviewEventSource.readyState === 1;
|
||||
status.textContent =
|
||||
"已更新 · 共 " +
|
||||
(live ? "SSE 已连接 · " : "") +
|
||||
"缓存 " +
|
||||
list.length +
|
||||
" 台 · 在线 " +
|
||||
s.all.online +
|
||||
" · 离线 " +
|
||||
s.all.offline +
|
||||
(pulled ? " · 拉取时间 " + pulled : "");
|
||||
(pulled ? " · " + pulled : "") +
|
||||
(d.pulling ? " · 后台拉取中" : " · 后台每 " + sec + "s 并行拉") +
|
||||
(d.error ? " · " + d.error : "");
|
||||
}
|
||||
} catch (e) {
|
||||
if (status) status.textContent = e.message || "加载失败";
|
||||
@@ -808,9 +826,60 @@
|
||||
box.innerHTML = "";
|
||||
}
|
||||
if (global.HubQuotes && HubQuotes.paint) HubQuotes.paint([]);
|
||||
} finally {
|
||||
overviewLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
function closeOverviewStream() {
|
||||
if (overviewEventSource) {
|
||||
overviewEventSource.close();
|
||||
overviewEventSource = null;
|
||||
}
|
||||
if (overviewReconnectTimer) {
|
||||
clearTimeout(overviewReconnectTimer);
|
||||
overviewReconnectTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
function connectOverviewStream() {
|
||||
const grid = document.getElementById("overview-grid");
|
||||
if (!grid || typeof EventSource === "undefined") return;
|
||||
closeOverviewStream();
|
||||
overviewEventSource = new EventSource("/api/overview/stream");
|
||||
overviewEventSource.addEventListener("overview", function (ev) {
|
||||
try {
|
||||
const st = JSON.parse(ev.data || "{}");
|
||||
const ver = Number(st.overview_version) || 0;
|
||||
if (ver && ver !== overviewVersion) {
|
||||
refresh({ silent: true });
|
||||
} else if (st.pulling) {
|
||||
const status = document.getElementById("overview-status");
|
||||
if (status) status.textContent = "后台并行拉取中…";
|
||||
}
|
||||
} catch (_) {}
|
||||
});
|
||||
overviewEventSource.onerror = function () {
|
||||
closeOverviewStream();
|
||||
const status = document.getElementById("overview-status");
|
||||
if (status) status.textContent = "SSE 断开,8s 后重连…";
|
||||
overviewReconnectTimer = setTimeout(function () {
|
||||
connectOverviewStream();
|
||||
refresh({ silent: true });
|
||||
}, 8000);
|
||||
};
|
||||
}
|
||||
|
||||
async function requestOverviewRefresh() {
|
||||
try {
|
||||
await fetch("/api/overview/refresh", {
|
||||
method: "POST",
|
||||
credentials: "same-origin",
|
||||
});
|
||||
} catch (_) {}
|
||||
refresh({ silent: true });
|
||||
}
|
||||
|
||||
const WEEKDAYS = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"];
|
||||
const LUNAR_MONTHS = ["正", "二", "三", "四", "五", "六", "七", "八", "九", "十", "冬", "腊"];
|
||||
const LUNAR_INFO = [
|
||||
@@ -1001,12 +1070,19 @@
|
||||
|
||||
function boot() {
|
||||
const btn = document.getElementById("btn-refresh");
|
||||
if (btn) btn.addEventListener("click", refresh);
|
||||
if (btn) btn.addEventListener("click", function () {
|
||||
requestOverviewRefresh();
|
||||
});
|
||||
tickBeijingClock();
|
||||
setInterval(tickBeijingClock, 1000);
|
||||
// 先进缓存秒开,再挂 SSE;不再前端定时现场拉实例
|
||||
refresh();
|
||||
setInterval(refresh, 10000);
|
||||
connectOverviewStream();
|
||||
}
|
||||
|
||||
global.HubOverview = { boot: boot, refresh: refresh };
|
||||
global.HubOverview = {
|
||||
boot: boot,
|
||||
refresh: refresh,
|
||||
requestRefresh: requestOverviewRefresh,
|
||||
};
|
||||
})(window);
|
||||
|
||||
Reference in New Issue
Block a user