Skip to Content

Vue3 + Element Plus 通用Table组件封装

在后台管理系统开发中,表格是最常用的组件之一。为了提高开发效率和代码复用性,我们通常会对基础的表格组件进行二次封装。本文将介绍如何基于 Element Plus 封装一个功能完善的通用 Table 组件。

设计思路

核心功能需求

  • 基础表格展示:支持数据展示和列配置
  • 多选功能:可选择性开启多选列
  • 序号列:可配置的序号显示
  • 自定义插槽:支持自定义列内容
  • 状态标签:内置状态标签显示
  • 分页功能:集成分页组件
  • 事件处理:完善的事件回调机制

组件特点

  1. 高度可配置:通过 props 控制各种功能的开启
  2. 插槽支持:灵活的自定义内容插槽
  3. 响应式设计:支持 v-model 双向绑定
  4. 类型安全:完整的 TypeScript 支持

组件实现

完整代码

<template> <div> <el-table :data="tableData" @selection-change="handleSelectionChange" v-bind="$attrs" > <!-- 多选列 --> <el-table-column v-if="showSelection" type="selection" width="55" align="center" /> <!-- 序号列 --> <el-table-column v-if="showIndex" type="index" :label="indexLabel || '序号'" :width="indexWidth || '60'" align="center" /> <el-table-column v-for="(item, index) in columns" :key="item.prop || index" :prop="item.prop" :label="item.label" :width="item.width" :align="item.align || 'left'" > <!-- 自定义插槽 --> <template #default="{ row }" v-if="item.slot"> <slot :name="item.slot" :row="row"> <!-- 默认内容 --> <span>{{ (row[item.prop] ?? "") === "" ? "--" : row[item.prop] }}</span> </slot> </template> <!-- 默认插槽 --> <template #default="{ row }" v-else> <span>{{ (row[item.prop] ?? "") === "" ? "--" : row[item.prop] }}</span> </template> <!-- 状态 --> <template v-if="item.options" #default="{ row }"> <span v-for="options in item.options"> <el-tag v-if="row[item.prop] === options.value" :type="options.type" :key="options.value" > {{ options.label }} </el-tag> </span> </template> </el-table-column> </el-table> <!-- 分页组件 --> <div class="pagination-container" v-if="total > 0"> <el-pagination v-model:current-page="curPage" v-model:page-size="pageSize" :page-sizes="pageSizes" background :layout="paginationLayout" :total="total" @size-change="handleSizeChange" @current-change="handleCurrentChange" /> </div> </div> </template> <script setup> dDefineOptions({ name: "Table", inheritAttrs: false, }); const page = defineModel("page", 1); const size = defineModel("size", 10); const props = defineProps({ tableData: { type: Array, default: () => [], }, columns: { type: Array, default: () => [], }, showSelection: { type: Boolean, default: false, }, showIndex: { type: Boolean, default: false, }, indexLabel: { type: String, default: "序号", }, indexWidth: { type: String, default: "60", }, total: { type: Number, default: 120, }, pageSizes: { type: Array, default: () => [10, 20, 50, 100], }, paginationLayout: { type: String, default: "total, sizes, prev, pager, next", }, }); const emits = defineEmits(["pagination", "selection-change"]); const pageSize = computed({ get: () => size.value, set: (val) => { size.value = val; }, }); const curPage = computed({ get: () => page.value, set: (val) => { page.value = val; }, }); const handleSelectionChange = (selection) => { emits("selection-change", selection); }; const handleCurrentChange = () => { emits("pagination"); }; const handleSizeChange = () => { emits("pagination"); }; </script> <style lang="scss" scoped> .pagination-container { margin-top: 20px; text-align: right; } </style>

核心功能解析

1. 列配置系统

组件通过 columns 属性接收列配置,支持多种列类型:

const columns = [ { prop: 'name', label: '姓名', width: '120', align: 'center' }, { prop: 'status', label: '状态', options: [ { value: 1, label: '启用', type: 'success' }, { value: 0, label: '禁用', type: 'danger' } ] }, { prop: 'action', label: '操作', slot: 'action' // 自定义插槽 } ]

2. 插槽机制

支持具名插槽,可以自定义列内容:

<template #action="{ row }"> <el-button type="primary" size="small" @click="edit(row)"> 编辑 </el-button> <el-button type="danger" size="small" @click="delete(row)"> 删除 </el-button> </template>

3. 分页集成

内置分页功能,支持双向绑定:

<Table v-model:page="currentPage" v-model:size="pageSize" :total="total" @pagination="loadData" />

4. 多选功能

通过 showSelection 开启多选,监听选择变化:

<Table :show-selection="true" @selection-change="handleSelection" />

使用示例

基础用法

<template> <div> <Table :table-data="tableData" :columns="columns" :show-index="true" :show-selection="true" :total="total" v-model:page="currentPage" v-model:size="pageSize" @pagination="loadData" @selection-change="handleSelection" > <!-- 自定义操作列 --> <template #action="{ row }"> <el-button type="primary" size="small" @click="editUser(row)"> 编辑 </el-button> <el-button type="danger" size="small" @click="deleteUser(row)"> 删除 </el-button> </template> </Table> </div> </template> <script setup> import { ref, reactive, onMounted } from 'vue' import Table from '@/components/Table.vue' // 响应式数据 const currentPage = ref(1) const pageSize = ref(10) const total = ref(0) const tableData = ref([]) const selectedRows = ref([]) // 列配置 const columns = reactive([ { prop: 'name', label: '姓名', width: '120' }, { prop: 'email', label: '邮箱', width: '200' }, { prop: 'status', label: '状态', width: '100', align: 'center', options: [ { value: 1, label: '启用', type: 'success' }, { value: 0, label: '禁用', type: 'danger' } ] }, { prop: 'createTime', label: '创建时间', width: '180' }, { prop: 'action', label: '操作', width: '150', align: 'center', slot: 'action' } ]) // 模拟数据 const mockData = [ { id: 1, name: '张三', email: 'zhangsan@example.com', status: 1, createTime: '2024-01-15 10:30:00' }, { id: 2, name: '李四', email: 'lisi@example.com', status: 0, createTime: '2024-01-14 09:20:00' }, { id: 3, name: '王五', email: 'wangwu@example.com', status: 1, createTime: '2024-01-13 14:45:00' } ] // 加载数据 const loadData = async () => { try { // 模拟API调用 await new Promise(resolve => setTimeout(resolve, 500)) // 模拟分页数据 const start = (currentPage.value - 1) * pageSize.value const end = start + pageSize.value tableData.value = mockData.slice(start, end) total.value = mockData.length } catch (error) { console.error('加载数据失败:', error) } } // 处理选择变化 const handleSelection = (selection) => { selectedRows.value = selection console.log('选中的行:', selection) } // 编辑用户 const editUser = (row) => { console.log('编辑用户:', row) // 这里可以打开编辑对话框或跳转到编辑页面 } // 删除用户 const deleteUser = (row) => { console.log('删除用户:', row) // 这里可以显示确认对话框 } // 组件挂载时加载数据 onMounted(() => { loadData() }) </script>

高级用法

<template> <div> <!-- 工具栏 --> <div class="toolbar"> <el-button type="primary" @click="batchDelete" :disabled="!selectedRows.length"> 批量删除 ({{ selectedRows.length }}) </el-button> <el-button @click="exportData"> 导出数据 </el-button> </div> <!-- 表格 --> <Table :table-data="tableData" :columns="columns" :show-index="true" :show-selection="true" :total="total" v-model:page="currentPage" v-model:size="pageSize" @pagination="loadData" @selection-change="handleSelection" stripe border > <!-- 自定义状态列 --> <template #status="{ row }"> <el-switch v-model="row.status" :active-value="1" :inactive-value="0" @change="updateStatus(row)" /> </template> <!-- 自定义操作列 --> <template #action="{ row }"> <el-dropdown> <el-button type="primary" size="small"> 操作 <el-icon><arrow-down /></el-icon> </el-button> <template #dropdown> <el-dropdown-menu> <el-dropdown-item @click="viewDetail(row)"> 查看详情 </el-dropdown-item> <el-dropdown-item @click="editUser(row)"> 编辑 </el-dropdown-item> <el-dropdown-item @click="resetPassword(row)"> 重置密码 </el-dropdown-item> <el-dropdown-item divided @click="deleteUser(row)"> <span style="color: #f56c6c">删除</span> </el-dropdown-item> </el-dropdown-menu> </template> </el-dropdown> </template> </Table> </div> </template> <script setup> // ... 其他代码类似基础用法 // 批量删除 const batchDelete = () => { if (selectedRows.value.length === 0) { ElMessage.warning('请选择要删除的数据') return } ElMessageBox.confirm( `确定要删除选中的 ${selectedRows.value.length} 条数据吗?`, '批量删除', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning', } ).then(() => { // 执行批量删除逻辑 console.log('批量删除:', selectedRows.value) ElMessage.success('删除成功') loadData() }) } // 更新状态 const updateStatus = async (row) => { try { // 调用API更新状态 console.log('更新状态:', row) ElMessage.success('状态更新成功') } catch (error) { // 回滚状态 row.status = row.status === 1 ? 0 : 1 ElMessage.error('状态更新失败') } } </script> <style scoped> .toolbar { margin-bottom: 16px; } </style>

组件优势

1. 开发效率提升

  • 快速配置:通过简单的配置即可生成复杂表格
  • 代码复用:一次封装,多处使用
  • 维护简单:统一的组件逻辑,便于维护和升级

2. 功能完善

  • 插槽支持:灵活的自定义内容
  • 状态管理:内置状态标签显示
  • 分页集成:无需额外配置分页逻辑
  • 事件处理:完善的事件回调机制

3. 扩展性强

  • 属性透传:支持 Element Plus 原生属性
  • 插槽机制:支持任意自定义内容
  • 事件监听:支持所有原生事件

总结

通过封装通用的 Table 组件,我们可以:

  1. 提高开发效率:减少重复代码,快速构建表格页面
  2. 统一用户体验:保持一致的交互和视觉效果
  3. 便于维护:集中管理表格相关逻辑,便于后期维护和功能扩展
  4. 增强可复用性:一次开发,多处使用,降低开发成本

这个组件在实际项目中已经得到了广泛应用,大大提升了后台管理系统的开发效率。你可以根据具体需求进一步扩展功能,比如添加排序、筛选、导出等功能。

Last updated on