上一篇,链接点击这里
做出来的效果是这样的
报表里面有很多功能,你们根据需要来弄。
接下来,是要讲如何在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;
}
}
可以参考我写的方法,但不一定适合。总结来说,从实体类对象把值赋给了对应的报表
参考效果:
点击预览,效果如下
打印功能也是可以的。记得连接打印机
更多功能,这里就不一 一展示了。