1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// 路由页面代码模块加载失败的自愈处理。
// Vite 重启/依赖预构建刷新、发布后旧分包失效时,页面首次 import 会失败,
// 稍后重试或整页刷新即可恢复,不需要用户手动按 F5。
const CHUNK_LOAD_ERROR = /Failed to fetch dynamically imported module|Importing a module script failed|Loading chunk .* failed|Outdated Optimize Dep/i
 
export function isChunkLoadError(error) {
  if (!error) return false
  return CHUNK_LOAD_ERROR.test(error.message || String(error))
}
 
export async function importWithRetry(loader, retries = 2, delay = 500) {
  for (let attempt = 0; ; attempt++) {
    try {
      return await loader()
    } catch (error) {
      if (attempt >= retries || !isChunkLoadError(error)) throw error
      await new Promise(resolve => setTimeout(resolve, delay * 2 ** attempt))
    }
  }
}
 
const RELOAD_STATE_KEY = 'lims-chunk-load-reload'
const RELOAD_WINDOW = 60 * 1000
const RELOAD_LIMIT = 3
 
// 重试后仍失败则自动整页刷新;60 秒内最多 3 次,避免真正故障时无限刷新。
export function autoReloadForChunkError() {
  const now = Date.now()
  let state = null
  try {
    state = JSON.parse(sessionStorage.getItem(RELOAD_STATE_KEY) || 'null')
  } catch (e) {
    state = null
  }
  if (!state || now - state.first > RELOAD_WINDOW) {
    state = { first: now, count: 0 }
  }
  if (state.count >= RELOAD_LIMIT) return false
  state.count += 1
  try {
    sessionStorage.setItem(RELOAD_STATE_KEY, JSON.stringify(state))
  } catch (e) {
    // 隐私模式等存储不可用时忽略,不影响刷新
  }
  setTimeout(() => window.location.reload(), 500)
  return true
}