作者 RuoYi

Excel注解支持导出对象的子列表方法

@@ -89,6 +89,11 @@ public @interface Excel @@ -89,6 +89,11 @@ public @interface Excel
89 public String[] combo() default {}; 89 public String[] combo() default {};
90 90
91 /** 91 /**
  92 + * 是否需要纵向合并单元格,应对需求:含有list集合单元格)
  93 + */
  94 + public boolean needMerge() default false;
  95 +
  96 + /**
92 * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写. 97 * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写.
93 */ 98 */
94 public boolean isExport() default true; 99 public boolean isExport() default true;
@@ -104,7 +109,7 @@ public @interface Excel @@ -104,7 +109,7 @@ public @interface Excel
104 public boolean isStatistics() default false; 109 public boolean isStatistics() default false;
105 110
106 /** 111 /**
107 - * 导出类型(0数字 1字符串) 112 + * 导出类型(0数字 1字符串 2图片
108 */ 113 */
109 public ColumnType cellType() default ColumnType.STRING; 114 public ColumnType cellType() default ColumnType.STRING;
110 115
@@ -143,22 +148,6 @@ public @interface Excel @@ -143,22 +148,6 @@ public @interface Excel
143 */ 148 */
144 public String[] args() default {}; 149 public String[] args() default {};
145 150
146 - public enum Align  
147 - {  
148 - AUTO(0), LEFT(1), CENTER(2), RIGHT(3);  
149 - private final int value;  
150 -  
151 - Align(int value)  
152 - {  
153 - this.value = value;  
154 - }  
155 -  
156 - public int value()  
157 - {  
158 - return this.value;  
159 - }  
160 - }  
161 -  
162 /** 151 /**
163 * 字段类型(0:导出导入;1:仅导出;2:仅导入) 152 * 字段类型(0:导出导入;1:仅导出;2:仅导入)
164 */ 153 */
@@ -7,12 +7,14 @@ import java.io.InputStream; @@ -7,12 +7,14 @@ import java.io.InputStream;
7 import java.io.OutputStream; 7 import java.io.OutputStream;
8 import java.lang.reflect.Field; 8 import java.lang.reflect.Field;
9 import java.lang.reflect.Method; 9 import java.lang.reflect.Method;
  10 +import java.lang.reflect.ParameterizedType;
10 import java.math.BigDecimal; 11 import java.math.BigDecimal;
11 import java.text.DecimalFormat; 12 import java.text.DecimalFormat;
12 import java.time.LocalDate; 13 import java.time.LocalDate;
13 import java.time.LocalDateTime; 14 import java.time.LocalDateTime;
14 import java.util.ArrayList; 15 import java.util.ArrayList;
15 import java.util.Arrays; 16 import java.util.Arrays;
  17 +import java.util.Collection;
16 import java.util.Comparator; 18 import java.util.Comparator;
17 import java.util.Date; 19 import java.util.Date;
18 import java.util.HashMap; 20 import java.util.HashMap;
@@ -24,6 +26,7 @@ import java.util.stream.Collectors; @@ -24,6 +26,7 @@ import java.util.stream.Collectors;
24 import javax.servlet.http.HttpServletResponse; 26 import javax.servlet.http.HttpServletResponse;
25 import org.apache.commons.lang3.ArrayUtils; 27 import org.apache.commons.lang3.ArrayUtils;
26 import org.apache.commons.lang3.RegExUtils; 28 import org.apache.commons.lang3.RegExUtils;
  29 +import org.apache.commons.lang3.reflect.FieldUtils;
27 import org.apache.poi.hssf.usermodel.HSSFClientAnchor; 30 import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
28 import org.apache.poi.hssf.usermodel.HSSFPicture; 31 import org.apache.poi.hssf.usermodel.HSSFPicture;
29 import org.apache.poi.hssf.usermodel.HSSFPictureData; 32 import org.apache.poi.hssf.usermodel.HSSFPictureData;
@@ -150,6 +153,26 @@ public class ExcelUtil<T> @@ -150,6 +153,26 @@ public class ExcelUtil<T>
150 private short maxHeight; 153 private short maxHeight;
151 154
152 /** 155 /**
  156 + * 合并后最后行数
  157 + */
  158 + private int subMergedLastRowNum = 0;
  159 +
  160 + /**
  161 + * 合并后开始行数
  162 + */
  163 + private int subMergedFirstRowNum = 1;
  164 +
  165 + /**
  166 + * 对象的子列表方法
  167 + */
  168 + private Method subMethod;
  169 +
  170 + /**
  171 + * 对象的子列表属性
  172 + */
  173 + private List<Field> subFields;
  174 +
  175 + /**
153 * 统计列表 176 * 统计列表
154 */ 177 */
155 private Map<Integer, Double> statistics = new HashMap<Integer, Double>(); 178 private Map<Integer, Double> statistics = new HashMap<Integer, Double>();
@@ -198,6 +221,7 @@ public class ExcelUtil<T> @@ -198,6 +221,7 @@ public class ExcelUtil<T>
198 createExcelField(); 221 createExcelField();
199 createWorkbook(); 222 createWorkbook();
200 createTitle(); 223 createTitle();
  224 + createSubHead();
201 } 225 }
202 226
203 /** 227 /**
@@ -207,13 +231,48 @@ public class ExcelUtil<T> @@ -207,13 +231,48 @@ public class ExcelUtil<T>
207 { 231 {
208 if (StringUtils.isNotEmpty(title)) 232 if (StringUtils.isNotEmpty(title))
209 { 233 {
  234 + subMergedFirstRowNum++;
  235 + subMergedLastRowNum++;
  236 + int titleLastCol = this.fields.size() - 1;
  237 + if (isSubList())
  238 + {
  239 + titleLastCol = titleLastCol + subFields.size() - 1;
  240 + }
210 Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0); 241 Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0);
211 titleRow.setHeightInPoints(30); 242 titleRow.setHeightInPoints(30);
212 Cell titleCell = titleRow.createCell(0); 243 Cell titleCell = titleRow.createCell(0);
213 titleCell.setCellStyle(styles.get("title")); 244 titleCell.setCellStyle(styles.get("title"));
214 titleCell.setCellValue(title); 245 titleCell.setCellValue(title);
215 - sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(),  
216 - this.fields.size() - 1)); 246 + sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(), titleLastCol));
  247 + }
  248 + }
  249 +
  250 + /**
  251 + * 创建对象的子列表名称
  252 + */
  253 + public void createSubHead()
  254 + {
  255 + if (isSubList())
  256 + {
  257 + subMergedFirstRowNum++;
  258 + subMergedLastRowNum++;
  259 + Row subRow = sheet.createRow(rownum);
  260 + int excelNum = 0;
  261 + for (Object[] objects : fields)
  262 + {
  263 + Excel attr = (Excel) objects[1];
  264 + Cell headCell1 = subRow.createCell(excelNum);
  265 + headCell1.setCellValue(attr.name());
  266 + headCell1.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor())));
  267 + excelNum++;
  268 + }
  269 + int headFirstRow = excelNum - 1;
  270 + int headLastRow = headFirstRow + subFields.size() - 1;
  271 + if (headLastRow > headFirstRow)
  272 + {
  273 + sheet.addMergedRegion(new CellRangeAddress(rownum, rownum, headFirstRow, headLastRow));
  274 + }
  275 + rownum++;
217 } 276 }
218 } 277 }
219 278
@@ -593,8 +652,20 @@ public class ExcelUtil<T> @@ -593,8 +652,20 @@ public class ExcelUtil<T>
593 // 写入各个字段的列头名称 652 // 写入各个字段的列头名称
594 for (Object[] os : fields) 653 for (Object[] os : fields)
595 { 654 {
  655 + Field field = (Field) os[0];
596 Excel excel = (Excel) os[1]; 656 Excel excel = (Excel) os[1];
597 - this.createCell(excel, row, column++); 657 + if (Collection.class.isAssignableFrom(field.getType()))
  658 + {
  659 + for (Field subField : subFields)
  660 + {
  661 + Excel subExcel = subField.getAnnotation(Excel.class);
  662 + this.createHeadCell(subExcel, row, column++);
  663 + }
  664 + }
  665 + else
  666 + {
  667 + this.createHeadCell(excel, row, column++);
  668 + }
598 } 669 }
599 if (Type.EXPORT.equals(type)) 670 if (Type.EXPORT.equals(type))
600 { 671 {
@@ -610,21 +681,60 @@ public class ExcelUtil<T> @@ -610,21 +681,60 @@ public class ExcelUtil<T>
610 * @param index 序号 681 * @param index 序号
611 * @param row 单元格行 682 * @param row 单元格行
612 */ 683 */
  684 + @SuppressWarnings("unchecked")
613 public void fillExcelData(int index, Row row) 685 public void fillExcelData(int index, Row row)
614 { 686 {
615 int startNo = index * sheetSize; 687 int startNo = index * sheetSize;
616 int endNo = Math.min(startNo + sheetSize, list.size()); 688 int endNo = Math.min(startNo + sheetSize, list.size());
  689 + int rowNo = (1 + rownum) - startNo;
617 for (int i = startNo; i < endNo; i++) 690 for (int i = startNo; i < endNo; i++)
618 { 691 {
619 - row = sheet.createRow(i + 1 + rownum - startNo); 692 + rowNo = i > 1 ? rowNo + 1 : rowNo + i;
  693 + row = sheet.createRow(rowNo);
620 // 得到导出对象. 694 // 得到导出对象.
621 T vo = (T) list.get(i); 695 T vo = (T) list.get(i);
  696 + Collection<?> subList = null;
  697 + if (isSubListValue(vo))
  698 + {
  699 + subList = getListCellValue(vo);
  700 + subMergedLastRowNum = subMergedLastRowNum + subList.size();
  701 + }
  702 +
622 int column = 0; 703 int column = 0;
623 for (Object[] os : fields) 704 for (Object[] os : fields)
624 { 705 {
625 Field field = (Field) os[0]; 706 Field field = (Field) os[0];
626 Excel excel = (Excel) os[1]; 707 Excel excel = (Excel) os[1];
627 - this.addCell(excel, row, vo, field, column++); 708 + if (Collection.class.isAssignableFrom(field.getType()) && StringUtils.isNotNull(subList))
  709 + {
  710 + boolean subFirst = false;
  711 + for (Object obj : subList)
  712 + {
  713 + if (subFirst)
  714 + {
  715 + rowNo++;
  716 + row = sheet.createRow(rowNo);
  717 + }
  718 + List<Field> subFields = FieldUtils.getFieldsListWithAnnotation(obj.getClass(), Excel.class);
  719 + int subIndex = 0;
  720 + for (Field subField : subFields)
  721 + {
  722 + if (subField.isAnnotationPresent(Excel.class))
  723 + {
  724 + subField.setAccessible(true);
  725 + Excel attr = subField.getAnnotation(Excel.class);
  726 + this.addCell(attr, row, (T) obj, subField, column + subIndex);
  727 + }
  728 + subIndex++;
  729 + }
  730 + subFirst = true;
  731 + }
  732 + this.subMergedFirstRowNum = this.subMergedFirstRowNum + subList.size();
  733 + }
  734 + else
  735 + {
  736 + this.addCell(excel, row, vo, field, column++);
  737 + }
628 } 738 }
629 } 739 }
630 } 740 }
@@ -759,7 +869,7 @@ public class ExcelUtil<T> @@ -759,7 +869,7 @@ public class ExcelUtil<T>
759 /** 869 /**
760 * 创建单元格 870 * 创建单元格
761 */ 871 */
762 - public Cell createCell(Excel attr, Row row, int column) 872 + public Cell createHeadCell(Excel attr, Row row, int column)
763 { 873 {
764 // 创建列 874 // 创建列
765 Cell cell = row.createCell(column); 875 Cell cell = row.createCell(column);
@@ -767,6 +877,15 @@ public class ExcelUtil<T> @@ -767,6 +877,15 @@ public class ExcelUtil<T>
767 cell.setCellValue(attr.name()); 877 cell.setCellValue(attr.name());
768 setDataValidation(attr, row, column); 878 setDataValidation(attr, row, column);
769 cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor()))); 879 cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor())));
  880 + if (isSubList())
  881 + {
  882 + // 填充默认样式,防止合并单元格样式失效
  883 + sheet.setDefaultColumnStyle(column, styles.get(StringUtils.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor())));
  884 + if (attr.needMerge())
  885 + {
  886 + sheet.addMergedRegion(new CellRangeAddress(rownum - 1, rownum, column, column));
  887 + }
  888 + }
770 return cell; 889 return cell;
771 } 890 }
772 891
@@ -874,6 +993,11 @@ public class ExcelUtil<T> @@ -874,6 +993,11 @@ public class ExcelUtil<T>
874 { 993 {
875 // 创建cell 994 // 创建cell
876 cell = row.createCell(column); 995 cell = row.createCell(column);
  996 + if (isSubListValue(vo) && attr.needMerge())
  997 + {
  998 + CellRangeAddress cellAddress = new CellRangeAddress(subMergedFirstRowNum, subMergedLastRowNum, column, column);
  999 + sheet.addMergedRegion(cellAddress);
  1000 + }
877 cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor()))); 1001 cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor())));
878 1002
879 // 用于读取对象中的属性 1003 // 用于读取对象中的属性
@@ -969,7 +1093,7 @@ public class ExcelUtil<T> @@ -969,7 +1093,7 @@ public class ExcelUtil<T>
969 for (String item : convertSource) 1093 for (String item : convertSource)
970 { 1094 {
971 String[] itemArray = item.split("="); 1095 String[] itemArray = item.split("=");
972 - if (StringUtils.containsAny(separator, propertyValue)) 1096 + if (StringUtils.containsAny(propertyValue, separator))
973 { 1097 {
974 for (String value : propertyValue.split(separator)) 1098 for (String value : propertyValue.split(separator))
975 { 1099 {
@@ -1006,7 +1130,7 @@ public class ExcelUtil<T> @@ -1006,7 +1130,7 @@ public class ExcelUtil<T>
1006 for (String item : convertSource) 1130 for (String item : convertSource)
1007 { 1131 {
1008 String[] itemArray = item.split("="); 1132 String[] itemArray = item.split("=");
1009 - if (StringUtils.containsAny(separator, propertyValue)) 1133 + if (StringUtils.containsAny(propertyValue, separator))
1010 { 1134 {
1011 for (String value : propertyValue.split(separator)) 1135 for (String value : propertyValue.split(separator))
1012 { 1136 {
@@ -1230,6 +1354,13 @@ public class ExcelUtil<T> @@ -1230,6 +1354,13 @@ public class ExcelUtil<T>
1230 field.setAccessible(true); 1354 field.setAccessible(true);
1231 fields.add(new Object[] { field, attr }); 1355 fields.add(new Object[] { field, attr });
1232 } 1356 }
  1357 + if (Collection.class.isAssignableFrom(field.getType()))
  1358 + {
  1359 + subMethod = getSubMethod(field.getName(), clazz);
  1360 + ParameterizedType pt = (ParameterizedType) field.getGenericType();
  1361 + Class<?> subClass = (Class<?>) pt.getActualTypeArguments()[0];
  1362 + this.subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class);
  1363 + }
1233 } 1364 }
1234 1365
1235 // 多注解 1366 // 多注解
@@ -1473,4 +1604,61 @@ public class ExcelUtil<T> @@ -1473,4 +1604,61 @@ public class ExcelUtil<T>
1473 } 1604 }
1474 return str; 1605 return str;
1475 } 1606 }
  1607 +
  1608 + /**
  1609 + * 是否有对象的子列表
  1610 + */
  1611 + public boolean isSubList()
  1612 + {
  1613 + return StringUtils.isNotNull(subFields) && subFields.size() > 0;
  1614 + }
  1615 +
  1616 + /**
  1617 + * 是否有对象的子列表,集合不为空
  1618 + */
  1619 + public boolean isSubListValue(T vo)
  1620 + {
  1621 + return StringUtils.isNotNull(subFields) && subFields.size() > 0 && StringUtils.isNotNull(getListCellValue(vo)) && getListCellValue(vo).size() > 0;
  1622 + }
  1623 +
  1624 + /**
  1625 + * 获取集合的值
  1626 + */
  1627 + public Collection<?> getListCellValue(Object obj)
  1628 + {
  1629 + Object value;
  1630 + try
  1631 + {
  1632 + value = subMethod.invoke(obj, new Object[] {});
  1633 + }
  1634 + catch (Exception e)
  1635 + {
  1636 + return new ArrayList<Object>();
  1637 + }
  1638 + return (Collection<?>) value;
  1639 + }
  1640 +
  1641 + /**
  1642 + * 获取对象的子列表方法
  1643 + *
  1644 + * @param name 名称
  1645 + * @param pojoClass 类对象
  1646 + * @return 子列表方法
  1647 + */
  1648 + public Method getSubMethod(String name, Class<?> pojoClass)
  1649 + {
  1650 + StringBuffer getMethodName = new StringBuffer("get");
  1651 + getMethodName.append(name.substring(0, 1).toUpperCase());
  1652 + getMethodName.append(name.substring(1));
  1653 + Method method = null;
  1654 + try
  1655 + {
  1656 + method = pojoClass.getMethod(getMethodName.toString(), new Class[] {});
  1657 + }
  1658 + catch (Exception e)
  1659 + {
  1660 + log.error("获取对象异常{}", e.getMessage());
  1661 + }
  1662 + return method;
  1663 + }
1476 } 1664 }