在企业级管理系统、数据中台等复杂前端项目中,表格作为数据展示与交互的核心组件,常面临数据量庞大需分页展示的场景。例如在管理系统中,需要在多页数据中批量勾选目标条目(如批量导出、批量删除)。
基于此,实现一个支持跨页选择的复选框表格成为提升用户体验的关键需求。该功能需要解决数据分页加载时选中状态的持久化维护、不同页面间勾选逻辑的一致性,以及结合TypeScript类型安全特性确保数据操作的可靠性,同时兼顾组件性能与可维护性,为复杂数据交互场景提供高效的解决方案。
交互演示与核心功能说明:
- 空状态展示:页面初始加载时呈现所有未被选中的数据
- 跨页选中功能演示:
在不同分页间勾选数据行,表格自动维护选中状态
切换页码时保留已选数据,支持连续跨页批量操作 - 数据获取与处理
点击"获取选中数据"按钮,实时提取选中项ID集合
支持两种数据获取模式:仅ID数组或完整数据对象
在控制台输出选中结果(实际项目中可用于批量提交、导出等操作) - 编辑场景模拟
点击"渲染数据"按钮,模拟编辑页回显已选数据
自动选中已被选择过的数据
第一种:采用原生html实现
代码如下:
javascript
<script setup lang="ts">
import { ref, computed } from 'vue'
const tableData = ref([
{
id: 1,
name: 'aaa',
age: 18
},
{
id: 2,
name: 'bbb',
age: 17
},
{
id: 3,
name: 'ccc',
age: 16
},
{
id: 4,
name: 'ddd',
age: 15
},
{
id: 5,
name: 'eeee',
age: 14
},
{
id: 6,
name: 'fff',
age: 19
},
{
id: 7,
name: 'ggg',
age: 20
},
{
id: 8,
name: 'hhh',
age: 21
},
{
id: 9,
name: 'iii',
age: 22
},
{
id: 10,
name: 'jjj',
age: 23
},
])
const pageSize = 5
const currentPage = ref(1)
const totalPages = computed(() => Math.ceil(tableData.value.length / pageSize))
const paginatedData = computed(() => {
const start = (currentPage.value - 1) * pageSize
const end = start + pageSize
return tableData.value.slice(start, end)
})
const nextPage = () => {
if (currentPage.value < totalPages.value) {
currentPage.value++
}
}
const prevPage = () => {
if (currentPage.value > 1) {
currentPage.value--
}
}
const selectedItems = ref<number[]>([]) // Initialize with IDs 2 and 8
const toggleSelection = (id: number) => {
const index = selectedItems.value.indexOf(id)
if (index === -1) {
selectedItems.value.push(id)
} else {
selectedItems.value.splice(index, 1)
}
}
const getSelectedData = () => {
// 获取单条数据的id
console.log('selectData', selectedItems.value)
// 获取单条数据
const selectedData = tableData.value.filter(item => selectedItems.value.includes(item.id))
console.log(JSON.stringify(selectedData, null, 2))
}
const setSelectedData = () =>{
selectedItems.value = [2,8]
}
</script>
<template>
<div>
<table class="border">
<tr>
<th></th>
<th>名字</th>
<th>年龄</th>
</tr>
<tr v-for="item in paginatedData" :key="item.id">
<td>
<input
type="checkbox"
:checked="selectedItems.includes(item.id)"
@change="toggleSelection(item.id)"
/>
</td>
<td>{{item.name}}</td>
<td>{{item.age}}</td>
</tr>
</table>
<div class="pagination">
<button @click="prevPage" :disabled="currentPage === 1">上一页</button>
<span>{{ currentPage }} / {{ totalPages }}</span>
<button @click="nextPage" :disabled="currentPage === totalPages">下一页</button>
</div>
<div class="selected-info">
已选择: {{ selectedItems.length }} 项
<button class="get-data-btn" @click="getSelectedData">获取选中的数据</button>
<button class="get-data-btn" @click="setSelectedData">渲染数据</button>
</div>
</div>
</template>
<style scoped>
.border {
width: 400px;
border: 1px solid lightyellow;
margin-bottom: 1rem;
}
.pagination {
display: flex;
gap: 1rem;
align-items: center;
justify-content: center;
margin-bottom: 1rem;
}
.selected-info {
text-align: center;
color: #646cff;
}
button {
padding: 0.5rem 1rem;
}
button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.get-data-btn {
margin-left: 1rem;
background-color: #42b883;
color: white;
}
.get-data-btn:hover {
border-color: #42b883;
}
</style>
实现效果如下:
第二种:采用element-plus实现
代码如下:
javascript
<script setup lang="ts">
import { ref, computed } from 'vue'
import { ElTable, ElTableColumn, ElPagination, ElButton } from 'element-plus'
const tableData = ref([
{ id: 1, name: 'aaa', age: 18 },
{ id: 2, name: 'bbb', age: 17 },
{ id: 3, name: 'ccc', age: 16 },
{ id: 4, name: 'ddd', age: 15 },
{ id: 5, name: 'eeee', age: 14 },
{ id: 6, name: 'fff', age: 19 },
{ id: 7, name: 'ggg', age: 20 },
{ id: 8, name: 'hhh', age: 21 },
{ id: 9, name: 'iii', age: 22 },
{ id: 10, name: 'jjj', age: 23 },
])
const currentPage = ref(1)
const pageSize = ref(5)
const multipleSelection = ref<number[]>([])
const paginatedData = computed(() => {
const start = (currentPage.value - 1) * pageSize.value
const end = start + pageSize.value
return tableData.value.slice(start, end)
})
const getSelectedData = () => {
console.log('Selected data:', multipleSelection.value)
}
const setSelectedData = () => {
multipleSelection.value = [2, 8]
}
const handleCheck = (e,id) => {
if(e) {
multipleSelection.value.push(id)
} else {
const index = multipleSelection.value.findIndex(item => item.id === id)
multipleSelection.value.splice(index, 1)
}
}
</script>
<template>
<div class="table-container">
<el-table
:data="paginatedData"
style="width: 100%"
>
<el-table-column header-align="center" align="center" width="40">
<template #default="scope">
<el-checkbox :model-value="multipleSelection.includes(scope.row.id)" @change="handleCheck($event, scope.row.id)" />
</template>
</el-table-column>
<el-table-column
prop="name"
label="名字"
/>
<el-table-column
prop="age"
label="年龄"
/>
</el-table>
<div class="footer">
<el-pagination
v-model:current-page="currentPage"
:page-size="pageSize"
:total="tableData.length"
layout="prev, pager, next"
/>
<div class="buttons">
<span class="selected-info">已选择: {{ multipleSelection.length }} 项</span>
<el-button type="primary" @click="getSelectedData">获取选中的数据</el-button>
<el-button type="primary" @click="setSelectedData">渲染数据</el-button>
</div>
</div>
</div>
</template>
<style scoped>
.table-container {
width: 100%;
max-width: 800px;
margin: 0 auto;
}
.footer {
margin-top: 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.buttons {
display: flex;
align-items: center;
gap: 16px;
}
.selected-info {
margin-right: 16px;
}
</style>
实现效果如下: