fix(web): 防止页面异常阻塞路由切换
This commit is contained in:
1 parent
8a8a96a012
commit
6ff5c6e949
6 files changed
+108
-3
No files matched your search
@@ -0,0 +1,45 @@
|
||||
<script setup lang="ts">
|
||||
import { onErrorCaptured, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
const router = useRouter()
|
||||
const error = ref<unknown>(null)
|
||||
|
||||
onErrorCaptured((caughtError, _instance, info) => {
|
||||
error.value = caughtError
|
||||
console.error('[RouteErrorBoundary]', info, caughtError)
|
||||
return false
|
||||
})
|
||||
|
||||
function reloadPage() {
|
||||
window.location.reload()
|
||||
}
|
||||
|
||||
async function goToDashboard() {
|
||||
error.value = null
|
||||
await router.push({ name: 'AdminDashboard' })
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<slot v-if="!error" />
|
||||
<div v-else class="route-error">
|
||||
<AResult status="error" title="页面加载失败" subtitle="页面数据异常,请重试或返回仪表盘">
|
||||
<template #extra>
|
||||
<ASpace>
|
||||
<AButton type="primary" @click="reloadPage">重新加载</AButton>
|
||||
<AButton @click="goToDashboard">返回仪表盘</AButton>
|
||||
</ASpace>
|
||||
</template>
|
||||
</AResult>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.route-error {
|
||||
display: flex;
|
||||
min-height: min(520px, calc(100vh - 160px));
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
@@ -27,6 +27,9 @@ console.log(
|
||||
)
|
||||
|
||||
const app = createApp(App)
|
||||
app.config.errorHandler = (error, _instance, info) => {
|
||||
console.error('[VueError]', info, error)
|
||||
}
|
||||
app.use(createPinia())
|
||||
app.use(ArcoVue)
|
||||
app.use(router)
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Message } from '@arco-design/web-vue'
|
||||
import axios from 'axios'
|
||||
import { marked } from 'marked'
|
||||
import request from '@/utils/request'
|
||||
import RouteErrorBoundary from '@/components/RouteErrorBoundary.vue'
|
||||
import { useWatermark } from '@/composables/useWatermark'
|
||||
import { useSiteName } from '@/composables/useSiteName'
|
||||
import {
|
||||
@@ -610,9 +611,11 @@ onUnmounted(() => {
|
||||
|
||||
<!-- Content -->
|
||||
<main class="admin-content page-bg">
|
||||
<router-view v-slot="{ Component }">
|
||||
<router-view v-slot="{ Component, route: currentRoute }">
|
||||
<Transition name="page-fade" mode="out-in">
|
||||
<component :is="Component" />
|
||||
<RouteErrorBoundary :key="currentRoute.fullPath">
|
||||
<component :is="Component" />
|
||||
</RouteErrorBoundary>
|
||||
</Transition>
|
||||
</router-view>
|
||||
</main>
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<script setup lang="ts">
|
||||
import { onErrorCaptured, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import Icon from '@/components/common/Icon.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const error = ref<unknown>(null)
|
||||
|
||||
onErrorCaptured((caughtError, _instance, info) => {
|
||||
error.value = caughtError
|
||||
console.error('[RouteErrorBoundary]', info, caughtError)
|
||||
return false
|
||||
})
|
||||
|
||||
function reloadPage() {
|
||||
window.location.reload()
|
||||
}
|
||||
|
||||
async function goToDashboard() {
|
||||
error.value = null
|
||||
await router.push({ name: 'TenantDashboard' })
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<slot v-if="!error" />
|
||||
<div v-else class="flex min-h-[min(520px,calc(100vh-10rem))] items-center justify-center px-4">
|
||||
<div class="w-full max-w-lg text-center">
|
||||
<div class="mx-auto flex h-14 w-14 items-center justify-center rounded-full bg-red-50 text-red-500">
|
||||
<Icon name="exclamationCircle" size="xl" />
|
||||
</div>
|
||||
<h2 class="mt-5 text-lg font-semibold text-gray-900">页面加载失败</h2>
|
||||
<p class="mt-2 text-sm text-gray-500">页面数据异常,请重试或返回仪表盘</p>
|
||||
<div class="mt-6 flex flex-wrap justify-center gap-3">
|
||||
<button type="button" class="btn btn-primary" @click="reloadPage">
|
||||
<Icon name="refresh" size="sm" />
|
||||
重新加载
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary" @click="goToDashboard">
|
||||
<Icon name="chart" size="sm" />
|
||||
返回仪表盘
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -29,6 +29,9 @@ console.log(
|
||||
)
|
||||
|
||||
const app = createApp(App)
|
||||
app.config.errorHandler = (error, _instance, info) => {
|
||||
console.error('[VueError]', info, error)
|
||||
}
|
||||
app.use(head)
|
||||
app.use(createPinia())
|
||||
app.use(router)
|
||||
|
||||
@@ -10,6 +10,7 @@ import { toast } from '@/utils/toast'
|
||||
import Icon from '@/components/common/Icon.vue'
|
||||
import MaintenanceBanner from '@/components/common/MaintenanceBanner.vue'
|
||||
import AnnouncementBanner from '@/components/common/AnnouncementBanner.vue'
|
||||
import RouteErrorBoundary from '@/components/common/RouteErrorBoundary.vue'
|
||||
import { marked } from 'marked'
|
||||
import request from '@/utils/request'
|
||||
|
||||
@@ -510,7 +511,11 @@ onBeforeUnmount(() => {
|
||||
class="tenant-main px-3 pb-5 pt-4 md:px-5 md:pb-6 lg:pt-5"
|
||||
:class="{ 'tenant-table-main': route.meta.viewportTable }"
|
||||
>
|
||||
<router-view />
|
||||
<router-view v-slot="{ Component, route: currentRoute }">
|
||||
<RouteErrorBoundary :key="currentRoute.fullPath">
|
||||
<component :is="Component" />
|
||||
</RouteErrorBoundary>
|
||||
</router-view>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user