有时候会出现以下需求,类似这样
image.png
其中不确定有几个分组,可能是A,B两个, 也可能是 A, B, C,D四个,因为是后台配置出来的(且分组下面的展示都一样:如这里的 A1,A2,A3,A4)
在分组数量不确定的时候,如是后台配增或减了分组之后,前端也要相对应的在页面进行修改。而且每新增一个组,对应组下面的A1,A2,A3,A4都得重新写一次,还要保证表格内容是可编辑的。这个时候表格需要动态处理表头信息。如果用官方提供的写法,整个页面会变得超长,且大部分都是重复的,所以这里我使用JSX试了一下,发现效果还不错,下面是实现过程
因为是模拟的,所以一些demo中写死但可以异步加载数据的地方会备注
// 说明
// tableChild是分组的数组
// tableTitle是表格的表头(最终拼接tableChild后形成最终的表头结构)
data () {
return {
tableChild: [ // 这个是可以动态的后台获取
{
id: 1,
title: 'A'
}, {
id: 2,
title: 'B'
}, {
id: 3,
title: 'C'
}, {
id: 4,
title: 'D'
}, {
id: 5,
title: 'E'
}, {
id: 6,
title: 'F'
}
],
tableTitle: [
{
title: '序号',
align: 'center',
tooltip: true,
minWidth: 80,
fixed: 'left',
type: 'index'
}, {
fixed: 'right',
title: '操作',
align: 'center',
tooltip: true,
width: 220,
render: (h, params) => {
let vm = <div style="display:flex;flexDirection:row;justifyContent:space-around;">
<i-button type="primary" size="small"replace>查看</i-button>
<i-button type="error" size="small" style="marginLeft:3px;">删除</i-button>
</div>
return vm
}
}
],
tableData: []
}
}
那么我们要做的第一步就是要先初始化表头信息。从而保证数据的正确性
created () {
this.initTableTitle()
},
methods: {
/**
* 初始化表头
*/
initTableTitle () {
let insert = this.getTableChild()
let titles = this.tableTitle
let stash = Array.from(titles)
titles.forEach((item, i) => {
if (i === 0) {
let j = i
for (let x = 0; x < insert.length; x++) {
stash.splice(j + x + 1, 0, insert[x])
}
}
})
this.tableTitle = stash
},
/**
* 遍历获得数组对象
* 可能会出现有分组,但没有数据的情况
* 所以这里用随机数处理,模拟真实情况
* @returns {Array}
*/
getTableChild () {
// 相同类型下所有的表头的数组 如果这里也是动态请求的也可以的
let arr = [
{
title: 'A1',
align: 'center',
tooltip: true,
minWidth: 80,
render: (h, params) => {
let objName = this.getObjName(params.column.spec)
let o = params.row[objName]
if (o) {
return <div>{o.t1}</div>
} else {
return <div>暂无</div>
}
}
}, {
title: 'A2',
align: 'center',
minWidth: 80,
tooltip: true,
render: (h, params) => {
// console.log(params.column.spec)
let objName = this.getObjName(params.column.spec)
let o = params.row[objName]
if (o) {
return <i-input size="default" value={o.t2}
onOn-change={this.bindVal.bind(this, params, objName, 't2')}
onOn-blur={this.modifyItem.bind(this, params, o, 't2')}></i-input>
} else {
return <div>暂无</div>
}
}
}, {
title: 'A3',
align: 'center',
tooltip: true,
minWidth: 80,
render: (h, params) => {
let objName = this.getObjName(params.column.spec)
let o = params.row[objName]
if (o) {
return <i-input size="default" value={o.t3}
onOn-change={this.bindVal.bind(this, params, objName, 't3')}
onOn-blur={this.modifyItem.bind(this, params, o, 't3')}></i-input>
} else {
return <div>暂无</div>
}
}
}, {
title: 'A4',
align: 'center',
tooltip: true,
minWidth: 100,
render: (h, params) => {
let objName = this.getObjName(params.column.spec)
let o = params.row[objName]
if (o) {
return <i-input size="default" value={o.t4}
onOn-change={this.bindVal.bind(this, params, objName, 't4')}
onOn-blur={this.modifyItem.bind(this, params, o, 't5')}></i-input>
} else {
return <div>暂无</div>
}
}
}
]
let list = this.tableChild // 要拼接的原表头
let stash = [] // 拼接后的结果
for (let i = 0; i < list.length; i++) {
let item = list[i]
let title = item.title
let arr1 = this.copyData(arr) // 这里得每次重新创建一份新的数组,否则会出引用传递,导致每份数据最后变成一样
for (let j = 0; j < arr1.length; j++) {
arr1[j].spec = title
}
let obj = {
align: 'center',
tooltip: true,
id: item.id,
title: item.title,
children: arr1
}
stash.push(obj)
}
return stash
},
/**
* 复制一个对象(简单一级对象带函数)
* 主要让分组下的render函数在复制后还能使用! 如果有更好的方法或思路,麻烦留个言
* @param obj
* @returns {*}
*/
clone (obj) {
if (obj === null) return null
if (typeof obj !== 'object') return obj
if (obj.constructor === Date) return new Date(obj)
let newObj = new obj.constructor() // 保持继承链
for (let key in obj) {
if (obj.hasOwnProperty(key)) { // 不遍历其原型链上的属性
let val = obj[key]
/* eslint-disable */
newObj[key] = typeof val === 'object' ? arguments.callee(val) : val // 使用arguments.callee解除与函数名的耦合,这样每个复制出来的render函数都能继续使用
}
}
return newObj
},
/**
* 遍历数组进行复制
* @param arr
* @returns {Array}
*/
copyData (arr) {
let res = []
for (let i in arr) {
let item = arr[i]
let o = this.clone(item)
res.push(o)
}
return res
}
}
在表头处理完成后就可以去请求数据了,这里也模拟了一下,最终效果如下
image.png
以上都需要有vue环境支持jsx的写法,详见
我的这篇文章
最后附demo的git地址
如有更好的建议或疑问,欢迎留言