diff --git a/src/layouts/components/MenuContent.vue b/src/layouts/components/MenuContent.vue index 2bb2a08b..a51430a8 100644 --- a/src/layouts/components/MenuContent.vue +++ b/src/layouts/components/MenuContent.vue @@ -37,7 +37,7 @@ type ListItemType = MenuRoute; const { navData } = defineProps({ navData: { type: Array as PropType, - default: () => [], + default: (): MenuRoute[] => [], }, }); @@ -80,7 +80,7 @@ const getMenuList = (list: MenuRoute[], basePath?: string): ListItemType[] => { redirect: item.redirect, }; }) - .filter((item) => item.meta && item.meta.hidden !== true); + .filter((item) => item.meta && item.meta.hidden !== true && item.meta.title); }; const getHref = (item: MenuRoute) => { diff --git a/src/permission.ts b/src/permission.ts index c771e906..55a355ff 100644 --- a/src/permission.ts +++ b/src/permission.ts @@ -2,9 +2,9 @@ import 'nprogress/nprogress.css'; // progress bar style import NProgress from 'nprogress'; // progress bar import { MessagePlugin } from 'tdesign-vue-next'; -import type { RouteRecordRaw } from 'vue-router'; -import router from '@/router'; +// import type { RouteRecordRaw } from 'vue-router'; +import router, { whiteListRoutePath } from '@/router'; import { getPermissionStore, useUserStore } from '@/store'; import { PAGE_NOT_FOUND_ROUTE } from '@/utils/route/constant'; @@ -13,9 +13,6 @@ NProgress.configure({ showSpinner: false }); router.beforeEach(async (to, from, next) => { NProgress.start(); - const permissionStore = getPermissionStore(); - const { whiteListRouters } = permissionStore; - const userStore = useUserStore(); if (userStore.token) { @@ -25,14 +22,12 @@ router.beforeEach(async (to, from, next) => { } try { await userStore.getUserInfo(); + const permissionStore = getPermissionStore(); + // 后端权限控制 const { asyncRoutes } = permissionStore; - if (asyncRoutes && asyncRoutes.length === 0) { - const routeList = await permissionStore.buildAsyncRoutes(); - routeList.forEach((item: RouteRecordRaw) => { - router.addRoute(item); - }); + await permissionStore.buildAsyncRoutes(); if (to.name === PAGE_NOT_FOUND_ROUTE.name) { // 动态添加路由后,此处应当重定向到fullPath,否则会加载404页面内容 @@ -43,11 +38,16 @@ router.beforeEach(async (to, from, next) => { return; } } - if (router.hasRoute(to.name)) { - next(); - } else { - next(`/`); - } + + // 前端权限控制 + // const { routers } = permissionStore; + // if (routers.length === 0) { + // await permissionStore.initRoutes(userStore.roles); + // next({ ...to, replace: true }); + // return; + // } + + next(); } catch (error) { MessagePlugin.error(error.message); next({ @@ -57,8 +57,7 @@ router.beforeEach(async (to, from, next) => { NProgress.done(); } } else { - /* white list router */ - if (whiteListRouters.includes(to.path)) { + if (whiteListRoutePath.includes(to.path)) { next(); } else { next({ @@ -73,9 +72,9 @@ router.beforeEach(async (to, from, next) => { router.afterEach((to) => { if (to.path === '/login') { const userStore = useUserStore(); - const permissionStore = getPermissionStore(); - userStore.logout(); + + const permissionStore = getPermissionStore(); permissionStore.restoreRoutes(); } NProgress.done(); diff --git a/src/router/index.ts b/src/router/index.ts index aff51fa4..d64d4552 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -18,16 +18,14 @@ const defaultRouterList: Array = [ name: 'login', component: () => import('@/pages/login/index.vue'), }, - { - path: '/', - redirect: '/dashboard/base', - }, ]; + // 存放固定路由 export const homepageRouterList: Array = mapModuleRouterList(homepageModules); export const fixedRouterList: Array = mapModuleRouterList(fixedModules); export const allRoutes = [...homepageRouterList, ...fixedRouterList, ...defaultRouterList]; +export const whiteListRoutePath = uniq(defaultRouterList.map((item) => item.path)); // 固定路由模块转换为路由 export function mapModuleRouterList(modules: Record): Array { @@ -83,7 +81,7 @@ export const getActive = (maxLevel = 3): string => { const router = createRouter({ history: createWebHistory(env === 'site' ? '/starter/vue-next/' : import.meta.env.VITE_BASE_URL), - routes: allRoutes, + routes: defaultRouterList, scrollBehavior() { return { el: '#app', diff --git a/src/router/modules/homepage.ts b/src/router/modules/homepage.ts index f5557f83..12f71cf2 100644 --- a/src/router/modules/homepage.ts +++ b/src/router/modules/homepage.ts @@ -5,6 +5,10 @@ import type { RouteRecordRaw } from 'vue-router'; import Layout from '@/layouts/index.vue'; export default [ + { + path: '/', + redirect: '/dashboard/base', + }, { path: '/dashboard', component: Layout, diff --git a/src/store/modules/permission-fe.ts b/src/store/modules/permission-fe.ts index aa089dd5..5676b721 100644 --- a/src/store/modules/permission-fe.ts +++ b/src/store/modules/permission-fe.ts @@ -5,63 +5,57 @@ import cloneDeep from 'lodash/cloneDeep'; import { defineStore } from 'pinia'; import type { RouteRecordRaw } from 'vue-router'; -import router, { allRoutes } from '@/router'; +import router, { fixedRouterList, homepageRouterList } from '@/router'; import { store } from '@/store'; -function filterPermissionsRouters(routes: Array, roles: Array) { - const res: Array = []; - const removeRoutes: Array = []; +// 严格模式 true: 路由无roleCode不可访问 false: 路由无roleCode可访问 +const CHECK_ROLE_STRICT = false; +function filterPermissionsRouters(routes: Array, roles: Array): Array { + if (routes.length === 0) return []; + const accessedRouters: Array = []; routes.forEach((route) => { - const children: Array = []; - route.children?.forEach((childRouter) => { - const roleCode = childRouter.meta?.roleCode || childRouter.name; - if (roles.includes(roleCode)) { - children.push(childRouter); - } else { - removeRoutes.push(childRouter); - } - }); - if (children.length > 0) { - route.children = children; - res.push(route); + const roleCode = route.meta?.roleCode; + const hasPermission = CHECK_ROLE_STRICT ? roles.includes(roleCode) : !roleCode || roles.includes(roleCode); + if (!hasPermission) { + return; } + if (!route.children || route.children.length === 0) { + accessedRouters.push(route); + return; + } + const accessedChildren = filterPermissionsRouters(route.children, roles); + route.children = accessedChildren; + accessedRouters.push(route); }); - return { accessedRouters: res, removeRoutes }; + return accessedRouters; } +const removeRouteFnSet = new Set<() => void>(); export const usePermissionStore = defineStore('permission', { state: () => ({ - whiteListRouters: ['/login'], routers: [], - removeRoutes: [], }), actions: { - async initRoutes(roles: Array) { - let accessedRouters = []; - - let removeRoutes: Array = []; + async initRoutes(roles: Array) { + let accessedRouters: Array = []; + const allRoutes = cloneDeep([...homepageRouterList, ...fixedRouterList]); // special token if (roles.includes('all')) { - accessedRouters = cloneDeep(allRoutes); + accessedRouters = allRoutes; } else { - const res = filterPermissionsRouters(allRoutes, roles); - accessedRouters = res.accessedRouters; - removeRoutes = res.removeRoutes; + accessedRouters = filterPermissionsRouters(allRoutes, roles); + } + for (const route of accessedRouters) { + removeRouteFnSet.add(router.addRoute(route)); } - this.routers = accessedRouters; - this.removeRoutes = removeRoutes; - - removeRoutes.forEach((item: RouteRecordRaw) => { - if (router.hasRoute(item.name)) { - router.removeRoute(item.name); - } - }); }, - async restore() { - this.removeRoutes.forEach((item: RouteRecordRaw) => { - router.addRoute(item); - }); + async restoreRoutes() { + for (const removeRoute of removeRouteFnSet) { + removeRoute(); + } + removeRouteFnSet.clear(); + this.routers = []; }, }, }); diff --git a/src/store/modules/permission.ts b/src/store/modules/permission.ts index 5398030c..62203327 100644 --- a/src/store/modules/permission.ts +++ b/src/store/modules/permission.ts @@ -1,6 +1,5 @@ import cloneDeep from 'lodash/cloneDeep'; import { defineStore } from 'pinia'; -import type { RouteRecordRaw } from 'vue-router'; import type { RouteItem } from '@/api/model/permissionModel'; import { getMenuList } from '@/api/permission'; @@ -8,19 +7,23 @@ import router, { fixedRouterList, homepageRouterList } from '@/router'; import { store } from '@/store'; import { transformObjectToRoute } from '@/utils/route'; +const removeRouteFnSet = new Set<() => void>(); + export const usePermissionStore = defineStore('permission', { state: () => ({ - whiteListRouters: ['/login'], routers: [], - removeRoutes: [], asyncRoutes: [], }), actions: { async initRoutes() { const accessedRouters = this.asyncRoutes; + const allRoutes = [...homepageRouterList, ...fixedRouterList, ...accessedRouters]; + for (const route of allRoutes) { + removeRouteFnSet.add(router.addRoute(route)); + } // 在菜单展示全部路由 - this.routers = cloneDeep([...homepageRouterList, ...accessedRouters, ...fixedRouterList]); + this.routers = cloneDeep(allRoutes); // 在菜单只展示动态路由和首页 // this.routers = [...homepageRouterList, ...accessedRouters]; // 在菜单只展示动态路由 @@ -39,11 +42,11 @@ export const usePermissionStore = defineStore('permission', { }, async restoreRoutes() { // 不需要在此额外调用initRoutes更新侧边导肮内容,在登录后asyncRoutes为空会调用 - this.asyncRoutes.forEach((item: RouteRecordRaw) => { - if (item.name) { - router.removeRoute(item.name); - } - }); + for (const removeRoute of removeRouteFnSet) { + removeRoute(); + } + removeRouteFnSet.clear(); + this.routers = []; this.asyncRoutes = []; }, }, diff --git a/src/store/modules/user.ts b/src/store/modules/user.ts index f68b4edc..d098e7cf 100644 --- a/src/store/modules/user.ts +++ b/src/store/modules/user.ts @@ -1,6 +1,5 @@ import { defineStore } from 'pinia'; -import { usePermissionStore } from '@/store'; import type { UserInfo } from '@/types/interface'; const InitUserInfo: UserInfo = { @@ -64,7 +63,7 @@ export const useUserStore = defineStore('user', { } return { name: 'td_dev', - roles: ['UserIndex', 'DashboardBase', 'login'], // 前端权限模型使用 如果使用请配置modules/permission-fe.ts使用 + roles: ['dev'], // 前端权限模型使用 如果使用请配置modules/permission-fe.ts使用 }; }; const res = await mockRemoteUserInfo(this.token); @@ -77,10 +76,6 @@ export const useUserStore = defineStore('user', { }, }, persist: { - afterRestore: () => { - const permissionStore = usePermissionStore(); - permissionStore.initRoutes(); - }, key: 'user', paths: ['token'], }, diff --git a/src/types/router.d.ts b/src/types/router.d.ts index f995e9a9..ae88563c 100644 --- a/src/types/router.d.ts +++ b/src/types/router.d.ts @@ -15,6 +15,6 @@ declare module 'vue-router' { keepAlive?: boolean; frameSrc?: string; frameBlank?: boolean; - // roleCode?: string; // 前端 roles 控制菜单权限 + roleCode?: string; // 前端 roles 控制菜单权限 } } diff --git a/src/utils/route/constant.ts b/src/utils/route/constant.ts index a3ec01c6..efcc07b6 100644 --- a/src/utils/route/constant.ts +++ b/src/utils/route/constant.ts @@ -1,14 +1,17 @@ +import type { RouteRecordRaw } from 'vue-router'; + export const LAYOUT = () => import('@/layouts/index.vue'); export const BLANK_LAYOUT = () => import('@/layouts/blank.vue'); export const IFRAME = () => import('@/layouts/components/FrameBlank.vue'); export const EXCEPTION_COMPONENT = () => import('@/pages/result/500/index.vue'); +export const PAGE_NOT_FOUND_COMPONENT = () => import('@/pages/result/404/index.vue'); export const PARENT_LAYOUT = () => new Promise((resolve) => { resolve({ name: 'ParentLayout' }); }); -export const PAGE_NOT_FOUND_ROUTE = { - path: '/:w+', +export const PAGE_NOT_FOUND_ROUTE: RouteRecordRaw = { + path: '/:pathMatch(.*)*', name: '404Page', - redirect: '/result/404', + component: PAGE_NOT_FOUND_COMPONENT, };