vue项目页面的打印和下载PDF(加水印)
vue项目页面的打印
打印的不用说,调用 window.print() 的方法即可; 注意点:如果用到背景图的话,需要CSS中添加设置;
div {
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
color-adjust: exact;
}
vue项目页面下载PDF
封装代码如下:
新建utils/pdf.js文件;
import html2canvas from "html2canvas"
import jsPDF from "jspdf"
export const downloadPDF = (el, title) => {
html2canvas(el, {
allowTaint: true,
useCORS: true,
dpi: 120,
background: '#FFFFFF',
}).then(canvas => {
let leftHeight = canvas.height
let a4Width = 595.28
let a4Height = 841.89
let a4HeightRef = Math.floor((canvas.width / a4Width) * a4Height)
let position = 0
let pageData = canvas.toDataURL('image/jpeg', 1.0)
let pdf = new jsPDF('p', 'pt', 'a4')
let createCanvas = document.createElement('canvas')
let height
pdf.setDisplayMode('fullwidth', 'continuous', 'FullScreen')
let pdfName = title || "个人简历"
function createImpl(canvas) {
console.log(leftHeight, a4HeightRef)
if (leftHeight > 0) {
let checkCount = 0
if (leftHeight > a4HeightRef) {
let i = position + a4HeightRef
for (i = position + a4HeightRef; i >= position; i--) {
let isWrite = true
for (let j = 0; j < canvas.width; j++) {
let c = canvas.getContext('2d').getImageData(j, i, 1, 1).data
if (c[0] != 0xff || c[1] != 0xff || c[2] != 0xff) {
isWrite = false
break
}
}
if (isWrite) {
checkCount++
if (checkCount >= 10) {
break
}
} else {
checkCount = 0
}
}
height = Math.round(i - position) || Math.min(leftHeight, a4HeightRef)
if (height <= 0) {
height = a4HeightRef
}
} else {
height = leftHeight
}
createCanvas.width = canvas.width
createCanvas.height = height
console.log('height:', height, 'pos', position)
let ctx = createCanvas.getContext('2d')
ctx.drawImage(
canvas,
0,
position,
canvas.width,
height,
0,
0,
canvas.width,
height,
)
if (position != 0) {
pdf.addPage()
}
pdf.addImage(
createCanvas.toDataURL('image/jpeg', 1.0),
'JPEG',
10,
20,
a4Width -30,
(a4Width / createCanvas.width) * height -30,
)
leftHeight -= height
position += height
if (leftHeight > 0) {
setTimeout(createImpl, 100, canvas)
} else {
pdf.save(pdfName + '.pdf')
}
}
}
if (leftHeight < a4HeightRef) {
pdf.addImage(
pageData,
'JPEG',
10,
20,
a4Width - 30,
(a4Width / canvas.width) * leftHeight -30,
)
pdf.save(pdfName + '.pdf')
} else {
try {
pdf.deletePage(0)
setTimeout(createImpl, 100, canvas)
} catch (err) {
console.log(err)
}
}
})
}
export const previewWater = (strName, strLink) => {
const can = document.createElement('canvas')
can.width = 500
can.height = 750
const cans = can.getContext('2d')
cans.rotate((-45 * Math.PI) / 180)
cans.translate(0, 0)
const txtLen = strName.length
const fontSize = txtLen > 12 ? '30px Simsun' : '40px Simsun'
cans.font = fontSize
cans.fillStyle = 'rgba(0, 0, 0, 0.3)'
cans.textAlign = 'center'
cans.textBaseline = 'Middle'
cans.fillText(strName, -txtLen * 12, 400)
cans.fillText(strLink, -txtLen * 12, 440)
cans.save()
return can.toDataURL()
}
注意要提前下载 jspdf 和 html2canvas 这两个包,项目中要用到;
项目里面是用另一种方式是把js包下载到本地,动态新建script标签引入,注意这样的方式可以在window的环境下调用方法,要用 window.html2canvas 或者 window.jspdf 等等;
handleCreateScript() {
let html2Canvas = document.createElement('script')
html2Canvas.src = `${process.env.VUE_LOCAL_PATH}js/html2canvas.min.js`
document.body.appendChild(html2Canvas)
let jspdf = document.createElement('script')
jspdf.src = `${process.env.VUE_LOCAL_PATH}js/jspdf.umd.min.js`
document.body.appendChild(jspdf)
}
created(){
this.handleCreateScript();
}
注意说一下水印,因为需求要求水印必须放到页面的上面,而不是页面的下方,只能另辟蹊径了,我用的方法是直接canvas的创建生成方式,用个div定位到页面上面,动态获取内容的高度,然后给这个div加高度、加背景图的方式添加页面水印;
效果如下:
具体代码如下:
<template>
<div class="preview-wrapper">
<div class="preview-btn">
<div class="content">
<div class="download-btn">
<div><span @click="handlePrint">打印</span></div>
<div><span @click="handleExport">下载</span></div>
</div>
</div>
</div>
<div class="user-info" ref="pdfWrapper">
<!-- 内容自己定义 -->
...
....
.....
......
.......
<!-- 水印要放到页面上面 用个div 定位到页面上方-->
<div
class="water-wrapper"
:style="{ backgroundImage: `url(${orgBackground})` }"
></div>
</div>
</div>
</template>
<script>
import { downloadPDF, previewWater } from '@/utils/pdf.js'
export default {
name: 'userinfo',
components: {
},
props: {},
data() {
return {
orgBackground: '',
}
},
computed: {},
created() {},
mounted() {
this.orgBackground = previewWater('XXXX网站', 'http://xxxxxxx.xxx.xx')
let userInfoWrapper = document.querySelector('.user-info')
console.log(userInfoWrapper, userInfoWrapper.offsetHeight)
let waterWrapper = document.querySelector('.water-wrapper')
waterWrapper.style.cssText = `height: ${userInfoWrapper.offsetHeight}px`
},
methods: {
handlePrint() {
window.print()
},
handleExport() {
downloadPDF(this.$refs.pdfWrapper, this.pfdName)
}
}
}
</script>
<style scoped lang="scss">
div {
-webkit-print-color-adjust: 'exact';
print-color-adjust: 'exact';
color-adjust: 'exact';
}
.preview-wrapper {
z-index: 999;
}
.user-info {
width: 1000px;
background: #fff;
position: relative;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: 0 auto;
padding: 0 50px 100px;
z-index: 1;
}
.water-wrapper {
position: absolute;
top: 0;
left: 0;
width: 1000px;
z-index: 999;
}
</style>
注意:之前写页面考虑不周到,发现对接接口后,添加水印的高度可能会有问题,尝试发现是父元素高度问题,因为我子元素都是组件的形式,请求数据前高度和请求后高度可能存在偏差和不一样,考虑到vue渲染的问题,导致水印获取父元素的高度不对,大家在对接接口后尝试会发现这个问题,现在改下mounted里面的调用,需要在获取数据后去获取父元素的高度,具体看如下代码;
<template>
<div class="preview-wrapper">
<div class="preview-btn">
<div class="content">
<div class="download-btn">
<div><span @click="handlePrint">打印</span></div>
<div><span @click="handleExport">下载</span></div>
</div>
</div>
</div>
<div class="user-info" ref="pdfWrapper">
<!-- 内容自己定义 -->
...
....
.....
......
.......
<!-- 水印要放到页面上面 用个div 定位到页面上方-->
<div
class="water-wrapper"
:style="{ backgroundImage: `url(${orgBackground})` }"
></div>
</div>
</div>
</template>
<script>
import { downloadPDF, previewWater } from '@/utils/pdf.js'
import { dataInfo } from '@/api/userinfo.js'
export default {
name: 'userinfo',
components: {},
props: {},
data() {
return {
orgBackground: '',
dataInfo: {}
}
},
computed: {},
watch: {
dataInfo(){
this.$nextTick(() => {
this.orgBackground = previewWater('XXXX网站', 'http://xxxxxxx.xxx.xx')
let userInfoWrapper = document.querySelector('.user-info')
console.log(userInfoWrapper, userInfoWrapper.offsetHeight)
let waterWrapper = document.querySelector('.water-wrapper')
waterWrapper.style.cssText = `height: ${userInfoWrapper.offsetHeight}px;`
})
}
},
created() {},
mounted() {},
methods: {
handlePrint() {
window.print()
},
handleExport() {
downloadPDF(this.$refs.pdfWrapper, this.pfdName)
},
handleDatainfo(){
dataInfo().then((res) => {
this.dataInfo = res.data
}).catch((error) => {
console.log(error)
})
}
}
}
</script>
<style scoped lang="scss">
div {
-webkit-print-color-adjust: 'exact';
print-color-adjust: 'exact';
color-adjust: 'exact';
}
.preview-wrapper {
z-index: 999;
}
.user-info {
width: 1000px;
background: #fff;
position: relative;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: 0 auto;
padding: 0 50px 100px;
z-index: 1;
}
.water-wrapper {
position: absolute;
top: 0;
left: 0;
width: 1000px;
z-index: 999;
}
</style>
友情提示:对于处理这部分类型的功能,要考虑的地方要注意,比如获取页面后的高度处理,图片的高度问题等等,希望程序猿们遇到问题解决问题!
封装异步PDF下载函数并添加loading效果
因产品需求的需要,当用户下载PDF时发现可能需要等待一段时间,所以加个loading效果;
export const downloadPDFTwo = (el, title) => {
return new Promise(async (resolve, reject) => {
try {
html2canvas(el, {
allowTaint: true,
useCORS: true,
dpi: 120,
background: '#FFFFFF',
}).then(canvas => {
let leftHeight = canvas.height
console.log(leftHeight)
let a4Width = 595.28
let a4Height = 841.89
let a4HeightRef = Math.floor((canvas.width / a4Width) * a4Height)
let position = 0
let pageData = canvas.toDataURL('image/jpeg', 1.0)
let pdf = new jsPDF('p', 'pt', 'a4')
let createCanvas = document.createElement('canvas')
let height
pdf.setDisplayMode('fullwidth', 'continuous', 'FullScreen')
let pdfName = title || "个人简历"
function createImpl(canvas) {
console.log(leftHeight, a4HeightRef)
if (leftHeight > 0) {
let checkCount = 0
if (leftHeight > a4HeightRef) {
let i = position + a4HeightRef
for (i = position + a4HeightRef; i >= position; i--) {
let isWrite = true
for (let j = 0; j < canvas.width; j++) {
let c = canvas.getContext('2d').getImageData(j, i, 1, 1).data
if (c[0] != 0xff || c[1] != 0xff || c[2] != 0xff) {
isWrite = false
break
}
}
if (isWrite) {
checkCount++
if (checkCount >= 10) {
break
}
} else {
checkCount = 0
}
}
height = Math.round(i - position) || Math.min(leftHeight, a4HeightRef)
if (height <= 0) {
height = a4HeightRef
}
} else {
height = leftHeight
}
createCanvas.width = canvas.width
createCanvas.height = height
console.log('height:', height, 'pos', position)
let ctx = createCanvas.getContext('2d')
ctx.drawImage(
canvas,
0,
position,
canvas.width,
height,
0,
0,
canvas.width,
height,
)
if (position != 0) {
pdf.addPage()
}
pdf.addImage(
createCanvas.toDataURL('image/jpeg', 1.0),
'JPEG',
10,
20,
a4Width - 30,
(a4Width / createCanvas.width) * height - 30,
)
leftHeight -= height
position += height
if (leftHeight > 0) {
setTimeout(createImpl, 100, canvas)
} else {
pdf.save(pdfName + '.pdf')
pdf.successFlag = true
resolve(pdf)
}
}
}
if (leftHeight < a4HeightRef) {
pdf.addImage(
pageData,
'JPEG',
10,
20,
a4Width - 30,
(a4Width / canvas.width) * leftHeight - 30,
)
pdf.save(pdfName + '.pdf')
pdf.successFlag = true
resolve(pdf)
} else {
try {
pdf.deletePage(0)
setTimeout(createImpl, 100, canvas)
} catch (err) {
console.log(err)
reject(err)
}
}
})
} catch (error) {
reject(error)
}
})
}
页面里面使用时添加loading效果即可
async handleExport() {
try {
this.fullscreenLoading = true
let pdf = await downloadPDFTwo(this.$refs.pdfWrapper, this.pfdName)
console.log(pdf, pdf.successFlag)
if (pdf.successFlag) {
this.fullscreenLoading = false
} else {
this.fullscreenLoading = false
console.log('下载打印失败,请重新尝试');
}
} catch (error) {
console.log(error);
}
},
页面代码如下:
<template>
<div class="preview-wrapper">
<div class="preview-btn">
<div class="content">
<div class="download-btn">
<div><span @click="handlePrint">打印</span></div>
<div><span @click="handleExport" v-loading.fullscreen.lock="fullscreenLoading">下载</span></div>
</div>
</div>
</div>
<div class="user-info" ref="pdfWrapper">
<!-- 内容自己定义 -->
...
....
.....
......
.......
<!-- 水印要放到页面上面 用个div 定位到页面上方-->
<div
class="water-wrapper"
:style="{ backgroundImage: `url(${orgBackground})` }"
></div>
</div>
</div>
</template>
<script>
import { downloadPDF, previewWater } from '@/utils/pdf.js'
import { dataInfo } from '@/api/userinfo.js'
export default {
name: 'userinfo',
components: {},
props: {},
data() {
return {
orgBackground: '',
dataInfo: {},
fullscreenLoading: false,
}
},
computed: {},
watch: {
dataInfo(){
this.$nextTick(() => {
this.orgBackground = previewWater('XXXX网站', 'http://xxxxxxx.xxx.xx')
let userInfoWrapper = document.querySelector('.user-info')
console.log(userInfoWrapper, userInfoWrapper.offsetHeight)
let waterWrapper = document.querySelector('.water-wrapper')
waterWrapper.style.cssText = `height: ${userInfoWrapper.offsetHeight}px;`
})
}
},
created() {},
mounted() {},
methods: {
handlePrint() {
window.print()
},
async handleExport() {
try {
this.fullscreenLoading = true
let pdf = await downloadPDFTwo(this.$refs.pdfWrapper, this.pfdName)
console.log(pdf, pdf.successFlag)
if (pdf.successFlag) {
this.fullscreenLoading = false
} else {
this.fullscreenLoading = false
console.log('下载打印失败,请重新尝试');
}
} catch (error) {
console.log(error);
}
},
handleDatainfo(){
dataInfo().then((res) => {
this.dataInfo = res.data
}).catch((error) => {
console.log(error)
})
}
}
}
</script>
<style scoped lang="scss">
div {
-webkit-print-color-adjust: 'exact';
print-color-adjust: 'exact';
color-adjust: 'exact';
}
.preview-wrapper {
z-index: 999;
}
.user-info {
width: 1000px;
background: #fff;
position: relative;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: 0 auto;
padding: 0 50px 100px;
z-index: 1;
}
.water-wrapper {
position: absolute;
top: 0;
left: 0;
width: 1000px;
z-index: 999;
}
</style>
|