多维表格
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

157 lines
4.5 KiB

<script lang="ts">
// modified version of default NuxtErrorBoundary component - https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/components/nuxt-error-boundary.ts
import { message } from 'ant-design-vue'
import * as Sentry from '@sentry/vue'
const MESSAGE_KEY = 'ErrorMessageKey'
export default {
emits: {
error(_error: unknown) {
return true
},
},
setup(_props, { emit }) {
const nuxtApp = useNuxtApp()
const error = ref()
const prevError = ref()
const errModal = computed(() => !!error.value)
const key = ref(0)
const repeated: Record<string, number> = {}
const isErrorExpanded = ref(false)
const { copy } = useCopy()
const reload = () => {
error.value = null
key.value++
// destroy the toast message
message.destroy(MESSAGE_KEY)
}
const navigateToHome = () => {
error.value = null
location.hash = '/'
location.reload()
}
const close = () => {
error.value = null
// destroy the toast message
message.destroy(MESSAGE_KEY)
}
onErrorCaptured((err) => {
if (import.meta.client && (!nuxtApp.isHydrating || !nuxtApp.payload.serverRendered)) {
console.error('UI Error :', err)
emit('error', err)
error.value = err
repeated[err.message] = (repeated[err.message] || 0) + 1
// reset repeated count after 30 seconds
setTimeout(() => {
repeated[err.message] = 0
}, 30000)
try {
Sentry.captureException(err)
} catch {
// ignore
}
// destroy any previous toast message to avoid duplicate messages
message.destroy(MESSAGE_KEY)
message.open({
key: MESSAGE_KEY,
content: h('div', [
h(
'div',
{
class: 'flex gap-3 py-1.5',
},
[
h(resolveComponent('GeneralIcon'), { icon: 'error', class: 'text-2xl text-red-500 -mt-1' }),
h('div', { class: 'text-left flex flex-col gap-1' }, [
h('div', { class: 'font-weight-bold' }, 'Page Loading Error'),
h('div', [h('span', { class: 'text-sm text-gray-500' }, 'Something went wrong while loading page!')]),
]),
h(
'div',
{
class: 'flex gap-1 justify-end',
},
[
repeated[err.message] > 2
? h(
resolveComponent('NcButton'),
{
onClick: navigateToHome,
type: 'text',
size: 'xsmall',
class: '!text-sm !px-2 !text-primary',
},
'Home',
)
: h(
resolveComponent('NcButton'),
{
onClick: reload,
type: 'text',
size: 'xsmall',
class: '!text-sm !px-2 !text-primary',
},
'Reload',
),
h(
resolveComponent('NcButton'),
{
onClick: close,
type: 'text',
size: 'xsmall',
class: 'flex items-center gap-1',
},
[h(resolveComponent('GeneralIcon'), { icon: 'close', class: '' })],
),
],
),
],
),
]),
duration: 5,
style: {
position: 'fixed',
bottom: '20px',
right: '20px',
},
})
return false
}
})
const copyError = async () => {
try {
if (error.value) await copy(`message: ${error.value.message}\n\n${error.value.stack}`)
message.info('Error message copied to clipboard.')
} catch (e) {
message.error('Something went wrong while copying to clipboard, please copy from browser console.')
}
}
return {
errModal,
error,
key,
isErrorExpanded,
prevError,
copyError,
reload,
navigateToHome,
}
},
}
</script>
<template>
<slot :key="key"></slot>
</template>