使用 electron-vue 搭建桌面應用開發模板
參考 PicGo 搭建符合本公司需求的桌面應用開發模板
已實現功能:
1.單行命令即可生成可安裝程式
2.使用 nsis 構建安裝嚮導
3.實現檔案的讀寫功能
4.視窗的最小化按鈕和關閉按鈕以及標題欄自定義,不使用 electron 自身攜帶的原生標題欄
5.視窗關閉儲存到托盤
6.托盤右鍵選單有'關於本產品'...選單
7.使用說明可在獨立視窗中開啟,且是以本地 pdf 形式
8.使用 nsis 製作安裝嚮導,實現證書自動安裝
實現過程:
1.單行命令即可生成可安裝程式
構建專案的時候選擇 electron-builder ,執行 npm run build 之後自動打包成 setup.exe 可安裝檔案
2.使用 nsis 構建安裝嚮導
參考蘇南大叔的‘如何利用 nsis 製作 electron 的安裝包’即可,electron-builder 外掛也有 nsis 配置項,但是侷限性有點大且不可控
3.實現檔案的讀寫功能
// 在 js 中定義 files 讀寫檔案 import path from "path"; import fs from "fs-extra"; import { app, remote } from "electron"; // 引入remote模組 const APP = process.type === "renderer" ? remote.app : app; // 根據process.type來分辨在哪種模式使用哪種模組 const STORE_PATH = APP.getPath("userData"); // 獲取electron應用的使用者目錄 if (!fs.pathExistsSync(STORE_PATH)) { // 如果不存在路徑 fs.mkdirpSync(STORE_PATH); // 就建立 } export const files = { read: function(filesName) { const path_ = path.join(STORE_PATH, filesName); let filesData = fs.readFileSync(path_, "utf-8", function(e, data) { if (e) throw e; return data; }); return filesData; }, write: function(filesName, writeStr) { const path_ = path.join(STORE_PATH, filesName); fs.open(path_, "w", function(e, fd) { if (e) throw e; fs.write(fd, writeStr, 0, "utf8", function(e) { if (e) throw e; fs.closeSync(fd); }); }); } };
呼叫:
4.視窗的最小化按鈕和關閉按鈕以及標題欄自定義,不使用 electron 自身攜帶的原生標題欄import { files } from "@/scripts/fileOpera.js"; const { ipcRenderer } = window.require("electron") export default { name: "landing-page", data() { return { filesData:'' }; }, methods: { writeFile: function() { // 寫入 userSet.txt 檔案 files.write( "/userSet.txt", 'write some to test' ); }, readFile: function() { // 讀取 userSet.txt 檔案 this.filesData = files.read("/userSet.txt"); }, openUseDirections: function(){ // render 程序與 main 程序互動開啟使用說明視窗 ipcRenderer.send("openUseDirections"); } } };
首先在建立視窗的時候需要把 frame 配置項設定為 false,其次在 App.vue 元件中自定義標題欄以及右側按鈕
HTML
<template>
<div id="app">
<div class="fake-title-bar">
<div class="title-mark">
<img src="../../static/menubar-nodarwin.png" />
標題
</div>
<div class="handle-bar" v-if="os === 'win32'">
<Icon type="minus" @click="minimizeWindow"></Icon>
<Icon type="close" @click="closeWindow"></Icon>
</div>
</div>
<router-view></router-view>
</div>
</template>
JS
<script>
import { remote } from "electron";
import { myMixin } from './mixins/test'
const { BrowserWindow } = remote;
import {time_differ, toISOString, formatDate} from '@/scripts/times.js'
export default {
name: "buildertest",
mixins: [myMixin],
data() {
return {
os: ""
};
},
created() {
this.os = process.platform;
},
methods: {
minimizeWindow() {
const window = BrowserWindow.getFocusedWindow();
window.minimize();
},
closeWindow() {
const window = BrowserWindow.getFocusedWindow();
window.close();
}
}
};
</script>
STYLES
<style lang='less'>
#app {
.fake-title-bar {
-webkit-app-region: drag;
height: h = 24px;
color: #2c2c2c;
font-size: 12px;
line-height: h;
width: 100%;
border-bottom: 1px solid #d8d8d8;
position: fixed;
z-index: 100;
.title-mark {
position: absolute;
left: 4px;
top: 0;
z-index: 10000;
padding-left: 26px;
img {
position: absolute;
top: 50%;
left: 4px;
transform: translateY(-50%);
height: 20px;
}
}
.handle-bar {
position: absolute;
top: 2px;
right: 4px;
width: 40px;
height: h;
z-index: 10000;
-webkit-app-region: no-drag;
i {
cursor: pointer;
font-size: 16px;
}
.ivu-icon-minus {
margin-right: 6px;
&:hover {
color: #409EFF;
}
}
.ivu-icon-close {
&:hover {
color: #F15140;
}
}
}
}
.writeFile {
position: absolute;
top: 40px;
font-size: 14px;
}
}
</style>
5.視窗關閉儲存到托盤
6.托盤右鍵選單有'關於本產品'...選單
// 定義 isQuit 變數儲存當前關閉是視窗觸發還是右鍵托盤退出選單觸發
let isQuit = false; // 預設是從視窗觸發
// 建立托盤選單
function createTray() {
const menubarPic =
process.platform === "darwin"
? `${__static}/menubar.png`
: `${__static}/menubar-nodarwin.png`;
tray = new Tray(menubarPic);
contextMenu = Menu.buildFromTemplate([
{
label: "關於",
click() {
dialog.showMessageBox({
title: "xxx",
message: "xxx",
detail: `版本: ${pkg.version}\n`
});
}
},
{
label: "開啟",
click() {
if (mainWindow === null) {
createWindow();
mainWindow.show();
mainWindow.maximize();
} else {
mainWindow.show();
mainWindow.maximize();
}
}
},
{
label: "退出",
click: function() {
isQuit = true;
app.quit();
app.quit(); // 程式設定關閉為最小化,所以呼叫兩次關閉,防止最大化時一次不能關閉
}
}
]);
// 設定此托盤圖表的懸停提示內容
tray.setToolTip("xxx產品名稱");
tray.on("right-click", () => {
tray.popUpContextMenu(contextMenu);
});
tray.on("click", () => {
if (mainWindow === null) {
createWindow();
mainWindow.show();
mainWindow.maximize();
} else {
mainWindow.show();
mainWindow.maximize();
}
});
}
// 監聽關閉事件
mainWindow.on("close", function(event) {
if (!isQuit) {
event.preventDefault();
mainWindow.hide();
}
return false;
});
// 點選圖示(桌面快捷方式)檢查當前活動例項的個數
const isSecondInstance = app.makeSingleInstance(() => {
if (mainWindow) {
if (mainWindow.isMinimized()) {
mainWindow.restore(); // 視窗從最小化恢復時觸發
}
mainWindow.show();
mainWindow.maximize();
mainWindow.focus();
}
});
if (isSecondInstance) {
app.quit();
}
7.使用說明可在獨立視窗中開啟,且是以本地 pdf 形式
// 定義 useDirection 儲存使用說明視窗例項
let useDirection = null;
// 使用 ipcMain 與 ipcRender 互動
ipcMain.on("openUseDirections", (event) => {
let path_ = app.getAppPath().split("\\").join("/");
if(path_.indexOf("app.asar") !== -1){
// 將目錄最後的 /app.asar 去除
path_ = path_.substr(0,path_.lastIndexOf('/'))
}
Menu.setApplicationMenu(null);//隱藏選單
if (useDirection) {
if (useDirection.isMinimized()) {
useDirection.restore(); // 視窗從最小化恢復時觸發
}
useDirection.show();
useDirection.focus();
}else{
let options = {
width: 838,
height: 600,
icon:`${__static}/icon.ico`,
title:'xxx',
autoHideMenuBar:true,
webPreferences: {
plugins: true
}
};
useDirection = new PDFWindow(options);
useDirection.loadURL(`file:///${path_}/static/xxx.pdf`);
}
useDirection.on('closed', () => {
useDirection = null;
})
event.sender.returnValue = false;
})
說明:
1) app.getAppPath ()返回當前應用所在的路徑,使用 electron-builder 打包的安裝程式安裝之後返回的路徑後面帶有 /app.asar 這一結尾路徑,需要將此路徑去除在重新組合 static/xxx.pdf 路徑,因 electron-builder 打包的檔案全部是 asar 檔案,可以自己寫指令碼在打包之後新建 static 資料夾,將 xxx.pdf 拷貝進該資料夾,也可手動操作。
2.)新建開啟本地 pdf 檔案的視窗使用了 electron-pdf-window 外掛(基本原理是對 pdf.js 外掛進行再次封裝)
- 安裝 npm install electron-pdf-window --save-dev
- 引入 const PDFWindow = require("electron-pdf-window");
8.使用 nsis 製作安裝嚮導,實現證書自動安裝
因本公司自己的產品在客戶端安裝之後需要安裝本公司自己簽發的 rootCA.crt 證書,所以在使用 nsis 製作安裝嚮導的時候將執行 run.bat 指令碼(安裝當前目錄的 rootCA.crt 證書到'受信任的根證書頒發機構')
- 使用蘇南大叔的‘如何利用 nsis 製作 electron 的安裝包’製作安裝嚮導
- 將 run.bat 指令碼和 rootCA.crt 證書都拷貝到 resources/static 目錄下即可
- 在 Section "MainSection" SEC01 指令碼的最後 SectionEnd 指令碼的前面新增 ExecShell "open" "$INSTDIR/resources/static/run.bat"