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

512 lines
16 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">
<div style="cursor: pointer;" @click.stop="sortFile">
<i :class="['el-icon-caret-top', asc === false ? '' : 'icon_check']"
style="display: block;margin-bottom: -5px;"></i>
<i :class="['el-icon-caret-bottom', asc === true ? '' : 'icon_check']"
style="display: block; margin-top: -5px;"></i>
</div>
<div>{{ $t('trials:none-dicom-show:fileList') }} </div>
</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 FileNameSorter from "@/utils/customSort"
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: [],
activeStudyId: '',
activeFileId: '',
activeStudyIndex: -1,
activeFileIndex: -1,
studyList: [],
pdf,
mp4,
BodyPart: {},
subjectVisitId: '',
studyId: '',
otherInfo: null,
criterionType: null,
isQcCheck: false,
isAudit: false,
asc: 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() {
this.asc = !this.asc
this.studyList.forEach(study => {
if (study.NoneDicomStudyFileList.length > 0) {
study.NoneDicomStudyFileList = FileNameSorter.sortFileNames(study.NoneDicomStudyFileList, {
direction: this.asc ? 'asc' : 'desc',
key: 'FileName'
})
}
})
this.activeStudyIndex = this.studyList.findIndex(item => item.Id === this.activeStudyId)
if (this.activeStudyIndex === -1) return
this.activeFileIndex = this.studyList[this.activeStudyIndex].NoneDicomStudyFileList.findIndex(item => item.Id === this.activeFileId)
},
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
this.activeStudyId = study.Id
const fileList = study.NoneDicomStudyFileList
this.activeFileId = fileList[fileIndex].Id
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
this.activeStudyId = obj.studyId
const fileList = this.studyList[studyIndex].NoneDicomStudyFileList || []
const fileIndex = fileList.findIndex(f => f.Path === obj.path)
if (fileIndex === -1) return
this.activeFileIndex = fileIndex
this.activeFileId = fileList[fileIndex].Id
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
this.activeStudyId = obj.studyId
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
this.activeFileId = fileList[fileIndex].Id
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 {
display: flex;
justify-content: center;
align-items: center;
font-size: 16px;
font-weight: bold;
color: #ddd;
background-color: #4c4c4c;
line-height: 40px;
user-select: none;
}
.icon_check {
color: #409EFF;
}
.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;
}
::v-deep .el-switch__label {
color: #ccc;
}
.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>