Vue 自定义级联菜单
Menu组件
1 <template> 2 <div class="menu" v-if="global.v > 0"> 3 <div>v:{{ global.v }} level:{{ global.level }}</div> 4 <ul @mouseenter="enter()" @mouseleave="leave()"> 5 <li v-for="menu in menus" :key="menu.id" @mouseenter="active(menu)" :class="{ active: level <= global.level && activeMenuId == menu.id }">{{ menu.id }}_L{{ level }}</li> 6 </ul> 7 <div v-if="submenu && this.level - this.global.level < 1" class="menu-sub"> 8 <A :global="global" :level="level + 1" :menus="submenu"></A> 9 </div> 10 </div> 11 </template> 12 13 <script> 14 export default { 15 name: 'A', 16 props: { 17 global: { 18 type: Object, 19 default: () => { 20 return { v: 0, level: 1 } 21 } 22 }, 23 level: {}, 24 menus: {} 25 }, 26 components: {}, 27 data() { 28 return { 29 submenu: null, 30 activeMenuId: null 31 } 32 }, 33 created() {}, 34 methods: { 35 active(menu) { 36 this.activeMenuId = menu.id 37 this.delay(() => { 38 this.submenu = menu.submenu 39 console.info('show_sub_menu') 40 }) 41 }, 42 enter() { 43 this.global.level = this.level 44 }, 45 leave() { 46 this.delay(() => { 47 if (this.level == this.global.level) this.global.level-- 48 }) 49 }, 50 showSub() { 51 return this.submenu && this.level - this.global.level > 0 52 }, 53 delay(cb, delay) { 54 let v = ++this.global.v 55 setTimeout(() => { 56 if (this.global.v === v) { 57 cb() 58 } 59 }, delay || 50) 60 }, 61 hide() { 62 this.global.v = 0 63 }, 64 show() { 65 this.global.v = 1 66 } 67 } 68 } 69 </script> 70 71 <style> 72 .menu { 73 display: inline-block; 74 position: relative; 75 border: solid 1px #abc; 76 padding: 0px; 77 min-width: 100px; 78 background-color: beige; 79 } 80 81 .menu ul { 82 padding: 0; 83 margin: 0; 84 } 85 86 .menu .menu-sub { 87 position: absolute; 88 top: 0; 89 left: 100%; 90 } 91 92 .menu ul li { 93 list-style: none; 94 margin: 2px; 95 padding: 2px 10px; 96 } 97 98 .menu .active { 99 background-color: rgb(183, 218, 250); 100 } 101 </style>
使用
1 <template> 2 <div class="hello"> 3 <div><button @click="showMenu">File</button></div> 4 <A :level="1" :menus="menus" ref="GMenu"></A> 5 </div> 6 </template> 7 8 <script> 9 import A from './A' 10 11 let menus = [ 12 { 13 id: 'a', 14 submenu: [ 15 { 16 id: 'aa', 17 submenu: [{ id: 'aaa' }, { id: 'aab' }, { id: 'aac' }] 18 }, 19 { id: 'ab' }, 20 { id: 'ac' } 21 ] 22 } 23 ] 24 25 let makeMenu = (menus, pid, deep) => { 26 let m = Math.floor(Math.random() * 10) + 9 27 for (let index = 0; index < m; index++) { 28 let menu = { id: pid + '' + index } 29 let submenu = [] 30 if (deep < 3) { 31 menu.submenu = makeMenu(submenu, menu.id, deep + 1) 32 } 33 menus.push(menu) 34 } 35 return menus 36 } 37 38 makeMenu(menus, 'm', 1) 39 40 console.info(menus) 41 42 export default { 43 components: { A }, 44 name: 'HelloWorld', 45 data() { 46 return { 47 menus: menus 48 } 49 }, 50 props: { 51 msg: String 52 }, 53 created() { 54 let self = this 55 document.getElementsByTagName('body')[0].addEventListener('click', (e) => { 56 e.stopPropagation() 57 e.preventDefault() 58 if (e.target.tagName === 'BODY') { 59 console.log(e) 60 self.hideMenu() 61 } 62 }) 63 }, 64 methods: { 65 showMenu() { 66 this.$refs.GMenu.show() 67 }, 68 hideMenu() { 69 this.$refs.GMenu.hide() 70 } 71 } 72 } 73 </script> 74 75 <!-- Add "scoped" attribute to limit CSS to this component only --> 76 <style scoped> 77 h3 { 78 margin: 40px 0 0; 79 } 80 ul { 81 list-style-type: none; 82 padding: 0; 83 } 84 li { 85 display: inline-block; 86 margin: 0 10px; 87 } 88 a { 89 color: #42b983; 90 } 91 </style>