影像替换与补充

temp
wangxiaoshuang 2026-08-21 11:34:52 +08:00
parent cb2fed1ad2
commit 1ca4fb7dd4
17 changed files with 832 additions and 459 deletions

View File

@ -488,3 +488,24 @@ export function getFilterTableQuestion(data) {
data data
}) })
} }
export function deleteImageCache(params) {
return request({
url: `/Study/deleteImageCache`,
method: 'delete',
params
})
}
export function getAnonymizeInfo(params) {
return request({
url: `/Study/getAnonymizeInfo`,
method: 'get',
params
})
}
export function updateModality(params) {
return request({
url: `/Study/updateModality`,
method: 'put',
params
})
}

View File

@ -213,6 +213,10 @@ export default {
}, },
methods: { methods: {
getInfo() {
var image = cornerstone.getImage(this.canvas)
return image
},
loadImageStack(dicomSeries, text = '') { loadImageStack(dicomSeries, text = '') {
this.tip = text this.tip = text
this.$nextTick(() => { this.$nextTick(() => {

View File

@ -296,6 +296,13 @@
<option v-for="(item, index) in colormapsList" :key="index" :value="item.id">{{ item.name }}</option> <option v-for="(item, index) in colormapsList" :key="index" :value="item.id">{{ item.name }}</option>
</select> </select>
</div> </div>
<div class="sideTool-wrapper">
<label for="Replacement" style="display: inline-block;color: #fff;line-height: 20px;cursor: pointer;">
替换
</label>
<input type="file" id="Replacement" @change="beginScanFiles($event, 'replace')" style="display: none;">
</div>
</div> </div>
<!-- 患者信息 --> <!-- 患者信息 -->
@ -382,6 +389,7 @@ import {
editPatientInfo editPatientInfo
} from '@/api/trials' } from '@/api/trials'
import { setPTClinicalDataForInstance, clearPTClinicalDataCache } from '@/utils/ptClinicalDataCache' import { setPTClinicalDataForInstance, clearPTClinicalDataCache } from '@/utils/ptClinicalDataCache'
import { changeFile } from "@/views/trials/trials-panel/reading/dicoms/components/upload.js"
export default { export default {
name: 'DicomsViewer', name: 'DicomsViewer',
components: { components: {
@ -400,6 +408,14 @@ export default {
modality: { modality: {
type: String, type: String,
default: '' default: ''
},
SeriesList: {
type: Array,
default: () => []
},
currentSeriesIndex: {
type: Number,
default: -1
} }
}, },
watch: { watch: {
@ -483,7 +499,9 @@ export default {
}, },
formLoading: false, formLoading: false,
type: '', type: '',
isEdit: 0 isEdit: 0,
fileKey: null,
file: null,
} }
}, },
computed: { computed: {
@ -511,6 +529,19 @@ export default {
}, },
methods: { methods: {
beginScanFiles(e, key) {
this.fileKey = key
this.file = e.target.files[0]
let image = this.$refs[this.activeItem].getInfo()
let study = {
seriesList: this.SeriesList,
currentSeriesIndex: this.currentSeriesIndex
}
this.$emit("update:loading", true)
changeFile('CRC', this.file, study, image)
// DicomEvent.$emit('getStudyFile')
},
setToolsPassive() { setToolsPassive() {
const elements = document.querySelectorAll('.dicom-item') const elements = document.querySelectorAll('.dicom-item')
const scope = this const scope = this

View File

@ -61,7 +61,8 @@
scope.row.UploadStudyList.length <= 0 scope.row.UploadStudyList.length <= 0
" @click.stop="handleViewReadingImages(scope.row)" :title="$t('upload:dicom:button:preview')" /> " @click.stop="handleViewReadingImages(scope.row)" :title="$t('upload:dicom:button:preview')" />
<!--删除---> <!--删除--->
<el-button circle icon="el-icon-delete" :title="$t('upload:dicom:button:delete')" @click.stop="remove(scope.row)" /> <el-button circle icon="el-icon-delete" :title="$t('upload:dicom:button:delete')"
@click.stop="remove(scope.row)" />
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
@ -380,6 +381,10 @@ export default {
isReading: { isReading: {
type: Boolean, type: Boolean,
default: false, default: false,
},
StudyInstanceUID: {
type: String,
default: '',
} }
}, },
components: { components: {
@ -1081,11 +1086,12 @@ export default {
async verifyStudy() { async verifyStudy() {
this.btnLoading = true this.btnLoading = true
var studyList = [] var studyList = []
let scope = this
this.selectArr.forEach((item) => { this.selectArr.forEach((item) => {
item.dicomInfo.uploadFileSize = 0 item.dicomInfo.uploadFileSize = 0
if (!item.uploadState.selected) { if (!item.uploadState.selected) {
studyList.push({ studyList.push({
studyInstanceUid: item.dicomInfo.studyUid, studyInstanceUid: scope.isReading && scope.StudyInstanceUID ? scope.StudyInstanceUID : item.dicomInfo.studyUid,
studyDate: item.dicomInfo.studyTime, studyDate: item.dicomInfo.studyTime,
}) })
} }
@ -1180,7 +1186,7 @@ export default {
let t = setInterval(() => { let t = setInterval(() => {
dicomUploadInProgress({ dicomUploadInProgress({
trialId: scope.trialId, trialId: scope.trialId,
studyInstanceUid: dicomInfo.studyUid, studyInstanceUid: scope.isReading && scope.StudyInstanceUID ? scope.StudyInstanceUID : dicomInfo.studyUid,
VisitTaskId: dicomInfo.visitTaskId, VisitTaskId: dicomInfo.visitTaskId,
}).then((res) => { }).then((res) => {
console.log(dicomInfo.visitTaskId) console.log(dicomInfo.visitTaskId)
@ -1207,7 +1213,7 @@ export default {
dicomInfo.RadiopharmaceuticalStartTime, dicomInfo.RadiopharmaceuticalStartTime,
studyId: dicomInfo.studyId, studyId: dicomInfo.studyId,
studyInstanceUid: dicomInfo.studyUid, studyInstanceUid: scope.isReading && scope.StudyInstanceUID ? scope.StudyInstanceUID : dicomInfo.studyUid,
studyTime: dicomInfo.studyTime, studyTime: dicomInfo.studyTime,
description: dicomInfo.description, description: dicomInfo.description,
seriesCount: dicomInfo.seriesNum, seriesCount: dicomInfo.seriesNum,
@ -1302,146 +1308,157 @@ export default {
// Record.FileCount++ // Record.FileCount++
// } // }
// else { // else {
let path = `/${params.trialId}/Image/${params.subjectId let path = `/${params.trialId}/Image/${params.subjectId
}/${params.subjectVisitId}/${dicomInfo.visitTaskId }/${params.subjectVisitId}/${dicomInfo.visitTaskId
}/${scope.getGuid(
dicomInfo.studyUid +
v.seriesUid +
o.instanceUid +
params.trialId
)}`
if (scope.IsImageSegment) {
path = `/${params.trialId}/Image/${params.subjectId
}/${params.subjectVisitId}/AnnotationImage/${dicomInfo.visitTaskId
}/${scope.getGuid( }/${scope.getGuid(
dicomInfo.studyUid + dicomInfo.studyUid +
v.seriesUid + v.seriesUid +
o.instanceUid + o.instanceUid +
params.trialId params.trialId
)}` )}`
if (scope.IsImageSegment) { }
path = `/${params.trialId}/Image/${params.subjectId if (scope.isClose) return
}/${params.subjectVisitId}/AnnotationImage/${dicomInfo.visitTaskId let res = await dcmUpload(
}/${scope.getGuid( {
dicomInfo.studyUid + path: path,
v.seriesUid + file: o.file,
o.instanceUid + speed: true,
params.trialId },
)}` scope.isReading && scope.StudyInstanceUID ? {
} AnonymizeFixedList: [
if (scope.isClose) return {
let res = await dcmUpload( Element: '000D',
{ Group: '0020',
path: path, ReplaceValue: scope.StudyInstanceUID,
file: o.file, Id: 'StudyInstanceUID'
speed: true,
},
null,
(percentage, checkpoint, lastPer) => {
dicomInfo.uploadFileSize +=
checkpoint.size * (percentage - lastPer)
if (
dicomInfo.uploadFileSize > dicomInfo.fileSize
) {
dicomInfo.uploadFileSize = dicomInfo.fileSize
} }
}, ],
{ AnonymizeNotFixedList: [],
fileName: o.file.name, DicomStoreInfo: {}
fileSize: o.file.size, } : null,
fileType: 'application/dicom', (percentage, checkpoint, lastPer) => {
uploadBatchId: uploadBatchId, dicomInfo.uploadFileSize +=
batchDataType: 5, checkpoint.size * (percentage - lastPer)
trialId: params.trialId, if (
subjectId: params.subjectId, dicomInfo.uploadFileSize > dicomInfo.fileSize
subjectVisitId: params.subjectVisitId, ) {
dicomInfo.uploadFileSize = dicomInfo.fileSize
} }
) },
if (!res || !res.url) { {
params.failedFileCount++ fileName: o.file.name,
} else { fileSize: o.file.size,
if (ii === 0 && o.modality !== 'SR') { fileType: 'application/dicom',
try { uploadBatchId: uploadBatchId,
let fileId = batchDataType: 5,
cornerstoneWADOImageLoader.wadouri.fileManager.add( trialId: params.trialId,
o.file subjectId: params.subjectId,
) subjectVisitId: params.subjectVisitId,
let blob = await scope.dicomToPng( }
fileId, )
o.imageColumns, if (!res || !res.url) {
o.imageRows params.failedFileCount++
} else {
if (ii === 0 && o.modality !== 'SR') {
try {
let fileId =
cornerstoneWADOImageLoader.wadouri.fileManager.add(
o.file
) )
let thumbnailPath = `/${params.trialId}/Image/${params.subjectId}/${params.subjectVisitId}/${dicomInfo.visitTaskId}/${dicomInfo.studyUid}/${v.seriesUid}.jpg` let blob = await scope.dicomToPng(
if (scope.IsImageSegment) { fileId,
thumbnailPath = `/${params.trialId}/Image/${params.subjectId}/${params.subjectVisitId}/AnnotationImage/${dicomInfo.visitTaskId}/${dicomInfo.studyUid}/${v.seriesUid}.jpg` o.imageColumns,
} o.imageRows
let OSSclient = scope.OSSclient )
let seriesRes = await OSSclient.put( let thumbnailPath = `/${params.trialId}/Image/${params.subjectId}/${params.subjectVisitId}/${dicomInfo.visitTaskId}/${dicomInfo.studyUid}/${v.seriesUid}.jpg`
thumbnailPath, if (scope.IsImageSegment) {
blob, thumbnailPath = `/${params.trialId}/Image/${params.subjectId}/${params.subjectVisitId}/AnnotationImage/${dicomInfo.visitTaskId}/${dicomInfo.studyUid}/${v.seriesUid}.jpg`
{
fileName: `${v.seriesUid}.jpg`,
fileSize: blob.size,
fileType: 'image/jpeg',
uploadBatchId: uploadBatchId,
batchDataType: 6,
trialId: params.trialId,
subjectId: params.subjectId,
subjectVisitId: params.subjectVisitId,
}
)
if (seriesRes && seriesRes.url) {
ImageResizePath = scope.$getObjectName(
seriesRes.url
)
}
} catch (e) {
console.log(e)
} }
let OSSclient = scope.OSSclient
let seriesRes = await OSSclient.put(
thumbnailPath,
blob,
{
fileName: `${v.seriesUid}.jpg`,
fileSize: blob.size,
fileType: 'image/jpeg',
uploadBatchId: uploadBatchId,
batchDataType: 6,
trialId: params.trialId,
subjectId: params.subjectId,
subjectVisitId: params.subjectVisitId,
}
)
if (seriesRes && seriesRes.url) {
ImageResizePath = scope.$getObjectName(
seriesRes.url
)
}
} catch (e) {
console.log(e)
} }
} }
if (res && res.url) { }
instanceList.push({ if (res && res.url) {
studyInstanceUid: dicomInfo.studyUid, instanceList.push({
seriesInstanceUid: v.seriesUid, studyInstanceUid: scope.isReading && scope.StudyInstanceUID ? scope.StudyInstanceUID : dicomInfo.studyUid,
sopInstanceUid: o.instanceUid, seriesInstanceUid: v.seriesUid,
SOPClassUID: o.SOPClassUID, sopInstanceUid: o.instanceUid,
TransferSytaxUID: o.TransferSytaxUID, SOPClassUID: o.SOPClassUID,
MediaStorageSOPInstanceUID: TransferSytaxUID: o.TransferSytaxUID,
o.MediaStorageSOPInstanceUID, MediaStorageSOPInstanceUID:
MediaStorageSOPClassUID: o.MediaStorageSOPInstanceUID,
o.MediaStorageSOPClassUID, MediaStorageSOPClassUID:
instanceNumber: o.instanceNumber, o.MediaStorageSOPClassUID,
instanceTime: o.instanceTime, instanceNumber: o.instanceNumber,
imageRows: o.imageRows, instanceTime: o.instanceTime,
imageColumns: o.imageColumns, imageRows: o.imageRows,
sliceLocation: o.sliceLocation, imageColumns: o.imageColumns,
sliceThickness: o.sliceThickness, sliceLocation: o.sliceLocation,
numberOfFrames: o.numberOfFrames, sliceThickness: o.sliceThickness,
pixelSpacing: o.pixelSpacing, numberOfFrames: o.numberOfFrames,
imagerPixelSpacing: o.imagerPixelSpacing, pixelSpacing: o.pixelSpacing,
frameOfReferenceUID: o.frameOfReferenceUID, imagerPixelSpacing: o.imagerPixelSpacing,
windowCenter: o.windowCenter, frameOfReferenceUID: o.frameOfReferenceUID,
windowWidth: o.windowWidth, windowCenter: o.windowCenter,
path: scope.$getObjectName(res.url), windowWidth: o.windowWidth,
FileSize: o.FileSize, path: scope.$getObjectName(res.url),
FileSize: o.FileSize,
PhotometricInterpretation: PhotometricInterpretation:
o.PhotometricInterpretation, o.PhotometricInterpretation,
BitsAllocated: o.BitsAllocated, BitsAllocated: o.BitsAllocated,
PixelRepresentation: o.PixelRepresentation, PixelRepresentation: o.PixelRepresentation,
RescaleIntercept: o.RescaleIntercept, RescaleIntercept: o.RescaleIntercept,
RescaleSlope: o.RescaleSlope, RescaleSlope: o.RescaleSlope,
ImagePositionPatient: o.ImagePositionPatient, ImagePositionPatient: o.ImagePositionPatient,
ImageOrientationPatient: ImageOrientationPatient:
o.ImageOrientationPatient, o.ImageOrientationPatient,
SequenceOfUltrasoundRegions: SequenceOfUltrasoundRegions:
o.SequenceOfUltrasoundRegions, o.SequenceOfUltrasoundRegions,
FrameTime: o.FrameTime, FrameTime: o.FrameTime,
CorrectedImage: o.CorrectedImage, CorrectedImage: o.CorrectedImage,
Units: o.Units, Units: o.Units,
DecayCorrection: o.DecayCorrection, DecayCorrection: o.DecayCorrection,
EncapsulatedDocument: o.EncapsulatedDocument, EncapsulatedDocument: o.EncapsulatedDocument,
}) })
o.myPath = scope.$getObjectName(res.url) o.myPath = scope.$getObjectName(res.url)
Record.Uploaded.push(name) Record.Uploaded.push(name)
dicomInfo.failedFileCount++ dicomInfo.failedFileCount++
Record.FileCount++ Record.FileCount++
} else { } else {
Record.Failed.push(name) Record.Failed.push(name)
Record.FileCount++ Record.FileCount++
} }
// } // }
resolve1() resolve1()
} catch (e) { } catch (e) {
@ -1460,7 +1477,7 @@ export default {
} }
} }
params.study.seriesList.push({ params.study.seriesList.push({
studyInstanceUid: dicomInfo.studyUid, studyInstanceUid: scope.isReading && scope.StudyInstanceUID ? scope.StudyInstanceUID : dicomInfo.studyUid,
seriesInstanceUid: v.seriesUid, seriesInstanceUid: v.seriesUid,
seriesNumber: v.seriesNumber, seriesNumber: v.seriesNumber,
seriesTime: v.seriesTime, seriesTime: v.seriesTime,

View File

@ -7,7 +7,7 @@
<dicomFile v-if="activeName === 'dicom'" :SubjectId="SubjectId" :SubjectCode="SubjectCode" <dicomFile v-if="activeName === 'dicom'" :SubjectId="SubjectId" :SubjectCode="SubjectCode"
:Criterion="Criterion" :TaskId="VisitTaskId" :isUpload.sync="isUpload" :Criterion="Criterion" :TaskId="VisitTaskId" :isUpload.sync="isUpload"
:isReadingTaskViewInOrder="isReadingTaskViewInOrder" :IsImageSegment="IsImageSegment" :forbid="forbid" :isReadingTaskViewInOrder="isReadingTaskViewInOrder" :IsImageSegment="IsImageSegment" :forbid="forbid"
:isReading="isReading" /> :isReading="isReading" :StudyInstanceUID="StudyInstanceUID" />
</el-tab-pane> </el-tab-pane>
<el-tab-pane :label="$t('uploadDicomAndNonedicom:label:nonedicom')" name="nonedicom"> <el-tab-pane :label="$t('uploadDicomAndNonedicom:label:nonedicom')" name="nonedicom">
<nonedicomFile v-if="activeName === 'nonedicom'" :SubjectId="SubjectId" :SubjectCode="SubjectCode" <nonedicomFile v-if="activeName === 'nonedicom'" :SubjectId="SubjectId" :SubjectCode="SubjectCode"
@ -63,6 +63,10 @@ export default {
isReading: { isReading: {
type: Boolean, type: Boolean,
default: false, default: false,
},
StudyInstanceUID: {
type: String,
default: '',
} }
}, },
data() { data() {

View File

@ -6,6 +6,7 @@ export const anonymization = function (file, config) {
try { try {
const reader = new FileReader() const reader = new FileReader()
let AnonymizeFixedList = config.AnonymizeFixedList let AnonymizeFixedList = config.AnonymizeFixedList
console.log(AnonymizeFixedList, 'AnonymizeFixedList')
let AnonymizeNotFixedList = config.AnonymizeNotFixedList let AnonymizeNotFixedList = config.AnonymizeNotFixedList
let DicomStoreInfo = config.DicomStoreInfo let DicomStoreInfo = config.DicomStoreInfo
reader.onload = async (event) => { reader.onload = async (event) => {
@ -15,9 +16,11 @@ export const anonymization = function (file, config) {
let dataset = dcmjs.data.DicomMessage.readFile(buffer) let dataset = dcmjs.data.DicomMessage.readFile(buffer)
for (var i = 0; i < AnonymizeFixedList.length; i++) { for (var i = 0; i < AnonymizeFixedList.length; i++) {
let AnonymizeFixed = AnonymizeFixedList[i] let AnonymizeFixed = AnonymizeFixedList[i]
if (!dataset.dict.hasOwnProperty(`${AnonymizeFixed.Group + AnonymizeFixed.Element}`)) continue if (!dataset.dict.hasOwnProperty(`${AnonymizeFixed.Group + AnonymizeFixed.Element}`) && !dataset.meta.hasOwnProperty(`${AnonymizeFixed.Group + AnonymizeFixed.Element}`)) continue
if (dataset.dict[AnonymizeFixed.Group + AnonymizeFixed.Element]) { if (dataset.dict[AnonymizeFixed.Group + AnonymizeFixed.Element]) {
dataset.dict[AnonymizeFixed.Group + AnonymizeFixed.Element].Value[0] = AnonymizeFixed.ReplaceValue dataset.dict[AnonymizeFixed.Group + AnonymizeFixed.Element].Value[0] = AnonymizeFixed.ReplaceValue
} else if (dataset.meta[AnonymizeFixed.Group + AnonymizeFixed.Element]) {
dataset.meta[AnonymizeFixed.Group + AnonymizeFixed.Element].Value[0] = AnonymizeFixed.ReplaceValue
} else { } else {
dataset.dict[AnonymizeFixed.Group + AnonymizeFixed.Element] = { dataset.dict[AnonymizeFixed.Group + AnonymizeFixed.Element] = {
vr: AnonymizeFixed.ValueRepresentation, vr: AnonymizeFixed.ValueRepresentation,
@ -60,7 +63,7 @@ export const anonymization = function (file, config) {
let newDicomFile = dataset.write() // fragmentMultiframe 原始数据是否进行分割 let newDicomFile = dataset.write() // fragmentMultiframe 原始数据是否进行分割
const bufferArray = new Uint8Array(newDicomFile) const bufferArray = new Uint8Array(newDicomFile)
const blob = new Blob([bufferArray], { type: 'application/octet-stream' }) const blob = new Blob([bufferArray], { type: 'application/octet-stream' })
resolve({ blob, pixelDataElement }) resolve({ blob, pixelDataElement, Modality: dataset.dict['00080060'].Value[0] })
} catch (err) { } catch (err) {
console.log(file, 'warning') console.log(file, 'warning')
console.log(err) console.log(err)

View File

@ -12,7 +12,8 @@ export const dcmUpload = async function (data, config, progressFn, fileInfo) {
let res = await Vue.prototype.OSSclient.multipartUpload(Object.assign(data, { file: blob.blob }), progressFn, fileInfo) let res = await Vue.prototype.OSSclient.multipartUpload(Object.assign(data, { file: blob.blob }), progressFn, fileInfo)
resolve({ resolve({
...res, ...res,
image: blob.pixelDataElement image: blob.pixelDataElement,
Modality: blob.Modality
}) })
// let OSSclientA = await OSSclient // let OSSclientA = await OSSclient
// let blob = await encoder(file) // let blob = await encoder(file)

View File

@ -40,7 +40,7 @@ async function ossGenerateSTS() {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
try { try {
let _vm = router.default.app let _vm = router.default.app
if (_vm._route.path !== '/trials/trials-panel/visit/crc-upload') { if (_vm._route.path !== '/trials/trials-panel/visit/crc-upload' && _vm._route.path !== '/readingDicoms' && _vm._route.path !== '/showdicom') {
var objectItem = objectName.split('/') var objectItem = objectName.split('/')
// objectItem[objectItem.length - 1] = new Date().getTime() + '_' + objectItem[objectItem.length - 1] // objectItem[objectItem.length - 1] = new Date().getTime() + '_' + objectItem[objectItem.length - 1]
objectItem[objectItem.length - 1] = `${objectItem[objectItem.length - 1].substring( objectItem[objectItem.length - 1] = `${objectItem[objectItem.length - 1].substring(
@ -57,13 +57,13 @@ async function ossGenerateSTS() {
const trialId = urlParams.get('trialId') const trialId = urlParams.get('trialId')
if (Object.keys(fileInfo).length !== 0) { if (Object.keys(fileInfo).length !== 0) {
fileInfo.fileType = mimeTypeToExt(fileInfo.fileType) fileInfo.fileType = mimeTypeToExt(fileInfo.fileType)
let params = Object.assign({path: objectName}, fileInfo) let params = Object.assign({ path: objectName }, fileInfo)
addOrUpdateFileUploadRecord(params) addOrUpdateFileUploadRecord(params)
} else if (trialId) { } else if (trialId) {
const fileName = objectName.split('/').pop() const fileName = objectName.split('/').pop()
const fileType = fileName.includes('.') const fileType = fileName.includes('.')
? fileName.split('.').pop().toLowerCase() ? fileName.split('.').pop().toLowerCase()
: '' : ''
let params = { trialId, path: objectName, fileName, fileType } let params = { trialId, path: objectName, fileName, fileType }
addOrUpdateFileUploadRecord(params) addOrUpdateFileUploadRecord(params)
} }
@ -97,7 +97,7 @@ async function ossGenerateSTS() {
OSSclient = new OSS(Vue.prototype.OSSclientConfig); OSSclient = new OSS(Vue.prototype.OSSclientConfig);
} }
let _vm = router.default.app let _vm = router.default.app
if (_vm._route.path !== '/trials/trials-panel/visit/crc-upload') { if (_vm._route.path !== '/trials/trials-panel/visit/crc-upload' && _vm._route.path !== '/readingDicoms' && _vm._route.path !== '/showdicom') {
var objectItem = data.path.split('/') var objectItem = data.path.split('/')
// objectItem[objectItem.length - 1] = new Date().getTime() + '_' + objectItem[objectItem.length - 1] // objectItem[objectItem.length - 1] = new Date().getTime() + '_' + objectItem[objectItem.length - 1]
objectItem[objectItem.length - 1] = `${objectItem[objectItem.length - 1].substring( objectItem[objectItem.length - 1] = `${objectItem[objectItem.length - 1].substring(
@ -114,13 +114,13 @@ async function ossGenerateSTS() {
const trialId = urlParams.get('trialId') const trialId = urlParams.get('trialId')
if (Object.keys(fileInfo).length !== 0) { if (Object.keys(fileInfo).length !== 0) {
fileInfo.fileType = mimeTypeToExt(fileInfo.fileType) fileInfo.fileType = mimeTypeToExt(fileInfo.fileType)
let params = Object.assign({path: data.path}, fileInfo) let params = Object.assign({ path: data.path }, fileInfo)
addOrUpdateFileUploadRecord(params) addOrUpdateFileUploadRecord(params)
} else if (trialId) { } else if (trialId) {
const fileName = data.path.split('/').pop() const fileName = data.path.split('/').pop()
const fileType = fileName.includes('.') const fileType = fileName.includes('.')
? fileName.split('.').pop().toLowerCase() ? fileName.split('.').pop().toLowerCase()
: '' : ''
let params = { trialId, path: data.path, fileName, fileType } let params = { trialId, path: data.path, fileName, fileType }
addOrUpdateFileUploadRecord(params) addOrUpdateFileUploadRecord(params)
} }
@ -151,7 +151,7 @@ async function ossGenerateSTS() {
try { try {
var name = objectName.split('/')[objectName.split('/').length - 1] var name = objectName.split('/')[objectName.split('/').length - 1]
let _vm = router.default.app let _vm = router.default.app
if (_vm._route.path !== '/trials/trials-panel/visit/crc-upload') { if (_vm._route.path !== '/trials/trials-panel/visit/crc-upload' && _vm._route.path !== '/readingDicoms' && _vm._route.path !== '/showdicom') {
var objectItem = objectName.split('/') var objectItem = objectName.split('/')
objectItem[objectItem.length - 1] = new Date().getTime() + '_' + objectItem[objectItem.length - 1] objectItem[objectItem.length - 1] = new Date().getTime() + '_' + objectItem[objectItem.length - 1]
objectName = objectItem.join('/') objectName = objectItem.join('/')
@ -225,7 +225,7 @@ function uploadAWS(aws, data, progress, fileInfo) {
const { file, path } = data; const { file, path } = data;
if (!file || !path) return reject('file and path be required'); if (!file || !path) return reject('file and path be required');
let _vm = router.default.app let _vm = router.default.app
if (_vm._route.path !== '/trials/trials-panel/visit/crc-upload') { if (_vm._route.path !== '/trials/trials-panel/visit/crc-upload' && _vm._route.path !== '/readingDicoms' && _vm._route.path !== '/showdicom') {
var objectItem = data.path.split('/') var objectItem = data.path.split('/')
// objectItem[objectItem.length - 1] = new Date().getTime() + '_' + objectItem[objectItem.length - 1] // objectItem[objectItem.length - 1] = new Date().getTime() + '_' + objectItem[objectItem.length - 1]
objectItem[objectItem.length - 1] = `${objectItem[objectItem.length - 1].substring( objectItem[objectItem.length - 1] = `${objectItem[objectItem.length - 1].substring(
@ -246,13 +246,13 @@ function uploadAWS(aws, data, progress, fileInfo) {
const trialId = urlParams.get('trialId') const trialId = urlParams.get('trialId')
if (Object.keys(fileInfo).length !== 0) { if (Object.keys(fileInfo).length !== 0) {
fileInfo.fileType = mimeTypeToExt(fileInfo.fileType) fileInfo.fileType = mimeTypeToExt(fileInfo.fileType)
let params = Object.assign({path: decodeUtf8(curPath)}, fileInfo) let params = Object.assign({ path: decodeUtf8(curPath) }, fileInfo)
addOrUpdateFileUploadRecord(params) addOrUpdateFileUploadRecord(params)
} else if (trialId) { } else if (trialId) {
const fileName = decodeUtf8(curPath).split('/').pop() const fileName = decodeUtf8(curPath).split('/').pop()
const fileType = fileName.includes('.') const fileType = fileName.includes('.')
? fileName.split('.').pop().toLowerCase() ? fileName.split('.').pop().toLowerCase()
: '' : ''
let params = { trialId, path: decodeUtf8(curPath), fileName, fileType } let params = { trialId, path: decodeUtf8(curPath), fileName, fileType }
addOrUpdateFileUploadRecord(params) addOrUpdateFileUploadRecord(params)
} }

View File

@ -125,7 +125,8 @@
</div> </div>
<div class="viewerContent"> <div class="viewerContent">
<dicom-viewer id="dicomViewer" ref="dicomViewer" style="height:100%" :loading.sync="loading" <dicom-viewer id="dicomViewer" ref="dicomViewer" style="height:100%" :loading.sync="loading"
:modality="modality" :Comparison.sync="isComparison" @loadStudy="loadStudy" /> :modality="modality" :Comparison.sync="isComparison" :SeriesList="seriesList"
:currentSeriesIndex="currentSeriesIndex" @loadStudy="loadStudy" />
</div> </div>
<!-- <div class="viewerRightSidePanel"> <!-- <div class="viewerRightSidePanel">
<dicom-tools /> <dicom-tools />
@ -179,7 +180,7 @@ export default {
description: '', description: '',
seriesCount: 0, seriesCount: 0,
seriesList: [], seriesList: [],
currentSeriesIndex: -1, currentSeriesIndex: 0,
arr: [], arr: [],
activeName: 'first', activeName: 'first',
tpList: [], tpList: [],
@ -253,6 +254,11 @@ export default {
}) })
workSpeedclose(true) workSpeedclose(true)
}, },
watch: {
currentSeriesIndex() {
console.log(this.currentSeriesIndex, 'currentSeriesIndex')
}
},
methods: { methods: {
async updateImageResizePath(data) { async updateImageResizePath(data) {
try { try {
@ -353,13 +359,14 @@ export default {
i.ImageId = imageId i.ImageId = imageId
} }
}) })
var subjectVisitId = this.$router.currentRoute.query.subjectVisitId var subjectVisitId = this.$router.currentRoute.query.subjectVisitId ? this.$router.currentRoute.query.subjectVisitId : item.SubjectVisitId
var studyId = this.$router.currentRoute.query.studyId var studyId = this.$router.currentRoute.query.studyId
var trialId = this.$router.currentRoute.query.trialId var trialId = this.$router.currentRoute.query.trialId
seriesList.push({ seriesList.push({
trialId, trialId,
subjectVisitId, subjectVisitId,
studyId, studyId,
subjectId: item.SubjectId,
imageIds: imageIds, imageIds: imageIds,
instanceInfoList: item.InstanceInfoList, instanceInfoList: item.InstanceInfoList,
seriesId: item.Id, seriesId: item.Id,
@ -415,12 +422,13 @@ export default {
i.ImageId = imageId i.ImageId = imageId
} }
}) })
var subjectVisitId = this.$router.currentRoute.query.subjectVisitId var subjectVisitId = this.$router.currentRoute.query.subjectVisitId ? this.$router.currentRoute.query.subjectVisitId : item.SubjectVisitId
var studyId = this.$router.currentRoute.query.studyId var studyId = this.$router.currentRoute.query.studyId
var trialId = this.$router.currentRoute.query.trialId var trialId = this.$router.currentRoute.query.trialId
seriesList.push({ seriesList.push({
trialId, trialId,
subjectVisitId, subjectVisitId,
subjectId: item.SubjectId,
studyId, studyId,
imageIds: imageIds, imageIds: imageIds,
instanceInfoList: item.InstanceInfoList, instanceInfoList: item.InstanceInfoList,
@ -1197,6 +1205,7 @@ export default {
border-color: #213a54 !important; border-color: #213a54 !important;
background-color: #213a54; background-color: #213a54;
} }
.frame_content_active { .frame_content_active {
border-color: #213a54 !important; border-color: #213a54 !important;
background-color: #213a54; background-color: #213a54;

View File

@ -1222,7 +1222,7 @@ export default {
cornerstoneTools.addToolForElement(element, RectangleRoiTool, { configuration: { allowEmptyLabel: true, handleRadius: false, drawHandlesOnHover: true, hideHandlesIfMoving: true } }) cornerstoneTools.addToolForElement(element, RectangleRoiTool, { configuration: { allowEmptyLabel: true, handleRadius: false, drawHandlesOnHover: true, hideHandlesIfMoving: true } })
} else if (toolName === 'Probe' && (parseInt(localStorage.getItem('CriterionType')) === 21)) { } else if (toolName === 'Probe' && (parseInt(localStorage.getItem('CriterionType')) === 21)) {
cornerstoneTools.addToolForElement(element, ProbeTool, { configuration: { fixedRadius: 5, handleRadius: true, drawHandlesOnHover: true, hideHandlesIfMoving: true, digits: this.digitPlaces } }) cornerstoneTools.addToolForElement(element, ProbeTool, { configuration: { fixedRadius: 5, handleRadius: true, drawHandlesOnHover: true, hideHandlesIfMoving: true, digits: this.digitPlaces } })
} else if (toolName === 'Probe' && parseInt(localStorage.getItem('CriterionType')) === 22) { } else if (toolName === 'Probe' && parseInt(localStorage.getItem('CriterionType')) === 22) {
cornerstoneTools.addToolForElement(element, ProbeTool, { configuration: { fixedRadius: 12, unit: 'mm', handleRadius: true, drawHandlesOnHover: true, hideHandlesIfMoving: true, digits: this.digitPlaces } }) cornerstoneTools.addToolForElement(element, ProbeTool, { configuration: { fixedRadius: 12, unit: 'mm', handleRadius: true, drawHandlesOnHover: true, hideHandlesIfMoving: true, digits: this.digitPlaces } })
} else { } else {
cornerstoneTools.addToolForElement(element, apiTool) cornerstoneTools.addToolForElement(element, apiTool)
@ -1675,7 +1675,10 @@ export default {
cornerstone.getDefaultViewportForImage(this.canvas, image) cornerstone.getDefaultViewportForImage(this.canvas, image)
) )
}, },
getInfo() {
var image = cornerstone.getImage(this.canvas)
return image
},
toggleDicomInfo() { toggleDicomInfo() {
this.toolState.dicomInfoVisible = !this.toolState.dicomInfoVisible this.toolState.dicomInfoVisible = !this.toolState.dicomInfoVisible
if (this.toolState.dicomInfoVisible) { if (this.toolState.dicomInfoVisible) {

View File

@ -447,6 +447,10 @@
<div class="text">{{ $t('trials:reading:button:download') }}</div> <div class="text">{{ $t('trials:reading:button:download') }}</div>
</div> </div>
</el-tooltip> </el-tooltip>
<label for="Replacement" style="color: #fff;cursor: pointer;">
替换
</label>
<input type="file" id="Replacement" @change="beginScanFiles($event, 'replace')" style="display: none;">
<div style="margin-left:auto;"> <div style="margin-left:auto;">
<div style="padding:5px;display: flex;"> <div style="padding:5px;display: flex;">
<!-- 手册 --> <!-- 手册 -->
@ -708,7 +712,8 @@
</el-dialog> </el-dialog>
<upload-dicom-and-nonedicom v-if="uploadImageVisible" :subject-id="uploadSubjectId" <upload-dicom-and-nonedicom v-if="uploadImageVisible" :subject-id="uploadSubjectId"
:subject-code="uploadSubjectCode" :criterion="uploadTrialCriterion" :visible.sync="uploadImageVisible" :subject-code="uploadSubjectCode" :criterion="uploadTrialCriterion" :visible.sync="uploadImageVisible"
:visit-task-id="taskId" :is-reading-task-view-in-order="isReadingTaskViewInOrder" :isReading="true" /> :visit-task-id="taskId" :is-reading-task-view-in-order="isReadingTaskViewInOrder" :isReading="true"
:StudyInstanceUID="StudyInstanceUID" />
<download-dicom-and-nonedicom v-if="downloadImageVisible" :subject-id="uploadSubjectId" <download-dicom-and-nonedicom v-if="downloadImageVisible" :subject-id="uploadSubjectId"
:subject-code="uploadSubjectCode" :criterion="uploadTrialCriterion" :task-id="taskId" :subject-code="uploadSubjectCode" :criterion="uploadTrialCriterion" :task-id="taskId"
:visible.sync="downloadImageVisible" :isReading="true" /> :visible.sync="downloadImageVisible" :isReading="true" />
@ -767,6 +772,8 @@ import const_ from '@/const/sign-code'
import { changeURLStatic } from '@/utils/history.js' import { changeURLStatic } from '@/utils/history.js'
import SystemInfo from "@/utils/systemInfo"; import SystemInfo from "@/utils/systemInfo";
import md5 from 'js-md5' import md5 from 'js-md5'
import { changeFile } from "./upload.js"
import dcmjs from '@/utils/dcmUpload/dcmjs'
export default { export default {
name: 'DicomViewer', name: 'DicomViewer',
components: { components: {
@ -845,6 +852,10 @@ export default {
default() { default() {
return true return true
} }
},
Loading: {
type: Boolean,
default: false
} }
}, },
data() { data() {
@ -978,7 +989,10 @@ export default {
fullScreenWidth: window.innerWidth - 570 + 'px', fullScreenWidth: window.innerWidth - 570 + 'px',
fullScreenHeight: window.innerHeight - 130 + 'px', fullScreenHeight: window.innerHeight - 130 + 'px',
ManualsClose: false ManualsClose: false,
fileKey: null,
file: null,
StudyInstanceUID: null
} }
}, },
@ -1213,6 +1227,12 @@ export default {
this.AspectRatio = windowWidth / windowHeight this.AspectRatio = windowWidth / windowHeight
}; };
this.getSystemInfoReading() this.getSystemInfoReading()
DicomEvent.$on('sendStudyFile', (study) => {
let image = this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].getInfo()
study.visitTaskList = this.visitTaskList
this.$emit("update:Loading", true)
changeFile('IR', this.file, study, image)
})
}, },
beforeDestroy() { beforeDestroy() {
DicomEvent.$off('updateImage') DicomEvent.$off('updateImage')
@ -1236,6 +1256,11 @@ export default {
}) })
}, },
methods: { methods: {
beginScanFiles(e, key) {
this.fileKey = key
this.file = e.target.files[0]
DicomEvent.$emit('getStudyFile')
},
handleReadingChart(e) { handleReadingChart(e) {
this.$emit('handleReadingChart', e) this.$emit('handleReadingChart', e)
}, },
@ -1283,6 +1308,10 @@ export default {
if (idx > -1) { if (idx > -1) {
this.taskId = this.visitTaskList[idx].VisitTaskId this.taskId = this.visitTaskList[idx].VisitTaskId
} }
let image = this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].getInfo()
let dataset = dcmjs.data.DicomMessage.readFile(image.data.byteArray.buffer)
this.StudyInstanceUID = dataset.dict['0020000D'].Value[0]
dataset = null
this.uploadSubjectCode = localStorage.getItem("subjectCode") this.uploadSubjectCode = localStorage.getItem("subjectCode")
this.uploadSubjectId = localStorage.getItem("subjectId") this.uploadSubjectId = localStorage.getItem("subjectId")
this.uploadTrialCriterion = this.trialCriterion this.uploadTrialCriterion = this.trialCriterion

View File

@ -1,5 +1,5 @@
<template> <template>
<div class="dicom-container"> <div class="dicom-container" v-loading="loading">
<div :class="{ 'dicom-list': true, studyHide: !studyShow }"> <div :class="{ 'dicom-list': true, studyHide: !studyShow }">
<div class="container"> <div class="container">
<div class="related-study-wrapper"> <div class="related-study-wrapper">
@ -27,7 +27,8 @@
class="study-wrapper"> class="study-wrapper">
<StudyList v-if="selectArr.includes(s.VisitTaskId)" :ref="s.VisitTaskId" :visit-task-id="s.VisitTaskId" <StudyList v-if="selectArr.includes(s.VisitTaskId)" :ref="s.VisitTaskId" :visit-task-id="s.VisitTaskId"
:trial-id="trialId" :subject-visit-id="s.VisitId" :task-blind-name="s.TaskBlindName" :trial-id="trialId" :subject-visit-id="s.VisitId" :task-blind-name="s.TaskBlindName"
:is-reading-show-subject-info="isReadingShowSubjectInfo" :is-reading-task-view-in-order="isReadingTaskViewInOrder" @loadImageStack="loadImageStack" :is-reading-show-subject-info="isReadingShowSubjectInfo"
:is-reading-task-view-in-order="isReadingTaskViewInOrder" @loadImageStack="loadImageStack"
@previewNoneDicoms="previewNoneDicoms" /> @previewNoneDicoms="previewNoneDicoms" />
</div> </div>
</div> </div>
@ -38,7 +39,7 @@
</div> </div>
<div class="dicom-viewer"> <div class="dicom-viewer">
<div class="container"> <div class="container">
<DicomViewer v-if="activeTaskVisitId" ref="dicomViewer" :is-show="isShow" <DicomViewer v-if="activeTaskVisitId" ref="dicomViewer" :is-show="isShow" :Loading.sync="loading"
:question-form-change-state="questionFormChangeState" :question-form-change-num="questionFormChangeNum" :question-form-change-state="questionFormChangeState" :question-form-change-num="questionFormChangeNum"
:is-exists-clinical-data="isExistsClinicalData" :is-exists-no-dicom-file="isExistsNoDicomFile" :is-exists-clinical-data="isExistsClinicalData" :is-exists-no-dicom-file="isExistsNoDicomFile"
:is-reading-show-subject-info="isReadingShowSubjectInfo" :studyShow="studyShow" :is-reading-show-subject-info="isReadingShowSubjectInfo" :studyShow="studyShow"

View File

@ -1,9 +1,11 @@
<template> <template>
<div v-loading="loading" class="study-wrapper"> <div v-loading="loading" class="study-wrapper">
<h4 v-if="isReadingShowSubjectInfo" style="color: #ddd;padding: 5px 0px;margin: 0;text-align: center;background-color: #4c4c4c;"> <h4 v-if="isReadingShowSubjectInfo"
style="color: #ddd;padding: 5px 0px;margin: 0;text-align: center;background-color: #4c4c4c;">
{{ subjectCode }} {{ subjectCode }}
</h4> </h4>
<h4 v-if="isReadingShowSubjectInfo" style="color: #ddd;padding: 5px 0px;margin: 0;text-align: center;background-color: #4c4c4c;margin-bottom: 5px;"> <h4 v-if="isReadingShowSubjectInfo"
style="color: #ddd;padding: 5px 0px;margin: 0;text-align: center;background-color: #4c4c4c;margin-bottom: 5px;">
{{ taskBlindName }} {{ taskBlindName }}
</h4> </h4>
<div class="ps"> <div class="ps">
@ -14,116 +16,103 @@
<!-- 关键序列 --> <!-- 关键序列 -->
{{ $t('trials:reading:title:keySeries') }} {{ $t('trials:reading:title:keySeries') }}
</div> </div>
<div <div v-else class="dicom-desc" style="white-space: normal;">
v-else <div>
class="dicom-desc" <div style="text-overflow: ellipsis;overflow: hidden;">
style="white-space: normal;"
>
<div>
<div style="text-overflow: ellipsis;overflow: hidden;">
<span v-if="taskInfo && taskInfo.IsShowStudyName && study.StudyName" :title="study.StudyName"> <span v-if="taskInfo && taskInfo.IsShowStudyName && study.StudyName" :title="study.StudyName">
{{study.StudyName}} {{ study.StudyName }}
</span> </span>
</div>
<div>
<span class="study-meta-line" :title="study.Modalities">
<span v-if="isReadingTaskViewInOrder !== 0" class="study-code" :title="study.StudyCode">{{ study.StudyCode }}</span>
<span class="study-modality">{{ study.Modalities }}({{ study.SeriesCount }})</span>
<span class="patient-info" v-if="['PT、CT', 'CT、PT', 'PET-CT'].includes(study.Modalities)">
<el-popover placement="right-start" trigger="hover" popper-class="patient-info-popper">
<h4>{{ $t('trials:ptData:title') }}</h4>
<div class="patient-info-row">
<label>{{ $t('trials:ptData:label:patientSex') }}</label>
<span>{{ study.PatientSex }}</span>
</div>
<div class="patient-info-row">
<label>{{ $t('trials:ptData:label:patientWeight') }}</label>
<span>{{ study.PatientWeight }}</span>
</div>
<div class="patient-info-row">
<label>{{ $t('trials:ptData:label:totalDose') }}</label>
<span>{{ study.RadionuclideTotalDose }}</span>
</div>
<div class="patient-info-row">
<label>{{ $t('trials:ptData:label:halfLife') }}</label>
<span>{{ study.RadionuclideHalfLife }}</span>
</div>
<div class="patient-info-row">
<label>{{ $t('trials:ptData:label:injectTime') }}</label>
<span>{{ study.RadiopharmaceuticalStartTime }}</span>
</div>
<div class="patient-info-row">
<label>{{ $t('trials:ptData:label:acquisitionTime') }}</label>
<span>{{ study.AcquisitionTime }}</span>
</div>
<i slot="reference" class="el-icon-document"
style="font-size: 15px;cursor: pointer;color: #f5f7fa;" />
</el-popover>
</span>
</span>
</div>
<div v-if="study.Description" class="study-desc-text" :title="study.Description">{{ study.Description }}</div>
</div> </div>
<div>
<span class="study-meta-line" :title="study.Modalities">
<span v-if="isReadingTaskViewInOrder !== 0" class="study-code" :title="study.StudyCode">{{
study.StudyCode }}</span>
<span class="study-modality">{{ study.Modalities }}({{ study.SeriesCount }})</span>
<span class="patient-info" v-if="['PT、CT', 'CT、PT', 'PET-CT'].includes(study.Modalities)">
<el-popover placement="right-start" trigger="hover" popper-class="patient-info-popper">
<h4>{{ $t('trials:ptData:title') }}</h4>
<div class="patient-info-row">
<label>{{ $t('trials:ptData:label:patientSex') }}</label>
<span>{{ study.PatientSex }}</span>
</div>
<div class="patient-info-row">
<label>{{ $t('trials:ptData:label:patientWeight') }}</label>
<span>{{ study.PatientWeight }}</span>
</div>
<div class="patient-info-row">
<label>{{ $t('trials:ptData:label:totalDose') }}</label>
<span>{{ study.RadionuclideTotalDose }}</span>
</div>
<div class="patient-info-row">
<label>{{ $t('trials:ptData:label:halfLife') }}</label>
<span>{{ study.RadionuclideHalfLife }}</span>
</div>
<div class="patient-info-row">
<label>{{ $t('trials:ptData:label:injectTime') }}</label>
<span>{{ study.RadiopharmaceuticalStartTime }}</span>
</div>
<div class="patient-info-row">
<label>{{ $t('trials:ptData:label:acquisitionTime') }}</label>
<span>{{ study.AcquisitionTime }}</span>
</div>
<i slot="reference" class="el-icon-document"
style="font-size: 15px;cursor: pointer;color: #f5f7fa;" />
</el-popover>
</span>
</span>
</div>
<div v-if="study.Description" class="study-desc-text" :title="study.Description">{{ study.Description }}
</div>
</div>
</div> </div>
</template> </template>
<div class="series"> <div class="series">
<div <div v-for="(series, i) in study.SeriesList" :key="i" style="position:relative;margin-top:5px;"
v-for="(series, i) in study.SeriesList" series-type="current" @click="showSeriesImage(index, i, series)">
:key="i"
style="position:relative;margin-top:5px;"
series-type="current"
@click="showSeriesImage(index,i,series)"
>
<div <div :class="{ 'series-active': i == seriesIndex && index === studyIndex }" class="series-wrapper">
:class="{'series-active': i==seriesIndex && index === studyIndex}" <el-image class="image-preview" :src="series.previewImageUrl" fit="fill" crossorigin="anonymous" />
class="series-wrapper"
>
<el-image
class="image-preview"
:src="series.previewImageUrl"
fit="fill"
crossorigin="anonymous"
/>
<div class="image-desc"> <div class="image-desc">
<div class="flex-div"> <div class="flex-div">
<div style="width: 40px;display: flex;flex-direction: row;justify-content: space-between;"> <div style="width: 40px;display: flex;flex-direction: row;justify-content: space-between;">
<div v-if="!study.IsCriticalSequence">#{{ series.seriesNumber }}</div> <div v-if="!study.IsCriticalSequence">#{{ series.seriesNumber }}</div>
<div v-if="series.isDicom && series.prefetchInstanceCount<series.instanceCount * 100 && series.modality!== 'SR'"> <div
v-if="series.isDicom && series.prefetchInstanceCount < series.instanceCount * 100 && series.modality !== 'SR'">
<!-- 下载 --> <!-- 下载 -->
<el-tooltip v-if="!series.isLoading" class="item" effect="dark" :content="$t('trials:reading:button:download')" placement="bottom"> <el-tooltip v-if="!series.isLoading" class="item" effect="dark"
<i class="el-icon-video-play" style="font-size: 18px;margin-right: 5px;color: #ffeb3b;cursor: pointer;" @click.stop="loadSeries(series,index,i)" /> :content="$t('trials:reading:button:download')" placement="bottom">
<i class="el-icon-video-play"
style="font-size: 18px;margin-right: 5px;color: #ffeb3b;cursor: pointer;"
@click.stop="loadSeries(series, index, i)" />
</el-tooltip> </el-tooltip>
</div> </div>
<el-tooltip v-else-if="series.isDicom && series.prefetchInstanceCount === 0 &&series.modality!== 'SR'" class="item" effect="dark" :content="$t('trials:reading:button:download')" placement="bottom"> <el-tooltip
<i class="el-icon-video-play" style="font-size: 18px;margin-right: 5px;color: #ffeb3b;cursor: pointer;" @click.stop="loadSeries(series,index,i)" /> v-else-if="series.isDicom && series.prefetchInstanceCount === 0 && series.modality !== 'SR'"
class="item" effect="dark" :content="$t('trials:reading:button:download')" placement="bottom">
<i class="el-icon-video-play"
style="font-size: 18px;margin-right: 5px;color: #ffeb3b;cursor: pointer;"
@click.stop="loadSeries(series, index, i)" />
</el-tooltip> </el-tooltip>
</div> </div>
<div v-if="series.isExistMutiFrames && series.instanceCount > 1"> <div v-if="series.isExistMutiFrames && series.instanceCount > 1">
<el-popover <el-popover placement="right" trigger="hover" popper-class="instance_frame_wrapper">
placement="right"
trigger="hover"
popper-class="instance_frame_wrapper"
>
<div class="frame_list"> <div class="frame_list">
<div <div v-for="(instance, idx) in series.instanceInfoList" :key="instance.Id"
v-for="(instance, idx) in series.instanceInfoList" class="frame_content" :class="{ 'frame_content_active': activeInstanceId === instance.Id }"
:key="instance.Id" :style="{ 'margin-bottom': idx < series.instanceInfoList.length - 1 ? '5px' : '0px' }"
class="frame_content" @click.stop="showMultiFrames(index, series, i, instance)">
:class="{ 'frame_content_active': activeInstanceId === instance.Id }"
:style="{'margin-bottom':idx<series.instanceInfoList.length-1? '5px':'0px'}"
@click.stop="showMultiFrames(index,series, i, instance)"
>
<div> <div>
<div>{{ instance.InstanceNumber }}</div> <div>{{ instance.InstanceNumber }}</div>
<div>{{ `${instance.NumberOfFrames > 0 ? instance.KeyFramesList.length > 0 ? instance.KeyFramesList.length : instance.NumberOfFrames : 1} frame` }}</div> <div>{{ `${instance.NumberOfFrames > 0 ? instance.KeyFramesList.length > 0 ?
instance.KeyFramesList.length : instance.NumberOfFrames : 1} frame` }}</div>
</div> </div>
</div> </div>
</div> </div>
<i slot="reference" class="el-icon-connection" style="font-size: 15px;cursor: pointer;color: #ffeb3b;" /> <i slot="reference" class="el-icon-connection"
style="font-size: 15px;cursor: pointer;color: #ffeb3b;" />
</el-popover> </el-popover>
</div> </div>
</div> </div>
@ -145,9 +134,11 @@
</p> </p>
<div class="flex-div"> <div class="flex-div">
<div v-if="measureData && measureData.findIndex(v=>v.SeriesId === series.seriesId && v.MeasureData) > -1"> <div
v-if="measureData && measureData.findIndex(v => v.SeriesId === series.seriesId && v.MeasureData) > -1">
<!-- 有标注 --> <!-- 有标注 -->
<el-tooltip class="item" effect="dark" :content="$t('trials:reading:button:marked')" placement="right"> <el-tooltip class="item" effect="dark" :content="$t('trials:reading:button:marked')"
placement="right">
<i class="el-icon-star-on" style="font-size: 16px;color: #ff5722;" /> <i class="el-icon-star-on" style="font-size: 16px;color: #ff5722;" />
</el-tooltip> </el-tooltip>
@ -155,10 +146,10 @@
</div> </div>
</div> </div>
</div> </div>
<div v-if="series.isDicom && series.prefetchInstanceCount>0 && series.prefetchInstanceCount<series.instanceCount * 100" style="width: 100%;"> <div
<el-progress v-if="series.isDicom && series.prefetchInstanceCount > 0 && series.prefetchInstanceCount < series.instanceCount * 100"
:percentage="parseInt((series.prefetchInstanceCount / series.instanceCount).toFixed(2))" style="width: 100%;">
/> <el-progress :percentage="parseInt((series.prefetchInstanceCount / series.instanceCount).toFixed(2))" />
</div> </div>
</div> </div>
@ -168,24 +159,19 @@
</div> </div>
<div class="sr-wrapper"> <div class="sr-wrapper">
<el-dialog <el-dialog :visible.sync="srDialogVisible"
:visible.sync="srDialogVisible" :custom-class="isSrFullscreen ? 'sr-full-dialog-container' : 'sr-dialog-container'" :show-close="false"
:custom-class="isSrFullscreen?'sr-full-dialog-container':'sr-dialog-container'" :close-on-click-modal="false" :fullscreen="isSrFullscreen">
:show-close="false"
:close-on-click-modal="false"
:fullscreen="isSrFullscreen"
>
<span slot="title" class="dialog-footer"> <span slot="title" class="dialog-footer">
<div style="position: absolute;right: 20px;top: 10px;"> <div style="position: absolute;right: 20px;top: 10px;">
<svg-icon :icon-class="isSrFullscreen?'exit-fullscreen':'fullscreen'" style="cursor: pointer;font-size: 20px;color:#000;" @click="isSrFullscreen=!isSrFullscreen" /> <svg-icon :icon-class="isSrFullscreen ? 'exit-fullscreen' : 'fullscreen'"
<svg-icon icon-class="dClose" style="cursor: pointer;font-size: 25px;margin-left: 10px;" @click="srDialogVisible = false" /> style="cursor: pointer;font-size: 20px;color:#000;" @click="isSrFullscreen = !isSrFullscreen" />
<svg-icon icon-class="dClose" style="cursor: pointer;font-size: 25px;margin-left: 10px;"
@click="srDialogVisible = false" />
</div> </div>
</span> </span>
<div style="height: 100%;margin:0;"> <div style="height: 100%;margin:0;">
<SrList <SrList v-if="srDialogVisible" :sr-info="srInfo" />
v-if="srDialogVisible"
:sr-info="srInfo"
/>
</div> </div>
</el-dialog> </el-dialog>
</div> </div>
@ -296,6 +282,9 @@ export default {
var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId) var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
this.measureData = this.visitTaskList[idx].MeasureData this.measureData = this.visitTaskList[idx].MeasureData
}) })
DicomEvent.$on("getStudyFile", () => {
DicomEvent.$emit('sendStudyFile', { studyList: this.studyList, studyIndex: this.studyIndex, seriesIndex: this.seriesIndex, visitTaskId: this.visitTaskId })
})
// DicomEvent.$on('setReadingState', readingTaskState => { // DicomEvent.$on('setReadingState', readingTaskState => {
// var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId) // var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
@ -320,7 +309,7 @@ export default {
methods: { methods: {
debounce(fn, delay) { debounce(fn, delay) {
let timer = null let timer = null
return function() { return function () {
const context = this const context = this
const args = arguments const args = arguments
clearTimeout(timer) clearTimeout(timer)
@ -334,7 +323,7 @@ export default {
// //
this.getInitSeries().then((res) => { this.getInitSeries().then((res) => {
requestPoolManager.startTaskTimer() requestPoolManager.startTaskTimer()
res.map(async(item) => { res.map(async (item) => {
// this.loadInitialImage(item) // this.loadInitialImage(item)
const imageId = item.imageIds[item.imageIdIndex] const imageId = item.imageIds[item.imageIdIndex]
const p = parseInt(new Date().getTime()) const p = parseInt(new Date().getTime())
@ -456,8 +445,8 @@ export default {
this.studyIndex = obj.studyIndex this.studyIndex = obj.studyIndex
this.seriesIndex = obj.seriesIndex this.seriesIndex = obj.seriesIndex
seriesList.push(obj.series) seriesList.push(obj.series)
this.activeNames = [`${this.studyList[ this.studyIndex].StudyId}`] this.activeNames = [`${this.studyList[this.studyIndex].StudyId}`]
this.studyList[ obj.studyIndex].SeriesList[obj.seriesIndex].isFirstRender = true this.studyList[obj.studyIndex].SeriesList[obj.seriesIndex].isFirstRender = true
} else { } else {
if (this.studyList.length > 0) { if (this.studyList.length > 0) {
// //
@ -494,7 +483,7 @@ export default {
this.studyIndex = secondObj.studyIndex this.studyIndex = secondObj.studyIndex
this.seriesIndex = secondObj.seriesIndex this.seriesIndex = secondObj.seriesIndex
seriesList.push(secondObj.series) seriesList.push(secondObj.series)
this.studyList[ secondObj.studyIndex].SeriesList[secondObj.seriesIndex].isFirstRender = true this.studyList[secondObj.studyIndex].SeriesList[secondObj.seriesIndex].isFirstRender = true
this.studyIndex = secondObj.studyIndex this.studyIndex = secondObj.studyIndex
this.seriesIndex = secondObj.seriesIndex this.seriesIndex = secondObj.seriesIndex
@ -527,7 +516,7 @@ export default {
// const instanceList = seriesList[srIdx].instanceList // const instanceList = seriesList[srIdx].instanceList
const imageIds = seriesList[srIdx].imageIds const imageIds = seriesList[srIdx].imageIds
// const filterStr = seriesList[srIdx].isExistMutiFrames ? `frame=${measureDatas[i].MeasureData.frame}&instanceId=${measureDatas[i].InstanceId}` : `instanceId=${measureDatas[i].InstanceId}` // const filterStr = seriesList[srIdx].isExistMutiFrames ? `frame=${measureDatas[i].MeasureData.frame}&instanceId=${measureDatas[i].InstanceId}` : `instanceId=${measureDatas[i].InstanceId}`
let instanceIndex = seriesList[srIdx].instanceInfoList.findIndex(k=>k.Id === measureDatas[i].InstanceId) let instanceIndex = seriesList[srIdx].instanceInfoList.findIndex(k => k.Id === measureDatas[i].InstanceId)
let filterStr = '' let filterStr = ''
if (instanceIndex > -1 && seriesList[srIdx].isExistMutiFrames) { if (instanceIndex > -1 && seriesList[srIdx].isExistMutiFrames) {
if (seriesList[srIdx].instanceInfoList[instanceIndex].NumberOfFrames > 0) { if (seriesList[srIdx].instanceInfoList[instanceIndex].NumberOfFrames > 0) {
@ -611,7 +600,7 @@ export default {
// const instanceList = seriesList[srIdx].imageIds // const instanceList = seriesList[srIdx].imageIds
const imageIds = seriesList[srIdx].imageIds const imageIds = seriesList[srIdx].imageIds
// const filterStr = seriesList[srIdx].isExistMutiFrames ? `frame=${measureDatas[mIdx].MeasureData.frame}&instanceId=${measureDatas[mIdx].InstanceId}` : `instanceId=${measureDatas[mIdx].InstanceId}` // const filterStr = seriesList[srIdx].isExistMutiFrames ? `frame=${measureDatas[mIdx].MeasureData.frame}&instanceId=${measureDatas[mIdx].InstanceId}` : `instanceId=${measureDatas[mIdx].InstanceId}`
let instanceIndex = seriesList[srIdx].instanceInfoList.findIndex(i=>i.Id === measureDatas[mIdx].InstanceId) let instanceIndex = seriesList[srIdx].instanceInfoList.findIndex(i => i.Id === measureDatas[mIdx].InstanceId)
let filterStr = '' let filterStr = ''
if (instanceIndex > -1 && seriesList[srIdx].isExistMutiFrames) { if (instanceIndex > -1 && seriesList[srIdx].isExistMutiFrames) {
if (seriesList[srIdx].instanceInfoList[instanceIndex].NumberOfFrames > 0) { if (seriesList[srIdx].instanceInfoList[instanceIndex].NumberOfFrames > 0) {
@ -968,15 +957,17 @@ export default {
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.study-wrapper{ .study-wrapper {
::v-deep .el-progress-bar__inner{ ::v-deep .el-progress-bar__inner {
transition: width 0s ease; transition: width 0s ease;
} }
width:100%; width:100%;
height: 100%; height: 100%;
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
.dicom-desc{
.dicom-desc {
font-weight: bold; font-weight: bold;
font-size: 13px; font-size: 13px;
text-align: left; text-align: left;
@ -985,6 +976,7 @@ export default {
white-space: normal; white-space: normal;
overflow: visible; overflow: visible;
} }
.study-meta-line { .study-meta-line {
// display: grid; // display: grid;
// grid-template-columns: minmax(0, 1fr) auto; // grid-template-columns: minmax(0, 1fr) auto;
@ -994,6 +986,7 @@ export default {
width: 100%; width: 100%;
min-width: 0; min-width: 0;
} }
.study-meta-main { .study-meta-main {
display: block; display: block;
min-width: 0; min-width: 0;
@ -1001,11 +994,13 @@ export default {
overflow-wrap: anywhere; overflow-wrap: anywhere;
flex: 1; flex: 1;
} }
.study-code, .study-code,
.study-modality { .study-modality {
white-space: normal; white-space: normal;
margin: 0 2px; margin: 0 2px;
} }
.study-desc-text { .study-desc-text {
min-width: 0; min-width: 0;
overflow: hidden; overflow: hidden;
@ -1017,19 +1012,23 @@ export default {
overflow-anchor: none; overflow-anchor: none;
touch-action: auto; touch-action: auto;
} }
.series-active { .series-active {
background-color: #607d8b!important; background-color: #607d8b !important;
border: 1px solid #607d8b!important; border: 1px solid #607d8b !important;
} }
::v-deep .el-progress__text{
::v-deep .el-progress__text {
color: #ccc; color: #ccc;
font-size: 12px; font-size: 12px;
} }
.series{
.series {
width: 100%; width: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: flex-start; justify-content: flex-start;
.series-wrapper { .series-wrapper {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@ -1042,11 +1041,13 @@ export default {
border-radius: 2px; border-radius: 2px;
border: 1px solid #404040; border: 1px solid #404040;
background-color: #3a3a3a; background-color: #3a3a3a;
.el-progress__text{
.el-progress__text {
display: none; display: none;
} }
.el-progress-bar{
padding-right:0px; .el-progress-bar {
padding-right: 0px;
} }
.image-preview { .image-preview {
@ -1055,9 +1056,11 @@ export default {
border: 2px solid #252525; border: 2px solid #252525;
cursor: pointer; cursor: pointer;
} }
.image-desc { .image-desc {
vertical-align: top; vertical-align: top;
p{
p {
width: 95px; width: 95px;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
@ -1066,7 +1069,8 @@ export default {
color: #ddd; color: #ddd;
margin: 0px; margin: 0px;
line-height: 1.5; line-height: 1.5;
div{
div {
width: 95px; width: 95px;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;
@ -1074,7 +1078,8 @@ export default {
} }
} }
} }
.flex-div{
.flex-div {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
@ -1091,21 +1096,25 @@ export default {
} }
} }
::v-deep .el-collapse{
::v-deep .el-collapse {
border: none; border: none;
.el-collapse-item{
background-color: #000!important; .el-collapse-item {
background-color: #000 !important;
color: #ddd; color: #ddd;
} }
.el-collapse-item__content{
padding-bottom:5px; .el-collapse-item__content {
background-color: #000!important; padding-bottom: 5px;
background-color: #000 !important;
} }
.el-collapse-item__header{
background-color: #000!important; .el-collapse-item__header {
background-color: #000 !important;
color: #ddd; color: #ddd;
border-bottom-color:#5a5a5a; border-bottom-color: #5a5a5a;
padding-left: 1px; padding-left: 1px;
min-height: 40px; min-height: 40px;
height: auto; height: auto;
@ -1114,35 +1123,42 @@ export default {
padding-top: 6px; padding-top: 6px;
padding-bottom: 6px; padding-bottom: 6px;
} }
.el-collapse-item__arrow{
.el-collapse-item__arrow {
align-self: flex-start; align-self: flex-start;
margin-top: 2px; margin-top: 2px;
line-height: 20px; line-height: 20px;
} }
} }
.sr-wrapper{
::v-deep .el-dialog{ .sr-wrapper {
::v-deep .el-dialog {
background: #fff !important; background: #fff !important;
border: 1px solid #ddd; border: 1px solid #ddd;
// color: #ddd; // color: #ddd;
.el-dialog__title{ .el-dialog__title {
color:#fff; color: #fff;
} }
} }
::v-deep .sr-dialog-container{
::v-deep .sr-dialog-container {
margin-top: 50px !important; margin-top: 50px !important;
width:75%; width: 75%;
height:80%; height: 80%;
} }
::v-deep .el-dialog__body{
padding: 10px; ::v-deep .el-dialog__body {
height: calc(100% - 50px); padding: 10px;
height: calc(100% - 50px);
} }
.el-dialog__header{
.el-dialog__header {
position: relative; position: relative;
} }
.sr-full-dialog-container{
::v-deep .is-fullscreen .el-dialog__body{ .sr-full-dialog-container {
::v-deep .is-fullscreen .el-dialog__body {
height: calc(100% - 50px); height: calc(100% - 50px);
} }
} }
@ -1150,25 +1166,29 @@ export default {
} }
</style> </style>
<style> <style>
.instance_frame_wrapper{ .instance_frame_wrapper {
min-width: 120px; min-width: 120px;
background-color: #2c2c2c; background-color: #2c2c2c;
border: 1px solid #2c2c2c; border: 1px solid #2c2c2c;
padding: 5px; padding: 5px;
} }
.frame_list{
.frame_list {
max-height: 500px; max-height: 500px;
overflow-y: auto; overflow-y: auto;
} }
.instance_frame_wrapper ::-webkit-scrollbar { .instance_frame_wrapper ::-webkit-scrollbar {
width: 7px; width: 7px;
height: 7px; height: 7px;
} }
.instance_frame_wrapper ::-webkit-scrollbar-thumb {
.instance_frame_wrapper ::-webkit-scrollbar-thumb {
border-radius: 10px; border-radius: 10px;
background: #d0d0d0; background: #d0d0d0;
} }
.frame_content{
.frame_content {
height: 50px; height: 50px;
padding: 5px; padding: 5px;
display: flex; display: flex;
@ -1177,6 +1197,7 @@ export default {
font-size: 12px; font-size: 12px;
border: 1px solid #404040; border: 1px solid #404040;
} }
.frame_content:hover { .frame_content:hover {
/* font-weight: bold; */ /* font-weight: bold; */
/* box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); */ /* box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); */
@ -1185,6 +1206,7 @@ export default {
border-color: #213a54 !important; border-color: #213a54 !important;
background-color: #213a54; background-color: #213a54;
} }
.frame_content_active { .frame_content_active {
border-color: #213a54 !important; border-color: #213a54 !important;
background-color: #213a54; background-color: #213a54;
@ -1197,6 +1219,7 @@ export default {
line-height: 1; line-height: 1;
flex: 0 0 auto; flex: 0 0 auto;
} }
.patient-info-popper { .patient-info-popper {
font-size: 12px; font-size: 12px;
color: #ddd; color: #ddd;
@ -1221,7 +1244,7 @@ export default {
line-height: 18px; line-height: 18px;
} }
.patient-info-popper .patient-info-row + .patient-info-row { .patient-info-popper .patient-info-row+.patient-info-row {
margin-top: 4px; margin-top: 4px;
} }

View File

@ -0,0 +1,204 @@
import { dcmUpload } from '@/utils/dcmUpload/dcmUpload'
import dcmjs from '@/utils/dcmUpload/dcmjs'
import { deleteImageCache, getAnonymizeInfo, updateModality } from '@/api/reading'
import * as dicomParser from 'dicom-parser'
import * as cornerstone from 'cornerstone-core'
import * as cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader'
var config = {
maxWebWorkers: 4,
startWebWorkersOnDemand: true,
taskConfiguration: {
decodeTask: {
initializeCodecsOnStartup: false,
},
},
}
cornerstoneWADOImageLoader.webWorkerManager.initialize(config)
cornerstoneWADOImageLoader.external.dicomParser = dicomParser
cornerstoneWADOImageLoader.external.cornerstone = cornerstone
import Vue from 'vue'
function dicomToPng(imageId, width, height) {
return new Promise((resolve) => {
cornerstone.loadImage(imageId).then(async (image) => {
let canvas = document.createElement('canvas')
canvas.width = (width * 60) / height
canvas.height = 60
if (image) {
cornerstone.renderToCanvas(canvas, image)
// 将 Canvas 图像对象转换为 PNG 格式
let blob = await canvasToBlob(canvas)
resolve(blob)
} else {
resolve()
}
})
}).catch((reason) => {
reason()
})
}
function canvasToBlob(canvas) {
return new Promise((resolve) => {
canvas.toBlob((blob) => {
resolve(blob)
})
})
}
async function UpdateModality(params) {
try {
let res = await updateModality(params)
if (res.IsSuccess) {
return true
}
return false
} catch (err) {
console.log(err)
return false
}
}
async function getAnonymize(VisitTaskId, SubjectVisitId) {
try {
let params = {}
if (VisitTaskId) {
params.VisitTaskId = VisitTaskId
} else {
params.SubjectVisitId = SubjectVisitId
}
let res = await getAnonymizeInfo(params)
if (res.IsSuccess) {
return res.Result
}
return false
} catch (err) {
console.log(err)
return false
}
}
async function deleteCache(path) {
try {
let params = {
path
}
let res = await deleteImageCache(params)
if (res.IsSuccess) {
return true
}
return false
} catch (err) {
console.log(err)
return false
}
}
/**
* 0002,0003 MediaStorageSOPInstanceUID
* 0008,0018 SOPInstanceUID
* 0020,000D StudyInstanceUID
* 0020,000E SeriesInstanceUID
*
*/
export async function changeFile(key, file, study, image) {
// return console.log(study, 'study')
let dataset = dcmjs.data.DicomMessage.readFile(image.data.byteArray.buffer)
// console.log(dataset, 'dataset')
let MediaStorageSOPInstanceUID = dataset.meta['00020003'].Value[0]
let SOPInstanceUID = dataset.dict['00080018'].Value[0]
let StudyInstanceUID = dataset.dict['0020000D'].Value[0]
let SeriesInstanceUID = dataset.dict['0020000E'].Value[0]
// let Modality = dataset.dict['00080060'].Value[0]
let { visitTaskId, visitTaskList = [], studyList, studyIndex, seriesIndex, seriesList, currentSeriesIndex } = study
let series = key === 'IR' ? studyList[studyIndex].SeriesList[seriesIndex] : seriesList[currentSeriesIndex]
console.log(series, 'seriesId')
let index = visitTaskList.findIndex(i => i.VisitTaskId === visitTaskId)
let config = null
if (key === 'IR') {
config = await getAnonymize(visitTaskId)
} else {
config = await getAnonymize(null, series.subjectVisitId)
}
if (!config) return false
config.AnonymizeFixedList.push({
Element: '0003',
Group: '0002',
ReplaceValue: MediaStorageSOPInstanceUID,
Id: 'MediaStorageSOPInstanceUID'
})
config.AnonymizeFixedList.push({
Element: '0018',
Group: '0008',
ReplaceValue: SOPInstanceUID,
Id: 'SOPInstanceUID'
})
config.AnonymizeFixedList.push({
Element: '000D',
Group: '0020',
ReplaceValue: StudyInstanceUID,
Id: 'StudyInstanceUID'
})
config.AnonymizeFixedList.push({
Element: '000E',
Group: '0020',
ReplaceValue: SeriesInstanceUID,
Id: 'SeriesInstanceUID'
})
let uploadBatchId = Vue.prototype.$guid()
let path = image.imageId.split(`wadouri:${Vue.prototype.OSSclientConfig.basePath}`)[1].split("?")[0]
let res = await dcmUpload(
{
path: path,
file: file,
speed: true,
},
config,
(percentage, checkpoint, lastPer) => {
},
{
fileName: file.name,
fileSize: file.size,
fileType: 'application/dicom',
uploadBatchId: uploadBatchId,
batchDataType: 1,
// trialId: params.trialId,
subjectId: key === 'IR' ? localStorage.getItem("subjectId") : series.subjectId,
subjectVisitId: key === 'IR' ? visitTaskList[index].VisitId : series.subjectVisitId,
}
)
if (!res || !res.url) return false
let Modality = res.Modality
let fileId =
cornerstoneWADOImageLoader.wadouri.fileManager.add(
file
)
let blob = await dicomToPng(
fileId,
image.columns,
image.rows
)
let thumbnailPath = series.previewImageUrl.split(Vue.prototype.OSSclientConfig.basePath)[1]
let OSSclient = Vue.prototype.OSSclient
try {
let seriesRes = await OSSclient.put(thumbnailPath, blob, {
fileName: thumbnailPath.split('/')[thumbnailPath.split('/').length - 1],
fileSize: blob.size,
fileType: 'image/jpeg',
uploadBatchId: uploadBatchId,
batchDataType: 2,
// trialId: params.trialId,
subjectId: key === 'IR' ? localStorage.getItem("subjectId") : series.subjectId,
subjectVisitId: key === 'IR' ? visitTaskList[index].VisitId : series.subjectVisitId,
})
console.log(seriesRes, 'seriesRes')
if (!seriesRes || !seriesRes.url) return false
res = await Promise.all([deleteCache(path), deleteCache(thumbnailPath)])
if (!res) return false
let params = {
Modality,
SeriesId: series.seriesId,
IsCRCReplace: key === 'IR' ? false : true
}
res = await UpdateModality(params)
if (!res) return false
window.location.reload(true)
} catch (e) {
console.log(e)
}
}

View File

@ -105,6 +105,7 @@
<el-button icon="el-icon-delete" :title="$t('trials:uploadedDicoms:action:delete')" circle <el-button icon="el-icon-delete" :title="$t('trials:uploadedDicoms:action:delete')" circle
:disabled="!isAfresh && data.SubmitState === 2 && data.SubmitTime && moment(data.SubmitTime).isAfter(moment(scope.row.UploadedTime))" :disabled="!isAfresh && data.SubmitState === 2 && data.SubmitTime && moment(data.SubmitTime).isAfter(moment(scope.row.UploadedTime))"
@click="handleDeleteStudy(scope.row)" /> @click="handleDeleteStudy(scope.row)" />
<el-button icon="el-icon-upload2" title="上传" circle @click="upload(scope.row)" />
<!-- <el-button--> <!-- <el-button-->
<!-- icon="el-icon-toilet-paper"--> <!-- icon="el-icon-toilet-paper"-->
<!-- circle--> <!-- circle-->
@ -423,7 +424,8 @@
<DicomPreview :uid="uid" :studyList="uploadQueues" /> <DicomPreview :uid="uid" :studyList="uploadQueues" />
</el-dialog> </el-dialog>
<!--pet-ct临床数据上传--> <!--pet-ct临床数据上传-->
<el-dialog v-if="petVisible" :show-close="true" :close-on-click-modal="false" :visible.sync="petVisible" append-to-body> <el-dialog v-if="petVisible" :show-close="true" :close-on-click-modal="false" :visible.sync="petVisible"
append-to-body>
<uploadPetClinicalData :subject-visit-id="data.Id" :data="data" :studyData="studyData" :allow-add-or-edit="true" <uploadPetClinicalData :subject-visit-id="data.Id" :data="data" :studyData="studyData" :allow-add-or-edit="true"
@getStudyInfo="getStudyInfo" /> @getStudyInfo="getStudyInfo" />
</el-dialog> </el-dialog>
@ -650,6 +652,7 @@ export default {
BodyPart: {}, BodyPart: {},
isClose: false, isClose: false,
StudyInstanceUID: null
} }
}, },
computed: { computed: {
@ -682,6 +685,10 @@ export default {
this.OSSclient.close() this.OSSclient.close()
}, },
methods: { methods: {
upload(row) {
this.StudyInstanceUID = row.StudyInstanceUid
this.$refs.pathClear.click()
},
handleDragover(e) { handleDragover(e) {
e.stopPropagation() e.stopPropagation()
e.preventDefault() e.preventDefault()
@ -1274,11 +1281,12 @@ export default {
verifyStudy() { verifyStudy() {
this.btnLoading = true this.btnLoading = true
var studyList = [] var studyList = []
let scope = this
this.selectArr.forEach((item) => { this.selectArr.forEach((item) => {
item.dicomInfo.uploadFileSize = 0 item.dicomInfo.uploadFileSize = 0
if (!item.uploadState.selected) { if (!item.uploadState.selected) {
studyList.push({ studyList.push({
studyInstanceUid: item.dicomInfo.studyUid, studyInstanceUid: scope.StudyInstanceUID ? scope.StudyInstanceUID : item.dicomInfo.studyUid,
studyDate: item.dicomInfo.studyTime, studyDate: item.dicomInfo.studyTime,
}) })
} }
@ -1293,6 +1301,8 @@ export default {
batchVerifyStudyAllowUpload(param) batchVerifyStudyAllowUpload(param)
.then(async (res) => { .then(async (res) => {
var messageArr = [] var messageArr = []
res.Result = []
console.log(res)
res.Result.forEach((item) => { res.Result.forEach((item) => {
const i = this.uploadQueues.findIndex( const i = this.uploadQueues.findIndex(
(value) => value.dicomInfo.studyUid === item.StudyInstanceUid (value) => value.dicomInfo.studyUid === item.StudyInstanceUid
@ -1475,8 +1485,21 @@ export default {
}) })
}, },
// //
archiveStudy(index, config) { archiveStudy(index, config = {}) {
var scope = this var scope = this
try {
if (!config.AnonymizeFixedList) config.AnonymizeFixedList = []
config.AnonymizeFixedList.push(
{
Element: '000D',
Group: '0020',
ReplaceValue: scope.StudyInstanceUID,
Id: 'StudyInstanceUID'
}
)
} catch (err) {
console.log(err)
}
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
try { try {
preArchiveDicomStudy({ preArchiveDicomStudy({
@ -1506,7 +1529,7 @@ export default {
let t = setInterval(() => { let t = setInterval(() => {
dicomUploadInProgress({ dicomUploadInProgress({
trialId: scope.trialId, trialId: scope.trialId,
studyInstanceUid: dicomInfo.studyUid, studyInstanceUid: scope.StudyInstanceUID ? scope.StudyInstanceUID : dicomInfo.studyUid,
}).then((res) => { }) }).then((res) => { })
}, 5000) }, 5000)
scope.myInterval.push(t) scope.myInterval.push(t)
@ -1531,7 +1554,7 @@ export default {
dicomInfo.RadiopharmaceuticalStartTime, dicomInfo.RadiopharmaceuticalStartTime,
studyId: dicomInfo.studyId, studyId: dicomInfo.studyId,
studyInstanceUid: dicomInfo.studyUid, studyInstanceUid: scope.StudyInstanceUID ? scope.StudyInstanceUID : dicomInfo.studyUid,
studyTime: dicomInfo.studyTime, studyTime: dicomInfo.studyTime,
description: dicomInfo.description, description: dicomInfo.description,
seriesCount: dicomInfo.seriesNum, seriesCount: dicomInfo.seriesNum,
@ -1626,143 +1649,143 @@ export default {
// Record.FileCount++ // Record.FileCount++
// } // }
// else { // else {
let path = `/${params.trialId}/Image/${params.subjectId let path = `/${params.trialId}/Image/${params.subjectId
}/${params.subjectVisitId}/${dicomInfo.studyUid }/${params.subjectVisitId}/${dicomInfo.studyUid
}/${scope.getGuid( }/${scope.getGuid(
dicomInfo.studyUid + dicomInfo.studyUid +
v.seriesUid + v.seriesUid +
o.instanceUid + o.instanceUid +
params.trialId params.trialId
)}` )}`
if (scope.isClose) return if (scope.isClose) return
console.log(o.file) console.log(o.file)
let res = await dcmUpload( let res = await dcmUpload(
{ {
path: path, path: path,
file: o.file, file: o.file,
speed: true, speed: true,
}, },
config, config,
(percentage, checkpoint, lastPer) => { (percentage, checkpoint, lastPer) => {
dicomInfo.uploadFileSize += dicomInfo.uploadFileSize +=
checkpoint.size * (percentage - lastPer) checkpoint.size * (percentage - lastPer)
if ( if (
dicomInfo.uploadFileSize > dicomInfo.fileSize dicomInfo.uploadFileSize > dicomInfo.fileSize
) { ) {
dicomInfo.uploadFileSize = dicomInfo.fileSize dicomInfo.uploadFileSize = dicomInfo.fileSize
}
if (
Math.abs(
dicomInfo.uploadFileSize -
dicomInfo.fileSize
) < 5000
) {
dicomInfo.uploadFileSize = dicomInfo.fileSize
}
},
{
fileName: o.file.name,
fileSize: o.file.size,
fileType: 'application/dicom',
uploadBatchId: uploadBatchId,
batchDataType: 1,
trialId: params.trialId,
subjectId: params.subjectId,
subjectVisitId: params.subjectVisitId,
} }
) if (
if (!res || !res.url) { Math.abs(
params.failedFileCount++ dicomInfo.uploadFileSize -
} else { dicomInfo.fileSize
if (ii === 0 && o.modality !== 'SR') { ) < 5000
try { ) {
let fileId = dicomInfo.uploadFileSize = dicomInfo.fileSize
cornerstoneWADOImageLoader.wadouri.fileManager.add( }
o.file },
) {
let blob = await scope.dicomToPng( fileName: o.file.name,
fileId, fileSize: o.file.size,
o.imageColumns, fileType: 'application/dicom',
o.imageRows uploadBatchId: uploadBatchId,
batchDataType: 1,
trialId: params.trialId,
subjectId: params.subjectId,
subjectVisitId: params.subjectVisitId,
}
)
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.subjectId}/${params.subjectVisitId}/${dicomInfo.studyUid}/${v.seriesUid}.jpg` let thumbnailPath = `/${params.trialId}/Image/${params.subjectId}/${params.subjectVisitId}/${dicomInfo.studyUid}/${v.seriesUid}.jpg`
let OSSclient = scope.OSSclient let OSSclient = scope.OSSclient
let seriesRes = await OSSclient.put( let seriesRes = await OSSclient.put(
thumbnailPath, thumbnailPath,
blob, blob,
{ {
fileName: `${v.seriesUid}.jpg`, fileName: `${v.seriesUid}.jpg`,
fileSize: blob.size, fileSize: blob.size,
fileType: 'image/jpeg', fileType: 'image/jpeg',
uploadBatchId: uploadBatchId, uploadBatchId: uploadBatchId,
batchDataType: 2, batchDataType: 2,
trialId: params.trialId, trialId: params.trialId,
subjectId: params.subjectId, subjectId: params.subjectId,
subjectVisitId: params.subjectVisitId, subjectVisitId: params.subjectVisitId,
}
)
if (seriesRes && seriesRes.url) {
ImageResizePath = scope.$getObjectName(
seriesRes.url
)
} }
} catch (e) { )
console.log(e) if (seriesRes && seriesRes.url) {
ImageResizePath = scope.$getObjectName(
seriesRes.url
)
} }
} catch (e) {
console.log(e)
} }
} }
if (res && res.url) { }
instanceList.push({ if (res && res.url) {
studyInstanceUid: dicomInfo.studyUid, instanceList.push({
seriesInstanceUid: v.seriesUid, studyInstanceUid: scope.StudyInstanceUID ? scope.StudyInstanceUID : dicomInfo.studyUid,
SOPClassUID: o.SOPClassUID, seriesInstanceUid: v.seriesUid,
TransferSytaxUID: o.TransferSytaxUID, SOPClassUID: o.SOPClassUID,
MediaStorageSOPInstanceUID: TransferSytaxUID: o.TransferSytaxUID,
o.MediaStorageSOPInstanceUID, MediaStorageSOPInstanceUID:
MediaStorageSOPClassUID: o.MediaStorageSOPInstanceUID,
o.MediaStorageSOPClassUID, MediaStorageSOPClassUID:
sopInstanceUid: o.instanceUid, o.MediaStorageSOPClassUID,
instanceNumber: o.instanceNumber, sopInstanceUid: o.instanceUid,
instanceTime: o.instanceTime, instanceNumber: o.instanceNumber,
imageRows: o.imageRows, instanceTime: o.instanceTime,
imageColumns: o.imageColumns, imageRows: o.imageRows,
sliceLocation: o.sliceLocation, imageColumns: o.imageColumns,
sliceThickness: o.sliceThickness, sliceLocation: o.sliceLocation,
numberOfFrames: o.numberOfFrames, sliceThickness: o.sliceThickness,
pixelSpacing: o.pixelSpacing, numberOfFrames: o.numberOfFrames,
imagerPixelSpacing: o.imagerPixelSpacing, pixelSpacing: o.pixelSpacing,
frameOfReferenceUID: o.frameOfReferenceUID, imagerPixelSpacing: o.imagerPixelSpacing,
windowCenter: o.windowCenter, frameOfReferenceUID: o.frameOfReferenceUID,
windowWidth: o.windowWidth, windowCenter: o.windowCenter,
path: scope.$getObjectName(res.url), windowWidth: o.windowWidth,
FileSize: o.FileSize, path: scope.$getObjectName(res.url),
FileSize: o.FileSize,
PhotometricInterpretation: PhotometricInterpretation:
o.PhotometricInterpretation, o.PhotometricInterpretation,
BitsAllocated: o.BitsAllocated, BitsAllocated: o.BitsAllocated,
PixelRepresentation: o.PixelRepresentation, PixelRepresentation: o.PixelRepresentation,
RescaleIntercept: o.RescaleIntercept, RescaleIntercept: o.RescaleIntercept,
RescaleSlope: o.RescaleSlope, RescaleSlope: o.RescaleSlope,
ImagePositionPatient: o.ImagePositionPatient, ImagePositionPatient: o.ImagePositionPatient,
ImageOrientationPatient: ImageOrientationPatient:
o.ImageOrientationPatient, o.ImageOrientationPatient,
SequenceOfUltrasoundRegions: SequenceOfUltrasoundRegions:
o.SequenceOfUltrasoundRegions, o.SequenceOfUltrasoundRegions,
FrameTime: o.FrameTime, FrameTime: o.FrameTime,
CorrectedImage: o.CorrectedImage, CorrectedImage: o.CorrectedImage,
Units: o.Units, Units: o.Units,
DecayCorrection: o.DecayCorrection, DecayCorrection: o.DecayCorrection,
EncapsulatedDocument: o.EncapsulatedDocument, EncapsulatedDocument: o.EncapsulatedDocument,
}) })
o.myPath = scope.$getObjectName(res.url) o.myPath = scope.$getObjectName(res.url)
Record.Uploaded.push(name) Record.Uploaded.push(name)
dicomInfo.failedFileCount++ dicomInfo.failedFileCount++
Record.FileCount++ Record.FileCount++
} else { } else {
Record.Failed.push(name) Record.Failed.push(name)
Record.FileCount++ Record.FileCount++
} }
// } // }
resolve1() resolve1()
} catch (e) { } catch (e) {
@ -1781,7 +1804,7 @@ export default {
} }
} }
params.study.seriesList.push({ params.study.seriesList.push({
studyInstanceUid: dicomInfo.studyUid, studyInstanceUid: scope.StudyInstanceUID ? scope.StudyInstanceUID : dicomInfo.studyUid,
seriesInstanceUid: v.seriesUid, seriesInstanceUid: v.seriesUid,
seriesNumber: v.seriesNumber, seriesNumber: v.seriesNumber,
seriesTime: v.seriesTime, seriesTime: v.seriesTime,

View File

@ -52,7 +52,7 @@ module.exports = defineConfig({
}, },
'/api': { '/api': {
target: 'http://106.14.89.110:30000', target: 'http://192.168.3.99:6100',
// target: 'http://101.132.253.119:7010', // uat // target: 'http://101.132.253.119:7010', // uat
changeOrigin: true, changeOrigin: true,
secure: false, secure: false,