注释(未完成)

uat_us
wangxiaoshuang 2026-04-22 15:06:39 +08:00
parent 06959b4465
commit f7afca6e73
3 changed files with 461 additions and 184 deletions

View File

@ -1,7 +1,15 @@
<template> <template>
<div id="canvas" ref="canvas" v-loading="loading" :element-loading-text="NSTip" <div
element-loading-background="rgba(0, 0, 0, 0.8)" style="width:100%;height:100%;position:relative;" id="canvas"
class="cornerstone-element" @contextmenu.prevent="onContextmenu" @mouseup="sliderMouseup"> ref="canvas"
v-loading="loading"
:element-loading-text="NSTip"
element-loading-background="rgba(0, 0, 0, 0.8)"
style="width:100%;height:100%;position:relative;"
class="cornerstone-element"
@contextmenu.prevent="onContextmenu"
@mouseup="sliderMouseup"
>
<div v-show="dicomInfo.series" class="info-series"> <div v-show="dicomInfo.series" class="info-series">
<div>Series #{{ dicomInfo.series }}</div> <div>Series #{{ dicomInfo.series }}</div>
<div>Image #{{ dicomInfo.frame }}</div> <div>Image #{{ dicomInfo.frame }}</div>
@ -15,25 +23,20 @@
<div v-show="dicomInfo.thick">Slice Thickness {{ dicomInfo.thick }}mm</div> <div v-show="dicomInfo.thick">Slice Thickness {{ dicomInfo.thick }}mm</div>
<div>WW/WC {{ dicomInfo.wwwc }}</div> <div>WW/WC {{ dicomInfo.wwwc }}</div>
<div>Zoom {{ dicomInfo.zoom }}</div> <div>Zoom {{ dicomInfo.zoom }}</div>
<div v-show="dicomInfo.location">Location {{ dicomInfo.location }}mm</div> --> <div v-show="dicomInfo.location">Location {{ dicomInfo.location }}mm</div>-->
<!-- <div v-show="toolState.clipPlaying">FPS {{ dicomInfo.fps }}</div> --> <!-- <div v-show="toolState.clipPlaying">FPS {{ dicomInfo.fps }}</div> -->
<div v-show="mousePosition.mo"> <div v-show="mousePosition.mo">
Pos: {{ mousePosition.x ? mousePosition.x.toFixed(0) : '' }}, {{ mousePosition.y ? mousePosition.y.toFixed(0) : Pos: {{ mousePosition.x ? mousePosition.x.toFixed(0) : '' }}, {{ mousePosition.y ? mousePosition.y.toFixed(0) :
'' }} '' }}
</div> </div>
<div <div
v-if="(dicomInfo.modality === 'CT' || dicomInfo.modality === 'DR' || dicomInfo.modality === 'CR') && mousePosition.mo"> v-if="(dicomInfo.modality === 'CT' || dicomInfo.modality === 'DR' || dicomInfo.modality === 'CR') && mousePosition.mo"
HU: {{ mousePosition.mo }} >HU: {{ mousePosition.mo }}</div>
</div> <div
<div v-else-if="(dicomInfo.modality === 'PT' && mousePosition.suv)"> v-else-if="(dicomInfo.modality === 'PT' && mousePosition.suv)"
SUVbw(g/ml): {{ mousePosition.suv.toFixed(3) }} >SUVbw(g/ml): {{ mousePosition.suv.toFixed(3) }}</div>
</div> <div v-else-if="mousePosition.mo">Density: {{ mousePosition.mo }}</div>
<div v-else-if="mousePosition.mo"> <div>W*H: {{ dicomInfo.size }}</div>
Density: {{ mousePosition.mo }}
</div>
<div>
W*H: {{ dicomInfo.size }}
</div>
<div>Zoom: {{ dicomInfo.zoom }}</div> <div>Zoom: {{ dicomInfo.zoom }}</div>
</div> </div>
@ -49,31 +52,28 @@
<!-- <div v-show="dicomInfo.acc">ACC {{ dicomInfo.acc }}</div> --> <!-- <div v-show="dicomInfo.acc">ACC {{ dicomInfo.acc }}</div> -->
<!-- <div>{{ dicomInfo.time }}</div> --> <!-- <div>{{ dicomInfo.time }}</div> -->
</div> </div>
<div ref="sliderBox" class="my_slider_box" <div
ref="sliderBox"
class="my_slider_box"
style="position: absolute;right: 1px;height: calc(100% - 100px);transform: translateY(-50%);top: calc(50% - 30px);width: 10px;background: #333;cursor: pointer" style="position: absolute;right: 1px;height: calc(100% - 100px);transform: translateY(-50%);top: calc(50% - 30px);width: 10px;background: #333;cursor: pointer"
@click.stop="goViewer($event)"> @click.stop="goViewer($event)"
<div :style="{ top: height + '%' }" >
<div
:style="{ top: height + '%' }"
style="z-index:10;background: #9e9e9e;height: 20px;width: 100%;position: absolute;top: 0;cursor: move" style="z-index:10;background: #9e9e9e;height: 20px;width: 100%;position: absolute;top: 0;cursor: move"
@mousedown="sliderMousedown($event)" /> @mousedown="sliderMousedown($event)"
</div> />
<div style="position: absolute;left: 50%;top: 15px;color: #f44336;">
{{ markers.top }}
</div>
<div style="position: absolute;top: 50%;right: 15px;color: #f44336;">
{{ markers.right }}
</div> </div>
<div style="position: absolute;left: 50%;top: 15px;color: #f44336;">{{ markers.top }}</div>
<div style="position: absolute;top: 50%;right: 15px;color: #f44336;">{{ markers.right }}</div>
<div style="position: absolute;left: 50%;bottom: 15px;color: #f44336;"> <div style="position: absolute;left: 50%;bottom: 15px;color: #f44336;">{{ markers.bottom }}</div>
{{ markers.bottom }} <div style="position: absolute;top: 50%;left: 15px;color: #f44336;">{{ markers.left }}</div>
</div>
<div style="position: absolute;top: 50%;left: 15px;color: #f44336;">
{{ markers.left }}
</div>
<div class="info-instance"> <div class="info-instance">
<!-- <div v-show="dicomInfo.pixel"> <!-- <div v-show="dicomInfo.pixel">
Pixel: {{ dicomInfo.pixel }}mm Pixel: {{ dicomInfo.pixel }}mm
</div> --> </div>-->
<div v-show="dicomInfo.location">Location: {{ dicomInfo.location }}</div> <div v-show="dicomInfo.location">Location: {{ dicomInfo.location }}</div>
<div v-show="dicomInfo.thick">Slice Thickness: {{ dicomInfo.thick }}mm</div> <div v-show="dicomInfo.thick">Slice Thickness: {{ dicomInfo.thick }}mm</div>
<div v-show="dicomInfo.wwwc">WW/WL: {{ dicomInfo.wwwc }}</div> <div v-show="dicomInfo.wwwc">WW/WL: {{ dicomInfo.wwwc }}</div>
@ -81,9 +81,19 @@
<!-- <div v-show="stack.firstImageLoading" class="load-indicator"> <!-- <div v-show="stack.firstImageLoading" class="load-indicator">
Loading Series #{{ stack.seriesNumber }}... Loading Series #{{ stack.seriesNumber }}...
</div>--> </div>-->
<el-dialog v-if="dcmTag.visible" :visible.sync="dcmTag.visible" :close-on-click-modal="false" :title="dcmTag.title" <el-dialog
width="1000px" custom-class="base-dialog-wrapper" append-to-body> v-if="dcmTag.visible"
<dicom-tags :image-id="stack.imageIds[stack.currentImageIdIndex]" @close="dcmTag.visible = false" /> :visible.sync="dcmTag.visible"
:close-on-click-modal="false"
:title="dcmTag.title"
width="1000px"
custom-class="base-dialog-wrapper"
append-to-body
>
<dicom-tags
:image-id="stack.imageIds[stack.currentImageIdIndex]"
@close="dcmTag.visible = false"
/>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
@ -101,6 +111,7 @@ import invertOrientationString from '@/views/trials/trials-panel/reading/dicoms/
import calculateSUV from '@/views/trials/trials-panel/reading/dicoms/tools/calculateSUV' import calculateSUV from '@/views/trials/trials-panel/reading/dicoms/tools/calculateSUV'
// import requestPoolManager from '@/utils/request-pool' // import requestPoolManager from '@/utils/request-pool'
import ScaleOverlayTool from '@/views/trials/trials-panel/reading/dicoms/tools/ScaleOverlay/ScaleOverlayTool' import ScaleOverlayTool from '@/views/trials/trials-panel/reading/dicoms/tools/ScaleOverlay/ScaleOverlayTool'
import Note_RectangleRoiTool from '@/views/trials/trials-panel/reading/dicoms/tools/RectangleRoi/Note_RectangleRoiTool'
cornerstoneTools.external.cornerstone = cornerstone cornerstoneTools.external.cornerstone = cornerstone
cornerstoneTools.external.Hammer = Hammer cornerstoneTools.external.Hammer = Hammer
cornerstoneTools.external.cornerstoneMath = cornerstoneMath cornerstoneTools.external.cornerstoneMath = cornerstoneMath
@ -117,7 +128,7 @@ export default {
computed: { computed: {
NSTip() { NSTip() {
return `${this.$store.state.trials.downloadSize}, NS: ${this.$store.state.trials.downloadTip}` return `${this.$store.state.trials.downloadSize}, NS: ${this.$store.state.trials.downloadTip}`
} },
}, },
data() { data() {
return { return {
@ -130,7 +141,7 @@ export default {
seriesNumber: '', seriesNumber: '',
imageIds: [], imageIds: [],
currentImageIdIndex: 0, currentImageIdIndex: 0,
firstImageLoading: false firstImageLoading: false,
// preventCache: true // preventCache: true
}, },
dicomInfo: { dicomInfo: {
@ -150,14 +161,14 @@ export default {
wwwc: '', wwwc: '',
zoom: 0, zoom: 0,
location: '', location: '',
fps: 5 fps: 5,
}, },
toolState: { toolState: {
initialized: false, initialized: false,
activeTool: 'none', activeTool: 'none',
dicomInfoVisible: false, dicomInfoVisible: false,
clipPlaying: false, clipPlaying: false,
viewportInvert: false viewportInvert: false,
}, },
loadImagePromise: null, loadImagePromise: null,
AnnotationSync: null, AnnotationSync: null,
@ -168,18 +179,20 @@ export default {
sliderInfo: { sliderInfo: {
oldB: null, oldB: null,
oldM: null, oldM: null,
isMove: false isMove: false,
}, },
mousePosition: { x: '', y: '', mo: '' }, mousePosition: { x: '', y: '', mo: '' },
markers: { top: '', right: '', bottom: '', left: '' }, markers: { top: '', right: '', bottom: '', left: '' },
orientationMarkers: [], orientationMarkers: [],
originalMarkers: [], originalMarkers: [],
dcmTag: { visible: false, title: this.$t('trials:dicom-tag:title') } dcmTag: { visible: false, title: this.$t('trials:dicom-tag:title') },
} }
}, },
mounted() { mounted() {
this.type = this.$router.currentRoute.query.type ? this.$router.currentRoute.query.type : '' this.type = this.$router.currentRoute.query.type
? this.$router.currentRoute.query.type
: ''
this.canvas = this.$refs.canvas this.canvas = this.$refs.canvas
this.canvas.addEventListener('cornerstonenewimage', this.onNewImage) this.canvas.addEventListener('cornerstonenewimage', this.onNewImage)
this.canvas.addEventListener( this.canvas.addEventListener(
@ -200,7 +213,10 @@ export default {
document.addEventListener('mousemove', (e) => { document.addEventListener('mousemove', (e) => {
this.sliderMousemove(e) this.sliderMousemove(e)
}) })
this.canvas.addEventListener('cornerstonetoolsstackscroll', this.stackScrollCallback) this.canvas.addEventListener(
'cornerstonetoolsstackscroll',
this.stackScrollCallback
)
}, },
methods: { methods: {
@ -210,7 +226,11 @@ export default {
this.stack.seriesId = dicomSeries.seriesId this.stack.seriesId = dicomSeries.seriesId
this.stack.seriesNumber = dicomSeries.seriesNumber this.stack.seriesNumber = dicomSeries.seriesNumber
this.stack.imageIds = dicomSeries.imageIds this.stack.imageIds = dicomSeries.imageIds
this.stack.currentImageIdIndex = dicomSeries.imageIdIndex && dicomSeries.imageIdIndex < dicomSeries.imageIds.length ? dicomSeries.imageIdIndex : 0 this.stack.currentImageIdIndex =
dicomSeries.imageIdIndex &&
dicomSeries.imageIdIndex < dicomSeries.imageIds.length
? dicomSeries.imageIdIndex
: 0
this.stack.firstImageLoading = true this.stack.firstImageLoading = true
this.stack.description = dicomSeries.description this.stack.description = dicomSeries.description
this.toolState.viewportInvert = false this.toolState.viewportInvert = false
@ -226,13 +246,17 @@ export default {
this.toolState.clipPlaying = false this.toolState.clipPlaying = false
this.loading = true this.loading = true
cornerstone.loadAndCacheImage(this.stack.imageIds[this.stack.currentImageIdIndex]) cornerstone
.then(image => { .loadAndCacheImage(
this.stack.imageIds[this.stack.currentImageIdIndex]
)
.then((image) => {
this.loading = false this.loading = false
if (this.stack.imageIds.indexOf(image.imageId) !== -1) { if (this.stack.imageIds.indexOf(image.imageId) !== -1) {
this.onFirstImageLoaded(image) this.onFirstImageLoaded(image)
} }
}).catch((error) => { })
.catch((error) => {
this.loading = false this.loading = false
if (error.error && error.error.message) { if (error.error && error.error.message) {
this.$alert(error.error.message) this.$alert(error.error.message)
@ -255,15 +279,17 @@ export default {
const apiTool = cornerstoneTools[`${toolName}Tool`] const apiTool = cornerstoneTools[`${toolName}Tool`]
if (apiTool) { if (apiTool) {
const toolAlreadyAddedToElement = cornerstoneTools.getToolForElement( const toolAlreadyAddedToElement =
element, cornerstoneTools.getToolForElement(element, apiTool)
apiTool
)
if (!toolAlreadyAddedToElement) { if (!toolAlreadyAddedToElement) {
if (toolName === 'RectangleRoi') { if (toolName === 'RectangleRoi') {
cornerstoneTools.addToolForElement(element, apiTool, { configuration: { showMinMax: true, showStatsText: true } }) cornerstoneTools.addToolForElement(element, apiTool, {
configuration: { showMinMax: true, showStatsText: true },
})
} else if (toolName === 'EllipticalRoi') { } else if (toolName === 'EllipticalRoi') {
cornerstoneTools.addToolForElement(element, apiTool, { configuration: { showMinMax: true } }) cornerstoneTools.addToolForElement(element, apiTool, {
configuration: { showMinMax: true },
})
} else { } else {
cornerstoneTools.addToolForElement(element, apiTool) cornerstoneTools.addToolForElement(element, apiTool)
} }
@ -287,8 +313,26 @@ export default {
false false
) )
}) })
if (!cornerstoneTools.getToolForElement(element, cornerstoneTools.WwwcRegionTool)) { if (
cornerstoneTools.addToolForElement(element, cornerstoneTools.WwwcRegionTool) !cornerstoneTools.getToolForElement(element, Note_RectangleRoiTool)
) {
console.log(
Note_RectangleRoiTool.name,
'Note_RectangleRoiTool is add'
)
console.log(element, 'element')
cornerstoneTools.addToolForElement(element, Note_RectangleRoiTool)
}
if (
!cornerstoneTools.getToolForElement(
element,
cornerstoneTools.WwwcRegionTool
)
) {
cornerstoneTools.addToolForElement(
element,
cornerstoneTools.WwwcRegionTool
)
} }
if ( if (
!cornerstoneTools.getToolForElement( !cornerstoneTools.getToolForElement(
@ -347,7 +391,9 @@ export default {
// var instanceId = image.imageId.split('/')[image.imageId.split('/').length - 1] // var instanceId = image.imageId.split('/')[image.imageId.split('/').length - 1]
// instanceId = instanceId.split('.')[0] // instanceId = instanceId.split('.')[0]
// this.stack.instanceId = instanceId // this.stack.instanceId = instanceId
this.height = (this.stack.currentImageIdIndex) * 100 / (this.stack.imageIds.length - 1) this.height =
(this.stack.currentImageIdIndex * 100) /
(this.stack.imageIds.length - 1)
this.resetWwwc() this.resetWwwc()
}, },
onNewImage(e) { onNewImage(e) {
@ -366,8 +412,9 @@ export default {
data.string('x00080030') data.string('x00080030')
) )
this.dicomInfo.series = data.string('x00200011') this.dicomInfo.series = data.string('x00200011')
this.dicomInfo.frame = `${this.stack.currentImageIdIndex + 1}/${this.stack.imageIds.length this.dicomInfo.frame = `${this.stack.currentImageIdIndex + 1}/${
}` this.stack.imageIds.length
}`
this.dicomInfo.size = `${data.uint16('x00280011')}x${data.uint16( this.dicomInfo.size = `${data.uint16('x00280011')}x${data.uint16(
'x00280010' 'x00280010'
)}` )}`
@ -379,21 +426,30 @@ export default {
if (this.dicomInfo.thick) { if (this.dicomInfo.thick) {
this.dicomInfo.thick = this.dicomInfo.thick.toFixed(2) this.dicomInfo.thick = this.dicomInfo.thick.toFixed(2)
} }
const newImageIdIndex = this.stack.imageIds.findIndex(i => i === e.detail.image.imageId) const newImageIdIndex = this.stack.imageIds.findIndex(
(i) => i === e.detail.image.imageId
)
if (newImageIdIndex === -1) return if (newImageIdIndex === -1) return
this.stack.currentImageIdIndex = newImageIdIndex this.stack.currentImageIdIndex = newImageIdIndex
this.stack.imageIdIndex = newImageIdIndex this.stack.imageIdIndex = newImageIdIndex
this.series.imageIdIndex = newImageIdIndex this.series.imageIdIndex = newImageIdIndex
this.height = (this.stack.currentImageIdIndex) * 100 / (this.stack.imageIds.length - 1) this.height =
(this.stack.currentImageIdIndex * 100) /
(this.stack.imageIds.length - 1)
this.resetWwwc() this.resetWwwc()
}, },
stackScrollCallback(e) { stackScrollCallback(e) {
const { detail } = e const { detail } = e
if (this.isScrollSync) { if (this.isScrollSync) {
this.$emit('scrollSync', { canvasIndex: this.canvasIndex, direction: detail.direction }) this.$emit('scrollSync', {
canvasIndex: this.canvasIndex,
direction: detail.direction,
})
} }
this.stack.currentImageIdIndex = e.detail.newImageIdIndex this.stack.currentImageIdIndex = e.detail.newImageIdIndex
this.height = (this.stack.currentImageIdIndex) * 100 / (this.stack.imageIds.length - 1) this.height =
(this.stack.currentImageIdIndex * 100) /
(this.stack.imageIds.length - 1)
// var priority = new Date(new Date().setHours(23, 59, 59, 999)).getTime() // var priority = new Date(new Date().setHours(23, 59, 59, 999)).getTime()
// requestPoolManager.loadAndCacheImagePlus(this.stack.imageIds[this.stack.currentImageIdIndex], this.stack.seriesId, priority) // requestPoolManager.loadAndCacheImagePlus(this.stack.imageIds[this.stack.currentImageIdIndex], this.stack.seriesId, priority)
@ -408,7 +464,9 @@ export default {
if (date) { if (date) {
date = `${date.substr(0, 4)}-${date.substr(4, 2)}-${date.substr(6, 2)}` date = `${date.substr(0, 4)}-${date.substr(4, 2)}-${date.substr(6, 2)}`
} }
if (time) { time = `${time.substr(0, 2)}:${time.substr(2, 2)}:${time.substr(4, 2)}` } if (time) {
time = `${time.substr(0, 2)}:${time.substr(2, 2)}:${time.substr(4, 2)}`
}
return time ? `${date} ${time}` : `${date} 00:00:00` return time ? `${date} ${time}` : `${date} 00:00:00`
}, },
@ -453,7 +511,7 @@ export default {
top: oppositeColumn, top: oppositeColumn,
bottom: column, bottom: column,
left: oppositeRow, left: oppositeRow,
right: row right: row,
} }
if (!markers) { if (!markers) {
return return
@ -500,13 +558,7 @@ export default {
if (image.color) { if (image.color) {
stats.storedPixels = this.getRGBPixels(element, x, y, 1, 1) stats.storedPixels = this.getRGBPixels(element, x, y, 1, 1)
} else { } else {
stats.storedPixels = cornerstone.getStoredPixels( stats.storedPixels = cornerstone.getStoredPixels(element, x, y, 1, 1)
element,
x,
y,
1,
1
)
stats.sp = stats.storedPixels[0] stats.sp = stats.storedPixels[0]
stats.mo = stats.sp * image.slope + image.intercept stats.mo = stats.sp * image.slope + image.intercept
stats.suv = calculateSUV(image, stats.sp) stats.suv = calculateSUV(image, stats.sp)
@ -533,7 +585,8 @@ export default {
if (enabledElement.image.color) { if (enabledElement.image.color) {
for (row = 0; row < height; row++) { for (row = 0; row < height; row++) {
for (column = 0; column < width; column++) { for (column = 0; column < width; column++) {
spIndex = ((row + y) * enabledElement.image.columns + (column + x)) * 4 spIndex =
((row + y) * enabledElement.image.columns + (column + x)) * 4
const red = pixelData[spIndex] const red = pixelData[spIndex]
const green = pixelData[spIndex + 1] const green = pixelData[spIndex + 1]
const blue = pixelData[spIndex + 2] const blue = pixelData[spIndex + 2]
@ -551,7 +604,8 @@ export default {
}, },
sliderMousedown(e) { sliderMousedown(e) {
var boxHeight = this.$refs['sliderBox'].clientHeight var boxHeight = this.$refs['sliderBox'].clientHeight
this.sliderInfo.oldB = parseInt(e.srcElement.style.top) * boxHeight / 100 this.sliderInfo.oldB =
(parseInt(e.srcElement.style.top) * boxHeight) / 100
this.sliderInfo.oldM = e.clientY this.sliderInfo.oldM = e.clientY
this.sliderInfo.isMove = true this.sliderInfo.isMove = true
e.stopImmediatePropagation() e.stopImmediatePropagation()
@ -564,9 +618,14 @@ export default {
var boxHeight = this.$refs['sliderBox'].clientHeight var boxHeight = this.$refs['sliderBox'].clientHeight
if (PX < 0) return if (PX < 0) return
if (PX > boxHeight) return if (PX > boxHeight) return
var height = PX * 100 / boxHeight var height = (PX * 100) / boxHeight
var index = Math.trunc(this.stack.imageIds.length * this.height / 100) var index = Math.trunc((this.stack.imageIds.length * this.height) / 100)
index = index > this.stack.imageIds.length ? this.stack.imageIds.length : index < 0 ? 0 : index index =
index > this.stack.imageIds.length
? this.stack.imageIds.length
: index < 0
? 0
: index
// if (!cornerstone.imageCache.getImageLoadObject(this.stack.imageIds[index])) return // if (!cornerstone.imageCache.getImageLoadObject(this.stack.imageIds[index])) return
this.height = height this.height = height
if (this.stack.currentImageIdIndex !== index) { if (this.stack.currentImageIdIndex !== index) {
@ -578,9 +637,9 @@ export default {
}, },
goViewer(e) { goViewer(e) {
// console.log(this.$refs['sliderBox'].clientHeight) // console.log(this.$refs['sliderBox'].clientHeight)
var height = e.offsetY * 100 / this.$refs['sliderBox'].clientHeight var height = (e.offsetY * 100) / this.$refs['sliderBox'].clientHeight
this.height = height this.height = height
var index = Math.trunc(this.stack.imageIds.length * this.height / 100) var index = Math.trunc((this.stack.imageIds.length * this.height) / 100)
scroll(this.canvas, index) scroll(this.canvas, index)
}, },
onClipStopped() { onClipStopped() {
@ -673,10 +732,7 @@ export default {
} }
this.toolState.clipPlaying = true this.toolState.clipPlaying = true
cornerstoneTools.playClip(this.canvas, this.dicomInfo.fps) cornerstoneTools.playClip(this.canvas, this.dicomInfo.fps)
cornerstoneTools.getToolState( cornerstoneTools.getToolState(this.canvas, 'playClip').data[0].loop = true
this.canvas,
'playClip'
).data[0].loop = true
}, },
setFps(fps) { setFps(fps) {
this.dicomInfo.fps = fps this.dicomInfo.fps = fps
@ -712,8 +768,8 @@ export default {
invert: false, invert: false,
preventZoomOutsideImage: false, preventZoomOutsideImage: false,
minScale: 0.1, minScale: 0.1,
maxScale: 20.0 maxScale: 20.0,
} },
}) })
cornerstoneTools.setToolActive('Zoom', { mouseButtonMask: 1 }) cornerstoneTools.setToolActive('Zoom', { mouseButtonMask: 1 })
this.toolState.activeTool = 'zoom' this.toolState.activeTool = 'zoom'
@ -798,8 +854,9 @@ export default {
cornerstoneTools.setToolPassiveForElement(this.canvas, toolName) cornerstoneTools.setToolPassiveForElement(this.canvas, toolName)
}, },
setToolActive(toolName) { setToolActive(toolName) {
console.log(this.canvas, 'this.canvas')
cornerstoneTools.setToolActiveForElement(this.canvas, toolName, { cornerstoneTools.setToolActiveForElement(this.canvas, toolName, {
mouseButtonMask: 1 mouseButtonMask: 1,
}) })
}, },
setAllToolsPassive() { setAllToolsPassive() {
@ -833,7 +890,7 @@ export default {
) )
} }
cornerstoneTools.setToolEnabledForElement(this.canvas, 'ReferenceLines', { cornerstoneTools.setToolEnabledForElement(this.canvas, 'ReferenceLines', {
synchronizationContext: synchronizer synchronizationContext: synchronizer,
}) })
// cornerstoneTools.addTool(cornerstoneTools.CrosshairsTool) // cornerstoneTools.addTool(cornerstoneTools.CrosshairsTool)
@ -864,7 +921,7 @@ export default {
} }
cornerstoneTools.setToolActiveForElement(this.canvas, toolName, { cornerstoneTools.setToolActiveForElement(this.canvas, toolName, {
mouseButtonMask: 1, mouseButtonMask: 1,
synchronizationContext: synchronizer synchronizationContext: synchronizer,
}) })
}, },
disabledViewPortToolSync(synchronizer, toolName) { disabledViewPortToolSync(synchronizer, toolName) {
@ -1095,7 +1152,7 @@ export default {
'CobbAngle', 'CobbAngle',
'Angle', 'Angle',
'Bidirectional', 'Bidirectional',
'FreehandRoi' 'FreehandRoi',
] ]
for (let i = 0; i < toolROITypes.length; i++) { for (let i = 0; i < toolROITypes.length; i++) {
const toolROIType = toolROITypes[i] const toolROIType = toolROITypes[i]
@ -1115,12 +1172,12 @@ export default {
removeLabel(item) { removeLabel(item) {
const promise = scroll(this.canvas, item.data.imageIdIndex) const promise = scroll(this.canvas, item.data.imageIdIndex)
const scope = this const scope = this
Promise.all([promise]).then(res => { Promise.all([promise]).then((res) => {
cornerstoneTools.removeToolState(scope.canvas, item.type, item.data) cornerstoneTools.removeToolState(scope.canvas, item.type, item.data)
cornerstone.updateImage(scope.canvas) cornerstone.updateImage(scope.canvas)
}) })
} },
} },
} }
</script> </script>

View File

@ -16,10 +16,7 @@
@click="activateDicomCanvas(0)" @click="activateDicomCanvas(0)"
@dblclick="setFullScreen($event)" @dblclick="setFullScreen($event)"
> >
<dicom-canvas <dicom-canvas ref="dicomCanvas0" style="width:100%;height:100%" />
ref="dicomCanvas0"
style="width:100%;height:100%"
/>
</div> </div>
<div <div
v-show="layoutRow>=1&&layoutCol>=2" v-show="layoutRow>=1&&layoutCol>=2"
@ -148,7 +145,7 @@
@click="setToolActive($event,'WwwcRegion')" @click="setToolActive($event,'WwwcRegion')"
> >
<svg-icon icon-class="wwwcRegion" style="font-size:20px;" /> <svg-icon icon-class="wwwcRegion" style="font-size:20px;" />
</button> --> </button>-->
<button <button
:title="$t('trials:reading:button:reverseColor')" :title="$t('trials:reading:button:reverseColor')"
class="btn-link" class="btn-link"
@ -156,33 +153,71 @@
> >
<svg-icon icon-class="reversecolor" style="font-size:20px;" /> <svg-icon icon-class="reversecolor" style="font-size:20px;" />
</button> </button>
<button :title="$t('trials:reading:button:zoom')" class="btn-link" data-tool="Zoom" @click="setToolActive($event,'Zoom')"> <button
:title="$t('trials:reading:button:zoom')"
class="btn-link"
data-tool="Zoom"
@click="setToolActive($event,'Zoom')"
>
<svg-icon icon-class="magnifier" style="font-size:20px;" /> <svg-icon icon-class="magnifier" style="font-size:20px;" />
</button> </button>
<button :title="$t('trials:dicom-show:lens')" class="btn-link" data-tool="Magnify" @click="setToolActive($event,'Magnify')"> <button
:title="$t('trials:dicom-show:lens')"
class="btn-link"
data-tool="Magnify"
@click="setToolActive($event,'Magnify')"
>
<svg-icon icon-class="zoom" style="font-size:20px;" /> <svg-icon icon-class="zoom" style="font-size:20px;" />
</button> </button>
<button :title="$t('trials:reading:button:rotate')" class="btn-link dropdown" data-tool="Rotate"> <button
:title="$t('trials:reading:button:rotate')"
class="btn-link dropdown"
data-tool="Rotate"
>
<svg-icon icon-class="rotate" style="font-size:20px;" /> <svg-icon icon-class="rotate" style="font-size:20px;" />
<div class="dropdown-content"> <div class="dropdown-content">
<div @click.stop="setDicomCanvasRotate(1)">{{ $t('trials:reading:button:rotateDefault') }}</div> <div
<div @click.stop="setDicomCanvasRotate(2)">{{ $t('trials:reading:button:rotateHorizontal') }}</div> @click.stop="setDicomCanvasRotate(1)"
<div @click.stop="setDicomCanvasRotate(3)">{{ $t('trials:reading:button:rotateVertical') }}</div> >{{ $t('trials:reading:button:rotateDefault') }}</div>
<div @click.stop="setDicomCanvasRotate(4)">{{ $t('trials:reading:button:rotateTurnLeft') }}</div> <div
<div @click.stop="setDicomCanvasRotate(5)">{{ $t('trials:reading:button:rotateTurnRight') }}</div> @click.stop="setDicomCanvasRotate(2)"
>{{ $t('trials:reading:button:rotateHorizontal') }}</div>
<div
@click.stop="setDicomCanvasRotate(3)"
>{{ $t('trials:reading:button:rotateVertical') }}</div>
<div
@click.stop="setDicomCanvasRotate(4)"
>{{ $t('trials:reading:button:rotateTurnLeft') }}</div>
<div
@click.stop="setDicomCanvasRotate(5)"
>{{ $t('trials:reading:button:rotateTurnRight') }}</div>
</div> </div>
</button> </button>
<button :title="$t('trials:reading:button:move')" class="btn-link" data-tool="Pan" @click="setToolActive($event,'Pan')"> <button
:title="$t('trials:reading:button:move')"
class="btn-link"
data-tool="Pan"
@click="setToolActive($event,'Pan')"
>
<svg-icon icon-class="move" style="font-size:20px;" /> <svg-icon icon-class="move" style="font-size:20px;" />
</button> </button>
<button :title="$t('trials:reading:button:fitWindow')" class="btn-link" data-tool="fitToWindow" @click="fitToType($event,'fitToWindow')"> <button
:title="$t('trials:reading:button:fitWindow')"
class="btn-link"
data-tool="fitToWindow"
@click="fitToType($event,'fitToWindow')"
>
<svg-icon icon-class="fitToWindow" style="font-size:20px;" /> <svg-icon icon-class="fitToWindow" style="font-size:20px;" />
</button> </button>
<button :title="$t('trials:reading:button:fitImage')" class="btn-link" data-tool="fitToImage" @click="fitToType($event,'fitToImage')"> <button
:title="$t('trials:reading:button:fitImage')"
class="btn-link"
data-tool="fitToImage"
@click="fitToType($event,'fitToImage')"
>
<svg-icon icon-class="fitToImage" style="font-size:20px;" /> <svg-icon icon-class="fitToImage" style="font-size:20px;" />
</button> </button>
<!-- <button title="旋转" class="btn-link dropdown" data-tool="Rotate" @click="setToolActive($event,'Rotate')"> --> <!-- <button title="旋转" class="btn-link dropdown" data-tool="Rotate" @click="setToolActive($event,'Rotate')"> -->
</div> </div>
</div> </div>
<!-- 测量标注 --> <!-- 测量标注 -->
@ -190,65 +225,138 @@
<div class="sideTool-title">{{ $t('trials:dicom-show:measurementLabeling') }}</div> <div class="sideTool-title">{{ $t('trials:dicom-show:measurementLabeling') }}</div>
<div class="sideTool-wrapper"> <div class="sideTool-wrapper">
<!-- 探针 --> <!-- 探针 -->
<button :title="$t('trials:dicom-show:Probe')" class="btn-link" data-tool="Probe" @click="setToolActive($event,'Probe')"> <button
:title="$t('trials:dicom-show:Probe')"
class="btn-link"
data-tool="Probe"
@click="setToolActive($event,'Probe')"
>
<svg-icon icon-class="pixel" style="font-size:20px;" /> <svg-icon icon-class="pixel" style="font-size:20px;" />
</button> </button>
<!-- 长度测量 --> <!-- 长度测量 -->
<button :title="$t('trials:dicom-show:Length')" class="btn-link" data-tool="Length" @click="setToolActive($event,'Length')"> <button
:title="$t('trials:dicom-show:Length')"
class="btn-link"
data-tool="Length"
@click="setToolActive($event,'Length')"
>
<svg-icon icon-class="length" style="font-size:20px;" /> <svg-icon icon-class="length" style="font-size:20px;" />
</button> </button>
<!-- 角度测量 --> <!-- 角度测量 -->
<button :title="$t('trials:dicom-show:Angle')" class="btn-link" data-tool="Angle" @click="setToolActive($event,'Angle')"> <button
:title="$t('trials:dicom-show:Angle')"
class="btn-link"
data-tool="Angle"
@click="setToolActive($event,'Angle')"
>
<svg-icon icon-class="angle" style="font-size:20px;" /> <svg-icon icon-class="angle" style="font-size:20px;" />
</button> </button>
<!-- Cobb测量 --> <!-- Cobb测量 -->
<button :title="$t('trials:dicom-show:CobbAngle')" class="btn-link" data-tool="CobbAngle" @click="setToolActive($event,'CobbAngle')"> <button
:title="$t('trials:dicom-show:CobbAngle')"
class="btn-link"
data-tool="CobbAngle"
@click="setToolActive($event,'CobbAngle')"
>
<svg-icon icon-class="cobb" style="font-size:20px;" /> <svg-icon icon-class="cobb" style="font-size:20px;" />
</button> </button>
<!-- 椭圆测量 --> <!-- 椭圆测量 -->
<button :title="$t('trials:dicom-show:EllipticalRoi')" class="btn-link" data-tool="EllipticalRoi" @click="setToolActive($event,'EllipticalRoi')"> <button
:title="$t('trials:dicom-show:EllipticalRoi')"
class="btn-link"
data-tool="EllipticalRoi"
@click="setToolActive($event,'EllipticalRoi')"
>
<svg-icon icon-class="oval" style="font-size:20px;" /> <svg-icon icon-class="oval" style="font-size:20px;" />
</button> </button>
<!-- 矩形测量 --> <!-- 矩形测量 -->
<button :title="$t('trials:dicom-show:RectangleRoi')" class="btn-link" data-tool="RectangleRoi" @click="setToolActive($event,'RectangleRoi')"> <button
:title="$t('trials:dicom-show:RectangleRoi')"
class="btn-link"
data-tool="RectangleRoi"
@click="setToolActive($event,'RectangleRoi')"
>
<svg-icon icon-class="rectangle" style="font-size:20px;" /> <svg-icon icon-class="rectangle" style="font-size:20px;" />
</button> </button>
<!-- 多边形标记 --> <!-- 多边形标记 -->
<button :title="$t('trials:dicom-show:FreehandRoi')" class="btn-link" data-tool="FreehandRoi" @click="setToolActive($event,'FreehandRoi')"> <button
:title="$t('trials:dicom-show:FreehandRoi')"
class="btn-link"
data-tool="FreehandRoi"
@click="setToolActive($event,'FreehandRoi')"
>
<svg-icon icon-class="polygon" style="font-size:20px;" /> <svg-icon icon-class="polygon" style="font-size:20px;" />
</button> </button>
<!-- 十字线 --> <!-- 十字线 -->
<button :title="$t('trials:dicom-show:Bidirectional')" class="btn-link" data-tool="Bidirectional" @click="setToolActive($event,'Bidirectional')"> <button
:title="$t('trials:dicom-show:Bidirectional')"
class="btn-link"
data-tool="Bidirectional"
@click="setToolActive($event,'Bidirectional')"
>
<svg-icon icon-class="bidirection" style="font-size:20px;" /> <svg-icon icon-class="bidirection" style="font-size:20px;" />
</button> </button>
<!-- 文字标注 --> <!-- 文字标注 -->
<button :title="$t('trials:dicom-show:ArrowAnnotate')" class="btn-link" data-tool="ArrowAnnotate" @click="setToolActive($event,'ArrowAnnotate')"> <button
:title="$t('trials:dicom-show:ArrowAnnotate')"
class="btn-link"
data-tool="ArrowAnnotate"
@click="setToolActive($event,'ArrowAnnotate')"
>
<svg-icon icon-class="label" style="font-size:20px;" /> <svg-icon icon-class="label" style="font-size:20px;" />
</button> </button>
<!-- 清除测量和标记 --> <!-- 清除测量和标记 -->
<button :title="$t('trials:dicom-show:Eraser')" class="btn-link" data-tool="Eraser" @click="setToolActive($event,'Eraser')"> <button
:title="$t('trials:dicom-show:Eraser')"
class="btn-link"
data-tool="Eraser"
@click="setToolActive($event,'Eraser')"
>
<svg-icon icon-class="clear" style="font-size:20px;" /> <svg-icon icon-class="clear" style="font-size:20px;" />
</button> </button>
<!-- 截屏 --> <!-- 截屏 -->
<button :title="$t('trials:dicom-show:image')" class="btn-link" @click="currentDicomCanvas.saveImage()"> <button
:title="$t('trials:dicom-show:image')"
class="btn-link"
@click="currentDicomCanvas.saveImage()"
>
<svg-icon icon-class="image" style="font-size:20px;" /> <svg-icon icon-class="image" style="font-size:20px;" />
</button> </button>
<!-- 标签 --> <!-- 标签 -->
<button :title="$t('trials:dicom-show:tags')" class="btn-link" @click="currentDicomCanvas.showTags()"> <button
:title="$t('trials:dicom-show:tags')"
class="btn-link"
@click="currentDicomCanvas.showTags()"
>
<svg-icon icon-class="dictionary" style="font-size:20px;" /> <svg-icon icon-class="dictionary" style="font-size:20px;" />
</button> </button>
</div> </div>
</div> </div>
<div class="measureTool-wrapper"> <div class="measureTool-wrapper">
<button
:title="$t('trials:dicom-show:EllipticalRoi')"
class="btn-link"
@click="setToolActive($event,'Note_RectangleRoi')"
>
<svg-icon icon-class="oval" style="font-size:20px;" />
</button>
<!-- 播放 --> <!-- 播放 -->
<div class="sideTool-title">{{ $t('trials:dicom-show:play') }}</div> <div class="sideTool-title">{{ $t('trials:dicom-show:play') }}</div>
<div class="sideTool-wrapper"> <div class="sideTool-wrapper">
<!-- 第一帧 --> <!-- 第一帧 -->
<button class="btn-link" :title="$t('trials:dicom-show:firstframe')" @click="currentDicomCanvas.scrollPage(-9999)"> <button
class="btn-link"
:title="$t('trials:dicom-show:firstframe')"
@click="currentDicomCanvas.scrollPage(-9999)"
>
<svg-icon icon-class="firstframe" style="font-size:20px;" /> <svg-icon icon-class="firstframe" style="font-size:20px;" />
</button> </button>
<!-- 显示上一张影像 --> <!-- 显示上一张影像 -->
<button class="btn-link" :title="$t('trials:dicom-show:previousframe')" @click="currentDicomCanvas.scrollPage(-1)"> <button
class="btn-link"
:title="$t('trials:dicom-show:previousframe')"
@click="currentDicomCanvas.scrollPage(-1)"
>
<svg-icon icon-class="previousframe" style="font-size:20px;" /> <svg-icon icon-class="previousframe" style="font-size:20px;" />
</button> </button>
<!-- 播放 --> <!-- 播放 -->
@ -259,14 +367,27 @@
/> />
</button> </button>
<!-- 下一帧 --> <!-- 下一帧 -->
<button class="btn-link" :title="$t('trials:dicom-show:nextframe')" @click="currentDicomCanvas.scrollPage(1)"> <button
class="btn-link"
:title="$t('trials:dicom-show:nextframe')"
@click="currentDicomCanvas.scrollPage(1)"
>
<svg-icon icon-class="nextframe" style="font-size:20px;" /> <svg-icon icon-class="nextframe" style="font-size:20px;" />
</button> </button>
<!-- 最后一帧 --> <!-- 最后一帧 -->
<button class="btn-link" :title="$t('trials:dicom-show:lastframe')" @click="currentDicomCanvas.scrollPage(9999)"> <button
class="btn-link"
:title="$t('trials:dicom-show:lastframe')"
@click="currentDicomCanvas.scrollPage(9999)"
>
<svg-icon icon-class="lastframe" style="font-size:20px;" /> <svg-icon icon-class="lastframe" style="font-size:20px;" />
</button> </button>
<select v-model="fps" class="sidetool-select" style="width:60px" @change="setDicomCanvasfps($event)"> <select
v-model="fps"
class="sidetool-select"
style="width:60px"
@change="setDicomCanvasfps($event)"
>
<!-- 默认值 --> <!-- 默认值 -->
<option :value="5">5</option> <option :value="5">5</option>
<option :value="10">10</option> <option :value="10">10</option>
@ -293,25 +414,16 @@
<!-- 自定义 --> <!-- 自定义 -->
<option :value="0">{{ $t('trials:dicom-show:custom') }}</option> <option :value="0">{{ $t('trials:dicom-show:custom') }}</option>
<!-- 区域窗宽 --> <!-- 区域窗宽 -->
<option :value="1" style="border-bottom:1px solid #fff;">{{ $t('trials:reading:button:wwwcRegion') }}</option> <option
<option :value="2"> :value="1"
CT Abdomen style="border-bottom:1px solid #fff;"
</option> >{{ $t('trials:reading:button:wwwcRegion') }}</option>
<option :value="3"> <option :value="2">CT Abdomen</option>
CT Angio <option :value="3">CT Angio</option>
</option> <option :value="4">CT Bone</option>
<option :value="4"> <option :value="5">CT Brain</option>
CT Bone <option :value="6">CT Chest</option>
</option> <option :value="7">CT Lungs</option>
<option :value="5">
CT Brain
</option>
<option :value="6">
CT Chest
</option>
<option :value="7">
CT Lungs
</option>
</select> </select>
</div> </div>
@ -332,7 +444,6 @@
:value="item.id" :value="item.id"
>{{ item.name }}</option> >{{ item.name }}</option>
</select> </select>
</div> </div>
</div> </div>
</div> </div>
@ -368,7 +479,7 @@ export default {
name: 'DicomsViewer', name: 'DicomsViewer',
components: { components: {
DicomCanvas, DicomCanvas,
CustomWwwcForm CustomWwwcForm,
}, },
data() { data() {
return { return {
@ -379,12 +490,12 @@ export default {
currentDicomCanvasIndex: 0, currentDicomCanvasIndex: 0,
currentDicomCanvas: { currentDicomCanvas: {
toolState: { toolState: {
clipPlaying: false clipPlaying: false,
} },
}, },
rowHeight: '100%', rowHeight: '100%',
sync: { sync: {
Wwwc: null Wwwc: null,
}, },
colormapsList: [], colormapsList: [],
rotateList: [], rotateList: [],
@ -393,11 +504,14 @@ export default {
layout: null, layout: null,
seriesList: [], seriesList: [],
customWwc: { visible: false, title: null }, customWwc: { visible: false, title: null },
fps: 15 fps: 15,
} }
}, },
mounted() { mounted() {
this.customWwc = { visible: false, title: this.$t('DicomViewer:data:customWwc') } this.customWwc = {
visible: false,
title: this.$t('DicomViewer:data:customWwc'),
}
this.rotateList[0] = '1' this.rotateList[0] = '1'
this.colorList[0] = '' this.colorList[0] = ''
this.wwwcList[0] = '-1' this.wwwcList[0] = '-1'
@ -646,13 +760,12 @@ export default {
} }
} }
} }
} },
} },
} }
</script> </script>
<style> <style>
.dicom-wrapper { .dicom-wrapper {
display: flex; display: flex;
} }
@ -664,18 +777,17 @@ export default {
overflow: auto; overflow: auto;
} }
.dicom-wrapper .case-dialog-div{ .dicom-wrapper .case-dialog-div {
pointer-events: none; pointer-events: none;
} }
.dicom-wrapper .case-dialog-class .el-dialog__body{ .dicom-wrapper .case-dialog-class .el-dialog__body {
max-height:300px; max-height: 300px;
overflow-y: auto; overflow-y: auto;
} }
.dicom-wrapper .el-dialog__header{ .dicom-wrapper .el-dialog__header {
padding: 15px; padding: 15px;
} }
.dicom-wrapper .el-dialog__body{ .dicom-wrapper .el-dialog__body {
padding: 10px 20px; padding: 10px 20px;
} }
.dicom-viewer { .dicom-viewer {
@ -711,12 +823,12 @@ export default {
z-index: 1; z-index: 1;
} }
.dicom-wrapper ::-webkit-scrollbar { .dicom-wrapper ::-webkit-scrollbar {
width: 7px; width: 7px;
height: 7px; height: 7px;
} }
.dicom-wrapper ::-webkit-scrollbar-thumb { .dicom-wrapper ::-webkit-scrollbar-thumb {
border-radius: 10px; border-radius: 10px;
background: gray; background: gray;
} }
.dicom-wrapper .dicom-tools { .dicom-wrapper .dicom-tools {
/* display: flex; /* display: flex;
@ -732,27 +844,26 @@ export default {
font-size: 13px; font-size: 13px;
overflow: hidden; overflow: hidden;
} }
.dicom-wrapper .measure-wrapper{ .dicom-wrapper .measure-wrapper {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex: 1; flex: 1;
overflow: hidden; overflow: hidden;
} }
.measure-wrapper .measure-list{ .measure-wrapper .measure-list {
flex: 1; flex: 1;
overflow-y: auto; overflow-y: auto;
} }
.measure-list-header{ .measure-list-header {
padding: 2px 0px; padding: 2px 0px;
border-bottom: 1px solid gray; border-bottom: 1px solid gray;
} }
.measure-wrapper ul{ .measure-wrapper ul {
margin: 0; margin: 0;
padding: 0 5px; padding: 0 5px;
list-style: none; list-style: none;
} }
.measure-wrapper ul li{ .measure-wrapper ul li {
height: 20px; height: 20px;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
@ -861,14 +972,14 @@ export default {
background-color: #ffffff00; background-color: #ffffff00;
cursor: pointer; cursor: pointer;
} }
.dicom-wrapper .btn-submit{ .dicom-wrapper .btn-submit {
cursor: pointer; cursor: pointer;
/* background-color: #eeeeee; */ /* background-color: #eeeeee; */
background: #eeeeee; background: #eeeeee;
border: 1px solid #DCDFE6; border: 1px solid #dcdfe6;
border-radius: 4px; border-radius: 4px;
color: #606266; color: #606266;
font-size: 13px; font-size: 13px;
} }
/* .dicom-wrapper .btn-link:hover, /* .dicom-wrapper .btn-link:hover,
@ -877,7 +988,7 @@ export default {
border-color: transparent; border-color: transparent;
background-color: transparent; background-color: transparent;
} */ } */
.dicom-wrapper .iconHover:hover{ .dicom-wrapper .iconHover:hover {
color: red; color: red;
} }
.dicom-wrapper .dropdown { .dicom-wrapper .dropdown {
@ -892,20 +1003,19 @@ export default {
color: #d0d0d0; color: #d0d0d0;
background-color: #323232; background-color: #323232;
min-width: 100px; min-width: 100px;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
border: 1px solid #4e4e4e; border: 1px solid #4e4e4e;
padding: 5px; padding: 5px;
} }
.dicom-wrapper .dropdown:hover .dropdown-content { .dicom-wrapper .dropdown:hover .dropdown-content {
display: block; display: block;
} }
.dicom-wrapper .dropdown-content div{ .dicom-wrapper .dropdown-content div {
height: 25px; height: 25px;
line-height: 25px; line-height: 25px;
cursor:point; cursor: point;
} }
.dicom-wrapper .dropdown-content div:hover{ .dicom-wrapper .dropdown-content div:hover {
background-color: #16477b90; background-color: #16477b90;
} }
</style> </style>

View File

@ -0,0 +1,110 @@
import cornerstone from 'cornerstone-core';
import cornerstoneTools from 'cornerstone-tools';
const BaseAnnotationTool = cornerstoneTools.importInternal('base/BaseAnnotationTool');
const getToolState = cornerstoneTools.getToolState;
const drawHandles = cornerstoneTools.importInternal('drawing/drawHandles');
export default class Note_RectangleRoiTool extends BaseAnnotationTool {
constructor(props = {}) {
super({
name: 'Note_RectangleRoi',
supportedInteractionTypes: ['Mouse'],
configuration: {
customColor: '#FF4444',
showLabel: true,
handleRadius: 6,
...props.configuration
}
});
}
createNewMeasurement(eventData) {
const { currentPoints } = eventData;
const startPoint = currentPoints.canvas;
return {
visible: true,
active: true,
color: this.configuration.customColor,
handles: {
start: { x: startPoint.x, y: startPoint.y, highlight: true, active: false },
end: { x: startPoint.x, y: startPoint.y, highlight: true, active: false },
textBox: {
active: false, hasMoved: false, movesIndependently: false,
drawnIndependently: true, allowedOutsideImage: true, hasBoundingBox: true
}
}
};
}
pointNearTool(element, data, coords) {
const minX = Math.min(data.handles.start.x, data.handles.end.x);
const maxX = Math.max(data.handles.start.x, data.handles.end.x);
const minY = Math.min(data.handles.start.y, data.handles.end.y);
const maxY = Math.max(data.handles.start.y, data.handles.end.y);
const threshold = 5;
return coords.x >= minX - threshold && coords.x <= maxX + threshold &&
coords.y >= minY - threshold && coords.y <= maxY + threshold;
}
// 稳健的 renderToolData 实现
renderToolData(evt) {
// 1. 获取 element尝试多种来源
const element = evt.detail?.element || evt.element || evt?.detail?.eventData?.element;
if (!element) {
console.warn('renderToolData: Cannot find element');
return;
}
// 2. 获取工具数据
const toolData = getToolState(element, this.name);
if (!toolData || !toolData.data || toolData.data.length === 0) return;
// 3. 获取 canvas context
const enabledElement = cornerstone.getEnabledElement(element);
const context = enabledElement.canvas.getContext('2d');
if (!context) return;
// 4. 绘制所有标注
context.save();
toolData.data.forEach(data => {
if (!data.visible) return;
context.strokeStyle = data.color || this.configuration.customColor;
context.fillStyle = 'transparent';
context.lineWidth = 2;
const start = data.handles.start;
const end = data.handles.end;
const width = end.x - start.x;
const height = end.y - start.y;
context.strokeRect(start.x, start.y, width, height);
if (data.active) {
drawHandles(context, { element }, [start, end], {
handleRadius: this.configuration.handleRadius,
color: data.color
});
}
if (this.configuration.showLabel) {
const area = Math.abs(width * height);
const text = `${area.toFixed(0)}px²`;
const textCoords = {
x: (start.x + end.x) / 2,
y: Math.min(start.y, end.y) - 10
};
context.font = '12px Arial';
context.fillStyle = data.color;
context.fillText(text, textCoords.x, textCoords.y);
}
});
context.restore();
}
}