Commit e05d728978cd6744f816f7a6b9c930048c94ae3d
1 parent
8778b343
update Menu
update Menu
Showing
10 changed files
with
437 additions
and
38 deletions
Show diff stats
src/components/dropdown/dropdown.vue
@@ -4,10 +4,7 @@ | @@ -4,10 +4,7 @@ | ||
4 | v-clickoutside="handleClose" | 4 | v-clickoutside="handleClose" |
5 | @mouseenter="handleMouseenter" | 5 | @mouseenter="handleMouseenter" |
6 | @mouseleave="handleMouseleave"> | 6 | @mouseleave="handleMouseleave"> |
7 | - <div | ||
8 | - :class="[prefixCls-rel]" | ||
9 | - v-el:reference | ||
10 | - @click="handleClick"><slot></slot></div> | 7 | + <div :class="[prefixCls-rel]" v-el:reference @click="handleClick"><slot></slot></div> |
11 | <Drop v-show="visible" :placement="placement" :transition="transition" v-ref:drop><slot name="list"></slot></Drop> | 8 | <Drop v-show="visible" :placement="placement" :transition="transition" v-ref:drop><slot name="list"></slot></Drop> |
12 | </div> | 9 | </div> |
13 | </template> | 10 | </template> |
src/components/menu/menu-group.vue
1 | <template> | 1 | <template> |
2 | - | 2 | + <li :class="[prefixCls + '-item-group']"> |
3 | + <div :class="[prefixCls + '-item-group-title']">{{ title }}</div> | ||
4 | + <ul><slot></slot></ul> | ||
5 | + </li> | ||
3 | </template> | 6 | </template> |
4 | <script> | 7 | <script> |
8 | + const prefixCls = 'ivu-menu'; | ||
9 | + | ||
5 | export default { | 10 | export default { |
6 | - props: {}, | 11 | + name: 'MenuGroup', |
12 | + props: { | ||
13 | + title: { | ||
14 | + type: String, | ||
15 | + default: '' | ||
16 | + } | ||
17 | + }, | ||
7 | data () { | 18 | data () { |
8 | - return {} | 19 | + return { |
20 | + prefixCls: prefixCls | ||
21 | + } | ||
9 | }, | 22 | }, |
10 | - computed: {}, | ||
11 | - methods: {} | 23 | + computed: { |
24 | + | ||
25 | + }, | ||
26 | + methods: { | ||
27 | + | ||
28 | + } | ||
12 | } | 29 | } |
13 | </script> | 30 | </script> |
14 | \ No newline at end of file | 31 | \ No newline at end of file |
src/components/menu/menu-item.vue
1 | <template> | 1 | <template> |
2 | - | 2 | + <li :class="classes" @click.stop="handleClick"><slot></slot></li> |
3 | </template> | 3 | </template> |
4 | <script> | 4 | <script> |
5 | + const prefixCls = 'ivu-menu'; | ||
6 | + | ||
5 | export default { | 7 | export default { |
6 | - props: {}, | 8 | + name: 'MenuItem', |
9 | + props: { | ||
10 | + key: { | ||
11 | + type: [String, Number], | ||
12 | + required: true | ||
13 | + }, | ||
14 | + disabled: { | ||
15 | + type: Boolean, | ||
16 | + default: false | ||
17 | + } | ||
18 | + }, | ||
7 | data () { | 19 | data () { |
8 | - return {} | 20 | + return { |
21 | + active: false | ||
22 | + } | ||
23 | + }, | ||
24 | + computed: { | ||
25 | + classes () { | ||
26 | + return [ | ||
27 | + `${prefixCls}-item`, | ||
28 | + { | ||
29 | + [`${prefixCls}-item-active`]: this.active, | ||
30 | + [`${prefixCls}-item-selected`]: this.active, | ||
31 | + [`${prefixCls}-item-disabled`]: this.disabled | ||
32 | + } | ||
33 | + ] | ||
34 | + } | ||
9 | }, | 35 | }, |
10 | - computed: {}, | ||
11 | - methods: {} | 36 | + methods: { |
37 | + handleClick () { | ||
38 | + this.$dispatch('on-menu-item-select', this.key); | ||
39 | + } | ||
40 | + } | ||
12 | } | 41 | } |
13 | </script> | 42 | </script> |
14 | \ No newline at end of file | 43 | \ No newline at end of file |
src/components/menu/menu.vue
1 | <template> | 1 | <template> |
2 | - | 2 | + <ul :class="classes"><slot></slot></ul> |
3 | </template> | 3 | </template> |
4 | <script> | 4 | <script> |
5 | + import { oneOf } from '../../utils/assist'; | ||
6 | + | ||
7 | + const prefixCls = 'ivu-menu'; | ||
8 | + | ||
5 | export default { | 9 | export default { |
6 | - props: {}, | 10 | + props: { |
11 | + mode: { | ||
12 | + validator (value) { | ||
13 | + return oneOf(value, ['horizontal', 'vertical']); | ||
14 | + }, | ||
15 | + default: 'vertical' | ||
16 | + }, | ||
17 | + theme: { | ||
18 | + validator (value) { | ||
19 | + return oneOf(value, ['light', 'dark', 'primary']); | ||
20 | + }, | ||
21 | + default: 'light' | ||
22 | + }, | ||
23 | + activeKey: { | ||
24 | + type: [String, Number] | ||
25 | + }, | ||
26 | + openKeys: { | ||
27 | + type: Array | ||
28 | + }, | ||
29 | + accordion: { | ||
30 | + type: Boolean, | ||
31 | + default: false | ||
32 | + } | ||
33 | + }, | ||
7 | data () { | 34 | data () { |
8 | - return {} | 35 | + return { |
36 | + | ||
37 | + } | ||
38 | + }, | ||
39 | + computed: { | ||
40 | + classes () { | ||
41 | + return [ | ||
42 | + `${prefixCls}`, | ||
43 | + { | ||
44 | + [`${prefixCls}-${this.mode}`]: this.mode, | ||
45 | + [`${prefixCls}-${this.theme}`]: this.theme | ||
46 | + } | ||
47 | + ] | ||
48 | + } | ||
49 | + }, | ||
50 | + methods: { | ||
51 | + updateActiveKey () { | ||
52 | + this.$children.forEach((item, index) => { | ||
53 | + if (!this.activeKey && index === 0) this.activeKey = item.key; | ||
54 | + | ||
55 | + if (item.$options.name === 'Submenu') { | ||
56 | + item.active = false; | ||
57 | + item.$children.forEach(subitem => { | ||
58 | + if (subitem.$options.name === 'MenuGroup') { | ||
59 | + subitem.$children.forEach(groupItem => { | ||
60 | + if (groupItem.key === this.activeKey) { | ||
61 | + groupItem.active = true; | ||
62 | + groupItem.$parent.$parent.active = true; | ||
63 | + } else { | ||
64 | + groupItem.active = false; | ||
65 | + } | ||
66 | + }) | ||
67 | + } else { | ||
68 | + if (subitem.key === this.activeKey) { | ||
69 | + subitem.active = true; | ||
70 | + subitem.$parent.active = true; | ||
71 | + } else { | ||
72 | + subitem.active = false; | ||
73 | + } | ||
74 | + } | ||
75 | + }) | ||
76 | + } else { | ||
77 | + item.active = item.key === this.activeKey; | ||
78 | + } | ||
79 | + }) | ||
80 | + } | ||
81 | + }, | ||
82 | + compiled () { | ||
83 | + this.updateActiveKey(); | ||
9 | }, | 84 | }, |
10 | - computed: {}, | ||
11 | - methods: {} | 85 | + events: { |
86 | + 'on-menu-item-select' (key) { | ||
87 | + this.activeKey = key; | ||
88 | + this.updateActiveKey(); | ||
89 | + this.$emit('on-select', key); | ||
90 | + } | ||
91 | + } | ||
12 | } | 92 | } |
13 | </script> | 93 | </script> |
14 | \ No newline at end of file | 94 | \ No newline at end of file |
src/components/menu/submenu.vue
1 | <template> | 1 | <template> |
2 | - | 2 | + <li :class="classes" @mouseenter="handleMouseenter" @mouseleave="handleMouseleave"> |
3 | + <div :class="[prefixCls + '-title']" v-el:reference> | ||
4 | + <span><slot name="title"></slot></span> | ||
5 | + <Icon type="ios-arrow-down"></Icon> | ||
6 | + </div> | ||
7 | + <ul :class="[prefixCls]" v-if="mode === 'vertical'"></ul> | ||
8 | + <Drop v-else v-show="opened" placement="bottom" transition="slide-up" v-ref:drop><slot></slot></Drop> | ||
9 | + </li> | ||
3 | </template> | 10 | </template> |
4 | <script> | 11 | <script> |
12 | + import Drop from '../select/dropdown.vue'; | ||
13 | + import Icon from '../icon/icon.vue'; | ||
14 | + const prefixCls = 'ivu-menu'; | ||
15 | + | ||
5 | export default { | 16 | export default { |
6 | - props: {}, | 17 | + name: 'Submenu', |
18 | + components: { Icon, Drop }, | ||
19 | + props: { | ||
20 | + key: { | ||
21 | + type: [String, Number], | ||
22 | + required: true | ||
23 | + } | ||
24 | + }, | ||
7 | data () { | 25 | data () { |
8 | - return {} | 26 | + return { |
27 | + prefixCls: prefixCls, | ||
28 | + active: false, | ||
29 | + opened: false | ||
30 | + } | ||
31 | + }, | ||
32 | + computed: { | ||
33 | + classes () { | ||
34 | + return [ | ||
35 | + `${prefixCls}-submenu`, | ||
36 | + { | ||
37 | + [`${prefixCls}-item-active`]: this.active, | ||
38 | + [`${prefixCls}-opened`]: this.opened | ||
39 | + } | ||
40 | + ] | ||
41 | + }, | ||
42 | + mode () { | ||
43 | + return this.$parent.mode; | ||
44 | + } | ||
45 | + }, | ||
46 | + methods: { | ||
47 | + handleMouseenter () { | ||
48 | + if (this.mode === 'vertical') return; | ||
49 | + clearTimeout(this.timeout); | ||
50 | + this.timeout = setTimeout(() => { | ||
51 | + this.opened = true; | ||
52 | + }, 250); | ||
53 | + }, | ||
54 | + handleMouseleave () { | ||
55 | + if (this.mode === 'vertical') return; | ||
56 | + clearTimeout(this.timeout); | ||
57 | + this.timeout = setTimeout(() => { | ||
58 | + this.opened = false; | ||
59 | + }, 150); | ||
60 | + } | ||
61 | + }, | ||
62 | + watch: { | ||
63 | + mode (val) { | ||
64 | + if (val === 'horizontal') { | ||
65 | + this.$refs.drop.update(); | ||
66 | + } | ||
67 | + }, | ||
68 | + opened (val) { | ||
69 | + if (this.mode === 'vertical') return; | ||
70 | + if (val) { | ||
71 | + this.$refs.drop.update(); | ||
72 | + } else { | ||
73 | + this.$refs.drop.destroy(); | ||
74 | + } | ||
75 | + } | ||
9 | }, | 76 | }, |
10 | - computed: {}, | ||
11 | - methods: {} | 77 | + events: { |
78 | + 'on-menu-item-select' () { | ||
79 | + this.opened = false; | ||
80 | + return true; | ||
81 | + } | ||
82 | + } | ||
12 | } | 83 | } |
13 | </script> | 84 | </script> |
14 | \ No newline at end of file | 85 | \ No newline at end of file |
src/styles/components/menu.less
1 | +@menu-prefix-cls: ~"@{css-prefix}menu"; | ||
2 | +@menu-dropdown-item-prefix-cls: ~"@{menu-prefix-cls}-horizontal .@{menu-prefix-cls}-submenu .@{select-dropdown-prefix-cls} .@{menu-prefix-cls}-item"; | ||
3 | + | ||
4 | +.@{menu-prefix-cls} { | ||
5 | + display: block; | ||
6 | + margin: 0; | ||
7 | + padding: 0; | ||
8 | + outline: none; | ||
9 | + list-style: none; | ||
10 | + color: @text-color; | ||
11 | + font-size: @font-size-base; | ||
12 | + position: relative; | ||
13 | + | ||
14 | + &-horizontal{ | ||
15 | + height: 60px; | ||
16 | + line-height: 60px; | ||
17 | + | ||
18 | + &.@{menu-prefix-cls}-light{ | ||
19 | + &:after{ | ||
20 | + content: ''; | ||
21 | + display: block; | ||
22 | + width: 100%; | ||
23 | + height: 1px; | ||
24 | + background: @border-color-base; | ||
25 | + position: absolute; | ||
26 | + bottom: 0; | ||
27 | + left: 0; | ||
28 | + } | ||
29 | + } | ||
30 | + } | ||
31 | + &-vertical{ | ||
32 | + width: 240px; | ||
33 | + &.@{menu-prefix-cls}-light{ | ||
34 | + &:after{ | ||
35 | + content: ''; | ||
36 | + display: block; | ||
37 | + width: 1px; | ||
38 | + height: 100%; | ||
39 | + background: @border-color-base; | ||
40 | + position: absolute; | ||
41 | + top: 0; | ||
42 | + bottom: 0; | ||
43 | + right: 0; | ||
44 | + } | ||
45 | + } | ||
46 | + } | ||
47 | + | ||
48 | + &-light{ | ||
49 | + background: #fff; | ||
50 | + } | ||
51 | + &-dark{ | ||
52 | + background: @title-color; | ||
53 | + } | ||
54 | + &-primary{ | ||
55 | + background: @primary-color; | ||
56 | + } | ||
57 | + | ||
58 | + &-item{ | ||
59 | + display: block; | ||
60 | + outline: none; | ||
61 | + list-style: none; | ||
62 | + font-size: @font-size-base; | ||
63 | + position: relative; | ||
64 | + z-index: 1; | ||
65 | + cursor: pointer; | ||
66 | + transition: all @transition-time @ease-in-out; | ||
67 | + | ||
68 | + .@{menu-prefix-cls}-light &{ | ||
69 | + color: @text-color; | ||
70 | + } | ||
71 | + .@{menu-prefix-cls}-dark &{ | ||
72 | + color: @subsidiary-color; | ||
73 | + &-active, &:hover{ | ||
74 | + color: #fff; | ||
75 | + } | ||
76 | + } | ||
77 | + .@{menu-prefix-cls}-primary &{ | ||
78 | + color: #fff; | ||
79 | + &-active, &:hover{ | ||
80 | + background: @link-active-color; | ||
81 | + } | ||
82 | + } | ||
83 | + } | ||
84 | + &-horizontal &-item, | ||
85 | + &-horizontal &-submenu | ||
86 | + { | ||
87 | + float: left; | ||
88 | + padding: 0 20px; | ||
89 | + position: relative; | ||
90 | + cursor: pointer; | ||
91 | + z-index: 1; | ||
92 | + transition: all @transition-time @ease-in-out; | ||
93 | + } | ||
94 | + | ||
95 | + &-light&-horizontal &-item, &-light&-horizontal &-submenu{ | ||
96 | + height: inherit; | ||
97 | + line-height: inherit; | ||
98 | + border-bottom: 2px solid transparent; | ||
99 | + &-active, &:hover{ | ||
100 | + color: @primary-color; | ||
101 | + border-bottom: 2px solid @primary-color; | ||
102 | + } | ||
103 | + } | ||
104 | + | ||
105 | + &-dark&-horizontal &-item{ | ||
106 | + &-active, &:hover{ | ||
107 | + color: #fff; | ||
108 | + } | ||
109 | + } | ||
110 | + | ||
111 | + &-primary&-horizontal &-item{ | ||
112 | + &-active, &:hover{ | ||
113 | + background: @link-active-color; | ||
114 | + } | ||
115 | + } | ||
116 | + | ||
117 | + &-horizontal &-submenu .@{select-dropdown-prefix-cls} { | ||
118 | + width: 100%; | ||
119 | + .@{menu-prefix-cls}-item{ | ||
120 | + height: auto; | ||
121 | + line-height: normal; | ||
122 | + border-bottom: 0; | ||
123 | + float: none; | ||
124 | + } | ||
125 | + } | ||
126 | + | ||
127 | + &-item-group{ | ||
128 | + line-height: normal; | ||
129 | + &-title { | ||
130 | + padding-left: 8px; | ||
131 | + font-size: 12px; | ||
132 | + color: @legend-color; | ||
133 | + height: 30px; | ||
134 | + line-height: 30px; | ||
135 | + } | ||
136 | + } | ||
137 | +} | ||
138 | +.select-item(@menu-prefix-cls, @menu-dropdown-item-prefix-cls); | ||
0 | \ No newline at end of file | 139 | \ No newline at end of file |
test/app.vue
@@ -44,6 +44,7 @@ li + li { | @@ -44,6 +44,7 @@ li + li { | ||
44 | <li><a v-link="'/table'">Table</a></li> | 44 | <li><a v-link="'/table'">Table</a></li> |
45 | <li><a v-link="'/dropdown'">Dropdown</a></li> | 45 | <li><a v-link="'/dropdown'">Dropdown</a></li> |
46 | <li><a v-link="'/tabs'">Tabs</a></li> | 46 | <li><a v-link="'/tabs'">Tabs</a></li> |
47 | + <li><a v-link="'/menu'">Menu</a></li> | ||
47 | </ul> | 48 | </ul> |
48 | </nav> | 49 | </nav> |
49 | <router-view></router-view> | 50 | <router-view></router-view> |
test/main.js
@@ -117,6 +117,11 @@ router.map({ | @@ -117,6 +117,11 @@ router.map({ | ||
117 | component: function (resolve) { | 117 | component: function (resolve) { |
118 | require(['./routers/tabs.vue'], resolve); | 118 | require(['./routers/tabs.vue'], resolve); |
119 | } | 119 | } |
120 | + }, | ||
121 | + '/menu': { | ||
122 | + component: function (resolve) { | ||
123 | + require(['./routers/menu.vue'], resolve); | ||
124 | + } | ||
120 | } | 125 | } |
121 | }); | 126 | }); |
122 | 127 |
1 | +<template> | ||
2 | + <div> | ||
3 | + <i-button @click="toggleMode">调整方向</i-button> | ||
4 | + <Menu :mode="mode" active-key="1"> | ||
5 | + <Menu-item key="1"> | ||
6 | + <Icon type="ionic"></Icon> | ||
7 | + <span>导航一</span> | ||
8 | + </Menu-item> | ||
9 | + <Menu-item key="2">导航二</Menu-item> | ||
10 | + <Submenu key="3"> | ||
11 | + <span slot="title">导航三</span> | ||
12 | + <Menu-group title="集合1"> | ||
13 | + <Menu-item key="3-1">导航三 - 一</Menu-item> | ||
14 | + <Menu-item key="3-2">导航三 - 二</Menu-item> | ||
15 | + </Menu-group> | ||
16 | + <Menu-group title="集合2"> | ||
17 | + <Menu-item key="3-3">导航三 - 三</Menu-item> | ||
18 | + <Menu-item key="3-4">导航三 - 四</Menu-item> | ||
19 | + </Menu-group> | ||
20 | + </Submenu> | ||
21 | + <Menu-item key="4">导航四</Menu-item> | ||
22 | + </Menu> | ||
23 | + <br><br> | ||
24 | + <!--<Menu mode="horizontal" theme="dark" active-key="1">--> | ||
25 | + <!--<Menu-item key="1">--> | ||
26 | + <!--<Icon type="ionic"></Icon>--> | ||
27 | + <!--<span>导航一</span>--> | ||
28 | + <!--</Menu-item>--> | ||
29 | + <!--<Menu-item key="2">导航二</Menu-item>--> | ||
30 | + <!--<Submenu key="3">--> | ||
31 | + <!--<span slot="title">导航三</span>--> | ||
32 | + <!--<Menu-item key="3-1">导航三 - 一</Menu-item>--> | ||
33 | + <!--<Menu-item key="3-2">导航三 - 二</Menu-item>--> | ||
34 | + <!--<Menu-item key="3-3">导航三 - 三</Menu-item>--> | ||
35 | + <!--</Submenu>--> | ||
36 | + <!--<Menu-item key="4">导航四</Menu-item>--> | ||
37 | + <!--</Menu>--> | ||
38 | + <!--<br><br>--> | ||
39 | + <!--<Menu mode="horizontal" theme="primary" active-key="1">--> | ||
40 | + <!--<Menu-item key="1">--> | ||
41 | + <!--<Icon type="ionic"></Icon>--> | ||
42 | + <!--<span>导航一</span>--> | ||
43 | + <!--</Menu-item>--> | ||
44 | + <!--<Menu-item key="2">导航二</Menu-item>--> | ||
45 | + <!--<Submenu key="3">--> | ||
46 | + <!--<span slot="title">导航三</span>--> | ||
47 | + <!--<Menu-item key="3-1">导航三 - 一</Menu-item>--> | ||
48 | + <!--<Menu-item key="3-2">导航三 - 二</Menu-item>--> | ||
49 | + <!--<Menu-item key="3-3">导航三 - 三</Menu-item>--> | ||
50 | + <!--</Submenu>--> | ||
51 | + <!--<Menu-item key="4">导航四</Menu-item>--> | ||
52 | + <!--</Menu>--> | ||
53 | + </div> | ||
54 | +</template> | ||
55 | +<script> | ||
56 | + export default { | ||
57 | + props: {}, | ||
58 | + data () { | ||
59 | + return { | ||
60 | + mode: 'horizontal' | ||
61 | + } | ||
62 | + }, | ||
63 | + computed: {}, | ||
64 | + methods: { | ||
65 | + toggleMode () { | ||
66 | + this.mode = this.mode === 'horizontal' ? 'vertical' : 'horizontal'; | ||
67 | + } | ||
68 | + } | ||
69 | + } | ||
70 | +</script> | ||
0 | \ No newline at end of file | 71 | \ No newline at end of file |
test/routers/select.vue
1 | <template> | 1 | <template> |
2 | - <Row style="margin: 100px"> | ||
3 | - <i-col span="12" style="padding-right:10px"> | ||
4 | - <i-select :model.sync="model11" filterable> | ||
5 | - <i-option v-for="item in cityList" :value="item.value">{{ item.label }}</i-option> | ||
6 | - </i-select> | ||
7 | - </i-col> | ||
8 | - <i-col span="12"> | ||
9 | - <i-select :model.sync="model12" filterable multiple> | ||
10 | - <i-option v-for="item in cityList" :value="item.value">{{ item.label }}</i-option> | ||
11 | - </i-select> | ||
12 | - </i-col> | ||
13 | - </Row> | 2 | + <i-button @click="model8 = ''">clear</i-button> |
3 | + <i-select :model.sync="model8" clearable style="width:200px"> | ||
4 | + <i-option v-for="item in cityList" :value="item.value">{{ item.label }}</i-option> | ||
5 | + </i-select> | ||
14 | </template> | 6 | </template> |
15 | <script> | 7 | <script> |
16 | export default { | 8 | export default { |
@@ -42,8 +34,7 @@ | @@ -42,8 +34,7 @@ | ||
42 | label: '重庆市' | 34 | label: '重庆市' |
43 | } | 35 | } |
44 | ], | 36 | ], |
45 | - model11: '', | ||
46 | - model12: [] | 37 | + model8: '' |
47 | } | 38 | } |
48 | } | 39 | } |
49 | } | 40 | } |