赞
踩
做什么:封装通用左侧菜单栏组件
怎么做:使用Element-Plus
组件库中的el-menu
组件进行二次封装
技术栈:Vue3 + Ts + Vite,且采用 setup 语法糖写法
准备工作:请各位自行引入Element-plus组件库,本文中有用到 svg组件,svg组件封装教程请看第五点
查看
Element-plus
组件库中的el-menu
组件,不难发现,菜单栏大致可以分为两类,一
类是有子菜单的,一类是无子菜单的
。
所以我们将对这两类进行分情况设计,再结合递归
,即可完成根据路由列表,动态渲染
菜单栏
<script lang="ts" setup> // sidebarItem 项组件 import SideBarItem from './sidebarItem.vue'; import { useRouter } from 'vue-router'; // 拿到路由列表,过滤我们不想要的 const router = useRouter(); const routerList = router.getRoutes().filter((v) => v.meta && v.meta.isShow); </script> <template> <div class="sidebar"> <!-- 项目名称及logo --> <div class="sidebar-logo flex-center"> <svg-icon icon-class="logo" /> <span>VitalityAdmin</span> </div> <!-- 导航菜单 --> <el-menu active-text-color="#fff" background-color="#001529" :default-active="$route.path" text-color="#999" :unique-opened="true" router> <!-- 引入子组件 --> <SideBarItem :routerList="routerList" /> </el-menu> <!-- active-text-color:当前菜单项被选中时,字体的颜色 --> <!-- background-color:这个menu菜单的背景色 --> <!-- default-active: 当前激活菜单的 index --> <!-- text-color:菜单项字体颜色 --> <!-- unique-opened:unique-opened 是否只保持一个子菜单的展开 --> <!-- router:是否使用 vue-router 的模式,启用该模式会在激活导航时以 index 作为 path 进行路由跳转 --> </div> </template> <style lang="scss" scoped> .sidebar { height: 100%; .sidebar-logo { height: 48px; background-color: #002140; color: #fff; font-weight: 700; line-height: 48px; text-align: center; font-size: 20px; } .el-menu { height: calc(100% - 48px); border-right: 0; overflow: auto; } } </style>
<script setup lang="ts"> import { RouteRecordRaw } from 'vue-router'; // 做类型限制,解决ts类型报错 type CustomRouteRecordRaw = RouteRecordRaw & { meta: { isShow?: boolean; }; }; const props = defineProps({ // 拿到父组件传递过来的路由列表进行渲染 routerList: { type: Array as () => CustomRouteRecordRaw[], required: true } }); </script> <template> <template v-for="item in props.routerList" :key="item.path"> <!-- 当该菜单项有子菜单时 --> <el-sub-menu :index="item.path" v-if="item.children && item.children.length > 0"> <template #title v-if="item.meta.icon"> <!-- 菜单项图标,我此处用的是全局封装的 svg组件 --> <el-icon><svg-icon :icon-class="item.meta.icon" /></el-icon> <!-- 菜单项名称,在路由中定义好 --> <span>{{ item.meta.title }}</span> </template> <!-- 若路由中未定义菜单项icon,则仅展示名称--(我的仅一级菜单有图标) --> <template #title v-else>{{ item.meta.title }}</template> <!-- 递归遍历-自己调用自己(核心代码) --> <sidebarItem :routerList="( item.children as CustomRouteRecordRaw[])" /> </el-sub-menu> <!-- 当前菜单项无子菜单 --> <el-menu-item :index="item.path" v-else> <!-- 与上面注释大致相同,不多做额外注释 --> <template v-if="item.meta.icon"> <el-icon><svg-icon :icon-class="item.meta.icon" /></el-icon> <span>{{ item.meta.title }}</span> </template> <template v-else> {{ item.meta.title }} </template> </el-menu-item> </template> </template> <style scoped lang="scss"> .is-active { background: #409eff; font-weight: 700; } .el-menu-item { &:hover { color: #fff; font-weight: 700; } } .el-menu--collapse { .el-menu-item { justify-content: center; } } // 下列代码是用于兼容horizontal所写,酌情删或留 .el-menu--horizontal { .el-menu-item.is-active { background-color: transparent !important; border-bottom: 2px solid #409eff !important; .el-icon, span { color: #409eff !important; } } .el-sub-menu.is-active { .el-sub-menu__title { border: 0 !important; } .el-icon, span { color: #409eff !important; } } } </style>
isShow: true, // 控制当前项是否在菜单栏中渲染出来,比如你写了 login 页面的路由,但是并不希望 login在menu菜单中渲染出来,即可设为false
title: ‘首页’, // menu菜单项的名称,没啥好说的
icon: ‘menu-home’ // menu菜单项的图标,我此处是与封装好的 svg 组件结合使用的
export default [ { path: '/layout', name: 'layoutIndex', component: () => import('@/layout/index.vue'), children: [ { path: '/home', name: 'homeIndex', component: () => import('@/views/home/index.vue'), meta: { isShow: true, // 控制当前项是否在菜单栏中渲染出来,比如你写了 login 页面的路由,但是并不希望 login在menu菜单中渲染出来,即可设为false title: '首页', // menu菜单项的名称,没啥好说的 icon: 'menu-home' // menu菜单项的图标,我此处是与封装好的 svg 组件结合使用的 } }, { path: '/echarts', name: 'echartIndex', // component: () => import('@/views/echarts/index.vue'), meta: { isShow: true, title: 'Echarts页', icon: 'menu-echarts' }, children: [ { path: '/echarts/barCharts', name: 'barCharts', component: () => import('@/views/echarts/barCharts.vue'), meta: { title: '柱状图' } }, { path: '/echarts/pieCharts', name: 'pieCharts', component: () => import('@/views/echarts/pieCharts.vue'), meta: { title: '饼图' } } ] }, { path: '/package', name: 'packageIndex', component: () => import('@/views/package/index.vue'), meta: { isShow: true, title: '组件', icon: 'menu-package' } }, { path: '/menu', name: 'menuIndex', redirect: '/menu/menu-1', meta: { isShow: true, title: '一级菜单', icon: 'menu-package' }, children: [ { path: '/menu/menu-1', name: 'menu-1', component: () => import('@/views/menu/menu1.vue'), meta: { title: '二级菜单-1' } }, { path: '/menu/menu-2', name: 'menu-2', component: () => import('@/views/menu/menu2.vue'), meta: { title: '二级菜单-2' }, children: [ { path: '/menu/menu-2/children', name: 'menu3', component: () => import('@/views/menu/menu3.vue'), meta: { title: '三级菜单' } } ] } ] } ] } ];
本文不展开讲解
svg组件
的封装与使用,有需要的朋友欢迎参考下面的svg组件
封装教程
svg组件封装教程:http://t.csdnimg.cn/rv1zr
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。