|
@@ -1,7 +1,9 @@
|
|
|
package cn.iocoder.yudao.framework.excel.core.handler;
|
|
|
|
|
|
import cn.hutool.core.collection.CollUtil;
|
|
|
+import cn.hutool.core.lang.Assert;
|
|
|
import cn.iocoder.yudao.framework.common.core.KeyValue;
|
|
|
+import cn.iocoder.yudao.framework.excel.core.enums.ExcelColumn;
|
|
|
import com.alibaba.excel.write.handler.SheetWriteHandler;
|
|
|
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
|
|
|
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
|
|
@@ -11,6 +13,8 @@ import org.apache.poi.ss.util.CellRangeAddressList;
|
|
|
|
|
|
import java.util.Comparator;
|
|
|
import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.stream.Collectors;
|
|
|
|
|
|
/**
|
|
|
* 基于固定 sheet 实现下拉框
|
|
@@ -20,18 +24,20 @@ import java.util.List;
|
|
|
public class SelectSheetWriteHandler implements SheetWriteHandler {
|
|
|
|
|
|
private static final String DICT_SHEET_NAME = "字典sheet";
|
|
|
+ public static final int FIRST_ROW = 1; // 数据起始行从 0 开始,本项目第一行有标题所以从 1 开始如果您的 Excel 有多行标题请自行更改
|
|
|
+ public static final int LAST_ROW = 2000; // 下拉列需要创建下拉框的行数,默认两千行如需更多请自行调整
|
|
|
+ private final List<KeyValue<ExcelColumn, List<String>>> selectMap;
|
|
|
|
|
|
- // TODO @puhui999:key 不使用 int 值么?感觉不是很优雅哈。
|
|
|
- private final List<KeyValue<Integer, List<String>>> selectMap;
|
|
|
-
|
|
|
- private static final char[] ALPHABET = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
|
|
|
- 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};
|
|
|
-
|
|
|
- public SelectSheetWriteHandler(List<KeyValue<Integer, List<String>>> selectMap) {
|
|
|
+ public SelectSheetWriteHandler(List<KeyValue<ExcelColumn, List<String>>> selectMap) {
|
|
|
if (CollUtil.isEmpty(selectMap)) {
|
|
|
this.selectMap = null;
|
|
|
return;
|
|
|
}
|
|
|
+ // 校验一下 key 是否唯一
|
|
|
+ Map<String, Long> nameCounts = selectMap.stream()
|
|
|
+ .collect(Collectors.groupingBy(item -> item.getKey().name(), Collectors.counting()));
|
|
|
+ Assert.isFalse(nameCounts.entrySet().stream().allMatch(entry -> entry.getValue() > 1), "下拉数据 key 重复请排查!!!");
|
|
|
+
|
|
|
selectMap.sort(Comparator.comparing(item -> item.getValue().size())); // 升序不然创建下拉会报错
|
|
|
this.selectMap = selectMap;
|
|
|
}
|
|
@@ -41,16 +47,14 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
|
|
|
if (CollUtil.isEmpty(selectMap)) {
|
|
|
return;
|
|
|
}
|
|
|
- // 需要设置下拉框的 sheet 页
|
|
|
- Sheet currentSheet = writeSheetHolder.getSheet();
|
|
|
- DataValidationHelper helper = currentSheet.getDataValidationHelper();
|
|
|
- Workbook workbook = writeWorkbookHolder.getWorkbook();
|
|
|
|
|
|
- // 数据字典的 sheet 页
|
|
|
+ // 1.1 获取相应操作对象
|
|
|
+ DataValidationHelper helper = writeSheetHolder.getSheet().getDataValidationHelper(); // 需要设置下拉框的 sheet 页的数据验证助手
|
|
|
+ Workbook workbook = writeWorkbookHolder.getWorkbook(); // 获得工作簿
|
|
|
+
|
|
|
+ // 1.2 创建数据字典的 sheet 页
|
|
|
Sheet dictSheet = workbook.createSheet(DICT_SHEET_NAME);
|
|
|
- for (KeyValue<Integer, List<String>> keyValue : selectMap) {
|
|
|
- // 设置下拉单元格的首行、末行、首列、末列
|
|
|
- CellRangeAddressList rangeAddressList = new CellRangeAddressList(1, 65533, keyValue.getKey(), keyValue.getKey());
|
|
|
+ for (KeyValue<ExcelColumn, List<String>> keyValue : selectMap) {
|
|
|
int rowLen = keyValue.getValue().size();
|
|
|
// 设置字典 sheet 页的值 每一列一个字典项
|
|
|
for (int i = 0; i < rowLen; i++) {
|
|
@@ -58,58 +62,42 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
|
|
|
if (row == null) {
|
|
|
row = dictSheet.createRow(i);
|
|
|
}
|
|
|
- row.createCell(keyValue.getKey()).setCellValue(keyValue.getValue().get(i));
|
|
|
+ row.createCell(keyValue.getKey().getColNum()).setCellValue(keyValue.getValue().get(i));
|
|
|
}
|
|
|
-
|
|
|
- // TODO @puhui999:下面 1. 2.1 2.2 2.3 我是按照已经理解的,调整了下格式;这样可读性更好;在 52 到 62 行,你可以看看,是不是也弄下序号;
|
|
|
- // 1. 创建可被其他单元格引用的名称
|
|
|
- Name name = workbook.createName();
|
|
|
- // TODO @puhui999:下面的 excelColumn 和 refers 两行,是不是可以封装成一个方法,替代 getExcelColumn;
|
|
|
- String excelColumn = getExcelColumn(keyValue.getKey());
|
|
|
- String refers = DICT_SHEET_NAME + "!$" + excelColumn + "$1:$" + excelColumn + "$" + rowLen; // 下拉框数据来源 eg:字典sheet!$B1:$B2
|
|
|
- name.setNameName("dict" + keyValue.getKey()); // 设置名称的名字
|
|
|
- name.setRefersToFormula(refers); // 设置公式
|
|
|
-
|
|
|
- // 2.1 设置约束
|
|
|
- DataValidationConstraint constraint = helper.createFormulaListConstraint("dict" + keyValue.getKey()); // 设置引用约束
|
|
|
- DataValidation validation = helper.createValidation(constraint, rangeAddressList);
|
|
|
- if (validation instanceof HSSFDataValidation) {
|
|
|
- validation.setSuppressDropDownArrow(false);
|
|
|
- } else {
|
|
|
- validation.setSuppressDropDownArrow(true);
|
|
|
- validation.setShowErrorBox(true);
|
|
|
- }
|
|
|
- // 2.2 阻止输入非下拉框的值
|
|
|
- validation.setErrorStyle(DataValidation.ErrorStyle.STOP);
|
|
|
- validation.createErrorBox("提示", "此值不存在于下拉选择中!");
|
|
|
- // 2.3 添加下拉框约束
|
|
|
- writeSheetHolder.getSheet().addValidationData(validation);
|
|
|
+ // 1.3 设置单元格下拉选择
|
|
|
+ setColSelect(writeSheetHolder, workbook, helper, keyValue);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 将数字列转化成为字母列
|
|
|
- *
|
|
|
- * @param num 数字
|
|
|
- * @return 字母
|
|
|
+ * 设置单元格下拉选择
|
|
|
*/
|
|
|
- // TODO @puhui999:这个是必须字母列哇?还是数字其实也可以哈?主要想看看,怎么能把这个逻辑,进一步简化
|
|
|
- private String getExcelColumn(int num) {
|
|
|
- String column;
|
|
|
- int len = ALPHABET.length - 1;
|
|
|
- int first = num / len;
|
|
|
- int second = num % len;
|
|
|
- if (num <= len) {
|
|
|
- column = ALPHABET[num] + "";
|
|
|
+ private static void setColSelect(WriteSheetHolder writeSheetHolder, Workbook workbook, DataValidationHelper helper,
|
|
|
+ KeyValue<ExcelColumn, List<String>> keyValue) {
|
|
|
+ // 1.1 创建可被其他单元格引用的名称
|
|
|
+ Name name = workbook.createName();
|
|
|
+ String excelColumn = keyValue.getKey().name();
|
|
|
+ // 1.2 下拉框数据来源 eg:字典sheet!$B1:$B2
|
|
|
+ String refers = DICT_SHEET_NAME + "!$" + excelColumn + "$1:$" + excelColumn + "$" + keyValue.getValue().size();
|
|
|
+ name.setNameName("dict" + keyValue.getKey()); // 设置名称的名字
|
|
|
+ name.setRefersToFormula(refers); // 设置公式
|
|
|
+ // 2.1 设置约束
|
|
|
+ DataValidationConstraint constraint = helper.createFormulaListConstraint("dict" + keyValue.getKey()); // 设置引用约束
|
|
|
+ // 设置下拉单元格的首行、末行、首列、末列
|
|
|
+ CellRangeAddressList rangeAddressList = new CellRangeAddressList(FIRST_ROW, LAST_ROW,
|
|
|
+ keyValue.getKey().getColNum(), keyValue.getKey().getColNum());
|
|
|
+ DataValidation validation = helper.createValidation(constraint, rangeAddressList);
|
|
|
+ if (validation instanceof HSSFDataValidation) {
|
|
|
+ validation.setSuppressDropDownArrow(false);
|
|
|
} else {
|
|
|
- column = ALPHABET[first - 1] + "";
|
|
|
- if (second == 0) {
|
|
|
- column = column + ALPHABET[len];
|
|
|
- } else {
|
|
|
- column = column + ALPHABET[second - 1];
|
|
|
- }
|
|
|
+ validation.setSuppressDropDownArrow(true);
|
|
|
+ validation.setShowErrorBox(true);
|
|
|
}
|
|
|
- return column;
|
|
|
+ // 2.2 阻止输入非下拉框的值
|
|
|
+ validation.setErrorStyle(DataValidation.ErrorStyle.STOP);
|
|
|
+ validation.createErrorBox("提示", "此值不存在于下拉选择中!");
|
|
|
+ // 2.3 添加下拉框约束
|
|
|
+ writeSheetHolder.getSheet().addValidationData(validation);
|
|
|
}
|
|
|
|
|
|
}
|