vue+iView 权限实践之动态显示侧边栏菜单
实现:iview vue 根据不同的登录用户角色显示不同的左侧菜单权限。
需求:实现登入不同的用户,可以访问不同的侧边栏菜单。
一,菜单栏的显示方式
组件 side-menu 传入菜单数据(menuList)渲染。
1.组件 side-menu
<side-menu
accordion
ref="sideMenu"
:active-name="$route.name"
:collapsed="collapsed"
@on-select="turnToPage"
:menu-list="menuList">
</side-menu>
2.menuList数据来源:
menuList() {
let arr;
if (this.tabMenu == 0) {
arr = this.demoMenu;
}
if (this.tabMenu == 1) {
arr = this.$store.getters.menuList;
}
return arr;
},
3.vuex中处理后的生成的菜单数据。
getters: {
menuList: (state, getters, rootState) =>
getMenuByRouter(routers, rootState.user.access),
},
二,登入时,根据用户权限(access)匹配菜单权限数组
routers,user.access 根据方法 getMenuByRouter()匹配生成新的菜单对象数组
menuList: (state, getters, rootState) =>
getMenuByRouter(routers, rootState.user.access),
- rootState是在 actions里:
const { user: { token, userId, userName } } = rootState
actions里addErrorLog获取到相关用户信息(info)存入到user:
addErrorLog ({ commit, rootState }, info)
let data = {...info,token,userId, userName}
2.routers为从后端获取到的原始对象数组,类似这种数据类型:
[
{
icon:"",
meta:{title:"用户信息",name:"user-info"},
right: undefined,size: undefined
};
{
children:[
icon: "_jinruquanping",
meta:{title: "信息报表", icon: "_jinruquanping", size: 19},
name: "info_report",
right: undefined,
size: 19,
]
icon: "_baobiaoguanli",
meta: {icon: "_baobiaoguanli", title: "报表概览", showAlways: true, size: 13, right: 8},
name: "reporting_management",
right: 8,
size: 13,
};
{
children:[
{
icon: "_jinruquanping",
meta:{title: "特殊通道", icon: "_jinruquanping", size: 19},
name: "channel_list",
right: undefined,
size: 19,
},
{
icon: "_jinruquanping",
meta:{title: "通道匹配", icon: "_jinruquanping", size: 19},
name: "channel_match",
right: undefined,
size: 19,
}
]
icon: "_celveguanli",
meta: {title: "派单发送", icon: "_celveguanli", showAlways: true, size: 14},
name: "send_management",
right: undefined,
size: 14,
};
]
三,getMenuByRouter()方法的具体实现
判断并且匹配access,生成新的侧边栏导航带单数组。
- src/libs/tool.js: 中hasOneOf方法
hasOneOf 至少有一个元素包含在目标数组中
//判断要查询的数组是否至少有一个元素包含在目标数组中
export const hasOneOf = (targetarr, arr) => {
return targetarr.some(_ => arr.indexOf(_) > -1)
}
2.src/libs/util.js中getMenuByRouter方法
2.1 hasChild方法
hasChild 子节点存在且至少有一条:
export const hasChild = (item) => {
return item.children && item.children.length !== 0
}
2.2 showThisMenuEle方法
showThisMenuEle
传入的路由数组router中access和登录时获取存入的access一致时,显示菜单(返回true)
const showThisMenuEle = (item, access) => {
if (item.meta && item.meta.access && item.meta.access.length) {
if (hasOneOf(item.meta.access, access)) return true
else return false
} else return true
}
2.3 hasChild + showThisMenuEle
至少有一条时,且有access,重复遍历处理子节点
if ((hasChild(item) || (item.meta && item.meta.showAlways)) && showThisMenuEle(item, access)) {
obj.children = getMenuByRouter(item.children, access)
}
目标跳转路径等于路由跳转路径,access都存在是,存入有权限的侧边栏菜单中。
if (item.meta && item.meta.href) obj.href = item.meta.href
if (showThisMenuEle(item, access)) res.push(obj)
四,测试
1.store/module/app.js中,手动修改传入的rootState.user.access
getters: {
//用户路由菜单权限处理(获取路由和登入用户权限作匹配生成新的侧边栏菜单)
//menuList: (state, getters, rootState) => getMenuByRouter(routers, rootState.user.access),
menuList: (state, getters, rootState) => getMenuByRouter(routers, "admin"),//测试权限菜单。
errorCount: state => state.errorList.length
},
2.在routers中
修改meta下的access用户角色数组。access: [‘adminSuper’]
children: [{
path: 'rest_list',
name: rest_list',
meta: {
title: '其他列表',
icon: '_jinruquanping',
size: 19,
access: ['adminSuper']
},
component: () =>
import ("@/view/policy_management/rest_list.vue")
},
]
3.启动项目,显示页面,不同用户左侧菜单有不同的菜单显示效果,测试有效。
五,部分组件代码
1.main.vue中的side-menu组件
import SideMenu from "./components/side-menu";
export default {
name: "Main",
components: {
SideMenu,
}
}
a.side-menu.vue
<template>
<div class="side-menu-wrapper">
<slot></slot>
<Menu ref="menu" v-show="!collapsed" :active-name="activeName" :open-names="openedNames" :accordion="accordion" :theme="theme" width="auto" @on-select="handleSelect">
<template v-for="item in menuList">
<template v-if="item.children && item.children.length === 1">
<side-menu-item v-if="showChildren(item)" :key="`menu-${item.name}`" :parent-item="item"></side-menu-item>
<menu-item v-else :name="getNameOrHref(item, true)" :key="`menu-${item.children[0].name}`"><common-icon :type="item.children[0].icon || ''"/><span>{{ showTitle(item.children[0]) }}</span></menu-item>
</template>
<template v-else>
<side-menu-item v-if="showChildren(item)" :key="`menu-${item.name}`" :parent-item="item"></side-menu-item>
<menu-item v-else :name="getNameOrHref(item)" :key="`menu-${item.name}`"><common-icon :type="item.icon || ''"/><span>{{ showTitle(item) }}</span></menu-item>
</template>
</template>
</Menu>
<div class="menu-collapsed" v-show="collapsed" :list="menuList">
<template v-for="item in menuList">
<collapsed-menu :center="true" v-if="item.children && item.children.length > 1" @on-click="handleSelect" hide-title :root-icon-size="rootIconSize" :icon-size="iconSize" :theme="theme" :parent-item="item" :key="`drop-menu-${item.name}`"></collapsed-menu>
<Tooltip transfer v-else :content="showTitle(item.children && item.children[0] ? item.children[0] : item)" placement="right" :key="`drop-menu-${item.name}`">
<a @click="handleSelect(getNameOrHref(item, true))" class="drop-menu-a" :style="{textAlign: 'center'}"><common-icon :right="0" :size="item.size" :color="textColor" :type="item.icon || (item.children && item.children[0].icon)"/></a>
</Tooltip>
</template>
</div>
</div>
</template>
<script>
import SideMenuItem from './side-menu-item.vue'
import CollapsedMenu from './collapsed-menu.vue'
import { getUnion } from '@/libs/tools'
import mixin from './mixin'
export default {
name: 'SideMenu',
mixins: [ mixin ],
components: {
SideMenuItem,
CollapsedMenu
},
props: {
menuList: {
type: Array,
default () {
return []
}
},
collapsed: {
type: Boolean
},
theme: {
type: String,
default: 'dark'
},
rootIconSize: {
type: Number,
default: 20
},
iconSize: {
type: Number,
default: 16
},
accordion: Boolean,
activeName: {
type: String,
default: ''
},
openNames: {
type: Array,
default: () => []
}
},
data () {
return {
openedNames: []
}
},
methods: {
handleSelect (name) {
this.$emit('on-select', name)
},
getOpenedNamesByActiveName (name) {
return this.$route.matched.map(item => item.name).filter(item => item !== name)
},
updateOpenName (name) {
if (name === this.$config.homeName) this.openedNames = []
else this.openedNames = this.getOpenedNamesByActiveName(name)
}
},
computed: {
textColor () {
return this.theme === 'dark' ? '#fff' : '#495060'
}
},
watch: {
activeName (name) {
if (this.accordion) this.openedNames = this.getOpenedNamesByActiveName(name)
else this.openedNames = getUnion(this.openedNames, this.getOpenedNamesByActiveName(name))
},
openNames (newNames) {
this.openedNames = newNames
},
openedNames () {
this.$nextTick(() => {
this.$refs.menu.updateOpened()
})
}
},
mounted () {
this.openedNames = getUnion(this.openedNames, this.getOpenedNamesByActiveName(name))
}
}
</script>
<style lang="less">
@import './side-menu.less';
</style>
b.side-menu.less
.side-menu-wrapper{
user-select: none;
.menu-collapsed{
padding-top: 10px;
.ivu-dropdown{
width: 100%;
.ivu-dropdown-rel a{
width: 100%;
}
}
.ivu-tooltip{
width: 100%;
.ivu-tooltip-rel{
width: 100%;
}
.ivu-tooltip-popper .ivu-tooltip-content{
.ivu-tooltip-arrow{
border-right-color: #fff;
}
.ivu-tooltip-inner{
background: #fff;
color: #495060;
}
}
}
}
a.drop-menu-a{
display: inline-block;
padding: 6px 15px;
width: 100%;
text-align: center;
color: #495060;
}
}
.menu-title{
padding-left: 6px;
margin-left: -20px;
}
2.getMenuByRouter方法
src/libs/util.js中getMenuByRouter方法
/**
* @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,
size: (item.meta && item.meta.size) || undefined,
right: (item.meta && item.meta.right) || undefined
}
if ((hasChild(item) || (item.meta && item.meta.showAlways)) && showThisMenuEle(item, access)) {
obj.children = getMenuByRouter(item.children, access)
}
if (item.meta && item.meta.href) obj.href = item.meta.href
if (showThisMenuEle(item, access)) res.push(obj)
}
})
return res
}
这样,就可以实现,登录不同的用户角色,iview侧边栏动的动态菜单展示,权限菜单就简单总结至此,欢迎关注评论,交流学习。
更多推荐
所有评论(0)