Web前端开发之页面Print打印实现方案

195次阅读
没有评论
Web前端开发之页面Print打印实现方案

在Web开发中,打印功能在特定场景下至关重要,如报表生成、票据打印等。本文将探讨Web前端打印的常见方案,并介绍在实际应用中的技巧。

一、Web前端打印的基本方案

(一)浏览器自带打印功能

最基础的打印方式是利用浏览器自带的打印功能。用户可通过浏览器菜单或快捷键(如Ctrl+P)调用打印预览界面。此方法简单易用,无需额外代码,但自定义能力有限,通常会打印整个页面内容。

(二)JavaScript控制打印

通过JavaScript的window.print()方法,可编程地触发浏览器打印功能。此方法同样可打印整个页面,但可通过前端代码在打印前进行一些简单的页面处理,如隐藏不需要打印的元素。

function printPage() {
  window.print();
}

二、Web前端打印的进阶方案

(一)vue-print-nb插件

vue-print-nb是Vue项目中常用的打印插件之一,提供了便捷的指令式打印功能。

安装插件

npm install vue-print-nb --save

全局注册

import Print from 'vue-print-nb';
Vue.use(Print);

通过ID指定打印区域

<div id="printTest">
  <p>我是打印区域</p>
</div>
<button v-print="'#printTest'">打印</button>

通过配置对象指定打印区域及相关参数

<div id="printTest">
  <p>我是打印区域</p>
</div>
<button v-print="printTest">打印</button>
data() {
  return {
    printTest: {
      id: 'printTest', // 打印元素的id
      popTitle: '页眉标题' // 页眉标题
    }
  };
}

通过自定义方法打印指定打印区域及相关参数

插件未提供方法来调用打印功能,通过修改源码添加方法来扩展这一需求,以便更方便的使用。

在print.js中添加导出方法:

export function localPrint(options, global = this) {
  const binding = { value: options || {} }
  let id = ''
  let vue = global
  if (binding?.value?.clickMounted) {
    binding.value.clickMounted(vue)
  }
  if (typeof binding.value === 'string') {
    // 全局打印
    id = binding.value
  } else if (typeof binding.value === 'object' && !!binding.value.id) {
    // 局部打印
    id = binding.value.id
    let ids = id.replace(new RegExp("#", "g"), '')
    let elsdom = document.getElementById(ids)
    if (!elsdom) {
      console.log("id in Error")
      id = ''
    }
  }
  new Print({
    ids: id, // * 局部打印必传入id
    vue,
    url: binding.value.url, // 打印指定的网址,这里不能跟id共存 如果共存id的优先级会比较高
    standard: '', // 文档类型,默认是html5,可选 html5,loose,strict
    extraHead: binding.value.extraHead, // 附加在head标签上的额外标签,使用逗号分隔
    extraCss: binding.value.extraCss, // 额外的css连接,多个逗号分开
    previewTitle: binding.value.previewTitle || '打印预览', // 打印预览的标题
    zIndex: binding.value.zIndex || 20002, // 预览窗口的z-index
    previewPrintBtnLabel: binding.value.previewPrintBtnLabel || '打印', // 打印预览的标题
    popTitle: binding.value.popTitle || '', // title的标题
    preview: binding.value.preview || false, // 是否启动预览模式
    asyncUrl: binding.value.asyncUrl,
    previewBeforeOpenCallback () { // 预览窗口打开之前的callback
      binding.value.previewBeforeOpenCallback && binding.value.previewBeforeOpenCallback(vue)
    },
    previewOpenCallback () { // 预览窗口打开之后的callback
      binding.value.previewOpenCallback && binding.value.previewOpenCallback(vue)
    },
    openCallback () { // 调用打印之后的回调事件
      binding.value.openCallback && binding.value.openCallback(vue)
    },
    closeCallback () {
      binding.value.closeCallback && binding.value.closeCallback(vue)
    },
    beforeOpenCallback () {
      binding.value.beforeOpenCallback && binding.value.beforeOpenCallback(vue)
    }
  });
}

使用:

localPrint({
  id: 'printMe',
  // ...其它参数
})

(二)print.js插件

print.js是一款功能强大的JavaScript库,专门用于在网页中实现自定义打印功能。它能够轻松地打印HTML元素、图像以及PDF文件,并且提供了丰富的配置选项,让开发者可以灵活地控制打印的样式与内容。

安装插件

npm install print-js --save

打印HTML元素

<div id="printBill">
  <p>我是打印区域</p>
</div>
<el-button type="primary" @click="dayin">打印</el-button>
import printJS from 'print-js';

methods: {
  dayin() {
    printJS({
      printable: 'printBill', // 标签元素id
      type: 'html',
      header: '',
      targetStyles: ['*'],
      style: '' // 自定义样式
    });
  }
}

打印图像

printJS({
  printable: 'imageToPrint', // 图像元素id
  type: 'image'
});

打印PDF文件

printJS({
  printable: 'document.pdf', // PDF文件路径
  type: 'pdf'
});

(三)通过iframe实现打印

此方法适用于复杂打印需求,可完全自定义打印内容,不受页面现有布局和样式的限制。

布局打印内容页面

先单独完成一个页面,专门用于展示打印内容,布局和样式都根据打印需求进行设计。

在Vue页面中引入iframe

<iframe
  style="position: absolute; top: -1000px; left: -1000px; width: 100%"
  id="print"
  ref="print"
  :src="iframeSrc"
></iframe>

Vue中控制打印

<el-button type="primary" @click="dayin">打印</el-button>
methods: {
  dayin() {
    let iframe = document.getElementById('print');
    this.$refs.print.contentWindow.focus();
    this.$refs.print.contentWindow.print();
  }
}

iframe页面接收数据

若需从Vue页面向iframe页面传递数据,可在iframe页面启动时监听message事件。

window.addEventListener('message', (e) => {
  console.log(e);
  this.pType = e.data.info; // 接收Vue传来的数据
});

三、打印功能的高级应用

(一)分页打印

在打印报表等场景中,常需对内容进行分页处理。可通过前端代码将数据按页分割,再结合模板引擎(如Mustache)生成每页的HTML字符串,最后通过iframe进行打印。

数据处理

将接口返回的原始数据转换成分页数据结构,每页数据包含是否需要页头、页码、当页数据列表等信息。

const calculatePageNum = (serverDataList) => {
  const firstPageMaxNum = 36;
  const otherPageMaxNum = 40;

  const pageList = [];
  let currentPage = 0;
  serverDataList.forEach((item, index) => {
    const { dataId, dataName, dataNum } = item;
    currentPage = index < firstPageMaxNum ? 1 : 1 + Math.ceil((index + 1 - firstPageMaxNum) / otherPageMaxNum);

    if (!pageList[currentPage - 1]) {
      pageList[currentPage - 1] = {
        pageHead: currentPage === 1,
        pageNum: currentPage,
        list: [item]
      };
    } else {
      pageList[currentPage - 1].list.push(item);
    }
  });
  return pageList;
};

模板渲染

设计HTML模板,使用模板语法遍历分页数据,生成每页的HTML结构。

<body class="a4-body">
  {{#pageList}}
  <section class="a4-page">
    {{#pageHead}}
    <header class="head">
      <h2>{{pageTitle}}</h2>
    </header>
    {{/pageHead}}
    <table class="a4-table">
      <tr>
        <th>数据ID</th>
        <th>数据名称</th>
        <th>数据数量</th>
      </tr>
      {{#list}}
      <tr>
        <td>{{dataId}}</td>
        <td>{{dataName}}</td>
        <td>{{dataNum}}</td>
      </tr>
      {{/list}}
    </table>
  </section>
  <ul class="a4-footer">
    <li>第{{pageNum}}页 总{{pageList.length}}页</li>
  </ul>
  {{/pageList}}
</body>

CSS布局

通过CSS确保每页的高度固定,符合A4纸张尺寸,内部元素布局可根据实际需求调整。

.a4-body {
  width: 208mm;
  margin: 0 auto;
  text-align: center;
}

.a4-page {
  width: 100%;
  padding: 6mm;
  height: 288mm;
  margin: 0 auto;
  box-sizing: border-box;
}

.a4-footer {
  line-height: 9mm;
}

(二)静默打印

某些场景下,需实现点击按钮直接打印,无需弹出打印预览界面。这在纯前端环境下难以实现,但可通过开发打印App来达成。

打印App设计

使用Electron等框架开发PC端打印App,实现连接和管理打印机、与浏览器通信两大核心功能。

浏览器与打印App通信

打印App启用Socket Server服务,监听特定端口。浏览器端启动Websocket客户端,建立与打印App的连接。需打印时,浏览器通过Socket发送打印指令和内容,打印App接收后调用打印机完成打印。

四、常用的CSS打印属性

使用 page-break-beforepage-break-after 属性来插入分页符。这两个属性可以应用于需要分页的元素上,例如 <div><p><table> 等。可以设置的属性值有:

  • auto:自动决定是否插入分页符(默认值)。
  • always:始终插入分页符。
  • avoid:尽量避免插入分页符。
  • left:在元素之前插入分页符,并使下一页从左侧开始。
  • right:在元素之前插入分页符,并使下一页从右侧开始。
@media print { .page-break { page-break-before: always; } }

在需要分页的元素上添加相应的类名,例如 .page-break,以便在打印时插入分页符。

设置打印布局(横向、纵向、边距)

@media print {
    @page {
      /* 纵向 */
      size: portrait; 
 
      /* 横向 */
      size: landscape;
 
      /* 边距 上右下左 */
      margin: 1cm 2cm 1cm 2cm;
    }
  }

去除页眉页脚

@media print {
  @page {
    margin: 0;
  }
}

五、总结

Web前端打印虽看似简单,却涉及多种技术和方案。在项目中,可根据实际需求选择合适的打印方法。

需要注意的是,使用第三方组件库(如element-ui)时,因其实现实现方式,可能会出现样式问题,如表格超出打印页面、打印不全等。建议需要打印的内容最好是使用原生table和样式布局。

正文完
 0
Alexion
版权声明:本站原创文章,由 Alexion 于2025-01-15发表,共计5531字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)