electron实现打印功能支持静默打印、无感打印

 更新时间:2023年12月20日 08:58:07   作者:Congwei12138  
使用electron开发应用遇到了打印小票的功能,实现途中还是几经波折,下面这篇文章主要给大家介绍了关于electron实现打印功能支持静默打印、无感打印的相关资料,需要的朋友可以参考下

实现思路

业务上目前有两种打印的方法:

  • webview标签:electron提供webview用于在一个独立的 frame 和进程里显示外部 web 内容。但是在Electron >= 5中是禁用该标签的,所以就直接放弃它。
  • webContent.print方法:webCompent是主进程用来渲染和控制网页的对象,而它的print方法是用来打印渲染进程中的网页内容。这里我们选择这个方法。

总体思路大概为:

  • 在页面点击打印按钮,发布订阅去创建一个隐藏的新窗口,窗口内容就是我们要打印的内容。利用新窗口展示的路径的hash和打印页面路由相互匹配实现指定打印内容。
  • 在打印页面初始化后,需要窗口去掉标题的按钮,防止乱入到打印页面。然后在根据路由传递的参数查找数据渲染页面。 再然后发布printHandlePrint事件进行打印并设置静默打印和去除边距。最后打印完成发布destroyPrintWindow事件销毁新窗口。

实现

1、创建打印方法

使用ipcMain.handle向主进程订阅打印相关事件。其中printHandlePrint订阅就是调用打印机的方法。最重要的是openPrintWindow订阅,它的内部去调用了openPrintWindow方法,它的用处就是用于创建一个新的BrowserWindow窗口,将要打印的内容放进新窗口中,这样我们就可以实现打印指定内容的功能。destroyPrintWindow订阅作用于销毁创建的新窗口。

可以看到openPrintWindow会接受两个参数,第一个参数是事件对象,第二个参数是发布事件时传递的参数,而这个参数最终会通过路由的方式传递到打印的新窗口中。实现渲染进程和渲染进程的伪通信。

方法写好之后记得要在app.whenReady()后调用这个方法。

// printHandle.ts
let win: BrowserWindow;
export function usePrintHandle() {
  // 获取系统打印机详情
  ipcMain.handle("getPrinters", async (event) => {
    return await event.sender.getPrintersAsync();
  });
  // 调用打印机打印
  ipcMain.handle(
    "printHandlePrint",
    async (event, options: WebContentsPrintOptions) => {
      return new Promise((resolve) => {
        event.sender.print(
          options,
          (success: boolean, failureReason: string) => {
            resolve({ success, failureReason });
          }
        );
      });
    }
  );
  // 创建打印界面
  ipcMain.handle("openPrintWindow", (_, id) => {
    // id 用于传递的参数
    openPrintWindow(id);
  });
  // 销毁打印界面
  ipcMain.handle("destroyPrintWindow", () => {
    if (win) win.destroy();
  });
}
​
// index.ts
app.whenReady().then(()=>{
  usePrintHandle();
})

2、创建新的BrowserWindow窗口

首先这个创建新窗口的函数会在openPrintWindow订阅中触发,也就是在点击了打印按钮时触发。所以会判断时候存在这个新窗口,如果存在就要隐藏并销毁掉,这样可以保证我们每次点击打印按钮时都会是最新的隐藏的窗口。

然后需要在ready-to-show事件中将新窗口隐藏掉,这样做的好处是这个新窗口一直都是隐藏的,从而实现无感打印。

最后有一个问题就是如何将打印的数据添加到新的窗口中,可以通过新窗口实例对象的loadURL方法,这个方法传递一个我们要展示内容的路径。printURLCustom方法中可以看到路径就是项目打包根页面,而后面的hash就是我们路由的hash模式路由。 所以我们可以写一个print组件,然后为这个组件增加print路由。这样我们打开的新窗口显示就是print组件的内容。

openPrintWindow订阅的参数会作为路由的query参数拼接到路径上面,print组件就可以拿到点击组件打印传递的参数。

// router.ts
{path: "/print",name: "打印",component: () => import("@renderer/views/print/index.vue")},
// path.ts
/**
 * 获取真正的地址
 *
 * @param {string} devPath 开发环境路径
 * @param {string} proPath 生产环境路径
 * @param {string} [hash=""] hash值
 * @param {string} [search=""] search值
 * @return {*}  {string} 地址
 */
function getUrl(
  devPath: string,
  proPath: string,
  hash: string = "",
  search: string = ""
): string {
  const url = isDev
    ? new URL(`http://localhost:${process.env.PORT}`)
    : new URL("file://");
  url.pathname = isDev ? devPath : proPath;
  url.hash = hash;
  url.search = search;
  return url.href;
}
export const printURLCustom = (id) =>
  getUrl("",join(__dirname, "..", "renderer", "index.html"),`#/print?id=${id}`);
​
// printHandle.ts
let win: BrowserWindow;
const otherWindowConfig: BrowserWindowConstructorOptions = {
  height: 595,
  useContentSize: true,
  width: 1140,
  autoHideMenuBar: true,
  minWidth: 842,
  frame: false,
  show: false,
  webPreferences: {
    contextIsolation: false,
    nodeIntegration: true,
    webSecurity: false,
    // 如果是开发模式可以使用devTools
    devTools: process.env.NODE_ENV === "development",
    // 在macos中启用橡皮动画
    scrollBounce: process.platform === "darwin",
  },
};
export function openPrintWindow(id) {
  if (win) {
    win.hide(); /*测试*/
    win.destroy();
    return;
  }
  win = new BrowserWindow({
    titleBarStyle: "hidden",
    ...Object.assign(otherWindowConfig, {}),
  });
  win.loadURL(printURLCustom(id));
  win.setMenu(null);
  win.on("ready-to-show", () => {
    win.hide();
  });
  win.on("closed", () => {
    win = null;
  });
}
​

3、创建点击打印组件

这里是通过ipcRenderer渲染进程携带参数发布openPrintWindow事件。这里需要注意的是ipcRenderer要使用commonjs引入,否则会报错TypeError: path.join is not a function. 但是浏览器不支持commonjs,所以非常不建议在浏览器上跑这个项目。

<template>
    <div @click=print>打印</div>
</template>
<script lang="ts" setup>
  const { ipcRenderer } = require("electron"); // 浏览器环境报错
  const onPrint = async () => {
    await ipcRenderer.invoke("openPrintWindow", 123123); // 创建新窗口并传递数据
  };
</script>

4、创建打印组件

在打印新窗口时虽然设置了frame:false,但是标题的关闭、最小化这些按钮还存在,导致打印也会把按钮打印到页面。我没有找到完美的解决办法,所以直接删除了DOM。

参数可以通过route.query拿到点击打印按钮传递的参数。

通过ipcRenderer渲染进程发布printHandlePrint事件,传递参数为的打印机配置。其中启用了静默打印和去除打印页面边距。

静默打印是指用默认的打印机打印出所有页并且不希望弹出对话框进行设置,所以在打印的时候要设置好系统默认打印机。

最后在打印完成时发布destroyPrintWindow事件,销毁掉打印新窗口。

<template>
    <div>我要被打印了</div>
</template>
<script lang="ts" setup>
  import { useRoute } from "vue-router";
  import { ref, onMounted  } from "vue";
  
  const { ipcRenderer } = require("electron"); // 浏览器环境报错
  
  const route = useRoute();
  const id = route.query.id as string; // 接收参数
​
  onMounted(async () => {
    const windowTitle = window.document.querySelector(".window-title ");
    windowTitle && windowTitle.remove(); // 删除顶部标题关闭按钮
    // await getDataApi(id); // 获取数据
    try {
      await ipcRenderer.invoke("printHandlePrint", {
        silent: true, // 静默打印
        margins: { marginType: "none" }, // 网页的边距
      }); 
    } catch (error) {
    } finally {
      await ipcRenderer.invoke("destroyPrintWindow"); // 打印完成销毁新窗口
    }
  });
</script>

总结 

到此这篇关于electron实现打印功能支持静默打印、无感打印的文章就介绍到这了,更多相关electron打印功能内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • vue 实现在同一界面实现组件的动态添加和删除功能

    vue 实现在同一界面实现组件的动态添加和删除功能

    这篇文章主要介绍了vue 实现在同一界面实现组件的动态添加和删除,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-06-06
  • Vue lazyload图片懒加载实例详解

    Vue lazyload图片懒加载实例详解

    本文通过实例代码给大家介绍了Vue lazyload图片懒加载的相关知识,通过导入配置等操作src/main.jswenj ,代码简单易懂,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2021-12-12
  • vue3中refs用法举例小结

    vue3中refs用法举例小结

    这篇文章主要给大家介绍了关于vue3中refs用法举例的相关资料,Vue.js中的$refs是一个对象,它持有已注册过ref特性的所有组件和元素,需要的朋友可以参考下
    2023-08-08
  • element-ui在table中如何禁用其中几行

    element-ui在table中如何禁用其中几行

    这篇文章主要介绍了element-ui在table中如何禁用其中几行问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-10-10
  • vue中watch监听不到变化的解决

    vue中watch监听不到变化的解决

    本文主要介绍了vue中watch监听不到变化的解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-01-01
  • vue如何引入sass全局变量

    vue如何引入sass全局变量

    sass或者less都提供变量设置,在需求切换主题的项目中使用less或者sass变量,这篇文章主要介绍了vue引入sass全局变量,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-06-06
  • vue使用ajax获取后台数据进行显示的示例

    vue使用ajax获取后台数据进行显示的示例

    今天小编就为大家分享一篇vue使用ajax获取后台数据进行显示的示例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-08-08
  • vue3如何加载本地图片等静态资源浅析

    vue3如何加载本地图片等静态资源浅析

    在最近新起的项目中,用到了较新的技术栈vue3.2+vite+ts,跟着网上的写法渐渐上手了,下面这篇文章主要给大家介绍了关于vue3如何加载本地图片等静态资源的相关资料,需要的朋友可以参考下
    2023-04-04
  • ruoyi-vue3 集成aj-captcha实现滑块、文字点选验证码功能

    ruoyi-vue3 集成aj-captcha实现滑块、文字点选验证码功能

    这篇文章主要介绍了 ruoyi-vue3 集成aj-captcha实现滑块、文字点选验证码,本文基于后端RuoYi-Vue 3.8.7 和 前端 RuoYi-Vue3 3.8.7,集成以AJ-Captcha文字点选验证码为例,不需要键盘手动输入,极大优化了传统验证码用户体验不佳的问题,感兴趣的朋友一起看看吧
    2023-12-12
  • Vue element-ui父组件控制子组件的表单校验操作

    Vue element-ui父组件控制子组件的表单校验操作

    这篇文章主要介绍了Vue element-ui父组件控制子组件的表单校验操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-07-07

最新评论