Commit c463ab87579ca750bc91fb6873f8f0730295bc5d
1 parent
6ff31952
add Cascader
add Cascader
Showing
9 changed files
with
345 additions
and
60 deletions
Show diff stats
src/components/cascader/cascader.vue
1 | <template> | 1 | <template> |
2 | - <div :class="[prefixCls]"> | 2 | + <div :class="classes" v-clickoutside="handleClose"> |
3 | <i-input | 3 | <i-input |
4 | readonly | 4 | readonly |
5 | :disabled="disabled" | 5 | :disabled="disabled" |
6 | :value.sync="displayRender" | 6 | :value.sync="displayRender" |
7 | :size="size" | 7 | :size="size" |
8 | - :placeholder="placeholder"></i-input> | 8 | + :placeholder="placeholder" |
9 | + @on-focus="onFocus"></i-input> | ||
10 | + <Icon type="ios-close" :class="[prefixCls + '-arrow']" v-show="showCloseIcon" @click.stop="clearSelect"></Icon> | ||
11 | + <Icon type="arrow-down-b" :class="[prefixCls + '-arrow']"></Icon> | ||
12 | + <Dropdown v-show="visible" transition="slide-up"> | ||
13 | + <div> | ||
14 | + <Caspanel | ||
15 | + :prefix-cls="prefixCls" | ||
16 | + :data.sync="data" | ||
17 | + :disabled="disabled" | ||
18 | + :trigger="trigger" | ||
19 | + @on-update-result="updateResult"></Caspanel> | ||
20 | + </div> | ||
21 | + </Dropdown> | ||
9 | </div> | 22 | </div> |
10 | </template> | 23 | </template> |
11 | <script> | 24 | <script> |
12 | import iInput from '../input/input.vue'; | 25 | import iInput from '../input/input.vue'; |
13 | import Dropdown from '../select/dropdown.vue'; | 26 | import Dropdown from '../select/dropdown.vue'; |
27 | + import Icon from '../icon/icon.vue'; | ||
28 | + import Caspanel from './caspanel.vue'; | ||
14 | import clickoutside from '../../directives/clickoutside'; | 29 | import clickoutside from '../../directives/clickoutside'; |
15 | - import { oneOf, MutationObserver } from '../../utils/assist'; | 30 | + import { oneOf } from '../../utils/assist'; |
16 | 31 | ||
17 | const prefixCls = 'ivu-cascader'; | 32 | const prefixCls = 'ivu-cascader'; |
18 | 33 | ||
19 | export default { | 34 | export default { |
35 | + components: { iInput, Dropdown, Icon, Caspanel }, | ||
36 | + directives: { clickoutside }, | ||
20 | props: { | 37 | props: { |
21 | data: { | 38 | data: { |
22 | type: Array, | 39 | type: Array, |
@@ -33,7 +50,7 @@ | @@ -33,7 +50,7 @@ | ||
33 | }, | 50 | }, |
34 | clearable: { | 51 | clearable: { |
35 | type: Boolean, | 52 | type: Boolean, |
36 | - default: false | 53 | + default: true |
37 | }, | 54 | }, |
38 | placeholder: { | 55 | placeholder: { |
39 | type: String, | 56 | type: String, |
@@ -56,18 +73,31 @@ | @@ -56,18 +73,31 @@ | ||
56 | }, | 73 | }, |
57 | renderFormat: { | 74 | renderFormat: { |
58 | type: Function, | 75 | type: Function, |
59 | - default: (label, selectedData) => { | ||
60 | - label.join('/'); | 76 | + default (label, selectedData) { |
77 | + return label.join('/'); | ||
61 | } | 78 | } |
62 | } | 79 | } |
63 | }, | 80 | }, |
64 | data () { | 81 | data () { |
65 | return { | 82 | return { |
66 | prefixCls: prefixCls, | 83 | prefixCls: prefixCls, |
67 | - selected: [] | 84 | + visible: false, |
85 | + selected: [], | ||
86 | + tmpSelected: [] | ||
68 | } | 87 | } |
69 | }, | 88 | }, |
70 | computed: { | 89 | computed: { |
90 | + classes () { | ||
91 | + return [ | ||
92 | + `${prefixCls}`, | ||
93 | + { | ||
94 | + [`${prefixCls}-show-clear`]: this.showCloseIcon | ||
95 | + } | ||
96 | + ] | ||
97 | + }, | ||
98 | + showCloseIcon () { | ||
99 | + return this.value && this.value.length && this.clearable; | ||
100 | + }, | ||
71 | displayRender () { | 101 | displayRender () { |
72 | let label = []; | 102 | let label = []; |
73 | for (let i = 0; i < this.selected.length; i++) { | 103 | for (let i = 0; i < this.selected.length; i++) { |
@@ -78,7 +108,22 @@ | @@ -78,7 +108,22 @@ | ||
78 | } | 108 | } |
79 | }, | 109 | }, |
80 | methods: { | 110 | methods: { |
81 | - | 111 | + clearSelect () { |
112 | + | ||
113 | + }, | ||
114 | + handleClose () { | ||
115 | + this.visible = false; | ||
116 | + }, | ||
117 | + onFocus () { | ||
118 | + this.visible = true; | ||
119 | + }, | ||
120 | + updateResult (result) { | ||
121 | + console.log(JSON.stringify(result)) | ||
122 | + this.selected = result; | ||
123 | + } | ||
124 | + }, | ||
125 | + ready () { | ||
126 | + | ||
82 | } | 127 | } |
83 | } | 128 | } |
84 | </script> | 129 | </script> |
85 | \ No newline at end of file | 130 | \ No newline at end of file |
1 | +<template> | ||
2 | + <li :class="classes">{{ data.label }}<i v-if="data.children && data.children.length" class="ivu-icon ivu-icon-ios-arrow-right"></i></li> | ||
3 | +</template> | ||
4 | +<script> | ||
5 | + export default { | ||
6 | + props: { | ||
7 | + data: Object, | ||
8 | + prefixCls: String, | ||
9 | + tmpItem: Object | ||
10 | + }, | ||
11 | + computed: { | ||
12 | + classes () { | ||
13 | + return [ | ||
14 | + `${this.prefixCls}-menu-item`, | ||
15 | + { | ||
16 | + [`${this.prefixCls}-menu-item-active`]: this.tmpItem.value === this.data.value | ||
17 | + } | ||
18 | + ] | ||
19 | + } | ||
20 | + }, | ||
21 | + ready () { | ||
22 | + | ||
23 | + } | ||
24 | + } | ||
25 | +</script> | ||
0 | \ No newline at end of file | 26 | \ No newline at end of file |
1 | +<template> | ||
2 | + <ul v-if="data && data.length" :class="[prefixCls + '-menu']"> | ||
3 | + <Casitem | ||
4 | + v-for="item in data" | ||
5 | + :prefix-cls="prefixCls" | ||
6 | + :data.sync="item" | ||
7 | + :tmp-item="tmpItem" | ||
8 | + @click.stop="handleClickItem(item)" | ||
9 | + @mouseenter.stop="handleHoverItem(item)"></Casitem> | ||
10 | + </ul><Caspanel v-if="sublist && sublist.length" :prefix-cls="prefixCls" :data.sync="sublist" :disabled="disabled" :trigger="trigger" @on-update-result="updateResult"></Caspanel> | ||
11 | +</template> | ||
12 | +<script> | ||
13 | + import Casitem from './casitem.vue'; | ||
14 | + import { oneOf } from '../../utils/assist'; | ||
15 | + | ||
16 | + export default { | ||
17 | + name: 'Caspanel', | ||
18 | + components: { Casitem }, | ||
19 | + props: { | ||
20 | + data: { | ||
21 | + type: Array, | ||
22 | + default () { | ||
23 | + return [] | ||
24 | + } | ||
25 | + }, | ||
26 | + sublist: { | ||
27 | + type: Array, | ||
28 | + default () { | ||
29 | + return [] | ||
30 | + } | ||
31 | + }, | ||
32 | + disabled: Boolean, | ||
33 | + changeOnSelect: Boolean, | ||
34 | + trigger: { | ||
35 | + validator (value) { | ||
36 | + return oneOf(value, ['click', 'hover']); | ||
37 | + } | ||
38 | + }, | ||
39 | + prefixCls: String | ||
40 | + }, | ||
41 | + data () { | ||
42 | + return { | ||
43 | + tmpItem: {}, | ||
44 | + result: [] | ||
45 | + } | ||
46 | + }, | ||
47 | + methods: { | ||
48 | + handleClickItem (item) { | ||
49 | + if (this.trigger !== 'click') return; | ||
50 | + this.handleTriggerItem(item); | ||
51 | + }, | ||
52 | + handleHoverItem (item) { | ||
53 | + if (this.trigger !== 'hover') return; | ||
54 | + this.handleTriggerItem(item); | ||
55 | + }, | ||
56 | + handleTriggerItem (item) { | ||
57 | + if (item.disabled) return; | ||
58 | + | ||
59 | + if (item.children && item.children.length){ | ||
60 | + this.sublist = item.children; | ||
61 | + // todo 实时选择 | ||
62 | + } else { | ||
63 | + this.sublist = []; | ||
64 | + // todo 选择 | ||
65 | + } | ||
66 | + | ||
67 | + // return value back | ||
68 | + const backItem = this.getBaseItem(item); | ||
69 | + | ||
70 | + this.tmpItem = backItem; | ||
71 | + this.$emit('on-update-result', [backItem]); | ||
72 | + }, | ||
73 | + updateResult (item) { | ||
74 | + this.result = [this.tmpItem].concat(item); | ||
75 | + this.$emit('on-update-result', this.result); | ||
76 | + }, | ||
77 | + getBaseItem (item) { | ||
78 | + let backItem = Object.assign({}, item); | ||
79 | + if (backItem.children) { | ||
80 | + delete backItem.children; | ||
81 | + } | ||
82 | + | ||
83 | + return backItem; | ||
84 | + } | ||
85 | + }, | ||
86 | + watch: { | ||
87 | + data () { | ||
88 | + this.sublist = []; | ||
89 | + } | ||
90 | + }, | ||
91 | + ready () { | ||
92 | + // todo 初始化时,判断预设的值 | ||
93 | + } | ||
94 | + } | ||
95 | +</script> | ||
0 | \ No newline at end of file | 96 | \ No newline at end of file |
1 | +@cascader-prefix-cls: ~"@{css-prefix}cascader"; | ||
2 | +@cascader-item-prefix-cls: ~"@{css-prefix}cascader-menu-item"; | ||
3 | + | ||
4 | +.@{cascader-prefix-cls} { | ||
5 | + position: relative; | ||
6 | + | ||
7 | + .@{css-prefix}input{ | ||
8 | + display: block; | ||
9 | + cursor: pointer; | ||
10 | + } | ||
11 | + | ||
12 | + .@{cascader-prefix-cls}-arrow:nth-of-type(1) { | ||
13 | + display: none; | ||
14 | + cursor: pointer; | ||
15 | + } | ||
16 | + | ||
17 | + &:hover { | ||
18 | + .@{cascader-prefix-cls}-arrow:nth-of-type(1) { | ||
19 | + display: inline-block; | ||
20 | + } | ||
21 | + } | ||
22 | + &-show-clear:hover .@{cascader-prefix-cls}-arrow:nth-of-type(2){ | ||
23 | + display: none; | ||
24 | + } | ||
25 | + | ||
26 | + &-arrow { | ||
27 | + position: absolute; | ||
28 | + top: 50%; | ||
29 | + right: 8px; | ||
30 | + line-height: 1; | ||
31 | + margin-top: -6px; | ||
32 | + font-size: @font-size-base; | ||
33 | + color: @subsidiary-color; | ||
34 | + .transition(all @transition-time @ease-in-out); | ||
35 | + } | ||
36 | + | ||
37 | + .@{select-dropdown-prefix-cls} { | ||
38 | + padding: 0; | ||
39 | + white-space: nowrap; | ||
40 | + } | ||
41 | + | ||
42 | + .select-item(@cascader-prefix-cls, @cascader-item-prefix-cls); | ||
43 | + | ||
44 | + &-menu{ | ||
45 | + display: inline-block; | ||
46 | + min-width: 100px; | ||
47 | + height: 180px; | ||
48 | + margin: 0; | ||
49 | + padding: 5px 0; | ||
50 | + vertical-align: top; | ||
51 | + list-style: none; | ||
52 | + border-right: 1px solid @border-color-split; | ||
53 | + overflow: auto; | ||
54 | + | ||
55 | + &:first-child { | ||
56 | + | ||
57 | + } | ||
58 | + &:last-child { | ||
59 | + border-right-color: transparent; | ||
60 | + margin-right: -1px; | ||
61 | + } | ||
62 | + &:only-child { | ||
63 | + | ||
64 | + } | ||
65 | + | ||
66 | + & &-item{ | ||
67 | + position: relative; | ||
68 | + padding-right: 24px; | ||
69 | + .transition(all @transition-time @ease-in-out); | ||
70 | + | ||
71 | + i{ | ||
72 | + font-size: @font-size-small; | ||
73 | + position: absolute; | ||
74 | + right: 15px; | ||
75 | + top: 50%; | ||
76 | + margin-top: -6px; | ||
77 | + } | ||
78 | + | ||
79 | + &-active{ | ||
80 | + background-color: @background-color-select-hover; | ||
81 | + font-weight: bold; | ||
82 | + } | ||
83 | + } | ||
84 | + } | ||
85 | +} | ||
0 | \ No newline at end of file | 86 | \ No newline at end of file |
src/styles/components/index.less
@@ -25,4 +25,5 @@ | @@ -25,4 +25,5 @@ | ||
25 | @import "tooltip"; | 25 | @import "tooltip"; |
26 | @import "poptip"; | 26 | @import "poptip"; |
27 | @import "input"; | 27 | @import "input"; |
28 | -@import "slider"; | ||
29 | \ No newline at end of file | 28 | \ No newline at end of file |
29 | +@import "slider"; | ||
30 | +@import "cascader"; | ||
30 | \ No newline at end of file | 31 | \ No newline at end of file |
src/styles/components/select.less
@@ -183,50 +183,7 @@ | @@ -183,50 +183,7 @@ | ||
183 | } | 183 | } |
184 | } | 184 | } |
185 | 185 | ||
186 | -.@{select-item-prefix-cls} { | ||
187 | - margin: 0; | ||
188 | - padding: 7px 16px; | ||
189 | - clear: both; | ||
190 | - color: @text-color; | ||
191 | - font-size: @font-size-small !important; | ||
192 | - //border-radius: @btn-border-radius-small; | ||
193 | - white-space: nowrap; | ||
194 | - cursor: pointer; | ||
195 | - .transition(background @transition-time @ease-in-out); | ||
196 | - | ||
197 | - &:hover{ | ||
198 | - background: @background-color-select-hover; | ||
199 | - } | ||
200 | - | ||
201 | - &-focus { | ||
202 | - background: @background-color-select-hover; | ||
203 | - } | ||
204 | - | ||
205 | - &-disabled { | ||
206 | - color: @btn-disable-color; | ||
207 | - cursor: @cursor-disabled; | ||
208 | - | ||
209 | - &:hover { | ||
210 | - color: @btn-disable-color; | ||
211 | - background-color: #fff; | ||
212 | - cursor: @cursor-disabled; | ||
213 | - } | ||
214 | - } | ||
215 | - | ||
216 | - &-selected ,&-selected:hover{ | ||
217 | - color: #fff; | ||
218 | - background: @selected-color; | ||
219 | - } | ||
220 | - | ||
221 | - &-selected&-focus { | ||
222 | - background: shade(@selected-color, 10%); | ||
223 | - } | ||
224 | -} | ||
225 | - | ||
226 | -.@{select-prefix-cls}-large .@{select-item-prefix-cls}{ | ||
227 | - padding: 7px 16px 8px; | ||
228 | - font-size: @font-size-base !important; | ||
229 | -} | 186 | +.select-item(@select-prefix-cls, @select-item-prefix-cls); |
230 | 187 | ||
231 | .@{select-prefix-cls}-multiple .@{select-item-prefix-cls} { | 188 | .@{select-prefix-cls}-multiple .@{select-item-prefix-cls} { |
232 | &-selected{ | 189 | &-selected{ |
src/styles/mixins/index.less
@@ -14,4 +14,5 @@ | @@ -14,4 +14,5 @@ | ||
14 | @import "breadcrumb"; | 14 | @import "breadcrumb"; |
15 | @import "mask"; | 15 | @import "mask"; |
16 | @import "content"; // card、modal | 16 | @import "content"; // card、modal |
17 | -@import "tooltip"; | ||
18 | \ No newline at end of file | 17 | \ No newline at end of file |
18 | +@import "tooltip"; | ||
19 | +@import "select"; | ||
19 | \ No newline at end of file | 20 | \ No newline at end of file |
1 | +.select-item(@size-class, @item-class) { | ||
2 | + .@{item-class} { | ||
3 | + margin: 0; | ||
4 | + padding: 7px 16px; | ||
5 | + clear: both; | ||
6 | + color: @text-color; | ||
7 | + font-size: @font-size-small !important; | ||
8 | + white-space: nowrap; | ||
9 | + list-style: none; | ||
10 | + cursor: pointer; | ||
11 | + .transition(background @transition-time @ease-in-out); | ||
12 | + | ||
13 | + &:hover{ | ||
14 | + background: @background-color-select-hover; | ||
15 | + } | ||
16 | + | ||
17 | + &-focus { | ||
18 | + background: @background-color-select-hover; | ||
19 | + } | ||
20 | + | ||
21 | + &-disabled { | ||
22 | + color: @btn-disable-color; | ||
23 | + cursor: @cursor-disabled; | ||
24 | + | ||
25 | + &:hover { | ||
26 | + color: @btn-disable-color; | ||
27 | + background-color: #fff; | ||
28 | + cursor: @cursor-disabled; | ||
29 | + } | ||
30 | + } | ||
31 | + | ||
32 | + &-selected ,&-selected:hover{ | ||
33 | + color: #fff; | ||
34 | + background: @selected-color; | ||
35 | + } | ||
36 | + | ||
37 | + &-selected&-focus { | ||
38 | + background: shade(@selected-color, 10%); | ||
39 | + } | ||
40 | + } | ||
41 | + | ||
42 | + .@{size-class}-large .@{item-class} { | ||
43 | + padding: 7px 16px 8px; | ||
44 | + font-size: @font-size-base !important; | ||
45 | + } | ||
46 | +} | ||
0 | \ No newline at end of file | 47 | \ No newline at end of file |
test/routers/cascader.vue
1 | <template> | 1 | <template> |
2 | - <div style="margin: 150px;width:300px"> | ||
3 | - <Cascader></Cascader> | 2 | + <div style="margin: 50px;width:300px"> |
3 | + <Cascader :data="data" :value="value"></Cascader> | ||
4 | </div> | 4 | </div> |
5 | </template> | 5 | </template> |
6 | <script> | 6 | <script> |
7 | import { Cascader } from 'iview'; | 7 | import { Cascader } from 'iview'; |
8 | export default { | 8 | export default { |
9 | props: { | 9 | props: { |
10 | - | 10 | + |
11 | }, | 11 | }, |
12 | data () { | 12 | data () { |
13 | return { | 13 | return { |
14 | - | 14 | + value: [], |
15 | + data: [{ | ||
16 | + value: 'zhejiang', | ||
17 | + label: 'Zhejiang', | ||
18 | + children: [{ | ||
19 | + value: 'hangzhou', | ||
20 | + label: 'Hangzhou' | ||
21 | + }], | ||
22 | + }, { | ||
23 | + value: 'jiangsu', | ||
24 | + label: 'Jiangsu', | ||
25 | + children: [{ | ||
26 | + value: 'nanjing', | ||
27 | + label: 'Nanjing', | ||
28 | + children: [{ | ||
29 | + value: 'zhonghuamen', | ||
30 | + label: 'Zhong Hua Men', | ||
31 | + children: [{ | ||
32 | + value: 'abc', | ||
33 | + label: 'ABC' | ||
34 | + }] | ||
35 | + }] | ||
36 | + }, { | ||
37 | + value: 'hhh', | ||
38 | + label: 'HHH', | ||
39 | + children: [{ | ||
40 | + value: 'ddd', | ||
41 | + label: 'DDD' | ||
42 | + }] | ||
43 | + }] | ||
44 | + }] | ||
15 | } | 45 | } |
16 | }, | 46 | }, |
17 | computed: { | 47 | computed: { |
18 | - | 48 | + |
19 | }, | 49 | }, |
20 | methods: { | 50 | methods: { |
21 | - | 51 | + |
22 | } | 52 | } |
23 | } | 53 | } |
24 | </script> | 54 | </script> |
25 | \ No newline at end of file | 55 | \ No newline at end of file |