Browse Source

fixed(级联下拉框数据错误)

1.修复过多级联下拉选项导致的无法写入问题
Emil.Zhang 6 tháng trước cách đây
mục cha
commit
0385a444c2

+ 67 - 41
ruoyi-common/ruoyi-common-excel/src/main/java/org/dromara/common/excel/core/ExcelDownHandler.java

@@ -55,6 +55,7 @@ public class ExcelDownHandler implements SheetWriteHandler {
      * 下拉可选项
      */
     private final List<DropDownOptions> dropDownOptions;
+    private final DictService dictService;
     /**
      * 当前单选进度
      */
@@ -63,7 +64,6 @@ public class ExcelDownHandler implements SheetWriteHandler {
      * 当前联动选择进度
      */
     private int currentLinkedOptionsSheetIndex;
-    private final DictService dictService;
 
     public ExcelDownHandler(List<DropDownOptions> options) {
         this.dropDownOptions = options;
@@ -139,8 +139,8 @@ public class ExcelDownHandler implements SheetWriteHandler {
             } 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,10 +171,24 @@ public class ExcelDownHandler implements SheetWriteHandler {
         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 @@ public class ExcelDownHandler implements SheetWriteHandler {
         // 设置数据校验为序列模式,引用的是名称管理器中的别名
         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 @@ public class ExcelDownHandler implements SheetWriteHandler {
                 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 @@ public class ExcelDownHandler implements SheetWriteHandler {
                 // 二级只能主表每一行的每一列添加二级校验
                 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++;
     }
 

+ 31 - 17
ruoyi-modules/ruoyi-demo/src/main/java/org/dromara/demo/service/impl/ExportExcelServiceImpl.java

@@ -1,5 +1,6 @@
 package org.dromara.demo.service.impl;
 
+import cn.hutool.core.util.RandomUtil;
 import cn.hutool.core.util.StrUtil;
 import jakarta.servlet.http.HttpServletResponse;
 import lombok.Data;
@@ -121,8 +122,9 @@ public class ExportExcelServiceImpl implements IExportExcelService {
         List<DemoCityData> provinceList = new ArrayList<>();
 
         // 实际业务中一般采用数据库读取的形式,这里直接拼接创建
-        provinceList.add(new DemoCityData(0, null, "安徽省"));
-        provinceList.add(new DemoCityData(1, null, "江苏省"));
+        provinceList.add(new DemoCityData(0, null, "P100000"));
+        provinceList.add(new DemoCityData(1, null, "P200000"));
+        provinceList.add(new DemoCityData(2, null, "P300000"));
 
         return provinceList;
     }
@@ -137,11 +139,11 @@ public class ExportExcelServiceImpl implements IExportExcelService {
         List<DemoCityData> cityList = new ArrayList<>();
 
         // 实际业务中一般采用数据库读取的形式,这里直接拼接创建
-        cityList.add(new DemoCityData(0, 0, "合肥市"));
-        cityList.add(new DemoCityData(1, 0, "芜湖市"));
-        cityList.add(new DemoCityData(2, 1, "南京市"));
-        cityList.add(new DemoCityData(3, 1, "无锡市"));
-        cityList.add(new DemoCityData(4, 1, "徐州市"));
+        cityList.add(new DemoCityData(0, 0, "C110000"));
+        cityList.add(new DemoCityData(1, 0, "C120000"));
+        cityList.add(new DemoCityData(2, 1, "C210000"));
+        cityList.add(new DemoCityData(3, 1, "C220000"));
+        cityList.add(new DemoCityData(4, 1, "C230000"));
 
         selectParentData(provinceList, cityList);
 
@@ -157,17 +159,29 @@ public class ExportExcelServiceImpl implements IExportExcelService {
     private List<DemoCityData> getAreaList(List<DemoCityData> cityList) {
         List<DemoCityData> areaList = new ArrayList<>();
 
+        int minCount = 500;
+        int maxCount = 10000;
+
         // 实际业务中一般采用数据库读取的形式,这里直接拼接创建
-        areaList.add(new DemoCityData(0, 0, "瑶海区"));
-        areaList.add(new DemoCityData(1, 0, "庐江区"));
-        areaList.add(new DemoCityData(2, 1, "南宁县"));
-        areaList.add(new DemoCityData(3, 1, "镜湖区"));
-        areaList.add(new DemoCityData(4, 2, "玄武区"));
-        areaList.add(new DemoCityData(5, 2, "秦淮区"));
-        areaList.add(new DemoCityData(6, 3, "宜兴市"));
-        areaList.add(new DemoCityData(7, 3, "新吴区"));
-        areaList.add(new DemoCityData(8, 4, "鼓楼区"));
-        areaList.add(new DemoCityData(9, 4, "丰县"));
+        for (int i = 0; i < RandomUtil.randomInt(minCount, maxCount); i++) {
+            areaList.add(new DemoCityData(areaList.size(), 0, String.format("A11%04d", i)));
+        }
+
+        for (int i = 0; i < RandomUtil.randomInt(minCount, maxCount); i++) {
+            areaList.add(new DemoCityData(areaList.size(), 1, String.format("A12%04d", i)));
+        }
+
+        for (int i = 0; i < RandomUtil.randomInt(minCount, maxCount); i++) {
+            areaList.add(new DemoCityData(areaList.size(), 2, String.format("A21%04d", i)));
+        }
+
+        for (int i = 0; i < RandomUtil.randomInt(minCount, maxCount); i++) {
+            areaList.add(new DemoCityData(areaList.size(), 3, String.format("A22%04d", i)));
+        }
+
+        for (int i = 0; i < RandomUtil.randomInt(minCount, maxCount); i++) {
+            areaList.add(new DemoCityData(areaList.size(), 4, String.format("A23%04d", i)));
+        }
 
         selectParentData(cityList, areaList);