DEV Community

Cover image for uniapp 入门实战 19:将前端页面导出成pdf
FreeCoderX
FreeCoderX

Posted on

1

uniapp 入门实战 19:将前端页面导出成pdf


背景

产品要求公司的小程序和网站需要将商品详情导出成pdf,所以今天有琢磨一下如何将前端页面导出成pdf

实现效果

导出效果.gif

Web网站

提醒:以下代码图片地址,请自行修改一下。

  • demo: 整体来说很简单,就是借助html2canvas将dom转成图片,然后jsPDF添加图片,然后保存即可
  <!DOCTYPE html>
  <html lang="en">

  <head>
   <meta charset="UTF-8" />
   <title>title</title>
   <style>
    .btn {
     width: 300px;
     height: 100px;
    }

    img {
     width: 100%;
     object-fit: cover;
    }
   </style>
   <script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"></script>
   <script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
  </head>

  <body>
   <button type="button"
       class="btn btn-primary"
       onclick="exportPdf()">导出PDF</button>
   <div id="app">
    <h1>自然石纹的纯粹之美</h1>
    <img src="https://xxx/img-0-0.png?x-oss-process=image/resize,m_fill,h_1133,w_750,limit_0,q_80"
       alt="">
    <div>
     5A全抛釉 打造高性价比空间
    </div>
   </div>

   <script type="module">
    import jsPDF from 'https://cdn.jsdelivr.net/npm/jspdf@2.5.2/+esm';

    function exportPdf() {
     let shareContent = document.getElementById('app');
     let width = shareContent.offsetWidth;
     let height = shareContent.offsetHeight;
     html2canvas(shareContent, {
      dpi: 900,
      scrolly: 0,
      // width:eleW,//生成后的宽度
      // height:eleH,//生成后的高度
      scrollx: -10,
      useCORS: true, //允许canvas画布内可以跨域请求外部链接图片, 允许跨域请求。
      // backgroundColor: null //避免图片有白色边框
     }).then((canvas) => {
      let context = canvas.getContext('2d');
      context.mozImageSmoothingEnabled = false;
      context.webkitImageSmoothingEnabled = false;
      context.msImageSmoothingEnabled = false;
      context.imageSmoothingEnabled = false;
      let pageData = canvas.toDataURL('image/jpeg', 1.0);
      let img = new Image();
      img.src = pageData;
      img.onload = () => {
       img.width = img.width;
       img.height = img.height;
       console.log(img.width, '------ img.width');
       console.log(img.height, '------img.height');
       let pdf = null;
       if (width > height) {
        // 此可以根据打印的大小进行自动调节
        // eslint-disable-next-line
        pdf = new jsPDF('l', 'mm', [width, height]);
       } else {
        // eslint-disable-next-line
        pdf = new jsPDF('p', 'mm', [width, height]);
       }
       pdf.addImage(pageData, 'jpeg', 0, 0, width, height);
       pdf.save('安全服务协议' + '.pdf');  //h5在这就可以保存pdf
      };
     }).catch((r) => {
      console.log(r);
     })
    }

    window.exportPdf = exportPdf;
   </script>
  </body>

  </html>

Enter fullscreen mode Exit fullscreen mode

微信小程序

说明:公司小程序项目是用的uniapp开发的

方法1:通过wx.miniProgram.postMessage将pdf数据传给小程序

提醒:以下代码图片地址,请自行修改一下。

  • h5 webview页面
  <!DOCTYPE html>
  <html lang="en">

  <head>
   <meta charset="UTF-8" />
   <title>title</title>
   <style>
    .btn {
     width: 300px;
     height: 100px;
    }

    img {
     width: 100%;
     object-fit: cover;
    }
   </style>
   <script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js"></script>
   <script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
  </head>

  <body>
   <button type="button"
       class="btn btn-primary"
       onclick="exportPdf()">导出PDF</button>
   <div id="app">
    <h1>自然石纹的纯粹之美</h1>
    <img src="https://xxx/img-0-0.png?x-oss-process=image/resize,m_fill,h_1133,w_750,limit_0,q_80"
       alt="">
    <div>
     5A全抛釉 打造高性价比空间
    </div>
   </div>

   <script type="module">
    import jsPDF from 'https://cdn.jsdelivr.net/npm/jspdf@2.5.2/+esm';

    function exportPdf() {
     let shareContent = document.getElementById('app');
     let width = shareContent.offsetWidth;
     let height = shareContent.offsetHeight;
     html2canvas(shareContent, {
      dpi: 900,
      scrolly: 0,
      // width:eleW,//生成后的宽度
      // height:eleH,//生成后的高度
      scrollx: -10,
      useCORS: true, //允许canvas画布内可以跨域请求外部链接图片, 允许跨域请求。
      // backgroundColor: null //避免图片有白色边框
     }).then((canvas) => {
      let context = canvas.getContext('2d');
      context.mozImageSmoothingEnabled = false;
      context.webkitImageSmoothingEnabled = false;
      context.msImageSmoothingEnabled = false;
      context.imageSmoothingEnabled = false;
      let pageData = canvas.toDataURL('image/jpeg', 1.0);
      let img = new Image();
      img.src = pageData;
      img.onload = () => {
       img.width = img.width;
       img.height = img.height;
       console.log(img.width, '------ img.width');
       console.log(img.height, '------img.height');
       let pdf = null;
       if (width > height) {
        // 此可以根据打印的大小进行自动调节
        // eslint-disable-next-line
        pdf = new jsPDF('l', 'mm', [width, height]);
       } else {
        // eslint-disable-next-line
        pdf = new jsPDF('p', 'mm', [width, height]);
       }
       pdf.addImage(pageData, 'jpeg', 0, 0, width, height);
       pdf.save('安全服务协议' + '.pdf');  //h5在这就可以保存pdf

       wx.miniProgram.getEnv(function (res) {
        console.log("当前环境:" + JSON.stringify(res));
        if (res.miniprogram) {
         wx.miniProgram.postMessage({
          data: {
           imageData: pdf.output("datauristring"),
          }
         });
         wx.miniProgram.navigateBack({ delta: 1 })
        }
       });
      };
     }).catch((r) => {
      console.log(r);
     })
    }

    window.exportPdf = exportPdf;
   </script>
  </body>

  </html>

Enter fullscreen mode Exit fullscreen mode
  • uniapp 页面代码

特别提醒:请将页面部署至自己的服务器下,然后修改一下地址,然后在小程序后台把部署的域名配置到web合法域名列表下,不然webview无法加载页面

  <template>
   <PageLayout>
    <web-view src="https://xxx.com/pdf_test.html"
         @message="handleGetMessage"></web-view>
   </PageLayout>
  </template>

  <script>
  export default {
   data() {
    return {
     imageData: "",
    }
   },
   methods: {
    handleGetMessage(e) {
     console.log("收到webview消息:", e)
     this.imageData = e.detail.data[0].imageData
     console.log("收到webview消息: imageData=", this.imageData);
     const base64 = this.imageData.split("base64,")[1]
     console.log("收到webview消息: path=", base64);
     this.download(base64)
    },
    async download(base64) {
     base64 = base64.replace(/[\r\n]/g, "");
     const fs = wx.getFileSystemManager();
     const buffer = wx.base64ToArrayBuffer(base64);
     const filePath = wx.env.USER_DATA_PATH + "/" + Date.now() + ".pdf"
     fs.writeFile({
      filePath,
      data: buffer,
      success(res) {
       uni.openDocument({
        showMenu: true,
        fileType: "pdf",
        filePath,
        success: function (res) {
         console.log("打开文档成功")
        }
       })
      },
      fail(err) {
       console.log("错误", err)
      }
     })
    },
   },
   onLoad(e) {

   }
  }

  </script>

  <style scoped></style>

Enter fullscreen mode Exit fullscreen mode

方法2:通过后端中转数据,然后通过导航传参(最后的备用方案)

补充说明:如果你碰到h5通过wx.miniProgram.postMessage发送消息,小程序死活的搜索不到消息,返回、分享都没用。解决办法:可以通过将数据保存至后端,然后再通过导航将参数传给小程序,然后再下载pdf。具体代码就罗列了,简单说下思路

  1. 将h5导出的pdf传给后端,然后返回一个id
  2. 通过wx.miniProgram.navigateTo({url: ‘/pages/xx/xx?id=234’})
  3. 然后xx页面加载的onLoad回调中从option拿到参数后,反查后端拿pdf

总结

  • 部署webview页面时,需要小程序后台配置合法域名,否则部分正常加载webview的页面
  • web页面中用到的图片,相应的域名也要在小程序后台进行配置,配置到request合法域名列表中,不然图片可能加载不出来
  • 小程序2个坑位,千万要注意,别掉坑里了。
    1. uniapp webview 监听postmessage的函数是message,微信小程序里面监听的方法是bindmessage(注意:千万别搞错了)
    2. postMessage发送后不是立即响应的,而是只在特点时机触发的 (后退、组件销毁、分享触发并收到消息

参考文献

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Immerse yourself in a wealth of knowledge with this piece, supported by the inclusive DEV Community—every developer, no matter where they are in their journey, is invited to contribute to our collective wisdom.

A simple “thank you” goes a long way—express your gratitude below in the comments!

Gathering insights enriches our journey on DEV and fortifies our community ties. Did you find this article valuable? Taking a moment to thank the author can have a significant impact.

Okay