Select 远程查询组件

161 阅读1分钟

使用

<Select
              ref="select"
              v-model="storeId"
              :label="'name'"
              :options="storeList"
              :loading="storeLoading"
              :triggering="triggering"
              :remoteTriggering="remoteTriggering"
              :size="'small'"
              :placeholder="'请选择/输入店铺名字进行查找'"
              @visibleChange="visibleChange"
              @loadMore="loadMore"
              @remoteMethod="remoteMethod"
              @change="storeChange"
            />

data:

        /* 顶部店铺 start*/
      triggering: true, //select是否允许加载更多
      remoteTriggering: true, //select远程是否允许加载更多
      storeId: '',
      storeList: [],
      storeLoading: false,
      selectForm: {
        pageNum: 1,
        pageSize: 10,
        name: ''
      },
      /* 顶部店铺 end*/

methods:

    remoteMethod(query, page) {
      this.selectForm.pageNum = page;
      this.selectForm.name = query;
      this.getStoreListData(true);
    },
    
  loadMore(page, isRemote) {
      this.selectForm.pageNum = page;
      this.getStoreListData(isRemote);
    },
   visibleChange() {
      // 发送请求的查找关键字
      this.selectForm.name = '';
      //远程开启可以下拉
      this.remoteTriggering = true;
    },

接口:

  /* 店铺 start */
    getStoreListData: async function(isRemote) {
      this.storeLoading = true;
      try {
        const res = await moveShopList({
          pageNum: this.selectForm.pageNum,
          pageSize: this.selectForm.pageSize,
          name: this.selectForm.name
        });
        if (res.code === 200) {
          const { list, total } = res.data;
          this.storeList = list;
          //当数据小于10条或者集合的长度大于或者等于后端返回给我们的总数时不能向下触发请求了
          // list.length < 10
          this.$nextTick(() => {
            // 不加这个会多执行一次请求
            let optionsList = this.$refs.select.optionsList;
            /* 远程的触底需要跟原本列表的分开 远程的每次关闭下拉框都需要设置为true */
            if (isRemote) {
              if (optionsList && optionsList.length >= total) {
                this.remoteTriggering = false;
              } else {
                this.remoteTriggering = true;
              }
            } else {
              if (optionsList && optionsList.length >= total) {
                this.triggering = false;
              } else {
                this.triggering = true;
              }
            }
          });
          this.storeLoading = false;
        } else {
          this.storeLoading = false;
          this.$message.error(res.message);
        }
      } catch (error) {
        this.storeLoading = false;
        console.log(error);
      }
    },
    /* 店铺 end */

组件:

<template>
  <div>
    <el-select
      v-el-select-loadmore="loadMore"
      :value="value"
      :size="size"
      :loading="loading"
      :multiple="multiple"
      :placeholder="placeholder"
      :clearable="clearable"
      filterable
      remote
      :remote-method="
        (query) => {
          remoteMethod(query, value);
        }
      "
      style="width: 100%"
      @change="change"
      @input="$emit('input', $event)"
      @visible-change="visibleChange"
    >
      <el-option v-if="hasAll" :label="defaultLabel" value="" />
      <el-option
        v-for="item in optionsList"
        :key="item.id"
        :label="item[label]"
        :value="item.id"
      />
    </el-select>
  </div>
</template>

<script>
export default {
  name: "Select",
  directives: {
    "el-select-loadmore": {
      bind(el, binding) {
        // 获取element-ui定义好的scroll盒子
        const DOM = el.querySelector(
          ".el-select-dropdown .el-select-dropdown__wrap"
        );
        DOM.addEventListener("scroll", function () {
          /**
           * scrollHeight 获取元素内容高度(只读)
           * scrollTop 获取或者设置元素的偏移值,常用于, 计算滚动条的位置, 当一个元素的容器没有产生垂直方向的滚动条, 那它的scrollTop的值默认为0.
           * clientHeight 读取元素的可见高度(只读)
           * 如果元素滚动到底, 下面等式返回true, 没有则返回false:
           * ele.scrollHeight - ele.scrollTop === ele.clientHeight;
           */
          const condition =
            this.scrollHeight - this.scrollTop <= this.clientHeight;
          if (condition) {
            binding.value();
          }
        });
      },
    },
  },
  props: {
    // 传入的数据,必填
    value: {
      type: [String, Number, Array],
      default: null,
    },
    //展示绑定label
    label:{
      require:true,
      type:String,
      default:""
    },
    // 选项数据,必填
    options: {
      type: Array,
      default: () => {
        return [];
      },
    },
    triggering: {
      type: Boolean,
      default: true,
    },
    remoteTriggering: {
      type: Boolean,
      default: true,
    },
    size: {
      type: String,
      default: "medium",
    },
    clearable: {
      type: Boolean,
      default: false,
    },
    // 是否有全部选项
    hasAll: {
      type: Boolean,
      default: false,
    },
    defaultLabel: {
      type: String,
      default: "全部",
    },
    // 加载loading
    loading: {
      type: Boolean,
      default: false,
    },
    // 提示
    placeholder: {
      type: String,
      default: "请选择",
    },
    // 是否支持多选
    multiple: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      page: 1, // 没有开启远程 原本的pageNum
      pageRemote: 1, // 如果搜索的内容很多 也需要触底处理加载更多
      timer: null,
      optionsList: [], //下拉框展示的数据
      oldOptions: [], // 远程搜索了 选中了分页的数据 再关闭下拉框 再次打开下拉框是不是应该展现上一次关键字查询的记录列表  这里是把查询的结果拼接到没有远程之前的列表 这样回显就能看到了
      isRemote: false, //是否开启远程 输入内容为true
    };
  },
  watch: {
    options: {
      handler(val) {
        if (this.isRemote) {
          if (val) {
            // this.optionsList = val;
            if (this.pageRemote > 1) {
              this.optionsList = [...this.optionsList, ...val];
            } else {
              this.optionsList = val;
            }
            console.log("this.oldOptions", this.oldOptions);
            this.oldOptions = this.oldOptions.filter((item) => {
              return !val.some((valItem) => item.id === valItem.id);
            });
            this.oldOptions = [...this.oldOptions, ...val];
          }
        } else {
          /* 去除旧数组中出现有新数组一样的数据  为什么写这段?因为比如page在第一页 远程查询的内容在第二页 查询到的内容会拼接在原来的数组 原来的数据再次下拉刷新 就会遇到重复的数据 */
          if (val) {
            this.optionsList = this.optionsList.filter((item) => {
              return !val.some((valItem) => item.id === valItem.id);
            });
            this.optionsList = [...this.optionsList, ...val];
          }
        }
      },
      deep: true,
    },
  },
  mounted() {
    this.optionsList = this.options;
  },
  methods: {
    change(val) {
      this.$emit("change", val);
    },
    visibleChange(status) {
      //=status 出现下拉框为true 收回去为false
      if (!status) {
        if (this.isRemote) {
          this.isRemote = false;
          this.optionsList = [...this.oldOptions];
        }
      }
      this.$emit("visibleChange", status);
    },
    loadMore() {
      console.log(this.isRemote, this.triggering, this.remoteTriggering);
      if (this.isRemote) {
        // if (this.pageRemote === 1) {
        //   console.log("是1");
        //   this.$emit('loadMore', this.pageRemote)
        //   this.pageRemote++
        // } else {
        //   if(this.triggering === false) return
        //   this.pageRemote++
        //   this.$emit('loadMore', this.pageRemote)
        // }
        /* 开启默认列表可以下拉 不需要了 */
        // this.triggering = true;
        // this.$emit('TriggeringSetTrue')
        if (this.remoteTriggering === false) return;
        this.pageRemote++;
        /* 第三个参数是开启远程 */
        this.$emit("loadMore", this.pageRemote,true);
      } else {
        if (this.triggering === false) return;
        console.log("this.page", this.page);
        this.page++;
        this.$emit("loadMore", this.page);
      }
    },
    remoteMethod(query) {//删除input框的内容 也会触发 第一次把数据拉到最底部再开启远程 远程搜索完毕 没有选中 清空 这个时候再次触发下拉也是属于远程的页数加的是pageRemote 
      console.log("remoteMethod");
      this.pageRemote = 1;
      if (this.timer) {
        clearTimeout(this.timer);
        this.timer = null;
      }
      this.timer = setTimeout(() => {
        this.isRemote = true;
        this.oldOptions = [...this.optionsList];
        this.optionsList = [];
        this.$emit("remoteMethod", query, this.pageRemote);
      }, 500);
    },
  },
};
</script>

<style lang='scss' scoped>
</style>