diff --git a/src/icons/svg/sphericalBrush.svg b/src/icons/svg/sphericalBrush.svg new file mode 100644 index 00000000..565c0f48 --- /dev/null +++ b/src/icons/svg/sphericalBrush.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/store/modules/user.js b/src/store/modules/user.js index 3ecc4eab..8a932720 100644 --- a/src/store/modules/user.js +++ b/src/store/modules/user.js @@ -121,7 +121,7 @@ const actions = { localStorage.setItem('CompanyInfo', JSON.stringify(response.Result.CompanyInfo)) const data = response.Result commit('SET_ROLES', data.BasicInfo.AccountList) - if (data.BasicInfo.IsFirstAdd || data.BasicInfo.LoginState === 1) { + if (data.BasicInfo.IsFirstAdd || data.BasicInfo.LoginState === 1 || data.BasicInfo.IsNeedResetPwd) { try { zzSessionStorage.setItem('userId', data.BasicInfo.Id) commit('SET_TOKEN', data.JWTStr) diff --git a/src/views/login/index.vue b/src/views/login/index.vue index 89dbf290..ed041659 100644 --- a/src/views/login/index.vue +++ b/src/views/login/index.vue @@ -156,7 +156,7 @@ -

{{ $t('login:title:system') }}

+

{{ $t('login:title:system') }}

V{{ $version.IsEnv_US ? $version.Version_US : $version.Version }} @@ -374,6 +374,15 @@ export default { }) }, 500) return + } else if (res.BasicInfo.IsNeedResetPwd) { + // 当前用户管理员重置密码,请先修改密码之后再次登录 + this.$message.success(this.$t('login:message:login5')) + setTimeout(() => { + this.$router.push({ + path: `/user-recompose?UserId=${res.BasicInfo.IdentityUserId}&Email=${res.BasicInfo.EMail}&UserName=${res.BasicInfo.UserName}&lang=${this.$i18n.locale}&access_token=${res.JWTStr}&isUpdate=1`, + }) + }, 500) + return } else if (res.BasicInfo.NeedChangePassWord) { // 请先修改密码后再登录! this.$alert( diff --git a/src/views/trials/trials-panel/reading/dicoms3D/components/ReadPage.vue b/src/views/trials/trials-panel/reading/dicoms3D/components/ReadPage.vue index 15af978e..9261590f 100644 --- a/src/views/trials/trials-panel/reading/dicoms3D/components/ReadPage.vue +++ b/src/views/trials/trials-panel/reading/dicoms3D/components/ReadPage.vue @@ -3076,6 +3076,8 @@ export default { } } else if (this.activeTool === FusionJumpToPointTool.toolName) { this.setFusionMipJumpEnabled(false) + } else if (this.activeTool === 'SphericalBrush') { + toolGroup.setToolDisabled('ThresholdSphere') } else { toolGroup.setToolPassive(this.activeTool) } diff --git a/src/views/trials/trials-panel/reading/dicoms3D/components/Segmentations.vue b/src/views/trials/trials-panel/reading/dicoms3D/components/Segmentations.vue index b55d7595..613efaf4 100644 --- a/src/views/trials/trials-panel/reading/dicoms3D/components/Segmentations.vue +++ b/src/views/trials/trials-panel/reading/dicoms3D/components/Segmentations.vue @@ -19,7 +19,7 @@

+ @click.prevent="initThreshold()">
+
+ +
+ v-if="activeTool === 'SphericalBrush' || activeTool === 'CircularEraser' || activeTool === 'CircularBrush' || ThresholdTools.includes(activeTool)"> {{ $t('trials:reading:Segmentations:title:EraserConfigSection') }}
-
+
{{ $t('trials:reading:Segmentations:title:EraserConfig') }} @@ -656,8 +662,13 @@ export default { handleClickPopover(item) { this.popoverId = `popover-${item.segmentationId}_${item.segmentIndex}` }, - initThreshold() { + initThreshold(key = null) { if (this.isMPR) return false + if (key === 'SphericalBrush') { + this.setToolActive(this.ThresholdTools[1], 'SphericalBrush') + this.setSphericalBrushConfig() + return false + } if (!this.ThresholdTools.includes(this.activeTool)) { this.setToolActive(this.ThresholdTools[0]) this.thresholdType = this.ThresholdTools[0] @@ -777,7 +788,7 @@ export default { } }, - setToolActive(toolName) { + setToolActive(toolName, name = null) { if (this.segmentList.length <= 0) return false if (this.curSegment.lock) return false if (this.isMPR) return false @@ -806,10 +817,15 @@ export default { // if (toolName === 'CircularEraser') { // console.log(toolGroup.getToolInstance(toolName)) // } - this.$emit('update:activeTool', toolName) - this.setBrushSize(toolName) - if (this.ThresholdTools.includes(toolName)) { - this.setBrushThreshold() + if (name) { + this.$emit('update:activeTool', name) + this.setBrushSize(name) + } else { + this.$emit('update:activeTool', toolName) + this.setBrushSize(toolName) + if (this.ThresholdTools.includes(toolName)) { + this.setBrushThreshold() + } } } @@ -1516,6 +1532,21 @@ export default { if (toolName === 'ThresholdSphere') { this.setDynamicRadius() } + if (toolName === 'SphericalBrush') { + this.setSphericalBrushConfig() + } + }, + setSphericalBrushConfig() { + let volume = cache.getVolume(this.series.SeriesInstanceUid); + let { spacing, numFrames } = volume + let constant = numFrames * spacing[2] / 100 + let dynamicRadius = Math.ceil(this.brushSize * constant) + let obj = { + dynamicRadius: dynamicRadius, + isDynamic: false, + range: [-10000, 100000] + } + this.setBrushThreshold(obj) }, setDynamicRadius() { let volume = cache.getVolume(this.series.SeriesInstanceUid); @@ -1523,7 +1554,7 @@ export default { let constant = numFrames * spacing[2] / 100 this.brushThreshold.dynamicRadius = Math.ceil(this.brushSize * constant) }, - setBrushThreshold() { + setBrushThreshold(OBJ = null) { const toolGroupId = this.isMPR ? this.volumeToolGroupId : `${this.viewportKey}-${this.activeViewportIndex}` let brushThreshold = { isDynamic: this.brushThreshold.isDynamic, @@ -1532,6 +1563,7 @@ export default { if (!this.brushThreshold.isDynamic) { brushThreshold.range = this.brushThreshold.range } + if (OBJ) brushThreshold = OBJ CStUtils.segmentation.setBrushThresholdForToolGroup(toolGroupId, brushThreshold); }, async createSegmentation(segmentationId) { @@ -1566,7 +1598,7 @@ export default { }, segmentationModifiedCallback(evt) { const { detail } = evt; - let tools = [...this.ThresholdTools, ...this.tools] + let tools = [...this.ThresholdTools, ...this.tools, 'SphericalBrush'] if (!detail || detail.segmentIndex === 255 || !this.activeTool || !tools.includes(this.activeTool)) { return; } @@ -1584,12 +1616,26 @@ export default { console.log(stats) if (mode === 'individual') { const segmentStats = stats; - for (const segmentIndex of indices) { if (segmentStats[segmentIndex]) { const segmentStat = segmentStats[segmentIndex]; // console.log(segmentStat, 'segmentStat') segmentStat.count.label = 'Voxels'; + if (this.series.Modality === 'PT') { + const renderingEngine = getRenderingEngine(this.renderingEngineId) + const viewportId = `${this.viewportKey}-${this.activeViewportIndex}` + const viewport = renderingEngine.getViewport(viewportId); + let imageIds = viewport.getImageIds(this.series.SeriesInstanceUid) + let imageId = imageIds[0] + const suvFactor = metaData.get('scalingModule', imageId) || {}; + console.log(suvFactor, 'suvFactor') + segmentStat.sulpeak = { + label: "Mean Pixel", + name: "sulpeak", + unit: "SUL", + value: suvFactor.suvlbm && suvFactor.suvbw ? segmentStat.mean.value * suvFactor.suvlbm / suvFactor.suvbw : 0, + } + } let segmentGroup = this.segmentList.find(item => item.segmentationId === segmentationId) if (segmentGroup) { let segment = segmentGroup.segments.find(item => item.segmentIndex === segmentIndex) @@ -1617,12 +1663,12 @@ export default { contentMouseup() { try { // console.log("segment contentMouseup") - if (!this.drawing) return false if (this.timeoutId) { clearTimeout(this.timeoutId); this.timeoutId = null; } this.timeoutId = setTimeout(() => { + if (!this.drawing) return false this.timeoutId = null; this.drawing = false; let segmentGroup = this.segmentList.find(item => item.segmentationId === this.segmentationId) @@ -2498,6 +2544,21 @@ export default { .EraserConfig { margin: 5px 0; + ::v-deep .el-input-number { + width: 90px; + } + + ::v-deep .el-input-number.is-without-controls .el-input__inner { + padding: 0 10px; + width: 90px; + } + + ::v-deep .el-radio-button__inner { + width: 105px; + } +} + +.RadiusConfig { ::v-deep .el-input-number { width: 50px; } diff --git a/src/views/trials/trials-panel/reading/dicoms3D/components/toolConfig.js b/src/views/trials/trials-panel/reading/dicoms3D/components/toolConfig.js index 17274ebe..ec331e93 100644 --- a/src/views/trials/trials-panel/reading/dicoms3D/components/toolConfig.js +++ b/src/views/trials/trials-panel/reading/dicoms3D/components/toolConfig.js @@ -409,7 +409,7 @@ const config = { 'name': 'Labelmap分割', 'icon': 'labelmap', 'toolName': 'Labelmap', - 'props': ['max', 'min', 'volume', 'count', 'mean', 'stdDev', 'length', 'width'], + 'props': ['max', 'min', 'volume', 'count', 'mean', 'stdDev', 'length', 'width', 'sulpeak'], 'i18nKey': 'trials:reading:button:Labelmap', 'isDisabled': false, 'disabledReason': '' diff --git a/src/views/trials/trials-panel/reading/visit-review/components/FileViewer.vue b/src/views/trials/trials-panel/reading/visit-review/components/FileViewer.vue index d4961984..115db698 100644 --- a/src/views/trials/trials-panel/reading/visit-review/components/FileViewer.vue +++ b/src/views/trials/trials-panel/reading/visit-review/components/FileViewer.vue @@ -32,13 +32,8 @@
- +
  • @@ -56,12 +51,8 @@
  • {{ $t('trials:reading:keyImage:noData') }}
  • -
  • +
  • @@ -117,13 +108,9 @@ :title="$t('trials:nondicom-show:scale')" @click.prevent="setAnnotateToolActive('Lengthscale')">
    - + -
    +
    @@ -131,8 +118,8 @@
    -
    +
    @@ -349,7 +336,7 @@ - + @@ -1448,7 +1435,7 @@ export default { this.saveCustomAnnotationTimer = null } this.saveCustomAnnotation(annotation) - } + } } else { // if (this.isNumber(operateStateEnum)) { // this.removeAnnotation(annotation) @@ -1943,7 +1930,7 @@ export default { if (currentViewport.fileType && currentViewport.fileType.includes('mp4')) { divForDownloadViewport = this.$refs[`grid-cell-${this.activeCanvasIndex}`][0] const videoEl = this.$refs[`videovideo-${this.activeCanvasIndex}`][0] - + if (videoEl) { // 将视频帧画到 canvas 上生成图片 const tempCanvas = document.createElement('canvas') @@ -1952,7 +1939,7 @@ export default { const containerHeight = videoEl.clientHeight tempCanvas.width = containerWidth tempCanvas.height = containerHeight - + const ctx = tempCanvas.getContext('2d') // 填充黑色背景 ctx.fillStyle = '#000' @@ -1961,7 +1948,7 @@ export default { // 计算视频保持比例的缩放尺寸和位置 (等同于 object-fit: contain) const videoWidth = videoEl.videoWidth || containerWidth const videoHeight = videoEl.videoHeight || containerHeight - + const scale = Math.min(containerWidth / videoWidth, containerHeight / videoHeight) const drawWidth = videoWidth * scale const drawHeight = videoHeight * scale @@ -1970,9 +1957,9 @@ export default { // 将视频帧居中绘制 ctx.drawImage(videoEl, offsetX, offsetY, drawWidth, drawHeight) - + const frameBase64 = tempCanvas.toDataURL('image/png') - + // 创建一个 img 标签遮盖在 video 上 tempImg = document.createElement('img') tempImg.src = frameBase64 @@ -1982,7 +1969,7 @@ export default { tempImg.style.width = '100%' tempImg.style.height = '100%' tempImg.style.zIndex = '99' - + const videoContainer = this.$refs[`video-content-main-${this.activeCanvasIndex}`][0] videoContainer.style.position = 'relative' videoContainer.appendChild(tempImg) @@ -1998,11 +1985,11 @@ export default { // 等待 DOM 更新 await this.$nextTick() - const canvas = await html2canvas(divForDownloadViewport, { + const canvas = await html2canvas(divForDownloadViewport, { useCORS: true, backgroundColor: '#000000' // 指定截图背景为黑色 }) - + // 截图后恢复 if (tempImg) { tempImg.remove() @@ -2010,7 +1997,7 @@ export default { const base64Str = canvas.toDataURL('image/png', 1) const downloadName = `${Date.now()}.png` - + const a = document.createElement('a') a.href = base64Str a.download = downloadName @@ -2600,11 +2587,13 @@ export default { } } } + .personal_config { ::v-deep .el-tabs__content { height: 450px; overflow-y: auto; } + ::v-deep .hot-keys-label { color: #dfdfdf !important; }