38 Commits

Author SHA1 Message Date
wangxiaoshuang 0273601f7b Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2026-03-25 17:46:45 +08:00
wangxiaoshuang c3a154f762 分割长短径工具问题修复 2026-03-25 17:46:31 +08:00
caiyiling cdb97f6405 w/l预设模板移除CT Bone1
continuous-integration/drone/push Build is passing
2026-03-25 17:31:59 +08:00
caiyiling 4794429a57 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-03-25 16:51:31 +08:00
caiyiling 77a633d699 SPECT融合更改 2026-03-25 16:45:44 +08:00
wangxiaoshuang 64d1ebf350 导入分割数据与分段进行绑定
continuous-integration/drone/push Build is passing
2026-03-25 16:44:55 +08:00
wangxiaoshuang 5336cb5353 分割标记删除进行提示
continuous-integration/drone/push Build is passing
2026-03-25 14:11:52 +08:00
wangxiaoshuang 0a288b9f47 分割标记重命名问题修改
continuous-integration/drone/push Build is passing
2026-03-25 14:03:04 +08:00
wangxiaoshuang 674d483cbf Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2026-03-25 13:52:23 +08:00
wangxiaoshuang cbcb396441 分割标记跳转修改 2026-03-25 13:52:14 +08:00
caiyiling 9a3e972723 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-03-25 11:00:20 +08:00
caiyiling b593615e9e Lugano标准融合页面伪彩更改 2026-03-25 10:59:55 +08:00
wangxiaoshuang d30c9a5f3b 图像滚动问题解决
continuous-integration/drone/push Build is passing
2026-03-25 09:26:11 +08:00
caiyiling dd3d738eed Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-03-25 09:10:25 +08:00
caiyiling d9edaf4c27 OCT标准FCT平均值计算逻辑更改 2026-03-25 09:09:35 +08:00
wangxiaoshuang a5e0dd89ff 1
continuous-integration/drone/push Build is running
2026-03-25 09:07:21 +08:00
wangxiaoshuang 73f8f816b7 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is failing
2026-03-24 17:56:10 +08:00
wangxiaoshuang 19d5144b70 问题绑定分割标记后查看 2026-03-24 17:55:57 +08:00
caiyiling e0ecd3f4ac Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is running
2026-03-24 17:55:31 +08:00
caiyiling 30e0e374bc 调窗更改 2026-03-24 17:54:59 +08:00
wangxiaoshuang 25b909c660 分割默认工具功能修复
continuous-integration/drone/push Build is passing
2026-03-24 16:45:44 +08:00
caiyiling 5cb3e8e606 同步调窗更改
continuous-integration/drone/push Build is passing
2026-03-24 15:46:25 +08:00
wangxiaoshuang 619af2e874 分割问题绑定标记显示问题
continuous-integration/drone/push Build is passing
2026-03-24 15:27:17 +08:00
wangxiaoshuang 43f1736b05 1
continuous-integration/drone/push Build is passing
2026-03-24 15:12:49 +08:00
wangxiaoshuang c9be5678bd Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is running
2026-03-24 15:09:51 +08:00
wangxiaoshuang 4e5ecb3938 分割添加轮廓工具 2026-03-24 15:09:45 +08:00
caiyiling 7870c3d74b CT/NM融合更改
continuous-integration/drone/push Build is passing
2026-03-24 14:54:46 +08:00
wangxiaoshuang d07adc4948 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
# Conflicts:
#	src/views/trials/trials-panel/reading/dicoms3D/components/ReadPage.vue
#	src/views/trials/trials-panel/reading/dicoms3D/components/StudyList.vue
2026-03-23 15:10:29 +08:00
wangxiaoshuang 266886fa64 阅片添加分割工具 2026-03-23 10:01:22 +08:00
caiyiling e012eba0c2 阅片页面样式更改
continuous-integration/drone/push Build is passing
2026-03-19 16:54:25 +08:00
caiyiling 75d296405b cornerstone版本升级
continuous-integration/drone/push Build is passing
2026-03-19 15:12:54 +08:00
caiyiling 07c92c48a4 融合图像筛选更改
continuous-integration/drone/push Build is passing
2026-03-13 14:11:52 +08:00
hang b5427dd17b 修改打包文件配置
continuous-integration/drone/push Build is passing
2026-03-13 13:49:27 +08:00
hang 385643643e uat合并main 导致打包文件被覆盖,重新编写 2026-03-13 13:47:23 +08:00
caiyiling 756f1293aa 融合图像补充模态为NM类型的影像 2026-03-13 13:35:38 +08:00
caiyiling eee19f0ae9 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main 2026-03-13 13:20:27 +08:00
caiyiling 78c0910db7 选择融合图像时扩充NM模态的影像 2026-03-13 13:18:24 +08:00
wangxiaoshuang 18a71725f9 自动化脚本变更
continuous-integration/drone/push Build is passing
2026-03-06 09:44:56 +08:00
92 changed files with 10253 additions and 10743 deletions
+35 -3
View File
@@ -1,7 +1,7 @@
kind: pipeline
type: ssh
name: ssh-linux-test-irc-publish
name: ssh-linux-uat-irc-publish
platform:
os: Linux
@@ -19,7 +19,7 @@ server:
steps:
- name: publish-uat-irc-web
commands:
- bash /opt/1panel/xc-deploy-new/devops-center/build-vue-then-publish.sh IRC_Web uat uat
- bash /opt/1panel/xc-deploy-new/devops-center/build-vue-then-publish.sh IRC_Web uat --mode uat
- name: notify-wecom
commands:
- echo $DRONE_COMMIT_AUTHOR "$DRONE_COMMIT_MESSAGE"
@@ -33,6 +33,38 @@ trigger:
- uat
---
kind: pipeline
type: ssh
name: ssh-linux-test-irc-publish
platform:
os: Linux
arch: 386
clone:
disable: true #禁用默认克隆
server:
host: 106.14.89.110
user: root
password:
from_secret: local_pwd
steps:
- name: publish-test-irc-web
commands:
- bash /opt/1panel/xc-deploy-new/devops-center/build-vue-then-publish.sh IRC_Web main
- name: notify-wecom
commands:
- echo $DRONE_COMMIT_AUTHOR "$DRONE_COMMIT_MESSAGE"
- bash /opt/1panel/xc-deploy-new/devops-center/drone-notify-wecom.sh "$DRONE_BUILD_STATUS" "$DRONE_REPO_NAME" "$DRONE_BRANCH" "$DRONE_BUILD_NUMBER" "4355b98e-1e72-4678-8dfb-2fc6ad0bf449" "$DRONE_COMMIT_MESSAGE" "$DRONE_COMMIT_AUTHOR" "Test_IRC_WEB" "irc.test.extimaging.com"
when:
status:
- success
- failure
trigger:
branch:
- main
+4910 -6087
View File
File diff suppressed because it is too large Load Diff
+9 -10
View File
@@ -15,18 +15,17 @@
},
"dependencies": {
"@aws-sdk/client-s3": "^3.701.0",
"@cornerstonejs/adapters": "^2.19.7",
"@cornerstonejs/adapters": "4.19.2",
"@cornerstonejs/calculate-suv": "^1.1.0",
"@cornerstonejs/core": "^2.19.7",
"@cornerstonejs/dicom-image-loader": "^2.19.7",
"@cornerstonejs/tools": "^2.19.7",
"@cornerstonejs/core": "^4.19.2",
"@cornerstonejs/dicom-image-loader": "^4.19.2",
"@cornerstonejs/tools": "^4.19.2",
"@fingerprintjs/fingerprintjs": "^4.6.2",
"@icr/polyseg-wasm": "^0.4.0",
"@microsoft/signalr": "^8.0.7",
"@riophae/vue-treeselect": "^0.4.0",
"@vue-office/docx": "^1.6.2",
"@vue-office/excel": "^1.7.11",
"@vue/composition-api": "^1.7.2",
"ali-oss": "^6.17.1",
"axios": "^0.18.1",
"core-js": "^3.8.3",
@@ -60,11 +59,10 @@
"screenfull": "^6.0.2",
"sortablejs": "^1.15.5",
"streamsaver": "^2.0.6",
"svg-sprite-loader": "^4.1.3",
"svgo": "^1.2.2",
"v-viewer": "^1.7.4",
"vcrontab": "^0.3.5",
"vue": "^2.6.14",
"vue": "2.7.16",
"vue-clipboard2": "^0.3.3",
"vue-contextmenujs": "^1.4.11",
"vue-count-to": "^1.0.13",
@@ -98,9 +96,10 @@
"path-browserify": "^1.0.1",
"process": "^0.11.10",
"sass": "^1.63.2",
"sass-loader": "^10.4.1",
"sass-loader": "^16.0.7",
"svg-sprite-loader": "^6.0.11",
"terser-webpack-plugin": "^5.3.10",
"vue-template-compiler": "^2.6.14",
"vue-template-compiler": "2.7.16",
"webpack": "^5.96.1",
"webpack-bundle-analyzer": "^4.10.2"
},
@@ -125,4 +124,4 @@
"not dead",
"not op_mini all"
]
}
}
+1 -2
View File
@@ -115,8 +115,7 @@ export default {
}
},
mounted() {
// this.show = process.env.VUE_APP_OSS_PATH === '/test/dist'
this.show = false
this.show = process.env.VUE_APP_OSS_PATH === '/test/dist'
Vue.prototype.$openI18n = this.openI18n
},
// watch: {
+81
View File
@@ -319,3 +319,84 @@ export function getReportsChartData(param) {
data: param
})
}
// 分割相关
// 获取分割组
export function getSegmentationList(param) {
return request({
url: `/Segmentation/getSegmentationList`,
method: 'post',
data: param
})
}
// 新增修改分割组
export function addOrUpdateSegmentation(param) {
return request({
url: `/Segmentation/addOrUpdateSegmentation`,
method: 'post',
data: param
})
}
// 删除分割组
export function deleteSegmentation(segmentationId) {
return request({
url: `/Segmentation/deleteSegmentation/${segmentationId}`,
method: 'delete'
})
}
// 获取分割
export function getSegmentList(param) {
return request({
url: `/Segmentation/getSegmentList`,
method: 'post',
data: param
})
}
// 新增修改分割
export function addOrUpdateSegment(param) {
return request({
url: `/Segmentation/addOrUpdateSegment`,
method: 'post',
data: param
})
}
// 删除分割
export function deleteSegment(segmentId) {
return request({
url: `/Segmentation/deleteSegment/${segmentId}`,
method: 'delete'
})
}
// 获取分割绑定关系
export function getSegmentBindingList(param) {
return request({
url: `/Segmentation/getSegmentBindingList`,
method: 'post',
data: param
})
}
// 保存分割绑定关系(可用作修改删除)
export function saveSegmentBindingAndAnswer(param) {
return request({
url: `/Segmentation/saveSegmentBindingAndAnswer`,
method: 'post',
data: param
})
}
// 获取表格问题配置
export function getReadingTableQuestionTrialById(params) {
return request({
url: `/ReadingQuestion/getReadingTableQuestionTrialById`,
method: 'post',
params
})
}
// 获取外层问题配置
export function getReadingQuestionTrialById(params) {
return request({
url: `/ReadingQuestion/getReadingQuestionTrialById`,
method: 'post',
params
})
}
+14 -8
View File
@@ -74,10 +74,11 @@ export default {
zzSessionStorage.setItem('lastWorkbench', `${v.path}${query ? '?' : ''}${query}`)
var firstGoIn = this.trialsRouter.children.find(v => { return v.name === 'TrialsPanel' }).children[0]
if (this.trialsTab === '/trials/trials-panel') {
this.$router.replace({ path: `${firstGoIn.path}${query ? '?' : ''}${query}` })
var targetPath = firstGoIn.path
if (firstGoIn.children && firstGoIn.children.length > 0) {
this.$router.replace({ path: `${firstGoIn.children[0].path}${query ? '?' : ''}${query}` })
targetPath = firstGoIn.children[0].path
}
this.$router.replace({ path: `${targetPath}${query ? '?' : ''}${query}` })
}
// document.cookie = 'TrialId=' + this.$route.query.trialId + ';path=/'
},
@@ -112,10 +113,11 @@ export default {
this.getTrialList()
var firstGoIn = this.trialsRouter.children.find(v => { return v.name === 'TrialsPanel' }).children[0]
if (this.trialsTab === '/trials/trials-panel') {
this.$router.replace({ path: `${firstGoIn.path}${query ? '?' : ''}${query}` })
var targetPath = firstGoIn.path
if (firstGoIn.children && firstGoIn.children.length > 0) {
this.$router.replace({ path: `${firstGoIn.children[0].path}${query ? '?' : ''}${query}` })
targetPath = firstGoIn.children[0].path
}
this.$router.replace({ path: `${targetPath}${query ? '?' : ''}${query}` })
}
},
methods: {
@@ -201,15 +203,19 @@ export default {
if (~url.indexOf('?')) {
query = url.split('?')[1]
}
this.$router.push({ path: `${v.name}${query ? '?' : ''}${query}` })
var trialsPanelList = this.trialsRouter.children.find(v => { return v.name === 'TrialsPanel' }).children
var isHasChild = trialsPanelList.find(c => { return c.path === v.name }) && trialsPanelList.find(c => { return c.path === v.name }).children
if (trialsPanelList.find(c => { return c.path === v.name }) && trialsPanelList.find(c => { return c.path === v.name }).tabHiddn) {
var trialsPanelList = this.trialsRouter.children.find(item => { return item.name === 'TrialsPanel' }).children
var currentRoute = trialsPanelList.find(c => { return c.path === v.name })
if (currentRoute && currentRoute.tabHiddn) {
this.$router.push({ path: `${v.name}${query ? '?' : ''}${query}` })
return
}
var isHasChild = currentRoute && currentRoute.children
if (isHasChild && isHasChild.length > 0) {
this.trialsTabChild = isHasChild[0].path
this.$router.push({ path: `${this.trialsTabChild}${query ? '?' : ''}${query}` })
} else {
this.$router.push({ path: `${v.name}${query ? '?' : ''}${query}` })
}
} catch (e) {
console.log(e)
+1 -1
View File
@@ -116,7 +116,7 @@ export default {
display: none !important; /* 隐藏原生 radio 输入,但仍然允许交互 */
}
::v-deep.el-radio:focus:not(.is-focus):not(:active):not(.is-disabled)
::v-deep .el-radio:focus:not(.is-focus):not(:active):not(.is-disabled)
.el-radio__inner {
box-shadow: none !important;
}
@@ -712,12 +712,12 @@ export default {
var studyUid = data.string('x0020000d')
if (!studyUid) return resolve()
var pixelDataElement = data.elements.x7fe00010
if (!pixelDataElement && modality !== 'SR') return resolve()
if (!pixelDataElement && modality !== 'SR' && modality !== 'ECG') return resolve()
var studyIndex = 0
while (
studyIndex < scope.uploadQueues.length &&
scope.uploadQueues[studyIndex].dicomInfo.studyUid !== studyUid &&
(pixelDataElement || modality === 'SR') &&
(pixelDataElement || modality === 'SR' || modality === 'ECG') &&
modality != ''
) {
++studyIndex
@@ -86,16 +86,16 @@ export default {
}
</script>
<style lang="scss" scoped>
::v-deep.el-tabs--left .el-tabs__header.is-left {
::v-deep .el-tabs--left .el-tabs__header.is-left {
margin-right: 0;
}
.uploadDicomAndNonedicom {
::v-deep.el-tabs--border-card>.el-tabs__header .el-tabs__item {
::v-deep .el-tabs--border-card>.el-tabs__header .el-tabs__item {
color: #909399;
}
::v-deep.el-tabs--border-card>.el-tabs__header .el-tabs__item.is-active {
::v-deep .el-tabs--border-card>.el-tabs__header .el-tabs__item.is-active {
color: #428bca;
background-color: #fff;
border-right-color: #dcdfe6;
+1
View File
@@ -0,0 +1 @@
<svg width="24px" height="24px" viewBox="0 0 24 24" class="h-6 w-6"><g id="tool-seg-brush" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><rect id="Rectangle" x="0" y="0" width="24" height="24"></rect><path d="M3.24640621,21.8286833 C3.09173375,21.7551472 2.99513748,21.5971513 3.00018895,21.4259625 C3.00524042,21.2547737 3.11098486,21.1027485 3.26972426,21.0384606 C5.3260304,20.2059201 4.66362518,17.8620247 5.27421252,16.6088957 C6.02197747,15.1026514 7.84114758,14.4766383 9.35746132,15.2037675 C13.9485253,17.4422999 8.48346644,24.3211232 3.24640621,21.8286833 Z" id="Path" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M21.5968442,2.302022 C21.1256527,1.8826075 20.4094461,1.90228142 19.9619901,2.34693083 L9.69255027,12.5887345 C11.0536437,13.0051578 12.1843437,13.9616637 12.8206229,15.2349008 L21.7410706,3.93687606 C22.1347378,3.44008272 22.0714081,2.72222021 21.5968442,2.302022 Z" id="Path" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+12
View File
@@ -0,0 +1,12 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" class="h-6 w-6" color="#fff">
<path
d="M10.3945 12.6113C10.043 12.6113 9.78516 12.3945 9.78516 12.0371C9.78516 11.6855 10.043 11.4688 10.3945 11.4688H13.2012C13.5527 11.4688 13.8164 11.6855 13.8164 12.0371C13.8164 12.3945 13.5527 12.6113 13.2012 12.6113H10.3945Z"
fill="currentColor"></path>
<path opacity="0.5"
d="M18.312 6.84473C21.8473 8.88583 22.9225 13.6424 20.7134 17.4687C18.5042 21.2951 13.8473 22.7422 10.312 20.7011C9.51661 20.2419 8.84737 19.6439 8.31042 18.9512C11.3012 19.3598 14.4827 17.8251 16.1946 14.8598C17.9067 11.8945 17.6457 8.3712 15.7963 5.98538C16.6645 6.10409 17.5167 6.38556 18.312 6.84473Z"
fill="currentColor"></path>
<circle cx="13.7852" cy="13.4688" r="8" stroke="currentColor"></circle>
<path
d="M4.50586 8.22266C4.14844 8.22266 3.92578 7.9707 3.92578 7.60742V5.68555H2.10352C1.74609 5.68555 1.5 5.46875 1.5 5.11719C1.5 4.76562 1.74609 4.54297 2.10352 4.54297H3.92578V2.61523C3.92578 2.25195 4.14844 2 4.50586 2C4.85742 2 5.08008 2.25195 5.08008 2.61523V4.54297H6.89648C7.25391 4.54297 7.50586 4.76562 7.50586 5.11719C7.50586 5.46875 7.25391 5.68555 6.89648 5.68555H5.08008V7.60742C5.08008 7.9707 4.85742 8.22266 4.50586 8.22266Z"
fill="currentColor"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

+1
View File
@@ -0,0 +1 @@
<svg width="18px" height="18px" viewBox="0 0 18 18" class="text-primary"><g id="view-fill" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><g id="Group-13"><rect id="Rectangle" x="0" y="0" width="18" height="18"></rect><rect id="Rectangle" fill="currentColor" x="2" y="2" width="14" height="14" rx="1"></rect></g></g></svg>

After

Width:  |  Height:  |  Size: 338 B

+1
View File
@@ -0,0 +1 @@
<svg width="18px" height="18px" viewBox="0 0 18 18" class="text-primary"><g id="view-outline-fill" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><g id="Group-13"><rect id="Rectangle" x="0" y="0" width="18" height="18"></rect><rect id="Rectangle" stroke="currentColor" x="1.5" y="1.5" width="15" height="15" rx="1"></rect><rect id="Rectangle" fill="currentColor" x="3.5" y="3.5" width="11" height="11" rx="1"></rect></g></g></svg>

After

Width:  |  Height:  |  Size: 446 B

+1
View File
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1774331916694" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1627" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M198.4 960A134.4 134.4 0 0 1 64 825.6V198.4A134.4 134.4 0 0 1 198.4 64h268.8a44.8 44.8 0 0 1 44.8 44.8 44.8 44.8 0 0 1-44.8 44.8H198.4a44.8 44.8 0 0 0-44.8 44.8v627.2a44.8 44.8 0 0 0 44.8 44.8h627.2a44.8 44.8 0 0 0 44.8-44.8V556.8a44.8 44.8 0 0 1 44.8-44.8 44.8 44.8 0 0 1 44.8 44.8v268.8A134.4 134.4 0 0 1 825.6 960z m281.984-416.384a44.8 44.8 0 0 1 0-63.296L807.232 153.6H646.464a44.8 44.8 0 0 1-44.8-44.8 44.8 44.8 0 0 1 44.8-44.8h267.264a44.8 44.8 0 0 1 28.288 8.896l1.408 1.152 0.64 0.512 1.024 0.96a41.216 41.216 0 0 1 3.136 3.072l0.896 1.088 0.512 0.64 1.152 1.344a44.8 44.8 0 0 1 8.832 28.288V377.28a44.8 44.8 0 0 1-44.8 44.8 44.8 44.8 0 0 1-44.8-44.8V216.512l-326.4 326.4a44.8 44.8 0 0 1-32 13.248A44.8 44.8 0 0 1 480 543.616z" p-id="1628" fill="#2c2c2c"></path></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

+1
View File
@@ -0,0 +1 @@
<svg width="18px" height="18px" viewBox="0 0 18 18" class="text-primary"><g id="view-outline" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><g id="Group-13"><rect id="Rectangle" x="0" y="0" width="18" height="18"></rect><rect id="Rectangle" stroke="currentColor" x="1.5" y="1.5" width="15" height="15" rx="1"></rect></g></g></svg>

After

Width:  |  Height:  |  Size: 347 B

+4 -2
View File
@@ -56,8 +56,10 @@ export default {
</script>
<style lang="scss" scoped>
@import "~@/styles/mixin.scss";
@import "~@/styles/variables.module.scss";
@use "@/styles/mixin.scss" as *;
@use "@/styles/variables.module.scss" as *;
// @import "~@/styles/mixin.scss";
// @import "~@/styles/variables.module.scss";
.app-wrapper {
@include clearfix;
-6
View File
@@ -156,12 +156,6 @@ export const constantRoutes = [
hidden: true,
component: () => import('@/views/trials/trials-panel/reading/dicoms/components/Fusion/PetCt')
},
{
path: '/fusion',
name: 'fusion',
hidden: true,
component: () => import('@/views/trials/trials-panel/reading/dicoms/components/Fusion/demo/index')
},
{
path: '/historyScreenshot',
+46 -39
View File
@@ -1,15 +1,16 @@
@import "./variables.module.scss";
@import "./mixin.scss";
@import "./transition.scss";
@import "./element-ui.scss";
@import "./sidebar.scss";
@use "./variables.module.scss";
@use "./mixin.scss";
@use "./transition.scss";
@use "./element-ui.scss";
@use "./sidebar.scss";
body {
height: 100%;
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB",
"Microsoft YaHei", "微软雅黑", Arial, sans-serif;
// font-family:"Helvetica Neue", Helvetica, Arial, sans-serif;
// font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
// "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial,
@@ -205,133 +206,139 @@ body .el-table th.gutter {
.el-descriptions {
box-sizing: border-box;
font-size: 14px;
color: #303133
color: #303133;
}
.el-descriptions__header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px
margin-bottom: 20px;
}
.el-descriptions__title {
font-size: 16px;
font-weight: 700
font-weight: 700;
}
.el-descriptions__body {
color: #606266;
background-color: #fff
background-color: #fff;
}
.el-descriptions__body .el-descriptions__table {
border-collapse: collapse;
width: 100%;
table-layout: fixed
table-layout: fixed;
}
.el-descriptions__body .el-descriptions__table .el-descriptions-item__cell {
box-sizing: border-box;
text-align: left;
font-weight: 400;
line-height: 1.5
line-height: 1.5;
}
.el-descriptions__body .el-descriptions__table .el-descriptions-item__cell.is-left {
text-align: left
.el-descriptions__body
.el-descriptions__table
.el-descriptions-item__cell.is-left {
text-align: left;
}
.el-descriptions__body .el-descriptions__table .el-descriptions-item__cell.is-center {
text-align: center
.el-descriptions__body
.el-descriptions__table
.el-descriptions-item__cell.is-center {
text-align: center;
}
.el-descriptions__body .el-descriptions__table .el-descriptions-item__cell.is-right {
text-align: right
.el-descriptions__body
.el-descriptions__table
.el-descriptions-item__cell.is-right {
text-align: right;
}
.el-descriptions .is-bordered {
table-layout: auto
table-layout: auto;
}
.el-descriptions .is-bordered .el-descriptions-item__cell {
border: 1px solid #ebeef5;
padding: 12px 10px
padding: 12px 10px;
}
.el-descriptions :not(.is-bordered) .el-descriptions-item__cell {
padding-bottom: 12px
padding-bottom: 12px;
}
.el-descriptions--medium.is-bordered .el-descriptions-item__cell {
padding: 10px
padding: 10px;
}
.el-descriptions--medium:not(.is-bordered) .el-descriptions-item__cell {
padding-bottom: 10px
padding-bottom: 10px;
}
.el-descriptions--small {
font-size: 12px
font-size: 12px;
}
.el-descriptions--small.is-bordered .el-descriptions-item__cell {
padding: 8px 10px
padding: 8px 10px;
}
.el-descriptions--small:not(.is-bordered) .el-descriptions-item__cell {
padding-bottom: 8px
padding-bottom: 8px;
}
.el-descriptions--mini {
font-size: 12px
font-size: 12px;
}
.el-descriptions--mini.is-bordered .el-descriptions-item__cell {
padding: 6px 10px
padding: 6px 10px;
}
.el-descriptions--mini:not(.is-bordered) .el-descriptions-item__cell {
padding-bottom: 6px
padding-bottom: 6px;
}
.el-descriptions-item {
vertical-align: top
vertical-align: top;
}
.el-descriptions-item__container {
display: flex
display: flex;
}
.el-descriptions-item__container .el-descriptions-item__content,
.el-descriptions-item__container .el-descriptions-item__label {
display: inline-flex;
align-items: baseline
align-items: baseline;
}
.el-descriptions-item__container .el-descriptions-item__content {
flex: 1
flex: 1;
}
.el-descriptions-item__label.has-colon:after {
content: ":";
position: relative;
top: -.5px
top: -0.5px;
}
.el-descriptions-item__label.is-bordered-label {
font-weight: 700;
color: #909399;
background: #fafafa
background: #fafafa;
}
.el-descriptions-item__label:not(.is-bordered-label) {
margin-right: 10px
margin-right: 10px;
}
.el-descriptions-item__content {
word-break: break-word;
overflow-wrap: break-word
overflow-wrap: break-word;
}
.feedBack-box {
@@ -350,7 +357,7 @@ body .el-table th.gutter {
top: 0;
width: 100%;
height: 100%;
opacity: .5;
opacity: 0.5;
background: #000;
z-index: 3999;
}
@@ -371,4 +378,4 @@ body .el-table th.gutter {
height: 20px !important;
vertical-align: -0.4em !important;
cursor: pointer;
}
}
+1 -1
View File
@@ -8366,7 +8366,7 @@
for (var i = 0; i < valarr.length; i++) {
var checkValue = valarr[i],
checklen = lengths[i],
checklen = lengths[i] || 0,
isString = false,
displaylen = checklen;
+2 -2
View File
@@ -965,8 +965,8 @@ export default {
}
</script>
<style lang="scss" scoped>
.imageBox {
<style lang="scss">
.viewerContainer .imageBox {
position: relative;
i {
+87 -90
View File
@@ -158,108 +158,105 @@
<div class="viewerSidethumbinner">
<el-collapse v-model="relationActiveName" @change="handelRelationActiveChange">
<template v-for="item in relationStudyListByVisitName">
<div :key="`${item.VisitName}`">
<div v-show="item.VisitName" class="text-desc" style="background-color: #1f1f1f;">
{{ item.VisitName }}
</div>
<template v-for="(study, studyIndex) in relationStudyList">
<el-collapse-item :key="`${study.StudyId}`" :name="`${study.StudyId}`"
v-if="study.VisitName === item.VisitName">
<template slot="title">
<div v-for="item in relationStudyListByVisitName" :key="`${item.VisitName}`">
<div v-show="item.VisitName" class="text-desc" style="background-color: #1f1f1f;">
{{ item.VisitName }}
</div>
<template v-for="(study, studyIndex) in relationStudyList">
<el-collapse-item :key="`${study.StudyId}`" :name="`${study.StudyId}`"
v-if="study.VisitName === item.VisitName">
<template slot="title">
<div class="text-desc">
{{ study.StudyCode }}
</div>
<!-- <div v-show="study.Description" class="text-desc">
{{ study.Description }}
</div> -->
<div v-show="study.SeriesCount" class="text-desc">
{{ study.Modalities }} : {{ study.SeriesCount }} Series
</div>
</template>
<div v-show="study.Description" class="text-desc" style="background-color: #1f1f1f;">
{{ study.Description }}
<div class="text-desc">
{{ study.StudyCode }}
</div>
<div class="viewerSidethumbs ps" style="position: relative;">
<div class="viewerSidethumbinner">
<div v-for="(seriesItem, index) in study.seriesList" :key="seriesItem.seriesId">
<div class="viewernavigatorwrapper"
style="position: relative;border:1px solid #434343;" series-type="relation"
@click="showRelationSeriesImage($event, seriesItem, studyIndex, index)">
<div class="imageBox" style="width: 72px;height:72px;">
<img class="image-preview" :src="seriesItem.previewImageUrl"
crossorigin="anonymous" alt="" style="width: 72px;height:72px;" fit="fill" />
<i class="el-icon-refresh" :title="$t('tip:refreshImage')"
@click.stop="refreshImage(item)"></i>
</div>
<!-- <div v-show="study.Description" class="text-desc">
{{ study.Description }}
</div> -->
<div class="viewernavitextwrapper">
<div
style="padding: 1px 5px 1px 1px;display: flex;justify-content: space-between;">
<div v-if="seriesItem.keySeries" style="color:red">
Key Images
</div>
<div v-else>#{{ seriesItem.seriesNumber }}</div>
<div v-if="seriesItem.isExistMutiFrames && seriesItem.instanceCount > 1">
<el-popover placement="right-start" trigger="click"
popper-class="instance_frame_wrapper">
<div class="frame_list">
<div v-for="(instance, idx) in seriesItem.instanceInfoList"
:key="instance.Id" class="frame_content"
:style="{ 'margin-bottom': idx < seriesItem.instanceInfoList.length - 1 ? '5px' : '0px' }"
@click="showMultiFrames(studyIndex, seriesItem, index, instance)">
<div>
<div>{{ instance.InstanceNumber }}</div>
<div>{{ `${instance.NumberOfFrames > 0 ? instance.NumberOfFrames : 1}
frame`
}}</div>
</div>
<div v-show="study.SeriesCount" class="text-desc">
{{ study.Modalities }} : {{ study.SeriesCount }} Series
</div>
</template>
<div v-show="study.Description" class="text-desc" style="background-color: #1f1f1f;">
{{ study.Description }}
</div>
<div class="viewerSidethumbs ps" style="position: relative;">
<div class="viewerSidethumbinner">
<div v-for="(seriesItem, index) in study.seriesList" :key="seriesItem.seriesId">
<div class="viewernavigatorwrapper"
style="position: relative;border:1px solid #434343;" series-type="relation"
@click="showRelationSeriesImage($event, seriesItem, studyIndex, index)">
<div class="imageBox" style="width: 72px;height:72px;">
<img class="image-preview" :src="seriesItem.previewImageUrl"
crossorigin="anonymous" alt="" style="width: 72px;height:72px;" fit="fill" />
<i class="el-icon-refresh" :title="$t('tip:refreshImage')"
@click.stop="refreshImage(item)"></i>
</div>
<div class="viewernavitextwrapper">
<div
style="padding: 1px 5px 1px 1px;display: flex;justify-content: space-between;">
<div v-if="seriesItem.keySeries" style="color:red">
Key Images
</div>
<div v-else>#{{ seriesItem.seriesNumber }}</div>
<div v-if="seriesItem.isExistMutiFrames && seriesItem.instanceCount > 1">
<el-popover placement="right-start" trigger="click"
popper-class="instance_frame_wrapper">
<div class="frame_list">
<div v-for="(instance, idx) in seriesItem.instanceInfoList"
:key="instance.Id" class="frame_content"
:style="{ 'margin-bottom': idx < seriesItem.instanceInfoList.length - 1 ? '5px' : '0px' }"
@click="showMultiFrames(studyIndex, seriesItem, index, instance)">
<div>
<div>{{ instance.InstanceNumber }}</div>
<div>{{ `${instance.NumberOfFrames > 0 ? instance.NumberOfFrames : 1}
frame`
}}</div>
</div>
</div>
<i slot="reference" class="el-icon-connection"
style="font-size: 15px;cursor: pointer;" />
</el-popover>
</div>
</div>
</div>
<i slot="reference" class="el-icon-connection"
style="font-size: 15px;cursor: pointer;" />
</el-popover>
</div>
<div v-show="seriesItem.instanceCount" style="padding: 1px;">
{{ seriesItem.modality }}: {{ seriesItem.instanceCount }} image
</div>
<div v-show="seriesItem.sliceThickness" style="padding: 1px;">
T: {{ seriesItem.sliceThickness }}
</div>
<el-tooltip v-show="seriesItem.description" class="item" effect="dark"
:content="seriesItem.description" placement="bottom">
<div v-show="seriesItem.description"
style="width: 120px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;padding: 1x;">
{{ seriesItem.description }}
</div>
</el-tooltip>
</div>
<div v-show="seriesItem.instanceCount" style="padding: 1px;">
{{ seriesItem.modality }}: {{ seriesItem.instanceCount }} image
</div>
<div v-show="seriesItem.sliceThickness" style="padding: 1px;">
T: {{ seriesItem.sliceThickness }}
</div>
<el-tooltip v-show="seriesItem.description" class="item" effect="dark"
:content="seriesItem.description" placement="bottom">
<div v-show="seriesItem.description"
style="width: 120px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;padding: 1x;">
{{ seriesItem.description }}
</div>
</el-tooltip>
</div>
<!-- <el-image
class="image-preview"
style="height:72px;width:72px;"
:src="seriesItem.previewImageUrl"
fit="fill"
/> -->
<div
v-if="seriesItem.prefetchInstanceCount > 0 && seriesItem.prefetchInstanceCount < seriesItem.instanceCount * 100">
<el-progress
:percentage="parseInt((seriesItem.prefetchInstanceCount / seriesItem.instanceCount).toFixed(2))" />
</div>
</div>
<!-- <el-image
class="image-preview"
style="height:72px;width:72px;"
:src="seriesItem.previewImageUrl"
fit="fill"
/> -->
<div
v-if="seriesItem.prefetchInstanceCount > 0 && seriesItem.prefetchInstanceCount < seriesItem.instanceCount * 100">
<el-progress
:percentage="parseInt((seriesItem.prefetchInstanceCount / seriesItem.instanceCount).toFixed(2))" />
</div>
</div>
</div>
</el-collapse-item>
</template>
</div>
</template>
</div>
</el-collapse-item>
</template>
</div>
</el-collapse>
</div>
@@ -1012,7 +1009,7 @@ export default {
</script>
<style lang="scss">
.imageBox {
.viewerContainer .imageBox {
position: relative;
i {
@@ -278,7 +278,7 @@ export default {
padding: 5px 20px;
margin-top: 5px;
background: #fff;
::v-deep.el-form-item {
::v-deep .el-form-item {
padding-bottom: 20px;
}
@@ -292,10 +292,10 @@ export default {
}
}
.form-label-width{
::v-deep.el-form-item__label{
::v-deep .el-form-item__label{
width: 140px;
}
::v-deep.el-form-item__content{
::v-deep .el-form-item__content{
margin-left: 140px;
}
}
@@ -304,7 +304,7 @@ export default {
text-align: center;
}
::v-deep.el-form-item {
::v-deep .el-form-item {
margin-bottom: 0px;
padding-top: 5px;
border-bottom: 1px solid #f5f7fa;
@@ -156,7 +156,7 @@ export default {
<style lang="scss" scoped>
.equipment_form_content{
padding: 0 10px;
::v-deep.el-form-item {
::v-deep .el-form-item {
margin-bottom: 0px;
padding: 5px 0 20px 0;
border-bottom: 1px solid #f5f7fa;
@@ -295,7 +295,7 @@ export default {
<style lang="scss" scoped>
.participants_form_content{
padding: 0 10px;
::v-deep.el-form-item {
::v-deep .el-form-item {
margin-bottom: 0px;
padding: 5px 0 20px 0;
border-bottom: 1px solid #f5f7fa;
@@ -309,7 +309,7 @@ export default {
<style lang="scss" scoped>
.participants_form_content{
padding: 0 10px;
::v-deep.el-form-item {
::v-deep .el-form-item {
margin-bottom: 0px;
padding: 5px 0 20px 0;
border-bottom: 1px solid #f5f7fa;
+2 -2
View File
@@ -381,7 +381,7 @@ export default {
padding: 5px 20px;
margin-top: 5px;
background: #fff;
::v-deep.el-form-item {
::v-deep .el-form-item {
padding-bottom: 20px;
}
}
@@ -398,7 +398,7 @@ export default {
text-align: center;
}
::v-deep.el-form-item {
::v-deep .el-form-item {
margin-bottom: 0px;
padding-top: 5px;
border-bottom: 1px solid #f5f7fa;
+3 -3
View File
@@ -357,7 +357,7 @@ export default {
overflow-y: auto;
}
::v-deep.el-card__body {
::v-deep .el-card__body {
padding: 10px;
}
@@ -365,13 +365,13 @@ export default {
// padding: 10px 20px 20px 20px;
// }
::v-deep.full-dialog-container {
::v-deep .full-dialog-container {
.el-dialog__body {
height: calc(100% - 80px);
}
}
::v-deep.dialog-container {
::v-deep .dialog-container {
// margin-top: 50px !important;
width: 75%;
height: 80%;
@@ -44,7 +44,7 @@ export default {
overflow: hidden;
display: flex;
flex-direction: column;
::v-deep.el-card__body {
::v-deep .el-card__body {
padding: 0px;
}
.trials-tab{
+4 -2
View File
@@ -28,8 +28,10 @@ export default {
</script>
<style lang="scss" scoped>
@import "~@/styles/mixin.scss";
@import "~@/styles/variables.module.scss";
// @import "~@/styles/mixin.scss";
// @import "~@/styles/variables.module.scss";
@use "@/styles/mixin.scss" as *;
@use "@/styles/variables.module.scss" as *;
.trials-wrapper {
@include clearfix;
@@ -2928,12 +2928,12 @@ export default {
}
.series-table {
::v-deep.el-table {
::v-deep .el-table {
background-color: #1e1e1e !important;
color: #dfdfdf;
}
::v-deep.el-table td.el-table__cell,
::v-deep .el-table td.el-table__cell,
.el-table th.el-table__cell.is-leaf {
border-bottom: 1px solid #dfdfdf;
}
@@ -2944,14 +2944,14 @@ export default {
background-color: #1e1e1e;
}
::v-deep.el-table__header-wrapper {
::v-deep .el-table__header-wrapper {
th {
background-color: #1e1e1e !important;
color: #dfdfdf;
}
}
::v-deep.el-table__body-wrapper {
::v-deep .el-table__body-wrapper {
tr {
background-color: #1e1e1e !important;
color: #dfdfdf;
@@ -2962,7 +2962,7 @@ export default {
}
}
::v-deep.el-table__empty-block {
::v-deep .el-table__empty-block {
background-color: #1e1e1e !important;
}
}
@@ -319,23 +319,23 @@ export default {
</script>
<style lang="scss" scoped>
.series-table{
::v-deep.el-table{
::v-deep .el-table{
background-color: #1e1e1e !important;
color: #dfdfdf;
}
::v-deep.el-table td.el-table__cell, .el-table th.el-table__cell.is-leaf{
::v-deep .el-table td.el-table__cell, .el-table th.el-table__cell.is-leaf{
border-bottom: 1px solid #dfdfdf;
}
.el-table--border::after, .el-table--group::after, .el-table::before{
background-color: #1e1e1e;
}
::v-deep.el-table__header-wrapper{
::v-deep .el-table__header-wrapper{
th{
background-color: #1e1e1e !important;
color: #dfdfdf;
}
}
::v-deep.el-table__body-wrapper{
::v-deep .el-table__body-wrapper{
tr{
background-color: #1e1e1e !important;
color: #dfdfdf;
@@ -344,7 +344,7 @@ export default {
background-color: #1e1e1e !important;
}
}
::v-deep.el-table__empty-block{
::v-deep .el-table__empty-block{
background-color: #1e1e1e !important;
}
}
@@ -140,46 +140,8 @@
</div>
</el-tooltip>
<!-- 伪彩 -->
<el-tooltip class="item" effect="dark" :content="$t('trials:lugano:button:colormap')" placement="bottom">
<div class="colorBar" style="display:flex;justify-content: flex-start;align-items: center;position: relative;"
@mouseleave="isSlideMoving = false">
<div class="tool-wrapper" style="margin-right:0px" @click.stop="showColorBarPanel($event)"
@mouseleave="handleColorBarMouseout">
<div>
<!-- <canvas id="colorBarCanvas" /> -->
<div class="dropdown">
<div id="colorBar" class="icon" style="display: flex;align-items: center;width:266px">
<canvas id="colorBarCanvas" />
</div>
<!-- 伪彩 -->
<div class="text">{{ $t('trials:lugano:button:colormap') }}</div>
<div class="dropdown-content" style="width:266px">
<ul>
<li v-for="(colorMap, index) in colorMaps" :key="colorMap"
style="display: flex;align-items: center;margin-bottom:5px;padding:0 5px;justify-content: space-between;"
:class="{ activeLi: rgbPresetName === colorMap }" @click="setColorMap(colorMap)">
<canvas :id="`colorBarCanvas${index}`" />
<span style="margin-left:5px;font-size: 10px;">{{ colorMap }}</span>
</li>
</ul>
</div>
</div>
</div>
</div>
<div style="margin-left:-1px;border: 1px solid #424242;">
<el-input v-model="range" size="mini" style="width:120px" maxlength="3"
oninput="if(value){value=value.replace(/[^\d]/g,'')} if(value<=0){value=''}" @change="upperRangeChange">
<template slot="append">g/ml</template>
</el-input>
</div>
<div id="slider" style="position: absolute;left: 6px;top:5px;cursor: pointer;">
<div id="sliderBox" class="slider" style="height: 17px;width: 10px;background-color: #909399;" />
<div id="slider-position" style="color:#ddd;font-size: 12px;">0</div>
</div>
</div>
</el-tooltip>
<colorMap v-show="isFusion" ref="colorMap" :unit="fusionOverlayModality === 'NM' ? 'counts' : 'g/ml'"
:modality="fusionOverlayModality" @setColorMap="setColorMap" @voiChange="voiChange" />
<!-- 截屏 -->
<!-- <el-tooltip class="item" effect="dark" :content="`${$t('trials:reading:button:screenShot')}`" placement="bottom">
@@ -217,15 +179,14 @@
<Viewport ref="CT_AXIAL" :index="1" :active-index="activeIndex"
:is-reading-show-subject-info="isReadingShowSubjectInfo" :series-info="ctSeries"
:rendering-engine-id="renderingEngineId" viewport-id="CT_AXIAL" :volume="ctVolume"
:measure-datas="measureDatas" :style="1 === activeIndex ? viewportStyle : {}"
@upperRangeChange="upperRangeChange" />
:measure-datas="measureDatas" :style="1 === activeIndex ? viewportStyle : {}" />
<Viewport ref="PT_AXIAL" :index="2" :active-index="activeIndex"
:is-reading-show-subject-info="isReadingShowSubjectInfo" :series-info="petSeries"
:rendering-engine-id="renderingEngineId" viewport-id="PT_AXIAL" :volume="ptVolume"
:measure-datas="measureDatas" :style="2 === activeIndex ? viewportStyle : {}"
@upperRangeChange="upperRangeChange" />
<Viewport ref="FUSION_AXIAL" :index="3" :active-index="activeIndex"
:is-reading-show-subject-info="isReadingShowSubjectInfo" :series-info="petSeries"
:is-reading-show-subject-info="isReadingShowSubjectInfo" :series-info="petSeries" :ct-series-info="ctSeries"
:rendering-engine-id="renderingEngineId" viewport-id="FUSION_AXIAL" :volume="ptVolume"
:measure-datas="measureDatas" :rgb-preset-name="rgbPresetName" :style="3 === activeIndex ? viewportStyle : {}"
@upperRangeChange="upperRangeChange" />
@@ -306,6 +267,7 @@ import vtkPiecewiseFunction from '@kitware/vtk.js/Common/DataModel/PiecewiseFunc
import { mat4, vec3 } from 'gl-matrix'
import html2canvas from 'html2canvas'
import readingChart from '@/components/readingChart'
import colorMap from '@/views/trials/trials-panel/reading/dicoms3D/components/colorMap.vue'
// import vtkColorTransferFunction from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction'
// import vtkMath from '@kitware/vtk.js/Common/Core/Math'
// import CircleROITool from './tools/CircleROITool'
@@ -395,7 +357,8 @@ export default {
TableQuestions,
CustomWwwcForm,
FusionForm,
readingChart
readingChart,
colorMap
},
data() {
return {
@@ -441,6 +404,8 @@ export default {
upper: 6,
isSlideMoving: false,
viewportStyle: {},
isFusion: true,
fusionOverlayModality: 'PT',
defaultWwwc: [
{ label: this.$t('trials:reading:button:wwwcDefault'), val: -1, ww: null, wc: null }, // 默认值
{ label: this.$t('trials:reading:button:wwwcCustom'), val: 0, ww: null, wc: null }, // 自定义
@@ -458,7 +423,9 @@ export default {
activeCanvasWW: null,
activeCanvasWC: null,
fusion: { visible: false }, // 历史记录融合调窗
screenshotWindow: null
screenshotWindow: null,
hasVoiChanged: false,
lastUpper: null
// initFirstAnnotation:false
}
},
@@ -512,13 +479,13 @@ export default {
})
this.colorMaps = getColormapNames()
this.colorMaps.unshift('hsv')
this.$nextTick(() => {
this.renderColorMaps()
this.handleElementsClick()
this.initPage()
this.upperRangeChange(this.range)
this.initSlider()
})
this.$nextTick(() => {
// this.renderColorMaps()
this.handleElementsClick()
this.initPage()
// this.upperRangeChange(this.range)
// this.initSlider()
})
FusionEvent.$on('getAnnotations', () => {
console.log('getAnnotations')
@@ -744,6 +711,7 @@ export default {
return new Promise(resolve => {
getDicomSeriesInfo({ seriesId }).then(res => {
var series = res.Result
this.fusionOverlayModality = series.Modality
var imageIds = []
var instanceList = []
series.InstanceInfoList.forEach(instance => {
@@ -1057,7 +1025,7 @@ export default {
// volume to use for the WindowLevelTool for the fusion viewports
fusionToolGroup.addTool(WindowLevelTool.toolName, {
volumeId: ptVolumeId
volumeId: ptVolumeId2
});
[ctToolGroup, ptToolGroup, fusionToolGroup].forEach((toolGroup) => {
@@ -1627,148 +1595,141 @@ export default {
var slider = document.getElementById('slider')
var sliderBox = document.getElementById('sliderBox')
var container = document.getElementById('colorBarCanvas')
slider.addEventListener('mousedown', () => {
if (!slider || !sliderBox || !container) return
slider.addEventListener('mousedown', (e) => {
this.isSlideMoving = true
e.stopPropagation()
// document.onselectstart = function() { return false }// 解决拖动会选中文字的问题
// document.ondragstart = function() { return false }
})
document.addEventListener('mousemove', (e) => {
if (this.isSlideMoving) {
var containerWidth = container.clientWidth
var colorBarContainer = document.getElementById('colorBar')
if (!colorBarContainer) return
var containerWidth = colorBarContainer.clientWidth
var sliderWidth = sliderBox.clientWidth
var maxLeft = containerWidth - sliderWidth
var left = e.clientX - container.getBoundingClientRect().left
var position = null
position = left
// 获取相对于整个父容器的外边距位置
var rect = colorBarContainer.getBoundingClientRect()
var left = e.clientX - rect.left - (sliderWidth / 2)
if (left < 0) {
left = 6
position = 0
left = 0
} else if (left > maxLeft) {
left = maxLeft + 6
position = maxLeft
left = maxLeft
}
slider.style.left = left + 'px'
var positionValue = document.getElementById('slider-position')
var upper = this.range
position = parseInt((position / maxLeft) * upper)
positionValue.textContent = position
var position = parseInt((left / maxLeft) * upper)
if (positionValue) {
positionValue.textContent = position
}
this.upper = position
this.voiChange(position)
if (this.sliderTimeout) {
cancelAnimationFrame(this.sliderTimeout)
}
this.sliderTimeout = requestAnimationFrame(() => {
this.voiChange(position)
})
}
})
document.addEventListener('mouseup', () => {
this.isSlideMoving = false
document.addEventListener('mouseup', (e) => {
if (this.isSlideMoving) {
this.isSlideMoving = false
if (this.sliderTimeout) {
cancelAnimationFrame(this.sliderTimeout)
}
this.voiChange(this.upper)
}
// document.onselectstart = null
// document.ondragstart = null
})
},
upperRangeChange(v) {
if (v === 0 || v < this.upper) {
return
}
var sliderBox = document.getElementById('sliderBox')
var container = document.getElementById('colorBarCanvas')
var containerWidth = container.clientWidth
var sliderWidth = sliderBox.clientWidth
var maxLeft = containerWidth - sliderWidth
var left = (this.upper / this.range) * maxLeft
if (left < 0) {
left = 6
} else if (left >= maxLeft) {
left = maxLeft + 6
}
var slider = document.getElementById('slider')
slider.style.left = left + 'px'
var positionValue = document.getElementById('slider-position')
positionValue.textContent = this.upper
},
renderColorMaps() {
this.createColorBar(this.rgbPresetName, 'colorBarCanvas', 256, 15)
this.$refs['FUSION_AXIAL'].setPreset(this.rgbPresetName)
this.colorMaps.forEach((e, index) => {
this.createColorBar(e, `colorBarCanvas${index}`, 180, 15)
})
},
getVOIRange(viewportId, volumeId) {
const defaultImageRange = { lower: -1000, upper: 1000 }
const viewport = (
renderingEngine.getViewport(viewportId)
)
const volumeActor = volumeId
? viewport.getActor(volumeId)
: viewport.getDefaultActor()
if (!volumeActor || !csUtils.isImageActor(volumeActor)) {
return defaultImageRange
}
const voiRange = (volumeActor.actor)
.getProperty()
.getRGBTransferFunction(0)
.getRange()
return voiRange[0] === 0 && voiRange[1] === 0
? defaultImageRange
: { lower: voiRange[0], upper: voiRange[1] }
},
voiChange(v) {
// console.log(this.lastUpper, this.hasVoiChanged, v)
if (this.lastUpper === v && this.hasVoiChanged) return;
this.lastUpper = v;
this.hasVoiChanged = true;
let viewportIds = ['FUSION_AXIAL', 'PT_AXIAL', 'PET_MIP_CORONAL']
viewportIds.map(viewportId => {
// const volumeId = viewportId === 'viewportId' ? ptVolumeId : ctVolumeId
const volumeId = ptVolumeId
const volumeId = viewportId === 'FUSION_AXIAL' ? ptVolumeId2 : ptVolumeId
const voiRange = { lower: 0, upper: v }
const viewport = (
renderingEngine.getViewport(viewportId)
)
if (!viewport) return
// const volumeId = viewport.getVolumeId()
// const viewportsContainingVolumeUID = csUtils.getViewportsWithVolumeId(
// volumeId,
// viewport.renderingEngineId
// )
viewport.setProperties({ voiRange }, volumeId)
viewport.render()
this.$refs[viewport.id].setWwWc()
// viewportsContainingVolumeUID.forEach((vp) => {
// vp.render()
// this.$refs[vp.id].setWwWc()
// console.log(vp.id)
// })
this.$refs[viewport.id] && this.$refs[viewport.id].setWwWc()
})
// 反向同步到 colorMap 组件,更新滑块
if (this.$refs.colorMap) {
this.$refs.colorMap.changeVoi(v)
}
},
// async setColorMap(rgbPresetName) {
// this.rgbPresetName = rgbPresetName
// if (this.$refs.FUSION_AXIAL) {
// this.$refs.FUSION_AXIAL.setPreset(this.rgbPresetName)
// this.$refs.FUSION_AXIAL.renderColorBar(this.rgbPresetName)
// }
// const viewport = getRenderingEngine(renderingEngineId)?.getViewport('FUSION_AXIAL')
// if (!viewport) return
// const actorEntry = viewport.getActors()?.find(e => String(e.uid || '').includes('PT_VOLUME_ID2'))
// if (!actorEntry?.actor) return
// const preset = rgbPresetName === 'hsv' ? vtkColorMaps.getPresetByName(rgbPresetName) : getColormap(rgbPresetName)
// const cfun = vtkColorTransferFunction.newInstance()
// const rgbPoints = preset?.RGBPoints
// if (Array.isArray(rgbPoints)) {
// for (let i = 0; i < rgbPoints.length; i += 4) {
// cfun.addRGBPoint(rgbPoints[i], rgbPoints[i + 1], rgbPoints[i + 2], rgbPoints[i + 3])
// }
// }
// cfun.setMappingRange(0, 5)
// actorEntry.actor.getProperty().setRGBTransferFunction(0, cfun)
// const ofun = vtkPiecewiseFunction.newInstance()
// ofun.addPoint(0, 0.0)
// ofun.addPoint(0.1, 0.9)
// ofun.addPoint(5, 1.0)
// actorEntry.actor.getProperty().setScalarOpacity(0, ofun)
// viewport.render()
// },
async setColorMap(rgbPresetName) {
this.rgbPresetName = rgbPresetName
let viewports = ['FUSION_AXIAL', 'PT_AXIAL', 'PET_MIP_CORONAL']
let viewports = ['FUSION_AXIAL']
viewports.map(v => {
this.$refs[v].setPreset(this.rgbPresetName)
this.$refs[v].renderColorBar(this.rgbPresetName)
this.createColorBar(this.rgbPresetName, 'colorBarCanvas', 256, 15)
const renderingEngine = getRenderingEngine(renderingEngineId)
const viewport = (
renderingEngine.getViewport(v)
)
viewport.setProperties({ colormap: { name: rgbPresetName } }, ptVolumeId)
viewport.render()
this.$refs[v] && this.$refs[v].setPreset(this.rgbPresetName)
this.$refs[v] && this.$refs[v].setColorMap(rgbPresetName)
})
// this.$refs['FUSION_AXIAL'].setPreset(this.rgbPresetName)
// this.$refs['FUSION_AXIAL'].renderColorBar(this.rgbPresetName)
// this.createColorBar(this.rgbPresetName, 'colorBarCanvas', 256, 15)
// const renderingEngine = getRenderingEngine(renderingEngineId)
},
upperRangeChange(v) {
if (this.upper === v) return
this.upper = v;
// const viewport = (
// renderingEngine.getViewport(viewportIds.FUSION.AXIAL)
// )
// viewport.setProperties({ colormap: { name: rgbPresetName } }, ptVolumeId)
// // viewport.setProperties({ colormap: { name: rgbPresetName }}, ctVolumeId)
// viewport.render()
// document.onselectstart = function() { return false }// 解决拖动会选中文字的问题
// document.ondragstart = function() { return false }
if (v === 0) {
return
}
this.voiChange(v)
// 反向同步到 colorMap 组件,更新滑块
if (this.$refs.colorMap) {
this.$refs.colorMap.changeVoi(v)
}
},
setPetColorMap(volumeInfo) {
const { volumeActor } = volumeInfo
@@ -1979,17 +1940,24 @@ export default {
// document.onselectstart = function() { return false }// 解决拖动会选中文字的问题
// document.ondragstart = function() { return false }
},
setWwwc(v) {
// this.changeMapperRange(v.wc, v.ww)
const renderingEngine = getRenderingEngine(renderingEngineId)
let viewportId = this.activeIndex === 1 ? 'CT_AXIAL' : this.activeIndex === 2 ? 'PT_AXIAL' : this.activeIndex === 3 ? 'FUSION_AXIAL' : 'PET_MIP_CORONAL'
const viewport = renderingEngine.getViewport(viewportId)
const lower = v.wc - v.ww / 2
const upper = v.wc + v.ww / 2 - 1
viewport.setProperties({ voiRange: { upper: upper, lower: lower } })
viewport.render()
this.customWwc.visible = false
},
// setWwwc(v) {
// // this.changeMapperRange(v.wc, v.ww)
// const renderingEngine = getRenderingEngine(renderingEngineId)
// let viewportId = this.activeIndex === 1 ? 'CT_AXIAL' : this.activeIndex === 2 ? 'PT_AXIAL' : this.activeIndex === 3 ? 'FUSION_AXIAL' : 'PET_MIP_CORONAL'
// const viewport = renderingEngine.getViewport(viewportId)
// const lower = v.wc - v.ww / 2
// const upper = v.wc + v.ww / 2 - 1
// viewport.setProperties({ voiRange: { upper: upper, lower: lower } })
// viewport.render()
// if (viewportId !== 'CT_AXIAL') {
// const nextUpper = Math.round(upper)
// this.upper = nextUpper
// if (this.$refs.colorMap) {
// this.$refs.colorMap.changeVoi(nextUpper)
// }
// }
// this.customWwc.visible = false
// },
changeMapperRange(wc, ww) {
var lower = wc - ww / 2.0
var upper = wc + ww / 2.0
@@ -2262,7 +2230,7 @@ export default {
background-color: #000;
padding: 5px 2px;
::v-deep.el-dialog {
::v-deep .el-dialog {
background: #1e1e1e;
border: 1px solid #ddd;
color: #ddd;
@@ -19,6 +19,8 @@
</div>
<div v-if="index !== 4">Image: #{{ `${seriesInfo.imageIdIndex + 1} / ${seriesInfo.imageMaxLength}` }}</div>
<div v-if="index === 1 || index === 2">{{ seriesInfo.modality }}</div>
<div v-if="index === 3">{{ ctSeriesInfo ? ctSeriesInfo.modality + ' / ' + seriesInfo.modality : seriesInfo.modality }}</div>
<div v-if="index === 4">MIP</div>
</div>
<!-- 描述信息 -->
<div
@@ -135,6 +137,12 @@ export default {
return {}
}
},
ctSeriesInfo: {
type: Object,
default() {
return null
}
},
renderingEngineId: {
type: String,
required: true
@@ -200,7 +208,6 @@ export default {
mounted() {
const digitPlaces = parseInt(this.$route.query.digitPlaces)
this.digitPlaces = digitPlaces === -1 ? 2 : digitPlaces
console.log(toolsUtilities)
this.subjectCode = this.$route.query.subjectCode
var element = document.getElementById(`viewport${this.index}`)
element.addEventListener(VOLUME_NEW_IMAGE, this.handleVolumeNewImage)
@@ -328,7 +335,7 @@ export default {
let properties = viewport.getProperties()
if (this.index === 3) {
// const volumeId = viewport.getVolumeId()
const volumeId = `cornerstoneStreamingImageVolume:PT_VOLUME_ID`
const volumeId = `cornerstoneStreamingImageVolume:PT_VOLUME_ID2`
properties = viewport.getProperties(volumeId)
}
if (properties && properties.voiRange) {
@@ -338,6 +345,10 @@ export default {
upper
)
this.imageInfo.wwwc = `${Math.round(windowWidth)}/${Math.round(windowCenter)}`
// 当为PT或融合视口或MIP视口时,触发外部联动 (排除纯CT视口)
if (this.index !== 1) {
this.$emit('upperRangeChange', Math.round(upper))
}
}
},
handleMouseMove(e) {
@@ -567,10 +578,17 @@ export default {
ctx.fillStyle = gradient
ctx.fillRect(0, 0, rectWidth, rectHeight)
},
setColorMap(presetName) {
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine.getViewport(this.viewportId)
let volumeId = `cornerstoneStreamingImageVolume:PT_VOLUME_ID`
viewport.setProperties({ colormap: { name: presetName } }, volumeId)
viewport.render()
},
setWwWc() {
let properties = viewport.getProperties()
if (this.index === 3) {
properties = viewport.getProperties(`cornerstoneStreamingImageVolume:PT_VOLUME_ID`)
properties = viewport.getProperties(`cornerstoneStreamingImageVolume:PT_VOLUME_ID2`)
}
if (properties && properties.voiRange) {
var { lower, upper } = properties.voiRange
@@ -582,7 +600,10 @@ export default {
upper
)
this.imageInfo.wwwc = `${Math.round(windowWidth)}/${Math.round(windowCenter)}`
this.$emit('upperRangeChange', Math.round(windowWidth))
// 当为PT或融合视口或MIP视口时,触发外部联动 (排除纯CT视口)
if (this.index !== 1) {
this.$emit('upperRangeChange', Math.round(upper))
}
}
},
async getScreenshots() {
@@ -1,106 +0,0 @@
<template>
<div v-loading="loading" class="ecrf-wrapper">
<h4 style="color: #ddd;padding: 5px 0px;margin: 0;">
评估说明
</h4>
<el-form
ref="questions"
size="small"
:model="questionForm"
>
<el-form-item
label="访视点备注"
prop="note"
:rules="[
{ required: true,
message:$t('common:ruleMessage:specify'), trigger: ['blur', 'change']},
]"
>
<el-input
v-model="questionForm.note"
:disabled="readingTaskState >= 2"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"
maxlength="500"
/>
</el-form-item>
<el-form-item v-if="readingTaskState < 2">
<div style="text-align:right">
<el-button size="mini" @click="handleSave">{{ $t('common:button:save') }}</el-button>
</div>
</el-form-item>
</el-form>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'ECRF',
data() {
return {
loading: false,
questionForm: {
note: ''
},
readingTaskState: 1,
isBaseLineTask: false,
visitTaskId: ''
}
},
mounted() {
this.trialId = this.$route.query.trialId
this.visitTaskId = this.$route.query.visitTaskId
},
beforeDestroy() {
},
methods: {
handleSave() {
this.$refs['questions'].validate((valid) => {
if (!valid) return
})
},
}
}
</script>
<style lang="scss" scoped>
.ecrf-wrapper{
::v-deep .el-form-item__label{
color: #c3c3c3;
text-align: left;
}
::v-deep .el-input__inner{
background-color: transparent;
color: #ddd;
border: 1px solid #5e5e5e;
}
::v-deep .el-textarea__inner{
background-color: transparent;
color: #ddd;
border: 1px solid #5e5e5e;
}
::v-deep .el-form-item{
display: flex;
flex-direction: row;
justify-content: flex-start;
flex-wrap: wrap;
}
::v-deep .el-form-item__content{
flex: 1;
}
::v-deep .el-button--mini, .el-button--mini.is-round {
padding: 7px 10px;
}
.el-form-item__content
.el-select{
width: 100%;
}
}
</style>
@@ -1,148 +0,0 @@
<template>
<div v-loading="loading" class="ecrf-wrapper">
<h4 style="color: #ddd;padding: 5px 0px;margin: 0;">
影像质量
</h4>
<el-form
ref="questions"
size="small"
:model="questionForm"
>
<el-form-item
label="影像质量"
prop="imageQuality"
:rules="[
{ required: true,
message:$t('common:ruleMessage:select'), trigger: ['blur', 'change']},
]"
>
<el-radio-group v-model="questionForm.imageQuality">
<el-radio
v-for="item of $d.ImageQualityEvaluation"
:key="item.id"
:label="item.value"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
v-if="questionForm.imageQuality === 2"
label="影像质量问题"
prop="imageQualityIssues"
:rules="[
{ required: true,
message:$t('common:ruleMessage:select'), trigger: ['blur', 'change']},
]"
>
<el-select
v-model="questionForm.imageQualityIssues"
:disabled="readingTaskState >= 2"
clearable
>
<el-option
v-for="item of $d.ImageQualityIssues"
:key="item.id"
:value="item.value"
:label="item.label"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="questionForm.imageQualityIssues === 5"
label="影像质量备注"
prop="note"
:rules="[
{ required: true,
message:$t('common:ruleMessage:specify'), trigger: ['blur', 'change']},
]"
>
<el-input
v-model="questionForm.note"
:disabled="readingTaskState >= 2"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"
maxlength="500"
/>
</el-form-item>
<el-form-item v-if="readingTaskState < 2">
<div style="text-align:right">
<el-button size="mini" @click="handleSave">{{ $t('common:button:save') }}</el-button>
</div>
</el-form-item>
</el-form>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'ECRF',
data() {
return {
loading: false,
questionForm: {
imageQuality: null,
imageQualityIssues: null,
note: ''
},
readingTaskState: 1,
isBaseLineTask: false,
visitTaskId: ''
}
},
mounted() {
this.trialId = this.$route.query.trialId
},
beforeDestroy() {
},
methods: {
handleSave() {
this.$refs['questions'].validate((valid) => {
if (!valid) return
})
},
}
}
</script>
<style lang="scss" scoped>
.ecrf-wrapper{
::v-deep .el-form-item__label{
color: #c3c3c3;
text-align: left;
}
::v-deep .el-input__inner{
background-color: transparent;
color: #ddd;
border: 1px solid #5e5e5e;
}
::v-deep .el-textarea__inner{
background-color: transparent;
color: #ddd;
border: 1px solid #5e5e5e;
}
::v-deep .el-form-item{
display: flex;
flex-direction: row;
justify-content: flex-start;
flex-wrap: wrap;
}
::v-deep .el-form-item__content{
flex: 1;
}
::v-deep .el-button--mini, .el-button--mini.is-round {
padding: 7px 10px;
}
.el-form-item__content
.el-select{
width: 100%;
}
}
</style>
@@ -1,354 +0,0 @@
<template>
<div class="measurement-wrapper">
<div class="container">
<h4 style="color: #ddd;padding: 5px 0px;margin: 0;">
核医学评估
</h4>
<div class="lesion_list">
<div class="flex-row" style="margin:3px 0;">
<div class="title">盆腔淋巴结</div>
</div>
<el-table
:data="questions"
style="width: 100%"
row-key="rowIndex"
:expand-row-keys="expands"
@expand-change="expandSelect"
size="mini"
>
<el-table-column type="expand">
<template slot-scope="props">
<el-form :ref="props.row.rowIndex" size="small" :model="props.row" label-width="80px" style="padding-right: 10px">
<el-form-item label="部位">
<el-input
v-model="props.row.part"
disabled
/>
</el-form-item>
<el-form-item
label="转移灶"
prop="distraction"
:rules="[
{ required: true,
message:$t('common:ruleMessage:select'), trigger: ['blur', 'change']},
]"
>
<el-select v-model="props.row.distraction" style="width:100%;">
<el-option label="阴性" :value="1"></el-option>
<el-option label="阳性" :value="2"></el-option>
</el-select>
</el-form-item>
<el-form-item label="SUVmax">
<el-input
v-model="props.row.suvMax"
disabled
/>
</el-form-item>
<el-form-item >
<div style="text-align:right">
<el-button v-if="props.row.annotation" size="mini" @click="clearAnnotation(props.row)">{{ $t('trials:reading:button:removeMark') }}</el-button>
<el-button size="mini" @click="handleSave(props.row)">{{ $t('common:button:save') }}</el-button>
</div>
</el-form-item>
</el-form>
</template>
</el-table-column>
<el-table-column
label="部位"
prop="part">
</el-table-column>
<el-table-column
label="转移灶"
prop="distraction">
<template slot-scope="scope">
{{scope.row.distraction === 1 ? '阴性' : scope.row.distraction === 2 ? '阳性' : ''}}
</template>
</el-table-column>
<el-table-column
label="SUVmax"
prop="suvMax">
</el-table-column>
</el-table>
</div>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import FusionEvent from './../FusionEvent'
export default {
name: 'TableQuestionList',
components: {
},
data() {
return {
height: window.innerHeight - 140,
questions: [
{
rowIndex: '001',
part: '髂内',
distraction: '',
suvMax: null,
saveTypeEnum: 1,
annotation: null
},
{
rowIndex: '002',
part: '髂外',
distraction: '',
suvMax: null,
saveTypeEnum: 1,
annotation: null
},
{
rowIndex: '003',
part: '髂总',
distraction: '',
suvMax: null,
saveTypeEnum: 1,
annotation: null
},
{
rowIndex: '004',
part: '闭孔',
distraction: '',
suvMax: null,
saveTypeEnum: 1,
annotation: null
}
],
visitTaskId: '',
isCurrentTask: false,
loading: false,
readingTaskState: 1,
isBaseLineTask: false,
taskBlindName: '',
expands:[]
}
},
mounted() {
},
beforeDestroy() {
},
methods: {
expandSelect(row, expandedRows) {
this.expands = []
if (expandedRows.length > 0) {
row ? this.expands.push(row.rowIndex) : ''
}
if (this.expands.length > 0 && row.annotation) {
FusionEvent.$emit('imageLocation', { annotation: row.annotation })
}
},
async clearAnnotation(row) {
// 是否确认清除标记?
const confirm = await this.$confirm(
this.$t('trials:reading:warnning:msg47'),
{
type: 'warning',
distinguishCancelAndClose: true
}
)
if (confirm !== 'confirm') return
let i = this.questions.findIndex(i=>i.rowIndex === row.rowIndex)
FusionEvent.$emit('removeAnnotation', { annotation: this.questions[i].annotation })
this.questions[i].annotation = null
this.questions[i].suvMax = null
},
async handleSave(row) {
let loading = null
try {
let valid = await this.$refs[row.rowIndex].validate()
if (!valid) return
loading = this.$loading({ fullscreen: true })
setTimeout(() => {
// 模拟保存成功
loading.close()
this.$message.success(this.$t('common:message:savedSuccessfully'))
// 可以在这里添加更多的逻辑,比如更新页面上的某些元素
}, 2000);
} catch(e) {
if (loading) {
loading.close()
}
}
},
setActiveCollapse(annotationUID) {
for (let i = 0; i < this.questions.length; i++) {
let obj = this.questions[i]
if (obj.annotation && obj.annotation.data && obj.annotation.data.annotationUID === annotationUID) {
this.expands = [this.questions[i].rowIndex]
break
}
}
},
isCanActiveTool(toolName) {
if (this.expands.length > 0) {
let isExist = this.isExistMeasureData(this.expands[0])
return { isCanActiveTool: isExist, reason: '' }
} else {
return { isCanActiveTool: false, reason: '' }
}
},
isExistMeasureData(rowIndex) {
let i = this.questions.findIndex(i=>i.rowIndex === rowIndex)
if (this.questions[i].annotation) {
return false
} else {
return true
}
},
// modifyMeasuredData(measureObj) {
// if (measureObj.questionInfo) {
// this.activeItem.activeCollapseId = measureObj.questionInfo.QuestionId
// this.activeItem.activeRowIndex = String(measureObj.questionInfo.RowIndex)
// this.activeName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
// const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
// if (this.$refs[refName]) {
// this.$refs[refName][0].setMeasureData(measureObj.measureData)
// }
// }
// },
// 设置测量数据
setMeasuredData(measureData) {
if (measureData.data && measureData.data.data && measureData.data.data.remark ) {
// 编辑
// 展开并更新标记信息
let remark = measureData.data.data.remark
let i = this.questions.findIndex(i=>i.part === remark)
this.questions[i].suvMax = measureData.suvMax
this.questions[i].annotation = measureData
this.expands = [this.questions[i].rowIndex]
} else {
if (this.expands.length > 0) {
let i = this.questions.findIndex(i=>i.rowIndex === this.expands[0])
if (measureData.data && measureData.data.data) {
measureData.data.data.remark = this.questions[i].part
}
this.questions[i].annotation = measureData
}
}
},
}
}
</script>
<style lang="scss" scoped>
.measurement-wrapper{
// .container{
// padding: 10px;
// }
.title{
padding: 5px;
font-weight: bold;
color: #ddd;
font-size: 15px;
}
.add-icon{
padding: 5px;
font-weight: bold;
color: #ddd;
font-size: 15px;
border: 1px solid #938b8b;
margin-bottom: 2px;
cursor: pointer;
}
.add-icon:hover{
background-color: #607d8b;
}
.flex-row{
display: flex;
flex-direction: row;
justify-content: space-between;
background-color: #424242;
}
.lesion_list{
position: relative;
::v-deep .el-table, .el-table__expanded-cell {
background-color: #000;
color: #fff;
border-color:#444444;
.el-form-item__label {
color: #fff;
}
.el-input__inner {
background-color: rgba(0, 0, 0, .1);
border-color: #606266;
color: #fff;
}
.el-input.is-disabled .el-input__inner {
background-color: #303133;
border-color: #444444;
color: #c0c4cc;
cursor: not-allowed;
}
}
::v-deep .el-table th, .el-table tr {
background-color: #000;
color: #fff;
border-color:#444444;
}
::v-deep .el-table__body tr > td{
background-color:#000 !important;
color: #fff;
border-color:#444444;
}
// ::v-deep .el-table__body tr:hover > td{
// background-color:#858282 !important;
// color: #fff;
// border-color:#444444;
// }
::v-deep .el-table--border th.gutter:last-of-type{
border: none;
}
::v-deep .el-card__header{
border: none;
padding: 10px;
}
}
.el-collapse{
border-bottom:none;
border-top:none;
::v-deep .el-collapse-item{
background-color: #000!important;
color: #ddd;
}
::v-deep .el-collapse-item__header{
background-color: #000!important;
color: #ddd;
border-bottom-color:#5a5a5a;
padding-left: 5px;
height: 35px;
line-height: 35px;
}
::v-deep .el-collapse-item__wrap{
background-color: #000!important;
color: #ddd;
}
::v-deep .el-collapse-item__content{
width:260px;
position: absolute;
top: 0px;
right: 0px;
// border: 1px solid #ffeb3b;
border: 1px solid #fff;
z-index: 1;
color: #ddd;
padding: 5px;
background-color:#1e1e1e;
}
}
}
</style>
@@ -1,690 +0,0 @@
<template>
<div
class="viewport_container"
:class="['item',activeIndex === index?'item_active':'']"
@mouseup="sliderMouseup"
>
<div :id="`viewport${index}`" ref="viewportCanvas" style="height: 100%;width:100%" />
<!-- 序列信息 -->
<div
v-if="seriesInfo.taskBlindName"
class="seriesInfo_wrapper"
:style="{color:index%2 == 1 ? '#ddd' : '#666'}"
>
<h2 v-if="isReadingShowSubjectInfo" class="taskInfo_container">
{{ subjectCode }} {{ seriesInfo.taskBlindName }}
</h2>
<div v-if="index === 1 || index === 2">
Series: #{{ seriesInfo.seriesNumber }}
</div>
<div v-if="index !== 4">Image: #{{ `${seriesInfo.imageIdIndex + 1} / ${seriesInfo.imageMaxLength}` }}</div>
<div v-if="index === 1 || index === 2">{{ seriesInfo.modality }}</div>
</div>
<!-- 描述信息 -->
<div
v-if="seriesInfo.description && (index === 1 || index === 2)"
class="descInfo_wrapper"
:style="{color:index%2 == 1 ? '#ddd' : '#666'}"
>
<div>{{ seriesInfo.description }}</div>
</div>
<!-- 图像信息 -->
<div
v-if="seriesInfo.description && (index === 1 || index === 2 || index === 3)"
class="imageInfo_wrapper_l"
:style="{color:index%2 == 1 ? '#ddd' : '#666'}"
>
<div v-show="mousePosition.index.length > 0">
Pos: {{ mousePosition.index[0] }}, {{ mousePosition.index[1] }}, {{ mousePosition.index[2] }}
</div>
<div v-if="(seriesInfo.modality === 'CT' || seriesInfo.modality === 'DR' || seriesInfo.modality === 'CR') && mousePosition.value">
HU: {{ mousePosition.value }}
</div>
<div v-else-if="(seriesInfo.modality === 'PT' && mousePosition.value)">
SUVbw(g/ml): {{ digitPlaces === -1 ?mousePosition.value.toFixed(3) :mousePosition.value.toFixed(digitPlaces) }}
</div>
<div v-else-if="mousePosition.value">
Density: {{ mousePosition.value }}
</div>
<div v-if="index === 1 || index === 2">
W*H: {{ imageInfo.size }}
</div>
<!-- <div v-if="index === 1 || index === 2">Zoom: {{ imageInfo.zoom }}</div> -->
</div>
<div
v-if="seriesInfo.description && (index === 1 || index === 2 || index === 3)"
class="imageInfo_wrapper_r"
:style="{color:index%2 == 1 ? '#ddd' : '#666'}"
>
<div v-show="imageInfo.location && index !== 3 ">Location: {{ `${imageInfo.location} mm` }}</div>
<div v-show="seriesInfo.sliceThickness && index !== 3">Slice Thickness: {{ `${Number(seriesInfo.sliceThickness).toFixed(2)} mm` }}</div>
<div v-show="imageInfo.wwwc ">WW/WL: {{ imageInfo.wwwc }}</div>
</div>
<div v-if="index !== 4" ref="sliderBox" class="slider_box" :style="{background: index === 2?'#ddd':'#333'}" @click.stop="goViewer($event)">
<div :style="{top: sliderBoxHeight + '%'}" class="box" @click.stop.prevent="() => {return}" @mousedown.stop="sliderMousedown($event)" />
</div>
<div style="position: absolute;left: 50%;top: 30px;color: #f44336;transform: translateX(-50%);">
{{ markers.top }}
</div>
<div style="position: absolute;top: 50%;right: 15px;color: #f44336;transform: translateY(-50%);">
{{ markers.right }}
</div>
<div style="position: absolute;left: 50%;bottom: 30px;color: #f44336;transform: translateX(-50%);">
{{ markers.bottom }}
</div>
<div style="position: absolute;top: 50%;left: 15px;color: #f44336;transform: translateY(-50%);">
{{ markers.left }}
</div>
<div v-if="presetName" class="color_bar">
<canvas id="colorBar_Canvas" />
</div>
<div v-if="index === 4" id="rotateBar" ref="rotateBar" class="rotate_slider_box" @click.stop="clickRotate($event)">
<div id="rotateSlider" :style="{left: rotateBarLeft + 'px'}" class="box" @click.stop.prevent="() => {return}" @mousedown.stop="rotateBarMousedown($event)" />
</div>
</div>
</template>
<script>
import {
getRenderingEngine,
metaData,
utilities,
// getOrCreateCanvas,
Enums } from '@cornerstonejs/core'
import {
utilities as toolsUtilities,
annotation,
Enums as toolsEnums
// cursors
} from '@cornerstonejs/tools'
import vtkColorMaps from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps'
import { vec3, mat4 } from 'gl-matrix'
import html2canvas from 'html2canvas'
const { getColormap } = utilities.colormap
const {
VOLUME_NEW_IMAGE
// VOLUME_LOADED,
// IMAGE_VOLUME_MODIFIED,
// VOLUME_VIEWPORT_NEW_VOLUME
} = Enums.Events
var renderingEngine
var viewport
export default {
name: 'Viewport',
props: {
activeIndex: {
type: Number,
default: 0
},
index: {
// 1:ct 2:pet 3:fusion 4:mip
type: Number,
required: true
},
isReadingShowSubjectInfo: {
type: Boolean,
default: false
},
seriesInfo: {
type: Object,
default() {
return {}
}
},
renderingEngineId: {
type: String,
required: true
},
viewportId: {
type: String,
required: true
},
volume: {
type: Object,
default() {
return {}
}
},
measureDatas: {
type: Array,
default() {
return []
}
}
},
data() {
return {
subjectCode: '',
mousePosition: { index: [], value: null, modalityUnit: '', world: [], ctVal: null, ptVal: null },
digitPlaces: -1,
imageInfo: {
zoom: null,
size: null,
location: null,
sliceThickness: null,
wwwc: null
},
sliderBoxHeight: 0,
sliderInfo: {
oldB: null,
oldM: null,
isMove: false
},
rotateAngle: 0,
rotateBarLeft: 0,
rotateBarInfo: {
initX: null,
initLeft: null,
isMove: false
},
isFirstRender: true,
defaultWindowLevel: {},
presetName: '',
orientationMarkers: [],
originalMarkers: [],
markers: { top: '', right: '', bottom: '', left: '' }
}
},
watch: {
activeIndex: {
handler(v) {
console.log('activeIndex ', v)
}
}
},
mounted() {
const digitPlaces = parseInt(this.$route.query.digitPlaces)
this.digitPlaces = digitPlaces === -1 ? 2 : digitPlaces
// console.log(toolsUtilities)
this.subjectCode = this.$route.query.subjectCode
var element = document.getElementById(`viewport${this.index}`)
element.addEventListener(VOLUME_NEW_IMAGE, this.handleVolumeNewImage)
element.addEventListener(Enums.Events.CAMERA_MODIFIED, this.handleCameraModified)
element.addEventListener(Enums.Events.VOI_MODIFIED, this.handleVOIModified)
element.addEventListener(toolsEnums.Events.MOUSE_WHEEL, this.handletoolsMouseWheel)
element.addEventListener('CORNERSTONE_TOOLS_MOUSE_MOVE', this.handleMouseMove)
element.addEventListener('mouseleave', this.handleMouseLeave)
document.addEventListener('mouseup', () => {
this.sliderMouseup()
if (this.index === 4) {
this.rotateBarMouseup()
}
})
document.addEventListener('mousemove', (e) => {
this.sliderMousemove(e)
if (this.index === 4) {
this.rotateBarMousemove(e)
}
})
},
methods: {
handleVolumeNewImage(e) {
const { imageIndex } = e.detail
if (this.viewportId === e.detail.viewportId && this.index !== 4) {
this.seriesInfo.imageIdIndex = imageIndex
}
renderingEngine = getRenderingEngine(this.renderingEngineId)
viewport = renderingEngine.getViewport(this.viewportId)
if (viewport) {
var zoom = viewport.getZoom()
if (zoom) {
this.imageInfo.zoom = zoom.toFixed(4)
}
var imageId = viewport.getCurrentImageId()
if (imageId) {
const imagePlaneModule = metaData.get('imagePlaneModule', imageId)
this.imageInfo.size = `${imagePlaneModule.columns}*${imagePlaneModule.rows}`
this.imageInfo.location = imagePlaneModule.sliceLocation
this.getOrientationMarker()
this.sliderBoxHeight = imageIndex * 100 / (this.seriesInfo.imageMaxLength - 1)
}
var properties = viewport.getProperties()
if (properties && properties.voiRange) {
var { lower, upper } = properties.voiRange
const windowWidth = upper - lower
const windowCenter = (upper + lower) / 2
this.defaultWindowLevel.windowWidth = windowWidth
this.defaultWindowLevel.windowCenter = windowCenter
this.imageInfo.wwwc = `${Math.round(windowWidth)}/${Math.round(windowCenter)}`
}
if (this.presetName) {
this.renderColorBar(this.presetName)
}
}
},
getOrientationMarker() {
// eslint-disable-next-line no-unused-vars
const { viewUp, viewPlaneNormal } = viewport.getCamera()
const viewRight = vec3.create()
vec3.cross(viewRight, viewUp, viewPlaneNormal)
const columnCosines = [-viewUp[0], -viewUp[1], -viewUp[2]]
const rowCosines = viewRight
const rowString = toolsUtilities.orientation.getOrientationStringLPS(rowCosines)
const columnString = toolsUtilities.orientation.getOrientationStringLPS(columnCosines)
const oppositeRowString = toolsUtilities.orientation.invertOrientationStringLPS(rowString)
const oppositeColumnString = toolsUtilities.orientation.invertOrientationStringLPS(columnString)
this.markers.top = oppositeColumnString
this.markers.right = rowString
this.markers.bottom = columnString
this.markers.left = oppositeRowString
this.orientationMarkers = [oppositeColumnString, rowString, columnString, oppositeRowString]
if (this.originalMarkers.length === 0) {
this.originalMarkers = [...this.orientationMarkers]
}
},
rotateMarkers(type) {
var markers = [...this.orientationMarkers]
if (type === 4) {
// 左转90度
this.orientationMarkers = markers.slice(1, 4).concat(markers[0])
} else if (type === 5) {
// 右转90度
this.orientationMarkers = [markers[3]].concat(markers.slice(0, 3))
}
this.setMarkers()
},
resetMarks() {
this.orientationMarkers = [...this.originalMarkers]
this.setMarkers()
},
setMarkers() {
var markers = [...this.orientationMarkers]
for (const key in this.markers) {
var v = markers.shift(0)
this.markers[key] = v
}
},
handleCameraModified(e) {
renderingEngine = getRenderingEngine(this.renderingEngineId)
viewport = renderingEngine.getViewport(this.viewportId)
if (!viewport) return
var zoom = viewport.getZoom()
if (!zoom) return
this.imageInfo.zoom = zoom.toFixed(4)
// console.log(e)
},
handleVOIModified(e) {
renderingEngine = getRenderingEngine(this.renderingEngineId)
viewport = renderingEngine.getViewport(this.viewportId)
if (!viewport) return
var properties = viewport.getProperties()
if (properties && properties.voiRange) {
var { lower, upper } = properties.voiRange
const { windowWidth, windowCenter } = utilities.windowLevel.toWindowLevel(
lower,
upper
)
this.imageInfo.wwwc = `${Math.round(windowWidth)}/${Math.round(windowCenter)}`
}
},
handleMouseMove(e) {
if (this.index !== 4) {
const { currentPoints } = e.detail
const worldPoint = currentPoints.world
const { imageData } = this.volume
const index = imageData.worldToIndex(worldPoint)
index[0] = Math.floor(index[0])
index[1] = Math.floor(index[1])
index[2] = Math.floor(index[2])
this.mousePosition.index = index
this.mousePosition.value = this.getValue(this.volume, worldPoint)
}
},
getValue(volume, worldPos) {
const { dimensions, scalarData, imageData } = volume
const index = imageData.worldToIndex(worldPos)
index[0] = Math.floor(index[0])
index[1] = Math.floor(index[1])
index[2] = Math.floor(index[2])
if (!utilities.indexWithinDimensions(index, dimensions)) {
return
}
const yMultiple = dimensions[0]
const zMultiple = dimensions[0] * dimensions[1]
const value = scalarData[index[2] * zMultiple + index[1] * yMultiple + index[0]]
return value
},
handleMouseLeave(e) {
this.mousePosition.index = []
this.mousePosition.value = null
},
goViewer(e) {
var height = e.offsetY * 100 / this.$refs['sliderBox'].clientHeight
this.sliderBoxHeight = height
var index = Math.trunc(this.seriesInfo.imageMaxLength * this.sliderBoxHeight / 100)
if (this.seriesInfo.imageIdIndex !== index) {
this.scroll(index)
}
},
sliderMousedown(e) {
var boxHeight = this.$refs['sliderBox'].clientHeight
this.sliderInfo.oldB = parseInt(e.srcElement.style.top) * boxHeight / 100
this.sliderInfo.oldM = e.clientY
this.sliderInfo.isMove = true
e.stopImmediatePropagation()
e.stopPropagation()
e.preventDefault()
},
sliderMousemove(e) {
if (!this.sliderInfo.isMove) return
var PX = this.sliderInfo.oldB - (this.sliderInfo.oldM - e.clientY)
var boxHeight = this.$refs['sliderBox'].clientHeight
// 滚动翻页
if (PX < 0) return
if (PX > boxHeight) return
const height = PX * 100 / boxHeight
var index = Math.trunc(this.seriesInfo.imageMaxLength * height / 100)
index = index > this.seriesInfo.imageMaxLength ? this.seriesInfo.imageMaxLength : index < 0 ? 0 : index
this.sliderBoxHeight = height
if (this.seriesInfo.imageIdIndex !== index) {
this.scroll(index)
}
},
handletoolsMouseWheel(e) {
const { viewportId, wheel } = e.detail
if (viewportId === 'PET_MIP_CORONAL') {
const container = document.getElementById('rotateBar')
const slider = document.getElementById('rotateSlider')
const containerWidth = container.offsetWidth
const sliderWidth = slider.offsetWidth
const maxX = containerWidth - sliderWidth
const { direction } = wheel
var x = Math.trunc(30 * ((containerWidth - sliderWidth) / 360))
if (direction > 0 && (this.rotateBarLeft + x) > maxX) {
this.rotateBarLeft = x - (containerWidth - sliderWidth - this.rotateBarLeft)
} else if (direction < 0 && (this.rotateBarLeft < x)) {
this.rotateBarLeft = containerWidth - (x - this.rotateBarLeft + sliderWidth)
} else {
this.rotateBarLeft = x * direction + this.rotateBarLeft
}
}
},
rotateBarMousemove(e) {
// 滚动旋转
if (!this.rotateBarInfo.isMove) return
const container = document.getElementById('rotateBar')
const slider = document.getElementById('rotateSlider')
const containerWidth = container.offsetWidth
const sliderWidth = slider.offsetWidth
let x = Math.trunc(e.clientX - this.rotateBarInfo.initX + this.rotateBarInfo.initLeft)
if (x < 0) x = 0
if (x > containerWidth - sliderWidth) x = containerWidth - sliderWidth
const deltaX = x - this.rotateBarLeft
const angle = Math.sin((deltaX * (360 / (containerWidth - sliderWidth))) * Math.PI / 180)
this.rotate(angle)
this.rotateBarLeft = x
},
rotateBarMousedown(e) {
console.log('rotateBarMousedown')
this.rotateBarInfo.initLeft = e.srcElement.offsetLeft
this.rotateBarInfo.initX = e.clientX
this.rotateBarInfo.isMove = true
e.stopImmediatePropagation()
e.stopPropagation()
e.preventDefault()
},
rotate(angle) {
renderingEngine = getRenderingEngine(this.renderingEngineId)
viewport = renderingEngine.getViewport(this.viewportId)
const camera = viewport.getCamera()
const { viewUp, position, focalPoint } = camera
const [cx, cy, cz] = focalPoint
const [ax, ay, az] = [0, 0, 1]
const newPosition = [0, 0, 0]
const newFocalPoint = [0, 0, 0]
const newViewUp = [0, 0, 0]
const transform = mat4.identity(new Float32Array(16))
mat4.translate(transform, transform, [cx, cy, cz])
mat4.rotate(transform, transform, angle, [ax, ay, az])
mat4.translate(transform, transform, [-cx, -cy, -cz])
vec3.transformMat4(newPosition, position, transform)
vec3.transformMat4(newFocalPoint, focalPoint, transform)
mat4.identity(transform)
mat4.rotate(transform, transform, angle, [ax, ay, az])
vec3.transformMat4(newViewUp, viewUp, transform)
viewport.setCamera({
position: newPosition,
viewUp: newViewUp,
focalPoint: newFocalPoint
})
viewport.render()
},
clickRotate(e) {
// console.log('clickRotate')
const container = document.getElementById('rotateBar')
const containerWidth = container.offsetWidth
const slider = document.getElementById('rotateSlider')
const sliderWidth = slider.offsetWidth
const x = Math.trunc(e.offsetX)
const deltaX = x - this.rotateBarLeft
const angle = Math.sin((deltaX * (360 / (containerWidth - sliderWidth))) * Math.PI / 180)
this.rotate(angle)
this.rotateBarLeft = x
},
async scroll(index) {
renderingEngine = getRenderingEngine(this.renderingEngineId)
viewport = renderingEngine.getViewport(this.viewportId)
const actorEntries = viewport.getActors()
if (!actorEntries) {
return
}
// const delta = index - this.seriesInfo.imageIdIndex
// toolsUtilities.scroll(viewport, {
// delta,
// volumeId: actorEntries.uid
// })
await utilities.jumpToSlice(viewport.element, {
imageIndex: index,
});
renderingEngine.render()
},
rotateBarMouseup(e) {
this.rotateBarInfo.isMove = false
},
sliderMouseup(e) {
this.sliderInfo.isMove = false
},
setAnnotation(imageId, element) {
this.measureDatas.forEach(item => {
if (item.OtherMeasureData) {
var { metadata, annotationUID } = item.OtherMeasureData
var { referencedImageId } = metadata
console.log(annotationUID, annotation.state.getAnnotation(annotationUID))
if (!annotation.state.getAnnotation(annotationUID) && referencedImageId === imageId) {
annotation.state.addAnnotation(item.OtherMeasureData, element)
}
}
})
},
setPreset(presetName) {
this.presetName = presetName
},
renderColorBar(presetName) {
var colorMap = null
if (presetName === 'hsv') {
colorMap = vtkColorMaps.getPresetByName(presetName)
} else {
colorMap = getColormap(presetName)
}
const rgbPoints = colorMap.RGBPoints
const canvas = document.getElementById('colorBar_Canvas')
const ctx = canvas.getContext('2d')
const canvasWidth = 160
const canvasHeight = 5
const rectWidth = 160
const rectHeight = canvasHeight
canvas.width = canvasWidth
canvas.height = canvasHeight
const gradient = ctx.createLinearGradient(0, 0, rectWidth, 0)
for (let i = 0; i < rgbPoints.length; i += 4) {
let position = 0
if (rgbPoints[0] === -1) {
position = (rgbPoints[i] + 1) / 2
} else {
position = rgbPoints[i]
}
const color = `rgb(${parseInt(rgbPoints[i + 1] * 255)}, ${parseInt(rgbPoints[i + 2] * 255)}, ${parseInt(rgbPoints[i + 3] * 255)})`
gradient.addColorStop(position, color)
}
ctx.fillStyle = gradient
ctx.fillRect(0, 0, rectWidth, rectHeight)
},
setWwWc() {
var properties = viewport.getProperties()
if (properties && properties.voiRange) {
var { lower, upper } = properties.voiRange
const windowWidth = upper - lower
const windowCenter = (upper + lower) / 2
this.imageInfo.wwwc = `${Math.round(windowWidth)}/${Math.round(windowCenter)}`
}
},
async getScreenshots() {
const divForDownloadViewport = document.querySelector(
`div[data-viewport-uid="FUSION_AXIAL"]`
)
// const divForDownloadViewport = document.querySelector(
// '.viewport_container'
// )
var canvas = await html2canvas(divForDownloadViewport)
var pictureBaseStr = canvas.toDataURL('image/png', 1)
return pictureBaseStr
}
}
}
</script>
<style lang="scss" scoped>
.viewport_container{
width:100%;
height: 100%;
.seriesInfo_wrapper {
position: absolute;
left: 10px;
top: 10px;
text-align: left;
font-size: 12px;
z-index: 1;
.taskInfo_container{
color:#f44336;
padding: 5px 0px;
margin: 0;
}
}
.descInfo_wrapper{
position: absolute;
right: 20px;
top: 10px;
text-align: right;
font-size: 12px;
z-index: 1;
}
.imageInfo_wrapper_l{
position: absolute;
left: 10px;
bottom: 5px;
text-align: left;
font-size: 12px;
z-index: 1;
}
.imageInfo_wrapper_r{
position: absolute;
right: 40px;
bottom: 5px;
text-align: right;
font-size: 12px;
z-index: 1;
}
.slider_box{
position: absolute;
right: 1px;
height: calc(100% - 120px);
transform: translateY(-50%);
top: calc(50% - 30px);
width: 10px;
background: #333;
cursor: pointer;
.box{
z-index:10;
background: #9e9e9e;
height: 20px;
width: 100%;
position: absolute;
top: 0;
cursor: move
}
}
.color_bar{
position: absolute;
// transform:translateY(-50%);
transform: rotate(-90deg);
transform-origin: right;
left: -150px;
top: 30%;
// transform-origin: top left;
z-index: 1;
// background: #f44336;
}
.rotate_slider_box{
position: absolute;
width: 380px;
height: 10px;
bottom: 5px;
left: 50%;
transform: translateX(-50%);
background: #333;
cursor: pointer;
.box{
z-index:10;
background: #9e9e9e;
height: 100%;
width: 20px;
position: absolute;
top: 0;
cursor: move
}
}
// .my_slider_box:after{
// content: '';
// position: absolute;
// bottom: -20px;
// left: 0;
// height: 20px;
// width: 100%;
// background: #333;
// }
}
</style>
File diff suppressed because it is too large Load Diff
@@ -1319,12 +1319,12 @@ export default {
.merge-table {
padding: 0 10px;
::v-deep.el-table {
::v-deep .el-table {
background-color: #1e1e1e !important;
color: #383838;
}
::v-deep.el-table td.el-table__cell,
::v-deep .el-table td.el-table__cell,
.el-table th.el-table__cell.is-leaf {
border-bottom: 1px solid #383838;
}
@@ -1335,7 +1335,7 @@ export default {
background-color: #1e1e1e;
}
::v-deep.el-table__header-wrapper {
::v-deep .el-table__header-wrapper {
th {
background-color: #1e1e1e !important;
color: #dfdfdf;
@@ -1343,7 +1343,7 @@ export default {
}
}
::v-deep.el-table__body-wrapper {
::v-deep .el-table__body-wrapper {
tr {
background-color: #1e1e1e !important;
color: #dfdfdf;
@@ -1354,7 +1354,7 @@ export default {
}
}
::v-deep.el-table__empty-block {
::v-deep .el-table__empty-block {
background-color: #1e1e1e !important;
}
@@ -989,21 +989,21 @@ export default {
padding: 5px 0;
}
::v-deep.el-table__fixed-right-patch {
::v-deep .el-table__fixed-right-patch {
background-color: #000 !important;
border-color: #444444;
}
::v-deep.el-table__fixed-body-wrapper tr:hover>td {
::v-deep .el-table__fixed-body-wrapper tr:hover>td {
background-color: #000 !important;
}
::v-deep.el-table--scrollable-x .el-table__body-wrapper {
::v-deep .el-table--scrollable-x .el-table__body-wrapper {
z-index: 2;
}
}
::v-deep.el-tag.el-tag--info {
::v-deep .el-tag.el-tag--info {
color: #000;
}
}
@@ -1436,12 +1436,12 @@ export default {
.merge-table {
padding: 0 10px;
::v-deep.el-table {
::v-deep .el-table {
background-color: #1e1e1e !important;
color: #383838;
}
::v-deep.el-table td.el-table__cell,
::v-deep .el-table td.el-table__cell,
.el-table th.el-table__cell.is-leaf {
border-bottom: 1px solid #383838;
}
@@ -1452,7 +1452,7 @@ export default {
background-color: #1e1e1e;
}
::v-deep.el-table__header-wrapper {
::v-deep .el-table__header-wrapper {
th {
background-color: #1e1e1e !important;
color: #dfdfdf;
@@ -1460,7 +1460,7 @@ export default {
}
}
::v-deep.el-table__body-wrapper {
::v-deep .el-table__body-wrapper {
tr {
background-color: #1e1e1e !important;
color: #dfdfdf;
@@ -1471,7 +1471,7 @@ export default {
}
}
::v-deep.el-table__empty-block {
::v-deep .el-table__empty-block {
background-color: #1e1e1e !important;
}
@@ -1436,12 +1436,12 @@ export default {
.merge-table {
padding: 0 10px;
::v-deep.el-table {
::v-deep .el-table {
background-color: #1e1e1e !important;
color: #383838;
}
::v-deep.el-table td.el-table__cell,
::v-deep .el-table td.el-table__cell,
.el-table th.el-table__cell.is-leaf {
border-bottom: 1px solid #383838;
}
@@ -1452,7 +1452,7 @@ export default {
background-color: #1e1e1e;
}
::v-deep.el-table__header-wrapper {
::v-deep .el-table__header-wrapper {
th {
background-color: #1e1e1e !important;
color: #dfdfdf;
@@ -1460,7 +1460,7 @@ export default {
}
}
::v-deep.el-table__body-wrapper {
::v-deep .el-table__body-wrapper {
tr {
background-color: #1e1e1e !important;
color: #dfdfdf;
@@ -1471,7 +1471,7 @@ export default {
}
}
::v-deep.el-table__empty-block {
::v-deep .el-table__empty-block {
background-color: #1e1e1e !important;
}
@@ -687,11 +687,27 @@ export default {
const m1 = this.qsForm[this.m1Id]
const m2 = this.qsForm[this.m2Id]
const m3 = this.qsForm[this.m3Id]
if (isNaN(parseFloat(m1)) || isNaN(parseFloat(m2)) || isNaN(parseFloat(m3))) {
this.$set(this.qsForm, this.avgId, null)
} else {
const avg = (parseFloat(m1) + parseFloat(m2) + parseFloat(m3)) / 3
// if (isNaN(parseFloat(m1)) || isNaN(parseFloat(m2)) || isNaN(parseFloat(m3))) {
// this.$set(this.qsForm, this.avgId, null)
// } else {
// const avg = (parseFloat(m1) + parseFloat(m2) + parseFloat(m3)) / 3
// this.$set(this.qsForm, this.avgId, this.numberToFixed(avg))
// }
//
const validNumbers = []
if (!isNaN(parseFloat(m1))) validNumbers.push(parseFloat(m1))
if (!isNaN(parseFloat(m2))) validNumbers.push(parseFloat(m2))
if (!isNaN(parseFloat(m3))) validNumbers.push(parseFloat(m3))
//
if (validNumbers.length > 0) {
// /
const sum = validNumbers.reduce((a, b) => a + b, 0)
const avg = sum / validNumbers.length
this.$set(this.qsForm, this.avgId, this.numberToFixed(avg))
} else {
//
this.$set(this.qsForm, this.avgId, null)
}
}
},
@@ -999,20 +1015,20 @@ export default {
padding: 5px 0;
}
::v-deep.el-table__fixed-right-patch {
::v-deep .el-table__fixed-right-patch {
background-color: #000 !important;
border-color: #444444;
}
::v-deep.el-table__fixed-body-wrapper tr:hover>td {
::v-deep .el-table__fixed-body-wrapper tr:hover>td {
background-color: #000 !important;
}
::v-deep.el-table--scrollable-x .el-table__body-wrapper {
::v-deep .el-table--scrollable-x .el-table__body-wrapper {
z-index: 2;
}
}
::v-deep.el-tag.el-tag--info {
::v-deep .el-tag.el-tag--info {
color: #000;
}
}
@@ -614,7 +614,7 @@ export default {
}
::v-deep.el-tabs {
::v-deep .el-tabs {
box-sizing: border-box;
padding: 0 5px;
height: 100%;
@@ -665,7 +665,7 @@ export default {
border: 1px solid #607d8b !important;
}
::v-deep.el-progress__text {
::v-deep .el-progress__text {
color: #ccc;
font-size: 12px;
}
@@ -1203,12 +1203,12 @@ export default {
.merge-table {
padding: 0 10px;
::v-deep.el-table {
::v-deep .el-table {
background-color: #1e1e1e !important;
color: #383838;
}
::v-deep.el-table td.el-table__cell,
::v-deep .el-table td.el-table__cell,
.el-table th.el-table__cell.is-leaf {
border-bottom: 1px solid #383838;
}
@@ -1219,7 +1219,7 @@ export default {
background-color: #1e1e1e;
}
::v-deep.el-table__header-wrapper {
::v-deep .el-table__header-wrapper {
th {
background-color: #1e1e1e !important;
color: #dfdfdf;
@@ -1227,7 +1227,7 @@ export default {
}
}
::v-deep.el-table__body-wrapper {
::v-deep .el-table__body-wrapper {
tr {
background-color: #1e1e1e !important;
color: #dfdfdf;
@@ -1238,7 +1238,7 @@ export default {
}
}
::v-deep.el-table__empty-block {
::v-deep .el-table__empty-block {
background-color: #1e1e1e !important;
}
@@ -1130,12 +1130,12 @@ export default {
.merge-table {
padding: 0 10px;
::v-deep.el-table {
::v-deep .el-table {
background-color: #1e1e1e !important;
color: #383838;
}
::v-deep.el-table td.el-table__cell,
::v-deep .el-table td.el-table__cell,
.el-table th.el-table__cell.is-leaf {
border-bottom: 1px solid #383838;
}
@@ -1146,7 +1146,7 @@ export default {
background-color: #1e1e1e;
}
::v-deep.el-table__header-wrapper {
::v-deep .el-table__header-wrapper {
th {
background-color: #1e1e1e !important;
color: #dfdfdf;
@@ -1154,7 +1154,7 @@ export default {
}
}
::v-deep.el-table__body-wrapper {
::v-deep .el-table__body-wrapper {
tr {
background-color: #1e1e1e !important;
color: #dfdfdf;
@@ -1165,7 +1165,7 @@ export default {
}
}
::v-deep.el-table__empty-block {
::v-deep .el-table__empty-block {
background-color: #1e1e1e !important;
}
@@ -952,7 +952,7 @@ export default {
background-color: #607d8b!important;
border: 1px solid #607d8b!important;
}
::v-deep.el-progress__text{
::v-deep .el-progress__text{
color: #ccc;
font-size: 12px;
}
@@ -1022,7 +1022,7 @@ export default {
}
}
::v-deep.el-collapse{
::v-deep .el-collapse{
border: none;
.el-collapse-item{
background-color: #000!important;
@@ -1043,7 +1043,7 @@ export default {
}
}
.sr-wrapper{
::v-deep.el-dialog{
::v-deep .el-dialog{
background: #fff !important;
border: 1px solid #ddd;
// color: #ddd;
@@ -1051,12 +1051,12 @@ export default {
color:#fff;
}
}
::v-deep.sr-dialog-container{
::v-deep .sr-dialog-container{
margin-top: 50px !important;
width:75%;
height:80%;
}
::v-deep.el-dialog__body{
::v-deep .el-dialog__body{
padding: 10px;
height: calc(100% - 50px);
}
@@ -1064,7 +1064,7 @@ export default {
position: relative;
}
.sr-full-dialog-container{
::v-deep.is-fullscreen .el-dialog__body{
::v-deep .is-fullscreen .el-dialog__body{
height: calc(100% - 50px);
}
}
@@ -887,7 +887,7 @@ export default {
background-color: #607d8b!important;
border: 1px solid #607d8b!important;
}
::v-deep.el-progress__text{
::v-deep .el-progress__text{
color: #ccc;
font-size: 12px;
}
@@ -956,7 +956,7 @@ export default {
}
}
}
::v-deep.el-collapse{
::v-deep .el-collapse{
border: none;
.el-collapse-item{
background-color: #000!important;
@@ -977,7 +977,7 @@ export default {
}
}
.sr-wrapper{
::v-deep.el-dialog{
::v-deep .el-dialog{
background: #fff !important;
border: 1px solid #ddd;
// color: #ddd;
@@ -985,12 +985,12 @@ export default {
color:#fff;
}
}
::v-deep.sr-dialog-container{
::v-deep .sr-dialog-container{
margin-top: 50px !important;
width:75%;
height:80%;
}
::v-deep.el-dialog__body{
::v-deep .el-dialog__body{
padding: 10px;
height: calc(100% - 50px);
}
@@ -998,7 +998,7 @@ export default {
position: relative;
}
.sr-full-dialog-container{
::v-deep.is-fullscreen .el-dialog__body{
::v-deep .is-fullscreen .el-dialog__body{
height: calc(100% - 50px);
}
}
@@ -529,7 +529,7 @@ export default {
}
</script>
<style lang="scss" scoped>
::v-deep.el-message-box__headerbtn {
::v-deep .el-message-box__headerbtn {
display: none;
}
@@ -541,7 +541,7 @@ export default {
box-sizing: border-box;
background-color: #000;
::v-deep.el-tabs {
::v-deep .el-tabs {
box-sizing: border-box;
height: 100%;
display: flex;
@@ -573,11 +573,11 @@ export default {
}
::v-deep.hot-keys-label {
::v-deep .hot-keys-label {
color: #dfdfdf !important;
}
::v-deep.shortcut-key-input span {
::v-deep .shortcut-key-input span {
color: #dfdfdf !important;
}
@@ -588,7 +588,7 @@ export default {
// }
// }
::v-deep.el-dialog {
::v-deep .el-dialog {
background: #1e1e1e;
border: 1px solid #ddd;
color: #ddd;
@@ -614,13 +614,13 @@ export default {
}
::v-deep.dialog-container {
::v-deep .dialog-container {
margin-top: 50px !important;
width: 75%;
height: 80%;
}
::v-deep.el-dialog__body {
::v-deep .el-dialog__body {
padding: 20px;
height: calc(100% - 70px);
}
@@ -636,7 +636,7 @@ export default {
}
.full-dialog-container {
::v-deep.is-fullscreen .el-dialog__body {
::v-deep .is-fullscreen .el-dialog__body {
height: calc(100% - 70px);
}
}
@@ -142,7 +142,7 @@ export default {
getStudyList(obj) {
if (obj) {
var studyList = obj.StudyList || []
studyList = studyList.filter(i => !i.IsCriticalSequence && (i.Modalities.indexOf('CT') !== -1 || i.Modalities.indexOf('MR') !== -1) && i.Modalities.indexOf('PT') !== -1)
studyList = studyList.filter(i => !i.IsCriticalSequence && (i.Modalities.indexOf('CT') !== -1 || i.Modalities.indexOf('MR') !== -1) && (i.Modalities.indexOf('PT') !== -1 || i.Modalities.indexOf('NM') !== -1))
if (studyList.length === 0) return
this.studyList = studyList
}
@@ -200,7 +200,7 @@ export default {
var series = seriesList.filter(series => series.Modality === 'CT' || series.Modality === 'MR')
this.ctSeries = series.sort((a, b) => b.instanceCount - a.instanceCount)
series = seriesList.filter(series => series.Modality === 'PT')
series = seriesList.filter(series => series.Modality === 'PT' || series.Modality === 'NM')
this.petSeries = series.sort((a, b) => b.instanceCount - a.instanceCount)
}
},
@@ -235,12 +235,12 @@ export default {
</script>
<style lang="scss" scoped>
.series-table {
::v-deep.el-table {
::v-deep .el-table {
background-color: #1e1e1e !important;
color: #dfdfdf;
}
::v-deep.el-table td.el-table__cell,
::v-deep .el-table td.el-table__cell,
.el-table th.el-table__cell.is-leaf {
border-bottom: 1px solid #dfdfdf;
}
@@ -251,14 +251,14 @@ export default {
background-color: #1e1e1e;
}
::v-deep.el-table__header-wrapper {
::v-deep .el-table__header-wrapper {
th {
background-color: #1e1e1e !important;
color: #dfdfdf;
}
}
::v-deep.el-table__body-wrapper {
::v-deep .el-table__body-wrapper {
tr {
background-color: #1e1e1e !important;
color: #dfdfdf;
@@ -269,7 +269,7 @@ export default {
}
}
::v-deep.el-table__empty-block {
::v-deep .el-table__empty-block {
background-color: #1e1e1e !important;
}
}
@@ -0,0 +1,823 @@
<template>
<div ref="viewport-volume" class="viewport-wrapper" v-loading="loading" :element-loading-text="NSTip"
element-loading-background="rgba(0, 0, 0, 0.8)" @mouseup="sliderMouseup" @mousemove="sliderMousemove"
@mouseleave="sliderMouseleave" :style="{ color: '#ddd' }">
<div v-if="series && taskInfo" class="left-top-text">
<div v-if="taskInfo.IsExistsClinicalData" class="cd-info" :title="$t('trials:reading:button:clinicalData')">
<svg-icon style="cursor: pointer;" icon-class="documentation" class="svg-icon"
@click.stop="viewCD(series.TaskInfo.VisitTaskId)" />
</div>
<h2 v-if="taskInfo.IsReadingShowSubjectInfo && series.TaskInfo" class="subject-info">
{{ `${series.TaskInfo.SubjectCode} ${series.TaskInfo.TaskBlindName} ` }}
</h2>
<div>Series: #{{ series.SeriesNumber }}</div>
<div v-if="imageInfo.total">Image: #{{ `${series.SliceIndex + 1}/${imageInfo.total}` }}</div>
<div>{{ series.Modality }}</div>
</div>
<div v-if="series" class="right-top-text">
<div>{{ series.Description }}</div>
</div>
<div v-if="series" class="left-bottom-text">
<div v-show="mousePosition.index.length > 0">
Pos: {{ mousePosition.index[0] }}, {{ mousePosition.index[1] }}, {{ mousePosition.index[2] }}
</div>
<div
v-if="(series.Modality === 'CT' || series.Modality === 'DR' || series.Modality === 'CR') && mousePosition.value">
HU: {{ mousePosition.value }}
</div>
<div v-else-if="(series.Modality === 'PT' && mousePosition.value)">
SUVbw(g/ml): {{ digitPlaces === -1 ? mousePosition.value.toFixed(3) :
mousePosition.value.toFixed(digitPlaces)
}}
</div>
<div v-else-if="mousePosition.value">
Density: {{ mousePosition.value }}
</div>
<div v-show="imageInfo.size">
W*H: {{ imageInfo.size }}
</div>
</div>
<div v-if="series" class="right-bottom-text">
<div v-show="imageInfo.location">Location: {{
`${Number(imageInfo.location).toFixed(digitPlaces)} mm`
}}</div>
<div v-show="imageInfo.sliceThickness">Slice Thickness: {{
`${Number(imageInfo.sliceThickness).toFixed(digitPlaces)} mm`
}}</div>
<div v-show="imageInfo.wwwc">WW/WL: {{ imageInfo.wwwc }}</div>
</div>
<div class="orientation-top">
{{ markers.top }}
</div>
<div class="orientation-right">
{{ markers.right }}
</div>
<div class="orientation-bottom">
{{ markers.bottom }}
</div>
<div class="orientation-left">
{{ markers.left }}
</div>
<div ref="sliderBox" class="right-slider-box" @click.stop="clickSlider($event)">
<div :style="{ top: sliderInfo.height + '%' }" class="slider" @click.stop.prevent="() => { return }"
@mousedown.stop="sliderMousedown($event)" />
</div>
</div>
</template>
<script>
import {
metaData,
getRenderingEngine,
utilities as csUtils,
cache
} from '@cornerstonejs/core'
import * as cornerstoneTools from '@cornerstonejs/tools'
import { createImageIdsAndCacheMetaData } from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/createImageIdsAndCacheMetaData'
import setCtTransferFunctionForVolumeActor from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/setCtTransferFunctionForVolumeActor'
import { setPetColorMapTransferFunctionForVolumeActor } from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/setPetColorMapTransferFunctionForVolumeActor'
import { vec3, mat4 } from 'gl-matrix'
import DicomEvent from '@/views/trials/trials-panel/reading/dicoms/components/DicomEvent'
export default {
name: 'MPRViewport',
props: {
renderingEngineId: {
type: String,
required: true
},
viewportId: {
type: String,
required: true
},
viewportIndex: {
type: Number,
required: true
},
MPRInfo: {
type: Object,
default: () => {
return {}
}
}
},
data() {
return {
element: '',
series: {},
ctSeries: {},
petSeries: {},
taskInfo: null,
sliderInfo: {
oldB: null,
oldM: null,
isMove: false,
height: 0
},
mousePosition: {
index: [],
value: null,
modalityUnit: '',
world: []
},
imageInfo: {
zoom: null,
size: null,
location: null,
sliceThickness: null,
wwwc: null,
total: 0,
sliceThickness: 0
},
digitPlaces: 2,
orientationMarkers: [],
originalMarkers: [],
markers: { top: '', right: '', bottom: '', left: '' },
playClipState: false,
wwwcIdx: 2,
presetName: '',
volumeId: null,
defaultWindowLevel: {},
rotateAngle: 0,
rotateBarLeft: 0,
loading: false,
}
},
mounted() {
this.taskInfo = JSON.parse(sessionStorage.getItem('taskInfo'))
const digitPlaces = Number(localStorage.getItem('digitPlaces'))
this.digitPlaces = digitPlaces === -1 ? this.digitPlaces : digitPlaces
this.$nextTick(() => {
this.initViewport()
})
},
watch: {
MPRInfo: {
handler() {
if (!this.series.orientation) return false
switch (this.series.orientation) {
case 'AXIAL':
this.imageInfo.size = `${this.MPRInfo.SAGITTAL.imageNum}*${this.MPRInfo.CORONAL.imageNum}`
break;
case 'CORONAL':
this.imageInfo.size = `${this.MPRInfo.SAGITTAL.imageNum}*${this.MPRInfo.AXIAL.imageNum}`
break;
case 'SAGITTAL':
this.imageInfo.size = `${this.MPRInfo.CORONAL.imageNum}*${this.MPRInfo.AXIAL.imageNum}`
break;
}
},
deep: true
}
},
methods: {
initViewport() {
this.element = this.$refs['viewport-volume']
const resizeObserver = new ResizeObserver(() => {
const renderingEngine = getRenderingEngine(this.renderingEngineId)
if (renderingEngine) {
renderingEngine.resize(true, false)
}
})
this.element.oncontextmenu = (e) => e.preventDefault()
// resizeObserver.observe(this.element)
this.element.addEventListener("CORNERSTONE_VOLUME_NEW_IMAGE", this.stackNewImage)
this.element.addEventListener('CORNERSTONE_VOI_MODIFIED', this.voiModified)
this.element.addEventListener('wheel', (e) => {
// console.log('CORNERSTONE_STACK_VIEWPORT_SCROLL')
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine.getViewport(this.viewportId)
const currentImageIdIndex = viewport.getCurrentImageIdIndex()
const totalImages = this.imageInfo.total;
//
if (currentImageIdIndex >= totalImages - 1 && e.wheelDeltaY < 0) {
//
csUtils.jumpToSlice(viewport.element, { imageIndex: 0 });
} else if (currentImageIdIndex <= 0 && e.wheelDeltaY > 0) {
//
csUtils.jumpToSlice(viewport.element, { imageIndex: totalImages - 1 });
}
});
this.element.addEventListener('CORNERSTONE_TOOLS_MOUSE_MOVE', this.cornerstoneToolsMouseMove)
// this.element.addEventListener(cornerstoneTools.Enums.Events.MOUSE_WHEEL, this.handletoolsMouseWheel)
this.element.addEventListener('mouseleave', () => {
this.mousePosition.index = []
this.mousePosition.value = null
})
document.addEventListener('mouseup', () => {
this.sliderMouseup()
})
document.addEventListener('mousemove', (e) => {
this.sliderMousemove(e)
})
},
determineImagePlane(imageOrientationPatient) {
// imageOrientationPatient [rowX, rowY, rowZ, colX, colY, colZ]
// (rowX, rowY, rowZ)
const [rowX, rowY, rowZ] = imageOrientationPatient;
// X, Y, Z
const dotX = Math.abs(rowX);
const dotY = Math.abs(rowY);
const dotZ = Math.abs(rowZ);
//
const maxDot = Math.max(dotX, dotY, dotZ);
//
if (maxDot === dotX) {
// X 线 Y Z
// 线
//
// X-Y (rowZ ) Axial
// X-Z (rowY ) Sagittal
// 线线
// 线 = ×
const [colX, colY, colZ] = imageOrientationPatient.slice(3);
const normalX = rowY * colZ - rowZ * colY;
const normalY = rowZ * colX - rowX * colZ;
const normalZ = rowX * colY - rowY * colX;
const absNormalX = Math.abs(normalX);
const absNormalY = Math.abs(normalY);
const absNormalZ = Math.abs(normalZ);
const maxNormal = Math.max(absNormalX, absNormalY, absNormalZ);
if (maxNormal === absNormalZ) {
return 'AXIAL';
} else if (maxNormal === absNormalY) {
return 'SAGITTAL';
} else if (maxNormal === absNormalX) {
return 'CORONAL';
}
} else if (maxDot === dotY) {
// Y Coronal
return 'SAGITTAL';
} else if (maxDot === dotZ) {
// Z Sagittal
return 'CORONAL';
}
return 'unknown';
},
stackNewImage(e) {
const { detail } = e
this.series.SliceIndex = detail.imageIndex
this.sliderInfo.height = detail.imageIndex * 100 / detail.numberOfSlices
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine.getViewport(this.viewportId)
const zoom = viewport.getZoom()
this.imageInfo.zoom = zoom.toFixed(4)
let imageIds = viewport.getImageIds(this.volumeId)
let imageId = imageIds[0]
let volume = cache.getVolume(this.volumeId)
let spacing = volume ? volume.spacing : []
// if (this.series.orientation === 'AXIAL') imageId = viewport.getCurrentImageId()
if (imageId && volume) {
this.$emit('setMPRInfo', { type: this.series.orientation, key: "imageNum", value: detail.numberOfSlices })
const imagePlaneModule = metaData.get('imagePlaneModule', imageId)
let type = this.determineImagePlane(imagePlaneModule.imageOrientationPatient)
this.imageInfo.location = type === this.series.orientation ? imagePlaneModule.sliceLocation : ''
this.imageInfo.sliceThickness = type === this.series.orientation ? spacing[2] : spacing[0]
this.imageInfo.total = detail.numberOfSlices
this.getOrientationMarker()
let properties = viewport.getProperties(this.volumeId)
if (properties && properties.voiRange) {
var { lower, upper } = properties.voiRange
const windowWidth = upper - lower
const windowCenter = (upper + lower) / 2
this.defaultWindowLevel.windowWidth = windowWidth
this.defaultWindowLevel.windowCenter = windowCenter
this.imageInfo.wwwc = `${Math.round(windowWidth)}/${Math.round(windowCenter)}`
}
}
if (volume) {
const toolGroupId = 'share-viewport-volume'
const toolGroup = cornerstoneTools.ToolGroupManager.getToolGroup(toolGroupId)
toolGroup.setToolEnabled('ScaleOverlay')
}
// const toolGroupId = this.viewportId
},
setFullScreen(index) {
setTimeout(() => {
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine.getViewport(
this.viewportId
)
csUtils.jumpToSlice(viewport.element, { imageIndex: index })
viewport.render()
})
},
voiModified(e) {
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine.getViewport(this.viewportId)
let properties = viewport.getProperties()
if (properties && properties.voiRange) {
var { lower, upper } = properties.voiRange
const { windowWidth, windowCenter } = csUtils.windowLevel.toWindowLevel(
lower,
upper
)
this.imageInfo.wwwc = `${Math.round(windowWidth)}/${Math.round(windowCenter)}`
this.$emit('upperRangeChange', Math.round(windowWidth))
}
},
getOrientationMarker() {
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine.getViewport(this.viewportId)
const { viewUp, viewPlaneNormal } = viewport.getCamera()
const viewRight = vec3.create()
vec3.cross(viewRight, viewUp, viewPlaneNormal)
const columnCosines = [-viewUp[0], -viewUp[1], -viewUp[2]]
const rowCosines = viewRight
const rowString = cornerstoneTools.utilities.orientation.getOrientationStringLPS(rowCosines)
const columnString = cornerstoneTools.utilities.orientation.getOrientationStringLPS(columnCosines)
const oppositeRowString = cornerstoneTools.utilities.orientation.invertOrientationStringLPS(rowString)
const oppositeColumnString = cornerstoneTools.utilities.orientation.invertOrientationStringLPS(columnString)
this.markers.top = oppositeColumnString
this.markers.right = rowString
this.markers.bottom = columnString
this.markers.left = oppositeRowString
this.orientationMarkers = [oppositeColumnString, rowString, columnString, oppositeRowString]
if (this.originalMarkers.length === 0) {
this.originalMarkers = [...this.orientationMarkers]
}
},
setMarkers() {
const markers = [...this.orientationMarkers]
for (const key in this.markers) {
const v = markers.shift(0)
this.markers[key] = v
}
},
resetOrientationMarkers() {
if (this.originalMarkers.length > 0) {
this.orientationMarkers = [...this.originalMarkers]
this.setMarkers()
}
},
rotateOrientationMarkers(type) {
if (this.orientationMarkers.length > 0) {
if (type === 1) {
this.resetOrientationMarkers()
return
}
const markers = [...this.orientationMarkers]
if (type === 2) {
//
this.orientationMarkers[0] = markers[2]
this.orientationMarkers[2] = markers[0]
} else if (type === 3) {
//
this.orientationMarkers[1] = markers[3]
this.orientationMarkers[3] = markers[1]
} else if (type === 4) {
// 90
this.orientationMarkers = markers.slice(1, 4).concat(markers[0])
} else if (type === 5) {
// 90
this.orientationMarkers = [markers[3]].concat(markers.slice(0, 3))
}
this.setMarkers()
}
},
toggleClipPlay(isPlay, framesPerSecond) {
this.playClipState = isPlay
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine.getViewport(this.viewportId)
if (isPlay) {
cornerstoneTools.utilities.cine.playClip(viewport.element, { framesPerSecond, loop: true })
} else {
cornerstoneTools.utilities.cine.stopClip(viewport.element)
}
},
scrollPage(type) {
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine.getViewport(this.viewportId)
const currentImageIdIndex = viewport.getCurrentImageIdIndex()
const numImages = viewport.getImageIds().length
let newImageIdIndex = null
if (type === 0) {
newImageIdIndex = 0
} else if (type === -1) {
newImageIdIndex = currentImageIdIndex === 0 ? currentImageIdIndex : currentImageIdIndex - 1
} else if (type === 1) {
newImageIdIndex = currentImageIdIndex === numImages - 1 ? currentImageIdIndex : currentImageIdIndex + 1
} else if (type === 99999) {
newImageIdIndex = numImages - 1
}
// viewport.setImageIdIndex(newImageIdIndex)
csUtils.jumpToSlice(viewport.element, { imageIndex: newImageIdIndex })
cornerstoneTools.utilities.cine.stopClip(viewport.element)
},
setZoom(ratio) {
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine.getViewport(this.viewportId)
const zoom = viewport.getZoom()
if (ratio > 0) {
viewport.setZoom(zoom * 1.05)
} else {
viewport.setZoom(zoom / 1.05)
}
viewport.render()
},
resize(forceFitToWindow) {
console.log('resize: ', forceFitToWindow)
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine.getViewport(this.viewportId)
if (!forceFitToWindow) {
viewport.setZoom(0.5)
viewport.render()
} else {
viewport.setZoom(1)
viewport.render()
}
},
voiChange(v) {
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const voiRange = { lower: 0, upper: v }
const viewport = renderingEngine.getViewport(this.viewportId)
if (!viewport) return
let volumeId = this.volumeId
const viewportsContainingVolumeUID = csUtils.getViewportsWithVolumeId(
volumeId,
viewport.renderingEngineId
)
viewport.setProperties({ voiRange }, volumeId)
viewportsContainingVolumeUID.forEach((vp) => {
vp.render()
// this.$refs[vp.id].voiModified()
this.voiModified()
})
},
setPreset(presetName) {
this.presetName = presetName
},
async createImageIdsAndCacheMetaData(obj) {
this.loading = true
await createImageIdsAndCacheMetaData({
modality: obj.Modality,
imageIds: obj.ImageIds
})
this.loading = false
},
async setSeriesInfo(obj, isLocate = false) {
try {
let data = obj
if (this.series && data.Id === this.series.Id && data.Description === this.series.Description && !isLocate && !data.isLocation) {
data.SliceIndex = this.series.SliceIndex
}
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine.getViewport(this.viewportId)
if (isLocate) return csUtils.jumpToSlice(viewport.element, { imageIndex: data.SliceIndex });
this.series = {}
this.volumeId = data.SeriesInstanceUid
this.series = { ...data }
viewport
.setVolumes([{
volumeId: this.volumeId, callback: (r) => {
if (this.series.Modality === 'PT') {
setPetColorMapTransferFunctionForVolumeActor(r, true)
} else {
setCtTransferFunctionForVolumeActor(r)
}
console.log("渲染成功")
DicomEvent.$emit("isloaded", {})
}
}]).then(res => {
if (data.isLocation) {
setTimeout(() => { csUtils.jumpToSlice(viewport.element, { imageIndex: data.SliceIndex }); })
}
})
viewport.render()
} catch (e) {
console.log(e)
}
},
cornerstoneToolsMouseMove(e) {
const { currentPoints } = e.detail
const worldPoint = currentPoints.world
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine.getViewport(this.viewportId)
const imageData = viewport.getImageData()
if (!imageData) return
const index = imageData.imageData.worldToIndex(worldPoint)
index[0] = Math.floor(index[0])
index[1] = Math.floor(index[1])
index[2] = Math.floor(index[2])
this.mousePosition.index = index
},
toggleTask(evt, visitTaskNum, i) {
this.$emit('activeViewport', this.viewportIndex)
const num = visitTaskNum + i
if (num >= 0 && num <= this.taskInfo.VisitNum) {
this.$emit('toggleTaskByViewport', { series: this.series, visitTaskNum: num })
}
evt.stopImmediatePropagation()
evt.stopPropagation()
evt.preventDefault()
},
viewCD(taskId) {
this.$emit('previewCD', taskId)
},
setWwwcIdx(idx) {
this.wwwcIdx = idx
},
clickSlider(e) {
const height = e.offsetY * 100 / this.$refs['sliderBox'].clientHeight
this.sliderInfo.height = height
let sliceIdx = Math.trunc(this.imageInfo.total * height / 100)
sliceIdx = sliceIdx >= this.imageInfo.total ? this.imageInfo.total : sliceIdx < 0 ? 0 : sliceIdx
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine.getViewport(
this.viewportId
)
// viewport.setImageIdIndex(sliceIdx)
csUtils.jumpToSlice(viewport.element, { imageIndex: sliceIdx })
viewport.render()
},
sliderMouseup(e) {
this.sliderInfo.isMove = false
this.$emit('contentMouseup', e)
},
sliderMousedown(e) {
const boxHeight = this.$refs['sliderBox'].clientHeight
this.sliderInfo.oldB = parseInt(e.srcElement.style.top) * boxHeight / 100
this.sliderInfo.oldM = e.clientY
this.sliderInfo.isMove = true
e.stopImmediatePropagation()
e.stopPropagation()
e.preventDefault()
},
sliderMousemove(e) {
if (!this.sliderInfo.isMove) return
const delta = this.sliderInfo.oldB - (this.sliderInfo.oldM - e.clientY)
const boxHeight = this.$refs['sliderBox'].clientHeight
if (delta < 0) return
if (delta > boxHeight) return
const height = delta * 100 / boxHeight
let sliceIdx = Math.trunc(this.imageInfo.total * height / 100)
sliceIdx = sliceIdx >= this.imageInfo.total ? this.imageInfo.total - 1 : sliceIdx < 0 ? 0 : sliceIdx
this.sliderInfo.height = height
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine.getViewport(
this.viewportId
)
// viewport.setImageIdIndex(sliceIdx)
csUtils.jumpToSlice(viewport.element, { imageIndex: sliceIdx })
viewport.render()
},
sliderMouseleave(e) {
if (!this.sliderInfo.isMove) return
this.sliderInfo.isMove = false
},
rotate(angle) {
let renderingEngine = getRenderingEngine(this.renderingEngineId)
let viewport = renderingEngine.getViewport(this.viewportId)
const camera = viewport.getCamera()
const { viewUp, position, focalPoint } = camera
const [cx, cy, cz] = focalPoint
const [ax, ay, az] = [0, 0, 1]
const newPosition = [0, 0, 0]
const newFocalPoint = [0, 0, 0]
const newViewUp = [0, 0, 0]
const transform = mat4.identity(new Float32Array(16))
mat4.translate(transform, transform, [cx, cy, cz])
mat4.rotate(transform, transform, angle, [ax, ay, az])
mat4.translate(transform, transform, [-cx, -cy, -cz])
vec3.transformMat4(newPosition, position, transform)
vec3.transformMat4(newFocalPoint, focalPoint, transform)
mat4.identity(transform)
mat4.rotate(transform, transform, angle, [ax, ay, az])
vec3.transformMat4(newViewUp, viewUp, transform)
viewport.setCamera({
position: newPosition,
viewUp: newViewUp,
focalPoint: newFocalPoint
})
viewport.render()
},
clickRotate(e) {
// console.log('clickRotate')
const container = document.getElementById('rotateBar')
const containerWidth = container.offsetWidth
const slider = document.getElementById('rotateSlider')
const sliderWidth = slider.offsetWidth
const x = Math.trunc(e.offsetX)
const deltaX = x - this.rotateBarLeft
const angle = Math.sin((deltaX * (360 / (containerWidth - sliderWidth))) * Math.PI / 180)
this.rotate(angle)
this.rotateBarLeft = x
},
preventDefault(e) {
e.stopImmediatePropagation()
e.stopPropagation()
e.preventDefault()
}
},
computed: {
NSTip() {
return `NS: ${this.$store.state.trials.uploadTip}`
}
},
}
</script>
<style lang="scss" scoped>
.viewport-wrapper {
width: 100%;
height: 100%;
position: relative;
cursor: default !important;
.left-top-text {
position: absolute;
left: 5px;
top: 5px;
// color: #ddd;
z-index: 1;
font-size: 12px;
.cd-info {
// color: #ddd;
font-size: 18px;
}
.subject-info {
color: #f44336;
padding: 5px 0px;
margin: 0;
}
}
.top-center-tool {
position: absolute;
left: 50%;
top: 5px;
transform: translateX(-50%);
z-index: 1;
.toggle-visit-container {
display: flex;
}
.arrw_icon {
width: 20px;
height: 20px;
background-color: #3f3f3f;
text-align: center;
line-height: 20px;
border-radius: 10%;
}
.arrow_text {
height: 20px;
line-height: 20px;
background-color: #00000057;
color: #fff;
padding: 0 10px;
font-size: 14px;
}
}
.right-top-text {
position: absolute;
right: 5px;
top: 5px;
// color: #ddd;
z-index: 1;
font-size: 12px;
}
.left-bottom-text {
position: absolute;
left: 5px;
bottom: 5px;
// color: #ddd;
z-index: 1;
font-size: 12px;
}
.right-bottom-text {
position: absolute;
right: 5px;
bottom: 5px;
// color: #ddd;
z-index: 1;
font-size: 12px;
}
.right-slider-box {
position: absolute;
right: 1px;
height: calc(100% - 140px);
transform: translateY(-50%);
top: calc(50% - 30px);
width: 10px;
background: #333;
z-index: 1;
cursor: pointer;
}
.right-slider-box:after {
content: '';
position: absolute;
bottom: -20px;
left: 0;
height: 20px;
width: 100%;
background: #333;
}
.slider {
height: 20px;
width: 100%;
position: absolute;
top: 0;
z-index: 10;
background: #9e9e9e;
cursor: move
}
.orientation-top {
position: absolute;
left: 50%;
top: 30px;
color: #f44336;
transform: translateX(-50%);
z-index: 1;
}
.orientation-bottom {
position: absolute;
left: 50%;
bottom: 15px;
color: #f44336;
transform: translateX(-50%);
z-index: 1;
}
.orientation-left {
position: absolute;
top: 50%;
left: 15px;
color: #f44336;
transform: translateY(-50%);
z-index: 1;
}
.orientation-right {
position: absolute;
top: 50%;
right: 15px;
color: #f44336;
transform: translateY(-50%);
z-index: 1;
}
}
.color_bar {
position: absolute;
transform: rotate(-90deg);
transform-origin: right;
left: -150px;
top: 30%;
z-index: 1;
}
.rotate_slider_box {
position: absolute;
width: 380px;
height: 10px;
bottom: 5px;
left: 50%;
transform: translateX(-50%);
background: #333;
z-index: 10;
cursor: pointer;
.box {
z-index: 10;
background: #9e9e9e;
height: 100%;
width: 20px;
position: absolute;
top: 0;
cursor: move
}
}
</style>
@@ -1,7 +1,12 @@
<template>
<div ref="viewport-fusion" class="viewport-wrapper" v-loading="loading" :element-loading-text="NSTip"
element-loading-background="rgba(0, 0, 0, 0.8)" @mouseup="sliderMouseup" @mousemove="sliderMousemove"
@mouseleave="sliderMouseleave" :style="{ color: series.Modality === 'PT' || isMip ? '#666' : '#ddd' }">
@mouseleave="sliderMouseleave" :style="{ color: series.Modality === 'PT' || series.Modality === 'NM' || isMip ? '#666' : '#ddd' }">
<div v-if="isFusion" class="opacity-slider-wrapper" @mousedown.stop @mousemove.stop @mouseup.stop @wheel.stop>
<div class="slider-title">{{ Math.round(fusionOpacity * 100) }}%</div>
<input type="range" min="0" max="1" step="0.05" v-model.number="fusionOpacity" @input="applyFusionOpacity"
class="opacity-slider" />
</div>
<div v-if="series && taskInfo" class="left-top-text">
<div v-if="taskInfo.IsExistsClinicalData && !isMip && !isFusion" class="cd-info"
:title="$t('trials:reading:button:clinicalData')">
@@ -14,30 +19,16 @@
<div v-if="!isMip && !isFusion">Series: #{{ series.SeriesNumber }}</div>
<div v-if="series.Stack && !isMip">Image: #{{ `${series.SliceIndex + 1}/${series.Stack.length}` }}</div>
<div v-if="!isMip && !isFusion">{{ series.Modality }}</div>
<div v-if="isFusion">{{ ctSeries.Modality }} / {{ series.Modality }}</div>
<div v-if="isMip">MIP</div>
</div>
<!-- <div v-if="series && taskInfo && taskInfo.IsReadingTaskViewInOrder === 1 && series.TaskInfo && !isMip && !isFusion"
class="top-center-tool">
<div class="toggle-visit-container">
<div class="arrw_icon"
:style="{ cursor: series.TaskInfo.VisitTaskNum !== 0 ? 'pointer' : 'not-allowed', color: series.TaskInfo.VisitTaskNum !== 0 ? '#fff' : '#6b6b6b' }"
@click.stop.prevent="toggleTask($event, series.TaskInfo.VisitTaskNum, -1)"
@dblclick.stop="preventDefault($event)">
<i class="el-icon-caret-left" />
</div>
<div class="arrow_text">
{{ series.TaskInfo.TaskBlindName }}
</div>
<div class="arrw_icon"
:style="{ cursor: series.TaskInfo.VisitTaskNum < taskInfo.VisitNum ? 'pointer' : 'not-allowed', color: series.TaskInfo.VisitTaskNum < taskInfo.VisitNum ? '#fff' : '#6b6b6b' }"
@click.stop.prevent="toggleTask($event, series.TaskInfo.VisitTaskNum, 1)"
@dblclick.stop="preventDefault($event)">
<i class="el-icon-caret-right" />
</div>
</div>
</div> -->
<div v-if="series && !isMip && !isFusion" class="right-top-text">
<div>{{ series.Description }}</div>
</div>
<div v-if="isFusion && !isMip" class="fusion-order-toggle" @mousedown.stop @mouseup.stop
@click.stop="toggleFusionRenderOrder">
{{ fusionCtOnTop ? `${ctSeries.Modality}/${series.Modality}` : `${series.Modality}/${ctSeries.Modality}` }}
</div>
<div v-if="series" class="left-bottom-text">
<div v-show="mousePosition.index.length > 0 && !isMip">
Pos: {{ mousePosition.index[0] }}, {{ mousePosition.index[1] }}, {{ mousePosition.index[2] }}
@@ -104,9 +95,14 @@ import {
import * as cornerstoneTools from '@cornerstonejs/tools'
import cornerstoneDICOMImageLoader from '@cornerstonejs/dicom-image-loader';
import vtkColorMaps from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps'
import { createImageIdsAndCacheMetaData } from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/createImageIdsAndCacheMetaData'
import setCtTransferFunctionForVolumeActor from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/setCtTransferFunctionForVolumeActor'
import { setPetColorMapTransferFunctionForVolumeActor } from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/setPetColorMapTransferFunctionForVolumeActor'
import {
setMipTransferFunctionForVolumeActor,
setPetTransferFunctionForVolumeActor
} from './helpers/index.js'
const { BlendModes, OrientationAxis } = Enums;
const { getColormap } = csUtils.colormap;
import { vec3, mat4 } from 'gl-matrix'
@@ -134,6 +130,7 @@ export default {
petSeries: {},
isFusion: false,
isMip: false,
fusionCtOnTop: false,
taskInfo: null,
sliderInfo: {
oldB: null,
@@ -172,7 +169,11 @@ export default {
isMove: false
},
ptVolumeId: null,
loading: false
loading: false,
Colorbar: null,
fusionOpacity: 0.5,
topFusionVolumeActor: null,
currentVoiUpper: null
}
},
mounted() {
@@ -220,7 +221,9 @@ export default {
},
stackNewImage(e) {
const { detail } = e
this.series.SliceIndex = detail.imageIndex
if (this.series) {
this.series.SliceIndex = detail.imageIndex
}
this.sliderInfo.height = detail.imageIndex * 100 / detail.numberOfSlices
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine.getViewport(this.viewportId)
@@ -267,16 +270,29 @@ export default {
const viewport = renderingEngine.getViewport(this.viewportId)
let properties = viewport.getProperties()
if (this.isFusion) {
properties = viewport.getProperties(this.volumeId)
properties = viewport.getProperties(this.ptVolumeId || this.volumeId)
}
if (properties && properties.voiRange) {
var { lower, upper } = properties.voiRange
if ((!upper || upper === 0) && this.currentVoiUpper > 0) {
upper = this.currentVoiUpper
} else if (upper) {
this.currentVoiUpper = upper
}
if (!upper) return
const { windowWidth, windowCenter } = csUtils.windowLevel.toWindowLevel(
lower,
upper
)
this.imageInfo.wwwc = `${Math.round(windowWidth)}/${Math.round(windowCenter)}`
this.$emit('upperRangeChange', Math.round(windowWidth))
if (this.series.Modality === 'PT' || this.series.Modality === 'NM' || this.isFusion) {
this.$emit('upperRangeChange', Math.round(upper))
}
if (this.isFusion && !this.fusionCtOnTop && this.topFusionVolumeActor) {
this.applyFusionOpacity()
}
}
},
getOrientationMarker() {
@@ -397,6 +413,11 @@ export default {
voiChange(v) {
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const voiRange = { lower: 0, upper: v }
this.currentVoiUpper = v
if (this.isFusion) {
console.log('voiChange', v)
}
const viewport = renderingEngine.getViewport(this.viewportId)
if (!viewport) return
let volumeId = this.isFusion ? this.ptVolumeId : this.volumeId
@@ -406,6 +427,11 @@ export default {
)
viewport.setProperties({ voiRange }, volumeId)
// if (this.isFusion && !this.fusionCtOnTop && this.topFusionVolumeActor) {
// this.applyFusionOpacity()
// }
viewportsContainingVolumeUID.forEach((vp) => {
vp.render()
// this.$refs[vp.id].voiModified()
@@ -445,6 +471,22 @@ export default {
ctx.fillStyle = gradient
ctx.fillRect(0, 0, rectWidth, rectHeight)
},
opacityChange(opacity) {
this.fusionOpacity = opacity
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine.getViewport(this.viewportId)
if (!viewport) return
let volumeId = this.isFusion ? this.ptVolumeId : this.volumeId
viewport.setProperties(
{ colormap: { opacity: Number(opacity) } },
volumeId
)
viewport.render()
},
applyFusionOpacity() {
this.opacityChange(this.fusionOpacity)
},
setPreset(presetName) {
this.presetName = presetName
},
@@ -476,23 +518,103 @@ export default {
})
this.loading = false
},
getFusionVolumes() {
const ctVolumeId = this.ctSeries?.SeriesInstanceUid
const ptFusionVolumeId = this.ptVolumeId
if (!ctVolumeId || !ptFusionVolumeId) {
return []
}
const ctEntry = {
volumeId: ctVolumeId,
callback: (r) => {
setCtTransferFunctionForVolumeActor({ ...r, volumeId: ctVolumeId })
if (this.fusionCtOnTop) {
this.topFusionVolumeActor = r.volumeActor
this.applyFusionOpacity()
}
console.log("融合ct渲染成功")
}
}
const ptFusionEntry = {
volumeId: ptFusionVolumeId,
callback: (r) => {
setPetColorMapTransferFunctionForVolumeActor({ ...r, volumeId: ptFusionVolumeId })
if (!this.fusionCtOnTop) {
this.topFusionVolumeActor = r.volumeActor
this.applyFusionOpacity()
}
console.log("融合pet渲染成功")
}
}
const volumes = []
if (this.series.Modality !== 'NM') {
volumes.push({
volumeId: this.volumeId,
callback: (r) => {
console.log("融合pet渲染成功");
}
})
}
if (this.fusionCtOnTop) {
volumes.push(ptFusionEntry, ctEntry)
} else {
volumes.push(ctEntry, ptFusionEntry)
}
return volumes
},
async applyFusionRenderOrder() {
if (!this.isFusion) return
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine?.getViewport?.(this.viewportId)
if (!viewport) return
const volumes = this.getFusionVolumes()
if (!volumes.length) return
const camera = viewport.getCamera?.()
const savedVoiUpper = this.currentVoiUpper
await viewport.setVolumes(volumes)
if (camera) {
viewport.setCamera(camera)
}
if (savedVoiUpper) {
viewport.setProperties({ voiRange: { lower: 0, upper: savedVoiUpper } }, this.ptVolumeId)
}
viewport.render()
},
toggleFusionRenderOrder() {
if (!this.isFusion) return
this.fusionCtOnTop = !this.fusionCtOnTop
this.applyFusionRenderOrder()
this.$emit('upperRangeChange', Math.round(this.currentVoiUpper))
},
async setSeriesInfo(obj, isLocate = false, option = {}) {
try {
let { data } = obj
if (this.series && data.Id === this.series.Id && data.Description === this.series.Description && !isLocate) {
data.SliceIndex = this.series.SliceIndex
}
// this.series = { ...data }
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine.getViewport(this.viewportId)
if (isLocate) return csUtils.jumpToSlice(viewport.element, { imageIndex: data.SliceIndex });
this.volumeId = data.SeriesInstanceUid
this.ptVolumeId = null
this.series = {}
this.topFusionVolumeActor = null
let { isFusion, isMip, colorMap } = option
this.isFusion = isFusion;
this.isMip = isMip;
if (this.isFusion) {
this.fusionCtOnTop = false
this.$nextTick(() => {
this.renderColorBar(this.presetName)
})
@@ -500,32 +622,10 @@ export default {
let { ct, data } = obj
this.series = { ...data }
this.ctSeries = { ...ct }
this.petSeries = { ...data }
await viewport
.setVolumes([
{
volumeId: this.volumeId, callback: (r) => {
setPetColorMapTransferFunctionForVolumeActor(r)
console.log("融合pet渲染成功");
}
},
{
volumeId: ct.SeriesInstanceUid, callback: (r) => {
setCtTransferFunctionForVolumeActor(r)
console.log("融合ct渲染成功")
}
},
{
volumeId: this.ptVolumeId, callback: (r) => {
setPetColorMapTransferFunctionForVolumeActor(r)
console.log("融合pet渲染成功");
}
},
]).then(res => {
if (colorMap) {
this.setColorMap(this.presetName)
}
})
await viewport.setVolumes(this.getFusionVolumes())
} else {
this.series = { ...data }
if (this.isMip) {
@@ -540,7 +640,12 @@ export default {
.setVolumes([{
volumeId: this.volumeId,
callback: (r) => {
setPetColorMapTransferFunctionForVolumeActor(r)
if (this.series.Modality === 'NM') {
setMipTransferFunctionForVolumeActor({ ...r, volumeId: this.volumeId })
} else {
setPetTransferFunctionForVolumeActor(r)
}
// setPetColorMapTransferFunctionForVolumeActor(r)
console.log("mip渲染成功")
},
slabThickness,
@@ -548,34 +653,24 @@ export default {
defaultOptions: {
orientation: OrientationAxis.CORONAL
}
}]).then(res => {
if (colorMap) {
this.setColorMap(this.presetName)
}
if (isLocate) {
setTimeout(() => { csUtils.jumpToSlice(viewport.element, { imageIndex: data.SliceIndex }); })
}
})
}])
} else {
viewport
.setVolumes([{
volumeId: this.volumeId, callback: (r) => {
if (this.series.Modality === 'PT') {
setPetColorMapTransferFunctionForVolumeActor(r, true)
if (this.series.Modality === 'PT' || this.series.Modality === 'NM') {
// setPetColorMapTransferFunctionForVolumeActor(r, true)
setPetTransferFunctionForVolumeActor({ ...r, volumeId: this.volumeId })
} else {
setCtTransferFunctionForVolumeActor(r)
setCtTransferFunctionForVolumeActor({ ...r, volumeId: this.volumeId })
}
console.log("渲染成功")
}
}]).then(res => {
if (colorMap) {
this.setColorMap(this.presetName)
}
})
}])
}
}
viewport.render()
this.voiChange(this.currentVoiUpper)
} catch (e) {
console.log(e)
}
@@ -751,7 +846,11 @@ export default {
e.stopImmediatePropagation()
e.stopPropagation()
e.preventDefault()
}
},
},
beforeDestroy() {
this.series = null
this.topFusionVolumeActor = null
},
computed: {
NSTip() {
@@ -767,6 +866,41 @@ export default {
position: relative;
cursor: default !important;
.opacity-slider-wrapper {
position: absolute;
right: 15px;
top: 50%;
transform: translateY(-50%);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
z-index: 10;
background: rgba(0, 0, 0, 0.5);
padding: 10px;
border-radius: 4px;
width: 40px;
.slider-title {
font-size: 12px;
color: #ddd;
margin-bottom: 10px;
width: 30px;
text-align: center;
}
.opacity-slider {
-webkit-appearance: slider-vertical;
writing-mode: bt-lr;
width: 10px;
height: 150px;
padding: 0;
margin: 0;
cursor: pointer;
outline: none;
}
}
.left-top-text {
position: absolute;
left: 5px;
@@ -826,6 +960,20 @@ export default {
font-size: 12px;
}
.fusion-order-toggle {
position: absolute;
right: 5px;
top: 5px;
z-index: 2;
font-size: 12px;
padding: 2px 8px;
background: rgba(0, 0, 0, 0.35);
border: 1px solid rgba(255, 255, 255, 0.25);
border-radius: 2px;
cursor: pointer;
user-select: none;
}
.left-bottom-text {
position: absolute;
left: 5px;
@@ -943,4 +1091,4 @@ export default {
cursor: move
}
}
</style>
</style>
File diff suppressed because it is too large Load Diff
@@ -1986,12 +1986,12 @@ export default {
.merge-table {
padding: 0 10px;
::v-deep.el-table {
::v-deep .el-table {
background-color: #1e1e1e !important;
color: #383838;
}
::v-deep.el-table td.el-table__cell,
::v-deep .el-table td.el-table__cell,
.el-table th.el-table__cell.is-leaf {
border-bottom: 1px solid #383838;
}
@@ -2002,7 +2002,7 @@ export default {
background-color: #1e1e1e;
}
::v-deep.el-table__header-wrapper {
::v-deep .el-table__header-wrapper {
th {
background-color: #1e1e1e !important;
color: #dfdfdf;
@@ -2010,7 +2010,7 @@ export default {
}
}
::v-deep.el-table__body-wrapper {
::v-deep .el-table__body-wrapper {
tr {
background-color: #1e1e1e !important;
color: #dfdfdf;
@@ -2021,7 +2021,7 @@ export default {
}
}
::v-deep.el-table__empty-block {
::v-deep .el-table__empty-block {
background-color: #1e1e1e !important;
}
@@ -0,0 +1,238 @@
<template>
<el-form ref="segmentForm" :model="form" label-width="120px" label-position="left">
<!-- 检查名称 -->
<el-form-item :label="$t('segment:form:label:studyName')" prop="taskBlindName">
<el-select v-model="form.studyId" clearable @change="(e) => handleChange(e, 'study')">
<el-option v-for="item in studyList" :key="item.StudyId" :label="item.StudyCode"
:value="item.StudyId" />
</el-select>
</el-form-item>
<!-- 序列名称 -->
<el-form-item :label="$t('segment:form:label:seriesName')" prop="taskBlindName">
<el-select v-model="form.seriesId" clearable @change="(e) => handleChange(e, 'series')">
<el-option v-for="item in seriesList" :key="item.Id"
:label="`#${item.SeriesNumber}/${item.Modality}/${item.Description}`" :value="item.Id" />
</el-select>
</el-form-item>
<!-- 分割组名称 -->
<el-form-item :label="$t('segment:form:label:segmentGroupName')" prop="taskBlindName">
<el-select v-model="form.segmentGroupId" clearable @change="(e) => handleChange(e, 'segmentGroup')">
<el-option v-for="item in segmentGroupList" :key="item.Id" :label="item.SegmentationName"
:value="item.Id" />
</el-select>
</el-form-item>
<!-- 分割名称 -->
<el-form-item :label="$t('segment:form:label:segmentName')" prop="taskBlindName">
<el-select v-model="form.segmentId" clearable>
<el-option v-for="item in segmentList" :key="item.Id" :label="item.SegmentName" :value="item.Id" />
</el-select>
</el-form-item>
<el-form-item style="text-align:right;">
<!-- 取消 -->
<el-button size="mini" @click="handleCancel">{{ $t('common:button:cancel') }}</el-button>
<!-- 确认 -->
<el-button type="primary" size="mini" @click="handleSave">
{{ $t('common:button:confirm') }}</el-button>
</el-form-item>
</el-form>
</template>
<script>
import { getSegmentationList, getSegmentList, getSegmentBindingList } from '@/api/reading'
export default {
name: 'FusionForm',
props: {
visible: {
type: Boolean,
default: false
},
visitInfo: {
type: Object,
default: () => {
return {}
}
}
},
data() {
return {
form: {
studyId: '',
seriesId: '',
segmentGroupId: '',
segmentId: ""
},
studyList: [],
seriesList: [],
segmentGroupList: [],
segmentList: [],
segmentionList: [],
series: {}
}
},
// mounted() {
// this.init()
// },
methods: {
setSeries(series) {
this.series = series
},
async init() {
let studyList = Object.assign(this.visitInfo.StudyList, {})
let s = await this.getSegmentationList()
this.segmentionList = s
let StudyIds = s.map(item => item.StudyId)
let SeriesIds = s.map(item => item.SeriesId)
studyList = studyList.filter(item => StudyIds.includes(item.StudyId))
studyList.forEach(study => {
study.SeriesArr = study.SeriesList.filter(item => SeriesIds.includes(item.Id))
})
this.studyList = studyList
if (this.visitInfo.operateStateEnum === 21) {
this.form.studyId = this.series.StudyId
this.handleChange(null, 'study')
this.form.seriesId = this.series.Id
this.handleChange(null, 'series')
}
if (this.visitInfo.operateStateEnum === 22) {
let o = {}
if (this.isTableQuestion) {
o.TableQuestionId = this.visitInfo.operateQuestionId
o.RowId = this.visitInfo.operateRowId
} else {
o.QuestionId = this.visitInfo.operateQuestionId
}
let list = await this.getSegmentBindingList(o)
let segmentGroup = this.segmentionList.find(item => item.Id === list[0].SegmentationId)
this.form.studyId = segmentGroup.StudyId
this.handleChange(null, 'study')
this.form.seriesId = segmentGroup.SeriesId
this.handleChange(null, 'series')
this.form.segmentGroupId = list[0].SegmentationId
this.handleChange(null, 'segmentGroup')
this.form.segmentId = list[0].SegmentId
}
},
async handleChange(e, key) {
if (key === 'study') {
this.seriesList = this.studyList.find(item => item.StudyId === this.form.studyId).SeriesArr
}
if (key === 'series') {
this.segmentGroupList = this.segmentionList.filter(item => item.SeriesId === this.form.seriesId)
}
if (key === 'segmentGroup') {
let list = await this.getSegmentList(this.form.segmentGroupId)
this.segmentList = list.filter(item => item.SegmentJson)
}
},
handleCancel() {
this.$emit("update:visible", false)
},
handleSave() {
let segment = this.segmentList.find(item => item.Id === this.form.segmentId)
if (segment.SegmentJson) {
let obj = JSON.parse(segment.SegmentJson)
segment.stats = obj.stats
segment.bidirectional = obj.bidirectional
}
this.$emit('handleSegmentSave', segment)
this.handleCancel()
},
//
async getSegmentationList() {
try {
let data = {
VisitTaskId: this.visitInfo.VisitTaskId,
PageSize: 9999,
PageIndex: 1,
}
this.loading = true;
let res = await getSegmentationList(data);
this.loading = false;
if (res.IsSuccess) {
return res.Result.CurrentPageData;
}
} catch (err) {
this.loading = false
console.log(err)
}
},
//
async getSegmentList(id) {
try {
let data = {
SegmentationId: id,
PageSize: 9999,
PageIndex: 1,
}
this.loading = true;
let res = await getSegmentList(data)
this.loading = false;
if (res.IsSuccess) {
return res.Result.CurrentPageData
}
} catch (err) {
this.loading = false
console.log(err)
}
},
//
async getSegmentBindingList(param = {}) {
try {
let data = {
VisitTaskId: this.visitInfo.VisitTaskId,
PageSize: 9999,
PageIndex: 1,
}
data = Object.assign(data, param)
let res = await getSegmentBindingList(data)
if (res.IsSuccess) {
return res.Result.CurrentPageData
}
} catch (err) {
console.log(err)
}
},
}
}
</script>
<style lang="scss" scoped>
.series-table {
::v-deep.el-table {
background-color: #1e1e1e !important;
color: #dfdfdf;
}
::v-deep.el-table td.el-table__cell,
.el-table th.el-table__cell.is-leaf {
border-bottom: 1px solid #dfdfdf;
}
.el-table--border::after,
.el-table--group::after,
.el-table::before {
background-color: #1e1e1e;
}
::v-deep.el-table__header-wrapper {
th {
background-color: #1e1e1e !important;
color: #dfdfdf;
}
}
::v-deep.el-table__body-wrapper {
tr {
background-color: #1e1e1e !important;
color: #dfdfdf;
}
tr:hover>td {
background-color: #1e1e1e !important;
}
}
::v-deep.el-table__empty-block {
background-color: #1e1e1e !important;
}
}
</style>
File diff suppressed because it is too large Load Diff
@@ -1,16 +1,10 @@
<template>
<div v-loading="loading" class="study-wrapper">
<div class="study-info">
<div
v-if="taskInfo && taskInfo.IsReadingShowSubjectInfo"
:title="taskInfo.SubjectCode"
>
<div v-if="taskInfo && taskInfo.IsReadingShowSubjectInfo" :title="taskInfo.SubjectCode">
{{ taskInfo.SubjectCode }}
</div>
<div
v-if="taskInfo && taskInfo.IsReadingShowSubjectInfo"
:title="visitTaskInfo.TaskBlindName"
>
<div v-if="taskInfo && taskInfo.IsReadingShowSubjectInfo" :title="visitTaskInfo.TaskBlindName">
{{ visitTaskInfo.TaskBlindName }}
</div>
</div>
@@ -18,15 +12,14 @@
<el-collapse v-model="activeNames">
<el-collapse-item v-for="(study, index) in studyList" :key="`${study.StudyId}`" :name="`${study.StudyId}`">
<template slot="title">
<div
v-if="!study.IsCriticalSequence"
class="dicom-desc"
>
<div v-if="!study.IsCriticalSequence" class="dicom-desc">
<template v-if="taskInfo && taskInfo.IsShowStudyName">
<div style="text-overflow: ellipsis;overflow: hidden;">
<span :title="study.StudyCode">{{ study.StudyCode }}</span>
<span v-if="study.StudyName" :title="study.StudyName" style="margin-left: 5px;">{{ study.StudyName }}</span>
<span v-else :title="study.Modalities" style="margin-left: 5px;">{{ `${study.Modalities} (${study.SeriesCount})` }}</span>
<span v-if="study.StudyName" :title="study.StudyName" style="margin-left: 5px;">{{ study.StudyName
}}</span>
<span v-else :title="study.Modalities" style="margin-left: 5px;">{{ `${study.Modalities}
(${study.SeriesCount})` }}</span>
</div>
<div v-if="study.StudyName" style="text-overflow: ellipsis;overflow: hidden;">
<span :title="study.Modalities">{{ `${study.Modalities} (${study.SeriesCount})` }}</span>
@@ -46,53 +39,38 @@
</div>
</template>
<div class="dicom-list-container">
<div
v-for="(series, i) in study.SeriesList"
:key="series.Id"
style="position:relative;margin-top:1px;"
@click="activeSeries(series, i, index)"
>
<div
:class="{'series-active': index === activeStudyIndex && i === activeSeriesIndex}"
class="series-wrapper"
>
<div v-for="(series, i) in study.SeriesList" :key="series.Id" style="position:relative;margin-top:1px;"
@click="activeSeries(series, i, index)">
<div :class="{ 'series-active': index === activeStudyIndex && i === activeSeriesIndex }"
class="series-wrapper">
<div class="series-image">
<el-image
style="width: 100%;height: 100%;"
<el-image style="width: 100%;height: 100%;"
:src="`${OSSclientConfig.basePath}${series.ImageResizePath || series.NoneDicomFileFirstFile}`"
fit="fill"
crossorigin="anonymous"
/>
fit="fill" crossorigin="anonymous" />
</div>
<div class="series-text">
<div v-if="series.IsExistMutiFrames && series.InstanceCount > 1"
style="position: absolute;right: 0;top: 0;">
<el-popover
placement="right"
trigger="hover"
popper-class="instance_frame_wrapper"
>
<div v-if="series.IsExistMutiFrames && series.InstanceCount > 1"
style="position: absolute;right: 0;top: 0;">
<el-popover placement="right" trigger="hover" popper-class="instance_frame_wrapper">
<div class="frame_list">
<div
v-for="(instance, idx) in series.InstanceInfoList"
:key="instance.Id"
class="frame_content"
:style="{'margin-bottom':idx<series.InstanceInfoList.length-1? '5px':'0px'}"
@click.stop="showMultiFrames(index,series, i, instance)"
>
<div v-for="(instance, idx) in series.InstanceInfoList" :key="instance.Id" class="frame_content"
:style="{ 'margin-bottom': idx < series.InstanceInfoList.length - 1 ? '5px' : '0px' }"
@click.stop="showMultiFrames(index, series, i, instance)">
<div>
<div>{{ instance.InstanceNumber }}</div>
<div>{{ `${instance.NumberOfFrames > 0 ? instance.KeyFramesList.length > 0 ? instance.KeyFramesList.length : instance.NumberOfFrames : 1} frame` }}</div>
<div>{{ `${instance.NumberOfFrames > 0 ? instance.KeyFramesList.length > 0 ?
instance.KeyFramesList.length : instance.NumberOfFrames : 1} frame` }}</div>
</div>
</div>
</div>
<i slot="reference" class="el-icon-connection" style="font-size: 15px;cursor: pointer;color: #ffeb3b;" />
<i slot="reference" class="el-icon-connection"
style="font-size: 15px;cursor: pointer;color: #ffeb3b;" />
</el-popover>
</div>
<div v-if="!study.IsCriticalSequence" class="text-desc" :title="series.SeriesNumber">
#{{ series.SeriesNumber }}
</div>
<div v-if="series.Description" class="text-desc" :title="series.Description">
{{ series.Description }}
@@ -104,17 +82,19 @@
<span v-show="series.LoadedImageCount < series.InstanceCount">
{{ series.Modality }}: {{ series.LoadedImageCount }}/{{ series.InstanceCount }} image
</span>
<span v-show="series.LoadedImageCount >= series.InstanceCount">{{ series.Modality }}: {{ series.InstanceCount }} image</span>
<span v-show="series.LoadedImageCount >= series.InstanceCount">{{ series.Modality }}: {{
series.InstanceCount
}} image</span>
</div>
<div style="line-height: 12px;">
<i v-show="series.IsBeMark || markedSeriesIds.includes(series.Id)" class="el-icon-star-on" style="font-size: 12px;color: #ff5722;" />
<i v-show="series.IsBeMark || markedSeriesIds.includes(series.Id)" class="el-icon-star-on"
style="font-size: 12px;color: #ff5722;" />
</div>
</div>
</div>
<div v-if="series.LoadedImageCount > 0 && series.LoadedImageCount < series.InstanceCount" style="width: 100%;">
<el-progress
:percentage="parseInt((series.LoadedImageProgress / series.InstanceCount).toFixed(2))"
/>
<div v-if="series.LoadedImageCount > 0 && series.LoadedImageCount < series.InstanceCount"
style="width: 100%;">
<el-progress :percentage="parseInt((series.LoadedImageProgress / series.InstanceCount).toFixed(2))" />
</div>
</div>
</div>
@@ -124,6 +104,7 @@
</div>
</template>
<script>
import DicomEvent from '@/views/trials/trials-panel/reading/dicoms/components/DicomEvent'
export default {
name: 'StudyList',
props: {
@@ -168,6 +149,7 @@ export default {
this.activeStudyIndex = studyIndex
this.activeSeriesIndex = seriesIndex
this.$emit('activeSeries', series)
DicomEvent.$emit('activeSeries', series)
},
activeStudy(id) {
if (this.activeNames.indexOf(id) > -1) return
@@ -179,6 +161,7 @@ export default {
const studyId = this.studyList[studyIndex].StudyId
if (!studyId) return
this.activeStudy(studyId)
DicomEvent.$emit('activeSeries', this.studyList[studyIndex].SeriesList[this.activeSeriesIndex])
},
showMultiFrames(studyIndex, series, seriesIndex, instance) {
let obj = Object.assign({}, series)
@@ -203,7 +186,7 @@ export default {
obj.ImageIds = imageIds
obj.SliceIndex = 0
this.$emit('showMultiFrame', obj)
},
getPreviousOrNextSeries(type, series) {
const seriseList = this.studyList.map(s => s.SeriesList).flat()
@@ -224,13 +207,14 @@ export default {
}
</script>
<style lang="scss" scoped>
.study-wrapper{
width:100%;
.study-wrapper {
width: 100%;
height: 100%;
overflow-y: hidden;
overflow-x: hidden;
display: flex;
flex-direction: column;
.study-info {
font-size: 16px;
font-weight: bold;
@@ -241,7 +225,8 @@ export default {
background-color: #4c4c4c;
height: 50px;
}
.dicom-desc{
.dicom-desc {
font-weight: bold;
font-size: 13px;
text-align: left;
@@ -258,19 +243,22 @@ export default {
touch-action: auto;
overflow-y: auto;
}
.series-active {
background-color: #607d8b!important;
border: 1px solid #607d8b!important;
background-color: #607d8b !important;
border: 1px solid #607d8b !important;
}
::v-deep.el-progress__text{
::v-deep .el-progress__text{
color: #ccc;
font-size: 12px;
}
.dicom-list-container{
.dicom-list-container {
width: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
.series-wrapper {
width: 100%;
padding: 5px;
@@ -279,21 +267,26 @@ export default {
align-items: center;
cursor: pointer;
background-color: #3a3a3a;
.el-progress__text{
.el-progress__text {
display: none;
}
.el-progress-bar{
padding-right:0px;
.el-progress-bar {
padding-right: 0px;
}
.series-image {
width: 50px;
height: 50px;
}
.series-text {
flex: 1;
padding-left: 5px;
color: #ddd;
position: relative;
.text-desc {
width: 100px;
white-space: nowrap;
@@ -306,27 +299,31 @@ export default {
}
}
::v-deep.el-collapse{
::v-deep .el-collapse{
border: none;
.el-collapse-item{
background-color: #000!important;
.el-collapse-item {
background-color: #000 !important;
color: #ddd;
}
.el-collapse-item__content{
padding-bottom:0px;
background-color: #000!important;
.el-collapse-item__content {
padding-bottom: 0px;
background-color: #000 !important;
}
.el-collapse-item__header{
background-color: #000!important;
.el-collapse-item__header {
background-color: #000 !important;
color: #ddd;
border-bottom-color:#5a5a5a;
border-bottom-color: #5a5a5a;
padding-left: 5px;
// height: 50px;
line-height: 20px;
}
}
::v-deep .el-progress-bar__inner{
::v-deep .el-progress-bar__inner {
transition: width 0s ease;
}
}
@@ -68,6 +68,8 @@
<script>
import {
metaData,
volumeLoader,
setVolumesForViewports,
getRenderingEngine,
utilities as csUtils,
cache
@@ -77,8 +79,9 @@ import { createImageIdsAndCacheMetaData } from '@/views/trials/trials-panel/read
import setCtTransferFunctionForVolumeActor from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/setCtTransferFunctionForVolumeActor'
import { setPetColorMapTransferFunctionForVolumeActor } from '@/views/trials/trials-panel/reading/dicoms/components/Fusion/js/setPetColorMapTransferFunctionForVolumeActor'
import { vec3, mat4 } from 'gl-matrix'
import DicomEvent from '@/views/trials/trials-panel/reading/dicoms/components/DicomEvent'
export default {
name: 'ImageViewport',
name: 'MPRViewport',
props: {
renderingEngineId: {
type: String,
@@ -92,12 +95,6 @@ export default {
type: Number,
required: true
},
MPRInfo: {
type: Object,
default: () => {
return {}
}
}
},
data() {
return {
@@ -125,7 +122,9 @@ export default {
sliceThickness: null,
wwwc: null,
total: 0,
sliceThickness: 0
sliceThickness: 0,
imageOrientationPatient: [],
imagePositionPatient: []
},
digitPlaces: 2,
orientationMarkers: [],
@@ -189,10 +188,10 @@ export default {
const totalImages = this.imageInfo.total;
//
if (currentImageIdIndex >= totalImages - 1) {
if (currentImageIdIndex >= totalImages - 1 && e.wheelDeltaY < 0) {
//
csUtils.jumpToSlice(viewport.element, { imageIndex: 0 });
} else if (currentImageIdIndex <= 0) {
} else if (currentImageIdIndex <= 0 && e.wheelDeltaY > 0) {
//
csUtils.jumpToSlice(viewport.element, { imageIndex: totalImages - 1 });
}
@@ -271,20 +270,21 @@ export default {
const viewport = renderingEngine.getViewport(this.viewportId)
const zoom = viewport.getZoom()
this.imageInfo.zoom = zoom.toFixed(4)
let imageIds = viewport.getImageIds(this.volumeId)
let imageId = imageIds[0]
let volume = cache.getVolume(this.volumeId)
let { spacing } = volume
// if (this.series.orientation === 'AXIAL') imageId = viewport.getCurrentImageId()
let imageId = viewport.getCurrentImageId()
if (imageId) {
this.$emit('setMPRInfo', { type: this.series.orientation, key: "imageNum", value: detail.numberOfSlices })
const imagePlaneModule = metaData.get('imagePlaneModule', imageId)
let type = this.determineImagePlane(imagePlaneModule.imageOrientationPatient)
this.imageInfo.location = type === this.series.orientation ? imagePlaneModule.sliceLocation : ''
this.imageInfo.sliceThickness = type === this.series.orientation ? spacing[2] : spacing[0]
this.imageInfo.imageOrientationPatient = imagePlaneModule.imageOrientationPatient
this.imageInfo.imagePositionPatient = imagePlaneModule.imagePositionPatient
this.imageInfo.size = `${imagePlaneModule.columns}*${imagePlaneModule.rows}`
this.imageInfo.location = imagePlaneModule.sliceLocation
this.imageInfo.total = detail.numberOfSlices
this.getOrientationMarker()
let properties = viewport.getProperties(this.volumeId)
this.$emit('renderAnnotations', this.series)
let properties = viewport.getProperties()
if (this.isFusion) {
properties = viewport.getProperties(this.ptVolumeId)
}
if (properties && properties.voiRange) {
var { lower, upper } = properties.voiRange
const windowWidth = upper - lower
@@ -293,11 +293,10 @@ export default {
this.defaultWindowLevel.windowCenter = windowCenter
this.imageInfo.wwwc = `${Math.round(windowWidth)}/${Math.round(windowCenter)}`
}
const toolGroupId = this.viewportId
const toolGroup = cornerstoneTools.ToolGroupManager.getToolGroup(toolGroupId)
toolGroup.setToolEnabled('ScaleOverlay')
}
// const toolGroupId = this.viewportId
const toolGroupId = 'share-viewport-volume'
const toolGroup = cornerstoneTools.ToolGroupManager.getToolGroup(toolGroupId)
toolGroup.setToolEnabled('ScaleOverlay')
},
setFullScreen(index) {
@@ -468,22 +467,38 @@ export default {
})
this.loading = false
},
async getVolume(serie, isFusion = false) {
return new Promise(async res => {
let volumeId = `${isFusion ? 'fusion_' : ''}` + serie.SeriesInstanceUid;
let volume = null;
if (cache.getVolume(volumeId)) {
volume = cache.getVolume(volumeId)
} else {
await this.createImageIdsAndCacheMetaData(serie)
volume = await volumeLoader.createAndCacheVolume(volumeId, { imageIds: serie.ImageIds })
volume.load()
}
res({ volumeId, volume })
})
},
async setSeriesInfo(obj, isLocate = false) {
try {
let data = obj
if (this.series && data.Id === this.series.Id && data.Description === this.series.Description && !isLocate && !data.isLocation) {
data.SliceIndex = this.series.SliceIndex
}
// console.log(data.SliceIndex)
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine.getViewport(this.viewportId)
if (isLocate) return csUtils.jumpToSlice(viewport.element, { imageIndex: data.SliceIndex });
this.series = {}
this.volumeId = data.SeriesInstanceUid
let res = await this.getVolume(obj)
this.volumeId = res.volumeId
this.series = { ...data }
viewport
.setVolumes([{
volumeId: this.volumeId, callback: (r) => {
if (this.series.Modality === 'PT') {
if (this.series.Modality === 'PT' || this.series.Modality === 'NM') {
setPetColorMapTransferFunctionForVolumeActor(r, true)
} else {
setCtTransferFunctionForVolumeActor(r)
@@ -491,9 +506,13 @@ export default {
console.log("渲染成功")
}
}]).then(res => {
if (data.segment) {
return DicomEvent.$emit("isloaded", { segment: data.segment })
}
if (data.isLocation) {
setTimeout(() => { csUtils.jumpToSlice(viewport.element, { imageIndex: data.SliceIndex }); })
}
DicomEvent.$emit("isloaded", {})
})
viewport.render()
} catch (e) {
@@ -30,8 +30,8 @@
</div>
</div>
</div>
<div style="margin-left:-1px;border: 1px solid #424242;">
<el-input v-model="range" size="mini" style="width:120px" maxlength="3"
<div v-if="modality !== 'NM'" style="margin-left:-1px;border: 1px solid #424242;">
<el-input v-model="range" size="mini" style="width:120px" :maxlength="maxLength"
oninput="if(value){value=value.replace(/[^\d]/g,'')} if(value<=0){value=''}"
@change="upperRangeChange">
<template slot="append">g/ml</template>
@@ -54,12 +54,23 @@ import vtkColorMaps from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/C
const { registerColormap, getColormapNames, getColormap } = csUtils.colormap
export default {
name: "colorMap",
props: {
modality: {
type: String,
default: ''
},
maxLength: {
type: [Number, String],
default: 6
}
},
data() {
return {
colorMaps: [],
rgbPresetName: 'siemens',
range: 40,
upper: 6,
isSlideMoving: false,
}
},
mounted() {
@@ -72,9 +83,24 @@ export default {
this.renderColorMaps()
this.upperRangeChange(this.range)
this.initSlider()
this.syncSliderPosition()
})
},
methods: {
syncSliderPosition(retry = 0) {
var sliderBox = document.getElementById('sliderBox')
var container = document.getElementById('colorBarCanvas')
if (!sliderBox || !container) return
var maxLeft = container.clientWidth - sliderBox.clientWidth
if (maxLeft <= 0) {
if (retry < 30) {
requestAnimationFrame(() => this.syncSliderPosition(retry + 1))
}
return
}
this.updateSliderPosition()
},
renderColorMaps() {
this.createColorBar(this.rgbPresetName, 'colorBarCanvas', 256, 15)
this.colorMaps.forEach((e, index) => {
@@ -84,10 +110,28 @@ export default {
voiChange(v) {
this.$emit('voiChange', v)
},
changeVoi(v) {
//
if (v === this.upper) return
var range = Number(this.range) || 0
var upper = Number(v) || 0
if (range <= 0) {
upper = 0
} else if (upper > range) {
upper = range
} else if (upper < 0) {
upper = 0
}
this.upper = upper
this.syncSliderPosition()
},
initSlider() {
var slider = document.getElementById('slider')
var sliderBox = document.getElementById('sliderBox')
var container = document.getElementById('colorBarCanvas')
if (!slider || !sliderBox || !container) return
slider.addEventListener('mousedown', () => {
this.isSlideMoving = true
})
@@ -96,6 +140,8 @@ export default {
var containerWidth = container.clientWidth
var sliderWidth = sliderBox.clientWidth
var maxLeft = containerWidth - sliderWidth
if (maxLeft <= 0) return
var left = e.clientX - container.getBoundingClientRect().left
var position = null
position = left
@@ -109,9 +155,16 @@ export default {
slider.style.left = left + 'px'
var positionValue = document.getElementById('slider-position')
var upper = this.range
var upper = Number(this.range) || 0
position = parseInt((position / maxLeft) * upper)
positionValue.textContent = position
if (position > upper) position = upper
if (position < 0) position = 0
if (this.modality === 'NM') {
positionValue.textContent = upper > 0 ? Math.round((position / upper) * 100) + '%' : '0%'
} else {
positionValue.textContent = position
}
this.upper = position
this.voiChange(position)
}
@@ -120,25 +173,63 @@ export default {
this.isSlideMoving = false
})
},
upperRangeChange(v) {
if (v === 0 || v < this.upper) {
return
}
updateSliderPosition() {
var sliderBox = document.getElementById('sliderBox')
var container = document.getElementById('colorBarCanvas')
if (!sliderBox || !container) return
var containerWidth = container.clientWidth
var sliderWidth = sliderBox.clientWidth
var maxLeft = containerWidth - sliderWidth
var left = (this.upper / this.range) * maxLeft
if (maxLeft <= 0) return
var range = Number(this.range) || 0
var upper = Number(this.upper) || 0
if (range <= 0) {
upper = 0
} else if (upper > range) {
upper = range
} else if (upper < 0) {
upper = 0
}
if (upper !== this.upper) {
this.upper = upper
}
var left = range > 0 ? (upper / range) * maxLeft : 0
if (left < 0) {
left = 6
left = 0
} else if (left >= maxLeft) {
left = maxLeft + 6
left = maxLeft
}
var slider = document.getElementById('slider')
slider.style.left = left + 'px'
if (slider) {
slider.style.left = (left + 6) + 'px'
}
var positionValue = document.getElementById('slider-position')
positionValue.textContent = this.upper
if (positionValue) {
if (this.modality === 'NM') {
positionValue.textContent = range > 0 ? Math.round((upper / range) * 100) + '%' : '0%'
} else {
positionValue.textContent = upper
}
}
},
upperRangeChange(v) {
if (this.upper === v) return
var nextRange = Number(v) || 0
if (nextRange <= 0) {
this.upper = 0
this.updateSliderPosition()
this.voiChange(0)
return
}
this.range = nextRange
if (this.upper > nextRange) {
this.upper = nextRange
this.voiChange(this.upper)
}
this.syncSliderPosition()
},
createColorBar(rgbPresetName, elId, width, height) {
var colorMap = null
@@ -185,6 +276,7 @@ export default {
init() {
this.createColorBar(this.rgbPresetName, 'colorBarCanvas', 256, 15)
this.$emit("setColorMap", this.rgbPresetName)
this.syncSliderPosition()
}
}
}
@@ -322,4 +414,4 @@ export default {
color: #ddd;
}
}
</style>
</style>
@@ -59,6 +59,7 @@
:parentQsId="parentQsId" :isBaseline="isBaseline" :reading-task-state="readingTaskState"
:question-form="QuestionsForm" :visit-task-id="visitTaskId" :criterion-id="criterionId"
:type="addOrEdit.type" :calculationList="calculationList" :questionsMarkStatus="questionsMarkStatus"
:questionsSegmentMarkStatus="questionsSegmentMarkStatus"
@formItemTableNumberChange="formItemTableNumberChange" @resetFormItemData="resetTableFormItemData"
@setFormItemData="setFormTableItemData" @operateImageMarker="operateImageMarker" @save="save"
@handleReadingChart="handleReadingChart" />
@@ -183,7 +184,49 @@
<el-input v-if="question.Type === 'increment'" v-model="questionForm[question.Id]" disabled />
<!-- 数值 -->
<template v-if="question.Type === 'number' && (question.ImageMarkEnum === 1 || question.ImageMarkEnum === 2)">
<div style="display: flex;flex-direction: row;justify-content: flex-start;align-items: center;">
<div style="display: flex;flex-direction: row;justify-content: flex-start;align-items: center;"
v-if="question.ImageMarkTypeEnum === 1">
<el-input type="text" @change="(val) => { formItemNumberChange(val, question) }"
@input="numberInput(question.Id)"
@blur="questionsSegmentMarkStatus[question.Id] ? () => { } : handleMarkedQsBlur(questionForm[question.Id], questionForm, question.Id, question)"
v-model="questionForm[question.Id]"
:title="questionsSegmentMarkStatus[question.Id] ? `${questionsSegmentMarkStatus[question.Id].SegmentationName}_${questionsSegmentMarkStatus[question.Id].SegmentName}` : question.Remark"
:disabled="(questionsSegmentMarkStatus[question.Id] && question.ImageMarkEnum === 2) || question.ImageMarkEnum === 1 || readingTaskState === 2"
style="width: 150px;margin-right: 5px;">
<template v-if="question.Unit !== 0" slot="append">
{{ question.Unit !== 4 ? $fd('ValueUnit', question.Unit) : question.CustomUnit }}
</template>
</el-input>
<svg-icon v-if="question.ShowChartTypeEnum > 0 && taskInfo.IsReadingTaskViewInOrder === 1"
icon-class="readingChart" class="svg-icon svg-readingChart" @click.stop="(e) => handleReadingChart({
e,
data: {
QuestionId: question.Id,
QuestionName: question.QuestionName
}
})" />
<!-- 绑定 -->
<el-button v-if="readingTaskState < 2 && (!questionsSegmentMarkStatus[question.Id])" size="mini" type="text"
@click="operateImageMarker({ operateStateEnum: 21, question })">
{{ $t('dicom3D:CustomizeQuestionFormItem:button:bind') }}
</el-button>
<!-- 更改 -->
<el-button v-if="readingTaskState < 2 && (questionsSegmentMarkStatus[question.Id])" size="mini" type="text"
@click="operateImageMarker({ operateStateEnum: 22, question })">
{{ $t('dicom3D:CustomizeQuestionFormItem:button:edit') }}
</el-button>
<!-- 查看 -->
<el-button v-if="questionsSegmentMarkStatus[question.Id]" size="mini" type="text"
@click="operateImageMarker({ operateStateEnum: 23, question })">
{{ $t('dicom3D:CustomizeQuestionFormItem:button:view') }}
</el-button>
<!-- 移除 -->
<el-button v-if="readingTaskState < 2 && (questionsSegmentMarkStatus[question.Id])" size="mini" type="text"
@click="operateImageMarker({ operateStateEnum: 24, question })">
{{ $t('dicom3D:CustomizeQuestionFormItem:button:remove') }}
</el-button>
</div>
<div style="display: flex;flex-direction: row;justify-content: flex-start;align-items: center;" v-else>
<el-input type="text" @change="(val) => { formItemNumberChange(val, question) }"
@input="numberInput(question.Id)"
@blur="questionsMarkStatus[question.Id] && questionsMarkStatus[question.Id].isMarked ? () => { } : handleMarkedQsBlur(questionForm[question.Id], questionForm, question.Id, question)"
@@ -312,9 +355,10 @@
:isNoneDicom="isNoneDicom" :reading-task-state="readingTaskState" :question-form="questionForm"
:visit-task-id="visitTaskId" :criterion-id="criterionId" :calculationList="calculationList"
:questionMarkInfoList="questionMarkInfoList" :questionsMarkStatus="questionsMarkStatus"
@formItemNumberChange="formItemNumberChange" @setFormItemData="setFormItemData"
@resetFormItemData="resetFormItemData" @getQuestions="getQuestions" @operateImageMarker="operateImageMarker"
@unBindAnnotationToQuestion="unBindAnnotationToQuestion" @handleReadingChart="handleReadingChart" />
:questionsSegmentMarkStatus="questionsSegmentMarkStatus" @formItemNumberChange="formItemNumberChange"
@setFormItemData="setFormItemData" @resetFormItemData="resetFormItemData" @getQuestions="getQuestions"
@operateImageMarker="operateImageMarker" @unBindAnnotationToQuestion="unBindAnnotationToQuestion"
@handleReadingChart="handleReadingChart" @saveSegmentBindingAndAnswer="saveSegmentBindingAndAnswer" />
</template>
<!-- <base-model :config="addOrEdit"
@@ -397,6 +441,12 @@ export default {
return {}
}
},
questionsSegmentMarkStatus: {
type: Object,
default() {
return {}
}
},
isNoneDicom: {
type: Boolean,
default: false
@@ -556,8 +606,65 @@ export default {
this.operateImageMarker({ operateStateEnum, question: this.question, picturePath })
}
})
DicomEvent.$on('setTableQuestionAnswer', async (DATA) => {
let { id, answer, ParentQsId, data } = DATA
// console.log(ParentQsId, this.question.Id)
if (this.question.Id === ParentQsId) {
this.QuestionsForm[id] = answer
if (data.RowId) {
let i = this.AnswersList.findIndex(i => i.RowId === this.QuestionsForm.RowId)
this.AnswersList[i][id] = this.QuestionsForm[id]
this.$emit('setFormItemData', { key: this.question.Id, val: this.AnswersList, question: this.question })
this.formItemNumberChange(this.question.Id, true)
this.$emit('saveSegmentBindingAndAnswer', [data])
} else {
try {
// loading = this.$loading({ fullscreen: true })
let answers = []
let reg = new RegExp(/^[0-9a-zA-Z]{8}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{12}$/)
for (const k in this.QuestionsForm) {
if (reg.test(k)) {
if (answers.findIndex(i => i.tableQuestionId === k) === -1) {
answers.push({ tableQuestionId: k, answer: this.QuestionsForm[k] })
}
}
}
this.QuestionsList.forEach(k => {
if (reg.test(k.Id)) {
if (answers.findIndex(i => i.tableQuestionId === k.Id) === -1) {
answers.push({ tableQuestionId: k.Id, answer: '' })
}
}
})
let params = {
questionId: this.question.Id,
RowIndex: this.questionForm[this.question.Id].length + 1,
RowId: this.QuestionsForm.RowId ? this.QuestionsForm.RowId : '',
visitTaskId: this.visitTaskId,
trialId: this.$route.query.trialId,
answerList: answers
}
let res = await submitTableQuestion(params)
if (res.IsSuccess) {
this.QuestionsForm.RowId = res.Result.RowId
obj.rowId = res.Result.RowId
data.RowId = res.Result.RowId
this.AnswersList.push(this.QuestionsForm)
this.$emit('setFormItemData', { key: this.question.Id, val: this.AnswersList, question: this.question })
this.formItemNumberChange(this.question.Id, true)
this.$emit('saveSegmentBindingAndAnswer', [data])
}
} catch (e) {
console.log(e)
}
}
}
})
},
methods: {
saveSegmentBindingAndAnswer(list) {
this.$emit('saveSegmentBindingAndAnswer', list)
},
handleReadingChart(row) {
this.$emit('handleReadingChart', row)
},
@@ -1349,13 +1456,13 @@ export default {
getAnnotationProp(annotation, prop) {
if (!annotation) return
let referencedImageId = null
if (annotation.from) {
referencedImageId = `${annotation?.metadata?.volumeId}?sliceIndex=${annotation?.metadata?.sliceIndex}&viewPlaneNormal=${annotation?.metadata?.viewPlaneNormal.map(i => i == 0 ? 0 : i).join(',')}`
if (annotation.from || annotation.metadata.volumeId) {
referencedImageId = `${annotation?.metadata?.volumeId}?sliceIndex=${annotation?.metadata?.sliceIndex}&viewPlaneNormal=${annotation?.metadata?.viewPlaneNormal.map(i => i == 0 ? Number(0).toFixed(3) : Number(i).toFixed(3)).join(',')}`
} else {
referencedImageId = annotation?.metadata?.referencedImageId
}
if (!referencedImageId) return null
const cacheKey = annotation.from ? `volumeId:${referencedImageId}` : `imageId:${referencedImageId}`
const cacheKey = annotation.from || annotation.metadata.volumeId ? `volumeId:${referencedImageId}` : `imageId:${referencedImageId}`
const points = ['x', 'y', 'z'];
const cachedStats = annotation.markTool === "ArrowAnnotate" ? annotation.data?.handles?.points[0] : annotation.data?.cachedStats?.[cacheKey]
const hasProp = cachedStats
@@ -19,9 +19,10 @@
:isNoneDicom="isNoneDicom" :question="question" :question-form="questionForm"
:reading-task-state="readingTaskState" :criterion-id="criterionId" :calculation-list="calculationList"
:question-mark-info-list="questionMarkInfoList" :questions-mark-status="questionsMarkStatus"
:is-baseline="isBaseLineTask" @resetFormItemData="resetFormItemData" @setFormItemData="setFormItemData"
@getQuestions="getQuestions" @operateImageMarker="operateImageMarker"
@unBindAnnotationToQuestion="unBindAnnotationToQuestion" @handleReadingChart="handleReadingChart" />
:questionsSegmentMarkStatus="questionsSegmentMarkStatus" :is-baseline="isBaseLineTask"
@resetFormItemData="resetFormItemData" @setFormItemData="setFormItemData" @getQuestions="getQuestions"
@operateImageMarker="operateImageMarker" @unBindAnnotationToQuestion="unBindAnnotationToQuestion"
@handleReadingChart="handleReadingChart" @saveSegmentBindingAndAnswer="saveSegmentBindingAndAnswer" />
</template>
<el-form-item v-if="readingTaskState < 2">
@@ -58,7 +59,7 @@
<script>
import { getCustomTableQuestionAnswer, changeDicomReadingQuestionAnswer, submitVisitTaskQuestionsInDto, verifyVisitTaskQuestions, getQuestionCalculateRelation, saveTaskQuestion } from '@/api/trials'
import { setSkipReadingCache, resetReadingTask, saveTableQuestionMark, saveAnswerAndBindingNoneDicomMark, changePlottingScaleChangeAnswer } from '@/api/reading'
import { setSkipReadingCache, resetReadingTask, saveTableQuestionMark, saveAnswerAndBindingNoneDicomMark, changePlottingScaleChangeAnswer, getSegmentBindingList, saveSegmentBindingAndAnswer, getSegmentList, getSegmentationList } from '@/api/reading'
import const_ from '@/const/sign-code'
import QuestionFormItem from './QuestionFormItem'
import SignForm from '@/views/trials/components/newSignForm'
@@ -116,6 +117,7 @@ export default {
imageTool: '',
imageToolAttribute: '',
questionsMarkStatus: {},
questionsSegmentMarkStatus: {},
digitPlaces: 2,
questionImageToolAttributeInfo: {},
unSaveTargets: [],
@@ -133,6 +135,7 @@ export default {
this.digitPlaces = digitPlaces === -1 ? this.digitPlaces : digitPlaces
this.getQuestionCalculateRelation()
this.getQuestions(true)
this.initSegmentBinding()
DicomEvent.$on('opentableCol', (data) => {
let { visible } = data
this.isTableVisible = visible
@@ -433,8 +436,13 @@ export default {
REMOVE: 4, //
SAVE_OUTER: 5, //
UPDATE: 6, //
SAVE_TABLE: 7 //
SAVE_TABLE: 7, //
BIND_SEGMENT: 21, //
CHANGE_SEGMENT: 22, //
VIEW_SEGMENT: 23, //
REMOVE_SEGMENT: 24, //
}
// console.log(obj, 'obj')
const { question, operateStateEnum, rowId, answer } = obj
const { Id, IsTableQuestion, ImageTool, ImageToolAttribute, ParentQsId, RowId, QuestionName, QuestionEnName } = question
@@ -446,7 +454,7 @@ export default {
operateQuestionId: Id,
operateQuestionName: this.isEN ? QuestionEnName : QuestionName,
operateRowId: RowId,
operateParentQsId: ParentQsId
operateParentQsId: ParentQsId,
})
const stateHandlers = {
[STATE.BIND]: () => this.$emit('setReadingToolPassive'),
@@ -456,12 +464,182 @@ export default {
[STATE.REMOVE]: this.handleRemoveAnnotation,
[STATE.SAVE_OUTER]: this.isNoneDicom ? this.handleSaveNoneDicomOuterQuestions : this.handleSaveOuterQuestions,
[STATE.UPDATE]: this.handleUpdateValue,
[STATE.SAVE_TABLE]: this.isNoneDicom ? this.handleSaveNoneDicomTableQuestions : this.handleSaveTableQuestions
[STATE.SAVE_TABLE]: this.isNoneDicom ? this.handleSaveNoneDicomTableQuestions : this.handleSaveTableQuestions,
[STATE.BIND_SEGMENT]: this.handleBindSegment,
[STATE.CHANGE_SEGMENT]: this.handleBindSegment,
[STATE.VIEW_SEGMENT]: this.handleViewSegment,
[STATE.REMOVE_SEGMENT]: this.handleRemoveSegment,
}
const handler = stateHandlers[operateStateEnum]
handler && await handler.call(this, obj)
},
async handleViewSegment(obj) {
try {
let o = {}
if (this.isTableQuestion) {
o.TableQuestionId = this.operateQuestionId
o.RowId = this.operateRowId
} else {
o.QuestionId = this.operateQuestionId
}
let list = await this.getSegmentBindingList(o)
if (list && list.length > 0) {
let segment = await this.getSegmentList(list[0].SegmentId)
if (segment[0].SegmentJson) {
let obj = JSON.parse(segment[0].SegmentJson)
segment[0].stats = obj.stats
segment[0].bidirectional = obj.bidirectional
segment[0].segmentationId = segment[0].SegmentationId
segment[0].segmentIndex = segment[0].SegmentMumber
}
let segmentGroup = await this.getSegmentationList(list[0].SegmentationId)
this.$emit('viewCustomAnnotationSeries', {
visitTaskId: this.visitTaskId,
segment: segment[0],
segmentGroup: segmentGroup[0]
})
}
} catch (err) {
console.log(err)
}
},
handleBindSegment(obj) {
this.$emit("openSegmentForm", { visitInfo: this.visitInfo, operateStateEnum: obj.operateStateEnum, isTableQuestion: this.isTableQuestion, operateQuestionId: this.operateQuestionId, operateRowId: this.operateRowId })
},
handleSegmentSave(obj) {
let imageToolAttribute = this.imageToolAttribute
let answer = ''
if (imageToolAttribute === 'length' || imageToolAttribute === 'width') {
let s = {
length: "maxMajor",
width: 'maxMinor'
}
if (!obj.bidirectional || !obj.bidirectional.maxMajor) return this.$confirm(this.$t("segment:error:notValue"))
answer = obj.bidirectional[s[imageToolAttribute]] ? Number(obj.bidirectional[s[imageToolAttribute]]).toFixed(this.digitPlaces) : ''
} else {
if (!obj.stats) return this.$confirm(this.$t("segment:error:notValue"))
answer = obj.stats[imageToolAttribute] ? Number((obj.stats[imageToolAttribute]).value).toFixed(this.digitPlaces) : ''
}
let o = {
Answer: answer,
SegmentId: obj.Id,
SegmentationId: obj.SegmentationId,
QuestionId: this.isTableQuestion ? this.operateParentQsId : this.operateQuestionId,
RowId: this.isTableQuestion ? this.operateRowId : null,
TableQuestionId: this.isTableQuestion ? this.operateQuestionId : null,
VisitTaskId: this.visitTaskId,
}
if (this.isTableQuestion) {
return DicomEvent.$emit('setTableQuestionAnswer', { id: this.operateQuestionId, answer, ParentQsId: this.operateParentQsId, data: o })
} else {
this.$set(this.questionForm, this.operateQuestionId, answer)
}
this.saveSegmentBindingAndAnswer([o])
},
//
async handleRemoveSegment(obj) {
const { question } = obj
let confirm = await this.$confirm(this.$t('segment:confirm:sureDelete'))
if (!confirm) return false
this.$set(this.questionForm, question.Id, '')
let o = {
Answer: '',
SegmentId: null,
SegmentationId: null,
QuestionId: this.isTableQuestion ? this.operateParentQsId : this.operateQuestionId,
RowId: this.isTableQuestion ? this.operateRowId : null,
TableQuestionId: this.isTableQuestion ? this.operateQuestionId : null,
VisitTaskId: this.visitTaskId,
}
this.saveSegmentBindingAndAnswer([o])
},
//
async saveSegmentBindingAndAnswer(list) {
try {
let data = {
VisitTaskId: this.visitTaskId,
BindingList: list
}
let res = await saveSegmentBindingAndAnswer(data)
if (res.IsSuccess) {
this.initSegmentBinding()
}
} catch (err) {
console.log(err)
}
},
//
async initSegmentBinding() {
try {
let list = await this.getSegmentBindingList()
this.questionsSegmentMarkStatus = {}
list.forEach(item => {
if (item.TableQuestionId && item.RowId) {
this.$set(this.questionsSegmentMarkStatus, `${item.RowId}_${item.TableQuestionId}`, { SegmentId: item.SegmentId, SegmentationName: item.SegmentationName, SegmentName: item.SegmentName, })
} else {
this.$set(this.questionsSegmentMarkStatus, item.QuestionId, { SegmentId: item.SegmentId, SegmentationName: item.SegmentationName, SegmentName: item.SegmentName, })
}
})
} catch (err) {
console.log(err)
}
},
//
async getSegmentBindingList(param = {}) {
try {
let data = {
VisitTaskId: this.visitTaskId,
PageSize: 9999,
PageIndex: 1,
}
data = Object.assign(data, param)
let res = await getSegmentBindingList(data)
if (res.IsSuccess) {
return res.Result.CurrentPageData
}
} catch (err) {
console.log(err)
}
},
//
async getSegmentList(id) {
try {
let data = {
// SegmentationId: id,
PageSize: 9999,
PageIndex: 1,
}
if (id) data.Id = id
let res = await getSegmentList(data)
if (res.IsSuccess) {
return res.Result.CurrentPageData
}
} catch (err) {
console.log(err)
}
},
//
async getSegmentationList(id) {
try {
let data = {
VisitTaskId: this.visitInfo.VisitTaskId,
PageSize: 9999,
PageIndex: 1,
}
if (id) data.Id = id
let res = await getSegmentationList(data);
if (res.IsSuccess) {
return res.Result.CurrentPageData;
}
} catch (err) {
console.log(err)
}
},
async handleViewAnnotation(obj) {
const index = this.findMarkIndex(obj.question)
if (index === -1) return
@@ -1049,13 +1227,13 @@ export default {
getAnnotationProp(annotation, prop) {
if (!annotation || !prop) return
let referencedImageId = null
if (annotation.from) {
referencedImageId = `${annotation?.metadata?.volumeId}?sliceIndex=${annotation?.metadata?.sliceIndex}&viewPlaneNormal=${annotation?.metadata?.viewPlaneNormal.map(i => i == 0 ? 0 : i).join(',')}`
if (annotation.from || annotation.metadata.volumeId) {
referencedImageId = `${annotation?.metadata?.volumeId}?sliceIndex=${annotation?.metadata?.sliceIndex}&viewPlaneNormal=${annotation?.metadata?.viewPlaneNormal.map(i => i == 0 ? Number(0).toFixed(3) : Number(i).toFixed(3)).join(',')}`
} else {
referencedImageId = annotation?.metadata?.referencedImageId
}
if (!referencedImageId) return null
const cacheKey = annotation.from ? `volumeId:${referencedImageId}` : `imageId:${referencedImageId}`
const cacheKey = annotation.from || annotation.metadata.volumeId ? `volumeId:${referencedImageId}` : `imageId:${referencedImageId}`
const points = ['x', 'y', 'z']
const cachedStats = annotation.markTool === "ArrowAnnotate" ? annotation.data?.handles?.points[0] : annotation.data?.cachedStats?.[cacheKey]
const hasProp = cachedStats
@@ -107,7 +107,53 @@
<el-input v-if="question.Type === 'increment'" v-model="questionForm[question.Id]" disabled />
<!-- 数值 -->
<template v-if="question.Type === 'number' && (question.ImageMarkEnum === 1 || question.ImageMarkEnum === 2)">
<div style="display: flex;flex-direction: row;justify-content: flex-start;align-items: center;">
<div style="display: flex;flex-direction: row;justify-content: flex-start;align-items: center;"
v-if="question.ImageMarkTypeEnum === 1">
<el-input type="text" @change="(val) => { formItemNumberChange(val, question) }"
@input="numberInput(question.Id)"
@blur="questionsSegmentMarkStatus[rowId ? `${rowId}_${question.Id}` : question.Id] ? () => { } : handleMarkedQsBlur(questionForm[question.Id], questionForm, question.Id, question)"
v-model="questionForm[question.Id]"
:title="questionsSegmentMarkStatus[rowId ? `${rowId}_${question.Id}` : question.Id] ? `${questionsSegmentMarkStatus[rowId ? `${rowId}_${question.Id}` : question.Id].SegmentationName}_${questionsSegmentMarkStatus[rowId ? `${rowId}_${question.Id}` : question.Id].SegmentName}` : question.Remark"
:disabled="(questionsSegmentMarkStatus[rowId ? `${rowId}_${question.Id}` : question.Id] && question.ImageMarkEnum === 2) || question.ImageMarkEnum === 1 || question.IsPreinstall"
style="width: 150px;margin-right: 5px;">
<template v-if="question.Unit !== 0" slot="append">
{{ question.Unit !== 4 ? $fd('ValueUnit', question.Unit) : question.CustomUnit }}
</template>
</el-input>
<svg-icon v-if="question.ShowChartTypeEnum > 0 && taskInfo.IsReadingTaskViewInOrder === 1"
icon-class="readingChart" class="svg-icon svg-readingChart" @click.stop="(e) => handleReadingChart({
e,
data: {
TableQuestionId: question.Id,
RowIndex: questionForm.RowIndex,
QuestionName: question.QuestionName
}
})" />
<!-- 绑定 -->
<el-button
v-if="readingTaskState < 2 && (!questionsSegmentMarkStatus[rowId ? `${rowId}_${question.Id}` : question.Id])"
size="mini" type="text" @click="operateImageMarker({ operateStateEnum: 21, question })">
{{ $t('dicom3D:CustomizeQuestionFormItem:button:bind') }}
</el-button>
<!-- 更改 -->
<el-button
v-if="readingTaskState < 2 && (questionsSegmentMarkStatus[rowId ? `${rowId}_${question.Id}` : question.Id])"
size="mini" type="text" @click="operateImageMarker({ operateStateEnum: 22, question })">
{{ $t('dicom3D:CustomizeQuestionFormItem:button:edit') }}
</el-button>
<!-- 查看 -->
<el-button v-if="questionsSegmentMarkStatus[rowId ? `${rowId}_${question.Id}` : question.Id]" size="mini"
type="text" @click="operateImageMarker({ operateStateEnum: 23, question })">
{{ $t('dicom3D:CustomizeQuestionFormItem:button:view') }}
</el-button>
<!-- 移除 -->
<el-button
v-if="readingTaskState < 2 && (questionsSegmentMarkStatus[rowId ? `${rowId}_${question.Id}` : question.Id])"
size="mini" type="text" @click="operateImageMarker({ operateStateEnum: 24, question })">
{{ $t('dicom3D:CustomizeQuestionFormItem:button:remove') }}
</el-button>
</div>
<div style="display: flex;flex-direction: row;justify-content: flex-start;align-items: center;" v-else>
<el-input type="text" @change="(val) => { formItemNumberChange(val, question) }"
@input="numberInput(question.Id)"
@blur="questionsMarkStatus[question.Id] && questionsMarkStatus[question.Id].isMarked ? () => { } : handleMarkedQsBlur(questionForm[question.Id], questionForm, question.Id, question)"
@@ -276,6 +322,12 @@ export default {
return {}
}
},
questionsSegmentMarkStatus: {
type: Object,
default() {
return {}
}
},
parentQsId: {
type: String,
default: ''
@@ -0,0 +1,8 @@
import setPetTransferFunctionForVolumeActor from "./setPetTransferFunctionForVolumeActor";
import setMipTransferFunctionForVolumeActor from "./setMipTransferFunctionForVolumeActor";
export {
setPetTransferFunctionForVolumeActor,
setMipTransferFunctionForVolumeActor
};
@@ -0,0 +1,49 @@
import { cache, metaData, utilities } from "@cornerstonejs/core";
import vtkPiecewiseFunction from "@kitware/vtk.js/Common/DataModel/PiecewiseFunction";
export default function setMipTransferFunctionForVolumeActor({
volumeActor,
volumeId,
}) {
const mapper = volumeActor.getMapper?.();
if (mapper?.setSampleDistance) {
mapper.setSampleDistance(1.0);
}
const rgbTransferFunction = volumeActor
.getProperty()
.getRGBTransferFunction(0);
let range = null;
const imageVolume = volumeId ? cache.getVolume(volumeId) : null;
const imageId = imageVolume?.imageIds?.[0];
if (imageId) {
const voiLutModule = metaData.get("voiLutModule", imageId);
const rawCenter = Array.isArray(voiLutModule?.windowCenter)
? voiLutModule.windowCenter[0]
: voiLutModule?.windowCenter;
const rawWidth = Array.isArray(voiLutModule?.windowWidth)
? voiLutModule.windowWidth[0]
: voiLutModule?.windowWidth;
const center = Number(rawCenter);
const width = Number(rawWidth);
if (Number.isFinite(center) && Number.isFinite(width) && width > 0) {
const upper = center + width / 2;
range = [0, upper];
} else if (Number.isFinite(center)) {
range = [0, center];
}
}
if (!range) {
range = [0, 5];
}
rgbTransferFunction.setRange(range[0], range[1]);
utilities.invertRgbTransferFunction(rgbTransferFunction);
const upper = Number(range[1]);
if (Number.isFinite(upper) && upper > 0) {
const ofun = vtkPiecewiseFunction.newInstance();
ofun.addPoint(0, 0.0);
ofun.addPoint(upper * 0.02, 0.9);
ofun.addPoint(upper, 1.0);
volumeActor.getProperty().setScalarOpacity(0, ofun);
}
}
@@ -0,0 +1,11 @@
import { utilities } from '@cornerstonejs/core';
export default function setPetTransferFunction({ volumeActor }) {
const rgbTransferFunction = volumeActor
.getProperty()
.getRGBTransferFunction(0);
rgbTransferFunction.setRange(0, 5);
utilities.invertRgbTransferFunction(rgbTransferFunction);
}
@@ -2070,12 +2070,12 @@ export default {
.merge-table {
padding: 0 10px;
::v-deep.el-table {
::v-deep .el-table {
background-color: #1e1e1e !important;
color: #383838;
}
::v-deep.el-table td.el-table__cell,
::v-deep .el-table td.el-table__cell,
.el-table th.el-table__cell.is-leaf {
border-bottom: 1px solid #383838;
}
@@ -2086,7 +2086,7 @@ export default {
background-color: #1e1e1e;
}
::v-deep.el-table__header-wrapper {
::v-deep .el-table__header-wrapper {
th {
background-color: #1e1e1e !important;
color: #dfdfdf;
@@ -2094,7 +2094,7 @@ export default {
}
}
::v-deep.el-table__body-wrapper {
::v-deep .el-table__body-wrapper {
tr {
background-color: #1e1e1e !important;
color: #dfdfdf;
@@ -2105,7 +2105,7 @@ export default {
}
}
::v-deep.el-table__empty-block {
::v-deep .el-table__empty-block {
background-color: #1e1e1e !important;
}
@@ -386,6 +386,35 @@ const config = {
'disabledReason': ''
},
],
'customizeStandardsSegmentDicom': [
// {
// 'name': 'Contour分割',
// 'icon': 'contour',
// 'toolName': 'Contour',
// 'props': ['length'],
// 'i18nKey': 'trials:reading:button:Contour',
// 'isDisabled': false,
// 'disabledReason': ''
// },
{
'name': 'Labelmap分割',
'icon': 'labelmap',
'toolName': 'Labelmap',
'props': ['max', 'min', 'volume', 'count', 'mean', 'stdDev', 'length', 'width'],
'i18nKey': 'trials:reading:button:Labelmap',
'isDisabled': false,
'disabledReason': ''
},
// {
// 'name': 'Surface分割',
// 'icon': 'surface',
// 'toolName': 'Surface',
// 'props': ['area', 'mean', 'max', 'stdDev'],
// 'i18nKey': 'trials:reading:button:Surface',
// 'isDisabled': false,
// 'disabledReason': ''
// },
],
}
const getTools = (criterionType) => {
const standard = config.standards.find(s => s.type === criterionType)
@@ -399,4 +428,8 @@ const getCustomizeStandardsNoneDicomTools = (toolNames) => {
const filteredTools = config.customizeStandardsNoneDicom.filter(item => toolNames.includes(item.toolName))
return filteredTools || []
}
export { config, getTools, getCustomizeStandardsTools, getCustomizeStandardsNoneDicomTools }
const getCustomizeStandardsSegmentDicomTools = (toolNames) => {
const filteredTools = config.customizeStandardsSegmentDicom.filter(item => toolNames.includes(item.toolName))
return filteredTools || []
}
export { config, getTools, getCustomizeStandardsTools, getCustomizeStandardsNoneDicomTools, getCustomizeStandardsSegmentDicomTools }
@@ -0,0 +1,264 @@
// import { getEnabledElement, utilities as csUtils, getEnabledElementByViewportId, utilities, } from '@cornerstonejs/core';
// import { addAnnotation, getAllAnnotations, getAnnotations, removeAnnotation, } from '../../stateManagement/annotation/annotationState';
// import { isAnnotationLocked } from '../../stateManagement/annotation/annotationLocking';
// import { isAnnotationVisible } from '../../stateManagement/annotation/annotationVisibility';
// import { drawLine as drawLineSvg, drawHandles as drawHandlesSvg, } from '../../drawingSvg';
// import { getViewportIdsWithToolToRender } from '../../utilities/viewportFilters';
// import { hideElementCursor } from '../../cursors/elementCursor';
// import triggerAnnotationRenderForViewportIds from '../../utilities/triggerAnnotationRenderForViewportIds';
// import BidirectionalTool from '../annotation/BidirectionalTool';
// import { getSegmentIndexColor } from '../../stateManagement/segmentation/config/segmentationColor';
import { utilities, getEnabledElementByViewportId, getEnabledElement } from '@cornerstonejs/core';
import * as cornerstoneTools from '@cornerstonejs/tools'
const {
annotation,
drawing,
utilities: TSUtilities,
BidirectionalTool,
cursors,
segmentation
} = cornerstoneTools
const { addAnnotation, getAllAnnotations, getAnnotations, removeAnnotation } = annotation.state
const { isAnnotationLocked } = annotation.locking
const { isAnnotationVisible } = annotation.visibility
const { drawLine: drawLineSvg, drawHandles: drawHandlesSvg } = drawing
const { getViewportIdsWithToolToRender } = TSUtilities.viewportFilters
const { triggerAnnotationRenderForViewportIds } = TSUtilities
const { hideElementCursor } = cursors.elementCursor
const { getSegmentIndexColor } = segmentation.config.color
class SegmentBidirectionalTool extends BidirectionalTool {
static { this.toolName = 'SegmentBidirectional'; }
constructor(toolProps = {}) {
super(toolProps);
this.renderAnnotation = (enabledElement, svgDrawingHelper) => {
let renderStatus = true;
const { viewport } = enabledElement;
const { element } = viewport;
const viewportId = viewport.id;
let annotations = getAnnotations(this.getToolName(), element);
if (!annotations?.length) {
return renderStatus;
}
annotations = this.filterInteractableAnnotationsForElement(element, annotations);
if (!annotations?.length) {
return renderStatus;
}
const targetId = this.getTargetId(viewport);
const renderingEngine = viewport.getRenderingEngine();
const styleSpecifier = {
toolGroupId: this.toolGroupId,
toolName: this.getToolName(),
viewportId: enabledElement.viewport.id,
};
for (let i = 0; i < annotations.length; i++) {
const annotation = annotations[i];
const { annotationUID, data } = annotation;
const { points, activeHandleIndex } = data.handles;
const canvasCoordinates = points.map((p) => viewport.worldToCanvas(p));
styleSpecifier.annotationUID = annotationUID;
const { segmentIndex, segmentationId } = annotation.metadata;
const { lineWidth, lineDash, shadow } = this.getAnnotationStyle({
annotation,
styleSpecifier,
});
const colorArray = getSegmentIndexColor(viewportId, segmentationId, segmentIndex) || [255, 255, 255];
// console.log(viewportId, segmentationId, segmentIndex, 'colorArray')
// console.log(colorArray, 'colorArray')
const color = `rgb(${colorArray.slice(0, 3).join(',')})`;
if (!data.cachedStats[targetId] ||
data.cachedStats[targetId].unit == null) {
data.cachedStats[targetId] = {
length: null,
width: null,
unit: null,
};
this._calculateCachedStats(annotation, renderingEngine, enabledElement);
}
else if (annotation.invalidated) {
this._throttledCalculateCachedStats(annotation, renderingEngine, enabledElement);
}
if (!viewport.getRenderingEngine()) {
console.warn('Rendering Engine has been destroyed');
return renderStatus;
}
let activeHandleCanvasCoords;
if (!isAnnotationVisible(annotationUID)) {
continue;
}
if (!isAnnotationLocked(annotationUID) &&
!this.editData &&
activeHandleIndex !== null) {
activeHandleCanvasCoords = [canvasCoordinates[activeHandleIndex]];
}
if (activeHandleCanvasCoords) {
const handleGroupUID = '0';
drawHandlesSvg(svgDrawingHelper, annotationUID, handleGroupUID, activeHandleCanvasCoords, {
color,
});
}
const dataId1 = `${annotationUID}-line-1`;
const dataId2 = `${annotationUID}-line-2`;
const lineUID = '0';
drawLineSvg(svgDrawingHelper, annotationUID, lineUID, canvasCoordinates[0], canvasCoordinates[1], {
color,
lineWidth,
lineDash,
shadow,
}, dataId1);
const secondLineUID = '1';
drawLineSvg(svgDrawingHelper, annotationUID, secondLineUID, canvasCoordinates[2], canvasCoordinates[3], {
color,
lineWidth,
lineDash,
shadow,
}, dataId2);
renderStatus = true;
const textLines = this.configuration.getTextLines(data, targetId);
if (!textLines || textLines.length === 0) {
continue;
}
if (!this.renderLinkedTextBoxAnnotation({
enabledElement,
svgDrawingHelper,
annotation,
styleSpecifier,
textLines,
canvasCoordinates,
})) {
continue;
}
}
return renderStatus;
};
}
addNewAnnotation(evt) {
const eventDetail = evt.detail;
const { currentPoints, element } = eventDetail;
const worldPos = currentPoints.world;
const enabledElement = getEnabledElement(element);
const { viewport } = enabledElement;
this.isDrawing = true;
const camera = viewport.getCamera();
const { viewPlaneNormal, viewUp } = camera;
const referencedImageId = this.getReferencedImageId(viewport, worldPos, viewPlaneNormal, viewUp);
const FrameOfReferenceUID = viewport.getFrameOfReferenceUID();
const annotation = {
highlighted: true,
invalidated: true,
metadata: {
toolName: this.getToolName(),
viewPlaneNormal: [...viewPlaneNormal],
viewUp: [...viewUp],
FrameOfReferenceUID,
referencedImageId,
...viewport.getViewReference({ points: [worldPos] }),
},
data: {
handles: {
points: [
[...worldPos],
[...worldPos],
[...worldPos],
[...worldPos],
],
textBox: {
hasMoved: false,
worldPosition: [0, 0, 0],
worldBoundingBox: {
topLeft: [0, 0, 0],
topRight: [0, 0, 0],
bottomLeft: [0, 0, 0],
bottomRight: [0, 0, 0],
},
},
activeHandleIndex: null,
},
label: '',
cachedStats: {},
},
};
addAnnotation(annotation, element);
const viewportIdsToRender = getViewportIdsWithToolToRender(element, this.getToolName());
this.editData = {
annotation,
viewportIdsToRender,
handleIndex: 1,
movingTextBox: false,
newAnnotation: true,
hasMoved: false,
};
this._activateDraw(element);
hideElementCursor(element);
evt.preventDefault();
triggerAnnotationRenderForViewportIds(viewportIdsToRender);
return annotation;
}
static {
this.hydrate = (viewportId, axis, options) => {
const enabledElement = getEnabledElementByViewportId(viewportId);
if (!enabledElement) {
return;
}
const { viewport } = enabledElement;
const existingAnnotations = getAllAnnotations();
const toolAnnotations = existingAnnotations.filter((annotation) => annotation.metadata.toolName === 'SegmentBidirectional');
const existingAnnotation = toolAnnotations.find((annotation) => {
const { metadata } = annotation;
if (metadata.segmentIndex === options?.segmentIndex &&
metadata.segmentationId === options?.segmentationId) {
return true;
}
return false;
});
if (existingAnnotation) {
removeAnnotation(existingAnnotation.annotationUID);
}
const { FrameOfReferenceUID, referencedImageId, viewPlaneNormal, instance, } = this.hydrateBase(SegmentBidirectionalTool, enabledElement, axis[0], options);
const [majorAxis, minorAxis] = axis;
const [major0, major1] = majorAxis;
const [minor0, minor1] = minorAxis;
const points = [major0, major1, minor0, minor1];
const { toolInstance, ...serializableOptions } = options || {};
const annotation = {
annotationUID: options?.annotationUID || utilities.uuidv4(),
data: {
handles: {
points,
activeHandleIndex: null,
textBox: {
hasMoved: false,
worldPosition: [0, 0, 0],
worldBoundingBox: {
topLeft: [0, 0, 0],
topRight: [0, 0, 0],
bottomLeft: [0, 0, 0],
bottomRight: [0, 0, 0],
},
},
},
cachedStats: {},
},
highlighted: false,
autoGenerated: false,
invalidated: false,
isLocked: false,
isVisible: true,
metadata: {
segmentIndex: options?.segmentIndex,
segmentationId: options?.segmentationId,
toolName: instance.getToolName(),
viewPlaneNormal,
FrameOfReferenceUID,
referencedImageId,
...serializableOptions,
},
};
addAnnotation(annotation, viewport.element);
triggerAnnotationRenderForViewportIds([viewport.id]);
return annotation;
};
}
}
export default SegmentBidirectionalTool;
@@ -317,7 +317,7 @@ export default {
color: yellow;
}
}
::v-deep.el-button--text{
::v-deep .el-button--text{
color: #d0d0d0;
}
}
@@ -494,7 +494,7 @@ export default {
<style lang="scss" scoped>
.medical-feedback-ir{
::v-deep.search {
::v-deep .search {
padding: 0px !important;
}
}
@@ -366,7 +366,7 @@ export default {
color: yellow;
}
}
::v-deep.el-button--text{
::v-deep .el-button--text{
color: #d0d0d0;
}
}
@@ -161,16 +161,16 @@ export default {
</script>
<style lang="scss" scoped>
.issues-form{
::v-deep.el-radio-group{
::v-deep .el-radio-group{
width:300px;
}
::v-deep.el-textarea{
::v-deep .el-textarea{
width:300px;
}
::v-deep.el-input{
::v-deep .el-input{
width:300px;
}
::v-deep.el-select{
::v-deep .el-select{
width:300px;
}
@@ -297,7 +297,7 @@ export default {
height: 100%;
padding: 0 10px;
box-sizing: border-box;
::v-deep.el-tabs{
::v-deep .el-tabs{
box-sizing: border-box;
height: 100%;
display: flex;
@@ -324,12 +324,12 @@ export default {
}
}
::v-deep.dialog-container{
::v-deep .dialog-container{
margin-top: 50px !important;
width:75%;
height:80%;
}
::v-deep.el-dialog__body{
::v-deep .el-dialog__body{
padding: 20px 20px 0 20px;
height: calc(100% - 70px);
}
@@ -339,7 +339,7 @@ export default {
}
.full-dialog-container{
::v-deep.is-fullscreen .el-dialog__body{
::v-deep .is-fullscreen .el-dialog__body{
height: calc(100% - 70px);
}
}
@@ -884,7 +884,7 @@ export default {
</script>
<style lang="scss" scoped>
.read-task-allocation {
::v-deep.search {
::v-deep .search {
padding: 0px !important;
}
}
@@ -548,7 +548,7 @@ export default {
height: 100%;
background-color: #fff;
::v-deep.search {
::v-deep .search {
padding: 0px !important;
}
}
@@ -323,12 +323,8 @@ export default {
)
var token = getToken()
var path = ''
if (this.readingTool === 0 || this.readingTool === 2) {
if (this.criterionType === 0 && this.trialId === '08dd28b3-6843-fc05-0242-ac1301000000') {
path = `/fusion?TrialReadingCriterionId=${this.TrialReadingCriterionId}&trialId=${this.trialId}&studyId=62b3dfc4-1e04-4180-910d-fe595f398361&ctseriesId=1bd24f53-d419-32e5-92d4-2b04640aaa65&ptseriesId=2b7b128d-8c3f-8357-ad14-e38f3acbbdff&subjectCode=${row.SubjectCode}&subjectId=${row.SubjectId}&TokenKey=${token}&lang=${this.$i18n.locale}`
} else {
path = `/readingDicoms?TrialReadingCriterionId=${this.TrialReadingCriterionId}&trialId=${this.trialId}&subjectCode=${row.SubjectCode}&subjectId=${row.SubjectId}&isReadingTaskViewInOrder=${this.isReadingTaskViewInOrder}&criterionType=${this.criterionType}&readingTool=${this.readingTool}&TokenKey=${token}`
}
if (this.readingTool === 0 || this.readingTool === 2 || this.readingTool === 3) {
path = `/readingDicoms?TrialReadingCriterionId=${this.TrialReadingCriterionId}&trialId=${this.trialId}&subjectCode=${row.SubjectCode}&subjectId=${row.SubjectId}&isReadingTaskViewInOrder=${this.isReadingTaskViewInOrder}&criterionType=${this.criterionType}&readingTool=${this.readingTool}&TokenKey=${token}`
} else {
path = `/noneDicomReading?TrialReadingCriterionId=${this.TrialReadingCriterionId}&trialId=${this.trialId}&subjectCode=${row.SubjectCode}&subjectId=${row.SubjectId}&isReadingTaskViewInOrder=${this.isReadingTaskViewInOrder}&criterionType=${this.criterionType}&readingTool=${this.readingTool}&TokenKey=${token}`
}
@@ -408,7 +404,7 @@ export default {
height: 100%;
background-color: #fff;
::v-deep.search {
::v-deep .search {
padding: 0px !important;
}
}
@@ -466,7 +466,7 @@ export default {
</script>
<style lang="scss" scoped>
.reread-task{
::v-deep.search {
::v-deep .search {
padding: 0px !important;
}
}
@@ -246,7 +246,7 @@ export default {
border: 1px solid #607d8b !important;
}
::v-deep.el-progress__text {
::v-deep .el-progress__text {
color: #ccc;
font-size: 12px;
}
@@ -295,7 +295,7 @@ export default {
}
}
::v-deep.el-collapse {
::v-deep .el-collapse {
border: none;
.el-collapse-item {
@@ -221,12 +221,12 @@ export default {
overflow-y: auto;
}
::v-deep.el-collapse-item__header {
::v-deep .el-collapse-item__header {
background: #e5ecef;
padding-left: 10px;
}
::v-deep.el-collapse-item__content {
::v-deep .el-collapse-item__content {
padding: 10px;
}
@@ -562,7 +562,7 @@ export default {
background: #fff;
padding: 0px 10px;
box-sizing: border-box;
::v-deep.search {
::v-deep .search {
padding: 0px !important;
}
::v-deep .el-tabs--border-card>.el-tabs__content {
@@ -484,8 +484,15 @@
:disabled="form.IsRequired === 2 && item.value === 1">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<!-- 测量工具 ImageTool -->
<!--标记类型-->
<el-form-item v-if="form.ImageMarkEnum === 1 || form.ImageMarkEnum === 2"
:label="$t('trials:readingUnit:qsList:title:ImageMarkTypeEnum')" prop="ImageMarkTypeEnum">
<el-radio-group v-model="form.ImageMarkTypeEnum" @change="ImageMarkTypeEnumChange">
<el-radio v-for="item of $d.ImageMarkType" :key="item.id" :label="item.value">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<!-- 测量工具 ImageTool -->
<el-form-item v-if="(form.ImageMarkEnum === 1 || form.ImageMarkEnum === 2) && form.ImageMarkTypeEnum === 0"
:label="$t('trials:readingUnit:qsList:title:ImageTool')" prop="ImageTool" :rules="[
{ required: true, message: this.$t('common:ruleMessage:select') }
]">
@@ -495,6 +502,17 @@
</el-radio>
</el-radio-group>
</el-form-item>
<!-- 分割工具 ImageTool -->
<el-form-item v-if="(form.ImageMarkEnum === 1 || form.ImageMarkEnum === 2) && form.ImageMarkTypeEnum === 1"
:label="$t('trials:readingUnit:qsList:title:ImageTool')" prop="ImageTool" :rules="[
{ required: true, message: this.$t('common:ruleMessage:select') }
]">
<el-radio-group v-model="form.ImageTool" @change="imageSegmentToolChange">
<el-radio v-for="tool of readingSegmentTools" :key="tool.toolName" :label="tool.toolName">
{{ $t(tool.i18nKey) }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- 测量值 ImageToolAttribute -->
<el-form-item v-if="form.ImageTool && imageToolAttributes.length > 0"
:label="$t('trials:readingUnit:qsList:title:ImageToolAttribute')" prop="ImageToolAttribute" :rules="[
@@ -696,6 +714,12 @@ export default {
return []
}
},
readingSegmentTools: {
type: Array,
default() {
return []
}
},
readingVersionEnum: {
type: Number,
default: 0
@@ -754,6 +778,7 @@ export default {
ClassifyEditType: null,
ClassifyShowType: null,
ImageMarkEnum: 0,
ImageMarkTypeEnum: 0,
ShowChartTypeEnum: 0,
ImageTool: '',
ImageToolAttribute: '',
@@ -1050,9 +1075,12 @@ export default {
}
}
}
if (this.form.ImageTool) {
if (this.form.ImageTool && this.form.ImageMarkTypeEnum === 0) {
this.imageToolChange(this.form.ImageTool)
}
if (this.form.ImageTool && this.form.ImageMarkTypeEnum === 1) {
this.imageSegmentToolChange(this.form.ImageTool)
}
}
if (!this.data.ShowOrder && this.data.ShowOrder !== 0) {
if (this.list.length > 0) {
@@ -1172,14 +1200,26 @@ export default {
this.form.ImageTool = ''
this.form.ImageToolAttribute = ''
this.imageToolAttributes = []
this.form.ImageMarkTypeEnum = 0
}
},
ImageMarkTypeEnumChange(val) {
this.form.ImageTool = ''
this.form.ImageToolAttribute = ''
this.imageToolAttributes = []
},
imageToolChange(v) {
let i = this.readingTools.findIndex(tool => tool.toolName === v)
if (i > -1) {
this.imageToolAttributes = this.readingTools[i].props
}
},
imageSegmentToolChange(v) {
let i = this.readingSegmentTools.findIndex(tool => tool.toolName === v)
if (i > -1) {
this.imageToolAttributes = this.readingSegmentTools[i].props
}
},
parentQuestionChange(val, form) {
this.isParentExistGroup = false
if (val) {
@@ -1273,6 +1313,7 @@ export default {
form.ClassifyType = null
form.ClassifyShowType = null
form.ImageMarkEnum = 0
form.ImageMarkTypeEnum = 0
form.ShowChartTypeEnum = 0
form.ImageTool = ''
form.ImageToolAttribute = ''
@@ -1,148 +1,81 @@
<template>
<div v-loading="loading">
<div
class="search-form"
style="text-align: left;"
>
<div class="search-form" style="text-align: left;">
<!-- 新增 -->
<el-button
v-if="!isConfirm && hasPermi(['trials:trials-panel:setting:reading-unit:edit']) && !isFromSystem"
size="mini"
type="primary"
@click="handleAdd"
>
<el-button v-if="!isConfirm && hasPermi(['trials:trials-panel:setting:reading-unit:edit']) && !isFromSystem"
size="mini" type="primary" @click="handleAdd">
{{ $t('common:button:add') }}
</el-button>
<!-- 预览 -->
<el-button
v-if="isPreview"
:disabled="tblList.length===0"
size="mini"
type="primary"
@click="handlePreview"
>
<el-button v-if="isPreview" :disabled="tblList.length === 0" size="mini" type="primary" @click="handlePreview">
{{ $t('common:button:preview') }}
</el-button>
</div>
<el-table
:data="tblList"
size="small"
>
<el-table-column
prop="ShowOrder"
label=""
width="50"
/>
<el-table :data="tblList" size="small">
<el-table-column prop="ShowOrder" label="" width="50" />
<!-- 分组 -->
<el-table-column
prop="QuestionGroupName"
v-if="$i18n.locale === 'zh'"
:label="$t('trials:readingUnit:qsList:title:groupNameEn')"
show-overflow-tooltip
/>
<el-table-column prop="QuestionGroupName" v-if="$i18n.locale === 'zh'"
:label="$t('trials:readingUnit:qsList:title:groupNameEn')" show-overflow-tooltip />
<!-- 分组(EN) -->
<el-table-column
prop="QuestionGroupEnName"
v-if="$i18n.locale === 'en'"
:label="$t('trials:readingUnit:qsList:title:groupNameEn')"
show-overflow-tooltip
/>
<el-table-column prop="QuestionGroupEnName" v-if="$i18n.locale === 'en'"
:label="$t('trials:readingUnit:qsList:title:groupNameEn')" show-overflow-tooltip />
<!-- 名称 -->
<el-table-column
prop="QuestionName"
v-if="$i18n.locale === 'zh'"
:label="$t('trials:readingUnit:qsList:title:qsNameEn')"
show-overflow-tooltip
/>
<el-table-column prop="QuestionName" v-if="$i18n.locale === 'zh'"
:label="$t('trials:readingUnit:qsList:title:qsNameEn')" show-overflow-tooltip />
<!-- 名称(EN) -->
<el-table-column
prop="QuestionEnName"
v-if="$i18n.locale === 'en'"
:label="$t('trials:readingUnit:qsList:title:qsNameEn')"
show-overflow-tooltip
/>
<el-table-column prop="QuestionEnName" v-if="$i18n.locale === 'en'"
:label="$t('trials:readingUnit:qsList:title:qsNameEn')" show-overflow-tooltip />
<!-- 题型 -->
<el-table-column
prop="Type"
:label="$t('trials:readingUnit:qsList:title:type')"
show-overflow-tooltip
>
<el-table-column prop="Type" :label="$t('trials:readingUnit:qsList:title:type')" show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('Criterion_Question_Type',scope.row.Type) }}
{{ $fd('Criterion_Question_Type', scope.row.Type) }}
</template>
</el-table-column>
<!-- 是否显示 -->
<el-table-column
prop="ShowQuestion"
:label="$t('trials:readingUnit:qsList:title:isShow')"
show-overflow-tooltip
>
<el-table-column prop="ShowQuestion" :label="$t('trials:readingUnit:qsList:title:isShow')" show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('ShowQuestion',scope.row.ShowQuestion) }}
{{ $fd('ShowQuestion', scope.row.ShowQuestion) }}
</template>
</el-table-column>
<!-- 是否必填 -->
<el-table-column
prop="IsRequired"
:label="$t('trials:readingUnit:qsList:title:isRequired')"
show-overflow-tooltip
>
<el-table-column prop="IsRequired" :label="$t('trials:readingUnit:qsList:title:isRequired')"
show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('QuestionRequired',scope.row.IsRequired) }}
{{ $fd('QuestionRequired', scope.row.IsRequired) }}
</template>
</el-table-column>
<!-- 是否裁判问题 -->
<el-table-column
prop="IsJudgeQuestion"
:label="$t('trials:readingUnit:qsList:title:isJudgeQuestion')"
width="120"
show-overflow-tooltip
>
<el-table-column prop="IsJudgeQuestion" :label="$t('trials:readingUnit:qsList:title:isJudgeQuestion')" width="120"
show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('YesOrNo', scope.row.IsJudgeQuestion) }}
</template>
</el-table-column>
<!-- 是否在阅片页面显示 -->
<el-table-column
prop="IsShowInDicom"
:label="$t('trials:readingUnit:qsList:title:isShowInDicom')"
width="140"
show-overflow-tooltip
>
<el-table-column prop="IsShowInDicom" :label="$t('trials:readingUnit:qsList:title:isShowInDicom')" width="140"
show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('YesOrNo', scope.row.IsShowInDicom) }}
</template>
</el-table-column>
<!-- 是否在全局阅片显示 -->
<el-table-column
prop="GlobalReadingShowType"
:label="$t('trials:readingUnit:qsList:title:globalReadingShowType')"
width="160"
show-overflow-tooltip
>
<el-table-column prop="GlobalReadingShowType" :label="$t('trials:readingUnit:qsList:title:globalReadingShowType')"
width="160" show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('GlobalReadingShowType', scope.row.GlobalReadingShowType) }}
</template>
</el-table-column>
<!-- 导出目标表格 -->
<el-table-column
prop="ExportResult"
:label="$t('trials:readingUnit:qsList:title:ExportResult')"
width="160"
show-overflow-tooltip
>
<el-table-column prop="ExportResult" :label="$t('trials:readingUnit:qsList:title:ExportResult')" width="160"
show-overflow-tooltip>
<template slot-scope="scope">
{{ getStringResult(scope.row.ExportResult, 'ExportResult') }}
</template>
</el-table-column>
<!-- 限制编辑 -->
<el-table-column
prop="LimitEdit"
:label="$t('trials:readingUnit:qsList:title:limitEdit')"
width="160"
show-overflow-tooltip
>
<el-table-column prop="LimitEdit" :label="$t('trials:readingUnit:qsList:title:limitEdit')" width="160"
show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('LimitEdit', scope.row.LimitEdit) }}
</template>
@@ -154,105 +87,50 @@
width="140"
show-overflow-tooltip
/> -->
<el-table-column
prop=""
:label="$t('common:action:action')"
width="300"
show-overflow-tooltip
>
<el-table-column prop="" :label="$t('common:action:action')" width="300" show-overflow-tooltip>
<template slot-scope="scope">
<!-- 编辑 -->
<el-button
v-if="!isConfirm && !isFromSystem && hasPermi(['trials:trials-panel:setting:reading-unit:edit'])"
type="primary"
size="mini"
@click="handleEdit(scope.row)"
>
<el-button v-if="!isConfirm && !isFromSystem && hasPermi(['trials:trials-panel:setting:reading-unit:edit'])"
type="primary" size="mini" @click="handleEdit(scope.row)">
{{ $t('trials:readingUnit:qsList:title:edit') }}
</el-button>
<!-- 查看 -->
<el-button
v-else
type="primary"
size="mini"
@click="handlelook(scope.row)"
>
<el-button v-else type="primary" size="mini" @click="handlelook(scope.row)">
{{ $t('trials:readingUnit:qsList:title:view') }}
</el-button>
<!-- 表格问题 -->
<el-button
v-if="scope.row.Type === 'table' || scope.row.Type === 'basicTable'"
type="primary"
size="mini"
@click="handleConfig(scope.row)"
>
<el-button v-if="scope.row.Type === 'table' || scope.row.Type === 'basicTable'" type="primary" size="mini"
@click="handleConfig(scope.row)">
{{ $t('trials:readingUnit:qsList:title:tableQs') }}
</el-button>
<!-- 删除 -->
<el-button
v-if="!isConfirm && !isFromSystem && hasPermi(['trials:trials-panel:setting:reading-unit:edit'])"
type="danger"
size="mini"
@click="handleDelete(scope.row)"
>
<el-button v-if="!isConfirm && !isFromSystem && hasPermi(['trials:trials-panel:setting:reading-unit:edit'])"
type="danger" size="mini" @click="handleDelete(scope.row)">
{{ $t('trials:readingUnit:qsList:title:delete') }}
</el-button>
</template>
</el-table-column>
</el-table>
<el-dialog
v-if="addOrEdit.visible"
:visible.sync="addOrEdit.visible"
:close-on-click-modal="false"
:title="addOrEdit.title"
width="800px"
append-to-body
custom-class="base-dialog-wrapper"
>
<questions-form
ref="addOrEdit"
:data="rowData"
:trial-criterion-id="trialCriterionId"
:is-from-system="isFromSystem"
:digit-places="digitPlaces"
:readingTools="readingTools"
:readingVersionEnum="readingVersionEnum"
:list="tblList"
:is-look="isLook"
:is-system-criterion="isSystemCriterion"
@close="addOrEdit.visible = false"
@getList="getList"
@reloadArbitrationRules="reloadArbitrationRules"
/>
<el-dialog v-if="addOrEdit.visible" :visible.sync="addOrEdit.visible" :close-on-click-modal="false"
:title="addOrEdit.title" width="800px" append-to-body custom-class="base-dialog-wrapper">
<questions-form ref="addOrEdit" :data="rowData" :trial-criterion-id="trialCriterionId"
:is-from-system="isFromSystem" :digit-places="digitPlaces" :readingTools="readingTools"
:readingSegmentTools="readingSegmentTools" :readingVersionEnum="readingVersionEnum" :list="tblList"
:is-look="isLook" :is-system-criterion="isSystemCriterion" @close="addOrEdit.visible = false" @getList="getList"
@reloadArbitrationRules="reloadArbitrationRules" />
</el-dialog>
<el-dialog
v-if="preview.visible"
:visible.sync="preview.visible"
:close-on-click-modal="false"
:title="preview.title"
:fullscreen="true"
>
<el-dialog v-if="preview.visible" :visible.sync="preview.visible" :close-on-click-modal="false"
:title="preview.title" :fullscreen="true">
<questions-preview :criterion-id="trialCriterionId" :is-system-criterion="isSystemCriterion" :form-type="1" />
</el-dialog>
<el-dialog
v-if="config.visible"
:visible.sync="config.visible"
:close-on-click-modal="false"
:title="config.title"
:fullscreen="true"
>
<table-qs-list
:digit-places="digitPlaces"
:reading-question-id="rowData.Id"
:is-from-system="isFromSystem"
:is-confirm="isConfirm"
:criterion-id="trialCriterionId"
:readingTools="readingTools"
:readingVersionEnum="readingVersionEnum"
@close="config.visible = false"
/>
<el-dialog v-if="config.visible" :visible.sync="config.visible" :close-on-click-modal="false" :title="config.title"
:fullscreen="true">
<table-qs-list :digit-places="digitPlaces" :reading-question-id="rowData.Id" :is-from-system="isFromSystem"
:is-confirm="isConfirm" :criterion-id="trialCriterionId" :readingTools="readingTools"
:readingSegmentTools="readingSegmentTools" :readingVersionEnum="readingVersionEnum"
@close="config.visible = false" />
</el-dialog>
</div>
</template>
@@ -310,6 +188,12 @@ export default {
return []
}
},
readingSegmentTools: {
type: Array,
default() {
return []
}
},
readingVersionEnum: {
type: Number,
default: 0
@@ -350,7 +234,8 @@ export default {
this.rowData = {
ReadingQuestionCriterionTrialId: this.trialCriterionId,
ReadingCriterionPageId: this.readingCriterionPageId,
Id: '' }
Id: ''
}
this.isLook = false
this.addOrEdit.title = this.$t('trials:readingUnit:qsList:title:add')// ''
this.addOrEdit.visible = true
@@ -362,9 +247,9 @@ export default {
this.rowData.ReadingCriterionPageId = this.readingCriterionPageId
let title = ''
if (this.$i18n.locale === 'zh' && (row.QuestionName || row.GroupName)) {
title =`${row.QuestionName ? row.QuestionName : row.GroupName}`
title = `${row.QuestionName ? row.QuestionName : row.GroupName}`
} else if (this.$i18n.locale === 'en' && (row.QuestionEnName || row.GroupEnName)) {
title =`: ${row.QuestionEnName ? row.QuestionEnName : row.GroupEnName}`
title = `: ${row.QuestionEnName ? row.QuestionEnName : row.GroupEnName}`
}
this.addOrEdit.title = `${this.$t('trials:readingUnit:qsList:title:view')} ${title} `// ''
this.addOrEdit.visible = true
@@ -400,8 +285,8 @@ export default {
// this.preview.visible = true
window.localStorage.setItem('TrialReadingCriterionId', this.TrialReadingCriterionId)
var token = getToken()
var path = `/criterionquestions?TrialReadingCriterionId=${this.trialCriterionId}&isSystemCriterion=${this.isSystemCriterion}&TokenKey=${token}`
var path = `/criterionquestions?TrialReadingCriterionId=${this.trialCriterionId}&isSystemCriterion=${this.isSystemCriterion}&TokenKey=${token}`
var routeData = this.$router.resolve({ path })
this.openWindow = window.open(routeData.href, '_blank')
},
@@ -431,5 +316,4 @@ export default {
}
</script>
<style lang="scss" scoped>
</style>
<style lang="scss" scoped></style>
@@ -1,59 +1,37 @@
<template>
<div>
<el-form
ref="readingCriterionsForm"
v-loading="loading"
:model="form"
:rules="rules"
label-width="120px"
size="small"
>
<el-form ref="readingCriterionsForm" v-loading="loading" :model="form" :rules="rules" label-width="120px"
size="small">
<!-- '表单问题' -->
<el-form-item :label="$t('trials:readingUnit:readingCriterion:title:formQs')">
<questions-list
:ref="`questionList${trialReadingCriterionId}`"
v-if="form.FormType===1"
:trial-reading-criterion-id="trialReadingCriterionId"
:list="readingInfo.TrialQuestionList"
:trial-criterion-id="readingInfo.TrialCriterionId"
:is-confirm="isConfirm"
:is-system-criterion="isSystemCriterion"
:is-from-system="readingInfo.IsFromSystem"
:digit-places="digitPlaces"
:readingTools="readingTools"
:readingVersionEnum="readingVersionEnum"
@reloadArbitrationRules="reloadArbitrationRules"
/>
<questions-list :ref="`questionList${trialReadingCriterionId}`" v-if="form.FormType === 1"
:trial-reading-criterion-id="trialReadingCriterionId" :list="readingInfo.TrialQuestionList"
:trial-criterion-id="readingInfo.TrialCriterionId" :is-confirm="isConfirm"
:is-system-criterion="isSystemCriterion" :is-from-system="readingInfo.IsFromSystem"
:digit-places="digitPlaces" :readingTools="readingTools" :readingSegmentTools="readingSegmentTools"
:readingVersionEnum="readingVersionEnum" @reloadArbitrationRules="reloadArbitrationRules" />
</el-form-item>
<el-form-item v-if=" hasPermi(['trials:trials-panel:setting:reading-unit:edit'])">
<!-- &lt;!&ndash; 保存 &ndash;&gt;-->
<!-- <el-button-->
<!-- v-if="!isConfirm && isAdditionalAssessment"-->
<!-- type="primary"-->
<!-- @click="handleSave(true)"-->
<!-- >-->
<!-- {{ $t('common:button:save') }}-->
<!-- </el-button>-->
<el-form-item v-if="hasPermi(['trials:trials-panel:setting:reading-unit:edit'])">
<!-- &lt;!&ndash; 保存 &ndash;&gt;-->
<!-- <el-button-->
<!-- v-if="!isConfirm && isAdditionalAssessment"-->
<!-- type="primary"-->
<!-- @click="handleSave(true)"-->
<!-- >-->
<!-- {{ $t('common:button:save') }}-->
<!-- </el-button>-->
<!-- 基础数据配置 -->
<el-button
type="primary"
@click="configBaseDataVisible = true"
>
<el-button type="primary" @click="configBaseDataVisible = true">
{{ $t('trials:readingUnit:readingCriterion:title:baseDataCfg') }}
</el-button>
</el-form-item>
</el-form>
<!-- 基础数据配置 -->
<el-dialog
v-if="configBaseDataVisible"
:title="$t('trials:readingUnit:readingCriterion:title:baseDataCfg')"
:visible.sync="configBaseDataVisible"
:close-on-click-modal="false"
:fullscreen="true"
append-to-body
custom-class="base-dialog-wrapper"
>
<BaseDataConfig :trial-reading-criterion-id="trialReadingCriterionId" :is-from-system="readingInfo.IsFromSystem" :is-confirm="isConfirm" />
<el-dialog v-if="configBaseDataVisible" :title="$t('trials:readingUnit:readingCriterion:title:baseDataCfg')"
:visible.sync="configBaseDataVisible" :close-on-click-modal="false" :fullscreen="true" append-to-body
custom-class="base-dialog-wrapper">
<BaseDataConfig :trial-reading-criterion-id="trialReadingCriterionId" :is-from-system="readingInfo.IsFromSystem"
:is-confirm="isConfirm" />
</el-dialog>
@@ -85,6 +63,12 @@ export default {
default() {
return []
}
},
readingSegmentTools: {
type: Array,
default() {
return []
}
}
},
data() {
@@ -122,7 +106,7 @@ export default {
this.additionalAssessmentOptionList = res.Result
if (this.additionalAssessmentOptionList.length > 0) {
this.additionalAssessmentOptionList.forEach(v => {
this.$set(this.form, 'AdditionalAssessmentType'+v.Id, v.IsSelected)
this.$set(this.form, 'AdditionalAssessmentType' + v.Id, v.IsSelected)
})
}
getTrialReadingCriterionInfo({ trialId, TrialReadingCriterionId: this.trialReadingCriterionId }).then(res => {
@@ -185,7 +169,7 @@ export default {
reloadArbitrationRules() {
this.$emit('reloadArbitrationRules')
},
handleConfig() {}
handleConfig() { }
}
}
@@ -30,9 +30,8 @@
</el-radio>
</el-radio-group>
</el-form-item>
<!-- 测量工具 -->
<el-form-item
v-if="CriterionType === 0 && (form.ReadingTool === 0 || form.ReadingTool === 1 || form.ReadingTool === 2) && form.ReadingVersionEnum === 1"
<!-- 测量工具 && (form.ReadingTool === 0 || form.ReadingTool === 1 || form.ReadingTool === 2)-->
<el-form-item v-if="CriterionType === 0 && form.ReadingVersionEnum === 1"
:label="$t('trials:readingUnit:readingRules:title:measureTool')">
<el-checkbox-group v-model="form.ReadingToolList" :disabled="isConfirm ||
!hasPermi(['trials:trials-panel:setting:reading-unit:edit'])
@@ -42,6 +41,17 @@
</el-checkbox>
</el-checkbox-group>
</el-form-item>
<!--分割工具 && (form.ReadingTool === 0 || form.ReadingTool === 1 || form.ReadingTool === 2)-->
<el-form-item v-if="CriterionType === 0 && form.ReadingVersionEnum === 1 && form.ReadingTool === 3"
:label="$t('trials:readingUnit:readingRules:title:segmentTool')">
<el-checkbox-group v-model="form.SegmentToolList" :disabled="isConfirm ||
!hasPermi(['trials:trials-panel:setting:reading-unit:edit'])
">
<el-checkbox v-for="tool in segmentTools" :key="tool.toolName" :label="tool.toolName" name="SegmentToolList">
{{ $t(`${tool.i18nKey}`) }}
</el-checkbox>
</el-checkbox-group>
</el-form-item>
<!--检查类型筛选-->
<el-form-item :label="$t('trials:processCfg:form:IsImageFilter')" prop="IsImageFilter">
<el-radio-group v-model="form.IsImageFilter" @input="IsImageFilterChange" :disabled="isConfirm ||
@@ -421,6 +431,7 @@ export default {
ReadingTool: 0,
ReadingVersionEnum: null,
ReadingToolList: [],
SegmentToolList: [],
ReadingTaskViewEnum: null,
IsImageLabeled: null,
IsReadingShowSubjectInfo: null,
@@ -616,7 +627,8 @@ export default {
modalityList: [],
CriterionModalitys: [],
modalityIsCheck: false, //
tools: []
tools: [],
segmentTools: []
}
},
mounted() {
@@ -737,13 +749,20 @@ export default {
//
handleReadingToolInput(val) {
this.form.ReadingToolList = []
if (this.CriterionType !== 0) return this.tools = []
if (val === 0 || val === 2) {
this.form.SegmentToolList = []
if (this.CriterionType !== 0) {
this.segmentTools = []
return this.tools = []
}
if (val === 0 || val === 2 || val === 3) {
this.tools = [...config.customizeStandards]
}
if (val === 1) {
this.tools = [...config.customizeStandardsNoneDicom]
}
if (val === 3) {
this.segmentTools = [...config.customizeStandardsSegmentDicom]
}
},
//
IsImageFilterChange(data) {
@@ -786,12 +805,15 @@ export default {
this.form.KeyFileListStr = this.form.KeyFileList.map(item => item.FileName).join(',')
}
if (this.CriterionType === 0) {
if (this.form.ReadingTool === 0 || this.form.ReadingTool === 2) {
if (this.form.ReadingTool === 0 || this.form.ReadingTool === 2 || this.form.ReadingTool === 3) {
this.tools = [...config.customizeStandards]
}
if (this.form.ReadingTool === 1) {
this.tools = [...config.customizeStandardsNoneDicom]
}
if (this.form.ReadingTool === 3) {
this.segmentTools = [...config.customizeStandardsSegmentDicom]
}
}
this.CriterionModalitys = this.form.CriterionModalitys
? this.form.CriterionModalitys.split('|')
@@ -817,7 +839,7 @@ export default {
this.$emit('setGlobalReading', res.Result.IsGlobalReading)
this.$emit('setOncologyReading', res.Result.IsOncologyReading)
this.$emit('setDigitPlaces', res.Result.DigitPlaces)
this.$emit('setReadingTools', { readingTools: res.Result.ReadingToolList, isNoneDicom: this.form.ReadingTool === 1 })
this.$emit('setReadingTools', { readingTools: res.Result.ReadingToolList, readingSegmentTools: res.Result.SegmentToolList, ReadingTool: this.form.ReadingTool })
if (res.Result.ReadingType === 1) {
this.$emit('setArbitrationReading', false)
@@ -859,7 +881,7 @@ export default {
this.$emit('setGlobalReading', this.form.IsGlobalReading)
this.$emit('setOncologyReading', this.form.IsOncologyReading)
this.$emit('setDigitPlaces', this.form.DigitPlaces)
this.$emit('setReadingTools', { readingTools: this.form.ReadingToolList, isNoneDicom: this.form.ReadingTool === 1 })
this.$emit('setReadingTools', { readingTools: this.form.ReadingToolList, readingSegmentTools: this.form.SegmentToolList, ReadingTool: this.form.ReadingTool })
if (this.form.ReadingType === 1) {
this.$emit('setArbitrationReading', false)
}
@@ -286,8 +286,15 @@
:disabled="form.IsRequired === 2 && item.value === 1">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<!-- 测量工具 ImageTool -->
<!--标记类型-->
<el-form-item v-if="form.ImageMarkEnum === 1 || form.ImageMarkEnum === 2"
:label="$t('trials:readingUnit:qsList:title:ImageMarkTypeEnum')" prop="ImageMarkTypeEnum">
<el-radio-group v-model="form.ImageMarkTypeEnum" @change="ImageMarkTypeEnumChange">
<el-radio v-for="item of $d.ImageMarkType" :key="item.id" :label="item.value">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<!-- 测量工具 ImageTool -->
<el-form-item v-if="(form.ImageMarkEnum === 1 || form.ImageMarkEnum === 2) && form.ImageMarkTypeEnum === 0"
:label="$t('trials:readingUnit:qsList:title:ImageTool')" prop="ImageTool" :rules="[
{ required: true, message: this.$t('common:ruleMessage:select') }
]">
@@ -297,6 +304,17 @@
</el-radio>
</el-radio-group>
</el-form-item>
<!-- 分割工具 ImageTool -->
<el-form-item v-if="(form.ImageMarkEnum === 1 || form.ImageMarkEnum === 2) && form.ImageMarkTypeEnum === 1"
:label="$t('trials:readingUnit:qsList:title:ImageTool')" prop="ImageTool" :rules="[
{ required: true, message: this.$t('common:ruleMessage:select') }
]">
<el-radio-group v-model="form.ImageTool" @change="imageSegmentToolChange">
<el-radio v-for="tool of readingSegmentTools" :key="tool.toolName" :label="tool.toolName">
{{ $t(tool.i18nKey) }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- 测量值 ImageToolAttribute -->
<el-form-item v-if="form.ImageTool && imageToolAttributes.length > 0"
:label="$t('trials:readingUnit:qsList:title:ImageToolAttribute')" prop="ImageToolAttribute" :rules="[
@@ -563,6 +581,12 @@ export default {
return []
}
},
readingSegmentTools: {
type: Array,
default() {
return []
}
},
readingVersionEnum: {
type: Number,
default: 0
@@ -617,6 +641,7 @@ export default {
ClassifyEditType: null,
ClassifyShowType: null,
ImageMarkEnum: 0,
ImageMarkTypeEnum: 0,
ShowChartTypeEnum: 0,
ImageTool: '',
ImageToolAttribute: '',
@@ -898,9 +923,12 @@ export default {
}
}
}
if (this.form.ImageTool) {
if (this.form.ImageTool && this.form.ImageMarkTypeEnum === 0) {
this.imageToolChange(this.form.ImageTool)
}
if (this.form.ImageTool && this.form.ImageMarkTypeEnum === 1) {
this.imageSegmentToolChange(this.form.ImageTool)
}
}
if (this.form.ClassifyTableQuestionId) {
this.classifyQuestionChange(this.form.ClassifyTableQuestionId)
@@ -1011,14 +1039,26 @@ export default {
this.form.ImageTool = ''
this.form.ImageToolAttribute = ''
this.imageToolAttributes = []
this.form.ImageMarkTypeEnum = 0
}
},
ImageMarkTypeEnumChange(val) {
this.form.ImageTool = ''
this.form.ImageToolAttribute = ''
this.imageToolAttributes = []
},
imageToolChange(v) {
let i = this.readingTools.findIndex(tool => tool.toolName === v)
if (i > -1) {
this.imageToolAttributes = this.readingTools[i].props
}
},
imageSegmentToolChange(v) {
let i = this.readingSegmentTools.findIndex(tool => tool.toolName === v)
if (i > -1) {
this.imageToolAttributes = this.readingSegmentTools[i].props
}
},
async parentQuestionChange(val, form) {
if (val) {
var index = this.parentOptions.findIndex(item => {
@@ -1129,6 +1169,7 @@ export default {
form.ClassifyType = null
form.ClassifyShowType = null
form.ImageMarkEnum = 0
form.ImageMarkTypeEnum = 0
form.ShowChartTypeEnum = 0
form.ImageTool = ''
form.ImageToolAttribute = ''
@@ -1,73 +1,37 @@
<template>
<div v-loading="loading">
<div
class="search-form"
style="text-align: right;"
>
<el-button
v-if="!isConfirm && !isFromSystem"
size="mini"
type="primary"
@click="handleAdd"
>
{{$t('common:button:new')}}
<div class="search-form" style="text-align: right;">
<el-button v-if="!isConfirm && !isFromSystem" size="mini" type="primary" @click="handleAdd">
{{ $t('common:button:new') }}
</el-button>
</div>
<el-table
v-adaptive="{bottomOffset:60}"
:data="list"
size="small"
height="100"
>
<el-table-column
prop="ShowOrder"
:label="$t('trials:qcCfg:table:order')"
min-width="70"
/>
<el-table v-adaptive="{ bottomOffset: 60 }" :data="list" size="small" height="100">
<el-table-column prop="ShowOrder" :label="$t('trials:qcCfg:table:order')" min-width="70" />
<!-- 名称 -->
<el-table-column
prop="QuestionName"
v-if="$i18n.locale === 'zh'"
:label="$t('trials:readingUnit:qsList:title:qsName')"
show-overflow-tooltip
/>
<el-table-column prop="QuestionName" v-if="$i18n.locale === 'zh'"
:label="$t('trials:readingUnit:qsList:title:qsName')" show-overflow-tooltip />
<!-- 名称(EN) -->
<el-table-column
prop="QuestionEnName"
v-if="$i18n.locale === 'en'"
:label="$t('trials:readingUnit:qsList:title:qsNameEn')"
show-overflow-tooltip
/>
<el-table-column prop="QuestionEnName" v-if="$i18n.locale === 'en'"
:label="$t('trials:readingUnit:qsList:title:qsNameEn')" show-overflow-tooltip />
<!-- 题型 -->
<el-table-column
prop="Type"
:label="$t('trials:readingUnit:qsList:title:type')"
show-overflow-tooltip
>
<el-table-column prop="Type" :label="$t('trials:readingUnit:qsList:title:type')" show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('Criterion_Question_Type',scope.row.Type) }}
{{ $fd('Criterion_Question_Type', scope.row.Type) }}
</template>
</el-table-column>
<!-- 是否显示 -->
<el-table-column
prop="ShowQuestion"
:label="$t('trials:readingUnit:qsList:title:isShow')"
show-overflow-tooltip
>
<el-table-column prop="ShowQuestion" :label="$t('trials:readingUnit:qsList:title:isShow')" show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('ShowQuestion',scope.row.ShowQuestion) }}
{{ $fd('ShowQuestion', scope.row.ShowQuestion) }}
</template>
</el-table-column>
<!-- 是否必填 -->
<el-table-column
prop="IsRequired"
:label="$t('trials:readingUnit:qsList:title:isRequired')"
show-overflow-tooltip
>
<el-table-column prop="IsRequired" :label="$t('trials:readingUnit:qsList:title:isRequired')"
show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('QuestionRequired',scope.row.IsRequired) }}
{{ $fd('QuestionRequired', scope.row.IsRequired) }}
</template>
</el-table-column>
<!-- 是否裁判问题 -->
@@ -104,41 +68,26 @@
</template>
</el-table-column> -->
<!-- 导出目标表格 -->
<el-table-column
prop="ExportResult"
:label="$t('trials:readingUnit:qsList:title:ExportResult')"
width="160"
show-overflow-tooltip
>
<el-table-column prop="ExportResult" :label="$t('trials:readingUnit:qsList:title:ExportResult')" width="160"
show-overflow-tooltip>
<template slot-scope="scope">
{{ getStringResult(scope.row.ExportResult, 'ExportResult') }}
</template>
</el-table-column>
<!-- 限制编辑 -->
<el-table-column
prop="LimitEdit"
:label="$t('trials:readingUnit:qsList:title:limitEdit')"
show-overflow-tooltip
>
<el-table-column prop="LimitEdit" :label="$t('trials:readingUnit:qsList:title:limitEdit')" show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('LimitEdit', scope.row.LimitEdit) }}
</template>
</el-table-column>
<el-table-column
prop="IsCopy"
:label="$t('trials:readingUnit:qsList:title:PrecopyValue')"
show-overflow-tooltip
>
<el-table-column prop="IsCopy" :label="$t('trials:readingUnit:qsList:title:PrecopyValue')" show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('YesOrNo', scope.row.IsCopy) }}
</template>
</el-table-column>
<!-- 最大行数 -->
<el-table-column
prop="MaxRowCount"
:label="$t('trials:readingUnit:qsList:title:MaxRowCount')"
show-overflow-tooltip
>
<el-table-column prop="MaxRowCount" :label="$t('trials:readingUnit:qsList:title:MaxRowCount')"
show-overflow-tooltip>
</el-table-column>
<!-- 注释 -->
<!-- <el-table-column
@@ -148,35 +97,15 @@
show-overflow-tooltip
>
</el-table-column> -->
<el-table-column
prop=""
:label="$t('common:action:action')"
width="200"
show-overflow-tooltip
>
<el-table-column prop="" :label="$t('common:action:action')" width="200" show-overflow-tooltip>
<template slot-scope="scope">
<el-button
v-if="!isConfirm && !isFromSystem"
type="primary"
size="mini"
@click="handleEdit(scope.row)"
>
<el-button v-if="!isConfirm && !isFromSystem" type="primary" size="mini" @click="handleEdit(scope.row)">
{{ $t('common:button:edit') }}
</el-button>
<el-button
v-else
type="primary"
size="mini"
@click="handleLook(scope.row)"
>
<el-button v-else type="primary" size="mini" @click="handleLook(scope.row)">
{{ $t('trials:readingPeriod:button:view') }}
</el-button>
<el-button
v-if="!isConfirm && !isFromSystem"
type="danger"
size="mini"
@click="handleDelete(scope.row)"
>
<el-button v-if="!isConfirm && !isFromSystem" type="danger" size="mini" @click="handleDelete(scope.row)">
{{ $t('common:button:delete') }}
</el-button>
@@ -184,29 +113,12 @@
</el-table-column>
</el-table>
<el-dialog
v-if="addOrEdit.visible"
:visible.sync="addOrEdit.visible"
:close-on-click-modal="false"
:title="addOrEdit.title"
width="600px"
append-to-body
custom-class="base-dialog-wrapper"
>
<TableQsForm
ref="addOrEdit"
:data="rowData"
:list="list"
:type="type"
:is-from-system="isFromSystem"
:digit-places="digitPlaces"
:reading-question-id="readingQuestionId"
:criterion-id="criterionId"
:readingTools="readingTools"
:readingVersionEnum="readingVersionEnum"
@close="addOrEdit.visible = false"
@getList="getList"
/>
<el-dialog v-if="addOrEdit.visible" :visible.sync="addOrEdit.visible" :close-on-click-modal="false"
:title="addOrEdit.title" width="600px" append-to-body custom-class="base-dialog-wrapper">
<TableQsForm ref="addOrEdit" :data="rowData" :list="list" :type="type" :is-from-system="isFromSystem"
:digit-places="digitPlaces" :reading-question-id="readingQuestionId" :criterion-id="criterionId"
:readingTools="readingTools" :readingSegmentTools="readingSegmentTools" :readingVersionEnum="readingVersionEnum"
@close="addOrEdit.visible = false" @getList="getList" />
</el-dialog>
</div>
@@ -250,6 +162,12 @@ export default {
return []
}
},
readingSegmentTools: {
type: Array,
default() {
return []
}
},
readingVersionEnum: {
type: Number,
default: 0
@@ -296,9 +214,9 @@ export default {
this.type = 'look'
let title = ''
if (this.$i18n.locale === 'zh' && (row.QuestionName || row.GroupName)) {
title =`${row.QuestionName ? row.QuestionName : row.GroupName}`
title = `${row.QuestionName ? row.QuestionName : row.GroupName}`
} else if (this.$i18n.locale === 'en' && (row.QuestionEnName || row.GroupEnName)) {
title =`: ${row.QuestionEnName ? row.QuestionEnName : row.GroupEnName}`
title = `: ${row.QuestionEnName ? row.QuestionEnName : row.GroupEnName}`
}
this.addOrEdit.title = `${this.$t('trials:readingPeriod:button:view')}${title}`
this.addOrEdit.visible = true
@@ -337,4 +255,3 @@ export default {
}
}
</script>
@@ -29,8 +29,8 @@
<el-collapse-item :title="$t('trials:readingUnit:readingCriterion')" name="2">
<ReadingCriterions :ref="`readingCriterions${item.TrialReadingCriterionId}`"
:trial-reading-criterion-id="TrialReadingCriterionId" :digit-places="digitPlaces"
:readingTools="readingTools" :is-additional-assessment="isAdditionalAssessment"
@reloadArbitrationRules="reloadArbitrationRules" />
:readingTools="readingTools" :readingSegmentTools="readingSegmentTools"
:is-additional-assessment="isAdditionalAssessment" @reloadArbitrationRules="reloadArbitrationRules" />
</el-collapse-item>
<!-- 全局阅片 -->
<el-collapse-item v-if="isGlobalReading && CriterionType !== 10"
@@ -107,7 +107,7 @@ import GlobalReading from "./components/GlobalReading";
import OncologyForm from "./components/OncologyForm";
import SignForm from "@/views/trials/components/newSignForm";
import const_ from "@/const/sign-code";
import { getCustomizeStandardsTools, getCustomizeStandardsNoneDicomTools } from '@/views/trials/trials-panel/reading/dicoms3D/components/toolConfig'
import { getCustomizeStandardsTools, getCustomizeStandardsNoneDicomTools, getCustomizeStandardsSegmentDicomTools } from '@/views/trials/trials-panel/reading/dicoms3D/components/toolConfig'
export default {
name: "ReadingUnit",
components: {
@@ -141,7 +141,8 @@ export default {
isGlobalReading: false,
digitPlaces: 0,
isAdditionalAssessment: false,
readingTools: []
readingTools: [],
readingSegmentTools: []
};
},
watch: {
@@ -230,13 +231,16 @@ export default {
this.digitPlaces = digitPlaces;
},
setReadingTools(data) {
let { readingTools, isNoneDicom } = data
if (isNoneDicom) {
let { readingTools, readingSegmentTools, ReadingTool } = data
if (ReadingTool === 1) {
this.readingTools = getCustomizeStandardsNoneDicomTools(readingTools)
} else {
this.readingTools = getCustomizeStandardsTools(readingTools)
}
if (ReadingTool === 3) {
this.readingSegmentTools = getCustomizeStandardsSegmentDicomTools(readingSegmentTools)
}
console.log(this.readingSegmentTools, 'this.readingSegmentTools')
},
setIsClinicalReading(isClinicalReading) {
this.isClinicalReading = isClinicalReading;
@@ -689,7 +689,7 @@ export default {
</script>
<style lang="scss" scoped>
.reading-period {
::v-deep.search {
::v-deep .search {
padding: 0px !important;
}
}
@@ -71,7 +71,6 @@ export default {
'#505372',
'#ff994d',
'#fb628b',
],
trialCriterionList: [],
loading_left: false,
@@ -435,7 +435,7 @@ export default {
}
}
.menuSwitch {
::v-deep.el-switch__core {
::v-deep .el-switch__core {
height: 14px;
width: 28px !important;
&::after {
@@ -446,7 +446,7 @@ export default {
}
}
.el-switch.is-checked {
::v-deep.el-switch__core {
::v-deep .el-switch__core {
height: 14px;
width: 28px !important;
&::after {
@@ -891,12 +891,12 @@ export default {
var studyUid = data.string('x0020000d')
if (!studyUid) return resolve()
var pixelDataElement = data.elements.x7fe00010
if (!pixelDataElement && modality !== 'SR') return resolve()
if (!pixelDataElement && modality !== 'SR' && modality !== 'ECG') return resolve()
var studyIndex = 0
while (
studyIndex < scope.uploadQueues.length &&
scope.uploadQueues[studyIndex].dicomInfo.studyUid !== studyUid &&
(pixelDataElement || modality === 'SR') &&
(pixelDataElement || modality === 'SR' || modality === 'ECG') &&
modality != ''
) {
++studyIndex
+3 -3
View File
@@ -53,7 +53,7 @@ module.exports = defineConfig({
'/api': {
target: 'http://106.14.89.110:30000',
// target: 'http://101.132.253.119:7010', // uat
// target: 'http://101.132.253.119:7010', // uat
changeOrigin: true,
secure: false,
pathRewrite: {
@@ -99,7 +99,7 @@ module.exports = defineConfig({
]
}),
// new BundleAnalyzerPlugin(),
process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'production' || process.env.VUE_APP_OSS_CONFIG_BUCKET === 'zyypacs-usa' ? function () { }
process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'production' || process.env.VUE_APP_OSS_CONFIG_BUCKET === 'zyypacs-usa' ? function() { }
: new WebpackAliyunOss({
from: ['./dist/**'],
dist: process.env.VUE_APP_OSS_PATH + distDate,
@@ -186,7 +186,7 @@ module.exports = defineConfig({
// 生成文件的最大体积
maxAssetSize: 3000000000,
// 只给出js的性能提示
assetFilter: function (assetFileName) {
assetFilter: function(assetFileName) {
return assetFileName.endsWith('.js')
}
}