前言
element里面的table组件在我所在的公司项目中是最常用到的组件,这个组件十分的灵活,它自带了自定义单元格,自定义表头,实现列固定等许多方便的api。
但是,如果你的table组件只是单纯的展示单元格内容,不同的可能只是列的名称和数据,那么你会发现每次都要写一堆的template是十分的痛苦。
因此,我对el-table进行了二次封装,在不改动其可自定义编辑单元格的基础上,对其进行通过写配置的方式进行渲染,减少工作量。
组件的设计
我只需要传入两个参数,一个是columns,一个是data
props: {
// 列配置
columns: {
type: Array,
require: true,
},
// 数据
data: {
type: Array,
require: true,
},
},
使用方法(参考)
<ETable :columns="columns" :data="tableData">
<template #age="{ row }">
<el-input v-model="row.age" />
</template>
</ETable>
data() {
return {
columns: [
{
type: "selection",
fixed: "left",
},
{
prop: "name",
label: "名字",
width: "300",
},
{
prop: "age",
label: "年龄",
width: "300",
},
{
prop: "sexual",
label: "性别",
fixed: "right",
},
],
tableData: [
{
name: "test-name",
age: "test-age",
sexual: "male"
}
],
};
},
组件的实现
我们翻看官方文档,发现el-table-column提供了 scopedSlots 这个属性。那么,思路就明确了,如果组件中对应的列没有template,那么我们自己做一个默认的 template 展示数据即可。
export default {
name: "ETable",
props: {
columns: {
type: Array,
require: true,
},
data: {
type: Array,
require: true,
},
},
data() {
return {};
},
methods: {
renderScopedSlot(propName, scopedSlots) {
// 说明是column自带的选择框或者序号等
if (!propName) {
return;
}
// 默认的模板
const tmp = {
default: ({ row }) => <span>{row[propName]}</span>,
};
// 有提供就用提供的模板
if (scopedSlots[propName]) {
tmp.default = scopedSlots[propName];
}
return tmp;
},
},
render() {
const {
$attrs,
$listeners,
$scopedSlots,
data,
columns,
renderScopedSlot,
} = this;
// 兼容el-table的api和事件
const tableProps = {
on: $listeners,
attrs: $attrs,
};
return (
<div class="e-table">
<el-table data={data} {...tableProps}>
{columns.map(({ label, prop, ...otherProps }) => (
<el-table-column
key={label}
label={label}
scopedSlots={renderScopedSlot(prop, $scopedSlots)}
{...{
props: { ...otherProps },
}}
></el-table-column>
))}
</el-table>
</div>
);
},
};
测试
<template>
<div>
<ETable :columns="columns" :data="tableData" @selection-change="handleSelectionChange">
<template #name="{ row }"> {{ "测试slot" + row.name }} </template>
<template #age="{ row }">
<el-input v-model="row.age" />
</template>
</ETable>
</div>
</template>
import ETable from "./components/index.vue";
export default {
components: { ETable },
props: {},
data() {
return {
columns: [
{
type: "selection",
fixed: "left",
},
{
prop: "name",
label: "名字",
width: "300",
},
{
prop: "age",
label: "年龄",
width: "300",
},
{
prop: "area",
label: "地区",
width: "300",
},
{
prop: "test1",
label: "测试1",
width: "300",
},
{
prop: "test2",
label: "测试2",
width: "300",
},
{
prop: "sexual",
label: "性别",
fixed: "right",
},
],
tableData: [],
rowSelection: [],
};
},
mounted() {
this.tableData = this.generateData(this.columns);
},
methods: {
generateData(columns, num = 100) {
let arr = [];
for (let i = 0; i < num; i++) {
arr.push(
Object.assign(
{},
columns.reduce((prev, curr) => {
return {
...prev,
[curr.prop]: ((i + 1) * Math.random()).toFixed(3),
};
}, {})
)
);
}
return arr;
},
handleSelectionChange(selection) {
this.rowSelection = selection;
},
},
};
效果
可以看到,无论是element自带的选择框,还是固定列都能使用。同时,我自定义的name,age列可以自定义,同时其他列是正常的渲染。