vue3+ts实现打印以及封装打印hooks

最近在写vue3项目的时候碰到一些重复的需求,也就是后台管理系统的打印模块,打印与后端沟通,返回的是html格式,前端通过v-html解析即可

1.插件选择:这里打印插件选择了print-js用来做打印,因为该插件可以解析html,img等格式同时支持局部打印(针对DOM的id属性等),所以选择这个,打印效果如下

 

2.打印公共组件封装

         2.1先看后端返回数据格式:

        

        2.2可以看到是一个html数组,所以打印组件使用v-html解析时应该是个数组 ,所以组件封装如下

// 打印公共组件
<template>
  <div id="print_area" class="print-area">
    <div v-for="(item, index) in htmlArrays" :key="index" v-html="item" style="page-break-after: always"></div>
  </div>
</template>

<script setup lang="ts">
import { computed, nextTick } from 'vue'
import print from 'print-js'
const _props = defineProps(['htmlArrays'])
const _emits = defineEmits(['printDialogChange'])
const htmlArrays = computed(() => _props.htmlArrays)

// page-break-after: always 此样式用于打印分页

// 打开此样式可取消页眉页脚
// const style = '@page {margin:0 10mm};'

const toPrint = () => {
  nextTick(() => {
    print({
      printable: 'print_area',
      type: 'html',
      // style,
      scanStyles: false,
      // 浏览器打印对话框关闭钩子
      onPrintDialogClose() {
        _emits('printDialogChange')
      },
      // PDF加载后钩子
      onLoadingEnd() {
        _emits('printDialogChange')
      },
    })
  })
}
defineExpose({ toPrint })
</script>

<style scoped lang="less">
.print-area {
  display: none;
  position: fixed;
  left: -1000px;
  top: -1000px;
}
</style>

       2.3 父组件在调用,只需要请求到数据使用props传给子组件,调用子组件打印方法即可唤起浏览器预览打印     

// 使用组件
<receivePrint :htmlArrays="htmlArrays" ref="printDom" />

// 引入组件
import receivePrint from '@/components/content-print/index.vue'

const printDom = ref() // 打印dom
const htmlArrays = ref<string[]>([]) // 打印数组


 /**
 * @method 给指定字符串的指定位置插入指定字符串
 * @param source 要插入的字符串
 * @param start 开始位置
 * @param newStr 插入的字符串
 * @returns 返回插入后的源字符串
 */
const insertStr = (source: string, start: number, newStr: string): string => {
  return source.slice(0, start) + newStr + source.slice(start)
}
 

// 从后端请求到的数据
const handlePrint = async (type: 'single' | 'all', record?: any) => {
  const params = { wahousingPlanDetailId: goodsState.value['id'], wahousingPlanId: goodsState.value['warehousingPlanId'], items: [] as any[] }
  let printItems: Record<string, string | number>
  if (type === 'single') {
    printItems = { shJs: record.total, shSl: `${record.total}件`, trackCode: record.trackingNumber, pieceWeight: record.pieceWeight }
    params['items'].push(printItems)
  }
  if (type === 'all') {
    contentTableParam.dataSource.forEach((el: { total: number; trackingNumber: string; pieceWeight: number }) => {
      printItems = { shJs: el.total, shSl: `${el.total}件`, trackCode: el.trackingNumber, pieceWeight: el.pieceWeight }
      params['items'].push(printItems)
    })
  }
// 此处请求后端数据,上面不用看
  const { Success, ResultCode, Tag } = await globalProperties.$api.receiving_api.receivingPrintByteArray(params)
  if (Success && ResultCode === 200) {
    Tag.forEach((v: string) => {
      // 给每一个html插入margin:0的样式
      v = insertStr(v, -14, '<style>body{margin:0}</style>')
    })
    htmlArrays.value = Tag
    printDom.value.toPrint()
  }
}

3.由于后续业务庞大,打印的模块很多,索性将打印封装成了业务hooks

        3.1在src/hooks目录下新建usePrint.ts文件 

        

        3.2 封装时要考虑hooks的健壮性以及拓展性,所以子组件的ref,htmlArrays,都可定义到hooks里最后return出去

        3.3代码如下:

                3.3.1:可以看到handlePrint函数需要我们传入2个参数,一个是请求的钩子,一个是请求参数,此函数会自动发起请求并唤醒子组件的打印


import { ref, getCurrentInstance } from 'vue'
/**
 * @method 打印hooks
 * @returns {*}
 */
export default function usePrint(): any {
  const print = ref(null) // 下拉框选中值,用来指定templateId
  const printDom = ref() // 打印Ref绑定dom
  const printOptions = ref([]) // 打印下拉选项
  const htmlArrays = ref<string[]>([]) // 后端返回的html数组
  const {
    appContext: {
      config: { globalProperties },
    },
  }: any = getCurrentInstance()

  /**
   * @method 打印模版
   * @param type 打印模版选项
   * @returns Promise<void>
   */
  async function initPrintOptions(type: string) {
    const { Success, Tag } = await globalProperties.$api.goDownPlanList_api.printTemplateGetTemplateList({ type })
    if (Success) {
      printOptions.value = Tag
    }
  }

  /**
   * @method 下拉选择打印
   * @param cb 请求回调函数
   * @param params 请求参数
   * @returns Promise<void>
   */
  async function handlePrint(cb: (arg: Record<string, any>) => Promise<{ Success: boolean; Tag: string[] }> | { Success: boolean; Tag: string[] }, params: Record<string, any>) {
    if (!print.value) {
      globalProperties.$message.error('请先选择打印模板')
      return
    }
    const { Success, Tag } = await cb(params)
    if (Success) {
      htmlArrays.value = Tag
      printDom.value.toPrint()
    }
  }

  /**
   * @method 托盘单条/多条打印
   * @param cb 打印请求回调
   * @param params 请求参数
   * @returns Promise<void>
   */
  async function labelPrint(cb: (arg: Record<string, any>) => Promise<{ Success: boolean; Tag: string[] }> | { Success: boolean; Tag: string[] }, params: Record<string, any>) {
    const { Success, Tag } = await cb(params)
    if (Success) {
      htmlArrays.value = Tag
      printDom.value.toPrint()
    }
  }

  /**
   * @method 打印dialog弹出或关闭
   * @returns void
   */
  function printDialogChange() {
    print.value = null
  }

  return {
    print,
    printDom,
    htmlArrays,
    printOptions,
    handlePrint,
    initPrintOptions,
    printDialogChange,
    labelPrint,
  }
}

                        3.3.2 父组件代码可更新为

  <godownPlanPrint :htmlArrays="htmlArrays" ref="printDom"  />

import usePrint from '@/hooks/usePrint'

// 把打印所需操作从hooks解构出来,因为此模块需要其他方法,就一起解构出来了
const { htmlArrays, handlePrint, initPrintOptions, printDialogChange, printOptions, print, printDom } = usePrint()
// 普通模块只需要关注handlePrint函数即可

//调用接口时只需要执行以下代码 
const params = {
 linkIds: contentTableParam.selectedRowKeys,
 templateId: print.value,
  }

//调用hooks打印,传入请求的钩子与请求参数即可实现打印
 handlePrint(globalProperties.$api.goDownPlanList_api.warehousingPlanPrint, params)

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值