Browse Source

Select: refactor and bug fix

Leopoldthecoder 8 years ago
parent
commit
ce3bed6cb5

+ 18 - 7
examples/docs/zh-CN/select.md

@@ -81,15 +81,15 @@
           value: 'Guangzhou',
           label: '广州'
         }],
-        value: '',
+        value: '选项2',
         value2: '',
         value3: '',
         value4: '',
         value5: [],
         value6: '',
-        value7: [],
+        value7: '',
         value8: '',
-        value9: [],
+        value9: '',
         loading: false,
         states: ["Alabama", "Alaska", "Arizona", "Arkansas", "California", "Colorado", "Connecticut", "Delaware", "Florida", "Georgia", "Hawaii", "Idaho", "Illinois", "Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana", "Maine", "Maryland", "Massachusetts", "Michigan", "Minnesota", "Mississippi", "Missouri", "Montana", "Nebraska", "Nevada", "New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina", "North Dakota", "Ohio", "Oklahoma", "Oregon", "Pennsylvania", "Rhode Island", "South Carolina", "South Dakota", "Tennessee", "Texas", "Utah", "Vermont", "Virginia", "Washington", "West Virginia", "Wisconsin", "Wyoming"]
       };
@@ -97,9 +97,20 @@
     
     mounted() {
       this.list = this.states.map(item => { return { value: item, label: item }; });
+//      this.options4 = this.states.map(item => { return { value: item, label: item }; });
+//      this.value9 = ['Vermont'];
     },
 
     methods: {
+      empty() {
+        this.value9 = ['New York'];
+      },
+      aa(val) {
+        console.log('change', val)
+      },
+      bb() {
+        this.options.splice(0, 1, { label: 'haha', value: 'haha' });
+      },
       remoteMethod(query) {
         if (query !== '') {
           this.loading = true;
@@ -129,11 +140,11 @@
 ### 基础用法
 
 适用广泛的基础单选
-
+<el-button @click="bb">bb</el-button>
 :::demo `v-model`的值为当前被选中的`el-option`的 value 属性值
 ```html
 <template>
-  <el-select v-model="value" placeholder="请选择">
+  <el-select v-model="value" placeholder="请选择" @change="aa">
     <el-option
       v-for="item in options"
       :label="item.label"
@@ -499,13 +510,13 @@
 ### 远程搜索
 
 从服务器搜索数据,输入关键字进行查找
-
+<el-button @click="empty">aa</el-button>
 :::demo 为了启用远程搜索,需要将`filterable`和`remote`设置为`true`,同时传入一个`remote-method`。`remote-method`为一个`Function`,它会在输入值发生变化时调用,参数为当前输入值。需要注意的是,如果`el-option`是通过`v-for`指令渲染出来的,此时需要为`el-option`添加`key`属性,且其值需具有唯一性,比如此例中的`item.value`。
 ```html
 <template>
   <el-select
     v-model="value9"
-    multiple
+    clearable
     filterable
     remote
     placeholder="请输入关键词"

+ 1 - 3
packages/pagination/src/pagination.js

@@ -140,10 +140,8 @@ export default {
         return (
           <span class="el-pagination__sizes">
             <el-select
-              size="small"
               value={ this.$parent.internalPageSize }
-              on-change={ this.handleChange }
-              width={ 110 }>
+              on-change={ this.handleChange }>
               {
                 this.pageSizes.map(item =>
                     <el-option

+ 8 - 20
packages/select/src/option.vue

@@ -4,7 +4,11 @@
     @click.stop="selectOptionClick"
     class="el-select-dropdown__item"
     v-show="visible"
-    :class="{ 'selected': itemSelected, 'is-disabled': disabled || groupDisabled, 'hover': parent.hoverIndex === index }">
+    :class="{
+      'selected': itemSelected,
+      'is-disabled': disabled || groupDisabled,
+      'hover': parent.hoverIndex === index
+    }">
     <slot>
       <span>{{ currentLabel }}</span>
     </slot>
@@ -63,23 +67,11 @@
       },
 
       itemSelected() {
-        if (Object.prototype.toString.call(this.parent.selected) === '[object Object]') {
-          return this === this.parent.selected;
-        } else if (Array.isArray(this.parent.selected)) {
+        if (!this.parent.multiple) {
+          return this.value === this.parent.value;
+        } else {
           return this.parent.value.indexOf(this.value) > -1;
         }
-      },
-
-      currentSelected() {
-        return this.selected || (this.parent.multiple ? this.parent.value.indexOf(this.value) > -1 : this.parent.value === this.value);
-      }
-    },
-
-    watch: {
-      currentSelected(val) {
-        if (val === true) {
-          this.dispatch('ElSelect', 'addOptionToValue', this);
-        }
       }
     },
 
@@ -122,10 +114,6 @@
       this.parent.filteredOptionsCount++;
       this.index = this.parent.options.indexOf(this);
 
-      if (this.currentSelected === true) {
-        this.dispatch('ElSelect', 'addOptionToValue', [this, true]);
-      }
-
       this.$on('queryChange', this.queryChange);
       this.$on('handleGroupDisabled', this.handleGroupDisabled);
       this.$on('resetIndex', this.resetIndex);

+ 87 - 124
packages/select/src/select.vue

@@ -1,19 +1,26 @@
 <template>
   <div
     class="el-select"
-    v-clickoutside="handleClose"
-    :class="{ 'is-multiple': multiple, 'is-small': size === 'small' }">
-    <div class="el-select__tags" v-if="multiple" @click.stop="toggleMenu" ref="tags" :style="{ 'max-width': inputWidth - 32 + 'px' }">
+    v-clickoutside="handleClose">
+    <div
+      class="el-select__tags"
+      v-if="multiple"
+      @click.stop="toggleMenu"
+      ref="tags"
+      :style="{ 'max-width': inputWidth - 32 + 'px' }">
       <transition-group @after-leave="resetInputHeight">
         <el-tag
           v-for="item in selected"
-          :key="item"
+          :key="item.value"
           closable
           :hit="item.hitState"
           type="primary"
           @close="deleteTag($event, item)"
-          close-transition>{{ item.currentLabel }}</el-tag>
+          close-transition>
+          {{ item.currentLabel }}
+        </el-tag>
       </transition-group>
+
       <input
         type="text"
         class="el-select__input"
@@ -40,7 +47,7 @@
       :disabled="disabled"
       :readonly="!filterable || multiple"
       @focus="toggleMenu"
-      @click="toggleMenu"
+      @click="handleIconClick"
       @mousedown.native="handleMouseDown"
       @keyup.native="debouncedOnInputChange"
       @keydown.native.down.prevent="navigateOptions('next')"
@@ -94,22 +101,18 @@
       },
 
       showCloseIcon() {
-        let criteria = this.clearable && this.inputHovering && !this.multiple && this.options.indexOf(this.selected) > -1;
+        let criteria = this.clearable &&
+          this.inputHovering &&
+          !this.multiple &&
+          this.value !== undefined &&
+          this.value !== '';
         if (!this.$el) return false;
-
         this.$nextTick(() => {
           let icon = this.$el.querySelector('.el-input__icon');
           if (icon) {
-            if (criteria) {
-              icon.addEventListener('click', this.deleteSelected);
-              addClass(icon, 'is-show-close');
-            } else {
-              icon.removeEventListener('click', this.deleteSelected);
-              removeClass(icon, 'is-show-close');
-            }
+            criteria ? addClass(icon, 'is-show-close') : removeClass(icon, 'is-show-close');
           }
         });
-
         return criteria;
       },
 
@@ -143,7 +146,6 @@
     props: {
       name: String,
       value: {},
-      size: String,
       disabled: Boolean,
       clearable: Boolean,
       filterable: Boolean,
@@ -163,18 +165,16 @@
     data() {
       return {
         options: [],
-        selected: {},
+        selected: this.multiple ? [] : {},
         isSelect: true,
         inputLength: 20,
         inputWidth: 0,
-        valueChangeBySelected: false,
         cachedPlaceHolder: '',
         optionsCount: 0,
         filteredOptionsCount: 0,
         dropdownUl: null,
         visible: false,
         selectedLabel: '',
-        selectInit: false,
         hoverIndex: -1,
         query: '',
         voidRemoteQuery: false,
@@ -191,79 +191,24 @@
       },
 
       value(val) {
-        if (this.valueChangeBySelected) {
-          this.valueChangeBySelected = false;
-          return;
-        }
-        this.$nextTick(() => {
-          if (this.multiple && Array.isArray(val)) {
-            this.$nextTick(() => {
-              this.resetInputHeight();
-            });
-            this.selectedInit = true;
-            this.selected = [];
-            this.currentPlaceholder = this.cachedPlaceHolder;
-            val.forEach(item => {
-              let option = this.options.filter(option => option.value === item)[0];
-              if (option) {
-                this.$emit('addOptionToValue', option);
-              }
-            });
-          }
-          if (!this.multiple) {
-            let option = this.options.filter(option => option.value === val)[0];
-            if (option) {
-              this.$emit('addOptionToValue', option);
-            } else {
-              this.selected = {};
-              this.selectedLabel = '';
-            }
-          }
-          this.resetHoverIndex();
-        });
-      },
-
-      selected(val, oldVal) {
         if (this.multiple) {
-          if (this.selected.length > 0) {
+          this.resetInputHeight();
+          if (val.length > 0) {
             this.currentPlaceholder = '';
           } else {
             this.currentPlaceholder = this.cachedPlaceHolder;
           }
-          this.$nextTick(() => {
-            this.resetInputHeight();
-          });
-          if (this.selectedInit) {
-            this.selectedInit = false;
-            return;
-          }
-          this.valueChangeBySelected = true;
-          const result = val.map(item => item.value);
-
-          this.$emit('input', result);
-          this.$emit('change', result);
-          this.dispatch('ElFormItem', 'el.form.change', val);
-          if (this.filterable) {
-            this.query = '';
-            this.hoverIndex = -1;
-            this.$refs.input.focus();
-            this.inputLength = 20;
-          }
-        } else {
-          if (this.selectedInit) {
-            this.selectedInit = false;
-            return;
-          }
-          if (val.value === oldVal.value) return;
-          this.$emit('input', val.value);
-          this.$emit('change', val.value);
         }
+        this.selected = this.getSelected();
+        if (this.filterable) {
+          this.inputLength = 20;
+        }
+        this.$emit('change', val);
+        this.dispatch('ElFormItem', 'el.form.change', val);
       },
 
       query(val) {
-        this.$nextTick(() => {
-          this.broadcast('ElSelectDropdown', 'updatePopper');
-        });
+        this.broadcast('ElSelectDropdown', 'updatePopper');
         if (this.multiple && this.filterable) {
           this.resetInputHeight();
         }
@@ -283,16 +228,18 @@
       visible(val) {
         if (!val) {
           this.$refs.reference.$el.querySelector('input').blur();
-          if (this.$el.querySelector('.el-input__icon')) {
-            removeClass(this.$el.querySelector('.el-input__icon'), 'is-reverse');
+          let icon = this.$el.querySelector('.el-input__icon');
+          if (icon) {
+            removeClass(icon, 'is-reverse');
           }
           this.broadcast('ElSelectDropdown', 'destroyPopper');
           if (this.$refs.input) {
             this.$refs.input.blur();
           }
+          this.query = '';
           this.resetHoverIndex();
           if (!this.multiple) {
-            if (this.dropdownUl && this.selected.$el) {
+            if (this.dropdownUl && this.selected && this.selected.$el) {
               this.bottomOverflowBeforeHidden = this.selected.$el.getBoundingClientRect().bottom - this.$refs.popper.$el.getBoundingClientRect().bottom;
             }
             if (this.selected && this.selected.value) {
@@ -302,7 +249,7 @@
         } else {
           let icon = this.$el.querySelector('.el-input__icon');
           if (icon && !hasClass(icon, 'el-icon-circle-close')) {
-            addClass(this.$el.querySelector('.el-input__icon'), 'is-reverse');
+            addClass(icon, 'is-reverse');
           }
           this.broadcast('ElSelectDropdown', 'updatePopper');
           if (this.filterable) {
@@ -327,10 +274,47 @@
 
       options(val) {
         this.optionsAllDisabled = val.length === val.filter(item => item.disabled === true).length;
+        // 下面这行先去掉,因为 remote !multiple 时会有问题
+//        this.selected = this.getSelected();
+        if (this.multiple) {
+          this.resetInputHeight();
+        }
       }
     },
 
     methods: {
+      getSelected() {
+        if (this.multiple) {
+          let result = [];
+          this.value.forEach(value => {
+            let option = this.options.filter(option => option.value === value)[0];
+            if (option) {
+              result.push(option);
+            } else {
+              result.push({
+                value: this.value,
+                currentLabel: value,
+                hitState: false
+              });
+            }
+          });
+          return result;
+        } else {
+          let option = this.options.filter(option => option.value === this.value)[0] ||
+            { value: this.value, currentLabel: this.value };
+          this.selectedLabel = option.currentLabel;
+          return option;
+        }
+      },
+
+      handleIconClick(event) {
+        if (this.iconClass === 'circle-close') {
+          this.deleteSelected(event);
+        } else {
+          this.toggleMenu();
+        }
+      },
+
       handleMouseDown(event) {
         if (event.target.tagName !== 'INPUT') return;
         if (this.visible) {
@@ -363,22 +347,7 @@
 
       deletePrevTag(e) {
         if (e.target.value.length <= 0 && !this.toggleLastOptionHitState()) {
-          this.selected.pop();
-        }
-      },
-
-      addOptionToValue(option, init) {
-        if (this.multiple) {
-          if (this.selected.indexOf(option) === -1 && (this.remote ? this.value.indexOf(option.value) === -1 : true)) {
-            this.selectedInit = !!init;
-            this.selected.push(option);
-            this.resetHoverIndex();
-          }
-        } else {
-          this.selectedInit = !!init;
-          this.selected = option;
-          this.selectedLabel = option.currentLabel;
-          this.hoverIndex = option.index;
+          this.value.pop();
         }
       },
 
@@ -418,20 +387,19 @@
 
       handleOptionSelect(option) {
         if (!this.multiple) {
-          this.selected = option;
-          this.selectedLabel = option.currentLabel;
+          this.$emit('input', option.value);
           this.visible = false;
         } else {
           let optionIndex = -1;
-          this.selected.forEach((item, index) => {
-            if (item === option || item.currentValue === option.currentValue) {
+          this.value.forEach((item, index) => {
+            if (item === option.value) {
               optionIndex = index;
             }
           });
           if (optionIndex > -1) {
-            this.selected.splice(optionIndex, 1);
+            this.value.splice(optionIndex, 1);
           } else {
-            this.selected.push(option);
+            this.value.push(option.value);
           }
         }
       },
@@ -497,8 +465,6 @@
 
       deleteSelected(event) {
         event.stopPropagation();
-        this.selected = {};
-        this.selectedLabel = '';
         this.$emit('input', '');
         this.$emit('change', '');
         this.visible = false;
@@ -507,7 +473,7 @@
       deleteTag(event, tag) {
         let index = this.selected.indexOf(tag);
         if (index > -1) {
-          this.selected.splice(index, 1);
+          this.value.splice(index, 1);
         }
         event.stopPropagation();
       },
@@ -535,9 +501,11 @@
 
     created() {
       this.cachedPlaceHolder = this.currentPlaceholder = this.placeholder;
-      if (this.multiple) {
-        this.selectedInit = true;
-        this.selected = [];
+      if (this.multiple && !Array.isArray(this.value)) {
+        this.$emit('input', []);
+      }
+      if (!this.multiple && !this.value) {
+        this.$emit('input', '');
       }
       if (this.remote) {
         this.voidRemoteQuery = true;
@@ -547,20 +515,15 @@
         this.onInputChange();
       });
 
-      this.$on('addOptionToValue', this.addOptionToValue);
       this.$on('handleOptionClick', this.handleOptionSelect);
       this.$on('onOptionDestroy', this.onOptionDestroy);
     },
 
     mounted() {
       addResizeListener(this.$el, this.resetInputWidth);
-      if (this.remote && this.multiple && Array.isArray(this.value)) {
-        this.selected = this.options.reduce((prev, curr) => {
-          return this.value.indexOf(curr.value) > -1 ? prev.concat(curr) : prev;
-        }, []);
-        this.$nextTick(() => {
-          this.resetInputHeight();
-        });
+      this.selected = this.getSelected();
+      if (this.remote && this.multiple) {
+        this.resetInputHeight();
       }
       this.$nextTick(() => {
         if (this.$refs.reference.$el) {

+ 2 - 0
packages/theme-default/src/pagination.css

@@ -23,6 +23,8 @@
       width: 110px;
       input {
         padding-right: 25px;
+        border-radius: var(--border-radius-small);
+        height: 28px;
       }
     }
 

+ 0 - 7
packages/theme-default/src/select.css

@@ -12,13 +12,6 @@
     display: block;
     position: relative;
 
-    @when small {
-      & input {
-        border-radius: var(--border-radius-small);
-        height: 28px;
-      }
-    }
-
     &:hover {
       .el-input__inner {
         border-color: var(--select-border-color-hover);

+ 16 - 14
test/unit/specs/select.spec.js

@@ -387,21 +387,23 @@ describe('Select', () => {
       remoteMethod
     });
     const select = vm.$children[0];
-    select.query = '面';
-    setTimeout(() => {
-      expect(select.filteredOptionsCount).to.equal(1);
-      select.query = '';
-      select.options[0].$el.click();
-      vm.$nextTick(() => {
-        expect(vm.value[0]).to.equal('选项4');
-        select.deletePrevTag({ target: select.$refs.input });
-        select.deletePrevTag({ target: select.$refs.input });
-        select.resetInputState({ keyCode: 1 });
+    vm.$nextTick(() => {
+      select.query = '面';
+      setTimeout(() => {
+        expect(select.filteredOptionsCount).to.equal(1);
+        select.query = '';
+        select.options[0].$el.click();
         vm.$nextTick(() => {
-          expect(vm.value.length).to.equal(0);
-          done();
+          expect(vm.value[0]).to.equal('选项4');
+          select.deletePrevTag({ target: select.$refs.input });
+          select.deletePrevTag({ target: select.$refs.input });
+          select.resetInputState({ keyCode: 1 });
+          vm.$nextTick(() => {
+            expect(vm.value.length).to.equal(0);
+            done();
+          });
         });
-      });
-    }, 250);
+      }, 250);
+    });
   });
 });