Spring Boot集成EasyExcel实现数据导出

news2025/1/21 12:21:33

        在本文中,我们将探讨如何使用Spring Boot集成EasyExcel库来实现数据导出功能。我们将学习如何通过EasyExcel库生成Excel文件,并实现一些高级功能,如支持列下拉和自定义单元格样式,自适应列宽、行高,动态表头 ,以及如何同时导出多个sheet页的数据。

引入依赖

        首先,我们需要在pom.xml文件中添加EasyExcel和相关的依赖项

            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi-ooxml</artifactId>
                <version>4.1.2</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>easyexcel</artifactId>
                <version>3.2.1</version>
            </dependency>
            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi</artifactId>
                <version>4.1.2</version>
            </dependency>

创建参数类

 动态生成EXCEL参数类

        支持sheet名称、模版类、动态表头、数据集、下拉列、单元格样式定义。


import lombok.Data;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

/**
 * <p>  导出动态参数   </p>

 */
@Data
public class EasyExcelExportDynamicParam implements Serializable {
    /**
     * sheet名称
     */
    private String sheetName;

    /**
     * 模版
     */
    private Class<?> template;

    /**
     * 数据集
     */
    private List<?> dataList;

    /**
     * 动态表头
     */
    private List<List<String>> dynamicHeaderList;

    /**
     * 单元格样式map,key为行下标,
     * Map<Integer,EasyExcelExportDynamicStyleParam> key为列下标
     */
    private Map<Integer, Map<Integer, EasyExcelExportDynamicStyleParam>> styleMap;

    /**
     * 下拉选项 key为列下标
     */
    private Map<Integer, ExcelSelectedResolve> selectedMap;
}

单元格样式参数类

        支持字体颜色、背景颜色、字体、字体大小、单元格内容对齐方式。

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.poi.ss.usermodel.HorizontalAlignment;

import java.io.Serializable;

/**
 * <p>  EasyExcel导出动态单元格样式   </p>

 */
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class EasyExcelExportDynamicStyleParam implements Serializable {

    /**
     * 字体颜色 IndexedColors.WHITE.getIndex()
     */
    private Short fontColor;

    /**
     * 背景颜色
     */
    private Short bgColor;

    /**
     * 字体
     */
    private String fontName;

    /**
     * 字体大小
     */
    private Short fontSize;

    /**
     * 单元格内容对齐方式
     */
    private HorizontalAlignment alignment;

}

 单元格添加下拉列表配置

        支持注解方式设置单元格下拉列表,起始行、结束行、固定下拉内容、动态下拉内容。

import java.lang.annotation.*;

/**
 * <p>  excel动态下拉框数据填充   </p>

 */
@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelSelected {
    /**
     * 固定下拉内容
     */
    String[] source() default {};

    /**
     * 动态下拉内容服务类
     */
    String dynamicData() default "";

    /**
     * 动态下拉内容参数
     *
     * @return
     */
    String dynamicParam() default "";

    /**
     * 设置下拉框的起始行,默认为第二行
     */
    int firstRow() default 1;

    /**
     * 设置下拉框的结束行,默认10000行
     */
    int lastRow() default 5000;
}

/**
 * <p>  excel动态下拉框数据服务提供者   </p>

 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelDynamicData {

    /**
     * 提供数据的服务名
     *
     * @return
     */
    String name();

}

/**
 * <p>     </p>

 */
public interface ExcelDynamicSelectHandler {
    /**
     * 获取动态生成的下拉框可选数据
     * @return 动态生成的下拉框可选数据
     */
    String[] getSource(String param);
}


import com.alibaba.excel.annotation.ExcelProperty;
import com.yt.bi.goods.common.annotation.ExcelSelected;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.util.*;

/**
 * <p>  自定义ExcelSelected注解解析  </p>

 */
@Data
@Slf4j
public class ExcelSelectedResolve {
    /**
     * 下拉内容
     */
    private String[] source;

    /**
     * 设置下拉框的起始行,默认为第二行
     */
    private int firstRow = 1;

    /**
     * 设置下拉框的结束行
     */
    private int lastRow = 2000;

    /**
     * 解析表头类中的下拉注解
     *
     * @param head 表头类
     * @return Map<下拉框列索引, 下拉框内容> map
     */
    public static Map<Integer, ExcelSelectedResolve> resolveSelectedAnnotation(Class<?> head) {
        Map<Integer, ExcelSelectedResolve> selectedMap = new HashMap<>();
        if (Objects.isNull(head)) {
            return selectedMap;
        }
        // getDeclaredFields(): 返回全部声明的属性;getFields(): 返回public类型的属性
        Field[] fields = head.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            // 解析注解信息
            ExcelSelected selected = field.getAnnotation(ExcelSelected.class);
            ExcelProperty property = field.getAnnotation(ExcelProperty.class);
            if (selected == null) {
                continue;
            }
            String[] source = resolveSelectedSource(selected);
            if (source == null || source.length == 0) {
                continue;
            }
            ExcelSelectedResolve excelSelectedResolve = new ExcelSelectedResolve();
            excelSelectedResolve.setSource(source);
            excelSelectedResolve.setFirstRow(selected.firstRow());
            excelSelectedResolve.setLastRow(selected.lastRow());
            if (property != null && property.index() >= 0) {
                selectedMap.put(property.index(), excelSelectedResolve);
            } else {
                selectedMap.put(i, excelSelectedResolve);
            }
        }
        return selectedMap;
    }

    /**
     * 解析表头类中的配置注解
     *
     * @param head 表头类
     */
    public static List<List<String>> resolvePropertyAnnotation(Class<?> head) {
        List<List<String>> list = new ArrayList<>();
        if (Objects.isNull(head)) {
            return list;
        }
        // getDeclaredFields(): 返回全部声明的属性;getFields(): 返回public类型的属性
        Field[] fields = head.getDeclaredFields();
        for (Field field : fields) {
            ExcelProperty property = field.getAnnotation(ExcelProperty.class);
            if (property != null) {
                list.add(Arrays.asList(property.value()));
            }
        }
        return list;
    }

    /**
     * 获取下拉框选项值
     *
     * @param excelSelected
     * @return
     */
    private static String[] resolveSelectedSource(ExcelSelected excelSelected) {
        if (excelSelected == null) {
            return null;
        }
        // 获取固定下拉框的内容
        String[] source = excelSelected.source();
        if (source.length > 0) {
            return source;
        }

        // 获取动态下拉框的内容
        ExcelDynamicSelectHandler excelDynamicSelectHandler = ExcelDynamicDataStrategyFactory.doStrategy(excelSelected.dynamicData());
        if (Objects.nonNull(excelDynamicSelectHandler)) {
            return excelDynamicSelectHandler.getSource(excelSelected.dynamicParam());
        }
        return null;
    }

}

创建导出功能工具类

        为了实现高内聚和低耦合的设计,我们可以创建一个导出功能的工具类EasyExcelUtil,支持动态表头生成、多sheet、下拉列等功能

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;


@Slf4j
public class EasyExcelUtil {
   

    /**
     * 生成多个sheet
     *
     * @param response
     * @param paramList
     * @param fileName
     * @throws IOException
     */
    public static void exportExcel(HttpServletResponse response, List<EasyExcelExportDynamicParam> paramList, String fileName) throws IOException {
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        fileName = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName);
        try (ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).build()) {
            for (EasyExcelExportDynamicParam param : paramList) {
                ExcelWriterSheetBuilder writerSheetBuilder = EasyExcel.writerSheet(param.getSheetName())
                        .head(param.getDynamicHeaderList())
                        .head(param.getTemplate());
                // 样式
                Map<Integer, Map<Integer, EasyExcelExportDynamicStyleParam>> styleMap = param.getStyleMap();
                if (CollectionUtil.isNotEmpty(styleMap)) {
                    writerSheetBuilder.registerWriteHandler(new CellStyleSheetWriteHandler(styleMap));
                }
                // 下拉选择
                Map<Integer, ExcelSelectedResolve> selectedMap =
                        CollectionUtil.isNotEmpty(param.getSelectedMap()) ? param.getSelectedMap() : ExcelSelectedResolve.resolveSelectedAnnotation(param.getTemplate());
                if (CollectionUtil.isNotEmpty(selectedMap)) {
                    writerSheetBuilder.registerWriteHandler(new SelectedSheetWriteHandler(selectedMap));
                }
                excelWriter.write(param.getDataList(), writerSheetBuilder.build());
            }
            excelWriter.finish();
        }
    }

}

使用CellWriteHandler实现自定义单元格样式

        EasyExcel提供了CellWriteHandler接口,其中的afterCellDispose方法在单元格写操作完成并销毁后被调用。我们可以通过实现该接口并重写afterCellDispose方法来实现自定义单元格样式。

        在重写的afterCellDispose方法中,我们可以获取到已经创建好的单元格,并添加自定义的样式。这个方法在每个单元格写操作完成后都会被调用,因此我们可以根据需要对特定的单元格或整个表格进行样式处理。

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.util.StyleUtil;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * <p>  excel设置动态列样式处理器   </p>

 */
@Data
@AllArgsConstructor
public class CellStyleSheetWriteHandler implements CellWriteHandler {
    private static final short DEFAULT_FONT_SIZE = 14;
    private static final String DEFAULT_FONT_NAME = "宋体";
    private static final short DEFAULT_FONT_COLOR = 8;
    private static final short DEFAULT_BG_COLOR = 22;

    private Map<Integer, Map<Integer, EasyExcelExportDynamicStyleParam>> styleMap;

    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,
                                 List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex,
                                 Boolean isHead) {
        int rowIndex = cell.getRowIndex();
        Sheet sheet = cell.getSheet();
        Workbook workbook = sheet.getWorkbook();
        Row row = sheet.getRow(rowIndex);
        int columnIndex = cell.getColumnIndex();

        if (isHead) {
            // 表头设置自适应列宽
            // 获取单元格内容长度(以字符为单位)
            String stringCellValue = cell.getStringCellValue();
            int contentLength = stringCellValue.length();
            // 计算自动调整后的列宽(加上一些额外空间)
            int newWidth = contentLength > 10 ? (contentLength + 35) * 256 : (contentLength + 12) * 256;
            sheet.setColumnWidth(columnIndex, newWidth);

            // 表头设置自适应行高
            String[] split = stringCellValue.split("\\n");
            if (split != null && split.length > 0) {
                setRowHeight(row, (short) ((split.length + 1.2) * 256));
            }
        }
        if (CollectionUtil.isEmpty(styleMap)) {
            return;
        }

        Map<Integer, EasyExcelExportDynamicStyleParam> indexes = styleMap.get(rowIndex);
        if (CollectionUtils.isEmpty(indexes)) {
            return;
        }

        // 自定义样式
        setCellStyle(row, cell, workbook, indexes.get(columnIndex));
    }

    /**
     * 自定义样式
     *
     * @param cell
     * @param workbook
     * @param styleParam
     */
    private void setCellStyle(Row row, Cell cell, Workbook workbook, EasyExcelExportDynamicStyleParam styleParam) {
        if (Objects.isNull(styleParam)) {
            return;
        }
        // 字体
        Font font = workbook.createFont();
        font.setFontName(StringUtils.isNotBlank(styleParam.getFontName()) ? styleParam.getFontName() : DEFAULT_FONT_NAME);
        font.setFontHeightInPoints(Objects.nonNull(styleParam.getFontSize()) ? styleParam.getFontSize() : DEFAULT_FONT_SIZE);
        font.setBold(true);
        font.setColor(Objects.nonNull(styleParam.getFontColor()) ? styleParam.getFontColor() : DEFAULT_FONT_COLOR);

        WriteCellStyle writeCellStyle = new WriteCellStyle();
        writeCellStyle.setFillForegroundColor(Objects.nonNull(styleParam.getBgColor()) ? styleParam.getBgColor() : DEFAULT_BG_COLOR);
        writeCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
        writeCellStyle.setWrapped(true);

        CellStyle cellStyle = workbook.createCellStyle();
        // 克隆原有样式属性
        cellStyle.cloneStyleFrom(cell.getCellStyle());
        CellStyle newCellStyle = StyleUtil.buildCellStyle(workbook, cellStyle, writeCellStyle);
        newCellStyle.setFont(font);

        if (Objects.nonNull(styleParam.getAlignment())) {
            newCellStyle.setAlignment(styleParam.getAlignment());
        }

        // 设置新样式
        cell.setCellStyle(newCellStyle);
    }

    /**
     * 设置行高
     *
     * @param row
     * @param height
     */
    private void setRowHeight(Row row, short height) {
        if (row != null) {
            row.setHeight(height);
        }
    }

    /**
     * 写入器排序问题AbstractCellWriteHandler使用的默认序号是0,
     * EasyExcel自己的样式填充器FillStyleCellWriteHandler使用序号是50000(可在OrderConstant类中查到),
     * 也就是说我们在这个类中重写样式时又被easy excel重写回去了。
     * 解决方法是重写order方法使其大于50000 即可。
     *
     * @return
     */
    @Override
    public int order() {
        return 1000000;
    }

 使用SheetWriteHandler实现自定义下拉列表处理

        建一个名为SelectedSheetWriteHandler的类,并实现com.alibaba.excel.write.handler.SheetWriteHandler接口。这个接口中定义了一些回调方法,允许你在生成Excel文件的过程中进行自定义处理。
        重写afterSheetCreate方法:在SelectedSheetWriteHandler类中,实现afterSheetCreate方法。这个方法会在每个Sheet创建完成后被调用,我们可以在这里进行下拉列表的处理。我们可以在每个Sheet创建完成后,为指定的单元格添加下拉列表,并设置数据源。这样,我们就能更好地控制用户在Excel中输入的数据,提高数据的准确性和一致性。



import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.apache.poi.ss.usermodel.DataValidation;
import org.apache.poi.ss.usermodel.DataValidationConstraint;
import org.apache.poi.ss.usermodel.DataValidationHelper;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddressList;

import java.util.Map;
import java.util.Objects;

/**
 * <p>  excel设置下拉选项处理器   </p>

 */

@Data
@AllArgsConstructor
public class SelectedSheetWriteHandler implements SheetWriteHandler {
    private final Map<Integer, ExcelSelectedResolve> selectedMap;

    @Override
    public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
        if (CollectionUtil.isEmpty(selectedMap)) {
            return;
        }
        // 这里可以对cell进行任何操作
        Sheet sheet = writeSheetHolder.getSheet();
        DataValidationHelper helper = sheet.getDataValidationHelper();
        selectedMap.forEach((k, v) -> {
            if (Objects.isNull(v)) {
                return;
            }
            // 设置下拉列表的行: 首行,末行,首列,末列
            CellRangeAddressList rangeList = new CellRangeAddressList(v.getFirstRow(), v.getLastRow(), k, k);
            // 设置下拉列表的值
            DataValidationConstraint constraint = helper.createExplicitListConstraint(v.getSource());
            // 设置约束
            DataValidation validation = helper.createValidation(constraint, rangeList);
            // 阻止输入非下拉选项的值
            validation.setErrorStyle(DataValidation.ErrorStyle.STOP);
            validation.setShowErrorBox(true);
            validation.setSuppressDropDownArrow(true);
            validation.createErrorBox("提示", "请输入下拉选项中的内容");
            sheet.addValidationData(validation);
        });
    }
}

使用示例

示例一(多sheet页固定表头,支持动态下拉列表)

1:定义模版类

import com.alibaba.excel.annotation.ExcelProperty;
import com.yt.bi.goods.common.annotation.ExcelSelected;
import com.yt.bi.goods.common.constant.ExcelConstants;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.io.Serializable;


@Data
public class ProductSkuUpdateBasicsTemplateDTO implements Serializable {
    private static final long serialVersionUID = 1L;

    // 字符串的头背景设置成黄色 IndexedColors.PINK.getIndex()
    @ExcelProperty(value = {"导入说明:\n" +, "基础信息", "*SKU"}, index = 0)
    private String sku;

    @ExcelProperty(value = {"导入说明:\n" +, "基础信息", "产品名称"}, index = 1)
    private String productName;

    @ExcelProperty(value = {"导入说明:\n" +, "基础信息", "末级品类编码"}, index = 2)
    private String categoryCodeLast;

    @ExcelSelected(dynamicData = "bi_dict", dynamicParam = "product_origin_receiving", firstRow = 3)
    @ExcelProperty(value = {"导入说明:\n" +, "基础信息", "头程方式"}, index = 3)
    private String originReceiving;

    @ApiModelProperty("是否有配件 1是 0否")
    @ExcelSelected(source = {"是", "否"}, firstRow = 3)
    @ExcelProperty(value = {"导入说明:\n" +, "基础信息", "是否包含配件"}, index = 4)
    private String haveParts;

    @ApiModelProperty("是否反倾销 1=是; 0=否;")
    @ExcelSelected(source = {"是", "否"}, firstRow = 3)
    @ExcelProperty(value = {"导入说明:\n" +, "基础信息", "是否反倾销"}, index = 5)
    private String antiDumpingFlag;

    @ApiModelProperty("是否带电 1是 0否")
    @ExcelSelected(source = {"是", "否"}, firstRow = 3)
    @ExcelProperty(value = {"导入说明:\n" +, "基础信息", "是否带电"}, index = 6)
    private String electrifyFlag;

    @ApiModelProperty("主项目组")
    @ExcelSelected(dynamicData = "erp_dict", dynamicParam = "main_project_team", firstRow = 3)
    @ExcelProperty(value = {"导入说明:\n" +, "基础信息", "主项目组"}, index = 7)
    private String mainProjectTeam;

    @ApiModelProperty("输入电压")
    @ExcelSelected(dynamicData = "sku_voltage", firstRow = 3)
    @ExcelProperty(value = {"导入说明:\n" +, "基础信息", "输入电压"}, index = 8)
    private String voltage;

    @ApiModelProperty("产品开发人员")
    @ExcelProperty(value = {"导入说明:\n" +, "基础信息", "产品开发人员工号(多个人员请用&隔开)"}, index = 9)
    private String productDeveloper;

}


import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.ContentRowHeight;
import com.alibaba.excel.annotation.write.style.HeadFontStyle;
import com.alibaba.excel.annotation.write.style.HeadRowHeight;
import com.alibaba.excel.enums.BooleanEnum;
import lombok.Data;

import java.io.Serializable;


@HeadFontStyle(
        fontName = "宋体",
        color = Short.MAX_VALUE,
        fontHeightInPoints = 14
)
@HeadRowHeight(value = 30)
@ContentRowHeight(value = 20)
@ColumnWidth(value = 15)
@Data
public class DeveloperTemplateDTO implements Serializable {


    @ExcelProperty({"工号"})
    private String accountName;

    @ExcelProperty({"人员名称"})
    private String userName;
}
其中ProductSkuUpdateBasicsTemplateDTO模版类种的haveParts等字段为固定值下拉列表,mainProjectTeam等字段为动态值下拉列表,动态下拉列表数据提供示例如下:
/**
 * ERP数据字典处理类

 */
@Slf4j
@Component
@ExcelDynamicData(name = "erp_dict")
public class ErpDictDataRpcHandle implements ExcelDynamicSelectHandler {
   

    /**
     * 查询字典信息
     *
     * @param param
     * @return
     */
    @Override
    public String[] getSource(String dictType) {
        if (StringUtils.isBlank(param)) {
            return new String[0];
        }
        ErpDictDataQuery query = new ErpDictDataQuery();
        query.setTopFlag(Constants.ZERO);
        query.setDictType(dictType);
        List<ErpDictDataDTO> dictDataDTOList = erpDictList(query);// 查询数据库或其他方式获取数据
        if (CollectionUtils.isNotEmpty(dictDataDTOList)) {
            return dictDataDTOList.stream().map(ErpDictDataDTO::getDictLabel).toArray(String[]::new);
        }
        return new String[0];
    }
}

2:构建导出参数


    public void batchUpdateSkuImportTemplate(HttpServletResponse response) throws IOException {
        // 导入数据页
        List<EasyExcelExportDynamicParam> paramList = new ArrayList<>();
        EasyExcelExportDynamicParam param = new EasyExcelExportDynamicParam();
        param.setSheetName("导入数据页");
        param.setTemplate(ProductSkuUpdateBasicsTemplateDTO.class);
        param.setDataList(new ArrayList<>());
		// 构建样式,第三行,第一列背景色黄色,字体红色
        buildStyle(param);
        paramList.add(param);

		 // 人员对照表
        EasyExcelExportDynamicParam developerParam = new EasyExcelExportDynamicParam();
        developerParam.setSheetName("人员对照表");
        developerParam.setTemplate(DeveloperTemplateDTO.class);
        List<DeveloperTemplateDTO> templateDTOList = new ArrayList<>();
        developerParam.setDataList(templateDTOList);
        paramList.add(developerParam);

        EasyExcelUtil.exportExcel(response, paramList, "多sheet页导出");
    }

    /**
     * 样式
     *
     * @param param
     */
    private void buildStyle(EasyExcelExportDynamicParam param) {
        Map<Integer, Map<Integer, EasyExcelExportDynamicStyleParam>> rowStyleMap = new HashMap<>();
        Map<Integer, EasyExcelExportDynamicStyleParam> oneRowMap = new HashMap<>();
        Map<Integer, EasyExcelExportDynamicStyleParam> twoRowMap = new HashMap<>();
        Map<Integer, EasyExcelExportDynamicStyleParam> threeRowMap = new HashMap<>();

        EasyExcelExportDynamicStyleParam oneRowParam = EasyExcelExportDynamicStyleParam.builder()
                .bgColor(IndexedColors.WHITE.getIndex()).alignment(HorizontalAlignment.LEFT)
                .build();
        oneRowMap.put(0, oneRowParam);

        EasyExcelExportDynamicStyleParam twoRowParam = EasyExcelExportDynamicStyleParam.builder()
                .bgColor(IndexedColors.PALE_BLUE.getIndex())
                .build();
        twoRowMap.put(0, twoRowParam);

        EasyExcelExportDynamicStyleParam threeRowParam = EasyExcelExportDynamicStyleParam.builder()
                .bgColor(IndexedColors.YELLOW.getIndex())
                .fontColor(IndexedColors.RED.getIndex())
                .build();
        threeRowMap.put(0, threeRowParam);

        rowStyleMap.put(0, oneRowMap);
        rowStyleMap.put(1, twoRowMap);
        rowStyleMap.put(2, threeRowMap);
        param.setStyleMap(rowStyleMap);
    }

 3:导出结果示例

 示例二(多sheet页固定+动态表头,支持动态下拉列表,动态设置单元格格式)

1:定义固定表头模版类

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.*;
import com.alibaba.excel.enums.BooleanEnum;
import com.alibaba.excel.enums.poi.FillPatternTypeEnum;
import com.yt.bi.goods.common.annotation.ExcelSelected;
import com.yt.bi.goods.common.constant.ExcelConstants;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;

import java.io.Serializable;


@Data
public class ProductAddNormalSkuBasicsTemplateDTO implements Serializable {
    private static final long serialVersionUID = 1L;


    @ExcelProperty(value = {"导入说明:\n" +, "基础信息", "*国家"}, index = 0)
    private String country;


    @ExcelProperty(value = {"导入说明:\n" +, "基础信息", "*序列"}, index = 1)
    private String series;


    @ExcelProperty(value = {"导入说明:\n" +, "基础信息", "*产品名称"}, index = 2)
    private String productName;


    @ExcelSelected(dynamicData = "bi_dict", dynamicParam = "product_origin_receiving", firstRow = 3)
    @ExcelProperty(value = {"导入说明:\n" +, "基础信息", "*头程方式"}, index = 3)
    private String originReceiving;


    @ApiModelProperty("是否有配件 1是 0否")
    @ExcelSelected(source = {"是", "否"}, firstRow = 3)
    @ExcelProperty(value = {"导入说明:\n" +, "基础信息", "*是否包含配件"}, index = 4)
    private String haveParts;


    @ApiModelProperty("是否反倾销 1=是; 0=否;")
    @ExcelSelected(source = {"是", "否"}, firstRow = 3)
    @ExcelProperty(value = {"导入说明:\n" +, "基础信息", "*是否反倾销"}, index = 5)
    private String antiDumpingFlag;


    @ApiModelProperty("主项目组")
    @ExcelSelected(dynamicData = "erp_dict", dynamicParam = "main_project_team", firstRow = 3)
    @ExcelProperty(value = {"导入说明:\n" +, "基础信息", "*主项目组"}, index = 6)
    private String mainProjectTeam;


    @ApiModelProperty("是否带电 1是 0否")
    @ExcelSelected(source = {"是", "否"}, firstRow = 3)
    @ExcelProperty(value = {"导入说明:\n" +, "基础信息", "*是否带电"}, index = 7)
    private String electrifyFlag;


    @ExcelSelected(dynamicData = "sku_voltage", firstRow = 3)
    @ExcelProperty(value = {"导入说明:\n" +, "基础信息", "输入电压"}, index = 8)
    private String voltage;


    @ApiModelProperty("产品开发人员")
    @ExcelProperty(value = {"导入说明:\n" +, "基础信息", "产品开发人员工号(多个人员请用&隔开)"}, index = 9)
    private String productDeveloper;



    @ApiModelProperty("备注")
    @ExcelProperty(value = {"导入说明:\n" +, "基础信息", "备注"}, index = 10)
    private String remark;
}




@HeadFontStyle(
        fontName = "宋体",
        color = Short.MAX_VALUE,
        fontHeightInPoints = 14
)
@HeadRowHeight(value = 30)
@ContentRowHeight(value = 20)
@ColumnWidth(value = 15)
@Data
public class DeveloperTemplateDTO implements Serializable {


    @ExcelProperty({"工号"})
    private String accountName;

    @ExcelProperty({"人员名称"})
    private String userName;
}
其中ProductAddNormalSkuBasicsTemplateDTO模版类种的haveParts等字段为固定值下拉列表,mainProjectTeam等字段为动态值下拉列表,动态下拉列表数据提供示例如下:
/**
 * ERP数据字典处理类

 */
@Slf4j
@Component
@ExcelDynamicData(name = "erp_dict")
public class ErpDictDataRpcHandle implements ExcelDynamicSelectHandler {
   

    /**
     * 查询字典信息
     *
     * @param param
     * @return
     */
    @Override
    public String[] getSource(String dictType) {
        if (StringUtils.isBlank(param)) {
            return new String[0];
        }
        ErpDictDataQuery query = new ErpDictDataQuery();
        query.setTopFlag(Constants.ZERO);
        query.setDictType(dictType);
        List<ErpDictDataDTO> dictDataDTOList = erpDictList(query);// 查询数据库或其他方式获取数据
        if (CollectionUtils.isNotEmpty(dictDataDTOList)) {
            return dictDataDTOList.stream().map(ErpDictDataDTO::getDictLabel).toArray(String[]::new);
        }
        return new String[0];
    }
}

2:构建导出参数

public void batchAddSkuImportTemplate(HttpServletResponse response) {
        List<EasyExcelExportDynamicParam> paramList = new ArrayList<>();
        EasyExcelExportDynamicParam param = new EasyExcelExportDynamicParam();
  

        // 生成基础信息表头
        List<List<String>> listList = ExcelSelectedResolve.resolvePropertyAnnotation(ProductAddNormalSkuBasicsTemplateDTO.class);
        // 查询品类属性
        ProductCategoryAttributeValueDTO attributeValueDTO = productCategoryAttributeService.queryCategoryAttributeByCategoryCode(categoryCodeLast);
        // 生成规格属性表头
        List<ProductCategoryAttributeDTO> specAttributeList = attributeValueDTO.getSpecAttributeList();
        if (CollectionUtil.isNotEmpty(specAttributeList)) {
            List<List<String>> attributeNameList = specAttributeList.stream().map(x -> Arrays.asList("导入说明:\n" +, "产品属性-规格属性", attributeNameRequiredFlag(x))).collect(Collectors.toList());
            listList.addAll(attributeNameList);
        }
        // 生成销售属性表头
        List<ProductCategoryAttributeDTO> salesAttributeList = attributeValueDTO.getSalesAttributeList();
        if (CollectionUtil.isNotEmpty(salesAttributeList)) {
            List<List<String>> attributeNameList = salesAttributeList.stream().map(x -> Arrays.asList("导入说明:\n" +, "产品属性-销售属性", attributeNameRequiredFlag(x))).collect(Collectors.toList());
            listList.addAll(attributeNameList);
        }
        // 生成标签属性表头
        List<ProductCategoryAttributeDTO> tagAttributeList = attributeValueDTO.getTagAttributeList();
        if (CollectionUtil.isNotEmpty(tagAttributeList)) {
            List<List<String>> attributeNameList = tagAttributeList.stream().map(x -> Arrays.asList("导入说明:\n" +, "产品属性-标签属性", attributeNameRequiredFlag(x))).collect(Collectors.toList());
            listList.addAll(attributeNameList);
        }
        // 设置颜色
        Map<Integer, Map<Integer, EasyExcelExportDynamicStyleParam>> styleMap = new HashMap<>();
        Map<Integer, EasyExcelExportDynamicStyleParam> paramMap = new HashMap<>();
        // 判断第三行带*号列明都加上颜色
        Integer num = Constants.ZERO;
        for (List<String> line : listList) {
            String secondLineName = line.get(2);
            if (secondLineName.startsWith("*")) {
                EasyExcelExportDynamicStyleParam styleParam = EasyExcelExportDynamicStyleParam.builder()
                        .bgColor(IndexedColors.YELLOW.getIndex())
                        .fontColor(IndexedColors.RED.getIndex())
                        .build();
                paramMap.put(num, styleParam);
            }
            num++;
        }
        styleMap.put(2, paramMap);

        Map<Integer, EasyExcelExportDynamicStyleParam> one = new HashMap<>();
        EasyExcelExportDynamicStyleParam oneParam = EasyExcelExportDynamicStyleParam.builder()
                .bgColor(IndexedColors.WHITE.getIndex())
                .alignment(HorizontalAlignment.LEFT)
                .build();
        one.put(0, oneParam);
        styleMap.put(0, one);
        param.setStyleMap(styleMap);
        param.setDataList(new ArrayList<>());
        param.setDynamicHeaderList(listList);
        // 生成基础信息下拉
        Map<Integer, ExcelSelectedResolve> head = ExcelSelectedResolve.resolveSelectedAnnotation(clazz);
        param.setSelectedMap(head);
        param.setSheetName("导入数据页");
        paramList.add(param);
        
		 // 人员对照表
        EasyExcelExportDynamicParam developerParam = new EasyExcelExportDynamicParam();
        developerParam.setSheetName("人员对照表");
        developerParam.setTemplate(DeveloperTemplateDTO.class);
        List<DeveloperTemplateDTO> templateDTOList = new ArrayList<>();
        developerParam.setDataList(templateDTOList);
        paramList.add(developerParam);

        EasyExcelUtil.exportExcel(response, paramList, "多sheet页导出");

    }

 3:导出结果示例

注意:当使用动态表头和固定表头组合生成时,需要统一把表头单元格字段内容写入到List<List<String>> 当中。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1007301.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

处理:对于移动类型Z21和帐户8051010100 供应商货物移动 (014)的不同的字段选择

对于移动类型Z21和帐户8051010100 供应商货物移动 (014)的不同的字段选择 消息编号 M7093 诊断 来自 移动类型Z21和 总帐科目8051010100的字段选择串的比较显示了在一点或更多点处有不兼容的字段选择组合。 字段选择不同的字段&#xff1a;供应商货物移动 范例 在移动类型屏幕上…

JavaScript逻辑题:输出1000之内的所有完数。所谓完数指的是:如果一个数恰好等于它的所有因子之和,这个数就称为完数。

// 定义函数function judgeNum(){// 定义数组存储完数let arr []// for循环1000以内的所有数for(let i 1;i<1000;i){// 定义sum存储一个数的因子之和let sum 0;// 内层循环一个数的因子for(let j 1;j<i;j){if(i % j 0){sum j;}}// 如果一个数和它的因子之和相等&am…

【idea】将已有项目文件夹添加为项目模块

前后效果 变更方法 适用场景 项目为maven项目适用&#xff0c;如果不是maven项目不适用

IDEA指定Maven settings file文件未生效

背景&#xff1a;在自己电脑上配置的时候&#xff0c;由于公司项目和我自己的项目的Maven仓库不一致&#xff0c;我就在项目中指定了各自的Maven配置文件。但是我发现公司的项目私有仓库地址IDEA总是识别不到&#xff01; 俩个配置文件分别是&#xff1a; /Users/sml/Mine/研发…

Vue3 快速入门和模板语法

vite方式建立项目 1.安装vue vite组件 # 安装组件或更新 npm i vite vue -g# 建立项目 npm init vue v301 cd v301 npm run dev# 建立项目 vue create vue v302 cd v302 npm run dev 2. 配置项目vite.config.ts import {fileURLToPath, URL} from node:urlimport {defineConfi…

web-案例

分页插件 登录 事务

Docker进阶:Docker Compose(容器编排) 管理多容器应用—实战案例演示

Docker进阶&#xff1a;Docker Compose&#xff08;容器编排&#xff09; 管理多容器应用—实战案例演示 一、Docker Compose简介二、Docker Compose安装三、Docker Compose卸载四、Docker Compose核心概念4.1、一文件原则&#xff08;docker-compose.yml&#xff09;4.2、服务…

时间序列场景下多种数据填充算法实践与对比分析

在时间序列建模任务中&#xff0c;模型往往对于缺失数据是比较敏感的&#xff0c;大量的缺失数据甚至会导致训练出来的模型完全不可用&#xff0c;在我前面的博文中也有写到过数据填充相关的内容&#xff0c;感兴趣的话可以自行移步阅读即可&#xff1a; 《python 基于滑动平均…

硬件学习 PAD9.5 day01 原理图布局开始设置, 元器件的调用和绘制, 新建库, 库添加元器件,

1. 原理图的布局设置 1.1 打开布局设置界面 1.2. 布局设置界面 1.3. 界面大小设置 自己 点击框里的 向下的箭头 获取更多的选择。 1.4 包括白框的的大小 2. 元器件的调用 和绘制 2.1. 打开一个库 和 新建一个库 1. 进入新建库的界面 和选择系统自带的库 2.2 绘制新的器件的…

【计算机视觉 | 目标检测】干货:目标检测常见算法介绍合集(四)

文章目录 四十六、Parallel Feature Pyramid Network四十七、ScanSSD四十七、RetinaMask四十八、CornerNet-Saccade四十九、CentripetalNet五十、Fast Focal Detection Network五十一、CornerNet-Squeeze五十二、Paddle Anchor Free Network五十三、Human Robot Interaction Pi…

[Rust GUI]eframe(egui框架)代码示例

-2、eframe代替品 你可以使用egui的其他绑定&#xff0c;例如&#xff1a;egui-miniquad&#xff0c;bevy_egui&#xff0c;egui_sdl2_gl 等。 -1、注意 egui库相当于核心库&#xff0c;需要借助eframe框架就可以写界面了。 eframe使用egui_glow渲染&#xff0c;而egui_glow…

华为云云耀云服务器L实例评测 | 强大性能与高可靠性的完美结合

华为云云耀云服务器L实例评测 | 瑞吉外卖下载与部署_软工菜鸡的博客-CSDN博客 上次发布了一篇手把手带领读者在华为云服务器搭建后端程序员无人不知的 瑞吉外卖 项目&#xff0c;效果良好&#xff0c;很多粉丝给我反馈还想学习别的项目以及其它软件的服务器部署&#xff0c;这不…

Mobpush与A/B测试:覆盖多应用场景下的精细化运营神器

在信息爆炸的移动应用领域&#xff0c;实现长效稳定的用户增长的关键在于能够和用户建立互信、持久的联系。而优质的推送内容不仅可以提高用户参与度和留存率&#xff0c;还有助于增加收入、改善用户体验&#xff0c;以及建立强大的用户社区。但千人一面的推送很难同时满足不同…

网络安全宣传周|探索AI数字人的魅力和价值所在

9月11日至9月17日是国家网络安全宣传周&#xff0c;在福州举办的安全博览会上有着多种人工智能模型产品亮相现场&#xff0c;吸引着众多参观者的目光&#xff0c;尤其是AI数字人面对不同的问题、不同的场景都可以进行实时响应&#xff0c;不同于冷冰冰的传统智能客服的对话场景…

c++day6---9.13

思维导图&#xff1a; 改变类型只需将选择功能函数中的Zhan<double> z;中的double改为相对的类型: 栈&#xff1a; 头文件&#xff1a; #ifndef ZHAN_H #define ZHAN_H #include <iostream>using namespace std;template<typename T> class Zhan { privat…

RK3399 android7.1 实现双wifi功能 STA+AP

wifi模组&#xff1a; 主板使用的wifi模块为海华AW-NM43438W模组以及客户提供了一款USB接口的5G双频无线网卡RTL8821CU。 双wifi功能实现效果&#xff1a; 主板自带的wifi模组作为station正常连接外部wifi,USB接口的外接网卡作为AP&#xff08;热点&#xff09;供其他设备连接…

JavaScript逻辑题:牙膏2元 牙刷5元 牙膏盒15元 请问正好花完100元 有多少情况?

// 定义牙膏 牙刷 牙膏盒分别的价格 let toothpaste 0;let toothbrush 0;let toothpastebox 0;// 定义sum用来存储几种情况let sum 0;//第一层循环 循环牙膏买多少for (let i 0; i < 20; i){toothpaste 5 * i;// 二层循环 循环牙刷的数量for (let j 0; j < 50; j…

【Java Web】HTML 标签 总结

目录 1.HTML 2.标签 1. head 标签 1.图标 2.样式居中 2. body 标签 1.注释 &#xff1a; 2.加载图片 3.加载视频 效果 4.区域 效果 5.上下跳转&#xff0c;页面跳转 效果 6.表格 效果 7.有序列表&#xff0c;无序列表 效果 8.登录 效果 9.按钮 10.多选框…

目标分类笔记(一): 利用包含多个网络多种训练策略的框架来完成多目标分类任务(从数据准备到训练测试部署的完整流程)

目标分类 一、目标分类介绍1.1 二分类和多分类的区别1.2 单标签和多标签输出的区别 二、代码获取三、数据集准备四、环境搭建4.1 环境测试 五、模型训练六、模型测试6.1 多标签训练-单标签输出结果6.2 多标签训练-多标签输出结果 一、目标分类介绍 目标分类是一种监督学习任务…

使用flask实现一个简单的代理服务

背景: 有一些客户的服务是我本地windows电脑开vpn之后才能访问的。为了让公司内网的别的电脑不开vpn也能正常请求客户的接口&#xff0c;方便调试&#xff0c;所以使用我的windows电脑实现一个代理的功能。 原理简单画个图: 功能简单直接上代码: from flask import Flask, re…