elementplus 二次封装 select 自定义指令上拉加载更多 完美解决 多次接口调用 重新加载数据多次调用数据!!!

效果:(名字都是测试数据 随便乱写的 若有冒犯 请联系)

select 二次封装

网上的这种自定义指令上拉加载更多的实例有很多,但是基本都是有缺陷和问题的。为了记录这个问题 我研究了一天,在今天终于搞定了 呜呜呜。

网上遇到的问题:

1、封装的自定义指令没法用(后续会讲解原理)

2、封装完 select 之后 如果一个页面有多个封装的 select 会导致多次触发加载更多。比如一个页面有3个 select 其中一个触发了加载更多 则其他两个也会同时触发(如果是调接口则会触发三次)(后续会讲解原理)

3、当上拉触底多次(比如调用接口可能是 pageindex:1, pageindex:2, pageindex:3 获取三次的数据),这个时候点击其他区域 将 option 隐藏起来 这时候重新点击 select 会重新触发三次的接口请求。(后续会讲解原理)

开始二次封装 selec 组件(只写重点代码其他自己参考)

"element-plus": "^2.8.0",
"vue": "^3.2.47",

1、子组件

1、components/el-load-search-select/index.vue

<template>
  <!-- :popper-class-name 和 :popper-class 和 v-loadmore 为这里的重点!!!!-->
  <el-select
    :popper-class-name="popperClassName"
    v-model="selectedValue"
    v-loadmore="handleLoadMore"
    placeholder="请选择"
    :popper-class="popperClassName"
    :multiple="!isOnlySingle"
    collapse-tags
    reserve-keyword
    v-bind="$attrs"
    :teleported="true"
    filterable
    remote
    :remote-method="handleRemoteMethod"
    @visible-change="handleVisibleChange"
    @change="onSelectChange"
  >
    <el-option v-if="isShowAll" label="全部" value=""></el-option>
    <!-- :label="item.Name" -->
    <el-option
      v-for="item in baseSelectUserList"
      :key="item.Id"
      :label="item.Name"
      :value="item[keyName]"
    >
      <span style="float: left">{{ item.Name }}</span>
      <span style="float: right; color: #8492a6; font-size: 13px">{{
        onShowLabel(item)
      }}</span>
    </el-option>
  </el-select>
</template>
<script setup lang="ts">
   const props = defineProps({
  /** 双向绑定 */
  modelValue: {
    type: [String, Array as () => Array<string>],
    default: null,
  },
  /** 机构参数 */
  orgIds: {
    type: [String, Array as () => Array<string>],
    default: "",
  },
  /** 科室参数 */
  deptIds: {
    type: [String, Array as () => Array<string>],
    default: "",
  },
  /** 角色 */
  roleTypes: {
    type: [String, Array as () => Array<string>],
    default: "",
  },
  /** 是否获取权限内的数据 */
  scopeable: {
    type: Boolean,
    default: false,
  },
  /** 是否是通过redash */
  isRedash: {
    type: Boolean,
    default: false,
  },
  /** 选择的是否是单选 */
  isOnlySingle: {
    type: Boolean,
    default: true,
  },
  /** 双向绑定的值是哪个字段 */
  keyName: {
    type: String,
    default: "Id",
  },
  /** 关键字搜索默认数据 */
  remoteName: {
    type: String,
    default: "",
  },
  /** dtoType */
  dtoTypeName: {
    type: String,
    default: "QueryUserOutputDto3",
  },
  /** 下拉列表显示什么数据 */
  selectShowLabel: {
    type: String,
    default: "",
  },
  /** 是否显示全部选择 */
  isShowAll: {
    type: Boolean,
    default: true,
  },
  /** 是否显示默认列表数据(主要是做回显操作) */
  isUseDefaultList: {
    type: Boolean,
    default: false,
  },
  /** 一个页面多次使用 select popperClassName不能重复 切记!!!!!!  */
  popperClassName: {
    require: true,
    type: String,
  },
});
</script>

2、父组件

<el-load-search-select
            v-model="transferAssistant.oldPhone"
            placeholder="请选择医助"
            :role-types="['doctor']"
            :scopeable="false"
            :is-redash="false"
            :is-only-single="true"
            key-name="PhoneNumber"
            popper-class-name="oldAssistant" // 这里是重点
            style="width: 140px;"
          />

3、自定义指令

src/directive/elSelectLoadMore.ts

import { debounce, DebouncedFunc } from "lodash";
export default {
  mounted(el: any, binding: { value: () => void; },vnode:any) {
    console.log('el', el)

    // 这里的popper-class-name就是使用 select 时候传递的 popper-class-name="oldAssistant" !!!!!
    const popperClassName = el.getAttribute('popper-class-name')
    console.log('popperClassName', popperClassName);
    
    let scrollWrap = document.querySelector(`.${popperClassName} .el-select-dropdown__wrap`)!
    
    // let scrollWrap = document.querySelector('.selectPopperClass .el-select-dropdown__wrap')!
    // 把监听的方法防抖一下
    const handle = debounce((e) => {
      console.log('scrollWrap.scrollTop', scrollWrap.scrollTop);
      console.log('scrollWrap.scrollHeight', scrollWrap.scrollHeight);
      console.log('scrollWrap.clientHeight', scrollWrap.clientHeight);
      let scrollDistance = scrollWrap.scrollHeight - scrollWrap.scrollTop
      // 比如此处预留10个像素的位置用于触底
      if (scrollWrap.clientHeight + 10 > scrollDistance) {
        binding.value() // 触底通知一下,外界
      }
    }, 170)
    // 绑定监听滚动事件
    scrollWrap?.addEventListener('scroll', handle)
    // 方法挂载到元素身上便于解绑时使用
    el._hanlde = handle
 
  },
  unmounted(el: { _hanlde: EventListenerOrEventListenerObject | null; }, binding: any) {
    let scrollWrap: any = document.querySelector('.el-scrollbar__wrap')
    console.log('scrollWrap', scrollWrap)
    scrollWrap?.removeEventListener('scroll', el._hanlde)
    el._hanlde = null
 
  }
}

src/directive/index.ts

import loadmore from "./elSelectLoadMore";
// 自定义指令对象,用于遍历注册
const directives = {
  loadmore,
} as any;
// 批量注册指令并暴露到main.js中去便于注册
export default {
  install(app: { directive: (arg0: string, arg1: any) => void }) {
    Object.keys(directives).forEach((key) => {
      app.directive(key, directives[key]);
    });
  },
};

src/mian.ts

import directive from './directive';
app.use(directive);

上面是所有的代码,接下来讲解上面的三个问题

三个问题的讲解

1、 封装的自定义指令没法用

原因出在let scrollWrap = document.querySelector()! 时候里面写的 class 实际在 dom 中不存在

在 elementplus 时候 select 进行了重构 element 的封装不适合了

2、封装完 select 之后 如果一个页面有多个封装的 select 会导致多次触发加载更多

这里的 select 是在 body 上面的 里面的 class 名称都是一样的 如果你  let scrollWrap = document.querySelector()!里面写的 class 名称是一样的话 就是导致这个问题

因为你每一个都是相同的 class 都回触发自定义指令

而我这边是自己传入了一个自定义属性(上面框起来 有 oldAssistant 和 newAssistant) 通过querySelector(自定义属性)! 那就每一个select不一样了

所以为什么我很强调popper-class-name="" 使用组件的这个属性为什么一定要不一样

3、点击其他区域 将 option 隐藏起来 这时候重新点击 select 会重新触发多次请求

在之前可能你请求了三次 数据可能有100条  假设每一条高度是20px 这时候你的整体高度就是2000px  这个时候你重新点击 select  又去加载数据(远程搜索 空字符串)可能只有30条 这个时候高度就是600px 那么之前的滚动条是在最底部的 elementplus 记录了你上一次的位置 这次你的高度只有600px 完全没有到达之前的高度 就会导致一直触发上拉触底 一直等到你的高度>=2000px的时候才会停止(以上只是推理 没有验证)

完美收官!! 若有问题 下方评论哦 对你有用是我最大的荣幸

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

多喜乐 长安宁

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值