当前位置:   article > 正文

electron+vue3 进行点击关闭按钮时,弹出自定义弹出框_electron点击菜单弹出窗口

electron点击菜单弹出窗口

视频效果展示

准备前提

开始前,首先你要先弄明白什么是主进程,什么是渲染进程。

主进程

  • 启动后一直存在的,相当于一个树的主干
  • 并不会展示出来,是看不到的
  • 所有跟系统资源交互的操作都在这里进行
  • 操控渲染进程,新建或销毁一个渲染进程

渲染进程

  • 渲染进程是一个个的浏览器窗口,就是用于展示
  • 渲染进程就是能看到的界面
  • 如果需要跟系统交互,就需要用 ipcRenderer 跟主进程进行数据交互。

注意:electron框架运行起来以后,主动创建的一个窗口,我们可以称其为:主窗口

思路流程

  1. 在主窗口创建的过程中,我们可以为其绑定通用的窗口事件,在其绑定的窗口事件中,为其加上窗口关闭监听的操作,然后在窗口关闭监听的过程中,先判断一下当前关闭的窗口是否为主窗口,如果不是则需要在group 中删除其对应的值(会默认直接关闭),如果是主窗口 则先 由主进程向渲染进程发送消息(渲染进程收到后为 触发了关闭事件),然后 阻止窗口默认关闭事件 e.preventDefault();
  2. 在默认的窗口参数中去除 默认的顶部导航 frame: false,然后自己封装一个顶部的导航,在自己封装的导航中 监听接收 主进程发送过来的消息,监听到以后实现自己相对应的逻辑

下面为我的部分代码展示

  1. //主进程
  2. /**
  3. * 创建窗口
  4. * @param windowConfig 窗口创建参数
  5. */
  6. createWindows(windowConfig: IWindowConfig & IWindowsZi): BrowserWindow {
  7. // 先通过key判断是否已窗口,有则聚焦
  8. let windowKey = windowConfig.key;
  9. let isMainWin = windowConfig.isMainWin; // 是否主窗口(当为true时会替代当前主窗口)
  10. if (windowKey && windowKey.length > 0) {
  11. /// 先从窗口组中取出记录
  12. const wg: WindowGroup = this.group.get(windowKey);
  13. if (wg) {
  14. /// 根据记录中的窗口id获取窗口,假如存在该窗口,则聚焦该窗口
  15. const oldWin = BrowserWindow.fromId(wg.windowId);
  16. if (oldWin) {
  17. oldWin.focus();
  18. return oldWin;
  19. }
  20. }
  21. }
  22. // 创建窗口对象
  23. const win: BrowserWindow = new BrowserWindow(
  24. Object.assign({}, defaultWindowConfig, windowConfig)
  25. );
  26. // 将窗口的关键信息与key关联,存入窗口组中
  27. windowKey = windowKey || win.id.toString();
  28. this.group.set(windowKey, {
  29. windowId: win.id,
  30. webContentsId: win.webContents.id,
  31. });
  32. // 是否主窗口
  33. if (isMainWin) {
  34. if (this.main) {
  35. // this.main.close();
  36. this.main.destroy();
  37. }
  38. this.main = win;
  39. }
  40. // 根据当前环境加载页面,并传递参数
  41. const param = windowConfig.param
  42. ? "?urlParamData=" + windowConfig.param
  43. : "";
  44. if (process.env.VITE_DEV_SERVER_URL) {
  45. // 如果是开发环境,则直接访问本地跑起的服务,拼接对应的路由
  46. win.loadURL(`${url}#${windowConfig.route}${param}`);
  47. } else {
  48. // 如果是线上环境,则加载html文件的路径,然后拼接路由
  49. win.loadFile(indexHtmlPath, { hash: windowConfig.route + param });
  50. }
  51. // win.webContents.openDevTools(); // 打开 开发者工具
  52. // 绑定通用窗口事件
  53. this.bindWindowEvent(win, windowConfig);
  54. // console.log(this.group);
  55. return win;
  56. }
  57. /**
  58. * 绑定窗口事件
  59. * @param win 窗口对象
  60. * @param windowConfig 窗口创建参数
  61. */
  62. bindWindowEvent(win: BrowserWindow | any, windowConfig: IWindowConfig) {
  63. win.once("ready-to-show", () => {
  64. win.show();
  65. });
  66. // 窗口关闭监听,此事件触发时,窗口即将关闭,可以拒绝关闭,此时窗口对象还未销毁
  67. win.on("close", (e: any) => {
  68. // let windowId = this.group.get(windowConfig.key).windowId;
  69. if (this.main == win) {
  70. win.webContents.send("mainSendToRender", { data: true });
  71. e.preventDefault();
  72. // win.destroy(); // 强制关闭窗口
  73. } else {
  74. // 设置窗口透明
  75. if (win != null) {
  76. win.setOpacity(0);
  77. const key = windowConfig.key || win.id.toString();
  78. this.group.delete(key);
  79. }
  80. }
  81. }); // 尝试关闭窗口
  82. // 此事件触发时,窗口已关闭,窗口对象已销毁
  83. win.on("closed", () => {
  84. // 在窗口对象被关闭时,取消订阅所有与该窗口相关的事件
  85. if (win != null) {
  86. win.removeAllListeners();
  87. // 引用置空
  88. win = null;
  89. }
  90. });
  91. }
  1. // 我自己封装的 顶部导航
  2. <template>
  3. <drag-tool>
  4. <div class="header">
  5. <div class="header-DIv">
  6. <a-button v-if="!minBidden" @click="zuiMin" type="text">
  7. <template #icon><MinusOutlined class="fontSizeTu" /></template>
  8. </a-button>
  9. <a-button
  10. v-if="!maxBidden"
  11. class="margin-ZY"
  12. @click="zuiMax"
  13. type="text"
  14. >
  15. <template #icon><BorderOutlined class="fontSizeTu-fang" /></template>
  16. </a-button>
  17. <a-button v-if="!closeBidden" @click="Guan" type="text" danger>
  18. <template #icon><CloseOutlined class="fontSizeTu" /></template>
  19. </a-button>
  20. </div>
  21. </div>
  22. </drag-tool>
  23. <a-modal
  24. v-model:open="visible"
  25. :maskClosable="false"
  26. centered
  27. :destroyOnClose="true"
  28. title="关闭提示"
  29. ok-text="确认"
  30. cancel-text="取消"
  31. width="400px"
  32. :footer="null"
  33. >
  34. <div style="display: flex; flex-direction: column">
  35. <div class="marginTop15">
  36. <a-radio-group v-model:value="closeValue" name="radioGroup">
  37. <a-radio :value="1">最小化到托盘</a-radio>
  38. <a-radio :value="2">直接退出</a-radio>
  39. </a-radio-group>
  40. </div>
  41. <div class="tanChu">
  42. <a-checkbox v-model:checked="closeChecked">不再提醒</a-checkbox>
  43. <div>
  44. <a-button type="primary" @click="hideModal">确认</a-button>
  45. </div>
  46. </div>
  47. </div>
  48. </a-modal>
  49. </template>
  50. <script setup lang="ts">
  51. import { reactive, ref, onMounted } from "vue";
  52. import { ipcRenderer } from "electron";
  53. import electronUtils from "@/utils/electronUtils";
  54. import cacheUtils from "@/utils/cacheUtils";
  55. import {
  56. MinusOutlined,
  57. BorderOutlined,
  58. CloseOutlined,
  59. } from "@ant-design/icons-vue";
  60. const closeChecked = ref(false);
  61. const closeValue = ref(1);
  62. const visible = ref(false);
  63. const props = defineProps({
  64. minBidden: {
  65. type: Boolean,
  66. default: () => {
  67. return false;
  68. },
  69. },
  70. maxBidden: {
  71. type: Boolean,
  72. default: () => {
  73. return false;
  74. },
  75. },
  76. closeBidden: {
  77. type: Boolean,
  78. default: () => {
  79. return false;
  80. },
  81. },
  82. });
  83. onMounted(() => {
  84. ipcRenderer.on("mainSendToRender", function (event, arg) {
  85. const a = cacheUtils.get("closeChecked");
  86. const b = cacheUtils.get("closeValue");
  87. if (a) {
  88. // 如果已选择 不再提醒
  89. switch (b) {
  90. case 1:
  91. electronUtils.miniMizeTray();
  92. break;
  93. case 2:
  94. electronUtils.allColTray();
  95. break;
  96. }
  97. } else {
  98. if (arg.data) {
  99. visible.value = true;
  100. }
  101. }
  102. });
  103. });
  104. // 点击最小化
  105. const zuiMin = () => {
  106. electronUtils.zuiMin();
  107. };
  108. // 点击最大化
  109. const zuiMax = () => {
  110. electronUtils.zuiMax();
  111. };
  112. // 点击关闭窗口
  113. const Guan = () => {
  114. electronUtils.closeGuan();
  115. };
  116. // 点击确认
  117. async function hideModal() {
  118. if (closeChecked.value) {
  119. cacheUtils.set("closeChecked", true); // 判断是否 不再提醒
  120. cacheUtils.set("closeValue", closeValue.value); // 选项
  121. }
  122. switch (closeValue.value) {
  123. case 1:
  124. visible.value = false;
  125. setTimeout(() => {
  126. electronUtils.miniMizeTray();
  127. }, 150);
  128. break;
  129. case 2:
  130. visible.value = false;
  131. electronUtils.allColTray();
  132. break;
  133. }
  134. }
  135. </script>
  136. <style lang="scss" scoped>
  137. .header {
  138. width: 100%;
  139. display: flex;
  140. flex-direction: row-reverse;
  141. // background-color: #fff;
  142. .header-DIv {
  143. margin: 8px 15px;
  144. display: flex;
  145. flex-direction: row;
  146. }
  147. }
  148. </style>

最终的实现效果

这里的话,我的代码里面 是已经实现了 这个关闭弹窗里面的 全部的功能。

我的窗口关闭的逻辑是这样的:我们可以先将窗口分为 主窗口和其他窗口,当我们打开了多个窗口的时候,点击其他窗口会直接关闭,只有点击主窗口的关闭按钮时,才会出来这个弹窗。当在主窗口中 直接退出的话,我们就需要退出所有的窗口,而点击其他窗口关闭时则不用,所以需要使用 app.quit(); 来退出。但是如果只写一个这个用来退出的话,你就会发现 这个弹窗会一直弹。这是因为 app.quit() 这个方法触发以后,会自动的去触发窗口事件中的 关闭窗口事件,所以,就需要在执行 app.quit 前,让this.main 不等于 主窗口的win,这样就可以很好的解决这个问题。

这里的this.main 是 我在创建 主窗口 或者 更改主窗口的过程中,存进去的当前的主窗口的 win。win 是指 每个窗口被创建时,自己的窗口对象。

到这里就结束啦~ 感谢大家的观看  如需有什么不懂的 欢迎评论区留言,写的哪不好的,也可以留言哦~

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

闽ICP备14008679号