自定义标准阅片交互更改

main
caiyiling 2025-06-06 17:25:23 +08:00
parent 8d5dfd0258
commit f98ea18205
6 changed files with 321 additions and 246 deletions

View File

@ -596,6 +596,8 @@ const {
// RectangleROITool, // RectangleROITool,
PlanarFreehandROITool, PlanarFreehandROITool,
CircleROITool, CircleROITool,
AngleTool,
CobbAngleTool,
EraserTool, EraserTool,
MIPJumpToClickTool, MIPJumpToClickTool,
VolumeRotateTool, VolumeRotateTool,
@ -1136,6 +1138,8 @@ export default {
cornerstoneTools.addTool(BidirectionalTool) cornerstoneTools.addTool(BidirectionalTool)
cornerstoneTools.addTool(ScaleOverlayTool) cornerstoneTools.addTool(ScaleOverlayTool)
cornerstoneTools.addTool(CircleROITool) cornerstoneTools.addTool(CircleROITool)
cornerstoneTools.addTool(AngleTool)
cornerstoneTools.addTool(CobbAngleTool)
cornerstoneTools.addTool(MIPJumpToClickTool) cornerstoneTools.addTool(MIPJumpToClickTool)
cornerstoneTools.addTool(VolumeRotateTool) cornerstoneTools.addTool(VolumeRotateTool)
@ -1183,6 +1187,12 @@ export default {
toolGroup.addTool(CircleROITool.toolName, { toolGroup.addTool(CircleROITool.toolName, {
getTextLines: this.getCircleROIToolTextLines getTextLines: this.getCircleROIToolTextLines
}) })
toolGroup.addTool(AngleTool.toolName, {
getTextLines: this.getAngleToolTextLines
})
toolGroup.addTool(CobbAngleTool.toolName, {
getTextLines: this.getCobbAngleToolTextLines
})
if (toolGroupId === 'viewport-fusion-3') { if (toolGroupId === 'viewport-fusion-3') {
toolGroup.addTool(VolumeRotateTool.toolName) toolGroup.addTool(VolumeRotateTool.toolName)
toolGroup.setToolActive(VolumeRotateTool.toolName, { toolGroup.setToolActive(VolumeRotateTool.toolName, {
@ -1229,6 +1239,8 @@ export default {
toolGroup.setToolPassive(LengthTool.toolName) toolGroup.setToolPassive(LengthTool.toolName)
toolGroup.setToolPassive(BidirectionalTool.toolName) toolGroup.setToolPassive(BidirectionalTool.toolName)
toolGroup.setToolPassive(CircleROITool.toolName) toolGroup.setToolPassive(CircleROITool.toolName)
toolGroup.setToolPassive(AngleTool.toolName)
toolGroup.setToolPassive(CobbAngleTool.toolName)
} else { } else {
toolGroup.setToolEnabled(ArrowAnnotateTool.toolName) toolGroup.setToolEnabled(ArrowAnnotateTool.toolName)
toolGroup.setToolEnabled(RectangleROITool.toolName) toolGroup.setToolEnabled(RectangleROITool.toolName)
@ -1236,6 +1248,8 @@ export default {
toolGroup.setToolEnabled(LengthTool.toolName) toolGroup.setToolEnabled(LengthTool.toolName)
toolGroup.setToolEnabled(BidirectionalTool.toolName) toolGroup.setToolEnabled(BidirectionalTool.toolName)
toolGroup.setToolEnabled(CircleROITool.toolName) toolGroup.setToolEnabled(CircleROITool.toolName)
toolGroup.setToolEnabled(AngleTool.toolName)
toolGroup.setToolEnabled(CobbAngleTool.toolName)
} }
toolGroup.setToolPassive(EraserTool.toolName) toolGroup.setToolPassive(EraserTool.toolName)
}) })
@ -1666,7 +1680,6 @@ export default {
} }
const textLines = [] const textLines = []
if (data.label) { if (data.label) {
// textLines.push(data.label)
textLines.push(data.status ? `${data.label}(${data.status})` : data.label) textLines.push(data.status ? `${data.label}(${data.status})` : data.label)
} }
textLines.push(`${parseFloat(length).toFixed(this.digitPlaces)} ${unit}`) textLines.push(`${parseFloat(length).toFixed(this.digitPlaces)} ${unit}`)
@ -1688,7 +1701,9 @@ export default {
} = cachedVolumeStats || {} } = cachedVolumeStats || {}
const textLines = [] const textLines = []
if (data.label) {
textLines.push(data.label)
}
if (area) { if (area) {
const areaLine = isEmptyArea const areaLine = isEmptyArea
? `Area: Oblique not supported` ? `Area: Oblique not supported`
@ -1743,6 +1758,9 @@ export default {
} }
const textLines = [] const textLines = []
if (data.label) {
textLines.push(data.label)
}
textLines.push(`Area: ${this.reRound(area, this.digitPlaces)} ${areaUnit}`) textLines.push(`Area: ${this.reRound(area, this.digitPlaces)} ${areaUnit}`)
textLines.push(`Mean: ${this.reRound(mean, this.digitPlaces)} ${modalityUnit}`) textLines.push(`Mean: ${this.reRound(mean, this.digitPlaces)} ${modalityUnit}`)
@ -1756,11 +1774,8 @@ export default {
const { length, width, unit } = cachedStats[targetId] const { length, width, unit } = cachedStats[targetId]
const textLines = [] const textLines = []
// if (label) {
// textLines.push(label);
// }
if (label) { if (label) {
// textLines.push(label)
textLines.push(data.status ? `${label}(${data.status})` : label) textLines.push(data.status ? `${label}(${data.status})` : label)
} }
if (length === undefined) { if (length === undefined) {
@ -1790,7 +1805,7 @@ export default {
const textLines = [] const textLines = []
if (data.label) { if (data.label) {
// textLines.push(data.label) // textLines.push(data.label)
textLines.push(data.status ? `${data.label}(${data.status})` : data.label) textLines.push(data.label)
} }
if (radius) { if (radius) {
@ -1821,8 +1836,40 @@ export default {
return textLines return textLines
}, },
getAngleToolTextLines(data, targetId) {
const cachedVolumeStats = data.cachedStats[targetId]
const { angle } = cachedVolumeStats
if (angle === undefined) {
return
}
if (isNaN(angle)) {
return [`${angle}`]
}
const textLines = []
if (label) {
textLines.push(label)
}
textLines.push(`${angle.toFixed(this.digitPlaces)} ${String.fromCharCode(176)}`)
return textLines
},
getCobbAngleToolTextLines(data, targetId) {
const cachedVolumeStats = data.cachedStats[targetId]
const { angle } = cachedVolumeStats
if (angle === undefined) {
return
}
const textLines = []
if (label) {
textLines.push(label)
}
textLines.push(`${angle.toFixed(this.digitPlaces)} ${String.fromCharCode(176)}`)
return textLines
},
reRound(result, finalPrecision) { reRound(result, finalPrecision) {
if (result.includes(', ')) { if (typeof result === 'string' && result.includes(', ')) {
const numStrs = result.split(', ') const numStrs = result.split(', ')
const processed = numStrs.map(str => this.processSingle(str, finalPrecision)) const processed = numStrs.map(str => this.processSingle(str, finalPrecision))
return processed.join(', ') return processed.join(', ')

View File

@ -232,9 +232,9 @@
<el-input <el-input
type="number" type="number"
@change="(val) => { formItemNumberChange(val, question) }" @change="(val) => { formItemNumberChange(val, question) }"
@blur="!questionsMarkStatus[question.Id] ? handleBlur(questionForm[question.Id], questionForm, question.Id) : ()=>{}" @blur="questionsMarkStatus[question.Id] && questionsMarkStatus[question.Id].isMarked ? ()=>{} : handleMarkedQsBlur(questionForm[question.Id], questionForm, question.Id, question) "
v-model="questionForm[question.Id]" v-model="questionForm[question.Id]"
:disabled="(questionsMarkStatus[question.Id] && question.ImageMarkEnum === 2) || question.ImageMarkEnum === 1" :disabled="(questionsMarkStatus[question.Id].isMarked && question.ImageMarkEnum === 2) || question.ImageMarkEnum === 1"
style="width: 150px;" style="width: 150px;"
> >
<template v-if="question.Unit !== 0" slot="append"> <template v-if="question.Unit !== 0" slot="append">
@ -243,7 +243,7 @@
</el-input> </el-input>
<!-- 测量 --> <!-- 测量 -->
<el-button <el-button
v-if="readingTaskState < 2 && !questionsMarkStatus[question.Id]" v-if="readingTaskState < 2 && (!questionsMarkStatus[question.Id].isMarked)"
size="mini" size="mini"
type="text" type="text"
@click="operateImageMarker({operateStateEnum: 1, question})" @click="operateImageMarker({operateStateEnum: 1, question})"
@ -252,7 +252,7 @@
</el-button> </el-button>
<!-- 绑定 --> <!-- 绑定 -->
<el-button <el-button
v-if="readingTaskState < 2 && !questionsMarkStatus[question.Id]" v-if="readingTaskState < 2 && (!questionsMarkStatus[question.Id].isMarked)"
size="mini" size="mini"
type="text" type="text"
@click="operateImageMarker({operateStateEnum: 0, question})" @click="operateImageMarker({operateStateEnum: 0, question})"
@ -261,7 +261,7 @@
</el-button> </el-button>
<!-- 查看 --> <!-- 查看 -->
<el-button <el-button
v-if="questionsMarkStatus[question.Id]" v-if="questionsMarkStatus[question.Id].isMarked"
size="mini" size="mini"
type="text" type="text"
@click="operateImageMarker({operateStateEnum: 2, question})" @click="operateImageMarker({operateStateEnum: 2, question})"
@ -270,7 +270,7 @@
</el-button> </el-button>
<!-- 更改 --> <!-- 更改 -->
<el-button <el-button
v-if="readingTaskState < 2 && questionsMarkStatus[question.Id]" v-if="readingTaskState < 2 && (questionsMarkStatus[question.Id].isMarked)"
size="mini" size="mini"
type="text" type="text"
@click="operateImageMarker({operateStateEnum: 3, question})" @click="operateImageMarker({operateStateEnum: 3, question})"
@ -279,7 +279,7 @@
</el-button> </el-button>
<!-- 移除 --> <!-- 移除 -->
<el-button <el-button
v-if="readingTaskState < 2 && questionsMarkStatus[question.Id]" v-if="readingTaskState < 2 && (questionsMarkStatus[question.Id].isMarked)"
size="mini" size="mini"
type="text" type="text"
@click="operateImageMarker({operateStateEnum: 4, question})" @click="operateImageMarker({operateStateEnum: 4, question})"
@ -288,12 +288,12 @@
</el-button> </el-button>
<!-- 保存 --> <!-- 保存 -->
<el-button <el-button
v-if="readingTaskState < 2 && questionsMarkStatus[question.Id]" v-if="readingTaskState < 2 && !questionsMarkStatus[question.Id].isSaved"
size="mini" size="mini"
type="text" type="text"
@click="operateImageMarker({operateStateEnum: 5, question})" @click="operateImageMarker({operateStateEnum: 5, question})"
> >
<el-tooltip v-if="questionsMarkStatus[question.Id] === 1" class="item" effect="dark" :content="$t('trials:reading:button:unsaved')" placement="bottom"> <el-tooltip v-if="!questionsMarkStatus[question.Id].isSaved" class="item" effect="dark" :content="$t('trials:reading:button:unsaved')" placement="bottom">
<i class="el-icon-warning" style="color:red" /> <i class="el-icon-warning" style="color:red" />
</el-tooltip> </el-tooltip>
保存 保存
@ -601,6 +601,10 @@ export default {
handleBlur(value, a, b) { handleBlur(value, a, b) {
this.$set(a, b, parseFloat(value).toFixed(this.digitPlaces)) this.$set(a, b, parseFloat(value).toFixed(this.digitPlaces))
}, },
handleMarkedQsBlur(value, a, b, question) {
this.$set(a, b, parseFloat(value).toFixed(this.digitPlaces))
this.$emit('operateImageMarker', {operateStateEnum: 6, question})
},
deleteTableCol(row, index) { deleteTableCol(row, index) {
this.$confirm(this.$t('trials:uploadNonDicoms:message:msg1')).then(() => { this.$confirm(this.$t('trials:uploadNonDicoms:message:msg1')).then(() => {
const loading = this.$loading({ fullscreen: true }) const loading = this.$loading({ fullscreen: true })

View File

@ -123,7 +123,7 @@ export default {
operateRowId: '', operateRowId: '',
imageTool: '', imageTool: '',
imageToolAttribute: '', imageToolAttribute: '',
questionsMarkStatus: {}, // 12 questionsMarkStatus: {},
digitPlaces: 2 digitPlaces: 2
} }
}, },
@ -169,8 +169,8 @@ export default {
this.questions = res.Result.SinglePage this.questions = res.Result.SinglePage
this.questionsMarkStatus = {} this.questionsMarkStatus = {}
this.questionMarkInfoList = res.OtherInfo.QuestionMarkInfoList.map(i => { this.questionMarkInfoList = res.OtherInfo.QuestionMarkInfoList.map(i => {
if (i.QuestionId && i.MeasureData) { if (i.QuestionId) {
this.$set(this.questionsMarkStatus, i.QuestionId, 2) this.$set(this.questionsMarkStatus, i.QuestionId, {isMarked: i.MeasureData !== '', isSaved: true})
} }
if (typeof i.MeasureData === 'string' && i.MeasureData) { if (typeof i.MeasureData === 'string' && i.MeasureData) {
i.MeasureData = JSON.parse(i.MeasureData) i.MeasureData = JSON.parse(i.MeasureData)
@ -381,9 +381,12 @@ export default {
this.$set(this.questionForm, obj.question.Id, '') this.$set(this.questionForm, obj.question.Id, '')
const i = this.questionMarkInfoList.findIndex(i => i.QuestionId === obj.question.Id) const i = this.questionMarkInfoList.findIndex(i => i.QuestionId === obj.question.Id)
if (i > -1) { if (i > -1) {
this.questionMarkInfoList.splice(i, 1) this.questionMarkInfoList[i].MeasureData = ''
this.questionMarkInfoList[i].StudyId = ''
this.questionMarkInfoList[i].SeriesId = ''
this.questionMarkInfoList[i].InstanceId = ''
} }
this.$set(this.questionsMarkStatus, obj.question.Id, 0) this.$set(this.questionsMarkStatus, obj.question.Id, {isMarked: false, isSaved: false})
} else if (obj.operateStateEnum === 5) { } else if (obj.operateStateEnum === 5) {
// //
this.loading = true this.loading = true
@ -404,12 +407,15 @@ export default {
} }
await saveTaskQuestion(-10, params) await saveTaskQuestion(-10, params)
this.$set(this.questionsMarkStatus, obj.question.Id, 2) this.$set(this.questionsMarkStatus, obj.question.Id, {isMarked: this.questionMarkInfoList[i].MeasureData ? true : false, isSaved: true})
this.loading = false this.loading = false
} catch (e) { } catch (e) {
console.log(e) console.log(e)
this.loading = false this.loading = false
} }
} else if (obj.operateStateEnum === 6) {
//
this.$set(this.questionsMarkStatus, obj.question.Id, {isMarked: false, isSaved: false})
} }
}, },
async bindAnnotationToQuestion(annotation) { async bindAnnotationToQuestion(annotation) {
@ -458,7 +464,7 @@ export default {
this.questionMarkInfoList[i].MeasureData = annotation this.questionMarkInfoList[i].MeasureData = annotation
} }
this.setAnswerToQuestion(annotation, this.operateQuestionId) this.setAnswerToQuestion(annotation, this.operateQuestionId)
this.$set(this.questionsMarkStatus, this.operateQuestionId, 1) this.$set(this.questionsMarkStatus, this.operateQuestionId, {isMarked: true, isSaved: false})
} catch (e) { } catch (e) {
console.log(e) console.log(e)
} }
@ -468,7 +474,7 @@ export default {
if (i === -1) return if (i === -1) return
this.questionMarkInfoList[i].measureData = annotation this.questionMarkInfoList[i].measureData = annotation
this.setAnswerToQuestion(annotation, this.questionMarkInfoList[i].QuestionId) this.setAnswerToQuestion(annotation, this.questionMarkInfoList[i].QuestionId)
this.$set(this.questionsMarkStatus, this.questionMarkInfoList[i].QuestionId, 1) this.$set(this.questionsMarkStatus, this.questionMarkInfoList[i].QuestionId, {isMarked: true, isSaved: false})
}, },
setAnswerToQuestion(annotation, questionId) { setAnswerToQuestion(annotation, questionId) {
if (!questionId || !annotation) return if (!questionId || !annotation) return

View File

@ -274,6 +274,24 @@ const config = {
'i18nKey': 'trials:reading:button:Circle', 'i18nKey': 'trials:reading:button:Circle',
'isDisabled': false, 'isDisabled': false,
'disabledReason': '' 'disabledReason': ''
},
{
'name': '角度工具',
'icon': 'angle',
'toolName': 'Angle',
'props': ['angle'],
'i18nKey': 'trials:reading:button:Angle',
'isDisabled': false,
'disabledReason': ''
},
{
'name': '角度工具',
'icon': 'cobb',
'toolName': 'CobbAngle',
'props': ['angle'],
'i18nKey': 'trials:reading:button:CobbAngle',
'isDisabled': false,
'disabledReason': ''
} }
] ]
} }

View File

@ -1095,7 +1095,7 @@ export default {
return textLines return textLines
}, },
reRound(result, finalPrecision) { reRound(result, finalPrecision) {
if (result.includes(', ')) { if (typeof result === 'string' && result.includes(', ')) {
const numStrs = result.split(', ') const numStrs = result.split(', ')
const processed = numStrs.map(str => this.processSingle(str, finalPrecision)) const processed = numStrs.map(str => this.processSingle(str, finalPrecision))
return processed.join(', ') return processed.join(', ')