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
 | 
			
		||||
.npmrc
 | 
			
		||||
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
 | 
			
		||||
| Attribute      | Description          | Type      | Accepted Values       | Default  |
 | 
			
		||||
|---------- |-------------- |---------- |--------------------------------  |-------- |
 | 
			
		||||
| multiple | whether multiple-select is activated | 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 | — |
 | 
			
		||||
| 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 |
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -632,11 +632,16 @@
 | 
			
		|||
```
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
:::tip
 | 
			
		||||
如果 Select 的绑定值为对象类型,请务必指定 `value-key` 作为它的唯一性标识。
 | 
			
		||||
:::
 | 
			
		||||
 | 
			
		||||
### Select Attributes 
 | 
			
		||||
| 参数      | 说明          | 类型      | 可选值                           | 默认值  |
 | 
			
		||||
|---------- |-------------- |---------- |--------------------------------  |-------- |
 | 
			
		||||
| multiple | 是否多选 | boolean | — | false |
 | 
			
		||||
| disabled | 是否禁用 | boolean | — | false |
 | 
			
		||||
| value-key | 作为 value 唯一标识的键名,绑定值为对象类型时必填 | string | — | value |
 | 
			
		||||
| size | 输入框尺寸 | string | large/small/mini | — |
 | 
			
		||||
| clearable | 单选时是否可以清空选项 | boolean | — | false |
 | 
			
		||||
| multiple-limit | 多选时用户最多可以选择的项目数,为 0 则不限制 | number | — | 0 |
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,6 +17,7 @@
 | 
			
		|||
 | 
			
		||||
<script type="text/babel">
 | 
			
		||||
  import Emitter from 'element-ui/src/mixins/emitter';
 | 
			
		||||
  import { getValueByPath } from 'element-ui/src/utils/util';
 | 
			
		||||
 | 
			
		||||
  export default {
 | 
			
		||||
    mixins: [Emitter],
 | 
			
		||||
| 
						 | 
				
			
			@ -47,8 +48,13 @@
 | 
			
		|||
    },
 | 
			
		||||
 | 
			
		||||
    computed: {
 | 
			
		||||
      isObject() {
 | 
			
		||||
        const type = typeof this.value;
 | 
			
		||||
        return type !== 'string' && type !== 'number';
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
      currentLabel() {
 | 
			
		||||
        return this.label || ((typeof this.value === 'string' || typeof this.value === 'number') ? this.value : '');
 | 
			
		||||
        return this.label || (this.isObject ? '' : this.value);
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
      currentValue() {
 | 
			
		||||
| 
						 | 
				
			
			@ -65,9 +71,9 @@
 | 
			
		|||
 | 
			
		||||
      itemSelected() {
 | 
			
		||||
        if (!this.parent.multiple) {
 | 
			
		||||
          return this.value === this.parent.value;
 | 
			
		||||
          return this.isEqual(this.value, this.parent.value);
 | 
			
		||||
        } else {
 | 
			
		||||
          return this.parent.value.indexOf(this.value) > -1;
 | 
			
		||||
          return this.contains(this.parent.value, this.value);
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -92,6 +98,26 @@
 | 
			
		|||
    },
 | 
			
		||||
 | 
			
		||||
    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) {
 | 
			
		||||
        this.groupDisabled = val;
 | 
			
		||||
      },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,7 +11,7 @@
 | 
			
		|||
      <transition-group @after-leave="resetInputHeight">
 | 
			
		||||
        <el-tag
 | 
			
		||||
          v-for="item in selected"
 | 
			
		||||
          :key="item.value"
 | 
			
		||||
          :key="getValueKey(item)"
 | 
			
		||||
          closable
 | 
			
		||||
          :hit="item.hitState"
 | 
			
		||||
          type="primary"
 | 
			
		||||
| 
						 | 
				
			
			@ -104,6 +104,8 @@
 | 
			
		|||
  import { addResizeListener, removeResizeListener } from 'element-ui/src/utils/resize-event';
 | 
			
		||||
  import { t } from 'element-ui/src/locale';
 | 
			
		||||
  import scrollIntoView from 'element-ui/src/utils/scroll-into-view';
 | 
			
		||||
  import { getValueByPath } from 'element-ui/src/utils/util';
 | 
			
		||||
 | 
			
		||||
  const sizeMap = {
 | 
			
		||||
    'large': 42,
 | 
			
		||||
    'small': 30,
 | 
			
		||||
| 
						 | 
				
			
			@ -193,7 +195,11 @@
 | 
			
		|||
          return t('el.select.placeholder');
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
      defaultFirstOption: Boolean
 | 
			
		||||
      defaultFirstOption: Boolean,
 | 
			
		||||
      valueKey: {
 | 
			
		||||
        type: String,
 | 
			
		||||
        default: 'value'
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    data() {
 | 
			
		||||
| 
						 | 
				
			
			@ -359,15 +365,20 @@
 | 
			
		|||
 | 
			
		||||
      getOption(value) {
 | 
			
		||||
        let option;
 | 
			
		||||
        const type = typeof value;
 | 
			
		||||
        const isObject = type !== 'string' && type !== 'number';
 | 
			
		||||
        for (let i = this.cachedOptions.length - 1; i >= 0; 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;
 | 
			
		||||
            break;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        if (option) return option;
 | 
			
		||||
        const label = typeof value === 'string' || typeof value === 'number'
 | 
			
		||||
        const label = !isObject
 | 
			
		||||
          ? value : '';
 | 
			
		||||
        let newOption = {
 | 
			
		||||
          value: value,
 | 
			
		||||
| 
						 | 
				
			
			@ -497,7 +508,7 @@
 | 
			
		|||
      handleOptionSelect(option) {
 | 
			
		||||
        if (this.multiple) {
 | 
			
		||||
          const value = this.value.slice();
 | 
			
		||||
          const optionIndex = value.indexOf(option.value);
 | 
			
		||||
          const optionIndex = this.getValueIndex(value, option.value);
 | 
			
		||||
          if (optionIndex > -1) {
 | 
			
		||||
            value.splice(optionIndex, 1);
 | 
			
		||||
          } else if (this.multipleLimit <= 0 || value.length < this.multipleLimit) {
 | 
			
		||||
| 
						 | 
				
			
			@ -516,6 +527,25 @@
 | 
			
		|||
        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() {
 | 
			
		||||
        if (this.filterable && this.query === '' && this.visible) {
 | 
			
		||||
          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 ElTag from 'element-ui/packages/tag';
 | 
			
		||||
import objectAssign from 'element-ui/src/utils/merge';
 | 
			
		||||
import { getValueByPath } from './util';
 | 
			
		||||
import { getValueByPath } from 'element-ui/src/utils/util';
 | 
			
		||||
 | 
			
		||||
let columnIdSeed = 1;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,3 +1,5 @@
 | 
			
		|||
import { getValueByPath } from 'element-ui/src/utils/util';
 | 
			
		||||
 | 
			
		||||
export const getCell = function(event) {
 | 
			
		||||
  let cell = event.target;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -11,24 +13,6 @@ export const getCell = function(event) {
 | 
			
		|||
  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) {
 | 
			
		||||
  return obj !== null && typeof obj === 'object';
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,3 +19,21 @@ export function toObject(arr) {
 | 
			
		|||
  }
 | 
			
		||||
  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