写一个vue+element selected下拉框虚拟列表
时间: 2023-08-29 10:13:57 浏览: 102
以下是一个简单的vue element selected下拉框虚拟列表组件,可以根据需求进行修改和扩展:
```vue
<template>
<el-select v-model="selected" placeholder="请选择" @visible-change="handleVisibleChange">
<el-option
v-for="(item, index) in options"
:key="item.value"
:label="item.label"
:value="item.value"
:disabled="item.disabled"
:style="getOptionStyle(index)"
></el-option>
</el-select>
</template>
<script>
export default {
data() {
return {
selected: null,
options: [],
visibleOptions: [],
itemHeight: 36, // 每一项的高度
visibleItemCount: 10, // 可见项的数量
bufferItemCount: 3, // 缓冲项的数量,用于提高滚动性能
startIndex: 0, // 可见项的起始索引
endIndex: 0, // 可见项的结束索引
scrollTop: 0, // 滚动的距离
scrollHeight: 0, // 滚动区域的高度
lastScrollTime: 0, // 上一次滚动的时间戳
}
},
computed: {
totalHeight() {
return this.itemHeight * this.options.length
},
},
watch: {
options: {
handler() {
this.updateVisibleOptions()
},
deep: true,
},
scrollTop() {
this.updateVisibleOptions()
},
},
mounted() {
this.updateVisibleOptions()
window.addEventListener('resize', this.handleWindowResize)
},
beforeDestroy() {
window.removeEventListener('resize', this.handleWindowResize)
},
methods: {
// 更新可见选项
updateVisibleOptions() {
// 计算可见项的起始索引和结束索引
const scrollTop = Math.max(0, Math.min(this.scrollTop, this.totalHeight - this.clientHeight))
const startIndex = Math.floor(scrollTop / this.itemHeight)
const endIndex = Math.min(
this.options.length - 1,
startIndex + this.visibleItemCount + this.bufferItemCount * 2 - 1
)
const visibleOptions = this.options.slice(startIndex, endIndex + 1)
// 更新状态
this.startIndex = startIndex
this.endIndex = endIndex
this.visibleOptions = visibleOptions
},
// 获取选项的样式
getOptionStyle(index) {
const style = {}
if (index < this.startIndex - this.bufferItemCount || index > this.endIndex + this.bufferItemCount) {
style.display = 'none'
} else {
style.display = 'block'
style.height = this.itemHeight + 'px'
style.transform = `translateY(${(index - this.startIndex) * this.itemHeight}px)`
}
return style
},
// 获取滚动区域的高度
getScrollHeight() {
return Math.min(this.totalHeight, this.visibleItemCount * this.itemHeight)
},
// 处理下拉框的可见性变化
handleVisibleChange(visible) {
if (visible) {
this.scrollHeight = this.getScrollHeight()
this.lastScrollTime = Date.now()
}
},
// 处理窗口大小变化
handleWindowResize() {
this.scrollHeight = this.getScrollHeight()
},
// 处理滚动事件
handleScroll(event) {
const now = Date.now()
if (now - this.lastScrollTime > 100) {
this.scrollTop = event.target.scrollTop
this.lastScrollTime = now
}
},
},
}
</script>
<style scoped>
/* 设置下拉框的样式 */
.el-select__caret {
display: none;
}
.el-select-dropdown .el-scrollbar__wrap {
max-height: none !important;
}
.el-select-dropdown .el-scrollbar__thumb {
background-color: #ccc;
border-radius: 4px;
}
.el-select-dropdown .el-scrollbar__thumb:hover {
background-color: #aaa;
}
</style>
```
使用方法:
```vue
<template>
<div>
<virtual-select :options="options" v-model="selected" />
</div>
</template>
<script>
import VirtualSelect from './VirtualSelect'
export default {
components: {
VirtualSelect,
},
data() {
return {
selected: null,
options: [
{ label: '选项一', value: '1' },
{ label: '选项二', value: '2' },
{ label: '选项三', value: '3' },
// ...
],
}
},
}
</script>
```
阅读全文