1.首先在文件内引入htmlToPdf.js
这里代码引入了html2canvas和jspdf
//需要 npm i html2Canvas 和 npm i jspdf
在这里将getPdf 这个函数挂载到Vue的原型上,最后return一个promise对象(包含了resolve的base64Pdf,以便于处理),在局部组件内可进行.then以进行上传后端等操作。
插件代码如下
import html2Canvas from 'html2canvas'
import JsPDF from 'jspdf'
export default {
install(Vue, options) {
/*** * @param {*} reportName 下载时候的标题* @param {*} isDownload 是否下载默认为下载,传false不下载*/Vue.prototype.getPdf = function (reportName, isDownload = true) {
// var target = document.getElementsByClassName("right-aside")[0];// target.style.background = "#FFFFFF";return new Promise((resolve, reject) => {
var title = reportName;html2Canvas(document.querySelector('#pdfDom'), {
allowTaint: true}).then((canvas) => {
let contentWidth = canvas.widthlet contentHeight = canvas.height//一页pdf显示html页面生成的canvas高度;let pageHeight = contentWidth / 592.28 * 841.89//未生成pdf的html页面高度let leftHeight = contentHeight//页面偏移let position = 0//a4纸的尺寸[595.28,841.89],html页面生成的canvas在pdf中图片的宽高let imgWidth = 595.28let imgHeight = 592.28 / contentWidth * contentHeightlet pageData = canvas.toDataURL('image/jpeg', 1.0)let PDF = new JsPDF('', 'pt', 'a4')//有两个高度需要区分,一个是html页面的实际高度,和生成pdf的页面高度(841.89)//当内容未超过pdf一页显示的范围,无需分页if (leftHeight < pageHeight) {
PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight)} else {
while (leftHeight > 0) {
PDF.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight)leftHeight -= pageHeightposition -= 841.89//避免添加空白页if (leftHeight > 0) {
PDF.addPage()}}}if (isDownload) {
PDF.save(title + '.pdf')}// 删除本地存储的base64字段var pdfData = PDF.output('datauristring')//获取base64Pdfresolve(pdfData)})})}}
}
接下在main.js直接引入刚刚的代码文件
import htmlToPdf from '@/utils/htmlToPdf'
在局部组件时,准备下载时
<button @click="toGetPdf(0)">下载PDF</button>//这种情况是只下载,不上传后端<button @click="toGetPdf(1, 0)">下载PDF</button>//这种情况是只走上传后端接口,不下载
toGetPdf(val = false, download = true) {
/*** val 决定走不走上传接口,默认为不上传给后端* download 默认是下载* //* */this.$nextTick(() => {
setTimeout(() => {
window.scrollTo(0, 0); //这行代码很重要,它让页面的滚动条跳到了最上方如果点击打印按钮的时候,滚动条没有在最上方,打印内容会是不完整的,体验也会差let title ="个人报告"this.getPdf(title, download) //download:false为不下载,这里调用了刚刚引用的全局函数,.then得到的值是base64位的pdf文件.then((res) => {
if (val) {
console.log("准备上传");this.UploadPdf(res);} else {
console.log("不上传");}});}, 1000);});},
下两个函数是上传文件的接口和base64转文件流的函数
由于是pdf的base64位至少需要1M,传给后端有些大,所以前端转成文件流formData形式传给后端
//上传pdf接口UploadPdf(res) {
//res拿到base64的pdflet pdfBase64Str = res;let title ="上传给后端的个人报告"var myfile = this.dataURLtoFile(pdfBase64Str, title + ".pdf");//调用一下下面的转文件流函数var formdata = new FormData();formdata.append("file", myfile); // 文件对象//该uploadMy为接口,直接以formdata格式传给后台uploadMy(formdata).then((res) => {
console.log("上传pdf接口", res);}).catch((err) => {
console.log("上传pdf接口", err);});},
/* 将base64转换为文件,接收2个参数,第一是base64,第二个是文件名字 最后返回文件对象 */dataURLtoFile(dataurl, filename) {
var arr = dataurl.split(","),mime = arr[0].match(/:(.*?);/)[1],bstr = atob(arr[1]),n = bstr.length,u8arr = new Uint8Array(n);while (n--) {
u8arr[n] = bstr.charCodeAt(n);}return new File([u8arr], filename, {
type: mime });},
需要注意的是 上传文件的请求头为 ‘Content-Type’: ‘multipart/form-data’,post请求。
下面代码网络请求接口文件部分代码 为个人记录,不适用于所有人项目,且为部分代码。
通常做pdf下载有上面代码就足够了,下面可忽略
//api/index.js文件:import $request from '@/utils/http'
let baseUrl = '/api/test'if (process.env.NODE_ENV === 'development') {
baseUrl = '/dev'bigDataUrl = '/service'collectUrl = '/collect'
}
// 上传文件-个人
export function uploadMy(data) {
return $request.postUpload(baseUrl + '/common/upload', data)
}//@/utils/http.js文件
import axios from './request'
/** post请求 lcl编写 请求头为上传的请求头*/
function postUpload(url, data,config) {
return new Promise((resolve, reject) => {
axios.post(url, data, config?config:{
headers: {
// 'Content-Type': 'application/x-www-form-urlencoded''Content-Type': 'multipart/form-data'}}).then((res) => {
// if (res.data.code === '801' || res.data.code === '802' || res.data.code === '804') {
// removeToken()// router.push({ name: 'login' })// }resolve(res.data)}).catch((err) => {
reject(err)})})
}//@/utils/request.js文件import axios from 'axios'
// import { Message, MessageBox } from 'element-ui'
import store from '@/store'
import {
getToken, removeToken} from '@/utils/auth'// create an axios instanceconst service = axios.create({
// baseURL: process.env.VUE_APP_APIURL, // baseURL: "/api/test", timeout: 20000 // request timeout 超过20s则失败
})// request interceptor
service.interceptors.request.use(config => {
// Do something before request is sent// 让每个请求携带token-- ['Token']为自定义key 请根据实际情况自行修改if(store.getters.token) {
config.headers['Authorization'] = store.getters.token}return config},error => {
Promise.reject(error)}
)// response interceptor
service.interceptors.response.use(// response => response,/*** 下面的注释为通过在response里,自定义code来标示请求状态* 当code返回如下情况则说明权限有问题,登出并返回到登录页* 如想通过 xmlhttprequest 来状态码标识 逻辑可写在下面error中* 以下代码均为样例,请结合自生需求加以修改,若不需要,则可删除*/response => {
const res = response.dataif (res.code !== 200 && res.code !== 204) {
// Message({
// message: res.msg,// type: 'error',// duration: 5 * 1000// })if (res.code === 401 || res.code === 501 || res.code === 804) {
// 请自行在引入 MessageBox// import { Message, MessageBox } from 'element-ui'// MessageBox.confirm('你已被登出,可以取消继续留在该页面,或者重新登录', '确定登出', {
// confirmButtonText: '重新登录',// cancelButtonText: '取消',// type: 'warning'// }).then(() => {
removeToken();location.reload();store.dispatch('FedLogOut').then(() => {
location.reload() // 为了重新实例化vue-router对象 避免bug});// })}return Promise.reject(res)} else {
return response}},error => {
// Message({
// message: error.msg,// type: 'error',// duration: 5 * 1000// })return Promise.reject(error)}
)export default service
以上便是利用JsPDF和html2canvas先获取屏幕快照,生成pdf并以文件流形式上传到后端,默认生成的pdf为A4纸大小。