vite-electron 静默打印功能实现

server/2024/11/17 4:30:09/

系列文章目录

electron+vite+vue3 快速入门教程


文章目录

  • 系列文章目录
  • 前言
  • 一、实现方案
  • 二、< webview />讲解
    • 1、属性
    • 2、 监听事件
    • 3、方法
  • 三、 webview与渲染进程通信
    • 1.渲染进程--->webview
    • 2.webview--->渲染进程:
  • 四、代码实战
    • 打印样式说明
    • 踩坑说明


前言

本文将介绍基于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:

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标签}})

1、属性

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

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

javascript">     <webview:src="webviewUrl"webpreferences="contextIsolation=false"nodeintegration/>

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

javascript">主进程main.js:```javascriptmainWindow = 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 ApicontextIsolation: false,//关闭隔离}})

2、 监听事件

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

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

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

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类型

javascript">webviewRef.value.print({silent: true,//静默打印deviceName: 'XP-58',//设备名称margins: { marginType: 'none' },//无边距pageSize:{height:210000width:58000}})

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


三、 webview与渲染进程通信

1.渲染进程—>webview

webview.send + ipcRenderer.on

渲染进程:

javascript">      <webviewref="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>   

webview(print.html):

javascript"><script>const { ipcRenderer } = require('electron')//接收渲染进程消息ipcRenderer.on("message",res=>{console.log(res)// from renderer  
})
</script>

2.webview—>渲染进程:

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

webview(print.html):

javascript"><script>const { ipcRenderer } = require('electron')//给渲染进程发送消息ipcRenderer.sendToHost('startPrint')
})
</script>

渲染进程:

javascript"> <webviewref="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> 

四、代码实战

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

要实现效果图:

请添加图片描述

请添加图片描述

渲染进程文件index.vue

javascript"><template><div class="container"><webviewclass="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>

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

print.html(打印内容):

javascript"><!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>

主进程main/index.js:

添加获取print.html路径

javascript">  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')}})

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

javascript">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 ApicontextIsolation: false,//关闭隔离}})

打印样式说明

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

javascript">      @page {size: 58mm 210mm;/**设置纸张尺寸 58mm*210mm*/margin:0,/**无边距*/}
javascript"> @page {size: A4;/**设置纸张大小A4*/}

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

javascript"> <style>/**web渲染样式*/.img{width:200px}/**打印样式**/@media print {.img{width:30mm}}
</style>

踩坑说明

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

javascript">npm install electron@24

http://www.ppmy.cn/server/22992.html

相关文章

算法课程笔记——如何倍增

快速幂 读入量大于1e5不要用cin读入&#xff0c;要用也要关闭同步流 第i个o次方的父亲 #include<bits/stdc.h>usingnamespacestd; #definemaxn 110000#definell long longintn, a[maxn], f[maxn][40]; intquery(intl, intr){intk (int)(log((r - l 1) * 1.0) / log(2.0…

数据结构与算法-迭代加深搜索算法

迭代加深搜索&#xff08;Iterative Deepening Search&#xff0c;IDS&#xff09; 是一种常用的搜索算法&#xff0c;结合了深度优先搜索的空间效率和广度优先搜索的完备性和最优性。其核心思想是重复进行深度优先搜索&#xff0c;但每次都增加搜索的深度限制&#xff0c;直到…

VR全景创业项目应该如何开展?未来有市场吗?

伴随着5G网络的发展&#xff0c;VR全景得到了众多的关注和提升。与此同时&#xff0c;各行各业都开始关注自身产业在互联网的展示效果&#xff0c;因为年轻一代的生活已经离不开互联网&#xff0c;而VR全景在互联网上的3D展示效果能给商家带来流量&#xff0c;提升营业额。 随着…

使用Docker搭建Nacos集群

本次是在Mac的M1版本上使用Docker搭建Nacos集群的详细步骤&#xff0c;并记录了一些遇到的问题及解决方案。 搭建涉及&#xff1a;1个Nginx 3个Nacos 1个MySQL 使用版本&#xff1a;Nginx为1.22.0&#xff0c;Nacos为2.0.3&#xff0c;MySQL为8.0.30 一、创建虚拟网络 因为M…

数据结构 第六章 树与二叉树(三)

&#x1f680; 【考纲要求】二叉树的遍历 &#x1f680; 第六章第一节内容请查看此链接 树的基本概念 &#x1f680; 第六章第二节内容请查看此链接 二叉树的定义、四种特殊的二叉树和二叉树的存储结构 三、二叉树的遍历和线索二叉树 3.1二叉树的遍历 所谓的二叉树的遍历就是…

VUE3与Uniapp 二 (响应式变量ref)

<template><!-- 响应式数据变量是双项绑定 --><view>{{num}}</view><view>{{string}}</view><view>{{arry[2]}}</view><view>{{obj.name}}</view> </template><script setup>// 使用ref定义响应式数据…

三维点云处理-聚类(上)

聚类&#xff08;Cluster&#xff09;是数据处理中常用的一种分析方法&#xff0c;聚类的目标是将相似的数据对象划分到同一个簇中&#xff0c;使得同一簇内的数据对象的相似性尽可能大&#xff0c;而不同簇中的数据对象的差异性也尽可能大。  这里主要是介绍两种比较经典的聚…

ArcGIS批量寻找图层要素中的空洞

空洞指的是图层中被要素包围所形成的没有被要素覆盖的地方&#xff0c;当图层要素数量非常庞大时&#xff0c;寻找这些空洞就不能一个一个的通过目测去寻找了&#xff0c;需要通过使用工具来实现这一目标。 一、【要素转线】工具 利用【要素转线】工具可以将空洞同图层要素处于…