electron + vue專案實現列印小票功能

Monica_d發表於2018-11-24

前言:該文章需要一定的electron基礎同學。如需瞭解更多相關資訊,請移步electron官方文件

一、需求:

公司專案需要通過electron呼叫系統印表機,實現列印小票的功能。
複製程式碼

二、分析:

electron列印大概有兩種:

第一種:通過window的webcontent物件,使用此種方式需要單獨開出一個列印的視窗,可以將該視窗隱藏,但是通訊呼叫相對複雜。
第二種:使用頁面的webview元素呼叫列印,可以將webview隱藏在呼叫的頁面中,通訊方式比較簡單。

兩個物件呼叫列印方法的使用方式都一樣。

本文是通過第二種方法實現靜默列印。
複製程式碼

三、實現過程:

1、要實現列印功能,首先要知道我們的裝置上有哪些印表機。方法是:在渲染執行緒通過electron的ipcRenderer物件傳送事件到主執行緒獲取。(本文的渲染執行緒可以當做為一個print.vue檔案)

(1)主執行緒(electron.js)虛擬碼如下:
//引入electron
import electron from 'electron';

//建立一個瀏覽器物件
const window = new electron.BrowserWindow({
    width,
    height,
    frame: false,
    show: false,
    backgroundColor: '#4b5b79',
    minWidth: 1024,
    minHeight: 768,
    webPreferences: { webSecurity: false },
  });
  
//在主執行緒下,通過ipcMain物件監聽渲染執行緒傳過來的getPrinterList事件
electron.ipcMain.on('getPrinterList', (event) => {
    //主執行緒獲取印表機列表
    const list = window.webContents.getPrinters();
    
    //通過webContents傳送事件到渲染執行緒,同時將印表機列表也傳過去
    window.webContents.send('getPrinterList', list);
});

===============================================================================

(2)渲染執行緒(print.vue檔案)虛擬碼如下:
<template>
</template>
<script>
    //引入ipcRenderer物件,該物件和主執行緒的ipcMain通訊
    import { ipcRenderer } from 'electron';
    
    //渲染執行緒主動傳送getPrinterList事件到主執行緒請求印表機列表
    ipcRenderer.send('getPrinterList'); 
    
    //監聽主執行緒獲取到印表機列表後的回撥
      ipcRenderer.once('getPrinterList', (event, data) => {
        //data就是印表機列表
        this.printList = data;
      });
</script>

//獲取印表機列表完成
複製程式碼

2、(重頭戲來了)獲取印表機列表後,就需要通過electron自帶的"webview"標籤實現小票排版。"webview"是什麼?可以把它當做"iframe"標籤,它裡面顯示的是你需要列印的內容。

(1)使用"webview"之前,需要新建一個print.html檔案,把你要列印的內容通過print.html顯示出來。我們專案的需求是將要列印的內容通過canvas畫出後,再將canvas轉成圖片資源(base64),然後放到"webview"裡面顯示,虛擬碼如下:

<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>
  //@page樣式是用來設定印表機列印出來的樣式,例如設定小票外邊距樣式
    @page {
      margin: 0px;
    }
  </style>
</head>
<body id='bd'>
</body>
<script>
    //引入ipcRenderer物件
  const {ipcRenderer} = require('electron')
  
  //監聽渲染執行緒傳過來的webview-print-render事件
  ipcRenderer.on('webview-print-render', (event, deviceInfo) => {
    // 動態建立一個img標籤,然後插入到<body>中。deviceInfo是渲染執行緒傳過來的資料
    let html = '';
    html = `<img src="${deviceInfo.imgSource}"
     width="${deviceInfo.imgWidth}px"
     height="${deviceInfo.imgHeight}px">`;
    document.getElementById('bd').innerHTML = html;
    
    //當圖片插入到頁面後,通過ipcRenderer物件的sendToHost方法和渲染執行緒通訊,告訴渲染執行緒列印的內容已經準備完畢,請開始列印操作
    ipcRenderer.sendToHost('webview-print-do');
  });
</script>
</html>
複製程式碼

(2)html檔案建立完成後,將print.html引入到《webview src="./xxxx/print.html"》。該"webview"需要顯式的定義在print.vue檔案中,但需要將它用v-show="false"隱藏,不能用v-if,因為我們需要"webview"的dom節點存在於頁面上,只是不展示而已。

<template>
  <div v-show="false">
    <webview ref="printWebview" src="./xxxx/print.html" nodeintegration/>
  </div>
</template>

<script>
mounted() {
    //當vue節點渲染完成後,獲取<webview>節點
    const webview = this.$refs.printWebview;
    
    //監聽<webview>裡面的訊息,也就是監聽print.html裡面的ipcRenderer.sendToHost傳送的事件,當該事件傳送成功後就會進入下面的回撥事件中執行列印操作。
    webview.addEventListener('ipc-message', (event) => {
      if (event.channel === 'webview-print-do') {
        //如果收到<webview>傳過來的事件,名為"webview-print-do",就執行 webview.print列印方法,列印<webview>裡面的內容。
        webview.print(
          {
            //是否是靜默列印
            silent: true,
            printBackground: true,
            //印表機的名稱,就是本文一開始獲得的印表機列表其中一個
            deviceName: 'xxx',
          },
          (data) => {
            //這個回撥是列印後的回撥事件,data為true就是列印成功,為false就列印失敗
            console.log('webview success', data);
          },
        );
      }
    });
},
</script>
複製程式碼

到這裡本electron呼叫印表機的功能就實現了。但本文章有很多細節沒有講到,只是大概的給了一個思路,如果寫的有不對之處,還望見諒。

相關文章