Show visible SSE live status with last update time on dashboard.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,7 +1,10 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% block title %}总览 · {{ app_name }}{% endblock %}
|
{% block title %}总览 · {{ app_name }}{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2 style="margin:0 0 14px">总览 <span class="muted" style="font-size:13px;font-weight:400" id="live-hint">· 秒级推送</span></h2>
|
<h2 style="margin:0 0 14px;display:flex;align-items:center;gap:10px;flex-wrap:wrap">
|
||||||
|
总览
|
||||||
|
<span id="live-hint" class="tag" style="font-size:12px;font-weight:500">连接中…</span>
|
||||||
|
</h2>
|
||||||
<div class="grid cols-3">
|
<div class="grid cols-3">
|
||||||
<div class="card"><div class="muted">账户</div><div class="stat">{{ accounts|length }}</div></div>
|
<div class="card"><div class="muted">账户</div><div class="stat">{{ accounts|length }}</div></div>
|
||||||
<div class="card">
|
<div class="card">
|
||||||
@@ -78,6 +81,29 @@
|
|||||||
var liveUrl = {{ url_for('dashboard_live')|tojson }};
|
var liveUrl = {{ url_for('dashboard_live')|tojson }};
|
||||||
var detailBase = {{ url_for('record_detail', tid=0)|tojson }}.replace(/0$/, '');
|
var detailBase = {{ url_for('record_detail', tid=0)|tojson }}.replace(/0$/, '');
|
||||||
var hint = document.getElementById('live-hint');
|
var hint = document.getElementById('live-hint');
|
||||||
|
var mode = 'connecting';
|
||||||
|
var pollStarted = false;
|
||||||
|
var msgCount = 0;
|
||||||
|
|
||||||
|
function pad(n) { return n < 10 ? '0' + n : String(n); }
|
||||||
|
function nowClock() {
|
||||||
|
var d = new Date();
|
||||||
|
return pad(d.getHours()) + ':' + pad(d.getMinutes()) + ':' + pad(d.getSeconds());
|
||||||
|
}
|
||||||
|
|
||||||
|
function setHint(text, kind) {
|
||||||
|
if (!hint) return;
|
||||||
|
hint.textContent = text;
|
||||||
|
hint.className = 'tag' + (kind ? ' ' + kind : '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function markUpdated() {
|
||||||
|
if (mode === 'sse') {
|
||||||
|
setHint('SSE · 更新于 ' + nowClock(), 'green');
|
||||||
|
} else if (mode === 'poll') {
|
||||||
|
setHint('轮询 1s · 更新于 ' + nowClock(), '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function esc(s) {
|
function esc(s) {
|
||||||
return String(s == null ? '' : s)
|
return String(s == null ? '' : s)
|
||||||
@@ -136,30 +162,46 @@
|
|||||||
document.getElementById('stat-online').textContent = String(data.online_count);
|
document.getElementById('stat-online').textContent = String(data.online_count);
|
||||||
renderMachines(data.online_machines || []);
|
renderMachines(data.online_machines || []);
|
||||||
renderTrades(data.trades || []);
|
renderTrades(data.trades || []);
|
||||||
|
markUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
function startPollFallback() {
|
function startPollFallback(reason) {
|
||||||
if (hint) hint.textContent = '· 轮询 1s(SSE 不可用)';
|
if (pollStarted) return;
|
||||||
|
pollStarted = true;
|
||||||
|
mode = 'poll';
|
||||||
|
setHint(reason || '轮询 1s(SSE 不可用)', '');
|
||||||
setInterval(function () {
|
setInterval(function () {
|
||||||
fetch(liveUrl, { credentials: 'same-origin' })
|
fetch(liveUrl, { credentials: 'same-origin' })
|
||||||
.then(function (r) { return r.json(); })
|
.then(function (r) { return r.json(); })
|
||||||
.then(apply)
|
.then(apply)
|
||||||
.catch(function () {});
|
.catch(function () { setHint('轮询失败 · 重试中', 'red'); });
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof EventSource === 'undefined') {
|
if (typeof EventSource === 'undefined') {
|
||||||
startPollFallback();
|
startPollFallback('轮询 1s(浏览器不支持 SSE)');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setHint('SSE 连接中…', '');
|
||||||
var es = new EventSource(streamUrl);
|
var es = new EventSource(streamUrl);
|
||||||
|
es.onopen = function () {
|
||||||
|
mode = 'sse';
|
||||||
|
setHint('SSE 已连接', 'green');
|
||||||
|
};
|
||||||
es.onmessage = function (ev) {
|
es.onmessage = function (ev) {
|
||||||
|
msgCount += 1;
|
||||||
|
mode = 'sse';
|
||||||
try { apply(JSON.parse(ev.data)); } catch (e) {}
|
try { apply(JSON.parse(ev.data)); } catch (e) {}
|
||||||
};
|
};
|
||||||
es.onerror = function () {
|
es.onerror = function () {
|
||||||
es.close();
|
// 已收到过数据则让浏览器自行重连;长时间无数据再降级轮询
|
||||||
startPollFallback();
|
if (msgCount === 0 || es.readyState === EventSource.CLOSED) {
|
||||||
|
try { es.close(); } catch (e) {}
|
||||||
|
startPollFallback('轮询 1s(SSE 断开)');
|
||||||
|
} else {
|
||||||
|
setHint('SSE 重连中…', '');
|
||||||
|
}
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user