使用html2pdf实现前端页面打印,批量打印导出为PDF

javascript 复制代码
// 使用注意:
// 如果页面中有图片会因为跨域问题导致图片下载不下来
// 所以在图片标签中需要加入一些属性:crossOrigin 和 url后加一个随机后缀
// <Image crossOrigin="anonymous" src={`${faceInfo.user_face_url}?v=${new Date()}`} width={200} alt="" />
const { jsPDF } = window.jspdf
/**
 *
 * @param {*} id 要下载部分页面的元素id
 * @param {*} htmlTitle 下载文件文件名称
 */
export default function (id, htmlTitle = '标题') {
  let element = document.body;
  if (id) {
    element = document.getElementById(id);
  }
  return html2canvas(element, {
    logging: false,
    useCORS: true,
  }).then(function (canvas) {
    var pdf = new jsPDF('p', 'mm', 'a4'); // A4纸,纵向
    var ctx = canvas.getContext('2d');
    var a4w = 190;
    var a4h = 257; // A4大小,210mm x 297mm,四边各保留20mm的边距
    var imgHeight = Math.floor((a4h * canvas.width) / a4w); // 按A4显示比例换算一页图像的像素高度
    var renderedHeight = 0;

    while (renderedHeight < canvas.height) {
      var page = document.createElement('canvas');
      page.width = canvas.width;
      page.height = Math.min(imgHeight, canvas.height - renderedHeight); // 可能内容不足一页

      // 用getImageData剪裁指定区域,并画到前面创建的canvas对象中
      page
        .getContext('2d')
        .putImageData(
          ctx.getImageData(
            0,
            renderedHeight,
            canvas.width,
            Math.min(imgHeight, canvas.height - renderedHeight),
          ),
          0,
          0,
        );
      pdf.addImage(
        page.toDataURL('image/jpeg', 1.0),
        'JPEG',
        10,
        10,
        a4w,
        Math.min(a4h, (a4w * page.height) / page.width),
      ); // 添加图像到页面,保留10mm边距

      renderedHeight += imgHeight;
      if (renderedHeight < canvas.height) {
        pdf.addPage();
      } // 如果后面还有内容,添加一个空页
      // delete page;
    }
    // pdf.save(htmlTitle);
    var pdfBase64Str = pdf.output('datauristring'); //获取base64Pdf
    var myfile = dataURLtoFile(pdfBase64Str, htmlTitle + '.pdf'); //调用一下下面的转文件流函数
    return myfile;
  });
}

/*
将base64转换为文件,接收2个参数,第一是base64,第二个是文件名字
最后返回文件对象
*/
const 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 });
};
javascript 复制代码
// 使用
import React, { useState, useEffect, useRef } from 'react';
import { Space, Button, Modal, Spin } from 'antd';
import { httpAdminTyreHandoverPhotoDetail } from '@/api'
import html2pdf from '@/utils/html2pdf';
import JSZip from 'jszip'
import { saveAs } from "file-saver";
import './style.less'
import ReactToPrint from 'react-to-print';
import { mmToPixels } from "@/utils/tools";

const Index = ({ selectedIds, setShow }) => {
  const [data, setData] = useState({})

  const ref = useRef();

  const [pageLoading, set_pageLoading] = useState(false)
  useEffect(async () => {

    try{
      set_pageLoading(true)
      const list = await Promise.all(
        selectedIds.map(async id => {
          const res = await httpAdminTyreHandoverPhotoDetail({ id });
          return res.data;
        })
      )
      setData(list)
      set_pageLoading(false)
    } catch(e) {
      console.error(e)
    }
  }, [selectedIds])

  const [loading, setLoading] = useState(false)

  const butns = [
      <Button 
        disabled={pageLoading}
        loading={loading} 
        type="primary"
        onClick={async () => {
          setLoading(true)
          var zip = new JSZip();
          await Promise.all(
            data.map(async (item, index) => {
              const pdfFile = await html2pdf(`batch-export-box${index}`);
              zip.file(`文件:${item?.order_num}.pdf`, pdfFile);
            })
          )
          zip.generateAsync({ type: "blob" })
            .then(function (content) {
              saveAs(content, "文件.zip");
            });
          setLoading(false)   
        }}
      >
        导出PDF文件
      </Button>,
      <ReactToPrint
        trigger={() => <Button type="primary" disabled={pageLoading}>打印</Button>}
        content={() => ref.current}
      />,
  ]


  // A4宽度转像素
  const widthPixels = mmToPixels(210); // 大约1485像素
  // A4高度转像素
  const heightPixels = mmToPixels(297); // 大约2105像素

  return ( 
    <Modal
      open={true}
      width='1200px'
      onCancel={() => setShow(false)}
      footer={
        [
          ...butns,
          <Button onClick={() => setShow(false)}>取消</Button>
        ]
      }
      title={<Space>
        <span>批量打印</span>
        {butns[0]}
        {butns[1]}
      </Space>}
    >
      <Spin spinning={pageLoading}>
      <div ref={(e) => ref.current = e}>
        <div style={{ width: '100%' }}>
          {
            data?.length ? data.map((item, index) => {
              return <div
                id={`batch-export-box${index}`}
                style={{ padding: '12px', pageBreakAfter: 'always', width:`${widthPixels}px` }}
              >
                <Content info={item} />
              </div>
            }) : ''
          }
        </div>
      </div>
      </Spin>
    </Modal>
  )
}

export default Index;

const Content = ({info}) => {
  return <div className='PDFDescriptions' style={{width: '100%'}} direction="vertical">
    内容
</div>
}
相关推荐
m0_7482402530 分钟前
前端如何检测用户登录状态是否过期
前端
black^sugar31 分钟前
纯前端实现更新检测
开发语言·前端·javascript
寻找沙漠的人1 小时前
前端知识补充—CSS
前端·css
GISer_Jing1 小时前
2025前端面试热门题目——计算机网络篇
前端·计算机网络·面试
m0_748245521 小时前
吉利前端、AI面试
前端·面试·职场和发展
理想不理想v2 小时前
webpack最基础的配置
前端·webpack·node.js
pubuzhixing2 小时前
开源白板新方案:Plait 同时支持 Angular 和 React 啦!
前端·开源·github
2401_857600952 小时前
SSM 与 Vue 共筑电脑测评系统:精准洞察电脑世界
前端·javascript·vue.js
2401_857600952 小时前
数字时代的医疗挂号变革:SSM+Vue 系统设计与实现之道
前端·javascript·vue.js
GDAL2 小时前
vue入门教程:组件透传 Attributes
前端·javascript·vue.js