|
|
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.ss.util.CellRangeAddress;import org.apache.poi.xssf.usermodel.*;
import java.io.IOException;import java.io.InputStream;import java.math.BigDecimal;import java.math.RoundingMode;import java.util.*;import java.util.regex.Matcher;import java.util.regex.Pattern;
public class ExcelTemplateALPHA {
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<String, Object> variables = new HashMap<>(); private List<Map<String, Object>> listVariables = new ArrayList<>(); // 存储需要合并的单元格区域信息:[列表起始索引, 列表结束索引, 列索引]
private List<int[]> mergeRegions = new ArrayList<>(); // 存储需要设置行高的行信息:[起始行索引, 结束行索引, 行高]
private List<int[]> rowHeights = 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 invoiceLie = false; // 发票价格列是否使用固定小数位(单价5位、总价2位)
@Setter private boolean fixedInvoicePriceScale = false; // 报关单价格列是否固定两位小数(净重/单价/总价)
@Setter private boolean fixedDeclarationPriceScale = false; // 数字靠右 箱单
@Setter private boolean intRight = false; // 报关单
@Setter private boolean delRight = false; // 箱单
@Setter private boolean boxFlag = false;
private ExcelTemplateALPHA(){}
public static ExcelTemplateALPHA load(InputStream template) throws IOException { ExcelTemplateALPHA excelTemplate = new ExcelTemplateALPHA(); 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<String, Object> row){ listVariables.add(row); }
public void addListVarAll(Collection rows){ listVariables.addAll(rows); }
/** * 添加需要合并的单元格区域(基于列表索引) * @param startListIndex 列表起始索引(相对于listVariables) * @param endListIndex 列表结束索引(相对于listVariables) * @param colIndex 列索引 */ public void addMergeRegion(int startListIndex, int endListIndex, int colIndex) { if (startListIndex <= endListIndex) { mergeRegions.add(new int[]{startListIndex, endListIndex, colIndex, colIndex, 0}); // 格式:[startRow, endRow, startCol, endCol, alignType]
} }
/** * 添加合并区域,并指定对齐方式 * @param startListIndex 起始行索引 * @param endListIndex 结束行索引 * @param colIndex 列索引 * @param alignType 对齐方式:0=右对齐,1=左对齐,2=居中 */ public void addMergeRegion(int startListIndex, int endListIndex, int colIndex, int alignType) { if (startListIndex <= endListIndex) { mergeRegions.add(new int[]{startListIndex, endListIndex, colIndex, colIndex, alignType}); } }
/** * 添加跨列的合并区域(支持横向和纵向同时合并) * @param startListIndex 起始行索引 * @param endListIndex 结束行索引 * @param startColIndex 起始列索引 * @param endColIndex 结束列索引 * @param alignType 对齐方式:0=右对齐,1=左对齐,2=居中 */ public void addMergeRegion(int startListIndex, int endListIndex, int startColIndex, int endColIndex, int alignType) { if (startListIndex <= endListIndex && startColIndex <= endColIndex) { mergeRegions.add(new int[]{startListIndex, endListIndex, startColIndex, endColIndex, alignType}); } }
/** * 设置行高 * @param startListIndex 起始行索引(相对于listVariables) * @param endListIndex 结束行索引(相对于listVariables) * @param height 行高(单位:点) */ public void setRowHeight(int startListIndex, int endListIndex, int height) { if (startListIndex <= endListIndex) { rowHeights.add(new int[]{startListIndex, endListIndex, height}); } }
public void clearAll(){ variables.clear(); listVariables.clear(); mergeRegions.clear(); rowHeights.clear(); moveShape = false; moveSeal = false; cellStyle = false; cellStyle2 = false; rangeStyle = false; priceRight = false; fixedInvoicePriceScale = false; fixedDeclarationPriceScale = false; intRight = false; delRight = false; } private void applyMultilineStyleIfNeeded(XSSFCell cell, String content) { if (cell == null || content == null || !content.contains("\n")) { return; }
XSSFCellStyle baseStyle = cell.getCellStyle(); XSSFCellStyle multilineStyle = workbook.createCellStyle(); if (baseStyle != null) { multilineStyle.cloneStyleFrom(baseStyle); } multilineStyle.setWrapText(true); multilineStyle.setVerticalAlignment(VerticalAlignment.TOP); cell.setCellStyle(multilineStyle);
XSSFRow row = cell.getRow(); if (row == null) { return; }
int lineCount = content.split("\n", -1).length; if (lineCount <= 1) { return; }
float defaultRowHeight = row.getSheet().getDefaultRowHeightInPoints(); float currentRowHeight = row.getHeightInPoints(); float targetHeight = Math.max(currentRowHeight, defaultRowHeight * lineCount); if (targetHeight > currentRowHeight) { row.setHeightInPoints(targetHeight); } } 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) { int listStartRow = 24; // Excel 第25行(0-based)
int moveOffset = listVariables.size() - 1;
for (POIXMLDocumentPart part : sheet.getRelations()) { if (!(part instanceof XSSFDrawing)) { continue; }
XSSFDrawing drawing = (XSSFDrawing) part; for (XSSFShape shape : drawing.getShapes()) { if (!(shape instanceof XSSFSimpleShape)) { continue; }
XSSFSimpleShape simpleShape = (XSSFSimpleShape) shape; ClientAnchor anchor = (ClientAnchor) simpleShape.getAnchor();
if (anchor != null && anchor.getRow1() >= listStartRow) { anchor.setRow1(anchor.getRow1() + moveOffset); if (anchor instanceof XSSFClientAnchor) { XSSFClientAnchor xAnchor = (XSSFClientAnchor) anchor; try { xAnchor.setRow2(xAnchor.getRow2() + moveOffset); } catch (Exception ignore) { // row2 不存在,忽略
} } } } } }
// 遍历所有图片 将列表下方的形状坐标也下移
if (moveSeal) { List<POIXMLDocumentPart> relations = sheet.getRelations(); for (POIXMLDocumentPart part : relations) { if (part instanceof XSSFDrawing) { XSSFDrawing drawing = (XSSFDrawing) part; List<XSSFShape> 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); }
} } } } }
Set<Integer> dtlRows = new LinkedHashSet<>(); List<Integer> boxRows = new ArrayList<>(); Set<Integer> poNoCols = new HashSet<>(); Set<Integer> invoiceUnitPriceCols = new HashSet<>(); Set<Integer> invoiceTotalPriceCols = new HashSet<>(); Set<Integer> declarationFixedPriceCols = new HashSet<>(); Set<Integer> declarationQtyCols = new HashSet<>(); //整体填值
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) || "#{levy}".equals(cellValue) || "#{artNo}".equals(cellValue)) { dtlRows.add(i); } if ("#{cmcInvoice}".equals(cellValue)) { boxRows.add(i); } // 仅记录明细模板行上的po_no列,避免同名占位符误伤其它数字列
if (i == dtlRowIndex && "#{po_no}".equalsIgnoreCase(cellValue)) { poNoCols.add(j); } if (i == dtlRowIndex) { Matcher matcherDtlField = PATTERN_DTL_FIELD.matcher(cellValue); while (matcherDtlField.find()) { String dtlField = matcherDtlField.group(1); if (isInvoiceUnitPriceField(dtlField)) { invoiceUnitPriceCols.add(j); } else if (isInvoiceTotalPriceField(dtlField)) { invoiceTotalPriceCols.add(j); } else if (isDeclarationFixedPriceField(dtlField)) { declarationFixedPriceCols.add(j); } else if (isDeclarationQtyField(dtlField)) { declarationQtyCols.add(j); } } } Matcher matcherHdr = PATTERN_HDR_FIELD.matcher(cellValue); String result = c.getStringCellValue(); boolean hasHeaderPlaceholder = false; while (matcherHdr.find()) { hasHeaderPlaceholder = true; 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)); } boolean isVoyageCell = "${voyage}".equals(cellValue); boolean isHsCodeDescCell = cellValue.contains("${hs_code_desc}"); if ("${phone1}".equals(cellValue) || "${phone2}".equals(cellValue) || "${hs_code}".equals(cellValue) || "${pickupAddress}".equals(cellValue) || isVoyageCell || isHsCodeDescCell) { c.setCellValue(result); // 字符串
if ("${pickupAddress}".equals(cellValue) || isVoyageCell) { applyMultilineCellStyle(c, result, isVoyageCell); } if (isHsCodeDescCell) { applyHsCodeDescRichText(c, result); } } else { try { // 更严格的数字检查
String cleanResult = result.replace(",", "").trim(); if (!cleanResult.isEmpty() && !cleanResult.equals("-") && !cleanResult.equals(".")) { double num = Double.parseDouble(cleanResult); if (Double.isFinite(num)) { c.setCellValue(num); // 数值
// goods_total_qty 根据实际小数位动态设置格式
if ("${goods_total_qty}".equals(cellValue)) { XSSFCellStyle fmtStyle = workbook.createCellStyle(); fmtStyle.cloneStyleFrom(c.getCellStyle()); DataFormat dataFormat = workbook.createDataFormat(); fmtStyle.setDataFormat(dataFormat.getFormat(buildDecimalFormat(num))); c.setCellStyle(fmtStyle); } } else { c.setCellValue(result); // 字符串
} } else { c.setCellValue(result); // 字符串
} } catch (NumberFormatException e) { c.setCellValue(result); // 字符串
} } } if (hasHeaderPlaceholder) { applyMultilineStyleIfNeeded(c, result); } Matcher matcherDtl = PATTERN_DTL_FIELD.matcher(cellValue); while (matcherDtl.find()) { for (int ri = 1; ri <= matcherDtl.groupCount(); ri++) { String field = matcherDtl.group(ri); // 检查数组边界,避免越界异常
int listIndex = i - dtlRowIndex; if (listIndex >= 0 && listIndex < listVariables.size()) { c.setCellValue(cellValue.replace(matcherDtl.group(0), String.valueOf(listVariables.get(listIndex).getOrDefault(field, "")))); } } }
// 设置样式
if (cellStyle && dtlRowIndex >= 0 && i >= dtlRowIndex && i < dtlRowIndex + listVariables.size()) { 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); style.setWrapText(true); 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使用,第6、7、8是价格列居右
for (int i = 6; i < (invoiceLie?11:9); i++) { XSSFRow row = sheet.getRow(dtlRow); if (row == null) { continue; } XSSFCell c7 = row.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(); String numFmt; if (fixedInvoicePriceScale && invoiceUnitPriceCols.contains(i)) { numFmt = "#,##0.00000"; } else if (fixedInvoicePriceScale && invoiceTotalPriceCols.contains(i)) { numFmt = "#,##0.00"; } else if (c7.getCellType() == CellType.NUMERIC) { numFmt = buildDecimalFormat(c7.getNumericCellValue()); } else { numFmt = "#,##0.00"; } style7.setDataFormat(dataFormat.getFormat(numFmt));
c7.setCellStyle(style7); } } if (intRight) { //仅供箱单excel使用
// 覆盖到第10列(索引9),保证9、10列按数字处理
for (int i = 4; i < 11; i++) { XSSFRow row = sheet.getRow(dtlRow); if (row == null) { continue; } XSSFCell c7 = row.getCell(i); if (c7 == null) { continue; } boolean isPoNoColumn = poNoCols.contains(i); BigDecimal numericValue = null; // 尝试把字符串转成数值
if (!isPoNoColumn && c7.getCellType() == CellType.STRING) { String strVal = c7.getStringCellValue(); BigDecimal parsed = parseNumericString(strVal); if (parsed != null) { numericValue = parsed; c7.setCellValue(parsed.doubleValue()); // 转换为数值写回
} } else if (!isPoNoColumn && c7.getCellType() == CellType.NUMERIC) { numericValue = BigDecimal.valueOf(c7.getNumericCellValue()); }
// 创建样式
XSSFCellStyle style7 = workbook.createCellStyle(); style7.setBorderRight(BorderStyle.MEDIUM); style7.setBorderLeft(i==4?BorderStyle.MEDIUM:BorderStyle.NONE); style7.setBorderBottom(BorderStyle.NONE); style7.setBorderTop(BorderStyle.NONE);
// 判断单元格的值是否小于等于0,如果小于等于0则加粗
boolean isBold = false; if (numericValue != null && numericValue.compareTo(BigDecimal.ZERO) <= 0) { isBold = true; }
Font font7 = workbook.createFont(); font7.setFontName("Arial"); font7.setFontHeightInPoints((short) 10); if (isBold) { font7.setBold(true); } style7.setFont(font7);
style7.setVerticalAlignment(VerticalAlignment.TOP); style7.setAlignment(HorizontalAlignment.CENTER); DataFormat dataFormat = workbook.createDataFormat(); if (isPoNoColumn) { // po_no 是编号列,必须按文本处理,不能套用千分位
style7.setDataFormat(dataFormat.getFormat("@")); } else { // 设置千分位格式(根据单元格实际小数位动态决定格式)
String numFmt2; if (numericValue != null) { // 去掉末尾0后按实际小数位显示:20.000000 -> 20,0.150000 -> 0.15
numFmt2 = buildDecimalFormatByDecimal(numericValue); } else if (c7.getCellType() == CellType.NUMERIC) { numFmt2 = buildDecimalFormat(c7.getNumericCellValue()); } else { numFmt2 = "#,##0"; } style7.setDataFormat(dataFormat.getFormat(numFmt2)); } c7.setCellStyle(style7); }
// net_weight列(J列,索引9)保持顶部右对齐
/*XSSFRow row = sheet.getRow(dtlRow); if (row != null) { XSSFCell netWeightCell = row.getCell(9); if (netWeightCell != null) { XSSFCellStyle netWeightStyle = workbook.createCellStyle(); netWeightStyle.cloneStyleFrom(netWeightCell.getCellStyle()); netWeightStyle.setVerticalAlignment(VerticalAlignment.TOP); netWeightStyle.setAlignment(HorizontalAlignment.RIGHT); netWeightCell.setCellStyle(netWeightStyle); } }*/ } } } if (delRight) { for (Integer dtlRow : dtlRows) { for (int i = 0; i < 17; i++) { XSSFRow row = sheet.getRow(dtlRow); if (row == null) { continue; } XSSFCell c7 = row.getCell(i); if (c7 == null) { continue; } // 创建样式
XSSFCellStyle style = workbook.createCellStyle(); style.setBorderRight(i==16?BorderStyle.THIN:BorderStyle.NONE); style.setBorderLeft(i==0?BorderStyle.THIN:BorderStyle.NONE); style.setBorderBottom(BorderStyle.THIN); style.setBorderTop(BorderStyle.NONE); Font font = workbook.createFont(); font.setFontName("Arial"); font.setFontHeightInPoints((short) 10); style.setFont(font); style.setVerticalAlignment(VerticalAlignment.CENTER); style.setAlignment(HorizontalAlignment.LEFT); c7.setCellStyle(style); boolean declarationFixedTwoScaleColumn = fixedDeclarationPriceScale && declarationFixedPriceCols.contains(i); boolean declarationQtyColumn = fixedDeclarationPriceScale && declarationQtyCols.contains(i); boolean declarationNumberColumn = fixedDeclarationPriceScale ? (declarationFixedTwoScaleColumn || declarationQtyColumn) : (i == 6 || i == 11); if (declarationNumberColumn) { BigDecimal numericValue = null; if (c7.getCellType() == CellType.STRING) { numericValue = parseNumericString(c7.getStringCellValue()); } else if (c7.getCellType() == CellType.NUMERIC) { numericValue = BigDecimal.valueOf(c7.getNumericCellValue()); } if (numericValue != null) { if (declarationFixedTwoScaleColumn) { numericValue = numericValue.setScale(2, RoundingMode.HALF_UP); } c7.setCellValue(numericValue.doubleValue()); } // 创建样式
XSSFCellStyle style7 = workbook.createCellStyle(); style7.setBorderRight(BorderStyle.NONE); style7.setBorderLeft(BorderStyle.NONE); style7.setBorderBottom(BorderStyle.THIN); 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(); String numFmt3; if (declarationFixedTwoScaleColumn) { numFmt3 = "#,##0.00"; } else if (declarationQtyColumn) { numFmt3 = "#,##0"; } else if (c7.getCellType() == CellType.NUMERIC) { numFmt3 = buildDecimalFormat(c7.getNumericCellValue()); } else { numFmt3 = "#,##0.00"; } style7.setDataFormat(dataFormat.getFormat(numFmt3)); c7.setCellStyle(style7); } } } } if (boxFlag) { for (Integer dtlRow : boxRows) { for (int i = 1; i < 6; i++) { if (i==1 || i==4 || i==5) { XSSFRow row = sheet.getRow(dtlRow); if (row == null) { continue; } XSSFCell c7 = row.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.THIN); style7.setBorderLeft(BorderStyle.THIN); style7.setBorderBottom(BorderStyle.THIN); style7.setBorderTop(BorderStyle.THIN);
Font font7 = workbook.createFont(); font7.setFontName("DengXian"); // 等线
font7.setFontHeightInPoints((short) 11); // 11号
style7.setFont(font7);
style7.setVerticalAlignment(VerticalAlignment.CENTER); style7.setAlignment(HorizontalAlignment.RIGHT);
// 设置千分位格式(根据单元格实际小数位动态决定格式)
DataFormat dataFormat = workbook.createDataFormat(); String numFmt4 = (c7.getCellType() == CellType.NUMERIC) ? buildDecimalFormat(c7.getNumericCellValue()) : "#,##0"; style7.setDataFormat(dataFormat.getFormat(numFmt4)); c7.setCellStyle(style7); } } } }
// 处理合并单元格 - 将列表索引转换为实际行号并合并
if (dtlRowIndex >= 0 && !mergeRegions.isEmpty()) { for (int[] region : mergeRegions) { int startRow = dtlRowIndex + region[0]; int endRow = dtlRowIndex + region[1]; int startCol = region[2]; int endCol = region.length >= 4 ? region[3] : region[2]; // 支持跨列,如果没有指定endCol则等于startCol
boolean needMerge = startRow < endRow || startCol < endCol; if (needMerge) { // 创建新的合并区域(支持横向和纵向合并)
CellRangeAddress mergeRange = new CellRangeAddress(startRow, endRow, startCol, endCol);
// 检查并删除与新合并区域冲突的已存在合并区域
List<Integer> toRemove = new ArrayList<>(); for (int i = 0; i < sheet.getNumMergedRegions(); i++) { CellRangeAddress existingRegion = sheet.getMergedRegion(i); if (existingRegion != null && mergeRange.intersects(existingRegion)) { toRemove.add(i); } } // 从后往前删除,避免索引变化
for (int i = toRemove.size() - 1; i >= 0; i--) { sheet.removeMergedRegion(toRemove.get(i)); }
// 添加新的合并区域
sheet.addMergedRegion(mergeRange); }
// 根据是否实际合并设置对齐方式:
// 1) 已合并:垂直居中 + 水平居中
// 2) 未合并(单格):垂直顶对齐 + 水平居中
XSSFRow row = sheet.getRow(startRow); if (row != null) { XSSFCell cell = row.getCell(startCol); if (cell != null) { XSSFCellStyle mergeStyle = workbook.createCellStyle(); mergeStyle.cloneStyleFrom(cell.getCellStyle()); mergeStyle.setVerticalAlignment(needMerge ? VerticalAlignment.CENTER : VerticalAlignment.TOP); mergeStyle.setAlignment(HorizontalAlignment.CENTER); cell.setCellStyle(mergeStyle); } } } }
// 处理行高设置
if (dtlRowIndex >= 0 && !rowHeights.isEmpty()) { for (int[] heightInfo : rowHeights) { int startRow = dtlRowIndex + heightInfo[0]; int endRow = dtlRowIndex + heightInfo[1]; int height = heightInfo[2];
for (int rowIdx = startRow; rowIdx <= endRow; rowIdx++) { XSSFRow row = sheet.getRow(rowIdx); if (row != null) { // 设置行高(Excel行高单位是1/20点,所以需要乘以20)
row.setHeight((short) (height * 20)); } } } }
return workbook; }
private void applyMultilineCellStyle(XSSFCell cell, String value, boolean voyageCell) { if (cell == null || value == null || !value.contains("\n")) { return; } XSSFCellStyle wrapStyle = workbook.createCellStyle(); wrapStyle.cloneStyleFrom(cell.getCellStyle()); wrapStyle.setWrapText(true); wrapStyle.setVerticalAlignment(VerticalAlignment.TOP); wrapStyle.setAlignment(HorizontalAlignment.LEFT); if (voyageCell) { XSSFFont baseFont = workbook.getFontAt(cell.getCellStyle().getFontIndexAsInt()); XSSFFont monoFont = workbook.createFont(); monoFont.setFontName("Courier New"); monoFont.setFontHeight(baseFont.getFontHeight()); monoFont.setBold(false); monoFont.setColor(baseFont.getColor()); wrapStyle.setFont(monoFont); } cell.setCellStyle(wrapStyle);
XSSFRow row = cell.getRow(); if (row != null) { int lineCount = value.split("\n", -1).length; CellRangeAddress mergedRegion = getMergedRegionForCell(cell); int defaultHeight = row.getSheet().getDefaultRowHeight(); if (mergedRegion != null && mergedRegion.getFirstRow() != mergedRegion.getLastRow()) { int rowSpan = mergedRegion.getLastRow() - mergedRegion.getFirstRow() + 1; int targetLines = Math.max(2, Math.min(lineCount, 15)); int totalTargetHeight = defaultHeight * targetLines; short perRowHeight = (short) Math.max(defaultHeight, Math.min(defaultHeight * 4, (totalTargetHeight + rowSpan - 1) / rowSpan)); for (int rowIndex = mergedRegion.getFirstRow(); rowIndex <= mergedRegion.getLastRow(); rowIndex++) { XSSFRow mergedRow = row.getSheet().getRow(rowIndex); if (mergedRow == null) { mergedRow = row.getSheet().createRow(rowIndex); } if (mergedRow.getHeight() < perRowHeight) { mergedRow.setHeight(perRowHeight); } } } else { short targetHeight = (short) (defaultHeight * Math.max(2, Math.min(lineCount, 6))); if (row.getHeight() < targetHeight) { row.setHeight(targetHeight); } } } }
private CellRangeAddress getMergedRegionForCell(XSSFCell cell) { if (cell == null || cell.getSheet() == null) { return null; } int rowIndex = cell.getRowIndex(); int colIndex = cell.getColumnIndex(); for (CellRangeAddress region : cell.getSheet().getMergedRegions()) { if (region.isInRange(rowIndex, colIndex)) { return region; } } return null; }
private void applyHsCodeDescRichText(XSSFCell cell, String value) { if (cell == null || value == null || !value.contains("货物描述")) { return; } XSSFCellStyle wrapStyle = workbook.createCellStyle(); wrapStyle.cloneStyleFrom(cell.getCellStyle()); wrapStyle.setWrapText(true); wrapStyle.setVerticalAlignment(VerticalAlignment.TOP); wrapStyle.setAlignment(HorizontalAlignment.LEFT); cell.setCellStyle(wrapStyle);
String titleText = "货物描述:"; int titleStart = value.indexOf(titleText); if (titleStart < 0) { titleText = "货物描述:"; titleStart = value.indexOf(titleText); } if (titleStart < 0) { return; } int titleEnd = titleStart + titleText.length(); XSSFRichTextString richText = new XSSFRichTextString(value); XSSFFont baseFont = workbook.getFontAt(cell.getCellStyle().getFontIndexAsInt());
XSSFFont titleFont = workbook.createFont(); titleFont.setFontName(baseFont.getFontName()); titleFont.setFontHeight(baseFont.getFontHeight()); titleFont.setColor(baseFont.getColor()); titleFont.setBold(true);
XSSFFont valueFont = workbook.createFont(); valueFont.setFontName(baseFont.getFontName()); valueFont.setFontHeightInPoints((short) 10); valueFont.setColor(baseFont.getColor()); valueFont.setBold(false);
richText.applyFont(titleStart, titleEnd, titleFont); if (titleEnd < value.length()) { richText.applyFont(titleEnd, value.length(), valueFont); } cell.setCellValue(richText); }
private boolean isInvoiceUnitPriceField(String field) { return "unitPrice".equalsIgnoreCase(field) || "tp".equalsIgnoreCase(field); }
private boolean isInvoiceTotalPriceField(String field) { return "totalPrice".equalsIgnoreCase(field) || "ttl_amount".equalsIgnoreCase(field); }
private boolean isDeclarationFixedPriceField(String field) { return "net_weight".equalsIgnoreCase(field) || "unit_price".equalsIgnoreCase(field) || "total_price".equalsIgnoreCase(field); }
private boolean isDeclarationQtyField(String field) { return "qty".equalsIgnoreCase(field); }
private BigDecimal parseNumericString(String value) { if (value == null) { return null; } String clean = value.replace(",", "").trim(); if (clean.isEmpty() || "-".equals(clean) || ".".equals(clean)) { return null; } try { return new BigDecimal(clean); } catch (NumberFormatException e) { return null; } }
private String buildDecimalFormatByDecimal(BigDecimal value) { if (value == null) { return "#,##0"; } int scale = Math.max(0, value.stripTrailingZeros().scale()); if (scale <= 0) { return "#,##0"; } return "#,##0." + "0".repeat(scale); }
/** * 根据数值的实际小数位数动态生成 Excel 数字格式串(带千分位)。 * 例:1.0 → "#,##0",12.34 → "#,##0.00",0.00123 → "#,##0.00000" * 使用 Double.toString() 转 BigDecimal 再去尾零,避免浮点误差导致位数虚高。 */ private String buildDecimalFormat(double value) { if (value == 0) { return "#,##0"; } BigDecimal bd = new BigDecimal(Double.toString(Math.abs(value))).stripTrailingZeros(); int scale = Math.max(0, bd.scale()); if (scale == 0) { return "#,##0"; } return "#,##0." + "0".repeat(scale); }}
|