Parcourir la source

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

RuoYi il y a 3 ans
Parent
commit
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 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 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 332
             final String name = m.group(1).toLowerCase();
333 333
             if (allowed(name))
334 334
             {
335
-                if (false == inArray(name, vSelfClosingTags))
335
+                if (!inArray(name, vSelfClosingTags))
336 336
                 {
337 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 4
 import java.io.InputStream;
5 5
 import java.lang.reflect.Field;
6 6
 import java.lang.reflect.Method;
7
+import java.lang.reflect.ParameterizedType;
7 8
 import java.math.BigDecimal;
8 9
 import java.text.DecimalFormat;
9 10
 import java.time.LocalDate;
10 11
 import java.time.LocalDateTime;
11 12
 import java.util.ArrayList;
12 13
 import java.util.Arrays;
14
+import java.util.Collection;
13 15
 import java.util.Comparator;
14 16
 import java.util.Date;
15 17
 import java.util.HashMap;
@@ -20,6 +22,7 @@ import java.util.stream.Collectors;
20 22
 import javax.servlet.http.HttpServletResponse;
21 23
 import org.apache.commons.lang3.ArrayUtils;
22 24
 import org.apache.commons.lang3.RegExUtils;
25
+import org.apache.commons.lang3.reflect.FieldUtils;
23 26
 import org.apache.poi.ss.usermodel.BorderStyle;
24 27
 import org.apache.poi.ss.usermodel.Cell;
25 28
 import org.apache.poi.ss.usermodel.CellStyle;
@@ -127,6 +130,26 @@ public class ExcelUtil<T>
127 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 155
     private Map<Integer, Double> statistics = new HashMap<Integer, Double>();
@@ -175,6 +198,7 @@ public class ExcelUtil<T>
175 198
         createExcelField();
176 199
         createWorkbook();
177 200
         createTitle();
201
+        createSubHead();
178 202
     }
179 203
 
180 204
     /**
@@ -184,19 +208,54 @@ public class ExcelUtil<T>
184 208
     {
185 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 218
             Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0);
188 219
             titleRow.setHeightInPoints(30);
189 220
             Cell titleCell = titleRow.createCell(0);
190 221
             titleCell.setCellStyle(styles.get("title"));
191 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 257
      * 对excel表单默认第一个索引名转换成list
199
-     *
258
+     * 
200 259
      * @param is 输入流
201 260
      * @return 转换后集合
202 261
      */
@@ -207,7 +266,7 @@ public class ExcelUtil<T>
207 266
 
208 267
     /**
209 268
      * 对excel表单默认第一个索引名转换成list
210
-     *
269
+     * 
211 270
      * @param is 输入流
212 271
      * @param titleNum 标题占用行数
213 272
      * @return 转换后集合
@@ -219,7 +278,7 @@ public class ExcelUtil<T>
219 278
 
220 279
     /**
221 280
      * 对excel表单指定表格索引名转换成list
222
-     *
281
+     * 
223 282
      * @param sheetName 表格索引名
224 283
      * @param titleNum 标题占用行数
225 284
      * @param is 输入流
@@ -378,7 +437,6 @@ public class ExcelUtil<T>
378 437
      * @param list 导出数据集合
379 438
      * @param sheetName 工作表的名称
380 439
      * @return 结果
381
-     * @throws IOException
382 440
      */
383 441
     public void exportExcel(HttpServletResponse response, List<T> list, String sheetName)
384 442
     {
@@ -393,7 +451,6 @@ public class ExcelUtil<T>
393 451
      * @param sheetName 工作表的名称
394 452
      * @param title 标题
395 453
      * @return 结果
396
-     * @throws IOException
397 454
      */
398 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 490
      * 对list数据源将其里面的数据导入到excel表单
434
-     *
491
+     * 
435 492
      * @return 结果
436 493
      */
437 494
     public void exportExcel(HttpServletResponse response)
@@ -468,8 +525,20 @@ public class ExcelUtil<T>
468 525
             // 写入各个字段的列头名称
469 526
             for (Object[] os : fields)
470 527
             {
528
+                Field field = (Field) os[0];
471 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 543
             if (Type.EXPORT.equals(type))
475 544
             {
@@ -481,32 +550,71 @@ public class ExcelUtil<T>
481 550
 
482 551
     /**
483 552
      * 填充excel数据
484
-     *
553
+     * 
485 554
      * @param index 序号
486 555
      * @param row 单元格行
487 556
      */
557
+    @SuppressWarnings("unchecked")
488 558
     public void fillExcelData(int index, Row row)
489 559
     {
490 560
         int startNo = index * sheetSize;
491 561
         int endNo = Math.min(startNo + sheetSize, list.size());
562
+        int rowNo = (1 + rownum) - startNo;
492 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 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 576
             int column = 0;
498 577
             for (Object[] os : fields)
499 578
             {
500 579
                 Field field = (Field) os[0];
501 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 618
      * @param wb 工作薄对象
511 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 748
         Cell cell = row.createCell(column);
@@ -642,12 +750,21 @@ public class ExcelUtil<T>
642 750
         cell.setCellValue(attr.name());
643 751
         setDataValidation(attr, row, column);
644 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 762
         return cell;
646 763
     }
647 764
 
648 765
     /**
649 766
      * 设置单元格信息
650
-     *
767
+     * 
651 768
      * @param value 单元格值
652 769
      * @param attr 注解相关
653 770
      * @param cell 单元格信息
@@ -749,6 +866,11 @@ public class ExcelUtil<T>
749 866
             {
750 867
                 // 创建cell
751 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 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 961
         for (String item : convertSource)
840 962
         {
841 963
             String[] itemArray = item.split("=");
842
-            if (StringUtils.containsAny(separator, propertyValue))
964
+            if (StringUtils.containsAny(propertyValue, separator))
843 965
             {
844 966
                 for (String value : propertyValue.split(separator))
845 967
                 {
@@ -863,7 +985,7 @@ public class ExcelUtil<T>
863 985
 
864 986
     /**
865 987
      * 反向解析值 男=0,女=1,未知=2
866
-     *
988
+     * 
867 989
      * @param propertyValue 参数值
868 990
      * @param converterExp 翻译注解
869 991
      * @param separator 分隔符
@@ -876,7 +998,7 @@ public class ExcelUtil<T>
876 998
         for (String item : convertSource)
877 999
         {
878 1000
             String[] itemArray = item.split("=");
879
-            if (StringUtils.containsAny(separator, propertyValue))
1001
+            if (StringUtils.containsAny(propertyValue, separator))
880 1002
             {
881 1003
                 for (String value : propertyValue.split(separator))
882 1004
                 {
@@ -1049,6 +1171,13 @@ public class ExcelUtil<T>
1049 1171
                         field.setAccessible(true);
1050 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 1230
      * @param sheetNo sheet数量
1102 1231
      * @param index 序号
1103 1232
      */
@@ -1114,7 +1243,7 @@ public class ExcelUtil<T>
1114 1243
 
1115 1244
     /**
1116 1245
      * 获取单元格值
1117
-     *
1246
+     * 
1118 1247
      * @param row 获取的行
1119 1248
      * @param column 获取单元格列号
1120 1249
      * @return 单元格值
@@ -1174,7 +1303,7 @@ public class ExcelUtil<T>
1174 1303
 
1175 1304
     /**
1176 1305
      * 判断是否是空行
1177
-     *
1306
+     * 
1178 1307
      * @param row 判断的行
1179 1308
      * @return
1180 1309
      */
@@ -1227,4 +1356,61 @@ public class ExcelUtil<T>
1227 1356
         }
1228 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
 }