irc_web/src/views/trials/trials-panel/visit/crc-upload/components/uploadDicomFiles2.vue

2073 lines
74 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div class="upload-dicom-files-wrapper">
<!-- 已上传的dicom影像记录 -->
<el-divider content-position="left">{{
$t("trials:uploadedDicoms:title:dicomUploaded")
}}</el-divider>
<div class="functions" style="text-align: right">
<!-- //批量删除已上传的影像 -->
<el-button
:disabled="deleteArr.length === 0"
type="primary"
size="small"
icon="el-icon-delete"
@click="handleBatchDelete"
>
{{ $t("trials:uploadedDicoms:action:delete") }}
</el-button>
<!-- 预览 -->
<el-button
type="primary"
icon="el-icon-view"
size="small"
:disabled="studyList.length === 0"
@click="handlePreviewAllFiles"
>
{{ $t("trials:uploadedDicoms:action:preview") }}
</el-button>
</div>
<el-table
v-loading="studyLoading"
:data="studyList"
style="width: 100%"
:row-class-name="tableRowClassName"
max-height="250"
@selection-change="handleUploadedSelectionChange"
>
<el-table-column type="selection" width="55" />
<!-- 检查编号 -->
<el-table-column
prop="StudyCode"
:label="$t('trials:uploadedDicoms:table:studyId')"
min-width="80"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tooltip
placement="top"
v-if="
(() => {
var r = false;
if (scope.row.IsHaveUploadFailed) {
uploadQueues.forEach((v) => {
if (
v.dicomInfo.studyUid === scope.row.StudyInstanceUid &&
v.uploadState.record &&
v.uploadState.record.Failed.length
) {
r = true;
}
});
}
return r;
})()
"
>
<div slot="content">
{{ $t("trials:uploadDicomList:table:status4") }}
</div>
<span
class="el-icon-warning"
style="color: #cbb024; cursor: pointer"
v-if="scope.row.IsHaveUploadFailed"
></span>
</el-tooltip>
<el-tooltip placement="top" v-if="!scope.row.IsCompleteClinicalData">
<div slot="content">
{{ $t("trials:crc-upload:confirm:message") }}
</div>
<span
class="el-icon-warning"
style="color: red; cursor: pointer"
></span>
</el-tooltip>
{{ scope.row.StudyCode }}
</template>
</el-table-column>
<!-- 检查类型 -->
<el-table-column
prop="ModalityForEdit"
:label="$t('trials:audit:table:modality')"
/>
<!-- 检查设备 -->
<el-table-column
prop="Modalities"
:label="$t('trials:audit:table:modality1')"
/>
<!-- 检查部位 -->
<el-table-column
prop="BodyPartForEdit"
:label="$t('trials:uploadedDicoms:table:bodyPart')"
min-width="100"
show-overflow-tooltip
>
<template slot-scope="scope">
{{ getBodyPart(scope.row.BodyPartForEdit) }}
</template>
</el-table-column>
<!-- 序列数量 -->
<el-table-column
prop="SeriesCount"
:label="$t('trials:uploadedDicoms:table:seriesCount')"
min-width="100"
show-overflow-tooltip
/>
<!-- 图像数量 -->
<el-table-column
prop="InstanceCount"
:label="$t('trials:uploadedDicoms:table:instanceCount')"
min-width="100"
show-overflow-tooltip
/>
<!-- 检查日期 -->
<el-table-column
prop="StudyTime"
:label="$t('trials:uploadedDicoms:table:studyDate')"
min-width="120"
show-overflow-tooltip
>
<template slot-scope="scope">
{{
scope.row.StudyTime
? moment(scope.row.StudyTime).format("YYYY-MM-DD")
: ""
}}
</template>
</el-table-column>
<!-- 上传时间 -->
<el-table-column
prop="UploadedTime"
:label="$t('trials:uploadedDicoms:table:uploadedTime')"
min-width="80"
show-overflow-tooltip
/>
<el-table-column
:label="$t('common:action:action')"
min-width="260"
fixed="right"
>
<template slot-scope="scope">
<!-- 预览 -->
<el-button
icon="el-icon-view"
:disabled="scope.row.SeriesCount === 0 || scope.row.IsDeleted"
:title="$t('trials:uploadedDicoms:action:preview')"
circle
@click="handleViewStudy(scope.row)"
/>
<!-- 上传临床数据 -->
<el-button
icon="el-icon-upload2"
v-if="
['PT、CT', 'CT、PT', 'PET-CT'].includes(scope.row.Modalities) &&
relationInfo.IsHaveStudyClinicalData
"
:title="$t('trials:workbench:title:UploadClinicalData')"
circle
@click="handleUploadPetData(scope.row)"
/>
<!-- 编辑 -->
<el-button
icon="el-icon-edit-outline"
v-hasPermi="['trials:trials-panel:visit:crc-upload:edit']"
:title="$t('common:button:edit')"
circle
:disabled="scope.row.IsDeleted"
@click="handleEditStudy(scope.row)"
/>
<!-- 删除 -->
<el-button
icon="el-icon-delete"
:disabled="scope.row.IsDeleted"
:title="$t('trials:uploadedDicoms:action:delete')"
circle
@click="handleDeleteStudy(scope.row)"
/>
<!-- <el-button-->
<!-- icon="el-icon-toilet-paper"-->
<!-- circle-->
<!-- :title="$t('trials:uploadDicomFiles:button:historical')"-->
<!-- @click="handleHistorical(scope.row)"-->
<!-- />-->
</template>
</el-table-column>
</el-table>
<!-- 多文件上传 -->
<el-divider content-position="left">{{
$t("trials:uploadedDicoms:title:dicomFilesOnly")
}}</el-divider>
<el-tabs v-model="uploadActiveName">
<!-- 本地上传 -->
<el-tab-pane
:label="$t('trials:uploadedDicoms:tab:uploadFile')"
name="file"
>
<form id="inputForm" ref="uploadForm" enctype="multipart/form-data">
<div class="form-group">
<div id="directoryInputWrapper" class="btn btn-link file-input">
<el-button
type="primary"
:disabled="btnLoading"
:loading="btnLoading"
size="small"
>{{
$t("trials:uploadedDicomsicom:button:selectFolder")
}}</el-button
>
<input
type="file"
name="file"
ref="pathClear"
:disabled="btnLoading"
webkitdirectory
multiple
@change="beginScanFiles($event)"
/>
</div>
</div>
</form>
<!-- 文件列表 -->
<el-table
ref="dicomFilesTable"
:data="uploadQueues"
:row-key="(row) => row.studyIndex"
class="dicomFiles-table"
@selection-change="handleSelectionChange"
>
<el-table-column
type="selection"
width="55"
:selectable="handleSelectable"
/>
<el-table-column type="index" width="40" />
<el-table-column min-width="200" show-overflow-tooltip>
<template slot="header">
<el-tooltip placement="top">
<div slot="content">
{{ $t("trials:uploadDicomList:table:studyDetail1") }}<br />
{{ $t("trials:uploadDicomList:table:studyDetail2") }}<br />
{{ $t("trials:uploadedDicoms:table:studyDate") }}
</div>
<span>{{ $t("trials:uploadDicomList:table:studyInfo") }}</span>
</el-tooltip>
</template>
<template slot-scope="scope">
<div style="line-height: 15px">
<div>
<div>
<span v-if="scope.row.dicomInfo.accNumber"
><span style="font-weight: 500">Acc:</span>
{{ scope.row.dicomInfo.accNumber }}</span
>
<span v-else style="color: #f44336">N/A</span>
</div>
<div style="display: inline-block; margin-right: 2px">
<span v-if="scope.row.dicomInfo.modality.length > 0">
{{ scope.row.dicomInfo.modality.join("、") }},</span
>
<span v-else style="color: #f44336">N/A,</span>
</div>
<div style="display: inline-block; margin-right: 2px">
<span v-if="scope.row.seriesList.length"
>{{ scope.row.seriesList.length }} Series,</span
>
<span v-else style="color: #f44336">N/A,</span>
</div>
<div style="display: inline-block">
<span v-if="scope.row.fileList.length"
>{{ scope.row.fileList.length }} Instances</span
>
<span v-else style="color: #f44336">N/A</span>
</div>
</div>
<div>
<div style="display: inline-block; margin-right: 2px">
<span v-if="scope.row.dicomInfo.bodyPart">
{{ scope.row.dicomInfo.bodyPart }},
</span>
<span v-else style="color: #f44336">N/A, </span>
</div>
<div style="display: inline-block">
<span v-if="scope.row.dicomInfo.description">
{{ scope.row.dicomInfo.description }}</span
>
<span v-else style="color: #f44336">N/A</span>
</div>
</div>
<div>
{{ scope.row.dicomInfo.studyTime }}
</div>
</div>
</template>
</el-table-column>
<el-table-column min-width="130" show-overflow-tooltip>
<template slot="header">
<el-tooltip placement="top">
<div slot="content">
{{ $t("trials:uploadDicomList:table:pId") }}<br />
{{ $t("trials:uploadDicomList:table:patientName") }}<br />
{{ $t("trials:uploadDicomList:table:pInfo") }}
</div>
<span>{{
$t("trials:uploadDicomList:table:patientInfo")
}}</span>
</el-tooltip>
</template>
<template slot-scope="scope">
<div style="line-height: 15px">
<div>
<span v-if="scope.row.dicomInfo.patientId"
><span style="font-weight: 500">PID: </span
>{{ scope.row.dicomInfo.patientId }}</span
>
<span v-else style="color: #f44336">N/A</span>
</div>
<div>
<span
:class="[
scope.row.dicomInfo.patientName ? '' : 'colorOfRed',
]"
>
{{
scope.row.dicomInfo.patientName
? scope.row.dicomInfo.patientName
: "N/A"
}}
</span>
</div>
<div>
<span
:class="[
scope.row.dicomInfo.patientSex ? '' : 'colorOfRed',
]"
>
{{
scope.row.dicomInfo.patientSex
? scope.row.dicomInfo.patientSex
: "N/A"
}},
</span>
<span
:class="[
scope.row.dicomInfo.patientAge ? '' : 'colorOfRed',
]"
>
{{
scope.row.dicomInfo.patientAge
? scope.row.dicomInfo.patientAge
: "N/A"
}},
</span>
<span
:class="[
scope.row.dicomInfo.patientBirthDate ? '' : 'colorOfRed',
]"
>
{{
scope.row.dicomInfo.patientBirthDate
? scope.row.dicomInfo.patientBirthDate
: "N/A"
}}
</span>
</div>
</div>
</template>
</el-table-column>
<el-table-column
:label="$t('trials:uploadDicomList:table:failedFileCount')"
min-width="120"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-progress
color="#409eff"
:percentage="
(scope.row.dicomInfo.failedFileCount * 100) /
scope.row.dicomInfo.fileCount
"
:show-text="false"
/>
<span
>{{ scope.row.dicomInfo.failedFileCount }} /
{{ scope.row.dicomInfo.fileCount }}</span
>
</template>
</el-table-column>
<el-table-column
:label="$t('trials:uploadDicomList:table:status')"
min-width="140"
show-overflow-tooltip
>
<template slot-scope="scope">
<span
v-if="
!scope.row.dicomInfo.failedFileCount &&
!scope.row.dicomInfo.isInit
"
>
{{ $t("trials:uploadDicomList:table:status1") }}</span
>
<span
style="color: #409eff"
v-else-if="
!scope.row.dicomInfo.failedFileCount &&
scope.row.dicomInfo.isInit &&
btnLoading
"
>{{ $t("trials:uploadDicomList:table:status2") }}</span
>
<span
style="color: #409eff"
v-else-if="
scope.row.dicomInfo.failedFileCount <
scope.row.dicomInfo.fileCount &&
!scope.row.uploadState.record
"
>{{ $t("trials:uploadDicomList:table:status2") }}</span
>
<span
style="color: #2cc368"
v-else-if="
scope.row.dicomInfo.failedFileCount ===
scope.row.dicomInfo.fileCount
"
>{{ $t("trials:uploadDicomList:table:status3") }}</span
>
<span
style="color: #f66"
v-else-if="
scope.row.uploadState.record &&
scope.row.uploadState.record.fileCount === 0
"
>{{ $t("trials:uploadDicomList:table:status5") }}</span
>
<span style="color: #f66" v-else>{{
$t("trials:uploadDicomList:table:Failed")
}}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('trials:uploadDicomList:table:record')"
min-width="140"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tooltip placement="top" v-if="scope.row.uploadState.record">
<div slot="content">
<div style="max-height: 500px; overflow-y: auto">
{{ $t("trials:uploadDicomList:table:Existed") }}:
<div v-if="scope.row.uploadState.record.Existed.length">
<div
v-for="item of scope.row.uploadState.record.Existed"
:key="item"
style="font-size: 12px; color: #baa72a"
>
{{ item }}
</div>
</div>
<div v-else>&nbsp;</div>
{{ $t("trials:uploadDicomList:table:Uploaded") }}:
<div v-if="scope.row.uploadState.record.Uploaded.length">
<div
v-for="item of scope.row.uploadState.record.Uploaded"
:key="item"
style="font-size: 12px; color: #24b837"
>
{{ item }}
</div>
</div>
<div v-else>&nbsp;</div>
<br />
{{ $t("trials:uploadDicomList:table:Failed") }}:
<div v-if="scope.row.uploadState.record.Failed.length">
<div
v-for="item of scope.row.uploadState.record.Failed"
:key="item"
style="font-size: 12px; color: #f66"
>
{{ item }}
</div>
</div>
<div v-else>&nbsp;</div>
</div>
</div>
<el-button size="mini" style="cursor: pointer">
<span style="font-size: 12px; color: #baa72a">{{
scope.row.uploadState.record.Existed.length
}}</span>
/
<span style="font-size: 12px; color: #24b837">{{
scope.row.uploadState.record.Uploaded.length
}}</span>
/
<span style="font-size: 12px; color: #f66">{{
scope.row.uploadState.record.Failed.length
}}</span>
</el-button>
</el-tooltip>
</template>
</el-table-column>
<el-table-column
v-show="isScan"
:label="$t('common:action:action')"
width="200"
>
<template slot-scope="scope">
<!-- 预览 -->
<!-- :disabled="scope.row.dicomInfo.failedFileCount < scope.row.dicomInfo.fileCount && scope.row.dicomInfo.failedFileCount !== 0"-->
<el-button
icon="el-icon-view"
circle
:disabled="
scope.row.uploadState.stateCode !== '' &&
scope.row.dicomInfo.failedFileCount <
scope.row.dicomInfo.fileCount &&
!scope.row.uploadState.record
"
:title="$t('trials:uploadedDicoms:action:preview')"
size="small"
@click="handlePreview(scope.row.dicomInfo.studyUid)"
/>
<!-- 移除 -->
<el-button
icon="el-icon-delete"
circle
:title="$t('trials:uploadedDicoms:action:delete')"
size="small"
:disabled="
scope.row.uploadState.stateCode !== '' &&
scope.row.dicomInfo.failedFileCount <
scope.row.dicomInfo.fileCount &&
!scope.row.uploadState.record
"
@click="handleDelete(scope.$index, scope.row)"
/>
</template>
</el-table-column>
</el-table>
<div style="text-align: right; padding: 10px 0px">
<!-- 关闭 -->
<!-- <el-button :disabled="btnLoading" size="small" type="primary" @click="cancel">
{{ $t('trials:uploadDicomList:button:close') }}
</el-button> -->
<!-- 上传 -->
<el-button
size="small"
type="primary"
:disabled="selectArr.length == 0 || !isScan"
:loading="btnLoading"
@click="beginUploadQueues"
>
{{ $t("trials:uploadDicomList:button:upload") }}
</el-button>
</div>
</el-tab-pane>
<!-- pacs上传 -->
<el-tab-pane
:label="$t('trials:uploadNonDicoms:tab:uploadPacs')"
name="pacs"
:disabled="btnLoading"
>
<uploadDicomPacs
v-if="uploadActiveName === 'pacs'"
ref="dicomPacs"
:subjectVisitId="subjectVisitId"
:subjectId="subjectId"
@getList="getParentList"
/>
</el-tab-pane>
</el-tabs>
<!-- 预览影像模态框 -->
<el-dialog
v-if="previewVisible"
:fullscreen="true"
:show-close="true"
:visible.sync="previewVisible"
append-to-body
>
<DicomPreview :uid="uid" :studyList="uploadQueues" />
</el-dialog>
<!--pet-ct临床数据上传-->
<el-dialog
v-if="petVisible"
:show-close="true"
:visible.sync="petVisible"
append-to-body
>
<uploadPetClinicalData
:subject-visit-id="data.Id"
:data="data"
:studyData="studyData"
:allow-add-or-edit="true"
@getStudyInfo="getStudyInfo"
/>
</el-dialog>
<!-- 校验警告信息模态框 -->
<el-dialog
v-if="warning_cfg.visible"
:visible.sync="warning_cfg.visible"
width="500px"
:close-on-click-modal="false"
append-to-body
title="Warning"
custom-class="warning-dialog"
>
<div style="border: 1px solid #e0e0e0; padding: 10px">
<!-- Information from DICOM headers not consistent with that of this subject -->
<div style="color: red; font-size: 14px; margin-bottom: 10px">
{{ $t("trials:uploadDicomList:message:informationConsistent") }}:
</div>
<div v-for="(item, i) in warningArr" :key="item.index">
<div>{{ `(${i + 1}). ACC: ${item.accNumber}` }}</div>
<div
v-for="(warning, index) in item.warnings"
:key="index"
style="margin: 10px 0px; font-size: 13px"
>
<ul>
<li>{{ warning }}</li>
</ul>
</div>
</div>
</div>
<div slot="footer" class="base-modal-footer">
<el-button size="small" type="primary" @click="handleCancelWarnVisible">
{{ $t("common:button:cancel") }}
</el-button>
<el-button size="small" type="primary" @click="handleContinueUpload">
{{ $t("trials:uploadDicomList:button:upload") }}
</el-button>
</div>
</el-dialog>
<el-dialog
v-if="editStudyInfoVisible"
:title="$t('trials:audit:action:edit')"
:visible.sync="editStudyInfoVisible"
:close-on-click-modal="false"
append-to-body
custom-class="base-dialog-wrapper"
width="600px"
>
<div
style="
padding: 10px;
border: 1px solid #e0e0e0;
max-height: 650px;
overflow-y: auto;
"
>
<el-form ref="studyForm" :model="studyForm" label-width="100px">
<!-- 检查编号 -->
<el-form-item :label="$t('trials:audit:table:studyId')">
<el-input v-model="studyForm.StudyCode" disabled />
</el-form-item>
<!-- 检查类型 -->
<el-form-item
v-if="studyForm.IsDicomData"
:label="$t('trials:audit:table:modality')"
>
<el-input v-model="studyForm.Modalities" disabled />
</el-form-item>
<!-- 检查类型 -->
<el-form-item
v-else
:label="$t('trials:audit:table:modality')"
prop="Modalities"
:rules="[
{
required: true,
message: $t('common:ruleMessage:specify'),
trigger: 'blur',
},
]"
>
<el-radio-group v-model="studyForm.Modality">
<el-radio
v-for="m in trialModalitys"
v-show="m !== ''"
:key="m"
:label="m"
style="margin-bottom: 15px"
/>
</el-radio-group>
</el-form-item>
<!-- 检查部位 -->
<el-form-item
:label="$t('trials:audit:table:bodyPart')"
prop="BodyPartForEdit"
:rules="[
{
required: true,
message: $t('common:ruleMessage:specify'),
trigger: 'blur',
},
]"
>
<el-checkbox-group v-model="studyForm.BodyPartForEdit">
<el-checkbox
v-for="bodyPart in trialBodyPartTypes"
:key="bodyPart"
:label="bodyPart"
>{{
$fd("Bodypart", bodyPart, "Code", BodyPart, "Name")
}}</el-checkbox
>
</el-checkbox-group>
</el-form-item>
<!-- 序列数量 -->
<el-form-item :label="$t('trials:audit:table:seriesCount')">
<el-input v-model="studyForm.SeriesCount" disabled />
</el-form-item>
<!-- 图像数量 -->
<el-form-item
v-if="studyForm.InstanceCount"
:label="$t('trials:audit:table:instanceCount')"
>
<el-input v-model="studyForm.InstanceCount" disabled />
</el-form-item>
<!-- 检查日期 -->
<el-form-item :label="$t('trials:audit:table:studyDate')">
<el-date-picker
v-model="studyForm.StudyTime"
disabled
type="date"
value-format="yyyy-MM-dd"
format="yyyy-MM-dd"
style="width: 100%"
/>
</el-form-item>
</el-form>
</div>
<div slot="footer" class="dialog-footer">
<el-button
:disabled="btnLoading"
size="small"
type="primary"
@click="editStudyInfoVisible = false"
>
{{ $t("common:button:cancel") }}
</el-button>
<el-button
:loading="btnLoading"
size="small"
type="primary"
@click="handleUpdateStudyInfo"
>
{{ $t("common:button:save") }}
</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {
dicomUploadInProgress,
batchVerifyStudyAllowUpload,
getSubjectVisitUploadedStudyList,
deleteStudyList,
updateModality,
preArchiveDicomStudy,
addOrUpdateArchiveStudy,
getCRCClinicalData,
} from "@/api/trials";
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 DicomPreview from "@/views/dicom-show/dicom-preview";
import uploadPetClinicalData from "./uploadPetClinicalData.vue";
import dicomStore from "@/utils/dicom-store";
import { getToken } from "@/utils/auth";
import { dcmUpload } from "@/utils/dcmUpload/dcmUpload";
import { convertBytes } from "@/utils/dicom-character-set";
import moment from "moment";
import store from "@/store";
import uploadDicomPacs from "./uploadDiocmPacs.vue";
export default {
name: "UploadDicomFiles",
components: { DicomPreview, uploadPetClinicalData, uploadDicomPacs },
props: {
data: {
type: Object,
default() {
return {};
},
},
subjectVisitId: {
type: String,
required: true,
},
subjectId: {
type: String,
required: true,
},
activeName: {
type: String,
required: true,
},
},
data() {
return {
uploadActiveName: "file",
editStudyInfoVisible: false,
studyForm: {
StudyCode: "",
IsDicomData: true,
Modalities: "",
BodyPartForEdit: [],
SeriesCount: null,
StudyTime: "",
},
uploadQueues: dicomStore.studyList, // 上传队列
selectArr: [], // 已勾选待上传文件
scanState: "", // 当前上传队列上传状态
form: {
SiteName: "",
SubjectName: "",
VisitName: "",
SubjectId: "",
VisitNum: null,
},
btnLoading: false,
previewVisible: false,
warning_cfg: { visible: false },
warningArr: [],
isScan: false,
studyLoading: false,
studyList: [],
deleteArr: [],
trialId: "",
moment,
uploadCount: 0,
uploadedCount: 0,
wsList: [],
relationInfo: null,
trialBodyPartTypes: [],
trialModalitys: [],
myInterval: [],
studyErrorList: [],
confirmFlag: false,
// pet-ct临床数据上传
petVisible: false,
studyData: [],
BodyPart: {},
};
},
watch: {
btnLoading() {
store.dispatch("trials/setUnLock", this.btnLoading);
},
},
async mounted() {
this.trialId = this.$route.query.trialId;
this.BodyPart.Bodypart = await this.$getBodyPart(this.$route.query.trialId);
if (Object.keys(this.data).length) {
this.form = { ...this.data };
}
this.getStudyInfo();
this.uploadQueues = [];
},
beforeDestroy() {
this.myInterval.forEach((v) => {
clearInterval(v);
});
this.myInterval = [];
store.dispatch("trials/setUnLock", false);
},
methods: {
getParentList() {
this.getStudyInfo();
this.$emit("getList");
},
handleHistorical(row) {
this.$router.push(
`/trials/trials-panel/trial-summary/upload-monitor?trialId=${this.trialId}&trialCode=${this.$route.query.trialCode}&${this.$route.query.researchProgramNo}&studyCode=${row.StudyCode}`
);
},
// 打开检查信息编辑框
handleEditStudy(row) {
this.editStudyInfoVisible = true;
this.studyForm = { ...row };
var bodyPart = [];
if (this.studyForm.BodyPartForEdit.indexOf("|") !== -1) {
bodyPart = this.studyForm.BodyPartForEdit.split("|");
} else if (this.studyForm.BodyPartForEdit !== "") {
bodyPart.push(this.studyForm.BodyPartForEdit);
}
this.$set(this.studyForm, "Modality", row.ModalityForEdit);
this.studyForm.BodyPartForEdit = bodyPart;
},
// 更新拍片部位/拍片类型信息
handleUpdateStudyInfo() {
this.$refs["studyForm"].validate((valid) => {
if (!valid) return;
this.btnLoading = true;
this.studyForm.BodyPart = this.studyForm.BodyPartForEdit.join("|");
// this.studyForm.Modality = this.studyForm.Modalities
var params = {
id: this.studyForm.StudyId,
subjectVisitId: this.data.Id,
type: 1,
modality: this.studyForm.Modality,
bodyPart: this.studyForm.BodyPart,
};
updateModality(this.trialId, params)
.then((res) => {
this.btnLoading = false;
if (res.IsSuccess) {
this.getStudyInfo();
this.$message.success(
this.$t("common:message:savedSuccessfully")
);
this.editStudyInfoVisible = false;
}
})
.catch(() => {
this.btnLoading = false;
});
});
},
format(v) {
return v;
},
// 获取已上传的dicom影像
getStudyInfo() {
this.studyLoading = true;
getSubjectVisitUploadedStudyList(this.subjectVisitId)
.then((res) => {
// this.studyList = res.Result
this.relationInfo = res.OtherInfo;
this.trialBodyPartTypes = this.relationInfo.BodyPartTypes
? this.relationInfo.BodyPartTypes.split("|")
: [];
this.trialModalitys = this.relationInfo.Modalitys
? this.relationInfo.Modalitys.split("|")
: [];
this.studyLoading = false;
this.studyList = res.Result;
})
.catch(() => {
this.studyLoading = false;
});
},
// 弹框提示pet-ct临床数据未上传
petDataTip() {
if (this.confirmFlag) {
this.confirmFlag = false;
this.$confirm(
this.$t("trials:crc-upload:confirm:message"),
this.$t("trials:uploadDicomList:label:prompt"),
{
confirmButtonText: this.$t("trials:reviewTrack:impactList:save"),
// cancelButtonText: this.$t("common:button:cancel"),
type: "warning",
showCancelButton: false,
}
)
.then(() => {
this.confirmFlag = false;
})
.catch(() => {
this.confirmFlag = false;
});
}
},
// 预览单个检查影像
handleViewStudy(row) {
var token = getToken();
const routeData = this.$router.resolve({
path: `/showdicom?studyId=${row.StudyId}&TokenKey=${token}&type=Study`,
});
window.open(routeData.href, "_blank");
},
// 打开上传pet临床数据弹框
handleUploadPetData(row) {
this.studyData = row;
this.petVisible = true;
},
// 预览所有影像
handlePreviewAllFiles() {
var token = getToken();
if (this.$route.path === "/trials/trials-panel/visit/crc-question") {
var routeData = this.$router.resolve({
path: `/showvisitdicoms?trialId=${this.trialId}&visitInfo=${this.data.VisitName}(${this.data.VisitNum})&subjectVisitId=${this.data.SubjectVisitId}&isFromCRCUpload=1&TokenKey=${token}`,
});
} else {
var routeData = this.$router.resolve({
path: `/showvisitdicoms?trialId=${this.trialId}&visitInfo=${this.data.VisitName}(${this.data.VisitNum})&subjectVisitId=${this.data.Id}&isFromCRCUpload=1&TokenKey=${token}`,
});
}
window.open(routeData.href, "_blank");
},
// 扫描待上传文件
beginScanFiles(e) {
var files = e.target.files;
if (files.length > 0) {
var checkFiles = [...files];
var scope = this;
scope.scanState = "";
scope.isScan = false;
var p = new Promise(function (resolve) {
resolve();
});
var validFilesCount = 0;
for (var i = 0; i < files.length; ++i) {
(function (index) {
p = p.then(function () {
if (files[index].name.indexOf("DICOMDIR") === -1) {
validFilesCount = validFilesCount + 1;
return scope.parseDicomFile(files[index]);
}
});
})(i);
}
p.then(function (result) {
scope.uploadQueues.forEach((v, i) => {
scope.$refs.dicomFilesTable.toggleRowSelection(v, true);
});
scope.scanState = "finished";
scope.isScan = true;
});
}
},
// 按序列UUID本地归档
parseDicomFile(file) {
var scope = this;
return new Promise(function (resolve, reject) {
if (scope.scanState === "cancelling") {
resolve();
return;
}
var reader = new FileReader();
reader.onload = function (e) {
try {
var data = dicomParser.parseDicom(new Uint8Array(e.target.result));
var modality = data.string("x00080060") || "";
var studyUid = data.string("x0020000d");
var pixelDataElement = data.elements.x7fe00010;
var studyIndex = 0;
while (
studyIndex < scope.uploadQueues.length &&
scope.uploadQueues[studyIndex].dicomInfo.studyUid !== studyUid &&
(pixelDataElement || modality === "SR") &&
modality != ""
) {
++studyIndex;
}
if (studyIndex >= scope.uploadQueues.length) {
var date = data.string("x00080020");
var time = data.string("x00080030");
var 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`;
}
const patientNameElement = data.elements.x00100010;
const patientNameBytes = new Uint8Array(
data.byteArray.buffer,
patientNameElement ? patientNameElement.dataOffset : 0,
patientNameElement ? patientNameElement.length : 0
);
const patientNameStr = convertBytes(
data.string("x00080005"),
patientNameBytes
);
scope.uploadQueues.push({
studyIndex: studyIndex,
seriesList: [],
fileList: [],
dicomInfo: {
studyId: data.string("x00200010"),
studyUid: studyUid,
patientId: data.string("x00100020"),
patientName: patientNameStr,
patientAge: data.string("x00101010")
? data.string("x00101010")
: "",
patientSex: data.string("x00100040")
? data.string("x00100040")
: "",
patientBirthDate: data.string("x00100030"),
hospitalName: data.string("x00080080"),
accNumber: data.string("x00080050"),
bodyPart: data.string("x00180015") || "",
modality: [],
institutionName: data.string("x00080080"),
description: data.string("x00081030"),
//0008,0032
acquisitionTime: data.string("x00080032"),
acquisitionNumber: data.string("x00200012"),
triggerTime: data.string("x00181060"),
studyTime: studyTime,
VisitInfo: {},
SubjectInfo: {},
failedFileCount: 0,
fileSize: 0,
fileCount: 0,
isInit: false,
},
uploadState: {
selected: false,
stateCode: "",
beginUploadTime: {},
progressValue: 0,
},
});
}
var modality = scope.uploadQueues[studyIndex].dicomInfo.modality;
var currentModality = data.string("x00080060");
if (!(modality.indexOf(currentModality) > -1)) {
modality.push(currentModality);
}
var fileList = scope.uploadQueues[studyIndex].fileList;
var instanceUid = data.string("x00080018");
var instanceIndex = 0;
while (
instanceIndex < fileList.length &&
fileList[instanceIndex].instanceUid !== instanceUid &&
(pixelDataElement || modality === "SR")
) {
++instanceIndex;
}
if (instanceIndex >= fileList.length) {
fileList.push({
instanceUid: instanceUid,
file: file,
});
}
scope.uploadQueues[studyIndex].dicomInfo.fileCount =
fileList.length;
scope.uploadQueues[studyIndex].dicomInfo.fileSize = fileList.reduce(
(prev, next) => {
return prev + next.file.size;
},
0
);
var seriesUid = data.string("x0020000e");
var seriesList = scope.uploadQueues[studyIndex].seriesList;
var seriesItem = seriesList.find(function (item) {
return item.seriesUid === seriesUid;
});
if (!seriesItem && (pixelDataElement || modality === "SR")) {
var date = data.string("x00080021");
var time = data.string("x00080031");
var seriesTime = "";
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) {
seriesTime = time ? `${date} ${time}` : `${date} 00:00:00`;
}
const seriesDescriptionElement = data.elements.x0008103e;
const seriesDescriptionBytes = new Uint8Array(
data.byteArray.buffer,
seriesDescriptionElement
? seriesDescriptionElement.dataOffset
: 0,
seriesDescriptionElement ? seriesDescriptionElement.length : 0
);
const seriesDescriptionStr = convertBytes(
data.string("x00080005"),
seriesDescriptionBytes
);
seriesItem = {
seriesUid: seriesUid,
seriesNumber: data.intString("x00200011") || 1,
modality: data.string("x00080060") || "",
description: seriesDescriptionStr,
seriesTime: seriesTime,
sliceThickness: data.string("x00180050") || "",
imagePositionPatient: data.string("x00200032") || "",
imageOrientationPatient: data.string("x00200037") || "",
sequenceName: data.string("x00180024") || "",
protocolName: data.string("x00181030") || "",
imagerPixelSpacing: data.string("x00181164") || "",
instanceList: [],
};
seriesList.push(seriesItem);
}
var instanceList = seriesItem.instanceList;
var instanceItem = instanceList.find(function (item) {
return item.instanceUid === instanceUid;
});
if (!instanceItem) {
var date = data.string("x00080023");
var time = data.string("x00080033");
var instanceTime = "";
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) {
instanceTime = time ? `${date} ${time}` : `${date} 00:00:00`;
}
instanceItem = {
instanceUid: instanceUid,
instanceNumber: data.intString("x00200013") || 1,
frameCount: data.intString("x00280008") || 1,
instanceTime: instanceTime,
imageRows: data.uint16("x00280010") || 0,
imageColumns: data.uint16("x00280011") || 0,
sliceLocation: data.intString("x00201041") || 0,
sliceThickness: data.string("x00180050") || "",
numberOfFrames: data.intString("x00280008") || 0,
pixelSpacing: data.string("x00280030") || "",
imagerPixelSpacing: data.string("x00181164") || "",
frameOfReferenceUID: data.string("x00200052") || "",
windowCenter: data.string("x00281050") || "",
windowWidth: data.string("x00281051") || "",
modality: data.string("x00080060") || "",
file: file,
};
instanceList.push(instanceItem);
}
resolve();
} catch (error) {
console.log(error);
resolve();
}
};
reader.onerror = function (e) {
resolve();
};
reader.readAsArrayBuffer(file);
});
},
// 上传之前校验基本信息及检查是否已上传
beginUploadQueues() {
this.scanState = "uploading";
this.studyErrorList = [];
// console.log(this.uploadQueues, this.selectArr)
this.warningArr = [];
for (let i = 0; i < this.selectArr.length; ++i) {
const index = this.selectArr[i].studyIndex;
this.uploadQueues[index].uploadState.stateCode = "uploading";
this.verifyBasicInfos(index);
}
if (this.warningArr.length) {
this.warning_cfg.visible = true;
this.btnLoading = false;
} else {
this.verifyStudy();
}
},
// 校验影像中检查时间、性别是否与受试者匹配
verifyBasicInfos(index) {
var warnings = [];
if (
this.uploadQueues[index].dicomInfo.patientSex &&
this.form.SubjectSex
) {
if (
this.uploadQueues[index].dicomInfo.patientSex.substr(0, 1) !==
this.form.SubjectSex.substr(0, 1)
) {
warnings.push(
this.$t("trials:uploadDicomList:message:genderConsistent")
);
}
}
if (warnings.length > 0) {
this.warningArr.push({
index: this.uploadQueues[index].studyIndex + 1,
accNumber: this.uploadQueues[index].dicomInfo.accNumber,
warnings: warnings,
});
}
},
// 校验该检查是否上传过
verifyStudy() {
this.btnLoading = true;
var studyList = [];
this.selectArr.forEach((item) => {
if (!item.uploadState.selected) {
studyList.push({
studyInstanceUid: item.dicomInfo.studyUid,
studyDate: item.dicomInfo.studyTime,
});
}
});
var param = {
trialId: this.trialId,
subjectId: this.subjectId,
subjectVisitId: this.subjectVisitId,
visitNum: this.form.VisitNum,
studyInstanceUidList: studyList,
};
batchVerifyStudyAllowUpload(param)
.then(async (res) => {
var messageArr = [];
res.Result.forEach((item) => {
const i = this.uploadQueues.findIndex(
(value) => value.dicomInfo.studyUid === item.StudyInstanceUid
);
let instanceUidList = [];
item.UploadedSeriesList.forEach((v) => {
v.SOPInstanceUIDList.forEach((v1) => {
instanceUidList.push(v1);
});
});
// isReUploadinstanceUidList1.includes(instanceUidList2)
try {
this.uploadQueues[i].seriesList.forEach((v) => {
let o = item.UploadedSeriesList.find((v1) => {
return v.seriesUid === v1.SeriesId;
// v.instanceList.forEach(v2 => {
// let o2 = v1.SOPInstanceUIDList.find(v3 => {
// return v3 === v2.instanceUid
// })
// if (!!o2) {
// v2.isReUpload = true
// }
// })
});
if (!!o) {
v.isReUpload = true;
}
});
this.uploadQueues[i].seriesList.forEach((v) => {
v.instanceList.forEach((v2) => {
let o2 = instanceUidList.find((v3) => {
return v3 === v2.instanceUid;
});
if (!!o2) {
v2.isReUpload = true;
}
});
});
} catch (e) {
console.log(e);
}
// 不能上传或不能重传时,收集校验结果提示,并取消勾选,设置上传状态
if (item.ErrorMesseage) {
this.uploadQueues[i].uploadState.AllowReUpload =
item.AllowReUpload;
this.$refs.dicomFilesTable.toggleRowSelection(
this.uploadQueues[i]
);
this.uploadQueues[i].uploadState.selected = true;
const msg = `${item.ErrorMesseage}`;
messageArr.push(msg);
}
// 可以重传时记录需要被覆盖的检查ID
if (item.AllowReUpload) {
this.uploadQueues[i].AbandonStudyId = item.StudyInfo
? item.StudyInfo.Id
: "";
}
this.uploadQueues[i].uploadState.AllowReUpload = item.AllowReUpload;
});
if (messageArr.length > 0) {
var li = messageArr.map((msg) => {
return `<li>${msg}</li>`;
});
const content = `
<div>
<ol>
${li.join("")}
</ol>
</div>`;
this.$alert(
content,
this.$t("trials:uploadDicomList:label:prompt"),
{
confirmButtonText: this.$t(
"trials:uploadDicomList:label:confirm"
),
dangerouslyUseHTMLString: true,
callback: (action) => {},
}
);
this.btnLoading = false;
}
var results = [];
this.uploadCount = this.selectArr.length;
this.uploadedCount = 0;
for (let i = 0; i < this.selectArr.length; i++) {
const index = this.selectArr[i].studyIndex;
if (!this.uploadQueues[index].uploadState.selected) {
await this.archiveStudy(index, res.OtherInfo);
}
}
this.$set(this, "uploadQueues", [
...this.uploadQueues.filter((v) => {
return (
!v.uploadState.record ||
(v.uploadState.record && !!v.uploadState.record.Failed.length)
);
}),
]);
this.$nextTick(() => {
this.selectArr = [];
this.uploadQueues.forEach((v, i) => {
if (v.uploadState.record) {
v.uploadState.selected = false;
this.$refs.dicomFilesTable.toggleRowSelection(v, true);
}
v.studyIndex = i;
});
this.$refs.pathClear.value = "";
this.btnLoading = false;
// console.log(11111);
if (this.studyErrorList.length > 0) {
let msg = this.$t(
"trials:uploadDicomFiles2:message:studyErrorList"
);
msg = msg.replaceAll("xxx", this.studyErrorList.join("、"));
this.$confirm(msg, this.$t("trials:hotkeys:message:tip"), {
confirmButtonText: this.$t(
"trials:uploadDicomFiles2:button:reupload"
),
}).then(() => {
this.beginUploadQueues();
});
}
if (this.confirmFlag) {
this.petDataTip();
}
});
})
.catch((_) => {
this.btnLoading = false;
});
},
objectToQuery() {
let obj = arguments[0];
let prefix = arguments[1];
if (typeof obj !== "object") return "";
const attrs = Object.keys(obj);
return attrs.reduce((query, attr, index) => {
// 判断是否是第一层第一个循环
if (index === 0 && !prefix) query += "?";
if (typeof obj[attr] === "object") {
const subPrefix = prefix ? `${prefix}[${attr}]` : attr;
query += this.objectToQuery(obj[attr], subPrefix);
} else {
if (prefix) {
query += `${prefix}[${attr}]=${obj[attr]}`;
} else {
query += `${attr}=${obj[attr]}`;
}
}
// 判断是否是第一层最后一个循环
if (index !== attrs.length - 1) query += "&";
return query;
}, "");
},
canvasToBlob(canvas) {
return new Promise((resolve) => {
canvas.toBlob((blob) => {
resolve(blob);
});
});
},
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 this.canvasToBlob(canvas);
resolve(blob);
} else {
resolve();
}
});
}).catch((reason) => {
reason();
});
},
// 上传影像并归档
archiveStudy(index, config) {
var scope = this;
return new Promise(function (resolve, reject) {
try {
preArchiveDicomStudy({
trialId: scope.trialId,
trialSiteId: scope.data.TrialSiteId,
subjectId: scope.subjectId,
subjectVisitId: scope.subjectVisitId,
// failedFileCount: scope.uploadQueues[index].dicomInfo.failedFileCount,
fileSize: scope.uploadQueues[index].dicomInfo.fileSize,
fileCount: scope.uploadQueues[index].dicomInfo.fileCount,
IsDicomReUpload:
scope.uploadQueues[index].uploadState.AllowReUpload,
})
.then(async (res) => {
scope.uploadQueues[index].dicomInfo.failedFileCount = 0;
scope.$set(scope.uploadQueues[index].dicomInfo, "isInit", true);
let dicomInfo = scope.uploadQueues[index].dicomInfo;
let seriesNum = scope.uploadQueues[index].seriesList.length;
let fileNum = scope.uploadQueues[index].fileList.length;
let seriesList = scope.uploadQueues[index].seriesList;
let fileList = scope.uploadQueues[index].fileList;
dicomInfo.seriesNum = seriesNum;
dicomInfo.fileNum = fileNum;
dicomInfo.trialSiteId = scope.data.TrialSiteId;
dicomInfo.subjectId = scope.data.SubjectId;
dicomInfo.subjectVisitId = scope.subjectVisitId;
let t = setInterval(() => {
dicomUploadInProgress({
trialId: scope.trialId,
studyInstanceUid: dicomInfo.studyUid,
}).then((res) => {});
}, 5000);
scope.myInterval.push(t);
let Record = {
Failed: [],
Existed: [],
Uploaded: [],
FileCount: 0,
};
let params = {
trialId: scope.trialId,
trialSiteId: scope.data.TrialSiteId,
subjectId: scope.subjectId,
subjectVisitId: scope.subjectVisitId,
studyMonitorId: res.Result,
failedFileCount: 0,
RecordPath: null,
study: {
studyId: dicomInfo.studyId,
studyInstanceUid: dicomInfo.studyUid,
studyTime: dicomInfo.studyTime,
description: dicomInfo.description,
seriesCount: dicomInfo.seriesNum,
instanceCount: dicomInfo.fileNum,
institutionName: dicomInfo.institutionName,
patientId: config.DicomStoreInfo.SubjectCode,
patientName: "",
patientAge: "",
patientSex: config.DicomStoreInfo.SubjectSex,
accessionNumber: dicomInfo.accNumber,
patientBirthDate: "",
acquisitionTime: dicomInfo.acquisitionTime,
acquisitionNumber: dicomInfo.acquisitionNumber,
triggerTime: dicomInfo.triggerTime,
bodyPartExamined: "",
seriesList: [],
},
};
let arr = [];
for (let i = 0; i < seriesList.length; i++) {
let v = seriesList[i];
let instanceList = [];
let ImageResizePath;
for (let ii = 0; ii < v.instanceList.length; ii++) {
arr.push(
(function () {
return new Promise(async (resolve1) => {
try {
let o = v.instanceList[ii];
let name = `${v.instanceList[ii].file.webkitRelativePath}_${v.instanceList[ii].instanceUid}`;
if (o.isReUpload) {
dicomInfo.failedFileCount++;
Record.Existed.push(name);
Record.FileCount++;
} else if (o.myPath) {
instanceList.push({
studyInstanceUid: dicomInfo.studyUid,
seriesInstanceUid: v.seriesUid,
sopInstanceUid: o.instanceUid,
instanceNumber: o.instanceNumber,
instanceTime: o.instanceTime,
imageRows: o.imageRows,
imageColumns: o.imageColumns,
sliceLocation: o.sliceLocation,
sliceThickness: o.sliceThickness,
numberOfFrames: o.numberOfFrames,
pixelSpacing: o.pixelSpacing,
imagerPixelSpacing: o.imagerPixelSpacing,
frameOfReferenceUID: o.frameOfReferenceUID,
windowCenter: o.windowCenter,
windowWidth: o.windowWidth,
path: o.myPath,
});
Record.Uploaded.push(name);
dicomInfo.failedFileCount++;
Record.FileCount++;
} else {
let path = `/${params.trialId}/Image/${
params.trialSiteId
}/${params.subjectId}/${params.subjectVisitId}/${
dicomInfo.studyUid
}/${scope.getGuid(
dicomInfo.studyUid +
v.seriesUid +
o.instanceUid +
params.trialId
)}`;
let res = await dcmUpload(path, o.file, config);
if (!res || !res.url) {
params.failedFileCount++;
} else {
if (ii === 0 && o.modality !== "SR") {
try {
let fileId =
cornerstoneWADOImageLoader.wadouri.fileManager.add(
o.file
);
let blob = await scope.dicomToPng(
fileId,
o.imageColumns,
o.imageRows
);
let thumbnailPath = `/${params.trialId}/Image/${params.trialSiteId}/${params.subjectId}/${params.subjectVisitId}/${dicomInfo.studyUid}/${v.seriesUid}.png`;
let OSSclient = scope.OSSclient;
let seriesRes = await OSSclient.put(
thumbnailPath,
blob
);
if (seriesRes && seriesRes.url) {
ImageResizePath = scope.$getObjectName(
seriesRes.url
);
}
} catch (e) {
console.log(e);
}
}
}
if (res && res.url) {
instanceList.push({
studyInstanceUid: dicomInfo.studyUid,
seriesInstanceUid: v.seriesUid,
sopInstanceUid: o.instanceUid,
instanceNumber: o.instanceNumber,
instanceTime: o.instanceTime,
imageRows: o.imageRows,
imageColumns: o.imageColumns,
sliceLocation: o.sliceLocation,
sliceThickness: o.sliceThickness,
numberOfFrames: o.numberOfFrames,
pixelSpacing: o.pixelSpacing,
imagerPixelSpacing: o.imagerPixelSpacing,
frameOfReferenceUID: o.frameOfReferenceUID,
windowCenter: o.windowCenter,
windowWidth: o.windowWidth,
path: scope.$getObjectName(res.url),
});
o.myPath = scope.$getObjectName(res.url);
Record.Uploaded.push(name);
dicomInfo.failedFileCount++;
Record.FileCount++;
} else {
Record.Failed.push(name);
Record.FileCount++;
}
}
resolve1();
} catch (e) {
console.log(e);
resolve1();
}
});
})()
);
if (arr.length >= 66 || ii === v.instanceList.length - 1) {
await Promise.all(arr);
arr = [];
}
}
params.study.seriesList.push({
studyInstanceUid: dicomInfo.studyUid,
seriesInstanceUid: v.seriesUid,
seriesNumber: v.seriesNumber,
seriesTime: v.seriesTime,
sliceThickness: v.sliceThickness,
imagePositionPatient: v.imagePositionPatient,
imageOrientationPatient: v.imageOrientationPatient,
sequenceName: v.sequenceName,
protocolName: v.protocolName,
imagerPixelSpacing: v.imagerPixelSpacing,
acquisitionTime: dicomInfo.acquisitionTime,
acquisitionNumber: dicomInfo.acquisitionNumber,
triggerTime: dicomInfo.triggerTime,
modality: v.modality,
description: v.description,
instanceCount: v.instanceList.length,
bodyPartExamined: dicomInfo.bodyPart,
instanceList: instanceList,
ImageResizePath: ImageResizePath,
});
}
let text = JSON.stringify(Record);
let logJsonBlob = scope.generateTxtFile(text);
let logJsonObjectName = `/${params.trialId}/Image/${params.trialSiteId}/${params.subjectId}/${params.subjectVisitId}/${dicomInfo.studyUid}/${params.studyMonitorId}.txt`;
let logRes;
try {
logRes = await scope.OSSclient.put(
logJsonObjectName,
logJsonBlob
);
} catch (e) {
scope.uploadQueues[index].uploadState.record = Record;
scope.studyErrorList.push(dicomInfo.accNumber);
clearInterval(t);
resolve();
}
for (let i = 0; i < seriesList.length; i++) {
let v = seriesList[i];
let o = params.study.seriesList.find((o) => {
return o.seriesInstanceUid === v.seriesUid;
});
if (o && !o.ImageResizePath) {
let fileId =
cornerstoneWADOImageLoader.wadouri.fileManager.add(
v.instanceList[0].file
);
let blob = await scope.dicomToPng(
fileId,
v.instanceList[0].imageColumns,
v.instanceList[0].imageRows
);
let thumbnailPath = `/${params.trialId}/Image/${params.trialSiteId}/${params.subjectId}/${params.subjectVisitId}/${dicomInfo.studyUid}/${v.seriesUid}.png`;
let OSSclient = scope.OSSclient;
try {
let seriesRes = await OSSclient.put(thumbnailPath, blob);
if (seriesRes && seriesRes.url) {
o.ImageResizePath = scope.$getObjectName(seriesRes.url);
}
} catch (e) {
console.log(e);
}
}
}
if (logRes && logRes.url) {
params.study.instanceCount = dicomInfo.failedFileCount;
params.RecordPath = scope.$getObjectName(logRes.url);
addOrUpdateArchiveStudy(params)
.then((res) => {
if (dicomInfo.failedFileCount === dicomInfo.fileCount) {
scope.$message.success(
scope.$t("trials:uploadDicomList:label:uploaded")
);
} else {
scope.studyErrorList.push(dicomInfo.accNumber);
}
scope.uploadQueues[index].uploadState.record = Record;
let flag =
res.Result === "PET-CT" ||
res.Result === "CT、PT" ||
res.Result === "PT、CT";
// let flag = params.study.seriesList.some(item=>item.modality==='PT');
if (flag && scope.relationInfo.IsHaveStudyClinicalData) {
scope.confirmFlag = true;
}
scope.getStudyInfo();
if (
scope.$route.path !==
"/trials/trials-panel/visit/crc-question"
) {
scope.$emit("getList");
}
clearInterval(t);
resolve();
})
.catch((res) => {
scope.uploadQueues[index].uploadState.record = Record;
scope.studyErrorList.push(dicomInfo.accNumber);
clearInterval(t);
resolve();
});
} else {
scope.uploadQueues[index].uploadState.record = Record;
scope.studyErrorList.push(dicomInfo.accNumber);
clearInterval(t);
resolve();
}
})
.catch(() => {
let Record = {
Failed: [],
Existed: [],
Uploaded: [],
FileCount: 0,
};
let fileList = scope.uploadQueues[index].fileList;
fileList.forEach((v) => {
Record.Failed.push(v.webkitRelativePath);
});
scope.uploadQueues[index].uploadState.record = Record;
scope.studyErrorList.push(dicomInfo.accNumber);
resolve();
});
} catch (e) {
console.log(e);
resolve();
}
});
},
generateTxtFile(text) {
let blob = new Blob(["\ufeff", text], { type: "text/plain" });
return blob;
},
// warning弹框取消按钮回调
handleCancelWarnVisible() {
this.warning_cfg.visible = false;
for (var i = 0; i < this.selectArr.length; ++i) {
const index = this.selectArr[i].studyIndex;
this.uploadQueues[index].uploadState.stateCode = "";
}
},
// 忽略warning继续上传
handleContinueUpload() {
this.warning_cfg.visible = false;
this.verifyStudy();
},
// 预览影像
handlePreview(uid) {
this.previewVisible = true;
this.uid = uid;
},
// 删除影像列表中的某个影像
handleDelete(index, row) {
this.$confirm(this.$t("trials:uploadedDicoms:message:deleteMes"), {
type: "warning",
distinguishCancelAndClose: true,
}).then(() => {
this.$refs.uploadForm.reset();
this.uploadQueues.splice(index, 1);
this.uploadQueues.forEach((v, i) => {
if (v.uploadState.record) {
v.uploadState.selected = false;
this.$refs.dicomFilesTable.toggleRowSelection(v, true);
}
v.studyIndex = i;
});
this.handleSelectionChange(this.uploadQueues);
});
},
// 当选择项发生变化时收集待上传文件数据
handleSelectionChange(selection) {
selection.sort((n1, n2) => {
return n1.studyIndex - n2.studyIndex;
});
this.selectArr = selection;
},
// 根据是否上传状态决定CheckBox 是否可以勾选
handleSelectable(row) {
if (row.uploadState.selected || this.btnLoading) {
return false;
} else {
return true;
}
},
// 收集已上传文件列表勾选项
handleUploadedSelectionChange(selection) {
this.deleteArr = [];
selection.forEach((item) => {
this.deleteArr.push(item.StudyId);
});
},
// 批量删除已上传的影像
handleBatchDelete() {
this.$confirm(this.$t("trials:uploadedDicoms:message:deleteMes"), {
type: "warning",
distinguishCancelAndClose: true,
})
.then(() => {
this.studyLoading = true;
deleteStudyList(this.trialId, this.subjectVisitId, this.deleteArr)
.then((res) => {
if (res.IsSuccess) {
this.getStudyInfo();
if (
this.$route.path !== "/trials/trials-panel/visit/crc-question"
) {
this.$emit("getList");
}
this.$message.success(
this.$t("trials:uploadedDicoms:message:deleteSuccessfully")
);
}
})
.catch(() => {
this.studyLoading = true;
});
})
.catch(() => {});
},
// 删除已上传的某个检查
handleDeleteStudy(row) {
this.$confirm(this.$t("trials:uploadedDicoms:message:deleteMes"), {
type: "warning",
distinguishCancelAndClose: true,
})
.then(() => {
this.studyLoading = true;
deleteStudyList(this.trialId, this.subjectVisitId, [row.StudyId])
.then((res) => {
if (res.IsSuccess) {
this.getStudyInfo();
if (this.uploadActiveName === "pacs") {
this.$refs.dicomPacs.getList();
}
if (
this.$route.path !== "/trials/trials-panel/visit/crc-question"
) {
this.$emit("getList");
}
this.$message.success(
this.$t("trials:uploadedDicoms:message:deleteSuccessfully")
);
}
})
.catch(() => {
this.studyLoading = true;
});
})
.catch(() => {});
},
// cancel按钮回调
cancel() {
this.scanState = "cancelling";
// 关闭上传模态框
this.$emit("close");
},
compare(start, end) {
start = new Date(start);
start = start.getTime();
end = new Date(end);
end = end.getTime();
var time = 0;
if (start > end) {
time = start - end;
} else {
time = end - start;
}
return Math.floor(time / 86400000);
},
exportFailedFiles(errorFiles) {
var elementA = document.createElement("a");
var str = "";
errorFiles.forEach((el) => {
str += el + "\n";
});
elementA.setAttribute("href", "data:text/html;charset=utf-8," + str);
elementA.setAttribute("download", +new Date() + ".txt");
elementA.style.display = "none";
document.body.appendChild(elementA);
elementA.click();
document.body.removeChild(elementA);
},
// 设置已删除序列行样式
tableRowClassName({ row, rowIndex }) {
if (row.IsDeleted) {
return "delete-row";
} else {
return "";
}
},
getBodyPart(bodyPart) {
if (!bodyPart) return "";
var separator = ",";
if (bodyPart.indexOf("|") > -1) {
separator = "|";
} else if (bodyPart.indexOf(",") > -1) {
separator = ",";
} else if (bodyPart.indexOf("") > -1) {
separator = "";
}
var arr = bodyPart.split(separator);
var newArr = arr.map((i) => {
return this.$fd("Bodypart", i.trim(), "Code", this.BodyPart, "Name");
});
return newArr.join(" | ");
},
},
};
</script>
<style lang="scss">
.upload-dicom-files-wrapper {
.dicomFiles-table {
.el-table__body-wrapper {
height: 300px;
overflow-y: auto;
}
}
.delete-row {
text-decoration-line: line-through;
color: #c0c4cc;
}
.previewActive:hover {
cursor: pointer;
color: #428bca;
}
#inputForm label {
font-weight: normal;
}
#inputForm .file-input {
position: relative;
overflow: hidden;
display: inline-block;
}
#inputForm .file-input input[type="file"] {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
cursor: pointer;
}
#inputForm #listWrapper {
margin-top: 10px;
margin-bottom: 50px;
}
#inputForm #listWrapper a {
text-decoration: none;
}
#inputForm .text-left {
text-align: left;
}
.colorOfRed {
color: #f44336;
}
}
</style>