Bootstrap

vue3项目实战(五)---左侧导航菜单栏、菜单组件封装

一 、菜单组件封装

1.1 编写静态路由

const menuItems = reactive([
  {
    name: 'index',
    path:'/index',
    meta: {
      title: '首页',
      hidden: false,
      icon: 'HomeFilled',
    },

  },
  {
    name: 'Acl',
    path:'/acl',
    meta: {
      title: '权限管理',
      icon: 'Lock',
    },
    redirect: '/acl/user',
    children:[
      {
        name: 'User',
        path: '/acl/user',
        component: AclUser,
        meta: {
          title: '用户管理',
          icon: 'User',
        },
      },
      {
        path: '/acl/role',
        component: Role,
        name: 'Role',
        meta: {
          title: '角色管理',
          icon: 'UserFilled'
        },
      },
      {
        path: '/acl/permission',
        component: Permission,
        name: 'Permission',
        meta: {
          title: '菜单管理',
          icon: 'Monitor',
        }
      }
      ]
  },

  {
    name: 'system',
    path:'/system',
    meta: {
      title: '系统管理',
      icon: 'Setting',
    },
    children:[
      {
        path: '/system/dict',
        component: Dict,
        name: 'Permission',
        meta: {
          title: '字典管理',
          icon: 'Monitor',
          hidden: false,
        }
      }
    ]

  },
])

1.2 编写对应的子页面

1.2.1 权限管理
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
1.2.2 系统管理
在这里插入图片描述
在这里插入图片描述
二、route下index.ts调整

import {
  createRouter,
  createWebHashHistory,
  type RouteRecordRaw
} from 'vue-router'

// 引入 login.ts
import LoginRouter from '@/router/modules/login.ts'

// 引入 组件页面
import Home from '@/views/layout/Index.vue'
import Index from '@/views/layout/index/Index.vue';
import System from '@/views/system/System.vue'
import DictList from "@/views/system/dict/Dict.vue";
import Acl from "@/views/acl/Acl.vue";
import AclUser from "@/views/acl/user/AclUser.vue";
import Role from "@/views/acl/role/Role.vue";
import Permission from "@/views/acl/permission/Permission.vue";







// RouteRecordRaw 内置的接口类型
const routes: RouteRecordRaw[] = [
  ...LoginRouter,
  {
    path: '/',
    redirect: '/home'
  },

  {
    path: '/home', // 布局页
    name: 'Home',
    component: Home,
    redirect:'/index',// 进入主页的时候重定向到Index页面
    children: [
      {
        path: '/index',
        name: 'index',
        component: Index,
      },
      {
        path: '/acl',
        component: Acl,
        name: 'Acl',
        children: [
          {
            path: '/acl/user',
            component: AclUser,
            name: 'User',
          },
          {
            path: '/acl/role',
            component: Role,
            name: 'Role',
          },
          {
            path: '/acl/permission',
            component:Permission,
            name: 'Permission',
          },
        ]
      },
      {
        path: '/system',
        name: 'system',
        component: System,
        children:[
          {
            path: '/system/dict',
            name: 'dict',
            component: DictList,
          }
        ]
      }


    ],

  }

]

const router = createRouter({
  history: createWebHashHistory(),
  routes
})

// 导出路由
export default router

三 、MenuTree组件封装

3.1 components新建MenuTree文件夹,文件夹 新建MenuTree.vue

<template>
  <div>
    <template v-for="(item, index) in menuList" :key="item.path">
      <!-- 没有子路由 -->
      <template v-if="!item.children">
        <el-menu-item
          :index="item.path"
          v-if="!item.meta.hidden"
          @click="goRoute"
        >
          <template #title>
            <!-- 图标 -->
            <el-icon>
              <component :is="item.meta.icon"></component>
            </el-icon>
            <span>{{ item.meta.title }}</span>
          </template>
        </el-menu-item>
      </template>

      <!-- 只有一个子集 -->
      <template v-if="item.children && item.children.length == 1">
        <el-menu-item
          @click="goRoute"
          :index="item.children[0].path"
          v-if="!item.children[0].meta.hidden"
        >
          <el-icon>
            <component :is="item.children[0].meta.icon"></component>
          </el-icon>
          <template #title>
            <span>{{ item.children[0].meta.title }}</span>
          </template>
        </el-menu-item>
      </template>

      <!-- 有子路由,  有子菜单的继续遍历(递归) -->
      <el-sub-menu
        :index="item.path"
        v-if="item.children && item.children.length >= 1"
      >
        <template #title>
          <!-- 图标 -->
          <el-icon>
            <component :is="item.meta.icon"></component>
          </el-icon>
          <span>{{ item.meta.title }}</span>
        </template>
        <!-- 路由递归 : 二级路由还有子路由,子路由又还有子路由-->
        <MenuTree :menuList="item.children"></MenuTree>
      </el-sub-menu>
    </template>
  </div>
</template>

<script lang="ts" setup>
import { useRouter } from 'vue-router'
const router = useRouter()
// 获取父组件传递过来的全部路由数组
defineProps(['menuList'])

const goRoute = (e: any) => {
  console.log(e.index)
  router.push(e.index)
}
</script>

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

四、NarBar.vue中使用

<template>
  <el-aside class="layout-menu">
    <!-- logo -->
    <div class="layout-logo">
      <img src="@/assets/vue.svg" alt="logo图标" />
      <p>v3后台管理系统</p>
    </div>
    <!-- 展示菜单 -->
    <!-- 滚动组件 -->
    <el-scrollbar class="layout-scrollbar">
      <el-menu>
        <!-- 菜单组件-->
        <MenuTree :menu-list="menuItems"></MenuTree>
      </el-menu>

      <!--      <el-menu default-active="2" class="el-menu-vertical-demo" router>-->
      <!--        <el-menu-item index="/index">-->
      <!--          <el-icon><House /></el-icon>-->
      <!--          <span>首页</span>-->
      <!--        </el-menu-item>-->
      <!--        <el-sub-menu index="/user">-->
      <!--          <template #title>-->
      <!--            <el-icon><User /></el-icon>-->
      <!--            <span>用户管理</span>-->
      <!--          </template>-->
      <!--          <el-menu-item index="/user/userList">-->
      <!--            <span>用户列表</span>-->
      <!--          </el-menu-item>-->
      <!--        </el-sub-menu>-->

      <!--        <el-sub-menu index="/system">-->
      <!--          <template #title>-->
      <!--            <el-icon><Setting /></el-icon>-->
      <!--            <span>系统管理</span>-->
      <!--          </template>-->
      <!--          <el-menu-item index="/system/dict">-->
      <!--            <span>字典列表页面</span>-->
      <!--          </el-menu-item>-->
      <!--          <el-menu-item index="/system/roleList">-->
      <!--            <span>角色列表页面</span>-->
      <!--          </el-menu-item>-->
      <!--        </el-sub-menu>-->
      <!--      </el-menu>-->
    </el-scrollbar>
  </el-aside>
</template>
<script setup lang="ts">
import { HomeFilled, Setting } from '@element-plus/icons-vue'
import { reactive } from 'vue'
import MenuTree from '@/components/MenuTree/MenuTree.vue'
import Dict from '@/views/system/dict/Dict.vue'
import Acl from '@/views/acl/Acl.vue'
import Role from '@/views/acl/role/Role.vue'
import Permission from '@/views/acl/permission/Permission.vue'
import AclUser from '@/views/acl/user/AclUser.vue'

const menuItems = reactive([
  {
    name: 'index',
    path: '/index',
    meta: {
      title: '首页',
      hidden: false,
      icon: 'HomeFilled'
    }
  },
  {
    name: 'Acl',
    path: '/acl',
    meta: {
      title: '权限管理',
      icon: 'Lock'
    },
    redirect: '/acl/user',
    children: [
      {
        name: 'User',
        path: '/acl/user',
        component: AclUser,
        meta: {
          title: '用户管理',
          icon: 'User'
        }
      },
      {
        path: '/acl/role',
        component: Role,
        name: 'Role',
        meta: {
          title: '角色管理',
          icon: 'UserFilled'
        }
      },
      {
        path: '/acl/permission',
        component: Permission,
        name: 'Permission',
        meta: {
          title: '菜单管理',
          icon: 'Monitor'
        }
      }
    ]
  },

  {
    name: 'system',
    path: '/system',
    meta: {
      title: '系统管理',
      icon: 'Setting'
    },
    children: [
      {
        path: '/system/dict',
        component: Dict,
        name: 'Permission',
        meta: {
          title: '字典管理',
          icon: 'Monitor',
          hidden: false
        }
      }
    ]
  }
])
</script>

<style lang="scss" scoped>
.layout-menu {
  border-right: 1px solid #e4e7ed;
  width: $base-menu-width;
  background-color: $base-menu-background;
  .layout-logo {
    width: 100%;
    height: $base-menu-logo-height;
    color: white;
    display: flex;
    align-items: center;
    img {
      width: 40px;
      height: 40px;
    }
    p {
      color: white;
    }
  }

  .layout-scrollbar {
    width: 100%;
    height: calc(100vh - $base-menu-logo-height);
    .el-menu {
      border-right: none;
      background-color: $base-menu-background;
    }
  }
}
</style>

五、运行代码查看效果

在这里插入图片描述
这里字典管理出现了两次。是因为 我们需要根据自己的需要选择当前菜单下只有一个子菜单时,父菜单是否展示。
如果父菜单展示的话,红色部分的注释掉即可
在这里插入图片描述如果父菜单不展示的红色部分改为大于1就可以了。
在这里插入图片描述我喜欢都展示,所以注释掉了=1的情况。

六、最终版以及效果

<template>
  <div>
    <template v-for="(item, index) in menuList" :key="item.path">
      <!-- 没有子路由 -->
      <template v-if="!item.children">
        <el-menu-item
          :index="item.path"
          v-if="!item.meta.hidden"
          @click="goRoute"
        >
          <template #title>
            <!-- 图标 -->
            <el-icon>
              <component :is="item.meta.icon"></component>
            </el-icon>
            <span>{{ item.meta.title }}</span>
          </template>
        </el-menu-item>
      </template>

      <!-- 只有一个子集 -->
<!--      <template v-if="item.children && item.children.length == 1">-->
<!--        <el-menu-item-->
<!--          @click="goRoute"-->
<!--          :index="item.children[0].path"-->
<!--          v-if="!item.children[0].meta.hidden"-->
<!--        >-->
<!--          <el-icon>-->
<!--            <component :is="item.children[0].meta.icon"></component>-->
<!--          </el-icon>-->
<!--          <template #title>-->
<!--            <span>{{ item.children[0].meta.title }}</span>-->
<!--          </template>-->
<!--        </el-menu-item>-->
<!--      </template>-->

      <!-- 有子路由,  有子菜单的继续遍历(递归) -->
      <el-sub-menu
        :index="item.path"
        v-if="item.children && item.children.length >= 1"
      >
        <template #title>
          <!-- 图标 -->
          <el-icon>
            <component :is="item.meta.icon"></component>
          </el-icon>
          <span>{{ item.meta.title }}</span>
        </template>
        <!-- 路由递归 : 二级路由还有子路由,子路由又还有子路由-->
        <MenuTree :menuList="item.children"></MenuTree>
      </el-sub-menu>
    </template>
  </div>
</template>

<script lang="ts" setup>
import { useRouter } from 'vue-router'
const router = useRouter()
// 获取父组件传递过来的全部路由数组
defineProps(['menuList'])

const goRoute = (e: any) => {
  console.log(e.index)
  router.push(e.index)
}
</script>

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

在这里插入图片描述

;