• vue-admin-better前端页面-菜单-权限配置


    码云地址:

    https://gitee.com/chu1204505056/vue-admin-better

    其登录逻辑为:

    1、登录拿取到token,分配到vuex中;

    2、通过token拿取到用户的permission角色名称,用户头像,昵称,并存放到vuex中;

    3、每次路由时候,调取配置的permission.js ,如果没有角色名称,则返回第二步,如果有,则通

    过token向后台请求当前用户的权限路由,并对基础路由进行拼接,存放到vuex中进行状态管理;

    1、登录部分

    调用 this.$store .dispatch('user/login', this.form)并返回值,

    user/login这样调用是因为,store中导出namespaced为true,就可以根据简写找到vuex中actions的方法

    export default {

        namespaced: true,//引用需要的模块名称 /menu/GetUserMenu

        state,

        mutations,

        actions

    }

    1. login() {
    2. this.loading = true
    3. this.$store
    4. .dispatch('user/login', this.form)
    5. .then(() => {
    6. const routerPath = '/'
    7. this.$router.push(routerPath).catch(() => {})
    8. this.loading = false
    9. })
    10. .catch(() => {
    11. this.loading = false
    12. })
    13. },

     import { getUserInfo, login, logout } from '@/api/userRegistration'

    调用自己定义的登录接口,主要返回token

    1. async login({ commit }, userInfo) {
    2. const { data } = await login(userInfo)
    3. const accessToken = data.access_token;
    4. const setUserInfo=data.userInfo
    5. if (accessToken) {
    6. commit('setAccessToken', accessToken)
    7. commit('setUserInfo',setUserInfo)
    8. const hour = new Date().getHours()
    9. console.log(hour)
    10. const thisTime =
    11. hour < 8
    12. ? '早上好'
    13. : hour <= 11
    14. ? '上午好'
    15. : hour <= 13
    16. ? '中午好'
    17. : hour < 18
    18. ? '下午好'
    19. : '晚上好'
    20. Vue.prototype.$baseNotify(`欢迎登录${title}`, `${thisTime}!`)
    21. } else {
    22. Vue.prototype.$baseMessage(
    23. `登录接口异常,未正确返回${tokenName}...`,
    24. 'error'
    25. )
    26. }
    27. },

    登录接口

    我后台采用 Spring Scurity的oauth2进行登录,接口如下

    1. // 数据格式
    2. const headers = { 'Content-Type': 'application/x-www-form-urlencoded' }
    3. // 请求头添加 Authorization: Basic client_id:client_secret
    4. const auth = {
    5. username: 'mxg-blog-admin',
    6. password: '123456',
    7. }
    8. export function login(data) {
    9. return request({
    10. headers: headers,
    11. auth: auth,
    12. url: `/supplierAuth/login`,
    13. method: 'POST',
    14. params: data,
    15. })
    16. }

    返回字段如下 

     

    2、permission.js 进行路由拦截

    采用如下,对每次路由进行拦截

    router.beforeResolve(async (to, from, next) => {

    逻辑还是比较好理解

    1、判断是否有token,判断路由是否在白名单

    2、判断是否有权限角色(user/permissions)存储 

    3、如果没有则获取用户权限,获取路由并拼接,如果存在,则直接访问

    1. router.beforeResolve(async (to, from, next) => {
    2. if (progressBar) VabProgress.start()
    3. let hasToken = store.getters['user/accessToken']
    4. if (hasToken) {
    5. if (to.path === '/login') {
    6. next({ path: '/' })
    7. if (progressBar) VabProgress.done()
    8. } else {
    9. const hasPermissions =
    10. store.getters['user/permissions'] &&
    11. store.getters['user/permissions'].length > 0
    12. if (hasPermissions) {
    13. next()
    14. } else {
    15. try {
    16. let permissions
    17. if (!loginInterception) {
    18. //settings.js loginInterception为false时,创建虚拟权限
    19. await store.dispatch('user/setPermissions', ['admin'])
    20. permissions = ['admin']
    21. } else {
    22. permissions = await store.dispatch('user/getUserInfo')
    23. }
    24. let accessRoutes = []
    25. if (authentication === 'intelligence') {
    26. accessRoutes = await store.dispatch('routes/setRoutes', permissions)
    27. } else if (authentication === 'all') {
    28. // console.log("all")
    29. console.log('拿取到用户信息')
    30. accessRoutes = await store.dispatch('routes/setAllRoutes')
    31. }
    32. console.log(accessRoutes)
    33. accessRoutes.forEach((item) => {
    34. console.log(item)
    35. router.addRoute(item)
    36. })
    37. next({ ...to, replace: true })
    38. } catch {
    39. await store.dispatch('user/resetAccessToken')
    40. if (progressBar) VabProgress.done()
    41. }
    42. }
    43. }
    44. } else {
    45. console.log(routesWhiteList)
    46. if (routesWhiteList.indexOf(to.path) !== -1) {
    47. next()
    48. } else {
    49. if (recordRoute) {
    50. next(`/login?redirect=${to.path}`)
    51. } else {
    52. next('/login')
    53. }
    54. if (progressBar) VabProgress.done()
    55. }
    56. }
    57. document.title = getPageTitle(to.meta.title)
    58. })
    59. router.afterEach(() => {
    60. if (progressBar) VabProgress.done()
    61. })

    获取用户信息 

      permissions = await store.dispatch('user/getUserInfo')

     获取用户信息

    1. async getUserInfo({ commit, state }) {
    2. const { data } = await getUserInfo(state.accessToken)
    3. if (!data) {
    4. Vue.prototype.$baseMessage('验证失败,请重新登录...', 'error')
    5. return false
    6. }
    7. let { permissions, username, avatar } = data
    8. if (permissions && username && Array.isArray(permissions)) {
    9. commit('setPermissions', permissions)
    10. commit('setUsername', username)
    11. commit('setAvatar', avatar)
    12. return permissions
    13. } else {
    14. Vue.prototype.$baseMessage('用户信息接口异常', 'error')
    15. return false
    16. }
    17. },

    接口如下 ,这个后台可以根据token,自由发挥

    1. export function getUserInfo(accessToken) {
    2. return request({
    3. url: '/supplierSystem/menu/userPermission',
    4. method: 'post',
    5. data: {
    6. "accessToken": accessToken,
    7. },
    8. })
    9. }

    获取在permission.js中路由,并动态添加到route中

    1. accessRoutes = await store.dispatch('routes/setAllRoutes')
    2. console.log(accessRoutes)
    3. accessRoutes.forEach((item) => {
    4. console.log(item)
    5. router.addRoute(item)
    6. })

    在store中找到routes/setAllRoutes方法,获取后台当前用户路由,这个路由,后台要按照前端方式拼接

    这个是源代码的设置路由

    1. async setAllRoutes({ commit }) {
    2. let { data } = await getRouterList()
    3. data.push({ path: '*', redirect: '/404', hidden: true })
    4. let accessRoutes = convertRouter(data)
    5. console.log(accessRoutes);
    6. commit('setAllRoutes', accessRoutes)
    7. return accessRoutes
    8. },

     这个是我的设置的路由。主要是加入了按钮的权限集合在里面

    1. async setAllRoutes({ commit }) {
    2. let { data } = await getRouterList()
    3. let dataRoute = data.menuTreeList
    4. dataRoute.push({ path: '*', redirect: '/404', hidden: true })
    5. let accessRoutes = convertRouter(dataRoute)
    6. commit('setAllRoutes', accessRoutes)
    7. commit('setButtonList', data.buttonList)
    8. return accessRoutes
    9. },

    1. const state = () => ({
    2. routes: [],
    3. partialRoutes: [],
    4. buttonList: [],
    5. })
    6. const getters = {
    7. routes: (state) => state.routes,
    8. partialRoutes: (state) => state.partialRoutes,
    9. buttonList: (state) => state.buttonList,
    10. }
    11. const mutations = {
    12. setRoutes(state, routes) {
    13. state.routes = constantRoutes.concat(routes)
    14. },
    15. setAllRoutes(state, routes) {
    16. state.routes = constantRoutes.concat(routes)
    17. },
    18. setButtonList(state, buttonList) {
    19. state.buttonList = buttonList
    20. },
    21. setPartialRoutes(state, routes) {
    22. state.partialRoutes = constantRoutes.concat(routes)
    23. },
    24. }

    后端构建的菜单结构模式如下 

    1. {
    2. path: '/',
    3. component: 'Layout',
    4. redirect: 'index',
    5. children: [
    6. {
    7. path: 'index',
    8. name: 'Index',
    9. component: '@/views/index/index',
    10. meta: {
    11. title: '首页',
    12. icon: 'home',
    13. affix: true,
    14. },
    15. },
    16. ],
    17. },
    18. {
    19. path: '/personnelManagement',
    20. component: 'Layout',
    21. redirect: 'noRedirect',
    22. name: 'PersonnelManagement',
    23. meta: { title: '人员', icon: 'users-cog', permissions: ['admin'] },
    24. children: [
    25. {
    26. path: 'userManagement',
    27. name: 'UserManagement',
    28. component: '@/views/personnelManagement/userManagement/index',
    29. meta: { title: '用户管理' },
    30. },
    31. {
    32. path: 'roleManagement',
    33. name: 'RoleManagement',
    34. component: '@/views/personnelManagement/roleManagement/index',
    35. meta: { title: '角色管理' },
    36. },
    37. {
    38. path: 'menuManagement',
    39. name: 'MenuManagement',
    40. component: '@/views/personnelManagement/menuManagement/index',
    41. meta: { title: '菜单管理', badge: 'New' },
    42. },
    43. ],
    44. },

    后端测试构建菜单 (没有加按钮权限集合)

    1. @Override
    2. public Result selectMenuThreeByUserIdTwo(String userId) {
    3. ArrayList<Object> arrayList = new ArrayList<>();
    4. HashMap<String, Object> hashMap = new HashMap<>();
    5. hashMap.put("path", "/");
    6. hashMap.put("component", "Layout");
    7. hashMap.put("redirect", "index");
    8. LinkedList<Object> children = new LinkedList<>();
    9. HashMap<String, Object> hashMap1 = new HashMap<>();
    10. hashMap1.put("path", "index");
    11. hashMap1.put("name", "Index");
    12. hashMap1.put("component", "@/views/index/index");
    13. HashMap<Object, Object> hashMap2 = new HashMap<>();
    14. hashMap2.put("title", "首页");
    15. hashMap1.put("meta", hashMap2);
    16. children.add(hashMap1);
    17. hashMap.put("children", children);
    18. arrayList.add(hashMap);
    19. HashMap<String, Object> hashMap11 = new HashMap<>();
    20. hashMap11.put("path", "/personnelManagement");
    21. hashMap11.put("component", "Layout");
    22. hashMap11.put("redirect", "noRedirect");
    23. hashMap11.put("name", "PersonnelManagement");
    24. HashMap<Object, Object> hashMap22 = new HashMap<>();
    25. hashMap22.put("title", "人员管理");
    26. hashMap11.put("meta", hashMap22);
    27. ArrayList<Object> arrayList2 = new ArrayList<>();
    28. HashMap<Object, Object> hashMap3 = new HashMap<>();
    29. hashMap3.put("path", "userManagement");
    30. hashMap3.put("name", "UserManagement");
    31. hashMap3.put("component", "@/views/personnelManagement/userManagement/index");
    32. HashMap<Object, Object> hashMap4 = new HashMap<>();
    33. hashMap4.put("title", "人员管理");
    34. hashMap3.put("meta", hashMap4);
    35. arrayList2.add(hashMap3);
    36. HashMap<Object, Object> hashMap5 = new HashMap<>();
    37. hashMap5.put("path", "roleManagement");
    38. hashMap5.put("name", "roleManagement");
    39. hashMap5.put("component", "@/views/personnelManagement/roleManagement/index");
    40. HashMap<Object, Object> hashMap6 = new HashMap<>();
    41. hashMap6.put("title", "角色管理");
    42. hashMap5.put("meta", hashMap6);
    43. arrayList2.add(hashMap5);
    44. HashMap<Object, Object> hashMap7 = new HashMap<>();
    45. hashMap7.put("path", "roleManagement");
    46. hashMap7.put("name", "roleManagement");
    47. hashMap7.put("component", "@/views/personnelManagement/roleManagement/index");
    48. HashMap<Object, Object> hashMap8 = new HashMap<>();
    49. hashMap8.put("title", "菜单管理");
    50. // hashMap8.put("badge","New");
    51. hashMap7.put("meta", hashMap6);
    52. arrayList2.add(hashMap7);
    53. hashMap11.put("children", arrayList2);
    54. arrayList.add(hashMap11);
    55. return Result.ok(arrayList);
    56. }

    右边页面

    通过如下导入的 vab-side-bar文件,如果你想自定义修改菜单栏,则可以找到下面修改

    在nodemudle中可以查看下载的菜单文件, 

    3、自定义按钮权限 

    前面在菜单和按钮集合存在vuex之后;配置自定义指令,来显示或者隐藏按钮

    1. // 引入所有要注册的全局指令
    2. import permission from './permission'
    3. // 将permission文件导出的对象引入
    4. export default (Vue) => {
    5. // 第一个参数为指令名称,v-permission 使用,
    6. // 注意在生命指令名时候,不能加v-
    7. Vue.directive('permission', permission)
    8. }
    9. // 然后在main.js 中全局引入这个文件

    permission .js如下

    1. //自定义权限指令
    2. import store from '@/store/index'
    3. // 导出一个对象
    4. export default {
    5. // 指令钩子
    6. inserted(el, binding) {
    7. //el 指令作用到哪个元素上
    8. //获取使用指令时传递的值
    9. const { value } = binding
    10. //获取用户当前所拥有的按钮权限
    11. // store.getters['user/permissions']
    12. // const buttonList = store.getters && store.getters.buttonList
    13. const buttonList = store.getters['routes/buttonList']
    14. if (value) {
    15. // 用some,不用foreach 因为如果下面为true,则进行终止循环
    16. const hasPermission = buttonList.some((button) => {
    17. return button === value
    18. })
    19. // 如果没有权限,则将元素移除
    20. if (!hasPermission) {
    21. el.parentNode && el.parentNode.removeChild(el)
    22. }
    23. } else {
    24. throw new Error("需要权限标识!比如v-permission='article:delete'")
    25. }
    26. },
    27. }

     在main.js中引入这个自定义指令,

    import directive from '@/directive' //导入自定义指令

    Vue.use(directive)

    使用如下 

    1. <el-button
    2. type="primary"
    3. @click="handleQuery"
    4. v-permission="'user:search'"
    5. >
    6. 搜索
    7. </el-button>

     

  • 相关阅读:
    cesium文字实现避让功能
    C++ | 简单线程池的实现
    flutter 创建插件
    Google Data Fusion构建数据ETL任务
    pyhton 类动态绑定函数
    基于Python的“书怡”在线书店系统的设计与实现毕业设计源码082332
    CentOS8中文乱码问题
    【个人博客系统 × Redis】“最后的升级” · 连接Redis · Redis的基本使用
    linux 硬盘存储剩余容量自动化监控+报警通知
    R包学习——reshape包中melt、cast、merge函数用法
  • 原文地址:https://blog.csdn.net/weixin_43288858/article/details/125615077