Просмотр исходного кода

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

RuoYi лет назад: 3
Родитель
Сommit
5408af9d1f

+ 5 - 0
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/annotation/Excel.java

@@ -84,6 +84,11 @@ public @interface Excel
84
     public String[] combo() default {};
84
     public String[] combo() default {};
85
 
85
 
86
     /**
86
     /**
87
+     * 是否需要纵向合并单元格,应对需求:含有list集合单元格)
88
+     */
89
+    public boolean needMerge() default false;
90
+
91
+    /**
87
      * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写.
92
      * 是否导出数据,应对需求:有时我们需要导出一份模板,这是标题需要但内容需要用户手工填写.
88
      */
93
      */
89
     public boolean isExport() default true;
94
     public boolean isExport() default true;

+ 1 - 1
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/html/HTMLFilter.java

@@ -332,7 +332,7 @@ public final class HTMLFilter
332
             final String name = m.group(1).toLowerCase();
332
             final String name = m.group(1).toLowerCase();
333
             if (allowed(name))
333
             if (allowed(name))
334
             {
334
             {
335
-                if (false == inArray(name, vSelfClosingTags))
335
+                if (!inArray(name, vSelfClosingTags))
336
                 {
336
                 {
337
                     if (vTagCounts.containsKey(name))
337
                     if (vTagCounts.containsKey(name))
338
                     {
338
                     {

+ 207 - 21
ruoyi-common/ruoyi-common-core/src/main/java/com/ruoyi/common/core/utils/poi/ExcelUtil.java

@@ -4,12 +4,14 @@ import java.io.IOException;
4
 import java.io.InputStream;
4
 import java.io.InputStream;
5
 import java.lang.reflect.Field;
5
 import java.lang.reflect.Field;
6
 import java.lang.reflect.Method;
6
 import java.lang.reflect.Method;
7
+import java.lang.reflect.ParameterizedType;
7
 import java.math.BigDecimal;
8
 import java.math.BigDecimal;
8
 import java.text.DecimalFormat;
9
 import java.text.DecimalFormat;
9
 import java.time.LocalDate;
10
 import java.time.LocalDate;
10
 import java.time.LocalDateTime;
11
 import java.time.LocalDateTime;
11
 import java.util.ArrayList;
12
 import java.util.ArrayList;
12
 import java.util.Arrays;
13
 import java.util.Arrays;
14
+import java.util.Collection;
13
 import java.util.Comparator;
15
 import java.util.Comparator;
14
 import java.util.Date;
16
 import java.util.Date;
15
 import java.util.HashMap;
17
 import java.util.HashMap;
@@ -20,6 +22,7 @@ import java.util.stream.Collectors;
20
 import javax.servlet.http.HttpServletResponse;
22
 import javax.servlet.http.HttpServletResponse;
21
 import org.apache.commons.lang3.ArrayUtils;
23
 import org.apache.commons.lang3.ArrayUtils;
22
 import org.apache.commons.lang3.RegExUtils;
24
 import org.apache.commons.lang3.RegExUtils;
25
+import org.apache.commons.lang3.reflect.FieldUtils;
23
 import org.apache.poi.ss.usermodel.BorderStyle;
26
 import org.apache.poi.ss.usermodel.BorderStyle;
24
 import org.apache.poi.ss.usermodel.Cell;
27
 import org.apache.poi.ss.usermodel.Cell;
25
 import org.apache.poi.ss.usermodel.CellStyle;
28
 import org.apache.poi.ss.usermodel.CellStyle;
@@ -127,6 +130,26 @@ public class ExcelUtil<T>
127
     private short maxHeight;
130
     private short maxHeight;
128
 
131
 
129
     /**
132
     /**
133
+     * 合并后最后行数
134
+     */
135
+    private int subMergedLastRowNum = 0;
136
+
137
+    /**
138
+     * 合并后开始行数
139
+     */
140
+    private int subMergedFirstRowNum = 1;
141
+
142
+    /**
143
+     * 对象的子列表方法
144
+     */
145
+    private Method subMethod;
146
+
147
+    /**
148
+     * 对象的子列表属性
149
+     */
150
+    private List<Field> subFields;
151
+
152
+    /**
130
      * 统计列表
153
      * 统计列表
131
      */
154
      */
132
     private Map<Integer, Double> statistics = new HashMap<Integer, Double>();
155
     private Map<Integer, Double> statistics = new HashMap<Integer, Double>();
@@ -175,6 +198,7 @@ public class ExcelUtil<T>
175
         createExcelField();
198
         createExcelField();
176
         createWorkbook();
199
         createWorkbook();
177
         createTitle();
200
         createTitle();
201
+        createSubHead();
178
     }
202
     }
179
 
203
 
180
     /**
204
     /**
@@ -184,19 +208,54 @@ public class ExcelUtil<T>
184
     {
208
     {
185
         if (StringUtils.isNotEmpty(title))
209
         if (StringUtils.isNotEmpty(title))
186
         {
210
         {
211
+            subMergedFirstRowNum++;
212
+            subMergedLastRowNum++;
213
+            int titleLastCol = this.fields.size() - 1;
214
+            if (isSubList())
215
+            {
216
+                titleLastCol = titleLastCol + subFields.size() - 1;
217
+            }
187
             Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0);
218
             Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0);
188
             titleRow.setHeightInPoints(30);
219
             titleRow.setHeightInPoints(30);
189
             Cell titleCell = titleRow.createCell(0);
220
             Cell titleCell = titleRow.createCell(0);
190
             titleCell.setCellStyle(styles.get("title"));
221
             titleCell.setCellStyle(styles.get("title"));
191
             titleCell.setCellValue(title);
222
             titleCell.setCellValue(title);
192
-            sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(),
193
-                    this.fields.size() - 1));
223
+            sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(), titleLastCol));
224
+        }
225
+    }
226
+
227
+    /**
228
+     * 创建对象的子列表名称
229
+     */
230
+    public void createSubHead()
231
+    {
232
+        if (isSubList())
233
+        {
234
+            subMergedFirstRowNum++;
235
+            subMergedLastRowNum++;
236
+            Row subRow = sheet.createRow(rownum);
237
+            int excelNum = 0;
238
+            for (Object[] objects : fields)
239
+            {
240
+                Excel attr = (Excel) objects[1];
241
+                Cell headCell1 = subRow.createCell(excelNum);
242
+                headCell1.setCellValue(attr.name());
243
+                headCell1.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor())));
244
+                excelNum++;
245
+            }
246
+            int headFirstRow = excelNum - 1;
247
+            int headLastRow = headFirstRow + subFields.size() - 1;
248
+            if (headLastRow > headFirstRow)
249
+            {
250
+                sheet.addMergedRegion(new CellRangeAddress(rownum, rownum, headFirstRow, headLastRow));
251
+            }
252
+            rownum++;
194
         }
253
         }
195
     }
254
     }
196
 
255
 
197
     /**
256
     /**
198
      * 对excel表单默认第一个索引名转换成list
257
      * 对excel表单默认第一个索引名转换成list
199
-     *
258
+     * 
200
      * @param is 输入流
259
      * @param is 输入流
201
      * @return 转换后集合
260
      * @return 转换后集合
202
      */
261
      */
@@ -207,7 +266,7 @@ public class ExcelUtil<T>
207
 
266
 
208
     /**
267
     /**
209
      * 对excel表单默认第一个索引名转换成list
268
      * 对excel表单默认第一个索引名转换成list
210
-     *
269
+     * 
211
      * @param is 输入流
270
      * @param is 输入流
212
      * @param titleNum 标题占用行数
271
      * @param titleNum 标题占用行数
213
      * @return 转换后集合
272
      * @return 转换后集合
@@ -219,7 +278,7 @@ public class ExcelUtil<T>
219
 
278
 
220
     /**
279
     /**
221
      * 对excel表单指定表格索引名转换成list
280
      * 对excel表单指定表格索引名转换成list
222
-     *
281
+     * 
223
      * @param sheetName 表格索引名
282
      * @param sheetName 表格索引名
224
      * @param titleNum 标题占用行数
283
      * @param titleNum 标题占用行数
225
      * @param is 输入流
284
      * @param is 输入流
@@ -378,7 +437,6 @@ public class ExcelUtil<T>
378
      * @param list 导出数据集合
437
      * @param list 导出数据集合
379
      * @param sheetName 工作表的名称
438
      * @param sheetName 工作表的名称
380
      * @return 结果
439
      * @return 结果
381
-     * @throws IOException
382
      */
440
      */
383
     public void exportExcel(HttpServletResponse response, List<T> list, String sheetName)
441
     public void exportExcel(HttpServletResponse response, List<T> list, String sheetName)
384
     {
442
     {
@@ -393,7 +451,6 @@ public class ExcelUtil<T>
393
      * @param sheetName 工作表的名称
451
      * @param sheetName 工作表的名称
394
      * @param title 标题
452
      * @param title 标题
395
      * @return 结果
453
      * @return 结果
396
-     * @throws IOException
397
      */
454
      */
398
     public void exportExcel(HttpServletResponse response, List<T> list, String sheetName, String title)
455
     public void exportExcel(HttpServletResponse response, List<T> list, String sheetName, String title)
399
     {
456
     {
@@ -431,7 +488,7 @@ public class ExcelUtil<T>
431
 
488
 
432
     /**
489
     /**
433
      * 对list数据源将其里面的数据导入到excel表单
490
      * 对list数据源将其里面的数据导入到excel表单
434
-     *
491
+     * 
435
      * @return 结果
492
      * @return 结果
436
      */
493
      */
437
     public void exportExcel(HttpServletResponse response)
494
     public void exportExcel(HttpServletResponse response)
@@ -468,8 +525,20 @@ public class ExcelUtil<T>
468
             // 写入各个字段的列头名称
525
             // 写入各个字段的列头名称
469
             for (Object[] os : fields)
526
             for (Object[] os : fields)
470
             {
527
             {
528
+                Field field = (Field) os[0];
471
                 Excel excel = (Excel) os[1];
529
                 Excel excel = (Excel) os[1];
472
-                this.createCell(excel, row, column++);
530
+                if (Collection.class.isAssignableFrom(field.getType()))
531
+                {
532
+                    for (Field subField : subFields)
533
+                    {
534
+                        Excel subExcel = subField.getAnnotation(Excel.class);
535
+                        this.createHeadCell(subExcel, row, column++);
536
+                    }
537
+                }
538
+                else
539
+                {
540
+                    this.createHeadCell(excel, row, column++);
541
+                }
473
             }
542
             }
474
             if (Type.EXPORT.equals(type))
543
             if (Type.EXPORT.equals(type))
475
             {
544
             {
@@ -481,32 +550,71 @@ public class ExcelUtil<T>
481
 
550
 
482
     /**
551
     /**
483
      * 填充excel数据
552
      * 填充excel数据
484
-     *
553
+     * 
485
      * @param index 序号
554
      * @param index 序号
486
      * @param row 单元格行
555
      * @param row 单元格行
487
      */
556
      */
557
+    @SuppressWarnings("unchecked")
488
     public void fillExcelData(int index, Row row)
558
     public void fillExcelData(int index, Row row)
489
     {
559
     {
490
         int startNo = index * sheetSize;
560
         int startNo = index * sheetSize;
491
         int endNo = Math.min(startNo + sheetSize, list.size());
561
         int endNo = Math.min(startNo + sheetSize, list.size());
562
+        int rowNo = (1 + rownum) - startNo;
492
         for (int i = startNo; i < endNo; i++)
563
         for (int i = startNo; i < endNo; i++)
493
         {
564
         {
494
-            row = sheet.createRow(i + 1 + rownum - startNo);
565
+            rowNo = i > 1 ? rowNo + 1 : rowNo + i;
566
+            row = sheet.createRow(rowNo);
495
             // 得到导出对象.
567
             // 得到导出对象.
496
             T vo = (T) list.get(i);
568
             T vo = (T) list.get(i);
569
+            Collection<?> subList = null;
570
+            if (isSubListValue(vo))
571
+            {
572
+                subList = getListCellValue(vo);
573
+                subMergedLastRowNum = subMergedLastRowNum + subList.size();
574
+            }
575
+
497
             int column = 0;
576
             int column = 0;
498
             for (Object[] os : fields)
577
             for (Object[] os : fields)
499
             {
578
             {
500
                 Field field = (Field) os[0];
579
                 Field field = (Field) os[0];
501
                 Excel excel = (Excel) os[1];
580
                 Excel excel = (Excel) os[1];
502
-                this.addCell(excel, row, vo, field, column++);
581
+                if (Collection.class.isAssignableFrom(field.getType()) && StringUtils.isNotNull(subList))
582
+                {
583
+                    boolean subFirst = false;
584
+                    for (Object obj : subList)
585
+                    {
586
+                        if (subFirst)
587
+                        {
588
+                            rowNo++;
589
+                            row = sheet.createRow(rowNo);
590
+                        }
591
+                        List<Field> subFields = FieldUtils.getFieldsListWithAnnotation(obj.getClass(), Excel.class);
592
+                        int subIndex = 0;
593
+                        for (Field subField : subFields)
594
+                        {
595
+                            if (subField.isAnnotationPresent(Excel.class))
596
+                            {
597
+                                subField.setAccessible(true);
598
+                                Excel attr = subField.getAnnotation(Excel.class);
599
+                                this.addCell(attr, row, (T) obj, subField, column + subIndex);
600
+                            }
601
+                            subIndex++;
602
+                        }
603
+                        subFirst = true;
604
+                    }
605
+                    this.subMergedFirstRowNum = this.subMergedFirstRowNum + subList.size();
606
+                }
607
+                else
608
+                {
609
+                    this.addCell(excel, row, vo, field, column++);
610
+                }
503
             }
611
             }
504
         }
612
         }
505
     }
613
     }
506
 
614
 
507
     /**
615
     /**
508
      * 创建表格样式
616
      * 创建表格样式
509
-     *
617
+     * 
510
      * @param wb 工作薄对象
618
      * @param wb 工作薄对象
511
      * @return 样式列表
619
      * @return 样式列表
512
      */
620
      */
@@ -634,7 +742,7 @@ public class ExcelUtil<T>
634
     /**
742
     /**
635
      * 创建单元格
743
      * 创建单元格
636
      */
744
      */
637
-    public Cell createCell(Excel attr, Row row, int column)
745
+    public Cell createHeadCell(Excel attr, Row row, int column)
638
     {
746
     {
639
         // 创建列
747
         // 创建列
640
         Cell cell = row.createCell(column);
748
         Cell cell = row.createCell(column);
@@ -642,12 +750,21 @@ public class ExcelUtil<T>
642
         cell.setCellValue(attr.name());
750
         cell.setCellValue(attr.name());
643
         setDataValidation(attr, row, column);
751
         setDataValidation(attr, row, column);
644
         cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor())));
752
         cell.setCellStyle(styles.get(StringUtils.format("header_{}_{}", attr.headerColor(), attr.headerBackgroundColor())));
753
+        if (isSubList())
754
+        {
755
+            // 填充默认样式,防止合并单元格样式失效
756
+            sheet.setDefaultColumnStyle(column, styles.get(StringUtils.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor())));
757
+            if (attr.needMerge())
758
+            {
759
+                sheet.addMergedRegion(new CellRangeAddress(rownum - 1, rownum, column, column));
760
+            }
761
+        }
645
         return cell;
762
         return cell;
646
     }
763
     }
647
 
764
 
648
     /**
765
     /**
649
      * 设置单元格信息
766
      * 设置单元格信息
650
-     *
767
+     * 
651
      * @param value 单元格值
768
      * @param value 单元格值
652
      * @param attr 注解相关
769
      * @param attr 注解相关
653
      * @param cell 单元格信息
770
      * @param cell 单元格信息
@@ -749,6 +866,11 @@ public class ExcelUtil<T>
749
             {
866
             {
750
                 // 创建cell
867
                 // 创建cell
751
                 cell = row.createCell(column);
868
                 cell = row.createCell(column);
869
+                if (isSubListValue(vo) && attr.needMerge())
870
+                {
871
+                    CellRangeAddress cellAddress = new CellRangeAddress(subMergedFirstRowNum, subMergedLastRowNum, column, column);
872
+                    sheet.addMergedRegion(cellAddress);
873
+                }
752
                 cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor())));
874
                 cell.setCellStyle(styles.get(StringUtils.format("data_{}_{}_{}", attr.align(), attr.color(), attr.backgroundColor())));
753
 
875
 
754
                 // 用于读取对象中的属性
876
                 // 用于读取对象中的属性
@@ -839,7 +961,7 @@ public class ExcelUtil<T>
839
         for (String item : convertSource)
961
         for (String item : convertSource)
840
         {
962
         {
841
             String[] itemArray = item.split("=");
963
             String[] itemArray = item.split("=");
842
-            if (StringUtils.containsAny(separator, propertyValue))
964
+            if (StringUtils.containsAny(propertyValue, separator))
843
             {
965
             {
844
                 for (String value : propertyValue.split(separator))
966
                 for (String value : propertyValue.split(separator))
845
                 {
967
                 {
@@ -863,7 +985,7 @@ public class ExcelUtil<T>
863
 
985
 
864
     /**
986
     /**
865
      * 反向解析值 男=0,女=1,未知=2
987
      * 反向解析值 男=0,女=1,未知=2
866
-     *
988
+     * 
867
      * @param propertyValue 参数值
989
      * @param propertyValue 参数值
868
      * @param converterExp 翻译注解
990
      * @param converterExp 翻译注解
869
      * @param separator 分隔符
991
      * @param separator 分隔符
@@ -876,7 +998,7 @@ public class ExcelUtil<T>
876
         for (String item : convertSource)
998
         for (String item : convertSource)
877
         {
999
         {
878
             String[] itemArray = item.split("=");
1000
             String[] itemArray = item.split("=");
879
-            if (StringUtils.containsAny(separator, propertyValue))
1001
+            if (StringUtils.containsAny(propertyValue, separator))
880
             {
1002
             {
881
                 for (String value : propertyValue.split(separator))
1003
                 for (String value : propertyValue.split(separator))
882
                 {
1004
                 {
@@ -1049,6 +1171,13 @@ public class ExcelUtil<T>
1049
                         field.setAccessible(true);
1171
                         field.setAccessible(true);
1050
                         fields.add(new Object[] { field, attr });
1172
                         fields.add(new Object[] { field, attr });
1051
                     }
1173
                     }
1174
+                    if (Collection.class.isAssignableFrom(field.getType()))
1175
+                    {
1176
+                        subMethod = getSubMethod(field.getName(), clazz);
1177
+                        ParameterizedType pt = (ParameterizedType) field.getGenericType();
1178
+                        Class<?> subClass = (Class<?>) pt.getActualTypeArguments()[0];
1179
+                        this.subFields = FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class);
1180
+                    }
1052
                 }
1181
                 }
1053
 
1182
 
1054
                 // 多注解
1183
                 // 多注解
@@ -1097,7 +1226,7 @@ public class ExcelUtil<T>
1097
 
1226
 
1098
     /**
1227
     /**
1099
      * 创建工作表
1228
      * 创建工作表
1100
-     *
1229
+     * 
1101
      * @param sheetNo sheet数量
1230
      * @param sheetNo sheet数量
1102
      * @param index 序号
1231
      * @param index 序号
1103
      */
1232
      */
@@ -1114,7 +1243,7 @@ public class ExcelUtil<T>
1114
 
1243
 
1115
     /**
1244
     /**
1116
      * 获取单元格值
1245
      * 获取单元格值
1117
-     *
1246
+     * 
1118
      * @param row 获取的行
1247
      * @param row 获取的行
1119
      * @param column 获取单元格列号
1248
      * @param column 获取单元格列号
1120
      * @return 单元格值
1249
      * @return 单元格值
@@ -1174,7 +1303,7 @@ public class ExcelUtil<T>
1174
 
1303
 
1175
     /**
1304
     /**
1176
      * 判断是否是空行
1305
      * 判断是否是空行
1177
-     *
1306
+     * 
1178
      * @param row 判断的行
1307
      * @param row 判断的行
1179
      * @return
1308
      * @return
1180
      */
1309
      */
@@ -1227,4 +1356,61 @@ public class ExcelUtil<T>
1227
         }
1356
         }
1228
         return str;
1357
         return str;
1229
     }
1358
     }
1359
+
1360
+    /**
1361
+     * 是否有对象的子列表
1362
+     */
1363
+    public boolean isSubList()
1364
+    {
1365
+        return StringUtils.isNotNull(subFields) && subFields.size() > 0;
1366
+    }
1367
+
1368
+    /**
1369
+     * 是否有对象的子列表,集合不为空
1370
+     */
1371
+    public boolean isSubListValue(T vo)
1372
+    {
1373
+        return StringUtils.isNotNull(subFields) && subFields.size() > 0 && StringUtils.isNotNull(getListCellValue(vo)) && getListCellValue(vo).size() > 0;
1374
+    }
1375
+
1376
+    /**
1377
+     * 获取集合的值
1378
+     */
1379
+    public Collection<?> getListCellValue(Object obj)
1380
+    {
1381
+        Object value;
1382
+        try
1383
+        {
1384
+            value = subMethod.invoke(obj, new Object[] {});
1385
+        }
1386
+        catch (Exception e)
1387
+        {
1388
+            return new ArrayList<Object>();
1389
+        }
1390
+        return (Collection<?>) value;
1391
+    }
1392
+
1393
+    /**
1394
+     * 获取对象的子列表方法
1395
+     * 
1396
+     * @param name 名称
1397
+     * @param pojoClass 类对象
1398
+     * @return 子列表方法
1399
+     */
1400
+    public Method getSubMethod(String name, Class<?> pojoClass)
1401
+    {
1402
+        StringBuffer getMethodName = new StringBuffer("get");
1403
+        getMethodName.append(name.substring(0, 1).toUpperCase());
1404
+        getMethodName.append(name.substring(1));
1405
+        Method method = null;
1406
+        try
1407
+        {
1408
+            method = pojoClass.getMethod(getMethodName.toString(), new Class[] {});
1409
+        }
1410
+        catch (Exception e)
1411
+        {
1412
+            log.error("获取对象异常{}", e.getMessage());
1413
+        }
1414
+        return method;
1415
+    }
1230
 }
1416
 }