iview admin目前是没有给我处理好权限这块的逻辑。因此,权限这块仍是得咱们本身去撸。(脸上笑嘻嘻、内心mmp!)前端
作权限,说到底就是为了让不一样权限的用户, 能够访问不一样的功能模块。好比我是admin权限, 我就能够随心所欲了,你服不服??? 那我若是是只有某个权限,可能我只能使用某一个或者某几个功能(也就是前端页面)。页面内的具体功能也是同样的道理,举个栗子:我是admin权限,我能够对某个表格进行:增、删、改、查、上传等一系列操做。若是我不是,可能就收到权限的限制,只能进行查看和上传,其余的功能是无权限进行操做的。好了,大概的思路就是这样的,光BB谁不会啊;那具体看一下基于iview admin的权限是如何实现。vue
咱们从上面的思路具体分析一下,首先,不一样用户能够访问不一样的模块(页面),因此咱们要作到不一样身份的用户进入系统的时候,就会根据这个用户的身份显示不一样的页面,显示不一样的菜单。咱们的项目正常是路由(router.js)是在本地配置好的一个路由文件,因此,要想实现动态的路由,咱们就必须让router.js实现动态生成。我选用的方案是vue-router 2.2版本新增了一个router.addRoutes(routes)方法去实现。(可能还有一些“巨佬”是用的其余方案,'佩服三连')。那用了addRouter方法以后呢,实际上咱们本地的router.js是只须要一些基本的路由了,其余均可以删掉了。感受有点啰嗦了,我先贴代码压压惊吧。node
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文件的方法:vue-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的 差很少的。---- 上面的函数里,我在本地存了一下路由的文件,后面会用到。vuex
成功拿到路由文件以后,咱们就能够再main.js里让addRouter兄dei登场了。代码:npm
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是一个函数,因此须要特殊处理。json
/** * @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)
页面中具体按钮的使用:
<Icon type="md-cloud-download" v-hasPermission="'output'" @click="exportTaskExcel" />
注意:绑定的字段必须是字符串格式。具体页面上的按钮权限的分配在前端页面是怎么控制的,彻底能够去xBoot里借鉴。我也不知道我写的你们看不看得懂,若是看不懂,再多看一遍,再看不懂欢迎留言或者加我QQ互相学习:602353272。最后,再BB一句,有巨佬有更好的方案欢迎赐教!