|
|
|
@ -0,0 +1,233 @@
|
|
|
|
|
package com.ruoyi.common.utils.poi;
|
|
|
|
|
|
|
|
|
|
import com.ruoyi.common.annotation.Excel;
|
|
|
|
|
import com.ruoyi.common.utils.StringUtils;
|
|
|
|
|
import org.apache.poi.ss.usermodel.*;
|
|
|
|
|
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
|
|
|
|
|
|
|
|
|
|
import javax.servlet.http.HttpServletResponse;
|
|
|
|
|
import java.io.IOException;
|
|
|
|
|
import java.io.UnsupportedEncodingException;
|
|
|
|
|
import java.lang.reflect.Field;
|
|
|
|
|
import java.net.URLEncoder;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.util.Comparator;
|
|
|
|
|
import java.util.Date;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* ClassName: MultiSheetExcelUtil
|
|
|
|
|
* Package: com.ruoyi.common.utils.poi
|
|
|
|
|
* Description:
|
|
|
|
|
* @Author wangxy
|
|
|
|
|
* @Create 2025/4/23 14:24
|
|
|
|
|
* @Version 1.0
|
|
|
|
|
*/public class MultiSheetExcelUtil {
|
|
|
|
|
|
|
|
|
|
private Workbook workbook;
|
|
|
|
|
private HttpServletResponse response;
|
|
|
|
|
private String fileName;
|
|
|
|
|
|
|
|
|
|
public MultiSheetExcelUtil() {
|
|
|
|
|
this.workbook = new SXSSFWorkbook();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void initExport(HttpServletResponse response, String fileName) {
|
|
|
|
|
this.response = response;
|
|
|
|
|
this.fileName = fileName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public <T> void createSheet(List<T> list, String sheetName, Class<T> clazz) {
|
|
|
|
|
Sheet sheet = workbook.createSheet(sheetName);
|
|
|
|
|
List<FieldMeta> fields = getFields(clazz); // 使用新版getFields
|
|
|
|
|
createTitleRow(sheet, fields);
|
|
|
|
|
fillDataRows(sheet, list, fields);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void finishExport() throws IOException {
|
|
|
|
|
try {
|
|
|
|
|
String encodedFileName = encodeFilename(fileName);
|
|
|
|
|
response.setHeader("Content-Disposition", "attachment;filename*=UTF-8''" + encodedFileName);
|
|
|
|
|
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8");
|
|
|
|
|
workbook.write(response.getOutputStream());
|
|
|
|
|
} finally {
|
|
|
|
|
if (workbook != null) {
|
|
|
|
|
((SXSSFWorkbook) workbook).dispose();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 新增的编码方法
|
|
|
|
|
private String encodeFilename(String filename) {
|
|
|
|
|
try {
|
|
|
|
|
return URLEncoder.encode(filename, "UTF-8").replace("+", "%20");
|
|
|
|
|
} catch (UnsupportedEncodingException e) {
|
|
|
|
|
return filename;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private static class FieldMeta {
|
|
|
|
|
private final Field field; // 反射字段对象
|
|
|
|
|
private final String title; // 表头名称(@Excel.name)
|
|
|
|
|
private final String dateFormat; // 日期格式(@Excel.dateFormat)
|
|
|
|
|
private final int sort; // 排序(@Excel.sort)
|
|
|
|
|
|
|
|
|
|
public FieldMeta(Field field, String title, String dateFormat, int sort) {
|
|
|
|
|
this.field = field;
|
|
|
|
|
this.title = title;
|
|
|
|
|
this.dateFormat = dateFormat;
|
|
|
|
|
this.sort = sort;
|
|
|
|
|
this.field.setAccessible(true); // 提前设置可访问
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Getter 方法
|
|
|
|
|
public Field getField() { return field; }
|
|
|
|
|
public String getTitle() { return title; }
|
|
|
|
|
public String getDateFormat() { return dateFormat; }
|
|
|
|
|
public int getSort() { return sort; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取所有带@Excel注解的字段(包含父类)
|
|
|
|
|
*/
|
|
|
|
|
private static List<FieldMeta> getFields(Class<?> clazz) {
|
|
|
|
|
List<FieldMeta> fields = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
// 递归获取所有字段(包括父类)
|
|
|
|
|
Class<?> currentClass = clazz;
|
|
|
|
|
while (currentClass != null) {
|
|
|
|
|
for (Field field : currentClass.getDeclaredFields()) {
|
|
|
|
|
Excel excelAnno = field.getAnnotation(Excel.class);
|
|
|
|
|
if (excelAnno != null) {
|
|
|
|
|
fields.add(new FieldMeta(
|
|
|
|
|
field,
|
|
|
|
|
excelAnno.name(),
|
|
|
|
|
excelAnno.dateFormat(),
|
|
|
|
|
excelAnno.sort()
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
currentClass = currentClass.getSuperclass();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 按sort排序
|
|
|
|
|
fields.sort(Comparator.comparingInt(FieldMeta::getSort));
|
|
|
|
|
return fields;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void setCellValue(Cell cell, Object value, FieldMeta meta) {
|
|
|
|
|
if (value == null) {
|
|
|
|
|
cell.setCellValue("");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// 处理日期类型
|
|
|
|
|
if (value instanceof Date) {
|
|
|
|
|
String format = StringUtils.isNotEmpty(meta.getDateFormat())
|
|
|
|
|
? meta.getDateFormat()
|
|
|
|
|
: "yyyy-MM-dd"; // 默认格式
|
|
|
|
|
|
|
|
|
|
CellStyle style = workbook.createCellStyle();
|
|
|
|
|
style.setDataFormat(
|
|
|
|
|
workbook.getCreationHelper()
|
|
|
|
|
.createDataFormat()
|
|
|
|
|
.getFormat(format)
|
|
|
|
|
);
|
|
|
|
|
cell.setCellValue((Date) value);
|
|
|
|
|
cell.setCellStyle(style);
|
|
|
|
|
}
|
|
|
|
|
// 处理数字类型
|
|
|
|
|
else if (value instanceof Number) {
|
|
|
|
|
cell.setCellValue(((Number) value).doubleValue());
|
|
|
|
|
}
|
|
|
|
|
// 其他类型
|
|
|
|
|
else {
|
|
|
|
|
cell.setCellValue(value.toString());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 创建带样式的表头
|
|
|
|
|
*/
|
|
|
|
|
private CellStyle createHeaderStyle(Workbook workbook) {
|
|
|
|
|
CellStyle style = workbook.createCellStyle();
|
|
|
|
|
// 背景色
|
|
|
|
|
style.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
|
|
|
|
|
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
|
|
|
|
|
// 边框
|
|
|
|
|
style.setBorderTop(BorderStyle.THIN);
|
|
|
|
|
style.setBorderBottom(BorderStyle.THIN);
|
|
|
|
|
style.setBorderLeft(BorderStyle.THIN);
|
|
|
|
|
style.setBorderRight(BorderStyle.THIN);
|
|
|
|
|
// 字体
|
|
|
|
|
Font font = workbook.createFont();
|
|
|
|
|
font.setColor(IndexedColors.BLACK.getIndex());
|
|
|
|
|
font.setFontName("Arial");
|
|
|
|
|
font.setFontHeightInPoints((short) 12);
|
|
|
|
|
font.setBold(true);
|
|
|
|
|
style.setFont(font);
|
|
|
|
|
// 对齐方式
|
|
|
|
|
style.setAlignment(HorizontalAlignment.CENTER);
|
|
|
|
|
style.setVerticalAlignment(VerticalAlignment.CENTER);
|
|
|
|
|
|
|
|
|
|
return style;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 创建数据行的样式(可选)
|
|
|
|
|
*/
|
|
|
|
|
private CellStyle createDataStyle(Workbook workbook) {
|
|
|
|
|
CellStyle style = workbook.createCellStyle();
|
|
|
|
|
// 设置边框
|
|
|
|
|
style.setVerticalAlignment(VerticalAlignment.CENTER);
|
|
|
|
|
style.setBorderRight(BorderStyle.THIN);
|
|
|
|
|
style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
|
|
|
|
|
style.setBorderLeft(BorderStyle.THIN);
|
|
|
|
|
style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
|
|
|
|
|
style.setBorderTop(BorderStyle.THIN);
|
|
|
|
|
style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
|
|
|
|
|
style.setBorderBottom(BorderStyle.THIN);
|
|
|
|
|
style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
|
|
|
|
|
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
|
|
|
|
|
// 设置自动换行(可选)
|
|
|
|
|
//style.setWrapText(true);
|
|
|
|
|
return style;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void createTitleRow(Sheet sheet, List<FieldMeta> fields) {
|
|
|
|
|
Row headerRow = sheet.createRow(0);
|
|
|
|
|
CellStyle headerStyle = createHeaderStyle(workbook);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < fields.size(); i++) {
|
|
|
|
|
Cell cell = headerRow.createCell(i);
|
|
|
|
|
cell.setCellValue(fields.get(i).getTitle());
|
|
|
|
|
cell.setCellStyle(headerStyle);
|
|
|
|
|
sheet.setColumnWidth(i, 6000); // 默认列宽
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private <T> void fillDataRows(Sheet sheet, List<T> list, List<FieldMeta> fields) {
|
|
|
|
|
CellStyle dataStyle = createDataStyle(workbook);
|
|
|
|
|
int rowNum = 1;
|
|
|
|
|
for (T item : list) {
|
|
|
|
|
Row row = sheet.createRow(rowNum++);
|
|
|
|
|
for (int i = 0; i < fields.size(); i++) {
|
|
|
|
|
FieldMeta meta = fields.get(i);
|
|
|
|
|
Cell cell = row.createCell(i);
|
|
|
|
|
try {
|
|
|
|
|
// 直接通过缓存的Field对象获取值
|
|
|
|
|
Object value = meta.getField().get(item);
|
|
|
|
|
cell.setCellStyle(dataStyle);
|
|
|
|
|
setCellValue(cell, value, meta); // 传递FieldMeta对象
|
|
|
|
|
} catch (IllegalAccessException e) {
|
|
|
|
|
cell.setCellValue("N/A");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|