后台管理系统多层嵌套侧边栏(自用)

508 阅读3分钟

前期准备

需要的组件:

image.png

layout/index.vue:路由组件,用来整个页面,包括侧边栏、头部、主要内容区域,是根路由所映射的组件。

layout/main-layout.vue:路由组件,用来渲染主要内容区域内的内容,是每个菜单目录的路由所映射的组件,是该目录下路由映射组件的一个容器。

layout/index.vue是根路由所映射的组件,这个组件中的主要内容区域main中放一个路由视图占位符,这样当访问所有根路由的子路由时,都会在index

注意:侧边栏菜单的所有菜单项都是根路由的children,<router-view>路由视图占位符,渲染映射组件路由的亲儿子children里面的路由规则中所映射的组件。

为啥要main-layout.vue这个组件,因为路由视图占位符<router-view>路由视图占位符只负责渲染当前组件路由亲儿子children下的路由所映射的组件。但我们有多层嵌套,所以需要一个公共的组件来渲染多层嵌套的路由所映射的组件。为啥不用layout/index.vue,因为它里面渲染的是整个页面的结构,包括侧边栏、头部、主要内容区域,用layout/index.vue的话页面会项俄罗斯套娃。

image.png

App.vue中的<router-view>用来渲染根路由所映射的组件。 image.png

layout/index.vue中的<router-view>用来渲染根路由的亲儿子级别的路由映射的组件。

image.png

main-layout.vue中的<router-view>用来渲染各模块下面的children的路由映射的组件

image.png

路由列表结构

三级菜单

侧边栏.gif

路由部分,meeting.js

/** 引入公共组件 */
import { MainLayout } from '../constant'

/**
 * @description 会议室路由
 */
export default {
  path: '/meeting-manage' /** 路径 */,
  name: 'meeting-manage' /** 名称 */,
  component: MainLayout /** 组件 */,
  meta: {
    /** 路由元信息 */
    title: '会议室管理' /** 菜单的名称 */,
    icon: 'el-icon-pie-chart' /** 菜单的图标 */,
    role: 500 /** 页面权限 */,
    rank: 4 /** 排序,数值越大越靠后(下) */
  },
  children: [
    // 多层嵌套菜单
    {
      path: 'nest',
      name: 'nest',
      component: MainLayout,
      meta: {
        title: '第二层'
      },
      children: [
        {
          path: 'nest3-1',
          name: 'nest3-1',
          component: () => import(/* webpackChunkName: "meeting-manage" */ '@/views/meeting-room/nest.vue'),
          meta: {
            title: '第三层-1'
          }
        },
        {
          path: 'nest3-2',
          name: 'nest3-2',
          component: () => import(/* webpackChunkName: "meeting-manage" */ '@/views/meeting-room/fore.vue'),
          meta: {
            title: '第三层-2'
          }
        }
      ]
    }
  ]
}

四级菜单路由结构

/** 引入公共组件 */
import { MainLayout } from '../constant'

/**
 * @description 会议室路由
 */
export default {
  path: '/meeting-manage' /** 路径 */,
  name: 'meeting-manage' /** 名称 */,
  component: MainLayout /** 组件 */,
  meta: {
    /** 路由元信息 */
    title: '会议室管理' /** 菜单的名称 */,
    icon: 'el-icon-pie-chart' /** 菜单的图标 */,
    role: 500 /** 页面权限 */,
    rank: 4 /** 排序,数值越大越靠后(下) */
  },
  children: [
    // 多层嵌套菜单
    {
      path: 'nest',
      name: 'nest',
      component: MainLayout,
      meta: {
        title: '第二层'
      },
      children: [
        {
          path: 'nest3-1',
          name: 'nest3-1',
          component: () => import(/* webpackChunkName: "meeting-manage" */ '@/views/meeting-room/nest.vue'),
          meta: {
            title: '第三层-1'
          },
          children: [
            {
              path: 'nest4',
              name: 'nest4',
              component: () => import(/* webpackChunkName: "meeting-manage" */ '@/views/meeting-room/fore.vue'),
              meta: {
                title: '第四层菜单'
              }
            }
          ]
        }
      ]
    }
  ]
}

侧边栏.gif

侧边栏组件结构

侧边栏内菜单项组件部分 sidebar-item.vue

在这个组件中使用到了组件递归,这个组件的结构是:一级菜单和下拉菜单,下拉菜单里面又会有一级菜单和下拉菜单以此类推,那么下拉菜单的结构跟这个组件的结构完全一模一样,所以才下拉菜单中使用组件自己,进行组件递归。

<template>
  <div>
    <!-- 仅一级菜单 -->
    <el-menu-item v-if="!(item.children && item.children.length !== 0)"
                  :index="item.path">
      <i :class="item.meta.icon"></i>
      <span slot="title">{{ item.meta.title }}</span>
    </el-menu-item>
    <!-- 多级菜单 -->
    <el-submenu v-else
                :index="item.path">
      <template>
        <template slot="title">
          <i :class="item.meta.icon"></i>
          <span slot="title">{{ item.meta.title }}</span>
        </template>
      </template>
      <!-- 组件自己使用自己,递归组件 -->
      <sidebar-item v-for="child in item.children"
                    :key="child.path"
                    :item="child" />
    </el-submenu>
  </div>
</template>

<script>
export default {
  name: 'SidebarItem',
  props: {
    // 菜单项
    item: {
      type: Object,
      required: true
    }
  }
}
</script>

<style lang="scss" scoped></style>

侧边栏组件部分 sidebar/index.vue

<template>
  <div class="sidebar">
    <!-- logo -->
    <Logo />
    <!-- 侧边栏菜单 -->
    <el-menu :default-active="$route.name"
             background-color="#001529"
             text-color="#9fa3a8"
             active-text-color="#fff"
             class="el-menu-vertical-demo"
             @open="handleOpen"
             @close="handleClose"
             @select="handleSelect"
             :collapse="isCollapse">
      <sidebar-item v-for="item in userRoutes[0].children"
                    :key="item.name"
                    :item="item" />
    </el-menu>
  </div>
</template>