main.mjs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. import { app, BrowserWindow, ipcMain, dialog } from "electron";
  2. import { fileURLToPath } from "url";
  3. import path from "path";
  4. import { execFile } 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. // **开发环境**
  40. pythonExePath = path.join(__dirname, "../serves/dist/api_test.exe");
  41. } else {
  42. //这里需要区分开发环境还是生产环境 生产环境用到这个路径
  43. pythonExePath = path.join(
  44. process.resourcesPath, // `app.asar.unpacked` 的默认路径
  45. "app.asar.unpacked",
  46. "serves",
  47. "dist",
  48. "api_test.exe"
  49. );
  50. }
  51. // **修改 Python EXE 的路径**
  52. if (!fs.existsSync(pythonExePath)) {
  53. reject(`Python EXE 不存在: ${pythonExePath}`);
  54. return;
  55. }
  56. // **参数列表:API 名称 + JSON 格式参数**
  57. // const args = [apiName, JSON.stringify(params)];
  58. // ✅ **解决中文乱码**:使用 Base64 进行参数传输
  59. const args = [
  60. apiName,
  61. Buffer.from(JSON.stringify(params)).toString("base64"),
  62. ];
  63. const res = execFile(
  64. pythonExePath,
  65. args,
  66. { encoding: "utf8" },
  67. (error, stdout, stderr) => {
  68. if (error) {
  69. console.error("❌ Python EXE 运行失败:", error);
  70. reject(`Error: ${error.message}`);
  71. return;
  72. }
  73. if (stderr) {
  74. console.warn("⚠️ Python EXE 输出警告:", stderr);
  75. }
  76. // **尝试解析 JSON**
  77. try {
  78. console.log("✅ Python EXE 输出:", stdout);
  79. resolve(JSON.parse(stdout)); // 返回 JSON 数据
  80. } catch (parseError) {
  81. console.warn("⚠️ Python EXE 返回的不是 JSON:", stdout);
  82. resolve(stdout.trim()); // 返回原始文本
  83. }
  84. }
  85. );
  86. // **✅ 实时查看 Python print 的输出**
  87. res.stdout.on("data", (data) => {
  88. console.log("🐍 Python stdout:", data.toString());
  89. });
  90. res.stderr.on("data", (data) => {
  91. console.error("🐍 Python stderr:", data.toString());
  92. });
  93. });
  94. });
  95. ipcMain.handle("get-install-path", async () => {
  96. return process.execPath; // 获取可执行文件路径
  97. });
  98. // 监听前端请求,读取 CSV 文件
  99. ipcMain.handle("read-csv", async (event, filePath) => {
  100. try {
  101. const csvData = fs.readFileSync(filePath, "utf-8");
  102. const parsedData = Papa.parse(csvData, {
  103. header: true,
  104. skipEmptyLines: true, // ← 这个也可以帮助跳过完全空的行
  105. transformHeader: (header) => header.trim(), // ← 这个也可以帮助跳过完全空的行
  106. });
  107. // 过滤掉所有字段都为空的行
  108. const cleanData = parsedData.data.filter((row) =>
  109. Object.values(row).some((val) => val && val.trim() !== "")
  110. );
  111. return cleanData; // 返回解析后的数据
  112. } catch (error) {
  113. console.error("读取 CSV 失败:", error);
  114. return { error: error.message };
  115. }
  116. });
  117. // 监听 get-file-path 事件 监听渲染进程请求文件路径
  118. ipcMain.handle("get-file-path", async () => {
  119. try {
  120. const { filePaths } = await dialog.showOpenDialog({
  121. properties: ["openFile", "multiSelections"], // ✅ 允许选择多个文件
  122. });
  123. if (filePaths.length > 0) {
  124. console.log("用户选择的文件路径:", filePaths[0]);
  125. return filePaths[0]; // 返回文件路径
  126. } else {
  127. console.log("用户取消了选择");
  128. return null; // 用户没有选择文件
  129. }
  130. } catch (error) {
  131. console.error("获取文件路径失败:", error);
  132. return null;
  133. }
  134. });
  135. mainWindow.on("close", (event) => {
  136. event.preventDefault(); // ✅ 阻止默认关闭行为
  137. const choice = dialog.showMessageBoxSync(mainWindow, {
  138. type: "question",
  139. buttons: ["取消", "退出"],
  140. defaultId: 0, // 默认选中 "取消"
  141. title: "确认退出",
  142. message: "退出程序正在分析中的数据会丢失,确定要退出程序吗?",
  143. });
  144. if (choice === 1) {
  145. // ✅ 直接移除监听器,允许窗口关闭
  146. mainWindow.removeAllListeners("close");
  147. mainWindow.close();
  148. }
  149. });
  150. mainWindow.on("closed", () => {
  151. mainWindow = null; // ✅ 窗口关闭后清空引用
  152. });
  153. }
  154. app.whenReady().then(createWindow);
  155. app.on("window-all-closed", () => {
  156. if (process.platform !== "darwin") app.quit();
  157. });
  158. app.on("activate", () => {
  159. if (BrowserWindow.getAllWindows().length === 0) createWindow();
  160. });
  161. export { createWindow };