在中后台项目开发中,表格是高频使用的核心组件,基于 Element Plus 的el-table封装通用表格组件,能够统一表格样式、简化重复代码、提升开发效率。本文将详细讲解一款通用表格组件的封装思路、完整实现及使用方式,该组件兼顾了通用性与灵活性,适配日常开发中的各类表格场景。
一、封装思路
本次封装的核心目标是打造一款「基础能力通用化、个性化配置灵活化」的表格组件:
- 抽离表格通用配置(如高度、高亮行、合并单元格方法)作为基础 Props;
- 将
el-table和el-pagination的原生属性 / 事件通过透传方式交给父组件控制,保留原生组件的灵活性; - 统一列渲染逻辑,支持自定义
render函数实现复杂单元格内容展示; - 整合表格标题、分页等常用元素,形成完整的表格模块。
二、通用表格组件完整实现(MineTable.vue)
vue
<template>
<el-card class="mine-table">
<!-- 表格标题 -->
<el-text class="table-name">{{ tableName }}</el-text>
<!-- 核心表格容器 -->
<el-table
ref="elTable"
class="base-table"
:highlight-current-row="currentRow"
:preserve-expanded-content="true"
:span-method="spanMethod"
:data="data"
:height="height"
v-bind="tableProps" <!-- 透传el-table原生属性 -->
v-on="tableEvents" <!-- 透传el-table原生事件 -->
>
<el-table-column
v-for="(item, index) in columnsData"
:key="index"
v-bind="item" <!-- 透传列配置属性 -->
>
<!-- 展开列自定义渲染 -->
<template v-if="item.type === 'expand'" #default="scope">
<component :is="item.render" v-bind="scope"></component>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<el-pagination
class="base-pagination"
layout="total, sizes, prev, pager, next, jumper"
:page-sizes="[5, 10, 20, 30, 40, 50]"
background
v-bind="paginationProps" <!-- 透传el-pagination原生属性 -->
v-on="paginationEvents" <!-- 透传el-pagination原生事件 -->
/>
</el-card>
</template>
<script setup>
import { computed, ref } from "vue"
// 关闭默认属性透传,避免属性泄露到外层DOM节点
defineOptions({
inheritAttrs: false
})
// 定义组件Props
const props = defineProps({
// 表格基础配置
tableName: { type: String, default: "", description: "表格标题" },
currentRow: { type: Boolean, default: false, description: "是否高亮当前行" },
height: { type: String, default: "60vh", description: "表格高度" },
data: { type: Array, default: () => [], description: "表格数据源" },
columns: { type: Array, default: () => [], description: "列配置项" },
spanMethod: { type: Function, default: () => {}, description: "单元格合并方法" },
// el-table原生属性透传(支持所有el-table属性)
tableProps: { type: Object, default: () => ({}) },
// el-table原生事件透传(支持所有el-table事件)
tableEvents: { type: Object, default: () => ({}) },
// el-pagination原生属性透传(支持所有el-pagination属性)
paginationProps: { type: Object, default: () => ({}) },
// el-pagination原生事件透传(支持所有el-pagination事件)
paginationEvents: { type: Object, default: () => ({}) },
})
// 暴露表格Ref,方便父组件调用el-table的原生方法
const elTable = ref(null)
defineExpose({ elTable })
// 列数据格式化处理,统一支持render函数渲染
const columnsData = computed(() => {
return props.columns.map(item => ({
formatter: (row, column, cellValue, index) => formatter(item, row, column, cellValue, index),
...item
}))
})
// 单元格内容格式化逻辑
const formatter = (item, row, column, cellValue, index) => {
// 优先级:行数据中的render函数 > 列配置中的render函数 > 默认值
if (row?.[column.property]?.render) {
return row[column.property].render(row, column, cellValue, index)
} else if (item?.render) {
return item.render(row, column, cellValue, index)
}
return row[column.property]
}
</script>
<style lang="scss" scoped>
.mine-table {
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
.table-name {
font-size: 18px;
font-weight: bold;
margin-bottom: 12px;
display: flex;
align-items: center;
&::after {
content: "";
width: 5px;
height: 100%;
background-color: var(--el-color-primary);
margin-right: 12px;
}
}
.base-table {
width: 100%;
margin: 0 auto;
min-width: 0;
border: var(--el-table-border);
border-radius: 4px;
}
.base-pagination {
margin-top: 12px;
}
}
</style>
核心封装点说明
- 属性 / 事件透传 :通过
tableProps/tableEvents、paginationProps/paginationEvents分别透传el-table和el-pagination的原生属性与事件,既保留了原生组件的全部能力,又无需在组件内重复定义中转逻辑。 - 统一列渲染 :封装了
formatter函数,支持两种自定义渲染方式 ------ 列配置中的render函数、行数据中的render函数,满足复杂单元格的展示需求。 - 基础样式整合:内置了表格标题、表格容器、分页的统一样式,无需在业务页面重复编写样式代码。
- Ref 暴露 :将
el-table的 Ref 暴露给父组件,方便调用clearSelection、toggleRowSelection等原生方法。
三、组件使用示例
1. 基础使用(仅核心配置)
这是最常用的场景,只需配置表格数据、列配置、基础样式即可:
vue
<template>
<div class="demo-container">
<!-- 通用表格组件使用 -->
<MineTable
height="200px"
tableName="用户列表"
:data="tableData"
:columns="tableColumns"
/>
</div>
</template>
<script setup>
import { ref } from "vue"
import MineTable from "@/components/MineTable.vue"
import { ElMessage, ElPopconfirm, ElButton, ElText } from "element-plus"
// 表格数据源
const tableData = ref([
{ id: 1, name: "张三", email: "zhangsan@example.com" },
{ id: 2, name: "李四", email: "lisi@example.com" },
{ id: 3, name: "王五", email: "wangwu@example.com" }
])
// 列配置项
const tableColumns = ref([
{ type: "index", label: "序号", width: 80 }, // 序号列
{
label: "用户名称",
prop: "name",
// 自定义单元格渲染
render: (row) => <ElText type="primary">{row.name}</ElText>
},
{
label: "操作",
width: 100,
// 操作列:带确认弹窗的删除按钮
render: (row) => {
const deleteUser = () => {
// 模拟删除逻辑
tableData.value = tableData.value.filter(item => item.id !== row.id)
ElMessage.success(`已删除用户:${row.name}`)
}
return (
<ElPopconfirm
title="确定删除吗?"
onConfirm={deleteUser}
confirmButtonText="确定"
cancelButtonText="取消"
v-slots={{
reference: () => <ElButton type="danger" size="small" link>删除</ElButton>
}}
/>
)
}
}
])
</script>
<style scoped>
.demo-container {
width: 800px;
margin: 20px auto;
}
</style>
2. 进阶使用(透传原生属性 / 事件)
如果需要使用el-table或el-pagination的原生能力(如斑马纹、行点击事件、分页回调等),可通过透传 Props 实现:
vue
<template>
<div class="demo-container">
<MineTable
height="300px"
tableName="用户列表"
:data="tableData"
:columns="tableColumns"
<!-- 透传el-table原生属性 -->
:table-props="{
border: true, // 显示表格边框
stripe: true, // 斑马纹效果
showHeader: true // 显示表头
}"
<!-- 透传el-table原生事件 -->
:table-events="{
'row-click': (row) => ElMessage.info(`点击了${row.name}的行`), // 行点击事件
'sort-change': (val) => console.log('排序变更:', val) // 排序变更事件
}"
<!-- 透传el-pagination原生属性 -->
:pagination-props="{
currentPage: 1, // 当前页码
pageSize: 10, // 每页条数
total: 100 // 总条数
}"
<!-- 透传el-pagination原生事件 -->
:pagination-events="{
'size-change': (size) => console.log('每页条数变更:', size), // 页大小变更
'current-change': (page) => console.log('页码变更:', page) // 页码变更
}"
/>
</div>
</template>
四、总结
本次封装的通用表格组件具备以下特点:
- 通用性强:整合了表格标题、分页等常用元素,统一了基础样式和渲染逻辑;
- 灵活性高:通过属性 / 事件透传,保留了 Element Plus 原生组件的全部能力,适配各类个性化需求;
- 易用性好:使用方式简洁,基础场景只需配置数据和列,进阶场景可透传原生属性 / 事件;
- 可扩展:在此基础上可进一步扩展空状态、加载状态、列宽自适应等通用能力,适配更多业务场景。
该组件能够有效减少中后台项目中表格相关的重复代码,提升开发效率,同时保持了足够的灵活性,满足不同业务场景的个性化需求。