Vue-admin-element,路由权限和按钮权限理解及全部流程

前言

个人理解的路由权限,按钮权限无非就是,根据登录用户的角色来向后台请求这个角色拥有的权限;

换种说法就是,登录的账号它能查看的路由有哪些,对应的页面按钮操作又有哪些,知道了这些信息后就是交给前端去通过返回的数据操作替换路由表展示返回数据里的路由

一、登录

login.vue

handleLogin() {
  this.$refs.loginForm.validate(valid => {
    if (valid) {
      // 将username和password派发到该方法中
      this.$store.dispatch('user/login', this.loginForm)
        .then(() => {
          // 登录成功,跳转至首页
          this.$router.push({ path: '/' })
        })
        .catch(() => {
          this.loading = false
        })
    } else {
      console.log('error submit!!')
      return false
    }
  })
}

src/store/modules/user.js

const actions = {
  // user login
  login({ commit }, userInfo) {
    // 根据页面递来的数据
    const { username, password } = userInfo
    return new Promise((resolve, reject) => {
      // api/user
      // 连接后端接口,根据前端请求的用户信息,后端返回唯一对应的随机token
      login({ username: username.trim(), password: password }).then(response => {
        const { data } = response
        // commit会触发mutations,存储token
        commit('SET_TOKEN', data.token)
        setToken(data.token)
        resolve()
      }).catch(error => {
        reject(error)
      })
    })
  }
}

二、权限路由

src/permission.js

import router from './router'
import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress' // progress bar
import 'nprogress/nprogress.css' // progress bar style
import { getToken } from '@/utils/auth' // get token from cookie
import getPageTitle from '@/utils/get-page-title'
 
NProgress.configure({ showSpinner: false }) // NProgress Configuration
const whiteList = ['/login'] // 白名单
 
//全局前置路由,若无next()则不会进行下一步骤
//在每个路由跳转前都会执行该方法
router.beforeEach(async(to, from, next) => {
  // start progress bar
  NProgress.start()
  // set page title
  document.title = getPageTitle(to.meta.title)
  // 根据是否存在token,判断是否登录
  const hasToken = getToken()
  
  // 若token存在,则该用户已登录
  if (hasToken) {
    if (to.path === '/login') {
      // 若此时页面在登录页面,则跳转至首页
      next({ path: '/' })
      NProgress.done() 
    } else {
      // 若此时不在登录页面,则获取用户角色权限,判断所在页面该用户是否能进入
      const hasRoles = store.getters.roles && store.getters.roles.length > 0
 
      if (hasRoles) {
        next()
      } else {
        try {
          // 若该用户之前未存储角色权限在store中,则前往store目录下的该路径,根据store存储过的token,获取并存储该用户的角色权限
          const { roles } = await store.dispatch('user/getInfo')
          // 将角色权限派发到该路径下
          const accessRoutes = await store.dispatch('permission/generateRoutes', roles)
          // 生成动态路由
          router.addRoutes(accessRoutes)
          // 再重新返回判断角色权限是否存在,进行下一步骤
          next({ ...to, replace: true })
        } catch (error) {
          // remove token and go to login page to re-login
          await store.dispatch('user/resetToken')
          //这里会报个参数类型异常,所以将参数 由error 改为了 error.message 
          Message.error(error.message || 'Has Error')
          next(`/login?redirect=${to.path}`)
          NProgress.done()
        }
      }
 
    }
  } else {
    /* has no token*/
    if (whiteList.indexOf(to.path) !== -1) {
      // in the free login whitelist, go directly
      next()
    } else {
      // other pages that do not have permission to access are redirected to the login page.
      next(`/login?redirect=${to.path}`)
      NProgress.done()
    }
  }
})
 
router.afterEach(() => {
  // finish progress bar
  NProgress.done()
})

src/store/modules/user.js

const actions = {
  // get user info
  getInfo({ commit, state }) {
    return new Promise((resolve, reject) => {
      // 连接后端接口,根据token数据,后端返回对应的用户信息,如角色权限roles
      getInfo(state.token).then(response => {
        const { data } = response
 
        if (!data) {
          return reject('Verification failed, please Login again.')
        }
 
        const { name, avatar, roles } = data
      
        // 存储用户信息,方便用于全局
        commit('SET_NAME', name)
        commit('SET_AVATAR', avatar)
        commit('SET_ROLES', roles)
        resolve(data)
      }).catch(error => {
        reject(error)
      })
    })
  }
}

 src/router/index.js

import Vue from "vue";
import Router from "vue-router";

Vue.use(Router);

/* Layout */
import Layout from "@/layout";

// 固定路由
export const constantRoutes = [
  {
    path: "/login",
    component: () => import("@/views/login/index"),
    // hidden: true,
    neme: "Login",
  },
  {
    path: "/rebuild",
    component: () => import("@/views/login/rebuild"), // 忘记密码页
    // hidden: true
  },
  {
    path: "/auth-redirect",
    component: () => import("@/views/login/auth-redirect"),
    // hidden: true
  },
  {
    path: "/403",
    component: () => import("@/views/403"),
    // hidden: true
  },
  {
    path: "/",
    component: Layout,
    redirect: "/dashboard",
    children: [
      {
        path: "dashboard",
        component: () => import("@/views/dashboard/index"),
        name: "Dashboard",
        meta: {
          title: "首页",
          icon: "el-icon-s-home",
          roles: ["admin"],
          affix: true,
        },
      },
      {
        path: "/personal",
        component: () => import("@/views/permission/personal.vue"),
        name: "PersonalPermission",
        hidden: true,
        meta: {
          title: "个人中心",
        },
      },
    ],
  },
];
// 权限(动态)路由(需要根据权限进行判断)
export const asyncRoutes = [
  // 后台权限
  {
    path: "/permission",
    component: Layout,
    redirect: "/permission/user",
    alwaysShow: true, // will always show the root menu
    name: "Permission",
    meta: {
      title: "权限模块",
      icon: "lock",
      roles: ["admin", "editor"], // you can set roles in root nav
    },
    children: [
      {
        path: "/permission/user",
        component: () => import("@/views/permission/user.vue"),
        name: "UserPermission",
        meta: {
          title: "用户管理",
        },
      },
      {
        path: "/permission/role",
        component: () => import("@/views/permission/role.vue"),
        name: "RolePermission",
        // hidden: true,
        meta: {
          title: "角色管理",
          roles: ["admin"],
        },
      },
      {
        path: "/permission/roleModify",
        component: () => import("@/views/permission/roleModify.vue"),
        name: "RoleModifyPermission",
        hidden: true,
        meta: {
          title: "角色管理调整",
        },
        // hidden: true
      },
      {
        path: "/permission/rightsManage",
        component: () => import("@/views/permission/rightsManage.vue"),
        name: "RightsManagePermission",
        // hidden: true,
        meta: {
          title: "权限管理",
          roles: ["admin"],
        },
      },
      {
        path: "/permission/file",
        component: () => import("@/views/permission/file.vue"),
        name: "FilePermission",
        // hidden: true,
        meta: {
          title: "文件管理",
          roles: ["admin"],
        },
      },
    ],
  },
  // 数据同步
  {
    path: "/dataSync",
    component: Layout,
    redirect: "/data-sync/database-access",
    alwaysShow: true, // will always show the root menu
    name: "DataSync",
    meta: {
      title: "数据同步",
      icon: "el-icon-s-data",
      roles: ["admin"], // you can set roles in root nav
    },
    children: [
      {
        path: "siteList",
        component: () => import("@/views/dataSync/siteList.vue"),
        name: "SiteList",
        meta: {
          title: "网站列表",
        },
      },
      {
        path: "taskList",
        component: () => import("@/views/dataSync/taskList.vue"),
        name: "TaskList",
        meta: {
          title: "任务列表",
        },
      },
      {
        path: "sourceList",
        component: () => import("@/views/dataSync/sourceList.vue"),
        name: "SourceList",
        meta: {
          title: "数据源列表",
        },
      },
      {
        path: "databaseAccess",
        component: () => import("@/views/dataSync/databaseAccess.vue"),
        name: "DatabaseAccess",
        meta: {
          title: "数据库接入",
        },
      },
      {
        path: "directorySync",
        component: () => import("@/views/dataSync/directorySync.vue"),
        name: "directorySync",
        meta: {
          title: "目录同步",
        },
      },
      {
        path: "theLog",
        component: () => import("@/views/dataSync/theLog.vue"),
        name: "theLog",
        meta: {
          title: "日志",
        },
      },
    ],
  },
  // 邮件课题管理 Email topic
  {
    path: "/emailTopic",
    component: Layout,
    redirect: "/email-topic/email-list",
    alwaysShow: true, // will always show the root menu
    name: "emailTopic",
    meta: {
      title: "邮件课题管理",
      icon: "youJian",
      roles: ["admin", "editor"], // you can set roles in root nav
    },
    children: [
      {
        path: "/emailTopic/emailTopicList",
        component: () => import("@/views/emailTopic/emailTopicList.vue"),
        name: "EmailTopicList",
        meta: {
          title: "邮件课题列表",
        },
      },
      // hidden: true,
      {
        path: "additionTopic",
        component: () => import("@/views/emailTopic/additionTopic.vue"),
        name: "AdditionTopic",
        hidden: true,
        meta: {
          title: "邮件课题新增",
        },
      },
      {
        path: "additionTopic_edit",
        component: () => import("@/views/emailTopic/additionTopic.vue"),
        name: "AdditionTopic_edit",
        hidden: true,
        meta: {
          title: "邮件课题编辑",
        },
      },
      {
        path: "emailTopic_search",
        component: () => import("@/views/emailTopic/emailTopic_se.vue"),
        name: "EmailTopic_search",
        hidden: true,
        meta: {
          title: "课题邮箱搜索",
        },
      },
      {
        path: "topic_upload",
        component: () => import("@/views/emailTopic/topic_upload.vue"),
        name: "Topic_Upload",
        hidden: true,
        meta: {
          title: "上传课题记录",
        },
      },
      {
        path: "topic_log",
        component: () => import("@/views/emailTopic/topic_log.vue"),
        name: "Topic_Log",
        hidden: true,
        meta: {
          title: "上传日志记录",
        },
      },
    ],
  },
  // // 发帖课题列表  Post topic
  {
    path: "/postTopic",
    component: Layout,
    redirect: "/post-topic/post-list",
    alwaysShow: true, // will always show the root menu
    name: "PostTopic",
    meta: {
      title: "发帖课题管理",
      icon: "el-icon-s-unfold",
      roles: ["admin"], // you can set roles in root nav
    },
    children: [
      // 发帖课题列表
      {
        path: "postTopicList",
        component: () => import("@/views/postTopic/postTopicList.vue"),
        name: "PostTopicList",
        meta: {
          title: "发帖课题列表",
        },
      },
      {
        path: "addPostTopic",
        component: () => import("@/views/postTopic/addPostTopic.vue"),
        name: "AddPostTopic",
        meta: {
          title: "新增发帖课题",
        },
        hidden: true,
      },
      {
        path: "uploadTopic",
        component: () => import("@/views/postTopic/uploadTopic.vue"),
        name: "UploadTopic",
        meta: {
          title: "上传课题记录",
        },
        hidden: true,
      },
      {
        path: "uploadPostTopic",
        component: () => import("@/views/postTopic/uploadPostTopic.vue"),
        name: "UploadPostTopic",
        meta: {
          title: "上传发帖记录",
        },
        hidden: true,
      },
      {
        path: "deletePostTopic",
        component: () => import("@/views/postTopic/deletePostTopic.vue"),
        name: "DeletePostTopic",
        meta: {
          title: "修改发帖课题",
        },
        hidden: true,
      },
      // 品牌版本列表
      {
        path: "brandVersionList",
        component: () => import("@/views/postTopic/brandVersionList.vue"),
        name: "BrandVersionList",
        meta: {
          title: "品牌版本列表",
        },
      },
      // 平台列表
      {
        path: "platformList",
        component: () => import("@/views/postTopic/platformList.vue"),
        name: "PlatformList",
        meta: {
          title: "平台列表",
        },
      },
    ],
  },
];

// 创建路由
const createRouter = () =>
  new Router({
    mode: "history",
    // base: '/'
    scrollBehavior: () => ({ y: 0 }),
    routes: constantRoutes,
  });

const router = createRouter();

// 重置路由
export function resetRouter() {
  const newRouter = createRouter();
  router.matcher = newRouter.matcher; // reset router
}

export default router;

src/store/modules/permission.js 

这里是操作后端返回的数据,转变为本地路由的形式,在存储起来,之后 在 permission.js 文件中使用  router.addRoutes()   作用是动态添加可访问路由表

// 引入全部动态路由 和静态路由
import { asyncRoutes, constantRoutes } from "@/router/index";
import API from "@/api";
// 声明变量 储存数据
const state = {
  routes: [],
};
// 同步修改state中变量的值
const mutations = {
  setRoutes(state, menus) {
    state.routes = [...constantRoutes, ...menus];
  },
};
// 异步修改state中的值,
// 通过调用mutations中的函数来间接修改
const actions = {
  // 根据权限数组和 全部动态路由数组
  // 进行筛选 出具有权限的路由对象数组
  async filterRoutes(store, menus) {
    const getJurisd = [];
    const menues = [];
    const menu = [];
    const numberF = [];
    await API.getJurisdList().then((res) => {
      let menus = res.data.data;
      const num = [];
      const value = [];
      //拿到所有子路由
      function getName(val) {
        val.forEach((item, index) => {
          if (item.children) {
            //这里不加没有父级
            value.push(item);
            getName(item.children);
          } else {
            num.push(item);
          }
        });
        return [num, value];
      }
      let items = getName(menus);
      //子:过滤出符合条件的数据
      let vueRouter = items[0].filter((item) => {
        return item.route_vue !== null && item.route_vue !== "";
      });
      //父:过滤出符合条件的数据
      let vueRoute = items[1].filter((item) => {
        return item.route_vue !== null && item.route_vue !== "";
      });
      vueRouter.push(...vueRoute);
      //只要需要的数据
      vueRouter.forEach((item) => {
        menues.push(item.name);
        menu.push(item.route_vue);
        getJurisd.push({ name: item.name, route_vue: item.route_vue });
      });
      //父级
      menus.forEach((item) => {
        numberF.push({ name: item.name, route_vue: item.route_vue });
      });
    });
    //替换路由数据
    function filterMenu(menuList, getJurisd) {
      menuList.forEach((item) => {
        if (item.children) {
          filterMenu(item.children, getJurisd);
        } else {
          getJurisd.forEach((obj) => {
            if (item.name == obj.route_vue) {
              item.meta.title = obj.name;
            }
          });
        }
      });
    }
    const myMenu = filterMenu(asyncRoutes, getJurisd);

    asyncRoutes.forEach((item) => {
      numberF.forEach((obj) => {
        if (item.name == obj.route_vue) {
          item.meta.title = obj.name;
        }
      });
    });
    // memus: 请求回来的路由权限数组
    // 全部的动态路由数组
    //过滤选择出父级符合标准的路由
    const res = asyncRoutes.filter((item) => menus.includes(item.name));
    const data = [];
    asyncRoutes.forEach(function (e, i) {
      data.push(e.children.filter((item) => menus.includes(item.name)));
      asyncRoutes[i].children = data[i];
    });
    //过滤出子路由为空的
    const routes = asyncRoutes.filter(function (item) {
      if (!item.children.length == 0) {
        return item;
      }
    });
    store.commit("setRoutes", routes);
    return routes;
  },
};

export default {
  namespaced: true,
  state,
  mutations,
  actions,
};

这里项目返回的后端数据形式是:

 返回的路由数据形式:

返回的是路由的 name 

 返回的按钮权限数据形式:

src/store/getters.js 

// 存储用于全局
roles: state =>state.user.roles,
permission_routes: state=>state.permission.routes

src/layout/components/Sidebar/index.vue

<!--根据角色权限路由表遍历菜单-->
<sidebar-item v-for="route in permission_routes" :key="route.path" :item="route" :base-path="route.path" />
 
<script>
import { mapGetters } from 'vuex'
import SidebarItem from './SidebarItem'
 
export default {
  components: { SidebarItem },
  computed: {
    ...mapGetters([
      // 获取全局中存储的被角色权限过滤过的路由表
      'permission_routes'
    ]),
  }
}
</script>

三、按钮权限 

mixin 全局混入的方法

按钮权限的话 使用 Vue自定义指令 或者 使用 mixin 全局混入的方法,

这里我使用的是  mixin 全局混入

在main.js 里面 导入

import mixin from '@/mixin/index'
Vue.mixin(mixin)

 src/mixin/index.js  创建文件夹,代码如下:

import store from '@/store'
// 利用mixin(混入)来封装全局函数
// 这里封装在methods中的所有函数,都自动会添加到
// 每个vue文件中methods中
// 封装判断按钮权限的函数
export default {
  methods: {
    // 判断是否具有按钮权限的函数
    // 判断需要有2个条件
    // 1、需要有判断的标识符(到底要判断哪个按钮显示隐藏),这个标识符就是绑定函数时传递过来的值
    // 2、按钮权限的数据集合,就是保存到vuex中的operation数组
    isBtnPerm(key) {
      const { userInfo } = store.state.user
      if (userInfo.roles.operation && userInfo.roles.operation.length) {
        return userInfo.roles.operation.some(item => item === key)
      }
      return false
    }
    }
  }

  

以上是本人做路由权限及按钮权限的理解,希望对读者能有帮助 !

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值