fix: 上传完成内存泄漏

This commit is contained in:
mozzie 2024-09-20 12:30:56 +08:00
parent e5bf5685cf
commit 2cbab89273
9 changed files with 89 additions and 43 deletions

View File

@ -8,12 +8,11 @@ export const registerDicomHandler = () => {
const dia = await dialog.showOpenDialog({ properties: ["openDirectory"] }); const dia = await dialog.showOpenDialog({ properties: ["openDirectory"] });
if (dia.canceled) return null; if (dia.canceled) return null;
const dcmPaths = await filterDicoms(dia.filePaths[0]); const dcmPaths = await filterDicoms(dia.filePaths[0]);
await uploadFilesInBatches({ uploadFilesInBatches({
filePaths: dcmPaths, filePaths: dcmPaths,
batchSize: 6, batchSize: 6,
feedback: (d) => event.reply("dicom:upload:detail", d), feedback: (d) => event.reply("dicom:upload:detail", d),
}); });
event.reply("dicom:upload:finished"); // 重新刷新病人列表
}); });
ipcMain.handle("dicom:select", async () => { ipcMain.handle("dicom:select", async () => {

View File

@ -108,11 +108,4 @@ export const uploadFilesInBatches = async ({
totalEndTime - totalStartTime totalEndTime - totalStartTime
} ms` } ms`
); );
return {
totalSuccess,
totalFailed,
totalTime: totalEndTime - totalStartTime,
progress: 100,
};
}; };

View File

@ -75,7 +75,8 @@
"react-router-dom": "^6.26.0", "react-router-dom": "^6.26.0",
"tailwind-merge": "^2.4.0", "tailwind-merge": "^2.4.0",
"tailwindcss-animate": "^1.0.7", "tailwindcss-animate": "^1.0.7",
"zod": "3.23.8" "zod": "3.23.8",
"mitt": "3.0.1"
}, },
"devDependencies": { "devDependencies": {
"@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-icons": "^1.3.0",

View File

@ -28,19 +28,23 @@ import {
} from "@/components/ui/dialog"; } from "@/components/ui/dialog";
import { Progress } from "@/components/ui/progress"; import { Progress } from "@/components/ui/progress";
import { RocketIcon } from "@radix-ui/react-icons"; import { RocketIcon } from "@radix-ui/react-icons";
import emitter from "@/lib/events";
interface ScanProgress { interface UploadProgress {
percentage: number; progress: number;
totalSuccess: number;
totalFailed: number;
} }
const defaultProgress = {
percentage: 0,
};
export const MenuBar = () => { export const MenuBar = () => {
const { toast } = useToast(); const { toast } = useToast();
const navigate = useNavigate(); const navigate = useNavigate();
const [progress, setProgress] = useState<ScanProgress>(defaultProgress); const [uploading, setUploading] = useState<UploadProgress>({
progress: 0,
totalSuccess: 0,
totalFailed: 0,
});
const [inferOption, setInferOption] = const [inferOption, setInferOption] =
useState<inferDeviceType[]>(inferDevices); useState<inferDeviceType[]>(inferDevices);
const [importDialogVisible, setImportDialogVisible] = useState(false); const [importDialogVisible, setImportDialogVisible] = useState(false);
@ -48,9 +52,9 @@ export const MenuBar = () => {
useEffect(() => { useEffect(() => {
const handleUploadFeedback = ( const handleUploadFeedback = (
_event: Electron.IpcRendererEvent, _event: Electron.IpcRendererEvent,
data: { progress: number; totalSuccess: number; totalFailed: number } data: UploadProgress
) => { ) => {
setProgress({ percentage: data.progress }); setUploading(data);
}; };
window.ipcRenderer.on("dicom:upload:detail", handleUploadFeedback); window.ipcRenderer.on("dicom:upload:detail", handleUploadFeedback);
return () => { return () => {
@ -59,12 +63,20 @@ export const MenuBar = () => {
}, []); }, []);
useEffect(() => { useEffect(() => {
const visible = ![0, 100].includes(progress.percentage); const p = uploading?.progress;
setImportDialogVisible(visible); if (p > 0 && p < 100 && !importDialogVisible) {
}, [progress.percentage]); setImportDialogVisible(true);
} else if (p === 100 && importDialogVisible) {
setImportDialogVisible(false);
toast({
title: "操作",
description: `导入完成,成功: ${uploading.totalSuccess},失败: ${uploading.totalFailed}`,
});
emitter.emit("datasource:fetch");
}
}, [uploading, importDialogVisible, navigate, toast]);
const handleImportDicom = () => { const handleImportDicom = () => {
navigate("datasource");
window.ipcRenderer.send("dicom:upload"); window.ipcRenderer.send("dicom:upload");
}; };
@ -192,9 +204,9 @@ export const MenuBar = () => {
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<Alert> <Alert>
<RocketIcon className="h-4 w-4" /> <RocketIcon className="h-4 w-4" />
<AlertTitle></AlertTitle> <AlertTitle></AlertTitle>
<AlertDescription> <AlertDescription>
<Progress value={progress?.percentage} /> <Progress value={uploading.progress} />
</AlertDescription> </AlertDescription>
</Alert> </Alert>
</div> </div>

View File

@ -0,0 +1,5 @@
import mitt from "mitt";
const emitter = mitt();
export default emitter;

View File

@ -13,6 +13,7 @@ import { PatientInfo, SeriesInfo } from "./type";
import { PatientList } from "./PatientList"; import { PatientList } from "./PatientList";
import { StudyList } from "./StudyList"; import { StudyList } from "./StudyList";
import { SeriesList } from "./SeriesList"; import { SeriesList } from "./SeriesList";
import emitter from "@/lib/events";
export const Datasource = () => { export const Datasource = () => {
const rawPatientsRef = useRef<PatientInfo[]>([]); const rawPatientsRef = useRef<PatientInfo[]>([]);
@ -23,24 +24,20 @@ export const Datasource = () => {
const [selectedStudyId, setSelectedStudyId] = useState<string | null>(null); const [selectedStudyId, setSelectedStudyId] = useState<string | null>(null);
const navigate = useNavigate(); const navigate = useNavigate();
const fetchPatients = () => { useEffect(() => {
const fetchData = () => {
window.ipcRenderer window.ipcRenderer
.invoke("dicom:select") .invoke("dicom:select")
.then((patients: PatientInfo[]) => { .then((patients: PatientInfo[]) => {
console.log(patients); console.log("patients", patients);
rawPatientsRef.current = patients; rawPatientsRef.current = patients;
setPatients(patients); setPatients(patients);
}); });
}; };
fetchData();
useEffect(() => { emitter.on("datasource:fetch", fetchData);
fetchPatients();
}, []);
useEffect(() => {
window.ipcRenderer.on("dicom:upload:finished", fetchPatients);
return () => { return () => {
window.ipcRenderer.off("dicom:upload:finished", fetchPatients); emitter.off("datasource:fetch", fetchData);
}; };
}, []); }, []);

View File

@ -5,9 +5,14 @@ import {
TooltipProvider, TooltipProvider,
TooltipTrigger, TooltipTrigger,
} from "@/components/ui/tooltip"; } from "@/components/ui/tooltip";
import { ResetIcon } from "@radix-ui/react-icons";
import { MoonIcon, PersonStanding } from "lucide-react"; import { MoonIcon, PersonStanding } from "lucide-react";
export const ToolBarMenu = () => { export interface ToolBarMenuProps {
onToolButtonClick?: (key: string) => void;
}
export const ToolBarMenu = (props: ToolBarMenuProps) => {
return ( return (
<div className="flex gap-x-2"> <div className="flex gap-x-2">
<TooltipProvider> <TooltipProvider>
@ -34,6 +39,22 @@ export const ToolBarMenu = () => {
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</TooltipProvider> </TooltipProvider>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
variant="ghost"
size="icon"
onClick={() => props.onToolButtonClick?.("reset")}
>
<ResetIcon className="h-4 w-4" />
</Button>
</TooltipTrigger>
<TooltipContent side="bottom">
<p></p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div> </div>
); );
}; };

View File

@ -18,6 +18,7 @@ import {
import { import {
IStackViewport, IStackViewport,
IVolumeViewport,
PublicViewportInput, PublicViewportInput,
} from "@cornerstonejs/core/dist/types/types"; } from "@cornerstonejs/core/dist/types/types";
import { import {
@ -253,10 +254,19 @@ export const Viewer = () => {
} }
}, [index]); }, [index]);
const onToolMenuClick = (key: string) => {
const engine = renderingEngineRef.current;
if (key === "reset" && engine) {
const viewport = engine.getViewport(viewportIds[0]) as IVolumeViewport;
viewport.resetCamera(true, true, true, true, false);
viewport.render();
}
};
return ( return (
<div className="w-full h-full flex flex-col"> <div className="w-full h-full flex flex-col">
<div className="flex-shrink-0 border-b border-secondary"> <div className="flex-shrink-0 border-b border-secondary">
<ToolBarMenu /> <ToolBarMenu onToolButtonClick={onToolMenuClick} />
</div> </div>
<div className="flex-grow"> <div className="flex-grow">
<ResizablePanelGroup direction="horizontal" className="w-full h-full"> <ResizablePanelGroup direction="horizontal" className="w-full h-full">

View File

@ -151,6 +151,9 @@ importers:
lucide-react: lucide-react:
specifier: ^0.408.0 specifier: ^0.408.0
version: 0.408.0(react@18.3.1) version: 0.408.0(react@18.3.1)
mitt:
specifier: 3.0.1
version: 3.0.1
node-machine-id: node-machine-id:
specifier: 1.1.12 specifier: 1.1.12
version: 1.1.12 version: 1.1.12
@ -4034,6 +4037,9 @@ packages:
resolution: {integrity: sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg==} resolution: {integrity: sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg==}
engines: {node: '>= 18'} engines: {node: '>= 18'}
mitt@3.0.1:
resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
mkdirp-classic@0.5.3: mkdirp-classic@0.5.3:
resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
@ -9845,6 +9851,8 @@ snapshots:
minipass: 7.1.2 minipass: 7.1.2
rimraf: 5.0.10 rimraf: 5.0.10
mitt@3.0.1: {}
mkdirp-classic@0.5.3: {} mkdirp-classic@0.5.3: {}
mkdirp@1.0.4: {} mkdirp@1.0.4: {}