Web实现纯前端输出pdf打印 print

11 篇文章 0 订阅
8 篇文章 0 订阅

技术栈:JavaScript + Vue + Print.js

简介

后台管理系统中,会常用到打印功能,打印一些表格数据、信息之类的。
此处做个简单的打印示例。
此功能模块,主要分为三个部分

  1. 打印模板文件,提前编写好的数据展示模板
  2. 打印组件,将模板文件名跟模板文件映射起来
  3. 应用demo,实际应用

实现

打印模板文件

根据实际需求,绘制好需要的展示效果,布局、css样式等实现。
有个按钮,模拟模板的点击打印事件。

<template>
  <div>
    <div v-if="record" id="theidforvprint">
      <h2>Title</h2>
      <table :style="bindStyle.tableCom" class="showBorder">
        <tr>
          <td class="td-label">姓名</td>
          <td class="td-value">{{ record.name }}</td>
          <td class="td-label">年龄</td>
          <td class="td-value">{{ record.age }}</td>
        </tr>
        <tr>
          <td class="td-label">性别</td>
          <td class="td-value">{{ record.sex }}</td>
          <td class="td-label">电话</td>
          <td class="td-value">{{ record.tel }}</td>
        </tr>
        <tr>
          <td class="td-label">住址</td>
          <td class="td-value">{{ record.address }}</td>
        </tr>
        <tr class="border-none-bottom">
          <td class="td-label">公司</td>
          <td class="td-value">{{ record.company }}</td>
          <td class="td-label">工号</td>
          <td class="td-value">{{ record.code }}</td>
        </tr>
        <tr class="border-none-vertical">
          <td class="td-label">部门</td>
          <td class="td-value">{{ record.dep }}</td>
        </tr>
        <tr>
          <td class="td-label">描述</td>
          <td class="td-value">{{ record.desc }}</td>
        </tr>
      </table>
    </div>
    <button ref="printBtn" v-print="'#theidforvprint'" style="opacity: 0">
      导出
    </button>
  </div>
</template>
<script>
export default {
  name: "TableCase",
  data() {
    return {
      record: null,
      bindStyle: {
        tableCom: {
          borderCollapse: "collapse",
          borderSpacing: "0",
          tableLayout: "fixed",
          width: "100%",
          fontFamily: "sans-serif",
        },
      },
    };
  },

  created() {
    console.log("created");
  },
  mounted() {
    console.log("mounted");
  },
  methods: {
    handleRecord(record) {
      for (let key in record) {
        let obj = {
          label: key,
          value: record[key],
          col: key === "tel" ? 2 : 1,
        };
        this.renderConfigList.push(obj);
      }
    },
    async print(record) {
      this.record = record;
      await this.$refs.printBtn.click();
      this.record = null; //hide dom
      this.renderConfigList = null;
    },
  },
};
</script>
<style lang="scss" scoped>
@media print {
  #theidforvprint {
    width: 100%;
    h2 {
      text-align: center;
    }
    td {
      font-size: 16px;
      line-height: 1.5;
      color: #999999;
    }
    .td-label {
      font-weight: bold;
    }
    // 正确代码
    .showBorder tr {
      border: 1px solid #999999;
    }

    .showBorder tr:first-child {
      border-bottom: 0;
    }
    .showBorder tr:nth-child(-n + 3):nth-child(n + 2) {
      border-top: 0;
      border-bottom: 0;
    }
    .showBorder .border-none-vertical {
      border-top: 0;
      border-bottom: 0;
    }
    .showBorder .border-none-bottom {
      border-bottom: 0;
    }
  }
}
</style>

打印组件

将涉及到的模板文件都引入到此组件中,组件根据模板文件名自动匹配、渲染指定打印模板。

<template>
  <div>
    <component ref="certainTemp" :is="templateName" id="theidforvprint" />
  </div>
</template>
<script>
import tableCase from './case/tableCase'
export default {
  name: "TemplateRoot",
  components: {
    tableCase
  },
  data() {
    return {
      templateName: '',
    }
  },
  methods: {
    async print(record, templateName) {
      this.templateName = templateName;
      await this.$forceUpdate();
      this.$refs.certainTemp.print(record);
    },
  },
}
</script>

应用

测试案例

点击“导出数据按钮”,调用浏览器打印功能。

<template>
  <div>
    <button @click="toPrint">导出数据</button>
    <templateRoot ref="templateRoot" />
  </div>
</template>
<script>
import templateRoot from './printTemplate/templateRoot.vue';
export default {
  name: "PrintData",
  components: {
    templateRoot,
  },
  data() {
    return { 
      list: {
        name: "name",
        age: "20",
        sex: "women",
        address: "xx省xx市xxx路xxxx号",
        company: "zxcvbnasd",
        code: "110",
        dep: "叽里呱啦部门",
        tel: "1234567",
        desc: "叽里呱啦能力,工作内容为啊吧啊吧,擅长嘻嘻哈哈,时常7788"
      }
    }
  },
  methods: {
    toPrint() {
      this.$refs.templateRoot.print(this.list, "tableCase");
    }
  }
   
}
</script>

效果

浏览器调用起的打印预览

附: Print工具源码

index.js

print 入口文件

import Print from './packages/print.js';
Print.install = function(Vue) {
    Vue.directive('print', Print);
};

export default Print;

print.js

'use strict';

Object.defineProperty(exports, "__esModule", {
  value: true
});

var _printarea = require('./printarea.js');

var _printarea2 = _interopRequireDefault(_printarea);

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

exports.default = {
  directiveName: 'print',
  bind: function bind(el, binding, vnode) {
    var vue = vnode.context;
    var closeBtn = true;
    el.addEventListener('click', function (event) {
      if (binding.value) {
        localPrint();
      } else {
        window.print();
      }
    });

    var localPrint = function localPrint() {
      vue.$nextTick(function () {
        if (closeBtn) {
          closeBtn = false;
          var print = new _printarea2.default({
            el: binding.value,
            endCallback: function endCallback() {
              closeBtn = true;
            }
          });
        }
      });
    };
  },
  // update: function update(el, binding) {},
  // unbind: function unbind(el) {}
};

printarea.js

packages/printarea.js

'use strict'

Object.defineProperty(exports, '__esModule', {
  value: true
})

var _assign = require('babel-runtime/core-js/object/assign')

var _assign2 = _interopRequireDefault(_assign)

var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck')

var _classCallCheck3 = _interopRequireDefault(_classCallCheck2)

var _createClass2 = require('babel-runtime/helpers/createClass')

var _createClass3 = _interopRequireDefault(_createClass2)

function _interopRequireDefault(obj) {
  return obj && obj.__esModule ? obj : { default: obj }
}

var _class = (function () {
  function _class(option) {
    ;(0, _classCallCheck3.default)(this, _class)

    this.standards = {
      strict: 'strict',
      loose: 'loose',
      html5: 'html5'
    }
    this.counter = 0
    this.settings = {
      standard: this.standards.html5,
      extraHead: '',
      extraCss: '',
      popTitle: '',
      endCallback: null,
      el: ''
    }
    ;(0, _assign2.default)(this.settings, option)
    this.init()
  }

  ;(0, _createClass3.default)(_class, [
    {
      key: 'init',
      value: function init() {
        this.counter++
        this.settings.id = 'printArea_' + this.counter
        var box = document.getElementById(this.settings.id)
        if (box) {
          box.parentNode.removeChild(box)
        }
        var PrintAreaWindow = this.getPrintWindow()
        this.write(PrintAreaWindow.doc)
        this.settings.endCallback()
      }
    },
    {
      key: 'print',
      value: function print(PAWindow) {
        var paWindow = PAWindow
        console.log('---调用打印 focus-----')
        paWindow.focus()
        paWindow.print()
        console.log('---调用打印 print-----')
      }
    },
    {
      key: 'write',
      value: function write(PADocument, $ele) {
        PADocument.open()
        PADocument.write(this.docType() + '<html>' + this.getHead() + this.getBody() + '</html>')
        PADocument.close()
      }
    },
    {
      key: 'docType',
      value: function docType() {
        if (this.settings.standard === this.standards.html5) {
          return '<!DOCTYPE html>'
        }
        var transitional = this.settings.standard === this.standards.loose ? ' Transitional' : ''
        var dtd = this.settings.standard === this.standards.loose ? 'loose' : 'strict'

        return (
          '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01' +
          transitional +
          '//EN" "http://www.w3.org/TR/html4/' +
          dtd +
          '.dtd">'
        )
      }
    },
    {
      key: 'getHead',
      value: function getHead() {
        var extraHead = ''
        var links = ''
        var style = ''
        if (this.settings.extraHead) {
          this.settings.extraHead.replace(/([^,]+)/g, function (m) {
            extraHead += m
          })
        }
        ;[].forEach.call(document.querySelectorAll('link'), function (item, i) {
          if (item.href.indexOf('.css') >= 0) {
            links += '<link type="text/css" rel="stylesheet" href="' + item.href + '" >'
          }
        })

        for (var i = 0; i < document.styleSheets.length; i++) {
          let flag = false
          try {
            flag = document.styleSheets[i].cssRules || document.styleSheets[i].rules
          } catch (err) {
            let flag = false
            console.error(err)
          }
          if (flag) {
            var rules = document.styleSheets[i].cssRules || document.styleSheets[i].rules
            for (var b = 0; b < rules.length; b++) {
              try {
                style += rules[b].cssText
              } catch (err) { console.log(err) }
            }
          }
        }

        if (this.settings.extraCss) {
          this.settings.extraCss.replace(/([^,\s]+)/g, function (m) {
            links += '<link type="text/css" rel="stylesheet" href="' + m + '">'
          })
        }

        return (
          '<head><title>' +
          this.settings.popTitle +
          '</title>' +
          extraHead +
          links +
          '<style type="text/css">' +
          style +
          '</style></head>'
        )
      }
    },
    {
      key: 'getBody',
      value: function getBody() {
        var ele = this.getFormData(document.querySelector(this.settings.el))
        var htm = ele.outerHTML
        console.log('htm', htm)
        return '<body>' + htm + '</body>'
      }
    },
    {
      key: 'getFormData',
      value: function getFormData(ele) {
        var copy = ele.cloneNode(true)

        var allElements = copy.querySelectorAll('*')
        ;[].forEach.call(allElements, function (item) {
          var attr = item.getAttribute('ignore-print')
          attr = attr == null ? item.getAttribute('ignoreprint') : attr
          if (attr != null && attr.toString() === 'true') {
            item.outerHTML = ''
          }
        })

        var copiedInputs = copy.querySelectorAll('input,select,textarea')
        ;[].forEach.call(copiedInputs, function (item, i) {
          var typeInput = item.getAttribute('type')
          var copiedInput = copiedInputs[i]

          if (typeInput == null) {
            typeInput = item.tagName === 'SELECT' ? 'select' : item.tagName === 'TEXTAREA' ? 'textarea' : ''
          }
          if (typeInput === 'radio' || typeInput === 'checkbox') {
            copiedInput.setAttribute('checked', item.checked)
          } else if (typeInput === 'select') {
            ;[].forEach.call(copiedInput.querySelectorAll('option'), function (op, b) {
              if (op.selected) {
                op.setAttribute('selected', true)
              }
            })
          } else if (typeInput === 'textarea') {
            copiedInput.innerHTML = item.value
          } else {
            copiedInput.value = item.value
            copiedInput.setAttribute('value', item.value)
          }
        })

        var sourceCanvas = ele.querySelectorAll('canvas')
        var copyCanvas = copy.querySelectorAll('canvas')

        ;[].forEach.call(copyCanvas, function (item, i) {
          var url = sourceCanvas[i].toDataURL()

          item.outerHTML = '<img src="' + url + '" style="width:100%;"/>'
        })

        return copy
      }
    },
    {
      key: 'getPrintWindow',
      value: function getPrintWindow() {
        var f = this.Iframe()
        return {
          win: f.contentWindow || f,
          doc: f.doc
        }
      }
    },
    {
      key: 'Iframe',
      value: function Iframe() {
        var frameId = this.settings.id
        var iframe = void 0
        var that = this
        try {
          iframe = document.createElement('iframe')
          document.body.appendChild(iframe)
          iframe.style.border = '0px'
          iframe.style.position = 'absolute'
          iframe.style.width = '0px'
          iframe.style.height = '0px'
          iframe.style.right = '0px'
          iframe.style.top = '0px'
          iframe.setAttribute('id', frameId)
          iframe.setAttribute('src', new Date().getTime())
          iframe.doc = null
          iframe.onload = function () {
            var win = iframe.contentWindow || iframe
            that.print(win)
          }
          iframe.doc = iframe.contentDocument
            ? iframe.contentDocument
            : iframe.contentWindow
            ? iframe.contentWindow.document
            : iframe.document
        } catch (e) {
          throw new Error(e + '. iframes may not be supported in this browser.')
        }

        if (iframe.doc == null) {
          throw new Error('Cannot find document.')
        }

        return iframe
      }
    }
  ])
  return _class
})()

exports.default = _class

  • 10
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Web API实现前端发送的打印请求,可以按照以下步骤进行: 1. 在Web API的控制器中,定义一个接收打印请求的方法: ```csharp [HttpPost] public IActionResult Print([FromBody] PrintRequest request) { // 处理打印请求 return Ok(); } ``` 其中,PrintRequest是一个自定义的请求模型,包含报表名称和报表参数等信息。 2. 在处理打印请求的方法中,使用DevExpress Reporting库加载报表文件,设置报表参数,最后通过ReportPrintTool执行打印操作: ```csharp using DevExpress.XtraReports.UI; using DevExpress.XtraReports.Web.WebDocumentViewer; [HttpPost] public IActionResult Print([FromBody] PrintRequest request) { // 加载报表文件 var report = XtraReport.FromFile(Path.Combine(_hostingEnvironment.ContentRootPath, "Reports", request.ReportName + ".repx")); // 设置报表参数 foreach (var parameter in request.Parameters) { report.Parameters[parameter.Key].Value = parameter.Value; } // 执行打印操作 var printTool = new ReportPrintTool(report); printTool.PrintDialog(); printTool.Dispose(); return Ok(); } ``` 其中,_hostingEnvironment是IWebHostEnvironment的实例,用于获取报表文件的路径。 3. 在前端中,使用axios或其他HTTP库向Web API发送打印请求: ```javascript import axios from 'axios'; axios.post('http://localhost:5000/print', { reportName: 'Report1', parameters: { parameter1: 'value1', parameter2: 'value2' } }).then(response => { console.log(response); }); ``` 其中,reportName表示报表名称,parameters是一个对象,包含报表中需要的参数。 这样,就可以在Web API实现前端发送的打印请求。在处理打印请求的方法中,使用DevExpress Reporting库加载报表文件,设置报表参数,并执行打印操作。在前端中,使用axios或其他HTTP库向Web API发送打印请求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值