Просмотр исходного кода

submenu auto expand based on default-active value

baiyaaaaa 9 лет назад
Родитель
Сommit
dd29867845

+ 9 - 0
CHANGELOG.md

@@ -1,6 +1,15 @@
 ## 更新日志
 
+### 1.0.0-rc.5(待发布)
+
+*2016-XX-XX*
+
 - 修复 Table 头部不同步的问题
+- 修复 Menu 组件 default-active 绑定动态值无法更新的问题
+- 新增特性 Menu 组件中若选中子菜单项现在会自动展开所有父级菜单
+
+#### 非兼容性更新
+- Menu 组件的 `unique-opend` 属性修正为 `unique-opened`
 
 ### 1.0.0-rc.4
 

+ 1 - 7
examples/docs/menu.md

@@ -30,12 +30,6 @@
 
 <script>
   export default {
-    data() {
-      return {
-        search: '',
-        search2: ''
-      };
-    },
     methods: {
       handleopen(key, keyPath) {
         console.log(key, keyPath);
@@ -159,7 +153,7 @@
 | theme     | 主题色   | string    | light,dark | light |
 | default-active | 当前激活菜单的 index | string    | — | — |
 | default-openeds | 当前打开的submenu的 key 数组 | Array    | — | — |
-| unique-opend  | 是否只保持一个子菜单的展开 | boolean   | — | false   |
+| unique-opened  | 是否只保持一个子菜单的展开 | boolean   | — | false   |
 | router  | 是否使用 vue-router 的模式,启用该模式会在激活导航时以 index 作为 path 进行路由跳转 | boolean   | — | false   |
 
 ### Menu Events

+ 5 - 19
packages/menu/src/menu-item.vue

@@ -1,9 +1,12 @@
 <script>
+  import menuMixin from './menu-mixin';
   module.exports = {
     name: 'el-menu-item',
 
     componentName: 'menu-item',
 
+    mixins: [menuMixin],
+
     props: {
       index: {
         type: String,
@@ -15,24 +18,6 @@
       }
     },
     computed: {
-      indexPath() {
-        var path = [this.index];
-        var parent = this.$parent;
-        while (parent.$options._componentTag !== 'el-menu') {
-          if (parent.index) {
-            path.unshift(parent.index);
-          }
-          parent = parent.$parent;
-        }
-        return path;
-      },
-      rootMenu() {
-        var parent = this.$parent;
-        while (parent.$options._componentTag !== 'el-menu') {
-          parent = parent.$parent;
-        }
-        return parent;
-      },
       active() {
         return this.index === this.rootMenu.activeIndex;
       }
@@ -42,7 +27,8 @@
         this.rootMenu.handleSelect(this.index, this.indexPath);
       }
     },
-    mounted() {
+    created() {
+      this.rootMenu.menuItems[this.index] = this;
     }
   };
 </script>

+ 22 - 0
packages/menu/src/menu-mixin.js

@@ -0,0 +1,22 @@
+module.exports = {
+  computed: {
+    indexPath() {
+      var path = [this.index];
+      var parent = this.$parent;
+      while (parent.$options._componentTag !== 'el-menu') {
+        if (parent.index) {
+          path.unshift(parent.index);
+        }
+        parent = parent.$parent;
+      }
+      return path;
+    },
+    rootMenu() {
+      var parent = this.$parent;
+      while (parent.$options._componentTag !== 'el-menu') {
+        parent = parent.$parent;
+      }
+      return parent;
+    }
+  }
+};

+ 48 - 13
packages/menu/src/menu.vue

@@ -37,33 +37,67 @@
         type: String,
         default: 'light'
       },
-      uniqueOpend: Boolean,
+      uniqueOpened: Boolean,
       router: Boolean
     },
     data() {
       return {
         activeIndex: this.defaultActive,
-        openedMenus: this.defaultOpeneds.slice(0)
+        openedMenus: this.defaultOpeneds.slice(0),
+        menuItems: {},
+        submenus: {}
       };
     },
+    watch: {
+      defaultActive(value) {
+        this.activeIndex = value;
+        let indexPath = this.menuItems[value].indexPath;
+
+        this.handleSelect(value, indexPath);
+      },
+      defaultOpeneds(value) {
+        this.openedMenus = value;
+      }
+    },
     methods: {
-      handleMenuExpand(index, indexPath) {
-        if (this.uniqueOpend) {
-          this.broadcast('submenu', 'close-menu', indexPath);
-          this.openedMenus = this.openedMenus.filter((index) => {
+      openMenu(index, indexPath) {
+        let openedMenus = this.openedMenus;
+        if (openedMenus.indexOf(index) !== -1) return;
+        if (this.uniqueOpened) {
+          openedMenus = openedMenus.filter(index => {
             return indexPath.indexOf(index) !== -1;
           });
         }
-        this.$emit('open', index, indexPath);
+        openedMenus.push(index);
       },
-      handleMenuCollapse(index, indexPath) {
+      closeMenu(index, indexPath) {
         this.openedMenus.splice(this.openedMenus.indexOf(index), 1);
-        this.$emit('close', index, indexPath);
+      },
+      handleSubmenuClick(index, indexPath) {
+        let isOpened = this.openedMenus.indexOf(index) !== -1;
+
+        if (isOpened) {
+          this.closeMenu(index, indexPath);
+          this.$emit('close', index, indexPath);
+        } else {
+          this.openMenu(index, indexPath);
+          this.$emit('open', index, indexPath);
+        }
       },
       handleSelect(index, indexPath) {
         this.activeIndex = index;
         this.$emit('select', index, indexPath);
-        this.broadcast('submenu', 'select', [index, indexPath]);
+
+        if (this.mode === 'horizontal') {
+          this.broadcast('submenu', 'item-select', [index, indexPath]);
+          this.openedMenus = [];
+        } else {
+          // 展开该菜单项的路径上所有子菜单
+          indexPath.forEach(index => {
+            let submenu = this.submenus[index];
+            submenu && this.openMenu(index, submenu.indexPath);
+          });
+        }
 
         if (this.router) {
           this.$router.push(index);
@@ -71,9 +105,10 @@
       }
     },
     mounted() {
-      this.broadcast('submenu', 'open-menu', this.openedMenus);
-      this.$on('expand-menu', this.handleMenuExpand);
-      this.$on('collapse-menu', this.handleMenuCollapse);
+      let index = this.activeIndex;
+      let indexPath = this.menuItems[index].indexPath;
+
+      this.handleSelect(index, indexPath);
     }
   };
 </script>

+ 35 - 88
packages/menu/src/submenu.vue

@@ -1,12 +1,33 @@
+<template>
+  <li
+    :class="{ 'el-submenu': true, 'is-active': active, 'is-opened': opened}"
+    @mouseenter="handleMouseenter"
+    @mouseleave="handleMouseleave"
+  >
+    <div class="el-submenu__title" @click="handleClick">
+
+      <slot name="title"></slot>
+      <i :class="{
+          'el-submenu__icon-arrow': true,
+          'el-icon-arrow-down': rootMenu.mode === 'vertical',
+          'el-icon-caret-bottom': rootMenu.mode === 'horizontal'
+        }">
+      </i>
+    </div>
+    <transition :name="rootMenu.mode === 'horizontal' ? 'md-fade-bottom' : ''">
+      <ul class="el-menu" v-show="opened"><slot></slot></ul>
+    </transition>
+  </li>
+</template>
 <script>
-  import emitter from 'main/mixins/emitter';
+  import menuMixin from './menu-mixin';
 
   module.exports = {
     name: 'el-submenu',
 
     componentName: 'submenu',
 
-    mixins: [emitter],
+    mixins: [menuMixin],
 
     props: {
       index: {
@@ -16,117 +37,43 @@
     },
     data() {
       return {
-        opened: false,
         timeout: null,
         active: false
       };
     },
     computed: {
-      indexPath() {
-        var path = [this.index];
-        var parent = this.$parent;
-        while (parent.$options._componentTag !== 'el-menu') {
-          if (parent.index) {
-            path.unshift(parent.index);
-          }
-          parent = parent.$parent;
-        }
-        return path;
-      },
-      rootMenu() {
-        var parent = this.$parent;
-        while (parent.$options._componentTag !== 'el-menu') {
-          parent = parent.$parent;
-        }
-        return parent;
-      },
-      mode() {
-        return this.rootMenu.mode;
+      opened() {
+        return this.rootMenu.openedMenus.indexOf(this.index) !== -1;
       }
     },
     methods: {
       handleClick() {
-        if (this.mode === 'vertical') {
-          if (!this.opened) {
-            this.dispatch('menu', 'expand-menu', [this.index, this.indexPath]);
-            this.opened = true;
-          } else {
-            this.dispatch('menu', 'collapse-menu', [this.index, this.indexPath]);
-            this.opened = false;
-          }
-        }
+        this.rootMenu.handleSubmenuClick(this.index, this.indexPath);
       },
       handleMouseenter() {
-        if (this.mode === 'horizontal') {
+        if (this.rootMenu.mode === 'horizontal') {
           clearTimeout(this.timeout);
           this.timeout = setTimeout(() => {
-            this.dispatch('menu', 'expand-menu', [this.index, this.indexPath]);
-            this.opened = true;
+            this.rootMenu.openMenu(this.index, this.indexPath);
           }, 300);
         }
       },
       handleMouseleave() {
-        if (this.mode === 'horizontal') {
+        if (this.rootMenu.mode === 'horizontal') {
           clearTimeout(this.timeout);
           this.timeout = setTimeout(() => {
-            this.dispatch('menu', 'collapse-menu', [this.index, this.indexPath]);
-            this.opened = false;
+            this.rootMenu.closeMenu(this.index, this.indexPath);
           }, 300);
         }
       }
     },
+    created() {
+      this.rootMenu.submenus[this.index] = this;
+    },
     mounted() {
-      this.$on('close-menu', (openedIndexs) => {
-        if (openedIndexs && openedIndexs.indexOf(this.index) === -1) {
-          this.opened = false;
-        }
+      this.$on('item-select', (index, indexPath) => {
+        this.active = indexPath.indexOf(this.index) !== -1;
       });
-      this.$on('open-menu', (IndexsArray) => {
-        if (IndexsArray && IndexsArray.indexOf(this.index) !== -1) {
-          this.opened = true;
-        }
-      });
-      this.$on('select', (index, indexPath) => {
-        if (this.mode === 'horizontal') {
-          this.active = indexPath.indexOf(this.index) !== -1;
-          this.opened = false;
-        }
-      });
-    },
-    render(h) {
-      var submenu;
-
-      if (this.mode === 'horizontal') {
-        submenu = (
-          <transition name="md-fade-bottom">
-            {this.opened ? <ul class="el-menu">{this.$slots.default}</ul> : null }
-          </transition>
-        );
-      } else {
-        submenu = (
-          this.opened ? <ul class="el-menu">{this.$slots.default}</ul> : null
-        );
-      }
-
-      return (
-        <li
-          class={{ 'el-submenu': true, 'is-active': this.active, 'is-opened': this.opened}}
-          on-mouseenter={this.handleMouseenter}
-          on-mouseleave={this.handleMouseleave}
-        >
-          <div class="el-submenu__title" on-click={this.handleClick}>
-
-            {this.$slots.title}
-            <i class={{
-              'el-submenu__icon-arrow': true,
-              'el-icon-arrow-down': this.mode === 'vertical',
-              'el-icon-caret-bottom': this.mode === 'horizontal'
-            }}>
-            </i>
-          </div>
-          {submenu}
-        </li>
-      );
     }
   };
 </script>