VUE动态权限菜单

第一种 后台返回的是多维数组,即菜单格式以及排好

// main.js
Vue.prototype.routerLoad = {
    // 遍历添加路由
    bianli(arr) {
        return arr.map(v => {
            // 子菜单判断
            if (v.subs) {
                // this.menus.push(...this.bianli(v.subs))
                return this.bianli(v.subs);
            } else {
                return {
                    path: "/" + v.index,
                    name: v.index,
                    // 路由地址拼接---注意动态路由对文件名的要求很高,最好名称一致
                    component: () =>import (`@/components/page/${v.index}/${v.index}.vue`),
                    meta: { title: v.title }
                }
            }
        })

    },
    // 获取菜单
    loadMenus() {
        let userId = sessionStorage.getItem('userId')
        if (userId) {
        	// 因为是在main.js里面,所以这里是 axios
        	// 掉接口获取后台返回的菜单格式
            axios.get(`/api/permission/role/functions/${userId}`).then(res => {
                    console.log('动态路由添加成功')
                    console.log(res)
                    let menus = res.data;
                	// vuex中存储 菜单列表 用于页面菜单的显示
                    store.commit('setMenus', menus)
                    // flat(number)  数组拉平 number:拉平的层数,默认一层
                	// 由于遍历调用多次,所以返回的是多维数组,所以需要拉平
                	// 想要代码严谨,拉平次数可以动态添加
                    let routerArr = this.bianli(menus).flat(9);
                	// 路由添加添加
                    router.options.routes[1].children.push(...routerArr);
                    router.addRoutes(router.options.routes);
                    // 刷新后跳转到刷新前页面---因为路由是动态添加的,所以要保存刷新前页面的路由
                    if (sessionStorage.getItem('path') == '/') {
                        sessionStorage.setItem('path', '/dashboard')
                    }
                	// 刷新后的路由跳转
                    router.push({ path: sessionStorage.getItem('path') })

                })
                .catch(err => {
                    console.log("网络错误");
                });
        }
    }
}
//使用钩子函数对路由进行权限跳转
router.beforeEach((to, from, next) => {
    // 存储当前路由值,用于刷新跳转
    if (to.path != '/404' && to.path != "/403") {
    	// 跳转时存储路由 
        sessionStorage.setItem('path', to.fullPath)
    }
})

new Vue({
    router,
    store,
    render: h => h(App),
    created() {
        // 本地存储时存储一个值,防止初始打开登陆页面时调用---可以用 token
        // 主要用于页面刷新时调用
        if (sessionStorage.getItem('token')) {
            this.routerLoad.loadMenus()
        }
    }
}).$mount('#app')

-------------------------------------------------------------------------------

// Login.vue 页面
// 登陆成功后
    sessionStorage.setItem('token',123465)
	this.routerLoad.loadMenus()
	// 跳转首页
	this.$router.push('/dashboard')

-------------------------------------------------------------------------------

// store.js
import Vue from "vue"
import Vuex from 'vuex'
Vue.use(Vuex);
const store = new Vuex.Store({
  state: {
    menus: []
  },
  mutations: {
    // 菜单
    setMenus(state, menus) {
        state.menus = menus
    }
  },
  actions: {
  },
  getters: {

  }
});
export default store

第二种 后台返回的是一维数组,即通过里面的父级Id值来遍历判断

// main.js
Vue.prototype.routerLoad = {
    /**
     * 过滤函数
     */
    filterArr(arr, i) {
        let obj = undefined
        arr.find(item => {
            if (item.id == i) {
                obj = item
                return true
            } else if (item.subs) {
                obj = this.filterArr(item.subs, i)
                if (obj) {
                    return obj
                }
            }
        })
        return obj
    },
    /**
     * 遍历函数
     */
    arr(arr, roleTree, roleTreeObj) {
        arr.find((item, index) => {
            let itemObj = {
                    icon: item.iocn,  // 菜单图标
                    index: item.urlPre,  // 菜单列表跳转路径
                    title: item.name,  // title
                    id: item.id,  // 菜单id值
                    sort: item.sort,  // 菜单排序值---菜单列表项的前后顺序
                    superior: item.superior  // 父级菜单
                }
            // 最外层菜单---一级菜单
            if (item.id == item.superior) {
                roleTree.push(itemObj)
            } else {
            	// 调用过滤函数查找到当前父级id在菜单数组的位置
                let obj = this.filterArr(roleTree, item.superior)
                // 没有匹配到时:因为后台返回的菜单顺序不同,有可能父级菜单在下面,子菜单在上面
                if (!obj) {
                    // 存储暂时还没有遍历到的上级,以键值对的形式存储,{父级id : 子菜单列表数组}
                    // 判断对象中的父级id是否存在
                    if (!roleTreeObj[item.superior]) {
                        roleTreeObj[item.superior] = []
                    }
                    roleTreeObj[item.superior].push(itemObj)
                } else {
                	// 有结果时,判断当前父级是否有子数组
                    if (!Array.isArray(obj.subs)) {
                        obj.subs = []
                    }
                    obj.subs.push(itemObj)
                }
            }

        })
        // 遍历完成,返回菜单数组和菜单对象
        return { roleTree, roleTreeObj }
    },
    /**
     * 对象遍历函数
     */
    binaliObj(allObj) {
        for (let key in allObj.roleTreeObj) {
            let obj = this.filterArr(allObj.roleTree, key)
            if (obj) {
                if (!Array.isArray(obj.subs)) {
                    obj.subs = []
                }
                obj.subs.push(...allObj.roleTreeObj[key])
                // 每添加一个就删除对象里面对应的值
                delete allObj.roleTreeObj[key]
            }
        }
        // for in 循环完后判断对象里面是否还有数据
        if (Object.keys(allObj.roleTreeObj).length != 0) {
            this.binaliObj(allObj)
        }
        // return allObj.roleTree
        // 复杂数据类型,所以不需要return出来
        return false
    },
    /**
     * 数组排序
     */
    sortArr(arr) {
        arr.sort(function(a, b) {
            if (a.sort > b.sort) { // a-->arr[j]  b-->arr[j+1]
                return 1;
            } else if (a.sort == b.sort) {
                return 0;
            } else {
                return -1;
            }
        })
        // 多维数组,所以遍历排序
        arr.forEach(item => {
            if (item.subs) {
                this.sortArr(item.subs)
            }
        });

    },
    // 遍历添加路由
    bianli(arr) {
        return arr.map(v => {
            // 子菜单判断
            if (v.subs) {
                // this.menus.push(...this.bianli(v.subs))
                return this.bianli(v.subs);
            } else {
                return {
                    path: v.index,
                    name: v.index.slice(1),
                    component: () => {
                    	// 因为编写代码时,文件格式没写好,所以这里要进行判断
                        if (v.index == '/Dashboard') {
                            return import (`@/components/page${v.index}.vue`)
                        } else if (v.index == '/SI') {
                            return import (`@/components/page/Sim/Sim.vue`)
                        } else {
                            return import (`@/components/page${v.index}${v.index}.vue`)
                        }
                    },
                    meta: { title: v.title }
                }
            }
        })

    },
    // 获取菜单
    loadMenus() {
        axios.get(`/api/common/v1.0/getMenu`).then(res => {
                console.log('动态获取成功')
                // 后台返回的时一维数组
                // 用于保存最终生成的多维数组
                let roleTree = []
                // 初次遍历,返回菜单数组和父级id组成的对象
                let allObj = this.arr(res.data.data, roleTree, {})
                console.log(allObj)
                // 再次=遍历,用于解决菜单对象,最后返回一个完整版的菜单数组
                this.binaliObj(allObj)
                // 排序
                this.sortArr(allObj.roleTree)
                // vuex中存储 菜单列表 用于页面菜单的显示
                store.commit('setMenus', allObj.roleTree)
                // 路由添加
                let routerArr = this.bianli(allObj.roleTree).flat(9)
                router.options.routes[1].children.push(...routerArr);
                router.addRoutes(router.options.routes);
                console.log(router)
                // 刷新后跳转到刷新前页面 --- 这个if判断好像有点多余,因为路由里面有了重定向
                if (sessionStorage.getItem('path') == '/') {
                    sessionStorage.setItem('path', '/dashboard')
                }
                // 刷新后的路由跳转
                router.push({ path: sessionStorage.getItem('path') })

            })
            .catch(err => {
                alert("菜单错误");
            });

    }
}

//使用钩子函数对路由进行权限跳转
router.beforeEach((to, from, next) => {
    // 存储当前路由值,用于刷新跳转
    if (to.path != '/404' && to.path != "/403") {
    	// 跳转时存储路由 
        sessionStorage.setItem('path', to.fullPath)
    }
})

new Vue({
    router,
    store,
    render: h => h(App),
    created() {
        // 本地存储时存储一个值,防止初始打开登陆页面时调用
        // 主要用于页面刷新时调用
        if (sessionStorage.getItem('token')) {
            this.routerLoad.loadMenus()
        }
    }
}).$mount('#app')

-------------------------------------------------------------------------------

// Login.vue 页面
// 登陆成功后
    sessionStorage.setItem('token',123465)
	this.routerLoad.loadMenus()
	// 跳转首页
    this.$router.push('/')

-------------------------------------------------------------------------------

// store.js
import Vue from "vue"
import Vuex from 'vuex'
Vue.use(Vuex);
const store = new Vuex.Store({
  state: {
    menus: []
  },
  mutations: {
    // 菜单
    setMenus(state, menus) {
        state.menus = menus
    }
  },
  actions: {
  },
  getters: {

  }
});
export default store

/**
 *	第二中情况还有一种解决方法:
 *		1. 先通过 id == parentId 来获取最外层菜单,放到一个数组里
 *		2. 然后遍历两个数组,通过 id == parentId,匹配成功就 push 进当前的数组项
 *		3. 按照步骤二循环遍历,直至后台返回的数组里面没有数据
 */
最后菜单列表渲染页面用 this.$store.state.menus 获取菜单列表就行
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue动态菜单权限控制指的是根据用户的角色或权限动态生成菜单,并根据用户的权限来控制菜单的展示和访问。 实现Vue动态菜单权限控制的一种常用方法是通过在前端使用路由和菜单配置来实现。首先,我们需要在后端返回当前用户的角色或权限信息。然后,在前端根据这些信息生成对应的路由和菜单配置。 在Vue中,可以使用`vue-router`来管理路由,使用`v-if`或`v-show`来控制菜单的展示和隐藏。我们可以根据当前用户的权限动态生成路由和菜单配置,并在路由配置中设置相应的权限验证。 具体的实现步骤如下: 1. 获取用户的角色或权限信息。 2. 根据角色或权限信息动态生成路由配置。可以根据角色配置不同的路由,或者通过权限配置动态生成需要鉴权的路由。 3. 在Vue的路由配置中将生成的路由配置添加到路由表中。 4. 在菜单组件中根据当前用户的角色或权限信息生成菜单配置,并通过`v-if`或`v-show`控制菜单的展示和隐藏。 5. 在路由守卫中进行权限验证,判断当前用户是否有权限访问该路由。如果没有权限,则进行相应的处理,如跳转到登录页或展示无权限页面。 通过以上步骤,我们可以实现Vue动态菜单权限控制。通过根据用户的角色或权限信息生成对应的路由和菜单配置,并在路由配置和菜单组件中进行权限验证和展示控制,实现不同用户的菜单权限控制。这样可以提高系统的安全性和灵活性,使得不同用户可以根据其角色或权限访问对应的功能页面。同时也可以保护敏感操作,提升系统的稳定性和用户体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值