diff --git a/nginx-k3s.conf b/nginx-k3s.conf index 6646a5d..c153901 100644 --- a/nginx-k3s.conf +++ b/nginx-k3s.conf @@ -55,109 +55,118 @@ server { } location = /login { - try_files /index.html =200; + try_files $uri @spa; } location = /login/ { - try_files /index.html =200; + try_files $uri @spa; } rewrite ^/admin/?$ /index.html last; location = /admin { - try_files /index.html =200; + try_files $uri @spa; } location = /admin/ { - try_files /index.html =200; + try_files $uri @spa; } location ~ ^/api/(upstreamInterface|upstreamInterfaceConfig|upstreamInterfaceConfigEdit|downstreamInterface(|/newPort)|appList(|/createApplication)|requestLog|callStatistics|apiDebugger|apiTest)(/|$) { - try_files /index.html =200; + try_files $uri @spa; } - location ^~ /xhs/startService { try_files /index.html =200; } - location ^~ /xhs/creation { try_files /index.html =200; } - location ^~ /xhs/publishHistory { try_files /index.html =200; } - location ^~ /xhs/skills { try_files /index.html =200; } - location ^~ /xhs/humanSettings { try_files /index.html =200; } - location ^~ /xhs/agentPair { try_files /index.html =200; } - location = /xhs/analysis { try_files /index.html =200; } - location = /xhs/analysis/ { try_files /index.html =200; } + location ^~ /xhs/startService { try_files $uri @spa; } + location ^~ /xhs/creation { try_files $uri @spa; } + location ^~ /xhs/publishHistory { try_files $uri @spa; } + location ^~ /xhs/skills { try_files $uri @spa; } + location ^~ /xhs/humanSettings { try_files $uri @spa; } + location ^~ /xhs/agentPair { try_files $uri @spa; } + location = /xhs/analysis { try_files $uri @spa; } + location = /xhs/analysis/ { try_files $uri @spa; } - location ^~ /wechat/register { try_files /index.html =200; } - location ^~ /wechat/bind { try_files /index.html =200; } - location ^~ /h5 { try_files /index.html =200; } - location ^~ /track/query { try_files /index.html =200; } - location ^~ /operation/inboundScan { try_files /index.html =200; } - location ^~ /operation/outboundScan { try_files /index.html =200; } - location ^~ /operation/printLabel { try_files /index.html =200; } - location ^~ /operation/parcelArrival { try_files /index.html =200; } - location ^~ /operation/fulfillPack { try_files /index.html =200; } + location ^~ /wechat/register { try_files $uri @spa; } + location ^~ /wechat/bind { try_files $uri @spa; } + location ^~ /h5 { try_files $uri @spa; } + location ^~ /track/query { try_files $uri @spa; } + location ^~ /operation/inboundScan { try_files $uri @spa; } + location ^~ /operation/outboundScan { try_files $uri @spa; } + location ^~ /operation/printLabel { try_files $uri @spa; } + location ^~ /operation/parcelArrival { try_files $uri @spa; } + location ^~ /operation/fulfillPack { try_files $uri @spa; } # 订单前端路由(^~ 须比下方 /order/ API 正则代理更优先) - location ^~ /order/priceCheck { try_files /index.html =200; } - location ^~ /order/placeOrder { try_files /index.html =200; } - location ^~ /order/myOrders { try_files /index.html =200; } - location ^~ /order/fulfillOrders { try_files /index.html =200; } - location ^~ /order/waybillManage { try_files /index.html =200; } - location ^~ /order/pickupOrder { try_files /index.html =200; } - location ^~ /order/customs9610Order { try_files /index.html =200; } - location ^~ /order/batchJobs { try_files /index.html =200; } - location ^~ /order/templateMapping { try_files /index.html =200; } - location ^~ /order/platformStore { try_files /index.html =200; } - location ^~ /order/platformStoreOrder { try_files /index.html =200; } + location ^~ /order/priceCheck { try_files $uri @spa; } + location ^~ /order/placeOrder { try_files $uri @spa; } + location ^~ /order/myOrders { try_files $uri @spa; } + location ^~ /order/fulfillOrders { try_files $uri @spa; } + location ^~ /order/waybillManage { try_files $uri @spa; } + location ^~ /order/pickupOrder { try_files $uri @spa; } + location ^~ /order/customs9610Order { try_files $uri @spa; } + location ^~ /order/batchJobs { try_files $uri @spa; } + location ^~ /order/templateMapping { try_files $uri @spa; } + location ^~ /order/platformStore { try_files $uri @spa; } + location ^~ /order/platformStoreOrder { try_files $uri @spa; } # 知识库前端路由(^~ 须比下方 /kb/ API 代理更优先) - location ^~ /kb/category { try_files /index.html =200; } - location ^~ /kb/article { try_files /index.html =200; } + location ^~ /kb/category { try_files $uri @spa; } + location ^~ /kb/article { try_files $uri @spa; } - location = /warehouse/parcel { try_files /index.html =200; } - location = /warehouse/inventory { try_files /index.html =200; } - location = /warehouse/sku { try_files /index.html =200; } - location = /warehouse/binLocation { try_files /index.html =200; } - location ^~ /warehouse/inboundNotice { try_files /index.html =200; } - location ^~ /Plugin/ { try_files /index.html =200; } + location = /warehouse/parcel { try_files $uri @spa; } + location = /warehouse/inventory { try_files $uri @spa; } + location = /warehouse/sku { try_files $uri @spa; } + location = /warehouse/binLocation { try_files $uri @spa; } + location ^~ /warehouse/inboundNotice { try_files $uri @spa; } + location ^~ /Plugin/ { try_files $uri @spa; } - location ^~ /channel/salesProducts { try_files /index.html =200; } - location ^~ /channel/serviceChannels { try_files /index.html =200; } - location ^~ /channel/priceArea { try_files /index.html =200; } - location ^~ /channel/blacklist { try_files /index.html =200; } - location ^~ /channel/remoteZones { try_files /index.html =200; } - location ^~ /channel/weightRules { try_files /index.html =200; } - location ^~ /channel/weightRounding { try_files /index.html =200; } - location ^~ /channel/autoTrigger { try_files /index.html =200; } - location ^~ /channel/issueTemplate { try_files /index.html =200; } - location ^~ /channel/labelDesign { try_files /index.html =200; } + location ^~ /channel/salesProducts { try_files $uri @spa; } + location ^~ /channel/serviceChannels { try_files $uri @spa; } + location ^~ /channel/priceArea { try_files $uri @spa; } + location ^~ /channel/blacklist { try_files $uri @spa; } + location ^~ /channel/remoteZones { try_files $uri @spa; } + location ^~ /channel/weightRules { try_files $uri @spa; } + location ^~ /channel/weightRounding { try_files $uri @spa; } + location ^~ /channel/autoTrigger { try_files $uri @spa; } + location ^~ /channel/issueTemplate { try_files $uri @spa; } + location ^~ /channel/labelDesign { try_files $uri @spa; } # 营销前端路由(^~ 须比下方 /marketing/ API 正则代理更优先) - location ^~ /marketing/memberLevel { try_files /index.html =200; } - location ^~ /marketing/pointsRule { try_files /index.html =200; } - location ^~ /marketing/pointsFlow { try_files /index.html =200; } - location ^~ /marketing/couponTemplate { try_files /index.html =200; } - location ^~ /marketing/couponGrant { try_files /index.html =200; } - location ^~ /marketing/commissionRule { try_files /index.html =200; } - location ^~ /marketing/commissionDetail { try_files /index.html =200; } - location ^~ /marketing/commissionWithdraw { try_files /index.html =200; } - location ^~ /marketing/referralTree { try_files /index.html =200; } + location ^~ /marketing/memberLevel { try_files $uri @spa; } + location ^~ /marketing/pointsRule { try_files $uri @spa; } + location ^~ /marketing/pointsFlow { try_files $uri @spa; } + location ^~ /marketing/couponTemplate { try_files $uri @spa; } + location ^~ /marketing/couponGrant { try_files $uri @spa; } + location ^~ /marketing/commissionRule { try_files $uri @spa; } + location ^~ /marketing/commissionDetail { try_files $uri @spa; } + location ^~ /marketing/commissionWithdraw { try_files $uri @spa; } + location ^~ /marketing/referralTree { try_files $uri @spa; } # 系统管理前端路由(^~ 须比下方 /system/ API 正则代理更优先) - location = /system/dict { try_files /index.html =200; } - location = /system/warehouseAddress { try_files /index.html =200; } - location = /system/announcement { try_files /index.html =200; } - location ^~ /system/users { try_files /index.html =200; } - location ^~ /system/roles { try_files /index.html =200; } - location ^~ /system/menus { try_files /index.html =200; } - location ^~ /system/organizations { try_files /index.html =200; } - location ^~ /system/batchEmail { try_files /index.html =200; } - location ^~ /system/otherConfig { try_files /index.html =200; } - location ^~ /system/aiAssistant { try_files /index.html =200; } - location ^~ /system/aiSkills { try_files /index.html =200; } - location ^~ /system/mcpTools { try_files /index.html =200; } - location ^~ /system/dataPerm/ { try_files /index.html =200; } + location = /system/dict { try_files $uri @spa; } + location = /system/warehouseAddress { try_files $uri @spa; } + location = /system/announcement { try_files $uri @spa; } + location ^~ /system/users { try_files $uri @spa; } + location ^~ /system/roles { try_files $uri @spa; } + location ^~ /system/menus { try_files $uri @spa; } + location ^~ /system/organizations { try_files $uri @spa; } + location ^~ /system/batchEmail { try_files $uri @spa; } + location ^~ /system/otherConfig { try_files $uri @spa; } + location ^~ /system/aiAssistant { try_files $uri @spa; } + location ^~ /system/aiSkills { try_files $uri @spa; } + location ^~ /system/mcpTools { try_files $uri @spa; } + location ^~ /system/dataPerm/ { try_files $uri @spa; } + + # SPA 回退:try_files $uri @spa 统一带 no-cache,避免部署后浏览器缓存旧 index.html + location @spa { + add_header Cache-Control "no-cache, no-store, must-revalidate" always; + add_header Pragma "no-cache" always; + add_header Expires "0" always; + try_files /index.html =404; + } location = /index.html { - add_header Cache-Control "no-cache, no-store, must-revalidate"; - add_header Pragma "no-cache"; - add_header Expires "0"; + add_header Cache-Control "no-cache, no-store, must-revalidate" always; + add_header Pragma "no-cache" always; + add_header Expires "0" always; + try_files /index.html =404; } # 微信 JS 安全域名校验:原样返回 txt,禁止 SPA 回落 index.html @@ -176,7 +185,6 @@ server { } location / { - index index.html; - try_files $uri /index.html; + try_files $uri @spa; } } diff --git a/src/main.ts b/src/main.ts index fb5e954..56abfb1 100644 --- a/src/main.ts +++ b/src/main.ts @@ -14,6 +14,7 @@ import { } from './plugins/heavy-plugins'; import { initAuthKeepAlive } from '@/utils/auth-keepalive'; import { installWeChatH5HistoryGuard } from '@/utils/device'; +import { installChunkReloadHandlers, reloadOnChunkLoadError } from '@/utils/chunkReload'; import { setAuthToken } from './api/rbac'; import { useRbacUserStore } from '@/store/modules/rbac-user'; import { applyRbacUserToUserStore } from '@/utils/enter-system'; @@ -75,6 +76,11 @@ const app = createApp(App); app.use(ArcoVue, {}); app.use(ArcoVueIcon); app.use(router); +installChunkReloadHandlers(); +router.onError((error) => { + if (reloadOnChunkLoadError(error)) return; + throw error; +}); app.use(store); useAppStore().hydrateTheme(); app.use(i18n); diff --git a/src/utils/chunkReload.ts b/src/utils/chunkReload.ts new file mode 100644 index 0000000..48d57df --- /dev/null +++ b/src/utils/chunkReload.ts @@ -0,0 +1,42 @@ +/** 部署后旧 chunk 404 时自动刷新,避免 lazy route 白屏 */ + +const RELOAD_KEY = 'oms_chunk_reload_ts'; +const RELOAD_COOLDOWN_MS = 15_000; + +export function isChunkLoadError(error: unknown): boolean { + const msg = error instanceof Error ? error.message : String(error ?? ''); + return ( + /Failed to fetch dynamically imported module/i.test(msg) || + /Importing a module script failed/i.test(msg) || + /Loading chunk [\da-zA-Z_-]+ failed/i.test(msg) || + /ChunkLoadError/i.test(msg) + ); +} + +/** 检测到 chunk 加载失败则刷新页面;10s 内仅刷新一次,防止死循环 */ +export function reloadOnChunkLoadError(error: unknown): boolean { + if (typeof window === 'undefined' || !isChunkLoadError(error)) { + return false; + } + try { + const last = Number(sessionStorage.getItem(RELOAD_KEY) || 0); + const now = Date.now(); + if (now - last < RELOAD_COOLDOWN_MS) { + return false; + } + sessionStorage.setItem(RELOAD_KEY, String(now)); + } catch { + /* ignore */ + } + window.location.reload(); + return true; +} + +export function installChunkReloadHandlers(): void { + if (typeof window === 'undefined') return; + window.addEventListener('unhandledrejection', (event) => { + if (reloadOnChunkLoadError(event.reason)) { + event.preventDefault(); + } + }); +}