网上查了几个方案,要不就是不兼容,要不就是不支持vue3,
最终找到一个合适的,并且已上线使用,需要修改一下样式:
代码如下:
main.js里引用
import 'vue3-virtual-scroller/dist/vue3-virtual-scroller.css';
import { RecycleScroller } from 'vue3-virtual-scroller';
app.component('RecycleScroller', RecycleScroller);
vue文件:
<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"><RecycleScrollerclass="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> - <span>{{ item.name }}</span></div></template></RecycleScroller></div></Teleport></el-form-item>
js代码:
<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代码:
.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;}