SOURCE

<!--
  下拉框组件
  modelValue:双向绑定
  data: 下拉可选项
  disabled: 是否禁用
-->

<template>
  <el-select
    v-bind="props.props"
    :modelValue="modelValue"
    fit-input-width
    :suffix-icon="elSelectArrowIcon"
    :disabled="disabled"
    :filterable="filterable"
    :placeholder="placeholder"
    :popper-class="props.props.popperClass"
    :remote-method="remoteMethod"
    @change="handleInput"
    @keyup.enter="keyupEnter()"
    @blur="blur"
    @clear="clear"
    @focus="focus"
    v-limitLength="props.maxlength"
  >
    <div
      :infiniteScrollImmediate="false"
      :infinite-scroll-delay="500"
      :infiniteScrollDisabled="!props?.props?.infiniteScroll"
      v-infinite-scroll="scrollLoad"
      ref="scrollRef"
    >
      <el-option
        v-for="(item, index) in selectData"
        :key="item + index"
        :label="item.label"
        :value="item.value"
      >
        <BaseToolTip placement="top" :rows="1" :content="item.label">
          {{ item.label }}
        </BaseToolTip>
      </el-option>
    </div>
  </el-select>
</template>
<script>
import { defineComponent } from 'vue';
export default defineComponent({
  type: 'BaseSelect',
  inheritAttrs: false,
});
</script>
<script setup>
import SvgIcon from '@/components/autoImport/SvgIcon/index.vue';
import { computed, h, ref } from 'vue';

const props = defineProps({
  modelValue: {
    type: [String, Number],
    default: '',
  },
  options: {
    type: Array,
    default: () => [],
    validator(value) {
      const arr = ['label', 'value'];
      const result = value.every(item => arr.every(key => Object.keys(item).includes(key)));
      return result;
    },
  },
  disabled: {
    type: Boolean,
    default: false,
  },
  placeholder: {
    type: String,
    default: '请选择',
  },
  filterable: {
    type: Boolean,
    default: false,
  },
  props: {
    type: Object,
  },
  maxlength: {
    type: Number,
  },
});

const elSelectArrowIcon = (props, context) =>
  h(SvgIcon, {
    iconClass: 'el-select-arrow-icon',
    className: 'el-select-arrow-icon',
  });

const pageNo = ref(1);
const keyword = ref('');
const scrollRef = ref(null);

const selectData = computed(() => [...props.options]);

const emits = defineEmits(['change', 'focus', 'blur']);

const handleInput = value => {
  emits('update:modelValue', `${value}`.trim());
  emits('change', `${value}`.trim());
};

// 回车触发查询
const keyupEnter = (val = props.modelValue, isResetPageNo = true) => {
  const str = `${val || ''}`.trim();
  keyword.value = str;
  isResetPageNo && (pageNo.value = 1);
  if (props?.props?.remote && props?.props?.remoteMethod) {
    props.props.remoteMethod(str, { pageNo: pageNo.value });
  }
};

// 触发远程搜索
const remoteMethod = (val, isResetPageNo = true) => {
  if (isResetPageNo) {
    scrollRef.value.parentNode.parentNode.scrollTop = 0;
    !(keyword.value === val && !val && selectData.value.length) && keyupEnter(val);
  } else {
    keyupEnter(val);
  }
};

// 触发滚动加载回调
const scrollLoad = () => {
  if (props?.props?.infiniteScroll) {
    pageNo.value = pageNo.value + 1; // 翻页+1
    keyupEnter(keyword.value, false);
  }
};

const clear = () => {
  keyupEnter('');
};

const blur = () => {
  emits('blur');
};

const focus = e => {
  !keyword.value && !selectData.value.length && keyupEnter();
  emits('focus', e);
};

const vLimitLength = {
  created: (el, { value }) => {
    const input = el.getElementsByTagName('input')[0];
    if (input) {
      input.setAttribute('maxlength', value);
    }
  },
};
</script>
<style lang="scss">
.el-select {
  width: 100%;
  .el-input {
    &.is-focus {
      .el-input__inner {
        border-color: #2367f1;
      }
    }
    .el-input__suffix {
      right: 8px;
    }
  }
  .el-input__inner:focus {
    border-color: #2367f1;
  }
  &:hover {
    .el-input__inner {
      border-color: #2367f1;
    }
  }
  .el-select-arrow-icon {
    width: 14px;
    height: 6px;
  }
}

.el-popper.el-select__popper {
  &[role='tooltip'] {
    background: #ffffff;
    box-shadow: 0px 2px 6px 0px rgba(10, 80, 200, 0.18);
    border: 0;
  }
  .el-select-dropdown {
    border-radius: 2px;
    .el-select-dropdown__list {
      padding: 10px 0;
      .el-select-dropdown__item {
        height: 36px;
        line-height: 36px;
        color: #333;
        font-size: 14px;
        padding-left: 8px;
        padding-right: 10px;
        &:hover {
          background-color: #e7eff9;
        }
        &.selected {
          color: #2367f1;
          font-weight: normal;
        }
        &.hover {
          background-color: #fff;
          &:hover {
            background-color: #e7eff9;
          }
        }
      }
    }
  }
  .el-popper__arrow {
    display: none;
  }
}
</style>
console 命令行工具 X clear

                    
>
console