|
@@ -2,10 +2,9 @@ package cn.tr.module.excel.core.handler.write;
|
|
|
|
|
|
|
|
import cn.hutool.core.collection.CollectionUtil;
|
|
import cn.hutool.core.collection.CollectionUtil;
|
|
|
import cn.hutool.core.lang.Pair;
|
|
import cn.hutool.core.lang.Pair;
|
|
|
-import cn.hutool.core.util.CharUtil;
|
|
|
|
|
-import cn.hutool.core.util.ClassUtil;
|
|
|
|
|
-import cn.hutool.core.util.ReflectUtil;
|
|
|
|
|
-import cn.hutool.core.util.StrUtil;
|
|
|
|
|
|
|
+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.ExcelSelect;
|
|
|
import cn.tr.module.export.annotation.ExcelPropertySupport;
|
|
import cn.tr.module.export.annotation.ExcelPropertySupport;
|
|
|
import cn.tr.module.export.handler.AbstractSelectConverter;
|
|
import cn.tr.module.export.handler.AbstractSelectConverter;
|
|
@@ -13,19 +12,19 @@ import cn.tr.module.export.handler.NoneSelectConverter;
|
|
|
import cn.tr.module.constant.ExcelConstant;
|
|
import cn.tr.module.constant.ExcelConstant;
|
|
|
import com.alibaba.excel.annotation.ExcelProperty;
|
|
import com.alibaba.excel.annotation.ExcelProperty;
|
|
|
import com.alibaba.excel.converters.Converter;
|
|
import com.alibaba.excel.converters.Converter;
|
|
|
-import com.alibaba.excel.metadata.data.WriteCellData;
|
|
|
|
|
import com.alibaba.excel.write.handler.CellWriteHandler;
|
|
import com.alibaba.excel.write.handler.CellWriteHandler;
|
|
|
import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
|
|
import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
|
|
|
-import com.alibaba.excel.write.metadata.style.WriteCellStyle;
|
|
|
|
|
|
|
+import lombok.Data;
|
|
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
import org.apache.poi.ss.usermodel.*;
|
|
import org.apache.poi.ss.usermodel.*;
|
|
|
import org.apache.poi.ss.util.CellRangeAddressList;
|
|
import org.apache.poi.ss.util.CellRangeAddressList;
|
|
|
import org.apache.poi.xssf.usermodel.*;
|
|
import org.apache.poi.xssf.usermodel.*;
|
|
|
-import reactor.util.function.Tuple2;
|
|
|
|
|
|
|
|
|
|
import javax.validation.constraints.NotNull;
|
|
import javax.validation.constraints.NotNull;
|
|
|
import java.lang.reflect.Field;
|
|
import java.lang.reflect.Field;
|
|
|
import java.time.temporal.Temporal;
|
|
import java.time.temporal.Temporal;
|
|
|
import java.util.*;
|
|
import java.util.*;
|
|
|
|
|
+import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* @ClassName : CustomSheetWriteHandler
|
|
* @ClassName : CustomSheetWriteHandler
|
|
@@ -33,22 +32,16 @@ import java.util.*;
|
|
|
* @Author : LF
|
|
* @Author : LF
|
|
|
* @Date: 2023年05月19日
|
|
* @Date: 2023年05月19日
|
|
|
*/
|
|
*/
|
|
|
-
|
|
|
|
|
|
|
+@Slf4j
|
|
|
public class CustomCellWriteHandler implements CellWriteHandler {
|
|
public class CustomCellWriteHandler implements CellWriteHandler {
|
|
|
//excel中的属性列的位置
|
|
//excel中的属性列的位置
|
|
|
- private final Map<String, Integer> excelColumnProperty;
|
|
|
|
|
-
|
|
|
|
|
|
|
+ private final Map<String, Integer> excelColumnProperty = new HashMap<>();
|
|
|
//级联属性映射集合 map("下级属性","上级属性")
|
|
//级联属性映射集合 map("下级属性","上级属性")
|
|
|
- private final Map<String, String> linkagePropertyMap;
|
|
|
|
|
-
|
|
|
|
|
|
|
+ private final Map<String, String> linkagePropertyMap = new HashMap<>();
|
|
|
//属性所对应的级联集合
|
|
//属性所对应的级联集合
|
|
|
- private final Map<String, List<Pair<String, List<String>>>> selectPropertyMap ;
|
|
|
|
|
|
|
+ private final Map<String, List<Pair<String, List<String>>>> selectPropertyMap = new HashMap<>();
|
|
|
|
|
|
|
|
- public CustomCellWriteHandler(Map<String, Integer> excelColumnProperty, Map<String, String> linkagePropertyMap, Map<String,List<Pair<String, List<String>>>> selectPropertyMap) {
|
|
|
|
|
- this.excelColumnProperty = excelColumnProperty;
|
|
|
|
|
- this.linkagePropertyMap = linkagePropertyMap;
|
|
|
|
|
- this.selectPropertyMap = selectPropertyMap;
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ boolean setSelectColumn=false;
|
|
|
|
|
|
|
|
@Override
|
|
@Override
|
|
|
public void afterCellDispose(CellWriteHandlerContext context) {
|
|
public void afterCellDispose(CellWriteHandlerContext context) {
|
|
@@ -64,27 +57,11 @@ public class CustomCellWriteHandler implements CellWriteHandler {
|
|
|
createComboColumn(field,cell);
|
|
createComboColumn(field,cell);
|
|
|
//进行日期格式校验
|
|
//进行日期格式校验
|
|
|
createDateColumn(field,cell);
|
|
createDateColumn(field,cell);
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- /**
|
|
|
|
|
- * 锁定表头单元格
|
|
|
|
|
- * @param sheet
|
|
|
|
|
- */
|
|
|
|
|
- private void clockHead(CellWriteHandlerContext context,Sheet sheet,Cell cell,boolean head){
|
|
|
|
|
- if(head){
|
|
|
|
|
- CellStyle clockStyle =sheet.getWorkbook().createCellStyle();
|
|
|
|
|
- clockStyle.setLocked(true);
|
|
|
|
|
- CellStyle unClockStyle =sheet.getWorkbook().createCellStyle();
|
|
|
|
|
- unClockStyle.setLocked(false);
|
|
|
|
|
-
|
|
|
|
|
- sheet.setDefaultColumnStyle(cell.getColumnIndex(),unClockStyle);
|
|
|
|
|
- cell.setCellStyle(clockStyle);
|
|
|
|
|
}else {
|
|
}else {
|
|
|
- WriteCellData<?> cellData = context.getFirstCellData();
|
|
|
|
|
- WriteCellStyle style = cellData.getOrCreateStyle();
|
|
|
|
|
- style.setLocked(false);
|
|
|
|
|
- cellData.setWriteCellStyle(style);
|
|
|
|
|
|
|
+ if(!setSelectColumn){
|
|
|
|
|
+ createLinkageSelectColumn(cell.getSheet());
|
|
|
|
|
+ setSelectColumn=true;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -130,16 +107,10 @@ public class CustomCellWriteHandler implements CellWriteHandler {
|
|
|
AbstractSelectConverter<?> converter = ReflectUtil.newInstance(converterClass);
|
|
AbstractSelectConverter<?> converter = ReflectUtil.newInstance(converterClass);
|
|
|
List<Pair<String, List<String>>> linkageSelectList = converter.buildLinkageSelectList(Arrays.asList(select.param()));
|
|
List<Pair<String, List<String>>> linkageSelectList = converter.buildLinkageSelectList(Arrays.asList(select.param()));
|
|
|
|
|
|
|
|
|
|
+
|
|
|
excelColumnProperty.put(field.getName(),cell.getColumnIndex());
|
|
excelColumnProperty.put(field.getName(),cell.getColumnIndex());
|
|
|
linkagePropertyMap.put(field.getName(),select.linkageParentProperty());
|
|
linkagePropertyMap.put(field.getName(),select.linkageParentProperty());
|
|
|
selectPropertyMap.put(field.getName(),linkageSelectList);
|
|
selectPropertyMap.put(field.getName(),linkageSelectList);
|
|
|
-// List<String> values = converter.comboList(Arrays.asList(select.param()));
|
|
|
|
|
-// if(CollectionUtil.isEmpty(values)){
|
|
|
|
|
-// return;
|
|
|
|
|
-// }
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-// generateRangeList( cell.getSheet(),values,cell.getColumnIndex(),cell.getSheet().getWorkbook());
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -204,18 +175,115 @@ public class CustomCellWriteHandler implements CellWriteHandler {
|
|
|
setComment(cell,columnIndex,promptMsg,allowEmpty);
|
|
setComment(cell,columnIndex,promptMsg,allowEmpty);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+
|
|
|
|
|
+ private void setComment(Cell cell,int columnIndex,String msg,boolean allowEmpty){
|
|
|
|
|
+ Comment comment = cell.getCellComment();
|
|
|
|
|
+ Drawing<?> drawingPatriarch = cell.getSheet().createDrawingPatriarch();
|
|
|
|
|
+ // 在第一行 第二列创建一个批注
|
|
|
|
|
+ comment =comment==null? drawingPatriarch.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, columnIndex, 0, columnIndex, 0)):comment;
|
|
|
|
|
+ // 输入批注信息
|
|
|
|
|
+ msg=allowEmpty?msg:"---必输项---\n"+msg;
|
|
|
|
|
+ comment.setString(new XSSFRichTextString(msg));
|
|
|
|
|
+ // 将批注添加到单元格对象中
|
|
|
|
|
+ cell.setCellComment(comment);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
- * 构造有效性数据约束
|
|
|
|
|
|
|
+ * 创建级联搜索列
|
|
|
|
|
+ * @param sheet
|
|
|
*/
|
|
*/
|
|
|
- private void generateRangeList(Sheet sheet, Collection<String> options, int column, Workbook wb) {
|
|
|
|
|
- if (CollectionUtil.size(options)== 0){
|
|
|
|
|
|
|
+ private void createLinkageSelectColumn(Sheet sheet){
|
|
|
|
|
+ //没有级联
|
|
|
|
|
+ if(CollectionUtil.isEmpty(linkagePropertyMap)){
|
|
|
return;
|
|
return;
|
|
|
}
|
|
}
|
|
|
- //获取所有sheet页个数
|
|
|
|
|
- String hiddenSheetName = "hiddenSheet";
|
|
|
|
|
- Sheet hiddenSheet = wb.getSheet(hiddenSheetName);
|
|
|
|
|
|
|
+ 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 {
|
|
|
|
|
+ //级联,针对本级
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 级联下拉选择框
|
|
|
|
|
+ * @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);
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 非级联下拉选择框
|
|
|
|
|
+ * @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 void doCreateNoneLinkageSelectColumn(String propertyName,Sheet sheet, Collection<String> options, int column, Workbook wb) {
|
|
|
|
|
+ Sheet hiddenSheet = wb.getSheet(propertyName);
|
|
|
if(hiddenSheet==null){
|
|
if(hiddenSheet==null){
|
|
|
- hiddenSheet = wb.createSheet(hiddenSheetName);
|
|
|
|
|
|
|
+ hiddenSheet = wb.createSheet(propertyName);
|
|
|
}
|
|
}
|
|
|
Row row;
|
|
Row row;
|
|
|
//写入下拉数据到新的sheet页中
|
|
//写入下拉数据到新的sheet页中
|
|
@@ -228,7 +296,7 @@ public class CustomCellWriteHandler implements CellWriteHandler {
|
|
|
cell.setCellValue(CollectionUtil.get(options,i));
|
|
cell.setCellValue(CollectionUtil.get(options,i));
|
|
|
}
|
|
}
|
|
|
//获取新sheet页内容
|
|
//获取新sheet页内容
|
|
|
- String strFormula = hiddenSheetName + "!$"+CharUtil.toString((char)(65+column))+"$1:$"+CharUtil.toString((char)(65+column))+"$65535";
|
|
|
|
|
|
|
+ String strFormula = propertyName + "!$"+ CharUtil.toString((char)(65+column))+"$1:$"+CharUtil.toString((char)(65+column))+"$65535";
|
|
|
XSSFDataValidationConstraint constraint = new XSSFDataValidationConstraint(DataValidationConstraint.ValidationType.LIST,strFormula);
|
|
XSSFDataValidationConstraint constraint = new XSSFDataValidationConstraint(DataValidationConstraint.ValidationType.LIST,strFormula);
|
|
|
// 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列
|
|
// 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列
|
|
|
CellRangeAddressList regions = createColumnCellRange(column);
|
|
CellRangeAddressList regions = createColumnCellRange(column);
|
|
@@ -245,22 +313,10 @@ public class CustomCellWriteHandler implements CellWriteHandler {
|
|
|
// 作用在目标sheet上
|
|
// 作用在目标sheet上
|
|
|
sheet.addValidationData(dataValidation);
|
|
sheet.addValidationData(dataValidation);
|
|
|
//将新建的sheet页隐藏掉
|
|
//将新建的sheet页隐藏掉
|
|
|
- wb.setSheetHidden(1, true);
|
|
|
|
|
|
|
+ wb.setSheetHidden(wb.getSheetIndex(propertyName), true);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
- private void setComment(Cell cell,int columnIndex,String msg,boolean allowEmpty){
|
|
|
|
|
- Comment comment = cell.getCellComment();
|
|
|
|
|
- Drawing<?> drawingPatriarch = cell.getSheet().createDrawingPatriarch();
|
|
|
|
|
- // 在第一行 第二列创建一个批注
|
|
|
|
|
- comment =comment==null? drawingPatriarch.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, columnIndex, 0, columnIndex, 0)):comment;
|
|
|
|
|
- // 输入批注信息
|
|
|
|
|
- msg=allowEmpty?msg:"---必输项---\n"+msg;
|
|
|
|
|
- comment.setString(new XSSFRichTextString(msg));
|
|
|
|
|
- // 将批注添加到单元格对象中
|
|
|
|
|
- cell.setCellComment(comment);
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
private CellRangeAddressList createHeadCellRange(int column){
|
|
private CellRangeAddressList createHeadCellRange(int column){
|
|
|
return new CellRangeAddressList(0, 0,column,column);
|
|
return new CellRangeAddressList(0, 0,column,column);
|
|
|
}
|
|
}
|
|
@@ -268,4 +324,26 @@ public class CustomCellWriteHandler implements CellWriteHandler {
|
|
|
private CellRangeAddressList createColumnCellRange(int column){
|
|
private CellRangeAddressList createColumnCellRange(int column){
|
|
|
return new CellRangeAddressList(1, ExcelConstant.MAX_ROW,column,column);
|
|
return new CellRangeAddressList(1, ExcelConstant.MAX_ROW,column,column);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ @Data
|
|
|
|
|
+ static class LinkageSelectNode extends TreeNode<String> {
|
|
|
|
|
+
|
|
|
|
|
+ private String name;
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 当前级联节点所拥有的选择项
|
|
|
|
|
+ */
|
|
|
|
|
+ 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;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public String getId() {
|
|
|
|
|
+ return name;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|