main.mjs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. import { app, BrowserWindow, ipcMain, dialog, shell } from "electron";
  2. import { fileURLToPath } from "url";
  3. import path from "path";
  4. import { 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/icon.png"),//这里切换程序图标
  24. });
  25. if (process.env.NODE_ENV === "development") {
  26. mainWindow.loadURL("http://localhost:5173");
  27. // mainWindow.webContents.openDevTools(); //这是打开控制台
  28. } else {
  29. mainWindow.loadFile(path.join(__dirname, "../dist/index.html"));
  30. // mainWindow.webContents.openDevTools(); //这是打开控制台
  31. }
  32. // ✅ 运行 Python EXE
  33. ipcMain.handle("run-python-exe", async (event, apiName, params = {}) => {
  34. return new Promise((resolve, reject) => {
  35. let pythonExePath;
  36. if (process.env.NODE_ENV === "development") {
  37. pythonExePath = path.join(__dirname, "../serves/dist/api_test.exe");
  38. } else {
  39. pythonExePath = path.join(
  40. process.resourcesPath,
  41. "app.asar.unpacked",
  42. "serves",
  43. "dist",
  44. "api_test.exe"
  45. );
  46. }
  47. if (!fs.existsSync(pythonExePath)) {
  48. reject(`Python EXE 不存在: ${pythonExePath}`);
  49. return;
  50. }
  51. const args = [
  52. apiName,
  53. Buffer.from(JSON.stringify(params)).toString("base64"),
  54. ];
  55. const child = spawn(pythonExePath, args, { encoding: "utf8" });
  56. let stdoutData = "";
  57. let stderrData = "";
  58. child.stdout.on("data", (data) => (stdoutData += data.toString()));
  59. child.stderr.on("data", (data) => (stderrData += data.toString()));
  60. child.on("close", (code) => {
  61. if (code !== 0) {
  62. reject(`Python EXE exited with code ${code}\n${stderrData}`);
  63. return;
  64. }
  65. try {
  66. resolve(JSON.parse(stdoutData));
  67. } catch (err) {
  68. resolve(stdoutData.trim());
  69. }
  70. });
  71. });
  72. });
  73. // ✅ 读取 CSV
  74. ipcMain.handle("read-csv", async (event, filePath) => {
  75. try {
  76. const csvData = fs.readFileSync(filePath, "utf-8");
  77. const parsedData = Papa.parse(csvData, {
  78. header: true,
  79. skipEmptyLines: true,
  80. transformHeader: (header) => header.trim(),
  81. });
  82. const cleanData = parsedData.data.filter((row) =>
  83. Object.values(row).some((val) => val && val.trim() !== "")
  84. );
  85. return cleanData;
  86. } catch (error) {
  87. console.error("读取 CSV 失败:", error);
  88. return { error: error.message };
  89. }
  90. });
  91. // ✅ 文件选择
  92. ipcMain.handle("get-file-path", async () => {
  93. try {
  94. const { filePaths } = await dialog.showOpenDialog({
  95. properties: ["openFile", "multiSelections"],
  96. });
  97. return filePaths.length > 0 ? filePaths[0] : null;
  98. } catch (error) {
  99. console.error("获取文件路径失败:", error);
  100. return null;
  101. }
  102. });
  103. // ✅ 保存数据到文件夹?
  104. ipcMain.handle("save-submission", async (event, args) => {
  105. try {
  106. const baseDir = path.join(app.getPath("documents"), "雷击引下线数据");
  107. const { folderName, files } = args;
  108. const targetDir = path.join(baseDir, folderName);
  109. fs.mkdirSync(targetDir, { recursive: true });
  110. const savedPaths = [];
  111. for (const f of files) {
  112. const filePath = path.join(targetDir, f.fileName);
  113. const data =
  114. typeof f.content === "string" ? f.content : JSON.stringify(f.content);
  115. fs.writeFileSync(filePath, data, "utf8");
  116. savedPaths.push({ fileName: f.fileName, fullPath: filePath });
  117. }
  118. return { ok: true, dir: targetDir, savedPaths };
  119. } catch (err) {
  120. console.error("save-submission failed:", err);
  121. return { ok: false, error: String(err) };
  122. }
  123. });
  124. /** ✅ 删除单个文件夹 */
  125. ipcMain.handle("delete-folder", async (event, folderPath) => {
  126. try {
  127. if (!folderPath || !fs.existsSync(folderPath)) {
  128. return { ok: false, error: "文件夹不存在" };
  129. }
  130. fs.rmSync(folderPath, { recursive: true, force: true });
  131. console.log("已删除文件夹:", folderPath);
  132. return { ok: true };
  133. } catch (err) {
  134. console.error("删除文件夹失败:", err);
  135. return { ok: false, error: String(err) };
  136. }
  137. });
  138. /** ✅ 清空所有数据文件夹 */
  139. ipcMain.handle("clear-all-folders", async () => {
  140. try {
  141. const baseDir = path.join(app.getPath("documents"), "雷击引下线数据");
  142. if (fs.existsSync(baseDir)) {
  143. fs.rmSync(baseDir, { recursive: true, force: true });
  144. console.log("已清空目录:", baseDir);
  145. }
  146. return { ok: true };
  147. } catch (err) {
  148. console.error("清空文件夹失败:", err);
  149. return { ok: false, error: String(err) };
  150. }
  151. });
  152. // ✅ 读取文本文件
  153. ipcMain.handle("read-text-file", async (event, filePath) => {
  154. try {
  155. const content = fs.readFileSync(filePath, "utf8");
  156. return { ok: true, content };
  157. } catch (err) {
  158. return { ok: false, error: String(err) };
  159. }
  160. });
  161. // ✅ 读取 JSON 文件
  162. ipcMain.handle("read-json-file", async (event, filePath) => {
  163. try {
  164. const text = fs.readFileSync(filePath, "utf8");
  165. const json = JSON.parse(text);
  166. return { ok: true, json };
  167. } catch (err) {
  168. return { ok: false, error: String(err) };
  169. }
  170. });
  171. // ✅ 打开文件夹(只打开到指定目录)
  172. ipcMain.handle("open-path", async (event, folderPath) => {
  173. try {
  174. const stats = fs.statSync(folderPath);
  175. const target = stats.isDirectory()
  176. ? folderPath
  177. : path.dirname(folderPath);
  178. await shell.openPath(target);
  179. return { ok: true, opened: target };
  180. } catch (err) {
  181. console.error("打开路径失败:", err);
  182. return { ok: false, error: String(err) };
  183. }
  184. });
  185. // ✅ 关闭确认
  186. mainWindow.on("close", (event) => {
  187. event.preventDefault();
  188. const choice = dialog.showMessageBoxSync(mainWindow, {
  189. type: "question",
  190. buttons: ["取消", "退出"],
  191. defaultId: 0,
  192. title: "确认退出",
  193. message: "退出程序正在分析中的数据会丢失,确定要退出程序吗?",
  194. });
  195. if (choice === 1) {
  196. mainWindow.removeAllListeners("close");
  197. mainWindow.close();
  198. }
  199. });
  200. mainWindow.on("closed", () => {
  201. mainWindow = null;
  202. });
  203. }
  204. app.whenReady().then(createWindow);
  205. app.on("window-all-closed", () => {
  206. if (process.platform !== "darwin") app.quit();
  207. });
  208. app.on("activate", () => {
  209. if (BrowserWindow.getAllWindows().length === 0) createWindow();
  210. });
  211. export { createWindow };