依赖版本:
- "electron": "^22.3.4"
- "vue": "^3.3.4"
- "vite": "^4.4.9"
第一步:开启Electron的Webview标签功能
// Create the browser window.
const mainWindow = new BrowserWindow({
width: 1920,
height: 1080,
show: false,
autoHideMenuBar: true,
...(process.platform === 'linux' ? { icon } : {}),
webPreferences: {
preload: join(__dirname, '../preload/index.js'),
sandbox: false,
// webviewTag配置如下
webviewTag: true,
nodeIntegration: true,
contextIsolation: false,
webSecurity: false
}
})
第二步:在index.html的同级目录下新建一个print.html用来承载打印的内容
<!-- /src/print.html -->
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>小票打印</title>
<style>
html,
body {
padding: 0;
margin: 0;
}
</style>
<style media="print">
/* 这里可以引入打印的样式 */
@import './src/assets/css/print.css';
</style>
</head>
<body>
<div id="print" class="ticket-body"></div>
</body>
<script type="module">
const { ipcRenderer } = require('electron')
window.onload = () => {
// 监听渲染层发送的渲染打印内容事件
ipcRenderer.on('onRenderPrint', (e, html, options) => {
document.getElementById('print').innerHTML = html
// 渲染完成后发送打印事件
ipcRenderer.sendToHost('onPrint', options)
})
}
</script>
</html>
第三步:获取print.html的地址
由于在electron中,开发环境和生产环境的资源地址是不一样的,开发环境使用的是浏览器的地址,而生产环境使用的地址是Electron内部的地址
开发环境:
生产环境:
我的方法是通过 渲染进程 向 主进程 发送一个自定义事件来获取到这个地址
// 主进程main.js
ipcMain.handle('getPrintFileLocation', () => {
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
// 开发环境
return `${process.env['ELECTRON_RENDERER_URL']}/print.html`
} else {
// 生产环境
return join(__dirname, '../renderer/print.html')
}
})
第四步:在渲染进程中获取print.html
// utils/print.js
/**
* 获取 print.html 文件路径
*/
function getPrintFileLocation() {
return new Promise((resolve, reject) => {
window.electron.ipcRenderer
.invoke('getPrintFileLocation')
.then((url) => {
resolve(url)
})
.catch((e) => {
reject(e)
})
})
}
第五步:动态创建一个WebView标签
// utils/print.js
/**
* 创建webviewTag
* @returns {Promise<Electron.WebviewTag>}
*/
function createWebviewTag() {
return new Promise((reslove) => {
getPrintFileLocation().then(async (printFileUrl) => {
let webviewTag = document.createElement('webview')
// 设置标签的属性
webviewTag.src = printFileUrl
webviewTag.setAttribute('nodeintegration', true)
webviewTag.setAttribute('webpreferences', 'contextIsolation=no')
webviewTag.id = 'print-webview'
// 将webview隐藏掉
webviewTag.style.height = '0px'
webviewTag.style.width = '0px'
webviewTag.style.visibility = 'hidden'
// 如果已经存在该节点则移除掉
if (document.body.contains(document.getElementById('print-webview'))) {
document.body.removeChild(document.getElementById('print-webview'))
}
document.body.appendChild(webviewTag)
reslove(webviewTag)
})
})
}
第六步:向主进程发送自定义事件,获取系统默认打印机的信息
// main.js
ipcMain.handle('getDefaultPrinterInfo', async (event) => {
let printers = await event.sender.getPrintersAsync()
return printers.find((item) => item.isDefault)
})
// utils/print.js
/**
* 获取打印机名称
* @returns {Promise<string>}
*/
function getPrinterName() {
return new Promise(async (resolve) => {
let printerName = await window.electron.ipcRenderer.invoke('getDefaultPrinterInfo')
resolve(printerName)
})
}
第七步:一切准备就绪,开始打印
// utils/print.js
/**
* 基于 Electron 的 webview 静默打印
* @param {string} html 要打印的html内容,
* @param {any} options 打印配置
*/
export async function print(html, options) {
return new Promise(async (resolve, reject) => {
if (typeof window.electron === 'undefined') {
// 如果是允许在浏览器上则调用浏览器的打印,自己封装的浏览器打印函数,如无需要则跳过
let dom = document.createElement('body')
dom.innerHTML = html
webPrint(dom, {})
resolve()
} else {
// electron 静默打印
const webviewTag = await createWebviewTag() // 获取第五步中创建的webview标签
const deviceName = await getPrinterName() // 获取第六步中从主进程那里拿到的打印机的名称
// 监听webview标签已经准备就绪
webviewTag.addEventListener('dom-ready', () => {
// 向webview绑定的print.html发送渲染事件,通知其渲染内容
webviewTag.send('onRenderPrint', html)
// 事件发送完成之后监听print.html那边发送过来的onPrint事件
webviewTag.addEventListener('ipc-message', (event) => {
if (event.channel === 'onPrint') {
// 调用打印
webviewTag
.print({
silent: true,
printBackground: true,
deviceName,
margins: {
marginType: 'none'
},
...options
})
.then(() => {
resolve()
})
.catch(() => {
reject()
})
}
})
})
}
})
}
webviewTag.print() 中的options参考:
第八步:使用
<template>
<div class="ticket-body" ref="ticketRef">测试打印内容</div>
<button @click="handlePrint">打印</button>
</template>
<script>
import { print } from '@/utils/webview-print'
import { ref } from 'vue'
export default {
setup() {
const ticketRef = ref()
async function handlePrint() {
await print(ticketRef.value.innerHTML)
}
return {
ticketRef,
handlePrint
}
}
}
</script>