在element-ui menu组件基础上,封装一个动态侧边菜单栏组件

本文介绍了如何在Vue.js应用中实现基于路由权限的侧边栏菜单动态渲染。组件包括index.vue和SideItem.vue,菜单数据从store的permission_routes获取,根据用户角色过滤。SideItem组件递归生成菜单项,支持多级子菜单。当alwaysShow为true时,即使只有一个子菜单也会显示根菜单。文章还展示了不同设置下菜单的展示效果。
摘要由CSDN通过智能技术生成

组件由两个文件构成,都放在了Sidebar目录下,分别是index.vue和SideItem.vue,使用时直接引入index.vue即可。

菜单栏数据从路由表获取,示例中通过mapGetters从store中获取permission_routes。

在不考虑权限时,这些数据可以直接从router/index.js文件引入,permission_routes是根据登录用户权限筛选后的路由。示例中路由表的数据格式如下:

/**
  * hidden: true                   如果设置为true,选项将不会显示在侧边栏中(默认值为false)
  * alwaysShow: true               如果设置为true,将始终显示根菜单
  * meta : {
      roles: ['admin','editor']    控制页面路由时使用
      title: 'title'               侧边栏和面包屑中显示的名称(推荐设置)
      icon: 'el-icon-x' 图标显示在侧边栏中
    }
 */
[

  {
    path: '/admin/a',
    component: Admin,
    meta: { title: '1', icon: 'el-icon-menu' , roles: ['admin']},
    // alwaysShow: true,
    children: [
      { path: 'aa', 
        component: () => import('@/views/admin/Menu1'), 
        meta: { title: '11', icon: 'el-icon-menu', roles: ['admin']},
        // alwaysShow: true,
        children: [
          { path: 'aaa', 
            component: () => import('@/views/admin/Menu1/Menu2'), 
            meta: { title: '111', icon: 'el-icon-menu' , roles: ['admin']}, 
            // alwaysShow: true,
            children: [
              { path: 'aaaa', 
                component: () => import('@/views/admin/Menu1/Menu2/Menu3'), 
                meta: { title: '1111', icon: 'el-icon-menu' , roles: ['admin']}
              }
            ]
          }
        ]
      }
    ]
  },

  {
    path: '/admin/b',
    component: Admin,
    meta: { title: '2', icon: 'el-icon-menu' , roles: ['admin']},
    children: [
      { path: 'ba', 
        component: () => import('@/views/admin/menu2/Menu21'), 
        meta: { title: '21', icon: 'el-icon-menu', roles: ['admin']}
      },
      { path: 'bb', 
        component: () => import('@/views/admin/menu2/Menu22'), 
        meta: { title: '22', icon: 'el-icon-menu', roles: ['admin']}
      }
    ]
  }
]

菜单栏组件逻辑:菜单栏由el-menu整体包裹,可展开的菜单项由el-submenu包裹,最终不可再展开的菜单项是el-menu-item。无论是el-submenu还是el-menu-item,都通过SideItem组件生成。如果有子菜单(children),SideItem组件会通过调用组件自身的方式递归生成(类似函数递归),由于是自调用,组件名一定要写。

fragment是一个vue编码但不渲染的标签(vue-fragment),可用div替代它。

resolvePath()方法结合组件传参(basePath),将路由表中当前路由的path与其上级路由path拼接,组成当前菜单的完整路径。

最终代码:

...Sidebar/index.vue

<template>
  <el-menu background-color="#49586D" text-color="#EFF3F6" active-text-color="#2CA9E1" :unique-opened="false" 
    :collapse-transition="false" :default-active="activePath" router mode="vertical">
    <!-- SideItem组件 -->
    <side-item v-for="route in permission_routes" :route="route" :basePath="route.path" />
  </el-menu>
</template>

<script>
import {mapGetters} from 'vuex'
import SideItem from './SideItem.vue'

export default {
  name: 'SideBar',
  components: { SideItem },
  data() {
    return {
      activePath: ''
    }
  },
  created() {
    this.activePath = this.$route.path
  },
  computed: {
    // 获取所有路由
    ...mapGetters([
      'permission_routes'
    ]),
    
  }
};
</script>

<style lang="less" scoped>
.el-menu {
  border-right: none;
}
</style>

...Sidebar/SideItem.vue

<template>
  <fragment v-if="!route.hidden">
    <!-- 没有子菜单 -->
    <el-menu-item v-if="!route.children" :index="basePath">
      <i :class="route.meta.icon"></i>
      <span slot="title">{{route.meta.title}}</span>
    </el-menu-item>

    <!-- 有一个子菜单, 可不显示根菜单 -->
    <fragment v-else-if="(route.children.length == 1) && !route.alwaysShow">
      <!-- SideItem组件递归 -->
      <side-item
        v-for="child in route.children"
        :route="child"
        :basePath="resolvePath(child.path)"
      />
    </fragment>

    <!-- 有子菜单, 且显示根菜单 -->
    <el-submenu v-else :index="basePath">
      <template slot="title">
        <i :class="route.meta.icon"></i>
        <span slot="title">{{route.meta.title}}</span>
      </template>

      <!-- SideItem组件递归 -->
      <side-item
        v-for="child in route.children"
        :route="child"
        :basePath="resolvePath(child.path)"
      />
    </el-submenu>

  </fragment>
</template>

<script>
import path from 'path'

export default {
  name: 'SideItem',
  props: {
    route: {
      type: Object,
      required: true
    },
    basePath: {
      type: String,
      default: ''
    }
  },
  methods: {
    // 路径拼接
    resolvePath(routePath) {
      return path.resolve(this.basePath, routePath)
    }
  },
  
};
</script>

<style lang="less" scoped>

</style>

菜单栏效果如下:

把   // alwaysShow: true  注释放开后的效果: 

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值