使⽤el-table实现单元格合并
需要把相同内容的相邻单元格合并。起初,我知道el-table有span-method这样⼀个属性,让我们告诉它怎么合并,但是由于我并没有花太多的时间在这上⾯,所以就认为这个东西不能实现我的需求,毕竟⽂档给的例⼦太过简单,我的举⼀反三能⼒有差,就这样完美的错过了。
我就发挥了⾃⼰的想象⼒。打算在表格渲染完成后,⽤原⽣js来直接操作el-table⽣成的table。并且我也实现了这个思路,代码如下:
function mergeCells(tbody) {
const trs = tbody.querySelectorAll('tr');
// 记录原始总⾏
const trTotal = trs.length;
// 逐⾏合并同⾏⾥的内容相同的单元格
for (let i = 0; i < trTotal; i++) {
let lastTd;
let lastTdText;
let colSpan;
const tds = trs[i].querySelectorAll('td');
for (let j = 0; j < tds.length; j++) {
const currentTd = tds[j];
const currentTdText = currentTd.innerText;
// 如果相同,colSpan 加1
if (lastTd && lastTdText === currentTdText) {
colSpan++;
if (j === tds.length - 1) { // 最后⼀列了
mergeColumns(lastTd, colSpan);
}
} else {
if (lastTd && colSpan > 1) {
mergeColumns(lastTd, colSpan);
}
lastTd = currentTd;
lastTdText = currentTdText;
colSpan = 1;
}
}
}
// 逐列合并相同内容的单元格
for (let i = 0; i < trTotal; i++) {
const tds = trs[i].querySelectorAll('td');
let lastTd;
for (let j = 0; j < tds.length; j++) {
tds[j].start = lastTd && (lastTd.start + lSpan) || 0;
lastTd = tds[j];
}
}
for (let i = 0; i < trTotal; i++) {
let rowSpan;
const tds  = trs[i].querySelectorAll('td');
for (let j = 0; j < tds.length; j++) {
const currentTd = tds[j];
const currentTdText = tds[j].innerText;
rowSpan = 1;
let nextTd = currentTd;
while ((nextTd  = nextRowTdOfCell(nextTd)) && nextTd.innerText === currentTdText) {
rowSpan++;
}
if (rowSpan > 1) {
mergeRows(currentTd, rowSpan);
}
}
}
}
}
/**
寻td的下⼀⾏中相同位置的td
何为相同位置?start和colSpan相同即是
**/
function nextRowTdOfCell(td) {
const nextTr = ElementSibling;
if (!nextTr) {
return;
}
const start = td.start;
const colSpan = td.colSpan;
const tds = nextTr.querySelectorAll('td');
let item;
for (let i = 0; i < tds.length; i++) {
if (tds[i].start === start && tds[i].colSpan === colSpan) {
return tds[i];
}
}
}
/**
合并不同⾏上的列
@param {Element} startId 开始合并的td
@param {Number} rowSpan 要合并的⾏数
**/
function mergeRows(startTd, rowSpan) {
let nextTd = nextRowTdOfCell(startId);
for (let i = 0; i < rowSpan; i++) {
const tempTd = nextRowTdOfCell(nextTd);
nextTd = tempTd;
}
}
/**
合并同⾏上的列
**/
function mergeColumns(startId, colSpan) {
let nextTd = EelementSibling;
for (let i = 0; i < colSpan; i++) {
const tempTd = ElementSibling;
nextTd = tempTd;
}
}
以上就是我的第⼀个⽅案,很快我就发现它跟el-table不能很好的协作。如果我点击了排序或是分页,结构就会乱掉,具体原因是什么我没兴趣去研究了。我只能另⽅案了。这次我觉得我应该看⼀下span-method。终于,我搞懂了span-method的⽤法,它接收⼀个对象,⾥⾯包含row column rowIndex columnIndex。对于每⼀个单元格都会调⽤这个函数。以它返回的值来确定合并的策略。它可以返回⼀个有两个元素的数组,抑或是⼀个对象,反正都是⽤来表⽰当前单元格的⾏跨度和列跨度,即rowSpan和colSpan.
问题明朗了,我只要能判断出来哪个单元格需要合并单元格,并且把合并的策略返回就好了。还要说明⼀下返回值的具体意思:
[1, 1] 表⽰不合并,如果没有返回任何值,默认值就是这个。
[0, 0] 当前单元格不会显⽰。
为了能判断出来某个列的合并策略,需要对原始数据进⾏处理。我的原始数据是常见的格式,⼀个对象的数组,如下[{
name: ‘李⽩’,
age: ‘10’,
}]
简单说⼀下我的思路,这⾥借鉴了我上⾯操作tbody的思路。
1. 先把对象数组转换成⼀个对象⼆维数组,如下
[
[{value: '李⽩'}, {value: '10'}...]
...
]
这⾥的顺序⼀定要跟el-table-column的prop顺序⼀致。
2. 然后先合并⾏,再合并列
function generateMergeCellInfo(data) {
// 1. 处理数据的代码我就省略了
// 收集每⾏中的相同数据的信息
for (let i = 0; i < data.length; i++) {
const row = data[i];
let colSpan = 1;
let lastText;
let lastItem;
for (let j = 0; j < row.length; j++) {
const currentText = row[j].value;
if (currentText === lastText) {
colSpan++;
row[j] = null;
} else {
if (colSpan > 1) {
}
lastItem = row[j];
lastText = currentText;
}
}
}
// 给每个数据计算colStart
for (let i = 0; i < data.length; i++) { // 先去除为空的对象
data[i] = data[i].filter(Boolean);
}
for (let i = 0; i < data.length; i++) {
const columns = data[i];
let lastItem;
for (let j = 0; j < columns.length; j++) {
columns[j].colorStart = lastItem && (lStart + (lSpan || 1)) || 0;
columns[j].colSpan = columns[j].colSpan || 1;
lastItem = columns[j];
}
}
// 获取下⼀⾏中跟当前单元格相同的单元格信息
function nextRowTdOfCell(column, nextRow) {
return nextRow && nextRow.find(c => c && c.colStart === lStart && c.colSpan === lSpan);
}
// 跨⾏计算
for (let i = 0; i < data.length; i++) {
let rowSpan;
const columns = data[i];
const columns = data[i];
for (let j = 0; j < columns.length; j++) {
const currentColumn = columns[j];
if (!currentColumn) {
continue;
}
const currentText = currentColumn.value;
rowSpan = 1;
let nextColumn;
let nextRowIndex = i + 1;
while ((nextColumn = nextRowTdOfCell(currentColumn, data[nextRowIndex])) && nextColumn.value === currentText) {
const index = data[nextRowIndex].indexOf(nextColumn);
data[nextRowIndex][index] = null;
nextRowIndex++;
rowSpan++;
}
if (rowSpan > 1) {
} else {
}
}
}
for (let i = 0; i < data.length; i++) {
data[i] = data[i].filter(Boolean);
}
const ret = [];
for (let i = 0; i < data.length; i++) {
if (data[i] && data[i].length) {
ret.push(...data[i]);
}
}
return ret;
}
// 接下来就是span-method
spanMethod({row, column, rowIndex, columnIndex}) {
// 这⾥假设已经获取了mergeCellInfo
if (!mergeCellInfo) return;
const info = mergeCellInfo.find(item => {
return rowIndex >= wStart && rowIndex < (wStart + wSpan) && columnIndex >= lSpan && columnIndex < (lStart + item. colSpan);
});
if (info) {
js合并两个数组if (lStart === columnIndex && wStart === rowIndex) {
return [wSpan, lSpan];
} else {
return [0, 0];
}
} else {
return [1,1];
}
}
到此代码就敲完了,好累呀,基本可⽤,边界条件应该也考虑了。
如果对你有帮助,请帮我点赞呀,嘻嘻:)

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。