每当开发过程中遇到涉及表格el-table组件时,都需要重复编写表格自带的逻辑模板,想着能不能写个组件,将el-table模板功能抽离出来。今天,他来了。
步骤一:初始化封装组件
首先,创建一个名为 BaseTable.vue 或类似名称的 Vue 单文件组件 (SFC),用来封装 el-table。
<template>
<div class="base-table">
<el-table :data="tableData" :height="tableHeight" stripe style="width: 100%"
@row-click="handleRowClick"
@sort-change="handleSortChange"
:loading="isLoading">
<el-table-column v-for="(column, index) in columns" :key="index" :label="column.label" :prop="column.prop"
:sortable="column.sortable" :width="column.width">
<template #default="scope">
{{ getColumnValue(scope.row, column) }}
</template>
</el-table-column>
<!-- 其他动态列... -->
<el-table-column v-if="hasSelection" type="selection" />
<el-table-column v-if="hasActions" label="操作" width="180">
<template #default="scope">
<el-button size="small" @click="handleEdit(scope.$index, scope.row)">编辑</el-button>
<el-button size="small" type="danger" @click="handleDelete(scope.$index, scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
v-if="showPagination"
v-model:current-page="currentPage"
:page-sizes="[10, 20, 50]"
:page-size="pageSize"
layout="prev, pager, next, jumper, sizes, total"
:total="total"
@current-change="handlePageChange"
@size-change="handlePageSizeChange"
/>
</div>
</template>
<script setup lang="ts">
import { ref, computed, PropType, watch, toRefs } from 'vue';
import { ElTable, ElTableColumn, ElPagination } from 'element-plus';
interface TableColumn {
prop: string;
label: string;
sortable?: boolean;
width?: string | number;
formatter?: (row: any, column: TableColumn) => string;
}
interface TableProps {
data: any[];
columns: TableColumn[];
hasSelection?: boolean;
hasActions?: boolean;
showPagination?: boolean;
initialPageSize?: number;
initialCurrentPage?: number;
remoteDataFn?: (params: any) => Promise<any>;
}
const props = defineProps({
data: { type: Array as PropType<any[]>, default: () => [] },
columns: { type: Array as PropType<TableColumn[]>, required: true },
hasSelection: { type: Boolean, default: false },
hasActions: { type: Boolean, default: false },
showPagination: { type: Boolean, default: true },
initialPageSize: { type: Number, default: 10 },
initialCurrentPage: { type: Number, default: 1 },
remoteDataFn: { type: Function as PropType<(params: any) => Promise<any>>, default: null },
});
const emit = defineEmits(['row-click', 'edit', 'delete']);
const { data: originData, columns, hasSelection, hasActions, showPagination, initialPageSize, initialCurrentPage, remoteDataFn } = toRefs(props);
// 内部状态
const tableData = ref(originData.value);
const currentPage = ref(initialCurrentPage.value);
const pageSize = ref(initialPageSize.value);
const sortKey = ref('');
const sortOrder = ref('ascend');
const isLoading = ref(false);
const tableHeight = computed(() => '500px'); // 根据实际情况调整高度
watch([currentPage, pageSize], async () => {
if (remoteDataFn) {
await loadRemoteData();
}
});
async function loadRemoteData() {
isLoading.value = true;
try {
const params = {
page: currentPage.value,
pageSize: pageSize.value,
sortKey,
sortOrder,
};
const response = await remoteDataFn!(params);
tableData.value = response.data;
total.value = response.total; // 假设response包含总条数
} catch (error) {
console.error('获取数据失败', error);
} finally {
isLoading.value = false;
}
}
// 方法示例
function getColumnValue(row: any, column: TableColumn) {
return column.formatter ? column.formatter(row, column) : row[column.prop];
}
function handleRowClick(row: any, event: Event) {
emit('row-click', row, event);
}
function handleEdit(index: number, row: any) {
emit('edit', row);
}
function handleDelete(index: number, row: any) {
emit('delete', row);
}
// 分页、排序相关事件处理
function handlePageChange(page: number) {
currentPage.value = page;
}
function handlePageSizeChange(size: number) {
pageSize.value = size;
}
function handleSortChange({ column, prop, order }: { column: any; prop: string; order: string }) {
sortKey.value = prop;
sortOrder.value = order;
if (remoteDataFn) {
currentPage.value = 1; // 切换排序时重置回第一页
loadRemoteData();
}
}
</script>
<style scoped>
.base-table {
/* 添加自定义样式 */
}
</style>
App.vue(调用示例)
<template>
<div id="app">
<BaseTable :data="tableData" :columns="tableColumns" :remoteDataFn="fetchData" showPagination hasSelection hasActions />
</div>
</template>
<script setup lang="ts">
import BaseTable from './components/BaseTable.vue';
import { reactive } from 'vue';
const tableData = reactive([]);
const tableColumns = [
{ prop: 'id', label: 'ID', width: '80' },
{ prop: 'name', label: '姓名', sortable: true },
{ prop: 'age', label: '年龄', width: '100' },
{ prop: 'email', label: '邮箱' },
];
async function fetchData(params: any) {
// 这里模拟远程接口调用,实际应用中替换为真实的API请求
const response = await fetch('/api/users', {
method: 'POST',
body: JSON.stringify(params),
});
const data = await response.json();
return data;
}
// 初始化数据
async function init() {
await fetchData({ page: 1, pageSize: 10 });
}
init();
</script>
在这个例子中,BaseTable 组件接收了 data、columns、remoteDataFn 等属性,并提供了点击行、编辑和删除等事件。同时,它还包含了分页功能和远程数据加载的逻辑,当用户改变页面、页大小或排序时会自动触发数据刷新。在父组件 App.vue 中,我们定义了表格数据结构和列配置,并通过 fetchData 方法模拟了远程数据获取。