dicom阅片更改

uat
caiyiling 2025-04-10 15:26:11 +08:00
parent e3ad114207
commit 42711dadb5
12 changed files with 7144 additions and 406 deletions

View File

@ -0,0 +1,318 @@
<template>
<div>
<div
v-if="!!question.GroupName && question.Type==='group'"
>
<h4 style="color: #ddd;padding: 5px 0px;margin: 0;">
{{ language==='en'?question.GroupEnName:question.GroupName }}
</h4>
</div>
<template v-else>
<el-form-item
v-if="(question.ShowQuestion===1 && question.ParentTriggerValueList.includes(String(questionForm[question.ParentId]))) || question.ShowQuestion===0 "
:label="`${question.QuestionName}`"
:prop="question.Id"
:rules="[
{ required: (question.IsRequired === 0 || (question.IsRequired ===1 && question.RelevanceId && (questionForm[question.RelevanceId] === question.RelevanceValue))) && question.Type!=='group' && question.Type!=='summary',
message: ['radio', 'select', 'checkbox'].includes(question.Type) ? $t('common:ruleMessage:select') : $t('common:ruleMessage:specify'), trigger: ['blur', 'change']},
]"
:class="[question.Type==='group' ? 'mb' : (question.Type==='upload' || question.QuestionName.length > 15) ?'uploadWrapper' : '']"
>
<!-- 输入框 -->
<el-input
v-if="question.Type==='input'"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2"
@change="((val)=>{formItemChange(val, question)})"
/>
<!-- 多行文本输入框 -->
<el-input
v-else-if="question.Type==='textarea'"
v-model="questionForm[question.Id]"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"
maxlength="500"
:disabled="readingTaskState >= 2"
@change="((val)=>{formItemChange(val, question)})"
/>
<!-- 下拉框 -->
<el-select
v-else-if="question.Type==='select'"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2 || ((question.TableQuestionType === 2 || question.QuestionGenre === 2) && !!question.DictionaryCode)"
clearable
@change="((val)=>{formItemChange(val, question)})"
>
<template v-if="question.TableQuestionType === 1">
<el-option
v-for="item in organList"
:key="item.Id"
:label="item[question.DataTableColumn]"
:value="item[question.DataTableColumn]"
/>
</template>
<template v-else-if="question.TableQuestionType === 3 || question.QuestionGenre === 3">
<el-option
v-for="item of $d[question.DictionaryCode]"
:key="item.id"
:value="String(item.value)"
:label="item.label"
/>
</template>
<template v-else-if="(question.TableQuestionType === 2 || question.QuestionGenre === 2) && question.DictionaryCode">
<el-option
v-for="item of $d[question.DictionaryCode]"
:key="item.id"
:value="String(item.value)"
:label="item.label"
/>
</template>
<template v-else>
<el-option
v-for="val in question.TypeValue.split('|')"
:key="val"
:label="val"
:value="val"
/>
</template>
</el-select>
<!-- 单选 -->
<el-radio-group
v-else-if="question.Type==='radio'"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2"
@change="((val)=>{formItemChange(val, question)})"
>
<template v-if="question.DictionaryCode">
<el-radio
v-for="item of $d[question.DictionaryCode]"
:key="item.id"
:label="String(item.value)"
>
{{ item.label }}
</el-radio>
</template>
<template v-else-if="question.TypeValue">
<el-radio
v-for="val in question.TypeValue.split('|')"
:key="val"
:label="val"
>
{{ val }}
</el-radio>
</template>
</el-radio-group>
<!-- 复选框 -->
<el-checkbox-group
v-else-if="question.Type==='checkbox'"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2"
>
<el-checkbox
v-for="val in question.TypeValue.split('|')"
:key="val"
:label="val"
>
{{ val }}
</el-checkbox>
</el-checkbox-group>
<!-- 数值 -->
<template v-else-if="question.Type==='number'">
<el-input-number
v-if="question.ValueType === 0"
v-model="questionForm[question.Id]"
:precision="0"
:disabled="readingTaskState >= 2"
/>
<el-input-number
v-else-if="question.ValueType === 3"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2"
/>
<el-input-number
v-else-if="question.ValueType === 1 || question.ValueType === 2"
v-model="questionForm[question.Id]"
:precision="digitPlaces"
:disabled="readingTaskState >= 2"
/>
</template>
<el-input
v-else-if="question.Type==='calculation'"
v-model="questionForm[question.Id]"
disabled
>
<template v-if="question.Unit" slot="append">
{{ $fd('ValueUnit', parseInt(question.Unit)) }}
</template>
</el-input>
<!-- 上传图像 -->
<el-upload
v-else-if="question.Type==='upload'"
action
:accept="accept"
:limit="question.ImageCount"
:on-preview="handlePictureCardPreview"
:before-upload="handleBeforeUpload"
:http-request="uploadScreenshot"
list-type="picture-card"
:on-remove="handleRemove"
:file-list="fileList"
:class="{disabled:fileList.length >= question.ImageCount}"
:disabled="readingTaskState >= 2"
>
<i slot="default" class="el-icon-plus" />
<div slot="file" slot-scope="{file}">
<img
class="el-upload-list__item-thumbnail"
:src="OSSclientConfig.basePath + file.url"
alt=""
>
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-preview"
@click="handlePictureCardPreview(file)"
>
<i class="el-icon-zoom-in" />
</span>
<span
v-if="readingTaskState < 2"
class="el-upload-list__item-delete"
@click="handleRemove(file)"
>
<i class="el-icon-delete" />
</span>
</span>
</div>
</el-upload>
<el-dialog
v-if="question.Type==='upload'"
append-to-body
:visible.sync="imgVisible"
width="600px"
>
<el-image :src="imageUrl" width="100%">
<div slot="placeholder" class="image-slot">
加载中<span class="dot">...</span>
</div>
</el-image>
</el-dialog>
</el-form-item>
</template>
<question-form-item
v-for="(item) in question.Childrens"
:key="item.Id"
:question="item"
:question-form="questionForm"
:reading-task-state="readingTaskState"
:visitTaskId="visitTaskId"
/>
</div>
</template>
<script>
// import { uploadReadingAnswerImage } from '@/api/trials'
import { mapGetters } from 'vuex'
export default {
name: 'QuestionFormItem',
props: {
questionForm: {
type: Object,
default() {
return {}
}
},
question: {
type: Object,
default() {
return {}
}
},
readingTaskState: {
type: Number,
required: true
},
visitTaskId: {
type: String,
default: ''
}
},
data() {
return {
fileList: [],
accept: '.png,.jpg,.jpeg',
imgVisible: false,
imageUrl: '',
urls: [],
digitPlaces: 2
}
},
computed: {
...mapGetters(['language'])
},
watch: {
questionForm: {
deep: true,
immediate: true,
handler(v) {
// console.log(v)
}
}
},
mounted() {
let digitPlaces = Number(localStorage.getItem('digitPlaces'))
this.digitPlaces = digitPlaces === -1 ? this.digitPlaces : digitPlaces
},
methods: {
formItemChange(v, question) {
if (question.Childrens.length > 0) {
this.resetChild(question.Childrens)
} else {
this.$emit('setFormItemData', { key: question.Id, val: v })
}
},
resetChild(obj) {
obj.forEach(i => {
this.$emit('resetFormItemData', i.Id)
if (i.Childrens && i.Childrens.length > 0) {
this.resetChild(i.Childrens)
}
})
},
resetFormItemData(v) {
this.$emit('resetFormItemData', v)
},
setFormItemData(obj) {
this.$emit('setFormItemData', obj)
},
}
}
</script>
<style lang="scss" scoped>
.mb{
margin-bottom: 0px;
}
.disabled{
::v-deep .el-upload--picture-card {
display: none;
}
}
.uploadWrapper{
display: flex;
flex-direction: column !important;
align-items: flex-start;
}
::v-deep .el-input.is-disabled .el-input__inner{
background-color: #646464a1;
}
::v-deep .el-input-group__append, .el-input-group__prepend{
padding: 0 10px;
}
::v-deep .el-form-item__content {
width: 100%;
}
::v-deep .el-select.is-disabled .el-input__inner{
background-color: #646464a1;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,352 @@
<template>
<div class="table-question-form">
<div style="display: flex;justify-content: space-between;">
<h3 v-if="questionForm.LesionName" style="color: #ddd;padding: 5px 0px;margin: 0;">
{{ questionForm.LesionName }}
</h3>
<!-- 关闭 -->
<div>
<i class="el-icon-circle-close" style="font-size: 25px;cursor: pointer;" @click="close" />
</div>
</div>
<el-form-item
:label="$t('trials:reading:title:lesionType')"
prop="LesionType"
:rules="[
{ required:true,message: $t('common:ruleMessage:select'), trigger: ['blur']},
]"
>
<!-- 下拉框 -->
<el-select
v-model="questionForm.LesionType"
filterable
:disabled="!isCurrentTask || readingTaskState>=2 || !isBaseLineTask"
@change="((val)=>{lesionTypeChange(val)})"
>
<el-option
v-for="item of $d.LesionType"
v-show="!(isBaseLineTask && item.value === 2)"
:key="item.id"
:value="item.value"
:label="item.label"
/>
</el-select>
</el-form-item>
<el-form-item
v-for="qs in questions"
v-show="qs.ShowQuestion!==2"
:key="qs.Id"
:label="`${qs.QuestionName}`"
:prop="qs.Id"
:rules="[
{ required: (qs.IsRequired === 0 || (qs.IsRequired ===1 && qs.RelevanceId && (questionForm[qs.RelevanceId] === qs.RelevanceValue)) || (qs.QuestionMark === 6 && questionForm.IsCanEditPosition === true) || (questionForm.IsCanEditPosition && qs.QuestionMark === 10)) && qs.Type!=='group' && qs.Type!=='summary',
message:['radio', 'select', 'checkbox'].includes(qs.Type) ? $t('common:ruleMessage:select') : $t('common:ruleMessage:specify'), trigger: ['blur','change']},
]"
>
<!-- 输入框 -->
<template v-if="qs.Type==='input' || qs.Type==='number'">
<el-input
v-if="qs.Type==='input' || qs.Type==='number'"
v-model="questionForm[qs.Id]"
:disabled="!isCurrentTask || readingTaskState>=2 || qs.QuestionMark === 0 || qs.QuestionMark === 1 || qs.QuestionMark === 2 || qs.QuestionMark === 5 || (qs.QuestionMark === 6 && (isCurrentTaskAdd === 'False' || (isCurrentTaskAdd === 'True' && !questionForm.IsCanEditPosition) || !!answer.SplitOrMergeLesionName))|| (qs.QuestionMark === 8 && (isCurrentTaskAdd === 'False' || !!answer.SplitOrMergeLesionName) && lesionType !== 2) || (qs.QuestionMark === 10 && (isCurrentTaskAdd === 'False' || !!answer.SplitOrMergeLesionName))"
@change="((val)=>{formItemChange(val, qs)})"
>
<template v-if="(qs.QuestionMark===0 || qs.QuestionMark===1) && qs.Unit" slot="append">
{{ $fd('ValueUnit', parseInt(qs.Unit)) }}
</template>
</el-input>
</template>
<!-- 多行文本输入框 -->
<el-input
v-if="qs.Type==='textarea'"
v-model="questionForm[qs.Id]"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"
:disabled="!isCurrentTask || readingTaskState>=2"
@change="((val)=>{formItemChange(val, qs)})"
/>
<!-- 下拉框 -->
<el-select
v-if="qs.Type==='select'"
v-model="questionForm[qs.Id]"
filterable
:placeholder="qs.QuestionMark === 8 ? $t('common:placeholder:selectorsearch') : qs.QuestionMark === 2 ? '' : $t('common:placeholder:select')"
:disabled="!isCurrentTask || readingTaskState>=2 || qs.QuestionMark === 0 || qs.QuestionMark === 1 || qs.QuestionMark === 2 || qs.QuestionMark === 5 || (qs.QuestionMark === 6 && (isCurrentTaskAdd === 'False' || (isCurrentTaskAdd === 'True' && !questionForm.IsCanEditPosition) || !!answer.SplitOrMergeLesionName))|| (qs.QuestionMark === 8 && (isCurrentTaskAdd === 'False'|| !!answer.SplitOrMergeLesionName)) || (qs.QuestionMark === 10 && (isCurrentTaskAdd === 'False' || (isCurrentTaskAdd === 'True' && !questionForm.IsCanEditPosition) || !!answer.SplitOrMergeLesionName))"
@change="((val)=>{formItemChange(val, qs)})"
>
<template v-if="qs.QuestionMark === 8" #prefix>
<span style="padding-left: 5px;">
<i class="el-icon-search" />
</span>
</template>
<template v-if="qs.TableQuestionType === 1">
<el-option
v-for="item in organList"
:key="item.Id"
:label="item[qs.DataTableColumn]"
:value="item[qs.DataTableColumn]"
/>
</template>
<template v-else-if="qs.DictionaryCode && qs.QuestionMark === 7 && isBaseLineTask">
<el-option
v-for="item of $d[qs.DictionaryCode]"
v-show="(lesionType === 0 && item.value ===0) || (lesionType === 1 && (item.value ===0))"
:key="item.id"
:value="item.value"
:label="item.label"
/>
</template>
<template v-else-if="qs.DictionaryCode && qs.QuestionMark === 7 && !isBaseLineTask">
<el-option-group
:label="!isNaN(parseFloat(answer.LastTaskState)) ? `${$t('trials:dicomReading:tip:lastVisitStatus')} ${$fd(qs.DictionaryCode,parseFloat(answer.LastTaskState))}` : ''"
>
<!-- 首次分裂的病灶只能选择存在 -->
<template v-if="answer.IsFristAdd=== 'True' && answer.SplitOrMergeType === '0'">
<el-option
v-for="item of $d[qs.DictionaryCode]"
v-show="item.value === 0"
:key="item.id"
:value="item.value"
:label="item.label"
/>
</template>
<!-- 首次添加的新病灶不能为无法评估和消失 -->
<template v-else-if="isCurrentTaskAdd=== 'True' && lesionType === 2">
<el-option
v-for="item of $d[qs.DictionaryCode]"
v-show="item.value === 0 || item.value === 1"
:key="item.id"
:value="item.value"
:label="item.label"
/>
</template>
<template v-else>
<el-option
v-for="item of filterState($d[qs.DictionaryCode])"
:key="item.id"
:value="item.value"
:label="item.label"
/>
</template>
</el-option-group>
</template>
<template v-else-if="qs.DictionaryCode && qs.QuestionMark !== 7">
<el-option
v-for="item of $d[qs.DictionaryCode]"
:key="item.id"
:value="item.value"
:label="item.label"
/>
</template>
<template v-else>
<el-option
v-for="val in qs.TypeValue.split('|')"
:key="val"
:label="val"
:value="val"
/>
</template>
</el-select>
<!-- 单选 -->
<el-radio-group
v-if="qs.Type==='radio'"
v-model="questionForm[qs.id]"
:disabled="!isCurrentTask || readingTaskState>=2"
>
<el-radio
v-for="val in qs.options.split('|')"
:key="val"
:label="val"
>
{{ val }}
</el-radio>
</el-radio-group>
</el-form-item>
</div>
</template>
<script>
export default {
name: 'TableQuestionFormItem',
props: {
tableInfo: {
type: Object,
default() {
return {}
}
},
answer: {
type: Object,
default() {
return {}
}
},
questionForm: {
type: Object,
default() {
return {}
}
},
organs: {
type: Array,
default() {
return []
}
},
readingTaskState: {
type: Number,
required: true
},
isBaseLineTask: {
type: Boolean,
required: true
},
isCurrentTask: {
type: Boolean,
required: true
}
},
watch: {
questionForm: {
deep: true,
immediate: true,
handler(v) {
if (v.MeasureData) {
const {markTool} = v.MeasureData
if (markTool === 'Bidirectional') {
this.getOrganList(1)
} else if (markTool === 'Length') {
this.getOrganList(0)
} else {
this.getOrganList()
}
} else {
this.getOrganList()
}
this.isCurrentTaskAdd = v.IsCurrentTaskAdd
}
},
tableInfo: {
deep: true,
immediate: true,
handler(v) {
if (v) {
this.lesionType = v.LesionType
this.questions = v.TableQuestions.Questions
}
}
},
},
data(){
return{
isCurrentTaskAdd: null,
lesionType: null,
questions: [],
organList: []
}
},
methods: {
lesionTypeChange(v) {
this.$emit('lesionTypeChange', {tableId: this.tableInfo.Id, rowIndex: this.answer.RowIndex, newLesionType: v})
},
formItemChange(v, qs) {
let updateArr = []
if (qs.QuestionMark === 8 && qs.RelationQuestions.length > 0) {
//
let index = this.organList.findIndex(item => item[qs.DataTableColumn] === v)
if (index > -1) {
let selected = this.organList[index]
qs.RelationQuestions.map(q => {
let val = selected[q.DataTableColumn]
updateArr.push({questionId: q.Id, val: val, questionMark: q.QuestionMark})
})
updateArr.push({questionId: 'OrganInfoId', val: selected.OrganInfoId})
updateArr.push({questionId: 'IsCanEditPosition', val: selected.IsCanEditPosition})
} else {
question.RelationQuestions.map(q => {
updateArr.push({questionId: q.Id, val: '', questionMark: q.QuestionMark})
})
}
}
this.$emit('update', {tableId: this.tableInfo.Id, rowIndex: this.answer.RowIndex, questionMark: qs.QuestionMark, questionId: qs.Id, val: v, updateArr})
},
getOrganList(isLymphNodes = null) {
let idx = this.organs.findIndex(i => i.LesionType === this.questionForm.LesionType)
if (idx > -1) {
let arr = this.organs[idx].OrganList
if (!isNaN(parseInt(isLymphNodes))) {
this.organList = arr.filter((item) => item.IsLymphNodes === parseInt(isLymphNodes))
} else {
this.organList = arr
}
}
},
filterState(arr) {
if (!this.isBaseLineTask) {
const isLymphLesion = this.questionForm.IsLymphNodes
const lesionLength = this.questionForm.LesionLength
const lesionShort = this.questionForm.LesionShort
const bLesionL = !isNaN(parseFloat(this.questionForm.BaseLineMajorAxis)) ? parseFloat(this.questionForm.BaseLineMajorAxis) : 0
const bLesionS = !isNaN(parseFloat(this.questionForm.BaseLineShortAxis)) ? parseFloat(this.questionForm.BaseLineShortAxis) : 0
if (this.lesionType === 1 && isLymphLesion === 1 && this.questionForm.MeasureData && this.questionForm.MeasureData.markTool === 'Bidirectional' && lesionShort < bLesionS) {
arr = arr.filter(i => i.value !== 1)
} else if (this.lesionType === 1 && isLymphLesion === 1 && this.questionForm.MeasureData && this.questionForm.MeasureData.markTool === 'Bidirectional' && lesionShort >= 10 && lesionShort > bLesionS) {
arr = arr.filter(i => i.value === 0 || i.value === 1)
} else if (this.lesionType === 1 && isLymphLesion === 0 && this.questionForm.MeasureData && (this.questionForm.MeasureData.markTool === 'Length' || this.questionForm.MeasureData.markTool === 'Bidirectional') && lesionLength < bLesionL) {
arr = arr.filter(i => i.value !== 1)
}
return arr
} else {
return arr
}
},
close() {
this.$emit('close', {tableId: this.tableInfo.Id, rowIndex: this.answer.RowIndex})
}
}
}
</script>
<style lang="scss" scoped>
.table-question-form{
::v-deep .el-form-item__label{
color: #c3c3c3;
}
::v-deep .el-input .el-input__inner{
background-color: transparent;
color: #ddd;
border: 1px solid #5e5e5e;
}
::v-deep .el-form-item{
display: flex;
flex-direction: row;
justify-content: flex-start;
}
::v-deep .el-form-item__content{
flex: 1;
}
::v-deep .el-input.is-disabled .el-input__inner{
background-color: #646464a1;
}
::v-deep .el-select.is-disabled .el-input__inner{
background-color: #646464a1;
}
::v-deep .el-button--mini, .el-button--mini.is-round {
padding: 7px 10px;
}
.el-form-item__content
.el-select{
width: 100%;
}
.input-width1{
width: calc(100% - 60px)!important;
}
.input-width2{
width: 100% !important;
}
}
</style>

View File

@ -0,0 +1,915 @@
<template>
<div class="report-wrapper">
<el-card v-loading="loading" shadow="never">
<div slot="header" class="clearfix report-header">
<!-- 电子影像病例报告表eICRF -->
<h3 style="margin:0;padding:0;">{{ $t('trials:readingReport:title:eicrf') }}</h3>
<div style="margin-left:auto">
<el-switch
v-model="isShowDetail"
:active-text="$t('trials:readingReport:title:expandDetails')"
:inactive-text="$t('trials:readingReport:title:collapseDetails')"
style="margin-right:5px;"
@change="handleShowDetail"
/>
<el-button
v-if="readingTaskState<2"
type="primary"
size="small"
@click="skipTask"
>
<!-- 跳过 -->
{{ $t('trials:readingReport:button:skip') }}
</el-button>
<el-button
v-if="readingTaskState<2"
type="primary"
size="small"
@click="getReportInfo(false)"
>
<!-- 刷新 -->
{{ $t('trials:readingReport:button:refresh') }}
</el-button>
<el-button
v-if="readingTaskState<2 && criterionType !== 10"
type="primary"
size="small"
@click="handleSave(true)"
>
<!-- 保存 -->
{{ $t('common:button:save') }}
</el-button>
<el-button
v-if="readingTaskState<2"
type="primary"
size="small"
@click="beforeLeave"
>
<!-- 提交 -->
{{ $t('common:button:submit') }}
</el-button>
</div>
</div>
<div ref="tableWrapper" style="height:100%;overflow-y: auto;">
<el-table
v-if="height"
ref="reportList"
:data="taskQuestions"
row-key="Id"
border
:expand-row-keys="expandedRows"
:height="height"
:tree-props="{children: 'Childrens', hasChildren: 'hasChildren'}"
size="mini"
>
<el-table-column
prop=""
label=""
show-overflow-tooltip
width="350px"
>
<template slot-scope="scope">
<span v-if="scope.row.QuestionName" :style="{color: ((scope.row.isLymphNodes === 1 && scope.row.QuestionMark === 1) || (scope.row.isLymphNodes === 0 && scope.row.QuestionMark === 0)) && (scope.row.LesionType === 0 || scope.row.LesionType === 5) ? '#f66' : '#fff'}">{{ scope.row.QuestionName }}</span>
<span
v-else
style="font-weight: bold;font-size: 16px;color: #f44336;"
>
{{ language==='en'?scope.row.GroupEnName:scope.row.GroupName }}
</span>
</template>
</el-table-column>
<el-table-column
v-for="task in visitTaskList"
:key="task.VisitTaskId"
prop="date"
show-overflow-tooltip
width="150px"
>
<template slot="header">
<div v-if="task.IsCurrentTask">
<div>
{{ task.BlindName }}
</div>
</div>
<div v-else>
<div>
{{ task.BlindName }}
<el-button type="text" size="small" @click="previewDicoms(task)">
<span class="el-icon-view" />
</el-button>
</div>
</div>
</template>
<template slot-scope="scope">
<span :style="{color: ((scope.row.isLymphNodes === 1 && scope.row.QuestionMark === 1) || (scope.row.isLymphNodes === 0 && scope.row.QuestionMark === 0)) && (scope.row.LesionType === 0 || scope.row.LesionType === 5) || (scope.row.QuestionMark === 12) || scope.row.HighlightAnswerList.includes(`${scope.row.Answers[task.VisitTaskId]}`) ? '#f66' : '#fff'}">
<template v-if="task.VisitTaskId === visitTaskId && readingTaskState < 2 && [13,14,15,42].includes(scope.row.QuestionType)">
<!-- 是否存在疾病基线时可修改 -->
<template v-if="task.IsBaseLine && scope.row.QuestionType=== 15">
<el-select
v-if="scope.row.Type==='select' && scope.row.DictionaryCode"
v-model="currentExistDisease"
size="mini"
@change="handleExistDiseaseChange"
>
<el-option
v-for="item of $d[ scope.row.DictionaryCode]"
:key="item.id"
:value="item.value"
:label="item.label"
/>
</el-select>
</template>
<!-- 整体肿瘤评估非基线可修改 -->
<template v-else-if="!task.IsBaseLine && (scope.row.QuestionType=== 13 || scope.row.QuestionType=== 42)">
<el-select
v-if="scope.row.Type==='select' && scope.row.DictionaryCode"
v-model="currentEvaluateResult"
size="mini"
@change="handleEvaluateResultChange"
>
<template v-if="criterionType === 1 && tLesionCount">
<el-option
v-for="item of $d[ scope.row.DictionaryCode]"
v-show="(!task.IsBaseLine && item.value > -1) && (task.CrterionDictionaryGroup.indexOf(item.raw.CrterionDictionaryGroup) > -1) && (item.value !== 1 && item.value !== 3)"
:key="item.id"
:value="item.value"
:label="item.label"
/>
</template>
<template v-else-if="criterionType === 1 && ntLesionCount">
<el-option
v-for="item of $d[ scope.row.DictionaryCode]"
v-show="(!task.IsBaseLine && item.value > -1) && (task.CrterionDictionaryGroup.indexOf(item.raw.CrterionDictionaryGroup) > -1) && (item.value !== 1)"
:key="item.id"
:value="item.value"
:label="item.label"
/>
</template>
<template v-else>
<el-option
v-for="item of $d[ scope.row.DictionaryCode]"
v-show="(!task.IsBaseLine && item.value > -1) && (task.CrterionDictionaryGroup.indexOf(item.raw.CrterionDictionaryGroup) > -1)"
:key="item.id"
:value="item.value"
:label="item.label"
/>
</template>
</el-select>
</template>
<!-- <template v-else-if="task.IsBaseLine && scope.row.QuestionType=== 13">
{{ $fd(scope.row.DictionaryCode, currentEvaluateResult) }}
</template> -->
<!-- 访视点备注是否存在疾病与系统不一致或者整体肿瘤评估与系统不一致时必填 -->
<!-- tumorEvaluate && task.VisitTaskId === visitTaskId && scope.row.QuestionType=== 14 && (currentEvaluateResult !== tumorEvaluate || currentExistDisease !== isExistDisease) -->
<template v-else-if="task.VisitTaskId === visitTaskId && scope.row.QuestionType=== 14 ">
<!-- v-if="tumorEvaluate && (currentEvaluateResult !== tumorEvaluate || currentExistDisease !== isExistDisease)" -->
<template>
<!-- 输入框 -->
<el-input
v-if="scope.row.Type==='input'"
v-model="currentTaskReason"
size="mini"
@change="evaluateReasonChange"
/>
<el-input
v-else-if="scope.row.Type==='textarea'"
v-model="currentTaskReason"
:autosize="{ minRows: 2, maxRows: 4}"
size="mini"
maxlength="500"
@change="evaluateReasonChange"
/>
<!-- 系统评估结果为xxx,与当前调整的结果不一致请填写调整原因 -->
<p v-if="currentEvaluateResult !== tumorEvaluate" style="width: 140px;padding:0 2px;white-space: normal;word-break: break-all;word-wrap: break-word;" v-html="getWarningText()" />
<p v-else-if="currentExistDisease !== isExistDisease" style="width: 140px;padding:0 2px;white-space: normal;word-break: break-all;word-wrap: break-word;">{{ $t('trials:readingReport:title:sysEvaluationRes') }}<span style="color:red">{{ $fd('ExistDisease',isExistDisease) }}</span>{{ $t('trials:readingReport:message:msg1') }}
</p>
</template>
<!-- <template v-else>
<span>{{ currentTaskReason }}</span>
</template> -->
</template>
<template v-else-if="scope.row.DictionaryCode">
{{ $fd(scope.row.DictionaryCode, scope.row.Answers[task.VisitTaskId]) }}
</template>
<template v-else>
{{ scope.row.Answers[task.VisitTaskId] }}
</template>
</template>
<template v-else-if="scope.row.QuestionType=== 15">
<el-tooltip v-if="getAnswerInfo(scope.row.Answer,task.VisitTaskId,'IsGlobalChange')" :content="`${$t('trials:reading:warnning:msg55')}${$fd(scope.row.DictionaryCode, getAnswerInfo(scope.row.Answer,task.VisitTaskId,'GlobalChangeAnswer'))}`" placement="top" effect="light">
<span style="color:red;"> {{ $fd(scope.row.DictionaryCode, scope.row.Answers[task.VisitTaskId]) }}</span>
</el-tooltip>
<span v-else>{{ $fd(scope.row.DictionaryCode, scope.row.Answers[task.VisitTaskId]) }}</span>
</template>
<template v-else-if="scope.row.QuestionType=== 13 || scope.row.QuestionType=== 42">
<el-tooltip v-if="getAnswerInfo(scope.row.Answer,task.VisitTaskId,'IsGlobalChange')" :content="`${$t('trials:reading:warnning:msg55')}${$fd(scope.row.DictionaryCode, getAnswerInfo(scope.row.Answer,task.VisitTaskId,'GlobalChangeAnswer'))}`" placement="top" effect="light">
<span style="color:red;"> {{ $fd(scope.row.DictionaryCode, scope.row.Answers[task.VisitTaskId]) }}</span>
</el-tooltip>
<span v-else>{{ $fd(scope.row.DictionaryCode, scope.row.Answers[task.VisitTaskId]) }}</span>
</template>
<template v-else-if="scope.row.QuestionType=== 22">
{{ scope.row.Answers[task.VisitTaskId] === '-1' ? $t('trials:readingReport:title:unknow') : scope.row.Answers[task.VisitTaskId] }}
</template>
<template v-else-if="scope.row.DictionaryCode">
{{ $fd(scope.row.DictionaryCode, scope.row.Answers[task.VisitTaskId]) }}
</template>
<template v-else-if="criterionType === 10">
{{ isNaN(parseInt(scope.row.Answers[task.VisitTaskId]))?scope.row.Answers[task.VisitTaskId]:`${scope.row.Answers[task.VisitTaskId]}` }}
</template>
<template v-else-if="(scope.row.ValueType === 0 || scope.row.ValueType === 1) && scope.row.Unit">
{{ `${scope.row.Answers[task.VisitTaskId]}` }}
<span v-if="scope.row.Answers[task.VisitTaskId] !== 'NA' && !isNaN(parseInt(scope.row.Answers[task.VisitTaskId]))">{{ `${$fd('ValueUnit',scope.row.Unit)}` }}</span>
</template>
<template v-else-if="scope.row.ValueType === 2">
{{ isNaN(parseInt(scope.row.Answers[task.VisitTaskId]))?scope.row.Answers[task.VisitTaskId]:`${scope.row.Answers[task.VisitTaskId]}%` }}
</template>
<template v-else-if="scope.row.Answers && scope.row.Answers.hasOwnProperty(task.VisitTaskId)">
{{ scope.row.Answers[task.VisitTaskId] }}
</template>
</span>
</template>
</el-table-column>
</el-table>
</div>
</el-card>
<!-- 附加评估 -->
<el-dialog
v-if="additionalAssessmentsDig.visible"
:visible.sync="additionalAssessmentsDig.visible"
:close-on-click-modal="false"
:title="additionalAssessmentsDig.title"
width="600px"
>
<AdditionalAssessment
:questions="assessmentQuestions"
:visit-task-id="visitTaskId"
@close="additionalAssessmentsDig.visible = false"
@sign="sign"
/>
</el-dialog>
<!-- 签名框 -->
<el-dialog
v-if="signVisible"
:visible.sync="signVisible"
:close-on-click-modal="false"
width="600px"
custom-class="base-dialog-wrapper"
>
<div slot="title">
<span style="font-size:18px;">{{ $t('common:dialogTitle:sign') }}</span>
<span style="font-size:12px;margin-left:5px">{{ `(${$t('common:label:sign')}${ currentUser })` }}</span>
</div>
<SignForm ref="signForm" :sign-code-enum="signCode" @closeDialog="closeSignDialog" />
</el-dialog>
</div>
</template>
<script>
import { getReadingReportEvaluation, changeDicomReadingQuestionAnswer, submitDicomVisitTask, verifyVisitTaskQuestions, getTaskAdditionalQuestion } from '@/api/trials'
import { setSkipReadingCache } from '@/api/reading'
import { getAutoCutNextTask } from '@/api/user'
import const_ from '@/const/sign-code'
import SignForm from '@/views/trials/components/newSignForm'
import { getToken } from '@/utils/auth'
import store from '@/store'
import { mapGetters } from 'vuex'
import { changeURLStatic } from '@/utils/history.js'
import AdditionalAssessment from '@/views/trials/trials-panel/reading/dicoms/components/AdditionalAssessment'
export default {
name: 'ReportPage',
components: { SignForm, AdditionalAssessment },
data() {
return {
currentUser: zzSessionStorage.getItem('userName'),
signVisible: false,
signCode: null,
visitTaskList: [],
taskQuestions: [],
loading: false,
answers: [],
readingTaskState: 2,
tumorEvaluate: null,
currentEvaluateResult: null,
isExistDisease: null,
currentExistDisease: null,
currentTaskReason: '',
answerArr: [],
questions: [],
isShowDetail: false,
criterionType: 0,
height: 0,
additionalAssessmentsDig: { visible: false, title: this.$t('trials:trials-panel:setting:reading-unit:TrialCriterionAdditionalAssessmentTypeList') },
assessmentQuestions: [],
tLesionCount: null,
ntLesionCount: null,
openWindow: null,
expandedRows: [],
taskInfo: {},
visitTaskId: '',
isBaselineTask: false
}
},
computed: {
...mapGetters(['language'])
},
watch: {
taskQuestions() {
this.$nextTick(() => {
this.getTableHeight()
this.$refs.reportList && this.$refs.reportList.doLayout()
this.setScrollTop()
})
}
},
created() { this.getTableHeight() },
mounted() {
this.taskInfo = JSON.parse(localStorage.getItem('taskInfo'))
this.criterionType = this.taskInfo.CriterionType
this.visitTaskId = this.taskInfo.VisitTaskId
this.isBaselineTask = this.taskInfo.IsBaseLine
window.addEventListener('resize', () => {
this.handleResize()
this.setScrollTop()
})
this.getReportInfo()
},
beforeDestroy() {
if (this.openWindow) {
this.openWindow.close()
}
},
methods: {
myConfirm(msg) {
return new Promise(resolve => {
this.$confirm(msg, {
type: 'warning',
showCancelButton: true
}).then(() => {
resolve(true)
}).catch(() => {
resolve(false)
})
})
},
async beforeLeave() {
this.handleConfirm()
},
getAnswerInfo(answerArr, visitTaskId, prop) {
var i = answerArr.findIndex(i => i.VisitTaskId === visitTaskId)
if (i === -1) return ''
var val = answerArr[i][prop]
return isNaN(parseInt(val)) ? val : parseInt(val)
},
getTableHeight() {
this.height = window.innerHeight - 170
},
async getReportInfo(IsCalculate) {
this.loading = true
try {
var params = {
visitTaskId: this.visitTaskId,
trialId: this.$router.currentRoute.query.trialId,
IsCalculate: IsCalculate !== false
}
const res = await getReadingReportEvaluation(params)
if (res.IsSuccess) {
this.readingTaskState = res.Result.ReadingTaskState
this.tumorEvaluate = res.Result.CalculateResult && res.Result.CalculateResult.TumorEvaluate ? parseInt(res.Result.CalculateResult.TumorEvaluate) : null
this.isExistDisease = res.Result.CalculateResult && res.Result.CalculateResult.IsExistDisease ? parseInt(res.Result.CalculateResult.IsExistDisease) : null
this.answerArr = []
this.questions = res.Result.TaskQuestions.concat()
this.visitTaskList = res.Result.VisitTaskList
var taskQuestions = this.getQuestions(res.Result.TaskQuestions, !this.isShowDetail, null, null)
this.taskQuestions = []
taskQuestions.forEach(item => {
this.$set(this.taskQuestions, this.taskQuestions.length, item)
})
const tLesion = res.Result.LesionCountList.find(i => i.LesionType === 0)
this.tLesionCount = tLesion ? tLesion.Count : 0
const ntLesion = res.Result.LesionCountList.find(i => i.LesionType === 1)
this.ntLesionCount = ntLesion ? ntLesion.Count : 0
this.setScrollTop()
}
this.loading = false
} catch (e) {
this.loading = false
console.log(e)
}
},
setScrollTop(a) {
setTimeout(() => {
this.$nextTick(() => {
if (this.$refs.reportList) {
this.getTableHeight()
this.$refs.reportList.bodyWrapper.scrollTop = this.$refs.reportList.bodyWrapper.scrollHeight
}
})
}, 50)
},
getQuestions(questions, isNTFilterLength, lesionType, isLymphNodes) {
const arr = []
if (questions.length !== 0) {
questions.forEach((item) => {
if (this.criterionType === 21) {
if (!this.isShowDetail) {
if (!item.RowId) {
this.expandedRows.push(item.Id)
}
} else {
this.expandedRows.push(item.Id)
}
} else {
this.expandedRows.push(item.Id)
}
//
//
//
lesionType = item.LesionType
var filterArr = [7]
// 0 5
// 12678irecisit
// 0123ID4567810
// 11051106
if ((item.LesionType === 1 || item.LesionType === 2 || item.LesionType === 6 || item.LesionType === 7 || item.LesionType === 8) && isNTFilterLength) {
filterArr = [0, 1, 3, 4, 5, 6, 2, 8, 10, 7, 1105]
} else {
filterArr = [3, 4, 5, 6, 2, 8, 10, 7, 1106, 1105]
}
if ((lesionType === 0 || lesionType === 5) && isLymphNodes === 0 && !this.isShowDetail && (this.criterionType === 1 || this.criterionType === 3 || this.criterionType === 17)) {
filterArr.push(1)
}
if (isLymphNodes === 0) {
filterArr.push(1)
}
if (!(filterArr.includes(item.QuestionMark))) {
const obj = item
obj.Answers = {}
if (item.RowIndex > 0) {
try {
var idx = item.Childrens.findIndex(i => i.QuestionMark === 8)
var idxLoc = item.Childrens.findIndex(i => i.QuestionMark === 10)
var state = item.Childrens.findIndex(i => i.QuestionMark === 7)
if (idx > -1) {
if (item.Childrens[idx].Answer.length > 0) {
var k = item.Childrens[idx].Answer.findIndex(v => v.Answer !== '')
var part = ''
if (obj.IsCanEditPosition) {
part = `${item.Childrens[idx].Answer[k].Answer}--${item.Childrens[idxLoc].Answer[k].Answer}`
} else {
part = `${item.Childrens[idx].Answer[k].Answer}`
}
if (item.SplitOrMergeLesionName && k > -1) {
// obj.QuestionName = `${obj.QuestionName} --${part} (Split from ${item.SplitOrMergeLesionName})`
obj.QuestionName = `${obj.QuestionName} --${part}`
var Answers = {}
if (state >= 0) {
this.visitTaskList.forEach(v => {
const o = item.Childrens[state].Answer.find(v1 => {
return v1.VisitTaskId === v.VisitTaskId
})
if (o) {
Answers[o.VisitTaskId] = this.$fd(item.Childrens[state].DictionaryCode, parseInt(o.Answer))
}
})
}
this.$set(obj, 'Answers', Answers)
// obj.QuestionName = `${obj.QuestionName} `
} else if (!item.SplitOrMergeLesionName && k > -1) {
obj.QuestionName = `${obj.QuestionName}--${part}`
const Answers = {}
if (state >= 0) {
this.visitTaskList.forEach(v => {
const o = item.Childrens[state].Answer.find(v1 => {
return v1.VisitTaskId === v.VisitTaskId
})
if (o) {
Answers[o.VisitTaskId] = this.$fd(item.Childrens[state].DictionaryCode, parseInt(o.Answer))
}
})
}
this.$set(obj, 'Answers', Answers)
// obj.QuestionName = `${obj.QuestionName} `
} else {
obj.QuestionName = `${obj.QuestionName} `
}
if (item.ReportMark) {
obj.QuestionName = `${obj.QuestionName} (${item.ReportMark})`
}
if (this.criterionType === 1 || this.criterionType === 3 || this.criterionType === 17) {
var idxLymphNode = item.Childrens.findIndex(i => i.QuestionMark === 2)
if (idxLymphNode > -1) {
isLymphNodes = item.Childrens[idxLymphNode].Answer[k].Answer ? parseInt(item.Childrens[idxLymphNode].Answer[k].Answer) : null
item.Childrens.forEach(v => {
this.$set(v, 'isLymphNodes', isLymphNodes)
})
}
}
}
}
if (this.criterionType === 21) {
var liverSegmentIdx = item.Childrens.findIndex(i => i.QuestionMark === 1106)
if (liverSegmentIdx > -1) {
if (item.Childrens[liverSegmentIdx].Answer.length > 0) {
let v = item.Childrens[liverSegmentIdx].Answer[0].Answer
obj.QuestionName = this.$fd(item.Childrens[liverSegmentIdx].DictionaryCode, parseInt(v))
let Answers = {}
let mean = item.Childrens.findIndex(i => i.QuestionMark === 1104)
if (mean > -1) {
this.visitTaskList.forEach(v => {
const o = item.Childrens[mean].Answer.find(v1 => {
return v1.VisitTaskId === v.VisitTaskId
})
if (o) {
Answers[o.VisitTaskId] = isNaN(parseFloat(o.Answer)) ? o.Answer : `${o.Answer}%`
}
})
}
this.$set(obj, 'Answers', Answers)
}
}
}
} catch (e) {
console.log(e)
}
}
item.Answer.forEach(i => {
if (item.DictionaryCode) {
obj.Answers[i.VisitTaskId] = i.Answer ? parseInt(i.Answer) : null
} else {
obj.Answers[i.VisitTaskId] = i.Answer
}
})
if (item.QuestionType === 15) {
this.currentExistDisease = obj.Answers[this.visitTaskId]
this.answerArr.push({ id: item.QuestionId, answer: obj.Answers[this.visitTaskId], questionType: 15 })
}
if (item.QuestionType === 13 || item.QuestionType === 42) {
this.currentEvaluateResult = obj.Answers[this.visitTaskId]
this.answerArr.push({ id: item.QuestionId, answer: obj.Answers[this.visitTaskId], questionType: item.QuestionType })
}
if (item.QuestionType === 14) {
this.currentTaskReason = obj.Answers[this.visitTaskId]
this.answerArr.push({ id: item.QuestionId, answer: obj.Answers[this.visitTaskId], questionType: 14 })
}
if (item.Childrens.length >= 1) {
obj.Childrens = this.getQuestions(item.Childrens, isNTFilterLength, lesionType, isLymphNodes)
}
arr.push(obj)
}
})
}
return arr
},
handleShowDetail(val) {
this.getReportInfo()
// this.taskQuestions = this.getQuestions(res.Result.TaskQuestions, !this.isShowDetail, null)
},
handleExistDiseaseChange(val) {
// this.currentExistDisease = parseInt(val)
if (val === this.isExistDisease && this.tumorEvaluate === this.currentEvaluateResult) {
this.currentTaskReason = ''
this.evaluateReasonChange('')
}
this.currentTaskReason = ''
this.evaluateReasonChange('')
var idx = this.answerArr.findIndex(i => i.questionType === 15)
if (idx > -1) {
this.answerArr[idx].answer = val
}
},
handleEvaluateResultChange(val) {
// this.currentEvaluateResult = parseInt(val)
if (val === this.tumorEvaluate && this.isExistDisease === this.currentExistDisease) {
this.currentTaskReason = ''
this.evaluateReasonChange('')
}
this.currentTaskReason = ''
this.evaluateReasonChange('')
var idx = this.answerArr.findIndex(i => i.questionType === 13 || i.questionType === 42)
if (idx > -1) {
this.answerArr[idx].answer = val
}
this.setScrollTop()
},
evaluateReasonChange(val) {
var idx = this.answerArr.findIndex(i => i.questionType === 14)
if (idx > -1) {
this.answerArr[idx].answer = val
}
},
async handleConfirm() {
this.loading = true
try {
await this.handleSave(false)
await verifyVisitTaskQuestions({ visitTaskId: this.visitTaskId })
var i = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
var isBaseline = this.visitTaskList[i].IsBaseLine
if (isBaseline) {
const res = await getTaskAdditionalQuestion({ visitTaskId: this.visitTaskId })
this.assessmentQuestions = res.Result
if (this.assessmentQuestions.length > 0) {
//
this.additionalAssessmentsDig.visible = true
} else {
const { ImageAssessmentReportConfirmation } = const_.processSignature
this.signCode = ImageAssessmentReportConfirmation
this.signVisible = true
}
} else {
const { ImageAssessmentReportConfirmation } = const_.processSignature
this.signCode = ImageAssessmentReportConfirmation
this.signVisible = true
}
this.loading = false
} catch (e) {
this.loading = false
}
},
sign() {
this.additionalAssessmentsDig.visible = false
this.$nextTick(() => {
const { ImageAssessmentReportConfirmation } = const_.processSignature
this.signCode = ImageAssessmentReportConfirmation
this.signVisible = true
})
},
handleResize() {
this.$nextTick(() => {
this.$refs.reportList && this.$refs.reportList.doLayout()
})
},
//
closeSignDialog(isSign, signInfo) {
if (isSign) {
this.signConfirm(signInfo)
} else {
this.signVisible = false
}
},
//
async signConfirm(signInfo) {
this.loading = true
try {
var params = {
data: {
visitTaskId: this.visitTaskId
},
signInfo: signInfo
}
const res = await submitDicomVisitTask(params)
if (res.IsSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
if (this.$refs['signForm']) {
this.$refs['signForm'].btnLoading = false
}
this.signVisible = false
// window.location.reload()
// window.opener.postMessage('refreshTaskList', window.location)
//
this.readingTaskState = 2
this.$emit('setReadingTaskState', 2)
const res = await getAutoCutNextTask()
var isAutoTask = res.Result.AutoCutNextTask
if (isAutoTask) {
window.location.reload()
} else {
// ''
this.$confirm(this.$t('trials:readingReport:message:msg4'), {
type: 'warning',
distinguishCancelAndClose: true
})
.then(() => {
window.location.reload()
})
.catch(action => {
changeURLStatic('visitTaskId', this.visitTaskId)
})
}
window.opener.postMessage('refreshTaskList', window.location)
}
this.loading = false
} catch (e) {
this.loading = false
if (this.$refs['signForm'] && this.$refs['signForm'].btnLoading) {
this.$refs['signForm'].btnLoading = false
}
}
},
previewDicoms(task) {
if (this.openWindow) {
this.openWindow.close()
}
var token = getToken()
var trialReadingCriterionId = this.$router.currentRoute.query.TrialReadingCriterionId
// var subjectCode = this.$router.currentRoute.query.subjectCode
var subjectCode = localStorage.getItem('subjectCode')
var subjectId = this.$router.currentRoute.query.subjectId
var trialId = this.$router.currentRoute.query.trialId
var isReadingTaskViewInOrder = this.$router.currentRoute.query.isReadingTaskViewInOrder
var criterionType = this.$router.currentRoute.query.criterionType
var readingTool = this.$router.currentRoute.query.readingTool
var path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${trialId}&subjectCode=${subjectCode}&subjectId=${subjectId}&visitTaskId=${task.VisitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}`
const routeData = this.$router.resolve({ path })
this.openWindow = window.open(routeData.href, '_blank')
},
handleSave(isPrompt) {
return new Promise(async(resolve, reject) => {
var isBeill
var evaluateResult = ''
var evaluateAjustReason = ''
this.answers = []
var isExistEvaluateResult = false
this.answerArr.map(item => {
if (item.questionType === 13 || item.questionType === 42) {
evaluateResult = item.answer
isExistEvaluateResult = true
}
if (item.questionType === 14) {
evaluateAjustReason = item.answer
}
if (item.questionType === 15) {
isBeill = item.answer
}
this.answers.push({ id: item.id, answer: item.answer })
})
console.log(this.currentExistDisease, this.isExistDisease, evaluateAjustReason, isBeill)
if (this.currentExistDisease !== this.isExistDisease && !evaluateAjustReason) {
this.$confirm(this.$t('trials:readingReport:message:msg2'), {
type: 'warning',
showCancelButton: false,
callback: action => {}
})
resolve()
return
}
if (isExistEvaluateResult && (evaluateResult === null && !this.isBaselineTask)) {
//
this.$confirm(this.$t('trials:readingReport:message:msg2'), {
type: 'warning',
showCancelButton: false,
callback: action => {}
})
resolve()
return
}
if (isExistEvaluateResult && (evaluateResult !== this.tumorEvaluate) && !evaluateAjustReason) {
//
this.$confirm(this.$t('trials:readingReport:message:msg3'), {
type: 'warning',
showCancelButton: false,
callback: action => {}
})
resolve()
return
}
this.loading = true
try {
var params = {
visitTaskId: this.visitTaskId,
answers: this.answers
}
const res = await changeDicomReadingQuestionAnswer(params)
if (res.IsSuccess && isPrompt) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
this.loading = false
resolve()
} catch (e) {
this.loading = false
reject()
}
})
},
getWarningText() {
var sysRes = ''
var curRes = ''
if (this.CriterionType === 2) {
sysRes = this.$fd('ImagingOverallAssessment_Lugano', this.tumorEvaluate)
curRes = this.$fd('ImagingOverallAssessment_Lugano', this.currentEvaluateResult)
} else {
sysRes = this.$fd('OverallAssessment', this.tumorEvaluate)
curRes = this.$fd('OverallAssessment', this.currentEvaluateResult)
}
if (!curRes) {
return ''
}
const msg = this.$t('trials:readingReport:message:msg9').replace('xxx', '<font color="red">' + sysRes + '</font>').replace('yyy', '<font color="red">' + curRes + '</font>')
return msg
},
async skipTask() {
try {
//
const confirm = await this.$confirm(
this.$t('trials:readingReport:message:skipConfirm'),
{
type: 'warning',
distinguishCancelAndClose: true
}
)
if (confirm !== 'confirm') return
this.loading = true
const res = await setSkipReadingCache({ visitTaskId: this.visitTaskId })
this.loading = false
if (res.IsSuccess) {
window.location.reload()
}
} catch (e) {
this.loading = false
console.log(e)
}
}
}
}
</script>
<style lang="scss" scoped>
.report-wrapper{
height: 100%;
padding: 10px 0px;
// background-color: #fff;
background-color: #000;
::-webkit-scrollbar {
width: 7px;
height: 7px;
}
::-webkit-scrollbar-thumb {
border-radius: 10px;
background: #d0d0d0;
}
.report-header{
display: flex;
}
.el-card{
display:flex;flex-direction: column;height: 100%;
background-color: #000;
color: #ffffff;
border:none;
::v-deep .el-card__body{
flex: 1;
}
}
// ::v-deep .el-table__cell{
// background-color: #000;
// color: #ffffff;
// }
// ::v-deep .el-table{
// background-color: #000;
// color: #ffffff;
// }
// ::v-deep .el-table__cell{
// background-color: #000;
// color: #ffffff;
// }
::v-deep .el-table, .el-table__expanded-cell {
background-color: #000;
color: #fff;
border-color:#444444;
}
::v-deep .el-table th, .el-table tr {
background-color: #000;
color: #fff;
border-color:#444444;
}
::v-deep .el-table__body tr > td{
background-color:#000 !important;
color: #fff;
border-color:#444444;
}
::v-deep .el-table__body tr:hover > td{
background-color:#858282 !important;
color: #fff;
border-color:#444444;
}
::v-deep .el-table--border th.gutter:last-of-type{
border: none;
}
::v-deep .el-card__header{
border: none;
padding: 10px;
}
}
::v-deep .el-switch__label{
color:#fff;
}
::v-deep .el-switch__label.is-active{
color: #428bca;
}
.colorOfRed{
color: #f66;
}
</style>

View File

@ -22,13 +22,13 @@
v-if="!study.IsCriticalSequence"
class="dicom-desc"
>
<template v-if="taskInfo && taskInfo.IsShowStudyName">
<template v-if="taskInfo && taskInfo.IsShowStudyName">
<div style="text-overflow: ellipsis;overflow: hidden;">
<span :title="study.StudyCode">{{ study.StudyCode }}</span>
<span v-if="study.StudyName" :title="study.StudyName" style="margin-left: 5px;">{{ study.StudyName }}</span>
<span v-else :title="study.Modalities" style="margin-left: 5px;">{{ `${study.Modalities} (${study.SeriesCount})` }}</span>
</div>
<div style="text-overflow: ellipsis;overflow: hidden;" v-if="study.StudyName" >
<div v-if="study.StudyName" style="text-overflow: ellipsis;overflow: hidden;">
<span :title="study.Modalities">{{ `${study.Modalities} (${study.SeriesCount})` }}</span>
</div>
</template>
@ -38,7 +38,7 @@
<span :title="study.Modalities">{{ `${study.Modalities} (${study.SeriesCount})` }}</span>
</div>
</template>
</div>
<div v-else>
<!-- 关键序列 -->
@ -64,14 +64,14 @@
crossorigin="anonymous"
/>
</div>
<div class="series-text" >
<div class="text-desc" v-if="!study.IsCriticalSequence" :title="series.SeriesNumber">
<div class="series-text">
<div v-if="!study.IsCriticalSequence" class="text-desc" :title="series.SeriesNumber">
#{{ series.SeriesNumber }}
</div>
<div class="text-desc" v-if="series.Description" :title="series.Description">
<div v-if="series.Description" class="text-desc" :title="series.Description">
{{ series.Description }}
</div>
<div class="text-desc" v-if="series.SliceThickness && !study.IsCriticalSequence">
<div v-if="series.SliceThickness && !study.IsCriticalSequence" class="text-desc">
T: {{ parseFloat(series.SliceThickness).toFixed(digitPlaces) }}
</div>
<div class="text-desc">
@ -80,7 +80,7 @@
</span>
<span v-show="series.LoadedImageCount >= series.InstanceCount">{{ series.Modality }}: {{ series.InstanceCount }} image</span>
</div>
<div v-show="series.IsBeMark">
<div v-show="annotations.findIndex(v=>v.seriesId === series.Id && v.MeasureData) > -1">
<i class="el-icon-star-on" style="font-size: 16px;color: #ff5722;" />
</div>
</div>
@ -116,14 +116,16 @@ export default {
activeSeriesIndex: -1,
taskInfo: null,
studyList: [],
annotations: [],
digitPlaces: 2
}
},
mounted() {
this.taskInfo = JSON.parse(localStorage.getItem('taskInfo'))
let digitPlaces = Number(localStorage.getItem('digitPlaces'))
const digitPlaces = Number(localStorage.getItem('digitPlaces'))
this.digitPlaces = digitPlaces === -1 ? this.digitPlaces : digitPlaces
this.studyList = this.visitTaskInfo.StudyList
this.annotations = this.visitTaskInfo.Annotations
if (this.studyList.length === 0) return
this.$nextTick(() => {
this.activeStudy(this.studyList[0].StudyId)
@ -142,22 +144,22 @@ export default {
setSeriesActive(studyIndex, seriesIndex) {
this.activeStudyIndex = studyIndex
this.activeSeriesIndex = seriesIndex
let studyId = this.studyList[studyIndex].StudyId
const studyId = this.studyList[studyIndex].StudyId
if (!studyId) return
this.activeStudy(studyId)
},
getPreviousOrNextSeries(type, series) {
let seriseList = this.studyList.map(s => s.SeriesList).flat()
let i = seriseList.findIndex(i => i.Id === series.Id && i.StudyId === series.StudyId)
const seriseList = this.studyList.map(s => s.SeriesList).flat()
const i = seriseList.findIndex(i => i.Id === series.Id && i.StudyId === series.StudyId)
if (i === -1) return
let newIndex = null
if (type === -1) {
newIndex = i === 0 ? i : i - 1
} else {
newIndex = i >= seriseList.length - 1 ? i : i +1
newIndex = i >= seriseList.length - 1 ? i : i + 1
}
let studyIndex = seriseList[newIndex].StudyIndex
let seriesIndex = seriseList[newIndex].SeriesIndex
const studyIndex = seriseList[newIndex].StudyIndex
const seriesIndex = seriseList[newIndex].SeriesIndex
this.setSeriesActive(studyIndex, seriesIndex)
this.activeSeries(seriseList[newIndex], seriesIndex, studyIndex)
}

View File

@ -1,22 +1,23 @@
<template>
<div
<div
ref="viewport"
class="viewport-wrapper"
@mouseup="sliderMouseup"
@mousemove="sliderMousemove"
@mouseleave="sliderMouseleave"
>
<div class="left-top-text" v-if="series && taskInfo">
<div v-if="series && taskInfo" class="left-top-text">
<div
v-if="taskInfo.IsExistsClinicalData"
class="cd-info"
:title="$t('trials:reading:button:clinicalData')"
>
<svg-icon
style="cursor: pointer;"
icon-class="documentation"
class="svg-icon"
@click.stop="viewCD(series.TaskInfo.VisitTaskId)" />
<svg-icon
style="cursor: pointer;"
icon-class="documentation"
class="svg-icon"
@click.stop="viewCD(series.TaskInfo.VisitTaskId)"
/>
</div>
<h2
v-if="taskInfo.IsReadingShowSubjectInfo"
@ -26,7 +27,7 @@
</h2>
<div>Series: #{{ series.SeriesNumber }}</div>
<div>Image: #{{ `${series.SliceIndex + 1}/${series.ImageIds.length}` }}</div>
<div>{{series.Modality}}</div>
<div>{{ series.Modality }}</div>
</div>
<div
v-if="series && taskInfo && taskInfo.IsReadingTaskViewInOrder === 1"
@ -54,10 +55,10 @@
</div>
</div>
</div>
<div class="right-top-text" v-if="series">
<div v-if="series" class="right-top-text">
<div>{{ series.Description }}</div>
</div>
<div class="left-bottom-text" v-if="series">
<div v-if="series" class="left-bottom-text">
<div v-show="mousePosition.index.length > 0">
Pos: {{ mousePosition.index[0] }}, {{ mousePosition.index[1] }}, {{ mousePosition.index[2] }}
</div>
@ -74,7 +75,7 @@
W*H: {{ imageInfo.size }}
</div>
</div>
<div class="right-bottom-text" v-if="series">
<div v-if="series" class="right-bottom-text">
<div v-show="imageInfo.location">Location: {{ `${Number(imageInfo.location).toFixed(digitPlaces)} mm` }}</div>
<div v-show="series.SliceThickness">Slice Thickness: {{ `${Number(series.SliceThickness).toFixed(digitPlaces)} mm` }}</div>
<div v-show="imageInfo.wwwc ">WW/WL: {{ imageInfo.wwwc }}</div>
@ -98,20 +99,20 @@
</div>
</template>
<script>
import * as cornerstonejs from '@cornerstonejs/core'
// import * as cornerstonejs from '@cornerstonejs/core'
import {
RenderingEngine,
Enums,
imageLoader,
// RenderingEngine,
// Enums,
// imageLoader,
metaData,
getRenderingEngine,
eventTarget,
// eventTarget,
utilities as csUtils
} from '@cornerstonejs/core'
import * as cornerstoneTools from '@cornerstonejs/tools'
import { vec3, mat4 } from 'gl-matrix'
import { vec3 } from 'gl-matrix'
export default {
name: 'Viewport',
name: 'ImageViewport',
props: {
renderingEngineId: {
type: String,
@ -126,7 +127,7 @@ export default {
required: true
}
},
data(){
data() {
return {
element: '',
series: null,
@ -137,10 +138,10 @@ export default {
isMove: false,
height: 0
},
mousePosition: {
index: [],
value: null,
modalityUnit: '',
mousePosition: {
index: [],
value: null,
modalityUnit: '',
world: []
},
imageInfo: {
@ -160,12 +161,11 @@ export default {
},
mounted() {
this.taskInfo = JSON.parse(localStorage.getItem('taskInfo'))
let digitPlaces = Number(localStorage.getItem('digitPlaces'))
const digitPlaces = Number(localStorage.getItem('digitPlaces'))
this.digitPlaces = digitPlaces === -1 ? this.digitPlaces : digitPlaces
this.$nextTick(()=>{
this.$nextTick(() => {
this.initViewport()
})
console.log(cornerstonejs)
},
methods: {
initViewport() {
@ -180,12 +180,12 @@ export default {
resizeObserver.observe(this.element)
this.element.addEventListener('CORNERSTONE_STACK_NEW_IMAGE', this.stackNewImage)
this.element.addEventListener('CORNERSTONE_VOI_MODIFIED', this.voiModified)
this.element.addEventListener('CORNERSTONE_TOOLS_MOUSE_MOVE', this.cornerstoneToolsMouseMove)
this.element.addEventListener('mouseleave', () => {
this.mousePosition.index = []
})
// console.log(cornerstonejs,cornerstoneTools)
this.mousePosition.index = []
})
// console.log(cornerstoneTools)
// element.addEventListener('CORNERSTONE_STACK_NEW_IMAGE', this.stackNewImage)
},
stackNewImage(e) {
@ -194,13 +194,14 @@ export default {
this.sliderInfo.height = detail.imageIdIndex * 100 / (this.series.ImageIds.length - 1)
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine.getViewport(this.viewportId)
let zoom = viewport.getZoom()
const zoom = viewport.getZoom()
this.imageInfo.zoom = zoom.toFixed(4)
this.imageInfo.size = `${detail.image.columns}*${detail.image.rows}`
const imagePlaneModule = metaData.get('imagePlaneModule', detail.imageId)
this.imageInfo.location = imagePlaneModule.sliceLocation
// this.imageInfo.wwwc = `${Math.round(detail.image.windowWidth)}/${Math.round(detail.image.windowCenter)}`
this.getOrientationMarker()
this.$emit('renderAnnotations', this.series)
},
voiModified(e) {
const renderingEngine = getRenderingEngine(this.renderingEngineId)
@ -239,9 +240,9 @@ export default {
}
},
setMarkers() {
let markers = [...this.orientationMarkers]
const markers = [...this.orientationMarkers]
for (const key in this.markers) {
let v = markers.shift(0)
const v = markers.shift(0)
this.markers[key] = v
}
},
@ -258,7 +259,7 @@ export default {
this.resetOrientationMarkers()
return
}
let markers = [...this.orientationMarkers]
const markers = [...this.orientationMarkers]
if (type === 2) {
//
this.orientationMarkers[0] = markers[2]
@ -281,7 +282,7 @@ export default {
this.playClipState = isPlay
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine.getViewport(this.viewportId)
if (isPlay) {
cornerstoneTools.utilities.cine.playClip(viewport.element, { framesPerSecond })
} else {
@ -291,9 +292,9 @@ export default {
scrollPage(type) {
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine.getViewport(this.viewportId)
const currentImageIdIndex = viewport.getCurrentImageIdIndex();
const numImages = viewport.getImageIds().length;
let newImageIdIndex = null;
const currentImageIdIndex = viewport.getCurrentImageIdIndex()
const numImages = viewport.getImageIds().length
let newImageIdIndex = null
if (type === 0) {
newImageIdIndex = 0
} else if (type === -1) {
@ -301,34 +302,36 @@ export default {
} else if (type === 1) {
newImageIdIndex = currentImageIdIndex === numImages - 1 ? currentImageIdIndex : currentImageIdIndex + 1
} else if (type === 99999) {
newImageIdIndex = numImages -1
newImageIdIndex = numImages - 1
}
viewport.setImageIdIndex(newImageIdIndex);
viewport.setImageIdIndex(newImageIdIndex)
},
setZoom(ratio) {
const renderingEngine = getRenderingEngine(this.renderingEngineId);
const viewport = renderingEngine.getViewport(this.viewportId);
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine.getViewport(this.viewportId)
const zoom = viewport.getZoom();
const zoom = viewport.getZoom()
if (ratio > 0) {
viewport.setZoom(zoom * 1.05);
viewport.setZoom(zoom * 1.05)
} else {
viewport.setZoom(zoom / 1.05);
viewport.setZoom(zoom / 1.05)
}
viewport.render();
viewport.render()
},
resize(forceFitToWindow) {
console.log('resize: ', forceFitToWindow)
const renderingEngine = getRenderingEngine(this.renderingEngineId);
const renderingEngine = getRenderingEngine(this.renderingEngineId)
renderingEngine.resize(true, forceFitToWindow)
},
async setSeriesInfo(obj){
if (this.series && obj.Id === this.series.Id && obj.Description === this.series.Description) return
this.series = {...obj}
async setSeriesInfo(obj, isLocate = false) {
if (this.series && obj.Id === this.series.Id && obj.Description === this.series.Description && !isLocate) {
obj.SliceIndex = this.series.SliceIndex
}
this.series = { ...obj }
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine.getViewport(this.viewportId)
await viewport.setStack(obj.ImageIds, obj.SliceIndex)
cornerstoneTools.utilities.stackPrefetch.enable(viewport.element);
cornerstoneTools.utilities.stackPrefetch.enable(viewport.element)
viewport.render()
},
cornerstoneToolsMouseMove(e) {
@ -348,7 +351,7 @@ export default {
this.$emit('activeViewport', this.viewportIndex)
const num = visitTaskNum + i
if (num >= 0 && num <= this.taskInfo.VisitNum) {
this.$emit('toggleTaskByViewport', {series: this.series, visitTaskNum: num})
this.$emit('toggleTaskByViewport', { series: this.series, visitTaskNum: num })
}
evt.stopImmediatePropagation()
@ -410,7 +413,7 @@ export default {
e.stopImmediatePropagation()
e.stopPropagation()
e.preventDefault()
},
}
}
}
</script>
@ -548,4 +551,4 @@ export default {
z-index: 1;
}
}
</style>
</style>

View File

@ -6,6 +6,7 @@
<!-- 阅片 -->
<el-tab-pane
v-if="taskInfo"
ref="readPage"
:label="$t('trials:reading:tabTitle:review')"
name="read"
>
@ -17,7 +18,10 @@
:label="$t('trials:reading:tabTitle:report')"
name="report"
>
<report-page v-if="activeName === 'report'" />
<report-page
v-if="activeName === 'report'"
@setReadingTaskState="setReadingTaskState"
/>
</el-tab-pane>
</el-tabs>
</div>
@ -39,6 +43,11 @@ export default {
},
mounted() {
this.taskInfo = JSON.parse(localStorage.getItem('taskInfo'))
},
methods: {
setReadingTaskState(state) {
this.$refs['readPage'].setReadingTaskState(state)
}
}
}
</script>

View File

@ -0,0 +1,318 @@
<template>
<div>
<div
v-if="!!question.GroupName && question.Type==='group'"
>
<h4 style="color: #ddd;padding: 5px 0px;margin: 0;">
{{ language==='en'?question.GroupEnName:question.GroupName }}
</h4>
</div>
<template v-else>
<el-form-item
v-if="(question.ShowQuestion===1 && question.ParentTriggerValueList.includes(String(questionForm[question.ParentId]))) || question.ShowQuestion===0 "
:label="`${question.QuestionName}`"
:prop="question.Id"
:rules="[
{ required: (question.IsRequired === 0 || (question.IsRequired ===1 && question.RelevanceId && (questionForm[question.RelevanceId] === question.RelevanceValue))) && question.Type!=='group' && question.Type!=='summary',
message: ['radio', 'select', 'checkbox'].includes(question.Type) ? $t('common:ruleMessage:select') : $t('common:ruleMessage:specify'), trigger: ['blur', 'change']},
]"
:class="[question.Type==='group' ? 'mb' : (question.Type==='upload' || question.QuestionName.length > 15) ?'uploadWrapper' : '']"
>
<!-- 输入框 -->
<el-input
v-if="question.Type==='input'"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2"
@change="((val)=>{formItemChange(val, question)})"
/>
<!-- 多行文本输入框 -->
<el-input
v-else-if="question.Type==='textarea'"
v-model="questionForm[question.Id]"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"
maxlength="500"
:disabled="readingTaskState >= 2"
@change="((val)=>{formItemChange(val, question)})"
/>
<!-- 下拉框 -->
<el-select
v-else-if="question.Type==='select'"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2 || ((question.TableQuestionType === 2 || question.QuestionGenre === 2) && !!question.DictionaryCode)"
clearable
@change="((val)=>{formItemChange(val, question)})"
>
<template v-if="question.TableQuestionType === 1">
<el-option
v-for="item in organList"
:key="item.Id"
:label="item[question.DataTableColumn]"
:value="item[question.DataTableColumn]"
/>
</template>
<template v-else-if="question.TableQuestionType === 3 || question.QuestionGenre === 3">
<el-option
v-for="item of $d[question.DictionaryCode]"
:key="item.id"
:value="String(item.value)"
:label="item.label"
/>
</template>
<template v-else-if="(question.TableQuestionType === 2 || question.QuestionGenre === 2) && question.DictionaryCode">
<el-option
v-for="item of $d[question.DictionaryCode]"
:key="item.id"
:value="String(item.value)"
:label="item.label"
/>
</template>
<template v-else>
<el-option
v-for="val in question.TypeValue.split('|')"
:key="val"
:label="val"
:value="val"
/>
</template>
</el-select>
<!-- 单选 -->
<el-radio-group
v-else-if="question.Type==='radio'"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2"
@change="((val)=>{formItemChange(val, question)})"
>
<template v-if="question.DictionaryCode">
<el-radio
v-for="item of $d[question.DictionaryCode]"
:key="item.id"
:label="String(item.value)"
>
{{ item.label }}
</el-radio>
</template>
<template v-else-if="question.TypeValue">
<el-radio
v-for="val in question.TypeValue.split('|')"
:key="val"
:label="val"
>
{{ val }}
</el-radio>
</template>
</el-radio-group>
<!-- 复选框 -->
<el-checkbox-group
v-else-if="question.Type==='checkbox'"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2"
>
<el-checkbox
v-for="val in question.TypeValue.split('|')"
:key="val"
:label="val"
>
{{ val }}
</el-checkbox>
</el-checkbox-group>
<!-- 数值 -->
<template v-else-if="question.Type==='number'">
<el-input-number
v-if="question.ValueType === 0"
v-model="questionForm[question.Id]"
:precision="0"
:disabled="readingTaskState >= 2"
/>
<el-input-number
v-else-if="question.ValueType === 3"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2"
/>
<el-input-number
v-else-if="question.ValueType === 1 || question.ValueType === 2"
v-model="questionForm[question.Id]"
:precision="digitPlaces"
:disabled="readingTaskState >= 2"
/>
</template>
<el-input
v-else-if="question.Type==='calculation'"
v-model="questionForm[question.Id]"
disabled
>
<template v-if="question.Unit" slot="append">
{{ $fd('ValueUnit', parseInt(question.Unit)) }}
</template>
</el-input>
<!-- 上传图像 -->
<el-upload
v-else-if="question.Type==='upload'"
action
:accept="accept"
:limit="question.ImageCount"
:on-preview="handlePictureCardPreview"
:before-upload="handleBeforeUpload"
:http-request="uploadScreenshot"
list-type="picture-card"
:on-remove="handleRemove"
:file-list="fileList"
:class="{disabled:fileList.length >= question.ImageCount}"
:disabled="readingTaskState >= 2"
>
<i slot="default" class="el-icon-plus" />
<div slot="file" slot-scope="{file}">
<img
class="el-upload-list__item-thumbnail"
:src="OSSclientConfig.basePath + file.url"
alt=""
>
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-preview"
@click="handlePictureCardPreview(file)"
>
<i class="el-icon-zoom-in" />
</span>
<span
v-if="readingTaskState < 2"
class="el-upload-list__item-delete"
@click="handleRemove(file)"
>
<i class="el-icon-delete" />
</span>
</span>
</div>
</el-upload>
<el-dialog
v-if="question.Type==='upload'"
append-to-body
:visible.sync="imgVisible"
width="600px"
>
<el-image :src="imageUrl" width="100%">
<div slot="placeholder" class="image-slot">
加载中<span class="dot">...</span>
</div>
</el-image>
</el-dialog>
</el-form-item>
</template>
<question-form-item
v-for="(item) in question.Childrens"
:key="item.Id"
:question="item"
:question-form="questionForm"
:reading-task-state="readingTaskState"
:visitTaskId="visitTaskId"
/>
</div>
</template>
<script>
// import { uploadReadingAnswerImage } from '@/api/trials'
import { mapGetters } from 'vuex'
export default {
name: 'QuestionFormItem',
props: {
questionForm: {
type: Object,
default() {
return {}
}
},
question: {
type: Object,
default() {
return {}
}
},
readingTaskState: {
type: Number,
required: true
},
visitTaskId: {
type: String,
default: ''
}
},
data() {
return {
fileList: [],
accept: '.png,.jpg,.jpeg',
imgVisible: false,
imageUrl: '',
urls: [],
digitPlaces: 2
}
},
computed: {
...mapGetters(['language'])
},
watch: {
questionForm: {
deep: true,
immediate: true,
handler(v) {
// console.log(v)
}
}
},
mounted() {
let digitPlaces = Number(localStorage.getItem('digitPlaces'))
this.digitPlaces = digitPlaces === -1 ? this.digitPlaces : digitPlaces
},
methods: {
formItemChange(v, question) {
if (question.Childrens.length > 0) {
this.resetChild(question.Childrens)
} else {
this.$emit('setFormItemData', { key: question.Id, val: v })
}
},
resetChild(obj) {
obj.forEach(i => {
this.$emit('resetFormItemData', i.Id)
if (i.Childrens && i.Childrens.length > 0) {
this.resetChild(i.Childrens)
}
})
},
resetFormItemData(v) {
this.$emit('resetFormItemData', v)
},
setFormItemData(obj) {
this.$emit('setFormItemData', obj)
},
}
}
</script>
<style lang="scss" scoped>
.mb{
margin-bottom: 0px;
}
.disabled{
::v-deep .el-upload--picture-card {
display: none;
}
}
.uploadWrapper{
display: flex;
flex-direction: column !important;
align-items: flex-start;
}
::v-deep .el-input.is-disabled .el-input__inner{
background-color: #646464a1;
}
::v-deep .el-input-group__append, .el-input-group__prepend{
padding: 0 10px;
}
::v-deep .el-form-item__content {
width: 100%;
}
::v-deep .el-select.is-disabled .el-input__inner{
background-color: #646464a1;
}
</style>

View File

@ -0,0 +1,360 @@
<template>
<div class="table-question-form">
<div style="display: flex;justify-content: space-between;">
<h3 v-if="qsForm.LesionName" style="color: #ddd;padding: 5px 0px;margin: 0;">
{{ qsForm.LesionName }}
</h3>
<!-- 关闭 -->
<div>
<i class="el-icon-circle-close" style="font-size: 25px;cursor: pointer;" @click="close" />
</div>
</div>
<el-form-item
:label="$t('trials:reading:title:lesionType')"
prop="LesionType"
:rules="[
{ required:true,message: $t('common:ruleMessage:select'), trigger: ['blur']},
]"
>
<!-- 下拉框 -->
<el-select
v-model="qsForm.LesionType"
filterable
:disabled="!isCurrentTask || readingTaskState>=2 || !isBaseLineTask"
@change="((val)=>{lesionTypeChange(val)})"
>
<el-option
v-for="item of $d.LesionType"
v-show="!(isBaseLineTask && item.value === 2)"
:key="item.id"
:value="item.value"
:label="item.label"
/>
</el-select>
</el-form-item>
<!-- 靶病灶非靶病灶的器官为肝脏/Liver时,需要选择典型肝内病灶该值默认为否如果选择是对于靶病灶需要提示按照独立影像评估章程的相关规则对于典型肝内靶病灶请测量肿瘤活性部分最长径提示方式为在长径属性上显示浮框或者增加
-->
<el-form-item
v-for="qs in questions"
v-show="qs.ShowQuestion!==2"
:key="qs.Id"
:label="`${qs.QuestionName}`"
:prop="qs.Id"
:title="(qs.QuestionMark === 0 && (questionForm.LesionOrgan === '肝脏' || questionForm.LesionOrgan === 'Liver') && (lesionType === 0 || lesionType === 1) && questionForm.IntrahepaticLesion) ? $t('trials:mRecist:warnning:msg1') : ''"
:rules="[
{ required: (qs.IsRequired === 0 || (qs.IsRequired ===1 && qs.RelevanceId && (questionForm[qs.RelevanceId] === qs.RelevanceValue)) || (qs.QuestionMark === 6 && questionForm.IsCanEditPosition === true) || (questionForm.IsCanEditPosition && qs.QuestionMark === 10) || (qs.QuestionMark === 21 && (questionForm.LesionOrgan === '肝脏' || questionForm.LesionOrgan === 'Liver') && (lesionType === 0 || lesionType === 1 || lesionType === 2)) || (qs.QuestionMark === 0 && (questionForm.LesionOrgan === '肝脏' || questionForm.LesionOrgan === 'Liver') && (lesionType === 0 || lesionType === 1 || lesionType === 2))) && qs.Type!=='group' && qs.Type!=='summary',
message:['radio', 'select', 'checkbox'].includes(qs.Type) ? $t('common:ruleMessage:select') : $t('common:ruleMessage:specify'), trigger: ['blur','change']},
]"
>
<!-- 输入框 -->
<template v-if="qs.Type==='input' || qs.Type==='number'">
<el-input
v-if="qs.Type==='input' || qs.Type==='number'"
v-model="qsForm[qs.Id]"
:disabled="!isCurrentTask || readingTaskState>=2 || qs.QuestionMark === 0 || qs.QuestionMark === 1 || qs.QuestionMark === 2 || qs.QuestionMark === 5 || (qs.QuestionMark === 6 && (isCurrentTaskAdd === 'False' || (isCurrentTaskAdd === 'True' && !questionForm.IsCanEditPosition) || !!answer.SplitOrMergeLesionName))|| (qs.QuestionMark === 8 && (isCurrentTaskAdd === 'False' || !!answer.SplitOrMergeLesionName) && lesionType !== 2) || (qs.QuestionMark === 10 && (isCurrentTaskAdd === 'False' || !!answer.SplitOrMergeLesionName))"
@change="((val)=>{formItemChange(val, qs)})"
>
<template v-if="(qs.QuestionMark===0 || qs.QuestionMark===1) && qs.Unit" slot="append">
{{ $fd('ValueUnit', parseInt(qs.Unit)) }}
</template>
</el-input>
</template>
<!-- 多行文本输入框 -->
<el-input
v-if="qs.Type==='textarea'"
v-model="qsForm[qs.Id]"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"
:disabled="!isCurrentTask || readingTaskState>=2"
@change="((val)=>{formItemChange(val, qs)})"
/>
<!-- 下拉框 -->
<el-select
v-if="qs.Type==='select'"
v-model="qsForm[qs.Id]"
filterable
:placeholder="qs.QuestionMark === 8 ? $t('common:placeholder:selectorsearch') : qs.QuestionMark === 2 ? '' : $t('common:placeholder:select')"
:disabled="!isCurrentTask || readingTaskState>=2 || qs.QuestionMark === 0 || qs.QuestionMark === 1 || qs.QuestionMark === 2 || qs.QuestionMark === 5 || (qs.QuestionMark === 6 && (isCurrentTaskAdd === 'False' || (isCurrentTaskAdd === 'True' && !questionForm.IsCanEditPosition) || !!answer.SplitOrMergeLesionName))|| (qs.QuestionMark === 8 && (isCurrentTaskAdd === 'False'|| !!answer.SplitOrMergeLesionName)) || (qs.QuestionMark === 10 && (isCurrentTaskAdd === 'False' || (isCurrentTaskAdd === 'True' && !questionForm.IsCanEditPosition) || !!answer.SplitOrMergeLesionName))"
@change="((val)=>{formItemChange(val, qs)})"
>
<template v-if="qs.QuestionMark === 8" #prefix>
<span style="padding-left: 5px;">
<i class="el-icon-search" />
</span>
</template>
<template v-if="qs.TableQuestionType === 1">
<el-option
v-for="item in organList"
:key="item.Id"
:label="item[qs.DataTableColumn]"
:value="item[qs.DataTableColumn]"
/>
</template>
<template v-else-if="qs.DictionaryCode && qs.QuestionMark === 7 && isBaseLineTask">
<el-option
v-for="item of $d[qs.DictionaryCode]"
v-show="(lesionType === 0 && item.value ===0) || (lesionType === 1 && (item.value ===0))"
:key="item.id"
:value="item.value"
:label="item.label"
/>
</template>
<template v-else-if="qs.DictionaryCode && qs.QuestionMark === 7 && !isBaseLineTask">
<el-option-group
:label="!isNaN(parseFloat(answer.LastTaskState)) ? `${$t('trials:dicomReading:tip:lastVisitStatus')} ${$fd(qs.DictionaryCode,parseFloat(answer.LastTaskState))}` : ''"
>
<!-- 首次分裂的病灶只能选择存在 -->
<template v-if="answer.IsFristAdd=== 'True' && answer.SplitOrMergeType === '0'">
<el-option
v-for="item of $d[qs.DictionaryCode]"
v-show="item.value === 0"
:key="item.id"
:value="item.value"
:label="item.label"
/>
</template>
<!-- 首次添加的新病灶不能为无法评估和消失 -->
<template v-else-if="isCurrentTaskAdd=== 'True' && lesionType === 2">
<el-option
v-for="item of $d[qs.DictionaryCode]"
v-show="item.value === 0 || item.value === 1"
:key="item.id"
:value="item.value"
:label="item.label"
/>
</template>
<template v-else>
<el-option
v-for="item of filterState($d[qs.DictionaryCode])"
:key="item.id"
:value="item.value"
:label="item.label"
/>
</template>
</el-option-group>
</template>
<template v-else-if="qs.DictionaryCode && qs.QuestionMark !== 7">
<el-option
v-for="item of $d[qs.DictionaryCode]"
:key="item.id"
:value="item.value"
:label="item.label"
/>
</template>
<template v-else>
<el-option
v-for="val in qs.TypeValue.split('|')"
:key="val"
:label="val"
:value="val"
/>
</template>
</el-select>
<!-- 单选 -->
<el-radio-group
v-if="qs.Type==='radio'"
v-model="qsForm[qs.id]"
:disabled="!isCurrentTask || readingTaskState>=2"
>
<el-radio
v-for="val in qs.options.split('|')"
:key="val"
:label="val"
>
{{ val }}
</el-radio>
</el-radio-group>
</el-form-item>
</div>
</template>
<script>
export default {
name: 'TableQuestionFormItem',
props: {
tableInfo: {
type: Object,
default() {
return {}
}
},
answer: {
type: Object,
default() {
return {}
}
},
questionForm: {
type: Object,
default() {
return {}
}
},
organs: {
type: Array,
default() {
return []
}
},
readingTaskState: {
type: Number,
required: true
},
isBaseLineTask: {
type: Boolean,
required: true
},
isCurrentTask: {
type: Boolean,
required: true
}
},
data() {
return {
isCurrentTaskAdd: null,
lesionType: null,
questions: [],
organList: [],
qsForm: {}
}
},
watch: {
questionForm: {
deep: true,
immediate: true,
handler(v) {
if (v.MeasureData) {
const { markTool } = v.MeasureData
if (markTool === 'Bidirectional') {
this.getOrganList(1)
} else if (markTool === 'Length') {
this.getOrganList(0)
} else {
this.getOrganList()
}
} else {
this.getOrganList()
}
for (const key in v) {
this.$set(this.qsForm, key, v[key])
}
this.isCurrentTaskAdd = v.IsCurrentTaskAdd
}
},
tableInfo: {
deep: true,
immediate: true,
handler(v) {
if (v) {
this.lesionType = v.LesionType
this.questions = v.TableQuestions.Questions
}
}
}
},
methods: {
lesionTypeChange(v) {
this.$emit('lesionTypeChange', { tableId: this.tableInfo.Id, rowIndex: this.answer.RowIndex, newLesionType: v })
},
formItemChange(v, qs) {
const updateArr = []
if (qs.QuestionMark === 8 && qs.RelationQuestions.length > 0) {
//
const index = this.organList.findIndex(item => item[qs.DataTableColumn] === v)
if (index > -1) {
const selected = this.organList[index]
qs.RelationQuestions.map(q => {
const val = selected[q.DataTableColumn]
updateArr.push({ questionId: q.Id, val: val, questionMark: q.QuestionMark })
})
updateArr.push({ questionId: 'OrganInfoId', val: selected.OrganInfoId })
updateArr.push({ questionId: 'IsCanEditPosition', val: selected.IsCanEditPosition })
} else {
qs.RelationQuestions.map(q => {
updateArr.push({ questionId: q.Id, val: '', questionMark: q.QuestionMark })
})
}
}
this.$emit('update', { tableId: this.tableInfo.Id, rowIndex: this.answer.RowIndex, questionMark: qs.QuestionMark, questionId: qs.Id, val: v, updateArr })
},
getOrganList(isLymphNodes = null) {
const idx = this.organs.findIndex(i => i.LesionType === this.questionForm.LesionType)
if (idx > -1) {
const arr = this.organs[idx].OrganList
if (!isNaN(parseInt(isLymphNodes))) {
this.organList = arr.filter((item) => item.IsLymphNodes === parseInt(isLymphNodes))
} else {
this.organList = arr
}
}
},
filterState(arr) {
if (!this.isBaseLineTask) {
const isLymphLesion = this.questionForm.IsLymphNodes
const lesionLength = this.questionForm.LesionLength
const lesionShort = this.questionForm.LesionShort
const bLesionL = !isNaN(parseFloat(this.questionForm.BaseLineMajorAxis)) ? parseFloat(this.questionForm.BaseLineMajorAxis) : 0
const bLesionS = !isNaN(parseFloat(this.questionForm.BaseLineShortAxis)) ? parseFloat(this.questionForm.BaseLineShortAxis) : 0
if (this.lesionType === 1 && isLymphLesion === 1 && this.questionForm.MeasureData && this.questionForm.MeasureData.markTool === 'Bidirectional' && lesionShort < bLesionS) {
arr = arr.filter(i => i.value !== 1)
} else if (this.lesionType === 1 && isLymphLesion === 1 && this.questionForm.MeasureData && this.questionForm.MeasureData.markTool === 'Bidirectional' && lesionShort >= 10 && lesionShort > bLesionS) {
arr = arr.filter(i => i.value === 0 || i.value === 1)
} else if (this.lesionType === 1 && isLymphLesion === 0 && this.questionForm.MeasureData && (this.questionForm.MeasureData.markTool === 'Length' || this.questionForm.MeasureData.markTool === 'Bidirectional') && lesionLength < bLesionL) {
arr = arr.filter(i => i.value !== 1)
}
return arr
} else {
return arr
}
},
close() {
this.$emit('close', { tableId: this.tableInfo.Id, rowIndex: this.answer.RowIndex })
}
}
}
</script>
<style lang="scss" scoped>
.table-question-form{
::v-deep .el-form-item__label{
color: #c3c3c3;
}
::v-deep .el-input .el-input__inner{
background-color: transparent;
color: #ddd;
border: 1px solid #5e5e5e;
}
::v-deep .el-form-item{
display: flex;
flex-direction: row;
justify-content: flex-start;
}
::v-deep .el-form-item__content{
flex: 1;
}
::v-deep .el-input.is-disabled .el-input__inner{
background-color: #646464a1;
}
::v-deep .el-select.is-disabled .el-input__inner{
background-color: #646464a1;
}
::v-deep .el-button--mini, .el-button--mini.is-round {
padding: 7px 10px;
}
.el-form-item__content
.el-select{
width: 100%;
}
.input-width1{
width: calc(100% - 60px)!important;
}
.input-width2{
width: 100% !important;
}
}
</style>

View File

@ -8,25 +8,33 @@ const config = {
"name": "直径测量工具",
"icon": "length",
"toolName": "Length",
"i18nKey": "trials:reading:button:length"
"i18nKey": "trials:reading:button:length",
"isDisabled": false,
"disabledReason": ''
},
{
"name": "长短径测量工具",
"icon": "bidirection",
"toolName": "Bidirectional",
"i18nKey": "trials:reading:button:bidirectional"
"i18nKey": "trials:reading:button:bidirectional",
"isDisabled": false,
"disabledReason": ''
},
{
"name": "矩形工具",
"icon": "rectangle",
"toolName": "RectangleRoi",
"i18nKey": "trials:reading:button:rectangle"
"i18nKey": "trials:reading:button:rectangle",
"isDisabled": false,
"disabledReason": ''
},
{
"name": "箭头工具",
"icon": "arrow",
"toolName": "ArrowAnnotate",
"i18nKey": "trials:reading:button:arrowAnnotate"
"i18nKey": "trials:reading:button:arrowAnnotate",
"isDisabled": false,
"disabledReason": ''
}
]
},
@ -38,25 +46,33 @@ const config = {
"name": "直径测量工具",
"icon": "length",
"toolName": "Length",
"i18nKey": "trials:reading:button:length"
"i18nKey": "trials:reading:button:length",
"isDisabled": false,
"disabledReason": ''
},
{
"name": "长短径测量工具",
"icon": "bidirection",
"toolName": "Bidirectional",
"i18nKey": "trials:reading:button:bidirectional"
"i18nKey": "trials:reading:button:bidirectional",
"isDisabled": false,
"disabledReason": ''
},
{
"name": "矩形工具",
"icon": "rectangle",
"toolName": "RectangleRoi",
"i18nKey": "trials:reading:button:rectangle"
"i18nKey": "trials:reading:button:rectangle",
"isDisabled": false,
"disabledReason": ''
},
{
"name": "箭头工具",
"icon": "arrow",
"toolName": "ArrowAnnotate",
"i18nKey": "trials:reading:button:arrowAnnotate"
"i18nKey": "trials:reading:button:arrowAnnotate",
"isDisabled": false,
"disabledReason": ''
}
]
},
@ -68,25 +84,33 @@ const config = {
"name": "直径测量工具",
"icon": "length",
"toolName": "Length",
"i18nKey": "trials:reading:button:length"
"i18nKey": "trials:reading:button:length",
"isDisabled": false,
"disabledReason": ''
},
{
"name": "长短径测量工具",
"icon": "bidirection",
"toolName": "Bidirectional",
"i18nKey": "trials:reading:button:bidirectional"
"i18nKey": "trials:reading:button:bidirectional",
"isDisabled": false,
"disabledReason": ''
},
{
"name": "矩形工具",
"icon": "rectangle",
"toolName": "RectangleRoi",
"i18nKey": "trials:reading:button:rectangle"
"i18nKey": "trials:reading:button:rectangle",
"isDisabled": false,
"disabledReason": ''
},
{
"name": "箭头工具",
"icon": "arrow",
"toolName": "ArrowAnnotate",
"i18nKey": "trials:reading:button:arrowAnnotate"
"i18nKey": "trials:reading:button:arrowAnnotate",
"isDisabled": false,
"disabledReason": ''
}
]
},
@ -98,25 +122,33 @@ const config = {
"name": "直径测量工具",
"icon": "length",
"toolName": "Length",
"i18nKey": "trials:reading:button:length"
"i18nKey": "trials:reading:button:length",
"isDisabled": false,
"disabledReason": ''
},
{
"name": "长短径测量工具",
"icon": "bidirection",
"toolName": "Bidirectional",
"i18nKey": "trials:reading:button:bidirectional"
"i18nKey": "trials:reading:button:bidirectional",
"isDisabled": false,
"disabledReason": ''
},
{
"name": "矩形工具",
"icon": "rectangle",
"toolName": "RectangleRoi",
"i18nKey": "trials:reading:button:rectangle"
"i18nKey": "trials:reading:button:rectangle",
"isDisabled": false,
"disabledReason": ''
},
{
"name": "箭头工具",
"icon": "arrow",
"toolName": "ArrowAnnotate",
"i18nKey": "trials:reading:button:arrowAnnotate"
"i18nKey": "trials:reading:button:arrowAnnotate",
"isDisabled": false,
"disabledReason": ''
}
]
},
@ -128,13 +160,17 @@ const config = {
"name": "矩形工具",
"icon": "rectangle",
"toolName": "RectangleRoi",
"i18nKey": "trials:reading:button:rectangle"
"i18nKey": "trials:reading:button:rectangle",
"isDisabled": false,
"disabledReason": ''
},
{
"name": "箭头工具",
"icon": "arrow",
"toolName": "ArrowAnnotate",
"i18nKey": "trials:reading:button:arrowAnnotate"
"i18nKey": "trials:reading:button:arrowAnnotate",
"isDisabled": false,
"disabledReason": ''
}
]
},
@ -146,19 +182,25 @@ const config = {
"name": "直径测量工具",
"icon": "length",
"toolName": "Length",
"i18nKey": "trials:reading:button:length"
"i18nKey": "trials:reading:button:length",
"isDisabled": false,
"disabledReason": ''
},
{
"name": "矩形工具",
"icon": "rectangle",
"toolName": "RectangleRoi",
"i18nKey": "trials:reading:button:rectangle"
"i18nKey": "trials:reading:button:rectangle",
"isDisabled": false,
"disabledReason": ''
},
{
"name": "箭头工具",
"icon": "arrow",
"toolName": "ArrowAnnotate",
"i18nKey": "trials:reading:button:arrowAnnotate"
"i18nKey": "trials:reading:button:arrowAnnotate",
"isDisabled": false,
"disabledReason": ''
}
]
},
@ -180,7 +222,9 @@ const config = {
"name": "圆形测量",
"icon": "oval",
"toolName": "Probe",
"i18nKey": "trials:reading:button:circle"
"i18nKey": "trials:reading:button:circle",
"isDisabled": false,
"disabledReason": ''
}
]
}