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

发布时间:2023-02-22 14:30

项目背景

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

开发框架:electron-vue

vue版本:v2.6.14

electron版本:v17.2.0

node版本:v16.13.0

实现流程:\"\"

 

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="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。

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

ItVuer - 免责声明 - 关于我们 - 联系我们

本网站信息来源于互联网,如有侵权请联系:561261067@qq.com

桂ICP备16001015号