package com.xujie.sys.common.utils; import lombok.Setter; import org.apache.poi.ooxml.POIXMLDocumentPart; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.*; import java.io.IOException; import java.io.InputStream; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; public class ExcelTemplate { final private static Pattern PATTERN_HDR_FIELD = Pattern.compile("\\$\\{(.*?)}", Pattern.MULTILINE); final private static Pattern PATTERN_DTL_FIELD = Pattern.compile("#\\{(.*?)}", Pattern.MULTILINE); private XSSFWorkbook workbook; private Map variables = new HashMap<>(); private List> listVariables = new ArrayList<>(); // 是否下移形状格式 @Setter private boolean moveShape = false; // 是否下移印章(印章是图片) @Setter private boolean moveSeal = false; // 是否设置单元格样式 @Setter private boolean cellStyle = false; // 是否设置单元格样式 针对RFID不需要序号定制 @Setter private boolean cellStyle2 = false; // 是否设置合并单元格样式 @Setter private boolean rangeStyle = false; // 价格靠右 发票 @Setter private boolean priceRight = false; // 数字靠右 箱单 @Setter private boolean intRight = false; // 报关单 @Setter private boolean delRight = false; private ExcelTemplate(){} public static ExcelTemplate load(InputStream template) throws IOException { ExcelTemplate excelTemplate = new ExcelTemplate(); excelTemplate.workbook = new XSSFWorkbook(template); return excelTemplate; } public void addVar(String key, Object value){ variables.put(key, value); } public void addVarAll(Map values){ variables.putAll(values); } public void addListVar(Map row){ listVariables.add(row); } public void addListVarAll(Collection rows){ listVariables.addAll(rows); } public void clearAll(){ variables.clear(); listVariables.clear(); moveShape = false; moveSeal = false; cellStyle = false; rangeStyle = false; priceRight = false; } private boolean findAndRemoveMergedRegion(XSSFSheet sheet, int rowIndex) { for (int mi = 0; mi < sheet.getMergedRegions().size(); mi++) { if (sheet.getMergedRegions().get(mi).getFirstRow() == rowIndex) { sheet.removeMergedRegion(mi); return true; } } return false; } public XSSFWorkbook render(int index) throws IOException { XSSFSheet sheet = workbook.getSheetAt(index); CellCopyPolicy copyPolicy = new CellCopyPolicy(); int dtlRowIndex = -1; // find detail rows for (int i = 0; i < sheet.getLastRowNum(); i++) { XSSFRow row = sheet.getRow(i); if (row == null) { continue; } for (int j = 0; j < row.getLastCellNum(); j++) { XSSFCell c = sheet.getRow(i).getCell(j); if (c == null) { continue; } String cellValue = c.toString(); Matcher matcherDtl = PATTERN_DTL_FIELD.matcher(cellValue); if (matcherDtl.find()) { dtlRowIndex = i; break; } } if (dtlRowIndex >= 0) { break; } } if (dtlRowIndex < sheet.getLastRowNum() && listVariables.size() > 1) { //循环列表以下区域整体下移 int _rows = sheet.getLastRowNum(); for(int i = _rows; i > dtlRowIndex; i--){ sheet.copyRows(i, i, i + listVariables.size() -1 , copyPolicy); } for(int i = 0; i < listVariables.size() - 1; i++) { int destRowIndex = dtlRowIndex + 1 + i; sheet.createRow(destRowIndex); //clear destination row while(findAndRemoveMergedRegion(sheet, destRowIndex)){ continue; } sheet.copyRows(dtlRowIndex, dtlRowIndex, destRowIndex, copyPolicy); } } // 遍历所有形状 将列表下方的形状坐标也下移 if (moveShape) { List relations = sheet.getRelations(); for (POIXMLDocumentPart part : relations) { if (part instanceof XSSFDrawing) { XSSFDrawing drawing = (XSSFDrawing) part; List shapes = drawing.getShapes(); for(int i = 0; i < shapes.size(); i++) { if ((i==3 || i==5) && shapes.get(i) instanceof XSSFSimpleShape) { XSSFSimpleShape simpleShape = (XSSFSimpleShape) shapes.get(i); ClientAnchor anchor = (ClientAnchor) simpleShape.getAnchor(); // 调整行坐标实现下移 anchor.setRow1(anchor.getRow1() + listVariables.size() - 1); anchor.setRow2(anchor.getRow2() + listVariables.size() - 1); } if ((i==4 || i==6) && shapes.get(i) instanceof XSSFSimpleShape) { XSSFSimpleShape simpleShape = (XSSFSimpleShape) shapes.get(i); ClientAnchor anchor = (ClientAnchor) simpleShape.getAnchor(); // 调整行坐标实现下移 因为只占一行 所以不需要调整row2 anchor.setRow1(anchor.getRow1() + listVariables.size() - 1); } } } } } // 遍历所有图片 将列表下方的形状坐标也下移 if (moveSeal) { List relations = sheet.getRelations(); for (POIXMLDocumentPart part : relations) { if (part instanceof XSSFDrawing) { XSSFDrawing drawing = (XSSFDrawing) part; List shapes = drawing.getShapes(); for (XSSFShape shape : shapes) { // 只处理图片类型的形状 if (shape instanceof XSSFPicture) { XSSFPicture picture = (XSSFPicture) shape; // 调整行坐标实现下移 ClientAnchor anchor = (ClientAnchor) picture.getAnchor(); if (anchor.getRow1() > 10) { // 只下移位于10行之后的图片 anchor.setRow1(anchor.getRow1() + listVariables.size() - 1); anchor.setRow2(anchor.getRow2() + listVariables.size() - 1); } } } } } } List dtlRows = new ArrayList<>(); //整体填值 for (int i = 0; i <= sheet.getLastRowNum(); i++) { XSSFRow row = sheet.getRow(i); if (row == null) { continue; } for (int j = 0; j < row.getLastCellNum(); j++) { XSSFCell c = sheet.getRow(i).getCell(j); if (c == null) { continue; } String cellValue = c.toString(); if ("#{pn}".equals(cellValue)) { dtlRows.add(i); } if ("#{levy}".equals(cellValue)) { dtlRows.add(i); } Matcher matcherHdr = PATTERN_HDR_FIELD.matcher(cellValue); String result = c.getStringCellValue(); while (matcherHdr.find()) { for (int ri = 1; ri <= matcherHdr.groupCount(); ri++) { String field = matcherHdr.group(ri); Object value = variables.getOrDefault(field, ""); result = result.replace(matcherHdr.group(0), String.valueOf(value)); } if ("${phone1}".equals(cellValue) || "${phone2}".equals(cellValue) || "${hs_code}".equals(cellValue)) { c.setCellValue(result); // 字符串 } else { try { double num = Double.parseDouble(result.replace(",", "")); c.setCellValue(num); // 数值 } catch (NumberFormatException e) { c.setCellValue(result); // 字符串 } } } Matcher matcherDtl = PATTERN_DTL_FIELD.matcher(cellValue); while (matcherDtl.find()) { for (int ri = 1; ri <= matcherDtl.groupCount(); ri++) { String field = matcherDtl.group(ri); c.setCellValue(cellValue.replace(matcherDtl.group(0), String.valueOf(listVariables.get(i - dtlRowIndex).getOrDefault(field, "")))); } // 设置样式 if (cellStyle) { XSSFCellStyle style = c.getCellStyle(); style.setBorderBottom(BorderStyle.NONE); style.setBorderTop(BorderStyle.NONE); style.setWrapText(true); style.setAlignment(HorizontalAlignment.LEFT); c.setCellStyle(style); if (rangeStyle && j < 4) { for (int mi = 0; mi < 3; mi++) { XSSFCell nextc = sheet.getRow(i).getCell(c.getColumnIndex()+mi+1); if (nextc == null) { continue; } nextc.setCellStyle(style); } } } } } } // 设置明细样式,因为明细是动态添加的,会有样式和模版偏差,此处主要是第2、3列的样式需要设置 if (cellStyle) { for (Integer dtlRow : dtlRows) { XSSFCell c1 = sheet.getRow(dtlRow).getCell(cellStyle2?0:1); if (c1 == null) { continue; } XSSFCellStyle style = workbook.createCellStyle(); style.setBorderRight(BorderStyle.NONE); style.setBorderLeft(BorderStyle.MEDIUM); // 实线 style.setBorderBottom(BorderStyle.NONE); style.setBorderTop(BorderStyle.NONE); Font font = workbook.createFont(); font.setFontName("Arial"); // 设置字体 font.setFontHeightInPoints((short) 10); // 设置字号 style.setFont(font); style.setVerticalAlignment(VerticalAlignment.TOP); c1.setCellStyle(style); XSSFCell c2 = sheet.getRow(dtlRow).getCell(cellStyle2?1:2); if (c2 == null) { continue; } XSSFCellStyle style2 = workbook.createCellStyle(); style2.setBorderRight(BorderStyle.MEDIUM); style2.setBorderLeft(BorderStyle.NONE); style2.setBorderBottom(BorderStyle.NONE); style2.setBorderTop(BorderStyle.NONE); Font font2 = workbook.createFont(); font2.setFontName("Arial"); // 设置字体 font2.setFontHeightInPoints((short) 10); // 设置字号 style2.setFont(font2); style2.setVerticalAlignment(VerticalAlignment.TOP); style2.setAlignment(HorizontalAlignment.LEFT); style2.setWrapText(true); c2.setCellStyle(style2); if (priceRight) { //仅供发票excel使用,第8、9是价格列居右 for (int i = 6; i < 9; i++) { XSSFCell c7 = sheet.getRow(dtlRow).getCell(i); if (c7 == null) { continue; } // 尝试把字符串转成数值 if (c7.getCellType() == CellType.STRING) { String strVal = c7.getStringCellValue(); if (strVal != null && !strVal.trim().isEmpty()) { try { double num = Double.parseDouble(strVal.replace(",", "")); c7.setCellValue(num); // 转换为数值写回 } catch (NumberFormatException e) { // 如果不是数字就保留原字符串 System.out.println("非数字,保持原值: " + strVal); } } } // 创建样式 XSSFCellStyle style7 = workbook.createCellStyle(); style7.setBorderRight(BorderStyle.MEDIUM); style7.setBorderLeft(BorderStyle.NONE); style7.setBorderBottom(BorderStyle.NONE); style7.setBorderTop(BorderStyle.NONE); Font font7 = workbook.createFont(); font7.setFontName("Arial"); font7.setFontHeightInPoints((short) 10); style7.setFont(font7); style7.setVerticalAlignment(VerticalAlignment.TOP); style7.setAlignment(HorizontalAlignment.RIGHT); // 设置千分位格式 DataFormat dataFormat = workbook.createDataFormat(); style7.setDataFormat(i==6?dataFormat.getFormat("#,##0"): i==7?dataFormat.getFormat("#,##0.00000") :dataFormat.getFormat("#,##0.00")); c7.setCellStyle(style7); } } if (intRight) { //仅供箱单excel使用 for (int i = 4; i < 9; i++) { XSSFCell c7 = sheet.getRow(dtlRow).getCell(i); if (c7 == null) { continue; } // 尝试把字符串转成数值 if (c7.getCellType() == CellType.STRING) { String strVal = c7.getStringCellValue(); if (strVal != null && !strVal.trim().isEmpty()) { try { double num = Double.parseDouble(strVal.replace(",", "")); c7.setCellValue(num); // 转换为数值写回 } catch (NumberFormatException e) { // 如果不是数字就保留原字符串 System.out.println("非数字,保持原值: " + strVal); } } } // 创建样式 XSSFCellStyle style7 = workbook.createCellStyle(); style7.setBorderRight(BorderStyle.MEDIUM); style7.setBorderLeft(i==4?BorderStyle.MEDIUM:BorderStyle.NONE); style7.setBorderBottom(BorderStyle.NONE); style7.setBorderTop(BorderStyle.NONE); Font font7 = workbook.createFont(); font7.setFontName("Arial"); font7.setFontHeightInPoints((short) 10); style7.setFont(font7); style7.setVerticalAlignment(VerticalAlignment.TOP); style7.setAlignment(HorizontalAlignment.RIGHT); // 设置千分位格式 if (i==4 || i==5 || i==8) { DataFormat dataFormat = workbook.createDataFormat(); style7.setDataFormat(dataFormat.getFormat("#,##0")); } else { DataFormat dataFormat = workbook.createDataFormat(); style7.setDataFormat(dataFormat.getFormat("#,##0.00")); } c7.setCellStyle(style7); } } } } if (delRight) { for (Integer dtlRow : dtlRows) { for (int i = 6; i < 12; i++) { if (i==6 || i==11) { XSSFCell c7 = sheet.getRow(dtlRow).getCell(i); if (c7 == null) { continue; } // 尝试把字符串转成数值 if (c7.getCellType() == CellType.STRING) { String strVal = c7.getStringCellValue(); if (strVal != null && !strVal.trim().isEmpty()) { try { double num = Double.parseDouble(strVal.replace(",", "")); c7.setCellValue(num); // 转换为数值写回 } catch (NumberFormatException e) { // 如果不是数字就保留原字符串 System.out.println("非数字,保持原值: " + strVal); } } } // 创建样式 XSSFCellStyle style7 = workbook.createCellStyle(); style7.setBorderRight(BorderStyle.NONE); style7.setBorderLeft(BorderStyle.NONE); style7.setBorderBottom(BorderStyle.NONE); style7.setBorderTop(BorderStyle.NONE); Font font7 = workbook.createFont(); font7.setFontName("Arial"); font7.setFontHeightInPoints((short) 10); style7.setFont(font7); style7.setVerticalAlignment(VerticalAlignment.CENTER); style7.setAlignment(HorizontalAlignment.RIGHT); // 设置千分位格式 DataFormat dataFormat = workbook.createDataFormat(); style7.setDataFormat(i==6?dataFormat.getFormat("#,##0"):dataFormat.getFormat("#,##0.00")); c7.setCellStyle(style7); } } } } return workbook; } }