自动切换任务更改;PT序列支持修改检查的患者数据
continuous-integration/drone/push Build is passing Details

uat_us
caiyiling 2026-04-23 11:39:07 +08:00
parent 04aa61658c
commit 6c36c48b82
22 changed files with 609 additions and 40 deletions

View File

@ -4430,4 +4430,22 @@ export function updateImageResizePath(data) {
method: 'post',
data
})
}
// 获取PET图像上患者信息
export function getPatientInfo(data) {
return request({
url: `/Study/getPatientInfo`,
method: 'post',
data
})
}
//编辑患者基本信息
export function editPatientInfo(data) {
return request({
url: `/Study/editPatientInfo`,
method: 'post',
data
})
}

View File

@ -335,6 +335,100 @@
</div>
</div>
<!-- 患者信息 -->
<div class="measureTool-wrapper patient-form" v-if="isHaveStudyClinicalData && type === 'Study' && modality && ['PT、CT', 'CT、PT', 'PET-CT'].includes(modality)">
<div class="sideTool-title">{{ $t('trials:tab:patientData') }}</div>
<div class="sideTool-wrapper">
<el-form
ref="patientForm"
:model="formData"
:rules="rules"
label-width="150"
v-loading="formLoading"
>
<!-- 性别 -->
<el-form-item :label="$t('trials:ptData:label:patientSex')" prop="PatientSex">
<el-select
v-model="formData.PatientSex"
:placeholder="$t('common:ruleMessage:select')"
style="width: 100%"
size="small"
:disabled="!isEdit"
>
<el-option :label="$t('trials:patientSex:male')" value="M"></el-option>
<el-option :label="$t('trials:patientSex:female')" value="F"></el-option>
</el-select>
</el-form-item>
<!-- 体重kg 例如 70.5-->
<el-form-item :label="$t('trials:ptData:label:patientWeight')" prop="PatientWeight">
<el-input
v-model.number="formData.PatientWeight"
:placeholder="$t('trials:patientWeight:eg')"
style="width: 100%"
size="small"
:disabled="!isEdit"
></el-input>
</el-form-item>
<!-- 总剂量Bq 例如 740000000-->
<el-form-item :label="$t('trials:ptData:label:totalDose')" prop="RadionuclideTotalDose">
<el-input
v-model.number="formData.RadionuclideTotalDose"
:placeholder="$t('trials:totalDose:eg')"
style="width: 100%"
size="small"
:disabled="!isEdit"
></el-input>
</el-form-item>
<!-- 半衰期s 例如 21600-->
<el-form-item :label="$t('trials:ptData:label:halfLife')" prop="RadionuclideHalfLife">
<el-input
v-model.number="formData.RadionuclideHalfLife"
:placeholder="$t('trials:halfLife:eg')"
style="width: 100%"
size="small"
:disabled="!isEdit"
></el-input>
</el-form-item>
<!-- 注射时间s Unix 相对秒-->
<el-form-item :label="$t('trials:ptData:label:injectTime')" prop="RadiopharmaceuticalStartTime">
<el-input
v-model.number="formData.RadiopharmaceuticalStartTime"
:placeholder="$t('trials:injectTime:eg')"
style="width: 100%"
@input="computeTimeRelation"
size="small"
:disabled="!isEdit"
></el-input>
</el-form-item>
<!-- 成像时间s Unix 相对秒-->
<el-form-item :label="$t('trials:ptData:label:acquisitionTime')" prop="AcquisitionTime">
<el-input
v-model.number="formData.AcquisitionTime"
:placeholder="$t('trials:injectTime:eg')"
style="width: 100%"
@input="computeTimeRelation"
size="small"
:disabled="!isEdit"
></el-input>
</el-form-item>
<!-- 时间一致性检查 -->
<el-form-item :label="$t('trials:ptData:label:timeCheck')">
<el-input
v-model="formData.TimeCheck"
disabled
style="width: 100%"
size="small"
></el-input>
</el-form-item>
<!-- 提交 -->
<el-form-item style="margin-top: 20px;text-align: right;" v-if="isEdit">
<el-button type="primary" @click="submitForm" size="small">{{ $t('trials:ptData:button:submit') }}</el-button>
</el-form-item>
</el-form>
</div>
</div>
</div>
<el-dialog
v-if="customWwc.visible"
@ -363,13 +457,22 @@ cornerstoneTools.external.cornerstone = cornerstone
cornerstoneTools.external.Hammer = Hammer
cornerstoneTools.external.cornerstoneMath = cornerstoneMath
import '@/utils/dialog'
import {
getPatientInfo,
editPatientInfo
} from '@/api/trials'
export default {
name: 'DicomsViewer',
components: {
DicomCanvas,
CustomWwwcForm
},
props: {
modality: {
type: String,
default: ''
}
},
data() {
return {
activeTool: '',
@ -393,7 +496,48 @@ export default {
layout: null,
seriesList: [],
customWwc: { visible: false, title: null },
fps: 15
fps: 15,
formData: {
Id: '',
PatientSex: '',
PatientWeight: null,
RadionuclideTotalDose: null,
RadionuclideHalfLife: null,
RadiopharmaceuticalStartTime: null,
AcquisitionTime: null,
TimeCheck: ''
},
rules: {
PatientSex: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: 'change' }
],
PatientWeight: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
{ type: 'number', min: 0, message: this.$t('trials:ptData:ruleMessage:number1'), trigger: 'blur' }//0
],
RadionuclideTotalDose: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
{ type: 'number', min: 0, message: this.$t('trials:ptData:ruleMessage:number1'), trigger: 'blur' }//0
],
RadionuclideHalfLife: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
{ type: 'number', min: 0, message: this.$t('trials:ptData:ruleMessage:number1'), trigger: 'blur' }
],
RadiopharmaceuticalStartTime: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
{ type: 'number', message: this.$t('trials:ptData:ruleMessage:number2'), trigger: 'blur' }//
],
AcquisitionTime: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
{ type: 'number', message: this.$t('trials:ptData:ruleMessage:number2'), trigger: 'blur' },//
//
{ validator: this.validateTime, trigger: 'blur' }
]
},
formLoading: false,
isHaveStudyClinicalData: false,
type: '',
isEdit: 0
}
},
mounted() {
@ -403,6 +547,12 @@ export default {
this.wwwcList[0] = '-1'
this.colormapsList = cornerstone.colors.getColormapsList()
this.currentDicomCanvas = this.$refs['dicomCanvas0']
this.isHaveStudyClinicalData = this.$route.query.isHaveStudyClinicalData === 'true'
this.type = this.$route.query.type
this.isEdit = parseInt(this.$route.query.showDelete)
if (this.isHaveStudyClinicalData && this.type === 'Study' && ['PT、CT', 'CT、PT', 'PET-CT'].includes(this.modality)) {
this.getPatientInfo()
}
},
methods: {
@ -646,6 +796,68 @@ export default {
}
}
}
},
//
validateTime(rule, value, callback) {
const { RadiopharmaceuticalStartTime } = this.formData
if (value && RadiopharmaceuticalStartTime !== null && value < RadiopharmaceuticalStartTime) {
callback(new Error(this.$t('trials:ptData:ruleMessage:number3')))//
} else {
callback()
}
},
computeTimeRelation() {
const startTime = this.formData.RadiopharmaceuticalStartTime
const acquireTime = this.formData.AcquisitionTime
if (!startTime || !acquireTime) {
this.formData.TimeCheck = ''
return
}
if (startTime <= acquireTime) {
this.formData.TimeCheck = this.$t('trials:ptData:timeCheck:val1') //
} else {
this.formData.TimeCheck = this.$t('trials:ptData:timeCheck:val2') // >
}
},
async getPatientInfo() {
try {
this.formLoading = true
let studyId = this.$route.query.studyId
let res = await getPatientInfo({studyId: studyId})
this.formData = {
Id: res.Result.Id || '',
PatientSex: res.Result.PatientSex || '',
PatientWeight: parseFloat(res.Result.PatientWeight) || null,
RadionuclideTotalDose: parseFloat(res.Result.RadionuclideTotalDose) || null,
RadionuclideHalfLife: parseFloat(res.Result.RadionuclideHalfLife) || null,
RadiopharmaceuticalStartTime: parseFloat(res.Result.RadiopharmaceuticalStartTime) || '',
AcquisitionTime: parseFloat(res.Result.AcquisitionTime) || '',
TimeCheck: ''
}
this.computeTimeRelation()
this.formLoading = false
} catch(e) {
this.formLoading = false
console.log(e)
}
},
async submitForm() {
try {
let valid = await this.$refs.patientForm.validate()
if (!valid) return
this.formLoading = true
let res = await editPatientInfo(this.formData)
this.formLoading = false
if (res.IsSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
} catch(e) {
this.formLoading = false
console.log(e)
}
}
}
}
@ -655,6 +867,7 @@ export default {
.dicom-wrapper {
display: flex;
height: 100%;
}
.dicom-wrapper .case-dialog-class {
position: fixed;
@ -730,7 +943,7 @@ export default {
/* border: 1px solid blueviolet; */
color: #d0d0d0;
font-size: 13px;
overflow: hidden;
overflow-y: auto;
}
.dicom-wrapper .measure-wrapper{
display: flex;
@ -908,4 +1121,18 @@ export default {
background-color: #16477b90;
}
.patient-form .el-form-item {
margin-bottom: 15px;
}
.patient-form .el-form-item__label {
color: #d0d0d0;
}
.patient-form .el-input.is-disabled .el-input__inner {
background-color: #424244;
}
.patient-form .el-input .el-input__inner {
background-color: #323232;
color: #d0d0d0;
}
</style>

View File

@ -123,12 +123,11 @@
</div>
</div>
<div class="viewerContent">
<dicom-viewer id="dicomViewer" ref="dicomViewer" style="height:100%" />
<dicom-viewer id="dicomViewer" ref="dicomViewer" style="height:100%" :modality="modality"/>
</div>
<!-- <div class="viewerRightSidePanel">
<dicom-tools />
</div> -->
</div>
</div>

View File

@ -2341,7 +2341,7 @@ export default {
this.readingTaskState = 2
await store.dispatch('reading/setVisitTaskReadingTaskState', { visitTaskId: this.visitTaskId, readingTaskState: 2 })
await store.dispatch('reading/setCurrentReadingTaskState', 2)
const res = await getAutoCutNextTask()
const res = await getAutoCutNextTask({imageToolType: 1})
var isAutoTask = res.Result.AutoCutNextTask
if (isAutoTask) {
window.location.reload()

View File

@ -31,6 +31,12 @@
import { setAutoCutNextTask, getAutoCutNextTask } from '@/api/user'
export default {
name: 'Others',
props: {
imageToolType: {
type: Number,
default: 1
}
},
data() {
return {
form: {
@ -47,7 +53,7 @@ export default {
async initForm() {
this.loading = true
try {
const res = await getAutoCutNextTask()
const res = await getAutoCutNextTask({imageToolType: this.imageToolType})
if (res.IsSuccess) {
this.form.AutoCutNextTask = res.Result.AutoCutNextTask
this.form.IsDoubleScreen = res.Result.IsDoubleScreen
@ -62,7 +68,8 @@ export default {
if (!valid) return
this.loading = true
try {
const res = await setAutoCutNextTask(this.form)
let params = Object.assign(this.form, {imageToolType: this.imageToolType})
const res = await setAutoCutNextTask(params)
if (res.IsSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
}

View File

@ -733,7 +733,7 @@ export default {
await store.dispatch('reading/setVisitTaskReadingTaskState', { visitTaskId: this.visitTaskId, readingTaskState: 2 })
// DicomEvent.$emit('setReadingState', 2)
await store.dispatch('reading/setCurrentReadingTaskState', 2)
const res = await getAutoCutNextTask()
const res = await getAutoCutNextTask({imageToolType: 1})
var isAutoTask = res.Result.AutoCutNextTask
if (isAutoTask) {
// DicomEvent.$emit('reload')
@ -780,7 +780,7 @@ export default {
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 })
const res = await getAutoCutNextTask()
const res = await getAutoCutNextTask({imageToolType: 1})
let IsDoubleScreen = false
if (res.IsSuccess) {
IsDoubleScreen = res.Result.IsDoubleScreen

View File

@ -411,7 +411,7 @@
<WL v-if="activeName === '2'" @getWwcTpl="getWwcTpl" />
</el-tab-pane>
<el-tab-pane :label="$t('trials:reading:tab:others')" name="3">
<Others v-if="activeName === '3'" />
<Others v-if="activeName === '3'" :imageToolType="1"/>
</el-tab-pane>
</el-tabs>
</el-dialog>

View File

@ -835,7 +835,7 @@ export default {
store.dispatch('reading/setVisitTaskReadingTaskState', { visitTaskId: this.visitTaskId, readingTaskState: 2 })
DicomEvent.$emit('setReadingState', 2)
window.opener.postMessage('refreshTaskList', window.location)
const res = await getAutoCutNextTask()
const res = await getAutoCutNextTask({imageToolType: 1})
let isAutoTask = res.Result.AutoCutNextTask
if (isAutoTask) {
window.location.reload()
@ -875,7 +875,7 @@ export default {
var trialReadingCriterionId = this.$router.currentRoute.query.TrialReadingCriterionId
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 })
const res = await getAutoCutNextTask()
const res = await getAutoCutNextTask({imageToolType: 1})
let IsDoubleScreen = false
if (res.IsSuccess) {
IsDoubleScreen = res.Result.IsDoubleScreen

View File

@ -23,6 +23,12 @@
import { setAutoCutNextTask, getAutoCutNextTask } from '@/api/user'
export default {
name: 'Others',
props: {
imageToolType: {
type: Number,
default: 1
}
},
data() {
return {
form: {
@ -38,7 +44,7 @@ export default {
async initForm() {
this.loading = true
try{
await getAutoCutNextTask()
await getAutoCutNextTask({imageToolType: this.imageToolType})
this.form.AutoCutNextTask = res.Result.AutoCutNextTask
this.loading = false
}catch(e){
@ -50,7 +56,8 @@ export default {
if (!valid) return
this.loading = true
try{
await setAutoCutNextTask(this.form)
let params = Object.assign(this.form, {imageToolType: this.imageToolType})
await setAutoCutNextTask(params)
this.loading = false
this.$message.success(this.$t('common:message:savedSuccessfully'))
}catch(e){

View File

@ -53,20 +53,26 @@
:visit-task-id="visitTaskId" :reading-category="readingCategory" :subject-code="subjectCode"
:task-blind-name="taskBlindName" :is-reading-show-subject-info="isReadingShowSubjectInfo"
:is-reading-show-previous-results="isReadingShowPreviousResults"
:is-exists-clinical-data="isExistsClinicalData" />
:is-exists-clinical-data="isExistsClinicalData"
:imageToolType="1"
/>
<!-- 裁判阅片 -->
<AdReview v-else-if="isShow && readingCategory === 4" :trial-id="trialId" :subject-id="subjectId"
:visit-task-id="visitTaskId" :reading-category="readingCategory" :subject-code="subjectCode"
:task-blind-name="taskBlindName" :is-reading-show-subject-info="isReadingShowSubjectInfo"
:is-reading-show-previous-results="isReadingShowPreviousResults"
:is-exists-clinical-data="isExistsClinicalData" />
:is-exists-clinical-data="isExistsClinicalData"
:imageToolType="1"
/>
<!-- 肿瘤学阅片 -->
<OncologyReview v-else-if="isShow && readingCategory === 5" :trial-id="trialId" :subject-id="subjectId"
:visit-task-id="visitTaskId" :reading-category="readingCategory" :subject-code="subjectCode"
:task-blind-name="taskBlindName" :is-reading-show-subject-info="isReadingShowSubjectInfo"
:is-reading-show-previous-results="isReadingShowPreviousResults"
:is-exists-clinical-data="isExistsClinicalData" />
:is-exists-clinical-data="isExistsClinicalData"
:imageToolType="1"
/>
<el-dialog :visible.sync="dialogVisible" :custom-class="isFullscreen ? 'full-dialog-container' : 'dialog-container'"
:show-close="false" :close-on-click-modal="false" :fullscreen="isFullscreen">

View File

@ -477,7 +477,7 @@
</el-tab-pane>
<!-- 其他 -->
<el-tab-pane :label="$t('trials:reading:tab:others')" name="3">
<Others v-if="personalConfigDialog.activeName === '3'" />
<Others v-if="personalConfigDialog.activeName === '3'" :imageToolType="1"/>
</el-tab-pane>
</el-tabs>

View File

@ -918,7 +918,7 @@ export default {
//
this.readingTaskState = 2
this.$emit('setReadingTaskState', 2)
const res = await getAutoCutNextTask()
const res = await getAutoCutNextTask({imageToolType: 1})
var isAutoTask = res.Result.AutoCutNextTask
if (isAutoTask) {
window.location.reload()
@ -963,7 +963,7 @@ export default {
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 })
const res = await getAutoCutNextTask()
const res = await getAutoCutNextTask({imageToolType: 1})
let IsDoubleScreen = false
if (res.IsSuccess) {
IsDoubleScreen = res.Result.IsDoubleScreen

View File

@ -74,8 +74,39 @@
#{{ series.SeriesNumber }}
</div>
<div v-if="series.Description" class="text-desc" :title="series.Description">
<div v-if="series.Description" class="text-desc" :title="series.Description" style="position: relative;">
{{ series.Description }}
<div class="patient-info" style="position: absolute;right: 0;top: 0;" v-if="taskInfo && taskInfo.IsExistsClinicalData && ['PT','PET'].includes(series.Modality)">
<el-popover placement="right" trigger="hover" popper-class="patient-info-popper">
<h4>{{ $t('trials:ptData:title') }}</h4>
<div class="patient-info-row">
<label>{{ $t('trials:ptData:label:patientSex') }}</label>
<span>{{ study.PatientSex }}</span>
</div>
<div class="patient-info-row">
<label>{{ $t('trials:ptData:label:patientWeight') }}</label>
<span>{{ study.PatientWeight }}</span>
</div>
<div class="patient-info-row">
<label>{{ $t('trials:ptData:label:totalDose') }}</label>
<span>{{ study.RadionuclideTotalDose }}</span>
</div>
<div class="patient-info-row">
<label>{{ $t('trials:ptData:label:halfLife') }}</label>
<span>{{ study.RadionuclideHalfLife }}</span>
</div>
<div class="patient-info-row">
<label>{{ $t('trials:ptData:label:injectTime') }}</label>
<span>{{ study.RadiopharmaceuticalStartTime }}</span>
</div>
<div class="patient-info-row">
<label>{{ $t('trials:ptData:label:acquisitionTime') }}</label>
<span>{{ study.AcquisitionTime }}</span>
</div>
<i slot="reference" class="el-icon-document"
style="font-size: 15px;cursor: pointer;color: #428bca;" />
</el-popover>
</div>
</div>
<div v-if="series.SliceThickness && !study.IsCriticalSequence" class="text-desc">
T: {{ parseFloat(series.SliceThickness).toFixed(digitPlaces) }}
@ -223,10 +254,56 @@ export default {
const seriesIndex = seriseList[newIndex].SeriesIndex
this.setSeriesActive(studyIndex, seriesIndex)
this.activeSeries(seriseList[newIndex], seriesIndex, studyIndex)
},
showPatientInfo(study) {
console.log(study)
}
}
}
</script>
<style lang="scss">
.patient-info-popper {
font-size: 12px;
color: #ddd;
background-color: #2f2f2f;
border-color: #5a5a5a;
padding: 8px 10px;
}
.patient-info-popper[x-placement^='right'] .popper__arrow {
border-right-color: #5a5a5a;
}
.patient-info-popper[x-placement^='right'] .popper__arrow::after {
border-right-color: #2f2f2f;
}
.patient-info-popper .patient-info-row {
display: grid;
grid-template-columns: 100px minmax(0, 1fr);
column-gap: 12px;
align-items: center;
line-height: 18px;
}
.patient-info-popper .patient-info-row + .patient-info-row {
margin-top: 4px;
}
.patient-info-popper label {
color: #bbb;
// font-weight: 600;
white-space: nowrap;
}
.patient-info-popper span {
text-align: left;
min-width: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
</style>
<style lang="scss" scoped>
.study-wrapper {
width: 100%;

View File

@ -987,7 +987,7 @@ export default {
this.readingTaskState = 2
this.$emit('setReadingTaskState', 2)
window.opener.postMessage('refreshTaskList', window.location)
const res = await getAutoCutNextTask()
const res = await getAutoCutNextTask({imageToolType: 1})
const isAutoTask = res.Result.AutoCutNextTask
if (isAutoTask) {
window.location.reload()
@ -1031,7 +1031,7 @@ export default {
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 })
const res = await getAutoCutNextTask()
const res = await getAutoCutNextTask({imageToolType: 1})
let IsDoubleScreen = false
if (res.IsSuccess) {
IsDoubleScreen = res.Result.IsDoubleScreen

View File

@ -153,6 +153,10 @@ export default {
isExistsClinicalData: {
type: Boolean,
required: true
},
imageToolType: {
type: Number,
required: true
}
},
data() {
@ -361,7 +365,7 @@ export default {
this.signVisible = false
//
this.readingTaskState = 2
const res = await getAutoCutNextTask()
const res = await getAutoCutNextTask({imageToolType: this.imageToolType})
var isAutoTask = res.Result.AutoCutNextTask
if (isAutoTask) {
// DicomEvent.$emit('getNextTask')

View File

@ -8,14 +8,18 @@
:reading-category="taskInfo.ReadingCategory" :subject-code="taskInfo.SubjectCode"
:task-blind-name="taskInfo.TaskBlindName" :is-reading-show-subject-info="taskInfo.IsReadingShowSubjectInfo"
:is-reading-show-previous-results="taskInfo.IsReadingShowPreviousResults"
:is-exists-clinical-data="taskInfo.IsExistsClinicalData" />
:is-exists-clinical-data="taskInfo.IsExistsClinicalData"
:imageToolType="2"
/>
<!-- 裁判阅片 -->
<ad-review v-else-if="taskInfo && taskInfo.ReadingCategory === 4" :trial-id="trialId"
:subject-id="taskInfo.SubjectId" :visit-task-id="taskInfo.VisitTaskId"
:reading-category="taskInfo.ReadingCategory" :subject-code="taskInfo.SubjectCode"
:task-blind-name="taskInfo.TaskBlindName" :is-reading-show-subject-info="taskInfo.IsReadingShowSubjectInfo"
:is-reading-show-previous-results="taskInfo.IsReadingShowPreviousResults"
:is-exists-clinical-data="taskInfo.IsExistsClinicalData" />
:is-exists-clinical-data="taskInfo.IsExistsClinicalData"
:imageToolType="2"
/>
<!-- 肿瘤学阅片 -->
<!-- <oncology-review v-else-if="taskInfo && taskInfo.ReadingCategory=== 5" /> -->
<el-dialog :visible.sync="clinicalDataVisible"

View File

@ -205,6 +205,10 @@ export default {
isExistsClinicalData: {
type: Boolean,
required: true
},
imageToolType: {
type: Number,
required: true
}
},
data() {
@ -397,7 +401,7 @@ export default {
//
this.oncologyInfo.ReadingTaskState = 2
const res = await getAutoCutNextTask()
const res = await getAutoCutNextTask({imageToolType: this.imageToolType})
var isAutoTask = res.Result.AutoCutNextTask
if (isAutoTask) {
// store.dispatch('reading/resetVisitTasks')

View File

@ -442,7 +442,8 @@ export default {
path = `/noneDicomReading?TrialReadingCriterionId=${row.TrialReadingCriterionId}&trialId=${this.trialId}&subjectCode=${row.SubjectCode}&subjectId=${row.SubjectId}&visitTaskId=${row.Id}&isReadingTaskViewInOrder=${row.IsReadingTaskViewInOrder}&criterionType=${row.CriterionType}&readingTool=${row.ReadingTool}&TokenKey=${token}`
}
var routeData = this.$router.resolve({ path })
const res = await getAutoCutNextTask()
let imageToolType = row.ReadingTool === 1 ? 2 : 1
const res = await getAutoCutNextTask({imageToolType})
let IsDoubleScreen = false
if (res.IsSuccess) {
IsDoubleScreen = res.Result.IsDoubleScreen

View File

@ -329,7 +329,8 @@ export default {
path = `/noneDicomReading?TrialReadingCriterionId=${this.TrialReadingCriterionId}&trialId=${this.trialId}&subjectCode=${row.SubjectCode}&subjectId=${row.SubjectId}&isReadingTaskViewInOrder=${this.isReadingTaskViewInOrder}&criterionType=${this.criterionType}&readingTool=${this.readingTool}&TokenKey=${token}`
}
var routeData = this.$router.resolve({ path })
const res = await getAutoCutNextTask()
let imageToolType = this.readingTool === 1 ? 2 : 1
const res = await getAutoCutNextTask({imageToolType})
let IsDoubleScreen = false
if (res.IsSuccess) {
IsDoubleScreen = res.Result.IsDoubleScreen

View File

@ -775,7 +775,7 @@ export default {
localStorage.setItem('taskInfo', JSON.stringify(this.taskInfo))
store.dispatch('noneDicomReview/setCurrentTaskState', 2)
window.opener.postMessage('refreshTaskList', window.location)
const res = await getAutoCutNextTask()
const res = await getAutoCutNextTask({ imageToolType: 2 })
const isAutoTask = res.Result.AutoCutNextTask
if (isAutoTask) {
window.location.reload()
@ -814,7 +814,7 @@ export default {
var trialReadingCriterionId = this.$router.currentRoute.query.TrialReadingCriterionId
var path = `/noneDicomReading?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 })
const res = await getAutoCutNextTask()
const res = await getAutoCutNextTask({ imageToolType: 2 })
let IsDoubleScreen = false
if (res.IsSuccess) {
IsDoubleScreen = res.Result.IsDoubleScreen

View File

@ -1,10 +1,12 @@
<template>
<div v-loading="loading" class="clinical-data-wrapper">
<el-tabs type="border-card">
<el-tab-pane v-for="cd in clinicalDatas" :key="cd.ClinicalDataTrialSetId" :label="$i18n.locale === 'zh'
? cd.ClinicalDataSetName
: cd.ClinicalDataSetEnName
">
<el-tabs type="border-card" v-model="activeName" @tab-click="tabClick">
<el-tab-pane
v-for="cd in clinicalDatas"
:key="cd.ClinicalDataTrialSetId"
:label="$i18n.locale === 'zh' ? cd.ClinicalDataSetName: cd.ClinicalDataSetEnName"
:name="cd.ClinicalDataTrialSetId"
>
<!-- 格式化录入 -->
<div v-if="cd.ClinicalUploadType === 0">
<!-- 既往放疗史 -->
@ -222,6 +224,90 @@
:open-type="'add'" @close="getClinicalData" />
</div>
</el-tab-pane>
<!-- 患者数据 -->
<el-tab-pane :label="$t('trials:tab:patientData')" name="patientForm">
<el-form
ref="patientForm"
:model="formData"
:rules="rules"
label-width="100"
v-loading="formLoading"
>
<div class="form-row">
<!-- 性别 -->
<el-form-item class="form-item-half" :label="$t('trials:ptData:label:patientSex')" prop="PatientSex">
<el-select
v-model="formData.PatientSex"
:placeholder="$t('common:ruleMessage:select')"
style="width: 100%"
>
<el-option :label="$t('trials:patientSex:male')" value="M"></el-option>
<el-option :label="$t('trials:patientSex:female')" value="F"></el-option>
</el-select>
</el-form-item>
<!-- 体重kg 例如 70.5-->
<el-form-item class="form-item-half" :label="$t('trials:ptData:label:patientWeight')" prop="PatientWeight">
<el-input
v-model.number="formData.PatientWeight"
:placeholder="$t('trials:patientWeight:eg')"
style="width: 100%"
></el-input>
</el-form-item>
</div>
<div class="form-row">
<!-- 总剂量Bq 例如 740000000-->
<el-form-item class="form-item-half" :label="$t('trials:ptData:label:totalDose')" prop="RadionuclideTotalDose">
<el-input
v-model.number="formData.RadionuclideTotalDose"
:placeholder="$t('trials:totalDose:eg')"
style="width: 100%"
></el-input>
</el-form-item>
<!-- 半衰期s 例如 21600-->
<el-form-item class="form-item-half" :label="$t('trials:ptData:label:halfLife')" prop="RadionuclideHalfLife">
<el-input
v-model.number="formData.RadionuclideHalfLife"
:placeholder="$t('trials:halfLife:eg')"
style="width: 100%"
></el-input>
</el-form-item>
</div>
<div class="form-row">
<!-- 注射时间s Unix 相对秒-->
<el-form-item class="form-item-half" :label="$t('trials:ptData:label:injectTime')" prop="RadiopharmaceuticalStartTime">
<el-input
v-model.number="formData.RadiopharmaceuticalStartTime"
:placeholder="$t('trials:injectTime:eg')"
style="width: 100%"
@input="computeTimeRelation"
></el-input>
</el-form-item>
<!-- 成像时间s Unix 相对秒-->
<el-form-item class="form-item-half" :label="$t('trials:ptData:label:acquisitionTime')" prop="AcquisitionTime">
<el-input
v-model.number="formData.AcquisitionTime"
:placeholder="$t('trials:injectTime:eg')"
style="width: 100%"
@input="computeTimeRelation"
></el-input>
</el-form-item>
</div>
<!-- 时间一致性检查 -->
<el-form-item :label="$t('trials:ptData:label:timeCheck')">
<el-input
v-model="formData.TimeCheck"
disabled
style="width: 100%"
></el-input>
</el-form-item>
<!-- 提交 -->
<el-form-item style="margin-top: 20px;text-align: right;">
<el-button type="primary" @click="submitForm">{{ $t('trials:ptData:button:submit') }}</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
</el-tabs>
<!-- 既往放疗史 -->
@ -269,6 +355,8 @@ import {
deletePreviousSurgery,
getCRCClinicalData,
addOrUpdateReadingClinicalData,
getPatientInfo,
editPatientInfo
} from '@/api/trials'
import PreviousRadiotherapy from './previousRadiotherapy'
import PreviousSurgery from './previousSurgery'
@ -336,6 +424,45 @@ export default {
downloadLoading: false,
clinicalDatas: [],
previewObj: { visible: false, filePath: '', fileType: '' },
activeName: '',
formData: {
Id: '',
PatientSex: '',
PatientWeight: null,
RadionuclideTotalDose: null,
RadionuclideHalfLife: null,
RadiopharmaceuticalStartTime: null,
AcquisitionTime: null,
TimeCheck: ''
},
rules: {
PatientSex: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: 'change' }
],
PatientWeight: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
{ type: 'number', min: 0, message: this.$t('trials:ptData:ruleMessage:number1'), trigger: 'blur' }//0
],
RadionuclideTotalDose: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
{ type: 'number', min: 0, message: this.$t('trials:ptData:ruleMessage:number1'), trigger: 'blur' }//0
],
RadionuclideHalfLife: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
{ type: 'number', min: 0, message: this.$t('trials:ptData:ruleMessage:number1'), trigger: 'blur' }
],
RadiopharmaceuticalStartTime: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
{ type: 'number', message: this.$t('trials:ptData:ruleMessage:number2'), trigger: 'blur' }//
],
AcquisitionTime: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
{ type: 'number', message: this.$t('trials:ptData:ruleMessage:number2'), trigger: 'blur' },//
//
{ validator: this.validateTime, trigger: 'blur' }
]
},
formLoading: false
}
},
mounted() {
@ -452,6 +579,7 @@ export default {
getCRCClinicalData(param)
.then((res) => {
this.clinicalDatas = res.Result
this.activeName = res.Result.length > 0 ? res.Result[0].ClinicalDataTrialSetId : ''
this.loading = false
})
.catch(() => {
@ -636,6 +764,70 @@ export default {
handleDownloadTpl(cd) {
window.open(this.OSSclientConfig.basePath + cd.Path)
},
//
validateTime(rule, value, callback) {
const { RadiopharmaceuticalStartTime } = this.formData
if (value && RadiopharmaceuticalStartTime !== null && value < RadiopharmaceuticalStartTime) {
callback(new Error(this.$t('trials:ptData:ruleMessage:number3')))//
} else {
callback()
}
},
computeTimeRelation() {
const startTime = this.formData.RadiopharmaceuticalStartTime
const acquireTime = this.formData.AcquisitionTime
if (!startTime || !acquireTime) {
this.formData.TimeCheck = ''
return
}
if (startTime <= acquireTime) {
this.formData.TimeCheck = this.$t('trials:ptData:timeCheck:val1') //
} else {
this.formData.TimeCheck = this.$t('trials:ptData:timeCheck:val2') // >
}
},
async tabClick(tab) {
try {
const name = tab.name
if (name === 'patientForm' && !this.formData.Id) {
this.formLoading = true
let res = await getPatientInfo({studyId: this.studyData.StudyId})
this.formData = {
Id: res.Result.Id || '',
PatientSex: res.Result.PatientSex || '',
PatientWeight: parseFloat(res.Result.PatientWeight) || null,
RadionuclideTotalDose: parseFloat(res.Result.RadionuclideTotalDose) || null,
RadionuclideHalfLife: parseFloat(res.Result.RadionuclideHalfLife) || null,
RadiopharmaceuticalStartTime: parseFloat(res.Result.RadiopharmaceuticalStartTime) || '',
AcquisitionTime: parseFloat(res.Result.AcquisitionTime) || '',
TimeCheck: ''
}
this.computeTimeRelation()
this.formLoading = false
}
} catch(e) {
this.formLoading = false
console.log(e)
}
},
async submitForm() {
try {
let valid = await this.$refs.patientForm.validate()
if (!valid) return
this.formLoading = true
let res = await editPatientInfo(this.formData)
this.formLoading = false
if (res.IsSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
} catch(e) {
this.formLoading = false
console.log(e)
}
}
},
}
</script>
@ -646,5 +838,16 @@ export default {
margin-bottom: 10px;
background-color: #dcdfe6;
}
.form-row {
display: flex;
gap: 20px;
margin-bottom: 0px;
.el-form-item {
margin-bottom: 15px;
}
}
.form-item-half {
flex: 1;
}
}
</style>

View File

@ -76,7 +76,18 @@
<el-table-column type="selection" width="55" v-if='$store.state.trials.config.IsSupportQCDownloadImage'>
</el-table-column>
<!-- 检查编号 -->
<el-table-column prop="StudyCode" :label="$t('trials:audit:table:studyId')" sortable />
<el-table-column prop="StudyCode" :label="$t('trials:audit:table:studyId')" sortable>
<template slot-scope="scope">
<el-tooltip
placement="top"
v-if="['PT、CT', 'CT、PT', 'PET-CT'].includes(scope.row.Modalities) && IsHaveStudyClinicalData && scope.row.IsHasEmptyPatientInfo"
>
<div slot="content">{{ $t('trials:audit:message:ptDataValid') }}</div>
<span class="el-icon-warning" style="color: red; cursor: pointer"></span>
</el-tooltip>
{{ scope.row.StudyCode }}
</template>
</el-table-column>
<!-- 检查名称 -->
<el-table-column prop="StudyName" v-if="relationInfo.IsShowStudyName"
:label="$t('trials:audit:table:StudyName')" sortable>
@ -149,7 +160,7 @@
<!-- 预览PET-CT数据 -->
<el-button type="primary" icon="el-icon-document tip-i" :title="$t('trials:audit:tab:clinicalData')"
v-if="
['PT、CT', 'CT、PT', 'PET-CT'].includes(
['PT、CT', 'CT、PT', 'PET-CT'].includes(
scope.row.Modalities
) && IsHaveStudyClinicalData
" circle :disabled="scope.row.IsDeleted" @click="handlePreviewClinicalData(scope.row)" />
@ -2441,7 +2452,7 @@ export default {
const routeData = this.$router.resolve({
path: `/showdicom?trialId=${this.trialId}&subjectVisitId=${this.data.Id
}&studyId=${row.StudyId}&showDelete=${this.isAudit ? 0 : 1
}&TokenKey=${token}&type=Study`,
}&TokenKey=${token}&type=Study&isHaveStudyClinicalData=${this.IsHaveStudyClinicalData}`,
})
this.open = window.open(routeData.href, '_blank')
},