前言
纯前端(基于vue2)实现对上传的pdf,进行盖章或签名后,并将处理后的pdf下载下来
目标

1 实现pdf上传与预览
2 实现手动签名,签名与印章的拖拽移动
3 将盖章或签名的文件进行绘制与下载

实现效果展示
【vue2】实现上传pdf文件盖章与签名,盖章文件下载_盖章

所用的第三方依赖

  1. pdfh5 实现pdf文件的在线预览
  2. vue-esign 在画布上面签名,生成签名图片
  3. html2canvas html绘制图片
  4. jspdf 生成pdf、下载pdf

大致的逻辑
盖章与签名以图片的形式在pdf预览上进行拖拽,拖拽到pdf上
将拖拽的图片dragImg放到pdfViewer下,循环pdfViewer下的元素如果有dragImg

文件目录
【vue2】实现上传pdf文件盖章与签名,盖章文件下载_签名_02

DragImg组件:主要是实现图片的拖拽效果
inedx主文件:文件的上传预览等
SignImg组件:签名组件

接下来,一步一步去实现我们所需的效果

1 pdf文件上传

el-upload实现上传并监听before-upload事件,在before-upload事件中限制文件大小、格式、类型等,若文件符合使用FileReader()方法将文件转化为Base64,url接收Base64数据(本文用的是Base64实现文预览效果)。
若你想pdf5中使用地址方式,可以在上传成功之后将文件下载到项目的对应位置,并将地址信息保存在url属性中。

2 pdf在线预览

安装

npm i pdfh5 -S

引入

import Pdfh5 from “pdfh5”
import “pdfh5/css/pdfh5.css”

使用

// 将pdf展示在id为preViewPdf的标签中
// pdfurl可以是文件地址,也可以是Base64(本文使用Base64)
this.pdfh5 = new Pdfh5('#preViewPdf', {
        pdfurl: url, // '../../static/test.pdf'文件地址 或 Base64
        maxZoom: 1, // 手势缩放最大倍数
        // lazy:true,
        // scrollEnable:true
})
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.

其它可参考pdf5说明文档<https://www.npmjs.com/package/pdfh5>

3 手动签名

将签名封装到SignImg组件中
安装

npm i vue-esign -S

引入

import vueEsign from 'vue-esign'
components: {vueEsign}
  • 1.
  • 2.

使用vue-esign生成签名照
使用了<vueEsign></<vueEsign>标签来调用 vue-esign 插件,并通过设置 widthheight 属性来自定义签字区域的大小。isCrop设置是否裁剪,lineWidth 画笔粗细,lineColor 画笔颜色。

<vueEsign ref="esign" style="width: 100%!important;height:83vh !important;margin-left: -0.3rem;" :isCrop="isCrop" :lineWidth="lineWidth" :lineColor="lineColor" :bgColor.sync="bgColor" >
</vueEsign>
  • 1.
  • 2.

签名完成通过handleGenerate方法将签名转化为图片

    <el-button type="primary" @click="handleGenerate">确定签名</el-button>
    //生成签名图片..
    handleGenerate() {
      this.$refs.esign.generate().then(res => {
        // res是一个图片的Base64格式
        this.handleReset(); //清空画布
        /**
           业务逻辑
        **
      }).catch(err => {
        console.log(err)
        alert("请签名之后提交");
      })
    }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.

最终效果
【vue2】实现上传pdf文件盖章与签名,盖章文件下载_签名_03

上传印章盖章以及签名照片的对象都是图片,通过拖拽图片从而实现pdf的签名与盖章效果。
以下就不再对以上操作进行一一的详细展示,仅对说明如何实现图片拖拽效果

4 拖拽效果

封装一个DragImg拖拽组件,将签名图片以及电子印章图片都放在这个组件中,从而实现签名与印章在pdf中的拖拽效果。
自定义拖拽指令drag
父元素绑定自定义指令,子元素展示图片
组件中的元素使用的是相对于父元素的绝对定位

 <div v-drag="{ that }" onclick="" style="width: 100%;height: 100%;position: absolute;">
     <img :class="'imgType_' + imgType" style="width: 100%;" :src="imgSrc" />
 </div>
  props: {
    imgData: {
      type: String,
      default: ''
    },
    // ...一些其他辅助参数或方法
    // 禁止拖拽方法
    pauseHandle:{
      type: Function,
      value: null,
    }
  },
 directives: {
    // 自定义指令
    drag: {
      bind: function (el, binding) {
        let oDiv = el;   //当前元素
        let that = binding.value.that;
        oDiv.onmousedown = function (e) {
          e.preventDefault();
          // 下载过程中禁止拖拽
          const isPause = that.pauseHandle() || false
          if(isPause) return
          let bw = document.body.clientWidth;
          let bh = document.body.clientHeight;
          //鼠标按下,计算当前元素距离可视区的距离
          let disX = e.clientX - oDiv.offsetLeft;
          let disY = e.clientY - oDiv.offsetTop;
          // 计算两边坐标
          document.onmousemove = function (e) {
            let l = 0, t = 0;
            // 拖动边界
            if (e.clientX >= bw) {
              l = bw - disX;
            } else if (e.clientX <= 0) {
              {
                l = 0 - disX;
              }
            } else {
              l = e.clientX - disX;
            }
            if (e.clientY >= bh) {
              t = bh - disY;
            } else if (e.clientY <= 0) {
              t = 0 - disY;
            } else {
              t = e.clientY - disY;
            }
            //移动当前元素
            oDiv.style.left = l + 'px';
            oDiv.style.top = t + 'px';
          };
          // 鼠标停止移动时,事件移除
          document.onmouseup = function (e) {
            document.onmousemove = null;
            document.onmouseup = null;
          };
        };
      }
    }
  },
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.

5 pdf下载

会用到的第三放插件是JsPDFhtml2canvas
在下载之前,先大致讲一下盖章与签字到pdf上的处理逻辑
1 将dragImg放到.pdfViewer元素下,通过循环查询.pdfViewer下的元素当className == 'dragImg'
根据dragImg.style.top - pdfViewer.style.top,计算图片应插入第几页,修改dragImg绝对定位后将dragImg插入相对应的.pageContainer元素下。签名或者印章在对应的页面中。
2 html2canvas将每一页绘制成图片,通过addPage创建空白页与addImage添加图片,实现pdf的生成。
3 PDF.save()下载生成的pdf。
先说明一下 一会儿会用到的几个元素的id

.pdfViewer 存放整个pdf
.pageContainer pdf中的一页(多页就多个兄弟pageContainer)
.dragImg 是你拖拽的签名或者印章图

创建印章并拖拽

let scrollt = document.querySelector(".pdfViewer").parentElement.parentElement.scrollTop;
var dragImg = Vue.extend(DragImg);
const app = new dragImg({
    propsData: {
          deleteDragImg: this.deleteDragImg, // 删除图片方法
          imgData: imgData || this.signImgBase64, // 图片的Base64
          isReuse: isReuse,
          top: scrollt, //距离顶部的距离
          imgType: type,
          pauseHandle:this.pauseHandle // 是否禁用拖拽方法
        },
    ref: 'a'
}).$mount(document.createElement('div'));
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
      // this.pdfh5 当前预览pdf
      let currentNum = parseInt(this.pdfh5.pageNow[0].innerText);
      this.pdfh5.pages[this.pdfh5.currentNum - 1].appendChild(app.$el)
      this.pdfh5.pages[currentNum - 1].appendChild(app.$el)
      // 将图片插入到.pdfViewer下
      document.querySelector(".pdfViewer").appendChild(app.$el)
    }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

【vue2】实现上传pdf文件盖章与签名,盖章文件下载_pdf_04
将签名或者印章方法对应的页面中

    // 页面中有样式名为dragImg的,代表该页面有签名或者签章
          // 有签名或签章就绘制页面
          if (parentElement[i].className == 'dragImg') {
            const promise = new Promise((resolve, reject) => {
              let html = parentElement[i]
              let ele = html.querySelector('#pic')
              let eleImg = html.querySelector('#pic div')
              let top = ele.style.top ? ele.style.top.split('px')[0] - 0 : 0
              let topImg = eleImg.style.top ? eleImg.style.top.split('px')[0] - 0 : 0
              let currentTop = top + topImg
              if (currentTop > baseNum) {
                console.log('第二页及以上')
                let j = Math.ceil(currentTop / baseNum)
                let pageContainer = document.querySelector(".pdfViewer .pageContainer" + j)
                html.style.top = 0
                ele.style.top = 0
                eleImg.style.top = currentTop - baseNum * (j - 1) + 'px'
                // 插入到对应页面中
                pageContainer.appendChild(html)
                resolve()
              } else {
                console.log('第一页')
                let pageContainer = document.querySelector(".pdfViewer .pageContainer1")
                pageContainer.appendChild(html)
                resolve()
              }
            })
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
// 绘制图片
html2canvas(parent.children[i])
    .then(function (canvas) {
         // 绘制.pageContainer元素
         if (parent.children[i].className != 'dragImg') {
                let url = canvas.toDataURL();
                let contentWidth = canvas.width
                let contentHeight = canvas.height
                let pageHeight = (contentWidth / 592.28) * 841.89
                let leftHeight = contentHeight
                let position = 0
                let imgWidth = 595.28
                let imgHeight = (592.28 / contentWidth) * contentHeight
                let pageData = canvas.toDataURL('image/jpeg', 1.0)
                // 第二页及以上
                if (i != 0) PDF.addPage() // 添加空白页
                if (leftHeight < pageHeight) {
                  PDF.addImage(pageData, 'JPEG', 0, 0, imgWidth, imgHeight) //插入图片
                } else {
                  if (leftHeight > 0) {
                    PDF.addImage(pageData, 'JPEG', 0, position, imgWidth, imgHeight) //插入图片
                    leftHeight -= pageHeight
                    position -= 841.89
                  }
                }
              }
              if (length - 1 == i) {
                setTimeout(()=>{
                  that.loading = false
                  // pdf下载
                  PDF.save(new Date().getTime() + '.pdf')
                },1000)
              }
            });
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.

完整代码下载地址:
<https://download.csdn.net/download/weixin_45291798/88580529?spm=1001.2014.3001.5503>

其他

​依赖安装报错解决
​​【vue2】实现上传pdf文件盖章与签名,盖章文件下载_pdf_05

npm install canvas@2.8.0 --ignore-scripts

只执行npm install canvas会报新的错误