irc_web/src/views/none-dicoms/components/study-list.vue

479 lines
15 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

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

<template>
<div 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>