diff --git a/heritage-plugins/heritage-easyexcel/pom.xml b/heritage-plugins/heritage-easyexcel/pom.xml
new file mode 100644
index 0000000..c344246
--- /dev/null
+++ b/heritage-plugins/heritage-easyexcel/pom.xml
@@ -0,0 +1,35 @@
+
+
+ 4.0.0
+
+ org.leocoder.heritage
+ heritage-plugins
+ ${revision}
+
+
+
+ heritage-easyexcel
+ heritage-easyexcel
+ EasyExcel导入导出插件
+
+
+
+
+ org.leocoder.heritage
+ heritage-common
+ ${revision}
+
+
+
+ com.alibaba
+ easyexcel
+
+
+
+ org.springframework.boot
+ spring-boot-starter-aop
+
+
+
\ No newline at end of file
diff --git a/heritage-plugins/heritage-easyexcel/src/main/java/org/leocoder/heritage/easyexcel/anno/EnableCoderEasyExcel.java b/heritage-plugins/heritage-easyexcel/src/main/java/org/leocoder/heritage/easyexcel/anno/EnableCoderEasyExcel.java
new file mode 100755
index 0000000..06e1d50
--- /dev/null
+++ b/heritage-plugins/heritage-easyexcel/src/main/java/org/leocoder/heritage/easyexcel/anno/EnableCoderEasyExcel.java
@@ -0,0 +1,18 @@
+package org.leocoder.heritage.easyexcel.anno;
+
+import org.leocoder.heritage.easyexcel.bigdata.BigDataEasyExcelUtil;
+import org.leocoder.heritage.easyexcel.column.ColumnExcelUtil;
+import org.leocoder.heritage.easyexcel.core.utils.EasyExcelUtil;
+import org.springframework.context.annotation.Import;
+
+import java.lang.annotation.*;
+
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+// core文件夹是核心代码,bigdata是大数据导出,column是自定义列导出
+@Import({EasyExcelUtil.class, ColumnExcelUtil.class, BigDataEasyExcelUtil.class})
+public @interface EnableCoderEasyExcel {
+
+}
diff --git a/heritage-plugins/heritage-easyexcel/src/main/java/org/leocoder/heritage/easyexcel/bigdata/BigDataEasyExcelUtil.java b/heritage-plugins/heritage-easyexcel/src/main/java/org/leocoder/heritage/easyexcel/bigdata/BigDataEasyExcelUtil.java
new file mode 100755
index 0000000..cedf910
--- /dev/null
+++ b/heritage-plugins/heritage-easyexcel/src/main/java/org/leocoder/heritage/easyexcel/bigdata/BigDataEasyExcelUtil.java
@@ -0,0 +1,171 @@
+package org.leocoder.heritage.easyexcel.bigdata;
+
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.ExcelWriter;
+import com.alibaba.excel.support.ExcelTypeEnum;
+import com.alibaba.excel.write.builder.ExcelWriterBuilder;
+import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
+import com.alibaba.excel.write.metadata.WriteSheet;
+import com.alibaba.excel.write.metadata.style.WriteCellStyle;
+import com.alibaba.excel.write.metadata.style.WriteFont;
+import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
+import jakarta.servlet.http.HttpServletResponse;
+import org.apache.poi.ss.usermodel.BorderStyle;
+import org.apache.poi.ss.usermodel.HorizontalAlignment;
+import org.apache.poi.ss.usermodel.IndexedColors;
+import org.apache.poi.ss.usermodel.VerticalAlignment;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URLEncoder;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author Leocoder
+ * @description [EasyExcelUtil千万级别分页]
+ */
+public class BigDataEasyExcelUtil {
+
+ private static final Logger log = LoggerFactory.getLogger(BigDataEasyExcelUtil.class);
+
+ private static final int MAXROWS = 10000;
+
+ /**
+ * @description [获取默认表头内容的样式]
+ */
+ private static HorizontalCellStyleStrategy getDefaultHorizontalCellStyleStrategy() {
+ /** 表头样式 **/
+ WriteCellStyle headWriteCellStyle = new WriteCellStyle();
+ // 背景色(浅灰色)
+ headWriteCellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
+ // 字体大小
+ WriteFont headWriteFont = new WriteFont();
+ headWriteFont.setFontHeightInPoints((short) 20);
+ headWriteCellStyle.setWriteFont(headWriteFont);
+ //设置表头居中对齐
+ headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
+ /** 内容样式 **/
+ WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
+ // 内容字体样式(名称、大小)
+ WriteFont contentWriteFont = new WriteFont();
+ contentWriteFont.setFontName("宋体");
+ contentWriteFont.setFontHeightInPoints((short) 20);
+ contentWriteCellStyle.setWriteFont(contentWriteFont);
+ //设置内容垂直居中对齐
+ contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+ //设置内容水平居中对齐
+ contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
+ //设置边框样式
+ contentWriteCellStyle.setBorderLeft(BorderStyle.HAIR);
+ contentWriteCellStyle.setBorderTop(BorderStyle.HAIR);
+ contentWriteCellStyle.setBorderRight(BorderStyle.HAIR);
+ contentWriteCellStyle.setBorderBottom(BorderStyle.HAIR);
+ // 头样式与内容样式合并
+ return new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle);
+ }
+
+ /**
+ * @description [导出,单个Sheet,最大支持xlsx格式sheet的行数]
+ */
+ public static void writeExcel(HttpServletResponse response, List extends Object> data, String fileName, String sheetName, Class clazz) throws Exception {
+ long exportStartTime = System.currentTimeMillis();
+ log.info("报表导出Size: " + data.size() + "条。");
+ EasyExcel.write(getOutputStream(fileName, response), clazz).excelType(ExcelTypeEnum.XLSX).sheet(sheetName).registerWriteHandler(getDefaultHorizontalCellStyleStrategy()).doWrite(data);
+ System.out.println("报表导出结束时间:" + new Date() + ";写入耗时: " + (System.currentTimeMillis() - exportStartTime) + "ms");
+ }
+
+ /**
+ * @param data 查询结果
+ * @param fileName 导出文件名称
+ * @param clazz 映射实体class类
+ * @param 查询结果类型
+ * @description [单一类型大批量数据导出,适用于超过一百万的数据,需要分多个sheet页来导出。自动分页]
+ */
+ public static void writeExcel(HttpServletResponse response, List data, String fileName, Class clazz) throws Exception {
+ long exportStartTime = System.currentTimeMillis();
+ log.info("报表导出Size: " + data.size() + "条。");
+
+ // 分割的集合
+ List> lists = SplitList.splitList(data, MAXROWS);
+
+ OutputStream out = getOutputStream(fileName, response);
+ ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(out, clazz).excelType(ExcelTypeEnum.XLSX);
+ ExcelWriter excelWriter = excelWriterBuilder.build();
+ ExcelWriterSheetBuilder excelWriterSheetBuilder;
+ WriteSheet writeSheet;
+ for (int i = 1; i <= lists.size(); i++) {
+ excelWriterSheetBuilder = new ExcelWriterSheetBuilder(excelWriter);
+ excelWriterSheetBuilder.sheetNo(i);
+ excelWriterSheetBuilder.sheetName("sheet" + i);
+ writeSheet = excelWriterSheetBuilder.build();
+ excelWriter.write(lists.get(i - 1), writeSheet);
+ }
+ out.flush();
+ excelWriter.finish();
+ out.close();
+ System.out.println("报表导出结束时间:" + new Date() + ";写入耗时: " + (System.currentTimeMillis() - exportStartTime) + "ms");
+ }
+
+ public static OutputStream getOutputStream(String fileName, HttpServletResponse response) throws Exception {
+ fileName = URLEncoder.encode(fileName, "UTF-8");
+ // .xls
+ // response.setContentType("application/vnd.ms-excel");
+ // .xlsx
+ response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+ response.setCharacterEncoding("utf8");
+ response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");
+ return response.getOutputStream();
+ }
+
+ /**
+ * @param out 输出流
+ * @param flag 是否添加默认打印样式,为 true 添加,为 false 不添加。大批量导出去除样式可以节省更多的资源
+ */
+ public static ExcelWriter buildExcelWriter(OutputStream out, Boolean flag) {
+ ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(out).excelType(ExcelTypeEnum.XLSX);
+ if (flag) {
+ excelWriterBuilder.registerWriteHandler(getDefaultHorizontalCellStyleStrategy());
+ }
+ return excelWriterBuilder.build();
+ }
+
+ /**
+ * @description [默认构建带样式]
+ */
+ public static ExcelWriter buildExcelWriter(OutputStream out) {
+ return buildExcelWriter(out, true);
+ }
+
+ /**
+ * @description [单纯写入,适用于手动分页]
+ */
+ public static void writeOnly(ExcelWriter excelWriter, List data, Class clazz, Integer sheetNo, String sheetName) {
+ long exportStartTime = System.currentTimeMillis();
+ log.info("报表" + sheetNo + "写入Size: " + data.size() + "条。");
+ ExcelWriterSheetBuilder excelWriterSheetBuilder;
+ WriteSheet writeSheet;
+ excelWriterSheetBuilder = new ExcelWriterSheetBuilder(excelWriter);
+ excelWriterSheetBuilder.sheetNo(sheetNo);
+ excelWriterSheetBuilder.sheetName(sheetName);
+ writeSheet = excelWriterSheetBuilder.build();
+ writeSheet.setClazz(clazz);
+ excelWriter.write(data, writeSheet);
+ log.info("报表" + sheetNo + "写入耗时: " + (System.currentTimeMillis() - exportStartTime) + "ms");
+ }
+
+
+ /**
+ * @description [导出]
+ */
+ public static void finishWriter(OutputStream out, ExcelWriter excelWriter) throws IOException {
+ out.flush();
+ excelWriter.finish();
+ out.close();
+ System.out.println("报表导出结束时间:" + new Date());
+ }
+
+
+}
diff --git a/heritage-plugins/heritage-easyexcel/src/main/java/org/leocoder/heritage/easyexcel/bigdata/SplitList.java b/heritage-plugins/heritage-easyexcel/src/main/java/org/leocoder/heritage/easyexcel/bigdata/SplitList.java
new file mode 100755
index 0000000..fcdef19
--- /dev/null
+++ b/heritage-plugins/heritage-easyexcel/src/main/java/org/leocoder/heritage/easyexcel/bigdata/SplitList.java
@@ -0,0 +1,65 @@
+package org.leocoder.heritage.easyexcel.bigdata;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @description [List集合分割工具类]
+ * @author Leocoder
+ */
+public class SplitList {
+
+ /**
+ * @param list 待切割集合
+ * @param len 集合按照多大size来切割
+ */
+ public static List> splitList(List list, int len) {
+ if (list == null || list.size() == 0 || len < 1) {
+ return null;
+ }
+ List> result = new ArrayList>();
+ int size = list.size();
+ int count = (size + len - 1) / len;
+
+ for (int i = 0; i < count; i++) {
+ List subList = list.subList(i * len, ((i + 1) * len > size ? size : len * (i + 1)));
+ result.add(subList);
+ }
+ return result;
+ }
+
+ /**
+ * @param source 源集合
+ * @param n 分成n个集合
+ * @param 集合类型
+ * @description [集合平均分组]
+ */
+ public static List> groupList(List source, int n) {
+ if (source == null || source.size() == 0 || n < 1) {
+ return null;
+ }
+ if (source.size() < n) {
+ return Arrays.asList(source);
+ }
+ List> result = new ArrayList>();
+ int number = source.size() / n;
+ int remaider = source.size() % n;
+ // 偏移量,每有一个余数分配,就要往右偏移一位
+ int offset = 0;
+ for (int i = 0; i < n; i++) {
+ List list1 = null;
+ if (remaider > 0) {
+ list1 = source.subList(i * number + offset, (i + 1) * number + offset + 1);
+ remaider--;
+ offset++;
+ } else {
+ list1 = source.subList(i * number + offset, (i + 1) * number + offset);
+ }
+ result.add(list1);
+ }
+
+ return result;
+ }
+
+}
diff --git a/heritage-plugins/heritage-easyexcel/src/main/java/org/leocoder/heritage/easyexcel/column/ColumnExcelUtil.java b/heritage-plugins/heritage-easyexcel/src/main/java/org/leocoder/heritage/easyexcel/column/ColumnExcelUtil.java
new file mode 100755
index 0000000..568b212
--- /dev/null
+++ b/heritage-plugins/heritage-easyexcel/src/main/java/org/leocoder/heritage/easyexcel/column/ColumnExcelUtil.java
@@ -0,0 +1,265 @@
+package org.leocoder.heritage.easyexcel.column;
+
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.ExcelWriter;
+import com.alibaba.excel.support.ExcelTypeEnum;
+import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
+import com.alibaba.excel.write.handler.WriteHandler;
+import com.alibaba.excel.write.metadata.WriteSheet;
+import com.alibaba.excel.write.metadata.style.WriteCellStyle;
+import com.alibaba.excel.write.metadata.style.WriteFont;
+import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
+import jakarta.servlet.ServletOutputStream;
+import jakarta.servlet.http.HttpServletResponse;
+import org.apache.poi.ss.usermodel.*;
+import org.springframework.http.MediaType;
+import org.springframework.util.CollectionUtils;
+
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * @author Leocoder
+ * @description [ColumExcelUtil-自定义表头单/多sheet导出]
+ */
+public class ColumnExcelUtil {
+
+ /**
+ * 头部样式
+ */
+ private static WriteCellStyle getHeadStyle() {
+ // 头的策略
+ WriteCellStyle headWriteCellStyle = new WriteCellStyle();
+ // 背景颜色
+ headWriteCellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
+ headWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
+
+ // 字体
+ WriteFont headWriteFont = new WriteFont();
+ // 设置字体名字
+ headWriteFont.setFontName("微软雅黑");
+ // 设置字体大小
+ headWriteFont.setFontHeightInPoints((short) 10);
+ // 字体加粗
+ headWriteFont.setBold(false);
+ // 在样式用应用设置的字体
+ headWriteCellStyle.setWriteFont(headWriteFont);
+ // 边框样式
+ setBorderStyle(headWriteCellStyle);
+ // 设置自动换行
+ headWriteCellStyle.setWrapped(true);
+ // 设置水平对齐的样式为居中对齐
+ headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
+ // 设置垂直对齐的样式为居中对齐
+ headWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+ // 设置文本收缩至合适
+ // headWriteCellStyle.setShrinkToFit(true);
+
+ return headWriteCellStyle;
+ }
+
+ /**
+ * 内容样式
+ */
+ private static WriteCellStyle getContentStyle() {
+ // 内容的策略
+ WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
+
+ // 背景白色
+ // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定
+ contentWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
+ contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
+
+ // 设置字体
+ WriteFont contentWriteFont = new WriteFont();
+ // 设置字体大小
+ contentWriteFont.setFontHeightInPoints((short) 10);
+ // 设置字体名字
+ contentWriteFont.setFontName("宋体");
+ // 在样式用应用设置的字体
+ contentWriteCellStyle.setWriteFont(contentWriteFont);
+ // 设置样式
+ setBorderStyle(contentWriteCellStyle);
+ // 水平居中
+ contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
+ // 垂直居中
+ contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+ // 设置自动换行
+ contentWriteCellStyle.setWrapped(true);
+ // 设置单元格格式是:文本格式,方式长数字文本科学计数法
+ // contentWriteCellStyle.setDataFormatData();
+ // 设置文本收缩至合适
+ // contentWriteCellStyle.setShrinkToFit(true);
+
+ return contentWriteCellStyle;
+ }
+
+ /**
+ * 边框样式
+ */
+ private static void setBorderStyle(WriteCellStyle cellStyle) {
+ // 设置底边框
+ cellStyle.setBorderBottom(BorderStyle.THIN);
+ // 设置底边框颜色
+ cellStyle.setBottomBorderColor(IndexedColors.BLACK1.getIndex());
+ // 设置左边框
+ cellStyle.setBorderLeft(BorderStyle.THIN);
+ // 设置左边框颜色
+ cellStyle.setLeftBorderColor(IndexedColors.BLACK1.getIndex());
+ // 设置右边框
+ cellStyle.setBorderRight(BorderStyle.THIN);
+ // 设置右边框颜色
+ cellStyle.setRightBorderColor(IndexedColors.BLACK1.getIndex());
+ // 设置顶边框
+ cellStyle.setBorderTop(BorderStyle.THIN);
+ // 设置顶边框颜色
+ cellStyle.setTopBorderColor(IndexedColors.BLACK1.getIndex());
+ }
+
+ /**
+ * @param response 返回
+ * @param fileName 文件名
+ * @param sheetNames sheet集合
+ * @param headerList 表头集合
+ * @param dataList 数据集合
+ */
+ public static void excelNoModelSheetExport(HttpServletResponse response, String fileName, List sheetNames,
+ List>> headerList, List>> dataList) {
+ ServletOutputStream out = null;
+ try {
+ // 清除缓存,确保每次导出都是从零开始
+ CustomColumnWidthHandler.clearCache();
+
+ out = getOut(response, fileName);
+ sheetNames = sheetNames.stream().distinct().collect(Collectors.toList());
+ int num = sheetNames.size();
+
+ // 设置基础样式
+ HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(getHeadStyle(), getContentStyle());
+ // 创建ExcelWriter对象
+ ExcelWriter excelWriter = EasyExcel.write(out).build();
+ for (int i = 0; i < num; i++) {
+ ExcelWriterSheetBuilder sheetBuilder = EasyExcel.writerSheet(i, sheetNames.get(i)).head(headerList.get(i))
+ // 注册自定义列宽处理器
+ .registerWriteHandler(new CustomColumnWidthHandler())
+ // 注册样式策略
+ .registerWriteHandler(horizontalCellStyleStrategy);
+ // 写入数据
+ WriteSheet writeSheet = sheetBuilder.build();
+ if (CollectionUtils.isEmpty(dataList)) {
+ excelWriter.write(new ArrayList<>(), writeSheet);
+ } else {
+ excelWriter.write(dataList.get(i), writeSheet);
+ }
+ }
+ // 完成写入并关闭ExcelWriter
+ excelWriter.finish();
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ if (Objects.nonNull(out)) {
+ try {
+ out.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ /**
+ * @param response 返回
+ * @param fileName 文件名
+ * @param sheetNames sheet集合
+ * @param headerList 表头集合
+ * @param dataList 数据集合
+ * @param writeHandler 自定义样式
+ */
+ public static void excelNoModelSheetExport(HttpServletResponse response, String fileName, List sheetNames,
+ List>> headerList, List>> dataList, WriteHandler writeHandler) {
+ ServletOutputStream out = null;
+ try {
+ // 清除缓存,确保每次导出都是从零开始
+ CustomColumnWidthHandler.clearCache();
+
+ out = getOut(response, fileName);
+ sheetNames = sheetNames.stream().distinct().collect(Collectors.toList());
+ int num = sheetNames.size();
+
+ // 设置基础样式
+ HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(getHeadStyle(), getContentStyle());
+ ExcelWriter excelWriter = EasyExcel.write(out).build();
+ for (int i = 0; i < num; i++) {
+ ExcelWriterSheetBuilder sheetBuilder = EasyExcel.writerSheet(i, sheetNames.get(i)).head(headerList.get(i))
+ // 注册自定义列宽处理器
+ .registerWriteHandler(new CustomColumnWidthHandler())
+ // 注册样式策略
+ .registerWriteHandler(horizontalCellStyleStrategy);
+ if (Objects.nonNull(writeHandler)) {
+ // 自定义样式设置
+ sheetBuilder.registerWriteHandler(writeHandler);
+ }
+ // 写入数据
+ WriteSheet writeSheet = sheetBuilder.build();
+ if (CollectionUtils.isEmpty(dataList)) {
+ excelWriter.write(new ArrayList<>(), writeSheet);
+ } else {
+ // 完成写入并关闭ExcelWriter
+ excelWriter.write(dataList.get(i), writeSheet);
+ }
+ }
+ excelWriter.finish();
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ if (Objects.nonNull(out)) {
+ try {
+ out.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ private static ServletOutputStream getOut(HttpServletResponse response, String fileName) throws Exception {
+ fileName = fileName + new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + ExcelTypeEnum.XLSX.getValue();
+ setAttachmentResponseHeader(response, fileName);
+ response.setCharacterEncoding("utf-8");
+ response.setContentType(MediaType.MULTIPART_FORM_DATA_VALUE);
+ response.setContentType("application/vnd.ms-excel;charset=utf-8");
+ return response.getOutputStream();
+ }
+
+ /**
+ * 下载文件名重新编码
+ *
+ * @param response 响应对象
+ * @param realFileName 真实文件名
+ */
+ public static void setAttachmentResponseHeader(HttpServletResponse response, String realFileName) {
+ String percentEncodedFileName = percentEncode(realFileName);
+ String contentDispositionValue = "attachment; filename=" + percentEncodedFileName + ";filename*=utf-8''" + percentEncodedFileName;
+ response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
+ response.setHeader("Content-Disposition", contentDispositionValue);
+ }
+
+ /**
+ * 百分号编码工具方法
+ *
+ * @param s 需要百分号编码的字符串
+ * @return 百分号编码后的字符串
+ */
+ public static String percentEncode(String s) {
+ String encode = URLEncoder.encode(s, StandardCharsets.UTF_8);
+ return encode.replaceAll("\\+", "%20");
+ }
+
+}
diff --git a/heritage-plugins/heritage-easyexcel/src/main/java/org/leocoder/heritage/easyexcel/column/CustomColumnWidthHandler.java b/heritage-plugins/heritage-easyexcel/src/main/java/org/leocoder/heritage/easyexcel/column/CustomColumnWidthHandler.java
new file mode 100755
index 0000000..70d5a3a
--- /dev/null
+++ b/heritage-plugins/heritage-easyexcel/src/main/java/org/leocoder/heritage/easyexcel/column/CustomColumnWidthHandler.java
@@ -0,0 +1,91 @@
+package org.leocoder.heritage.easyexcel.column;
+
+import com.alibaba.excel.enums.CellDataTypeEnum;
+import com.alibaba.excel.metadata.Head;
+import com.alibaba.excel.metadata.data.WriteCellData;
+import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
+import com.alibaba.excel.write.style.column.AbstractColumnWidthStyleStrategy;
+import org.apache.poi.ss.usermodel.Cell;
+import org.springframework.util.CollectionUtils;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Leocoder
+ * @description [CustomHandler - 自定义列宽]
+ */
+public class CustomColumnWidthHandler extends AbstractColumnWidthStyleStrategy {
+
+ /**
+ * 最大列宽
+ */
+ private static final int MAX_COLUMN_WIDTH = 255;
+
+ private static final Map> CACHE = new HashMap<>(8);
+
+ public CustomColumnWidthHandler() {
+ }
+
+ /**
+ * 清除缓存
+ */
+ public static void clearCache() {
+ CACHE.clear();
+ }
+
+ @Override
+ protected void setColumnWidth(WriteSheetHolder writeSheetHolder, List> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
+ boolean needSetWidth = isHead || !CollectionUtils.isEmpty(cellDataList);
+ if (isHead) {
+ // 如果不是最后一个表头,则不改变列宽
+ List headNameList = head.getHeadNameList();
+ if (!CollectionUtils.isEmpty(headNameList)) {
+ int size = headNameList.size();
+ if (!cell.getStringCellValue().equals(headNameList.get(size - 1))) {
+ return;
+ }
+ }
+ }
+ if (needSetWidth) {
+ Map maxColumnWidthMap = CACHE.computeIfAbsent(writeSheetHolder.getSheetNo(), k -> new HashMap<>(16));
+ Integer columnWidth = this.dataLength(cellDataList, cell, isHead);
+ if (columnWidth >= 0) {
+ if (columnWidth > MAX_COLUMN_WIDTH) {
+ columnWidth = MAX_COLUMN_WIDTH;
+ }
+ Integer maxColumnWidth = maxColumnWidthMap.get(cell.getColumnIndex());
+ if (maxColumnWidth == null || columnWidth > maxColumnWidth) {
+ maxColumnWidthMap.put(cell.getColumnIndex(), columnWidth);
+ writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), columnWidth * 256);
+ }
+ }
+ }
+ }
+
+ private Integer dataLength(List> cellDataList, Cell cell, Boolean isHead) {
+ if (isHead) {
+ return cell.getStringCellValue().getBytes().length;
+ } else {
+ WriteCellData> cellData = cellDataList.get(0);
+ CellDataTypeEnum type = cellData.getType();
+ if (type == null) {
+ return -1;
+ } else {
+ switch (type) {
+ case STRING:
+ return cellData.getStringValue().getBytes().length;
+ case BOOLEAN:
+ return cellData.getBooleanValue().toString().getBytes().length;
+ case NUMBER:
+ return cellData.getNumberValue().toString().getBytes().length;
+ case DATE:
+ return cellData.getDateValue().toString().getBytes().length;
+ default:
+ return -1;
+ }
+ }
+ }
+ }
+}
diff --git a/heritage-plugins/heritage-easyexcel/src/main/java/org/leocoder/heritage/easyexcel/column/CustomStyle.java b/heritage-plugins/heritage-easyexcel/src/main/java/org/leocoder/heritage/easyexcel/column/CustomStyle.java
new file mode 100755
index 0000000..35a94d5
--- /dev/null
+++ b/heritage-plugins/heritage-easyexcel/src/main/java/org/leocoder/heritage/easyexcel/column/CustomStyle.java
@@ -0,0 +1,167 @@
+package org.leocoder.heritage.easyexcel.column;
+
+import com.alibaba.excel.metadata.Head;
+import com.alibaba.excel.metadata.data.WriteCellData;
+import com.alibaba.excel.write.handler.CellWriteHandler;
+import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
+import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
+import org.apache.poi.ss.usermodel.*;
+
+import java.util.List;
+
+/**
+ * @author Leocoder
+ * @description [CustomStyle-自定义样式]
+ */
+public class CustomStyle implements CellWriteHandler {
+
+ @Override
+ public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List> cellDataList,
+ Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
+ int rowIndex = cell.getRowIndex();
+ int columnIndex = cell.getColumnIndex();
+ String sheetName = cell.getSheet().getSheetName();
+ if (rowIndex == 1 && columnIndex == 0) {
+ // 设置表头统计的样式,由于单元格是合并的,只设置第一列就行了
+ setTotalStyle(writeSheetHolder, cellDataList, cell);
+ return;
+ }
+ if (!isHead) {
+ // 内容样式处理
+ Workbook workbook = cell.getSheet().getWorkbook();
+ if (sheetName.equals("手机")) {
+ // 判断是否为”手机“sheet
+ if (columnIndex == 3) {
+ // 判断价格
+ // 注意,这里的 cell.get**Value 有多个方法,一定要准确,否则会报错,报错后不会再进入这个拦截器,直接导出了
+ // 如果无法准确判断应该用哪个 getValue,可以 debug 测试
+ double value = cell.getNumericCellValue();
+ if (value > 5000.00) {
+ // 字体样式改为红色
+ CellStyle cellStyle = getContentCellStyle(workbook, IndexedColors.WHITE.getIndex(), IndexedColors.RED.getIndex());
+ //设置当前单元格样式
+ cell.setCellStyle(cellStyle);
+ // 这里要把 WriteCellData的样式清空
+ // 不然后面还有一个拦截器 FillStyleCellWriteHandler 默认会将 WriteCellStyle 设置到cell里面去 会导致自己设置的不一样
+ cellDataList.get(0).setWriteCellStyle(null);
+ }
+ } else if (columnIndex == 4) {
+ // 判断CPU
+ String value = cell.getStringCellValue();
+ if (value.contains("骁龙8 Gen3")) {
+ // 背景改为黄色
+ CellStyle cellStyle = getContentCellStyle(workbook, IndexedColors.YELLOW.getIndex(), IndexedColors.BLACK.getIndex());
+ cell.setCellStyle(cellStyle);
+ cellDataList.get(0).setWriteCellStyle(null);
+ }
+ }
+ } else {
+ // ”电脑“sheet
+ if (columnIndex == 2) {
+ // 判断价格
+ double value = cell.getNumericCellValue();
+ if (value > 10000.00) {
+ // 字体样式改为红色
+ CellStyle cellStyle = getContentCellStyle(workbook, IndexedColors.WHITE.getIndex(), IndexedColors.RED.getIndex());
+ cell.setCellStyle(cellStyle);
+ cellDataList.get(0).setWriteCellStyle(null);
+ }
+ } else if (columnIndex == 4) {
+ String value = cell.getStringCellValue();
+ if (value.contains("RTX 4090")) {
+ // 背景改为蓝色
+ CellStyle cellStyle = getContentCellStyle(workbook, IndexedColors.LIGHT_BLUE.getIndex(), IndexedColors.BLACK.getIndex());
+ cell.setCellStyle(cellStyle);
+ cellDataList.get(0).setWriteCellStyle(null);
+ }
+ }
+ }
+ }
+ }
+
+ private CellStyle getContentCellStyle(Workbook workbook, short ffColorIndex, short fontColorIndex) {
+ // 单元格策略
+ CellStyle cellStyle = workbook.createCellStyle();
+ // 设置背景颜色
+ cellStyle.setFillForegroundColor(ffColorIndex);
+ cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+ // 设置垂直居中为居中对齐
+ cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+ // 设置左右对齐为居中对齐
+ cellStyle.setAlignment(HorizontalAlignment.CENTER);
+ // 自动换行
+ cellStyle.setWrapText(true);
+ // 设置边框
+ setBorderStyle(cellStyle);
+ // 字体
+ Font font = workbook.createFont();
+ //设置字体名字
+ font.setFontName("宋体");
+ //设置字体大小
+ font.setFontHeightInPoints((short) 10);
+ // 设置字体颜色
+ font.setColor(fontColorIndex);
+ //字体加粗
+ font.setBold(false);
+ //在样式用应用设置的字体
+ cellStyle.setFont(font);
+ return cellStyle;
+ }
+
+ private void setTotalStyle(WriteSheetHolder writeSheetHolder, List> cellDataList, Cell cell) {
+ Workbook workbook = cell.getSheet().getWorkbook();
+ //设置行高
+ writeSheetHolder.getSheet().getRow(cell.getRowIndex()).setHeight((short) (1.4 * 256 * 2));
+ // 单元格策略
+ CellStyle cellStyle = workbook.createCellStyle();
+ // 设置背景颜色灰色
+ cellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
+ cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
+ // 设置垂直居中为居中对齐
+ cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+ // 设置左右对齐为靠右对齐
+ cellStyle.setAlignment(HorizontalAlignment.RIGHT);
+ // 自动换行
+ cellStyle.setWrapText(true);
+ // 设置边框
+ setBorderStyle(cellStyle);
+ // 字体
+ Font font = workbook.createFont();
+ //设置字体名字
+ font.setFontName("微软雅黑");
+ //设置字体大小
+ font.setFontHeightInPoints((short) 10);
+ //字体加粗
+ font.setBold(false);
+ //在样式用应用设置的字体
+ cellStyle.setFont(font);
+ //设置当前单元格样式
+ cell.setCellStyle(cellStyle);
+ // 这里要把 WriteCellData的样式清空
+ // 不然后面还有一个拦截器 FillStyleCellWriteHandler 默认会将 WriteCellStyle 设置到cell里面去 会导致自己设置的不一样
+ cellDataList.get(0).setWriteCellStyle(null);
+ }
+
+ /**
+ * 边框样式
+ */
+ private static void setBorderStyle(CellStyle cellStyle) {
+ //设置底边框
+ cellStyle.setBorderBottom(BorderStyle.THIN);
+ //设置底边框颜色
+ cellStyle.setBottomBorderColor(IndexedColors.BLACK1.getIndex());
+ //设置左边框
+ cellStyle.setBorderLeft(BorderStyle.THIN);
+ //设置左边框颜色
+ cellStyle.setLeftBorderColor(IndexedColors.BLACK1.getIndex());
+ //设置右边框
+ cellStyle.setBorderRight(BorderStyle.THIN);
+ //设置右边框颜色
+ cellStyle.setRightBorderColor(IndexedColors.BLACK1.getIndex());
+ //设置顶边框
+ cellStyle.setBorderTop(BorderStyle.THIN);
+ //设置顶边框颜色
+ cellStyle.setTopBorderColor(IndexedColors.BLACK1.getIndex());
+ }
+
+}
diff --git a/heritage-plugins/heritage-easyexcel/src/main/java/org/leocoder/heritage/easyexcel/core/enums/ExcelTemplateEnum.java b/heritage-plugins/heritage-easyexcel/src/main/java/org/leocoder/heritage/easyexcel/core/enums/ExcelTemplateEnum.java
new file mode 100755
index 0000000..d8cf859
--- /dev/null
+++ b/heritage-plugins/heritage-easyexcel/src/main/java/org/leocoder/heritage/easyexcel/core/enums/ExcelTemplateEnum.java
@@ -0,0 +1,71 @@
+package org.leocoder.heritage.easyexcel.core.enums;
+
+import lombok.Getter;
+
+/**
+ * @author Leocoder
+ * @description [ExcelTemplateEnum导入模板监听器]
+ */
+@Getter
+public enum ExcelTemplateEnum {
+
+ /**
+ * 单sheet导出
+ */
+ TEMPLATE_1("1", "下载模版"),
+
+ /**
+ * 模板格式
+ */
+ TEMPLATE_SUFFIX("xlsx", ".xlsx"),
+ TEMPLATE_SUFFIX_XLS("xls", ".xls"),
+ TEMPLATE_SUFFIX_DOCX("docx", ".docx"),
+
+ /**
+ * 模板路径
+ */
+ TEMPLATE_PATH("path", "excel"),
+ ;
+
+ private final String code;
+ private final String desc;
+
+ ExcelTemplateEnum(String code, String desc) {
+ this.code = code;
+ this.desc = desc;
+ }
+
+ /**
+ * 通过code获取msg
+ *
+ * @param code 枚举值
+ * @return
+ */
+ public static String getMsgByCode(String code) {
+ if (code == null) {
+ return null;
+ }
+ ExcelTemplateEnum enumList = getByCode(code);
+ if (enumList == null) {
+ return null;
+ }
+ return enumList.getDesc();
+ }
+
+ public static String getCode(ExcelTemplateEnum enumList) {
+ if (enumList == null) {
+ return null;
+ }
+ return enumList.getCode();
+ }
+
+ public static ExcelTemplateEnum getByCode(String code) {
+ for (ExcelTemplateEnum enumList : values()) {
+ if (enumList.getCode().equals(code)) {
+ return enumList;
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/heritage-plugins/heritage-easyexcel/src/main/java/org/leocoder/heritage/easyexcel/core/listener/UploadDataListener.java b/heritage-plugins/heritage-easyexcel/src/main/java/org/leocoder/heritage/easyexcel/core/listener/UploadDataListener.java
new file mode 100755
index 0000000..7a0c382
--- /dev/null
+++ b/heritage-plugins/heritage-easyexcel/src/main/java/org/leocoder/heritage/easyexcel/core/listener/UploadDataListener.java
@@ -0,0 +1,52 @@
+package org.leocoder.heritage.easyexcel.core.listener;
+
+import com.alibaba.excel.context.AnalysisContext;
+import com.alibaba.excel.event.AnalysisEventListener;
+import com.alibaba.excel.exception.ExcelDataConvertException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Leocoder
+ * @description [UploadDataListener]
+ */
+public class UploadDataListener extends AnalysisEventListener {
+
+ // 数据集
+ private final List list = new ArrayList<>();
+
+ public List getList() {
+ return this.list;
+ }
+
+ /**
+ * @description [每条数据都会进入]
+ * @author Leocoder
+ */
+ @Override
+ public void invoke(T object, AnalysisContext analysisContext) {
+ this.list.add(object);
+ }
+
+ /**
+ * @description [数据解析完调用]
+ * @author Leocoder
+ */
+ @Override
+ public void doAfterAllAnalysed(AnalysisContext analysisContext) {
+
+ }
+
+ /**
+ * @description [异常时调用]
+ * @author Leocoder
+ */
+ @Override
+ public void onException(Exception exception, AnalysisContext context) throws Exception {
+ // 数据解析异常
+ if (exception instanceof ExcelDataConvertException excelDataConvertException) {
+ throw new RuntimeException("第" + excelDataConvertException.getRowIndex() + "行" + excelDataConvertException.getColumnIndex() + "列" + "数据解析异常");
+ }
+ }
+}
diff --git a/heritage-plugins/heritage-easyexcel/src/main/java/org/leocoder/heritage/easyexcel/core/utils/EasyExcelUtil.java b/heritage-plugins/heritage-easyexcel/src/main/java/org/leocoder/heritage/easyexcel/core/utils/EasyExcelUtil.java
new file mode 100755
index 0000000..90e2172
--- /dev/null
+++ b/heritage-plugins/heritage-easyexcel/src/main/java/org/leocoder/heritage/easyexcel/core/utils/EasyExcelUtil.java
@@ -0,0 +1,303 @@
+package org.leocoder.heritage.easyexcel.core.utils;
+
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.EasyExcelFactory;
+import com.alibaba.excel.ExcelWriter;
+import com.alibaba.excel.read.builder.ExcelReaderBuilder;
+import com.alibaba.excel.write.metadata.WriteSheet;
+import com.alibaba.excel.write.metadata.fill.FillConfig;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+import org.leocoder.heritage.easyexcel.core.enums.ExcelTemplateEnum;
+import org.leocoder.heritage.easyexcel.core.listener.UploadDataListener;
+import org.springframework.stereotype.Component;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Leocoder
+ * @description [EasyExcelUtil导出工具类]
+ */
+@Slf4j
+@Component
+public class EasyExcelUtil {
+
+ /**
+ * @param file:文件流
+ * @param clazz:数据对象
+ * @param sheetName:要读取的sheet [不传:默认读取第一个sheet]
+ * @description [导入简单excel数据]
+ */
+ public List importExcel(MultipartFile file, Class clazz, String sheetName) {
+ try {
+ this.checkFile(file);
+ UploadDataListener uploadDataListener = new UploadDataListener<>();
+ ExcelReaderBuilder builder = EasyExcelFactory.read(file.getInputStream(), clazz, uploadDataListener);
+ if (StringUtils.isEmpty(sheetName)) {
+ builder.sheet().doRead();
+ } else {
+ builder.sheet(sheetName).doRead();
+ }
+ return uploadDataListener.getList();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * @param multipartFile 传入文件
+ * @param objList 需要导入的sheet页实体类型集合
+ * @param index sheet页个数
+ * @param indexList 需要导入sheet页下标集合
+ * @description [指定sheet页导入通用方法]
+ */
+ public List> importExcelsByIndex(MultipartFile multipartFile, List objList, int index, List indexList) {
+ try {
+ if (multipartFile == null) {
+ throw new RuntimeException("文件为空");
+ }
+ List> resultList = new LinkedList<>();
+ //初始化导入sheet页实体类型下标
+ int objListClass = 0;
+ for (int i = 0; i < index; i++) {
+ if (indexList.contains(i)) {
+ UploadDataListener uploadDataListener = new UploadDataListener<>();
+ List excels;
+ EasyExcelFactory.read(multipartFile.getInputStream(), objList.get(objListClass).getClass(), uploadDataListener).sheet(i).doRead();
+ excels = uploadDataListener.getList();
+ resultList.add(excels);
+ objListClass++;
+ }
+ }
+ return resultList;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * @param file 文件流
+ * @param index 需要读取的sheet个数 [默认0开始,如果传入3,则读取0 1 2]
+ * @param params 每个sheet里面需要封装的对象[如果index为3,则需要传入对应的3个对象]
+ * @description [读取多个sheet]
+ */
+ public List> importExcels(MultipartFile file, int index, List