From 8bdac9bd6e3c3c1b91c921192ff23d55f6fb76e0 Mon Sep 17 00:00:00 2001 From: caiyiling <1321909229@qq.com> Date: Fri, 7 Aug 2026 16:02:57 +0800 Subject: [PATCH] =?UTF-8?q?=E9=98=85=E7=89=87=E5=B7=A5=E5=85=B7=E6=9B=B4?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reading/dicoms3D/components/ReadPage.vue | 45 ++++++++++++++++- .../reading/dicoms3D/components/Viewport.vue | 35 ++++++++++++- .../dicoms3D/components/VolumeViewport.vue | 50 ++++++++++++------- 3 files changed, 107 insertions(+), 23 deletions(-) diff --git a/src/views/trials/trials-panel/reading/dicoms3D/components/ReadPage.vue b/src/views/trials/trials-panel/reading/dicoms3D/components/ReadPage.vue index 96a8f1e8..9240870a 100644 --- a/src/views/trials/trials-panel/reading/dicoms3D/components/ReadPage.vue +++ b/src/views/trials/trials-panel/reading/dicoms3D/components/ReadPage.vue @@ -160,6 +160,17 @@ + +
+ + +
+ +
+ +
@@ -381,13 +392,16 @@ :rendering-engine-id="renderingEngineId" :viewport-id="`viewport-${index}`" :viewport-index="index" :histogramVisible="histogramVisible" :actionConfiguration="actionConfiguration" :SegmentConfig="SegmentConfig" :segmentationId.sync="segId" :segmentIndex.sync="segIndex" - :curSegSeries.sync="curSegSeries" @activeViewport="activeViewport" + :curSegSeries.sync="curSegSeries" :is-scroll-sync="isScrollSync" + @activeViewport="activeViewport" @toggleTaskByViewport="toggleTaskByViewport" @previewCD="previewCD" @renderAnnotations="renderAnnotations" @contentMouseup="contentMouseup" - @resetViewport="resetViewport" @resetHistogram="resetHistogram" v-if="readingTool === 3" + @resetViewport="resetViewport" @resetHistogram="resetHistogram" + @scrollSync="handleViewportScrollSync" v-if="readingTool === 3" v-resize="(e) => handleSizeChange(e, `viewport-${index}`)" />
@@ -799,6 +813,7 @@ export default { activeViewportWC: null, clipPlaying: false, fps: 15, + isScrollSync: false, manualsDialog: { visible: false, isFullscreen: false, justKeyDoc: false }, hotKeyList: [], forceFitToWindow: false, @@ -3415,8 +3430,34 @@ export default { scrollPage(type) { // if (this.histogramVisible) return false this.clipPlaying = false + if (this.isScrollSync && !this.isFusion && !this.isMPR && this.cells.length > 1) { + for (let i = 0; i < this.cells.length; i++) { + const viewportRef = this.$refs[`${this.viewportKey}-${i}`] + if (viewportRef && viewportRef[0] && typeof viewportRef[0].scrollPage === 'function') { + viewportRef[0].scrollPage(type) + } + } + return + } this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].scrollPage(type) }, + setImageIndexSync() { + this.isScrollSync = !this.isScrollSync + }, + handleViewportScrollSync({ viewportIndex, type, direction }) { + if (!this.isScrollSync || this.isFusion || this.isMPR || this.cells.length <= 1) return + const syncType = type ?? direction + if (viewportIndex !== this.activeViewportIndex || ![-1, 1, 0, 99999].includes(syncType)) return + + this.clipPlaying = false + for (let i = 0; i < this.cells.length; i++) { + if (i === viewportIndex) continue + const viewportRef = this.$refs[`${this.viewportKey}-${i}`] + if (viewportRef && viewportRef[0] && typeof viewportRef[0].scrollPage === 'function') { + viewportRef[0].scrollPage(syncType) + } + } + }, // 播放 toggleClipPlay(isPlay) { // if (this.histogramVisible) return false diff --git a/src/views/trials/trials-panel/reading/dicoms3D/components/Viewport.vue b/src/views/trials/trials-panel/reading/dicoms3D/components/Viewport.vue index 06805756..14764a7a 100644 --- a/src/views/trials/trials-panel/reading/dicoms3D/components/Viewport.vue +++ b/src/views/trials/trials-panel/reading/dicoms3D/components/Viewport.vue @@ -117,6 +117,10 @@ export default { viewportIndex: { type: Number, required: true + }, + isScrollSync: { + type: Boolean, + default: false } }, data() { @@ -191,6 +195,7 @@ export default { this.element.oncontextmenu = (e) => e.preventDefault() this.resizeObserver.observe(this.element) this.element.addEventListener('CORNERSTONE_STACK_NEW_IMAGE', this.stackNewImage) + this.element.addEventListener('wheel', this.handleWheelSync) // this.element.addEventListener('CORNERSTONE_VOI_MODIFIED', this.voiModified) // this.element.addEventListener('CORNERSTONE_CAMERA_MODIFIED', this.cameraModified) @@ -374,6 +379,32 @@ export default { toolGroup.setToolEnabled('ScaleOverlay') } }, + handleWheelSync(e) { + if (!this.isScrollSync) return + + const deltaY = typeof e.deltaY === 'number' ? e.deltaY : -e.wheelDeltaY + if (!deltaY) return + + const renderingEngine = getRenderingEngine(this.renderingEngineId) + const viewport = renderingEngine && renderingEngine.getViewport(this.viewportId) + if (!viewport) return + + const currentImageIdIndex = viewport.getCurrentImageIdIndex() + const numImages = viewport.getImageIds().length + if (numImages <= 1) return + const direction = deltaY > 0 ? 1 : -1 + let type = direction + + if (direction === 1 && currentImageIdIndex >= numImages - 1) { + type = 0 + csUtils.jumpToSlice(viewport.element, { imageIndex: 0 }) + } else if (direction === -1 && currentImageIdIndex <= 0) { + type = 99999 + csUtils.jumpToSlice(viewport.element, { imageIndex: numImages - 1 }) + } + + this.$emit('scrollSync', { viewportIndex: this.viewportIndex, type }) + }, scrollPage(type) { const renderingEngine = getRenderingEngine(this.renderingEngineId) const viewport = renderingEngine.getViewport(this.viewportId) @@ -383,9 +414,9 @@ export default { if (type === 0) { newImageIdIndex = 0 } else if (type === -1) { - newImageIdIndex = currentImageIdIndex === 0 ? currentImageIdIndex : currentImageIdIndex - 1 + newImageIdIndex = currentImageIdIndex === 0 ? numImages - 1 : currentImageIdIndex - 1 } else if (type === 1) { - newImageIdIndex = currentImageIdIndex === numImages - 1 ? currentImageIdIndex : currentImageIdIndex + 1 + newImageIdIndex = currentImageIdIndex === numImages - 1 ? 0 : currentImageIdIndex + 1 } else if (type === 99999) { newImageIdIndex = numImages - 1 } diff --git a/src/views/trials/trials-panel/reading/dicoms3D/components/VolumeViewport.vue b/src/views/trials/trials-panel/reading/dicoms3D/components/VolumeViewport.vue index da5619f2..5bf1bfb8 100644 --- a/src/views/trials/trials-panel/reading/dicoms3D/components/VolumeViewport.vue +++ b/src/views/trials/trials-panel/reading/dicoms3D/components/VolumeViewport.vue @@ -130,6 +130,10 @@ export default { type: Number, required: true }, + isScrollSync: { + type: Boolean, + default: false + }, histogramVisible: { type: Boolean, default: false @@ -304,23 +308,7 @@ export default { this.element.addEventListener("CORNERSTONE_VOLUME_NEW_IMAGE", this.stackNewImage) this.element.addEventListener('CORNERSTONE_VOI_MODIFIED', this.voiModified) this.element.addEventListener('CORNERSTONE_IMAGE_RENDERED', this.imageRendered) - this.element.addEventListener('wheel', (e) => { - // if (this.histogramVisible) return false - console.log('CORNERSTONE_STACK_VIEWPORT_SCROLL') - const renderingEngine = getRenderingEngine(this.renderingEngineId) - const viewport = renderingEngine.getViewport(this.viewportId) - const currentImageIdIndex = viewport.getCurrentImageIdIndex() - const totalImages = this.imageInfo.total; - - // 手动实现循环逻辑 - if (currentImageIdIndex >= totalImages - 1 && e.wheelDeltaY < 0) { - // 滚动到最后一张时跳回第一张 - csUtils.jumpToSlice(viewport.element, { imageIndex: 0 }); - } else if (currentImageIdIndex <= 0 && e.wheelDeltaY > 0) { - // 滚动到第一张时跳回最后一张 - csUtils.jumpToSlice(viewport.element, { imageIndex: totalImages - 1 }); - } - }); + this.element.addEventListener('wheel', this.handleWheelSync) this.element.addEventListener('CORNERSTONE_TOOLS_MOUSE_MOVE', this.cornerstoneToolsMouseMove) // this.element.addEventListener(cornerstoneTools.Enums.Events.MOUSE_WHEEL, this.handletoolsMouseWheel) this.element.addEventListener('mouseleave', () => { @@ -390,6 +378,30 @@ export default { setFilp(f = false) { this.isFlip = f }, + handleWheelSync(e) { + const deltaY = typeof e.deltaY === 'number' ? e.deltaY : -e.wheelDeltaY + if (!deltaY) return + + const renderingEngine = getRenderingEngine(this.renderingEngineId) + const viewport = renderingEngine.getViewport(this.viewportId) + const currentImageIdIndex = viewport.getCurrentImageIdIndex() + const totalImages = this.imageInfo.total + if (totalImages <= 1) return + const direction = deltaY > 0 ? 1 : -1 + let type = direction + + if (currentImageIdIndex >= totalImages - 1 && direction === 1) { + type = 0 + csUtils.jumpToSlice(viewport.element, { imageIndex: 0 }) + } else if (currentImageIdIndex <= 0 && direction === -1) { + type = 99999 + csUtils.jumpToSlice(viewport.element, { imageIndex: totalImages - 1 }) + } + + if (this.isScrollSync) { + this.$emit('scrollSync', { viewportIndex: this.viewportIndex, type }) + } + }, stackNewImage(e) { if (this.isFlip) return this.isFlip = false const { detail } = e @@ -573,9 +585,9 @@ export default { if (type === 0) { newImageIdIndex = 0 } else if (type === -1) { - newImageIdIndex = currentImageIdIndex === 0 ? currentImageIdIndex : currentImageIdIndex - 1 + newImageIdIndex = currentImageIdIndex === 0 ? numImages - 1 : currentImageIdIndex - 1 } else if (type === 1) { - newImageIdIndex = currentImageIdIndex === numImages - 1 ? currentImageIdIndex : currentImageIdIndex + 1 + newImageIdIndex = currentImageIdIndex === numImages - 1 ? 0 : currentImageIdIndex + 1 } else if (type === 99999) { newImageIdIndex = numImages - 1 }