vue前端页面实现打印与将页面转换为PDF下载

vue前端页面实现打印与将页面转换为PDF下载:
首先是一个合同页面,需要的功能是,直接在页面打印该合同,或者将该合同以PDF的格式下载下来:
在这里插入图片描述
页面最下方:
在这里插入图片描述
首先是打印:
调用了doPrint函数:

doPrint() {
    
    
      this.$print(this.$refs.print);
    }

在utils下面定义print.js:
在这里插入图片描述

// 打印类属性、方法定义
/* eslint-disable */
const Print = function (dom, options) {
    
    
    if (!(this instanceof Print)) return new Print(dom, options);

    this.options = this.extend({
    
    
      'noPrint': '.no-print'
    }, options);

    if ((typeof dom) === "string") {
    
    
      this.dom = document.querySelector(dom);
    } else {
    
    
      this.isDOM(dom)
      this.dom = this.isDOM(dom) ? dom : dom.$el;
    }

    this.init();
  };
  Print.prototype = {
    
    
    init: function () {
    
    
      var content = this.getStyle() + this.getHtml();
      this.writeIframe(content);
    },
    extend: function (obj, obj2) {
    
    
      for (var k in obj2) {
    
    
        obj[k] = obj2[k];
      }
      return obj;
    },

    getStyle: function () {
    
    
      var str = "",
        styles = document.querySelectorAll('style,link');
      for (var i = 0; i < styles.length; i++) {
    
    
        str += styles[i].outerHTML;
      }
      str += "<style>" + (this.options.noPrint ? this.options.noPrint : '.no-print') + "{display:none;}</style>";

      return str;
    },

    getHtml: function () {
    
    
      var inputs = document.querySelectorAll('input');
      var textareas = document.querySelectorAll('textarea');
      var selects = document.querySelectorAll('select');

      for (var k = 0; k < inputs.length; k++) {
    
    
        if (inputs[k].type == "checkbox" || inputs[k].type == "radio") {
    
    
          if (inputs[k].checked == true) {
    
    
            inputs[k].setAttribute('checked', "checked")
          } else {
    
    
            inputs[k].removeAttribute('checked')
          }
        } else if (inputs[k].type == "text") {
    
    
          inputs[k].setAttribute('value', inputs[k].value)
        } else {
    
    
          inputs[k].setAttribute('value', inputs[k].value)
        }
      }

      for (var k2 = 0; k2 < textareas.length; k2++) {
    
    
        if (textareas[k2].type == 'textarea') {
    
    
          textareas[k2].innerHTML = textareas[k2].value
        }
      }

      for (var k3 = 0; k3 < selects.length; k3++) {
    
    
        if (selects[k3].type == 'select-one') {
    
    
          var child = selects[k3].children;
          for (var i in child) {
    
    
            if (child[i].tagName == 'OPTION') {
    
    
              if (child[i].selected == true) {
    
    
                child[i].setAttribute('selected', "selected")
              } else {
    
    
                child[i].removeAttribute('selected')
              }
            }
          }
        }
      }
      // 包裹要打印的元素
      // fix: https://github.com/xyl66/vuePlugs_printjs/issues/36
      let outerHTML = this.wrapperRefDom(this.dom).outerHTML
      return outerHTML;
    },
    // 向父级元素循环,包裹当前需要打印的元素
    // 防止根级别开头的 css 选择器不生效
    wrapperRefDom: function (refDom) {
    
    
      let prevDom = null
      let currDom = refDom
      // 判断当前元素是否在 body 中,不在文档中则直接返回该节点
      if (!this.isInBody(currDom)) return currDom

      while (currDom) {
    
    
        if (prevDom) {
    
    
          let element = currDom.cloneNode(false)
          element.appendChild(prevDom)
          prevDom = element
        } else {
    
    
          prevDom = currDom.cloneNode(true)
        }

        currDom = currDom.parentElement
      }

      return prevDom
    },

    writeIframe: function (content) {
    
    
      var w, doc, iframe = document.createElement('iframe'),
        f = document.body.appendChild(iframe);
      iframe.id = "myIframe";
      //iframe.style = "position:absolute;width:0;height:0;top:-10px;left:-10px;";
      iframe.setAttribute('style', 'position:absolute;width:0;height:0;top:-10px;left:-10px;');
      w = f.contentWindow || f.contentDocument;
      doc = f.contentDocument || f.contentWindow.document;
      doc.open();
      doc.write(content);
      doc.close();
      var _this = this
      iframe.onload = function(){
    
    
        _this.toPrint(w);
        setTimeout(function () {
    
    
          document.body.removeChild(iframe)
        }, 100)
      }
    },

    toPrint: function (frameWindow) {
    
    
      try {
    
    
        setTimeout(function () {
    
    
          frameWindow.focus();
          try {
    
    
            if (!frameWindow.document.execCommand('print', false, null)) {
    
    
              frameWindow.print();
            }
          } catch (e) {
    
    
            frameWindow.print();
          }
          frameWindow.close();
        }, 10);
      } catch (err) {
    
    
        console.log('err', err);
      }
    },
    // 检查一个元素是否是 body 元素的后代元素且非 body 元素本身
    isInBody: function (node) {
    
    
      return (node === document.body) ? false : document.body.contains(node);
    },
    isDOM: (typeof HTMLElement === 'object') ?
      function (obj) {
    
    
        return obj instanceof HTMLElement;
      } :
      function (obj) {
    
    
        return obj && typeof obj === 'object' && obj.nodeType === 1 && typeof obj.nodeName === 'string';
      }
  };
  const MyPlugin = {
    
    }
  MyPlugin.install = function (Vue, options) {
    
    
    // 4. 添加实例方法
    Vue.prototype.$print = Print
  }
  export default MyPlugin

在style中加入以下代码:
在这里插入图片描述

@media print {
    
    
  //重要
  /deep/ #app .content-container {
    
    
    width: 100% !important;
    // height: 100% !important;
    padding: 0 !important;
    // margin: 0 !important;
    margin-left: 0px !important;
  }
  //重要
  /deep/ #app .main-container {
    
    
    width: 100% !important;
    height: 100% !important;
    padding: 0 !important;
    margin: 0 !important;
  }
}
.pagehead {
    
    
  width: 100%;
  overflow: hidden;
}
.titleimg {
    
    
  height: 42px;
  width: 100%;
  margin-bottom: 0px;
}

/deep/ .el-radio__input.is-disabled + span.el-radio__label {
    
    
  color: #000 !important;
  font-weight: bold !important;
  font-size: 16px !important;
  padding-left: 3px !important;
}
.paper {
    
    
  padding-bottom: 60px;
}
.btnbox {
    
    
  width: calc(100vw - 180px);
  position: fixed;
  bottom: 0;
  right: 0;
  height: 60px;
  background-color: #fff;
  box-shadow: 1px 3px 9px rgba($color: #000000, $alpha: 0.2);
  background-color: #fff;
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
}
/deep/.el-image {
    
    
  width: 100px;
  height: 100px;
}
.dialog-footer {
    
    
  padding-top: 20px;
  text-align: center;
  color: #000000;
}
.el-form-item {
    
    
  display: inline-block;
  width: 40%;
  margin-bottom: 5px;
}

.contract-box {
    
    
  font-family: SimSun;
  line-height: 1.6;
  color: #000;
  font-weight: bold;
  text-align: center;
  margin: 10px auto;
  padding: 2% 23mm;
  div {
    
    
    margin-bottom: 10px;
  }
  .xiahuaxian {
    
    
    border-bottom: 1px solid #333;
    padding: 0 10px;
  }
  .space {
    
    
    padding-left: 30px;
  }

  .header {
    
    
    font-weight: 500;

    .title {
    
    
      font-weight: bold;
      font-family: SimSun;
      color: #000;
      font-size: 24px;
    }
    .sub-title {
    
    
      font-weight: bold;
      font-family: SimSun;
      color: #000;
      font-size: 22px;
      padding-top: 10px;
    }
  }
  .center {
    
    
    // padding-top: 10px;
    font-weight: 500;
    font-size: 16px;
    text-align: left;
    color: #000;
    // font-weight: bold;
  }
  .content {
    
    
    font-weight: 500;
    font-size: 16px;
    text-align: left;
    color: #000;
    font-weight: bold;
    text-indent: 2em;
    margin-bottom: 10px;
    text-indent: 40px;
  }
}
/deep/ .el-radio__input.is-disabled + span.el-radio__label {
    
    
  color: #333;
}
/deep/ .el-radio__input.is-disabled.is-checked .el-radio__inner {
    
    
  background-color: #ffffff;
  border-color: #9c9c9c;
}
/deep/ .el-radio__input.is-disabled .el-radio__inner {
    
    
  background-color: #ffffff;
  border-color: #9c9c9c;
}
/deep/ .el-radio__input.is-disabled.is-checked .el-radio__inner::after {
    
    
  background-color: #7e8ba5;
}
.mytable {
    
    
  width: 100%;
  text-align: center;
  border-collapse: collapse;
  .mytd {
    
    
    // padding: 10px 0;
    height: 34px;
    .inner-div {
    
    
      text-align: left;
      padding-top: 5px;
      padding-left: 5px;
    }
  }
}
.sign-box {
    
    
  display: flex;
  align-items: flex-start;
  .sign-item {
    
    
    width: 50%;
    display: flex;
    justify-content: flex-start;
    align-items: flex-end;
  }
}
.sheet {
    
    
  -webkit-box-shadow: 0 0.5mm 2mm rgb(255, 255, 255);
  box-shadow: 0 0.5mm 2mm rgba(0, 0, 0, 0);
}

在main.js中引入并使用:import Print from './utils/print.js' Vue.use(Print)
在这里插入图片描述
测试打印:
在这里插入图片描述

二、下载:
在utils下面定义htmlToPdf.js:

// 导出页面为PDF格式
import html2Canvas from "html2canvas";
import JsPDF from "jspdf";
export default {
    
    
  install(Vue, options) {
    
    
    Vue.prototype.getPdf = function() {
    
    
      var title = this.htmlTitle;
      html2Canvas(document.querySelector("#pdfDom"), {
    
    
        allowTaint: true
      }).then(function(canvas) {
    
    
        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);
        let PDF = new JsPDF("", "pt", "a4");
        if (leftHeight < pageHeight) {
    
    
          PDF.addImage(pageData, "JPEG", 0, 0, imgWidth, imgHeight);
        } else {
    
    
          while (leftHeight > 0) {
    
    
            PDF.addImage(pageData, "JPEG", 0, position, imgWidth, imgHeight);
            leftHeight -= pageHeight;
            position -= 841.89;
            if (leftHeight > 0) {
    
    
              PDF.addPage();
            }
          }
        }
        PDF.save(title + ".pdf");
      });
    };
  }
};

在main.js中调用:import htmlToPdf from './utils/htmlToPdf.js' Vue.use(htmlToPdf)
在这里插入图片描述

这里保存后页面会报错:找不到jspdf这个模块

These dependencies were not found:

* html2canvas in ./src/utils/htmlToPdf.js
* jspdf in ./src/utils/htmlToPdf.js

To install them, you can run: npm install --save html2canvas jspdf

安装该模块:cnpm install [email protected]
在这里插入图片描述

测试下载:
在这里插入图片描述

其中页面引入的css文件已经放在云盘:链接: https://pan.baidu.com/s/1iNj8Z1Wk4JiaiCXHfNXR4w?pwd=6666 提取码: 6666 复制这段内容后打开百度网盘手机App,操作更方便哦
在这里插入图片描述

合同页面的模板代码如下:

扫描二维码关注公众号,回复: 16263922 查看本文章
<template>
  <div style="  background-color: #fff;">
    <!-- <el-breadcrumb
      separator-class="el-icon-arrow-right"
      style="margin: 0 5px 5px 5px"
    >
      <el-breadcrumb-item :to="{ path: 'contract' }"
        >合同列表</el-breadcrumb-item
      >
      <el-breadcrumb-item>合同详情</el-breadcrumb-item>
    </el-breadcrumb> -->
    <div class="paper A4" style="margin:0 auto;background-color: #fff;">
      <div class="contract-box sheet" ref="print" id="pdfDom">
        <div class="header">
          <!-- <div class="pagehead" style="margin-bottom: 0">
            <el-image
       class="titleimg"
      :src="contractInfo.logo"></el-image>

          </div> -->
          <div class="title" style="margin-bottom: 0">
            {
    
    {
    
     contractInfo.title }}
          </div>

          <!-- <div class="sub-title" style="margin-bottom: 0">工程业务合约</div> -->
        </div>
        <div class="center">
          <div>
              <span>  甲方单位名称:</span>
              <span class="xiahuaxian"
                >{
    
    {
    
     contractInfo.partya }}</span>
              <span class="space"></span>
               <span>  甲方代表:</span>
              <span class="xiahuaxian"
                >{
    
    {
    
     contractInfo.partyaDelegate }}</span>
              <span class="space"></span>
              <span>  甲方代表联系方式:</span>
              <span class="xiahuaxian">{
    
    {
    
     contractInfo.partyaMobile }}</span>
              <span class="space"></span>
              <span>  甲方公司地址:</span>
              <span class="xiahuaxian">{
    
    {
    
    contractInfo.partyaAddress }}</span>
               <!-- <span class="space"></span>
              <span>  甲方项目经理:</span>
              <span class="xiahuaxian">{
    
    {
    
     contractInfo.siteManager }}</span>
              <span class="space"></span>
              <span>  联系方式:</span>
              <span class="xiahuaxian">{
    
    {
    
     contractInfo.siteManagerMobile }}</span> -->
            </div>
            <div>
               <span>  乙方单位名称:</span>
              <span class="xiahuaxian"
                >{
    
    {
    
     contractInfo.partyb }}</span>
              <span class="space"></span>
              <span>  乙方代表:</span>
              <span class="xiahuaxian"
                >{
    
    {
    
     contractInfo.partybDelegate }}</span>
              <span class="space"></span>
              <span>  乙方代表联系方式:</span>
              <span class="xiahuaxian">{
    
    {
    
     contractInfo.partybMobile }}</span>
               <span class="space"></span>
              <span>  乙方公司地址:</span>
              <span class="xiahuaxian">{
    
    {
    
     contractInfo.partybAddress }}</span>
              <!-- <span class="space"></span>
              <span> 经理联系方式:</span>
              <span class="xiahuaxian">{
    
    {
    
     contractInfo.partybManagerMobile }}</span> -->
            </div>
          <div class="content">
            {
    
    {
    
     contractInfo.content }}
          </div>

          <div>
            <div>
              <!-- <span>工程项目地址:</span>
              <span class="xiahuaxian"
                >{
    
    {
    
     contractInfo.province }}{
    
    {
    
     contractInfo.city
                }}{
    
    {
    
     contractInfo.region
                }}{
    
    {
    
     contractInfo.detailAddress }}</span
              > -->
              <span class="space"></span>
              <span>约定服务工期:</span>
              <span class="xiahuaxian"
                >{
    
    {
    
     contractInfo.startTime }}{
    
    {
    
    
                  contractInfo.finishTime
                }}</span
              >
            </div>
            <div>
              包工:工程项目总额:<span class="xiahuaxian">
                {
    
    {
    
     contractInfo.totalMoney }} </span
              ><span class="space"></span>
              定金及预付款:<span class="xiahuaxian">
                {
    
    {
    
     contractInfo.frontMoney }}</span
              ><span class="space"></span>
              项目尾款:<span class="xiahuaxian">{
    
    {
    
    
                contractInfo.finishMoney
              }}</span
              ></div>
            <!-- <div>
              点工:<span class="xiahuaxian">{
    
    {
    
    
                contractInfo.spotWorkWages
              }}</span
              >/<span class="space"></span>
              约定服务时间:<span class="xiahuaxian"
                >{
    
    {
    
     contractInfo.appointmentStartTime }}{
    
    {
    
    
                  contractInfo.appointmentEndTime
                }}</span
              >
              <span class="space"></span>
              约定加班费:<span class="xiahuaxian">{
    
    {
    
    
                contractInfo.appointmentWorkOvertime
              }}</span>
              <span class="space"></span>
            </div> -->
            <!-- <div>
              付款渠道:
              <el-radio
                :label="1"
                v-model="isCheck"
                :disabled="true"
                style="color: #000; font-weight: bold; font-size: 16px"
                >微信</el-radio
              >
              <el-radio
                :label="2"
                v-model="isCheck"
                :disabled="true"
                style="color: #000; font-weight: bold"
                >支付宝</el-radio
              >
              <el-radio
                :label="3"
                v-model="isCheck"
                :disabled="true"
                style="color: #000; font-weight: bold"
                >银行转账</el-radio
              >
              <el-radio
                :label="4"
                v-model="isCheck"
                :disabled="true"
                style="color: #000; font-weight: bold"
                >现金</el-radio
              >
              <el-radio
                :label="5"
                v-model="isCheck"
                :disabled="true"
                style="color: #000; font-weight: bold"
                >其他</el-radio
              >
              约定付款时间:<span class="xiahuaxian"></span>
            </div> -->
            <!-- <div>工程服务明细及要求:</div>
            <div>
              <table border="1" class="mytable">
                <tr v-if="otherContractProofing.length>0">
                  <td class="mytd" colspan="1">名称</td>
                  <td class="mytd" colspan="1">数量</td>
                  <td class="mytd" colspan="1">单价</td>
                  <td class="mytd" colspan="1">总价</td>
                </tr>
                <tr >
                  <td class="mytd" colspan="1">{
    
    {
    
     contractProofing.name }}</td>
                  <td class="mytd" colspan="1">{
    
    {
    
     contractProofing.num }}</td>
                  <td class="mytd" colspan="1">{
    
    {
    
     contractProofing.price }}</td>
                  <td class="mytd" colspan="1">
                    {
    
    {
    
     contractProofing.totalPrice }}
                  </td>
                </tr>
                <tr v-for="(item, index) in otherContractProofing" :key="index"  v-if="otherContractProofing.length>0">
                  <td class="mytd" colspan="1">{
    
    {
    
     item.name }}</td>
                  <td class="mytd" colspan="1">{
    
    {
    
     item.num }}</td>
                  <td class="mytd" colspan="1">{
    
    {
    
     item.price }}</td>
                  <td class="mytd" colspan="1">{
    
    {
    
     item.totalPrice }}</td>
                </tr>


                <tr  v-if="otherContractProofing.length>0">
                  <td class="mytd">合计:</td>
                  <td class="mytd">{
    
    {
    
     total.num }}</td>
                  <td class="mytd">{
    
    {
    
     total.price }}</td>
                  <td class="mytd">{
    
    {
    
     total.totalPrice }}</td>
                </tr>
                <tr>
                  <td class="mytd" colspan="5">
                    <div class="inner-div">
                      特殊注意事项:{
    
    {
    
     contractInfo.specialNotes }}
                    </div>
                  </td>
                </tr>
                <tr>
                  <td class="mytd" colspan="5">
                    <div class="inner-div">
                      约定服务质量及施工要求{
    
    {
    
     contractInfo.quality}}
                    </div>
                  </td>
                </tr>
              </table>
            </div> -->
            <div>
              合约备注:<br />
              {
    
    {
    
     contractInfo.remarks }}
            </div>
            <!-- <div>
              合约备注:
              <br />
              1.本合约一式三份,甲方持一份,乙方两份,以甲方签字,乙方签字盖章生效。
              <br />
              2.如有任何合约变动,需经双方协商,书面补充该协议,口头协议视为无效。
            </div> -->
            <div style="height: 1px"></div>
             <div class="sign-box" style="margin-bottom: 0px">
               <div class="sign-item" style="margin-bottom: 0">
                <span>甲方签名:</span>
               <!-- <img style="width:60px;height:60px" src="" alt=""> -->
              </div>
              <div class="sign-item" style="margin-bottom: 0">
                <span>乙方签名:</span>
               <!-- <img style="width:60px;height:60px" src="" alt=""> -->
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>

  </div>
</template>

猜你喜欢

转载自blog.csdn.net/weixin_42260782/article/details/127745837