程序员必备小知识系列--使用JasperReport报表工具,做出Java项目报表打印功能(三)
上一篇做出来的效果是这样的
报表里面有很多功能,你们根据需要来弄。 接下来,是要讲如何在java项目中应用。会在前后端代码实现。
一:前端代码(这里是angular前端,我也不是很擅长前端) 1.页面
<button type="button" class="btn btn-primary" (click)="previewReport(prodProcessCabinetOrder)">预览</button> <button type="button" class="btn btn-primary" (click)="printReport(prodProcessCabinetOrder)">打印</button> <button type="button" class="btn btn-primary" (click)="update(prodProcessCabinetOrder)">修改</button> // 中间省略了一些其他代码 <div class="modal fade" id="pdfViewerModel" role="dialog" tabindex="-1" aria-labelledby="pdfViewerModelLabel"> <div class="modal-dialog modal-lg" role="document" style="width: 60%"> <div class="modal-content"> <div class="modal-header"> <button type="button" (click)="close()" class="close" aria-label="Close"> <span aria-hidden="true">×</span> </button> <h4 class="modal-title" id="pdfViewerModelLabel">打印预览</h4> </div> <div class="modal-body" style="height: 800px;"> <ng2-pdfjs-viewer #pdfViewerOnDemand></ng2-pdfjs-viewer> </div> </div> </div> </div> 复制代码
2.前端需要导入pdf的模块什么的,这一点不是很了解,不擅长前端
import { PdfJsViewerModule } from 'ng2-pdfjs-viewer'; 复制代码
3.angular也有实体类,跟后台代码一样,不过其他前端框架是没有实体类的(用其他前端框架的,可以忽略)
export class ProdProcessCabinetOrder { id: number; orderNo: string; orderType: string; drawingNo: string; productType: string; customerNo: string; customerName: string; consignee: string; consigneePhone: string; consigneeAddr: string; terminalCustomer: string; documentMakerNo: string; documentMakerName: string; documentMakeDate: string; deliveryDate: string; orderStatus: number; processCabinetDetails: ProdProcessCabinetDetail[]; downloadExceled: boolean; grain: number; processOrderRouting: ProcessOrderRouting; refOrderNo: string; } 复制代码
4.前端实现功能(这个类似于ajax什么的)
@ViewChild('pdfViewerOnDemand', { static: false }) pdfViewerOnDemand; previewReport(prodProcessCabinetOrder: ProdProcessCabinetOrder) { let fileName = '柜体生产流程单' + prodProcessCabinetOrder.refOrderNo + '.pdf';//给文件名赋值 //根据id找到prodProcessCabinetOrder对象 this.prodProcessCabinetOrderService.getReportById(prodProcessCabinetOrder.id).subscribe( res => { //下面几个是给pdfViewerOnDemand属性赋值 this.pdfViewerOnDemand.pdfSrc = res; this.pdfViewerOnDemand.downloadFileName = fileName; this.pdfViewerOnDemand.refresh(); $('#pdfViewerModel').modal({ backdrop: 'static', keyboard: false, show: true }); }, error => this.errorMessage = <any>error ); } //打印报表功能 printReport(prodProcessCabinetOrder: ProdProcessCabinetOrder) { this.prodProcessCabinetOrderService.printReportById(prodProcessCabinetOrder.id).subscribe( any => { }, error => this.errorMessage = <any>error ); } //关闭报表 close() { $('#pdfViewerModel').modal('hide'); } 复制代码
不同于layui,easyui这些前端框架,用的ajax会不一样(思路可以这样:根据id找到该对象,如何就触发其具体功能,把需要的参数传给后端)
二:编译文件以及所需要的文件 jrxml文件,右击,选择Compile Report
文件编译之后,生成的jasper文件,把需要的jasper文件放到java项目工程里
我把编译的文件放在resources包下,stsong是字体文件,jasperreports_extension.properties是报表的配置文件
报表中,我用的是华文字体
stsong文件下的fonts.xml代码:
<?xml version="1.0" encoding="UTF-8"?> <fontFamilies> <fontFamily name="华文宋体"> <normal>stsong/stsong.TTF</normal> <bold>stsong/stsong.TTF</bold> <italic>stsong/stsong.TTF</italic> <boldItalic>stsong/stsong.TTF</boldItalic> <pdfEncoding>Identity-H</pdfEncoding> <pdfEmbedded>true</pdfEmbedded> <exportFonts> <export key="net.sf.jasperreports.html">'华文宋体', Arial, Helvetica, sans-serif</export> <export key="net.sf.jasperreports.xhtml">'华文宋体', Arial, Helvetica, sans-serif</export> </exportFonts> </fontFamily> </fontFamilies> 复制代码
stsong.TTF文件(双击打开是这样的)
jasperreports_extension.properties文件
net.sf.jasperreports.extension.registry.factory.simple.font.families=net.sf.jasperreports.engine.fonts.SimpleFontExtensionsRegistryFactory net.sf.jasperreports.extension.simple.font.families.lobstertwo=stsong/fonts.xml 复制代码
至于上面说的文件,网上都有,都是下载下来的
三:后端代码(新建javaweb项目那些就不演示了) 记得导入相关的jar包 1.实体类
@Entity public class ProdProcessCabinetOrder { @Id @GeneratedValue private Integer id; private String orderNo; private String orderType; private String drawingNo; private String productType; private String customerNo; private String customerName; private String consignee; private String consigneePhone; private String consigneeAddr; private String terminalCustomer; private String documentMakerNo; private String documentMakerName; private Date documentMakeDate; private Date deliveryDate; @ColumnDefault("0") private int grain; private int orderStatus; @ElementCollection(fetch = FetchType.EAGER) @CollectionTable(name = "prod_process_cabinet_order_process_cabinet_details", joinColumns = @JoinColumn(name = "prod_process_cabinet_order_id")) private List<ProdProcessCabinetDetail> processCabinetDetails;//这个是子对象 private boolean downloadExceled; @ManyToOne private ProcessOrderRouting processOrderRouting; @ColumnDefault("false") private boolean done; private String refOrderNo; } 复制代码
2.子对象
@Embeddable public class ProdProcessCabinetDetail { private String cabinetComponentNo; private String cabinetComponentName; private float cabinetComponentWidth; private float cabinetComponentHeight; @ColumnDefault("0") private float thick; private int cabinetComponentQuantity; private String cabinetComponentItemNo; private String cabinetComponentItemName; private float cabinetComponentItemWidth; private float cabinetComponentItemHeight; private String cabinetEdgeItemNo; private String cabinetEdgeItemName; private String cabinetEdgeItemNo2; private String cabinetEdgeItemName2; private int widthEdgeQty; private int heightEdgeQty; @ColumnDefault("0") private float edge1ItemSize; @ColumnDefault("0") private float edge2ItemSize; private String slot; private String remark; @ColumnDefault("false") private boolean packinged; } 复制代码
3.报表类
public class ProdProcessCabinetReport { private Integer index; private String cabinetComponentName; private float cabinetComponentWidth; private float cabinetComponentHeight; private float cabinetComponentItemWidth; private float cabinetComponentItemHeight; private int cabinetComponentQuantity; private float thick; private String cabinetComponentItemName; private String cabinetEdgeItemName; private String cabinetEdgeItemName2; private String remark; //省略getter,setter } 复制代码
注意:我这里需要多个对象,所以,把代码放出来了。你们根据自己的实际情况,一个实体类也可以的
4.service层代码:
@Override public ResponseEntity<byte[]> getReportById(Integer id) { ProdProcessCabinetOrder prodProcessCabinetOrder = this.getProdProcessCabinetOrder(id);//根据id找到ProdProcessCabinetOrder对象 HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.parseMediaType("application/pdf"));//报表的格式 String pdfName = "柜体生产流程单" + prodProcessCabinetOrder.getRefOrderNo() + ".pdf";//文件名字 headers.setContentDispositionFormData(pdfName, pdfName);//调用HttpHeaders方法 byte[] contents = buildPdfDocument(prodProcessCabinetOrder);//具体实现功能 ResponseEntity<byte[]> response = new ResponseEntity<byte[]>(contents, headers, HttpStatus.OK); prodProcessCabinetOrder.setDownloadExceled(true);//设置相关属性 processCabinetOrderRepository.save(prodProcessCabinetOrder);//保存 return response; } private byte[] buildPdfDocument(ProdProcessCabinetOrder prodProcessCabinetOrder) { ByteArrayOutputStream os = new ByteArrayOutputStream(); try { Resource resource = new ClassPathResource("jasperreport/productionprocess/cabinet/cabinet.jasper");//编译文件的路径 FileInputStream fis = new FileInputStream(resource.getFile()); Resource subReportResource = new ClassPathResource( "jasperreport/productionprocess/cabinet/cabinet_footer.jasper");//第二个编译文件的路径 String footerSubReportPath = subReportResource.getFile().getPath(); fillReportData(prodProcessCabinetOrder, fis, os, footerSubReportPath);//处理文件的具体功能 } catch (IOException | JRException e) { e.printStackTrace(); } return os.toByteArray(); } private void fillReportData(ProdProcessCabinetOrder prodProcessCabinetOrder, FileInputStream fis, ByteArrayOutputStream os, String footerSubReportPath) throws JRException { List<ProdProcessCabinetDetail> prodProcessCabinetDetails = prodProcessCabinetOrder.getProcessCabinetDetails();//获得prodProcessCabinetOrder对象的子对象ProdProcessCabinetDetail Map<String, Object> parameters = fillReportParametersData(prodProcessCabinetOrder, prodProcessCabinetDetails, footerSubReportPath);//生成参数的方法,这些参数是报表里面的那些Parameters,就是从这里赋值的 List<ProdProcessCabinetReport> reportFields = fillReportFieldsData(prodProcessCabinetDetails);//这个是Fields的方法,后台的对应报表里面的Fields JasperRunManager.runReportToPdfStream(fis, os, parameters, new JRBeanCollectionDataSource(reportFields));//报表工具提供的方法,这个方法具体实现可以网上了解一些 } //这个方法是对应报表里面的Parameters,这里赋值,传给报表 private Map<String, Object> fillReportParametersData(ProdProcessCabinetOrder prodProcessCabinetOrder, List<ProdProcessCabinetDetail> prodProcessCabinetDetails, String footerSubReportPath) { String qrCodePic = QRCodeUtil.genQRCodePic(OrderType.PROCESS_CABINET_ORDER.getIndex(), prodProcessCabinetOrder.getOrderNo(), prodProcessCabinetOrder.getRefOrderNo(), qrCodeFileProps);//这个是获得图片路径 Map<String, Object> parameters = new HashMap<String, Object>(); parameters.put("reportTitle", "柜体生产流程单"); parameters.put("qrCodePic", qrCodePic); parameters.put("refOrderNo", prodProcessCabinetOrder.getRefOrderNo()); parameters.put("orderType", prodProcessCabinetOrder.getOrderType()); parameters.put("productType", prodProcessCabinetOrder.getProductType()); parameters.put("drawingNo", prodProcessCabinetOrder.getDrawingNo()); parameters.put("deliveryDate", DateFormat.formatDate("yyyy-MM-dd", prodProcessCabinetOrder.getDeliveryDate())); parameters.put("terminalCustomer", prodProcessCabinetOrder.getTerminalCustomer()); double sumComponItemsArea = prodProcessCabinetOrder.sumComponItemsArea(); parameters.put("sumComponItemsArea", (double) (Math.round(sumComponItemsArea * 100)) / 100); parameters.put("sumQty", prodProcessCabinetOrder.sumQty()); parameters.put("customerName", prodProcessCabinetOrder.getCustomerName()); parameters.put("documentMakeDate", DateFormat.formatDate("yyyy-MM-dd", prodProcessCabinetOrder.getDocumentMakeDate())); parameters.put("documentMakerName", prodProcessCabinetOrder.getDocumentMakerName()); parameters.put("cabinet_footer_report_path", footerSubReportPath); return parameters; } //这个方法和下面的方法是生成Fields数据,对应报表的Fields private List<ProdProcessCabinetReport> fillReportFieldsData( List<ProdProcessCabinetDetail> prodProcessCabinetDetails) { List<ProdProcessCabinetReport> reportFields = new ArrayList<ProdProcessCabinetReport>(); if (prodProcessCabinetDetails != null && prodProcessCabinetDetails.size() > 0) { int index = 1; for (ProdProcessCabinetDetail detail : prodProcessCabinetDetails) { fillReportFieldData(detail, reportFields, index); index++; } } return reportFields; } //获得Fields数据 private void fillReportFieldData(ProdProcessCabinetDetail detail, List<ProdProcessCabinetReport> reportFields, int index) { ProdProcessCabinetReport processCabinetReport = new ProdProcessCabinetReport(); processCabinetReport.setIndex(index); processCabinetReport.setCabinetComponentName(detail.getCabinetComponentName()); processCabinetReport.setCabinetComponentWidth(detail.getCabinetComponentWidth()); processCabinetReport.setCabinetComponentHeight(detail.getCabinetComponentHeight()); processCabinetReport.setCabinetComponentItemWidth(detail.getCabinetComponentItemWidth()); processCabinetReport.setCabinetComponentItemHeight(detail.getCabinetComponentItemHeight()); processCabinetReport.setCabinetComponentQuantity(detail.getCabinetComponentQuantity()); processCabinetReport.setThick(detail.getThick()); processCabinetReport.setCabinetComponentItemName(detail.getCabinetComponentItemName()); processCabinetReport.setCabinetEdgeItemName(detail.getCabinetEdgeItemName()); processCabinetReport.setCabinetEdgeItemName2(detail.getCabinetEdgeItemName2()); processCabinetReport.setRemark(detail.getRemark()); reportFields.add(processCabinetReport); } @Override public void printReportById(Integer id) { ProdProcessCabinetOrder processCabinetOrder = this.getProdProcessCabinetOrder(id); byte[] pdfByte = this.buildPdfDocument(processCabinetOrder); PdfPrintUtil pdfPrintUtil = new PdfPrintUtil(); pdfPrintUtil.printPdf(pdfByte, "######"); } 复制代码
打印方法
public class PdfPrintUtil { public void printPdf(byte[] pdfByte, String printerName) { PDDocument document = null; try { document = PDDocument.load(pdfByte); PrinterJob job = setPrinterJonParas(document, printerName); job.print(); } catch (IOException | PrinterException e) { System.out.println("打印pdf文件出错"); e.printStackTrace(); } finally { if (document != null) { try { document.close(); } catch (IOException e) { System.out.println("关闭pdf-document出错"); e.printStackTrace(); } } } } /*** * * @param document * @param printerName * 打印机名称 * @return 没有指定则找默认的打印机 * @throws PrinterException */ private PrinterJob setPrinterJonParas(PDDocument document, String printerName) throws PrinterException { // 设置纸张及缩放 PDFPrintable pdfPrintable = new PDFPrintable(document, Scaling.ACTUAL_SIZE); PageFormat pageFormat = new PageFormat(); // 设置打印方向(横向) pageFormat.setOrientation(PageFormat.LANDSCAPE); // 设置纸张属性 pageFormat.setPaper(this.getPaper()); Book book = new Book(); book.append(pdfPrintable, pageFormat, document.getNumberOfPages()); PrinterJob job = PrinterJob.getPrinterJob(); job.setPrintService(this.getPrinter(printerName)); job.setPageable(book); // 设置打印份数 job.setCopies(1); return job; } private Paper getPaper() { Paper paper = new Paper(); // 默认为A4纸张,对应像素宽和高分别为595,842 int width = 595; int height = 842; // 设置边距,单位是像素 int marginLeft = 0, marginRight = 0, marginTop = 0, marginBottom = 0; paper.setSize(width, height); paper.setImageableArea(marginLeft, marginTop, width - (marginLeft + marginRight), height - (marginTop + marginBottom)); return paper; } private PrintService getPrinter(String printerName) { DocFlavor psInFormat = DocFlavor.INPUT_STREAM.PDF; PrintRequestAttributeSet pras = new HashPrintRequestAttributeSet(); // A4纸 pras.add(MediaSizeName.ISO_A4); // 单页 pras.add(Sides.ONE_SIDED); PrintService[] printServices = PrintServiceLookup.lookupPrintServices(psInFormat, pras); PrintService myPrinter = null; for (PrintService printService : printServices) { System.out.println("打印机名字" + printService.getName()); if (printService.getName().contains(printerName)) { myPrinter = printService; break; } } if (myPrinter == null) { myPrinter = PrintServiceLookup.lookupDefaultPrintService(); } return myPrinter; } } 复制代码
可以参考我写的方法,但不一定适合。总结来说,从实体类对象把值赋给了对应的报表
参考效果:
点击预览,效果如下
打印功能也是可以的。记得连接打印机
更多功能,这里就不一 一展示了。
作者:exodus3
链接:https://juejin.cn/post/7020798085534056462