乐趣区

关于vue.js:Vue-权限菜单及按钮权限

一. 服务端数据

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);
}
}
}
退出移动版