|
@@ -0,0 +1,391 @@
|
|
|
+<template>
|
|
|
+ <div
|
|
|
+ class="el-select"
|
|
|
+ :class="[selectSize ? 'el-select--' + selectSize : '']"
|
|
|
+ @click.stop="toggleMenu"
|
|
|
+ v-clickoutside="handleClose"
|
|
|
+ >
|
|
|
+ <!-- update author: tangdm descript: add overflow: 'hidden' -->
|
|
|
+ <div
|
|
|
+ class="el-select__tags"
|
|
|
+ v-if="multiple"
|
|
|
+ ref="tags"
|
|
|
+ :style="{ 'max-width': inputWidth - 32 + 'px', width: '100%', overflow: 'hidden' }"
|
|
|
+ >
|
|
|
+ <span v-if="collapseTags && selected.length">
|
|
|
+ <el-tag
|
|
|
+ :closable="!selectDisabled"
|
|
|
+ :size="collapseTagSize"
|
|
|
+ :hit="selected[0].hitState"
|
|
|
+ @close="deleteTag($event, selected[0])"
|
|
|
+ disable-transitions
|
|
|
+ >
|
|
|
+ <span class="el-select__tags-text" :title="selected[0].currentLabel">{{ selected[0].currentLabel }}</span>
|
|
|
+ </el-tag>
|
|
|
+ <el-tag v-if="selected.length > 1" :closable="false" :size="collapseTagSize" disable-transitions>
|
|
|
+ <span class="el-select__tags-text">+ {{ selected.length - 1 }}</span>
|
|
|
+ </el-tag>
|
|
|
+ </span>
|
|
|
+ <!-- update author: tangdm descript: add dragTags -->
|
|
|
+ <transition-group @after-leave="resetInputHeight" v-if="!collapseTags && !dragTags">
|
|
|
+ <el-tag
|
|
|
+ v-for="item in selected"
|
|
|
+ :key="getValueKey(item)"
|
|
|
+ :closable="!selectDisabled"
|
|
|
+ :size="collapseTagSize"
|
|
|
+ :hit="item.hitState"
|
|
|
+ @close="deleteTag($event, item)"
|
|
|
+ disable-transitions
|
|
|
+ >
|
|
|
+ <span class="el-select__tags-text" :title="item.currentLabel">{{ item.currentLabel }}</span>
|
|
|
+ </el-tag>
|
|
|
+ </transition-group>
|
|
|
+ <!-- add author: tangdm descript: drag-tags content -->
|
|
|
+ <transition @after-leave="resetInputHeight" v-if="!collapseTags && dragTags">
|
|
|
+ <div class="dg-drag-tags__content" ref="tagContent" :style="[{ width: tagItemLength + 'px' }]">
|
|
|
+ <el-tag
|
|
|
+ v-for="item in selected"
|
|
|
+ :key="getValueKey(item)"
|
|
|
+ :closable="!selectDisabled"
|
|
|
+ :size="collapseTagSize"
|
|
|
+ :hit="item.hitState"
|
|
|
+ ref="tagItem"
|
|
|
+ @close="deleteTag($event, item)"
|
|
|
+ disable-transitions
|
|
|
+ >
|
|
|
+ <span class="el-select__tags-text" :title="item.currentLabel">{{ item.currentLabel }}</span>
|
|
|
+ </el-tag>
|
|
|
+ </div>
|
|
|
+ </transition>
|
|
|
+ <input
|
|
|
+ type="text"
|
|
|
+ class="el-select__input"
|
|
|
+ :class="[selectSize ? `is-${selectSize}` : '']"
|
|
|
+ :disabled="selectDisabled"
|
|
|
+ :autocomplete="autoComplete || autocomplete"
|
|
|
+ @focus="handleFocus"
|
|
|
+ @blur="softFocus = false"
|
|
|
+ @click.stop
|
|
|
+ @keyup="managePlaceholder"
|
|
|
+ @keydown="resetInputState"
|
|
|
+ @keydown.down.prevent="navigateOptions('next')"
|
|
|
+ @keydown.up.prevent="navigateOptions('prev')"
|
|
|
+ @keydown.enter.prevent="selectOption"
|
|
|
+ @keydown.esc.stop.prevent="visible = false"
|
|
|
+ @keydown.delete="deletePrevTag"
|
|
|
+ @compositionstart="handleComposition"
|
|
|
+ @compositionupdate="handleComposition"
|
|
|
+ @compositionend="handleComposition"
|
|
|
+ v-model="query"
|
|
|
+ @input="debouncedQueryChange"
|
|
|
+ v-if="filterable"
|
|
|
+ :style="{
|
|
|
+ 'flex-grow': '1',
|
|
|
+ width: inputLength / (inputWidth - 32) + '%',
|
|
|
+ 'max-width': inputWidth - 42 + 'px'
|
|
|
+ }"
|
|
|
+ ref="input"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <el-input
|
|
|
+ ref="reference"
|
|
|
+ v-model="selectedLabel"
|
|
|
+ type="text"
|
|
|
+ :placeholder="currentPlaceholder"
|
|
|
+ :name="name"
|
|
|
+ :id="id"
|
|
|
+ :autocomplete="autoComplete || autocomplete"
|
|
|
+ :size="selectSize"
|
|
|
+ :disabled="selectDisabled"
|
|
|
+ :readonly="readonly"
|
|
|
+ :validate-event="false"
|
|
|
+ :class="{ 'is-focus': visible }"
|
|
|
+ @focus="handleFocus"
|
|
|
+ @blur="handleBlur"
|
|
|
+ @keyup.native="debouncedOnInputChange"
|
|
|
+ @keydown.native.down.stop.prevent="navigateOptions('next')"
|
|
|
+ @keydown.native.up.stop.prevent="navigateOptions('prev')"
|
|
|
+ @keydown.native.enter.prevent="selectOption"
|
|
|
+ @keydown.native.esc.stop.prevent="visible = false"
|
|
|
+ @keydown.native.tab="visible = false"
|
|
|
+ @paste.native="debouncedOnInputChange"
|
|
|
+ @mouseenter.native="inputHovering = true"
|
|
|
+ @mouseleave.native="inputHovering = false"
|
|
|
+ >
|
|
|
+ <template slot="prefix" v-if="$slots.prefix">
|
|
|
+ <slot name="prefix"></slot>
|
|
|
+ </template>
|
|
|
+ <template slot="suffix">
|
|
|
+ <i v-show="!showClose" :class="['el-select__caret', 'el-input__icon', 'el-icon-' + iconClass]"></i>
|
|
|
+ <i v-if="showClose" class="el-select__caret el-input__icon el-icon-circle-close" @click="handleClearClick"></i>
|
|
|
+ </template>
|
|
|
+ </el-input>
|
|
|
+ <transition name="el-zoom-in-top" @before-enter="handleMenuEnter" @after-leave="doDestroy">
|
|
|
+ <el-select-menu ref="popper" :append-to-body="popperAppendToBody" v-show="visible && emptyText !== false">
|
|
|
+ <!-- <template v-if="emptyText && (!allowCreate || loading || (allowCreate && options.length === 0 ))">
|
|
|
+ <slot name="empty" v-if="$slots.empty"></slot>
|
|
|
+ <p class="el-select-dropdown__empty" v-else>
|
|
|
+ {{ emptyText }}
|
|
|
+ </p>
|
|
|
+ </template> -->
|
|
|
+ <slot></slot>
|
|
|
+ </el-select-menu>
|
|
|
+ </transition>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script type="text/babel">
|
|
|
+import { Select as ElSelect } from 'element-ui';
|
|
|
+import debounce from 'throttle-debounce/debounce';
|
|
|
+import { addResizeListener } from '@uiSrcPath/utils/resize-event.js';
|
|
|
+import { complex } from '@uiSrcPath/dg-utils/shear.js';
|
|
|
+// import { valueEquals } from '@uiSrcPath/utils/util.js';
|
|
|
+import _ from 'lodash';
|
|
|
+
|
|
|
+ElSelect.components.ElSelectMenu.watch['$parent.inputWidth'] = function () {
|
|
|
+ this.minWidth = Math.max(this.$parent.$el.getBoundingClientRect().width, this.$parent.$el.clientWidth) + 'px';
|
|
|
+};
|
|
|
+
|
|
|
+const Select = complex(ElSelect, ['created', 'mounted']);
|
|
|
+
|
|
|
+export default {
|
|
|
+ mixins: [Select],
|
|
|
+
|
|
|
+ props: {
|
|
|
+ // add author: tangdm
|
|
|
+ dragTags: Boolean,
|
|
|
+ defaultProp: Object
|
|
|
+ },
|
|
|
+
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ dragStatus: false,
|
|
|
+ tagItemLength: 0,
|
|
|
+ debounceTime: 300,
|
|
|
+ oldValue: ''
|
|
|
+ };
|
|
|
+ },
|
|
|
+
|
|
|
+ computed: {
|
|
|
+ scrollbarCls() {
|
|
|
+ const { allowCreate, query, filteredOptionsCount, scrollbarClass } = this;
|
|
|
+ return [!allowCreate && query && filteredOptionsCount === 0 ? 'is-empty' : '', scrollbarClass || ''].join(' ');
|
|
|
+ },
|
|
|
+ // 覆盖element ui - select中方法
|
|
|
+ debounce() {
|
|
|
+ return this.debounceTime;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ watch: {
|
|
|
+ selectDisabled() {
|
|
|
+ // create author:lutz describe:!multiple不渲染
|
|
|
+ if (!this.multiple) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.resetInputHeight();
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ // add author: tangdm
|
|
|
+ selected() {
|
|
|
+ this.$nextTick(() => {
|
|
|
+ // 检测判断标签值是否存在
|
|
|
+ if (this.$refs.tagContent) {
|
|
|
+ this.$refs.tagContent.style.transform = 'translateX(0px)';
|
|
|
+ }
|
|
|
+
|
|
|
+ // 计算标签数量值的长度
|
|
|
+ let count = 0;
|
|
|
+ let tagItem = this.$refs.tagItem;
|
|
|
+ if (tagItem) {
|
|
|
+ tagItem.forEach((item) => {
|
|
|
+ count += item.$el.offsetWidth + 8;
|
|
|
+ });
|
|
|
+ }
|
|
|
+ this.tagItemLength = count;
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ methods: {
|
|
|
+ valueEquals(a, b) {
|
|
|
+ // see: https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
|
|
|
+ if (a === b) return true;
|
|
|
+ if (!(a instanceof Array)) return false;
|
|
|
+ if (!(b instanceof Array)) return false;
|
|
|
+ if (a.length !== b.length) return false;
|
|
|
+ for (let i = 0; i !== a.length; ++i) {
|
|
|
+ if (a[i] !== b[i]) return false;
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ },
|
|
|
+ resetInputHeight() {
|
|
|
+ if (this.collapseTags && !this.filterable) return;
|
|
|
+ this.$nextTick(() => {
|
|
|
+ if (!this.$refs.reference) return;
|
|
|
+ let inputChildNodes = this.$refs.reference.$el.childNodes;
|
|
|
+ let input = [].filter.call(inputChildNodes, (item) => item.tagName === 'INPUT')[0];
|
|
|
+ const tags = this.$refs.tags;
|
|
|
+ const sizeInMap = this.initialInputHeight || 40;
|
|
|
+
|
|
|
+ // update author: tangdm descript: 不同分辨率兼容高度问题
|
|
|
+ // [code] input.style.height = this.selected.length === 0
|
|
|
+ // ? sizeInMap + 'px'
|
|
|
+ // : Math.max(
|
|
|
+ // tags ? (tags.clientHeight + (tags.clientHeight > sizeInMap ? 6 : 0)) : 0,
|
|
|
+ // sizeInMap
|
|
|
+ // ) + 'px';
|
|
|
+
|
|
|
+ input.style.height =
|
|
|
+ this.selected.length === 0 ? sizeInMap + 'px' : Math.max(tags ? tags.clientHeight : 0, sizeInMap) + 'px';
|
|
|
+ if (this.visible && this.emptyText !== false) {
|
|
|
+ this.broadcast('ElSelectDropdown', 'updatePopper');
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ toggleMenu() {
|
|
|
+ // update author: tangdm descript: 拖拽状态判断
|
|
|
+ if (this.dragStatus) {
|
|
|
+ this.dragStatus = false;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!this.selectDisabled) {
|
|
|
+ if (this.menuVisibleOnFocus) {
|
|
|
+ this.menuVisibleOnFocus = false;
|
|
|
+ } else {
|
|
|
+ this.visible = !this.visible;
|
|
|
+ }
|
|
|
+ if (this.visible) {
|
|
|
+ (this.$refs.input || this.$refs.reference).focus();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // @update @author:lutz @descript:添加 change 事件参数
|
|
|
+ emitChange(val) {
|
|
|
+ const { getLabel } = this;
|
|
|
+ if (!this.valueEquals(this.value, val)) {
|
|
|
+ this.$emit('change', val, getLabel(val));
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // @add @author:lutz @descript:获取过滤值
|
|
|
+ getLabel(val) {
|
|
|
+ const { data } = this.$attrs;
|
|
|
+ const { value } = this.defaultProp;
|
|
|
+ return _.isArray(val)
|
|
|
+ ? _.intersectionBy(
|
|
|
+ data,
|
|
|
+ val.map((item) => ({ [value]: item })),
|
|
|
+ value
|
|
|
+ )
|
|
|
+ : _.find(data, (item) => val === item[value]);
|
|
|
+ },
|
|
|
+
|
|
|
+ resetInputWidth() {
|
|
|
+ // update author: tangdm [code] this.inputWidth = this.$refs.reference.$el.getBoundingClientRect().width;
|
|
|
+ // descript: element count item's width for error in layers.
|
|
|
+ this.inputWidth = Math.max(
|
|
|
+ this.$refs.reference.$el.getBoundingClientRect().width,
|
|
|
+ this.$refs.reference.$el.clientWidth
|
|
|
+ );
|
|
|
+ },
|
|
|
+
|
|
|
+ // 下拉触发
|
|
|
+ handleScrollToBottom() {
|
|
|
+ this.$emit('scroll-bottom');
|
|
|
+ },
|
|
|
+ handleQueryChange(val) {
|
|
|
+ if (this.oldValue === val || this.isOnComposition) return;
|
|
|
+ this.oldValue = val;
|
|
|
+ this.$nextTick(() => {
|
|
|
+ if (this.visible) this.broadcast('ElSelectDropdown', 'updatePopper');
|
|
|
+ });
|
|
|
+ this.hoverIndex = -1;
|
|
|
+ if (this.multiple && this.filterable) {
|
|
|
+ this.$nextTick(() => {
|
|
|
+ const length = this.$refs.input.value.length * 15 + 20;
|
|
|
+ this.inputLength = this.collapseTags ? Math.min(50, length) : length;
|
|
|
+ this.managePlaceholder();
|
|
|
+ this.resetInputHeight();
|
|
|
+ });
|
|
|
+ }
|
|
|
+ if (this.remote && typeof this.remoteMethod === 'function') {
|
|
|
+ this.hoverIndex = -1;
|
|
|
+ this.remoteMethod(val);
|
|
|
+ } else if (typeof this.filterMethod === 'function') {
|
|
|
+ this.filterMethod(val);
|
|
|
+ this.broadcast('ElOptionGroup', 'queryChange');
|
|
|
+ } else {
|
|
|
+ this.filteredOptionsCount = this.optionsCount;
|
|
|
+ this.broadcast('ElOption', 'queryChange', val);
|
|
|
+ this.broadcast('ElOptionGroup', 'queryChange');
|
|
|
+ }
|
|
|
+ if (this.defaultFirstOption && (this.filterable || this.remote) && this.filteredOptionsCount) {
|
|
|
+ this.checkDefaultFirstOption();
|
|
|
+ }
|
|
|
+ this.$emit('filter-org', val);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ created() {
|
|
|
+ this.cachedPlaceHolder = this.currentPlaceholder = this.placeholder;
|
|
|
+ if (this.multiple && !Array.isArray(this.value)) {
|
|
|
+ this.$emit('input', []);
|
|
|
+ }
|
|
|
+ if (!this.multiple && Array.isArray(this.value)) {
|
|
|
+ this.$emit('input', '');
|
|
|
+ }
|
|
|
+
|
|
|
+ this.debouncedOnInputChange = debounce(this.debounce, () => {
|
|
|
+ this.onInputChange();
|
|
|
+ });
|
|
|
+
|
|
|
+ this.debouncedQueryChange = debounce(this.debounce, (e) => {
|
|
|
+ this.handleQueryChange(e.target.value);
|
|
|
+ });
|
|
|
+
|
|
|
+ this.$on('handleOptionClick', this.handleOptionSelect);
|
|
|
+ this.$on('setSelected', this.setSelected);
|
|
|
+ },
|
|
|
+
|
|
|
+ mounted() {
|
|
|
+ if (this.multiple && Array.isArray(this.value) && this.value.length > 0) {
|
|
|
+ this.currentPlaceholder = '';
|
|
|
+ }
|
|
|
+ addResizeListener(this.$el, this.handleResize);
|
|
|
+
|
|
|
+ const reference = this.$refs.reference;
|
|
|
+ if (reference && reference.$el) {
|
|
|
+ const sizeMap = {
|
|
|
+ medium: 36,
|
|
|
+ small: 32,
|
|
|
+ mini: 28
|
|
|
+ };
|
|
|
+ const input = reference.$el.querySelector('input');
|
|
|
+
|
|
|
+ // add author: tangdm descript: 增加分辨率识别
|
|
|
+ const clientBodyWidth = document.body.clientWidth;
|
|
|
+ const autoFixedHeight = clientBodyWidth < 1366 ? 24 : clientBodyWidth < 1681 ? 28 : 32;
|
|
|
+
|
|
|
+ // update author: tangdm [code] this.initialInputHeight = input.getBoundingClientRect().height || sizeMap[this.selectSize];
|
|
|
+ this.initialInputHeight = sizeMap[this.selectSize] || autoFixedHeight;
|
|
|
+ }
|
|
|
+ if (this.remote && this.multiple) {
|
|
|
+ this.resetInputHeight();
|
|
|
+ }
|
|
|
+ this.$nextTick(() => {
|
|
|
+ if (reference && reference.$el) {
|
|
|
+ // update author: tangdm [code] this.inputWidth = reference.$el.getBoundingClientRect().width;
|
|
|
+ this.inputWidth = Math.max(reference.$el.getBoundingClientRect().width, reference.$el.clientWidth);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ this.setSelected();
|
|
|
+ }
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+
|