
- 实现展开关闭过渡效果
- 选中项刷新页面后保持选中展开效果
- 子菜单动态缩进
- 使用组件递归实现
index.vue
<template>
<div class="menu">
<collapseMenu :list="$router.options.routes" :depth="1" :currentName="currentName"
@routeJump="routeJump"></collapseMenu>
</div>
</template>
<script>
import collapseMenu from './collapse-menu'
export default {
components: {
collapseMenu
},
data() {
return {
currentName: null
};
},
mounted() {
this.currentName = this.$route.name
},
methods: {
routeJump(item) {
this.$router.push({name: item.name})
this.currentName = this.$route.name
}
}
}
</script>
<style lang="scss">
.menu {
height: 100%;
width: 50%;
overflow: auto;
background: #000000;
}
</style>
collapse-menu.vue
<template>
<div :class="depth === 1 ? 'collapse-menu' :'menu-children'"
:style="{display:depth === 1 ? '' : 'none'}">
<template v-for="(item,index) in list">
<div class="menu-group" v-if="item.children && item.children.length" :key="index"
@click.stop="openMenu($event.currentTarget.children[1],item.name)">
<div class="menu-title" :style="{'padding-left': `${depth * 20}px`}">
<span>
<i v-show="item.meta.icon" v-html="item.meta.icon"></i>
{{item.meta.title}}
</span>
<span class="icon-top" :class="{active:activeName[item.name]}">▲</span>
</div>
<collapse-menu :list="item.children" :depth="depth + 1" :current-name="currentName"
v-on="$listeners"></collapse-menu>
</div>
<div class="menu-title" :key="index" :style="{'padding-left': `${depth * 20}px`}"
:class="{'menu-active':currentName === item.name}"
v-else
@click.stop="routeJump(item,$event)">
<span>
<i v-show="item.meta.icon" v-html="item.meta.icon"></i>
{{item.meta.title}}
</span>
</div>
</template>
</div>
</template>
<script>
export default {
name: "collapse-menu",
props: {
list: Array,
depth: Number,
currentName: String
},
data() {
return {
activeName: {},
activeDelay: {},
doc: document,
}
},
mounted() {
for (let item of this.$route.matched) {
for (let value of this.list) {
if (item.name === value.name) {
this.$el.opened = true
this.$el.style.display = ''
this.$set(this.activeName, value.name, !this.activeName[value.name])
return
}
}
}
},
methods: {
openMenu(dom, key) {
if (this.activeDelay[key]) {
return
}
dom.ontransitionend = (el) => {
if (el.target.classList.contains('menu-children')) {
if (dom.opened) {
dom.opened = false
dom.style.height = ``
dom.style.display = 'none'
} else {
dom.opened = true
dom.style.height = ``
}
}
this.$set(this.activeDelay, key, false)
el.stopPropagation()
}
if (!dom.opened) {
dom.style.display = 'block'
dom.style.height = `0`
this.$nextTick(() => {
dom.style.height = `${dom.scrollHeight}px`
})
} else {
dom.style.height = `${dom.scrollHeight}px`
setTimeout(() => {
dom.style.height = '0'
})
}
this.$set(this.activeDelay, key, true)
this.$set(this.activeName, key, !this.activeName[key])
},
routeJump(item) {
if (this.$route.name !== item.name) {
this.$emit('routeJump', item)
}
}
}
}
</script>
<style lang="scss" scoped>
.menu-children {
transition: height .5s;
}
.menu-children, .collapse-menu {
overflow: hidden;
color: #cccccc;
}
.icon-top {
margin-top: 3px;
transition: transform 0.5s;
}
.active {
transform: rotateZ(180deg);
}
.menu-title {
padding-right:20px;
display: flex;
justify-content: space-between;
align-items: center;
cursor: pointer;
height: 45px;
&:hover {
background: #1E2088;
span {
color: #ffffff;
}
}
span {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
i:first-child{
margin-right: 10px;
}
}
}
.menu-active {
background: #1E2088;
color: #ffffff;
}
</style>
测试router文件
export default [
{
path: '/test',
name: 'test',
meta: {
title: 'XX配置',
icon: '<i style="font-style: initial;">☀</i>'
},
children: [
{
path: 'vcvc',
name: 'vcvc',
meta: {
title: 'vcvc'
},
children: [
{
path: 'aaa',
name: 'aaa',
meta: {
title: 'aaa'
},
}, {
path: 'nnn',
name: 'nnn',
meta: {
title: 'nnn'
},
}
]
},
{
path: 'fdf',
name: 'fdf',
meta: {
title: 'fdf',
icon: '<i style="font-style: initial;">☁</i>'
},
children: [
{
path: 'opop',
name: 'opop',
meta: {
title: 'opop'
},
},
{
path: 'jjj',
name: 'jjj',
meta: {
title: 'jjj'
},
children: [
{
path: 'pppp',
name: 'pppp',
meta: {
title: 'pppp'
},
}, {
path: 'ilili',
name: 'ilili',
meta: {
title: 'ilili'
},
},
]
}
]
},
{
path: 'test',
name: 'test',
meta: {
title: 'test',
icon: '<i style="font-style: initial;">☔</i>'
},
children: [
{
path: 'nbnb',
name: 'nbnb',
meta: {
title: 'nbnb'
},
}
]
},
]
},
{
path: '/aaaa',
name: 'aaaa',
meta: {
title: 'aas',
icon: '<i style="font-style: initial;">♗</i>'
},
},
]