背景介绍
jsx二次封装table表格组件,通过数据控制属性并展示对应内容,无需重复定义组件标签,只需传入对应数据以及属性所需值,实现动态的table组件
图片预览
官方
二次封装组件
需求分析
需要支持:
- 动态配置表头
- 流体高度
- 多选
- 树形数据
- 动态内容
- 不断完善组件所支持的属性
先看一下官方文档的树形数据表格代码
我们需要简化写法让el-table、el-table-column标签的属性可以动态化,实现可控属性,拓展属性支持,所以我们用数据代替属性
首先我们定义一份el-table、el-table-column的属性数据
export const domlist = {
"componentName":"elMain",
"class":["container"],
"childrenNode":[
{
"componentName":"elMain",
"class":["title"],
},
{
"componentName":"elTable",
"tableDataName":"tableData",
"tableHeightName":"tableHeight",
"refName":"multipleTable",
"tooltipEffect":"dark",
"style":"width: 100%;margin-bottom: 20px;",
"class":["accountTable"],
"stripe":false,
"defaultSort":{"prop":"orderNum","order":"ascending"},
"defaultExpandAll":true,
"treeProp":{"children":"childrenNode","hasChildren":"hasChildren"},
"rowKey":"id",
"childrenNode":[
{"componentName":"elTableColumn","label":"pageRule.componentName","value":"componentName","childrenNode":[]},
{"componentName":"elTableColumn","label":"pageRule.attributeName","value":"attributeName","childrenNode":[]},
{"componentName":"elTableColumn","label":"pageRule.attributeName","value":"attributeName","childrenNode":[]},
{"componentName":"elTableColumn","label":"pageRule.attributeType","value":"attributeType","childrenNode":[]},
{"componentName":"elTableColumn","label":"pageRule.attributeLabel","value":"attributeLabel","childrenNode":[]},
{"componentName":"elTableColumn","label":"pageRule.attributeModel","value":"attributeModel","childrenNode":[]},
{"componentName":"elTableColumn","label":"table.operation","width":"260","align":"center","operation":true,
"childrenNode":[
{"componentName":"elButton","class":["editBtn"],"btnType":"text","event":"editContent","title":["form.edit"],"params":false,"paramsName":"row","childrenNode":[]},
{"componentName":"elButton","class":["delBtn"],"btnType":"text","event":"delHandle","title":["form.delete"],"params":false,"paramsName":"row","childrenNode":[]}
]
}
]
}
]
}
上图的数据html界面,如下代码所示
<main class="container">
<div class="title"></div>
<div class="el-table"></div>
</main>
这里我们可以对这些属性进行动态控制,这些属性都是一一对应el-table所需要的属性
然后我们开始利用jsx渲染表格,利用jsx渲染函数时需要对vue jsx有一定的了解,建议先打开官方渲染函数 & JSX
我们建立一个table.vue的文件,代码如下所示
<template>
<elTableJsx :node="domlist"/>
</template>
<script>
import elTableJsx from '@/components/elTableJsx/index'
import { tableData }from './tableData'
import { domlist } from './domlist'
export default{
provide(){
return {
superParams:this
}
},
components:{
elTableJsx
},
data(){
return {
tableData:tableData,
domlist:domlist,
tableHeight:'600'
}
},
methods:{
getDataList() {
//获取数据
}
}
}
</script>
这里我们引入一个elTableJsx的组件,并引入一份属性数据domlist传递给组件,另外还需要一份表格数据tableData,创建可以向多层子组件注入的依赖provide用于用子组件提供数据和方法(调用)
创建elTableJsx子组件用于渲染table组件,代码如下所示
<script type="text/jsx">
import { filteri18n } from '@/utils/index'
import Template from './template'
export default {
inject: ['superParams'],
name: 'Index',
props:{
node:{
default:function () {
return {}
}
}
},
render(h){
const node = this.node;
if(!!node){
return h(
node.componentName,
{
'class':node.class,
},
this.deepChildrenComponent(node,h)
)
}
},
components:{
Template
},
methods:{
filteri18n,
deepChildrenComponent(node,h){
var that = this;
return node.childrenNode.map(function (item) {
return h(
item.componentName == 'elMain' ? 'div' : item.componentName,
{
'class':item.class,
props: //compontent attribute
item.componentName == 'elTable' ?
{
class:item.class,
data:that.superParams[item.tableDataName],
tooltipEffect:item.tooltipEffect,
style:[item.style],
stripe:item.stripe,
height:that.superParams[item.tableHeightName],
defaultSort:item.defaultSort || '',
rowKey:item.rowKey || '',
defaultExpandAll:item.defaultExpandAll || '',
treeProps:item.treeProp || '',
} : item.componentName == 'elTableColumn' ?
{
type:item.type,
label:that.$t(item.label),
width:item.width,
align:'center'
}
: item,
on:{
'&click':function (e) {
switch (item.componentName) {
case "elButton" :
const elButtonParent = that.$parent;
that.btnClick(item);
break;
default:
break;
}
},
'selection-change':function (e) {
that.selectChange(e,item);
}
},
directives: [
{
name: 'loading',
value: item.componentName == 'elTable' ? that.tableLoading : false
}
],
key:Math.random(), //elTable required
ref:item.refName || '',
scopedSlots: item.componentName == 'elTableColumn' && !item.operation && {
default: props => h('Template',{props,item}) //通过单文件组件展示对应的信息(组件需要的一切都是通过 context 参数传递)
}
},
item.childrenNode && item.childrenNode.length > 0
? that.deepChildrenComponent(item,h)
: item.componentName == 'elButton'
? [h('span',that.filteri18n(item.title))]
: [h('h1','elTableJsx')]
)
})
},
btnClick(item){
if(item.params){
this.superParams[item.event](item.paramsName);
}else if(item.tableParams){
this.superParams[item.event](this.rowData);
} else{
this.superParams[item.event]();
}
},
selectChange(val,item){
var selectName = item.selectionEvent;
this.superParams[selectName](val);
},
indexMethod(index) {
if (index < 9) {
return "0" + (index + 1);
} else {
return index + 1;
}
},
getObjKey(row,key){
return _.get(row, key);
}
},
data(){
return {
tableLoading:true
}
},
mounted(){
var that = this;
setTimeout(() => {
that.tableLoading = false;
}, 1500);
}
}
</script>
<style lang="scss" scoped>
@import "~@/styles/elComponent.scss";
</style>
代码解析:
- node:父组件传入的数据
- deepChildrenComponent:用于递归组件
- Template:elTableColumn单文件组件(没有响应式数据,只作展示数据用途)
- defaultSort、rowKey、defaultExpandAll、treeProps:在jsx props表示该组件的属性也就是该组件所支持的属性,这些名称都是element ui table组件中所支持的(查看组件源码)
- props动态化:因为处于递归我们每次循环都可得到自定义的组件(组件可以多样化)因此使用三元表达式来处理不同组件
- selection-change:表格勾选方法,写在on里面取到的是勾选的值
- directives:自定义vue指令,这里添加了自定义的loading(elment ui 支持),控制起关闭即可
- scopedSlots:作用域插槽这里可展示动态组件,代码中展示了elTableColumn的单文件组件
- 再次执行that.deepChildrenComponent(item,h):递归子组件
- h('span',that.filteri18n(item.title))]:用于设置按钮的标题
- superParams:最高级组件可以获取其属性以及调用该组件的方法(getDataList())
- btnClick:按钮点击事件
Template 单文件组件,代码如下所示
export default {
functional: true,
name:'Template',
render(h,ctx){
const props = ctx.props;
const row = ctx.data.props.row;
const item = ctx.data.item;
const parent = ctx.parent;
return (
item.type == "index"
? h('span',parent.indexMethod(props.$index))
: item.value
? item.formatDate
? <span>{ parent.formatDate(parent.getObjKey(row,item.value)) }</span>
: <span>{ parent.getObjKey(row,item.value) }</span>
: <span>operation</span>
)
},
data(){
return {
}
}
}
代码解析:
- ctx:父组件传过来的数据
- index:表格的序号(进行+1处理)
- formatDate:是否支持时间格式转换
- getObjKey:父组件属性值获取方法
也不知道这样实现符不符合逻辑,但是用在开发上便利了许多,这个封装只是针对el-table组件的jsx二次封装,我的开源项目后面也会拓展更多的属性,更多的组件,希望能帮助到各位
自己写的一个小开源项目:基于自定义数据配置自定义/引入组件(目前已支持部分element ui组件)的管理后台