• vite-electron 静默打印功能实现


    系列文章目录

    electron+vite+vue3 快速入门教程



    前言

    本文将介绍基于electron-vite构建工具下vue3项目内如何实现打印机静默打印功能,并以热敏打印机打印二维码为示例用代码实现该功能。


    一、实现方案

    electron实现打印方案有2种:
    1、webContents.print({deviceName}):主线程内创建一个新窗口打印整个窗口内容
    2、< webview />标签引入本地静态html,通过webview dom对象调用 print({deviceName})方法打印html内容

    ps:deviceName为打印机设备名称,可通过webContents.getPrintersAsync()获取

    webview 主要在渲染进程进行操作相对于webContents方案来说传值比较方便,用起来比较简单顺手,本文将采用webview 方案进行讲解

    二、< webview />讲解

    < webview />标签类似iframe,内容内嵌窗口显示,Electron >= 5默认禁用 webview 标签,开启需要在主进程创建窗口设置webPreferences.webviewTag为ture

    主进程main.js:

      mainWindow = new BrowserWindow({
        width,
        height,
        show: false,
        maximizable: true,
        autoHideMenuBar: true,
        minHeight: height * 0.9,
        ...(process.platform === 'linux' ? { icon } : {}),
        webPreferences: {
          preload: join(__dirname, '../preload/index.js'),
          sandbox: false,
          webviewTag: true,//开启webview标签
        }
      })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    1、属性

    src:html文件本地路径或网络url,由于开发环境和生产环境加载资源方式不同,本地html文件打包后要生效需要通过主进程获取
    nodeintegration:允许使用node APIs
    webpreferences:是一个由逗号分割的字符串列表,其中指定了要设置在 webview 上的 Web 首选项

    实现打印功能需要webview跟渲染进程双向通信,所以首选项需要关闭隔离并使用nodeApi
    如下:

         <webview
          :src="webviewUrl"
          webpreferences="contextIsolation=false"
          nodeintegration
        />
    
    • 1
    • 2
    • 3
    • 4
    • 5

    不仅需要在webview标签上设置,同时需要在主进程上设置才会生效:

    主进程main.js:
    
    ```javascript
      mainWindow = new BrowserWindow({
        width,
        height,
        show: false,
        maximizable: true,
        autoHideMenuBar: true,
        minHeight: height * 0.9,
        ...(process.platform === 'linux' ? { icon } : {}),
        webPreferences: {
          preload: join(__dirname, '../preload/index.js'),
          sandbox: false,
          webviewTag: true,//开启webview标签
          
          nodeIntegration: true,//使用node Api
          contextIsolation: false,//关闭隔离
        }
      })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    2、 监听事件

    dom-ready:webview渲染完成回调。

    ipc-message:监听webview(html)发出消息

          <webview
          :src="webviewUrl"
          webpreferences="contextIsolation=false"
          nodeintegration
          @ipc-message="onWebviewIpcMessage"
        />
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
     <script setup>
     const onWebviewIpcMessage=event=>{
          if(event.channel == 'startPrint'){//startPrint  消息自定义标识符
             console.log("来自webview消息")
          }
     }
     </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    3、方法

    < webview>.print([options]):开始打印

    options:
    silent:是否静默打印,值Boolean类型
    deviceName:打印设备名称,值String类型
    margins: 设置纸张边距,值为对象marginType :{top,bottom.left,right}分别设置上下左右边距
    pageSize:设置纸张尺寸,可选值A4,A5,A6等或者对象包含宽高属性{height:210000,width:58000} ,单位微米
    header : 设置页眉内容,值String类型
    footer: 设置页脚内容,值String类型

    webviewRef.value.print({
          silent: true,//静默打印
          deviceName: 'XP-58',//设备名称
          margins: { marginType: 'none' },//无边距
          pageSize:{
             height:210000
             width:58000
            }
        })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    更多options属性可查看< webview>.print([options])


    三、 webview与渲染进程通信

    1.渲染进程—>webview

    webview.send + ipcRenderer.on

    渲染进程:

          <webview
          ref="webviewRef"
          :src="webviewUrl"
          webpreferences="contextIsolation=false"
          nodeintegration
          @dom-ready="onReady"
        />
     <script setup>
       import {ref} from 'vue';
       const webviewRef=ref();
       const onReady=()=>{
         //给webview发送消息
          webviewRef.value.send("message","from renderer")
       }
    </script>   
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    webview(print.html):

    <script>
      const { ipcRenderer } = require('electron')
      //接收渲染进程消息
       ipcRenderer.on("message",res=>{
        console.log(res)// from renderer  
    })
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2.webview—>渲染进程:

    ipcRenderer.sendToHost + < webview/>标签ipc-message事件

    webview(print.html):

    <script>
      const { ipcRenderer } = require('electron')
      //给渲染进程发送消息
      ipcRenderer.sendToHost('startPrint')
    })
    </script>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    渲染进程:

     <webview
          ref="webviewRef"
          :src="webviewUrl"
          webpreferences="contextIsolation=false"
          nodeintegration
          @ipc-message="onWebviewIpcMessage"
        />
     <script setup>
       import {ref} from 'vue';
       const webviewRef=ref();
       //接收webview发送的消息
        const onWebviewIpcMessage = (event) => {
         if (event.channel == 'startPrint') {
           console.log("收到webview消息")
        }
       }
    </script> 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    四、代码实战

    下面实现如下场景:热敏打印机打印产品二维码小票功能,从接口动态获取产品规格和产品型号写入数据并打印出来

    要实现效果图:

    请添加图片描述

    请添加图片描述

    渲染进程文件index.vue

    <template>
      <div class="container">
        <webview
        class="webview"
          ref="webviewRef"
          :src="webviewUrl"
          webpreferences="contextIsolation=false"
          nodeintegration
          @ipc-message="onWebviewIpcMessage"
        />
        <button class="btn" @click="handlePrint">打印</button>
      </div>
    </template>
    <script setup>
    import { ref} from 'vue'
    
    const webviewRef = ref()
    const webviewUrl = ref()
    
    //从主进程获取html文件路径
    electron.ipcRenderer.invoke('getWebviewFile').then((file) => {
        webviewUrl.value = file
    })
    
    
    //接收webview发送的消息
    const onWebviewIpcMessage = (event) => {
    //webviwe数据设置完毕开始打印
      if (event.channel == 'startPrint') {
        webviewRef.value.print({
          silent: true,//静默打印
          deviceName: 'XP-58',//演示直接写死,实际开发可从主进程webContents.getPrintersAsync()获取
          margins: { marginType: 'none' },//无边距
        })
      }
    }
    
    //打印
    const handlePrint = () => {
      //规格和批号参数演示写死,实际可从接口请求获取
      webviewRef.value && webviewRef.value.send('render', { sp:'H4254c',no:'n700a025' })
    }
    
    </script>
    <style lang="scss" scoped>
    .container{
        padding: 30px;
        box-sizing: border-box
    }
    .webview{
        height: 50vh;
        width:300px;
        padding: 0px;
        background-color: #fff;
    }
    .btn{
        margin-top: 30px;
        width: 200px;
    
    }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    webiview:
    renderer/public目录(没有就新建)新建html目录和print.html文件
    在这里插入图片描述

    print.html(打印内容):

    <!doctype html>
    <html>
      <head>
        <meta charset="UTF-8" />
        <style>
          * {
            margin: 0;
            padding: 0;
          }
        
          @page {
            margin: 0;
            size: 58mm 210mm;/**设置纸张尺寸,也可以填A4、A5等*/
          }
          .page {
            width: 100%;
            display: flex;
            flex-direction: column;
            padding: 0;
            box-sizing: border-box;
            margin: 0;
           
          }
          .img {
            width: 100%;
            height: auto;
          }
          .content {
            display: flex;
            flex-direction: column;
            align-items: flex-start;
            font-size: 20px;
            margin-top: 10px;
          }
          .txt {
            line-height: 30px;
          }
    
          /**打印样式增加内边距*/
          @media print {
            .page {
              width: 100%;
              display: flex;
              flex-direction: column;
              box-sizing: border-box;
              margin: 0;
              padding: 3mm;
              box-sizing: border-box;
            }
          }
        </style>
      </head>
      <body>
        <div  class="page">
          <img class="img" src="data:image/png;base64,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" />
          <div class="content">
            <span id="sp" class="txt"></span>
            <span id="no" class="txt"></span>
          </div>
        </div>
      </body>
      <script>
        const { ipcRenderer } = require('electron')
    
        //监听渲染进程发送过来的数据
        ipcRenderer.on('render', (e, res) => {
          //从渲染进程获取规格、批号数据更新到页面
           document.getElementById('sp').innerHTML=`产品规格:${res.sp}`
           document.getElementById('no').innerHTML=`产品批号:${res.no}`
           //通知渲染进程开始打印
          ipcRenderer.sendToHost('startPrint')
        })
      </script>
    </html>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75

    主进程main/index.js:

    添加获取print.html路径

      ipcMain.handle('getWebviewFile', () => {
        if (is.dev && process.env['ELECTRON_RENDERER_URL']) {//开发环境
          return process.env['ELECTRON_RENDERER_URL'] + '/html/print.html'
        } else {//生产环境
          return join(__dirname, '../renderer/html/print.html')
        }
      })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    开启webview标签、关闭隔离、使用nodeApi

    function createWindow() {
      const primaryDisplay = screen.getPrimaryDisplay()
      const { width, height } = primaryDisplay.workAreaSize
      // Create the browser window.
      mainWindow = new BrowserWindow({
        width,
        height,
        show: false,
        maximizable: true,
        // resizable:false,
        autoHideMenuBar: true,
        minHeight: height * 0.9,
        // frame: false,
        ...(process.platform === 'linux' ? { icon } : {}),
        webPreferences: {
          preload: join(__dirname, '../preload/index.js'),
          sandbox: false,
          webSecurity: false, //跨域处理,
          
          webviewTag: true,//开启webview标签
          nodeIntegration: true,//使用node Api
          contextIsolation: false,//关闭隔离
    
        }
      })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    打印样式说明

    1.除了从打印函数设置参数也可以通过@page设置打印页面样式,size支持A4,A5等常见纸张尺寸或者自定义长宽,单位支持mm、cm等

          @page {
            size: 58mm 210mm;/**设置纸张尺寸 58mm*210mm*/
            margin:0,/**无边距*/
          }
    
    • 1
    • 2
    • 3
    • 4
     @page {
            size: A4;/**设置纸张大小A4*/
          }
    
    • 1
    • 2
    • 3

    2.@media print 单独设置打印样式

     <style>
       /**web渲染样式*/
       .img{
          width:200px
         }
     /**打印样式**/
     @media print {
       .img{
          width:30mm
         }
     }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    踩坑说明

    当前electron最新版本为28,此版本有bug设置纸张尺寸和样式会无效,导致打印出来的样式很大或者很小,可以回退到24版本解决

    npm install electron@24
    
    • 1
  • 相关阅读:
    [免费专栏] Android安全之利用ADT获取内存中的敏感信息
    第八章 配置命名空间(三)
    控制算法::速度前馈
    pytorch深度学习实战15
    社交网络用户行为分析,各类社交软件用户分析
    Gradio入门到进阶全网最详细教程[一]:快速搭建AI算法可视化部署演示(侧重项目搭建和案例分享)
    Kubernetes 深入理解Kubernetes(二) 声明组织对象
    JSP 装饰公司网站myeclipse开发mysql数据库MVC模式java编程网页设计系统采用mvc框架
    2022-2028全球视频监控软件行业调研及趋势分析报告
    设计循环队列,解决假溢出问题
  • 原文地址:https://blog.csdn.net/sd1sd2/article/details/138078998