195 lines
6.1 KiB
TypeScript
195 lines
6.1 KiB
TypeScript
import { Button, Col, Row, Space, message } from "antd";
|
|
import { useDicomUploader } from "./DicomUploader";
|
|
import { Series, Study } from "./DicomUploader/util";
|
|
import { DicomTable } from "./DicomTable";
|
|
import { useDomain } from "@/hook/useDomain";
|
|
import { useState } from "react";
|
|
import { User } from "@@/domain/User/entities/User";
|
|
import { limitConcurrency } from "./limitConcurrency";
|
|
import { CloudUploadOutlined, InboxOutlined } from "@ant-design/icons";
|
|
import { openOHIFViewer, flatternStudies, FlatStudyItem } from "./util";
|
|
import { DicomFileInfo } from "./DicomFileInfo";
|
|
import { UploadProgressModal } from "./UploadProgressModal";
|
|
import AssignModal from "./AssignModal";
|
|
|
|
interface DicomUploadProps {
|
|
children?: JSX.Element;
|
|
}
|
|
|
|
export const DicomUpload = (props: DicomUploadProps) => {
|
|
const [messageApi, contextHolder] = message.useMessage();
|
|
const { UploadInput, fileCalculator, studys, isLoading } = useDicomUploader();
|
|
const { dicomDomainService, userDomainService } = useDomain();
|
|
const { dcmFileNum, totalFileNum, dcmFileSize } = fileCalculator;
|
|
const [selectRows, setSelectedRows] = useState<Study[]>([]);
|
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
const [isAssignLoading, setIsAssignLoading] = useState(false);
|
|
const [annotators, setAnnotators] = useState<User[]>([]);
|
|
const [selectAnnotator, setSelectAnnotator] = useState<User | undefined>(
|
|
undefined
|
|
);
|
|
const [visible, setVisible] = useState<boolean>(false);
|
|
const [percent, setPercent] = useState<number>(0);
|
|
|
|
/**
|
|
* 单个序列阅片
|
|
*/
|
|
const onUploadFiles = async (study: Study, series: Series) => {
|
|
const { SeriesInstanceUID, subs } = series;
|
|
const { StudyInstanceUID } = study;
|
|
const instances = await dicomDomainService.existInPacs({
|
|
SeriesInstanceUID,
|
|
StudyInstanceUID,
|
|
});
|
|
// pacs已存在
|
|
if (instances.length === subs.length) {
|
|
openOHIFViewer(StudyInstanceUID, SeriesInstanceUID);
|
|
} else {
|
|
setVisible(true);
|
|
limitConcurrency(
|
|
series.subs.map((f: File) => () => dicomDomainService.upload2Pacs(f)),
|
|
10,
|
|
(completed, total) => {
|
|
console.log(`${completed} out of ${total} tasks completed.`);
|
|
setPercent(Math.floor((completed / total) * 100));
|
|
},
|
|
() => {
|
|
setVisible(false);
|
|
setPercent(0);
|
|
openOHIFViewer(StudyInstanceUID, SeriesInstanceUID);
|
|
}
|
|
);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 返回不在pacs中的影像数据
|
|
*/
|
|
const filterDicomPromise = () => {
|
|
const items = flatternStudies(studys);
|
|
// 过滤存在pacs中的序列
|
|
const checkExistPromises = items.map(
|
|
(item) =>
|
|
new Promise(async (resolve) => {
|
|
const instances = await dicomDomainService.existInPacs({
|
|
SeriesInstanceUID: item.SeriesInstanceUID,
|
|
StudyInstanceUID: item.StudyInstanceUID,
|
|
});
|
|
const existInPacs = instances.length === item.Files.length;
|
|
resolve(existInPacs ? false : item);
|
|
})
|
|
) as Promise<FlatStudyItem | false>[];
|
|
return Promise.all(checkExistPromises);
|
|
};
|
|
|
|
/**
|
|
* 上传全部
|
|
*/
|
|
const onUploadEntiredDicom = () => {
|
|
// 上传到pacs
|
|
filterDicomPromise().then((result) => {
|
|
const files = result.map((i) => (!i ? [] : i.Files)).flat();
|
|
if (files.length === 0) return messageApi.info("全部影像均已存在pacs中");
|
|
setVisible(true);
|
|
limitConcurrency(
|
|
files.map((f: File) => () => dicomDomainService.upload2Pacs(f)),
|
|
10,
|
|
(completed, total) => {
|
|
console.log(`${completed} out of ${total} tasks completed.`);
|
|
setPercent(Math.floor((completed / total) * 100));
|
|
},
|
|
() => {
|
|
setVisible(false);
|
|
setPercent(0);
|
|
}
|
|
);
|
|
});
|
|
};
|
|
|
|
const onClickAssign = () => {
|
|
// 检查是否全部传到pacs
|
|
setIsAssignLoading(true);
|
|
filterDicomPromise().then((result) => {
|
|
const files = result.map((i) => (!i ? [] : i.Files)).flat();
|
|
if (files.length !== 0) {
|
|
setIsAssignLoading(false);
|
|
return messageApi.info("请先上传全部影像到pacs");
|
|
}
|
|
userDomainService.getDmpAnnotators().then((res) => {
|
|
setIsAssignLoading(false);
|
|
setIsModalOpen(true);
|
|
setAnnotators(res.data as User[]);
|
|
});
|
|
});
|
|
};
|
|
|
|
/**
|
|
* 分配任务
|
|
*/
|
|
const onAssignConfirm = () => {
|
|
if (!selectAnnotator?.id) return;
|
|
userDomainService.createArchiveTask(selectAnnotator, selectRows);
|
|
setSelectAnnotator(undefined);
|
|
setIsModalOpen(false);
|
|
};
|
|
|
|
return (
|
|
<div style={{ padding: 20 }}>
|
|
<Row style={{ paddingBottom: 20 }}>
|
|
<Col span={24}>
|
|
<Space>
|
|
<UploadInput />
|
|
<Button
|
|
icon={<CloudUploadOutlined />}
|
|
disabled={Number(dcmFileNum) === 0}
|
|
type="primary"
|
|
onClick={onUploadEntiredDicom}
|
|
>
|
|
上传全部
|
|
</Button>
|
|
<Button
|
|
icon={<InboxOutlined />}
|
|
disabled={selectRows.length === 0}
|
|
type="primary"
|
|
loading={isAssignLoading}
|
|
onClick={onClickAssign}
|
|
>
|
|
分配
|
|
</Button>
|
|
</Space>
|
|
</Col>
|
|
</Row>
|
|
<DicomFileInfo
|
|
dcmFileNum={dcmFileNum}
|
|
dcmFileSize={dcmFileSize}
|
|
totalFileNum={totalFileNum}
|
|
/>
|
|
<DicomTable
|
|
studys={studys}
|
|
loading={isLoading}
|
|
onSelectedRows={(rows) => setSelectedRows(rows)}
|
|
onUploadFiles={onUploadFiles}
|
|
/>
|
|
<AssignModal
|
|
isOpen={isModalOpen}
|
|
options={annotators.map((a) => ({
|
|
value: a.id,
|
|
label: a.username,
|
|
}))}
|
|
value={selectAnnotator?.id as number}
|
|
onSelectAnnotator={(id: number) =>
|
|
setSelectAnnotator(annotators.find((a) => a.id === id))
|
|
}
|
|
onConfirm={onAssignConfirm}
|
|
onCancel={() => setIsModalOpen(false)}
|
|
/>
|
|
<UploadProgressModal
|
|
title="上传影像中"
|
|
isOpen={visible}
|
|
percent={percent}
|
|
/>
|
|
{contextHolder}
|
|
</div>
|
|
);
|
|
};
|