详解和实现数据表格中的行数据合并功能

192 阅读3分钟

前言

需求场景:

在提供了数据查看和修改的表格视图中(如table、a-table等...),允许用户自行选择多行数据,依据当前状态进行特定列数据的合并操作。选中的数据将统一显示为选中组的首条数据值。同时,页面会即时反馈显示合并后的效果,提供直观的操作反馈。

效果

image.png

方案选型

依赖库:vxe-table(:merge-cells="mergeCells"用作效果展示)

核心逻辑

根据数据行中的mergeId是否相等判断是否有过合并操作,前端执行合并操作后需将合并的数据的mergeId设置为相同的数值,并将选中数据中需要合并的数据项置为第一行选中的数据,并在页面展示合并后的效果。

保证两个一致:

  • 数据一致性(数据覆盖) 优先级:极高
  • 展示一致性(根据mergeId调整展示) 优先级:高

数据源举例

描述: 常规的后端返回结构,数组对象,数组中每一项指代每一条数据。

列表数据示例:

data:[
{mergeId:1,...},
{mergeId:2,...},
{mergeId:3,...}
]

合并逻辑:

- 数据一致性

coverParams:需要覆盖的参数名,存在于列表数据中

firstData:第一条数据

selectRows:选中的数据集合

 coverParams.forEach(item => {
      for (let i = 1; i < number; i++) {
        // 选中的数据都覆盖第一条数据的值
        selectRows[i][item] = firstData[item];
        // }
      }
    });

- 展示一致性

newCurrentRows:由于选择时的随机性和数据结构的不稳定性,记录点击的位置点,并将位置点排序记录。

// 生成正序数组newCurrentRows
    const newCurrentRows = currentRows.sort((a, b) => a - b);
// 若newCurrentRows数据不连续 则将选中数据都置为队尾
    const isEqual = newCurrentRows.every((value, index, array) => {
      if (index === 0) {
        return true;
      } else {
        return value === array[index - 1] + 1;
      }
    })

情况1:选中的数据连续

coverCols:需要合并的行号组,字段的展示位置,用作生成mergeCells

firstRow:第一行位置

number:对应rowspan,得到跨几行的数据

// 获取选中了几条数据
    const number = state.selectRows.length;
  // 取第一个行号
    const firstRow = newCurrentRows[0];
if (isEqual) {
const mergeCellsArr = [];
      coverCols.forEach(item => {
        mergeCellsArr.push({
          row: firstRow,
          col: item,
          rowspan: number,
          colspan: 1
        });
      });
      mergeCells.value = mergeCellsArr;
}

情况2:选中的数据不连续

核心处理:将不连续的数据处理取出放置队尾,满足连续条件继续操作。

      // 非选中的数据
      const fristElements = [];
      // 选中的数据集合
      const secondElements = [];
      // // 选中的数据置于队尾
      // // 遍历原始数组
      const dataNumber = dataSource.value.length;
      for (let i = 0; i < dataNumber; i++) {
        // 如果i在 newCurrentRows中不存在,则将其置于fristElements前列
        if (!newCurrentRows.includes(i)) {
          fristElements.push(dataSource.value[i]);
        }
        // // 如果i在 newCurrentRows中存在,则将其置于fristElements后列
        if (newCurrentRows.includes(i)) {
          secondElements.push(dataSource.value[i]);
        }
      }
      const newSecondElements = secondElements.map(item => {
        return {
          ...item
        };
      });
      // 将新数组的元素追加到原始数组的末尾
      dataSource.value = fristElements.concat(newSecondElements);

存在合并标识的数据处理

主要用作数据中存在mergeId标记如何展示合并效果。

// 初始数据根据mergeId组装合并效果
  const baseDataMerge = () => {
    // coverCols 需要合并的列号  注塑全新
    const baseCoverCols = getCoverCols();
    // length 需要合并的行数
    const mergedCells = [];
    dataSource.value.forEach((row, rowIndex) => {
      //检查当前行的mergeId是否和下一行的mergeId一样  且mergeId存在
      if (
        rowIndex < dataSource.value.length - 1 &&
        row.mergeId === dataSource.value[rowIndex + 1].mergeId &&
        row.mergeId !== undefined
      ) {
        // 仅在第一次符合条件时 判断横跨几行  默认跨两行
        if (
          dataSource.value[rowIndex + 3] &&
          row.mergeId === dataSource.value[rowIndex + 3].mergeId
        ) {
          // 横跨3行
          state.mergeLength = 4;
        } else if (
          dataSource.value[rowIndex + 2] &&
          row.mergeId === dataSource.value[rowIndex + 2].mergeId
        ) {
          // 横跨4行
          state.mergeLength = 3;
        } else {
          // 横跨2行
          state.mergeLength = 2;
        }
        baseCoverCols.forEach(item => {
          mergedCells.push({
            row: rowIndex, // 开始行,由符合条件的
            rowspan: state.mergeLength, // 合并行数,由选中的数据条数决定
            col: item,
            colspan: 1
          });
        });
        state.mergeFlag = true;
      }
    });
    console.log('初始数据合并效果', mergedCells);
    return mergedCells;
  };