feat: 前端上传全部交后端

This commit is contained in:
mozzie 2024-08-07 10:28:05 +08:00
parent cc48e5742a
commit d69de380f7
7 changed files with 174 additions and 149 deletions

View File

@ -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;
};

View File

@ -0,0 +1 @@
export const EVENT_PARSE_DICOM = "PARSE_DICOM";

View File

@ -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 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 result = await processFilesInBatches(filePaths, batchSize);
console.log(result);
const unraw = await processFilesInBatches(filePaths, batchSize);
console.timeEnd("分批处理");
const result = structureMetadata(unraw);
event.reply(EVENT_PARSE_DICOM + ":RES", result);
}
});
};

View File

@ -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") {

View File

@ -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<HTMLInputElement | null>(null);
const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
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 (
<div className="p-2">
<div className="grid w-full max-w-sm items-center gap-1.5">
<Button onClick={() => fileInputRef.current?.click()}>dicom</Button>
<input
type="file"
webkitdirectory="true"
ref={fileInputRef}
mozdirectory="true"
multiple
style={{ display: "none" }}
onChange={(e) => handleFileChange(e)}
/>
<Button onClick={handleOpenDialog}>dicom</Button>
</div>
<div>{JSON.stringify(uploadDicomInfo)}</div>
</div>
);
};

View File

@ -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 (
<div className="h-full">
<div className={`title-bar drag h-[36px] flex`}>
<div
className={`flex-1 flex no-drag items-center justify-between ${titleBarStyles}`}
className={`inline-flex no-drag items-center justify-between ${titleBarStyles}`}
>
<MenuBar />
</div>

View File

@ -12,11 +12,13 @@ import {
MenubarSubContent,
MenubarSubTrigger,
MenubarTrigger,
} from "@/components/ui/menubar"
} from "@/components/ui/menubar";
export const MenuBar = () => {
return (
<Menubar>
<Menubar
style={{ background: "transparent", border: 0, boxShadow: "none" }}
>
<MenubarMenu>
<MenubarTrigger>File</MenubarTrigger>
<MenubarContent>
@ -103,6 +105,5 @@ export const MenuBar = () => {
</MenubarContent>
</MenubarMenu>
</Menubar>
)
}
);
};