当前位置:   article > 正文

electron实现静默打印(各种踩坑解决)

electron实现静默打印(各种踩坑解决)

前车之鉴

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

需求思路

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

具体实现

main
  • getPrinter

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

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

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

  1. private print(event: IpcMainInvokeEvent, options: WebContentsPrintOptions) {
  2. return new Promise(resolve => {
  3. event.sender.print(options, (success: boolean, failureReason: string) => {
  4. resolve({ success, failureReason })
  5. })
  6. })
  7. }
  • createPrint

创建打印窗口(显示可预览,隐藏可静默)
这里有一个print页面要写,路径指向此页面路由
区分开发环境和生产环境
数据我是通过query传参方式通信,也可以用其他方式(store,cookie等)

  1. private createPrint(_, data: string) {
  2. if (win) {
  3. win.destroy()
  4. win = null
  5. }
  6. win = new BrowserWindow({
  7. titleBarStyle: 'hidden',
  8. width: 1240,
  9. height: 768,
  10. useContentSize: true,
  11. frame: false,
  12. show: false,
  13. webPreferences: {
  14. preload: join(__dirname, '../preload/index.js'),
  15. sandbox: false
  16. }
  17. })
  18. const url = is.dev ? new URL(process.env.ELECTRON_RENDERER_URL!) : new URL('file://')
  19. url.pathname = is.dev ? '' : join(__dirname, '../renderer/index.html')
  20. url.hash = `#/print?data=${data}`
  21. win.loadURL(url.href)
  22. // win.webContents.openDevTools()
  23. win.setMenu(null)
  24. win.on('ready-to-show', () => {
  25. // win?.show()
  26. win?.hide()
  27. })
  28. win.on('closed', () => {
  29. win = null
  30. })
  31. }
  • destroyPrint
  1. private destroyPrint() {
  2. if (win) {
  3. win.destroy()
  4. win = null
  5. }
  6. }
  • 其他代码
  1. // 在class外部定义win
  2. let win = null as BrowserWindow | null
  3. // 提供register
  4. register() {
  5. ipcMain.handle('get-printers', this.getPrinters)
  6. ipcMain.handle('print', this.print)
  7. ipcMain.handle('create-print-window', this.createPrint)
  8. ipcMain.handle('destroy-print-window', this.destroyPrint)
  9. }
preload
  1. const api = {
  2. printer: {
  3. getPrinter: () => ipcRenderer.invoke('get-printers'),
  4. print: (options: WebContentsPrintOptions) => ipcRenderer.invoke('print', options),
  5. createPrintWindow: (data: string) => ipcRenderer.invoke('create-print-window', data),
  6. destroyPrintWindow: () => ipcRenderer.invoke('destroy-print-window')
  7. }
  8. }
  9. contextBridge.exposeInMainWorld('api', api)
renderer
  • 触发打印功能
  1. const printClick = ref(false)
  2. const handlePrint = async (data: Order) => {
  3. if (printClick.value) {
  4. return
  5. }
  6. printClick.value = true
  7. const list = await window.api.printer.getPrinter()
  8. console.log(list)
  9. if (!list.length) {
  10. toast('没有检测到打印设备!', 'error')
  11. return
  12. }
  13. toast('正在打印出货单...', 'info')
  14. await window.api.printer.createPrintWindow(
  15. JSON.stringify({ ...data, createTime: formatDate(data.createTime) })
  16. )
  17. printClick.value = false
  18. }
  • 打印窗口页面
  1. <template>
  2. ........
  3. <!-- 打印内容和样式 -->
  4. <!-- handle里面 win.show()和控制台功能可临时调试放开注释 -->
  5. </template>
  6. <script setup name="Print" lang="ts">
  7. import { WebContentsPrintOptions } from 'electron'
  8. import { onMounted } from 'vue'
  9. import { useRoute } from 'vue-router'
  10. // 从query获取内容
  11. const query = useRoute().query
  12. const { data } = query
  13. const order: Order = JSON.parse(data as string)
  14. // 这里加了延时,后面解释...
  15. onMounted(() => {
  16. setTimeout(print, 100)
  17. })
  18. // 这里解释
  19. // el-table看到的样式和打印出来的样式区别更大,在于style内联样式的问题
  20. // 渲染后会在.el-table__header,.el-table__body等DOM上计算出宽度来优化样式
  21. // 如果是用户自己点击打印按钮,再去做样式处理setTableFrame是没有问题的,因为样式是后来我们自己加上的100%
  22. // 而为了实现静默下载,需要在页面渲染完成就立即打印,此时elementui也刚刚计算好宽度赋值,而覆盖掉我们的逻辑
  23. // 所以延时了一波,样式没变化,但打印出来的样式就和我们看到的页面样式一样了
  24. const setTableFrame = () => {
  25. //el-table设置宽度100%
  26. const tableNodes = document.querySelectorAll(
  27. '.el-table__header,.el-table__body'
  28. ) as NodeListOf<HTMLElement>
  29. tableNodes.forEach(table => {
  30. table.style.width = '100%'
  31. const children = table.children
  32. for (let i = 0; i < children.length; i++) {
  33. const child = children[i]
  34. if (child.localName === 'colgroup') {
  35. child.innerHTML = ''
  36. }
  37. }
  38. })
  39. //el-table cell设置每个宽度100%
  40. const cells = document.querySelectorAll('.cell') as NodeListOf<HTMLElement>
  41. cells.forEach(cell => {
  42. cell.style.width = '100%'
  43. cell.removeAttribute('style')
  44. })
  45. }
  46. // 打印,先重置el-table样式
  47. const print = async () => {
  48. setTableFrame()
  49. try {
  50. // 设置打印参数,具体看文档
  51. const options: WebContentsPrintOptions = {
  52. silent: true,
  53. margins: { marginType: 'none' },
  54. pageSize: 'A4'
  55. }
  56. await window.api.printer.print(options)
  57. } catch (error) {
  58. console.log(error)
  59. } finally {
  60. // 打印完成,调用destory
  61. await window.api.printer.destroyPrintWindow()
  62. }
  63. }
  64. </script>

踩坑

如果是普通下载(非静默),到此就没有问题了
我的版本是electron@27,设置silent: true后,有问题,会缩放很小,而且居中展示
那么有问题,就肯定不止我一个人遇到,就肯定有解决方法
不过@24官方已经不支持更新维护了,但是基本没啥问题(打印功能很迷,据说时不时一个版本好,一个版本又坏,然后又好)
后期项目还要支持win7,还得降级到@21,没bug不出问题就完事~

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/495883
推荐阅读
相关标签
  

闽ICP备14008679号