百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

easyexcel导出时如何设置单元格批注和下拉框列表

toyiye 2024-06-21 12:09 60 浏览 0 评论

一、概述

springboot集成easyexcel实现通用异步导出平台

导出需求存在多样化,有时我们会需要在导出时新增几列,然后再进行导入更新操作;导出新增的列比如性别、城市等等,表头设置批注信息,提醒用户填写时的注意事项;单元格设置下拉框,让用户方便选择,减少不必要的填写错误;本文将介绍下easyexcel导出时如何设置单元格批注和下拉框列表。

二、实战

案例基于springboot集成easyexcel实现通用异步导出平台实现

1、新增批注注解:ExcelAnnotation

import java.lang.annotation.Target;
import java.lang.annotation.Retention;
import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelAnnotation {

    /**
     * 批注内容
     */
    String value() default "";
}

2、新增下拉框注解:ExcelDropdown

import java.lang.annotation.Target;
import java.lang.annotation.Retention;
import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelDropdown {

    /**
     * 下拉框内容
     */
    String value() default "";
}

3、学生实体类(Student)

import lombok.Data;
import java.io.Serializable;
import com.alibaba.excel.annotation.ExcelProperty;
import com.easyexcel.demo.annotation.ExcelDropdown;
import com.easyexcel.demo.annotation.ExcelAnnotation;

@Data
public class Student implements Serializable {

    private static final long serialVersionUID = 7685359843020686195L;

    /**
     * 主键值
     */
    @ExcelProperty(value = "主键值", index = 0)
    private Integer id;

    /**
     * 学生姓名
     */
    @ExcelProperty(value = "学生姓名", index = 1)
    private String stuName;

    /**
     * 学生编码
     */
    @ExcelProperty(value = "学生编码", index = 2)
    private String stuCode;

    /**
     * 性别
     */
    @ExcelProperty(value = "性别", index = 3)
    @ExcelAnnotation(value = "选择学生性别") // 单元格批注信息
    @ExcelDropdown(value = "男,女") // 单元格下拉框
    private String gender;

    /**
     * 城市名称
     */
    @ExcelProperty(value = "城市名称", index = 4)
    @ExcelAnnotation(value = "选择城市名称") // 单元格批注信息
    private String cityName;
}

4、导出模板类改造(ExportTemplate)

解析单元格批注、和下拉框注解、并注册WriteHandler

import com.alibaba.fastjson.JSON;
import com.easyexcel.demo.entity.Student;
import lombok.extern.slf4j.Slf4j;
import com.alibaba.excel.ExcelWriter;
import com.google.common.collect.Maps;
import com.easyexcel.demo.so.ExportSo;
import com.alibaba.excel.EasyExcelFactory;
import com.easyexcel.demo.so.TaskDefinition;
import org.springframework.stereotype.Service;
import com.easyexcel.demo.entity.ExportTaskEntity;
import com.easyexcel.demo.mapper.ExportTaskMapper;
import com.alibaba.excel.annotation.ExcelProperty;
import com.easyexcel.demo.annotation.ExcelDropdown;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.easyexcel.demo.service.ExportBaseService;
import com.easyexcel.demo.annotation.ExcelAnnotation;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;

import java.util.Map;
import java.util.Date;
import java.util.List;
import java.util.Arrays;
import java.lang.reflect.Field;
import javax.annotation.Resource;

@Slf4j
@Service
public class ExportTemplate {

    @Resource
    private ExportTaskMapper exportTaskMapper;

    public void exportData(ExportSo exportSo, String taskId) {
        String businessTypeCode = exportSo.getBusinessTypeCode();
        TaskDefinition taskDefinition = TaskDefinition.getTaskDefinition(businessTypeCode);
        if (null == taskDefinition) {
            throw new IllegalArgumentException("导出业务类型编码异常!");
        }
        String fileName = taskDefinition.getFileName();
        String filePath = "D:\\excel\\" + fileName + ".xlsx";
        ExportTaskEntity updateExportTaskEntity = new ExportTaskEntity();
        updateExportTaskEntity.setTaskId(taskId);
        updateExportTaskEntity.setFilePath(filePath);
        updateExportTaskEntity.setUpdateTime(new Date());
        updateExportTaskEntity.setTaskStatusCode(TaskStatusEnum.SUCCESS.getCode());
        updateExportTaskEntity.setTaskStatusName(TaskStatusEnum.SUCCESS.getDesc());
        try {
            // 执行导出业务逻辑(查询数据、写入excel)
            Class clazz = parseClazz(taskDefinition.getExportClass());
          	// 注意:此处需设置表头实体类对象:Student.class
            ExcelWriter excelWriter = EasyExcelFactory.write(filePath, Student.class).build();
            String exportService = taskDefinition.getExportService();
            ExportBaseService exportBaseService = (ExportBaseService) SpringUtils.getBean(exportService);

            Map<Integer, List<String>> dropdownDataMap = Maps.newHashMap();
            Map<Integer, String> annotationDataMap = Maps.newHashMap();
            // 解析列下拉框列表数据、和列批注数据
            parseAnnotationData(clazz, dropdownDataMap, annotationDataMap);
          	// 注册ReportCellWriteHandler,且needHead设置为true
            WriteSheet writeSheet = EasyExcelFactory.writerSheet(fileName).registerWriteHandler(new  (dropdownDataMap, annotationDataMap)).needHead(true).build();
            exportBaseService.export(exportSo, excelWriter, writeSheet);
            excelWriter.finish();
        } catch (Exception e) {
            updateExportTaskEntity.setTaskStatusCode(TaskStatusEnum.FAIL.getCode());
            updateExportTaskEntity.setTaskStatusName(TaskStatusEnum.FAIL.getDesc());
            log.error("导出任务异常 任务ID: {} 导出参数: {} 异常信息: ", taskId, JSON.toJSONString(exportSo), e);
        }
        // 更新导出任务
        exportTaskMapper.updateExportTask(updateExportTaskEntity);
    }

    private Class parseClazz(String exportClass) {
        if (StringUtils.isBlank(exportClass)) {
            throw new IllegalArgumentException("导出实体类全类名不可为空!");
        }
        Class clazz = null;
        try {
            clazz = Class.forName(exportClass);
        } catch (ClassNotFoundException e) {
            log.warn("parseClazz error: ", e);
        }
        if (null == clazz) {
            throw new RuntimeException("clazz解析异常");
        }
        return clazz;
    }

    private void parseAnnotationData(Class clazz, Map<Integer, List<String>> dropdownDataMap, Map<Integer, String> annotationDataMap) {
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (!field.isAnnotationPresent(ExcelProperty.class)) {
                continue;
            }
            // 解析列索引
            ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
            int index = excelProperty.index();
            if (field.isAnnotationPresent(ExcelDropdown.class)) {
                // 解析下拉框列表
                ExcelDropdown excelDropdown = field.getAnnotation(ExcelDropdown.class);
                dropdownDataMap.put(index, Arrays.asList(excelDropdown.value().split(",")));
            }
            if (field.isAnnotationPresent(ExcelAnnotation.class)) {
                // 解析列批注
                ExcelAnnotation excelAnnotationComment = field.getAnnotation(ExcelAnnotation.class);
                annotationDataMap.put(index, excelAnnotationComment.value());
            }
        }
    }
}

5、新增ReportCellWriteHandler(实现CellWriteHandler接口,重写afterCellDispose方法)

import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;
import com.alibaba.excel.metadata.Head;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.util.CellRangeAddressList;
import com.alibaba.excel.metadata.data.WriteCellData;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.commons.collections4.CollectionUtils;
import com.alibaba.excel.write.handler.CellWriteHandler;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;

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

@Slf4j
public class ReportCellWriteHandler implements CellWriteHandler {

    /**
     * 下拉框数据
     */
    private final Map<Integer, List<String>> dropDownDataMap;

    /**
     * 批注数据
     */
    private final Map<Integer, String> annotationDataMap;

    public ReportCellWriteHandler(Map<Integer, List<String>> dropDownDataMap, Map<Integer, String> annotationDataMap) {
        this.dropDownDataMap = dropDownDataMap;
        this.annotationDataMap = annotationDataMap;
    }

    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder,
                                 WriteTableHolder writeTableHolder,
                                 List<WriteCellData<?>> cellDataList,
                                 Cell cell,
                                 Head head,
                                 Integer relativeRowIndex,
                                 Boolean isHead) {
        if (null == cell || null == cell.getRow()) {
            return;
        }
        try {
            if (cell.getRow().getRowNum() != 0) { // 非表头处理
                // 设置下拉框数据
                dealDropdownData(writeSheetHolder, cell.getRowIndex(), cell.getColumnIndex());
            } else { // 表头处理
                // 设置批注信息
                dealAnnotationData(writeSheetHolder, cell);
            }
        } catch (Exception e) {
            log.warn("afterCellDispose error: ", e);
        }
    }

    /**
     * 设置下拉框数据
     *
     * @param writeSheetHolder sheet holder
     * @param rowIndex         行号
     * @param columnIndex      列号
     */
    private void dealDropdownData(WriteSheetHolder writeSheetHolder, int rowIndex, int columnIndex) {
        if (dropDownDataMap.isEmpty() || CollectionUtils.isEmpty(dropDownDataMap.get(columnIndex))) {
            return;
        }
        Sheet sheet = writeSheetHolder.getSheet();
        DataValidationHelper helper = sheet.getDataValidationHelper();
        // 设置下拉列表的行: 首行,末行(目前限制3w),首列,末列
        CellRangeAddressList rangeList = new CellRangeAddressList(rowIndex, 30000, columnIndex, columnIndex);
        // 设置下拉列表的值
        DataValidationConstraint constraint;
        // 直接设置下拉选
        constraint = helper.createExplicitListConstraint(dropDownDataMap.get(columnIndex).toArray(new String[0]));
        // 设置约束
        DataValidation validation = helper.createValidation(constraint, rangeList);
        // 阻止输入非下拉选项的值
        validation.setErrorStyle(DataValidation.ErrorStyle.STOP);
        validation.setShowErrorBox(true);
        validation.setSuppressDropDownArrow(true);
        validation.createErrorBox("提示", "请输入下拉选项中的内容");
        sheet.addValidationData(validation);
    }

    private void dealAnnotationData(WriteSheetHolder writeSheetHolder, Cell cell) {
        if (annotationDataMap.isEmpty() || StringUtils.isBlank(annotationDataMap.get(cell.getColumnIndex()))) {
            return;
        }
        int columnIndex = cell.getColumnIndex();
        int rowIndex = cell.getRowIndex();
        Drawing<?> drawing = writeSheetHolder.getSheet().createDrawingPatriarch();
        Comment comment = drawing.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, columnIndex, rowIndex, columnIndex + 5, rowIndex + 5));
        comment.setString(new XSSFRichTextString(annotationDataMap.get(columnIndex)));
        cell.setCellComment(comment);
    }
}

6、发送测试请求

接口:
http://localhost:8008/export/addExportTaskInfo

POST请求

参数:

{
    "businessTypeCode": "1", // 导出任务定义中定义的业务类型枚举
    "exportParam": { // 这里是具体的业务参数;比如根据城市导出学生信息
    }
}

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码