vue-pdf實現預覽pdf並使用C-Lodop實現列印功能

来源:https://www.cnblogs.com/feng-xl/archive/2022/09/16/16700052.html
-Advertisement-
Play Games

本人的工作項目中,需求是: 點擊“列印”按鈕,打開pdf預覽彈出框,彈出框有:頭部選擇列印模板、列印方式、印表機,都是下拉選擇框;中部是pdf預覽塊;底部是確定列印。 準備工作: 預覽pdf,後端介面返回了pdf預覽地址,可線上直接打開。vue-pdf插件可以滿足需求。 選擇方式如果選擇本地列印,下 ...


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

  點擊“列印”按鈕,打開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/[email protected]/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
    }

  完畢。歡迎指出。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ​ 回顧數據倉庫的發展歷程,大致可以將其分為幾個階段:萌芽探索到全企業集成時代、企業數據集成時代、混亂時代--"數據倉庫之父"間的論戰、理論模型確認時代以及數據倉庫產品百家爭鳴時代。 數據倉庫理論發展歷程 上世紀70年代,IBM的E.F.Codd等人提出關係型資料庫後,MIT的研究員提出單獨構建分析 ...
  • redis集群的搭建 redis集群的三種模式 主從複製 哨兵模式 cluster集群 redis-cluster集群的搭建(在一臺linux中搭建,三主三從) 下載安裝redis5.0.3 cd /opt 下載redis安裝包 wget http://download.redis.io/relea ...
  • Appuploader可以輔助在Windows、linux或mac系統直接申請iOS證書p12,及上傳ipa到App Store,最方便在Windows開發上架沒有蘋果Mac電腦的開發者!配合本教程使用,可以快速掌握如何真機測試及上架! 點擊蘋果證書 按鈕 點擊新增 ​ 輸入證書密碼,名稱 這個密碼 ...
  • 移動端中的元素內容超出時,對容器設置overflow-x: auto就可以通過手勢水平移動。但是 PC 端只能通過滑鼠滾輪上下滑動,而不能水平移動。 只需要給元素添加一個監聽滑鼠滾輪事件,上下滑動時修改其 scrollLeft 屬性值就可以實現。直接貼上代碼: <div class="horizon ...
  • 好家伙, 我的飛機大戰部署上線了 胖虎的飛機大戰 感興趣的可以去玩一下 (怕有人接受不了這個背景,我還貼心的準備切換背景按鈕,然而這並沒有什麼用) 現在,我們停下腳步,重新審視這個游戲 現在基本的框架都弄出來了,敵機,英雄,子彈,分數,生命 但是,這個“游戲“有個非常致命的問題, 他不好玩,(不好玩 ...
  • 本文分別使用 SFC(模板方式)和 tsx 方式對 Element Plus *el-menu* 組件進行二次封裝,實現配置化的菜單,有了配置化的菜單,後續便可以根據路由動態渲染菜單。 ...
  • vue3中,新增了 defineComponent ,它並沒有實現任何的邏輯,只是把接收的 Object 直接返回,它的存在是完全讓傳入的整個對象獲得對應的類型,它的存在就是完全為了服務 TypeScript 而存在的。 我都知道普通的組件就是一個普通的對象,既然是一個普通的對象,那自然就不會獲得自 ...
  • 這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 一、前言 入職的第一個需求是跟著一位前端大佬一起完成的一個活動項目。 由於是一起開發,當然不會放過閱讀大佬的代碼的機會。 因為我的頁面中需要使用到倒計時功能,發現大佬的已經寫了個現成的倒計時組件,於是直接就拿過來用了。 傳個參數就實現了功 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 在我們開發過程中基本上不可或缺的用到一些敏感機密數據,比如SQL伺服器的連接串或者是OAuth2的Secret等,這些敏感數據在代碼中是不太安全的,我們不應該在源代碼中存儲密碼和其他的敏感數據,一種推薦的方式是通過Asp.Net Core的機密管理器。 機密管理器 在 ASP.NET Core ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 順序棧的介面程式 目錄順序棧的介面程式頭文件創建順序棧入棧出棧利用棧將10進位轉16進位數驗證 頭文件 #include <stdio.h> #include <stdbool.h> #include <stdlib.h> 創建順序棧 // 指的是順序棧中的元素的數據類型,用戶可以根據需要進行修改 ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • C總結與剖析:關鍵字篇 -- <<C語言深度解剖>> 目錄C總結與剖析:關鍵字篇 -- <<C語言深度解剖>>程式的本質:二進位文件變數1.變數:記憶體上的某個位置開闢的空間2.變數的初始化3.為什麼要有變數4.局部變數與全局變數5.變數的大小由類型決定6.任何一個變數,記憶體賦值都是從低地址開始往高地 ...
  • 如果讓你來做一個有狀態流式應用的故障恢復,你會如何來做呢? 單機和多機會遇到什麼不同的問題? Flink Checkpoint 是做什麼用的?原理是什麼? ...
  • C++ 多級繼承 多級繼承是一種面向對象編程(OOP)特性,允許一個類從多個基類繼承屬性和方法。它使代碼更易於組織和維護,並促進代碼重用。 多級繼承的語法 在 C++ 中,使用 : 符號來指定繼承關係。多級繼承的語法如下: class DerivedClass : public BaseClass1 ...
  • 前言 什麼是SpringCloud? Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的開發便利性簡化了分散式系統的開發,比如服務註冊、服務發現、網關、路由、鏈路追蹤等。Spring Cloud 並不是重覆造輪子,而是將市面上開發得比較好的模塊集成進去,進行封裝,從 ...
  • class_template 類模板和函數模板的定義和使用類似,我們已經進行了介紹。有時,有兩個或多個類,其功能是相同的,僅僅是數據類型不同。類模板用於實現類所需數據的類型參數化 template<class NameType, class AgeType> class Person { publi ...
  • 目錄system v IPC簡介共用記憶體需要用到的函數介面shmget函數--獲取對象IDshmat函數--獲得映射空間shmctl函數--釋放資源共用記憶體實現思路註意 system v IPC簡介 消息隊列、共用記憶體和信號量統稱為system v IPC(進程間通信機制),V是羅馬數字5,是UNI ...