From 3f19a39721b31ced6aa0626be8257544da8179b6 Mon Sep 17 00:00:00 2001 From: liyang Date: Sun, 28 Aug 2022 21:21:02 +0800 Subject: [PATCH] =?UTF-8?q?Excel=E5=AF=BC=E5=85=A5=E6=A8=A1=E6=9D=BF?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=89=B9=E6=B3=A8=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../excel/builder/CellModelBuilder.java | 33 +++++++ .../excel/builder/SheetModelBuilder.java | 27 ++++++ .../common/excel/common/SheetCellModel.java | 29 ++++++ .../ruoyi/common/excel/common/SheetModel.java | 25 ++++++ .../handler/CustomCommentWriteHandler.java | 90 +++++++++++++++++++ .../common/excel/model/CommentModel.java | 42 +++++++++ .../com/ruoyi/common/utils/poi/ExcelUtil.java | 46 +++++++++- .../ruoyi/common/utils/poi/PoiExcelUtil.java | 70 +++++++++++++++ .../demo/controller/TestDemoController.java | 15 ++++ .../src/main/resources/vm/vue/v3/readme.txt | 2 +- ruoyi-ui/src/views/demo/demo/index.vue | 11 ++- 11 files changed, 387 insertions(+), 3 deletions(-) create mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/excel/builder/CellModelBuilder.java create mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/excel/builder/SheetModelBuilder.java create mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/excel/common/SheetCellModel.java create mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/excel/common/SheetModel.java create mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/excel/handler/CustomCommentWriteHandler.java create mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/excel/model/CommentModel.java create mode 100644 ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/PoiExcelUtil.java diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/excel/builder/CellModelBuilder.java b/ruoyi-common/src/main/java/com/ruoyi/common/excel/builder/CellModelBuilder.java new file mode 100644 index 000000000..bad644f26 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/excel/builder/CellModelBuilder.java @@ -0,0 +1,33 @@ +package com.ruoyi.common.excel.builder; + +import com.ruoyi.common.excel.common.SheetModel; +import lombok.Getter; + +/** + * 单元格信息构建器 + * + * @author liyang + **/ +@Getter +public abstract class CellModelBuilder extends SheetModelBuilder { + /** + * 列索引 + */ + protected final int colIndex; + /** + * 行索引 + */ + protected final int rowIndex; + + protected CellModelBuilder(String sheetName, int rowIndex, int colIndex) { + super(sheetName); + this.rowIndex = rowIndex; + this.colIndex = colIndex; + } + /** + * 具体构建方法 + * @return + */ + @Override + abstract protected SheetModel build() ; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/excel/builder/SheetModelBuilder.java b/ruoyi-common/src/main/java/com/ruoyi/common/excel/builder/SheetModelBuilder.java new file mode 100644 index 000000000..5ba19ee6c --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/excel/builder/SheetModelBuilder.java @@ -0,0 +1,27 @@ +package com.ruoyi.common.excel.builder; + + +import com.ruoyi.common.excel.common.SheetModel; +import lombok.Getter; + +/** + * sheet页信息构建器 + * + * @author liyang + **/ +@Getter +public abstract class SheetModelBuilder { + /** + * sheet名称 + */ + protected final String sheetName; + protected SheetModelBuilder(String sheetName) { + this.sheetName = sheetName; + } + + /** + * 具体构建方法 + * @return + */ + abstract protected SheetModel build() ; +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/excel/common/SheetCellModel.java b/ruoyi-common/src/main/java/com/ruoyi/common/excel/common/SheetCellModel.java new file mode 100644 index 000000000..31fbf34ba --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/excel/common/SheetCellModel.java @@ -0,0 +1,29 @@ +package com.ruoyi.common.excel.common; + +import com.ruoyi.common.excel.builder.CellModelBuilder; +import lombok.Getter; + +/** + * 单元格信息 + * + * @author liyang + */ +@Getter +public class SheetCellModel extends SheetModel { + /** + * 列索引 + */ + protected int colIndex; + /** + * 行索引 + */ + protected int rowIndex; + + public SheetCellModel(CellModelBuilder cellModelBuilder) { + super(cellModelBuilder); + this.rowIndex = cellModelBuilder.getRowIndex(); + this.colIndex = cellModelBuilder.getColIndex(); + } + protected SheetCellModel(){ + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/excel/common/SheetModel.java b/ruoyi-common/src/main/java/com/ruoyi/common/excel/common/SheetModel.java new file mode 100644 index 000000000..d8ef18f29 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/excel/common/SheetModel.java @@ -0,0 +1,25 @@ +package com.ruoyi.common.excel.common; + +import com.ruoyi.common.excel.builder.SheetModelBuilder; +import lombok.Getter; + +/** + * sheet页信息 + * + * @author liyang + */ +@Getter +public class SheetModel { + /** + * sheet名称 + */ + protected String sheetName; + + public SheetModel(SheetModelBuilder sheetModelBuilder) { + this.sheetName = sheetModelBuilder.getSheetName(); + } + + protected SheetModel() { + + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/excel/handler/CustomCommentWriteHandler.java b/ruoyi-common/src/main/java/com/ruoyi/common/excel/handler/CustomCommentWriteHandler.java new file mode 100644 index 000000000..3f09d4e14 --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/excel/handler/CustomCommentWriteHandler.java @@ -0,0 +1,90 @@ +package com.ruoyi.common.excel.handler; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.alibaba.excel.write.handler.RowWriteHandler; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; +import com.alibaba.excel.write.metadata.holder.WriteTableHolder; +import com.ruoyi.common.excel.model.CommentModel; +import com.ruoyi.common.utils.poi.PoiExcelUtil; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 自定义批注处理器 + * + * @author liyang + */ +public class CustomCommentWriteHandler implements RowWriteHandler { + /** + * sheet页名称列表 + */ + private List sheetNameList; + List commentList = new ArrayList<>(); + + /** + * 自定义批注适配器构造方法 + * + * @param commentList 批注信息 + * @param extension 文件后缀(xlsx、xls) + */ + public CustomCommentWriteHandler(List commentList, String extension) { + if (CollUtil.isEmpty(commentList)) { + return; + } + //文件不为指定的格式时,默认为Xlsx + if (StrUtil.equals(extension, "xlsx") == false && StrUtil.equals(extension, "xls") == false) { + extension = "xlsx"; + } + this.commentList = commentList.stream().filter(x -> + StrUtil.isNotBlank(x.getSheetName()) && x.getColIndex() >=0 && x.getRowIndex() >= 0 && StrUtil.isNotBlank(x.getCommentContent()) + ).collect(Collectors.toList()); + sheetNameList = this.commentList.stream().map(x -> x.getSheetName()).distinct().collect(Collectors.toList()); + this.extension = extension; + } + + /** + * 文档后缀名 + */ + private String extension; + + @Override + public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, + Integer relativeRowIndex, Boolean isHead) { + Sheet sheet = writeSheetHolder.getSheet(); + //不需要添加批注,或者当前sheet页不需要添加批注 + if (CollUtil.isEmpty(commentList) || sheetNameList.contains(sheet.getSheetName()) == false) { + return; + } + //获取当前行的批注信息 + List rowCommentList = commentList.stream().filter(x -> + StrUtil.equals(x.getSheetName(), sheet.getSheetName()) + && StrUtil.equals(String.valueOf(relativeRowIndex), String.valueOf(x.getRowIndex()))).collect(Collectors.toList()); + //当前行没有批注信息 + if (CollUtil.isEmpty(rowCommentList)) { + return; + } + List colIndexList = rowCommentList.stream().map(x -> x.getColIndex()).distinct().collect(Collectors.toList()); + for (Integer colIndex : colIndexList) { + //同一单元格的批注信息 + List cellCommentList = rowCommentList.stream().filter(x -> + StrUtil.equals(String.valueOf(colIndex), String.valueOf(x.getColIndex()))).collect(Collectors.toList()); + if (CollUtil.isEmpty(cellCommentList)) { + continue; + } + //批注内容拼成一条 + String commentContent = cellCommentList.stream().map(x -> x.getCommentContent()).collect(Collectors.joining()); + Cell cell = row.getCell(colIndex); + PoiExcelUtil.addComment(cell, commentContent, extension); + } + //删除批注信息 + commentList.remove(rowCommentList); + //重新获取要添加的sheet页姓名 + sheetNameList = commentList.stream().map(x -> x.getSheetName()).distinct().collect(Collectors.toList()); + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/excel/model/CommentModel.java b/ruoyi-common/src/main/java/com/ruoyi/common/excel/model/CommentModel.java new file mode 100644 index 000000000..82321eeed --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/excel/model/CommentModel.java @@ -0,0 +1,42 @@ +package com.ruoyi.common.excel.model; + +import com.ruoyi.common.excel.common.SheetCellModel; +import lombok.Getter; + +/** + * 批注信息类 + * + * @author liyang + */ +@Getter +public class CommentModel extends SheetCellModel { + /** + * 批注内容 + */ + private String commentContent; + + private CommentModel() { + } + + /** + * 生成批注信息 + * + * @param sheetName sheet页名称 + * @param rowIndex 行号 + * @param columnIndex 列号 + * @param commentContent 批注内容 + * @return + */ + public static CommentModel createCommentModel(String sheetName, int rowIndex, int columnIndex, String commentContent) { + CommentModel commentModel = new CommentModel(); + //sheet页名称 + commentModel.sheetName = sheetName; + //行号 + commentModel.rowIndex = rowIndex; + //列号 + commentModel.colIndex = columnIndex; + //批注内容 + commentModel.commentContent = commentContent; + return commentModel; + } +} diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java index e6a67b26b..3c92b367d 100644 --- a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/ExcelUtil.java @@ -15,6 +15,8 @@ import com.ruoyi.common.excel.CellMergeStrategy; import com.ruoyi.common.excel.DefaultExcelListener; import com.ruoyi.common.excel.ExcelListener; import com.ruoyi.common.excel.ExcelResult; +import com.ruoyi.common.excel.handler.CustomCommentWriteHandler; +import com.ruoyi.common.excel.model.CommentModel; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.file.FileUtils; import lombok.AccessLevel; @@ -116,7 +118,49 @@ public class ExcelUtil { throw new RuntimeException("导出Excel异常"); } } - + /** + * 导出excel模板 + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param clazz 实体类 + * @param commentList 批注 + * @param response 响应体 + */ + public static void exportExcelCommentTemplate(List list, List commentList, String sheetName, Class clazz, HttpServletResponse response) { + exportExcelCommentTemplate(list,commentList, sheetName, clazz, false, response); + } + /** + * 导出excel模板 + * + * @param list 导出数据集合 + * @param sheetName 工作表的名称 + * @param clazz 实体类 + * @param merge 是否合并单元格 + * @param commentList 批注 + * @param response 响应体 + */ + public static void exportExcelCommentTemplate(List list, List commentList, String sheetName, Class clazz, boolean merge, HttpServletResponse response) { + try { + resetResponse(sheetName, response); + ServletOutputStream os = response.getOutputStream(); + ExcelWriterSheetBuilder builder = EasyExcel.write(os, clazz) + .autoCloseStream(false) + // 自动适配 + .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) + .registerWriteHandler(new CustomCommentWriteHandler(commentList, "xlsx")) + // 大数值自动转换 防止失真 + .registerConverter(new ExcelBigNumberConvert()) + .sheet(sheetName); + if (merge) { + // 合并处理器 + builder.registerWriteHandler(new CellMergeStrategy(list, true)); + } + builder.doWrite(list); + } catch (IOException e) { + throw new RuntimeException("导出Excel异常"); + } + } /** * 单表多数据模板导出 模板格式为 {.属性} * diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/PoiExcelUtil.java b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/PoiExcelUtil.java new file mode 100644 index 000000000..1b5ccac6c --- /dev/null +++ b/ruoyi-common/src/main/java/com/ruoyi/common/utils/poi/PoiExcelUtil.java @@ -0,0 +1,70 @@ +package com.ruoyi.common.utils.poi; + +import org.apache.poi.hssf.usermodel.HSSFClientAnchor; +import org.apache.poi.hssf.usermodel.HSSFRichTextString; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFRichTextString; + +/** + * POI Excel工具类 + * + * @author liyang + */ +public class PoiExcelUtil { + /** + * 给Cell添加批注 + * + * @param cell 单元格 + * @param value 批注内容 + * @param extension 扩展名 + */ + public static void addComment(Cell cell, String value, String extension) { + Sheet sheet = cell.getSheet(); + cell.removeCellComment(); + if ("xls".equals(extension)) { + ClientAnchor anchor = new HSSFClientAnchor(); + // 关键修改 + anchor.setDx1(0); + anchor.setDx2(0); + anchor.setDy1(0); + anchor.setDy2(0); + anchor.setCol1(cell.getColumnIndex()); + anchor.setRow1(cell.getRowIndex()); + anchor.setCol2(cell.getColumnIndex() + 5); + anchor.setRow2(cell.getRowIndex() + 6); + // 结束 + Drawing drawing = sheet.getDrawingPatriarch(); + if (drawing == null) { + drawing = sheet.createDrawingPatriarch(); + } + Comment comment = drawing.createCellComment(anchor); + // 输入批注信息 + comment.setString(new HSSFRichTextString(value)); + // 将批注添加到单元格对象中 + cell.setCellComment(comment); + } else if ("xlsx".equals(extension)) { + ClientAnchor anchor = new XSSFClientAnchor(); + // 关键修改 + anchor.setDx1(0); + anchor.setDx2(0); + anchor.setDy1(0); + anchor.setDy2(0); + anchor.setCol1(cell.getColumnIndex()); + anchor.setRow1(cell.getRowIndex()); + anchor.setCol2(cell.getColumnIndex() + 5); + anchor.setRow2(cell.getRowIndex() + 6); + // 结束 + Drawing drawing = sheet.getDrawingPatriarch(); + if (drawing == null) { + drawing = sheet.createDrawingPatriarch(); + } + Comment comment = drawing.createCellComment(anchor); + // 输入批注信息 + comment.setString(new XSSFRichTextString(value)); + // 将批注添加到单元格对象中 + cell.setCellComment(comment); + } + } + +} diff --git a/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java index 33b341282..a80b63926 100644 --- a/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java +++ b/ruoyi-demo/src/main/java/com/ruoyi/demo/controller/TestDemoController.java @@ -13,6 +13,7 @@ import com.ruoyi.common.core.validate.EditGroup; import com.ruoyi.common.core.validate.QueryGroup; import com.ruoyi.common.enums.BusinessType; import com.ruoyi.common.excel.ExcelResult; +import com.ruoyi.common.excel.model.CommentModel; import com.ruoyi.common.utils.ValidatorUtils; import com.ruoyi.common.utils.poi.ExcelUtil; import com.ruoyi.demo.domain.TestDemo; @@ -29,6 +30,7 @@ import org.springframework.web.multipart.MultipartFile; import javax.servlet.http.HttpServletResponse; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.TimeUnit; @@ -145,4 +147,17 @@ public class TestDemoController extends BaseController { @PathVariable Long[] ids) { return toAjax(iTestDemoService.deleteWithValidByIds(Arrays.asList(ids), true) ? 1 : 0); } + + /** + * 获取导入模板 + */ + @PostMapping("/importTemplate") + public void importTemplate(HttpServletResponse response) { + List commentList = new ArrayList<>(); + String sheetName = "组织机构"; + commentList.add(CommentModel.createCommentModel(sheetName, 0, 0, "部门id不能为空\n换行文本")); + commentList.add(CommentModel.createCommentModel(sheetName, 0, 1, "用户id不能为空\n换行文本")); + commentList.add(CommentModel.createCommentModel(sheetName, 0, 2, "排序好不能为空\n换行文本")); + ExcelUtil.exportExcelCommentTemplate(new ArrayList<>(),commentList, sheetName, TestDemoImportVo.class, response); + } } diff --git a/ruoyi-generator/src/main/resources/vm/vue/v3/readme.txt b/ruoyi-generator/src/main/resources/vm/vue/v3/readme.txt index 99239bb53..e6dc758af 100644 --- a/ruoyi-generator/src/main/resources/vm/vue/v3/readme.txt +++ b/ruoyi-generator/src/main/resources/vm/vue/v3/readme.txt @@ -1 +1 @@ -ʹõRuoYi-Vue3ǰˣôҪһ´Ŀ¼ģindex.vue.vmindex-tree.vue.vmļϼvueĿ¼ \ No newline at end of file +如果使用的是Vue3前端,那么需要覆盖一下此目录的模板index.vue.vm、index-tree.vue.vm文件到上级vue目录。 diff --git a/ruoyi-ui/src/views/demo/demo/index.vue b/ruoyi-ui/src/views/demo/demo/index.vue index 65387c9a5..316d3a150 100644 --- a/ruoyi-ui/src/views/demo/demo/index.vue +++ b/ruoyi-ui/src/views/demo/demo/index.vue @@ -190,6 +190,10 @@ >
将文件拖到此处,或点击上传
+
+ 仅允许导入xls、xlsx格式文件。 + 下载模板 +