资讯专栏INFORMATION COLUMN

iview admin动态路由、权限控制个人见解

LeanCloud / 3337人阅读

摘要:目前是没有给我处理好权限这块的逻辑。我们的项目正常是路由是在本地配置好的一个路由文件,所以,要想实现动态的路由,我们就必须让实现动态生成。具体页面上的按钮权限的分配在前端页面是怎么控制的,完全可以去里借鉴。

iview admin目前是没有给我处理好权限这块的逻辑。所以,权限这块还是得我们自己去撸。(脸上笑嘻嘻、心里mmp!)

思路

做权限,说到底就是为了让不同权限的用户, 可以访问不同的功能模块。比如我是admin权限, 我就可以为所欲为了,你服不服??? 那我如果是只有某个权限,可能我只能使用某一个或者某几个功能(也就是前端页面)。页面内的具体功能也是一样的道理,举个栗子:我是admin权限,我可以对某个表格进行:增、删、改、查、上传等一系列操作。如果我不是,可能就收到权限的限制,只能进行查看和上传,其他的功能是无权限进行操作的。好了,大概的思路就是这样的,光BB谁不会啊;那具体看一下基于iview admin的权限是如何实现。

具体实现逻辑

我们从上面的思路具体分析一下,首先,不同用户可以访问不同的模块(页面),所以我们要做到不同身份的用户进入系统的时候,就会根据这个用户的身份显示不同的页面,显示不同的菜单。我们的项目正常是路由(router.js)是在本地配置好的一个路由文件,所以,要想实现动态的路由,我们就必须让router.js实现动态生成。我选用的方案是vue-router 2.2版本新增了一个router.addRoutes(routes)方法去实现。(可能还有一些“巨佬”是用的其他方案,"佩服三连")。那用了addRouter方法之后呢,实际上我们本地的router.js是只需要一些基本的路由了,其他都可以删掉了。感觉有点啰嗦了,我先贴代码压压惊吧。

import Main from "@/components/main"
/**
 * iview-admin中meta除了原生参数外可配置的参数:
 * meta: {
 *  title: { String|Number|Function }
 *         显示在侧边栏、面包屑和标签栏的文字
 *         使用"{{ 多语言字段 }}"形式结合多语言使用,例子看多语言的路由配置;
 *         可以传入一个回调函数,参数是当前路由对象,例子看动态路由和带参路由
 *  hideInBread: (false) 设为true后此级路由将不会出现在面包屑中,示例看QQ群路由配置
 *  hideInMenu: (false) 设为true后在左侧菜单不会显示该页面选项
 *  notCache: (false) 设为true后页面在切换标签后不会缓存,如果需要缓存,无需设置这个字段,而且需要设置页面组件name属性和路由配置的name一致
 *  access: (null) 可访问该页面的权限数组,当前路由设置的权限会影响子路由
 *  icon: (-) 该页面在左侧菜单、面包屑和标签导航处显示的图标,如果是自定义图标,需要在图标名称前加下划线"_"
 *  beforeCloseName: (-) 设置该字段,则在关闭当前tab页时会去"@/router/before-close.js"里寻找该字段名对应的方法,作为关闭前的钩子函数
 * }
 */

var Router = [{
        path: "/",
        name: "_home",
        redirect: "/home",
        component: Main,
        meta: {
            icon: "md-home",
            title: "首页",
            hideInMenu: true
        },
        children: [{
            path: "/home",
            name: "home",
            meta: {
                icon: "md-home",
                title: "首页"
            },
            component: () => import("@/view/home/home.vue")
        }]
    },
    {
        path: "/401",
        name: "error_401",
        meta: {
            hideInMenu: true
        },
        component: () => import("@/view/error-page/401.vue")
    },
    {
        path: "/500",
        name: "error_500",
        meta: {
            hideInMenu: true
        },
        component: () => import("@/view/error-page/500.vue")
    },
    {
        path: "*",
        name: "error_404",
        meta: {
            hideInMenu: true
        },
        component: () => import("@/view/error-page/404.vue")
    }
]

export default Router

呐~ 我本地路由文件只留下了这些基本的路由,!!!那我其他的路由怎么办??--- 不慌,具体的业务路由代码先不用管,交给addRouter这位兄dei和你们后端的兄dei就好了。你要做的就是拿到后端给你返回的list,然后用addRouter方法添加到router里就好了。
接下来,你把你之前的路由文件原封不动的ctrl+c给后端的兄dei,就是你删除掉的那些路由,这里说明一下:(list的格式必须和router.js里的格式一致,可以和后端兄弟商量一下了,让他帮你把格式造好直接返给你。)
接下来进入关键的一步了,终于又可以贴代码了!!!
在vuex的app.js我定义了一个获取router文件的方法:

    getUserRouters({
            commit
        }) {
            const code = getParams(window.location.href).code
            return new Promise((resolve, reject) => {
                try {
                    callBack(code).then(res => {
                        let routers = backendMenusToRouters(res.data.resultData.route)
                        commit("setRouters", routers)
                        setToken(res.data.resultData.token)
                        localSave("dataMenuList",JSON.stringify(res.data.resultData.route))
                        resolve(res.data.resultData.route)
                    }).catch(err => {
                        reject(err)
                    })
                } catch (error) {
                    reject(error)
                }
            })
        }

ok,我为什么没有用iview admin的登陆逻辑呢,是因为我们这边的登陆会走一个平台的验证,公司统一的,其实和iview admin的 差不多的。---- 上面的函数里,我在本地存了一下路由的文件,后面会用到。

成功拿到路由文件之后,我们就可以再main.js里让addRouter兄dei登场了。代码:

    const token = getToken()
    const queryCode = getParams(window.location.href).code
    if (!token && !queryCode) {
        //调登陆逻辑
    } else if (!token && queryCode) {
       //调用app.js里的getUserRouters方法
        store.dispatch("getUserRouters").then(res => {
            new Vue({
                el: "#app",
                router,
                i18n,
                store,
                render: h => h(App),
                mounted() {
                    const routers = backendMenusToRouters(res)
                    router.addRoutes(routers)
                },
            })
        }) 
    } else {
        //如果是登陆过的,后者是正常刷新操作,只要从localStorage里拿数据就好了
        router.addRoutes(backendMenusToRouters(JSON.parse(localRead("dataMenuList"))))
        new Vue({
            el: "#app",
            router,
            i18n,
            store,
            render: h => h(App)
        })
    }

上面的backendMenusToRouters函数我贴出来,这个函数就是为了处理路由文件的,路由挂载component是一个函数,所以需要特殊处理。

    /**
 * @description 将后端菜单树转换为路由树
 * @param {Array} menus
 * @returns {Array}
 */
export const backendMenusToRouters = (menus) => {
    let routers = []
    forEach(menus, (menu) => {
        // 将后端数据转换成路由数据
        let route = backendMenuToRoute(menu)
        // 如果后端数据有下级,则递归处理下级
        if (menu.children && menu.children.length !== 0) {
            route.children = backendMenusToRouters(menu.children)
        }
        routers.push(route)
    })
    return routers
}

/**
 * @description 将后端菜单转换为路由
 * @param {Object} menu
 * @returns {Object}
 */
const backendMenuToRoute = (menu) => {
    // 具体内容根据自己的数据结构来定,这里需要注意的一点是
    // 原先routers写法是component: () => import("@/view/error-page/404.vue")
    // 经过json数据转换,这里会丢失,所以需要按照上面提过的做转换,下面只写了核心点,其他自行处理
    let route = Object.assign({}, menu)
    route.component = resolve => require([`@/${menu.component}`], resolve)
    return route
}

关于处理component需要配置个依赖:
npm install babel-plugin-syntax-dynamic-import

.babelrc 增加
{
"plugins": ["syntax-dynamic-import"]
}

这里加一句,vuex里app.js的menuList方法需做稍稍的小改动:

    
     menuList: (state, getters, rootState) => getMenuByRouter(state.routers, rootState.user.access)

main.vue的menuList赋值:

    menuList( ) {
            return this.$store.getters.menuList
        },

到这里,你的动态路由可以说已经完成了。

那又有同学问了,那还有一些页面是子页面, 不需要在菜单里显示怎么处理???别慌,我喝口水慢慢跟你说。
我们可以看一下路由的配置,在meta的对象里,有个hideInMenu属性,妥了,那我们搞起来吧。其实iview admin已经帮我写好了这块的逻辑处理,代码在util.js里:

    /**
 * @param {Array} list 通过路由列表得到菜单列表
 * @returns {Array}
 */
export const getMenuByRouter = (list, access) => {
    let res = []
    forEach(list, item => {
        if (!item.meta || (item.meta && !item.meta.hideInMenu)) {
            let obj = {
                icon: (item.meta && item.meta.icon) || "",
                name: item.name,
                meta: item.meta
            }
            if ((hasChild(item) || (item.meta && item.meta.showAlways))) {
                obj.children = getMenuByRouter(item.children, access)
            }
            if (item.meta && item.meta.href) obj.href = item.meta.href
            // if (showThisMenuEle(item, access)) res.push(obj)
            res.push(obj)
        }
    })
    return res
}

-,- ok,大功告成! 慢着... 那如果我想对菜单进行增删改操作怎么办??
好办!你就前端写页面吧,这里我推荐一个很成熟的方案,我目前项目就是参考他的做的。传送门:xBoot管理系统

个人认为他们的菜单管理做的真的很棒!!我又不要脸的借鉴了他们的功能权限,进行了我们项目的功能权限管理的设计。

具体实现逻辑

首先,每个角色的具体功能权限是在meta的access里携带进来的,access是一个数组,["del","add"....]我和后端定义好了每个字段代表什么功能权限。比如:"del"代表删除按钮、"upload"代表上传功能等等... 当我们进入不同的页面的时候,根据access的功能列表给用户设置不同的功能权限。这里是借鉴了网上的实现逻辑,自定义一个指令,然后每个按钮根据功能绑定不同的字段,做是否remove动作。好了,又开始bb了,贴代码吧,在libs定义一个hasPermission.js文件:

    const hasPermission = {
    install (Vue, options) {
        Vue.directive("hasPermission", {
            inserted (el, binding, vnode) {
                let permissionList = vnode.context.$route.meta.access;
                if (!permissionList.includes(binding.value)) {
                    el.remove()
                }
            }
        });
    }
};
export default hasPermission;

在main.js里全局定义:

    import hasPermission from "@/libs/hasPermission.js"
    Vue.use(hasPermission)

页面中具体按钮的使用:

    

注意:绑定的字段必须是字符串格式。
具体页面上的按钮权限的分配在前端页面是怎么控制的,完全可以去xBoot里借鉴。
我也不知道我写的大家看不看得懂,如果看不懂,再多看一遍,再看不懂欢迎留言或者加我QQ互相学习:602353272。
最后,再BB一句,有巨佬有更好的方案欢迎赐教!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/105191.html

相关文章

  • 关于前后端分离权限控制,元素细粒度(iview-admin实现)

    摘要:按钮方面按钮通过自定义指令绑定其特定的操作接口信息如产品上传按钮,需要拥有产品上传的信息,才可以继续执行按钮的业务逻辑。 开篇啰嗦几句 在传统单体项目中,通常会有一些框架用来管理熟知的权限。如耳濡目染的 Shiro 或者 Spring Security 。然而,到了现在这个时代,新开始的项目会更多的才用后端微服务 + 前端 mvvm 的架构开始书写项目。权限控制方面将变得有些许晦涩。当...

    YorkChen 评论0 收藏0
  • vue轻量级后台管理系统基础模板

    摘要:具体实现请查看和的退出登陆回调方法。现在除了必要的页面需要在一开始添加到路由表里,其他的页面都可以根据后台数据来自动生成。另外,如果在未登陆时要访问某一指定页面,会重定向到登陆页,登陆成功后会自动跳到这个指定页面。 项目地址 vue-admin-template 在线预览 更新 2019.6.25 更新 修复路由表冲突问题 退出当前用户,换账号重新登陆时,上个账号和现在的账号路由表会有...

    2shou 评论0 收藏0
  • iview-admin 关于权限控制

    摘要:权限控制也就是用户只能访问被分配的资源。在前端展示上也笼统的分为菜单权限控制和页面中按钮权限控制。菜单是路由的直接体现,菜单控制实际上就是路由控制。系统中解决权限方案参照 权限控制也就是用户只能访问被分配的资源。在前端展示上也笼统的分为菜单权限控制和页面中按钮权限控制。 1.菜单是路由的直接体现,菜单控制实际上就是路由控制。在路由的before 钩子函数中校验当前访问的地址是否有权访问...

    spademan 评论0 收藏0
  • Vue全栈开发之百度贴吧

    这个百度贴吧的项目是 vue + koa + sequelize 的项目。 由于没有百度贴吧API接口,所以自己写了后端 项目部分截图(GIF) showImg(https://user-gold-cdn.xitu.io/2019/7/13/16bea513a0805b84?w=480&h=1040&f=gif&s=4456077);showImg(https://user-gold-cdn.xi...

    stefanieliang 评论0 收藏0

发表评论

0条评论

LeanCloud

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<