fix: reduce embed tab flash with persistent pane show/hide
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
/**
|
||||
* 中控 iframe 壳:顶栏/统计常驻,tab 内容走 /api/embed/page/<tab>。
|
||||
* 各 tab 面板常驻 DOM,切换时 show/hide,避免整页替换闪屏。
|
||||
*/
|
||||
(function (global) {
|
||||
const TAB_PATH = {
|
||||
@@ -15,7 +16,7 @@
|
||||
|
||||
let navToken = 0;
|
||||
let loadingTab = false;
|
||||
const tabHtmlCache = new Map();
|
||||
const tabPanes = new Map();
|
||||
|
||||
/** 自带校验后 form.submit() 的表单,勿在捕获阶段再 fetch 一份(会双发 POST) */
|
||||
const CUSTOM_SUBMIT_FORM_IDS = new Set(["add-order-form", "key-form", "roll-form"]);
|
||||
@@ -39,9 +40,8 @@
|
||||
return "";
|
||||
}
|
||||
|
||||
function setRootLoading(on) {
|
||||
const root = document.getElementById("embed-page-root");
|
||||
if (root) root.classList.toggle("is-embed-tab-loading", !!on);
|
||||
function pageRoot() {
|
||||
return document.getElementById("embed-page-root");
|
||||
}
|
||||
|
||||
function setNavActive(tab) {
|
||||
@@ -68,7 +68,7 @@
|
||||
if (tab === "trade") {
|
||||
if (typeof global.refreshOrderDefaults === "function") global.refreshOrderDefaults();
|
||||
if (typeof global.initOrderEntryModelSelect === "function") {
|
||||
var root = document.getElementById("embed-page-root") || document;
|
||||
const root = pageRoot() || document;
|
||||
global.initOrderEntryModelSelect(root);
|
||||
}
|
||||
if (global.ManualOrderRrPreview && typeof global.ManualOrderRrPreview.wire === "function") {
|
||||
@@ -93,20 +93,17 @@
|
||||
global.refreshPriceSnapshotConditional();
|
||||
}
|
||||
if (global.SymbolLivePrice && typeof global.SymbolLivePrice.init === "function") {
|
||||
const root = document.getElementById("embed-page-root") || document;
|
||||
const root = pageRoot() || document;
|
||||
global.SymbolLivePrice.init(root);
|
||||
}
|
||||
if (global.JournalUploadSlots && typeof global.JournalUploadSlots.init === "function") {
|
||||
const root = document.getElementById("embed-page-root") || document;
|
||||
const root = pageRoot() || document;
|
||||
global.JournalUploadSlots.init(root);
|
||||
}
|
||||
}
|
||||
|
||||
function injectFragment(html) {
|
||||
const root = document.getElementById("embed-page-root");
|
||||
if (!root) return;
|
||||
root.innerHTML = html;
|
||||
root.querySelectorAll("script").forEach((old) => {
|
||||
function runScripts(container) {
|
||||
container.querySelectorAll("script").forEach((old) => {
|
||||
const s = document.createElement("script");
|
||||
if (old.src) s.src = old.src;
|
||||
else s.textContent = old.textContent;
|
||||
@@ -114,6 +111,49 @@
|
||||
});
|
||||
}
|
||||
|
||||
function showPane(tab) {
|
||||
tabPanes.forEach((pane, name) => {
|
||||
pane.hidden = name !== tab;
|
||||
});
|
||||
}
|
||||
|
||||
function mountPane(tab, html) {
|
||||
const root = pageRoot();
|
||||
if (!root) return null;
|
||||
const existing = tabPanes.get(tab);
|
||||
if (existing) existing.remove();
|
||||
|
||||
const pane = document.createElement("div");
|
||||
pane.className = "embed-tab-pane";
|
||||
pane.setAttribute("data-embed-pane", tab);
|
||||
pane.hidden = true;
|
||||
|
||||
const holder = document.createElement("div");
|
||||
holder.innerHTML = html;
|
||||
runScripts(holder);
|
||||
while (holder.firstChild) pane.appendChild(holder.firstChild);
|
||||
|
||||
root.appendChild(pane);
|
||||
tabPanes.set(tab, pane);
|
||||
return pane;
|
||||
}
|
||||
|
||||
function initBootPane() {
|
||||
const root = pageRoot();
|
||||
if (!root || tabPanes.size > 0) return;
|
||||
const tab = getTab();
|
||||
if (root.querySelector("[data-embed-pane]")) return;
|
||||
if (!root.childNodes.length) return;
|
||||
|
||||
const pane = document.createElement("div");
|
||||
pane.className = "embed-tab-pane";
|
||||
pane.setAttribute("data-embed-pane", tab);
|
||||
Array.from(root.childNodes).forEach((node) => pane.appendChild(node));
|
||||
root.appendChild(pane);
|
||||
tabPanes.set(tab, pane);
|
||||
showPane(tab);
|
||||
}
|
||||
|
||||
function embedPageUrl(tab) {
|
||||
const qs = listWindowQueryString();
|
||||
let url = "/api/embed/page/" + encodeURIComponent(tab);
|
||||
@@ -134,44 +174,44 @@
|
||||
}
|
||||
|
||||
function warmTabCache(tab) {
|
||||
if (!tab || tabHtmlCache.has(tab)) return;
|
||||
if (!tab || tabPanes.has(tab) || loadingTab) return;
|
||||
fetchTabHtml(tab)
|
||||
.then((html) => {
|
||||
tabHtmlCache.set(tab, html);
|
||||
if (!tabPanes.has(tab)) mountPane(tab, html);
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
function clearTabCache() {
|
||||
tabHtmlCache.clear();
|
||||
tabPanes.forEach((pane) => pane.remove());
|
||||
tabPanes.clear();
|
||||
}
|
||||
|
||||
function activateTab(tab, opts) {
|
||||
const options = opts || {};
|
||||
showPane(tab);
|
||||
setNavActive(tab);
|
||||
if (!options.skipUrl) syncUrl(tab, !!options.replace);
|
||||
runPageInit(tab);
|
||||
}
|
||||
|
||||
async function loadTab(tab, opts) {
|
||||
const options = opts || {};
|
||||
if (!tab || loadingTab) return;
|
||||
const token = ++navToken;
|
||||
const force = !!options.force;
|
||||
const cached = !force && tabHtmlCache.get(tab);
|
||||
if (!tab) return;
|
||||
|
||||
if (cached) {
|
||||
injectFragment(cached);
|
||||
setNavActive(tab);
|
||||
if (!options.skipUrl) syncUrl(tab, !!options.replace);
|
||||
runPageInit(tab);
|
||||
warmTabCache(tab);
|
||||
if (tabPanes.has(tab) && !options.force) {
|
||||
activateTab(tab, options);
|
||||
return;
|
||||
}
|
||||
|
||||
if (loadingTab) return;
|
||||
const token = ++navToken;
|
||||
loadingTab = true;
|
||||
setRootLoading(true);
|
||||
try {
|
||||
const html = await fetchTabHtml(tab);
|
||||
if (token !== navToken) return;
|
||||
tabHtmlCache.set(tab, html);
|
||||
injectFragment(html);
|
||||
setNavActive(tab);
|
||||
if (!options.skipUrl) syncUrl(tab, !!options.replace);
|
||||
runPageInit(tab);
|
||||
mountPane(tab, html);
|
||||
activateTab(tab, options);
|
||||
} catch (e) {
|
||||
if (token === navToken) {
|
||||
const flash = document.getElementById("embed-flash");
|
||||
@@ -181,16 +221,16 @@
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (token === navToken) {
|
||||
loadingTab = false;
|
||||
setRootLoading(false);
|
||||
}
|
||||
if (token === navToken) loadingTab = false;
|
||||
}
|
||||
}
|
||||
|
||||
function reloadCurrentTab() {
|
||||
tabHtmlCache.delete(getTab());
|
||||
return loadTab(getTab(), { replace: true, skipUrl: true, force: true });
|
||||
const tab = getTab();
|
||||
const pane = tabPanes.get(tab);
|
||||
if (pane) pane.remove();
|
||||
tabPanes.delete(tab);
|
||||
return loadTab(tab, { replace: true, skipUrl: true, force: true });
|
||||
}
|
||||
|
||||
function postFormAndReload(form, label) {
|
||||
@@ -304,6 +344,7 @@
|
||||
if (!isEmbedShell()) return;
|
||||
patchApplyListWindow();
|
||||
patchHardNavigations();
|
||||
initBootPane();
|
||||
bindNav();
|
||||
runPageInit(getTab());
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user