feat: 前端上传,后端分析demo
This commit is contained in:
parent
a6acf68ec4
commit
f2304a8e71
35
electron/ipcMainHandlers.ts
Normal file
35
electron/ipcMainHandlers.ts
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import fs from "fs";
|
||||||
|
import path from "path";
|
||||||
|
import * as dicomParser from "dicom-parser"; /* */
|
||||||
|
import { ipcMain } from "electron";
|
||||||
|
|
||||||
|
// 定义一个异步函数来递归地查找.dcm文件
|
||||||
|
const findDcmFiles = async (
|
||||||
|
dir: string,
|
||||||
|
fileList: string[] = []
|
||||||
|
): Promise<string[]> => {
|
||||||
|
const files = await fs.promises.readdir(dir, { withFileTypes: true });
|
||||||
|
await Promise.all(
|
||||||
|
files.map(async (file) => {
|
||||||
|
const filePath = path.join(dir, file.name);
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
await findDcmFiles(filePath, fileList); // 递归调用以遍历子目录
|
||||||
|
} else if (file.name.endsWith(".dcm")) {
|
||||||
|
fileList.push(filePath); // 如果文件是.dcm文件,添加到列表中
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return fileList;
|
||||||
|
};
|
||||||
|
|
||||||
|
const registerIpcMainHandlers = () => {
|
||||||
|
ipcMain.on("parseDicom", async (event, file: string) => {
|
||||||
|
const rootFolder = path.dirname(file);
|
||||||
|
const fileList = await findDcmFiles(rootFolder);
|
||||||
|
console.log("rootFolder", rootFolder);
|
||||||
|
console.log("fileList", fileList);
|
||||||
|
// event.sender.send("parseDicomResponse", files);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export default registerIpcMainHandlers;
|
|
@ -9,6 +9,9 @@ import {
|
||||||
import { createRequire } from "node:module";
|
import { createRequire } from "node:module";
|
||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
|
import { readdirSync } from "node:fs";
|
||||||
|
import dicomParser from "dicom-parser";
|
||||||
|
import registerIpcMainHandlers from "./ipcMainHandlers";
|
||||||
|
|
||||||
const require = createRequire(import.meta.url);
|
const require = createRequire(import.meta.url);
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
@ -33,7 +36,7 @@ function createWindow() {
|
||||||
icon: path.join(process.env.VITE_PUBLIC, "tray-icon.svg"),
|
icon: path.join(process.env.VITE_PUBLIC, "tray-icon.svg"),
|
||||||
// frame: false,
|
// frame: false,
|
||||||
titleBarStyle: "hidden", // customButtonsOnHover || hidden || hiddenInset
|
titleBarStyle: "hidden", // customButtonsOnHover || hidden || hiddenInset
|
||||||
titleBarOverlay: { height: 36, color: '#f8f8f8' }, // 渲染进程发消息动态改变这个
|
titleBarOverlay: { height: 36, color: "#f8f8f8" }, // 渲染进程发消息动态改变这个
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
preload: path.join(__dirname, "preload.mjs"),
|
preload: path.join(__dirname, "preload.mjs"),
|
||||||
},
|
},
|
||||||
|
@ -42,7 +45,7 @@ function createWindow() {
|
||||||
// Test active push message to Renderer-process.
|
// Test active push message to Renderer-process.
|
||||||
win.webContents.on("did-finish-load", () => {
|
win.webContents.on("did-finish-load", () => {
|
||||||
win?.webContents.send("main-process-message", {
|
win?.webContents.send("main-process-message", {
|
||||||
platform: process.platform === 'darwin' ? 'macos' : 'windows'
|
platform: process.platform === "darwin" ? "macos" : "windows",
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -114,6 +117,7 @@ app.on("activate", () => {
|
||||||
|
|
||||||
app.whenReady().then(() => {
|
app.whenReady().then(() => {
|
||||||
createWindow();
|
createWindow();
|
||||||
|
registerIpcMainHandlers();
|
||||||
|
|
||||||
// 设置 Dock 图标
|
// 设置 Dock 图标
|
||||||
if (process.platform === "darwin") {
|
if (process.platform === "darwin") {
|
||||||
|
|
|
@ -11,20 +11,25 @@
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@ant-design/icons": "^5.4.0",
|
||||||
"@radix-ui/react-dialog": "^1.1.1",
|
"@radix-ui/react-dialog": "^1.1.1",
|
||||||
|
"@radix-ui/react-label": "^2.1.0",
|
||||||
"@radix-ui/react-menubar": "^1.1.1",
|
"@radix-ui/react-menubar": "^1.1.1",
|
||||||
"@radix-ui/react-slot": "^1.1.0",
|
"@radix-ui/react-slot": "^1.1.0",
|
||||||
"@radix-ui/react-toast": "^1.2.1",
|
"@radix-ui/react-toast": "^1.2.1",
|
||||||
"@radix-ui/react-tooltip": "^1.1.2",
|
"@radix-ui/react-tooltip": "^1.1.2",
|
||||||
"@types/react-icons": "^3.0.0",
|
"@types/react-icons": "^3.0.0",
|
||||||
|
"antd": "^5.20.0",
|
||||||
"class-variance-authority": "^0.7.0",
|
"class-variance-authority": "^0.7.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"cmdk": "^1.0.0",
|
"cmdk": "^1.0.0",
|
||||||
"custom-electron-titlebar": "^4.2.8",
|
"custom-electron-titlebar": "^4.2.8",
|
||||||
"date-fns": "^3.6.0",
|
"date-fns": "^3.6.0",
|
||||||
|
"dicom-parser": "1.8.21",
|
||||||
"dockview": "^1.15.2",
|
"dockview": "^1.15.2",
|
||||||
"flexlayout-react": "^0.7.15",
|
"flexlayout-react": "^0.7.15",
|
||||||
"lucide-react": "^0.408.0",
|
"lucide-react": "^0.408.0",
|
||||||
|
"object-hash": "^3.0.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-day-picker": "^8.10.1",
|
"react-day-picker": "^8.10.1",
|
||||||
"react-desktop": "^0.3.9",
|
"react-desktop": "^0.3.9",
|
||||||
|
|
949
pnpm-lock.yaml
949
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
25
src/components/ui/input.tsx
Normal file
25
src/components/ui/input.tsx
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
import * as React from "react"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
export interface InputProps
|
||||||
|
extends React.InputHTMLAttributes<HTMLInputElement> {}
|
||||||
|
|
||||||
|
const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
||||||
|
({ className, type, ...props }, ref) => {
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
type={type}
|
||||||
|
className={cn(
|
||||||
|
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
ref={ref}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
Input.displayName = "Input"
|
||||||
|
|
||||||
|
export { Input }
|
26
src/components/ui/label.tsx
Normal file
26
src/components/ui/label.tsx
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
"use client"
|
||||||
|
|
||||||
|
import * as React from "react"
|
||||||
|
import * as LabelPrimitive from "@radix-ui/react-label"
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const labelVariants = cva(
|
||||||
|
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
|
||||||
|
)
|
||||||
|
|
||||||
|
const Label = React.forwardRef<
|
||||||
|
React.ElementRef<typeof LabelPrimitive.Root>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
|
||||||
|
VariantProps<typeof labelVariants>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<LabelPrimitive.Root
|
||||||
|
ref={ref}
|
||||||
|
className={cn(labelVariants(), className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
Label.displayName = LabelPrimitive.Root.displayName
|
||||||
|
|
||||||
|
export { Label }
|
|
@ -1,7 +1,46 @@
|
||||||
import React from "react";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
const Aorta = () => {
|
const Aorta = () => {
|
||||||
return <div>aorta</div>;
|
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 ?? "");
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.ipcRenderer.off("parseDicomResponse", () => {});
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
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)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Aorta;
|
export default Aorta;
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
import Dockview from "./ Dock";
|
|
||||||
import { Outlet, Link } from "react-router-dom";
|
import { Outlet, Link } from "react-router-dom";
|
||||||
import { GoFileDirectory } from "react-icons/go";
|
import { GoFileDirectory } from "react-icons/go";
|
||||||
import Aorta from "@/pages/Aorta";
|
|
||||||
|
|
||||||
const LayoutMain = () => {
|
const LayoutMain = () => {
|
||||||
const platform =
|
const platform =
|
||||||
document.querySelector("html")?.getAttribute("platform") ?? "macos";
|
document.querySelector("html")?.getAttribute("platform") ?? "macos";
|
||||||
|
@ -16,7 +13,7 @@ const LayoutMain = () => {
|
||||||
<div className="relative h-[calc(100%-36px)]">
|
<div className="relative h-[calc(100%-36px)]">
|
||||||
<div className="flex h-full">
|
<div className="flex h-full">
|
||||||
<div className="workspace w-[36px] h-full border-r">
|
<div className="workspace w-[36px] h-full border-r">
|
||||||
<ul className="flex flex-col items-center pt-2">
|
<ul className="flex flex-col items-center pt-2 gap-2">
|
||||||
<li className="w-[22px] h-[22px] flex flex-col items-center justify-center">
|
<li className="w-[22px] h-[22px] flex flex-col items-center justify-center">
|
||||||
<Link to="/">1</Link>
|
<Link to="/">1</Link>
|
||||||
<GoFileDirectory />
|
<GoFileDirectory />
|
||||||
|
@ -28,7 +25,6 @@ const LayoutMain = () => {
|
||||||
</div>
|
</div>
|
||||||
<div className="relative w-[calc(100%-36px)] h-full">
|
<div className="relative w-[calc(100%-36px)] h-full">
|
||||||
<Outlet />
|
<Outlet />
|
||||||
{/* <Dockview /> */}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user