vue+iView 权限实践之动态显示侧边栏菜单

43 篇文章 1 订阅
14 篇文章 0 订阅
需求:实现登入不同的用户,可以访问不同的侧边栏菜单。
一,菜单栏的显示方式

组件 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),
  1. 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,生成新的侧边栏导航带单数组。

  1. 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侧边栏动的动态菜单展示,权限菜单就简单总结至此,欢迎关注评论,交流学习。

在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值