- Realize the transition effect of expanding and closing
- The selected item refreshes the page and keeps the selected expansion effect
- Dynamic indentation of submenus
- Implement using component recursion
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.$route.push ({name: routeJump(routeJump(item)); item.name}) this.currentName = this.$route.name } } } </script> <style lang="scss"> .menu { height: 100%; width: 50%; overflow: auto; background: #000000; } </style>Copy the code
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: {}, // true indicates the transition is still in progress, false indicates the transition is complete. document, } }, Mounted () {for (let value 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 **/ openMenu(dom, key) {// throttling // true indicates that the transition effect is not finished, If (this.activeDelay[key]) {return} // Transition end triggers dom.onTransitionEnd = (el) => {if (el. Target. ClassList. The contains (' menu - children ')) {/ close true/false if (dom. The opened) {dom. The 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) // Set triangle class this. 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; The transition: 0.5 s transform; } .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>Copy the code
Testing the Router file
Export default [{path: '/test', name: 'test', meta: {title: 'XX config ', icon: '< I style=" font-size: 14px! Important; 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>' }, }, ]Copy the code