Vue3+Vite+TypeScript项目中跨页多选表格的实现与应用
在企业级管理系统、数据中台等复杂前端项目中,表格作为数据展示与交互的核心组件,常面临数据量庞大需分页展示的场景。例如在管理系统中,需要在多页数据中批量勾选目标条目(如批量导出、批量删除)。
基于此,实现一个支持跨页选择的复选框表格成为提升用户体验的关键需求。该功能需要解决数据分页加载时选中状态的持久化维护、不同页面间勾选逻辑的一致性,以及结合TypeScript类型安全特性确保数据操作的可靠性,同时兼顾组件性能与可维护性,为复杂数据交互场景提供高效的解决方案。
交互演示与核心功能说明:
- 空状态展示:页面初始加载时呈现所有未被选中的数据
- 跨页选中功能演示:
在不同分页间勾选数据行,表格自动维护选中状态
切换页码时保留已选数据,支持连续跨页批量操作 - 数据获取与处理
点击“获取选中数据”按钮,实时提取选中项ID集合
支持两种数据获取模式:仅ID数组或完整数据对象
在控制台输出选中结果(实际项目中可用于批量提交、导出等操作) - 编辑场景模拟
点击“渲染数据”按钮,模拟编辑页回显已选数据
自动选中已被选择过的数据
第一种:采用原生html实现
代码如下:
<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) * pageSizeconst end = start + pageSizereturn 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 8const toggleSelection = (id: number) => {const index = selectedItems.value.indexOf(id)if (index === -1) {selectedItems.value.push(id)} else {selectedItems.value.splice(index, 1)}
}const getSelectedData = () => {// 获取单条数据的idconsole.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实现
代码如下:
<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.valueconst end = start + pageSize.valuereturn 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-columnprop="name"label="名字"/><el-table-columnprop="age"label="年龄"/></el-table><div class="footer"><el-paginationv-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>
实现效果如下: