Vue动态添加路由实现菜单和数据权限管理

3 篇文章 0 订阅

前言

在我们做一个管理系统的时候,一般比较重要也是最基础的地方就是权限管控这部分。

权限管理在来说基本有两个方面:

一、控制页面级访问权限;

     页面访问权限控制实质上是控制页面是否显示,这里有两种方法:

     1、添加所有路由和菜单,当用户在浏览器输入菜单路径时,不在权限内的显示无权限;

     2、动态添加只在权限内的菜单和路由,当用户输入url绕过菜单时,显示404页面,提示无权限。

二、控制数据操作权限。

      比如页面上的增删改查等一系列按钮的控制。

当然做权限管控,一般我们都会有专门的权限维护页面,包括菜单、角色、用户管理。

Mock菜单维护

本文一切从简,就不详细介绍维护页面了,这里mock出所需的经过分配权限后的菜单数据如下:

// 正常来说这里是后台根据权限维护页,返回给前台的数据
// menuName 是菜单名
// menuUrl 是菜单对应的路由
// buttonList 是按钮的集合,按钮分为两种,1.点击按钮跳转到一个页面(需要添加路由) 2.点击按钮做数据操作(不需添加路由,menuUrl为空)
export const navData = [
    {
        menuName: '系统管理',
        childrenMenu: [
            {
                menuName: '消息管理',
                menuUrl: 'xxxx/xxxxx'
                // 此处菜单添加按钮
                buttonList: [
                    {
                        menuName: '新增消息',
                        menuUrl: ''
                    },
                    {
                        menuName: '查询',
                        menuUrl: ''
                    }
                ]
            },
            {
                menuName: '数据管理',
                menuUrl: 'xxxx/xxxx'
            }
        ]
    },
    {
        menuName: '任务计算',
        menuUrl: 'xxxxxxxxx/xxxxxx/xxxx',
        // 此处菜单添加按钮
        buttonList: [
            {
                menuName: '新建',
                menuUrl: 'xxxxxx'
            }
        ]
    },
    {
        menuName: '任务发布',
        childrenMenu: [
            {
                menuName: '定时发布',
                menuUrl: 'xxxxx'
            },
            {
                menuName: '立即发布',
                menuUrl: 'xxxx'
            },
            {
                menuName: '发布历史',
                menuUrl: 'xxxx'
            }
        ]
    },
    {
        menuName: '任务更新',
        menuUrl: 'xxxx',
        // 此处菜单添加按钮
        buttonList: [
            {
                menuName: '新建',
                menuUrl: 'xxxx'
            }
        ]
    }
]

mock出的数据只是一种,实际数据是根据分配的权限不一样,后台返回的不一样的。

渲染菜单

根据element-ui的菜单组件,改写项目的菜单组件。(本文主要讲权限控制这里就不详细说明了)

路由数据处理

首先我们构思是在构建vue对象的时候,去调取后台查询菜单权限的接口,然后把获取的菜单存放在vuex中,最后经过处理成router,用router.addRoutes方法去动态添加路由。然后根据菜单id,去查询每个页面的按钮权限,通过自定义指令控制按钮的显示隐藏。

1.首先在vuex中构建menu.js


import http from '@/js/http.js'

import apiPath from '@/api/api-path'
const menu = {
    namespaced: true,
    state: {
        menus: [],
        permissionButton: []
    },
    mutations: {
        update (state, data) {
            state.menus= data
        },
        clear (state) {
            state.menus= []
        },
        changButtonList (state, data) {
            state.permissionButton = data
        }
    },
    actions: {
        load ({ commit }) {
            return http.getRequest(apiPath.getUserInfo, {}, (res) => {
                const items = res.data  // 此处为后台返回的权限集合
                commit('update', items)
                return res.data
            })
        },
        getButton ({ commit }, data) {
            return http.getRequest(apiPath.getButtonByMenuId, {menuId: data.id}, (res) => {
                let buttonList = []
                if (res.data && res.data.buttonList) {
                    res.data.buttonList.forEach(item => {
                        buttonList.push(item.menuName)
                    })
                }
                commit('changButtonList', buttonList)
            })
        }
    }
}

export default menu

2.在vuex的index.js注册挂载模块

import Vue from 'vue'
import Vuex from 'vuex'
import menu from './menu.js'


Vue.use(Vuex)

const store = new Vuex.Store({
    actions: {
        init ({ dispatch, commit }) {
            return dispatch('menu/load').then((data) => {
                    // 做一些自己想做的操作
                return data
            })
        }
    }
})

store.registerModule('menu', menu)

3.在router中构建index.js动态添加路由

import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)

const createRouter = () =>
    new Router({
        routes: [],
        scrollBehavior (to, from, savedPosition) {
            if (savedPosition) {
                return savedPosition
            } else {
                return { x: 0, y: 0 }
            }
        },
        strict: process.env.NODE_ENV !== 'production'
    })

const router = createRouter()
export default router

export const addRoutes = (routes) => {
    router.addRoutes([
        ...routes,
        {
            path: '/',
            redirect: '首页的路由地址'
        },
        {
            path: '*',
            component: () => import('@/components/common/UnAuthorized.vue'),
            meta: { title: '页面找不到' }
        }
    ])
}

4.编写路由处理文件getrouter.js (根据配置路由处理文件加载路径和路由参数等)

function getPaths (menus) {
    let arr = []
    menus.forEach((item) => {
        const { childrenMenu } = item
        if (item.menuUrl) {
            arr.push({
                path: item.menuUrl,
                menuName: item.menuName,
                id: item.id
            })
        }
        if (childrenMenu && childrenMenu.length > 0) {
            const result = getPaths(childrenMenu)
            arr = arr.concat(result)
        }
    })
    return arr
}

const importFile = (path) => {
    const componentName = path.split('/').map((str) => {
        if (str && str.indexOf('-') > -1) {
            let strs = ""
            str.split('-').map((item) => {
                return item && item.replace(item[0], item[0].toUpperCase())
            }).forEach((items) => {
                strs += items
            })
            str = strs
        }
        return str
    }).join('/')
    const fileUrl= () => import(/* webpackChunkName: "[request]" */`@/components${componentName}.vue`)
    return fileUrl
}
const getName = (path) => {
    const name = path.split('/').map((str) => {
        return str
    }).filter((item) => {
        if (item) return item
    }).join('-')
    return name
}

export default function getRoutes (data) {
    return getPaths(data).map(({ path, menuName, id }) => ({
        path,
        name: getName(path),
        component: importFile(path),
        meta: {
            title: menuName,
            id
        }
    }))
}

5.new vue添加处理逻辑

import getRoutes from 'getrouter.js'

new Vue({
    router,
    store,
    methods: {
        // 初始化获取菜单并添加路由
        init () {
            store.dispatch('init').then((menus) => {
                addRoutes(getRoutes(menus))
            })
        }
    },
    created () {
        this.init()
    },
    render: h => h(App)
}).$mount('#app')

6.添加路由监控查询菜单按钮权限

router.beforeEach((to, from, next) => {
    if (to.meta.id) {
        store.dispatch('menu/getButton', {
            id: to.meta.id
        })
    }
    next()
})

7.最后我们编写全局指令去控制按钮显示隐藏

/* 使用方法:v-hasPermission="'按钮名'" */
Vue.directive('hasPermission', {
  update (el, binding, vnode) {
      let permissionList = vnode.context.$store.state.menu.permissionButton
           if (permissionList && permissionList.length                 
                &&!permissionList.includes(binding.value)) {
                    el.style.display = 'none'
                } else {
                    el.style.display = 'inline-block'
                }
    }
})

至此,我们的权限管控就到此结束了!

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值