前车之鉴

也是阅读了很多资料和前人踩的坑,直接使用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. 程序语言多态(overide) - delphi 版本

    程序语言多态 - delphi 版本 前言: 所有程序语言都差不多,特写一篇 delphi 版本 的多态:其它语言 类同. 都是一些别人规定的语法而已,别人用一个下午设计一门语言,愚弄天下程序员一生: ...

  2. go中的 4种 for循环

    package main import "fmt" func main() { // 第一种写法 for i := 1;i < 10; i++ { fmt.Println(& ...

  3. MySQL系列文章汇总

    MySQL系列文章汇总: 导读: 大家好,我是xbhog,MySQL还是到了单独开一个系列了,这样不管是对我还是对读者来说在查找的时候都会方便一些: 话不多说,来看下,该系列会持续更新的(还是看学到哪 ...

  4. 多层PCB的制造工艺流程

    多层PCB的制造工艺流程 多层板制造方法有电镀通孔法以及高密度增层法两种,都是通过不同工艺的组合来实现电路板结构.其中目前采用最多的是电镀通孔法,电镀通孔法经过超过半个世纪的发展与完善,电镀通孔法无论 ...

  5. 2024 SICTF Round#3出题 crypto misc osint

    有幸参与了本次比赛crypto misc OSINT出题,难易程度循序渐进,下面记录一下本人题目题解(( 比赛网址:https://yuanshen.life/ CRYPTO SuperbRSA(85 ...

  6. Js实用小技巧

    Js实用小技巧 这是一份Js实用小技巧,也可以是一份Js挨打小技巧,下面的一系列操作虽然能够在一定程度上使代码更加简洁,但是在缺少注释的情况下会降低可读性,所以需要谨慎使用这些黑魔法. 位元算 取整 ...

  7. Oracle system identifier already exists specify another SID

    问题说明 Centos7重装Oracle,使用DBCA重新创建实例test的时候报错,中文意思是: Oracle系统标识符(SID)"test"已存在,请指定另一个SID. 问题原 ...

  8. Nologging到底何时才能生效

    转了一篇EYGLE的文章 -------------------------------------------------- 最初的问题是这个帖子: http://www.itpub.net/sho ...

  9. Fiddler捕获Java发送的HttpURLConnection请求

    1.说明 平常使用Fiddler抓包工具查看浏览器的请求和响应信息很方便, 但有时候我们也需要拦截java代码执行的http请求. 以便更好的调试程序.具体方法如下: 2.编写Java代码 // 配置 ...

  10. 《系列二》-- 7、后置处理器-PostProcessor

    目录 什么是后置处理器 spring 源码中已知的,顶级PostProcessor 其它 "后置处理器" 阅读之前要注意的东西:本文就是主打流水账式的源码阅读,主导的是一个参考,主 ...