在日常开发过程中,难免会用到表格。虽然现在很多框架自带表格的合并功能,但是有时候如果指示想开发一个小功能而且引入一个ui库,有点杀鸡用牛刀的感觉。其实,用自定义指令和函数,也可以批量合并单元格。
场景
我们用vue生成表格时,一般是数据为数组,然后用v-for变量。
// 数据
data(){
return {
tableData: [
{name:'张三',school: '混沌小学',degree:'小学'},
{name:'张三',school: '混沌高中',degree:'高中'},
{name:'张三',school: '混沌大学',degree:'本科'},
{name:'李四',school: '秩序小学',degree:'小学'},
{name:'李四',school: '秩序小学',degree:'高中'},
{name:'李四',school: '秩序小学',degree:'本科'}
]
}
},
// template
<template>
<div id="app">
<table class="table">
<tr>
<td class="bg-light">姓名</td>
<td class="bg-light">学校</td>
<td class="bg-light">学历</td>
</tr>
<tr v-for="(el,index) in tableData" :key="el+index">
<td >{{el.name}}</td>
<td>{{el.school}}</td>
<td>{{el.degree}}</td>
</tr>
</table>
</div>
</template>
效果如下:
这时,测试就希望姓名这里能合并起来
达成这种效果有两个点:
- name所有相同的第一项开始的位置,确定开始跨行,以及连续出现的次数,确定跨行的数值。
- tb标签是变量出来的,开始跨行后,将后续多余的删掉。
解决这两个就能实现跨行了。
先解决第一个问题,统计name有多少不同的值,不同的值起始位置,出现的次数。
// 这么我们先分两部
// 1.将tableData中我们所需要的name先拿出来,可以用es6中的map优雅的实现出来
const getKeys = (arr,key)=>arr.map((item,index)=>({key:item[key],index:index}));
// 2.统计出现的不同的值种类,每个不同种类的值、个数以及起始位置。这个可以用es6的reduce实现。
const rowSpanCount =arr=> arr.reduce((cur,prev)=>{
const {key,index} = prev;
Reflect.has(cur,key)?cur[key].num++:Reflect.set(cur,key,{start:index,num:1});
return cur;
},{})
// 结果
rowSpanCount(getKeys(this.tableData,'name'))
/*{
"张三": {
"start": 0,
"num": 3
},
"李四": {
"start": 3,
"num": 3
}
}*/
然后在computed中调用
computed: {
rowCount() {
const getKeys = (arr,key)=>arr.map((item,index)=>({key:item[key],index:index}));
const rowSpanCount =arr=> arr.reduce((cur,prev)=>{
const {key,index} = prev;
Reflect.has(cur,key)?cur[key].num++:Reflect.set(cur,key,{start:index,num:1});
return cur;
},{})
return rowSpanCount(getKeys(this.tableData,'name'))
}
},
解决第一个后,后面就是为td设置属性,并且删除多余的td了。方式有很多,这里推荐用自定义指令的方式,能减少不必要的代码。
// 定义一个方法,用于判断是不是开始跨行的项,
methods: {
setRowSpan(key, activeIndex) {
const value = this.rowCount[key];
const { num, start } = value;
return activeIndex === start? num : 0;//如果第一次出现的项,为跨行相,返回需要跨行的数值,如果不是第一次出现,则为需要删除的多余相,返回值0;
},
}
// 自定义指令,用途设置rowSpan和删除多余的td
directives:{
rowSpan:{
inserted(el, binding) {
const { value } = binding;
value ? el.setAttribute('rowSpan', value) : el.parentNode.removeChild(el);
}
}
},