easyexcel导出excel模板时,标题带下拉框及其下拉值过多不显示问题

背景:easyexcel导出excel模板时,标题带下拉框及其下拉值过多不显示问题。
使用的easyexcel版本:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>2.2.4</version>
</dependency>

实现:
1、自定义注解类

import java.lang.annotation.*;

/**
 * 标记导出excel的下拉数据集
 *
 * @return result
 * @since 2021-01:15 14:22:09
 */
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DropDownField {

    /**
     * 固定下拉内容
     *
     * @return result
     * @since 2021-01:15 14:21:35
     */
    String[] source() default {};

    /**
     * 动态下拉内容
     *
     * @return result
     * @since 2021-01:15 14:22:30
     */
    Class[] sourceClass() default {};

    /**
     * 设置字典常量值
     *
     * @return result
     * @since 2021-01:15 14:22:30
     */
    String value() default "";

    /**
     * 设置DB类
     *
     * @return result
     * @since 2021-01:15 14:22:30
     */
    Class<?> clazz() default java.lang.Object.class;

    /**
     * 标识是DICT还是DB或其他;true;DB或其他;false:dict
     *
     * @return result
     * @since 2021-01:15 14:22:30
     */
    boolean type() default true;
}

2、实体类

import com.alibaba.excel.annotation.ExcelProperty;
import com.test.constant.Constant;
import com.test.service.TestService;
import lombok.Data;

/**
 * Description
 *
 * @Since 2020-12-07
 */
@Data
public class TestExcel {

    /**
     * 姓名
     */
    @ExcelProperty(value = "姓名")
    private String name;

    /**
     * 部门
     */
    @ExcelProperty(value = "部门")
    @DropDownField(sourceClass = TestDropDownSetImpl.class, clazz = TestService.class)
    private String department;

    /**
     * 类型
     */
    @ExcelProperty(value = "类型")
    @DropDownField(sourceClass = TestDropDownSetImpl.class, value = Constant.TYPE, type = false)
    private String type;
}

3、获取数据源的接口及其实现类
接口

import com.test.service.DictBizService;

/**
 * @since 2021/1/15
 */
public abstract class AbstractDropDown {

    /**
     * 获取数据源
     *
     * @param clzz DB 类
     * @return result
     * @since 2021-01:15 16:20:44
     */
    abstract String[] getSourceByDb(Class clzz);

    /**
     * 获取数据源
     *
     * @param dictBizService 字典对象
     * @param dictConstant      字典常量
     * @return result
     * @since 2021-01:15 16:20:44
     */
    String[] getSourceByDict(DictBizService dictBizService, String dictConstant){
        if (ObjectUtil.isNotNull(dictConstant)) {
            Map<String, String> map = dictBizService.getMap(dictConstant);
            String[] valueArray = new String[map.size()];
            return map.values().toArray(valueArray);
        }
        return new String[0];
    }
}

实现类

import com.test.vo.TestVO;
import com.test.service.TestService;
import com.test.service.DictBizService;
import cn.hutool.core.util.ObjectUtil;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @since 2021/1/15
 */
@Component
public class ProductCostDropDownSetImpl implements AbstractDropDown {

    @Override
    public String[] getSourceByDb(Class clzz) {
        if (clzz.isAssignableFrom(TestService.class)) {
            TestService testService= new TestService();
            List<TestVO> list = testService.getDepartment();
            List<String> valueList = list.parallelStream().map(TestVO::getDepartment).collect(Collectors.toList());
            String[] valueArray = new String[valueList.size()];
            return valueList.toArray(valueArray);
        }
        return new String[0];
    }
}

4、解析注解获取动态或静态下拉框值的接口及其实现类
接口

/**
 * @since 2021/1/15
 */
public interface ResolveDropAnnotation {
    /**
     * 解析注解
     *
     * @param dropDownField annotation
     * @return result
     * @since 2021-01:15 18:35:42
     */
    String[] resolve(DropDownField dropDownField);
}

实现类

import com.test.service.DictBizService;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Arrays;
import java.util.Optional;

/**
 * @since 2021/1/15
 */
@Component
public class TestResolveDropAnnotation implements ResolveDropAnnotation {

    @Resource
    @Lazy
    DictBizService dictBizService;

    @Override
    public String[] resolve(DropDownField dropDownField) {
        if (!Optional.ofNullable(dropDownField).isPresent()) {
            return new String[0];
        }

        // 获取固定下拉信息
        String[] source = dropDownField.source();
        if (source.length > 0) {
            return source;
        }

        // 获取动态的下拉数据
        Class<? extends AbstractDropDown>[] classes = dropDownField.sourceClass();
        try {
            AbstractDropDown abstractDropDown = Arrays.stream(classes).findFirst().get().newInstance();
            boolean type = dropDownField.type();
            String[] dynamicSource;
            if (type) {
                Class clazz = dropDownField.clazz();
                dynamicSource = abstractDropDown.getSourceByDb(clazz);
            } else {
                String dictConstant = dropDownField.value();
                dynamicSource = abstractDropDown.getSourceByDict(dictBizService, dictConstant);
            }
            if (null != dynamicSource && dynamicSource.length > 0) {
                return dynamicSource;
            }
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return new String[0];
    }
}

5、下拉框值处理及映射到相应的行列中

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.*;
import org.apache.poi.ss.util.CellRangeAddressList;

import java.util.Map;

/**
 * @since 2021/1/15
 */

public class ExcelCellWriteHandler implements SheetWriteHandler {

    private final Map<Integer, String[]> map;

    /**
     * 设置阈值,避免生成的导入模板下拉值获取不到
     */
    private static final Integer LIMIT_NUMBER = 100;

    public ExcelCellWriteHandler(Map<Integer, String[]> map) {
        this.map = map;
    }

    @Override
    public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
    }

    @Override
    public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
        // 这里可以对cell进行任何操作
        Sheet sheet = writeSheetHolder.getSheet();
        DataValidationHelper helper = sheet.getDataValidationHelper();

        // k 为存在下拉数据集的单元格下表 v为下拉数据集
        map.forEach((k, v) -> {
            // 设置下拉单元格的首行 末行 首列 末列
            CellRangeAddressList rangeList = new CellRangeAddressList(1, 65536, k, k);
            // 如果下拉值总数大于100,则使用一个新sheet存储,避免生成的导入模板下拉值获取不到
            if (v.length > LIMIT_NUMBER) {
                //定义sheet的名称
                //1.创建一个隐藏的sheet 名称为 hidden + k
                String sheetName = "hidden" + k;
                Workbook workbook = writeWorkbookHolder.getWorkbook();
                Sheet hiddenSheet = workbook.createSheet(sheetName);
                for (int i = 0, length = v.length; i < length; i++) {
                    // 开始的行数i,列数k
                    hiddenSheet.createRow(i).createCell(k).setCellValue(v[i]);
                }
                Name category1Name = workbook.createName();
                category1Name.setNameName(sheetName);
                String excelLine = getExcelLine(k);
                // =hidden!$H:$1:$H$50  sheet为hidden的 H1列开始H50行数据获取下拉数组
                String refers = "=" + sheetName + "!$" + excelLine + "$1:$" + excelLine + "$" + (v.length + 1);
                // 将刚才设置的sheet引用到你的下拉列表中
                DataValidationConstraint constraint = helper.createFormulaListConstraint(refers);
                DataValidation dataValidation = helper.createValidation(constraint, rangeList);
                writeSheetHolder.getSheet().addValidationData(dataValidation);
                // 设置存储下拉列值得sheet为隐藏
                int hiddenIndex = workbook.getSheetIndex(sheetName);
                if (!workbook.isSheetHidden(hiddenIndex)) {
                    workbook.setSheetHidden(hiddenIndex, true);
                }
            }
            // 下拉列表约束数据
            DataValidationConstraint constraint = helper.createExplicitListConstraint(v);
            // 设置约束
            DataValidation validation = helper.createValidation(constraint, rangeList);
            // 阻止输入非下拉选项的值
            validation.setErrorStyle(DataValidation.ErrorStyle.STOP);
            validation.setShowErrorBox(true);
            validation.setSuppressDropDownArrow(true);
            validation.createErrorBox("提示", "此值与单元格定义格式不一致");
            // validation.createPromptBox("填写说明:","填写内容只能为下拉数据集中的单位,其他单位将会导致无法入仓");
            sheet.addValidationData(validation);
        });
    }

    /**
     * 返回excel列标A-Z-AA-ZZ
     *
     * @param num 列数
     * @return java.lang.String
     */
    private String getExcelLine(int num) {
        String line = "";
        int first = num / 26;
        int second = num % 26;
        if (first > 0) {
            line = (char) ('A' + first - 1) + "";
        }
        line += (char) ('A' + second) + "";
        return line;
    }
}

6、导出service

import com.test.service.TestService;

@Service
public class TestService {
    @Resource
    ResolveDropAnnotation resolveDropAnnotation;
	public void export(HttpServletResponse response) throws IOException {
        try {
            // 获取该类声明的所有字段
            Field[] fields = TestExcel.class.getDeclaredFields();
            // 响应字段对应的下拉集合
            Map<Integer, String[]> map = new HashMap<>();
            Field field;
            // 循环判断哪些字段有下拉数据集,并获取
            for (int i = NumberEnum.ZERO.getNumber(); i < fields.length; i++) {
                field = fields[i];
                // 解析注解信息
                DropDownField dropDownField = field.getAnnotation(DropDownField.class);
                if (null != dropDownField) {
                    String[] sources = resolveDropAnnotation.resolve(dropDownField);
                    if (null != sources && sources.length > NumberEnum.ZERO.getNumber()) {
                        map.put(i, sources);
                    }
                }
            }
            ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream(), ProductCostExcel.class)
                    .registerWriteHandler(new ExcelCellWriteHandler(map)).build();
            WriteSheet sheet = EasyExcel.writerSheet(NumberEnum.ZERO.getNumber(), "导出明细").build();
            excelWriter.write(null, sheet);
            excelWriter.finish();
        } catch (Throwable var6) {
            log.error("导出模板异常!");
            throw var6;
        }
    }
	
	public List<TestVO> getDepartment(){
		System.out.println("获取部门的方法...");
	}
}

7、controller

import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import com.test.service.TestService;

@RestController
@RequestMapping("/test")
public class TestController {
	@Resource
	TestService testService;
	/**
     * 下载模板
     */
    @GetMapping("/export")
    @ApiOperationSupport(order = 1)
    @ApiOperation(value = "下载模板", notes = "下载模板")
    public void export(HttpServletResponse response) {
        try {
            test.Service.export(response);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在做导出时,需要用到下拉框值做值选入,避免出现手动输入危险。

reference link:
https://blog.csdn.net/qq_44605317/article/details/107376876?utm_medium=distribute.pc_relevant.none-task-blog-OPENSEARCH-3.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-OPENSEARCH-3.control

你可能感兴趣的:(util,JavaSE学习笔记,easyexcel,easyexcel,下拉框,easyexcel,下拉值过多)