Vue3权限管理,实现侧边栏的渲染以及路由权限拦截。
1.侧边栏渲染
index.vue 侧边栏
<el-scrollbar>
<el-menu
:default-active="activeMenu"
:router="true"
:collapse="isCollapse"
:collapse-transition="false"
:unique-opened="true"
background-color="#191a20"
text-color="#bdbdc0"
active-text-color="#fff"
>
<SubItem :menuList="menuList"></SubItem> //列表渲染
</el-menu>
</el-scrollbar>
<script setup lang="ts">
//后台数据获取,存储到pinia
onMounted(async () => {
loading.value = true;
try {
const { data } = await getMenuList();
console.log(data);
// 将路由信息存储到pinia中
menuStore.setMenuList(data as Menu.MenuOptions[]);
} finally {
// 关闭加载
loading.value = false;
}
});
//从pinia中读取数据
const menuList = computed((): Menu.MenuOptions[] => menuStore.menuList);
</script>
menu.ts 侧边栏数据Pinia
import { defineStore } from "pinia";
import { MenuState } from "../interface";
import piniaPersistConfig from "@/config/piniaPersist"; //数据持久化
// MenuStore
export const MenuStore = defineStore({
id: "MenuState",
state: (): MenuState => ({
// menu collapse
isCollapse: false,
// menu List
menuList: [] //数据列表
}),
getters: {},
actions: {
async setCollapse() {
this.isCollapse = !this.isCollapse;
},
//数据存储,并持久化,全局都能使用
async setMenuList(menuList: Menu.MenuOptions[]) {
this.menuList = menuList;
}
},
//持久化
persist: piniaPersistConfig("MenuState")
});
2.路由权限(在侧边栏渲染的基础上)
index.vue 侧边栏
<script setup lang="ts">
//后台数据获取,存储到pinia
onMounted(async () => {
loading.value = true;
try {
const { data } = await getMenuList();
// 将data处理成一维数组,存储到 pinia 中
const dynamicRouter = handleRouter(data as Menu.MenuOptions[]);//处理函数在最后
authStore.setAuthRouter(dynamicRouter);
// 将路由信息存储到pinia中
menuStore.setMenuList(data as Menu.MenuOptions[]);
} finally {
// 关闭加载
loading.value = false;
}
});
//从pinia中读取数据
const menuList = computed((): Menu.MenuOptions[] => menuStore.menuList);
</script>
auth.ts 权限路由Pinia
import { defineStore } from "pinia";
import { AuthState } from "../interface";
import piniaPersistConfig from "@/config/piniaPersist";
// AuthStore
export const AuthStore = defineStore({
id: "AuthState",
state: (): AuthState => ({
// 用户按钮权限列表
authButtons: {},
// 路由权限列表
authRouter: []
}),
getters: {
// 处理权限按钮数据,用于方便控制按钮
authButtonsObj: state => {
return state.authButtons;
},
// 后台返回的菜单数据,用于方便控制路由跳转时权限(这里已经处理成一维数组了)
dynamicRouter: state => {
return state.authRouter;
}
},
actions: {
// setAuthButtons 处理权限按钮
async setAuthButtons(authButtonList: { [key: string]: any }) {
this.authButtons = authButtonList;
},
// setAuthRouter 处理权限路由
async setAuthRouter(dynamicRouter: string[]) {
this.authRouter = dynamicRouter;
}
},
persist: piniaPersistConfig("AuthState")
});
index.ts 路由守卫
···
import { AuthStore } from "@/store/modules/auth";
import { GlobalStore } from "@/store";
import { AxiosCanceler } from "@/api/helper/axiosCancel";
// 路由前置拦截器
router.beforeEach((to, from, next) => {
NProgress.start();
// 清除之前的所有请求
const axiosCanceler = new AxiosCanceler();
axiosCanceler.removeAllPending();
// 判断当前路由是否需要权限
if (to.matched.some(record => record.meta.requiresAuth)) {
console.log(to);
return next();
}
// if (!to.meta.requiresAuth) {
// console.log(to);
// return next();
// }
// * 判断是否有Token
const globalStore = GlobalStore();
if (!globalStore.token) {
next({
path: "/login"
});
NProgress.done();
return;
}
// 判断to的路由是否在当前角色的路由表中
const authStore = AuthStore();
// * Dynamic Router(动态路由,根据后端返回的菜单数据生成的一维数组)
const dynamicRouter = authStore.dynamicRouter;
const staticRouter = [HOME_URL, "/403"];
const routerList = dynamicRouter.concat(staticRouter);
// * 如果访问的地址没有在路由表中重定向到403页面
if (routerList.indexOf(to.path) !== -1) return next();
next({
path: "/403"
});
});
router.afterEach(() => {
NProgress.done();
});
export default router;
数组处理
/**
* 使用递归处理路由菜单
* @param newArr 所有菜单数组
*/
export function handleRouter(routerList: Menu.MenuOptions[], newArr: string[] = []) {
routerList.forEach((item: Menu.MenuOptions) => {
typeof item === "object" && item.path && newArr.push(item.path);
item.children && item.children.length && handleRouter(item.children, newArr);
});
return newArr;
}