在开发过程中,我们经常需要创建具有复杂功能的表格组件。本文将介绍如何使用 Vue 3 和 Element Plus 库来构建一个可扩展的表格组件,包括增加自定义插槽、操作栏按钮以及动态列内容。
组件概述
我们将创建一个名为 Table
的 Vue 组件,该组件具有以下特性:
- 支持固定表头和边框
- 可配置的列宽和最小列宽
- 支持树形数据和展开行
- 自定义列内容和操作栏按钮
- 动态计算表格高度
以下是组件的实现步骤和代码示例。
组件实现
1. 组件模板
首先,我们定义组件的模板部分,使用 el-table
组件来创建表格,并添加三个自定义插槽:default
、operationsStart
和 operationsEnd
。
html
<template>
<el-table
border
stripe
:data="data"
:height="tableHeight"
table-layout="fixed"
:row-key="rowKey"
:highlight-current-row="highlightCurrentRow"
:tree-props="treeProps"
:header-cell-style="{
background: '#f5f7fa',
color: '#606266',
fontSize: '13px',
}"
@selection-change="handleSelectionChange"
:v-loading="loading"
:row-class-name="tableRowClassName"
:expand-row-keys="expandRowKeys"
class="absolute"
>
<el-table-column
v-if="showSelection"
align="center"
fixed
type="selection"
width="55"
/>
<el-table-column
v-for="tableModel in tableModels"
:key="tableModel.key"
:prop="tableModel.key"
:label="tableModel.label"
:width="tableModel.width"
:min-width="tableModel.minWidth"
:align="tableModel.align"
:header-align="tableModel.headerAlign"
>
<template #default="{ row }">
<slot
v-if="slots[tableModel.key]"
:name="tableModel.key"
v-bind="row"
></slot>
<span v-else>
{{ row[tableModel.key] }}
</span>
</template>
</el-table-column>
<el-table-column
fixed="right"
label="操作"
:width="operationsWidth"
:min-width="minWidth"
:align="operationsAlign"
:header-align="operationsHeaderAlign"
v-if="shoeOperations"
>
<template #default="{ row, $index }">
<slot name="operationsStart" v-bind="row"></slot>
<el-button
link
type="primary"
:icon="Edit"
@click="handleEdit(row, $index)"
:v-hasPerm="editHasPerm"
v-if="showEditBtn"
>编辑
</el-button>
<el-button
@click="handleDelete(row, $index)"
link
type="danger"
:icon="Delete"
v-if="showDeleteBtn"
:v-hasPerm="deleteHasPerm"
>删除
</el-button>
<slot name="operationsEnd" v-bind="row"></slot>
</template>
</el-table-column>
</el-table>
</template>
2. 组件脚本
在脚本部分,我们定义了组件的 props、slots 和 emits,以及一些必要的响应式数据和函数。
javascript
<script setup lang="ts">
// 使用 unknown 类型表示任何值
type MyUnknownType = unknown;
/*
* @slot default 插槽
* operationsStart 从操作栏开头增加按钮
* operationsStart 从操作栏结尾增加按钮
* Key TableModel里面的Key,用来自定义列的内容
* */
import { Delete, Edit } from '@element-plus/icons-vue';
// 【接口】table表格模型
interface ITableModel {
// 表头显示文字
label: string;
// 表格列Keu
key: string;
// 表格列宽
width?: number | string;
// 表格列显示文字
value?: string;
// 表格内容位置
align?: string;
// 表格表头位置
headerAlign?: string;
// 列最小宽度
minWidth?: number | string;
}
// 【接口】接受传参字段
interface IProps {
// 生成表格参数
tableModels?: ITableModel[];
// 表格数据
data: object[];
// 是否为树形表格
treeProps?: unknown;
// 加载状态
loading?: boolean;
// 表格行Keu
rowKey?: any;
// 表格高度
height?: any;
// 表格自适应减去的高度
// eslint-disable-next-line vue/prop-name-casing
dynamic?: number;
// 表格单选
highlightCurrentRow?: boolean;
// 是否显示编辑按钮
showEditBtn?: boolean;
// 是否显示多选框
showSelection?: boolean;
// 是否显示删除按钮
showDeleteBtn?: boolean;
// 删除权限
deleteHasPerm?: any;
// 编辑权限
editHasPerm?: any;
// 操作栏宽度
operationsWidth?: number;
// 是否显示操作栏
shoeOperations?: boolean;
// 操作栏内容位置
operationsAlign?: string;
// 操作栏头部位置
operationsHeaderAlign?: string;
// 操作栏最小宽度
minWidth?: number | string;
// 树表格时默认展开参数
expandRowKeys?: any;
}
// 【接口】分页
interface IPage {
// 当前页
pageNum: number;
// 每页显示条数
pageSize: number;
}
// 初始化默认参数
const props = withDefaults(defineProps<IProps>(), {
loading: false,
rowKey: 'id',
dynamic: 0,
showEditBtn: true,
highlightCurrentRow: false,
showSelection: false,
showDeleteBtn: true,
deleteHasPerm: [],
editHasPerm: [],
tableModels: () => [],
treeProps: () => [],
shoeOperations: true,
operationsWidth: 150,
operationsAlign: 'center',
operationsHeaderAlign: 'center',
expandRowKeys: [],
minWidth: 'auto',
height: 'auto'
});
// 插槽对象
const slots = useSlots();
// 定义组件发出的事件
const emit = defineEmits(['handleEdit', 'handleDelete', 'handlePagination', 'handleSelectionChange', 'handlePage']);
const tableHeight = ref<any>();
// watch监听动态计算table的高度,根据dynamic是否大于0,dynamic层级大于height
watch(
[() => props.height, () => props.dynamic],
([height, dynamic]) => {
if (height && !dynamic) {
tableHeight.value = height;
} else if ((!height && dynamic) || (height && dynamic)) {
// 获取浏览器窗口高度
const windowHeight = window.innerHeight;
tableHeight.value = windowHeight - dynamic;
} else {
tableHeight.value = 'auto';
}
},
{
deep: true,
immediate: true
}
);
// 增加样式Class
const tableRowClassName = ({ row, rowIndex }: { row: Object; rowIndex: number }) => {
if (rowIndex % 2 === 1) {
return 'warning-row';
}
return '';
};
// 点了多选
const handleSelectionChange = (val: ITableModel[]) => {
emit('handleSelectionChange', val);
};
// 点击了编辑
const handleEdit = (data: object, index: number) => {
emit('handleEdit', data, index);
};
// 点击了删除
const handleDelete = (data: object, index: number) => {
emit('handleDelete', data, index);
};
</script>
3. 使用案例
以下是如何在父组件中使用 Table
组件的示例,包括自定义列内容和操作栏按钮。
html
<Table
:dynamic="dynamicHeight"
:tableModels="table.tableModels"
:data="table.data"
:loading="table.loading"
:showSelection="table.showSelection"
:operationsWidth="table.operationsWidth"
@handle-edit="openDialog"
@handle-delete="handleDelete"
@handle-selection-change="handleSelectionChange"
>
<!-- 自定义列【status】字段 -->
<template #status="scope">
<el-tag size="small" v-if="scope.status === 1" type="success">
正常
</el-tag>
<el-tag size="small" v-else type="info">禁用</el-tag>
</template>
<!-- 操作栏从开头增加按钮 -->
<template #operationsStart="scope">
<el-button
type="primary"
size="small"
link
:icon="Position"
@click="openMenuDialog(scope)"
>
分配权限
</el-button>
</template>
</Table>
4. 渲染表格的数据
最后,我们定义表格的列模型和数据。
javascript
const table = reactive({
tableModels: [
{
label: "角色名称",
key: "name",
align: "left",
headerAlign: "center",
value: "name",
minWidth: 200,
},
{
label: "角色编码",
key: "code",
width: 200,
align: "center",
headerAlign: "center",
value: "code",
},
{
label: "数据权限",
key: "dataScope",
width: 200,
align: "center",
headerAlign: "center",
value: "dataScope",
},
{
label: "状态",
key: "status",
width: 200,
align: "center",
headerAlign: "center",
value: "status",
},
{
label: "排序",
key: "sort",
width: 200,
align: "center",
headerAlign: "center",
value: "sort",
},
],
data: [] as any[],
showSelection: true,
operationsWidth: 250,
loading: false,
});
结论
通过以上步骤,我们创建了一个功能丰富、可扩展的表格组件。这个组件可以根据实际需求调整列配置、操作按钮和自定义内容,非常适合在复杂的应用场景中使用。希望这篇文章能帮助您在项目中更好地利用 Vue 3 和 Element Plus 库。如果您有任何疑问或建议,欢迎在评论区留言交流。