|
|
@@ -1,20 +1,18 @@
|
|
|
package cn.tr.module.excel.core.handler.write;
|
|
|
|
|
|
import cn.hutool.core.collection.CollectionUtil;
|
|
|
-import cn.hutool.core.lang.Pair;
|
|
|
import cn.hutool.core.util.*;
|
|
|
-import cn.tr.core.tree.TreeNode;
|
|
|
-import cn.tr.core.utils.TreeUtil;
|
|
|
import cn.tr.module.export.annotation.ExcelSelect;
|
|
|
import cn.tr.module.export.annotation.ExcelPropertySupport;
|
|
|
-import cn.tr.module.export.handler.AbstractSelectConverter;
|
|
|
-import cn.tr.module.export.handler.NoneSelectConverter;
|
|
|
+import cn.tr.module.export.handler.AbstractCascadeSelectConverter;
|
|
|
+import cn.tr.module.export.handler.NoneCascadeSelectConverter;
|
|
|
import cn.tr.module.constant.ExcelConstant;
|
|
|
import com.alibaba.excel.annotation.ExcelProperty;
|
|
|
import com.alibaba.excel.converters.Converter;
|
|
|
import com.alibaba.excel.write.handler.CellWriteHandler;
|
|
|
import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
|
|
|
import lombok.Data;
|
|
|
+import lombok.Getter;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
import org.apache.poi.ss.usermodel.*;
|
|
|
import org.apache.poi.ss.util.CellRangeAddressList;
|
|
|
@@ -33,15 +31,16 @@ import java.util.stream.Collectors;
|
|
|
* @Date: 2023年05月19日
|
|
|
*/
|
|
|
@Slf4j
|
|
|
+@Getter
|
|
|
public class CustomCellWriteHandler implements CellWriteHandler {
|
|
|
//excel中的属性列的位置
|
|
|
- private final Map<String, Integer> excelColumnProperty = new HashMap<>();
|
|
|
- //级联属性映射集合 map("下级属性","上级属性")
|
|
|
- private final Map<String, String> linkagePropertyMap = new HashMap<>();
|
|
|
- //属性所对应的级联集合
|
|
|
- private final Map<String, List<Pair<String, List<String>>>> selectPropertyMap = new HashMap<>();
|
|
|
+ private final Map<String, Integer> columnIndexMap= new HashMap<>();
|
|
|
|
|
|
- boolean setSelectColumn=false;
|
|
|
+ //级联属性映射集合 map("上级属性","下级属性")
|
|
|
+ private final List<CascadeSelectNode> cascadeSelectNodeList = new ArrayList<>();
|
|
|
+
|
|
|
+ //已经处理过的子级级联属性
|
|
|
+ private Set<String> doneChildCascadeProperty=new HashSet<>();
|
|
|
|
|
|
@Override
|
|
|
public void afterCellDispose(CellWriteHandlerContext context) {
|
|
|
@@ -54,14 +53,10 @@ public class CustomCellWriteHandler implements CellWriteHandler {
|
|
|
//进行数字校验
|
|
|
createNumColumn(field,cell);
|
|
|
//进行字典校验
|
|
|
- createComboColumn(field,cell);
|
|
|
+ createComboColumn(field);
|
|
|
//进行日期格式校验
|
|
|
createDateColumn(field,cell);
|
|
|
- }else {
|
|
|
- if(!setSelectColumn){
|
|
|
- createLinkageSelectColumn(cell.getSheet());
|
|
|
- setSelectColumn=true;
|
|
|
- }
|
|
|
+ columnIndexMap.put(field.getName(),cell.getColumnIndex());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -72,7 +67,6 @@ public class CustomCellWriteHandler implements CellWriteHandler {
|
|
|
){
|
|
|
NotNull notNull = field.getAnnotation(NotNull.class);
|
|
|
DataValidationHelper helper = cell.getSheet().getDataValidationHelper();
|
|
|
- //DVConstraint constrain1 = DVConstraint.CreateDateConstraint(条件,"最小时间","最大时间","时间格式"); //这是检查时间的方法
|
|
|
addValidateData(cell.getSheet(),
|
|
|
cell,
|
|
|
helper.createDateConstraint(DataValidationConstraint.OperatorType.BETWEEN,"Date(1900, 1, 1)","Date(2099, 12, 31)","yyyy-MM-dd"),
|
|
|
@@ -85,32 +79,33 @@ public class CustomCellWriteHandler implements CellWriteHandler {
|
|
|
/**
|
|
|
* 集合列校验
|
|
|
* @param field
|
|
|
- * @param cell
|
|
|
*/
|
|
|
- private void createComboColumn(Field field,Cell cell){
|
|
|
+ private void createComboColumn(Field field){
|
|
|
ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
|
|
|
ExcelPropertySupport excelPropertySupport = field.getAnnotation(ExcelPropertySupport.class);
|
|
|
if(excelPropertySupport==null){
|
|
|
return;
|
|
|
}
|
|
|
ExcelSelect select = excelPropertySupport.select();
|
|
|
- Class<? extends AbstractSelectConverter<?>> converterClass = select.converter();
|
|
|
- if(converterClass== NoneSelectConverter.class){
|
|
|
+ Class<? extends AbstractCascadeSelectConverter<?>> converterClass = select.converter();
|
|
|
+ if(converterClass== NoneCascadeSelectConverter.class){
|
|
|
Class<? extends Converter<?>> excelPropertyConverterClass = excelProperty.converter();
|
|
|
- if(ClassUtil.isAssignable(AbstractSelectConverter.class,excelPropertyConverterClass)&&excelPropertyConverterClass!= NoneSelectConverter.class){
|
|
|
- converterClass= (Class<? extends AbstractSelectConverter<?>>) excelPropertyConverterClass;
|
|
|
+ if(ClassUtil.isAssignable(AbstractCascadeSelectConverter.class,excelPropertyConverterClass)&&excelPropertyConverterClass!= NoneCascadeSelectConverter.class){
|
|
|
+ converterClass= (Class<? extends AbstractCascadeSelectConverter<?>>) excelPropertyConverterClass;
|
|
|
}else {
|
|
|
return;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- AbstractSelectConverter<?> converter = ReflectUtil.newInstance(converterClass);
|
|
|
- List<Pair<String, List<String>>> linkageSelectList = converter.buildLinkageSelectList(Arrays.asList(select.param()));
|
|
|
-
|
|
|
+ AbstractCascadeSelectConverter<?> converter = ReflectUtil.newInstance(converterClass);
|
|
|
+ Map<String, List<String>> cascadeSelectList = converter.buildCascadeSelectList(Arrays.asList(select.param()));
|
|
|
|
|
|
- excelColumnProperty.put(field.getName(),cell.getColumnIndex());
|
|
|
- linkagePropertyMap.put(field.getName(),select.linkageParentProperty());
|
|
|
- selectPropertyMap.put(field.getName(),linkageSelectList);
|
|
|
+ if(doneChildCascadeProperty.contains(select.cascade())){
|
|
|
+ throw new IllegalArgumentException("所导出类的级联值-{"+field.getName()+"}不可出现多个上级联结,");
|
|
|
+ }else {
|
|
|
+ doneChildCascadeProperty.add(select.cascade());
|
|
|
+ }
|
|
|
+ cascadeSelectNodeList.add(new CascadeSelectNode(field.getName(),select.cascade(),cascadeSelectList));
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -146,8 +141,8 @@ public class CustomCellWriteHandler implements CellWriteHandler {
|
|
|
DataValidationConstraint.ValidationType.INTEGER,
|
|
|
DataValidationConstraint.OperatorType.BETWEEN, String.valueOf(Integer.MIN_VALUE), String.valueOf(Integer.MAX_VALUE)),
|
|
|
cell.getColumnIndex(),
|
|
|
- "请输入"+Integer.MIN_VALUE+"-"+Integer.MAX_VALUE+"之间的数字",
|
|
|
- "请输入"+Integer.MIN_VALUE+"-"+Integer.MAX_VALUE+"之间的数字",
|
|
|
+ "请输入"+0+"-"+1000000+"之间的数字",
|
|
|
+ "请输入"+0+"-"+1000000+"之间的数字",
|
|
|
notNull==null);
|
|
|
}
|
|
|
}
|
|
|
@@ -188,162 +183,64 @@ public class CustomCellWriteHandler implements CellWriteHandler {
|
|
|
cell.setCellComment(comment);
|
|
|
}
|
|
|
|
|
|
-
|
|
|
- /**
|
|
|
- * 创建级联搜索列
|
|
|
- * @param sheet
|
|
|
- */
|
|
|
- private void createLinkageSelectColumn(Sheet sheet){
|
|
|
- //没有级联
|
|
|
- if(CollectionUtil.isEmpty(linkagePropertyMap)){
|
|
|
- return;
|
|
|
- }
|
|
|
- List<LinkageSelectNode> selectNodes = new ArrayList<>();
|
|
|
- //已经处理过的字段
|
|
|
- linkagePropertyMap.forEach((currentProperty,parentProperty)->{
|
|
|
- selectNodes.add(new LinkageSelectNode(currentProperty,parentProperty,selectPropertyMap.get(currentProperty)));
|
|
|
- });
|
|
|
- List<LinkageSelectNode> treeNode=TreeUtil.buildTree(selectNodes);
|
|
|
- List<String> doneProperty = new ArrayList<>();
|
|
|
- linkagePropertyMap.forEach((currentProperty,parentProperty)->{
|
|
|
- if(StrUtil.isBlank(parentProperty)){
|
|
|
- //非级联
|
|
|
- noneLinkageSelectColumn(sheet,currentProperty);
|
|
|
- }else {
|
|
|
- //级联,针对本级
|
|
|
-
|
|
|
- }
|
|
|
- });
|
|
|
+ private CellRangeAddressList createColumnCellRange(int column){
|
|
|
+ return new CellRangeAddressList(1, ExcelConstant.MAX_ROW,column,column);
|
|
|
}
|
|
|
|
|
|
- /**
|
|
|
- * 级联下拉选择框
|
|
|
- * @param sheet 当前工作簿
|
|
|
- * @param parentProperty 父级级联参数
|
|
|
- * @param property 当前需要处理的属性名称
|
|
|
- * @param doneProperty 已经设置过下拉选择框的属性名称
|
|
|
- */
|
|
|
- private void linkageSelectColumn(Sheet sheet,String parentProperty,String property,Set<String> doneProperty ){
|
|
|
- Integer columnIndex = excelColumnProperty.get(property);
|
|
|
- if(ObjectUtil.isNull(columnIndex)){
|
|
|
- log.warn("[execl文件导出],property:{}显示为级联属性,找不到对应的列数",property);
|
|
|
- return;
|
|
|
- }
|
|
|
- //级联时构造一颗级联树
|
|
|
-
|
|
|
- if(doneProperty.contains(property)){
|
|
|
- //该属性已经处理过
|
|
|
-
|
|
|
- }else {
|
|
|
- //该属性尚未处理
|
|
|
- List<Pair<String, List<String>>> optionalSelectValues = selectPropertyMap.get(property);
|
|
|
- //创建工作簿
|
|
|
- Workbook workbook = sheet.getWorkbook();
|
|
|
- Sheet selectPropertySheet =workbook.createSheet(property);
|
|
|
- Sheet parentSelectPropertySheet = workbook.getSheet(parentProperty);
|
|
|
- if(ObjectUtil.isNotNull(parentSelectPropertySheet)){
|
|
|
- //若父级工作簿已存在,重置,级联时两字段只能一一对应
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- workbook.setSheetHidden(workbook.getSheetIndex(selectPropertySheet),true);
|
|
|
+ @Data
|
|
|
+ public static class CascadeSelectNode{
|
|
|
|
|
|
- }
|
|
|
+ /**
|
|
|
+ * 当前属性名称
|
|
|
+ */
|
|
|
+ private String fieldName;
|
|
|
|
|
|
- }
|
|
|
+ /**
|
|
|
+ * 级联下级属性名称
|
|
|
+ */
|
|
|
+ private String subordinateFieldName;
|
|
|
|
|
|
+ /**
|
|
|
+ * 下级属性
|
|
|
+ */
|
|
|
+ private CascadeSelectNode subordinateNode;
|
|
|
|
|
|
- /**
|
|
|
- * 非级联下拉选择框
|
|
|
- * @param sheet
|
|
|
- * @param property
|
|
|
- */
|
|
|
- private void noneLinkageSelectColumn(Sheet sheet,String property){
|
|
|
- Integer columnIndex = excelColumnProperty.get(property);
|
|
|
- if(ObjectUtil.isNull(columnIndex)){
|
|
|
- log.warn("[execl文件导出],property:{}显示为级联属性,找不到对应的列数",property);
|
|
|
- return;
|
|
|
- }
|
|
|
- List<Pair<String, List<String>>> optionalSelectValues = selectPropertyMap.get(property);
|
|
|
- List<String> selectValues = new ArrayList<>();
|
|
|
- if(CollectionUtil.isEmpty(optionalSelectValues)){
|
|
|
- selectValues.add("--- 暂无数据 ---");
|
|
|
- }else {
|
|
|
- optionalSelectValues.forEach(pair->{
|
|
|
- if(CollectionUtil.isNotEmpty(pair.getValue())){
|
|
|
- selectValues.addAll(pair.getValue());
|
|
|
- }
|
|
|
- });
|
|
|
- }
|
|
|
- doCreateNoneLinkageSelectColumn(property,sheet,selectValues,columnIndex,sheet.getWorkbook());
|
|
|
- }
|
|
|
|
|
|
+ private Map<String,List<String>> cascadeSelect;
|
|
|
|
|
|
- private void doCreateNoneLinkageSelectColumn(String propertyName,Sheet sheet, Collection<String> options, int column, Workbook wb) {
|
|
|
- Sheet hiddenSheet = wb.getSheet(propertyName);
|
|
|
- if(hiddenSheet==null){
|
|
|
- hiddenSheet = wb.createSheet(propertyName);
|
|
|
- }
|
|
|
- Row row;
|
|
|
- //写入下拉数据到新的sheet页中
|
|
|
- for (int i = 0; i < options.size(); i++) {
|
|
|
- row = hiddenSheet.getRow(i);
|
|
|
- if(row==null){
|
|
|
- row=hiddenSheet.createRow(i);
|
|
|
- }
|
|
|
- Cell cell = row.createCell(column);
|
|
|
- cell.setCellValue(CollectionUtil.get(options,i));
|
|
|
+ public CascadeSelectNode(String fieldName, String subordinateFieldName, Map<String,List<String>> cascadeSelect) {
|
|
|
+ this.fieldName=fieldName;
|
|
|
+ this.subordinateFieldName = subordinateFieldName;
|
|
|
+ this.cascadeSelect=cascadeSelect;
|
|
|
}
|
|
|
- //获取新sheet页内容
|
|
|
- String strFormula = propertyName + "!$"+ CharUtil.toString((char)(65+column))+"$1:$"+CharUtil.toString((char)(65+column))+"$65535";
|
|
|
- XSSFDataValidationConstraint constraint = new XSSFDataValidationConstraint(DataValidationConstraint.ValidationType.LIST,strFormula);
|
|
|
- // 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列
|
|
|
- CellRangeAddressList regions = createColumnCellRange(column);
|
|
|
- // 数据有效性对象
|
|
|
- DataValidationHelper helper = sheet.getDataValidationHelper();
|
|
|
- DataValidation dataValidation = helper.createValidation(constraint, regions);
|
|
|
- if (dataValidation instanceof XSSFDataValidation) {
|
|
|
- // 数据校验
|
|
|
- dataValidation.setSuppressDropDownArrow(true);
|
|
|
- dataValidation.setShowErrorBox(true);
|
|
|
- } else {
|
|
|
- dataValidation.setSuppressDropDownArrow(false);
|
|
|
- }
|
|
|
- // 作用在目标sheet上
|
|
|
- sheet.addValidationData(dataValidation);
|
|
|
- //将新建的sheet页隐藏掉
|
|
|
- wb.setSheetHidden(wb.getSheetIndex(propertyName), true);
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- private CellRangeAddressList createHeadCellRange(int column){
|
|
|
- return new CellRangeAddressList(0, 0,column,column);
|
|
|
- }
|
|
|
-
|
|
|
- private CellRangeAddressList createColumnCellRange(int column){
|
|
|
- return new CellRangeAddressList(1, ExcelConstant.MAX_ROW,column,column);
|
|
|
- }
|
|
|
-
|
|
|
- @Data
|
|
|
- static class LinkageSelectNode extends TreeNode<String> {
|
|
|
-
|
|
|
- private String name;
|
|
|
|
|
|
/**
|
|
|
- * 当前级联节点所拥有的选择项
|
|
|
+ * 构建树
|
|
|
+ * @return
|
|
|
*/
|
|
|
- private List<Pair<String, List<String>>> selectValues;
|
|
|
-
|
|
|
- public LinkageSelectNode(String name, String parentId,List<Pair<String, List<String>>> selectValues) {
|
|
|
- this.setParentId(parentId);
|
|
|
- this.name = name;
|
|
|
- this.selectValues = selectValues;
|
|
|
- }
|
|
|
+ public static List<CascadeSelectNode> buildTree(List<CascadeSelectNode> source){
|
|
|
+ List<CascadeSelectNode> result = new ArrayList<>();
|
|
|
+ Map<String, CascadeSelectNode> cascadeNodeMap
|
|
|
+ = source
|
|
|
+ .stream()
|
|
|
+ .collect(Collectors.groupingBy(CascadeSelectNode::getFieldName, Collectors.collectingAndThen(Collectors.toList(), CollectionUtil::getFirst)));
|
|
|
+ Set<String> childFieldNameSet = new HashSet<>();
|
|
|
+ for (CascadeSelectNode cascadeSelectNode : source) {
|
|
|
+ String subordinateFieldName = cascadeSelectNode.getSubordinateFieldName();
|
|
|
+ childFieldNameSet.add(subordinateFieldName);
|
|
|
+ CascadeSelectNode subordinateNode = cascadeNodeMap.get(subordinateFieldName);
|
|
|
+ if(subordinateNode==null&&StrUtil.isNotBlank(subordinateFieldName)){
|
|
|
+ subordinateNode=new CascadeSelectNode(subordinateFieldName,null,null);
|
|
|
+ }
|
|
|
+ cascadeSelectNode.setSubordinateNode(subordinateNode);
|
|
|
+ }
|
|
|
|
|
|
- @Override
|
|
|
- public String getId() {
|
|
|
- return name;
|
|
|
+ for (CascadeSelectNode cascadeSelectNode : source) {
|
|
|
+ if (!childFieldNameSet.contains(cascadeSelectNode.getFieldName())) {
|
|
|
+ result.add(cascadeSelectNode);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return result;
|
|
|
}
|
|
|
}
|
|
|
}
|