vue.js

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript类库 > vue.js > vue打印指定组件内容

vue实现打印指定组件内容的示例详解

作者:不要止步于此

这篇文章主要和大家分享一下vue中打印指定组件内容,多页打印自动适配纸张大小打印的方案,文中的示例代码讲解详细,需要的可以参考一下

前言

大家好,最近在做vue项目时使用到了print-js打印指定组件内容,并且还得切换纸张大小时做到自动适配布局的需求,其中遇到了一些问题及我的解题思路想给大家分享一下,希望日后能帮助到大家。

背景

大致需求是在页面中提供一个多选框组,可以从中选择打印哪些内容,每个勾选项打印格式都是一样的,不同的是每个勾选项独占一页,且响应式适配纸张大小,大致效果如下图所示:

了解需求后,我们开始做开发准备工作!

引入print-js,并封装打印函数

npm install print-js@1.3.1

usePrint.js

import printJS from 'print-js'

export const usePrint = () => {
  printJS({
    // 需要打印区域设置的Id
    printable: 'print-area',
    // 打印类型
    type: 'html',
    // 默认值为800,我们把把设置为100%
    maxWidth: '100%', 
    // *代表应用所有样式,默认值为null,如果不设置,打印窗口则会忽略所有样式
    targetStyles: ['*'],
  });

上面主要注意点是注意点是修改maxWidth的默认值及设置targetStyles应用组件所写的样式。想要了解更多可参考:print-js文档

封装打印选择器及打印内容组件

引入插件后,我们先把打印的组件结构先写好,下面先把打印内容选择器结构写好

PrintSelector.vue

<template>
  <div>
    <!-- 这里是我们封装的打印组件 -->
    <PrintArea :data="printData" />
    <el-checkbox :value="checkAll" @change="onCheckAllChange">全选</el-checkbox>
    <div style="margin: 15px 0;"></div>
    <el-checkbox-group v-model="checkedItems" @change="onCheckedChange">
      <el-checkbox v-for="item in options" :label="item.id" :key="item.id">{{item.label}}</el-checkbox>
    </el-checkbox-group>
    <div style="margin: 15px 0;"></div>
    <el-button @click="onPrint">打印</el-button>
  </div>
</template>

<script>
import { usePrint } from './hooks/usePrint'
import PrintArea from './PrintArea.vue'
export default {
  components: {
    PrintArea
  },
  data() {
    return {
      checkedItems: [],
      options: [
        { id: 1, label: '项目1' },
        { id: 2, label: '项目2' },
        { id: 3, label: '项目3' },
      ]
    }
  },
  computed: {
    checkAll() {
      return this.checkedItems.length === this.options.length;
    },
    printData() {
      return this.options.filter(item => this.checkedItems.includes(item.id))
    }
  },
  methods: {
    onCheckAllChange() {
      if(this.checkAll) {
        this.checkedItems = []
      } else {
        this.checkedItems = this.options.map(item => item.id)
      }
    },
    onCheckedChange(val) {
      this.checkedItems = val
    },
    onPrint() {
      usePrint()
    }
  },
}
</script>

注意,上面我们把需要打印的组件PrintArea引入到了PrintSelector组件中,因为在调用printJS方法时需要通过id获取页面dom元素,所以需要组件的dom元素渲染到页面中来,但是我们又不希望页面中显示打印的内容,这里可以通过设置display: none把元素隐藏起来。但,隐藏后有一个需要注意的点,我们先把PrintArea组件写出来,后面再进行讲解。

PrintArea.vue

<template>
  <div id="print-area" v-show="false">
    <div class="item" v-for="item in data" :key="item.id">
      <div class="head">头部</div>
      <div class="main">
        <span class="text">{{ item.label }}</span>
      </div>
      <div class="footer">底部</div>
    </div>
  </div>
</template>
<script>
export default {
  props: {
    data: Array
  }
}
</script>

这个组件就是我们需要打印的内容,外部勾选需要打印的内容后传入当前组件即可,每一个item代表着一页。我们回到前文提到的设置display:none时的问题,我们给idprint-area打印内容跟标签隐藏了起来,如果这时选择项目点击打印后,会发现打印预览窗口为空白!!!

其实问题就是出现给打印区域设置了display:none,所以打印预览窗口也把内容隐藏了,解决办法也简单,就是给打印区域再套一层div,把v-show="false"设置在外层div上即可。

<template>
  <div v-show="false">
    <div id="print-area">
      ...省略其中代码
    </div>
  </div>
</template>

这样子打印窗口就能正常显示内容了。

我们下面通过一个小例子模拟打印预览讲解一下这个问题出现的原因:

demo.html

<body>
  <div class="wrapper">
    <!-- 打印区域 -->
    <div id="print-area" style="display: none;">
      <div class="item">111</div>
      <div class="item">222</div>
    </div>
  </div>
  <!-- 模拟打印预览窗口 -->
  <div id="print-window"></div>
  <script>
    window.onload = function() {
      const printArea = document.getElementById('print-area')
      const printWindow = document.getElementById('print-window')
      // 把打印内容放入打印预览窗口
      printWindow.appendChild(printArea)
    }
  </script>
</body>

上面我们实现了一个简单的模拟打印流程,在打印区域的div上设置了display: none,所以调用appendChild放入模拟的打印窗口div下时,页面并没有内容显示,因为print-area样式还是设置为隐藏。所以我们通过在外面增加一层用来设置隐藏样式的div。这样,我们获取idprint-areadom元素后,当前元素上没有设置隐藏样式,所以appendChild放到打印预览窗口,就不会隐藏打印的内容了!

页面布局设计

我们在PrintArea.vue文件中先把样式补上,看下效果

<style lang="scss" scoped>
#print-area {
  background-color: deeppink;
  .item {
    display: flex;
    flex-direction: column;
    background-color: bisque;
    .head {
      text-align: center;
      line-height: 100px;
      font-weight: bold;
      font-size: 36px;
      background-color: azure;
    }
    .main {
      flex: 1;
      display: flex;
      justify-content: center;
      align-items: center;
    }
    .footer {
      text-align: center;
      line-height: 100px;
      font-weight: bold;
      font-size: 20px;
      background-color: #eee;
    }
  }
}
</style>

我们的需求是根据每个item进行分页打印,分页实现可以直接在.item下设置page-break-after: always;样式即可,但是还有一个需求是怎样能够让item响应式适应纸张大小,占满整个区域呢?第一想到的就是height: 100%,但,这样就得保证每一层父组件的高度为页面文档的高度100%。好像不太理想,那设置每一页item的高度为100vh呢?下图可看出也并没有达到想要的效果:

解决办法是让打印区域脱离文档流,设置一个绝对定位

#print-area {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  .item {
    height: 100%;
  }
}

上面我们巧用绝对定位的上下左右距离都为0,使容器能撑满整个纸张大小,最后给每一页item继承100%高度,这样子布局就能够适应各种纸张大小了,即使切换纸张也能够达到撑满整个容器的效果。

总结

以上我们主要是通过在打印内容区域外设置v-show="false"来隐藏元素,使打印内容不受隐藏影响,并通过一个小demo演示了问题出现的原因;接着我们通过在每个item下设置page-break-after: always;实现分页;最后通过绝对定位使容器撑满整个纸张,以达到切换纸张大小时也能撑满整个容器的效果。

到此这篇关于vue实现打印指定组件内容的示例详解的文章就介绍到这了,更多相关vue打印指定组件内容内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文