|  | @@ -1,9 +1,12 @@
 | 
	
		
			
				|  |  |  package cn.iocoder.yudao.framework.excel.core.handler;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  import cn.hutool.core.collection.CollUtil;
 | 
	
		
			
				|  |  | -import cn.hutool.core.lang.Assert;
 | 
	
		
			
				|  |  | +import cn.hutool.core.map.MapUtil;
 | 
	
		
			
				|  |  | +import cn.hutool.extra.spring.SpringUtil;
 | 
	
		
			
				|  |  |  import cn.iocoder.yudao.framework.common.core.KeyValue;
 | 
	
		
			
				|  |  | -import cn.iocoder.yudao.framework.excel.core.enums.ExcelColumn;
 | 
	
		
			
				|  |  | +import cn.iocoder.yudao.framework.excel.core.annotations.ExcelColumnSelect;
 | 
	
		
			
				|  |  | +import cn.iocoder.yudao.framework.excel.core.service.ExcelColumnSelectDataService;
 | 
	
		
			
				|  |  | +import com.alibaba.excel.annotation.ExcelProperty;
 | 
	
		
			
				|  |  |  import com.alibaba.excel.write.handler.SheetWriteHandler;
 | 
	
		
			
				|  |  |  import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
 | 
	
		
			
				|  |  |  import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
 | 
	
	
		
			
				|  | @@ -11,10 +14,11 @@ import org.apache.poi.hssf.usermodel.HSSFDataValidation;
 | 
	
		
			
				|  |  |  import org.apache.poi.ss.usermodel.*;
 | 
	
		
			
				|  |  |  import org.apache.poi.ss.util.CellRangeAddressList;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -import java.util.Comparator;
 | 
	
		
			
				|  |  | -import java.util.List;
 | 
	
		
			
				|  |  | -import java.util.Map;
 | 
	
		
			
				|  |  | -import java.util.stream.Collectors;
 | 
	
		
			
				|  |  | +import java.lang.annotation.Annotation;
 | 
	
		
			
				|  |  | +import java.lang.reflect.Field;
 | 
	
		
			
				|  |  | +import java.util.*;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  /**
 | 
	
		
			
				|  |  |   * 基于固定 sheet 实现下拉框
 | 
	
	
		
			
				|  | @@ -23,6 +27,9 @@ import java.util.stream.Collectors;
 | 
	
		
			
				|  |  |   */
 | 
	
		
			
				|  |  |  public class SelectSheetWriteHandler implements SheetWriteHandler {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    private static final List<String> ALPHABET = getExcelColumnNameList();
 | 
	
		
			
				|  |  | +    private static final List<ExcelColumnSelectDataService> EXCEL_COLUMN_SELECT_DATA_SERVICES = new ArrayList<>();
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |      /**
 | 
	
		
			
				|  |  |       * 数据起始行从 0 开始
 | 
	
		
			
				|  |  |       *
 | 
	
	
		
			
				|  | @@ -36,21 +43,46 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      private static final String DICT_SHEET_NAME = "字典sheet";
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    // TODO @puhui999:Map<ExcelColumn, List<String>> 可以么?之前用 keyvalue 的原因,返回给前端,无法用 linkedhashmap,默认 key 会乱序
 | 
	
		
			
				|  |  | -    private final List<KeyValue<ExcelColumn, List<String>>> selectMap;
 | 
	
		
			
				|  |  | +    private final List<KeyValue<Integer, List<String>>> selectMap = new ArrayList<>(); // 使用 List + KeyValue 组合方便排序
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    public SelectSheetWriteHandler(List<KeyValue<ExcelColumn, List<String>>> selectMap) {
 | 
	
		
			
				|  |  | -        if (CollUtil.isEmpty(selectMap)) {
 | 
	
		
			
				|  |  | -            this.selectMap = null;
 | 
	
		
			
				|  |  | +    public SelectSheetWriteHandler(Class<?> head) {
 | 
	
		
			
				|  |  | +        // 加载下拉数据获取接口
 | 
	
		
			
				|  |  | +        Map<String, ExcelColumnSelectDataService> beansMap = SpringUtil.getBeanFactory().getBeansOfType(ExcelColumnSelectDataService.class);
 | 
	
		
			
				|  |  | +        if (MapUtil.isEmpty(beansMap)) {
 | 
	
		
			
				|  |  |              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 重复请排查!!!");
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | +        if (CollUtil.isEmpty(EXCEL_COLUMN_SELECT_DATA_SERVICES) || EXCEL_COLUMN_SELECT_DATA_SERVICES.size() != beansMap.values().size()) {
 | 
	
		
			
				|  |  | +            EXCEL_COLUMN_SELECT_DATA_SERVICES.clear();
 | 
	
		
			
				|  |  | +            EXCEL_COLUMN_SELECT_DATA_SERVICES.addAll(convertList(beansMap.values(), b -> b));
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        // 解析下拉数据
 | 
	
		
			
				|  |  | +        Map<String, Field> excelPropertyFields = getFieldsWithAnnotation(head, ExcelProperty.class);
 | 
	
		
			
				|  |  | +        Map<String, Field> excelColumnSelectFields = getFieldsWithAnnotation(head, ExcelColumnSelect.class);
 | 
	
		
			
				|  |  | +        int colIndex = 0;
 | 
	
		
			
				|  |  | +        for (String fieldName : excelPropertyFields.keySet()) {
 | 
	
		
			
				|  |  | +            Field field = excelColumnSelectFields.get(fieldName);
 | 
	
		
			
				|  |  | +            if (field != null) {
 | 
	
		
			
				|  |  | +                getSelectDataList(colIndex, field);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            colIndex++;
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  |          selectMap.sort(Comparator.comparing(item -> item.getValue().size())); // 升序不然创建下拉会报错
 | 
	
		
			
				|  |  | -        this.selectMap = selectMap;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 获得下拉数据
 | 
	
		
			
				|  |  | +     *
 | 
	
		
			
				|  |  | +     * @param colIndex 列索引
 | 
	
		
			
				|  |  | +     * @param field    字段
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    private void getSelectDataList(int colIndex, Field field) {
 | 
	
		
			
				|  |  | +        EXCEL_COLUMN_SELECT_DATA_SERVICES.forEach(selectDataService -> {
 | 
	
		
			
				|  |  | +            List<String> stringList = selectDataService.handle(field.getAnnotation(ExcelColumnSelect.class).value());
 | 
	
		
			
				|  |  | +            if (CollUtil.isEmpty(stringList)) {
 | 
	
		
			
				|  |  | +                return;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +            selectMap.add(new KeyValue<>(colIndex, stringList));
 | 
	
		
			
				|  |  | +        });
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      @Override
 | 
	
	
		
			
				|  | @@ -65,7 +97,7 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |          // 2. 创建数据字典的 sheet 页
 | 
	
		
			
				|  |  |          Sheet dictSheet = workbook.createSheet(DICT_SHEET_NAME);
 | 
	
		
			
				|  |  | -        for (KeyValue<ExcelColumn, List<String>> keyValue : selectMap) {
 | 
	
		
			
				|  |  | +        for (KeyValue<Integer, List<String>> keyValue : selectMap) {
 | 
	
		
			
				|  |  |              int rowLength = keyValue.getValue().size();
 | 
	
		
			
				|  |  |              // 2.1 设置字典 sheet 页的值 每一列一个字典项
 | 
	
		
			
				|  |  |              for (int i = 0; i < rowLength; i++) {
 | 
	
	
		
			
				|  | @@ -73,7 +105,7 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
 | 
	
		
			
				|  |  |                  if (row == null) {
 | 
	
		
			
				|  |  |                      row = dictSheet.createRow(i);
 | 
	
		
			
				|  |  |                  }
 | 
	
		
			
				|  |  | -                row.createCell(keyValue.getKey().getColNum()).setCellValue(keyValue.getValue().get(i));
 | 
	
		
			
				|  |  | +                row.createCell(keyValue.getKey()).setCellValue(keyValue.getValue().get(i));
 | 
	
		
			
				|  |  |              }
 | 
	
		
			
				|  |  |              // 2.2 设置单元格下拉选择
 | 
	
		
			
				|  |  |              setColumnSelect(writeSheetHolder, workbook, helper, keyValue);
 | 
	
	
		
			
				|  | @@ -84,10 +116,10 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
 | 
	
		
			
				|  |  |       * 设置单元格下拉选择
 | 
	
		
			
				|  |  |       */
 | 
	
		
			
				|  |  |      private static void setColumnSelect(WriteSheetHolder writeSheetHolder, Workbook workbook, DataValidationHelper helper,
 | 
	
		
			
				|  |  | -                                        KeyValue<ExcelColumn, List<String>> keyValue) {
 | 
	
		
			
				|  |  | +                                        KeyValue<Integer, List<String>> keyValue) {
 | 
	
		
			
				|  |  |          // 1.1 创建可被其他单元格引用的名称
 | 
	
		
			
				|  |  |          Name name = workbook.createName();
 | 
	
		
			
				|  |  | -        String excelColumn = keyValue.getKey().name();
 | 
	
		
			
				|  |  | +        String excelColumn = ALPHABET.get(keyValue.getKey());
 | 
	
		
			
				|  |  |          // 1.2 下拉框数据来源 eg:字典sheet!$B1:$B2
 | 
	
		
			
				|  |  |          String refers = DICT_SHEET_NAME + "!$" + excelColumn + "$1:$" + excelColumn + "$" + keyValue.getValue().size();
 | 
	
		
			
				|  |  |          name.setNameName("dict" + keyValue.getKey()); // 设置名称的名字
 | 
	
	
		
			
				|  | @@ -97,7 +129,7 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
 | 
	
		
			
				|  |  |          DataValidationConstraint constraint = helper.createFormulaListConstraint("dict" + keyValue.getKey()); // 设置引用约束
 | 
	
		
			
				|  |  |          // 设置下拉单元格的首行、末行、首列、末列
 | 
	
		
			
				|  |  |          CellRangeAddressList rangeAddressList = new CellRangeAddressList(FIRST_ROW, LAST_ROW,
 | 
	
		
			
				|  |  | -                keyValue.getKey().getColNum(), keyValue.getKey().getColNum());
 | 
	
		
			
				|  |  | +                keyValue.getKey(), keyValue.getKey());
 | 
	
		
			
				|  |  |          DataValidation validation = helper.createValidation(constraint, rangeAddressList);
 | 
	
		
			
				|  |  |          if (validation instanceof HSSFDataValidation) {
 | 
	
		
			
				|  |  |              validation.setSuppressDropDownArrow(false);
 | 
	
	
		
			
				|  | @@ -112,4 +144,28 @@ public class SelectSheetWriteHandler implements SheetWriteHandler {
 | 
	
		
			
				|  |  |          writeSheetHolder.getSheet().addValidationData(validation);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | +    public static Map<String, Field> getFieldsWithAnnotation(Class<?> clazz, Class<? extends Annotation> annotationClass) {
 | 
	
		
			
				|  |  | +        Map<String, Field> annotatedFields = new LinkedHashMap<>();
 | 
	
		
			
				|  |  | +        Field[] fields = clazz.getDeclaredFields();
 | 
	
		
			
				|  |  | +        for (Field field : fields) {
 | 
	
		
			
				|  |  | +            if (field.isAnnotationPresent(annotationClass)) {
 | 
	
		
			
				|  |  | +                annotatedFields.put(field.getName(), field);
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        return annotatedFields;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    private static List<String> getExcelColumnNameList() {
 | 
	
		
			
				|  |  | +        ArrayList<String> strings = new ArrayList<>();
 | 
	
		
			
				|  |  | +        for (int i = 1; i <= 52; i++) { // 生成 52 列名称,需要更多请重写此方法
 | 
	
		
			
				|  |  | +            if (i <= 26) {
 | 
	
		
			
				|  |  | +                strings.add(String.valueOf((char) ('A' + i - 1))); // 使用 ASCII 码值转字母
 | 
	
		
			
				|  |  | +            } else {
 | 
	
		
			
				|  |  | +                strings.add(String.valueOf((char) ('A' + (i - 1) / 26 - 1)) + (char) ('A' + (i - 1) % 26));
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        return strings;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  |  }
 |