Files

222 lines
8.2 KiB
HTML
Raw Permalink 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 %}工作台 · 期权中控{% endblock %}
{% block body %}
<section class="desk" id="desk-root">
<aside class="desk-side" id="desk-side">
<div class="desk-side-head">
<h2 class="desk-side-title">客户端</h2>
<button type="button" class="desk-side-toggle" id="desk-side-toggle" title="折叠/展开" aria-label="折叠客户端列表">‹</button>
</div>
<ul class="desk-list" id="desk-list">
{% for inst in instances %}
<li class="desk-list-item{% if current and current.id == inst.id %} active{% endif %}" data-id="{{ inst.id }}" data-name="{{ inst.name }}" data-base-url="{{ inst.base_url }}" title="{{ inst.name }}">
<a href="/desk?id={{ inst.id }}">
<span class="desk-list-name">{{ inst.name }}</span>
</a>
</li>
{% else %}
<li class="muted desk-list-empty">无启用实例</li>
{% endfor %}
</ul>
</aside>
<div class="desk-main">
{% if current and open_url %}
<div class="desk-toolbar">
<button type="button" class="desk-side-toggle desk-side-toggle--bar" id="desk-side-toggle-bar" title="展开客户端列表" aria-label="展开客户端列表">›</button>
<strong id="desk-current-name">{{ current.name }}</strong>
<span class="muted" id="desk-current-url">{{ current.base_url }}</span>
<a class="btn" id="desk-open-blank" href="{{ open_url }}" target="_blank" rel="noopener">新标签打开</a>
<a class="btn" href="/">回总览</a>
</div>
<iframe class="desk-frame" id="desk-frame" src="{{ open_url }}" title="{{ current.name }}"></iframe>
{% else %}
<div class="desk-toolbar">
<button type="button" class="desk-side-toggle desk-side-toggle--bar" id="desk-side-toggle-bar" title="展开客户端列表" aria-label="展开客户端列表">›</button>
</div>
<div class="empty">请从左侧选择客户端,或到「实例」添加公网 crypto_okx.</div>
{% endif %}
</div>
</section>
<section class="hub-phone-desk hub-phone-chrome">
<p class="hub-phone-desk-hint muted">点开后进入该客户端(手机页),不经过中控工作台嵌页。</p>
<ul class="hub-phone-desk-list">
{% for inst in instances %}
<li>
<button type="button" class="btn hub-phone-desk-open" data-open-id="{{ inst.id }}">{{ inst.name }}</button>
</li>
{% else %}
<li class="muted">无启用实例</li>
{% endfor %}
</ul>
</section>
{% endblock %}
{% block scripts %}
<script>
(function () {
var phone = document.documentElement.classList.contains("hub-phone-html");
if (!phone) return;
var blank = document.getElementById("desk-open-blank");
if (blank && blank.getAttribute("href")) {
window.location.replace(blank.getAttribute("href"));
return;
}
document.querySelectorAll(".hub-phone-desk-open[data-open-id]").forEach(function (btn) {
btn.addEventListener("click", function () {
var id = btn.getAttribute("data-open-id");
if (!id) return;
fetch("/api/instances/" + encodeURIComponent(id) + "/open_url")
.then(function (r) { return r.json(); })
.then(function (d) {
if (d && d.url) window.location.href = d.url;
})
.catch(function () {});
});
});
})();
</script>
<script>
(function () {
var KEY = "hub_desk_side_collapsed";
var root = document.getElementById("desk-root");
if (!root) return;
function apply(collapsed) {
root.classList.toggle("is-side-collapsed", !!collapsed);
try { localStorage.setItem(KEY, collapsed ? "1" : "0"); } catch (e) {}
}
function toggle() {
apply(!root.classList.contains("is-side-collapsed"));
}
var btn = document.getElementById("desk-side-toggle");
var bar = document.getElementById("desk-side-toggle-bar");
if (btn) btn.addEventListener("click", toggle);
if (bar) bar.addEventListener("click", toggle);
var saved = false;
try { saved = localStorage.getItem(KEY) === "1"; } catch (e) {}
apply(saved);
var MODE_KEY = "hub_desk_instance_modes";
var list = document.getElementById("desk-list");
if (!list) return;
var items = Array.prototype.slice.call(list.querySelectorAll("li.desk-list-item[data-id]"));
if (!items.length) return;
function readModeCache() {
try {
var raw = sessionStorage.getItem(MODE_KEY);
var parsed = raw ? JSON.parse(raw) : {};
return parsed && typeof parsed === "object" ? parsed : {};
} catch (e) {
return {};
}
}
function writeModeCache(map) {
try { sessionStorage.setItem(MODE_KEY, JSON.stringify(map || {})); } catch (e) {}
}
function groupLabel(mode) {
var li = document.createElement("li");
li.className = "desk-group-label";
li.setAttribute("aria-hidden", "true");
var span = document.createElement("span");
span.className = "overview-mode-tag overview-mode-tag--" + mode;
span.textContent = mode;
li.appendChild(span);
return li;
}
function regroup(modeById) {
var sim = [];
var live = [];
var other = [];
items.forEach(function (li) {
var m = String(modeById[li.getAttribute("data-id")] || "").toLowerCase();
if (m === "sim") sim.push(li);
else if (m === "live") live.push(li);
else other.push(li);
});
list.innerHTML = "";
if (sim.length) {
list.appendChild(groupLabel("sim"));
sim.forEach(function (li) { list.appendChild(li); });
}
if (live.length) {
list.appendChild(groupLabel("live"));
live.forEach(function (li) { list.appendChild(li); });
}
other.forEach(function (li) { list.appendChild(li); });
}
var cached = readModeCache();
if (Object.keys(cached).length) regroup(cached);
function setActive(iid) {
items.forEach(function (li) {
li.classList.toggle("active", String(li.getAttribute("data-id")) === String(iid));
});
}
function switchClient(li) {
var iid = li.getAttribute("data-id");
if (!iid) return;
var name = li.getAttribute("data-name") || "";
var baseUrl = li.getAttribute("data-base-url") || "";
var frame = document.getElementById("desk-frame");
if (!frame) {
window.location.href = "/desk?id=" + encodeURIComponent(iid);
return;
}
setActive(iid);
var nameEl = document.getElementById("desk-current-name");
var urlEl = document.getElementById("desk-current-url");
if (nameEl) nameEl.textContent = name;
if (urlEl) urlEl.textContent = baseUrl;
frame.setAttribute("title", name);
fetch("/api/instances/" + iid + "/open_url", {
credentials: "same-origin",
headers: { Accept: "application/json" },
})
.then(function (r) { return r.json(); })
.then(function (d) {
if (!d || !d.ok || !d.url) throw new Error((d && d.msg) || "无法打开");
frame.src = d.url;
var blank = document.getElementById("desk-open-blank");
if (blank) blank.href = d.url;
try { history.replaceState(null, "", "/desk?id=" + encodeURIComponent(iid)); } catch (e) {}
})
.catch(function () {
window.location.href = "/desk?id=" + encodeURIComponent(iid);
});
}
list.addEventListener("click", function (ev) {
var a = ev.target.closest("a");
if (!a) return;
var li = a.closest("li.desk-list-item[data-id]");
if (!li) return;
if (ev.metaKey || ev.ctrlKey || ev.shiftKey || ev.altKey || ev.button !== 0) return;
ev.preventDefault();
switchClient(li);
});
Promise.all(
items.map(function (li) {
var iid = li.getAttribute("data-id");
return fetch("/api/instances/" + iid + "/version", {
credentials: "same-origin",
headers: { Accept: "application/json" },
})
.then(function (r) { return r.json().catch(function () { return {}; }); })
.then(function (v) { return { id: iid, mode: (v && v.mode) || "" }; })
.catch(function () { return { id: iid, mode: cached[iid] || "" }; });
})
).then(function (rows) {
var map = {};
rows.forEach(function (row) { map[row.id] = row.mode; });
writeModeCache(map);
regroup(map);
});
})();
</script>
{% endblock %}