# vue2模拟vue-element-admin手写角色权限的实现

> 作者/来源: UCloud 运营管理员
> 发布时间: 2022-09-25T14:04:00.000Z
> 分类: 其他
> 标签: AI, 存储, 前端
> 原文链接: http://117.50.162.249:3000/yun/articles/2457

---

# vue2模拟vue-element-admin手写角色权限的实现

> 来源: https://www.ucloud.cn/yun/127835.html
> 作者: 3403771864
> 发布日期: 发布于2022-09-25 22:04

权限

　　路由权限

　　静态路由：固定的路由，没有权限。如login页面

　　动态路由：根据不同的角色，后端返回不同的路由接口。通过meta中的roles去做筛选

　　store存储路由

　　3　　//地址：store/modules/permission

```
　　import { routes as constantRoutes } from '@/router'
　　
　　// 根据meta.roles去判断该角色是否有路由权限
　　function hasPermission(roles, route) {
　　if (route.meta && route.meta.roles) {
　　return route.meta.roles.some(val => val === roles)
　　}
　　return true
　　}
　　
　　/**
　　* 递归动态路由
　　* @param routes 动态路由
　　* @param roles 角色
　　*/
　　export function filterAsyncRoutes(routes, roles) {
　　const res = []
　　routes.forEach(route => {
　　const tmp = { ...route }
　　if (hasPermission(roles, tmp)) {
　　if (tmp.children) {
　　//后台传来的路由字符串，转换为组件对象
　　// let a = `../views/${route.component}`;
　　// route.component = () => import(a); // 导入组件
　　tmp.children = filterAsyncRoutes(tmp.children, roles)
　　}
　　res.push(tmp)
　　}
　　})
　　
　　return res
　　}
　　
　　//模拟后端传过来的路由
　　export const asyncRoutes = [
　　{
　　path: '/',
　　name: 'home',
　　redirect: '/PickupTask',
　　meta: {
　　title: '首页',
　　//纯前端去做动态路由
　　roles: ['admin']
　　},
　　component: () => import('@/views/HomeView.vue'),
　　children: [
　　{
　　path: 'PickupTask',
　　name: 'PickupTask',
　　meta: {
　　title: 'PickupTask',
　　},
　　component: () => import('@/views/Sd/PickupTask.vue'),
　　},
　　{
　　path: 'access',
　　hidden: true,
　　component: () => import('@/views/demo/Access.vue'),
　　meta: {
　　title: 'access',
　　roles: ['admin'],
　　//按钮权限标识
　　button: {
　　'btn:access:createUser': 'hidden',
　　'btn:access:editUser': 'disable'
　　},
　　},
　　},
　　],
　　}
　　]
　　
　　const permisssion = {
　　// namespaced: true, -> store.dispatch('permisssion/generateRoutes', 'admin');
　　state: {
　　//静态路由+动态路由
　　routes: [],
　　//动态路由
　　addRoutes: []
　　},
　　mutations: {
　　SET_ROUTES: (state, routes) => {
　　state.addRoutes = routes
　　state.routes = constantRoutes.concat(routes)
　　}
　　},
　　actions: {
　　generateRoutes({ commit }, roles) {
　　return new Promise(resolve => {
　　let accessedRoutes = filterAsyncRoutes(asyncRoutes, roles)
　　commit('SET_ROUTES', accessedRoutes)
　　resolve(accessedRoutes)
　　})
　　}
　　}
　　}　　
　　export default permisssion
```

　　router添加路由

　　将store中的动态路由使用addRoute添加(**最新版本去掉了addRoutes只能使用addRoute添加路由**)。

```
　//地址：router/index
　　import Vue from 'vue';
　　import VueRouter, { RouteConfig } from 'vue-router';
　　import store from '@/store';
　　
　　Vue.use(VueRouter);
　　
　　const isProd = process.env.NODE_ENV === 'production';
　　const routerContext = require.context('./', true, /index.js$/);
　　//静态路由
　　export let routes: any = [];
　　
　　routerContext.keys().forEach((route) => {
　　// route就是路径
　　// 如果是根目录的index不做处理
　　if (route.startsWith('./index')) {
　　return;
　　}
　　const routerModule = routerContext(route);
　　routes = [...routes, ...(routerModule.default || routerModule)];
　　});
　　
　　// 创建 router 实例，然后传 `routes` 配置
　　const router = new VueRouter({
　　mode: 'history',
　　base: isProd ? '/vue-demo/' : process.env.BASE_URL,
　　routes,
　　scrollBehavior(to, from, savedPosition) {
　　if (to.hash) {
　　return {
　　selector: to.hash,
　　};
　　}
　　},
　　});
　　
　　let registerRouteFresh = true;
　　/**
　　* 全局全局前置守卫
　　* to : 将要进入的目标路由对象
　　* from : 即将离开的目标路由对象
　　*/
　　router.beforeEach(async (to: any, from, next) => {
　　//设置当前页的title
　　document.title = to.meta.title;
　　if (to.path === '/login' && localStorage.getItem('token')) {
　　next('/');
　　}
　　console.log(registerRouteFresh);
　　//如果首次或者刷新界面，next(...to, replace: true)会循环遍历路由，
　　//如果to找不到对应的路由那么他会再执行一次beforeEach((to, from, next))直到找到对应的路由，
　　//我们的问题在于页面刷新以后异步获取数据，直接执行next()感觉路由添加了但是在next()之后执行的，
　　//所以我们没法导航到相应的界面。这里使用变量registerRouteFresh变量做记录，直到找到相应的路由以后，把值设置为false然后走else执行next(),整个流程就走完了，路由也就添加完了。
　　if (registerRouteFresh) {
　　//设置路由
　　const accessRoutes = await store.dispatch('generateRoutes', 'admin');
　　let errorPage = {
　　path: '*',
　　name: '404',
　　component: () => import('../views/404.vue'),
　　};
　　// 将404添加进去
　　// 现在才添加的原因是：作为一级路由，当刷新，动态路由还未加载，路由就已经做了匹配，找不到就跳到了404
　　router.addRoute({ ...errorPage });
　　accessRoutes.forEach((item: RouteConfig) => {
　　router.addRoute(item);
　　});
　　//获取路由配置
　　console.log(router.getRoutes());
　　//通过next({...to, replace})解决刷新后路由失效的问题
　　next({ ...to, replace: true });
　　registerRouteFresh = false;
　　} else {
　　next();
　　}
　　next();
　　});
　　
　　export default router;
```

　　菜单权限

　　路由遍历，通过store路由权限中的permission.state.routes去做处理

　　按钮权限

　　准备：存储按钮标识

```
　　//地址：store/modules/user
　　import {
　　userInfo,
　　} from '@/api'
　　
　　const user = {
　　state: {
　　role: 'admin',
　　mockButton: {
　　'btn:access:createUser': 'show',
　　'btn:access:editUser': 'show'
　　}
　　},
　　//更改 Vuex 的 store 中的状态的唯一方法是提交 mutation
　　mutations: {
　　change_role: (state, data) => {
　　state.role = data.role
　　},
　　change_btn: (state, data) => {
　　state.mockButton = data.mockButton
　　}
　　},
　　}
　　
　　export default user
```

　　指令

　　通过模拟传入按钮标识的属性，去判断按钮是否隐藏或者禁用

```
　　//地址：directive/permission/index
　　import permission from './permissionBtn'
　　
　　const install = function(Vue) {
　　Vue.directive('permission', permission)
　　}
　　
　　if (window.Vue) {
　　window['permission'] = permission
　　Vue.use(install); // eslint-disable-line
　　}
　　
　　permission.install = install
　　export default permission
```

```
　　//地址：directive/permission/permissionBtn
　　import store from '@/store'
　　
　　function checkPermission(el, binding) {
　　const { value } = binding
　　const roles = store.getters && store.getters.role
　　// 获取模拟权限按钮标识
　　const mockButton = store.getters && store.getters.mockButton
　　// 设置按钮属性
　　if (mockButton[value] === 'disabled') {
　　el.disabled = true
　　el.setAttribute('disabled', true)
　　}
　　
　　if (mockButton[value] === 'hidden') {
　　el.style.display = 'none'
　　}
　　if (mockButton[value] === 'show') {
　　el.style.display = 'block'
　　el.disabled = false
　　}
　　// throw new Error(`need roles! Like v-permission="['admin','editor']"`)
　　}
　　
　　export default {
　　inserted(el, binding) {
　　checkPermission(el, binding)
　　},
　　update(el, binding) {
　　checkPermission(el, binding)
　　}
　　}
```

　　//应用

```
　　<template>
　　<div>
　　<a-button @click="changeRole">切换角色</a-button>
　　<span>当前角色：{{ role }}</span>
　　<!-- 注意一定要加disabled属性，才能设置它的disabled值 -->
　　<a-button :disabled="false" v-permission="'btn:access:createUser'">
　　新建用户
　　</a-button>
　　<a-button :disabled="false" v-permission="'btn:access:editUser'">
　　编辑用户
　　</a-button>
　　</div>
　　</template>
　　
　　<script>
　　import { Vue, Component, Watch } from "vue-property-decorator";
　　import permission from "@/directive/permission/index.js"; // 权限判断指令
　　// import checkPermission from '@/utils/permission' // 权限判断函数
　　@Component({
　　directives: {
　　permission,
　　},
　　computed: {
　　role() {
　　return this.$store.getters.role;
　　},
　　},
　　})
　　export default class Access extends Vue {
　　get role() {
　　return this.$store.getters.role;
　　}
　　
　　changeRole() {
　　//设置按钮权限
　　this.$store.commit("change_btn", {
　　mockButton:
　　this.role === "admin"
　　? {
　　"btn:access:createUser": "hidden",
　　"btn:access:editUser": "disabled",
　　}
　　: {
　　"btn:access:createUser": "show",
　　"btn:access:editUser": "show",
　　},
　　});
　　//设置角色
　　this.$store.commit("change_role", {
　　role: this.role === "admin" ? "edit" : "admin",
　　});
　　}
　　}
　　</script>
```

　　函数

```
　　　/**
　　* @param {Array} value
　　* @returns {Boolean}
　　* @example see @/views/permission/directive.vue
　　* 除了使用指令，也可以使用函数
　　*/
　　export default function checkPermission(value) {
　　if (value && value instanceof Array && value.length > 0) {
　　const roles = store.getters && store.getters.roles
　　const permissionRoles = value
　　
　　const hasPermission = roles.some(role => {
　　return permissionRoles.includes(role)
　　})
　　return hasPermission
　　}
　　console.error(`need roles! Like v-permission="['admin','editor']"`)
　　return false
　　}
```

```
　　<template>
　　<div>
　　<a-button
　　v-if="hasPerms('btn:access:createUser')"
　　:disable="hasPerms('btn:access:createUser')"
　　>
　　新建用户
　　</a-button>
　　<a-button
　　v-if="hasPerms('btn:access:editUser')"
　　:disable="hasPerms('btn:access:editUser')"
　　>
　　编辑用户
　　</a-button>
　　</div>
　　</template>
　　
　　<script>
　　import { Vue, Component, Watch } from "vue-property-decorator";
　　import checkPermission from "@/utils/permission"; // 权限判断函数
　　@Component
　　export default class Access extends Vue {
　　hasPerms(params) {
　　return checkPermission(params);
　　}
　　}
　　</script>
```

      内容已讲述完毕，希望大家有收获。欢迎阅读更多相关内容。