当前el-table的多选实现是仅仅选择当前的数据,不满足需求
当数据是一次性全部返回的时候,我们需要全选的时候选择的是全部数据,这时组件库不满足需求


<template>
<div class="table-container">
<!-- 全选和操作 -->
<div class="toolbar">
<el-button
type="danger"
:disabled="selectedIds.size === 0"
@click="handleDeleteSelected"
>
批量删除
</el-button>
<span v-if="selectedIds.size > 0" class="selection-info">
已选择 {{ selectedIds.size }} 项
</span>
</div>
<el-table
:data="paginatedData"
style="width: 100%"
v-loading="loading"
row-key="id"
ref="tableRef"
>
<!-- 手动多选列 -->
<el-table-column width="55" align="center">
<template #header>
<el-checkbox
v-model="allSelected"
:indeterminate="isIndeterminate"
@change="handleSelectAll"
/>
</template>
<template #default="{ row }">
<el-checkbox v-model="row.selected" @change="handleRowSelect(row)" />
</template>
</el-table-column>
<el-table-column prop="id" label="ID" width="80" />
<el-table-column prop="name" label="姓名" width="120" />
<el-table-column prop="age" label="年龄" width="80" />
<el-table-column prop="email" label="邮箱" />
<el-table-column prop="address" label="地址" />
<el-table-column prop="createTime" label="创建时间" width="180" />
</el-table>
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[10, 20, 50, 100]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
background
style="margin-top: 20px; text-align: right"
/>
</div>
</template>
<script setup lang="ts">
import { ref, computed, nextTick, onMounted } from "vue";
import type { TableInstance } from "element-plus";
import { ta } from "element-plus/es/locales.mjs";
interface User {
id: number;
name: string;
age: number;
email: string;
address: string;
createTime: string;
selected?: boolean;
}
const tableData = ref<User[]>([]);
const tableRef = ref<TableInstance>();
const loading = ref(false);
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(100);
const selectedIds = ref<Set<number>>(new Set());
// 分页数据
const paginatedData = computed(() => {
const start = (currentPage.value - 1) * pageSize.value;
const end = start + pageSize.value;
return tableData.value.slice(start, end);
});
// 全选/半选状态
const allSelected = ref(false);
const isIndeterminate = computed(() => {
return selectedIds.value.size > 0 && selectedIds.value.size < total.value;
});
const updateSelectStatus = () => {
const currentPageIds = paginatedData.value.map((i) => i.id);
const selectedCount = currentPageIds.filter((id) =>
selectedIds.value.has(id)
).length;
allSelected.value =
selectedCount === currentPageIds.length && currentPageIds.length > 0;
};
// 行选中变化
const handleRowSelect = (row: User) => {
if (row.selected) selectedIds.value.add(row.id);
else selectedIds.value.delete(row.id);
updateSelectStatus();
};
// 全选/取消全选当前页
const handleSelectAll = (val: boolean) => {
if (val) {
tableData.value.forEach((item) => {
selectedIds.value.add(item.id);
item.selected = true;
});
} else {
selectedIds.value.clear();
tableData.value.forEach((item) => {
item.selected = false;
});
}
updateSelectStatus();
};
// 分页切换时更新行选中状态
const updateCurrentPageSelection = () => {
nextTick(() => {
paginatedData.value.forEach((item) => {
item.selected = selectedIds.value.has(item.id);
});
updateSelectStatus();
});
};
// 分页事件
const handleSizeChange = (val: number) => {
pageSize.value = val;
currentPage.value = 1;
updateCurrentPageSelection();
};
const handleCurrentChange = (val: number) => {
currentPage.value = val;
updateCurrentPageSelection();
};
// 批量删除
const handleDeleteSelected = () => {
tableData.value = tableData.value.filter(
(item) => !selectedIds.value.has(item.id)
);
selectedIds.value.clear();
updateCurrentPageSelection();
total.value = tableData.value.length;
};
// 模拟数据
const generateMockData = (count: number) => {
const data: User[] = [];
for (let i = 1; i <= count; i++) {
data.push({
id: i,
name: `用户${i}`,
age: Math.floor(Math.random() * 50) + 18,
email: `user${i}@example.com`,
address: `北京市朝阳区街道${i}号`,
createTime: new Date(
Date.now() - Math.floor(Math.random() * 7 * 24 * 60 * 60 * 1000)
).toLocaleDateString(),
});
}
return data;
};
const getTableData = () => {
loading.value = true;
setTimeout(() => {
tableData.value = generateMockData(total.value);
updateCurrentPageSelection();
loading.value = false;
}, 300);
};
onMounted(() => {
getTableData();
});
</script>
<style scoped>
.table-container {
padding: 20px;
}
.toolbar {
margin-bottom: 15px;
display: flex;
align-items: center;
gap: 10px;
}
.selection-info {
color: #606266;
font-size: 14px;
}
</style>