Files
gold_monitor/app/templates/dashboard.html
T
2026-10-17 21:04:55 +08:00

167 lines
6.2 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{% extends "base.html" %}
{% block title %}总览 · {{ app_name }}{% endblock %}
{% block content %}
<h2 style="margin:0 0 14px">总览 <span class="muted" style="font-size:13px;font-weight:400" id="live-hint">· 秒级推送</span></h2>
<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"><span id="stat-online">{{ online_count }}</span><span class="muted" style="font-size:16px"> / {{ machine_count }}</span></div>
</div>
<div class="card"><div class="muted">实时成交(条)</div><div class="stat" id="stat-trades">{{ trades|length }}</div></div>
</div>
<div class="grid cols-2" style="margin-top:14px">
<div class="card">
<h3>在线机器</h3>
<table>
<thead><tr><th>机器</th><th>状态</th><th>最近心跳</th></tr></thead>
<tbody id="online-machines-body">
{% for m in online_machines %}
<tr>
<td>{{ m.name }} <span class="muted">#{{ m.id }}</span></td>
<td><span class="tag green">在线</span></td>
<td class="muted">{{ m.last_seen_fmt }}</td>
</tr>
{% else %}
<tr><td colspan="3" class="muted">当前无在线机器(客户端约每 2 秒心跳)</td></tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="card">
<h3>快速入口</h3>
<p class="muted">下载通用辅助包并用激活码绑定;成交自动上报到本页实时记录与交易复盘。</p>
<p style="margin-top:12px">
<a class="btn" href="{{ url_for('download_page') }}">下载中心</a>
<a class="btn secondary" href="{{ url_for('records_page') }}">交易复盘</a>
</p>
<p class="muted" style="margin-top:14px">对外地址:{{ public_base }}</p>
</div>
</div>
<div class="card" style="margin-top:14px">
<h3>机器实时交易记录</h3>
<table>
<thead>
<tr>
<th>时间</th><th>机器</th><th>品种</th><th>方向</th><th>手数</th>
<th>盈亏</th><th>账户</th><th>单号</th><th></th>
</tr>
</thead>
<tbody id="live-trades-body">
{% for t in trades %}
<tr>
<td class="muted">{{ t.time_fmt }}</td>
<td>{{ t.machine_name or '—' }}</td>
<td>{{ t.symbol }}</td>
<td><span class="tag {% if t.side=='buy' %}green{% else %}red{% endif %}">{{ t.side }}</span></td>
<td>{{ t.volume if t.volume is not none else '—' }}</td>
<td style="color:{% if t.profit and t.profit>0 %}var(--green){% elif t.profit and t.profit<0 %}var(--red){% else %}inherit{% endif %}">
{{ t.profit if t.profit is not none else '—' }}
</td>
<td>{{ t.account_name or '—' }}</td>
<td class="muted">{{ t.ticket or '—' }}</td>
<td><a href="{{ url_for('record_detail', tid=t.id) }}">复盘</a></td>
</tr>
{% else %}
<tr><td colspan="9" class="muted">暂无成交。客户端上报后将实时显示。</td></tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}
{% block scripts %}
<script>
(function () {
var streamUrl = {{ url_for('dashboard_stream')|tojson }};
var liveUrl = {{ url_for('dashboard_live')|tojson }};
var detailBase = {{ url_for('record_detail', tid=0)|tojson }}.replace(/0$/, '');
var hint = document.getElementById('live-hint');
function esc(s) {
return String(s == null ? '' : s)
.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
}
function renderMachines(list) {
var body = document.getElementById('online-machines-body');
if (!body) return;
if (!list.length) {
body.innerHTML = '<tr><td colspan="3" class="muted">当前无在线机器(客户端约每 2 秒心跳)</td></tr>';
return;
}
body.innerHTML = list.map(function (m) {
return '<tr><td>' + esc(m.name) + ' <span class="muted">#' + esc(m.id) + '</span></td>' +
'<td><span class="tag green">在线</span></td>' +
'<td class="muted">' + esc(m.last_seen_fmt || '—') + '</td></tr>';
}).join('');
}
function profitColor(p) {
if (p == null || p === '') return 'inherit';
var n = Number(p);
if (n > 0) return 'var(--green)';
if (n < 0) return 'var(--red)';
return 'inherit';
}
function renderTrades(list) {
var body = document.getElementById('live-trades-body');
if (!body) return;
document.getElementById('stat-trades').textContent = String(list.length);
if (!list.length) {
body.innerHTML = '<tr><td colspan="9" class="muted">暂无成交。客户端上报后将实时显示。</td></tr>';
return;
}
body.innerHTML = list.map(function (t) {
var sideCls = t.side === 'buy' ? 'green' : 'red';
return '<tr>' +
'<td class="muted">' + esc(t.time_fmt) + '</td>' +
'<td>' + esc(t.machine_name || '—') + '</td>' +
'<td>' + esc(t.symbol) + '</td>' +
'<td><span class="tag ' + sideCls + '">' + esc(t.side) + '</span></td>' +
'<td>' + esc(t.volume != null ? t.volume : '—') + '</td>' +
'<td style="color:' + profitColor(t.profit) + '">' + esc(t.profit != null ? t.profit : '—') + '</td>' +
'<td>' + esc(t.account_name || '—') + '</td>' +
'<td class="muted">' + esc(t.ticket || '—') + '</td>' +
'<td><a href="' + detailBase + t.id + '">复盘</a></td>' +
'</tr>';
}).join('');
}
function apply(data) {
if (!data || !data.ok) return;
document.getElementById('stat-online').textContent = String(data.online_count);
renderMachines(data.online_machines || []);
renderTrades(data.trades || []);
}
function startPollFallback() {
if (hint) hint.textContent = '· 轮询 1s(SSE 不可用)';
setInterval(function () {
fetch(liveUrl, { credentials: 'same-origin' })
.then(function (r) { return r.json(); })
.then(apply)
.catch(function () {});
}, 1000);
}
if (typeof EventSource === 'undefined') {
startPollFallback();
return;
}
var es = new EventSource(streamUrl);
es.onmessage = function (ev) {
try { apply(JSON.parse(ev.data)); } catch (e) {}
};
es.onerror = function () {
es.close();
startPollFallback();
};
})();
</script>
{% endblock %}