fix: 上传完成内存泄漏
This commit is contained in:
parent
e5bf5685cf
commit
2cbab89273
|
@ -8,12 +8,11 @@ export const registerDicomHandler = () => {
|
|||
const dia = await dialog.showOpenDialog({ properties: ["openDirectory"] });
|
||||
if (dia.canceled) return null;
|
||||
const dcmPaths = await filterDicoms(dia.filePaths[0]);
|
||||
await uploadFilesInBatches({
|
||||
uploadFilesInBatches({
|
||||
filePaths: dcmPaths,
|
||||
batchSize: 6,
|
||||
feedback: (d) => event.reply("dicom:upload:detail", d),
|
||||
});
|
||||
event.reply("dicom:upload:finished"); // 重新刷新病人列表
|
||||
});
|
||||
|
||||
ipcMain.handle("dicom:select", async () => {
|
||||
|
|
|
@ -108,11 +108,4 @@ export const uploadFilesInBatches = async ({
|
|||
totalEndTime - totalStartTime
|
||||
} ms`
|
||||
);
|
||||
|
||||
return {
|
||||
totalSuccess,
|
||||
totalFailed,
|
||||
totalTime: totalEndTime - totalStartTime,
|
||||
progress: 100,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -75,7 +75,8 @@
|
|||
"react-router-dom": "^6.26.0",
|
||||
"tailwind-merge": "^2.4.0",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"zod": "3.23.8"
|
||||
"zod": "3.23.8",
|
||||
"mitt": "3.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@radix-ui/react-icons": "^1.3.0",
|
||||
|
|
|
@ -28,19 +28,23 @@ import {
|
|||
} from "@/components/ui/dialog";
|
||||
import { Progress } from "@/components/ui/progress";
|
||||
import { RocketIcon } from "@radix-ui/react-icons";
|
||||
import emitter from "@/lib/events";
|
||||
|
||||
interface ScanProgress {
|
||||
percentage: number;
|
||||
interface UploadProgress {
|
||||
progress: number;
|
||||
totalSuccess: number;
|
||||
totalFailed: number;
|
||||
}
|
||||
|
||||
const defaultProgress = {
|
||||
percentage: 0,
|
||||
};
|
||||
|
||||
export const MenuBar = () => {
|
||||
const { toast } = useToast();
|
||||
const navigate = useNavigate();
|
||||
const [progress, setProgress] = useState<ScanProgress>(defaultProgress);
|
||||
const [uploading, setUploading] = useState<UploadProgress>({
|
||||
progress: 0,
|
||||
totalSuccess: 0,
|
||||
totalFailed: 0,
|
||||
});
|
||||
|
||||
const [inferOption, setInferOption] =
|
||||
useState<inferDeviceType[]>(inferDevices);
|
||||
const [importDialogVisible, setImportDialogVisible] = useState(false);
|
||||
|
@ -48,9 +52,9 @@ export const MenuBar = () => {
|
|||
useEffect(() => {
|
||||
const handleUploadFeedback = (
|
||||
_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);
|
||||
return () => {
|
||||
|
@ -59,12 +63,20 @@ export const MenuBar = () => {
|
|||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const visible = ![0, 100].includes(progress.percentage);
|
||||
setImportDialogVisible(visible);
|
||||
}, [progress.percentage]);
|
||||
const p = uploading?.progress;
|
||||
if (p > 0 && p < 100 && !importDialogVisible) {
|
||||
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 = () => {
|
||||
navigate("datasource");
|
||||
window.ipcRenderer.send("dicom:upload");
|
||||
};
|
||||
|
||||
|
@ -192,9 +204,9 @@ export const MenuBar = () => {
|
|||
<div className="flex items-center space-x-2">
|
||||
<Alert>
|
||||
<RocketIcon className="h-4 w-4" />
|
||||
<AlertTitle>扫描进度</AlertTitle>
|
||||
<AlertTitle>导入进度</AlertTitle>
|
||||
<AlertDescription>
|
||||
<Progress value={progress?.percentage} />
|
||||
<Progress value={uploading.progress} />
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
</div>
|
||||
|
|
5
apps/desktop/src/lib/events.ts
Normal file
5
apps/desktop/src/lib/events.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import mitt from "mitt";
|
||||
|
||||
const emitter = mitt();
|
||||
|
||||
export default emitter;
|
|
@ -13,6 +13,7 @@ import { PatientInfo, SeriesInfo } from "./type";
|
|||
import { PatientList } from "./PatientList";
|
||||
import { StudyList } from "./StudyList";
|
||||
import { SeriesList } from "./SeriesList";
|
||||
import emitter from "@/lib/events";
|
||||
|
||||
export const Datasource = () => {
|
||||
const rawPatientsRef = useRef<PatientInfo[]>([]);
|
||||
|
@ -23,24 +24,20 @@ export const Datasource = () => {
|
|||
const [selectedStudyId, setSelectedStudyId] = useState<string | null>(null);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const fetchPatients = () => {
|
||||
window.ipcRenderer
|
||||
.invoke("dicom:select")
|
||||
.then((patients: PatientInfo[]) => {
|
||||
console.log(patients);
|
||||
rawPatientsRef.current = patients;
|
||||
setPatients(patients);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchPatients();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
window.ipcRenderer.on("dicom:upload:finished", fetchPatients);
|
||||
const fetchData = () => {
|
||||
window.ipcRenderer
|
||||
.invoke("dicom:select")
|
||||
.then((patients: PatientInfo[]) => {
|
||||
console.log("patients", patients);
|
||||
rawPatientsRef.current = patients;
|
||||
setPatients(patients);
|
||||
});
|
||||
};
|
||||
fetchData();
|
||||
emitter.on("datasource:fetch", fetchData);
|
||||
return () => {
|
||||
window.ipcRenderer.off("dicom:upload:finished", fetchPatients);
|
||||
emitter.off("datasource:fetch", fetchData);
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
|
|
@ -5,9 +5,14 @@ import {
|
|||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip";
|
||||
import { ResetIcon } from "@radix-ui/react-icons";
|
||||
import { MoonIcon, PersonStanding } from "lucide-react";
|
||||
|
||||
export const ToolBarMenu = () => {
|
||||
export interface ToolBarMenuProps {
|
||||
onToolButtonClick?: (key: string) => void;
|
||||
}
|
||||
|
||||
export const ToolBarMenu = (props: ToolBarMenuProps) => {
|
||||
return (
|
||||
<div className="flex gap-x-2">
|
||||
<TooltipProvider>
|
||||
|
@ -34,6 +39,22 @@ export const ToolBarMenu = () => {
|
|||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</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>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -18,6 +18,7 @@ import {
|
|||
|
||||
import {
|
||||
IStackViewport,
|
||||
IVolumeViewport,
|
||||
PublicViewportInput,
|
||||
} from "@cornerstonejs/core/dist/types/types";
|
||||
import {
|
||||
|
@ -253,10 +254,19 @@ export const Viewer = () => {
|
|||
}
|
||||
}, [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 (
|
||||
<div className="w-full h-full flex flex-col">
|
||||
<div className="flex-shrink-0 border-b border-secondary">
|
||||
<ToolBarMenu />
|
||||
<ToolBarMenu onToolButtonClick={onToolMenuClick} />
|
||||
</div>
|
||||
<div className="flex-grow">
|
||||
<ResizablePanelGroup direction="horizontal" className="w-full h-full">
|
||||
|
|
|
@ -151,6 +151,9 @@ importers:
|
|||
lucide-react:
|
||||
specifier: ^0.408.0
|
||||
version: 0.408.0(react@18.3.1)
|
||||
mitt:
|
||||
specifier: 3.0.1
|
||||
version: 3.0.1
|
||||
node-machine-id:
|
||||
specifier: 1.1.12
|
||||
version: 1.1.12
|
||||
|
@ -4034,6 +4037,9 @@ packages:
|
|||
resolution: {integrity: sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg==}
|
||||
engines: {node: '>= 18'}
|
||||
|
||||
mitt@3.0.1:
|
||||
resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
|
||||
|
||||
mkdirp-classic@0.5.3:
|
||||
resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
|
||||
|
||||
|
@ -9845,6 +9851,8 @@ snapshots:
|
|||
minipass: 7.1.2
|
||||
rimraf: 5.0.10
|
||||
|
||||
mitt@3.0.1: {}
|
||||
|
||||
mkdirp-classic@0.5.3: {}
|
||||
|
||||
mkdirp@1.0.4: {}
|
||||
|
|
Loading…
Reference in New Issue
Block a user