前端打印功能(vue +springboot)

后端

  • 后端
    • 依赖
    • 生成pdf的方法
    • pdf转图片
    • 使用(用的打印模版是带参数的 ,参数是aaa)
    • 总结
  • 前端
    • 页面
  • 效果

后端

依赖

依赖 一个是用模版生成对应的pdf,一个是用来将pdf转成图片需要的

      <!--打印的--><dependency><groupId>net.sf.jasperreports</groupId><artifactId>jasperreports</artifactId></dependency><dependency><groupId>org.apache.pdfbox</groupId><artifactId>pdfbox</artifactId><version>3.0.0</version></dependency>

所需的文件资源 jasper是模版,然后font是pdf转图片的时候需要的字体
在这里插入图片描述
由于有资源 所以pom配置俩包下面不允许压缩

    <build><resources><resource><targetPath>${project.build.directory}/classes</targetPath><directory>src/main/resources</directory><filtering>true</filtering><excludes><exclude>**/*.jasper</exclude><exclude>**/*.jrxml</exclude><exclude>**/*.TTF</exclude></excludes></resource><resource><targetPath>${project.build.directory}/classes</targetPath><directory>src/main/resources</directory><filtering>false</filtering><includes><include>**/*.jasper</include><include>**/*.jrxml</include><include>**/*.TTF</include></includes></resource></resources></build>

生成pdf的方法

用到的实体类

package com.xueyi.common.core.utils.print.dto;import lombok.Data;@Data
public class PdfInfoDto {private Integer height;private Integer width;private byte[] pdfBytes;
}

生成pdf的方法

package com.xueyi.common.core.utils.print;import com.xueyi.common.core.utils.print.dto.PdfInfoDto;
import net.sf.jasperreports.engine.*;
import net.sf.jasperreports.engine.export.JRPdfExporter;
import net.sf.jasperreports.engine.util.JRLoader;
import net.sf.jasperreports.export.SimpleExporterInput;
import net.sf.jasperreports.export.SimpleOutputStreamExporterOutput;
import net.sf.jasperreports.export.SimplePdfExporterConfiguration;
import org.springframework.core.io.ClassPathResource;import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;public class PrintUtil {public static PdfInfoDto printPDF(String modelName, HashMap hashMap) throws IOException, JRException {// 获取模板路径ClassPathResource classPathResource = new ClassPathResource("/jasper/"+modelName+".jasper");// 获取模板输入流InputStream inputStream = classPathResource.getInputStream();// 读取模板JasperReport report = (JasperReport) JRLoader.loadObject(inputStream);//数据库数据填充报表JasperPrint jprint = JasperFillManager.fillReport(report, hashMap,new JREmptyDataSource());DefaultJasperReportsContext defaultJasperReportsContext=DefaultJasperReportsContext.getInstance();
//        defaultJasperReportsContext.setProperty("net.sf.jasperreports.fonts.STSong-Light", "path/to/STSong-Light.ttf");//创建导出对象JRPdfExporter exporter = new JRPdfExporter(defaultJasperReportsContext);//设置要导出的流exporter.setExporterInput(new SimpleExporterInput(jprint));ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();exporter.setExporterOutput(new SimpleOutputStreamExporterOutput(byteArrayOutputStream));SimplePdfExporterConfiguration configuration = new SimplePdfExporterConfiguration();configuration.setCreatingBatchModeBookmarks(true);exporter.setConfiguration(configuration);// 导出pdfexporter.exportReport();byte[] bytes=byteArrayOutputStream.toByteArray();byteArrayOutputStream.close();PdfInfoDto pdfInfo=new PdfInfoDto();pdfInfo.setPdfBytes(bytes);pdfInfo.setHeight(jprint.getPageHeight());pdfInfo.setWidth(jprint.getPageWidth());return pdfInfo;}}

pdf转图片

用到的实体类

package com.xueyi.common.core.utils.print.dto;import lombok.Data;@Data
public class ImageInfoDto {private Integer height;private Integer width;private byte[] imageBytes;
}
package com.xueyi.common.core.utils.print;import com.xueyi.common.core.utils.print.dto.ImageInfoDto;
import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.PDFRenderer;import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;public class Pdf2PngUtil {public static List<ImageInfoDto> pdf2png(byte[] bytes) throws IOException {ArrayList<ImageInfoDto> list=new ArrayList();PDDocument doc=Loader.loadPDF(bytes);PDFRenderer renderer = new PDFRenderer(doc);int pageCount = doc.getNumberOfPages();for (int i = 0; i < pageCount; i++) {// dpi为144,越高越清晰,转换越慢BufferedImage image = renderer.renderImageWithDPI(i, 144); // Windows native DPIByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();ImageIO.createImageOutputStream(byteArrayOutputStream);ImageIO.write(image, "png", byteArrayOutputStream);ImageInfoDto imageInfo=new ImageInfoDto();imageInfo.setHeight(image.getHeight());imageInfo.setWidth(image.getWidth());imageInfo.setImageBytes(byteArrayOutputStream.toByteArray());list.add(imageInfo);byteArrayOutputStream.close();}doc.close();return  list;}
}

使用(用的打印模版是带参数的 ,参数是aaa)

返回类

@Data
public class PrintDto {@Serialprivate static final long serialVersionUID = 1L;private String jasperName;private HashMap hashMap;private List<ImageInfoDto> imageInfoDtoList;private PdfInfoDto pdfInfoDto;
}

使用

    @PostMapping("/print")public AjaxResult print(@RequestBody TransSchedulerQuery transSchedulerQuery) throws IOException, JRException {PrintDto printDto=new PrintDto();printDto.setJasperName(transSchedulerQuery.getJasperName());HashMap hashMap=new HashMap();hashMap.put("aaa","你看得到吗?");printDto.setPdfInfoDto(PrintUtil.printPDF(transSchedulerQuery.getJasperName(),hashMap));printDto.setImageInfoDtoList(Pdf2PngUtil.pdf2png(printDto.getPdfInfoDto().getPdfBytes()));return  AjaxResult.success(printDto);}

总结

后端的思路其实很简单,就是用带参数的打印模版,然后把对应参数送进去生成pdf,由于前端需要图片和pdf两种,所以又把生成的pdf转图片生成了一下图片的list.然后不管事pdf和图片,都是直接把文件本身传递回去了,没有存到本地,用url的方法.直接把文件的byte数组传递到前端了(一般序列化的方式就是base64,所以json序列化的时候自动转好了的前端接到的是字符串形式的)

前端

前端其实很简单,就是把得到的文件信息拿到,转成blob形式,然后预览一下.唯一的问题就是拿到的字符串需要进行转码,从base64变回byte数组.因为要有预览的效果,所以一直尝试前端pdf转换然后要自定义打印啥的(Lodop),用了很多无用的代码,后续定下来打印就调用浏览器的打印啥的,那个打印按钮有打印前和打印后的回调函数的,已经满足需求了.

export function base64ToByteArray(base64String: string): Uint8Array {const binaryString = atob(base64String);const length = binaryString.length;const bytes = new Uint8Array(length);for (let i = 0; i < length; i++) {bytes[i] = binaryString.charCodeAt(i);}return bytes;
}

页面

<template><BasicModal  v-bind="$attrs" :width="ModalWidthEnum.COMMON" @register="registerModal" :canFullscreen="false" :title="variable.title" :height="400"   :okText="t('public.button.print')"   @ok="handleSubmit" ><div style="margin-top: 5px;text-align: center;"><span> {{t("public.describe.all")}}{{variable.imagesUrlList.length}}{{t("public.describe.page")}}{{t("public.describe.clickToTurnOver")}}</span></div><iframe ref="pdfFrameRef" :src="variable.pdfUrl" style="display: none"></iframe><Row ref="rowRef">
<!--      <Col :span="1" >-->
<!--        <div style="height: 100%;">-->
<!--          <CaretLeftOutlined style="position: absolute;top: 45%"/>-->
<!--        </div>-->
<!--      </Col>--><Col :span="24"><div style="border: black 1px solid;" ><Image :src="variable.imagesUrlList[0]" width="100%" :preview="variable.preview" @click="variable.preview = true"/><div style="display: none"><Image.PreviewGroup :preview="{ visible:variable.preview, onVisibleChange: vis => (variable.preview = vis) }"><Image v-for="(item) in variable.imagesUrlList"  :src="item" width="100%" /></Image.PreviewGroup></div></div></Col>
<!--      <Col :span="1" >-->
<!--        <div style="height: 100%;">-->
<!--          <CaretRightOutlined style="position: absolute;top: 45%"/>-->
<!--        </div>--><!--      </Col>--></Row></BasicModal>
</template><script setup lang="ts">
import {reactive, ref} from 'vue';
import {useMessage} from '@/hooks/web/useMessage';
import {BasicModal, useModalInner} from '@/components/Modal';
import {useI18n} from "@/hooks/web/useI18n";
import {ModalWidthEnum} from "@/enums";
import {Image,Row,Col,} from 'ant-design-vue';
// 定义国际化
const { t } = useI18n();
const emit = defineEmits(['success']);
const {createMessage} = useMessage();
const isUpdate = ref(true);
import {base64ToByteArray} from "@/utils/commonFunctions/commonFunctions";
/** 标题初始化 */
const variable = reactive<any>({ids: [],fatherParam:{},title:"",pdfUrl:null,pdfBlob:null,imagesUrlList:[],currentIndex:0,currentHeight:0,preview:false,afterPrintFunction:null
});const pdfFrameRef=ref()
const rowRef=ref()const [registerModal, {setModalProps, closeModal,changeOkLoading,changeLoading}] = useModalInner(async (data) => {variable.fatherParam=JSON.parse(JSON.stringify(data.fatherParam))variable.afterPrintFunction=data.afterPrintFunctionvariable.title=data.fatherParam.titlevariable.imagesUrlList=[]variable.currentIndex=0variable.preview=false//有图片的情况if (data.fatherParam.data.imageInfoDtoList?.length>0){variable.currentHeight=variable.fatherParam.data.imageInfoDtoList[0].heightvariable.pdfBlob = new Blob([base64ToByteArray(variable.fatherParam.data?.pdfInfoDto?.pdfBytes)], { type: 'application/pdf'});variable.pdfUrl= window.URL.createObjectURL(variable.pdfBlob)variable.fatherParam.data.imageInfoDtoList.forEach(item=>{const blob1 = new Blob([base64ToByteArray(item.imageBytes)], { type: 'image/png'});variable.imagesUrlList.push(window.URL.createObjectURL(blob1))})}});// const getPrintDevice = () => {
//   var loop = getLodop(); // 创建一个LODOP对象
//   let counter = loop.GET_PRINTER_COUNT(); // 获取打印机个数
//   //初始化下printNameList打印数组
//   variable.printDeviceList = [];
//   for (let i = 0; i < counter; i++) {
//     //将打印机存入printList数组中
//     variable.printDeviceList.push({name:loop.GET_PRINTER_NAME(i),value:i});
//   }
//
//   //获取默认打印机并设置
//   var defaultName =loop.GET_PRINTER_NAME(-1);
//   variable.printDeviceList.forEach(item=>{
//     if(item.name==defaultName){
//       variable.printDevice=item.value
//     }
//   })
// }// const doPrint = () => {
//   var LODOP = getLodop(); // 创建一个LODOP对象
//   // LODOP.ADD_PRINT_IMAGE(0, 0, "<img>", "EMF", variable.pdfUrl);
//   LODOP.PRINT_INIT("打印控件功能演示_Lodop功能_按网址打印");
//   let base64="data:image/png;base64,"+variable.fatherParam.data.imageListByte[0]
//   LODOP.ADD_PRINT_IMAGE(0,0,"100%","100%",base64);
//   console.log(base64)
//   LODOP.PRINT();
// }// // 创建 LODOP 实例
// function getLodop() {
//   let LODOP;
//   if (window.LODOP) {
//     LODOP = window.LODOP;
//   } else if (window.parent.LODOP) {
//     LODOP = window.parent.LODOP;
//   } else {
//     LODOP = (function () {
//       var LODOP = null;
//       var s = document.createElement('script');
//       s.src = '/LODOPfuncs.js';
//       s.id = 'LODOPfuncs';
//       s.type = 'text/javascript';
//       document.getElementsByTagName('head')[0].appendChild(s);
//       document.onpropertychange = function (e) {
//         if (e.propertyName === 'LODOP') {
//           LODOP = e.srcElement.LODOP;
//         }
//       };
//       return LODOP;
//     })();
//   }
//   return LODOP;
// }/** 提交按钮 */
async function handleSubmit() {pdfFrameRef.value.contentWindow.print();window.onafterprint = afterPrint;
}/*** 浏览器打印的回调*/
const afterPrint = async () =>  {variable.afterPrintFunction()
}</script>

效果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/56485.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LCD补充

LCD补充 目录 LCD补充 tip:随着我们学的越来越多&#xff0c;代码长度越来越长&#xff0c;编译越来越慢&#xff0c;有没有超过内存是我们比较关心的一件事&#xff0c;通过以下方法可以实时看到写的代码的大小 回顾LCD LCD补充功能 -- 1、有关在LCD上显示动图&#xff…

前端使用Canvas实现网页电子签名(撤销、下载)

前言&#xff1a;一般在一些后台的流程资料以及审核的场景中会需要电子签名&#xff0c;介绍一种用canvas实现的电子签名&#xff0c;此案例用的是原生js 效果展示&#xff1a; 一、html和css&#xff1a; <div class"divCla2"><canvas id"myCanvas&q…

数据结构-排序算法

基于交换的排序算法 快速排序&#xff1a; 最优情况 最优情况下&#xff0c;每次找到的参考轴把数据分成均匀的两半&#xff0c;最后应该是一个平衡二叉树状态&#xff1b;二叉树的层数&#xff08;logn&#xff09;即为递归需要进行的次数&#xff0c;并且每轮递归结束时&…

Java语言-抽象类

目录 1.抽象类概念 2.抽象类语法 3.抽象类特性 4.抽象类作用 1.抽象类概念 在面向对象的概念中&#xff0c;所有的对象都是通过类来描绘的&#xff0c;但是反过来&#xff0c;并不是所有的类都是用来描绘对象的&#xff0c; 如果 一个类中没有包含足够的信息来描绘一个具体…

115.WEB渗透测试-信息收集-ARL(6)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;114.WEB渗透测试-信息收集-ARL&#xff08;5&#xff09; httpd就是apache环境&#xff0…

跨平台音摄像头|屏幕推送选OBS还是SmartPublisher?

好多开发者希望搞明白OBS和 SmartPublisher的区别和使用场景差别&#xff0c;本文就二者差别做个对比&#xff1a; OBS OBS&#xff08;Open Broadcaster Software&#xff09;是一款免费且开源的跨平台流媒体和视频录制软件。以下是关于它的详细介绍&#xff1a; 功能特点&a…

音乐播放器项目专栏介绍​

1.简介 本专栏使用Qt QWidget作为显示界面&#xff0c;你将会学习到以下内容&#xff1a; 1.大量ui美化的实例。 2.各种复杂ui布局。 3.常见显示效果实现。 4.大量QSS实例。 5.Qt音频播放&#xff0c;音乐歌词文件加载&#xff0c;展示。 6.播放器界面换肤。 相信学习了本专栏…

Oracle Expdp按条件导出-指定表数据

1.场景描述 业务需求&#xff1a;导出A机构、2024的数据&#xff0c;以dmp格式&#xff0c;保留导出日志。首先&#xff0c;需要分析库中需要导出的表清单、表的机构字段约束、表的时间约束&#xff1b;然后再导出。 2.方案分析 本次采用Oracle的expdp数据泵方式导出&#xf…

基于Docker的FRP内网穿透部署

服务器搭建&#xff08;FRPS&#xff09; 创建配置文件 # 创建存放目录 sudo mkdir /etc/frp # 创建frps.ini文件 nano /etc/frp/frps.ini frps.ini内容如下&#xff1a; [common] # 监听端口 bind_port 7000 # 面板端口 dashboard_port 7500 # 登录面板账号设置 dashboa…

《数字信号处理》学习09-部分分式展开法计算z 逆变换

在之前的文章中&#xff0c;我已经学习了使用留数法&#xff08;围线积分法&#xff09;来计算z逆变换 《数字信号处理》学习08-围线积分法&#xff08;留数法&#xff09;计算z 逆变换-CSDN博客 接着学习第二种计算z变换的方法&#xff1a;部分分式展开法。 目录 一&…

决策智能与强化学习:重放比率(replay ratio)

知乎&#xff1a;DILab决策实验室&#xff08;已授权&#xff09;链接&#xff1a;https://zhuanlan.zhihu.com/p/898641863 0. 概览 近年来&#xff0c;深度强化学习&#xff08;Deep Reinforcement Learning, DRL&#xff09;在诸多领域取得了显著的成果。然而&#xff0c;随…

域环境模拟实验搭建

1. 域环境搭建 总体来说下一步下一步即可 域&#xff1a;统一的管理计算机的集群&#xff0c;中心管理机器&#xff08;域控制器 DC&#xff09;管理整个内网&#xff08;域内成员机器&#xff09; 条件&#xff1a;1. 需要一台域控制器 -- windows server版本&#xff08;w…

高级英语1第四版教材全解pdf课后答案+课文翻译张汉熙

《高级英语1》是张汉熙教授编著的一本英语教材&#xff0c;广泛用于国内高校英语专业高年级学生的教学。这本书以提高学生的英语综合能力为目标&#xff0c;注重语言知识的系统性和实用性&#xff0c;同时强调跨文化交际能力的培养。书中选材丰富&#xff0c;涵盖了文学、历史、…

Cloudlog delete_oqrs_line 未授权SQL注入漏洞复现

0x01 产品简介 Cloudlog 是一个自托管的 PHP 应用程序,可让您在任何地方记录您的业余无线电联系人。使用PHP和MySQL构建的基于Web的业余无线电记录应用程序支持从HF到微波的一般站记录任务 0x02 漏洞概述 Cloudlog delete_oqrs_line 接口存在未授权SQL注入漏洞,未经身份验…

UE5学习笔记24-添加武器弹药

一、给角色的武器添加弹药 1.创建界面&#xff0c;根据笔记23的界面中添加 2.绑定界面控件 UPROPERTY(meta (Bindwidget))UTextBlock* WeaponAmmoAmount;UPROPERTY(meta (Bindwidget))UTextBlock* CarriedAmmoAmount; 3.添加武器类型枚举 3.1创建武器类型枚举头文件 3.2创建文…

Java 小游戏《超级马里奥》

文章目录 一、效果展示二、代码编写1. 素材准备2. 创建窗口类3. 创建常量类4. 创建动作类5. 创建关卡类6. 创建障碍物类7. 创建马里奥类8. 编写程序入口 一、效果展示 二、代码编写 1. 素材准备 首先创建一个基本的 java 项目&#xff0c;并将本游戏需要用到的图片素材 image…

PPT怎么转成PDF?5个超简单转换方法,快来学(包靠谱)

PPT怎么转成PDF&#xff1f;PPT&#xff08;PowerPoint演示文稿&#xff09;是一种重要的文件格式&#xff0c;通过它&#xff0c;我们能够直观地呈现信息、分享观点以及进行商务演示。然而在有些时候&#xff0c;我们需要将PPT文件转化为PDF格式&#xff0c;以便于分享、打印或…

基于PHP考研互助系统【附源码】

基于PHP考研互助系统 效果如下&#xff1a; 系统首页界面 用户注册界面 考研论坛页面 每日打卡页面 管理员登录主页面 管理员主界面 用户管理界面 备考经验界面 研究背景 近些年&#xff0c;随着中国经济发展&#xff0c;人民的生活质量逐渐提高&#xff0c;对网络的依赖性越…

软件Bug和缺陷的区别是什么?

前言 在任何软件生命周期中&#xff0c;软件缺陷的出现几乎是不可避免的。建立一套有效的缺陷管理流程的目的是为了减少软件缺陷出现的几率&#xff0c;并且大幅度降低由于软件缺陷带来的负面影响。对于缺陷管理流程的投资&#xff0c;可以大幅度的降低由于返工/修复缺陷导致的…

模版和STL

个人主页&#xff1a;Jason_from_China-CSDN博客 所属栏目&#xff1a;C系统性学习_Jason_from_China的博客-CSDN博客 所属栏目&#xff1a;C知识点的补充_Jason_from_China的博客-CSDN博客 前言 string是标准库中的一个类&#xff0c;它位于<string>头文件中。 这个类提…