diff --git a/app/controller/test.js b/app/controller/test.js deleted file mode 100644 index c2f797d..0000000 --- a/app/controller/test.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict'; - -const BaseController = require('./base'); - -class TestController extends BaseController { - async index() { - const { app, ctx, service } = this; - const query = ctx.request.query; - console.log('env:%j', app.config.env); - const res = 0; - const data = { - env: app.config.env, - }; - - - - console.log('res:%j', res); - this.sendSuccess(data, 'ok'); - } -} - -module.exports = TestController; diff --git a/app/controller/v1/example.js b/app/controller/v1/example.js index c91cd6f..8f45cb6 100644 --- a/app/controller/v1/example.js +++ b/app/controller/v1/example.js @@ -7,6 +7,20 @@ const path = require('path'); class ExampleController extends BaseController { + /** + * test electron api + */ + async testElectronApi() { + const { ctx, service } = this; + const body = ctx.request.body; + const id = body.id; + const data = {}; + + await service.example.testElectronApi(id); + + this.sendSuccess(data); + } + async openLocalDir() { const self = this; const { ctx, service } = this; @@ -211,6 +225,28 @@ class ExampleController extends BaseController { this.sendSuccess(data); } + + /** + * 显示消息对话框 + */ + async messageShow() { + const { service } = this; + const data = {}; + await service.example.messageShow(); + + this.sendSuccess(data); + } + + /** + * 显示消息对话框和确认 + */ + async messageShowConfirm() { + const { service } = this; + const data = {}; + await service.example.messageShowConfirm(); + + this.sendSuccess(data); + } } module.exports = ExampleController; diff --git a/app/router/example.js b/app/router/example.js index 4a089ba..62b22e1 100644 --- a/app/router/example.js +++ b/app/router/example.js @@ -33,4 +33,11 @@ module.exports = app => { router.post('/api/v1/example/openSoftware', controller.v1.example.openSoftware); // select file dir router.post('/api/v1/example/selectFileDir', controller.v1.example.selectFileDir); + // test some electron api + router.post('/api/v1/example/testElectronApi', controller.v1.example.testElectronApi); + // message show + router.post('/api/v1/example/messageShow', controller.v1.example.messageShow); + // message show confirm + router.post('/api/v1/example/messageShowConfirm', controller.v1.example.messageShowConfirm); + }; \ No newline at end of file diff --git a/app/service/example.js b/app/service/example.js index d703997..01e8133 100644 --- a/app/service/example.js +++ b/app/service/example.js @@ -91,7 +91,27 @@ class ExampleService extends BaseService { } return result.data; + } + + async testElectronApi(id = 0) { + await this.ipcCall('example.testElectronApi'); + + return null; + } + + async messageShow() { + await this.ipcCall('example.messageShow'); + + return true; } + + async messageShowConfirm() { + await this.ipcCall('example.messageShowConfirm'); + + return true; + } + + } module.exports = ExampleService; \ No newline at end of file diff --git a/app/service/test.js b/app/service/test.js deleted file mode 100644 index 47d90b2..0000000 --- a/app/service/test.js +++ /dev/null @@ -1,7 +0,0 @@ -'use strict'; - -const BaseService = require('./base'); - -class TestService extends BaseService {} - -module.exports = TestService; diff --git a/electron/apis/example.js b/electron/apis/example.js index 19d148a..b931212 100644 --- a/electron/apis/example.js +++ b/electron/apis/example.js @@ -1,5 +1,9 @@ 'use strict'; +/** + * egg服务调用electron功能时,建议使用该模块 + */ + const path = require('path'); const fs = require('fs'); const _ = require('lodash'); @@ -7,7 +11,6 @@ const {exec} = require('child_process'); const {app, webContents, shell, dialog} = require('electron'); const AutoLaunchManager = require('../lib/autoLaunch'); const shortcut = require('../lib/shortcut'); -const eLogger = require('../lib/eLogger').get(); /** * app根目录 @@ -107,8 +110,8 @@ exports.openSoftware = function (softName = '') { /** * 选择目录 */ - exports.selectDir = function () { - var filePaths = dialog.showOpenDialogSync({ +exports.selectDir = function () { + const filePaths = dialog.showOpenDialogSync({ properties: ['openDirectory', 'createDirectory'] }); console.log('[example] [selectDir] filePaths:', filePaths); @@ -119,6 +122,50 @@ exports.openSoftware = function (softName = '') { return filePaths[0]; } +/** + * 测试用的 - 忽略 + */ +exports.testElectronApi = function () { + const filePaths = dialog.showSaveDialogSync({ + properties: ['openFile', 'multiSelections'] + }); + console.log('[example] [testElectronApi] filePaths:', filePaths); + + return true; +} + +/** + * 显示消息对话框 + */ +exports.messageShow = function () { + dialog.showMessageBoxSync({ + type: 'info', // "none", "info", "error", "question" 或者 "warning" + title: '自定义标题-message', + message: '自定义消息内容', + detail: '其它的额外信息' + }) + + return true; +} + +/** + * 显示消息对话框和确认 + */ +exports.messageShowConfirm = function () { + const res = dialog.showMessageBoxSync({ + type: 'info', + title: '自定义标题-message', + message: '自定义消息内容', + detail: '其它的额外信息', + cancelId: 1, // 用于取消对话框的按钮的索引 + defaultId: 0, // 设置默认选中的按钮 + buttons: ['确认', '取消'], // 按钮及索引 + }) + console.log('[example] [messageShowConfirm] 结果:', res, res === 0 ? '点击确认按钮' : '点击取消按钮'); + + return true; +} + function getElectronPath(filepath) { //filepath = path.resolve(filepath); filepath = filepath.replace("resources", ""); diff --git a/electron/ipc/example.js b/electron/ipc/example.js index 6c53ca5..c764721 100644 --- a/electron/ipc/example.js +++ b/electron/ipc/example.js @@ -1,8 +1,71 @@ -const { answerRenderer } = require('./index') +'use strict'; -answerRenderer('example.hello', async (msg) => { +/** + * 前端(html)调用electron功能时,建议使用该模块 + * + * 定义的function 接收三个参数 + * @param event ipcMain事件对象 + * @param channel 频道 + * @param arg 接收到的消息 + */ + +const {dialog} = require('electron'); + +let myTimer = null; + +exports.hello = function (event, channel, msg) { let newMsg = msg + " +1" let reply = '' reply = '收到:' + msg + ',返回:' + newMsg return reply -}) +} + +exports.messageShow = function (event, channel, arg) { + dialog.showMessageBoxSync({ + type: 'info', // "none", "info", "error", "question" 或者 "warning" + title: '自定义标题-message', + message: '自定义消息内容', + detail: '其它的额外信息' + }) + + return '打开了消息框'; +} + +exports.messageShowConfirm = function (event, channel, arg) { + const res = dialog.showMessageBoxSync({ + type: 'info', + title: '自定义标题-message', + message: '自定义消息内容', + detail: '其它的额外信息', + cancelId: 1, // 用于取消对话框的按钮的索引 + defaultId: 0, // 设置默认选中的按钮 + buttons: ['确认', '取消'], // 按钮及索引 + }) + let data = (res === 0) ? '点击确认按钮' : '点击取消按钮'; + console.log('[electron] [example] [messageShowConfirm] 结果:', res, ); + + return data; +} + +/** + * 长消息 - 开始 + */ +exports.socketMessageStart = function (event, channel, arg) { + // 每隔1秒,向前端页面发送消息 + // 用定时器模拟 + myTimer = setInterval(function(e, c, msg) { + let timeNow = Date.now(); + let data = msg + ':' + timeNow; + e.reply(`${c}`, data) + }, 1000, event, channel, arg) + + return '开始了' +} + +/** + * 长消息 - 停止 + */ +exports.socketMessageStop = function () { + clearInterval(myTimer); + return '停止了' +} \ No newline at end of file diff --git a/electron/ipc/index.js b/electron/ipc/index.js deleted file mode 100644 index 1c4b5d3..0000000 --- a/electron/ipc/index.js +++ /dev/null @@ -1,40 +0,0 @@ -const { ipcMain: ipc } = require('electron') -const path = require('path') -const fs = require('fs') - -/** - * 发送响应信息给渲染进程 - * @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.setup = () => { - 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) - } - }) -} diff --git a/electron/lib/api.js b/electron/lib/api.js index 15b31fb..4a829db 100644 --- a/electron/lib/api.js +++ b/electron/lib/api.js @@ -7,7 +7,6 @@ const _ = require('lodash'); const storage = require('./storage'); const socketIo = require('socket.io'); const eLogger = require('./eLogger').get(); -// const {app} = require('electron'); const apis = {}; @@ -112,5 +111,4 @@ function setApi() { */ function getApiName (jsname, method) { return jsname + '.' + method; - //return jsname + method.charAt(0).toUpperCase() + method.slice(1); } diff --git a/electron/lib/ipcMain.js b/electron/lib/ipcMain.js new file mode 100644 index 0000000..e236b11 --- /dev/null +++ b/electron/lib/ipcMain.js @@ -0,0 +1,68 @@ +const { ipcMain: ipc } = require('electron') +const path = require('path') +const fs = require('fs') +const _ = require('lodash'); + +/** + * 发送响应信息给渲染进程 + * @param event + * @param channel + * @param data + * @private + */ +const _echo = (event, channel, data) => { + console.log('[ipc] [answerRenderer] result: ', {channel, data}) + event.reply(`${channel}`, data) +} + +/** + * 执行主进程函数,并响应渲染进程 + * @param channel + * @param callback + */ +const answerRenderer = (channel, callback) => { + ipc.on(channel, async (event, param) => { + const result = await callback(event, channel, param) + _echo(event, channel, result) + }) +} + +/** + * get api method name + * ex.) jsname='user' method='get' => 'user.get' + * @param {String} jsname + * @param {String} method + */ +const getApiName = (jsname, method) => { + return jsname + '.' + method; +} + +/** + * 加载所有的主程序 + */ +exports.setup = () => { + const ipcDir = path.normalize(__dirname + '/../ipc'); + fs.readdirSync(ipcDir).forEach(function (filename) { + if (path.extname(filename) === '.js' && filename !== 'index.js') { + const name = path.basename(filename, '.js'); + const fileObj = require(`../ipc/${filename}`); + _.map(fileObj, function(fn, method) { + let methodName = getApiName(name, method); + answerRenderer(methodName, fn); + }); + } + }) +} + + + +// exports.setup = () => { +// const ipcDir = path.normalize(__dirname + '/../ipc'); + +// fs.readdirSync(ipcDir).forEach(function (filename) { +// if (path.extname(filename) === '.js' && filename !== 'index.js') { +// const filePath = path.join(ipcDir, filename) +// require(filePath) +// } +// }) +// } diff --git a/electron/setup.js b/electron/setup.js index 1abd2a4..1b9fc1d 100644 --- a/electron/setup.js +++ b/electron/setup.js @@ -1,10 +1,10 @@ 'use strict'; -const storage = require('./lib/storage'); -const config = require('./config'); const is = require('electron-is'); +const config = require('./config'); +const storage = require('./lib/storage'); const api = require('./lib/api'); -const ipc = require('./ipc'); +const ipc = require('./lib/ipcMain'); const eLogger = require('./lib/eLogger'); const crash = require('./lib/crashReport'); diff --git a/frontend/src/api/main.js b/frontend/src/api/main.js index b8e6f6e..5447573 100644 --- a/frontend/src/api/main.js +++ b/frontend/src/api/main.js @@ -11,6 +11,9 @@ const mainApi = { autoLaunchIsEnabled: '/api/v1/example/autoLaunchIsEnabled', openSoftware: '/api/v1/example/openSoftware', selectFileDir: '/api/v1/example/selectFileDir', + messageShow: '/api/v1/example/messageShow', + messageShowConfirm: '/api/v1/example/messageShowConfirm', + testElectronApi: '/api/v1/example/testElectronApi', } /** diff --git a/frontend/src/config/router.config.js b/frontend/src/config/router.config.js index 788807d..84b90a8 100644 --- a/frontend/src/config/router.config.js +++ b/frontend/src/config/router.config.js @@ -43,6 +43,11 @@ export const constantRouterMap = [ name: 'DemoSystemIndex', component: () => import('@/views/demo/system/Index') }, + { + path: '/demo/testapi/index', + name: 'DemoTestApiIndex', + component: () => import('@/views/demo/testapi/Index') + }, ] }, { diff --git a/frontend/src/layouts/DemoMenu.vue b/frontend/src/layouts/DemoMenu.vue index 20bea4d..f01a5c0 100644 --- a/frontend/src/layouts/DemoMenu.vue +++ b/frontend/src/layouts/DemoMenu.vue @@ -48,12 +48,18 @@ export default { pageName: 'DemoSystemIndex', params: {} }, - 'menu_900' : { + 'menu_800' : { icon: 'profile', title: '快捷键', pageName: 'DemoShortcutIndex', params: {} }, + 'menu_900' : { + icon: 'profile', + title: '测试', + pageName: 'DemoTestApiIndex', + params: {} + }, } }; }, diff --git a/frontend/src/main.js b/frontend/src/main.js index a9b89c5..97debf0 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -4,15 +4,15 @@ import 'ant-design-vue/dist/antd.less'; import App from './App' import router from './router' import { VueAxios } from './utils/request' -import InjectIpc from '@/utils/injectIpc' +import IpcRenderer from '@/utils/ipcRenderer' import HotKeyInput from '@/utils/shortcut/index.js' Vue.use(antd) // mount axios to `Vue.$http` and `this.$http` Vue.use(VueAxios) // 全局注入IPC -Vue.use(InjectIpc) - +Vue.use(IpcRenderer) +// 快捷键框组件 Vue.use(HotKeyInput) Vue.config.productionTip = false diff --git a/frontend/src/utils/injectIpc.js b/frontend/src/utils/ipcRenderer.js similarity index 68% rename from frontend/src/utils/injectIpc.js rename to frontend/src/utils/ipcRenderer.js index 1b85cc7..c128d3c 100644 --- a/frontend/src/utils/injectIpc.js +++ b/frontend/src/utils/ipcRenderer.js @@ -11,16 +11,17 @@ const callMain = (ipc, channel, param) => { return new Promise((resolve) => { // 声明渲染进程函数, 用于主进程函数回调, 返回数据 // 调用主进程函数 - ipc.send(channel, param) - ipc.once(channel, (event, result) => { + ipc.on(channel, (event, result) => { + console.log('[ipcRenderer] [callMain] result:', result) resolve(result) }) + ipc.send(channel, param) }) } export default { install(Vue) { Vue.prototype.$ipc = ipc // 全局注入ipc - Vue.prototype.$callMain = (channel, param) => callMain(ipc, channel, param) // 全局注入调用主进程函数的方法 + Vue.prototype.$ipcCallMain = (channel, param) => callMain(ipc, channel, param) // 全局注入调用主进程函数的方法 } } diff --git a/frontend/src/utils/request.js b/frontend/src/utils/request.js index cb89958..ebdcd98 100644 --- a/frontend/src/utils/request.js +++ b/frontend/src/utils/request.js @@ -7,7 +7,7 @@ import { VueAxios } from './axios' const request = axios.create({ // API 请求的默认前缀 baseURL: process.env.VUE_APP_API_BASE_URL, - timeout: 6000 // 请求超时时间 + timeout: 60000 // 请求超时时间 }) // 异常拦截处理器 diff --git a/frontend/src/utils/util.js b/frontend/src/utils/util.js deleted file mode 100644 index 165630e..0000000 --- a/frontend/src/utils/util.js +++ /dev/null @@ -1,68 +0,0 @@ -export function timeFix () { - const time = new Date() - const hour = time.getHours() - return hour < 9 ? '早上好' : hour <= 11 ? '上午好' : hour <= 13 ? '中午好' : hour < 20 ? '下午好' : '晚上好' -} - -export function welcome () { - const arr = ['休息一会儿吧', '准备吃什么呢?', '要不要打一把 DOTA', '我猜你可能累了'] - const index = Math.floor(Math.random() * arr.length) - return arr[index] -} - -/** - * 触发 window.resize - */ -export function triggerWindowResizeEvent () { - const event = document.createEvent('HTMLEvents') - event.initEvent('resize', true, true) - event.eventType = 'message' - window.dispatchEvent(event) -} - -export function handleScrollHeader (callback) { - let timer = 0 - - let beforeScrollTop = window.pageYOffset - callback = callback || function () {} - window.addEventListener( - 'scroll', - event => { - clearTimeout(timer) - timer = setTimeout(() => { - let direction = 'up' - const afterScrollTop = window.pageYOffset - const delta = afterScrollTop - beforeScrollTop - if (delta === 0) { - return false - } - direction = delta > 0 ? 'down' : 'up' - callback(direction) - beforeScrollTop = afterScrollTop - }, 50) - }, - false - ) -} - -export function isIE () { - const bw = window.navigator.userAgent - const compare = (s) => bw.indexOf(s) >= 0 - const ie11 = (() => 'ActiveXObject' in window)() - return compare('MSIE') || ie11 -} - -/** - * Remove loading animate - * @param id parent element id or class - * @param timeout - */ -export function removeLoadingAnimate (id = '', timeout = 1500) { - if (id === '') { - return - } - setTimeout(() => { - document.body.removeChild(document.getElementById(id)) - }, timeout) -} - \ No newline at end of file diff --git a/frontend/src/views/demo/file/Index.vue b/frontend/src/views/demo/file/Index.vue index 17435f9..65deaa5 100644 --- a/frontend/src/views/demo/file/Index.vue +++ b/frontend/src/views/demo/file/Index.vue @@ -35,19 +35,16 @@
- 2. 打开文件夹 + 2. 系统原生对话框
- - - - - 打开 - - - - + + 消息提示(ipc) + 消息提示与确认(ipc) + 消息提示(http) + 消息提示与确认(http) +
@@ -65,6 +62,24 @@ +
+
+ + 4. 打开文件夹 + +
+
+ + + + + 打开 + + + + +
+ @@ -137,16 +152,52 @@ export default { this.$message.error(`${info.file.name} file upload failed.`); } }, - selectDir() { + selectDir() { localApi('selectFileDir', {}).then(res => { if (res.code !== 0) { return false } - console.log('res.data.dir:', res.data.dir) + console.log('res.data.dir:', res.data.dir) this.dir_path = res.data.dir; }).catch(err => { this.$message.error('异常') }) + }, + messageShow(type) { + const self = this; + console.log('[messageShow] type:', type) + if (type == 'http') { + localApi('messageShow', {}).then(res => { + if (res.code !== 0) { + return false + } + console.log('res:', res) + }).catch(err => { + self.$message.error(err + '异常') + }) + } else { + self.$ipcCallMain('example.messageShow', '').then(r => { + self.$message.info(r); + }) + } + }, + messageShowConfirm(type) { + const self = this; + console.log('[messageShowConfirm] type:', type) + if (type == 'http') { + localApi('messageShowConfirm', {}).then(res => { + if (res.code !== 0) { + return false + } + console.log('res:', res) + }).catch(err => { + self.$message.error(err + '异常') + }) + } else { + self.$ipcCallMain('example.messageShowConfirm', '').then(r => { + self.$message.info(r); + }) + } }, } }; @@ -163,5 +214,8 @@ export default { .one-block-2 { padding-top: 10px; } + .footer { + padding-top: 10px; + } } diff --git a/frontend/src/views/demo/socket/Index.vue b/frontend/src/views/demo/socket/Index.vue index 4f54434..8fa6147 100644 --- a/frontend/src/views/demo/socket/Index.vue +++ b/frontend/src/views/demo/socket/Index.vue @@ -28,6 +28,18 @@ +
+ + 3. 长消息: 服务端持续向前端页面发消息 + +
+
+ + 开始 + 结束 + 结果:{{ socketMessageString }} + +
diff --git a/frontend/src/views/demo/testapi/Index.vue b/frontend/src/views/demo/testapi/Index.vue new file mode 100644 index 0000000..d3b7004 --- /dev/null +++ b/frontend/src/views/demo/testapi/Index.vue @@ -0,0 +1,51 @@ + + + diff --git a/update.md b/update.md index 11f3699..77f200b 100644 --- a/update.md +++ b/update.md @@ -3,10 +3,12 @@ 2. demo增加分类 3. demo界面优化,重新排版 4. 增加选择文件夹目录 -5. 修复拉伸窗口空白 -6. 路由分类 -7. 删除非必要代码 -8. 软件名称统一 +5. 增加ipc通信模块与apis模块语法统一 +6. 增加路由分类 +7. 增加ipc支持长通信,服务端持续向页面发消息 +8. 增加操作系统弹框demo +9. 删除非必要代码 +10. 修复拉伸窗口空白 ## 1.13.0 1. 修复自动更新