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;
  }
相关推荐
To_OC18 分钟前
LC 17 电话号码的字母组合:我的回溯算法,就是从这道题开窍的
javascript·算法·leetcode
vipbic26 分钟前
中后台越做越乱后,我用插件化把它救回来了
前端·vue.js
Hyyy32 分钟前
Computer Use 适合做什么,不适合做什么——一次真实使用后的思考
前端
小和尚同志1 小时前
前端 AI 单元测试思考与落地
前端·人工智能·aigc
invicinble2 小时前
c端系统,其实更像一个信息展示平台
前端
你怎么知道我是队长2 小时前
JavaScript的变量和数据类型介绍
开发语言·javascript·ecmascript
李姆斯3 小时前
管理是否可以被完全量化
前端·产品经理·团队管理
名字还没想好☜4 小时前
Next.js 中间件实战:鉴权、重定向与 A/B 分流
开发语言·前端·javascript·中间件·react·next.js
广州灵眸科技有限公司4 小时前
瑞芯微RV1126B开发板(EASY-EAI-PI2) INI文件操作
java·前端·javascript·网络·人工智能
心中有国也有家4 小时前
AtomGit Flutter 鸿蒙客户端:ModalBottomSheet 实战
android·javascript·学习·flutter·华为·harmonyos