committed by
GitHub
11 changed files with 419 additions and 110 deletions
@ -0,0 +1,98 @@ |
|||
import type { VNode } from 'vue'; |
|||
import type { |
|||
RouteLocationNormalizedLoaded, |
|||
RouteLocationNormalizedLoadedGeneric, |
|||
} from 'vue-router'; |
|||
|
|||
import { computed } from 'vue'; |
|||
|
|||
import { preferences, usePreferences } from '@vben/preferences'; |
|||
|
|||
/** |
|||
* 转换组件,自动添加 name |
|||
* @param component |
|||
* @param route |
|||
*/ |
|||
export function transformComponent( |
|||
component: VNode, |
|||
route: RouteLocationNormalizedLoadedGeneric, |
|||
) { |
|||
// 组件视图未找到,如果有设置后备视图,则返回后备视图,如果没有,则抛出错误
|
|||
if (!component) { |
|||
console.error( |
|||
'Component view not found,please check the route configuration', |
|||
); |
|||
return undefined; |
|||
} |
|||
|
|||
const routeName = route.name as string; |
|||
// 如果组件没有 name,则直接返回
|
|||
if (!routeName) { |
|||
return component; |
|||
} |
|||
const componentName = (component?.type as any)?.name; |
|||
|
|||
// 已经设置过 name,则直接返回
|
|||
if (componentName) { |
|||
return component; |
|||
} |
|||
|
|||
// componentName 与 routeName 一致,则直接返回
|
|||
if (componentName === routeName) { |
|||
return component; |
|||
} |
|||
|
|||
// 设置 name
|
|||
component.type ||= {}; |
|||
(component.type as any).name = routeName; |
|||
|
|||
return component; |
|||
} |
|||
|
|||
/** |
|||
* Layout相关hook |
|||
*/ |
|||
export function useLayoutHook() { |
|||
const { keepAlive } = usePreferences(); |
|||
/** |
|||
* 是否使用动画 |
|||
*/ |
|||
const getEnabledTransition = computed(() => { |
|||
const { transition } = preferences; |
|||
const transitionName = transition.name; |
|||
return transitionName && transition.enable; |
|||
}); |
|||
|
|||
/** |
|||
* 获取路由过渡动画 |
|||
* @param _route |
|||
*/ |
|||
function getTransitionName(_route: RouteLocationNormalizedLoaded) { |
|||
// 如果偏好设置未设置,则不使用动画
|
|||
const { tabbar, transition } = preferences; |
|||
const transitionName = transition.name; |
|||
if (!transitionName || !transition.enable) { |
|||
return; |
|||
} |
|||
|
|||
// 标签页未启用或者未开启缓存,则使用全局配置动画
|
|||
if (!tabbar.enable || !keepAlive) { |
|||
return transitionName; |
|||
} |
|||
|
|||
// 如果页面已经加载过,则不使用动画
|
|||
// if (route.meta.loaded) {
|
|||
// return;
|
|||
// }
|
|||
// 已经打开且已经加载过的页面不使用动画
|
|||
// const inTabs = getCachedTabs.value.includes(route.name as string);
|
|||
|
|||
// return inTabs && route.meta.loaded ? undefined : transitionName;
|
|||
return transitionName; |
|||
} |
|||
|
|||
return { |
|||
getEnabledTransition, |
|||
getTransitionName, |
|||
}; |
|||
} |
|||
@ -0,0 +1,2 @@ |
|||
export { default as RouteCachedPage } from './route-cached-page.vue'; |
|||
export { default as RouteCachedView } from './route-cached-view.vue'; |
|||
@ -0,0 +1,36 @@ |
|||
<!-- 本组件用于获取缓存的route并保存到pinia --> |
|||
<script setup lang="ts"> |
|||
import type { VNode } from 'vue'; |
|||
import type { RouteLocationNormalizedLoadedGeneric } from 'vue-router'; |
|||
|
|||
import { watch } from 'vue'; |
|||
|
|||
import { useTabbarStore } from '@vben/stores'; |
|||
|
|||
interface Props { |
|||
component?: VNode; |
|||
route: RouteLocationNormalizedLoadedGeneric; |
|||
} |
|||
|
|||
/** |
|||
* 这是页面缓存组件,不做任何的的实际渲染 |
|||
*/ |
|||
defineOptions({ |
|||
render() { |
|||
return null; |
|||
}, |
|||
}); |
|||
const props = defineProps<Props>(); |
|||
|
|||
const { addCachedRoute } = useTabbarStore(); |
|||
|
|||
watch( |
|||
() => props.route, |
|||
() => { |
|||
if (props.component && props.route.meta.domCached) { |
|||
addCachedRoute(props.component, props.route); |
|||
} |
|||
}, |
|||
{ immediate: true }, |
|||
); |
|||
</script> |
|||
@ -0,0 +1,98 @@ |
|||
<script setup lang="ts"> |
|||
import { computed, unref, watch } from 'vue'; |
|||
import { useRoute } from 'vue-router'; |
|||
|
|||
import { preferences } from '@vben/preferences'; |
|||
import { getTabKey, storeToRefs, useTabbarStore } from '@vben/stores'; |
|||
|
|||
import { transformComponent, useLayoutHook } from '../hooks'; |
|||
|
|||
const route = useRoute(); |
|||
|
|||
const tabbarStore = useTabbarStore(); |
|||
|
|||
const { getTabs, getCachedRoutes, getExcludeCachedTabs } = |
|||
storeToRefs(tabbarStore); |
|||
const { removeCachedRoute } = tabbarStore; |
|||
|
|||
const { getEnabledTransition, getTransitionName } = useLayoutHook(); |
|||
|
|||
/** |
|||
* 是否启用tab |
|||
*/ |
|||
const enableTabbar = computed(() => preferences.tabbar.enable); |
|||
|
|||
const computedCachedRouteKeys = computed(() => { |
|||
if (!unref(enableTabbar)) { |
|||
return []; |
|||
} |
|||
return unref(getTabs) |
|||
.filter((item) => item.meta.domCached) |
|||
.map((item) => getTabKey(item)); |
|||
}); |
|||
|
|||
/** |
|||
* 监听缓存路由变化,删除不存在的缓存路由 |
|||
*/ |
|||
watch(computedCachedRouteKeys, (keys) => { |
|||
unref(getCachedRoutes).forEach((item) => { |
|||
if (!keys.includes(item.key)) { |
|||
removeCachedRoute(item.key); |
|||
} |
|||
}); |
|||
}); |
|||
|
|||
/** |
|||
* 所有缓存的route |
|||
*/ |
|||
const computedCachedRoutes = computed(() => { |
|||
if (!unref(enableTabbar)) { |
|||
return []; |
|||
} |
|||
// 刷新路由可刷新缓存 |
|||
const excludeCachedTabKeys = unref(getExcludeCachedTabs); |
|||
return [...unref(getCachedRoutes).values()].filter((item) => { |
|||
const componentType: any = item.component.type || {}; |
|||
let componentName = componentType.name; |
|||
if (!componentName) { |
|||
componentName = item.route.name; |
|||
} |
|||
return !excludeCachedTabKeys.includes(componentName); |
|||
}); |
|||
}); |
|||
|
|||
/** |
|||
* 是否显示 |
|||
*/ |
|||
const computedShowView = computed(() => unref(computedCachedRoutes).length > 0); |
|||
|
|||
const computedCurrentRouteKey = computed(() => { |
|||
return getTabKey(route); |
|||
}); |
|||
</script> |
|||
|
|||
<template> |
|||
<template v-if="computedShowView"> |
|||
<template v-for="item in computedCachedRoutes" :key="item.key"> |
|||
<Transition |
|||
v-if="getEnabledTransition" |
|||
appear |
|||
mode="out-in" |
|||
:name="getTransitionName(item.route)" |
|||
> |
|||
<component |
|||
v-show="item.key === computedCurrentRouteKey" |
|||
:is="transformComponent(item.component, item.route)" |
|||
/> |
|||
</Transition> |
|||
<template v-else> |
|||
<component |
|||
v-show="item.key === computedCurrentRouteKey" |
|||
:is="transformComponent(item.component, item.route)" |
|||
/> |
|||
</template> |
|||
</template> |
|||
</template> |
|||
</template> |
|||
|
|||
<style scoped></style> |
|||
Loading…
Reference in new issue