Table组件的封装特点:
-
灵活的插槽机制:提供默认插槽和多个自定义插槽,轻松实现功能扩展和个性化内容渲染;
-
完善的分页功能:集成分页控件,支持页码和页大小的切换,方便数据管理和浏览;
-
树形结构支持:能够展示层级关系的数据,适用于需要树形展示的场景;
-
行点击响应:内置行点击事件,方便用户进行行级别的交互操作;
-
选择功能:支持单选或多选模式,轻松实现表格行的选择事件处理;
-
更多扩展特性:预留接口和配置项,支持排序、筛选、自定义列渲染等高级功能,以适应多样化的业务场景。
封装步骤:
步骤一:创建通用表格组件结构
- 创建组件文件:在项目中创建一个名为CommonTable.vue文件
- 定义组件模版:在
<template>标签内,定义表格的结构,包括表格头部、主体和分页。
<template>
<div class="table-main">
<!-- 表格头部 操作按钮 -->
<div class="table-header">
<div class="header-button-lf">
<slot name="tableHeader"/>
</div>
<div class="header-button-ri">
<slot name="toolButton"/>
</div>
</div>
<!-- 表格主体 -->
<el-table
ref="table"
v-bind="$attrs"
:data="tableData"
:header-cell-style="cellStyle"
@row-click="rowClick"
:border="border"
:rowKey="rowKey"
:height="height"
:lazy="lazy"
:tree-props="treeProps"
:max-height="maxHeight"
@selection-change="selectionChange"
>
<!-- 默认插槽 -->
<slot/>
<template v-for="(item, index) in columns">
<el-table-column
v-if="item.prop !== 'operation'"
:key="index"
v-bind="item"
:align="item.align? item.align : 'center'"
>
<template v-slot:default="scope" v-if="item.slotName">
<!-- 这里展示 自定义 插槽内容 -->
<slot :name="item.slotName" v-bind="scope"/>
</template>
<template v-if="item.children">
<el-table-column
v-for="(child, iex) in item.children"
:key="iex"
v-bind="child"
:align="item.align? item.align : 'center'"
>
<template v-slot:default="scope" v-if="item.slotName">
<!-- 这里展示 自定义 插槽内容 -->
<slot :name="item.slotName" v-bind="scope"/>
</template>
</el-table-column>
</template>
</el-table-column>
<el-table-column
v-if="item.prop === 'operation'"
:key="index"
v-bind="item"
:align="item.align? item.align : 'center'"
>
<template v-slot:default="scope">
<!-- 这里展示 operation 插槽内容 -->
<slot name="operation" v-bind="scope"/>
</template>
</el-table-column>
</template>
<template #empty>
<el-empty :image-size="300" description="暂无数据"></el-empty>
</template>
</el-table>
<!-- 分页组件 -->
<div class="pagination">
<el-pagination
v-if="pagination"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="pageParam.pageNum"
:page-sizes="[10, 20, 30, 40, 50, 100]"
:page-size="pageParam.pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
/>
</div>
</div>
</template>
表格头部容器 .table-header 这是一个包裹所有头部内容的容器
操作按钮区域 .header-button-lf 与 .header-button-ri 这个区域用于放置表格的头部操作按钮,它通过一个名为 tableHeader yu toolButton 的插槽来实现内容的自定义。这意味着在使用 CommonTable 组件时,你可以通过这个插槽插入任何自定义的头部内容,比如搜索框、筛选器等。
表格主体 以下是对 <el-table> 组件及其示例属性的讲解:
ref="table":为表格设置引用名称,方便在 Vue 组件中通过this.$refs.table访问表格实例。v-bind="$attrs":将父组件传递给子组件的所有非 prop 属性绑定到<el-table>上,使得可以传递更多属性。:data="tableData":绑定表格数据,tableData是一个数组,数组中的每个对象代表表格的一行。:header-cell-style="cellStyle":自定义表头单元格的样式。@row-click="rowClick":监听行点击事件,当点击某一行时触发rowClick方法。:border="border":设置表格是否带有边框。:rowKey="rowKey":行的 key,用于优化渲染。:height="height":表格的高度,如果设置了这个属性,表格会固定高度并出现滚动条。:lazy="lazy":是否懒加载子节点数据。:tree-props="treeProps":树形表格的配置选项。:max-height="maxHeight":表格的最大高度。@selection-change="selectionChange":当选择项发生变化时会触发这个事件。
插槽 <slot/> 这是一个默认插槽,允许你在 <el-table> 组件内部插入任何内容,比如自定义的列或者行。
列的循环渲染 使用 v-for 指令循环 columns 数组,为每个列定义 <el-table-column> 组件。
v-if="item.prop !== 'operation'":判断当前列是否为操作列,如果不是,则渲染常规列。:key="index":为每个列设置唯一的 key 值,通常是列的索引。v-bind="item":将列的配置对象绑定到<el-table-column>上。:align="item.align? item.align : 'center'":设置列的对齐方式,默认为居中。
自定义插槽内容 如果列配置中有 slotName 属性,则表示该列需要自定义渲染内容。使用 <slot :name="item.slotName" v-bind="scope"/> 插槽来展示自定义内容。
子列的渲染 如果列配置中有 children 属性,表示这是一个分组列,需要递归渲染子列。
操作列的渲染 如果列的 prop 属性值为 'operation',则渲染操作列。操作列通常包含对当前行的操作按钮。
<slot name="operation" v-bind="scope"/>:使用名为operation的插槽来展示操作按钮。
空状态插槽 使用 #empty 插槽来定义当表格没有数据时的展示内容。这里使用了 <el-empty> 组件来显示“暂无数据”的信息
步骤二:定义组件的props和events
- 定义 props:在
<script>标签内的export default对象中,定义组件接收的属性。 - 定义 events:同样在
<script>标签内,定义组件触发的事件。
在 Vue 组件中,props 和 events 是组件与外部环境通信的关键机制。以下是对 CommonTable 组件中定义的 props 和 events 的详细讲解:
<script>
export default {
name: 'CommonTable',
props: {
lazy: {
type: Boolean,
default: false
},
treeProps: {
type: Object,
default() {
return {}
}
},
rowKey: {
type: String,
default: ''
},
height: {
type: [String, Number],
default: '100%'
},
// 表格最高高度
maxHeight: {
type: [String, Number],
default: '100%'
},
// 表格边框
border: {
type: Boolean,
default: true
},
// 表格数据
tableData: {
type: Array,
default() {
return [];
}
},
// 表格列
columns: {
type: Array,
default() {
return [];
}
},
// 表格分页组件 ==> 非必传(默认为true)
pagination: {
type: Boolean,
default: true
},
total: {
type: Number,
default: 0
},
// 表格表头样式
cellStyle: {
type: Object,
default() {
return {};
},
required: false
}
},
data() {
return {
// 分页参数
pageParam: {
pageSize: 10,
pageNum: 1
}
};
},
methods: {
rowClick(row) {
this.$emit("rowClick", row)
},
// 表格多选
selectionChange(val) {
this.$emit('selectionChange', val);
},
// 每页条数
handleSizeChange(val) {
this.pageParam.pageSize = val;
this.pageParam.pageNum = 1; // 切换每页显示条数时,将页码重置为 1
this.$emit('upPage', this.pageParam);
},
// 当前页
handleCurrentChange(val) {
this.pageParam.pageNum = val;
this.$emit('upPage', this.pageParam);
}
}
};
</script>
Props
props 是组件的自定义属性,允许父组件向子组件传递数据。
lazy:指示表格是否开启懒加载,懒加载通常用于树形表格或大量数据的分页加载。treeProps:当表格为树形表格时,该属性用于配置树形表格的属性,如子节点键名等。rowKey:表格行的 key 值,用于优化渲染性能,特别是在进行排序或筛选操作时。height:表格的高度,可以是像素值或百分比。maxHeight:表格的最大高度,当内容超出此高度时,将显示滚动条。border:是否显示表格的边框。tableData:表格的数据源,每个数组元素代表表格的一行。columns:表格的列配置,数组中的每个对象定义了列的属性,如列名、数据字段等。pagination:是否显示分页组件。total:表格数据的总条数,用于分页组件计算总页数。cellStyle:自定义表头单元格的样式。
Events
events 是组件的自定义事件,允许子组件向父组件发送消息或通知。
rowClick:当表格的某一行被点击时触发,传递被点击的行数据作为参数。selectionChange:当表格的选择项发生变化时触发,传递当前选择的行数据数组作为参数。upPage:当分页参数发生变化时触发,传递当前的pageParam对象作为参数,该对象包含pageSize和pageNum属性。
Methods
以下是组件内部定义的方法,用于处理事件和逻辑:
rowClick(row):当行被点击时调用,触发rowClick事件,并将当前行数据传递给父组件。selectionChange(val): 当表格的选择项发生变化时调用,触发selectionChange事件,并将选择的行数据数组传递给父组件。handleSizeChange(val):当分页组件的每页显示条数发生变化时调用,更新pageParam对象,并触发upPage事件。handleCurrentChange(val):当分页组件的当前页码发生变化时调用,更新pageParam对象,并触发upPage事件。
通过这些 props 和 events,CommonTable 组件可以灵活地接收外部数据,同时也能够将内部状态和事件通知给外部,从而实现组件的复用和高度定制化。
commonTable组件的使用
<template>
<CommonTable
:tableData="data"
v-loading="loading"
:columns="columns"
:total="total"
row-key="id"
:lazy="true"
:treeProps="{children: 'children', hasChildren: 'hasChildren'}"
@upPage="getUpPage"
@selectionChange="handleSelectionChange"
>
<template #toolButton>
<el-button class="filter-item" size="mini" type="primary" @click="add">添加</el-button>
</template>
<template #operation="scope">
<el-button size="mini" type="text" @click="edit(scope.row)">编辑</el-button>
<el-button size="mini" type="text" @click="copy(scope.row)">复制</el-button>
<el-button size="mini" type="text">发布</el-button>
<el-button size="mini" type="text">下架</el-button>
<el-button size="mini" type="text">删除</el-button>
</template>
<template #jobTest="scope">
<span>{{scope.row.jobTest}} </span>
</template>
<template #Status="scope">
<span>
{{scope.row.Status === '00'?'未发布':(scope.row.Status ==='01'?'测评中'(scope.row.Status === '02'?'已结束':'已下架'))}}
</span>
</template>
<template #operation="scope">
<el-button type="text" @click="checkOut('查看', scope.row)">查看</el-button>
<el-button type="text" @click="checkOut('审核', scope.row)">审核</el-button>
</template>
</CommonTable>
</template>
export default {
data(){
return {
multipleSelection: [],
pageParam: {
pageNum: 1, // 当前页码
pageSize: 10 // 每页显示的数据条数
}
columns: [
{type: 'selection', minWidth: 50},
{prop: 'Account', label: '账号', minWidth: 300},
{prop: 'Name', label: '名称', minWidth: 200},
{prop: 'Time', label: '最近登录时间', minWidth: 300},
{prop: 'jobTestName', label: '测评标题', minWidth: 160,slotName: 'jobTest'},
{prop: 'Status', label: '发布状态', minWidth: 100,slotName: 'Status'},
{
prop: 'publish',
label: '审核状态',
minWidth: 110,
formatter: function (row, column, cellValue) {
const option = shzt.find((opt) => opt.value == cellValue);
return option ? option.label : cellValue;
}
},
{
prop: 'accountStatus',
label: '账号状态',
minWidth: 100,
formatter: function (row, column, cellValue) {
switch (cellValue) {
case '01':
return '已启用';
default:
return '已挂起';
}
}
},
{prop: 'operation', label: '操作', fixed: 'right', minWidth: 100}
]
}
},
methods: {
// 查询按钮
searchData() { },
// 查看 审核
checkOut(val, row) { },
getUpPage(pageParam) {
this.pageParam = pageParam;
this.searchData();
},
// 选中数据
handleSelectionChange(val) {
this.multipleSelection = val
},
}
}