jsPDF和html2canvas生成pdf,组件用的elementplus,亲测30多页,20s实现

发布于:2025-07-04 ⋅ 阅读:(14) ⋅ 点赞:(0)

在这个直接图片转pdf躺过的坑

  1. 利用html2canvas超过8到10页就不能用了,所以如果你的数据确保不会出现8页以上,简单快捷使用它;简要代码详见代码区块1;
  2. 我的需求是超过10页,那么就只能用pdf了,详见简要代码区块2;
  3. 2者结合的示例,详见代码区块3;
  4. 针对el-table没有标题的处理,详见组件区块代码4
  5. 亲测30多页,20s差不多就可以打包出来。

代码区块1

const element = printContent.value;
        if (!element) {
            console.error('printContent 元素未找到');
            return;
        }
        const opt = {
            margin: 10,
            filename: `${printTitle.value}_${dayjs().format('YYYYMMDD')}.pdf`,
            image: { type: 'jpeg', quality: 0.95 },  // 适当降低质量减小文件大小
            html2canvas: {
                scale: 2,
                useCORS: true,       // 解决跨域图片问题
                allowTaint: true     // 允许渲染外部图片
            }
        }

        await html2pdf().set(opt).from(element).save();

代码区块2

// 创建新的PDF实例
const opt = {
            margin: 10,
            filename: `${printTitle.value}_${dayjs().format('YYYYMMDD')}.pdf`,
            image: { type: 'jpeg', quality: 0.95 },  // 适当降低质量减小文件大小
            html2canvas: {
                scale: 2,
                useCORS: true,       // 解决跨域图片问题
                allowTaint: true     // 允许渲染外部图片
            },
            jsPDF: {
                unit: 'mm',
                format: 'a4',
                orientation: 'landscape',
                hotfixes: ["px_scaling"]  // 解决某些缩放问题
            },
            pagebreak: {
                mode: ['css', 'avoid-all'],  // 优先使用CSS分页控制
                before: '.page-break'        // 手动分页标记类名
            }
        }

        const pdf = new jsPDF(
            {
                orientation: 'landscape',
                unit: 'mm',
                format: 'a4',
                margin: 10 ,
            }
        )
         // 验证纸张的尺寸 并指定内容的宽度,经测试单位应该px,并width使用10%失效;所以本人在这里通过我的内容区域合计约等于1200px
        const pdfSize ={
            width: pdf.internal.pageSize.getWidth(),
            height: pdf.internal.pageSize.getHeight(),
            contentWidth: 1200, //定义内容宽度
        }
// 单页用法及本例的实际应用:
//封面
		/**这里的元素选择器可以是id或者class,由于这里示例单页所有用的id*/
        const coverPage = element.querySelector('#coverPage');
        console.log('封面元素:', coverPage);  // 应该打印出 DOM 元素
        if (!coverPage) {
            console.error('未找到封面元素 #coverPage');
            return;
        }
        // 1.添加封面 及项目信息使用html2canvas
        // 分阶段提示
        ElMessage.info('开始生成封面...');
        /**创建画布*/
        const CoverCanvas = await html2canvas(coverPage, {
                scale: opt.html2canvas.scale,
                useCORS: true,
                allowTaint: true,
                width: pdfSize.contentWidth,
                height:pdfSize.height * pdfSize.contentWidth/pdfSize.width,


            },
        );
        /**生成图片信息*/
        const coverimgData = CoverCanvas.toDataURL('image/jpeg', opt.image.quality);
       /**图片数据写入画布*/
        pdf.addImage(
            coverimgData,
            'JPEG',
            opt.margin,
            opt.margin,
            imgWidth,
            imgHeight
        );
        //保存 可以每个部分分段测试
        pdf.save(opt.filename);




 // 针对多页的列表数据处理
//3.录入企业列表
        pdf.addPage(); //用于将后续的图片数据加到pdf对象,组件代码 可以参考区块代码4
        ElMessage.info('正在处理待抽取企业列表...');
        const inputCompages = element.querySelectorAll('.inputPdf-table');
        for (let i = 0; i < inputCompages.length; i++) {
            const inputCompage = inputCompages[i];

            // 使用html2canvas单独渲染每一页
            const inputComcanvas = await html2canvas(inputCompage, {
                    scale: opt.html2canvas.scale,
                    useCORS: true,
                    allowTaint: true,
                    width: pdfSize.contentWidth,
                    height:pdfSize.height * pdfSize.contentWidth/pdfSize.width,


                },
            );
            const inputComimgData = inputComcanvas.toDataURL('image/jpeg', opt.image.quality);

            // 添加新页(第一页不需要添加)
            if (i > 0) {
                pdf.addPage();
            }
            pdf.addImage(
                inputComimgData,
                'JPEG',
                opt.margin,
                opt.margin,
                imgWidth,
                imgHeight
            );
        }
//保存 可以每个部分分段测试
        pdf.save(opt.filename);

代码区块3

const exportToPDF = async () => {
    try {
        const element = printContent.value;
        if (!element) {
            console.error('printContent 元素未找到');
            return;
        }
        // 生成进度提示开始
        const loading = ElLoading.service({
            lock: true,
            text: 'PDF生成中,请稍候...',
            background: 'rgba(0, 0, 0, 0.7)'
        });
        // 生成进度提示条结束
        // const opt = {
        //     margin: 10,
        //     filename: `${printTitle.value}_${dayjs().format('YYYYMMDD')}.pdf`,
        //     image: { type: 'jpeg', quality: 0.98 },
        //     html2canvas: { scale: 2 },
        //     jsPDF: { unit: 'mm', format: 'a4', orientation: 'landscape' },
        //     pagebreak: { mode: ['avoid-all', 'css', 'legacy'] }
        // }
        const opt = {
            margin: 10,
            filename: `${printTitle.value}_${dayjs().format('YYYYMMDD')}.pdf`,
            image: { type: 'jpeg', quality: 0.95 },  // 适当降低质量减小文件大小
            html2canvas: {
                scale: 2,
                useCORS: true,       // 解决跨域图片问题
                allowTaint: true     // 允许渲染外部图片
            },
            jsPDF: {
                unit: 'mm',
                format: 'a4',
                orientation: 'landscape',
                hotfixes: ["px_scaling"]  // 解决某些缩放问题
            },
            pagebreak: {
                mode: ['css', 'avoid-all'],  // 优先使用CSS分页控制
                before: '.page-break'        // 手动分页标记类名
            }
        }

        // 创建新的PDF实例
        const pdf = new jsPDF(
            {
                orientation: 'landscape',
                unit: 'mm',
                format: 'a4',
                margin: 10 ,
            }
        )
        const pdfSize ={
            width: pdf.internal.pageSize.getWidth(),
            height: pdf.internal.pageSize.getHeight(),
            contentWidth: 1200, //定义内容宽度
        }
        const imgWidth = pdfSize.width;  // A4横向宽度 - 边距
        const imgHeight = pdfSize.height;
        //封面
        const coverPage = element.querySelector('#coverPage');
        console.log('封面元素:', coverPage);  // 应该打印出 DOM 元素
        if (!coverPage) {
            console.error('未找到封面元素 #coverPage');
            return;
        }
        // 1.添加封面 及项目信息使用html2canvas
        // 分阶段提示
        ElMessage.info('开始生成封面...');
        const CoverCanvas = await html2canvas(coverPage, {
                scale: opt.html2canvas.scale,
                useCORS: true,
                allowTaint: true,
                width: pdfSize.contentWidth,
                height:pdfSize.height * pdfSize.contentWidth/pdfSize.width,


            },
        );
        const coverimgData = CoverCanvas.toDataURL('image/jpeg', opt.image.quality);
        pdf.addImage(
            coverimgData,
            'JPEG',
            opt.margin,
            opt.margin,
            imgWidth,
            imgHeight
        );
        //2.项目信息
        pdf.addPage();
        ElMessage.info('正在处理项目信息...');
        const projectPage = element.querySelector('#projectPage');
        // console.log('封面元素:', projectPage);  // 应该打印出 DOM 元素
        if (!projectPage) {
            console.error('未找到封面元素 #projectPage');
            return;
        }
        const projectCanvas = await html2canvas(projectPage, {
                scale: opt.html2canvas.scale,
                useCORS: true,
                allowTaint: true,
                width: pdfSize.contentWidth,
                height:pdfSize.height * pdfSize.contentWidth/pdfSize.width,


            },
        );
        const projectimgData = projectCanvas.toDataURL('image/jpeg', opt.image.quality);
        pdf.addImage(
            projectimgData,
            'JPEG',
            opt.margin,
            opt.margin,
            imgWidth,
            imgHeight
        );
        //3.录入企业列表
        pdf.addPage();
        ElMessage.info('正在处理待抽取企业列表...');
        const inputCompages = element.querySelectorAll('.inputPdf-table');
        for (let i = 0; i < inputCompages.length; i++) {
            const inputCompage = inputCompages[i];

            // 使用html2canvas单独渲染每一页
            const inputComcanvas = await html2canvas(inputCompage, {
                    scale: opt.html2canvas.scale,
                    useCORS: true,
                    allowTaint: true,
                    width: pdfSize.contentWidth,
                    height:pdfSize.height * pdfSize.contentWidth/pdfSize.width,


                },
            );
            const inputComimgData = inputComcanvas.toDataURL('image/jpeg', opt.image.quality);

            // 添加新页(第一页不需要添加)
            if (i > 0) {
                pdf.addPage();
            }
            pdf.addImage(
                inputComimgData,
                'JPEG',
                opt.margin,
                opt.margin,
                imgWidth,
                imgHeight
            );
        }
        // const inputCompanyListPage = element.querySelector('#inputCompanyListPage');
        // // console.log('封面元素:', projectPage);  // 应该打印出 DOM 元素
        // if (!inputCompanyListPage) {
        //     console.error('未找到封面元素 #inputCompanyListPage');
        //     return;
        // }
        // const inputCompanyListCanvas = await html2canvas(inputCompanyListPage, {
        //         scale: opt.html2canvas.scale,
        //         useCORS: true,
        //         allowTaint: true,
        //         width: pdfSize.contentWidth,
        //         height:pdfSize.height * pdfSize.contentWidth/pdfSize.width,
        //
        //
        //     },
        // );
        // const inputCompanyListimgData = inputCompanyListCanvas.toDataURL('image/jpeg', opt.image.quality);
        // pdf.addImage(
        //     inputCompanyListimgData,
        //     'JPEG',
        //     opt.margin,
        //     opt.margin,
        //     imgWidth,
        //     imgHeight
        // );
        // pdf.save(opt.filename);
        // return
        pdf.addPage();
        ElMessage.info('正在处理打印记录...');
        const pages = element.querySelectorAll('.pdf-table');

        if (pages.length <5) {
            console.log('导出小PDF');
            await html2pdf().set(opt).from(element).save();
            return;
        }
        // 4.打印记录
        for (let i = 0; i < pages.length; i++) {
            const page = pages[i];

            // 使用html2canvas单独渲染每一页
            const canvas = await html2canvas(page, {
                scale: opt.html2canvas.scale,
                useCORS: true,
                allowTaint: true,
                width: pdfSize.contentWidth,
                height:pdfSize.height * pdfSize.contentWidth/pdfSize.width,
                },
            );

            const imgData = canvas.toDataURL('image/jpeg', opt.image.quality);

            // 添加新页(第一页不需要添加)
            if (i > 0) {
                pdf.addPage();
            }


            pdf.addImage(
                imgData,
                'JPEG',
                opt.margin,
                opt.margin,
                imgWidth,
                imgHeight
            );
        }
        // 保存PDF
        pdf.save(opt.filename);
        ElMessage.success('PDF导出完成!');
        console.log('PDF导出成功');
        loading.close()
    } catch (err) {
        console.error('PDF导出失败:', err);
        // 可以添加用户提示,如Element UI的Message
        ElMessage.error('PDF导出失败: ' + err.message);
    }
}

区块代码4

<div class="print-content" v-if="!showHitOnly" id="inputCompanyListPage">
                    <div v-for="(pageData, index) in inputCompanyListdData" :key="index" class="print-page">
                        <div  class="inputPdf-table">
                            <div class="print-header">
                                <h2>{{ inputProjectPrintTitle }}</h2>
                            </div>
                            <el-table
                                :data="pageData"
                                border
                                style="width: 100%"
                                :row-class-name="tableRowClassName"
                                :title="inputProjectPrintTitle"
                            >

                                <el-table-column
                                    v-if="printSort"
                                    type="index"
                                    label="序号"
                                    width="60"
                                    :index="inputCompanyListindexMethod" />
                                <el-table-column prop="companyName" label="企业名称" width="300" />
                                <el-table-column prop="creditCode" label="社会统一信用代码号" width="180" />
                                <el-table-column prop="numberplate" label="号码牌" width="80" />
                                <el-table-column prop="agentName" label="联系人" width="160" />
                                <el-table-column prop="agentContactMethod" label="联系电话" width="auto"/>
                            </el-table>
                        </div>
                    </div>
                </div>

网站公告

今日签到

点亮在社区的每一天
去签到