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 | 4 | v-clickoutside="handleClose" |
5 | 5 | @mouseenter="handleMouseenter" |
6 | 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 | 8 | <Drop v-show="visible" :placement="placement" :transition="transition" v-ref:drop><slot name="list"></slot></Drop> |
12 | 9 | </div> |
13 | 10 | </template> | ... | ... |
src/components/menu/menu-group.vue
1 | 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 | 6 | </template> |
4 | 7 | <script> |
8 | + const prefixCls = 'ivu-menu'; | |
9 | + | |
5 | 10 | export default { |
6 | - props: {}, | |
11 | + name: 'MenuGroup', | |
12 | + props: { | |
13 | + title: { | |
14 | + type: String, | |
15 | + default: '' | |
16 | + } | |
17 | + }, | |
7 | 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 | 30 | </script> |
14 | 31 | \ No newline at end of file | ... | ... |
src/components/menu/menu-item.vue
1 | 1 | <template> |
2 | - | |
2 | + <li :class="classes" @click.stop="handleClick"><slot></slot></li> | |
3 | 3 | </template> |
4 | 4 | <script> |
5 | + const prefixCls = 'ivu-menu'; | |
6 | + | |
5 | 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 | 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 | 42 | </script> |
14 | 43 | \ No newline at end of file | ... | ... |
src/components/menu/menu.vue
1 | 1 | <template> |
2 | - | |
2 | + <ul :class="classes"><slot></slot></ul> | |
3 | 3 | </template> |
4 | 4 | <script> |
5 | + import { oneOf } from '../../utils/assist'; | |
6 | + | |
7 | + const prefixCls = 'ivu-menu'; | |
8 | + | |
5 | 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 | 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 | 93 | </script> |
14 | 94 | \ No newline at end of file | ... | ... |
src/components/menu/submenu.vue
1 | 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 | 10 | </template> |
4 | 11 | <script> |
12 | + import Drop from '../select/dropdown.vue'; | |
13 | + import Icon from '../icon/icon.vue'; | |
14 | + const prefixCls = 'ivu-menu'; | |
15 | + | |
5 | 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 | 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 | 84 | </script> |
14 | 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 | 139 | \ No newline at end of file | ... | ... |
test/app.vue
test/main.js
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 | 71 | \ No newline at end of file | ... | ... |
test/routers/select.vue
1 | 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 | 6 | </template> |
15 | 7 | <script> |
16 | 8 | export default { |
... | ... | @@ -42,8 +34,7 @@ |
42 | 34 | label: '重庆市' |
43 | 35 | } |
44 | 36 | ], |
45 | - model11: '', | |
46 | - model12: [] | |
37 | + model8: '' | |
47 | 38 | } |
48 | 39 | } |
49 | 40 | } | ... | ... |