本人的工作项目中,需求是:

  点击“打印”按钮,打开pdf预览弹出框,弹出框有:头部选择打印模板、打印方式、打印机,都是下拉选择框;中部是pdf预览块;底部是确定打印。

 

准备工作:

  预览pdf,后端接口返回了pdf预览地址,可在线直接打开。vue-pdf插件可以满足需求。

  选择方式如果选择本地打印,下拉列表是该电脑所连接的所有打印设备,且连接打印机打印出pdf内容。我司花钱购买了C-Lodop商用产品,用这个打印控件可以满足需求。官方地址:http://www.lodop.net/

 

直接上关键代码,温馨提示:预览pdf在最后

  一、由于项目中许多地方会用到该打印预览弹框,封装一个print.vue组件文件,核心代码:

  1、打印机下拉资源获取:

    首先是:LodopFuncs.js文件,官网可以下载,根据本人工作需求情况,做了一些改动。

//= =本JS是加载Lodop插件或Web打印服务CLodop/Lodop7的综合示例,可直接使用,建议理解后融入自己程序==
// 资料链接:http://www.lodop.net/

import message from 'ant-design-vue/es/message'
import modal from 'ant-design-vue/es/modal'

let CLodopIsLocal, CLodopJsState

//= =判断是否需要CLodop(那些不支持插件的浏览器):==
function needCLodop () {
  try {
    const ua = navigator.userAgent
    if (ua.match(/Windows\sPhone/i)) return true
    if (ua.match(/iPhone|iPod|iPad/i)) return true
    if (ua.match(/Android/i)) return true
    if (ua.match(/Edge\D?\d+/i)) return true

    const verTrident = ua.match(/Trident\D?\d+/i)
    const verIE = ua.match(/MSIE\D?\d+/i)
    let verOPR = ua.match(/OPR\D?\d+/i)
    let verFF = ua.match(/Firefox\D?\d+/i)
    const x64 = ua.match(/x64/i)
    if ((!verTrident) && (!verIE) && (x64)) return true
    else if (verFF) {
      verFF = verFF[0].match(/\d+/)
      if ((verFF[0] >= 41) || (x64)) return true
    } else if (verOPR) {
      verOPR = verOPR[0].match(/\d+/)
      if (verOPR[0] >= 32) return true
    } else if ((!verTrident) && (!verIE)) {
      let verChrome = ua.match(/Chrome\D?\d+/i)
      if (verChrome) {
        verChrome = verChrome[0].match(/\d+/)
        if (verChrome[0] >= 41) return true
      }
    }
    return false
  } catch (err) {
    return true
  }
}

// 加载CLodop时用双端口(http是8000/18000,而https是8443/8444)以防其中某端口被占,
// 主JS文件名“CLodopfuncs.js”是固定名称,其内容是动态的,与其链接的打印环境有关:
function loadCLodop () {
  if (CLodopJsState === 'loading' || CLodopJsState === 'complete') return
  CLodopJsState = 'loading'
  const head = document.head || document.getElementsByTagName('head')[0] || document.documentElement
  const JS1 = document.createElement('script')
  const JS2 = document.createElement('script')
  console.log(window.location.protocol === 'https:', 'https')

  // if (window.location.protocol === 'https:') {
  //   JS1.src = 'https://localhost.lodop.net:8443/CLodopfuncs.js'
  //   JS2.src = 'https://localhost.lodop.net:8444/CLodopfuncs.js'
  // } else {
  JS1.src = 'http://localhost:8000/CLodopfuncs.js'
  JS2.src = 'http://localhost:18000/CLodopfuncs.js?priority=1'
  // }
  JS1.onload = JS2.onload = function () { CLodopJsState = 'complete' }
  JS1.onerror = JS2.onerror = function (evt) { CLodopJsState = 'complete' }
  head.insertBefore(JS1, head.firstChild)
  head.insertBefore(JS2, head.firstChild)
  CLodopIsLocal = !!((JS1.src + JS2.src).match(/\/\/localho|\/\/127.0.0./i))
}

/**
 * @description: 提示下载
 * @param {String} href 下载地址
 * @return {*}
 */
function modalOfDownload (href) {
  const content = '打印控件需要升级!点击【确定】下载并执行安装'
  modal.confirm({
    content,
    onOk () {
      const a = document.createElement('a')
      a.href = 'https://test-xxxxxxxxx.cos.ap-xxx.xxx.com/kits/' + href
      document.body.appendChild(a)
      a.click()
      a.remove()
    }
  })
}

/**
 * @description: 获取LODOP对象主过程,判断是否安装、需否升级
 * @return {Object} { state: 3 , LODOP,   message: '' } state: 1未安装、2未运行、3具备打印条件
 */
function getLodop () {
  let LODOP

  try {
    if (needCLodop()) {
      try {
        LODOP = window.getCLodop()
      } catch (err) {}

      console.log('CLodopJsState', CLodopJsState)
      if (!LODOP && CLodopJsState !== 'complete') {
        if (CLodopJsState === 'loading') {
          message.info('网页还没下载完毕,请稍等一下再操作.')
        } else {
          message.info('未曾加载Lodop主JS文件,请先调用loadCLodop过程.')
        }
        return { state: 0 }
      }

      // 不存在则提示安装
      if (!LODOP) {
        if (CLodopIsLocal) {
          return { state: 2, message: '此前已安装过,点击【运行】直接再次启动' }
        } else {
          return { state: 1, message: 'Web打印服务CLodop未安装启动,点击下载执行并安装' }
        }
      } else {
        // 判断版本
        const isWinIE = (/MSIE/i.test(navigator.userAgent)) || (/Trident/i.test(navigator.userAgent))
        const isWinIE64 = isWinIE && (/x64/i.test(navigator.userAgent))
        if (window.CLODOP.CVERSION < '4.1.5.5') {
          const exeHref = isWinIE64 ? 'install_lodop64.exe' : 'install_lodop32.exe'
          modalOfDownload(exeHref)
          return { state: 0 }
        }
      }
    }

    if (LODOP) {
      //= ==如下空白位置适合调用统一功能(如注册语句、语言选择等):=======================
      LODOP.SET_LICENSES('', '7***************9', '', '')
      console.log('SET_LICENSES执⾏了')

      // LODOP.SET_LICENSES('', '13*******39', 'ED*******10', 'D6**********8')

      //= ==============================================================================
      return { state: 3, LODOP, message: '具备打印条件' }
    }
  } catch (err) {
    console.error('getLodop出错:' + err)
  }
}

if (needCLodop()) { loadCLodop() } // 开始加载
export { getLodop }

  项目中引入:

import { getLodop } from '@/utils/LodopFuncs'

  methods方法中使用:

  /**
     * @description: 获取本地已连接打印机
     */
    getLocalPrinter () {
      const lop = getLodop()
      console.log('lop对象', lop)

      if (!lop.state) return

      if ([1, 2].includes(lop.state)) {
        this.initPrinterOptions()
        this.showPlugin = true
        return
      }

      this.LODOP = lop.LODOP
      const counter = this.LODOP.GET_PRINTER_COUNT() // 获取打印机个数
      console.log(counter, '获取打印机个数')

      var printNameList = []
      for (let i = 0; i < counter; i++) {
        const printerName = this.LODOP.GET_PRINTER_NAME(i)
        printNameList.push({ printerName, id: printerName })
      }
      this.printerOptions = JSON.parse(JSON.stringify(printNameList)) // 打印机下拉列表资源
    }

  2、连接打印机,打印出pdf在线地址的内容:

    (1)上面的打印机资源完成赋值以后,下拉选择你需要打印的打印机,并记住选择的id。

    (2)点击弹框的确定,开始打印,执行方法如下:

  /**
     * @description: 本地打印
     */
    doLocalPrint () {
      console.log(this.LODOP.PRINT_INIT, 'lodop是否运行')
      if (!this.LODOP.PRINT_INIT) {
        // 考虑中途卸载或者停用的情况,再提示打印插件下载,弹出插件下载提示框
        this.initPrinterOptions()
        this.showPlugin = true
        return
      }

      // 开启懒加载
      this.loading.confirm = true
      this.$message.loading('准备打印中...', 0)

      // 打印初始化配置
      this.LODOP.PRINT_INIT(`${this.title}_${dateUtils.format('yyyyMMdd')}`)
      this.LODOP.SET_PRINTER_INDEX(this.dataForm.printerId)

      // 循环打印 【我们项目存在批量打印,所以需要一张张加进去再打印】
      for (let i = 0; i < this.printList.length; i++) {
        const pdfIm = this.printList[i]
        const pageWidth = pdfIm.pageWidth ? Math.floor(pdfIm.pageWidth / 1.1756) : 2050
        console.log(pageWidth, '本地打印宽度')
        this.LODOP.SET_PRINT_PAGESIZE(1, pageWidth, pdfIm.pageHeight) // http://www.lodop.net/demolist/PrintSample5.html
        this.LODOP.ADD_PRINT_PDF(0, 0, '100%', '100%', this.demoDownloadPDF(pdfIm.urls)) // (Top,Left,Width,Height,strURLorContent) 上边距/左边距/
        // this.LODOP.ADD_PRINT_HTM(0, '5mm') // http://www.lodop.net/demolist/PrintSample46.html
        this.LODOP.PRINT() // 执行打印
      }

      this.$message.success('单据打印中...')
      // 关闭处理
      this.loading.confirm = false
    },

  demoDownloadPDF (url) {
      if (!/^https?:/i.test(url)) return
      let xhr = null
      if (window.XMLHttpRequest) xhr = new XMLHttpRequest()
      else xhr = new window.ActiveXObject('MSXML2.XMLHTTP')
      xhr.open('GET', url, false) // 同步方式
      if (xhr.overrideMimeType) {
        try {
          xhr.responseType = 'arraybuffer'
          var arrybuffer = true
        } catch (err) {
          xhr.overrideMimeType('text/plain; charset=x-user-defined')
        }
      }
      xhr.send(null)
      var data = xhr.response || xhr.responseBody
      let dataArray = null
      if (typeof Uint8Array !== 'undefined') {
        if (arrybuffer) dataArray = new Uint8Array(data)
        else {
          dataArray = new Uint8Array(data.length)
          for (var i = 0; i < dataArray.length; i++) {
            dataArray[i] = data.charCodeAt(i)
          }
        }
      } else dataArray = window.VBS_BinaryToArray(data).toArray() // 兼容IE低版本
      return this.demoGetBASE64(dataArray)
    },
    demoGetBASE64 (dataArray) {
      var digits = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='
      var strData = ''
      for (var i = 0, ii = dataArray.length; i < ii; i += 3) {
        if (isNaN(dataArray[i])) break
        var b1 = dataArray[i] & 0xff
        var b2 = dataArray[i + 1] & 0xff
        var b3 = dataArray[i + 2] & 0xff
        var d1 = b1 >> 2
        var d2 = ((b1 & 3) << 4) | (b2 >> 4)
        var d3 = i + 1 < ii ? ((b2 & 0xf) << 2) | (b3 >> 6) : 64
        var d4 = i + 2 < ii ? b3 & 0x3f : 64
        strData +=
          digits.substring(d1, d1 + 1) +
          digits.substring(d2, d2 + 1) +
          digits.substring(d3, d3 + 1) +
          digits.substring(d4, d4 + 1)
      }
      return strData
    }

  3、vue-pdf预览:

    (1)页面元素核心代码:

    <!-- pdf预览 -->
      <div class="preview" v-loading="loading.preview">
        <template v-if="pdfList.length">
          <template v-for="pdfItem in pdfList">
            <pdf v-for="(pPage, pIdx) in pdfItem.numPages" :key="pIdx + pdfItem.id" :page="pPage" :src="pdfItem.src" />
          </template>
        </template>
        <empty v-else text="暂无预览结果" />
      </div>

    (2)script的data部分:

      导入:

import pdf from 'vue-pdf'

      关键变量:

printList: [], // 打印的数据列表
pdfList: [], // pdf文件列表

    (3)拿到后端返回的数据后,转化为vue-pdf插件可用数据:

// 请求数据api
const { res } = await this.$caputured(this.$api[this.config.apiName], params)
 if (res) {
    this.printList = res.data
    this.setPdfSrc(res.data)
 } else {
    this.pdfList = [] // 清除pdf列表
    this.printList = [] // 清除pdf列表
 }
   /**
     * @description: 设置pdf
     */
    async setPdfSrc (pdfUrls) {
      const list = []
      for (let i = 0; i < pdfUrls.length; i++) {
        const loadingTask = pdf.createLoadingTask({
          url: pdfUrls[i].urls, // pdf地址
          cMapUrl: 'https://cdn.jsdelivr.net/npm/pdfjs-dist@2.2.228/cmaps/', // 加载字体包
          cMapPacked: true
        })
        await loadingTask.promise.then((p) => {
          list.push({
            id: pdfUrls[i].id,
            src: loadingTask,
            numPages: p.numPages
          })
        })
      }
      this.pdfList = list
    }

  4、其他情况,弹出的提示框按钮需求,有运行c-lodop和下载:

<a-button class="margin-right-8" @click="loadClodop">运行</a-button>
<a-button type="primary" @click="downloadClodop">下载并安装</a-button>

    /**
     * @description: 运行
     */
    loadClodop () {
      // 查看本机是否安装(控件或web打印服务)
      const lop = getLodop()
      if (lop.state === 1) this.$message.warn(lop.message, 3)
      else if (lop.state === 2) {
        // 手动触发运行
        const a = document.createElement('a')
        a.href = 'CLodop.protocol:setup'
        a.target = '_self'
        document.body.appendChild(a)
        a.click()
        a.remove()
        this.showPlugin = false
      }
    },


    /**
     * @description: 下载
     */
    downloadClodop () {
      const a = document.createElement('a')
      a.href = '下载地址'
      document.body.appendChild(a)
      a.click()
      this.showPlugin = false
    }

  完毕。欢迎指出。