大图合并、进度计划导出

This commit is contained in:
lcj
2025-11-17 09:08:58 +08:00
parent 57b662837d
commit b501965d25
20 changed files with 855 additions and 61 deletions

View File

@ -0,0 +1,40 @@
package org.dromara.common.excel.handler;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Workbook;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
public class LockColumnHandler implements CellWriteHandler {
private final Set<Integer> lockColumns;
public LockColumnHandler(Collection<Integer> lockColumns) {
this.lockColumns = new HashSet<>(lockColumns);
}
@Override
public void afterCellDispose(CellWriteHandlerContext context) {
Cell cell = context.getCell();
if (cell == null) return;
Workbook workbook = cell.getSheet().getWorkbook();
// 必须为所有单元格创建一个新的 CellStyle
CellStyle style = workbook.createCellStyle();
// 锁定指定列
if (lockColumns.contains(cell.getColumnIndex())) {
style.setLocked(true);
} else {
style.setLocked(false); // 非锁定列必须明确设置为 false
}
cell.setCellStyle(style);
}
}

View File

@ -0,0 +1,26 @@
package org.dromara.common.excel.handler;
import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import org.apache.poi.ss.usermodel.Sheet;
/**
* @author lilemy
* @date 2025-11-14 15:05
*/
public class SheetProtectHandler implements SheetWriteHandler {
private final String password;
public SheetProtectHandler(String password) {
this.password = password;
}
@Override
public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder,
WriteSheetHolder writeSheetHolder) {
Sheet sheet = writeSheetHolder.getSheet();
sheet.protectSheet(password); // 可为空字符串
}
}

View File

@ -4,7 +4,10 @@ import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.resource.ClassPathResource;
import cn.hutool.core.util.IdUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.read.builder.ExcelReaderBuilder;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
@ -14,26 +17,27 @@ import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.file.FileUtils;
import org.dromara.common.excel.convert.ExcelBigNumberConvert;
import org.dromara.common.excel.core.*;
import org.dromara.common.excel.handler.DataWriteHandler;
import org.dromara.common.excel.handler.LockColumnHandler;
import org.dromara.common.excel.handler.SheetProtectHandler;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
* Excel相关处理
*
* @author Lion Li
*/
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class ExcelUtil {
@ -47,6 +51,41 @@ public class ExcelUtil {
return EasyExcel.read(is).head(clazz).autoCloseStream(false).sheet().doReadSync();
}
/**
* 同步读取 Excel支持读取多个 Sheet
*
* @param is 输入流(一次性读取)
* @param clazz Excel 映射实体类
* @return 所有 Sheet 的数据汇总
*/
public static <T> List<T> importExcelAllSheet(InputStream is, Class<T> clazz) {
// 用于存放所有 sheet 的数据
List<T> allData = new ArrayList<>();
// 1. 构建 ExcelReader
ExcelReaderBuilder readerBuilder = EasyExcel.read(is);
ExcelReader reader = readerBuilder.build();
// 2. 获取 Excel 中全部 Sheet 信息(包含 SheetNo、页名等
List<ReadSheet> readSheets = reader.excelExecutor().sheetList();
// 3. 遍历每一个 Sheet
for (ReadSheet sheet : readSheets) {
// 为每个 Sheet 创建独立监听器,用于接收读取结果
DefaultExcelListener<T> listener = new DefaultExcelListener<>(false);
// 4. 构建当前 Sheet 的读取器 使用 sheet.getSheetNo() 指定当前 sheet
ReadSheet readSheet = EasyExcel.readSheet(sheet.getSheetNo())
// 设置头映射实体类
.head(clazz)
// 注册读取监听器
.registerReadListener(listener)
.build();
// 5. 开始读取当前 Sheet
reader.read(readSheet);
// 6. 收集当前 Sheet 读取的数据
allData.addAll(listener.getExcelResult().getList());
}
// 7. 关闭读取器,释放资源
reader.finish();
return allData;
}
/**
* 使用校验监听器 异步导入 同步返回
@ -204,6 +243,37 @@ public class ExcelUtil {
builder.doWrite(list);
}
/**
* 导出excel
*
* @param list 导出数据集合
* @param sheetName 工作表的名称
* @param clazz 实体类
* @param lockColumns 锁定列
* @param merge 是否合并单元格
* @param os 输出流
*/
public static <T> void exportExcel(List<T> list, String sheetName, Class<T> clazz, List<Integer> lockColumns,
boolean merge, OutputStream os, List<DropDownOptions> options) {
ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz)
.registerWriteHandler(new LockColumnHandler(lockColumns)) // 锁定第3列
.registerWriteHandler(new SheetProtectHandler("dawdawdwad")) // 保护整张 sheet
.autoCloseStream(false)
// 自动适配
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
// 大数值自动转换 防止失真
.registerConverter(new ExcelBigNumberConvert())
.registerWriteHandler(new DataWriteHandler(clazz))
.sheet(sheetName);
if (merge) {
// 合并处理器
builder.registerWriteHandler(new CellMergeStrategy(list, true));
}
// 添加下拉框操作
builder.registerWriteHandler(new ExcelDownHandler(options));
builder.doWrite(list);
}
/**
* 导出多sheet excel增强版解决XML安全问题
*
@ -212,7 +282,9 @@ public class ExcelUtil {
* @param clazz 实体类
* @param optionsList 级联下拉选内容列表
*/
public static <T> void exportMultiSheetExcelEnhanced(List<List<T>> sheetData, List<String> sheetNames, Class<T> clazz, List<List<DropDownOptions>> optionsList, HttpServletResponse response) throws IOException {
public static <T> void exportMultiSheetExcelEnhanced(List<List<T>> sheetData, List<String> sheetNames,
Class<T> clazz, List<List<DropDownOptions>> optionsList,
HttpServletResponse response) throws IOException {
resetResponse("file", response);
ExcelWriter excelWriter = null;
ServletOutputStream os = response.getOutputStream();
@ -254,12 +326,71 @@ public class ExcelUtil {
excelWriter.finish();
} catch (Exception e) {
// 记录日志但不中断主流程
e.printStackTrace();
log.error("Excel 导出错误", e);
}
}
}
}
/**
* 导出多sheet excel增强版解决XML安全问题
*
* @param sheetData 多个sheet的数据
* @param sheetNames 多个sheet的名称
* @param clazz 实体类
* @param optionsList 级联下拉选内容列表
*/
public static <T> void exportExcel(List<List<T>> sheetData, List<String> sheetNames, List<Integer> lockColumns,
Class<T> clazz, List<List<DropDownOptions>> optionsList,
HttpServletResponse response) throws IOException {
resetResponse("file", response);
ExcelWriter excelWriter = null;
ServletOutputStream os = response.getOutputStream();
try {
// 使用SXSSFWorkbook避免内存问题并减少XML处理复杂度
excelWriter = EasyExcel.write(os)
.head(clazz)
.registerWriteHandler(new LockColumnHandler(lockColumns)) // 锁定第3列
// .registerWriteHandler(new SheetProtectHandler("dawdawdwad")) // 保护整张 sheet
.autoCloseStream(false)
.registerConverter(new ExcelBigNumberConvert())
// 自动适配
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.registerWriteHandler(new DataWriteHandler(clazz))
.build();
// 为每个sheet写入数据
for (int i = 0; i < sheetData.size(); i++) {
// 创建基本sheet配置
WriteSheet writeSheet = EasyExcel.writerSheet(i, sheetNames.get(i))
.head(clazz)
.build();
// 添加下拉选项(如果存在)
if (optionsList != null && optionsList.size() > i && optionsList.get(i) != null) {
ExcelDownHandler handler = new ExcelDownHandler(optionsList.get(i));
writeSheet.setCustomWriteHandlerList(
Collections.singletonList(handler));
}
// 写入数据
excelWriter.write(sheetData.get(i), writeSheet);
}
} finally {
// 确保资源正确释放
if (excelWriter != null) {
try {
excelWriter.finish();
} catch (Exception e) {
// 记录日志但不中断主流程
log.error("Excel 导出错误", e);
}
}
}
}
/**
* 单表多数据模板导出 模板格式为 {.属性}