Compare commits

...

2 Commits

Author SHA1 Message Date
caiyiling dce3719c58 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is running Details
2025-06-05 16:00:49 +08:00
caiyiling d05a4437d4 自定义标准配置更改及自定义阅片更改 2025-06-05 16:00:12 +08:00
11 changed files with 257 additions and 46 deletions

View File

@ -438,6 +438,8 @@
@resetAnnotations="resetAnnotations"
@setReadingTaskState="setReadingTaskState"
@viewCustomAnnotationSeries="viewCustomAnnotationSeries"
@setReadingToolActive="setReadingToolActive"
@setReadingToolPassive="setReadingToolPassive"
/>
</div>
</div>
@ -719,7 +721,8 @@ export default {
studyList: [],
volumeData: {},
fusionSerieId: {},
loadingText: null
loadingText: null,
toolNames: ['Length', 'Bidirectional', 'RectangleROI', 'ArrowAnnotate', 'CircleROI', 'Eraser']
// resetAnnotation: false , // 使
}
},
@ -1160,7 +1163,7 @@ export default {
})
toolGroup.addTool(RectangleROITool.toolName, {
cachedStats: false,
getTextLines: this.getRectangleROIToolTextLines
getTextLines: this.criterionType === 0 ? this.getCustomRectangleROIToolTextLines : this.getRectangleROIToolTextLines
})
toolGroup.addTool(PlanarFreehandROITool.toolName, {
allowOpenContours: false,
@ -1685,7 +1688,7 @@ export default {
} = cachedVolumeStats || {}
const textLines = []
if (area) {
const areaLine = isEmptyArea
? `Area: Oblique not supported`
@ -1728,11 +1731,23 @@ export default {
if (data.label) {
textLines.push(data.status ? `${data.label}(${data.status})` : data.label)
}
// textLines.push(`Area: ${parseFloat(area).toFixed(this.digitPlaces)} ${areaUnit}`)
// textLines.push(`Mean: ${csUtils.roundNumber(mean)} ${modalityUnit}`)
// textLines.push(`Max: ${csUtils.roundNumber(max)} ${modalityUnit}`)
// textLines.push(`Std Dev: ${csUtils.roundNumber(stdDev)} ${modalityUnit}`)
return textLines
},
getCustomRectangleROIToolTextLines(data, targetId) {
const cachedVolumeStats = data.cachedStats[targetId]
const { area, mean, max, stdDev, areaUnit, modalityUnit } = cachedVolumeStats
if (mean === undefined) {
return
}
const textLines = []
textLines.push(`Area: ${this.reRound(area, this.digitPlaces)} ${areaUnit}`)
textLines.push(`Mean: ${this.reRound(mean, this.digitPlaces)} ${modalityUnit}`)
textLines.push(`Max: ${this.reRound(max, this.digitPlaces)} ${modalityUnit}`)
textLines.push(`Std Dev: ${this.reRound(stdDev, this.digitPlaces)} ${modalityUnit}`)
return textLines
},
@ -1751,12 +1766,10 @@ export default {
if (length === undefined) {
return textLines
}
// spaceBetweenSlices & pixelSpacing &
// magnitude in each direction? Otherwise, this is "px"?
textLines.push(
`L: ${csUtils.roundNumber(length)} ${unit || unit}`,
`S: ${csUtils.roundNumber(width)} ${unit}`
`L: ${parseFloat(length).toFixed(this.digitPlaces)} ${unit || unit}`,
`S: ${parseFloat(width).toFixed(this.digitPlaces)} ${unit}`
)
return textLines
@ -1783,31 +1796,48 @@ export default {
if (radius) {
const radiusLine = isEmptyArea
? `Radius: Oblique not supported`
: `Radius: ${csUtils.roundNumber(radius)} ${radiusUnit}`
: `Radius: ${this.reRound(radius, this.digitPlaces)} ${radiusUnit}`
textLines.push(radiusLine)
}
if (area) {
const areaLine = isEmptyArea
? `Area: Oblique not supported`
: `Area: ${csUtils.roundNumber(area)} ${areaUnit}`
: `Area: ${parseFloat(area)} ${areaUnit}`
textLines.push(areaLine)
}
if (mean) {
textLines.push(`Mean: ${csUtils.roundNumber(mean)} ${modalityUnit}`)
textLines.push(`Mean: ${this.reRound(mean, this.digitPlaces)} ${modalityUnit}`)
}
if (max) {
textLines.push(`Max: ${csUtils.roundNumber(max)} ${modalityUnit}`)
textLines.push(`Max: ${this.reRound(max, this.digitPlaces)} ${modalityUnit}`)
}
if (stdDev) {
textLines.push(`Std Dev: ${csUtils.roundNumber(stdDev)} ${modalityUnit}`)
textLines.push(`Std Dev: ${this.reRound(stdDev, this.digitPlaces)} ${modalityUnit}`)
}
return textLines
},
reRound(result, finalPrecision) {
if (result.includes(', ')) {
const numStrs = result.split(', ')
const processed = numStrs.map(str => this.processSingle(str, finalPrecision))
return processed.join(', ')
}
return this.processSingle(result, finalPrecision)
},
processSingle(str, precision) {
const num = parseFloat(str)
if (isNaN(num)) return 'NaN'
//
if (Math.abs(num) < 0.0001) return str
const factor = 10 ** precision
return (Math.round(num * factor + 0.0000001) / factor).toFixed(precision)
},
//
setToolActive(toolName) {
const toolGroupId = `${this.viewportKey}-${this.activeViewportIndex}`
@ -1847,6 +1877,37 @@ export default {
this.activeTool = toolName
}
}
if (this.criterionType === 0 && this.readingTaskState < 2) {
this.$refs[`ecrf_${this.taskInfo.VisitTaskId}`][0].resetOperateState()
}
},
setReadingToolActive(toolName) {
if (this.readingTaskState === 2) return
if (this.activeTool === toolName) return
const series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
if (series && series.TaskInfo.VisitTaskId && series.TaskInfo.VisitTaskId === this.taskInfo.VisitTaskId) {
const toolGroupId = `${this.viewportKey}-${this.activeViewportIndex}`
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
if (this.activeTool) {
toolGroup.setToolPassive(this.activeTool)
}
toolGroup.setToolActive(toolName, {
bindings: [{ mouseButton: MouseBindings.Primary }]
})
this.activeTool = toolName
}
},
setReadingToolPassive() {
if (this.readingTaskState === 2) return
if (this.activeTool && this.toolNames.includes(this.activeTool)) {
const series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
if (series && series.TaskInfo.VisitTaskId && series.TaskInfo.VisitTaskId === this.taskInfo.VisitTaskId) {
const toolGroupId = `${this.viewportKey}-${this.activeViewportIndex}`
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
toolGroup.setToolPassive(this.activeTool)
this.activeTool = ''
}
}
},
setMoreToolActive(toolName) {
if (this.readingTaskState === 2) return

View File

@ -232,22 +232,32 @@
<el-input
type="number"
@change="(val) => { formItemNumberChange(val, question) }"
@blur="!questionsMarkStatus[question.Id] ? handleBlur(questionForm[question.Id], questionForm, question.Id) : ()=>{}"
v-model="questionForm[question.Id]"
disabled
:disabled="(questionsMarkStatus[question.Id] && question.ImageMarkEnum === 2) || question.ImageMarkEnum === 1"
style="width: 150px;"
>
<template v-if="question.Unit !== 0" slot="append">
{{ question.Unit !== 4 ? $fd('ValueUnit', question.Unit) : question.CustomUnit }}
</template>
</el-input>
<!-- 标记 -->
<!-- 测量 -->
<el-button
v-if="readingTaskState < 2 && !questionsMarkStatus[question.Id]"
size="mini"
type="text"
@click="operateImageMarker({operateStateEnum: 1, question})"
>
标记
测量
</el-button>
<!-- 绑定 -->
<el-button
v-if="readingTaskState < 2 && !questionsMarkStatus[question.Id]"
size="mini"
type="text"
@click="operateImageMarker({operateStateEnum: 0, question})"
>
绑定
</el-button>
<!-- 查看 -->
<el-button

View File

@ -118,9 +118,11 @@ export default {
isBaseLineTask: false,
rerender: true,
questionMarkInfoList: [],
operateStateEnum: null, // 12345
operateStateEnum: null, // 012345
operateQuestionId: '',
operateRowId: '',
imageTool: '',
imageToolAttribute: '',
questionsMarkStatus: {}, // 12
digitPlaces: 2
}
@ -347,6 +349,7 @@ export default {
}
})
},
verifyAnnotationIsBound(annotation) {
const i = this.questionMarkInfoList.findIndex(i => i.MeasureData && i.MeasureData.annotationUID === annotation.annotationUID)
return i > -1
@ -354,9 +357,14 @@ export default {
async operateImageMarker(obj) {
this.operateStateEnum = obj.operateStateEnum
this.operateQuestionId = obj.question.Id
if (obj.operateStateEnum === 1) {
this.imageTool = obj.question.ImageTool
this.imageToolAttribute = obj.question.ImageToolAttribute
if (obj.operateStateEnum === 0) {
//
this.$emit('setReadingToolPassive')
} else if (obj.operateStateEnum === 1) {
//
this.$emit('setReadingToolActive', obj.question.ImageTool)
} else if (obj.operateStateEnum === 2) {
//
const i = this.questionMarkInfoList.findIndex(i => i.QuestionId === obj.question.Id)
@ -366,8 +374,9 @@ export default {
}
} else if (obj.operateStateEnum === 3) {
//
// this.$set(this.questionsMarkStatus, obj.question.Id, 1)
this.$emit('setReadingToolPassive')
} else if (obj.operateStateEnum === 4) {
this.$emit('setReadingToolPassive')
//
this.$set(this.questionForm, obj.question.Id, '')
const i = this.questionMarkInfoList.findIndex(i => i.QuestionId === obj.question.Id)
@ -405,14 +414,20 @@ export default {
},
async bindAnnotationToQuestion(annotation) {
try {
if (!(this.operateStateEnum === 1 || this.operateStateEnum === 3)) return
if (!(this.operateStateEnum === 0 || this.operateStateEnum === 1 || this.operateStateEnum === 3)) return
if (this.operateStateEnum === 1 && annotation.markTool !== this.imageTool) return
if (!this.operateQuestionId) return
if (this.operateStateEnum === 3) {
if (this.operateStateEnum === 0 || this.operateStateEnum === 3) {
if (!annotation.data.label) {
this.$alert('该标记不能与问题绑定!')
return
}
const confirm = await this.$confirm('是否确认更改?', {
if (annotation.markTool !== this.imageTool) {
this.$alert('该标记不能与问题绑定!')
return
}
let msg = this.operateStateEnum === 0 ? '是否确认绑定?' : '是否确认更改?'
const confirm = await this.$confirm(msg, {
type: 'warning',
distinguishCancelAndClose: true
})
@ -449,7 +464,6 @@ export default {
}
},
updateAnnotationToQuestion(annotation) {
console.log('updateAnnotationToQuestion', annotation)
const i = this.questionMarkInfoList.findIndex(i => i.MeasureData && i.MeasureData.annotationUID === annotation.annotationUID)
if (i === -1) return
this.questionMarkInfoList[i].measureData = annotation
@ -464,13 +478,25 @@ export default {
length = length ? parseFloat(length).toFixed(this.digitPlaces) : length
this.$set(this.questionForm, questionId, length)
} else if (annotation.metadata.toolName === 'Bidirectional') {
// let length = annotation.data.cachedStats[`imageId:${referencedImageId}`].length
// length = length ? parseFloat(length).toFixed(this.digitPlaces) : length
let short = annotation.data.cachedStats[`imageId:${referencedImageId}`].width
short = short ? parseFloat(short).toFixed(this.digitPlaces) : short
this.$set(this.questionForm, questionId, short)
if (this.imageToolAttribute === 'length') {
let length = annotation.data.cachedStats[`imageId:${referencedImageId}`].length
length = length ? parseFloat(length).toFixed(this.digitPlaces) : length
this.$set(this.questionForm, questionId, length)
} else if (this.imageToolAttribute === 'width') {
let short = annotation.data.cachedStats[`imageId:${referencedImageId}`].width
short = short ? parseFloat(short).toFixed(this.digitPlaces) : short
this.$set(this.questionForm, questionId, short)
}
}
},
resetOperateState() {
console.log('resetOperateState')
this.operateStateEnum = null
this.operateQuestionId = ''
this.operateRowId = ''
this.imageTool = ''
this.imageToolAttribute = ''
},
async resetForm() {
const confirm = await this.$confirm(
this.$t('trials:dicomReading:message:confirmReset1'),

View File

@ -460,6 +460,7 @@ export default {
},
formItemNumberChange(v, question) {
this.$emit('formItemTableNumberChange', v, question)
this.$emit('setFormItemData', { key: question.Id, val: v, question: question })
// this.$emit('formItemTableNumberChange', v, question)
},
resetChild(obj) {

View File

@ -353,7 +353,9 @@ export default {
}
},
formItemNumberChange(v, question) {
this.$emit('formItemTableNumberChange', v, question)
// this.$emit('formItemTableNumberChange', v, question)
this.$emit('formItemTableNumberChange')
this.$emit('setFormItemData', { key: question.Id, val: v, question: question })
},
resetChild(obj) {
obj.forEach(i => {

View File

@ -729,17 +729,62 @@
</div>
<!-- 影像标记 -->
<el-form-item
v-if="form.Type === 'number' && !isFromSystem"
v-if="form.Type === 'number' && !isFromSystem && readingVersionEnum"
:label="$t('trials:readingUnit:qsList:title:imageMarkEnum')"
prop="ImageMarkEnum"
>
<el-radio-group
v-model="form.ImageMarkEnum"
:disabled="form.IsRequired === 0"
@change="imageMarkEnumChange"
>
<el-radio v-for="item of $d.ImageMark" :key="item.id" :label="item.value" :disabled="form.IsRequired === 2 && item.value === 1">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<!-- 测量工具 ImageTool -->
<el-form-item
v-if="form.ImageMarkEnum === 1 || form.ImageMarkEnum === 2"
:label="$t('trials:readingUnit:qsList:title:ImageTool')"
prop="ImageTool"
:rules="[
{ required: true, message: this.$t('common:ruleMessage:select')}
]"
>
<el-radio-group
v-model="form.ImageTool"
:disabled="form.IsRequired === 0"
@change="imageToolChange"
>
<el-radio
v-for="tool of readingTools"
:key="tool.toolName"
:label="tool.toolName"
>
{{ $t(tool.i18nKey) }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- 测量值 ImageToolAttribute -->
<el-form-item
v-if="form.ImageTool"
:label="$t('trials:readingUnit:qsList:title:ImageToolAttribute')"
prop="ImageToolAttribute"
:rules="[
{ required: true, message: this.$t('common:ruleMessage:select')}
]"
>
<el-radio-group
v-model="form.ImageToolAttribute"
>
<el-radio
v-for="i of imageToolAttributes"
:key="i"
:label="i"
>
{{ i }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- 最大上传个数 -->
<el-form-item
v-if="form.Type === 'upload'"
@ -1031,6 +1076,16 @@ export default {
isLook: {
type: Boolean,
default: true
},
readingTools: {
type: Array,
default() {
return []
}
},
readingVersionEnum: {
type: Number,
default: 0
}
},
@ -1085,9 +1140,13 @@ export default {
ClassifyType: null,
ClassifyEditType: null,
ClassifyShowType: null,
ImageMarkEnum: 0
ImageMarkEnum: 0,
ImageTool: '',
ImageToolAttribute: '',
// IsEnable: true
},
imageToolAttributes: [],
rules: {
Type: [
{required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur', 'change']}
@ -1363,6 +1422,9 @@ export default {
}
}
}
if (this.form.ImageTool) {
this.imageToolChange(this.form.ImageTool)
}
}
if (!this.data.ShowOrder && this.data.ShowOrder !== 0) {
if (this.list.length > 0) {
@ -1477,8 +1539,20 @@ export default {
form.ImageMarkEnum = 0
}
},
imageMarkEnumChange(val) {
if (val === 0) {
this.form.ImageTool = ''
this.form.ImageToolAttribute = ''
this.imageToolAttributes = []
}
},
imageToolChange(v) {
let i = this.readingTools.findIndex(tool=>tool.toolName === v)
if (i > -1) {
this.imageToolAttributes = this.readingTools[i].props
}
},
parentQuestionChange(val, form) {
console.log(val)
this.isParentExistGroup = false
if (val) {
var index = this.parentOptions.findIndex(item => {
@ -1571,6 +1645,9 @@ export default {
form.ClassifyType = null
form.ClassifyShowType = null
form.ImageMarkEnum = 0
form.ImageTool = ''
form.ImageToolAttribute = ''
this.imageToolAttributes = []
},
getLesionType() {
return new Promise((resolve, reject) => {

View File

@ -210,12 +210,14 @@
append-to-body
custom-class="base-dialog-wrapper"
>
<QuestionsForm
<questions-form
ref="addOrEdit"
:data="rowData"
:trial-criterion-id="trialCriterionId"
:is-from-system="isFromSystem"
:digit-places="digitPlaces"
:readingTools="readingTools"
:readingVersionEnum="readingVersionEnum"
:list="tblList"
:is-look="isLook"
:is-system-criterion="isSystemCriterion"
@ -231,7 +233,7 @@
:title="preview.title"
:fullscreen="true"
>
<QuestionsPreview :criterion-id="trialCriterionId" :is-system-criterion="isSystemCriterion" :form-type="1" />
<questions-preview :criterion-id="trialCriterionId" :is-system-criterion="isSystemCriterion" :form-type="1" />
</el-dialog>
<el-dialog
@ -241,7 +243,7 @@
:title="config.title"
:fullscreen="true"
>
<TableQsList
<table-qs-list
:digit-places="digitPlaces"
:reading-question-id="rowData.Id"
:is-from-system="isFromSystem"
@ -299,6 +301,16 @@ export default {
isFromSystem: {
type: Boolean,
default: true
},
readingTools: {
type: Array,
default() {
return []
}
},
readingVersionEnum: {
type: Number,
default: 0
}
},
@ -321,7 +333,6 @@ export default {
},
mounted() {
this.tblList = this.list
console.log('ql', this.isSystemCriterion)
},
methods: {
getList() {

View File

@ -10,7 +10,7 @@
>
<!-- '表单问题' -->
<el-form-item :label="$t('trials:readingUnit:readingCriterion:title:formQs')">
<QuestionsList
<questions-list
:ref="`questionList${trialReadingCriterionId}`"
v-if="form.FormType===1"
:trial-reading-criterion-id="trialReadingCriterionId"
@ -20,6 +20,8 @@
:is-system-criterion="isSystemCriterion"
:is-from-system="readingInfo.IsFromSystem"
:digit-places="digitPlaces"
:readingTools="readingTools"
:readingVersionEnum="readingVersionEnum"
@reloadArbitrationRules="reloadArbitrationRules"
/>
</el-form-item>
@ -77,6 +79,12 @@ export default {
isAdditionalAssessment: {
type: Boolean,
default: false
},
readingTools: {
type: Array,
default() {
return []
}
}
},
data() {
@ -99,7 +107,8 @@ export default {
readingInfo: {},
isConfirm: true,
configBaseDataVisible: false,
additionalAssessmentOptionList: null
additionalAssessmentOptionList: null,
readingVersionEnum: 0
}
},
mounted() {
@ -118,6 +127,7 @@ export default {
}
getTrialReadingCriterionInfo({ trialId, TrialReadingCriterionId: this.trialReadingCriterionId }).then(res => {
this.loading = false
this.readingVersionEnum = res.OtherInfo.ReadingVersionEnum
this.readingInfo = res.Result
for (const k in this.form) {
if (res.Result.hasOwnProperty(k)) {
@ -126,7 +136,6 @@ export default {
}
this.isConfirm = res.Result.IsSign
this.isSystemCriterion = res.Result.IsSystemCriterion
console.log(this.isSystemCriterion)
}).catch(() => {
this.loading = false
})
@ -137,7 +146,6 @@ export default {
//
handleSave(isPrompt = true) {
return new Promise((resolve, reject) => {
console.log(this.form)
this.$refs['readingCriterionsForm'].validate((valid) => {
if (!valid) {
resolve(false)

View File

@ -917,6 +917,8 @@ export default {
this.$emit('setGlobalReading', res.Result.IsGlobalReading)
this.$emit('setOncologyReading', res.Result.IsOncologyReading)
this.$emit('setDigitPlaces', res.Result.DigitPlaces)
this.$emit('setReadingTools', res.Result.ReadingToolList)
if (res.Result.ReadingType === 1) {
this.$emit('setArbitrationReading', false)
}
@ -957,6 +959,7 @@ export default {
this.$emit('setGlobalReading', this.form.IsGlobalReading)
this.$emit('setOncologyReading', this.form.IsOncologyReading)
this.$emit('setDigitPlaces', this.form.DigitPlaces)
this.$emit('setReadingTools', this.form.ReadingToolList)
if (this.form.ReadingType === 1) {
this.$emit('setArbitrationReading', false)
}

View File

@ -1199,6 +1199,12 @@ export default {
}
}
},
classifyQuestionChange (v){
let obj = this.selectQuestions.find(i=>i.Id === v)
let arr = obj && obj.TypeValue ? obj.TypeValue.split('|') : []
arr = arr.length > 0 ? arr.map(i=>i.trim()) : []
this.classifyQuestionOptions = arr
},
getBasicConfigSelect() {
getCriterionDictionaryList({
CriterionId: this.criterionId,

View File

@ -46,6 +46,7 @@
@setGlobalReading="setGlobalReading"
@setOncologyReading="setOncologyReading"
@setDigitPlaces="setDigitPlaces"
@setReadingTools="setReadingTools"
/>
</el-collapse-item>
<!-- 阅片标准 -->
@ -57,6 +58,7 @@
:ref="`readingCriterions${item.TrialReadingCriterionId}`"
:trial-reading-criterion-id="TrialReadingCriterionId"
:digit-places="digitPlaces"
:readingTools="readingTools"
:is-additional-assessment="isAdditionalAssessment"
@reloadArbitrationRules="reloadArbitrationRules"
/>
@ -174,7 +176,7 @@ import GlobalReading from "./components/GlobalReading";
import OncologyForm from "./components/OncologyForm";
import SignForm from "@/views/trials/components/newSignForm";
import const_ from "@/const/sign-code";
import { getCustomizeStandardsTools } from '@/views/trials/trials-panel/reading/dicoms3D/components/toolConfig'
export default {
name: "ReadingUnit",
components: {
@ -208,6 +210,7 @@ export default {
isGlobalReading: false,
digitPlaces: 0,
isAdditionalAssessment: false,
readingTools: []
};
},
watch: {
@ -295,6 +298,9 @@ export default {
setDigitPlaces(digitPlaces) {
this.digitPlaces = digitPlaces;
},
setReadingTools(readingTools) {
this.readingTools = getCustomizeStandardsTools(readingTools)
},
setIsClinicalReading(isClinicalReading) {
this.isClinicalReading = isClinicalReading;
},