本人的工作项目中,需求是:
点击“打印”按钮,打开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
}
完毕。欢迎指出。
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po