有一個web項目須要用客戶端來包裝一下 項目的主要業務都在服務器上 因此項目的大多數功能都用url 地址來訪問; 客戶端登錄界面在本地 打包客戶端的本地登錄界面 作爲登錄入口; electron 開發中 有一個概念須要理解 我暫且叫主進程嗎 main, 這個進程的控制 在項目的 配置 package.json 的main 字段中定義; main.js 做爲electron 控制的後端入口, 基本的主要控制都是在這個文件裏面定義 eletron 控制系統 的接口不少也在這裏面定義,詳細能夠查看官方API 文檔;
{ "name": "linksame", "version": "1.0.4", "description": "鄰盛管家客戶端", "main": "main.js", // 主進程控制定義 "scripts": { "start": "electron .", "dist": "electron-builder -wm", "build": "electron-builder", "test": "echo \"Error: no test specified\" && exit 1" },
使用到的nodejs 模塊:javascript
"electron-main-notification": "^1.0.1", // 右下角消息提醒 組件 "electron-notify": "^0.1.0",// 消息提醒組件 "electron-packager": "^10.1.2",// 項目打包 組件 打包爲獨立的綠色運行程序 "electron-squirrel-startup": "^1.0.0",// 升級包工具 用處不詳 "electron-updater": "^2.20.1",// 一個不錯的升級包 製做組件 咱們用它作的升級包 提醒 "gulp": "^3.9.1", // 工具鏈工具 本地開發 "nedb": "^1.8.0",// 一個數據庫組件 對象性質 nodejs "request": "^2.83.0" // 一個請求庫
項目用到 eletron 主要的模塊 說明php
const electron = require('electron') // //import { autoUpdater } from "electron-updater" const autoUpdater=require('electron-updater').autoUpdater // 升級包檢測 不一樣於官方默認的那個 autoUpdater.autoDownload = false // 配置取消自動下載 // Module to control application life. const app = electron.app // 項目基礎控制 // Module to create native browser window. const BrowserWindow = electron.BrowserWindow // 開窗口控制 const BrowserView = electron.BrowserView // 窗口控制另一種方式 const dialog = electron.dialog // 系統彈出地址框 const Tray = electron.Tray // 右下角任務欄 const Menu = electron.Menu //右下角任務欄 菜單控制 const Notification = electron.Notification // 消息提醒 const window = electron.window const ipcMain = require('electron').ipcMain // 重點 主進程通訊專用 發通道信息 監聽通道 const ipcRenderer = require('electron').ipcRenderer // 重點 子窗口 通訊專用 能夠在子窗口中 和 主進程通訊 //const storage = require('electron-json-storage') const {shell} = require('electron') // electron 基礎功能接口 例如 打開瀏覽器網頁 ; 打開文件 運行程序等等; const notify = require('electron-main-notification') // 也是一個消息對話框 const {session} = require('electron') // seesion cookie 控制 const fs = require('fs'); // nodejs 模塊 文件文本操做等等 io const nedb = require('nedb'); // 數據庫 一個nodejs 中能夠用的數據庫
const ipcMain = require('electron').ipcMain // 重點 主進程通訊專用 發通道信息 監聽通道
const ipcRenderer = require('electron').ipcRenderer // 重點 子窗口 通訊專用 能夠在子窗口中 和 主進程通訊html
// 例如 主穿褲中監聽 界面發來的命令 主進程中執行相應操做
ipcMain.on('window-min',function(){
mainWindow.minimize();
})
// 主進程發起送消息 其餘頁面會收到數據domianObj
mainWindow.webContents.send('islogin',domainObj)java
npm run build
參考 :https://www.electron.build/auto-updatenode
const autoUpdater=require('electron-updater').autoUpdater // 升級包檢查模塊 包含檢查升級包 下載 安裝等接口封裝 都在裏面web
autoUpdater.setFeedURL('http://www.linksame.com/release/');shell
升級包檢測 比較數據庫
app.getVersion() // 系統當前版本
// 檢查升級包接口 返回 Promise 對象
let checkinfo=autoUpdater.checkForUpdates();
//console.log('checkinfo:',checkinfo)
checkinfo.then(function(data){
console.log('datav:::',data.versionInfo.version)
console.log('datav2:::',data)
// 返回的data 爲網絡上版本最新版信息 可獲取系統版本號 和 最新版本比如較 肯定要部要升級
if(data.versionInfo.version!=app.getVersion()){
updateHandle();
//const UpdateInfo=require('electron-updater').UpdateInfo
//console.log('UpdateInfo:',)
}
})npm
異步變成 新規範 一些功能中用到了 例如 autoUpdater 升級管理 模塊的的一些接口 就是 該類型json
項目組控制 介紹說明
if (require('electron-squirrel-startup')) return; const electron = require('electron') //import { autoUpdater } from "electron-updater" const autoUpdater=require('electron-updater').autoUpdater autoUpdater.autoDownload = false // 配置取消自動下載 // Module to control application life. const app = electron.app // Module to create native browser window. const BrowserWindow = electron.BrowserWindow const BrowserView = electron.BrowserView const dialog = electron.dialog const Tray = electron.Tray const Menu = electron.Menu const Notification = electron.Notification const window = electron.window const ipcMain = require('electron').ipcMain const ipcRenderer = require('electron').ipcRenderer //const storage = require('electron-json-storage') const {shell} = require('electron') const notify = require('electron-main-notification') const {session} = require('electron') const fs = require('fs'); const nedb = require('nedb'); // 數據庫 const path = require('path') const url = require('url') const icologo=__dirname+'\\ioc\\ls.ico' // 域名 const domain="http://www.linksame.com"; const loginpath=domain+"/index.php?app=Core&m=Pcdlogin&network=1&ip=0" const request = require('request') // 判斷網絡配置 const options = { method: 'post', url:domain+'/release/network.json', form: "content", headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }; const message={ error:'check version error', checking:'check updateing ......', updateAva:'find a New Version,downloading ......', updateNotAva:'now it New best', }; const os = require('os'); // init obj let mainWindow let tray = null const app_data=app.getPath('userData') console.log('app_data',app_data) // 設置共享運行目錄 global.linksame = { runpath: app_data } // 實例化鏈接對象(不帶參數默認爲內存數據庫) const db = new nedb({ filename: path.join(app_data,'\\Cache\\downloadfile.db'), autoload: true }); let date=new Date(); const initobj={ width:1024, height:760, } function createWindow() { // Create the browser window. mainWindow = new BrowserWindow({ width: 712, height: 588, icon:icologo, title:'鄰盛企業管家', frame: false, transparent: true, webPreferences:{ nodeIntegration:true, nodeIntegrationInWorker:true, } }) //Menu.setApplicationMenu(null); const mainindex = path.join('file://', __dirname, 'index.html') mainWindow.loadURL(mainindex) mainWindow.once('ready-to-show', () => { mainWindow.show() }) // Open the DevTools. // mainWindow.webContents.openDevTools() // Emitted when the window is closed. mainWindow.on('closed', function() { mainWindow = null }) //捕獲新打開窗口事件 定製新窗口 mainWindow.webContents.on('new-window', (event, url) => { event.preventDefault() const win = new BrowserWindow({ width: 1024, height: 760, icon:__dirname+'./ioc/ls.ico', title:'鄰盛企業管家', frame: false, transparent: true, backgroundColor:'#4385F4', }) win.once('ready-to-show', () => win.show()) win.loadURL(url) //event.newGuest = win console.log('windowID:',win.id) ipcMain.on('sub-close',function(d){ console.log('d',d) win.hide() }) }) // download mainWindow.webContents.session.on('will-download', (event, item, webContents) => { // Set the save path, making Electron not to prompt a save dialog. item.on('updated', (event, state) => { if (state === 'interrupted') { console.log('Download is interrupted but can be resumed') } else if (state === 'progressing') { // set loading progressBar mainWindow.setProgressBar(item.getReceivedBytes() / item.getTotalBytes()); if (item.isPaused()) { console.log('Download is paused') } else { if(Math.ceil(item.getReceivedBytes()/1024/1024)===1){ let title='稍等 正在下載'; let body= item.getSavePath(); let ico=getico(item.getSavePath()); notifly(title,body,ico) } let download = `Received bytes: ${Math.ceil(item.getReceivedBytes()/1024/1024)} M / ${Math.ceil(item.getTotalBytes()/1024/1024)}M` console.log(`Received bytes: ${Math.ceil(item.getReceivedBytes()/1024/1024)} M / ${Math.ceil(item.getTotalBytes()/1024/1024)}M`) } } }) item.once('done', (event, state) => { if (state === 'completed') { const filepath=item.getSavePath(); var arr = filepath.split('\\'); let filename=arr[arr.length-1]; let title=filename+' 下載完成!'; let body= item.getSavePath()+' 打開'; let ico=getico(item.getSavePath()); //notifly(title,body,ico) notiflyclick(title,body,function(){ shell.openItem(filepath) }) console.log('Download successfully') db.insert({ name: filename, path:item.getSavePath(), datetime:date.toLocaleDateString(), sizes:Math.ceil(item.getTotalBytes()/1024/1024), }, (err, ret) => { console.log('insert successfully',err,ret) }); } else { console.log(`Download failed: ${state}`) } }) }) // 任務欄圖標菜單 A tray = new Tray(icologo) const contextMenu = Menu.buildFromTemplate([{ label: '幫助中心', type: 'normal', icon: __dirname+'\\ioc\\help.png', click: function() { shell.openExternal('http://help.linksame.com/') } }, { label: '官網', type: 'normal', icon: __dirname+'\\ioc\\web.png',click:function(){ shell.openExternal('http://www.linksame.com') }}, { label: '移動端', type: 'normal', icon:__dirname+'\\ioc\\phone.png',click:function(){ //shell.openExternal('http://www.linksame.com/index.php?app=Core&m=V7&a=download') const modalPath = path.join('file://', __dirname, 'qcode.html') //let win = new BrowserWindow({ width: 705, height: 250,resizable:false,autoHideMenuBar:true,type: 'desktop', icon: './ioc/download2.png' }) let win = new BrowserWindow({ title:'移動設備軟件下載', width: 250, height: 250, autoHideMenuBar:true, type: 'desktop', icon:__dirname+'\\ioc\\phone.png', resizable:false, maximizable:false, }) //win.setApplicationMenu(null); win.on('close', function() { win = null }) win.loadURL(modalPath) win.show() } }, { label: '下載管理', type: 'normal', icon:__dirname+'\\ioc\\down.png', click: function() { const modalPath = path.join('file://', __dirname, 'download.html') //let win = new BrowserWindow({ width: 705, height: 250,resizable:false,autoHideMenuBar:true,type: 'desktop', icon: './ioc/download2.png' }) let win = new BrowserWindow({ width: 705, height: 250, autoHideMenuBar:true, type: 'desktop', icon: __dirname+'\\ioc\\download2.png', resizable:false, maximizable:false, }) //win.setApplicationMenu(null); win.on('close', function() { win = null }) win.loadURL(modalPath) win.show() } }, { label: '設置', type: 'submenu', icon:__dirname+'\\ioc\\setting.png', submenu: [ { label: '開機啓動', type: 'normal', icon:__dirname+'\\ioc\\sysset.png' }, { label: '更新緩存', type: 'normal', icon:__dirname+'\\ioc\\clear-2.png',click:function(){ let cachepath=app_data+'/Cache' let dir=fs.readdir(cachepath,(err,file)=>{ for(v in file){ console.log('file',v) let rmnum=shell.moveItemToTrash(path.join(cachepath,file[v])) console.log('remove',rmnum) } notifly('緩存清理','緩存清理完成!',__dirname+'\\ioc\\ok.png') }) }}, ] }, { label: '升級', type: 'normal', icon: __dirname+'\\ioc\\upgrate.png',click:function(){ updateHandle(); } }, { label: '註銷', type: 'normal', icon: __dirname+'\\ioc\\zx.png', role: 'close',click:function(){ // console.log('siht',console.log(ses.getUserAgent())) // 查詢與指定 url 相關的全部 cookies. /*session.defaultSession.cookies.remove('http://www.linksame.com','sns_shell',function(cookies) { console.log('remove~~~~') });*/ session.defaultSession.cookies.get({url:domain}, function(error, cookies) { let domainObj=cookies for (var i in domainObj){ console.log(i,':',domainObj[i]) session.defaultSession.cookies.remove(domain,domainObj[i].name, function(data) { console.log('remove',data); }); } //console.log('ddd',domainObj) }); let newobj=session.defaultSession.cookies.get({ url : domain }, function(error, cookies) { console.log('login out coockie:',newobj) }); app.quit(); //session.cookies.remove("http://www.linksame.com", name, callback) }}, { label: '退出', type: 'normal', icon:__dirname+'\\ioc\\loginout.png', role: 'close',click:function(){ const dopt={ type:'question', title:'你肯定要退出嗎?', buttons:['肯定','取消'], defaultId:1, message:'退出後 會關閉鄰盛企業管家。', //icon:'./ioc/ls.ico', noLink:true, } dialog.showMessageBox(dopt,function(e){ console.log('e:',e) if(e==0){ app.quit(); }else{ console.log('nat close') } // }) }} ]) tray.setToolTip('鄰盛企業管家 你的企業好幫手!') tray.setContextMenu(contextMenu) } // This method will be called when Electron has finished // initialization and is ready to create browser windows. // Some APIs can only be used after this event occurs. app.on('ready', createWindow) // Quit when all windows are closed. app.on('window-all-closed', function() { // On OS X it is common for applications and their menu bar // to stay active until the user quits explicitly with Cmd + Q if (process.platform !== 'darwin') { app.quit() } }) app.on('activate', function() { // On OS X it's common to re-create a window in the app when the // dock icon is clicked and there are no other windows open. if (mainWindow === null) { createWindow() } }) //登陸窗口最小化 ipcMain.on('window-min',function(){ mainWindow.minimize(); }) //從新設置大小 ipcMain.on('window-reset',function(){ console.log(initobj.width, initobj.height) mainWindow.setSize(initobj.width, initobj.height) mainWindow.center() }) //登陸窗口最大化 ipcMain.on('window-max',function(data){ console.log('data',data) if(mainWindow.isMaximized()){ mainWindow.restore(); }else{ mainWindow.maximize(); } }) ipcMain.on('window-close',function(){ mainWindow.close(); }) // 檢查登錄 ipcMain.on('islogin',function(){ session.defaultSession.cookies.get({url:domain,name:'sns_username'}, function(error, cookies) { let domainObj=cookies // console.log('ddd',domainObj[0].value) mainWindow.webContents.send('islogin',domainObj) }); }) // 檢查網絡 ipcMain.on('testNetwork',function(){ request(options, function (err, res, body) { if (err) { console.log(err) }else { mainWindow.webContents.send('testNetwork',body) } }) }) // 註銷登錄 ipcMain.on('loginout',function(){ session.defaultSession.cookies.get({url:domain}, function(error, cookies) { let domainObj=cookies for (var i in domainObj){ console.log(i,':',domainObj[i]) session.defaultSession.cookies.remove(domain,domainObj[i].name, function(data) { console.log('remove',data); }); } //console.log('ddd',domainObj) }); //app.quit(); //mainWindow.loadURL(loginpath) }) // 消息通知 函數 function notifly(title,body,ico){ const opt = { icon: ico, title: title, body: body, } const m = new Notification(opt); m.show() } // 可點擊事件的通知 function notiflyclick(title,body,callback){ notify(title, { body: body }, () => { console.log('The notification got clicked on!') callback() }) } //自動獲取圖標 function getico(path){ let str = path.substring(path.lastIndexOf(".")+1); switch (str) { case 'doc': return __dirname+'\\ioc/format/doc.png'; break; case 'docx': return __dirname+'\\ioc/format/doc.png'; break; case 'xls': return __dirname+'\\ioc/format/excel.png'; break; case 'xlsx': return __dirname+'\\ioc/format/excel.png'; break; case 'csv': return __dirname+'\\ioc/format/excel.png'; break; case 'exe': return __dirname+'\\ioc/format/exe.png'; break; case 'html': return __dirname+'\\ioc/format/file_html.png'; break; case 'htm': return __dirname+'\\ioc/format/file_html.png'; break; case 'pptx': return __dirname+'\\ioc/format/ppt.png'; break; case 'ppx': return __dirname+'\\ioc/format/ppt.png'; break; case 'rar': return __dirname+'\\ioc/format/rar.png'; break; case 'zip': return __dirname+'\\ioc/format/zip.png'; break; case 'gz': return __dirname+'\\ioc/format/zip.png'; break; case 'tar': return __dirname+'\\ioc/format/zip.png'; break; case 'pdf': return __dirname+'\\ioc/format/pdf.png'; break; case 'png': return __dirname+'\\ioc/format/png.png'; break; case 'jpg': return __dirname+'\\ioc/format/jpg.png'; break; case 'gif': return __dirname+'\\ioc/format/gif.png'; break; default: return __dirname+'\\ioc/format/file.png'; break; } } //================ 升級 autoUpdater.setFeedURL('http://www.linksame.com/release/'); // 檢測更新,在你想要檢查更新的時候執行,renderer事件觸發後的操做自行編寫 function updateHandle(){ autoUpdater.on('error', function(error){ sendUpdateMessage(message.error) }); autoUpdater.on('checking-for-update', function(info) { console.log('checking for update:::',info) sendUpdateMessage(message.checking) }); autoUpdater.on('update-available', function(info) { console.log('update-available:::',info) upwin.webContents.send('checkinfo', info) sendUpdateMessage(message.updateAva) }); autoUpdater.on('update-not-available', function(info) { sendUpdateMessage(message.updateNotAva) }); // 更新下載進度事件 autoUpdater.on('download-progress', function(progressObj) { console.log('downloading:',progressObj) upwin.webContents.send('downloadProgress', progressObj) }) autoUpdater.on('update-downloaded', function (event, releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate) { ipcMain.on('isUpdateNow', (e, arg) => { //some code here to handle event autoUpdater.quitAndInstall(); }) console.log(releaseNotes, releaseName, releaseDate, updateUrl, quitAndUpdate) //upwin.webContents.send('isUpdateNow') }); //執行自動更新檢查 //autoUpdater.checkForUpdates(); ipcMain.on("checkForUpdate",()=>{ //執行自動更新檢查 appUpdater.autoDownload = false autoUpdater.checkForUpdates(); //console.log('checkinfo:',checkinfo) }) //執行下載 ipcMain.on("download",()=>{ autoUpdater.downloadUpdate() }) console.log('now check updateing ~~~~') // autoUpdater.checkForUpdates(); //open upgroud dialog const modalPath = path.join('file://', __dirname, 'upgroud.html') //let win = new BrowserWindow({ width: 705, height: 250,resizable:false,autoHideMenuBar:true,type: 'desktop', icon: './ioc/download2.png' }) let upwin = new BrowserWindow({ width: 705, height: 250, autoHideMenuBar:true, type: 'desktop', icon: __dirname+'\\ioc\\upgrate2.png', resizable:false, maximizable:false, }) //win.setApplicationMenu(null); upwin.on('close', function() { upwin.webContents.send('close') //ipcMain.removeAllListeners(["checkForUpdate", "download", "isUpdateNow"]) upwin = null //ipcRenderer.removeAll(["checkForUpdate", "download", "isUpdateNow"]); }) upwin.loadURL(modalPath) upwin.show() } // 經過main進程發送事件給renderer進程,提示更新信息 // mainWindow = new BrowserWindow() function sendUpdateMessage(text){ console.log('text:',text) //mainWindow.webContents.send('message', text) } // 第一次運行軟件 判斷網絡 function initview(){ } // 自動隨機檢查升級包 function checkUp(){ let checkinfo=autoUpdater.checkForUpdates(); //console.log('checkinfo:',checkinfo) checkinfo.then(function(data){ console.log('data:::',data.versionInfo.version) if(data.versionInfo.version){ updateHandle(); } }) } //checkUp(); //隨機時間執行檢查升級 let randoms=Math.round(Math.random()*9+1)*60000; console.log('randoms=',randoms) setTimeout(function(){ checkUp(); },randoms) // hi i'm a vision 1.0.2 hahah