Compare commits
399 Commits
18a71725f9
...
46aa64365f
| Author | SHA1 | Date |
|---|---|---|
|
|
46aa64365f | |
|
|
d2fae40100 | |
|
|
4030b1c95b | |
|
|
d689c23f5a | |
|
|
4a7f8a0a93 | |
|
|
7daa7fda0c | |
|
|
b25426711a | |
|
|
f06f7827cf | |
|
|
3ea46b5d1e | |
|
|
72634c785d | |
|
|
2dbf6a1fb3 | |
|
|
755f305389 | |
|
|
29a6870fb5 | |
|
|
2d99e074b8 | |
|
|
6e6e2d11d0 | |
|
|
6b8711232e | |
|
|
eb0b9d6fb8 | |
|
|
7ef1cafeff | |
|
|
3ac3e41d0d | |
|
|
1763e49650 | |
|
|
016a5e93ea | |
|
|
e4d731222b | |
|
|
aeeba1dfb7 | |
|
|
d7ebed0b05 | |
|
|
312e85d9b3 | |
|
|
3ee32c2e56 | |
|
|
2778b34f14 | |
|
|
6736121309 | |
|
|
e48c00f817 | |
|
|
e5f7ad3b31 | |
|
|
b7a0a336dd | |
|
|
d83a086087 | |
|
|
d03ce19b02 | |
|
|
a2668b39be | |
|
|
a30fd58d79 | |
|
|
31d0872653 | |
|
|
e63e38222e | |
|
|
65d7a95d5f | |
|
|
298619717a | |
|
|
d6713ba9a4 | |
|
|
0e252b0377 | |
|
|
52ab454860 | |
|
|
0e2e57446b | |
|
|
94b0354fe4 | |
|
|
93ed9943fa | |
|
|
e04e94a792 | |
|
|
b7c57c3c27 | |
|
|
015eb78b70 | |
|
|
04580ed1bb | |
|
|
b6877d4fe8 | |
|
|
b53f566160 | |
|
|
c767004a2d | |
|
|
3a546e6e89 | |
|
|
6af2f0555e | |
|
|
a82526e21d | |
|
|
c65ad92d39 | |
|
|
36d5661044 | |
|
|
feb94dc832 | |
|
|
756750783c | |
|
|
db3f7b4cde | |
|
|
a9d62cf28d | |
|
|
04014ab524 | |
|
|
2a68b40875 | |
|
|
375a7e7071 | |
|
|
efc3b03392 | |
|
|
730a375509 | |
|
|
d34b4b796c | |
|
|
427f060935 | |
|
|
6a10cb1afe | |
|
|
962ac2e693 | |
|
|
3567f2d625 | |
|
|
bd980cd867 | |
|
|
284432ffa4 | |
|
|
16e2fbc8ff | |
|
|
ac6be424ee | |
|
|
351b2a627f | |
|
|
dd919f123e | |
|
|
3a33abf02e | |
|
|
917373e081 | |
|
|
632463327c | |
|
|
59a14c0e37 | |
|
|
511640f66f | |
|
|
c26f5df102 | |
|
|
98e9af71a3 | |
|
|
b59e0c12f6 | |
|
|
fed2677689 | |
|
|
a55278e323 | |
|
|
8f808d73b5 | |
|
|
717c5a6529 | |
|
|
ec676d8d2b | |
|
|
2ce14c6d72 | |
|
|
10b5bba0dd | |
|
|
159a36cd70 | |
|
|
4a17946598 | |
|
|
d6f36cbbda | |
|
|
e6a15e35f6 | |
|
|
27a9ccd3c2 | |
|
|
e8dda21459 | |
|
|
7ebbfb9621 | |
|
|
9da7c1a159 | |
|
|
197f17fc99 | |
|
|
d680c57482 | |
|
|
7266d0a4f5 | |
|
|
3e19e85a8d | |
|
|
c9057e12cd | |
|
|
10bac01371 | |
|
|
67d54aedb7 | |
|
|
1a21aa38f3 | |
|
|
64f0998896 | |
|
|
04d9a53a0e | |
|
|
fd0412004e | |
|
|
64ef1e513c | |
|
|
a83349387a | |
|
|
9500842628 | |
|
|
998e340919 | |
|
|
753127381f | |
|
|
a5c3dac047 | |
|
|
a7337a347b | |
|
|
85b88d4564 | |
|
|
763e00a6ca | |
|
|
6a345e35cd | |
|
|
09e9c62f3c | |
|
|
af97e2b4ed | |
|
|
e0dd443d84 | |
|
|
1c0cdde9ad | |
|
|
e100d45bc1 | |
|
|
49c7482d76 | |
|
|
22b77ff70f | |
|
|
3a35f4a966 | |
|
|
8ff9e3b232 | |
|
|
e189080e5c | |
|
|
07b1e5d1f9 | |
|
|
2f92bf6dfb | |
|
|
391e696d8a | |
|
|
9ff7928b16 | |
|
|
3c0971e961 | |
|
|
1f20834dd3 | |
|
|
f34665fcb9 | |
|
|
00e03c782d | |
|
|
5e6c8e6936 | |
|
|
cd2c56c510 | |
|
|
ec7cb78018 | |
|
|
6a65b6507f | |
|
|
d08df877d7 | |
|
|
10a43d4c04 | |
|
|
a87769a60f | |
|
|
4d808ed87c | |
|
|
0a3a4e3bb4 | |
|
|
0aab18924b | |
|
|
2bf9b21f40 | |
|
|
06d0ce2290 | |
|
|
1b6e53819c | |
|
|
a59509dd7d | |
|
|
d88f7c43b2 | |
|
|
c3543e19c8 | |
|
|
72b1a3906d | |
|
|
06794447ec | |
|
|
00c5570692 | |
|
|
cd2df6e576 | |
|
|
52f9c17234 | |
|
|
0b78561331 | |
|
|
c12da4de3e | |
|
|
4f16060a46 | |
|
|
212bd65bc1 | |
|
|
afc35daffa | |
|
|
5bf8e7ecf6 | |
|
|
192f7a3316 | |
|
|
d88535b75e | |
|
|
128cdc0f75 | |
|
|
4a8b1da094 | |
|
|
728f3238ca | |
|
|
e245057bb2 | |
|
|
7bf80c10eb | |
|
|
ecf4c8a2c7 | |
|
|
c532babf98 | |
|
|
2fa54eaac6 | |
|
|
50de867772 | |
|
|
24a48e3d23 | |
|
|
13429f45ed | |
|
|
8545e8f1d6 | |
|
|
619a7b2ee3 | |
|
|
544f83d118 | |
|
|
1541e3d353 | |
|
|
7a454d09ff | |
|
|
06f52f4f24 | |
|
|
877e47ccb1 | |
|
|
c373b59f23 | |
|
|
73e8f24c3d | |
|
|
1e57777d3b | |
|
|
aca7f07d00 | |
|
|
35e52fbdcf | |
|
|
aaeadbc440 | |
|
|
60d3dfa6e7 | |
|
|
417fefb04b | |
|
|
27646e2c46 | |
|
|
48b154bf87 | |
|
|
acfa552cbe | |
|
|
aa6a226ce1 | |
|
|
09144f2b98 | |
|
|
9b8c6788bf | |
|
|
4aa1a05ea9 | |
|
|
45790141df | |
|
|
6804d909b8 | |
|
|
1a1bf6810c | |
|
|
c9e7719c71 | |
|
|
07b23fce1d | |
|
|
9f5cbad22e | |
|
|
3cf4f95770 | |
|
|
5a01f10957 | |
|
|
4ed0ebc913 | |
|
|
9971c3ecca | |
|
|
3c10ae73dd | |
|
|
5cbd13cc3d | |
|
|
7dcc524bd3 | |
|
|
bc4639a238 | |
|
|
0f25958d9b | |
|
|
ff0d204612 | |
|
|
91386589ae | |
|
|
b2a9f279a1 | |
|
|
b4fc5d970f | |
|
|
e046bffdbd | |
|
|
38ea9ddb0b | |
|
|
1194e56139 | |
|
|
741c723bab | |
|
|
fea0047f58 | |
|
|
5a943fbefe | |
|
|
6c88af0a03 | |
|
|
26412931a9 | |
|
|
5a1b64a023 | |
|
|
64d8c853b3 | |
|
|
0261fb04c6 | |
|
|
ab372a9c29 | |
|
|
f7dd68f7f3 | |
|
|
946203f124 | |
|
|
f0eb814492 | |
|
|
6c36c48b82 | |
|
|
47b05526a2 | |
|
|
f7afca6e73 | |
|
|
04aa61658c | |
|
|
86c506bc1d | |
|
|
06959b4465 | |
|
|
838ab975cc | |
|
|
7aa33672a9 | |
|
|
81297954d3 | |
|
|
25e348f428 | |
|
|
c101302cca | |
|
|
c4fd8c3ea3 | |
|
|
a7e7419a20 | |
|
|
55da1d9219 | |
|
|
2a842fd2f8 | |
|
|
ca2e9e0253 | |
|
|
b4c06990f6 | |
|
|
599fad48ba | |
|
|
72b5d515ba | |
|
|
608bd42836 | |
|
|
040f66f182 | |
|
|
7afbc360e5 | |
|
|
74c5d032fb | |
|
|
43aa371c36 | |
|
|
398ec90fae | |
|
|
890910a763 | |
|
|
3b5ebfcba1 | |
|
|
b971235164 | |
|
|
e53a18f212 | |
|
|
774f43a6d0 | |
|
|
88f274ab89 | |
|
|
3345a989ae | |
|
|
11000ef9f8 | |
|
|
57be74aea8 | |
|
|
8724eeeafb | |
|
|
7c71a6d342 | |
|
|
cb04be1b56 | |
|
|
63709b414d | |
|
|
7001bc9ec0 | |
|
|
c12833d283 | |
|
|
22baa213ae | |
|
|
09c1c3ff12 | |
|
|
94f43f4b11 | |
|
|
1a42cfd47c | |
|
|
b6b918efbf | |
|
|
a16d86d942 | |
|
|
266698cc79 | |
|
|
eac6b25b9e | |
|
|
47d7fb00e2 | |
|
|
312ab754e1 | |
|
|
0ff5a21d04 | |
|
|
5a7fa1fb77 | |
|
|
5289c156c0 | |
|
|
a68c27fd43 | |
|
|
641c2e6ade | |
|
|
5b4cc097bc | |
|
|
b1de662677 | |
|
|
d2d1063ed5 | |
|
|
2bc4b32d3a | |
|
|
366ec62cd7 | |
|
|
866e2e5c6b | |
|
|
16e2db37cd | |
|
|
ee6c5bf8a5 | |
|
|
48cfe99f12 | |
|
|
f65264a2a6 | |
|
|
769c5f8440 | |
|
|
6321bddfd3 | |
|
|
e30fb2a041 | |
|
|
478adcde21 | |
|
|
34ce5142f2 | |
|
|
3b993bcd46 | |
|
|
3f2e39f46c | |
|
|
d317402f85 | |
|
|
6cb63bdc59 | |
|
|
8db641db16 | |
|
|
9da9515930 | |
|
|
2be119de6b | |
|
|
c9f9615488 | |
|
|
be137b3840 | |
|
|
d80101f86b | |
|
|
3c0b084f7e | |
|
|
8a88a64c36 | |
|
|
3d538d6e67 | |
|
|
67d2a83ec8 | |
|
|
2efb229c96 | |
|
|
c930480336 | |
|
|
b39ea3a032 | |
|
|
ff5aea17a5 | |
|
|
477b6935ff | |
|
|
8b6432ad14 | |
|
|
ca35fe6df4 | |
|
|
307c02725f | |
|
|
72d0494baa | |
|
|
d1017bc565 | |
|
|
76e6ef087f | |
|
|
c5c9e4364c | |
|
|
920d0b21c6 | |
|
|
3fe16b4166 | |
|
|
bc2d5463ff | |
|
|
c63811d55c | |
|
|
832fa82d4b | |
|
|
b3ae801fe9 | |
|
|
0dbd74270d | |
|
|
aae5968d74 | |
|
|
79a7ad0cf7 | |
|
|
a35309d9b5 | |
|
|
a987ac086b | |
|
|
fdc2bc5f8a | |
|
|
84f68c79f0 | |
|
|
b77dc34523 | |
|
|
91554e0e30 | |
|
|
2efb7fd36a | |
|
|
dbdec93efc | |
|
|
e631ce73f2 | |
|
|
5931dcdc67 | |
|
|
b55ffc0821 | |
|
|
8600054405 | |
|
|
9a88539294 | |
|
|
09211c8841 | |
|
|
2724e0267f | |
|
|
a0e355d69b | |
|
|
04a0a1ccc3 | |
|
|
c3e967acaa | |
|
|
740a0aa221 | |
|
|
e8f95d09ba | |
|
|
2c305af49e | |
|
|
5c76bda1e7 | |
|
|
0273601f7b | |
|
|
c3a154f762 | |
|
|
cdb97f6405 | |
|
|
4794429a57 | |
|
|
77a633d699 | |
|
|
64d1ebf350 | |
|
|
5336cb5353 | |
|
|
0a288b9f47 | |
|
|
674d483cbf | |
|
|
cbcb396441 | |
|
|
9a3e972723 | |
|
|
b593615e9e | |
|
|
d30c9a5f3b | |
|
|
dd3d738eed | |
|
|
d9edaf4c27 | |
|
|
a5e0dd89ff | |
|
|
73f8f816b7 | |
|
|
19d5144b70 | |
|
|
e0ecd3f4ac | |
|
|
30e0e374bc | |
|
|
25b909c660 | |
|
|
5cb3e8e606 | |
|
|
619af2e874 | |
|
|
43f1736b05 | |
|
|
c9be5678bd | |
|
|
4e5ecb3938 | |
|
|
7870c3d74b | |
|
|
d07adc4948 | |
|
|
266886fa64 | |
|
|
e012eba0c2 | |
|
|
75d296405b | |
|
|
07c92c48a4 | |
|
|
b5427dd17b | |
|
|
385643643e | |
|
|
756f1293aa | |
|
|
eee19f0ae9 | |
|
|
78c0910db7 |
|
|
@ -1,7 +1,7 @@
|
|||
|
||||
kind: pipeline
|
||||
type: ssh
|
||||
name: ssh-linux-test-irc-publish
|
||||
name: ssh-linux-uat-irc-publish
|
||||
|
||||
platform:
|
||||
os: Linux
|
||||
|
|
@ -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
|
||||
|
||||
|
||||
25
package.json
|
|
@ -9,24 +9,25 @@
|
|||
"build:usa": "vue-cli-service build --mode usa",
|
||||
"build:usa_prod": "vue-cli-service build --mode usa_prod",
|
||||
"pre": "vue-cli-service build --mode pre",
|
||||
"postinstall": "patch-package",
|
||||
"i18n": "npm run i18n:zh && npm run i18n:en",
|
||||
"i18n:zh": "node i18nGenerate.js lang=zh keyCol=5 valCol=6",
|
||||
"i18n:en": "node i18nGenerate.js lang=en keyCol=5 valCol=7"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.701.0",
|
||||
"@cornerstonejs/adapters": "^2.19.7",
|
||||
"@aws-sdk/client-s3": "3.726.1",
|
||||
"@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/polymorphic-segmentation": "^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",
|
||||
|
|
@ -38,7 +39,8 @@
|
|||
"dcmjs": "^0.29.8",
|
||||
"dicom-parser": "^1.8.9",
|
||||
"dicomedit": "^0.1.0",
|
||||
"echarts": "^4.8.0",
|
||||
"echarts": "^6.0.0",
|
||||
"echarts-map": "^3.0.1",
|
||||
"element-ui": "^2.15.14",
|
||||
"exceljs": "^4.4.0",
|
||||
"file-saver": "^2.0.5",
|
||||
|
|
@ -60,11 +62,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",
|
||||
|
|
@ -95,12 +96,14 @@
|
|||
"html-webpack-plugin": "^5.6.3",
|
||||
"mini-css-extract-plugin": "^2.9.2",
|
||||
"node-polyfill-webpack-plugin": "^4.0.0",
|
||||
"patch-package": "^8.0.0",
|
||||
"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"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
diff --git a/node_modules/@cornerstonejs/dicom-image-loader/dist/esm/imageLoader/isColorConversionRequired.js b/node_modules/@cornerstonejs/dicom-image-loader/dist/esm/imageLoader/isColorConversionRequired.js
|
||||
index 33469a3..f39736f 100644
|
||||
--- a/node_modules/@cornerstonejs/dicom-image-loader/dist/esm/imageLoader/isColorConversionRequired.js
|
||||
+++ b/node_modules/@cornerstonejs/dicom-image-loader/dist/esm/imageLoader/isColorConversionRequired.js
|
||||
@@ -14,10 +14,7 @@ export default function isColorConversionRequired(imageFrame) {
|
||||
(3 * Math.ceil(columns / 2) + Math.floor(columns / 2)) * rows);
|
||||
}
|
||||
else if (photometricInterpretation.endsWith('422')) {
|
||||
- return (pixelDataLength ===
|
||||
- (3 * Math.ceil(columns / 2) + Math.floor(columns / 2)) *
|
||||
- Math.ceil(rows / 2) +
|
||||
- Math.floor(rows / 2) * columns);
|
||||
+ return pixelDataLength === rows * columns * 2;
|
||||
}
|
||||
else {
|
||||
return photometricInterpretation !== 'RGB' || planarConfiguration === 1;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
window.config = {
|
||||
timeZone: false
|
||||
}
|
||||
|
|
@ -67,6 +67,7 @@
|
|||
num = e.data
|
||||
}
|
||||
</script>
|
||||
<script src="/config.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
|
|
|
|||
66
src/App.vue
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div id="app" style="position: relative">
|
||||
<div id="app" style="position: relative" :class="{ mask: isLock }">
|
||||
<router-view />
|
||||
<div v-show="show" v-if="$route.matched.length > 0" v-adaptive @click="openI18n" style="
|
||||
position: fixed;
|
||||
|
|
@ -63,7 +63,7 @@
|
|||
" size="mini"></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="ValueCN" :label="$t('il8n:table:state')" sortable="custom">
|
||||
<el-table-column prop="State" :label="$t('il8n:table:state')" sortable="custom">
|
||||
<template slot-scope="scope">
|
||||
<el-select v-model="scope.row.State" clearable filterable size="mini">
|
||||
<el-option v-for="item of $d.InternationalizationKeyState"
|
||||
|
|
@ -83,7 +83,7 @@
|
|||
</div>
|
||||
</el-drawer>
|
||||
<feedBack v-if="$route.matched.length > 0" />
|
||||
<!-- <timeTag /> -->
|
||||
<timeTag v-if="config.timeZone" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -94,14 +94,15 @@ import {
|
|||
} from '@/api/dictionary/dictionary'
|
||||
import { getTrialExtralConfig } from '@/api/trials'
|
||||
import feedBack from '@/views/trials/trials-layout/components/feedBack'
|
||||
// import timeTag from '@/components/timeTag'
|
||||
import timeTag from '@/components/timeTag'
|
||||
import Vue from 'vue'
|
||||
import i18n from './lang'
|
||||
import DicomEvent from '@/views/trials/trials-panel/reading/dicoms/components/DicomEvent'
|
||||
export default {
|
||||
name: 'App',
|
||||
components: {
|
||||
feedBack,
|
||||
// timeTag
|
||||
timeTag
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -112,12 +113,17 @@ export default {
|
|||
arr: [],
|
||||
il8nExternal: false,
|
||||
State: null,
|
||||
config: window.config,
|
||||
isLock: false
|
||||
}
|
||||
},
|
||||
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
|
||||
// this.getIsLock()
|
||||
// DicomEvent.$on("isLock", (isLock) => {
|
||||
// this.isLock = isLock
|
||||
// })
|
||||
},
|
||||
// watch: {
|
||||
// '$route.query': {
|
||||
|
|
@ -149,17 +155,30 @@ export default {
|
|||
// },
|
||||
// },
|
||||
methods: {
|
||||
getIsLock() {
|
||||
if (zzSessionStorage.getItem('isLock') === 'true') {
|
||||
this.isLock = true
|
||||
} else {
|
||||
this.isLock = false
|
||||
}
|
||||
},
|
||||
// 排序
|
||||
handleSortByColumn(column) {
|
||||
if (column.order === 'ascending') {
|
||||
this.tableData.sort((a, b) =>
|
||||
a[column.prop].localeCompare(b[column.prop])
|
||||
)
|
||||
} else {
|
||||
this.tableData.sort((a, b) =>
|
||||
b[column.prop].localeCompare(a[column.prop])
|
||||
)
|
||||
const { prop, order } = column
|
||||
if (!prop || !order) {
|
||||
this.tableData = [...this.tableData]
|
||||
return
|
||||
}
|
||||
|
||||
const collator = new Intl.Collator('zh-Hans-CN', {
|
||||
numeric: true,
|
||||
sensitivity: 'base',
|
||||
})
|
||||
|
||||
this.tableData.sort((a, b) => {
|
||||
const result = collator.compare(a[prop], b[prop])
|
||||
return order === 'ascending' ? result : -result
|
||||
})
|
||||
},
|
||||
handleStateChange() {
|
||||
this.tableData.forEach((item) => {
|
||||
|
|
@ -301,6 +320,23 @@ export default {
|
|||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.mask {
|
||||
position: relative;
|
||||
filter: blur(2px);
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.mask::after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
content: '';
|
||||
display: block;
|
||||
background: rgba(255, 253, 253, 0.2);
|
||||
}
|
||||
|
||||
$light_gray: #606266;
|
||||
|
||||
.el-tooltip__popper {
|
||||
|
|
|
|||
|
|
@ -391,3 +391,35 @@ export function getReSendEmail(data) {
|
|||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 获取系统基础配置
|
||||
export function getSystemBasicConfigInfo() {
|
||||
return request({
|
||||
url: `/DeployConfig/getSystemBasicConfigInfo`,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
// 更新系统基础配置
|
||||
export function updateSystemBasicConfig(data) {
|
||||
return request({
|
||||
url: `/DeployConfig/updateSystemBasicConfig`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
// 获取系统邮件配置
|
||||
export function getEmailConfigInfo() {
|
||||
return request({
|
||||
url: `/DeployConfig/getEmailConfigInfo`,
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
// 更新系统邮件配置
|
||||
export function updateSystemEmailConfig(data) {
|
||||
return request({
|
||||
url: `/DeployConfig/updateSystemEmailConfig`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
import request from '@/utils/request'
|
||||
|
||||
export function addOrUpdateFileUploadRecord(params) {
|
||||
return request({
|
||||
url: `/FileUploadRecord/addOrUpdateFileUploadRecord`,
|
||||
method: 'post',
|
||||
data: params
|
||||
})
|
||||
}
|
||||
|
||||
export function batchAddSyncFileTask(params) {
|
||||
return request({
|
||||
url: `/FileUploadRecord/batchAddSyncFileTask`,
|
||||
method: 'post',
|
||||
data: params
|
||||
})
|
||||
}
|
||||
|
||||
export function getSubjectUploadRecordList(params) {
|
||||
return request({
|
||||
url: `/FileUploadRecord/getSubjectUploadRecordList`,
|
||||
method: 'post',
|
||||
data: params
|
||||
})
|
||||
}
|
||||
|
||||
export function getFileUploadRecordList(params) {
|
||||
return request({
|
||||
url: `/FileUploadRecord/getFileUploadRecordList`,
|
||||
method: 'post',
|
||||
data: params
|
||||
})
|
||||
}
|
||||
|
||||
export function getUploadFileSyncRecordList(params) {
|
||||
return request({
|
||||
url: `/FileUploadRecord/getUploadFileSyncRecordList`,
|
||||
method: 'post',
|
||||
data: params
|
||||
})
|
||||
}
|
||||
|
|
@ -319,3 +319,132 @@ 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
|
||||
})
|
||||
}
|
||||
// 修改分割组保存状态
|
||||
export function changeSegmentationSavedStatus(data) {
|
||||
return request({
|
||||
url: `/Segmentation/changeSegmentationSavedStatus`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
// 修改分割片段锁定状态
|
||||
export function lockOrUnLockSegment(data) {
|
||||
return request({
|
||||
url: `/Segmentation/lockOrUnLockSegment`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
// 图像数据匿名
|
||||
export function studyMaskImage(data) {
|
||||
return request({
|
||||
url: `/Study/studyMaskImage`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
// 撤销匿名
|
||||
export function studyUndoMaskImage(data) {
|
||||
return request({
|
||||
url: `/Study/studyUndoMaskImage`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
// 获取分割组历史版本
|
||||
export function getSegmentationVersionList(data) {
|
||||
return request({
|
||||
url: `/Segmentation/getSegmentationVersionList`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
// 恢复分割历史版本
|
||||
export function restoreSegmentationVersion(data) {
|
||||
return request({
|
||||
url: `/Segmentation/restoreSegmentationVersion`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
|
@ -4431,3 +4431,47 @@ export function updateImageResizePath(data) {
|
|||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 获取PET图像上患者信息
|
||||
export function getPatientInfo(data) {
|
||||
return request({
|
||||
url: `/Study/getPatientInfo`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
//编辑患者基本信息
|
||||
export function editPatientInfo(data) {
|
||||
return request({
|
||||
url: `/Study/editPatientInfo`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
//修正患者基本信息
|
||||
export function amendmentPatientInfo(data) {
|
||||
return request({
|
||||
url: `/Inspection/Study/amendmentPatientInfo`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
//修改临床数据后 将签名状态变更为未签名
|
||||
export function updateReadModuleClinicalData(data) {
|
||||
return request({
|
||||
url: `/ReadingClinicalData/updateReadModuleClinicalData`,
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export function getTrialUnreadVisitList(params) {
|
||||
return request({
|
||||
url: `/DownloadAndUpload/getTrialUnreadVisitList`,
|
||||
method: 'get',
|
||||
params,
|
||||
})
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
<div>Series #{{ dicomInfo.series }}</div>
|
||||
<div>Image #{{ dicomInfo.frame }}</div>
|
||||
<div>{{ dicomInfo.modality }}</div>
|
||||
<div v-if="isComparison" style="font-size: 30px;color: #428bca;">{{ tip }}</div>
|
||||
</div>
|
||||
<div v-show="dicomInfo.series" class="info-image">
|
||||
<!-- <div>
|
||||
|
|
@ -23,17 +24,11 @@
|
|||
</div>
|
||||
<div
|
||||
v-if="(dicomInfo.modality === 'CT' || dicomInfo.modality === 'DR' || dicomInfo.modality === 'CR') && mousePosition.mo">
|
||||
HU: {{ mousePosition.mo }}
|
||||
</div>
|
||||
<div v-else-if="(dicomInfo.modality === 'PT' && mousePosition.suv)">
|
||||
SUVbw(g/ml): {{ mousePosition.suv.toFixed(3) }}
|
||||
</div>
|
||||
<div v-else-if="mousePosition.mo">
|
||||
Density: {{ mousePosition.mo }}
|
||||
</div>
|
||||
<div>
|
||||
W*H: {{ dicomInfo.size }}
|
||||
</div>
|
||||
HU: {{ mousePosition.mo }}</div>
|
||||
<div v-else-if="(dicomInfo.modality === 'PT' && mousePosition.suv)">SUVbw(g/ml): {{ mousePosition.suv.toFixed(3)
|
||||
}}</div>
|
||||
<div v-else-if="mousePosition.mo">Density: {{ mousePosition.mo }}</div>
|
||||
<div>W*H: {{ dicomInfo.size }}</div>
|
||||
|
||||
<div>Zoom: {{ dicomInfo.zoom }}</div>
|
||||
</div>
|
||||
|
|
@ -56,21 +51,14 @@
|
|||
style="z-index:10;background: #9e9e9e;height: 20px;width: 100%;position: absolute;top: 0;cursor: move"
|
||||
@mousedown="sliderMousedown($event)" />
|
||||
</div>
|
||||
<div style="position: absolute;left: 50%;top: 15px;color: #f44336;">
|
||||
{{ markers.top }}
|
||||
</div>
|
||||
<div style="position: absolute;top: 50%;right: 15px;color: #f44336;">
|
||||
{{ markers.right }}
|
||||
</div>
|
||||
<div style="position: absolute;left: 50%;top: 15px;color: #f44336;">{{ markers.top }}</div>
|
||||
<div style="position: absolute;top: 50%;right: 15px;color: #f44336;">{{ markers.right }}</div>
|
||||
|
||||
<div style="position: absolute;left: 50%;bottom: 15px;color: #f44336;">
|
||||
{{ markers.bottom }}
|
||||
</div>
|
||||
<div style="position: absolute;top: 50%;left: 15px;color: #f44336;">
|
||||
{{ markers.left }}
|
||||
</div>
|
||||
<div style="position: absolute;left: 50%;bottom: 15px;color: #f44336;">{{ markers.bottom }}</div>
|
||||
<div style="position: absolute;top: 50%;left: 15px;color: #f44336;">{{ markers.left }}</div>
|
||||
|
||||
<div class="info-instance">
|
||||
<svg-icon icon-class="IsMasked" style="font-size:20px;" v-show="dicomInfo.IsMasked" />
|
||||
<!-- <div v-show="dicomInfo.pixel">
|
||||
Pixel: {{ dicomInfo.pixel }}mm
|
||||
</div>-->
|
||||
|
|
@ -92,15 +80,18 @@
|
|||
import * as cornerstone from 'cornerstone-core'
|
||||
import * as cornerstoneMath from 'cornerstone-math'
|
||||
import * as cornerstoneTools from 'cornerstone-tools'
|
||||
import metaDataProvider from '@/utils/metaDataProvider'
|
||||
|
||||
const scroll = cornerstoneTools.import('util/scrollToIndex')
|
||||
import Hammer from 'hammerjs'
|
||||
import getOrientationString from '@/views/trials/trials-panel/reading/dicoms/tools/OrientationMarkers/getOrientationString'
|
||||
import invertOrientationString from '@/views/trials/trials-panel/reading/dicoms/tools/OrientationMarkers/invertOrientationString'
|
||||
|
||||
import calculateSUV from '@/views/trials/trials-panel/reading/dicoms/tools/calculateSUV'
|
||||
const calculateSUV = cornerstoneTools.import('util/calculateSUV')
|
||||
// import requestPoolManager from '@/utils/request-pool'
|
||||
import ScaleOverlayTool from '@/views/trials/trials-panel/reading/dicoms/tools/ScaleOverlay/ScaleOverlayTool'
|
||||
import Note_RectangleRoiTool from '@/views/trials/trials-panel/reading/dicoms/tools/RectangleRoi/Note_RectangleRoiTool'
|
||||
let isMetaDataProviderAdded = false
|
||||
cornerstoneTools.external.cornerstone = cornerstone
|
||||
cornerstoneTools.external.Hammer = Hammer
|
||||
cornerstoneTools.external.cornerstoneMath = cornerstoneMath
|
||||
|
|
@ -110,6 +101,7 @@ cornerstoneTools.toolColors.setActiveColor('rgb(0, 255, 0)')
|
|||
// cornerstoneTools.init({ showSVGCursors: true })
|
||||
cornerstoneTools.init()
|
||||
const ToolStateManager = cornerstoneTools.globalImageIdSpecificToolStateManager
|
||||
console.log(cornerstoneTools, 'cornerstoneTools')
|
||||
import DicomTags from './DicomTags'
|
||||
export default {
|
||||
name: 'DicomCanvas',
|
||||
|
|
@ -117,6 +109,12 @@ export default {
|
|||
computed: {
|
||||
NSTip() {
|
||||
return `${this.$store.state.trials.downloadSize}, NS: ${this.$store.state.trials.downloadTip}`
|
||||
},
|
||||
},
|
||||
props: {
|
||||
isComparison: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
|
|
@ -130,7 +128,7 @@ export default {
|
|||
seriesNumber: '',
|
||||
imageIds: [],
|
||||
currentImageIdIndex: 0,
|
||||
firstImageLoading: false
|
||||
firstImageLoading: false,
|
||||
// preventCache: true
|
||||
},
|
||||
dicomInfo: {
|
||||
|
|
@ -150,14 +148,15 @@ export default {
|
|||
wwwc: '',
|
||||
zoom: 0,
|
||||
location: '',
|
||||
fps: 5
|
||||
fps: 5,
|
||||
IsMasked: false
|
||||
},
|
||||
toolState: {
|
||||
initialized: false,
|
||||
activeTool: 'none',
|
||||
dicomInfoVisible: false,
|
||||
clipPlaying: false,
|
||||
viewportInvert: false
|
||||
viewportInvert: false,
|
||||
},
|
||||
loadImagePromise: null,
|
||||
AnnotationSync: null,
|
||||
|
|
@ -168,18 +167,21 @@ export default {
|
|||
sliderInfo: {
|
||||
oldB: null,
|
||||
oldM: null,
|
||||
isMove: false
|
||||
isMove: false,
|
||||
},
|
||||
mousePosition: { x: '', y: '', mo: '' },
|
||||
markers: { top: '', right: '', bottom: '', left: '' },
|
||||
orientationMarkers: [],
|
||||
originalMarkers: [],
|
||||
dcmTag: { visible: false, title: this.$t('trials:dicom-tag:title') }
|
||||
dcmTag: { visible: false, title: this.$t('trials:dicom-tag:title') },
|
||||
tip: ''
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.type = this.$router.currentRoute.query.type ? this.$router.currentRoute.query.type : ''
|
||||
this.type = this.$router.currentRoute.query.type
|
||||
? this.$router.currentRoute.query.type
|
||||
: ''
|
||||
this.canvas = this.$refs.canvas
|
||||
this.canvas.addEventListener('cornerstonenewimage', this.onNewImage)
|
||||
this.canvas.addEventListener(
|
||||
|
|
@ -200,17 +202,28 @@ export default {
|
|||
document.addEventListener('mousemove', (e) => {
|
||||
this.sliderMousemove(e)
|
||||
})
|
||||
this.canvas.addEventListener('cornerstonetoolsstackscroll', this.stackScrollCallback)
|
||||
this.canvas.addEventListener(
|
||||
'cornerstonetoolsstackscroll',
|
||||
this.stackScrollCallback
|
||||
)
|
||||
},
|
||||
|
||||
methods: {
|
||||
loadImageStack(dicomSeries) {
|
||||
loadImageStack(dicomSeries, text = '') {
|
||||
this.tip = text
|
||||
this.$nextTick(() => {
|
||||
if (!dicomSeries || !Array.isArray(dicomSeries.imageIds) || dicomSeries.imageIds.length === 0) {
|
||||
return
|
||||
}
|
||||
this.series = dicomSeries
|
||||
this.stack.seriesId = dicomSeries.seriesId
|
||||
this.stack.seriesNumber = dicomSeries.seriesNumber
|
||||
this.stack.imageIds = dicomSeries.imageIds
|
||||
this.stack.currentImageIdIndex = dicomSeries.imageIdIndex && dicomSeries.imageIdIndex < dicomSeries.imageIds.length ? dicomSeries.imageIdIndex : 0
|
||||
this.stack.currentImageIdIndex =
|
||||
dicomSeries.imageIdIndex &&
|
||||
dicomSeries.imageIdIndex < dicomSeries.imageIds.length
|
||||
? dicomSeries.imageIdIndex
|
||||
: 0
|
||||
this.stack.firstImageLoading = true
|
||||
this.stack.description = dicomSeries.description
|
||||
this.toolState.viewportInvert = false
|
||||
|
|
@ -221,18 +234,27 @@ export default {
|
|||
// this.stack.instanceId = instanceId
|
||||
this.toolState.clipPlaying = false
|
||||
const element = this.$refs.canvas
|
||||
if (!isMetaDataProviderAdded) {
|
||||
// 注册自定义 metaDataProvider:统一 SUV 口径(优先接口,缺失回退 DICOM)
|
||||
cornerstone.metaData.addProvider(metaDataProvider, 100000)
|
||||
isMetaDataProviderAdded = true
|
||||
}
|
||||
cornerstone.enable(element)
|
||||
cornerstoneTools.stopClip(this.canvas)
|
||||
this.toolState.clipPlaying = false
|
||||
this.loading = true
|
||||
|
||||
cornerstone.loadAndCacheImage(this.stack.imageIds[this.stack.currentImageIdIndex])
|
||||
.then(image => {
|
||||
cornerstone
|
||||
.loadAndCacheImage(
|
||||
this.stack.imageIds[this.stack.currentImageIdIndex]
|
||||
)
|
||||
.then((image) => {
|
||||
this.loading = false
|
||||
if (this.stack.imageIds.indexOf(image.imageId) !== -1) {
|
||||
this.onFirstImageLoaded(image)
|
||||
}
|
||||
}).catch((error) => {
|
||||
})
|
||||
.catch((error) => {
|
||||
this.loading = false
|
||||
if (error.error && error.error.message) {
|
||||
this.$alert(error.error.message)
|
||||
|
|
@ -255,15 +277,17 @@ export default {
|
|||
const apiTool = cornerstoneTools[`${toolName}Tool`]
|
||||
|
||||
if (apiTool) {
|
||||
const toolAlreadyAddedToElement = cornerstoneTools.getToolForElement(
|
||||
element,
|
||||
apiTool
|
||||
)
|
||||
const toolAlreadyAddedToElement =
|
||||
cornerstoneTools.getToolForElement(element, apiTool)
|
||||
if (!toolAlreadyAddedToElement) {
|
||||
if (toolName === 'RectangleRoi') {
|
||||
cornerstoneTools.addToolForElement(element, apiTool, { configuration: { showMinMax: true, showStatsText: true } })
|
||||
cornerstoneTools.addToolForElement(element, apiTool, {
|
||||
configuration: { showMinMax: true, showStatsText: true },
|
||||
})
|
||||
} else if (toolName === 'EllipticalRoi') {
|
||||
cornerstoneTools.addToolForElement(element, apiTool, { configuration: { showMinMax: true } })
|
||||
cornerstoneTools.addToolForElement(element, apiTool, {
|
||||
configuration: { showMinMax: true },
|
||||
})
|
||||
} else {
|
||||
cornerstoneTools.addToolForElement(element, apiTool)
|
||||
}
|
||||
|
|
@ -287,8 +311,28 @@ export default {
|
|||
false
|
||||
)
|
||||
})
|
||||
if (!cornerstoneTools.getToolForElement(element, cornerstoneTools.WwwcRegionTool)) {
|
||||
cornerstoneTools.addToolForElement(element, cornerstoneTools.WwwcRegionTool)
|
||||
if (
|
||||
!cornerstoneTools.getToolForElement(element, Note_RectangleRoiTool)
|
||||
) {
|
||||
cornerstoneTools.addToolForElement(element, Note_RectangleRoiTool, {
|
||||
configuration: {
|
||||
color: '#f00',
|
||||
lineWidth: 2,
|
||||
drawHandles: false,
|
||||
fillColor: 'rgba(0, 0, 0, 1)',
|
||||
},
|
||||
})
|
||||
}
|
||||
if (
|
||||
!cornerstoneTools.getToolForElement(
|
||||
element,
|
||||
cornerstoneTools.WwwcRegionTool
|
||||
)
|
||||
) {
|
||||
cornerstoneTools.addToolForElement(
|
||||
element,
|
||||
cornerstoneTools.WwwcRegionTool
|
||||
)
|
||||
}
|
||||
if (
|
||||
!cornerstoneTools.getToolForElement(
|
||||
|
|
@ -347,20 +391,27 @@ export default {
|
|||
// var instanceId = image.imageId.split('/')[image.imageId.split('/').length - 1]
|
||||
// instanceId = instanceId.split('.')[0]
|
||||
// this.stack.instanceId = instanceId
|
||||
this.height = (this.stack.currentImageIdIndex) * 100 / (this.stack.imageIds.length - 1)
|
||||
this.height = this.getStackHeightPercent()
|
||||
this.resetWwwc()
|
||||
},
|
||||
onNewImage(e) {
|
||||
e.detail.enabledElement.options = {}
|
||||
var data = e.detail.image.data
|
||||
this.dicomInfo.hospital = data.string('x00080080')
|
||||
// let instanceInfo = this.series.instanceInfoList.find(item => item.ImageId === e.detail.image.imageId)
|
||||
let instanceInfo = this.series?.instanceInfoList?.find(item => {
|
||||
let s1 = item.ImageId ? item.ImageId.split("?")[0] : ''
|
||||
let s2 = e.detail.image.imageId ? e.detail.image.imageId.split("?")[0] : ''
|
||||
return s1 === s2
|
||||
})
|
||||
this.dicomInfo.IsMasked = instanceInfo ? instanceInfo.IsMasked : false
|
||||
// this.dicomInfo.pid = data.string('x00100020')
|
||||
this.dicomInfo.pid = data.string('x00120040')
|
||||
this.dicomInfo.name = data.string('x00100010')
|
||||
this.dicomInfo.age = data.string('x00101010')
|
||||
this.dicomInfo.sex = data.string('x00100040')
|
||||
this.dicomInfo.acc = data.string('x00080050') // 登记号
|
||||
this.dicomInfo.modality = data.string('x00080060')
|
||||
this.dicomInfo.modality = (data.string('x00080060') || '').trim()
|
||||
this.dicomInfo.time = this.formatDicomDateTime(
|
||||
data.string('x00080020'),
|
||||
data.string('x00080030')
|
||||
|
|
@ -379,21 +430,32 @@ export default {
|
|||
if (this.dicomInfo.thick) {
|
||||
this.dicomInfo.thick = this.dicomInfo.thick.toFixed(2)
|
||||
}
|
||||
const newImageIdIndex = this.stack.imageIds.findIndex(i => i === e.detail.image.imageId)
|
||||
const newImageIdIndex = this.stack.imageIds.findIndex(
|
||||
(i) => i === e.detail.image.imageId
|
||||
)
|
||||
if (newImageIdIndex === -1) return
|
||||
this.stack.currentImageIdIndex = newImageIdIndex
|
||||
this.stack.imageIdIndex = newImageIdIndex
|
||||
this.series.imageIdIndex = newImageIdIndex
|
||||
this.height = (this.stack.currentImageIdIndex) * 100 / (this.stack.imageIds.length - 1)
|
||||
this.resetWwwc()
|
||||
this.height = this.getStackHeightPercent()
|
||||
},
|
||||
getStackHeightPercent() {
|
||||
const imageCount = Array.isArray(this.stack.imageIds) ? this.stack.imageIds.length : 0
|
||||
if (imageCount <= 1) {
|
||||
return 0
|
||||
}
|
||||
return (this.stack.currentImageIdIndex * 100) / (imageCount - 1)
|
||||
},
|
||||
stackScrollCallback(e) {
|
||||
const { detail } = e
|
||||
if (this.isScrollSync) {
|
||||
this.$emit('scrollSync', { canvasIndex: this.canvasIndex, direction: detail.direction })
|
||||
this.$emit('scrollSync', {
|
||||
canvasIndex: this.canvasIndex,
|
||||
direction: detail.direction,
|
||||
})
|
||||
}
|
||||
this.stack.currentImageIdIndex = e.detail.newImageIdIndex
|
||||
this.height = (this.stack.currentImageIdIndex) * 100 / (this.stack.imageIds.length - 1)
|
||||
this.height = this.getStackHeightPercent()
|
||||
// var priority = new Date(new Date().setHours(23, 59, 59, 999)).getTime()
|
||||
|
||||
// requestPoolManager.loadAndCacheImagePlus(this.stack.imageIds[this.stack.currentImageIdIndex], this.stack.seriesId, priority)
|
||||
|
|
@ -408,7 +470,9 @@ export default {
|
|||
if (date) {
|
||||
date = `${date.substr(0, 4)}-${date.substr(4, 2)}-${date.substr(6, 2)}`
|
||||
}
|
||||
if (time) { time = `${time.substr(0, 2)}:${time.substr(2, 2)}:${time.substr(4, 2)}` }
|
||||
if (time) {
|
||||
time = `${time.substr(0, 2)}:${time.substr(2, 2)}:${time.substr(4, 2)}`
|
||||
}
|
||||
return time ? `${date} ${time}` : `${date} 00:00:00`
|
||||
},
|
||||
|
||||
|
|
@ -453,7 +517,7 @@ export default {
|
|||
top: oppositeColumn,
|
||||
bottom: column,
|
||||
left: oppositeRow,
|
||||
right: row
|
||||
right: row,
|
||||
}
|
||||
if (!markers) {
|
||||
return
|
||||
|
|
@ -500,13 +564,7 @@ export default {
|
|||
if (image.color) {
|
||||
stats.storedPixels = this.getRGBPixels(element, x, y, 1, 1)
|
||||
} else {
|
||||
stats.storedPixels = cornerstone.getStoredPixels(
|
||||
element,
|
||||
x,
|
||||
y,
|
||||
1,
|
||||
1
|
||||
)
|
||||
stats.storedPixels = cornerstone.getStoredPixels(element, x, y, 1, 1)
|
||||
stats.sp = stats.storedPixels[0]
|
||||
stats.mo = stats.sp * image.slope + image.intercept
|
||||
stats.suv = calculateSUV(image, stats.sp)
|
||||
|
|
@ -533,7 +591,8 @@ export default {
|
|||
if (enabledElement.image.color) {
|
||||
for (row = 0; row < height; row++) {
|
||||
for (column = 0; column < width; column++) {
|
||||
spIndex = ((row + y) * enabledElement.image.columns + (column + x)) * 4
|
||||
spIndex =
|
||||
((row + y) * enabledElement.image.columns + (column + x)) * 4
|
||||
const red = pixelData[spIndex]
|
||||
const green = pixelData[spIndex + 1]
|
||||
const blue = pixelData[spIndex + 2]
|
||||
|
|
@ -551,7 +610,8 @@ export default {
|
|||
},
|
||||
sliderMousedown(e) {
|
||||
var boxHeight = this.$refs['sliderBox'].clientHeight
|
||||
this.sliderInfo.oldB = parseInt(e.srcElement.style.top) * boxHeight / 100
|
||||
this.sliderInfo.oldB =
|
||||
(parseInt(e.srcElement.style.top) * boxHeight) / 100
|
||||
this.sliderInfo.oldM = e.clientY
|
||||
this.sliderInfo.isMove = true
|
||||
e.stopImmediatePropagation()
|
||||
|
|
@ -564,9 +624,14 @@ export default {
|
|||
var boxHeight = this.$refs['sliderBox'].clientHeight
|
||||
if (PX < 0) return
|
||||
if (PX > boxHeight) return
|
||||
var height = PX * 100 / boxHeight
|
||||
var index = Math.trunc(this.stack.imageIds.length * this.height / 100)
|
||||
index = index > this.stack.imageIds.length ? this.stack.imageIds.length : index < 0 ? 0 : index
|
||||
var height = (PX * 100) / boxHeight
|
||||
var index = Math.trunc((this.stack.imageIds.length * this.height) / 100)
|
||||
index =
|
||||
index > this.stack.imageIds.length
|
||||
? this.stack.imageIds.length
|
||||
: index < 0
|
||||
? 0
|
||||
: index
|
||||
// if (!cornerstone.imageCache.getImageLoadObject(this.stack.imageIds[index])) return
|
||||
this.height = height
|
||||
if (this.stack.currentImageIdIndex !== index) {
|
||||
|
|
@ -578,9 +643,9 @@ export default {
|
|||
},
|
||||
goViewer(e) {
|
||||
// console.log(this.$refs['sliderBox'].clientHeight)
|
||||
var height = e.offsetY * 100 / this.$refs['sliderBox'].clientHeight
|
||||
var height = (e.offsetY * 100) / this.$refs['sliderBox'].clientHeight
|
||||
this.height = height
|
||||
var index = Math.trunc(this.stack.imageIds.length * this.height / 100)
|
||||
var index = Math.trunc((this.stack.imageIds.length * this.height) / 100)
|
||||
scroll(this.canvas, index)
|
||||
},
|
||||
onClipStopped() {
|
||||
|
|
@ -673,10 +738,7 @@ export default {
|
|||
}
|
||||
this.toolState.clipPlaying = true
|
||||
cornerstoneTools.playClip(this.canvas, this.dicomInfo.fps)
|
||||
cornerstoneTools.getToolState(
|
||||
this.canvas,
|
||||
'playClip'
|
||||
).data[0].loop = true
|
||||
cornerstoneTools.getToolState(this.canvas, 'playClip').data[0].loop = true
|
||||
},
|
||||
setFps(fps) {
|
||||
this.dicomInfo.fps = fps
|
||||
|
|
@ -687,6 +749,7 @@ export default {
|
|||
var viewport = cornerstone.getViewport(this.canvas)
|
||||
// viewport.invert = false
|
||||
var image = cornerstone.getImage(this.canvas)
|
||||
if (!viewport || !image) return
|
||||
viewport.voi.windowWidth = image.windowWidth
|
||||
viewport.voi.windowCenter = image.windowCenter
|
||||
cornerstone.setViewport(this.canvas, viewport)
|
||||
|
|
@ -712,8 +775,8 @@ export default {
|
|||
invert: false,
|
||||
preventZoomOutsideImage: false,
|
||||
minScale: 0.1,
|
||||
maxScale: 20.0
|
||||
}
|
||||
maxScale: 20.0,
|
||||
},
|
||||
})
|
||||
cornerstoneTools.setToolActive('Zoom', { mouseButtonMask: 1 })
|
||||
this.toolState.activeTool = 'zoom'
|
||||
|
|
@ -797,9 +860,120 @@ export default {
|
|||
setToolPassive(toolName) {
|
||||
cornerstoneTools.setToolPassiveForElement(this.canvas, toolName)
|
||||
},
|
||||
async reloadImage(newImageId = null) {
|
||||
// 1. 获取当前imageId(如果未指定新imageId)
|
||||
let element = this.canvas
|
||||
this.stack.imageIds.splice(this.stack.currentImageIdIndex, 1, newImageId)
|
||||
const currentImageId =
|
||||
newImageId || cornerstone.getImage(element)?.imageId
|
||||
|
||||
if (!currentImageId) {
|
||||
console.error('没有找到可用的imageId')
|
||||
return
|
||||
}
|
||||
|
||||
// 2. 清除该影像关联的工具状态(标注数据)
|
||||
this.clearToolStateForImage(element, currentImageId)
|
||||
|
||||
// 3. 从缓存中移除该影像
|
||||
this.removeImageFromCache(currentImageId)
|
||||
|
||||
// 4. 重新加载并渲染
|
||||
await this.loadAndRenderImage(element, currentImageId)
|
||||
},
|
||||
clearToolStateForImage(element, imageId) {
|
||||
// 获取全局状态管理器
|
||||
const globalToolStateManager =
|
||||
cornerstoneTools.globalImageIdSpecificToolStateManager
|
||||
|
||||
if (globalToolStateManager) {
|
||||
// 方式1:清空该影像的全部工具数据
|
||||
globalToolStateManager.clearImageIdToolState(imageId)
|
||||
console.log(`已清除影像 ${imageId} 的所有标注数据`)
|
||||
}
|
||||
|
||||
// 方式2:如果使用元素级别的状态管理器
|
||||
const elementToolStateManager =
|
||||
cornerstoneTools.getElementToolStateManager(element)
|
||||
if (elementToolStateManager && elementToolStateManager.get(element)) {
|
||||
// 清空该元素上当前显示影像的数据
|
||||
elementToolStateManager.clear(element)
|
||||
}
|
||||
},
|
||||
removeImageFromCache(imageId) {
|
||||
const imageCache = cornerstone.imageCache
|
||||
|
||||
if (imageCache && imageCache[imageId]) {
|
||||
// 从缓存中移除
|
||||
delete imageCache[imageId]
|
||||
console.log(`已从缓存中移除影像: ${imageId}`)
|
||||
}
|
||||
|
||||
// 也可以使用官方API(如果可用)
|
||||
if (typeof cornerstone.imageCache.removeImage === 'function') {
|
||||
cornerstone.imageCache.removeImageLoadObject(imageId)
|
||||
}
|
||||
},
|
||||
async loadAndRenderImage(element, imageId) {
|
||||
try {
|
||||
// 1. 清除当前画布内容(可选)
|
||||
const canvas = element.querySelector('canvas')
|
||||
if (canvas) {
|
||||
const ctx = canvas.getContext('2d')
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height)
|
||||
}
|
||||
|
||||
// 2. 重新加载图像(强制从源头重新获取)
|
||||
const image = await cornerstone.loadAndCacheImage(imageId)
|
||||
|
||||
// 3. 获取或创建视口设置
|
||||
let viewport = cornerstone.getViewport(element)
|
||||
if (!viewport) {
|
||||
viewport = cornerstone.getDefaultViewport(element, image)
|
||||
}
|
||||
|
||||
// 4. 显示图像
|
||||
cornerstone.displayImage(element, image, viewport)
|
||||
|
||||
// 5. 触发重绘
|
||||
cornerstone.updateImage(element)
|
||||
|
||||
console.log(`影像 ${imageId} 重新加载并渲染完成`)
|
||||
|
||||
return image
|
||||
} catch (error) {
|
||||
console.error('加载图像失败:', error)
|
||||
throw error
|
||||
}
|
||||
},
|
||||
getNote_RectangleRoi() {
|
||||
return new Promise(resolve => {
|
||||
let toolInfo = cornerstoneTools.getToolState(this.canvas, 'Note_RectangleRoi')
|
||||
let image = cornerstone.getImage(this.canvas)
|
||||
resolve({ toolInfo, image })
|
||||
})
|
||||
// console.log(
|
||||
// cornerstoneTools.getToolState(this.canvas, 'Note_RectangleRoi')
|
||||
// )
|
||||
// console.log(cornerstone.getImage(this.canvas))
|
||||
// let image = cornerstone.getImage(this.canvas)
|
||||
// // cornerstone.imageCache.removeImageLoadObject(image.imageId)
|
||||
|
||||
// this.reloadImage(this.canvas, image.imageId)
|
||||
},
|
||||
removeNote_RectangleRoi() {
|
||||
const toolState = cornerstoneTools.getToolState(this.canvas, 'Note_RectangleRoi');
|
||||
if (toolState && toolState.data.length > 0) {
|
||||
let arr = toolState.data.map(item => item)
|
||||
arr.forEach(item => {
|
||||
cornerstoneTools.removeToolState(this.canvas, 'Note_RectangleRoi', item);
|
||||
})
|
||||
cornerstone.updateImage(this.canvas);
|
||||
}
|
||||
},
|
||||
setToolActive(toolName) {
|
||||
cornerstoneTools.setToolActiveForElement(this.canvas, toolName, {
|
||||
mouseButtonMask: 1
|
||||
mouseButtonMask: 1,
|
||||
})
|
||||
},
|
||||
setAllToolsPassive() {
|
||||
|
|
@ -833,7 +1007,7 @@ export default {
|
|||
)
|
||||
}
|
||||
cornerstoneTools.setToolEnabledForElement(this.canvas, 'ReferenceLines', {
|
||||
synchronizationContext: synchronizer
|
||||
synchronizationContext: synchronizer,
|
||||
})
|
||||
|
||||
// cornerstoneTools.addTool(cornerstoneTools.CrosshairsTool)
|
||||
|
|
@ -864,7 +1038,7 @@ export default {
|
|||
}
|
||||
cornerstoneTools.setToolActiveForElement(this.canvas, toolName, {
|
||||
mouseButtonMask: 1,
|
||||
synchronizationContext: synchronizer
|
||||
synchronizationContext: synchronizer,
|
||||
})
|
||||
},
|
||||
disabledViewPortToolSync(synchronizer, toolName) {
|
||||
|
|
@ -1095,7 +1269,7 @@ export default {
|
|||
'CobbAngle',
|
||||
'Angle',
|
||||
'Bidirectional',
|
||||
'FreehandRoi'
|
||||
'FreehandRoi',
|
||||
]
|
||||
for (let i = 0; i < toolROITypes.length; i++) {
|
||||
const toolROIType = toolROITypes[i]
|
||||
|
|
@ -1115,12 +1289,12 @@ export default {
|
|||
removeLabel(item) {
|
||||
const promise = scroll(this.canvas, item.data.imageIdIndex)
|
||||
const scope = this
|
||||
Promise.all([promise]).then(res => {
|
||||
Promise.all([promise]).then((res) => {
|
||||
cornerstoneTools.removeToolState(scope.canvas, item.type, item.data)
|
||||
cornerstone.updateImage(scope.canvas)
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -74,14 +74,16 @@ 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=/'
|
||||
},
|
||||
trialId(v) {
|
||||
if (!this.$route.query.trialId) return
|
||||
getUserDocumentList({ TrialId: this.$route.query.trialId }).then(async res => {
|
||||
var total = res.OtherInfo.NeedSignCount
|
||||
var TrialStatusStr = res.OtherInfo.TrialStatusStr
|
||||
|
|
@ -99,6 +101,7 @@ export default {
|
|||
if (~url.indexOf('?')) {
|
||||
query = url.split('?')[1]
|
||||
}
|
||||
if (this.$route.query.trialId) {
|
||||
getUserDocumentList({ TrialId: this.$route.query.trialId }).then(async res => {
|
||||
var total = res.OtherInfo.NeedSignCount
|
||||
var TrialStatusStr = res.OtherInfo.TrialStatusStr
|
||||
|
|
@ -108,14 +111,16 @@ export default {
|
|||
await store.dispatch('user/setTrialStatusStr', TrialStatusStr)
|
||||
this.TrialConfig = res.OtherInfo.TrialConfig
|
||||
})
|
||||
}
|
||||
this.selectedTab()
|
||||
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: {
|
||||
|
|
@ -130,6 +135,9 @@ export default {
|
|||
let chartList = [
|
||||
'/trials/trials-panel/trial-summary/report-forms'
|
||||
]
|
||||
let dataSyncList = [
|
||||
`/trials/trials-panel/trial-summary/data-sync`
|
||||
]
|
||||
var qualityList = [
|
||||
'/trials/trials-panel/visit/crc-question',
|
||||
'/trials/trials-panel/visit/qc-check',
|
||||
|
|
@ -145,6 +153,9 @@ export default {
|
|||
'/trials/trials-panel/trial-summary/image-inspect',
|
||||
'/trials/trials-panel/trial-summary/push-record',
|
||||
]
|
||||
if (this.$store.state.trials.config.TrialDataStoreType !== 1 && ~dataSyncList.indexOf(path)) {
|
||||
isShow = false
|
||||
}
|
||||
if (!this.$store.state.trials.config.IsExternalViewTrialChart && ~chartList.indexOf(path) && this.hasPermi(['role:cmm', 'role:cpm', 'role:ea', 'role:mc', 'role:smm', 'role:spm'])) {
|
||||
isShow = false
|
||||
}
|
||||
|
|
@ -201,15 +212,19 @@ export default {
|
|||
if (~url.indexOf('?')) {
|
||||
query = url.split('?')[1]
|
||||
}
|
||||
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}` })
|
||||
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) {
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -2,36 +2,21 @@
|
|||
<!--FEEDBACK-->
|
||||
<div v-if="visible" @click.stop="() => false" class="feedBack-box">
|
||||
<div class="feedBack-box-modal"></div>
|
||||
<el-dialog
|
||||
:visible.sync="visible"
|
||||
v-dialogDrag
|
||||
width="800px"
|
||||
:close-on-click-modal="false"
|
||||
@close="cancel"
|
||||
:modal="false"
|
||||
>
|
||||
<el-dialog :visible.sync="visible" v-dialogDrag width="800px" :close-on-click-modal="false" @close="cancel"
|
||||
:modal="false">
|
||||
<div slot="title">
|
||||
{{ title }}
|
||||
</div>
|
||||
<!-- 项目详情 -->
|
||||
<div class="trialsBox" v-if="visitTaskId || SubjectVisitId">
|
||||
<el-form
|
||||
label-position="right"
|
||||
:model="form"
|
||||
:inline="true"
|
||||
class="trialsForm"
|
||||
v-if="type === 'detail'"
|
||||
>
|
||||
<el-form label-position="right" :model="form" :inline="true" class="trialsForm" v-if="type === 'detail'">
|
||||
<el-form-item :label="$t('feedBack:trials:code')" style="width: 40%">
|
||||
<span>{{ form.TrialCode }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('feedBack:trials:name')" style="width: 40%">
|
||||
<span>{{ form.ExperimentName }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:label="$t('feedBack:trials:siteCode')"
|
||||
style="width: 40%"
|
||||
>
|
||||
<el-form-item :label="$t('feedBack:trials:siteCode')" style="width: 40%">
|
||||
<span>{{ form.TrialSiteCode }}</span>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('feedBack:trials:visit')" style="width: 40%">
|
||||
|
|
@ -39,47 +24,21 @@
|
|||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<el-form
|
||||
ref="feedBackForm"
|
||||
label-position="right"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-width="100px"
|
||||
>
|
||||
<el-form ref="feedBackForm" label-position="right" :model="form" :rules="rules" label-width="100px">
|
||||
<!-- 影像异常tip -->
|
||||
<p class="tip" v-if="type === 'imgfail'">
|
||||
<i
|
||||
class="el-icon-warning-outline"
|
||||
style="color: #f56c6c; font-size: 24px"
|
||||
></i>
|
||||
<i class="el-icon-warning-outline" style="color: #f56c6c; font-size: 24px"></i>
|
||||
<span>{{ $t('feedBack:imgfail:tip') }}</span>
|
||||
</p>
|
||||
<!-- 问题反馈 -->
|
||||
<el-form-item
|
||||
:label="$t('feedBack:form:feedBack')"
|
||||
prop="QuestionType"
|
||||
v-if="type === 'feedback' && trialId"
|
||||
>
|
||||
<el-select
|
||||
v-model="form.QuestionType"
|
||||
style="width: 100%"
|
||||
popper-class="feedBack-select-box"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in QuestionTypeOptions"
|
||||
:key="item.id"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
>
|
||||
<el-form-item :label="$t('feedBack:form:feedBack')" prop="QuestionType" v-if="type === 'feedback' && trialId">
|
||||
<el-select v-model="form.QuestionType" style="width: 100%" popper-class="feedBack-select-box">
|
||||
<el-option v-for="item in QuestionTypeOptions" :key="item.id" :label="item.label" :value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 问题反馈 -->
|
||||
<el-form-item
|
||||
:label="$t('feedBack:form:feedBack')"
|
||||
prop="QuestionType"
|
||||
v-if="type === 'detail'"
|
||||
>
|
||||
<el-form-item :label="$t('feedBack:form:feedBack')" prop="QuestionType" v-if="type === 'detail'">
|
||||
<span>{{
|
||||
QuestionTypeOptions.filter(
|
||||
(item) => item.value === form.QuestionType
|
||||
|
|
@ -87,43 +46,22 @@
|
|||
}}</span>
|
||||
</el-form-item>
|
||||
<!-- 问题描述 -->
|
||||
<el-form-item
|
||||
:label="$t('feedBack:form:description')"
|
||||
prop="QuestionDescription"
|
||||
>
|
||||
<el-input
|
||||
v-model="form.QuestionDescription"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
:maxlength="500"
|
||||
:disabled="type === 'detail'"
|
||||
/>
|
||||
<el-form-item :label="$t('feedBack:form:description')" prop="QuestionDescription">
|
||||
<el-input v-model="form.QuestionDescription" type="textarea" :rows="4" :maxlength="500"
|
||||
:disabled="type === 'detail'" />
|
||||
</el-form-item>
|
||||
<!-- 截图 -->
|
||||
<el-form-item :label="$t('feedBack:form:screenshot')" prop="screenshot">
|
||||
<uploadImage
|
||||
:path.sync="form.ScreenshotList"
|
||||
:isUpload.sync="loading"
|
||||
:trialId="trialId"
|
||||
:disabled="type === 'detail'"
|
||||
ref="uploadImage"
|
||||
/>
|
||||
<uploadImage :path.sync="form.ScreenshotList" :isUpload.sync="loading" :trialId="trialId"
|
||||
:disabled="type === 'detail'" ref="uploadImage" />
|
||||
</el-form-item>
|
||||
<!-- 反馈时间 -->
|
||||
<el-form-item
|
||||
:label="$t('feedBack:form:time')"
|
||||
prop="screenshot"
|
||||
v-if="type === 'detail'"
|
||||
>
|
||||
<el-form-item :label="$t('feedBack:form:time')" prop="screenshot" v-if="type === 'detail'">
|
||||
<span>{{ form.CreateTime }}</span>
|
||||
</el-form-item>
|
||||
<!-- 状态 -->
|
||||
<el-form-item
|
||||
:label="$t('feedBack:form:status')"
|
||||
prop="screenshot"
|
||||
v-if="type === 'detail' && level > 7"
|
||||
>
|
||||
<el-switch
|
||||
<el-form-item :label="$t('feedBack:form:status')" prop="screenshot" v-if="type === 'detail' && level >= 7">
|
||||
<!-- <el-switch
|
||||
v-model="form.State"
|
||||
active-color="#13ce66"
|
||||
inactive-color="#ff4949"
|
||||
|
|
@ -134,21 +72,27 @@
|
|||
:disabled="level < 8 || !isStateChange"
|
||||
@change="changeState"
|
||||
>
|
||||
</el-switch>
|
||||
</el-switch> -->
|
||||
<el-select v-model="form.State" :popper-append-to-body="false" :disabled="level < 8">
|
||||
<el-option v-for="item in $d.FeedBackStatus" :key="item.id" :label="item.label"
|
||||
:value="item.value"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 无法解决原因 -->
|
||||
<el-form-item :label="$t('feedBack:form:Reason')" prop="Reason"
|
||||
v-if="type === 'detail' && level >= 7 && form.State === 2">
|
||||
<el-input v-model="form.Reason" type="textarea" :rows="4" :maxlength="500" :disabled="level < 8" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" v-if="type !== 'detail' || isImgfail">
|
||||
<!--type !== 'detail'-->
|
||||
<div slot="footer" v-if="level >= 7 || isImgfail">
|
||||
<!-- 取消 -->
|
||||
<el-button size="small" @click.stop="cancel">
|
||||
<el-button size="small" @click.stop="cancel" v-if="type !== 'detail' || level >= 8">
|
||||
{{ $t('feedBack:button:cancel') }}
|
||||
</el-button>
|
||||
<!-- 保存 -->
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
@click.stop="save"
|
||||
:loading="loading"
|
||||
>
|
||||
<el-button type="primary" size="small" @click.stop="save" :loading="loading"
|
||||
v-if="type !== 'detail' || level >= 8">
|
||||
{{ $t('feedBack:button:save') }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
|
@ -190,6 +134,7 @@ export default {
|
|||
SubjectVisitId: null,
|
||||
ScreenshotList: [],
|
||||
ScreenshotListStr: null,
|
||||
Reason: null
|
||||
},
|
||||
rules: {
|
||||
QuestionType: [
|
||||
|
|
@ -307,6 +252,7 @@ export default {
|
|||
// 修改状态
|
||||
async changeState() {
|
||||
if (this.isImgfail) return
|
||||
if (this.form.State !== 1 && this.form.State !== 0) return false
|
||||
try {
|
||||
let data = {
|
||||
IdList: [this.Id],
|
||||
|
|
@ -381,6 +327,10 @@ export default {
|
|||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
::v-deep.el-popper {
|
||||
z-index: 4000 !important;
|
||||
}
|
||||
|
||||
.tip {
|
||||
width: 86%;
|
||||
margin: auto;
|
||||
|
|
@ -389,12 +339,14 @@ export default {
|
|||
padding: 0 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
// border-radius: 5px;
|
||||
// background-color: #eee;
|
||||
i {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.trialsBox {
|
||||
margin: auto;
|
||||
margin-bottom: 20px;
|
||||
|
|
@ -405,11 +357,13 @@ export default {
|
|||
border-radius: 5px;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.trialsForm {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.el-form-item {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,20 +8,49 @@
|
|||
|
||||
<script>
|
||||
import { getReportsChartData } from "@/api/reading"
|
||||
let echarts = require('echarts/lib/echarts');
|
||||
import * as echarts from 'echarts/core';
|
||||
import { LineChart } from 'echarts/charts';
|
||||
import {
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
GridComponent,
|
||||
DataZoomComponent,
|
||||
LegendComponent,
|
||||
DatasetComponent,
|
||||
// 内置数据转换器组件 (filter, sort)
|
||||
TransformComponent
|
||||
} from 'echarts/components';
|
||||
// 标签自动布局、全局过渡动画等特性
|
||||
import { LabelLayout, UniversalTransition } from 'echarts/features';
|
||||
// 引入 Canvas 渲染器,注意引入 CanvasRenderer 或者 SVGRenderer 是必须的一步
|
||||
import { CanvasRenderer } from 'echarts/renderers';
|
||||
echarts.use([
|
||||
TitleComponent,
|
||||
TooltipComponent,
|
||||
GridComponent,
|
||||
DatasetComponent,
|
||||
TransformComponent,
|
||||
DataZoomComponent,
|
||||
LegendComponent,
|
||||
LineChart,
|
||||
LabelLayout,
|
||||
UniversalTransition,
|
||||
CanvasRenderer
|
||||
]);
|
||||
// let echarts = require('echarts/lib/echarts');
|
||||
|
||||
// 按需引入图表
|
||||
// require('echarts/lib/chart/bar');
|
||||
require('echarts/lib/chart/line');
|
||||
// require('echarts/lib/chart/line');
|
||||
// require('echarts/lib/chart/pie');
|
||||
// require('echarts/lib/chart/scatter');
|
||||
|
||||
// 按需引入组件
|
||||
require('echarts/lib/component/tooltip');
|
||||
require('echarts/lib/component/title');
|
||||
require('echarts/lib/component/legend');
|
||||
require('echarts/lib/component/grid');
|
||||
require('echarts/lib/component/dataZoom');
|
||||
// require('echarts/lib/component/tooltip');
|
||||
// require('echarts/lib/component/title');
|
||||
// require('echarts/lib/component/legend');
|
||||
// require('echarts/lib/component/grid');
|
||||
// require('echarts/lib/component/dataZoom');
|
||||
export default {
|
||||
name: "readingChart",
|
||||
props: {
|
||||
|
|
@ -33,7 +62,8 @@ export default {
|
|||
zIndex: 9,
|
||||
chart: null,
|
||||
loading: false,
|
||||
key: 'readingChart'
|
||||
key: 'readingChart',
|
||||
isInteger: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -72,12 +102,13 @@ export default {
|
|||
let res = await getReportsChartData(params)
|
||||
this.loading = false
|
||||
if (res.IsSuccess) {
|
||||
this.isInteger = res.Result.Type === 'number' && res.Result.ValueType === 0
|
||||
let LatestScanDateList = res.Result.LatestScanDateList.map(item => item.split(" ")[0])
|
||||
let obj = {
|
||||
title: QuestionName,
|
||||
xAxisData: LatestScanDateList || [],
|
||||
series: [],
|
||||
unit: this.$fd("ValueUnit", res.Result.Unit),
|
||||
unit: res.Result.Unit === 0 ? '' : res.Result.Unit === 4 ? res.Result.CustomUnit : this.$fd("ValueUnit", res.Result.Unit),
|
||||
visitName: res.Result.VisitTaskNameList,
|
||||
min: null,
|
||||
max: null
|
||||
|
|
@ -94,43 +125,6 @@ export default {
|
|||
type: 'line'
|
||||
})
|
||||
});
|
||||
// if (Array.isArray(res.Result.LatestScanDateList) && res.Result.LatestScanDateList.length >= 2) {
|
||||
// let hours = moment(res.Result.LatestScanDateList[res.Result.LatestScanDateList.length - 1]).diff(moment(res.Result.LatestScanDateList[0]), 'hours');
|
||||
// let days = moment(res.Result.LatestScanDateList[res.Result.LatestScanDateList.length - 1]).diff(moment(res.Result.LatestScanDateList[0]), 'days');
|
||||
// let months = moment(res.Result.LatestScanDateList[res.Result.LatestScanDateList.length - 1]).diff(moment(res.Result.LatestScanDateList[0]), 'months');
|
||||
// console.log(hours, 'hours')
|
||||
// console.log(days, 'days')
|
||||
// console.log(months, 'months')
|
||||
// if (hours < 24) {
|
||||
// obj.min = moment(res.Result.LatestScanDateList[0]).format('YYYY-MM-DD') + ' 00:00:00'
|
||||
// obj.max = moment(res.Result.LatestScanDateList[0]).format('YYYY-MM-DD') + ' 23:59:59'
|
||||
// }
|
||||
// if (days >= 1 && days <= 7) {
|
||||
// obj.min = moment(res.Result.LatestScanDateList[0]).format('YYYY-MM-DD') + ' 00:00:00'
|
||||
// obj.max = moment(res.Result.LatestScanDateList[0]).add(7, 'days').format('YYYY-MM-DD') + ' 23:59:59'
|
||||
// }
|
||||
// if (days > 7 && days < 30) {
|
||||
// obj.min = moment(res.Result.LatestScanDateList[0]).startOf('month').format('YYYY-MM-DD') + ' 00:00:00'
|
||||
// obj.max = moment(res.Result.LatestScanDateList[0]).endOf('month').format('YYYY-MM-DD') + ' 23:59:59'
|
||||
// }
|
||||
// if (months >= 1 && months <= 3) {
|
||||
// obj.min = moment(res.Result.LatestScanDateList[0]).startOf('month').format('YYYY-MM-DD') + ' 00:00:00'
|
||||
// obj.max = moment(res.Result.LatestScanDateList[0]).add(4, 'months').startOf('month').format('YYYY-MM-DD') + ' 23:59:59'
|
||||
// }
|
||||
// if (months > 3 && months <= 6) {
|
||||
// obj.min = moment(res.Result.LatestScanDateList[0]).startOf('month').format('YYYY-MM-DD') + ' 00:00:00'
|
||||
// obj.max = moment(res.Result.LatestScanDateList[0]).add(7, 'months').startOf('month').format('YYYY-MM-DD') + ' 23:59:59'
|
||||
// }
|
||||
// if (months > 6 && months <= 12) {
|
||||
// obj.min = moment(res.Result.LatestScanDateList[0]).startOf('month').format('YYYY-MM-DD') + ' 00:00:00'
|
||||
// obj.max = moment(res.Result.LatestScanDateList[0]).add(13, 'months').startOf('month').format('YYYY-MM-DD') + ' 23:59:59'
|
||||
// }
|
||||
// if (months > 12) {
|
||||
// obj.min = moment(res.Result.LatestScanDateList[0]).startOf('month').format('YYYY-MM-DD') + ' 00:00:00'
|
||||
// obj.max = moment(res.Result.LatestScanDateList[res.Result.LatestScanDateList.length - 1]).add(1, 'months').startOf('month').format('YYYY-MM-DD') + ' 23:59:59'
|
||||
// }
|
||||
// }
|
||||
// console.log(obj)
|
||||
this.initChart(obj)
|
||||
}
|
||||
} catch (err) {
|
||||
|
|
@ -151,7 +145,13 @@ export default {
|
|||
text: obj.title,
|
||||
textStyle: {
|
||||
color: "#fff"
|
||||
}
|
||||
},
|
||||
left: 0,
|
||||
top: 0
|
||||
},
|
||||
grid: {
|
||||
left: 50,
|
||||
bottom: 30
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
|
|
@ -193,6 +193,7 @@ export default {
|
|||
}
|
||||
},
|
||||
axisLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: '#fff',
|
||||
}
|
||||
|
|
@ -200,6 +201,9 @@ export default {
|
|||
},
|
||||
series: obj.series
|
||||
};
|
||||
if (this.isInteger) {
|
||||
option.yAxis.minInterval = 1
|
||||
}
|
||||
// 4. 使用配置项渲染图表
|
||||
this.chart.setOption(option);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -70,16 +70,13 @@
|
|||
<div style="margin: 10px 0" class="top">
|
||||
<span>{{ $t('upload:dicom:uploadTitle') }}</span>
|
||||
<div class="btnBox" v-if="!forbid">
|
||||
<span style="margin-right: 10px">
|
||||
{{ $store.state.trials.uploadTip }}
|
||||
</span>
|
||||
<span style="margin-right: 10px">{{ $store.state.trials.uploadTip }}</span>
|
||||
<form id="inputForm" ref="uploadForm" enctype="multipart/form-data">
|
||||
<div class="form-group">
|
||||
<div id="directoryInputWrapper" class="btn btn-link file-input">
|
||||
<el-button type="primary" :disabled="btnLoading" :loading="btnLoading" size="mini">
|
||||
{{ $t('upload:dicom:button:batchUpload') }}
|
||||
</el-button>
|
||||
<input type="file" name="file" ref="pathClear" :disabled="btnLoading" webkitdirectory multiple title=""
|
||||
<el-button type="primary" :disabled="btnLoading" :loading="btnLoading" size="mini">{{
|
||||
$t('upload:dicom:button:batchUpload') }}</el-button>
|
||||
<input type="file" name="file" ref="pathClear" :disabled="btnLoading" webkitdirectory multiple title
|
||||
@change="beginScanFiles($event)" />
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -94,8 +91,10 @@
|
|||
<template slot="header">
|
||||
<el-tooltip placement="top">
|
||||
<div slot="content">
|
||||
{{ $t('trials:uploadDicomList:table:studyDetail1') }}<br />
|
||||
{{ $t('trials:uploadDicomList:table:studyDetail2') }}<br />
|
||||
{{ $t('trials:uploadDicomList:table:studyDetail1') }}
|
||||
<br />
|
||||
{{ $t('trials:uploadDicomList:table:studyDetail2') }}
|
||||
<br />
|
||||
{{ $t('trials:uploadedDicoms:table:studyDate') }}
|
||||
</div>
|
||||
<span>{{ $t('trials:uploadDicomList:table:studyInfo') }}</span>
|
||||
|
|
@ -105,13 +104,15 @@
|
|||
<div style="line-height: 15px">
|
||||
<div>
|
||||
<div>
|
||||
<span v-if="scope.row.dicomInfo.accNumber"><span style="font-weight: 500">Acc:</span>
|
||||
{{ scope.row.dicomInfo.accNumber }}</span>
|
||||
<span v-if="scope.row.dicomInfo.accNumber">
|
||||
<span style="font-weight: 500">Acc:</span>
|
||||
{{ scope.row.dicomInfo.accNumber }}
|
||||
</span>
|
||||
<span v-else style="color: #f44336">N/A</span>
|
||||
</div>
|
||||
<div style="display: inline-block; margin-right: 2px">
|
||||
<span v-if="scope.row.dicomInfo.modality.length > 0">
|
||||
{{ scope.row.dicomInfo.modality.join('、') }},</span>
|
||||
<span v-if="scope.row.dicomInfo.modality.length > 0">{{ scope.row.dicomInfo.modality.join('、')
|
||||
}},</span>
|
||||
<span v-else style="color: #f44336">N/A,</span>
|
||||
</div>
|
||||
<div style="display: inline-block; margin-right: 2px">
|
||||
|
|
@ -126,20 +127,15 @@
|
|||
|
||||
<div>
|
||||
<div style="display: inline-block; margin-right: 2px">
|
||||
<span v-if="scope.row.dicomInfo.bodyPart">
|
||||
{{ scope.row.dicomInfo.bodyPart }},
|
||||
</span>
|
||||
<span v-if="scope.row.dicomInfo.bodyPart">{{ scope.row.dicomInfo.bodyPart }},</span>
|
||||
<span v-else style="color: #f44336">N/A,</span>
|
||||
</div>
|
||||
<div style="display: inline-block">
|
||||
<span v-if="scope.row.dicomInfo.description">
|
||||
{{ scope.row.dicomInfo.description }}</span>
|
||||
<span v-if="scope.row.dicomInfo.description">{{ scope.row.dicomInfo.description }}</span>
|
||||
<span v-else style="color: #f44336">N/A</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
{{ scope.row.dicomInfo.studyTime }}
|
||||
</div>
|
||||
<div>{{ scope.row.dicomInfo.studyTime }}</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
|
@ -147,8 +143,10 @@
|
|||
<template slot="header">
|
||||
<el-tooltip placement="top">
|
||||
<div slot="content">
|
||||
{{ $t('trials:uploadDicomList:table:pId') }}<br />
|
||||
{{ $t('trials:uploadDicomList:table:patientName') }}<br />
|
||||
{{ $t('trials:uploadDicomList:table:pId') }}
|
||||
<br />
|
||||
{{ $t('trials:uploadDicomList:table:patientName') }}
|
||||
<br />
|
||||
{{ $t('trials:uploadDicomList:table:pInfo') }}
|
||||
</div>
|
||||
<span>{{ $t('trials:uploadDicomList:table:patientInfo') }}</span>
|
||||
|
|
@ -157,8 +155,11 @@
|
|||
<template slot-scope="scope">
|
||||
<div style="line-height: 15px">
|
||||
<div>
|
||||
<span v-if="scope.row.dicomInfo.patientId"><span style="font-weight: 500">PID: </span>{{
|
||||
scope.row.dicomInfo.patientId }}</span>
|
||||
<span v-if="scope.row.dicomInfo.patientId">
|
||||
<span style="font-weight: 500">PID:</span>
|
||||
{{
|
||||
scope.row.dicomInfo.patientId }}
|
||||
</span>
|
||||
<span v-else style="color: #f44336">N/A</span>
|
||||
</div>
|
||||
<div>
|
||||
|
|
@ -227,8 +228,7 @@
|
|||
<span v-if="
|
||||
!scope.row.dicomInfo.failedFileCount &&
|
||||
!scope.row.dicomInfo.isInit
|
||||
">
|
||||
{{ $t('trials:uploadDicomList:table:status1') }}</span>
|
||||
">{{ $t('trials:uploadDicomList:table:status1') }}</span>
|
||||
<span style="color: #409eff" v-else-if="
|
||||
!scope.row.dicomInfo.failedFileCount &&
|
||||
scope.row.dicomInfo.isInit &&
|
||||
|
|
@ -246,9 +246,11 @@
|
|||
scope.row.uploadState.record &&
|
||||
scope.row.uploadState.record.fileCount === 0
|
||||
">{{ $t('trials:uploadDicomList:table:status5') }}</span>
|
||||
<span style="color: #f66" v-else>{{
|
||||
<span style="color: #f66" v-else>
|
||||
{{
|
||||
$t('trials:uploadDicomList:table:Failed')
|
||||
}}</span>
|
||||
}}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('trials:uploadDicomList:table:record')" min-width="140" show-overflow-tooltip>
|
||||
|
|
@ -259,42 +261,42 @@
|
|||
{{ $t('trials:uploadDicomList:table:Existed') }}:
|
||||
<div v-if="scope.row.uploadState.record.Existed.length">
|
||||
<div v-for="item of scope.row.uploadState.record.Existed" :key="item"
|
||||
style="font-size: 12px; color: #baa72a">
|
||||
{{ item }}
|
||||
</div>
|
||||
style="font-size: 12px; color: #baa72a">{{ item }}</div>
|
||||
</div>
|
||||
<div v-else> </div>
|
||||
{{ $t('trials:uploadDicomList:table:Uploaded') }}:
|
||||
<div v-if="scope.row.uploadState.record.Uploaded.length">
|
||||
<div v-for="item of scope.row.uploadState.record.Uploaded" :key="item"
|
||||
style="font-size: 12px; color: #24b837">
|
||||
{{ item }}
|
||||
</div>
|
||||
style="font-size: 12px; color: #24b837">{{ item }}</div>
|
||||
</div>
|
||||
<div v-else> </div>
|
||||
<br />
|
||||
{{ $t('trials:uploadDicomList:table:Failed') }}:
|
||||
<div v-if="scope.row.uploadState.record.Failed.length">
|
||||
<div v-for="item of scope.row.uploadState.record.Failed" :key="item"
|
||||
style="font-size: 12px; color: #f66">
|
||||
{{ item }}
|
||||
</div>
|
||||
style="font-size: 12px; color: #f66">{{ item }}</div>
|
||||
</div>
|
||||
<div v-else> </div>
|
||||
</div>
|
||||
</div>
|
||||
<el-button size="mini" style="cursor: pointer">
|
||||
<span style="font-size: 12px; color: #baa72a">{{
|
||||
<span style="font-size: 12px; color: #baa72a">
|
||||
{{
|
||||
scope.row.uploadState.record.Existed.length
|
||||
}}</span>
|
||||
}}
|
||||
</span>
|
||||
/
|
||||
<span style="font-size: 12px; color: #24b837">{{
|
||||
<span style="font-size: 12px; color: #24b837">
|
||||
{{
|
||||
scope.row.uploadState.record.Uploaded.length
|
||||
}}</span>
|
||||
}}
|
||||
</span>
|
||||
/
|
||||
<span style="font-size: 12px; color: #f66">{{
|
||||
<span style="font-size: 12px; color: #f66">
|
||||
{{
|
||||
scope.row.uploadState.record.Failed.length
|
||||
}}</span>
|
||||
}}
|
||||
</span>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
|
|
@ -335,6 +337,7 @@ cornerstoneWADOImageLoader.external.cornerstone = cornerstone
|
|||
import { convertBytes } from '@/utils/dicom-character-set'
|
||||
import { parseDicom } from '@/utils/parseDicom.js'
|
||||
import { dcmUpload } from '@/utils/dcmUpload/dcmUpload'
|
||||
import dcmjs from '@/utils/dcmUpload/dcmjs'
|
||||
import store from '@/store'
|
||||
import { getToken } from '@/utils/auth'
|
||||
export default {
|
||||
|
|
@ -373,7 +376,7 @@ export default {
|
|||
IsImageSegment: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
}
|
||||
},
|
||||
},
|
||||
components: {
|
||||
'study-view': studyView,
|
||||
|
|
@ -455,8 +458,10 @@ export default {
|
|||
}
|
||||
if (this.IsImageSegment) {
|
||||
params.IsImageSegmentLabel = true
|
||||
}
|
||||
else if (this.Criterion.CriterionType == 19 || this.Criterion.CriterionType == 20) {
|
||||
} else if (
|
||||
this.Criterion.CriterionType == 19 ||
|
||||
this.Criterion.CriterionType == 20
|
||||
) {
|
||||
params.IsImageSegmentLabel = false
|
||||
}
|
||||
this.loading = true
|
||||
|
|
@ -543,7 +548,8 @@ export default {
|
|||
this.openIsUpload = isUpload
|
||||
this.openSubjectVisitId = item.SubjectVisitId || item.SourceSubjectVisitId
|
||||
this.openVisitTaskId = item.VisitTaskId
|
||||
this.model_cfg.title = `${item.SubjectCode || ''} > ${this.IsImageSegment ? item.VisitName : item.TaskBlindName}`
|
||||
this.model_cfg.title = `${item.SubjectCode || ''} > ${this.IsImageSegment ? item.VisitName : item.TaskBlindName
|
||||
}`
|
||||
this.modelList = item[list]
|
||||
this.model_cfg.visible = true
|
||||
},
|
||||
|
|
@ -645,7 +651,10 @@ export default {
|
|||
for (let i = 0; i < checkFiles.length; i++) {
|
||||
let item = checkFiles[i]
|
||||
var dicom = await parseDicom(item, ['StudyInstanceUid', 'Modality'])
|
||||
if (!!~this.errStudyUidList.indexOf(dicom.StudyInstanceUid) || (this.IsImageSegment && dicom.Modality !== 'IVUS')) {
|
||||
if (
|
||||
!!~this.errStudyUidList.indexOf(dicom.StudyInstanceUid) ||
|
||||
(this.IsImageSegment && dicom.Modality !== 'IVUS')
|
||||
) {
|
||||
this.hasOtherStudy = true
|
||||
checkFiles.splice(i, 1)
|
||||
i--
|
||||
|
|
@ -712,12 +721,13 @@ 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
|
||||
|
|
@ -809,6 +819,39 @@ export default {
|
|||
},
|
||||
})
|
||||
}
|
||||
if (
|
||||
!scope.uploadQueues[studyIndex].dicomInfo.RadionuclideTotalDose
|
||||
) {
|
||||
let dataset = dcmjs.data.DicomMessage.readFile(e.target.result)
|
||||
if (
|
||||
dataset.dict['00540016'] &&
|
||||
dataset.dict['00540016'].Value &&
|
||||
dataset.dict['00540016'].Value[0]
|
||||
) {
|
||||
let RadionuclideTotalDose = dataset.dict['00540016'].Value[0][
|
||||
'00181074'
|
||||
]
|
||||
? dataset.dict['00540016'].Value[0]['00181074'].Value[0]
|
||||
: null
|
||||
let RadionuclideHalfLife = dataset.dict['00540016'].Value[0][
|
||||
'00181075'
|
||||
]
|
||||
? dataset.dict['00540016'].Value[0]['00181075'].Value[0]
|
||||
: null
|
||||
let RadiopharmaceuticalStartTime = dataset.dict['00540016']
|
||||
.Value[0]['00181072']
|
||||
? dataset.dict['00540016'].Value[0]['00181072'].Value[0]
|
||||
: null
|
||||
scope.uploadQueues[studyIndex].dicomInfo.RadionuclideTotalDose =
|
||||
RadionuclideTotalDose
|
||||
scope.uploadQueues[studyIndex].dicomInfo.RadionuclideHalfLife =
|
||||
RadionuclideHalfLife
|
||||
scope.uploadQueues[
|
||||
studyIndex
|
||||
].dicomInfo.RadiopharmaceuticalStartTime =
|
||||
RadiopharmaceuticalStartTime
|
||||
}
|
||||
}
|
||||
var modality = scope.uploadQueues[studyIndex].dicomInfo.modality
|
||||
var currentModality = data.string('x00080060')
|
||||
if (!(modality.indexOf(currentModality) > -1)) {
|
||||
|
|
@ -876,8 +919,9 @@ export default {
|
|||
)
|
||||
seriesItem = {
|
||||
seriesUid: seriesUid,
|
||||
RadiopharmaceuticalInformationSequence: data.string('x00540016') || "",
|
||||
AcquisitionDate: data.string('x00080022') || "",
|
||||
RadiopharmaceuticalInformationSequence:
|
||||
data.string('x00540016') || '',
|
||||
AcquisitionDate: data.string('x00080022') || '',
|
||||
|
||||
DicomSeriesDate: data.string('x00080021'),
|
||||
DicomSeriesTime: data.string('x00080031'),
|
||||
|
|
@ -1085,16 +1129,20 @@ export default {
|
|||
var scope = this
|
||||
return new Promise(function (resolve, reject) {
|
||||
try {
|
||||
let subjectVisitId = null;
|
||||
let subjectVisitId = null
|
||||
let dicomInfo = {}
|
||||
if (scope.VisitTaskId) {
|
||||
scope.StudyInstanceUidList.forEach(item => {
|
||||
scope.StudyInstanceUidList.forEach((item) => {
|
||||
if (item.VisitTaskId === scope.VisitTaskId) {
|
||||
subjectVisitId = item.SourceSubjectVisitId
|
||||
}
|
||||
})
|
||||
} else {
|
||||
scope.StudyInstanceUidList.forEach(item => {
|
||||
if (item.StudyInstanceUid === scope.uploadQueues[index].dicomInfo.studyUid) {
|
||||
scope.StudyInstanceUidList.forEach((item) => {
|
||||
if (
|
||||
item.StudyInstanceUid ===
|
||||
scope.uploadQueues[index].dicomInfo.studyUid
|
||||
) {
|
||||
subjectVisitId = item.SourceSubjectVisitId
|
||||
}
|
||||
})
|
||||
|
|
@ -1112,7 +1160,7 @@ export default {
|
|||
.then(async (res) => {
|
||||
scope.uploadQueues[index].dicomInfo.failedFileCount = 0
|
||||
scope.$set(scope.uploadQueues[index].dicomInfo, 'isInit', true)
|
||||
let dicomInfo = scope.uploadQueues[index].dicomInfo
|
||||
dicomInfo = scope.uploadQueues[index].dicomInfo
|
||||
let seriesNum = scope.uploadQueues[index].seriesList.length
|
||||
let fileNum = scope.uploadQueues[index].fileList.length
|
||||
let seriesList = scope.uploadQueues[index].seriesList
|
||||
|
|
@ -1144,6 +1192,11 @@ export default {
|
|||
failedFileCount: 0,
|
||||
RecordPath: null,
|
||||
study: {
|
||||
RadionuclideTotalDose: dicomInfo.RadionuclideTotalDose,
|
||||
RadionuclideHalfLife: dicomInfo.RadionuclideHalfLife,
|
||||
RadiopharmaceuticalStartTime:
|
||||
dicomInfo.RadiopharmaceuticalStartTime,
|
||||
|
||||
studyId: dicomInfo.studyId,
|
||||
studyInstanceUid: dicomInfo.studyUid,
|
||||
studyTime: dicomInfo.studyTime,
|
||||
|
|
@ -1153,8 +1206,8 @@ export default {
|
|||
institutionName: dicomInfo.institutionName,
|
||||
patientId: dicomInfo.patientId,
|
||||
patientName: '',
|
||||
patientAge: '',
|
||||
patientSex: dicomInfo.patientSex,
|
||||
patientAge: dicomInfo.patientAge,
|
||||
patientSex: config?.DicomStoreInfo.SubjectSex || dicomInfo.patientSex,
|
||||
accessionNumber: dicomInfo.accNumber,
|
||||
patientBirthDate: '',
|
||||
acquisitionTime: dicomInfo.acquisitionTime,
|
||||
|
|
@ -1174,6 +1227,7 @@ export default {
|
|||
},
|
||||
}
|
||||
let arr = []
|
||||
let uploadBatchId = scope.$guid()
|
||||
for (let i = 0; i < seriesList.length; i++) {
|
||||
let v = seriesList[i]
|
||||
let instanceList = []
|
||||
|
|
@ -1196,8 +1250,10 @@ export default {
|
|||
seriesInstanceUid: v.seriesUid,
|
||||
SOPClassUID: o.SOPClassUID,
|
||||
TransferSytaxUID: o.TransferSytaxUID,
|
||||
MediaStorageSOPInstanceUID: o.MediaStorageSOPInstanceUID,
|
||||
MediaStorageSOPClassUID: o.MediaStorageSOPClassUID,
|
||||
MediaStorageSOPInstanceUID:
|
||||
o.MediaStorageSOPInstanceUID,
|
||||
MediaStorageSOPClassUID:
|
||||
o.MediaStorageSOPClassUID,
|
||||
sopInstanceUid: o.instanceUid,
|
||||
instanceNumber: o.instanceNumber,
|
||||
instanceTime: o.instanceTime,
|
||||
|
|
@ -1214,14 +1270,17 @@ export default {
|
|||
path: o.myPath,
|
||||
FileSize: o.FileSize,
|
||||
|
||||
PhotometricInterpretation: o.PhotometricInterpretation,
|
||||
PhotometricInterpretation:
|
||||
o.PhotometricInterpretation,
|
||||
BitsAllocated: o.BitsAllocated,
|
||||
PixelRepresentation: o.PixelRepresentation,
|
||||
RescaleIntercept: o.RescaleIntercept,
|
||||
RescaleSlope: o.RescaleSlope,
|
||||
ImagePositionPatient: o.ImagePositionPatient,
|
||||
ImageOrientationPatient: o.ImageOrientationPatient,
|
||||
SequenceOfUltrasoundRegions: o.SequenceOfUltrasoundRegions,
|
||||
ImageOrientationPatient:
|
||||
o.ImageOrientationPatient,
|
||||
SequenceOfUltrasoundRegions:
|
||||
o.SequenceOfUltrasoundRegions,
|
||||
FrameTime: o.FrameTime,
|
||||
CorrectedImage: o.CorrectedImage,
|
||||
Units: o.Units,
|
||||
|
|
@ -1266,6 +1325,16 @@ export default {
|
|||
) {
|
||||
dicomInfo.uploadFileSize = dicomInfo.fileSize
|
||||
}
|
||||
},
|
||||
{
|
||||
fileName: o.file.name,
|
||||
fileSize: o.file.size,
|
||||
fileType: 'application/dicom',
|
||||
uploadBatchId: uploadBatchId,
|
||||
batchDataType: 5,
|
||||
trialId: params.trialId,
|
||||
subjectId: params.subjectId,
|
||||
subjectVisitId: params.subjectVisitId,
|
||||
}
|
||||
)
|
||||
if (!res || !res.url) {
|
||||
|
|
@ -1289,7 +1358,17 @@ export default {
|
|||
let OSSclient = scope.OSSclient
|
||||
let seriesRes = await OSSclient.put(
|
||||
thumbnailPath,
|
||||
blob
|
||||
blob,
|
||||
{
|
||||
fileName: `${v.seriesUid}.jpg`,
|
||||
fileSize: blob.size,
|
||||
fileType: 'image/jpeg',
|
||||
uploadBatchId: uploadBatchId,
|
||||
batchDataType: 6,
|
||||
trialId: params.trialId,
|
||||
subjectId: params.subjectId,
|
||||
subjectVisitId: params.subjectVisitId,
|
||||
}
|
||||
)
|
||||
if (seriesRes && seriesRes.url) {
|
||||
ImageResizePath = scope.$getObjectName(
|
||||
|
|
@ -1308,8 +1387,10 @@ export default {
|
|||
sopInstanceUid: o.instanceUid,
|
||||
SOPClassUID: o.SOPClassUID,
|
||||
TransferSytaxUID: o.TransferSytaxUID,
|
||||
MediaStorageSOPInstanceUID: o.MediaStorageSOPInstanceUID,
|
||||
MediaStorageSOPClassUID: o.MediaStorageSOPClassUID,
|
||||
MediaStorageSOPInstanceUID:
|
||||
o.MediaStorageSOPInstanceUID,
|
||||
MediaStorageSOPClassUID:
|
||||
o.MediaStorageSOPClassUID,
|
||||
instanceNumber: o.instanceNumber,
|
||||
instanceTime: o.instanceTime,
|
||||
imageRows: o.imageRows,
|
||||
|
|
@ -1325,14 +1406,17 @@ export default {
|
|||
path: scope.$getObjectName(res.url),
|
||||
FileSize: o.FileSize,
|
||||
|
||||
PhotometricInterpretation: o.PhotometricInterpretation,
|
||||
PhotometricInterpretation:
|
||||
o.PhotometricInterpretation,
|
||||
BitsAllocated: o.BitsAllocated,
|
||||
PixelRepresentation: o.PixelRepresentation,
|
||||
RescaleIntercept: o.RescaleIntercept,
|
||||
RescaleSlope: o.RescaleSlope,
|
||||
ImagePositionPatient: o.ImagePositionPatient,
|
||||
ImageOrientationPatient: o.ImageOrientationPatient,
|
||||
SequenceOfUltrasoundRegions: o.SequenceOfUltrasoundRegions,
|
||||
ImageOrientationPatient:
|
||||
o.ImageOrientationPatient,
|
||||
SequenceOfUltrasoundRegions:
|
||||
o.SequenceOfUltrasoundRegions,
|
||||
FrameTime: o.FrameTime,
|
||||
CorrectedImage: o.CorrectedImage,
|
||||
Units: o.Units,
|
||||
|
|
@ -1387,7 +1471,8 @@ export default {
|
|||
instanceList: instanceList,
|
||||
ImageResizePath: ImageResizePath,
|
||||
|
||||
RadiopharmaceuticalInformationSequence: v.RadiopharmaceuticalInformationSequence,
|
||||
RadiopharmaceuticalInformationSequence:
|
||||
v.RadiopharmaceuticalInformationSequence,
|
||||
AcquisitionDate: v.AcquisitionDate,
|
||||
})
|
||||
}
|
||||
|
|
@ -1430,7 +1515,16 @@ export default {
|
|||
}
|
||||
let OSSclient = scope.OSSclient
|
||||
try {
|
||||
let seriesRes = await OSSclient.put(thumbnailPath, blob)
|
||||
let seriesRes = await OSSclient.put(thumbnailPath, blob, {
|
||||
fileName: `${v.seriesUid}.jpg`,
|
||||
fileSize: blob.size,
|
||||
fileType: 'image/jpeg',
|
||||
uploadBatchId: uploadBatchId,
|
||||
batchDataType: 6,
|
||||
trialId: params.trialId,
|
||||
subjectId: params.subjectId,
|
||||
subjectVisitId: params.subjectVisitId,
|
||||
})
|
||||
if (seriesRes && seriesRes.url) {
|
||||
o.ImageResizePath = scope.$getObjectName(seriesRes.url)
|
||||
}
|
||||
|
|
@ -1448,6 +1542,7 @@ export default {
|
|||
if (scope.IsImageSegment) {
|
||||
params.IsImageSegmentLabel = true
|
||||
}
|
||||
params.UploadBatchId = uploadBatchId
|
||||
addOrUpdateArchiveTaskStudy(params)
|
||||
.then((res) => {
|
||||
if (dicomInfo.failedFileCount === dicomInfo.fileCount) {
|
||||
|
|
@ -1542,7 +1637,9 @@ export default {
|
|||
var token = getToken()
|
||||
let trialId = this.$route.query.trialId
|
||||
const routeData = this.$router.resolve({
|
||||
path: `/showvisitdicoms?page=upload&trialId=${trialId}&visitTaskId=${this.IsImageSegment ? 'undefined' : row.VisitTaskId}&subjectVisitId=${row.SourceSubjectVisitId}&isReading=1&TokenKey=${token}`,
|
||||
path: `/showvisitdicoms?page=upload&trialId=${trialId}&visitTaskId=${this.IsImageSegment ? 'undefined' : row.VisitTaskId
|
||||
}&subjectVisitId=${row.SourceSubjectVisitId
|
||||
}&isReading=1&TokenKey=${token}`,
|
||||
})
|
||||
this.open = window.open(routeData.href, '_blank')
|
||||
},
|
||||
|
|
|
|||
|
|
@ -548,8 +548,9 @@ export default {
|
|||
})
|
||||
if (res.IsSuccess) {
|
||||
this.studyMonitorId = res.Result
|
||||
let uploadBatchId = this.$guid()
|
||||
for (let i = 0; i < num; i++) {
|
||||
funArr.push(this.handleUploadTask(this.selectArr, i))
|
||||
funArr.push(this.handleUploadTask(this.selectArr, i, uploadBatchId))
|
||||
}
|
||||
if (funArr.length > 0) {
|
||||
let res = await Promise.all(funArr)
|
||||
|
|
@ -560,7 +561,7 @@ export default {
|
|||
}
|
||||
},
|
||||
// 并发上传
|
||||
async handleUploadTask(arr, index) {
|
||||
async handleUploadTask(arr, index, uploadBatchId) {
|
||||
if (!this.uploadVisible) return
|
||||
let file = this.fileList.filter((item) => item.id === arr[index].id)[0]
|
||||
file.status = 1
|
||||
|
|
@ -576,7 +577,7 @@ export default {
|
|||
}
|
||||
file.curPath = path
|
||||
const fileData = await this.fileToBlob(file.file)
|
||||
let res = await this.fileToOss(path, fileData, file)
|
||||
let res = await this.fileToOss(path, fileData, file, uploadBatchId)
|
||||
if (res) {
|
||||
file.status = 2
|
||||
this.successFileList.push({
|
||||
|
|
@ -610,13 +611,13 @@ export default {
|
|||
}
|
||||
let ind = arr.findIndex((item) => item.status === 0)
|
||||
if (ind >= 0) {
|
||||
return this.handleUploadTask(arr, ind)
|
||||
return this.handleUploadTask(arr, ind, uploadBatchId)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
},
|
||||
// file上传到oss
|
||||
async fileToOss(path, file, item) {
|
||||
async fileToOss(path, file, item, uploadBatchId) {
|
||||
try {
|
||||
let res = await this.OSSclient.multipartUpload(
|
||||
{
|
||||
|
|
@ -629,6 +630,17 @@ export default {
|
|||
if (item.uploadFileSize > file.fileSize) {
|
||||
item.uploadFileSize = file.fileSize > 0 ? file.fileSize : 1
|
||||
}
|
||||
},
|
||||
{
|
||||
fileName: item.name,
|
||||
fileSize: item.size,
|
||||
fileType: item.fileType,
|
||||
uploadBatchId: uploadBatchId,
|
||||
batchDataType: 7,
|
||||
trialId: this.$route.query.trialId,
|
||||
subjectId: this.currentRow.SubjectId,
|
||||
subjectVisitId: this.currentRow.SourceSubjectVisitId,
|
||||
studyCode: this.SubjectCode
|
||||
}
|
||||
)
|
||||
if (res) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,16 @@
|
|||
import resize from './resize'
|
||||
|
||||
const install = function (Vue) {
|
||||
// 绑定v-adaptive指令
|
||||
Vue.directive('resize', resize)
|
||||
}
|
||||
|
||||
// if (window.Vue) {
|
||||
// window['adaptive'] = adaptive
|
||||
// // eslint-disable-next-line no-undef
|
||||
// Vue.use(install)
|
||||
// }
|
||||
|
||||
resize.install = install
|
||||
|
||||
export default resize
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
const map = new WeakMap()
|
||||
const ob = new ResizeObserver(entries => {
|
||||
for (const entry of entries) {
|
||||
const handler = map.get(entry.target)
|
||||
if (handler) {
|
||||
const { inlineSize, blockSize } = entry.contentBoxSize[0]
|
||||
handler({ width: inlineSize, height: blockSize })
|
||||
}
|
||||
}
|
||||
})
|
||||
export default {
|
||||
bind(el, binding) {
|
||||
map.set(el, binding.value)
|
||||
ob.observe(el)
|
||||
},
|
||||
unbind(el) {
|
||||
ob.unobserve(el)
|
||||
}
|
||||
}
|
||||
|
|
@ -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="1777010831812" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1646" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512 896c-230.058667 0-421.461333-165.546667-461.610667-384a468.565333 468.565333 0 0 1 142.506667-258.816L89.6 149.973333A42.666667 42.666667 0 1 1 149.930667 89.6L934.4 874.112a42.666667 42.666667 0 0 1-60.373333 60.330667l-111.061334-111.061334A466.901333 466.901333 0 0 1 512 896zM253.226667 313.6A382.506667 382.506667 0 0 0 137.514667 512a384.213333 384.213333 0 0 0 563.242666 249.088l-86.528-86.528A192 192 0 0 1 349.44 409.770667L253.226667 313.6z m297.770666 297.728l-138.325333-138.325333a106.666667 106.666667 0 0 0 138.282667 138.282666l0.042666 0.042667z m285.312 22.357333l10.24-18.218666A381.226667 381.226667 0 0 0 886.485333 512a384.213333 384.213333 0 0 0-440.661333-292.949333c-2.944 0.512-9.173333 1.877333-18.730667 4.096a42.88 42.88 0 1 1-19.2-83.541334A470.912 470.912 0 0 1 512 128c230.058667 0 421.461333 165.546667 461.610667 384a467.072 467.072 0 0 1-57.685334 153.856 94.933333 94.933333 0 0 1-8.021333 11.178667 44.373333 44.373333 0 0 1-58.794667 9.088 39.552 39.552 0 0 1-12.8-52.437334z m-188.501333-257.493333a192 192 0 0 1 55.850667 147.626667l-203.52-203.477334a192 192 0 0 1 147.626666 55.893334z" fill="#e6e6e6" p-id="1647"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
|
|
@ -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 |
|
|
@ -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 +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="1751350116150" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="20392" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M870.4 819.2a51.2 51.2 0 1 1 0 102.4 51.2 51.2 0 0 1 0-102.4zM512 179.2a332.8 332.8 0 1 1 0 665.6 332.8 332.8 0 0 1 0-665.6z m0 51.2a281.6 281.6 0 1 0 0 563.2 281.6 281.6 0 0 0 0-563.2zM153.6 102.4a51.2 51.2 0 1 1 0 102.4 51.2 51.2 0 0 1 0-102.4z" fill="#ADAEB8" p-id="20393"></path></svg>
|
||||
<?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="1597744457882" class="icon" viewBox="0 0 1170 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10125" width="285.64453125" height="250" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"></style></defs><path d="M926.216214 404.588947l1.511115-151.84272H1079.570048c18.035893 0 33.098301-15.062408 33.098301-33.0983a33.390775 33.390775 0 0 0-33.098301-33.098301H927.727329V33.195792a33.390775 33.390775 0 0 0-33.049555-33.049555 33.390775 33.390775 0 0 0-33.0983 33.049555c0 3.022231 0 4.533346 1.511115 7.555577v144.287142H711.24787a33.390775 33.390775 0 0 0-33.098301 33.147046c0 18.035893 15.062408 33.049555 33.098301 33.049555H863.090589v139.802542c0 3.022231-1.46237 4.533346-1.46237 7.506831 0 18.035893 15.013662 33.098301 33.049555 33.098301 15.013662 1.46237 28.564955-10.529062 31.53844-27.053839zM1032.969201 790.995763a29.393631 29.393631 0 0 0-22.520493 10.480316 546.000095 546.000095 0 0 1-117.282049 88.717095 29.101157 29.101157 0 0 0-15.062408 25.591469c0 16.524777 13.551292 30.027324 30.07607 30.027324 4.533346 0 10.529062-1.46237 13.551292-2.973485 1.46237 0 1.46237-1.511115 1.46237-1.511115a523.235874 523.235874 0 0 0 127.811111-97.735041c6.044461-5.995716 10.529062-13.551292 10.529061-22.569238a28.028752 28.028752 0 0 0-28.564954-30.027325zM793.920503 935.282906c-2.973485 0-7.506831 0-10.529062 1.511115-46.600847 15.062408-96.223925 24.080354-145.798258 27.053839-15.062408 1.511115-25.59147 15.062408-25.591469 30.07607 0 16.573523 13.551292 30.07607 30.07607 30.07607 55.618794-4.4846 108.215357-13.502547 159.398295-30.07607a28.95492 28.95492 0 0 0 22.520494-28.564954 30.173561 30.173561 0 0 0-30.07607-30.07607zM535.324796 960.874375c-12.040177-1.511115-24.031609-1.511115-36.071785-3.02223a564.962155 564.962155 0 0 1-49.623078-7.506832c-230.030752-45.089732-389.380302-202.976912-389.380302-380.362355 0-139.851288 99.19741-269.124768 260.106822-339.805969 13.502547-5.995716 27.053839-12.040177 40.556386-16.573523 10.529062-4.4846 22.569239-7.506831 33.0983-10.480317 12.040177-4.533346 21.058123-15.062408 21.058124-28.564954a30.173561 30.173561 0 0 0-30.07607-30.07607 25.396487 25.396487 0 0 0-15.062408 4.4846c-10.529062 3.022231-19.498262 6.044461-28.564955 9.017947l-45.089732 18.035893C114.357309 254.257343 0.097491 406.148808 0.097491 568.471843c0 207.510258 180.407674 387.917932 439.00338 440.563241l54.107679 9.017946c12.040177 1.46237 25.59147 3.022231 37.631646 3.022231h1.46237c16.573523 0 30.07607-13.551292 30.07607-30.07607a30.758509 30.758509 0 0 0-27.05384-30.07607zM1169.798258 569.982958a30.173561 30.173561 0 0 0-30.07607-30.07607 30.173561 30.173561 0 0 0-30.07607 30.07607v1.46237c0 46.649593-10.529062 93.25044-33.049555 136.829057-1.511115 3.022231-1.511115 7.506831-1.511115 12.040177 0 16.573523 13.551292 30.07607 30.07607 30.07607 13.502547 0 24.031609-9.017946 28.564954-21.058124a361.985243 361.985243 0 0 0 36.071786-157.88718v-1.46237z" p-id="10126"></path><path d="M1052.516209 297.835959c-5.995716-4.533346-13.502547-9.017946-22.520493-9.017946a30.173561 30.173561 0 0 0-30.07607 30.07607c0 7.506831 2.973485 15.013662 9.017946 21.009378a375.14657 375.14657 0 0 1 79.699148 118.793164c4.4846 10.529062 15.013662 19.547008 27.053839 19.547008 16.524777 0 30.07607-13.551292 30.07607-30.07607 0-4.4846-1.511115-7.506831-3.02223-12.040177a422.039891 422.039891 0 0 0-90.179464-138.291427zM666.158138 182.065026h4.4846c16.573523 0 30.07607-13.551292 30.07607-30.07607 0-15.013662-12.040177-28.564955-27.053839-28.564954v-1.46237a761.602133 761.602133 0 0 0-154.86495-1.559861h-3.022231a30.173561 30.173561 0 0 0-30.07607 30.07607c0 16.573523 13.551292 30.07607 30.07607 30.07607h3.022231v1.462369c46.600847-5.94697 97.735041-4.4846 147.358119 0z" p-id="10127"></path></svg>
|
||||
|
Before Width: | Height: | Size: 623 B After Width: | Height: | Size: 3.8 KiB |
|
|
@ -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="1777014169186" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4736" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M1024 739.560727V1024H739.560727v-84.898909h199.540364v-199.540364H1024z m-939.101091 0v199.540364h199.540364V1024H0V739.560727h84.898909zM824.878545 199.121455v625.75709H199.121455V199.121455h625.75709z m-85.317818 85.317818H284.439273v455.121454h455.121454V284.439273zM1024 0v284.439273h-84.898909V84.898909h-199.540364V0H1024zM284.439273 0v84.898909H84.898909v199.540364H0V0h284.439273z" fill="#e6e6e6" p-id="4737"></path></svg>
|
||||
|
After Width: | Height: | Size: 764 B |
|
|
@ -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 |
|
|
@ -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 +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="1609205172733" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2919" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M320 640v256H256v-192H64v-64h256z m640 0v64h-192v192h-64v-256h256zM512 384a128 128 0 1 1 0 256 128 128 0 0 1 0-256zM320 128v256H64V320h192V128h64z m448 0v192h192v64h-256V128h64z" p-id="2920"></path></svg>
|
||||
<?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="1777426164195" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5541" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M316 672h60c4.4 0 8-3.6 8-8V360c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v304c0 4.4 3.6 8 8 8zM512 622c22.1 0 40-17.9 40-39 0-23.1-17.9-41-40-41s-40 17.9-40 41c0 21.1 17.9 39 40 39zM512 482c22.1 0 40-17.9 40-39 0-23.1-17.9-41-40-41s-40 17.9-40 41c0 21.1 17.9 39 40 39z" p-id="5542"></path><path d="M880 112H144c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V144c0-17.7-14.3-32-32-32z m-40 728H184V184h656v656z" p-id="5543"></path><path d="M648 672h60c4.4 0 8-3.6 8-8V360c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v304c0 4.4 3.6 8 8 8z" p-id="5544"></path></svg>
|
||||
|
Before Width: | Height: | Size: 581 B After Width: | Height: | Size: 908 B |
|
|
@ -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="1777352764855" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10736" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M587.808 383.392l-102.208 102.208A37.472 37.472 0 0 0 512 549.408a36.288 36.288 0 0 0 26.272-11.104l315.84-315.232a454.976 454.976 0 1 0 43.52 61.888l-54.688 54.752a372.704 372.704 0 1 1-94.816-116.928l-106.56 106.496-53.792 54.144z" p-id="10737"></path></svg>
|
||||
|
After Width: | Height: | Size: 594 B |
|
|
@ -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="1775115523115" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5347" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M458.105263 970.105263h-215.578947V264.084211h215.578947V970.105263z m-161.68421-53.894737h107.789473V317.978947h-107.789473V916.210526z" fill="#ffffff" p-id="5348"></path><path d="M296.421053 970.105263h-215.578948V479.663158h215.578948V970.105263z m-161.684211-53.894737h107.789474V533.557895h-107.789474V916.210526zM943.157895 970.105263h-215.578948V479.663158h215.578948V970.105263z m-161.684211-53.894737h107.789474V533.557895h-107.789474V916.210526zM619.789474 970.105263h-215.578948V53.894737h215.578948v916.210526z m-161.684211-53.894737h107.789474V107.789474h-107.789474v808.421052z" fill="#ffffff" p-id="5349"></path><path d="M781.473684 970.105263h-215.578947V264.084211h215.578947V970.105263z m-161.68421-53.894737h107.789473V317.978947h-107.789473V916.210526z" fill="#ffffff" p-id="5350"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
|
@ -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 |
|
|
@ -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 |
|
|
@ -1 +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="1597744457882" class="icon" viewBox="0 0 1170 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10125" width="285.64453125" height="250" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"></style></defs><path d="M926.216214 404.588947l1.511115-151.84272H1079.570048c18.035893 0 33.098301-15.062408 33.098301-33.0983a33.390775 33.390775 0 0 0-33.098301-33.098301H927.727329V33.195792a33.390775 33.390775 0 0 0-33.049555-33.049555 33.390775 33.390775 0 0 0-33.0983 33.049555c0 3.022231 0 4.533346 1.511115 7.555577v144.287142H711.24787a33.390775 33.390775 0 0 0-33.098301 33.147046c0 18.035893 15.062408 33.049555 33.098301 33.049555H863.090589v139.802542c0 3.022231-1.46237 4.533346-1.46237 7.506831 0 18.035893 15.013662 33.098301 33.049555 33.098301 15.013662 1.46237 28.564955-10.529062 31.53844-27.053839zM1032.969201 790.995763a29.393631 29.393631 0 0 0-22.520493 10.480316 546.000095 546.000095 0 0 1-117.282049 88.717095 29.101157 29.101157 0 0 0-15.062408 25.591469c0 16.524777 13.551292 30.027324 30.07607 30.027324 4.533346 0 10.529062-1.46237 13.551292-2.973485 1.46237 0 1.46237-1.511115 1.46237-1.511115a523.235874 523.235874 0 0 0 127.811111-97.735041c6.044461-5.995716 10.529062-13.551292 10.529061-22.569238a28.028752 28.028752 0 0 0-28.564954-30.027325zM793.920503 935.282906c-2.973485 0-7.506831 0-10.529062 1.511115-46.600847 15.062408-96.223925 24.080354-145.798258 27.053839-15.062408 1.511115-25.59147 15.062408-25.591469 30.07607 0 16.573523 13.551292 30.07607 30.07607 30.07607 55.618794-4.4846 108.215357-13.502547 159.398295-30.07607a28.95492 28.95492 0 0 0 22.520494-28.564954 30.173561 30.173561 0 0 0-30.07607-30.07607zM535.324796 960.874375c-12.040177-1.511115-24.031609-1.511115-36.071785-3.02223a564.962155 564.962155 0 0 1-49.623078-7.506832c-230.030752-45.089732-389.380302-202.976912-389.380302-380.362355 0-139.851288 99.19741-269.124768 260.106822-339.805969 13.502547-5.995716 27.053839-12.040177 40.556386-16.573523 10.529062-4.4846 22.569239-7.506831 33.0983-10.480317 12.040177-4.533346 21.058123-15.062408 21.058124-28.564954a30.173561 30.173561 0 0 0-30.07607-30.07607 25.396487 25.396487 0 0 0-15.062408 4.4846c-10.529062 3.022231-19.498262 6.044461-28.564955 9.017947l-45.089732 18.035893C114.357309 254.257343 0.097491 406.148808 0.097491 568.471843c0 207.510258 180.407674 387.917932 439.00338 440.563241l54.107679 9.017946c12.040177 1.46237 25.59147 3.022231 37.631646 3.022231h1.46237c16.573523 0 30.07607-13.551292 30.07607-30.07607a30.758509 30.758509 0 0 0-27.05384-30.07607zM1169.798258 569.982958a30.173561 30.173561 0 0 0-30.07607-30.07607 30.173561 30.173561 0 0 0-30.07607 30.07607v1.46237c0 46.649593-10.529062 93.25044-33.049555 136.829057-1.511115 3.022231-1.511115 7.506831-1.511115 12.040177 0 16.573523 13.551292 30.07607 30.07607 30.07607 13.502547 0 24.031609-9.017946 28.564954-21.058124a361.985243 361.985243 0 0 0 36.071786-157.88718v-1.46237z" p-id="10126"></path><path d="M1052.516209 297.835959c-5.995716-4.533346-13.502547-9.017946-22.520493-9.017946a30.173561 30.173561 0 0 0-30.07607 30.07607c0 7.506831 2.973485 15.013662 9.017946 21.009378a375.14657 375.14657 0 0 1 79.699148 118.793164c4.4846 10.529062 15.013662 19.547008 27.053839 19.547008 16.524777 0 30.07607-13.551292 30.07607-30.07607 0-4.4846-1.511115-7.506831-3.02223-12.040177a422.039891 422.039891 0 0 0-90.179464-138.291427zM666.158138 182.065026h4.4846c16.573523 0 30.07607-13.551292 30.07607-30.07607 0-15.013662-12.040177-28.564955-27.053839-28.564954v-1.46237a761.602133 761.602133 0 0 0-154.86495-1.559861h-3.022231a30.173561 30.173561 0 0 0-30.07607 30.07607c0 16.573523 13.551292 30.07607 30.07607 30.07607h3.022231v1.462369c46.600847-5.94697 97.735041-4.4846 147.358119 0z" p-id="10127"></path></svg>
|
||||
<?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="1777352032068" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5387" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M924.8 337.8c-22.6-53.4-54.9-101.3-96-142.4s-89-73.4-142.4-96C631.2 76.1 572.5 64.2 512 64.2S392.9 76.1 337.6 99.4c-53.4 22.6-101.3 54.9-142.4 96s-73.4 89-96 142.4C75.9 393.1 64 451.8 64 512.2c0 60.5 11.8 119.1 35.2 174.4 22.6 53.4 54.9 101.3 96 142.4 41.1 41.1 89 73.4 142.4 96 55.3 23.4 113.9 35.2 174.4 35.2s119.1-11.8 174.4-35.2c53.4-22.6 101.3-54.9 142.4-96 41.1-41.1 73.4-89 96-142.4 23.4-55.3 35.2-113.9 35.2-174.4 0-60.4-11.8-119.1-35.2-174.4zM780.3 780.5C708.6 852.2 613.4 891.6 512 891.6c-101.3 0-196.6-39.5-268.3-111.1S132.6 613.6 132.6 512.2c0-101.3 39.5-196.6 111.1-268.3s167-111.1 268.3-111.1c101.3 0 196.6 39.5 268.3 111.1s111.1 166.9 111.1 268.3S852 708.8 780.3 780.5z" p-id="5388"></path><path d="M512 512.2m-106.2 0a106.2 106.2 0 1 0 212.4 0 106.2 106.2 0 1 0-212.4 0Z" p-id="5389"></path></svg>
|
||||
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 1.1 KiB |
|
|
@ -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="1779246817161" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5348" width="200" height="200" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M473 71a8 8 0 0 1 8-8h64a8 8 0 0 1 8 8v77.161C722.848 166.622 857.812 301.289 876.729 471H952a8 8 0 0 1 8 8v64a8 8 0 0 1-8 8h-75.053C858.871 721.652 723.515 857.305 553 875.839V951a8 8 0 0 1-8 8h-64a8 8 0 0 1-8-8v-75.161C302.818 857.341 167.659 722.182 149.161 552H72a8 8 0 0 1-8-8v-64a8 8 0 0 1 8-8h77.161C167.659 301.818 302.818 166.659 473 148.161V71z m326 441c0-157.953-128.047-286-286-286S227 354.047 227 512s128.047 286 286 286 286-128.047 286-286z m-286 60c33.137 0 60-26.863 60-60s-26.863-60-60-60-60 26.863-60 60 26.863 60 60 60z" fill="#ffffff" p-id="5349"></path></svg>
|
||||
|
After Width: | Height: | Size: 913 B |
|
|
@ -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="1777432778355" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2607" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M954.026667 913.6128H600.746667a35.908267 35.908267 0 0 1-35.771734-35.771733V116.736a35.703467 35.703467 0 0 1 68.266667-15.018667l353.143467 761.0368a35.566933 35.566933 0 0 1-2.2528 34.269867 35.362133 35.362133 0 0 1-30.173867 16.5888z" fill="#FFFFFF" p-id="2608"></path><path d="M465.373867 913.544533H112.162133a35.6352 35.6352 0 0 1-32.494933-50.722133L432.878933 101.717333a35.6352 35.6352 0 0 1 40.277334-19.8656 35.566933 35.566933 0 0 1 27.989333 34.952534V877.909333a35.771733 35.771733 0 0 1-35.771733 35.6352z" fill="#FFFFFF" fill-opacity=".4" p-id="2609"></path></svg>
|
||||
|
After Width: | Height: | Size: 916 B |
|
|
@ -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="1777426622063" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8288" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M667.69962 294.974152l0 589.688383 316.384787 0L667.69962 294.974152zM632.522446 884.662535 632.522446 572.426231 34.882973 884.662535 632.522446 884.662535zM510.16777 357.862457l-137.677663-31.323403c0 0 97.523127-217.392192 349.512279-98.842169 0 0-196.516746-248.618381-487.190965 67.571978l-137.677663-31.273261 158.803819 250.082732L510.16777 357.862457z" p-id="8289"></path></svg>
|
||||
|
After Width: | Height: | Size: 719 B |
|
|
@ -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="1777426691300" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2490" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M34.884 883.64h316.385V293.95L34.884 883.64z m949.201 0L386.446 571.402v312.236h597.64zM763.03 513.053l158.805-250.083-137.678 31.274c-290.674-316.19-487.191-67.572-487.191-67.572 251.99-118.55 349.512 98.842 349.512 98.842l-137.677 31.323L763.03 513.054z" fill="" p-id="2491"></path></svg>
|
||||
|
After Width: | Height: | Size: 623 B |
|
|
@ -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="1777432945307" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4263" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M106.496 928.426667V575.2832c0-19.729067 16.042667-35.771733 35.771733-35.771733h761.0368a35.703467 35.703467 0 0 1 15.086934 68.266666L157.354667 960.9216a35.566933 35.566933 0 0 1-34.269867-2.321067 35.362133 35.362133 0 0 1-16.5888-30.173866z" fill="#FFFFFF" p-id="4264"></path><path d="M106.496 439.842133V86.6304a35.6352 35.6352 0 0 1 50.7904-32.494933l761.0368 353.211733a35.6352 35.6352 0 0 1 19.933867 40.209067 35.566933 35.566933 0 0 1-34.952534 27.989333H142.199467a35.771733 35.771733 0 0 1-35.703467-35.703467z" fill="#FFFFFF" fill-opacity=".4" p-id="4265"></path></svg>
|
||||
|
After Width: | Height: | Size: 916 B |
|
|
@ -0,0 +1 @@
|
|||
<svg width="24px" height="24px" viewBox="0 0 24 24" class="h-6 w-6"><g id="tool-seg-threshold" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><rect id="Rectangle" x="0" y="0" width="24" height="24"></rect><g id="Group" transform="translate(2.4184, 14.3673)" stroke="currentColor" stroke-width="1.5"><line x1="16.5816327" y1="2.13265306" x2="19.0816327" y2="2.13265306" id="Line-2" stroke-linecap="round"></line><line x1="0.0816326531" y1="2.13265306" x2="12.0816327" y2="2.13265306" id="Line-2" stroke-linecap="round"></line><circle id="Oval" cx="14.2244898" cy="2.09183673" r="2.09183673"></circle></g><g id="Group" transform="translate(11.7092, 7.4592) scale(-1, 1) translate(-11.7092, -7.4592)translate(1.9184, 5.3673)" stroke="currentColor" stroke-width="1.5"><line x1="16" y1="2.13265306" x2="19.0816327" y2="2.13265306" id="Line-2" stroke-linecap="round"></line><line x1="0.0816326531" y1="2.13265306" x2="11" y2="2.13265306" id="Line-2" stroke-linecap="round"></line><circle id="Oval" cx="13.2244898" cy="2.09183673" r="2.09183673"></circle></g></g></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
|
@ -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;
|
||||
|
|
|
|||
23
src/main.js
|
|
@ -1,5 +1,6 @@
|
|||
|
||||
import Vue from 'vue'
|
||||
// import DicomEvent from '@/views/trials/trials-panel/reading/dicoms/components/DicomEvent'
|
||||
import 'normalize.css/normalize.css' // A modern alternative to CSS resets
|
||||
// import { createVersionPolling } from "@/utils/version-polling.esm.js";
|
||||
import ElementUI, { MessageBox } from 'element-ui'
|
||||
|
|
@ -70,6 +71,9 @@ import FB from '@/components/feedBack/index'
|
|||
Vue.use(FB)
|
||||
import FBT from '@/components/feedBackTable/index'
|
||||
Vue.use(FBT)
|
||||
import resize from '@/directive/resize/index'
|
||||
// 表格自适应指令
|
||||
Vue.use(resize)
|
||||
import adaptive from '@/directive/adaptive/index'
|
||||
// 表格自适应指令
|
||||
Vue.use(adaptive)
|
||||
|
|
@ -334,8 +338,10 @@ async function VueInit() {
|
|||
})
|
||||
}
|
||||
let CompanyInfo = JSON.parse(localStorage.getItem('CompanyInfo'))
|
||||
if (CompanyInfo && CompanyInfo.SystemShortName && text.toUpperCase() !== "CIRCLE") {
|
||||
let test = new RegExp('IRC', 'ig')
|
||||
if (CompanyInfo && CompanyInfo.SystemShortName && (text.toUpperCase() !== "CIRCLE")) {
|
||||
// 精准匹配独立单词 IRC
|
||||
// let test = new RegExp('IRC', 'ig')
|
||||
let test = new RegExp('\\bIRC\\b', 'ig')
|
||||
text = text.replace(test, CompanyInfo.SystemShortName)
|
||||
}
|
||||
// return i18n.t(key)
|
||||
|
|
@ -446,6 +452,7 @@ async function VueInit() {
|
|||
isOpen = false
|
||||
isLock = null
|
||||
zzSessionStorage.removeItem('isLock')
|
||||
// DicomEvent.$emit('isLock', false)
|
||||
router.push("/login")
|
||||
}).then(() => {
|
||||
// _vm.$alert(lang === 'zh' ? '由于您长时间未操作,为保护您的数据安全已强制将您下线,如果需要继续操作请重新登陆!' : 'No operation for a long time non-operation, you have been forced logout to protect data security. If continue to operate, please login again!', {
|
||||
|
|
@ -457,7 +464,8 @@ async function VueInit() {
|
|||
})
|
||||
|
||||
} : () => { }, process.env.VUE_APP_LOGOUT_FOR_TIME,
|
||||
eval(process.env.VUE_APP_LOCK_FOR_PERMISSION) ? () => {
|
||||
eval(process.env.VUE_APP_LOCK_FOR_PERMISSION)
|
||||
? () => {
|
||||
var lang = zzSessionStorage.getItem('lang') ? zzSessionStorage.getItem('lang') : 'zh'
|
||||
if (_vm.$store.state.trials.unlock || WHITELIST.includes(_vm.$route.path)) {
|
||||
count = 0;
|
||||
|
|
@ -465,6 +473,7 @@ async function VueInit() {
|
|||
if (_vm.$route.path === '/login') {
|
||||
zzSessionStorage.removeItem('lastWorkbench')
|
||||
zzSessionStorage.removeItem('isLock')
|
||||
// DicomEvent.$emit('isLock', false)
|
||||
isLock = null
|
||||
}
|
||||
return
|
||||
|
|
@ -474,6 +483,7 @@ async function VueInit() {
|
|||
}
|
||||
isOpen = true
|
||||
zzSessionStorage.setItem('isLock', 'true')
|
||||
// DicomEvent.$emit('isLock', true)
|
||||
_vm.$msgbox({
|
||||
title: _vm.$t("env:lock:msgBox:title"),
|
||||
confirmButtonText: _vm.$t("env:lock:msgBox:confirmButtonText"),
|
||||
|
|
@ -505,7 +515,8 @@ async function VueInit() {
|
|||
isOpen = false
|
||||
count = 0;
|
||||
isLock = null
|
||||
zzSessionStorage.removeItem('isLock')
|
||||
// zzSessionStorage.removeItem('isLock')
|
||||
// DicomEvent.$emit('isLock', false)
|
||||
localStorage.setItem('count', '0')
|
||||
document.querySelector('#my_username').value = null
|
||||
document.querySelector('#my_password').value = null
|
||||
|
|
@ -610,7 +621,9 @@ async function VueInit() {
|
|||
])
|
||||
])
|
||||
})
|
||||
} : () => { }, process.env.VUE_APP_LOCK_FOR_TIME)
|
||||
} : () => { },
|
||||
process.env.VUE_APP_LOCK_FOR_TIME
|
||||
)
|
||||
}
|
||||
VueInit()
|
||||
// createVersionPolling({
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ function getQuestions(questions) {
|
|||
answerObj.angle = angle
|
||||
answerObj.saveTypeEnum = isNaN(parseFloat(angle)) ? 1 : 2
|
||||
}
|
||||
} else if (criterionType === 21) {
|
||||
} else if (criterionType === 21 || criterionType === 22) {
|
||||
// MRI-PDFF
|
||||
let isMeasurable = getQuestionAnswer(item.TableQuestions.Questions, 1105, answerObj)
|
||||
answerObj.isMeasurable = isMeasurable
|
||||
|
|
@ -699,7 +699,7 @@ const actions = {
|
|||
var measureData = state.visitTaskList[index].MeasureData
|
||||
// var idx = measureData.findIndex(item => item.MeasureData.uuid === obj.data.MeasureData.data.uuid)
|
||||
|
||||
if (criterionType === 21) {
|
||||
if (criterionType === 21 || criterionType === 22) {
|
||||
let i = measureData.findIndex(i => i.TableQuestionId === obj.data.TableQuestionId)
|
||||
if (i > -1) {
|
||||
for (const k in state.visitTaskList[index].MeasureData[i]) {
|
||||
|
|
@ -932,6 +932,12 @@ const actions = {
|
|||
data.IsDicom = study.IsDicom
|
||||
data.PreviewImageCount = 0
|
||||
data.IsCriticalSequence = study.IsCriticalSequence
|
||||
data.PatientSex = study.PatientSex
|
||||
data.PatientWeight = study.PatientWeight
|
||||
data.RadionuclideHalfLife = study.RadionuclideHalfLife
|
||||
data.RadionuclideTotalDose = study.RadionuclideTotalDose
|
||||
data.RadiopharmaceuticalStartTime = study.RadiopharmaceuticalStartTime
|
||||
data.AcquisitionTime = study.AcquisitionTime
|
||||
var seriesList = []
|
||||
study.SeriesList.forEach((series, seriesIndex) => {
|
||||
const imageIds = []
|
||||
|
|
|
|||
|
|
@ -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,139 +206,143 @@ 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
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.el-descriptions__body .el-descriptions__table .el-descriptions-item__cell.is-center {
|
||||
text-align: center
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.el-descriptions__body .el-descriptions__table .el-descriptions-item__cell.is-right {
|
||||
text-align: 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 {
|
||||
.el-dialog__wrapper {
|
||||
z-index: 3999 !important;
|
||||
}
|
||||
|
||||
.el-popper {
|
||||
z-index: 4000 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.feedBack-select-box {
|
||||
|
|
@ -350,7 +355,7 @@ body .el-table th.gutter {
|
|||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: .5;
|
||||
opacity: 0.5;
|
||||
background: #000;
|
||||
z-index: 3999;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import Vue from 'vue'
|
||||
import { anonymization } from './anonymization'
|
||||
export const dcmUpload = async function (data, config, progressFn) {
|
||||
export const dcmUpload = async function (data, config, progressFn, fileInfo) {
|
||||
return new Promise(async resolve => {
|
||||
try {
|
||||
// let blob = await encoder(file, config)
|
||||
|
|
@ -8,7 +8,8 @@ export const dcmUpload = async function (data, config, progressFn) {
|
|||
if (config) {
|
||||
blob = await anonymization(data.file, config)
|
||||
}
|
||||
let res = await Vue.prototype.OSSclient.multipartUpload(Object.assign(data, { file: blob.blob }), progressFn)
|
||||
|
||||
let res = await Vue.prototype.OSSclient.multipartUpload(Object.assign(data, { file: blob.blob }), progressFn, fileInfo)
|
||||
resolve({
|
||||
...res,
|
||||
image: blob.pixelDataElement
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,343 @@
|
|||
import getNumberValues from "./getNumberValues";
|
||||
import isNMReconstructable from "./isNMReconstructable";
|
||||
|
||||
/**
|
||||
* Get a subpart of Image Type dicom tag defined by index
|
||||
* @param {*} dataSet
|
||||
* @param {*} index 0 based index of the subtype
|
||||
*/
|
||||
function getImageTypeSubItemFromDataset(dataSet, index) {
|
||||
const imageType = dataSet.string("x00080008");
|
||||
|
||||
if (imageType) {
|
||||
const subTypes = imageType.split("\\");
|
||||
|
||||
if (subTypes.length > index) {
|
||||
return subTypes[index];
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
// DICOM 标准中针对 Enhanced CT/MR/PET 等“增强型多帧”(Enhanced Multi-frame)影像, 强制要求为每一帧显式记录真实的独立空间坐标 。
|
||||
|
||||
// - 它们的数据通常存放在: Per-frame Functional Groups Sequence (5200,9230) -> 对应的 Frame Item -> Plane Position Sequence (0020,9113) -> ImagePositionPatient (0020,0032) 。
|
||||
function getSharedFunctionalGroupsDataSet(dataSet) {
|
||||
const seq = dataSet?.elements?.x52009229;
|
||||
if (!seq?.items?.length) {
|
||||
return;
|
||||
}
|
||||
return seq.items[0].dataSet;
|
||||
}
|
||||
|
||||
function getPerFrameFunctionalGroupsDataSet(dataSet, frameIndex) {
|
||||
const seq = dataSet?.elements?.x52009230;
|
||||
if (!seq?.items?.length) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
!Number.isInteger(frameIndex) ||
|
||||
frameIndex < 0 ||
|
||||
frameIndex >= seq.items.length
|
||||
) {
|
||||
return;
|
||||
}
|
||||
return seq.items[frameIndex].dataSet;
|
||||
}
|
||||
|
||||
function getOrientationFromPlaneOrientationSequence(dataSet) {
|
||||
const seq = dataSet?.elements?.x00209116;
|
||||
if (!seq?.items?.length) {
|
||||
return;
|
||||
}
|
||||
return getNumberValues(seq.items[0].dataSet, "x00200037", 6);
|
||||
}
|
||||
|
||||
function getPositionFromPlanePositionSequence(dataSet) {
|
||||
const seq = dataSet?.elements?.x00209113;
|
||||
if (!seq?.items?.length) {
|
||||
return;
|
||||
}
|
||||
return getNumberValues(seq.items[0].dataSet, "x00200032", 3);
|
||||
}
|
||||
|
||||
function getPixelSpacingFromPixelMeasuresSequence(dataSet) {
|
||||
const seq = dataSet?.elements?.x00289110;
|
||||
if (!seq?.items?.length) {
|
||||
return;
|
||||
}
|
||||
return getNumberValues(seq.items[0].dataSet, "x00280030", 2);
|
||||
}
|
||||
|
||||
function getSliceThicknessFromPixelMeasuresSequence(dataSet) {
|
||||
const seq = dataSet?.elements?.x00289110;
|
||||
if (!seq?.items?.length) {
|
||||
return;
|
||||
}
|
||||
if (!seq.items[0]?.dataSet?.elements?.x00180050) {
|
||||
return;
|
||||
}
|
||||
return seq.items[0].dataSet.floatString("x00180050");
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the orientation from NM multiframe dataset, if image type
|
||||
* equal to RECON TOMO or RECON GATED TOMO
|
||||
* @param {*} dataSet
|
||||
* @returns
|
||||
*/
|
||||
function extractOrientationFromNMMultiframeDataset(dataSet) {
|
||||
let imageOrientationPatient;
|
||||
const modality = dataSet.string("x00080060");
|
||||
|
||||
if (modality?.includes("NM")) {
|
||||
const imageSubType = getImageTypeSubItemFromDataset(dataSet, 2);
|
||||
|
||||
if (imageSubType && isNMReconstructable(imageSubType)) {
|
||||
if (dataSet.elements.x00540022) {
|
||||
imageOrientationPatient = getNumberValues(
|
||||
dataSet.elements.x00540022.items[0].dataSet,
|
||||
"x00200037",
|
||||
6
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return imageOrientationPatient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the position from NM multiframe dataset, if image type
|
||||
* equal to RECON TOMO or RECON GATED TOMO
|
||||
* @param {*} dataSet
|
||||
* @returns
|
||||
*/
|
||||
function extractPositionFromNMMultiframeDataset(dataSet) {
|
||||
let imagePositionPatient;
|
||||
const modality = dataSet.string("x00080060");
|
||||
|
||||
if (modality?.includes("NM")) {
|
||||
const imageSubType = getImageTypeSubItemFromDataset(dataSet, 2);
|
||||
|
||||
if (imageSubType && isNMReconstructable(imageSubType)) {
|
||||
if (dataSet.elements.x00540022) {
|
||||
imagePositionPatient = getNumberValues(
|
||||
dataSet.elements.x00540022.items[0].dataSet,
|
||||
"x00200032",
|
||||
3
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return imagePositionPatient;
|
||||
}
|
||||
|
||||
function isMultiFrame(dataSet) {
|
||||
const numberOfFrames =
|
||||
dataSet.string("x00280008") || dataSet.uint16("x00280008");
|
||||
return numberOfFrames !== undefined && Number(numberOfFrames) > 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract orientation information from a dataset. It tries to get the orientation
|
||||
* from the Detector Information Sequence (for NM images) if image type equal
|
||||
* to RECON TOMO or RECON GATED TOMO
|
||||
* @param {*} dataSet
|
||||
* @returns
|
||||
*/
|
||||
function extractOrientationFromDataset(dataSet) {
|
||||
// let imageOrientationPatient;
|
||||
|
||||
// if (isMultiFrame(dataSet)) {
|
||||
// const perFrameDataSet = getPerFrameFunctionalGroupsDataSet(
|
||||
// dataSet,
|
||||
// frameIndex
|
||||
// );
|
||||
// imageOrientationPatient =
|
||||
// getOrientationFromPlaneOrientationSequence(perFrameDataSet);
|
||||
|
||||
// if (!imageOrientationPatient) {
|
||||
// const sharedDataSet = getSharedFunctionalGroupsDataSet(dataSet);
|
||||
// imageOrientationPatient =
|
||||
// getOrientationFromPlaneOrientationSequence(sharedDataSet);
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (!imageOrientationPatient) {
|
||||
// imageOrientationPatient = getNumberValues(dataSet, "x00200037", 6);
|
||||
// }
|
||||
let imageOrientationPatient = getNumberValues(dataSet, "x00200037", 6)
|
||||
// Trying to get the orientation from the Plane Orientation Sequence
|
||||
if (!imageOrientationPatient && dataSet.elements.x00209116) {
|
||||
imageOrientationPatient = getNumberValues(
|
||||
dataSet.elements.x00209116.items[0].dataSet,
|
||||
"x00200037",
|
||||
6
|
||||
);
|
||||
}
|
||||
|
||||
// If orientation not valid to this point, trying to get the orientation
|
||||
// from the Detector Information Sequence (for NM images) with image type
|
||||
// equal to RECON TOMO or RECON GATED TOMO
|
||||
|
||||
if (!imageOrientationPatient) {
|
||||
imageOrientationPatient =
|
||||
extractOrientationFromNMMultiframeDataset(dataSet);
|
||||
}
|
||||
|
||||
return imageOrientationPatient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract position information from a dataset. It tries to get the position
|
||||
* from the Detector Information Sequence (for NM images) if image type equal
|
||||
* to RECON TOMO or RECON GATED TOMO
|
||||
* @param {*} dataSet
|
||||
* @returns
|
||||
*/
|
||||
function extractPositionFromDataset(dataSet) {
|
||||
// let imagePositionPatient;
|
||||
|
||||
// if (isMultiFrame(dataSet)) {
|
||||
// const perFrameDataSet = getPerFrameFunctionalGroupsDataSet(
|
||||
// dataSet,
|
||||
// frameIndex
|
||||
// );
|
||||
// imagePositionPatient =
|
||||
// getPositionFromPlanePositionSequence(perFrameDataSet);
|
||||
|
||||
// if (!imagePositionPatient) {
|
||||
// const sharedDataSet = getSharedFunctionalGroupsDataSet(dataSet);
|
||||
// imagePositionPatient =
|
||||
// getPositionFromPlanePositionSequence(sharedDataSet);
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (!imagePositionPatient) {
|
||||
// imagePositionPatient = getNumberValues(dataSet, "x00200032", 3);
|
||||
// }
|
||||
let imagePositionPatient = getNumberValues(dataSet, 'x00200032', 3);
|
||||
// Trying to get the position from the Plane Position Sequence
|
||||
if (!imagePositionPatient && dataSet.elements.x00209113) {
|
||||
imagePositionPatient = getNumberValues(
|
||||
dataSet.elements.x00209113.items[0].dataSet,
|
||||
"x00200032",
|
||||
3
|
||||
);
|
||||
}
|
||||
|
||||
// If position not valid to this point, trying to get the position
|
||||
// from the Detector Information Sequence (for NM images)
|
||||
if (!imagePositionPatient) {
|
||||
imagePositionPatient = extractPositionFromNMMultiframeDataset(dataSet);
|
||||
}
|
||||
|
||||
return imagePositionPatient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the pixelSpacing information. If exists, extracts this information
|
||||
* from Pixel Measures Sequence
|
||||
* @param {*} dataSet
|
||||
* @returns
|
||||
*/
|
||||
function extractSpacingFromDataset(dataSet) {
|
||||
// let pixelSpacing = getNumberValues(dataSet, 'x00280030', 2);
|
||||
|
||||
// // If pixelSpacing not valid to this point, trying to get the spacing
|
||||
// // from the Pixel Measures Sequence
|
||||
// if (!pixelSpacing && dataSet.elements.x00289110) {
|
||||
// pixelSpacing = getNumberValues(
|
||||
// dataSet.elements.x00289110.items[0].dataSet,
|
||||
// 'x00280030',
|
||||
// 2
|
||||
// );
|
||||
// }
|
||||
|
||||
// return pixelSpacing;
|
||||
|
||||
let rowPixelSpacing = null;
|
||||
let columnPixelSpacing = null;
|
||||
// let pixelSpacing;
|
||||
|
||||
// if (isMultiFrame(dataSet)) {
|
||||
// const perFrameDataSet = getPerFrameFunctionalGroupsDataSet(
|
||||
// dataSet,
|
||||
// frameIndex
|
||||
// );
|
||||
// const sharedDataSet = getSharedFunctionalGroupsDataSet(dataSet);
|
||||
// pixelSpacing =
|
||||
// getPixelSpacingFromPixelMeasuresSequence(perFrameDataSet) ||
|
||||
// getPixelSpacingFromPixelMeasuresSequence(sharedDataSet);
|
||||
// }
|
||||
|
||||
// if (!pixelSpacing) {
|
||||
// pixelSpacing =
|
||||
// getNumberValues(dataSet, "x00280030", 2) ||
|
||||
// getPixelSpacingFromPixelMeasuresSequence(dataSet);
|
||||
// }
|
||||
let pixelSpacing = getNumberValues(dataSet, 'x00280030', 2);
|
||||
const imagePixelSpacing = getNumberValues(dataSet, "x00181164", 2);
|
||||
const estimatedRadiographicMagnificationFactor = getNumberValues(
|
||||
dataSet,
|
||||
"x00181114",
|
||||
2
|
||||
);
|
||||
if (pixelSpacing) {
|
||||
rowPixelSpacing = pixelSpacing[0];
|
||||
columnPixelSpacing = pixelSpacing[1];
|
||||
} else if (imagePixelSpacing && estimatedRadiographicMagnificationFactor) {
|
||||
rowPixelSpacing =
|
||||
imagePixelSpacing[0] / estimatedRadiographicMagnificationFactor[0];
|
||||
columnPixelSpacing =
|
||||
imagePixelSpacing[1] / estimatedRadiographicMagnificationFactor[1];
|
||||
} else if (imagePixelSpacing && !estimatedRadiographicMagnificationFactor) {
|
||||
rowPixelSpacing = imagePixelSpacing[0];
|
||||
columnPixelSpacing = imagePixelSpacing[1];
|
||||
}
|
||||
return rowPixelSpacing !== null
|
||||
? [rowPixelSpacing, columnPixelSpacing]
|
||||
: undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the sliceThickness information. If exists, extracts this information
|
||||
* from Pixel Measures Sequence
|
||||
* @param {*} dataSet
|
||||
* @returns
|
||||
*/
|
||||
function extractSliceThicknessFromDataset(dataSet) {
|
||||
let sliceThickness;
|
||||
if (dataSet.elements.x00180050) {
|
||||
sliceThickness = dataSet.floatString('x00180050');
|
||||
}
|
||||
else if (dataSet.elements.x00289110 &&
|
||||
dataSet.elements.x00289110.items.length &&
|
||||
dataSet.elements.x00289110.items[0].dataSet.elements.x00180050) {
|
||||
sliceThickness =
|
||||
dataSet.elements.x00289110.items[0].dataSet.floatString('x00180050');
|
||||
}
|
||||
|
||||
// if (sliceThickness === undefined && isMultiFrame(dataSet)) {
|
||||
// const perFrameDataSet = getPerFrameFunctionalGroupsDataSet(
|
||||
// dataSet,
|
||||
// frameIndex
|
||||
// );
|
||||
// const sharedDataSet = getSharedFunctionalGroupsDataSet(dataSet);
|
||||
// sliceThickness =
|
||||
// getSliceThicknessFromPixelMeasuresSequence(perFrameDataSet) ||
|
||||
// getSliceThicknessFromPixelMeasuresSequence(sharedDataSet);
|
||||
// }
|
||||
|
||||
return sliceThickness;
|
||||
}
|
||||
|
||||
export {
|
||||
getImageTypeSubItemFromDataset,
|
||||
extractOrientationFromDataset,
|
||||
extractPositionFromDataset,
|
||||
extractSpacingFromDataset,
|
||||
extractSliceThicknessFromDataset,
|
||||
};
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
function getNumberValues(
|
||||
dataSet,
|
||||
tag,
|
||||
minimumLength
|
||||
){
|
||||
const values = [];
|
||||
const valueAsString = dataSet.string(tag);
|
||||
|
||||
if (!valueAsString) {
|
||||
return;
|
||||
}
|
||||
const split = valueAsString.split('\\');
|
||||
|
||||
if (minimumLength && split.length < minimumLength) {
|
||||
return;
|
||||
}
|
||||
for (let i = 0; i < split.length; i++) {
|
||||
values.push(parseFloat(split[i]));
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
export default getNumberValues;
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
export default function isNMReconstructable(imageSubType) {
|
||||
return imageSubType === 'RECON TOMO' || imageSubType === 'RECON GATED TOMO';
|
||||
}
|
||||
|
|
@ -1,25 +1,123 @@
|
|||
import * as cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader'
|
||||
function parseImageId(imageId) {
|
||||
// build a url by parsing out the url scheme and frame index from the imageId
|
||||
const firstColonIndex = imageId.indexOf(':');
|
||||
import * as cornerstoneWADOImageLoader from "cornerstone-wado-image-loader";
|
||||
import store from "@/store";
|
||||
import { getPTClinicalDataForInstance } from "@/utils/ptClinicalDataCache";
|
||||
import {
|
||||
getImageTypeSubItemFromDataset,
|
||||
extractOrientationFromDataset,
|
||||
extractPositionFromDataset,
|
||||
extractSpacingFromDataset,
|
||||
extractSliceThicknessFromDataset,
|
||||
} from "./extractPositioningFromDataset";
|
||||
|
||||
let url = imageId.substring(firstColonIndex + 1);
|
||||
const frameIndex = url.indexOf('frame=');
|
||||
|
||||
let frame;
|
||||
|
||||
if (frameIndex !== -1) {
|
||||
const frameStr = url.substr(frameIndex + 6);
|
||||
|
||||
frame = parseInt(frameStr, 10);
|
||||
url = url.substr(0, frameIndex - 1);
|
||||
function toNumber(val) {
|
||||
if (val === undefined || val === null || val === "") return null;
|
||||
const n = typeof val === "number" ? val : parseFloat(val);
|
||||
return Number.isFinite(n) ? n : null;
|
||||
}
|
||||
|
||||
return {
|
||||
scheme: imageId.substr(0, firstColonIndex),
|
||||
url,
|
||||
frame,
|
||||
};
|
||||
function normalizeDicomTime(value) {
|
||||
if (value === undefined || value === null || value === "") return null;
|
||||
const num = toNumber(value);
|
||||
if (num != null) {
|
||||
return String(Math.floor(num)).padStart(6, "0");
|
||||
}
|
||||
if (typeof value !== "string") return null;
|
||||
const trimmed = value.trim();
|
||||
if (!trimmed) return null;
|
||||
const plain = trimmed.replace(/:/g, "");
|
||||
const parts = plain.split(".");
|
||||
const main = parts[0];
|
||||
const fractional = parts[1];
|
||||
if (!/^\d+$/.test(main)) {
|
||||
return trimmed;
|
||||
}
|
||||
const normalizedMain = main.padStart(6, "0");
|
||||
return fractional ? `${normalizedMain}.${fractional}` : normalizedMain;
|
||||
}
|
||||
|
||||
function getPTClinicalOverrideFromImageId(imageId) {
|
||||
try {
|
||||
const qIndex = imageId.indexOf("?");
|
||||
if (qIndex === -1) return null;
|
||||
const params = new URLSearchParams(imageId.slice(qIndex + 1));
|
||||
const visitTaskId = params.get("visitTaskId");
|
||||
const idx = params.get("idx");
|
||||
|
||||
// 场景1:trials/dicoms,imageId 带 visitTaskId + idx,优先使用列表中的最新 study 值
|
||||
if (visitTaskId && idx) {
|
||||
const parts = idx.split("|");
|
||||
const studyIndex = toNumber(parts[0]);
|
||||
if (Number.isInteger(studyIndex) && studyIndex >= 0) {
|
||||
const visitTaskList = store.state.reading.visitTaskList;
|
||||
if (Array.isArray(visitTaskList)) {
|
||||
const visitTaskInfo = visitTaskList.find(
|
||||
(v) => v && String(v.VisitTaskId) === visitTaskId
|
||||
);
|
||||
const study = visitTaskInfo?.StudyList?.[studyIndex];
|
||||
if (
|
||||
study &&
|
||||
!(
|
||||
study.PatientWeight == null &&
|
||||
study.RadionuclideTotalDose == null &&
|
||||
study.RadionuclideHalfLife == null &&
|
||||
study.RadiopharmaceuticalStartTime == null &&
|
||||
study.AcquisitionTime == null &&
|
||||
study.PatientSex == null
|
||||
)
|
||||
) {
|
||||
return study;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 场景2:src/components/Dicom,imageId 仅带 instanceId,回退到实例缓存
|
||||
const instanceId = params.get("instanceId");
|
||||
if (instanceId) {
|
||||
const cached = getPTClinicalDataForInstance(instanceId);
|
||||
if (cached) return cached;
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
function parseImageId(imageId) {
|
||||
// 兼容 frame 参数不在最后的情况:只移除 frame 参数本身,保留 instanceId/visitTaskId/idx 等其余 query
|
||||
const firstColonIndex = imageId.indexOf(":");
|
||||
const scheme = imageId.substr(0, firstColonIndex);
|
||||
const urlPart = imageId.substring(firstColonIndex + 1);
|
||||
let url = urlPart;
|
||||
let frame;
|
||||
const qIndex = urlPart.indexOf("?");
|
||||
if (qIndex !== -1) {
|
||||
const base = urlPart.slice(0, qIndex);
|
||||
const query = urlPart.slice(qIndex + 1);
|
||||
const parts = query.split("&").filter(Boolean);
|
||||
const preservedParts = [];
|
||||
|
||||
parts.forEach((part) => {
|
||||
const eqIndex = part.indexOf("=");
|
||||
const key = eqIndex === -1 ? part : part.slice(0, eqIndex);
|
||||
const value = eqIndex === -1 ? "" : part.slice(eqIndex + 1);
|
||||
|
||||
if (key === "frame") {
|
||||
const n = value !== "" ? parseInt(value, 10) : NaN;
|
||||
if (Number.isFinite(n)) {
|
||||
frame = n;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 保留原始 query 片段,避免 URLSearchParams 把 | 编码成 %7C 导致 dataSetCache key 对不上
|
||||
preservedParts.push(part);
|
||||
});
|
||||
|
||||
url = preservedParts.length ? `${base}?${preservedParts.join("&")}` : base;
|
||||
}
|
||||
|
||||
return { scheme, url, frame };
|
||||
}
|
||||
function getNumberValues(dataSet, tag, minimumLength) {
|
||||
const values = [];
|
||||
|
|
@ -28,7 +126,7 @@ function getNumberValues(dataSet, tag, minimumLength) {
|
|||
if (!valueAsString) {
|
||||
return;
|
||||
}
|
||||
const split = valueAsString.split('\\');
|
||||
const split = valueAsString.split("\\");
|
||||
|
||||
if (minimumLength && split.length < minimumLength) {
|
||||
return;
|
||||
|
|
@ -67,28 +165,31 @@ function getLutData(lutDataSet, tag, lutDescriptor) {
|
|||
return lut;
|
||||
}
|
||||
function populateSmallestLargestPixelValues(dataSet, imagePixelModule) {
|
||||
const pixelRepresentation = dataSet.uint16('x00280103');
|
||||
const pixelRepresentation = dataSet.uint16("x00280103");
|
||||
if (pixelRepresentation === 0) {
|
||||
imagePixelModule.smallestPixelValue = dataSet.uint16('x00280106');
|
||||
imagePixelModule.largestPixelValue = dataSet.uint16('x00280107');
|
||||
imagePixelModule.smallestPixelValue = dataSet.uint16("x00280106");
|
||||
imagePixelModule.largestPixelValue = dataSet.uint16("x00280107");
|
||||
} else {
|
||||
imagePixelModule.smallestPixelValue = dataSet.int16('x00280106');
|
||||
imagePixelModule.largestPixelValue = dataSet.int16('x00280107');
|
||||
imagePixelModule.smallestPixelValue = dataSet.int16("x00280106");
|
||||
imagePixelModule.largestPixelValue = dataSet.int16("x00280107");
|
||||
}
|
||||
imagePixelModule.largestPixelValue = imagePixelModule.largestPixelValue === 0 ? undefined : imagePixelModule.largestPixelValue;
|
||||
imagePixelModule.largestPixelValue =
|
||||
imagePixelModule.largestPixelValue === 0
|
||||
? undefined
|
||||
: imagePixelModule.largestPixelValue;
|
||||
}
|
||||
function populatePaletteColorLut(dataSet, imagePixelModule) {
|
||||
imagePixelModule.redPaletteColorLookupTableDescriptor = getLutDescriptor(
|
||||
dataSet,
|
||||
'x00281101'
|
||||
"x00281101"
|
||||
);
|
||||
imagePixelModule.greenPaletteColorLookupTableDescriptor = getLutDescriptor(
|
||||
dataSet,
|
||||
'x00281102'
|
||||
"x00281102"
|
||||
);
|
||||
imagePixelModule.bluePaletteColorLookupTableDescriptor = getLutDescriptor(
|
||||
dataSet,
|
||||
'x00281103'
|
||||
"x00281103"
|
||||
);
|
||||
|
||||
// The first Palette Color Lookup Table Descriptor value is the number of entries in the lookup table.
|
||||
|
|
@ -127,33 +228,196 @@ function populatePaletteColorLut(dataSet, imagePixelModule) {
|
|||
|
||||
imagePixelModule.redPaletteColorLookupTableData = getLutData(
|
||||
dataSet,
|
||||
'x00281201',
|
||||
"x00281201",
|
||||
imagePixelModule.redPaletteColorLookupTableDescriptor
|
||||
);
|
||||
imagePixelModule.greenPaletteColorLookupTableData = getLutData(
|
||||
dataSet,
|
||||
'x00281202',
|
||||
"x00281202",
|
||||
imagePixelModule.greenPaletteColorLookupTableDescriptor
|
||||
);
|
||||
imagePixelModule.bluePaletteColorLookupTableData = getLutData(
|
||||
dataSet,
|
||||
'x00281203',
|
||||
"x00281203",
|
||||
imagePixelModule.bluePaletteColorLookupTableDescriptor
|
||||
);
|
||||
}
|
||||
|
||||
function getSpacingBetweenSlices(dataSet) {
|
||||
if (dataSet?.elements?.x00180088) {
|
||||
return dataSet.floatString("x00180088");
|
||||
}
|
||||
const pixelMeasuresSequence = dataSet?.elements?.x00289110;
|
||||
if (
|
||||
pixelMeasuresSequence?.items?.length &&
|
||||
pixelMeasuresSequence.items[0]?.dataSet?.elements?.x00180088
|
||||
) {
|
||||
return pixelMeasuresSequence.items[0].dataSet.floatString("x00180088");
|
||||
}
|
||||
}
|
||||
function metaDataProvider(type, imageId) {
|
||||
const parsedImageId = parseImageId(imageId);
|
||||
const dataSet = cornerstoneWADOImageLoader.wadouri.dataSetCacheManager.get(parsedImageId.url);
|
||||
const dataSet = cornerstoneWADOImageLoader.wadouri.dataSetCacheManager.get(
|
||||
parsedImageId.url
|
||||
);
|
||||
|
||||
if (!dataSet) {
|
||||
return;
|
||||
}
|
||||
if (type === 'imagePlaneModule') {
|
||||
const imageOrientationPatient = getNumberValues(dataSet, 'x00200037', 6);
|
||||
const imagePositionPatient = getNumberValues(dataSet, 'x00200032', 3);
|
||||
const pixelSpacing = getNumberValues(dataSet, 'x00280030', 2);
|
||||
const imagePixelSpacing = getNumberValues(dataSet, 'x00181164', 2);
|
||||
const estimatedRadiographicMagnificationFactor = getNumberValues(dataSet, 'x00181114', 2);
|
||||
const ptOverride = getPTClinicalOverrideFromImageId(imageId);
|
||||
if (type === "generalSeriesModule") {
|
||||
const { dicomParser } = cornerstoneWADOImageLoader.external || {};
|
||||
const overrideTime = normalizeDicomTime(ptOverride?.AcquisitionTime);
|
||||
const seriesTimeRaw =
|
||||
overrideTime ||
|
||||
dataSet.string("x00080031") ||
|
||||
dataSet.string("x00080032") ||
|
||||
"";
|
||||
const acquisitionTimeRaw =
|
||||
overrideTime ||
|
||||
dataSet.string("x00080032") ||
|
||||
dataSet.string("x00080031") ||
|
||||
"";
|
||||
return {
|
||||
modality: dataSet.string("x00080060"),
|
||||
seriesInstanceUID: dataSet.string("x0020000e"),
|
||||
seriesNumber: dataSet.intString("x00200011"),
|
||||
studyInstanceUID: dataSet.string("x0020000d"),
|
||||
seriesDate: dicomParser.parseDA(dataSet.string("x00080021")),
|
||||
// 2D SUV 计算读取的是 seriesTime,这里同步使用用户录入采集时间
|
||||
// seriesTime: dicomParser.parseTM(String(seriesTimeRaw)),
|
||||
seriesTime: dicomParser.parseTM(dataSet.string('x00080031') || ''),
|
||||
acquisitionDate: dicomParser.parseDA(dataSet.string("x00080022") || ""),
|
||||
acquisitionTime: dicomParser.parseTM(String(acquisitionTimeRaw)),
|
||||
};
|
||||
}
|
||||
if (type === "patientStudyModule") {
|
||||
const weightOverride = ptOverride?.PatientWeight;
|
||||
const weight =
|
||||
weightOverride
|
||||
? toNumber(weightOverride)
|
||||
: dataSet.floatString("x00101030");
|
||||
return {
|
||||
patientAge: dataSet.intString("x00101010"),
|
||||
patientSize: dataSet.floatString("x00101020"),
|
||||
patientWeight: weight,
|
||||
};
|
||||
}
|
||||
|
||||
if (type === "petIsotopeModule") {
|
||||
const { dicomParser } = cornerstoneWADOImageLoader.external || {};
|
||||
const radiopharmaceuticalInfo = dataSet.elements.x00540016;
|
||||
|
||||
if (radiopharmaceuticalInfo === undefined) {
|
||||
return;
|
||||
}
|
||||
const firstRadiopharmaceuticalInfoDataSet =
|
||||
radiopharmaceuticalInfo.items[0].dataSet;
|
||||
|
||||
const startTimeRaw =
|
||||
firstRadiopharmaceuticalInfoDataSet.string("x00181072") || "";
|
||||
const totalDoseRaw =
|
||||
firstRadiopharmaceuticalInfoDataSet.floatString("x00181074");
|
||||
const halfLifeRaw =
|
||||
firstRadiopharmaceuticalInfoDataSet.floatString("x00181075");
|
||||
let startTimeValue = normalizeDicomTime(
|
||||
ptOverride?.RadiopharmaceuticalStartTime
|
||||
);
|
||||
if (!startTimeValue && startTimeRaw) {
|
||||
startTimeValue = normalizeDicomTime(startTimeRaw) || startTimeRaw;
|
||||
}
|
||||
const radiopharmaceuticalStartTime = dicomParser.parseTM(
|
||||
String(startTimeValue)
|
||||
);
|
||||
const overrideTotalDose =
|
||||
ptOverride?.RadionuclideTotalDose != null &&
|
||||
ptOverride.RadionuclideTotalDose !== ""
|
||||
? toNumber(ptOverride.RadionuclideTotalDose)
|
||||
: null;
|
||||
const overrideHalfLife =
|
||||
ptOverride?.RadionuclideHalfLife != null &&
|
||||
ptOverride.RadionuclideHalfLife !== ""
|
||||
? toNumber(ptOverride.RadionuclideHalfLife)
|
||||
: null;
|
||||
const radionuclideTotalDose =
|
||||
overrideTotalDose != null ? overrideTotalDose : toNumber(totalDoseRaw);
|
||||
const radionuclideHalfLife =
|
||||
overrideHalfLife != null ? overrideHalfLife : toNumber(halfLifeRaw);
|
||||
return {
|
||||
radiopharmaceuticalInfo: {
|
||||
radiopharmaceuticalStartTime,
|
||||
radionuclideTotalDose,
|
||||
radionuclideHalfLife,
|
||||
},
|
||||
};
|
||||
}
|
||||
if (type === "imagePlaneModule") {
|
||||
// const imageOrientationPatient = getNumberValues(dataSet, 'x00200037', 6);
|
||||
// const imagePositionPatient = getNumberValues(dataSet, 'x00200032', 3);
|
||||
// const pixelSpacing = getNumberValues(dataSet, 'x00280030', 2);
|
||||
const imagePixelSpacing = getNumberValues(dataSet, "x00181164", 2);
|
||||
const frameIndex =
|
||||
parsedImageId.frame !== undefined ? parsedImageId.frame - 1 : undefined;
|
||||
const imageOrientationPatient = extractOrientationFromDataset(
|
||||
dataSet,
|
||||
frameIndex
|
||||
);
|
||||
let imagePositionPatient = extractPositionFromDataset(dataSet, frameIndex);
|
||||
const pixelSpacing = extractSpacingFromDataset(dataSet, frameIndex);
|
||||
const sliceThickness = extractSliceThicknessFromDataset(
|
||||
dataSet,
|
||||
frameIndex
|
||||
);
|
||||
|
||||
const modality = dataSet.string("x00080060");
|
||||
if (
|
||||
modality &&
|
||||
modality.includes("NM") &&
|
||||
parsedImageId.frame !== undefined &&
|
||||
parsedImageId.frame > 1
|
||||
) {
|
||||
const spacingBetweenSlices = getSpacingBetweenSlices(dataSet);
|
||||
const step =
|
||||
spacingBetweenSlices !== undefined
|
||||
? spacingBetweenSlices
|
||||
: sliceThickness;
|
||||
if (
|
||||
imageOrientationPatient &&
|
||||
imagePositionPatient &&
|
||||
step !== undefined &&
|
||||
frameIndex !== undefined
|
||||
) {
|
||||
const rowCosines = [
|
||||
parseFloat(imageOrientationPatient[0]),
|
||||
parseFloat(imageOrientationPatient[1]),
|
||||
parseFloat(imageOrientationPatient[2]),
|
||||
];
|
||||
const columnCosines = [
|
||||
parseFloat(imageOrientationPatient[3]),
|
||||
parseFloat(imageOrientationPatient[4]),
|
||||
parseFloat(imageOrientationPatient[5]),
|
||||
];
|
||||
const normal = [
|
||||
rowCosines[1] * columnCosines[2] - rowCosines[2] * columnCosines[1],
|
||||
rowCosines[2] * columnCosines[0] - rowCosines[0] * columnCosines[2],
|
||||
rowCosines[0] * columnCosines[1] - rowCosines[1] * columnCosines[0],
|
||||
];
|
||||
|
||||
const offset = frameIndex * parseFloat(step);
|
||||
|
||||
imagePositionPatient = [
|
||||
parseFloat(imagePositionPatient[0]) + normal[0] * offset,
|
||||
parseFloat(imagePositionPatient[1]) + normal[1] * offset,
|
||||
parseFloat(imagePositionPatient[2]) + normal[2] * offset,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
const estimatedRadiographicMagnificationFactor = getNumberValues(
|
||||
dataSet,
|
||||
"x00181114",
|
||||
2
|
||||
);
|
||||
let columnPixelSpacing = null;
|
||||
|
||||
let rowPixelSpacing = null;
|
||||
|
|
@ -162,8 +426,10 @@ function metaDataProvider(type, imageId) {
|
|||
rowPixelSpacing = pixelSpacing[0];
|
||||
columnPixelSpacing = pixelSpacing[1];
|
||||
} else if (imagePixelSpacing && estimatedRadiographicMagnificationFactor) {
|
||||
rowPixelSpacing = imagePixelSpacing[0] / estimatedRadiographicMagnificationFactor[0];
|
||||
columnPixelSpacing = imagePixelSpacing[1] / estimatedRadiographicMagnificationFactor[1];
|
||||
rowPixelSpacing =
|
||||
imagePixelSpacing[0] / estimatedRadiographicMagnificationFactor[0];
|
||||
columnPixelSpacing =
|
||||
imagePixelSpacing[1] / estimatedRadiographicMagnificationFactor[1];
|
||||
} else if (imagePixelSpacing && !estimatedRadiographicMagnificationFactor) {
|
||||
rowPixelSpacing = imagePixelSpacing[0];
|
||||
columnPixelSpacing = imagePixelSpacing[1];
|
||||
|
|
@ -187,43 +453,48 @@ function metaDataProvider(type, imageId) {
|
|||
}
|
||||
|
||||
return {
|
||||
frameOfReferenceUID: dataSet.string('x00200052'),
|
||||
rows: dataSet.uint16('x00280010'),
|
||||
columns: dataSet.uint16('x00280011'),
|
||||
frameOfReferenceUID: dataSet.string("x00200052"),
|
||||
rows: dataSet.uint16("x00280010"),
|
||||
columns: dataSet.uint16("x00280011"),
|
||||
imageOrientationPatient,
|
||||
rowCosines,
|
||||
columnCosines,
|
||||
imagePositionPatient,
|
||||
sliceThickness: dataSet.floatString('x00180050'),
|
||||
sliceLocation: dataSet.floatString('x00201041'),
|
||||
sliceThickness,
|
||||
sliceLocation: dataSet.floatString("x00201041"),
|
||||
pixelSpacing,
|
||||
rowPixelSpacing,
|
||||
columnPixelSpacing,
|
||||
};
|
||||
}
|
||||
if (type === 'imagePixelModule') {
|
||||
if (type === "imagePixelModule") {
|
||||
const imagePixelModule = {
|
||||
samplesPerPixel: dataSet.uint16('x00280002'),
|
||||
photometricInterpretation: dataSet.string('x00280004'),
|
||||
rows: dataSet.uint16('x00280010'),
|
||||
columns: dataSet.uint16('x00280011'),
|
||||
bitsAllocated: dataSet.uint16('x00280100'),
|
||||
bitsStored: dataSet.uint16('x00280101'),
|
||||
highBit: dataSet.uint16('x00280102'),
|
||||
pixelRepresentation: dataSet.uint16('x00280103'),
|
||||
planarConfiguration: dataSet.uint16('x00280006'),
|
||||
pixelAspectRatio: dataSet.string('x00280034'),
|
||||
samplesPerPixel: dataSet.uint16("x00280002"),
|
||||
photometricInterpretation: dataSet.string("x00280004"),
|
||||
rows: dataSet.uint16("x00280010"),
|
||||
columns: dataSet.uint16("x00280011"),
|
||||
bitsAllocated: dataSet.uint16("x00280100"),
|
||||
bitsStored: dataSet.uint16("x00280101"),
|
||||
highBit: dataSet.uint16("x00280102"),
|
||||
pixelRepresentation: dataSet.uint16("x00280103"),
|
||||
planarConfiguration: dataSet.uint16("x00280006"),
|
||||
pixelAspectRatio: dataSet.string("x00280034"),
|
||||
};
|
||||
populateSmallestLargestPixelValues(dataSet, imagePixelModule);
|
||||
|
||||
if (
|
||||
imagePixelModule.photometricInterpretation === 'PALETTE COLOR' &&
|
||||
imagePixelModule.photometricInterpretation === "PALETTE COLOR" &&
|
||||
dataSet.elements.x00281101
|
||||
) {
|
||||
populatePaletteColorLut(dataSet, imagePixelModule);
|
||||
}
|
||||
return imagePixelModule;
|
||||
}
|
||||
// if (type === "multiframeModule") {
|
||||
// return {
|
||||
// NumberOfFrames: dataSet.uint16("x00280008") || dataSet.string("x00280008")
|
||||
// };
|
||||
// }
|
||||
// if (type === 'imagePixelModule') {
|
||||
// return {
|
||||
// samplesPerPixel: dataSet.uint16('x00280002'),
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ const stream = require('stream')
|
|||
import Vue from 'vue'
|
||||
import { customerHttp, OSSclose } from "@/utils/multipartUpload/oss"
|
||||
import { exist, AWSclose } from "@/utils/multipartUpload/aws"
|
||||
import { addOrUpdateFileUploadRecord } from '@/api/file'
|
||||
const { GetObjectStoreToken } = require('../api/user.js')
|
||||
const {
|
||||
S3Client,
|
||||
|
|
@ -22,7 +23,6 @@ async function ossGenerateSTS() {
|
|||
res = await GetObjectStoreToken()
|
||||
localStorage.setItem('stsToken', JSON.stringify(res))
|
||||
}
|
||||
|
||||
// res.Result.ObjectStoreUse = 'AWS';
|
||||
Vue.prototype.OSSclientConfig = { ...res.Result[res.Result.ObjectStoreUse] }
|
||||
Vue.prototype.OSSclientConfig.ObjectStoreUse = res.Result.ObjectStoreUse;
|
||||
|
|
@ -34,7 +34,8 @@ async function ossGenerateSTS() {
|
|||
Vue.prototype.OSSclientConfig.timeout = 10 * 60 * 1000
|
||||
let OSSclient = new OSS(Vue.prototype.OSSclientConfig)
|
||||
Vue.prototype.OSSclient = {
|
||||
put: async function (objectName, object) {
|
||||
put: async function (objectName, object, fileInfo = {}) {
|
||||
|
||||
OSSclient = await RefreshClient(OSSclient)
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
|
|
@ -52,6 +53,20 @@ async function ossGenerateSTS() {
|
|||
}
|
||||
let res = await OSSclient.put(objectName, object)
|
||||
if (res && res.url) {
|
||||
const urlParams = new URLSearchParams(window.location.search)
|
||||
const trialId = urlParams.get('trialId')
|
||||
if (Object.keys(fileInfo).length !== 0) {
|
||||
fileInfo.fileType = mimeTypeToExt(fileInfo.fileType)
|
||||
let params = Object.assign({path: objectName}, fileInfo)
|
||||
addOrUpdateFileUploadRecord(params)
|
||||
} else if (trialId) {
|
||||
const fileName = objectName.split('/').pop()
|
||||
const fileType = fileName.includes('.')
|
||||
? fileName.split('.').pop().toLowerCase()
|
||||
: ''
|
||||
let params = { trialId, path: objectName, fileName, fileType }
|
||||
addOrUpdateFileUploadRecord(params)
|
||||
}
|
||||
resolve({
|
||||
name: objectName,
|
||||
url: res.url
|
||||
|
|
@ -65,7 +80,7 @@ async function ossGenerateSTS() {
|
|||
}
|
||||
})
|
||||
},
|
||||
multipartUpload: async (data, progress) => {
|
||||
multipartUpload: async (data, progress, fileInfo = {}) => {
|
||||
OSSclient = await RefreshClient(OSSclient)
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
|
|
@ -95,6 +110,21 @@ async function ossGenerateSTS() {
|
|||
}
|
||||
let res = await customerHttp(OSSclient, data, progress);
|
||||
if (res) {
|
||||
const urlParams = new URLSearchParams(window.location.search)
|
||||
const trialId = urlParams.get('trialId')
|
||||
if (Object.keys(fileInfo).length !== 0) {
|
||||
fileInfo.fileType = mimeTypeToExt(fileInfo.fileType)
|
||||
let params = Object.assign({path: data.path}, fileInfo)
|
||||
addOrUpdateFileUploadRecord(params)
|
||||
} else if (trialId) {
|
||||
const fileName = data.path.split('/').pop()
|
||||
const fileType = fileName.includes('.')
|
||||
? fileName.split('.').pop().toLowerCase()
|
||||
: ''
|
||||
let params = { trialId, path: data.path, fileName, fileType }
|
||||
addOrUpdateFileUploadRecord(params)
|
||||
}
|
||||
|
||||
resolve({
|
||||
name: data.path,
|
||||
url: Vue.prototype.OSSclientConfig.viewEndpoint + decodeUtf8(res.name)
|
||||
|
|
@ -169,17 +199,17 @@ async function ossGenerateSTS() {
|
|||
}
|
||||
});
|
||||
Vue.prototype.OSSclient = {
|
||||
put: async function (objectName, object) {
|
||||
put: async function (objectName, object, fileInfo = {}) {
|
||||
let data = {
|
||||
file: object,
|
||||
path: objectName
|
||||
}
|
||||
aws = await RefreshClient(aws);
|
||||
return uploadAWS(aws, data, () => { });
|
||||
return uploadAWS(aws, data, () => { }, fileInfo);
|
||||
},
|
||||
multipartUpload: async (data, progress) => {
|
||||
multipartUpload: async (data, progress, fileInfo = {}) => {
|
||||
aws = await RefreshClient(aws);
|
||||
return uploadAWS(aws, data, progress);
|
||||
return uploadAWS(aws, data, progress, fileInfo);
|
||||
},
|
||||
close: () => {
|
||||
AWSclose();
|
||||
|
|
@ -189,7 +219,7 @@ async function ossGenerateSTS() {
|
|||
return
|
||||
}
|
||||
// AWS上传函数
|
||||
function uploadAWS(aws, data, progress) {
|
||||
function uploadAWS(aws, data, progress, fileInfo) {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const { file, path } = data;
|
||||
|
|
@ -211,6 +241,21 @@ function uploadAWS(aws, data, progress) {
|
|||
data.path = data.path.replace(`/${bucketName}/`, '');
|
||||
await exist(aws, bucketName, data, progress, (path, status) => {
|
||||
if (status === 'success') {
|
||||
const urlParams = new URLSearchParams(window.location.search)
|
||||
|
||||
const trialId = urlParams.get('trialId')
|
||||
if (Object.keys(fileInfo).length !== 0) {
|
||||
fileInfo.fileType = mimeTypeToExt(fileInfo.fileType)
|
||||
let params = Object.assign({path: decodeUtf8(curPath)}, fileInfo)
|
||||
addOrUpdateFileUploadRecord(params)
|
||||
} else if (trialId) {
|
||||
const fileName = decodeUtf8(curPath).split('/').pop()
|
||||
const fileType = fileName.includes('.')
|
||||
? fileName.split('.').pop().toLowerCase()
|
||||
: ''
|
||||
let params = { trialId, path: decodeUtf8(curPath), fileName, fileType }
|
||||
addOrUpdateFileUploadRecord(params)
|
||||
}
|
||||
resolve({
|
||||
name: decodeUtf8(curPath),
|
||||
url: Vue.prototype.OSSclientConfig.viewEndpoint + decodeUtf8(curPath)
|
||||
|
|
@ -320,5 +365,37 @@ function isCredentialsExpired(credentials) {
|
|||
return expireDate.getTime() - now.getTime() <= 300000;
|
||||
|
||||
}
|
||||
function mimeTypeToExt(mimeType) {
|
||||
const map = {
|
||||
// 图片
|
||||
'image/jpeg': 'jpg',
|
||||
'image/jpg': 'jpg',
|
||||
'image/png': 'png',
|
||||
'image/gif': 'gif',
|
||||
'image/webp': 'webp',
|
||||
'image/svg+xml': 'svg',
|
||||
'image/bmp': 'bmp',
|
||||
|
||||
// 文档
|
||||
'application/dicom': 'dcm',
|
||||
'application/pdf': 'pdf',
|
||||
'application/msword': 'doc',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'docx',
|
||||
'application/vnd.ms-excel': 'xls',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'xlsx',
|
||||
'application/vnd.ms-powerpoint': 'ppt',
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'pptx',
|
||||
'text/plain': 'txt',
|
||||
// 音频/视频
|
||||
'audio/mpeg': 'mp3',
|
||||
'audio/wav': 'wav',
|
||||
'video/mp4': 'mp4',
|
||||
'video/mpeg': 'mpeg',
|
||||
// 压缩包
|
||||
'application/zip': 'zip',
|
||||
'application/x-rar-compressed': 'rar',
|
||||
'application/x-7z-compressed': '7z',
|
||||
};
|
||||
return map[mimeType] || ''; // 找不到返回空字符串
|
||||
}
|
||||
export const OSSclient = ossGenerateSTS
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
// PT 临床数据缓存:用于统一 SUV 口径(优先接口,缺失则回退 DICOM 元数据)
|
||||
const instanceIdToClinicalData = new Map()
|
||||
|
||||
function normalizeId(id) {
|
||||
if (id === undefined || id === null) return null
|
||||
const s = String(id).trim()
|
||||
return s ? s : null
|
||||
}
|
||||
|
||||
export function setPTClinicalDataForInstance(instanceId, clinicalData) {
|
||||
const key = normalizeId(instanceId)
|
||||
if (!key) return
|
||||
if (!clinicalData || typeof clinicalData !== 'object') return
|
||||
instanceIdToClinicalData.set(key, { ...clinicalData })
|
||||
}
|
||||
|
||||
export function getPTClinicalDataForInstance(instanceId) {
|
||||
const key = normalizeId(instanceId)
|
||||
if (!key) return null
|
||||
return instanceIdToClinicalData.get(key) || null
|
||||
}
|
||||
|
||||
export function deletePTClinicalDataForInstance(instanceId) {
|
||||
const key = normalizeId(instanceId)
|
||||
if (!key) return
|
||||
instanceIdToClinicalData.delete(key)
|
||||
}
|
||||
|
||||
export function clearPTClinicalDataCache() {
|
||||
instanceIdToClinicalData.clear()
|
||||
}
|
||||
|
|
@ -28,7 +28,7 @@ async function executeTask() {
|
|||
task.callback({ success: true, res: result })
|
||||
|
||||
}).catch((error) => {
|
||||
task.callback({ success: true, err: error })
|
||||
task.callback({ success: false, err: error })
|
||||
})
|
||||
})
|
||||
// let starLoadTime = performance.now()
|
||||
|
|
|
|||
|
|
@ -52,18 +52,22 @@ function getScreen() {
|
|||
})
|
||||
|
||||
}
|
||||
export async function openWindow(url, name, Skip = false) {
|
||||
export async function openWindow(url, name, Skip = false, noreferrer = false) {
|
||||
// 判断浏览器是否兼容
|
||||
// 高版本的谷歌,edge不支持跨屏,需要降低浏览器版本86.0版
|
||||
let fulls = ''
|
||||
if (noreferrer) {
|
||||
fulls = 'noopener,noreferrer'
|
||||
}
|
||||
if (!window.getScreens && !window.getScreenDetails) {
|
||||
console.log('你的浏览器版本不支持多屏展示功能!');
|
||||
return window.open(url, name);
|
||||
return window.open(url, name, fulls);
|
||||
}
|
||||
let permission = await getPermission()
|
||||
if (!permission) {
|
||||
// alert('使用多屏功能请先进行授权')
|
||||
if (Skip) {
|
||||
return window.open(url, name);
|
||||
return window.open(url, name, fulls);
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
|
@ -72,14 +76,13 @@ export async function openWindow(url, name, Skip = false) {
|
|||
// 判断是否2个屏幕
|
||||
if (multiScreen.length < 2) {
|
||||
console.log('请接入多个显示屏!');
|
||||
return window.open(url, name);
|
||||
return window.open(url, name, fulls);
|
||||
}
|
||||
console.log(screen, 'screen')
|
||||
// 获取当前屏幕availLeft信息和所有信息比对,取另一个屏幕数据
|
||||
let currentAvailLeft = screen.availLeft ? screen.availLeft : '0'
|
||||
let newCurr = multiScreen.find((t) => t.availLeft != currentAvailLeft)
|
||||
console.log(newCurr, 'newCurr')
|
||||
let fulls = ''
|
||||
for (let key in newCurr) {
|
||||
fulls += `${key}=${(newCurr[key] || newCurr[key] === 0) ? newCurr[key] : 0},`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@
|
|||
<div ref="mapbox" class="map-wrapper" />
|
||||
</template>
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
import 'modules/echarts/map/js/world.js'
|
||||
import * as echarts from 'echarts'
|
||||
import worldMap from 'echarts-map/json/world.json';
|
||||
import { fontSize } from 'utils/fontsize'
|
||||
echarts.registerMap('world', worldMap);
|
||||
export default {
|
||||
props: {
|
||||
area: { type: String, default: '' },
|
||||
|
|
@ -76,8 +77,8 @@ export default {
|
|||
}
|
||||
return res
|
||||
}
|
||||
var series = []
|
||||
;[['Shanghai', BJData]].forEach(function(item, i) {
|
||||
var series = [];
|
||||
[['Shanghai', BJData]].forEach(function (item, i) {
|
||||
series.push(
|
||||
{
|
||||
type: 'lines',
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<div ref="trials_stats" style="width:100%;height:100%;" />
|
||||
</template>
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
import * as echarts from 'echarts'
|
||||
import { getTrialCountRank } from '@/api/dashboard'
|
||||
import { fontSize } from 'utils/fontsize'
|
||||
const Count = 5
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<div ref="enroll_stats" style="width:100%;height:100%;" />
|
||||
</template>
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
import * as echarts from 'echarts'
|
||||
import { getEnrollDataByQuarter } from '@/api/dashboard'
|
||||
import { fontSize } from 'utils/fontsize'
|
||||
const Count = 6
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<div ref="reviewers_stats" style="width:100%;height:100%;padding:5px;" />
|
||||
</template>
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
import * as echarts from 'echarts'
|
||||
import { getReviewersByRank } from '@/api/dashboard'
|
||||
import { fontSize } from 'utils/fontsize'
|
||||
export default {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<div ref="monthly_reading_stats" style="width:100%;height:100%;" />
|
||||
</template>
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
import * as echarts from 'echarts'
|
||||
import { getReadingDataByMonth } from '@/api/dashboard'
|
||||
import { fontSize } from 'utils/fontsize'
|
||||
const Count = 6
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<div ref="reading_rank" class="reading-rank" />
|
||||
</template>
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
import * as echarts from 'echarts'
|
||||
import { getReadingDataRank } from '@/api/dashboard'
|
||||
import { fontSize } from 'utils/fontsize'
|
||||
const Count = 5
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<div ref="reading_stats" style="width:100%;height:100%;padding-top:5px;" />
|
||||
</template>
|
||||
<script>
|
||||
import echarts from 'echarts'
|
||||
import * as echarts from 'echarts'
|
||||
import { getReadingDataByType } from '@/api/dashboard'
|
||||
import { fontSize } from 'utils/fontsize'
|
||||
export default {
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@
|
|||
v-for="(instance, idx) in item.instanceInfoList"
|
||||
:key="instance.InstanceUid"
|
||||
class="frame_content"
|
||||
:class="{ 'frame_content_active': activeInstanceUid === instance.InstanceUid }"
|
||||
:style="{'margin-bottom':idx<item.instanceInfoList.length-1? '5px':'0px'}"
|
||||
@click="showMultiFrames(item, index, instance)"
|
||||
>
|
||||
|
|
@ -136,7 +137,8 @@ export default {
|
|||
studyTitle: '',
|
||||
seriesCount: 0,
|
||||
seriesList: [],
|
||||
currentSeriesIndex: -1
|
||||
currentSeriesIndex: -1,
|
||||
activeInstanceUid: null
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -231,6 +233,7 @@ export default {
|
|||
},
|
||||
showMultiFrames(series, seriesIndex, instanceInfo) {
|
||||
this.currentSeriesIndex = seriesIndex
|
||||
this.activeInstanceUid = instanceInfo.InstanceUid
|
||||
const imageIds = []
|
||||
if (instanceInfo.NumberOfFrames && instanceInfo.NumberOfFrames > 1) {
|
||||
for (let j = 0; j < instanceInfo.NumberOfFrames; j++) {
|
||||
|
|
@ -252,6 +255,7 @@ export default {
|
|||
showSeriesImage(seriesIndex) {
|
||||
// if (seriesIndex === this.currentSeriesIndex) return;
|
||||
this.currentSeriesIndex = seriesIndex
|
||||
this.activeInstanceUid = null
|
||||
this.$refs.dicomViewer.loadImageStack(this.seriesList[seriesIndex])
|
||||
},
|
||||
closeDialog() {
|
||||
|
|
@ -429,4 +433,8 @@ export default {
|
|||
border-color: #213a54 !important;
|
||||
background-color: #213a54;
|
||||
}
|
||||
.frame_content_active {
|
||||
border-color: #213a54 !important;
|
||||
background-color: #213a54;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@
|
|||
<div class="frame_list">
|
||||
<div v-for="(instance, idx) in item.instanceInfoList" :key="instance.Id"
|
||||
class="frame_content"
|
||||
:class="{ 'frame_content_active': activeInstanceId === instance.Id }"
|
||||
:style="{ 'margin-bottom': idx < item.instanceInfoList.length - 1 ? '5px' : '0px' }"
|
||||
@click="showMultiFrames(item, index, instance)">
|
||||
<div>
|
||||
|
|
@ -72,17 +73,17 @@
|
|||
</div>
|
||||
</div>
|
||||
<i slot="reference" class="el-icon-connection" style="font-size: 15px;cursor: pointer;"
|
||||
@click="popperClick(seriesList, item)" />
|
||||
@click.stop="popperClick(seriesList, item)" />
|
||||
</el-popover>
|
||||
</div>
|
||||
</div>
|
||||
<div v-show="item.instanceCount" style="padding: 1px;">
|
||||
<div v-if="item.instanceCount" style="padding: 1px;">
|
||||
{{ item.modality }}: {{ item.instanceCount }} image
|
||||
</div>
|
||||
<div v-show="!item.keySeries && item.sliceThickness" style="padding: 1px;">
|
||||
<div v-if="!item.keySeries && item.sliceThickness" style="padding: 1px;">
|
||||
T: {{ parseFloat(item.sliceThickness).toFixed(2) }}
|
||||
</div>
|
||||
<div v-show="!item.keySeries && item.description"
|
||||
<div v-if="!item.keySeries && item.description"
|
||||
style="width: 120px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;padding: 1x;">
|
||||
{{ item.description }}
|
||||
</div>
|
||||
|
|
@ -123,12 +124,12 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="viewerContent">
|
||||
<dicom-viewer id="dicomViewer" ref="dicomViewer" style="height:100%" />
|
||||
<dicom-viewer id="dicomViewer" ref="dicomViewer" style="height:100%" :loading.sync="loading"
|
||||
:modality="modality" :Comparison.sync="isComparison" @loadStudy="loadStudy" />
|
||||
</div>
|
||||
<!-- <div class="viewerRightSidePanel">
|
||||
<dicom-tools />
|
||||
</div> -->
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
@ -202,7 +203,9 @@ export default {
|
|||
isFromCRCUpload: false,
|
||||
isReading: null,
|
||||
activeSeriesId: null,
|
||||
isPacs: false
|
||||
isPacs: false,
|
||||
isComparison: false,
|
||||
activeInstanceId: null
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
|
|
@ -307,7 +310,7 @@ export default {
|
|||
})
|
||||
})
|
||||
},
|
||||
async loadStudy() {
|
||||
async loadStudy(seriesId = null) {
|
||||
let params = {}
|
||||
if (this.isPacs) {
|
||||
params.IsPacs = true
|
||||
|
|
@ -327,7 +330,7 @@ export default {
|
|||
isReading = `?IsPacs=true`
|
||||
}
|
||||
const url = `/series/list/${this.studyId}${isReading}`
|
||||
this.getSeriesList(url)
|
||||
this.getSeriesList(url, seriesId)
|
||||
}
|
||||
},
|
||||
async loadPatientStudy() {
|
||||
|
|
@ -392,7 +395,7 @@ export default {
|
|||
console.log(err)
|
||||
}
|
||||
},
|
||||
async getSeriesList(url) {
|
||||
async getSeriesList(url, seriesId = null) {
|
||||
try {
|
||||
const data = await getSeriesList(url)
|
||||
if (data.IsSuccess) {
|
||||
|
|
@ -431,12 +434,12 @@ export default {
|
|||
isDeleted: item.IsDeleted,
|
||||
previewImageUrl: item.ImageResizePath ? this.OSSclientConfig.basePath + item.ImageResizePath : `/api/series/preview/${item.Id}`,
|
||||
instanceCount: item.InstanceCount,
|
||||
prefetchInstanceCount: 0,
|
||||
prefetchInstanceCount: seriesId ? item.InstanceInfoList.length * 100 : 0,
|
||||
hasLabel: item.HasLabel,
|
||||
keySeries: item.KeySeries,
|
||||
tpCode: this.tpCode,
|
||||
loadStatus: false,
|
||||
imageloadedArr: [],
|
||||
imageloadedArr: seriesId ? imageIds : [],
|
||||
isExistMutiFrames: item.IsExistMutiFrames,
|
||||
isShowPopper: false,
|
||||
subjectCode: item.SubjectCode,
|
||||
|
|
@ -446,8 +449,14 @@ export default {
|
|||
this.seriesList = seriesList
|
||||
if (this.seriesList.length > 0) {
|
||||
this.loadAllImages()
|
||||
this.$refs.dicomViewer.loadImageStack(this.seriesList[0], this.labels[this.tpCode])
|
||||
this.firstInstanceId = this.seriesList[0].imageIds[0]
|
||||
let index = 0;
|
||||
if (seriesId) {
|
||||
index = this.seriesList.findIndex(item => item.seriesId === seriesId)
|
||||
this.refreshImage(this.seriesList[index])
|
||||
}
|
||||
this.$refs.dicomViewer.loadImageStack(this.seriesList[index], this.labels[this.tpCode])
|
||||
this.firstInstanceId = this.seriesList[index].imageIds[0]
|
||||
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
|
|
@ -527,7 +536,9 @@ export default {
|
|||
}
|
||||
},
|
||||
showSeriesImage(e, seriesIndex, series) {
|
||||
if (this.isComparison) return false
|
||||
this.activeSeriesId = series.seriesId
|
||||
this.activeInstanceId = null
|
||||
workSpeedclose(true)
|
||||
// if (seriesIndex === this.currentSeriesIndex) return
|
||||
const element = e.currentTarget
|
||||
|
|
@ -576,6 +587,7 @@ export default {
|
|||
},
|
||||
showMultiFrames(series, seriesIndex, instanceInfo) {
|
||||
this.currentSeriesIndex = seriesIndex
|
||||
this.activeInstanceId = instanceInfo.Id
|
||||
const imageIds = []
|
||||
if (instanceInfo.NumberOfFrames && instanceInfo.NumberOfFrames > 1) {
|
||||
for (let j = 0; j < instanceInfo.NumberOfFrames; j++) {
|
||||
|
|
@ -587,6 +599,7 @@ export default {
|
|||
const seriesInfo = {
|
||||
trialId: series.trialId,
|
||||
subjectVisitId: series.subjectVisitId,
|
||||
instanceInfoList: series.instanceInfoList,
|
||||
studyId: series.studyId,
|
||||
imageIds: imageIds,
|
||||
seriesId: series.seriesId,
|
||||
|
|
@ -920,6 +933,7 @@ export default {
|
|||
}
|
||||
if (this.visitTaskId === params.visitTaskId) {
|
||||
const seriesIndex = params.seriesIndex
|
||||
if (!this.seriesList[seriesIndex]) return false
|
||||
var prefetchInstanceCount = this.seriesList[seriesIndex].prefetchInstanceCount
|
||||
var instanceCount = this.seriesList[seriesIndex].instanceCount
|
||||
if (!this.activeSeriesId) {
|
||||
|
|
@ -965,8 +979,8 @@ export default {
|
|||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.imageBox {
|
||||
<style lang="scss">
|
||||
.viewerContainer .imageBox {
|
||||
position: relative;
|
||||
|
||||
i {
|
||||
|
|
@ -1183,6 +1197,10 @@ export default {
|
|||
border-color: #213a54 !important;
|
||||
background-color: #213a54;
|
||||
}
|
||||
.frame_content_active {
|
||||
border-color: #213a54 !important;
|
||||
background-color: #213a54;
|
||||
}
|
||||
|
||||
/* .viewerRightSidePanel {
|
||||
width: 300px;
|
||||
|
|
|
|||
|
|
@ -19,25 +19,26 @@
|
|||
<el-collapse-item v-for="(study, index) in studyList" :key="`${study.StudyId}`"
|
||||
:name="`${study.StudyId}`">
|
||||
<template slot="title">
|
||||
|
||||
<div class="collapse-title-wrapper">
|
||||
<div class="text-desc">
|
||||
{{ study.StudyCode }}
|
||||
</div>
|
||||
<!-- <div v-show="study.Description" class="text-desc">
|
||||
{{ study.Description }}
|
||||
</div> -->
|
||||
<el-tooltip v-show="study.Description" class="item" effect="dark" :content="study.Description"
|
||||
<!-- <el-tooltip v-show="study.Description" class="item" effect="dark" :content="study.Description"
|
||||
placement="bottom">
|
||||
<div v-show="study.Description"
|
||||
<div v-if="study.Description"
|
||||
style="width: 50px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;padding: 1x;">
|
||||
{{ study.Description }}
|
||||
</div>
|
||||
</el-tooltip>
|
||||
<div v-show="study.SeriesCount" class="text-desc">
|
||||
</el-tooltip> -->
|
||||
<div v-if="study.SeriesCount" class="text-desc collapse-title-extra">
|
||||
{{ study.Modalities }} : {{ study.SeriesCount }} Series
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div v-show="study.Description" class="text-desc" style="background-color: #1f1f1f;">
|
||||
<div v-if="study.Description" class="text-desc" style="background-color: #1f1f1f;">
|
||||
{{ study.Description }}
|
||||
</div>
|
||||
<div v-for="(series, i) in study.SeriesList" :key="i"
|
||||
|
|
@ -65,6 +66,7 @@
|
|||
<div class="frame_list">
|
||||
<div v-for="(instance, idx) in series.instanceInfoList" :key="instance.Id"
|
||||
class="frame_content"
|
||||
:class="{ 'frame_content_active': activeInstanceId === instance.Id }"
|
||||
:style="{ 'margin-bottom': idx < series.instanceInfoList.length - 1 ? '5px' : '0px' }"
|
||||
@click="showMultiFrames(index, series, i, instance)">
|
||||
<!-- <div>
|
||||
|
|
@ -97,12 +99,12 @@
|
|||
</div>
|
||||
<i slot="reference" class="el-icon-connection"
|
||||
style="font-size: 15px;cursor: pointer;"
|
||||
@click="popperClick(studyList, series)" />
|
||||
@click.stop="popperClick(studyList, series)" />
|
||||
</el-popover>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div v-show="series.InstanceCount" style="padding: 1px;">
|
||||
<div v-show="series.instanceCount" style="padding: 1px;">
|
||||
{{ series.modality }}: {{ series.instanceCount }} image
|
||||
</div>
|
||||
<div v-show="series.sliceThickness" style="padding: 1px;">
|
||||
|
|
@ -158,8 +160,7 @@
|
|||
<div class="viewerSidethumbinner">
|
||||
|
||||
<el-collapse v-model="relationActiveName" @change="handelRelationActiveChange">
|
||||
<template v-for="item in relationStudyListByVisitName">
|
||||
<div :key="`${item.VisitName}`">
|
||||
<div v-for="item in relationStudyListByVisitName" :key="`${item.VisitName}`">
|
||||
<div v-show="item.VisitName" class="text-desc" style="background-color: #1f1f1f;">
|
||||
{{ item.VisitName }}
|
||||
</div>
|
||||
|
|
@ -167,7 +168,7 @@
|
|||
<el-collapse-item :key="`${study.StudyId}`" :name="`${study.StudyId}`"
|
||||
v-if="study.VisitName === item.VisitName">
|
||||
<template slot="title">
|
||||
|
||||
<div class="collapse-title-wrapper">
|
||||
<div class="text-desc">
|
||||
{{ study.StudyCode }}
|
||||
</div>
|
||||
|
|
@ -175,9 +176,10 @@
|
|||
{{ study.Description }}
|
||||
</div> -->
|
||||
|
||||
<div v-show="study.SeriesCount" class="text-desc">
|
||||
<div v-show="study.SeriesCount" class="text-desc collapse-title-extra">
|
||||
{{ study.Modalities }} : {{ study.SeriesCount }} Series
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<div v-show="study.Description" class="text-desc" style="background-color: #1f1f1f;">
|
||||
{{ study.Description }}
|
||||
|
|
@ -208,6 +210,7 @@
|
|||
<div class="frame_list">
|
||||
<div v-for="(instance, idx) in seriesItem.instanceInfoList"
|
||||
:key="instance.Id" class="frame_content"
|
||||
:class="{ 'frame_content_active': activeInstanceId === instance.Id }"
|
||||
:style="{ 'margin-bottom': idx < seriesItem.instanceInfoList.length - 1 ? '5px' : '0px' }"
|
||||
@click="showMultiFrames(studyIndex, seriesItem, index, instance)">
|
||||
<div>
|
||||
|
|
@ -258,8 +261,6 @@
|
|||
</template>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
</el-collapse>
|
||||
|
||||
</div>
|
||||
|
|
@ -330,7 +331,8 @@ export default {
|
|||
isFromCRCUpload: false,
|
||||
visitTaskId: null,
|
||||
page: '',
|
||||
activeSeriesId: null
|
||||
activeSeriesId: null,
|
||||
activeInstanceId: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
|
@ -349,20 +351,18 @@ export default {
|
|||
this.isFromCRCUpload = !!this.$router.currentRoute.query.isFromCRCUpload
|
||||
this.visitTaskId = this.$router.currentRoute.query.visitTaskId
|
||||
this.page = this.$route.query.page
|
||||
this.beforeUnloadHandler = () => {
|
||||
cornerstone.imageCache.purgeCache()
|
||||
requestPoolManager.resetRequestPool()
|
||||
}
|
||||
// cornerstone.events.addEventListener('cornerstoneimageloaded', this.cornerstoneImageLoaded)
|
||||
this.getStudiesInfo()
|
||||
cornerstone.events.addEventListener('cornerstoneimageloadprogress', this.cornerstoneimageloadprogress)
|
||||
window.addEventListener('beforeunload', e => {
|
||||
cornerstone.imageCache.purgeCache()
|
||||
requestPoolManager.resetRequestPool()
|
||||
})
|
||||
window.addEventListener('beforeunload', this.beforeUnloadHandler)
|
||||
},
|
||||
beforeDestroy() {
|
||||
requestPoolManager.stopTaskTimer()
|
||||
window.removeEventListener('beforeunload', e => {
|
||||
cornerstone.imageCache.purgeCache()
|
||||
requestPoolManager.resetRequestPool()
|
||||
})
|
||||
window.removeEventListener('beforeunload', this.beforeUnloadHandler)
|
||||
workSpeedclose(true)
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -492,14 +492,17 @@ export default {
|
|||
data.SeriesList = seriesList
|
||||
this.studyList.push(data)
|
||||
})
|
||||
if (this.studyList.length > 0) {
|
||||
this.$refs.dicomViewer.loadImageStack(this.studyList[0].SeriesList[0])
|
||||
const imageId = this.studyList[0].SeriesList[0].imageIds[0]
|
||||
const firstStudy = this.studyList.find(study => Array.isArray(study.SeriesList) && study.SeriesList.length > 0)
|
||||
if (firstStudy) {
|
||||
const firstSeries = firstStudy.SeriesList[0]
|
||||
if (!Array.isArray(firstSeries.imageIds) || firstSeries.imageIds.length === 0) return
|
||||
this.$refs.dicomViewer.loadImageStack(firstSeries)
|
||||
const imageId = firstSeries.imageIds[0]
|
||||
let instanceId = imageId.split('/')[imageId.split('/').length - 1]
|
||||
instanceId = instanceId.split('.')[0]
|
||||
this.firstInstanceId = instanceId
|
||||
this.activeNames = [this.studyList[0].StudyId]
|
||||
this.loadImages(this.studyList[0].SeriesList[0], 0)
|
||||
this.activeNames = [firstStudy.StudyId]
|
||||
this.loadImages(firstSeries, 0)
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
|
|
@ -524,6 +527,7 @@ export default {
|
|||
},
|
||||
showSeriesImage(e, studyIndex, seriesIndex, series) {
|
||||
this.activeSeriesId = series.seriesId
|
||||
this.activeInstanceId = null
|
||||
workSpeedclose(true)
|
||||
const element = e.currentTarget
|
||||
const elements = document.querySelectorAll('[series-type]')
|
||||
|
|
@ -565,6 +569,7 @@ export default {
|
|||
},
|
||||
showMultiFrames(studyIndex, series, seriesIndex, instanceInfo) {
|
||||
this.currentSeriesIndex = seriesIndex
|
||||
this.activeInstanceId = instanceInfo.Id
|
||||
const imageIds = []
|
||||
if (instanceInfo.NumberOfFrames && instanceInfo.NumberOfFrames > 1) {
|
||||
for (let j = 0; j < instanceInfo.NumberOfFrames; j++) {
|
||||
|
|
@ -576,6 +581,7 @@ export default {
|
|||
const seriesInfo = {
|
||||
trialId: series.trialId,
|
||||
subjectVisitId: series.subjectVisitId,
|
||||
instanceInfoList: series.instanceInfoList,
|
||||
studyId: series.studyId,
|
||||
imageIds: imageIds,
|
||||
seriesId: series.seriesId,
|
||||
|
|
@ -601,7 +607,11 @@ export default {
|
|||
}
|
||||
if (!isAddToTakPool) {
|
||||
var priority = parseInt(new Date().getTime())
|
||||
if (series.isExistMutiFrames) {
|
||||
if (series.isExistMutiFrames && imageIds.length > 1) {
|
||||
imageIds.map(imageId => {
|
||||
this.imageList.push({ imageId, seriesId: series.seriesId, priority })
|
||||
})
|
||||
} else if (series.isExistMutiFrames) {
|
||||
series.instanceInfoList.map(image => {
|
||||
this.imageList.push({ imageId: image.ImageId, seriesId: series.seriesId, priority })
|
||||
})
|
||||
|
|
@ -883,6 +893,7 @@ export default {
|
|||
},
|
||||
showRelationSeriesImage(e, series, studyIndex, index) {
|
||||
this.activeSeriesId = series.seriesId
|
||||
this.activeInstanceId = null
|
||||
workSpeedclose(true)
|
||||
this.currentRelationIndex = index
|
||||
const element = e.currentTarget
|
||||
|
|
@ -940,7 +951,7 @@ export default {
|
|||
if (this.imageList.length > 0) {
|
||||
requestPoolManager.startTaskTimer()
|
||||
this.imageList.map(image => {
|
||||
requestPoolManager.loadAndCacheImagePlus(image.imageId, image.seriesId, image.priority)
|
||||
requestPoolManager.loadAndCacheImagePlus(image.imageId, image.seriesId, image.priority).catch(() => {})
|
||||
})
|
||||
requestPoolManager.sortTaskPool()
|
||||
this.imageList = []
|
||||
|
|
@ -1012,7 +1023,7 @@ export default {
|
|||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.imageBox {
|
||||
.viewerContainer .imageBox {
|
||||
position: relative;
|
||||
|
||||
i {
|
||||
|
|
@ -1102,6 +1113,7 @@ export default {
|
|||
padding: 0;
|
||||
margin-right: 2px;
|
||||
color: #D0D0D0;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
|
|
@ -1110,6 +1122,8 @@ export default {
|
|||
word-break: break-all;
|
||||
display: table;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
overflow-x: hidden;
|
||||
border: 1px solid #3e3f3a;
|
||||
}
|
||||
|
||||
|
|
@ -1138,14 +1152,20 @@ export default {
|
|||
.viewerContainer .viewerLeftSidePanel .viewernavigatorwrapper {
|
||||
display: flex;
|
||||
width: 220px;
|
||||
/* height: 84px; */
|
||||
min-height: 85px;
|
||||
padding: 1px 2px 1px 2px;
|
||||
margin: 2px 0 1px 1px;
|
||||
align-items: flex-start;
|
||||
/* border-radius: 2px;
|
||||
border: 1px solid #404040; */
|
||||
|
||||
}
|
||||
|
||||
.viewerContainer .viewerLeftSidePanel .viewernavigatorwrapper .imageBox {
|
||||
align-self: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.viewerContainer .viewernavigatorwrapper .el-progress__text {
|
||||
display: none;
|
||||
}
|
||||
|
|
@ -1168,9 +1188,19 @@ export default {
|
|||
/* width: 120px;
|
||||
height: 80px; */
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding: 3px 1px 3px 4px;
|
||||
vertical-align: top;
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.viewerContainer .viewerLeftSidePanel .viewernavitextwrapper>div {
|
||||
max-width: 100%;
|
||||
white-space: normal;
|
||||
word-break: break-word;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.viewerContainer .viewerLeftSidePanel .viewerlabelwrapper {
|
||||
|
|
@ -1200,12 +1230,24 @@ export default {
|
|||
|
||||
.viewerContainer .el-collapse {
|
||||
border: none;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
box-sizing: border-box;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.viewerContainer .el-collapse-item {
|
||||
background-color: #585453 !important;
|
||||
color: #ddd;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.viewerContainer .el-collapse-item__wrap {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.viewerContainer .el-collapse-item__content {
|
||||
|
|
@ -1218,9 +1260,42 @@ export default {
|
|||
background-color: #585453 !important;
|
||||
color: #ddd;
|
||||
border-bottom-color: #5a5a5a;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
box-sizing: border-box;
|
||||
padding-left: 5px;
|
||||
height: 40px;
|
||||
height: auto;
|
||||
min-height: 30px;
|
||||
line-height: 20px;
|
||||
align-items: flex-start;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.viewerContainer .collapse-title-wrapper {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
column-gap: 6px;
|
||||
row-gap: 2px;
|
||||
line-height: 16px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.viewerContainer .collapse-title-wrapper .text-desc {
|
||||
flex: 0 1 auto;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
white-space: normal;
|
||||
word-break: break-word;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.viewerContainer .collapse-title-wrapper .collapse-title-extra {
|
||||
flex: 0 1 auto;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.instance_frame_wrapper {
|
||||
|
|
@ -1263,6 +1338,10 @@ export default {
|
|||
border-color: #213a54 !important;
|
||||
background-color: #213a54;
|
||||
}
|
||||
.frame_content_active {
|
||||
border-color: #213a54 !important;
|
||||
background-color: #213a54;
|
||||
}
|
||||
|
||||
/* .viewerRightSidePanel {
|
||||
width: 300px;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,11 @@
|
|||
</el-upload>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('dictionary:signature:form:DocLanguageType')" prop="DocLanguageType">
|
||||
<el-select v-model="form.DocLanguageType" style="width: 100%">
|
||||
<el-option v-for="item of $d.DocLanguageType" :key="item.id" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('dictionary:signature:form:NeedConfirmedUserTypeIdList')"
|
||||
prop="NeedConfirmedUserTypeIdList">
|
||||
<el-select v-model="form.NeedConfirmedUserTypeIdList" style="width: 100%" multiple>
|
||||
|
|
@ -83,6 +88,7 @@ export default {
|
|||
DocUserSignType: 0,
|
||||
CurrentStaffTrainDays: 1,
|
||||
NewStaffTrainDays: 14,
|
||||
DocLanguageType: null
|
||||
},
|
||||
rules: {
|
||||
FileTypeId: [
|
||||
|
|
@ -92,6 +98,13 @@ export default {
|
|||
trigger: ['blur'],
|
||||
},
|
||||
],
|
||||
DocLanguageType: [
|
||||
{
|
||||
required: true,
|
||||
message: this.$t('common:ruleMessage:select'),
|
||||
trigger: ['blur'],
|
||||
},
|
||||
],
|
||||
SignViewMinimumMinutes: [
|
||||
{
|
||||
required: true,
|
||||
|
|
@ -143,6 +156,9 @@ export default {
|
|||
this.form.DocUserSignType = this.data.DocUserSignType
|
||||
this.form.CurrentStaffTrainDays = this.data.CurrentStaffTrainDays
|
||||
this.form.NewStaffTrainDays = this.data.NewStaffTrainDays
|
||||
this.form.DocLanguageType = this.data.DocLanguageType
|
||||
} else {
|
||||
this.form.DocLanguageType = this.$i18n.locale !== 'zh' ? 1 : 0
|
||||
}
|
||||
this.loading = false
|
||||
},
|
||||
|
|
|
|||
|
|
@ -26,6 +26,11 @@
|
|||
<el-form-item :label="$t('dictionary:signature:search:Name')">
|
||||
<el-input v-model="searchData.Name" style="width: 130px" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('dictionary:signature:search:DocLanguageType')">
|
||||
<el-select v-model="searchData.DocLanguageType" style="width: 150px" clearable>
|
||||
<el-option v-for="item of $d.DocLanguageType" :key="item.id" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('dictionary:signature:table:NeedConfirmedUserTypes')">
|
||||
<el-select v-model="searchData.UserTypeId" style="width: 150px" clearable>
|
||||
<el-option v-for="item of userTypeOptions" :key="item.Id" :label="item.UserTypeShortName"
|
||||
|
|
@ -90,6 +95,12 @@
|
|||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="DocLanguageType" :label="$t('dictionary:signature:table:DocLanguageType')"
|
||||
show-overflow-tooltip sortable="custom" min-width="120px">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ $fd('DocLanguageType', scope.row.DocLanguageType) }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="SignViewMinimumMinutes" :label="$t('dictionary:signature:table:SignViewMinimumMinutes')"
|
||||
show-overflow-tooltip sortable="custom" min-width="200px" />
|
||||
<el-table-column prop="CurrentStaffTrainDays" :label="$t('dictionary:signature:table:CurrentStaffTrainDays')"
|
||||
|
|
@ -161,7 +172,7 @@
|
|||
<!-- 新增/编辑 -->
|
||||
<el-dialog v-if="editVisible" :visible.sync="editVisible" :close-on-click-modal="false" :title="title"
|
||||
width="600px" custom-class="base-dialog-wrapper">
|
||||
<TemplateForm :data="currentRow" @closeDialog="closeDialog" @getList="getList" />
|
||||
<TemplateForm :data="currentRow" @closeDialog="closeDialog" v-if="editVisible" @getList="getList" />
|
||||
</el-dialog>
|
||||
<!--附件列表-->
|
||||
<attachmentList v-if="config.visible" :config="config" :rowData="currentRow" :SystemDocumentId="SystemDocumentId"
|
||||
|
|
@ -200,6 +211,7 @@ const searchDataDefault = () => {
|
|||
IsDeleted: null,
|
||||
DocUserSignType: null,
|
||||
UserTypeId: null,
|
||||
DocLanguageType: null,
|
||||
Name: '',
|
||||
PageIndex: 1,
|
||||
PageSize: 20,
|
||||
|
|
|
|||
|
|
@ -1,13 +1,6 @@
|
|||
<template>
|
||||
<el-form
|
||||
ref="siteForm"
|
||||
v-loading="loading"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
class="demo-ruleForm"
|
||||
size="small"
|
||||
label-width="150px"
|
||||
>
|
||||
<el-form ref="siteForm" v-loading="loading" :model="form" :rules="rules" class="demo-ruleForm" size="small"
|
||||
label-width="150px">
|
||||
<div class="base-dialog-body">
|
||||
<!-- Site Code -->
|
||||
<el-form-item :label="$t('institutions:sites:label:siteCode')" v-if="form.Id" prop="SiteCode">
|
||||
|
|
@ -15,17 +8,11 @@
|
|||
</el-form-item>
|
||||
|
||||
<!-- 中心名称 -->
|
||||
<el-form-item
|
||||
:label="$t('institutions:sites:label:siteName')"
|
||||
prop="SiteName"
|
||||
>
|
||||
<el-form-item :label="$t('institutions:sites:label:siteName')" prop="SiteName">
|
||||
<el-input v-model="form.SiteName" />
|
||||
</el-form-item>
|
||||
<!-- 中心名称(CN) -->
|
||||
<el-form-item
|
||||
:label="$t('institutions:sites:label:siteNameCN')"
|
||||
prop="SiteNameCN"
|
||||
>
|
||||
<el-form-item :label="$t('institutions:sites:label:siteNameCN')" prop="SiteNameCN">
|
||||
<el-input v-model="form.SiteNameCN" />
|
||||
</el-form-item>
|
||||
<!-- Alias Name -->
|
||||
|
|
@ -38,7 +25,9 @@
|
|||
</el-form-item> -->
|
||||
<!-- Country -->
|
||||
<el-form-item :label="$t('institutions:sites:label:country')" prop="Country">
|
||||
<el-input v-model="form.Country" />
|
||||
<el-select v-model="form.Country" style="width: 100%">
|
||||
<el-option v-for="item of $d.SiteCountry" :key="item.id" :label="item.label" :value="item.label" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- City -->
|
||||
<el-form-item :label="$t('institutions:sites:label:city')" prop="City">
|
||||
|
|
@ -50,17 +39,8 @@
|
|||
</el-form-item>
|
||||
<!-- Affiliated Hospital -->
|
||||
<el-form-item :label="$t('institutions:sites:label:affiliatedHospital')">
|
||||
<el-select
|
||||
v-model="form.HospitalId"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in hospitalList"
|
||||
:key="item.Id"
|
||||
:label="item.HospitalName"
|
||||
:value="item.Id"
|
||||
/>
|
||||
<el-select v-model="form.HospitalId" clearable style="width: 100%">
|
||||
<el-option v-for="item in hospitalList" :key="item.Id" :label="item.HospitalName" :value="item.Id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- Director Name -->
|
||||
|
|
@ -82,20 +62,10 @@
|
|||
</div>
|
||||
<div class="base-dialog-footer" style="text-align: right; margin-top: 10px">
|
||||
<el-form-item>
|
||||
<el-button
|
||||
:disabled="btnLoading"
|
||||
size="small"
|
||||
type="primary"
|
||||
@click="handleCancel"
|
||||
>{{ $t('common:button:cancel') }}</el-button
|
||||
>
|
||||
<el-button
|
||||
size="small"
|
||||
type="primary"
|
||||
:loading="btnLoading"
|
||||
@click="handleSave"
|
||||
>{{ $t('common:button:save') }}</el-button
|
||||
>
|
||||
<el-button :disabled="btnLoading" size="small" type="primary" @click="handleCancel">{{
|
||||
$t('common:button:cancel') }}</el-button>
|
||||
<el-button size="small" type="primary" :loading="btnLoading" @click="handleSave">{{ $t('common:button:save')
|
||||
}}</el-button>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
<el-input v-model="password.UserType" disabled />
|
||||
</el-form-item> -->
|
||||
<!-- 用户名 -->
|
||||
<el-form-item :label="$t('recompose:form:userName')" prop="NewUserName">
|
||||
<el-form-item class="my_new_pwd" :label="$t('recompose:form:userName')" prop="NewUserName">
|
||||
<el-input v-model="password.NewUserName" :disabled="isUpdate" />
|
||||
</el-form-item>
|
||||
<!-- 新密码 -->
|
||||
|
|
@ -77,7 +77,7 @@ export default {
|
|||
callback(
|
||||
lang === 'zh'
|
||||
? new Error(
|
||||
'1)新建账号,用户名字符长度最小为4个字符,最大为16个字符,只可使用字母、数字、下划线;'
|
||||
'新建账号,用户名字符长度最小为4个字符,最大为16个字符,只可使用字母、数字、下划线;'
|
||||
)
|
||||
: new Error(
|
||||
'For a new account, the username must have:1) At least 4 characters;2) At most 16 characters;3)Only letters, numbers, and underscores are allowed.'
|
||||
|
|
|
|||
|
|
@ -4,122 +4,57 @@
|
|||
<el-form :inline="true">
|
||||
<!--项目编号/实验名称-->
|
||||
<el-form-item :label="$t('feedBack:search:trials')" v-if="level > 8">
|
||||
<el-input
|
||||
v-model="searchData.TrialKeyInfo"
|
||||
clearable
|
||||
style="width: 150px"
|
||||
></el-input>
|
||||
<el-input v-model="searchData.TrialKeyInfo" clearable style="width: 150px"></el-input>
|
||||
</el-form-item>
|
||||
<!--中心-->
|
||||
<el-form-item :label="$t('feedBack:search:site')" v-if="level > 7">
|
||||
<el-input
|
||||
v-model="searchData.TrialSiteCode"
|
||||
clearable
|
||||
style="width: 150px"
|
||||
></el-input>
|
||||
<el-input v-model="searchData.TrialSiteCode" clearable style="width: 150px"></el-input>
|
||||
</el-form-item>
|
||||
<!--受试者访视-->
|
||||
<el-form-item
|
||||
:label="$t('feedBack:search:subjectVisit')"
|
||||
v-if="level > 7"
|
||||
>
|
||||
<el-input
|
||||
v-model="searchData.SubejctAndVisitKeyInfo"
|
||||
clearable
|
||||
style="width: 150px"
|
||||
></el-input>
|
||||
<el-form-item :label="$t('feedBack:search:subjectVisit')" v-if="level > 7">
|
||||
<el-input v-model="searchData.SubejctAndVisitKeyInfo" clearable style="width: 150px"></el-input>
|
||||
</el-form-item>
|
||||
<!--角色-->
|
||||
<el-form-item :label="$t('feedBack:search:role')" v-if="level > 7">
|
||||
<el-select
|
||||
v-model="searchData.UserTypeEnum"
|
||||
clearable
|
||||
style="width: 150px"
|
||||
popper-class="feedBack-select-box"
|
||||
>
|
||||
<el-option
|
||||
v-for="item of UserTypeOptins"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
<el-select v-model="searchData.UserTypeEnum" clearable style="width: 150px"
|
||||
popper-class="feedBack-select-box">
|
||||
<el-option v-for="item of UserTypeOptins" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!--反馈人-->
|
||||
<el-form-item :label="$t('feedBack:search:user')" v-if="level > 7">
|
||||
<el-input
|
||||
v-model="searchData.FeedBackUserKeyInfo"
|
||||
clearable
|
||||
style="width: 150px"
|
||||
></el-input>
|
||||
<el-input v-model="searchData.FeedBackUserKeyInfo" clearable style="width: 150px"></el-input>
|
||||
</el-form-item>
|
||||
<!--问题类型-->
|
||||
<el-form-item :label="$t('feedBack:search:questionType')">
|
||||
<el-select
|
||||
v-model="searchData.QuestionType"
|
||||
clearable
|
||||
style="width: 150px"
|
||||
popper-class="feedBack-select-box"
|
||||
>
|
||||
<el-option
|
||||
v-for="item of QuestionTypeOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
<el-select v-model="searchData.QuestionType" clearable style="width: 150px"
|
||||
popper-class="feedBack-select-box">
|
||||
<el-option v-for="item of QuestionTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!--问题描述-->
|
||||
<el-form-item :label="$t('feedBack:search:description')">
|
||||
<el-input
|
||||
v-model="searchData.QuestionDescription"
|
||||
clearable
|
||||
style="width: 150px"
|
||||
></el-input>
|
||||
<el-input v-model="searchData.QuestionDescription" clearable style="width: 150px"></el-input>
|
||||
</el-form-item>
|
||||
<!--状态-->
|
||||
<el-form-item :label="$t('feedBack:search:status')" v-if="level > 7">
|
||||
<el-select
|
||||
v-model="searchData.State"
|
||||
clearable
|
||||
style="width: 150px"
|
||||
popper-class="feedBack-select-box"
|
||||
>
|
||||
<el-option
|
||||
v-for="item of $d.FeedBackStatus"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
<el-select v-model="searchData.State" clearable style="width: 150px" popper-class="feedBack-select-box">
|
||||
<el-option v-for="item of $d.FeedBackStatus" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!--反馈日期-->
|
||||
<el-form-item :label="$t('feedBack:search:time')">
|
||||
<el-date-picker
|
||||
v-model="datetimerange"
|
||||
type="datetimerange"
|
||||
:default-time="['00:00:00', '23:59:59']"
|
||||
:start-placeholder="$t('feedBack:search:beginTime')"
|
||||
:end-placeholder="$t('feedBack:search:endTime')"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
@change="handleDatetimeChange"
|
||||
style="width: 250px"
|
||||
popper-class="feedBack-select-box"
|
||||
/>
|
||||
<el-date-picker v-model="datetimerange" type="datetimerange" :default-time="['00:00:00', '23:59:59']"
|
||||
:start-placeholder="$t('feedBack:search:beginTime')" :end-placeholder="$t('feedBack:search:endTime')"
|
||||
value-format="yyyy-MM-dd HH:mm:ss" @change="handleDatetimeChange" style="width: 250px"
|
||||
popper-class="feedBack-select-box" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-search"
|
||||
@click="getList"
|
||||
>
|
||||
<el-button type="primary" icon="el-icon-search" @click="getList">
|
||||
{{ $t('common:button:search') }}
|
||||
</el-button>
|
||||
<!-- 重置 -->
|
||||
<el-button
|
||||
icon="el-icon-refresh-left"
|
||||
@click="handleReset"
|
||||
>
|
||||
<el-button icon="el-icon-refresh-left" @click="handleReset">
|
||||
{{ $t('common:button:reset') }}
|
||||
</el-button>
|
||||
<!-- <el-button
|
||||
|
|
@ -131,97 +66,32 @@
|
|||
{{ $t("common:button:export") }}
|
||||
</el-button> -->
|
||||
</el-form-item>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="resolve"
|
||||
style="float: right"
|
||||
v-if="level > 7"
|
||||
:disabled="tableSelectData.length <= 0"
|
||||
>
|
||||
<el-button type="primary" @click="resolve" style="float: right" v-if="level > 7"
|
||||
:disabled="tableSelectData.length <= 0">
|
||||
{{ $t('feedBack:button:resolve') }}
|
||||
</el-button>
|
||||
</el-form>
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
v-adaptive="{ bottomOffset: 45 }"
|
||||
height="100"
|
||||
:data="list"
|
||||
class="table"
|
||||
@selection-change="handleSelectChange"
|
||||
@sort-change="handleSortByColumn"
|
||||
:default-sort="{ prop: 'CreateTime', order: 'descending' }"
|
||||
>
|
||||
<el-table-column
|
||||
type="selection"
|
||||
align="center"
|
||||
width="45"
|
||||
v-if="level > 7"
|
||||
:selectable="handleSelectTable"
|
||||
/>
|
||||
<el-table v-loading="loading" v-adaptive="{ bottomOffset: 45 }" height="100" :data="list" class="table"
|
||||
@selection-change="handleSelectChange" @sort-change="handleSortByColumn"
|
||||
:default-sort="{ prop: 'CreateTime', order: 'descending' }">
|
||||
<el-table-column type="selection" align="center" width="45" v-if="level > 7" :selectable="handleSelectTable" />
|
||||
<el-table-column type="index" width="50" />
|
||||
<el-table-column
|
||||
:label="$t('feedBack:form:trialCode')"
|
||||
prop="TrialCode"
|
||||
min-width="100"
|
||||
show-overflow-tooltip
|
||||
sortable="custom"
|
||||
v-if="level > 8"
|
||||
/>
|
||||
<el-table-column
|
||||
:label="$t('feedBack:form:study')"
|
||||
prop="ExperimentName"
|
||||
min-width="140"
|
||||
show-overflow-tooltip
|
||||
sortable="custom"
|
||||
v-if="level > 8"
|
||||
/>
|
||||
<el-table-column
|
||||
:label="$t('feedBack:form:siteCode')"
|
||||
prop="TrialSiteCode"
|
||||
min-width="100"
|
||||
show-overflow-tooltip
|
||||
sortable="custom"
|
||||
v-if="level > 7"
|
||||
/>
|
||||
<el-table-column
|
||||
:label="$t('feedBack:form:SubjectCode')"
|
||||
prop="SubjectCode"
|
||||
min-width="120"
|
||||
show-overflow-tooltip
|
||||
sortable="custom"
|
||||
v-if="level > 7"
|
||||
/>
|
||||
<el-table-column
|
||||
:label="$t('feedBack:form:subjectVisit')"
|
||||
prop="SubjectVisitName"
|
||||
min-width="120"
|
||||
show-overflow-tooltip
|
||||
sortable="custom"
|
||||
v-if="level > 7"
|
||||
/>
|
||||
<el-table-column
|
||||
:label="$t('feedBack:form:role')"
|
||||
prop="FeedBackUserName"
|
||||
min-width="100"
|
||||
show-overflow-tooltip
|
||||
sortable="custom"
|
||||
v-if="level > 7"
|
||||
/>
|
||||
<el-table-column
|
||||
:label="$t('feedBack:form:user')"
|
||||
prop="FeedBackFullName"
|
||||
min-width="140"
|
||||
show-overflow-tooltip
|
||||
sortable="custom"
|
||||
v-if="level > 7"
|
||||
/>
|
||||
<el-table-column
|
||||
:label="$t('feedBack:form:questionType')"
|
||||
prop="QuestionType"
|
||||
min-width="180"
|
||||
show-overflow-tooltip
|
||||
sortable="custom"
|
||||
>
|
||||
<el-table-column :label="$t('feedBack:form:trialCode')" prop="TrialCode" min-width="100" show-overflow-tooltip
|
||||
sortable="custom" v-if="level > 8" />
|
||||
<el-table-column :label="$t('feedBack:form:study')" prop="ExperimentName" min-width="140" show-overflow-tooltip
|
||||
sortable="custom" v-if="level > 8" />
|
||||
<el-table-column :label="$t('feedBack:form:siteCode')" prop="TrialSiteCode" min-width="100"
|
||||
show-overflow-tooltip sortable="custom" v-if="level > 7" />
|
||||
<el-table-column :label="$t('feedBack:form:SubjectCode')" prop="SubjectCode" min-width="120"
|
||||
show-overflow-tooltip sortable="custom" v-if="level > 7" />
|
||||
<el-table-column :label="$t('feedBack:form:subjectVisit')" prop="SubjectVisitName" min-width="120"
|
||||
show-overflow-tooltip sortable="custom" v-if="level > 7" />
|
||||
<el-table-column :label="$t('feedBack:form:role')" prop="FeedBackUserName" min-width="100" show-overflow-tooltip
|
||||
sortable="custom" v-if="level > 7" />
|
||||
<el-table-column :label="$t('feedBack:form:user')" prop="FeedBackFullName" min-width="140" show-overflow-tooltip
|
||||
sortable="custom" v-if="level > 7" />
|
||||
<el-table-column :label="$t('feedBack:form:questionType')" prop="QuestionType" min-width="180"
|
||||
show-overflow-tooltip sortable="custom">
|
||||
<template slot-scope="scope">
|
||||
<span>{{
|
||||
QuestionTypeOptions.filter(
|
||||
|
|
@ -230,49 +100,22 @@
|
|||
}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
:label="$t('feedBack:form:description')"
|
||||
prop="QuestionDescription"
|
||||
min-width="200"
|
||||
show-overflow-tooltip
|
||||
sortable="custom"
|
||||
/>
|
||||
<el-table-column
|
||||
:label="$t('feedBack:form:time')"
|
||||
prop="CreateTime"
|
||||
min-width="180"
|
||||
show-overflow-tooltip
|
||||
sortable="custom"
|
||||
/>
|
||||
<el-table-column
|
||||
:label="$t('feedBack:form:status')"
|
||||
prop="State"
|
||||
min-width="120"
|
||||
show-overflow-tooltip
|
||||
sortable="custom"
|
||||
v-if="level > 7"
|
||||
>
|
||||
<el-table-column :label="$t('feedBack:form:description')" prop="QuestionDescription" min-width="200"
|
||||
show-overflow-tooltip sortable="custom" />
|
||||
<el-table-column :label="$t('feedBack:form:time')" prop="CreateTime" min-width="180" show-overflow-tooltip
|
||||
sortable="custom" />
|
||||
<el-table-column :label="$t('feedBack:form:status')" prop="State" min-width="120" show-overflow-tooltip
|
||||
sortable="custom" v-if="level > 7">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="['danger', 'success'][scope.row.State]">{{
|
||||
<el-tag :type="['danger', 'success', 'danger'][scope.row.State]">{{
|
||||
$fd('FeedBackStatus', scope.row.State)
|
||||
}}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
:label="$t('feedBack:form:uploadTime')"
|
||||
prop="UpdateTime"
|
||||
min-width="180"
|
||||
show-overflow-tooltip
|
||||
sortable="custom"
|
||||
v-if="level > 7"
|
||||
/>
|
||||
<el-table-column
|
||||
:label="$t('common:action:action')"
|
||||
fixed="right"
|
||||
prop="UserTypeShortName"
|
||||
width="100"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<el-table-column :label="$t('feedBack:form:uploadTime')" prop="UpdateTime" min-width="180" show-overflow-tooltip
|
||||
sortable="custom" v-if="level > 7" />
|
||||
<el-table-column :label="$t('common:action:action')" fixed="right" prop="UserTypeShortName" width="100"
|
||||
show-overflow-tooltip>
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" @click="getDetail(scope.row)">
|
||||
{{ $t('common:button:view') }}
|
||||
|
|
@ -281,12 +124,8 @@
|
|||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="pagination" style="text-align: right; margin-top: 5px">
|
||||
<pagination
|
||||
:total="total"
|
||||
:page.sync="searchData.PageIndex"
|
||||
:limit.sync="searchData.PageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
<pagination :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize"
|
||||
@pagination="getList" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -472,31 +311,37 @@ export default {
|
|||
display: flex;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 0;
|
||||
flex-grow: 4;
|
||||
|
||||
// border-right: 1px solid #ccc;
|
||||
.filter-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.data-table {
|
||||
flex: 1;
|
||||
padding: 5px 0px;
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
width: 0;
|
||||
flex-grow: 6;
|
||||
overflow-y: auto;
|
||||
border-right: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.selected-row {
|
||||
background-color: cadetblue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -152,7 +152,12 @@
|
|||
<!-- {{ $t('login:title:system_title_about') }} -->
|
||||
<svg-icon icon-class="login-logo" style="width: 250px; height: 71px" />
|
||||
</p>
|
||||
<p style="margin-bottom: 0px" v-else>{{ $t('login:title:system') }}</p>
|
||||
<div style="margin-bottom: 0px" v-else>
|
||||
<!-- {{ $t('login:title:system_title_about') }} -->
|
||||
<img src="@/assets/system.png" alt=""
|
||||
:style="{ width: isEN ? '180px' : '200px', height: isEN ? '60px' : '65px' }" />
|
||||
<p style="margin-bottom: 0px">{{ $t('login:title:system') }}</p>
|
||||
</div>
|
||||
<p style="margin-bottom: 20px; margin-top: 0">
|
||||
V{{ $version.IsEnv_US ? $version.Version_US : $version.Version }}
|
||||
</p>
|
||||
|
|
@ -167,12 +172,12 @@
|
|||
Copyright © {{ new Date().getFullYear() }} Shanghai Extensive Imaging
|
||||
Inc.
|
||||
</p>
|
||||
<div style="margin-bottom: 20px" v-if="NODE_ENV === 'usa'">
|
||||
<!-- <div style="margin-bottom: 20px" v-if="NODE_ENV === 'usa'">
|
||||
<img style="width: 180px" src="@/assets/zzlogo-usa.png" alt="" />
|
||||
</div>
|
||||
<div style="margin-bottom: 20px" v-else>
|
||||
<img style="width: 180px" src="@/assets/zzlogo2.png" alt="" />
|
||||
</div>
|
||||
</div> -->
|
||||
</div>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button type="primary" size="mini" @click="aboutVisible = false">
|
||||
|
|
|
|||
|
|
@ -49,9 +49,52 @@
|
|||
<el-input-number v-model="form.AverageEngravingCycle"
|
||||
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" controls-position="right" :min="0" />
|
||||
</el-form-item>
|
||||
<!-- MRI-PDFF 是否为本中心该适应症的常规诊疗检查项目? -->
|
||||
<el-form-item v-if="!notShowFieldList.includes('IsRoutineMRIPDEE')"
|
||||
:label="$t('trials:researchForm:form:IsRoutineMRIPDEE')" prop="IsRoutineMRIPDEE">
|
||||
<el-radio-group v-model="form.IsRoutineMRIPDEE" :disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory">
|
||||
<el-radio v-for="item of $d.YesOrNo" :key="`IsRoutineMRIPDEE${item.value}`" :label="item.value">{{
|
||||
item.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<!-- MRI-PDFF 检查的检测周期(含单次检查时长、预约等待时长等) -->
|
||||
<el-form-item
|
||||
v-if="!notShowFieldList.includes('MRIPDFFScanTime') || !notShowFieldList.includes('MRIPDFFLeadTime') || !notShowFieldList.includes('MRIPDFFOther')"
|
||||
:label="$t('trials:researchForm:form:IsMRIPDFF')">
|
||||
</el-form-item>
|
||||
<!-- 单次检查时长(分钟) -->
|
||||
<el-form-item v-if="!notShowFieldList.includes('MRIPDFFScanTime')"
|
||||
:label="$t('trials:researchForm:form:MRIPDFFScanTime')">
|
||||
<el-input-number v-model="form.MRIPDFFScanTime" :disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory"
|
||||
controls-position="right" :min="0" />
|
||||
</el-form-item>
|
||||
<!-- 平均预约等待时长(天) -->
|
||||
<el-form-item v-if="!notShowFieldList.includes('MRIPDFFLeadTime')"
|
||||
:label="$t('trials:researchForm:form:MRIPDFFLeadTime')">
|
||||
<el-input-number v-model="form.MRIPDFFLeadTime" :disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory"
|
||||
controls-position="right" :min="0" />
|
||||
</el-form-item>
|
||||
<!-- 特殊情况备注-->
|
||||
<el-form-item v-if="!notShowFieldList.includes('MRIPDFFOther')"
|
||||
:label="$t('trials:researchForm:form:MRIPDFFOther')">
|
||||
<el-input v-model="form.MRIPDFFOther" type="textarea" :autosize="{ minRows: 2, maxRows: 4 }"
|
||||
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" />
|
||||
</el-form-item>
|
||||
<!-- 如已选择研究者评估,项目是否会授权影像科老师参与本试验?如不单独授权,是否可在试验中保持 1-2 名固定技师操作?-->
|
||||
<el-form-item
|
||||
v-if="!notShowFieldList.includes('IsAuthorizeRadiologistsParticipate') || !notShowFieldList.includes('AssignFixedTechnologists')"
|
||||
:label="$t('trials:researchForm:form:IsAuthorize')" prop="IsAuthorize">
|
||||
<el-radio-group v-model="form.IsAuthorize" :disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory"
|
||||
@input="handleIsAuthorizeInput">
|
||||
<el-radio label="IsAuthorizeRadiologistsParticipate">{{
|
||||
$t('trials:researchForm:form:IsAuthorizeRadiologistsParticipate') }}</el-radio>
|
||||
<el-radio label="AssignFixedTechnologists">{{
|
||||
$t('trials:researchForm:form:AssignFixedTechnologists') }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<!-- 请确认参与本项目影像采集的影像技师具备对应的资质(如:“技师证”,对应设备的“大型设备上岗证”) -->
|
||||
<el-form-item v-if="!notShowFieldList.includes('IsConfirmImagingTechnologist')"
|
||||
:label="$t('trials:researchForm:form:isQualified')">
|
||||
:label="$t('trials:researchForm:form:isQualified')" prop="IsConfirmImagingTechnologist">
|
||||
<el-radio-group v-model="form.IsConfirmImagingTechnologist"
|
||||
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory">
|
||||
<el-radio v-for="item of $d.YesOrNo" :key="`IsConfirmImagingTechnologist${item.value}`" :label="item.value">{{
|
||||
|
|
@ -60,7 +103,7 @@
|
|||
</el-form-item>
|
||||
<!-- 原因 -->
|
||||
<el-form-item v-if="!notShowFieldList.includes('NotConfirmReson') && form.IsConfirmImagingTechnologist === false"
|
||||
:label="$t('trials:researchForm:form:notQualifiedReason')">
|
||||
:label="$t('trials:researchForm:form:notQualifiedReason')" prop="NotConfirmReson">
|
||||
<el-input v-model="form.NotConfirmReson" type="textarea" :autosize="{ minRows: 2, maxRows: 4 }"
|
||||
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" />
|
||||
</el-form-item>
|
||||
|
|
@ -74,7 +117,7 @@
|
|||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<!-- 是否严格按照研究单位影像手册参数完成图像采集 -->
|
||||
<el-form-item v-if="!notShowFieldList.includes('IsFollowStudyParameters')">
|
||||
<el-form-item v-if="!notShowFieldList.includes('IsFollowStudyParameters')" prop="IsFollowStudyParameters">
|
||||
<span slot="label" v-html="$t('trials:researchForm:form:isFollowStudyParam')" />
|
||||
<el-radio-group v-model="form.IsFollowStudyParameters"
|
||||
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" style="margin-right: 10px;">
|
||||
|
|
@ -86,11 +129,28 @@
|
|||
</el-button>
|
||||
</el-form-item>
|
||||
<!-- 不能严格按照研究单位影像手册参数采集图像原因 -->
|
||||
<el-form-item v-if="!notShowFieldList.includes('NotFollowReson') && !form.IsFollowStudyParameters">
|
||||
<el-form-item v-if="!notShowFieldList.includes('NotFollowReson') && !form.IsFollowStudyParameters"
|
||||
prop="NotFollowReson">
|
||||
<span slot="label" v-html="$t('trials:researchForm:form:notFollowStudyParam')" />
|
||||
<el-input v-model="form.NotFollowReson" type="textarea" :autosize="{ minRows: 2, maxRows: 4 }"
|
||||
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" />
|
||||
</el-form-item>
|
||||
<!-- 是否严格按照影像手册参数完成刻盘 -->
|
||||
<el-form-item v-if="!notShowFieldList.includes('ISStrictManualBurnFlag')" prop="ISStrictManualBurnFlag">
|
||||
<span slot="label" v-html="$t('trials:researchForm:form:ISStrictManualBurnFlag')" />
|
||||
<el-radio-group v-model="form.ISStrictManualBurnFlag"
|
||||
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" style="margin-right: 10px;">
|
||||
<el-radio v-for="item of $d.YesOrNo" :key="`ISStrictManualBurnFlag${item.value}`" :label="item.value">{{
|
||||
item.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<!-- 不能严格按照影像手册参数完成刻盘原因 -->
|
||||
<el-form-item v-if="!notShowFieldList.includes('NotStrictManualBurnFlagReason') && !form.ISStrictManualBurnFlag"
|
||||
prop="NotStrictManualBurnFlagReason">
|
||||
<span slot="label" v-html="$t('trials:researchForm:form:NotStrictManualBurnFlagReason')" />
|
||||
<el-input v-model="form.NotStrictManualBurnFlagReason" type="textarea" :autosize="{ minRows: 2, maxRows: 4 }"
|
||||
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<!-- 保存 -->
|
||||
<el-button v-if="state === 0 && userTypeEnumInt === 0 && !isHistory" type="primary" :loading="btnLoading"
|
||||
|
|
@ -152,6 +212,15 @@ export default {
|
|||
Phone: '', // 联系人电话
|
||||
Email: '', // 联系人邮箱
|
||||
AverageEngravingCycle: '',
|
||||
IsRoutineMRIPDEE: '',
|
||||
MRIPDFFScanTime: '',
|
||||
MRIPDFFLeadTime: '',
|
||||
MRIPDFFOther: '',
|
||||
IsAuthorize: '',
|
||||
IsAuthorizeRadiologistsParticipate: '',
|
||||
AssignFixedTechnologists: '',
|
||||
ISStrictManualBurnFlag: '',
|
||||
NotStrictManualBurnFlagReason: '',
|
||||
IsConfirmImagingTechnologist: '',
|
||||
NotConfirmReson: '',
|
||||
EfficacyEvaluatorType: '',
|
||||
|
|
@ -162,6 +231,30 @@ export default {
|
|||
TrialSiteId: [
|
||||
{ required: true, message: this.$t('trials:researchForm:formRule:specify'), trigger: 'blur' }
|
||||
],
|
||||
IsRoutineMRIPDEE: [
|
||||
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: 'blur' }
|
||||
],
|
||||
IsAuthorize: [
|
||||
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: 'blur' }
|
||||
],
|
||||
ISStrictManualBurnFlag: [
|
||||
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: 'blur' }
|
||||
],
|
||||
IsFollowStudyParameters: [
|
||||
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: 'blur' }
|
||||
],
|
||||
IsConfirmImagingTechnologist: [
|
||||
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: 'blur' }
|
||||
],
|
||||
NotConfirmReson: [
|
||||
{ required: true, message: this.$t('trials:researchForm:formRule:specify'), trigger: 'blur' }
|
||||
],
|
||||
NotFollowReson: [
|
||||
{ required: true, message: this.$t('trials:researchForm:formRule:specify'), trigger: 'blur' }
|
||||
],
|
||||
NotStrictManualBurnFlagReason: [
|
||||
{ required: true, message: this.$t('trials:researchForm:formRule:specify'), trigger: 'blur' }
|
||||
],
|
||||
UserName: [
|
||||
{ required: true, validator: (rule, value, callback) => { !value ? callback(new Error(this.$t('trials:researchForm:formRule:specify'))) : callback() }, trigger: 'blur' },
|
||||
{ min: 0, max: 50, message: this.$t('trials:researchForm:formRule:maxLength'), trigger: ['blur', 'change'] }
|
||||
|
|
@ -188,6 +281,11 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
handleIsAuthorizeInput(label) {
|
||||
this.form.IsAuthorizeRadiologistsParticipate = false
|
||||
this.form.AssignFixedTechnologists = false
|
||||
this.form[label] = true
|
||||
},
|
||||
async viewManual() {
|
||||
try {
|
||||
let data = {
|
||||
|
|
@ -231,6 +329,14 @@ export default {
|
|||
phone: this.form.Phone,
|
||||
email: this.form.Email,
|
||||
averageEngravingCycle: this.form.AverageEngravingCycle,
|
||||
IsRoutineMRIPDEE: this.form.IsRoutineMRIPDEE,
|
||||
MRIPDFFScanTime: this.form.MRIPDFFScanTime,
|
||||
MRIPDFFLeadTime: this.form.MRIPDFFLeadTime,
|
||||
MRIPDFFOther: this.form.MRIPDFFOther,
|
||||
IsAuthorizeRadiologistsParticipate: this.form.IsAuthorizeRadiologistsParticipate,
|
||||
AssignFixedTechnologists: this.form.AssignFixedTechnologists,
|
||||
ISStrictManualBurnFlag: this.form.ISStrictManualBurnFlag,
|
||||
NotStrictManualBurnFlagReason: this.form.NotStrictManualBurnFlagReason,
|
||||
isConfirmImagingTechnologist: this.form.IsConfirmImagingTechnologist,
|
||||
notConfirmReson: this.form.NotConfirmReson,
|
||||
efficacyEvaluatorType: this.form.EfficacyEvaluatorType,
|
||||
|
|
@ -275,6 +381,16 @@ export default {
|
|||
this.form.Phone = trialSiteSurvey.Phone // 联系人电话
|
||||
this.form.Email = trialSiteSurvey.Email // 联系人邮箱
|
||||
this.form.AverageEngravingCycle = trialSiteSurvey.AverageEngravingCycle
|
||||
this.form.IsRoutineMRIPDEE = trialSiteSurvey.IsRoutineMRIPDEE
|
||||
this.form.MRIPDFFScanTime = trialSiteSurvey.MRIPDFFScanTime
|
||||
this.form.MRIPDFFLeadTime = trialSiteSurvey.MRIPDFFLeadTime
|
||||
this.form.MRIPDFFOther = trialSiteSurvey.MRIPDFFOther
|
||||
this.form.IsAuthorizeRadiologistsParticipate = trialSiteSurvey.IsAuthorizeRadiologistsParticipate
|
||||
this.form.AssignFixedTechnologists = trialSiteSurvey.AssignFixedTechnologists
|
||||
if (this.form.IsAuthorizeRadiologistsParticipate) this.form.IsAuthorize = 'IsAuthorizeRadiologistsParticipate'
|
||||
if (this.form.AssignFixedTechnologists) this.form.IsAuthorize = 'AssignFixedTechnologists'
|
||||
this.form.ISStrictManualBurnFlag = trialSiteSurvey.ISStrictManualBurnFlag
|
||||
this.form.NotStrictManualBurnFlagReason = trialSiteSurvey.NotStrictManualBurnFlagReason
|
||||
this.form.IsConfirmImagingTechnologist = trialSiteSurvey.IsConfirmImagingTechnologist
|
||||
this.form.NotConfirmReson = trialSiteSurvey.NotConfirmReson
|
||||
this.form.EfficacyEvaluatorType = trialSiteSurvey.EfficacyEvaluatorType
|
||||
|
|
|
|||
|
|
@ -1,40 +1,98 @@
|
|||
<template>
|
||||
<el-form ref="equipmentForm" :model="form" :rules="rules" label-width="150px">
|
||||
<el-form ref="equipmentForm" :model="form" :rules="rules" label-width="380px">
|
||||
<div class="base-dialog-body">
|
||||
<!-- 扫描设备 -->
|
||||
<el-form-item :label="$t('trials:equiptResearch:form:equipment')" prop="EquipmentTypeId">
|
||||
<el-select
|
||||
v-model="form.EquipmentTypeId"
|
||||
style="width:100%"
|
||||
>
|
||||
<!-- <el-option
|
||||
v-for="item of dictionaryList.SiteSurvey_ScanEquipmentType"
|
||||
:key="item.Id"
|
||||
:label="item.Value"
|
||||
:value="item.Id"
|
||||
/> -->
|
||||
<el-option
|
||||
v-for="item of $d.SiteSurvey_ScanEquipmentType"
|
||||
:key="item.id"
|
||||
:label="item.label"
|
||||
:value="item.id"
|
||||
/>
|
||||
<el-form-item :label="$t('trials:equiptResearch:form:equipment')" prop="EquipmentTypeEnum"
|
||||
v-if="EquipmentControlFieldList.includes('EquipmentTypeEnum')">
|
||||
<div style="display: flex;align-items: center;">
|
||||
<el-select v-model="form.EquipmentTypeEnum" style="width:100%" @change="form.OtherEquipmentType = null">
|
||||
<el-option v-for="item of $d.SiteSurvey_ScanEquipmentType" :key="item.id" :label="item.label"
|
||||
:value="item.value" />
|
||||
</el-select>
|
||||
<el-input placeholder="" v-model="form.OtherEquipmentType" style="margin-left: 10px;"
|
||||
v-if="form.EquipmentTypeEnum == '-1'" clearable>
|
||||
</el-input>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<!-- 扫描参数 -->
|
||||
<el-form-item v-if="isShowParameters" :label="$t('trials:equiptResearch:form:param')">
|
||||
<el-form-item :label="$t('trials:equiptResearch:form:param')"
|
||||
v-if="EquipmentControlFieldList.includes('Parameters')" prop="Parameters">
|
||||
<el-input v-model="form.Parameters" />
|
||||
</el-form-item>
|
||||
<!-- 扫描仪器制造商名称 -->
|
||||
<el-form-item :label="$t('trials:equiptResearch:form:manufacturer')">
|
||||
<el-input v-model="form.ManufacturerName" />
|
||||
<el-form-item :label="$t('trials:equiptResearch:form:manufacturer')"
|
||||
v-if="EquipmentControlFieldList.includes('ManufacturerType')" prop="ManufacturerType">
|
||||
<div style="display: flex;align-items: center;">
|
||||
<el-select v-model="form.ManufacturerType" style="width:100%" @change="form.ManufacturerName = null">
|
||||
<el-option v-for="item of $d.ManufacturerType" :key="item.id" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
<el-input placeholder="" v-model="form.ManufacturerName" style="margin-left: 10px;"
|
||||
v-if="form.ManufacturerType == '-1'" clearable>
|
||||
</el-input>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<!-- 扫描仪型号 -->
|
||||
<el-form-item :label="$t('trials:equiptResearch:form:model')">
|
||||
<el-form-item :label="$t('trials:equiptResearch:form:model')"
|
||||
v-if="EquipmentControlFieldList.includes('ScannerType')" prop="ScannerType">
|
||||
<el-input v-model="form.ScannerType" />
|
||||
</el-form-item>
|
||||
<!-- 磁场强度 -->
|
||||
<el-form-item :label="$t('trials:equiptResearch:form:MagneticFieldStrengthType')"
|
||||
v-if="EquipmentControlFieldList.includes('MagneticFieldStrengthType')" prop="MagneticFieldStrengthType">
|
||||
<el-select v-model="form.MagneticFieldStrengthType" style="width:100%">
|
||||
<el-option v-for="item of $d.MagneticFieldStrengthType" :key="item.id" :label="item.label"
|
||||
:value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 体部线圈通道数 -->
|
||||
<el-form-item :label="$t('trials:equiptResearch:form:BodyCoilChannelCount')"
|
||||
v-if="EquipmentControlFieldList.includes('BodyCoilChannelCount')" prop="BodyCoilChannelCount">
|
||||
<el-select v-model="form.BodyCoilChannelCount" style="width:100%">
|
||||
<el-option v-for="item of $d.BodyCoilChannelCount" :key="item.id" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 是否具备专用的PDFF脂肪定量序列(CSE-MRI序列) -->
|
||||
<el-form-item :label="$t('trials:equiptResearch:form:HasDedicatedPdfFatQuantificationSequence')"
|
||||
v-if="EquipmentControlFieldList.includes('HasDedicatedPdfFatQuantificationSequence')"
|
||||
prop="HasDedicatedPdfFatQuantificationSequence">
|
||||
<el-select v-model="form.HasDedicatedPdfFatQuantificationSequence" style="width:100%"
|
||||
@change="form.PdfFatQuantificationSequenceType = null, form.OtherSequenceSpecification = null">
|
||||
<el-option v-for="item of $d.YesOrNo" :key="item.id" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- PDFF脂肪定量序列 -->
|
||||
<el-form-item :label="$t('trials:equiptResearch:form:PdfFatQuantificationSequenceType')"
|
||||
prop="PdfFatQuantificationSequenceType"
|
||||
v-if="form.HasDedicatedPdfFatQuantificationSequence && EquipmentControlFieldList.includes('PdfFatQuantificationSequenceType')">
|
||||
<div style="display: flex;align-items: center;">
|
||||
<el-select v-model="form.PdfFatQuantificationSequenceType" style="width:100%"
|
||||
@change="form.OtherSequenceSpecification = null">
|
||||
<el-option v-for="item of $d.PdfFatQuantificationSequenceType" :key="item.id" :label="item.label"
|
||||
:value="item.value" />
|
||||
</el-select>
|
||||
<el-input placeholder="" v-model="form.OtherSequenceSpecification" style="margin-left: 10px;"
|
||||
v-if="form.PdfFatQuantificationSequenceType == '-1'" clearable>
|
||||
</el-input>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<!-- 是否包含 T2/R2 校正(用于铁沉积校正) -->
|
||||
<el-form-item :label="$t('trials:equiptResearch:form:HasT2R2Correction')" prop="HasT2R2Correction"
|
||||
v-if="EquipmentControlFieldList.includes('HasT2R2Correction')">
|
||||
<el-select v-model="form.HasT2R2Correction" style="width:100%">
|
||||
<el-option v-for="item of $d.YesOrNo" :key="item.id" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 是否可完整导出 PDFF 参数图及全部原始 DICOM 数据 -->
|
||||
<el-form-item :label="$t('trials:equiptResearch:form:CanFullyExportPdfParameterMapsAndRawDicom')"
|
||||
prop="CanFullyExportPdfParameterMapsAndRawDicom"
|
||||
v-if="EquipmentControlFieldList.includes('CanFullyExportPdfParameterMapsAndRawDicom')">
|
||||
<el-select v-model="form.CanFullyExportPdfParameterMapsAndRawDicom" style="width:100%">
|
||||
<el-option v-for="item of $d.YesOrNo" :key="item.id" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 备注 -->
|
||||
<el-form-item :label="$t('trials:equiptResearch:form:remark')">
|
||||
<el-form-item :label="$t('trials:equiptResearch:form:remark')" prop="Note"
|
||||
v-if="EquipmentControlFieldList.includes('Note')">
|
||||
<el-input v-model="form.Note" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
|
@ -69,26 +127,94 @@ export default {
|
|||
type: String,
|
||||
default: ''
|
||||
},
|
||||
isShowParameters: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
EquipmentControlFieldList: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return []
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
form: {
|
||||
Id: '',
|
||||
EquipmentTypeId: '',
|
||||
Parameters: '',
|
||||
ManufacturerName: '',
|
||||
ScannerType: '',
|
||||
Note: '',
|
||||
EquipmentTypeEnum: null,
|
||||
OtherEquipmentType: null,
|
||||
Parameters: null,
|
||||
Note: null,
|
||||
ManufacturerType: null,
|
||||
ManufacturerName: null,
|
||||
ScannerType: null,
|
||||
MagneticFieldStrengthType: null,
|
||||
BodyCoilChannelCount: null,
|
||||
HasDedicatedPdfFatQuantificationSequence: null,
|
||||
PdfFatQuantificationSequenceType: null,
|
||||
OtherSequenceSpecification: null,
|
||||
HasT2R2Correction: null,
|
||||
CanFullyExportPdfParameterMapsAndRawDicom: null,
|
||||
TrialSiteSurveyId: ''
|
||||
},
|
||||
rules: {
|
||||
EquipmentTypeId: [
|
||||
EquipmentTypeEnum: [
|
||||
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] },
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
if (this.form.EquipmentTypeEnum === -1 && !this.form.OtherEquipmentType) {
|
||||
callback(this.$t('common:ruleMessage:specify'));
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change']
|
||||
},
|
||||
],
|
||||
Parameters: [
|
||||
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change'] }
|
||||
],
|
||||
Note: [
|
||||
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change'] }
|
||||
],
|
||||
ManufacturerType: [
|
||||
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] },
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
if (this.form.ManufacturerType === -1 && !this.form.ManufacturerName) {
|
||||
callback(this.$t('common:ruleMessage:specify'));
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change']
|
||||
},
|
||||
],
|
||||
ScannerType: [
|
||||
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change'] }
|
||||
],
|
||||
MagneticFieldStrengthType: [
|
||||
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] }
|
||||
]
|
||||
],
|
||||
BodyCoilChannelCount: [
|
||||
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] }
|
||||
],
|
||||
HasDedicatedPdfFatQuantificationSequence: [
|
||||
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] }
|
||||
],
|
||||
PdfFatQuantificationSequenceType: [
|
||||
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] },
|
||||
{
|
||||
validator: (rule, value, callback) => {
|
||||
if (this.form.PdfFatQuantificationSequenceType === -1 && !this.form.OtherSequenceSpecification) {
|
||||
callback(this.$t('common:ruleMessage:specify'));
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change']
|
||||
},
|
||||
],
|
||||
HasT2R2Correction: [
|
||||
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] }
|
||||
],
|
||||
CanFullyExportPdfParameterMapsAndRawDicom: [
|
||||
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] }
|
||||
],
|
||||
},
|
||||
btnLoading: false,
|
||||
dictionaryList: {}
|
||||
|
|
|
|||
|
|
@ -2,63 +2,88 @@
|
|||
<div class="equipment-wrapper">
|
||||
<div class="header-wrapper">
|
||||
<!-- 新增 -->
|
||||
<el-button
|
||||
v-if="state === 0 && userTypeEnumInt === 0 && !isHistory"
|
||||
size="small"
|
||||
type="primary"
|
||||
@click="handleAdd"
|
||||
>
|
||||
<el-button v-if="state === 0 && userTypeEnumInt === 0 && !isHistory && EquipmentControlFieldList.length > 0"
|
||||
size="small" type="primary" @click="handleAdd">
|
||||
{{ $t('common:button:add') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="list"
|
||||
border
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table v-loading="loading" :data="list" border style="width: 100%">
|
||||
<!-- 扫描设备 -->
|
||||
<el-table-column
|
||||
prop="EquipmentType"
|
||||
:label="$t('trials:equiptResearch:form:equipment')"
|
||||
min-width="120"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column v-if="EquipmentControlFieldList.includes('EquipmentTypeEnum')" prop="EquipmentTypeEnum"
|
||||
:label="$t('trials:equiptResearch:form:equipment')" min-width="120" show-overflow-tooltip>
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.OtherEquipmentType ? scope.row.OtherEquipmentType :
|
||||
$fd('SiteSurvey_ScanEquipmentType', scope.row.EquipmentTypeEnum) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- 扫描参数 -->
|
||||
<el-table-column
|
||||
v-if="isShowParameters"
|
||||
prop="Parameters"
|
||||
:label="$t('trials:equiptResearch:form:param')"
|
||||
min-width="100"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column v-if="EquipmentControlFieldList.includes('Parameters')" prop="Parameters"
|
||||
:label="$t('trials:equiptResearch:form:param')" min-width="100" show-overflow-tooltip />
|
||||
<!-- 扫描仪器制造商名称 -->
|
||||
<el-table-column
|
||||
min-width="120"
|
||||
prop="ManufacturerName"
|
||||
:label="$t('trials:equiptResearch:form:manufacturer')"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column v-if="EquipmentControlFieldList.includes('ManufacturerType')" min-width="120"
|
||||
prop="ManufacturerType" :label="$t('trials:equiptResearch:form:manufacturer')" show-overflow-tooltip>
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.ManufacturerName ? scope.row.ManufacturerName : $fd('ManufacturerType',
|
||||
scope.row.ManufacturerType) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- 扫描仪型号 -->
|
||||
<el-table-column
|
||||
min-width="120"
|
||||
prop="ScannerType"
|
||||
:label="$t('trials:equiptResearch:form:model')"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column v-if="EquipmentControlFieldList.includes('ScannerType')" min-width="120" prop="ScannerType"
|
||||
:label="$t('trials:equiptResearch:form:model')" show-overflow-tooltip />
|
||||
<!-- 磁场强度 -->
|
||||
<el-table-column v-if="EquipmentControlFieldList.includes('MagneticFieldStrengthType')" min-width="120"
|
||||
prop="MagneticFieldStrengthType" :label="$t('trials:equiptResearch:form:MagneticFieldStrengthType')"
|
||||
show-overflow-tooltip>
|
||||
<template slot-scope="scope">
|
||||
{{ $fd('MagneticFieldStrengthType', scope.row.MagneticFieldStrengthType) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- 体部线圈通道数 -->
|
||||
<el-table-column v-if="EquipmentControlFieldList.includes('BodyCoilChannelCount')" min-width="120"
|
||||
prop="BodyCoilChannelCount" :label="$t('trials:equiptResearch:form:BodyCoilChannelCount')"
|
||||
show-overflow-tooltip>
|
||||
<template slot-scope="scope">
|
||||
{{ $fd('BodyCoilChannelCount', scope.row.BodyCoilChannelCount) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- 是否具备专用的PDFF脂肪定量序列(CSE-MRI序列) -->
|
||||
<el-table-column v-if="EquipmentControlFieldList.includes('HasDedicatedPdfFatQuantificationSequence')"
|
||||
min-width="120" prop="HasDedicatedPdfFatQuantificationSequence"
|
||||
:label="$t('trials:equiptResearch:form:HasDedicatedPdfFatQuantificationSequence')" show-overflow-tooltip>
|
||||
<template slot-scope="scope">
|
||||
{{ $fd('YesOrNo', scope.row.HasDedicatedPdfFatQuantificationSequence) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- 专用的PDFF脂肪定量序列类型 -->
|
||||
<el-table-column v-if="EquipmentControlFieldList.includes('PdfFatQuantificationSequenceType')" min-width="120"
|
||||
prop="PdfFatQuantificationSequenceType"
|
||||
:label="$t('trials:equiptResearch:form:PdfFatQuantificationSequenceType')" show-overflow-tooltip><template
|
||||
slot-scope="scope">
|
||||
{{ scope.row.OtherSequenceSpecification ? scope.row.OtherSequenceSpecification :
|
||||
$fd('PdfFatQuantificationSequenceType', scope.row.PdfFatQuantificationSequenceType) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- 是否包含 T2/R2 校正(用于铁沉积校正) -->
|
||||
<el-table-column v-if="EquipmentControlFieldList.includes('HasT2R2Correction')" min-width="120"
|
||||
prop="HasT2R2Correction" :label="$t('trials:equiptResearch:form:HasT2R2Correction')" show-overflow-tooltip>
|
||||
<template slot-scope="scope">
|
||||
{{ $fd('YesOrNo', scope.row.HasT2R2Correction) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- 是否可完整导出 PDFF 参数图及全部原始 DICOM 数据 -->
|
||||
<el-table-column v-if="EquipmentControlFieldList.includes('CanFullyExportPdfParameterMapsAndRawDicom')"
|
||||
prop="CanFullyExportPdfParameterMapsAndRawDicom"
|
||||
:label="$t('trials:equiptResearch:form:CanFullyExportPdfParameterMapsAndRawDicom')" min-width="120"
|
||||
show-overflow-tooltip>
|
||||
<template slot-scope="scope">
|
||||
{{ $fd('YesOrNo', scope.row.CanFullyExportPdfParameterMapsAndRawDicom) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- 备注 -->
|
||||
<el-table-column
|
||||
min-width="120"
|
||||
prop="Note"
|
||||
:label="$t('trials:equiptResearch:form:precautions')"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
v-if="state === 0 && userTypeEnumInt === 0 && !isHistory"
|
||||
fixed="right"
|
||||
:label="$t('common:action:action')"
|
||||
width="100"
|
||||
>
|
||||
<el-table-column v-if="EquipmentControlFieldList.includes('Note')" min-width="120" prop="Note"
|
||||
:label="$t('trials:equiptResearch:form:precautions')" show-overflow-tooltip />
|
||||
<el-table-column v-if="state === 0 && userTypeEnumInt === 0 && !isHistory" fixed="right"
|
||||
:label="$t('common:action:action')" width="100">
|
||||
<template slot-scope="scope">
|
||||
<!-- 编辑 -->
|
||||
<el-button type="text" size="small" @click="handleEdit(scope.row)">
|
||||
|
|
@ -73,16 +98,10 @@
|
|||
</el-table>
|
||||
|
||||
<!-- 新增/编辑人员信息 -->
|
||||
<el-dialog
|
||||
v-if="editVisible"
|
||||
:visible.sync="editVisible"
|
||||
:close-on-click-modal="false"
|
||||
:title="title"
|
||||
width="600px"
|
||||
custom-class="base-dialog-wrapper"
|
||||
:append-to-body="userTypeEnumInt !== 0"
|
||||
>
|
||||
<EquipmentForm :data="rowData" :trial-site-survey-equipment-type="trialSiteSurveyEquipmentType" :is-show-parameters="isShowParameters" @getList="getList" @close="closeDialog" />
|
||||
<el-dialog v-if="editVisible" :visible.sync="editVisible" :close-on-click-modal="false" :title="title" width="800px"
|
||||
custom-class="base-dialog-wrapper" :append-to-body="userTypeEnumInt !== 0">
|
||||
<EquipmentForm :data="rowData" :trial-site-survey-equipment-type="trialSiteSurveyEquipmentType"
|
||||
:EquipmentControlFieldList="EquipmentControlFieldList" @getList="getList" @close="closeDialog" />
|
||||
</el-dialog>
|
||||
|
||||
</div>
|
||||
|
|
@ -115,7 +134,7 @@ export default {
|
|||
state: null,
|
||||
trialSiteSurveyId: '',
|
||||
trialId: '',
|
||||
isShowParameters: false
|
||||
EquipmentControlFieldList: []
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
|
@ -162,8 +181,12 @@ export default {
|
|||
}).catch(() => { this.loading = false })
|
||||
}).catch(() => { })
|
||||
},
|
||||
initList(TrialSiteEquipmentSurveyList, trialSiteSurvey, isShowParameters) {
|
||||
this.isShowParameters = isShowParameters
|
||||
initList(TrialSiteEquipmentSurveyList, trialSiteSurvey, EquipmentControlFieldList) {
|
||||
this.EquipmentControlFieldList = []
|
||||
EquipmentControlFieldList.forEach(item => {
|
||||
this.EquipmentControlFieldList.push(item.FiledName)
|
||||
})
|
||||
console.log(this.EquipmentControlFieldList, 'this.EquipmentControlFieldList')
|
||||
this.list = TrialSiteEquipmentSurveyList
|
||||
this.state = trialSiteSurvey.State
|
||||
this.$forceUpdate()
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@ export default {
|
|||
this.$refs['historicalParticipant'].initList(historicalArr, res.Result.TrialSiteSurvey)
|
||||
this.$refs['researchParticipants'].initList(newArr, res.Result.TrialSiteSurvey)
|
||||
// this.$refs['researchParticipants'].initList(res.Result.TrialSiteUserSurveyList, res.Result.TrialSiteSurvey)
|
||||
this.$refs['researchEquipments'].initList(res.Result.TrialSiteEquipmentSurveyList, res.Result.TrialSiteSurvey, !(res.Result.SiteSurveyFiledConfig && res.Result.SiteSurveyFiledConfig.ModifyFiledList.length > 0))
|
||||
this.$refs['researchEquipments'].initList(res.Result.TrialSiteEquipmentSurveyList, res.Result.TrialSiteSurvey, res.Result.SiteSurveyFiledConfig.EquipmentControlFieldList)
|
||||
this.isExistIncorrect = res.Result.TrialSiteUserSurveyList.every(item => item.IsCorrect === false)
|
||||
}
|
||||
this.loading = false
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@ export default {
|
|||
|
||||
this.$refs['historicalParticipant'].initList(historicalArr, res.Result.TrialSiteSurvey)
|
||||
this.$refs['researchParticipants'].initList(newArr, res.Result.TrialSiteSurvey)
|
||||
this.$refs['researchEquipments'].initList(res.Result.TrialSiteEquipmentSurveyList, res.Result.TrialSiteSurvey, !(res.Result.SiteSurveyFiledConfig && res.Result.SiteSurveyFiledConfig.ModifyFiledList.length > 0))
|
||||
this.$refs['researchEquipments'].initList(res.Result.TrialSiteEquipmentSurveyList, res.Result.TrialSiteSurvey, res.Result.SiteSurveyFiledConfig.EquipmentControlFieldList)
|
||||
this.isExistIncorrect = res.Result.TrialSiteUserSurveyList.every(item => item.IsCorrect === false)
|
||||
}
|
||||
this.loading = false
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<h2 style="text-align:center;">
|
||||
<!-- 中心调研表 -->
|
||||
{{ $t('trials:researchForm:title:question') }}
|
||||
<!-- <TopLang style="position: fixed;top: 40px;right: 40px" /> -->
|
||||
<TopLang style="position: fixed;top: 40px;right: 40px" />
|
||||
</h2>
|
||||
<el-card shadow="hover">
|
||||
<el-form ref="resetForm" v-loading="loading" :model="form" label-width="150px" style="width:80%;margin:0 auto;"
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
<script>
|
||||
import { mapGetters, mapMutations } from 'vuex'
|
||||
|
||||
import { changeURLStatic } from '@/utils/history.js'
|
||||
export default {
|
||||
name: 'TopLang',
|
||||
|
||||
|
|
@ -44,7 +44,8 @@ export default {
|
|||
this.$i18n.locale = lang
|
||||
this.setLanguage(lang)
|
||||
this.$updateDictionary()
|
||||
// window.location.reload()
|
||||
changeURLStatic('lang', lang)
|
||||
window.location.reload()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,423 @@
|
|||
<template>
|
||||
<div class="systemConfig" v-loading="loading">
|
||||
<div ref="leftContainer" class="left" style="padding: 20px;">
|
||||
<el-form :inline="true" :model="form" :rules="rules" ref="systemConfigForm"
|
||||
style="display: flex;align-items: center;justify-content: space-between;flex-wrap: wrap;">
|
||||
<h3>{{ $t("system:config:BasicSystemConfig") }}</h3>
|
||||
<el-form-item :label="$t('system:config:QcRiskControl')" style="width: 45%;">
|
||||
<el-select v-model="form.BasicSystemConfig.QCRiskControl" placeholder="">
|
||||
<el-option v-for="item in $d.YesOrNo" :key="item.id" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:config:OpenUserComplexPassword')" style="width: 45%;">
|
||||
<el-select v-model="form.BasicSystemConfig.OpenUserComplexPassword" placeholder="">
|
||||
<el-option v-for="item in $d.YesOrNo" :key="item.id" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:config:OpenSignDocumentBeforeWork')" style="width: 45%;">
|
||||
<el-select v-model="form.BasicSystemConfig.OpenSignDocumentBeforeWork" placeholder="">
|
||||
<el-option v-for="item in $d.YesOrNo" :key="item.id" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:config:OpenTrialRelationDelete')" style="width: 45%;">
|
||||
<el-select v-model="form.BasicSystemConfig.OpenTrialRelationDelete" placeholder="">
|
||||
<el-option v-for="item in $d.YesOrNo" :key="item.id" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:config:OpenLoginLimit')" style="width: 45%;">
|
||||
<el-select v-model="form.BasicSystemConfig.OpenLoginLimit" placeholder="">
|
||||
<el-option v-for="item in $d.YesOrNo" :key="item.id" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:config:LoginMaxFailCount')" style="width: 45%;"
|
||||
prop="BasicSystemConfig.LoginMaxFailCount">
|
||||
<el-input v-model="form.BasicSystemConfig.LoginMaxFailCount" clearable type="number" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:config:LoginFailLockMinutes')" style="width: 45%;"
|
||||
prop="BasicSystemConfig.LoginFailLockMinutes">
|
||||
<el-input v-model="form.BasicSystemConfig.LoginFailLockMinutes" clearable type="number" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:config:AutoLoginOutMinutes')" style="width: 45%;"
|
||||
prop="BasicSystemConfig.AutoLoginOutMinutes">
|
||||
<el-input v-model="form.BasicSystemConfig.AutoLoginOutMinutes" clearable type="number" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:config:OpenLoginMFA')" style="width: 45%;">
|
||||
<el-select v-model="form.BasicSystemConfig.OpenLoginMFA" placeholder="">
|
||||
<el-option v-for="item in $d.YesOrNo" :key="item.id" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:config:ContinuousReadingTimeMin')" style="width: 45%;"
|
||||
prop="BasicSystemConfig.ContinuousReadingTimeMin">
|
||||
<el-input v-model="form.BasicSystemConfig.ContinuousReadingTimeMin" clearable type="number" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:config:ReadingRestTimeMin')" style="width: 45%;"
|
||||
prop="BasicSystemConfig.ReadingRestTimeMin">
|
||||
<el-input v-model="form.BasicSystemConfig.ReadingRestTimeMin" clearable type="number" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:config:IsNeedChangePassWord')" style="width: 45%;">
|
||||
<el-select v-model="form.BasicSystemConfig.IsNeedChangePassWord" placeholder="">
|
||||
<el-option v-for="item in $d.YesOrNo" :key="item.id" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:config:ChangePassWordDays')" style="width: 45%;"
|
||||
prop="BasicSystemConfig.ChangePassWordDays">
|
||||
<el-input v-model="form.BasicSystemConfig.ChangePassWordDays" clearable type="number" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:config:TemplateType')" style="width: 45%;"
|
||||
prop="BasicSystemConfig.TemplateType">
|
||||
<el-select v-model="form.BasicSystemConfig.TemplateType" placeholder="">
|
||||
<el-option v-for="item in $d.SysTemplateType" :key="item.id" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:config:ThirdPdfUrl')" style="width: 45%;" prop="BasicSystemConfig.ThirdPdfUrl">
|
||||
<el-input v-model="form.BasicSystemConfig.ThirdPdfUrl" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:config:UserMFAVerifyMinutes')" style="width: 45%;"
|
||||
prop="BasicSystemConfig.UserMFAVerifyMinutes">
|
||||
<el-input v-model="form.BasicSystemConfig.UserMFAVerifyMinutes" clearable type="number" />
|
||||
</el-form-item>
|
||||
<h3>{{ $t("system:config:SystemEmailSendConfig") }}</h3>
|
||||
<el-form-item :label="$t('system:config:Host')" style="width: 45%;" prop="SystemEmailSendConfig.Host">
|
||||
<el-input v-model="form.SystemEmailSendConfig.Host" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:config:Port')" style="width: 45%;" prop="SystemEmailSendConfig.Port">
|
||||
<el-input v-model="form.SystemEmailSendConfig.Port" clearable type="number" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:config:Imap')" style="width: 45%;" prop="SystemEmailSendConfig.Imap">
|
||||
<el-input v-model="form.SystemEmailSendConfig.Imap" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:config:ImapPort')" style="width: 45%;" prop="SystemEmailSendConfig.ImapPort">
|
||||
<el-input v-model="form.SystemEmailSendConfig.ImapPort" clearable type="number" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:config:FromEmail')" style="width: 45%;" prop="SystemEmailSendConfig.FromEmail">
|
||||
<el-input v-model="form.SystemEmailSendConfig.FromEmail" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:config:FromName')" style="width: 45%;" prop="SystemEmailSendConfig.FromName">
|
||||
<el-input v-model="form.SystemEmailSendConfig.FromName" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:config:AuthorizationCode')" style="width: 45%;"
|
||||
prop="SystemEmailSendConfig.AuthorizationCode">
|
||||
<el-input v-model="form.SystemEmailSendConfig.AuthorizationCode" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:config:SiteUrl')" style="width: 45%;" prop="SystemEmailSendConfig.SiteUrl">
|
||||
<el-input v-model="form.SystemEmailSendConfig.SiteUrl" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:config:SystemShortName')" style="width: 45%;"
|
||||
prop="SystemEmailSendConfig.SystemShortName">
|
||||
<el-input v-model="form.SystemEmailSendConfig.SystemShortName" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:config:OrganizationName')" style="width: 45%;"
|
||||
prop="SystemEmailSendConfig.OrganizationName">
|
||||
<el-input v-model="form.SystemEmailSendConfig.OrganizationName" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:config:OrganizationNameCN')" style="width: 45%;"
|
||||
prop="SystemEmailSendConfig.OrganizationNameCN">
|
||||
<el-input v-model="form.SystemEmailSendConfig.OrganizationNameCN" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:config:PlatformName')" style="width: 45%;"
|
||||
prop="SystemEmailSendConfig.PlatformName">
|
||||
<el-input v-model="form.SystemEmailSendConfig.PlatformName" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:config:PlatformNameCN')" style="width: 45%;"
|
||||
prop="SystemEmailSendConfig.PlatformNameCN">
|
||||
<el-input v-model="form.SystemEmailSendConfig.PlatformNameCN" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:config:CompanyName')" style="width: 45%;"
|
||||
prop="SystemEmailSendConfig.CompanyName">
|
||||
<el-input v-model="form.SystemEmailSendConfig.CompanyName" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:config:CompanyNameCN')" style="width: 45%;"
|
||||
prop="SystemEmailSendConfig.CompanyNameCN">
|
||||
<el-input v-model="form.SystemEmailSendConfig.CompanyNameCN" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:config:CompanyShortName')" style="width: 45%;"
|
||||
prop="SystemEmailSendConfig.CompanyShortName">
|
||||
<el-input v-model="form.SystemEmailSendConfig.CompanyShortName" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:config:CompanyShortNameCN')" style="width: 45%;"
|
||||
prop="SystemEmailSendConfig.CompanyShortNameCN">
|
||||
<el-input v-model="form.SystemEmailSendConfig.CompanyShortNameCN" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:config:IsEnv_US')" style="width: 45%;">
|
||||
<el-select v-model="form.SystemEmailSendConfig.IsEnv_US" placeholder="">
|
||||
<el-option v-for="item in $d.YesOrNo" :key="item.id" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:config:EmailRegexStr')" style="width: 45%;"
|
||||
prop="SystemEmailSendConfig.EmailRegexStr">
|
||||
<el-input v-model="form.SystemEmailSendConfig.EmailRegexStr" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 100%;text-align: right;">
|
||||
<el-button type="primary" size="small" @click="handleSave">
|
||||
{{ $t('common:button:save') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import { getSystemBasicConfigInfo, getEmailConfigInfo, updateSystemBasicConfig, updateSystemEmailConfig } from '@/api/admin'
|
||||
export default {
|
||||
name: "systemConfig",
|
||||
data() {
|
||||
return {
|
||||
rules: {
|
||||
'BasicSystemConfig.LoginMaxFailCount': [
|
||||
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
|
||||
{ type: 'number', message: this.$t('systemConfig:ruleMessage:mustNumber'), trigger: 'blur' },
|
||||
],
|
||||
'BasicSystemConfig.LoginFailLockMinutes': [
|
||||
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
|
||||
{ type: 'number', message: this.$t('systemConfig:ruleMessage:mustNumber'), trigger: 'blur' },
|
||||
],
|
||||
'BasicSystemConfig.AutoLoginOutMinutes': [
|
||||
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
|
||||
{ type: 'number', message: this.$t('systemConfig:ruleMessage:mustNumber'), trigger: 'blur' },
|
||||
],
|
||||
'BasicSystemConfig.ContinuousReadingTimeMin': [
|
||||
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
|
||||
{ type: 'number', message: this.$t('systemConfig:ruleMessage:mustNumber'), trigger: 'blur' },
|
||||
],
|
||||
'BasicSystemConfig.ReadingRestTimeMin': [
|
||||
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
|
||||
{ type: 'number', message: this.$t('systemConfig:ruleMessage:mustNumber'), trigger: 'blur' },
|
||||
],
|
||||
'BasicSystemConfig.ChangePassWordDays': [
|
||||
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
|
||||
{ type: 'number', message: this.$t('systemConfig:ruleMessage:mustNumber'), trigger: 'blur' },
|
||||
],
|
||||
'BasicSystemConfig.TemplateType': [
|
||||
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
|
||||
{ type: 'number', message: this.$t('systemConfig:ruleMessage:mustNumber'), trigger: 'blur' },
|
||||
],
|
||||
'BasicSystemConfig.UserMFAVerifyMinutes': [
|
||||
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
|
||||
{ type: 'number', message: this.$t('systemConfig:ruleMessage:mustNumber'), trigger: 'blur' },
|
||||
],
|
||||
'BasicSystemConfig.ThirdPdfUrl': [
|
||||
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
|
||||
],
|
||||
|
||||
'SystemEmailSendConfig.Host': [
|
||||
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
|
||||
],
|
||||
'SystemEmailSendConfig.Port': [
|
||||
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
|
||||
{ type: 'number', message: this.$t('systemConfig:ruleMessage:mustNumber'), trigger: 'blur' },
|
||||
],
|
||||
'SystemEmailSendConfig.Imap': [
|
||||
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
|
||||
],
|
||||
'SystemEmailSendConfig.ImapPort': [
|
||||
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
|
||||
{ type: 'number', message: this.$t('systemConfig:ruleMessage:mustNumber'), trigger: 'blur' },
|
||||
],
|
||||
'SystemEmailSendConfig.FromEmail': [
|
||||
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
|
||||
],
|
||||
'SystemEmailSendConfig.FromName': [
|
||||
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
|
||||
],
|
||||
'SystemEmailSendConfig.AuthorizationCode': [
|
||||
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
|
||||
],
|
||||
'SystemEmailSendConfig.SiteUrl': [
|
||||
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
|
||||
],
|
||||
'SystemEmailSendConfig.SystemShortName': [
|
||||
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
|
||||
],
|
||||
'SystemEmailSendConfig.OrganizationName': [
|
||||
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
|
||||
],
|
||||
'SystemEmailSendConfig.OrganizationNameCN': [
|
||||
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
|
||||
],
|
||||
'SystemEmailSendConfig.PlatformName': [
|
||||
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
|
||||
],
|
||||
'SystemEmailSendConfig.PlatformNameCN': [
|
||||
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
|
||||
],
|
||||
'SystemEmailSendConfig.CompanyName': [
|
||||
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
|
||||
],
|
||||
'SystemEmailSendConfig.CompanyNameCN': [
|
||||
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
|
||||
],
|
||||
'SystemEmailSendConfig.CompanyShortName': [
|
||||
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
|
||||
],
|
||||
'SystemEmailSendConfig.CompanyShortNameCN': [
|
||||
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
|
||||
],
|
||||
'SystemEmailSendConfig.EmailRegexStr': [
|
||||
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
|
||||
],
|
||||
},
|
||||
form: {
|
||||
"BasicSystemConfig": {
|
||||
// 启用质控风险控制功能
|
||||
"QCRiskControl": true,
|
||||
// 打开用户复杂密码
|
||||
"OpenUserComplexPassword": false,
|
||||
// 是否在开始工作前强制签署电子知情同意书
|
||||
"OpenSignDocumentBeforeWork": false,
|
||||
// 是否打开项目关联删除
|
||||
"OpenTrialRelationDelete": false,
|
||||
//是否启用登录失败次数限制(防暴力破解)
|
||||
"OpenLoginLimit": false,
|
||||
// 连续登录失败多少次后触发锁定
|
||||
"LoginMaxFailCount": 5,
|
||||
//触发锁定后账号锁定时长(分钟)
|
||||
"LoginFailLockMinutes": 1,
|
||||
//无操作自动登出的超时时间(分钟)
|
||||
"AutoLoginOutMinutes": 10,
|
||||
// 是否启用多因子登录认证(MFA)
|
||||
"OpenLoginMFA": false,
|
||||
// 连续阅片的最长工作时间(分钟),超时后强制休息
|
||||
"ContinuousReadingTimeMin": 120,
|
||||
//强制休息时长(分钟)
|
||||
"ReadingRestTimeMin": 10,
|
||||
// 是否强制用户定期修改密码
|
||||
"IsNeedChangePassWord": true,
|
||||
// 密码有效期(天),到期后必须修改
|
||||
"ChangePassWordDays": 90,
|
||||
//模板类型1Elevate(LiLi)2Extensive(展影)
|
||||
"TemplateType": 2,
|
||||
// 转换PDF服务配置
|
||||
"ThirdPdfUrl": "http://106.14.89.110:30088/api/v1/convert/file/pdf",
|
||||
//MFA免验证发送天数
|
||||
"UserMFAVerifyMinutes": 1440
|
||||
},
|
||||
// 邮件服务配置(用于系统通知、找回密码、错误报警等'
|
||||
"SystemEmailSendConfig": {
|
||||
//企业邮箱sMTP服务器地址
|
||||
"Host": "smtp.qiye.aliyun.com",
|
||||
//SMTP端口
|
||||
"Port": 465,
|
||||
"Imap": "imap.qiye.aliyun.com",
|
||||
"ImapPort": 993,
|
||||
// 发件人邮箱地址
|
||||
"FromEmail": "test@extimaging.com",
|
||||
// 发件人显示名称
|
||||
"FromName": "Test IRC Imaging System",
|
||||
//SMTP授权码
|
||||
"AuthorizationCode": "SHzyyl2021",
|
||||
//系统对外访问地址
|
||||
"SiteUrl": "http://irc.test.extimaging.com/login",
|
||||
//系统简称-致性核查使用
|
||||
"SystemShortName": "IRC",
|
||||
// 组织英文名称-添加用户默认值
|
||||
"OrganizationName": "ExtImaging",
|
||||
// 组织中文名称
|
||||
"OrganizationNameCN": "ExtImaging",
|
||||
"PlatformName": "EICS",
|
||||
"PlatformNameCN": "展影云平台",
|
||||
//公司英文全称
|
||||
"CompanyName": "Extensive Imaging",
|
||||
// 公司中文全称,
|
||||
"CompanyNameCN": "上海展影医疗科技有限公司",
|
||||
//公司英文简称
|
||||
"CompanyShortName": "Extensive Imaging",
|
||||
//公司中文简称
|
||||
"CompanyShortNameCN": "展影医疗",
|
||||
// 是否为国际版环境 方便前端区分,用于区分1i1li还是irc 做一些事情"
|
||||
"IsEnv_US": false,
|
||||
// 邮箱格式校验正则表达式
|
||||
"EmailRegexStr": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
|
||||
//后端周期性邮件默认语言
|
||||
// "CronEmailDefaultCulture": "zh-CN"
|
||||
}
|
||||
},
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getInfo()
|
||||
},
|
||||
methods: {
|
||||
handleSave() {
|
||||
this.loading = true
|
||||
Promise.all([this.updateSystemBasicConfig(), this.updateSystemEmailConfig()]).then(res => {
|
||||
this.loading = false
|
||||
this.getInfo()
|
||||
})
|
||||
},
|
||||
getInfo() {
|
||||
this.loading = true
|
||||
Promise.all([this.getSystemBasicConfigInfo(), this.getEmailConfigInfo()]).then(res => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
async updateSystemBasicConfig() {
|
||||
try {
|
||||
let validate = await this.$refs.systemConfigForm.validate()
|
||||
if (!validate) return false
|
||||
let data = {}
|
||||
Object.keys(this.form.BasicSystemConfig).forEach(key => {
|
||||
data[key] = this.form.BasicSystemConfig[key]
|
||||
})
|
||||
let res = await updateSystemBasicConfig(data)
|
||||
if (res.IsSuccess) {
|
||||
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
},
|
||||
async updateSystemEmailConfig() {
|
||||
try {
|
||||
let validate = await this.$refs.systemConfigForm.validate()
|
||||
if (!validate) return false
|
||||
let data = {}
|
||||
Object.keys(this.form.SystemEmailSendConfig).forEach(key => {
|
||||
data[key] = this.form.SystemEmailSendConfig[key]
|
||||
})
|
||||
let res = await updateSystemEmailConfig(data)
|
||||
if (res.IsSuccess) {
|
||||
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
},
|
||||
async getSystemBasicConfigInfo() {
|
||||
try {
|
||||
let res = await getSystemBasicConfigInfo()
|
||||
if (res.IsSuccess) {
|
||||
Object.keys(this.form.BasicSystemConfig).forEach(key => {
|
||||
this.form.BasicSystemConfig[key] = res.Result[key]
|
||||
})
|
||||
}
|
||||
} catch (err) {
|
||||
this.loading = false
|
||||
console.log(err)
|
||||
}
|
||||
},
|
||||
async getEmailConfigInfo() {
|
||||
try {
|
||||
let res = await getEmailConfigInfo()
|
||||
if (res.IsSuccess) {
|
||||
Object.keys(this.form.SystemEmailSendConfig).forEach(key => {
|
||||
this.form.SystemEmailSendConfig[key] = res.Result[key]
|
||||
})
|
||||
}
|
||||
} catch (err) {
|
||||
this.loading = false
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.systemConfig {
|
||||
overflow-y: auto;
|
||||
|
||||
h3 {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,42 +1,21 @@
|
|||
<template>
|
||||
<el-form
|
||||
ref="userForm"
|
||||
size="small"
|
||||
:model="user"
|
||||
:rules="userFormRules"
|
||||
label-width="150px"
|
||||
style="width: 800px"
|
||||
>
|
||||
<el-form ref="userForm" size="small" :model="user" :rules="userFormRules" label-width="150px" style="width: 800px">
|
||||
<el-card class="Basic" shadow="never" size="small">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>{{ $t('system:userlist:title:Information') }}</span>
|
||||
</div>
|
||||
<el-form-item
|
||||
v-if="user.UserCode"
|
||||
:label="$t('system:userlist:table:S/N')"
|
||||
prop="UserCode"
|
||||
>
|
||||
<el-form-item v-if="user.UserCode" :label="$t('system:userlist:table:S/N')" prop="UserCode">
|
||||
<el-input v-model="user.UserCode" disabled />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
:label="$t('system:userlist:table:UserName')"
|
||||
class="my_new_pwd"
|
||||
prop="UserName"
|
||||
>
|
||||
<el-form-item :label="$t('system:userlist:table:UserName')" class="my_new_pwd" prop="UserName">
|
||||
<el-input v-model="user.UserName" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
:label="$t('system:userlist:table:LastName')"
|
||||
prop="LastName"
|
||||
>
|
||||
<el-form-item :label="$t('system:userlist:table:LastName')" prop="LastName">
|
||||
<el-input v-model="user.LastName" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:label="$t('system:userlist:table:FirstName')"
|
||||
prop="FirstName"
|
||||
>
|
||||
<el-form-item :label="$t('system:userlist:table:FirstName')" prop="FirstName">
|
||||
<el-input v-model="user.FirstName" />
|
||||
</el-form-item>
|
||||
<!-- <el-form-item :label="$t('system:userlist:table:Gender')" prop="Sex" style="margin-right:40px;">
|
||||
|
|
@ -51,105 +30,52 @@
|
|||
<el-form-item :label="$t('system:userlist:table:Phone')" prop="Phone">
|
||||
<el-input v-model="user.Phone" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="type == 1"
|
||||
:label="$t('system:userlist:table:Disable')"
|
||||
>
|
||||
<el-switch
|
||||
v-model="user.Status"
|
||||
:active-value="1"
|
||||
:inactive-value="0"
|
||||
/>
|
||||
<el-form-item v-if="type == 1" :label="$t('system:userlist:table:Disable')">
|
||||
<el-switch v-model="user.Status" :active-value="1" :inactive-value="0" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:userlist:table:IsTestUser')">
|
||||
<el-radio-group v-model="user.IsTestUser">
|
||||
<el-radio
|
||||
v-for="item of $d.YesOrNo"
|
||||
:key="item.id"
|
||||
:label="item.value"
|
||||
>{{ item.label }}</el-radio
|
||||
>
|
||||
<el-radio v-for="item of $d.YesOrNo" :key="item.id" :label="item.value">{{ item.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:label="$t('system:userlist:table:UserType')"
|
||||
prop="Roles"
|
||||
v-if="type === 0"
|
||||
>
|
||||
<el-form-item :label="$t('system:userlist:table:UserWorkLanguage')" prop="UserWorkLanguage">
|
||||
<el-select v-model="user.UserWorkLanguage" style="width: 100%">
|
||||
<el-option v-for="item of $d.UserWorkLanguage" :key="item.id" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('system:userlist:table:UserType')" prop="Roles" v-if="type === 0">
|
||||
<div style="display: flex; align-items: center">
|
||||
<el-select
|
||||
ref="userType"
|
||||
v-model="user.Roles"
|
||||
size="small"
|
||||
placeholder="Please select"
|
||||
multiple
|
||||
style="width: 100%"
|
||||
:disabled="user.CanEditUserType === false || type === 1"
|
||||
@change="handleChange"
|
||||
>
|
||||
<el-select ref="userType" v-model="user.Roles" size="small" placeholder="Please select" multiple
|
||||
style="width: 100%" :disabled="user.CanEditUserType === false || type === 1" @change="handleChange">
|
||||
<template v-for="userType of userTypeOptions">
|
||||
<el-option
|
||||
v-if="![4, 6, 20].includes(userType.UserTypeEnum)"
|
||||
:key="userType.Id"
|
||||
:label="userType.UserType"
|
||||
:value="userType.Id"
|
||||
/>
|
||||
<el-option v-if="![4, 6, 20].includes(userType.UserTypeEnum)" :key="userType.Id"
|
||||
:label="userType.UserType" :value="userType.Id" />
|
||||
</template>
|
||||
</el-select>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:label="$t('system:userlist:table:UserType')"
|
||||
prop="userTypeId"
|
||||
v-else
|
||||
>
|
||||
<el-form-item :label="$t('system:userlist:table:UserType')" prop="userTypeId" v-else>
|
||||
<div style="display: flex; align-items: center">
|
||||
<el-select
|
||||
ref="userType"
|
||||
v-model="Roles"
|
||||
size="small"
|
||||
placeholder="Please select"
|
||||
multiple
|
||||
style="width: 100%"
|
||||
:disabled="user.CanEditUserType === false || type === 1"
|
||||
@change="handleChange"
|
||||
>
|
||||
<el-select ref="userType" v-model="Roles" size="small" placeholder="Please select" multiple
|
||||
style="width: 100%" :disabled="user.CanEditUserType === false || type === 1" @change="handleChange">
|
||||
<template v-for="userType of userTypeOptions">
|
||||
<el-option
|
||||
v-if="![4, 6, 20].includes(userType.UserTypeEnum)"
|
||||
:key="userType.Id"
|
||||
:label="userType.UserType"
|
||||
:value="userType.Id"
|
||||
/>
|
||||
<el-option v-if="![4, 6, 20].includes(userType.UserTypeEnum)" :key="userType.Id"
|
||||
:label="userType.UserType" :value="userType.Id" />
|
||||
</template>
|
||||
</el-select>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
style="margin-left: 5px"
|
||||
v-if="type === 1"
|
||||
@click.stop="openRoleList"
|
||||
>
|
||||
<el-button type="primary" size="small" style="margin-left: 5px" v-if="type === 1" @click.stop="openRoleList">
|
||||
{{ $t('system:userlist:button:roles') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-card>
|
||||
|
||||
<el-card
|
||||
class="Affiliation"
|
||||
shadow="never"
|
||||
style="margin-top: 10px"
|
||||
size="small"
|
||||
>
|
||||
<el-card class="Affiliation" shadow="never" style="margin-top: 10px" size="small">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>{{ $t('system:userlist:title:Affiliation') }}</span>
|
||||
</div>
|
||||
<el-form-item prop="IsZhiZhun">
|
||||
<el-radio-group
|
||||
v-model="user.IsZhiZhun"
|
||||
@change="OrgnizationTypeChanged"
|
||||
>
|
||||
<el-radio-group v-model="user.IsZhiZhun" @change="OrgnizationTypeChanged">
|
||||
<el-radio :label="true">{{
|
||||
$t('system:userlist:title:Internal')
|
||||
}}</el-radio>
|
||||
|
|
@ -158,43 +84,22 @@
|
|||
}}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-show="user.IsZhiZhun === false"
|
||||
:label="$t('system:userlist:table:OrganizationName')"
|
||||
>
|
||||
<el-form-item v-show="user.IsZhiZhun === false" :label="$t('system:userlist:table:OrganizationName')">
|
||||
<el-input v-model="user.OrganizationName" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:label="$t('system:userlist:table:Department')"
|
||||
prop="DepartmentName"
|
||||
>
|
||||
<el-form-item :label="$t('system:userlist:table:Department')" prop="DepartmentName">
|
||||
<el-input v-model="user.DepartmentName" />
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:label="$t('system:userlist:table:Position')"
|
||||
prop="PositionName"
|
||||
>
|
||||
<el-form-item :label="$t('system:userlist:table:Position')" prop="PositionName">
|
||||
<el-input v-model="user.PositionName" />
|
||||
</el-form-item>
|
||||
</el-card>
|
||||
<el-form-item>
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
:disabled="isDisabled"
|
||||
style="margin: 10px 15px"
|
||||
@click="handleSave"
|
||||
>{{ $t('common:button:save') }}</el-button
|
||||
>
|
||||
<el-button type="primary" size="small" :disabled="isDisabled" style="margin: 10px 15px" @click="handleSave">{{
|
||||
$t('common:button:save') }}</el-button>
|
||||
</el-form-item>
|
||||
<roleList
|
||||
v-if="visible"
|
||||
:visible.sync="visible"
|
||||
:userId="userId"
|
||||
:list="userRoleList"
|
||||
:userTypeOptions="userTypeOptions"
|
||||
@getRoleList="getRoleList"
|
||||
/>
|
||||
<roleList v-if="visible" :visible.sync="visible" :userId="userId" :list="userRoleList"
|
||||
:userTypeOptions="userTypeOptions" @getRoleList="getRoleList" />
|
||||
</el-form>
|
||||
</template>
|
||||
<script>
|
||||
|
|
@ -365,7 +270,7 @@ export default {
|
|||
callback(
|
||||
lang === 'zh'
|
||||
? new Error(
|
||||
'1)新建账号,用户名字符长度最小为4个字符,最大为16个字符,只可使用字母、数字、下划线;'
|
||||
'新建账号,用户名字符长度最小为4个字符,最大为16个字符,只可使用字母、数字、下划线;'
|
||||
)
|
||||
: new Error(
|
||||
'For a new account, the username must have:1) At least 4 characters;2) At most 16 characters;3)Only letters, numbers, and underscores are allowed.'
|
||||
|
|
@ -379,6 +284,7 @@ export default {
|
|||
userRoleList: [],
|
||||
Roles: [],
|
||||
user: {
|
||||
UserWorkLanguage: null,
|
||||
Roles: [],
|
||||
UserRoleList: [],
|
||||
UserName: '',
|
||||
|
|
@ -399,6 +305,13 @@ export default {
|
|||
UserName: [
|
||||
{ required: true, validator: validatePassword, trigger: 'blur' },
|
||||
],
|
||||
UserWorkLanguage: [
|
||||
{
|
||||
required: true,
|
||||
message: 'Please Select',
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
],
|
||||
Roles: [
|
||||
{
|
||||
required: true,
|
||||
|
|
|
|||
|
|
@ -1,55 +1,30 @@
|
|||
<template>
|
||||
<box-content>
|
||||
<div class="search" style="position: relative">
|
||||
<SearchForm
|
||||
:that="this"
|
||||
:search-data="searchData"
|
||||
:search-form="searchForm"
|
||||
:search-handle="searchHandle"
|
||||
@search="handleSearch"
|
||||
@reset="handleReset"
|
||||
@new="handleAddUser"
|
||||
/>
|
||||
<SearchForm :that="this" :search-data="searchData" :search-form="searchForm" :search-handle="searchHandle"
|
||||
@search="handleSearch" @reset="handleReset" @new="handleAddUser" />
|
||||
</div>
|
||||
<base-table
|
||||
v-loading="loading"
|
||||
:columns="columns"
|
||||
:list="users"
|
||||
:search-data="searchData"
|
||||
:total="total"
|
||||
@getList="getList"
|
||||
@editCb="handleEditUser"
|
||||
@sendCb="addNewUserSendEmail"
|
||||
@deleteCb="handleDeleteUser"
|
||||
@sortByColumn="sortByColumn"
|
||||
>
|
||||
<base-table v-loading="loading" :columns="columns" :list="users" :search-data="searchData" :total="total"
|
||||
@getList="getList" @editCb="handleEditUser" @sendCb="addNewUserSendEmail" @deleteCb="handleDeleteUser"
|
||||
@sortByColumn="sortByColumn">
|
||||
<!-- 选择自定义slot -->
|
||||
<template slot="tip-slot" slot-scope="{ scope }">
|
||||
<i
|
||||
v-if="
|
||||
<i v-if="
|
||||
diffTime(scope.row.LastLoginTime) >= 90 &&
|
||||
diffTime(scope.row.LastChangePassWordTime) >= 90
|
||||
"
|
||||
class="el-icon-warning"
|
||||
style="color: #f56c6c"
|
||||
:title="$t('system:userlist:tip:overTimeAndoverTimePassWord')"
|
||||
/>
|
||||
<i
|
||||
v-else-if="diffTime(scope.row.LastLoginTime) >= 90"
|
||||
class="el-icon-warning"
|
||||
style="color: #f56c6c"
|
||||
:title="$t('system:userlist:tip:overTime')"
|
||||
/>
|
||||
<i
|
||||
v-else-if="diffTime(scope.row.LastChangePassWordTime) >= 90"
|
||||
class="el-icon-warning"
|
||||
style="color: #f56c6c"
|
||||
:title="$t('system:userlist:tip:overTimePassWord')"
|
||||
/>
|
||||
" class="el-icon-warning" style="color: #f56c6c"
|
||||
:title="$t('system:userlist:tip:overTimeAndoverTimePassWord')" />
|
||||
<i v-else-if="diffTime(scope.row.LastLoginTime) >= 90" class="el-icon-warning" style="color: #f56c6c"
|
||||
:title="$t('system:userlist:tip:overTime')" />
|
||||
<i v-else-if="diffTime(scope.row.LastChangePassWordTime) >= 90" class="el-icon-warning" style="color: #f56c6c"
|
||||
:title="$t('system:userlist:tip:overTimePassWord')" />
|
||||
</template>
|
||||
<template slot="genderSlot" slot-scope="{ scope }">
|
||||
{{ scope.row.Sex ? 'Male' : 'Female' }}
|
||||
</template>
|
||||
<template slot="UserWorkLanguageSlot" slot-scope="{ scope }">
|
||||
{{ $fd('UserWorkLanguage', scope.row.UserWorkLanguage) }}
|
||||
</template>
|
||||
<template slot="UserTypeSlot" slot-scope="{ scope }">
|
||||
{{
|
||||
Array.isArray(scope.row.UserRoleList) &&
|
||||
|
|
@ -155,6 +130,14 @@ export default {
|
|||
sortable: 'custom',
|
||||
showOverflowTooltip: true,
|
||||
},
|
||||
{
|
||||
// prop: 'UserWorkLanguage',
|
||||
label: this.$t('system:userlist:table:UserWorkLanguage'),
|
||||
minWidth: 120,
|
||||
slot: 'UserWorkLanguageSlot',
|
||||
sortable: 'custom',
|
||||
showOverflowTooltip: true,
|
||||
},
|
||||
// {
|
||||
// prop: 'Sex',
|
||||
// label: this.$t('system:userlist:table:Gender'),
|
||||
|
|
@ -287,6 +270,15 @@ export default {
|
|||
width: '120px',
|
||||
placeholder: '',
|
||||
},
|
||||
{
|
||||
type: 'Select',
|
||||
label: this.$t('system:userlist:label:UserWorkLanguage'),
|
||||
prop: 'UserWorkLanguage',
|
||||
width: '100px',
|
||||
options: [], // 下拉选项
|
||||
props: { label: 'label', value: 'value' }, // 下拉选项配置信息,必填
|
||||
placeholder: '',
|
||||
},
|
||||
{
|
||||
type: 'Input',
|
||||
label: this.$t('system:userlist:label:Phone'),
|
||||
|
|
@ -476,8 +468,10 @@ export default {
|
|||
// 获取用户类型下拉选项信息
|
||||
async getInfo() {
|
||||
const res = await getUserTypeList()
|
||||
const index = this.findItemIndex('UserType')
|
||||
let index = this.findItemIndex('UserType')
|
||||
this.$set(this.searchForm[index], 'options', res.Result)
|
||||
index = this.findItemIndex('UserWorkLanguage')
|
||||
this.$set(this.searchForm[index], 'options', this.$d.UserWorkLanguage)
|
||||
},
|
||||
handleAddUser() {
|
||||
this.$router.push({ path: '/system/user/add' })
|
||||
|
|
|
|||
|
|
@ -1,25 +1,145 @@
|
|||
<template>
|
||||
<div>
|
||||
<!-- <label>行数: </label>
|
||||
<input v-model.number="rows" type="number" min="1" max="3" />
|
||||
<label>列数: </label>
|
||||
<input v-model.number="cols" type="number" min="1" max="3" /> -->
|
||||
<GridLayout :rows="rows" :cols="cols" />
|
||||
<el-input v-model="input" placeholder="" clearable></el-input>
|
||||
<el-input v-model="pageIndex" placeholder="" clearable></el-input>
|
||||
<el-input v-model="pageSize" placeholder="" clearable></el-input>
|
||||
<el-button type="primary" size="small" :disabled="!input" @click.stop="getTrialUnreadVisitList">下载</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import GridLayout from './GridLayout.vue';
|
||||
|
||||
import { downLoadFile } from '@/utils/stream.js'
|
||||
import { getTrialUnreadVisitList, getExportSubjectVisitImageList } from '@/api/trials'
|
||||
export default {
|
||||
components: {
|
||||
GridLayout,
|
||||
},
|
||||
// components: {
|
||||
// GridLayout,
|
||||
// },
|
||||
data() {
|
||||
return {
|
||||
rows: 2,
|
||||
cols: 2,
|
||||
input: '08deab4a-f842-a7a4-0242-0a0001000000',
|
||||
pageIndex: 1,
|
||||
pageSize: 250
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async getTrialUnreadVisitList() {
|
||||
let params = {
|
||||
TrialId: this.input,
|
||||
PageIndex: this.pageIndex,
|
||||
PageSize: this.pageSize,
|
||||
}
|
||||
let res = await getTrialUnreadVisitList(params)
|
||||
if (res.IsSuccess) {
|
||||
let arr = res.Result
|
||||
this.LoopDownload(arr, 0)
|
||||
}
|
||||
},
|
||||
async LoopDownload(arr, index) {
|
||||
if (index >= arr.length) return false
|
||||
let res = await this.handleExportImage(false, true, arr[index])
|
||||
console.log(res, arr[index], 'res')
|
||||
index++
|
||||
this.LoopDownload(arr, index)
|
||||
},
|
||||
async handleExportImage(IsKeyImage = false, IsExportReading = false, SubjectVisitId) {
|
||||
try {
|
||||
let data = {
|
||||
TrialId: this.input,
|
||||
IsKeyImage,
|
||||
IsExportReading
|
||||
}
|
||||
data.SubjectVisitIdList = [SubjectVisitId]
|
||||
// if (!IsKeyImage) {
|
||||
// let confirm = await this.$confirm(this.$t('trials:imageSummary:confirm:space').replace('xxx', this.image_size.CheckImageSize))
|
||||
// if (!confirm) return false
|
||||
// }
|
||||
let res = await getExportSubjectVisitImageList(data)
|
||||
if (res.IsSuccess) {
|
||||
return this.downLoad(IsKeyImage, res.Result, IsExportReading)
|
||||
}
|
||||
return false
|
||||
} catch (err) {
|
||||
return false
|
||||
console.log(err)
|
||||
}
|
||||
},
|
||||
// 下载
|
||||
async downLoad(IsKeyImage = false, row, IsExportReading = false) {
|
||||
try {
|
||||
let { files, name } = this.formatDownloadFile(IsKeyImage, row, IsExportReading)
|
||||
return await downLoadFile(files, name, 'zip')
|
||||
// }
|
||||
} catch (err) {
|
||||
return false
|
||||
console.log(err)
|
||||
}
|
||||
},
|
||||
// 格式化下载文件路径
|
||||
formatDownloadFile(IsKeyImage = false, row, IsExportReading = false) {
|
||||
let files = [],
|
||||
name = `${this.$route.query.researchProgramNo}_${this.$t('trials:imageSummary:downloadname:dicom')}_${Date.now()}.zip`;
|
||||
if (IsExportReading) {
|
||||
name = `${row.VisitList[0].SubjectCode}_${row.VisitList[0].VisitName}.zip`;
|
||||
}
|
||||
if (!IsKeyImage) {
|
||||
//中心ID/受试者ID/访视名/Study ID_Study Date_Modality/文件
|
||||
row.VisitList.forEach(visit => {
|
||||
if (visit.StudyList && visit.StudyList.length > 0) {
|
||||
visit.StudyList.forEach(study => {
|
||||
let arr = []
|
||||
study.SeriesList.forEach(item => {
|
||||
if (!arr.includes(item.Modality)) {
|
||||
arr.push(item.Modality)
|
||||
}
|
||||
})
|
||||
let str = arr.join('_')
|
||||
let time = study.StudyTime.split(' ')[0]
|
||||
if (study.StudyDIRPath && !study.dirHas) {
|
||||
study.dirHas = true
|
||||
let obj = {
|
||||
name: `${study.StudyCode}_${time}_${str}/DICOMDIR`,
|
||||
url: this.OSSclientConfig.basePath + study.StudyDIRPath,
|
||||
}
|
||||
files.push(obj)
|
||||
}
|
||||
if (study.SeriesList && study.SeriesList.length > 0) {
|
||||
study.SeriesList.forEach(serie => {
|
||||
|
||||
if (serie.InstanceList && serie.InstanceList.length > 0) {
|
||||
serie.InstanceList.forEach(instance => {
|
||||
let instanceArr = instance.Path.split("/")
|
||||
let fileName = instance.FileName || instanceArr[instanceArr.length - 1]
|
||||
let obj = {
|
||||
name: `${study.StudyCode}_${time}_${str}/IMAGE/${fileName}`,
|
||||
url: this.OSSclientConfig.basePath + instance.Path,
|
||||
IsEncapsulated: instance.IsEncapsulated
|
||||
}
|
||||
files.push(obj)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
if (visit.NoneDicomStudyList && visit.NoneDicomStudyList.length > 0) {
|
||||
visit.NoneDicomStudyList.forEach(noneDicomStudy => {
|
||||
if (noneDicomStudy.FileList && noneDicomStudy.FileList.length > 0) {
|
||||
noneDicomStudy.FileList.forEach(file => {
|
||||
let time = noneDicomStudy.ImageDate.split(' ')[0]
|
||||
let obj = {
|
||||
name: `${noneDicomStudy.StudyCode}_${time}_${noneDicomStudy.Modality}/${file.FileName}`,
|
||||
url: this.OSSclientConfig.basePath + file.Path,
|
||||
}
|
||||
files.push(obj)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return { files, name }
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -17,11 +17,17 @@
|
|||
</el-form-item>
|
||||
|
||||
<!-- Sponsor -->
|
||||
<el-form-item :label="$t('trials:trials-list:table:sponsor')">
|
||||
<el-select v-model="searchData.SponsorId" style="width: 150px" clearable>
|
||||
<el-form-item :label="$t('trials:trials-list:table:sponsor')" v-if="IsZhiZhun">
|
||||
<el-select v-model="searchData.SponsorId" style="width: 150px" clearable filterable>
|
||||
<el-option v-for="item in sponsorList" :key="item.Id" :label="item.SponsorName" :value="item.Id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 状态 -->
|
||||
<el-form-item :label="$t('trials:trials-list:table:TrialStatusStr')" v-if="IsZhiZhun">
|
||||
<el-select v-model="searchData.TrialStatusStr" style="width: 150px" clearable>
|
||||
<el-option v-for="item in $d.TrialStatusEnum" :key="item.id" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- 阅片标准 -->
|
||||
<el-form-item v-if="hasPermi(['role:ir'])" :label="$t('trials:trials-list:table:IR_ReadingCriterionList')">
|
||||
<el-select v-model="searchData.CriterionType" style="width: 150px" clearable>
|
||||
|
|
@ -347,6 +353,7 @@ import {
|
|||
import { getTrialList_Export } from '@/api/export'
|
||||
import store from '@/store'
|
||||
import { mapGetters } from 'vuex'
|
||||
import { getUser } from '@/api/admin'
|
||||
import BaseContainer from '@/components/BaseContainer'
|
||||
import Pagination from '@/components/Pagination'
|
||||
import TrialForm from './components/TrialForm'
|
||||
|
|
@ -372,6 +379,7 @@ const searchDataDefault = () => {
|
|||
PageSize: 20,
|
||||
Asc: false,
|
||||
SortField: '',
|
||||
TrialStatusStr: null,
|
||||
CriterionType: null,
|
||||
PM_EMail: null,
|
||||
}
|
||||
|
|
@ -410,6 +418,7 @@ export default {
|
|||
{ value: 'III' },
|
||||
{ value: 'IV' },
|
||||
],
|
||||
IsZhiZhun: false,
|
||||
expeditedOption: this.$d.TrialExpeditedState,
|
||||
beginPickerOption: {
|
||||
disabledDate: (time) => {
|
||||
|
|
@ -439,6 +448,7 @@ export default {
|
|||
},
|
||||
created() {
|
||||
this.initPage()
|
||||
this.getUser()
|
||||
},
|
||||
mounted() {
|
||||
this.$EventBus.$on('reload', (data) => {
|
||||
|
|
@ -446,6 +456,11 @@ export default {
|
|||
})
|
||||
},
|
||||
methods: {
|
||||
getUser() {
|
||||
getUser().then(res => {
|
||||
this.IsZhiZhun = res.Result.IsZhiZhun
|
||||
})
|
||||
},
|
||||
initPage() {
|
||||
this.getList()
|
||||
store.dispatch('global/getSponsorList')
|
||||
|
|
|
|||
|
|
@ -5,77 +5,42 @@
|
|||
<!-- 用户基本信息 -->
|
||||
{{ $t("trials:trials-myinfo:title:basicInfo") }}
|
||||
</div>
|
||||
<el-form
|
||||
ref="userForm"
|
||||
label-position="right"
|
||||
:model="user"
|
||||
:rules="userFormRules"
|
||||
label-width="120px"
|
||||
>
|
||||
<el-form ref="userForm" label-position="right" :model="user" :rules="userFormRules" label-width="120px">
|
||||
<el-form-item v-if="user.Code" label="ID: " prop="Code">
|
||||
<el-input v-model="user.Code" disabled />
|
||||
</el-form-item>
|
||||
<!-- 姓 -->
|
||||
<el-form-item
|
||||
:disabled="user.UserTypeEnum === 8"
|
||||
:label="$t('trials:trials-myinfo:form:surname')"
|
||||
prop="LastName"
|
||||
>
|
||||
<el-input
|
||||
v-model="user.LastName"
|
||||
:placeholder="$t('trials:trials-myinfo:form:surname')"
|
||||
/>
|
||||
<el-form-item :disabled="user.UserTypeEnum === 8" :label="$t('trials:trials-myinfo:form:surname')"
|
||||
prop="LastName">
|
||||
<el-input v-model="user.LastName" :placeholder="$t('trials:trials-myinfo:form:surname')" />
|
||||
</el-form-item>
|
||||
<!-- 名 -->
|
||||
<el-form-item
|
||||
:disabled="user.UserTypeEnum === 8"
|
||||
:label="$t('trials:trials-myinfo:form:givenname')"
|
||||
prop="FirstName"
|
||||
>
|
||||
<el-input
|
||||
v-model="user.FirstName"
|
||||
:placeholder="$t('trials:trials-myinfo:form:givenname')"
|
||||
/>
|
||||
<el-form-item :disabled="user.UserTypeEnum === 8" :label="$t('trials:trials-myinfo:form:givenname')"
|
||||
prop="FirstName">
|
||||
<el-input v-model="user.FirstName" :placeholder="$t('trials:trials-myinfo:form:givenname')" />
|
||||
</el-form-item>
|
||||
<!-- 单位 -->
|
||||
<el-form-item
|
||||
:label="$t('trials:trials-myinfo:form:organization')"
|
||||
prop="OrganizationName"
|
||||
>
|
||||
<el-input
|
||||
:disabled="user.IsZhiZhun"
|
||||
v-model="user.OrganizationName"
|
||||
:placeholder="$t('trials:trials-myinfo:form:organization')"
|
||||
/>
|
||||
<el-form-item :label="$t('trials:trials-myinfo:form:organization')" prop="OrganizationName">
|
||||
<el-input :disabled="user.IsZhiZhun" v-model="user.OrganizationName"
|
||||
:placeholder="$t('trials:trials-myinfo:form:organization')" />
|
||||
</el-form-item>
|
||||
<!-- 部门 -->
|
||||
<el-form-item
|
||||
:label="$t('trials:trials-myinfo:form:department')"
|
||||
prop="DepartmentName"
|
||||
>
|
||||
<el-input
|
||||
v-model="user.DepartmentName"
|
||||
:placeholder="$t('trials:trials-myinfo:form:organization')"
|
||||
/>
|
||||
<el-form-item :label="$t('trials:trials-myinfo:form:department')" prop="DepartmentName">
|
||||
<el-input v-model="user.DepartmentName" :placeholder="$t('trials:trials-myinfo:form:organization')" />
|
||||
</el-form-item>
|
||||
<!-- 职位 -->
|
||||
<el-form-item
|
||||
:label="$t('trials:trials-myinfo:form:position')"
|
||||
prop="PositionName"
|
||||
>
|
||||
<el-input
|
||||
v-model="user.PositionName"
|
||||
:placeholder="$t('trials:trials-myinfo:form:position')"
|
||||
/>
|
||||
<el-form-item :label="$t('trials:trials-myinfo:form:position')" prop="PositionName">
|
||||
<el-input v-model="user.PositionName" :placeholder="$t('trials:trials-myinfo:form:position')" />
|
||||
</el-form-item>
|
||||
<!-- 工作语言 -->
|
||||
<el-form-item :label="$t('trials:trials-myinfo:form:UserWorkLanguage')" prop="UserWorkLanguage">
|
||||
<el-select v-model="user.UserWorkLanguage" style="width: 100%">
|
||||
<el-option v-for="item of $d.UserWorkLanguage" :key="item.id" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<!-- 保存 -->
|
||||
<el-button
|
||||
class="trial-info-btn"
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="handleSave"
|
||||
>
|
||||
<el-button class="trial-info-btn" type="primary" size="small" @click="handleSave">
|
||||
{{ $t("trials:trials-myinfo:button:save") }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
|
@ -102,6 +67,13 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
userFormRules: {
|
||||
UserWorkLanguage: [
|
||||
{
|
||||
required: true,
|
||||
message: this.$t("common:ruleMessage:select"),
|
||||
trigger: ["blur", "change"],
|
||||
},
|
||||
],
|
||||
UserName: [
|
||||
{
|
||||
required: true,
|
||||
|
|
|
|||
|
|
@ -27,23 +27,110 @@
|
|||
<span v-html="$t('trials:researchForm:form:isFollowStudyParam')"></span>
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="form.IsRoutineMRIPDEE">
|
||||
<span v-html="$t('trials:researchForm:form:IsRoutineMRIPDEE')"></span>
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="form.IsMRIPDFF" @change="(val) => handleChange(val, 'IsMRIPDFF')">
|
||||
<span v-html="$t('trials:researchForm:form:IsMRIPDFF')"></span>
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="form.IsAuthorize" @change="(val) => handleChange(val, 'IsAuthorize')">
|
||||
<span v-html="$t('trials:researchForm:form:IsAuthorize')"></span>
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="form.ISStrictManualBurnFlag"
|
||||
@change="(val) => handleChange(val, 'ISStrictManualBurnFlag')">
|
||||
<span v-html="$t('trials:researchForm:form:ISStrictManualBurnFlag')"></span>
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div style="margin-bottom: 20px;font-weight: bold;">
|
||||
{{ $t("trials:researchRecord:ImageManual:TableConfiguration") }}
|
||||
</div>
|
||||
<el-form size="small" :model="form" style="width:80%">
|
||||
<el-form-item :label="$t('trials:researchRecord:ImageManual:Equipment')">
|
||||
<el-radio-group v-model="form.IsCloseEquipmentSurvey">
|
||||
<el-radio-group v-model="form.IsCloseEquipmentSurvey" @input="handleInput">
|
||||
<el-radio :label="item.value" v-for="item in $d.YesOrNo" :key="item.id">{{ item.label
|
||||
}}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div style="margin-bottom: 20px;font-weight: bold;">
|
||||
{{ $t("trials:researchRecord:ImageManual:EquipmentConfig") }}
|
||||
</div>
|
||||
<el-form size="small" :model="EquipmentForm" style="width:80%" v-if="form.IsCloseEquipmentSurvey">
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="EquipmentForm.EquipmentTypeEnum">
|
||||
{{ $t('trials:researchForm:form:EquipmentTypeEnum') }}
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="EquipmentForm.Parameters">
|
||||
{{ $t('trials:researchForm:form:Parameters') }}
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="EquipmentForm.ManufacturerType">
|
||||
{{ $t('trials:researchForm:form:ManufacturerType') }}
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="EquipmentForm.ScannerType">
|
||||
{{ $t('trials:researchForm:form:ScannerType') }}
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="EquipmentForm.MagneticFieldStrengthType">
|
||||
{{ $t('trials:researchForm:form:MagneticFieldStrengthType') }}
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="EquipmentForm.BodyCoilChannelCount">
|
||||
{{ $t('trials:researchForm:form:BodyCoilChannelCount') }}
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="EquipmentForm.HasDedicatedPdfFatQuantificationSequence"
|
||||
@change="EquipmentForm.PdfFatQuantificationSequenceType = false">
|
||||
{{ $t('trials:researchForm:form:HasDedicatedPdfFatQuantificationSequence') }}
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="EquipmentForm.HasDedicatedPdfFatQuantificationSequence">
|
||||
<el-checkbox v-model="EquipmentForm.PdfFatQuantificationSequenceType">
|
||||
{{ $t('trials:researchForm:form:PdfFatQuantificationSequenceType') }}
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="EquipmentForm.HasT2R2Correction">
|
||||
{{ $t('trials:researchForm:form:HasT2R2Correction') }}
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="EquipmentForm.CanFullyExportPdfParameterMapsAndRawDicom">
|
||||
{{ $t('trials:researchForm:form:CanFullyExportPdfParameterMapsAndRawDicom') }}
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-checkbox v-model="EquipmentForm.Note">
|
||||
{{ $t('trials:researchForm:form:Note') }}
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div style="margin-bottom: 20px;font-weight: bold;">
|
||||
{{ $t("trials:researchRecord:ImageManual:Precautions") }}
|
||||
</div>
|
||||
<el-input type="textarea" :autosize="{ minRows: 4, maxRows: 99 }" placeholder=""
|
||||
v-model="form.ReplaceContent" @input="handleInput" style="width: 70%;" />
|
||||
v-model="form.ReplaceContentCN" style="width: 70%;" />
|
||||
<div style="margin: 20px 0;font-weight: bold;">
|
||||
{{ $t("trials:researchRecord:ImageManual:PrecautionsUS") }}
|
||||
</div>
|
||||
<el-input type="textarea" :autosize="{ minRows: 4, maxRows: 99 }" placeholder=""
|
||||
v-model="form.ReplaceContent" style="width: 70%;" />
|
||||
<div style="text-align: right;margin: 20px;" class="btnBox">
|
||||
<el-button type="primary" :loading="loading" size="small" @click="handleSave">
|
||||
{{ $t('common:button:save') }}
|
||||
|
|
@ -80,7 +167,30 @@ export default {
|
|||
ReplaceContentCN: '',
|
||||
IsOpenLostVistRead: false,
|
||||
IsSupportQCDownloadImage: false,
|
||||
IsCloseEquipmentSurvey: true
|
||||
IsCloseEquipmentSurvey: true,
|
||||
IsRoutineMRIPDEE: false,
|
||||
IsMRIPDFF: false,
|
||||
MRIPDFFScanTime: false,
|
||||
MRIPDFFLeadTime: false,
|
||||
MRIPDFFOther: false,
|
||||
IsAuthorize: false,
|
||||
IsAuthorizeRadiologistsParticipate: false,
|
||||
AssignFixedTechnologists: false,
|
||||
ISStrictManualBurnFlag: false,
|
||||
NotStrictManualBurnFlagReason: false
|
||||
},
|
||||
EquipmentForm: {
|
||||
Parameters: false,
|
||||
Note: false,
|
||||
EquipmentTypeEnum: false,
|
||||
ManufacturerType: false,
|
||||
ScannerType: false,
|
||||
MagneticFieldStrengthType: false,
|
||||
BodyCoilChannelCount: false,
|
||||
HasDedicatedPdfFatQuantificationSequence: false,
|
||||
PdfFatQuantificationSequenceType: false,
|
||||
HasT2R2Correction: false,
|
||||
CanFullyExportPdfParameterMapsAndRawDicom: false,
|
||||
},
|
||||
obj: {
|
||||
AverageEngravingCycle: false,
|
||||
|
|
@ -89,6 +199,14 @@ export default {
|
|||
EfficacyEvaluatorType: false,
|
||||
IsFollowStudyParameters: false,
|
||||
NotFollowReson: false,
|
||||
IsRoutineMRIPDEE: false,
|
||||
MRIPDFFScanTime: false,
|
||||
MRIPDFFLeadTime: false,
|
||||
MRIPDFFOther: false,
|
||||
IsAuthorizeRadiologistsParticipate: false,
|
||||
AssignFixedTechnologists: false,
|
||||
ISStrictManualBurnFlag: false,
|
||||
NotStrictManualBurnFlagReason: false,
|
||||
},
|
||||
loading: false
|
||||
}
|
||||
|
|
@ -100,9 +218,21 @@ export default {
|
|||
handleChange(val, key) {
|
||||
if (key === 'IsConfirmImagingTechnologist') this.form.NotConfirmReson = val
|
||||
if (key === 'IsFollowStudyParameters') this.form.NotFollowReson = val
|
||||
if (key === 'IsMRIPDFF') {
|
||||
this.form.MRIPDFFScanTime = val
|
||||
this.form.MRIPDFFLeadTime = val
|
||||
this.form.MRIPDFFOther = val
|
||||
}
|
||||
if (key === 'IsAuthorize') {
|
||||
this.form.IsAuthorizeRadiologistsParticipate = val
|
||||
this.form.AssignFixedTechnologists = val
|
||||
}
|
||||
if (key === 'ISStrictManualBurnFlag') this.form.NotStrictManualBurnFlagReason = val
|
||||
},
|
||||
handleInput(val) {
|
||||
this.form.ReplaceContentCN = val
|
||||
Object.keys(this.EquipmentForm).forEach(key => {
|
||||
this.EquipmentForm[key] = false
|
||||
})
|
||||
},
|
||||
async getInfo() {
|
||||
try {
|
||||
|
|
@ -115,12 +245,22 @@ export default {
|
|||
this.form[key] = true
|
||||
if (key === 'ReplaceContent' || key === 'ReplaceContentCN') this.form[key] = ''
|
||||
})
|
||||
Object.keys(this.EquipmentForm).forEach(key => {
|
||||
this.EquipmentForm[key] = false
|
||||
})
|
||||
res.Result.EquipmentControlFieldList.forEach(item => {
|
||||
if (this.EquipmentForm.hasOwnProperty(item.FiledName)) {
|
||||
this.EquipmentForm[item.FiledName] = true
|
||||
}
|
||||
})
|
||||
this.form.IsOpenLostVistRead = res.Result.IsOpenLostVistRead
|
||||
this.form.IsSupportQCDownloadImage = res.Result.IsSupportQCDownloadImage
|
||||
this.form.IsCloseEquipmentSurvey = !res.Result.IsCloseEquipmentSurvey
|
||||
if (Array.isArray(res.Result.NotShowFieldList) && res.Result.NotShowFieldList.length > 0) {
|
||||
res.Result.NotShowFieldList.forEach(key => {
|
||||
this.form[key] = false
|
||||
if (['MRIPDFFScanTime', 'MRIPDFFLeadTime', 'MRIPDFFOther'].includes(key)) this.form.IsMRIPDFF = false
|
||||
if (['IsAuthorizeRadiologistsParticipate', 'AssignFixedTechnologists'].includes(key)) this.form.IsAuthorize = false
|
||||
})
|
||||
}
|
||||
if (Array.isArray(res.Result.ModifyFiledList) && res.Result.ModifyFiledList.length > 0) {
|
||||
|
|
@ -128,8 +268,15 @@ export default {
|
|||
this.form.ReplaceContentCN = res.Result.ModifyFiledList[0].ReplaceContentCN.split('<p>').join('').split('</p>').filter(item => isNaN(item)).join("\n")
|
||||
} else {
|
||||
let arr = [this.$t('trials:equiptResearch:form:item1'), this.$t('trials:equiptResearch:form:item2'), this.$t('trials:equiptResearch:form:item3'), this.$t('trials:equiptResearch:form:item4')]
|
||||
let arr2 = [this.$t('trials:equiptResearch:form:item5'), this.$t('trials:equiptResearch:form:item6'), this.$t('trials:equiptResearch:form:item7'), this.$t('trials:equiptResearch:form:item8')]
|
||||
if (this.$i18n.locale !== 'zh') {
|
||||
this.form.ReplaceContentCN = arr2.join("\n")
|
||||
this.form.ReplaceContent = arr.join("\n")
|
||||
} else {
|
||||
this.form.ReplaceContentCN = arr.join("\n")
|
||||
this.form.ReplaceContent = arr2.join("\n")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
|
|
@ -168,7 +315,8 @@ export default {
|
|||
IsSupportQCDownloadImage: this.form.IsSupportQCDownloadImage,
|
||||
IsCloseEquipmentSurvey: !this.form.IsCloseEquipmentSurvey,
|
||||
ModifyFiledList: [],
|
||||
NotShowFieldList: []
|
||||
NotShowFieldList: [],
|
||||
EquipmentControlFieldList: []
|
||||
}
|
||||
Object.keys(this.obj).forEach(key => {
|
||||
if (!this.form[key]) data.NotShowFieldList.push(key)
|
||||
|
|
@ -187,6 +335,16 @@ export default {
|
|||
ReplaceContentCN: ReplaceContentCN,
|
||||
}
|
||||
data.ModifyFiledList.push(obj)
|
||||
Object.keys(this.EquipmentForm).forEach((key, index) => {
|
||||
if (this.EquipmentForm[key]) {
|
||||
let obj = {
|
||||
FiledName: key,
|
||||
IsView: true,
|
||||
ShowOrder: index,
|
||||
}
|
||||
data.EquipmentControlFieldList.push(obj)
|
||||
}
|
||||
})
|
||||
this.loading = true
|
||||
let res = await updateTrialExtralConfig(param, data)
|
||||
this.loading = false
|
||||
|
|
|
|||
|
|
@ -1,11 +1,7 @@
|
|||
<template>
|
||||
<div class="adReview_wrapper">
|
||||
<el-card
|
||||
v-if="isReadingShowSubjectInfo"
|
||||
shadow="never"
|
||||
:body-style="{ padding: '10px' }"
|
||||
style="margin-bottom: 10px"
|
||||
>
|
||||
<el-card v-if="isReadingShowSubjectInfo" shadow="never" :body-style="{ padding: '10px' }"
|
||||
style="margin-bottom: 10px">
|
||||
<h4>
|
||||
<!-- 受试者: -->
|
||||
{{ $t("trials:adReview:title:subject") }}
|
||||
|
|
@ -17,14 +13,11 @@
|
|||
<!-- <div slot="header" class="clearfix">
|
||||
<span style="font-weight: bold;">评估结果</span>
|
||||
</div> -->
|
||||
<div
|
||||
slot="header"
|
||||
style="
|
||||
<div slot="header" style="
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
"
|
||||
>
|
||||
">
|
||||
<!-- 评估结果 -->
|
||||
<div style="font-weight: bold">
|
||||
{{ $t("trials:adReview:title:result") }}
|
||||
|
|
@ -39,46 +32,33 @@
|
|||
|
||||
<el-table :data="adInfo.VisitInfoList" style="width: 100%">
|
||||
<!-- 访视名称 -->
|
||||
<el-table-column
|
||||
prop="VisitName"
|
||||
:label="$t('trials:adReview:table:visitName')"
|
||||
show-overflow-tooltip
|
||||
width="150"
|
||||
/>
|
||||
<el-table-column prop="VisitName" :label="$t('trials:adReview:table:visitName')" show-overflow-tooltip
|
||||
width="150" />
|
||||
|
||||
<!-- 评估结果 -->
|
||||
<el-table-column
|
||||
v-for="j in judgeQuestion"
|
||||
:key="j.armEnum"
|
||||
:label="
|
||||
j.armEnum === 1
|
||||
<el-table-column v-for="j in judgeQuestion" :key="j.armEnum" :label="j.armEnum === 1
|
||||
? $t('trials:adReview:table:viewR1')
|
||||
: j.armEnum === 2
|
||||
? $t('trials:adReview:table:viewR2')
|
||||
: $fd('ArmEnum', j.armEnum)
|
||||
"
|
||||
align="center"
|
||||
prop=""
|
||||
>
|
||||
" align="center" prop="">
|
||||
<template>
|
||||
<el-table-column
|
||||
v-for="(qs, i) in j.judgeQuestionList"
|
||||
:key="i"
|
||||
prop=""
|
||||
:label="qs"
|
||||
show-overflow-tooltip
|
||||
width="150"
|
||||
>
|
||||
<el-table-column v-for="(qs, i) in j.judgeQuestionList" :key="i" prop="" :label="qs" show-overflow-tooltip
|
||||
width="150">
|
||||
<template slot-scope="scope">
|
||||
<div v-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].QuestionType === 1">
|
||||
<span v-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].DictionaryCode">
|
||||
{{ $fd(scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].DictionaryCode,parseInt(scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer))
|
||||
{{
|
||||
$fd(scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].DictionaryCode,
|
||||
parseInt(scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer))
|
||||
}}
|
||||
</span>
|
||||
<span v-else>
|
||||
{{ scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer }}
|
||||
</span>
|
||||
<span v-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Unit && !isNaN(parseFloat(scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer))">{{ $fd('ValueUnit', parseInt(scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Unit)) }}</span>
|
||||
<span
|
||||
v-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Unit && !isNaN(parseFloat(scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer))">{{
|
||||
$fd('ValueUnit', parseInt(scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Unit)) }}</span>
|
||||
</div>
|
||||
<div v-else-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].QuestionType === 2">
|
||||
<div v-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer">
|
||||
|
|
@ -86,15 +66,11 @@
|
|||
{{ $fd("YesOrNo", scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer) }}
|
||||
</span>
|
||||
<!-- 查看详情 -->
|
||||
<el-button
|
||||
type="text"
|
||||
style="margin-left: 5px"
|
||||
@click="
|
||||
<el-button type="text" style="margin-left: 5px" @click="
|
||||
handleViewDetail(
|
||||
scope.row.VisitTaskInfoList[j.index].GlobalVisitTaskId
|
||||
)
|
||||
"
|
||||
>
|
||||
">
|
||||
{{ $t("trials:adReview:table:view") }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
|
@ -119,60 +95,34 @@
|
|||
</el-table-column>
|
||||
|
||||
<!-- 查看详情 -->
|
||||
<el-table-column
|
||||
:label="
|
||||
criterionType === 10
|
||||
<el-table-column :label="criterionType === 10
|
||||
? $t('trials:adReview:table:visitInfoview')
|
||||
: $t('trials:adReview:table:view')
|
||||
"
|
||||
width="200"
|
||||
:fixed="isFixed ? 'right' : false"
|
||||
>
|
||||
" width="200" :fixed="isFixed ? 'right' : false">
|
||||
<template slot-scope="scope">
|
||||
<!-- 查看R1详情 -->
|
||||
<el-button
|
||||
type="text"
|
||||
:title="$t('trials:adReview:table:viewR1')"
|
||||
@click="handleView(scope.row, 1)"
|
||||
>
|
||||
<el-button type="text" :title="$t('trials:adReview:table:viewR1')" @click="handleView(scope.row, 1)">
|
||||
R1
|
||||
</el-button>
|
||||
<!-- 查看R2详情 -->
|
||||
<el-button
|
||||
type="text"
|
||||
:title="$t('trials:adReview:table:viewR2')"
|
||||
@click="handleView(scope.row, 2)"
|
||||
>
|
||||
<el-button type="text" :title="$t('trials:adReview:table:viewR2')" @click="handleView(scope.row, 2)">
|
||||
R2
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 全局详情 PCWG -->
|
||||
<el-table-column
|
||||
v-if="criterionType === 10"
|
||||
prop="VisitName"
|
||||
:label="$t('trials:adReview:table:glInfo')"
|
||||
show-overflow-tooltip
|
||||
width="150"
|
||||
>
|
||||
<el-table-column v-if="criterionType === 10" prop="VisitName" :label="$t('trials:adReview:table:glInfo')"
|
||||
show-overflow-tooltip width="150">
|
||||
<template slot-scope="scope">
|
||||
<!-- 查看R1详情 -->
|
||||
<el-button
|
||||
v-if="scope.$index === adInfo.VisitInfoList.length - 1"
|
||||
type="text"
|
||||
:title="$t('trials:adReview:table:viewR1')"
|
||||
@click="handleViewGl(scope.row, 1)"
|
||||
>
|
||||
<el-button v-if="scope.$index === adInfo.VisitInfoList.length - 1" type="text"
|
||||
:title="$t('trials:adReview:table:viewR1')" @click="handleViewGl(scope.row, 1)">
|
||||
R1
|
||||
</el-button>
|
||||
<!-- 查看R2详情 -->
|
||||
<el-button
|
||||
v-if="scope.$index === adInfo.VisitInfoList.length - 1"
|
||||
type="text"
|
||||
:title="$t('trials:adReview:table:viewR2')"
|
||||
@click="handleViewGl(scope.row, 2)"
|
||||
>
|
||||
<el-button v-if="scope.$index === adInfo.VisitInfoList.length - 1" type="text"
|
||||
:title="$t('trials:adReview:table:viewR2')" @click="handleViewGl(scope.row, 2)">
|
||||
R2
|
||||
</el-button>
|
||||
</template>
|
||||
|
|
@ -186,120 +136,64 @@
|
|||
$t("trials:adReview:title:adResult")
|
||||
}}</span>
|
||||
</div>
|
||||
<el-form
|
||||
ref="adForm"
|
||||
v-loading="loading"
|
||||
:model="adForm"
|
||||
style="width: 800px"
|
||||
label-width="100"
|
||||
>
|
||||
<el-form ref="adForm" v-loading="loading" :model="adForm" style="width: 800px" label-width="100">
|
||||
<!-- 选择阅片人 -->
|
||||
<el-form-item
|
||||
:label="$t('trials:adReview:title:choseReader')"
|
||||
prop="judgeResultTaskId"
|
||||
:rules="[
|
||||
<el-form-item :label="$t('trials:adReview:title:choseReader')" prop="judgeResultTaskId" :rules="[
|
||||
{ required: true, message: this.$t('common:ruleMessage:select') },
|
||||
]"
|
||||
>
|
||||
<el-radio-group
|
||||
v-model="adForm.judgeResultTaskId"
|
||||
:disabled="adInfo.ReadingTaskState >= 2"
|
||||
>
|
||||
<el-radio
|
||||
v-for="t in visitTaskArmList"
|
||||
:key="t.VisitTaskId"
|
||||
:label="t.VisitTaskId"
|
||||
@change="handleVisitTaskArmChange"
|
||||
>
|
||||
]">
|
||||
<el-radio-group v-model="adForm.judgeResultTaskId" :disabled="adInfo.ReadingTaskState >= 2">
|
||||
<el-radio v-for="t in visitTaskArmList" :key="t.VisitTaskId" :label="t.VisitTaskId"
|
||||
@change="handleVisitTaskArmChange">
|
||||
{{ $fd("ArmEnum", t.ArmEnum) }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<!-- 裁判原因 -->
|
||||
<el-form-item
|
||||
:label="$t('trials:adReview:title:adReason')"
|
||||
prop="judgeResultRemark"
|
||||
:rules="[
|
||||
<el-form-item :label="$t('trials:adReview:title:adReason')" prop="judgeResultRemark" :rules="[
|
||||
{ required: true, message: this.$t('common:ruleMessage:specify') },
|
||||
{
|
||||
max: 500,
|
||||
message: `${this.$t('common:ruleMessage:maxLength')} 500`,
|
||||
trigger: ['blur', 'change'],
|
||||
},
|
||||
]"
|
||||
>
|
||||
]">
|
||||
<div style="position: relative">
|
||||
<div
|
||||
style="
|
||||
<div style="
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 30px;
|
||||
color: #606266;
|
||||
font-size: 13px;
|
||||
"
|
||||
>
|
||||
">
|
||||
{{ remark }}
|
||||
</div>
|
||||
<el-input
|
||||
v-model="adForm.judgeResultRemark"
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 4, maxRows: 6 }"
|
||||
:disabled="adInfo.ReadingTaskState >= 2"
|
||||
style="margin-top: 25px"
|
||||
/>
|
||||
<el-input v-model="adForm.judgeResultRemark" type="textarea" :autosize="{ minRows: 4, maxRows: 6 }"
|
||||
:disabled="adInfo.ReadingTaskState >= 2" style="margin-top: 25px" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
<!-- 截图说明 -->
|
||||
<el-form-item :label="$t('trials:adReview:title:screenShot')">
|
||||
<el-upload
|
||||
action
|
||||
:accept="accept"
|
||||
:on-preview="handlePictureCardPreview"
|
||||
:before-upload="handleBeforeUpload"
|
||||
:http-request="uploadScreenshot"
|
||||
list-type="picture-card"
|
||||
:on-remove="handleRemove"
|
||||
:file-list="fileList"
|
||||
:class="{ disabled: adInfo.ReadingTaskState >= 2 }"
|
||||
:disabled="adInfo.ReadingTaskState >= 2"
|
||||
>
|
||||
<el-upload action :accept="accept" :on-preview="handlePictureCardPreview" :before-upload="handleBeforeUpload"
|
||||
:http-request="uploadScreenshot" list-type="picture-card" :on-remove="handleRemove" :file-list="fileList"
|
||||
:class="{ disabled: adInfo.ReadingTaskState >= 2 }" :disabled="adInfo.ReadingTaskState >= 2">
|
||||
<i slot="default" class="el-icon-plus" />
|
||||
<div
|
||||
slot="file"
|
||||
slot-scope="{ file }"
|
||||
style="width: 100%; height: 100%"
|
||||
>
|
||||
<viewer
|
||||
:ref="file.url"
|
||||
:images="images"
|
||||
style="
|
||||
<div slot="file" slot-scope="{ file }" style="width: 100%; height: 100%">
|
||||
<viewer :ref="file.url" :images="images" style="
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
"
|
||||
>
|
||||
<img
|
||||
class="el-upload-list__item-thumbnail"
|
||||
:src="OSSclientConfig.basePath + file.url"
|
||||
alt=""
|
||||
crossorigin="anonymous"
|
||||
style="max-width: 100%; max-height: 100%"
|
||||
>
|
||||
">
|
||||
<img class="el-upload-list__item-thumbnail" :src="OSSclientConfig.basePath + file.url" alt=""
|
||||
crossorigin="anonymous" style="max-width: 100%; max-height: 100%">
|
||||
<span class="el-upload-list__item-actions">
|
||||
<span
|
||||
class="el-upload-list__item-preview"
|
||||
@click="handlePictureCardPreview(file)"
|
||||
>
|
||||
<span class="el-upload-list__item-preview" @click="handlePictureCardPreview(file)">
|
||||
<i class="el-icon-zoom-in" />
|
||||
</span>
|
||||
|
||||
<span
|
||||
v-if="adInfo.ReadingTaskState < 2"
|
||||
class="el-upload-list__item-delete"
|
||||
@click="handleRemove(file)"
|
||||
>
|
||||
<span v-if="adInfo.ReadingTaskState < 2" class="el-upload-list__item-delete"
|
||||
@click="handleRemove(file)">
|
||||
<i class="el-icon-delete" />
|
||||
</span>
|
||||
</span>
|
||||
|
|
@ -324,10 +218,7 @@
|
|||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-card
|
||||
v-if="isReadingShowPreviousResults"
|
||||
:body-style="{ padding: '10px' }"
|
||||
>
|
||||
<el-card v-if="isReadingShowPreviousResults" :body-style="{ padding: '10px' }">
|
||||
<div slot="header" class="clearfix">
|
||||
<!-- 既往裁判评估 -->
|
||||
<span style="font-weight: bold">{{
|
||||
|
|
@ -335,24 +226,12 @@
|
|||
}}</span>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
v-loading="priorLoading"
|
||||
:data="priorADList"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table v-loading="priorLoading" :data="priorADList" style="width: 100%">
|
||||
<!-- 裁判阅片 -->
|
||||
<el-table-column
|
||||
prop="TaskBlindName"
|
||||
:label="$t('trials:adReview:table:adReading')"
|
||||
show-overflow-tooltip
|
||||
width="200"
|
||||
/>
|
||||
<el-table-column
|
||||
prop="JudgeResultArm"
|
||||
:label="$t('trials:adReview:table:adResult')"
|
||||
show-overflow-tooltip
|
||||
width="200"
|
||||
>
|
||||
<el-table-column prop="TaskBlindName" :label="$t('trials:adReview:table:adReading')" show-overflow-tooltip
|
||||
width="200" />
|
||||
<el-table-column prop="JudgeResultArm" :label="$t('trials:adReview:table:adResult')" show-overflow-tooltip
|
||||
width="200">
|
||||
<template slot-scope="scope">
|
||||
{{ $fd("ArmEnum", scope.row.JudgeResultArm) }}
|
||||
</template>
|
||||
|
|
@ -360,35 +239,22 @@
|
|||
<el-table-column :label="$t('common:action:action')" width="200">
|
||||
<template slot-scope="scope">
|
||||
<!-- 查看详情 -->
|
||||
<el-button
|
||||
circle
|
||||
:title="$t('trials:adReview:table:view')"
|
||||
icon="el-icon-view"
|
||||
@click="handleViewDetail(scope.row.VisitTaskId)"
|
||||
/>
|
||||
<el-button circle :title="$t('trials:adReview:table:view')" icon="el-icon-view"
|
||||
@click="handleViewDetail(scope.row.VisitTaskId)" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
<!-- 签名框 -->
|
||||
<el-dialog
|
||||
v-if="signVisible"
|
||||
:visible.sync="signVisible"
|
||||
:close-on-click-modal="false"
|
||||
width="600px"
|
||||
custom-class="base-dialog-wrapper"
|
||||
>
|
||||
<el-dialog v-if="signVisible" :visible.sync="signVisible" :close-on-click-modal="false" width="600px"
|
||||
custom-class="base-dialog-wrapper">
|
||||
<div slot="title">
|
||||
<span style="font-size: 18px">{{ $t("common:dialogTitle:sign") }}</span>
|
||||
<span style="font-size: 12px; margin-left: 5px">{{
|
||||
`(${$t("common:label:sign")}${currentUser})`
|
||||
}}</span>
|
||||
</div>
|
||||
<SignForm
|
||||
ref="signForm"
|
||||
:sign-code-enum="signCode"
|
||||
@closeDialog="closeSignDialog"
|
||||
/>
|
||||
<SignForm ref="signForm" :sign-code-enum="signCode" @closeDialog="closeSignDialog" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -447,6 +313,10 @@ export default {
|
|||
isExistsClinicalData: {
|
||||
type: Boolean,
|
||||
required: true
|
||||
},
|
||||
imageToolType: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
|
|
@ -685,7 +555,7 @@ export default {
|
|||
// window.opener.postMessage('refreshTaskList', window.location)
|
||||
// 设置当前任务阅片状态为已读
|
||||
this.adInfo.ReadingTaskState = 2
|
||||
const res = await getAutoCutNextTask()
|
||||
const res = await getAutoCutNextTask({imageToolType: this.imageToolType})
|
||||
var isAutoTask = res.Result.AutoCutNextTask
|
||||
if (isAutoTask) {
|
||||
// store.dispatch('reading/resetVisitTasks')
|
||||
|
|
@ -729,17 +599,13 @@ export default {
|
|||
this.$router.currentRoute.query.TrialReadingCriterionId
|
||||
|
||||
var path = ''
|
||||
if (readingTool === 0 || readingTool === 2 ) {
|
||||
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${
|
||||
this.trialId
|
||||
}&subjectCode=${this.subjectCode}&subjectId=${
|
||||
this.subjectId
|
||||
if (readingTool === 0 || readingTool === 2 || readingTool === 3) {
|
||||
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.trialId
|
||||
}&subjectCode=${this.subjectCode}&subjectId=${this.subjectId
|
||||
}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
|
||||
} else {
|
||||
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${
|
||||
this.trialId
|
||||
}&subjectCode=${this.subjectCode}&subjectId=${
|
||||
this.subjectId
|
||||
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.trialId
|
||||
}&subjectCode=${this.subjectCode}&subjectId=${this.subjectId
|
||||
}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
|
||||
}
|
||||
var routeData = this.$router.resolve({ path })
|
||||
|
|
@ -766,21 +632,15 @@ export default {
|
|||
this.$router.currentRoute.query.TrialReadingCriterionId
|
||||
|
||||
var path = ''
|
||||
if (readingTool === 0 || readingTool === 2 ) {
|
||||
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${
|
||||
this.trialId
|
||||
}&subjectCode=${this.subjectCode}&subjectId=${
|
||||
this.subjectId
|
||||
}&visitTaskId=${
|
||||
task.VisitTaskId
|
||||
if (readingTool === 0 || readingTool === 2 || readingTool === 3) {
|
||||
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.trialId
|
||||
}&subjectCode=${this.subjectCode}&subjectId=${this.subjectId
|
||||
}&visitTaskId=${task.VisitTaskId
|
||||
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
|
||||
} else {
|
||||
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${
|
||||
this.trialId
|
||||
}&subjectCode=${this.subjectCode}&subjectId=${
|
||||
this.subjectId
|
||||
}&visitTaskId=${
|
||||
task.VisitTaskId
|
||||
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.trialId
|
||||
}&subjectCode=${this.subjectCode}&subjectId=${this.subjectId
|
||||
}&visitTaskId=${task.VisitTaskId
|
||||
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
|
||||
}
|
||||
var routeData = this.$router.resolve({ path })
|
||||
|
|
@ -801,21 +661,15 @@ export default {
|
|||
this.$router.currentRoute.query.TrialReadingCriterionId
|
||||
|
||||
var path = ''
|
||||
if (readingTool === 0 || readingTool === 2 ) {
|
||||
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${
|
||||
this.trialId
|
||||
}&subjectCode=${this.subjectCode}&subjectId=${
|
||||
this.subjectId
|
||||
}&visitTaskId=${
|
||||
task.GlobalVisitTaskId
|
||||
if (readingTool === 0 || readingTool === 2 || readingTool === 3) {
|
||||
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.trialId
|
||||
}&subjectCode=${this.subjectCode}&subjectId=${this.subjectId
|
||||
}&visitTaskId=${task.GlobalVisitTaskId
|
||||
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
|
||||
} else {
|
||||
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${
|
||||
this.trialId
|
||||
}&subjectCode=${this.subjectCode}&subjectId=${
|
||||
this.subjectId
|
||||
}&visitTaskId=${
|
||||
task.GlobalVisitTaskId
|
||||
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.trialId
|
||||
}&subjectCode=${this.subjectCode}&subjectId=${this.subjectId
|
||||
}&visitTaskId=${task.GlobalVisitTaskId
|
||||
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
|
||||
}
|
||||
var routeData = this.$router.resolve({ path })
|
||||
|
|
@ -932,17 +786,21 @@ export default {
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
|
||||
.box-mr {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.disabled {
|
||||
::v-deep .el-upload--picture-card {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .el-upload-list__item {
|
||||
transition: none !important;
|
||||
}
|
||||
|
||||
::v-deep .el-upload-list__item-thumbnail {
|
||||
/* 图片在方框内显示长边 */
|
||||
object-fit: scale-down !important;
|
||||
|
|
|
|||
|
|
@ -146,6 +146,7 @@ import BidirectionalTool from '@/views/trials/trials-panel/reading/dicoms/tools/
|
|||
import ArrowAnnotateTool from '@/views/trials/trials-panel/reading/dicoms/tools/ArrowAnnotate/ArrowAnnotateTool'
|
||||
import RectangleRoiTool from '@/views/trials/trials-panel/reading/dicoms/tools/RectangleRoi/RectangleRoiTool'
|
||||
import ProbeTool from '@/views/trials/trials-panel/reading/dicoms/tools/Probe/ProbeTool'
|
||||
import FixedCircleRoiTool from '@/views/trials/trials-panel/reading/dicoms/tools/FixedCircleRoi/FixedCircleRoiTool'
|
||||
// import OrientationMarkersTool from '@/views/trials/trials-panel/reading/dicoms/tools/OrientationMarkers/OrientationMarkersTool'
|
||||
import ScaleOverlayTool from '@/views/trials/trials-panel/reading/dicoms/tools/ScaleOverlay/ScaleOverlayTool'
|
||||
import getOrientationString from '@/views/trials/trials-panel/reading/dicoms/tools/OrientationMarkers/getOrientationString'
|
||||
|
|
@ -260,7 +261,7 @@ export default {
|
|||
series: '',
|
||||
ToolStateManager: null,
|
||||
renderedMeasured: [],
|
||||
measuredTools: ['Length', 'Bidirectional', 'ArrowAnnotate', 'RectangleRoi', 'Probe'],
|
||||
measuredTools: ['Length', 'Bidirectional', 'ArrowAnnotate', 'RectangleRoi', 'Probe', 'FixedCircleRoi'],
|
||||
measureData: [],
|
||||
selectedLesion: null,
|
||||
activeTool: 0, // 0:enable 1:passive 2:active
|
||||
|
|
@ -584,6 +585,13 @@ export default {
|
|||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
}
|
||||
} else if (this.CriterionType === 22 && this.activeToolName === 'Probe' && this.readingTaskState < 2) {
|
||||
if (!(e.detail.image.imageFrame.photometricInterpretation === 'MONOCHROME1' || e.detail.image.imageFrame.photometricInterpretation === 'MONOCHROME2')) {
|
||||
this.$alert(this.$t('trials:MRIPDFF:message:message5'))
|
||||
e.stopImmediatePropagation()
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
}
|
||||
}
|
||||
},
|
||||
pointNearTool(e) {
|
||||
|
|
@ -593,7 +601,9 @@ export default {
|
|||
var toolType = this.measuredTools[m]
|
||||
const toolState = ToolStateManager.getImageIdToolState(image.imageId, toolType)
|
||||
if (toolState) {
|
||||
var toolObj = new cornerstoneTools[`${toolType}Tool`]()
|
||||
const toolClass = this.getToolClassByType(toolType)
|
||||
if (!toolClass) continue
|
||||
var toolObj = new toolClass()
|
||||
var i = toolState.data.findIndex(data => toolObj.pointNearTool(element, data, currentPoints.canvas, 'mouse'))
|
||||
if (i > -1 && this.disabledMarks.length > 0 && this.disabledMarks.indexOf(toolState.data[i].remark) > -1) {
|
||||
return true
|
||||
|
|
@ -783,7 +793,9 @@ export default {
|
|||
var toolType = this.measuredTools[m]
|
||||
const toolState = ToolStateManager.getImageIdToolState(e.detail.image.imageId, toolType)
|
||||
if (toolState) {
|
||||
var toolObj = new cornerstoneTools[`${toolType}Tool`]()
|
||||
const toolClass = this.getToolClassByType(toolType)
|
||||
if (!toolClass) continue
|
||||
var toolObj = new toolClass()
|
||||
|
||||
var i = toolState.data.findIndex(data => toolObj.pointNearTool(element, data, currentPoints.canvas, 'mouse'))
|
||||
if (i > -1) {
|
||||
|
|
@ -1019,7 +1031,9 @@ export default {
|
|||
var toolType = this.measuredTools[t]
|
||||
const toolState = ToolStateManager.getImageIdToolState(e.detail.image.imageId, toolType)
|
||||
if (!toolState) continue
|
||||
var toolObj = new cornerstoneTools[`${toolType}Tool`]()
|
||||
const toolClass = this.getToolClassByType(toolType)
|
||||
if (!toolClass) continue
|
||||
var toolObj = new toolClass()
|
||||
var i = toolState.data.findIndex(data => toolObj.pointNearTool(element, data, currentPoints.canvas, 'mouse'))
|
||||
if (i > -1) {
|
||||
var idx = this.measureData.findIndex(item => item.MeasureData && item.MeasureData.data && item.MeasureData.data.uuid === toolState.data[i].uuid)
|
||||
|
|
@ -1046,7 +1060,7 @@ export default {
|
|||
measureData.wc = Math.round(viewport.voi.windowCenter)
|
||||
measureData.data.active = false
|
||||
var criterionType = parseInt(localStorage.getItem('CriterionType'))
|
||||
if (criterionType === 21) {
|
||||
if (criterionType === 21 || criterionType === 22) {
|
||||
measureData.tableQuestionId = this.measureData[idx].TableQuestionId
|
||||
}
|
||||
this.$emit('modifyMeasureData', { measureData, questionInfo })
|
||||
|
|
@ -1057,6 +1071,15 @@ export default {
|
|||
}
|
||||
}
|
||||
},
|
||||
getToolClassByType(toolType) {
|
||||
if (toolType === 'Length') return LengthTool
|
||||
if (toolType === 'Bidirectional') return BidirectionalTool
|
||||
if (toolType === 'ArrowAnnotate') return ArrowAnnotateTool
|
||||
if (toolType === 'RectangleRoi') return RectangleRoiTool
|
||||
if (toolType === 'Probe') return ProbeTool
|
||||
if (toolType === 'FixedCircleRoi') return FixedCircleRoiTool
|
||||
return cornerstoneTools[`${toolType}Tool`]
|
||||
},
|
||||
loadImageStack(dicomSeries) {
|
||||
return new Promise(resolve => {
|
||||
this.isInitWwwc = true
|
||||
|
|
@ -1134,7 +1157,8 @@ export default {
|
|||
// resolve()
|
||||
// })
|
||||
this.loading = true
|
||||
cornerstone.metaData.addProvider(metaDataProvider, 1);
|
||||
cornerstone.metaData.removeProvider(metaDataProvider)
|
||||
cornerstone.metaData.addProvider(metaDataProvider, 100000)
|
||||
cornerstone.loadAndCacheImage(this.stack.imageIds[this.stack.currentImageIdIndex])
|
||||
.then(async image => {
|
||||
if (this.stack.imageIds.indexOf(image.imageId) !== -1) {
|
||||
|
|
@ -1168,8 +1192,22 @@ export default {
|
|||
// Add the tool
|
||||
const toolName = toolBtn.getAttribute('data-tool')
|
||||
const apiTool = cornerstoneTools[`${toolName}Tool`]
|
||||
if (apiTool) {
|
||||
const toolAlreadyAddedToElement = cornerstoneTools.getToolForElement(element, apiTool)
|
||||
let toolClass = apiTool
|
||||
if (toolName === 'Length') {
|
||||
toolClass = LengthTool
|
||||
} else if (toolName === 'Bidirectional') {
|
||||
toolClass = BidirectionalTool
|
||||
} else if (toolName === 'ArrowAnnotate') {
|
||||
toolClass = ArrowAnnotateTool
|
||||
} else if (toolName === 'RectangleRoi') {
|
||||
toolClass = RectangleRoiTool
|
||||
} else if (toolName === 'Probe' && (parseInt(localStorage.getItem('CriterionType')) === 21)) {
|
||||
toolClass = ProbeTool
|
||||
} else if (toolName === 'Probe' && (parseInt(localStorage.getItem('CriterionType')) === 22)) {
|
||||
toolClass = FixedCircleRoiTool
|
||||
}
|
||||
if (toolClass) {
|
||||
const toolAlreadyAddedToElement = cornerstoneTools.getToolForElement(element, toolClass)
|
||||
|
||||
if (!toolAlreadyAddedToElement) {
|
||||
if (toolName === 'Length') {
|
||||
|
|
@ -1182,8 +1220,10 @@ export default {
|
|||
cornerstoneTools.addToolForElement(element, ArrowAnnotateTool, { configuration: { allowEmptyLabel: true, handleRadius: false, drawHandlesOnHover: true, hideHandlesIfMoving: true } })
|
||||
} else if (toolName === 'RectangleRoi') {
|
||||
cornerstoneTools.addToolForElement(element, RectangleRoiTool, { configuration: { allowEmptyLabel: true, handleRadius: false, drawHandlesOnHover: true, hideHandlesIfMoving: true } })
|
||||
} else if (toolName === 'Probe' && parseInt(localStorage.getItem('CriterionType')) === 21) {
|
||||
} else if (toolName === 'Probe' && (parseInt(localStorage.getItem('CriterionType')) === 21)) {
|
||||
cornerstoneTools.addToolForElement(element, ProbeTool, { configuration: { fixedRadius: 5, handleRadius: true, drawHandlesOnHover: true, hideHandlesIfMoving: true, digits: this.digitPlaces } })
|
||||
} else if (toolName === 'Probe' && parseInt(localStorage.getItem('CriterionType')) === 22) {
|
||||
cornerstoneTools.addToolForElement(element, ProbeTool, { configuration: { fixedRadius: 12, unit: 'mm', handleRadius: true, drawHandlesOnHover: true, hideHandlesIfMoving: true, digits: this.digitPlaces } })
|
||||
} else {
|
||||
cornerstoneTools.addToolForElement(element, apiTool)
|
||||
}
|
||||
|
|
@ -1527,7 +1567,7 @@ export default {
|
|||
measureData.wc = Math.round(viewport.voi.windowCenter)
|
||||
measureData.data.active = false
|
||||
var criterionType = parseInt(localStorage.getItem('CriterionType'))
|
||||
if (criterionType === 21) {
|
||||
if (criterionType === 21 || criterionType === 22) {
|
||||
measureData.tableQuestionId = this.measureData[idx].TableQuestionId
|
||||
}
|
||||
this.$emit('modifyMeasureData', { measureData, questionInfo })
|
||||
|
|
@ -1981,7 +2021,8 @@ export default {
|
|||
'CobbAngle',
|
||||
'Angle',
|
||||
'Bidirectional',
|
||||
'FreehandRoi'
|
||||
'FreehandRoi',
|
||||
'FixedCircleRoi'
|
||||
]
|
||||
for (let i = 0; i < toolROITypes.length; i++) {
|
||||
const toolROIType = toolROITypes[i]
|
||||
|
|
|
|||
|
|
@ -492,6 +492,10 @@
|
|||
:question-form-change-state="questionFormChangeState" :question-form-change-num="questionFormChangeNum"
|
||||
:is-show="isShow" :is-reading-show-subject-info="isReadingShowSubjectInfo"
|
||||
@handleReadingChart="handleReadingChart" />
|
||||
<MRIPDFFAdvance v-else-if="CriterionType === 22" ref="measurementList"
|
||||
:question-form-change-state="questionFormChangeState" :question-form-change-num="questionFormChangeNum"
|
||||
:is-show="isShow" :is-reading-show-subject-info="isReadingShowSubjectInfo"
|
||||
@handleReadingChart="handleReadingChart" />
|
||||
<h2 v-else style="color:#ddd">
|
||||
Developing...
|
||||
</h2>
|
||||
|
|
@ -539,7 +543,7 @@
|
|||
</el-tab-pane>
|
||||
<!-- 其他 -->
|
||||
<el-tab-pane :label="$t('trials:reading:tab:others')" name="3">
|
||||
<Others v-if="activeName === '3'" />
|
||||
<Others v-if="activeName === '3'" :imageToolType="1"/>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
|
|
@ -689,6 +693,7 @@ import LuganoWithoutPETQuestionList from './LuganoWithoutPET/QuestionList'
|
|||
import IVUSList from './IVUS/QuestionList'
|
||||
import OCTList from './OCT/QuestionList'
|
||||
import MRIPDFF from './MRIPDFF/QuestionList'
|
||||
import MRIPDFFAdvance from './MRIPDFFAdvance/QuestionList'
|
||||
import CustomWwwcForm from './CustomWwwcForm'
|
||||
import Manuals from './Manuals'
|
||||
import Hotkeys from './Hotkeys'
|
||||
|
|
@ -725,6 +730,7 @@ export default {
|
|||
IVUSList,
|
||||
OCTList,
|
||||
MRIPDFF,
|
||||
MRIPDFFAdvance,
|
||||
'download-dicom-and-nonedicom': downloadDicomAndNonedicom,
|
||||
'upload-dicom-and-nonedicom': uploadDicomAndNonedicom,
|
||||
SignForm
|
||||
|
|
@ -1012,6 +1018,10 @@ export default {
|
|||
this.measuredTools = [{
|
||||
toolName: 'Probe', text: this.$t('trials:reading:button:circle'), icon: 'oval', isDisabled: false, disabledReason: ''
|
||||
}]
|
||||
} else if (this.CriterionType === 22) {
|
||||
this.measuredTools = [{
|
||||
toolName: 'Probe', text: this.$t('trials:reading:button:circle'), icon: 'oval', isDisabled: false, disabledReason: ''
|
||||
}]
|
||||
}
|
||||
this.rotateList[0] = '1'
|
||||
this.colorList[0] = ''
|
||||
|
|
@ -1041,30 +1051,23 @@ export default {
|
|||
console.log('getMeasureData')
|
||||
})
|
||||
DicomEvent.$on('getScreenshots', async (measuredData, callback) => {
|
||||
if (this.currentDicomCanvasIndex > -1) {
|
||||
if (this.currentDicomCanvasIndex <= -1 || typeof callback !== 'function') return
|
||||
const shouldRelocate = this.shouldRelocateBeforeScreenshot(measuredData)
|
||||
if (shouldRelocate) {
|
||||
await this.imageLocation(measuredData)
|
||||
if (!measuredData.isMarked) {
|
||||
}
|
||||
if (!measuredData || !measuredData.isMarked) {
|
||||
callback()
|
||||
return
|
||||
}
|
||||
setTimeout(async () => {
|
||||
// var base64Str = this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].getScreenshots()
|
||||
const divForDownloadViewport = document.querySelector(
|
||||
`div[data-canvas-uid="dicomCanvas${this.currentDicomCanvasIndex}"]`
|
||||
)
|
||||
var canvas = await html2canvas(divForDownloadViewport)
|
||||
var base64Str = canvas.toDataURL('image/png', 1)
|
||||
console.log('getScreenshots')
|
||||
const base64Str = await this.captureActiveViewportScreenshot()
|
||||
callback(base64Str)
|
||||
}, 50)
|
||||
}
|
||||
})
|
||||
DicomEvent.$on('imageLocation', async (measuredData) => {
|
||||
return new Promise(async resolve => {
|
||||
if (!measuredData) return
|
||||
await this.imageLocation(measuredData)
|
||||
|
||||
console.log('imageLocation')
|
||||
await this.imageLocation(measuredData)
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
|
|
@ -1126,7 +1129,12 @@ export default {
|
|||
DicomEvent.$on('addAnnotation', async obj => {
|
||||
this.tmpData = Object.assign({}, obj.question)
|
||||
// await this.imageLocation(obj.locateInfo)
|
||||
if (this.CriterionType === 21) {
|
||||
this.setToolActive('Probe', true, null, 'tableQuestion')
|
||||
} else if (this.CriterionType === 22) {
|
||||
this.setToolActive('Probe', true, null, 'tableQuestion')
|
||||
}
|
||||
|
||||
})
|
||||
window.addEventListener('beforeunload', () => {
|
||||
if (this.petctWindow) {
|
||||
|
|
@ -1230,7 +1238,7 @@ export default {
|
|||
async getHotKeys() {
|
||||
// const loading = this.$loading({ fullscreen: true })
|
||||
try {
|
||||
const res = await getDoctorShortcutKey({ imageToolType: 0 })
|
||||
const res = await getDoctorShortcutKey({ imageToolType: 1 })
|
||||
res.Result.map(item => {
|
||||
this.hotKeyList.push({ id: item.Id, altKey: item.AltKey, ctrlKey: item.CtrlKey, shiftKey: item.ShiftKey, metaKey: item.MetaKey, key: item.Keyboardkey, code: item.Code, text: item.Text, shortcutKeyEnum: item.ShortcutKeyEnum })
|
||||
})
|
||||
|
|
@ -1695,6 +1703,43 @@ export default {
|
|||
}
|
||||
})
|
||||
},
|
||||
shouldRelocateBeforeScreenshot(measuredData) {
|
||||
if (!measuredData) return false
|
||||
const currentCanvas = this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`] && this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0]
|
||||
if (!currentCanvas || !currentCanvas.stack) return true
|
||||
const currentStack = currentCanvas.stack
|
||||
if (currentStack.visitTaskId !== measuredData.visitTaskId) return true
|
||||
|
||||
// 当前序列与病灶目标序列一致时,避免重复 imageLocation 引发的重载等待
|
||||
const targetSeries = this.getSeriesInfoByMark(measuredData.visitTaskId, measuredData)
|
||||
if (!targetSeries) return false
|
||||
if (currentStack.seriesId !== targetSeries.seriesId) return true
|
||||
if (typeof targetSeries.imageIdIndex === 'number' &&
|
||||
targetSeries.imageIdIndex !== currentStack.currentImageIdIndex) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
async captureActiveViewportScreenshot() {
|
||||
// const currentCanvas = this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`] && this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0]
|
||||
await this.$nextTick()
|
||||
const divForDownloadViewport = document.querySelector(
|
||||
`div[data-canvas-uid="dicomCanvas${this.currentDicomCanvasIndex}"]`
|
||||
)
|
||||
if (divForDownloadViewport) {
|
||||
try {
|
||||
const canvas = await html2canvas(divForDownloadViewport, {
|
||||
logging: false,
|
||||
useCORS: true,
|
||||
scale: 1
|
||||
})
|
||||
return canvas.toDataURL('image/png')
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
return ''
|
||||
},
|
||||
setToolToTarget(obj) {
|
||||
if (obj.readingTaskState < 2 && obj.markTool && !obj.isMarked) {
|
||||
if (this.activeTool) {
|
||||
|
|
@ -2160,7 +2205,7 @@ export default {
|
|||
},
|
||||
// 添加标记
|
||||
setMeasureData(data) {
|
||||
if (this.CriterionType === 21) {
|
||||
if (this.CriterionType === 21 || this.CriterionType === 22) {
|
||||
if (this.tmpData) {
|
||||
data.tableQuestionId = this.tmpData.Id
|
||||
data.tableQuestionMark = this.tmpData.QuestionMark
|
||||
|
|
@ -2174,7 +2219,7 @@ export default {
|
|||
},
|
||||
// 修改标记
|
||||
modifyMeasureData(data) {
|
||||
if (this.CriterionType === 21 && data.measureData.tableQuestionId) {
|
||||
if ((this.CriterionType === 21 || this.CriterionType === 22) && data.measureData.tableQuestionId) {
|
||||
this.$refs['measurementList'].modifyMeasuredData(data)
|
||||
} else {
|
||||
this.$refs['measurementList'].modifyMeasuredData(data)
|
||||
|
|
@ -2341,7 +2386,7 @@ export default {
|
|||
this.readingTaskState = 2
|
||||
await store.dispatch('reading/setVisitTaskReadingTaskState', { visitTaskId: this.visitTaskId, readingTaskState: 2 })
|
||||
await store.dispatch('reading/setCurrentReadingTaskState', 2)
|
||||
const res = await getAutoCutNextTask()
|
||||
const res = await getAutoCutNextTask({imageToolType: 1})
|
||||
var isAutoTask = res.Result.AutoCutNextTask
|
||||
if (isAutoTask) {
|
||||
window.location.reload()
|
||||
|
|
|
|||
|
|
@ -99,6 +99,15 @@
|
|||
</div>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-if="isFusion" class="item" effect="dark" :content="$t('trials:reading:button:crosshairsPosition')" placement="bottom">
|
||||
<div class="tool-wrapper">
|
||||
<div class="icon" :class="[activeTool === 'FusionJumpToPointTool' ? 'tool_active' : '']"
|
||||
@click.prevent="setFusionJumpToolActive">
|
||||
<svg-icon icon-class="position" class="svg-icon" />
|
||||
</div>
|
||||
<div class="text">{{ $t('trials:reading:button:crosshairsPosition') }}</div>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
<!-- 椭圆oval -->
|
||||
<template v-for="tool in measuredTools">
|
||||
<el-tooltip v-if="isCurrentTask && readingTaskState !== 2" :key="tool.toolName" class="item" effect="dark"
|
||||
|
|
@ -140,46 +149,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' ? '' : 'g/ml'"
|
||||
:modality="fusionOverlayModality" @setColorMap="setColorMap" @voiChange="voiChange" />
|
||||
|
||||
<!-- 截屏 -->
|
||||
<!-- <el-tooltip class="item" effect="dark" :content="`${$t('trials:reading:button:screenShot')}`" placement="bottom">
|
||||
|
|
@ -218,17 +189,17 @@
|
|||
: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" />
|
||||
@fusionSlicePointChange="handleFusionSlicePointChange" />
|
||||
<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" />
|
||||
@upperRangeChange="upperRangeChange" @fusionSlicePointChange="handleFusionSlicePointChange" />
|
||||
<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" />
|
||||
@upperRangeChange="upperRangeChange" @fusionSlicePointChange="handleFusionSlicePointChange" />
|
||||
<Viewport ref="PET_MIP_CORONAL" :index="4" :active-index="activeIndex"
|
||||
:is-reading-show-subject-info="isReadingShowSubjectInfo" :series-info="petSeries"
|
||||
:rendering-engine-id="renderingEngineId" viewport-id="PET_MIP_CORONAL" :measure-datas="measureDatas"
|
||||
|
|
@ -298,6 +269,7 @@ import CustomWwwcForm from './../CustomWwwcForm'
|
|||
import FusionForm from './FusionForm.vue'
|
||||
import { getTableAnswerRowInfoList, getDicomSeriesInfo } from '@/api/trials'
|
||||
import FusionEvent from './FusionEvent'
|
||||
import FusionJumpToPointTool from '../../../dicoms3D/components/tools/FusionJumpToPointTool'
|
||||
// import { ColorMaps } from '@kitware/vtk.js/Common/Core/ColorMaps'
|
||||
import vtkColorMaps from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps'
|
||||
import vtkColorTransferFunction from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction'
|
||||
|
|
@ -306,6 +278,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'
|
||||
|
|
@ -320,7 +293,6 @@ const {
|
|||
// StackScrollMouseWheelTool,
|
||||
StackScrollTool,
|
||||
synchronizers,
|
||||
MIPJumpToClickTool,
|
||||
// VolumeRotateMouseWheelTool,
|
||||
VolumeRotateTool,
|
||||
OrientationMarkerTool,
|
||||
|
|
@ -331,6 +303,7 @@ const {
|
|||
CrosshairsTool,
|
||||
TrackballRotateTool,
|
||||
ProbeTool,
|
||||
MIPJumpToClickTool,
|
||||
ScaleOverlayTool,
|
||||
utilities,
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
|
|
@ -395,7 +368,8 @@ export default {
|
|||
TableQuestions,
|
||||
CustomWwwcForm,
|
||||
FusionForm,
|
||||
readingChart
|
||||
readingChart,
|
||||
colorMap
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -441,6 +415,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 +434,15 @@ export default {
|
|||
activeCanvasWW: null,
|
||||
activeCanvasWC: null,
|
||||
fusion: { visible: false }, // 历史记录融合调窗
|
||||
screenshotWindow: null
|
||||
screenshotWindow: null,
|
||||
hasVoiChanged: false,
|
||||
lastUpper: null,
|
||||
fusionCrosshairStyle: {
|
||||
lineWidth: 2,
|
||||
lineLength: 20,
|
||||
centerHoleSize: 20,
|
||||
},
|
||||
fusionJumpEnabled: false
|
||||
// initFirstAnnotation:false
|
||||
}
|
||||
},
|
||||
|
|
@ -485,9 +469,6 @@ export default {
|
|||
mounted() {
|
||||
document.documentElement.style.userSelect = 'none'
|
||||
window.addEventListener('message', this.receiveMsg)
|
||||
|
||||
console.log(cornerstoneTools)
|
||||
console.log(cornerstone3D)
|
||||
this.$i18n.locale = this.$route.query.lang
|
||||
this.setLanguage(this.$route.query.lang)
|
||||
this.readingTaskState = parseInt(this.$route.query.readingTaskState)
|
||||
|
|
@ -513,11 +494,11 @@ export default {
|
|||
this.colorMaps = getColormapNames()
|
||||
this.colorMaps.unshift('hsv')
|
||||
this.$nextTick(() => {
|
||||
this.renderColorMaps()
|
||||
// this.renderColorMaps()
|
||||
this.handleElementsClick()
|
||||
this.initPage()
|
||||
this.upperRangeChange(this.range)
|
||||
this.initSlider()
|
||||
// this.upperRangeChange(this.range)
|
||||
// this.initSlider()
|
||||
})
|
||||
|
||||
FusionEvent.$on('getAnnotations', () => {
|
||||
|
|
@ -656,6 +637,7 @@ export default {
|
|||
this.setUpToolGroups()
|
||||
|
||||
this.setUpSynchronizers()
|
||||
this.dispatchFusionCenterPoint()
|
||||
// this.$refs['CT_AXIAL'].scroll(0)
|
||||
// this.$refs['PT_AXIAL'].scroll(0)
|
||||
// this.$refs['FUSION_AXIAL'].scroll(0)
|
||||
|
|
@ -744,6 +726,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 => {
|
||||
|
|
@ -866,6 +849,9 @@ export default {
|
|||
const { detail } = e
|
||||
const { annotation } = detail
|
||||
const { metadata } = annotation
|
||||
if (!metadata || metadata.toolName === FusionJumpToPointTool.toolName) {
|
||||
return
|
||||
}
|
||||
const measureData = {}
|
||||
measureData.frame = 0
|
||||
measureData.data = annotation
|
||||
|
|
@ -883,6 +869,9 @@ export default {
|
|||
const { detail } = e
|
||||
const { annotation } = detail
|
||||
const { metadata, data } = annotation
|
||||
if (!metadata || metadata.toolName === FusionJumpToPointTool.toolName) {
|
||||
return
|
||||
}
|
||||
// var i = this.initAnnotations.findIndex(i => i.orderMark === data.remark && !i.isInit)
|
||||
// if (i > -1) {
|
||||
// console.log(data.remark)
|
||||
|
|
@ -1006,6 +995,7 @@ export default {
|
|||
cornerstoneTools.addTool(WindowLevelTool)
|
||||
cornerstoneTools.addTool(ZoomTool)
|
||||
cornerstoneTools.addTool(StackScrollTool)
|
||||
cornerstoneTools.addTool(FusionJumpToPointTool)
|
||||
cornerstoneTools.addTool(MIPJumpToClickTool)
|
||||
cornerstoneTools.addTool(VolumeRotateTool)
|
||||
cornerstoneTools.addTool(EllipticalROITool)
|
||||
|
|
@ -1038,6 +1028,7 @@ export default {
|
|||
toolGroup.addTool(ProbeTool.toolName)
|
||||
toolGroup.addTool(ScaleOverlayTool.toolName)
|
||||
toolGroup.addTool(OrientationMarkerTool.toolName)
|
||||
toolGroup.addTool(FusionJumpToPointTool.toolName, this.getFusionJumpToolConfiguration())
|
||||
})
|
||||
|
||||
fusionToolGroup.addTool(PanTool.toolName)
|
||||
|
|
@ -1053,11 +1044,12 @@ export default {
|
|||
fusionToolGroup.addTool(ProbeTool.toolName)
|
||||
fusionToolGroup.addTool(ScaleOverlayTool.toolName)
|
||||
fusionToolGroup.addTool(OrientationMarkerTool.toolName)
|
||||
fusionToolGroup.addTool(FusionJumpToPointTool.toolName, this.getFusionJumpToolConfiguration())
|
||||
// Here is the difference in the toolGroups used, that we need to specify the
|
||||
// volume to use for the WindowLevelTool for the fusion viewports
|
||||
|
||||
fusionToolGroup.addTool(WindowLevelTool.toolName, {
|
||||
volumeId: ptVolumeId
|
||||
volumeId: ptVolumeId2
|
||||
});
|
||||
|
||||
[ctToolGroup, ptToolGroup, fusionToolGroup].forEach((toolGroup) => {
|
||||
|
|
@ -1104,6 +1096,7 @@ export default {
|
|||
true // overwrite
|
||||
)
|
||||
toolGroup.setToolEnabled(ScaleOverlayTool.toolName)
|
||||
|
||||
// toolGroup.setToolConfiguration(OrientationMarkerTool.toolName, {
|
||||
// orientationWidget: {
|
||||
// enabled: true,
|
||||
|
|
@ -1155,26 +1148,215 @@ export default {
|
|||
mipToolGroup.setToolActive(VolumeRotateTool.toolName, {
|
||||
bindings: [{ mouseButton: MouseBindings.Wheel }],
|
||||
});
|
||||
mipToolGroup.addTool('MIPJumpToClickTool', {
|
||||
//
|
||||
toolGroupId: ptToolGroupId
|
||||
mipToolGroup.addTool(PanTool.toolName)
|
||||
mipToolGroup.addTool(ZoomTool.toolName)
|
||||
mipToolGroup.setToolActive(PanTool.toolName, {
|
||||
bindings: [{ mouseButton: MouseBindings.Auxiliary }]
|
||||
})
|
||||
mipToolGroup.setToolActive(ZoomTool.toolName, {
|
||||
bindings: [{ mouseButton: MouseBindings.Secondary }]
|
||||
})
|
||||
mipToolGroup.addTool(FusionJumpToPointTool.toolName, this.getFusionJumpToolConfiguration())
|
||||
mipToolGroup.addTool(MIPJumpToClickTool.toolName, {
|
||||
targetViewportIds: this.getFusionJumpToolConfiguration().targetViewportIds
|
||||
})
|
||||
|
||||
// Set the initial state of the tools, here we set one tool active on left click.
|
||||
// This means left click will draw that tool.
|
||||
mipToolGroup.setToolActive('MIPJumpToClickTool', {
|
||||
bindings: [
|
||||
{
|
||||
mouseButton: MouseBindings.Primary // Left ClickR
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
// As the Stack Scroll mouse wheel is a tool using the `mouseWheelCallback`
|
||||
// hook instead of mouse buttons, it does not need to assign any mouse button.
|
||||
// mipToolGroup.setToolActive('VolumeRotateMouseWheel')
|
||||
// mipToolGroup.addTool(OrientationMarkerTool.toolName)
|
||||
// mipToolGroup.setToolActive(OrientationMarkerTool.toolName)
|
||||
mipToolGroup.addViewport(viewportIds.PETMIP.CORONAL, renderingEngineId)
|
||||
this.setFusionMipClickEnabled(!this.fusionJumpEnabled && this.isFusion)
|
||||
},
|
||||
getFusionJumpToolConfiguration() {
|
||||
return {
|
||||
targetViewportIds: [
|
||||
viewportIds.CT.AXIAL,
|
||||
viewportIds.PT.AXIAL,
|
||||
viewportIds.FUSION.AXIAL,
|
||||
viewportIds.PETMIP.CORONAL
|
||||
],
|
||||
useBrightestPoint: true,
|
||||
jumpToTargetViewports: true,
|
||||
dispatchEventName: 'fusion-mip-point-selected',
|
||||
getReferenceLineColor: this.setFusionCrosshairsToolLineColor,
|
||||
style: this.fusionCrosshairStyle,
|
||||
referenceLinesCenterGapRadius: this.fusionCrosshairStyle.centerHoleSize,
|
||||
minimal: {
|
||||
enabled: true,
|
||||
lineLengthInPx: this.fusionCrosshairStyle.lineLength,
|
||||
},
|
||||
mipViewportIds: [viewportIds.PETMIP.CORONAL]
|
||||
}
|
||||
},
|
||||
setFusionCrosshairsToolLineColor(viewportId) {
|
||||
const colors = {
|
||||
[viewportIds.CT.AXIAL]: '#0000ff',
|
||||
[viewportIds.PT.AXIAL]: '#0000ff',
|
||||
[viewportIds.FUSION.AXIAL]: '#0000ff',
|
||||
[viewportIds.PETMIP.CORONAL]: '#ff0000'
|
||||
}
|
||||
return colors[viewportId] || '#0000ff'
|
||||
},
|
||||
handleFusionSlicePointChange({ viewportId, worldPoint }) {
|
||||
if (!this.fusionJumpEnabled) return
|
||||
if (!this.isFusion) return
|
||||
if (!viewportId || !Array.isArray(worldPoint) || worldPoint.length < 3) return
|
||||
if (viewportId === viewportIds.PETMIP.CORONAL) return
|
||||
|
||||
const activeViewportId = this.activeIndex === 1
|
||||
? viewportIds.CT.AXIAL
|
||||
: this.activeIndex === 2
|
||||
? viewportIds.PT.AXIAL
|
||||
: this.activeIndex === 3
|
||||
? viewportIds.FUSION.AXIAL
|
||||
: viewportIds.PETMIP.CORONAL
|
||||
if (viewportId !== activeViewportId) return
|
||||
|
||||
const renderEngine = getRenderingEngine(renderingEngineId)
|
||||
if (!renderEngine) return
|
||||
const toolGroupCandidates = [fusionToolGroupId, mipToolGroupUID, ptToolGroupId, ctToolGroupId]
|
||||
let instance = null
|
||||
for (const toolGroupId of toolGroupCandidates) {
|
||||
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
|
||||
if (!toolGroup || !toolGroup.getToolInstance) continue
|
||||
instance = toolGroup.getToolInstance(FusionJumpToPointTool.toolName)
|
||||
if (instance && instance.setPoint) break
|
||||
}
|
||||
if (!instance || !instance.setPoint) return
|
||||
|
||||
instance.setPoint(worldPoint, viewportId, renderEngine.id, {
|
||||
jumpToTargetViewports: false,
|
||||
dispatchEvent: false,
|
||||
})
|
||||
},
|
||||
dispatchFusionCenterPoint(retryCount = 0) {
|
||||
if (!this.fusionJumpEnabled) return
|
||||
const renderEngine = getRenderingEngine(renderingEngineId)
|
||||
if (!renderEngine) return
|
||||
const toolGroupCandidates = [fusionToolGroupId, mipToolGroupUID, ptToolGroupId, ctToolGroupId]
|
||||
let instance = null
|
||||
for (const toolGroupId of toolGroupCandidates) {
|
||||
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
|
||||
if (!toolGroup || !toolGroup.getToolInstance) continue
|
||||
instance = toolGroup.getToolInstance(FusionJumpToPointTool.toolName)
|
||||
if (instance && instance.setPoint) break
|
||||
}
|
||||
if (!instance || !instance.setPoint) {
|
||||
if (retryCount < 10) {
|
||||
setTimeout(() => {
|
||||
this.dispatchFusionCenterPoint(retryCount + 1)
|
||||
}, 120)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const candidates = [viewportIds.FUSION.AXIAL, viewportIds.PT.AXIAL, viewportIds.CT.AXIAL]
|
||||
for (const viewportId of candidates) {
|
||||
const viewport = renderEngine.getViewport(viewportId)
|
||||
if (!viewport) continue
|
||||
const width = viewport.element ? viewport.element.clientWidth : null
|
||||
const height = viewport.element ? viewport.element.clientHeight : null
|
||||
let worldPoint = null
|
||||
if (width && height && viewport.canvasToWorld) {
|
||||
worldPoint = viewport.canvasToWorld([width / 2, height / 2])
|
||||
}
|
||||
if ((!worldPoint || worldPoint.length < 3) && viewport.getCamera) {
|
||||
const camera = viewport.getCamera()
|
||||
if (camera && camera.focalPoint) {
|
||||
worldPoint = camera.focalPoint
|
||||
}
|
||||
}
|
||||
if (!worldPoint || worldPoint.length < 3) continue
|
||||
instance.setPoint(worldPoint, viewportId, renderEngine.id, {
|
||||
jumpToTargetViewports: true,
|
||||
dispatchEvent: false,
|
||||
})
|
||||
return
|
||||
}
|
||||
if (retryCount < 10) {
|
||||
setTimeout(() => {
|
||||
this.dispatchFusionCenterPoint(retryCount + 1)
|
||||
}, 120)
|
||||
}
|
||||
},
|
||||
setFusionJumpToolActive() {
|
||||
if (this.activeTool === FusionJumpToPointTool.toolName) {
|
||||
this.setFusionJumpEnabled(false)
|
||||
this.activeTool = ''
|
||||
return
|
||||
}
|
||||
if (this.activeTool) {
|
||||
this.setToolMode('passive', this.activeTool)
|
||||
}
|
||||
this.setFusionJumpEnabled(true)
|
||||
this.activeTool = FusionJumpToPointTool.toolName
|
||||
},
|
||||
setFusionMipClickEnabled(enabled) {
|
||||
const toolGroup = ToolGroupManager.getToolGroup(mipToolGroupUID)
|
||||
if (!toolGroup || !toolGroup.hasTool(MIPJumpToClickTool.toolName)) return
|
||||
if (enabled && this.isFusion) {
|
||||
toolGroup.setToolActive(MIPJumpToClickTool.toolName, {
|
||||
bindings: [{ mouseButton: MouseBindings.Primary }]
|
||||
})
|
||||
} else {
|
||||
toolGroup.setToolDisabled(MIPJumpToClickTool.toolName)
|
||||
}
|
||||
},
|
||||
setFusionJumpEnabled(enabled) {
|
||||
this.fusionJumpEnabled = !!enabled
|
||||
const toolGroupIds = [ctToolGroupId, ptToolGroupId, fusionToolGroupId, mipToolGroupUID]
|
||||
if (enabled) {
|
||||
this.setFusionMipClickEnabled(false)
|
||||
toolGroupIds.forEach((toolGroupId) => {
|
||||
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
|
||||
if (!toolGroup || !toolGroup.hasTool(FusionJumpToPointTool.toolName)) return
|
||||
toolGroup.setToolActive(FusionJumpToPointTool.toolName, {
|
||||
bindings: [{ mouseButton: MouseBindings.Primary }]
|
||||
})
|
||||
})
|
||||
this.dispatchFusionCenterPoint()
|
||||
return
|
||||
}
|
||||
this.setFusionMipClickEnabled(this.isFusion)
|
||||
toolGroupIds.forEach((toolGroupId) => {
|
||||
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
|
||||
if (!toolGroup || !toolGroup.hasTool(FusionJumpToPointTool.toolName)) return
|
||||
toolGroup.setToolDisabled(FusionJumpToPointTool.toolName)
|
||||
})
|
||||
this.clearFusionJumpToPointAnnotations()
|
||||
},
|
||||
clearFusionJumpToPointAnnotations() {
|
||||
const annotations = cornerstoneTools.annotation.state.getAllAnnotations() || []
|
||||
const removeList = []
|
||||
for (let i = 0; i < annotations.length; i++) {
|
||||
const item = annotations[i]
|
||||
if (!item || !item.metadata) continue
|
||||
if (item.metadata.toolName !== FusionJumpToPointTool.toolName) continue
|
||||
removeList.push(item)
|
||||
}
|
||||
if (!removeList.length) return
|
||||
removeList.forEach(i => {
|
||||
cornerstoneTools.annotation.state.removeAnnotation(i.annotationUID)
|
||||
})
|
||||
const renderEngine = getRenderingEngine(renderingEngineId)
|
||||
if (!renderEngine) return
|
||||
const viewportIdsToRender = [
|
||||
viewportIds.CT.AXIAL,
|
||||
viewportIds.PT.AXIAL,
|
||||
viewportIds.FUSION.AXIAL,
|
||||
viewportIds.PETMIP.CORONAL
|
||||
]
|
||||
viewportIdsToRender.forEach(viewportId => {
|
||||
const viewport = renderEngine.getViewport(viewportId)
|
||||
if (viewport && viewport.render) {
|
||||
viewport.render()
|
||||
}
|
||||
})
|
||||
},
|
||||
getTextLines(data, targetId) {
|
||||
const cachedVolumeStats = data.cachedStats[targetId]
|
||||
|
|
@ -1238,15 +1420,23 @@ export default {
|
|||
},
|
||||
// 设置测量工具启用(不会对输入作出反应)
|
||||
setBasicToolActive(toolName) {
|
||||
var toolGroupIds = [ctToolGroupId, ptToolGroupId, fusionToolGroupId]
|
||||
var toolGroupIds = [ctToolGroupId, ptToolGroupId, fusionToolGroupId, mipToolGroupUID]
|
||||
if (toolName !== FusionJumpToPointTool.toolName && this.fusionJumpEnabled) {
|
||||
this.setFusionJumpEnabled(false)
|
||||
}
|
||||
toolGroupIds.forEach((toolGroupId) => {
|
||||
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
|
||||
if (!toolGroup || !toolGroup.hasTool(toolName)) return
|
||||
if (this.activeTool === toolName) {
|
||||
if (toolGroup.hasTool(toolName)) {
|
||||
toolGroup.setToolPassive(toolName)
|
||||
}
|
||||
} else {
|
||||
if (this.activeTool) {
|
||||
if (toolGroup.hasTool(this.activeTool)) {
|
||||
toolGroup.setToolPassive(this.activeTool)
|
||||
}
|
||||
}
|
||||
var bindings = []
|
||||
if (toolName === 'Pan') {
|
||||
bindings = [
|
||||
|
|
@ -1328,9 +1518,10 @@ export default {
|
|||
}
|
||||
},
|
||||
setToolMode(mode, toolName) {
|
||||
var toolGroupIds = [ctToolGroupId, ptToolGroupId, fusionToolGroupId]
|
||||
var toolGroupIds = [ctToolGroupId, ptToolGroupId, fusionToolGroupId, mipToolGroupUID]
|
||||
toolGroupIds.forEach((toolGroupId) => {
|
||||
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
|
||||
if (!toolGroup || !toolGroup.hasTool(toolName)) return
|
||||
toolGroup.setToolPassive(toolName)
|
||||
if (mode === 'enabled') {
|
||||
toolGroup.setToolEnabled(toolName)
|
||||
|
|
@ -1627,148 +1818,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)
|
||||
var position = parseInt((left / maxLeft) * upper)
|
||||
|
||||
if (positionValue) {
|
||||
positionValue.textContent = position
|
||||
}
|
||||
this.upper = position
|
||||
|
||||
if (this.sliderTimeout) {
|
||||
cancelAnimationFrame(this.sliderTimeout)
|
||||
}
|
||||
this.sliderTimeout = requestAnimationFrame(() => {
|
||||
this.voiChange(position)
|
||||
})
|
||||
}
|
||||
})
|
||||
document.addEventListener('mouseup', () => {
|
||||
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)
|
||||
this.$refs[v] && this.$refs[v].setPreset(this.rgbPresetName)
|
||||
this.$refs[v] && this.$refs[v].setColorMap(rgbPresetName)
|
||||
|
||||
const viewport = (
|
||||
renderingEngine.getViewport(v)
|
||||
)
|
||||
|
||||
viewport.setProperties({ colormap: { name: rgbPresetName } }, ptVolumeId)
|
||||
viewport.render()
|
||||
})
|
||||
// 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 +2163,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
|
||||
|
|
|
|||
|
|
@ -172,6 +172,13 @@ export default {
|
|||
beforeDestroy() {
|
||||
},
|
||||
methods: {
|
||||
getQuestionFormRef(refName) {
|
||||
const ref = this.$refs[refName]
|
||||
if (!ref) {
|
||||
return null
|
||||
}
|
||||
return Array.isArray(ref) ? ref[0] : ref
|
||||
},
|
||||
handleReadingChart(e) {
|
||||
this.$emit('handleReadingChart', e)
|
||||
},
|
||||
|
|
@ -191,7 +198,8 @@ export default {
|
|||
this.tableQuestions.forEach(item => {
|
||||
item.TableQuestions.Answers.forEach(i => {
|
||||
var refName = `${item.Id}_${i.RowIndex}`
|
||||
this.$refs[refName] && this.$refs[refName][0].initForm(isRerender)
|
||||
const questionFormRef = this.getQuestionFormRef(refName)
|
||||
questionFormRef && questionFormRef.initForm(isRerender)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -271,7 +279,8 @@ export default {
|
|||
this.tableQuestions.forEach(item => {
|
||||
item.TableQuestions.Answers.forEach(i => {
|
||||
var refName = `${item.Id}_${i.RowIndex}`
|
||||
this.$refs[refName] && this.$refs[refName][0].initForm()
|
||||
const questionFormRef = this.getQuestionFormRef(refName)
|
||||
questionFormRef && questionFormRef.initForm()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -379,7 +388,8 @@ export default {
|
|||
this.tableQuestions.forEach(item => {
|
||||
item.TableQuestions.Answers.forEach(i => {
|
||||
var refName = `${item.Id}_${i.RowIndex}`
|
||||
this.$refs[refName] && this.$refs[refName][0].initForm()
|
||||
const questionFormRef = this.getQuestionFormRef(refName)
|
||||
questionFormRef && questionFormRef.initForm()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -391,7 +401,8 @@ export default {
|
|||
var rowIndex = String(this.unSaveTargets[0].rowIndex)
|
||||
var questionId = this.unSaveTargets[0].questionId
|
||||
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
|
||||
if (rowIndex === this.activeItem.activeRowIndex && questionId === this.activeItem.activeCollapseId && !this.$refs[refName][0].questionForm.OtherMeasureData) {
|
||||
const questionFormRef = this.getQuestionFormRef(refName)
|
||||
if (rowIndex === this.activeItem.activeRowIndex && questionId === this.activeItem.activeCollapseId && questionFormRef && !questionFormRef.questionForm.OtherMeasureData) {
|
||||
if (toolName === 'CircleROI') {
|
||||
return { isCanActiveTool: true, reason: '' }
|
||||
} else {
|
||||
|
|
@ -522,7 +533,8 @@ export default {
|
|||
if (item.TableQuestions && item.TableQuestions.Answers) {
|
||||
item.TableQuestions.Answers.map(t => {
|
||||
const refName = `${item.Id}_${t.RowIndex}`
|
||||
if (this.$refs[refName] && this.$refs[refName][0] && this.$refs[refName][0].questionForm.saveTypeEnum !== 2) {
|
||||
const questionFormRef = this.getQuestionFormRef(refName)
|
||||
if (questionFormRef && questionFormRef.questionForm.saveTypeEnum !== 2) {
|
||||
var lessionName = this.getLesionName(item.OrderMark, t.RowIndex)
|
||||
arr.push({ lessionName: lessionName, rowIndex: t.RowIndex, questionId: item.Id })
|
||||
}
|
||||
|
|
@ -552,11 +564,15 @@ export default {
|
|||
this.activeItem.activeCollapseId = arr[0]
|
||||
this.$nextTick(() => {
|
||||
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
|
||||
if (this.$refs[refName][0].questionForm.IsDicomReading !== false) {
|
||||
var markTool = 'CircleROI'
|
||||
var isMarked = !!this.$refs[refName][0].questionForm.OtherMeasureData
|
||||
const questionFormRef = this.getQuestionFormRef(refName)
|
||||
if (!questionFormRef) {
|
||||
return
|
||||
}
|
||||
FusionEvent.$emit('imageLocation', { otherMeasureData: this.$refs[refName][0].questionForm.OtherMeasureData, markTool, isMarked })
|
||||
if (questionFormRef.questionForm.IsDicomReading !== false) {
|
||||
var markTool = 'CircleROI'
|
||||
var isMarked = !!questionFormRef.questionForm.OtherMeasureData
|
||||
}
|
||||
FusionEvent.$emit('imageLocation', { otherMeasureData: questionFormRef.questionForm.OtherMeasureData, markTool, isMarked })
|
||||
})
|
||||
} else {
|
||||
this.activeItem.activeRowIndex = null
|
||||
|
|
@ -576,7 +592,10 @@ export default {
|
|||
},
|
||||
collapseRightClick(e, obj, activeCollapseId, activeRowIndex) {
|
||||
const refName = `${activeCollapseId}_${activeRowIndex}`
|
||||
FusionEvent.$emit('imageLocation', { otherMeasureData: this.$refs[refName][0].questionForm.OtherMeasureData })
|
||||
const questionFormRef = this.getQuestionFormRef(refName)
|
||||
if (questionFormRef) {
|
||||
FusionEvent.$emit('imageLocation', { otherMeasureData: questionFormRef.questionForm.OtherMeasureData })
|
||||
}
|
||||
|
||||
e.stopImmediatePropagation()
|
||||
e.stopPropagation()
|
||||
|
|
@ -621,8 +640,12 @@ export default {
|
|||
this.activeName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
|
||||
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
|
||||
this.$nextTick(() => {
|
||||
const questionFormRef = this.getQuestionFormRef(refName)
|
||||
if (!questionFormRef) {
|
||||
return
|
||||
}
|
||||
if (deleteInfo) {
|
||||
this.$refs[refName][0].setDeleteInfo(deleteInfo)
|
||||
questionFormRef.setDeleteInfo(deleteInfo)
|
||||
}
|
||||
if (questionsObj.otherMeasureData) {
|
||||
const measureData = {}
|
||||
|
|
@ -631,7 +654,7 @@ export default {
|
|||
measureData.type = questionsObj.otherMeasureData.metadata.toolName
|
||||
measureData.suvMax = questionsObj.suvMax
|
||||
|
||||
this.$refs[refName][0].setMeasureData(measureData)
|
||||
questionFormRef.setMeasureData(measureData)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
|
@ -719,21 +742,28 @@ export default {
|
|||
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)
|
||||
const questionFormRef = this.getQuestionFormRef(refName)
|
||||
if (questionFormRef) {
|
||||
questionFormRef.setMeasureData(measureObj.measureData)
|
||||
}
|
||||
}
|
||||
},
|
||||
clearMeasuredData() {
|
||||
if (this.activeItem.activeCollapseId && this.activeItem.activeRowIndex && this.activeName) {
|
||||
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
|
||||
this.$refs[refName][0].clearMeasurement()
|
||||
const questionFormRef = this.getQuestionFormRef(refName)
|
||||
if (questionFormRef) {
|
||||
questionFormRef.clearMeasurement()
|
||||
}
|
||||
}
|
||||
},
|
||||
setOutsideMeasuredData(annotation) {
|
||||
if (this.activeItem.activeCollapseId && this.activeItem.activeRowIndex && this.activeName) {
|
||||
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
|
||||
this.$refs[refName][0].setOutsideMeasuredData(annotation)
|
||||
const questionFormRef = this.getQuestionFormRef(refName)
|
||||
if (questionFormRef) {
|
||||
questionFormRef.setOutsideMeasuredData(annotation)
|
||||
}
|
||||
}
|
||||
},
|
||||
// 设置测量数据
|
||||
|
|
@ -779,8 +809,22 @@ export default {
|
|||
// 判断是否存在测量数据
|
||||
this.$nextTick(() => {
|
||||
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
|
||||
if (!this.$refs[refName][0].questionForm.OtherMeasureData) {
|
||||
this.$refs[refName][0].setMeasureData(measureData)
|
||||
const questionFormRef = this.getQuestionFormRef(refName)
|
||||
if (!questionFormRef) {
|
||||
if (this.isBaseLineTask) {
|
||||
var idx = this.tableQuestions.findIndex(item => item.LesionType === 0)
|
||||
if (this.tableQuestions[idx].TableQuestions.Answers.length < this.tableQuestions[idx].MaxQuestionCount && (measureData.type === 'CircleROI')) {
|
||||
this.createTTarget(measureData)
|
||||
} else {
|
||||
this.createNTTarget(measureData)
|
||||
}
|
||||
} else {
|
||||
this.createNLTarget(measureData)
|
||||
}
|
||||
return
|
||||
}
|
||||
if (!questionFormRef.questionForm.OtherMeasureData) {
|
||||
questionFormRef.setMeasureData(measureData)
|
||||
} else {
|
||||
if (this.isBaseLineTask) {
|
||||
var idx = this.tableQuestions.findIndex(item => item.LesionType === 0)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -79,7 +81,7 @@
|
|||
{{ markers.left }}
|
||||
</div>
|
||||
<div v-if="presetName" class="color_bar">
|
||||
<canvas id="colorBar_Canvas" />
|
||||
<canvas ref="colorBarCanvas" />
|
||||
</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)" />
|
||||
|
|
@ -135,6 +137,12 @@ export default {
|
|||
return {}
|
||||
}
|
||||
},
|
||||
ctSeriesInfo: {
|
||||
type: Object,
|
||||
default() {
|
||||
return null
|
||||
}
|
||||
},
|
||||
renderingEngineId: {
|
||||
type: String,
|
||||
required: true
|
||||
|
|
@ -154,6 +162,10 @@ export default {
|
|||
default() {
|
||||
return []
|
||||
}
|
||||
},
|
||||
rgbPresetName: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
|
|
@ -195,12 +207,21 @@ export default {
|
|||
handler(v) {
|
||||
console.log('activeIndex ', v)
|
||||
}
|
||||
},
|
||||
rgbPresetName: {
|
||||
immediate: true,
|
||||
handler(v) {
|
||||
this.presetName = v || ''
|
||||
if (!this.presetName) return
|
||||
this.$nextTick(() => {
|
||||
this.renderColorBar(this.presetName)
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
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)
|
||||
|
|
@ -265,6 +286,24 @@ export default {
|
|||
this.renderColorBar(this.presetName)
|
||||
}
|
||||
}
|
||||
|
||||
if (this.index !== 4 && viewport) {
|
||||
const width = viewport.element.clientWidth
|
||||
const height = viewport.element.clientHeight
|
||||
let worldPoint = null
|
||||
if (width && height && viewport.canvasToWorld) {
|
||||
worldPoint = viewport.canvasToWorld([width / 2, height / 2])
|
||||
}
|
||||
if ((!worldPoint || worldPoint.length < 3) && viewport.getCamera) {
|
||||
worldPoint = viewport.getCamera()?.focalPoint
|
||||
}
|
||||
if (Array.isArray(worldPoint) && worldPoint.length >= 3) {
|
||||
this.$emit('fusionSlicePointChange', {
|
||||
viewportId: this.viewportId,
|
||||
worldPoint: [...worldPoint]
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
getOrientationMarker() {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
|
|
@ -328,7 +367,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 +377,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) {
|
||||
|
|
@ -545,7 +588,8 @@ export default {
|
|||
colorMap = getColormap(presetName)
|
||||
}
|
||||
const rgbPoints = colorMap.RGBPoints
|
||||
const canvas = document.getElementById('colorBar_Canvas')
|
||||
const canvas = this.$refs.colorBarCanvas
|
||||
if (!canvas) return
|
||||
const ctx = canvas.getContext('2d')
|
||||
const canvasWidth = 160
|
||||
const canvasHeight = 5
|
||||
|
|
@ -567,10 +611,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 +633,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>
|
||||
|
|
@ -1,20 +1,38 @@
|
|||
import { metaData } from '@cornerstonejs/core'
|
||||
// import { InstanceMetadata } from '@cornerstonejs/calculate-suv'
|
||||
import cornerstoneDICOMImageLoader from '@cornerstonejs/dicom-image-loader'
|
||||
import { getPTClinicalDataForInstance } from '@/utils/ptClinicalDataCache'
|
||||
|
||||
function parseImageId(imageId) {
|
||||
// build a url by parsing out the url scheme and frame index from the imageId
|
||||
const firstColonIndex = imageId.indexOf(':')
|
||||
|
||||
let url = imageId.substring(firstColonIndex + 1)
|
||||
const frameIndex = url.indexOf('frame=')
|
||||
|
||||
const urlPart = imageId.substring(firstColonIndex + 1)
|
||||
let url = urlPart
|
||||
let frame
|
||||
const qIndex = urlPart.indexOf('?')
|
||||
|
||||
if (frameIndex !== -1) {
|
||||
const frameStr = url.substr(frameIndex + 6)
|
||||
if (qIndex !== -1) {
|
||||
const base = urlPart.slice(0, qIndex)
|
||||
const query = urlPart.slice(qIndex + 1)
|
||||
const parts = query.split('&').filter(Boolean)
|
||||
const preservedParts = []
|
||||
|
||||
frame = parseInt(frameStr, 10)
|
||||
url = url.substr(0, frameIndex - 1)
|
||||
parts.forEach((part) => {
|
||||
const eqIndex = part.indexOf('=')
|
||||
const key = eqIndex === -1 ? part : part.slice(0, eqIndex)
|
||||
const value = eqIndex === -1 ? '' : part.slice(eqIndex + 1)
|
||||
|
||||
if (key === 'frame') {
|
||||
const n = value !== '' ? parseInt(value, 10) : NaN
|
||||
if (Number.isFinite(n)) {
|
||||
frame = n
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
preservedParts.push(part)
|
||||
})
|
||||
|
||||
url = preservedParts.length ? `${base}?${preservedParts.join('&')}` : base
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
@ -23,6 +41,7 @@ function parseImageId(imageId) {
|
|||
frame
|
||||
}
|
||||
}
|
||||
|
||||
function getMetaData(type, imageId) {
|
||||
// const { dicomParser } = cornerstoneDICOMImageLoader.external
|
||||
const parsedImageId = parseImageId(imageId)
|
||||
|
|
@ -32,6 +51,7 @@ function getMetaData(type, imageId) {
|
|||
if (!dataSet) {
|
||||
return
|
||||
}
|
||||
|
||||
if (type === 'petImageModule') {
|
||||
// 1340137.4196974 240000
|
||||
// console.log(dataSet.string('x00541300'), dataSet.string('x00181242'))
|
||||
|
|
@ -41,173 +61,69 @@ function getMetaData(type, imageId) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default function getPTImageIdInstanceMetadata(imageId) {
|
||||
const instanceId = getInstanceIdFromImageId(imageId)
|
||||
const ptClinicalData = instanceId ? getPTClinicalDataForInstance(instanceId) : null
|
||||
const petSequenceModule = metaData.get('petIsotopeModule', imageId)
|
||||
const generalSeriesModule = metaData.get('generalSeriesModule', imageId) || {}
|
||||
const patientStudyModule = metaData.get('patientStudyModule', imageId) || {}
|
||||
const ptSeriesModule = metaData.get('petSeriesModule', imageId) || {}
|
||||
const ptImageModule = getMetaData('petImageModule', imageId) || {}
|
||||
const radiopharmaceuticalInfo = petSequenceModule?.radiopharmaceuticalInfo
|
||||
|
||||
const generalSeriesModule = metaData.get('generalSeriesModule', imageId)
|
||||
const patientStudyModule = metaData.get('patientStudyModule', imageId)
|
||||
|
||||
const ptSeriesModule = metaData.get('petSeriesModule', imageId)
|
||||
// const ptImageModule = metaData.get('petImageModule', imageId)
|
||||
|
||||
const ptImageModule = getMetaData('petImageModule', imageId)
|
||||
if (!petSequenceModule) {
|
||||
if (!radiopharmaceuticalInfo) {
|
||||
throw new Error('petSequenceModule metadata is required')
|
||||
}
|
||||
|
||||
const radiopharmaceuticalInfo = petSequenceModule.radiopharmaceuticalInfo
|
||||
|
||||
const { seriesDate, seriesTime, acquisitionDate, acquisitionTime } =
|
||||
generalSeriesModule
|
||||
var { patientWeight } = patientStudyModule
|
||||
// console.log('更改前:', patientWeight)
|
||||
// patientWeight = patientWeight * 10
|
||||
// console.log('更改后:', patientWeight)
|
||||
const { correctedImage, units, decayCorrection } = ptSeriesModule
|
||||
|
||||
if (
|
||||
seriesDate === undefined ||
|
||||
seriesTime === undefined ||
|
||||
patientWeight === undefined ||
|
||||
acquisitionDate === undefined ||
|
||||
acquisitionTime === undefined ||
|
||||
correctedImage === undefined ||
|
||||
units === undefined ||
|
||||
decayCorrection === undefined ||
|
||||
radiopharmaceuticalInfo.radionuclideTotalDose === undefined ||
|
||||
radiopharmaceuticalInfo.radionuclideHalfLife === undefined ||
|
||||
(radiopharmaceuticalInfo.radiopharmaceuticalStartDateTime === undefined &&
|
||||
seriesDate === undefined &&
|
||||
radiopharmaceuticalInfo.radiopharmaceuticalStartTime === undefined)
|
||||
//
|
||||
) {
|
||||
throw new Error('required metadata are missing')
|
||||
}
|
||||
const patientWeight = toNumber(
|
||||
ptClinicalData?.PatientWeight,
|
||||
patientStudyModule.patientWeight
|
||||
)
|
||||
const totalDose = toNumber(
|
||||
ptClinicalData?.RadionuclideTotalDose,
|
||||
radiopharmaceuticalInfo.radionuclideTotalDose
|
||||
)
|
||||
const halfLife = toNumber(
|
||||
ptClinicalData?.RadionuclideHalfLife,
|
||||
radiopharmaceuticalInfo.radionuclideHalfLife
|
||||
)
|
||||
const startTime = firstValue(
|
||||
normalizeDicomTime(ptClinicalData?.RadiopharmaceuticalStartTime),
|
||||
toDicomTimeString(radiopharmaceuticalInfo.radiopharmaceuticalStartTime)
|
||||
)
|
||||
const acquisitionTime = firstValue(
|
||||
normalizeDicomTime(ptClinicalData?.AcquisitionTime),
|
||||
toDicomTimeString(generalSeriesModule.acquisitionTime)
|
||||
)
|
||||
|
||||
const instanceMetadata = {
|
||||
CorrectedImage: correctedImage,
|
||||
Units: units,
|
||||
RadionuclideHalfLife: radiopharmaceuticalInfo.radionuclideHalfLife,
|
||||
RadionuclideTotalDose: radiopharmaceuticalInfo.radionuclideTotalDose,
|
||||
DecayCorrection: decayCorrection,
|
||||
CorrectedImage: ptSeriesModule.correctedImage,
|
||||
Units: ptSeriesModule.units,
|
||||
RadionuclideHalfLife: halfLife,
|
||||
RadionuclideTotalDose: totalDose,
|
||||
DecayCorrection: ptSeriesModule.decayCorrection,
|
||||
PatientWeight: patientWeight,
|
||||
SeriesDate: seriesDate,
|
||||
SeriesTime: seriesTime,
|
||||
AcquisitionDate: acquisitionDate,
|
||||
SeriesDate: toDicomDateString(generalSeriesModule.seriesDate),
|
||||
SeriesTime: toDicomTimeString(generalSeriesModule.seriesTime),
|
||||
AcquisitionDate: toDicomDateString(generalSeriesModule.acquisitionDate),
|
||||
AcquisitionTime: acquisitionTime
|
||||
}
|
||||
|
||||
if (
|
||||
radiopharmaceuticalInfo.radiopharmaceuticalStartDateTime &&
|
||||
radiopharmaceuticalInfo.radiopharmaceuticalStartDateTime !== undefined &&
|
||||
typeof radiopharmaceuticalInfo.radiopharmaceuticalStartDateTime === 'string'
|
||||
) {
|
||||
instanceMetadata.RadiopharmaceuticalStartDateTime =
|
||||
radiopharmaceuticalInfo.radiopharmaceuticalStartDateTime
|
||||
}
|
||||
|
||||
if (
|
||||
radiopharmaceuticalInfo.radiopharmaceuticalStartDateTime &&
|
||||
radiopharmaceuticalInfo.radiopharmaceuticalStartDateTime !== undefined &&
|
||||
typeof radiopharmaceuticalInfo.radiopharmaceuticalStartDateTime !== 'string'
|
||||
) {
|
||||
const dateString = convertInterfaceDateToString(
|
||||
radiopharmaceuticalInfo.radiopharmaceuticalStartDateTime
|
||||
assignIfPresent(
|
||||
instanceMetadata,
|
||||
'RadiopharmaceuticalStartDateTime',
|
||||
toDicomDateString(radiopharmaceuticalInfo.radiopharmaceuticalStartDateTime)
|
||||
)
|
||||
instanceMetadata.RadiopharmaceuticalStartDateTime = dateString
|
||||
}
|
||||
|
||||
if (
|
||||
instanceMetadata.AcquisitionDate &&
|
||||
instanceMetadata.AcquisitionDate !== undefined &&
|
||||
typeof instanceMetadata.AcquisitionDate !== 'string'
|
||||
) {
|
||||
const dateString = convertInterfaceDateToString(
|
||||
instanceMetadata.AcquisitionDate
|
||||
assignIfPresent(instanceMetadata, 'RadiopharmaceuticalStartTime', startTime)
|
||||
assignIfPresent(instanceMetadata, 'FrameReferenceTime', ptImageModule.frameReferenceTime)
|
||||
assignIfPresent(instanceMetadata, 'ActualFrameDuration', ptImageModule.actualFrameDuration)
|
||||
assignIfPresent(
|
||||
instanceMetadata,
|
||||
'PatientSex',
|
||||
firstValue(ptClinicalData?.PatientSex, patientStudyModule.patientSex)
|
||||
)
|
||||
instanceMetadata.AcquisitionDate = dateString
|
||||
}
|
||||
|
||||
if (
|
||||
instanceMetadata.SeriesDate &&
|
||||
instanceMetadata.SeriesDate !== undefined &&
|
||||
typeof instanceMetadata.SeriesDate !== 'string'
|
||||
) {
|
||||
const dateString = convertInterfaceDateToString(
|
||||
instanceMetadata.SeriesDate
|
||||
)
|
||||
instanceMetadata.SeriesDate = dateString
|
||||
}
|
||||
|
||||
if (
|
||||
radiopharmaceuticalInfo.radiopharmaceuticalStartTime &&
|
||||
radiopharmaceuticalInfo.radiopharmaceuticalStartTime !== undefined &&
|
||||
typeof radiopharmaceuticalInfo.radiopharmaceuticalStartTime === 'string'
|
||||
) {
|
||||
instanceMetadata.RadiopharmaceuticalStartTime =
|
||||
radiopharmaceuticalInfo.radiopharmaceuticalStartTime
|
||||
}
|
||||
|
||||
if (
|
||||
radiopharmaceuticalInfo.radiopharmaceuticalStartTime &&
|
||||
radiopharmaceuticalInfo.radiopharmaceuticalStartTime !== undefined &&
|
||||
typeof radiopharmaceuticalInfo.radiopharmaceuticalStartTime !== 'string'
|
||||
) {
|
||||
const timeString = convertInterfaceTimeToString(
|
||||
radiopharmaceuticalInfo.radiopharmaceuticalStartTime
|
||||
)
|
||||
instanceMetadata.RadiopharmaceuticalStartTime = timeString
|
||||
}
|
||||
|
||||
if (
|
||||
instanceMetadata.AcquisitionTime &&
|
||||
instanceMetadata.AcquisitionTime !== undefined &&
|
||||
typeof instanceMetadata.AcquisitionTime !== 'string'
|
||||
) {
|
||||
const timeString = convertInterfaceTimeToString(
|
||||
instanceMetadata.AcquisitionTime
|
||||
)
|
||||
instanceMetadata.AcquisitionTime = timeString
|
||||
}
|
||||
|
||||
if (
|
||||
instanceMetadata.SeriesTime &&
|
||||
instanceMetadata.SeriesTime !== undefined &&
|
||||
typeof instanceMetadata.SeriesTime !== 'string'
|
||||
) {
|
||||
const timeString = convertInterfaceTimeToString(
|
||||
instanceMetadata.SeriesTime
|
||||
)
|
||||
instanceMetadata.SeriesTime = timeString
|
||||
}
|
||||
|
||||
if (
|
||||
ptImageModule.frameReferenceTime &&
|
||||
ptImageModule.frameReferenceTime !== undefined
|
||||
) {
|
||||
instanceMetadata.FrameReferenceTime = ptImageModule.frameReferenceTime
|
||||
}
|
||||
|
||||
if (
|
||||
ptImageModule.actualFrameDuration &&
|
||||
ptImageModule.actualFrameDuration !== undefined
|
||||
) {
|
||||
instanceMetadata.ActualFrameDuration = ptImageModule.actualFrameDuration
|
||||
}
|
||||
|
||||
if (
|
||||
patientStudyModule.patientSex &&
|
||||
patientStudyModule.patientSex !== undefined
|
||||
) {
|
||||
instanceMetadata.PatientSex = patientStudyModule.patientSex
|
||||
}
|
||||
|
||||
if (
|
||||
patientStudyModule.patientSize &&
|
||||
patientStudyModule.patientSize !== undefined
|
||||
) {
|
||||
instanceMetadata.PatientSize = patientStudyModule.patientSize
|
||||
}
|
||||
assignIfPresent(instanceMetadata, 'PatientSize', patientStudyModule.patientSize)
|
||||
|
||||
return instanceMetadata
|
||||
}
|
||||
|
|
@ -222,15 +138,81 @@ function convertInterfaceTimeToString(time) {
|
|||
'0'
|
||||
)
|
||||
|
||||
const timeString = `${hours}${minutes}${seconds}.${fractionalSeconds}`
|
||||
return timeString
|
||||
return `${hours}${minutes}${seconds}.${fractionalSeconds}`
|
||||
}
|
||||
|
||||
function convertInterfaceDateToString(date) {
|
||||
const month = `${date.month}`.padStart(2, '0')
|
||||
const day = `${date.day}`.padStart(2, '0')
|
||||
const dateString = `${date.year}${month}${day}`
|
||||
return dateString
|
||||
return `${date.year}${month}${day}`
|
||||
}
|
||||
|
||||
export { getPTImageIdInstanceMetadata }
|
||||
|
||||
function getInstanceIdFromImageId(imageId) {
|
||||
try {
|
||||
const qIndex = imageId.indexOf('?')
|
||||
if (qIndex === -1) return null
|
||||
const params = new URLSearchParams(imageId.slice(qIndex + 1))
|
||||
const instanceId = params.get('instanceId')
|
||||
if (!instanceId) return null
|
||||
return String(instanceId).trim() || null
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeDicomTime(value) {
|
||||
if (!hasValue(value)) return null
|
||||
if (typeof value === 'object') {
|
||||
return convertInterfaceTimeToString(value)
|
||||
}
|
||||
const raw = String(value).trim()
|
||||
if (!raw) return null
|
||||
|
||||
if (raw.includes(':')) {
|
||||
const parts = raw.split(':')
|
||||
const hh = `${parts[0] || '00'}`.padStart(2, '0')
|
||||
const mm = `${parts[1] || '00'}`.padStart(2, '0')
|
||||
const ss = `${parts[2] || '00'}`.padStart(2, '0')
|
||||
return `${hh}${mm}${ss}.000000`
|
||||
}
|
||||
|
||||
const cleaned = raw.replace(/[^\d.]/g, '')
|
||||
if (!cleaned) return null
|
||||
const [baseRaw, fracRaw] = cleaned.split('.')
|
||||
const base = `${baseRaw || ''}`.padStart(6, '0').slice(-6)
|
||||
const frac = `${fracRaw || ''}`.padEnd(6, '0').slice(0, 6)
|
||||
return `${base}.${frac}`
|
||||
}
|
||||
|
||||
function hasValue(value) {
|
||||
return value !== undefined && value !== null && value !== ''
|
||||
}
|
||||
|
||||
function firstValue(...values) {
|
||||
return values.find(hasValue)
|
||||
}
|
||||
|
||||
function toNumber(...values) {
|
||||
const value = firstValue(...values)
|
||||
if (!hasValue(value)) return value
|
||||
|
||||
const numberValue = Number(value)
|
||||
return Number.isFinite(numberValue) ? numberValue : value
|
||||
}
|
||||
|
||||
function toDicomTimeString(value) {
|
||||
if (!hasValue(value)) return undefined
|
||||
return typeof value === 'string' ? value : convertInterfaceTimeToString(value)
|
||||
}
|
||||
|
||||
function toDicomDateString(value) {
|
||||
if (!hasValue(value)) return undefined
|
||||
return typeof value === 'string' ? value : convertInterfaceDateToString(value)
|
||||
}
|
||||
|
||||
function assignIfPresent(target, key, value) {
|
||||
if (!hasValue(value)) return
|
||||
target[key] = value
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,31 @@
|
|||
import cornerstoneDICOMImageLoader from '@cornerstonejs/dicom-image-loader'
|
||||
import cornerstoneDICOMImageLoader from "@cornerstonejs/dicom-image-loader";
|
||||
import {
|
||||
getImageTypeSubItemFromDataset,
|
||||
extractOrientationFromDataset,
|
||||
extractPositionFromDataset,
|
||||
extractSpacingFromDataset,
|
||||
extractSliceThicknessFromDataset,
|
||||
} from "@/utils/extractPositioningFromDataset";
|
||||
import getNumberValues from "@/utils/getNumberValues";
|
||||
import isNMReconstructable from "@/utils/isNMReconstructable";
|
||||
function getSpacingBetweenSlices(dataSet) {
|
||||
if (dataSet?.elements?.x00180088) {
|
||||
return dataSet.floatString("x00180088");
|
||||
}
|
||||
const pixelMeasuresSequence = dataSet?.elements?.x00289110;
|
||||
if (
|
||||
pixelMeasuresSequence?.items?.length &&
|
||||
pixelMeasuresSequence.items[0]?.dataSet?.elements?.x00180088
|
||||
) {
|
||||
return pixelMeasuresSequence.items[0].dataSet.floatString("x00180088");
|
||||
}
|
||||
}
|
||||
function parseImageId(imageId) {
|
||||
// build a url by parsing out the url scheme and frame index from the imageId
|
||||
const firstColonIndex = imageId.indexOf(':');
|
||||
const firstColonIndex = imageId.indexOf(":");
|
||||
|
||||
let url = imageId.substring(firstColonIndex + 1);
|
||||
const frameIndex = url.indexOf('frame=');
|
||||
const frameIndex = url.indexOf("frame=");
|
||||
|
||||
let frame;
|
||||
|
||||
|
|
@ -21,127 +42,112 @@ function parseImageId(imageId) {
|
|||
frame,
|
||||
};
|
||||
}
|
||||
function getNumberValues(dataSet, tag, minimumLength) {
|
||||
const values = [];
|
||||
const valueAsString = dataSet.string(tag);
|
||||
|
||||
if (!valueAsString) {
|
||||
function metaDataProvider(type, imageId) {
|
||||
const parsedImageId = parseImageId(imageId);
|
||||
const dataSet = cornerstoneDICOMImageLoader.wadouri.dataSetCacheManager.get(
|
||||
parsedImageId.url
|
||||
);
|
||||
if (!dataSet) {
|
||||
return;
|
||||
}
|
||||
const split = valueAsString.split('\\');
|
||||
if (type === "imagePlaneModule") {
|
||||
const frameIndex = parsedImageId.frame !== undefined ? parsedImageId.frame - 1 : undefined;
|
||||
// const imageOrientationPatient = extractOrientationFromDataset(dataSet, frameIndex);
|
||||
// let imagePositionPatient = extractPositionFromDataset(dataSet, frameIndex);
|
||||
// const pixelSpacing = extractSpacingFromDataset(dataSet, frameIndex);
|
||||
// const sliceThickness = extractSliceThicknessFromDataset(dataSet, frameIndex);
|
||||
const imageOrientationPatient = extractOrientationFromDataset(dataSet);
|
||||
let imagePositionPatient = extractPositionFromDataset(dataSet);
|
||||
const pixelSpacing = extractSpacingFromDataset(dataSet);
|
||||
const sliceThickness = extractSliceThicknessFromDataset(dataSet);
|
||||
|
||||
if (minimumLength && split.length < minimumLength) {
|
||||
return;
|
||||
const modality = dataSet.string("x00080060");
|
||||
if (
|
||||
modality &&
|
||||
modality.includes("NM") &&
|
||||
parsedImageId.frame !== undefined &&
|
||||
parsedImageId.frame > 1
|
||||
) {
|
||||
// 坐标堆叠 (位置相同) :NM 多帧 DICOM 的空间位置 ( imagePositionPatient ) 通常只存在于 Detector Information Sequence 序列的第一个 Item 中。原来的代码在提取位置时,所有帧都被赋予了第一帧的位置。因为所有帧都挤在一个坐标上,3D 引擎无法根据厚度和法向量空间展开它们,导致间距 (Spacing) 为 0
|
||||
// 当识别到 NM 多帧图像且属于多帧切片时,通过 RowCosines 与 ColumnCosines 的叉乘计算出切片的 法向量(Normal) ,并结合 帧索引 (frameIndex) 以及 Spacing Between Slices (0018,0088)(层间距) 或者 Slice Thickness (0018,0050)(层厚) ,自动推算补全后续每一帧的 imagePositionPatient 真实三维坐标
|
||||
//
|
||||
if (
|
||||
imageOrientationPatient &&
|
||||
imagePositionPatient &&
|
||||
sliceThickness !== undefined
|
||||
) {
|
||||
const spacingBetweenSlices = getSpacingBetweenSlices(dataSet);
|
||||
const step =
|
||||
spacingBetweenSlices !== undefined ? spacingBetweenSlices : sliceThickness;
|
||||
const rowCosines = [
|
||||
parseFloat(imageOrientationPatient[0]),
|
||||
parseFloat(imageOrientationPatient[1]),
|
||||
parseFloat(imageOrientationPatient[2]),
|
||||
];
|
||||
const columnCosines = [
|
||||
parseFloat(imageOrientationPatient[3]),
|
||||
parseFloat(imageOrientationPatient[4]),
|
||||
parseFloat(imageOrientationPatient[5]),
|
||||
];
|
||||
const normal = [
|
||||
rowCosines[1] * columnCosines[2] - rowCosines[2] * columnCosines[1],
|
||||
rowCosines[2] * columnCosines[0] - rowCosines[0] * columnCosines[2],
|
||||
rowCosines[0] * columnCosines[1] - rowCosines[1] * columnCosines[0],
|
||||
];
|
||||
|
||||
const offset = frameIndex * parseFloat(step);
|
||||
|
||||
imagePositionPatient = [
|
||||
parseFloat(imagePositionPatient[0]) + normal[0] * offset,
|
||||
parseFloat(imagePositionPatient[1]) + normal[1] * offset,
|
||||
parseFloat(imagePositionPatient[2]) + normal[2] * offset,
|
||||
];
|
||||
}
|
||||
for (let i = 0; i < split.length; i++) {
|
||||
values.push(parseFloat(split[i]));
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
function getImageTypeSubItemFromDataset(dataSet, index) {
|
||||
const imageType = dataSet.string('x00080008');
|
||||
if (imageType) {
|
||||
const subTypes = imageType.split('\\');
|
||||
if (subTypes.length > index) {
|
||||
return subTypes[index];
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
function isNMReconstructable(imageSubType) {
|
||||
return imageSubType === 'RECON TOMO' || imageSubType === 'RECON GATED TOMO';
|
||||
}
|
||||
function extractOrientationFromNMMultiframeDataset(dataSet) {
|
||||
let imageOrientationPatient;
|
||||
const modality = dataSet.string('x00080060');
|
||||
if (modality?.includes('NM')) {
|
||||
const imageSubType = getImageTypeSubItemFromDataset(dataSet, 2);
|
||||
if (imageSubType && isNMReconstructable(imageSubType)) {
|
||||
if (dataSet.elements.x00540022) {
|
||||
imageOrientationPatient = getNumberValues(dataSet.elements.x00540022.items[0].dataSet, 'x00200037', 6);
|
||||
}
|
||||
}
|
||||
}
|
||||
return imageOrientationPatient;
|
||||
}
|
||||
function extractOrientationFromDataset(dataSet) {
|
||||
let imageOrientationPatient = getNumberValues(dataSet, 'x00200037', 6);
|
||||
if (!imageOrientationPatient && dataSet.elements.x00209116) {
|
||||
imageOrientationPatient = getNumberValues(dataSet.elements.x00209116.items[0].dataSet, 'x00200037', 6);
|
||||
}
|
||||
if (!imageOrientationPatient) {
|
||||
imageOrientationPatient =
|
||||
extractOrientationFromNMMultiframeDataset(dataSet);
|
||||
}
|
||||
return imageOrientationPatient;
|
||||
}
|
||||
function extractPositionFromNMMultiframeDataset(dataSet) {
|
||||
let imagePositionPatient;
|
||||
const modality = dataSet.string('x00080060');
|
||||
if (modality?.includes('NM')) {
|
||||
const imageSubType = getImageTypeSubItemFromDataset(dataSet, 2);
|
||||
if (imageSubType && isNMReconstructable(imageSubType)) {
|
||||
if (dataSet.elements.x00540022) {
|
||||
imagePositionPatient = getNumberValues(dataSet.elements.x00540022.items[0].dataSet, 'x00200032', 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
return imagePositionPatient;
|
||||
}
|
||||
function extractPositionFromDataset(dataSet) {
|
||||
let imagePositionPatient = getNumberValues(dataSet, 'x00200032', 3);
|
||||
if (!imagePositionPatient && dataSet.elements.x00209113) {
|
||||
imagePositionPatient = getNumberValues(dataSet.elements.x00209113.items[0].dataSet, 'x00200032', 3);
|
||||
}
|
||||
if (!imagePositionPatient) {
|
||||
imagePositionPatient = extractPositionFromNMMultiframeDataset(dataSet);
|
||||
}
|
||||
return imagePositionPatient;
|
||||
}
|
||||
function extractSliceThicknessFromDataset(dataSet) {
|
||||
let sliceThickness;
|
||||
if (dataSet.elements.x00180050) {
|
||||
sliceThickness = dataSet.floatString('x00180050');
|
||||
}
|
||||
else if (dataSet.elements.x00289110 &&
|
||||
dataSet.elements.x00289110.items.length &&
|
||||
dataSet.elements.x00289110.items[0].dataSet.elements.x00180050) {
|
||||
sliceThickness =
|
||||
dataSet.elements.x00289110.items[0].dataSet.floatString('x00180050');
|
||||
}
|
||||
return sliceThickness;
|
||||
}
|
||||
function extractSpacingFromDataset(dataSet) {
|
||||
let rowPixelSpacing = null
|
||||
let columnPixelSpacing = null
|
||||
let pixelSpacing = getNumberValues(dataSet, 'x00280030', 2);
|
||||
const imagePixelSpacing = getNumberValues(dataSet, 'x00181164', 2);
|
||||
const estimatedRadiographicMagnificationFactor = getNumberValues(dataSet, 'x00181114', 2);
|
||||
// const imageOrientationPatient = getNumberValues(dataSet, 'x00200037', 6);
|
||||
// const imagePositionPatient = getNumberValues(dataSet, 'x00200032', 3);
|
||||
// const pixelSpacing = extractSpacingFromDataset(dataSet);
|
||||
const imagePixelSpacing = getNumberValues(dataSet, "x00181164", 2);
|
||||
|
||||
// let rowCosines = null;
|
||||
|
||||
// let columnCosines = null;
|
||||
|
||||
// if (imageOrientationPatient) {
|
||||
// rowCosines = [
|
||||
// parseFloat(imageOrientationPatient[0]),
|
||||
// parseFloat(imageOrientationPatient[1]),
|
||||
// parseFloat(imageOrientationPatient[2]),
|
||||
// ];
|
||||
// columnCosines = [
|
||||
// parseFloat(imageOrientationPatient[3]),
|
||||
// parseFloat(imageOrientationPatient[4]),
|
||||
// parseFloat(imageOrientationPatient[5]),
|
||||
// ];
|
||||
// }
|
||||
const estimatedRadiographicMagnificationFactor = getNumberValues(
|
||||
dataSet,
|
||||
"x00181114",
|
||||
2
|
||||
);
|
||||
let columnPixelSpacing = null;
|
||||
|
||||
let rowPixelSpacing = null;
|
||||
|
||||
if (pixelSpacing) {
|
||||
rowPixelSpacing = pixelSpacing[0]
|
||||
columnPixelSpacing = pixelSpacing[1]
|
||||
rowPixelSpacing = pixelSpacing[0];
|
||||
columnPixelSpacing = pixelSpacing[1];
|
||||
} else if (imagePixelSpacing && estimatedRadiographicMagnificationFactor) {
|
||||
rowPixelSpacing = imagePixelSpacing[0] / estimatedRadiographicMagnificationFactor[0];
|
||||
columnPixelSpacing = imagePixelSpacing[1] / estimatedRadiographicMagnificationFactor[1];
|
||||
rowPixelSpacing =
|
||||
imagePixelSpacing[0] / estimatedRadiographicMagnificationFactor[0];
|
||||
columnPixelSpacing =
|
||||
imagePixelSpacing[1] / estimatedRadiographicMagnificationFactor[1];
|
||||
} else if (imagePixelSpacing && !estimatedRadiographicMagnificationFactor) {
|
||||
rowPixelSpacing = imagePixelSpacing[0];
|
||||
columnPixelSpacing = imagePixelSpacing[1];
|
||||
}
|
||||
return rowPixelSpacing !== null ? [rowPixelSpacing, columnPixelSpacing] : undefined
|
||||
}
|
||||
function metaDataProvider(type, imageId) {
|
||||
const parsedImageId = parseImageId(imageId);
|
||||
const dataSet = cornerstoneDICOMImageLoader.wadouri.dataSetCacheManager.get(parsedImageId.url)
|
||||
|
||||
|
||||
if (!dataSet) {
|
||||
return;
|
||||
}
|
||||
if (type === 'imagePlaneModule') {
|
||||
const imageOrientationPatient = getNumberValues(dataSet, 'x00200037', 6);
|
||||
const imagePositionPatient = getNumberValues(dataSet, 'x00200032', 3);
|
||||
const pixelSpacing = extractSpacingFromDataset(dataSet);
|
||||
|
||||
let rowCosines = null;
|
||||
|
||||
|
|
@ -160,33 +166,34 @@ function metaDataProvider(type, imageId) {
|
|||
];
|
||||
}
|
||||
return {
|
||||
frameOfReferenceUID: dataSet.string('x00200052'),
|
||||
rows: dataSet.uint16('x00280010'),
|
||||
columns: dataSet.uint16('x00280011'),
|
||||
frameOfReferenceUID: dataSet.string("x00200052"),
|
||||
rows: dataSet.uint16("x00280010"),
|
||||
columns: dataSet.uint16("x00280011"),
|
||||
imageOrientationPatient,
|
||||
rowCosines,
|
||||
columnCosines,
|
||||
imagePositionPatient,
|
||||
sliceThickness: dataSet.floatString('x00180050'),
|
||||
sliceLocation: dataSet.floatString('x00201041'),
|
||||
pixelSpacing: pixelSpacing,
|
||||
rowPixelSpacing: pixelSpacing ? pixelSpacing[0] : null,
|
||||
columnPixelSpacing: pixelSpacing ? pixelSpacing[1] : null,
|
||||
sliceThickness,
|
||||
sliceLocation: dataSet.floatString("x00201041"),
|
||||
pixelSpacing,
|
||||
rowPixelSpacing,
|
||||
columnPixelSpacing,
|
||||
};
|
||||
}
|
||||
if (type === 'nmMultiframeGeometryModule') {
|
||||
const modality = dataSet.string('x00080060');
|
||||
if (type === "nmMultiframeGeometryModule") {
|
||||
const modality = dataSet.string("x00080060");
|
||||
const imageSubType = getImageTypeSubItemFromDataset(dataSet, 2);
|
||||
return {
|
||||
modality,
|
||||
imageType: dataSet.string('x00080008'),
|
||||
imageType: dataSet.string("x00080008"),
|
||||
imageSubType,
|
||||
imageOrientationPatient: extractOrientationFromDataset(dataSet),
|
||||
imagePositionPatient: extractPositionFromDataset(dataSet),
|
||||
sliceThickness: extractSliceThicknessFromDataset(dataSet),
|
||||
pixelSpacing: extractSpacingFromDataset(dataSet),
|
||||
numberOfFrames: dataSet.uint16('x00280008'),
|
||||
isNMReconstructable: isNMReconstructable(imageSubType) && modality.includes('NM'),
|
||||
numberOfFrames: dataSet.uint16("x00280008"),
|
||||
isNMReconstructable:
|
||||
isNMReconstructable(imageSubType) && modality.includes("NM"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,14 +2,28 @@ import { utilities as csUtils } from '@cornerstonejs/core'
|
|||
|
||||
const scalingPerImageId = {}
|
||||
|
||||
function normalizeImageURI(imageURI) {
|
||||
if (!imageURI) return imageURI
|
||||
const qIndex = imageURI.indexOf('?')
|
||||
if (qIndex === -1) return imageURI
|
||||
const base = imageURI.slice(0, qIndex)
|
||||
const query = imageURI.slice(qIndex + 1)
|
||||
const params = new URLSearchParams(query)
|
||||
if (!params.has('frame')) return imageURI
|
||||
params.delete('frame')
|
||||
const rest = params.toString()
|
||||
return rest ? `${base}?${rest}` : base
|
||||
}
|
||||
|
||||
function addInstance(imageId, scalingMetaData) {
|
||||
const imageURI = csUtils.imageIdToURI(imageId)
|
||||
// 统一缩放元数据的 key:忽略 frame 参数,避免 getCurrentImageId() 带 frame 导致查不到 scalingModule
|
||||
const imageURI = normalizeImageURI(csUtils.imageIdToURI(imageId))
|
||||
scalingPerImageId[imageURI] = scalingMetaData
|
||||
}
|
||||
|
||||
function get(type, imageId) {
|
||||
if (type === 'scalingModule') {
|
||||
const imageURI = csUtils.imageIdToURI(imageId)
|
||||
const imageURI = normalizeImageURI(csUtils.imageIdToURI(imageId))
|
||||
return scalingPerImageId[imageURI]
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div v-loading="loading">
|
||||
<div class="base-dialog-body" style="height:380px;overflow-y: auto;">
|
||||
<div class="base-dialog-body" style="height:420px;overflow-y: auto;">
|
||||
<div class="hot-keys-container">
|
||||
<div v-for="(item, index) in hotKeyList" :key="`${item.tag}_${index}`" class="wrapper">
|
||||
<template>
|
||||
|
|
@ -60,7 +60,7 @@ export default {
|
|||
this.loading = true
|
||||
this.hotKeyList = []
|
||||
try {
|
||||
const res = await getDoctorShortcutKey({ imageToolType: this.readingTool })
|
||||
const res = await getDoctorShortcutKey({ imageToolType: this.readingTool === 1 ? 2 : 1 })
|
||||
if (res.IsSuccess) {
|
||||
res.Result.map(item => {
|
||||
this.hotKeyList.push({ id: item.Id, keys: { controlKey: { altKey: item.AltKey, ctrlKey: item.CtrlKey, shiftKey: item.ShiftKey, metaKey: item.MetaKey, key: item.Keyboardkey, code: item.Code }, text: item.Text }, label: item.ShortcutKeyEnum })
|
||||
|
|
|
|||