feat: integrate api in electron app

This commit is contained in:
kunkka 2020-10-27 23:08:38 +08:00
parent bd29570e31
commit 74bf714c28
240 changed files with 21003 additions and 112 deletions

View file

@ -1,12 +1,21 @@
"use strict";
const { exec } = require("child_process");
// const fs = require('fs')
import { app, protocol, BrowserWindow } from "electron";
import path from 'path'
// import { autoUpdater } from "electron-updater"
import {
app,
protocol,
BrowserWindow,
ipcMain,
dialog,
globalShortcut,
} from "electron";
import { createProtocol } from "vue-cli-plugin-electron-builder/lib";
import installExtension, { VUEJS_DEVTOOLS } from "electron-devtools-installer";
const isDevelopment = process.env.NODE_ENV !== "production";
// maybe use for modify app menu
// const contextMenu = require('electron-context-menu');
// import contextMenu from 'electron-context-menu'
const isDevelopment = process.env.NODE_ENV !== "production";
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
@ -18,36 +27,19 @@ protocol.registerSchemesAsPrivileged([
]);
function createWindow() {
console.log("Node Version: ", process.version);
const napi = exec("npm run napi:run");
let scriptOutput = "";
napi.stdout.setEncoding('utf8');
napi.stdout.on('data', (data) => {
console.log('napi: ' + data);
data = data.toString();
scriptOutput += data + '\n';
// TODO write file with stream
// const log = fs.createWriteStream(__dirname, '/tmp/' + +new Date + '.log')
// log.write(scriptOutput)
});
// napi.stdout.on('error', (err) => {
// console.log('napi error: ' + err);
// data = err.toString();
// scriptOutput += data + '\n';
// const log = fs.createWriteStream(__dirname, '/tmp/' + +new Date + 'error.log')
// log.write(scriptOutput)
// });
require('./electron/services')
// Create the browser window.
// Create the browser window.
win = new BrowserWindow({
width: 1920,
width: 1153,
height: 768,
webPreferences: {
webSecurity: false,
// Use pluginOptions.nodeIntegration, leave this alone
// See nklayman.github.io/vue-cli-plugin-electron-builder/guide/security.html#node-integration for more info
nodeIntegration: true,
},
icon: path.join(__static, "./img/icons/android-chrome-512x512.png"),
preload: path.join(__dirname, "./electron/preload.js"),
});
if (process.env.WEBPACK_DEV_SERVER_URL) {
@ -58,7 +50,7 @@ function createWindow() {
createProtocol("app");
// Load the index.html when not in development
win.loadURL("app://./index.html");
win.webContents.openDevTools();
// autoUpdater.checkForUpdatesAndNotify()
}
win.on("closed", () => {
@ -95,9 +87,38 @@ app.on("ready", async () => {
console.error("Vue Devtools failed to install:", e.toString());
}
}
// Register shortcut for debug
globalShortcut.register("CommandOrControl+K", function () {
win.webContents.openDevTools();
});
createWindow();
});
ipcMain.on("close", () => {
win.close();
app.quit();
});
ipcMain.on("minimize", () => {
win.minimize();
});
// autoUpdater.on("checking-for-update", () => {});
// autoUpdater.on("update-available", info => {
// console.log(info);
// dialog.showMessageBox({
// title: "新版本发布",
// message: "有新内容更新,稍后将重新为您安装",
// buttons: ["确定"],
// type: "info",
// noLink: true
// });
// });
// autoUpdater.on("update-downloaded", info => {
// console.log(info);
// autoUpdater.quitAndInstall();
// });
// Exit cleanly on request from parent process in development mode.
if (isDevelopment) {
if (process.platform === "win32") {
@ -112,3 +133,19 @@ if (isDevelopment) {
});
}
}
// Make sure the app is singleton.
function initialize() {
const shouldQuit = !app.requestSingleInstanceLock();
if (shouldQuit) return app.quit();
// loadComponent()
}
/**
* 注册主线程文件里的所有js
*/
// function loadComponent () {
// require('./electron/menu.js')
// }
initialize();

116
src/electron/command.js Normal file
View file

@ -0,0 +1,116 @@
"use strict";
import { app, ipcMain, Menu, MenuItem, BrowserWindow, globalShortcut } from 'electron'
let loginWindow, senders;
function openWindow(url) {
const win = new BrowserWindow({
height: 500,
width: 350,
useContentSize: true,
transparent: false,
frame: false,
darkTheme: true,
backgroundColor: "#FFF",
});
win.loadURL(url);
win.on("closed", () => {
loginWindow = null;
});
return win;
}
const menu = new Menu();
const settingsMenu = {
playMenu: function () {
const settings = {
paly: {
label: "播放",
click: function () {
senders.send("play-start");
},
},
addPlayList: {
label: "添加到播放列表",
click: function () {
senders.send("add-play-list");
},
},
collect: {
label: "收藏",
submenu: [
{
label: "创建新歌单",
click: function () {
senders.send("i-like-star");
},
},
],
},
share: {
label: "分享",
},
copyLink: {
label: "复制链接",
},
};
menu.append(new MenuItem(settings.paly));
menu.append(new MenuItem(settings.addPlayList));
menu.append(new MenuItem({ type: "separator" }));
menu.append(new MenuItem(settings.collect));
menu.append(new MenuItem(settings.share));
menu.append(new MenuItem(settings.copyLink));
},
};
export function command(mainWindow, winURL) {
// 显示播放菜单
settingsMenu.playMenu();
// 接收显示菜单指令
ipcMain.on("show-content-menu", (event) => {
senders = event.sender;
const win = BrowserWindow.fromWebContents(senders);
menu.popup(win);
});
// 设置app名称
app.setName("网易云音乐App");
// 关闭window窗口
ipcMain.on("window-close", (event) => {
app.quit();
});
// 最大化window窗口
ipcMain.on("window-max", (event) => {
if (mainWindow.isMaximized()) {
mainWindow.unmaximize();
} else {
mainWindow.maximize();
}
});
// 最小化window窗口
ipcMain.on("window-min", (event) => {
if (!mainWindow.isMinimized()) {
mainWindow.minimize();
}
});
// 新建登录窗口
ipcMain.on("open-login-window", (event, url) => {
if (loginWindow) {
loginWindow.focus();
} else {
loginWindow = openWindow(url);
}
});
// 关闭登录窗口
ipcMain.on("close-login-window", (event) => {
loginWindow.close();
});
// 触发调试 Shift+i
globalShortcut.register("Shift+i", () => {
require("electron-debug")({ showDevTools: true });
});
}

161
src/electron/menu.js Normal file
View file

@ -0,0 +1,161 @@
const { Menu, app } = require("electron");
const version = app.getVersion();
let win;
let updateSource = "menu"; // 更新事件触发来源 menu:通过菜单触发 vue:通过vue页面触发
let template = [
{
label: "编辑",
submenu: [
{
label: "剪切",
accelerator: (() => {
if (process.platform === "darwin") {
return "CmdOrCtrl+X";
} else {
return "Ctrl+X";
}
})(),
role: "cut",
},
{
label: "复制",
accelerator: (() => {
if (process.platform === "darwin") {
return "CmdOrCtrl+C";
} else {
return "Ctrl+C";
}
})(),
role: "copy",
},
{
label: "粘贴",
accelerator: (() => {
if (process.platform === "darwin") {
return "CmdOrCtrl+V";
} else {
return "Ctrl+V";
}
})(),
role: "paste",
},
],
},
{
label: "工具",
submenu: [
{
label: "刷新",
accelerator: (() => {
if (process.platform === "darwin") {
return "CmdOrCtrl+R";
} else {
return "F5";
}
})(),
click: (item, focusedWindow) => {
if (focusedWindow) {
focusedWindow.reload();
}
},
},
{
label: "全屏",
accelerator: (() => {
if (process.platform === "darwin") {
return "Ctrl+Command+F";
} else {
return "F11";
}
})(),
click: (item, focusedWindow) => {
if (focusedWindow) {
focusedWindow.setFullScreen(!focusedWindow.isFullScreen());
}
},
},
{
label: "检查",
accelerator: "F12",
click: (item, focusedWindow) => {
if (focusedWindow) {
focusedWindow.toggleDevTools();
}
},
},
],
},
];
function findReopenMenuItem() {
const menu = Menu.getApplicationMenu();
if (!menu) return;
let reopenMenuItem;
menu.items.forEach((item) => {
if (item.submenu) {
item.submenu.items.forEach((item) => {
if (item.key === "reopenMenuItem") {
reopenMenuItem = item;
}
});
}
});
return reopenMenuItem;
}
// mac 添加退出
if (process.platform === "darwin") {
const name = app.getName();
template.unshift({
label: name + " v" + version,
submenu: [
{
label: "退出",
accelerator: "Command+Q",
click: () => {
app.quit();
},
},
],
});
}
// win 添加更新菜单
if (process.platform === "win32") {
template.push({
label: "帮助",
submenu: [
{
label: `当前版本 v${version}`,
enabled: false,
},
{
label: "检查更新",
accelerator: "Ctrl+U",
click: (item, focusedWindow) => {
// 执行自动更新检查
win = focusedWindow;
updateSource = "menu";
autoUpdater.checkForUpdates();
},
},
],
});
}
app.on('ready', () => {
const menu = Menu.buildFromTemplate(template)
Menu.setApplicationMenu(menu)
})
app.on('browser-window-created', () => {
let reopenMenuItem = findReopenMenuItem()
if (reopenMenuItem) reopenMenuItem.enabled = false
})
app.on('window-all-closed', () => {
let reopenMenuItem = findReopenMenuItem()
if (reopenMenuItem) reopenMenuItem.enabled = true
})

0
src/electron/preload.js Normal file
View file

61
src/electron/services.js Normal file
View file

@ -0,0 +1,61 @@
const express = require("express");
const path = require("path");
const bodyParser = require('body-parser')
const cache = require('../../napi/util/apicache').middleware
const fileUpload = require('express-fileupload')
import routes from "../../napi/routes";
// Integrate API
const app = express()
// CORS & Preflight request
app.use((req, res, next) => {
if (req.path !== '/' && !req.path.includes('.')) {
res.set({
'Access-Control-Allow-Credentials': true,
'Access-Control-Allow-Origin': req.headers.origin || '*',
'Access-Control-Allow-Headers': 'X-Requested-With,Content-Type',
'Access-Control-Allow-Methods': 'PUT,POST,GET,DELETE,OPTIONS',
'Content-Type': 'application/json; charset=utf-8',
})
}
req.method === 'OPTIONS' ? res.status(204).end() : next()
})
// cookie parser
app.use((req, res, next) => {
req.cookies = {}
;(req.headers.cookie || '').split(/\s*;\s*/).forEach((pair) => {
let crack = pair.indexOf('=')
if (crack < 1 || crack == pair.length - 1) return
req.cookies[
decodeURIComponent(pair.slice(0, crack)).trim()
] = decodeURIComponent(pair.slice(crack + 1)).trim()
})
next()
})
// body parser
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: false }))
app.use(fileUpload())
// static
// app.use(express.static(path.join(__dirname, 'public')))
// cache
app.use(cache('2 minutes', (req, res) => res.statusCode === 200))
// router
Object.keys(routes).forEach(route => {
app.use(route, routes[route])
})
const port = process.env.PORT || 3000
const host = process.env.HOST || ''
app.server = app.listen(port, host, () => {
console.log(`server running @ http://${host ? host : 'localhost'}:${port}`)
})

View file

@ -2,7 +2,7 @@
import { register } from "register-service-worker";
if (process.env.NODE_ENV === "production") {
if (process.env.NODE_ENV === "production" && process.env.IS_ELECTRON === 'undefined') {
register(`${process.env.BASE_URL}service-worker.js`, {
ready() {
console.log(