preface
The sidebar needs to be generated according to the router in the background project. I thought that there would be no problems with using Element-Plus, but there are still many bugs in doing it
Generate the sidebar based on the route
The code for the index.vue section is simple, configure the attributes of el-menu and pass routes to sidebar-item. All the data passed to the sidebar-item here is useful.
<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>
Copy the code
The code for the sidebar-item section is a little more complicated. First of all, let’s think about the types of routes to the sidebar-item.
- A common route without child routes
- A nested routine with a subroute
- A nested route with multiple child routes
But there are more than three, when an incoming route has no child route you need an extra variable is-nest to determine if it is already a child route, so there is one more.
- Nested routines without child routing
We combine a nested route that has a child route with an ordinary route that has no child route to generate the following code
<template v-if='onlyHasOneShowing(item)&&(! onlyOneChild.date.children||onlyOneChild.date.noShowChild)'>
<component :is="isHttp(onlyOneChild.date.path)"
v-bind="attrObj(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>
function onlyHasOneShowing (item) {
if (item.children) {
const children = item.children
let showChild = []
children.map(item= > {
if (item.hidden) {
return false
} else {
showChild.push(item)
}
})
if (showChild.length == 0) { onlyOneChild.date = { ... item,path: ' '.noShowChild: true }
return true
}
if (showChild.length == 1) {
onlyOneChild.date = showChild[0]
return true}}else{ onlyOneChild.date = { ... item,path: ' '.noShowChild: true }
return true
}
return false
}
function pathResolve (routePath) {
return path.resolve(props.basePath, routePath)
}
function isHttp (path) {
if (path.includes('http')) {
return 'a'
} else {
return 'router-link'}}function attrObj (path) {
if (path.includes('http')) {
return {
href: path,
target: '_blank',}}return {
to: pathResolve(path)
}
}
Copy the code
- The pathResolve function is used to connect the child route to the current route
- IsHttp is used to determine whether a route is an external link. If so, return the A label, otherwise return router-link
- AttrObj is used with isHttp to assign attributes to the corresponding A or router-link
- The onlyHasOneShowing function determines if there is one child path to display, if there is only one child path to display, if there is no child path to display, and if there is more than one child path to display, the recursion continues
It’s a little hard to understand, but console.log makes it clear in a few keystrokes
Fixed an issue where el-Menu could not collapse due to recursive menu generation
If you ask me what I’m afraid of learning front-end for so long, my answer must be writing CSS. Fortunately, with tools like Elementui, CSS is written less, but it’s still something you can’t get around. One of the most annoying things about CSS is its all-in nature. If you don’t have a clear structure, writing CSS is torture.
So, when I found out that there was a problem with the folding, I was devastated, but fortunately, it was resolved.
Bug reason
The reason for the bug is simple: El-Menu wants the component wrapped underneath it to be El-Menu-Item or El-Sub-Menu. But our recursive component has a layer of divs wrapped around it, and that’s where the bug starts.
To solve
The solution is not difficult, we make a folding function is not good. According to the source code, element is to modify the sidebar-item width to achieve folding, here we use to modify the sidebar-container to achieve folding.
In the process of solving this problem, I learned how important it is to have a clean HTML structure. The HTML structure is as follows
<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>
Copy the code
We need to change the width of sidebar-Container and main-Container to achieve the folding effect. This is done by attaching a new class to the outer div when we click the collapse button, changing the width of both.
//css
/ / before folding
.main-container {
transition: margin-left .28s;
margin-left: 210px;
position: relative;
}
.sidebar-container {
transition: width 0.28s; width: 210px ! important; }/ / after folding
.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,
}
})
Copy the code
Then we need to hide the title part, leaving only the icon part, and unify the position of the ICONS in the two states
// The style before folding.sidebar-container {
.el-menu {
border: none;
height: 100%;
width: 100%;
span {
position: absolute;
left: 54px; }}} // The folded style.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; }}Copy the code
So far, the sidebar function is complete