irc_web/.svn/pristine/a3/a3ec382e4f39e8125fed89b8fa9...

1211 lines
44 KiB
Plaintext
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影像记录 -->
<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
/>
<!-- 检查类型 -->
<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="100" 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-edit-outline"
v-hasPermi="['trials:trials-panel:visit:crc-upload:upload']"
: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)"
/>
</template>
</el-table-column>
</el-table>
<!-- 多文件上传 -->
<form id="inputForm" ref="uploadForm" enctype="multipart/form-data">
<el-divider content-position="left">{{ $t('trials:uploadedDicoms:title:dicomFilesOnly') }}</el-divider>
<div class="form-group">
<div id="directoryInputWrapper" class="btn btn-link file-input">
<el-button type="primary" size="small">{{ $t('trials:uploadedDicomsicom:button:selectFolder') }}</el-button>
<input
type="file"
name="file"
webkitdirectory
multiple
@change="beginScanFiles($event)"
>
</div>
</div>
</form>
<!-- 文件列表 -->
<el-table
ref="dicomFilesTable"
:data="uploadQueues"
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:status')" 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:stats')"
min-width="140"
show-overflow-tooltip
>
<template slot-scope="scope">
</template>
</el-table-column>
<el-table-column v-show="isScan" :label="$t('common:action:action')" width="200">
<template slot-scope="scope">
<!-- 预览 -->
<el-button
icon="el-icon-view"
circle
: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 !== ''"
@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 || uploadCount !== uploadedCount"
@click="beginUploadQueues"
>
{{ $t('trials:uploadDicomList:button:upload') }}
</el-button>
</div>
<!-- 预览影像模态框 -->
<el-dialog
v-if="previewVisible"
:fullscreen="true"
:show-close="true"
:visible.sync="previewVisible"
append-to-body
>
<DicomPreview :uid="uid" />
</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)}}</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 {
batchVerifyStudyAllowUpload, getSubjectVisitUploadedStudyList, deleteStudyList, updateModality, preArchiveDicomStudy, addOrUpdateArchiveStudy
} from '@/api/trials'
import store from '@/store'
import * as dicomParser from 'dicom-parser'
import * as signalR from '@microsoft/signalr'
import DicomPreview from '@/views/dicom-show/dicom-preview'
import dicomStore from '@/utils/dicom-store'
import { getToken } from '@/utils/auth'
import {dcmUpload} from '@/utils/dcmUpload/dcmUpload'
import moment from 'moment'
export default {
name: 'UploadDicomFiles',
components: { DicomPreview },
props: {
data: {
type: Object,
default() {
return {}
}
},
subjectVisitId: {
type: String,
required: true
},
subjectId: {
type: String,
required: true
}
},
data() {
return {
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: [],
}
},
mounted() {
this.trialId = this.$route.query.trialId
if (Object.keys(this.data).length) {
this.form = { ...this.data }
}
this.getStudyInfo()
},
methods: {
// 打开检查信息编辑框
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.data.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
}).catch(() => { this.studyLoading = 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')
},
// 预览所有影像
handlePreviewAllFiles() {
var token = getToken()
const routeData = this.$router.resolve({
path: `/showvisitdicoms?trialId=${this.data.TrialId}&visitInfo=${this.data.VisitName}(${this.data.VisitNum})&subjectVisitId=${this.data.Id}&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()
})
for (var i = 0; i < files.length; ++i) {
(function(index) {
p = p.then(function() {
return scope.parseDicomFile(files[index])
})
})(i)
}
p.then(function(result) {
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 studyUid = data.string('x0020000d')
var studyIndex = 0
while (
studyIndex < scope.uploadQueues.length &&
scope.uploadQueues[studyIndex].dicomInfo.studyUid !== studyUid
) {
++studyIndex
}
if (studyIndex >= scope.uploadQueues.length) {
var date = data.string('x00080020')
var time = data.string('x00080030')
var studyTime = ''
if (date) {
// date = `${date.substr(0, 4)}-${date.substr(4, 2)}-${date.substr(6, 2)}`
date = `${date.substring(0, 4)}-${date.substring(4, 6)}-${date.substring(6, 8)}`
}
if (time) {
// time = `${time.substr(0, 2)}:${time.substr(2, 2)}:${time.substr(4, 2)}`
time = `${time.substring(0, 2)}:${time.substring(2, 4)}:${time.substring(4, 6)}`
}
if(date){
studyTime = time ? `${date} ${time}` : `${date} 00:00:00`
}
scope.uploadQueues.push({
studyIndex: studyIndex,
seriesList: [],
fileList: [],
dicomInfo: {
studyId: data.string('x00200010'),
studyUid: studyUid,
patientId: data.string('x00100020'),
patientName: data.string('x00100010') ? data.string('x00100010') : '',
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
},
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
console.log(fileList)
var instanceUid = data.string('x00080018')
var instanceIndex = 0
while (
instanceIndex < fileList.length &&
fileList[instanceIndex].instanceUid !== instanceUid
) {
++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) {
var date = data.string('x00080021')
var time = data.string('x00080031')
var seriesTime = ''
if (date) {
// date = `${date.substr(0, 4)}-${date.substr(4, 2)}-${date.substr(6, 2)}`
date = `${date.substring(0, 4)}-${date.substring(4, 6)}-${date.substring(6, 8)}`
}
if (time) {
// time = `${time.substr(0, 2)}:${time.substr(2, 2)}:${time.substr(4, 2)}`
time = `${time.substring(0, 2)}:${time.substring(2, 4)}:${time.substring(4, 6)}`
}
if(date){
seriesTime = time ? `${date} ${time}` : `${date} 00:00:00`
}
seriesItem = {
seriesUid: seriesUid,
seriesNumber: data.intString('x00200011') || 1,
modality: data.string('x00080060') || '',
description: data.string('x0008103e') || '',
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.substr(0, 4)}-${date.substr(4, 2)}-${date.substr(6, 2)}`
date = `${date.substring(0, 4)}-${date.substring(4, 6)}-${date.substring(6, 8)}`
}
if (time) {
// time = `${time.substr(0, 2)}:${time.substr(2, 2)}:${time.substr(4, 2)}`
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.intString('x00280010') || 0,
imageColumns: data.intString('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') || '',
file: file
}
instanceList.push(instanceItem)
}
resolve()
} catch (error) {
// scope.$message.error(scope.$t('trials:trials-panel:message:notIsHasDicom'))
console.log(error)
resolve()
}
}
reader.onerror = function(e) {
resolve()
}
reader.readAsArrayBuffer(file)
})
},
// 上传之前校验基本信息及检查是否已上传
beginUploadQueues() {
this.scanState = 'uploading'
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
} 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 => {
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((res) => {
var messageArr = []
res.Result.forEach((item) => {
// 不能上传或不能重传时,收集校验结果提示,并取消勾选,设置上传状态
if (item.ErrorMesseage) {
const i = this.uploadQueues.findIndex((value) => value.dicomInfo.studyUid === item.StudyInstanceUid)
// Uploading not allowed
this.uploadQueues[i].uploadState.stateText = this.$t('trials:uploadDicomList:label:uploadingNotAllowed')
this.uploadQueues[i].uploadState.selected = true
this.uploadQueues[i].uploadState.stateCode = 'Uploading not allowed'
this.uploadQueues[i].uploadState.errorMessage = item.ErrorMesseage
this.$refs.dicomFilesTable.toggleRowSelection(this.uploadQueues[i])
// const msg = `ACC:${this.uploadQueues[i].dicomInfo.accNumber}, ${item.ErrorMesseage}`
const msg = `${item.ErrorMesseage}`
messageArr.push(msg)
}
// 可以重传时记录需要被覆盖的检查ID
if (item.AllowReUpload) {
const i = this.uploadQueues.findIndex((value) => value.dicomInfo.studyUid === item.StudyInstanceUid)
this.uploadQueues[i].AbandonStudyId = item.StudyInfo ? item.StudyInfo.Id : ''
}
})
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
results.push(this.archiveStudy(index))
}
Promise.all(results).then(_ => {
this.btnLoading = false
}).catch(_ => {
this.btnLoading = false
})
}).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
}, '')
},
// 上传影像并归档
archiveStudy(index) {
var scope = this
console.log(scope.uploadQueues[index])
return new Promise(function(resolve, reject) {
preArchiveDicomStudy({
trialId: scope.trialId,
siteId: scope.data.SiteId,
subjectId: scope.subjectId,
subjectVisitId: scope.subjectVisitId,
failedFileCount: scope.uploadQueues[index].dicomInfo.failedFileCount,
fileSize: scope.uploadQueues[index].dicomInfo.fileSize,
fileCount: scope.uploadQueues[index].dicomInfo.fileCount
}).then(async res => {
var dicomInfo = scope.uploadQueues[index].dicomInfo
var seriesNum = scope.uploadQueues[index].seriesList.length
var fileNum = scope.uploadQueues[index].fileList.length
var seriesList = scope.uploadQueues[index].seriesList
var fileList = scope.uploadQueues[index].fileList
dicomInfo.seriesNum = seriesNum
dicomInfo.fileNum = fileNum
dicomInfo.siteId = scope.data.SiteId
dicomInfo.subjectId = scope.data.SubjectId
dicomInfo.subjectVisitId = scope.subjectVisitId
let params = {
trialId: scope.trialId,
siteId: scope.data.SiteId,
subjectId: scope.subjectId,
subjectVisitId: scope.subjectVisitId,
studyMonitorId: res.Result,
isAdd: true,
study: {
studyId: dicomInfo.studyId,
studyInstanceUid: dicomInfo.studyUid,
studyTime: dicomInfo.studyTime,
description: dicomInfo.description,
seriesCount: dicomInfo.seriesNum,
instanceCount: dicomInfo.fileNum,
institutionName: dicomInfo.institutionName,
patientId: dicomInfo.patientId,
patientName: dicomInfo.patientName,
patientAge: dicomInfo.patientAge,
patientSex: dicomInfo.patientSex,
accessionNumber: dicomInfo.accNumber,
patientBirthDate: dicomInfo.patientBirthDate,
acquisitionTime: dicomInfo.acquisitionTime,
acquisitionNumber: dicomInfo.acquisitionNumber,
triggerTime: dicomInfo.triggerTime,
bodyPartExamined: dicomInfo.bodyPart,
seriesList: [],
}
}
for (let i = 0; i < seriesList.length; i++) {
let v = seriesList[i]
let instanceList = []
for (let ii = 0; ii < v.instanceList.length; ii++) {
let o = v.instanceList[ii]
let path = `/dcmFile/${params.trialId}/${params.siteId}/${params.subjectId}/${params.subjectVisitId}/${dicomInfo.studyUid}/${v.seriesUid}/${o.instanceUid}`
let res = await dcmUpload(path, o.file)
dicomInfo.failedFileCount++
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: path
})
}
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
})
}
addOrUpdateArchiveStudy(params).then(res => {
scope.$message.success('上传成功')
scope.getStudyInfo()
scope.$emit('getList')
}).catch((res) => {
console.log(res)
})
scope.btnLoading = true
})
})
},
// 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.selectArr = []
this.uploadQueues.splice(index, 1)
})
},
// 当选择项发生变化时收集待上传文件数据
handleSelectionChange(selection) {
selection.sort((n1, n2) => {
return n1.studyIndex - n2.studyIndex
})
this.selectArr = selection
},
// 根据是否上传状态决定CheckBox 是否可以勾选
handleSelectable(row) {
if (row.uploadState.selected) {
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()
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()
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())
})
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: #33dac1;
}
#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>