前车之鉴

也是阅读了很多资料和前人踩的坑,直接使用webContent.print方法进行打印。其他方式要不就是Bug多,官方修复也有问题;要不就是官方升级版本后不再支持等

不赘述

需求思路

  • main里面实现printerHandle,暴露给渲染线程去调用打印等功能
  • 点击打印后,调出打印页面(新建窗口再隐藏)
  • 通过路径指向打印页面的路由地址,在此页面进行html和css编码,实现打印内容编辑
  • onMounted事件上直接执行打印操作,实现静默
  • 打印完成后,销毁窗口(此过程用户无感)

具体实现

main

  • getPrinter

获取打印机列表,有array.length再继续

  private async getPrinters(event: IpcMainInvokeEvent) {
const printers = await event.sender.getPrintersAsync()
return printers
}
  • print

打印功能,使用官方提供API

  private print(event: IpcMainInvokeEvent, options: WebContentsPrintOptions) {
return new Promise(resolve => {
event.sender.print(options, (success: boolean, failureReason: string) => {
resolve({ success, failureReason })
})
})
}
  • createPrint

创建打印窗口(显示可预览,隐藏可静默)

这里有一个print页面要写,路径指向此页面路由

区分开发环境和生产环境

数据我是通过query传参方式通信,也可以用其他方式(store,cookie等)

  private createPrint(_, data: string) {
if (win) {
win.destroy()
win = null
}
win = new BrowserWindow({
titleBarStyle: 'hidden',
width: 1240,
height: 768,
useContentSize: true,
frame: false,
show: false,
webPreferences: {
preload: join(__dirname, '../preload/index.js'),
sandbox: false
}
}) const url = is.dev ? new URL(process.env.ELECTRON_RENDERER_URL!) : new URL('file://')
url.pathname = is.dev ? '' : join(__dirname, '../renderer/index.html')
url.hash = `#/print?data=${data}` win.loadURL(url.href)
// win.webContents.openDevTools() win.setMenu(null)
win.on('ready-to-show', () => {
// win?.show()
win?.hide()
})
win.on('closed', () => {
win = null
})
}
  • destroyPrint
  private destroyPrint() {
if (win) {
win.destroy()
win = null
}
}
  • 其他代码
// 在class外部定义win
let win = null as BrowserWindow | null // 提供register
register() {
ipcMain.handle('get-printers', this.getPrinters)
ipcMain.handle('print', this.print)
ipcMain.handle('create-print-window', this.createPrint)
ipcMain.handle('destroy-print-window', this.destroyPrint)
}

preload

const api = {
printer: {
getPrinter: () => ipcRenderer.invoke('get-printers'),
print: (options: WebContentsPrintOptions) => ipcRenderer.invoke('print', options),
createPrintWindow: (data: string) => ipcRenderer.invoke('create-print-window', data),
destroyPrintWindow: () => ipcRenderer.invoke('destroy-print-window')
}
} contextBridge.exposeInMainWorld('api', api)

renderer

  • 触发打印功能
const printClick = ref(false)
const handlePrint = async (data: Order) => {
if (printClick.value) {
return
}
printClick.value = true
const list = await window.api.printer.getPrinter()
console.log(list) if (!list.length) {
toast('没有检测到打印设备!', 'error')
return
}
toast('正在打印出货单...', 'info')
await window.api.printer.createPrintWindow(
JSON.stringify({ ...data, createTime: formatDate(data.createTime) })
) printClick.value = false
}
  • 打印窗口页面
<template>
........
// 打印内容和样式
// handle里面 win.show()和控制台功能可临时调试放开注释
</template> <script setup name="Print" lang="ts">
import { WebContentsPrintOptions } from 'electron'
import { onMounted } from 'vue'
import { useRoute } from 'vue-router' // 从query获取内容
const query = useRoute().query
const { data } = query
const order: Order = JSON.parse(data as string) // 这里加了延时,后面解释...
onMounted(() => {
setTimeout(print, 100)
}) // 这里解释
// el-table看到的样式和打印出来的样式区别更大,在于style内联样式的问题
// 渲染后会在.el-table__header,.el-table__body等DOM上计算出宽度来优化样式
// 如果是用户自己点击打印按钮,再去做样式处理setTableFrame是没有问题的,因为样式是后来我们自己加上的100%
// 而为了实现静默下载,需要在页面渲染完成就立即打印,此时elementui也刚刚计算好宽度赋值,而覆盖掉我们的逻辑
// 所以延时了一波,样式没变化,但打印出来的样式就和我们看到的页面样式一样了
const setTableFrame = () => {
//el-table设置宽度100%
const tableNodes = document.querySelectorAll(
'.el-table__header,.el-table__body'
) as NodeListOf<HTMLElement>
tableNodes.forEach(table => {
table.style.width = '100%'
const children = table.children
for (let i = 0; i < children.length; i++) {
const child = children[i]
if (child.localName === 'colgroup') {
child.innerHTML = ''
}
}
}) //el-table cell设置每个宽度100%
const cells = document.querySelectorAll('.cell') as NodeListOf<HTMLElement>
cells.forEach(cell => {
cell.style.width = '100%'
cell.removeAttribute('style')
})
} // 打印,先重置el-table样式
const print = async () => {
setTableFrame()
try {
// 设置打印参数,具体看文档
const options: WebContentsPrintOptions = {
silent: true,
margins: { marginType: 'none' },
pageSize: 'A4'
}
await window.api.printer.print(options)
} catch (error) {
console.log(error)
} finally {
// 打印完成,调用destory
await window.api.printer.destroyPrintWindow()
}
}
</script>

踩坑

如果是普通下载(非静默),到此就没有问题了

我的版本是electron@27,设置silent: true后,有问题,会缩放很小,而且居中展示

那么有问题,就肯定不止我一个人遇到,就肯定有解决方法

不过@24官方已经不支持更新维护了,但是基本没啥问题(打印功能很迷,据说时不时一个版本好,一个版本又坏,然后又好)

后期项目还要支持win7,还得降级到@21,没bug不出问题就完事~

electron实现静默下载(各种踩坑解决)的更多相关文章

  1. IDEA打包javaFX及踩坑解决

    开门见山的说,先打包,再说坑. File-->Project Structure --> Artifacts-->(此处点加号)JAR-->From modules with ...

  2. EasyPoi 导入导出Excel时使用GroupName的踩坑解决过程

    一.开发功能介绍: 简单的一个excel导入功能 二.Excel导入模板(大致模板没写全): 姓名 性别 生日 客户分类 联系人姓名 联系人部门 备注 材料 综合 采购 张三 男 1994/05/25 ...

  3. ubuntu18.04 搭建scrapy环境(连环踩坑+解决办法)

    ---恢复内容开始--- 预期需求: 打算搭建scrapy环境,基于python3.x的 环境描述: ubuntu18.04自带了python3.6,打算在虚拟环境vlenv中跑scrapy,装好虚拟 ...

  4. Idea运行支付宝网站支付demo踩坑解决及其测试注意事项

    一.前言 在一些商城网上中,必不可少的是支付,支付宝和微信比较常见,最近小编也是在研究这一块,看看支付宝怎么进行支付的,支付宝给我们提供了demo和沙箱测试.减少我们的申请的麻烦,公钥和秘钥也比之前方 ...

  5. CompletionService用法踩坑解决优化

    转自:https://blog.csdn.net/xiao__miao/article/details/86352380 1.近期工作的时候,运维通知一个系统的内存一直在增长,leader叫我去排查, ...

  6. 编译课设·CLion到VS踩坑·解决·备忘录

    应试用,VS使用习惯和JB系差别还是蛮大的 打不过他们就加入他们 键位修改 工具-选项 键盘:改keymap 字体和颜色:宋体必改. 自动恢复:自动保存默认3分钟 CMake:自救时可以看一下 键位名 ...

  7. 【踩坑经历】一次Asp.NET小网站部署踩坑和解决经历

    2013年给1个大学的小客户部署过一个小型的Asp.NET网站,非常小,用的sqlite数据库,今年人家说要换台服务器,要重新部署一下,好吧,虽然早就过了服务时间,但无奈谁叫人家是客户了,二话不说,上 ...

  8. 记一次FTP下载踩坑的故(shi)事(gu)

    下班前领导忽然要求我将客户的日志服务器上一些日志拷贝到测试服务器中,不过领导只提供给我FTP的连接方式,很明显就是要我用FTP方式去做啦 一般来说FTP批量下载也就上网随便找个脚本的事,但是却成了我疯 ...

  9. Vue packages version mismatch的解决方法 初来乍到,踩坑日常

    初来乍到,踩坑日常 这个问题也是我也是接受别人项目,出现的问题,在下载好依赖后运行的时候报这样的错误 它上面显示两个版本一个vue的版本,一个vue-template-compiler版本,我这边忘了 ...

  10. electron踩坑系列之一

    前言 以electron作为基础框架,已经开发两个项目了.第一个项目,我主要负责用react写页面,第二项目既负责electron部分+UI部分. 做项目,就是踩坑, 一路做项目,一路踩坑,坑多不可怕 ...

随机推荐

  1. Delphi实现登录窗体与主窗体的过程

    登录窗体: type TfrmLogin = class(TForm) btn1: TButton; procedure btn1Click(Sender: TObject); private { P ...

  2. jwt 生成的token exp 的单位是秒

    public class Test { public static void main(String[] args) throws UnsupportedEncodingException { Dat ...

  3. 如何在windows端安装和配置maven

    1.下载maven 从官方下载maven,下载页面:http://maven.apache.org/download.cgi 2.安装maven maven压缩包解压到一个没有中文,空格或其他特殊字符 ...

  4. FreeSWITCH在session上执行特定dialplan

    操作系统 :CentOS 7.6_x64 FreeSWITCH版本 :1.10.9 日常开发中,会遇到需要在已存在的session上执行特定拨号方案的情况,今天整理下这方面的内容,我将从以下几个方面进 ...

  5. JOISC 2023 纪录

    记录一下 JOISC 2023 的做题记录 Day1 T1 Two Currencies 给定一棵树,在边上有总计 \(m\) 个检查站,经过一个检查站需要叫 \(1\) 枚金币或者若干枚银币.\(Q ...

  6. NC201985 立方数

    题目链接 题目 题目描述 对于给定的正整数 N,求最大的正整数 A,使得存在正整数 B,满足 \(A^3B=N\) 输入包含 T 组数据,1≤T≤10,000:\(1≤N≤10^{18}\) 输入描述 ...

  7. Sigrok逻辑分析仪软件(基于CY7C68013A)

    关于逻辑分析仪 逻辑分析仪在调试数字电路时是非常重要的工具. 其形式与示波器类似, 采集被检测信号的电平, 并绘制时序图进行分析. 逻辑分析仪和示波器的区别: 数字量和模拟量: 示波器采集的是模拟量, ...

  8. 从零开始学Spring Boot系列-前言

    从零开始学Spring Boot系列-前言 在数字化和信息化的时代,Java作为一种成熟.稳定且广泛应用的编程语言,已经成为构建企业级应用的首选.而在Java生态系统中,Spring框架无疑是其中最为 ...

  9. Direct2D 旋转篇

    微软文档:Transforms 本篇通过官方文档学习,整理出来的demo,初始样本请先创建一个普通的desktop app. ID2D1SolidColorBrush* m_pOriginalShap ...

  10. C++ 线程的学习---线程同步

    因为是学习篇,写下是为了个人的学习与理解.故参考其他文章为多. 为什么需要线程同步? 在程序中使用多线程时,一般很少有多个线程能在其生命期内进行完全独立的操作.更多的情况是一些线程进行某些处理操作,而 ...