From 1c0cdde9ada5898096940b639f08c8a134fde183 Mon Sep 17 00:00:00 2001
From: caiyiling <1321909229@qq.com>
Date: Thu, 7 May 2026 11:08:57 +0800
Subject: [PATCH 1/4] =?UTF-8?q?=E8=B4=A8=E6=8E=A7=E4=B8=B4=E5=BA=8A?=
=?UTF-8?q?=E6=95=B0=E6=8D=AE=E6=9B=B4=E6=94=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../reading/dicoms/components/DicomCanvas.vue | 60 +-
.../reading/dicoms/components/DicomViewer.vue | 21 +-
.../MRIPDFFAdvance/QuestionForm.vue | 864 ++++++++++++++++++
.../MRIPDFFAdvance/QuestionList.vue | 624 +++++++++++++
.../FixedCircleRoi/FixedCircleRoiTool.js | 347 +++++++
.../components/uploadPetClinicalData.vue | 3 +-
.../qc-check/components/qualityAssurance.vue | 2 +-
7 files changed, 1906 insertions(+), 15 deletions(-)
create mode 100644 src/views/trials/trials-panel/reading/dicoms/components/MRIPDFFAdvance/QuestionForm.vue
create mode 100644 src/views/trials/trials-panel/reading/dicoms/components/MRIPDFFAdvance/QuestionList.vue
create mode 100644 src/views/trials/trials-panel/reading/dicoms/tools/FixedCircleRoi/FixedCircleRoiTool.js
diff --git a/src/views/trials/trials-panel/reading/dicoms/components/DicomCanvas.vue b/src/views/trials/trials-panel/reading/dicoms/components/DicomCanvas.vue
index e4ac3899..926304e1 100644
--- a/src/views/trials/trials-panel/reading/dicoms/components/DicomCanvas.vue
+++ b/src/views/trials/trials-panel/reading/dicoms/components/DicomCanvas.vue
@@ -146,6 +146,7 @@ import BidirectionalTool from '@/views/trials/trials-panel/reading/dicoms/tools/
import ArrowAnnotateTool from '@/views/trials/trials-panel/reading/dicoms/tools/ArrowAnnotate/ArrowAnnotateTool'
import RectangleRoiTool from '@/views/trials/trials-panel/reading/dicoms/tools/RectangleRoi/RectangleRoiTool'
import ProbeTool from '@/views/trials/trials-panel/reading/dicoms/tools/Probe/ProbeTool'
+import FixedCircleRoiTool from '@/views/trials/trials-panel/reading/dicoms/tools/FixedCircleRoi/FixedCircleRoiTool'
// import OrientationMarkersTool from '@/views/trials/trials-panel/reading/dicoms/tools/OrientationMarkers/OrientationMarkersTool'
import ScaleOverlayTool from '@/views/trials/trials-panel/reading/dicoms/tools/ScaleOverlay/ScaleOverlayTool'
import getOrientationString from '@/views/trials/trials-panel/reading/dicoms/tools/OrientationMarkers/getOrientationString'
@@ -260,7 +261,7 @@ export default {
series: '',
ToolStateManager: null,
renderedMeasured: [],
- measuredTools: ['Length', 'Bidirectional', 'ArrowAnnotate', 'RectangleRoi', 'Probe'],
+ measuredTools: ['Length', 'Bidirectional', 'ArrowAnnotate', 'RectangleRoi', 'Probe', 'FixedCircleRoi'],
measureData: [],
selectedLesion: null,
activeTool: 0, // 0:enable 1:passive 2:active
@@ -584,6 +585,13 @@ export default {
e.stopPropagation()
e.preventDefault()
}
+ } else if (this.CriterionType === 22 && this.activeToolName === 'Probe' && this.readingTaskState < 2) {
+ if (!(e.detail.image.imageFrame.photometricInterpretation === 'MONOCHROME1' || e.detail.image.imageFrame.photometricInterpretation === 'MONOCHROME2')) {
+ this.$alert(this.$t('trials:MRIPDFF:message:message5'))
+ e.stopImmediatePropagation()
+ e.stopPropagation()
+ e.preventDefault()
+ }
}
},
pointNearTool(e) {
@@ -593,7 +601,9 @@ export default {
var toolType = this.measuredTools[m]
const toolState = ToolStateManager.getImageIdToolState(image.imageId, toolType)
if (toolState) {
- var toolObj = new cornerstoneTools[`${toolType}Tool`]()
+ const toolClass = this.getToolClassByType(toolType)
+ if (!toolClass) continue
+ var toolObj = new toolClass()
var i = toolState.data.findIndex(data => toolObj.pointNearTool(element, data, currentPoints.canvas, 'mouse'))
if (i > -1 && this.disabledMarks.length > 0 && this.disabledMarks.indexOf(toolState.data[i].remark) > -1) {
return true
@@ -783,7 +793,9 @@ export default {
var toolType = this.measuredTools[m]
const toolState = ToolStateManager.getImageIdToolState(e.detail.image.imageId, toolType)
if (toolState) {
- var toolObj = new cornerstoneTools[`${toolType}Tool`]()
+ const toolClass = this.getToolClassByType(toolType)
+ if (!toolClass) continue
+ var toolObj = new toolClass()
var i = toolState.data.findIndex(data => toolObj.pointNearTool(element, data, currentPoints.canvas, 'mouse'))
if (i > -1) {
@@ -1019,7 +1031,9 @@ export default {
var toolType = this.measuredTools[t]
const toolState = ToolStateManager.getImageIdToolState(e.detail.image.imageId, toolType)
if (!toolState) continue
- var toolObj = new cornerstoneTools[`${toolType}Tool`]()
+ const toolClass = this.getToolClassByType(toolType)
+ if (!toolClass) continue
+ var toolObj = new toolClass()
var i = toolState.data.findIndex(data => toolObj.pointNearTool(element, data, currentPoints.canvas, 'mouse'))
if (i > -1) {
var idx = this.measureData.findIndex(item => item.MeasureData && item.MeasureData.data && item.MeasureData.data.uuid === toolState.data[i].uuid)
@@ -1046,7 +1060,7 @@ export default {
measureData.wc = Math.round(viewport.voi.windowCenter)
measureData.data.active = false
var criterionType = parseInt(localStorage.getItem('CriterionType'))
- if (criterionType === 21) {
+ if (criterionType === 21 || criterionType === 22) {
measureData.tableQuestionId = this.measureData[idx].TableQuestionId
}
this.$emit('modifyMeasureData', { measureData, questionInfo })
@@ -1057,6 +1071,15 @@ export default {
}
}
},
+ getToolClassByType(toolType) {
+ if (toolType === 'Length') return LengthTool
+ if (toolType === 'Bidirectional') return BidirectionalTool
+ if (toolType === 'ArrowAnnotate') return ArrowAnnotateTool
+ if (toolType === 'RectangleRoi') return RectangleRoiTool
+ if (toolType === 'Probe') return ProbeTool
+ if (toolType === 'FixedCircleRoi') return FixedCircleRoiTool
+ return cornerstoneTools[`${toolType}Tool`]
+ },
loadImageStack(dicomSeries) {
return new Promise(resolve => {
this.isInitWwwc = true
@@ -1169,8 +1192,22 @@ export default {
// Add the tool
const toolName = toolBtn.getAttribute('data-tool')
const apiTool = cornerstoneTools[`${toolName}Tool`]
- if (apiTool) {
- const toolAlreadyAddedToElement = cornerstoneTools.getToolForElement(element, apiTool)
+ let toolClass = apiTool
+ if (toolName === 'Length') {
+ toolClass = LengthTool
+ } else if (toolName === 'Bidirectional') {
+ toolClass = BidirectionalTool
+ } else if (toolName === 'ArrowAnnotate') {
+ toolClass = ArrowAnnotateTool
+ } else if (toolName === 'RectangleRoi') {
+ toolClass = RectangleRoiTool
+ } else if (toolName === 'Probe' && (parseInt(localStorage.getItem('CriterionType')) === 21)) {
+ toolClass = ProbeTool
+ } else if (toolName === 'Probe' && (parseInt(localStorage.getItem('CriterionType')) === 22)) {
+ toolClass = FixedCircleRoiTool
+ }
+ if (toolClass) {
+ const toolAlreadyAddedToElement = cornerstoneTools.getToolForElement(element, toolClass)
if (!toolAlreadyAddedToElement) {
if (toolName === 'Length') {
@@ -1183,8 +1220,10 @@ export default {
cornerstoneTools.addToolForElement(element, ArrowAnnotateTool, { configuration: { allowEmptyLabel: true, handleRadius: false, drawHandlesOnHover: true, hideHandlesIfMoving: true } })
} else if (toolName === 'RectangleRoi') {
cornerstoneTools.addToolForElement(element, RectangleRoiTool, { configuration: { allowEmptyLabel: true, handleRadius: false, drawHandlesOnHover: true, hideHandlesIfMoving: true } })
- } else if (toolName === 'Probe' && parseInt(localStorage.getItem('CriterionType')) === 21) {
+ } else if (toolName === 'Probe' && (parseInt(localStorage.getItem('CriterionType')) === 21)) {
cornerstoneTools.addToolForElement(element, ProbeTool, { configuration: { fixedRadius: 5, handleRadius: true, drawHandlesOnHover: true, hideHandlesIfMoving: true, digits: this.digitPlaces } })
+ } else if (toolName === 'Probe' && parseInt(localStorage.getItem('CriterionType')) === 22) {
+ cornerstoneTools.addToolForElement(element, ProbeTool, { configuration: { radius: 5, unit: 'mm', handleRadius: true, drawHandlesOnHover: true, hideHandlesIfMoving: true, digits: this.digitPlaces } })
} else {
cornerstoneTools.addToolForElement(element, apiTool)
}
@@ -1528,7 +1567,7 @@ export default {
measureData.wc = Math.round(viewport.voi.windowCenter)
measureData.data.active = false
var criterionType = parseInt(localStorage.getItem('CriterionType'))
- if (criterionType === 21) {
+ if (criterionType === 21 || criterionType === 22) {
measureData.tableQuestionId = this.measureData[idx].TableQuestionId
}
this.$emit('modifyMeasureData', { measureData, questionInfo })
@@ -1982,7 +2021,8 @@ export default {
'CobbAngle',
'Angle',
'Bidirectional',
- 'FreehandRoi'
+ 'FreehandRoi',
+ 'FixedCircleRoi'
]
for (let i = 0; i < toolROITypes.length; i++) {
const toolROIType = toolROITypes[i]
diff --git a/src/views/trials/trials-panel/reading/dicoms/components/DicomViewer.vue b/src/views/trials/trials-panel/reading/dicoms/components/DicomViewer.vue
index cbd3f60f..cce7ce21 100644
--- a/src/views/trials/trials-panel/reading/dicoms/components/DicomViewer.vue
+++ b/src/views/trials/trials-panel/reading/dicoms/components/DicomViewer.vue
@@ -492,6 +492,10 @@
:question-form-change-state="questionFormChangeState" :question-form-change-num="questionFormChangeNum"
:is-show="isShow" :is-reading-show-subject-info="isReadingShowSubjectInfo"
@handleReadingChart="handleReadingChart" />
+
Developing...
@@ -689,6 +693,7 @@ import LuganoWithoutPETQuestionList from './LuganoWithoutPET/QuestionList'
import IVUSList from './IVUS/QuestionList'
import OCTList from './OCT/QuestionList'
import MRIPDFF from './MRIPDFF/QuestionList'
+import MRIPDFFAdvance from './MRIPDFFAdvance/QuestionList'
import CustomWwwcForm from './CustomWwwcForm'
import Manuals from './Manuals'
import Hotkeys from './Hotkeys'
@@ -725,6 +730,7 @@ export default {
IVUSList,
OCTList,
MRIPDFF,
+ MRIPDFFAdvance,
'download-dicom-and-nonedicom': downloadDicomAndNonedicom,
'upload-dicom-and-nonedicom': uploadDicomAndNonedicom,
SignForm
@@ -1012,6 +1018,10 @@ export default {
this.measuredTools = [{
toolName: 'Probe', text: this.$t('trials:reading:button:circle'), icon: 'oval', isDisabled: false, disabledReason: ''
}]
+ } else if (this.CriterionType === 22) {
+ this.measuredTools = [{
+ toolName: 'Probe', text: this.$t('trials:reading:button:circle'), icon: 'oval', isDisabled: false, disabledReason: ''
+ }]
}
this.rotateList[0] = '1'
this.colorList[0] = ''
@@ -1126,7 +1136,12 @@ export default {
DicomEvent.$on('addAnnotation', async obj => {
this.tmpData = Object.assign({}, obj.question)
// await this.imageLocation(obj.locateInfo)
- this.setToolActive('Probe', true, null, 'tableQuestion')
+ if (this.CriterionType === 21) {
+ this.setToolActive('Probe', true, null, 'tableQuestion')
+ } else if (this.CriterionType === 22) {
+ this.setToolActive('Probe', true, null, 'tableQuestion')
+ }
+
})
window.addEventListener('beforeunload', () => {
if (this.petctWindow) {
@@ -2160,7 +2175,7 @@ export default {
},
// 添加标记
setMeasureData(data) {
- if (this.CriterionType === 21) {
+ if (this.CriterionType === 21 || this.CriterionType === 22) {
if (this.tmpData) {
data.tableQuestionId = this.tmpData.Id
data.tableQuestionMark = this.tmpData.QuestionMark
@@ -2174,7 +2189,7 @@ export default {
},
// 修改标记
modifyMeasureData(data) {
- if (this.CriterionType === 21 && data.measureData.tableQuestionId) {
+ if ((this.CriterionType === 21 || this.CriterionType === 22) && data.measureData.tableQuestionId) {
this.$refs['measurementList'].modifyMeasuredData(data)
} else {
this.$refs['measurementList'].modifyMeasuredData(data)
diff --git a/src/views/trials/trials-panel/reading/dicoms/components/MRIPDFFAdvance/QuestionForm.vue b/src/views/trials/trials-panel/reading/dicoms/components/MRIPDFFAdvance/QuestionForm.vue
new file mode 100644
index 00000000..11315cae
--- /dev/null
+++ b/src/views/trials/trials-panel/reading/dicoms/components/MRIPDFFAdvance/QuestionForm.vue
@@ -0,0 +1,864 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $fd('ValueUnit', parseInt(qs.Unit)) }}
+
+
+
+
+ {{ $t('trials:MRIPDFF:button:measure') }}
+
+
+
+ {{ $t('trials:MRIPDFF:button:clear') }}
+
+
+
+ {{ $t('trials:MRIPDFF:button:return') }}
+
+
+
+
+
+
+
+ {{ $t('common:button:save') }}
+
+
+
+
+
+ { formItemChange(val, qs) })">
+
+ {{ $fd('ValueUnit', parseInt(qs.Unit)) }}
+
+
+ handleReadingChart({
+ e,
+ data: {
+ TableQuestionId: qs.Id,
+ RowIndex: questionForm.RowIndex,
+ QuestionName: qs.QuestionName
+ }
+ })" />
+
+
+
+ { formItemChange(val, qs) })" />
+
+
+ { formItemChange(val, qs) })">
+
+
+
+
+
+
+
+
+
+
+
+
+ { formItemChange(val, qs) })">
+
+
+ {{ item.label }}
+
+
+
+
+ {{ val }}
+
+
+
+
+
+ { formItemChange(val, qs) })">
+
+ {{ $fd('ValueUnit', parseInt(qs.Unit)) }}
+
+
+ handleReadingChart({
+ e,
+ data: {
+ TableQuestionId: qs.Id,
+ RowIndex: questionForm.RowIndex,
+ QuestionName: qs.QuestionName
+ }
+ })" />
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/trials/trials-panel/reading/dicoms/components/MRIPDFFAdvance/QuestionList.vue b/src/views/trials/trials-panel/reading/dicoms/components/MRIPDFFAdvance/QuestionList.vue
new file mode 100644
index 00000000..16e0c915
--- /dev/null
+++ b/src/views/trials/trials-panel/reading/dicoms/components/MRIPDFFAdvance/QuestionList.vue
@@ -0,0 +1,624 @@
+
+
+
+
+
+
+ {{ subjectCode }}
+ {{ taskBlindName }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ language === 'en' ? qs.GroupEnName : qs.GroupName }}
+
+
+
+
+
{{ item.QuestionName }}
+
+
+
+
+ {{ $t('trials:MRIPDFF:label:col1') }}
+
+
+
+ {{ $t('trials:MRIPDFF:label:col3') }}
+
+
+
+
+
+
+
+
+ {{ getLesionName(item.TableQuestions.Questions, q) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ isNaN(parseFloat(item.TableQuestions.Answers[i].mean)) ?
+ item.TableQuestions.Answers[i].mean : `${item.TableQuestions.Answers[i].mean}%` }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/trials/trials-panel/reading/dicoms/tools/FixedCircleRoi/FixedCircleRoiTool.js b/src/views/trials/trials-panel/reading/dicoms/tools/FixedCircleRoi/FixedCircleRoiTool.js
new file mode 100644
index 00000000..b400be40
--- /dev/null
+++ b/src/views/trials/trials-panel/reading/dicoms/tools/FixedCircleRoi/FixedCircleRoiTool.js
@@ -0,0 +1,347 @@
+import * as cornerstoneTools from 'cornerstone-tools'
+import * as cornerstone from 'cornerstone-core'
+import getCircleCoords from '../CircleRoi/getCircleCoords'
+const EVENTS = cornerstoneTools.EVENTS
+const getPixelSpacing = cornerstoneTools.importInternal('util/getPixelSpacing')
+const triggerEvent = cornerstoneTools.import('util/triggerEvent')
+const external = cornerstoneTools.external
+const getToolState = cornerstoneTools.getToolState
+const toolStyle = cornerstoneTools.toolStyle
+const toolColors = cornerstoneTools.toolColors
+const getModule = cornerstoneTools.getModule
+const getNewContext = cornerstoneTools.import('drawing/getNewContext')
+const draw = cornerstoneTools.import('drawing/draw')
+const setShadow = cornerstoneTools.import('drawing/setShadow')
+const drawCircle = cornerstoneTools.import('drawing/drawCircle')
+const drawHandles = cornerstoneTools.import('drawing/drawHandles')
+const drawLinkedTextBox = cornerstoneTools.import('drawing/drawLinkedTextBox')
+const getROITextBoxCoords = cornerstoneTools.import('util/getROITextBoxCoords')
+const numbersWithCommas = cornerstoneTools.import('util/numbersWithCommas')
+
+/**
+ * @public
+ * @class FixedCircleRoiTool
+ * @memberof Tools.Annotation
+ * @classdesc Tool for drawing circular regions of interest with a fixed radius, and measuring
+ * the statistics of the enclosed pixels.
+ * @extends Tools.Annotation.CircleRoiTool
+ */
+export default class FixedCircleRoiTool extends cornerstoneTools.CircleRoiTool {
+ constructor(props = {}) {
+ const defaultProps = {
+ name: 'FixedCircleRoi',
+ configuration: {
+ radius: 10, // Default radius in units (mm by default)
+ unit: 'mm', // 'mm' or 'px'
+ centerPointRadius: 0,
+ renderDashed: false,
+ drawHandlesOnHover: false,
+ handleRadius: 0,
+ digits: 1,
+ },
+ };
+ super(props, defaultProps);
+
+ // Explicitly set name to ensure it is defined
+ this.name = 'FixedCircleRoi';
+
+ // Manually merge configuration to ensure props override defaults
+ if (props && props.configuration) {
+ this.configuration = Object.assign(
+ {},
+ this.configuration || defaultProps.configuration,
+ props.configuration
+ );
+ }
+ }
+
+ createNewMeasurement(eventData) {
+ const measurementData = super.createNewMeasurement(eventData);
+
+ if (!measurementData) {
+ return;
+ }
+
+ const { image } = eventData;
+ // Fallback if configuration is missing
+ if (!this.configuration) {
+ console.warn(
+ 'FixedCircleRoiTool: configuration missing, using defaults'
+ );
+ this.configuration = { radius: 10, unit: 'mm' };
+ }
+
+ const config = this.configuration;
+ let radiusPixels = config.radius;
+
+ if (config.unit === 'mm') {
+ const pixelSpacing = getPixelSpacing(image);
+ console.log(
+ 'FixedCircleRoiTool: PixelSpacing retrieved:',
+ pixelSpacing
+ );
+
+ const { colPixelSpacing } = pixelSpacing;
+
+ if (colPixelSpacing && colPixelSpacing > 0) {
+ radiusPixels = config.radius / colPixelSpacing;
+ } else {
+ console.warn(
+ 'FixedCircleRoiTool: Invalid pixel spacing, treating radius as pixels'
+ );
+ }
+ }
+
+ // Ensure radiusPixels is a valid number
+ if (isNaN(radiusPixels)) {
+ console.warn(
+ 'FixedCircleRoiTool: radiusPixels is NaN, defaulting to 10px'
+ );
+ radiusPixels = 10;
+ }
+
+ // Set end handle position based on calculated radius pixels
+ measurementData.handles.end.x =
+ measurementData.handles.start.x + radiusPixels;
+ measurementData.handles.end.y = measurementData.handles.start.y;
+
+ console.log(
+ 'FixedCircleRoiTool created measurement:',
+ JSON.parse(JSON.stringify(measurementData))
+ );
+
+ // Invalidate to trigger stats calculation
+ measurementData.invalidated = true;
+
+ return measurementData;
+ }
+
+ /**
+ * Overwrite the addNewMeasurement method to prevent the default behavior
+ * of resizing the circle on mouse drag immediately after creation.
+ */
+ addNewMeasurement(evt, interactionType) {
+ const eventData = evt.detail;
+ if (
+ !eventData ||
+ !eventData.currentPoints ||
+ !eventData.currentPoints.image
+ ) {
+ console.warn(
+ 'FixedCircleRoiTool: Invalid eventData supplied to addNewMeasurement'
+ );
+ return;
+ }
+
+ const element = eventData.element;
+
+ // 1. Create the measurement data (which already has the fixed radius)
+ const measurementData = this.createNewMeasurement(eventData);
+
+ if (!measurementData) return;
+
+ // 2. Add it to the tool state
+ cornerstoneTools.addToolState(element, this.name, measurementData);
+
+ // 3. Ensure stats are available for completion listeners (e.g. mean value consumers).
+ if (!measurementData.cachedStats && eventData.image) {
+ this.updateCachedStats(eventData.image, element, measurementData)
+ }
+
+ // 4. Update the image to show the new annotation
+ cornerstone.updateImage(element);
+
+ // 5. Manually emit completion event since we bypass default drag-finish flow.
+ triggerEvent(element, EVENTS.MEASUREMENT_COMPLETED, {
+ toolName: this.name,
+ element,
+ measurementData,
+ })
+
+ // 6. Do NOT attach mouse/touch event listeners for resizing.
+ // The circle is created with fixed size and placed immediately.
+ }
+
+ handleSelectedCallback(evt, measurementData, handle, interactionType = 'mouse') {
+ // Lock circle radius by preventing start/end handle drag.
+ if (measurementData && measurementData.handles) {
+ const { start, end } = measurementData.handles
+ if (handle === start || handle === end) {
+ evt.stopImmediatePropagation?.()
+ evt.preventDefault?.()
+ return
+ }
+ }
+
+ super.handleSelectedCallback(evt, measurementData, handle, interactionType)
+ }
+
+ pointNearTool(element, data, coords, interactionType = 'mouse') {
+ const isNearDefault = super.pointNearTool(element, data, coords, interactionType)
+ if (isNearDefault) {
+ return true
+ }
+ if (!data || !data.handles || !data.handles.start || !data.handles.end) {
+ return false
+ }
+
+ const centerCanvas = external.cornerstone.pixelToCanvas(element, data.handles.start)
+ const edgeCanvas = external.cornerstone.pixelToCanvas(element, data.handles.end)
+ const radius = external.cornerstoneMath.point.distance(centerCanvas, edgeCanvas)
+ const distanceToCenter = external.cornerstoneMath.point.distance(centerCanvas, coords)
+
+ return distanceToCenter <= radius
+ }
+
+ renderToolData(evt) {
+ const toolData = getToolState(evt.currentTarget, this.name)
+
+ if (!toolData) {
+ return
+ }
+
+ const getDistance = external.cornerstoneMath.point.distance
+ const eventData = evt.detail
+ const { image, element, canvasContext } = eventData
+ const lineWidth = toolStyle.getToolWidth()
+ const {
+ handleRadius,
+ drawHandlesOnHover,
+ hideHandlesIfMoving,
+ renderDashed,
+ centerPointRadius,
+ } = this.configuration
+ const newContext = getNewContext(canvasContext.canvas)
+ const { rowPixelSpacing, colPixelSpacing } = getPixelSpacing(image)
+ const lineDash = getModule('globalConfiguration').configuration.lineDash
+
+ const seriesModule =
+ external.cornerstone.metaData.get('generalSeriesModule', image.imageId) ||
+ {}
+ const modality = seriesModule.modality
+ const hasPixelSpacing = rowPixelSpacing && colPixelSpacing
+
+ draw(newContext, context => {
+ for (let i = 0; i < toolData.data.length; i++) {
+ const data = toolData.data[i]
+
+ if (data.visible === false) {
+ continue
+ }
+
+ const color = toolColors.getColorIfActive(data)
+ const handleOptions = {
+ color,
+ handleRadius,
+ drawHandlesIfActive: drawHandlesOnHover,
+ hideHandlesIfMoving,
+ }
+
+ setShadow(context, this.configuration)
+
+ const startCanvas = external.cornerstone.pixelToCanvas(
+ element,
+ data.handles.start
+ )
+ const endCanvas = external.cornerstone.pixelToCanvas(
+ element,
+ data.handles.end
+ )
+ const radius = getDistance(startCanvas, endCanvas)
+
+ const circleOptions = { color }
+ if (renderDashed) {
+ circleOptions.lineDash = lineDash
+ }
+
+ drawCircle(
+ context,
+ element,
+ data.handles.start,
+ radius,
+ circleOptions,
+ 'pixel'
+ )
+
+ if (centerPointRadius && radius > 3 * centerPointRadius) {
+ drawCircle(
+ context,
+ element,
+ data.handles.start,
+ centerPointRadius,
+ circleOptions,
+ 'pixel'
+ )
+ }
+
+ if (data.handles) {
+ data.handles.start.drawnIndependently = true
+ data.handles.end.drawnIndependently = true
+ }
+ drawHandles(context, eventData, data.handles, handleOptions)
+
+ if (data.invalidated === true) {
+ if (data.cachedStats) {
+ this.throttledUpdateCachedStats(image, element, data)
+ } else {
+ this.updateCachedStats(image, element, data)
+ }
+ }
+
+ if (!data.handles.textBox.hasMoved) {
+ const defaultCoords = getROITextBoxCoords(
+ eventData.viewport,
+ data.handles
+ )
+ Object.assign(data.handles.textBox, defaultCoords)
+ }
+
+ const textBoxContent = []
+ console.log(data)
+ if (data.remark) {
+ textBoxContent.push(data.remark)
+ }
+
+ const digits = this.configuration.digits || 1
+ if (!image.color && data.cachedStats && Number.isFinite(data.cachedStats.mean)) {
+ const unit = modality === 'CT' && this.configuration.showHounsfieldUnits !== false ? 'HU' : ''
+ const meanText = numbersWithCommas(data.cachedStats.mean.toFixed(digits))
+ textBoxContent.push(unit ? `Mean: ${meanText} ${unit}` : `Mean: ${meanText}`)
+ }
+
+ if (!textBoxContent.length) {
+ continue
+ }
+
+ const textBoxAnchorPoints = handles =>
+ findTextBoxAnchorPoints(handles.start, handles.end)
+
+ drawLinkedTextBox(
+ context,
+ element,
+ data.handles.textBox,
+ textBoxContent,
+ data.handles,
+ textBoxAnchorPoints,
+ color,
+ lineWidth,
+ 20,
+ true
+ )
+
+ data.unit = modality === 'CT' && this.configuration.showHounsfieldUnits !== false ? 'HU' : ''
+ }
+ })
+ }
+}
+
+function findTextBoxAnchorPoints(startHandle, endHandle) {
+ const { left, top, width, height } = getCircleCoords(startHandle, endHandle)
+
+ return [
+ { x: left + width / 2, y: top },
+ { x: left, y: top + height / 2 },
+ { x: left + width / 2, y: top + height },
+ { x: left + width, y: top + height / 2 },
+ ]
+}
diff --git a/src/views/trials/trials-panel/visit/crc-upload/components/uploadPetClinicalData.vue b/src/views/trials/trials-panel/visit/crc-upload/components/uploadPetClinicalData.vue
index 5ed6a82a..57740b24 100644
--- a/src/views/trials/trials-panel/visit/crc-upload/components/uploadPetClinicalData.vue
+++ b/src/views/trials/trials-panel/visit/crc-upload/components/uploadPetClinicalData.vue
@@ -512,7 +512,8 @@ export default {
}
},
mounted() {
- this.isPatientFormCanEdit = this.allowAddOrEdit || this.isPatientFormAllowEdit
+ console.log(this.allowAddOrEdit,this.isPatientFormAllowEdit )
+ this.isPatientFormCanEdit = this.allowAddOrEdit || this.isPatientFormAllowEdit
this.getClinicalData()
},
methods: {
diff --git a/src/views/trials/trials-panel/visit/qc-check/components/qualityAssurance.vue b/src/views/trials/trials-panel/visit/qc-check/components/qualityAssurance.vue
index 6620aad8..ca1aab84 100644
--- a/src/views/trials/trials-panel/visit/qc-check/components/qualityAssurance.vue
+++ b/src/views/trials/trials-panel/visit/qc-check/components/qualityAssurance.vue
@@ -921,7 +921,7 @@
-
+
From af97e2b4edebead60ad94f1103eb4c35e1f96b4f Mon Sep 17 00:00:00 2001
From: caiyiling <1321909229@qq.com>
Date: Thu, 7 May 2026 12:16:01 +0800
Subject: [PATCH 2/4] =?UTF-8?q?crc=E4=BF=AE=E6=94=B9pt=E4=B8=B4=E5=BA=8A?=
=?UTF-8?q?=E6=95=B0=E6=8D=AE=E6=9B=B4=E6=94=B9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/Dicom/DicomViewer.vue | 2 +-
.../visit/crc-upload/components/studyInfo.vue | 10 ++++++++--
.../visit/crc-upload/components/uploadDicomFiles2.vue | 8 +++++++-
.../crc-upload/components/uploadPetClinicalData.vue | 1 -
4 files changed, 16 insertions(+), 5 deletions(-)
diff --git a/src/components/Dicom/DicomViewer.vue b/src/components/Dicom/DicomViewer.vue
index 41c39b9c..583b303a 100644
--- a/src/components/Dicom/DicomViewer.vue
+++ b/src/components/Dicom/DicomViewer.vue
@@ -496,7 +496,7 @@ export default {
this.colormapsList = cornerstone.colors.getColormapsList()
this.currentDicomCanvas = this.$refs['dicomCanvas0']
this.type = this.$route.query.type
- this.isEdit = parseInt(this.$route.query.showDelete)
+ this.isEdit = parseInt(this.$route.query.showDelete) || parseInt(this.$route.query.showEdit)
},
beforeDestroy() {
clearPTClinicalDataCache()
diff --git a/src/views/trials/trials-panel/visit/crc-upload/components/studyInfo.vue b/src/views/trials/trials-panel/visit/crc-upload/components/studyInfo.vue
index 4886d9c3..47dfcc74 100644
--- a/src/views/trials/trials-panel/visit/crc-upload/components/studyInfo.vue
+++ b/src/views/trials/trials-panel/visit/crc-upload/components/studyInfo.vue
@@ -151,7 +151,7 @@
-
+
@@ -309,8 +309,14 @@ export default {
// 预览影像
handleViewStudy(row) {
var token = getToken()
+ let path = ''
+ if (this.hasPermi(['trials:trials-panel:visit:crc-upload:edit'])) {
+ path = `/showdicom?studyId=${row.StudyId}&isFromCRCUpload=1&TokenKey=${token}&type=Study&showEdit=${(this.data.SubmitState * 1 < 2 || (this.data.SubmitState === 2 && this.data.IsQCConfirmedReupload)) ? 1 : 0}`
+ } else {
+ path = `/showdicom?studyId=${row.StudyId}&isFromCRCUpload=1&TokenKey=${token}&type=Study`
+ }
const routeData = this.$router.resolve({
- path: `/showdicom?studyId=${row.StudyId}&isFromCRCUpload=1&TokenKey=${token}&type=Study`,
+ path: path
})
var newWindow = window.open(routeData.href, '_blank')
this.$emit('setOpenWindow', newWindow)
diff --git a/src/views/trials/trials-panel/visit/crc-upload/components/uploadDicomFiles2.vue b/src/views/trials/trials-panel/visit/crc-upload/components/uploadDicomFiles2.vue
index 11cedab5..9b25e7bf 100644
--- a/src/views/trials/trials-panel/visit/crc-upload/components/uploadDicomFiles2.vue
+++ b/src/views/trials/trials-panel/visit/crc-upload/components/uploadDicomFiles2.vue
@@ -791,8 +791,14 @@ export default {
// 预览单个检查影像
handleViewStudy(row) {
var token = getToken()
+ let path = ''
+ if (this.hasPermi(['trials:trials-panel:visit:crc-upload:edit'])) {
+ path = `/showdicom?studyId=${row.StudyId}&TokenKey=${token}&type=Study&showEdit=${!(!this.isAfresh && this.data.SubmitState === 2 && this.data.SubmitTime) ? 1 : 0}`
+ } else {
+ path = `/showdicom?studyId=${row.StudyId}&TokenKey=${token}&type=Study`
+ }
const routeData = this.$router.resolve({
- path: `/showdicom?studyId=${row.StudyId}&TokenKey=${token}&type=Study`,
+ path: path,
})
window.open(routeData.href, '_blank')
},
diff --git a/src/views/trials/trials-panel/visit/crc-upload/components/uploadPetClinicalData.vue b/src/views/trials/trials-panel/visit/crc-upload/components/uploadPetClinicalData.vue
index 57740b24..ed6a8c8d 100644
--- a/src/views/trials/trials-panel/visit/crc-upload/components/uploadPetClinicalData.vue
+++ b/src/views/trials/trials-panel/visit/crc-upload/components/uploadPetClinicalData.vue
@@ -512,7 +512,6 @@ export default {
}
},
mounted() {
- console.log(this.allowAddOrEdit,this.isPatientFormAllowEdit )
this.isPatientFormCanEdit = this.allowAddOrEdit || this.isPatientFormAllowEdit
this.getClinicalData()
},
From 09e9c62f3c4a60eebb61753f9c2fa5a67c6d734b Mon Sep 17 00:00:00 2001
From: caiyiling <1321909229@qq.com>
Date: Thu, 7 May 2026 13:52:28 +0800
Subject: [PATCH 3/4] =?UTF-8?q?pt=E4=B8=B4=E5=BA=8A=E6=95=B0=E6=8D=AE?=
=?UTF-8?q?=E5=BC=B9=E7=AA=97=E5=8F=96=E6=B6=88=E7=82=B9=E5=87=BBmodal?=
=?UTF-8?q?=E5=85=B3=E9=97=ADDialog?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../reading/dicoms/components/DicomCanvas.vue | 2 +-
.../MRIPDFFAdvance/QuestionForm.vue | 2 -
.../MRIPDFFAdvance/QuestionList.vue | 2 -
.../reading/dicoms/tools/Probe/ProbeTool.js | 43 +++++++++++++++++--
.../visit/crc-upload/components/studyInfo.vue | 2 +-
.../components/uploadDicomFiles2.vue | 2 +-
.../visit/qc-check/components/dicomFiles.vue | 2 +-
.../qc-check/components/qualityAssurance.vue | 2 +-
8 files changed, 45 insertions(+), 12 deletions(-)
diff --git a/src/views/trials/trials-panel/reading/dicoms/components/DicomCanvas.vue b/src/views/trials/trials-panel/reading/dicoms/components/DicomCanvas.vue
index 926304e1..1c2022a1 100644
--- a/src/views/trials/trials-panel/reading/dicoms/components/DicomCanvas.vue
+++ b/src/views/trials/trials-panel/reading/dicoms/components/DicomCanvas.vue
@@ -1223,7 +1223,7 @@ export default {
} else if (toolName === 'Probe' && (parseInt(localStorage.getItem('CriterionType')) === 21)) {
cornerstoneTools.addToolForElement(element, ProbeTool, { configuration: { fixedRadius: 5, handleRadius: true, drawHandlesOnHover: true, hideHandlesIfMoving: true, digits: this.digitPlaces } })
} else if (toolName === 'Probe' && parseInt(localStorage.getItem('CriterionType')) === 22) {
- cornerstoneTools.addToolForElement(element, ProbeTool, { configuration: { radius: 5, unit: 'mm', handleRadius: true, drawHandlesOnHover: true, hideHandlesIfMoving: true, digits: this.digitPlaces } })
+ cornerstoneTools.addToolForElement(element, ProbeTool, { configuration: { fixedRadius: 5, unit: 'mm', handleRadius: true, drawHandlesOnHover: true, hideHandlesIfMoving: true, digits: this.digitPlaces } })
} else {
cornerstoneTools.addToolForElement(element, apiTool)
}
diff --git a/src/views/trials/trials-panel/reading/dicoms/components/MRIPDFFAdvance/QuestionForm.vue b/src/views/trials/trials-panel/reading/dicoms/components/MRIPDFFAdvance/QuestionForm.vue
index 11315cae..b6a654cd 100644
--- a/src/views/trials/trials-panel/reading/dicoms/components/MRIPDFFAdvance/QuestionForm.vue
+++ b/src/views/trials/trials-panel/reading/dicoms/components/MRIPDFFAdvance/QuestionForm.vue
@@ -385,13 +385,11 @@ export default {
},
setMeasureData(measureData, isInit = false) {
return new Promise(resolve => {
- console.log('setMeasureData', measureData)
if (!measureData || (measureData && measureData.tableQuestionId !== this.activeQuestionId)) {
resolve()
}
var data = {}
// 创建标记
- console.log('setMeasureData', measureData)
if (!measureData.data.remark) {
// 维护标记信息
measureData.data.remark = this.getLesionName(this.orderMark, this.activeQuestionMark)
diff --git a/src/views/trials/trials-panel/reading/dicoms/components/MRIPDFFAdvance/QuestionList.vue b/src/views/trials/trials-panel/reading/dicoms/components/MRIPDFFAdvance/QuestionList.vue
index 16e0c915..a3a722bc 100644
--- a/src/views/trials/trials-panel/reading/dicoms/components/MRIPDFFAdvance/QuestionList.vue
+++ b/src/views/trials/trials-panel/reading/dicoms/components/MRIPDFFAdvance/QuestionList.vue
@@ -432,7 +432,6 @@ export default {
}
},
modifyMeasuredData(measureObj) {
- console.log('modifyMeasuredData')
if (measureObj.questionInfo) {
this.activeItem.activeCollapseId = measureObj.questionInfo.QuestionId
this.activeItem.activeRowIndex = String(measureObj.questionInfo.RowIndex)
@@ -443,7 +442,6 @@ export default {
},
// 设置测量数据
setMeasuredData(measureData) {
- console.log('setMeasuredData')
if (this.activeItem.activeCollapseId) {
// 判断是否存在测量数据
this.$nextTick(() => {
diff --git a/src/views/trials/trials-panel/reading/dicoms/tools/Probe/ProbeTool.js b/src/views/trials/trials-panel/reading/dicoms/tools/Probe/ProbeTool.js
index a8224bbc..65baf902 100644
--- a/src/views/trials/trials-panel/reading/dicoms/tools/Probe/ProbeTool.js
+++ b/src/views/trials/trials-panel/reading/dicoms/tools/Probe/ProbeTool.js
@@ -1,4 +1,6 @@
import * as cornerstoneTools from 'cornerstone-tools'
+const EVENTS = cornerstoneTools.EVENTS
+const triggerEvent = cornerstoneTools.import('util/triggerEvent')
const external = cornerstoneTools.external
// State
const getToolState = cornerstoneTools.getToolState
@@ -87,7 +89,7 @@ export default class ProbeTool extends cornerstoneTools.ProbeTool {
return {
visible: true,
- active: true,
+ active: false,
color: undefined,
invalidated: true,
handles: {
@@ -100,8 +102,8 @@ export default class ProbeTool extends cornerstoneTools.ProbeTool {
end: {
x: eventData.currentPoints.image.x,
y: eventData.currentPoints.image.y,
- highlight: true,
- active: true,
+ highlight: false,
+ active: false,
radius: 0
},
// textBox: {
@@ -125,6 +127,41 @@ export default class ProbeTool extends cornerstoneTools.ProbeTool {
};
}
+ addNewMeasurement(evt, interactionType) {
+ const eventData = evt.detail
+ if (!eventData || !eventData.currentPoints || !eventData.currentPoints.image) {
+ return
+ }
+
+ const { element, image } = eventData
+ const measurementData = this.createNewMeasurement(eventData)
+
+ if (!measurementData) {
+ return
+ }
+
+ // Click-to-place and finish immediately; avoid default drag listeners.
+ measurementData.active = false
+ if (measurementData.handles && measurementData.handles.end) {
+ measurementData.handles.end.active = false
+ measurementData.handles.end.highlight = false
+ }
+
+ cornerstoneTools.addToolState(element, this.name, measurementData)
+
+ if (!measurementData.cachedStats && image) {
+ this.updateCachedStats(image, element, measurementData)
+ }
+
+ external.cornerstone.updateImage(element)
+
+ triggerEvent(element, EVENTS.MEASUREMENT_COMPLETED, {
+ toolName: this.name,
+ element,
+ measurementData
+ })
+ }
+
/**
*
*
diff --git a/src/views/trials/trials-panel/visit/crc-upload/components/studyInfo.vue b/src/views/trials/trials-panel/visit/crc-upload/components/studyInfo.vue
index 47dfcc74..0ccde59d 100644
--- a/src/views/trials/trials-panel/visit/crc-upload/components/studyInfo.vue
+++ b/src/views/trials/trials-panel/visit/crc-upload/components/studyInfo.vue
@@ -150,7 +150,7 @@
-
+
diff --git a/src/views/trials/trials-panel/visit/crc-upload/components/uploadDicomFiles2.vue b/src/views/trials/trials-panel/visit/crc-upload/components/uploadDicomFiles2.vue
index 9b25e7bf..ea97a047 100644
--- a/src/views/trials/trials-panel/visit/crc-upload/components/uploadDicomFiles2.vue
+++ b/src/views/trials/trials-panel/visit/crc-upload/components/uploadDicomFiles2.vue
@@ -423,7 +423,7 @@
-
+
diff --git a/src/views/trials/trials-panel/visit/qc-check/components/dicomFiles.vue b/src/views/trials/trials-panel/visit/qc-check/components/dicomFiles.vue
index 087fca9e..f69003dc 100644
--- a/src/views/trials/trials-panel/visit/qc-check/components/dicomFiles.vue
+++ b/src/views/trials/trials-panel/visit/qc-check/components/dicomFiles.vue
@@ -78,7 +78,7 @@
-
+
diff --git a/src/views/trials/trials-panel/visit/qc-check/components/qualityAssurance.vue b/src/views/trials/trials-panel/visit/qc-check/components/qualityAssurance.vue
index ca1aab84..958bca3c 100644
--- a/src/views/trials/trials-panel/visit/qc-check/components/qualityAssurance.vue
+++ b/src/views/trials/trials-panel/visit/qc-check/components/qualityAssurance.vue
@@ -920,7 +920,7 @@
-
+
From 6a345e35cdd5707a28ca5b0b488d220c2889cd8d Mon Sep 17 00:00:00 2001
From: caiyiling <1321909229@qq.com>
Date: Thu, 7 May 2026 13:59:31 +0800
Subject: [PATCH 4/4] =?UTF-8?q?=E8=A1=A8=E6=A0=BC=E5=88=97=E5=AE=BD?=
=?UTF-8?q?=E8=B0=83=E6=95=B4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../trials-panel/visit/crc-upload/components/studyInfo.vue | 2 +-
.../trials-panel/visit/qc-check/components/dicomFiles.vue | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/views/trials/trials-panel/visit/crc-upload/components/studyInfo.vue b/src/views/trials/trials-panel/visit/crc-upload/components/studyInfo.vue
index 0ccde59d..2219867a 100644
--- a/src/views/trials/trials-panel/visit/crc-upload/components/studyInfo.vue
+++ b/src/views/trials/trials-panel/visit/crc-upload/components/studyInfo.vue
@@ -55,7 +55,7 @@
-
+
-
+