-
-
@@ -444,6 +481,11 @@
@close="closeFusion" @fusion="handleFusion" />
+
+
+
@@ -462,7 +504,7 @@ import {
RenderingEngine,
Enums,
// imageLoader,
- // metaData,
+ metaData,
volumeLoader,
getRenderingEngine,
eventTarget,
@@ -479,7 +521,9 @@ import { getTools, getCustomizeStandardsTools, config } from './toolConfig'
import StudyList from './StudyList'
import Viewport from './Viewport'
import PetCtViewport from './PetCtViewport'
+import MPRViewport from './MPRViewport'
import VolumeViewport from './VolumeViewport'
+import Segmentations from './Segmentations'
import mRecisit from './mRecist/QuestionList'
import recisit from './Recist/QuestionList'
import customizeQuestionList from './customize/QuestionList'
@@ -491,15 +535,18 @@ import WL from '@/views/trials/trials-panel/reading/dicoms/components/WL'
import Others from '@/views/trials/trials-panel/reading/dicoms/components/Others'
import ClinicalData from '@/views/trials/trials-panel/reading/clinical-data'
import FusionForm from './FusionForm.vue'
+import SegmentForm from './SegmentForm.vue'
import colorMap from './colorMap.vue'
import RectangleROITool from './tools/RectangleROITool'
import ScaleOverlayTool from './tools/ScaleOverlayTool'
+import SegmentBidirectionalTool from './tools/SegmentBidirectionalTool'
import FixedRadiusCircleROITool from './tools/FixedRadiusCircleROITool'
import uploadDicomAndNonedicom from '@/components/uploadDicomAndNonedicom'
import downloadDicomAndNonedicom from '@/components/downloadDicomAndNonedicom'
import { getNetWorkSpeed, setNetWorkSpeedSizeAll, workSpeedclose } from "@/utils"
import readingChart from '@/components/readingChart'
import SystemInfo from "@/utils/systemInfo";
+import md5 from 'js-md5'
const { visibility } = annotation
const { ViewportType, Events } = Enums
const renderingEngineId = 'myRenderingEngine'
@@ -526,7 +573,11 @@ const {
VolumeRotateTool,
CrosshairsTool,
EllipticalROITool,
- synchronizers
+ synchronizers,
+ LabelMapEditWithContourTool,
+ BrushTool,
+ // SegmentBidirectionalTool,
+ utilities: CStUtils,
// cursors
} = cornerstoneTools
const { createCameraPositionSynchronizer, createVOISynchronizer, createSlabThicknessSynchronizer } = synchronizers
@@ -561,7 +612,9 @@ export default {
StudyList,
Viewport,
PetCtViewport,
+ MPRViewport,
VolumeViewport,
+ Segmentations,
mRecisit,
recisit,
customizeQuestionList,
@@ -572,6 +625,7 @@ export default {
Others,
ClinicalData,
FusionForm,
+ SegmentForm,
colorMap,
downloadDicomAndNonedicom,
uploadDicomAndNonedicom,
@@ -623,8 +677,7 @@ export default {
{ label: 'CT Lungs', wc: -400, ww: 1500 },
{ label: 'CT Abdomen', wc: 60, ww: 400 },
{ label: 'CT Liver', wc: 40, ww: 400 },
- { label: 'CT Bone', wc: 300, ww: 1500 },
- { label: 'CT Bone1', wc: 0, ww: 0 }
+ { label: 'CT Bone', wc: 300, ww: 1500 }
],
wwwcArr: [],
customWwc: { visible: false, title: this.$t('trials:reading:dagTitle:wwwcCustom') }, // 自定义调窗
@@ -686,15 +739,48 @@ export default {
SAGITTAL: {
imageNum: 0
},
- }
+ },
+
+ segmentVisible: false,
+ segmentVisitInfo: {},
+ formWrapperActiveName: 'segment',
+ actionConfiguration: {
+ contourBidirectional: {
+ method: CStUtils.segmentation.segmentContourAction,
+ bindings: [
+ {
+ key: 'c',
+ },
+ ],
+ data: {
+ segmentData: new Map(),
+ segmentationId: null
+ },
+ },
+ },
+ fusionOverlayModality: null,
+ lastUpper: null,
+ hasFusionUpperInitialized: false,
+ timer: null,
+ FullTimerOut: null,
+ isDelay: false
}
},
computed: {
gridStyle() {
return {
display: 'grid',
- gridTemplateRows: `repeat(${this.isMPR ? 2 : this.rows}, 1fr)`,
- gridTemplateColumns: `repeat(${this.isMPR ? 2 : this.cols}, 1fr)`,
+ gridTemplateRows: `repeat(${this.rows}, 1fr)`,
+ gridTemplateColumns: `repeat(${this.cols}, 1fr)`,
+ height: '100%',
+ width: '100%'
+ }
+ },
+ gridStyleMPR() {
+ return {
+ display: 'grid',
+ gridTemplateRows: `repeat(2, 1fr)`,
+ gridTemplateColumns: `repeat(2, 1fr)`,
height: '100%',
width: '100%'
}
@@ -711,11 +797,11 @@ export default {
return Array(this.rows * this.cols).fill(0)
},
viewportKey() {
- return this.isMPR ? 'viewport-volume' : this.isFusion ? 'viewport-fusion' : 'viewport'
+ return this.isMPR ? 'viewport-MPR' : this.isFusion ? 'viewport-fusion' : 'viewport'
},
openManuals() {
return !this.taskInfo.IsReadKeyFile && this.taskInfo.IsHaveKeyFile
- }
+ },
},
watch: {
openManuals: {
@@ -811,9 +897,32 @@ export default {
DicomEvent.$on('addNoneDicomMeasureData', data => {
this.open.postMessage({ type: 'addNoneDicomMeasureData', data: data }, window.location)
})
+ DicomEvent.$on('activeSeries', (series) => {
+ this.renderedTaskIds = []
+ })
document.addEventListener("click", this.foo);
+ this.getSystemInfoReading();
},
methods: {
+ resetQuestion() {
+ this.$refs[`ecrf_${this.lastViewportTaskId}`][0].getQuestions(false)
+ this.$refs[`ecrf_${this.lastViewportTaskId}`][0].initSegmentBinding()
+ },
+ handleSegmentSave(obj) {
+ this.$refs[`ecrf_${this.lastViewportTaskId}`][0].handleSegmentSave(obj)
+ },
+ openSegmentForm(obj) {
+ let { visitInfo } = obj
+ this.segmentVisitInfo = Object.assign(visitInfo, { ...obj })
+ this.segmentVisible = true
+ this.$nextTick(() => {
+ this.$refs.SegmentForm.setSeries(this.$refs[`viewport-${this.activeViewportIndex}`][0].series)
+ this.$refs.SegmentForm.init()
+ })
+ },
+ handleClick(tab, event) {
+ this.formWrapperActiveName = tab.name
+ },
setMPRInfo(obj) {
let { type, key, value } = obj
this.$set(this.MPRInfo[type], key, value)
@@ -1069,7 +1178,7 @@ export default {
}
let viewportIds = ['viewport-0', 'viewport-1', 'viewport-2', 'viewport-3']
const fusionViewportIds = ['viewport-fusion-0', 'viewport-fusion-1', 'viewport-fusion-2', 'viewport-fusion-3']
- const volumeViewportIds = ['viewport-volume-0', 'viewport-volume-1', 'viewport-volume-2']
+ const volumeViewportIds = ['viewport-MPR-0', 'viewport-MPR-1', 'viewport-MPR-2']
const element1 = this.$refs['viewport-0'][0].$el
const element2 = this.$refs['viewport-1'][0].$el
const element3 = this.$refs['viewport-2'][0].$el
@@ -1096,13 +1205,49 @@ export default {
element: element4
}
]
- if (this.criterionType === 0 && this.readingTool === 0) {
- const volumeElement1 = this.$refs['viewport-volume-0'][0].$el
- const volumeElement2 = this.$refs['viewport-volume-1'][0].$el
- const volumeElement3 = this.$refs['viewport-volume-2'][0].$el
+ if (this.readingTool === 3) {
+ viewportInputArray = [
+ {
+ viewportId: 'viewport-0',
+ type: ViewportType.ORTHOGRAPHIC,
+ element: element1,
+ defaultOptions: {
+ orientation: Enums.OrientationAxis.AXIAL
+ }
+ },
+ {
+ viewportId: 'viewport-1',
+ type: ViewportType.ORTHOGRAPHIC,
+ element: element2,
+ defaultOptions: {
+ orientation: Enums.OrientationAxis.AXIAL
+ }
+ },
+ {
+ viewportId: 'viewport-2',
+ type: ViewportType.ORTHOGRAPHIC,
+ element: element3,
+ defaultOptions: {
+ orientation: Enums.OrientationAxis.AXIAL
+ }
+ },
+ {
+ viewportId: 'viewport-3',
+ type: ViewportType.ORTHOGRAPHIC,
+ element: element4,
+ defaultOptions: {
+ orientation: Enums.OrientationAxis.AXIAL
+ }
+ }
+ ]
+ }
+ if ((this.criterionType === 0 && this.readingTool === 0) || this.readingTool === 3) {
+ const volumeElement1 = this.$refs['viewport-MPR-0'][0].$el
+ const volumeElement2 = this.$refs['viewport-MPR-1'][0].$el
+ const volumeElement3 = this.$refs['viewport-MPR-2'][0].$el
const arr = [
{
- viewportId: 'viewport-volume-0',
+ viewportId: 'viewport-MPR-0',
type: ViewportType.ORTHOGRAPHIC,
element: volumeElement1,
defaultOptions: {
@@ -1110,7 +1255,7 @@ export default {
}
},
{
- viewportId: 'viewport-volume-1',
+ viewportId: 'viewport-MPR-1',
type: ViewportType.ORTHOGRAPHIC,
element: volumeElement2,
defaultOptions: {
@@ -1118,7 +1263,7 @@ export default {
}
},
{
- viewportId: 'viewport-volume-2',
+ viewportId: 'viewport-MPR-2',
type: ViewportType.ORTHOGRAPHIC,
element: volumeElement3,
defaultOptions: {
@@ -1196,7 +1341,9 @@ export default {
cornerstoneTools.addTool(MIPJumpToClickTool)
cornerstoneTools.addTool(VolumeRotateTool)
cornerstoneTools.addTool(CrosshairsTool)
-
+ cornerstoneTools.addTool(LabelMapEditWithContourTool)
+ cornerstoneTools.addTool(BrushTool)
+ cornerstoneTools.addTool(SegmentBidirectionalTool)
viewportIds.forEach((viewportId, i) => {
// const toolGroupId = `viewport-${i}`
let toolGroupId = viewportId
@@ -1213,11 +1360,55 @@ export default {
toolGroup.addTool(PanTool.toolName)
toolGroup.addTool(ZoomTool.toolName)
+ toolGroup.addTool(BrushTool.toolName)
+ if (this.readingTool === 3 || toolGroupId === this.volumeToolGroupId) {
+ toolGroup.addToolInstance(
+ 'CircularBrush',
+ BrushTool.toolName,
+ {
+ activeStrategy: 'FILL_INSIDE_CIRCLE',
+ preview: {
+ previewColors: {}
+ }
+ }
+ );
+ toolGroup.addToolInstance(
+ 'CircularEraser',
+ BrushTool.toolName,
+ {
+ activeStrategy: 'ERASE_INSIDE_CIRCLE',
+ preview: {
+ previewColors: {}
+ }
+
+ }
+ )
+ toolGroup.addToolInstance(
+ 'ThresholdCircle',
+ BrushTool.toolName,
+ {
+ activeStrategy: 'THRESHOLD_INSIDE_CIRCLE',
+ }
+ )
+ toolGroup.addToolInstance(
+ 'ThresholdSphere',
+ BrushTool.toolName,
+ {
+ activeStrategy: 'THRESHOLD_INSIDE_SPHERE',
+ }
+ )
+ toolGroup.addTool(LabelMapEditWithContourTool.toolName);
+ toolGroup.addTool(SegmentBidirectionalTool.toolName, {
+ getTextLines: this.getBidirectionalToolTextLines
+ });
+ }
if (volumeViewportIds.includes(viewportId)) {
toolGroup.addTool(WindowLevelTool.toolName, {
targetViewportIds: volumeViewportIds
})
- toolGroup.addTool(CrosshairsTool.toolName);
+ toolGroup.addTool(CrosshairsTool.toolName, {
+ getReferenceLineColor: this.setCrosshairsToolLineColor
+ });
} else {
toolGroup.addTool(WindowLevelTool.toolName)
}
@@ -1249,7 +1440,8 @@ export default {
// cachedStats: false
})
toolGroup.addTool(BidirectionalTool.toolName, {
- // cachedStats: true
+ // cachedStats: true,
+ actions: this.actionConfiguration,
getTextLines: this.getBidirectionalToolTextLines
})
toolGroup.addTool(CircleROITool.toolName, {
@@ -1277,7 +1469,7 @@ export default {
]
})
toolGroup.addTool(MIPJumpToClickTool.toolName, {
- targetViewportIds: fusionViewportIds
+ targetViewportIds: fusionViewportIds.filter((id) => id !== toolGroupId)
})
// Set the initial state of the tools, here we set one tool active on left click.
@@ -1306,9 +1498,6 @@ export default {
toolGroup.setToolActive(PanTool.toolName, {
bindings: [{ mouseButton: MouseBindings.Auxiliary }]
})
- // toolGroup.setToolEnabled(ScaleOverlayTool.toolName);
- // toolGroup.setToolPassive(PanTool.toolName)
- // toolGroup.setToolPassive(ZoomTool.toolName)
toolGroup.setToolPassive(WindowLevelTool.toolName)
toolGroup.setToolPassive(WindowLevelRegionTool.toolName)
toolGroup.setToolPassive(PlanarRotateTool.toolName)
@@ -1324,6 +1513,10 @@ export default {
toolGroup.setToolPassive(FixedRadiusCircleROITool.toolName)
toolGroup.setToolPassive(AngleTool.toolName)
toolGroup.setToolPassive(CobbAngleTool.toolName)
+ if (this.readingTool === 3) {
+ toolGroup.setToolPassive(LabelMapEditWithContourTool.toolName)
+ toolGroup.setToolPassive(SegmentBidirectionalTool.toolName, {});
+ }
} else {
toolGroup.setToolEnabled(ArrowAnnotateTool.toolName)
toolGroup.setToolEnabled(RectangleROITool.toolName)
@@ -1335,18 +1528,19 @@ export default {
toolGroup.setToolEnabled(FixedRadiusCircleROITool.toolName)
toolGroup.setToolEnabled(AngleTool.toolName)
toolGroup.setToolEnabled(CobbAngleTool.toolName)
+ if (this.readingTool === 3) toolGroup.setToolEnabled(LabelMapEditWithContourTool.toolName)
}
toolGroup.setToolPassive(EraserTool.toolName)
})
-
eventTarget.addEventListener('cornerstoneimageloadprogress', this.imageLoadProgress)
// console.log(Events, toolsEvents)
if (this.readingTool === 2) {
this.setUpSynchronizers()
}
- if (this.criterionType === 0 && this.readingTool === 0) {
+ if ((this.criterionType === 0 && this.readingTool === 0) || this.readingTool === 3) {
this.setUpSynchronizersMPR()
}
+ renderingEngine.render();
},
// 影像下载进度回调
imageLoadProgress(e) {
@@ -1417,7 +1611,7 @@ export default {
// })
},
renderAnnotations(series) {
- const taskId = series.TaskInfo.VisitTaskId
+ const taskId = series.TaskInfo ? series.TaskInfo.VisitTaskId : null
if (!taskId || this.renderedTaskIds.includes(taskId)) return
this.renderedTaskIds.push(taskId)
const taskIdx = this.visitTaskList.findIndex(i => i.VisitTaskId === taskId)
@@ -1426,10 +1620,12 @@ export default {
annotations.map(i => {
if (i.MeasureData && !Object.hasOwn(i.MeasureData, 'isDicomReading')) {
const annotation = i.MeasureData
- annotation.highlighted = false
- cornerstoneTools.annotation.state.addAnnotation(annotation)
- if (this.visitTaskList[taskIdx].ReadingTaskState === 2) {
- cornerstoneTools.annotation.locking.setAnnotationLocked(annotation.annotationUID)
+ if ((this.readingTool === 3 && annotation.seriesId === series.Id) || this.readingTool < 3) {
+ annotation.highlighted = false
+ cornerstoneTools.annotation.state.addAnnotation(annotation)
+ if (this.visitTaskList[taskIdx].ReadingTaskState === 2) {
+ cornerstoneTools.annotation.locking.setAnnotationLocked(annotation.annotationUID)
+ }
}
}
})
@@ -1514,6 +1710,7 @@ export default {
this.$refs[`ecrf_${this.lastViewportTaskId}`][0].modifyAnnotation({ annotation, toolName: annotation.metadata.toolName })
}
this.setToolsPassive()
+
},
annotationRemovedListener(e) {
const { annotation } = e.detail
@@ -1550,9 +1747,10 @@ export default {
if (!annotation) return
const i = this.tools.findIndex(i => i.toolName === annotation.metadata.toolName)
if (i === -1) {
- this.setToolsPassive()
+ if (annotation.metadata.toolName !== LabelMapEditWithContourTool.toolName) this.setToolsPassive()
return
}
+ if (annotation.metadata.segmentationId) return
if (annotation.metadata.toolName === 'PlanarFreehandROI' && !annotation.data.contour.closed) return
const series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
if (series && series.TaskInfo.VisitTaskId && series.TaskInfo.VisitTaskId === this.taskInfo.VisitTaskId) {
@@ -1637,9 +1835,10 @@ export default {
if (!annotation) return
const i = this.tools.findIndex(i => i.toolName === annotation.metadata.toolName)
if (i === -1) {
- this.setToolsPassive()
+ if (annotation.metadata.toolName !== LabelMapEditWithContourTool.toolName) this.setToolsPassive()
return
}
+ if (annotation.metadata.segmentationId) return
if (!annotation.data.label) return
if (annotation.metadata.toolName === 'PlanarFreehandROI' && !annotation.data.contour.closed) return
const series = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series
@@ -1670,6 +1869,7 @@ export default {
// 临时标记
return
}
+ if (annotation.metadata.segmentationId) return
if (annotation.visitTaskId === this.taskInfo.VisitTaskId) {
const isBound = this.$refs[`ecrf_${annotation.visitTaskId}`][0].verifyAnnotationIsBound(annotation)
if (isBound && this.activeTool === 'Eraser') {
@@ -1722,6 +1922,9 @@ export default {
},
contentMouseup(e) {
console.log('contentMouseup')
+ if (this.$refs.Segmentations) {
+ this.$refs.Segmentations.contentMouseup()
+ }
if (this.curOperation.type === 'Modified') {
let annotation = this.curOperation.annotation
this.saveCustomAnnotation(annotation)
@@ -1841,6 +2044,15 @@ export default {
const viewport = renderingEngine.getViewport(viewportId)
viewport.render()
},
+ setCrosshairsToolLineColor(viewportId) {
+ let colors = [
+ '#ffd10a',
+ '#b6d634',
+ '#fb628b',
+ ]
+ let index = viewportId.split("-").pop()
+ return colors[colors.length - 1 - Number(index)] || colors[0]
+ },
getLengthToolTextLines(data, targetId) {
const cachedVolumeStats = data.cachedStats[targetId]
const { length, unit } = cachedVolumeStats
@@ -2268,6 +2480,7 @@ export default {
if (!obj.isCanActiveTool) {
if (this.activeTool === toolName) {
this.setToolsPassive()
+
}
this.tools[i].isDisabled = true
e.target.style.cursor = 'not-allowed'
@@ -2280,6 +2493,7 @@ export default {
// 旋转视口
setViewportRotate(value) {
this.setToolsPassive()
+
const renderingEngine = getRenderingEngine(renderingEngineId)
const viewportId = `${this.viewportKey}-${this.activeViewportIndex}`
const viewport = renderingEngine.getViewport(viewportId)
@@ -2319,8 +2533,19 @@ export default {
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].resetOrientationMarkers()
viewport.resetCamera({ resetPan: true, resetZoom: true, resetToCenter: true })
viewport.resetProperties()
+ if (this.isMPR) {
+ let volume = cache.getVolume(this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].volumeId)
+ const voi = metaData.get('voiLutModule', volume._imageIds[Math.ceil((volume._imageIds.length - 1) / 2)])
+ const lower = voi.windowCenter[0] - voi.windowWidth[0] / 2
+ const upper = voi.windowCenter[0] + voi.windowWidth[0] / 2 - 1
+ console.log(lower, upper)
+ viewport.setProperties({ voiRange: { upper: upper, lower: lower } })
+ }
viewport.render()
renderingEngine.render()
+ if (this.readingTool === 3) {
+ DicomEvent.$emit('isloaded', { isChange: false })
+ }
},
// 更改视图布局
async changeLayout(v) {
@@ -2438,7 +2663,7 @@ export default {
// 设置窗宽窗位
setWwwc(v) {
const renderingEngine = getRenderingEngine(renderingEngineId)
- const viewportId = `${this.viewportKey}-${this.activeViewportIndex}`
+ const viewportId = v.id ? v.id : `${this.viewportKey}-${this.activeViewportIndex}`
const viewport = renderingEngine.getViewport(viewportId)
const lower = v.wc - v.ww / 2
const upper = v.wc + v.ww / 2 - 1
@@ -2595,12 +2820,51 @@ export default {
this.forceFitToWindow = !forceFitToWindow
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].resize(forceFitToWindow)
},
+ // 重置视口
+ resetRenderingEngine() {
+ if (this.timer) {
+ clearInterval(this.timer)
+ this.timer = null
+ }
+ let index = null
+ this.timer = setTimeout(() => {
+ index = index || index === 0 ? index : this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series.SliceIndex
+ const renderingEngine = getRenderingEngine(renderingEngineId)
+ renderingEngine.resize(true, false)
+ renderingEngine.render()
+ this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setFullScreen(index)
+ clearTimeout(this.timer)
+ this.timer = null
+ }, 100)
+ },
+ setDelay(time) {
+ if (this.FullTimerOut) return false
+ this.FullTimerOut = setTimeout(() => {
+ this.isDelay = false
+ clearTimeout(this.FullTimerOut)
+ this.FullTimerOut = null
+ }, time)
+ },
// 切换全屏
toggleFullScreen(e, index) {
+ if (this.isDelay && (this.readingTool === 3 || this.isMPR)) return false
+
this.fullScreenIndex = this.fullScreenIndex === index ? null : index
this.activeViewportIndex = index
+ if (this.readingTool === 3 || this.isMPR) {
+ // this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setSeriesInfo(this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].series)
+ this.$nextTick(() => {
+ this.resetRenderingEngine()
+ this.isDelay = true
+ this.setDelay(2000)
+ if (this.readingTool === 3) {
+ DicomEvent.$emit('isloaded', { isChange: false })
+ }
+ })
+
+ }
if (this.isFusion) {
- const viewportIds = [`${this.viewportKey}-0`, `${this.viewportKey}-1`, `${this.viewportKey}-2`]
+ let viewportIds = [`${this.viewportKey}-0`, `${this.viewportKey}-1`, `${this.viewportKey}-2`]
viewportIds.forEach(id => {
const index = this.$refs[id][0].series.SliceIndex
this.$refs[id][0].setFullScreen(index)
@@ -2618,6 +2882,7 @@ export default {
this.cols = 1
this.activeViewportIndex = 0
this.setToolsPassive()
+
}
if (taskIndex === this.activeTaskIndex) return
if (!this.selectArr.includes(taskInfo.VisitTaskId)) {
@@ -2700,7 +2965,17 @@ export default {
this.activeViewportIndex = 0
this.fullScreenIndex = null
this.isMPR = false
- return this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setSeriesInfo(obj)
+ obj.isChange = false
+ this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setSeriesInfo(obj)
+ return this.$nextTick(() => {
+ const renderingEngine = getRenderingEngine(renderingEngineId)
+ renderingEngine.resize(true, false)
+ renderingEngine.render()
+ // if (this.readingTool === 3) {
+ // DicomEvent.$emit('isloaded', { isChange: false })
+ // }
+ })
+
}
if (!obj.IsDicom) {
return this.previewNoneDicoms(obj)
@@ -2848,7 +3123,7 @@ export default {
obj.IsMarked = true
obj.MeasureData = annotation
}
- if (annotation.from === 'MPR' && checkFrom) {
+ if ((annotation.from === 'MPR' || annotation.metadata.volumeId) && checkFrom) {
obj = Object.assign({}, seriesList[seriesIdx])
obj.SliceIndex = annotation?.metadata?.sliceIndex
obj.IsMarked = true
@@ -2863,13 +3138,22 @@ export default {
const i = this.visitTaskList.findIndex(i => i.VisitTaskId === obj.visitTaskId)
if (i === -1) return
const studyList = this.visitTaskList[i].StudyList
- let series = this.getMarkedSeries(studyList, obj.annotation, true)
+ let series = null
+ if (obj.segment) {
+ let study = studyList.find(item => item.StudyId === obj.segmentGroup.StudyId)
+ series = study.SeriesList.find(item => item.Id === obj.segmentGroup.SeriesId)
+ series.SliceIndex = 0
+ series.segment = obj.segment
+ } else {
+ series = this.getMarkedSeries(studyList, obj.annotation, true)
+ }
+
if (series) {
if (this.isFusion && series.SeriesInstanceUid !== this.fusionSerieId.ct && series.SeriesInstanceUid !== this.fusionSerieId.pt) {
return await this.$confirm(this.$t('trials:reading:confirm:fusionPTOrCTInconsistent'))
}
this.activeViewportIndex = this.cells.length - 1
- if (obj.annotation.from === 'MPR') {
+ if (obj.annotation && obj.annotation.from === 'MPR') {
let viewPlaneNormal = obj.annotation?.metadata?.viewPlaneNormal.map(i => i == 0 ? 0 : i).join(',')
if (viewPlaneNormal === '0,0,-1') {
this.activeViewportIndex = 0
@@ -2893,7 +3177,7 @@ export default {
}
if (this.isFusion) {
this.activeViewportIndex = 0
- if (series.Modality === 'PT') {
+ if (series.Modality === 'PT' || series.Modality === 'NM') {
this.activeViewportIndex = 2
}
series = {
@@ -2904,7 +3188,7 @@ export default {
}
}
}
- this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setSeriesInfo(series, true)
+ this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setSeriesInfo(series, obj.segment ? false : true)
this.$refs[series.TaskInfo.VisitTaskId][0].setSeriesActive(series.StudyIndex, series.SeriesIndex)
}
},
@@ -2950,7 +3234,7 @@ export default {
}
if (this.isFusion) {
this.activeViewportIndex = 0
- if (series.Modality === 'PT') {
+ if (series.Modality === 'PT' || series.Modality === 'NM') {
this.activeViewportIndex = 2
}
series = {
@@ -3085,7 +3369,6 @@ export default {
if (obj.isActiveTarget && obj.SplitOrMergeType !== '1' && obj.SplitOrMergeType !== '3') {
this.setToolToTarget(obj)
}
-
resolve()
})
},
@@ -3095,6 +3378,7 @@ export default {
const toolName = obj.markTool
if (this.activeTool) {
this.setToolsPassive()
+
}
const toolGroupId = this.isMPR ? this.volumeToolGroupId : `${this.viewportKey}-${this.activeViewportIndex}`
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
@@ -3315,9 +3599,9 @@ export default {
syncColormap: false
})
let viewportIds = [
- `viewport-volume-0`,
- `viewport-volume-1`,
- `viewport-volume-2`
+ `viewport-MPR-0`,
+ `viewport-MPR-1`,
+ `viewport-MPR-2`
]
viewportIds.forEach((viewportId) => {
MPRVoiSynchronizer.add({
@@ -3329,9 +3613,9 @@ export default {
// Add viewports to VOI synchronizers
[
- `viewport-volume-0`,
- `viewport-volume-1`,
- `viewport-volume-2`
+ `viewport-MPR-0`,
+ `viewport-MPR-1`,
+ `viewport-MPR-2`
].forEach((viewportId) => {
synchronizer.add({
renderingEngineId: this.renderingEngineId,
@@ -3341,7 +3625,8 @@ export default {
synchronizer.setEnabled(false);
},
setColorMap(rgbPresetName) {
- const fusionViewportIds = [`viewport-fusion-1`, `viewport-fusion-2`, `viewport-fusion-3`]
+ const fusionViewportIds = [`viewport-fusion-2`]
+ // const fusionViewportIds = [`viewport-fusion-1`, `viewport-fusion-2`, `viewport-fusion-3`]
fusionViewportIds.forEach(id => {
this.$refs[id][0].setPreset(rgbPresetName)
this.$refs[id][0].renderColorBar(rgbPresetName)
@@ -3358,7 +3643,7 @@ export default {
return new Promise(async (resolve, reject) => {
if (this.isMPR) {
if (!data) return resolve(false)
- let viewportSeries = this.$refs[`viewport-volume-0`][0].series
+ let viewportSeries = this.$refs[`viewport-MPR-0`][0].series
if (data && viewportSeries.SeriesInstanceUid === data.SeriesInstanceUid) return resolve(true)
}
if (!data) {
@@ -3376,8 +3661,8 @@ export default {
if (!res) return false
}
this.isMPR = true
- this.rows = 3
- this.cols = 1
+ // this.rows = 3
+ // this.cols = 1
this.loading = true
this.loadingText = this.$t('trials:lugano:message:loadVolumes')
if (!data) this.activeViewportIndex = 0
@@ -3386,9 +3671,9 @@ export default {
await this.getVolume(series)
this.loading = false
this.loadingText = null
- this.$refs[`viewport-volume-0`][0].setSeriesInfo(Object.assign({ orientation: 'AXIAL', isLocation: data && this.activeViewportIndex === 0 }, series))
- this.$refs[`viewport-volume-1`][0].setSeriesInfo(Object.assign({ orientation: 'SAGITTAL', isLocation: data && this.activeViewportIndex === 1 }, series))
- this.$refs[`viewport-volume-2`][0].setSeriesInfo(Object.assign({ orientation: 'CORONAL', isLocation: data && this.activeViewportIndex === 2 }, series))
+ this.$refs[`viewport-MPR-0`][0].setSeriesInfo(Object.assign({ orientation: 'AXIAL', isLocation: data && this.activeViewportIndex === 0 }, series))
+ this.$refs[`viewport-MPR-1`][0].setSeriesInfo(Object.assign({ orientation: 'SAGITTAL', isLocation: data && this.activeViewportIndex === 1 }, series))
+ this.$refs[`viewport-MPR-2`][0].setSeriesInfo(Object.assign({ orientation: 'CORONAL', isLocation: data && this.activeViewportIndex === 2 }, series))
resolve(false)
})
@@ -3403,6 +3688,7 @@ export default {
this.$refs[`ecrf_${this.taskInfo.VisitTaskId}`][0].removeAllNoSaveAnnotation()
}
const { ct, pt } = data
+ this.fusionOverlayModality = pt?.Modality || null
if (ct.ImageIds.length > 400) {
let res = await this.getSystemInfo()
if (!res) return false
@@ -3451,12 +3737,26 @@ export default {
this.$refs[`viewport-3`][0].setSeriesInfo(pt)
this.$refs[`viewport-fusion-0`][0].setSeriesInfo(ctData)
- this.$refs[`viewport-fusion-1`][0].setSeriesInfo(ptData, false, { colorMap: true })
+ this.$refs[`viewport-fusion-1`][0].setSeriesInfo(ptData, false, { colorMap: false })
this.$refs[`viewport-fusion-2`][0].setSeriesInfo(fusionData, false, { isFusion: true, colorMap: true })
- this.$refs[`viewport-fusion-3`][0].setSeriesInfo(ptData, false, { isMip: true, colorMap: true })
+ this.$refs[`viewport-fusion-3`][0].setSeriesInfo(ptData, false, { isMip: true, colorMap: false })
// this.resetAnnotation = false
this.$nextTick(() => {
this.$refs[`colorMap`].init()
+ if (this.fusionOverlayModality === 'NM') {
+ const imageIds = this.sortImageIdsByImagePositionPatient(pt.ImageIds)
+ const imageId = imageIds?.[0]
+ const voiLutModule = imageId ? metaData.get('voiLutModule', imageId) : null
+ const rawWidth = Array.isArray(voiLutModule?.windowWidth) ? voiLutModule.windowWidth[0] : voiLutModule?.windowWidth
+ const nmMax = Number(rawWidth)
+ if (Number.isFinite(nmMax) && nmMax > 0) {
+ const halfMax = Math.round(nmMax * 0.5)
+ this.$refs.colorMap.range = Math.round(nmMax)
+ this.$refs.colorMap.upper = halfMax
+ this.$refs.colorMap.upperRangeChange(Math.round(nmMax))
+ this.voiChange(halfMax)
+ }
+ }
})
} catch (err) {
console.log(err)
@@ -3477,22 +3777,75 @@ export default {
if (cache.getVolume(volumeId)) {
volume = cache.getVolume(volumeId)
} else {
- await this.$refs[`${this.viewportKey}-0`][0].createImageIdsAndCacheMetaData(serie)
- volume = await volumeLoader.createAndCacheVolume(volumeId, { imageIds: serie.ImageIds })
+ let imageIds = await this.$refs[`${this.viewportKey}-0`][0].createImageIdsAndCacheMetaData(serie)
+ // imageIds = this.sortImageIdsByImagePositionPatient(imageIds)
+ volume = await volumeLoader.createAndCacheVolume(volumeId, { imageIds: imageIds })
volume.load()
}
res({ volumeId, volume })
})
},
+ sortImageIdsByImagePositionPatient(imageIds) {
+ if (!Array.isArray(imageIds) || imageIds.length < 2) {
+ return imageIds;
+ }
+
+ const firstPlane = metaData.get('imagePlaneModule', imageIds[0]);
+ if (!firstPlane?.imagePositionPatient || !firstPlane?.imageOrientationPatient) {
+ return imageIds;
+ }
+
+ const reference = firstPlane.imagePositionPatient.map((v) => Number(v));
+ const iop = firstPlane.imageOrientationPatient.map((v) => Number(v));
+ const row = [iop[0], iop[1], iop[2]];
+ const col = [iop[3], iop[4], iop[5]];
+ const normal = [
+ row[1] * col[2] - row[2] * col[1],
+ row[2] * col[0] - row[0] * col[2],
+ row[0] * col[1] - row[1] * col[0],
+ ];
+
+ const pairs = [];
+ for (const imageId of imageIds) {
+ const plane = metaData.get('imagePlaneModule', imageId);
+ const ipp = plane?.imagePositionPatient;
+ if (!ipp) {
+ return imageIds;
+ }
+ const pos = ipp.map((v) => Number(v));
+ const positionVector = [
+ reference[0] - pos[0],
+ reference[1] - pos[1],
+ reference[2] - pos[2],
+ ];
+ const distance =
+ positionVector[0] * normal[0] +
+ positionVector[1] * normal[1] +
+ positionVector[2] * normal[2];
+ pairs.push({ imageId, distance });
+ }
+
+ pairs.sort((a, b) => b.distance - a.distance);
+ return pairs.map((p) => p.imageId);
+ },
upperRangeChange(upper) {
- this.$refs.colorMap.upper = upper
- this.$refs.colorMap.upperRangeChange(upper)
+ if (!this.hasFusionUpperInitialized) {
+ if (!upper) return
+ this.hasFusionUpperInitialized = true
+ }
+ if (this.lastUpper === upper) return;
+ this.lastUpper = upper;
+ if (this.$refs.colorMap) {
+ this.$refs.colorMap.changeVoi(upper)
+ }
+ this.voiChange(upper)
},
openFusion() {
this.fusionVisible = true
},
closeFusion() {
this.fusionVisible = false
+ this.fusionOverlayModality = null
},
getTrialCriterion() {
getCriterionReadingInfo({
@@ -3567,6 +3920,30 @@ export default {
})
},
+ async getSystemInfoReading() {
+ return new Promise(async resolve => {
+ let whitelisting = localStorage.getItem('whitelisting') ? JSON.parse(localStorage.getItem('whitelisting')) : []
+ let user = md5(sessionStorage.getItem('identityUserId'))
+ let r = whitelisting.some(item => item === user)
+ if (r) return resolve(true)
+ const systemInfo = new SystemInfo();
+ const allInfo = systemInfo.getAllInfo();
+ let deviceMemory = allInfo.hardware.deviceMemory; // 设备内存
+ let { width, height } = allInfo.screen; // 分辨率
+ // let discrete = allInfo.webgl.gpuType.discrete; // 是否独立显卡
+ // let estimatedMemory = allInfo.webgl.memoryInfo.estimatedMemory; // 显卡内存
+ // parseFloat(deviceMemory) < 16 ||
+ if (width < 1920 || height < 1080) {
+ let res = await this.$confirm(this.$t('browser:tip:Configuration'))
+ whitelisting.push(user)
+ localStorage.setItem('whitelisting', JSON.stringify(whitelisting))
+ resolve(res)
+ } else {
+ resolve(true)
+ }
+ })
+
+ },
},
beforeDestroy() {
DicomEvent.$off('isCanActiveNoneDicomTool')
@@ -3576,11 +3953,29 @@ export default {
clearTimeout(this.saveCustomAnnotationTimer)
this.saveCustomAnnotationTimer = null
}
+ if (this.timer) {
+ clearInterval(this.timer)
+ this.timer = null
+ }
+ if (this.FullTimerOut) {
+ clearTimeout(this.FullTimerOut)
+ this.FullTimerOut = null
+ this.isDelay = false
+ }
workSpeedclose(true)
}
}
+
+
diff --git a/src/views/trials/trials-panel/reading/dicoms3D/components/Recist/QuestionList.vue b/src/views/trials/trials-panel/reading/dicoms3D/components/Recist/QuestionList.vue
index ce914f67..28adf62b 100644
--- a/src/views/trials/trials-panel/reading/dicoms3D/components/Recist/QuestionList.vue
+++ b/src/views/trials/trials-panel/reading/dicoms3D/components/Recist/QuestionList.vue
@@ -1986,12 +1986,12 @@ export default {
.merge-table {
padding: 0 10px;
- ::v-deep.el-table {
+ ::v-deep .el-table {
background-color: #1e1e1e !important;
color: #383838;
}
- ::v-deep.el-table td.el-table__cell,
+ ::v-deep .el-table td.el-table__cell,
.el-table th.el-table__cell.is-leaf {
border-bottom: 1px solid #383838;
}
@@ -2002,7 +2002,7 @@ export default {
background-color: #1e1e1e;
}
- ::v-deep.el-table__header-wrapper {
+ ::v-deep .el-table__header-wrapper {
th {
background-color: #1e1e1e !important;
color: #dfdfdf;
@@ -2010,7 +2010,7 @@ export default {
}
}
- ::v-deep.el-table__body-wrapper {
+ ::v-deep .el-table__body-wrapper {
tr {
background-color: #1e1e1e !important;
color: #dfdfdf;
@@ -2021,7 +2021,7 @@ export default {
}
}
- ::v-deep.el-table__empty-block {
+ ::v-deep .el-table__empty-block {
background-color: #1e1e1e !important;
}
diff --git a/src/views/trials/trials-panel/reading/dicoms3D/components/SegmentForm.vue b/src/views/trials/trials-panel/reading/dicoms3D/components/SegmentForm.vue
new file mode 100644
index 00000000..0d6e3b38
--- /dev/null
+++ b/src/views/trials/trials-panel/reading/dicoms3D/components/SegmentForm.vue
@@ -0,0 +1,259 @@
+
+
+
+
+ handleChange(e, 'study')">
+
+
+
+
+
+ handleChange(e, 'series')">
+
+
+
+
+
+ handleChange(e, 'segmentGroup')">
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('common:button:cancel') }}
+
+
+ {{ $t('common:button:confirm') }}
+
+
+
+
+
diff --git a/src/views/trials/trials-panel/reading/dicoms3D/components/Segmentations.vue b/src/views/trials/trials-panel/reading/dicoms3D/components/Segmentations.vue
new file mode 100644
index 00000000..822566fa
--- /dev/null
+++ b/src/views/trials/trials-panel/reading/dicoms3D/components/Segmentations.vue
@@ -0,0 +1,2204 @@
+
+
+
+
+
+ {{ $t('trials:reading:Segmentations:title:tools') }}
+
+
+
+
+ {{ $t('trials:reading:Segmentations:title:EraserConfig') }}
+
+
+
+ {{ $t('trials:reading:Segmentations:title:thresholdType') }}
+ setToolActive(key)">
+
+ {{ $t(`trials:reading:Segmentations:radioButton:${tool}`) }}
+
+
+
+
+ {{ $t('trials:reading:Segmentations:title:isDynamic') }}
+
+
+ {{ $t('trials:reading:Segmentations:radioButton:Dynamic') }}
+
+
+ {{ $t('trials:reading:Segmentations:radioButton:Rang') }}
+
+
+
+
+
{{ $t('trials:reading:Segmentations:title:range') }}
+
+
+ --
+
+
+
+
+
+
+
+
+ {{ $t('trials:reading:Segmentations:title:Segment') }}
+
+
+
+
+
+ {{ $t('trials:reading:Segmentations:button:addSegmention') }}
+
+
+
+
+
{{ $t('trials:reading:Segmentations:title:Show:Fill&Outline') }}
+
+
+
+ {{ $t('trials:reading:Segmentations:title:Opacity') }}
+
+
+
+ {{ $t('trials:reading:Segmentations:title:Border') }}
+
+
+
+
+
+
+ {{
+ $t('trials:reading:Segmentations:title:InactiveSegmentationsShow')
+ }}
+
+
+
+
+
+
+
+
+
+ {{ $t('trials:reading:Segmentations:button:addSegmentGroup') }}
+
+
+ {{ $t('trials:reading:Segmentations:button:renameSegmentGroup') }}
+
+
+ {{ $t('trials:reading:Segmentations:button:exportSegmentGroup') }}
+
+
+ {{ $t('trials:reading:Segmentations:button:deleteSegmentGroup') }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("trials:reading:Segmentations:button:save") }}
+
+
+
+
+
+ {{ $t('trials:reading:Segmentations:button:addSegment') }}
+
+ i.segmentationId === segmentationId))" />
+
+
+
+
+
+
+
+ {{ $t('trials:reading:Segmentations:button:getBidirectional') }}
+
+
+
+ L:{{ Number(item.bidirectional.maxMajor).toFixed(2) }} mm
+ S:{{ Number(item.bidirectional.maxMinor).toFixed(2) }} mm
+
+
+
+
+
+
+
+
+
+ {{ k }}
+ {{ Number(item.stats[k].value).toFixed(2)
+ }}{{ item.stats[k].unit }}
+
+
+ {{ index + 1 }}
+
+
+
changeColor(e, item)">
+
{{ item.SegmentLabel }}
+
+
+
+
+
+
+
+ {{ $t('trials:reading:Segmentations:button:renameSegmentGroup') }}
+
+
+ {{ $t('trials:reading:Segmentations:button:deleteSegmentGroup') }}
+
+
+ {{ $t('trials:reading:Segmentations:button:lockSegment') }}
+
+
+ {{ $t('trials:reading:Segmentations:button:unlockSegment') }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t("trials:reading:Segmentations:button:saveAll") }}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/views/trials/trials-panel/reading/dicoms3D/components/StudyList.vue b/src/views/trials/trials-panel/reading/dicoms3D/components/StudyList.vue
index 1f14339d..e0f779a1 100644
--- a/src/views/trials/trials-panel/reading/dicoms3D/components/StudyList.vue
+++ b/src/views/trials/trials-panel/reading/dicoms3D/components/StudyList.vue
@@ -1,16 +1,10 @@
-
+
{{ taskInfo.SubjectCode }}
-
+
{{ visitTaskInfo.TaskBlindName }}
@@ -18,15 +12,14 @@
-