import { ref } from 'vue' import { defineStore } from 'pinia' import type { RouteRecordRaw } from 'vue-router' import { mapTree, toTreeArray } from 'xe-utils' import { cloneDeep, omit } from 'lodash-es' import { constantRoutes, systemRoutes } from '@/router/route' import type { RouteItem } from '@/apis' import { transformPathToName } from '@/utils' import { asyncRouteModules } from '@/router/asyncModules' const layoutComponentMap = { Layout: () => import('@/layout/index.vue'), ParentView: () => import('@/components/ParentView/index.vue'), } /** 将component由字符串转成真正的模块 */ const transformComponentView = (component: string) => { return layoutComponentMap[component as keyof typeof layoutComponentMap] || asyncRouteModules[component] } /** * @description 前端来做排序、格式化 * @params {menus} 后端返回的路由数据,已经根据当前用户角色过滤掉了没权限的路由 * 1. 对后端返回的路由数据进行排序,格式化 * 2. 同时将component由字符串转成真正的模块 */ const formatAsyncRoutes = (menus: RouteItem[]) => { if (!menus.length) return [] const pathMap = new Map() return mapTree(menus, (item) => { pathMap.set(item.id, item.path) if (item.children?.length) { item.children.sort((a, b) => (a?.sort ?? 0) - (b?.sort ?? 0)) } // 部分子菜单,例如:通知公告新增、查看详情,需要选中其父菜单 if (item.parentId && item.type === 2 && item.permission) { item.activeMenu = pathMap.get(item.parentId) } return { path: item.path, name: item.name ?? transformPathToName(item.path), component: transformComponentView(item.component), redirect: item.redirect, meta: { title: item.title, hidden: item.isHidden, keepAlive: item.isCache, icon: item.icon, showInTabs: item.showInTabs, activeMenu: item.activeMenu, }, } }) as unknown as RouteRecordRaw[] } /** 判断路由层级是否大于 2 */ export const isMultipleRoute = (route: RouteRecordRaw) => { return route.children?.some((child) => child.children?.length) ?? false } /** 路由降级(把三级及其以上的路由转化为二级路由) */ export const flatMultiLevelRoutes = (routes: RouteRecordRaw[]) => { return cloneDeep(routes).map((route) => { if (!isMultipleRoute(route)) return route return { ...route, children: toTreeArray(route.children).map((item) => omit(item, 'children')) as RouteRecordRaw[], } }) } const storeSetup = () => { // 所有路由(常驻路由 + 动态路由) const routes = ref([]) // 动态路由(异步路由) const asyncRoutes = ref([]) // 合并路由 const setRoutes = (data: RouteRecordRaw[]) => { // 合并路由并排序 routes.value = [...constantRoutes, ...systemRoutes, ...data] .sort((a, b) => (a.meta?.sort ?? 0) - (b.meta?.sort ?? 0)) asyncRoutes.value = data } // 生成路由 const generateRoutes = async (): Promise => { // 获取路由数据并已通过适配器转换 // const { data } = await getUserRouteWithAdapter() const data = [{ id: 1000, parentId: 0, title: '系统管理', type: 1, path: '/system', name: 'System', component: 'Layout', redirect: '/system/user', icon: 'settings', isExternal: false, isCache: false, isHidden: false, sort: 1, children: [ { id: 1010, parentId: 1000, title: '用户管理', type: 2, path: '/system/user', name: 'SystemUser', component: 'system/user/index', icon: 'user', isExternal: false, isCache: false, isHidden: false, sort: 1, }, { id: 1030, parentId: 1000, title: '角色管理', type: 2, path: '/system/role', name: 'SystemRole', component: 'system/role/index', icon: 'user-group', isExternal: false, isCache: false, isHidden: false, sort: 2, }, { id: 1050, parentId: 1000, title: '菜单管理', type: 2, path: '/system/menu', name: 'SystemMenu', component: 'system/menu/index', icon: 'menu', isExternal: false, isCache: false, isHidden: false, sort: 3, }, { id: 1070, parentId: 1000, title: '部门管理', type: 2, path: '/system/dept', name: 'SystemDept', component: 'system/dept/index', icon: 'mind-mapping', isExternal: false, isCache: false, isHidden: false, sort: 4, }, { id: 1090, parentId: 1000, title: '岗位管理', type: 2, path: '/system/post', name: 'SystemPost', component: 'system/post/index', icon: 'settings', isExternal: false, isCache: false, isHidden: false, sort: 5, }, ], }] // 使用已转换的数据生成路由 const asyncRoutes = formatAsyncRoutes(data as unknown as RouteItem[]) // 合并路由,避免重复 const allRoutes = [...asyncRoutes] const flatRoutes = flatMultiLevelRoutes(cloneDeep(allRoutes)) setRoutes(allRoutes) return flatRoutes } return { routes, asyncRoutes, generateRoutes, } } export const useRouteStore = defineStore('route', storeSetup, { persist: true })