Commit 7ec0b5330b4893d32d949532b1ad02c506c05e4e
1 parent
7959adf7
Cascader support search
Showing
3 changed files
with
82 additions
and
19 deletions
Show diff stats
examples/routers/cascader.vue
... | ... | @@ -2,8 +2,9 @@ |
2 | 2 | <Row> |
3 | 3 | <i-col span="4"> |
4 | 4 | <Button @click="handleLoad">load</Button> |
5 | + {{ v1 }} | |
5 | 6 | </i-col> |
6 | - <i-col span="6"> | |
7 | + <i-col span="4"> | |
7 | 8 | <Cascader :data="data3" filterable v-model="v1"></Cascader> |
8 | 9 | <!--<Cascader :data="data2" filterable v-model="v1" :loadData="loadData"></Cascader>--> |
9 | 10 | </i-col> |
... | ... | @@ -14,23 +15,26 @@ |
14 | 15 | data () { |
15 | 16 | return { |
16 | 17 | v1: [], |
17 | - data2: [{ | |
18 | - value: 'zhejiang', | |
19 | - label: '浙江', | |
20 | - children: [], | |
21 | - loading: false | |
22 | - }, { | |
23 | - value: 'jiangsu', | |
24 | - label: '江苏', | |
25 | - children: [{ | |
26 | - value: 'nanjing', | |
27 | - label: '南京', | |
18 | + data2: [ | |
19 | + { | |
20 | + value: 'zhejiang', | |
21 | + label: '浙江', | |
22 | + children: [], | |
23 | + loading: false | |
24 | + }, | |
25 | + { | |
26 | + value: 'jiangsu', | |
27 | + label: '江苏', | |
28 | 28 | children: [{ |
29 | - value: 'zhonghuamen', | |
30 | - label: '中华门' | |
29 | + value: 'nanjing', | |
30 | + label: '南京', | |
31 | + children: [{ | |
32 | + value: 'zhonghuamen', | |
33 | + label: '中华门' | |
34 | + }] | |
31 | 35 | }] |
32 | - }] | |
33 | - }], | |
36 | + } | |
37 | + ], | |
34 | 38 | data3: [{ |
35 | 39 | value: 'beijing', |
36 | 40 | label: '北京', |
... | ... | @@ -67,6 +71,7 @@ |
67 | 71 | label: '苏州', |
68 | 72 | children: [ |
69 | 73 | { |
74 | + disabled: true, | |
70 | 75 | value: 'zhuozhengyuan', |
71 | 76 | label: '拙政园', |
72 | 77 | }, | ... | ... |
src/components/cascader/cascader.vue
... | ... | @@ -5,7 +5,8 @@ |
5 | 5 | <i-input |
6 | 6 | :readonly="!filterable" |
7 | 7 | :disabled="disabled" |
8 | - v-model="displayRender" | |
8 | + :value="displayRender" | |
9 | + @on-change="handleInput" | |
9 | 10 | :size="size" |
10 | 11 | :placeholder="placeholder"></i-input> |
11 | 12 | <Icon type="ios-close" :class="[prefixCls + '-arrow']" v-show="showCloseIcon" @click.native.stop="clearSelect"></Icon> |
... | ... | @@ -16,12 +17,23 @@ |
16 | 17 | <Drop v-show="visible"> |
17 | 18 | <div> |
18 | 19 | <Caspanel |
20 | + v-show="!filterable || (filterable && query === '')" | |
19 | 21 | ref="caspanel" |
20 | 22 | :prefix-cls="prefixCls" |
21 | 23 | :data="data" |
22 | 24 | :disabled="disabled" |
23 | 25 | :change-on-select="changeOnSelect" |
24 | 26 | :trigger="trigger"></Caspanel> |
27 | + <div :class="[prefixCls + '-dropdown']" v-show="filterable && query !== ''"> | |
28 | + <ul :class="[selectPrefixCls + '-dropdown-list']"> | |
29 | + <li | |
30 | + :class="[selectPrefixCls + '-item', { | |
31 | + [selectPrefixCls + '-item-disabled']: item.disabled | |
32 | + }]" | |
33 | + v-for="(item, index) in querySelections" | |
34 | + @click="handleSelectItem(index)">{{ item.label }}</li> | |
35 | + </ul> | |
36 | + </div> | |
25 | 37 | </div> |
26 | 38 | </Drop> |
27 | 39 | </transition> |
... | ... | @@ -37,6 +49,7 @@ |
37 | 49 | import Emitter from '../../mixins/emitter'; |
38 | 50 | |
39 | 51 | const prefixCls = 'ivu-cascader'; |
52 | + const selectPrefixCls = 'ivu-select'; | |
40 | 53 | |
41 | 54 | export default { |
42 | 55 | name: 'Cascader', |
... | ... | @@ -100,11 +113,13 @@ |
100 | 113 | data () { |
101 | 114 | return { |
102 | 115 | prefixCls: prefixCls, |
116 | + selectPrefixCls: selectPrefixCls, | |
103 | 117 | visible: false, |
104 | 118 | selected: [], |
105 | 119 | tmpSelected: [], |
106 | 120 | updatingValue: false, // to fix set value in changeOnSelect type |
107 | - currentValue: this.value | |
121 | + currentValue: this.value, | |
122 | + query: '' | |
108 | 123 | }; |
109 | 124 | }, |
110 | 125 | computed: { |
... | ... | @@ -128,6 +143,32 @@ |
128 | 143 | } |
129 | 144 | |
130 | 145 | return this.renderFormat(label, this.selected); |
146 | + }, | |
147 | + querySelections () { | |
148 | + let selections = []; | |
149 | + function getSelections (arr, label, value) { | |
150 | + for (let i = 0; i < arr.length; i++) { | |
151 | + let item = arr[i]; | |
152 | + item.__label = label ? label + ' / ' + item.label : item.label; | |
153 | + item.__value = value ? value + ',' + item.value : item.value; | |
154 | + | |
155 | + if (item.children && item.children.length) { | |
156 | + getSelections(item.children, item.__label, item.__value); | |
157 | + delete item.__label; | |
158 | + delete item.__value; | |
159 | + } else { | |
160 | + selections.push({ | |
161 | + label: item.__label, | |
162 | + value: item.__value, | |
163 | + item: item, | |
164 | + disabled: !!item.disabled | |
165 | + }); | |
166 | + } | |
167 | + } | |
168 | + } | |
169 | + getSelections(this.data); | |
170 | + selections = selections.filter(item => item.label.indexOf(this.query) > -1); | |
171 | + return selections; | |
131 | 172 | } |
132 | 173 | }, |
133 | 174 | methods: { |
... | ... | @@ -146,7 +187,7 @@ |
146 | 187 | toggleOpen () { |
147 | 188 | if (this.disabled) return false; |
148 | 189 | if (this.visible) { |
149 | - this.handleClose(); | |
190 | + if (!this.filterable) this.handleClose(); | |
150 | 191 | } else { |
151 | 192 | this.onFocus(); |
152 | 193 | } |
... | ... | @@ -177,6 +218,19 @@ |
177 | 218 | }); |
178 | 219 | }); |
179 | 220 | } |
221 | + }, | |
222 | + handleInput (event) { | |
223 | + this.query = event.target.value; | |
224 | + }, | |
225 | + handleSelectItem (index) { | |
226 | + const item = this.querySelections[index]; | |
227 | + | |
228 | + if (item.item.disabled) return false; | |
229 | + this.query = ''; | |
230 | + const oldVal = JSON.stringify(this.currentValue); | |
231 | + this.currentValue = item.value.split(','); | |
232 | + this.emitValue(this.currentValue, oldVal); | |
233 | + this.handleClose(); | |
180 | 234 | } |
181 | 235 | }, |
182 | 236 | created () { | ... | ... |
src/styles/components/cascader.less