Commit fd1582c5b5bd07df12d2cadf2d0309ed8ce22afd

Authored by 梁灏
1 parent dcbfc232

support Menu & Layout

support Menu & Layout
CHANGE.md
... ... @@ -35,4 +35,7 @@ class 改为了 className
35 35 支持 v-model
36 36 ### Dropdown
37 37 DropdownItem key 改为 name, Dropdown 的 visible 要使用 @on-visible-change 捕获,不再 sync
38   -DropdownItem 里,this.$parent.$parent 与1.0 有区别
39 38 \ No newline at end of file
  39 +DropdownItem 里,this.$parent.$parent 与1.0 有区别
  40 +### Menu
  41 +MenuItem 和 Submenu 的 key 改为了 name
  42 +Menu 的 activeKey 改为 activeName,openKeys 改为 openNames
40 43 \ No newline at end of file
... ...
README.md
... ... @@ -17,7 +17,7 @@
17 17  
18 18 ## Programming
19 19 - [x] Grid
20   -- [ ] Layout
  20 +- [x] Layout
21 21 - [x] Button
22 22 - [x] Icon
23 23 - [x] Input
... ... @@ -49,7 +49,7 @@
49 49 - [x] Poptip
50 50 - [x] Carousel
51 51 - [x] Tree
52   -- [ ] Menu
  52 +- [x] Menu
53 53 - [x] Tabs
54 54 - [x] Dropdown
55 55 - [ ] Page
... ...
examples/app.vue
... ... @@ -40,6 +40,7 @@ li + li { border-left: solid 1px #bbb; padding-left: 10px; margin-left: 10px; }
40 40 <li><router-link to="/slider">Slider</router-link></li>
41 41 <li><router-link to="/dropdown">Dropdown</router-link></li>
42 42 <li><router-link to="/breadcrumb">Breadcrumb</router-link></li>
  43 + <li><router-link to="/menu">Menu</router-link></li>
43 44 </ul>
44 45 </nav>
45 46 <router-view></router-view>
... ...
examples/main.js
... ... @@ -124,6 +124,10 @@ const router = new VueRouter({
124 124 {
125 125 path: '/breadcrumb',
126 126 component: require('./routers/breadcrumb.vue')
  127 + },
  128 + {
  129 + path: '/menu',
  130 + component: require('./routers/menu.vue')
127 131 }
128 132 ]
129 133 });
... ...
examples/routers/menu.vue
  1 +<!--<template>-->
  2 + <!--<div>-->
  3 + <!--<Menu mode="horizontal" :theme="theme1" active-name="1" @on-select="s">-->
  4 + <!--<Menu-item name="1">-->
  5 + <!--<Icon type="ios-paper"></Icon>-->
  6 + <!--内容管理-->
  7 + <!--</Menu-item>-->
  8 + <!--<Menu-item name="2">-->
  9 + <!--<Icon type="ios-people"></Icon>-->
  10 + <!--用户管理-->
  11 + <!--</Menu-item>-->
  12 + <!--<Submenu name="3">-->
  13 + <!--<template slot="title">-->
  14 + <!--<Icon type="stats-bars"></Icon>-->
  15 + <!--统计分析-->
  16 + <!--</template>-->
  17 + <!--<Menu-group title="使用">-->
  18 + <!--<Menu-item name="3-1">新增和启动</Menu-item>-->
  19 + <!--<Menu-item name="3-2">活跃分析</Menu-item>-->
  20 + <!--<Menu-item name="3-3">时段分析</Menu-item>-->
  21 + <!--</Menu-group>-->
  22 + <!--<Menu-group title="留存">-->
  23 + <!--<Menu-item name="3-4">用户留存</Menu-item>-->
  24 + <!--<Menu-item name="3-5">流失用户</Menu-item>-->
  25 + <!--</Menu-group>-->
  26 + <!--</Submenu>-->
  27 + <!--<Menu-item name="4">-->
  28 + <!--<Icon type="settings"></Icon>-->
  29 + <!--综合设置-->
  30 + <!--</Menu-item>-->
  31 + <!--</Menu>-->
  32 + <!--<br>-->
  33 + <!--<p>切换主题</p>-->
  34 + <!--<Radio-group v-model="theme1">-->
  35 + <!--<Radio label="light"></Radio>-->
  36 + <!--<Radio label="dark"></Radio>-->
  37 + <!--<Radio label="primary"></Radio>-->
  38 + <!--</Radio-group>-->
  39 + <!--</div>-->
  40 +<!--</template>-->
  41 +<!--<script>-->
  42 + <!--export default {-->
  43 + <!--data () {-->
  44 + <!--return {-->
  45 + <!--theme1: 'light'-->
  46 + <!--}-->
  47 + <!--},-->
  48 + <!--methods: {-->
  49 + <!--s (s) {-->
  50 + <!--console.log(s)-->
  51 + <!--}-->
  52 + <!--}-->
  53 + <!--}-->
  54 +<!--</script>-->
  55 +
  56 +
1 57 <template>
2   - <Menu mode="horizontal" :theme="theme1" :active-key.sync="ak">
3   - <Menu-item key="1">
4   - <Icon type="ios-paper"></Icon>
5   - 内容管理
6   - </Menu-item>
7   - <Menu-item key="2">
8   - <Icon type="ios-people"></Icon>
9   - 用户管理
10   - </Menu-item>
11   - <Submenu key="3">
12   - <template slot="title">
13   - <Icon type="stats-bars"></Icon>
14   - 统计分析
15   - </template>
16   - <Menu-group title="使用">
17   - <Menu-item key="3-1">新增和启动</Menu-item>
18   - <Menu-item key="3-2">活跃分析</Menu-item>
19   - <Menu-item key="3-3">时段分析</Menu-item>
20   - </Menu-group>
21   - <Menu-group title="留存">
22   - <Menu-item key="3-4">用户留存</Menu-item>
23   - <Menu-item key="3-5">流失用户</Menu-item>
24   - </Menu-group>
25   - </Submenu>
26   - <Menu-item key="4">
27   - <Icon type="settings"></Icon>
28   - 综合设置
29   - </Menu-item>
30   - </Menu>
31   - <br>
32   - <p>切换主题</p>
33   - <Radio-group :model.sync="theme1">
34   - <Radio value="light"></Radio>
35   - <Radio value="dark"></Radio>
36   - <Radio value="primary"></Radio>
37   - </Radio-group>
38   - <i-button @click="ak = '2'">change</i-button>
  58 + <div>
  59 + <Row>
  60 + <i-col span="8">
  61 + <Menu :theme="theme2" @on-select="s">
  62 + <Submenu name="1">
  63 + <template slot="title">
  64 + <Icon type="ios-paper"></Icon>
  65 + 内容管理
  66 + </template>
  67 + <Menu-item name="1-1">文章管理</Menu-item>
  68 + <Menu-item name="1-2">评论管理</Menu-item>
  69 + <Menu-item name="1-3">举报管理</Menu-item>
  70 + </Submenu>
  71 + <Submenu name="2">
  72 + <template slot="title">
  73 + <Icon type="ios-people"></Icon>
  74 + 用户管理
  75 + </template>
  76 + <Menu-item name="2-1">新增用户</Menu-item>
  77 + <Menu-item name="2-2">活跃用户</Menu-item>
  78 + </Submenu>
  79 + <Submenu name="3">
  80 + <template slot="title">
  81 + <Icon type="stats-bars"></Icon>
  82 + 统计分析
  83 + </template>
  84 + <Menu-group title="使用">
  85 + <Menu-item name="3-1">新增和启动</Menu-item>
  86 + <Menu-item name="3-2">活跃分析</Menu-item>
  87 + <Menu-item name="3-3">时段分析</Menu-item>
  88 + </Menu-group>
  89 + <Menu-group title="留存">
  90 + <Menu-item name="3-4">用户留存</Menu-item>
  91 + <Menu-item name="3-5">流失用户</Menu-item>
  92 + </Menu-group>
  93 + </Submenu>
  94 + </Menu>
  95 + </i-col>
  96 + <i-col span="8">
  97 + <Menu :theme="theme2" active-name="1-2" :open-names="['1']" @on-select="s">
  98 + <Submenu name="1">
  99 + <template slot="title">
  100 + <Icon type="ios-paper"></Icon>
  101 + 内容管理
  102 + </template>
  103 + <Menu-item name="1-1">文章管理</Menu-item>
  104 + <Menu-item name="1-2">评论管理</Menu-item>
  105 + <Menu-item name="1-3">举报管理</Menu-item>
  106 + </Submenu>
  107 + <Submenu name="2">
  108 + <template slot="title">
  109 + <Icon type="ios-people"></Icon>
  110 + 用户管理
  111 + </template>
  112 + <Menu-item name="2-1">新增用户</Menu-item>
  113 + <Menu-item name="2-2">活跃用户</Menu-item>
  114 + </Submenu>
  115 + <Submenu name="3">
  116 + <template slot="title">
  117 + <Icon type="stats-bars"></Icon>
  118 + 统计分析
  119 + </template>
  120 + <Menu-group title="使用">
  121 + <Menu-item name="3-1">新增和启动</Menu-item>
  122 + <Menu-item name="3-2">活跃分析</Menu-item>
  123 + <Menu-item name="3-3">时段分析</Menu-item>
  124 + </Menu-group>
  125 + <Menu-group title="留存">
  126 + <Menu-item name="3-4">用户留存</Menu-item>
  127 + <Menu-item name="3-5">流失用户</Menu-item>
  128 + </Menu-group>
  129 + </Submenu>
  130 + </Menu>
  131 + </i-col>
  132 + <i-col span="8">
  133 + <Menu :theme="theme2" :open-names="['1']" accordion @on-select="s">
  134 + <Submenu name="1">
  135 + <template slot="title">
  136 + <Icon type="ios-paper"></Icon>
  137 + 内容管理
  138 + </template>
  139 + <Menu-item name="1-1">文章管理</Menu-item>
  140 + <Menu-item name="1-2">评论管理</Menu-item>
  141 + <Menu-item name="1-3">举报管理</Menu-item>
  142 + </Submenu>
  143 + <Submenu name="2">
  144 + <template slot="title">
  145 + <Icon type="ios-people"></Icon>
  146 + 用户管理
  147 + </template>
  148 + <Menu-item name="2-1">新增用户</Menu-item>
  149 + <Menu-item name="2-2">活跃用户</Menu-item>
  150 + </Submenu>
  151 + <Submenu name="3">
  152 + <template slot="title">
  153 + <Icon type="stats-bars"></Icon>
  154 + 统计分析
  155 + </template>
  156 + <Menu-group title="使用">
  157 + <Menu-item name="3-1">新增和启动</Menu-item>
  158 + <Menu-item name="3-2">活跃分析</Menu-item>
  159 + <Menu-item name="3-3">时段分析</Menu-item>
  160 + </Menu-group>
  161 + <Menu-group title="留存">
  162 + <Menu-item name="3-4">用户留存</Menu-item>
  163 + <Menu-item name="3-5">流失用户</Menu-item>
  164 + </Menu-group>
  165 + </Submenu>
  166 + </Menu>
  167 + </i-col>
  168 + </Row>
  169 + <br>
  170 + <p>切换主题</p>
  171 + <Radio-group v-model="theme2">
  172 + <Radio label="light"></Radio>
  173 + <Radio label="dark"></Radio>
  174 + </Radio-group>
  175 + </div>
39 176 </template>
40 177 <script>
41 178 export default {
42 179 data () {
43 180 return {
44   - theme1: 'light',
45   - ak: '1'
  181 + theme2: 'light'
  182 + }
  183 + },
  184 + methods: {
  185 + s (s) {
  186 + console.log(s);
46 187 }
47 188 }
48 189 }
... ...
src/components/menu/menu-item.vue
... ... @@ -2,12 +2,14 @@
2 2 <li :class="classes" @click.stop="handleClick"><slot></slot></li>
3 3 </template>
4 4 <script>
  5 + import Emitter from '../../mixins/emitter';
5 6 const prefixCls = 'ivu-menu';
6 7  
7 8 export default {
8 9 name: 'MenuItem',
  10 + mixins: [ Emitter ],
9 11 props: {
10   - key: {
  12 + name: {
11 13 type: [String, Number],
12 14 required: true
13 15 },
... ... @@ -36,8 +38,30 @@
36 38 methods: {
37 39 handleClick () {
38 40 if (this.disabled) return;
39   - this.$dispatch('on-menu-item-select', this.key);
  41 +
  42 + let parent = this.$parent;
  43 + let name = parent.$options.name;
  44 + while (parent && (!name || name !== 'Submenu')) {
  45 + parent = parent.$parent;
  46 + if (parent) name = parent.$options.name;
  47 + }
  48 +
  49 + if (parent) {
  50 + this.dispatch('Submenu', 'on-menu-item-select', this.name);
  51 + } else {
  52 + this.dispatch('Menu', 'on-menu-item-select', this.name);
  53 + }
40 54 }
  55 + },
  56 + mounted () {
  57 + this.$on('on-update-active-name', (name) => {
  58 + if (this.name === name) {
  59 + this.active = true;
  60 + this.dispatch('Submenu', 'on-update-active-name', true);
  61 + } else {
  62 + this.active = false;
  63 + }
  64 + });
41 65 }
42 66 };
43 67 </script>
... ...
src/components/menu/menu.vue
... ... @@ -3,10 +3,13 @@
3 3 </template>
4 4 <script>
5 5 import { oneOf } from '../../utils/assist';
  6 + import Emitter from '../../mixins/emitter';
6 7  
7 8 const prefixCls = 'ivu-menu';
8 9  
9 10 export default {
  11 + name: 'Menu',
  12 + mixins: [ Emitter ],
10 13 props: {
11 14 mode: {
12 15 validator (value) {
... ... @@ -20,10 +23,10 @@
20 23 },
21 24 default: 'light'
22 25 },
23   - activeKey: {
  26 + activeName: {
24 27 type: [String, Number]
25 28 },
26   - openKeys: {
  29 + openNames: {
27 30 type: Array,
28 31 default () {
29 32 return [];
... ... @@ -38,6 +41,11 @@
38 41 default: '240px'
39 42 }
40 43 },
  44 + data () {
  45 + return {
  46 + currentActiveName: this.activeName
  47 + };
  48 + },
41 49 computed: {
42 50 classes () {
43 51 let theme = this.theme;
... ... @@ -60,74 +68,46 @@
60 68 }
61 69 },
62 70 methods: {
63   - updateActiveKey () {
64   - this.$children.forEach((item, index) => {
65   - if (!this.activeKey && index === 0) {
66   - this.activeKey = -1;
67   - }
68   -
69   - if (item.$options.name === 'Submenu') {
70   - item.active = false;
71   - item.$children.forEach(subitem => {
72   - if (subitem.$options.name === 'MenuGroup') {
73   - subitem.$children.forEach(groupItem => {
74   - if (groupItem.key === this.activeKey) {
75   - groupItem.active = true;
76   - groupItem.$parent.$parent.active = true;
77   - } else {
78   - groupItem.active = false;
79   - }
80   - });
81   - } else if (subitem.$options.name === 'MenuItem') {
82   - if (subitem.key === this.activeKey) {
83   - subitem.active = true;
84   - subitem.$parent.active = true;
85   - } else {
86   - subitem.active = false;
87   - }
88   - }
89   - });
90   - } else if (item.$options.name === 'MenuGroup') {
91   - item.$children.forEach(groupItem => {
92   - groupItem.active = groupItem.key === this.activeKey;
93   - });
94   - } else if (item.$options.name === 'MenuItem') {
95   - item.active = item.key === this.activeKey;
96   - }
97   - });
  71 + updateActiveName () {
  72 + if (!this.currentActiveName) {
  73 + this.currentActiveName = -1;
  74 + }
  75 + this.broadcast('Submenu', 'on-update-active-name', false);
  76 + this.broadcast('MenuItem', 'on-update-active-name', this.currentActiveName);
98 77 },
99   - updateOpenKeys (key) {
100   - const index = this.openKeys.indexOf(key);
  78 + updateOpenKeys (name) {
  79 + const index = this.openNames.indexOf(name);
101 80 if (index > -1) {
102   - this.openKeys.splice(index, 1);
  81 + this.openNames.splice(index, 1);
103 82 } else {
104   - this.openKeys.push(key);
  83 + this.openNames.push(name);
105 84 }
106 85 },
107 86 updateOpened () {
108 87 this.$children.forEach(item => {
109 88 if (item.$options.name === 'Submenu') {
110   - if (this.openKeys.indexOf(item.key) > -1) item.opened = true;
  89 + if (this.openNames.indexOf(item.name) > -1) item.opened = true;
111 90 }
112 91 });
113 92 }
114 93 },
115   - compiled () {
116   - this.updateActiveKey();
  94 + mounted () {
  95 + this.updateActiveName();
117 96 this.updateOpened();
118   - },
119   - events: {
120   - 'on-menu-item-select' (key) {
121   - this.activeKey = key;
122   - this.$emit('on-select', key);
123   - }
  97 + this.$on('on-menu-item-select', (name) => {
  98 + this.currentActiveName = name;
  99 + this.$emit('on-select', name);
  100 + });
124 101 },
125 102 watch: {
126   - openKeys () {
127   - this.$emit('on-open-change', this.openKeys);
  103 + openNames () {
  104 + this.$emit('on-open-change', this.openNames);
  105 + },
  106 + activeName (val) {
  107 + this.currentActiveName = val;
128 108 },
129   - activeKey () {
130   - this.updateActiveKey();
  109 + currentActiveName () {
  110 + this.updateActiveName();
131 111 }
132 112 }
133 113 };
... ...
src/components/menu/submenu.vue
1 1 <template>
2 2 <li :class="classes" @mouseenter="handleMouseenter" @mouseleave="handleMouseleave">
3   - <div :class="[prefixCls + '-submenu-title']" v-el:reference @click="handleClick">
  3 + <div :class="[prefixCls + '-submenu-title']" ref="reference" @click="handleClick">
4 4 <slot name="title"></slot>
5 5 <Icon type="ios-arrow-down" :class="[prefixCls + '-submenu-title-icon']"></Icon>
6 6 </div>
7 7 <ul :class="[prefixCls]" v-if="mode === 'vertical'" v-show="opened"><slot></slot></ul>
8   - <Drop
9   - v-else
10   - v-show="opened"
11   - placement="bottom"
12   - transition="slide-up"
13   - v-ref:drop
14   - :style="dropStyle"><slot></slot></Drop>
  8 + <transition name="slide-up" v-else>
  9 + <Drop
  10 + v-show="opened"
  11 + placement="bottom"
  12 + ref="drop"
  13 + :style="dropStyle"><slot></slot></Drop>
  14 + </transition>
15 15 </li>
16 16 </template>
17 17 <script>
18 18 import Drop from '../select/dropdown.vue';
19 19 import Icon from '../icon/icon.vue';
20 20 import { getStyle } from '../../utils/assist';
  21 + import Emitter from '../../mixins/emitter';
21 22  
22 23 const prefixCls = 'ivu-menu';
23 24  
24 25 export default {
25 26 name: 'Submenu',
  27 + mixins: [ Emitter ],
26 28 components: { Icon, Drop },
27 29 props: {
28   - key: {
  30 + name: {
29 31 type: [String, Number],
30 32 required: true
31 33 },
... ... @@ -54,9 +56,11 @@
54 56 ];
55 57 },
56 58 mode () {
  59 + // todo while
57 60 return this.$parent.mode;
58 61 },
59 62 accordion () {
  63 + // todo while
60 64 return this.$parent.accordion;
61 65 },
62 66 dropStyle () {
... ... @@ -73,7 +77,8 @@
73 77  
74 78 clearTimeout(this.timeout);
75 79 this.timeout = setTimeout(() => {
76   - this.$parent.updateOpenKeys(this.key);
  80 + // todo while
  81 + this.$parent.updateOpenKeys(this.name);
77 82 this.opened = true;
78 83 }, 250);
79 84 },
... ... @@ -83,7 +88,8 @@
83 88  
84 89 clearTimeout(this.timeout);
85 90 this.timeout = setTimeout(() => {
86   - this.$parent.updateOpenKeys(this.key);
  91 + // todo while
  92 + this.$parent.updateOpenKeys(this.name);
87 93 this.opened = false;
88 94 }, 150);
89 95 },
... ... @@ -92,12 +98,14 @@
92 98 if (this.mode === 'horizontal') return;
93 99 const opened = this.opened;
94 100 if (this.accordion) {
  101 + // todo while
95 102 this.$parent.$children.forEach(item => {
96 103 if (item.$options.name === 'Submenu') item.opened = false;
97 104 });
98 105 }
99 106 this.opened = !opened;
100   - this.$parent.updateOpenKeys(this.key);
  107 + // todo while
  108 + this.$parent.updateOpenKeys(this.name);
101 109 }
102 110 },
103 111 watch: {
... ... @@ -117,11 +125,15 @@
117 125 }
118 126 }
119 127 },
120   - events: {
121   - 'on-menu-item-select' () {
  128 + mounted () {
  129 + this.$on('on-menu-item-select', (name) => {
122 130 if (this.mode === 'horizontal') this.opened = false;
  131 + this.dispatch('Menu', 'on-menu-item-select', name);
123 132 return true;
124   - }
  133 + });
  134 + this.$on('on-update-active-name', (status) => {
  135 + this.active = status;
  136 + });
125 137 }
126 138 };
127 139 </script>
... ...
src/components/select/dropdown.vue
... ... @@ -6,6 +6,7 @@
6 6 import Popper from 'popper.js';
7 7  
8 8 export default {
  9 + name: 'Drop',
9 10 props: {
10 11 placement: {
11 12 type: String,
... ...
src/index.js
... ... @@ -20,7 +20,7 @@ import Icon from &#39;./components/icon&#39;;
20 20 import Input from './components/input';
21 21 import InputNumber from './components/input-number';
22 22 // import LoadingBar from './components/loading-bar';
23   -// import Menu from './components/menu';
  23 +import Menu from './components/menu';
24 24 // import Message from './components/message';
25 25 // import Modal from './components/modal';
26 26 // import Notice from './components/notice';
... ... @@ -76,10 +76,10 @@ const iview = {
76 76 Input,
77 77 InputNumber,
78 78 // LoadingBar,
79   - // Menu,
80   - // MenuGroup: Menu.Group,
81   - // MenuItem: Menu.Item,
82   - // Submenu: Menu.Sub,
  79 + Menu,
  80 + MenuGroup: Menu.Group,
  81 + MenuItem: Menu.Item,
  82 + Submenu: Menu.Sub,
83 83 // Message,
84 84 // Modal,
85 85 // Notice,
... ...