网上查了几个方案,要不就是不兼容,要不就是不支持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> -
<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;
}