mirror of https://github.com/ElemeFE/element
Select: add value-key
parent
e3f5673487
commit
40a873e93e
|
@ -12,3 +12,4 @@ examples/pages/zh-CN
|
||||||
fe.element/element-ui
|
fe.element/element-ui
|
||||||
.npmrc
|
.npmrc
|
||||||
coverage
|
coverage
|
||||||
|
waiter.config.js
|
||||||
|
|
|
@ -637,11 +637,16 @@ Create and select new items that are not included in select options
|
||||||
```
|
```
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
If the binding value of Select is an object, make sure to assign `value-key` as its unique identity key name.
|
||||||
|
:::
|
||||||
|
|
||||||
### Select Attributes
|
### Select Attributes
|
||||||
| Attribute | Description | Type | Accepted Values | Default |
|
| Attribute | Description | Type | Accepted Values | Default |
|
||||||
|---------- |-------------- |---------- |-------------------------------- |-------- |
|
|---------- |-------------- |---------- |-------------------------------- |-------- |
|
||||||
| multiple | whether multiple-select is activated | boolean | — | false |
|
| multiple | whether multiple-select is activated | boolean | — | false |
|
||||||
| disabled | whether Select is disabled | boolean | — | false |
|
| disabled | whether Select is disabled | boolean | — | false |
|
||||||
|
| value-key | unique identity key name for value, required when value is an object | string | — | value |
|
||||||
| size | size of Input | string | large/small/mini | — |
|
| size | size of Input | string | large/small/mini | — |
|
||||||
| clearable | whether single select can be cleared | boolean | — | false |
|
| clearable | whether single select can be cleared | boolean | — | false |
|
||||||
| multiple-limit | maximum number of options user can select when `multiple` is `true`. No limit when set to 0 | number | — | 0 |
|
| multiple-limit | maximum number of options user can select when `multiple` is `true`. No limit when set to 0 | number | — | 0 |
|
||||||
|
|
|
@ -632,11 +632,16 @@
|
||||||
```
|
```
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
:::tip
|
||||||
|
如果 Select 的绑定值为对象类型,请务必指定 `value-key` 作为它的唯一性标识。
|
||||||
|
:::
|
||||||
|
|
||||||
### Select Attributes
|
### Select Attributes
|
||||||
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|
||||||
|---------- |-------------- |---------- |-------------------------------- |-------- |
|
|---------- |-------------- |---------- |-------------------------------- |-------- |
|
||||||
| multiple | 是否多选 | boolean | — | false |
|
| multiple | 是否多选 | boolean | — | false |
|
||||||
| disabled | 是否禁用 | boolean | — | false |
|
| disabled | 是否禁用 | boolean | — | false |
|
||||||
|
| value-key | 作为 value 唯一标识的键名,绑定值为对象类型时必填 | string | — | value |
|
||||||
| size | 输入框尺寸 | string | large/small/mini | — |
|
| size | 输入框尺寸 | string | large/small/mini | — |
|
||||||
| clearable | 单选时是否可以清空选项 | boolean | — | false |
|
| clearable | 单选时是否可以清空选项 | boolean | — | false |
|
||||||
| multiple-limit | 多选时用户最多可以选择的项目数,为 0 则不限制 | number | — | 0 |
|
| multiple-limit | 多选时用户最多可以选择的项目数,为 0 则不限制 | number | — | 0 |
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
<script type="text/babel">
|
<script type="text/babel">
|
||||||
import Emitter from 'element-ui/src/mixins/emitter';
|
import Emitter from 'element-ui/src/mixins/emitter';
|
||||||
|
import { getValueByPath } from 'element-ui/src/utils/util';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [Emitter],
|
mixins: [Emitter],
|
||||||
|
@ -47,8 +48,13 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
isObject() {
|
||||||
|
const type = typeof this.value;
|
||||||
|
return type !== 'string' && type !== 'number';
|
||||||
|
},
|
||||||
|
|
||||||
currentLabel() {
|
currentLabel() {
|
||||||
return this.label || ((typeof this.value === 'string' || typeof this.value === 'number') ? this.value : '');
|
return this.label || (this.isObject ? '' : this.value);
|
||||||
},
|
},
|
||||||
|
|
||||||
currentValue() {
|
currentValue() {
|
||||||
|
@ -65,9 +71,9 @@
|
||||||
|
|
||||||
itemSelected() {
|
itemSelected() {
|
||||||
if (!this.parent.multiple) {
|
if (!this.parent.multiple) {
|
||||||
return this.value === this.parent.value;
|
return this.isEqual(this.value, this.parent.value);
|
||||||
} else {
|
} else {
|
||||||
return this.parent.value.indexOf(this.value) > -1;
|
return this.contains(this.parent.value, this.value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -92,6 +98,26 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
isEqual(a, b) {
|
||||||
|
if (!this.isObject) {
|
||||||
|
return a === b;
|
||||||
|
} else {
|
||||||
|
const valueKey = this.parent.valueKey;
|
||||||
|
return getValueByPath(a, valueKey) === getValueByPath(b, valueKey);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
contains(arr = [], target) {
|
||||||
|
if (!this.isObject) {
|
||||||
|
return arr.indexOf(target) > -1;
|
||||||
|
} else {
|
||||||
|
const valueKey = this.parent.valueKey;
|
||||||
|
return arr.some(item => {
|
||||||
|
return getValueByPath(item, valueKey) === getValueByPath(target, valueKey);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
handleGroupDisabled(val) {
|
handleGroupDisabled(val) {
|
||||||
this.groupDisabled = val;
|
this.groupDisabled = val;
|
||||||
},
|
},
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
<transition-group @after-leave="resetInputHeight">
|
<transition-group @after-leave="resetInputHeight">
|
||||||
<el-tag
|
<el-tag
|
||||||
v-for="item in selected"
|
v-for="item in selected"
|
||||||
:key="item.value"
|
:key="getValueKey(item)"
|
||||||
closable
|
closable
|
||||||
:hit="item.hitState"
|
:hit="item.hitState"
|
||||||
type="primary"
|
type="primary"
|
||||||
|
@ -104,6 +104,8 @@
|
||||||
import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event';
|
import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event';
|
||||||
import { t } from 'element-ui/src/locale';
|
import { t } from 'element-ui/src/locale';
|
||||||
import scrollIntoView from 'element-ui/src/utils/scroll-into-view';
|
import scrollIntoView from 'element-ui/src/utils/scroll-into-view';
|
||||||
|
import { getValueByPath } from 'element-ui/src/utils/util';
|
||||||
|
|
||||||
const sizeMap = {
|
const sizeMap = {
|
||||||
'large': 42,
|
'large': 42,
|
||||||
'small': 30,
|
'small': 30,
|
||||||
|
@ -193,7 +195,11 @@
|
||||||
return t('el.select.placeholder');
|
return t('el.select.placeholder');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
defaultFirstOption: Boolean
|
defaultFirstOption: Boolean,
|
||||||
|
valueKey: {
|
||||||
|
type: String,
|
||||||
|
default: 'value'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
|
@ -359,15 +365,20 @@
|
||||||
|
|
||||||
getOption(value) {
|
getOption(value) {
|
||||||
let option;
|
let option;
|
||||||
|
const type = typeof value;
|
||||||
|
const isObject = type !== 'string' && type !== 'number';
|
||||||
for (let i = this.cachedOptions.length - 1; i >= 0; i--) {
|
for (let i = this.cachedOptions.length - 1; i >= 0; i--) {
|
||||||
const cachedOption = this.cachedOptions[i];
|
const cachedOption = this.cachedOptions[i];
|
||||||
if (cachedOption.value === value) {
|
const isEqual = isObject
|
||||||
|
? getValueByPath(cachedOption.value, this.valueKey) === getValueByPath(value, this.valueKey)
|
||||||
|
: cachedOption.value === value;
|
||||||
|
if (isEqual) {
|
||||||
option = cachedOption;
|
option = cachedOption;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (option) return option;
|
if (option) return option;
|
||||||
const label = typeof value === 'string' || typeof value === 'number'
|
const label = !isObject
|
||||||
? value : '';
|
? value : '';
|
||||||
let newOption = {
|
let newOption = {
|
||||||
value: value,
|
value: value,
|
||||||
|
@ -497,7 +508,7 @@
|
||||||
handleOptionSelect(option) {
|
handleOptionSelect(option) {
|
||||||
if (this.multiple) {
|
if (this.multiple) {
|
||||||
const value = this.value.slice();
|
const value = this.value.slice();
|
||||||
const optionIndex = value.indexOf(option.value);
|
const optionIndex = this.getValueIndex(value, option.value);
|
||||||
if (optionIndex > -1) {
|
if (optionIndex > -1) {
|
||||||
value.splice(optionIndex, 1);
|
value.splice(optionIndex, 1);
|
||||||
} else if (this.multipleLimit <= 0 || value.length < this.multipleLimit) {
|
} else if (this.multipleLimit <= 0 || value.length < this.multipleLimit) {
|
||||||
|
@ -516,6 +527,25 @@
|
||||||
this.$nextTick(() => this.scrollToOption());
|
this.$nextTick(() => this.scrollToOption());
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getValueIndex(arr = [], value) {
|
||||||
|
const type = typeof value;
|
||||||
|
const isObject = type !== 'string' && type !== 'number';
|
||||||
|
if (!isObject) {
|
||||||
|
return arr.indexOf(value);
|
||||||
|
} else {
|
||||||
|
const valueKey = this.valueKey;
|
||||||
|
let index = -1;
|
||||||
|
arr.some((item, i) => {
|
||||||
|
if (getValueByPath(item, valueKey) === getValueByPath(value, valueKey)) {
|
||||||
|
index = i;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
toggleMenu() {
|
toggleMenu() {
|
||||||
if (this.filterable && this.query === '' && this.visible) {
|
if (this.filterable && this.query === '' && this.visible) {
|
||||||
return;
|
return;
|
||||||
|
@ -626,6 +656,15 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getValueKey(item) {
|
||||||
|
const type = typeof item.value;
|
||||||
|
if (type === 'number' || type === 'string') {
|
||||||
|
return item.value;
|
||||||
|
} else {
|
||||||
|
return getValueByPath(item.value, this.valueKey);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import ElCheckbox from 'element-ui/packages/checkbox';
|
import ElCheckbox from 'element-ui/packages/checkbox';
|
||||||
import ElTag from 'element-ui/packages/tag';
|
import ElTag from 'element-ui/packages/tag';
|
||||||
import objectAssign from 'element-ui/src/utils/merge';
|
import objectAssign from 'element-ui/src/utils/merge';
|
||||||
import { getValueByPath } from './util';
|
import { getValueByPath } from 'element-ui/src/utils/util';
|
||||||
|
|
||||||
let columnIdSeed = 1;
|
let columnIdSeed = 1;
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { getValueByPath } from 'element-ui/src/utils/util';
|
||||||
|
|
||||||
export const getCell = function(event) {
|
export const getCell = function(event) {
|
||||||
let cell = event.target;
|
let cell = event.target;
|
||||||
|
|
||||||
|
@ -11,24 +13,6 @@ export const getCell = function(event) {
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getValueByPath = function(object, prop) {
|
|
||||||
prop = prop || '';
|
|
||||||
const paths = prop.split('.');
|
|
||||||
let current = object;
|
|
||||||
let result = null;
|
|
||||||
for (let i = 0, j = paths.length; i < j; i++) {
|
|
||||||
const path = paths[i];
|
|
||||||
if (!current) break;
|
|
||||||
|
|
||||||
if (i === j - 1) {
|
|
||||||
result = current[path];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
current = current[path];
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
|
|
||||||
const isObject = function(obj) {
|
const isObject = function(obj) {
|
||||||
return obj !== null && typeof obj === 'object';
|
return obj !== null && typeof obj === 'object';
|
||||||
};
|
};
|
||||||
|
|
|
@ -19,3 +19,21 @@ export function toObject(arr) {
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getValueByPath = function(object, prop) {
|
||||||
|
prop = prop || '';
|
||||||
|
const paths = prop.split('.');
|
||||||
|
let current = object;
|
||||||
|
let result = null;
|
||||||
|
for (let i = 0, j = paths.length; i < j; i++) {
|
||||||
|
const path = paths[i];
|
||||||
|
if (!current) break;
|
||||||
|
|
||||||
|
if (i === j - 1) {
|
||||||
|
result = current[path];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
current = current[path];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue