vue-pdf实现预览pdf并使用C-Lodop实现打印功能
本人的工作项目中,需求是:
点击“打印”按钮,打开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 }
完毕。欢迎指出。