vue3+element 滚动触底加载选择器

html 复制代码
<template>
  <el-select v-model="selectedValue" filterable :placeholder="placeholder" @visible-change="handleVisibleChange"
    @focus="handleFocus" popper-class="cit-scroll-select">
    <el-option v-for="item in props.options" :key="item[dataConfig.value]" :label="item[dataConfig.name]"
      :value="item[dataConfig.value]" />
    <template #footer v-if="showFooter">
      <div class="text-center">
        <span v-if="props.loading && props.options?.length < props.total">
          {{ props.loading ? '加载中...' : '' }}
        </span>
        <span v-else-if="props.options?.length === props.total">
          没有更多数据了
        </span>
      </div>
    </template>
  </el-select>
</template>

<script setup>
/**
* @author        全易
* @time          2025-12-08 17:38:17 星期一
* @description   触底加载 选择器
* @example       <scroll-select v-model="value" :options="options" :loading="loading" :total="total" @initData="getData" @reachBottom="reachBottom" />
**/

const selectedValue = defineModel()
const props = defineProps({
  loading: {
    type: Boolean,
    default: false
  },
  options: {
    type: Array,
    default: () => []
  },
  total: {
    type: Number,
    required: true
  },
  placeholder: {
    type: String,
    default: ''
  },
  queryForm: {
    type: Object,
    default: () => ({})
  },
  // 取值配置
  dataConfig: {
    type: Object,
    default: () => ({
      name: 'name',
      value: 'id'
    })
  }
})

// 显示底部加载更多
const showFooter = computed(() => {
  if (props.queryForm?.pageNum === 1) { return false }
  return (props.loading && props.options?.length < props.total) || (props.options?.length === props.total)
})

const showDefaultOption = computed(() => {
  // 当选中值不在options中时显示默认选项
  if (!selectedValue.value) return false;
  return !props.options.some(option => option.value === selectedValue.value);
})

const emits = defineEmits(['reachBottom'])
// 选择器显示/隐藏处理
const handleVisibleChange = (visible) => {
  if (visible && props.options?.length === 0) {
    emits('initData')
  }
}

// 获取焦点时加载数据
const handleFocus = () => {
  if (props.options?.length === 0) {
    emits('reachBottom')
  }
}

// 监听下拉框滚动事件
const setupScrollListener = () => {
  const popperEl = document.querySelector('.cit-scroll-select .el-select-dropdown__wrap')
  if (popperEl) {
    popperEl.addEventListener('scroll', handleScroll)
  }
}

// 移除滚动监听
const removeScrollListener = () => {
  const popperEl = document.querySelector('.cit-scroll-select .el-select-dropdown__wrap')
  if (popperEl) {
    popperEl.removeEventListener('scroll', handleScroll)
  }
}

// 滚动处理函数
const handleScroll = (e) => {
  const { scrollTop, scrollHeight, clientHeight } = e.target
  // 距离底部 60px 时开始加载
  if (scrollHeight - scrollTop - clientHeight < 60 && !props.loading && (props.options.length < props.total)) {
    emits('reachBottom')
  }
}

// 使用 MutationObserver 监听下拉框的出现
let observer = null
const initObserver = () => {
  observer = new MutationObserver((mutations) => {
    mutations.forEach((mutation) => {
      if (mutation.addedNodes.length) {
        const selectDropdown = document.querySelector('.cit-scroll-select .el-select-dropdown__wrap')
        if (selectDropdown) {
          setupScrollListener()
        }
      }
    })
  })

  // 监听 body 的变化
  observer.observe(document.body, {
    childList: true,
    subtree: true
  })
}

// 组件挂载时初始化
onMounted(() => {
  initObserver()
  // 初始加载数据
  emits('reachBottom')
})

// 组件卸载前清理
onBeforeUnmount(() => {
  if (observer) {
    observer.disconnect()
  }
  removeScrollListener()
})
</script>

<style scoped lang="scss"></style>
相关推荐
顶级自由人2 小时前
本地正常、线上正常,为什么一个 Hook 仍会报错?
前端·javascript·程序员
lerhxx2 小时前
R3F 第一人称漫游与碰撞检测:Pointer Lock + 不穿墙的滑墙秘诀(中)
前端·javascript·three.js
labixiong2 小时前
button按钮原生开关弹窗,零 JS 搞定80%交互场景
前端·javascript·html
隔岸观火烧连营2 小时前
如何用 WebCodecs 在浏览器里实现高清录屏 —— 无插件、无水印、直接导出 MP4
前端·javascript
Hilaku4 小时前
技术好就能升职是前端圈最大的谎言!
前端·javascript·程序员
光影少年4 小时前
react navite高频手写/实操题
前端·javascript·react native·react.js·前端框架
AI视觉网奇4 小时前
3d子部件高亮选中
开发语言·javascript·3d
阿图灵4 小时前
LangGraph 实战 03:Workflows 与 Agents——六种工作流模式与智能体实战(附 6 个可运行示例)
java·前端·javascript·工作流·ai agent·智能体·langgraph
2603_965896625 小时前
JavaScript 对象零基础详解|属性、方法、遍历、增删改查
开发语言·javascript·ecmascript
用户283209679375 小时前
从进程线程到 async/await:JS Event-loop事件循环机制底层解析
前端·javascript