package com.ruoyi.business.utils;
|
|
import com.ruoyi.business.vo.OfficialInventoryExportVo;
|
import com.ruoyi.common.annotation.Excel;
|
import com.ruoyi.common.utils.StringUtils;
|
import com.ruoyi.common.utils.poi.ExcelUtil;
|
import jakarta.servlet.http.HttpServletResponse;
|
import lombok.extern.slf4j.Slf4j;
|
import org.apache.poi.ss.usermodel.*;
|
import org.apache.poi.ss.util.CellRangeAddress;
|
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
|
|
import java.lang.reflect.Field;
|
import java.net.URLEncoder;
|
import java.util.ArrayList;
|
import java.util.Arrays;
|
import java.util.List;
|
|
@Slf4j
|
public class DynamicExcelUtil<T> extends ExcelUtil<T> {
|
private final List<String> dynamicHeaders;
|
private final List<Field> fields;
|
|
public DynamicExcelUtil(Class<T> clazz, List<String> dynamicHeaders) {
|
super(clazz);
|
this.dynamicHeaders = dynamicHeaders;
|
this.fields = getAllFields(clazz);
|
}
|
|
private List<Field> getAllFields(Class<?> clazz) {
|
List<Field> fields = new ArrayList<>();
|
Class<?> currentClass = clazz;
|
while (currentClass != null) {
|
fields.addAll(Arrays.asList(currentClass.getDeclaredFields()));
|
currentClass = currentClass.getSuperclass();
|
}
|
return fields;
|
}
|
|
@Override
|
public void exportExcel(HttpServletResponse response, List<T> list, String sheetName) {
|
try {
|
String validSheetName = StringUtils.isBlank(sheetName) ? "Sheet1" : sheetName;
|
|
// Create workbook directly instead of using parent's createWorkbook
|
Workbook workbook = new SXSSFWorkbook();
|
Sheet sheet = workbook.createSheet(validSheetName);
|
|
// Create styles
|
createCustomStyles(workbook);
|
|
// Create dynamic header
|
createDynamicHeader(sheet, workbook);
|
|
// Fill data
|
fillDynamicData(sheet, list);
|
|
// Set response
|
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(validSheetName + ".xlsx", "UTF-8"));
|
|
workbook.write(response.getOutputStream());
|
} catch (Exception e) {
|
log.error("导出Excel失败", e);
|
throw new RuntimeException("导出Excel失败: " + e.getMessage(), e);
|
}
|
}
|
|
private void createCustomStyles(Workbook workbook) {
|
// Create basic styles similar to parent class if needed
|
CellStyle headerStyle = workbook.createCellStyle();
|
Font headerFont = workbook.createFont();
|
headerFont.setBold(true);
|
headerStyle.setFont(headerFont);
|
headerStyle.setAlignment(HorizontalAlignment.CENTER);
|
headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);
|
}
|
|
/**
|
* 创建动态表头(父表头"煤质" + 子表头)
|
*/
|
private void createDynamicHeader(Sheet sheet, Workbook workbook) {
|
// 主表头(第1行)和子表头(第2行)
|
Row mainHeaderRow = sheet.createRow(0);
|
Row subHeaderRow = sheet.createRow(1);
|
|
// 创建表头样式(居中 + 加粗)
|
CellStyle headerStyle = workbook.createCellStyle();
|
Font headerFont = workbook.createFont();
|
headerFont.setBold(true);
|
headerStyle.setFont(headerFont);
|
headerStyle.setAlignment(HorizontalAlignment.CENTER);
|
headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);
|
|
int colIndex = 0;
|
|
// 遍历所有字段(固定字段 + 动态字段)
|
for (Field field : fields) {
|
Excel excel = field.getAnnotation(Excel.class);
|
if (excel != null) {
|
// 1. 如果是 "煤质" 字段,跳过(后面单独处理)
|
if (excel.name().equals("煤质")) {
|
continue;
|
}
|
|
// 2. 处理普通字段(非 "煤质" 的列)
|
// 2.1 在主表头(第1行)设置字段名
|
Cell mainHeaderCell = mainHeaderRow.createCell(colIndex);
|
mainHeaderCell.setCellValue(excel.name());
|
mainHeaderCell.setCellStyle(headerStyle);
|
|
// 2.2 在子表头(第2行)留空,但合并单元格使其占据两行高度
|
Cell subHeaderCell = subHeaderRow.createCell(colIndex);
|
subHeaderCell.setCellStyle(headerStyle); // 保持样式一致
|
|
// 2.3 合并当前列的第1行和第2行(垂直合并)
|
sheet.addMergedRegion(new CellRangeAddress(
|
0, 1, // 从第1行到第2行
|
colIndex, colIndex // 当前列
|
));
|
|
colIndex++; // 移动到下一列
|
}
|
}
|
|
// 3. 处理动态字段("煤质" 下的子表头)
|
if (!dynamicHeaders.isEmpty()) {
|
// 3.1 在主表头(第1行)设置 "煤质" 大标题(横向合并)
|
Cell coalHeaderCell = mainHeaderRow.createCell(colIndex);
|
coalHeaderCell.setCellValue("煤质");
|
coalHeaderCell.setCellStyle(headerStyle);
|
|
// 横向合并(第1行,跨越多列)
|
sheet.addMergedRegion(new CellRangeAddress(
|
0, 0, // 仅第1行
|
colIndex, colIndex + dynamicHeaders.size() - 1 // 跨越多列
|
));
|
|
// 3.2 在子表头(第2行)设置动态字段(如 "灰分"、"硫分")
|
for (int i = 0; i < dynamicHeaders.size(); i++) {
|
Cell subCell = subHeaderRow.createCell(colIndex + i);
|
subCell.setCellValue(dynamicHeaders.get(i));
|
subCell.setCellStyle(headerStyle);
|
}
|
}
|
}
|
|
/**
|
* 填充数据(固定字段 + 动态字段)
|
*/
|
private void fillDynamicData(Sheet sheet, List<T> list) {
|
int rowIndex = 2; // Data starts from row 3
|
CellStyle dataStyle = sheet.getWorkbook().createCellStyle();
|
dataStyle.setAlignment(HorizontalAlignment.CENTER);
|
|
for (T item : list) {
|
Row row = sheet.createRow(rowIndex++);
|
int colIndex = 0;
|
|
// Fill fixed fields
|
for (Field field : fields) {
|
Excel excel = field.getAnnotation(Excel.class);
|
if (excel != null && !excel.name().equals("煤质")) {
|
try {
|
field.setAccessible(true);
|
Object value = field.get(item);
|
Cell cell = row.createCell(colIndex++);
|
cell.setCellStyle(dataStyle);
|
setCellValue(cell, value);
|
} catch (Exception e) {
|
log.error("填充固定字段失败", e);
|
row.createCell(colIndex++).setCellValue("-");
|
}
|
}
|
}
|
|
// Fill dynamic fields
|
if (item instanceof OfficialInventoryExportVo vo) {
|
for (String header : dynamicHeaders) {
|
Cell cell = row.createCell(colIndex++);
|
cell.setCellStyle(dataStyle);
|
cell.setCellValue(vo.getDynamicProperty(header));
|
}
|
}
|
}
|
}
|
|
/**
|
* 设置单元格值
|
*/
|
private void setCellValue(Cell cell, Object value) {
|
if (value == null) {
|
cell.setCellValue("-");
|
} else if (value instanceof Number) {
|
cell.setCellValue(((Number) value).doubleValue());
|
} else {
|
cell.setCellValue(value.toString());
|
}
|
}
|
}
|