vue3+elementUI-plus实现select下拉框的虚拟滚动

网上查了几个方案,要不就是不兼容,要不就是不支持vue3,

最终找到一个合适的,并且已上线使用,需要修改一下样式:

代码如下:

main.js里引用

bash 复制代码
import 'vue3-virtual-scroller/dist/vue3-virtual-scroller.css';
import { RecycleScroller } from 'vue3-virtual-scroller';
app.component('RecycleScroller', RecycleScroller);

vue文件:

bash 复制代码
<el-form-item label="用户" prop="seriesId">
                    <div ref="selectWrapper" @click="toggleDropdown($event)" class="select-wrapper">
                        <el-input style="width: 240px"
                        placeholder="请输入搜索内容"
                        v-model="selectedOption"
                        :suffix-icon="selectedOption ? 'el-icon-circle-close' : null"
                        @clear="clearSearch"
                        ></el-input>
                    </div>
                    <el-icon class="clear-btn" v-if="selectedOption" @click.stop="clearSearch"><CircleClose /></el-icon>
                    <Teleport to="body">
                        <div v-show="isOpen"  ref="dropdown" class="virtual-dropdown" :style="dropdownStyles" @click="closeDropdown">
                            <RecycleScroller
                                class="virtual-list"
                                :buffer="1000"
                                :prerender="200"
                                style="height: 270px"
                                :item-size="24"
                                key-field="id"
                                :items="filteredSeriesList"
                                >
                                <template v-slot="{ item, index }">
                                    <div class="list-item" :key="index" @click.stop="handleItemClick(item)">
                                    <span>{{ item.id }}</span>&nbsp;-&nbsp;
                                    <span>{{ item.name }}</span>
                                    </div>
                                </template>
                                </RecycleScroller>
                        </div>
                    </Teleport>
                </el-form-item>

js代码:

bash 复制代码
<script setup name="LeadsList">
const rowCount = ref(0);
    const rowCount2 = ref(0);
    const leadsList = ref([]);
    const loading = ref(false);
    const activeButton = ref(0);
    const seriesList = ref([]);
    const filteredSeriesList = ref([]); // 初始状态下,筛选后的列表与原始列表相同
    const indexLayer = ref(false);
    const open = ref(false);
    const dropdown = ref(null);
    const repeatLoading = ref(false);
    const repeatList = ref([]);
    const dropdownStyles = ref({});
    const selectWrapper = ref({});
    const isOpen = ref(false);
    const selectedOption = ref('');
    const data = reactive({
        queryParams: {
            pageIndex: 1,
            pageSize: 10,
            phone: "",
            seriesId: null,
        },
        queryParamsRepeat: {
            pageIndex: 1,
            pageSize: 10,
            companyId: 1,
            userId: 1
        }
    });

    const { queryParams,queryParamsRepeat } = toRefs(data);


    watch(selectedOption, (newValue) => {
        search(newValue);
    });
    function search (keyword) {
        if (keyword.trim() === "") {
      filteredSeriesList.value = [...seriesList.value];
        } else {
        filteredSeriesList.value = seriesList.value.filter((item) =>
            item.name.toLowerCase().includes(keyword.toLowerCase())
        );
        }
    }
    function handleItemClick(item) {
      queryParams.value.seriesId = item.id;
      selectedOption.value = item.name;
      closeDropdown()
    }
    function closeDropdown(event = null) {
        isOpen.value = false;
        if (
            event &&
            (selectWrapper.value.contains(event.target) ||
            dropdown.value.contains(event.target))
        ) {
            return;
        }
        isOpen.value = false;
    }
    function toggleDropdown($event) {
        $event.stopPropagation(); // 阻止事件冒泡
        isOpen.value = !isOpen.value;
        if (isOpen.value) {
            const rect = selectWrapper.value.getBoundingClientRect();
            const { x, y, width, height } = rect;
            dropdownStyles.value = {
            position: 'fixed',
            top: `${y + height}px`,
            left: `${x}px`,
            width: `${width}px`,
            };
        }
    }
    function clearSearch () {
        queryParams.value.seriesId = '';
        selectedOption.value = '';
        filteredSeriesList.value = [...seriesList.value];
    }

css代码:

bash 复制代码
.virtual-dropdown {
  position: absolute;
  inset: 100% auto auto 0;
  z-index: 2000;
  width: 100%;
  overflow-y: auto;
  overflow-x: hidden;
  border: 1px solid #ebeef5;
  border-radius: 4px;
  background-color: #fff;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}

.list-item {
  display: flex;
  padding: 0 10px;
  align-items: center;
  height: 24px;
  cursor: pointer;
  white-space: nowrap;
  text-overflow: ellipsis;
  overflow: hidden;
}

.list-item:hover {
  background-color: #f5f7fa;
}
.clear-btn {
    border: none;
    background: transparent;
    cursor: pointer;
    position: absolute;
    right: 14px;
  }
相关推荐
IT_陈寒38 分钟前
Vite的静态资源打包让我熬夜到三点,这坑千万别跳
前端·人工智能·后端
山河木马1 小时前
矩阵专题0-webGL中的矩阵
javascript·webgl·计算机图形学
徐小夕1 小时前
万字拆解 JitWord:企业级实时协同文档底层架构 + 大模型 AI 融合完整实践
前端·vue.js·github
一份执念2 小时前
uni-app 小程序分包限制处理与主包体积优化实战
前端·微信小程序
Asize2 小时前
多模态生图:从 Vite 工程化到前端调用 Qwen Image
javascript·人工智能·后端
MariaH2 小时前
初识MySQL
前端
陳陈陳2 小时前
从Token到Embedding:一篇文章搞懂大模型的「文字数学变形记」
前端·javascript·ai编程
十有八七2 小时前
AI时代的置身X内
前端·人工智能
用户938515635072 小时前
从 O(n²) 到 O(nlogn):一文读懂快速排序的“快”与“妙”
javascript·算法
橘子星2 小时前
LLM 无状态架构实践:从原理到代码落地
前端·javascript·人工智能