commit
06c3f0f11c
17 changed files with 729 additions and 130 deletions
@ -0,0 +1,279 @@ |
|||||||
|
package org.springblade.desk.quality.util; |
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletResponse; |
||||||
|
import org.apache.poi.ss.usermodel.*; |
||||||
|
import org.apache.poi.ss.util.CellRangeAddress; |
||||||
|
import org.apache.poi.xssf.usermodel.XSSFWorkbook; |
||||||
|
import org.springblade.desk.dashboard.utils.DateUtils; |
||||||
|
import org.springblade.desk.quality.pojo.vo.LiquidTankTaskCopyVO; |
||||||
|
|
||||||
|
import java.io.OutputStream; |
||||||
|
import java.net.URLEncoder; |
||||||
|
import java.time.format.DateTimeFormatter; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
public class FullMergeExcelUtil { |
||||||
|
|
||||||
|
/** |
||||||
|
* 导出带合并标头+数据合并的Excel(User实体类版本) |
||||||
|
* @param response 响应对象 |
||||||
|
* @param fileName 导出文件名 |
||||||
|
* @param mergeTitle 合并标头的大标题 |
||||||
|
* @param columnTitles 列标题数组(["部门","用户ID","用户名","年龄","手机号"]) |
||||||
|
* @param userList 用户实体类列表 |
||||||
|
* @param mergeColumnIndexes 需要合并的列索引(如[0]表示合并部门列) |
||||||
|
* @throws Exception 异常 |
||||||
|
*/ |
||||||
|
public static void exportUserExcel(HttpServletResponse response, String fileName, |
||||||
|
String mergeTitle, String[] columnTitles, |
||||||
|
List<LiquidTankTaskCopyVO> userList, int[] mergeColumnIndexes) throws Exception { |
||||||
|
// 1. 创建工作簿和工作表
|
||||||
|
Workbook workbook = new XSSFWorkbook(); |
||||||
|
Sheet sheet = workbook.createSheet("数据报表"); |
||||||
|
// 设置列宽
|
||||||
|
for (int i = 0; i < columnTitles.length; i++) { |
||||||
|
sheet.autoSizeColumn(i); |
||||||
|
sheet.setColumnWidth(i, Math.max(sheet.getColumnWidth(i), 2000)); |
||||||
|
} |
||||||
|
|
||||||
|
// -------------------------- 第一步:创建合并标头 --------------------------
|
||||||
|
Row mergeTitleRow = sheet.createRow(0); |
||||||
|
mergeTitleRow.setHeightInPoints(30); |
||||||
|
Cell mergeTitleCell = mergeTitleRow.createCell(0); |
||||||
|
mergeTitleCell.setCellValue(mergeTitle); |
||||||
|
CellStyle mergeTitleStyle = createStyle(workbook, true, (short) 16, HorizontalAlignment.CENTER); |
||||||
|
mergeTitleCell.setCellStyle(mergeTitleStyle); |
||||||
|
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, columnTitles.length - 1)); |
||||||
|
|
||||||
|
// -------------------------- 第二步:创建列标题 --------------------------
|
||||||
|
Row columnTitleRow = sheet.createRow(1); |
||||||
|
columnTitleRow.setHeightInPoints(20); |
||||||
|
CellStyle columnTitleStyle = createStyle(workbook, true, (short) 12, HorizontalAlignment.CENTER); |
||||||
|
for (int i = 0; i < columnTitles.length; i++) { |
||||||
|
Cell cell = columnTitleRow.createCell(i); |
||||||
|
cell.setCellValue(columnTitles[i]); |
||||||
|
cell.setCellStyle(columnTitleStyle); |
||||||
|
sheet.autoSizeColumn(i); |
||||||
|
} |
||||||
|
|
||||||
|
// -------------------------- 第三步:填充User实体数据 --------------------------
|
||||||
|
int dataStartRow = 2; |
||||||
|
CellStyle dataStyle = createStyle(workbook, false, (short) 12, HorizontalAlignment.CENTER); |
||||||
|
for (int i = 0; i < userList.size(); i++) { |
||||||
|
Row dataRow = sheet.createRow(dataStartRow + i); |
||||||
|
dataRow.setHeightInPoints(20); |
||||||
|
LiquidTankTaskCopyVO user = userList.get(i); |
||||||
|
|
||||||
|
// 手动映射:列索引 → User字段(与columnTitles一一对应)
|
||||||
|
// 取样时间
|
||||||
|
int a = 0; |
||||||
|
Cell cell0 = dataRow.createCell(a); |
||||||
|
cell0.setCellStyle(dataStyle); |
||||||
|
cell0.setCellValue(user.getSampleDate()); |
||||||
|
|
||||||
|
//温度
|
||||||
|
int b = 1; |
||||||
|
Cell cell1 = dataRow.createCell(b); |
||||||
|
cell1.setCellStyle(dataStyle); |
||||||
|
cell1.setCellValue(user.getTemperatureActual()); |
||||||
|
|
||||||
|
//槽号
|
||||||
|
int c = 2; |
||||||
|
Cell cell2 = dataRow.createCell(c); |
||||||
|
// cell2.setCellStyle(dataStyle);
|
||||||
|
cell2.setCellValue(user.getWorkTankName() == null ? "" : user.getWorkTankName()); |
||||||
|
|
||||||
|
//体积(L)
|
||||||
|
int d = 3; |
||||||
|
Cell cell3 = dataRow.createCell(d); |
||||||
|
// cell3.setCellStyle(dataStyle);
|
||||||
|
cell3.setCellValue(user.getVolume() == null ? 0 : user.getVolume()); |
||||||
|
|
||||||
|
//分析项目
|
||||||
|
Cell cell4 = dataRow.createCell(4); |
||||||
|
cell4.setCellStyle(dataStyle); |
||||||
|
cell4.setCellValue(user.getTestElement() == null ? "" : user.getTestElement()); |
||||||
|
|
||||||
|
//规范下限(g/L)
|
||||||
|
Cell cell5 = dataRow.createCell(5); |
||||||
|
cell5.setCellStyle(dataStyle); |
||||||
|
cell5.setCellValue(user.getNormValueMin() == null ? 0 : user.getNormValueMin()); |
||||||
|
|
||||||
|
//目标下限(g/L)
|
||||||
|
Cell cell6 = dataRow.createCell(6); |
||||||
|
cell6.setCellStyle(dataStyle); |
||||||
|
cell6.setCellValue(user.getTargetValueMin() == null ? 0 : user.getTargetValueMin()); |
||||||
|
|
||||||
|
//添加点(g/L)
|
||||||
|
Cell cell7 = dataRow.createCell(7); |
||||||
|
cell7.setCellStyle(dataStyle); |
||||||
|
cell7.setCellValue(user.getFillingLocation() == null ? 0 : user.getFillingLocation()); |
||||||
|
|
||||||
|
//目标值(g/L)
|
||||||
|
Cell cell8 = dataRow.createCell(8); |
||||||
|
cell8.setCellStyle(dataStyle); |
||||||
|
cell8.setCellValue(user.getTargetValue() == null ? 0 : user.getTargetValue()); |
||||||
|
|
||||||
|
//目标上限(g/L)
|
||||||
|
Cell cell9 = dataRow.createCell(9); |
||||||
|
cell9.setCellStyle(dataStyle); |
||||||
|
cell9.setCellValue(user.getTargetValueMax() == null ? 0 : user.getTargetValueMax()); |
||||||
|
|
||||||
|
//规范上限(g/L)
|
||||||
|
Cell cell10 = dataRow.createCell(10); |
||||||
|
cell10.setCellStyle(dataStyle); |
||||||
|
cell10.setCellValue(user.getNormValueMax() == null ? 0 : user.getNormValueMax()); |
||||||
|
|
||||||
|
//化验频率
|
||||||
|
Cell cell11 = dataRow.createCell(11); |
||||||
|
cell11.setCellStyle(dataStyle); |
||||||
|
cell11.setCellValue(user.getTestFrequency() == null ? "" : user.getTestFrequency()); |
||||||
|
|
||||||
|
//化验值(g/L)
|
||||||
|
Cell cell12 = dataRow.createCell(12); |
||||||
|
cell12.setCellStyle(dataStyle); |
||||||
|
cell12.setCellValue(user.getFirstTestValue() == null ? 0 : user.getFirstTestValue()); |
||||||
|
|
||||||
|
//药品计算公式
|
||||||
|
Cell cell13 = dataRow.createCell(13); |
||||||
|
cell13.setCellStyle(dataStyle); |
||||||
|
cell13.setCellValue(user.getReportFormulaContent() == null ? "" : user.getReportFormulaContent()); |
||||||
|
|
||||||
|
//药品添加量
|
||||||
|
Cell cell14 = dataRow.createCell(14); |
||||||
|
cell14.setCellStyle(dataStyle); |
||||||
|
cell14.setCellValue(user.getActualAddValue() == null ? 0 : user.getActualAddValue()); |
||||||
|
|
||||||
|
//药品批次号
|
||||||
|
Cell cell15 = dataRow.createCell(15); |
||||||
|
cell15.setCellStyle(dataStyle); |
||||||
|
cell15.setCellValue(user.getDrugBatCode() == null ? "" : user.getDrugBatCode()); |
||||||
|
|
||||||
|
//药品监督员签名
|
||||||
|
Cell cell16 = dataRow.createCell(16); |
||||||
|
cell16.setCellStyle(dataStyle); |
||||||
|
cell16.setCellValue(user.getDrugSuUserSign()== null ? "" : user.getDrugSuUserSign()); |
||||||
|
|
||||||
|
//药品添加人签名
|
||||||
|
Cell cell17 = dataRow.createCell(17); |
||||||
|
cell17.setCellStyle(dataStyle); |
||||||
|
cell17.setCellValue(user.getDrugAddUserSign()== null ? "" : user.getDrugAddUserSign()); |
||||||
|
|
||||||
|
//调整后化验值(g/L)
|
||||||
|
Cell cell18 = dataRow.createCell(18); |
||||||
|
cell18.setCellStyle(dataStyle); |
||||||
|
cell18.setCellValue(user.getRepeatTestValue()== null ? 0 : user.getRepeatTestValue()); |
||||||
|
|
||||||
|
//1\合格 2、不合格
|
||||||
|
Cell cell19 = dataRow.createCell(19); |
||||||
|
cell19.setCellStyle(dataStyle); |
||||||
|
cell19.setCellValue(user.getQualified()== null ? 0 : user.getQualified()); |
||||||
|
|
||||||
|
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); |
||||||
|
//有效期
|
||||||
|
Cell cell20 = dataRow.createCell(20); |
||||||
|
cell20.setCellStyle(dataStyle); |
||||||
|
cell20.setCellValue(user.getValidDate().format(formatter)); |
||||||
|
|
||||||
|
//超出规范极限调整后的结果
|
||||||
|
Cell cell21 = dataRow.createCell(21); |
||||||
|
cell21.setCellStyle(dataStyle); |
||||||
|
cell21.setCellValue(user.getOutRangeResult()==null ? "" : user.getOutRangeResult()); |
||||||
|
|
||||||
|
//槽液清理记录
|
||||||
|
Cell cell22 = dataRow.createCell(22); |
||||||
|
cell22.setCellStyle(dataStyle); |
||||||
|
cell22.setCellValue(user.getClearRecord()==null ? "" : user.getClearRecord()); |
||||||
|
|
||||||
|
//控制规范
|
||||||
|
Cell cell23 = dataRow.createCell(23); |
||||||
|
cell23.setCellStyle(dataStyle); |
||||||
|
cell23.setCellValue(user.getNorm()==null ? "" : user.getNorm()); |
||||||
|
} |
||||||
|
|
||||||
|
// -------------------------- 第四步:合并数据列相同值 --------------------------
|
||||||
|
// if (mergeColumnIndexes != null && mergeColumnIndexes.length > 0 && userList.size() > 0) {
|
||||||
|
// for (int colIndex : mergeColumnIndexes) {
|
||||||
|
// if (colIndex < 0 || colIndex >= columnTitles.length) {
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
// mergeSameValueCells(sheet, dataStartRow, dataStartRow + userList.size() - 1, colIndex);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// -------------------------- 第五步:响应头和文件输出 --------------------------
|
||||||
|
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); |
||||||
|
response.setCharacterEncoding("UTF-8"); |
||||||
|
String encodeFileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20"); |
||||||
|
response.setHeader("Content-Disposition", "attachment;filename*=UTF-8''" + encodeFileName + ".xlsx"); |
||||||
|
|
||||||
|
OutputStream outputStream = response.getOutputStream(); |
||||||
|
workbook.write(outputStream); |
||||||
|
outputStream.flush(); |
||||||
|
outputStream.close(); |
||||||
|
workbook.close(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 合并指定列连续相同值的单元格(逻辑不变) |
||||||
|
*/ |
||||||
|
private static void mergeSameValueCells(Sheet sheet, int startRow, int endRow, int colIndex) { |
||||||
|
int mergeStartRow = startRow; |
||||||
|
for (int rowIndex = startRow + 1; rowIndex <= endRow; rowIndex++) { |
||||||
|
String currentValue = getCellValue(sheet.getRow(rowIndex).getCell(colIndex)); |
||||||
|
String prevValue = getCellValue(sheet.getRow(rowIndex - 1).getCell(colIndex)); |
||||||
|
|
||||||
|
if (!currentValue.equals(prevValue)) { |
||||||
|
if (mergeStartRow < rowIndex - 1) { |
||||||
|
sheet.addMergedRegion(new CellRangeAddress(mergeStartRow, rowIndex - 1, colIndex, colIndex)); |
||||||
|
} |
||||||
|
mergeStartRow = rowIndex; |
||||||
|
} |
||||||
|
|
||||||
|
if (rowIndex == endRow && mergeStartRow <= endRow) { |
||||||
|
sheet.addMergedRegion(new CellRangeAddress(mergeStartRow, endRow, colIndex, colIndex)); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 获取单元格字符串值(逻辑不变) |
||||||
|
*/ |
||||||
|
private static String getCellValue(Cell cell) { |
||||||
|
if (cell == null) { |
||||||
|
return ""; |
||||||
|
} |
||||||
|
switch (cell.getCellType()) { |
||||||
|
case STRING: |
||||||
|
return cell.getStringCellValue().trim(); |
||||||
|
case NUMERIC: |
||||||
|
return String.valueOf(cell.getNumericCellValue()).trim(); |
||||||
|
case BOOLEAN: |
||||||
|
return String.valueOf(cell.getBooleanCellValue()).trim(); |
||||||
|
case FORMULA: |
||||||
|
return cell.getCellFormula().trim(); |
||||||
|
default: |
||||||
|
return ""; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* 创建样式(逻辑不变) |
||||||
|
*/ |
||||||
|
private static CellStyle createStyle(Workbook workbook, boolean isBold, short fontSize, HorizontalAlignment alignment) { |
||||||
|
CellStyle style = workbook.createCellStyle(); |
||||||
|
Font font = workbook.createFont(); |
||||||
|
font.setBold(isBold); |
||||||
|
font.setFontName("宋体"); |
||||||
|
font.setFontHeightInPoints(fontSize); |
||||||
|
style.setFont(font); |
||||||
|
style.setAlignment(alignment); |
||||||
|
style.setVerticalAlignment(VerticalAlignment.CENTER); |
||||||
|
style.setBorderTop(BorderStyle.THIN); |
||||||
|
style.setBorderBottom(BorderStyle.THIN); |
||||||
|
style.setBorderLeft(BorderStyle.THIN); |
||||||
|
style.setBorderRight(BorderStyle.THIN); |
||||||
|
return style; |
||||||
|
} |
||||||
|
} |
||||||
Binary file not shown.
Loading…
Reference in new issue