前言
后台项目需要根据router生成sidebar,本来以为使用element-plus不会遇到什么问题,但是动手做还是有很多bug,小小记录一下
根据路由生成sidebar
index.vue
部分的代码很简单,配置一下el-menu的属性,然后把routes传给sidebar-item
。这里的传给sidebar-item
的数据都是有用的。
<el-menu :collapse="isCollapse"
:unique-opened="false"
:collapse-transition="false"
mode="vertical">
<sidebar-item v-for="route in routes"
:key="route.path"
:item="route"
:base-path="route.path" />
</el-menu>
复制代码
sidebar-item
部分的代码稍微有点复杂。首先我们先思考传给sidebar-item
的路由共有哪几种。
- 无子路由的普通路由
- 有一个子路由的嵌套路由
- 有多个子路由的嵌套路由
但是其实不止这三种,当传入路由无子路由时你需要额外一个变量is-nest
去判断它是否已经是一个子路由,所以还有一种。
- 无子路由的嵌套路由
我们把有一个子路由的嵌套路由与无子路由的普通路由合并,就有下面的生成代码
<div v-if="!item.hidden">
<template v-if='onlyHasOneShowing(item)&&(!onlyOneChild.date.children||onlyOneChild.date.noShowChild)'>
<component :is="isHttp(item.path)"
v-bind="propsLink(pathResolve(onlyOneChild.date.path))">
<el-menu-item :index='pathResolve(onlyOneChild.date.path)'
:class="{'submenu-title-noDropdown':!isNest}">
<item :icon="onlyOneChild.date.meta.icon"
:title="onlyOneChild.date.meta.title" />
</el-menu-item>
</component>
</template>
<el-sub-menu v-else
:index="pathResolve(item.path)"
popper-append-to-body>
<template #title>
<item v-if="item.meta"
:icon="item.meta && item.meta.icon"
:title="item.meta.title" />
</template>
<sidebar-item v-for="child in item.children"
:key="child.path"
:is-nest="true"
:item="child"
:base-path="pathResolve(child.path)"
class="nest-menu" />
</el-sub-menu>
</div>
复制代码
修复因为递归生成菜单导致el-menu无法折叠的问题
如果问我学前端这么久最怕什么,那我的回答一定是写css。好在接触了elementui
等工具之后,css写的少了,但是这仍是绕不开的东西。css最令人讨厌的莫过于其牵一发而动全身的性质,如果没有一个很清晰的结构,写css就是折磨自己。
所以,当我发现折叠出现问题的时候,我内心是崩溃的,不过好在是解决了。
bug原因
产生bug的原因很简单,el-menu
希望它下面包裹的组件是el-menu-item
或者el-sub-menu
。但是我们的递归组件里包裹了一层div
,所以bug就出现了。
解决
解决方法也不算困难,我们自己弄一个折叠功能不就好了吗。根据源码可以知道,element是采用修改sidebar-item
的宽度来实现折叠的,这里我们采用修改sidebar-container
来实现折叠。
在解决这个问题的过程中,我深刻体会到清晰的html结构是多么的重要。html结构如下
<div>
<sidebar-container>
<el-menu>
<submenu-title-noDropdown>
<item/>
</submenu-title-noDropdown>
<el-sub-menu>
<item/>
</el-sub-menu>
</el-menu>
</sidebar-container>
<main-container>
</main-container>
</div>
复制代码
我们想要实现折叠效果需要修改sidebar-container
和main-container
的宽度。实现方式就是,当我们点击折叠按钮时,给外层div附上新的class,进而修改两者的宽度。
//css
//折叠前
.main-container {
transition: margin-left .28s;
margin-left: 210px;
position: relative;
}
.sidebar-container {
transition: width 0.28s;
width: 210px !important;
}
//折叠后
.hideSidebar {
.sidebar-container {
width: 54px !important;
}
.main-container {
margin-left: 54px;
}
}
//html
<div :class="classObj">
<sidebar class="sidebar-container" />
<div class="main-container">
<navbar />
<app-main />
</div>
</div>
//js
let classObj = computed(() => {
return {
hideSidebar: store.getters.isCollapse,
openSidebar: !store.getters.isCollapse,
}
})
复制代码
之后我们需要把标题部分隐藏,只留下图标部分,同时统一一下两种状态下图标的位置即可
//折叠前的样式
.sidebar-container {
.el-menu {
border: none;
height: 100%;
width: 100%;
span {
position: absolute;
left: 54px;
}
}
}
//折叠后的样式
.hideSidebar {
.el-sub-menu {
overflow: hidden;
&>.el-sub-menu__title {
padding: 0 !important;
.svg-icon {
margin-left: 20px;
}
.sub-el-icon {
margin-left: 19px;
}
.el-sub-menu__icon-arrow {
display: none;
}
}
}
.el-menu--collapse {
.el-sub-menu {
&>.el-sub-menu__title {
&>span {
height: 0;
width: 0;
overflow: hidden;
visibility: hidden;
display: inline-block;
}
}
}
}
.submenu-title-noDropdown {
padding: 0 !important;
position: relative;
.svg-icon {
margin-left: 20px;
}
.sub-el-icon {
margin-left: 19px;
}
span {
height: 0;
width: 0;
overflow: hidden;
visibility: hidden;
display: inline-block;
}
}
复制代码
至此,sidebar功能完成