houzhongyi
2024-07-11 e7c1260db32209a078a962aaa0ad5492c35774fb
提交 | 用户 | 时间
e7c126 1 package com.iailab.framework.excel.core.handler;
H 2
3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.lang.Assert;
5 import cn.hutool.core.map.MapUtil;
6 import cn.hutool.core.util.ObjectUtil;
7 import cn.hutool.core.util.StrUtil;
8 import cn.hutool.extra.spring.SpringUtil;
9 import cn.hutool.poi.excel.ExcelUtil;
10 import com.iailab.framework.common.core.KeyValue;
11 import com.iailab.framework.dict.core.DictFrameworkUtils;
12 import com.iailab.framework.excel.core.annotations.ExcelColumnSelect;
13 import com.iailab.framework.excel.core.function.ExcelColumnSelectFunction;
14 import com.alibaba.excel.annotation.ExcelProperty;
15 import com.alibaba.excel.write.handler.SheetWriteHandler;
16 import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
17 import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
18 import lombok.extern.slf4j.Slf4j;
19 import org.apache.poi.hssf.usermodel.HSSFDataValidation;
20 import org.apache.poi.ss.usermodel.*;
21 import org.apache.poi.ss.util.CellRangeAddressList;
22
23 import java.lang.reflect.Field;
24 import java.util.Comparator;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28
29 import static com.iailab.framework.common.util.collection.CollectionUtils.convertList;
30
31 /**
32  * 基于固定 sheet 实现下拉框
33  *
34  * @author HUIHUI
35  */
36 @Slf4j
37 public class SelectSheetWriteHandler implements SheetWriteHandler {
38
39     /**
40      * 数据起始行从 0 开始
41      *
42      * 约定:本项目第一行有标题所以从 1 开始如果您的 Excel 有多行标题请自行更改
43      */
44     public static final int FIRST_ROW = 1;
45     /**
46      * 下拉列需要创建下拉框的行数,默认两千行如需更多请自行调整
47      */
48     public static final int LAST_ROW = 2000;
49
50     private static final String DICT_SHEET_NAME = "字典sheet";
51
52     /**
53      * key: 列 value: 下拉数据源
54      */
55     private final Map<Integer, List<String>> selectMap = new HashMap<>();
56
57     public SelectSheetWriteHandler(Class<?> head) {
58         // 加载下拉数据获取接口
59         Map<String, ExcelColumnSelectFunction> beansMap = SpringUtil.getBeanFactory().getBeansOfType(ExcelColumnSelectFunction.class);
60         if (MapUtil.isEmpty(beansMap)) {
61             return;
62         }
63
64         // 解析下拉数据
65         int colIndex = 0;
66         for (Field field : head.getDeclaredFields()) {
67             if (field.isAnnotationPresent(ExcelColumnSelect.class)) {
68                 ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
69                 if (excelProperty != null && excelProperty.index() != -1) {
70                     colIndex = excelProperty.index();
71                 }
72                 getSelectDataList(colIndex, field);
73             }
74             colIndex++;
75         }
76     }
77
78     /**
79      * 获得下拉数据,并添加到 {@link #selectMap} 中
80      *
81      * @param colIndex 列索引
82      * @param field    字段
83      */
84     private void getSelectDataList(int colIndex, Field field) {
85         ExcelColumnSelect columnSelect = field.getAnnotation(ExcelColumnSelect.class);
86         String dictType = columnSelect.dictType();
87         String functionName = columnSelect.functionName();
88         Assert.isTrue(ObjectUtil.isNotEmpty(dictType) || ObjectUtil.isNotEmpty(functionName),
89                 "Field({}) 的 @ExcelColumnSelect 注解,dictType 和 functionName 不能同时为空", field.getName());
90
91         // 情况一:使用 dictType 获得下拉数据
92         if (StrUtil.isNotEmpty(dictType)) { // 情况一: 字典数据 (默认)
93             selectMap.put(colIndex, DictFrameworkUtils.getDictDataLabelList(dictType));
94             return;
95         }
96
97         // 情况二:使用 functionName 获得下拉数据
98         Map<String, ExcelColumnSelectFunction> functionMap = SpringUtil.getApplicationContext().getBeansOfType(ExcelColumnSelectFunction.class);
99         ExcelColumnSelectFunction function = CollUtil.findOne(functionMap.values(), item -> item.getName().equals(functionName));
100         Assert.notNull(function, "未找到对应的 function({})", functionName);
101         selectMap.put(colIndex, function.getOptions());
102     }
103
104     @Override
105     public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
106         if (CollUtil.isEmpty(selectMap)) {
107             return;
108         }
109
110         // 1. 获取相应操作对象
111         DataValidationHelper helper = writeSheetHolder.getSheet().getDataValidationHelper(); // 需要设置下拉框的 sheet 页的数据验证助手
112         Workbook workbook = writeWorkbookHolder.getWorkbook(); // 获得工作簿
113         List<KeyValue<Integer, List<String>>> keyValues = convertList(selectMap.entrySet(), entry -> new KeyValue<>(entry.getKey(), entry.getValue()));
114         keyValues.sort(Comparator.comparing(item -> item.getValue().size())); // 升序不然创建下拉会报错
115
116         // 2. 创建数据字典的 sheet 页
117         Sheet dictSheet = workbook.createSheet(DICT_SHEET_NAME);
118         for (KeyValue<Integer, List<String>> keyValue : keyValues) {
119             int rowLength = keyValue.getValue().size();
120             // 2.1 设置字典 sheet 页的值,每一列一个字典项
121             for (int i = 0; i < rowLength; i++) {
122                 Row row = dictSheet.getRow(i);
123                 if (row == null) {
124                     row = dictSheet.createRow(i);
125                 }
126                 row.createCell(keyValue.getKey()).setCellValue(keyValue.getValue().get(i));
127             }
128             // 2.2 设置单元格下拉选择
129             setColumnSelect(writeSheetHolder, workbook, helper, keyValue);
130         }
131     }
132
133     /**
134      * 设置单元格下拉选择
135      */
136     private static void setColumnSelect(WriteSheetHolder writeSheetHolder, Workbook workbook, DataValidationHelper helper,
137                                         KeyValue<Integer, List<String>> keyValue) {
138         // 1.1 创建可被其他单元格引用的名称
139         Name name = workbook.createName();
140         String excelColumn = ExcelUtil.indexToColName(keyValue.getKey());
141         // 1.2 下拉框数据来源 eg:字典sheet!$B1:$B2
142         String refers = DICT_SHEET_NAME + "!$" + excelColumn + "$1:$" + excelColumn + "$" + keyValue.getValue().size();
143         name.setNameName("dict" + keyValue.getKey()); // 设置名称的名字
144         name.setRefersToFormula(refers); // 设置公式
145
146         // 2.1 设置约束
147         DataValidationConstraint constraint = helper.createFormulaListConstraint("dict" + keyValue.getKey()); // 设置引用约束
148         // 设置下拉单元格的首行、末行、首列、末列
149         CellRangeAddressList rangeAddressList = new CellRangeAddressList(FIRST_ROW, LAST_ROW,
150                 keyValue.getKey(), keyValue.getKey());
151         DataValidation validation = helper.createValidation(constraint, rangeAddressList);
152         if (validation instanceof HSSFDataValidation) {
153             validation.setSuppressDropDownArrow(false);
154         } else {
155             validation.setSuppressDropDownArrow(true);
156             validation.setShowErrorBox(true);
157         }
158         // 2.2 阻止输入非下拉框的值
159         validation.setErrorStyle(DataValidation.ErrorStyle.STOP);
160         validation.createErrorBox("提示", "此值不存在于下拉选择中!");
161         // 2.3 添加下拉框约束
162         writeSheetHolder.getSheet().addValidationData(validation);
163     }
164
165 }