您当前的位置: 首页 > 技术文章 > 前端开发

记一次Electron 桌面应用实现调用打印机打印小票功能

作者: 时间:2023-04-30阅读数:人阅读

项目背景

实现一款交互屏桌面应用软件,类似医院那张种给用户操作办理业务的应用程序。操作业务中在充值后可供用户打印小票。

开发框架:electron-vue

vue版本:v2.6.14

electron版本:v17.2.0

node版本:v16.13.0

实现流程:记一次Electron 桌面应用实现调用打印机打印小票功能(图1)

ps:提一下主结构的版本,是因为在electron中很多情况下出现各种问题是由版本问题导致的。

实现

1.获取系统打印机列表

在主进程窗口,通过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();
      });
    },
}

2.使用webview标签,实现打印

定义webview,在src中绑定打印的页面index.html,我们定义这个页面到static这个静态文件目录下面,因为这个页面的文件不会被打包导致打印失败。需要值得注意的是,得给webview添加2个属性:

nodeintegration

 webpreferences="contextIsolation=no"

<webview
  id="printWebview"
  ref="printWebview"
  :src="https://blog.csdn.net/Tsinbo1314/article/details/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();
          });
      }
    });
  },

3.注意事项 

有几个问题需要注意

第一点:不要纠结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。

大概就这么多,踩了好几个坑,比较重要的应该都记下来了。如果看到底的你还有什么问题,很欢迎讨论咨询,共同解决问题。

本站所有文章、数据、图片均来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:licqi@yunshuaiweb.com

加载中~
如果您对我们的成果表示认同并且觉得对你有所帮助可以给我们捐赠。您的帮助是对我们最大的支持和动力!
捐赠我们
扫码支持 扫码支持
扫码捐赠,你说多少就多少
2
5
10
20
50
自定义
您当前余额:元
支付宝
微信
余额

打开支付宝扫一扫,即可进行扫码捐赠哦

打开微信扫一扫,即可进行扫码捐赠哦

打开QQ钱包扫一扫,即可进行扫码捐赠哦