diff --git a/package.json b/package.json index ce6eca5..53aefe9 100644 --- a/package.json +++ b/package.json @@ -10,10 +10,10 @@ "dev": "vite --mode development --host", "dev:test": "vite --mode test --host", "dev:prod": "vite --mode production --host", - "build": "vite build --mode production", - "build:dev": "vite build --mode development", - "build:test": "vite build --mode test", - "build:prod": "vite build --mode production", + "build": "vite build --mode production && node scripts/generate-version.js", + "build:dev": "vite build --mode development && node scripts/generate-version.js", + "build:test": "vite build --mode test && node scripts/generate-version.js", + "build:prod": "vite build --mode production && node scripts/generate-version.js", "preview": "vite preview", "lint": "eslint . --fix" }, diff --git a/scripts/generate-version.js b/scripts/generate-version.js new file mode 100644 index 0000000..debd352 --- /dev/null +++ b/scripts/generate-version.js @@ -0,0 +1,43 @@ +// scripts/generate-version.js +import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs' +import { resolve, dirname } from 'path' +import { fileURLToPath } from 'url' + +// 获取当前目录 +const __filename = fileURLToPath(import.meta.url) +const __dirname = dirname(__filename) + +// 读取 .env 文件获取版本号 +const envPath = resolve(__dirname, '../.env') +let version = '1.0.0' + +try { + if (existsSync(envPath)) { + const envContent = readFileSync(envPath, 'utf-8') + const versionMatch = envContent.match(/VITE_APP_VERSION=([^\r\n]+)/) + if (versionMatch && versionMatch[1]) { + version = versionMatch[1].replace(/['"]/g, '') + } + } +} catch (error) { + console.warn('无法读取 .env 文件,使用默认版本号:', error.message) +} + +// 生成版本信息 +const versionInfo = { + version: version, + buildTime: new Date().toISOString(), + timestamp: Date.now(), +} + +// 确保 dist 目录存在 +const distDir = resolve(__dirname, '../dist') +if (!existsSync(distDir)) { + mkdirSync(distDir, { recursive: true }) +} + +// 写入 version.json +const outputPath = resolve(distDir, 'version.json') +writeFileSync(outputPath, JSON.stringify(versionInfo, null, 2)) + +console.log(`✅ 版本文件已生成: ${versionInfo.version} (${versionInfo.buildTime})`) diff --git a/src/main.js b/src/main.js index 38d5d98..11a3b4c 100644 --- a/src/main.js +++ b/src/main.js @@ -13,13 +13,12 @@ import { useLangStore } from '@/stores/lang' // 移动到顶部 logEnvInfo() // 版本检查 -if (versionChecker.checkVersion()) { - console.log('检测到新版本,正在更新...') - // 可以选择显示一个更新提示 - // if (confirm('检测到新版本,是否立即更新?')) { - // versionChecker.forceReload() - // } -} +versionChecker.checkVersion().then((hasUpdate) => { + if (hasUpdate) { + console.log('检测到新版本,正在更新...') + versionChecker.showUpdatePrompt() + } +}) // 初始化权限系统 console.debug('🔐 Permission system initialized') @@ -38,3 +37,13 @@ initI18n(langStore) app.use(i18n) app.mount('#app') + +// 定期检查版本更新(每5分钟) +if (versionChecker.enableCheck) { + setInterval(async () => { + const hasUpdate = await versionChecker.checkServerVersion() + if (hasUpdate) { + versionChecker.showUpdatePrompt() + } + }, 5 * 60 * 1000) +} diff --git a/src/utils/versionChecker.js b/src/utils/versionChecker.js index 7718b6e..f1f4cb5 100644 --- a/src/utils/versionChecker.js +++ b/src/utils/versionChecker.js @@ -3,61 +3,159 @@ export class VersionChecker { constructor() { this.currentVersion = import.meta.env.VITE_APP_VERSION || Date.now().toString() this.storageKey = 'h5_app_version' + this.serverVersionKey = 'app_server_version' + this.enableCheck = import.meta.env.VITE_ENABLE_VERSION_CHECK === 'true' } - // 检查版本更新 - checkVersion() { - const storedVersion = localStorage.getItem(this.storageKey) - - if (storedVersion !== this.currentVersion) { - // 版本不匹配,清除缓存并刷新 - this.clearCache() - localStorage.setItem(this.storageKey, this.currentVersion) - - // 延迟刷新,确保缓存清理完成 - setTimeout(() => { - this.forceReload() - }, 100) - - return true // 需要刷新 + // 检查服务器版本 + async checkServerVersion() { + if (!this.enableCheck) { + return false } - - return false // 不需要刷新 + + try { + // 强制从服务器获取最新版本信息,避免缓存 + const response = await fetch(`/version.json?t=${Date.now()}`, { + cache: 'no-cache', + headers: { + 'Cache-Control': 'no-cache', + Pragma: 'no-cache', + }, + }) + + if (!response.ok) { + console.warn('无法获取服务器版本信息') + return false + } + + const versionInfo = await response.json() + const serverVersion = versionInfo.version + const storedServerVersion = localStorage.getItem(this.serverVersionKey) + + // 更新服务器版本信息 + localStorage.setItem(this.serverVersionKey, serverVersion) + + // 比较版本 + if (storedServerVersion && storedServerVersion !== serverVersion) { + console.log(`检测到服务器版本更新: ${storedServerVersion} -> ${serverVersion}`) + return true + } + + return false + } catch (error) { + console.warn('版本检查失败:', error) + return false + } + } + + // 检查版本更新(综合方法) + async checkVersion() { + // 首先检查服务器版本 + const hasServerUpdate = await this.checkServerVersion() + + if (hasServerUpdate) { + this.handleVersionUpdate() + return true + } + + // 检查本地版本是否匹配 + const storedLocalVersion = localStorage.getItem(this.storageKey) + + if (storedLocalVersion !== this.currentVersion) { + // 本地版本变化,可能是首次加载或本地版本升级 + localStorage.setItem(this.storageKey, this.currentVersion) + console.log(`本地版本更新到: ${this.currentVersion}`) + } + + return false + } + + // 处理版本更新 + handleVersionUpdate() { + console.log('检测到新版本,正在清理缓存...') + this.clearCache() + localStorage.setItem(this.storageKey, this.currentVersion) + localStorage.setItem(this.serverVersionKey, this.currentVersion) } // 清除应用缓存 clearCache() { - // 清除localStorage(保留必要的数据) - const keysToKeep = ['user_token', 'user_info'] // 保留的关键数据 - const allKeys = Object.keys(localStorage) - - allKeys.forEach(key => { - if (!keysToKeep.includes(key)) { - localStorage.removeItem(key) + // 保存需要保留的数据 + const preservedData = {} + const keysToPreserve = ['user_token', 'user_info', 'lang'] + + keysToPreserve.forEach((key) => { + const value = localStorage.getItem(key) + if (value !== null) { + preservedData[key] = value } }) + // 清除所有localStorage + localStorage.clear() + + // 恢复保留的数据 + Object.keys(preservedData).forEach((key) => { + localStorage.setItem(key, preservedData[key]) + }) + // 清除sessionStorage sessionStorage.clear() - // 如果支持Service Worker,清除SW缓存 - if ('serviceWorker' in navigator) { - caches.keys().then(names => { - names.forEach(name => { - caches.delete(name) + // 清除所有缓存 + if ('caches' in window) { + caches + .keys() + .then((names) => { + names.forEach((name) => { + caches.delete(name) + }) + }) + .catch((err) => { + console.warn('清除缓存失败:', err) + }) + } + + // 注销Service Worker(如果存在) + if ('serviceWorker' in navigator) { + navigator.serviceWorker + .getRegistrations() + .then((registrations) => { + registrations.forEach((registration) => { + registration.unregister() + }) + }) + .catch((err) => { + console.warn('注销Service Worker失败:', err) }) - }) } } // 强制刷新页面 forceReload() { - // 尝试硬刷新(清除缓存) - if (window.location.reload) { - window.location.reload(true) - } else { - // 备用方案:重新加载页面 - window.location.href = window.location.href + '?t=' + Date.now() + // 尝试多种刷新方式确保生效 + try { + // 方法1: 强制重新加载 + if (window.location.reload) { + window.location.reload(true) + } + + // 方法2: 添加时间戳重新导航 + setTimeout(() => { + window.location.href = + window.location.origin + window.location.pathname + '?v=' + Date.now() + }, 100) + } catch (error) { + // 方法3: 最后的备用方案 + window.location.href = window.location.href + } + } + + // 显示更新提示 + showUpdatePrompt() { + const shouldUpdate = confirm('发现新版本,是否立即更新?更新将获得更好的体验。') + if (shouldUpdate) { + this.forceReload() } }