一、背景
若依 RuoYi-Vue 原本的台账列表导出(excel)功能,效果是统一样式,如下图所示(本文以用户管理页面演示):
现在有个需求,大致是导出可根据自定义模板导出,比如加上导出标题、导出日期、导出人等信息。
目标效果
举例,下图是客户要求的导出模板,希望 标题 ${title} 在导出的时候可以自定义,然后 $ {date} 自动赋值当前日期,${user} 自动赋值当前登录用户名,然后从下面带“表头”字样的那一行开始,填充原本若依导出的表格
模板:
目标效果如下:
二、后端
逻辑
先建一个excel模板,样式如上面所示,存放目录如 src/main/resources/templates/template.xlsx
找到原来用户管理台账导出接口 /export
增加新参数–导出标题 title,
动态替换模板里的变量参数,
修改原来的导出excel方法,在模板excel的基础上,再贴入原先的表格
实现
-
com.ruoyi.web.controller.system.SysUserController
导出接口
@Log(title = "用户管理", businessType = BusinessType.EXPORT) @PreAuthorize("@ss.hasPermi('system:user:export')") @PostMapping("/export") public void export(HttpServletResponse response, SysUser user, String title) throws IOException { List<SysUser> list = userService.selectUserList(user); ExcelUtil<SysUser> util = new ExcelUtil<SysUser>(SysUser.class); // util.exportExcel(response, list, "用户数据"); // 替换模板变量 Map<String, String> replacements = new HashMap<>(16); replacements.put("title",title); replacements.put("date",DateUtils.getDate()); replacements.put("user", getUsername()); // 读取模板文件 Resource resource = new ClassPathResource("templates/template.xlsx"); InputStream templateStream = resource.getInputStream(); util.exportExcelByTemplate(response, list, "用户数据",templateStream,replacements); }
注意
这里读取模板文件的方法是使用InputStream流读取resource目录下的资源,这样的好处是不需要打包,直接用idea启动项目,同个局域网的其他机器访问你本地项目也能进行测试
-
com.ruoyi.common.utils.poi.ExcelUtil
导出方法
/** * 对list数据源将其里面的数据导入到excel表单 * 改造:根据模板导出,可以替换模板里的变量 * @param response 返回数据 * @param list 导出数据集合 * @param sheetName 工作表的名称 * @return 结果 */ public void exportExcelByTemplate(HttpServletResponse response, List<T> list, String sheetName, InputStream inputStream, Map<String, String> replacements) throws IOException { response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setCharacterEncoding("utf-8"); if (list == null) { list = new ArrayList<T>(); } this.list = list; this.sheetName = sheetName; this.type = Type.EXPORT; // 创建工作簿对象 Workbook workbook = WorkbookFactory.create(inputStream); // 关闭输入流 inputStream.close(); // 获取指定的工作表 Sheet sheet = workbook.getSheetAt(0); // 选择第一个工作表,索引从0开始 // 遍历所有行和列,查找包含 ${key} 的单元格 for (Row row : sheet) { for (Cell cell : row) { if (cell.getCellType() == CellType.STRING) { String cellValue = cell.getStringCellValue(); if (cellValue.contains("${")) { // 判断是否包含占位符 for (Map.Entry<String, String> entry : replacements.entrySet()) { String key = "${" + entry.getKey() + "}"; if (cellValue.contains(key)) { cellValue = cellValue.replace(key, entry.getValue()); } } cell.setCellValue(cellValue); // 替换并保持格式 } } } } // 找到第一个单元格值为"表头"的行,并删除它 int headerRowIndex = -1; for (Row row : sheet) { Cell firstCell = row.getCell(0); // 获取第一列单元格 if (firstCell != null && firstCell.getCellType() == CellType.STRING && "表头".equals(firstCell.getStringCellValue())) { headerRowIndex = row.getRowNum(); break; } } if (headerRowIndex != -1) { sheet.removeRow(sheet.getRow(headerRowIndex)); this.rownum = headerRowIndex; } workbook.setSheetName(0, sheetName); this.wb = workbook; this.sheet = sheet; createExcelField(); this.styles = createStyles(wb); exportExcel(response); }
注意
this.rownum 是sheet的当前行数,这里因为我们要在模板已有内容的后面补充原先要导出的表格,所以这个rownum值要更新
三、前端
逻辑
点击导出按钮时,先弹窗让用户输入导出标题,如下图所示
然后点"确定"再调用导出方法,传入的参数新增"title"
实现
-
ruoyi-ui/src/views/system/user/index.vue
<el-col :span="1.5"> <el-button type="warning" plain icon="el-icon-download" size="mini" @click="openExportDialog" v-hasPermi="['system:user:export']" >导出</el-button> </el-col> <!-- 导出弹窗 --> <el-dialog title="导出数据" :visible.sync="exportDialogVisible" width="30%"> <el-form label-width="80px"> <el-form-item label="导出标题"> <el-input v-model="exportTitle" placeholder="请输入导出文件的标题"></el-input> </el-form-item> </el-form> <span slot="footer" class="dialog-footer"> <el-button @click="exportDialogVisible = false">取消</el-button> <el-button type="primary" @click="handleConfirmExport">确定</el-button> </span> </el-dialog>
data() { return { // 控制弹窗 exportDialogVisible: false, // 导出标题 exportTitle: "", }; }
// 打开导出弹窗 openExportDialog() { this.exportTitle = ""; // 清空输入框 this.exportDialogVisible = true; }, // 确认导出 handleConfirmExport() { if (!this.exportTitle) { this.$message.warning("请输入导出标题"); return; } this.exportDialogVisible = false; // 关闭弹窗 // 调用导出 API,带上标题参数 this.download("system/user/export", { ...this.queryParams, title: this.exportTitle }, `user_${new Date().getTime()}.xlsx`); },
四、最终效果
如下图所示