Compare commits

...

4 Commits

Author SHA1 Message Date
wangxiaoshuang 946203f124 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing Details
# Conflicts:
#	src/components/Dicom/DicomViewer.vue
#	src/views/dicom-show/dicom-study.vue
2026-04-23 15:53:04 +08:00
wangxiaoshuang f0eb814492 质控数据匿名 2026-04-23 15:50:15 +08:00
wangxiaoshuang 47b05526a2 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web 2026-04-22 15:06:47 +08:00
wangxiaoshuang f7afca6e73 注释(未完成) 2026-04-22 15:06:39 +08:00
5 changed files with 673 additions and 345 deletions

View File

@ -408,3 +408,19 @@ export function changeSegmentationSavedStatus(data) {
data data
}) })
} }
// 图像数据匿名
export function studyMaskImage(data) {
return request({
url: `/Study/studyMaskImage`,
method: 'post',
data
})
}
// 撤销匿名
export function studyUndoMaskImage(data) {
return request({
url: `/Study/studyUndoMaskImage`,
method: 'post',
data
})
}

View File

@ -23,17 +23,11 @@
</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 v-else-if="(dicomInfo.modality === 'PT' && mousePosition.suv)">SUVbw(g/ml): {{ mousePosition.suv.toFixed(3)
<div v-else-if="(dicomInfo.modality === 'PT' && mousePosition.suv)"> }}</div>
SUVbw(g/ml): {{ mousePosition.suv.toFixed(3) }} <div v-else-if="mousePosition.mo">Density: {{ mousePosition.mo }}</div>
</div> <div>W*H: {{ dicomInfo.size }}</div>
<div v-else-if="mousePosition.mo">
Density: {{ mousePosition.mo }}
</div>
<div>
W*H: {{ dicomInfo.size }}
</div>
<div>Zoom: {{ dicomInfo.zoom }}</div> <div>Zoom: {{ dicomInfo.zoom }}</div>
</div> </div>
@ -56,19 +50,11 @@
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>
<div style="position: absolute;left: 50%;top: 15px;color: #f44336;"> <div style="position: absolute;left: 50%;top: 15px;color: #f44336;">{{ markers.top }}</div>
{{ markers.top }} <div style="position: absolute;top: 50%;right: 15px;color: #f44336;">{{ markers.right }}</div>
</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">
@ -101,6 +87,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 +104,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 +117,7 @@ export default {
seriesNumber: '', seriesNumber: '',
imageIds: [], imageIds: [],
currentImageIdIndex: 0, currentImageIdIndex: 0,
firstImageLoading: false firstImageLoading: false,
// preventCache: true // preventCache: true
}, },
dicomInfo: { dicomInfo: {
@ -150,14 +137,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 +155,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 +189,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 +202,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 +222,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 +255,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 +289,28 @@ export default {
false false
) )
}) })
if (!cornerstoneTools.getToolForElement(element, cornerstoneTools.WwwcRegionTool)) { if (
cornerstoneTools.addToolForElement(element, cornerstoneTools.WwwcRegionTool) !cornerstoneTools.getToolForElement(element, Note_RectangleRoiTool)
) {
cornerstoneTools.addToolForElement(element, Note_RectangleRoiTool, {
configuration: {
color: '#f00',
lineWidth: 0.5,
drawHandles: false,
fillColor: 'rgba(0, 0, 0, 1)',
},
})
}
if (
!cornerstoneTools.getToolForElement(
element,
cornerstoneTools.WwwcRegionTool
)
) {
cornerstoneTools.addToolForElement(
element,
cornerstoneTools.WwwcRegionTool
)
} }
if ( if (
!cornerstoneTools.getToolForElement( !cornerstoneTools.getToolForElement(
@ -347,7 +369,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) {
@ -379,21 +403,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 +441,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 +488,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 +535,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 +562,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 +581,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 +595,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 +614,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 +709,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 +745,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'
@ -797,9 +830,110 @@ export default {
setToolPassive(toolName) { setToolPassive(toolName) {
cornerstoneTools.setToolPassiveForElement(this.canvas, toolName) cornerstoneTools.setToolPassiveForElement(this.canvas, toolName)
}, },
async reloadImage(newImageId = null) {
// 1. imageIdimageId
let element = this.canvas
this.stack.imageIds.splice(this.stack.currentImageIdIndex, 1, newImageId)
const currentImageId =
newImageId || cornerstone.getImage(element)?.imageId
if (!currentImageId) {
console.error('没有找到可用的imageId')
return
}
// 2.
this.clearToolStateForImage(element, currentImageId)
// 3.
this.removeImageFromCache(currentImageId)
// 4.
await this.loadAndRenderImage(element, currentImageId)
},
clearToolStateForImage(element, imageId) {
//
const globalToolStateManager =
cornerstoneTools.globalImageIdSpecificToolStateManager
if (globalToolStateManager) {
// 1
globalToolStateManager.clearImageIdToolState(imageId)
console.log(`已清除影像 ${imageId} 的所有标注数据`)
}
// 2使
const elementToolStateManager =
cornerstoneTools.getElementToolStateManager(element)
if (elementToolStateManager && elementToolStateManager.get(element)) {
//
elementToolStateManager.clear(element)
}
},
removeImageFromCache(imageId) {
const imageCache = cornerstone.imageCache
if (imageCache && imageCache[imageId]) {
//
delete imageCache[imageId]
console.log(`已从缓存中移除影像: ${imageId}`)
}
// 使API
if (typeof cornerstone.imageCache.removeImage === 'function') {
cornerstone.imageCache.removeImageLoadObject(imageId)
}
},
async loadAndRenderImage(element, imageId) {
try {
// 1.
const canvas = element.querySelector('canvas')
if (canvas) {
const ctx = canvas.getContext('2d')
ctx.clearRect(0, 0, canvas.width, canvas.height)
}
// 2.
const image = await cornerstone.loadAndCacheImage(imageId)
// 3.
let viewport = cornerstone.getViewport(element)
if (!viewport) {
viewport = cornerstone.getDefaultViewport(element, image)
}
// 4.
cornerstone.displayImage(element, image, viewport)
// 5.
cornerstone.updateImage(element)
console.log(`影像 ${imageId} 重新加载并渲染完成`)
return image
} catch (error) {
console.error('加载图像失败:', error)
throw error
}
},
getNote_RectangleRoi() {
return new Promise(resolve => {
let toolInfo = cornerstoneTools.getToolState(this.canvas, 'Note_RectangleRoi')
let image = cornerstone.getImage(this.canvas)
resolve({ toolInfo, image })
})
// console.log(
// cornerstoneTools.getToolState(this.canvas, 'Note_RectangleRoi')
// )
// console.log(cornerstone.getImage(this.canvas))
// let image = cornerstone.getImage(this.canvas)
// // cornerstone.imageCache.removeImageLoadObject(image.imageId)
// this.reloadImage(this.canvas, image.imageId)
},
setToolActive(toolName) { setToolActive(toolName) {
cornerstoneTools.setToolActiveForElement(this.canvas, toolName, { cornerstoneTools.setToolActiveForElement(this.canvas, toolName, {
mouseButtonMask: 1 mouseButtonMask: 1,
}) })
}, },
setAllToolsPassive() { setAllToolsPassive() {
@ -833,7 +967,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 +998,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 +1229,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 +1249,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

@ -6,105 +6,68 @@
<dicom-canvas ref="dicomCanvas" style="width:100%;height:100%" /> <dicom-canvas ref="dicomCanvas" style="width:100%;height:100%" />
</div> </div>
</div>--> </div>-->
<div class="Anonymous" v-if="isAnonymous">
<div v-show="layoutRow>=1" class="dicom-row" :style="{height: rowHeight}"> <div :class="{ btn: true, activeBtn: activeTool === 'Note_RectangleRoi' }"
<div @click="setToolActive($event, 'Note_RectangleRoi')">矩形</div>
v-show="layoutRow>=1&&layoutCol>=1" <div :class="{ btn: true, activeBtn: activeTool === 'Eraser' }" @click="setToolActive($event, 'Eraser')">清除
class="dicom-item"
:class="{'activeItem':activeItem=='dicomCanvas0'}"
data-index="0"
@click="activateDicomCanvas(0)"
@dblclick="setFullScreen($event)"
>
<dicom-canvas
ref="dicomCanvas0"
style="width:100%;height:100%"
/>
</div> </div>
<div <div class="btn" @click="anonymousImage"></div>
v-show="layoutRow>=1&&layoutCol>=2" <div class="btn">应用整个序列</div>
class="dicom-item" <!-- <div class="btn">刷新图像</div> -->
:class="{'activeItem':activeItem=='dicomCanvas1'}" <div class="btn" v-if="!isComparison"></div>
data-index="1" <div class="btn" v-else>退</div>
@click="activateDicomCanvas(1)" <div class="btn">恢复</div>
@dblclick="setFullScreen($event)" </div>
> <div v-show="layoutRow >= 1" class="dicom-row" :style="{ height: rowHeight }">
<div v-show="layoutRow >= 1 && layoutCol >= 1" class="dicom-item"
:class="{ 'activeItem': activeItem == 'dicomCanvas0' }" data-index="0" @click="activateDicomCanvas(0)"
@dblclick="setFullScreen($event)">
<dicom-canvas ref="dicomCanvas0" style="width:100%;height:100%" />
</div>
<div v-show="layoutRow >= 1 && layoutCol >= 2" class="dicom-item"
:class="{ 'activeItem': activeItem == 'dicomCanvas1' }" data-index="1" @click="activateDicomCanvas(1)"
@dblclick="setFullScreen($event)">
<dicom-canvas ref="dicomCanvas1" style="width:100%;height:100%" /> <dicom-canvas ref="dicomCanvas1" style="width:100%;height:100%" />
</div> </div>
<div <div v-show="layoutRow >= 1 && layoutCol >= 3" class="dicom-item"
v-show="layoutRow>=1&&layoutCol>=3" :class="{ 'activeItem': activeItem == 'dicomCanvas2' }" data-index="2" @click="activateDicomCanvas(2)"
class="dicom-item" @dblclick="setFullScreen($event)">
:class="{'activeItem':activeItem=='dicomCanvas2'}"
data-index="2"
@click="activateDicomCanvas(2)"
@dblclick="setFullScreen($event)"
>
<dicom-canvas ref="dicomCanvas2" style="width:100%;height:100%" /> <dicom-canvas ref="dicomCanvas2" style="width:100%;height:100%" />
</div> </div>
</div> </div>
<div v-show="layoutRow >= 2" class="dicom-row" :style="{ height: rowHeight }"> <div v-show="layoutRow >= 2" class="dicom-row" :style="{ height: rowHeight }">
<div <div v-show="layoutRow >= 2 && layoutCol >= 1" class="dicom-item"
v-show="layoutRow>=2&&layoutCol>=1" :class="{ 'activeItem': activeItem == 'dicomCanvas3' }" data-index="3" @click="activateDicomCanvas(3)"
class="dicom-item" @dblclick="setFullScreen($event)">
:class="{'activeItem':activeItem=='dicomCanvas3'}"
data-index="3"
@click="activateDicomCanvas(3)"
@dblclick="setFullScreen($event)"
>
<dicom-canvas ref="dicomCanvas3" style="width:100%;height:100%" /> <dicom-canvas ref="dicomCanvas3" style="width:100%;height:100%" />
</div> </div>
<div <div v-show="layoutRow >= 2 && layoutCol >= 2" class="dicom-item"
v-show="layoutRow>=2&&layoutCol>=2" :class="{ 'activeItem': activeItem == 'dicomCanvas4' }" data-index="4" @click="activateDicomCanvas(4)"
class="dicom-item" @dblclick="setFullScreen($event)">
:class="{'activeItem':activeItem=='dicomCanvas4'}"
data-index="4"
@click="activateDicomCanvas(4)"
@dblclick="setFullScreen($event)"
>
<dicom-canvas ref="dicomCanvas4" style="width:100%;height:100%" /> <dicom-canvas ref="dicomCanvas4" style="width:100%;height:100%" />
</div> </div>
<div <div v-show="layoutRow >= 2 && layoutCol >= 3" class="dicom-item"
v-show="layoutRow>=2&&layoutCol>=3" :class="{ 'activeItem': activeItem == 'dicomCanvas5' }" data-index="5" @click="activateDicomCanvas(5)"
class="dicom-item" @dblclick="setFullScreen($event)">
:class="{'activeItem':activeItem=='dicomCanvas5'}"
data-index="5"
@click="activateDicomCanvas(5)"
@dblclick="setFullScreen($event)"
>
<dicom-canvas ref="dicomCanvas5" style="width:100%;height:100%" /> <dicom-canvas ref="dicomCanvas5" style="width:100%;height:100%" />
</div> </div>
</div> </div>
<div v-show="layoutRow == 3" class="dicom-row" :style="{ height: rowHeight }"> <div v-show="layoutRow == 3" class="dicom-row" :style="{ height: rowHeight }">
<div <div v-show="layoutRow == 3 && layoutCol >= 1" class="dicom-item"
v-show="layoutRow==3&&layoutCol>=1" :class="{ 'activeItem': activeItem == 'dicomCanvas6' }" data-index="6" @click="activateDicomCanvas(6)"
class="dicom-item" @dblclick="setFullScreen($event)">
:class="{'activeItem':activeItem=='dicomCanvas6'}"
data-index="6"
@click="activateDicomCanvas(6)"
@dblclick="setFullScreen($event)"
>
<dicom-canvas ref="dicomCanvas6" style="width:100%;height:100%" /> <dicom-canvas ref="dicomCanvas6" style="width:100%;height:100%" />
</div> </div>
<div <div v-show="layoutRow == 3 && layoutCol >= 2" class="dicom-item"
v-show="layoutRow==3&&layoutCol>=2" :class="{ 'activeItem': activeItem == 'dicomCanvas7' }" data-index="7" @click="activateDicomCanvas(7)"
class="dicom-item" @dblclick="setFullScreen($event)">
:class="{'activeItem':activeItem=='dicomCanvas7'}"
data-index="7"
@click="activateDicomCanvas(7)"
@dblclick="setFullScreen($event)"
>
<dicom-canvas ref="dicomCanvas7" style="width:100%;height:100%" /> <dicom-canvas ref="dicomCanvas7" style="width:100%;height:100%" />
</div> </div>
<div <div v-show="layoutRow == 3 && layoutCol >= 3" class="dicom-item"
v-show="layoutRow==3&&layoutCol>=3" :class="{ 'activeItem': activeItem == 'dicomCanvas8' }" data-index="8" @click="activateDicomCanvas(8)"
class="dicom-item" @dblclick="setFullScreen($event)">
:class="{'activeItem':activeItem=='dicomCanvas8'}"
data-index="8"
@click="activateDicomCanvas(8)"
@dblclick="setFullScreen($event)"
>
<dicom-canvas ref="dicomCanvas8" style="width:100%;height:100%" /> <dicom-canvas ref="dicomCanvas8" style="width:100%;height:100%" />
</div> </div>
</div> </div>
@ -116,7 +79,7 @@
<div class="sideTool-title">{{ $t('trials:reading:button:layout') }}</div> <div class="sideTool-title">{{ $t('trials:reading:button:layout') }}</div>
<div class="sideTool-wrapper"> <div class="sideTool-wrapper">
<label>{{ $t('trials:reading:button:layout') }}:</label> <label>{{ $t('trials:reading:button:layout') }}:</label>
<select class="sidetool-select" style="width:90px" @change="changeLayout($event)"> <select class="sidetool-select" style="width:90px" :disabled="isAnonymous" @change="changeLayout($event)">
<option value="1x1" selected>1x1</option> <option value="1x1" selected>1x1</option>
<option value="1x2">1x2</option> <option value="1x2">1x2</option>
<option value="2x1">2x1</option> <option value="2x1">2x1</option>
@ -127,18 +90,15 @@
<option value="3x2">3x2</option> <option value="3x2">3x2</option>
<option value="3x3">3x3</option> <option value="3x3">3x3</option>
</select> </select>
<div class="btnBox" @click="openAnonymous"></div>
</div> </div>
</div> </div>
<!-- 图像变换 --> <!-- 图像变换 -->
<div class="measureTool-wrapper"> <div class="measureTool-wrapper">
<div class="sideTool-title">{{ $t('trials:dicom-show:transform') }}</div> <div class="sideTool-title">{{ $t('trials:dicom-show:transform') }}</div>
<div class="sideTool-wrapper"> <div class="sideTool-wrapper">
<button <button :title="$t('trials:reading:button:wwwc')" class="btn-link" data-tool="Wwwc"
:title="$t('trials:reading:button:wwwc')" @click="setToolActive($event, 'Wwwc')">
class="btn-link"
data-tool="Wwwc"
@click="setToolActive($event,'Wwwc')"
>
<svg-icon icon-class="reverse" style="font-size:20px;" /> <svg-icon icon-class="reverse" style="font-size:20px;" />
</button> </button>
<!-- <button <!-- <button
@ -149,17 +109,15 @@
> >
<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')" class="btn-link" @click="toggleInvert">
:title="$t('trials:reading:button:reverseColor')"
class="btn-link"
@click="toggleInvert"
>
<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">
@ -172,17 +130,19 @@
<div @click.stop="setDicomCanvasRotate(5)">{{ $t('trials:reading:button:rotateTurnRight') }}</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,43 +150,53 @@
<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>
<!-- 截屏 --> <!-- 截屏 -->
@ -244,26 +214,27 @@
<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>
<!-- 播放 --> <!-- 播放 -->
<button class="btn-link" :title="$t('trials:dicom-show:play')" @click="clipPlay"> <button class="btn-link" :title="$t('trials:dicom-show:play')" @click="clipPlay">
<svg-icon <svg-icon :icon-class="currentDicomCanvas.toolState.clipPlaying ? 'stop' : 'play'"
:icon-class="currentDicomCanvas.toolState.clipPlaying ? 'stop' : 'play'" style="font-size:20px;" />
style="font-size:20px;"
/>
</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)">
@ -282,163 +253,91 @@
<div class="sideTool-wrapper"> <div class="sideTool-wrapper">
<!-- 预设窗位值 --> <!-- 预设窗位值 -->
<label>{{ $t('trials:dicom-show:dicomCanvasWwwc') }}:</label> <label>{{ $t('trials:dicom-show:dicomCanvasWwwc') }}:</label>
<select <select v-model="wwwcList[currentDicomCanvasIndex]" class="sidetool-select" style="width:100px"
v-model="wwwcList[currentDicomCanvasIndex]" @change="setDicomCanvasWwwc($event)">
class="sidetool-select"
style="width:100px"
@change="setDicomCanvasWwwc($event)"
>
<!-- 默认值 --> <!-- 默认值 -->
<option :value="-1">{{ $t('trials:dicom-show:default') }}</option> <option :value="-1">{{ $t('trials:dicom-show:default') }}</option>
<!-- 自定义 --> <!-- 自定义 -->
<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 :value="1" style="border-bottom:1px solid #fff;">{{ $t('trials:reading:button:wwwcRegion') }}
<option :value="2">
CT Abdomen
</option>
<option :value="3">
CT Angio
</option>
<option :value="4">
CT Bone
</option>
<option :value="5">
CT Brain
</option>
<option :value="6">
CT Chest
</option>
<option :value="7">
CT Lungs
</option> </option>
<option :value="2">CT Abdomen</option>
<option :value="3">CT Angio</option>
<option :value="4">CT Bone</option>
<option :value="5">CT Brain</option>
<option :value="6">CT Chest</option>
<option :value="7">CT Lungs</option>
</select> </select>
</div> </div>
<div class="sideTool-wrapper"> <div class="sideTool-wrapper">
<!-- 伪彩色 --> <!-- 伪彩色 -->
<label>{{ $t('trials:dicom-show:pseudocolor') }}:</label> <label>{{ $t('trials:dicom-show:pseudocolor') }}:</label>
<select <select v-model="colorList[currentDicomCanvasIndex]" class="sidetool-select" style="width:90px"
v-model="colorList[currentDicomCanvasIndex]" @change="setColormap($event)">
class="sidetool-select"
style="width:90px"
@change="setColormap($event)"
>
<!-- 默认值 --> <!-- 默认值 -->
<option value>{{ $t('trials:dicom-show:default') }}</option> <option value>{{ $t('trials:dicom-show:default') }}</option>
<option <option v-for="(item, index) in colormapsList" :key="index" :value="item.id">{{ item.name }}</option>
v-for="(item,index) in colormapsList"
:key="index"
:value="item.id"
>{{ item.name }}</option>
</select> </select>
</div> </div>
</div> </div>
<!-- 患者信息 --> <!-- 患者信息 -->
<div class="measureTool-wrapper patient-form" v-if="isHaveStudyClinicalData && type === 'Study' && modality && ['PT、CT', 'CT、PT', 'PET-CT'].includes(modality)"> <div class="measureTool-wrapper patient-form"
v-if="isHaveStudyClinicalData && type === 'Study' && modality && ['PT、CT', 'CT、PT', 'PET-CT'].includes(modality)">
<div class="sideTool-title">{{ $t('trials:tab:patientData') }}</div> <div class="sideTool-title">{{ $t('trials:tab:patientData') }}</div>
<div class="sideTool-wrapper"> <div class="sideTool-wrapper">
<el-form <el-form ref="patientForm" :model="formData" :rules="rules" label-width="150" v-loading="formLoading">
ref="patientForm"
:model="formData"
:rules="rules"
label-width="150"
v-loading="formLoading"
>
<!-- 性别 --> <!-- 性别 -->
<el-form-item :label="$t('trials:ptData:label:patientSex')" prop="PatientSex"> <el-form-item :label="$t('trials:ptData:label:patientSex')" prop="PatientSex">
<el-select <el-select v-model="formData.PatientSex" :placeholder="$t('common:ruleMessage:select')"
v-model="formData.PatientSex" style="width: 100%" size="small" :disabled="!isEdit">
:placeholder="$t('common:ruleMessage:select')"
style="width: 100%"
size="small"
:disabled="!isEdit"
>
<el-option :label="$t('trials:patientSex:male')" value="M"></el-option> <el-option :label="$t('trials:patientSex:male')" value="M"></el-option>
<el-option :label="$t('trials:patientSex:female')" value="F"></el-option> <el-option :label="$t('trials:patientSex:female')" value="F"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<!-- 体重kg 例如 70.5--> <!-- 体重kg 例如 70.5-->
<el-form-item :label="$t('trials:ptData:label:patientWeight')" prop="PatientWeight"> <el-form-item :label="$t('trials:ptData:label:patientWeight')" prop="PatientWeight">
<el-input <el-input v-model.number="formData.PatientWeight" :placeholder="$t('trials:patientWeight:eg')"
v-model.number="formData.PatientWeight" style="width: 100%" size="small" :disabled="!isEdit"></el-input>
:placeholder="$t('trials:patientWeight:eg')"
style="width: 100%"
size="small"
:disabled="!isEdit"
></el-input>
</el-form-item> </el-form-item>
<!-- 总剂量Bq 例如 740000000--> <!-- 总剂量Bq 例如 740000000-->
<el-form-item :label="$t('trials:ptData:label:totalDose')" prop="RadionuclideTotalDose"> <el-form-item :label="$t('trials:ptData:label:totalDose')" prop="RadionuclideTotalDose">
<el-input <el-input v-model.number="formData.RadionuclideTotalDose" :placeholder="$t('trials:totalDose:eg')"
v-model.number="formData.RadionuclideTotalDose" style="width: 100%" size="small" :disabled="!isEdit"></el-input>
:placeholder="$t('trials:totalDose:eg')"
style="width: 100%"
size="small"
:disabled="!isEdit"
></el-input>
</el-form-item> </el-form-item>
<!-- 半衰期s 例如 21600--> <!-- 半衰期s 例如 21600-->
<el-form-item :label="$t('trials:ptData:label:halfLife')" prop="RadionuclideHalfLife"> <el-form-item :label="$t('trials:ptData:label:halfLife')" prop="RadionuclideHalfLife">
<el-input <el-input v-model.number="formData.RadionuclideHalfLife" :placeholder="$t('trials:halfLife:eg')"
v-model.number="formData.RadionuclideHalfLife" style="width: 100%" size="small" :disabled="!isEdit"></el-input>
:placeholder="$t('trials:halfLife:eg')"
style="width: 100%"
size="small"
:disabled="!isEdit"
></el-input>
</el-form-item> </el-form-item>
<!-- 注射时间s Unix 相对秒--> <!-- 注射时间s Unix 相对秒-->
<el-form-item :label="$t('trials:ptData:label:injectTime')" prop="RadiopharmaceuticalStartTime"> <el-form-item :label="$t('trials:ptData:label:injectTime')" prop="RadiopharmaceuticalStartTime">
<el-input <el-input v-model.number="formData.RadiopharmaceuticalStartTime" :placeholder="$t('trials:injectTime:eg')"
v-model.number="formData.RadiopharmaceuticalStartTime" style="width: 100%" @input="computeTimeRelation" size="small" :disabled="!isEdit"></el-input>
:placeholder="$t('trials:injectTime:eg')"
style="width: 100%"
@input="computeTimeRelation"
size="small"
:disabled="!isEdit"
></el-input>
</el-form-item> </el-form-item>
<!-- 成像时间s Unix 相对秒--> <!-- 成像时间s Unix 相对秒-->
<el-form-item :label="$t('trials:ptData:label:acquisitionTime')" prop="AcquisitionTime"> <el-form-item :label="$t('trials:ptData:label:acquisitionTime')" prop="AcquisitionTime">
<el-input <el-input v-model.number="formData.AcquisitionTime" :placeholder="$t('trials:injectTime:eg')"
v-model.number="formData.AcquisitionTime" style="width: 100%" @input="computeTimeRelation" size="small" :disabled="!isEdit"></el-input>
:placeholder="$t('trials:injectTime:eg')"
style="width: 100%"
@input="computeTimeRelation"
size="small"
:disabled="!isEdit"
></el-input>
</el-form-item> </el-form-item>
<!-- 时间一致性检查 --> <!-- 时间一致性检查 -->
<el-form-item :label="$t('trials:ptData:label:timeCheck')"> <el-form-item :label="$t('trials:ptData:label:timeCheck')">
<el-input <el-input v-model="formData.TimeCheck" disabled style="width: 100%" size="small"></el-input>
v-model="formData.TimeCheck"
disabled
style="width: 100%"
size="small"
></el-input>
</el-form-item> </el-form-item>
<!-- 提交 --> <!-- 提交 -->
<el-form-item style="margin-top: 20px;text-align: right;" v-if="isEdit"> <el-form-item style="margin-top: 20px;text-align: right;" v-if="isEdit">
<el-button type="primary" @click="submitForm" size="small">{{ $t('trials:ptData:button:submit') }}</el-button> <el-button type="primary" @click="submitForm" size="small">{{ $t('trials:ptData:button:submit')
}}</el-button>
</el-form-item> </el-form-item>
</el-form> </el-form>
</div> </div>
</div> </div>
</div> </div>
<el-dialog <el-dialog v-if="customWwc.visible" :visible.sync="customWwc.visible" :close-on-click-modal="false"
v-if="customWwc.visible" :title="customWwc.title" width="400px" custom-class="base-dialog-wrapper" append-to-body>
:visible.sync="customWwc.visible"
:close-on-click-modal="false"
:title="customWwc.title"
width="400px"
custom-class="base-dialog-wrapper"
append-to-body
>
<CustomWwwcForm @close="customWwc.visible = false" @setWwwc="setWwwc" /> <CustomWwwcForm @close="customWwc.visible = false" @setWwwc="setWwwc" />
</el-dialog> </el-dialog>
</div> </div>
@ -456,7 +355,10 @@ import Hammer from 'hammerjs'
cornerstoneTools.external.cornerstone = cornerstone cornerstoneTools.external.cornerstone = cornerstone
cornerstoneTools.external.Hammer = Hammer cornerstoneTools.external.Hammer = Hammer
cornerstoneTools.external.cornerstoneMath = cornerstoneMath cornerstoneTools.external.cornerstoneMath = cornerstoneMath
console.log(cornerstoneTools, 'cornerstoneTools')
console.log(cornerstone, 'cornerstone')
import '@/utils/dialog' import '@/utils/dialog'
import { studyMaskImage, studyUndoMaskImage } from "@/api/reading"
import { import {
getPatientInfo, getPatientInfo,
editPatientInfo editPatientInfo
@ -465,9 +367,13 @@ export default {
name: 'DicomsViewer', name: 'DicomsViewer',
components: { components: {
DicomCanvas, DicomCanvas,
CustomWwwcForm CustomWwwcForm,
}, },
props: { props: {
loading: {
type: Boolean,
default: false
},
modality: { modality: {
type: String, type: String,
default: '' default: ''
@ -475,6 +381,8 @@ export default {
}, },
data() { data() {
return { return {
isAnonymous: false,
isComparison: false,
activeTool: '', activeTool: '',
activeItem: 'dicomCanvas0', activeItem: 'dicomCanvas0',
layoutRow: 1, layoutRow: 1,
@ -482,12 +390,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: [],
@ -495,6 +403,7 @@ export default {
wwwcList: [], wwwcList: [],
layout: null, layout: null,
seriesList: [], seriesList: [],
series: {},
customWwc: { visible: false, title: null }, customWwc: { visible: false, title: null },
fps: 15, fps: 15,
formData: { formData: {
@ -541,7 +450,10 @@ export default {
} }
}, },
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'
@ -556,11 +468,83 @@ export default {
}, },
methods: { methods: {
anonymousImage() {
console.log(this.series, 'this.series')
this.$refs[`dicomCanvas0`].getNote_RectangleRoi().then(async obj => {
let { toolInfo, image } = obj
console.log(image, 'image')
if (!toolInfo || toolInfo.data.length <= 0) return this.$confirm(this.$t("DicomViewer:anonymous:notMark"))
let instanceInfo = this.series.instanceInfoList.find(item => item.ImageId === image.imageId)
let data = {
// SeriesId: this.series.seriesId,
instanceIdList: [instanceInfo.Id],
MaskRegionList: []
}
toolInfo.data.forEach(item => {
let currentStart = item.handles.start
let currentEnd = item.handles.end
let start = {
x: currentStart.x > currentEnd.x ? Math.round(currentEnd.x) : Math.round(currentStart.x),
y: currentStart.y > currentEnd.y ? Math.round(currentEnd.y) : Math.round(currentStart.y),
}
let end = {
x: currentStart.x > currentEnd.x ? Math.round(currentStart.x) : Math.round(currentEnd.x),
y: currentStart.y > currentEnd.y ? Math.round(currentStart.y) : Math.round(currentEnd.y),
}
let width = end.x - start.x
let height = end.y - start.y
data.MaskRegionList.push({
X: start.x,
Y: start.y,
Width: width,
Height: height
})
})
let res = await this.studyMaskImage(data)
if (!res) return false
// this.$emit("update:loading", true)
// let strs = image.imageId.split("?")
// let newImageId = `${strs[0]}-MaskImage?${strs[1]}`
// console.log(newImageId, 'newImageId')
// this.$refs[`dicomCanvas0`].reloadImage(newImageId)
})
},
openAnonymous() {
this.isAnonymous = !this.isAnonymous
if (!this.isAnonymous) {
const elements = document.querySelectorAll('.dicom-item')
const scope = this
scope.activeTool = null
Array.from(elements).forEach((element, index) => {
if (element.style.display !== 'none') {
scope.$refs[`dicomCanvas${index}`].setToolPassive(toolName)
}
})
} else {
this.activateDicomCanvas(0)
this.changeLayout('1x1')
}
},
async studyMaskImage(data) {
try {
this.$emit("update:loading", true)
let res = await studyMaskImage(data)
this.$emit("update:loading", false)
if (res.IsSuccess) {
return true
}
return false
} catch (err) {
console.log(err)
this.$emit("update:loading", false)
return false
}
},
loadImageStack(dicomSeries) { loadImageStack(dicomSeries) {
this.currentDicomCanvas.toolState.clipPlaying = false this.currentDicomCanvas.toolState.clipPlaying = false
this.$nextTick(() => { this.$nextTick(() => {
const series = Object.assign({}, dicomSeries) this.series = Object.assign({}, dicomSeries)
this.currentDicomCanvas.loadImageStack(series) this.currentDicomCanvas.loadImageStack(this.series)
}) })
}, },
loadOtherImageStack(seriesList) { loadOtherImageStack(seriesList) {
@ -609,7 +593,7 @@ export default {
} }
}, },
changeLayout(event) { changeLayout(event) {
const arr = event.target.value.split('x') const arr = event.target ? event.target.value.split('x') : event.split('x')
this.layoutRow = parseInt(arr[0]) this.layoutRow = parseInt(arr[0])
this.layoutCol = parseInt(arr[1]) this.layoutCol = parseInt(arr[1])
this.rowHeight = 100 / this.layoutRow + '%' this.rowHeight = 100 / this.layoutRow + '%'
@ -646,11 +630,21 @@ export default {
cornerstone.resize(e.currentTarget.children[0]) cornerstone.resize(e.currentTarget.children[0])
} }
}, },
fillColor() {
const elements = document.querySelectorAll('.dicom-item')
const scope = this
Array.from(elements).forEach((element, index) => {
if (element.style.display !== 'none') {
scope.$refs[`dicomCanvas${index}`].fillColor()
}
})
},
setToolActive(e, toolName) { setToolActive(e, toolName) {
const elements = document.querySelectorAll('.dicom-item') const elements = document.querySelectorAll('.dicom-item')
if (e.currentTarget.classList.contains('activeTool')) { if (e.currentTarget.classList.contains('activeTool')) {
e.currentTarget.classList.remove('activeTool') e.currentTarget.classList.remove('activeTool')
const scope = this const scope = this
scope.activeTool = null
Array.from(elements).forEach((element, index) => { Array.from(elements).forEach((element, index) => {
if (element.style.display !== 'none') { if (element.style.display !== 'none') {
scope.$refs[`dicomCanvas${index}`].setToolPassive(toolName) scope.$refs[`dicomCanvas${index}`].setToolPassive(toolName)
@ -664,6 +658,7 @@ export default {
e.currentTarget.classList.add('activeTool') e.currentTarget.classList.add('activeTool')
const scope = this const scope = this
scope.activeTool = toolName
Array.from(elements).forEach((element, index) => { Array.from(elements).forEach((element, index) => {
if (element.style.display !== 'none') { if (element.style.display !== 'none') {
scope.$refs[`dicomCanvas${index}`].setToolActive(toolName) scope.$refs[`dicomCanvas${index}`].setToolActive(toolName)
@ -864,11 +859,11 @@ export default {
</script> </script>
<style> <style>
.dicom-wrapper { .dicom-wrapper {
display: flex; display: flex;
height: 100%; height: 100%;
} }
.dicom-wrapper .case-dialog-class { .dicom-wrapper .case-dialog-class {
position: fixed; position: fixed;
left: 25%; left: 25%;
@ -879,30 +874,85 @@ export default {
.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 {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex: 1; flex: 1;
position: relative; position: relative;
} }
.Anonymous {
position: absolute;
border-radius: 5px;
bottom: 30px;
left: 5%;
right: 5%;
width: 90%;
height: 60px;
padding: 0 10px;
background-color: rgba(255, 255, 255, .2);
display: flex;
align-items: center;
justify-content: space-between;
z-index: 9999;
.btn {
width: 15%;
text-align: center;
height: 40px;
line-height: 30px;
border-radius: 15px;
background-color: rgba(255, 255, 255, .3);
cursor: pointer;
padding: 5px 10px;
border: 1px solid rgba(255, 255, 255, .7);
&:hover {
background-color: rgba(255, 255, 255, .5);
}
}
.activeBtn {
background-color: rgba(255, 255, 255, .5);
}
}
.btnBox {
display: inline-block;
width: 80px;
text-align: center;
height: 30px;
line-height: 20px;
border-radius: 15px;
background-color: rgba(255, 255, 255, .3);
cursor: pointer;
padding: 5px 10px;
border: 1px solid rgba(255, 255, 255, .7);
margin: 5px;
}
.dicom-wrapper .dicom-row { .dicom-wrapper .dicom-row {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
flex: 1; flex: 1;
width: 100%; width: 100%;
} }
.dicom-wrapper .dicom-item { .dicom-wrapper .dicom-item {
position: relative; position: relative;
width: 0; width: 0;
@ -912,9 +962,11 @@ export default {
padding: 5px; padding: 5px;
border: 1px solid #c8c8c8; border: 1px solid #c8c8c8;
} }
.dicom-wrapper .activeItem { .dicom-wrapper .activeItem {
border: 2px solid chocolate; border: 2px solid chocolate;
} }
.dicom-item-fullscreen { .dicom-item-fullscreen {
position: absolute; position: absolute;
left: 0; left: 0;
@ -923,14 +975,17 @@ export default {
height: 100%; height: 100%;
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;
flex-direction: column; */ flex-direction: column; */
@ -945,26 +1000,30 @@ export default {
font-size: 13px; font-size: 13px;
overflow-y: auto; overflow-y: auto;
} }
.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;
@ -981,9 +1040,11 @@ export default {
font-size: 13px; font-size: 13px;
color: #606626; color: #606626;
} }
.measure-wrapper select>option { .measure-wrapper select>option {
color: #323232; color: #323232;
} }
/* .measure-wrapper select { /* .measure-wrapper select {
height: 20px; height: 20px;
background-color: rgba(0, 0, 0, 0); background-color: rgba(0, 0, 0, 0);
@ -1003,6 +1064,7 @@ export default {
.dicom-canvas.active { .dicom-canvas.active {
border: 1px solid #337ab7; border: 1px solid #337ab7;
} }
.sideTool-title { .sideTool-title {
padding: 5px 8px; padding: 5px 8px;
background-color: #525252; background-color: #525252;
@ -1010,30 +1072,36 @@ export default {
font-size: 14px; font-size: 14px;
margin: 4px; margin: 4px;
} }
.sideTool-wrapper { .sideTool-wrapper {
border-bottom: 1px solid gray; border-bottom: 1px solid gray;
margin: 0px 4px; margin: 0px 4px;
padding: 4px; padding: 4px;
font-size: 12px; font-size: 12px;
} }
.sideTool-wrapper .btn { .sideTool-wrapper .btn {
font-size: 12px; font-size: 12px;
} }
.sidetool-select { .sidetool-select {
height: 30px; height: 30px;
background-color: rgba(0, 0, 0, 0); background-color: rgba(0, 0, 0, 0);
color: #d0d0d0; color: #d0d0d0;
border-radius: 4px; border-radius: 4px;
} }
.sidetool-select>option { .sidetool-select>option {
background-color: #323232; background-color: #323232;
} }
.sideTool-wrapper button.btn-link { .sideTool-wrapper button.btn-link {
height: 40px; height: 40px;
padding: 8px !important; padding: 8px !important;
border: 1px solid rgba(37, 37, 37, 1); border: 1px solid rgba(37, 37, 37, 1);
margin: 1px 1px; margin: 1px 1px;
} }
.sideTool-wrapper .btn-link { .sideTool-wrapper .btn-link {
display: inline-block; display: inline-block;
height: 40px; height: 40px;
@ -1041,6 +1109,7 @@ export default {
border: 1px solid rgba(37, 37, 37, 1); border: 1px solid rgba(37, 37, 37, 1);
margin: 1px 1px; margin: 1px 1px;
} }
.dicom-wrapper .btn-group { .dicom-wrapper .btn-group {
position: relative; position: relative;
display: inline-block; display: inline-block;
@ -1057,6 +1126,7 @@ export default {
border: 1px solid rgba(37, 37, 37, 1); border: 1px solid rgba(37, 37, 37, 1);
min-height: 30px; min-height: 30px;
} }
.dicom-wrapper .btn-group .btn-left { .dicom-wrapper .btn-group .btn-left {
position: relative; position: relative;
float: left; float: left;
@ -1065,20 +1135,23 @@ export default {
border-bottom-right-radius: 0; border-bottom-right-radius: 0;
border-top-right-radius: 0; border-top-right-radius: 0;
} }
.dicom-wrapper .activeTool { .dicom-wrapper .activeTool {
background: #16477b90 !important; background: #16477b90 !important;
border: none; border: none;
} }
.dicom-wrapper .btn-link { .dicom-wrapper .btn-link {
color: #cccccc; color: #cccccc;
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;
@ -1093,10 +1166,12 @@ export default {
.dicom-wrapper .iconHover:hover { .dicom-wrapper .iconHover:hover {
color: red; color: red;
} }
.dicom-wrapper .dropdown { .dicom-wrapper .dropdown {
position: relative; position: relative;
display: inline-block; display: inline-block;
} }
.dicom-wrapper .dropdown-content { .dicom-wrapper .dropdown-content {
display: none; display: none;
position: absolute; position: absolute;
@ -1109,14 +1184,17 @@ export default {
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;
} }
@ -1124,15 +1202,17 @@ export default {
.patient-form .el-form-item { .patient-form .el-form-item {
margin-bottom: 15px; margin-bottom: 15px;
} }
.patient-form .el-form-item__label { .patient-form .el-form-item__label {
color: #d0d0d0; color: #d0d0d0;
} }
.patient-form .el-input.is-disabled .el-input__inner { .patient-form .el-input.is-disabled .el-input__inner {
background-color: #424244; background-color: #424244;
} }
.patient-form .el-input .el-input__inner { .patient-form .el-input .el-input__inner {
background-color: #323232; background-color: #323232;
color: #d0d0d0; color: #d0d0d0;
} }
</style> </style>

View File

@ -123,7 +123,8 @@
</div> </div>
</div> </div>
<div class="viewerContent"> <div class="viewerContent">
<dicom-viewer id="dicomViewer" ref="dicomViewer" style="height:100%" :modality="modality"/> <dicom-viewer id="dicomViewer" ref="dicomViewer" style="height:100%" :loading.sync="loading"
:modality="modality" />
</div> </div>
<!-- <div class="viewerRightSidePanel"> <!-- <div class="viewerRightSidePanel">
<dicom-tools /> <dicom-tools />

View File

@ -0,0 +1,97 @@
// Note_RectangleRoiTool.js
import * as cornerstoneTools from 'cornerstone-tools';
export default class Note_RectangleRoiTool extends cornerstoneTools.RectangleRoiTool {
constructor(props = {}) {
const defaultProps = {
name: 'Note_RectangleRoi',
configuration: {
fillColor: 'rgba(255, 255, 0, 0.3)', // 默认填充颜色:半透明黄色
strokeColor: 'yellow', // 边框颜色
lineWidth: 2, // 边框宽度
drawHandles: true, // 是否绘制控制点
handleColor: 'white', // 控制点颜色
}
};
super({ ...defaultProps, ...props });
}
renderToolData(evt) {
const eventData = evt.detail;
const { element } = eventData;
// 获取工具状态
const toolData = cornerstoneTools.getToolState(element, this.name);
if (!toolData || !toolData.data || !toolData.data.length) {
return;
}
const canvas = eventData.canvasContext.canvas;
const context = canvas.getContext('2d');
context.save();
// 获取配置
const fillColor = this.configuration.fillColor || 'rgba(255, 255, 0, 0.3)';
const strokeColor = this.configuration.strokeColor || 'yellow';
const lineWidth = this.configuration.lineWidth || 2;
const drawHandles = this.configuration.drawHandles !== false;
// 遍历所有矩形标注
toolData.data.forEach((measurement) => {
if (!measurement.handles?.start || !measurement.handles?.end) {
return;
}
const start = measurement.handles.start;
const end = measurement.handles.end;
// 计算矩形坐标和尺寸
const x = Math.min(start.x, end.x);
const y = Math.min(start.y, end.y);
const width = Math.abs(end.x - start.x);
const height = Math.abs(end.y - start.y);
// 1. 绘制填充
if (fillColor) {
context.fillStyle = fillColor;
context.fillRect(x, y, width, height);
}
// 2. 绘制边框
context.strokeStyle = strokeColor;
context.lineWidth = lineWidth;
context.strokeRect(x, y, width, height);
// 3. 绘制控制点(可选)
if (drawHandles) {
this.drawHandles(context, measurement, eventData);
}
});
context.restore();
}
/**
* 绘制控制点
*/
drawHandles(context, measurement, eventData) {
const handles = measurement.handles;
const handleColor = this.configuration.handleColor || 'white';
// 绘制所有控制点
Object.keys(handles).forEach(key => {
const handle = handles[key];
if (handle && typeof handle.x === 'number' && typeof handle.y === 'number') {
context.beginPath();
context.arc(handle.x, handle.y, 5, 0, 2 * Math.PI);
context.fillStyle = handleColor;
context.fill();
context.strokeStyle = 'black';
context.lineWidth = 1;
context.stroke();
}
});
}
}