1.介绍
本文采用vue3.0+Ts+全局自定义指令实现动态按钮【操作】,应用场景为后端发送动态菜单+操作,操作信息存放在路由的meta中并将路由存在vuex中。模板文件使用全局自定义指令动态判断是否显示该按钮。
2.编写路由相关内容
2.1.先看下处理完后的路由meta中按钮的存放形式
2.2. 构建树,处理后端返回的路由
2.2.1 在auctions中调用函数获取后端路由(我这里是带有模块的,如果默认就是菜单无需考虑模块)
generateRoutes( {commit}: ActionContext<userState, userState> ) {
let modelId = store.getters.modelId;
return new Promise(resolve => {
getMenu({
modelId: modelId
}).then(response => {
if (response.status === 20000) {
let data = tree(response.data, modelId)
commit('SET_ROUTES', response.data)
resolve(data)
return
}
})
})
},
后端返回数据如下
{
"status": 20000,
"message": "获取菜单",
"data": [
{
"id": 2,
"path": "/main/systemManage",
"icon": "fa fa-bar-chart-o",
"name": "数据看板",
"pid": 1,
"type": 2,
"affix": 0,
"url": "",
"redirect": "/main/systemManage/dashbord"
},
{
"id": 3,
"path": "/main/systemManage/dashbord",
"icon": "fa fa-bar-chart-o",
"name": "数据看板",
"pid": 2,
"type": 3,
"affix": 0,
"url": "/main/systemManage/dashboard/index",
"redirect": ""
},
{
"id": 4,
"path": "/main/systemManage/auth",
"icon": "sfont system-menu",
"name": "权限管理",
"pid": 1,
"type": 2,
"affix": 0,
"url": "",
"redirect": ""
},
{
"id": 5,
"path": "users",
"icon": "",
"name": "管理员列表",
"pid": 4,
"type": 3,
"affix": 0,
"url": "/main/systemManage/users/index",
"redirect": ""
},
{
"id": 20,
"path": "",
"icon": "",
"name": "删除",
"pid": 5,
"type": 4,
"affix": 0,
"url": "delete",
"redirect": ""
},
{
"id": 6,
"path": "role",
"icon": "fa fa-life-bouy",
"name": "权限组列表",
"pid": 4,
"type": 3,
"affix": 0,
"url": "/main/systemManage/role/index",
"redirect": ""
},
{
"id": 7,
"path": "menu",
"icon": "fa fa-sitemap",
"name": "菜单管理",
"pid": 4,
"type": 3,
"affix": 0,
"url": "/main/systemManage/menu/index",
"redirect": ""
}
]
}
2.2.2 前端在vuex中处理数据,附上操作菜单和按钮的tree,这里面type【2:目录,3:菜单,4:按钮操作】
export function tree( arr: Array<any>, pid: internal ) {
const treeArr: Array<any> = []
arr.forEach(( item ) => {
if (item.pid == pid && item.type != 4) {
item.meta = {
title: item.name,
icon: item.icon,
affix: (item.affix === 1),
noCache: true,
btnPermissions: []
}
if (item.type === 2) {
item.component = Layout
if (!item.redirect) {
item.redirect = 'noRedirect'
}
} else if (item.type === 3) {
item.component = routeAllPathToCompMap[`../../views${item.url}.vue`];
item.meta.btnPermissions = getBtnPermissions(arr, item.id)
}
item.children = tree(arr, item.id)
if (item.children.length <= 0) {
item.children = []
}
treeArr.push(item)
}
})
return treeArr
}
function getBtnPermissions( arr: Array<any>, pid: internal ) {
let array: any = [];
arr.forEach(( item: any ) => {
if (item.type === 4 && item.pid === pid) {
array.push(item.url)
}
})
return array;
}
2.2.3. 在mutations中将路由存到vuex中
SET_ROUTES( state: userState, routes: Array<any> ) {
state.addRoutes = routes
},
3. 当访问每一个路由的时候需要将这个路由的meta中的按钮权限存在vuex中,用于判断当前路由的操作权限
setPermissions: ( state: userState, permissionsArr: Array<any> ) => {
state.permissions = permissionsArr
},
4.构建全局自定义指令
4.1. 在src的utils目录中(没有就创建一个)新建如下目录
4.2. 实现btnPermissions.ts
// 注册一个全局自定义指令 `v-hasPermission`
import store from "@/store";
export default function ( app: any ) {
app.directive('hasPermission', {
mounted( el: any, binding: any ) {
const {value} = binding;
const usersPermissions = store.getters.permissions;
let f = usersPermissions.some((item:any) => {
//some会循环所有元素,如果有一个元素满足则返回true并跳出循环,如果都没有则返回false
return item.indexOf(value) !== -1;
});
if (!f) {
///如果不满足则通过dom元素销毁这个按钮
el.parentNode && el.parentNode.removeChild(el);
}
}
})
}
4.3. 实现index.ts
import hasPermission from "./btnPermissions"
export default function registerDirectives( app: any ) {
hasPermission(app)
}
4.4. 全局挂载,main.js中引入刚刚的index.ts,并调用注册函数
5.模板文件中使用
<el-button v-hasPermission="['create']" type="primary" :icon="Plus" @click="handleAdd">添加
</el-button>