8 changed files with 1018 additions and 26 deletions
-
750src/main/java/com/xujie/sys/common/utils/ExcelTemplateYB.java
-
294src/main/java/com/xujie/sys/modules/ecss/service/impl/CoDelExcelTXServiceImpl.java
-
BINsrc/main/resources/templates/TX/declaration-all-template-pdf.xlsx
-
BINsrc/main/resources/templates/TX/declaration-all-template.xlsx
-
BINsrc/main/resources/templates/TX/declaration-packingList-template.xlsx
-
BINsrc/main/resources/templates/YB/declaration-all-template-pdf.xlsx
-
BINsrc/main/resources/templates/YB/declaration-all-template.xlsx
-
BINsrc/main/resources/templates/YB/declaration-packingList-template.xlsx
@ -0,0 +1,750 @@ |
|||
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.util.*; |
|||
import java.util.regex.Matcher; |
|||
import java.util.regex.Pattern; |
|||
|
|||
public class ExcelTemplateYB { |
|||
|
|||
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; |
|||
// 数字靠右 箱单 |
|||
@Setter |
|||
private boolean intRight = false; |
|||
// 报关单 |
|||
@Setter |
|||
private boolean delRight = false; |
|||
// 箱单 |
|||
@Setter |
|||
private boolean boxFlag = false; |
|||
|
|||
private ExcelTemplateYB(){} |
|||
|
|||
public static ExcelTemplateYB load(InputStream template) throws IOException { |
|||
ExcelTemplateYB excelTemplate = new ExcelTemplateYB(); |
|||
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; |
|||
intRight = false; |
|||
delRight = 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) { |
|||
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<>(); |
|||
//整体填值 |
|||
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); |
|||
} |
|||
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 { |
|||
// 更严格的数字检查 |
|||
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); // 字符串 |
|||
} |
|||
} |
|||
} |
|||
|
|||
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 (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); |
|||
if (i==6 || i==11) { |
|||
// 尝试把字符串转成数值 |
|||
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.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 = (c7.getCellType() == CellType.NUMERIC) |
|||
? buildDecimalFormat(c7.getNumericCellValue()) |
|||
: "#,##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 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); |
|||
} |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue