前言
以数据驱动表格,减少繁琐的代码。
代码
props部分
tableColumn: {
type: Array,
default: () => [
{
label: "序号",
align: "center",
width: 50,
type: "index",
},
{
prop: "name",
label: "名字",
align: "center",
},
{
prop: "age",
label: "年龄",
align: "center",
},
{
prop: "sex",
label: "性别",
align: "center",
},
],
},
tableData: {
type: Array,
default: () => [
{
name: "拖鞋丢你脸上",
age: 18,
sex: "男",
},
],
},
size: {
type: String,
default: "medium",
},
stripe: {
type: Boolean,
default: false,
},
border: {
type: Boolean,
default: false,
},
fit: {
type: Boolean,
default: true,
},
showHeader: {
type: Boolean,
default: true,
},
headerCellStyle: {
type: Function,
default: (/*{ row, column, rowIndex, columnIndex }*/) => {
return { color: "#000" };
},
},
cellStyle: {
type: Function,
default: (/** {row, column, rowIndex, columnIndex}*/) => {
return { color: "#333" };
},
},
pagination: {
type: Object,
default: () => ({
current: 1,
size: 10,
total: 0,
}),
},
pageSizes: {
type: Array,
default: () => [10, 20, 30, 50],
},
render部分
render(h) {
const {
tableData,
size,
stripe,
border,
fit,
showHeader,
headerCellStyle,
cellStyle,
sortChange,
select,
selectAll,
selectionChange,
tableColumn,
// 分页
pagination: { current, size: pageSize, total },
pageSizes,
sizeChange,
currentChange,
} = this;
return h(
"div",
{
class: {
"render-table": true,
},
},
[
h(
"el-table",
{
props: {
data: tableData,
size: size,
stripe: stripe,
border: border,
fit: fit,
"show-header": showHeader,
"header-cell-style": headerCellStyle,
"cell-style": cellStyle,
},
on: {
"sort-change": sortChange,
select: select,
"select-all": selectAll,
"selection-change": selectionChange,
},
style: {
width: "100%",
},
},
this.renderColumn(tableColumn, h)
),
h("el-pagination", {
props: {
"current-page": current,
"page-size": pageSize,
"page-sizes": pageSizes,
total: total,
layout: "total, sizes, prev, pager, next, jumper",
},
on: {
"size-change": sizeChange,
"current-change": currentChange,
},
style: {
"text-align": "right",
"margin-top": "10px",
},
}),
]
);
}
methods部分
createColumn(column) {
return [
"el-table-column",
{
props: {
...this.attributesColumn(column),
},
},
];
},
createScopedSlot({ scopedSlots, prop }, h) {
let { tag, text, textStyle, click, slot } = scopedSlots;
let { isTrue, isFunc, columnClick, scopedSlotText } = this;
return {
scopedSlots: {
default: (props) => {
const value = props["row"][prop];
if (slot) {
return h(
"div",
this.$scopedSlots["opration"]({
row: props["row"],
})
);
}
return h(
tag,
{
on: isTrue(click) ? { click: columnClick(props) } : {},
style:
isFunc(textStyle) &&
textStyle(props["$index"] + 1, value, props["row"]),
},
scopedSlotText(text, value, props)
);
},
},
};
},
renderColumn(columns, h) {
if (!columns || !columns.length) {
return;
}
let tableColumn = [];
columns.forEach((item) => {
let { scopedSlots } = item;
let column = this.createColumn(item);
let vNode;
if (this.notEmpty(scopedSlots)) {
column[1] = {
...column[1],
...this.createScopedSlot(item, h),
};
vNode = h(...column);
} else {
vNode = h(...column, this.renderColumn(item.children, h));
}
tableColumn.push(vNode);
});
return tableColumn;
},
attributesColumn(column) {
const attr = [
"type",
"label",
"prop",
"width",
"align",
"fixed",
"sortable",
"scopedSlots",
];
let obj = {};
attr.forEach((prop) => {
column[prop] && (obj[prop] = column[prop]);
});
return obj;
},
notEmpty(obj) {
if (typeof obj === "object" && JSON.stringify(obj) !== "{}") {
return true;
}
return false;
},
isTrue(v) {
return v === true;
},
isDef(v) {
return v === undefined;
},
isFunc(v) {
return typeof v === "function";
},
scopedSlotText(text, value, column) {
const { $index: index, row } = column;
if (!this.isDef(text)) {
return this.isFunc(text) ? text(index, value, row) : text;
}
return value;
},
//自定义列点击
columnClick(scope) {
let { $index, row } = scope;
return () => {
this.$emit("columnClick", {
index: $index,
...row,
});
};
},
//当表格的排序条件发生变化的时候会触发该事件
sortChange({ order, prop }) {
this.$emit("sortChange", { order, prop });
},
//当用户手动勾选数据行的 Checkbox 时触发的事件
select(selection, row) {
this.$emit("select", selection, row);
},
//当用户手动勾选全选 Checkbox 时触发的事件
selectAll(selection) {
this.$emit("select-all", selection);
},
//当选择项发生变化时会触发该事件
selectionChange(selection) {
this.$emit("selection-change", selection);
},
//pageSize 改变时会触发
sizeChange(size) {
this.$emit("pagination-change", {
current: this.pagination.current,
size,
});
},
//currentPage 改变时会触发
currentChange(current) {
this.$emit("pagination-change", {
current,
size:this.pagination.size,
});
},
用法
基础用法
<render-table />
效果
<render-table
:border="true"
:tableColumn="tableColumn"
:tableData="tableData"
/>
tableColumn: [
{
label: "序号",
type: "index",
width: 50,
align: "center",
},
{
label: "学生",
prop: "student",
align: "center",
},
{
label: "数学",
align: "center",
prop: "math",
},
{
label: "语文",
align: "center",
prop: "language",
},
{
label: "英语",
prop: "english",
align: "center",
},
],
tableData: [
{
student: "张三",
math: 60,
language: 60,
english: 60,
},
{
student: "王五",
math: 66,
language: 80,
english: 77,
},
],
通过设置tableColumn,tableData属性来给表格传递数据。
效果
多选
<render-table
:border="true"
:tableColumn="tableColumn"
:tableData="tableData"
@select='select'
@select-all='selectAll'
@selection-change='selectionChange'
/>
{
// label: "序号",
type: "selection",
width: 50,
align: "center",
},
实现多选只需要将type属性值设置为selection。同时提供了select,select-all,selection-change等方法。用法和el-table一致。
效果
支持表头嵌套
{
label: "成绩",
align: "center",
children: [
{
label: "数学",
align: "center",
prop: "math",
},
{
label: "语文",
align: "center",
prop: "language",
},
{
label: "英语",
prop: "english",
align: "center",
},
],
},
实现表头嵌套需要在tableColumn中添加children属性即可。
效果
支持自定义列
{
label: "学生",
prop: "student",
align: "center",
scopedSlots:{
tag:'span',
text:'自定义'
}
},
要实现自定义列需要添加scopedSlots并设置tag,text。其中tag指标签,text是自定义显示文本。text可以是string或者function
当text为函数时
{
label: "学生",
prop: "student",
align: "center",
scopedSlots:{
tag:'span',
text:(index,value,row)=>{
return value;
}
}
},
效果
如果需要设置样式则有textStyle
{
label: "学生",
prop: "student",
align: "center",
scopedSlots:{
tag:'span',
text:(index,value,row)=>{
return value;
},
textStyle:(index,value,row)=>{
return {color:'red'}
}
}
},
效果
需要监听点击回调时有click,columnClick。
<render-table
:border="true"
:tableColumn="tableColumn"
:tableData="tableData"
@select="select"
@select-all="selectAll"
@selection-change="selectionChange"
@columnClick="columnClick"
/>
{
label: "学生",
prop: "student",
align: "center",
scopedSlots:{
tag:'span',
text:(index,value,row)=>{
return value;
},
textStyle:(index,value,row)=>{
return {color:'red'}
},
click:true
}
},
columnClick(row) {
console.log(row);
},
一般来说表格点击都是和操作有关,例如编辑,你可以这样。
{
label:'操作',
align:'center',
scopedSlots:{
tag:'el-button',
click:true,
text:'编辑'
}
}
效果
到这里看起来是不是感觉还阔以,但是操作这里还是有一些问题。如果只使用tag+text+click配合的话是没法弄一些复杂的操作的,例如同时有编辑,删除,查看等按钮。这时就得使用slot这个属性了。
<render-table
:border="true"
:tableColumn="tableColumn"
:tableData="tableData"
@select="select"
@select-all="selectAll"
@selection-change="selectionChange"
@columnClick="columnClick"
>
<template #opration='{row}'>
<el-button @click="edit(row)">编辑</el-button>
<el-button>查看</el-button>
<el-button>删除</el-button>
</template>
</render-table>
{
label:'操作',
align:'center',
scopedSlots:{
slot:true
}
}
效果
属性
没特别说明的用法和el-table一样。
table
data: tableData,
size: size,
stripe: stripe,
border: border,
fit: fit,
"show-header": showHeader,
"header-cell-style": headerCellStyle,
"cell-style": cellStyle,
column
"type",
"label",
"prop",
"width",
"align",
"fixed",
"sortable",
"scopedSlots",
scopedSlots
tag: 元素标签,需要自定义时必填。
text:自定义文本渲染,可以是string/function。function将接收三个参数index,value,row
textStyle:自定义样式function接收三个参数index,value,row,返回值object
click:是否需要点击,true/false
slot:开启插槽,true/false
pagination
"current-page": current,
"page-size": pageSize,
"page-sizes": pageSizes,
total: total,
事件
columnClick(row) //自定义点击回调
//和el-table用法一样
sortChange,
select,
selectAll,
selectionChange,
//分页回调
pagination-change({current,size})
全部代码
//renderTable.vue
<script>
export default {
name: "Render-Table",
props: {
tableColumn: {
type: Array,
},
tableData: {
type: Array,
},
size: {
type: String,
default: "medium",
},
stripe: {
type: Boolean,
default: false,
},
border: {
type: Boolean,
default: false,
},
fit: {
type: Boolean,
default: true,
},
showHeader: {
type: Boolean,
default: true,
},
headerCellStyle: {
type: Function,
default: (/*{ row, column, rowIndex, columnIndex }*/) => {
return { color: "#000" };
},
},
cellStyle: {
type: Function,
default: (/** {row, column, rowIndex, columnIndex}*/) => {
return { color: "#333" };
},
},
pagination: {
type: Object,
default: () => ({
current: 1,
size: 10,
total: 0,
}),
},
pageSizes: {
type: Array,
default: () => [10, 20, 30, 50],
},
},
render(h) {
const {
tableData,
size,
stripe,
border,
fit,
showHeader,
headerCellStyle,
cellStyle,
sortChange,
select,
selectAll,
selectionChange,
tableColumn,
// 分页
pagination: { current, size: pageSize, total },
pageSizes,
sizeChange,
currentChange,
} = this;
return h(
"div",
{
class: {
"render-table": true,
},
},
[
h(
"el-table",
{
props: {
data: tableData,
size: size,
stripe: stripe,
border: border,
fit: fit,
"show-header": showHeader,
"header-cell-style": headerCellStyle,
"cell-style": cellStyle,
},
on: {
"sort-change": sortChange,
select: select,
"select-all": selectAll,
"selection-change": selectionChange,
},
style: {
width: "100%",
},
},
this.renderColumn(tableColumn, h)
),
h("el-pagination", {
props: {
"current-page": current,
"page-size": pageSize,
"page-sizes": pageSizes,
total: total,
layout: "total, sizes, prev, pager, next, jumper",
},
on: {
"size-change": sizeChange,
"current-change": currentChange,
},
style: {
"text-align": "right",
"margin-top": "10px",
},
}),
]
);
},
methods: {
createColumn(column) {
return [
"el-table-column",
{
props: {
...this.attributesColumn(column),
},
},
];
},
createScopedSlot({ scopedSlots, prop }, h) {
let { tag, text, textStyle, click, slot } = scopedSlots;
let { isTrue, isFunc, columnClick, scopedSlotText } = this;
return {
scopedSlots: {
default: (props) => {
const value = props["row"][prop];
if (slot) {
return h(
"div",
this.$scopedSlots["opration"]({
row: props["row"],
})
);
}
return h(
tag,
{
on: isTrue(click) ? { click: columnClick(props) } : {},
style:
isFunc(textStyle) &&
textStyle(props["$index"] + 1, value, props["row"]),
},
scopedSlotText(text, value, props)
);
},
},
};
},
renderColumn(columns, h) {
if (!columns || !columns.length) {
return;
}
let tableColumn = [];
columns.forEach((item) => {
let { scopedSlots } = item;
let column = this.createColumn(item);
let vNode;
if (this.notEmpty(scopedSlots)) {
column[1] = {
...column[1],
...this.createScopedSlot(item, h),
};
vNode = h(...column);
} else {
vNode = h(...column, this.renderColumn(item.children, h));
}
tableColumn.push(vNode);
});
return tableColumn;
},
attributesColumn(column) {
const attr = [
"type",
"label",
"prop",
"width",
"align",
"fixed",
"sortable",
"scopedSlots",
];
let obj = {};
attr.forEach((prop) => {
column[prop] && (obj[prop] = column[prop]);
});
return obj;
},
notEmpty(obj) {
if (typeof obj === "object" && JSON.stringify(obj) !== "{}") {
return true;
}
return false;
},
isTrue(v) {
return v === true;
},
isDef(v) {
return v === undefined;
},
isFunc(v) {
return typeof v === "function";
},
scopedSlotText(text, value, column) {
const { $index: index, row } = column;
if (!this.isDef(text)) {
return this.isFunc(text) ? text(index, value, row) : text;
}
return value;
},
//自定义列点击
columnClick(scope) {
let { $index, row } = scope;
return () => {
this.$emit("columnClick", {
index: $index,
...row,
});
};
},
//当表格的排序条件发生变化的时候会触发该事件
sortChange({ order, prop }) {
this.$emit("sortChange", { order, prop });
},
//当用户手动勾选数据行的 Checkbox 时触发的事件
select(selection, row) {
this.$emit("select", selection, row);
},
//当用户手动勾选全选 Checkbox 时触发的事件
selectAll(selection) {
this.$emit("select-all", selection);
},
//当选择项发生变化时会触发该事件
selectionChange(selection) {
this.$emit("selection-change", selection);
},
//pageSize 改变时会触发
sizeChange(size) {
this.$emit("pagination-change", {
current: this.pagination.current,
size,
});
},
//currentPage 改变时会触发
currentChange(current) {
this.$emit("pagination-change", {
current,
size: this.pagination.size,
});
},
},
};
</script>
以上就是全部代码了,就是把各个部分的代码组合起来而已。我也不清楚为什么你们都说跑不起来。