(function (global) { function fmtPnl(v) { if (v == null || v === "") return "—"; const n = Number(v); if (!Number.isFinite(n)) return "—"; return (n > 0 ? "+" : "") + n.toFixed(2) + " U"; } function renderCard(row) { const snap = row.snapshot || {}; const inst = snap.instance || {}; const pnl = (snap.pnl && snap.pnl.unrealized_usdt != null) ? snap.pnl.unrealized_usdt : snap.upl_total_usdc; const brief = snap.positions_brief || snap.positions || []; const online = !!row.online; const status = online ? '在线' : '离线'; const mode = inst.mode || "—"; const err = row.error ? '
错误: ' + escapeHtml(row.error) + "
" : ""; let rows = ""; (brief || []).slice(0, 6).forEach(function (p) { rows += "" + escapeHtml(p.inst_id || "") + "" + escapeHtml(String(p.sheets != null ? p.sheets : "")) + "" + escapeHtml(fmtPnl(p.pnl_usdt)) + ""; }); if (!rows) rows = '暂无持仓'; return ( '
' + "

" + escapeHtml(row.name || "") + "

" + "
" + status + " · " + escapeHtml(mode) + " · 浮盈 " + escapeHtml(fmtPnl(pnl)) + "
" + '
拉取: ' + escapeHtml(row.pulled_at || "") + "
" + err + "" + rows + "
合约张盈亏
" + '

内嵌打开 ' + '

' + "
" ); } function escapeHtml(s) { return String(s) .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """); } async function refresh() { const status = document.getElementById("overview-status"); const grid = document.getElementById("overview-grid"); if (!grid) return; if (status) status.textContent = "刷新中…"; try { const r = await fetch("/api/overview"); const d = await r.json(); if (!d.ok) throw new Error(d.msg || "失败"); const list = d.instances || []; if (!list.length) { grid.innerHTML = '

暂无启用实例,请先到「实例」添加.

'; } else { grid.innerHTML = list.map(renderCard).join(""); grid.querySelectorAll("[data-open-tab]").forEach(function (btn) { btn.addEventListener("click", async function () { const id = btn.getAttribute("data-open-tab"); const rr = await fetch("/api/instances/" + id + "/open_url"); const dd = await rr.json(); if (dd.ok && dd.url) window.open(dd.url, "_blank", "noopener"); else alert(dd.msg || "无法打开"); }); }); } if (status) status.textContent = "已更新 · 共 " + list.length + " 台"; } catch (e) { if (status) status.textContent = e.message || "加载失败"; } } function boot() { const btn = document.getElementById("btn-refresh"); if (btn) btn.addEventListener("click", refresh); refresh(); setInterval(refresh, 10000); } global.HubOverview = { boot: boot, refresh: refresh }; })(window);