Add hub order popup modal with compact instance trade embed (plan A).
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -2526,6 +2526,69 @@ button.btn-sm {
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.order-popup-modal .order-popup-card {
|
||||
width: min(920px, calc(100vw - 24px));
|
||||
max-height: calc(100vh - 32px);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.order-popup-head {
|
||||
padding: 12px 14px;
|
||||
border-bottom: 1px solid var(--border-soft);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.order-popup-head-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.order-popup-frame-wrap {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
min-height: 420px;
|
||||
max-height: calc(100vh - 120px);
|
||||
background: #0b0d14;
|
||||
}
|
||||
|
||||
.order-popup-frame {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 420px;
|
||||
border: 0;
|
||||
display: block;
|
||||
background: #0b0d14;
|
||||
}
|
||||
|
||||
.order-popup-loading {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: none;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
color: var(--muted);
|
||||
font-size: 13px;
|
||||
background: rgba(8, 10, 18, 0.82);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.order-popup-modal.is-loading .order-popup-loading {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.order-popup-modal.is-loading .order-popup-frame {
|
||||
opacity: 0.35;
|
||||
}
|
||||
|
||||
body.hub-order-popup-open {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.table-scroll {
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
|
||||
@@ -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") {
|
||||
|
||||
@@ -1000,6 +1000,27 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="order-popup-modal" class="modal order-popup-modal hidden" aria-hidden="true">
|
||||
<div class="modal-backdrop" id="order-popup-backdrop"></div>
|
||||
<div class="modal-card order-popup-card" role="dialog" aria-labelledby="order-popup-title">
|
||||
<div class="modal-head order-popup-head">
|
||||
<h3 id="order-popup-title">实盘下单</h3>
|
||||
<div class="order-popup-head-actions">
|
||||
<button type="button" id="order-popup-refresh" class="ghost">刷新</button>
|
||||
<button type="button" id="order-popup-newtab" class="ghost">新标签</button>
|
||||
<button type="button" class="ghost order-popup-close" id="order-popup-close" aria-label="关闭">×</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="order-popup-frame-wrap">
|
||||
<div id="order-popup-loading" class="order-popup-loading" aria-hidden="true">
|
||||
<span class="instance-frame-spinner" aria-hidden="true"></span>
|
||||
<span>加载实例下单页…</span>
|
||||
</div>
|
||||
<iframe id="order-popup-frame" class="order-popup-frame" title="实盘下单"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="tpsl-modal" class="modal hidden" aria-hidden="true">
|
||||
<div class="modal-backdrop" id="tpsl-modal-backdrop"></div>
|
||||
<div class="modal-panel" role="dialog" aria-labelledby="tpsl-modal-title">
|
||||
|
||||
Reference in New Issue
Block a user