系列文章
- vue3.0+ts+element-plus多页签应用模板:前言
- vue3.0+ts+element-plus多页签应用模板:项目搭建
- vue3.0+ts+element-plus多页签应用模板:vue-router与vuex
- vue3.0+ts+element-plus多页签应用模板:element-plus按需引入与动态换肤
- vue3.0+ts+element-plus多页签应用模板:如何优雅地使用Svg图标
- vue3.0+ts+element-plus多页签应用模板:侧边导航菜单(上)
- vue3.0+ts+element-plus多页签应用模板:侧边导航菜单(下)
- vue3.0+ts+element-plus多页签应用模板:多级路由缓存
- vue3.0+ts+element-plus多页签应用模板:头部工具栏(上)
- vue3.0+ts+element-plus多页签应用模板:头部工具栏(中)
- vue3.0+ts+element-plus多页签应用模板:头部工具栏(下)
一、先说点什么
从这一章开始,文章描述的重点,将会从实际代码转移到思路讲述,贴出的代码只是关键部分。俗话说授之以鱼不如授之以渔,要想学会一样东西,打通思路是最重要的。就和学会武功,要打通任督二脉是一个道理。好的,那么我们就进入侧边栏的实现吧。
二、从问题开始,侧边栏是干啥的?
这个问题问得好!我们在做一个东西之前,首先要了解这个东西有啥用,你光图好看有逼格那属于浪费精力。所以,我们必须要知道我们为什么要去做侧边栏。那么侧边栏是干啥的呢?它的核心功能只有一个,那就是路由跳转。用户可以通过这样一个侧边导航,很轻松的进入一个又一个页面,这就是它最本质的功能。
所以,有了这个思路,我们就知道,我们需要将路由配置与这个组件联系起来,每当我们改动路由配置的时候,它就会自动生成菜单项。好的,既然说到了菜单项,那我们就先来研究一下,每个菜单项应该包括一些什么东西:
- key:每个菜单项的唯一标识,最好是其名称的英文翻译
- type:菜单项的类别(可包含子级菜单项的下拉菜单、可包含子级菜单项的分组菜单和普通子级菜单项)
- label:菜单项名称
- icon:svg图标名
- hidden:该路由是否显示到菜单中
- roles:能够访问该路由的账号角色,可以是字符串或字符串数组
- disabled:是否已禁用状态展示该菜单项
- children:其下包含的子级菜单项
- onClick:点击当前路由会发生什么(一般情况下是在系统内跳转路由,但有时候也需要干点别的)
这样就分析好了每个菜单项的属性了,现在让我们来与路由配置项对应一下:
{
key: name, // 菜单项的唯一标识对应路由名
type: meta.type, // 除了children之外的都可以放进meta中
label: meta.label,
icon: meta.icon,
hidden: meta.hidden,
roles: meta.roles,
disabled: meta.disabled,
onClick: meta.onClick
children // 子级菜单项对应子路由
}
由此可见,二者可以非常完美的对应起来,那么根据这个思路,我们就可以开始封装组件了。
三、封装组件之思路分析
初步分析一下,整个组件里面都有些啥:
- 一个整体:AsideBar
- 头部下面的菜单:ModuleMenu
- 菜单里的菜单项:MenuItem
- 头部Logo与标题:SystemLogo
总结一下:
- 创建一个
MenuItem.vue
作为菜单项,将路由配置项转换为菜单配置项,并且要分三种不同的类别,其中submenu和group两类中需要递归调用自身 - 创建一个
ModuleMenu.vue
作为所有菜单项的容器,负责将路由配置传递给MenuItem.vue
- 创建一个
SystemLogo.vue
作为头部的容器,展示图标和系统名 - 创建一个
AsideBar.vue
作为一个整体的容器,读取路由配置信息并传递给ModuleMenu.vue
,同时需要接收外部传来的collapse(控制自身的折叠与展开)、iconName(logo图标名)、iconColor(logo颜色)、systemName(系统名)
好的,整体分析完成。下面我们来一个一个的去实现吧。
四、封装菜单部分
1. MenuItem
我们先来看一下MenuItem的template部分:
<template>
<!-- menuItems是经过转换的菜单项列表 -->
<template v-for="item in menuItems" :key="item.key">
<!-- 通过hidden和roles字段判断是否显示当前菜单项 -->
<template v-if="getItemShow(item)">
<!-- v-if submenu -->
<el-submenu v-if="item.type === 'submenu'" :index="item.key" :disabled="item.disabled">