mirror of
				https://github.com/continew-org/continew-admin-ui.git
				synced 2025-10-31 10:57:10 +08:00 
			
		
		
		
	refactor: 路由多级缓存调整为扁平化方案
This commit is contained in:
		| @@ -13,19 +13,32 @@ | ||||
|  | ||||
| <script lang="ts" setup> | ||||
| import type { RouteLocationMatched } from 'vue-router' | ||||
| import { useRouteStore } from '@/stores' | ||||
| import { findTree } from 'xe-utils' | ||||
|  | ||||
| const route = useRoute() | ||||
| const router = useRouter() | ||||
| const { routes } = useRouteStore() | ||||
|  | ||||
| let home: RouteLocationMatched | null = null | ||||
| const getHome = () => { | ||||
|   if (!home) { | ||||
|     const cloneRoutes = JSON.parse(JSON.stringify(routes)) as RouteLocationMatched[] | ||||
|     const obj = findTree(cloneRoutes, (i) => i.path === '/home') | ||||
|     home = obj.item | ||||
|   } | ||||
| } | ||||
|  | ||||
| const breadcrumbList = ref<RouteLocationMatched[]>([]) | ||||
| function getBreadcrumbList() { | ||||
|   // 只显示有title标题的 | ||||
|   const matched = route.matched.filter((item) => item.meta && item.meta.title) | ||||
|   const first = matched[0] | ||||
|   if (!isHome(first)) { | ||||
|     matched.unshift({ path: '/', meta: { title: '首页' } } as RouteLocationMatched) | ||||
|   getHome() | ||||
|   const cloneRoutes = JSON.parse(JSON.stringify(routes)) as RouteLocationMatched[] | ||||
|   const obj = findTree(cloneRoutes, (i) => i.path === route.path) | ||||
|   // 获取当前节点的所有上级节点集合,包含当前节点 | ||||
|   const arr = obj.nodes.filter((item) => item.meta && item.meta.title && item.meta.breadcrumb !== false) | ||||
|   if (home) { | ||||
|     breadcrumbList.value = [home, ...arr] | ||||
|   } | ||||
|   breadcrumbList.value = matched.filter((item) => item.meta && item.meta.title && item.meta.breadcrumb !== false) | ||||
| } | ||||
| getBreadcrumbList() | ||||
|  | ||||
| @@ -34,13 +47,6 @@ watchEffect(() => { | ||||
|   getBreadcrumbList() | ||||
| }) | ||||
|  | ||||
| // 判断是否为首页 | ||||
| function isHome(route: RouteLocationMatched) { | ||||
|   const name = (route?.name as string) || '' | ||||
|   if (!name) return false | ||||
|   return name.trim() === 'Home' | ||||
| } | ||||
|  | ||||
| // 路由跳转 | ||||
| function handleLink(item: RouteLocationMatched) { | ||||
|   const { redirect, path } = item | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| <template> | ||||
|   <a-layout class="main"> | ||||
|     <router-view v-slot="{ Component, route }"> | ||||
|       <transition :name="transitionName(route)" mode="out-in" appear> | ||||
|       <transition :name="appStore.transitionName" mode="out-in" appear> | ||||
|         <keep-alive :include="(tabsStore.cacheList as string[])"> | ||||
|           <component :is="Component" :key="route.matched?.[1]?.path" /> | ||||
|           <component :is="Component" :key="route.path" /> | ||||
|         </keep-alive> | ||||
|       </transition> | ||||
|     </router-view> | ||||
| @@ -11,22 +11,11 @@ | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import type { RouteLocationNormalizedLoaded } from 'vue-router' | ||||
| import { useAppStore, useTabsStore } from '@/stores' | ||||
|  | ||||
| defineOptions({ name: 'Main' }) | ||||
| const appStore = useAppStore() | ||||
| const tabsStore = useTabsStore() | ||||
|  | ||||
| // 过渡动画 | ||||
| const transitionName = computed(() => { | ||||
|   return function (route: RouteLocationNormalizedLoaded) { | ||||
|     if (route?.matched?.[1]?.meta?.animation === false) { | ||||
|       return '' | ||||
|     } | ||||
|     return appStore.transitionName | ||||
|   } | ||||
| }) | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
|   | ||||
| @@ -1,13 +1,15 @@ | ||||
| import { ref, toRaw } from 'vue' | ||||
| import { ref } from 'vue' | ||||
| import { defineStore } from 'pinia' | ||||
| import type { RouteRecordRaw } from 'vue-router' | ||||
| import { constantRoutes } from '@/router' | ||||
| import Layout from '@/layout/index.vue' | ||||
| import ParentView from '@/components/ParentView/index.vue' | ||||
| import { getUserRoute, type RouteItem } from '@/apis' | ||||
| import { mapTree } from 'xe-utils' | ||||
| import { mapTree, toTreeArray } from 'xe-utils' | ||||
| import { cloneDeep, omit } from 'lodash-es' | ||||
| import { transformPathToName } from '@/utils' | ||||
|  | ||||
| const Layout = () => import('@/layout/index.vue') | ||||
|  | ||||
| // 匹配views里面所有的.vue文件 | ||||
| const modules = import.meta.glob('@/views/**/*.vue') | ||||
|  | ||||
| @@ -64,6 +66,28 @@ const formatAsyncRoutes = (menus: RouteItem[]) => { | ||||
|   return routes as RouteRecordRaw[] | ||||
| } | ||||
|  | ||||
| /** 判断路由层级是否大于 2 */ | ||||
| export const isMultipleRoute = (route: RouteRecordRaw) => { | ||||
|   const children = route.children | ||||
|   if (children?.length) { | ||||
|     // 只要有一个子路由的 children 长度大于 0,就说明是三级及其以上路由 | ||||
|     return children.some((child) => child.children?.length) | ||||
|   } | ||||
|   return false | ||||
| } | ||||
|  | ||||
| /** 路由降级(把三级及其以上的路由转化为二级路由) */ | ||||
| export const flatMultiLevelRoutes = (routes: RouteRecordRaw[]) => { | ||||
|   const cloneRoutes = cloneDeep(routes) | ||||
|   cloneRoutes.forEach((route) => { | ||||
|     if (isMultipleRoute(route)) { | ||||
|       const flatRoutes = toTreeArray(route.children) | ||||
|       route.children = flatRoutes.map((i) => omit(i, 'children')) as RouteRecordRaw[] | ||||
|     } | ||||
|   }) | ||||
|   return cloneRoutes | ||||
| } | ||||
|  | ||||
| const storeSetup = () => { | ||||
|   // 所有路由(常驻路由 + 动态路由) | ||||
|   const routes = ref<RouteRecordRaw[]>([]) | ||||
| @@ -74,7 +98,6 @@ const storeSetup = () => { | ||||
|   const setRoutes = (data: RouteRecordRaw[]) => { | ||||
|     routes.value = constantRoutes.concat(data) | ||||
|     asyncRoutes.value = data | ||||
|     console.log('路由', toRaw(routes.value)) | ||||
|   } | ||||
|  | ||||
|   // 生成路由 | ||||
| @@ -84,7 +107,9 @@ const storeSetup = () => { | ||||
|       getUserRoute().then((res) => { | ||||
|         const asyncRoutes = formatAsyncRoutes(res.data) | ||||
|         setRoutes(asyncRoutes) | ||||
|         resolve(asyncRoutes) | ||||
|         const cloneRoutes = cloneDeep(asyncRoutes) | ||||
|         const flatRoutes = flatMultiLevelRoutes(cloneRoutes as RouteRecordRaw[]) | ||||
|         resolve(flatRoutes) | ||||
|       }) | ||||
|     }) | ||||
|   } | ||||
|   | ||||
| @@ -10,7 +10,8 @@ const storeSetup = () => { | ||||
|   const cacheList = ref<RouteRecordName[]>([]) // keep-alive缓存的数组, 元素是组件名 | ||||
|  | ||||
|   // 添加一个页签, 如果当前路由已经打开, 则不再重复添加 | ||||
|   const addTagItem = (item: RouteRecordRaw) => { | ||||
|   const addTagItem = (route: RouteRecordRaw) => { | ||||
|     const item = JSON.parse(JSON.stringify(route)) | ||||
|     if (tagList.value.some((i) => i.path === item.path)) return | ||||
|     if (item.meta?.showInTabs ?? true) { | ||||
|       tagList.value.push(item) | ||||
| @@ -35,7 +36,7 @@ const storeSetup = () => { | ||||
|     const arr: RouteRecordRaw[] = [] | ||||
|     _XEUtils_.eachTree(routeStore.routes, (item) => { | ||||
|       if (item.meta?.affix ?? false) { | ||||
|         arr.push(item) | ||||
|         arr.push(JSON.parse(JSON.stringify(item))) | ||||
|       } | ||||
|     }) | ||||
|     tagList.value = arr | ||||
|   | ||||
							
								
								
									
										4
									
								
								src/types/router.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								src/types/router.d.ts
									
									
									
									
										vendored
									
									
								
							| @@ -11,7 +11,7 @@ declare module 'vue-router' { | ||||
|     /**  默认false, 设置true的时候该路由不会在侧边栏出现 */ | ||||
|     hidden?: boolean | ||||
|     /** 默认true, 如果设置为false, 则不会在面包屑中显示 */ | ||||
|     breadcrumb?: false | ||||
|     breadcrumb?: boolean | ||||
|     /** 默认true, 如果设置为false, 它则不会显示在Tab栏中 */ | ||||
|     showInTabs?: boolean | ||||
|     /** 默认false, 如果设置为true, 它则会固定在Tab栏中, 例如首页 */ | ||||
| @@ -39,8 +39,6 @@ declare module 'vue-router' { | ||||
|     noShowingChildren?: boolean | ||||
|     /** 设置该路由进入的权限, 支持多个权限叠加 */ | ||||
|     roles?: string[] | ||||
|     /** 路由切换是否使用动画 */ | ||||
|     animation?: boolean | ||||
|     /** 排序 */ | ||||
|     sort?: number | ||||
|   } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user