Easyexcel universal row cell merging strategy

ElementUI table cell merging needs to return the following data, which represents the number of rows and columns to be merged. Only the merging of rows will be discussed below.

      objectSpanMethod({ row, column, rowIndex, columnIndex }) {
        if (columnIndex === 0) {
          if (rowIndex % 2 === 0) {
            return {
              rowspan: 2,
              colspan: 1
            };
          } else {
            return {
              rowspan: 0,
              colspan: 0
            };
          }
        }
      }

The above code means that the first column is merged every two rows, and the timing of the merge is in the first row. That is, when the number of rows is 1, 3, 5... (subscripts are 0, 2, 4...), merge (the rowIndex of the first row of elementUI table is 0, regardless of the header row), the java code below is by

1,3,5.. Set the merge start row, and then merge in 2,4,6...(that is, merge in the last row of the cells to be merged). This is a cyclic merging, and most of the real business is not like this, so the key point is the judgment of the conditional statement.

As mentioned above, the java code passes 1,3,5..., here it no longer represents the cycle merge, but the first cell row number when merging cells. For example, if you want to merge rows 2, 3, and 4 in the first column, then you need 2. Combine rows 5, 6, 7, and 8 in the first column, so all you need is 5. So how do you know whether it is 2 or 5? In fact, as long as you add a boolean parameter to the second row of data, the value of true represents the starting row of the cell to be merged, and the non-starting row is set to false. In this way, the judgment row numbers 2, and 5 are converted into boolean type parameters for judging the corresponding row data. Then there must be this parameter in row, so that any row can be merged. (If different columns and rows are merged differently, different columns can correspond to different boolean parameters and the number of rows to be merged rowspan).

After setting the boolean type parameter and adding the rowspan value, you have actually specified which cell to start the row merge and how many rows to merge. The following is the java code implementation.

AbstractGenericMergeStrategy is an abstract general merge strategy, and GenericRowMergeStrategy is an implementation of row merge.

public abstract class AbstractGenericMergeStrategy<T extends GenericMerge> extends AbstractMergeStrategy {
    //所有数据 目的就是从所有数据找到当前行数据row
    protected List<T> list;
    //表头行数
    protected int headRowNumber = 1;

    protected AbstractGenericMergeStrategy(List<T> list){
        this.list = list;
    }
    protected AbstractGenericMergeStrategy(List<T> list, int headRowNumber){
        this.list = list;
        this.headRowNumber = headRowNumber;
    }

    @Override
    protected void merge(Sheet sheet, Cell cell, Head head, int relativeRowIndex) {
        int columnIndex = cell.getColumnIndex();
        int rowIndex = cell.getRowIndex();
        //找到cell对应的当前行数据row
        T t = list.get(rowIndex - headRowNumber);
        GenericMergeBO bo = t.merge(columnIndex);
        if (bo != null){
            merge(sheet,cell,bo);
        }
    }

    protected abstract void merge(Sheet sheet, Cell cell,GenericMergeBO bo);

    protected void executorMerge(CellPoint cellPoint, Sheet sheet){
            CellRangeAddress cellRangeAddress = new CellRangeAddress(
                    cellPoint.getStartY(),
                    cellPoint.getEndY(),
                    cellPoint.getStartX(),
                    cellPoint.getEndX());
            sheet.addMergedRegionUnsafe(cellRangeAddress);
    }

    String getCellText(Cell cell){
        String text = "";
        CellType cellType = cell.getCellTypeEnum();
        if(CellType.STRING.equals(cellType)){
            text = cell.getStringCellValue();
        }else if(CellType.NUMERIC.equals(cellType)){
            text = Double.toString(cell.getNumericCellValue());
        }else if(CellType.BOOLEAN.equals(cellType)){
            text = Boolean.toString(cell.getBooleanCellValue());
        }
        return text;
    }
}
public class GenericRowMergeStrategy<T extends GenericMerge> extends AbstractGenericMergeStrategy<T> {
    private Map<String, CellPoint> col = new HashMap<>();

    public GenericRowMergeStrategy(List<T> list) {
        super(list);
    }

    public GenericRowMergeStrategy(List<T> list, int headRowNumber) {
        super(list, headRowNumber);
    }

    @Override
    protected void merge(Sheet sheet, Cell cell, GenericMergeBO bo) {
        if (Boolean.TRUE.equals(bo.getStartMergeCell())) {
            CellPoint cellPoint = col.get(sheet.getSheetName() + "C" + cell.getColumnIndex());
            if (cellPoint == null) {
                cellPoint = new CellPoint();
                cellPoint.setStartX(cell.getColumnIndex());
                cellPoint.setStartY(cell.getRowIndex());
                cellPoint.setEndX(cell.getColumnIndex());
                cellPoint.setEndY(cell.getRowIndex() + bo.getRowspan() - 1);
                cellPoint.setText(getCellText(cell));
                col.put(sheet.getSheetName() + "C" + cell.getColumnIndex(), cellPoint);
            } else {
                if (cellPoint.getStartY() != cellPoint.getEndY()) {
                    executorMerge(cellPoint, sheet);
                }
                cellPoint.setStartX(cell.getColumnIndex());
                cellPoint.setStartY(cell.getRowIndex());
                cellPoint.setEndX(cell.getColumnIndex());
                cellPoint.setEndY(cell.getRowIndex() + bo.getRowspan() - 1);
            }
        }
    }
}
public interface GenericMerge {
    GenericMergeBO merge(int columnIndex);
}
public class CellPoint {
    /**
     * 开始单元格x坐标
     */
    private int startX;
    /**
     * 结束单元格x坐标
     */
    private int endX;
    /**
     * 开始单元格y坐标
     */
    private int startY;
    /**
     * 结束单元格y坐标
     */
    private int endY;
    /**
     * 单元格内容,文本
     */
    private String text;
}
@Data
public class GenericMergeBO {
    /**
     * 是否是开始合并的单元格
     */
    private Boolean startMergeCell;
    /**
     * 合并的行数
     */
    private Integer rowspan;
    /**
     * 合并的列数
     */
    private Integer colspan;
}

 

use

When easyexcel is exported, pass in the GenericRowMergeStrategy strategy class. The exported data class implements the GenericMerge interface. The method body is to translate the elementUI method of merging cells into java code. The return parameter is one more startMergeCell whether it is the cell to start merging, for the sake of judgment. It does not need to be merged to return null.

Disadvantage

When different column merge strategies are different, it is more troublesome to construct data. Because it is necessary to correspond to different boolean parameters (whether it is the cell startMergeCell to start merging) and the number of merged rows (rowspan) for different strategies.

Comparative analysis of js/java methods

method

js: objectSpanMethod({ row, column, rowIndex, columnIndex }) {}

java: GenericMergeBO merge(int columnIndex);

Compared

js row-> java this (current object)

js columnIndex->java method parameter columnIndex

Because it is only row merging, column and rowIndex are not used. If other restrictions are required, the parameters of the GenericMerge interface method can be enriched

principle

For a column, the first true is the starting cell that needs to be merged, the data before the second true is the data to be merged, and so on, to construct how to merge the data in this column.

Example

As shown in the figure above, 0,1,2,8 is a merge strategy 3,4 is another merge strategy 5,6,7 are columns that do not need to be merged

The code to construct the data is as follows:

      List<User> list = new ArrayList<>();
        //根据用户姓名分组
        Map<String, List<User>> nameMap = list.stream().collect(Collectors.groupingBy(User::getName));
        nameMap.forEach((k,v)->{
            int index = v.size();
            for (User dto : v) {
                //给0,1,2,8列设置boolean参数和需要合并的行数
                dto.setIsRowSpan(index == v.size());
                dto.setRowIndexNumber(v.size());
                index--;
            }
            //每一个用户下又根据项目编号分组
            Map<String, List<User>> projectMap = v.stream().collect(Collectors.groupingBy(Project::getProjectNumber));
            projectMap.forEach((k1,v1)->{
                int j = v1.size();
                for (User dto : v1) {
                    //给3,4列设置boolean参数和需要合并的行数
                    dto.setIsProjectRowSpan(j == v1.size());
                    dto.setProjectRowIndexNumber(v1.size());
                    j--;
                }
            });
        });

The User class implements the GenericMerge interface code as follows:

    @Override
    public GenericMergeBO merge(int columnIndex) {
        GenericMergeBO bo = null;
        if (columnIndex == 0 || columnIndex == 1 || columnIndex == 2 || columnIndex == 3 || columnIndex == 4 || columnIndex == 8) {
            if (columnIndex == 0 || columnIndex == 1 || columnIndex == 2 || columnIndex == 8) {
                bo = new GenericMergeBO();
                bo.setStartMergeCell(this.isRowSpan);
                bo.setRowspan(this.rowIndexNumber);
                bo.setColspan(1);
                return bo;
            } else if(columnIndex == 3 || columnIndex == 4){
                bo = new GenericMergeBO();
                bo.setStartMergeCell(this.isProjectRowSpan);
                bo.setRowspan(this.projectRowIndexNumber);
                bo.setColspan(1);
                return bo; 
            }
        }
        return bo;
    }

The js code is as follows:

    objectSpanMethod({ row, column, rowIndex, columnIndex }) {
      if (columnIndex === 0 || columnIndex === 1 || columnIndex === 2 || columnIndex === 3 || columnIndex === 4 || columnIndex === 8) {
        if ((columnIndex === 0 || columnIndex === 1 || columnIndex === 2 || columnIndex === 8) && row.isRowSpan) {
          return {
            rowspan: row.rowIndexNumber,
            colspan: 1
          };
        } else if ((columnIndex === 3 || columnIndex === 4) && row.isProjectRowSpan) {
          return {
            rowspan: row.projectRowIndexNumber,
            colspan: 1
          };
        } else {
          return {
            rowspan: 0,
            colspan: 0
          };
        }
      }
    }

 

Note: Part of the content uses the code in https://github.com/alibaba/easyexcel/pull/1091 to add horizontal and vertical merging of cells according to the same cell content (use of CellPoint, col map)

 

Guess you like

Origin blog.csdn.net/sinat_33472737/article/details/106339424