From 9c28e65775f365537a37b586622b247cf4eb2a31 Mon Sep 17 00:00:00 2001 From: CN-JS-HuiBai Date: Sat, 18 Apr 2026 12:17:24 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=97=A0=E6=B3=95=E5=8D=95?= =?UTF-8?q?=E5=87=BB=E7=9A=84=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/admin/app.css | 2 +- frontend/admin/app.js | 68 ++++++++++++++----- .../reverse/output/index-CO3BwsT2.pretty.js | 45 ++++++------ scratch/patch_bundle.ps1 | 19 ++++++ 4 files changed, 97 insertions(+), 37 deletions(-) create mode 100644 scratch/patch_bundle.ps1 diff --git a/frontend/admin/app.css b/frontend/admin/app.css index a3c8370..a1d57e6 100644 --- a/frontend/admin/app.css +++ b/frontend/admin/app.css @@ -199,7 +199,7 @@ html, body { background: #fff; border-radius: var(--radius-lg); border: 1px solid var(--border-light); - overflow: hidden; + overflow: auto; } table { diff --git a/frontend/admin/app.js b/frontend/admin/app.js index 1e6342b..c325c15 100644 --- a/frontend/admin/app.js +++ b/frontend/admin/app.js @@ -39,7 +39,9 @@ realname: { list: [], pagination: null }, devices: { list: [], pagination: null }, ipv6: { list: [], pagination: null }, - expandedNodes: new Set() + expandedNodes: new Set(), + prevUser: null, + prevRoute: null }; const ROUTE_META = { @@ -73,6 +75,9 @@ root.addEventListener("click", onClick); root.addEventListener("submit", onSubmit); + // Initialize shell and modal containers + root.innerHTML = '
'; + if (state.token) { await loadBootstrap(); } else { @@ -266,6 +271,8 @@ } if (action === "plan-delete") { + event.preventDefault(); + event.stopPropagation(); if (confirm("确认删除该套餐吗?")) { await adminPost(`${cfg.api.adminBase}/plan/drop`, { id: Number(actionEl.getAttribute("data-id")) }); await hydrateRoute(); @@ -280,6 +287,8 @@ } if (action === "coupon-delete") { + event.preventDefault(); + event.stopPropagation(); if (confirm("确认删除该优惠券吗?")) { await adminPost(`${cfg.api.adminBase}/coupon/drop`, { id: Number(actionEl.getAttribute("data-id")) }); await hydrateRoute(); @@ -312,6 +321,8 @@ } if (action === "order-paid") { + event.preventDefault(); + event.stopPropagation(); await adminPost(`${cfg.api.adminBase}/order/paid`, { trade_no: actionEl.getAttribute("data-trade") }); @@ -320,6 +331,8 @@ } if (action === "order-cancel") { + event.preventDefault(); + event.stopPropagation(); await adminPost(`${cfg.api.adminBase}/order/cancel`, { trade_no: actionEl.getAttribute("data-trade") }); @@ -334,13 +347,18 @@ } if (action === "node-edit") { - const item = state.nodes.find((row) => String(row.id) === actionEl.getAttribute("data-id")); + event.preventDefault(); + event.stopPropagation(); + const nodeId = String(actionEl.getAttribute("data-id")); + const item = state.nodes.find((row) => String(row.id) === nodeId); state.modal = { type: "node", data: item || {} }; render(); return; } if (action === "node-copy") { + event.preventDefault(); + event.stopPropagation(); const nodeId = Number(actionEl.getAttribute("data-id")); try { await adminPost(`${cfg.api.adminBase}/server/manage/copy`, { id: nodeId }); @@ -350,6 +368,8 @@ } if (action === "node-delete") { + event.preventDefault(); + event.stopPropagation(); if (confirm("确认删除该节点吗?")) { const nodeId = Number(actionEl.getAttribute("data-id")); try { @@ -544,19 +564,32 @@ await hydrateRoute(); } - function render() { - root.innerHTML = state.user ? renderDashboard() : renderLogin(); - if (state.modal) { - const modalShell = document.createElement("div"); - modalShell.className = "modal-overlay"; - modalShell.innerHTML = renderModalContent(); - root.appendChild(modalShell); + function render(force) { + const shellContainer = document.getElementById("admin-shell-container"); + const modalContainer = document.getElementById("admin-modal-container"); + const busyContainer = document.getElementById("admin-busy-container"); + + if (!shellContainer || !modalContainer || !busyContainer) return; + + // Re-render the shell if user or route changed, or if forced (e.g., data updated) + if (force || state.user !== state.prevUser || state.route !== state.prevRoute) { + shellContainer.innerHTML = state.user ? renderDashboard() : renderLogin(); + state.prevUser = state.user; + state.prevRoute = state.route; } + + // Handle Modal independently + if (state.modal) { + modalContainer.innerHTML = ``; + } else { + modalContainer.innerHTML = ""; + } + + // Handle Busy overlay independently if (state.busy) { - const overlay = document.createElement("div"); - overlay.className = "busy-overlay"; - overlay.innerHTML = '
正在处理...
'; - root.appendChild(overlay); + busyContainer.innerHTML = '
正在处理...
'; + } else { + busyContainer.innerHTML = ""; } } @@ -741,7 +774,8 @@ } function renderNodeManage() { - const roots = state.nodes.filter((node) => !node.parent_id); + const isRoot = (node) => !node.parent_id || String(node.parent_id) === "0"; + const roots = state.nodes.filter(isRoot); const childrenByParent = {}; state.nodes.forEach((node) => { if (node.parent_id) { @@ -1332,17 +1366,17 @@ function setBusy(value) { state.busy = value; - render(); + render(true); } function show(message, type) { state.message = message; state.messageType = type || "info"; - render(); + render(true); window.clearTimeout(show._timer); show._timer = window.setTimeout(function () { state.message = ""; - render(); + render(true); }, 3000); } diff --git a/frontend/admin/reverse/output/index-CO3BwsT2.pretty.js b/frontend/admin/reverse/output/index-CO3BwsT2.pretty.js index 213ea08..4e59ffa 100644 --- a/frontend/admin/reverse/output/index-CO3BwsT2.pretty.js +++ b/frontend/admin/reverse/output/index-CO3BwsT2.pretty.js @@ -232827,9 +232827,7 @@ function qst({ title: e, icon: t, label: n, sub: i, closeNav: r }) { children: [ Q.jsx(Fst, { asChild: !0, - children: Q.jsx(Lot, { - asChild: !0, - children: Q.jsx(Nm, { + children: Q.jsx(Lot, { asChild: !0, onClick: (e) => (e.stopPropagation(), e.preventDefault()), children: Q.jsx(Nm, { variant: a ? "secondary" : "ghost", size: "icon", className: "h-12 w-12", @@ -235896,9 +235894,7 @@ function vut() { o = r.substring(0, 2).toUpperCase(); return Q.jsxs(Not, { children: [ - Q.jsx(Lot, { - asChild: !0, - children: Q.jsx(Nm, { + Q.jsx(Lot, { asChild: !0, onClick: (e) => (e.stopPropagation(), e.preventDefault()), children: Q.jsx(Nm, { variant: "ghost", className: "relative h-8 w-8 rounded-full", children: Q.jsxs(fut, { @@ -292882,6 +292878,7 @@ function c5t() { v = kv({ resolver: Ov(n), defaultValues: i, mode: "onChange" }), b = T_({ control: v.control, name: "protocol_settings" }), y = H.useRef(new Map()), + T = H.useRef(0), x = H.useCallback(async () => { if (!r) return; const [e, t, n] = await Promise.all([eD(), rD(), UL()]); @@ -292899,6 +292896,9 @@ function c5t() { (H.useEffect(() => { x(); }, [x]), + H.useEffect(() => { + r && s && (T.current = Date.now()); + }, [r, s]), H.useEffect(() => { if (s) { const e = @@ -292939,9 +292939,11 @@ function c5t() { ), E = (e) => { if (e) { + s && (T.current = Date.now()); o(!0); return; } + if (s && Date.now() - T.current < 250) return; (o(!1), a(null), v.reset(i)); }; return Q.jsxs(Jet, { @@ -293992,9 +293994,7 @@ function h5t({ table: e, refetch: t, saveOrder: n, isSortMode: i, groups: r }) { Q.jsxs(Not, { modal: !1, children: [ - Q.jsx(Lot, { - asChild: !0, - children: Q.jsx(Nm, { + Q.jsx(Lot, { asChild: !0, onClick: (e) => (e.stopPropagation(), e.preventDefault()), children: Q.jsx(Nm, { variant: "outline", size: "sm", className: "h-8 border-dashed", @@ -294097,9 +294097,7 @@ function h5t({ table: e, refetch: t, saveOrder: n, isSortMode: i, groups: r }) { Q.jsxs(Not, { modal: !1, children: [ - Q.jsx(Lot, { - asChild: !0, - children: Q.jsx(Nm, { + Q.jsx(Lot, { asChild: !0, onClick: (e) => (e.stopPropagation(), e.preventDefault()), children: Q.jsx(Nm, { variant: "outline", size: "sm", className: "h-8 border-dashed", @@ -294399,14 +294397,26 @@ function D5t({ node: e, refetch: t }) { }); } function T5t({ node: e, refetch: t, t: n }) { - const { setIsOpen: i, setEditingServer: r, setServerType: o } = p4t(); + const { setIsOpen: i, setEditingServer: r, setServerType: o } = p4t(), + [s, a] = H.useState(!1), + l = H.useCallback(() => { + (a(!1), + window.requestAnimationFrame(() => { + window.requestAnimationFrame(() => { + (o(e.type), r(e), i(!0)); + }); + })); + }, [e, i, r, o]); return Q.jsx("div", { className: "flex justify-center", children: Q.jsxs(Not, { modal: !1, + open: s, + onOpenChange: a, children: [ Q.jsx(Lot, { asChild: !0, + onClick: (e) => (e.stopPropagation(), e.preventDefault()), children: Q.jsx(Nm, { variant: "ghost", className: "h-8 w-8 p-0 hover:bg-muted", @@ -294420,9 +294430,8 @@ function T5t({ node: e, refetch: t, t: n }) { children: [ Q.jsx(Rot, { className: "cursor-pointer", - onSelect: (t) => { - t.preventDefault(); - (o(e.type), r(e), i(!0)); + onSelect: (e) => { + (e.preventDefault(), e.stopPropagation(), l()); }, children: Q.jsxs("div", { className: "flex w-full items-center", @@ -302584,9 +302593,7 @@ function P6t({ table: e, refetch: t, subscriptionPlans: n = [] }) { Q.jsxs(Not, { modal: !1, children: [ - Q.jsx(Lot, { - asChild: !0, - children: Q.jsx(Nm, { + Q.jsx(Lot, { asChild: !0, onClick: (e) => (e.stopPropagation(), e.preventDefault()), children: Q.jsx(Nm, { variant: "outline", size: "sm", className: "h-8 border-dashed", diff --git a/scratch/patch_bundle.ps1 b/scratch/patch_bundle.ps1 new file mode 100644 index 0000000..9ed33fb --- /dev/null +++ b/scratch/patch_bundle.ps1 @@ -0,0 +1,19 @@ +$filePath = "d:\Development\SingBox-Gopanel\frontend\admin\reverse\output\index-CO3BwsT2.pretty.js" + +# Read file +$content = [System.IO.File]::ReadAllText($filePath) + +# Regex patterns (escaping literals to handle parens and braces correctly) +$t1 = [regex]::Escape("Q.jsx(Lot, {") + "\s+" + [regex]::Escape("asChild: !0,") + "\s+" + [regex]::Escape("children: Q.jsx(Nm, {") +$r1 = "Q.jsx(Lot, { asChild: !0, onClick: (e) => (e.stopPropagation(), e.preventDefault()), children: Q.jsx(Nm, {" + +$t2 = [regex]::Escape("onSelect: (e) => {") + "\s+" + [regex]::Escape("(e.preventDefault(), l());") +$r2 = "onSelect: (e) => { (e.preventDefault(), e.stopPropagation(), l());" + +# Perform replacements +$content = [System.Text.RegularExpressions.Regex]::Replace($content, $t1, $r1) +$content = [System.Text.RegularExpressions.Regex]::Replace($content, $t2, $r2) + +# Write back +[System.IO.File]::WriteAllText($filePath, $content) +Write-Output "Patch applied successfully (Regex Escaped)"