可操作的无限长虚拟列表组件 - zptime/blog GitHub Wiki
无限长虚拟列表组件,提高渲染性能,有简单的文字提示气泡框,还有可自定义的操作栏,满足个性化需求
之前写过一个列表穿梭框组件,Ant Design Vue Transfer 穿梭框“造轮子”,里面实现过一个虚拟列表。
这次实现的是一个带操作的虚拟列表组件,结合 vue-virtual-scroller
实现滚动加载无限长列表,带有虚拟化(virtualization)功能,能够提高数据量大时候长列表的性能。
主要实现功能点:
-
RecycleScroller
插件使用:实现虚拟滚动 - 选中、未选中样式处理
- 标题处理(
Tooltip
):超过长度省略号,文字提示气泡框 - 操作栏处理(
Dropdown
):可自定义操作项
注意点:
-
color: currentColor;
:继承父元素的 color 属性 -
writing-mode: vertical-rl;
:定义了文本水平或垂直排布以及在块级元素中文本的行进方向。我主要用来让三个点垂直排列
- 模拟数据
const mockList = [
{ id: 11, name: "ant design vue part 1,ant design vue part 1" },
{ id: 12, name: "ant design vue part 2" },
{ id: 13, name: "ant design vue part 3" },
{ id: 14, name: "ant design vue part 4" },
{ id: 15, name: "ant design vue part 5" },
{ id: 16, name: "ant design vue part 6" },
{ id: 17, name: "ant design vue part 7" },
{ id: 18, name: "ant design vue part 8" },
{ id: 19, name: "ant design vue part 9" },
{ id: 20, name: "ant design vue part 10" },
{ id: 21, name: "ant design vue part 11" },
{ id: 22, name: "ant design vue part 12" },
{ id: 23, name: "ant design vue part 13" },
{ id: 24, name: "ant design vue part 14" },
{ id: 25, name: "ant design vue part 15" },
{ id: 26, name: "ant design vue part 16" },
{ id: 27, name: "ant design vue part 17" },
{ id: 28, name: "ant design vue part 18" },
];
- 使用:默认操作只有编辑和删除,用户可以自定义处理
参数 | 说明 | 类型 |
---|---|---|
dataSource | 数据源 | Array |
activeKey | 选中 key | string |
operation | 操作项 | slot |
<m-rank-list
:data-source="list"
:active-key="activeKey"
@handle-select="handleSelect"
>
// 自定义操作栏内容
<template slot="operation">
<a-menu-item>新增人员</a-menu-item>
<a-menu-item>编辑</a-menu-item>
<a-menu-item>删除</a-menu-item>
</template>
</m-rank-list>
<template>
<a-list class="list">
<RecycleScroller
class="recycle"
:items="dataList"
:item-size="32"
key-field="name"
>
<a-list-item
slot-scope="{ item }"
:key="item.id"
:class="['item', { 'item-selected': activeId === item.id }]"
@click="handleSelect(item)"
>
<rank-icon class="item-icon" />
<!-- Tooltip 文字提示 -->
<a-tooltip placement="topLeft" :title="item.name">
<div class="item-title">{{ item.name }}</div> </a-tooltip
>‘
<!-- Dropdown 下拉菜单 -->
<a-dropdown
placement="bottomRight"
:trigger="['hover']"
class="item-dropdown"
>
<span>...</span>
<a-menu slot="overlay">
<slot name="operation">
<a-menu-item @click="handleEdit(item)">编辑</a-menu-item>
<a-menu-item @click="handleDel(item)">删除</a-menu-item>
</slot>
</a-menu>
</a-dropdown>
</a-list-item>
</RecycleScroller>
</a-list>
</template>
<script>
import * as R from "ramda";
import { RecycleScroller } from "vue-virtual-scroller";
import "vue-virtual-scroller/dist/vue-virtual-scroller.css";
export default {
props: ["dataSource", "activeKey"],
data() {
return {
activeId: "",
dataList: [],
};
},
components: {
RecycleScroller,
},
watch: {
dataSource(val) {
if (val?.length) {
this.dataList = R.clone(val);
this.activeId = this.activeKey
? this.activeKey
: this.dataList?.[0]?.id;
} else {
this.dataList = [];
this.activeId = "";
}
},
},
methods: {
handleSelect(item) {
this.activeId = item.id;
this.$emit("handle-select", item);
},
handleEdit(item) {
this.$emit("handle-edit", item);
},
handleDel(id) {
this.$emit("handle-delete", item);
},
},
};
</script>
<style lang="scss" scoped>
.list {
.recycle {
max-height: 320px;
overflow-y: auto;
overflow-x: hidden;
margin-bottom: 24px;
}
.item {
display: flex;
align-items: center;
height: 32px;
padding: 0 0 0 4px;
border-bottom: 0;
cursor: pointer;
// 选中样式处理
&-selected {
background-color: #e6f7ff;
color: #1890ff;
.item-icon,
.item-dropdown {
// 继承颜色
color: currentColor;
}
}
&-icon {
font-size: 12px;
margin-right: 6px;
color: #ebedf0;
}
&-title {
flex: 1;
display: block;
// 超过长度省略号
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
&-dropdown {
cursor: pointer;
// 指定块流动方向
writing-mode: vertical-rl;
color: #ebedf0;
font-weight: bold;
}
}
}
</style>