取消 electron-renderer

This commit is contained in:
kuaifan 2022-02-22 21:02:27 +08:00
parent fb8f63f305
commit 49c5a9f621
13 changed files with 908 additions and 68 deletions

832
electron/main.js vendored
View File

@ -2,11 +2,14 @@ const fs = require('fs')
const fse = require('fs-extra')
const os = require("os");
const path = require('path')
const XLSX = require('xlsx');
const {app, BrowserWindow, ipcMain, dialog} = require('electron')
const {app, BrowserWindow, ipcMain, dialog, clipboard, nativeImage, shell} = require('electron')
const log = require("electron-log");
const fsProm = require('fs/promises');
const PDFDocument = require('pdf-lib').PDFDocument;
const crc = require('crc');
const zlib = require('zlib');
const utils = require('./utils');
const config = require('./package.json');
const log = require("electron-log");
let mainWindow = null,
subWindow = [],
@ -68,8 +71,9 @@ function createMainWindow() {
autoHideMenuBar: true,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
webSecurity: true,
nodeIntegration: true,
contextIsolation: false
contextIsolation: true
}
})
mainWindow.webContents.setUserAgent(mainWindow.webContents.getUserAgent() + " MainTaskWindow/" + process.platform + "/" + os.arch() + "/1.0");
@ -150,9 +154,10 @@ function createSubWindow(args) {
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
devTools: args.devTools !== false,
webSecurity: true,
nodeIntegration: true,
contextIsolation: false
}
contextIsolation: true
},
}, config))
browser.on('page-title-updated', (event, title) => {
if (title == "index.html" || args.titleFixed === true) {
@ -289,6 +294,15 @@ ipcMain.on('windowClose', (event) => {
event.returnValue = "ok"
})
/**
* 关闭窗口强制
*/
ipcMain.on('windowDestroy', (event) => {
const win = BrowserWindow.fromWebContents(event.sender);
win.destroy()
event.returnValue = "ok"
})
/**
* 设置窗口尺寸
* @param args {width, height, autoZoom, minWidth, minHeight, maxWidth, maxHeight}
@ -404,20 +418,794 @@ ipcMain.on('setDockBadge', (event, args) => {
event.returnValue = "ok"
})
/**
* 保存sheets
*/
ipcMain.on('saveSheet', (event, data, filename, opts) => {
const EXTENSIONS = "xls|xlsx|xlsm|xlsb|xml|csv|txt|dif|sylk|slk|prn|ods|fods|htm|html".split("|");
dialog.showSaveDialog({
title: 'Save file as',
defaultPath: filename,
filters: [{
name: "Spreadsheets",
extensions: EXTENSIONS
}]
}).then(o => {
XLSX.writeFile(data, o.filePath, opts);
//Pdf export
const MICRON_TO_PIXEL = 264.58 //264.58 micron = 1 pixel
const PNG_CHUNK_IDAT = 1229209940;
const LARGE_IMAGE_AREA = 30000000;
//NOTE: Key length must not be longer than 79 bytes (not checked)
function writePngWithText(origBuff, key, text, compressed, base64encoded) {
let isDpi = key == 'dpi';
let inOffset = 0;
let outOffset = 0;
let data = text;
let dataLen = isDpi ? 9 : key.length + data.length + 1; //we add 1 zeros with non-compressed data, for pHYs it's 2 of 4-byte-int + 1 byte
//prepare compressed data to get its size
if (compressed) {
data = zlib.deflateRawSync(encodeURIComponent(text));
dataLen = key.length + data.length + 2; //we add 2 zeros with compressed data
}
let outBuff = Buffer.allocUnsafe(origBuff.length + dataLen + 4); //4 is the header size "zTXt", "tEXt" or "pHYs"
try {
let magic1 = origBuff.readUInt32BE(inOffset);
inOffset += 4;
let magic2 = origBuff.readUInt32BE(inOffset);
inOffset += 4;
if (magic1 != 0x89504e47 && magic2 != 0x0d0a1a0a) {
throw new Error("PNGImageDecoder0");
}
outBuff.writeUInt32BE(magic1, outOffset);
outOffset += 4;
outBuff.writeUInt32BE(magic2, outOffset);
outOffset += 4;
} catch (e) {
log.error(e.message, {stack: e.stack});
throw new Error("PNGImageDecoder1");
}
try {
while (inOffset < origBuff.length) {
let length = origBuff.readInt32BE(inOffset);
inOffset += 4;
let type = origBuff.readInt32BE(inOffset)
inOffset += 4;
if (type == PNG_CHUNK_IDAT) {
// Insert zTXt chunk before IDAT chunk
outBuff.writeInt32BE(dataLen, outOffset);
outOffset += 4;
let typeSignature = isDpi ? 'pHYs' : (compressed ? "zTXt" : "tEXt");
outBuff.write(typeSignature, outOffset);
outOffset += 4;
if (isDpi) {
let dpm = Math.round(parseInt(text) / 0.0254) || 3937; //One inch is equal to exactly 0.0254 meters. 3937 is 100dpi
outBuff.writeInt32BE(dpm, outOffset);
outBuff.writeInt32BE(dpm, outOffset + 4);
outBuff.writeInt8(1, outOffset + 8);
outOffset += 9;
data = Buffer.allocUnsafe(9);
data.writeInt32BE(dpm, 0);
data.writeInt32BE(dpm, 4);
data.writeInt8(1, 8);
} else {
outBuff.write(key, outOffset);
outOffset += key.length;
outBuff.writeInt8(0, outOffset);
outOffset++;
if (compressed) {
outBuff.writeInt8(0, outOffset);
outOffset++;
data.copy(outBuff, outOffset);
} else {
outBuff.write(data, outOffset);
}
outOffset += data.length;
}
let crcVal = 0xffffffff;
crcVal = crc.crcjam(typeSignature, crcVal);
crcVal = crc.crcjam(data, crcVal);
// CRC
outBuff.writeInt32BE(crcVal ^ 0xffffffff, outOffset);
outOffset += 4;
// Writes the IDAT chunk after the zTXt
outBuff.writeInt32BE(length, outOffset);
outOffset += 4;
outBuff.writeInt32BE(type, outOffset);
outOffset += 4;
origBuff.copy(outBuff, outOffset, inOffset);
// Encodes the buffer using base64 if requested
return base64encoded ? outBuff.toString('base64') : outBuff;
}
outBuff.writeInt32BE(length, outOffset);
outOffset += 4;
outBuff.writeInt32BE(type, outOffset);
outOffset += 4;
origBuff.copy(outBuff, outOffset, inOffset, inOffset + length + 4);// +4 to move past the crc
inOffset += length + 4;
outOffset += length + 4;
}
} catch (e) {
log.error(e.message, {stack: e.stack});
throw e;
}
}
//TODO Create a lightweight html file similar to export3.html for exporting to vsdx
function exportVsdx(event, args, directFinalize) {
let win = new BrowserWindow({
width: 1280,
height: 800,
center: true,
show: false,
autoHideMenuBar: true,
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
devTools: args.devTools !== false,
webSecurity: true,
nodeIntegration: true,
contextIsolation: true
},
})
let loadEvtCount = 0;
function loadFinished() {
loadEvtCount++;
if (loadEvtCount == 2) {
win.webContents.send('export-vsdx', args);
ipcMain.once('export-vsdx-finished', (evt, data) => {
let hasError = false;
if (data == null) {
hasError = true;
}
//Set finalize here since it is call in the reply below
function finalize() {
win.destroy();
}
if (directFinalize === true) {
event.finalize = finalize;
} else {
//Destroy the window after response being received by caller
ipcMain.once('export-finalize', finalize);
}
if (hasError) {
event.reply('export-error');
} else {
event.reply('export-success', data);
}
});
}
}
//Order of these two events is not guaranteed, so wait for them async.
//TOOD There is still a chance we catch another window 'app-load-finished' if user created multiple windows quickly
ipcMain.once('app-load-finished', loadFinished);
win.webContents.on('did-finish-load', loadFinished);
}
async function mergePdfs(pdfFiles, xml) {
//Pass throgh single files
if (pdfFiles.length == 1 && xml == null) {
return pdfFiles[0];
}
try {
const pdfDoc = await PDFDocument.create();
pdfDoc.setCreator('diagrams.net');
if (xml != null) {
//Embed diagram XML as file attachment
await pdfDoc.attach(Buffer.from(xml).toString('base64'), 'diagram.xml', {
mimeType: 'application/vnd.jgraph.mxfile',
description: 'Diagram Content'
});
}
for (let i = 0; i < pdfFiles.length; i++) {
const pdfFile = await PDFDocument.load(pdfFiles[i].buffer);
const pages = await pdfDoc.copyPages(pdfFile, pdfFile.getPageIndices());
pages.forEach(p => pdfDoc.addPage(p));
}
const pdfBytes = await pdfDoc.save();
return Buffer.from(pdfBytes);
} catch (e) {
throw new Error('Error during PDF combination: ' + e.message);
}
}
//TODO Use canvas to export images if math is not used to speedup export (no capturePage). Requires change to export3.html also
function exportDiagram(event, args, directFinalize) {
if (args.format == 'vsdx') {
exportVsdx(event, args, directFinalize);
return;
}
let browser = null;
try {
browser = new BrowserWindow({
webPreferences: {
preload: `${__dirname}/electron-preload.js`,
backgroundThrottling: false,
contextIsolation: true,
nativeWindowOpen: true
},
show: false,
frame: false,
enableLargerThanScreen: true,
transparent: args.format == 'png' && (args.bg == null || args.bg == 'none'),
});
browser.loadURL(`file://${__dirname}/export3.html`);
const contents = browser.webContents;
let pageByPage = (args.format == 'pdf' && !args.print), from, pdfs;
if (pageByPage) {
from = args.allPages ? 0 : parseInt(args.from || 0);
to = args.allPages ? 1000 : parseInt(args.to || 1000) + 1; //The 'to' will be corrected later
pdfs = [];
args.from = from;
args.to = from;
args.allPages = false;
}
contents.on('did-finish-load', function () {
//Set finalize here since it is call in the reply below
function finalize() {
browser.destroy();
}
if (directFinalize === true) {
event.finalize = finalize;
} else {
//Destroy the window after response being received by caller
ipcMain.once('export-finalize', finalize);
}
function renderingFinishHandler(evt, renderInfo) {
if (renderInfo == null) {
event.reply('export-error');
return;
}
let pageCount = renderInfo.pageCount, bounds = null;
//For some reason, Electron 9 doesn't send this object as is without stringifying. Usually when variable is external to function own scope
try {
bounds = JSON.parse(renderInfo.bounds);
} catch (e) {
bounds = null;
}
let pdfOptions = {pageSize: 'A4'};
let hasError = false;
if (bounds == null || bounds.width < 5 || bounds.height < 5) //very small page size never return from printToPDF
{
//A workaround to detect errors in the input file or being empty file
hasError = true;
} else {
//Chrome generates Pdf files larger than requested pixels size and requires scaling
let fixingScale = 0.959;
let w = Math.ceil(bounds.width * fixingScale);
// +0.1 fixes cases where adding 1px below is not enough
// Increase this if more cropped PDFs have extra empty pages
let h = Math.ceil(bounds.height * fixingScale + 0.1);
pdfOptions = {
printBackground: true,
pageSize: {
width: w * MICRON_TO_PIXEL,
height: (h + 2) * MICRON_TO_PIXEL //the extra 2 pixels to prevent adding an extra empty page
},
marginsType: 1 // no margin
}
}
let base64encoded = args.base64 == '1';
if (hasError) {
event.reply('export-error');
} else if (args.format == 'png' || args.format == 'jpg' || args.format == 'jpeg') {
//Adds an extra pixel to prevent scrollbars from showing
let newBounds = {
width: Math.ceil(bounds.width + bounds.x) + 1,
height: Math.ceil(bounds.height + bounds.y) + 1
};
browser.setBounds(newBounds);
//TODO The browser takes sometime to show the graph (also after resize it takes some time to render)
// 1 sec is most probably enough (for small images, 5 for large ones) BUT not a stable solution
setTimeout(function () {
browser.capturePage().then(function (img) {
//Image is double the given bounds, so resize is needed!
let tScale = 1;
//If user defined width and/or height, enforce it precisely here. Height override width
if (args.h) {
tScale = args.h / newBounds.height;
} else if (args.w) {
tScale = args.w / newBounds.width;
}
newBounds.width *= tScale;
newBounds.height *= tScale;
img = img.resize(newBounds);
let data = args.format == 'png' ? img.toPNG() : img.toJPEG(args.jpegQuality || 90);
if (args.dpi != null && args.format == 'png') {
data = writePngWithText(data, 'dpi', args.dpi);
}
if (args.embedXml == "1" && args.format == 'png') {
data = writePngWithText(data, "mxGraphModel", args.xml, true,
base64encoded);
} else {
if (base64encoded) {
data = data.toString('base64');
}
}
event.reply('export-success', data);
});
}, bounds.width * bounds.height < LARGE_IMAGE_AREA ? 1000 : 5000);
} else if (args.format == 'pdf') {
if (args.print) {
pdfOptions = {
scaleFactor: args.pageScale,
printBackground: true,
pageSize: {
width: args.pageWidth * MICRON_TO_PIXEL,
//This height adjustment fixes the output. TODO Test more cases
height: (args.pageHeight * 1.025) * MICRON_TO_PIXEL
},
marginsType: 1 // no margin
};
contents.print(pdfOptions, (success, errorType) => {
//Consider all as success
event.reply('export-success', {});
});
} else {
contents.printToPDF(pdfOptions).then(async (data) => {
pdfs.push(data);
to = to > pageCount ? pageCount : to;
from++;
if (from < to) {
args.from = from;
args.to = from;
ipcMain.once('render-finished', renderingFinishHandler);
contents.send('render', args);
} else {
data = await mergePdfs(pdfs, args.embedXml == '1' ? args.xml : null);
event.reply('export-success', data);
}
})
.catch((error) => {
event.reply('export-error', error);
});
}
} else if (args.format == 'svg') {
contents.send('get-svg-data');
ipcMain.once('svg-data', (evt, data) => {
event.reply('export-success', data);
});
} else {
event.reply('export-error', 'Error: Unsupported format');
}
}
ipcMain.once('render-finished', renderingFinishHandler);
if (args.format == 'xml') {
ipcMain.once('xml-data', (evt, data) => {
event.reply('export-success', data);
});
ipcMain.once('xml-data-error', () => {
event.reply('export-error');
});
}
args.border = args.border || 0;
args.scale = args.scale || 1;
contents.send('render', args);
});
} catch (e) {
if (browser != null) {
browser.destroy();
}
event.reply('export-error', e);
console.log('export-error', e);
}
}
ipcMain.on('export', exportDiagram);
//================================================================
// Renderer Helper functions
//================================================================
const {COPYFILE_EXCL} = fs.constants;
const DRAFT_PREFEX = '~$';
const DRAFT_EXT = '.dtmp';
const BKP_PREFEX = '~$';
const BKP_EXT = '.bkp';
function isConflict(origStat, stat) {
return stat != null && origStat != null && stat.mtimeMs != origStat.mtimeMs;
}
function getDraftFileName(fileObject) {
let filePath = fileObject.path;
let draftFileName = '', counter = 1, uniquePart = '';
do {
draftFileName = path.join(path.dirname(filePath), DRAFT_PREFEX + path.basename(filePath) + uniquePart + DRAFT_EXT);
uniquePart = '_' + counter++;
} while (fs.existsSync(draftFileName));
return draftFileName;
}
async function getFileDrafts(fileObject) {
let filePath = fileObject.path;
let draftsPaths = [], drafts = [], draftFileName, counter = 1, uniquePart = '';
do {
draftsPaths.push(draftFileName);
draftFileName = path.join(path.dirname(filePath), DRAFT_PREFEX + path.basename(filePath) + uniquePart + DRAFT_EXT);
uniquePart = '_' + counter++;
} while (fs.existsSync(draftFileName)); //TODO this assume continuous drafts names
for (let i = 1; i < draftsPaths.length; i++) {
try {
let stat = await fsProm.lstat(draftsPaths[i]);
drafts.push({
data: await fsProm.readFile(draftsPaths[i], 'utf8'),
created: stat.ctimeMs,
modified: stat.mtimeMs,
path: draftsPaths[i]
});
} catch (e) {
} // Ignore
}
return drafts;
}
async function saveDraft(fileObject, data) {
if (data == null || data.length == 0) {
throw new Error('empty data');
} else {
let draftFileName = fileObject.draftFileName || getDraftFileName(fileObject);
await fsProm.writeFile(draftFileName, data, 'utf8');
return draftFileName;
}
}
async function saveFile(fileObject, data, origStat, overwrite, defEnc) {
let retryCount = 0;
let backupCreated = false;
let bkpPath = path.join(path.dirname(fileObject.path), BKP_PREFEX + path.basename(fileObject.path) + BKP_EXT);
let writeFile = async function () {
if (data == null || data.length == 0) {
throw new Error('empty data');
} else {
let writeEnc = defEnc || fileObject.encoding;
await fsProm.writeFile(fileObject.path, data, writeEnc);
let stat2 = await fsProm.stat(fileObject.path);
let writtenData = await fsProm.readFile(fileObject.path, writeEnc);
if (data != writtenData) {
retryCount++;
if (retryCount < 3) {
return await writeFile();
} else {
throw new Error('all saving trials failed');
}
} else {
if (backupCreated) {
fs.unlink(bkpPath, (err) => {
}); //Ignore errors!
}
return stat2;
}
}
};
async function doSaveFile() {
try {
await fsProm.copyFile(fileObject.path, bkpPath, COPYFILE_EXCL);
backupCreated = true;
} catch (e) {
} //Ignore
return await writeFile();
}
if (overwrite) {
return await doSaveFile();
} else {
let stat = fs.existsSync(fileObject.path) ?
await fsProm.stat(fileObject.path) : null;
if (stat && isConflict(origStat, stat)) {
new Error('conflict');
} else {
return await doSaveFile();
}
}
}
async function writeFile(path, data, enc) {
return await fsProm.writeFile(path, data, enc);
}
function getAppDataFolder() {
try {
let appDataDir = app.getPath('appData');
let drawioDir = appDataDir + '/dootask.com';
if (!fs.existsSync(drawioDir)) //Usually this dir already exists
{
fs.mkdirSync(drawioDir);
}
return drawioDir;
} catch (e) {
}
return '.';
}
function getDocumentsFolder() {
try {
return app.getPath('documents');
} catch (e) {
}
return '.';
}
function checkFileExists(pathParts) {
let filePath = path.join(...pathParts);
return {exists: fs.existsSync(filePath), path: filePath};
}
async function showOpenDialog(defaultPath, filters, properties) {
return dialog.showOpenDialogSync({
defaultPath: defaultPath,
filters: filters,
properties: properties
});
event.returnValue = "ok"
})
}
async function showSaveDialog(defaultPath, filters) {
return dialog.showSaveDialogSync({
defaultPath: defaultPath,
filters: filters
});
}
async function installPlugin(filePath) {
let pluginsDir = path.join(getAppDataFolder(), '/plugins');
if (!fs.existsSync(pluginsDir)) {
fs.mkdirSync(pluginsDir);
}
let pluginName = path.basename(filePath);
let dstFile = path.join(pluginsDir, pluginName);
if (fs.existsSync(dstFile)) {
throw new Error('fileExists');
} else {
await fsProm.copyFile(filePath, dstFile);
}
return {pluginName: pluginName, selDir: path.dirname(filePath)};
}
function uninstallPlugin(plugin) {
let pluginsFile = path.join(getAppDataFolder(), '/plugins', plugin);
if (fs.existsSync(pluginsFile)) {
fs.unlinkSync(pluginsFile);
}
return null
}
function dirname(path_p) {
return path.dirname(path_p);
}
async function readFile(filename, encoding) {
return await fsProm.readFile(filename, encoding);
}
async function fileStat(file) {
return await fsProm.stat(file);
}
async function isFileWritable(file) {
try {
await fsProm.access(file, fs.constants.W_OK);
return true;
} catch (e) {
return false;
}
}
function clipboardAction(method, data) {
if (method == 'writeText') {
clipboard.writeText(data);
} else if (method == 'readText') {
return clipboard.readText();
} else if (method == 'writeImage') {
clipboard.write({
image:
nativeImage.createFromDataURL(data.dataUrl), html: '<img src="' +
data.dataUrl + '" width="' + data.w + '" height="' + data.h + '">'
});
}
}
async function deleteFile(file) {
await fsProm.unlink(file);
}
function windowAction(method) {
let win = BrowserWindow.getFocusedWindow();
if (win) {
if (method == 'minimize') {
win.minimize();
} else if (method == 'maximize') {
win.maximize();
} else if (method == 'unmaximize') {
win.unmaximize();
} else if (method == 'close') {
win.close();
} else if (method == 'isMaximized') {
return win.isMaximized();
} else if (method == 'removeAllListeners') {
win.removeAllListeners();
}
}
}
function openExternal(url) {
shell.openExternal(url);
return null
}
function watchFile(path) {
let win = BrowserWindow.getFocusedWindow();
if (win) {
fs.watchFile(path, (curr, prev) => {
try {
win.webContents.send('fileChanged', {
path: path,
curr: curr,
prev: prev
});
} catch (e) {
} // Ignore
});
}
return null
}
function unwatchFile(path) {
fs.unwatchFile(path);
return null
}
ipcMain.on("rendererReq", async (event, args) => {
try {
let ret = null;
switch (args.action) {
case 'saveFile':
ret = await saveFile(args.fileObject, args.data, args.origStat, args.overwrite, args.defEnc);
break;
case 'writeFile':
ret = await writeFile(args.path, args.data, args.enc);
break;
case 'saveDraft':
ret = await saveDraft(args.fileObject, args.data);
break;
case 'getFileDrafts':
ret = await getFileDrafts(args.fileObject);
break;
case 'getAppDataFolder':
ret = getAppDataFolder();
break;
case 'getDocumentsFolder':
ret = await getDocumentsFolder();
break;
case 'checkFileExists':
ret = checkFileExists(args.pathParts);
break;
case 'showOpenDialog':
ret = await showOpenDialog(args.defaultPath, args.filters, args.properties);
break;
case 'showSaveDialog':
ret = await showSaveDialog(args.defaultPath, args.filters);
break;
case 'installPlugin':
ret = await installPlugin(args.filePath);
break;
case 'uninstallPlugin':
ret = await uninstallPlugin(args.plugin);
break;
case 'dirname':
ret = dirname(args.path);
break;
case 'readFile':
ret = await readFile(args.filename, args.encoding);
break;
case 'clipboardAction':
ret = clipboardAction(args.method, args.data);
break;
case 'deleteFile':
ret = await deleteFile(args.file);
break;
case 'fileStat':
ret = await fileStat(args.file);
break;
case 'isFileWritable':
ret = await isFileWritable(args.file);
break;
case 'windowAction':
ret = windowAction(args.method);
break;
case 'openExternal':
ret = await openExternal(args.url);
break;
case 'watchFile':
ret = await watchFile(args.path);
break;
case 'unwatchFile':
ret = await unwatchFile(args.path);
break;
}
event.reply('mainResp', {success: true, data: ret, reqId: args.reqId});
} catch (e) {
event.reply('mainResp', {error: true, msg: e.message, e: e, reqId: args.reqId});
}
});

View File

@ -40,10 +40,11 @@
},
"dependencies": {
"axios": "^0.26.0",
"crc": "^3.8.0",
"electron-squirrel-startup": "^1.0.0",
"electron-log": "^4.4.6",
"fs-extra": "^10.0.0",
"xlsx": "^0.18.1"
"pdf-lib": "^1.16.0"
},
"build": {
"appId": "com.dootask.task",

79
electron/preload.js vendored
View File

@ -1,14 +1,73 @@
// preload.js
const {
shell,
contextBridge,
ipcRenderer
} = require("electron");
// All of the Node.js APIs are available in the preload process.
// It has the same sandbox as a Chrome extension.
window.addEventListener('DOMContentLoaded', () => {
const replaceText = (selector, text) => {
const element = document.getElementById(selector)
if (element) element.innerText = text
let reqId = 1;
let reqInfo = {};
let fileChangedListeners = {};
ipcRenderer.on('mainResp', (event, resp) => {
let callbacks = reqInfo[resp.reqId];
if (resp.error) {
callbacks.error(resp.msg, resp.e);
} else {
callbacks.callback(resp.data);
}
for (const dependency of ['chrome', 'node', 'electron']) {
replaceText(`${dependency}-version`, process.versions[dependency])
delete reqInfo[resp.reqId];
});
ipcRenderer.on('fileChanged', (event, resp) => {
let listener = fileChangedListeners[resp.path];
if (listener) {
listener(resp.curr, resp.prev);
}
})
});
contextBridge.exposeInMainWorld(
'electron', {
request: (msg, callback, error) => {
msg.reqId = reqId++;
reqInfo[msg.reqId] = {callback: callback, error: error};
if (msg.action == 'watchFile') {
fileChangedListeners[msg.path] = msg.listener;
delete msg.listener;
}
ipcRenderer.send('rendererReq', msg);
},
registerMsgListener: (action, callback) => {
ipcRenderer.on(action, (event, args) => {
callback(args);
});
},
listenOnce: (action, callback) => {
ipcRenderer.once(action, (event, args) => {
callback(args);
});
},
sendMessage: (action, args) => {
ipcRenderer.send(action, args);
},
sendSyncMessage: (action, args) => {
ipcRenderer.sendSync(action, args);
},
openExternal: (url) => {
return new Promise((resolve, reject) => {
shell.openExternal(url).then(resolve).catch(reject)
});
}
}
);
contextBridge.exposeInMainWorld(
'process', {
type: process.type,
versions: process.versions
}
);

View File

@ -41,7 +41,6 @@
"cross-env": "^7.0.3",
"css-loader": "^6.5.1",
"echarts": "^5.2.2",
"electron": "^17.0.1",
"element-ui": "^2.15.6",
"file-loader": "^6.2.0",
"inquirer": "^8.2.0",
@ -75,8 +74,8 @@
"vue-template-compiler": "^2.6.14",
"vuedraggable": "^2.24.3",
"vuex": "^3.6.2",
"webpack": "^5.65.0",
"webpack-cli": "^4.9.1",
"webpack": "^5.69.1",
"webpack-cli": "^4.9.2",
"xlsx": "^0.17.4"
}
}

View File

@ -179,9 +179,8 @@ export default {
if (!this.$Electron) {
return;
}
const {ipcRenderer} = this.$Electron;
ipcRenderer.send('inheritClose');
ipcRenderer.on('windowClose', () => {
this.$Electron.sendMessage('inheritClose');
this.$Electron.registerMsgListener('windowClose', () => {
if (this.$Modal.removeLast()) {
return;
}
@ -189,9 +188,9 @@ export default {
this.cacheDrawerOverlay[this.cacheDrawerOverlay.length - 1].close();
return;
}
ipcRenderer.send('windowHidden');
this.$Electron.sendMessage('windowHidden');
})
ipcRenderer.on('dispatch', (event, args) => {
this.$Electron.registerMsgListener('dispatch', (event, args) => {
if (!$A.isJson(args)) {
return;
}

View File

@ -1,3 +1,5 @@
const isElectron = window && window.process && window.process.type;
import './functions/common'
import './functions/web'
@ -56,7 +58,7 @@ VueRouter.prototype.push = function push(location) {
}
const router = new VueRouter({
mode: !!__IS_ELECTRON ? 'hash' : 'history',
mode: isElectron ? 'hash' : 'history',
routes
});
@ -98,8 +100,8 @@ Vue.prototype.$Electron = null;
Vue.prototype.$Platform = "web";
Vue.prototype.$isMainElectron = false;
Vue.prototype.$isSubElectron = false;
if (!!__IS_ELECTRON) {
Vue.prototype.$Electron = require('electron');
if (isElectron) {
Vue.prototype.$Electron = electron;
Vue.prototype.$Platform = /macintosh|mac os x/i.test(navigator.userAgent) ? "mac" : "win";
Vue.prototype.$isMainElectron = /\s+MainTaskWindow\//.test(window.navigator.userAgent);
Vue.prototype.$isSubElectron = /\s+SubTaskWindow\//.test(window.navigator.userAgent);
@ -131,7 +133,7 @@ $A.isMainElectron = app.$isMainElectron;
$A.isSubElectron = app.$isSubElectron;
$A.execMainDispatch = (action, data) => {
if ($A.isSubElectron) {
$A.Electron.ipcRenderer.send('sendForwardMain', {
$A.Electron.sendMessage('sendForwardMain', {
channel: 'dispatch',
data: {action, data},
});

View File

@ -44,7 +44,7 @@ export default {
this.getReleases();
//
if (this.$Electron) {
this.$Electron.ipcRenderer.on('downloadDone', (event, {result}) => {
this.$Electron.registerMsgListener('downloadDone', (event, {result}) => {
if (result.name == this.repoData.name) {
this.downloadResult = result;
this.releasesNotification()
@ -186,7 +186,7 @@ export default {
if (this.compareVersion(latestVersion, currentVersion) === 1) {
//
console.log("New version: " + latestVersion);
this.$Electron.ipcRenderer.send('downloadFile', {
this.$Electron.sendMessage('downloadFile', {
url: this.repoData.browser_download_url
});
}
@ -241,10 +241,10 @@ export default {
if (!this.$Electron) {
return;
}
this.$Electron.ipcRenderer.send('openFile', {
this.$Electron.sendMessage('openFile', {
path: this.downloadResult.savePath
});
this.$Electron.ipcRenderer.send('windowQuit');
this.$Electron.sendMessage('windowQuit');
},
useSSOLogin() {

View File

@ -366,7 +366,7 @@
return
}
if ($A.Electron) {
$A.Electron.shell.openExternal(url).catch(() => {
$A.Electron.openExternal(url).catch(() => {
$A.modalError("下载失败");
});
} else {

View File

@ -475,7 +475,7 @@ export default {
unreadTotal: {
handler(num) {
if (this.$Electron) {
this.$Electron.ipcRenderer.send('setDockBadge', num);
this.$Electron.sendMessage('setDockBadge', num);
}
},
immediate: true

View File

@ -213,7 +213,7 @@ export default {
viewFile() {
if (this.$Electron) {
this.$Electron.ipcRenderer.send('windowRouter', {
this.$Electron.sendMessage('windowRouter', {
title: `${this.msgData.msg.name} (${$A.bytesToSize(this.msgData.msg.size)})`,
titleFixed: true,
name: 'file-msg-' + this.msgData.id,

View File

@ -1126,7 +1126,7 @@ export default {
config.minWidth = 800;
config.minHeight = 600;
}
this.$Electron.ipcRenderer.send('windowRouter', {
this.$Electron.sendMessage('windowRouter', {
title: this.taskDetail.name,
titleFixed: true,
name: 'task-' + this.taskDetail.id,
@ -1139,7 +1139,7 @@ export default {
resizeDialog() {
return new Promise(resolve => {
this.$Electron.ipcRenderer.sendSync('windowSize', {
this.$Electron.sendSyncMessage('windowSize', {
width: Math.max(1100, window.innerWidth),
height: Math.max(720, window.innerHeight),
minWidth: 800,
@ -1161,7 +1161,7 @@ export default {
viewFile(file) {
if (this.$Electron) {
this.$Electron.ipcRenderer.send('windowRouter', {
this.$Electron.sendMessage('windowRouter', {
title: `${file.name} (${$A.bytesToSize(file.size)})`,
titleFixed: true,
name: 'file-task-' + file.id,

View File

@ -854,7 +854,7 @@ export default {
},
openSingle(item) {
this.$Electron.ipcRenderer.send('windowRouter', {
this.$Electron.sendMessage('windowRouter', {
title: this.formatName(item),
titleFixed: true,
userAgent: "/hideenOfficeTitle/",

16
webpack.mix.js vendored
View File

@ -21,24 +21,16 @@ mix
.js('resources/assets/js/app.js', 'js')
.sass('resources/assets/sass/app.scss', 'css')
.setPublicPath(publicPath)
.webpackConfig(webpack => {
.webpackConfig(() => {
let config = {
output: {
chunkFilename: ({chunk}) => {
return `js/build/${mixBuildName(chunk.id)}.js`
}
},
plugins: [
new webpack.DefinePlugin({
'__IS_ELECTRON': isElectron,
})
]
};
if (isElectron) {
config.target = 'electron-renderer'
if (!isHot) {
config.output.publicPath = './'
}
};
if (isElectron && !isHot) {
config.output.publicPath = './'
}
return config
})