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>

 

上一篇:第二周


下一篇:vue权限控制——动态路由