插件:
-
jspdf
-
html2canvas
-
jszip
-
file-saver
实现:
- 先写好html模版
- 捕捉dom通过html2canvas生成图片
- 通过jspdf生成pdf
- pdf先不实例化,等所有pdf生成完之后
util.ts
import jsPDF from 'jspdf'
import html2canvas from 'html2canvas'
export const printPDF = async (domClassName: string) => {
const printables = Array.from(document.getElementsByClassName(domClassName)) as HTMLElement[]
if (!printables.length) {
throw `找不到 dom[class=${domClassName}]`
}
let p: jsPDF
const printableCanvases = await Promise.all(
printables.map((printable) => html2canvas(printable, { allowTaint: false, useCORS: true })),
)
printableCanvases.map((canvas, index) => {
if (!p) {
p = new jsPDF('L', 'pt', [canvas.width, canvas.height])
}
if (index !== 0) {
p.addPage([canvas.width, canvas.height])
}
p.addImage(canvas.toDataURL(), 'JPEG', 0, 0, canvas.width, canvas.height, undefined, 'FAST')
})
return p
}
index.ts生成一个pdf
import React, { useEffect } from 'react';
import { printPDF } from './util';
const Detail = () => {
useEffect(()=>{
printPDF('print').then(PDF=>{
PDF.save(`print.pdf`)
})
},[])
return (
<div className="print" id="print">
...
...
dom
</div>
)
}
多个pdf生成一个zip
执行步骤:
- 获取list
- 把每一条list渲染到dom上并生成pdf
- 把每一个pdf保存
- 所有pdf生成完之后生成zip
import React, { useEffect, useState } from 'react'
import { printPDF } from './util'
import JSZip from 'jszip'
import FileSaver from 'file-saver'
import moment from 'moment'
const Data = [{ name: '小红' }, { name: '小华' }, { name: '小明' }, { name: '小芳' }]
const Detail = () => {
const [info, setInfo] = useState({})
const [domData, setDomData] = useState([])
const [zipData, setZipData] = useState<any>([])
const [count, setCount] = useState(0)
// pageInit模拟请求
const pageInit = async () => {
const fn = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(Data || [])
}, 1000)
})
}
const res = await fn()
setDomData(res)
}
// 第一步初始化
useEffect(() => {
pageInit()
}, [])
// 第三步 更新新的Info重新执行第二步
useEffect(() => {
if (domData.length) {
setInfo(domData[count])
}
}, [domData])
// 第二步生成pdf
useEffect(() => {
if (info.name && count === zipData.length && count < domData.length) {
setTimeout(() => {
try {
printPDF('print')
.then((pdf: any) => {
setZipData([
...zipData,
{
PDF: pdf,
name: `${info.name}`,
},
])
setCount(count + 1)
})
.catch((e) => console.log(e))
} catch (error) {
console.log(JSON.stringify(error))
}
}, 6)
}
}, [info])
useEffect(() => {
if (domData.length && count < domData.length) {
setInfo(domData[count])
}
}, [count])
// 第四步确保所有的数据都变成pdf之后最后执行导出
useEffect(() => {
if (zipData.length && zipData.length === domData.length) {
const fn = async () => {
const zip = new JSZip()
Promise.all(zipData)
.then(async (pdfs: any) => {
for (let i = 0; i < pdfs.length; i++) {
const { PDF, name } = pdfs[i]
// 如果只是导出一个pdf,则导出pdf格式
if (pdfs.length === 1) {
PDF.save(`${name}.pdf`)
} else {
// 否则添加到压缩包里面
await zip.file(`${name}.pdf`, PDF.output('blob'))
}
}
if (pdfs.length > 1) {
zip.generateAsync({ type: 'blob' }).then((content) => {
console.log(content)
FileSaver.saveAs(content, `${moment().format('YYYY-MM-DD HH:mm')}`)
})
}
})
.finally(() => {
console.log('exportzipend')
})
}
fn()
}
}, [zipData])
return (
<div className="print" id="print">
<div>...</div>
... ... 名字:{info.name}
</div>
)
}
export default Detail
效果: