From: CodeBuddy Date: Wed, 23 Sep 2026 02:54:18 +0000 (+0800) Subject: fix(vue): 配置站点纯白页根因 - 模板表达式中的 window 被解析为组件作用域 X-Git-Url: http://acesimba.cloud/gitweb/?a=commitdiff_plain;h=eed72d26beb775ef18b109da175e0925dbcc068c;p=StationBuilder.git fix(vue): 配置站点纯白页根因 - 模板表达式中的 window 被解析为组件作用域 根因: 用户拖动过 AI 浮动按钮, fabPos 存入 localStorage; 该值存在时 Home.vue 模板 :style 中的 window.innerHeight 被 Vue 编译器当作组件属性 _ctx.window (undefined) 解析, 读取 .innerHeight 抛 TypeError, 整棵 Home 渲染树崩溃 -> 纯白页。React 版 JSX 不做此转换故无此问题。 修复: - Home.vue 新增 setup 绑定 const win = window, 模板该用 win.innerHeight (替代 window.innerHeight) - 同步修正另外两处模板内联处理器中的 window.confirm / window.history.replaceState (Home.vue:546 删除领域点击, Home.vue:597 站点切换) 同样规避 _ctx.window 解析 - main.js 新增屏幕可见的错误浮层(window.onerror/unhandledrejection/Vue errorHandler), 便于后续此类运行时错误直接显示到页面 脚本内(window.open/history.replaceState/document.* 等)的 window 仍是真实全局, 无需改动。 --- diff --git a/src-vue/Home.vue b/src-vue/Home.vue index 9b6020a..ae86fcc 100644 --- a/src-vue/Home.vue +++ b/src-vue/Home.vue @@ -54,6 +54,11 @@ import Placeholder from "./pages/Placeholder.vue"; import { fetchDb, buildMuLeaves } from "./lib/api.js"; import { meta, SITE_EDIT_STEPS, MODELS } from "./lib/meta.js"; +// `window` is not in Vue's template global allow-list, so referencing it inside a +// template expression resolves to the (undefined) component scope. Expose it as a +// setup binding instead. +const win = window; + const notify = (m) => { message.info(m, 2.2); }; const confirm = (msg) => Promise.resolve(window.confirm(msg)); @@ -538,7 +543,7 @@ function onMenuOpenChange(keys) { resourceNavOpen.value = keys.includes("resourc 领域分类
◆{{ d.name }} - × + ×
@@ -589,7 +594,7 @@ function onMenuOpenChange(keys) { resourceNavOpen.value = keys.includes("resourc
@@ -656,7 +661,7 @@ function onMenuOpenChange(keys) { resourceNavOpen.value = keys.includes("resourc AI 助手 diff --git a/src-vue/main.js b/src-vue/main.js index 7efc450..2dbd14a 100644 --- a/src-vue/main.js +++ b/src-vue/main.js @@ -21,4 +21,24 @@ import "./components/dropdown-button.less"; import App from "./App.vue"; -createApp(App).use(Antd).mount("#root"); +// Visible error overlay — surfaces runtime errors that would otherwise blank the page. +function showFatalError(title, detail) { + try { + let box = document.getElementById("sb-fatal-error"); + if (!box) { + box = document.createElement("div"); + box.id = "sb-fatal-error"; + box.setAttribute("style", "position:fixed;top:0;left:0;right:0;z-index:99999;background:#fff2f0;border-bottom:2px solid #ff4d4f;padding:12px 16px;font:13px/1.6 monospace;color:#a8071a;white-space:pre-wrap;word-break:break-all;max-height:60vh;overflow:auto;"); + document.body && document.body.appendChild(box); + } + box.textContent += `[${title}] ${detail}\n\n`; + } catch (e) { /* ignore */ } +} +window.addEventListener("error", (e) => showFatalError("error", (e.message || "") + "\n" + (e.filename || "") + ":" + (e.lineno || "") + "\n" + (e.error && e.error.stack ? e.error.stack : ""))); +window.addEventListener("unhandledrejection", (e) => showFatalError("unhandledrejection", String(e.reason && e.reason.stack ? e.reason.stack : e.reason))); + +const app = createApp(App); +app.config.errorHandler = (err, instance, info) => { + showFatalError("vue:" + info, String(err && err.stack ? err.stack : err)); +}; +app.use(Antd).mount("#root");