Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【特性讨论】数据写入能否按需设置横向 #307

Open
2259289435 opened this issue Nov 14, 2022 · 6 comments
Open

【特性讨论】数据写入能否按需设置横向 #307

2259289435 opened this issue Nov 14, 2022 · 6 comments
Labels

Comments

@2259289435
Copy link
Contributor

场景:使用 excel 打印,列头少情况下,经常采用数据合并,目的:一节省纸张,二数据量大

举例:
现在 excel 生成的格式为:
行一:跨列标题
行二:列1 列2
行三:数据1 数据1
行四:数据2 数据2

希望的格式为:两条数据合成一行
行一:跨列标题
行二:列1 列2 列1 列2
行三:数据1 数据1 数据2 数据2
行四:数据3 数据3 数据4 数据4
行五:数据5 数据5

现在通过编码去手动合并两行为一个对象显示可以实现,但底层还是数据与行 1 : 1,能否支持数据与行 N :1 ?

@wangguanquan
Copy link
Owner

wangguanquan commented Nov 14, 2022 via email

@wangguanquan
Copy link
Owner

wangguanquan commented Nov 14, 2022 via email

@2259289435
Copy link
Contributor Author

数据:
1 | 南瓜 | 11
2 | 冬瓜 | 12
3 | 西瓜 | 10
4 | 北瓜 | 12
...

最终效果
image

@2259289435
Copy link
Contributor Author

数据与行 N : 1,比如上图就是数据与行 3 : 1,好处就是作业人员使用的时候可以一张纸看到尽可能多的数据,并且节省纸张。

现在确实可以通用listmap业务自行二次封装处理,但这种场景是比较通用的一种业务需求,所以想了解能否把这种数据与行自定义对应的能力封装到框架内。

@wangguanquan
Copy link
Owner

感谢反馈,已了解使用场景,考虑添加扩展属性或注解以支持新特性

@wangguanquan
Copy link
Owner

临时的你可以使用如下自定义WorksheetWriter实现该功能,原理很简单首先是表头需要特殊处理,将原来的表头复制n次,其次写行数据的时候判断是否需要写行头和行尾。需要注意写出时取消原有的斑马线,如果要实现斑马线需要使用setStyleProcessor()方法添加一个样式处理器(取模判断是否在一行上即可)

  1. 自定义平铺WorksheetWriter
  2. 导出时使用该writer即可
/**
 * 自定义平铺WorksheetWriter
 */
public static class TileXMLWorksheetWriter extends XMLWorksheetWriter {
    private int tile; // 平铺的数量,也就是每行重复输出多少条数据
    private String date; // 可忽略,仅仅是表头上的日期
    public TileXMLWorksheetWriter(int tile) {
        this.tile = tile;
    } 
    
    public TileXMLWorksheetWriter(int tile, String date) {
        this.tile = tile;
        this.date = date;
    }

    public int getTile() {
        return tile;
    }

    public void setTile(int tile) {
        this.tile = tile;
    }

    public String getDate() {
        return date;
    }

    public void setDate(String date) {
        this.date = date;
    }

    @Override
    protected void writeBefore() throws IOException {
        // The header columns
        columns = sheet.getAndSortHeaderColumns();
        // Give new columns
        tileColumns();

        boolean nonHeader = sheet.getNonHeader() == 1;

        bw.write(Const.EXCEL_XML_DECLARATION);
        // Declaration
        bw.newLine();
        // Root node
        writeRootNode();

        // Dimension
        writeDimension();

        // SheetViews default value
        writeSheetViews();

        // Default row height and width
        int fillSpace = 6;
        BigDecimal width = BigDecimal.valueOf(!nonHeader ? sheet.getDefaultWidth() : 8.38D);
        String defaultWidth = width.setScale(2, BigDecimal.ROUND_HALF_UP).toString();
        writeSheetFormat(fillSpace, defaultWidth);

        // cols
        writeCols(fillSpace, defaultWidth);

        // Write body data
        beforeSheetDate(nonHeader);
    }

    protected void tileColumns() {
        if (tile == 1) return;

        int x = columns.length, y = x * tile, t = columns[columns.length - 1].getRealColIndex();
        // Bound check
        if (y > Const.Limit.MAX_COLUMNS_ON_SHEET)
            throw new TooManyColumnsException(y, Const.Limit.MAX_COLUMNS_ON_SHEET);

        Column[] _columns = new Column[y];
        for (int i = 0; i < y; i++) {
            // 第一个对象的表头不需要复制
            Column col = i < x ? columns[i] : new Column(columns[i % x]).addSubColumn(new Column());
            col.realColIndex = columns[i % x].realColIndex + t * (i / x);
            _columns[i] = col;

            // 替换拣货单上的日期
            Column _col = col;
            do {
                if (StringUtil.isNotEmpty(_col.getName()) && _col.getName().contains("{date}"))
                    _col.setName(_col.getName().replace("{date}", date));
            }
            while ((_col = _col.next) != null);
        }

        this.columns = _columns;

        // FIXME 这里强行指定合并替换掉原本的头
        List<Dimension> mergeCells = Collections.singletonList(new Dimension(1, (short) 1, 1, (short) y));
        sheet.putExtProp(Const.ExtendPropertyKey.MERGE_CELLS, mergeCells);
    }

    @Override
    protected void writeRow(Row row) throws IOException {
        Cell[] cells = row.getCells();
        int len = cells.length, r = row.getIndex() / tile + startRow, c = columns[columns.length - 1].realColIndex / tile, y = row.getIndex() % tile;
        if (y == 0) startRow(r - startRow, columns[columns.length - 1].realColIndex);

        for (int i = 0; i < len; i++) {
            Cell cell = cells[i];
            int xf = cell.xf, col = i + c * y;
            switch (cell.t) {
                case INLINESTR:
                case SST:
                    writeString(cell.sv, r, col, xf);
                    break;
                case NUMERIC:
                    writeNumeric(cell.nv, r, col, xf);
                    break;
                case LONG:
                    writeNumeric(cell.lv, r, col, xf);
                    break;
                case DATE:
                case DATETIME:
                case DOUBLE:
                case TIME:
                    writeDouble(cell.dv, r, col, xf);
                    break;
                case BOOL:
                    writeBool(cell.bv, r, col, xf);
                    break;
                case DECIMAL:
                    writeDecimal(cell.mv, r, col, xf);
                    break;
                case CHARACTER:
                    writeChar(cell.cv, r, col, xf);
                    break;
                case BLANK:
                    writeNull(r, col, xf);
                    break;
                default:
            }
        }
        // 注意这里可能不会关闭row需要在writeAfter进行二次处理
        if (y == tile - 1)
            bw.write("</row>");
    }

    @Override
    protected void writeRowAutoSize(Row row) throws IOException {
        Cell[] cells = row.getCells();
        int len = cells.length, r = row.getIndex() / tile + startRow, c = columns[columns.length - 1].realColIndex / tile, y = row.getIndex() % tile;
        if (y == 0) startRow(r - startRow, columns[columns.length - 1].realColIndex);

        for (int i = 0; i < len; i++) {
            Cell cell = cells[i];
            int xf = cell.xf, col = i + c * y;
            switch (cell.t) {
                case INLINESTR:
                case SST:
                    writeStringAutoSize(cell.sv, r, col, xf);
                    break;
                case NUMERIC:
                    writeNumericAutoSize(cell.nv, r, col, xf);
                    break;
                case LONG:
                    writeNumericAutoSize(cell.lv, r, col, xf);
                    break;
                case DATE:
                case DATETIME:
                case DOUBLE:
                case TIME:
                    writeDoubleAutoSize(cell.dv, r, col, xf);
                    break;
                case BOOL:
                    writeBool(cell.bv, r, col, xf);
                    break;
                case DECIMAL:
                    writeDecimalAutoSize(cell.mv, r, col, xf);
                    break;
                case CHARACTER:
                    writeChar(cell.cv, r, col, xf);
                    break;
                case BLANK:
                    writeNull(r, col, xf);
                    break;
                default:
            }
        }
        // 注意这里可能不会关闭row需要在writeAfter进行二次处理
        if (y == tile - 1)
            bw.write("</row>");
    }

    @Override
    protected void writeAfter(int total) throws IOException {
        if (total > 0 && (total - 1) % tile < tile - 1) bw.write("</row>");
        super.writeAfter(total);
    }
}
  • 测试代码
@Test public void tileWriter() throws IOException {
    List<TileEntity> data = TileEntity.randomTestData();
    new Workbook().cancelOddFill().addSheet(new ListSheet<>(data).setSheetWriter(new TileXMLWorksheetWriter(3, LocalDate.now().toString()))).writeTo(defaultTestPath);
}
  • 测试对象
public static class TileEntity {
    @ExcelColumn("{date} 拣货单")
    @ExcelColumn(value = "差异", maxWidth = 8.6D)
    private String diff;
    @ExcelColumn("{date} 拣货单")
    @ExcelColumn(value = "序号", maxWidth = 6.8D)
    private Integer no;
    @ExcelColumn("{date} 拣货单")
    @ExcelColumn(value = "商品", maxWidth = 12.0D)
    private String product;
    @ExcelColumn("{date} 拣货单")
    @ExcelColumn(value = "数量", maxWidth = 6.8D)
    private Integer num;

    public static List<TileEntity> randomTestData() {
        int n = random.nextInt(30) + 10;
        List<TileEntity> list = new ArrayList<>(n);
        for (int i = 0; i < n; i++) {
            TileEntity e = new TileEntity();
            e.no = i + 1;
            e.product = getRandomString(10);
            e.num = random.nextInt(20) + 1;
            list.add(e);
        }
        return list;
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants