如何以二开的方式引出Excel并自定义样式
小编有话说
在客户的实际业务中,经常需要引出报表或动态表单的数据,并且对引出数据的表格样式有个性化需求,比如添加标题行、调整单元格高度、单元格背景色、文字字体/颜色/字号等等。目前苍穹提供的报表引出插件暂不支持引出表格个性化样式的实现。
因此,本篇文章介绍如何通过纯二开的方式,帮助客户实现引出表格数据的个性化样式需求。
1 案例背景
客户系统有两张单据:水果单和化肥单,需要把这两张单据的数据引出到Excel表格中。
水果单据的关键字段有【产品类型】(下拉列表)、【产地】(基础资料)、【水果名称】(单据体文本),如下图所示:
图1-水果单据示例
化肥单据的关键字段有【肥料名称】(文本)、【产地】(基础资料),如下图所示:
图2-化肥单据示例
两张单据的列表数据如下:
图3-水果列表数据示例
图4-化肥列表数据示例
报表和引出数据都需要对2张单据进行查询,需要实现引出的效果如下:
图5-报表实现按钮示例
图6-引出Excel示例
2 解决方案
2.1 方案整体思路
在这个方案的设计过程中,报表和引出数据都需要对2张单据进行查询,并利用文件服务提供下载功能。
同时,把查询和引出的代码与报表插件剥离,放到工具类中,做到代码复用,方便后续将该方案迁移到其他业务场景中使用。
整体二开流程如下:
1. 新建报表页面,设计报表,注册报表插件;
2. 抽取查询数据公共代码,编写查询数据工具类;
3. 编写报表取数插件,查询数据展示在报表上;
4. 编写报表界面插件,实现引出Excel文件,并修改Excel样式。
2.2 二开步骤详解
步骤1:新建报表页面,注册报表取数插件和报表页面插件
在【开发平台】表单设计器中,设计报表页面,并注册报表取数插件和报表页面插件,如下图所示:
图7-设计报表列表字段
图8-注册报表取数插件
图9-注册报表页面插件
步骤2:编写查询数据工具类
查询数据的代码,是引出的数据源,同时也是报表的数据源,这一部分的逻辑写在一个工具类中,共用代码。
使用QueryServiceHelper查询数据,将2个DateSet对象用join进行合并,代码如下:
public class FPQueryUtil { public static DataSet queryData() { DataSet dataSetFruits = QueryServiceHelper.queryDataSet("FruitPro", "kdec_fruit_bill", "id, billno as kdec_fnumber, kdec_entryentity.kdec_fruits as kdec_fname, kdec_fruit_type, kdec_fruit_pro.id as proid, kdec_fruit_pro.name as kdec_pro", null, ""); DataSet dataSet2 = dataSetFruits.copy(); Iterator<Row> iterator = dataSet2.iterator(); List proList = new ArrayList<>(); while (iterator.hasNext()) { Row row = iterator.next(); proList.add(row.get("proid")); } QFilter regionFilter = new QFilter("kdec_region", QCP.in, proList); QFilter statusFilter = new QFilter("billstatus", QCP.like, "C"); DataSet dataSetFertilizer = QueryServiceHelper.queryDataSet("FruitPro", "kdec_fertilizer_bill", "id, billno as kdec_fernumber, kdec_name as kdec_fername, kdec_region", new QFilter[]{regionFilter,statusFilter}, ""); DataSet result = dataSetFruits.rightJoin(dataSetFertilizer) .select(new String[]{"kdec_fnumber", "kdec_fname", "kdec_pro", "kdec_fruit_type"}, new String[]{"kdec_fernumber", "kdec_fername"}) .on("proid", "kdec_region").finish(); return result; } }
步骤3:编写报表取数插件
报表取数插件实现代码如下:
public class FruitProReportListDataPlugin extends AbstractReportListDataPlugin { @Override public DataSet query(ReportQueryParam reportQueryParam, Object o) throws Throwable { return FPQueryUtil.queryData(); } }
步骤4:编写报表界面插件,引出Excel文件并修改样式
首先,监听按钮点击。调用以下代码,查询数据,构建Excel文件,弹出下载链接。代码如下:
@Override public void itemClick(ItemClickEvent evt) { super.itemClick(evt); switch (evt.getItemKey()) { case "kdec_export": { // 第一行标题 String title = "水果生产以及产地"; // 调用查询工具类,获取数据源对象DataSet DataSet dataSet = FPQueryUtil.queryData(); // excelList用于提供数据源给 excel工作簿,外层的list是行,里面的List<String>是列,存储value List<List<String>> excelList = getExcelList(dataSet); // 创建excel工作簿 XSSFWorkbook workbook = MyExportUtil.reportExcel(excelList, title); // 上传 String path = MyExportUtil.upload(title, workbook); // 弹出提示框 getView().showMessage("下载文件链接:"+ RequestContext.get().getClientFullContextPath() + "/attachment/download.do?path=" + path); } break; } }
接着,解析DataSet,构建数据集合。构建第二行表头,解析DataSet,构建List对象,为即将引出的excel提供数据源,代码如下:
public List<List<String>> getExcelList (DataSet dataSet) { // excelList用于提供数据源给 excel工作簿,外层的list是行,里面的List<String>是列,存储value List<List<String>> excelList = new ArrayList<>(); List<String> titles = new ArrayList<>(); titles.add("水果编码"); titles.add("水果名称"); titles.add("产地"); titles.add("化肥编码"); titles.add("化肥名称"); excelList.add(titles); while (dataSet.hasNext()) { Row row = dataSet.next(); List<String> strings = new ArrayList<>(); for ( int j = 0; j < row.size(); j++) { String value = row.getString(j); strings.add(value); } excelList.add(strings); } return excelList; }
随后,构造XSSFWorkbook。创建XSSFWorkbook对象和XSSFSheet工作表对象,将上一步的数据源填充进XSSFWorkbook,代码如下:
public static XSSFWorkbook reportExcel(List<List<String>> excelList, String title) { //创建excel工作簿 XSSFWorkbook workbook = new XSSFWorkbook(); //创建工作表sheet XSSFSheet sheet = workbook.createSheet(); // 创建标题 XSSFRow headRow = sheet.createRow(0); XSSFCell headCell = headRow.createCell(0); // 设置标题的值 headCell.setCellValue(title); // 合并第1行的前几列,合并列数 = excel的列数 CellRangeAddress titleCellAddresses = new CellRangeAddress(0, 0, 0, excelList.get(0).size()-1); sheet.addMergedRegion(titleCellAddresses); //写入单据列表数据 for (int i = 0; i < excelList.size(); i++) { // i+1是因为前面第1行加了一个标题,单据列表数据是从Excel的第2行开始的,所以要+1 XSSFRow nrow = sheet.createRow(i + 1); for (int u = 0; u < excelList.get(i).size(); u++) { XSSFCell ncell = nrow.createCell(u); ncell.setCellStyle(billStyle); ncell.setCellValue(excelList.get(i).get(u)); } } }
然后,调整表格的样式。为excel单元格设置背景、字颜色、字体等样式。感兴趣的开发者可以自行在csdn、百度等网站搜索XSSFCell的用法。部分代码如下:
//设置整个表格的默认列宽 sheet.setDefaultColumnWidth(20); // 设置首行标题的一些风格样式 XSSFCellStyle titleStyle = workbook.createCellStyle(); // 设置背景色 //设置填充方案 titleStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);; // 设置自定义填充颜色,天蓝色 titleStyle.setFillForegroundColor(new XSSFColor(new Color(135,206,250))); // 设置水平居中 titleStyle.setAlignment(HorizontalAlignment.CENTER); // 设置垂直居中 titleStyle.setVerticalAlignment(VerticalAlignment.CENTER); // 设置字 XSSFFont font = workbook.createFont(); // 字体颜色橘红色 font.setColor(new XSSFColor(new Color(255 ,69,0))); // 设置字号 font.setFontHeight(20); // 设置字体 font.setFontName("黑体"); titleStyle.setFont(font); // 设置标题行单元格的Style headCell.setCellStyle(titleStyle); // 普通单元格的style // 单据列表数据风格样式,设置字体为黑体,字号15 XSSFCellStyle billStyle = workbook.createCellStyle(); // 设置背景色以及填充方案 billStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND); billStyle.setFillForegroundColor(new XSSFColor(new Color(220,220,220))); // 设置字体 XSSFFont billFont = workbook.createFont(); billFont.setFontName("宋体"); billFont.setFontHeight(15); billStyle.setFont(billFont); // 在循环体中,XSSFCell对象调用setCellStyle方法,将billStyle设置给每一个单元格
调整好表格样式后,使用文件服务上传文件。调用文件服务上传之后,会有一个path返回,代码如下:
public static String upload (String entityName, XSSFWorkbook workbook) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy_MM_dd_HH_mm_ss"); String fileName = entityName + sdf.format(new Date()) + ".xlsx"; String pathName = "/offices/" + fileName; try { OutputStream outputStream = new ByteArrayOutputStream(); workbook.write(outputStream); InputStream inputStream = parse(outputStream); FileService fs = FileServiceFactory.getAttachmentFileService(); String path = fs.upload(new FileItem(fileName, pathName, inputStream)); return path; } catch (Exception e) { System.out.println(e.getMessage()); } return ""; } public static ByteArrayInputStream parse(final OutputStream out) throws Exception { ByteArrayOutputStream baos = (ByteArrayOutputStream) out; final ByteArrayInputStream swapStream = new ByteArrayInputStream(baos.toByteArray()); return swapStream; }
最后,拼接下载链接。将上一步返回的path对象进行拼接,可以得到文件下载链接,代码如下:
String downloadUrl = RequestContext.get().getClientFullContextPath() + "/attachment/download.do?path=" + path;
注意:如果需要直接下载链接,可以用openUrl方法,在浏览器中打开新页签,访问下载链接,这样浏览器就会直接下载这个Excel文件,代码如下:
getView().openUrl(downloadUrl);
2.3 效果展示
完成上述二开流程后,即可引出Excel并自定义样式,效果如下图所示:
图10-报表引出按钮效果
图11-文件下载提示
图12-文件下载成功
图13-引出Excel效果
3 方案价值
1. 此方案实现了客户的引出数据业务需求;
2. 查询和引出的代码与报表插件剥离,放到工具类中,做到代码复用。
4 常见问题
在上传时,如遇到了异常抛出,文件服务拒绝连接的场景,可以从以下方向排查问题:
检查文件服务的磁盘容量是否超出上限,尝试删除上传路径的部分文件,看看能否解决问题。
检查文件服务是否在运行,尝试重启文件服务。
检查服务端和客户端的端口号是否正确,默认是8100。
图14-异常抛出示例
5 相关资料
如需下载本方案相关补丁包和Java源代码,可下载原帖文末附件:
6 划重点
以二开的方式引出Excel并自定义样式的流程如下:
1. 新建报表页面,设计报表,注册报表插件;
2. 抽取查询数据公共代码,编写查询数据工具类;
3. 编写报表取数插件,查询数据展示在报表上;
4. 编写报表界面插件,实现引出Excel文件,并修改Excel样式。
#往期推荐#
更多精彩内容,“码”上了解!↓
如何以二开的方式引出Excel并自定义样式
本文2024-09-23 00:21:11发表“云苍穹知识”栏目。
本文链接:https://wenku.my7c.com/article/kingdee-cangqiong-138916.html