关键序列在标记完成后立即刷新显示
continuous-integration/drone/push Build is passing Details

main
wangxiaoshuang 2025-08-28 14:53:25 +08:00
parent c8ef378170
commit cc02fee7cb
9 changed files with 600 additions and 119 deletions

View File

@ -908,7 +908,118 @@ const actions = {
}
})
},
getKeySeries({ state }, obj) {
return new Promise(resolve => {
console.log('getKeySeries')
let { study, idx, visitTaskId } = obj
let data = null
var index = state.visitTaskList.findIndex(i => i.VisitTaskId === visitTaskId)
var keyImages = []
if (study) {
data = {}
const series = study.SeriesList && study.SeriesList
series.map(i => {
i.InstanceInfoList.map(k => {
keyImages.push({ instanceId: k.Id })
})
})
data.StudyId = study.StudyId
data.StudyCode = study.StudyCode
data.Modalities = study.Modalities
data.SeriesCount = study.SeriesCount
data.InstanceCount = study.InstanceCount
data.InstanceCount = study.InstanceCount
data.IsDicom = study.IsDicom
data.PreviewImageCount = 0
data.IsCriticalSequence = study.IsCriticalSequence
var seriesList = []
study.SeriesList.forEach((series, seriesIndex) => {
const imageIds = []
const instanceList = []
series.InstanceInfoList.forEach(instance => {
if (instance.NumberOfFrames && instance.NumberOfFrames > 1) {
if (study.IsCriticalSequence && instance.KeyFramesList.length > 0) {
instance.KeyFramesList.map(i => {
const imageId = `wadouri:${localStorage.getItem('location') !== 'USA' ? Vue.prototype.OSSclientConfig.basePath : Vue.prototype.OSSclientConfig.basePath}${instance.Path}?frame=${i}&instanceId=${instance.Id}&visitTaskId=${obj.visitTaskId}&idx=${idx}|${seriesIndex}|${index}`
imageIds.push(imageId)
})
} else {
for (let i = 0; i < instance.NumberOfFrames; i++) {
const imageId = `wadouri:${localStorage.getItem('location') !== 'USA' ? Vue.prototype.OSSclientConfig.basePath : Vue.prototype.OSSclientConfig.basePath}${instance.Path}?frame=${i}&instanceId=${instance.Id}&visitTaskId=${obj.visitTaskId}&idx=${idx}|${seriesIndex}|${index}`
imageIds.push(imageId)
}
}
instance.ImageId = `wadouri:${localStorage.getItem('location') !== 'USA' ? Vue.prototype.OSSclientConfig.basePath : Vue.prototype.OSSclientConfig.basePath}${instance.Path}?frame=${0}&instanceId=${instance.Id}&visitTaskId=${obj.visitTaskId}&idx=${idx}|${seriesIndex}|${index}`
} else {
const imageId = `wadouri:${localStorage.getItem('location') !== 'USA' ? Vue.prototype.OSSclientConfig.basePath : Vue.prototype.OSSclientConfig.basePath}${instance.Path}?instanceId=${instance.Id}&visitTaskId=${obj.visitTaskId}&idx=${idx}|${seriesIndex}|${index}`
imageIds.push(imageId)
instance.ImageId = imageId
}
if (!study.IsCriticalSequence && series.IsBeMark) {
const i = keyImages.findIndex(i => i.instanceId === instance.Id)
if (i > -1) {
keyImages[i].idx = idx
keyImages[i].seriesIndex = seriesIndex
}
}
instanceList.push(instance.Id)
})
seriesList.push({
isDicom: study.IsDicom,
imageIds: imageIds,
instanceInfoList: series.InstanceInfoList,
instanceList: instanceList,
seriesId: series.Id,
imageIdIndex: 0,
seriesUid: series.SeriesInstanceUid,
seriesNumber: series.SeriesNumber,
sliceThickness: series.SliceThickness,
modality: series.Modality,
description: series.Description,
previewImageUrl: study.IsDicom ? series.ImageResizePath ? Vue.prototype.OSSclientConfig.basePath + series.ImageResizePath : `/api/series/preview/${series.Id}` : Vue.prototype.OSSclientConfig.basePath + `${series.NoneDicomFileFirstFile}`,
instanceCount: series.InstanceCount,
instanceHtmlPathList: series.InstanceHtmlPathList,
prefetchInstanceCount: 0,
loadStatus: false,
imageloadedArr: series.InstanceInfoList,
studyId: study.StudyId,
studyIndex: idx,
seriesIndex: seriesIndex,
studyCode: study.StudyCode,
taskBlindName: state.visitTaskList[index].TaskBlindName,
visitTaskId: obj.visitTaskId,
readingTaskState: state.visitTaskList[index].ReadingTaskState,
isBaseLineTask: state.visitTaskList[index].IsBaseLineTask,
isCurrentTask: state.visitTaskList[index].IsCurrentTask,
isExistsClinicalData: state.visitTaskList[index].IsExistsClinicalData,
isLoading: false,
isBeMark: series.IsBeMark,
ww: series.WindowWidth,
wc: series.WindowCenter,
isExistMutiFrames: series.IsExistMutiFrames
})
})
data.SeriesList = seriesList
}
let stateIndex = state.visitTaskList[index].StudyList.findIndex(item => item.IsCriticalSequence)
console.log(stateIndex, 'stateIndex')
if (data) {
if (!!~stateIndex) {
state.visitTaskList[index].StudyList.splice(stateIndex, 1, data)
} else {
state.visitTaskList[index].StudyList.unshift(data)
}
} else {
if (!!~stateIndex) {
state.visitTaskList[index].StudyList.splice(stateIndex, 1)
}
}
state.visitTaskList[index].studyListInit = true
state.visitTaskList[index].KeyImages = keyImages
// sessionStorage.setItem('visitTaskList', state.visitTaskList.length > 0 ? JSON.stringify(state.visitTaskList) : '')
resolve(data)
})
},
updateStudyList({ state }, obj) {
return new Promise(resolve => {
var index = state.visitTaskList.findIndex(i => i.VisitTaskId === obj.visitTaskId)

View File

@ -292,7 +292,8 @@ export default {
],
scrollSyncInfo: { offset: 0 },
hideMeasureArr: []
hideMeasureArr: [],
IsCriticalSequence: false
}
},
@ -542,7 +543,7 @@ export default {
mouseUp(e) {
// console.log('mouseUp')
if (this.readingTaskState >= 2) return
if (this.readingTaskState >= 2 || this.IsCriticalSequence) return
this.image = e.detail.image
this.getToolStateInfo(e)
},
@ -566,6 +567,10 @@ export default {
e.stopPropagation()
e.preventDefault()
}
} else if (this.IsCriticalSequence) {
e.stopImmediatePropagation()
e.stopPropagation()
e.preventDefault()
}
},
pointNearTool(e) {
@ -909,7 +914,7 @@ export default {
})
},
setMeasureDataVisible() {
if (this.readingTaskState >= 2) return
if (this.readingTaskState >= 2 || this.IsCriticalSequence) return
var element = cornerstone.getEnabledElement(this.canvas)
var { imageId } = element.image
const imageInfo = this.getInstanceInfo(imageId)
@ -974,6 +979,7 @@ export default {
}
},
mouseClick(e) {
if (this.IsCriticalSequence) return false
const { element, currentPoints, image, viewport } = e.detail
var imageId = image.imageId
const imageInfo = this.getInstanceInfo(imageId)
@ -1031,6 +1037,12 @@ export default {
},
loadImageStack(dicomSeries) {
return new Promise(resolve => {
var idx = this.visitTaskList.findIndex(item => item.VisitTaskId === dicomSeries.visitTaskId)
if (!!~idx) {
this.IsCriticalSequence = this.visitTaskList[idx].StudyList[dicomSeries.studyIndex].IsCriticalSequence
} else {
this.IsCriticalSequence = false
}
this.isCurrentTask = dicomSeries.isCurrentTask
this.isBaseline = dicomSeries.isBaseLineTask
this.readingTaskState = dicomSeries.readingTaskState
@ -1329,7 +1341,7 @@ export default {
this.setMarkers()
},
onMeasurementcompleted(e) {
if (this.readingTaskState >= 2) return
if (this.readingTaskState >= 2 || this.IsCriticalSequence) return
var element = cornerstone.getEnabledElement(this.canvas)
var viewport = element.viewport
@ -1453,7 +1465,7 @@ export default {
onMeasurementmodified(e) {
//
// console.log('modified')
if (this.readingTaskState >= 2) return
if (this.readingTaskState >= 2 || this.IsCriticalSequence) return
const { measurementData, toolType } = e.detail
var element = cornerstone.getEnabledElement(this.canvas)
var viewport = element.viewport

View File

@ -280,6 +280,18 @@
<div class="text">{{ tool.text }}</div>
</div> -->
</div>
<el-tooltip v-if="CriterionType === 2 && studyList.length > 0" class="item" effect="dark" :content="$t('trials:lugano:button:fusion')" placement="bottom">
<div class="tool-wrapper">
<div
class="icon"
@click.prevent="handleFusion"
>
<svg-icon icon-class="fusion" class="svg-icon" />
</div>
<!-- 截屏 -->
<div class="text">{{ $t('trials:lugano:button:fusion') }}</div>
</div>
</el-tooltip>
<div class="tool-frame">
<!-- 第一帧 -->
<el-tooltip class="item" effect="dark" :content="$t('trials:dicom-show:firstframe')" placement="bottom">
@ -410,7 +422,14 @@
:is-show="isShow"
:is-reading-show-subject-info="isReadingShowSubjectInfo"
/>
<LuganoQuestionList
v-else-if="CriterionType === 2"
ref="measurementList"
:question-form-change-state="questionFormChangeState"
:question-form-change-num="questionFormChangeNum"
:is-show="isShow"
:is-reading-show-subject-info="isReadingShowSubjectInfo"
/>
<RecistQuestionList
v-else-if="CriterionType === 1"
ref="measurementList"
@ -531,6 +550,154 @@
</div>
<SignForm ref="signForm" :sign-code-enum="signCode" @closeDialog="closeSignDialog" />
</el-dialog>
<el-dialog
v-if="fusionSeries.visible"
:visible.sync="fusionSeries.visible"
:close-on-click-modal="false"
:title="$t('trials:lugano:dialogTitle:fusion') "
width="850px"
>
<el-form ref="fusionForm" :model="fusionForm" label-width="120px" label-position="left">
<!-- 任务名称 -->
<el-form-item
:label="$t('trials:lugano:fusionDialog:label:taskBlindName')"
prop="taskBlindName"
>
<el-input
v-model="fusionForm.taskBlindName"
:disabled="true"
style="width:200px"
/>
</el-form-item>
<!-- 检查 -->
<el-form-item
:label="$t('trials:lugano:fusionDialog:label:study')"
prop="studyID"
:rules="[
{ required: true, message: $t('common:ruleMessage:select'), trigger: 'blur' }
]"
>
<el-select
v-model="fusionForm.studyID"
clearable
@change="handleStudyIdChange"
>
<el-option
v-for="item in studyList"
:key="item.StudyId"
:label="item.StudyCode"
:value="item.StudyId"
/>
</el-select>
</el-form-item>
<!-- 融合图像 -->
<label style="color: #dfdfdf;line-height: 36px;vertical-align: middle;font-size: 14px;">{{ $t('trials:lugano:fusionDialog:label:fusionImages') }}</label>
<div style="display: flex;flex-direction: row;justify-content: space-between;margin-bottom:10px;" class="series-table">
<div style="border: 1px solid #5e5e5e;border-radius: 5px;padding: 0 10px;">
<!-- CT/MRI序列 -->
<h3 style="color:#ddd">{{ $t('trials:lugano:fusionDialog:label:ctSeries') }}</h3>
<el-table
ref="ctSeries"
:data="ctSeries"
:show-header="false"
style="width: 380px"
size="mini"
height="300"
:default-sort="{prop: 'instanceCount', order: 'descending'}"
@selection-change="handleCTSelectionChange"
@select="handleCTSelect"
>
<el-table-column
type="selection"
width="30"
/>
<el-table-column
label=""
width="100"
>
<template slot-scope="scope">
<el-image
style="width: 60px; height: 60px"
:src="scope.row.previewImageUrl"
fit="fit"
crossorigin="anonymous"
/>
</template>
</el-table-column>
<el-table-column
label=""
min-width="50"
show-overflow-tooltip
>
<template slot-scope="scope">
<div>#{{ scope.row.seriesNumber }} </div>
<div>{{ scope.row.modality }}: {{ scope.row.instanceCount }} images T: {{ parseFloat(scope.row.sliceThickness).toFixed(digitPlaces) }}</div>
<div>{{ scope.row.description }}</div>
</template>
</el-table-column>
</el-table>
</div>
<div style="border: 1px solid #5e5e5e;border-radius: 5px;padding: 0 10px;">
<!-- PET序列 -->
<h3 style="color:#ddd">{{ $t('trials:lugano:fusionDialog:label:ptSeries') }}</h3>
<el-table
ref="petSeries"
:data="petSeries"
:show-header="false"
style="width: 380px"
size="mini"
height="300"
@selection-change="handlePTSelectionChange"
@select="handlePTSelect"
>
<el-table-column
type="selection"
width="30"
/>
<el-table-column
label=""
width="100"
>
<template slot-scope="scope">
<el-image
style="width: 60px; height: 60px"
:src="scope.row.previewImageUrl"
fit="fit"
crossorigin="anonymous"
/>
</template>
</el-table-column>
<el-table-column
label=""
min-width="50"
show-overflow-tooltip
>
<template slot-scope="scope">
<div>#{{ scope.row.seriesNumber }} </div>
<div>{{ scope.row.modality }}: {{ scope.row.instanceCount }} images T: {{ parseFloat(scope.row.sliceThickness).toFixed(digitPlaces) }}</div>
<div>{{ scope.row.description }}</div>
</template>
</el-table-column>
</el-table>
</div>
</div>
<el-form-item style="text-align:right;">
<!-- 取消 -->
<el-button size="mini" @click="fusionSeries.visible = false">{{ $t('common:button:cancel') }}</el-button>
<!-- 确认 -->
<el-button
type="primary"
size="mini"
@click="handleSubmitFusionSeries"
>
{{ $t('common:button:confirm') }}</el-button>
</el-form-item>
</el-form>
</el-dialog>
</div>
</template>
<script>
@ -553,6 +720,7 @@ import MRecistQuestionList from './MRecist/QuestionList'
import RecistBMQuestionList from './RecistBM/QuestionList'
import IRecistQuestionList from './IRecist/QuestionList'
import PCWGQuestionList from './PCWG/QuestionList'
import LuganoQuestionList from './Lugano/QuestionList'
import CustomWwwcForm from './CustomWwwcForm'
import Manuals from './Manuals'
import Hotkeys from './Hotkeys'
@ -581,6 +749,7 @@ export default {
PCWGQuestionList,
RecistBMQuestionList,
IRecistQuestionList,
LuganoQuestionList,
SignForm
},
props: {
@ -715,7 +884,23 @@ export default {
taskId: '',
signVisible: false,
signCode: null,
currentUser: zzSessionStorage.getItem('userName')
currentUser: zzSessionStorage.getItem('userName'),
fusionSeries: { visible: false },
fusionForm: {
studyID: '',
visitTaskId: '',
readingTaskState: 2,
taskBlindName: '',
subjectCode: '',
isBaseLineTask: null,
isCurrentTask: null
},
ctSeriesInfo: {},
petSeriesInfo: {},
ctSeries: [],
petSeries: [],
petctWindow: null,
}
},
@ -1258,7 +1443,6 @@ export default {
visitTaskId = this.visitTaskList[i].VisitTaskId
}
this.getFirstSeries(activeSeries, visitTaskId).then(async baseSerires => {
console.log(activeSeries)
this.seriesStack = [baseSerires, activeSeries]
this.currentDicomCanvasIndex = 1
store.dispatch('reading/setLastCanvasTaskId', activeSeries.visitTaskId)
@ -1725,9 +1909,14 @@ export default {
enter(e, toolName) {
var i = this.measuredTools.findIndex(item => item.toolName === toolName)
if (i === -1) return
const index = this.visitTaskList.findIndex(i => i.VisitTaskId === this.activeSeries.visitTaskId)
let isCriticalSequence = false
if (!!~index) {
isCriticalSequence = this.visitTaskList[index].StudyList[this.activeSeries.studyIndex].IsCriticalSequence
}
var isCurrentTask = this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].isCurrentTask
var readingTaskState = this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].readingTaskState
if (!isCurrentTask || readingTaskState >= 2) {
if (!isCurrentTask || readingTaskState >= 2 || isCriticalSequence) {
this.measuredTools[i].isDisabled = true
e.target.style.cursor = 'not-allowed'
if (this.activeTool) {
@ -2052,6 +2241,120 @@ export default {
}
}
},
handleFusion() {
// https
// if (!window.isSecureContext) {
// this.$alert(this.$t('components:uploadvideo:message:xf3'))
// return
// }
// //
// if (!window.crossOriginIsolated) {
// this.$alert(this.$t('components:uploadvideo:message:xf4'))
// return
// }
if (this.imageQualityIssues && parseInt(this.imageQualityIssues) === 6) {
this.$alert(this.$t('trials:lugano:message:fusionWarn'))
return
}
const i = this.activeTaskInfo.visitTaskIndex
if (i < 0) return
if (this.studyList.length > 0) {
this.fusionForm.studyID = this.studyList[0].StudyId
this.handleStudyIdChange(this.studyList[0].StudyId)
} else {
this.fusionForm.studyID = ''
this.ctSeries = []
this.petSeries = []
}
this.fusionForm.visitTaskId = this.visitTaskList[i].VisitTaskId
this.fusionForm.readingTaskState = this.visitTaskList[i].ReadingTaskState
this.fusionForm.taskBlindName = this.visitTaskList[i].TaskBlindName
this.fusionForm.subjectCode = this.visitTaskList[i].SubjectCode
this.fusionForm.isBaseLineTask = this.visitTaskList[i].IsBaseLineTask
this.fusionForm.isCurrentTask = this.visitTaskList[i].IsCurrentTask
this.fusionSeries.visible = true
},
handleStudyIdChange(v) {
var i = this.studyList.findIndex(i => i.StudyId === v)
if (i === -1) {
this.ctSeries = []
this.petSeries = []
} else {
var seriesList = this.studyList[i].SeriesList
var series = seriesList.filter(series => series.modality === 'CT')
this.ctSeries = series.sort((a, b) => b.instanceCount - a.instanceCount)
series = seriesList.filter(series => series.modality === 'PT')
this.petSeries = series.sort((a, b) => b.instanceCount - a.instanceCount)
}
},
handleCTSelectionChange(rows) {
if (rows.length > 0) {
this.ctSeriesInfo = rows[0]
} else {
this.ctSeriesInfo = {}
}
},
handleCTSelect(selection) {
if (selection.length > 1) {
const del_row = selection.shift()
this.$refs.ctSeries.toggleRowSelection(del_row, false)
}
},
handlePTSelect(selection) {
if (selection.length > 1) {
const del_row = selection.shift()
this.$refs.petSeries.toggleRowSelection(del_row, false)
}
},
handlePTSelectionChange(rows) {
if (rows.length > 0) {
this.petSeriesInfo = rows[0]
} else {
this.petSeriesInfo = {}
}
},
handleSubmitFusionSeries() {
this.$refs['fusionForm'].validate((valid) => {
if (!valid) return
if (Object.keys(this.ctSeriesInfo).length === 0 || Object.keys(this.petSeriesInfo).length === 0) {
// ''
this.$confirm(this.$t('trials:lugano:fusionDialog:message:checkSeries'), this.$t('trials:lugano:fusionDialog:warning'), {
showCancelButton: false,
type: 'warning'
}).then(() => {}).catch(() => {})
return
}
var count = Math.abs(this.ctSeriesInfo.instanceCount - this.petSeriesInfo.instanceCount)
if (count > 10) {
// ', ?'
this.$confirm(this.$t('trials:lugano:fusionDialog:message:validSeries'), this.$t('trials:lugano:fusionDialog:warning'), {
type: 'warning'
}).then(() => {
this.fusion()
}).catch(() => {})
} else {
this.fusion()
}
})
},
fusion() {
// sessionStorage.setItem('ctSeriesInfo', JSON.stringify(this.ctSeriesInfo))
// sessionStorage.setItem('petSeriesInfo', JSON.stringify(this.petSeriesInfo))
this.fusionSeries.visible = false
var token = getToken()
var trialReadingCriterionId = this.$router.currentRoute.query.TrialReadingCriterionId
var trialId = this.$router.currentRoute.query.trialId
var digitPlaces = Number(localStorage.getItem('digitPlaces'))
var path = `/petct?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${trialId}&visitTaskId=${this.fusionForm.visitTaskId}&taskBlindName=${this.fusionForm.taskBlindName}&subjectCode=${this.fusionForm.subjectCode}&readingTaskState=${this.fusionForm.readingTaskState}&isBaseLineTask=${this.fusionForm.isBaseLineTask}&isCurrentTask=${this.fusionForm.isCurrentTask}&isReadingShowSubjectInfo=${this.isReadingShowSubjectInfo}&studyId=${this.fusionForm.studyID}&ctseriesId=${this.ctSeriesInfo.seriesId}&ptseriesId=${this.petSeriesInfo.seriesId}&digitPlaces=${digitPlaces}&TokenKey=${token}&lang=${this.$i18n.locale}`
var routeData = this.$router.resolve({ path })
if (this.petctWindow) {
this.petctWindow.close()
}
this.petctWindow = window.open(routeData.href, '_blank')
this.$nextTick(() => { this.petct.visible = true })
}
}
}
</script>

View File

@ -553,6 +553,7 @@ export default {
.then(async res => {
if (res.IsSuccess) {
DicomEvent.$emit('getReportInfo', true)
DicomEvent.$emit('getKeySeries')
}
loading.close()
resolve()
@ -862,6 +863,7 @@ export default {
}
deleteReadingRowAnswer(param)
.then(async res => {
DicomEvent.$emit('getKeySeries')
resolve()
}).catch(() => {
})
@ -2003,6 +2005,7 @@ export default {
this.$emit('close')
DicomEvent.$emit('getReportInfo', true)
DicomEvent.$emit('setMeasuredToolsPassive')
DicomEvent.$emit('getKeySeries')
}
loading.close()
} catch (e) {
@ -2106,6 +2109,7 @@ export default {
this.$emit('close', { lesionType: this.lesionType, rowIndex: this.rowIndex, visitTaskId: this.visitTaskId })
}
DicomEvent.$emit('refreshStudyListMeasureData')
DicomEvent.$emit('getKeySeries')
loading.close()
} catch (e) {
loading.close()

View File

@ -443,6 +443,7 @@ export default {
.then(async res => {
if (res.IsSuccess) {
DicomEvent.$emit('getReportInfo', true)
DicomEvent.$emit('getKeySeries')
}
loading.close()
resolve()
@ -738,6 +739,7 @@ export default {
}
deleteReadingRowAnswer(param)
.then(async res => {
DicomEvent.$emit('getKeySeries')
resolve()
}).catch(() => {
})
@ -1367,6 +1369,7 @@ export default {
this.$emit('close')
DicomEvent.$emit('getReportInfo', true)
DicomEvent.$emit('setMeasuredToolsPassive')
DicomEvent.$emit('getKeySeries')
}
loading.close()
} catch (e) {
@ -1454,6 +1457,7 @@ export default {
DicomEvent.$emit('getReportInfo', true)
// ''
this.$message.success(this.$t('common:message:deletedSuccessfully'))
DicomEvent.$emit('getKeySeries')
}
} else {
//

View File

@ -440,6 +440,7 @@ export default {
.then(async res => {
if (res.IsSuccess) {
DicomEvent.$emit('getReportInfo', true)
DicomEvent.$emit('getKeySeries')
}
resolve()
}).catch(() => {
@ -751,6 +752,7 @@ export default {
this.$emit('close')
DicomEvent.$emit('getReportInfo', true)
DicomEvent.$emit('setMeasuredToolsPassive')
DicomEvent.$emit('getKeySeries')
loading.close()
}
} catch (e) {
@ -820,6 +822,7 @@ export default {
DicomEvent.$emit('getMeasureData')
this.$emit('close', { lesionType: this.lesionType, rowIndex: this.rowIndex, visitTaskId: this.visitTaskId })
DicomEvent.$emit('getReportInfo', true)
DicomEvent.$emit('getKeySeries')
this.$message.success(this.$t('common:message:deletedSuccessfully'))
}
loading.close()

View File

@ -526,6 +526,7 @@ export default {
.then(async res => {
if (res.IsSuccess) {
DicomEvent.$emit('getReportInfo', true)
DicomEvent.$emit('getKeySeries')
}
loading.close()
resolve()
@ -791,6 +792,7 @@ export default {
}
deleteReadingRowAnswer(param)
.then(async res => {
DicomEvent.$emit('getKeySeries')
resolve()
}).catch(() => {
})
@ -1348,6 +1350,7 @@ export default {
this.$emit('close')
DicomEvent.$emit('getReportInfo', true)
DicomEvent.$emit('setMeasuredToolsPassive')
DicomEvent.$emit('getKeySeries')
}
loading.close()
} catch (e) {
@ -1432,6 +1435,7 @@ export default {
}
this.$emit('close', { lesionType: this.lesionType, rowIndex: this.rowIndex, visitTaskId: this.visitTaskId })
DicomEvent.$emit('getReportInfo', true)
DicomEvent.$emit('getKeySeries')
// ''
this.$message.success(this.$t('common:message:deletedSuccessfully'))
}

View File

@ -483,6 +483,7 @@ export default {
.then(async res => {
if (res.IsSuccess) {
DicomEvent.$emit('getReportInfo', true)
DicomEvent.$emit('getKeySeries')
}
loading.close()
resolve()
@ -716,6 +717,7 @@ export default {
}
deleteReadingRowAnswer(param)
.then(async res => {
DicomEvent.$emit('getKeySeries')
resolve()
}).catch(() => {
})
@ -1035,6 +1037,7 @@ export default {
this.$emit('close')
DicomEvent.$emit('getReportInfo', true)
DicomEvent.$emit('setMeasuredToolsPassive')
DicomEvent.$emit('getKeySeries')
}
loading.close()
} catch (e) {
@ -1127,6 +1130,7 @@ export default {
this.$emit('close', { lesionType: this.lesionType, rowIndex: this.rowIndex, visitTaskId: this.visitTaskId })
}
DicomEvent.$emit('refreshStudyListMeasureData')
DicomEvent.$emit('getKeySeries')
loading.close()
} catch (e) {
console.log(e)

View File

@ -1,9 +1,11 @@
<template>
<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 }}
</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 }}
</h4>
<div class="ps">
@ -14,12 +16,11 @@
<!-- 关键序列 -->
{{ $t('trials:reading:title:keySeries') }}
</div>
<div
v-else
class="dicom-desc"
style="width: 150px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;"
>
<el-tooltip class="item" effect="dark" :content="`${study.StudyCode} ${study.Description?study.Description:''} ${study.Modalities} (${study.SeriesCount})`" placement="right">
<div v-else class="dicom-desc"
style="width: 150px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;">
<el-tooltip class="item" effect="dark"
:content="`${study.StudyCode} ${study.Description ? study.Description : ''} ${study.Modalities} (${study.SeriesCount})`"
placement="right">
<div>
<span>{{ study.StudyCode }} {{ study.Description }}</span>
<span> {{ study.Modalities }} ({{ study.SeriesCount }})</span>
@ -29,60 +30,50 @@
</div>
</template>
<div class="series">
<div
v-for="(series, i) in study.SeriesList"
:key="i"
style="position:relative;margin-top:5px;"
series-type="current"
@click="showSeriesImage(index,i,series)"
>
<div v-for="(series, i) in study.SeriesList" :key="i" style="position:relative;margin-top:5px;"
series-type="current" @click="showSeriesImage(index, i, series)">
<div
:class="{'series-active': i==seriesIndex && index === studyIndex}"
class="series-wrapper"
>
<el-image
class="image-preview"
:src="series.previewImageUrl"
fit="fill"
crossorigin="anonymous"
/>
<div :class="{ 'series-active': i == seriesIndex && index === studyIndex }" class="series-wrapper">
<el-image class="image-preview" :src="series.previewImageUrl" fit="fill" crossorigin="anonymous" />
<div class="image-desc">
<div class="flex-div">
<div style="width: 40px;display: flex;flex-direction: row;justify-content: space-between;">
<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">
<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 v-if="!series.isLoading" 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>
</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">
<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
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>
</div>
<div v-if="series.isExistMutiFrames && series.instanceCount > 1">
<el-popover
placement="right"
trigger="hover"
popper-class="instance_frame_wrapper"
>
<el-popover placement="right" trigger="hover" popper-class="instance_frame_wrapper">
<div class="frame_list">
<div
v-for="(instance, idx) in series.instanceInfoList"
:key="instance.Id"
<div v-for="(instance, idx) in series.instanceInfoList" :key="instance.Id"
class="frame_content"
:style="{'margin-bottom':idx<series.instanceInfoList.length-1? '5px':'0px'}"
@click.stop="showMultiFrames(index,series, i, instance)"
>
:style="{ 'margin-bottom': idx < series.instanceInfoList.length - 1 ? '5px' : '0px' }"
@click.stop="showMultiFrames(index, series, i, instance)">
<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>
<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>
</div>
</div>
@ -103,9 +94,11 @@
</p>
<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;" />
</el-tooltip>
@ -113,10 +106,10 @@
</div>
</div>
</div>
<div v-if="series.isDicom && series.prefetchInstanceCount>0 && series.prefetchInstanceCount<series.instanceCount * 100" style="width: 100%;">
<el-progress
:percentage="parseInt((series.prefetchInstanceCount / series.instanceCount).toFixed(2))"
/>
<div
v-if="series.isDicom && series.prefetchInstanceCount > 0 && series.prefetchInstanceCount < series.instanceCount * 100"
style="width: 100%;">
<el-progress :percentage="parseInt((series.prefetchInstanceCount / series.instanceCount).toFixed(2))" />
</div>
</div>
@ -126,24 +119,19 @@
</div>
<div class="sr-wrapper">
<el-dialog
:visible.sync="srDialogVisible"
:custom-class="isSrFullscreen?'sr-full-dialog-container':'sr-dialog-container'"
:show-close="false"
:close-on-click-modal="false"
:fullscreen="isSrFullscreen"
>
<el-dialog :visible.sync="srDialogVisible"
:custom-class="isSrFullscreen ? 'sr-full-dialog-container' : 'sr-dialog-container'" :show-close="false"
:close-on-click-modal="false" :fullscreen="isSrFullscreen">
<span slot="title" class="dialog-footer">
<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="dClose" style="cursor: pointer;font-size: 25px;margin-left: 10px;" @click="srDialogVisible = false" />
<svg-icon :icon-class="isSrFullscreen ? 'exit-fullscreen' : 'fullscreen'"
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>
</span>
<div style="height: 100%;margin:0;">
<SrList
v-if="srDialogVisible"
:sr-info="srInfo"
/>
<SrList v-if="srDialogVisible" :sr-info="srInfo" />
</div>
</el-dialog>
</div>
@ -156,7 +144,7 @@ import DicomEvent from './DicomEvent'
import { mapGetters } from 'vuex'
import store from '@/store'
import SrList from './SrList'
import { getKeySeries } from '@/api/trials'
export default {
name: 'StudyList',
components: { SrList },
@ -247,7 +235,9 @@ export default {
var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
this.measureData = this.visitTaskList[idx].MeasureData
})
DicomEvent.$on('getKeySeries', () => {
this.getKeySeries()
})
// DicomEvent.$on('setReadingState', readingTaskState => {
// var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
// if (idx > -1) {
@ -269,9 +259,28 @@ export default {
// window.removeEventListener('beforeunload', e => { cornerstone.imageCache.purgeCache() })
},
methods: {
//
async getKeySeries() {
try {
var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
if (!~idx) return false
let data = {
TrialId: this.$route.query.trialId,
SujectVisitId: this.visitTaskList[idx].VisitId,
VisitTaskId: this.visitTaskId
}
let res = await getKeySeries(data)
if (res.IsSuccess) {
let index = this.studyList.findIndex(item => item.IsCriticalSequence)
await store.dispatch('reading/getKeySeries', { study: res.Result, idx: !~index ? 0 : index, visitTaskId: this.visitTaskId })
}
} catch (err) {
console.log(err)
}
},
debounce(fn, delay) {
let timer = null
return function() {
return function () {
const context = this
const args = arguments
clearTimeout(timer)
@ -285,7 +294,7 @@ export default {
//
this.getInitSeries().then((res) => {
requestPoolManager.startTaskTimer()
res.map(async(item) => {
res.map(async (item) => {
// this.loadInitialImage(item)
const imageId = item.imageIds[item.imageIdIndex]
const p = parseInt(new Date().getTime())
@ -407,8 +416,8 @@ export default {
this.studyIndex = obj.studyIndex
this.seriesIndex = obj.seriesIndex
seriesList.push(obj.series)
this.activeNames = [`${this.studyList[ this.studyIndex].StudyId}`]
this.studyList[ obj.studyIndex].SeriesList[obj.seriesIndex].isFirstRender = true
this.activeNames = [`${this.studyList[this.studyIndex].StudyId}`]
this.studyList[obj.studyIndex].SeriesList[obj.seriesIndex].isFirstRender = true
} else {
if (this.studyList.length > 0) {
//
@ -445,7 +454,7 @@ export default {
this.studyIndex = secondObj.studyIndex
this.seriesIndex = secondObj.seriesIndex
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.seriesIndex = secondObj.seriesIndex
@ -895,15 +904,17 @@ export default {
}
</script>
<style lang="scss" scoped>
.study-wrapper{
/deep/ .el-progress-bar__inner{
.study-wrapper {
/deep/ .el-progress-bar__inner {
transition: width 0s ease;
}
width:100%;
height: 100%;
overflow-y: auto;
overflow-x: hidden;
.dicom-desc{
.dicom-desc {
font-weight: bold;
font-size: 13px;
text-align: left;
@ -915,19 +926,23 @@ export default {
overflow-anchor: none;
touch-action: auto;
}
.series-active {
background-color: #607d8b!important;
border: 1px solid #607d8b!important;
background-color: #607d8b !important;
border: 1px solid #607d8b !important;
}
/deep/.el-progress__text{
/deep/.el-progress__text {
color: #ccc;
font-size: 12px;
}
.series{
.series {
width: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
.series-wrapper {
display: flex;
flex-direction: row;
@ -940,11 +955,13 @@ export default {
border-radius: 2px;
border: 1px solid #404040;
background-color: #3a3a3a;
.el-progress__text{
.el-progress__text {
display: none;
}
.el-progress-bar{
padding-right:0px;
.el-progress-bar {
padding-right: 0px;
}
.image-preview {
@ -953,9 +970,11 @@ export default {
border: 2px solid #252525;
cursor: pointer;
}
.image-desc {
vertical-align: top;
p{
p {
width: 95px;
white-space: nowrap;
overflow: hidden;
@ -964,7 +983,8 @@ export default {
color: #ddd;
margin: 0px;
line-height: 1.5;
div{
div {
width: 95px;
white-space: nowrap;
overflow: hidden;
@ -972,7 +992,8 @@ export default {
}
}
}
.flex-div{
.flex-div {
display: flex;
flex-direction: row;
justify-content: space-between;
@ -989,49 +1010,59 @@ export default {
}
}
/deep/.el-collapse{
/deep/.el-collapse {
border: none;
.el-collapse-item{
background-color: #000!important;
.el-collapse-item {
background-color: #000 !important;
color: #ddd;
}
.el-collapse-item__content{
padding-bottom:5px;
background-color: #000!important;
.el-collapse-item__content {
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;
border-bottom-color:#5a5a5a;
border-bottom-color: #5a5a5a;
padding-left: 5px;
height: 40px;
line-height: 20px;
}
}
.sr-wrapper{
/deep/.el-dialog{
.sr-wrapper {
/deep/.el-dialog {
background: #fff !important;
border: 1px solid #ddd;
// color: #ddd;
.el-dialog__title{
color:#fff;
.el-dialog__title {
color: #fff;
}
}
/deep/.sr-dialog-container{
/deep/.sr-dialog-container {
margin-top: 50px !important;
width:75%;
height:80%;
width: 75%;
height: 80%;
}
/deep/.el-dialog__body{
/deep/.el-dialog__body {
padding: 10px;
height: calc(100% - 50px);
}
.el-dialog__header{
.el-dialog__header {
position: relative;
}
.sr-full-dialog-container{
/deep/.is-fullscreen .el-dialog__body{
.sr-full-dialog-container {
/deep/.is-fullscreen .el-dialog__body {
height: calc(100% - 50px);
}
}
@ -1039,25 +1070,29 @@ export default {
}
</style>
<style>
.instance_frame_wrapper{
.instance_frame_wrapper {
min-width: 120px;
background-color: #2c2c2c;
border: 1px solid #2c2c2c;
padding: 5px;
}
.frame_list{
.frame_list {
max-height: 500px;
overflow-y: auto;
}
.instance_frame_wrapper ::-webkit-scrollbar {
width: 7px;
height: 7px;
}
.instance_frame_wrapper ::-webkit-scrollbar-thumb {
border-radius: 10px;
background: #d0d0d0;
}
.frame_content{
.frame_content {
height: 50px;
padding: 5px;
display: flex;
@ -1066,6 +1101,7 @@ export default {
font-size: 12px;
border: 1px solid #404040;
}
.frame_content:hover {
/* font-weight: bold; */
/* box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); */