2025-10-17 15:17:55 +08:00
|
|
|
|
import { app, shell, BrowserWindow, ipcMain, globalShortcut, dialog } from 'electron'
|
|
|
|
|
|
import path, { join } from 'path'
|
|
|
|
|
|
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
|
2025-08-29 09:35:52 +08:00
|
|
|
|
import icon from '../../resources/earth.png?asset'
|
2025-10-17 15:17:55 +08:00
|
|
|
|
import { Recorder } from "../preload/recorder";
|
2025-08-29 09:35:52 +08:00
|
|
|
|
import fs from 'fs'
|
2025-10-17 15:13:56 +08:00
|
|
|
|
import { exec, spawn } from 'child_process'
|
2025-09-11 18:06:03 +08:00
|
|
|
|
import dayjs from 'dayjs'
|
2025-10-17 15:13:56 +08:00
|
|
|
|
import os from "os";
|
|
|
|
|
|
import { GetHomeDir } from './config'
|
|
|
|
|
|
import { start, getServer } from "./app";
|
|
|
|
|
|
const yaml = require("js-yaml");
|
2025-08-29 09:35:52 +08:00
|
|
|
|
|
2025-09-09 10:06:18 +08:00
|
|
|
|
|
2025-08-29 09:35:52 +08:00
|
|
|
|
// 开发环境路径处理 - 确保添加正确的file协议
|
|
|
|
|
|
const devSplashPath = path.resolve(
|
|
|
|
|
|
app.getAppPath(),
|
|
|
|
|
|
'src',
|
|
|
|
|
|
'renderer',
|
|
|
|
|
|
'public',
|
|
|
|
|
|
'startUp',
|
|
|
|
|
|
'startUp.html'
|
|
|
|
|
|
)
|
|
|
|
|
|
// 开发环境必须添加file协议,使用URL构造器确保格式正确
|
|
|
|
|
|
const devSplashURL = new URL(`file:///${devSplashPath}`).href
|
|
|
|
|
|
|
|
|
|
|
|
// 生产环境路径处理
|
|
|
|
|
|
let prodSplashURL = ''
|
|
|
|
|
|
const prodPossiblePaths = [
|
|
|
|
|
|
path.join(process.resourcesPath, 'app.asar', 'out', 'renderer', 'startUp', 'startUp.html'),
|
|
|
|
|
|
path.join(process.resourcesPath, 'app.asar', 'renderer', 'startUp', 'startUp.html'),
|
|
|
|
|
|
path.join(process.resourcesPath, 'out', 'renderer', 'startUp', 'startUp.html'),
|
|
|
|
|
|
// 增加asar包外可能的路径
|
|
|
|
|
|
path.join(process.resourcesPath, 'app.asar.unpacked', 'resources', 'startUp', 'startUp.html')
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
// 寻找存在的生产环境路径
|
|
|
|
|
|
const prodSplashPath = prodPossiblePaths.find((p) => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 检查路径是否存在,处理asar内部文件检查
|
|
|
|
|
|
return fs.existsSync(p)
|
|
|
|
|
|
} catch (e: any) {
|
|
|
|
|
|
console.error('检查路径时出错:', p, e.message)
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
if (prodSplashPath) {
|
|
|
|
|
|
// 使用URL构造器处理路径,自动编码特殊字符
|
|
|
|
|
|
// 对于Windows系统,需要处理盘符前的斜杠问题
|
|
|
|
|
|
const normalizedPath =
|
|
|
|
|
|
process.platform === 'win32' ? prodSplashPath.replace(/^(\w:)/, '/$1') : prodSplashPath
|
|
|
|
|
|
|
|
|
|
|
|
prodSplashURL = new URL(`file://${normalizedPath}`).href
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.error('未找到有效的启动页路径,检查打包配置和文件是否存在')
|
|
|
|
|
|
// 可以在这里添加默认路径或错误处理
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 最终的启动页URL
|
|
|
|
|
|
const splashURL = process.env.NODE_ENV === 'development' ? devSplashURL : prodSplashURL
|
2025-09-25 16:21:00 +08:00
|
|
|
|
let startBatPath = process.env.NODE_ENV === 'development' ? path.resolve(app.getAppPath(), 'resources', 'java', 'start.bat') : path.join(process.resourcesPath, 'app.asar.unpacked', 'resources', 'java', 'start.bat')
|
2025-09-11 18:27:56 +08:00
|
|
|
|
startBatPath = process.platform === 'win32' ? startBatPath.replace(/^(\w:)/, '/$1') : startBatPath
|
2025-09-25 16:21:00 +08:00
|
|
|
|
let stopBatPath = process.env.NODE_ENV === 'development' ? path.resolve(app.getAppPath(), 'resources', 'java', 'stop.bat') : path.join(process.resourcesPath, 'app.asar.unpacked', 'resources', 'java', 'stop.bat')
|
2025-09-11 18:27:56 +08:00
|
|
|
|
stopBatPath = process.platform === 'win32' ? stopBatPath.replace(/^(\w:)/, '/$1') : stopBatPath
|
|
|
|
|
|
|
2025-08-29 09:35:52 +08:00
|
|
|
|
// const splashURL =
|
|
|
|
|
|
// process.env.NODE_ENV === 'development'
|
|
|
|
|
|
// ? `${join(app.getAppPath(), 'src/renderer/public/startUp/startUp.html')}`
|
|
|
|
|
|
// : `file://${join(app.getAppPath(), 'resources/app.asar/out/renderer/startUp/startUp.html')}`
|
2025-09-30 15:38:29 +08:00
|
|
|
|
let isRestart = false
|
2025-10-15 14:06:08 +08:00
|
|
|
|
|
2025-08-29 09:35:52 +08:00
|
|
|
|
function createWindow(): void {
|
|
|
|
|
|
// Create the browser window.
|
|
|
|
|
|
|
2025-09-23 11:17:50 +08:00
|
|
|
|
start();
|
|
|
|
|
|
|
2025-08-29 09:35:52 +08:00
|
|
|
|
// 创建启动窗口
|
|
|
|
|
|
const splashWindow = new BrowserWindow({
|
|
|
|
|
|
width: 896,
|
|
|
|
|
|
height: 510,
|
|
|
|
|
|
frame: false,
|
|
|
|
|
|
alwaysOnTop: true,
|
|
|
|
|
|
show: false,
|
|
|
|
|
|
webPreferences: {
|
|
|
|
|
|
nodeIntegration: true, // 开启 Node 集成
|
|
|
|
|
|
contextIsolation: false, // 关闭上下文隔离
|
|
|
|
|
|
devTools: true,
|
|
|
|
|
|
webSecurity: false,
|
|
|
|
|
|
allowRunningInsecureContent: true
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 显示启动页
|
|
|
|
|
|
splashWindow.loadURL(splashURL)
|
|
|
|
|
|
splashWindow.show()
|
|
|
|
|
|
// 创建主窗口(保持原有配置,但先不显示)
|
|
|
|
|
|
const mainWindow = new BrowserWindow({
|
|
|
|
|
|
minWidth: 1780, // 添加最小宽度限制
|
|
|
|
|
|
minHeight: 920, // 添加最小高度限制
|
|
|
|
|
|
// fullscreen: true,
|
|
|
|
|
|
show: false,
|
|
|
|
|
|
frame: true,
|
|
|
|
|
|
autoHideMenuBar: true,
|
|
|
|
|
|
useContentSize: true, // 窗口尺寸包含内容区域而非边框
|
|
|
|
|
|
simpleFullscreen: true, // 使用简单全屏模式(仅macOS有效)
|
|
|
|
|
|
backgroundColor: '#00000000', // 添加这行设置透明背景
|
2025-10-17 15:17:55 +08:00
|
|
|
|
...(process.platform === 'linux' ? { icon } : {}),
|
2025-08-29 09:35:52 +08:00
|
|
|
|
webPreferences: {
|
|
|
|
|
|
preload: join(__dirname, '../preload/index.js'),
|
|
|
|
|
|
sandbox: false,
|
|
|
|
|
|
nodeIntegration: true, // 开启 Node 集成
|
|
|
|
|
|
contextIsolation: false, // 关闭上下文隔离
|
|
|
|
|
|
devTools: true,
|
|
|
|
|
|
webSecurity: false,
|
|
|
|
|
|
allowRunningInsecureContent: true
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
2025-10-17 15:13:56 +08:00
|
|
|
|
let ymlBatPath = process.env.NODE_ENV === 'development' ? path.resolve(app.getAppPath(), 'resources', 'java', 'app', 'application.yml') : path.join(process.resourcesPath, 'app.asar.unpacked', 'resources', 'java', 'app', 'application.yml')
|
|
|
|
|
|
ymlBatPath = process.platform === 'win32' ? ymlBatPath.replace(/^(\w:)/, '/$1') : ymlBatPath
|
|
|
|
|
|
ipcMain.on("getIniConfig", (event, option) => {
|
|
|
|
|
|
let ymlPath = ymlBatPath.substring(1, 200)
|
|
|
|
|
|
console.log("iniPath", ymlPath);
|
|
|
|
|
|
|
|
|
|
|
|
const ymlContent = yaml.load(fs.readFileSync(ymlPath, 'utf8'));
|
|
|
|
|
|
|
2025-10-17 15:17:55 +08:00
|
|
|
|
if (option) {
|
2025-10-17 15:13:56 +08:00
|
|
|
|
ymlContent.server.port = option.port
|
|
|
|
|
|
fs.writeFileSync(ymlPath, yaml.dump(ymlContent));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
event.sender.send("YmlConfig", ymlContent);
|
|
|
|
|
|
});
|
2025-09-26 11:37:44 +08:00
|
|
|
|
ipcMain.on("restart", () => {
|
2025-09-30 15:38:29 +08:00
|
|
|
|
// app.relaunch();
|
|
|
|
|
|
// app.quit();
|
2025-10-15 14:06:08 +08:00
|
|
|
|
// cleanupProcess.kill();
|
2025-09-30 15:38:29 +08:00
|
|
|
|
// app.relaunch();
|
|
|
|
|
|
isRestart = true
|
2025-10-17 15:13:56 +08:00
|
|
|
|
closeAllWindows()
|
2025-09-26 11:37:44 +08:00
|
|
|
|
});
|
2025-08-29 09:35:52 +08:00
|
|
|
|
// 监听启动页完成的消息
|
|
|
|
|
|
ipcMain.on('splash-completed', () => {
|
|
|
|
|
|
// 启动页进度条已完成,可以关闭启动页并显示主窗口
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
splashWindow.destroy()
|
|
|
|
|
|
mainWindow.maximize() // 先最大化
|
|
|
|
|
|
mainWindow.show()
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
mainWindow.webContents.send('start-login-video')
|
|
|
|
|
|
}, 200) // 给一点时间让窗口完全显示
|
|
|
|
|
|
}, 1000)
|
|
|
|
|
|
})
|
|
|
|
|
|
ipcMain.on('quit-app', () => {
|
2025-09-25 16:21:00 +08:00
|
|
|
|
windowAllClosed()
|
2025-08-29 09:35:52 +08:00
|
|
|
|
})
|
|
|
|
|
|
ipcMain.on('renderNode', () => {
|
|
|
|
|
|
// 获取所有窗口并转发消息
|
|
|
|
|
|
BrowserWindow.getAllWindows().forEach((win) => {
|
|
|
|
|
|
// 移除条件判断,确保所有窗口都能收到消息
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
win.webContents.send('renderNode-reply')
|
|
|
|
|
|
}, 200)
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
2025-09-11 18:27:56 +08:00
|
|
|
|
ipcMain.on("open-directory-dialog", (event, option) => {
|
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
|
dialog.showOpenDialog(BrowserWindow.getFocusedWindow(), {
|
2025-09-18 20:48:30 +08:00
|
|
|
|
properties: option.properties,
|
|
|
|
|
|
filters: option.filters,
|
|
|
|
|
|
})
|
2025-09-11 18:27:56 +08:00
|
|
|
|
.then((files) => {
|
|
|
|
|
|
let arr = [];
|
|
|
|
|
|
if (!files.canceled) {
|
|
|
|
|
|
files.filePaths.forEach((url) => {
|
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
|
arr.push(url.replace(/\\/g, "/"));
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
event.sender.send("selectedItem", arr);
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
2025-10-17 15:17:55 +08:00
|
|
|
|
ipcMain.on("saveFile", (event, { title, filename, filters }) => {
|
2025-09-11 18:06:03 +08:00
|
|
|
|
dialog
|
|
|
|
|
|
.showSaveDialog({
|
|
|
|
|
|
title,
|
|
|
|
|
|
defaultPath: filename,
|
|
|
|
|
|
filters,
|
|
|
|
|
|
})
|
|
|
|
|
|
.then((files) => {
|
|
|
|
|
|
let path = "";
|
|
|
|
|
|
if (!files.canceled) {
|
|
|
|
|
|
path = files.filePath.replace(/\\/g, "/");
|
|
|
|
|
|
}
|
|
|
|
|
|
event.sender.send("selectedFileItem", path);
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
2025-10-17 15:17:55 +08:00
|
|
|
|
|
|
|
|
|
|
ipcMain.on("saveNetFile", (event, { title, filename, filters, url }) => {
|
|
|
|
|
|
dialog
|
|
|
|
|
|
.showSaveDialog({
|
|
|
|
|
|
title,
|
|
|
|
|
|
defaultPath: filename,
|
|
|
|
|
|
filters,
|
|
|
|
|
|
})
|
|
|
|
|
|
.then((files) => {
|
|
|
|
|
|
let path = "";
|
|
|
|
|
|
if (!files.canceled) {
|
|
|
|
|
|
path = files.filePath.replace(/\\/g, "/");
|
|
|
|
|
|
function callBack(key) {
|
|
|
|
|
|
console.log("下载完成");
|
|
|
|
|
|
event.sender.send("saveNetFileRes", key);
|
|
|
|
|
|
}
|
|
|
|
|
|
function downloadFile(url, path) {
|
|
|
|
|
|
/*request(
|
|
|
|
|
|
url,
|
|
|
|
|
|
{ headers: { Accept: "application/octet-stream" } },
|
|
|
|
|
|
(err, res, body) => {
|
|
|
|
|
|
if (!err && res.statusCode === 200) {
|
|
|
|
|
|
const filePath = path;
|
|
|
|
|
|
fs.writeFileSync(filePath, body);
|
|
|
|
|
|
console.log(url);
|
|
|
|
|
|
console.log(`文件已保存到: ${filePath}`);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.error("下载文件失败:", err);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
);*/
|
|
|
|
|
|
http
|
|
|
|
|
|
.get(url, (response) => {
|
|
|
|
|
|
let contentLength = parseInt(
|
|
|
|
|
|
response.headers["content-length"]
|
|
|
|
|
|
);
|
|
|
|
|
|
let downloadedLength = 0;
|
|
|
|
|
|
response.pipe(fs.createWriteStream(path));
|
|
|
|
|
|
response.on("end", () => {
|
|
|
|
|
|
callBack("success");
|
|
|
|
|
|
// Message.success('下载成功')
|
|
|
|
|
|
// dialog.showMessageBox(null,{type:'info',message:"下载完成"})
|
|
|
|
|
|
});
|
|
|
|
|
|
})
|
|
|
|
|
|
.on("error", (err) => {
|
|
|
|
|
|
console.log("完成");
|
|
|
|
|
|
callBack("error");
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
downloadFile(url, path);
|
|
|
|
|
|
}
|
|
|
|
|
|
/* filePaths = path;
|
|
|
|
|
|
webContents.downloadURL(url);*/
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
2025-09-30 15:38:29 +08:00
|
|
|
|
ipcMain.handle('getIsFullScreen', () => {
|
|
|
|
|
|
return mainWindow.isFullScreen()
|
|
|
|
|
|
});
|
2025-10-15 14:06:08 +08:00
|
|
|
|
ipcMain.on('toggle-fullscreen', (event, flag = null) => {
|
2025-09-30 15:38:29 +08:00
|
|
|
|
const win = BrowserWindow.fromWebContents(event.sender);
|
2025-10-15 14:06:08 +08:00
|
|
|
|
let full = !win!.isFullScreen()
|
|
|
|
|
|
if (flag != null) {
|
|
|
|
|
|
full = flag
|
|
|
|
|
|
}
|
|
|
|
|
|
win!.setFullScreen(full);
|
2025-09-30 15:38:29 +08:00
|
|
|
|
});
|
|
|
|
|
|
mainWindow.on('enter-full-screen', () => {
|
|
|
|
|
|
mainWindow.webContents.send('fullscreen-status-changed', true)
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
mainWindow.on('leave-full-screen', () => {
|
|
|
|
|
|
mainWindow.webContents.send('fullscreen-status-changed', false)
|
|
|
|
|
|
});
|
2025-09-11 18:06:03 +08:00
|
|
|
|
|
|
|
|
|
|
let recorder;
|
2025-09-11 19:17:52 +08:00
|
|
|
|
ipcMain.on("startRecoder", () => {
|
2025-09-11 18:06:03 +08:00
|
|
|
|
console.log("开始录制");
|
|
|
|
|
|
recorder = new Recorder();
|
|
|
|
|
|
recorder.start();
|
|
|
|
|
|
});
|
2025-09-11 19:17:52 +08:00
|
|
|
|
ipcMain.on("endRecoder", () => {
|
2025-09-11 18:06:03 +08:00
|
|
|
|
console.log("结束录制");
|
|
|
|
|
|
// 判断是否存在recorder,是否有recorder.end方法
|
|
|
|
|
|
if (!recorder) {
|
|
|
|
|
|
console.log("recorder不存在");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
recorder.end(() => {
|
|
|
|
|
|
let path = dialog.showSaveDialogSync({
|
|
|
|
|
|
title: "保存视频文件",
|
|
|
|
|
|
defaultPath: dayjs().format("YYYYMMDDHHmmss") + "视频录制.mp4",
|
2025-10-17 15:17:55 +08:00
|
|
|
|
filters: [{ name: "文件类型", extensions: ["mp4"] }],
|
2025-09-11 18:06:03 +08:00
|
|
|
|
});
|
|
|
|
|
|
if (path != undefined) {
|
|
|
|
|
|
recorder.move(path, () => {
|
|
|
|
|
|
recorder = null;
|
|
|
|
|
|
});
|
|
|
|
|
|
} else {
|
|
|
|
|
|
recorder = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
2025-09-23 11:17:50 +08:00
|
|
|
|
ipcMain.on("requireGEMarkerName", (event, obj) => {
|
|
|
|
|
|
/*
|
|
|
|
|
|
* obj= {
|
|
|
|
|
|
dirName: "GEMarker",
|
|
|
|
|
|
dirName1s: "GEMarker1s"
|
|
|
|
|
|
}
|
|
|
|
|
|
* */
|
|
|
|
|
|
|
2025-09-26 19:14:47 +08:00
|
|
|
|
// console.log('GetHomeDir()', GetHomeDir())
|
|
|
|
|
|
let prefix =
|
2025-09-30 15:38:29 +08:00
|
|
|
|
process.env.NODE_ENV === "development"
|
|
|
|
|
|
? "src/renderer/public"
|
|
|
|
|
|
: "resources/app.asar/out/renderer";
|
2025-09-23 11:17:50 +08:00
|
|
|
|
let data = {};
|
|
|
|
|
|
for (const objKey in obj) {
|
|
|
|
|
|
let files = fs.readdirSync(
|
|
|
|
|
|
path.join(
|
2025-09-26 19:14:47 +08:00
|
|
|
|
GetHomeDir(),
|
|
|
|
|
|
prefix,
|
2025-09-23 11:17:50 +08:00
|
|
|
|
obj[objKey]
|
|
|
|
|
|
)
|
|
|
|
|
|
);
|
2025-09-26 19:14:47 +08:00
|
|
|
|
// console.log(files);
|
2025-09-23 11:17:50 +08:00
|
|
|
|
for (let i = 0; i < files.length; i++) {
|
|
|
|
|
|
files[i] = obj[objKey] + "/" + files[i];
|
|
|
|
|
|
}
|
|
|
|
|
|
data[obj[objKey]] = files;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// let files = fs.readdirSync(path.join(global.__static ? global.__static : GetHomeDir() + "/static", dirName))
|
|
|
|
|
|
// data[dirName] = files
|
|
|
|
|
|
|
|
|
|
|
|
event.sender.send("dirFiles", data);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
let _winMap = new Map();
|
2025-09-18 20:48:30 +08:00
|
|
|
|
// 监听渲染进程创建新窗口的请求
|
2025-09-18 20:54:17 +08:00
|
|
|
|
// @ts-ignore
|
2025-09-23 11:17:50 +08:00
|
|
|
|
ipcMain.handle('create-new-window', async (event, params, url, option, id) => {
|
2025-09-18 20:48:30 +08:00
|
|
|
|
try {
|
|
|
|
|
|
const newWindow = await new BrowserWindow(params)
|
|
|
|
|
|
if (url) {
|
|
|
|
|
|
await newWindow.loadURL(url)
|
|
|
|
|
|
await newWindow.webContents.send("data", option)
|
2025-09-23 11:17:50 +08:00
|
|
|
|
newWindow.on('closed', () => {
|
|
|
|
|
|
_winMap.delete(id);
|
|
|
|
|
|
// a.delete(newWindow.id)
|
|
|
|
|
|
// closeCB(newWindow.id)
|
|
|
|
|
|
});
|
2025-09-18 20:48:30 +08:00
|
|
|
|
}
|
2025-09-23 11:17:50 +08:00
|
|
|
|
_winMap.set(id, newWindow.id);
|
|
|
|
|
|
return _winMap
|
2025-09-18 20:48:30 +08:00
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('创建窗口失败:', error);
|
|
|
|
|
|
throw error; // 抛出错误以便渲染进程捕获
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
2025-09-23 11:17:50 +08:00
|
|
|
|
//@ts-ignore
|
|
|
|
|
|
ipcMain.handle('show-window-by-id', async (event, id) => {
|
|
|
|
|
|
BrowserWindow.fromId(id)?.show()
|
|
|
|
|
|
})
|
2025-09-25 16:21:00 +08:00
|
|
|
|
ipcMain.handle('get-_winMap', () => {
|
2025-09-23 11:17:50 +08:00
|
|
|
|
return _winMap
|
|
|
|
|
|
})
|
2025-10-17 15:13:56 +08:00
|
|
|
|
ipcMain.on("openFFPlay", (e, obj) => {
|
|
|
|
|
|
let cmd = "";
|
|
|
|
|
|
let platform = os.platform();
|
|
|
|
|
|
if (platform === "win32") {
|
|
|
|
|
|
cmd = "ffplay.exe";
|
|
|
|
|
|
} else {
|
|
|
|
|
|
cmd = "ffplay";
|
|
|
|
|
|
}
|
|
|
|
|
|
let title = obj.name;
|
|
|
|
|
|
let child = spawn(
|
|
|
|
|
|
path.join(GetHomeDir(), `/ffplay/${cmd}`),
|
|
|
|
|
|
[
|
|
|
|
|
|
"-window_title",
|
|
|
|
|
|
title,
|
|
|
|
|
|
"-x",
|
|
|
|
|
|
"1300",
|
|
|
|
|
|
"-y",
|
|
|
|
|
|
"730",
|
|
|
|
|
|
"-rtsp_transport",
|
|
|
|
|
|
"tcp",
|
|
|
|
|
|
obj.url,
|
|
|
|
|
|
],
|
|
|
|
|
|
{
|
|
|
|
|
|
cwd: path.join(GetHomeDir(), "/ffplay/"),
|
|
|
|
|
|
stdio: "ignore",
|
|
|
|
|
|
// shell: true,
|
|
|
|
|
|
}
|
|
|
|
|
|
).on("exit", (err) => {
|
|
|
|
|
|
console.log("out");
|
|
|
|
|
|
console.log(err);
|
|
|
|
|
|
e.sender.send("openFFPlayOut", err);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
/* .on("stdout", function(err, m) {
|
|
|
|
|
|
console.log(m);
|
|
|
|
|
|
});*/
|
|
|
|
|
|
console.log("child", child.pid);
|
|
|
|
|
|
child.unref();
|
|
|
|
|
|
});
|
2025-08-29 09:35:52 +08:00
|
|
|
|
// 设置窗口标题和图标
|
|
|
|
|
|
mainWindow.webContents.setWindowOpenHandler((details) => {
|
|
|
|
|
|
shell.openExternal(details.url)
|
2025-10-17 15:17:55 +08:00
|
|
|
|
return { action: 'deny' }
|
2025-08-29 09:35:52 +08:00
|
|
|
|
})
|
|
|
|
|
|
// 注册 F5 快捷键刷新
|
|
|
|
|
|
globalShortcut.register('CommandOrControl+F5', () => {
|
|
|
|
|
|
if (mainWindow) {
|
|
|
|
|
|
mainWindow.reload()
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
mainWindow.webContents.send('start-login-video')
|
|
|
|
|
|
}, 200) // 给一点时间让窗口完成刷新
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
globalShortcut.register('CommandOrControl+F12', () => {
|
|
|
|
|
|
mainWindow.webContents.openDevTools()
|
|
|
|
|
|
})
|
|
|
|
|
|
// HMR for renderer base on electron-vite cli.
|
|
|
|
|
|
// Load the remote URL for development or the local html file for production.
|
|
|
|
|
|
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
|
|
|
|
|
|
mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL'])
|
|
|
|
|
|
} else {
|
|
|
|
|
|
mainWindow.loadFile(join(__dirname, '../renderer/index.html'))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 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.whenReady().then(() => {
|
2025-09-11 18:27:56 +08:00
|
|
|
|
// alert(devSplashURL)
|
|
|
|
|
|
// alert(prodSplashURL)
|
|
|
|
|
|
// dialog.showMessageBox({
|
|
|
|
|
|
// type: 'info',
|
|
|
|
|
|
// title: '信息1',
|
|
|
|
|
|
// message: devSplashURL,
|
|
|
|
|
|
// buttons: ['确定']
|
|
|
|
|
|
// })
|
|
|
|
|
|
// 执行批处理文件
|
|
|
|
|
|
exec(startBatPath.substring(1, 200), (error, stdout, stderr) => {
|
|
|
|
|
|
if (error) {
|
|
|
|
|
|
console.error(`执行错误: ${error.message}`);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (stderr) {
|
|
|
|
|
|
console.error(`错误输出: ${stderr}`);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
console.log(`批处理输出: ${stdout}`);
|
|
|
|
|
|
});
|
2025-08-29 09:35:52 +08:00
|
|
|
|
// Set app user model id for windows
|
|
|
|
|
|
electronApp.setAppUserModelId('com.electron')
|
|
|
|
|
|
|
|
|
|
|
|
// Default open or close DevTools by F12 in development
|
|
|
|
|
|
// and ignore CommandOrControl + R in production.
|
|
|
|
|
|
// see https://github.com/alex8088/electron-toolkit/tree/master/packages/utils
|
|
|
|
|
|
app.on('browser-window-created', (_, window) => {
|
|
|
|
|
|
optimizer.watchWindowShortcuts(window)
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// IPC test
|
|
|
|
|
|
ipcMain.on('ping', () => console.log('pong'))
|
|
|
|
|
|
|
|
|
|
|
|
createWindow()
|
|
|
|
|
|
|
|
|
|
|
|
app.on('activate', function () {
|
|
|
|
|
|
// On macOS 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 (BrowserWindow.getAllWindows().length === 0) createWindow()
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// Quit when all windows are closed, except on macOS. There, it's common
|
|
|
|
|
|
// for applications and their menu bar to stay active until the user quits
|
|
|
|
|
|
// explicitly with Cmd + Q.
|
2025-09-25 16:21:00 +08:00
|
|
|
|
// app.on('window-all-closed', () => {
|
|
|
|
|
|
// if (process.platform !== 'darwin') {
|
|
|
|
|
|
// getServer().close(() => {
|
|
|
|
|
|
// // 关闭后台服务
|
|
|
|
|
|
// exec(stopBatPath.substring(1, 200), () => {
|
|
|
|
|
|
// app.quit()
|
|
|
|
|
|
// });
|
|
|
|
|
|
// })
|
|
|
|
|
|
// }
|
|
|
|
|
|
// })
|
2025-08-29 09:35:52 +08:00
|
|
|
|
// 退出时注销所有快捷键
|
|
|
|
|
|
app.on('will-quit', () => {
|
2025-09-23 11:17:50 +08:00
|
|
|
|
globalShortcut.unregisterAll()
|
2025-08-29 09:35:52 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
2025-09-25 16:21:00 +08:00
|
|
|
|
// 用于跟踪是否正在执行退出流程
|
|
|
|
|
|
let isQuitting = false;
|
|
|
|
|
|
|
|
|
|
|
|
app.on('window-all-closed', () => {
|
|
|
|
|
|
windowAllClosed()
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
function windowAllClosed() {
|
|
|
|
|
|
// 防止重复触发退出流程
|
|
|
|
|
|
if (isQuitting) return;
|
|
|
|
|
|
isQuitting = true;
|
|
|
|
|
|
|
|
|
|
|
|
// macOS特殊处理(保持应用活跃)
|
|
|
|
|
|
if (process.platform === 'darwin') {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
console.log('所有窗口已关闭,执行清理脚本...');
|
|
|
|
|
|
getServer().close(() => {
|
|
|
|
|
|
// 执行批处理文件
|
2025-10-17 15:13:56 +08:00
|
|
|
|
const cleanupProcess = exec('D:/project/electron-4.0/electron-4/resources/java/stop.bat', (error, stdout, stderr) => {
|
2025-09-25 16:21:00 +08:00
|
|
|
|
if (error) {
|
|
|
|
|
|
console.error(`清理脚本执行失败: ${error.message}`);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (stderr) {
|
|
|
|
|
|
console.error(`清理脚本错误输出: ${stderr}`);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (stdout) {
|
|
|
|
|
|
console.log(`清理脚本输出: ${stdout}`);
|
|
|
|
|
|
}
|
2025-10-17 15:13:56 +08:00
|
|
|
|
if (isQuitting) {
|
|
|
|
|
|
forceQuit();
|
|
|
|
|
|
}
|
2025-09-25 16:21:00 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 监听子进程退出事件(确保即使脚本出错也能退出)
|
|
|
|
|
|
cleanupProcess.on('exit', (code) => {
|
|
|
|
|
|
console.log(`清理脚本退出,代码: ${code}`);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 超时保护:防止脚本卡住导致应用无法退出
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
if (isQuitting) {
|
|
|
|
|
|
console.log('清理脚本执行超时,强制退出');
|
|
|
|
|
|
cleanupProcess.kill(); // 终止卡住的脚本
|
|
|
|
|
|
forceQuit();
|
|
|
|
|
|
}
|
2025-10-17 15:13:56 +08:00
|
|
|
|
}, 3000); // 3秒超时
|
2025-09-25 16:21:00 +08:00
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 强制退出应用的辅助函数
|
|
|
|
|
|
function forceQuit() {
|
|
|
|
|
|
// 终止所有剩余的子进程
|
|
|
|
|
|
if (process.platform === 'win32') {
|
|
|
|
|
|
// Windows系统特殊处理
|
2025-09-30 15:38:29 +08:00
|
|
|
|
// if (isRestart) {
|
|
|
|
|
|
// isRestart = false
|
|
|
|
|
|
// app.relaunch();
|
|
|
|
|
|
// }
|
2025-10-17 15:13:56 +08:00
|
|
|
|
// let child = exec('taskkill /F /T /PID ' + process.pid, (error) => {
|
|
|
|
|
|
// if (error) console.error('强制终止失败:', error);
|
|
|
|
|
|
// child.kill();
|
|
|
|
|
|
// });
|
2025-09-30 15:38:29 +08:00
|
|
|
|
if (isRestart) {
|
|
|
|
|
|
app.relaunch();
|
|
|
|
|
|
}
|
2025-10-17 15:13:56 +08:00
|
|
|
|
console.log('------退出-------');
|
|
|
|
|
|
app.exit();
|
|
|
|
|
|
app.quit();
|
2025-09-25 16:21:00 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
// 其他系统
|
|
|
|
|
|
process.exit(0);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-17 15:13:56 +08:00
|
|
|
|
function closeAllWindows() {
|
|
|
|
|
|
// 1. 获取所有已打开的窗口
|
|
|
|
|
|
const allWindows = BrowserWindow.getAllWindows();
|
2025-10-17 15:17:55 +08:00
|
|
|
|
|
2025-10-17 15:13:56 +08:00
|
|
|
|
// 2. 遍历关闭每个窗口
|
|
|
|
|
|
allWindows.forEach(window => {
|
|
|
|
|
|
if (!window.isDestroyed()) { // 避免操作已销毁的窗口(防止报错)
|
|
|
|
|
|
window.close();
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-23 11:17:50 +08:00
|
|
|
|
console.log('=================================================')
|
|
|
|
|
|
global.sharedObject = {
|
|
|
|
|
|
hasService: false,
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-08-29 09:35:52 +08:00
|
|
|
|
// In this file you can include the rest of your app's specific main process
|
|
|
|
|
|
// code. You can also put them in separate files and require them here.
|