diff --git a/electron/core/dicom.ts b/electron/core/dicom.ts index c9fb8ac..d74f1a2 100644 --- a/electron/core/dicom.ts +++ b/electron/core/dicom.ts @@ -2,6 +2,12 @@ import path from "path"; import * as dicomParser from "dicom-parser"; import fs from "fs"; +export interface StructuredData { + [StudyInstanceUID: string]: { + [SeriesInstanceUID: string]: ExtractMetadata[]; + }; +} + export interface ExtractMetadata { filePath: string; StudyInstanceUID?: string; @@ -57,7 +63,7 @@ export const parseDICOMFile = async ( filePath, StudyInstanceUID, SeriesInstanceUID, - pixelData, + // pixelData, }; } catch (error) { console.error(`Error parsing file ${filePath}:`, error); @@ -91,3 +97,27 @@ export const processFilesInBatches = async ( } return results; }; + +export const structureMetadata = (data: ExtractMetadata[]): StructuredData => { + const structured: StructuredData = {}; + + data.forEach((item) => { + // 确保每个元素都有有效的 StudyInstanceUID 和 SeriesInstanceUID + if (item.StudyInstanceUID && item.SeriesInstanceUID) { + // 如果还没有为这个 StudyInstanceUID 创建记录,则初始化一个空对象 + if (!structured[item.StudyInstanceUID]) { + structured[item.StudyInstanceUID] = {}; + } + + // 如果这个 StudyInstanceUID 下还没有这个 SeriesInstanceUID 的记录,则初始化一个空数组 + if (!structured[item.StudyInstanceUID][item.SeriesInstanceUID]) { + structured[item.StudyInstanceUID][item.SeriesInstanceUID] = []; + } + + // 将当前元素添加到对应的数组中 + structured[item.StudyInstanceUID][item.SeriesInstanceUID].push(item); + } + }); + + return structured; +}; diff --git a/electron/ipcEvent/index.ts b/electron/ipcEvent/index.ts new file mode 100644 index 0000000..88782cf --- /dev/null +++ b/electron/ipcEvent/index.ts @@ -0,0 +1 @@ +export const EVENT_PARSE_DICOM = "PARSE_DICOM"; diff --git a/electron/ipcMainHandlers.ts b/electron/ipcMainHandlers.ts index 2bd59b5..22602fc 100644 --- a/electron/ipcMainHandlers.ts +++ b/electron/ipcMainHandlers.ts @@ -1,20 +1,26 @@ import path from "path"; -import { ipcMain } from "electron"; +import { dialog, ipcMain } from "electron"; import os from "os"; -import { findDcmFiles, processFilesInBatches } from "./core/dicom"; +import { findDcmFiles, processFilesInBatches, structureMetadata } from "./core/dicom"; +import { EVENT_PARSE_DICOM } from "./ipcEvent"; /** * 渲染进程和主进程的事件调度 */ -const registerIpcMainHandlers = () => { - ipcMain.on("parseDicom", async (event, file: string) => { - const rootFolder = path.dirname(file); - const filePaths = await findDcmFiles(rootFolder); - const batchSize = os.cpus().length * 1 || 10; - console.time("分批处理"); - const result = await processFilesInBatches(filePaths, batchSize); - console.log(result); - console.timeEnd("分批处理"); +const registerIpcMainHandlers = (mainWindow) => { + ipcMain.on(EVENT_PARSE_DICOM, async (event, file: string) => { + const dirDialog = await dialog.showOpenDialog(mainWindow, { + properties: ["openDirectory"], + }); + if (dirDialog.filePaths.length > 0) { + const filePaths = await findDcmFiles(dirDialog.filePaths[0]); + const batchSize = os.cpus().length * 1 || 10; + console.time("分批处理"); + const unraw = await processFilesInBatches(filePaths, batchSize); + console.timeEnd("分批处理"); + const result = structureMetadata(unraw); + event.reply(EVENT_PARSE_DICOM + ":RES", result); + } }); }; diff --git a/electron/main.ts b/electron/main.ts index 209a421..0576fca 100644 --- a/electron/main.ts +++ b/electron/main.ts @@ -38,7 +38,7 @@ function createWindow() { titleBarStyle: "hidden", // customButtonsOnHover || hidden || hiddenInset titleBarOverlay: { height: 36, color: "#f8f8f8" }, // 渲染进程发消息动态改变这个 webPreferences: { - preload: path.join(__dirname, "preload.mjs") + preload: path.join(__dirname, "preload.mjs"), }, }); @@ -117,7 +117,7 @@ app.on("activate", () => { app.whenReady().then(() => { createWindow(); - registerIpcMainHandlers(); + registerIpcMainHandlers(win); // 设置 Dock 图标 if (process.platform === "darwin") { @@ -130,4 +130,4 @@ app.whenReady().then(() => { // 注销全局快捷键,当应用退出时 app.on("will-quit", () => { globalShortcut.unregisterAll(); -}); +}); \ No newline at end of file diff --git a/src/pages/Aorta/index.tsx b/src/pages/Aorta/index.tsx index b89d9f4..ef40888 100644 --- a/src/pages/Aorta/index.tsx +++ b/src/pages/Aorta/index.tsx @@ -1,44 +1,32 @@ import { Button } from "@/components/ui/button"; -import { useEffect, useRef } from "react"; +import { EVENT_PARSE_DICOM } from "../../../electron/ipcEvent"; +import { useEffect, useState } from "react"; const Aorta = () => { - const fileInputRef = useRef(null); - - const handleFileChange = async (e: React.ChangeEvent) => { - e.persist(); - if (!fileInputRef.current?.files) return; - // 第一个文件给主进程 - window.ipcRenderer.send("parseDicom", e.target.files?.[0].path ?? ""); - }; + const [uploadDicomInfo, setUploadDicomInfo] = useState([]); useEffect(() => { - window.ipcRenderer.on("parseDicomResponse", (event, data) => { - if (data.error) { - console.error("Error parsing DICOM folder:", data.error); - } else { - console.log("DICOM Data Sets:", data); - } + window.ipcRenderer.on(EVENT_PARSE_DICOM + ":RES", (event, data) => { + console.log(data); + if (data.error) return; + setUploadDicomInfo(data); }); return () => { - window.ipcRenderer.off("parseDicomResponse", () => {}); + window.ipcRenderer.off(EVENT_PARSE_DICOM + ":RES", () => {}); }; }, []); + const handleOpenDialog = () => { + window.ipcRenderer.send(EVENT_PARSE_DICOM); + }; + return (
- - handleFileChange(e)} - /> +
+
{JSON.stringify(uploadDicomInfo)}
); }; diff --git a/src/pages/Layout.tsx b/src/pages/Layout.tsx index a85f17c..be3396a 100644 --- a/src/pages/Layout.tsx +++ b/src/pages/Layout.tsx @@ -1,19 +1,18 @@ import { Outlet, Link } from "react-router-dom"; import { GoFileDirectory } from "react-icons/go"; -import { MenuBar } from './MenuBar' +import { MenuBar } from "./MenuBar"; const LayoutMain = () => { const platform = document.querySelector("html")?.getAttribute("platform") ?? "macos"; - const titleBarStyles = platform === "macos" ? "pl-[5rem] pr-[.5rem]" : "pr-[10rem]"; return (
diff --git a/src/pages/MenuBar.tsx b/src/pages/MenuBar.tsx index b022fa5..2f3add2 100644 --- a/src/pages/MenuBar.tsx +++ b/src/pages/MenuBar.tsx @@ -1,108 +1,109 @@ import { - Menubar, - MenubarCheckboxItem, - MenubarContent, - MenubarItem, - MenubarMenu, - MenubarRadioGroup, - MenubarRadioItem, - MenubarSeparator, - MenubarShortcut, - MenubarSub, - MenubarSubContent, - MenubarSubTrigger, - MenubarTrigger, -} from "@/components/ui/menubar" + Menubar, + MenubarCheckboxItem, + MenubarContent, + MenubarItem, + MenubarMenu, + MenubarRadioGroup, + MenubarRadioItem, + MenubarSeparator, + MenubarShortcut, + MenubarSub, + MenubarSubContent, + MenubarSubTrigger, + MenubarTrigger, +} from "@/components/ui/menubar"; export const MenuBar = () => { - return ( - - - File - - - New Tab ⌘T - - - New Window ⌘N - - New Incognito Window - - - Share - - Email link - Messages - Notes - - - - - Print... ⌘P - - - - - Edit - - - Undo ⌘Z - - - Redo ⇧⌘Z - - - - Find - - Search the web - - Find... - Find Next - Find Previous - - - - Cut - Copy - Paste - - - - View - - Always Show Bookmarks Bar - - Always Show Full URLs - - - - Reload ⌘R - - - Force Reload ⇧⌘R - - - Toggle Fullscreen - - Hide Sidebar - - - - Profiles - - - Andy - Benoit - Luis - - - Edit... - - Add Profile... - - - - ) - -} \ No newline at end of file + return ( + + + File + + + New Tab ⌘T + + + New Window ⌘N + + New Incognito Window + + + Share + + Email link + Messages + Notes + + + + + Print... ⌘P + + + + + Edit + + + Undo ⌘Z + + + Redo ⇧⌘Z + + + + Find + + Search the web + + Find... + Find Next + Find Previous + + + + Cut + Copy + Paste + + + + View + + Always Show Bookmarks Bar + + Always Show Full URLs + + + + Reload ⌘R + + + Force Reload ⇧⌘R + + + Toggle Fullscreen + + Hide Sidebar + + + + Profiles + + + Andy + Benoit + Luis + + + Edit... + + Add Profile... + + + + ); +};