前言
最近失业在家无聊,想着开发一个网址导航,后端用nest写,前端用vue + naiveui,刚好写到后台管理的权限功能,就记录一下。初次写文章,文笔不好,请见谅。
思路
首先获取全部的vue页面,然后注册路由,登录时传一个权限列表比如这样 ['ADMIN', 'BLOCK_LIST'],前端定义好那些页面是对应哪个字段,然后通过匹配到没有的权限就把对应的路由删除,这样就实现了没有权限无法访问这个路径跳转到404页面。
开始
在vite中,我们可以使用glob获取文件,根据它我们可以获取到views下所有的vue页面,可以看一下我的文件夹格式。
可以看到在views文件夹下最多有两层文件夹,两层文件夹主要是为了分组,第二张图可以看到后台管理的菜单栏有些有下拉菜单,vue文件都是使用大驼峰命名,文件夹使用小驼峰命名。
然后就可以根据glob获取vue页面,在router下面的configs文件夹下创建一个ts文件叫做fetchRoutes.ts
const modules = import.meta.glob('@/views/*/*.vue');
// 获取分组的vue页面
const groupModules = import.meta.glob('@/views/*/*/*.vue');
const routes: RouteRecordRaw[] = [];
for (const path in modules) {
// 通过正则解析拿到的文件夹路径信息
const pathList = path.match(/src\/views\/(\S+?)\/(\S+?)\.vue/) as string[];
routes.push({
// 用vue文件名作为路由名称
name: pathList[2],
// 用文件夹作为路径
path: '/' + pathList[1],
component: modules[path],
});
}
// 获取分组路由
for (const path in groupModules) {
// 通过正则解析拿到的文件夹路径信息
const pathList = path.match(/src\/views\/(\S+?)\/(\S+?)\/(\S+?)\.vue/) as string[];
routes.push({
// 用vue文件名作为路由名称
name: pathList[3],
// 用文件夹作为路径,这里会使用两个文件夹的名字拼接成的路径,当然也可以不用,看个人
path: '/' + pathList[1] + '/' + pathList[2],
component: groupModules[path],
});
}
export routes;
在router的configs文件夹下再创建一个base.ts文件夹,用来配置一些路由基础信息,比如首页和404页面,主要是配置重定向,因为路由都已经通过glob获取了。
// 配置基础路径
export const baseRoutes: RouteRecordRaw[] = [
{ path: '/', redirect: '/home' },
{ path: '/:pathMatch(.*)', redirect: { name: 'NotFound' } },
];
然后在router下面的index.ts文件里定义路由
import { baseRoutes } from './configs/base';
import routes from './configs/fetchRoutes';
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [...baseRoutes, ...routes],
});
到了这一步,我们就把路由全部添加进去了,之后添加路由只需要在所用的ui框架的menu组件里添加对应的页面信息,比如我用的naiveui
然后开始添加路由权限,一般我们在登录的时候会把用户信息存起来,我这里存到了pinia,并且用了pinia-plugin-persistedstate做了持久化,同步到了localStorage。我把menu组件的数据也放到了pinia,方便等会删除路由的时候顺便匹配进行删除。
接着在router的configs文件夹里的base.ts文件里增加权限所对应的页面
// 这里的类型是因为我用了pont生成的,没有的话可以去掉,或者手动写
export type adminRole = defs.AdminUserDto['adminRole'][number];
// 设置路由权限
export const authRouter: Record<adminRole, string[]> = {
ADMIN: [''],
BLOCK_LIST: [],
};
使用vuerouter的路由守卫来通过权限修改路由
// 检测是否根据权限删除路由
let isCheckRoute = false;
router.beforeEach((to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) => {
// 通过pinia获取用户数据里的权限
const configStore = useConfigStore();
// 如果是进入登录页面则不做操作
if (to.name !== 'Login') {
// 未登录跳回登录页
if (!configStore.token) {
return next({ name: 'Login' });
} else if (!isCheckRoute) {
// 用户数据里的权限列表
const adminRole = configStore.userData?.adminRole;
// 匹配没有权限的路由
if (adminRole) {
const allAuthList = Object.keys(authRouter);
// 对比用户的权限和所有的权限列表,拿到差集就是用户没有的权限,删掉对应的路由就好
const notAuthKeyList = allAuthList.filter(i => !adminRole.includes(i));
notAuthKeyList.forEach(i => {
authRouter[i].forEach(name => {
if (router.hasRoute(name)) {
router.removeRoute(name);
}
// 这里是ui组件的菜单组件对应的数据,我把自定义一个函数进行删除
configStore.setNotShowMenu(name);
});
});
isCheckRoute = true;
// 当前路由使用的是未更改的routes,需要手动检测是否存在,不存在跳转到NotFound
if (!router.hasRoute(to.name!)) {
next({ name: 'NotFound' });
}
}
}
}
return next();
});
这样登录后就实现了通过账号权限列表实现前端路由权限