Files
2fauth-worker/frontend/src/shared/utils/asyncHelper.js
T

142 lines
5.2 KiB
JavaScript

import { defineAsyncComponent, h, onMounted, defineComponent } from 'vue'
import { ElEmpty, ElButton } from 'element-plus'
import { i18n } from '@/locales'
/**
* 增强的异步组件工厂函数
* 解决 PWA/Cloudflare 环境下分块加载失败导致的白屏问题
*/
export function createAsyncComponent(loader) {
return defineAsyncComponent({
loader: async () => {
console.log('[AsyncComponent] Starting to load component...')
try {
const comp = await loader()
console.log('[AsyncComponent] Component loaded successfully')
return comp
} catch (err) {
console.error('[AsyncComponent] Component load failed:', err)
throw err
}
},
// 加载中占位
loadingComponent: defineComponent({
name: 'AsyncLoading',
setup() {
const t = (key) => {
try {
return i18n.global.t(key)
} catch (e) {
return key
}
}
// 使用 CSS 关键帧动画注入(最轻量、无副作用)
const injectStyles = () => {
if (document.getElementById('async-loading-styles')) return
const style = document.createElement('style')
style.id = 'async-loading-styles'
style.textContent = `
@keyframes async-progress-flow {
0% { transform: translateX(-100%); }
100% { transform: translateX(100%); }
}
.async-progress-bar {
width: 200px;
height: 3px;
background: rgba(64, 158, 255, 0.1);
border-radius: 4px;
overflow: hidden;
position: relative;
margin-top: 16px;
}
.async-progress-inner {
position: absolute;
top: 0;
left: 0;
width: 50%;
height: 100%;
background: var(--el-color-primary);
border-radius: 4px;
animation: async-progress-flow 1.5s infinite ease-in-out;
box-shadow: 0 0 8px var(--el-color-primary);
}
`
document.head.appendChild(style)
}
onMounted(injectStyles)
return () => h('div', {
style: {
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
minHeight: '400px',
width: '100%',
backgroundColor: 'transparent'
}
}, [
// 文案优化 - 适配多语言
h('div', {
style: {
fontSize: '16px',
color: 'var(--el-text-color-regular)',
fontWeight: '500',
letterSpacing: '1px'
}
}, t('common.resource_loading')),
// 线性进度条
h('div', { class: 'async-progress-bar' }, [
h('div', { class: 'async-progress-inner' })
])
])
}
}),
// 失败占位
errorComponent: defineComponent({
name: 'AsyncError',
props: ['error'],
setup(props) {
console.error('[AsyncComponent] Rendering error component:', props.error)
const t = (key) => {
try {
return i18n.global.t(key)
} catch (e) {
return key
}
}
return () => h('div', {
style: { padding: '40px', textAlign: 'center' }
}, [
h(ElEmpty, { description: t('common.loading_failed') }, {
default: () => h(ElButton, {
type: 'primary',
onClick: () => window.location.reload()
}, t('common.refresh_now'))
})
])
}
}),
// 延迟显示加载组件 (防止闪烁)
delay: 200,
// 超时设置 (15秒)
timeout: 15000,
// 错误重试逻辑
onError(error, retry, fail, attempts) {
if (attempts <= 2) {
console.warn(`[AsyncComponent] Loading failed, retrying (${attempts}/2)...`, error)
setTimeout(() => retry(), 500)
} else {
fail()
}
}
})
}