vue download page to generate pdf, and print

1. Download page to generate pdf

1. Install the corresponding dependency package

npm install html2canvas;
npm install jspdf --save

2. Create a htmlToPdf.js file in utils and reference it globally in main.js

import html2Canvas from 'html2canvas'
import JsPDF from 'jspdf'
export default {
  install(Vue, options) {
    Vue.prototype.getPdf = function () {
      var title = this.htmlTitle //DPF title
        html2Canvas(document. querySelector('#printTest'), {
          allowTaint: true,
          taintTest: false,
          useCORS: true,
          y:0, // crop the Y axis
          x:0, // crop the x axis
          // width:1200, set the width
          // height:5000, set the height
          dpi: window.devicePixelRatio * 4, //Increase the resolution to a specific DPI by four times
          scale: 4 //increase the resolution proportionally
        }).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'
Vue.use(htmlToPdf)

3. Add a label to the vue page, the id is consistent with the js file, declare the variable to specify the pdf name

<div id="printTest">generate pdf content</div>
data() {
return {
    htmlTitle: 'The name of the pdf file to be downloaded'
  }
}

4. Add events

<el-button type="primary" @click="getPdf()">Download</el-button>

2. Print

1. Create a print.js file

//print.js
// Print class attributes, method definitions
/* 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>";
    // Remove the height: 100% style to solve the problem of style confusion under pagination
    str + = "<style>html,body,div{height: auto!important;}</style>";

    return str;
  },
  getHtml: function () {
    var inputs = document. querySelectorAll('input');
    var textareas = document. querySelectorAll('textarea');
    var selects = document. querySelectorAll('select');
    var canvas = document. querySelectorAll('canvas');
    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')
            }
          }
        }
      }
    }
    //canvass echars chart converted to picture
    for (var k4 = 0; k4 < canvass. length; k4 ++ ) {
      var imageURL = canvass[k4].toDataURL("image/png");
      var img = document.createElement("img");
      img.src = imageURL;
      img.setAttribute('style', 'max-width: 100%;');
      img.className = 'isNeedRemove'
      // canvass[k4].style.display = 'none'
      // canvass[k4].parentNode.style.width = '100%'
      // canvass[k4].parentNode.style.textAlign = 'center'
      canvass[k4].parentNode.insertBefore(img, canvass[k4].nextElementSibling);
    }
    // Wrap the element to be printed
    // fix: https://github.com/xyl66/vuePlugs_printjs/issues/36
    // return this.wrapperRefDom(this.dom).outerHTML;
    return this.dom.outerHTML;
  },
  // Loop to the parent element, wrap the element that needs to be printed currently
  // Prevent css selectors at the beginning of the root level from not taking effect
  wrapperRefDom: function (refDom) {
    let prevDom = null
    let currDom = refDom
    while (currDom & amp; & amp; currDom. tagName. toLowerCase() !== 'body') {
      if (prevDom) {
        let element = currDom. cloneNode(false)
        element.appendChild(prevDom)
        prevDom = element
      } else {
        prevDom = currDom. cloneNode(true)
      }

      currDom = currDom. parentElement
    }

    return currDom.tagName.toLowerCase() === 'body' ? currDom : 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);
    }
  },
  isDOM: (typeof HTMLElement === 'object') ?
    function (obj) {
      return obj instanceof HTMLElement;
    } :
    function (obj) {
      return obj & amp; & amp; typeof obj === 'object' & amp; & amp; obj.nodeType === 1 & amp; & amp; typeof obj.nodeName === 'string';
    }
};
const MyPlugin = {}
MyPlugin. install = function (Vue, options) {
  // 4. Add instance method
  Vue.prototype.$print = Print
}
export default MyPlugin

You can also install dependencies: npm install vue-print-nb –save

2. Introduce it in main.js and mount it globally

import Print from '@/assets/js/print.js'

Vue. use(Print);

3. Add tags

<div id="printTest" ref="print">printed content</div>
<el-button type="success" @click="getPrint">Print</el-button>

4. Method

 getPrint () {
      //Add ref to read the label, no-print does not participate in printing, and the printing call is as follows:
      this. $print(this. $refs. print);
      //Because each printing will add an img (convert the canvas of echarts to img), the classname added to this img has been configured in my print.js as isNeedRemove, so it needs to be removed after each printing, and I only have one The number of echarts and removal is determined according to business needs.
      this. $nextTick(() => {
        let arr = document.getElementsByClassName('isNeedRemove');
        if (arr. length) arr[0]. remove();
      });
    },

5. Modify the style (direct printing style may not be displayed)

@page {
  size: auto A4 landscape;
  margin: 20px auto;
}
@media print {
  // Write the styles you need here
  #printTest {}
}

If you have better suggestions, please leave your valuable opinions in the comment area! ! !