<!-- 布局内容 -->
|
<template>
|
<div class="layout-content" :class="{ 'overflow-auto': isFullPage }" :style="containerStyle">
|
<div id="app-content-header">
|
<!-- 节日滚动 -->
|
<ArtFestivalTextScroll v-if="!isFullPage" />
|
|
<!-- 路由信息调试 -->
|
<div
|
v-if="isOpenRouteInfo === 'true'"
|
class="px-2 py-1.5 mb-3 text-sm text-g-500 bg-g-200 border-full-d rounded-md"
|
>
|
router meta:{{ route.meta }}
|
</div>
|
</div>
|
|
<div v-if="routeRenderError" :key="route.fullPath" class="art-page-view art-route-state">
|
<div class="art-route-state__panel">
|
<div class="art-route-state__title">{{ t('message.routeRenderFailedTitle') }}</div>
|
<div class="art-route-state__desc">{{ routeRenderError }}</div>
|
<ElButton type="primary" @click="reloadCurrentRoute">
|
{{ t('common.actions.reload') }}
|
</ElButton>
|
</div>
|
</div>
|
|
<RouterView v-else-if="isRefresh" v-slot="{ Component, route }">
|
<!-- 缓存路由动画 -->
|
<Transition :name="showTransitionMask ? '' : actualTransition" mode="out-in" appear>
|
<div v-if="route.meta.keepAlive" :key="route.path" class="art-page-view" :style="contentStyle">
|
<KeepAlive :max="10" :exclude="keepAliveExclude">
|
<component :is="Component" :key="route.path" />
|
</KeepAlive>
|
</div>
|
</Transition>
|
|
<!-- 非缓存路由动画 -->
|
<Transition :name="showTransitionMask ? '' : actualTransition" mode="out-in" appear>
|
<div v-if="!route.meta.keepAlive" :key="route.path" class="art-page-view" :style="contentStyle">
|
<component :is="Component" :key="route.path" />
|
</div>
|
</Transition>
|
</RouterView>
|
|
<!-- 全屏页面切换过渡遮罩(用于提升页面切换视觉体验) -->
|
<Teleport to="body">
|
<div
|
v-show="showTransitionMask"
|
class="fixed top-0 left-0 z-[2000] w-screen h-screen pointer-events-none bg-box"
|
/>
|
</Teleport>
|
</div>
|
</template>
|
<script setup>
|
import { useRoute } from 'vue-router'
|
import { useI18n } from 'vue-i18n'
|
import { useAutoLayoutHeight } from '@/hooks/core/useLayoutHeight'
|
import { useSettingStore } from '@/store/modules/setting'
|
import { useWorktabStore } from '@/store/modules/worktab'
|
defineOptions({ name: 'ArtPageContent' })
|
const { t } = useI18n()
|
const route = useRoute()
|
const { containerMinHeight } = useAutoLayoutHeight()
|
const { pageTransition, containerWidth, refresh } = storeToRefs(useSettingStore())
|
const { keepAliveExclude } = storeToRefs(useWorktabStore())
|
const isRefresh = shallowRef(true)
|
const isOpenRouteInfo = import.meta.env.VITE_OPEN_ROUTE_INFO
|
const showTransitionMask = ref(false)
|
const isFirstLoad = ref(true)
|
const routeRenderError = ref('')
|
const isFullPage = computed(() => route.matched.some((r) => r.meta?.isFullPage))
|
const prevIsFullPage = ref(isFullPage.value)
|
const actualTransition = computed(() => {
|
if (isFirstLoad.value) return ''
|
if (prevIsFullPage.value && !isFullPage.value) return ''
|
return pageTransition.value
|
})
|
watch(isFullPage, (val, oldVal) => {
|
if (val !== oldVal) {
|
showTransitionMask.value = true
|
setTimeout(() => {
|
showTransitionMask.value = false
|
}, 50)
|
}
|
nextTick(() => {
|
prevIsFullPage.value = val
|
})
|
})
|
const containerStyle = computed(() =>
|
isFullPage.value
|
? {
|
position: 'fixed',
|
top: 0,
|
left: 0,
|
width: '100%',
|
height: '100vh',
|
zIndex: 2500,
|
background: 'var(--default-bg-color)'
|
}
|
: {
|
maxWidth: containerWidth.value
|
}
|
)
|
const contentStyle = computed(() => ({
|
minHeight: containerMinHeight.value
|
}))
|
const reload = () => {
|
isRefresh.value = false
|
nextTick(() => {
|
isRefresh.value = true
|
})
|
}
|
const reloadCurrentRoute = () => {
|
routeRenderError.value = ''
|
reload()
|
}
|
watch(refresh, reload, { flush: 'post' })
|
watch(
|
() => route.fullPath,
|
() => {
|
routeRenderError.value = ''
|
}
|
)
|
onErrorCaptured((error) => {
|
routeRenderError.value =
|
error instanceof Error ? error.message : String(error || t('message.routeRenderFailed'))
|
return false
|
})
|
onMounted(() => {
|
nextTick(() => {
|
isFirstLoad.value = false
|
})
|
})
|
</script>
|
|
<style scoped>
|
.art-route-state {
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
min-height: 320px;
|
}
|
|
.art-route-state__panel {
|
width: min(560px, 100%);
|
padding: 28px 32px;
|
background: var(--default-box-color);
|
border: 1px solid var(--art-card-border);
|
border-radius: calc(var(--custom-radius) + 6px);
|
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.04);
|
}
|
|
.art-route-state__title {
|
font-size: 18px;
|
font-weight: 600;
|
color: var(--art-text-primary);
|
}
|
|
.art-route-state__desc {
|
margin-top: 8px;
|
margin-bottom: 16px;
|
color: var(--art-text-secondary);
|
line-height: 1.7;
|
word-break: break-word;
|
}
|
|
</style>
|