一.服务端数据
Vue 权限菜单须要依据后端返回的数据来实现
[{pid:-1,name:'购物车',id:1,auth:'cart'},{pid:1,name:'购物车列表',id:4,auth:'cart-list'},{pid:4,name:'彩票',id:5,auth:'lottery'},{pid:4,name:'商品',id:6,auth:'product'},{pid:-1,name:'商店',id:2,auth:'shop'},{pid:-1,name:'集体核心',id:3,auth:'store'},];
通过 express 返回权限列表
const express = require('express');const app = express();app.all('_', (req, res, next) => {res.header('Access-Control-Allow-Origin', '_');// Access-Control-Allow-Headers ,可依据浏览器的 F12 查看,把对应的粘贴在这里就行res.header('Access-Control-Allow-Headers', 'Content-Type');res.header('Access-Control-Allow-Methods', '\*');res.header('Content-Type', 'application/json;charset=utf-8');next();});app.get('/roleAuth', (req, res) => {res.json({menuList: [{pid:-1,name:'购物车',id:1,auth:'cart'},{pid:1,name:'购物车列表',id:4,auth:'cart-list'},{pid:4,name:'彩票',id:5,auth:'lottery'},{pid:4,name:'商品',id:6,auth:'product'},{pid:-1,name:'商店',id:2,auth:'shop'},{pid:-1,name:'集体核心',id:3,auth:'store'},]});});app.listen(3000);
二.动态菜单
应用 element-ui 构建动态菜单
import ElementUI from 'element-ui';import 'element-ui/lib/theme-chalk/index.css';Vue.use(ElementUI);<el-menu default-active="2" class="el-menu-vertical-demo"><el-submenu index="1"><template slot="title">导航一</template><el-submenu index="1-1"><template slot="title">选项 1-1</template><el-menu-item index="1-1-1">选项 1-1-1</el-menu-item><el-menu-item index="1-1-2">选项 1-1-2</el-menu-item></el-submenu><el-menu-item index="1-2">选项 1-2</el-menu-item></el-submenu><el-menu-item index="2">导航二</el-menu-item><el-menu-item index="3">导航三</el-menu-item><el-menu-item index="4">导航四</el-menu-item></el-menu>路由配置import Vue from 'vue'import Router from 'vue-router'import Home from './views/Home.vue'Vue.use(Router)export const authRoutes = [ // 权限路由{path: '/cart',name: 'cart',component: () => import('@/views/Cart'),children: [{path: 'cart-list',name: 'cart-list',component: () => import('@/views/CartList'),children: [{path: 'lottery',name: 'lottery',component: () => import('@/views/Lottery'),},{path: 'product',name: 'product',component: () => import('@/views/Product'),},],},],},];export default new Router({ // 默认导出 首页和 404 页面mode: 'history',base: process.env.BASE_URL,routes: [{path: '/',name: 'home',component: Home},{path:'*',component:{render:h=>h('h1',{},'Not Found')}}]})
三.获取权限
依据后端返回的数据,格式化树结构,并提取用户权限
// 默认设置没有获取过权限export default new Vuex.Store({state: {hasPermission:false},mutations: {setPermission(state){state.hasPermission = true}},})
在路由跳转前看下是否获取过权限,如果没有获取过,就获取权限存入 vuex 中
router.beforeEach(async (to,from,next)=>{if(!store.state.hasPermission){// 获取最新路由列表let newRoutes = await store.dispatch('getRouteList');router.addRoutes(newRoutes); // 减少新路由next({...to,replace:true})}else{next(); // 获取过就不须要再次获取了}})
四.获取相干须要数据
const getMenListAndAuth = (menuList)=>{let menu = [];let sourceMap = {};let auth = [];menuList.forEach(m => {m.children = []; // 减少孩子列表sourceMap[m.id] = m;auth.push(m.auth)if(m.pid === -1){menu.push(m); // 根节点}else{sourceMap[m.pid] && sourceMap[m.pid].children.push(m)}});return {menu,auth} // 获取菜单数据和权限数据}async getRouteList({dispatch,commit}){let auths = await axios.get('http://localhost:3000/roleAuth');let menuList = auths.data.menuList;let {menu,auth} = getMenListAndAuth(menuList);}
五.找到须要增加的路由
import {authRoutes} from './router'const getRoutes = auth => {const filter = (authRoutes)=>{return authRoutes.filter(route=>{// 蕴含权限if(auth.includes(route.name)){if(route.children){route.children = filter(route.children);}return true;}})}return filter(authRoutes);};
// 获取须要增加的路由列表async getRouteList({ dispatch, commit }) {let auths = await axios.get("http://localhost:3000/roleAuth");let menuList = auths.data.menuList;let { menu, auth } = getMenListAndAuth(menuList);commit("setMenu", menu); // 将菜单数据保存起来commit("setPermission"); // 权限获取结束// 通过 auth 查找须要增加的路由return getRoutes(auth);}
六.递归渲染菜单
渲染 Menu 组件提取公共局部
<template> <div> <el-menu> <template v-for="menu in $store.state.menu"> <el-submenu v-if="menu.children.length" :key="menu.auth" :index="menu.auth"> <template slot="title">{{menu.name}}</template> <!-- 此处须要不停的递归 el-submenu --> </el-submenu> <el-menu-item v-else :key="menu.auth" :index="menu.auth">{{menu.name}}</el-menu-item> </template> </el-menu> </div></template>
编写递归组件
<template> <el-submenu :index="menu.auth"> <template slot="title">{{menu.name}}</template> <template v-for="(child,index) in menu.children"> <el-menu-item v-if="!child.children.length" :key="index"> <router-link :to="{name:child.auth}"> {{child.name}}</router-link> </el-menu-item> <!-- 如果有儿子持续递归组件 --> <ResubMenu :menu="child" v-else :key="index"></ResubMenu> </template> </el-submenu></template><script>export default { name:'ResubMenu', props:{ menu:{} }}</script>
七.权限按钮管制
state: { hasPermission: false, menu: [], // 菜单权限 btnPermission:{ // 按钮权限 edit:false, add:true }},
查看以后按钮是否有权限
<el-button v-has="'edit'">编辑</el-button><el-button v-has="'add'">增加</el-button>
自定义指令的应用
directives: {has: {inserted(el, bindings, vnode) {let value = bindings.value;// 在 vuex 中查看是否有按钮权限let flag = vnode.context.\$store.state.btnPermission[value];// 如果没有全选则将按钮删除即可!flag && el.parentNode.removeChild(el);}}}