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 获取菜单列表就行

你可能感兴趣的:(笔记)