From eed72d26beb775ef18b109da175e0925dbcc068c Mon Sep 17 00:00:00 2001 From: CodeBuddy Date: Wed, 23 Sep 2026 10:54:18 +0800 Subject: [PATCH] =?utf8?q?fix(vue):=20=E9=85=8D=E7=BD=AE=E7=AB=99=E7=82=B9?= =?utf8?q?=E7=BA=AF=E7=99=BD=E9=A1=B5=E6=A0=B9=E5=9B=A0=20-=20=E6=A8=A1?= =?utf8?q?=E6=9D=BF=E8=A1=A8=E8=BE=BE=E5=BC=8F=E4=B8=AD=E7=9A=84=20window?= =?utf8?q?=20=E8=A2=AB=E8=A7=A3=E6=9E=90=E4=B8=BA=E7=BB=84=E4=BB=B6?= =?utf8?q?=E4=BD=9C=E7=94=A8=E5=9F=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 根因: 用户拖动过 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 仍是真实全局, 无需改动。 --- src-vue/Home.vue | 11 ++++++++--- src-vue/main.js | 22 +++++++++++++++++++++- 2 files changed, 29 insertions(+), 4 deletions(-) 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"); -- 2.43.0