From 020fbe1b0ef376bb40092413769521de714560d6 Mon Sep 17 00:00:00 2001 From: wuzilong <704541015@qq.com> Date: Fri, 16 Oct 2026 13:53:15 +0800 Subject: [PATCH] feat(web-admin): force a refresh when a newer build is deployed. Co-authored-by: Cursor --- web-admin/src/env.d.ts | 1 + web-admin/src/main.ts | 4 ++- web-admin/src/utils/check-app-update.ts | 40 +++++++++++++++++++++++++ web-admin/vite.config.ts | 18 ++++++++++- 4 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 web-admin/src/utils/check-app-update.ts diff --git a/web-admin/src/env.d.ts b/web-admin/src/env.d.ts index 3472a6fa0..b1589d8b9 100755 --- a/web-admin/src/env.d.ts +++ b/web-admin/src/env.d.ts @@ -63,3 +63,4 @@ declare module 'qrcode.vue' { // 全局变量声明 declare const __APP_VERSION__: string // 版本号 +declare const __APP_BUILD_ID__: string // 构建指纹:短 git hash + 构建时间戳 diff --git a/web-admin/src/main.ts b/web-admin/src/main.ts index dbec157b6..5172c26b7 100644 --- a/web-admin/src/main.ts +++ b/web-admin/src/main.ts @@ -16,6 +16,7 @@ import '@styles/index.scss' // 样式 import '@utils/sys/console.ts' // 控制台输出内容 import { setupGlobDirectives } from './directives' import { setupErrorHandle } from './utils/sys/error-handle' +import { startAppUpdateCheck } from './utils/check-app-update' function maskSecret(value?: string) { if (!value) return '(empty)' @@ -62,4 +63,5 @@ setupGlobDirectives(app) setupErrorHandle(app) app.use(language) -app.mount('#app') \ No newline at end of file +app.mount('#app') +startAppUpdateCheck() \ No newline at end of file diff --git a/web-admin/src/utils/check-app-update.ts b/web-admin/src/utils/check-app-update.ts new file mode 100644 index 000000000..1b052e7f3 --- /dev/null +++ b/web-admin/src/utils/check-app-update.ts @@ -0,0 +1,40 @@ +// web-admin/src/utils/check-app-update.ts +import { startAppUpdateChecker } from './app-update' + +/** 生产环境检查 dist/version.json,发现新 buildId 后强制刷新。 */ +export function startAppUpdateCheck(): () => void { + return startAppUpdateChecker({ + isProd: import.meta.env.PROD, + localBuildId: __APP_BUILD_ID__, + localVersion: __APP_VERSION__, + baseUrl: import.meta.env.BASE_URL, + now: () => Date.now(), + fetchRemote: async (url) => { + const res = await fetch(url, { cache: 'no-store' }) + if (!res.ok) throw new Error(String(res.status)) + return res.json() + }, + onReload: () => { + location.reload() + }, + alertUpdate: async (message) => { + await ElMessageBox.alert(message, '发现新版本', { + confirmButtonText: '立即刷新', + showClose: false, + closeOnClickModal: false, + closeOnPressEscape: false, + showCancelButton: false, + dangerouslyUseHTMLString: true + }) + }, + setInterval: (fn, ms) => window.setInterval(fn, ms), + clearInterval: (id) => window.clearInterval(id), + subscribeVisible: (handler) => { + const listener = () => { + if (document.visibilityState === 'visible') handler() + } + document.addEventListener('visibilitychange', listener) + return () => document.removeEventListener('visibilitychange', listener) + } + }) +} diff --git a/web-admin/vite.config.ts b/web-admin/vite.config.ts index a3db01acf..541eda12e 100644 --- a/web-admin/vite.config.ts +++ b/web-admin/vite.config.ts @@ -1,6 +1,8 @@ +import { execSync } from 'node:child_process' import { defineConfig, loadEnv } from 'vite' import vue from '@vitejs/plugin-vue' import path from 'path' +import { createAppBuildMeta, readGitShort, writeVersionJson } from './scripts/app-build-meta' import { fileURLToPath } from 'url' import vueDevTools from 'vite-plugin-vue-devtools' import viteCompression from 'vite-plugin-compression' @@ -17,13 +19,20 @@ export default ({ mode }: { mode: string }) => { const env = loadEnv(mode, root) const { VITE_VERSION, VITE_PORT, VITE_BASE_URL, VITE_API_URL, VITE_API_PROXY_URL } = env + const gitShort = readGitShort(() => + execSync('git rev-parse --short HEAD', { + stdio: ['ignore', 'pipe', 'ignore'] + }).toString() + ) + const appBuildMeta = createAppBuildMeta(VITE_VERSION || '', Date.now(), gitShort) + console.log(`🚀 API_URL = ${VITE_API_URL}`) console.log(`🚀 VERSION = ${VITE_VERSION}`) return defineConfig({ define: { __APP_VERSION__: JSON.stringify(VITE_VERSION), - // html-to-docx 等依赖会读 Node 全局;浏览器映射到 globalThis + __APP_BUILD_ID__: JSON.stringify(appBuildMeta.buildId), global: 'globalThis' }, base: VITE_BASE_URL, @@ -88,6 +97,13 @@ export default ({ mode }: { mode: string }) => { } }, plugins: [ + { + name: 'emit-version-json', + apply: 'build', + closeBundle() { + writeVersionJson(path.resolve(root, 'dist'), appBuildMeta) + } + }, vue(), tailwindcss(), // 自动按需导入 API