main.mjs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. import { app, BrowserWindow, ipcMain, dialog } from "electron";
  2. import { fileURLToPath } from "url";
  3. import path from "path";
  4. import { execFile, spawn } from "child_process";
  5. import fs from "fs";
  6. import Papa from "./libs/papaparse/papaparse.js";
  7. process.env["ELECTRON_DISABLE_SECURITY_WARNINGS"] = "true";
  8. const __filename = fileURLToPath(import.meta.url);
  9. const __dirname = path.dirname(__filename);
  10. let mainWindow = null;
  11. function createWindow() {
  12. mainWindow = new BrowserWindow({
  13. width: 1400,
  14. height: 900,
  15. minWidth: 800,
  16. minHeight: 600,
  17. webPreferences: {
  18. preload: path.join(__dirname, "./preload.mjs"),
  19. contextIsolation: true,
  20. nodeIntegration: false,
  21. allowRunningInsecureContent: false,
  22. },
  23. icon: path.join(__dirname, "../src/assets/images/login/bg.png"), // 开发环境的图标
  24. });
  25. //环境判断引入不同的页面
  26. if (process.env.NODE_ENV === "development") {
  27. mainWindow.loadURL("http://localhost:5173");
  28. mainWindow.webContents.openDevTools();
  29. } else {
  30. //生产环境将引入打包好的文件
  31. mainWindow.loadFile(path.join(__dirname, "../dist/index.html"));
  32. mainWindow.webContents.openDevTools();
  33. }
  34. // **🔹 监听渲染进程调用 Python EXE**
  35. ipcMain.handle("run-python-exe", async (event, apiName, params = {}) => {
  36. return new Promise((resolve, reject) => {
  37. let pythonExePath = null;
  38. if (process.env.NODE_ENV === "development") {
  39. pythonExePath = path.join(__dirname, "../serves/dist/api_test.exe");
  40. } else {
  41. pythonExePath = path.join(
  42. process.resourcesPath,
  43. "app.asar.unpacked",
  44. "serves",
  45. "dist",
  46. "api_test.exe"
  47. );
  48. }
  49. if (!fs.existsSync(pythonExePath)) {
  50. reject(`Python EXE 不存在: ${pythonExePath}`);
  51. return;
  52. }
  53. const args = [
  54. apiName,
  55. Buffer.from(JSON.stringify(params)).toString("base64"),
  56. ];
  57. const child = spawn(pythonExePath, args, { encoding: "utf8" });
  58. let stdoutData = "";
  59. let stderrData = "";
  60. child.stdout.on("data", (data) => {
  61. const text = data.toString();
  62. stdoutData += text;
  63. console.log("🐍 Python stdout:", text);
  64. });
  65. child.stderr.on("data", (data) => {
  66. const text = data.toString();
  67. stderrData += text;
  68. console.error("🐍 Python stderr:", text);
  69. });
  70. child.on("close", (code) => {
  71. if (code !== 0) {
  72. reject(`Python EXE exited with code ${code}\n${stderrData}`);
  73. return;
  74. }
  75. try {
  76. resolve(JSON.parse(stdoutData));
  77. } catch (err) {
  78. console.warn("⚠️ Python EXE 输出不是 JSON:", stdoutData);
  79. resolve(stdoutData.trim());
  80. }
  81. });
  82. });
  83. });
  84. // ipcMain.handle("run-python-exe", async (event, apiName, params = {}) => {
  85. // return new Promise((resolve, reject) => {
  86. // let pythonExePath = null;
  87. // if (process.env.NODE_ENV === "development") {
  88. // // **开发环境**
  89. // pythonExePath = path.join(__dirname, "../serves/dist/api_test.exe");
  90. // } else {
  91. // //这里需要区分开发环境还是生产环境 生产环境用到这个路径
  92. // pythonExePath = path.join(
  93. // process.resourcesPath, // `app.asar.unpacked` 的默认路径
  94. // "app.asar.unpacked",
  95. // "serves",
  96. // "dist",
  97. // "api_test.exe"
  98. // );
  99. // }
  100. // // **修改 Python EXE 的路径**
  101. // if (!fs.existsSync(pythonExePath)) {
  102. // reject(`Python EXE 不存在: ${pythonExePath}`);
  103. // return;
  104. // }
  105. // // **参数列表:API 名称 + JSON 格式参数**
  106. // // const args = [apiName, JSON.stringify(params)];
  107. // // ✅ **解决中文乱码**:使用 Base64 进行参数传输
  108. // const args = [
  109. // apiName,
  110. // Buffer.from(JSON.stringify(params)).toString("base64"),
  111. // ];
  112. // const res = execFile(
  113. // pythonExePath,
  114. // args,
  115. // { encoding: "utf8" },
  116. // (error, stdout, stderr) => {
  117. // if (error) {
  118. // console.error("❌ Python EXE 运行失败:", error);
  119. // reject(`Error: ${error.message}`);
  120. // return;
  121. // }
  122. // if (stderr) {
  123. // console.warn("⚠️ Python EXE 输出警告:", stderr);
  124. // }
  125. // // **尝试解析 JSON**
  126. // try {
  127. // console.log("✅ Python EXE 输出:", stdout);
  128. // resolve(JSON.parse(stdout)); // 返回 JSON 数据
  129. // } catch (parseError) {
  130. // console.warn("⚠️ Python EXE 返回的不是 JSON:", stdout);
  131. // resolve(stdout.trim()); // 返回原始文本
  132. // }
  133. // }
  134. // );
  135. // // **✅ 实时查看 Python print 的输出**
  136. // res.stdout.on("data", (data) => {
  137. // console.log("🐍 Python stdout:", data.toString());
  138. // });
  139. // res.stderr.on("data", (data) => {
  140. // console.error("🐍 Python stderr:", data.toString());
  141. // });
  142. // });
  143. // });
  144. ipcMain.handle("get-install-path", async () => {
  145. return process.execPath; // 获取可执行文件路径
  146. });
  147. // 监听前端请求,读取 CSV 文件
  148. ipcMain.handle("read-csv", async (event, filePath) => {
  149. try {
  150. const csvData = fs.readFileSync(filePath, "utf-8");
  151. const parsedData = Papa.parse(csvData, {
  152. header: true,
  153. skipEmptyLines: true, // ← 这个也可以帮助跳过完全空的行
  154. transformHeader: (header) => header.trim(), // ← 这个也可以帮助跳过完全空的行
  155. });
  156. // 过滤掉所有字段都为空的行
  157. const cleanData = parsedData.data.filter((row) =>
  158. Object.values(row).some((val) => val && val.trim() !== "")
  159. );
  160. return cleanData; // 返回解析后的数据
  161. } catch (error) {
  162. console.error("读取 CSV 失败:", error);
  163. return { error: error.message };
  164. }
  165. });
  166. // 监听 get-file-path 事件 监听渲染进程请求文件路径
  167. ipcMain.handle("get-file-path", async () => {
  168. try {
  169. const { filePaths } = await dialog.showOpenDialog({
  170. properties: ["openFile", "multiSelections"], // ✅ 允许选择多个文件
  171. });
  172. if (filePaths.length > 0) {
  173. console.log("用户选择的文件路径:", filePaths[0]);
  174. return filePaths[0]; // 返回文件路径
  175. } else {
  176. console.log("用户取消了选择");
  177. return null; // 用户没有选择文件
  178. }
  179. } catch (error) {
  180. console.error("获取文件路径失败:", error);
  181. return null;
  182. }
  183. });
  184. mainWindow.on("close", (event) => {
  185. event.preventDefault(); // ✅ 阻止默认关闭行为
  186. const choice = dialog.showMessageBoxSync(mainWindow, {
  187. type: "question",
  188. buttons: ["取消", "退出"],
  189. defaultId: 0, // 默认选中 "取消"
  190. title: "确认退出",
  191. message: "退出程序正在分析中的数据会丢失,确定要退出程序吗?",
  192. });
  193. if (choice === 1) {
  194. // ✅ 直接移除监听器,允许窗口关闭
  195. mainWindow.removeAllListeners("close");
  196. mainWindow.close();
  197. }
  198. });
  199. mainWindow.on("closed", () => {
  200. mainWindow = null; // ✅ 窗口关闭后清空引用
  201. });
  202. }
  203. app.whenReady().then(createWindow);
  204. app.on("window-all-closed", () => {
  205. if (process.platform !== "darwin") app.quit();
  206. });
  207. app.on("activate", () => {
  208. if (BrowserWindow.getAllWindows().length === 0) createWindow();
  209. });
  210. export { createWindow };