上传、下载影像
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
parent
307403d897
commit
d7a21af10c
|
@ -15,3 +15,19 @@ export function getSubjectImageUploadList(params) {
|
||||||
params
|
params
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
// 预上传
|
||||||
|
export function preArchiveDicomStudy(data) {
|
||||||
|
return request({
|
||||||
|
url: '/DownloadAndUpload/preArchiveDicomStudy',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// 归档
|
||||||
|
export function addOrUpdateArchiveTaskStudy(data) {
|
||||||
|
return request({
|
||||||
|
url: '/DownloadAndUpload/addOrUpdateArchiveTaskStudy',
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
|
@ -1,6 +1,16 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="base-model-wrapper">
|
<div class="base-model-wrapper">
|
||||||
<el-dialog v-if="config.visible" v-dialogDrag :title="config.title" :append-to-body="config.appendToBody" :visible.sync="config.visible" :close-on-click-modal="false" :show-close="config.showClose" :width="config.width" :fullscreen="config.fullscreen">
|
<el-dialog
|
||||||
|
v-if="config.visible"
|
||||||
|
:v-dialogDrag="!config.dialogDrag"
|
||||||
|
:title="config.title"
|
||||||
|
:append-to-body="config.appendToBody"
|
||||||
|
:visible.sync="config.visible"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
:show-close="config.showClose"
|
||||||
|
:width="config.width"
|
||||||
|
:fullscreen="config.fullscreen"
|
||||||
|
>
|
||||||
<div class="base-modal-body">
|
<div class="base-modal-body">
|
||||||
<slot name="dialog-body" />
|
<slot name="dialog-body" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -12,46 +22,52 @@
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'BaseDialog',
|
name: "BaseDialog",
|
||||||
props: {
|
props: {
|
||||||
config: {
|
config: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => {
|
default: () => {
|
||||||
return {
|
return {
|
||||||
visible: false,
|
visible: false,
|
||||||
title: '',
|
title: "",
|
||||||
closeOnClickModal: false,
|
closeOnClickModal: false,
|
||||||
showClose: true,
|
showClose: true,
|
||||||
appendToBody: false,
|
appendToBody: false,
|
||||||
width: '100px',
|
width: "100px",
|
||||||
fullscreen: false
|
fullscreen: false,
|
||||||
}
|
dialogDrag: false,
|
||||||
}
|
};
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.base-model-wrapper{
|
.base-model-wrapper {
|
||||||
.el-dialog__header{
|
.el-dialog__header {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
.el-dialog__headerbtn{
|
.el-dialog__headerbtn {
|
||||||
top: 10px;
|
top: 10px;
|
||||||
right: 10px;
|
right: 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.el-dialog__body{
|
.el-dialog__body {
|
||||||
padding:10px 10px 10px 10px;
|
padding: 10px 10px 10px 10px;
|
||||||
.base-modal-body{
|
.base-modal-body {
|
||||||
min-height: 100px;
|
min-height: 100px;
|
||||||
max-height:650px;
|
max-height: 650px;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border: 1px solid #e0e0e0;
|
border: 1px solid #e0e0e0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.el-dialog__footer{
|
.el-dialog__footer {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
.notFooter {
|
||||||
|
.is-fullscreen .el-dialog__footer {
|
||||||
|
display: none;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,7 +1,75 @@
|
||||||
<template>
|
<template>
|
||||||
<base-model :config="model_cfg">
|
<el-dialog
|
||||||
<div slot="dialog-body">
|
:visible.sync="visible"
|
||||||
<div class="top" style="margin-bottom: 10px">
|
:fullscreen="true"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
custom-class="upload-dialog"
|
||||||
|
:before-close="beforeClose"
|
||||||
|
>
|
||||||
|
<span slot="title">{{ $t("trials:uploadImage:title:uploadImages") }}</span>
|
||||||
|
<el-table :data="list" style="width: 100%" height="300" :loading="loading">
|
||||||
|
<!--受试者-->
|
||||||
|
<el-table-column
|
||||||
|
prop="SubjectCode"
|
||||||
|
:label="$t('trials:uploadImage:table:subjectCode')"
|
||||||
|
/>
|
||||||
|
<!--任务名称-->
|
||||||
|
<el-table-column
|
||||||
|
prop="TaskBlindName"
|
||||||
|
:label="$t('trials:uploadImage:table:taskBlindName')"
|
||||||
|
/>
|
||||||
|
<!--原始检查数-->
|
||||||
|
<el-table-column
|
||||||
|
prop="OrginalStudyList"
|
||||||
|
:label="$t('trials:uploadImage:table:orginalStudyListNum')"
|
||||||
|
>
|
||||||
|
<template slot-scope="scope">{{
|
||||||
|
scope.row.OrginalStudyList &&
|
||||||
|
Array.isArray(scope.row.OrginalStudyList)
|
||||||
|
? scope.row.OrginalStudyList.length
|
||||||
|
: 0
|
||||||
|
}}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<!--后处理检查数-->
|
||||||
|
<el-table-column
|
||||||
|
prop="UploadStudyList"
|
||||||
|
:label="$t('trials:uploadImage:table:uploadStudyListNum')"
|
||||||
|
>
|
||||||
|
<template slot-scope="scope">{{
|
||||||
|
scope.row.UploadStudyList && Array.isArray(scope.row.UploadStudyList)
|
||||||
|
? scope.row.UploadStudyList.length
|
||||||
|
: 0
|
||||||
|
}}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
:label="$t('common:action:action')"
|
||||||
|
fixed="right"
|
||||||
|
width="140"
|
||||||
|
>
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<!--下载--->
|
||||||
|
<el-button
|
||||||
|
circle
|
||||||
|
icon="el-icon-download"
|
||||||
|
:title="$t('trials:uploadImage:button:download')"
|
||||||
|
@click.stop="downloadImage(scope.row)"
|
||||||
|
/>
|
||||||
|
<!--删除--->
|
||||||
|
<el-button
|
||||||
|
circle
|
||||||
|
icon="el-icon-delete"
|
||||||
|
:title="$t('trials:uploadImage:button:delete')"
|
||||||
|
@click.stop="remove(scope.row)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<p class="line">{{ $t("trials:uploadImage:button:uploadTableTitle") }}</p>
|
||||||
|
<div
|
||||||
|
class="top"
|
||||||
|
style="margin-bottom: 10px"
|
||||||
|
v-if="Criterion.ImageUploadEnum > 0"
|
||||||
|
>
|
||||||
<el-button type="primary" size="small" class="el-icon-upload2">
|
<el-button type="primary" size="small" class="el-icon-upload2">
|
||||||
<label for="file">
|
<label for="file">
|
||||||
{{ $t("trials:uploadImage:button:selectFolder") }}
|
{{ $t("trials:uploadImage:button:selectFolder") }}
|
||||||
|
@ -19,58 +87,49 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<el-table
|
<el-table
|
||||||
:data="list"
|
:data="uploadList"
|
||||||
border
|
|
||||||
style="width: 100%"
|
style="width: 100%"
|
||||||
height="300"
|
v-adaptive="{ bottomOffset: 40 }"
|
||||||
:loading="loading"
|
|
||||||
>
|
>
|
||||||
<!--受试者-->
|
<!--检查编号-->
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="subjectCode"
|
prop="StudyId"
|
||||||
:label="$t('trials:uploadImage:table:subjectCode')"
|
:label="$t('trials:uploadImage:table:StudyId')"
|
||||||
/>
|
/>
|
||||||
<!--任务名称-->
|
<!--患者姓名-->
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="taskBlindName"
|
prop="PatientName"
|
||||||
:label="$t('trials:uploadImage:table:taskBlindName')"
|
:label="$t('trials:uploadImage:table:PatientName')"
|
||||||
/>
|
/>
|
||||||
<!--原始检查数-->
|
|
||||||
<el-table-column
|
|
||||||
prop="orginalStudyList"
|
|
||||||
:label="$t('trials:uploadImage:table:orginalStudyListNum')"
|
|
||||||
>
|
|
||||||
<template slot-scope="scope">{{
|
|
||||||
scope.row.orginalStudyList.length
|
|
||||||
}}</template>
|
|
||||||
</el-table-column>
|
|
||||||
<!--后处理检查数-->
|
|
||||||
<el-table-column
|
|
||||||
prop="uploadStudyList"
|
|
||||||
:label="$t('trials:uploadImage:table:uploadStudyListNum')"
|
|
||||||
min-width="140"
|
|
||||||
>
|
|
||||||
<template slot-scope="scope">{{
|
|
||||||
scope.row.orginalStudyList.length
|
|
||||||
}}</template>
|
|
||||||
</el-table-column>
|
|
||||||
<!--上传进度-->
|
<!--上传进度-->
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="schedule"
|
prop="schedule"
|
||||||
:label="$t('trials:uploadImage:table:schedule')"
|
:label="$t('trials:uploadImage:table:schedule')"
|
||||||
|
min-width="150"
|
||||||
>
|
>
|
||||||
<template slot-scope="scope">{{
|
<template slot-scope="scope">
|
||||||
scope.row.orginalStudyList.length
|
<el-progress
|
||||||
}}</template>
|
color="#409eff"
|
||||||
|
:percentage="scope.row.schedule"
|
||||||
|
v-if="scope.row.schedule || scope.row.schedule === 0"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<!--上传状态-->
|
<!--上传状态-->
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="status"
|
prop="status"
|
||||||
:label="$t('trials:uploadImage:table:status')"
|
:label="$t('trials:uploadImage:table:status')"
|
||||||
|
min-width="120"
|
||||||
>
|
>
|
||||||
<template slot-scope="scope">{{
|
<template slot-scope="scope">
|
||||||
scope.row.orginalStudyList.length
|
<el-button
|
||||||
}}</template>
|
:type="['warning', 'success', 'danger'][scope.row.status]"
|
||||||
|
size="mini"
|
||||||
|
plain
|
||||||
|
v-if="scope.row.status || scope.row.status === 0"
|
||||||
|
>{{ $fd("UploadStatus", scope.row.status) }}</el-button
|
||||||
|
>
|
||||||
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<!--已有/成功/失败-->
|
<!--已有/成功/失败-->
|
||||||
<el-table-column
|
<el-table-column
|
||||||
|
@ -78,27 +137,33 @@
|
||||||
:label="$t('trials:uploadImage:table:scheduleNum')"
|
:label="$t('trials:uploadImage:table:scheduleNum')"
|
||||||
min-width="140"
|
min-width="140"
|
||||||
>
|
>
|
||||||
<template slot-scope="scope">{{
|
|
||||||
scope.row.orginalStudyList.length
|
|
||||||
}}</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column :label="$t('common:action:action')" fixed="right">
|
|
||||||
<template slot-scope="scope">
|
<template slot-scope="scope">
|
||||||
<el-button
|
<span v-if="scope.row.count">{{
|
||||||
circle
|
`${scope.row.count} /${scope.row.successCount} / ${scope.row.failCount}`
|
||||||
icon="el-icon-delete"
|
}}</span>
|
||||||
:title="$t('trials:uploadImage:button:delete')"
|
|
||||||
@click.stop="remove(scope.row, item)"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
:label="$t('common:action:action')"
|
||||||
|
fixed="right"
|
||||||
|
width="140"
|
||||||
|
>
|
||||||
|
<!-- <template slot-scope="scope">
|
||||||
|
</template> -->
|
||||||
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
</div>
|
</el-dialog>
|
||||||
</base-model>
|
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import baseModel from "@/components/BaseModel";
|
import baseModel from "@/components/BaseModel";
|
||||||
import { getSubjectImageUploadList } from "@/api/load.js";
|
import {
|
||||||
|
getSubjectImageUploadList,
|
||||||
|
preArchiveDicomStudy,
|
||||||
|
addOrUpdateArchiveTaskStudy,
|
||||||
|
} from "@/api/load.js";
|
||||||
|
import { downloadImage } from "@/utils/uploadZip.js";
|
||||||
|
import { parseDicom, getThumbnail } from "@/utils/parseDicom.js";
|
||||||
|
import { dcmUpload } from "@/utils/dcmUpload/dcmUpload";
|
||||||
export default {
|
export default {
|
||||||
name: "uploadImage",
|
name: "uploadImage",
|
||||||
props: {
|
props: {
|
||||||
|
@ -122,35 +187,22 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
model_cfg: {
|
|
||||||
visible: false,
|
|
||||||
showClose: true,
|
|
||||||
width: "1000px",
|
|
||||||
title: this.$t("trials:uploadImage:title:uploadImages"),
|
|
||||||
appendToBody: true,
|
|
||||||
},
|
|
||||||
list: [],
|
list: [],
|
||||||
|
StudyInstanceUidList: [],
|
||||||
|
SopInstanceUidList: [],
|
||||||
|
|
||||||
|
uploadList: [], // 待上传文件列表
|
||||||
|
dicomList: [], // 上传的文件列表
|
||||||
|
requestNum: 6,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
|
||||||
visible: {
|
|
||||||
handler() {
|
|
||||||
this.model_cfg.visible = this.visible;
|
|
||||||
},
|
|
||||||
immediate: true,
|
|
||||||
deep: true,
|
|
||||||
},
|
|
||||||
"model_cfg.visible": {
|
|
||||||
handler() {
|
|
||||||
this.$emit("update:visible", this.model_cfg.visible);
|
|
||||||
},
|
|
||||||
deep: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
created() {
|
created() {
|
||||||
this.getList();
|
this.getList();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
beforeClose() {
|
||||||
|
this.$emit("update:visible", false);
|
||||||
|
},
|
||||||
// 获取列表
|
// 获取列表
|
||||||
async getList() {
|
async getList() {
|
||||||
try {
|
try {
|
||||||
|
@ -161,7 +213,21 @@ export default {
|
||||||
let res = await getSubjectImageUploadList(params);
|
let res = await getSubjectImageUploadList(params);
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
if (res.IsSuccess) {
|
if (res.IsSuccess) {
|
||||||
|
this.StudyInstanceUidList = [];
|
||||||
|
this.SopInstanceUidList = [];
|
||||||
this.list = res.Result;
|
this.list = res.Result;
|
||||||
|
res.Result.forEach((item) => {
|
||||||
|
if (item.OrginalStudyList && Array.isArray(item.OrginalStudyList)) {
|
||||||
|
item.OrginalStudyList.forEach((data) => {
|
||||||
|
data.SubjectId = item.SubejctId;
|
||||||
|
data.VisitTaskId = item.VisitTaskId;
|
||||||
|
data.SourceSubjectVisitId = item.SourceSubjectVisitId;
|
||||||
|
this.StudyInstanceUidList.push(data);
|
||||||
|
data.SopInstanceUidList &&
|
||||||
|
this.SopInstanceUidList.push(...data.SopInstanceUidList);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err);
|
||||||
|
@ -170,6 +236,396 @@ export default {
|
||||||
},
|
},
|
||||||
// 删除
|
// 删除
|
||||||
remove(item) {},
|
remove(item) {},
|
||||||
|
// 打包下载
|
||||||
|
async downloadImage(item) {
|
||||||
|
try {
|
||||||
|
await downloadImage(
|
||||||
|
this.$route.query.trialId,
|
||||||
|
item.SourceSubjectVisitId
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 预上传
|
||||||
|
async preArchiveDicomStudy(post) {
|
||||||
|
try {
|
||||||
|
let res = await preArchiveDicomStudy(post);
|
||||||
|
if (res.IsSuccess) {
|
||||||
|
return res.Result;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 上传
|
||||||
|
async beginScanFiles(e) {
|
||||||
|
let files = e.target.files;
|
||||||
|
try {
|
||||||
|
this.dicomList = [];
|
||||||
|
for (var i = 0; i < files.length; ++i) {
|
||||||
|
let dicom = await parseDicom(files[i]);
|
||||||
|
let has = this.StudyInstanceUidList.some(
|
||||||
|
(item) => item.StudyInstanceUid === dicom.StudyInstanceUid
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
!dicom ||
|
||||||
|
!has ||
|
||||||
|
this.SopInstanceUidList.includes(dicom.SopInstanceUid)
|
||||||
|
)
|
||||||
|
continue;
|
||||||
|
dicom.file = files[i];
|
||||||
|
dicom.isUpload = 0;
|
||||||
|
this.StudyInstanceUidList.some((item) => {
|
||||||
|
if (item.StudyInstanceUid === dicom.StudyInstanceUid) {
|
||||||
|
dicom.params = {
|
||||||
|
TrialId: this.$route.query.trialId,
|
||||||
|
SubjectId: item.SubjectId,
|
||||||
|
VisitTaskId: item.VisitTaskId,
|
||||||
|
SourceSubjectVisitId: item.SourceSubjectVisitId,
|
||||||
|
};
|
||||||
|
let uploadHas = this.uploadList.some(
|
||||||
|
(uploadData, uploadIndex) => {
|
||||||
|
if (uploadData.StudyInstanceUid === dicom.StudyInstanceUid) {
|
||||||
|
if (uploadData.status > 0) {
|
||||||
|
uploadData.count = 1;
|
||||||
|
uploadData.successCount = 0;
|
||||||
|
uploadData.failCount = 0;
|
||||||
|
uploadData.schedule = 0;
|
||||||
|
uploadData.status = 0;
|
||||||
|
uploadData.FileSize = dicom.file.size;
|
||||||
|
uploadData.IsDicomReUpload = false;
|
||||||
|
} else {
|
||||||
|
uploadData.FileSize += dicom.file.size;
|
||||||
|
uploadData.count++;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
item.SopInstanceUidList &&
|
||||||
|
item.SopInstanceUidList.length > 0
|
||||||
|
) {
|
||||||
|
uploadData.IsDicomReUpload = true;
|
||||||
|
}
|
||||||
|
dicom.uploadIndex = uploadIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
return uploadData.StudyInstanceUid === dicom.StudyInstanceUid;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (!uploadHas) {
|
||||||
|
let uploadData = {
|
||||||
|
TrialId: this.$route.query.trialId,
|
||||||
|
SubjectId: item.SubjectId,
|
||||||
|
SourceSubjectVisitId: item.SourceSubjectVisitId,
|
||||||
|
StudyId: dicom.StudyId,
|
||||||
|
StudyInstanceUid: dicom.StudyInstanceUid,
|
||||||
|
FileSize: dicom.file.size,
|
||||||
|
count: 1,
|
||||||
|
successCount: 0,
|
||||||
|
failCount: 0,
|
||||||
|
schedule: 0,
|
||||||
|
status: 0,
|
||||||
|
IsDicomReUpload: false,
|
||||||
|
};
|
||||||
|
if (
|
||||||
|
item.SopInstanceUidList &&
|
||||||
|
item.SopInstanceUidList.length > 0
|
||||||
|
) {
|
||||||
|
uploadData.IsDicomReUpload = true;
|
||||||
|
}
|
||||||
|
this.uploadList.push(uploadData);
|
||||||
|
dicom.uploadIndex = this.uploadList.length - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return item.StudyInstanceUid === dicom.StudyInstanceUid;
|
||||||
|
});
|
||||||
|
this.dicomList.push(dicom);
|
||||||
|
}
|
||||||
|
for (let i = 0; i < this.uploadList.length; i++) {
|
||||||
|
let item = this.uploadList[i];
|
||||||
|
let data = {
|
||||||
|
TrialId: item.TrialId,
|
||||||
|
SubjectId: item.SubjectId,
|
||||||
|
SubjectVisitId: item.SourceSubjectVisitId,
|
||||||
|
FileSize: item.FileSize,
|
||||||
|
IsDicomReUpload: item.IsDicomReUpload,
|
||||||
|
FileCount: item.count,
|
||||||
|
};
|
||||||
|
let res = await this.preArchiveDicomStudy(data);
|
||||||
|
if (res) {
|
||||||
|
item.StudyMonitorId = res;
|
||||||
|
let arr = this.dicomList.filter((dicom) => dicom.uploadIndex === i);
|
||||||
|
let num =
|
||||||
|
arr.length > this.requestNum ? this.requestNum : arr.length;
|
||||||
|
let funArr = [];
|
||||||
|
for (let i = 0; i < num; i++) {
|
||||||
|
funArr.push(this.handleUploadTask(arr, i));
|
||||||
|
}
|
||||||
|
if (funArr.length > 0) {
|
||||||
|
let res = await Promise.all(funArr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 并发上传
|
||||||
|
async handleUploadTask(arr, index) {
|
||||||
|
arr[index].isUpload = 1;
|
||||||
|
let res = await this.dicomToOSS(
|
||||||
|
arr[index].file,
|
||||||
|
arr[index].params,
|
||||||
|
arr[index]
|
||||||
|
);
|
||||||
|
if (res) {
|
||||||
|
arr[index].path = res;
|
||||||
|
this.uploadList[arr[index].uploadIndex].successCount++;
|
||||||
|
arr[index].isUpload = 2;
|
||||||
|
} else {
|
||||||
|
this.uploadList[arr[index].uploadIndex].status = 2;
|
||||||
|
this.uploadList[arr[index].uploadIndex].failCount++;
|
||||||
|
arr[index].isUpload = 3;
|
||||||
|
}
|
||||||
|
this.uploadList[arr[index].uploadIndex].schedule = Math.floor(
|
||||||
|
((this.uploadList[arr[index].uploadIndex].successCount +
|
||||||
|
this.uploadList[arr[index].uploadIndex].failCount) /
|
||||||
|
this.uploadList[arr[index].uploadIndex].count) *
|
||||||
|
100
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
this.uploadList[arr[index].uploadIndex].schedule >= 100 &&
|
||||||
|
this.uploadList[arr[index].uploadIndex].successCount ===
|
||||||
|
this.uploadList[arr[index].uploadIndex].count
|
||||||
|
) {
|
||||||
|
this.uploadList[arr[index].uploadIndex].status = 1;
|
||||||
|
let res = await this.formatDicom(arr, arr[index].uploadIndex);
|
||||||
|
if (res) {
|
||||||
|
this.uploadList[arr[index].uploadIndex].Study =
|
||||||
|
res[arr[index].StudyId];
|
||||||
|
this.uploadList[arr[index].uploadIndex].status = 3;
|
||||||
|
res = await addOrUpdateArchiveTaskStudy(
|
||||||
|
this.uploadList[arr[index].uploadIndex]
|
||||||
|
);
|
||||||
|
if (res.IsSuccess) {
|
||||||
|
this.getList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let ind = arr.findIndex((item) => !item.isUpload);
|
||||||
|
if (ind >= 0) {
|
||||||
|
return this.handleUploadTask(arr, ind);
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 数据整理
|
||||||
|
async formatDicom(arr, index) {
|
||||||
|
let studyObj = {};
|
||||||
|
for (let i = 0; i < arr.length; i++) {
|
||||||
|
let dicom = arr[i];
|
||||||
|
if (dicom.uploadIndex !== index) continue;
|
||||||
|
if (!studyObj[dicom.StudyId]) {
|
||||||
|
studyObj[dicom.StudyId] = {
|
||||||
|
StudyId: dicom.StudyId,
|
||||||
|
StudyInstanceUid: dicom.StudyInstanceUid,
|
||||||
|
StudyTime: this.getTime(dicom.StudyDate, dicom.StudyTime),
|
||||||
|
Modalities: dicom.Modalities,
|
||||||
|
Description: dicom.StudyDescription,
|
||||||
|
SeriesCount: 1,
|
||||||
|
InstanceCount: 1,
|
||||||
|
InstitutionName: dicom.InstitutionName,
|
||||||
|
PatientId: dicom.PatientId,
|
||||||
|
PatientName: dicom.PatientName,
|
||||||
|
PatientAge: dicom.PatientAge,
|
||||||
|
PatientSex: dicom.PatientSex,
|
||||||
|
AccessionNumber: dicom.AccessionNumber,
|
||||||
|
PatientBirthDate: dicom.PatientBirthDate,
|
||||||
|
AcquisitionTime: dicom.AcquisitionTime,
|
||||||
|
AcquisitionNumber: dicom.AcquisitionNumber,
|
||||||
|
TriggerTime: dicom.TriggerTime,
|
||||||
|
BodyPartExamined: dicom.BodyPartExamined,
|
||||||
|
SeriesList: [
|
||||||
|
{
|
||||||
|
StudyInstanceUid: dicom.StudyInstanceUid,
|
||||||
|
SeriesInstanceUid: dicom.SeriesInstanceUid,
|
||||||
|
SeriesNumber: dicom.SeriesNumber,
|
||||||
|
SeriesTime: this.getTime(dicom.SeriesDate, dicom.SeriesTime),
|
||||||
|
Modality: dicom.Modality,
|
||||||
|
Description: dicom.SeriesDescription,
|
||||||
|
InstanceCount: 1,
|
||||||
|
SliceThickness: dicom.SliceThickness,
|
||||||
|
ImagePositionPatient: dicom.ImagePosition,
|
||||||
|
ImageOrientationPatient: dicom.ImageOrientation,
|
||||||
|
BodyPartExamined: dicom.seriesBodyPartExamined,
|
||||||
|
SequenceName: dicom.SequenceName,
|
||||||
|
ProtocolName: dicom.ProtocolName,
|
||||||
|
ImagerPixelSpacing: dicom.ImagePixelSpacing,
|
||||||
|
AcquisitionTime: dicom.AcquisitionTime,
|
||||||
|
AcquisitionNumber: dicom.AcquisitionNumber,
|
||||||
|
TriggerTime: dicom.TriggerTime,
|
||||||
|
ImageResizePath: await getThumbnail(
|
||||||
|
dicom.file,
|
||||||
|
dicom.params,
|
||||||
|
dicom
|
||||||
|
),
|
||||||
|
InstanceList: [
|
||||||
|
{
|
||||||
|
StudyInstanceUid: dicom.StudyInstanceUid,
|
||||||
|
SeriesInstanceUid: dicom.SeriesInstanceUid,
|
||||||
|
SopInstanceUid: dicom.SopInstanceUid,
|
||||||
|
InstanceNumber: dicom.ImageOrInstanceNumber,
|
||||||
|
InstanceTime: this.getTime(
|
||||||
|
dicom.ContentDate,
|
||||||
|
dicom.ContentTime
|
||||||
|
),
|
||||||
|
ImageRows: dicom.Rows || 0,
|
||||||
|
ImageColumns: dicom.Columns || 0,
|
||||||
|
SliceLocation: dicom.SliceLocation,
|
||||||
|
SliceThickness: dicom.SliceThickness,
|
||||||
|
NumberOfFrames: dicom.NumberOfFrames || 0,
|
||||||
|
PixelSpacing: dicom.PixelSpacing,
|
||||||
|
ImagerPixelSpacing: dicom.ImagePixelSpacing,
|
||||||
|
FrameOfReferenceUID: dicom.FrameOfReferenceUID,
|
||||||
|
WindowCenter: dicom.WindowCenter,
|
||||||
|
WindowWidth: dicom.WindowWidth,
|
||||||
|
Path: dicom.path,
|
||||||
|
HtmlPath: "",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
let seriesHas = studyObj[dicom.StudyId].SeriesList.some((item) => {
|
||||||
|
if (item.SeriesInstanceUid === dicom.SeriesInstanceUid) {
|
||||||
|
item.InstanceCount++;
|
||||||
|
let instance = {
|
||||||
|
StudyInstanceUid: dicom.StudyInstanceUid,
|
||||||
|
SeriesInstanceUid: dicom.SeriesInstanceUid,
|
||||||
|
SopInstanceUid: dicom.SopInstanceUid,
|
||||||
|
InstanceNumber: dicom.ImageOrInstanceNumber,
|
||||||
|
InstanceTime: this.getTime(
|
||||||
|
dicom.ContentDate,
|
||||||
|
dicom.ContentTime
|
||||||
|
),
|
||||||
|
ImageRows: dicom.Rows || 0,
|
||||||
|
ImageColumns: dicom.Columns || 0,
|
||||||
|
SliceLocation: dicom.SliceLocation,
|
||||||
|
SliceThickness: dicom.SliceThickness,
|
||||||
|
NumberOfFrames: dicom.NumberOfFrames || 0,
|
||||||
|
PixelSpacing: dicom.PixelSpacing,
|
||||||
|
ImagerPixelSpacing: dicom.ImagePixelSpacing,
|
||||||
|
FrameOfReferenceUID: dicom.FrameOfReferenceUID,
|
||||||
|
WindowCenter: dicom.WindowCenter,
|
||||||
|
WindowWidth: dicom.WindowWidth,
|
||||||
|
Path: dicom.path,
|
||||||
|
HtmlPath: "",
|
||||||
|
};
|
||||||
|
item.InstanceList.push(instance);
|
||||||
|
}
|
||||||
|
return item.SeriesInstanceUid === dicom.SeriesInstanceUid;
|
||||||
|
});
|
||||||
|
if (!seriesHas) {
|
||||||
|
studyObj[dicom.StudyId].SeriesCount++;
|
||||||
|
let series = {
|
||||||
|
StudyInstanceUid: dicom.StudyInstanceUid,
|
||||||
|
SeriesInstanceUid: dicom.SeriesInstanceUid,
|
||||||
|
SeriesNumber: dicom.SeriesNumber,
|
||||||
|
SeriesTime: this.getTime(dicom.SeriesDate, dicom.SeriesTime),
|
||||||
|
Modality: dicom.Modality,
|
||||||
|
Description: dicom.SeriesDescription,
|
||||||
|
InstanceCount: 1,
|
||||||
|
SliceThickness: dicom.SliceThickness,
|
||||||
|
ImagePositionPatient: dicom.ImagePosition,
|
||||||
|
ImageOrientationPatient: dicom.ImageOrientation,
|
||||||
|
BodyPartExamined: dicom.seriesBodyPartExamined,
|
||||||
|
SequenceName: dicom.SequenceName,
|
||||||
|
ProtocolName: dicom.ProtocolName,
|
||||||
|
ImagerPixelSpacing: dicom.ImagePixelSpacing,
|
||||||
|
AcquisitionTime: dicom.AcquisitionTime,
|
||||||
|
AcquisitionNumber: dicom.AcquisitionNumber,
|
||||||
|
TriggerTime: dicom.TriggerTime,
|
||||||
|
ImageResizePath: await getThumbnail(
|
||||||
|
dicom.file,
|
||||||
|
dicom.params,
|
||||||
|
dicom
|
||||||
|
),
|
||||||
|
InstanceList: [
|
||||||
|
{
|
||||||
|
StudyInstanceUid: dicom.StudyInstanceUid,
|
||||||
|
SeriesInstanceUid: dicom.SeriesInstanceUid,
|
||||||
|
SopInstanceUid: dicom.SopInstanceUid,
|
||||||
|
InstanceNumber: dicom.ImageOrInstanceNumber,
|
||||||
|
InstanceTime: this.getTime(
|
||||||
|
dicom.ContentDate,
|
||||||
|
dicom.ContentTime
|
||||||
|
),
|
||||||
|
ImageRows: dicom.Rows || 0,
|
||||||
|
ImageColumns: dicom.Columns || 0,
|
||||||
|
SliceLocation: dicom.SliceLocation,
|
||||||
|
SliceThickness: dicom.SliceThickness,
|
||||||
|
NumberOfFrames: dicom.NumberOfFrames || 0,
|
||||||
|
PixelSpacing: dicom.PixelSpacing,
|
||||||
|
ImagerPixelSpacing: dicom.ImagePixelSpacing,
|
||||||
|
FrameOfReferenceUID: dicom.FrameOfReferenceUID,
|
||||||
|
WindowCenter: dicom.WindowCenter,
|
||||||
|
WindowWidth: dicom.WindowWidth,
|
||||||
|
Path: dicom.path,
|
||||||
|
HtmlPath: "",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
studyObj[dicom.StudyId].SeriesList.push(series);
|
||||||
|
}
|
||||||
|
studyObj[dicom.StudyId].InstanceCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(studyObj);
|
||||||
|
return studyObj;
|
||||||
|
},
|
||||||
|
// dicom时间转换
|
||||||
|
getTime(date, time) {
|
||||||
|
let studyTime = "";
|
||||||
|
if (date) {
|
||||||
|
date = `${date.substring(0, 4)}-${date.substring(
|
||||||
|
4,
|
||||||
|
6
|
||||||
|
)}-${date.substring(6, 8)}`;
|
||||||
|
}
|
||||||
|
if (time) {
|
||||||
|
time = `${time.substring(0, 2)}:${time.substring(
|
||||||
|
2,
|
||||||
|
4
|
||||||
|
)}:${time.substring(4, 6)}`;
|
||||||
|
}
|
||||||
|
if (date) {
|
||||||
|
studyTime = time ? `${date} ${time}` : `${date} 00:00:00`;
|
||||||
|
}
|
||||||
|
return studyTime;
|
||||||
|
},
|
||||||
|
// 影像上传
|
||||||
|
async dicomToOSS(file, params, dicomInfo) {
|
||||||
|
let path = `/${params.TrialId}/TaskImage/${params.SubjectId}/${
|
||||||
|
params.VisitTaskId
|
||||||
|
}/${dicomInfo.StudyInstanceUid}/${this.getGuid(
|
||||||
|
dicomInfo.StudyInstanceUid +
|
||||||
|
dicomInfo.SeriesInstanceUid +
|
||||||
|
dicomInfo.SOPInstanceUid +
|
||||||
|
params.TrialId +
|
||||||
|
params.VisitTaskId
|
||||||
|
)}`;
|
||||||
|
try {
|
||||||
|
let res = await dcmUpload(path, file);
|
||||||
|
if (!res || !res.url) return false;
|
||||||
|
return this.$getObjectName(res.url);
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
@ -177,4 +633,18 @@ export default {
|
||||||
label {
|
label {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
.line {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin: 20px 0;
|
||||||
|
&::after {
|
||||||
|
display: block;
|
||||||
|
content: "";
|
||||||
|
flex: 1;
|
||||||
|
height: 1px;
|
||||||
|
border-top: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
|
@ -1,12 +1,13 @@
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import { encoder } from './encoder'
|
|
||||||
import { anonymization } from './anonymization'
|
import { anonymization } from './anonymization'
|
||||||
export const dcmUpload = async function(name, file, config){
|
export const dcmUpload = async function (name, file, config) {
|
||||||
return new Promise(async resolve => {
|
return new Promise(async resolve => {
|
||||||
try {
|
try {
|
||||||
// let blob = await encoder(file, config)
|
// let blob = await encoder(file, config)
|
||||||
// let blob = await fileToBlob(file)
|
let blob = await fileToBlob(file)
|
||||||
let blob = await anonymization(file, config)
|
if (config) {
|
||||||
|
blob = await anonymization(file, config)
|
||||||
|
}
|
||||||
let res = await Vue.prototype.OSSclient.put(name, blob.blob)
|
let res = await Vue.prototype.OSSclient.put(name, blob.blob)
|
||||||
resolve({
|
resolve({
|
||||||
...res,
|
...res,
|
||||||
|
@ -35,7 +36,7 @@ function fileToBlob(file) {
|
||||||
} else {
|
} else {
|
||||||
blob = e.target.result
|
blob = e.target.result
|
||||||
}
|
}
|
||||||
resolve(blob)
|
resolve({ blob })
|
||||||
})
|
})
|
||||||
// FileReader 以 ArrayBuffer 格式 读取 File 对象中数据
|
// FileReader 以 ArrayBuffer 格式 读取 File 对象中数据
|
||||||
reader.readAsArrayBuffer(file)
|
reader.readAsArrayBuffer(file)
|
||||||
|
|
|
@ -0,0 +1,188 @@
|
||||||
|
import * as dicomParser from "dicom-parser";
|
||||||
|
import * as cornerstone from "cornerstone-core";
|
||||||
|
import * as cornerstoneWADOImageLoader from "cornerstone-wado-image-loader";
|
||||||
|
cornerstoneWADOImageLoader.external.dicomParser = dicomParser;
|
||||||
|
cornerstoneWADOImageLoader.external.cornerstone = cornerstone;
|
||||||
|
import { convertBytes } from "@/utils/dicom-character-set";
|
||||||
|
import Vue from 'vue';
|
||||||
|
let dicom = {
|
||||||
|
PatientName: "x00100010", // 患者姓名
|
||||||
|
PatientId: "x00100020", // 患者ID
|
||||||
|
PatientBirthDate: "x00100030", // 患者出生日期
|
||||||
|
PatientBirthTime: "x00100032", // 患者出生时间
|
||||||
|
PatientSex: "x00100040", // 患者性别
|
||||||
|
PatientWeight: "x00101030", // 患者体重
|
||||||
|
PregnancyStatus: "x001021c0", // 怀孕状态
|
||||||
|
PatientAge: "x00101010", // 患者年龄(做检查时刻的患者年龄,而不是此刻患者的真实年龄)
|
||||||
|
AcquisitionTime: "x00080032",
|
||||||
|
AcquisitionNumber: "x00200012",
|
||||||
|
TriggerTime: "x00181060",
|
||||||
|
AccessionNumber: "x00080050", // 检查号
|
||||||
|
StudyId: "x00200010", // 检查ID
|
||||||
|
StudyInstanceUid: "x0020000d", // 检查实例号
|
||||||
|
StudyDate: "x00080020", // 检查日期
|
||||||
|
StudyTime: "x00080030", // 检查时间
|
||||||
|
Modalities: "x00080061", // 一个检查中含有的不同检查类型
|
||||||
|
BodyPartExamined: "x00080015", // 检查的部位
|
||||||
|
StudyDescription: "x00081030", // 检查的描述
|
||||||
|
InstitutionName: "x00080080",
|
||||||
|
SeriesNumber: "x00200011", // 序列号
|
||||||
|
SeriesInstanceUid: "x0020000e", // 序列实例号
|
||||||
|
Modality: "x00080060", // 检查模态
|
||||||
|
SeriesDescription: "x0008103e", // 检查描述和说明
|
||||||
|
SeriesDate: "x00080021", // 检查日期
|
||||||
|
SeriesTime: "x00080031", // 检查时间
|
||||||
|
SequenceName: "x00180024",
|
||||||
|
ProtocolName: "x00181030",
|
||||||
|
ImagePosition: "x00200032", // 图像位置
|
||||||
|
ImageOrientation: "x00200037", // 图像方位
|
||||||
|
ImagePixelSpacing: "x00181164",
|
||||||
|
SliceThickness: "x00180050", // 层厚
|
||||||
|
SpacingBetweenSlices: "x00180088", // 层间距
|
||||||
|
SliceLocation: "x00201041", // 相对位置
|
||||||
|
MRAcquisition: "x00180023",
|
||||||
|
seriesBodyPartExamined: "x00180015", // 身体部位
|
||||||
|
ImageType: "x00080008",
|
||||||
|
SopInstanceUid: "x00080018", // SOP实例UID
|
||||||
|
ContentDate: "x00080023", // 影像拍摄日期
|
||||||
|
ContentTime: "x00080033", // 影像拍摄时间
|
||||||
|
ImageOrInstanceNumber: "x00200013", // 图像码
|
||||||
|
SamplesPerPixel: "x00280002", // 图像采样率
|
||||||
|
PhotometricInterpretation: "x00280004", // 光度计(对于CT图像,用两个枚举值MONOCHROME1,MONOCHROME2 用来判断图像是否是彩色的;MONOCHROME 1/2是灰度图,RGB则是真彩色图)
|
||||||
|
Rows: "x00280010", // 行数
|
||||||
|
Columns: "x00280011", // 列数
|
||||||
|
NumberOfFrames: "x00280008",
|
||||||
|
PixelSpacing: "x00280030", // 像素间距
|
||||||
|
BitsAllocated: "x00280100", // 分配的位数
|
||||||
|
BitsStored: "x00280101", // 存储的位数
|
||||||
|
HighBit: "x00280102", // 高位
|
||||||
|
PixelRepresentation: "x00280103", // 像素数据的表现类型(一个枚举值,分别为十六进制数0000和0001.0000H = 无符号整型,0001H = 2的补码)
|
||||||
|
WindowCenter: "x00281050", // 窗位
|
||||||
|
WindowWidth: "x002811051", // 窗宽
|
||||||
|
RescaleIntercept: "x00281052", // 截距
|
||||||
|
RescaleSlope: "x00281053", // 斜率
|
||||||
|
RescaleType: "x00281054", // 输出值的单位
|
||||||
|
ParsingFormat: "x00080005",
|
||||||
|
FrameOfReferenceUID: "x00200052",
|
||||||
|
};
|
||||||
|
// 需要设置默认值
|
||||||
|
let defaultKey = ['Rows', 'Columns', 'SliceLocation', 'NumberOfFrames'];
|
||||||
|
// 需要格式解析
|
||||||
|
let pormatParseKey = ['PatientName', 'SeriesDescription', 'StudyDescription'];
|
||||||
|
// 解析dicom文件
|
||||||
|
export const parseDicom = (file, name = false) => {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
let reader = new FileReader();
|
||||||
|
reader.onload = function (e) {
|
||||||
|
try {
|
||||||
|
let data = dicomParser.parseDicom(new Uint8Array(e.target.result));
|
||||||
|
let res = {};
|
||||||
|
if (name && Array.isArray(name)) {
|
||||||
|
name.forEach((item) => {
|
||||||
|
if (dicom[item]) {
|
||||||
|
res[item] = data.string(dicom[item]) || data.uint16(dicom[item]) || data.intString(dicom[item]) || '';
|
||||||
|
}
|
||||||
|
if (item === 'SliceLocation') {
|
||||||
|
res[item] = data.intString(dicom[item])
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (name) {
|
||||||
|
if (dicom[name]) {
|
||||||
|
res[name] = data.string(dicom[name]) || data.uint16(dicom[name]) || data.intString(dicom[name]) || '';
|
||||||
|
if (name === 'SliceLocation') {
|
||||||
|
res[name] = data.intString(dicom[name])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reject("name is inexistence");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Object.keys(dicom).forEach((key) => {
|
||||||
|
res[key] = data.string(dicom[key]) || data.uint16(dicom[key]) || data.intString(dicom[key]) || '';
|
||||||
|
if (key === 'SliceLocation') {
|
||||||
|
res[key] = data.intString(dicom[key])
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
pormatParseKey.forEach(key => {
|
||||||
|
if (res[key]) {
|
||||||
|
const Element = data.elements[dicom[key]];
|
||||||
|
const Bytes = new Uint8Array(
|
||||||
|
data.byteArray.buffer,
|
||||||
|
Element ? Element.dataOffset : 0,
|
||||||
|
Element ? Element.length : 0
|
||||||
|
);
|
||||||
|
res[key] = convertBytes(
|
||||||
|
res.ParsingFormat,
|
||||||
|
Bytes
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defaultKey.forEach(key => {
|
||||||
|
if (!res[key] && res.hasOwnProperty(key)) {
|
||||||
|
res[key] = 0;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
resolve(res);
|
||||||
|
} catch (error) {
|
||||||
|
reject(error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.onerror = function (e) {
|
||||||
|
reject(e);
|
||||||
|
};
|
||||||
|
reader.readAsArrayBuffer(file);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
// 获取缩略图
|
||||||
|
export const getThumbnail = async (file, params, dicomInfo) => {
|
||||||
|
try {
|
||||||
|
if (dicomInfo.modality !== "SR") {
|
||||||
|
let fileId = cornerstoneWADOImageLoader.wadouri.fileManager.add(file);
|
||||||
|
let blob = await dicomToPng(
|
||||||
|
fileId,
|
||||||
|
dicomInfo.Columns,
|
||||||
|
dicomInfo.Rows
|
||||||
|
);
|
||||||
|
if (!blob) return "";
|
||||||
|
let thumbnailPath = `/${params.TrialId}/TaskImage/${params.SubjectId}/${params.VisitTaskId}/${dicomInfo.StudyInstanceUid}/${dicomInfo.SeriesInstanceUid}.png`;
|
||||||
|
let OSSclient = Vue.prototype.OSSclient;
|
||||||
|
let seriesRes = await OSSclient.put(thumbnailPath, blob);
|
||||||
|
if (seriesRes && seriesRes.url) {
|
||||||
|
return Vue.prototype.$getObjectName(seriesRes.url);
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const canvasToBlob = (canvas) => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
canvas.toBlob((blob) => {
|
||||||
|
resolve(blob);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const dicomToPng = (imageId, width, height) => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
cornerstone.loadImage(imageId).then(async (image) => {
|
||||||
|
let canvas = document.createElement("canvas");
|
||||||
|
canvas.width = width;
|
||||||
|
canvas.height = height;
|
||||||
|
if (image) {
|
||||||
|
cornerstone.renderToCanvas(canvas, image);
|
||||||
|
// 将 Canvas 图像对象转换为 PNG 格式
|
||||||
|
let blob = await canvasToBlob(canvas);
|
||||||
|
resolve(blob);
|
||||||
|
} else {
|
||||||
|
resolve(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}).catch((reason) => {
|
||||||
|
reason();
|
||||||
|
});
|
||||||
|
};
|
|
@ -43,6 +43,7 @@
|
||||||
prop="OrganizationName"
|
prop="OrganizationName"
|
||||||
>
|
>
|
||||||
<el-input
|
<el-input
|
||||||
|
:disabled="user.IsZhiZhun"
|
||||||
v-model="user.OrganizationName"
|
v-model="user.OrganizationName"
|
||||||
:placeholder="$t('trials:trials-myinfo:form:organization')"
|
:placeholder="$t('trials:trials-myinfo:form:organization')"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
>
|
>
|
||||||
<template v-for="item of $d.Criterion_Question_Type">
|
<template v-for="item of $d.Criterion_Question_Type">
|
||||||
<el-option
|
<el-option
|
||||||
v-if="item.value !== 'calculation' && item.value !== 'increment'"
|
v-if="item.value !== 'calculation' && item.value !== 'increment'&& item.value !== 'table'&& item.value !== 'group'"
|
||||||
:key="item.value"
|
:key="item.value"
|
||||||
:value="item.value"
|
:value="item.value"
|
||||||
:label="item.label"
|
:label="item.label"
|
||||||
|
|
|
@ -285,7 +285,7 @@ export default {
|
||||||
handleSubjectVisitChange(val) {
|
handleSubjectVisitChange(val) {
|
||||||
if (val) {
|
if (val) {
|
||||||
const selectArr = this.subjectVisitOptions.filter(item => item.Id === val)
|
const selectArr = this.subjectVisitOptions.filter(item => item.Id === val)
|
||||||
this.form.VisitNum = selectArr[0].VisitNum + 0.1
|
this.form.VisitNum = Math.ceil((selectArr[0].VisitNum + 0.1)*10)/10;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleIsFinalVisitChange(val) {
|
handleIsFinalVisitChange(val) {
|
||||||
|
|
|
@ -977,7 +977,15 @@ const PDFViewerApplication = {
|
||||||
|
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
},
|
},
|
||||||
|
decodeUtf8(bytes) {
|
||||||
|
let str = bytes.split('?');
|
||||||
|
let str2 = str[0].split('/');
|
||||||
|
let name = str2[str2.length-1];
|
||||||
|
name = encodeURIComponent(name);
|
||||||
|
str.shift();
|
||||||
|
str2.pop();
|
||||||
|
return str2.join("/")+'/'+name+'?'+str.join("?");
|
||||||
|
},
|
||||||
async open(file, args) {
|
async open(file, args) {
|
||||||
if (this.pdfLoadingTask) {
|
if (this.pdfLoadingTask) {
|
||||||
await this.close();
|
await this.close();
|
||||||
|
@ -992,6 +1000,7 @@ const PDFViewerApplication = {
|
||||||
const parameters = Object.create(null);
|
const parameters = Object.create(null);
|
||||||
|
|
||||||
if (typeof file === "string") {
|
if (typeof file === "string") {
|
||||||
|
file = this.decodeUtf8(file);
|
||||||
this.setTitleUsingUrl(file, file);
|
this.setTitleUsingUrl(file, file);
|
||||||
parameters.url = file;
|
parameters.url = file;
|
||||||
} else if (file && "byteLength" in file) {
|
} else if (file && "byteLength" in file) {
|
||||||
|
|
Loading…
Reference in New Issue