Skip to content
4 changes: 2 additions & 2 deletions src/layouts/components/MenuContent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ type ListItemType = MenuRoute;
const { navData } = defineProps({
navData: {
type: Array as PropType<MenuRoute[]>,
default: () => [],
default: (): MenuRoute[] => [],
},
});

Expand Down Expand Up @@ -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) => {
Expand Down
21 changes: 17 additions & 4 deletions src/permission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ router.beforeEach(async (to, from, next) => {
try {
await userStore.getUserInfo();

// 后端权限控制
const { asyncRoutes } = permissionStore;

if (asyncRoutes && asyncRoutes.length === 0) {
const routeList = await permissionStore.buildAsyncRoutes();
routeList.forEach((item: RouteRecordRaw) => {
Expand All @@ -43,10 +43,23 @@ router.beforeEach(async (to, from, next) => {
return;
}
}

// 前端权限控制
// const permissionStore = getPermissionStore();
// const { routers } = permissionStore;
// if (routers.length === 0) {
// await permissionStore.initRoutes(userStore.roles);
// }

if (router.hasRoute(to.name)) {
next();
} else {
next(`/`);
// 动态添加404 page
// router.addRoute(PAGE_NOT_FOUND_ROUTE);
// next(to.fullPath);

// 不添加404 page,重定向到首页
next({ path: '/' });
}
} catch (error) {
MessagePlugin.error(error.message);
Expand All @@ -73,9 +86,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();
Expand Down
125 changes: 91 additions & 34 deletions src/store/modules/permission-fe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,53 @@

import cloneDeep from 'lodash/cloneDeep';
import { defineStore } from 'pinia';
import type { RouteRecordRaw } from 'vue-router';
import type { RouteRecordName, RouteRecordRaw } from 'vue-router';

import router, { allRoutes } from '@/router';
import { store } from '@/store';

function filterPermissionsRouters(routes: Array<RouteRecordRaw>, roles: Array<unknown>) {
const res: Array<RouteRecordRaw> = [];
const removeRoutes: Array<RouteRecordRaw> = [];
// 严格模式 默认所有路由不可访问
const CHECK_ROLE_STRICT = false;
function filterPermissionsRouters(
routes: Array<RouteRecordRaw>,
roles: Array<unknown>,
whiteListRouters: Array<string>,
): {
accessedRouters: Array<RouteRecordRaw>;
removedRoutes: Array<RouteRecordRaw>;
} {
if (routes.length === 0) return { accessedRouters: [], removedRoutes: [] };
const accessedRouters: Array<RouteRecordRaw> = [];
const removedRoutes: Array<RouteRecordRaw> = [];
routes.forEach((route) => {
const children: Array<RouteRecordRaw> = [];
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);
const resolvedRoute = router.resolve(route);
const inWhiteList = whiteListRouters.includes(resolvedRoute.path);
if (!hasPermission && !inWhiteList) {
const removedRoute = cloneDeep(route);
removedRoutes.push(removedRoute);
return;
}
if (!route.children || route.children.length === 0) {
accessedRouters.push(route);
return;
}
const { accessedRouters: accessedChildren, removedRoutes: removedChildren } = filterPermissionsRouters(
route.children,
roles,
whiteListRouters,
);
route.children = accessedChildren;
accessedRouters.push(route);

if (removedChildren.length > 0) {
const removedRoute = cloneDeep(route);
removedRoute.children = removedChildren;
removedRoutes.push(removedRoute);
}
});
return { accessedRouters: res, removeRoutes };
return { accessedRouters, removedRoutes };
}

export const usePermissionStore = defineStore('permission', {
Expand All @@ -37,31 +60,65 @@ export const usePermissionStore = defineStore('permission', {
}),
actions: {
async initRoutes(roles: Array<unknown>) {
let accessedRouters = [];

let removeRoutes: Array<RouteRecordRaw> = [];
let accessedRouters: Array<RouteRecordRaw> = [];
let removedRoutes: Array<RouteRecordRaw> = [];
// special token
if (roles.includes('all')) {
accessedRouters = cloneDeep(allRoutes);
accessedRouters = allRoutes;
} else {
const res = filterPermissionsRouters(allRoutes, roles);
const res = filterPermissionsRouters(allRoutes, roles, this.whiteListRouters);
accessedRouters = res.accessedRouters;
removeRoutes = res.removeRoutes;
removedRoutes = res.removedRoutes;
}
this.routers = cloneDeep(accessedRouters);
this.removeRoutes = removedRoutes;

this.routers = accessedRouters;
this.removeRoutes = removeRoutes;

removeRoutes.forEach((item: RouteRecordRaw) => {
if (router.hasRoute(item.name)) {
router.removeRoute(item.name);
function checkNameInRoutes(name: RouteRecordName, routes: Array<RouteRecordRaw>) {
if (routes.length === 0) return false;
for (const route of routes) {
if (route.name === name) {
return true;
}
if (!route.children || route.children.length === 0) {
return false;
}
if (checkNameInRoutes(name, route.children)) {
return true;
}
}
return false;
}
function removeRoutes(routes: Array<RouteRecordRaw>) {
for (const route of routes) {
if (route.children && route.children.length > 0) {
removeRoutes(route.children);
}
const canRemoveRoute = !checkNameInRoutes(route.name, accessedRouters);
if (canRemoveRoute && router.hasRoute(route.name)) {
router.removeRoute(route.name);
}
}
});
}
removeRoutes(removedRoutes);
},
async restore() {
this.removeRoutes.forEach((item: RouteRecordRaw) => {
router.addRoute(item);
});
async restoreRoutes() {
function addRemovedRoutes(routes: Array<RouteRecordRaw>, parentName?: RouteRecordName) {
for (const route of routes) {
if (!router.hasRoute(route.name)) {
if (parentName) {
router.addRoute(parentName, route);
} else {
router.addRoute(route);
}
}
if (route.children && route.children.length > 0) {
addRemovedRoutes(route.children, route.name);
}
}
}
addRemovedRoutes(this.removeRoutes);
this.removeRoutes = [];
this.routers = [];
},
},
});
Expand Down
7 changes: 1 addition & 6 deletions src/store/modules/user.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { defineStore } from 'pinia';

import { usePermissionStore } from '@/store';
import type { UserInfo } from '@/types/interface';

const InitUserInfo: UserInfo = {
Expand Down Expand Up @@ -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);
Expand All @@ -77,10 +76,6 @@ export const useUserStore = defineStore('user', {
},
},
persist: {
afterRestore: () => {
const permissionStore = usePermissionStore();
permissionStore.initRoutes();
},
key: 'user',
paths: ['token'],
},
Expand Down
2 changes: 1 addition & 1 deletion src/types/router.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ declare module 'vue-router' {
keepAlive?: boolean;
frameSrc?: string;
frameBlank?: boolean;
// roleCode?: string; // 前端 roles 控制菜单权限
roleCode?: string; // 前端 roles 控制菜单权限
}
}
6 changes: 4 additions & 2 deletions src/utils/route/constant.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
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');
Expand All @@ -7,8 +9,8 @@ export const PARENT_LAYOUT = () =>
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',
};