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