diff --git a/apps/desktop/electron/ipcEvent/dicom/handler.ts b/apps/desktop/electron/ipcEvent/dicom/handler.ts index 84decd1..36529f0 100644 --- a/apps/desktop/electron/ipcEvent/dicom/handler.ts +++ b/apps/desktop/electron/ipcEvent/dicom/handler.ts @@ -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 () => { diff --git a/apps/desktop/electron/ipcEvent/dicom/util.ts b/apps/desktop/electron/ipcEvent/dicom/util.ts index bcf4fea..7073582 100644 --- a/apps/desktop/electron/ipcEvent/dicom/util.ts +++ b/apps/desktop/electron/ipcEvent/dicom/util.ts @@ -108,11 +108,4 @@ export const uploadFilesInBatches = async ({ totalEndTime - totalStartTime } ms` ); - - return { - totalSuccess, - totalFailed, - totalTime: totalEndTime - totalStartTime, - progress: 100, - }; }; diff --git a/apps/desktop/package.json b/apps/desktop/package.json index c0e1a88..306130a 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -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", diff --git a/apps/desktop/src/components/base/MenuBar/index.tsx b/apps/desktop/src/components/base/MenuBar/index.tsx index 397aeeb..3290338 100644 --- a/apps/desktop/src/components/base/MenuBar/index.tsx +++ b/apps/desktop/src/components/base/MenuBar/index.tsx @@ -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(defaultProgress); + const [uploading, setUploading] = useState({ + progress: 0, + totalSuccess: 0, + totalFailed: 0, + }); + const [inferOption, setInferOption] = useState(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 = () => {
- 扫描进度 + 导入进度 - +
diff --git a/apps/desktop/src/lib/events.ts b/apps/desktop/src/lib/events.ts new file mode 100644 index 0000000..cdadf92 --- /dev/null +++ b/apps/desktop/src/lib/events.ts @@ -0,0 +1,5 @@ +import mitt from "mitt"; + +const emitter = mitt(); + +export default emitter; diff --git a/apps/desktop/src/pages/Datasource/index.tsx b/apps/desktop/src/pages/Datasource/index.tsx index 62aba4a..c06a7b0 100644 --- a/apps/desktop/src/pages/Datasource/index.tsx +++ b/apps/desktop/src/pages/Datasource/index.tsx @@ -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([]); @@ -23,24 +24,20 @@ export const Datasource = () => { const [selectedStudyId, setSelectedStudyId] = useState(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); }; }, []); diff --git a/apps/desktop/src/pages/Viewer/ToolBarMenu/index.tsx b/apps/desktop/src/pages/Viewer/ToolBarMenu/index.tsx index a9faf9f..33317ef 100644 --- a/apps/desktop/src/pages/Viewer/ToolBarMenu/index.tsx +++ b/apps/desktop/src/pages/Viewer/ToolBarMenu/index.tsx @@ -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 (
@@ -34,6 +39,22 @@ export const ToolBarMenu = () => { + + + + + + +

复原

+
+
+
); }; diff --git a/apps/desktop/src/pages/Viewer/index.tsx b/apps/desktop/src/pages/Viewer/index.tsx index 36d86e8..e6c1ac3 100644 --- a/apps/desktop/src/pages/Viewer/index.tsx +++ b/apps/desktop/src/pages/Viewer/index.tsx @@ -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 (
- +
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b008218..4e23457 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -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: {}