Commit 01b54e302194cbb6f889df126551c9d94fca0924

Authored by 梁灏
1 parent 28587238

Select support remote search

examples/routers/select.vue
1 <template> 1 <template>
2 <div style="width: 200px;margin: 100px;"> 2 <div style="width: 200px;margin: 100px;">
3 - <i-select v-model="model" filterable clearable style="width:200px">  
4 - <i-option :value="option.value" v-for="option in options" :key="option">{{option.label}}</i-option> 3 + <i-select v-model="model" filterable remote :remote-method="remoteMethod" :loading="loading" clearable style="width:200px">
  4 + <i-option v-for="option in options" :value="option.value" :key="option">{{option.label}}</i-option>
5 </i-select> 5 </i-select>
  6 + <!--<Button @click="handleAdd">+</Button>-->
6 </div> 7 </div>
7 </template> 8 </template>
8 9
@@ -10,26 +11,85 @@ @@ -10,26 +11,85 @@
10 export default { 11 export default {
11 data () { 12 data () {
12 return { 13 return {
13 - model: 1, 14 + model: '',
14 options: [ 15 options: [
15 16
  17 + ],
  18 + list: [],
  19 + loading: false,
  20 + states: ["Alabama", "Alaska", "Arizona",
  21 + "Arkansas", "California", "Colorado",
  22 + "Connecticut", "Delaware", "Florida",
  23 + "Georgia", "Hawaii", "Idaho", "Illinois",
  24 + "Indiana", "Iowa", "Kansas", "Kentucky",
  25 + "Louisiana", "Maine", "Maryland",
  26 + "Massachusetts", "Michigan", "Minnesota",
  27 + "Mississippi", "Missouri", "Montana",
  28 + "Nebraska", "Nevada", "New Hampshire",
  29 + "New Jersey", "New Mexico", "New York",
  30 + "North Carolina", "North Dakota", "Ohio",
  31 + "Oklahoma", "Oregon", "Pennsylvania",
  32 + "Rhode Island", "South Carolina",
  33 + "South Dakota", "Tennessee", "Texas",
  34 + "Utah", "Vermont", "Virginia",
  35 + "Washington", "West Virginia", "Wisconsin",
  36 + "Wyoming"]
  37 + }
  38 + },
  39 + mounted () {
  40 + this.options = [
  41 +// {
  42 +// label: '全部',
  43 +// value: 0
  44 +// },{
  45 +// label: '苹果',
  46 +// value: 1
  47 +// },{
  48 +// label: '香蕉',
  49 +// value: 2
  50 +// },{
  51 +// label: '西瓜',
  52 +// value: 3
  53 +// }
  54 + ];
  55 + },
  56 + methods: {
  57 + handleAdd () {
  58 + this.options = [
  59 + {
  60 + label: '全部',
  61 + value: 0
  62 + },{
  63 + label: '苹果',
  64 + value: 1
  65 + },{
  66 + label: '香蕉',
  67 + value: 2
  68 + },{
  69 + label: '西瓜',
  70 + value: 3
  71 + }
16 ] 72 ]
  73 + },
  74 + remoteMethod (query) {
  75 + if (query !== '') {
  76 + this.loading = true;
  77 + setTimeout(() => {
  78 + this.loading = false;
  79 + this.options = this.list.filter(item => {
  80 + return item.label.toLowerCase()
  81 + .indexOf(query.toLowerCase()) > -1;
  82 + });
  83 + }, 200);
  84 + } else {
  85 + this.options = [];
  86 + }
17 } 87 }
18 }, 88 },
19 mounted () { 89 mounted () {
20 - this.options = [{  
21 - label: '全部',  
22 - value: 0  
23 - },{  
24 - label: '苹果',  
25 - value: 1  
26 - },{  
27 - label: '香蕉',  
28 - value: 2  
29 - },{  
30 - label: '西瓜',  
31 - value: 3  
32 - }]; 90 + this.list = this.states.map(item => {
  91 + return { value: item, label: item };
  92 + });
33 } 93 }
34 } 94 }
35 </script> 95 </script>
36 \ No newline at end of file 96 \ No newline at end of file
src/components/select/select.vue
@@ -22,12 +22,16 @@ @@ -22,12 +22,16 @@
22 @keydown.delete="handleInputDelete" 22 @keydown.delete="handleInputDelete"
23 ref="input"> 23 ref="input">
24 <Icon type="ios-close" :class="[prefixCls + '-arrow']" v-show="showCloseIcon" @click.native.stop="clearSingleSelect"></Icon> 24 <Icon type="ios-close" :class="[prefixCls + '-arrow']" v-show="showCloseIcon" @click.native.stop="clearSingleSelect"></Icon>
25 - <Icon type="arrow-down-b" :class="[prefixCls + '-arrow']"></Icon> 25 + <Icon type="arrow-down-b" :class="[prefixCls + '-arrow']" v-if="!remote"></Icon>
26 </div> 26 </div>
27 <transition :name="transitionName"> 27 <transition :name="transitionName">
28 - <Drop v-show="visible" :placement="placement" ref="dropdown">  
29 - <ul v-show="notFound" :class="[prefixCls + '-not-found']"><li>{{ localeNotFoundText }}</li></ul>  
30 - <ul v-show="!notFound" :class="[prefixCls + '-dropdown-list']" ref="options"><slot></slot></ul> 28 + <Drop v-show="(visible && options.length) ||
  29 + (visible && !options.length && loading) ||
  30 + (visible && remote && !loading && !options.length && query !== '')" :placement="placement" ref="dropdown">
  31 + <!--<Drop v-show="visible" :placement="placement" ref="dropdown">-->
  32 + <ul v-show="(notFound && !remote) || (remote && !loading && !options.length)" :class="[prefixCls + '-not-found']"><li>{{ localeNotFoundText }}</li></ul>
  33 + <ul v-show="(!notFound && !remote) || (remote && !loading && !notFound)" :class="[prefixCls + '-dropdown-list']" ref="options"><slot></slot></ul>
  34 + <ul v-show="loading" :class="[prefixCls + '-loading']">{{ localeLoadingText }}</ul>
31 </Drop> 35 </Drop>
32 </transition> 36 </transition>
33 </div> 37 </div>
@@ -74,6 +78,20 @@ @@ -74,6 +78,20 @@
74 filterMethod: { 78 filterMethod: {
75 type: Function 79 type: Function
76 }, 80 },
  81 + remote: {
  82 + type: Boolean,
  83 + default: false
  84 + },
  85 + remoteMethod: {
  86 + type: Function
  87 + },
  88 + loading: {
  89 + type: Boolean,
  90 + default: false
  91 + },
  92 + loadingText: {
  93 + type: String
  94 + },
77 size: { 95 size: {
78 validator (value) { 96 validator (value) {
79 return oneOf(value, ['small', 'large', 'default']); 97 return oneOf(value, ['small', 'large', 'default']);
@@ -103,6 +121,7 @@ @@ -103,6 +121,7 @@
103 selectedMultiple: [], 121 selectedMultiple: [],
104 focusIndex: 0, 122 focusIndex: 0,
105 query: '', 123 query: '',
  124 + selectToChangeQuery: false, // when select an option, set this first and set query, because query is watching, it will emit event
106 inputLength: 20, 125 inputLength: 20,
107 notFound: false, 126 notFound: false,
108 slotChangeDuration: false, // if slot change duration and in multiple, set true and after slot change, set false 127 slotChangeDuration: false, // if slot change duration and in multiple, set true and after slot change, set false
@@ -168,6 +187,13 @@ @@ -168,6 +187,13 @@
168 return this.notFoundText; 187 return this.notFoundText;
169 } 188 }
170 }, 189 },
  190 + localeLoadingText () {
  191 + if (this.loadingText === undefined) {
  192 + return this.t('i.select.loading');
  193 + } else {
  194 + return this.loadingText;
  195 + }
  196 + },
171 transitionName () { 197 transitionName () {
172 return this.placement === 'bottom' ? 'slide-up' : 'slide-down'; 198 return this.placement === 'bottom' ? 'slide-up' : 'slide-down';
173 } 199 }
@@ -187,15 +213,18 @@ @@ -187,15 +213,18 @@
187 }, 213 },
188 // find option component 214 // find option component
189 findChild (cb) { 215 findChild (cb) {
  216 + const _this = this;
190 const find = function (child) { 217 const find = function (child) {
191 const name = child.$options.componentName; 218 const name = child.$options.componentName;
192 219
193 if (name) { 220 if (name) {
194 cb(child); 221 cb(child);
195 } else if (child.$children.length) { 222 } else if (child.$children.length) {
196 - child.$children.forEach((innerChild) => {  
197 - find(innerChild, cb);  
198 - }); 223 + _this.$nextTick(() => {
  224 + child.$children.forEach((innerChild) => {
  225 + find(innerChild, cb);
  226 + });
  227 + })
199 } 228 }
200 }; 229 };
201 230
@@ -228,8 +257,10 @@ @@ -228,8 +257,10 @@
228 this.options = options; 257 this.options = options;
229 258
230 if (init) { 259 if (init) {
231 - this.updateSingleSelected(true, slot);  
232 - this.updateMultipleSelected(true, slot); 260 + if (!this.remote) {
  261 + this.updateSingleSelected(true, slot);
  262 + this.updateMultipleSelected(true, slot);
  263 + }
233 } 264 }
234 }, 265 },
235 updateSingleSelected (init = false, slot = false) { 266 updateSingleSelected (init = false, slot = false) {
@@ -535,18 +566,22 @@ @@ -535,18 +566,22 @@
535 document.addEventListener('keydown', this.handleKeydown); 566 document.addEventListener('keydown', this.handleKeydown);
536 567
537 this.$on('append', () => { 568 this.$on('append', () => {
538 - this.modelToQuery();  
539 - this.$nextTick(() => {  
540 - this.broadcastQuery('');  
541 - }); 569 + if (!this.remote) {
  570 + this.modelToQuery();
  571 + this.$nextTick(() => {
  572 + this.broadcastQuery('');
  573 + });
  574 + }
542 this.slotChange(); 575 this.slotChange();
543 this.updateOptions(true, true); 576 this.updateOptions(true, true);
544 }); 577 });
545 this.$on('remove', () => { 578 this.$on('remove', () => {
546 - this.modelToQuery();  
547 - this.$nextTick(() => {  
548 - this.broadcastQuery('');  
549 - }); 579 + if (!this.remote) {
  580 + this.modelToQuery();
  581 + this.$nextTick(() => {
  582 + this.broadcastQuery('');
  583 + });
  584 + }
550 this.slotChange(); 585 this.slotChange();
551 this.updateOptions(true, true); 586 this.updateOptions(true, true);
552 }); 587 });
@@ -565,6 +600,7 @@ @@ -565,6 +600,7 @@
565 } 600 }
566 601
567 if (this.filterable) { 602 if (this.filterable) {
  603 + this.selectToChangeQuery = true;
568 this.query = ''; 604 this.query = '';
569 this.$refs.input.focus(); 605 this.$refs.input.focus();
570 } 606 }
@@ -574,6 +610,7 @@ @@ -574,6 +610,7 @@
574 if (this.filterable) { 610 if (this.filterable) {
575 this.findChild((child) => { 611 this.findChild((child) => {
576 if (child.value === value) { 612 if (child.value === value) {
  613 + this.selectToChangeQuery = true;
577 this.query = child.label === undefined ? child.searchLabel : child.label; 614 this.query = child.label === undefined ? child.searchLabel : child.label;
578 } 615 }
579 }); 616 });
@@ -625,20 +662,29 @@ @@ -625,20 +662,29 @@
625 } 662 }
626 }, 663 },
627 query (val) { 664 query (val) {
628 - this.$emit('on-query-change', val); 665 + if (this.remote && this.remoteMethod) {
  666 + if (!this.selectToChangeQuery) {
  667 + this.$emit('on-query-change', val);
  668 + this.remoteMethod(val);
  669 + }
  670 + } else {
  671 + if (!this.selectToChangeQuery) {
  672 + this.$emit('on-query-change', val);
  673 + }
  674 + this.broadcastQuery(val);
629 675
630 - this.broadcastQuery(val);  
631 -  
632 - let is_hidden = true; 676 + let is_hidden = true;
633 677
634 - this.$nextTick(() => {  
635 - this.findChild((child) => {  
636 - if (!child.hidden) {  
637 - is_hidden = false;  
638 - } 678 + this.$nextTick(() => {
  679 + this.findChild((child) => {
  680 + if (!child.hidden) {
  681 + is_hidden = false;
  682 + }
  683 + });
  684 + this.notFound = is_hidden;
639 }); 685 });
640 - this.notFound = is_hidden;  
641 - }); 686 + }
  687 + this.selectToChangeQuery = false;
642 this.broadcast('Drop', 'on-update-popper'); 688 this.broadcast('Drop', 'on-update-popper');
643 } 689 }
644 } 690 }
src/locale/lang/en-US.js
@@ -2,7 +2,8 @@ export default { @@ -2,7 +2,8 @@ export default {
2 i: { 2 i: {
3 select: { 3 select: {
4 placeholder: 'Select', 4 placeholder: 'Select',
5 - noMatch: 'No matching data' 5 + noMatch: 'No matching data',
  6 + loading: 'Loading'
6 }, 7 },
7 table: { 8 table: {
8 noDataText: 'No Data', 9 noDataText: 'No Data',
src/locale/lang/es-ES.js
@@ -2,7 +2,8 @@ export default { @@ -2,7 +2,8 @@ export default {
2 i: { 2 i: {
3 select: { 3 select: {
4 placeholder: 'Seleccionar', 4 placeholder: 'Seleccionar',
5 - noMatch: 'Sin coincidencias' 5 + noMatch: 'Sin coincidencias',
  6 + loading: 'Cargando'
6 }, 7 },
7 table: { 8 table: {
8 noDataText: 'Sin Datos', 9 noDataText: 'Sin Datos',
src/locale/lang/ja-JP.js
@@ -2,7 +2,8 @@ export default { @@ -2,7 +2,8 @@ export default {
2 i: { 2 i: {
3 select: { 3 select: {
4 placeholder: '選んでください', 4 placeholder: '選んでください',
5 - noMatch: 'マッチするデータなし' 5 + noMatch: 'マッチするデータなし',
  6 + loading: 'ロード中'
6 }, 7 },
7 table: { 8 table: {
8 noDataText: 'データなし', 9 noDataText: 'データなし',
src/locale/lang/tr-TR.js
@@ -2,7 +2,8 @@ export default { @@ -2,7 +2,8 @@ export default {
2 i: { 2 i: {
3 select: { 3 select: {
4 placeholder: 'Seç', 4 placeholder: 'Seç',
5 - noMatch: 'Eşleşen veri yok' 5 + noMatch: 'Eşleşen veri yok',
  6 + loading: 'yükleme'
6 }, 7 },
7 table: { 8 table: {
8 noDataText: 'Veri Yok', 9 noDataText: 'Veri Yok',
src/locale/lang/zh-CN.js
@@ -2,7 +2,8 @@ export default { @@ -2,7 +2,8 @@ export default {
2 i: { 2 i: {
3 select: { 3 select: {
4 placeholder: '请选择', 4 placeholder: '请选择',
5 - noMatch: '无匹配数据' 5 + noMatch: '无匹配数据',
  6 + loading: '加载中'
6 }, 7 },
7 table: { 8 table: {
8 noDataText: '暂无数据', 9 noDataText: '暂无数据',
src/locale/lang/zh-TW.js
@@ -2,7 +2,8 @@ export default { @@ -2,7 +2,8 @@ export default {
2 i: { 2 i: {
3 select: { 3 select: {
4 placeholder: '請選擇', 4 placeholder: '請選擇',
5 - noMatch: '無匹配數據' 5 + noMatch: '無匹配數據',
  6 + loading: '加載中'
6 }, 7 },
7 table: { 8 table: {
8 noDataText: '暫無數據', 9 noDataText: '暫無數據',
src/styles/components/select.less
@@ -174,6 +174,10 @@ @@ -174,6 +174,10 @@
174 text-align: center; 174 text-align: center;
175 color: @btn-disable-color; 175 color: @btn-disable-color;
176 } 176 }
  177 + &-loading{
  178 + text-align: center;
  179 + color: @btn-disable-color;
  180 + }
177 181
178 &-multiple .@{css-prefix}tag{ 182 &-multiple .@{css-prefix}tag{
179 margin: 3px 4px 2px 0; 183 margin: 3px 4px 2px 0;