antd a-select 下拉列表无限加载封装

1,474 阅读1分钟

需求: 项目中有个select框,后端返回的数据过多。直接使用则会出现点击select后进行选择发现十分卡顿,影响体验。
解决: 利用先获取数据,从中截取一部分,根据下拉框的滚动实现无线加载。

须引入loadsh 用于对下拉操作进行防抖操作

<template>
  <div>
    <a-select
      v-bind="$attrs"
      v-on="$listeners"
      :style="{ width: `${width}` }"
      :placeholder="placeholder"
      :value="value"
      :showSearch="true"
      :allowClear="true"
      :dropdownMatchSelectWidth="false"
      :getPopupContainer="(triggerNode) => triggerNode.parentNode"
      :token-separators="separators"
      option-filter-prop="children"
      @search="handlerFilterOption"
      @popupScroll="handlerScroll"
      @blur="handlerBlur"
      @dropdownVisibleChange="handlerDropdown"
    >
      <a-select-option
        v-for="(item, index) in uniqFilterSelectOptions"
        :key="index"
        :value="item.value"
      >{{ item.label }}</a-select-option>
    </a-select>
  </div>
</template>
 
<script>
export default {
  name: "x-select",
  model: {
    prop: "value",
    event: "change",
  },
  props: {
    // 父组件通过v-model实现父子组件数据选择同步
    value: {
      type: [Array, Number, String],
      default: undefined,
    },
    // 下拉列表数据
    options: {
      type: Array,
      default: () => [],
    },
    width: {
      type: [Number, String],
      default: () => "100%",
    },
    // 初始化和每次滚动显示的最大条数
    maxShowStrips: {
      type: Number,
      default: 15,
    },
    placeholder: {
      type: String,
      default: "请选择",
    },
  },
  data() {
    return {
      separators: [";", ",", "/\n/", " "],
      userSerach: "", // 用户搜索时的内容
      filterOptions: [], // 过滤用户输入后的option
      selected: undefined, // 选择的数据
    };
  },
  computed: {
    // 避免重复插入截取的数据
    uniqFilterSelectOptions() {
      let { filterOptions = [] } = this;
      let optionsList = [];
      return filterOptions.filter((item) => {
        let { value } = item || {};
        if (optionsList.includes(value)) {
          return false;
        } else {
          optionsList.push(value);
          return true;
        }
      });
    },
  },
  methods: {
    // 得到下拉列表的数据
    getSelectOptions() {
      let { userSerach = "", options = [], maxShowStrips = 15 } = this;
      if (!Array.isArray(options)) return;
      let tempFilterOptions = userSerach
        ? options.filter((item) => {
          let { label = "" } = item || {};
          return label && label.indexOf(userSerach) !== -1;
        })
        : options;
 
      tempFilterOptions = tempFilterOptions.length > maxShowStrips
        ? tempFilterOptions.slice(0, maxShowStrips)
        : tempFilterOptions
 
      this.filterOptions = tempFilterOptions;
    },
    // 滚动条滚动,无限拼接数据
    handlerScroll: _.debounce(function () {
      let { options = [], maxShowStrips = 15, filterOptions = [] } = this;
      let currindex = filterOptions.length;
      let allIndex = options.length;
      let nextPageIndex = currindex + maxShowStrips;
      let newFilterOptions =
        nextPageIndex > allIndex
          ? options.slice(currindex, allIndex)
          : options.slice(currindex, nextPageIndex);
      this.filterOptions = filterOptions.concat(newFilterOptions);
    }, 200),
    // 用户改变搜索条件
    handlerFilterOption(value) {
      this.userSerach = value || "";
      this.getSelectOptions();
    },
    // 用户取消选中
    handlerBlur() {
      this.userSerach = "";
      this.getSelectOptions();
    },
    // 展开下拉菜单的回调
    handlerDropdown(flag) {
      if (flag) this.getSelectOptions();
    },
  },
  watch: {
    options: {
      handler() {
        this.getSelectOptions();
      },
      immediate: true,
      deep: true,
    },
  },
};
</script>

使用例子 可多选

<template>
    <x-select
       v-model="value"
       :options="List"
       @change='handleChange'
       placeholder="请选择.."
    />
    <!-- 多选则添加 mode="multiple" -->
</template>
 
<script>
export default {
  components: {
    x-select: () => import("./xSelect"),
  },
  data() {
    return {
      value: undefined,
      List: [],
    };
  },
  methods: {
    handleChange(value){
      console.log(value);
    }
  },
};
</script>