Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
2 changes: 2 additions & 0 deletions src/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@ const defaultRouterList: Array<RouteRecordRaw> = [
{
path: '/login',
name: 'login',
meta: { roleCode: 'dev' },
component: () => import('@/pages/login/index.vue'),
},
{
path: '/',
redirect: '/dashboard/base',
meta: { roleCode: 'dev' },
},
];
// 存放固定路由
Expand Down
2 changes: 2 additions & 0 deletions src/router/modules/homepage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export default [
},
icon: shallowRef(DashboardIcon),
orderNo: 0,
roleCode: 'dev',
},
children: [
{
Expand All @@ -28,6 +29,7 @@ export default [
zh_CN: '概览仪表盘',
en_US: 'Overview',
},
roleCode: 'dev',
},
},
{
Expand Down
8 changes: 4 additions & 4 deletions src/router/modules/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,27 @@ export default [
name: 'user',
component: Layout,
redirect: '/user/index',
meta: { title: { zh_CN: '个人中心', en_US: 'User Center' }, icon: 'user-circle' },
meta: { title: { zh_CN: '个人中心', en_US: 'User Center' }, icon: 'user-circle', roleCode: 'dev' },
children: [
{
path: 'index',
name: 'UserIndex',
component: () => import('@/pages/user/index.vue'),
meta: { title: { zh_CN: '个人中心', en_US: 'User Center' } },
meta: { title: { zh_CN: '个人中心', en_US: 'User Center' }, roleCode: 'dev' },
},
],
},
{
path: '/loginRedirect',
name: 'loginRedirect',
redirect: '/login',
meta: { title: { zh_CN: '登录页', en_US: 'Login' }, icon: shallowRef(LogoutIcon) },
meta: { title: { zh_CN: '登录页', en_US: 'Login' }, icon: shallowRef(LogoutIcon), roleCode: 'dev' },
component: () => import('@/layouts/blank.vue'),
children: [
{
path: 'index',
redirect: '/login',
meta: { title: { zh_CN: '登录页', en_US: 'Login' } },
meta: { title: { zh_CN: '登录页', en_US: 'Login' }, roleCode: 'dev' },
},
],
},
Expand Down
121 changes: 89 additions & 32 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);
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) {
if (route.children) {
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);
}
} else {
removeRoutes.push(childRouter);
accessedRouters.push(route);
}
});
if (children.length > 0) {
route.children = children;
res.push(route);
} else {
const removedRoute = cloneDeep(route);
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) {
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) {
console.log(parentName, route);
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',
};