import { cloneDeep } from 'lodash-es' import useSettingsStore from './settings' import useUserStore from './user' import { resolveRoutePath } from '@/utils' import { asyncRoutes, systemRoutes } from '@/router/routes' import type { Menu, Route } from '@/global' import { getUserMenus } from '@/api/system/login' function hasPermission(permissions: Menu.menu[], route: Route.recordMainRaw | Route.recordRaw) { let isAuth = false if (route.meta?.auth) { const index: number = permissions.findIndex((menu) => { return route.meta.auth === menu.url }) if (index && index > -1) { route.meta.icon = permissions[index].icon route.meta.title = permissions[index].name isAuth = true } } else { isAuth = true } return isAuth } function filterAsyncRoutes<T extends Route.recordMainRaw[] | Route.recordRaw[]>(routes: T, permissions: Menu.menu[]): T { const res: any = [] routes.forEach((route) => { if (hasPermission(permissions, route)) { const tmpRoute = cloneDeep(route) if (tmpRoute.children) { tmpRoute.children = filterAsyncRoutes(tmpRoute.children, permissions) tmpRoute.children.length && res.push(tmpRoute) } else { res.push(tmpRoute) } } }) return res } // 格式化后台路由 // function formatBackRoutes(routes: any, views = import.meta.glob('../../views/**/*.vue')): Route.recordMainRaw[] { // return routes.map((route: any) => { // switch (route.component) { // case 'Layout': // route.component = () => import('@/layouts/index.vue') // break // default: // if (route.component) { // route.component = views[`../../views/${route.component}`] // } // } // if (route.children) { // route.children = formatBackRoutes(route.children, views) // } // return route // }) // } // 将多层嵌套路由处理成两层,保留顶层和最子层路由,中间层级将被拍平 function flatAsyncRoutes<T extends Route.recordRaw>(routes: T): T { if (routes.children) { routes.children = flatAsyncRoutesRecursive(routes.children, [{ path: routes.path, title: routes.meta.title, hide: !routes.meta.breadcrumb && routes.meta.breadcrumb === false, }], routes.path) } return routes } function flatAsyncRoutesRecursive(routes: Route.recordRaw[], breadcrumb: Route.breadcrumb[] = [], baseUrl = ''): Route.recordRaw[] { const res: Route.recordRaw[] = [] routes.forEach((route) => { if (route.children) { const childrenBaseUrl = resolveRoutePath(baseUrl, route.path) const tmpBreadcrumb = cloneDeep(breadcrumb) if (route.meta.breadcrumb !== false) { tmpBreadcrumb.push({ path: childrenBaseUrl, title: route.meta.title, hide: !route.meta.breadcrumb && route.meta.breadcrumb === false, }) } const tmpRoute = cloneDeep(route) tmpRoute.path = childrenBaseUrl tmpRoute.meta.breadcrumbNeste = tmpBreadcrumb delete tmpRoute.children res.push(tmpRoute) const childrenRoutes = flatAsyncRoutesRecursive(route.children, tmpBreadcrumb, childrenBaseUrl) childrenRoutes.forEach((item) => { // 如果 path 一样则覆盖,因为子路由的 path 可能设置为空,导致和父路由一样,直接注册会提示路由重复 if (res.some(v => v.path === item.path)) { res.forEach((v, i) => { if (v.path === item.path) { res[i] = item } }) } else { res.push(item) } }) } else { const tmpRoute = cloneDeep(route) tmpRoute.path = resolveRoutePath(baseUrl, tmpRoute.path) // 处理面包屑导航 const tmpBreadcrumb = cloneDeep(breadcrumb) tmpBreadcrumb.push({ path: tmpRoute.path, title: tmpRoute.meta.title, hide: !tmpRoute.meta.breadcrumb && tmpRoute.meta.breadcrumb === false, }) tmpRoute.meta.breadcrumbNeste = tmpBreadcrumb res.push(tmpRoute) } }) return res } const useRouteStore = defineStore( // 唯一ID 'route', { state: () => ({ isGenerate: false, routes: [] as Route.recordMainRaw[], fileSystemRoutes: [] as Route.recordRaw[], currentRemoveRoutes: [] as Function[], systems: [] as Menu.menu[], // 子系统权限 menus: [] as Menu.menu[], // 菜单权限 btns: [] as Menu.menu[], // 按钮权限 }), getters: { // 扁平化路由(将三级及以上路由数据拍平成二级) flatRoutes: (state) => { const settingsStore = useSettingsStore() const routes: Route.recordRaw[] = [] if (state.routes) { if (settingsStore.app.routeBaseOn !== 'filesystem') { state.routes.forEach((item) => { routes.push(...cloneDeep(item.children)) }) routes.forEach(item => flatAsyncRoutes(item)) } else { routes.push(...cloneDeep(state.fileSystemRoutes)) } } return routes }, flatSystemRoutes: () => { const routes = [...systemRoutes] routes.forEach(item => flatAsyncRoutes(item)) return routes }, getBtns: (state) => { return state.btns }, }, actions: { // 根据权限动态生成路由(前端生成) generateRoutesAtFront(asyncRoutes: Route.recordMainRaw[]) { // eslint-disable-next-line no-async-promise-executor return new Promise<void>(async (resolve) => { const settingsStore = useSettingsStore() const userStore = useUserStore() let accessedRoutes // 如果权限功能开启,则需要对路由数据进行筛选过滤 if (settingsStore.app.enablePermission) { const permissions = userStore.getMenus accessedRoutes = filterAsyncRoutes(asyncRoutes, permissions) } else { accessedRoutes = cloneDeep(asyncRoutes) } // 设置 routes 数据 this.isGenerate = true this.routes = accessedRoutes.filter(item => item.children?.length !== 0) resolve() }) }, // 根据权限动态生成路由(后端获取) generateRoutesAtBack(currentSystem?: { name: string; code: string }) { const params = { resourceType: '', parentCode: '', } if (currentSystem) { params.parentCode = currentSystem.code // 子系统code } return new Promise<void>((resolve) => { getUserMenus(params).then((response) => { const data = response.data const menus = [] // 仅菜单 const btns = [] // 非菜单 if (data.menus && data.menus.length > 0) { const allmenus = [...data.menus] for (const menu of allmenus) { // 遍历菜单,获取所有的权限项 if (menu.resourceType === '02' || menu.resourceType === '03') { // 将是菜单的放入menus const menu_tmp = { url: menu.url, name: menu.name, icon: menu.icon } menus.push(menu_tmp) } const btn_tmp = { url: menu.url, name: menu.name, icon: menu.icon } btns.push(btn_tmp) } } this.menus = menus this.btns = btns const settingsStore = useSettingsStore() let accessedRoutes // 如果权限功能开启,则需要对路由数据进行筛选过滤 if (settingsStore.app.enablePermission) { accessedRoutes = filterAsyncRoutes(asyncRoutes, menus) } else { accessedRoutes = cloneDeep(asyncRoutes) } // 设置 routes 数据 this.isGenerate = true this.routes = accessedRoutes.filter(item => item.children.length !== 0) resolve() }) }) }, // 根据权限动态生成路由(文件系统生成) generateRoutesAtFilesystem(asyncRoutes: Route.recordRaw[]) { // eslint-disable-next-line no-async-promise-executor return new Promise<void>(async (resolve) => { const settingsStore = useSettingsStore() const userStore = useUserStore() let accessedRoutes // 如果权限功能开启,则需要对路由数据进行筛选过滤 if (settingsStore.app.enablePermission) { const permissions = await userStore.getPermissions() accessedRoutes = filterAsyncRoutes(asyncRoutes, permissions) } else { accessedRoutes = cloneDeep(asyncRoutes) } // 设置 routes 数据 this.isGenerate = true this.fileSystemRoutes = accessedRoutes.filter(item => item.children?.length !== 0) resolve() }) }, // 记录 accessRoutes 路由,用于登出时删除路由 setCurrentRemoveRoutes(routes: Function[]) { this.currentRemoveRoutes = routes }, // 清空动态路由 removeRoutes() { this.isGenerate = false this.routes = [] this.currentRemoveRoutes.forEach((removeRoute) => { removeRoute() }) this.currentRemoveRoutes = [] }, }, }, ) export default useRouteStore