疯狂的狮子Li
2025-01-20 3c8d864b5f68af5167199e0d5c9ff6c0c5852638
ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java
@@ -5,7 +5,9 @@
import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.metadata.FieldCache;
import com.alibaba.excel.metadata.FieldWrapper;
import com.alibaba.excel.util.ClassUtils;
import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
@@ -18,6 +20,7 @@
import org.dromara.common.core.service.DictService;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.core.utils.StreamUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.annotation.ExcelEnumFormat;
@@ -52,6 +55,7 @@
     * 下拉可选项
     */
    private final List<DropDownOptions> dropDownOptions;
    private final DictService dictService;
    /**
     * 当前单选进度
     */
@@ -60,7 +64,6 @@
     * 当前联动选择进度
     */
    private int currentLinkedOptionsSheetIndex;
    private final DictService dictService;
    public ExcelDownHandler(List<DropDownOptions> options) {
        this.dropDownOptions = options;
@@ -83,42 +86,39 @@
        Sheet sheet = writeSheetHolder.getSheet();
        // 开始设置下拉框 HSSFWorkbook
        DataValidationHelper helper = sheet.getDataValidationHelper();
        Field[] fields = writeWorkbookHolder.getClazz().getDeclaredFields();
        Workbook workbook = writeWorkbookHolder.getWorkbook();
        int length = fields.length;
        for (int i = 0; i < length; i++) {
        FieldCache fieldCache = ClassUtils.declaredFields(writeWorkbookHolder.getClazz(), writeWorkbookHolder);
        for (Map.Entry<Integer, FieldWrapper> entry : fieldCache.getSortedFieldMap().entrySet()) {
            Integer index = entry.getKey();
            FieldWrapper wrapper = entry.getValue();
            Field field = wrapper.getField();
            // 循环实体中的每个属性
            // 可选的下拉值
            List<String> options = new ArrayList<>();
            if (fields[i].isAnnotationPresent(ExcelDictFormat.class)) {
            if (field.isAnnotationPresent(ExcelDictFormat.class)) {
                // 如果指定了@ExcelDictFormat,则使用字典的逻辑
                ExcelDictFormat format = fields[i].getDeclaredAnnotation(ExcelDictFormat.class);
                ExcelDictFormat format = field.getDeclaredAnnotation(ExcelDictFormat.class);
                String dictType = format.dictType();
                String converterExp = format.readConverterExp();
                if (StrUtil.isNotBlank(dictType)) {
                if (StringUtils.isNotBlank(dictType)) {
                    // 如果传递了字典名,则依据字典建立下拉
                    Collection<String> values = Optional.ofNullable(dictService.getAllDictByDictType(dictType))
                        .orElseThrow(() -> new ServiceException(String.format("字典 %s 不存在", dictType)))
                        .values();
                    options = new ArrayList<>(values);
                } else if (StrUtil.isNotBlank(converterExp)) {
                } else if (StringUtils.isNotBlank(converterExp)) {
                    // 如果指定了确切的值,则直接解析确切的值
                    options = StrUtil.split(converterExp, format.separator(), true, true);
                    List<String> strList = StringUtils.splitList(converterExp, format.separator());
                    options = StreamUtils.toList(strList, s -> StringUtils.split(s, "=")[1]);
                }
            } else if (fields[i].isAnnotationPresent(ExcelEnumFormat.class)) {
            } else if (field.isAnnotationPresent(ExcelEnumFormat.class)) {
                // 否则如果指定了@ExcelEnumFormat,则使用枚举的逻辑
                ExcelEnumFormat format = fields[i].getDeclaredAnnotation(ExcelEnumFormat.class);
                ExcelEnumFormat format = field.getDeclaredAnnotation(ExcelEnumFormat.class);
                List<Object> values = EnumUtil.getFieldValues(format.enumClass(), format.textField());
                options = StreamUtils.toList(values, String::valueOf);
            }
            if (ObjectUtil.isNotEmpty(options)) {
                // 仅当下拉可选项不为空时执行
                // 获取列下标,默认为当前循环次数
                int index = i;
                if (fields[i].isAnnotationPresent(ExcelProperty.class)) {
                    // 如果指定了列下标,以指定的为主
                    index = fields[i].getDeclaredAnnotation(ExcelProperty.class).index();
                }
                if (options.size() > 20) {
                    // 这里限制如果可选项大于20,则使用额外表形式
                    dropDownWithSheet(helper, workbook, sheet, index, options);
@@ -139,8 +139,8 @@
            } else if (everyOptions.getOptions().size() > 10) {
                // 当一级选项参数个数大于10,使用额外表的形式
                dropDownWithSheet(helper, workbook, sheet, everyOptions.getIndex(), everyOptions.getOptions());
            } else if (everyOptions.getOptions().size() != 0) {
                // 当一级选项个数不为空,使用默认形式
            } else {
                // 否则使用默认形式
                dropDownWithSimple(helper, sheet, everyOptions.getIndex(), everyOptions.getOptions());
            }
        });
@@ -171,9 +171,23 @@
        Sheet linkedOptionsDataSheet = workbook.createSheet(WorkbookUtil.createSafeSheetName(linkedOptionsSheetName));
        // 将下拉表隐藏
        workbook.setSheetHidden(workbook.getSheetIndex(linkedOptionsDataSheet), true);
        // 完善横向的一级选项数据表
        // 选项数据
        List<String> firstOptions = options.getOptions();
        Map<String, List<String>> secoundOptionsMap = options.getNextOptions();
        // 采用按行填充数据的方式,避免EasyExcel出现数据无法写入的问题
        // Attempting to write a row in the range that is already written to disk
        // 使用ArrayList记载数据,防止乱序
        List<String> columnNames = new ArrayList<>();
        // 写入第一行,即第一级的数据
        Row firstRow = linkedOptionsDataSheet.createRow(0);
        for (int columnIndex = 0; columnIndex < firstOptions.size(); columnIndex++) {
            String columnName = firstOptions.get(columnIndex);
            firstRow.createCell(columnIndex)
                .setCellValue(columnName);
            columnNames.add(columnName);
        }
        // 创建名称管理器
        Name name = workbook.createName();
@@ -190,28 +204,12 @@
        // 设置数据校验为序列模式,引用的是名称管理器中的别名
        this.markOptionsToSheet(helper, sheet, options.getIndex(), helper.createFormulaListConstraint(linkedOptionsSheetName));
        for (int columIndex = 0; columIndex < firstOptions.size(); columIndex++) {
            // 先提取主表中一级下拉的列名
        // 创建二级选项的名称管理器
        for (int columIndex = 0; columIndex < columnNames.size(); columIndex++) {
            // 列名
            String firstOptionsColumnName = getExcelColumnName(columIndex);
            // 一次循环是每一个一级选项
            int finalI = columIndex;
            // 本次循环的一级选项值
            String thisFirstOptionsValue = firstOptions.get(columIndex);
            // 创建第一行的数据
            Optional.ofNullable(linkedOptionsDataSheet.getRow(0))
                // 如果不存在则创建第一行
                .orElseGet(() -> linkedOptionsDataSheet.createRow(finalI))
                // 第一行当前列
                .createCell(columIndex)
                // 设置值为当前一级选项值
                .setCellValue(thisFirstOptionsValue);
            // 第二行开始,设置第二级别选项参数
            List<String> secondOptions = secoundOptionsMap.get(thisFirstOptionsValue);
            if (CollUtil.isEmpty(secondOptions)) {
                // 必须保证至少有一个关联选项,否则将导致Excel解析错误
                secondOptions = Collections.singletonList("暂无_0");
            }
            // 对应的一级值
            String thisFirstOptionsValue = columnNames.get(columIndex);
            // 以该一级选项值创建子名称管理器
            Name sonName = workbook.createName();
@@ -222,7 +220,9 @@
                linkedOptionsSheetName,
                firstOptionsColumnName,
                firstOptionsColumnName,
                secondOptions.size() + 1
                // 二级选项存在则设置为(选项个数+1)行,否则设置为2行
                Math.max(Optional.ofNullable(secoundOptionsMap.get(thisFirstOptionsValue))
                    .orElseGet(ArrayList::new).size(), 1) + 1
            );
            // 设置名称管理器的引用位置
            sonName.setRefersToFormula(sonFunction);
@@ -235,25 +235,51 @@
                // 二级只能主表每一行的每一列添加二级校验
                markLinkedOptionsToSheet(helper, sheet, i, options.getNextIndex(), helper.createFormulaListConstraint(secondOptionsFunction));
            }
        }
            for (int rowIndex = 0; rowIndex < secondOptions.size(); rowIndex++) {
                // 从第二行开始填充二级选项
                int finalRowIndex = rowIndex + 1;
                int finalColumIndex = columIndex;
                Row row = Optional.ofNullable(linkedOptionsDataSheet.getRow(finalRowIndex))
                    // 没有则创建
                    .orElseGet(() -> linkedOptionsDataSheet.createRow(finalRowIndex));
                Optional
                    // 在本级一级选项所在的列
                    .ofNullable(row.getCell(finalColumIndex))
                    // 不存在则创建
                    .orElseGet(() -> row.createCell(finalColumIndex))
                    // 设置二级选项值
                    .setCellValue(secondOptions.get(rowIndex));
        // 将二级数据处理为按行区分
        Map<Integer, List<String>> columnValueMap = new HashMap<>();
        int currentRow = 1;
        while (currentRow >= 0) {
            boolean flag = false;
            List<String> rowData = new ArrayList<>();
            for (String columnName : columnNames) {
                List<String> data = secoundOptionsMap.get(columnName);
                if (CollUtil.isEmpty(data)) {
                    // 添加空字符串填充位置
                    rowData.add(" ");
                    continue;
                }
                // 取第一个
                String str = data.get(0);
                rowData.add(str);
                // 通过移除的方式避免重复
                data.remove(0);
                // 设置可以继续
                flag = true;
            }
            columnValueMap.put(currentRow, rowData);
            // 可以继续,则增加行数,否则置为负数跳出循环
            if (flag) {
                currentRow++;
            } else {
                currentRow = -1;
            }
        }
        // 填充第二级选项数据
        columnValueMap.forEach((rowIndex, rowValues) -> {
            Row row = linkedOptionsDataSheet.createRow(rowIndex);
            for (int columnIndex = 0; columnIndex < rowValues.size(); columnIndex++) {
                String rowValue = rowValues.get(columnIndex);
                // 填充位置的部分不渲染
                if (StrUtil.isNotBlank(rowValue)) {
                    row.createCell(columnIndex)
                        .setCellValue(rowValue);
                }
            }
        });
        currentLinkedOptionsSheetIndex++;
    }