Add hub order popup modal with compact instance trade embed (plan A).

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-25 23:12:08 +08:00
parent ee011800e1
commit 1767566951
9 changed files with 334 additions and 12 deletions
+129 -2
View File
@@ -394,6 +394,9 @@
let instanceFrameUrl = "";
/** @type {{ exchangeId: string, nextPath: string, title: string } | null} */
let instanceFrameCtx = null;
let orderPopupUrl = "";
/** @type {{ exchangeId: string, title: string, symbol: string } | null} */
let orderPopupCtx = null;
function isHubEmbedded() {
try {
@@ -449,6 +452,120 @@
shell.classList.remove("is-instance-nav-loading");
}
function setOrderPopupLoading(loading) {
const modal = document.getElementById("order-popup-modal");
if (!modal) return;
modal.classList.toggle("is-loading", !!loading);
}
function closeOrderPopup() {
const modal = document.getElementById("order-popup-modal");
const frame = document.getElementById("order-popup-frame");
orderPopupUrl = "";
orderPopupCtx = null;
if (frame) frame.src = "about:blank";
if (modal) {
modal.classList.add("hidden");
modal.setAttribute("aria-hidden", "true");
modal.classList.remove("is-loading");
}
document.body.classList.remove("hub-order-popup-open");
}
async function openOrderPopup(exchangeId, opts) {
const options = opts || {};
const symbol = (options.symbol || "").trim();
let next = "/trade?order_popup=1";
if (symbol) next += "&symbol=" + encodeURIComponent(symbol);
try {
const url = await fetchInstanceOpenUrl(exchangeId, next, { embed: true });
const row = lastMonitorRows.find((x) => String(x.id) === String(exchangeId));
const title = row ? row.name : exchangeId;
orderPopupCtx = { exchangeId: String(exchangeId), title, symbol };
orderPopupUrl = url;
const modal = document.getElementById("order-popup-modal");
const frame = document.getElementById("order-popup-frame");
const titleEl = document.getElementById("order-popup-title");
if (!modal || !frame) {
window.open(url, "_blank", "noopener");
return;
}
if (titleEl) titleEl.textContent = title + " · 实盘下单";
setOrderPopupLoading(true);
frame.src = url;
modal.classList.remove("hidden");
modal.setAttribute("aria-hidden", "false");
document.body.classList.add("hub-order-popup-open");
if (frame.dataset.orderPopupBound !== "1") {
frame.dataset.orderPopupBound = "1";
frame.addEventListener("load", () => {
setOrderPopupLoading(false);
try {
if (globalThis.HubTheme && typeof HubTheme.get === "function" && frame.contentWindow) {
frame.contentWindow.postMessage(
{ type: "hub-theme-sync", theme: HubTheme.get() },
"*"
);
}
} catch (_) {}
});
}
} catch (e) {
setOrderPopupLoading(false);
showToast(String(e), true);
}
}
async function refreshOrderPopup() {
if (!orderPopupCtx) return;
const frame = document.getElementById("order-popup-frame");
if (!frame) return;
try {
let next = "/trade?order_popup=1";
if (orderPopupCtx.symbol) {
next += "&symbol=" + encodeURIComponent(orderPopupCtx.symbol);
}
const url = await fetchInstanceOpenUrl(orderPopupCtx.exchangeId, next, { embed: true });
orderPopupUrl = url;
setOrderPopupLoading(true);
frame.src = url;
} catch (e) {
setOrderPopupLoading(false);
showToast(String(e), true);
}
}
function initOrderPopupModal() {
const modal = document.getElementById("order-popup-modal");
if (!modal || modal.dataset.bound === "1") return;
modal.dataset.bound = "1";
const backdrop = document.getElementById("order-popup-backdrop");
const closeBtn = document.getElementById("order-popup-close");
const refreshBtn = document.getElementById("order-popup-refresh");
const newTabBtn = document.getElementById("order-popup-newtab");
if (backdrop) backdrop.onclick = () => closeOrderPopup();
if (closeBtn) closeBtn.onclick = () => closeOrderPopup();
if (refreshBtn) refreshBtn.onclick = () => refreshOrderPopup();
if (newTabBtn) {
newTabBtn.onclick = () => {
if (!orderPopupCtx) return;
openInstance(orderPopupCtx.exchangeId, "/trade", { newTab: true });
};
}
if (!window.__hubOrderPopupMsgBound) {
window.__hubOrderPopupMsgBound = true;
window.addEventListener("message", (ev) => {
const d = ev.data;
if (!d || d.type !== "hub-order-popup-done") return;
refreshMonitorBoardNow();
showToast(
d.message || (d.ok ? "开仓请求已提交,请查看监控区" : "开仓提交可能失败,请查看表单提示"),
!d.ok
);
});
}
}
async function openInstance(exchangeId, nextPath, opts) {
const options = opts || {};
const newTab = !!options.newTab;
@@ -2385,6 +2502,15 @@
openMarketForPosition(btn.dataset.exId, btn.dataset.symbol, btn.dataset.exKey, btn.dataset.posCtx);
};
});
box.querySelectorAll(".btn-open-order-popup").forEach((btn) => {
btn.onclick = (ev) => {
ev.preventDefault();
ev.stopPropagation();
openOrderPopup(btn.dataset.exId, {
symbol: (btn.dataset.symbol || "").trim(),
});
};
});
box.querySelectorAll(".btn-open-instance").forEach((btn) => {
btn.onclick = (ev) => {
ev.preventDefault();
@@ -3214,7 +3340,7 @@
<div class="fs-head-actions">
<button type="button" class="ghost btn-expand-back">返回监控</button>
${flaskOpen ? `<a class="btn-link btn-open-instance btn-open-trade" href="#" data-ex-id="${esc(row.id)}" data-next="/trade" data-new-tab="1">打开实例</a>` : ""}
${flaskOpen ? `<a class="btn-link btn-open-instance" href="#" data-ex-id="${esc(row.id)}" data-next="/trade">下单</a>` : ""}
${flaskOpen ? `<a class="btn-link btn-open-order-popup" href="#" data-ex-id="${esc(row.id)}">下单</a>` : ""}
${flaskOpen ? `<a class="btn-link btn-open-instance" href="#" data-ex-id="${esc(row.id)}" data-next="/key_monitor">监控位</a>` : ""}
${flaskOpen ? `<a class="btn-link btn-open-instance" href="#" data-ex-id="${esc(row.id)}" data-next="/records">复盘</a>` : ""}
<button type="button" class="danger btn-close-ex" data-id="${esc(row.id)}">全平</button>
@@ -3542,7 +3668,7 @@
? `<a class="btn-link btn-open-instance btn-open-trade" href="#" data-ex-id="${esc(row.id)}" data-next="/trade" data-new-tab="1">打开实例</a>`
: "";
const openTrade = flaskOpen
? `<a class="btn-link btn-open-instance" href="#" data-ex-id="${esc(row.id)}" data-next="/trade">下单</a>`
? `<a class="btn-link btn-open-order-popup" href="#" data-ex-id="${esc(row.id)}">下单</a>`
: "";
const openKey = flaskOpen
? `<a class="btn-link btn-open-instance" href="#" data-ex-id="${esc(row.id)}" data-next="/key_monitor">监控位</a>`
@@ -4870,6 +4996,7 @@
initTpslModal();
initInstanceFrame();
initOrderPopupModal();
initFullscreen();
initMobileLayout();
if (globalThis.HubTheme && typeof HubTheme.initToggleUI === "function") {