发布时间:2023-02-22 14:30
实现一款交互屏桌面应用软件,类似医院那张种给用户操作办理业务的应用程序。操作业务中在充值后可供用户打印小票。
开发框架:electron-vue
vue版本:v2.6.14
electron版本:v17.2.0
node版本:v16.13.0
实现流程:
ps:提一下主结构的版本,是因为在electron中很多情况下出现各种问题是由版本问题导致的。
在主进程窗口,通过webContents对象的getPrintersAsync()方法,获取到系统打印机列表,在主进程中定义监听对象,供渲染进程监听
ipcMain.on('getPrinterList', async (event) => {
const list = await mainWindow.webContents.getPrintersAsync();
mainWindow.webContents.send('getPrinterList', list);
});
在渲染进程中通过ipcRender,来触发监听事件,通过once来处理返回的打印机数据。
import { ipcRenderer } from "electron";
...
methods:{
handlePrinter() {
ipcRenderer.send("getPrinterList");
ipcRenderer.once("getPrinterList", (event, data) => {
console.log(data);
data.forEach((item) => {
if (item.isDefault) {
this.printerName = item.name;
}
});
console.log("this.printerName: ", this.printerName);
this.printRender();
});
},
}
定义webview,在src中绑定打印的页面index.html,我们定义这个页面到static这个静态文件目录下面,因为这个页面的文件不会被打包导致打印失败。需要值得注意的是,得给webview添加2个属性:
nodeintegration
webpreferences="contextIsolation=no"
<webview
id="printWebview"
ref="printWebview"
:src="fullPath"
nodeintegration
webpreferences="contextIsolation=no"
style="width: 80mm; visibility: hidden"
/>
在index.html页面中我们定义一个监听事件,来监听是否触发打印,并将需求打印的内容填充到index.html页面中。完成后,通过sendToHost方法,发送一个消息webview-print-do来通知打印。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<style scoped>
body,
html {
padding: 0;
margin: 0;
font-size: 12px;
}
@page {
margin: 0px;
}
ul,li{
list-style: none;
}
ul{
padding-bottom: 40px;
}
.title{
text-align: center;
}
li{
margin-top: 10px;
}
li>span:first-of-type{
display: inline-block;
text-align: left;
}
.div1 {
overflow: hidden;
}
</style>
</head>
<body id="bd"></body>
<script type="module">
const { ipcRenderer } = require('electron')
window.onload = () => {
ipcRenderer.on('webview-print-render', (event, info) => {
console.log(event,info);
// 执行渲染
document.getElementById('bd').innerHTML = info.html
ipcRenderer.sendToHost('webview-print-do')
})
}
</script>
</html>
printRender() {
this.messageBox = this.$message({
type:'warning',
message: "打印中,请稍后",
duration: 0,
});
// 获取<webview>节点
const webview = this.$refs.printWebview;
console.log(webview);
webview.send("webview-print-render", {
printName: this.printerName,
html:
"<ul>" +
'<li class="title">欢迎光临浩森体育场馆</li>' +
"<li><span>会员名:</span><span>" +
this.taxInfo.customerName +
"</span></li>" +
"<li><span>会员手机号:</span><span>" +
this.taxInfo.customerPhone +
"</span></li>" +
"<li><span>消费类型:</span><span>"+ this.tradeTypeName +"</span></li>" +
"<li><span>订单创建时间:</span><span>" +
this.taxInfo.createTime +
"</span></li>" +
"<li><span>单号:</span><span>" +
this.taxInfo.orderNo +
"</span></li>" +
"<li><span>消费金额:</span><span>" +
this.taxInfo.totalFee +
"</span>元</li>" +
"<li><span>实际支付:</span><span>" +
this.taxInfo.realFee +
"</span>元</li>" +
"<li><span>支付方式:</span><span>" +
this.taxInfo.channelName +
"</span></li>" +
"<li><span>支付时间:</span><span>" +
this.taxInfo.payTime +
"</span></li>" +
"</ul>",
});
},
在打印页面,通过给webview添加监听,来判断是否可以打印了。如果监听到webview-print-do这个消息,然后就可以开始执行打印了
import { ipcRenderer } from "electron";
...
methods:{
handlePrinter() {
ipcRenderer.send("getPrinterList");
ipcRenderer.once("getPrinterList", (event, data) => {
data.forEach((item) => {
if (item.isDefault) {
this.printerName = item.name;
}
});
this.printRender();
});
},
}
...
mounted() {
const webview = this.$refs.printWebview;
webview.addEventListener("ipc-message", (event) => {
if (event.channel === "webview-print-do") {
webview
.print({
silent: true,//静默打印
printBackground: true,
deviceName: this.printerName, //打印机名称
})
.then((res) => {})
.catch((err) => {})
.finally(() => {
this.messageBox.close();
});
}
});
},
有几个问题需要注意
第一点:不要纠结index.html的内容,可以把这个文件看做是承载你打印内容的纸张,不用去看这个文件内是否报错
第二个点:webview中使用src路径,在本地测试的时候 可以通过../../这样的方式去找到index.html,但是在打包过后,这种访问方式是不行的,需要你通过path去获取到它的目录。具体的方法是这样的:
...
import path from "path";
export default {
data(){
return {
fullPath: path.join(__static, "print.html"),
}
}
...
}
第三个点:容易被忽视的,在前面也提到了,我使用的版本中,需要给webview标签加上2个属性,才能成功实现打印功能
nodeintegration
webpreferences="contextIsolation=no"
简单解释一下,第一个属性nodeintegration,是为了让webview访问的页面具有Node集成, 并且可以使用像 require
和 process
这样的node APIs 去访问低层系统资源。
第二个属性webpreferences="contextIsolation=no"
也是同样的目的,为了在index.html中使用require。
大概就这么多,踩了好几个坑,比较重要的应该都记下来了。如果看到底的你还有什么问题,很欢迎讨论咨询,共同解决问题。