|
|
@@ -7,10 +7,16 @@ import com.alibaba.excel.write.handler.context.RowWriteHandlerContext;
|
|
|
import com.nb.web.service.bus.service.dto.BusLiquidExcelVO;
|
|
|
import org.apache.poi.ss.usermodel.*;
|
|
|
import org.apache.poi.ss.util.CellRangeAddress;
|
|
|
+import org.apache.poi.util.IOUtils;
|
|
|
|
|
|
import java.util.List;
|
|
|
import java.util.Set;
|
|
|
import java.util.HashSet;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.io.ByteArrayInputStream;
|
|
|
+import java.util.Base64;
|
|
|
|
|
|
/**
|
|
|
* @ClassName : LiquidExcelMergeWriteHandler
|
|
|
@@ -18,11 +24,10 @@ import java.util.HashSet;
|
|
|
* @Author :
|
|
|
* @Date: 2025年09月11日
|
|
|
*/
|
|
|
-
|
|
|
public class LiquidExcelMergeWriteHandler implements RowWriteHandler {
|
|
|
// 最小行索引
|
|
|
private int minRowIndex = 0;
|
|
|
- // 需要合并的列索引(根据ExcelProperty的order值)
|
|
|
+ // 需要合并的列索引(根据ExcelProperty的order值,排除图片列)
|
|
|
private Set<Integer> includeColumnIndexes = new HashSet<Integer>() {{
|
|
|
add(0); // 序号 (num) - order = 1
|
|
|
add(1); // 手术名称 (clinicName) - order = 1
|
|
|
@@ -35,13 +40,18 @@ public class LiquidExcelMergeWriteHandler implements RowWriteHandler {
|
|
|
add(8); // 撤泵时间 (undoTime) - order = 8
|
|
|
add(15); // 总量 (totalDose) - order = 14
|
|
|
add(16); // 剩余量 (remainDose) - order = 15
|
|
|
- add(17); // 废液检查人 (liquidChecker) - order = 17
|
|
|
- add(18); // 废液核对备注 (liquidRemark) - order = 18
|
|
|
+ add(17); // 废液核对备注 (liquidRemark) - order = 18
|
|
|
+ add(18); // 废液核对时间 (liquidTime) - order = 19
|
|
|
add(19); // 废液核对时间 (liquidTime) - order = 19
|
|
|
add(20); // 废液核对时间 (liquidTime) - order = 19
|
|
|
- add(21); // 废液执行人 (liquidExecutor) - order = 16
|
|
|
+ add(21); // 废液核对时间 (liquidTime) - order = 19
|
|
|
+ // 21(废液检查人) 和 22(废液执行人) 不包含在内,因为它们是图片列
|
|
|
}};
|
|
|
|
|
|
+ // 图片列索引
|
|
|
+ private static final int LIQUID_EXECUTOR_COLUMN = 17; // 废液执行人 - order = 21
|
|
|
+ private static final int LIQUID_CHECKER_COLUMN = 18; // 废液检查人 - order = 23
|
|
|
+
|
|
|
private List<BusLiquidExcelVO> source;
|
|
|
|
|
|
public LiquidExcelMergeWriteHandler(List<BusLiquidExcelVO> source) {
|
|
|
@@ -56,32 +66,154 @@ public class LiquidExcelMergeWriteHandler implements RowWriteHandler {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- // 设置表头字体和大小
|
|
|
- setHeaderStyle(context.getWriteSheetHolder().getSheet());
|
|
|
+ Sheet sheet = context.getWriteSheetHolder().getSheet();
|
|
|
+ setHeaderStyle(sheet);
|
|
|
+
|
|
|
// 设置所有单元格垂直居中
|
|
|
- setVerticalAlignment(context.getWriteSheetHolder().getSheet());
|
|
|
+ setVerticalAlignment(sheet);
|
|
|
// 自适应列宽
|
|
|
- autoSizeColumns(context.getWriteSheetHolder().getSheet());
|
|
|
+ autoSizeColumns(sheet);
|
|
|
+ // 先执行合并操作
|
|
|
+ performCellMerge(sheet);
|
|
|
|
|
|
- Integer lastRowIndex = rowIndex;
|
|
|
+ // 再处理图片显示
|
|
|
+// handleImageColumnsAfterMerge(sheet);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 执行单元格合并
|
|
|
+ */
|
|
|
+ private void performCellMerge(Sheet sheet) {
|
|
|
+// Integer lastRowIndex = CollectionUtil.size(source) + 1;
|
|
|
+ Integer lastRowIndex = CollectionUtil.size(source) ;
|
|
|
int lastMergeIndex = lastRowIndex;
|
|
|
int currentIndex = lastRowIndex;
|
|
|
+
|
|
|
while (currentIndex >= 1) {
|
|
|
- BusLiquidExcelVO curSource = CollectionUtil.get(source, currentIndex -1 );
|
|
|
- BusLiquidExcelVO preSource = CollectionUtil.get(source, currentIndex -2);
|
|
|
+ BusLiquidExcelVO curSource = CollectionUtil.get(source, currentIndex - 1);
|
|
|
+ BusLiquidExcelVO preSource = CollectionUtil.get(source, currentIndex - 2);
|
|
|
if (!StrUtil.equals(curSource.getClinicId(), preSource.getClinicId()) || currentIndex == 1) {
|
|
|
// 合并指定的列
|
|
|
for (Integer i : includeColumnIndexes) {
|
|
|
- mergeCell(context.getWriteSheetHolder().getSheet(), currentIndex, lastMergeIndex, i, i);
|
|
|
+ mergeCell(sheet, currentIndex, lastMergeIndex, i, i);
|
|
|
}
|
|
|
currentIndex--;
|
|
|
lastMergeIndex = currentIndex;
|
|
|
- }else {
|
|
|
+ } else {
|
|
|
currentIndex--;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 合并后处理图片显示
|
|
|
+ */
|
|
|
+ private void handleImageColumnsAfterMerge(Sheet sheet) {
|
|
|
+ Drawing<?> drawing = sheet.createDrawingPatriarch();
|
|
|
+
|
|
|
+ // 按clinicId分组处理,每组只在第一行显示图片
|
|
|
+ Map<String, List<Integer>> groupRows = new HashMap<>();
|
|
|
+
|
|
|
+ for (int i = 0; i < source.size(); i++) {
|
|
|
+ BusLiquidExcelVO record = source.get(i);
|
|
|
+ String clinicId = record.getClinicId();
|
|
|
+ int rowIndex = i + 1;
|
|
|
+
|
|
|
+ groupRows.computeIfAbsent(clinicId, k -> new ArrayList<>()).add(rowIndex);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 每组只在第一行插入图片
|
|
|
+ for (Map.Entry<String, List<Integer>> entry : groupRows.entrySet()) {
|
|
|
+ List<Integer> rows = entry.getValue();
|
|
|
+ Integer firstRow = rows.get(0); // 获取该组的第一行
|
|
|
+ BusLiquidExcelVO record = source.get(firstRow - 1);
|
|
|
+ if (StrUtil.isNotEmpty(record.getLiquidExecutor())) {
|
|
|
+ insertImage(drawing, sheet, firstRow, LIQUID_EXECUTOR_COLUMN, record.getLiquidExecutor());
|
|
|
+ }
|
|
|
+
|
|
|
+ // 在合并区域的第一行插入图片
|
|
|
+ if (StrUtil.isNotEmpty(record.getLiquidChecker())) {
|
|
|
+ insertImage(drawing, sheet, firstRow, LIQUID_CHECKER_COLUMN, record.getLiquidChecker());
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void insertImage(Drawing<?> drawing, Sheet sheet, int rowIndex, int columnIndex, String base64Image) {
|
|
|
+ try {
|
|
|
+ // 解析Base64图片数据
|
|
|
+ String base64Data = base64Image;
|
|
|
+ if (base64Image.startsWith("data:image")) {
|
|
|
+ // 移除data:image前缀
|
|
|
+ base64Data = base64Image.substring(base64Image.indexOf(",") + 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ byte[] imageBytes = Base64.getDecoder().decode(base64Data);
|
|
|
+ ByteArrayInputStream bis = new ByteArrayInputStream(imageBytes);
|
|
|
+
|
|
|
+ // 创建图片
|
|
|
+ int pictureIdx = sheet.getWorkbook().addPicture(IOUtils.toByteArray(bis), Workbook.PICTURE_TYPE_PNG);
|
|
|
+ bis.close();
|
|
|
+
|
|
|
+ // 获取合并区域的大小
|
|
|
+ int endRow = getMergedRegionEndRow(sheet, rowIndex, columnIndex);
|
|
|
+ int endCol = columnIndex + 1; // 默认向右扩展一列
|
|
|
+
|
|
|
+ // 创建锚点(图片位置),覆盖整个合并区域
|
|
|
+ ClientAnchor anchor = drawing.createAnchor(
|
|
|
+ 0, 0, 0, 0, // dx1, dy1, dx2, dy2
|
|
|
+ columnIndex, rowIndex, // col1, row1 (起始列, 起始行)
|
|
|
+ endCol, endRow + 1 // col2, row2 (结束列, 结束行+1)
|
|
|
+ );
|
|
|
+
|
|
|
+ // 设置锚点类型为移动和随单元格大小调整
|
|
|
+ anchor.setAnchorType(ClientAnchor.AnchorType.MOVE_AND_RESIZE);
|
|
|
+
|
|
|
+ // 插入图片
|
|
|
+ drawing.createPicture(anchor, pictureIdx);
|
|
|
+
|
|
|
+ // 设置单元格样式
|
|
|
+ Row row = sheet.getRow(rowIndex);
|
|
|
+ if (row == null) {
|
|
|
+ row = sheet.createRow(rowIndex);
|
|
|
+ }
|
|
|
+ Cell cell = row.getCell(columnIndex);
|
|
|
+ if (cell == null) {
|
|
|
+ cell = row.createCell(columnIndex);
|
|
|
+ }
|
|
|
+
|
|
|
+ CellStyle style = sheet.getWorkbook().createCellStyle();
|
|
|
+ style.setAlignment(HorizontalAlignment.CENTER);
|
|
|
+ style.setVerticalAlignment(VerticalAlignment.CENTER);
|
|
|
+ cell.setCellStyle(style);
|
|
|
+
|
|
|
+ } catch (Exception e) {
|
|
|
+ // 如果图片处理失败,设置单元格为文本提示
|
|
|
+ Row row = sheet.getRow(rowIndex);
|
|
|
+ if (row == null) {
|
|
|
+ row = sheet.createRow(rowIndex);
|
|
|
+ }
|
|
|
+ Cell cell = row.getCell(columnIndex);
|
|
|
+ if (cell == null) {
|
|
|
+ cell = row.createCell(columnIndex);
|
|
|
+ }
|
|
|
+ cell.setCellValue("图片加载失败");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取指定单元格所在合并区域的结束行号
|
|
|
+ */
|
|
|
+ private int getMergedRegionEndRow(Sheet sheet, int rowIndex, int columnIndex) {
|
|
|
+ for (int i = 0; i < sheet.getNumMergedRegions(); i++) {
|
|
|
+ CellRangeAddress range = sheet.getMergedRegion(i);
|
|
|
+ if (range.isInRange(rowIndex, columnIndex)) {
|
|
|
+ return range.getLastRow();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return rowIndex; // 如果没有合并区域,返回当前行
|
|
|
+ }
|
|
|
+
|
|
|
private void setVerticalAlignment(Sheet sheet) {
|
|
|
CellStyle style = sheet.getWorkbook().createCellStyle();
|
|
|
// 设置垂直居中
|
|
|
@@ -93,11 +225,15 @@ public class LiquidExcelMergeWriteHandler implements RowWriteHandler {
|
|
|
style.setBorderBottom(BorderStyle.THIN);
|
|
|
style.setBorderLeft(BorderStyle.THIN);
|
|
|
style.setBorderRight(BorderStyle.THIN);
|
|
|
- // 应用到所有单元格
|
|
|
+ // 应用到所有单元格(排除图片列)
|
|
|
for (int i = 1; i <= sheet.getLastRowNum(); i++) {
|
|
|
Row row = sheet.getRow(i);
|
|
|
if (row != null) {
|
|
|
for (int j = 0; j < row.getLastCellNum(); j++) {
|
|
|
+ // 跳过图片列
|
|
|
+ if (j == LIQUID_CHECKER_COLUMN || j == LIQUID_EXECUTOR_COLUMN) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
Cell cell = row.getCell(j);
|
|
|
if (cell != null) {
|
|
|
cell.setCellStyle(style);
|
|
|
@@ -110,11 +246,12 @@ public class LiquidExcelMergeWriteHandler implements RowWriteHandler {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-
|
|
|
private void autoSizeColumns(Sheet sheet) {
|
|
|
- // 自适应列宽
|
|
|
- for (int i = 0; i < 20; i++) { // 20列
|
|
|
-// sheet.autoSizeColumn(i);
|
|
|
+ // 自适应列宽(排除图片列)
|
|
|
+ for (int i = 0; i < 22; i++) { // 总共22列
|
|
|
+ if (i != LIQUID_CHECKER_COLUMN && i != LIQUID_EXECUTOR_COLUMN) {
|
|
|
+// sheet.autoSizeColumn(i);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -147,6 +284,7 @@ public class LiquidExcelMergeWriteHandler implements RowWriteHandler {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
private void mergeCell(Sheet sheet, int startRowIndex, int endRowIndex, int startColumnIndex, int endColumnIndex) {
|
|
|
// 开始和结束行数一样
|
|
|
if (startRowIndex == endRowIndex) {
|