Commit 7ec0b5330b4893d32d949532b1ad02c506c05e4e

Authored by 梁灏
1 parent 7959adf7

Cascader support search

examples/routers/cascader.vue
@@ -2,8 +2,9 @@ @@ -2,8 +2,9 @@
2 <Row> 2 <Row>
3 <i-col span="4"> 3 <i-col span="4">
4 <Button @click="handleLoad">load</Button> 4 <Button @click="handleLoad">load</Button>
  5 + {{ v1 }}
5 </i-col> 6 </i-col>
6 - <i-col span="6"> 7 + <i-col span="4">
7 <Cascader :data="data3" filterable v-model="v1"></Cascader> 8 <Cascader :data="data3" filterable v-model="v1"></Cascader>
8 <!--<Cascader :data="data2" filterable v-model="v1" :loadData="loadData"></Cascader>--> 9 <!--<Cascader :data="data2" filterable v-model="v1" :loadData="loadData"></Cascader>-->
9 </i-col> 10 </i-col>
@@ -14,23 +15,26 @@ @@ -14,23 +15,26 @@
14 data () { 15 data () {
15 return { 16 return {
16 v1: [], 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 children: [{ 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 data3: [{ 38 data3: [{
35 value: 'beijing', 39 value: 'beijing',
36 label: '北京', 40 label: '北京',
@@ -67,6 +71,7 @@ @@ -67,6 +71,7 @@
67 label: '苏州', 71 label: '苏州',
68 children: [ 72 children: [
69 { 73 {
  74 + disabled: true,
70 value: 'zhuozhengyuan', 75 value: 'zhuozhengyuan',
71 label: '拙政园', 76 label: '拙政园',
72 }, 77 },
src/components/cascader/cascader.vue
@@ -5,7 +5,8 @@ @@ -5,7 +5,8 @@
5 <i-input 5 <i-input
6 :readonly="!filterable" 6 :readonly="!filterable"
7 :disabled="disabled" 7 :disabled="disabled"
8 - v-model="displayRender" 8 + :value="displayRender"
  9 + @on-change="handleInput"
9 :size="size" 10 :size="size"
10 :placeholder="placeholder"></i-input> 11 :placeholder="placeholder"></i-input>
11 <Icon type="ios-close" :class="[prefixCls + '-arrow']" v-show="showCloseIcon" @click.native.stop="clearSelect"></Icon> 12 <Icon type="ios-close" :class="[prefixCls + '-arrow']" v-show="showCloseIcon" @click.native.stop="clearSelect"></Icon>
@@ -16,12 +17,23 @@ @@ -16,12 +17,23 @@
16 <Drop v-show="visible"> 17 <Drop v-show="visible">
17 <div> 18 <div>
18 <Caspanel 19 <Caspanel
  20 + v-show="!filterable || (filterable && query === '')"
19 ref="caspanel" 21 ref="caspanel"
20 :prefix-cls="prefixCls" 22 :prefix-cls="prefixCls"
21 :data="data" 23 :data="data"
22 :disabled="disabled" 24 :disabled="disabled"
23 :change-on-select="changeOnSelect" 25 :change-on-select="changeOnSelect"
24 :trigger="trigger"></Caspanel> 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 </div> 37 </div>
26 </Drop> 38 </Drop>
27 </transition> 39 </transition>
@@ -37,6 +49,7 @@ @@ -37,6 +49,7 @@
37 import Emitter from '../../mixins/emitter'; 49 import Emitter from '../../mixins/emitter';
38 50
39 const prefixCls = 'ivu-cascader'; 51 const prefixCls = 'ivu-cascader';
  52 + const selectPrefixCls = 'ivu-select';
40 53
41 export default { 54 export default {
42 name: 'Cascader', 55 name: 'Cascader',
@@ -100,11 +113,13 @@ @@ -100,11 +113,13 @@
100 data () { 113 data () {
101 return { 114 return {
102 prefixCls: prefixCls, 115 prefixCls: prefixCls,
  116 + selectPrefixCls: selectPrefixCls,
103 visible: false, 117 visible: false,
104 selected: [], 118 selected: [],
105 tmpSelected: [], 119 tmpSelected: [],
106 updatingValue: false, // to fix set value in changeOnSelect type 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 computed: { 125 computed: {
@@ -128,6 +143,32 @@ @@ -128,6 +143,32 @@
128 } 143 }
129 144
130 return this.renderFormat(label, this.selected); 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 methods: { 174 methods: {
@@ -146,7 +187,7 @@ @@ -146,7 +187,7 @@
146 toggleOpen () { 187 toggleOpen () {
147 if (this.disabled) return false; 188 if (this.disabled) return false;
148 if (this.visible) { 189 if (this.visible) {
149 - this.handleClose(); 190 + if (!this.filterable) this.handleClose();
150 } else { 191 } else {
151 this.onFocus(); 192 this.onFocus();
152 } 193 }
@@ -177,6 +218,19 @@ @@ -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 created () { 236 created () {
src/styles/components/cascader.less
@@ -49,6 +49,10 @@ @@ -49,6 +49,10 @@
49 49
50 .select-item(@cascader-prefix-cls, @cascader-item-prefix-cls); 50 .select-item(@cascader-prefix-cls, @cascader-item-prefix-cls);
51 51
  52 + &-dropdown{
  53 + padding: 5px 0;
  54 + }
  55 +
52 &-menu{ 56 &-menu{
53 display: inline-block; 57 display: inline-block;
54 min-width: 100px; 58 min-width: 100px;