|
|
@@ -8,6 +8,7 @@ const fs = require('fs');
|
|
|
const path = require('path');
|
|
|
const CoreWindow = require('ee-core/electron/window');
|
|
|
const { BrowserWindow, Menu,app } = require('electron');
|
|
|
+const { spawn } = require('child_process');
|
|
|
|
|
|
const { readConfigFile } = require('../utils/config');
|
|
|
const configDeault = readConfigFile();
|
|
|
@@ -28,6 +29,102 @@ class UtilsController extends Controller {
|
|
|
super(ctx);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 运行外部工具(如exe)
|
|
|
+ * @param {{exeName?: string, exePath?: string, args?: string[]}} params
|
|
|
+ */
|
|
|
+ async runExternalTool (params = {}) {
|
|
|
+ try {
|
|
|
+ const { exeName, exePath, args = [] } = params;
|
|
|
+ const targetName = exePath || exeName;
|
|
|
+
|
|
|
+ if (!targetName) {
|
|
|
+ throw new Error('缺少可执行文件名称');
|
|
|
+ }
|
|
|
+
|
|
|
+ const isPackaged = app.isPackaged;
|
|
|
+ const execDir = isPackaged ? path.dirname(app.getPath('exe')) : path.join(app.getAppPath(), '.');
|
|
|
+ const resourcesDir = process.resourcesPath;
|
|
|
+ const candidates = [];
|
|
|
+
|
|
|
+ const pushCandidate = (candidatePath, isAbsolute = false) => {
|
|
|
+ if (!candidatePath) return;
|
|
|
+ const normalized = isAbsolute || path.isAbsolute(candidatePath)
|
|
|
+ ? candidatePath
|
|
|
+ : path.join(execDir, candidatePath);
|
|
|
+ if (!candidates.includes(normalized)) {
|
|
|
+ candidates.push(normalized);
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 允许前端显式传入候选路径数组
|
|
|
+ if (Array.isArray(params.candidatePaths)) {
|
|
|
+ params.candidatePaths.forEach((p) => pushCandidate(p));
|
|
|
+ }
|
|
|
+
|
|
|
+ if (path.isAbsolute(targetName)) {
|
|
|
+ pushCandidate(targetName, true);
|
|
|
+ } else {
|
|
|
+ if (isPackaged) {
|
|
|
+ pushCandidate(path.join(execDir, targetName), true);
|
|
|
+ pushCandidate(path.join(resourcesDir, targetName), true);
|
|
|
+ pushCandidate(path.join(resourcesDir, 'extraResources', targetName), true);
|
|
|
+ } else {
|
|
|
+ pushCandidate(targetName);
|
|
|
+ pushCandidate(path.join('build', 'extraResources', targetName));
|
|
|
+ pushCandidate(path.join('extraResources', targetName));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log('=======');
|
|
|
+ console.log(candidates);
|
|
|
+ const resolvedPath = candidates.find((filePath) => filePath && fs.existsSync(filePath));
|
|
|
+
|
|
|
+ if (!resolvedPath) {
|
|
|
+ throw new Error(`未找到工具:${targetName}`);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果已经有正在运行的子进程,则先关闭旧进程,再重新启动新的(保证始终只有一个进程,同时刷新页面参数)
|
|
|
+ if (this.app.externalToolProcess && !this.app.externalToolProcess.killed) {
|
|
|
+ try {
|
|
|
+ this.app.externalToolProcess.kill();
|
|
|
+ } catch (e) {
|
|
|
+ console.warn('关闭旧 externalTool 进程失败(忽略继续启动新进程):', e);
|
|
|
+ }
|
|
|
+ this.app.externalToolProcess = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ const child = spawn(resolvedPath, args, {
|
|
|
+ cwd: path.dirname(resolvedPath),
|
|
|
+ detached: true,
|
|
|
+ stdio: 'ignore',
|
|
|
+ windowsHide: false
|
|
|
+ });
|
|
|
+
|
|
|
+ // 记录子进程,方便后续复用/判断是否已退出
|
|
|
+ this.app.externalToolProcess = child;
|
|
|
+ child.on('exit', () => {
|
|
|
+ if (this.app.externalToolProcess === child) {
|
|
|
+ this.app.externalToolProcess = null;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ child.unref();
|
|
|
+ return {
|
|
|
+ code: 0,
|
|
|
+ data: {
|
|
|
+ path: resolvedPath,
|
|
|
+ reused: false
|
|
|
+ }
|
|
|
+ };
|
|
|
+ } catch (error) {
|
|
|
+ console.error('runExternalTool error:', error);
|
|
|
+ return {
|
|
|
+ code: 1,
|
|
|
+ msg: error.message || '运行外部工具失败'
|
|
|
+ };
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
|
|
|
/**
|
|
|
* 所有方法接收两个参数
|