资讯专栏INFORMATION COLUMN

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

3403771864 / 536人阅读

  权限

  路由权限

  静态路由:固定的路由,没有权限。如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>

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/127835.html

相关文章

  • 关于Vue2一些值得推荐文章 -- 五、六月份

    摘要:五六月份推荐集合查看最新的请点击集前端最近很火的框架资源定时更新,欢迎一下。苏幕遮燎沈香宋周邦彦燎沈香,消溽暑。鸟雀呼晴,侵晓窥檐语。叶上初阳乾宿雨,水面清圆,一一风荷举。家住吴门,久作长安旅。五月渔郎相忆否。小楫轻舟,梦入芙蓉浦。 五、六月份推荐集合 查看github最新的Vue weekly;请::点击::集web前端最近很火的vue2框架资源;定时更新,欢迎 Star 一下。 苏...

    sutaking 评论0 收藏0
  • 关于Vue2一些值得推荐文章 -- 五、六月份

    摘要:五六月份推荐集合查看最新的请点击集前端最近很火的框架资源定时更新,欢迎一下。苏幕遮燎沈香宋周邦彦燎沈香,消溽暑。鸟雀呼晴,侵晓窥檐语。叶上初阳乾宿雨,水面清圆,一一风荷举。家住吴门,久作长安旅。五月渔郎相忆否。小楫轻舟,梦入芙蓉浦。 五、六月份推荐集合 查看github最新的Vue weekly;请::点击::集web前端最近很火的vue2框架资源;定时更新,欢迎 Star 一下。 苏...

    khs1994 评论0 收藏0
  • vue2+element 管理后台 集成解决方案 没有没做,只要想不到

    摘要:目前的技术栈主要的采用由于是个人项目,所以数据请求都是用了代替。后续会出一系列的教程配套文章,如如何从零构建后台项目框架,如何做完整的用户系统如权限验证,二次登录等,如何二次开发组件如富文本,如何整合七牛等等文章,各种后台开发经验等等。 完整项目地址:vue-element-admin系类文章一:手摸手,带你用vue撸后台 系列一(基础篇)系类文章二:手摸手,带你用vue撸后台 系列二...

    sanyang 评论0 收藏0
  • Vue+Express全栈购物商城

    摘要:一前言提纲基于和框架写的一个全栈购物商城,记录项目过程中遇到的一些问题以及经验和技巧。服务端技术栈登录授权用认证机制,来实现登录登出。服务器配置和缓存策略,根据不同的来代理。申请证书全站升级到,配置的协议。一、前言提纲 基于Vue和Express框架写的一个全栈购物商城,记录项目过程中遇到的一些问题以及经验和技巧。 二、历史版本 基于Vue-CLI2.0:点我查看 这个分支版本是一两年前...

    Richard_Gao 评论0 收藏0

发表评论

0条评论

3403771864

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<