479 lines
15 KiB
Vue
479 lines
15 KiB
Vue
<template>
|
||
<div v-loading="loading" class="none-dicom-study-wrapper">
|
||
<div class="study-info">
|
||
{{ $t('trials:none-dicom-show:fileList') }}
|
||
</div>
|
||
<div class="ps" ref="studyBox_ps">
|
||
<el-collapse v-model="activeNames">
|
||
<el-collapse-item v-for="(study, index) in studyList" :key="`${study.Id}`" :name="`${study.Id}`">
|
||
<template slot="title">
|
||
<div class="dicom-desc">
|
||
<div style="text-overflow: ellipsis;overflow: hidden;">
|
||
<span :title="study.CodeView">{{ study.CodeView }}</span>
|
||
<span v-if="otherInfo && otherInfo.IsShowStudyName" :title="study.StudyName" style="margin-left: 5px;">
|
||
{{ study.StudyName }}
|
||
</span>
|
||
</div>
|
||
<div style="text-overflow: ellipsis;overflow: hidden;">
|
||
<span :title="study.BodyPart">{{ getBodyPart(study.BodyPart, study.BodyPartForEditOther) }}</span>
|
||
<span style="margin-left: 5px;" :title="study.Modality">{{ study.Modality }}</span>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
<div class="file-list-container">
|
||
<div v-for="(k, i) in study.NoneDicomStudyFileList" :key="i" style="position:relative;margin-top:1px;"
|
||
@click="selectFile(study, index, i, k)" :ref="`noneDicomRef_${index}_${i}`">
|
||
<div :class="{ 'file-active': index === activeStudyIndex && i === activeFileIndex }" class="file-wrapper">
|
||
<div class="file-main">
|
||
<div class="file-image">
|
||
<el-image
|
||
v-if="k.FileType === 'image/jpeg' || k.FileType === 'image/jpg' || k.FileType === 'image/bmp' || k.FileType === 'image/png'"
|
||
style="width: 100%;height: 100%;"
|
||
:src="`${OSSclientConfig.basePath}${k.Path}?x-oss-process=image/resize,w_50,h_50/format,png`"
|
||
fit="contain" crossorigin="anonymous" />
|
||
<el-image v-else-if="k.FileType === 'application/pdf'" style="width: 100%;height: 100%;" :src="pdf"
|
||
fit="contain" crossorigin="anonymous" />
|
||
<el-image v-else-if="
|
||
!!~k.FileType.indexOf('mp4')
|
||
" style="width: 100%; height: 100%" :src="mp4" fit="contain" crossorigin="anonymous" />
|
||
</div>
|
||
<div class="file-text" :title="k.FileName">
|
||
{{ k.FileName }}
|
||
</div>
|
||
</div>
|
||
<div v-if="isQcCheck" class="switchBox">
|
||
<div class="item">
|
||
<span>{{ $t('trials:audit:table:isReading') }}</span>
|
||
<el-switch v-model="k.IsReading" :disabled="k.IsDeleted || isAudit"
|
||
:active-text="$fd('YesOrNo', true)" :inactive-text="$fd('YesOrNo', false)"
|
||
@change="changeReadingStatus($event, study, k)" />
|
||
</div>
|
||
<div class="item">
|
||
<span>{{ $t('trials:audit:table:isDelete') }}</span>
|
||
<el-switch v-model="k.IsDeleted" :disabled="isAudit" :active-text="$fd('YesOrNo', true)"
|
||
:inactive-text="$fd('YesOrNo', false)" @change="changeDeleteStatus($event, study, k)" />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</el-collapse-item>
|
||
</el-collapse>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
<script>
|
||
import { getNoneDicomStudyList, setNodicomStudyState } from '@/api/trials'
|
||
import pdf from '@/assets/pdf.png'
|
||
import mp4 from '@/assets/mp4.png'
|
||
export default {
|
||
name: 'StudyList',
|
||
props: {
|
||
visitTaskInfo: {
|
||
type: Object,
|
||
default() {
|
||
return {}
|
||
}
|
||
},
|
||
isComparison: {
|
||
type: Boolean,
|
||
default: false
|
||
}
|
||
},
|
||
data() {
|
||
return {
|
||
loading: false,
|
||
activeNames: [],
|
||
activeStudyIndex: -1,
|
||
activeFileIndex: -1,
|
||
studyList: [],
|
||
pdf,
|
||
mp4,
|
||
BodyPart: {},
|
||
subjectVisitId: '',
|
||
studyId: '',
|
||
otherInfo: null,
|
||
criterionType: null,
|
||
isQcCheck: false,
|
||
isAudit: false
|
||
}
|
||
},
|
||
async mounted() {
|
||
this.subjectVisitId = this.$route.query.subjectVisitId
|
||
this.studyId = this.$route.query.studyId
|
||
this.isQcCheck = !!this.$route.query.isQcCheck
|
||
this.getNoneDicoms()
|
||
|
||
// this.$nextTick(() => {
|
||
// this.activeStudy(this.studyList[0].Id)
|
||
// })
|
||
this.BodyPart.Bodypart = await this.$getBodyPart(this.$route.query.trialId)
|
||
},
|
||
methods: {
|
||
scrollActiveFileIntoView() {
|
||
this.$nextTick(() => {
|
||
const refKey = `noneDicomRef_${this.activeStudyIndex}_${this.activeFileIndex}`
|
||
const targetRef = this.$refs[refKey]
|
||
const element = Array.isArray(targetRef) ? targetRef[0] : targetRef
|
||
if (!element) return
|
||
this.scrollElementToVisible(element)
|
||
})
|
||
},
|
||
scrollElementToVisible(element, options = {}) {
|
||
const container = this.$refs['studyBox_ps']
|
||
if (!container || !element) return
|
||
const elementTop = element.offsetTop
|
||
const elementBottom = elementTop + element.offsetHeight
|
||
const visibleTop = container.scrollTop
|
||
const visibleBottom = visibleTop + container.clientHeight
|
||
if (elementTop >= visibleTop && elementBottom <= visibleBottom) return
|
||
const offset = options.offset != null
|
||
? options.offset
|
||
: Math.max((container.clientHeight - element.offsetHeight) / 2, 0)
|
||
const targetPosition = Math.max(elementTop - offset, 0)
|
||
container.scrollTo({
|
||
top: targetPosition,
|
||
behavior: options.behavior || 'smooth'
|
||
})
|
||
},
|
||
async getNoneDicoms() {
|
||
const loading = this.$loading({ fullscreen: true })
|
||
try {
|
||
let res = await getNoneDicomStudyList(
|
||
this.subjectVisitId,
|
||
this.studyId,
|
||
false,
|
||
this.$route.query.visitTaskId,
|
||
!!this.$route.query.isReading,
|
||
this.$route.query.isImageSegmentLabel,
|
||
)
|
||
this.studyList = res.Result
|
||
this.studyList.forEach(study => {
|
||
if (study.NoneDicomStudyFileList && study.NoneDicomStudyFileList.length > 0) {
|
||
study.NoneDicomStudyFileList.forEach(file => {
|
||
if (file.Path.includes('.MaskNoneDicom_')) {
|
||
file.IsBeMark = true
|
||
} else {
|
||
file.IsBeMark = false
|
||
}
|
||
})
|
||
}
|
||
})
|
||
this.otherInfo = res.OtherInfo
|
||
this.isAudit = res.OtherInfo.AuditState * 1 > 6
|
||
const studyIndex = this.studyList.findIndex((item) => {
|
||
return item.NoneDicomStudyFileList.length > 0
|
||
})
|
||
if (studyIndex > -1) {
|
||
this.selectFile(this.studyList[studyIndex], studyIndex, 0)
|
||
let studyId = this.studyList[studyIndex].Id
|
||
this.activeNames = [studyId]
|
||
}
|
||
if (this.criterionType === 19 || this.criterionType === 20) {
|
||
this.sortFile()
|
||
}
|
||
loading.close()
|
||
} catch (e) {
|
||
console.log(e)
|
||
loading.close()
|
||
}
|
||
|
||
},
|
||
selected() {
|
||
|
||
},
|
||
sortFile() {
|
||
|
||
},
|
||
changeReadingStatus(callback, row, file) {
|
||
let statusStr = ''
|
||
if (callback) {
|
||
statusStr = this.$t('trials:audit:label:setSeriesReading')
|
||
file.IsReading = false
|
||
} else {
|
||
statusStr = this.$t('trials:audit:label:setSeriesNotReading')
|
||
file.IsReading = true
|
||
}
|
||
let message = this.$t('trials:audit:message:changeSeriesStatus').replace('xxx', statusStr)
|
||
message = message.replace('yyy', this.$fd('YesOrNo', !file.IsReading))
|
||
this.$confirm(message, {
|
||
distinguishCancelAndClose: true,
|
||
type: 'warning',
|
||
})
|
||
.then(() => {
|
||
const state = file.IsReading ? 1 : 2
|
||
this.loading = true
|
||
const params = {
|
||
SubjectVisitId: row.SubjectVisitId,
|
||
NoneDicomStudyId: row.Id,
|
||
State: state,
|
||
NoneDicomStudyFileId: file.Id,
|
||
}
|
||
return setNodicomStudyState(params)
|
||
.then((res) => {
|
||
this.loading = false
|
||
if (res.IsSuccess) {
|
||
this.$message.success(this.$t('common:message:savedSuccessfully'))
|
||
file.IsReading = !file.IsReading
|
||
}
|
||
})
|
||
.catch(() => {
|
||
this.loading = false
|
||
})
|
||
})
|
||
.catch(() => { })
|
||
},
|
||
changeDeleteStatus(callback, row, file) {
|
||
let statusStr = ''
|
||
if (callback) {
|
||
statusStr = this.$t('trials:audit:label:setSeriesDeleted')
|
||
file.IsDeleted = false
|
||
} else {
|
||
statusStr = this.$t('trials:audit:label:setSeriesNotDelete')
|
||
file.IsDeleted = true
|
||
}
|
||
let message = this.$t('trials:audit:message:changeSeriesStatus').replace('xxx', statusStr)
|
||
message = message.replace('yyy', this.$fd('YesOrNo', !file.IsDeleted))
|
||
this.$confirm(message, {
|
||
distinguishCancelAndClose: true,
|
||
type: 'warning',
|
||
})
|
||
.then(() => {
|
||
const state = file.IsDeleted ? 5 : 4
|
||
this.loading = true
|
||
const params = {
|
||
SubjectVisitId: row.SubjectVisitId,
|
||
NoneDicomStudyId: row.Id,
|
||
State: state,
|
||
NoneDicomStudyFileId: file.Id,
|
||
}
|
||
return setNodicomStudyState(params)
|
||
.then((res) => {
|
||
this.loading = false
|
||
if (res.IsSuccess) {
|
||
this.$message.success(this.$t('common:message:savedSuccessfully'))
|
||
file.IsDeleted = !file.IsDeleted
|
||
}
|
||
})
|
||
.catch(() => {
|
||
this.loading = false
|
||
})
|
||
})
|
||
.catch(() => { })
|
||
},
|
||
getBodyPart(bodyPart, other) {
|
||
if (!bodyPart && !other) return ''
|
||
var separator = ','
|
||
if (bodyPart.indexOf('|') > -1) {
|
||
separator = '|'
|
||
} else if (bodyPart.indexOf(',') > -1) {
|
||
separator = ','
|
||
} else if (bodyPart.indexOf(',') > -1) {
|
||
separator = ','
|
||
}
|
||
var arr = bodyPart.split(separator)
|
||
var newArr = arr.map((i) => {
|
||
return this.$fd('Bodypart', i.trim(), 'Code', this.BodyPart, 'Name')
|
||
})
|
||
if (other) {
|
||
newArr.push(other)
|
||
}
|
||
newArr = newArr.filter(Boolean)
|
||
return newArr.join(' | ')
|
||
},
|
||
// 切换文件
|
||
selectFile(study, studyIndex, fileIndex, isReset = false) {
|
||
if (this.isComparison) return false
|
||
this.activeStudyIndex = studyIndex
|
||
this.activeFileIndex = fileIndex
|
||
const fileList = study.NoneDicomStudyFileList
|
||
this.$emit('selectFile', { fileInfo: fileList[fileIndex], fileList, visitTaskInfo: this.visitTaskInfo, studyId: study.Id, isReset })
|
||
this.scrollActiveFileIntoView()
|
||
},
|
||
activeImage(obj) {
|
||
if (!obj || this.studyList.length === 0) return
|
||
const study = this.studyList.find(i => i.Id === obj.studyId)
|
||
const studyIndex = this.studyList.findIndex(i => i.Id === obj.studyId)
|
||
if (studyIndex === -1) return
|
||
this.activeStudy(`${obj.studyId}`)
|
||
this.activeStudyIndex = studyIndex
|
||
const fileList = this.studyList[studyIndex].NoneDicomStudyFileList || []
|
||
const fileIndex = fileList.findIndex(f => f.Path === obj.path)
|
||
if (fileIndex === -1) return
|
||
this.activeFileIndex = fileIndex
|
||
this.selectFile(study, studyIndex, fileIndex)
|
||
this.scrollActiveFileIntoView()
|
||
},
|
||
replaceImage(obj) {
|
||
if (!obj || this.studyList.length === 0) return
|
||
const study = this.studyList.find(i => i.Id === obj.studyId)
|
||
const studyIndex = this.studyList.findIndex(i => i.Id === obj.studyId)
|
||
if (studyIndex === -1) return
|
||
this.activeStudy(`${obj.studyId}`)
|
||
this.activeStudyIndex = studyIndex
|
||
const fileList = this.studyList[studyIndex].NoneDicomStudyFileList || []
|
||
const fileIndex = fileList.findIndex(f => f.Path === obj.path)
|
||
const file = fileList.find(f => f.Path === obj.path)
|
||
if (fileIndex === -1) return
|
||
this.activeFileIndex = fileIndex
|
||
file.Path = obj.file.Path
|
||
file.FullFilePath = obj.file.Path
|
||
file.IsBeMark = obj.IsBeMark
|
||
this.selectFile(study, studyIndex, fileIndex, true)
|
||
},
|
||
activeStudy(id) {
|
||
const studyId = `${id}`
|
||
if (this.activeNames.indexOf(studyId) > -1) return
|
||
this.activeNames.push(studyId)
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
<style lang="scss" scoped>
|
||
.none-dicom-study-wrapper {
|
||
width: 100%;
|
||
height: 100%;
|
||
overflow-y: hidden;
|
||
overflow-x: hidden;
|
||
display: flex;
|
||
flex-direction: column;
|
||
|
||
.study-info {
|
||
font-size: 16px;
|
||
font-weight: bold;
|
||
color: #ddd;
|
||
// padding: 5px 0px;
|
||
margin: 0;
|
||
text-align: center;
|
||
background-color: #4c4c4c;
|
||
line-height: 40px;
|
||
}
|
||
|
||
.dicom-desc {
|
||
font-weight: bold;
|
||
font-size: 13px;
|
||
text-align: left;
|
||
color: #d0d0d0;
|
||
padding: 2px 8px 2px 2px;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
.ps {
|
||
flex: 1;
|
||
overflow-anchor: none;
|
||
touch-action: auto;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.file-active {
|
||
// background: #16477b;
|
||
background-color: #183553 !important;
|
||
border: 1px solid #23527b !important;
|
||
}
|
||
|
||
::v-deep .el-progress__text {
|
||
color: #ccc;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.file-list-container {
|
||
width: 100%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: flex-start;
|
||
|
||
.file-wrapper {
|
||
width: 100%;
|
||
padding: 5px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: stretch;
|
||
cursor: pointer;
|
||
background-color: #3a3a3a;
|
||
|
||
// color: #ddd;
|
||
// white-space: nowrap;
|
||
// overflow: hidden;
|
||
// text-overflow: ellipsis;
|
||
.file-main {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
min-width: 0;
|
||
}
|
||
|
||
.el-progress__text {
|
||
display: none;
|
||
}
|
||
|
||
.el-progress-bar {
|
||
padding-right: 0px;
|
||
}
|
||
|
||
.file-image {
|
||
width: 50px;
|
||
height: 50px;
|
||
}
|
||
|
||
.file-text {
|
||
flex: 1;
|
||
padding-left: 5px;
|
||
color: #ddd;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
.switchBox {
|
||
width: 100%;
|
||
margin-top: 6px;
|
||
color: #ddd;
|
||
|
||
.item {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 5px;
|
||
}
|
||
|
||
.item:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
}
|
||
|
||
::v-deep .el-collapse {
|
||
border: none;
|
||
|
||
.el-collapse-item {
|
||
background-color: #000 !important;
|
||
color: #ddd;
|
||
}
|
||
|
||
.el-collapse-item__content {
|
||
padding-bottom: 0px;
|
||
background-color: #000 !important;
|
||
}
|
||
|
||
.el-collapse-item__header {
|
||
background-color: #353535 !important;
|
||
color: #ddd;
|
||
border-bottom-color: #5a5a5a;
|
||
padding-left: 5px;
|
||
height: 60px;
|
||
line-height: 20px;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
::v-deep .el-progress-bar__inner {
|
||
transition: width 0s ease;
|
||
}
|
||
}
|
||
</style>
|