自定义指令实现el-select下拉菜单无限滚动

3,002 阅读2分钟

el-select下拉菜单无限滚

当我们在工作中需要使用到下拉菜单来使用数据,不管是有没有开启远程搜索,在面对数据量特别大的时候又正好遇到需要开启无限滚动的需求,而原生的elementplus组件库中的el-select又没有一个api可以满足这个需要,那么我们除了自己去封装一个下拉菜单,此时就可以在全局封装一个自定义指令来解决这个问题

注册一个Vue的自定义指令,每当使用el-select滚动到列表底部的时候就请求下一页数据,来达到下拉滚动加载更多的目的。

1. 首先创建一个loadmore.ts

export default {
  mounted(el, binding) {
    // 延迟执行,确保元素已渲染完成
    setTimeout(() => {
      // 查找 el 元素中的 .el-select-dropdown .el-select-dropdown__wrap 元素
      const selectWrapper = el.querySelector('.el-select-dropdown .el-select-dropdown__wrap');
      if (selectWrapper) {
        // 给 selectWrapper 添加 scroll 事件监听器
        selectWrapper.addEventListener('scroll', function() {
          const scrollHeight = this.scrollHeight; // 获取元素的滚动高度
          const scrollTop = this.scrollTop; // 获取元素的滚动条垂直位置
          const clientHeight = this.clientHeight; // 获取元素的可见高度
          
          // 如果滚动到接近底部的位置(距离底部小于等于 5 像素)
          if (scrollTop + clientHeight >= scrollHeight - 5) {
            // 调用指令的绑定值(通常是一个方法)
            binding.value();
          }
        });
      }
    }, 0); // 延迟时间为 0,确保代码在当前同步代码执行完成后执行
  },

  // 指令被解绑时调用的钩子
  unmounted(el) {
    // 查找 el 元素中的 .el-select-dropdown .el-select-dropdown__wrap 元素
    const selectWrapper = el.querySelector('.el-select-dropdown .el-select-dropdown__wrap');
    if (selectWrapper) {
      // 移除 scroll 事件监听器
      selectWrapper.removeEventListener('scroll');
    }
  }
};

首先要注意使用时需要用setTimeout来包裹防止dom元素生成的慢从而导致的报错,

el 代表绑定指令的 DOM 元素,从el中拿到.el-select-dropdown .el-select-dropdown__wrap 也就是下拉菜单

unmounted用于注销指令,其余内容可查看注释

2.在全局中注册

  • 在全局中注册自定义指令
import selectLoadMore from '@/utils/loadmore.js';
const app = createApp(App)
app.directive('selectLoadMore', selectLoadMore);

3.在下拉菜单中使用

在需要的地方使用自定义指令,需要注意的地方在于必须要加一个:teleported="false",

他的含义是

teleported是否将下拉列表插入至 body 元素

因为下拉菜单的组件teleported默认值为true,默认会插入到body中的,这会导致自定义指令不生效,他所挂载的组件中找不到.el-select-dropdown .el-select-dropdown__wrap

当设置:teleported="false",时要注意下拉菜单的层级关系防止页面覆盖掉下拉菜单

 <el-select
  v-model="detailData.custId"
  filterable
  remote
  clearable
  reserve-keyword
  :placeholder="$trans('accountManagement.pleasesecustomer')"
  remote-show-suffix
  :remote-method="remoteMethodcustName"
  :loading="loading"
  @clear="queryCustomerMapListFunc"
  v-selectLoadMore="loadMore"
  :teleported="false"
>
  <el-option
    style="width:400px"

    v-for="item in custNameList"
    :key="item.value"
    :label="item.label"
    :value="item.value"
  />
</el-select>

之后就是正常的无限滚动的方法

  const loadMore = () => {
      // 实现加载更多数据的逻辑
      ++listQuery.value.pageNo
      // 模拟加载更多数据
      queryCustomerMapList({ custIdList: {}, ...listQuery.value }).then((res) => {
        const data = res.data.map((item) => ({
          label: item.custName,
          value: item.custId,
        }))
        custNameList.value = [...custNameList.value,...data]
      })
    }