Skip to content

虚拟列表-复选框-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>