You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
960 lines
44 KiB
960 lines
44 KiB
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);
|
|
}
|
|
}
|