前车之鉴

也是阅读了很多资料和前人踩的坑,直接使用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. .NET 云原生架构师训练营(模块二 基础巩固 EF Core 介绍)--学习笔记

    2.4.2 EF Core -- 介绍 ORM Repository 仓储 UnitOfWork 工作单元 DB Context 与 DB Set EF Core快速开始示例 ORM ORM:obje ...

  2. phpBB3在Nginx反向代理中的X-Forwarded-For IP检查

    记录一下phpBB3对反向代理中的IP处理机制 处理几个phpBB3项目迁移, 部分运行场景转移到内网, 需要外网通过nginx/openresty之类的网关反向代理进行访问. 在网关处已经正确配置了 ...

  3. Java并发编程实例--19.在一个锁中使用多个条件

    一个锁可能关联了一个或多个条件.这些条件可以在Condition接口中声名. 使用这些条件的目的是去控制一个锁并且可以检查一个条件是true或false,如果为false,则暂停直到 另一个线程来唤醒 ...

  4. Go语言并发编程(2):channel 通道介绍和使用

    一.简介 channel 不仅可以用于 goroutine 间进行安全通信,还可以用于同步内存访问. 而且 Go 社区强烈推荐使用 channel 通道实现 goroutine 之间的通信, 不要通过 ...

  5. Eharts立体柱状图

    一下这三个div大小不一样 为了保证每次柱状图渲染正确 添加key <div class="echart1" id="dangerChart1" key= ...

  6. 双层循环练习,pass_break_continue,和for循环---day06

    1.双层循环练习 2.pass_break_continue pass:在代码块中无代码可写时,可用pass占位 break:终止当前循环,只能应用在循环里 continue:跳过当前循环,从下一次开 ...

  7. pyqt5中通过pycharm配置designer(win和mac都适用,修改下designer目录路径即可)

    安装 pip install PyQt5 -i https://pypi.douban.com/simple pip install PyQt5-tools -i https://pypi.douba ...

  8. Mac环境下, VMware Fusion Pro下的虚拟机( CentOS 7)的 NAT网络配置

    前提实现说明 1.vm版本VMware Fusion Pro 12.1.0 2.centos版本centos7.6 1.虚拟机能访问外网,虚拟机能访问mac本机: 2.mac本机可以连接虚拟机 操作步 ...

  9. 一文了解 Nebula Graph DBaaS 服务——Nebula Graph Cloud Service

    Nebula Graph DBaaS 作为一款 DBaaS(DataBase as s Service)的产品,Nebula Graph Cloud Service 极大地降低了研发人员使用 Nebu ...

  10. Dungeon Master 题解

    这道题的题意简单来说:就是在3D迷宫里找出口,也就是三维地图,需要用到三维数组 由于本人写代码极易出错,所以在输入三维数组的时候修改了c(column,即列)的值,重复定义了没看到==,后面改成定义成 ...