虚拟列表-复选框-vxe-list
html
<template>
<div>
<p>
<vxe-button @click="loadData(10)">加载10条</vxe-button>
<vxe-button @click="loadData(500)">加载500条</vxe-button>
<vxe-button @click="loadData(3000)">加载3000条</vxe-button>
<vxe-button @click="loadData(10000)">加载1w条</vxe-button>
<vxe-button @click="loadData(100000)">加载10w条</vxe-button>
<vxe-button @click="loadData(300000)">加载30w条</vxe-button>
<vxe-checkbox v-model="allChecked" :indeterminate="indeterminate" @change="onChangeAll">全选</vxe-checkbox>
</p>
<vxe-list :height="gridHeight" :loading="loading" :data="list" class="my-list mytable-scrollbar">
<template #default="{items}">
<div class="my-list-item" v-for="item in items" :key="item.id">
<ul style="display: flex">
<li v-for="current in item.items" :key="current.id">
<el-card style="height: 220px; width: 184px" shadow="hover">
<vxe-checkbox v-model="current.checked" @change="onChangeItem"></vxe-checkbox> {{ current.id }}
</el-card>
</li>
</ul>
</div>
</template>
</vxe-list>
</div>
</template>
<script setup>
import {ref, onMounted, nextTick, computed, watchEffect} from 'vue';
import {VXETable} from 'vxe-table';
import useResize from '@/renderer/index/composables/useResize.js';
const {width, height} = useResize();
const loading = ref(false);
const list = ref([]);
const allChecked = ref(false);
const indeterminate = ref(false);
const gridHeight = computed(() => height.value - 100);
function groupDataByRows(data, colNum) {
const result = [];
let rowIndex = 0;
while (data.length > 0) {
const row = {
id: rowIndex,
label: 'row_' + rowIndex,
items: []
};
for (let i = 0; i < colNum; i++) {
if (data.length > 0) {
row.items.push(data.shift());
}
}
result.push(row);
rowIndex++;
}
return result;
}
function onChangeAll(value) {
console.log('onChangeAll', value.checked);
indeterminate.value = false;
list.value = list.value.map((item) => {
item.items = item.items.map((current) => {
current.checked = value.checked;
return {...current};
});
return {...item};
});
}
function onChangeItem(value) {
let allCheckedCount = 0;
let totalItemCount = 0;
list.value.forEach((item) => {
totalItemCount += item.items.length;
item.items.forEach((current) => {
if (current.checked) {
allCheckedCount++;
}
});
});
allChecked.value = allCheckedCount === totalItemCount;
indeterminate.value = !allChecked.value && allCheckedCount > 0;
}
// 模拟后台
const mockList = [];
const getList = (size) => {
return new Promise((resolve) => {
setTimeout(() => {
if (size > mockList.length) {
for (let index = mockList.length; index < size; index++) {
mockList.push({
id: index,
label: `row_${index}`
});
}
}
resolve(mockList.slice(0, size));
}, 100);
});
};
const colNum = computed(() => {
return Math.max(1, Math.floor(width.value / (186 + 30)));
});
const loadData = async (size) => {
loading.value = true;
list.value = groupDataByRows(await getList(size), colNum.value);
// console.log('list.value', JSON.stringify(list.value));
loading.value = false;
const startTime = Date.now();
await nextTick();
await VXETable.modal.message({content: `渲染 ${size} 行,用时 ${Date.now() - startTime}毫秒`, status: 'info'});
};
watchEffect(() => {
console.log('watch colNum', colNum.value);
loadData(200);
});
// 初始化
onMounted(async () => {
loadData(200);
});
</script>
<style scoped>
.my-list {
border: 1px solid #e8eaec;
}
.my-list .my-list-item {
height: 230px;
}
.my-list-item ul {
margin: 0;
padding: 0;
list-style: none;
}
.my-list-item li + li {
margin-left: 20px;
}
</style>