Bläddra i källkod

Merge pull request #9 from i-CNNN/master

Demo:渲染进程与主进程使用原生IPC通信
Wallace Gao 4 år sedan
förälder
incheckning
ee572ef1df

+ 52 - 13
electron/apis/base.js

@@ -1,21 +1,60 @@
-'use strict';
-
-const AutoLaunchManager = require('../lib/AutoLaunch');
+'use strict'
+const { app, dialog } = require('electron')
+const AutoLaunchManager = require('../lib/AutoLaunch')
 
 
 exports.autoLaunchEnable = function () {
 exports.autoLaunchEnable = function () {
-  const autoLaunchManager = new AutoLaunchManager();
-  const enable = autoLaunchManager.enable();
-  return enable;
+  const autoLaunchManager = new AutoLaunchManager()
+  const enable = autoLaunchManager.enable()
+  return enable
 }
 }
 
 
 exports.autoLaunchDisable = function () {
 exports.autoLaunchDisable = function () {
-  const autoLaunchManager = new AutoLaunchManager();
-  const disable = autoLaunchManager.disable();
-  return disable;
+  const autoLaunchManager = new AutoLaunchManager()
+  const disable = autoLaunchManager.disable()
+  return disable
 }
 }
 
 
 exports.autoLaunchIsEnabled = function () {
 exports.autoLaunchIsEnabled = function () {
-  const autoLaunchManager = new AutoLaunchManager();
-  const isEnable = autoLaunchManager.isEnabled();
-  return isEnable;
-}
+  const autoLaunchManager = new AutoLaunchManager()
+  const isEnable = autoLaunchManager.isEnabled()
+  return isEnable
+}
+
+
+exports.appExit = function () {
+  app.exit()
+}
+
+exports.appRelaunch = function () {
+  app.relaunch()
+  app.exit()
+}
+
+
+/**
+ * 选择本地文件夹
+ * @param title 弹出框的标题
+ * @return {Promise<*>}
+ */
+exports.choiceFolder = async function (title = '') {
+  return await dialog.showOpenDialog({
+    properties: ['openDirectory'],
+    title: title
+  })
+}
+
+/**
+ * 选择本地文件
+ * @param title 弹出框的标题
+ * @param extensions 后缀名集合 e.g: ['exe','txt','png']
+ * @return {Promise<*>}
+ */
+exports.choiceFile = async function (title = '', extensions = []) {
+  return await dialog.showOpenDialog({
+    properties: ['openFile'],
+    filters: [{
+      extensions: extensions
+    }],
+    title: title
+  })
+}

+ 4 - 3
electron/config.js

@@ -37,6 +37,7 @@ const config = {
     minHeight: 600,
     minHeight: 600,
     webPreferences: {
     webPreferences: {
       //webSecurity: false,
       //webSecurity: false,
+      contextIsolation: false, // 设置此项为false后,才可在渲染进程中使用electron api
       nodeIntegration: true,
       nodeIntegration: true,
       preload: path.join(__dirname, '../preload.js')
       preload: path.join(__dirname, '../preload.js')
     },
     },
@@ -78,7 +79,7 @@ exports.get = function (flag = '', env = 'prod') {
   if (flag === 'webEgg') {
   if (flag === 'webEgg') {
     return config.egg;
     return config.egg;
   }
   }
-  
+
   if (flag === 'egg') {
   if (flag === 'egg') {
     const eggConfig = storage.getEggConfig();
     const eggConfig = storage.getEggConfig();
     if (env === 'prod' && eggConfig.port) {
     if (env === 'prod' && eggConfig.port) {
@@ -90,8 +91,8 @@ exports.get = function (flag = '', env = 'prod') {
   if (flag === 'autoUpdate') {
   if (flag === 'autoUpdate') {
     return config.autoUpdate;
     return config.autoUpdate;
   }
   }
-  
+
   return {};
   return {};
 };
 };
 
 
-exports = module.exports;
+exports = module.exports;

+ 6 - 0
electron/ipc/example.js

@@ -0,0 +1,6 @@
+const { answerRenderer } = require('./index')
+
+answerRenderer('example.test', async (name) => {
+  const luckNum = (Math.random()*1000).toFixed()
+  return `${name}, 你的幸运数字是:${luckNum}`
+})

+ 40 - 0
electron/ipc/index.js

@@ -0,0 +1,40 @@
+const { ipcMain: ipc } = require('electron')
+const path = require('path')
+const fs = require('fs-extra')
+
+/**
+ * 发送响应信息给渲染进程
+ * @param event
+ * @param channel
+ * @param data
+ * @private
+ */
+const _echo = (event, channel, data) => {
+  event.reply(`${channel}`, data)
+}
+
+/**
+ * 执行主进程函数,并响应渲染进程
+ * @param channel
+ * @param callback
+ */
+module.exports.answerRenderer = (channel, callback) => {
+  ipc.on(channel, async (event, param) => {
+    const result = await callback(param)
+    _echo(event, channel, result)
+  })
+}
+
+/**
+ * 加载所有的主程序
+ */
+module.exports.loadIPC = () => {
+  const ipcDir = path.normalize(__dirname + '/')
+
+  fs.readdirSync(ipcDir).forEach(function (filename) {
+    if (path.extname(filename) === '.js' && filename !== 'index.js') {
+      const filePath = path.join(ipcDir, filename)
+      require(filePath)
+    }
+  })
+}

+ 22 - 0
electron/ipc/system.js

@@ -0,0 +1,22 @@
+const { appExit, appRelaunch, choiceFile, choiceFolder } = require('../apis/base')
+const { answerRenderer } = require('./index')
+
+/**
+ * 退出app
+ */
+answerRenderer('system.exit', appExit)
+
+/**
+ * 重启app
+ */
+answerRenderer('system.relaunch', appRelaunch)
+
+/**
+ * 选择系统文件夹
+ */
+answerRenderer('system.choiceFolder', choiceFolder)
+
+/**
+ * 选择文件文件
+ */
+answerRenderer('system.choiceFile', choiceFile)

+ 4 - 1
electron/setup.js

@@ -5,6 +5,7 @@ const storage = require('./storage');
 const config = require('./config');
 const config = require('./config');
 const is = require('electron-is');
 const is = require('electron-is');
 const api = require('./api');
 const api = require('./api');
+const ipc = require('./ipc');
 
 
 module.exports = () => {
 module.exports = () => {
   storage.setup();
   storage.setup();
@@ -16,6 +17,8 @@ module.exports = () => {
     autoUpdater.setup();
     autoUpdater.setup();
   }
   }
   api.setup();
   api.setup();
+  // 加载所有的主进程函数
+  ipc.loadIPC();
 }
 }
 
 
 function logger () {
 function logger () {
@@ -32,4 +35,4 @@ function logger () {
   }
   }
 
 
   return true;
   return true;
-};
+};

+ 6 - 1
frontend/src/config/router.config.js

@@ -18,10 +18,15 @@ export const constantRouterMap = [
         component: () => import('@/views/file/UploadFile')
         component: () => import('@/views/file/UploadFile')
       },
       },
       {
       {
+        path: 'ipcExample',
+        name: 'IpcExample',
+        component: () => import('@/views/file/IpcExample')
+      },
+      {
         path: 'setting',
         path: 'setting',
         name: 'Setting',
         name: 'Setting',
         component: () => import('@/views/Setting')
         component: () => import('@/views/Setting')
       }
       }
     ]
     ]
   }
   }
-]
+]

+ 12 - 9
frontend/src/main.js

@@ -1,17 +1,20 @@
-import Vue from 'vue';
-import antd from 'ant-design-vue';
-import 'ant-design-vue/dist/antd.css';
-import App from './App';
-import router from './router';
+import Vue from 'vue'
+import antd from 'ant-design-vue'
+import 'ant-design-vue/dist/antd.css'
+import App from './App'
+import router from './router'
 import { VueAxios } from './utils/request'
 import { VueAxios } from './utils/request'
+import InjectIpc from '@/utils/injectIpc'
 
 
-Vue.use(antd);
+Vue.use(antd)
 // mount axios to `Vue.$http` and `this.$http`
 // mount axios to `Vue.$http` and `this.$http`
 Vue.use(VueAxios)
 Vue.use(VueAxios)
+// 全局注入IPC
+Vue.use(InjectIpc)
 
 
-Vue.config.productionTip = false;
+Vue.config.productionTip = false
 
 
 new Vue({
 new Vue({
   router,
   router,
-  render: h => h(App),
-}).$mount('#app');
+  render: h => h(App)
+}).$mount('#app')

+ 26 - 0
frontend/src/utils/injectIpc.js

@@ -0,0 +1,26 @@
+const { ipcRenderer: ipc } = window.require('electron')
+
+/**
+ * 异步调用主函数
+ * @param ipc
+ * @param channel
+ * @param param
+ * @returns {Promise<unknown>}
+ */
+const callMain = (ipc, channel, param) => {
+  return new Promise((resolve) => {
+    // 声明渲染进程函数, 用于主进程函数回调, 返回数据
+    // 调用主进程函数
+    ipc.send(channel, param)
+    ipc.once(channel, (event, result) => {
+      resolve(result)
+    })
+  })
+}
+
+export default {
+  install(Vue) {
+    Vue.prototype.$ipc = ipc // 全局注入ipc
+    Vue.prototype.$callMain = (channel, param) => callMain(ipc, channel, param) // 全局注入调用主进程函数的方法
+  }
+}

+ 5 - 0
frontend/src/views/Layout.vue

@@ -63,6 +63,11 @@ export default {
             title: '打开文件夹',
             title: '打开文件夹',
             pageName: 'FileOpenDir',
             pageName: 'FileOpenDir',
             params: {},
             params: {},
+          },
+          'subMenu_3' : {
+            title: 'IpcExample',
+            pageName: 'IpcExample',
+            params: {},
           }
           }
         },
         },
         'menu_2' : {
         'menu_2' : {

+ 45 - 0
frontend/src/views/file/IpcExample.vue

@@ -0,0 +1,45 @@
+<template>
+  <div>
+    <h3 :style="{ marginBottom: '16px' }">
+      渲染进程与主进程IPC通信Demo
+    </h3>
+    <a-list bordered>
+      <a-button @click="choiceFolder">选择本地文件夹</a-button>
+      <div>{{ choiceFolderInfo }}</div>
+      <a-button @click="choiceFile">选择本地文件</a-button>
+      <div>{{ choiceFileInfo }}</div>
+
+      <a-button @click="getMyLuckNum">摇一个幸运数字</a-button>
+      <div>{{ luckNum }}</div>
+    </a-list>
+  </div>
+</template>
+<script>
+const getMyLuckNum = require('./luckNum')
+export default {
+  data() {
+    return {
+      choiceFolderInfo: '',
+      choiceFileInfo: '',
+      luckNum: ''
+    }
+  },
+  methods: {
+    choiceFolder() {
+      this.$callMain('system.choiceFolder', '我要选择系统的文件夹').then(r => {
+        this.choiceFolderInfo = JSON.stringify(r)
+      })
+    },
+    choiceFile() {
+      this.$callMain('system.choiceFile', '我只选择excel文件', ['xlsx', 'xls']).then(r => {
+        this.choiceFileInfo = JSON.stringify(r)
+      })
+    },
+    getMyLuckNum() {
+      // 在外部js中调用主进程函数
+      getMyLuckNum(this, 'CNNN').then(r => this.luckNum = r)
+    }
+  }
+}
+</script>
+<style></style>

+ 5 - 0
frontend/src/views/file/luckNum.js

@@ -0,0 +1,5 @@
+function getMyLuckNum(vue, name) {
+  return vue.$callMain('example.test', name)
+}
+
+module.exports = getMyLuckNum