mirror of https://github.com/ElemeFE/element
Select: refactor and bug fix
parent
568df8058d
commit
ce3bed6cb5
|
@ -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="请输入关键词"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
this.selected = this.getSelected();
|
||||
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.$emit('change', val);
|
||||
this.dispatch('ElFormItem', 'el.form.change', val);
|
||||
},
|
||||
|
||||
query(val) {
|
||||
this.$nextTick(() => {
|
||||
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.selected = this.getSelected();
|
||||
if (this.remote && this.multiple) {
|
||||
this.resetInputHeight();
|
||||
});
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.reference.$el) {
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
width: 110px;
|
||||
input {
|
||||
padding-right: 25px;
|
||||
border-radius: var(--border-radius-small);
|
||||
height: 28px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -387,6 +387,7 @@ describe('Select', () => {
|
|||
remoteMethod
|
||||
});
|
||||
const select = vm.$children[0];
|
||||
vm.$nextTick(() => {
|
||||
select.query = '面';
|
||||
setTimeout(() => {
|
||||
expect(select.filteredOptionsCount).to.equal(1);
|
||||
|
@ -404,4 +405,5 @@ describe('Select', () => {
|
|||
});
|
||||
}, 250);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue