149 Commits

Author SHA1 Message Date
wangxiaoshuang b977f1ab1d Merge branch 'uat' into uat_us
# Conflicts:
#	src/views/trials/trials-layout/components/trialsNavbar.vue
#	src/views/trials/trials-panel/reading/read-task/index.vue
2026-08-21 11:38:52 +08:00
caiyiling a7dffe94a9 既往裁判阅片结果表优化 2026-08-20 13:58:22 +08:00
caiyiling d0f1e35e40 IR列表高度调整 2026-08-20 13:39:17 +08:00
caiyiling e9b0ec3c0f 1
continuous-integration/drone/push Build is passing
2026-08-20 10:11:43 +08:00
caiyiling 2691020e62 裁判阅片页面更改
continuous-integration/drone/push Build is passing
2026-08-20 09:44:16 +08:00
caiyiling f8e86e7b27 1
continuous-integration/drone/push Build is passing
2026-08-19 17:50:36 +08:00
caiyiling 120f5db62a 裁判规则配置在基线添加应用到随访的功能
continuous-integration/drone/push Build is passing
2026-08-19 10:57:54 +08:00
caiyiling 7f65376c94 样式调整
continuous-integration/drone/push Build is passing
2026-08-18 16:09:08 +08:00
caiyiling 53101bc811 Merge branch 'uat' of https://gitea.frp.extimaging.com/XCKJ/irc_web into uat
continuous-integration/drone/push Build is passing
2026-08-18 15:49:36 +08:00
caiyiling 8c931a2cbb 裁判规则及阅片更改 2026-08-18 15:49:10 +08:00
wangxiaoshuang a6c3ae8e05 IVUS&OCT阅片优化点:靶段标记和阅片页面数据下载和导入
continuous-integration/drone/push Build is passing
2026-08-18 09:32:10 +08:00
caiyiling e2b105cf39 阅片规则配置更改
continuous-integration/drone/push Build is pending
2026-08-17 17:18:54 +08:00
caiyiling de5c46c916 计划外访视裁判规则配置 2026-08-17 16:21:19 +08:00
caiyiling b2ea219acc Merge branch 'uat' of https://gitea.frp.extimaging.com/XCKJ/irc_web into uat
continuous-integration/drone/push Build is passing
2026-08-17 15:41:31 +08:00
caiyiling 2b04301d1c 裁判阅片及规则配置更改 2026-08-17 15:40:49 +08:00
wangxiaoshuang ee58ae4a22 非dicom影像阅片大小不对、无法移动问题解决
continuous-integration/drone/push Build is passing
2026-08-14 15:47:44 +08:00
wangxiaoshuang e497a31608 中心数据中三个表格添加调研状态字段和查询条件
continuous-integration/drone/push Build is passing
2026-08-14 13:24:36 +08:00
wangxiaoshuang 31c268450a 1
continuous-integration/drone/push Build is passing
2026-08-13 17:09:45 +08:00
wangxiaoshuang 3b69f5de32 国际化修改
continuous-integration/drone/push Build is passing
2026-08-13 17:00:12 +08:00
wangxiaoshuang ca5663d00a 中心设备表格样式调整
continuous-integration/drone/push Build is passing
2026-08-13 16:36:55 +08:00
wangxiaoshuang c85505c7b0 中心设备、中心基本信息导出 2026-08-13 15:45:40 +08:00
wangxiaoshuang 38ef9c9d58 1 2026-08-13 10:45:54 +08:00
wangxiaoshuang b7696cdf38 中心调研列表添加导出
continuous-integration/drone/push Build is passing
2026-08-13 10:27:23 +08:00
wangxiaoshuang 0c8ac0e011 中心调研日志添加国际化
continuous-integration/drone/push Build is passing
2026-08-13 09:39:06 +08:00
wangxiaoshuang a13bad3a1f 中心调研表格样式问题
continuous-integration/drone/push Build was killed
2026-08-12 17:41:43 +08:00
wangxiaoshuang 9f8cab4e50 中心调研操作日志添加字典
continuous-integration/drone/push Build is passing
2026-08-12 16:37:17 +08:00
wangxiaoshuang 9a748b0c8a 中心调研问题修改 2026-08-12 16:19:15 +08:00
wangxiaoshuang 9b596ce0ed 中心调研流程按钮控制 2026-08-12 14:41:54 +08:00
wangxiaoshuang 967688e94e 调研表历史日志预览
continuous-integration/drone/push Build is passing
2026-08-12 14:28:19 +08:00
caiyiling 0bbb93d663 中心调研更改
continuous-integration/drone/push Build is passing
2026-08-12 10:39:36 +08:00
wangxiaoshuang 9448805f36 靶段标注下载dicom文件目录变更
continuous-integration/drone/push Build is passing
2026-08-10 17:07:01 +08:00
caiyiling 5bbbd9334e ivus & oct 导表后自动保存逻辑更改
continuous-integration/drone/push Build is passing
2026-08-05 16:48:41 +08:00
caiyiling 9256970662 IVUS & OCT标准更改
continuous-integration/drone/push Build is passing
2026-08-05 09:08:46 +08:00
wangxiaoshuang c53890add7 质控dicom编辑检查类型报错修改
continuous-integration/drone/push Build is passing
2026-08-04 13:14:58 +08:00
wangxiaoshuang cbd860a35e 先下载再上传的逻辑指定IVUS和OCT标准
continuous-integration/drone/push Build is passing
2026-07-31 15:01:02 +08:00
wangxiaoshuang 7e9eb1f3a5 待阅列表屏蔽上传下载按钮
continuous-integration/drone/push Build is passing
2026-07-31 11:00:34 +08:00
wangxiaoshuang 254e43ac3d 分割球体工具bug修复
continuous-integration/drone/push Build is passing
2026-07-30 10:47:35 +08:00
caiyiling 197b3213e7 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main 2026-07-29 16:06:05 +08:00
caiyiling 3bfda749d8 质控预览非DICOM文件处理zip类型的数据 2026-07-29 16:05:06 +08:00
wangxiaoshuang 6f911d0d74 锁定蒙版添加白名单
continuous-integration/drone/push Build is passing
2026-07-29 15:53:17 +08:00
caiyiling 78f4a78994 测量数据预览按钮控制更改
continuous-integration/drone/push Build is passing
2026-07-29 09:53:43 +08:00
caiyiling 4e1911223d 1
continuous-integration/drone/push Build is passing
2026-07-28 09:17:40 +08:00
caiyiling 7dd02e84aa 样式调整
continuous-integration/drone/push Build is passing
2026-07-27 15:38:03 +08:00
caiyiling 7f260799d8 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-07-27 15:22:13 +08:00
caiyiling 06c7a8126b IVUS & OCT测量表格添加新开页签预览功能 2026-07-27 15:21:30 +08:00
wangxiaoshuang 78624d9cce 阅片中上传影像需要在下载影像后才能上传
continuous-integration/drone/push Build is passing
2026-07-27 14:28:55 +08:00
caiyiling 6ecc0e58e9 重置标记更改;标记定位更改
continuous-integration/drone/push Build is passing
2026-07-16 15:40:28 +08:00
wangxiaoshuang 84a81e1851 临床数据输入框禁用样式修改
continuous-integration/drone/push Build is passing
2026-07-16 14:12:04 +08:00
wangxiaoshuang b09209af31 完全随机分割问题绑定弹框不显示检查编号显示检查模态
continuous-integration/drone/push Build is passing
2026-07-16 13:30:48 +08:00
caiyiling 9892070ccc 播放功能修改
continuous-integration/drone/push Build is passing
2026-07-16 11:43:10 +08:00
wangxiaoshuang 63cbc8746f 完全随机融合弹框不显示检查号显示检查模态
continuous-integration/drone/push Build is passing
2026-07-16 10:54:55 +08:00
wangxiaoshuang fe64feac80 路由守卫判断条件修改
continuous-integration/drone/push Build was killed
2026-07-15 14:49:51 +08:00
wangxiaoshuang dce7fd9737 Merge branch 'uat'
continuous-integration/drone/push Build is passing
2026-07-14 17:54:02 +08:00
wangxiaoshuang 6187c585ad 接口频繁请求不提示
continuous-integration/drone/push Build is passing
2026-07-14 17:53:39 +08:00
wangxiaoshuang 22ac496d7b 用户编辑页添加国际化
continuous-integration/drone/push Build is passing
2026-07-14 11:50:46 +08:00
caiyiling 45c0e5030b Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-07-10 17:54:12 +08:00
caiyiling 36e23b8135 通知列表添加默认排序 2026-07-10 17:53:50 +08:00
wangxiaoshuang b530f68446 1
continuous-integration/drone/push Build is passing
2026-07-10 17:11:39 +08:00
wangxiaoshuang 383bfabae8 1
continuous-integration/drone/push Build is passing
2026-07-10 16:59:18 +08:00
wangxiaoshuang 196ed7f993 完全随机ir上传下载页面及预览页屏蔽受试者编号和项目编号
continuous-integration/drone/push Build is passing
2026-07-10 16:53:41 +08:00
caiyiling 70cc0027cd 受试者状态更改弹窗移除结束访视日期
continuous-integration/drone/push Build is passing
2026-07-10 16:21:44 +08:00
caiyiling 975b7d39a7 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-07-10 15:06:24 +08:00
caiyiling 21bcd1fddb 只有顺序阅片的时显示是否复制后续任务阅片表单 2026-07-10 15:05:39 +08:00
wangxiaoshuang 04bbde8583 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web 2026-07-10 13:55:06 +08:00
wangxiaoshuang f3c4bedbfd 趋势图提示红色改为蓝色 2026-07-10 13:54:54 +08:00
caiyiling 835aedc800 1
continuous-integration/drone/push Build is passing
2026-07-10 13:40:57 +08:00
caiyiling 9026b1d7fd 自定义标准完全随机阅片更改
continuous-integration/drone/push Build is passing
2026-07-10 13:12:44 +08:00
caiyiling b8f96efa66 列表调整
continuous-integration/drone/push Build is passing
2026-07-10 11:18:05 +08:00
caiyiling 4c4f68dbd0 完全随机阅片规则更改
continuous-integration/drone/push Build is passing
2026-07-10 11:12:55 +08:00
caiyiling 11c56d2f7a 1
continuous-integration/drone/push Build is passing
2026-07-09 16:52:58 +08:00
caiyiling a72521304e Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-07-09 16:33:50 +08:00
caiyiling 8baf6cb88f IR阅片模块相关页面添加任务名称搜索条件 2026-07-09 16:33:14 +08:00
wangxiaoshuang 71e04c3bf1 中心调研移动端确认弹框样式修改
continuous-integration/drone/push Build is passing
2026-07-09 15:17:44 +08:00
caiyiling 1af9b48622 完全随机IR阅片列表及阅片页面更改
continuous-integration/drone/push Build is pending
2026-07-09 14:17:02 +08:00
wangxiaoshuang 447b74fa55 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2026-07-09 13:29:51 +08:00
wangxiaoshuang c633fbcf87 外层问题配置为表格问题不显示时阅片时不展示表格 2026-07-09 13:29:45 +08:00
caiyiling 1cc57ad1d6 修复保存申请重阅表单异常问题
continuous-integration/drone/push Build is passing
2026-07-09 09:30:19 +08:00
caiyiling f282fdfabc 是否复制后续表单去除标准限制
continuous-integration/drone/push Build is passing
2026-07-08 16:46:50 +08:00
caiyiling d77c6ecf03 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-07-08 16:04:27 +08:00
caiyiling 1db75cf4f7 MRI-PDFF报告页数据更改 2026-07-08 16:03:55 +08:00
wangxiaoshuang 7bc70bfd6c 【中心调研】中心调研表手机扫描二维码进入时,同pc一样也需要增加验证。上传文件功能也要能正常使用
continuous-integration/drone/push Build is passing
2026-07-07 17:35:33 +08:00
wangxiaoshuang 69ba30e5a9 【趋势图】趋势图访视结果后增加查看按钮,可以查看已阅结果
continuous-integration/drone/push Build is passing
2026-07-07 10:25:32 +08:00
wangxiaoshuang 18482ddaa9 【中心调研】选择项目方案号生成验证码,项目方案号中有特殊符号,生成的验证码中未显示特殊符号
continuous-integration/drone/push Build is passing
2026-07-07 09:38:14 +08:00
caiyiling 14e2afcfee Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is pending
2026-07-06 17:22:30 +08:00
caiyiling 39dadbbf47 受试者任务状态变更原因更改 2026-07-06 17:20:59 +08:00
wangxiaoshuang d0ba340fea 去除分割上传文件按钮
continuous-integration/drone/push Build is passing
2026-07-06 11:44:37 +08:00
wangxiaoshuang 8d4ba686e8 Merge branch 'uat' into uat_us
# Conflicts:
#	src/App.vue
2026-06-05 15:34:09 +08:00
wangxiaoshuang cebfc0b88d Merge branch 'main' into uat_us 2026-04-30 17:36:36 +08:00
wangxiaoshuang 88b79e4aa1 Merge branch 'main' into uat_us 2026-04-30 17:14:39 +08:00
wangxiaoshuang 3bf3503c99 Merge branch 'main' into uat_us 2026-04-30 16:33:38 +08:00
wangxiaoshuang a969eabc44 1 2026-04-30 09:06:22 +08:00
wangxiaoshuang 7dbade1527 Merge branch 'main' into uat_us 2026-04-29 18:08:01 +08:00
wangxiaoshuang a628d75217 1 2026-04-29 09:32:10 +08:00
wangxiaoshuang 577bbcd182 Merge branch 'main' into uat_us 2026-04-28 15:42:05 +08:00
wangxiaoshuang c580c70bd0 1 2026-04-27 16:47:59 +08:00
wangxiaoshuang c339adb73f Merge branch 'main' into uat_us
# Conflicts:
#	src/views/trials/trials-panel/reading/dicoms3D/components/customize/ReportPage.vue
2026-04-27 16:47:23 +08:00
wangxiaoshuang fe5b1a805b 1 2026-04-22 15:08:09 +08:00
wangxiaoshuang 4a93aac236 1 2026-04-22 11:54:36 +08:00
wangxiaoshuang e972179753 Merge branch 'main' into uat_us 2026-04-17 11:49:14 +08:00
wangxiaoshuang a3e79db023 Merge branch 'main' into uat_us 2026-04-17 10:01:16 +08:00
wangxiaoshuang 3e454a75d6 双屏预览文档修改 2026-04-02 13:51:10 +08:00
wangxiaoshuang 79d59cbc4a Merge branch 'main' into uat_us 2026-04-02 09:33:50 +08:00
wangxiaoshuang 0f19a612b7 Merge branch 'main' into uat_us
# Conflicts:
#	src/views/trials/trials-layout/components/trialsNavbar.vue
2026-04-01 16:18:38 +08:00
wangxiaoshuang 3dcd03252e 项目显示时间 2026-03-05 15:48:31 +08:00
wangxiaoshuang 783b54ff66 usa_prod配置更改 2026-03-05 10:52:27 +08:00
wangxiaoshuang 6064971ff8 Merge branch 'uat' into uat_us
# Conflicts:
#	vue.config.js
2026-03-05 10:22:00 +08:00
wangxiaoshuang 8cef0141a4 Merge branch 'uat' into uat_us
# Conflicts:
#	src/views/login/index.vue
#	vue.config.js
2025-10-31 15:55:58 +08:00
wangxiaoshuang 60415e41d1 uat环境打包配置修改 2025-02-10 14:33:00 +08:00
wangxiaoshuang d7e85aa766 Merge branch 'main' into uat_us 2025-02-10 14:18:22 +08:00
wangxiaoshuang ef4d6420f3 美国uat环境配置修改,使用gzip压缩 2025-02-08 15:42:54 +08:00
wangxiaoshuang b6ffcd3d6b Merge branch 'main' into uat_us 2025-01-24 16:33:53 +08:00
wangxiaoshuang 914a6af94a LiLi简历设置去除盲化中文名称 2025-01-24 14:02:11 +08:00
wangxiaoshuang fee7e80929 打包配置修改 2025-01-24 13:57:32 +08:00
wangxiaoshuang d065b8b2c1 LiLi简历去除身份证、手机号校验,预览不显示中文简历 2025-01-24 13:56:48 +08:00
wangxiaoshuang 52445f3791 Merge branch 'main' into uat_us 2025-01-24 13:51:27 +08:00
wangxiaoshuang c4f8e08b91 LiLi环境logo 2025-01-24 10:19:56 +08:00
wangxiaoshuang f7968667f6 Merge branch 'main' into uat_us 2025-01-24 10:17:54 +08:00
wangxiaoshuang 24303b208d Merge branch 'main' into uat_us 2025-01-23 18:41:20 +08:00
wangxiaoshuang 296c251847 1 2025-01-23 18:40:25 +08:00
wangxiaoshuang 3b9e5cddb7 Merge branch 'main' into uat_us 2025-01-23 16:37:18 +08:00
wangxiaoshuang d11092338d 打包配置修改 2025-01-23 14:06:28 +08:00
wangxiaoshuang 848427ac3c Merge branch 'main' into uat_us 2025-01-23 13:03:36 +08:00
wangxiaoshuang d70cffe306 Merge branch 'main' into uat_us 2025-01-22 15:29:37 +08:00
wangxiaoshuang 0cd44bf4c6 Merge branch 'main' into uat_us 2025-01-21 15:51:22 +08:00
DESKTOP-6C3NK6N\WXS a2939e52b2 v1.7.0发布 2024-10-14 17:05:51 +08:00
DESKTOP-6C3NK6N\WXS 99e6f700c3 国际化修改 2024-09-14 10:33:27 +08:00
DESKTOP-6C3NK6N\WXS 45c50bf67d 1.6版本发布 2024-09-05 11:15:04 +08:00
DESKTOP-6C3NK6N\WXS 19e3177e23 Merge branch 'uat' into uat_us 2024-09-05 10:45:21 +08:00
DESKTOP-6C3NK6N\WXS 437010c0f7 1 2024-08-30 13:40:35 +08:00
DESKTOP-6C3NK6N\WXS 01b3d6e85e 1 2024-08-12 15:43:38 +08:00
DESKTOP-6C3NK6N\WXS cb032485e1 Merge branch 'uat' into uat_us 2024-08-12 14:39:21 +08:00
DESKTOP-6C3NK6N\WXS 4b771a9d70 1 2024-08-12 13:43:26 +08:00
DESKTOP-6C3NK6N\WXS 86321d8405 Merge branch 'uat' into uat_us 2024-08-12 13:42:50 +08:00
DESKTOP-6C3NK6N\WXS b9f3b24608 Merge branch 'uat' into uat_us 2024-08-09 13:42:57 +08:00
DESKTOP-6C3NK6N\WXS bde8a7d572 1 2024-08-09 13:12:16 +08:00
DESKTOP-6C3NK6N\WXS 267ee60fc2 Merge branch 'uat' into uat_us 2024-08-09 13:11:36 +08:00
DESKTOP-6C3NK6N\WXS ef891857c4 1 2024-08-08 16:42:14 +08:00
DESKTOP-6C3NK6N\WXS 787f6425e0 Merge branch 'uat' into uat_us 2024-08-08 16:41:40 +08:00
DESKTOP-6C3NK6N\WXS 4929bb594d Merge branch 'uat' into uat_us 2024-08-07 18:31:12 +08:00
DESKTOP-6C3NK6N\WXS 4b9c2a073b 1 2024-08-07 18:17:17 +08:00
DESKTOP-6C3NK6N\WXS 861c7022b9 Merge branch 'uat' into uat_us 2024-08-07 18:17:05 +08:00
DESKTOP-6C3NK6N\WXS 21e0568b90 Merge branch 'uat' into uat_us 2024-08-07 17:27:36 +08:00
DESKTOP-6C3NK6N\WXS ffc9372c25 Merge branch 'uat' into uat_us 2024-08-07 16:20:52 +08:00
DESKTOP-6C3NK6N\WXS a58a6e4931 Merge branch 'uat' into uat_us 2024-08-07 15:33:24 +08:00
DESKTOP-6C3NK6N\WXS 6ac94ec1a0 Merge branch 'uat' into uat_us 2024-08-02 14:17:49 +08:00
DESKTOP-6C3NK6N\WXS ddc175961c Merge branch 'uat' into uat_us 2024-08-02 14:09:18 +08:00
DESKTOP-6C3NK6N\WXS 098a838c1e 美国测试环境 2024-08-02 09:52:14 +08:00
DESKTOP-6C3NK6N\WXS 6bb7d47511 Merge branch 'uat' into uat_us 2024-07-31 14:08:34 +08:00
DESKTOP-6C3NK6N\WXS 8e641300d2 1 2024-07-30 18:09:14 +08:00
92 changed files with 6653 additions and 2066 deletions
+5 -1
View File
@@ -2,7 +2,11 @@
ENV = 'usa'
NODE_ENV = 'usa'
# base public path
VUE_APP_BASE_PATH = 'https://ei-code-prod.s3.amazonaws.com/2024-07-30/'
# VUE_APP_BASE_PATH = 'https://d3taa4kz0xxv95.cloudfront.net/2025-10-31/'
VUE_APP_BASE_PATH = '/'
# onlyoffice地址
VUE_APP_ONLYOFFICE_URL = "https://onlyoffice.uat.elevateimaging.ai"
# 是否开启登陆限制 true:是 false:否
VUE_APP_LOGIN_FOR_PERMISSION = true
+4 -1
View File
@@ -2,7 +2,10 @@
ENV = 'usa'
NODE_ENV = 'usa'
# base public path
VUE_APP_BASE_PATH = 'https://ei-med-s3-code.s3.amazonaws.com/2024-07-30/'
VUE_APP_BASE_PATH = '/'
# onlyoffice地址
VUE_APP_ONLYOFFICE_URL = "https://office.lili.elevateimaging.ai"
# 是否开启登陆限制 true:是 false:否
VUE_APP_LOGIN_FOR_PERMISSION = true
+44
View File
@@ -0,0 +1,44 @@
# just a flag
ENV = 'usa'
NODE_ENV = 'usa'
# base public path
VUE_APP_BASE_PATH = 'https://ei-code-prod.s3.amazonaws.com/2024-08-12/'
# 是否开启登陆限制 true:是 false:否
VUE_APP_LOGIN_FOR_PERMISSION = true
# 是否开启长时间无操作锁定弹框MFA验证 true:是 false:否
VUE_APP_LOCK_FOR_PERMISSION_MFA = true
# 是否开启长时间无操作锁定弹框 true:是 false:否
VUE_APP_LOCK_FOR_PERMISSION = true
# 无操作锁定弹框判断时间 单位:秒
VUE_APP_LOCK_FOR_TIME = 900
# 是否开启长时间无操作登出 true:是 false:否
VUE_APP_LOGOUT_FOR_PERMISSION = true
# 无操作锁定弹框判断时间 单位:秒
VUE_APP_LOGOUT_FOR_TIME = 1800
# 是否开启密码正则验证 true:是 false:否
VUE_APP_PASSWORD_FOR_PERMISSION = true
# 是否开启密码正则验证 true:是 false:否
VUE_APP_PASSWORD_FOR_REGULAR = ^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[-_.@^+\$!%#*?&\$~])[A-Za-z0-9-~_.@^+\$~!%#*?&]{8,32}$
# 是否开启文档签署验证 true:是 false:否
VUE_APP_WORD_FOR_PERMISSION = true
# 公司名称
VUE_APP_COMPANY_NAME = "Elevate Imaging"
# dicom文件地址
VUE_APP_DICOM_PATH = 'https://zyypacs-uat.oss-cn-shanghai.aliyuncs.com'
VUE_APP_OSS_CONFIG_REGION = 'oss-cn-shanghai'
VUE_APP_OSS_CONFIG_BUCKET = 'zyypacs-uat'
VUE_APP_OSS_PATH = '/usa/dist'
+10
View File
@@ -57,6 +57,7 @@
"sortablejs": "^1.15.5",
"streamsaver": "^2.0.6",
"svgo": "^1.2.2",
"unzipit": "^2.0.3",
"v-viewer": "^1.7.4",
"vcrontab": "^0.3.5",
"vue": "2.7.16",
@@ -21835,6 +21836,15 @@
"node": ">=0.10.0"
}
},
"node_modules/unzipit": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/unzipit/-/unzipit-2.0.3.tgz",
"integrity": "sha512-e6+ftgSQRj9Hwf2mIWFwYIGqBoKV7AziG/d7pep2Uv3NCzHbhLO5d9dGRwpahVlnpQ7aQC+5kWk72dnWocdYyA==",
"license": "MIT",
"engines": {
"node": ">=18"
}
},
"node_modules/unzipper": {
"version": "0.10.14",
"resolved": "https://registry.npmmirror.com/unzipper/-/unzipper-0.10.14.tgz",
+2
View File
@@ -6,6 +6,7 @@
"build": "vue-cli-service build",
"build:prod": "vue-cli-service build --mode prod",
"build:uat": "vue-cli-service build --mode uat",
"build:usa_test": "vue-cli-service build --mode usa_test",
"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",
@@ -63,6 +64,7 @@
"sortablejs": "^1.15.5",
"streamsaver": "^2.0.6",
"svgo": "^1.2.2",
"unzipit": "^2.0.3",
"v-viewer": "^1.7.4",
"vcrontab": "^0.3.5",
"vue": "2.7.16",
Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 21 KiB

BIN
View File
Binary file not shown.
+2 -1
View File
@@ -98,6 +98,7 @@ import timeTag from '@/components/timeTag'
import Vue from 'vue'
import i18n from './lang'
import DicomEvent from '@/views/trials/trials-panel/reading/dicoms/components/DicomEvent'
import WHITELIST from "./utils/whiteList"
export default {
name: 'App',
components: {
@@ -156,7 +157,7 @@ export default {
// },
methods: {
getIsLock() {
if (zzSessionStorage.getItem('isLock') === 'true') {
if (zzSessionStorage.getItem('isLock') === 'true' && !WHITELIST.includes(this.$route.path)) {
this.isLock = true
} else {
this.isLock = false
+27
View File
@@ -316,3 +316,30 @@ export function GetReadingMedicineSystemQuestionList_Export(data) {
data
})
}
// 导出中心调研
export function getTrialSiteSurveyList_Export(data) {
return requestDownload({
url: `/ExcelExport/getTrialSiteSurveyList_Export`,
responseType: 'blob',
method: 'post',
data
})
}
// 导出中心设备
export function getSiteSurveyEquipmentList_Export(data) {
return requestDownload({
url: `/ExcelExport/getSiteSurveyEquipmentList_Export`,
responseType: 'blob',
method: 'post',
data
})
}
// 导出中心调研基本信息
export function getSiteSurveyInfoList_Export(data) {
return requestDownload({
url: `/ExcelExport/getSiteSurveyInfoList_Export`,
responseType: 'blob',
method: 'post',
data
})
}
+8
View File
@@ -480,3 +480,11 @@ export function getNoneDicomMarkList(data) {
data
})
}
export function getFilterTableQuestion(data) {
return request({
url: `/ReadingImageTask/getFilterTableQuestion`,
method: 'post',
data
})
}
+28
View File
@@ -129,3 +129,31 @@ export function getUserTypeList(userTypeEnum) {
method: 'get'
})
}
export function getSiteSurveyLogList(param) {
return request({
url: `/TrialSiteSurvey/getSiteSurveyLogList`,
method: 'post',
data: param
})
}
export function getTrialIsSPMJoin(trialId) {
return request({
url: `/TrialSiteSurvey/getTrialIsSPMJoin?trialId=${trialId}`,
method: 'get'
})
}
export function getSiteSurveyEquipmentList(param) {
return request({
url: `/TrialSiteSurvey/getSiteSurveyEquipmentList`,
method: 'post',
data: param
})
}
export function getSiteSurveyInfoList(param) {
return request({
url: `/TrialSiteSurvey/getSiteSurveyInfoList`,
method: 'post',
data: param
})
}
+64
View File
@@ -488,3 +488,67 @@ export function setRandomTaskOrder(data) {
data
})
}
export function getJudgeForm(data) {
return request({
url: `/ReadingImageTask/getJudgeForm`,
method: 'post',
data
})
}
export function setJudgeForm(data) {
return request({
url: `/ReadingImageTask/setJudgeForm`,
method: 'post',
data
})
}
export function getSingleVisitJudgeAnswer(data) {
return request({
url: `/ReadingImageTask/getSingleVisitJudgeAnswer`,
method: 'post',
data
})
}
export function submitJudgeForm(data) {
return request({
url: `/Inspection/ReadingImageTask/SubmitJudgeForm`,
method: 'post',
data
})
}
export function getTrialQuestionVisitJudgeRule(data) {
return request({
url: `/ReadingQuestion/getTrialQuestionVisitJudgeRule`,
method: 'post',
data
})
}
export function setTrialQuestionVisitJudgeRule(data) {
return request({
url: `/ReadingQuestion/setTrialQuestionVisitJudgeRule`,
method: 'post',
data
})
}
export function copyTrialQuestionVisitJudgeRule(data) {
return request({
url: `/ReadingQuestion/copyTrialQuestionVisitJudgeRule`,
method: 'post',
data
})
}
export function applyBaseLineTrialQuestionVisitJudgeRule(data) {
return request({
url: `/ReadingQuestion/applyBaseLineTrialQuestionVisitJudgeRule`,
method: 'post',
data
})
}
+8
View File
@@ -94,6 +94,14 @@ export function getTrialJudgyInfo(params) {
data: params
})
}
export function setTrialJudgeRuleInfo(params) {
return request({
url: `/ReadingQuestion/setTrialJudgeRuleInfo`,
method: 'post',
data: params
})
}
// 获取检查部位列表
export function getTrialBodyPartList(params) {
return request({
+7 -3
View File
@@ -34,8 +34,8 @@
</div>
<div class="info-subject">
<div v-if="series.subjectCode">{{ series.subjectCode }}</div>
<div v-if="series.visitName">{{ series.visitName }}</div>
<div v-if="series.subjectCode && IsReadingTaskViewInOrder !== 0">{{ series.subjectCode }}</div>
<div v-if="series.visitName && IsReadingTaskViewInOrder !== 0">{{ series.visitName }}</div>
<div>{{ stack.description }}</div>
<!-- <div>{{ dicomInfo.hospital }}</div> -->
<!-- <div v-show="dicomInfo.pid">{{ dicomInfo.pid }}</div> -->
@@ -174,7 +174,8 @@ export default {
orientationMarkers: [],
originalMarkers: [],
dcmTag: { visible: false, title: this.$t('trials:dicom-tag:title') },
tip: ''
tip: '',
IsReadingTaskViewInOrder: 2
}
},
@@ -182,6 +183,9 @@ export default {
this.type = this.$router.currentRoute.query.type
? this.$router.currentRoute.query.type
: ''
if (this.$router.currentRoute.query.IsReadingTaskViewInOrder) {
this.IsReadingTaskViewInOrder = Number(this.$router.currentRoute.query.IsReadingTaskViewInOrder)
}
this.canvas = this.$refs.canvas
this.canvas.addEventListener('cornerstonenewimage', this.onNewImage)
this.canvas.addEventListener(
@@ -33,7 +33,7 @@
<el-table-column type="index" width="40" />
<!--受试者-->
<el-table-column :label="$t('download:table:subjectCode')" min-width="130" prop="SubjectCode"
show-overflow-tooltip />
show-overflow-tooltip v-if="isReadingTaskViewInOrder !== 0" />
<!--访视名称-->
<el-table-column prop="VisitName" :label="$t('download:table:VisitName')" sortable v-if="IsImageSegment" />
<!--任务名称-->
@@ -78,7 +78,7 @@
</el-table>
<study-view v-if="model_cfg.visible" :model_cfg="model_cfg" :modelList="modelList" :bodyPart="bodyPart"
:subjectVisitId="modelSubjectVisitId" :IsDicom="IsDicom" :isDownload="true" :visitTaskId="modelTaskId"
:IsImageSegment="IsImageSegment" :Criterion="Criterion" />
:IsImageSegment="IsImageSegment" :Criterion="Criterion" :IsReadingTaskViewInOrder="isReadingTaskViewInOrder" />
</el-dialog>
</template>
<script>
@@ -136,6 +136,14 @@ export default {
IsImageSegment: {
type: Boolean,
default: false,
},
isReadingTaskViewInOrder: {
type: Number,
default: 2,
},
isReading: {
type: Boolean,
default: false,
}
},
data() {
@@ -170,6 +178,7 @@ export default {
this.getList()
this.title = `Download Images${this.SubjectCode}${this.Criterion.TrialReadingCriterionName}`
if (this.IsImageSegment) this.title = `Download Images${this.SubjectCode}`
if (this.isReadingTaskViewInOrder === 0) this.title = ''
},
beforeDestroy() {
store.dispatch('trials/setUnLock', false)
@@ -327,6 +336,14 @@ export default {
if (data.StudyList && data.StudyList.length > 0) {
let StudyList = data.StudyList
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) {
let obj = {
name: `${data.SubjectCode}/${data.TaskBlindName
@@ -338,7 +355,7 @@ export default {
obj = {
name: `${data.SubjectCode}/${data.VisitName
}/${this.$fd('IsDicom', true)}/${study.StudyCode
}/DICOMDIR`,
}_${time}_${str}/DICOMDIR`,
url: this.OSSclientConfig.basePath + study.StudyDIRPath,
}
} else if (this.IsReadingTaskViewInOrder === 0) {
@@ -371,7 +388,7 @@ export default {
obj = {
name: `${data.SubjectCode}/${data.VisitName
}/${this.$fd('IsDicom', true)}/${study.StudyCode
}/IMAGE/${fileName}`,
}_${time}_${str}/IMAGE/${fileName}`,
url: this.OSSclientConfig.basePath + instance.Path,
IsEncapsulated: instance.IsEncapsulated
}
@@ -437,6 +454,9 @@ export default {
let params = {
TrialImageDownloadId: this.downloadId,
}
if (this.isReading) {
params.VisitTaskId = this.TaskId
}
await downloadImageSuccess(params)
} catch (err) {
console.log(err)
@@ -447,6 +467,9 @@ export default {
if (this.IsImageSegment) {
this.model_cfg.title = `${item.SubjectCode || ''} > ${item.VisitName}`
}
if (this.isReadingTaskViewInOrder === 0) {
this.model_cfg.title = ''
}
if (item.IsDicom) {
this.modelList = item.DicomStudyList
} else {
@@ -488,11 +511,11 @@ export default {
let routeData = null
if (!this.IsImageSegment && (this.Criterion.CriterionType == 19 || this.Criterion.CriterionType == 20)) {
routeData = this.$router.resolve({
path: `/showNoneDicoms?trialId=${trialId}&isImageSegmentLabel=${false}&visitTaskId=${row.VisitTaskId}&subjectVisitId=${row.SourceSubjectVisitId}&TokenKey=${token}&isReading=true`,
path: `/showNoneDicoms?trialId=${trialId}&isImageSegmentLabel=${false}&visitTaskId=${row.VisitTaskId}&subjectVisitId=${row.SourceSubjectVisitId}&TokenKey=${token}&isReading=true&IsReadingTaskViewInOrder=${this.isReadingTaskViewInOrder}`,
})
} else {
routeData = this.$router.resolve({
path: `/showNoneDicoms?trialId=${trialId}&visitTaskId=${row.VisitTaskId}&subjectVisitId=${row.SourceSubjectVisitId}&TokenKey=${token}&isReading=true`,
path: `/showNoneDicoms?trialId=${trialId}&visitTaskId=${row.VisitTaskId}&subjectVisitId=${row.SourceSubjectVisitId}&TokenKey=${token}&isReading=true&IsReadingTaskViewInOrder=${this.isReadingTaskViewInOrder}`,
})
}
this.open = window.open(routeData.href, '_blank')
@@ -505,7 +528,7 @@ export default {
var token = getToken()
let trialId = this.$route.query.trialId
const routeData = this.$router.resolve({
path: `/showvisitdicoms?page=download&trialId=${trialId}&visitTaskId=${row.VisitTaskId}&subjectVisitId=${row.SourceSubjectVisitId}&isReading=1&TokenKey=${token}`,
path: `/showvisitdicoms?page=download&trialId=${trialId}&visitTaskId=${row.VisitTaskId}&subjectVisitId=${row.SourceSubjectVisitId}&isReading=1&TokenKey=${token}&IsReadingTaskViewInOrder=${this.isReadingTaskViewInOrder}`,
})
this.open = window.open(routeData.href, '_blank')
},
@@ -10,7 +10,8 @@
<!--检查列表-->
<el-table :data="list" style="width: 100%" height="300" :loading="loading">
<!--受试者-->
<el-table-column prop="SubjectCode" :label="$t('upload:dicom:table:subjectCode')" sortable />
<el-table-column prop="SubjectCode" :label="$t('upload:dicom:table:subjectCode')" sortable
v-if="isReadingTaskViewInOrder !== 0" />
<!--访视名称-->
<el-table-column prop="VisitName" :label="$t('download:table:VisitName')" v-if="IsImageSegment" sortable />
<!--任务名称-->
@@ -42,7 +43,8 @@
<template slot-scope="scope">
<div class="btnBox">
<!--上传--->
<form id="inputForm" :ref="`uploadForm_${scope.row.Id}`" enctype="multipart/form-data" v-if="!forbid">
<form id="inputForm" :ref="`uploadForm_${scope.row.Id}`" enctype="multipart/form-data"
v-if="!forbid && (!isReading || isDownloaded)">
<div class="form-group" style="margin-right: 10px">
<div :id="`directoryInputWrapper_${scope.row.Id}`" class="btn btn-link file-input">
<el-button circle icon="el-icon-upload2" :disabled="btnLoading" :loading="btnLoading"
@@ -69,7 +71,7 @@
</el-table>
<div style="margin: 10px 0" class="top">
<span>{{ $t('upload:dicom:uploadTitle') }}</span>
<div class="btnBox" v-if="!forbid">
<div class="btnBox" v-if="!forbid && (!isReading || isDownloaded)">
<span style="margin-right: 10px">{{ $store.state.trials.uploadTip }}</span>
<form id="inputForm" ref="uploadForm" enctype="multipart/form-data">
<div class="form-group">
@@ -378,6 +380,10 @@ export default {
type: Boolean,
default: false,
},
isReading: {
type: Boolean,
default: false,
}
},
components: {
'study-view': studyView,
@@ -427,7 +433,8 @@ export default {
openSubjectVisitId: null,
openVisitTaskId: null,
TrialModality: [],
IsReadingTaskViewInOrder: 2
IsReadingTaskViewInOrder: 2,
isDownloaded: false
}
},
async mounted() {
@@ -470,6 +477,10 @@ export default {
let res = await getSubjectImageUploadList(params)
this.loading = false
if (res.IsSuccess) {
this.isDownloaded = this.Criterion && this.Criterion.CriterionType && (
this.Criterion.CriterionType == 19 ||
this.Criterion.CriterionType == 20
) ? res.OtherInfo.IsIRImageDownloaded : true
this.TrialModality = res.OtherInfo.TrialModality.split('|')
this.IsReadingTaskViewInOrder = res.OtherInfo.IsReadingTaskViewInOrder || res.OtherInfo.IsReadingTaskViewInOrder === 0 ? res.OtherInfo.IsReadingTaskViewInOrder : 2
this.StudyInstanceUidList = []
@@ -553,6 +564,7 @@ export default {
this.openVisitTaskId = item.VisitTaskId
this.model_cfg.title = `${item.SubjectCode || ''} > ${this.IsImageSegment ? item.VisitName : item.TaskBlindName
}`
if (this.isReadingTaskViewInOrder === 0) this.model_cfg.title = ''
this.modelList = item[list]
this.model_cfg.visible = true
},
@@ -1642,7 +1654,7 @@ export default {
const routeData = this.$router.resolve({
path: `/showvisitdicoms?page=upload&trialId=${trialId}&visitTaskId=${this.IsImageSegment ? 'undefined' : row.VisitTaskId
}&subjectVisitId=${row.SourceSubjectVisitId
}&isReading=1&TokenKey=${token}&IsReadingTaskViewInOrder=${this.IsReadingTaskViewInOrder}`,
}&isReading=1&TokenKey=${token}&IsReadingTaskViewInOrder=${this.isReadingTaskViewInOrder}`,
})
this.open = window.open(routeData.href, '_blank')
},
@@ -3,15 +3,16 @@
:append-to-body="true" class="uploadDicomAndNonedicom">
<span slot="title">{{ title }}</span>
<el-tabs type="border-card" v-model="activeName">
<el-tab-pane :label="$t('uploadDicomAndNonedicom:label:dicom')" name="dicom">
<el-tab-pane :label="$t('uploadDicomAndNonedicom:label:dicom')" name="dicom" v-if="!IsImageSegment">
<dicomFile v-if="activeName === 'dicom'" :SubjectId="SubjectId" :SubjectCode="SubjectCode"
:Criterion="Criterion" :TaskId="VisitTaskId" :isUpload.sync="isUpload"
:isReadingTaskViewInOrder="isReadingTaskViewInOrder" :IsImageSegment="IsImageSegment" :forbid="forbid" />
:isReadingTaskViewInOrder="isReadingTaskViewInOrder" :IsImageSegment="IsImageSegment" :forbid="forbid"
:isReading="isReading" />
</el-tab-pane>
<el-tab-pane :label="$t('uploadDicomAndNonedicom:label:nonedicom')" name="nonedicom">
<nonedicomFile v-if="activeName === 'nonedicom'" :SubjectId="SubjectId" :SubjectCode="SubjectCode"
:Criterion="Criterion" :VisitTaskId="VisitTaskId" :isUpload.sync="isUpload" :IsImageSegment="IsImageSegment"
:forbid="forbid" />
:isReadingTaskViewInOrder="isReadingTaskViewInOrder" :forbid="forbid" :isReading="isReading" />
</el-tab-pane>
</el-tabs>
</el-dialog>
@@ -58,6 +59,10 @@ export default {
IsImageSegment: {
type: Boolean,
default: false,
},
isReading: {
type: Boolean,
default: false,
}
},
data() {
@@ -67,9 +72,16 @@ export default {
isUpload: false,
}
},
watch: {
IsImageSegment() {
if (this.IsImageSegment) this.activeName = 'nonedicom'
}
},
mounted() {
this.title = `Upload Images${this.SubjectCode}${this.Criterion.TrialReadingCriterionName}`
if (this.IsImageSegment) this.title = `Upload Images${this.SubjectCode}`
if (this.isReadingTaskViewInOrder === 0) this.title = ''
if (this.IsImageSegment) this.activeName = 'nonedicom'
store.dispatch('trials/setUnLock', true)
},
methods: {
@@ -14,7 +14,8 @@
<el-table :data="list" style="width: 100%" v-adaptive="{ bottomOffset: 60 }" :loading="loading"
:default-sort="{ prop: 'TaskBlindName', order: 'descending' }">
<!--受试者 sortable="custom"-->
<el-table-column prop="SubjectCode" :label="$t('upload:nonedicom:table:subject')" />
<el-table-column prop="SubjectCode" :label="$t('upload:nonedicom:table:subject')"
v-if="isReadingTaskViewInOrder !== 0" />
<!--访视名称-->
<el-table-column prop="VisitName" :label="$t('upload:nonedicom:table:VisitName')" sortable
v-if="IsImageSegment" />
@@ -110,7 +111,7 @@
:title="$t('upload:nonedicom:button:preview')" @click.stop="handlePreviewNoneDicomFiles(scope.row)" />
<!--上传--->
<el-button circle icon="el-icon-upload2" :title="$t('upload:nonedicom:button:upload')" v-if="!forbid"
@click.native.prevent="handleUpload(scope.row)" />
@click.native.prevent="handleUpload(scope.row)" :disabled="isReading && !isDownloaded" />
<!--删除--->
<el-button :disabled="scope.row.UploadedFileCount <= 0 ||
scope.row.ReadingTaskState === 2
@@ -223,6 +224,7 @@ import {
import { preArchiveStudy, uploadNoneDicomFile } from '@/api/trials'
import store from '@/store'
import { getToken } from '@/utils/auth'
import { unzipRaw } from 'unzipit';
let defaultSearchData = () => {
return {
SubjectId: null,
@@ -265,6 +267,14 @@ export default {
type: Boolean,
default: false,
},
isReadingTaskViewInOrder: {
type: Number,
default: 0,
},
isReading: {
type: Boolean,
default: false,
}
},
data() {
return {
@@ -286,7 +296,8 @@ export default {
BodyPart: {},
relationInfo: {
ImageFormatList: []
}
},
isDownloaded: false
}
},
watch: {
@@ -322,6 +333,34 @@ export default {
store.dispatch('trials/setUnLock', false)
},
methods: {
async readZipFileListRaw(zipFile) {
try {
// 1. 使用 unzipRaw 读取原始条目列表
const { entries } = await unzipRaw(zipFile);
// 2. entries 是一个数组,需要手动处理
const fileList = entries.map(entry => {
// 3. 使用 TextDecoder 指定编码来解码文件名
// 例如,对于简体中文 ZIP 包,可以使用 'gbk'
const decoder = new TextDecoder('gbk'); // 或者 'big5'
const name = decoder.decode(entry.nameBytes);
return {
path: name,
name: name.split('/').pop(),
isDirectory: entry.isDirectory,
size: entry.size,
lastModified: entry.lastModDate
};
});
console.log('解码后的文件列表:', fileList);
return fileList;
} catch (error) {
console.error('读取 ZIP 失败:', error);
throw error;
}
},
async getList() {
try {
this.searchData.SubjectId = this.SubjectId
@@ -343,6 +382,10 @@ export default {
if (res.IsSuccess) {
this.list = res.Result
this.relationInfo = res.OtherInfo
this.isDownloaded = this.Criterion && this.Criterion.CriterionType && (
this.Criterion.CriterionType == 19 ||
this.Criterion.CriterionType == 20
) ? res.OtherInfo.IsIRImageDownloaded : true
this.faccept = []
this.relationInfo.ImageFormatList.forEach((item) => {
this.faccept.push(`.${item}`)
@@ -356,11 +399,15 @@ export default {
// 扫描待上传文件
beginScanFiles(e) {
var files = [...e.target.files]
var sameFiles = []
var sameFiles = [], zipFiles = [], f = false
files.forEach((file) => {
var extendName = file.name
.substring(file.name.lastIndexOf('.'))
.toLocaleLowerCase()
if (extendName === '.zip' && this.IsImageSegment) {
if (file.name.indexOf('筛选期') > -1 || file.name.indexOf('研究结束') > -1) f = true
zipFiles.push(file)
}
if (
this.faccept.indexOf(extendName) !== -1 &&
this.fileList.findIndex((v) => v.name === file.name) > -1
@@ -368,6 +415,16 @@ export default {
sameFiles.push(file.name)
}
})
if (zipFiles.length > 0) {
zipFiles.forEach(async file => {
let list = await this.readZipFileListRaw(file)
if (!f) f = list.some(i => i.path.indexOf('筛选期') > -1 || i.path.indexOf('研究结束') > -1)
list = null
})
}
if (f) return this.$confirm(this.$t("trials:uploadNonDicoms:confirm:zipNameNonCompliant"), '', {
type: "warning"
})
var scope = this
if (sameFiles.length > 0) {
const h = this.$createElement
@@ -475,7 +532,7 @@ export default {
let trialId = this.$route.query.trialId
var token = getToken()
const routeData = this.$router.resolve({
path: `/showNoneDicoms?trialId=${trialId}&subjectVisitId=${row.SourceSubjectVisitId}&studyId=${row.Id}&visitTaskId=${row.VisitTaskId}&TokenKey=${token}`,
path: `/showNoneDicoms?trialId=${trialId}&subjectVisitId=${row.SourceSubjectVisitId}&studyId=${row.Id}&visitTaskId=${row.VisitTaskId}&TokenKey=${token}&IsReadingTaskViewInOrder=${this.isReadingTaskViewInOrder}`,
})
this.open = window.open(routeData.href, '_blank')
},
@@ -7,7 +7,8 @@
</el-button>
<el-table :data="modelList" style="width: 100%" height="300">
<!--检查编号-->
<el-table-column prop="StudyCode" :label="$t('trials:uploadImage:table:StudyCode')" />
<el-table-column prop="StudyCode" :label="$t('trials:uploadImage:table:StudyCode')"
v-if="IsReadingTaskViewInOrder !== 0" />
<!--检查类型-->
<el-table-column prop="ModalityForEdit" :label="$t('trials:uploadImage:table:ModalityForEdit')"
v-if="IsDicom" />
@@ -187,11 +188,11 @@ export default {
var token = getToken()
if (!this.IsImageSegment && (this.Criterion.CriterionType == 19 || this.Criterion.CriterionType == 20)) {
routeData = this.$router.resolve({
path: `/showNoneDicoms?trialId=${trialId}&isImageSegmentLabel=${false}&visitTaskId=${this.visitTaskId}&subjectVisitId=${this.subjectVisitId}&TokenKey=${token}&isReading=true`,
path: `/showNoneDicoms?trialId=${trialId}&isImageSegmentLabel=${false}&visitTaskId=${this.visitTaskId}&subjectVisitId=${this.subjectVisitId}&TokenKey=${token}&isReading=true&IsReadingTaskViewInOrder=${this.IsReadingTaskViewInOrder}`,
})
} else {
routeData = this.$router.resolve({
path: `/showNoneDicoms?trialId=${trialId}&visitTaskId=${this.visitTaskId}&subjectVisitId=${this.subjectVisitId}&TokenKey=${token}&isReading=true`,
path: `/showNoneDicoms?trialId=${trialId}&visitTaskId=${this.visitTaskId}&subjectVisitId=${this.subjectVisitId}&TokenKey=${token}&isReading=true&IsReadingTaskViewInOrder=${this.IsReadingTaskViewInOrder}`,
})
}
+7 -2
View File
@@ -92,8 +92,13 @@ router.beforeEach(async (to, from, next) => {
await store.dispatch('user/getUserInfo')
const accessRoutes = await store.dispatch('permission/generateRoutes')
resetRouter()
router.addRoutes(accessRoutes)
next({ ...to, replace: true })
if (accessRoutes.length > 0) {
router.addRoutes(accessRoutes)
next({ ...to, replace: true })
} else {
next()
}
} catch (error) {
console.log(error)
// 删除token并进入登录页面以重新登录
+11
View File
@@ -149,6 +149,11 @@ export const constantRoutes = [
hidden: true,
component: () => import('@/views/trials/trials-panel/reading/dicoms/none-dicoms')
},
{
path: '/ecrfList',
hidden: true,
component: () => import('@/views/trials/trials-panel/reading/dicoms/components/TableList')
},
{
path: '/readingPage',
name: 'readingPage',
@@ -223,6 +228,12 @@ export const constantRoutes = [
component: () => import('@/views/research/form'),
hidden: true
},
{
path: '/researchLog',
name: 'researchLog',
component: () => import('@/views/research/form'),
hidden: true
},
{
path: '/researchLogin_m',
name: 'researchLogin_m',
+4
View File
@@ -376,4 +376,8 @@ body .el-table th.gutter {
height: 20px !important;
vertical-align: -0.4em !important;
cursor: pointer;
}
.mobile_confirm {
width: 200px;
}
+3
View File
@@ -118,6 +118,9 @@ service.interceptors.response.use(
if (res.IsSuccess) {
return Promise.resolve(res)
} else if (res.IsSuccess === false) {
if (res.Code === -4) {
return Promise.resolve(res)
}
if (res.Code !== 5) {
MessageBox.confirm(res.ErrorMessage, {
type: 'warning',
+9 -3
View File
@@ -11,10 +11,10 @@ function zipFiles(zipName, files) {
store.dispatch('trials/setUnLock', true)
files = formatFiles(files)
// 创建压缩文件输出流
const zipFileOutputStream = streamSaver.createWriteStream(zipName);
let zipFileOutputStream = streamSaver.createWriteStream(zipName);
// 创建下载文件流
const fileIterator = files.values();
const readableZipStream = new ZIP({
let readableZipStream = new ZIP({
async pull(ctrl) {
const fileInfo = fileIterator.next();
if (fileInfo.done) {//迭代终止
@@ -41,9 +41,13 @@ function zipFiles(zipName, files) {
.then(() => {
console.log("同步下载打包结束时间:" + new Date());
store.dispatch('trials/setUnLock', false)
readableZipStream = null;
zipFileOutputStream = null;
resolve(true)
}).catch(err => {
console.log(err);
readableZipStream = null;
zipFileOutputStream = null;
resolve(false)
});
} else {
@@ -61,14 +65,16 @@ async function updateFile(file, name) {
return new Promise(async resolve => {
try {
store.dispatch('trials/setUnLock', true)
const fileOutputStream = streamSaver.createWriteStream(name);
let fileOutputStream = streamSaver.createWriteStream(name);
// file = decodeUtf8(file);
let res = await fetch(file);
res.body.pipeTo(fileOutputStream).then(() => {
store.dispatch('trials/setUnLock', true)
fileOutputStream = null;
resolve(true)
}).catch(err => {
console.log(err)
fileOutputStream = null;
resolve(false)
});
} catch (err) {
+1 -1
View File
@@ -1,2 +1,2 @@
const WHITELIST = ['/', "/curriculumVitae", 'researchDetail_m', '/researchForm', '/ReviewersResearch', '/login', '/link_expired', '/error', '/resetpassword', '/recompose', '/email-recompose', '/trialStats', '/showdicom', '/imagesShare', '/audit', '/preview', '/researchLogin', '/researchLogin_m', '/blindResumeInfo', '/trialsResume', '/joinVerify', '/showNoneDicoms', '/noneDicomReading', '/clinicalData', '/readingDicoms', '/readingPage', '/visitDicomReview', '/visitNondicomReview', '/globalReview', '/adReview', '/oncologyReview', '/nonedicoms']
const WHITELIST = ['/', "/curriculumVitae", 'researchDetail_m', '/researchForm', '/ReviewersResearch', '/login', '/link_expired', '/error', '/resetpassword', '/recompose', '/email-recompose', '/trialStats', '/showdicom', '/imagesShare', '/audit', '/preview', '/researchLogin', '/researchLogin_m', '/blindResumeInfo', '/trialsResume', '/joinVerify', '/showNoneDicoms', '/noneDicomReading', '/clinicalData', '/readingDicoms', '/readingPage', '/visitDicomReview', '/visitNondicomReview', '/globalReview', '/adReview', '/oncologyReview', '/nonedicoms', '/ecrfList', '/researchLog']
export default WHITELIST
+3 -3
View File
@@ -20,7 +20,7 @@
:name="`${study.StudyId}`">
<template slot="title">
<div class="collapse-title-wrapper">
<div class="text-desc">
<div class="text-desc" v-if="IsReadingTaskViewInOrder !== 0">
{{ study.StudyCode }}
</div>
<!-- <div v-show="study.Description" class="text-desc">
@@ -157,7 +157,7 @@
</el-tab-pane>
<el-tab-pane v-show="!visitTaskId" v-if="IsReadingTaskViewInOrder >= 2"
:label="$t('trials:dicom-show:relatedVisit')" name="relation-study" class="pane-relation-wrapper">
<div class="viewerSidethumbinner">
<div class="viewerSidethumbinner">
<el-collapse v-model="relationActiveName" @change="handelRelationActiveChange">
<div v-for="item in relationStudyListByVisitName" :key="`${item.VisitName}`">
@@ -169,7 +169,7 @@
v-if="study.VisitName === item.VisitName">
<template slot="title">
<div class="collapse-title-wrapper">
<div class="text-desc">
<div class="text-desc" v-if="IsReadingTaskViewInOrder !== 0">
{{ study.StudyCode }}
</div>
<!-- <div v-show="study.Description" class="text-desc">
+6 -3
View File
@@ -1,6 +1,6 @@
<template>
<div class="wscn-http404-container">
<div class="wscn-http404" style="display: flex;align-items: center">
<div class="wscn-http404" style="display: flex;align-items: center;flex-wrap: wrap;justify-content: center;">
<div class="pic-404">
<!-- <img class="pic-404__parent" src="@/assets/login-bg.png" alt="404"> -->
<svg-icon icon-class="login-bg" style="width: 100%; height: 100%" />
@@ -55,18 +55,20 @@ export default {
position: absolute;
top: 40%;
left: 50%;
width: 100%;
}
.wscn-http404 {
position: relative;
width: 1200px;
width: 100%;
padding: 0 50px;
overflow: hidden;
.pic-404 {
position: relative;
float: left;
width: 500px;
width: 100%;
max-width: 500px;
height: 300px;
overflow: hidden;
margin-right: 10px;
@@ -211,6 +213,7 @@ export default {
animation-name: slideUp;
animation-duration: 0.5s;
animation-fill-mode: forwards;
text-align: center;
}
&__headline {
+7 -9
View File
@@ -6,9 +6,7 @@
<div class="login-body">
<div class="login-l">
<div class="login-logo">
<img v-if="language === 'zh'" src="@/assets/zzlogo2.png" alt="" />
<img v-else-if="NODE_ENV === 'usa'" src="@/assets/zzlogo-usa.png" alt="" class="usa-logo" />
<img v-else src="@/assets/zzlogo4.png" alt="" />
<img src="@/assets/zzlogo-usa.png" alt="" class="usa-logo" />
</div>
<div :class="{
'login-image': true,
@@ -21,14 +19,14 @@
<div class="login-r">
<div class="title-container">
<!-- IRC Management System -->
<div v-if="NODE_ENV === 'usa'">
<!-- <div v-if="NODE_ENV === 'usa'"> -->
<svg-icon icon-class="login-logo" style="width: 300px; height: 94px" />
</div>
<div class="title" v-else>
<!-- </div> -->
<!-- <div class="title" v-else>
<img src="@/assets/system.png" alt=""
:style="{ width: isEN ? '180px' : '200px', height: isEN ? '60px' : '65px' }" />
<div :style="`font-size:${isEN ? '28px' : '35px'}`">{{ $t('login:title:system') }}</div>
</div>
</div> -->
</div>
<el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" auto-complete="on"
label-position="left">
@@ -98,10 +96,10 @@
$t('login:message:PrivacyPolicyName') }}</span>
</div>
<div style="text-align: right">
<TopLang v-if="
<!-- <TopLang v-if="
VUE_APP_OSS_CONFIG_REGION !== 'oss-us-west-1' &&
NODE_ENV !== 'usa'
" />
" /> -->
<!-- Forget password? -->
<el-button type="text" size="medium" @click.native.prevent="handleResetPwd" style="color: rgb(0, 147, 221)">
{{ $t('login:button:forgetPassword') }}
+5 -1
View File
@@ -153,7 +153,8 @@ export default {
isAudit: false,
activeNames: [],
Asc: false
Asc: false,
IsReadingTaskViewInOrder: 2
}
},
async created() {
@@ -165,6 +166,9 @@ export default {
store.dispatch('user/setToken', this.$router.currentRoute.query.TokenKey)
changeURLStatic('TokenKey', '')
}
if (this.$router.currentRoute.query.IsReadingTaskViewInOrder) {
this.IsReadingTaskViewInOrder = Number(this.$router.currentRoute.query.IsReadingTaskViewInOrder)
}
this.subjectVisitId = this.$router.currentRoute.query.subjectVisitId
this.studyId = this.$router.currentRoute.query.studyId
this.getNoneDicomList()
@@ -50,6 +50,9 @@
</div>
<div v-if="v.fileType.includes('zip')" class="content flex_col">
<img :title="v.FileName" crossorigin="anonymous" :src="zipImg" height="100%"/>
</div>
</div>
</div>
<div class="Anonymous" v-if="isAnonymous">
@@ -158,6 +161,7 @@ import hardcodedMetaDataProvider from '@/views/trials/trials-panel/reading/visit
import registerWebImageLoader from '@/views/trials/trials-panel/reading/visit-review/js/registerWebImageLoader'
import Note_RectangleRoiTool from '@/views/trials/trials-panel/reading/dicoms3D/components/tools/Note_RectangleRoiTool'
import { noneDicomStudyMaskImage, noneDicomStudyUndoMaskImage } from "@/api/reading"
import zipImg from '@/assets/zip.png'
const { ViewportType } = Enums
const renderingEngineId = 'myRenderingEngine'
const {
@@ -221,7 +225,7 @@ export default {
activeTool: '',
imageType: ['image/jpeg', 'image/jpg', 'image/bmp', 'image/png'],
loading: false,
isFitToWindowMode: false,
isFitToWindowMode: true,
trialId: null,
activeName: '1',
tools: [],
@@ -231,7 +235,8 @@ export default {
tip: [
this.$t('DicomViewer:anonymous:after'),
this.$t('DicomViewer:anonymous:before')
]
],
zipImg
}
},
computed: {
@@ -42,6 +42,9 @@
<el-image v-else-if="
!!~k.FileType.indexOf('mp4')
" style="width: 100%; height: 100%" :src="mp4" fit="contain" crossorigin="anonymous" />
<el-image v-else-if="
!!~k.FileType.indexOf('zip')
" style="width: 100%; height: 100%" :src="zipImg" fit="contain" crossorigin="anonymous" />
</div>
<div class="file-text" :title="k.FileName">
{{ k.FileName }}
@@ -73,6 +76,7 @@ import { getNoneDicomStudyList, setNodicomStudyState } from '@/api/trials'
import FileNameSorter from "@/utils/customSort"
import pdf from '@/assets/pdf.png'
import mp4 from '@/assets/mp4.png'
import zipImg from '@/assets/zip.jpg'
export default {
name: 'StudyList',
props: {
@@ -98,6 +102,7 @@ export default {
studyList: [],
pdf,
mp4,
zipImg,
BodyPart: {},
subjectVisitId: '',
studyId: '',
@@ -25,19 +25,10 @@
<div class="login_content">
<div class="form-label-width">
<el-form-item :label="$t('trials:researchForm:form:siteName')" prop="TrialSiteId">
<el-select
v-model="form.TrialSiteId"
filterable
style="width:100%;"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory"
@change="handleSiteChange"
>
<el-option
v-for="(item,index) of siteOptions"
:key="index"
:label="item.TrialSiteAliasName"
:value="item.TrialSiteId"
/>
<el-select v-model="form.TrialSiteId" filterable style="width:100%;"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" @change="handleSiteChange">
<el-option v-for="(item, index) of siteOptions" :key="index" :label="item.TrialSiteAliasName"
:value="item.TrialSiteId" />
</el-select>
</el-form-item>
<!-- 中心编号 -->
@@ -49,74 +40,149 @@
<el-input v-model="form.UserName" :disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" />
</el-form-item>
<!-- 联系电话 -->
<el-form-item
:label="$t('trials:researchForm:form:contactorPhone')"
prop="Phone"
>
<el-form-item :label="$t('trials:researchForm:form:contactorPhone')" prop="Phone">
<el-input v-model="form.Phone" :disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" />
</el-form-item>
<!-- 联系邮箱 -->
<el-form-item :label="$t('trials:researchForm:form:contactorEmail')">
<el-form-item :label="$t('trials:researchForm:form:contactorEmail')" prop="Email">
<el-input v-model="form.Email" disabled />
</el-form-item>
<el-form-item :label="$t('trials:researchForm:form:CommonUploadRecordId')
" v-if="IsSupportUploadFile" class="file">
<el-input v-model="form.SiteSurveyFile.FileName" type="textarea" :autosize="{ minRows: 1, maxRows: 3 }"
style="margin-right: 5px;" disabled />
<div class="upload" v-if="!(!(state === 0 && userTypeEnumInt === 0) || isHistory)">
<input accept=".pdf,.docx,.doc" type="file" name="uploadFolder" class="select-file" title=""
@change="beginScanFiles($event)" />
<div class="btn-select">
{{ $t('dictionary:template:basicData:button:selectFile') }}
</div>
</div>
<!-- <el-button type="primary" @click="viewManualFile"
:disabled="!this.form.SiteSurveyFile || !this.form.SiteSurveyFile.Path">{{
$t('trials:researchForm:form:preview') }}</el-button> -->
<el-button type="primary" @click="downLoad"
:disabled="!this.form.SiteSurveyFile || !this.form.SiteSurveyFile.Path">{{
$t('trials:researchForm:form:download') }}</el-button>
</el-form-item>
<!-- 平均刻盘周期 -->
<el-form-item v-if="!notShowFieldList.includes('AverageEngravingCycle')" :label="$t('trials:researchForm:form:engravingCycle')">
<el-input-number v-model="form.AverageEngravingCycle" :disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" controls-position="right" :min="0" style="width:100%;" />
<el-form-item v-if="!notShowFieldList.includes('AverageEngravingCycle')"
:label="$t('trials:researchForm:form:engravingCycle')">
<el-input-number v-model="form.AverageEngravingCycle"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" controls-position="right" :min="0"
style="width:100%;" />
</el-form-item>
</div>
<!-- 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')">
<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"
>{{ item.label }}</el-radio>
<el-form-item v-if="!notShowFieldList.includes('IsConfirmImagingTechnologist')"
: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">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<!-- 原因 -->
<el-form-item
v-if="!notShowFieldList.includes('NotConfirmReson') && form.IsConfirmImagingTechnologist === false"
:label="$t('trials:researchForm:form:notQualifiedReason')"
>
<el-input
v-model="form.NotConfirmReson"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory"
/>
: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>
<!-- 研究单位疗效评估人员类型 -->
<el-form-item v-if="!notShowFieldList.includes('EfficacyEvaluatorType')" :label="$t('trials:researchForm:form:staffType')">
<el-radio-group v-model="form.EfficacyEvaluatorType" :disabled="!(state === 0 && userTypeEnumInt === 0)|| isHistory">
<el-radio v-for="item of $d.EfficacyEvaluatorType" :key="`EfficacyEvaluatorType${item.value}`" :label="item.value">{{ item.label }}</el-radio>
<el-form-item v-if="!notShowFieldList.includes('EfficacyEvaluatorType')"
:label="$t('trials:researchForm:form:staffType')">
<el-radio-group v-model="form.EfficacyEvaluatorType"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory">
<el-radio v-for="item of $d.EfficacyEvaluatorType" :key="`EfficacyEvaluatorType${item.value}`"
:label="item.value">{{ item.label }}</el-radio>
</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">
<el-radio v-for="item of $d.YesOrNo" :key="`IsFollowStudyParameters${item.value}`" :label="item.value">{{ item.label }}</el-radio>
<el-radio-group v-model="form.IsFollowStudyParameters"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory">
<el-radio v-for="item of $d.YesOrNo" :key="`IsFollowStudyParameters${item.value}`" :label="item.value">{{
item.label }}</el-radio>
</el-radio-group>
<el-button type="primary" size="small" style="margin-left: 5px;" @click="viewManual">
{{ $t('trials:researchForm:button:viewManual') }}
</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-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>
</div>
</el-form>
</div>
</template>
<script>
import { getTrialSiteSelect } from '@/api/trials'
import { getTrialSiteSelect, getTrialDocumentList, addOrUpdateCommonUploadRecord } from '@/api/trials'
import { addOrUpdateTrialSiteSurvey } from '@/api/research'
export default {
name: 'ResearchBasicInfo',
@@ -124,7 +190,15 @@ export default {
isHistory: {
type: Boolean,
default: false
}
},
IsOnlyUploadFile: {
type: Boolean,
default: false
},
IsSupportUploadFile: {
type: Boolean,
default: false
},
},
data() {
var checkPhone = (rule, value, callback) => {
@@ -156,16 +230,51 @@ export default {
Phone: '', // 联系人电话
Email: '', // 联系人邮箱
AverageEngravingCycle: '',
IsRoutineMRIPDEE: '',
MRIPDFFScanTime: '',
MRIPDFFLeadTime: '',
MRIPDFFOther: '',
IsAuthorize: '',
IsAuthorizeRadiologistsParticipate: '',
AssignFixedTechnologists: '',
ISStrictManualBurnFlag: '',
NotStrictManualBurnFlagReason: '',
IsConfirmImagingTechnologist: '',
NotConfirmReson: '',
EfficacyEvaluatorType: '',
IsFollowStudyParameters: '',
NotFollowReson: ''
NotFollowReson: '',
CommonUploadRecordId: null,
SiteSurveyFile: []
},
rules: {
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'] }
@@ -186,14 +295,173 @@ export default {
state: null,
userTypeEnumInt: zzSessionStorage.getItem('userTypeEnumInt') * 1,
isShow: false,
notShowFieldList: []
notShowFieldList: [],
perview_visible: false,
ExternalList: []
}
},
methods: {
async beginScanFiles(e) {
try {
let file = e.target.files[0]
let name = file.name
if (!this.checkFileSuffix(name)) return this.$message.warning(this.$t("trials:researchRecord:ImageManual:message:checkFileSuffix"))
this.loading = true
file = await this.fileToBlob(file)
let scope = this
var index = name.lastIndexOf('.')
var type = name.substring(index + 1, name.length)
let res = await this.OSSclient.put(
`/${this.$route.query.trialId}/InspectionUpload/SiteSurvey/${this.form.TrialSiteId}/${scope.getGuid(Date.now() + '_' + name.split('.')[0])}.${type}`,
file
)
let data = {
Path: this.$getObjectName(res.url),
FileName: name,
FileSize: file.size,
FileType: type
}
res = await addOrUpdateCommonUploadRecord(data)
this.loading = false
if (res.IsSuccess) {
this.form.CommonUploadRecordId = res.Result
data.Id = res.Result
this.form.SiteSurveyFile = data
}
} catch (err) {
console.log(err)
this.loading = false;
}
},
checkFileSuffix(fileName) {
var index = fileName.lastIndexOf('.')
var suffix = fileName.substring(index + 1, fileName.length)
if ('.pdf'.toLocaleLowerCase().search(suffix.toLocaleLowerCase()) === -1 && '.docx'.toLocaleLowerCase().search(suffix.toLocaleLowerCase()) === -1 && '.doc'.toLocaleLowerCase().search(suffix.toLocaleLowerCase()) === -1) {
return false
} else {
return true
}
},
handleIsAuthorizeInput(label) {
this.form.IsAuthorizeRadiologistsParticipate = false
this.form.AssignFixedTechnologists = false
this.form[label] = true
},
async viewManualFile() {
if (!this.form.SiteSurveyFile || !this.form.SiteSurveyFile.Path) return this.$message.warning(this.$t("trials:researchForm:message:notFile"))
this.$preview({
path: this.form.SiteSurveyFile.Path,
type: this.form.SiteSurveyFile.FileType,
title: this.form.SiteSurveyFile.Path.FileName,
})
},
async downLoad() {
if (!this.form.SiteSurveyFile || !this.form.SiteSurveyFile.Path) return this.$message.warning(this.$t("trials:researchForm:message:notFile"))
let link = document.createElement('a')
link.href = this.OSSclientConfig.basePath + this.form.SiteSurveyFile.Path;
link.target = '_blank'
link.download = this.form.SiteSurveyFile.FileName
link.style.display = 'none'
document.body.append(link)
link.click()
document.body.removeChild(link)
link = null
},
async viewManual() {
try {
let data = {
PageIndex: 1,
PageSize: 20,
TrialId: this.$route.query.trialId,
IsPublish: true,
FileTypeCode: 4,
IsDeleted: false
}
let res = await getTrialDocumentList(data)
if (res.IsSuccess) {
const { CurrentPageData } = res.Result
if (CurrentPageData.length <= 0) return this.$message.warning(this.$t("trials:researchRecord:ImageManual:message:noImageManual"))
this.ExternalList = []
CurrentPageData.forEach(item => {
let obj = {
FilePath: item.Path,
FileFormat: 'pdf',
Name: item.Name
}
this.ExternalList.push(obj)
});
// this.perview_visible = true
this.ExternalList.forEach(item => {
let link = document.createElement('a')
link.href = this.OSSclientConfig.basePath + item.FilePath;
link.target = '_blank'
link.download = item.Name
link.style.display = 'none'
document.body.append(link)
link.click()
document.body.removeChild(link)
link = null
})
}
} catch (err) {
console.log(err)
}
},
// 保存基本信息
handleSave(isAutoCommit) {
return new Promise(async(resolve) => {
handleSave(isAutoCommit, isCheck = false) {
return new Promise(async (resolve) => {
isCheck = false
try {
if (this.IsOnlyUploadFile && !isCheck) {
if (!this.form.UserName) {
this.$message.warning(this.$t("trials:researchForm:message:notUserName"))
return resolve(false)
}
if (!this.form.Phone) {
this.$message.warning(this.$t("trials:researchForm:message:notPhone"))
return resolve(false)
}
if (!this.form.CommonUploadRecordId) {
this.$message.warning(this.$t("trials:researchForm:message:notCommonUploadRecordId"))
return resolve(false)
}
this.btnLoading = true
const param = {
id: this.form.Id,
trialId: this.$route.query.trialId,
trialSiteId: this.form.TrialSiteId,
userName: this.form.UserName,
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,
isFollowStudyParameters: this.form.IsFollowStudyParameters,
notFollowReson: this.form.NotFollowReson,
CommonUploadRecordId: this.form.CommonUploadRecordId
}
addOrUpdateTrialSiteSurvey(param).then(res => {
this.btnLoading = false
if (res.IsSuccess && !isAutoCommit) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
resolve(true)
}).catch(() => {
this.btnLoading = false
reject(false)
})
return resolve(true)
}
const valid = await this.$refs['researchBSForm'].validate()
if (valid) {
this.loading = true
@@ -205,11 +473,20 @@ 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,
isFollowStudyParameters: this.form.IsFollowStudyParameters,
notFollowReson: this.form.NotFollowReson
notFollowReson: this.form.NotFollowReson,
CommonUploadRecordId: this.form.CommonUploadRecordId
}
const res = await addOrUpdateTrialSiteSurvey(param)
this.loading = false
@@ -232,7 +509,7 @@ export default {
// 初始化
async initForm(trialInfo, trialSiteSurvey, notShowFieldList) {
// 获取项目下的site
const { Result } = await getTrialSiteSelect(this.$route.query.trialId)
const { Result } = await getTrialSiteSelect(this.$route.query.trialId, { IgnoreDisable: true })
this.siteOptions = Result
this.form.Id = trialSiteSurvey.Id
this.form.Sponsor = trialInfo.Sponsor // 申办方
@@ -246,7 +523,20 @@ export default {
this.form.UserName = trialSiteSurvey.UserName // 联系人
this.form.Phone = trialSiteSurvey.Phone // 联系人电话
this.form.Email = trialSiteSurvey.Email // 联系人邮箱
this.form.CommonUploadRecordId = trialSiteSurvey.CommonUploadRecordId
this.form.SiteSurveyFile = trialSiteSurvey.SiteSurveyFile || {}
this.form.fileStr = this.form.SiteSurveyFile.FileName
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
@@ -267,39 +557,46 @@ export default {
}
</script>
<style lang="scss" scoped>
.research_bs_content{
.research_bs_content {
.basic_content{
.basic_content {
padding: 0 20px;
background: #fff;
}
.login_content{
.login_content {
padding: 5px 20px;
margin-top: 5px;
background: #fff;
::v-deep .el-form-item {
padding-bottom: 20px;
}
}
.code_content{
display:flex;
.code_content {
display: flex;
flex-direction: row;
justify-content: flex-start;
.el-input{
.el-input {
margin-right: 10px;
}
}
.form-label-width{
::v-deep .el-form-item__label{
.form-label-width {
::v-deep .el-form-item__label {
width: 140px;
}
::v-deep .el-form-item__content{
::v-deep .el-form-item__content {
margin-left: 140px;
}
}
.submit_content{
.submit_content {
margin-top: 20px;
text-align: center;
}
@@ -308,10 +605,61 @@ export default {
margin-bottom: 0px;
padding-top: 5px;
border-bottom: 1px solid #f5f7fa;
.el-form-item__content{
.el-form-item__content {
color: #82848a;
}
}
}
.upload {
display: inline-block;
height: 36px;
width: 90px;
padding: 0 10px;
line-height: 23px;
position: relative;
text-decoration: none;
border-radius: 3px;
overflow: hidden;
text-align: center;
background: #428bca;
border-color: #428bca;
color: #fff;
margin-right: 5px;
.select-file {
height: 36px;
width: 90px;
position: absolute;
overflow: hidden;
left: 0;
top: 0;
opacity: 0;
font-size: 0;
}
.btn-select {
//给显示在页面上的按钮写样式
// width: 70px;
height: 36px;
line-height: 36px;
text-align: center;
cursor: pointer;
border-radius: 24px;
overflow: hidden;
position: absolute;
top: 0;
left: 10px;
pointer-events: none; //pointer-events:none用来控制该标签的点击穿透事件
}
}
.file {
::v-deep .el-form-item__content {
display: flex;
align-items: center;
}
}
</style>
@@ -1,59 +1,107 @@
<template>
<div
v-loading="loading"
class="equipment_form_content"
>
<el-form
ref="equipmentForm"
:model="form"
:rules="rules"
label-position="left"
>
<div v-loading="loading" class="equipment_form_content">
<el-form ref="equipmentForm" :model="form" :rules="rules" label-position="left">
<!-- 扫描设备 -->
<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 $d.SiteSurvey_ScanEquipmentType"
:key="item.id"
:label="item.label"
:value="item.id"
/>
</el-select>
<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 style="text-align: center;padding:20px 0px;">
<!-- 取消 -->
<el-button
size="large"
type="primary"
@click="handleCancel"
>
<el-button size="large" type="primary" @click="handleCancel">
{{ $t("common:button:cancel") }}
</el-button>
<!-- 保存 -->
<el-button
size="large"
type="primary"
@click="handleSave"
>
<el-button size="large" type="primary" @click="handleSave">
{{ $t("common:button:save") }}
</el-button>
</div>
@@ -76,29 +124,96 @@ 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'] }
],
},
loading: false,
dictionaryList: {}
}
},
mounted() {
@@ -154,13 +269,15 @@ export default {
}
</script>
<style lang="scss" scoped>
.equipment_form_content{
.equipment_form_content {
padding: 0 10px;
::v-deep .el-form-item {
margin-bottom: 0px;
padding: 5px 0 20px 0;
border-bottom: 1px solid #f5f7fa;
.el-form-item__content{
.el-form-item__content {
color: #82848a;
}
}
@@ -13,18 +13,22 @@
<i class="el-icon-receiving" />
</div>
<div class="p_info">
<div
class="p_info_basic"
:style="{maxWidth:w+'px','white-space': 'nowrap',
overflow: 'hidden',
'text-overflow': 'ellipsis'}"
>
<div class="p_text">{{ item.EquipmentType }}</div>
<div v-if="isShowParameters" class="p_text" style="margin-left:10px">{{ item.Parameters }}</div>
<div class="p_text" style="margin-left:10px">{{ item.ManufacturerName }}</div>
<div class="p_info_basic" :style="{
maxWidth: w + 'px', 'white-space': 'nowrap',
overflow: 'hidden',
'text-overflow': 'ellipsis'
}">
<div class="p_text" v-if="EquipmentControlFieldList.includes('EquipmentTypeEnum')">
{{ item.OtherEquipmentType ? item.OtherEquipmentType :
$fd('SiteSurvey_ScanEquipmentType', item.EquipmentTypeEnum) }}</div>
<div v-if="EquipmentControlFieldList.includes('Parameters')" class="p_text" style="margin-left:10px">{{
item.Parameters }}</div>
<div class="p_text" style="margin-left:10px" v-if="EquipmentControlFieldList.includes('ManufacturerType')">{{
item.ManufacturerName ? item.ManufacturerName : $fd('ManufacturerType',
item.ManufacturerType) }}</div>
</div>
<div class="p_text">{{ item.ScannerType }}</div>
<div class="p_text">{{ item.Note }}</div>
<div class="p_text" v-if="EquipmentControlFieldList.includes('ScannerType')">{{ item.ScannerType }}</div>
<div class="p_text" v-if="EquipmentControlFieldList.includes('Note')">{{ item.Note }}</div>
</div>
<div v-if="state === 0 && userTypeEnumInt === 0 && !isHistory" class="p_func">
<el-button type="text" @click="handleEdit(item)">
@@ -36,19 +40,9 @@
</div>
</div>
<!-- 添加/编辑设备信息 -->
<el-drawer
:title="title"
:visible.sync="formVisible"
direction="btt"
size="70%"
>
<EquipmentForm
v-if="formVisible"
:equipment-info="equipmentInfo"
:is-show-parameters="isShowParameters"
@getList="getList"
@close="close"
/>
<el-drawer :title="title" :visible.sync="formVisible" direction="btt" size="70%">
<EquipmentForm v-if="formVisible" :equipment-info="equipmentInfo"
:EquipmentControlFieldList="EquipmentControlFieldList" @getList="getList" @close="close" />
</el-drawer>
</div>
</template>
@@ -81,7 +75,7 @@ export default {
state: null,
trialSiteSurveyId: '',
trialId: '',
isShowParameters: false
EquipmentControlFieldList: []
}
},
mounted() {
@@ -142,8 +136,11 @@ export default {
console.log(e)
}
},
initList(TrialSiteEquipmentSurveyList, trialSiteSurvey, isShowParameters) {
this.isShowParameters = isShowParameters
initList(TrialSiteEquipmentSurveyList, trialSiteSurvey, EquipmentControlFieldList) {
this.EquipmentControlFieldList = []
EquipmentControlFieldList.forEach(item => {
this.EquipmentControlFieldList.push(item.FiledName)
})
this.list = TrialSiteEquipmentSurveyList
this.state = trialSiteSurvey.State
this.$forceUpdate()
@@ -157,63 +154,70 @@ export default {
}
</script>
<style lang="scss" scoped>
.equipments_content{
.equipments_content {
background: #fff;
padding: 10px;
.title{
.title {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.equipment_info{
.equipment_info {
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
border-top: 1px solid #f5f7fa;
.p_icon{
.p_icon {
width: 70px;
font-size: 25px;
font-weight: bold;
text-align: center;
}
.p_info{
.p_info {
position: relative;
flex:1;
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-evenly;
// border-right: 1px solid #f5f7fa;
font-size: 13px;
}
.p_info:after {
content: '';
position: absolute;
top: 50%;
transform: translateY(-50%);
right: 0;
height: 50px;
width: 1px;
background-color: #f5f7fa;
content: '';
position: absolute;
top: 50%;
transform: translateY(-50%);
right: 0;
height: 50px;
width: 1px;
background-color: #f5f7fa;
}
.p_info_basic{
.p_info_basic {
display: flex;
flex-direction: row;
align-items: center;
}
.p_text{
.p_text {
line-height: 25px;
color: #82848a;
}
.p_func{
.p_func {
width: 80px;
padding: 0 10px;
text-align: center;
}
}
}
</style>
+27 -24
View File
@@ -4,30 +4,22 @@
<div class="d_content">
<!-- 项目基本信息 -->
<BasicInfo ref="basicInfo" />
<BasicInfo ref="basicInfo" :IsOnlyUploadFile="IsOnlyUploadFile" :IsSupportUploadFile="IsSupportUploadFile" />
<!-- 历史人员 -->
<HistoricalParticipants ref="historicalParticipant" class="mt5" />
<!-- 新增人员 -->
<Participants ref="participants" class="mt5" />
<!-- 设备调研 -->
<Equipments ref="equipments" class="mt5" />
<Equipments ref="equipments" class="mt5" v-if="!siteSurveyNoteInfo.IsCloseEquipmentSurvey" />
<!-- 备注 -->
<Notes ref="notes" class="mt5" />
</div>
<div class="d_footer">
<div class="d_func_row">
<div
v-if="state === 0 && userTypeEnumInt === 0"
class="d_func"
@click="handleSave"
>
<div v-if="state === 0 && userTypeEnumInt === 0" class="d_func" @click="handleSave">
{{ $t('common:button:save') }}
</div>
<div
v-if="(state === 0 && userTypeEnumInt === 0)"
class="d_func"
@click="handleSubmit('submit')"
>
<div v-if="(state === 0 && userTypeEnumInt === 0)" class="d_func" @click="handleSubmit('submit')">
{{ $t('trials:researchForm:button:submit') }}
</div>
</div>
@@ -58,7 +50,9 @@ export default {
userTypeEnumInt: 0,
rejectVisible: false,
rejectForm: { reason: '' },
siteSurveyNoteInfo: null
siteSurveyNoteInfo: {},
IsSupportUploadFile: false,
IsOnlyUploadFile: false
}
},
mounted() {
@@ -78,6 +72,9 @@ export default {
if (res.Result.SiteSurveyFiledConfig && res.Result.SiteSurveyFiledConfig.ModifyFiledList.length > 0) {
this.siteSurveyNoteInfo = res.Result.SiteSurveyFiledConfig.ModifyFiledList.find(i => i.NeedModifyFiled === 'SiteSurveyNote')
}
this.IsSupportUploadFile = res.Result.SiteSurveyFiledConfig.IsSupportUploadFile
this.IsOnlyUploadFile = res.Result.SiteSurveyFiledConfig.IsOnlyUploadFile
this.siteSurveyNoteInfo.IsCloseEquipmentSurvey = res.Result.SiteSurveyFiledConfig.IsCloseEquipmentSurvey
this.state = res.Result.TrialSiteSurvey.State
this.$refs['basicInfo'].initForm(res.Result.TrialInfo, res.Result.TrialSiteSurvey, res.Result.SiteSurveyFiledConfig ? res.Result.SiteSurveyFiledConfig.NotShowFieldList : null)
var historicalArr = []
@@ -92,7 +89,7 @@ export default {
this.$refs['historicalParticipant'].initList(historicalArr, res.Result.TrialSiteSurvey)
this.$refs['participants'].initList(newArr, res.Result.TrialSiteSurvey)
this.$refs['equipments'].initList(res.Result.TrialSiteEquipmentSurveyList, res.Result.TrialSiteSurvey, !(res.Result.SiteSurveyFiledConfig && res.Result.SiteSurveyFiledConfig.ModifyFiledList.length > 0))
this.$refs['equipments'].initList(res.Result.TrialSiteEquipmentSurveyList, res.Result.TrialSiteSurvey, res.Result.SiteSurveyFiledConfig.EquipmentControlFieldList)
this.$refs['notes'].initPage(this.siteSurveyNoteInfo)
}
this.loading = false
@@ -129,7 +126,8 @@ export default {
this.userTypeEnumInt === 0 ? this.$t('trials:researchForm:message:submitWarning') : this.$t('trials:researchForm:message:submitWarning2'),
{
type: 'warning',
distinguishCancelAndClose: true
distinguishCancelAndClose: true,
customClass: "mobile_confirm"
}
)
if (confirm !== 'confirm') return
@@ -160,9 +158,10 @@ export default {
}
</script>
<style lang="scss" scoped>
.research_detal_wrapper{
background-color:#f5f7fa;
.d_title{
.research_detal_wrapper {
background-color: #f5f7fa;
.d_title {
margin-bottom: 5px;
line-height: 80px;
font-size: 28px;
@@ -173,10 +172,11 @@ export default {
top: 0;
z-index: 10;
}
// .d_content{
// }
.d_footer{
.d_footer {
position: sticky;
bottom: 0;
z-index: 10;
@@ -185,12 +185,14 @@ export default {
text-align: center;
// padding: 20px;
}
.d_func_row{
.d_func_row {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.d_func{
.d_func {
flex: 1;
margin: 20px;
color: #fff;
@@ -207,15 +209,16 @@ export default {
line-height: 1;
white-space: nowrap;
cursor: pointer;
&:hover{
&:hover {
background: #68a2d5;
border-color: #68a2d5;
color: #fff;
}
}
.mt5{
.mt5 {
margin-top: 5px
}
}
</style>
+240 -140
View File
@@ -1,131 +1,88 @@
<template>
<div class="research_login_m_content">
<div class="title">{{ $t('trials:researchForm:title:question') }}</div>
<el-form
ref="loginForm"
v-loading="loading"
label-position="left"
label-width="120px"
:model="form"
:rules="rules"
>
<div class="basic_content">
<!-- 项目编号 -->
<el-form-item
:label="$t('trials:researchForm:form:trialId')"
>
<span>{{ form.TrialCode }}</span>
</el-form-item>
<!-- 试验方案号 -->
<el-form-item
:label="$t('trials:researchForm:form:researchNo')"
>
<span>{{ form.ResearchProgramNo }}</span>
</el-form-item>
<!-- 试验名称 -->
<el-form-item
:label="$t('trials:researchForm:form:researchName')"
>
<span>{{ form.ExperimentName }}</span>
</el-form-item>
<!-- 适应症类型 -->
<el-form-item
:label="$t('trials:researchForm:form:decleareType')"
>
<span>{{ form.IndicationType }}</span>
</el-form-item>
<div v-if="IsExpired"></div>
<div v-else-if="verify" class="LinkVerificationCode_content">
<el-form ref="codeForm" v-loading="loading" label-position="top" label-width="120px" :model="codeForm"
:rules="code_rules">
<div class="basic_content">
<!-- 项目编号 -->
<el-form-item :label="$t('trials:researchForm:message:LinkVerificationCode')" prop="LinkVerificationCode">
<el-input v-model="codeForm.LinkVerificationCode" autocomplete="new-password" />
</el-form-item>
</div>
</el-form>
<div class="submit_content">
<el-button size="large" type="primary" @click="getLinkVerificationCodeIsEffective">
{{ $t('common:button:submit') }}
</el-button>
</div>
<div class="login_content">
<el-form-item
:label="$t('trials:researchForm:form:siteName')"
prop="TrialSiteId"
>
<el-select
v-model="form.TrialSiteId"
filterable
style="width:100%;"
@change="handleSiteChange"
>
<el-option
v-for="(item,index) of siteOptions"
:key="index"
:label="item.TrialSiteAliasName"
:value="item.TrialSiteId"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="form.TrialSiteId && isHaveSiteSurveyRecord"
label=""
style="text-align:right;"
>
<!-- 更新调研表 -->
<el-link
v-if="!form.IsUpdate"
type="primary"
@click="form.IsUpdate = true"
>
{{ $t('trials:researchForm:button:updateQsForm') }}
</el-link>
<!-- 取消更新调研表 -->
<el-link
v-else
type="primary"
@click="form.IsUpdate = false;form.ReplaceUserEmailOrPhone=''"
>
{{ $t('trials:researchForm:button:cancelUpdateQsForm') }}
</el-link>
</el-form-item>
</div>
<template v-else>
<div class="title">{{ $t('trials:researchForm:title:question') }}</div>
<el-form ref="loginForm" v-loading="loading" label-position="left" label-width="120px" :model="form"
:rules="rules">
<div class="basic_content">
<!-- 项目编号 -->
<el-form-item :label="$t('trials:researchForm:form:trialId')">
<span>{{ form.TrialCode }}</span>
</el-form-item>
<!-- 试验方案号 -->
<el-form-item :label="$t('trials:researchForm:form:researchNo')">
<span>{{ form.ResearchProgramNo }}</span>
</el-form-item>
<!-- 试验名称 -->
<el-form-item :label="$t('trials:researchForm:form:researchName')">
<span>{{ form.ExperimentName }}</span>
</el-form-item>
<!-- 适应症类型 -->
<el-form-item :label="$t('trials:researchForm:form:decleareType')">
<span>{{ form.IndicationType }}</span>
</el-form-item>
</div>
<div class="login_content">
<el-form-item :label="$t('trials:researchForm:form:siteName')" prop="TrialSiteId">
<el-select v-model="form.TrialSiteId" filterable style="width:100%;" @change="handleSiteChange">
<el-option v-for="(item, index) of siteOptions" :key="index" :label="item.TrialSiteAliasName"
:value="item.TrialSiteId" />
</el-select>
</el-form-item>
<el-form-item v-if="form.TrialSiteId && isHaveSiteSurveyRecord" label="" style="text-align:right;">
<!-- 更新调研表 -->
<el-link v-if="!form.IsUpdate" type="primary" @click="form.IsUpdate = true">
{{ $t('trials:researchForm:button:updateQsForm') }}
</el-link>
<!-- 取消更新调研表 -->
<el-link v-else type="primary" @click="form.IsUpdate = false; form.ReplaceUserEmailOrPhone = ''">
{{ $t('trials:researchForm:button:cancelUpdateQsForm') }}
</el-link>
</el-form-item>
<!-- 原调研表填写人邮箱 -->
<el-form-item
v-if="form.IsUpdate"
:label="$t('trials:researchForm:form:originalEmail')"
prop="ReplaceUserEmailOrPhone"
>
<el-input
v-model="form.ReplaceUserEmailOrPhone"
autocomplete="new-password"
/>
</el-form-item>
<!-- 联系邮箱 -->
<el-form-item
:label="$t('trials:researchForm:form:contactorEmail')"
prop="EmailOrPhone"
>
<el-input
v-model="form.EmailOrPhone"
autocomplete="new-password"
/>
</el-form-item>
<el-form-item
:label="$t('trials:researchForm:form:verifyCode')"
>
<div class="code_content">
<el-input
v-model="form.VerificationCode"
autocomplete="new-password"
/>
<el-button
type="primary"
:disabled="sendDisabled || !form.EmailOrPhone || count > 0"
@click="handleSendCode"
>
{{ this.$t('trials:researchForm:button:send') }} {{ sendTitle ? `${sendTitle}` : null }}
<!-- 原调研表填写人邮箱 -->
<el-form-item v-if="form.IsUpdate" :label="$t('trials:researchForm:form:originalEmail')"
prop="ReplaceUserEmailOrPhone">
<el-input v-model="form.ReplaceUserEmailOrPhone" autocomplete="new-password" />
</el-form-item>
<!-- 联系邮箱 -->
<el-form-item :label="$t('trials:researchForm:form:contactorEmail')" prop="EmailOrPhone">
<el-input v-model="form.EmailOrPhone" autocomplete="new-password" />
</el-form-item>
<el-form-item :label="$t('trials:researchForm:form:verifyCode')">
<div class="code_content">
<el-input v-model="form.VerificationCode" autocomplete="new-password" />
<el-button type="primary" :disabled="sendDisabled || !form.EmailOrPhone || count > 0"
@click="handleSendCode">
{{ this.$t('trials:researchForm:button:send') }} {{ sendTitle ? `${sendTitle}` : null }}
</el-button>
</div>
</el-form-item>
<div class="submit_content">
<el-button size="large" type="primary" @click="onSubmit">
{{ $t('common:button:submit') }}
</el-button>
</div>
</el-form-item>
<div class="submit_content">
<el-button
size="large"
type="primary"
@click="onSubmit"
>
{{ $t('common:button:submit') }}
</el-button>
</div>
</div>
</el-form>
</el-form>
</template>
</div>
</template>
<script>
@@ -133,6 +90,10 @@ import { sendVerifyCode, verifySendCode, getTrialSurveyInitInfo } from '@/api/re
import { getUserMenuTree, getUserPermissions } from '@/api/user'
import store from '@/store'
import { mapGetters, mapMutations } from 'vuex'
import {
getLinkLinkExpirationTime,
getLinkVerificationCodeIsEffective
} from '@/api/trials'
export default {
name: 'ResearchMobileLogin',
data() {
@@ -185,6 +146,16 @@ export default {
}
return {
trialId: '',
IsExpired: true,
verify: true,
codeForm: {
LinkVerificationCode: null,
},
code_rules: {
LinkVerificationCode: [
{ required: true, message: this.$t('trials:researchForm:formRule:specify'), trigger: ['blur'] }
],
},
form: {
Sponsor: null, // 申办方
ResearchProgramNo: null, // 方案号
@@ -239,16 +210,119 @@ export default {
])
},
mounted() {
this.$i18n.locale = this.$route.query.lang
this.setLanguage(this.$route.query.lang)
this.$updateDictionary()
if (this.$route.query.trialId) {
this.trialId = this.$route.query.trialId
this.initPage()
let lang = this.$route.query.lang
if (!lang) {
const language = navigator.language
lang = 'en'
if (language.includes("zh")) {
lang = 'zh'
}
}
this.$i18n.locale = lang
this.setLanguage(lang)
this.$updateDictionary()
this.getLinkTimeIsExpired()
},
methods: {
...mapMutations({ setLanguage: 'lang/setLanguage' }),
async getLinkTimeIsExpired() {
try {
let data = {
TrialId: this.$route.query.trialId,
}
let res = await getLinkLinkExpirationTime(data)
if (res.IsSuccess) {
if (res.Result.IsExpired) {
return this.$router.replace('/link_expired')
// return this.$confirm(this.$t("trials:researchForm:confirm:linkIsExpired"), '', {
// type: 'warning'
// })
// return this.IsExpired = true
}
// this.customPrompt()
this.IsExpired = false
}
} catch (err) {
console.log(err)
}
},
async getLinkVerificationCodeIsEffective() {
try {
let validate = await this.$refs.codeForm.validate()
if (!validate) return false
let data = {
TrialId: this.$route.query.trialId,
LinkVerificationCode: this.codeForm.LinkVerificationCode
}
let res = await getLinkVerificationCodeIsEffective(data)
if (res.IsSuccess) {
if (!res.Result.IsEffective) {
return false
}
this.verify = false
if (this.$route.query.trialId) {
this.trialId = this.$route.query.trialId
this.initPage()
}
if (this.$route.query.isUpload) {
this.isUpload = true
this.form.IsUpdate = true
let { email, oldEMail, trialSiteId } = this.$route.query
if (trialSiteId) this.form.TrialSiteId = trialSiteId
if (oldEMail) this.form.ReplaceUserEmailOrPhone = oldEMail
if (email && email !== 'null') {
this.form.EmailOrPhone = email
} else {
this.form.EmailOrPhone = oldEMail
}
if ((email && email !== 'null') || (oldEMail && oldEMail !== 'null')) {
this.isNeedUpload = false
}
}
}
return true
} catch (err) {
return false
console.log(err)
}
},
async customPrompt(name) {
try {
const that = this
// 请输入标记名称
let message = this.$t('trials:researchForm:message:LinkVerificationCode')
const { value } = await this.$prompt(message, '', {
showClose: false,
cancelButtonText: this.$t('common:button:cancel'),
confirmButtonText: this.$t('trials:researchForm:button:saveLinkCode'),
showCancelButton: false,
closeOnClickModal: false,
closeOnPressEscape: false,
inputValue: name,
beforeClose: async (action, instance, done) => {
if (action === 'confirm') {
const value = instance.inputValue
if (!value) {
that.$message.error(this.$t('trials:researchForm:error:noValue'))
} else {
let flag = await this.getLinkVerificationCodeIsEffective(value)
if (!flag) {
that.$message.error(this.$t('trials:researchForm:confirm:isEffective'))
} else {
done()
}
}
} else {
done()
}
}
})
this.getLinkVerificationCodeIsEffective(value)
} catch (err) {
console.log(err)
return null
}
},
// 初始化页面
async initPage() {
try {
@@ -292,8 +366,18 @@ export default {
if (res.IsSuccess) {
zzSessionStorage.clear()
this.$i18n.locale = this.$route.query.lang
this.setLanguage(this.$route.query.lang)
// this.$i18n.locale = this.$route.query.lang
// this.setLanguage(this.$route.query.lang)
let lang = this.$route.query.lang
if (!lang) {
const language = navigator.language
lang = 'en'
if (language.includes("zh")) {
lang = 'zh'
}
}
this.$i18n.locale = lang
this.setLanguage(lang)
store.dispatch('user/setToken', res.Result.Token)
zzSessionStorage.setItem('TokenKey', res.Result.Token)
var permissions = await getUserPermissions()
@@ -363,9 +447,10 @@ export default {
}
</script>
<style lang="scss" scoped>
.research_login_m_content{
background-color:#f5f7fa;
.title{
.research_login_m_content {
background-color: #f5f7fa;
.title {
margin-bottom: 5px;
line-height: 80px;
font-size: 28px;
@@ -373,27 +458,33 @@ export default {
text-align: center;
background: #fff;
}
.basic_content{
.basic_content {
padding: 0 20px;
background: #fff;
}
.login_content{
.login_content {
padding: 5px 20px;
margin-top: 5px;
background: #fff;
::v-deep .el-form-item {
padding-bottom: 20px;
}
}
.code_content{
display:flex;
.code_content {
display: flex;
flex-direction: row;
justify-content: flex-start;
.el-input{
.el-input {
margin-right: 10px;
}
}
.submit_content{
.submit_content {
margin-top: 20px;
text-align: center;
}
@@ -402,11 +493,20 @@ export default {
margin-bottom: 0px;
padding-top: 5px;
border-bottom: 1px solid #f5f7fa;
.el-form-item__content{
.el-form-item__content {
color: #82848a;
}
}
}
.LinkVerificationCode_content {
position: fixed;
top: 40%;
bottom: 40%;
right: 0;
left: 0;
}
</style>
@@ -412,6 +412,7 @@ export default {
//
handleSave(isAutoCommit, isCheck = false) {
return new Promise((resolve, reject) => {
isCheck = false
if (this.IsOnlyUploadFile && !isCheck) {
if (!this.form.UserName) {
this.$message.warning(this.$t("trials:researchForm:message:notUserName"))
@@ -191,7 +191,6 @@ export default {
EquipmentControlFieldList.forEach(item => {
this.EquipmentControlFieldList.push(item.FiledName)
})
console.log(this.EquipmentControlFieldList, 'this.EquipmentControlFieldList')
this.list = TrialSiteEquipmentSurveyList
this.state = trialSiteSurvey.State
this.$forceUpdate()
+93
View File
@@ -0,0 +1,93 @@
<template>
<div class="log-list">
<el-table v-loading="loading" :data="tableData" v-adaptive="{ bottomOffset: 40 }" height="100" style="width: 100%">
<el-table-column prop="CreateTime" :label="$t('trials:researchForm:logList:label:CreateTime')" min-width="120"
show-overflow-tooltip>
<template slot-scope="scope">
{{ scope.row.CreateTime ? moment(scope.row.CreateTime).format('YYYY-MM-DD HH:mm:ss') : '' }}
</template>
</el-table-column>
<el-table-column prop="Name" :label="$t('trials:researchForm:logList:label:Name')" min-width="100"
show-overflow-tooltip>
</el-table-column>
<el-table-column prop="UserType" :label="$t('trials:researchForm:logList:label:UserType')" min-width="100"
show-overflow-tooltip>
<template slot-scope="scope">
{{ scope.row.UserType && $fd('UserType', scope.row.UserType) ? $fd('UserType', scope.row.UserType) : '--' }}
</template>
</el-table-column>
<el-table-column prop="OptType" :label="$t('trials:researchForm:logList:label:OptType')" min-width="100"
show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('SiteSurveyOptTyp', scope.row.OptType) }}
</template>
</el-table-column>
<el-table-column prop="Json" :label="$t('trials:researchForm:logList:label:Json')" min-width="100"
show-overflow-tooltip>
<template slot-scope="scope">
<el-button v-if="scope.row.Json" type="text" @click="openLog(scope.row)">{{
$t('trials:researchForm:logList:label:SurveyForm') }}</el-button>
</template>
</el-table-column>
<el-table-column prop="Reason" :label="$t('trials:researchForm:logList:label:Reason')" min-width="140"
show-overflow-tooltip>
</el-table-column>
</el-table>
</div>
</template>
<script>
import { getSiteSurveyLogList } from '@/api/research'
import moment from 'moment'
import { getToken } from '@/utils/auth'
export default {
name: 'LogList',
props: {
trialSiteSurveyId: {
type: String,
required: true
}
},
data() {
return {
loading: false,
tableData: [],
moment,
openWindow: null
}
},
mounted() {
this.getList()
},
methods: {
async getList() {
try {
this.loading = true
let res = await getSiteSurveyLogList({ trialSiteSurveyId: this.trialSiteSurveyId })
if (res.IsSuccess) {
this.tableData = res.Result
}
} catch (e) {
console.log(e)
} finally {
this.loading = false
}
},
openLog(row) {
if (this.openWindow) {
this.openWindow.close()
}
var token = getToken()
var path = `/researchLog?trialId=${this.$route.query.trialId}&trialSiteSurveyId=${this.trialSiteSurveyId}&logId=${row.Id}&TokenKey=${token}`
this.openWindow = window.open(path, '_blank')
}
}
}
</script>
<style lang="scss" scoped>
.log-list {
height: 100%;
min-height: 0;
overflow: hidden;
}
</style>
+157 -35
View File
@@ -35,6 +35,10 @@
<el-button v-if="userTypeEnumInt === 0" type="primary" size="small" @click="handleHistory">
{{ $t('trials:researchForm:button:historicalRecord') }}
</el-button>
<!-- 操作日志 -->
<el-button type="primary" size="small" v-if="!isLog" @click="viewLog">
{{ $t('trials:researchForm:button:log') }}
</el-button>
<!-- 退出 -->
<el-button v-if="userTypeEnumInt === 0" type="primary" size="small" @click="handleBack">
{{ $t('trials:researchForm:button:loginOut') }}
@@ -133,16 +137,37 @@
</div>
</div>
</span>
<div style="height:100%;margin:0;">
<div class="history-dialog-content">
<HistoricalRecord :trial-id="trialId" :site-id="siteId" :trial-site-survey-id="trialSiteSurveyId" />
</div>
</el-dialog>
<el-dialog v-if="logVisible" :visible.sync="logVisible"
:custom-class="isFullscreen ? 'full-log-dialog-container' : 'log-dialog-container'" :close-on-click-modal="false"
:fullscreen="isFullscreen" :show-close="false" width="60%" append-to-body>
<span slot="title" class="dialog-footer">
<div style="display: flex;flex-direction: row;justify-content: space-between;">
<div>
{{ $t('trials:researchForm:button:log') }}
</div>
<div>
<svg-icon :icon-class="isFullscreen ? 'exit-fullscreen' : 'fullscreen'"
style="vertical-align: baseline;cursor: pointer;font-size: 20px;" @click="toggleLogFullscreen" />
<svg-icon icon-class="dClose" style="cursor: pointer;font-size: 25px;margin-left: 10px;"
@click="logVisible = false" />
</div>
</div>
</span>
<div class="log-dialog-content">
<LogList :trial-site-survey-id="trialSiteSurveyId" />
</div>
</el-dialog>
</div>
</template>
<script>
import { getSiteSurveyInfo, trialSurveySubmit, submissionRejection } from '@/api/research'
import { getQueryString } from '@/utils/history.js'
import { getSiteSurveyInfo, trialSurveySubmit, submissionRejection, getSiteSurveyLogList } from '@/api/research'
import { getQueryString, changeURLStatic } from '@/utils/history.js'
import BaseInfo from './components/BaseInfo'
import HistoricalParticipant from './components/HistoricalParticipant'
import ParticipantList from './components/ParticipantList'
@@ -150,9 +175,11 @@ import EquipmentList from './components/EquipmentList'
import { mapMutations } from 'vuex'
import TopLang from './topLang'
import HistoricalRecord from './components/HistoricalRecord'
import LogList from './components/LogList'
import store from '@/store'
export default {
name: 'QuestionForm',
components: { BaseInfo, HistoricalParticipant, ParticipantList, EquipmentList, TopLang, HistoricalRecord },
components: { BaseInfo, HistoricalParticipant, ParticipantList, EquipmentList, TopLang, HistoricalRecord, LogList },
props: {
isPreview: {
type: Boolean,
@@ -175,7 +202,10 @@ export default {
historyVisible: false,
siteSurveyNoteInfo: {},
IsSupportUploadFile: false,
IsOnlyUploadFile: false
IsOnlyUploadFile: false,
logVisible: false,
isLog: false,
logId: null
}
},
mounted() {
@@ -191,6 +221,12 @@ export default {
this.setLanguage(lang)
this.$updateDictionary()
this.trialSiteSurveyId = getQueryString('trialSiteSurveyId')
this.logId = getQueryString('logId')
if (this.logId) this.isLog = true
if (this.$router.currentRoute.query.TokenKey) {
store.dispatch('user/setToken', this.$router.currentRoute.query.TokenKey)
changeURLStatic('TokenKey', '')
}
this.initPage()
if (zzSessionStorage.getItem('userTypeEnumInt')) {
this.userTypeEnumInt = zzSessionStorage.getItem('userTypeEnumInt') * 1
@@ -198,7 +234,7 @@ export default {
},
computed: {
isPM() {
return this.hasPermi(['role:pm', 'role:spm', 'role:cpm', 'role:apm']) && this.state !== 3
return this.hasPermi(['role:pm', 'role:spm', 'role:cpm', 'role:apm']) && this.state !== 3 && this.IsOnlyUploadFile
}
},
methods: {
@@ -206,36 +242,75 @@ export default {
//
initPage() {
this.loading = true
getSiteSurveyInfo(this.trialId, this.trialSiteSurveyId).then(res => {
if (res.Result) {
// this.trialSiteSurveyEquipmentType = res.Result.TrialInfo.TrialSiteSurveyEquipmentType
// this.trialSiteSurveyUserRoles = res.Result.TrialInfo.TrialSiteSurveyUserRoles
if (res.Result.SiteSurveyFiledConfig && res.Result.SiteSurveyFiledConfig.ModifyFiledList.length > 0) {
this.siteSurveyNoteInfo = res.Result.SiteSurveyFiledConfig.ModifyFiledList.find(i => i.NeedModifyFiled === 'SiteSurveyNote')
}
this.IsSupportUploadFile = res.Result.SiteSurveyFiledConfig.IsSupportUploadFile
this.IsOnlyUploadFile = res.Result.SiteSurveyFiledConfig.IsOnlyUploadFile
this.siteSurveyNoteInfo.IsCloseEquipmentSurvey = res.Result.SiteSurveyFiledConfig.IsCloseEquipmentSurvey
this.state = res.Result.TrialSiteSurvey.State
this.siteId = res.Result.TrialSiteSurvey.TrialSiteId
this.$refs['baseResearchInfo'].initForm(res.Result.TrialInfo, res.Result.TrialSiteSurvey, res.Result.SiteSurveyFiledConfig ? res.Result.SiteSurveyFiledConfig.NotShowFieldList : null)
var historicalArr = []
var newArr = []
res.Result.TrialSiteUserSurveyList.map(i => {
if (i.IsHistoryUser) {
historicalArr.push(i)
} else {
newArr.push(i)
if (this.isLog) {
getSiteSurveyLogList({ trialSiteSurveyId: this.trialSiteSurveyId, Id: this.logId }).then(r => {
if (r.Result && r.Result[0].Json) {
let res = {
Result: JSON.parse(r.Result[0].Json)
}
})
if (res.Result) {
// this.trialSiteSurveyEquipmentType = res.Result.TrialInfo.TrialSiteSurveyEquipmentType
// this.trialSiteSurveyUserRoles = res.Result.TrialInfo.TrialSiteSurveyUserRoles
if (res.Result.SiteSurveyFiledConfig && res.Result.SiteSurveyFiledConfig.ModifyFiledList.length > 0) {
this.siteSurveyNoteInfo = res.Result.SiteSurveyFiledConfig.ModifyFiledList.find(i => i.NeedModifyFiled === 'SiteSurveyNote')
}
this.IsSupportUploadFile = res.Result.SiteSurveyFiledConfig.IsSupportUploadFile
this.IsOnlyUploadFile = res.Result.SiteSurveyFiledConfig.IsOnlyUploadFile
this.siteSurveyNoteInfo.IsCloseEquipmentSurvey = res.Result.SiteSurveyFiledConfig.IsCloseEquipmentSurvey
this.state = 3
this.siteId = res.Result.TrialSiteSurvey.TrialSiteId
this.$refs['baseResearchInfo'].initForm(res.Result.TrialInfo, res.Result.TrialSiteSurvey, res.Result.SiteSurveyFiledConfig ? res.Result.SiteSurveyFiledConfig.NotShowFieldList : null)
var historicalArr = []
var newArr = []
res.Result.TrialSiteUserSurveyList.map(i => {
if (i.IsHistoryUser) {
historicalArr.push(i)
} else {
newArr.push(i)
}
})
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.EquipmentControlFieldList)
this.isExistIncorrect = res.Result.TrialSiteUserSurveyList.every(item => item.IsCorrect === false)
}
this.loading = false
}
}).catch((err) => { console.log(err); this.loading = false })
} else {
getSiteSurveyInfo(this.trialId, this.trialSiteSurveyId).then(res => {
if (res.Result) {
// this.trialSiteSurveyEquipmentType = res.Result.TrialInfo.TrialSiteSurveyEquipmentType
// this.trialSiteSurveyUserRoles = res.Result.TrialInfo.TrialSiteSurveyUserRoles
if (res.Result.SiteSurveyFiledConfig && res.Result.SiteSurveyFiledConfig.ModifyFiledList.length > 0) {
this.siteSurveyNoteInfo = res.Result.SiteSurveyFiledConfig.ModifyFiledList.find(i => i.NeedModifyFiled === 'SiteSurveyNote')
}
this.IsSupportUploadFile = res.Result.SiteSurveyFiledConfig.IsSupportUploadFile
this.IsOnlyUploadFile = res.Result.SiteSurveyFiledConfig.IsOnlyUploadFile
this.siteSurveyNoteInfo.IsCloseEquipmentSurvey = res.Result.SiteSurveyFiledConfig.IsCloseEquipmentSurvey
this.state = res.Result.TrialSiteSurvey.State
this.siteId = res.Result.TrialSiteSurvey.TrialSiteId
this.$refs['baseResearchInfo'].initForm(res.Result.TrialInfo, res.Result.TrialSiteSurvey, res.Result.SiteSurveyFiledConfig ? res.Result.SiteSurveyFiledConfig.NotShowFieldList : null)
var historicalArr = []
var newArr = []
res.Result.TrialSiteUserSurveyList.map(i => {
if (i.IsHistoryUser) {
historicalArr.push(i)
} else {
newArr.push(i)
}
})
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.EquipmentControlFieldList)
this.isExistIncorrect = res.Result.TrialSiteUserSurveyList.every(item => item.IsCorrect === false)
}
this.loading = false
}).catch(() => { this.loading = false })
}
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.EquipmentControlFieldList)
this.isExistIncorrect = res.Result.TrialSiteUserSurveyList.every(item => item.IsCorrect === false)
}
this.loading = false
}).catch(() => { this.loading = false })
},
//
handleSubmit(type) {
@@ -347,7 +422,14 @@ export default {
handleHistory() {
this.isFullscreen = false
this.historyVisible = true
}
},
toggleLogFullscreen() {
this.isFullscreen = !this.isFullscreen
},
viewLog() {
this.isFullscreen = false
this.logVisible = true
},
}
}
</script>
@@ -399,5 +481,45 @@ export default {
}
}
.history-dialog-content,
.log-dialog-content {
height: 100%;
margin: 0;
}
.log-dialog-content {
overflow: hidden;
}
}
</style>
<style lang="scss">
.full-log-dialog-container,
.log-dialog-container {
overflow: hidden;
}
.full-log-dialog-container {
display: flex;
flex-direction: column;
}
.full-log-dialog-container .el-dialog__body {
flex: 1;
min-height: 0;
padding: 10px;
overflow: hidden;
}
.log-dialog-container .el-dialog__body {
height: calc(100% - 80px);
padding: 10px;
overflow: hidden;
}
.full-log-dialog-container .log-dialog-content,
.log-dialog-container .log-dialog-content {
height: 100%;
overflow: hidden;
}
</style>
-22
View File
@@ -155,17 +155,6 @@ export default {
},
],
IdCard: [
{
validator: (rule, value, callback) => {
let reg = new RegExp(/(^\d{15}$)|(^\d{17}([0-9]|X)$)/, 'ig')
if (value && !reg.test(value)) {
callback(new Error(this.$t('common:ruleMessage:pattern')))
} else {
callback()
}
},
trigger: 'blur',
},
{
max: 400,
message: this.$t('form:rules:maxLength:400'),
@@ -173,17 +162,6 @@ export default {
},
],
BankPhoneNum: [
{
validator: (rule, value, callback) => {
let reg = new RegExp(/^\d{11}$/, 'i')
if (value && !reg.test(value)) {
callback(new Error(this.$t('common:ruleMessage:pattern')))
} else {
callback()
}
},
trigger: 'blur',
},
{
max: 400,
message: this.$t('form:rules:maxLength:400'),
+2 -2
View File
@@ -21,7 +21,7 @@
:maxlength="400"
/>
</el-form-item>
<el-form-item
<!-- <el-form-item
:label="$t('system:Setting:label:Blind NameCN')"
prop="BlindNameCN"
>
@@ -30,7 +30,7 @@
style="width: 300px"
:maxlength="400"
/>
</el-form-item>
</el-form-item> -->
<el-form-item
:label="$t('system:Setting:label:Blind Publications')"
prop="BlindPublications"
@@ -211,17 +211,6 @@ export default {
},
],
IdCard: [
{
validator: (rule, value, callback) => {
let reg = new RegExp(/(^\d{15}$)|(^\d{17}([0-9]|X)$)/, 'ig')
if (value && !reg.test(value)) {
callback(new Error(this.$t('common:ruleMessage:pattern')))
} else {
callback()
}
},
trigger: 'blur',
},
{
max: 400,
message: this.$t('form:rules:maxLength:400'),
@@ -229,17 +218,6 @@ export default {
},
],
BankPhoneNum: [
{
validator: (rule, value, callback) => {
let reg = new RegExp(/^\d{11}$/, 'i')
if (value && !reg.test(value)) {
callback(new Error(this.$t('common:ruleMessage:pattern')))
} else {
callback()
}
},
trigger: 'blur',
},
{
max: 400,
message: this.$t('form:rules:maxLength:400'),
@@ -19,12 +19,12 @@
>
{{ DATA.BlindName }}
</el-form-item>
<el-form-item
<!-- <el-form-item
:label="$t('system:Setting:label:Blind NameCN')"
style="width: 45%"
>
{{ DATA.BlindNameCN }}
</el-form-item>
</el-form-item> -->
<div style="display: flex; font-size: 14px; margin-bottom: 22px">
<span style="display: inline-block; width: 120px; color: #000">{{
$t('system:Setting:label:Blind Publications')
@@ -109,19 +109,19 @@
<div class="form_title">
{{ $t('system:Setting:title:Blinded information') }}
</div>
<el-row>
<el-col :span="12">
<el-form-item
:label="$t('system:Setting:label:Blind Name')"
prop="BlindName"
>
<el-input
v-model="form.BlindName"
clearable
:maxlength="400"
></el-input>
</el-form-item>
</el-col>
<!-- <el-row>
<el-col :span="12"> -->
<el-form-item
:label="$t('system:Setting:label:Blind Name')"
prop="BlindName"
>
<el-input
v-model="form.BlindName"
clearable
:maxlength="400"
></el-input>
</el-form-item>
<!-- </el-col>
<el-col :span="12">
<el-form-item
:label="$t('system:Setting:label:Blind NameCN')"
@@ -134,7 +134,7 @@
></el-input>
</el-form-item>
</el-col>
</el-row>
</el-row> -->
<el-form-item
:label="$t('system:Setting:label:Blind Publications')"
prop="BlindPublications"
@@ -591,32 +591,9 @@
</div>
</div>
<div class="file" v-if="isAll && hasFile">
<div
class="curriculum"
v-if="
(resumeListCN && resumeListCN.length > 0) ||
(resumeListEN && resumeListEN.length > 0)
"
>
<div class="curriculum" v-if="resumeListEN && resumeListEN.length > 0">
<div class="title">{{ $t('curriculumVitae:curriculum:title') }}</div>
<div class="fileBox">
<div
class="file_title"
v-if="resumeListCN && resumeListCN.length > 0"
>
{{ $t('curriculumVitae:curriculum:fileTitle:CN') }}
</div>
<template v-if="true">
<div class="file" v-for="item in resumeListCN" :key="item.Id">
<div class="name" :title="item.FileName">{{ item.FileName }}</div>
<i
class="el-icon-download"
:title="$t('common:button:download')"
@click.stop="handlePreview(item)"
/>
</div>
</template>
<div class="noData" v-else>{{ $t('curriculumVitae:noData') }}</div>
<div
class="file_title"
v-if="resumeListEN && resumeListEN.length > 0"
+2 -2
View File
@@ -154,8 +154,8 @@ import NoticeForm from './components/from'
import Pagination from '@/components/Pagination'
const searchDataDefault = () => {
return {
Asc: true,
SortField: '',
Asc: false,
SortField: 'PublishedTime',
NoticeContent: null,
FileName: null,
NoticeTypeEnum: null,
+18 -18
View File
@@ -178,7 +178,7 @@ export default {
updateUser(this.user)
.then((res) => {
this.isDisabled = false
this.$message.success('Updated successfully')
this.$message.success(this.$t("common:message:updatedSuccessfully"))
})
.catch(() => {
this.isDisabled = false
@@ -190,7 +190,7 @@ export default {
this.user.Id = res.Result.Id
this.user.UserCode = res.Result.UserCode
this.$emit('getUserId', res.Result.Id)
this.$message.success('Added successfully')
this.$message.success(this.$t("common:message:addedSuccessfully"))
this.$router.push({ path: '/system/user/list' })
})
.catch(() => {
@@ -308,63 +308,63 @@ export default {
UserWorkLanguage: [
{
required: true,
message: 'Please Select',
message: this.$t("common:ruleMessage:select"),
trigger: ['blur', 'change'],
},
],
Roles: [
{
required: true,
message: 'Please Select',
message: this.$t("common:ruleMessage:select"),
trigger: ['blur', 'change'],
},
],
IsZhiZhun: [
{
required: true,
message: 'Please Select',
message: this.$t("common:ruleMessage:select"),
trigger: ['blur', 'change'],
},
],
OrganizationName: [
{ required: true, message: 'Please specify', trigger: 'blur' },
{ required: true, message: this.$t("common:ruleMessage:specify"), trigger: 'blur' },
],
LastName: [
{ required: true, message: 'Please specify', trigger: 'blur' },
{ max: 50, message: 'The maximum length is 50' },
{ required: true, message: this.$t("common:ruleMessage:specify"), trigger: 'blur' },
{ max: 50, message: this.$t("trials:researchForm:formRule:maxLength") },
],
FirstName: [
{ required: true, message: 'Please specify', trigger: 'blur' },
{ max: 50, message: 'The maximum length is 50' },
{ required: true, message: this.$t("common:ruleMessage:specify"), trigger: 'blur' },
{ max: 50, message: this.$t("trials:researchForm:formRule:maxLength") },
],
Phone: [
{
max: 20,
min: 7,
message: 'The length is 7 to 20',
message: this.$t("system:userInfo:ruleMessage:length7to20"),
trigger: ['blur'],
},
],
EMail: [
{
required: true,
message: 'Please input the email address',
message: this.$t("common:ruleMessage:specify"),
trigger: 'blur',
},
{
// type: 'email',
pattern: new RegExp(this.$reg().EmailRegexStr),
message: 'Please input the correct email address',
message: this.$t("trials:site:ruleMessage:correctEmail"),
trigger: ['blur'],
},
{ max: 50, message: 'The maximum length is 50' },
{ max: 50, message: this.$t("trials:researchForm:formRule:maxLength") },
],
Sex: [{ required: true, message: 'Please specify', trigger: 'blur' }],
Sex: [{ required: true, message: this.$t("common:ruleMessage:specify"), trigger: 'blur' }],
Status: [
{ required: true, message: 'Please specify', trigger: 'blur' },
{ required: true, message: this.$t("common:ruleMessage:specify"), trigger: 'blur' },
],
DepartmentName: [{ max: 50, message: 'The maximum length is 50' }],
PositionName: [{ max: 50, message: 'The maximum length is 50' }],
DepartmentName: [{ max: 50, message: this.$t("trials:researchForm:formRule:maxLength") }],
PositionName: [{ max: 50, message: this.$t("trials:researchForm:formRule:maxLength") }],
},
userTypeOptions: [],
isDisabled: false,
@@ -1,12 +1,10 @@
<template>
<div class="trials-navbar" style="position: relative">
<div class="leftMenu">
<img v-if="NODE_ENV === 'usa'" src="@/assets/title-logo.png" alt="" class="title-logo" />
<img v-else src="@/assets/system.png" alt="" style="width: 150px;height: 50px;margin-bottom: -7px;" />
<span style="white-space: nowrap" v-if="NODE_ENV !== 'usa'">
<!-- 中心影像系统EICS -->
<img src="@/assets/title-logo.png" alt="" class="title-logo" />
<!-- <span style="white-space: nowrap" v-if="NODE_ENV !== 'usa'">
{{ $t('trials:trials:title:eics') }}
</span>
</span> -->
</div>
<div class="center-menu">
<NoticeMarquee />
@@ -0,0 +1,311 @@
<template>
<BaseContainer>
<!-- 搜索框 -->
<template slot="search-container">
<el-form :inline="true">
<!-- 中心编号 -->
<el-form-item :label="$t('trials:researchStaff:table:siteId')">
<el-select v-model="searchData.TrialSiteId" clearable filterable style="width:120px;">
<el-option v-for="(item, index) of siteOptions" :key="index" :label="item.TrialSiteCode"
:value="item.TrialSiteId" />
</el-select>
</el-form-item>
<!-- 中心名称 -->
<el-form-item :label="$t('trials:researchStaff:table:siteName')">
<el-input v-model="searchData.TrialSiteName" class="mr" clearable style="width:120px;" />
</el-form-item>
<!-- 状态 -->
<el-form-item :label="$t('trials:researchRecord:table:status')">
<el-select v-model="searchData.State" clearable filterable style="width: 120px">
<el-option v-for="(item, index) of $d.ResearchRecord" :key="index" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<!-- 是否废除 -->
<!-- <el-form-item :label="$t('trials:researchRecord:table:isDeleted')">
<el-select v-model="searchData.IsDeleted" clearable filterable style="width: 120px">
<el-option v-for="item of $d.YesOrNo" v-show="item.raw.ValueCN !== '无'" :key="`IsDeleted${item.value}`"
:label="item.label" :value="item.value" />
</el-select>
</el-form-item> -->
<el-form-item>
<!-- 查询 -->
<el-button type="primary" icon="el-icon-search" @click="handleSearch">
{{ $t('common:button:search') }}
</el-button>
</el-form-item>
<!-- 重置 -->
<el-form-item>
<el-button type="primary" icon="el-icon-refresh-left" @click="handleReset">
{{ $t('common:button:reset') }}
</el-button>
</el-form-item>
<!-- 导出 -->
<el-form-item>
<el-button type="primary" icon="el-icon-download" :disabled="list.length === 0" @click="handleExport">
{{ $t('common:button:export') }}
</el-button>
</el-form-item>
</el-form>
</template>
<template slot="main-container">
<el-table ref="equipmentsList" v-loading="loading" v-adaptive="{ bottomOffset: 55 }" :data="list" stripe
height="100" @sort-change="handleSortByColumn">
<el-table-column type="index" width="50" />
<!-- 中心编号 -->
<el-table-column prop="TrialSiteCode" :label="$t('trials:researchStaff:table:siteId')" show-overflow-tooltip
sortable="custom" min-width="100">
</el-table-column>
<!-- 中心名称 -->
<el-table-column prop="TrialSiteName" :label="$t('trials:researchStaff:table:siteName')" show-overflow-tooltip
sortable="custom" min-width="120">
</el-table-column>
<!-- 状态 -->
<el-table-column prop="State" :label="$t('trials:researchRecord:table:status')" min-width="150"
sortable="custom" show-overflow-tooltip>
<template slot-scope="scope">
<el-tag v-if="scope.row.State === 0" type="primary">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
<el-tag v-if="scope.row.State === 1" type="info">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
<el-tag v-if="scope.row.State === 2" type="warning">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
<el-tag v-if="scope.row.State === 3" type="danger">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
</template>
</el-table-column>
<!-- 是否废除 -->
<!-- <el-table-column prop="IsDeleted" :label="$t('trials:researchRecord:table:isDeleted')" min-width="100"
sortable="custom" show-overflow-tooltip>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsDeleted" type="danger">{{
$fd('YesOrNo', scope.row.IsDeleted)
}}</el-tag>
<el-tag v-else type="primary">{{
$fd('YesOrNo', scope.row.IsDeleted)
}}</el-tag>
</template>
</el-table-column> -->
<!-- 联系人 -->
<el-table-column prop="UserName" :label="$t('trials:researchForm:form:contactor')" show-overflow-tooltip
sortable="custom" min-width="100" />
<!-- 联系电话 -->
<el-table-column prop="Phone" :label="$t('trials:researchForm:form:contactorPhone')" show-overflow-tooltip
sortable="custom" min-width="100" />
<!-- 联系邮箱 -->
<el-table-column prop="Email" :label="$t('trials:researchForm:form:contactorEmail')" show-overflow-tooltip
sortable="custom" min-width="100" />
<template v-if="equipmentControlFieldList.includes('EquipmentTypeEnum')">
<!-- 扫描设备 -->
<el-table-column 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>
</template>
<template v-if="equipmentControlFieldList.includes('Parameters')">
<!-- 扫描参数 -->
<el-table-column prop="Parameters" :label="$t('trials:equiptResearch:form:param')" min-width="100"
show-overflow-tooltip />
</template>
<template v-if="equipmentControlFieldList.includes('ManufacturerType')">
<!-- 扫描仪器制造商名称 -->
<el-table-column 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>
</template>
<template v-if="equipmentControlFieldList.includes('ScannerType')">
<!-- 扫描仪型号 -->
<el-table-column min-width="120" prop="ScannerType" :label="$t('trials:equiptResearch:form:model')"
show-overflow-tooltip />
</template>
<template v-if="equipmentControlFieldList.includes('MagneticFieldStrengthType')">
<!-- 磁场强度 -->
<el-table-column 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>
</template>
<template v-if="equipmentControlFieldList.includes('BodyCoilChannelCount')">
<!-- 体部线圈通道数 -->
<el-table-column 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>
</template>
<template v-if="equipmentControlFieldList.includes('HasDedicatedPdfFatQuantificationSequence')">
<!-- 是否具备专用的PDFF脂肪定量序列CSE-MRI序列 -->
<el-table-column 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>
</template>
<template v-if="equipmentControlFieldList.includes('PdfFatQuantificationSequenceType')">
<!-- 专用的PDFF脂肪定量序列类型 -->
<el-table-column 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>
</template>
<template v-if="equipmentControlFieldList.includes('HasT2R2Correction')">
<!-- 是否包含 T2/R2 校正用于铁沉积校正 -->
<el-table-column 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>
</template>
<template v-if="equipmentControlFieldList.includes('CanFullyExportPdfParameterMapsAndRawDicom')">
<!-- 是否可完整导出 PDFF 参数图及全部原始 DICOM 数据 -->
<el-table-column 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>
</template>
<template v-if="equipmentControlFieldList.includes('Note')">
<!-- 备注 -->
<el-table-column min-width="120" prop="Note" :label="$t('trials:equiptResearch:form:remark')"
show-overflow-tooltip />
</template>
</el-table>
<!-- 分页组件 -->
<pagination class="page" :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize"
@pagination="getList" />
</template>
</BaseContainer>
</template>
<script>
import { getTrialSiteSelect } from '@/api/trials'
import { getSiteSurveyEquipmentList } from '@/api/research'
import BaseContainer from '@/components/BaseContainer'
import Pagination from '@/components/Pagination'
import { getSiteSurveyEquipmentList_Export } from '@/api/export'
const searchDataDefault = () => {
return {
SortField: '',
State: "",
Asc: true,
PageIndex: 1,
PageSize: 20,
TrialId: '',
TrialSiteId: '',
TrialSiteName: ''
}
}
export default {
name: 'Equipments',
components: { BaseContainer, Pagination },
data() {
return {
searchData: searchDataDefault(),
loading: false,
list: [],
total: 0,
trialId: '',
otherInfo: {},
siteOptions: [],
equipmentControlFieldList: []
}
},
mounted() {
this.trialId = this.$route.query.trialId
this.getList()
this.getSite()
},
methods: {
async handleExport() {
await getSiteSurveyEquipmentList_Export(this.searchData)
},
async getList() {
try {
this.loading = true
this.searchData.TrialId = this.trialId
let res = await getSiteSurveyEquipmentList(this.searchData)
if (res.IsSuccess) {
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
this.otherInfo = res.OtherInfo
const { EquipmentControlFieldList } = res.OtherInfo
this.equipmentControlFieldList = []
EquipmentControlFieldList.forEach(item => {
this.equipmentControlFieldList.push(item.FiledName)
})
this.$nextTick(() => {
this.$refs.equipmentsList.doLayout()
})
}
} catch (e) {
console.log(e)
} finally {
this.loading = false
}
},
// site
async getSite() {
try {
let res = await getTrialSiteSelect(this.trialId)
this.siteOptions = res.Result
} catch (e) {
console.log(e)
}
},
//
handleReset() {
this.searchData = searchDataDefault()
this.getList()
this.$nextTick(() => {
this.$refs.list.clearSort()
})
},
//
handleSearch() {
this.getList()
},
//
handleSortByColumn(column) {
if (column.order === 'ascending') {
this.searchData.Asc = true
} else {
this.searchData.Asc = false
}
this.searchData.SortField = column.prop
this.getList()
}
}
}
</script>
@@ -0,0 +1,356 @@
<template>
<BaseContainer>
<!-- 搜索框 -->
<template slot="search-container">
<el-form :inline="true">
<!-- 中心编号 -->
<el-form-item :label="$t('trials:researchStaff:table:siteId')">
<el-select v-model="searchData.TrialSiteId" clearable filterable style="width:120px;">
<el-option v-for="(item, index) of siteOptions" :key="index" :label="item.TrialSiteCode"
:value="item.TrialSiteId" />
</el-select>
</el-form-item>
<!-- 中心名称 -->
<el-form-item :label="$t('trials:researchStaff:table:siteName')">
<el-input v-model="searchData.TrialSiteName" class="mr" clearable style="width:120px;" />
</el-form-item>
<!-- 状态 -->
<el-form-item :label="$t('trials:researchRecord:table:status')">
<el-select v-model="searchData.State" clearable filterable style="width: 120px">
<el-option v-for="(item, index) of $d.ResearchRecord" :key="index" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<!-- 是否废除 -->
<!-- <el-form-item :label="$t('trials:researchRecord:table:isDeleted')">
<el-select v-model="searchData.IsDeleted" clearable filterable style="width: 120px">
<el-option v-for="item of $d.YesOrNo" v-show="item.raw.ValueCN !== '无'" :key="`IsDeleted${item.value}`"
:label="item.label" :value="item.value" />
</el-select>
</el-form-item> -->
<el-form-item>
<!-- 查询 -->
<el-button type="primary" icon="el-icon-search" @click="handleSearch">
{{ $t('common:button:search') }}
</el-button>
</el-form-item>
<!-- 重置 -->
<el-form-item>
<el-button type="primary" icon="el-icon-refresh-left" @click="handleReset">
{{ $t('common:button:reset') }}
</el-button>
</el-form-item>
<!-- 导出 -->
<el-form-item>
<el-button type="primary" icon="el-icon-download" :disabled="list.length === 0" @click="handleExport">
{{ $t('common:button:export') }}
</el-button>
</el-form-item>
</el-form>
</template>
<template slot="main-container">
<el-table ref="questionsList" v-loading="loading" v-adaptive="{ bottomOffset: 55 }" :data="list" stripe
height="100" @sort-change="handleSortByColumn">
<el-table-column type="index" width="50" />
<!-- 项目编号 -->
<!-- <el-table-column
prop="TrialCode"
:label="$t('trials:researchForm:form:trialId')"
show-overflow-tooltip
sortable="custom"
min-width="100"
/> -->
<!-- 试验方案号 -->
<!-- <el-table-column
prop="ResearchProgramNo"
:label="$t('trials:researchForm:form:researchNo')"
show-overflow-tooltip
sortable="custom"
min-width="100"
/> -->
<!-- 试验名称 -->
<!-- <el-table-column
prop="ExperimentName"
:label="$t('trials:researchForm:form:researchName')"
show-overflow-tooltip
sortable="custom"
min-width="100"
/> -->
<!-- 适应症类型 -->
<!-- <el-table-column
prop="IndicationType"
:label="$t('trials:researchForm:form:decleareType')"
show-overflow-tooltip
sortable="custom"
min-width="100"
/> -->
<!-- 适应症类型 -->
<!-- <el-table-column
prop="IndicationType"
:label="$t('trials:researchForm:form:decleareType')"
show-overflow-tooltip
sortable="custom"
min-width="100"
/> -->
<!-- 中心编号 -->
<el-table-column prop="TrialSiteCode" :label="$t('trials:researchStaff:table:siteId')" show-overflow-tooltip
sortable="custom" min-width="100" />
<!-- 中心名称 -->
<el-table-column prop="SiteName" :label="$t('trials:researchStaff:table:siteName')" show-overflow-tooltip
sortable="custom" min-width="100" />
<!-- 状态 -->
<el-table-column prop="State" :label="$t('trials:researchRecord:table:status')" min-width="150"
sortable="custom" show-overflow-tooltip>
<template slot-scope="scope">
<el-tag v-if="scope.row.State === 0" type="primary">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
<el-tag v-if="scope.row.State === 1" type="info">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
<el-tag v-if="scope.row.State === 2" type="warning">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
<el-tag v-if="scope.row.State === 3" type="danger">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
</template>
</el-table-column>
<!-- 是否废除 -->
<!-- <el-table-column prop="IsDeleted" :label="$t('trials:researchRecord:table:isDeleted')" min-width="100"
sortable="custom" show-overflow-tooltip>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsDeleted" type="danger">{{
$fd('YesOrNo', scope.row.IsDeleted)
}}</el-tag>
<el-tag v-else type="primary">{{
$fd('YesOrNo', scope.row.IsDeleted)
}}</el-tag>
</template>
</el-table-column> -->
<!-- 联系人 -->
<el-table-column prop="UserName" :label="$t('trials:researchForm:form:contactor')" show-overflow-tooltip
sortable="custom" min-width="100" />
<!-- 联系电话 -->
<el-table-column prop="Phone" :label="$t('trials:researchForm:form:contactorPhone')" show-overflow-tooltip
sortable="custom" min-width="100" />
<!-- 联系邮箱 -->
<el-table-column prop="Email" :label="$t('trials:researchForm:form:contactorEmail')" show-overflow-tooltip
sortable="custom" min-width="100" />
<el-table-column v-show="otherInfo.IsSupportUploadFile" prop="SiteSurveyFile.FileName"
:label="$t('trials:researchForm:form:SiteSurveyFile')" show-overflow-tooltip min-width="100">
<template slot-scope="scope">
<el-button v-if="scope.row.SiteSurveyFile && scope.row.SiteSurveyFile.FileName" type="text"
@click="viewFile(scope.row.SiteSurveyFile)">
{{ scope.row.SiteSurveyFile.FileName }}
</el-button>
</template>
</el-table-column>
<template v-if="!notShowFieldList.includes('AverageEngravingCycle')">
<!-- 平均刻盘周期 -->
<el-table-column prop="AverageEngravingCycle" :label="$t('trials:researchForm:form:engravingCycle')"
show-overflow-tooltip />
</template>
<template v-if="!notShowFieldList.includes('IsRoutineMRIPDEE')">
<!-- MRI-PDFF 是否为本中心该适应症的常规诊疗检查项目 -->
<el-table-column prop="IsRoutineMRIPDEE" :label="$t('trials:researchForm:form:IsRoutineMRIPDEE')"
show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('YesOrNo', scope.row.IsRoutineMRIPDEE) }}
</template>
</el-table-column>
</template>
<template
v-if="!notShowFieldList.includes('MRIPDFFScanTime') || !notShowFieldList.includes('MRIPDFFLeadTime') || !notShowFieldList.includes('MRIPDFFOther')">
<!-- MRI-PDFF 检查的检测周期含单次检查时长预约等待时长等 -->
<!-- <el-table-column :label="$t('trials:researchForm:form:IsRoutineMRIPDEE')"> -->
<!-- 单次检查时长分钟 -->
<el-table-column v-show="!notShowFieldList.includes('MRIPDFFScanTime')" prop="MRIPDFFScanTime"
:label="$t('trials:researchForm:form:MRIPDFFScanTime')" show-overflow-tooltip />
<!-- 平均预约等待时长 -->
<el-table-column v-show="!notShowFieldList.includes('MRIPDFFLeadTime')" prop="MRIPDFFLeadTime"
:label="$t('trials:researchForm:form:MRIPDFFLeadTime')" show-overflow-tooltip />
<!-- 特殊情况备注-->
<el-table-column v-show="!notShowFieldList.includes('MRIPDFFOther')" prop="MRIPDFFOther"
:label="$t('trials:researchForm:form:MRIPDFFOther')" show-overflow-tooltip />
<!-- </el-table-column> -->
</template>
<template
v-if="!notShowFieldList.includes('IsAuthorizeRadiologistsParticipate') || !notShowFieldList.includes('AssignFixedTechnologists')">
<!-- 如已选择研究者评估项目是否会授权影像科老师参与本试验如不单独授权是否可在试验中保持 1-2 名固定技师操作 -->
<el-table-column prop="IsAuthorize" :label="$t('trials:researchForm:form:IsAuthorize')" show-overflow-tooltip>
<template slot-scope="scope">
<span v-if="scope.row.IsAuthorizeRadiologistsParticipate">
{{ $t('trials:researchForm:form:IsAuthorizeRadiologistsParticipate') }}
</span>
<span v-else-if="scope.row.AssignFixedTechnologists">
{{ $t('trials:researchForm:form:AssignFixedTechnologists') }}
</span>
</template>
</el-table-column>
</template>
<template v-if="!notShowFieldList.includes('IsConfirmImagingTechnologist')">
<!-- 请确认参与本项目影像采集的影像技师具备对应的资质技师证对应设备的大型设备上岗证 -->
<el-table-column prop="IsConfirmImagingTechnologist" :label="$t('trials:researchForm:form:isQualified')"
show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('YesOrNo', scope.row.IsConfirmImagingTechnologist) }}
</template>
</el-table-column>
</template>
<template v-if="!notShowFieldList.includes('NotConfirmReson')">
<!-- 原因 -->
<el-table-column prop="NotConfirmReson" :label="$t('trials:researchForm:form:notQualifiedReason')"
show-overflow-tooltip />
</template>
<template v-if="!notShowFieldList.includes('EfficacyEvaluatorType')">
<!-- 研究单位疗效评估人员类型 -->
<el-table-column prop="EfficacyEvaluatorType" :label="$t('trials:researchForm:form:staffType')"
show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('EfficacyEvaluatorType', scope.row.EfficacyEvaluatorType) }}
</template>
</el-table-column>
</template>
<template v-if="!notShowFieldList.includes('IsFollowStudyParameters')">
<!-- 是否严格按照研究单位影像手册参数完成图像采集 -->
<el-table-column prop="IsFollowStudyParameters" :label="$t('trials:researchForm:form:isFollowStudyParam')"
show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('YesOrNo', scope.row.IsFollowStudyParameters) }}
</template>
</el-table-column>
</template>
<template v-if="!notShowFieldList.includes('NotFollowReson')">
<!-- 不能严格按照研究单位影像手册参数采集图像原因 -->
<el-table-column prop="NotFollowReson" :label="$t('trials:researchForm:form:notFollowStudyParam')"
show-overflow-tooltip />
</template>
<template v-if="!notShowFieldList.includes('ISStrictManualBurnFlag')">
<!-- 是否严格按照影像手册参数完成刻盘 -->
<el-table-column prop="ISStrictManualBurnFlag" :label="$t('trials:researchForm:form:ISStrictManualBurnFlag')"
show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('YesOrNo', scope.row.ISStrictManualBurnFlag) }}
</template>
</el-table-column>
</template>
<template v-if="!notShowFieldList.includes('NotStrictManualBurnFlagReason')">
<!-- 不能严格按照影像手册参数完成刻盘原因 -->
<el-table-column prop="NotStrictManualBurnFlagReason"
:label="$t('trials:researchForm:form:NotStrictManualBurnFlagReason')" show-overflow-tooltip />
</template>
</el-table>
<!-- 分页组件 -->
<pagination class="page" :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize"
@pagination="getList" />
</template>
</BaseContainer>
</template>
<script>
import { getTrialSiteSelect } from '@/api/trials'
import { getSiteSurveyInfoList } from '@/api/research'
import BaseContainer from '@/components/BaseContainer'
import Pagination from '@/components/Pagination'
import { getSiteSurveyInfoList_Export } from '@/api/export'
const searchDataDefault = () => {
return {
SortField: '',
Asc: true,
State: "",
PageIndex: 1,
PageSize: 20,
TrialId: '',
TrialSiteId: '',
TrialSiteName: ''
}
}
export default {
name: 'Equipments',
components: { BaseContainer, Pagination },
data() {
return {
searchData: searchDataDefault(),
loading: false,
list: [],
total: 0,
trialId: '',
otherInfo: {},
siteOptions: [],
notShowFieldList: []
}
},
mounted() {
this.trialId = this.$route.query.trialId
this.getList()
this.getSite()
},
methods: {
async handleExport() {
await getSiteSurveyInfoList_Export(this.searchData)
},
async getList() {
try {
this.loading = true
this.searchData.TrialId = this.trialId
let res = await getSiteSurveyInfoList(this.searchData)
if (res.IsSuccess) {
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
this.otherInfo = res.OtherInfo
this.notShowFieldList = res.OtherInfo.NotShowFieldList
this.$nextTick(() => {
this.$refs.questionsList.doLayout()
})
}
} catch (e) {
console.log(e)
} finally {
this.loading = false
}
},
// site
async getSite() {
try {
let res = await getTrialSiteSelect(this.trialId)
this.siteOptions = res.Result
} catch (e) {
console.log(e)
}
},
viewFile(siteSurveyFile) {
this.$preview({
path: siteSurveyFile.Path,
type: siteSurveyFile.FileType,
title: siteSurveyFile.FileName,
})
},
//
handleReset() {
this.searchData = searchDataDefault()
this.getList()
this.$nextTick(() => {
this.$refs.list.clearSort()
})
},
//
handleSearch() {
this.getList()
},
//
handleSortByColumn(column) {
if (column.order === 'ascending') {
this.searchData.Asc = true
} else {
this.searchData.Asc = false
}
this.searchData.SortField = column.prop
this.getList()
}
}
}
</script>
@@ -0,0 +1,733 @@
<template>
<BaseContainer>
<!-- 搜索框 -->
<template slot="search-container">
<el-form :inline="true">
<!-- 中心 -->
<el-form-item :label="$t('trials:researchRecord:table:siteId')">
<el-select v-model="searchData.TrialSiteId" clearable filterable style="width: 120px">
<el-option v-for="(item, index) of siteOptions" :key="index" :label="item.TrialSiteCode"
:value="item.TrialSiteId" />
</el-select>
</el-form-item>
<!-- 联系人 -->
<el-form-item :label="$t('trials:researchRecord:table:contactor')">
<el-input v-model="searchData.UserKeyInfo" class="mr" clearable :placeholder="`${$t(
'trials:researchRecord:placeholder:contactorInfo'
)}`" style="width: 140px" />
</el-form-item>
<!-- 初审人 -->
<el-form-item :label="$t('trials:researchRecord:table:preliminaryUser')">
<el-input v-model="searchData.PreliminaryUserName" class="mr" clearable style="width: 140px" />
</el-form-item>
<!-- 审核人 -->
<el-form-item :label="$t('trials:researchRecord:table:ReviewerUser')">
<el-input v-model="searchData.ReviewerUserName" class="mr" clearable style="width: 140px" />
</el-form-item>
<!-- 状态 -->
<!-- <el-form-item :label="$t('trials:researchRecord:table:status')">
<el-select v-model="searchData.State" clearable filterable style="width: 120px">
<el-option v-for="(item, index) of $d.ResearchRecord" :key="index" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item> -->
<!-- 是否废除 -->
<el-form-item :label="$t('trials:researchRecord:table:isDeleted')">
<el-select v-model="searchData.IsDeleted" clearable filterable style="width: 120px">
<el-option v-for="item of $d.YesOrNo" v-show="item.raw.ValueCN !== '无'" :key="`IsDeleted${item.value}`"
:label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- 更新时间 -->
<el-form-item :label="$t('trials:researchRecord:table:updateTime')">
<el-date-picker v-model="searchData.DateRange" type="daterange" value-format="yyyy-MM-dd" format="yyyy-MM-dd"
style="width: 250px" />
</el-form-item>
<!-- 查询 -->
<el-button type="primary" icon="el-icon-search" @click="handleSearch">
{{ $t('common:button:search') }}
</el-button>
<!-- 重置 -->
<el-button type="primary" icon="el-icon-refresh-left" @click="handleReset">
{{ $t('common:button:reset') }}
</el-button>
<!-- 导出 -->
<el-button type="primary" @click="handleExport">
{{ $t('common:button:export') }}
</el-button>
<!-- 中心人员 -->
<el-button v-show="searchData.State === 3"
v-hasPermi="['trials:trials-panel:attachments:site-research:summary-record']" type="primary"
icon="el-icon-info" @click="showResearchUser">
{{ $t('trials:researchRecord:button:researchData') }}
</el-button>
<!-- 调查表链接 -->
<el-button v-show="searchData.State === 0"
v-hasPermi="['trials:trials-panel:attachments:site-research:questionnaire-link']" type="primary"
icon="el-icon-link" @click="showResearchLink">
{{ $t('trials:researchRecord:button:questionLink') }}
</el-button>
</el-form>
</template>
<template slot="main-container">
<!-- 系统文件列表 -->
<el-table ref="siteResearchList" v-loading="loading" v-adaptive="{ bottomOffset: 60 }" :data="list" stripe
height="100" @sort-change="handleSortByColumn">
<el-table-column type="index" width="50" />
<!-- 中心编号 -->
<el-table-column prop="TrialSiteCode" :label="$t('trials:researchRecord:table:siteId')" min-width="100"
sortable="custom" show-overflow-tooltip />
<!-- 中心名称 -->
<el-table-column prop="SiteName" :label="$t('trials:researchRecord:table:siteName')" min-width="100"
sortable="custom" show-overflow-tooltip />
<!-- 联系人 -->
<el-table-column prop="UserName" :label="$t('trials:researchRecord:table:contactor')" min-width="100"
sortable="custom" show-overflow-tooltip />
<!-- 联系电话 -->
<el-table-column prop="Phone" :label="$t('trials:researchRecord:table:contactorPhone')" min-width="100"
show-overflow-tooltip />
<!-- 联系邮箱 -->
<el-table-column prop="Email" :label="$t('trials:researchRecord:table:contactorEmail')" min-width="150"
show-overflow-tooltip />
<!-- 初审人 -->
<el-table-column prop="preliminaryUser" :label="$t('trials:researchRecord:table:preliminaryUser')"
min-width="150" show-overflow-tooltip>
<template slot-scope="scope">
{{
scope.row.PreliminaryUser
? scope.row.PreliminaryUser.RealName
: ''
}}
</template>
</el-table-column>
<!-- 审核人 -->
<el-table-column prop="ReviewerUser" :label="$t('trials:researchRecord:table:ReviewerUser')" min-width="150"
show-overflow-tooltip>
<template slot-scope="scope">
{{ scope.row.ReviewerUser ? scope.row.ReviewerUser.RealName : '' }}
</template>
</el-table-column>
<!-- 状态 -->
<el-table-column prop="State" :label="$t('trials:researchRecord:table:status')" min-width="150"
sortable="custom" show-overflow-tooltip>
<template slot-scope="scope">
<el-tag v-if="scope.row.State === 0" type="primary">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
<el-tag v-if="scope.row.State === 1" type="info">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
<el-tag v-if="scope.row.State === 2" type="warning">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
<el-tag v-if="scope.row.State === 3" type="danger">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
</template>
</el-table-column>
<!-- 是否废除 -->
<el-table-column prop="IsDeleted" :label="$t('trials:researchRecord:table:isDeleted')" min-width="100"
sortable="custom" show-overflow-tooltip>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsDeleted" type="danger">{{
$fd('YesOrNo', scope.row.IsDeleted)
}}</el-tag>
<el-tag v-else type="primary">{{
$fd('YesOrNo', scope.row.IsDeleted)
}}</el-tag>
</template>
</el-table-column>
<!-- 更新时间 -->
<el-table-column prop="UpdateTime" :label="$t('trials:researchRecord:table:updateTime')" min-width="150"
show-overflow-tooltip sortable="custom" />
<el-table-column width="150">
<template slot-scope="scope">
<!-- 查看 -->
<el-button v-show="searchData.State === 3" :disabled="scope.row.State !== 3" circle
:title="$t('common:button:view')" icon="el-icon-view" @click="handleViewResearchList(scope.row)" />
<!-- 审批 -->
<el-button v-show="(searchData.State === 1 || searchData.State === 2) && isAudit"
v-hasPermi="['trials:trials-panel:attachments:site-research:auidt']"
:disabled="scope.row.State === 0 || scope.row.State === 3 || scope.row.IsDeleted" circle
:title="$t('trials:researchRecord:action:view')" icon="el-icon-s-check"
@click="handleViewResearchList(scope.row)" />
<!-- 日志 -->
<el-button circle :title="$t('trials:researchRecord:action:log')" icon="el-icon-tickets"
@click="viewLog(scope.row)" />
<!-- 废除 -->
<el-button v-show="searchData.State === 0" v-hasPermi="[
'trials:trials-panel:attachments:site-research:abolish',
]" :disabled="scope.row.State !== 0 || scope.row.IsDeleted" circle
:title="$t('trials:researchRecord:action:abolish')" icon="el-icon-delete"
@click="handleRepealResearch(scope.row)" />
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination class="page" :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize"
@pagination="getList" />
</template>
<!-- 中心数据 trials:researchRecord:button:researchData-->
<!-- 中心人员 trials:researchRecord:dialogTitle:questionStaff-->
<el-dialog v-if="researchUserVisible" :visible.sync="researchUserVisible"
:title="$t('trials:researchRecord:button:researchData')" custom-class="base-dialog-wrapper" :fullscreen="true">
<div class="base-modal-body" style="border: 1px solid #ccc; padding: 10px">
<el-tabs v-model="activeName">
<el-tab-pane :label="$t('trials:researchRecord:dialogTitle:questionStaff')" name="first">
<Users v-if="activeName === 'first'" />
</el-tab-pane>
<el-tab-pane label="中心设备" name="second">
<Equipments v-if="activeName === 'second'" />
</el-tab-pane>
<el-tab-pane label="基本情况" name="third">
<Questions v-if="activeName === 'third'" />
</el-tab-pane>
</el-tabs>
</div>
</el-dialog>
<!-- 调查表 -->
<el-dialog v-if="researchInfoVisible" :visible.sync="researchInfoVisible" :fullscreen="true"
:close-on-click-modal="false">
<research-form v-if="researchInfoVisible" @refreshPage="getList" />
</el-dialog>
<!-- 调查表编辑 -->
<el-dialog v-if="ImageManualVisible" :visible.sync="ImageManualVisible" :fullscreen="true"
:close-on-click-modal="false" :title="$t('trials:researchRecord:dialogTitle:ImageManualEdit')">
<ImageManual v-if="ImageManualVisible" :trialSiteSurveyId="trialSiteSurveyId" @getList="getList" />
</el-dialog>
<!-- 调查表链接 -->
<base-model :config="share_model">
<template slot="dialog-body" v-loading="shareLoading">
<el-button size="small" type="primary" style="margin-bottom: 10px;" @click.stop="openImageManual">{{
$t('trials:researchRecord:label:edit')
}}</el-button>
<div style="width: 100%; display: flex">
<div class="date">
<el-form :model="shareForm" :rules="rules" ref="shareForm" label-width="100px">
<el-form-item :label="$t('trials:researchRecord:label:ExpirationDays')" prop="ExpirationDays">
<el-radio-group v-model="shareForm.ExpirationDays" @input="shareForm.OtherExpirationDays = null">
<el-radio :label="1">{{ $t('trials:researchRecord:label:day1') }}</el-radio>
<el-radio :label="7">{{ $t('trials:researchRecord:label:day7') }}</el-radio>
<el-radio :label="15">{{ $t('trials:researchRecord:label:day15') }}</el-radio>
<el-radio :label="`default`">{{ $t('trials:researchRecord:label:default') }}
<el-input placeholder="" type="number" @input="handleInput" v-model="shareForm.OtherExpirationDays"
clearable size="mini" style="width: 60px;" class="dayInput" />
<span style="margin-left: 10px;">{{ $t('trials:researchRecord:label:day') }}</span>
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('trials:researchRecord:label:LinkVerificationCode')" prop="LinkVerificationCode">
<el-radio-group v-model="shareForm.LinkVerificationCode"
@input="shareForm.OtherLinkVerificationCode = null">
<el-radio label="researchProgramNo">{{ $t('trials:researchRecord:label:researchProgramNo')
}}</el-radio>
<el-radio label="default">{{ $t('trials:researchRecord:label:default') }}
<el-input placeholder="" type="number" v-model="shareForm.OtherLinkVerificationCode" clearable
size="mini" style="width: 100px;" />
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<el-button type="primary" round @click="setLink" style="float: right;">
{{ $t('trials:researchRecord:button:setLink') }}
</el-button>
</div>
<div class="share">
<div class="shareLink">
<h3>{{ $t('trials:researchRecord:label:link') }}</h3>
<el-input ref="shareLink" v-model="shareLink" readonly type="textarea" autosize :rows="3" />
<div class="dateBox">
<span>{{ $t('trials:researchRecord:label:linkVerificationCode') }}</span>
<span>{{ LinkVerificationCode }}</span>
</div>
<div class="dateBox">
<span>{{ $t('trials:researchRecord:label:ValidityPeriod') }}</span>
<span>{{ validityPeriod }}</span>
<span style="color: red;" v-if="isExpired">({{ $t('trials:researchRecord:label:Expired') }})</span>
</div>
<el-button type="primary" round @click="copyLink" class="shareLinkBtn"
:disabled="!validityPeriod || isExpired">
{{ $t('trials:researchRecord:button:copyLink') }}
</el-button>
</div>
<div class="shareCode">
<h3>{{ $t('trials:researchRecord:label:shareCode') }}</h3>
<div style="display: flex;align-items: center;justify-content: space-between;">
<div class="qrCodeBox">
<div id="qrcode" ref="qrcode"></div>
</div>
<div class="codeBtnBox">
<el-button @click="handleCopyImg" type="primary" round :disabled="!validityPeriod || isExpired">{{
$t('trials:researchRecord:button:copyCode')
}}</el-button>
<el-button @click="savePic" round :disabled="!validityPeriod || isExpired">{{
$t('trials:researchRecord:button:savePic')
}}</el-button>
</div>
</div>
</div>
</div>
</div>
</template>
</base-model>
<el-dialog v-if="logVisible" :visible.sync="logVisible"
:custom-class="isFullscreen ? 'full-log-dialog-container' : 'log-dialog-container'" :close-on-click-modal="false"
:fullscreen="isFullscreen" :show-close="false" width="60%" append-to-body>
<span slot="title" class="dialog-footer">
<div style="display: flex;flex-direction: row;justify-content: space-between;">
<div>
{{ $t('trials:researchForm:button:log') }}
</div>
<div>
<svg-icon :icon-class="isFullscreen ? 'exit-fullscreen' : 'fullscreen'"
style="vertical-align: baseline;cursor: pointer;font-size: 20px;" @click="toggleLogFullscreen" />
<svg-icon icon-class="dClose" style="cursor: pointer;font-size: 25px;margin-left: 10px;"
@click="logVisible = false" />
</div>
</div>
</span>
<div class="log-dialog-content">
<LogList :trial-site-survey-id="trialSiteSurveyId" />
</div>
</el-dialog>
</BaseContainer>
</template>
<script>
import {
getTrialSiteSurveyList,
getTrialSiteSelect,
abandonSiteSurvey,
setTrialLinkExpirationTime,
getTrialLinkExpirationTime,
getLinkLinkExpirationTime,
} from '@/api/trials'
import { changeURLStatic } from '@/utils/history.js'
import BaseContainer from '@/components/BaseContainer'
import Pagination from '@/components/Pagination'
import Users from './users'
import Equipments from './Equipments'
import Questions from './Questions'
import ResearchForm from '@/views/research/form'
import BaseModel from '@/components/BaseModel'
import ImageManual from './ImageManual'
import QRCode from 'qrcodejs2'
import LogList from '@/views/research/components/LogList'
import { getTrialSiteSurveyList_Export } from '@/api/export'
const searchDataDefault = () => {
return {
SortField: '',
Asc: true,
PageIndex: 1,
PageSize: 20,
TrialSiteId: '',
UserKeyInfo: '',
State: null,
IsDeleted: '',
DateRange: [],
PreliminaryUserName: null,
ReviewerUserName: null,
}
}
export default {
name: 'ResearchList',
components: { BaseContainer, Pagination, Users, Equipments, Questions, ResearchForm, BaseModel, ImageManual, LogList },
props: {
isAudit: {
type: Boolean,
default: false
}
},
data() {
return {
searchData: searchDataDefault(),
loading: false,
list: [],
total: 0,
trialId: this.$route.query.trialId,
siteOptions: [],
researchUserVisible: false,
researchInfoVisible: false,
share_model: {
visible: false,
title: this.$t('trials:researchRecord:title:shark'),
width: '1000px',
},
shareLink: '',
researchState: this.$d.ResearchRecord,
qrcode: null,
validityPeriod: null,
LinkVerificationCode: null,
isExpired: false,
shareLoading: false,
shareForm: {
ExpirationDays: null,
OtherExpirationDays: null,
LinkVerificationCode: null,
OtherLinkVerificationCode: null,
},
rules: {
ExpirationDays: [
{ required: true, message: this.$t("common:ruleMessage:select"), trigger: 'blur' },
{
validator: (rule, value, callback) => {
if (value === 'default' && !this.shareForm.OtherExpirationDays) {
callback(new Error(this.$t("common:ruleMessage:specify")));
} else {
callback()
}
}, trigger: 'blur'
}
],
LinkVerificationCode: [
{ required: true, message: this.$t("common:ruleMessage:select"), trigger: 'blur' },
{
validator: (rule, value, callback) => {
if (value === 'default' && !this.shareForm.OtherLinkVerificationCode) {
callback(new Error(this.$t("common:ruleMessage:specify")));
} else {
callback()
}
}, trigger: 'blur'
}
],
},
ImageManualVisible: false,
trialSiteSurveyId: null,
activeName: 'first',
isFullscreen: false,
logVisible: false
}
},
mounted() {
// this.getList()
this.getSite()
},
methods: {
async handleExport() {
await getTrialSiteSurveyList_Export(this.searchData)
},
toggleLogFullscreen() {
this.isFullscreen = !this.isFullscreen
},
viewLog(row) {
this.trialSiteSurveyId = row.Id
this.isFullscreen = false
this.logVisible = true
},
async getLinkTimeIsExpired() {
try {
let data = {
TrialId: this.$route.query.trialId,
}
this.shareLoading = true
let res = await getLinkLinkExpirationTime(data)
this.shareLoading = false
if (res.IsSuccess) {
this.isExpired = res.Result.IsIsExpired
}
} catch (err) {
this.shareLoading = false
console.log(err)
}
},
async getLinkTime() {
try {
let data = {
TrialId: this.$route.query.trialId,
}
this.shareLoading = true
let res = await getTrialLinkExpirationTime(data)
this.shareLoading = false
if (res.IsSuccess) {
this.validityPeriod = res.Result.LinkExpirationTime
this.LinkVerificationCode = res.Result.LinkVerificationCode || res.Result.ResearchProgramNo
if (!this.validityPeriod) {
this.LinkVerificationCode = null
}
if (!this.validityPeriod) return false
this.getLinkTimeIsExpired()
this.shareLink = `${location.protocol}//${location.host}/researchLogin?trialId=${this.$route.query.trialId}`
this.$nextTick(() => {
this.creatQrCode()
})
}
} catch (err) {
this.shareLoading = false
console.log(err)
}
},
async setLink() {
try {
let validate = await this.$refs.shareForm.validate()
if (!validate) return false
let data = {
TrialId: this.$route.query.trialId,
ExpirationDays: this.shareForm.ExpirationDays,
LinkVerificationCode: this.shareForm.LinkVerificationCode
}
if (this.shareForm.LinkVerificationCode === 'researchProgramNo') {
data.LinkVerificationCode = null
}
if (this.shareForm.LinkVerificationCode === 'default') {
data.LinkVerificationCode = this.shareForm.OtherLinkVerificationCode
}
if (this.shareForm.ExpirationDays === 'default') {
data.ExpirationDays = this.shareForm.OtherExpirationDays
}
this.shareLoading = true
let res = await setTrialLinkExpirationTime(data)
this.shareLoading = false
if (res.IsSuccess) {
this.getLinkTime()
}
} catch (err) {
this.shareLoading = false
console.log(err)
}
},
handleInput(val) {
//
this.shareForm.OtherExpirationDays = val.replace(/[^\d]/g, '').replace(/^0+/, '')
},
openImageManual() {
// if (!this.trialSiteSurveyId) return false
this.ImageManualVisible = true
},
//
getList() {
this.loading = true
this.searchData.TrialId = this.trialId
if (this.searchData.DateRange && this.searchData.DateRange.length === 2) {
this.searchData.UpdateTimeBegin = this.searchData.DateRange[0]
this.searchData.updateTimeEnd = this.searchData.DateRange[1]
} else {
this.searchData.UpdateTimeBegin = ''
this.searchData.updateTimeEnd = ''
}
getTrialSiteSurveyList(this.searchData)
.then((res) => {
this.loading = false
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
})
.catch(() => {
this.loading = false
})
},
//
handleViewResearchList(row) {
changeURLStatic('trialSiteSurveyId', row.Id)
this.researchInfoVisible = true
},
//
handleRepealResearch(row) {
//
this.$confirm(this.$t('trials:researchRecord:message:abolish'), {
type: 'warning',
distinguishCancelAndClose: true,
}).then(() => {
abandonSiteSurvey(this.trialId, row.Id).then((res) => {
if (res.IsSuccess) {
this.getList()
//
this.$message.success(
this.$t('trials:researchRecord:message:abolishSuccessfully')
)
}
})
})
},
//
showResearchUser() {
this.researchUserVisible = true
this.activeName = 'first'
},
//
copyLink() {
//
this.$copyText(
`${this.$t('trials:researchRecord:message:researchFormLink')}: ${this.shareLink
}\n${this.$t('trials:researchRecord:label:linkVerificationCode')}${this.LinkVerificationCode}\n${this.$t('trials:researchRecord:label:ValidityPeriod')}${this.validityPeriod}`
)
.then((res) => {
//
this.$message.success(
this.$t('trials:researchRecord:message:copySuccessfully')
)
})
.catch(() => {
//
this.$alert(this.$t('trials:researchRecord:message:copyFailed'))
})
},
//
creatQrCode() {
this.$refs.qrcode.innerHTML = '' //
let text = this.shareLink
this.qrcode = new QRCode(this.$refs.qrcode, {
text: text, // ,#
width: 200,
height: 200,
colorDark: '#000000',
colorLight: '#ffffff',
correctLevel: QRCode.CorrectLevel.H,
})
// qrcode.clear(); //
},
//
savePic() {
let qrCodeCanvas = document
.getElementById('qrcode')
.getElementsByTagName('canvas')
let a = document.createElement('a')
a.href = qrCodeCanvas[0].toDataURL('image/url')
a.download = `${this.$route.query.researchProgramNo}${this.$t('trials:researchRecord:title:code')}.png`
a.click()
},
//
handleCopyImg() {
let qrCodeCanvas = document
.getElementById('qrcode')
.getElementsByTagName('canvas')
qrCodeCanvas[0].toBlob(async (blob) => {
console.log(blob)
const data = [
new ClipboardItem({
[blob.type]: blob,
}),
] // https://w3c.github.io/clipboard-apis/#dom-clipboard-write
await navigator.clipboard.write(data).then(
() => {
this.$message.success(
this.$t('trials:researchRecord:message:copySuccess')
)
},
() => {
this.$message.error(
this.$t('trials:researchRecord:message:UnableWrite')
)
}
)
})
},
// site
getSite() {
getTrialSiteSelect(this.trialId).then((res) => {
this.siteOptions = res.Result
})
},
//
showResearchLink() {
this.shareForm = {
ExpirationDays: null,
OtherExpirationDays: null,
LinkVerificationCode: null,
OtherLinkVerificationCode: null,
}
this.validityPeriod = null
this.isExpired = false
this.share_model.visible = true
this.getLinkTime()
// &lang=${this.$i18n.locale}
// this.trialSiteSurveyId = this.list[0].Id
},
//
handleReset() {
const state = this.searchData.State
this.searchData = searchDataDefault()
this.searchData.DateRange = []
if (this.searchData.DateRange && this.searchData.DateRange.length === 2) {
this.searchData.UpdateTimeBegin = this.searchData.DateRange[0]
this.searchData.updateTimeEnd = this.searchData.DateRange[1]
} else {
this.searchData.UpdateTimeBegin = ''
this.searchData.updateTimeEnd = ''
}
this.searchData.State = state
this.getList()
this.$nextTick(() => {
this.$refs.siteResearchList.clearSort()
})
},
//
handleSearch() {
this.getList()
},
//
handleSortByColumn(column) {
if (column.order === 'ascending') {
this.searchData.Asc = true
} else {
this.searchData.Asc = false
}
this.searchData.SortField = column.prop
this.getList()
},
},
beforeDestroy() {
if (this.qrcode) {
this.qrcode = null
}
},
}
</script>
<style lang="scss" scoped>
.date,
.share {
width: 55%;
height: 100%;
}
.dayInput {
::v-deep .el-input__inner {
padding-left: 5px;
}
}
.date {
padding-right: 10px;
}
.share {
width: 45%;
padding-left: 5%;
border-left: 1px solid #f0f0f0;
box-sizing: border-box;
}
.shareLinkBtn {
float: right;
}
.qrCodeBox {
width: 220px;
height: 220px;
display: flex;
border: 1px solid #c0c4cc;
border-radius: 5px;
box-shadow: 1px 1px 5px #c0c4cc;
align-items: center;
justify-content: center;
}
.dateBox {
margin: 10px;
}
.codeBtnBox {
::v-deep .el-button {
display: block;
margin: 10px auto;
}
}
</style>
@@ -6,31 +6,33 @@
<!-- 中心编号 -->
<el-form-item :label="$t('trials:researchStaff:table:siteId')">
<el-select v-model="searchData.TrialSiteId" clearable filterable style="width:120px;">
<el-option
v-for="(item,index) of siteOptions"
:key="index"
:label="item.TrialSiteCode"
:value="item.TrialSiteId"
/>
<el-option v-for="(item, index) of siteOptions" :key="index" :label="item.TrialSiteCode"
:value="item.TrialSiteId" />
</el-select>
</el-form-item>
<!-- 状态 -->
<el-form-item :label="$t('trials:researchRecord:table:status')">
<el-select v-model="searchData.State" clearable filterable style="width: 120px">
<el-option v-for="(item, index) of $d.ResearchRecord" :key="index" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<!-- 是否废除 -->
<!-- <el-form-item :label="$t('trials:researchRecord:table:isDeleted')">
<el-select v-model="searchData.IsDeleted" clearable filterable style="width: 120px">
<el-option v-for="item of $d.YesOrNo" v-show="item.raw.ValueCN !== '无'" :key="`IsDeleted${item.value}`"
:label="item.label" :value="item.value" />
</el-select>
</el-form-item> -->
<!-- 姓名 -->
<el-form-item :label="$t('trials:researchStaff:table:Name')">
<el-input v-model="searchData.UserName" class="mr" clearable style="width:120px;" />
</el-form-item>
<!-- 角色 -->
<el-form-item :label="$t('trials:researchStaff:table:role')">
<el-select
v-model="searchData.TrialRoleCode"
style="width:120px;"
clearable
>
<el-option
v-for="item of dict.type.SiteSurvey_UserRoles"
:key="item.value"
:label="item.label"
:value="parseInt(item.raw.Code)"
/>
<el-select v-model="searchData.TrialRoleCode" style="width:120px;" clearable>
<el-option v-for="item of dict.type.SiteSurvey_UserRoles" :key="item.value" :label="item.label"
:value="parseInt(item.raw.Code)" />
</el-select>
</el-form-item>
<!-- 单位 -->
@@ -39,42 +41,19 @@
</el-form-item>
<!-- 是否生成账号 -->
<el-form-item :label="$t('trials:researchStaff:table:isGenerateAccount')">
<el-select
v-model="searchData.IsGenerateAccount"
style="width:120px;"
clearable
>
<el-option v-for="item of $d.YesOrNo" :key="`IsGenerateAccount${item.value}`" :label="item.label" :value="item.value" />
<el-select v-model="searchData.IsGenerateAccount" style="width:120px;" clearable>
<el-option v-for="item of $d.YesOrNo" :key="`IsGenerateAccount${item.value}`" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<!-- 用户类型 -->
<el-form-item :label="$t('trials:researchStaff:table:userType')">
<el-select
v-model="searchData.UserTypeId"
style="width:120px;"
clearable
>
<el-option
v-for="item of userTypeOptions"
:key="item.Id"
:label="item.UserTypeShortName"
:value="item.Id"
>
<el-select v-model="searchData.UserTypeId" style="width:120px;" clearable>
<el-option v-for="item of userTypeOptions" :key="item.Id" :label="item.UserTypeShortName" :value="item.Id">
<span>{{ item.UserType }}</span>
</el-option>
</el-select>
</el-form-item>
<!-- 状态 -->
<!-- <el-form-item :label="$t('trials:researchStaff:table:status')">
<el-select v-model="searchData.State" clearable filterable style="width:120px;">
<el-option
v-for="(item,index) of researchUserState"
:key="index"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item> -->
<el-form-item>
<!-- 查询 -->
<el-button type="primary" icon="el-icon-search" @click="handleSearch">
@@ -89,25 +68,15 @@
</el-form-item>
<!-- 发邮件 -->
<el-form-item>
<el-button
v-hasPermi="['trials:trials-panel:attachments:site-research:summary-record:sendEmail']"
type="primary"
icon="el-icon-message"
:loading="sendEmailLoading"
:disabled="selectArr.length === 0"
@click="handleSendEmail"
>
<el-button v-hasPermi="['trials:trials-panel:attachments:site-research:summary-record:sendEmail']"
type="primary" icon="el-icon-message" :loading="sendEmailLoading" :disabled="selectArr.length === 0"
@click="handleSendEmail">
{{ $t('trials:researchRecord:dialogButton:sendEmail') }}
</el-button>
</el-form-item>
<!-- 导出 -->
<el-form-item>
<el-button
type="primary"
icon="el-icon-download"
:disabled="list.length === 0"
@click="handleExport"
>
<el-button type="primary" icon="el-icon-download" :disabled="list.length === 0" @click="handleExport">
{{ $t('common:button:export') }}
</el-button>
</el-form-item>
@@ -116,96 +85,74 @@
<template slot="main-container">
<!-- 系统文件列表 -->
<el-table
ref="researchUsers"
v-loading="loading"
v-adaptive="{bottomOffset:70}"
:data="list"
stripe
height="100"
@selection-change="handleSelectChange"
@sort-change="handleSortByColumn"
>
<el-table-column
type="selection"
width="50"
:selectable="handleSelectTable"
/>
<el-table ref="researchUsers" v-loading="loading" v-adaptive="{ bottomOffset: 55 }" :data="list" stripe
height="100" @selection-change="handleSelectChange" @sort-change="handleSortByColumn">
<el-table-column type="selection" width="50" :selectable="handleSelectTable" />
<el-table-column type="index" width="50" />
<!-- 中心编号 -->
<el-table-column
prop="TrialSiteSurvey.TrialSiteCode"
:label="$t('trials:researchStaff:table:siteId')"
show-overflow-tooltip
sortable="custom"
min-width="100"
>
<el-table-column prop="TrialSiteSurvey.TrialSiteCode" :label="$t('trials:researchStaff:table:siteId')"
show-overflow-tooltip sortable="custom" min-width="100">
<template slot-scope="scope">{{ `${scope.row.TrialSiteSurvey.TrialSiteCode}` }}</template>
</el-table-column>
<!-- 中心名称 -->
<el-table-column
prop="TrialSiteSurvey.SiteName"
:label="$t('trials:researchStaff:table:siteName')"
show-overflow-tooltip
sortable="custom"
min-width="120"
>
<el-table-column prop="TrialSiteSurvey.SiteName" :label="$t('trials:researchStaff:table:siteName')"
show-overflow-tooltip sortable="custom" min-width="120">
<template slot-scope="scope">{{ `${scope.row.TrialSiteSurvey.SiteName}` }}</template>
</el-table-column>
<!-- 状态 -->
<el-table-column prop="State" :label="$t('trials:researchRecord:table:status')" min-width="150"
sortable="custom" show-overflow-tooltip>
<template slot-scope="scope">
<el-tag v-if="scope.row.State === 0" type="primary">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
<el-tag v-if="scope.row.State === 1" type="info">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
<el-tag v-if="scope.row.State === 2" type="warning">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
<el-tag v-if="scope.row.State === 3" type="danger">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
</template>
</el-table-column>
<!-- 是否废除 -->
<!-- <el-table-column prop="IsDeleted" :label="$t('trials:researchRecord:table:isDeleted')" min-width="100"
sortable="custom" show-overflow-tooltip>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsDeleted" type="danger">{{
$fd('YesOrNo', scope.row.IsDeleted)
}}</el-tag>
<el-tag v-else type="primary">{{
$fd('YesOrNo', scope.row.IsDeleted)
}}</el-tag>
</template>
</el-table-column> -->
<!-- 姓名 -->
<el-table-column
prop="LastName"
:label="$t('trials:researchStaff:table:Name')"
show-overflow-tooltip
sortable="custom"
min-width="100"
>
<el-table-column prop="LastName" :label="$t('trials:researchStaff:table:Name')" show-overflow-tooltip
sortable="custom" min-width="100">
<template slot-scope="scope">{{ `${scope.row.LastName} / ${scope.row.FirstName}` }}</template>
</el-table-column>
<!-- 角色 -->
<el-table-column
prop="TrialRoleCode"
:label="$t('trials:researchStaff:table:role')"
show-overflow-tooltip
sortable="custom"
min-width="100"
>
<el-table-column prop="TrialRoleCode" :label="$t('trials:researchStaff:table:role')" show-overflow-tooltip
sortable="custom" min-width="100">
<template slot-scope="scope">
{{ $fd('SiteSurvey_UserRoles',scope.row.TrialRoleCode) }}
{{ $fd('SiteSurvey_UserRoles', scope.row.TrialRoleCode) }}
</template>
</el-table-column>
<!-- 电话号码 -->
<el-table-column
prop="Phone"
:label="$t('trials:researchStaff:table:phone')"
show-overflow-tooltip
sortable="custom"
min-width="120"
/>
<el-table-column prop="Phone" :label="$t('trials:researchStaff:table:phone')" show-overflow-tooltip
sortable="custom" min-width="120" />
<!-- 邮箱 -->
<el-table-column
prop="Email"
:label="$t('trials:researchStaff:table:email')"
show-overflow-tooltip
sortable="custom"
min-width="150"
/>
<el-table-column prop="Email" :label="$t('trials:researchStaff:table:email')" show-overflow-tooltip
sortable="custom" min-width="150" />
<!-- 单位 -->
<el-table-column
prop="OrganizationName"
:label="$t('trials:researchStaff:table:organization')"
show-overflow-tooltip
sortable="custom"
min-width="100"
/>
<el-table-column prop="OrganizationName" :label="$t('trials:researchStaff:table:organization')"
show-overflow-tooltip sortable="custom" min-width="100" />
<!-- 是否生成账号 -->
<el-table-column
prop="IsGenerateAccount"
:label="$t('trials:researchStaff:table:isGenerateAccount')"
show-overflow-tooltip
sortable="custom"
min-width="130"
>
<el-table-column prop="IsGenerateAccount" :label="$t('trials:researchStaff:table:isGenerateAccount')"
show-overflow-tooltip sortable="custom" min-width="130">
<template slot-scope="scope">
{{ $fd('YesOrNo', scope.row.IsGenerateAccount) }}
</template>
@@ -223,21 +170,11 @@
<!-- </el-table-column>-->
<!-- 用户类型 -->
<el-table-column
prop="UserType"
:label="$t('trials:researchStaff:table:userType')"
show-overflow-tooltip
sortable="custom"
min-width="100"
/>
<el-table-column prop="UserType" :label="$t('trials:researchStaff:table:userType')" show-overflow-tooltip
sortable="custom" min-width="100" />
<!-- 是否加入 -->
<el-table-column
prop="IsJoin"
:label="$t('trials:researchStaff:table:isJoin')"
show-overflow-tooltip
sortable="custom"
min-width="100"
>
<el-table-column prop="IsJoin" :label="$t('trials:researchStaff:table:isJoin')" show-overflow-tooltip
sortable="custom" min-width="100">
<template slot-scope="scope">
<el-tag v-if="scope.row.IsJoin" type="info">{{ $fd('IsJoin', scope.row.IsJoin) }}</el-tag>
<el-tag v-else type="danger">{{ $fd('IsJoin', scope.row.IsJoin) }}</el-tag>
@@ -246,7 +183,8 @@
</el-table>
<!-- 分页组件 -->
<pagination class="page" :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize" @pagination="getList" />
<pagination class="page" :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize"
@pagination="getList" />
</template>
</BaseContainer>
</template>
@@ -296,7 +234,6 @@ export default {
}
},
mounted() {
console.log(this.dict.type.SiteSurvey_UserRoles)
this.getList()
this.getSite()
this.getUserType()
@@ -1,670 +1,208 @@
<template>
<BaseContainer>
<!-- 搜索框 -->
<template slot="search-container">
<el-form :inline="true">
<!-- 中心 -->
<el-form-item :label="$t('trials:researchRecord:table:siteId')">
<el-select v-model="searchData.TrialSiteId" clearable filterable style="width: 120px">
<el-option v-for="(item, index) of siteOptions" :key="index" :label="item.TrialSiteCode"
:value="item.TrialSiteId" />
</el-select>
</el-form-item>
<!-- 联系人 -->
<el-form-item :label="$t('trials:researchRecord:table:contactor')">
<el-input v-model="searchData.UserKeyInfo" class="mr" clearable :placeholder="`${$t(
'trials:researchRecord:placeholder:contactorInfo'
)}`" style="width: 140px" />
</el-form-item>
<!-- 初审人 -->
<el-form-item :label="$t('trials:researchRecord:table:preliminaryUser')">
<el-input v-model="searchData.PreliminaryUserName" class="mr" clearable style="width: 140px" />
</el-form-item>
<!-- 审核人 -->
<el-form-item :label="$t('trials:researchRecord:table:ReviewerUser')">
<el-input v-model="searchData.ReviewerUserName" class="mr" clearable style="width: 140px" />
</el-form-item>
<!-- 状态 -->
<el-form-item :label="$t('trials:researchRecord:table:status')">
<el-select v-model="searchData.State" clearable filterable style="width: 120px">
<el-option v-for="(item, index) of $d.ResearchRecord" :key="index" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<!-- 是否废除 -->
<el-form-item :label="$t('trials:researchRecord:table:isDeleted')">
<el-select v-model="searchData.IsDeleted" clearable filterable style="width: 120px">
<el-option v-for="item of $d.YesOrNo" v-show="item.raw.ValueCN !== '无'" :key="`IsDeleted${item.value}`"
:label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- 更新时间 -->
<el-form-item :label="$t('trials:researchRecord:table:updateTime')">
<el-date-picker v-model="searchData.DateRange" type="daterange" value-format="yyyy-MM-dd" format="yyyy-MM-dd"
style="width: 250px" />
</el-form-item>
<!-- 查询 -->
<el-button type="primary" icon="el-icon-search" @click="handleSearch">
{{ $t('common:button:search') }}
</el-button>
<!-- 重置 -->
<el-button type="primary" icon="el-icon-refresh-left" @click="handleReset">
{{ $t('common:button:reset') }}
</el-button>
<!-- 中心人员 -->
<el-button v-hasPermi="[
'trials:trials-panel:attachments:site-research:summary-record',
]" type="primary" icon="el-icon-info" @click="showResearchUser">
{{ $t('trials:researchRecord:button:questionStaffs') }}
</el-button>
<!-- 调查表链接 -->
<el-button v-hasPermi="[
'trials:trials-panel:attachments:site-research:questionnaire-link',
]" type="primary" icon="el-icon-link" @click="showResearchLink">
{{ $t('trials:researchRecord:button:questionLink') }}
</el-button>
</el-form>
</template>
<template slot="main-container">
<!-- 系统文件列表 -->
<el-table ref="siteResearchList" v-loading="loading" v-adaptive="{ bottomOffset: 60 }" :data="list" stripe
height="100" @sort-change="handleSortByColumn">
<el-table-column type="index" width="50" />
<!-- 中心编号 -->
<el-table-column prop="TrialSiteCode" :label="$t('trials:researchRecord:table:siteId')" min-width="100"
sortable="custom" show-overflow-tooltip />
<!-- 中心名称 -->
<el-table-column prop="SiteName" :label="$t('trials:researchRecord:table:siteName')" min-width="100"
sortable="custom" show-overflow-tooltip />
<!-- 联系人 -->
<el-table-column prop="UserName" :label="$t('trials:researchRecord:table:contactor')" min-width="100"
sortable="custom" show-overflow-tooltip />
<!-- 联系电话 -->
<el-table-column prop="Phone" :label="$t('trials:researchRecord:table:contactorPhone')" min-width="100"
show-overflow-tooltip />
<!-- 联系邮箱 -->
<el-table-column prop="Email" :label="$t('trials:researchRecord:table:contactorEmail')" min-width="150"
show-overflow-tooltip />
<!-- 初审人 -->
<el-table-column prop="preliminaryUser" :label="$t('trials:researchRecord:table:preliminaryUser')"
min-width="150" show-overflow-tooltip>
<template slot-scope="scope">
{{
scope.row.PreliminaryUser
? scope.row.PreliminaryUser.RealName
: ''
}}
</template>
</el-table-column>
<!-- 审核人 -->
<el-table-column prop="ReviewerUser" :label="$t('trials:researchRecord:table:ReviewerUser')" min-width="150"
show-overflow-tooltip>
<template slot-scope="scope">
{{ scope.row.ReviewerUser ? scope.row.ReviewerUser.RealName : '' }}
</template>
</el-table-column>
<!-- 状态 -->
<el-table-column prop="State" :label="$t('trials:researchRecord:table:status')" min-width="150"
sortable="custom" show-overflow-tooltip>
<template slot-scope="scope">
<el-tag v-if="scope.row.State === 0" type="primary">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
<el-tag v-if="scope.row.State === 1" type="info">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
<el-tag v-if="scope.row.State === 2" type="warning">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
<el-tag v-if="scope.row.State === 3" type="danger">{{
$fd('ResearchRecord', scope.row.State)
}}</el-tag>
</template>
</el-table-column>
<!-- 是否废除 -->
<el-table-column prop="IsDeleted" :label="$t('trials:researchRecord:table:isDeleted')" min-width="100"
sortable="custom" show-overflow-tooltip>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsDeleted" type="danger">{{
$fd('YesOrNo', scope.row.IsDeleted)
}}</el-tag>
<el-tag v-else type="primary">{{
$fd('YesOrNo', scope.row.IsDeleted)
}}</el-tag>
</template>
</el-table-column>
<!-- 更新时间 -->
<el-table-column prop="UpdateTime" :label="$t('trials:researchRecord:table:updateTime')" min-width="150"
show-overflow-tooltip sortable="custom" />
<el-table-column width="150">
<template slot-scope="scope">
<!-- 查看 -->
<el-button :disabled="scope.row.State !== 3" circle :title="$t('common:button:view')" icon="el-icon-view"
@click="handleViewResearchList(scope.row)" />
<!-- 审批 -->
<el-button v-hasPermi="[
'trials:trials-panel:attachments:site-research:auidt',
]" :disabled="scope.row.State === 0 || scope.row.State === 3 || scope.row.IsDeleted" circle
:title="$t('trials:researchRecord:action:view')" icon="el-icon-s-check"
@click="handleViewResearchList(scope.row)" />
<!-- 废除 -->
<el-button v-hasPermi="[
'trials:trials-panel:attachments:site-research:abolish',
]" :disabled="scope.row.State !== 0 || scope.row.IsDeleted" circle
:title="$t('trials:researchRecord:action:abolish')" icon="el-icon-delete"
@click="handleRepealResearch(scope.row)" />
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination class="page" :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize"
@pagination="getList" />
</template>
<!-- 中心人员 -->
<el-dialog v-if="researchUserVisible" :visible.sync="researchUserVisible"
:title="$t('trials:researchRecord:dialogTitle:questionStaff')" custom-class="base-dialog-wrapper"
:fullscreen="true">
<div class="base-modal-body" style="border: 1px solid #ccc; padding: 10px">
<Users v-if="researchUserVisible" />
<div class="step-wrapper">
<el-steps :active="activeStatus" align-center :space="300">
<!-- 中心填写 -->
<el-step :title="$t('trials:siteResearch:timeline:step1')" class="click_cursor"
@click.native="handleClick(0)" />
<!-- SPM审核 -->
<el-step v-if="isSPMJoin" :title="$t('trials:siteResearch:timeline:step2')" class="click_cursor"
@click.native="handleClick(1)" />
<!-- PM审核 -->
<el-step :title="$t('trials:siteResearch:timeline:step3')" class="click_cursor"
@click.native="handleClick(2)" />
<!-- 审核完毕 -->
<el-step :title="$t('trials:siteResearch:timeline:step14')" class="click_cursor"
@click.native="handleClick(3)" />
</el-steps>
<div class="step-content">
<ResearchList ref="researchList" :isAudit="isAudit" />
</div>
</el-dialog>
<!-- 调查表 -->
<el-dialog v-if="researchInfoVisible" :visible.sync="researchInfoVisible" :fullscreen="true"
:close-on-click-modal="false">
<research-form v-if="researchInfoVisible" @refreshPage="getList" />
</el-dialog>
<!-- 调查表编辑 -->
<el-dialog v-if="ImageManualVisible" :visible.sync="ImageManualVisible" :fullscreen="true"
:close-on-click-modal="false" :title="$t('trials:researchRecord:dialogTitle:ImageManualEdit')">
<ImageManual v-if="ImageManualVisible" :trialSiteSurveyId="trialSiteSurveyId" @getList="getList" />
</el-dialog>
<!-- 调查表链接 -->
<base-model :config="share_model">
<template slot="dialog-body" v-loading="shareLoading">
<el-button size="small" type="primary" style="margin-bottom: 10px;" @click.stop="openImageManual">{{
$t('trials:researchRecord:label:edit')
}}</el-button>
<div style="width: 100%; display: flex">
<div class="date">
<el-form :model="shareForm" :rules="rules" ref="shareForm" label-width="100px">
<el-form-item :label="$t('trials:researchRecord:label:ExpirationDays')" prop="ExpirationDays">
<el-radio-group v-model="shareForm.ExpirationDays" @input="shareForm.OtherExpirationDays = null">
<el-radio :label="1">{{ $t('trials:researchRecord:label:day1') }}</el-radio>
<el-radio :label="7">{{ $t('trials:researchRecord:label:day7') }}</el-radio>
<el-radio :label="15">{{ $t('trials:researchRecord:label:day15') }}</el-radio>
<el-radio :label="`default`">{{ $t('trials:researchRecord:label:default') }}
<el-input placeholder="" type="number" @input="handleInput" v-model="shareForm.OtherExpirationDays"
clearable size="mini" style="width: 60px;" class="dayInput" />
<span style="margin-left: 10px;">{{ $t('trials:researchRecord:label:day') }}</span>
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('trials:researchRecord:label:LinkVerificationCode')" prop="LinkVerificationCode">
<el-radio-group v-model="shareForm.LinkVerificationCode"
@input="shareForm.OtherLinkVerificationCode = null">
<el-radio label="researchProgramNo">{{ $t('trials:researchRecord:label:researchProgramNo')
}}</el-radio>
<el-radio label="default">{{ $t('trials:researchRecord:label:default') }}
<el-input placeholder="" type="number" v-model="shareForm.OtherLinkVerificationCode" clearable
size="mini" style="width: 100px;" />
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<el-button type="primary" round @click="setLink" style="float: right;">
{{ $t('trials:researchRecord:button:setLink') }}
</el-button>
</div>
<div class="share">
<div class="shareLink">
<h3>{{ $t('trials:researchRecord:label:link') }}</h3>
<el-input ref="shareLink" v-model="shareLink" readonly type="textarea" autosize :rows="3" />
<div class="dateBox">
<span>{{ $t('trials:researchRecord:label:linkVerificationCode') }}</span>
<span>{{ LinkVerificationCode }}</span>
</div>
<div class="dateBox">
<span>{{ $t('trials:researchRecord:label:ValidityPeriod') }}</span>
<span>{{ validityPeriod }}</span>
<span style="color: red;" v-if="isExpired">({{ $t('trials:researchRecord:label:Expired') }})</span>
</div>
<el-button type="primary" round @click="copyLink" class="shareLinkBtn"
:disabled="!validityPeriod || isExpired">
{{ $t('trials:researchRecord:button:copyLink') }}
</el-button>
</div>
<div class="shareCode">
<h3>{{ $t('trials:researchRecord:label:shareCode') }}</h3>
<div style="display: flex;align-items: center;justify-content: space-between;">
<div class="qrCodeBox">
<div id="qrcode" ref="qrcode"></div>
</div>
<div class="codeBtnBox">
<el-button @click="handleCopyImg" type="primary" round :disabled="!validityPeriod || isExpired">{{
$t('trials:researchRecord:button:copyCode')
}}</el-button>
<el-button @click="savePic" round :disabled="!validityPeriod || isExpired">{{
$t('trials:researchRecord:button:savePic')
}}</el-button>
</div>
</div>
</div>
</div>
</div>
</template>
</base-model>
</div>
</BaseContainer>
</template>
<script>
import {
getTrialSiteSurveyList,
getTrialSiteSelect,
abandonSiteSurvey,
setTrialLinkExpirationTime,
getTrialLinkExpirationTime,
getLinkLinkExpirationTime,
} from '@/api/trials'
import { changeURLStatic } from '@/utils/history.js'
import { getTrialIsSPMJoin } from '@/api/research'
import BaseContainer from '@/components/BaseContainer'
import Pagination from '@/components/Pagination'
import Users from './components/users'
import ResearchForm from '@/views/research/form'
import BaseModel from '@/components/BaseModel'
import ImageManual from './components/ImageManual'
import QRCode from 'qrcodejs2'
const searchDataDefault = () => {
return {
SortField: '',
Asc: true,
PageIndex: 1,
PageSize: 20,
TrialSiteId: '',
UserKeyInfo: '',
State: null,
IsDeleted: '',
DateRange: [],
PreliminaryUserName: null,
ReviewerUserName: null,
}
}
import ResearchList from './components/ResearchList'
export default {
name: 'SiteResearchList',
components: { BaseContainer, Pagination, Users, ResearchForm, BaseModel, ImageManual },
name: 'SiteResearch',
components: {
BaseContainer,
ResearchList
},
data() {
return {
searchData: searchDataDefault(),
loading: false,
list: [],
total: 0,
trialId: this.$route.query.trialId,
siteOptions: [],
researchUserVisible: false,
researchInfoVisible: false,
share_model: {
visible: false,
title: this.$t('trials:researchRecord:title:shark'),
width: '1000px',
},
shareLink: '',
researchState: this.$d.ResearchRecord,
qrcode: null,
validityPeriod: null,
LinkVerificationCode: null,
isExpired: false,
shareLoading: false,
shareForm: {
ExpirationDays: null,
OtherExpirationDays: null,
LinkVerificationCode: null,
OtherLinkVerificationCode: null,
},
rules: {
ExpirationDays: [
{ required: true, message: this.$t("common:ruleMessage:select"), trigger: 'blur' },
{
validator: (rule, value, callback) => {
if (value === 'default' && !this.shareForm.OtherExpirationDays) {
callback(new Error(this.$t("common:ruleMessage:specify")));
} else {
callback()
}
}, trigger: 'blur'
isSPMJoin: true,
activeStatus: 0
}
},
watch: {
activeStatus: {
deep: true,
immediate: true,
handler(v) {
this.$nextTick(() => {
if (!this.isSPMJoin && v > 0) {
this.$refs['researchList'].searchData.State = v + 1
} else {
this.$refs['researchList'].searchData.State = v
}
],
LinkVerificationCode: [
{ required: true, message: this.$t("common:ruleMessage:select"), trigger: 'blur' },
{
validator: (rule, value, callback) => {
if (value === 'default' && !this.shareForm.OtherLinkVerificationCode) {
callback(new Error(this.$t("common:ruleMessage:specify")));
} else {
callback()
}
}, trigger: 'blur'
}
],
},
ImageManualVisible: false,
trialSiteSurveyId: null,
this.$refs['researchList'].getList()
})
}
}
},
computed: {
isAudit() {
if (this.isSPMJoin) {
return (this.hasPermi(['role:spm', 'role:cpm', 'role:apm']) && this.activeStatus === 1) || (this.hasPermi(['role:pm']) && this.activeStatus === 2)
} else {
return this.hasPermi(['role:pm']) && this.activeStatus === 1
}
}
},
mounted() {
this.getList()
this.getSite()
this.getTrialIsSPMJoin()
},
methods: {
async getLinkTimeIsExpired() {
async getTrialIsSPMJoin() {
try {
let data = {
TrialId: this.$route.query.trialId,
let trialId = this.$route.query.trialId
if (!trialId) return
let res = await getTrialIsSPMJoin(trialId)
this.isSPMJoin = res.Result
if (this.isSPMJoin) {
if (this.hasPermi(['role:pm'])) this.activeStatus = 2
if (this.hasPermi(['role:spm', 'role:cpm', 'role:apm'])) this.activeStatus = 1
} else {
if (this.hasPermi(['role:pm'])) this.activeStatus = 1
if (this.hasPermi(['role:spm', 'role:cpm', 'role:apm'])) this.activeStatus = 0
}
this.shareLoading = true
let res = await getLinkLinkExpirationTime(data)
this.shareLoading = false
if (res.IsSuccess) {
this.isExpired = res.Result.IsIsExpired
}
} catch (err) {
this.shareLoading = false
console.log(err)
} catch (e) {
console.log(e)
}
},
async getLinkTime() {
try {
let data = {
TrialId: this.$route.query.trialId,
}
this.shareLoading = true
let res = await getTrialLinkExpirationTime(data)
this.shareLoading = false
if (res.IsSuccess) {
this.validityPeriod = res.Result.LinkExpirationTime
this.LinkVerificationCode = res.Result.LinkVerificationCode || this.$route.query.researchProgramNo
if (!this.validityPeriod) {
this.LinkVerificationCode = null
}
if (!this.validityPeriod) return false
this.getLinkTimeIsExpired()
this.shareLink = `${location.protocol}//${location.host}/researchLogin?trialId=${this.$route.query.trialId}`
this.$nextTick(() => {
this.creatQrCode()
})
}
} catch (err) {
this.shareLoading = false
console.log(err)
}
handleClick(step) {
this.activeStatus = step
if (!this.isSPMJoin && step > 0) this.activeStatus = step - 1
},
async setLink() {
try {
let validate = await this.$refs.shareForm.validate()
if (!validate) return false
let data = {
TrialId: this.$route.query.trialId,
ExpirationDays: this.shareForm.ExpirationDays,
LinkVerificationCode: this.shareForm.LinkVerificationCode
}
if (this.shareForm.LinkVerificationCode === 'researchProgramNo') {
data.LinkVerificationCode = null
}
if (this.shareForm.LinkVerificationCode === 'default') {
data.LinkVerificationCode = this.shareForm.OtherLinkVerificationCode
}
if (this.shareForm.ExpirationDays === 'default') {
data.ExpirationDays = this.shareForm.OtherExpirationDays
}
this.shareLoading = true
let res = await setTrialLinkExpirationTime(data)
this.shareLoading = false
if (res.IsSuccess) {
this.getLinkTime()
}
} catch (err) {
this.shareLoading = false
console.log(err)
}
},
handleInput(val) {
//
this.shareForm.OtherExpirationDays = val.replace(/[^\d]/g, '').replace(/^0+/, '')
},
openImageManual() {
// if (!this.trialSiteSurveyId) return false
this.ImageManualVisible = true
},
//
getList() {
this.loading = true
this.searchData.TrialId = this.trialId
if (this.searchData.DateRange && this.searchData.DateRange.length === 2) {
this.searchData.UpdateTimeBegin = this.searchData.DateRange[0]
this.searchData.updateTimeEnd = this.searchData.DateRange[1]
} else {
this.searchData.UpdateTimeBegin = ''
this.searchData.updateTimeEnd = ''
}
getTrialSiteSurveyList(this.searchData)
.then((res) => {
this.loading = false
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
})
.catch(() => {
this.loading = false
})
},
//
handleViewResearchList(row) {
changeURLStatic('trialSiteSurveyId', row.Id)
this.researchInfoVisible = true
},
//
handleRepealResearch(row) {
//
this.$confirm(this.$t('trials:researchRecord:message:abolish'), {
type: 'warning',
distinguishCancelAndClose: true,
}).then(() => {
abandonSiteSurvey(this.trialId, row.Id).then((res) => {
if (res.IsSuccess) {
this.getList()
//
this.$message.success(
this.$t('trials:researchRecord:message:abolishSuccessfully')
)
}
})
})
},
//
showResearchUser() {
this.researchUserVisible = true
},
//
copyLink() {
//
this.$copyText(
`${this.$t('trials:researchRecord:message:researchFormLink')}: ${this.shareLink
}\n${this.$t('trials:researchRecord:label:linkVerificationCode')}${this.LinkVerificationCode}\n${this.$t('trials:researchRecord:label:ValidityPeriod')}${this.validityPeriod}`
)
.then((res) => {
//
this.$message.success(
this.$t('trials:researchRecord:message:copySuccessfully')
)
})
.catch(() => {
//
this.$alert(this.$t('trials:researchRecord:message:copyFailed'))
})
},
//
creatQrCode() {
this.$refs.qrcode.innerHTML = '' //
let text = this.shareLink
this.qrcode = new QRCode(this.$refs.qrcode, {
text: text, // ,#
width: 200,
height: 200,
colorDark: '#000000',
colorLight: '#ffffff',
correctLevel: QRCode.CorrectLevel.H,
})
// qrcode.clear(); //
},
//
savePic() {
let qrCodeCanvas = document
.getElementById('qrcode')
.getElementsByTagName('canvas')
let a = document.createElement('a')
a.href = qrCodeCanvas[0].toDataURL('image/url')
a.download = `${this.$route.query.researchProgramNo}${this.$t('trials:researchRecord:title:code')}.png`
a.click()
},
//
handleCopyImg() {
let qrCodeCanvas = document
.getElementById('qrcode')
.getElementsByTagName('canvas')
qrCodeCanvas[0].toBlob(async (blob) => {
console.log(blob)
const data = [
new ClipboardItem({
[blob.type]: blob,
}),
] // https://w3c.github.io/clipboard-apis/#dom-clipboard-write
await navigator.clipboard.write(data).then(
() => {
this.$message.success(
this.$t('trials:researchRecord:message:copySuccess')
)
},
() => {
this.$message.error(
this.$t('trials:researchRecord:message:UnableWrite')
)
}
)
})
},
// site
getSite() {
getTrialSiteSelect(this.trialId).then((res) => {
this.siteOptions = res.Result
})
},
//
showResearchLink() {
this.shareForm = {
ExpirationDays: null,
OtherExpirationDays: null,
LinkVerificationCode: null,
OtherLinkVerificationCode: null,
}
this.validityPeriod = null
this.isExpired = false
this.share_model.visible = true
this.getLinkTime()
// &lang=${this.$i18n.locale}
// this.trialSiteSurveyId = this.list[0].Id
},
//
handleReset() {
this.searchData = searchDataDefault()
this.searchData.DateRange = []
if (this.searchData.DateRange && this.searchData.DateRange.length === 2) {
this.searchData.UpdateTimeBegin = this.searchData.DateRange[0]
this.searchData.updateTimeEnd = this.searchData.DateRange[1]
} else {
this.searchData.UpdateTimeBegin = ''
this.searchData.updateTimeEnd = ''
}
this.getList()
this.$nextTick(() => {
this.$refs.siteResearchList.clearSort()
})
},
//
handleSearch() {
this.getList()
},
//
handleSortByColumn(column) {
if (column.order === 'ascending') {
this.searchData.Asc = true
} else {
this.searchData.Asc = false
}
this.searchData.SortField = column.prop
this.getList()
},
},
beforeDestroy() {
if (this.qrcode) {
this.qrcode = null
}
},
}
}
</script>
<style lang="scss" scoped>
.date,
.share {
width: 55%;
.step-wrapper {
height: 100%;
}
.dayInput {
::v-deep .el-input__inner {
padding-left: 5px;
}
}
.date {
padding-right: 10px;
}
.share {
width: 45%;
padding-left: 5%;
border-left: 1px solid #f0f0f0;
box-sizing: border-box;
}
.shareLinkBtn {
float: right;
}
.qrCodeBox {
width: 220px;
height: 220px;
flex: 1;
display: flex;
border: 1px solid #c0c4cc;
border-radius: 5px;
box-shadow: 1px 1px 5px #c0c4cc;
align-items: center;
flex-direction: column;
padding: 0;
margin: 0;
background-color: #fff;
}
.el-steps {
height: 80px;
justify-content: center;
}
.dateBox {
margin: 10px;
.step-content {
flex: 1;
}
.codeBtnBox {
.title {
height: 40px;
line-height: 40px;
padding-left: 5px;
background: #e4ebf1;
border-left: 3px solid #0fc8e0;
font-size: 13px;
::v-deep .el-button {
display: block;
margin: 10px auto;
p {
padding: 0;
margin: 0;
}
}
.trial-wrapper {
height: 60px;
.div-row {
display: flex;
justify-content: space-between;
margin: 10px 20px !important;
font-size: 13px;
.div-col {
flex: 1;
width: 100px;
white-space: nowrap !important;
text-overflow: ellipsis !important;
word-break: break-word !important;
overflow: hidden !important;
label {
font-weight: bold;
}
}
.div-textarea {
display: inline-block;
}
}
}
::v-deep .el-step__head.is-process {
color: #428bca;
border-color: #428bca;
}
::v-deep .el-step__title.is-process {
color: #428bca;
border-color: #428bca;
}
::v-deep .el-step__description.is-process {
color: #428bca;
border-color: #428bca;
}
::v-deep .el-step__head.is-process .el-step__line {
background-color: #428bca;
}
::v-deep .el-step__head.is-finish {
color: #303133;
border-color: #303133;
}
::v-deep .el-step__title.is-finish {
color: #303133;
border-color: #303133;
}
::v-deep .el-step__description.is-finish {
color: #303133;
border-color: #303133;
}
::v-deep .el-step__title.is-process {
text-decoration: none;
}
::v-deep .el-step__title.is-finish {
text-decoration: none;
}
::v-deep .el-step__head.is-finish .el-step__line {
background-color: #303133;
}
.click_cursor {
cursor: pointer;
}
</style>
File diff suppressed because it is too large Load Diff
@@ -218,9 +218,9 @@
<div class="text">{{ $t('trials:reading:button:screenShot') }}</div>
</div>
</el-tooltip>
<el-popover v-model="keySeriesPopoverVisible" placement="bottom" trigger="click" popper-class="key-series-popper"
@show="showKeySeriesPanel">
<el-popover v-model="keySeriesPopoverVisible" placement="bottom" trigger="click"
popper-class="key-series-popper" @show="showKeySeriesPanel">
<ul class="key-series-list">
<li v-if="keyStack && keyStack.taskBlindName" class="key-series-header">
<div class="key-series-header-top">
@@ -563,7 +563,7 @@
</el-button>
</div>
</div>
</transition>
@@ -596,7 +596,7 @@
</el-tab-pane>
<!-- 其他 -->
<el-tab-pane :label="$t('trials:reading:tab:others')" name="3">
<Others v-if="activeName === '3'" :imageToolType="1"/>
<Others v-if="activeName === '3'" :imageToolType="1" />
</el-tab-pane>
</el-tabs>
@@ -708,10 +708,10 @@
</el-dialog>
<upload-dicom-and-nonedicom v-if="uploadImageVisible" :subject-id="uploadSubjectId"
:subject-code="uploadSubjectCode" :criterion="uploadTrialCriterion" :visible.sync="uploadImageVisible"
:visit-task-id="taskId" :is-reading-task-view-in-order="isReadingTaskViewInOrder" />
:visit-task-id="taskId" :is-reading-task-view-in-order="isReadingTaskViewInOrder" :isReading="true" />
<download-dicom-and-nonedicom v-if="downloadImageVisible" :subject-id="uploadSubjectId"
:subject-code="uploadSubjectCode" :criterion="uploadTrialCriterion" :task-id="taskId"
:visible.sync="downloadImageVisible" />
:visible.sync="downloadImageVisible" :isReading="true" />
<!-- 签名框 -->
<el-dialog v-if="signVisible" :visible.sync="signVisible" :close-on-click-modal="false" width="600px"
custom-class="base-dialog-wrapper">
@@ -1129,7 +1129,7 @@ export default {
DicomEvent.$on('imageLocation', async (measuredData) => {
return new Promise(async resolve => {
if (!measuredData) return
await this.imageLocation(measuredData)
resolve()
})
@@ -1197,7 +1197,7 @@ export default {
} else if (this.CriterionType === 22) {
this.setToolActive('Probe', true, null, 'tableQuestion')
}
})
window.addEventListener('beforeunload', () => {
if (this.petctWindow) {
@@ -1451,7 +1451,7 @@ export default {
// } catch (err) {
// console.error(err)
// }
const series = this.getSeriesInfoByMark(visitTaskId, {lesionName: kf.MarkName})
const series = this.getSeriesInfoByMark(visitTaskId, { lesionName: kf.MarkName })
if (!series) return
this.loadImageStack(series)
},
@@ -2496,7 +2496,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({imageToolType: 1})
const res = await getAutoCutNextTask({ imageToolType: 1 })
var isAutoTask = res.Result.AutoCutNextTask
if (isAutoTask) {
window.location.reload()
@@ -2609,7 +2609,7 @@ export default {
await this.$confirm(this.$t('trials:lugano:fusionDialog:message:checkSeries'), this.$t('trials:lugano:fusionDialog:warning'), {
showCancelButton: false,
type: 'warning'
}).catch(() => {})
}).catch(() => { })
return
}
if (this.ctSeriesInfo.instanceCount > 400) {
@@ -33,17 +33,21 @@
<div v-for="item in qs.Childrens" :key="item.Id">
<div v-if="item.Type === 'basicTable'" class="flex-row" style="margin:3px 0;">
<div class="title">{{ item.QuestionName }}</div>
<div v-if="item.LesionType === 104 && readingTaskState < 2">
<div class="add-icon" @click.prevent="downloadTpl">
<div v-if="item.LesionType === 104">
<div
class="add-icon" @click.prevent="downloadTpl" v-if="readingTaskState < 2">
<i class="el-icon-download" />
</div>
<div class="add-icon" style="margin: 0 5px;"
@click.prevent="uploadTpl(item.LesionType, item.QuestionName)">
@click.prevent="uploadTpl(item.LesionType, item.QuestionName)" v-if="readingTaskState < 2">
<i class="el-icon-upload2" />
</div>
<div class="add-icon" @click.prevent="handleAddOrEdit('add', item)">
<div class="add-icon" @click.prevent="handleAddOrEdit('add', item)" v-if="readingTaskState < 2">
<i class="el-icon-plus" />
</div>
<div class="add-icon" @click.prevent="handleView(item)">
<i class="el-icon-view" />
</div>
</div>
</div>
@@ -185,11 +189,22 @@
</template>
<!-- 计算值 -->
<Questions ref="ecrf2" :question-form-change-state="true" :question-form-change-num="0" :is-qulity-issues="false"
:group-classify="4" style="margin-top:20px" />
<Questions
ref="ecrf2"
:question-form-change-state="true"
:question-form-change-num="0"
:is-qulity-issues="false"
:group-classify="4"
:question-type="12"
style="margin-top:20px" />
<!-- 评估结果 -->
<Questions ref="ecrf3" :question-form-change-state="true" :question-form-change-num="0" :is-qulity-issues="false"
<Questions
ref="ecrf3"
:question-form-change-state="true"
:question-form-change-num="0"
:is-qulity-issues="false"
:question-type="12"
:group-classify="5" />
<!-- 新增编辑弹窗 -->
<el-dialog v-if="addOrEdit.visible" :visible.sync="addOrEdit.visible" :close-on-click-modal="false"
@@ -222,6 +237,7 @@
<script>
import { saveTaskQuestion, submitTableQuestion, deleteReadingRowAnswer } from '@/api/trials'
import { resetReadingTask, getIVUSTemplate } from '@/api/reading'
import { getToken } from '@/utils/auth'
import DicomEvent from './../DicomEvent'
import store from '@/store'
import { mapGetters } from 'vuex'
@@ -284,7 +300,8 @@ export default {
sRoiEndDistanceId: '',
sRoiDistanceId: '',
rowSaveLoadingMap: {},
dialogSaveLoading: false
dialogSaveLoading: false,
childWindows: []
}
},
computed: {
@@ -324,11 +341,13 @@ export default {
DicomEvent.$on('refreshQuestions', _ => {
this.refreshQuestions()
})
window.addEventListener('beforeunload', this.closeAllChildWindows)
},
beforeDestroy() {
DicomEvent.$off('setCollapseActive')
DicomEvent.$off('getUnSaveTarget')
DicomEvent.$off('refreshQuestions')
window.removeEventListener('beforeunload', this.closeAllChildWindows)
},
methods: {
handleReadingChart(e) {
@@ -614,7 +633,7 @@ export default {
return v
}
},
async handleSave(index) {
async handleSave(index, options = {}) {
if (this.rowSaveLoadingMap[index]) return
const refName = `questions${index}`
this.$set(this.rowSaveLoadingMap, index, true)
@@ -632,9 +651,13 @@ export default {
answers: answers
}
await saveTaskQuestion(12, params)
this.$message.success(this.$t('common:message:savedSuccessfully'))
if (!options.silentSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
DicomEvent.$emit('getReportInfo', true)
this.refreshQuestions()
if (!options.skipRefresh) {
await this.refreshQuestions()
}
this.formChanged = false
} catch (e) {
console.log(e)
@@ -739,6 +762,24 @@ export default {
this.addOrEdit.visible = true
this.addOrEdit.lesionType = row.LesionType
},
handleView(item) {
localStorage.setItem('taskBlindName', this.taskBlindName)
localStorage.setItem('isReadingShowSubjectInfo', this.isReadingShowSubjectInfo)
let token = getToken()
let routeData = this.$router.resolve({ path: `/ecrfList?trialId=${this.$route.query.trialId}&visitTaskId=${this.visitTaskId}&questionId=${item.Id}&TokenKey=${token}` })
const win = window.open(routeData.href, '_blank')
if (win) {
this.childWindows.push(win)
}
},
closeAllChildWindows() {
this.childWindows.forEach(win => {
if (win && !win.closed) {
win.close()
}
})
this.childWindows = []
},
async saveFormData() {
if (this.dialogSaveLoading) return
this.dialogSaveLoading = true
@@ -832,9 +873,29 @@ export default {
}
},
async uploadDlgClose() {
this.refreshQuestions()
DicomEvent.$emit('getReportInfo', true)
this.upload.visible = false
try {
await this.refreshQuestions()
const saveTasks = []
for (let index = 0; index < this.questions.length; index++) {
const qs = this.questions[index]
if (qs.GroupClassify === 6) {
saveTasks.push(() => this.handleSave(index, { silentSuccess: true, skipRefresh: true }))
}
}
saveTasks.push(() => this.$refs['ecrf2'] && this.$refs['ecrf2'].handleSave({ silentSuccess: true, skipRefresh: true }))
saveTasks.push(() => this.$refs['ecrf3'] && this.$refs['ecrf3'].handleSave({ silentSuccess: true, skipRefresh: true }))
for (const saveTask of saveTasks) {
try {
await saveTask()
} catch (e) {
console.log(e)
}
}
} catch (e) {
console.log(e)
} finally {
this.upload.visible = false
}
},
renderHeader(h, { column, $index }) {
const span = document.createElement('span')
@@ -30,12 +30,12 @@
<div class="image-viewer__actions__inner">
<i class="el-icon-zoom-out" @click="handleActions('zoomOut')" />
<i class="el-icon-zoom-in" @click="handleActions('zoomIn')" />
<i class="el-image-viewer__actions__divider" />
<!-- <i class="el-image-viewer__actions__divider" /> -->
<i class="el-icon-c-scale-to-original" @click="toggleMode" />
<i class="el-image-viewer__actions__divider" />
<!-- <i class="el-image-viewer__actions__divider" /> -->
<i class="el-icon-refresh-left" @click="handleActions('anticlocelise')" />
<i class="el-icon-refresh-right" @click="handleActions('clocelise')" />
<i v-if="readingTaskState < 2" class="el-icon-bottom-right"
<i v-if="readingTaskState < 2 && taskInfo.CriterionType !== 19 && taskInfo.CriterionType !== 20" class="el-icon-bottom-right"
:style="{ cursor: isToolDisabled || readingTaskState >= 2 ? 'not-allowed' : 'pointer' }"
@click="handleDrawArrow" />
@@ -44,7 +44,7 @@
:style="{cursor:isToolDisabled || readingTaskState >=2 ?'not-allowed':'pointer'}"
@click="handleDrawRectangle"
/> -->
<svg-icon v-if="readingTaskState < 2" icon-class="rectangle" class="svg-icon"
<svg-icon v-if="readingTaskState < 2 && taskInfo.CriterionType !== 19 && taskInfo.CriterionType !== 20" icon-class="rectangle" class="svg-icon"
:style="{ cursor: isToolDisabled || readingTaskState >= 2 ? 'not-allowed' : 'pointer' }"
@click="handleDrawRectangle" />
</div>
@@ -109,6 +109,7 @@ export default {
}
},
data() {
const taskInfo = JSON.parse(localStorage.getItem('taskInfo') || '{}')
return {
loading: false,
index: this.initialIndex,
@@ -137,11 +138,13 @@ export default {
currentDrawData: {},
canvasData: [],
imgZoom: 1,
isDragging: false,
isToolDisabled: false,
lesionName: '',
visitTaskId: '',
isInit: false,
imgSize: { height: '', width: '' } //
imgSize: { height: '', width: '' }, //
taskInfo
}
},
computed: {
@@ -209,6 +212,10 @@ export default {
}
this.deviceSupportInstall()
},
beforeDestroy() {
window.removeEventListener('message', this.receiveMsg)
this.stopDragging()
},
methods: {
getCanvasData() {
return new Promise(async resolve => {
@@ -337,25 +344,55 @@ export default {
//
initCanvas(img) {
this.loading = true
this.canvasSize.height = img.height
this.canvasSize.width = img.width
this.imgSize.height = img.height
this.imgSize.width = img.width
const canvasDiv = document.getElementsByClassName('image-viewer__canvas')[0]
const widthRatio = canvasDiv && canvasDiv.offsetWidth ? canvasDiv.offsetWidth / img.width : 1
const heightRatio = canvasDiv && canvasDiv.offsetHeight ? canvasDiv.offsetHeight / img.height : 1
this.imgZoom = Math.min(widthRatio, heightRatio, 1)
this.canvasSize.height = Math.round(img.height * this.imgZoom)
this.canvasSize.width = Math.round(img.width * this.imgZoom)
this.canvasPosition = {
x: canvasDiv.offsetWidth / 2 - img.width / 2,
y: canvasDiv.offsetHeight / 2 - img.height / 2 - 25
x: canvasDiv.offsetWidth / 2 - this.canvasSize.width / 2,
y: canvasDiv.offsetHeight / 2 - this.canvasSize.height / 2 - 25
} //
this.$nextTick(async () => {
ctx = document.getElementById('canvas').getContext('2d') //
await ctx.drawImage(img, 0, 0, img.width, img.height) // canvas()
await ctx.drawImage(img, 0, 0, this.canvasSize.width, this.canvasSize.height) // canvas()
this.drawToArr()
this.loading = false
})
},
scalePoint(point) {
return {
x: point.x * this.imgZoom,
y: point.y * this.imgZoom
}
},
startDragging(e) {
const { offsetX, offsetY } = this.transform
const startX = e.pageX
const startY = e.pageY
this.isDragging = true
this._dragHandler = rafThrottle((ev) => {
this.transform.offsetX = offsetX + ev.pageX - startX
this.transform.offsetY = offsetY + ev.pageY - startY
})
this._dragEndHandler = () => {
this.stopDragging()
}
on(document, 'mousemove', this._dragHandler)
on(document, 'mouseup', this._dragEndHandler)
},
stopDragging() {
off(document, 'mousemove', this._dragHandler)
off(document, 'mouseup', this._dragEndHandler)
this._dragHandler = null
this._dragEndHandler = null
this.isDragging = false
},
//
clearCanvas() {
@@ -381,6 +418,11 @@ export default {
if (e.button !== 0) {
return
}
if (!this.model) {
this.startDragging(e)
e.preventDefault()
return
}
this.isMouseDown = true //
//
this.canvasMouseStart = {
@@ -391,6 +433,9 @@ export default {
//
async handleMouseMove(e) {
if (!this.model || this.isDragging) {
return
}
// ()
this.mousePosition = {
x: e.pageX,
@@ -437,10 +482,12 @@ export default {
var headlen = 10// 线
var theta = 45// 线线
var arrowX, arrowY// 线
var fromX = e.data.handles.start.x
var fromY = e.data.handles.start.y
var toX = e.data.handles.end.x
var toY = e.data.handles.end.y
var start = this.scalePoint(e.data.handles.start)
var end = this.scalePoint(e.data.handles.end)
var fromX = start.x
var fromY = start.y
var toX = end.x
var toY = end.y
//
var angle = Math.atan2(fromY - toY, fromX - toX) * 180 / Math.PI
var angle1 = (angle + theta) * Math.PI / 180
@@ -453,8 +500,8 @@ export default {
// 线
ctx.lineWidth = this.lineWidth
ctx.beginPath()
ctx.moveTo(fromX * this.imgZoom, fromY * this.imgZoom)
ctx.lineTo(toX * this.imgZoom, toY * this.imgZoom)
ctx.moveTo(fromX, fromY)
ctx.lineTo(toX, toY)
arrowX = toX + topX
arrowY = toY + topY
@@ -473,18 +520,16 @@ export default {
ctx.lineWidth = this.lineWidth
ctx.beginPath()
ctx.strokeStyle = this.color
const start = this.scalePoint(e.data.handles.start)
const end = this.scalePoint(e.data.handles.end)
//
ctx.strokeRect(
// this.canvasMouseStart.x,
// this.canvasMouseStart.y,
// e.offsetX - this.canvasMouseStart.x,
// e.offsetY - this.canvasMouseStart.y
e.data.handles.start.x,
e.data.handles.start.y,
e.data.handles.end.x - e.data.handles.start.x,
e.data.handles.end.y - e.data.handles.start.y
start.x,
start.y,
end.x - start.x,
end.y - start.y
)
this.drawText(ctx, { x: e.data.handles.start.x, y: e.data.handles.start.y }, { x: e.data.handles.end.x, y: e.data.handles.end.y }, e.data.remark, this.color)
this.drawText(ctx, start, end, e.data.remark, this.color)
}
}
})
@@ -495,6 +540,10 @@ export default {
if (e.button !== 0) {
return //
}
if (this.isDragging) {
this.stopDragging()
return
}
//
if (this.model === 'ArrowAnnotate' || this.model === 'RectangleRoi') {
@@ -33,17 +33,20 @@
<div v-for="item in qs.Childrens" :key="item.Id">
<div v-if="item.Type === 'basicTable'" class="flex-row" style="margin:3px 0;">
<div class="title">{{ item.QuestionName }}</div>
<div v-if="(item.LesionType === 104) && readingTaskState < 2">
<div class="add-icon" @click.prevent="downloadTpl(item.LesionType)">
<div v-if="(item.LesionType === 104)">
<div class="add-icon" @click.prevent="downloadTpl(item.LesionType)" v-if="readingTaskState < 2">
<i class="el-icon-download" />
</div>
<div class="add-icon" style="margin: 0 5px;"
@click.prevent="uploadTpl(item.LesionType, item.QuestionName)">
@click.prevent="uploadTpl(item.LesionType, item.QuestionName)" v-if="readingTaskState < 2">
<i class="el-icon-upload2" />
</div>
<div class="add-icon" @click.prevent="handleAddOrEdit('add', item)">
<div class="add-icon" @click.prevent="handleAddOrEdit('add', item)" v-if="readingTaskState < 2">
<i class="el-icon-plus" />
</div>
<div class="add-icon" @click.prevent="handleView(item)">
<i class="el-icon-view" />
</div>
</div>
</div>
@@ -186,11 +189,22 @@
</template>
<!-- 计算值 -->
<Questions ref="ecrf2" :question-form-change-state="true" :question-form-change-num="0" :is-qulity-issues="false"
:group-classify="4" style="margin-top:20px" />
<Questions
ref="ecrf2"
:question-form-change-state="true"
:question-form-change-num="0"
:is-qulity-issues="false"
:group-classify="4"
:question-type="12"
style="margin-top:20px" />
<!-- 评估结果 -->
<Questions ref="ecrf3" :question-form-change-state="true" :question-form-change-num="0" :is-qulity-issues="false"
<Questions
ref="ecrf3"
:question-form-change-state="true"
:question-form-change-num="0"
:is-qulity-issues="false"
:question-type="12"
:group-classify="5" />
<el-dialog v-if="addOrEdit.visible" :visible.sync="addOrEdit.visible" :close-on-click-modal="false"
:title="addOrEdit.title" width="500px">
@@ -224,6 +238,7 @@ import { saveTaskQuestion, submitTableQuestion, deleteReadingRowAnswer } from '@
import { resetReadingTask, getOCTFCTTemplate, getOCTLipidAngleTemplate } from '@/api/reading'
import DicomEvent from './../DicomEvent'
import store from '@/store'
import { getToken } from '@/utils/auth'
import { mapGetters } from 'vuex'
import Questions from './../Questions'
import QuestionTableFormItem from './QuestionTableFormItem'
@@ -285,7 +300,8 @@ export default {
sRoiEndDistanceId: '',
sRoiDistanceId: '',
rowSaveLoadingMap: {},
dialogSaveLoading: false
dialogSaveLoading: false,
childWindows: []
}
},
computed: {
@@ -325,11 +341,13 @@ export default {
DicomEvent.$on('refreshQuestions', _ => {
this.refreshQuestions()
})
window.addEventListener('beforeunload', this.closeAllChildWindows)
},
beforeDestroy() {
DicomEvent.$off('setCollapseActive')
DicomEvent.$off('getUnSaveTarget')
DicomEvent.$off('refreshQuestions')
window.removeEventListener('beforeunload', this.closeAllChildWindows)
},
methods: {
handleReadingChart(e) {
@@ -610,7 +628,7 @@ export default {
return v
}
},
async handleSave(index) {
async handleSave(index, options = {}) {
if (this.rowSaveLoadingMap[index]) return
const refName = `questions${index}`
this.$set(this.rowSaveLoadingMap, index, true)
@@ -628,9 +646,13 @@ export default {
answers: answers
}
await saveTaskQuestion(12, params)
this.$message.success(this.$t('common:message:savedSuccessfully'))
if (!options.silentSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
DicomEvent.$emit('getReportInfo', true)
this.refreshQuestions()
if (!options.skipRefresh) {
await this.refreshQuestions()
}
this.formChanged = false
} catch (e) {
console.log(e)
@@ -755,6 +777,27 @@ export default {
this.addOrEdit.visible = true
this.addOrEdit.lesionType = row.LesionType
},
handleView(item) {
if (this.openWindow) {
this.openWindow.close()
}
localStorage.setItem('taskBlindName', this.taskBlindName)
localStorage.setItem('isReadingShowSubjectInfo', this.isReadingShowSubjectInfo)
let token = getToken()
let routeData = this.$router.resolve({ path: `/ecrfList?trialId=${this.$route.query.trialId}&visitTaskId=${this.visitTaskId}&questionId=${item.Id}&TokenKey=${token}` })
const win = window.open(routeData.href, '_blank')
if (win) {
this.childWindows.push(win)
}
},
closeAllChildWindows() {
this.childWindows.forEach(win => {
if (win && !win.closed) {
win.close()
}
})
this.childWindows = []
},
async saveFormData() {
if (this.dialogSaveLoading) return
this.dialogSaveLoading = true
@@ -856,9 +899,29 @@ export default {
}
},
async uploadDlgClose() {
this.refreshQuestions()
DicomEvent.$emit('getReportInfo', true)
this.upload.visible = false
try {
await this.refreshQuestions()
const saveTasks = []
for (let index = 0; index < this.questions.length; index++) {
const qs = this.questions[index]
if (qs.GroupClassify === 6) {
saveTasks.push(() => this.handleSave(index, { silentSuccess: true, skipRefresh: true }))
}
}
saveTasks.push(() => this.$refs['ecrf2'] && this.$refs['ecrf2'].handleSave({ silentSuccess: true, skipRefresh: true }))
saveTasks.push(() => this.$refs['ecrf3'] && this.$refs['ecrf3'].handleSave({ silentSuccess: true, skipRefresh: true }))
for (const saveTask of saveTasks) {
try {
await saveTask()
} catch (e) {
console.log(e)
}
}
} catch (e) {
console.log(e)
} finally {
this.upload.visible = false
}
},
renderHeader(h, { column, $index }) {
const span = document.createElement('span')
@@ -279,84 +279,86 @@ export default {
})
},
handleSave() {
if (this.saveLoading) return
async handleSave(options = {}) {
if (this.saveLoading) return false
this.saveLoading = true
this.$refs['questions'].validate(async (valid) => {
if (!valid) {
this.saveLoading = false
return
}
// lugano
if ((this.criterionType === 2 || this.criterionType === 18) && this.groupClassify === 3) {
//
var existUnSave = this.checkAnnotationStatus(this.questions)
if (existUnSave) {
this.$alert(this.$t('trials:lugano:message:saveWarning'), this.$t('trials:lugano:fusionDialog:warning'))
this.saveLoading = false
return
}
var currentSpleenStatus = this.questionForm[this.spleenStatusId]
var currentSpleenLength = this.questionForm[this.spleenLengthId]
currentSpleenStatus = isNaN(parseInt(currentSpleenStatus)) ? null : parseInt(currentSpleenStatus)
var stIdx = this.measurements.findIndex(i => i.QuestionType === 60)
var slIdx = this.measurements.findIndex(i => i.QuestionType === 61)
if (currentSpleenLength && currentSpleenStatus === 5) {
// '!'
this.$alert(this.$t('trials:lugano:message:validSpleen1'), this.$t('trials:lugano:fusionDialog:warning'), {
callback: _ => { }
})
this.saveLoading = false
} else if (currentSpleenStatus === 5 && ((stIdx > -1 && this.measurements[stIdx].MeasureData) || (slIdx > -1 && this.measurements[slIdx].MeasureData))) {
//
this.$alert(this.$t('trials:lugano:message:validSpleen1'), this.$t('trials:lugano:fusionDialog:warning'), {
callback: _ => { }
})
this.saveLoading = false
} else if (((stIdx > -1 && this.measurements[stIdx].MeasureData) || (slIdx > -1 && this.measurements[slIdx].MeasureData)) && !currentSpleenLength) {
this.$alert(this.$t('trials:lugano:message:validSpleen2'), this.$t('trials:lugano:fusionDialog:warning'), {
callback: _ => { }
})
this.saveLoading = false
} else {
this.saveQuestionsList()
}
} else if ((this.criterionType === 2 || this.criterionType === 18) && this.groupClassify === 1 && this.questionForm[this.imageQualityIssuesId] && parseInt(this.questionForm[this.imageQualityIssuesId]) === 6) {
// PET-CT
let res = null
if (this.criterionType === 18) {
res = await getCanChooseNotMergeNoPet({ visitTaskId: this.visitTaskId })
} else {
res = await getCanChooseNotMerge({ visitTaskId: this.visitTaskId })
}
if (res.IsSuccess && !res.Result.IsCanChooseNotMerge) {
// this.$alert(this.$t('trials:lugano:saveQuestions:warning1'), this.$t('trials:lugano:fusionDialog:warning'), {showCancelButton: false})
this.$alert(this.$t('trials:lugano:saveQuestions:warning1'), this.$t('trials:lugano:fusionDialog:warning'), {
showCancelButton: false,
callback: action => { }
})
this.saveLoading = false
return
} else {
// PET-CTFDG-PETNE
this.$confirm(this.$t('trials:lugano:saveQuestions:warning2'), {
type: 'warning',
distinguishCancelAndClose: true
})
.then(() => {
this.saveQuestionsList()
})
}
} else {
this.saveQuestionsList()
}
// this.saveQuestionsList()
const valid = await new Promise(resolve => {
this.$refs['questions'].validate(v => resolve(v))
})
if (!valid) {
this.saveLoading = false
return false
}
// lugano
if ((this.criterionType === 2 || this.criterionType === 18) && this.groupClassify === 3) {
//
var existUnSave = this.checkAnnotationStatus(this.questions)
if (existUnSave) {
await this.$alert(this.$t('trials:lugano:message:saveWarning'), this.$t('trials:lugano:fusionDialog:warning'))
this.saveLoading = false
return false
}
var currentSpleenStatus = this.questionForm[this.spleenStatusId]
var currentSpleenLength = this.questionForm[this.spleenLengthId]
currentSpleenStatus = isNaN(parseInt(currentSpleenStatus)) ? null : parseInt(currentSpleenStatus)
var stIdx = this.measurements.findIndex(i => i.QuestionType === 60)
var slIdx = this.measurements.findIndex(i => i.QuestionType === 61)
if (currentSpleenLength && currentSpleenStatus === 5) {
// '!'
await this.$alert(this.$t('trials:lugano:message:validSpleen1'), this.$t('trials:lugano:fusionDialog:warning'), {
callback: _ => { }
})
this.saveLoading = false
return false
} else if (currentSpleenStatus === 5 && ((stIdx > -1 && this.measurements[stIdx].MeasureData) || (slIdx > -1 && this.measurements[slIdx].MeasureData))) {
//
await this.$alert(this.$t('trials:lugano:message:validSpleen1'), this.$t('trials:lugano:fusionDialog:warning'), {
callback: _ => { }
})
this.saveLoading = false
return false
} else if (((stIdx > -1 && this.measurements[stIdx].MeasureData) || (slIdx > -1 && this.measurements[slIdx].MeasureData)) && !currentSpleenLength) {
await this.$alert(this.$t('trials:lugano:message:validSpleen2'), this.$t('trials:lugano:fusionDialog:warning'), {
callback: _ => { }
})
this.saveLoading = false
return false
}
return await this.saveQuestionsList(options)
} else if ((this.criterionType === 2 || this.criterionType === 18) && this.groupClassify === 1 && this.questionForm[this.imageQualityIssuesId] && parseInt(this.questionForm[this.imageQualityIssuesId]) === 6) {
// PET-CT
let res = null
if (this.criterionType === 18) {
res = await getCanChooseNotMergeNoPet({ visitTaskId: this.visitTaskId })
} else {
res = await getCanChooseNotMerge({ visitTaskId: this.visitTaskId })
}
if (res.IsSuccess && !res.Result.IsCanChooseNotMerge) {
await this.$alert(this.$t('trials:lugano:saveQuestions:warning1'), this.$t('trials:lugano:fusionDialog:warning'), {
showCancelButton: false,
callback: action => { }
})
this.saveLoading = false
return false
}
// PET-CTFDG-PETNE
const confirm = await this.$confirm(this.$t('trials:lugano:saveQuestions:warning2'), {
type: 'warning',
distinguishCancelAndClose: true
}).then(() => true).catch(() => false)
if (!confirm) {
this.saveLoading = false
return false
}
return await this.saveQuestionsList(options)
} else {
return await this.saveQuestionsList(options)
}
},
async saveQuestionsList() {
async saveQuestionsList(options = {}) {
this.loading = true
var answers = []
var imageQuality = null
@@ -382,18 +384,22 @@ export default {
// })
// params.questionMarkInfoList = questionMarkInfoList
// }
saveTaskQuestion(this.questionType, params).then(async res => {
try {
const res = await saveTaskQuestion(this.questionType, params)
if (this.imageQualityIssuesId) {
store.dispatch('reading/setImageQualityIssues', this.questionForm[this.imageQualityIssuesId])
if (this.questionForm[this.imageQualityIssuesId] && parseInt(this.questionForm[this.imageQualityIssuesId]) === 6) {
DicomEvent.$emit('closePetct')
}
}
this.$message.success(this.$t('common:message:savedSuccessfully'))
var trialId = this.$route.query.trialId
await store.dispatch('reading/refreshDicomReadingQuestionAnswer', { trialId: trialId, visitTaskId: this.visitTaskId })
this.getQuestions(this.visitTaskId, true)
if (!options.silentSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
if (!options.skipRefresh) {
var trialId = this.$route.query.trialId
await store.dispatch('reading/refreshDicomReadingQuestionAnswer', { trialId: trialId, visitTaskId: this.visitTaskId })
this.getQuestions(this.visitTaskId, true)
}
this.loading = false
if (this.isQulityIssues) {
DicomEvent.$emit('questionFormChange', false)
@@ -417,10 +423,12 @@ export default {
}
this.formChanged = false
this.saveLoading = false
}).catch(() => {
return res
} catch (e) {
this.loading = false
this.saveLoading = false
})
throw e
}
},
checkAnnotationStatus(obj) {
for (let i = 0; i < obj.length; i++) {
@@ -27,7 +27,7 @@
class="study-wrapper">
<StudyList v-if="selectArr.includes(s.VisitTaskId)" :ref="s.VisitTaskId" :visit-task-id="s.VisitTaskId"
:trial-id="trialId" :subject-visit-id="s.VisitId" :task-blind-name="s.TaskBlindName"
:is-reading-show-subject-info="isReadingShowSubjectInfo" @loadImageStack="loadImageStack"
:is-reading-show-subject-info="isReadingShowSubjectInfo" :is-reading-task-view-in-order="isReadingTaskViewInOrder" @loadImageStack="loadImageStack"
@previewNoneDicoms="previewNoneDicoms" />
</div>
</div>
@@ -505,15 +505,23 @@ export default {
var idx = item.Childrens.findIndex(i => i.QuestionMark === 8)
var idxLoc = item.Childrens.findIndex(i => i.QuestionMark === 10)
var state = item.Childrens.findIndex(i => i.QuestionMark === 7)
if (this.CriterionType === 22) {
idx = item.Childrens.findIndex(i => i.QuestionMark === 1106)
}
if (idx > -1) {
if (item.Childrens[idx].Answer.length > 0) {
var k = item.Childrens[idx].Answer.findIndex(v => v.Answer !== '')
var part = ''
if (obj.IsCanEditPosition) {
part = `${item.Childrens[idx].Answer[k].Answer}--${item.Childrens[idxLoc].Answer[k].Answer}`
if (this.CriterionType === 22) {
part = item.Childrens[idx].Answer[k].Answer ? this.$fd('Liver4Segmentation', parseInt(item.Childrens[idx].Answer[k].Answer)) : ''
} else {
part = `${item.Childrens[idx].Answer[k].Answer}`
if (obj.IsCanEditPosition) {
part = `${item.Childrens[idx].Answer[k].Answer}--${item.Childrens[idxLoc].Answer[k].Answer}`
} else {
part = `${item.Childrens[idx].Answer[k].Answer}`
}
}
if (item.SplitOrMergeLesionName && k > -1) {
// obj.QuestionName = `${obj.QuestionName} --${part} (Split from ${item.SplitOrMergeLesionName})`
obj.QuestionName = `${obj.QuestionName} --${part}`
@@ -531,6 +539,7 @@ export default {
this.$set(obj, 'Answers', Answers)
// obj.QuestionName = `${obj.QuestionName} `
} else if (!item.SplitOrMergeLesionName && k > -1) {
console.log(part)
obj.QuestionName = `${obj.QuestionName}--${part}`
const Answers = {}
if (state >= 0) {
@@ -28,7 +28,7 @@
</div>
<div>
<span class="study-meta-line" :title="study.Modalities">
<span class="study-code" :title="study.StudyCode">{{ study.StudyCode }}</span>
<span v-if="isReadingTaskViewInOrder !== 0" class="study-code" :title="study.StudyCode">{{ study.StudyCode }}</span>
<span class="study-modality">{{ study.Modalities }}({{ study.SeriesCount }})</span>
<span class="patient-info" v-if="['PT、CT', 'CT、PT', 'PET-CT'].includes(study.Modalities)">
<el-popover placement="right-start" trigger="hover" popper-class="patient-info-popper">
@@ -226,6 +226,10 @@ export default {
isReadingShowSubjectInfo: {
type: Boolean,
required: true
},
isReadingTaskViewInOrder: {
type: Number,
required: true
}
},
data() {
@@ -340,7 +344,7 @@ export default {
}
})
var i = -1
var isReadingTaskViewInOrder = parseInt(this.$router.currentRoute.query.isReadingTaskViewInOrder)
var isReadingTaskViewInOrder = parseInt(this.isReadingTaskViewInOrder)
if (isReadingTaskViewInOrder === 2) {
//
i = res.length === 2 ? 1 : -1
@@ -427,7 +431,7 @@ export default {
async getInitSeries() {
var seriesList = []
var isReadingTaskViewInOrder = parseInt(this.$router.currentRoute.query.isReadingTaskViewInOrder)
var isReadingTaskViewInOrder = parseInt(this.isReadingTaskViewInOrder)
var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
this.studyList = this.visitTaskList[idx].StudyList
const studyList = this.visitTaskList[idx].StudyList.filter(i => i.IsDicom)
@@ -0,0 +1,115 @@
<template>
<div class="table-list-container" v-loading="loading">
<h3 v-if="isReadingShowSubjectInfo">
<span v-if="subjectCode">{{ subjectCode }} </span>
<span style="margin-left:5px;">{{ taskBlindName }}</span>
</h3>
<h3 v-if="tableQuestion && tableQuestion.GroupName">
{{ language === 'en' ? tableQuestion.GroupEnName : tableQuestion.GroupName }}
</h3>
<h4 v-if="tableQuestion && tableQuestion.QuestionName">
{{ tableQuestion.QuestionName }}
</h4>
<el-table
v-if="tableQuestion && tableQuestion.Type === 'basicTable' && tableQuestion.TableQuestions"
:ref="tableQuestion.Id"
:data="tableQuestion.TableQuestions.Answers"
height="100%"
>
<el-table-column
v-for="q of tableQuestion.TableQuestions.Questions"
:key="q.Id"
:prop="q.Id"
:label="q.QuestionName"
show-overflow-tooltip>
<template slot-scope="scope">
<span v-if="q.Unit > 0 && !isNaN(parseFloat(scope.row[q.Id]))">
{{ `${scope.row[q.Id]} ${$fd('ValueUnit', parseInt(q.Unit))}` }}
</span>
<span v-else-if="q.DictionaryCode">
{{`${scope.row[q.Id] instanceof Array ? scope.row[q.Id].map(item => $fd(q.DictionaryCode,
parseInt(item))).join(',') : $fd(q.DictionaryCode, parseInt(scope.row[q.Id]))}`}}
</span>
<span v-else-if="q.OptionTypeEnum === 1">
{{ `${scope.row[q.Id] instanceof Array ? scope.row[q.Id].join(',') : scope.row[q.Id]}` }}
</span>
<span v-else>
{{ `${scope.row[q.Id]}` }}
</span>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import { getFilterTableQuestion } from '@/api/reading'
import store from '@/store'
import { mapGetters } from 'vuex'
import { changeURLStatic } from '@/utils/history.js'
export default {
name: 'DicomTableList',
data() {
return {
isReadingShowSubjectInfo: false,
subjectCode: null,
taskBlindName: null,
questionId: null,
tableQuestion: null,
loading: false
}
},
computed: {
...mapGetters(['language'])
},
created() {
if (this.$route.query.TokenKey) {
store.dispatch('user/setToken', this.$route.query.TokenKey)
changeURLStatic('TokenKey', '')
}
this.subjectCode = localStorage.getItem('subjectCode')
this.taskBlindName = localStorage.getItem('taskBlindName')
this.isReadingShowSubjectInfo = localStorage.getItem('isReadingShowSubjectInfo')
this.getList()
},
methods: {
async getList() {
try {
this.loading = true
let params = {
trialId: this.$route.query.trialId,
visitTaskId: this.$route.query.visitTaskId,
questionId: this.$route.query.questionId
}
let res = await getFilterTableQuestion(params)
if (res.IsSuccess) {
this.tableQuestion = res.Result
}
} catch (e) {
console.log(e)
} finally {
this.loading = false
}
},
resetList() {
}
}
}
</script>
<style lang="scss" scoped>
.table-list-container {
height: 100%;
padding: 0 10px;
box-sizing: border-box;
display: flex;
flex-direction: column;
overflow: hidden;
}
h3, h4 {
flex-shrink: 0;
margin: 10px 0 10px;
}
</style>
@@ -411,7 +411,7 @@
<WL v-if="activeName === '2'" @getWwcTpl="getWwcTpl" />
</el-tab-pane>
<el-tab-pane :label="$t('trials:reading:tab:others')" name="3">
<Others v-if="activeName === '3'" :imageToolType="1"/>
<Others v-if="activeName === '3'" :imageToolType="1" />
</el-tab-pane>
</el-tabs>
</el-dialog>
@@ -448,10 +448,10 @@
/> -->
<upload-dicom-and-nonedicom v-if="uploadImageVisible" :SubjectId="uploadSubjectId" :SubjectCode="uploadSubjectCode"
:Criterion="uploadTrialCriterion" :visible.sync="uploadImageVisible" :VisitTaskId="taskId"
:isReadingTaskViewInOrder="isReadingTaskViewInOrder" />
:isReadingTaskViewInOrder="isReadingTaskViewInOrder" :isReading="true" />
<download-dicom-and-nonedicom v-if="downloadImageVisible" :SubjectId="uploadSubjectId"
:SubjectCode="uploadSubjectCode" :Criterion="uploadTrialCriterion" :TaskId="taskId"
:visible.sync="downloadImageVisible" />
:visible.sync="downloadImageVisible" :isReading="true"/>
</div>
</template>
<script>
@@ -147,7 +147,7 @@
</template>
</el-table-column>
</el-table>
<div style="text-align:right;margin-top: 10px;">
<div style="text-align:right;margin-top: 10px;" v-if="!IsViewStudyPart">
<!-- 确认 -->
<el-button type="primary" @click="handleConfirmBP">{{ $t('trials:reading:button:confirm')
}}</el-button>
@@ -223,7 +223,8 @@ export default {
readingVersionEnum: null,
dialogBodyPartVisible: false,
isReadKeyFile: true,
isHaveKeyFile: false
isHaveKeyFile: false,
IsViewStudyPart: true
}
},
computed: {
@@ -619,6 +620,14 @@ export default {
}
}
::v-deep .el-dialog.cd-dialog-container {
.el-input.is-disabled .el-input__inner {
background-color: rgb(245, 247, 250);
color: #666;
border: 1px solid #dcdfe6;
}
}
}
::v-deep .dialog-container {
@@ -12,7 +12,9 @@
{ required: true, message: $t('common:ruleMessage:select'), trigger: 'blur' }
]">
<el-select v-model="fusionForm.studyId" clearable @change="handleStudyIdChange">
<el-option v-for="item in studyList" :key="item.StudyId" :label="item.StudyCode" :value="item.StudyId" />
<el-option v-for="item in studyList" :key="item.StudyId"
:label="taskInfo.IsReadingTaskViewInOrder !== 0 ? item.StudyCode : `${item.Modalities}(${item.SeriesCount})`"
:value="item.StudyId" />
</el-select>
</el-form-item>
<!-- 融合图像 -->
@@ -111,10 +113,12 @@ export default {
ctSeries: [],
petSeries: [],
petctWindow: null,
digitPlaces: 2
digitPlaces: 2,
taskInfo: {}
}
},
mounted() {
this.taskInfo = JSON.parse(sessionStorage.getItem('taskInfo'))
var digitPlaces = Number(localStorage.getItem('digitPlaces'))
this.digitPlaces = digitPlaces === -1 ? this.digitPlaces : digitPlaces
this.initForm()
@@ -187,6 +187,7 @@ export default {
originalMarkers: [],
markers: { top: '', right: '', bottom: '', left: '' },
playClipState: false,
clipFramesPerSecond: null,
wwwcIdx: 2,
presetName: '',
volumeId: null,
@@ -625,6 +626,22 @@ export default {
}
},
syncViewportStaticState() {
const renderingEngine = getRenderingEngine(this.renderingEngineId)
if (!renderingEngine) return
const viewport = renderingEngine.getViewport(this.viewportId)
if (!viewport) return
this.getOrientationMarker()
const toolGroup =
cornerstoneTools.ToolGroupManager.getToolGroupForViewport(
this.viewportId,
this.renderingEngineId
) || cornerstoneTools.ToolGroupManager.getToolGroup(this.viewportId)
if (toolGroup) {
toolGroup.setToolEnabled('ScaleOverlay')
}
},
setFullScreen(index) {
setTimeout(() => {
const renderingEngine = getRenderingEngine(this.renderingEngineId)
@@ -727,14 +744,33 @@ export default {
}
},
toggleClipPlay(isPlay, framesPerSecond) {
this.playClipState = isPlay
const renderingEngine = getRenderingEngine(this.renderingEngineId)
if (!renderingEngine) return
const viewport = renderingEngine.getViewport(this.viewportId)
if (!viewport?.element) return
const parsedFramesPerSecond = Number(framesPerSecond)
const nextFramesPerSecond = Number.isFinite(parsedFramesPerSecond)
? parsedFramesPerSecond
: (this.clipFramesPerSecond || 15)
const isSameSpeedWhilePlaying = this.playClipState &&
isPlay &&
this.clipFramesPerSecond === nextFramesPerSecond
if (isPlay) {
cornerstoneTools.utilities.cine.playClip(viewport.element, { framesPerSecond, loop: true })
if (isSameSpeedWhilePlaying) return
cornerstoneTools.utilities.cine.playClip(viewport.element, {
framesPerSecond: nextFramesPerSecond,
loop: true,
waitForRendered: 1
})
this.clipFramesPerSecond = nextFramesPerSecond
this.playClipState = true
} else {
cornerstoneTools.utilities.cine.stopClip(viewport.element)
this.clipFramesPerSecond = null
this.playClipState = false
}
},
scrollPage(type) {
@@ -1125,6 +1161,7 @@ export default {
}
viewport.render()
this.syncViewportStaticState()
if (this.currentVoiUpper > 0) {
this.voiChange(this.currentVoiUpper)
}
@@ -284,7 +284,7 @@
<svg-icon icon-class="lastframe" class="svg-icon" />
</div>
<select v-model="fps" :title="$t('trials:dicom-show:speed')" class="select-wrapper"
:disabled="clipPlaying">
@change="handleClipFpsChange">
<!-- 默认值 -->
<option :value="5">5</option>
<option :value="10">10</option>
@@ -582,10 +582,11 @@
:renderingEngineId="renderingEngineId" :visitInfo="taskInfo" /> -->
<upload-dicom-and-nonedicom v-if="uploadImageVisible" :subject-id="uploadSubjectId"
:subject-code="uploadSubjectCode" :criterion="uploadTrialCriterion" :visible.sync="uploadImageVisible"
:visit-task-id="taskId" :is-reading-task-view-in-order="isReadingTaskViewInOrder" />
:visit-task-id="taskId" :is-reading-task-view-in-order="isReadingTaskViewInOrder" :isReading="true" />
<download-dicom-and-nonedicom v-if="downloadImageVisible" :subject-id="uploadSubjectId"
:subject-code="uploadSubjectCode" :criterion="uploadTrialCriterion" :task-id="taskId"
:visible.sync="downloadImageVisible" />
:visible.sync="downloadImageVisible" :is-reading-task-view-in-order="isReadingTaskViewInOrder"
:isReading="true" />
<readingChart ref="readingChart" />
</div>
</template>
@@ -1221,12 +1222,7 @@ export default {
this.selectArr.push(item.VisitTaskId)
}
if (item.IsCurrentTask) {
this.markedSeriesIds = []
annotations.map(i => {
if (i.MeasureData && i.MeasureData.seriesId) {
this.markedSeriesIds.push(i.MeasureData.seriesId)
}
})
this.syncMarkedSeriesIds(annotations)
}
}
@@ -2052,7 +2048,7 @@ export default {
// this.$refs['ecrf'].setAnnotation({ annotation, toolName: annotation.metadata.toolName })
this.$refs[`ecrf_${this.lastViewportTaskId}`][0].setAnnotation({ annotation, toolName: annotation.metadata.toolName })
this.markedSeriesIds.push(series.Id)
this.syncMarkedSeriesIdsFromState(series.TaskInfo.VisitTaskId)
}
this.setToolsPassive()
@@ -2087,16 +2083,14 @@ export default {
throw errorMsg
}
if (annotation.visitTaskId === this.taskInfo.VisitTaskId && annotation.seriesId) {
const index = this.markedSeriesIds.indexOf(annotation.seriesId)
if (index !== -1) {
this.markedSeriesIds.splice(index, 1)
}
this.syncMarkedSeriesIdsFromState(annotation.visitTaskId)
} else {
const errorMsg = { message: 'annotation Not allowed to operate' }
throw errorMsg
}
} catch (e) {
cornerstoneTools.annotation.state.addAnnotation(annotation)
this.syncMarkedSeriesIdsFromState(annotation.visitTaskId)
const renderingEngine = getRenderingEngine(renderingEngineId)
for (let i = 0; i < this.cells.length; i++) {
const viewportId = `${this.viewportKey}-${i}`
@@ -2133,7 +2127,7 @@ export default {
annotation.sliceThickness = series.SliceThickness
annotation.numberOfFrames = isNaN(parseInt(params.frame)) ? null : parseInt(params.frame)
annotation.markTool = annotation.metadata.toolName
this.markedSeriesIds.push(series.Id)
this.syncMarkedSeriesIdsFromState(series.TaskInfo.VisitTaskId)
const operateStateEnum = this.$refs[`ecrf_${this.taskInfo.VisitTaskId}`][0].operateStateEnum
const markName = await this.customPrompt(!this.isNumber(operateStateEnum))
@@ -2275,10 +2269,7 @@ export default {
if (!res.IsSuccess) throw ''
}
}
const index = this.markedSeriesIds.indexOf(annotation.seriesId)
if (index !== -1) {
this.markedSeriesIds.splice(index, 1)
}
this.syncMarkedSeriesIdsFromState(annotation.visitTaskId)
const renderingEngine = getRenderingEngine(renderingEngineId)
for (let i = 0; i < this.cells.length; i++) {
const viewportId = `${this.viewportKey}-${i}`
@@ -2291,6 +2282,7 @@ export default {
}
} catch (e) {
cornerstoneTools.annotation.state.addAnnotation(annotation)
this.syncMarkedSeriesIdsFromState(annotation.visitTaskId)
const renderingEngine = getRenderingEngine(renderingEngineId)
for (let i = 0; i < this.cells.length; i++) {
const viewportId = `${this.viewportKey}-${i}`
@@ -2367,6 +2359,22 @@ export default {
this.$set(this.visitTaskList[taskIdx], 'Annotations', annotations)
this.$set(this.visitTaskList[taskIdx], 'AnnotationUIDs', annotationUIDs)
},
collectMarkedSeriesIds(list = []) {
return [...new Set(
list
.map(item => item?.MeasureData?.seriesId || item?.seriesId)
.filter(Boolean)
)]
},
syncMarkedSeriesIds(list = []) {
this.markedSeriesIds = this.collectMarkedSeriesIds(list)
},
syncMarkedSeriesIdsFromState(visitTaskId = this.taskInfo?.VisitTaskId) {
const annotations = cornerstoneTools.annotation.state.getAllAnnotations().filter(item => {
return item.visitTaskId === visitTaskId && item.seriesId
})
this.syncMarkedSeriesIds(annotations)
},
async resetAnnotations(visitTaskId) {
if (this.readingTaskState === 2) return
const taskIdx = this.visitTaskList.findIndex(i => i.VisitTaskId === visitTaskId)
@@ -2401,9 +2409,9 @@ export default {
if (i.MeasureData) {
const annotation = i.MeasureData
cornerstoneTools.annotation.state.addAnnotation(annotation)
this.markedSeriesIds.push(annotation.seriesId)
}
})
this.syncMarkedSeriesIds(annotations)
const renderingEngine = getRenderingEngine(renderingEngineId)
for (let i = 0; i < this.cells.length; i++) {
const viewportId = `${this.viewportKey}-${i}`
@@ -3415,6 +3423,15 @@ export default {
this.clipPlaying = !this.clipPlaying
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].toggleClipPlay(isPlay, this.fps)
},
handleClipFpsChange() {
if (!this.clipPlaying) return
const activeViewportRef = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`]
const activeViewport = activeViewportRef && activeViewportRef[0]
if (!activeViewport || typeof activeViewport.toggleClipPlay !== 'function') return
activeViewport.toggleClipPlay(true, this.fps)
},
//
async getWwcTpl() {
try {
@@ -4037,8 +4054,10 @@ export default {
}
}
}
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setSeriesInfo(series, true)
this.$refs[series.TaskInfo.VisitTaskId][0].setSeriesActive(series.StudyIndex, series.SeriesIndex)
if (!this.isActiveViewportAtMeasureLocation(obj.annotation)) {
this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`][0].setSeriesInfo(series, true)
this.$refs[series.TaskInfo.VisitTaskId][0].setSeriesActive(series.StudyIndex, series.SeriesIndex)
}
setTimeout(async () => {
const divForDownloadViewport = document.querySelector(
`div[data-viewport-uid="${this.viewportKey}-${this.activeViewportIndex}"]`
@@ -4049,9 +4068,48 @@ export default {
}, 200)
}
},
getMeasureImageLocation(measureData = {}) {
const referencedImageId = measureData?.metadata?.referencedImageId
const referencedImageParams = referencedImageId ? this.getInstanceInfo(referencedImageId) : {}
return {
visitTaskId: measureData.visitTaskId,
studyId: measureData.studyId || measureData.StudyId,
seriesId: measureData.seriesId || measureData.SeriesId,
instanceId: measureData.instanceId || measureData.InstanceId || referencedImageParams.instanceId || null,
frame: measureData.numberOfFrames ?? measureData.NumberOfFrames ?? referencedImageParams.frame
}
},
getActiveViewportImageLocation() {
const viewportRef = this.$refs[`${this.viewportKey}-${this.activeViewportIndex}`]?.[0]
const currentSeries = viewportRef?.series
if (!currentSeries) return null
const renderingEngine = getRenderingEngine(renderingEngineId)
const viewport = renderingEngine?.getViewport(`${this.viewportKey}-${this.activeViewportIndex}`)
const currentImageId = viewport?.getCurrentImageId?.()
const currentImageParams = currentImageId ? this.getInstanceInfo(currentImageId) : {}
return {
visitTaskId: currentSeries.TaskInfo?.VisitTaskId,
studyId: currentSeries.StudyId,
seriesId: currentSeries.Id,
instanceId: currentImageParams.instanceId || null,
frame: currentImageParams.frame
}
},
isActiveViewportAtMeasureLocation(measureData) {
const targetLocation = this.getMeasureImageLocation(measureData)
const currentLocation = this.getActiveViewportImageLocation()
if (!currentLocation || !targetLocation.seriesId) return false
if (targetLocation.visitTaskId && currentLocation.visitTaskId !== targetLocation.visitTaskId) return false
if (targetLocation.studyId && currentLocation.studyId !== targetLocation.studyId) return false
if (currentLocation.seriesId !== targetLocation.seriesId) return false
if (targetLocation.instanceId && currentLocation.instanceId !== targetLocation.instanceId) return false
return currentLocation.frame === targetLocation.frame
},
async getScreenshots(measureData, callback) {
if (measureData) {
await this.imageLocation(measureData)
if (!this.isActiveViewportAtMeasureLocation(measureData.annotation)) {
await this.imageLocation(measureData)
}
const divForDownloadViewport = document.querySelector(
`div[data-viewport-uid="${this.viewportKey}-${this.activeViewportIndex}"]`
)
@@ -4,7 +4,8 @@
<el-form-item :label="$t('segment:form:label:studyName')" prop="taskBlindName">
<el-select v-model="form.studyId" clearable @change="(e) => handleChange(e, 'study')"
@clear="(e) => handleClear(e, 'study')">
<el-option v-for="item in studyList" :key="item.StudyId" :label="item.StudyCode"
<el-option v-for="item in studyList" :key="item.StudyId"
:label="taskInfo.IsReadingTaskViewInOrder !== 0 ? item.StudyCode : `${item.Modalities}(${item.SeriesCount})`"
:value="item.StudyId" />
</el-select>
</el-form-item>
@@ -87,12 +88,13 @@ export default {
}, trigger: ['blur', 'change']
}
]
}
},
taskInfo: {}
}
},
// mounted() {
// this.init()
// },
mounted() {
this.taskInfo = JSON.parse(sessionStorage.getItem('taskInfo'))
},
methods: {
setSeries(series) {
this.series = series
@@ -148,13 +150,19 @@ export default {
this.handleClear(e, key)
if (key === 'study') {
this.seriesList = this.studyList.find(item => item.StudyId === this.form.studyId).SeriesArr
this.form.seriesId = null
this.form.segmentGroupId = null
this.form.segmentId = null
}
if (key === 'series') {
this.segmentGroupList = this.segmentionList.filter(item => item.SeriesId === this.form.seriesId)
this.form.segmentGroupId = null
this.form.segmentId = null
}
if (key === 'segmentGroup') {
let list = await this.getSegmentList(this.form.segmentGroupId)
this.segmentList = list.filter(item => item.SegmentJson)
this.form.segmentId = null
}
},
handleClear(e, key) {
@@ -40,9 +40,9 @@
@click.prevent="setToolActive('CircularEraser')">
<svg-icon icon-class="clear" class="svg-icon" />
</div>
<div :class="['tool-item']">
<!-- <div :class="['tool-item']">
<input type="file" @change="beginScanFiles($event)">
</div>
</div> -->
</div>
<div class="ConfigBox">
<div class="EraserConfig"
@@ -133,8 +133,8 @@
</div>
<div class="SegmentConfig">
<span>{{ $t('trials:reading:Segmentations:title:Border') }}</span>
<el-slider v-model="SegmentConfig.outlineWidth" show-input :step="1" :max="10"
input-size="mini" :show-input-controls="false" />
<el-slider v-model="SegmentConfig.outlineWidth" show-input :step="1" :max="10" input-size="mini"
:show-input-controls="false" />
</div>
<span class="line" />
<div class="SegmentConfig" style="justify-content: flex-start;">
@@ -143,7 +143,7 @@
</el-switch>
<span style="margin-left: 5px;">{{
$t('trials:reading:Segmentations:title:InactiveSegmentationsShow')
}}</span>
}}</span>
</div>
</div>
<template v-if="segmentList.length > 0">
@@ -326,7 +326,7 @@
<template slot-scope="scope">
<el-button type="text" @click.stop="restoreSegmentationVersion(scope.row)">{{
$t('trials:reading:Segmentations:button:recovery')
}}</el-button>
}}</el-button>
</template>
</el-table-column>
</el-table>
@@ -602,6 +602,7 @@ export default {
brushSize: {
handler() {
this.setBrushSize(this.activeTool)
this.setSegmentConfig()
},
deep: true
},
@@ -803,12 +804,16 @@ export default {
toolGroup.setToolPassive(this.activeTool)
}
this.$emit('update:activeTool', '')
} else if (this.activeTool === name) {
toolGroup.setToolPassive(this.ThresholdTools[1])
this.$emit('update:activeTool', '')
} else {
if (this.activeTool) {
if (toolName === CrosshairsTool.toolName) {
toolGroup.setToolDisabled(this.activeTool)
} else {
toolGroup.setToolPassive(this.activeTool)
toolGroup.setToolPassive(this.ThresholdTools[1])
}
}
toolGroup.setToolActive(toolName, {
@@ -4,7 +4,10 @@
<div v-if="taskInfo && taskInfo.IsReadingShowSubjectInfo" :title="taskInfo.SubjectCode">
{{ taskInfo.SubjectCode }}
</div>
<div v-if="taskInfo && taskInfo.IsReadingShowSubjectInfo" :title="visitTaskInfo.TaskBlindName">
<div :title="visitTaskInfo.TaskBlindName">
<span v-if="taskInfo && !taskInfo.IsReadingShowSubjectInfo">
{{ $t('trials:reading:title:taskName') }}
</span>
{{ visitTaskInfo.TaskBlindName }}
</div>
</div>
@@ -21,7 +24,7 @@
</div>
<div>
<span class="study-meta-line" :title="study.Modalities">
<span class="study-code" :title="study.StudyCode">{{ study.StudyCode }}</span>
<span v-if="taskInfo && taskInfo.IsReadingTaskViewInOrder !== 0" class="study-code" :title="study.StudyCode">{{ study.StudyCode }}</span>
<span class="study-modality">{{ `${study.Modalities}(${study.SeriesCount})` }}</span>
<span class="patient-info" v-if="['PT、CT', 'CT、PT', 'PET-CT'].includes(study.Modalities)">
<el-popover placement="right-start" trigger="hover" popper-class="patient-info-popper">
@@ -329,7 +332,13 @@ export default {
margin: 0;
text-align: center;
background-color: #4c4c4c;
height: 50px;
min-height: 50px;
height: auto;
flex-shrink: 0;
display: flex;
flex-direction: column;
justify-content: center;
word-break: break-word;
}
.dicom-desc {
@@ -7,8 +7,8 @@
<svg-icon style="cursor: pointer;" icon-class="documentation" class="svg-icon"
@click.stop="viewCD(series.TaskInfo.VisitTaskId)" />
</div>
<h2 v-if="taskInfo.IsReadingShowSubjectInfo" class="subject-info">
{{ `${series.TaskInfo.SubjectCode} ${series.TaskInfo.TaskBlindName} ` }}
<h2 class="subject-info">
{{ taskInfo.IsReadingShowSubjectInfo ? series.TaskInfo.SubjectCode + ' ' + series.TaskInfo.TaskBlindName : series.TaskInfo.TaskBlindName }}
</h2>
<div>Series: #{{ series.SeriesNumber }}</div>
<div v-if="series.Stack">Image: #{{ `${series.SliceIndex + 1}/${series.Stack.length}` }}</div>
@@ -150,6 +150,8 @@ export default {
originalMarkers: [],
markers: { top: '', right: '', bottom: '', left: '' },
playClipState: false,
clipFramesPerSecond: null,
toggleClipPlayTimer: null,
wwwcIdx: 2,
loading: false,
forceFitToWindow: false,
@@ -169,6 +171,10 @@ export default {
this.resizeObserver.unobserve(this.element)
this.resizeObserver.disconnect()
}
if (this.toggleClipPlayTimer) {
clearInterval(this.toggleClipPlayTimer)
this.toggleClipPlayTimer = null
}
},
methods: {
initViewport() {
@@ -210,12 +216,11 @@ export default {
this.imageInfo.imageOrientationPatient = imagePlaneModule.imageOrientationPatient
this.imageInfo.imagePositionPatient = imagePlaneModule.imagePositionPatient
this.imageInfo.location = imagePlaneModule.sliceLocation
// this.imageInfo.wwwc = `${Math.round(detail.image.windowWidth)}/${Math.round(detail.image.windowCenter)}`
this.getOrientationMarker()
this.$emit('renderAnnotations', this.series)
const toolGroupId = this.viewportId
const toolGroup = cornerstoneTools.ToolGroupManager.getToolGroup(toolGroupId)
toolGroup.setToolEnabled('ScaleOverlay')
const toolGroup = cornerstoneTools.ToolGroupManager.getToolGroup(this.viewportId)
if (toolGroup) {
toolGroup.setToolEnabled('ScaleOverlay')
}
},
imageRendered(e) {
const renderingEngine = getRenderingEngine(this.renderingEngineId)
@@ -312,14 +317,61 @@ export default {
}
},
toggleClipPlay(isPlay, framesPerSecond) {
this.playClipState = isPlay
const renderingEngine = getRenderingEngine(this.renderingEngineId)
if (!renderingEngine) return
const viewport = renderingEngine.getViewport(this.viewportId)
if (!viewport?.element) return
const parsedFramesPerSecond = Number(framesPerSecond)
const nextFramesPerSecond = Number.isFinite(parsedFramesPerSecond)
? parsedFramesPerSecond
: (this.clipFramesPerSecond || 15)
const isSameSpeedWhilePlaying = this.playClipState &&
isPlay &&
this.clipFramesPerSecond === nextFramesPerSecond
if (isPlay) {
cornerstoneTools.utilities.cine.playClip(viewport.element, { framesPerSecond, loop: true })
if (isSameSpeedWhilePlaying) return
cornerstoneTools.utilities.cine.stopClip(viewport.element)
if (this.toggleClipPlayTimer) {
clearInterval(this.toggleClipPlayTimer)
this.toggleClipPlayTimer = null
}
const frameInterval = Math.max(16, Math.round(1000 / nextFramesPerSecond))
this.toggleClipPlayTimer = setInterval(() => {
const imageIds = viewport.getImageIds()
if (!imageIds?.length) return
let index = viewport.getCurrentImageIdIndex() + 1
if (index > imageIds.length - 1) {
index = 0
}
csUtils.jumpToSlice(viewport.element, {
imageIndex: index,
debounceLoading: false
})
}, frameInterval)
this.clipFramesPerSecond = nextFramesPerSecond
this.playClipState = true
} else {
cornerstoneTools.utilities.cine.stopClip(viewport.element)
if (this.toggleClipPlayTimer) {
clearInterval(this.toggleClipPlayTimer)
this.toggleClipPlayTimer = null
}
this.clipFramesPerSecond = null
this.playClipState = false
}
},
syncViewportStaticState() {
this.getOrientationMarker()
this.$emit('renderAnnotations', this.series)
const toolGroup = cornerstoneTools.ToolGroupManager.getToolGroup(this.viewportId)
if (toolGroup) {
toolGroup.setToolEnabled('ScaleOverlay')
}
},
scrollPage(type) {
@@ -339,7 +391,13 @@ export default {
}
// viewport.setImageIdIndex(newImageIdIndex)
csUtils.jumpToSlice(viewport.element, { imageIndex: newImageIdIndex })
if (this.toggleClipPlayTimer) {
clearInterval(this.toggleClipPlayTimer)
this.toggleClipPlayTimer = null
}
cornerstoneTools.utilities.cine.stopClip(viewport.element)
this.clipFramesPerSecond = null
this.playClipState = false
},
setZoom(ratio) {
const renderingEngine = getRenderingEngine(this.renderingEngineId)
@@ -432,6 +490,7 @@ export default {
}
this.prefetchMetadataInformation(obj.ImageIds, obj.Modality)
await viewport.setStack(this.series.Stack, obj.SliceIndex)
this.syncViewportStaticState()
viewport.render()
} catch (e) {
@@ -1,10 +1,12 @@
<template>
<div class="criterion-form-item">
<div v-if="!!question.GroupName && question.Type === 'group'"
<div
v-if="(!!question.GroupName && question.Type === 'group') && ((question.ShowQuestion === 1 && question.ParentTriggerValueList.includes(String(questionForm[question.ParentId]))) || question.ShowQuestion === 0)"
style="font-weight: bold;font-size: 16px;margin: 5px 0px;color:#fff;">
{{ question.GroupName }}
</div>
<div v-if="question.Type === 'table' || question.Type === 'basicTable'"
<div
v-if="(question.Type === 'table' || question.Type === 'basicTable') && ((question.ShowQuestion === 1 && question.ParentTriggerValueList.includes(String(questionForm[question.ParentId]))) || question.ShowQuestion === 0)"
style="font-weight: bold;font-size: 14px;margin: 5px 0px;">
<div style="display: flex;justify-content: space-between;align-items: center;color:#fff;margin: 10px 0 5px">
<span :title="question.Remark">{{ question.QuestionName }}</span>
@@ -14,7 +16,8 @@
{{ $t('common:button:add') }}
</el-button>
</div>
<el-table :data="questionForm[question.Id]">
<el-table :data="questionForm[question.Id]"
v-if="(question.ShowQuestion === 1 && question.ParentTriggerValueList.includes(String(questionForm[question.ParentId]))) || question.ShowQuestion === 0">
<el-table-column :label="$t('CustomizeQuestionFormItem:label:OrderMark')" width="60px" show-overflow-tooltip>
<template slot-scope="scope">
{{ question.OrderMark }}{{ scope.$index + 1 }}
@@ -43,7 +46,7 @@
<el-button type="text" size="mini" @click="openAddTableCol(question, scope.$index)">
{{ question.IsPreinstall ?
$t('CustomizeQuestionFormItem:button:assessment') : readingTaskState >= 2 ? $t('common:button:view') :
$t('common:button:edit') }}
$t('common:button:edit') }}
</el-button>
<el-button type="text" size="mini" :disabled="addOrEdit.visible"
v-if="readingTaskState < 2 && (scope.row.IsCurrentTaskAdd === 'True' || !question.IsCopyLesions || isBaseline) && !question.IsPreinstall && (question.AddDeleteTypeEnum === 0 || (isBaseline && question.AddDeleteTypeEnum === 1) || (!isBaseline && question.AddDeleteTypeEnum === 2))"
@@ -2,9 +2,16 @@
<div v-loading="loading" class="questionList-wrapper">
<div class="container">
<div class="basic-info">
<h3 v-if="taskInfo && taskInfo.IsReadingShowSubjectInfo">
<span v-if="visitInfo.SubjectCode">{{ visitInfo.SubjectCode }} </span>
<span style="margin-left:5px;">{{ visitInfo.TaskBlindName }}</span>
<h3>
<span v-if="taskInfo && taskInfo.IsReadingShowSubjectInfo && visitInfo.SubjectCode" style="margin-right:5px;">
{{ visitInfo.SubjectCode }}
</span>
<span>
<span v-if="taskInfo && !taskInfo.IsReadingShowSubjectInfo">
{{ $t('trials:reading:title:taskName') }}
</span>
{{ visitInfo.TaskBlindName }}
</span>
</h3>
<div v-if="readingTaskState < 2">
<el-tooltip class="item" effect="dark" :content="$t('trials:dicomReading:message:confirmReset')"
@@ -35,7 +35,9 @@
scope.row.QuestionName }}
<svg-icon
v-if="scope.row.ShowChartTypeEnum > 0 || (scope.row.LesionType === 0 && scope.row.ReportLayType === 1)"
icon-class="readingChart" class="svg-icon svg-readingChart-mini" @click.stop="(e) => handleReadingChart({
icon-class="readingChart"
class="svg-icon svg-readingChart-mini"
@click.stop="(e) => handleReadingChart({
e,
data: {
QuestionId: scope.row.RowIndex ? null : scope.row.QuestionId,
@@ -49,8 +51,13 @@
<span v-else style="font-weight: bold;font-size: 16px;color: #f44336;">{{ scope.row.GroupName }}</span>
</template>
</el-table-column>
<el-table-column v-for="task in visitTaskList" :key="task.VisitTaskId" prop="date" show-overflow-tooltip
width="200px">
<el-table-column
v-for="task in visitTaskList"
:key="task.VisitTaskId"
prop="date"
show-overflow-tooltip
width="200px"
>
<template slot="header">
<div v-if="task.IsCurrentTask">{{ task.BlindName }}</div>
<div v-else>
@@ -64,15 +71,18 @@
</template>
<template slot-scope="scope">
<template
v-if="readingTaskState < 2 && task.VisitTaskId === visitTaskId && (scope.row.Type === 'input' || scope.row.Type === 'number' || scope.row.Type === 'select' || scope.row.Type === 'textarea' || scope.row.Type === 'radio')">
v-if="readingTaskState < 2 && task.VisitTaskId === visitTaskId && (scope.row.Type === 'input' || scope.row.Type === 'number' || scope.row.Type === 'select' || scope.row.Type === 'textarea' || scope.row.Type === 'radio')"
>
<!-- 输入框 -->
<div>
<template
v-if="!((task.IsBaseLine && scope.row.LimitEdit === 1) || (!task.IsBaseLine && scope.row.LimitEdit === 2) || scope.row.LimitEdit === 0)" />
v-if="!((task.IsBaseLine && scope.row.LimitEdit === 1) || (!task.IsBaseLine && scope.row.LimitEdit === 2) || scope.row.LimitEdit === 0)"
/>
<el-input
v-else-if="questionForm[scope.row.QuestionId] instanceof Array && (scope.row.Type === 'input' || scope.row.Type === 'textarea') && !scope.row.IsShowInDicom && ((task.IsBaseLine && scope.row.LimitEdit === 1) || (!task.IsBaseLine && scope.row.LimitEdit === 2) || scope.row.LimitEdit === 0)"
v-model="questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId]"
size="mini" />
size="mini"
/>
<span
v-else-if="questionForm[scope.row.QuestionId] instanceof Array && (scope.row.Type === 'input' || scope.row.Type === 'textarea')">{{
questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId] }}</span>
@@ -84,13 +94,22 @@
<el-select
v-else-if="questionForm[scope.row.QuestionId] instanceof Array && (scope.row.Type === 'select' || scope.row.Type === 'radio') && !scope.row.IsShowInDicom && ((task.IsBaseLine && scope.row.LimitEdit === 1) || (!task.IsBaseLine && scope.row.LimitEdit === 2) || scope.row.LimitEdit === 0)"
v-model="questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId]"
size="mini" clearable :multiple="scope.row.OptionTypeEnum === 1">
size="mini"
clearable
:multiple="scope.row.OptionTypeEnum === 1"
>
<template>
<el-option v-for="val in scope.row.TypeValue.split('|')" :key="val" :label="val" :value="val" />
<el-option
v-for="val in scope.row.TypeValue.split('|')"
:key="val"
:label="val"
:value="val"
/>
</template>
</el-select>
<span
v-else-if="questionForm[scope.row.QuestionId] instanceof Array && questionForm[scope.row.QuestionId][scope.row.xfIndex] && (scope.row.Type === 'select' || scope.row.Type === 'radio')">
v-else-if="questionForm[scope.row.QuestionId] instanceof Array && questionForm[scope.row.QuestionId][scope.row.xfIndex] && (scope.row.Type === 'select' || scope.row.Type === 'radio')"
>
{{
Array.isArray(questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId])
?
@@ -100,23 +119,32 @@
</span>
<el-select
v-else-if="(scope.row.Type === 'select' || scope.row.Type === 'radio') && !scope.row.IsShowInDicom && ((task.IsBaseLine && scope.row.LimitEdit === 1) || (!task.IsBaseLine && scope.row.LimitEdit === 2) || scope.row.LimitEdit === 0)"
v-model="questionForm[scope.row.QuestionId]" size="mini" clearable
:multiple="scope.row.OptionTypeEnum === 1">
v-model="questionForm[scope.row.QuestionId]"
size="mini"
clearable
:multiple="scope.row.OptionTypeEnum === 1"
>
<template>
<el-option v-for="val in scope.row.TypeValue.split('|')" :key="val" :label="val" :value="val" />
<el-option
v-for="val in scope.row.TypeValue.split('|')"
:key="val"
:label="val"
:value="val"
/>
</template>
</el-select>
<span v-else-if="scope.row.Type === 'select' || scope.row.Type === 'radio'">
{{
questionForm[scope.row.QuestionId] && Array.isArray(questionForm[scope.row.QuestionId]) ?
questionForm[scope.row.QuestionId].join(',') :
questionForm[scope.row.QuestionId]
questionForm[scope.row.QuestionId] && Array.isArray(questionForm[scope.row.QuestionId]) ?
questionForm[scope.row.QuestionId].join(',') :
questionForm[scope.row.QuestionId]
}}
</span>
<el-input
v-else-if="scope.row.DataSource !== 1 && questionForm[scope.row.QuestionId] instanceof Array && scope.row.Type === 'number' && (scope.row.xfIndex || scope.row.xfIndex === 0) && !scope.row.IsShowInDicom && ((task.IsBaseLine && scope.row.LimitEdit === 1) || (!task.IsBaseLine && scope.row.LimitEdit === 2) || scope.row.LimitEdit === 0)"
v-model="questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId]"
:disabled="scope.row.DataSource === 1" size="mini"
:disabled="scope.row.DataSource === 1"
size="mini"
@blur="limitBlur(questionForm[scope.row.QuestionId][scope.row.xfIndex], scope.row.TableQuestionId, scope.row.ValueType)"
@focus="() => { questionId = scope.row.QuestionId }">
<template v-if="scope.row.Unit !== 0" slot="append">
@@ -126,35 +154,40 @@
<template v-else-if="scope.row.ValueType === 2" slot="append">%</template>
</el-input>
<span
v-else-if="questionForm[scope.row.QuestionId] instanceof Array && scope.row.Type === 'number' && (scope.row.xfIndex || scope.row.xfIndex === 0)">
<template v-if="(scope.row.ValueType === 0 || scope.row.ValueType === 1) && scope.row.Unit">
v-else-if="questionForm[scope.row.QuestionId] instanceof Array && scope.row.Type === 'number' && (scope.row.xfIndex || scope.row.xfIndex === 0)"
>
<template
v-if="(scope.row.ValueType === 0 || scope.row.ValueType === 1) && scope.row.Unit"
>
{{
isNaN(parseInt(questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId]))
?
questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId] :
`${questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId]}
isNaN(parseInt(questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId]))
?
questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId] :
`${questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId]}
${scope.row.Unit !== 4 ? $fd('ValueUnit', scope.row.Unit) : scope.row.CustomUnit}` }}
</template>
<template v-else-if="scope.row.ValueType === 2">
{{
isNaN(parseInt(questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId]))
?
questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId] :
`${questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId]}
isNaN(parseInt(questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId]))
?
questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId] :
`${questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId]}
%` }}
</template>
<template v-else>
{{
isNaN(parseInt(questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId]))
?
questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId] :
`${questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId]}`
isNaN(parseInt(questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId]))
?
questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId] :
`${questionForm[scope.row.QuestionId][scope.row.xfIndex][scope.row.TableQuestionId]}`
}}
</template>
</span>
<el-input
v-else-if="scope.row.DataSource !== 1 && scope.row.Type === 'number' && !scope.row.IsShowInDicom && ((task.IsBaseLine && scope.row.LimitEdit === 1) || (!task.IsBaseLine && scope.row.LimitEdit === 2) || scope.row.LimitEdit === 0)"
v-model="questionForm[scope.row.QuestionId]" :disabled="scope.row.DataSource === 1" size="mini"
v-model="questionForm[scope.row.QuestionId]"
:disabled="scope.row.DataSource === 1"
size="mini"
@blur="limitBlur(questionForm, scope.row.QuestionId, scope.row.ValueType)"
@focus="() => { questionId = scope.row.QuestionId }">
<template v-if="scope.row.Unit !== 0" slot="append">
@@ -164,19 +197,21 @@
<template v-else-if="scope.row.ValueType === 2" slot="append">%</template>
</el-input>
<span v-else-if="scope.row.Type === 'number'">
<template v-if="(scope.row.ValueType === 0 || scope.row.ValueType === 1) && scope.row.Unit">
<template
v-if="(scope.row.ValueType === 0 || scope.row.ValueType === 1) && scope.row.Unit"
>
{{ isNaN(parseInt(questionForm[scope.row.QuestionId])) ?
questionForm[scope.row.QuestionId] : `${questionForm[scope.row.QuestionId]} ${scope.row.Unit !== 4
?
$fd('ValueUnit', scope.row.Unit) : scope.row.CustomUnit}` }}
questionForm[scope.row.QuestionId] : `${questionForm[scope.row.QuestionId]} ${scope.row.Unit !== 4
?
$fd('ValueUnit', scope.row.Unit) : scope.row.CustomUnit}` }}
</template>
<template v-else-if="scope.row.ValueType === 2">
{{ isNaN(parseInt(questionForm[scope.row.QuestionId])) ?
questionForm[scope.row.QuestionId] : `${questionForm[scope.row.QuestionId]} %` }}
questionForm[scope.row.QuestionId] : `${questionForm[scope.row.QuestionId]} %` }}
</template>
<template v-else>
{{ isNaN(parseInt(questionForm[scope.row.QuestionId])) ? questionForm[scope.row.QuestionId] :
questionForm[scope.row.QuestionId] }}
questionForm[scope.row.QuestionId] }}
</template>
</span>
</div>
@@ -190,22 +225,25 @@
</template>
<template v-else-if="scope.row.DictionaryCode">
{{scope.row.Answers[task.VisitTaskId] instanceof Array
? scope.row.Answers[task.VisitTaskId].map(item => $fd(scope.row.DictionaryCode,
item)).join(",") : $fd(scope.row.DictionaryCode,
scope.row.Answers[task.VisitTaskId])}}
? scope.row.Answers[task.VisitTaskId].map(item => $fd(scope.row.DictionaryCode,
item)).join(",") : $fd(scope.row.DictionaryCode,
scope.row.Answers[task.VisitTaskId])}}
</template>
<template v-else-if="criterionType === 10">
{{
isNaN(parseInt(scope.row.Answers[task.VisitTaskId])) ? scope.row.Answers[task.VisitTaskId] :
`${scope.row.Answers[task.VisitTaskId]}`
isNaN(parseInt(scope.row.Answers[task.VisitTaskId])) ? scope.row.Answers[task.VisitTaskId] :
`${scope.row.Answers[task.VisitTaskId]}`
}}
</template>
<template
v-else-if="!((task.IsBaseLine && scope.row.LimitEdit === 1) || (!task.IsBaseLine && scope.row.LimitEdit === 2) || scope.row.LimitEdit === 0)" />
<template v-else-if="(scope.row.ValueType === 0 || scope.row.ValueType === 1) && scope.row.Unit">
v-else-if="!((task.IsBaseLine && scope.row.LimitEdit === 1) || (!task.IsBaseLine && scope.row.LimitEdit === 2) || scope.row.LimitEdit === 0)"
/>
<template
v-else-if="(scope.row.ValueType === 0 || scope.row.ValueType === 1) && scope.row.Unit"
>
{{
isNaN(parseInt(scope.row.Answers[task.VisitTaskId])) ? scope.row.Answers[task.VisitTaskId] :
`${scope.row.Answers[task.VisitTaskId]}
isNaN(parseInt(scope.row.Answers[task.VisitTaskId])) ? scope.row.Answers[task.VisitTaskId] :
`${scope.row.Answers[task.VisitTaskId]}
${scope.row.Unit !== 4 ? $fd('ValueUnit', scope.row.Unit) : scope.row.CustomUnit}` }}
</template>
<template v-else-if="scope.row.ValueType === 2">
@@ -221,17 +259,26 @@
</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>
<span
style="font-size:12px;margin-left:5px"
>{{ `(${$t('common:label:sign')}${currentUser})` }}</span>
</div>
<SignForm ref="signForm" :sign-code-enum="signCode" @closeDialog="closeSignDialog" />
</el-dialog>
<!-- 预览文件 -->
<viewer ref="picture_perview_customizeReportPage" style="margin: 0 10px"
<viewer
ref="picture_perview_customizeReportPage"
style="margin: 0 10px"
v-if="currentType && ['png', 'jpg', 'jpeg'].includes(currentType.toLowerCase())"
:images="[`${OSSclientConfig.basePath}${currentPath}`]" :options="viewerOptions">
<img v-show="false" :src="`${OSSclientConfig.basePath}${currentPath}`" alt="Image" />
@@ -693,8 +740,8 @@ export default {
dataArr.length === 0
? 0
: dataArr.reduce((acc, curr) => {
return acc + (typeof curr === 'number' ? curr : 0)
}, 0) / dataArr.length
return acc + (typeof curr === 'number' ? curr : 0)
}, 0) / dataArr.length
break
case 11:
if (!isNaN(parseFloat(this.questionForm[o.TableQuestionId]))) {
@@ -722,8 +769,8 @@ export default {
dataArr.length === 0
? 0
: dataArr.reduce((acc, curr) => acc && curr)
? 1
: 0
? 1
: 0
break
case 14:
if (!isNaN(parseFloat(this.questionForm[o.TableQuestionId]))) {
@@ -735,8 +782,8 @@ export default {
dataArr.length === 0
? 0
: dataArr.reduce((acc, curr) => acc || curr, 0)
? 1
: 0
? 1
: 0
break
case 15:
if (!isNaN(parseFloat(this.questionForm[o.TableQuestionId]))) {
@@ -761,8 +808,8 @@ export default {
return isNaN(num)
? ''
: isFinite(num)
? num.toFixed(this.digitPlaces)
: '∞'
? num.toFixed(this.digitPlaces)
: '∞'
},
getReportInfo() {
this.loading = true
@@ -972,8 +1019,8 @@ export default {
)}</span><a style="color:#409EFF"
href="/screen.pdf"
target="blank">${this.$t(
'trials:reading:tip:openFile'
)}</a></div>`
'trials:reading:tip:openFile'
)}</a></div>`
let confirm = await this.$confirm(html, '', {
dangerouslyUseHTMLString: true,
})
@@ -1143,13 +1190,13 @@ export default {
border-color: #444444;
}
::v-deep .el-table__body tr>td {
::v-deep .el-table__body tr > td {
background-color: #000 !important;
color: #fff;
border-color: #444444;
}
::v-deep .el-table__body tr:hover>td {
::v-deep .el-table__body tr:hover > td {
background-color: #858282 !important;
color: #fff;
border-color: #444444;
@@ -221,6 +221,7 @@ export default {
Object.keys(this.seriesData).forEach(key => {
if (key !== 'default' || this.isNeedDefault) seriesData.push(this.seriesData[key])
})
console.log(seriesData, 'seriesData')
this.chart.clear();
const option = {
useUTC: true,
@@ -297,10 +298,10 @@ export default {
} else {
res = await this.getCurrentSliceValuesFromVoxelManager(this.renderingEngineId, `${this.viewportKey}-${this.activeViewportIndex}`)
}
if (res) {
let obj = {
let obj = {
}
}
if (res) {
res.values.forEach(item => {
if (obj[item]) {
obj[item]++
@@ -323,10 +324,24 @@ export default {
})
}
seriesData.sort((a, b) => a[0] - b[0])
let MIN = seriesData[0][0]
let MAX = seriesData[seriesData.length - 1][0]
seriesData = []
for (let i = MIN; i <= MAX; i++) {
if (obj[i]) {
let arr = [i, obj[i]]
seriesData.push(arr)
} else {
let arr = [i, 0]
seriesData.push(arr)
}
}
seriesData.sort((a, b) => a[0] - b[0])
return {
seriesData: {
type: 'line',
symbolSize: 0,
stack: 'Total',
data: seriesData
},
min: min,
@@ -1,6 +1,6 @@
<template>
<BaseContainer class="medical-feedback-ir">
<el-tabs v-model="TrialReadingCriterionId" type="border-card">
<el-tabs v-if="TrialReadingCriterionId" v-model="TrialReadingCriterionId" type="border-card">
<el-tab-pane v-for="i of trialCriterionList" :key="i.TrialReadingCriterionId" :label="i.TrialReadingCriterionName" :name="i.TrialReadingCriterionId">
<div v-if="TrialReadingCriterionId === i.TrialReadingCriterionId">
<div slot="search-container">
@@ -24,7 +24,7 @@
</el-select>
</el-form-item>
<!-- 受试者编号 -->
<el-form-item :label="$t('trials:medicalFeedback:table:subjectCode')">
<el-form-item v-if="otherInfo && otherInfo.IsReadingTaskViewInOrder !== 0" :label="$t('trials:medicalFeedback:table:subjectCode')">
<el-input
v-model="searchData.SubjectCode"
style="width:130px;"
@@ -32,7 +32,7 @@
/>
</el-form-item>
<!-- 盲态任务标识 -->
<!-- <el-form-item
<el-form-item
style="margin-bottom:10px"
:label="$t('trials:medicalFeedback:table:taskBlindName')"
>
@@ -41,7 +41,7 @@
style="width:100px;"
clearable
/>
</el-form-item> -->
</el-form-item>
<!-- 任务类型 -->
<el-form-item
style="margin-bottom:10px"
@@ -148,6 +148,7 @@
</div>
<div slot="main-container">
<el-table
:ref="TrialReadingCriterionId"
v-adaptive="{bottomOffset:75}"
v-loading="loading"
:data="list"
@@ -172,6 +173,7 @@
<!-- 受试者编号 -->
<el-table-column
v-if="otherInfo && otherInfo.IsReadingTaskViewInOrder !== 0"
prop="SubjectCode"
min-width="100"
:label="$t('trials:medicalFeedback:table:subjectCode')"
@@ -310,7 +312,6 @@
<el-table-column
:label="$t('common:action:action')"
min-width="100"
fixed="right"
>
<template slot-scope="scope">
<!-- 查看 -->
@@ -409,12 +410,14 @@ export default {
chatForm: { visible: false, title: '' }, //
auditVisible: false,
trialCriterionList: [],
TrialReadingCriterionId: '0'
TrialReadingCriterionId: '',
otherInfo: null
}
},
watch: {
TrialReadingCriterionId(v) {
if (v) {
this.searchData = searchDataDefault()
this.getList()
}
}
@@ -437,7 +440,11 @@ export default {
getIRMedicalFeedbackList(this.searchData).then(res => {
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
this.otherInfo = res.OtherInfo
this.loading = false
this.$nextTick(() => {
this.$refs[this.TrialReadingCriterionId][0].doLayout()
})
}).catch(() => { this.loading = false })
},
handleSearch() {
@@ -454,7 +461,11 @@ export default {
},
handleReply(row) {
this.currentRow = { ...row }
this.chatForm.title = `${this.$t('trials:medicalFeedback:title:qaRecord')} (${row.SubjectCode} ${row.TaskBlindName})`
if (this.otherInfo && this.otherInfo.IsReadingTaskViewInOrder !== 0) {
this.chatForm.title = `${this.$t('trials:medicalFeedback:title:qaRecord')} (${row.SubjectCode} ${row.TaskBlindName})`
} else {
this.chatForm.title = `${this.$t('trials:medicalFeedback:title:qaRecord')} (${row.TaskBlindName})`
}
this.chatForm.visible = true
},
async nextTask(taskMedicalReviewId) {
@@ -1,6 +1,6 @@
<template>
<BaseContainer v-loading="loading" class="read-task">
<el-tabs v-model="TrialReadingCriterionId" type="border-card">
<el-tabs v-if="TrialReadingCriterionId" v-model="TrialReadingCriterionId" type="border-card">
<el-tab-pane v-for="item of trialCriterionList" :key="item.TrialReadingCriterionId"
:label="item.TrialReadingCriterionName" :name="item.TrialReadingCriterionId">
<div v-if="TrialReadingCriterionId === item.TrialReadingCriterionId">
@@ -13,9 +13,13 @@
</el-select>
</el-form-item>
<!-- 受试者编号 -->
<el-form-item :label="$t('trials:readTask:table:subjectCode')">
<el-form-item v-if="otherInfo && otherInfo.IsReadingTaskViewInOrder !== 0" :label="$t('trials:readTask:table:subjectCode')">
<el-input v-model="searchData.SubjectCode" style="width:130px;" clearable />
</el-form-item>
<!-- 任务名称 -->
<el-form-item :label="$t('trials:readTask:table:blindName')">
<el-input v-model="searchData.TaskBlindName" style="width:130px;" clearable />
</el-form-item>
<!-- 任务状态 -->
<el-form-item style="margin-bottom:10px" :label="$t('trials:readTask:table:taskState')">
<el-select v-model="searchData.TaskState" clearable style="width:120px;">
@@ -50,7 +54,7 @@
</span>
</div>
<div slot="main-container">
<el-table v-adaptive="{ bottomOffset: 75 }" :data="list" stripe height="100"
<el-table :ref="TrialReadingCriterionId" v-adaptive="{ bottomOffset: 75 }" :data="list" stripe height="100"
@sort-change="handleSortChange">
<el-table-column type="index" width="40" align="left" />
<!-- 是否加急 -->
@@ -70,7 +74,7 @@
show-overflow-tooltip
/> -->
<!-- 受试者编号 -->
<el-table-column prop="SubjectCode" min-width="100" :label="$t('trials:readTask:table:subjectCode')"
<el-table-column v-if="otherInfo && otherInfo.IsReadingTaskViewInOrder !== 0" prop="SubjectCode" min-width="100" :label="$t('trials:readTask:table:subjectCode')"
sortable="custom" show-overflow-tooltip />
<!-- 盲态任务标识 -->
<el-table-column prop="TaskBlindName" min-width="100" :label="$t('trials:readTask:table:blindName')"
@@ -149,7 +153,7 @@
scope.row.ReReadingApplyState) }}</el-tag>
</template>
</el-table-column>
<el-table-column :label="$t('common:action:action')" width="250" fixed="right">
<el-table-column :label="$t('common:action:action')" width="200" >
<template slot-scope="scope">
<!-- 查看 -->
<el-button circle :title="$t('trials:readTask:button:view')" icon="el-icon-view"
@@ -177,7 +181,8 @@
custom-class="base-dialog-wrapper">
<el-form ref="reasonForm" :rules="rules" :model="ApplyforReasonForm" class="demo-ruleForm" size="small"
label-width="380px">
<p>{{ $t('trials:readTask:applyReread:title').replace('xxx', rowData.SubjectCode).replace('yyy', rowData.TaskBlindName) }}</p>
<p v-if="otherInfo && otherInfo.IsReadingTaskViewInOrder !== 0">{{ $t('trials:readTask:applyReread:title').replace('xxx', rowData.SubjectCode).replace('yyy', rowData.TaskBlindName) }}</p>
<p v-else>{{ $t('trials:readTask:applyReread:title2').replace('yyy', rowData.TaskBlindName) }}</p>
<!-- 申请原因 -->
<el-divider content-position="left">{{ $t('trials:readTask:title:applyReason') }}</el-divider>
<!-- 申请原因 -->
@@ -201,7 +206,7 @@
<!-- 是否复制阅片表单 -->
<el-form-item :label="$t('trials:readTask:title:copyForm')" prop="IsCopyOrigenalForms" :rules="[
{ required: true, message: $t('common:ruleMessage:select') },
]">
]" v-if="rowData.ReadingTool !== 3">
<el-radio-group v-model="ApplyforReasonForm.IsCopyOrigenalForms">
<el-radio v-for="item of $d.YesOrNo" :key="`IsCopyOrigenalForms${item.raw.Id}`" :label="item.value">{{
item.label }}</el-radio>
@@ -209,7 +214,7 @@
</el-form-item>
<!-- 是否复制阅片表单 -->
<el-form-item :label="$t('trials:readTask:title:IsCopyLesionAnswer')"
v-if="ApplyforReasonForm.IsCopyOrigenalForms && isTumor && rowData.ReadingCategory === 1" prop="IsCopyFollowForms" :rules="[
v-if="ApplyforReasonForm.IsCopyOrigenalForms && rowData.ReadingCategory === 1 && otherInfo.IsReadingTaskViewInOrder === 1" prop="IsCopyFollowForms" :rules="[
{ required: true, message: $t('common:ruleMessage:select') },
]">
<el-radio-group v-model="ApplyforReasonForm.IsCopyFollowForms">
@@ -229,7 +234,7 @@
<el-table-column prop="TrialSiteCode" :label="$t('trials:readTask:table:siteCode')" min-width="100"
show-overflow-tooltip />
<!-- 受试者编号 -->
<el-table-column prop="SubjectCode" :label="$t('trials:readTask:table:subjectCode')" min-width="120"
<el-table-column v-if="otherInfo && otherInfo.IsReadingTaskViewInOrder !== 0" prop="SubjectCode" :label="$t('trials:readTask:table:subjectCode')" min-width="120"
show-overflow-tooltip />
<!-- <el-table-column
prop="VisitTaskNum"
@@ -331,6 +336,7 @@ import { openWindow } from "@/utils/splitScreen";
const searchDataDefault = () => {
return {
SubjectCode: '',
TaskBlindName: '',
SortField: '',
PageIndex: 1,
PageSize: 20,
@@ -365,13 +371,15 @@ export default {
RequestReReadingReason: [{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }]
},
trialCriterionList: [],
TrialReadingCriterionId: '0',
openWindow: null
TrialReadingCriterionId: '',
openWindow: null,
otherInfo: null
}
},
watch: {
TrialReadingCriterionId(v) {
if (v) {
this.searchData = searchDataDefault()
this.getList()
}
}
@@ -413,7 +421,11 @@ export default {
getIRHaveReadTaskList(this.searchData).then(res => {
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
this.otherInfo = res.OtherInfo
this.loading = false
this.$nextTick(() => {
this.$refs[this.TrialReadingCriterionId][0].doLayout()
})
}).catch(() => { this.loading = false })
},
handleSearch() {
@@ -471,7 +483,8 @@ export default {
this.ApplyforReasonVisible = true
this.ApplyforReasonForm = {
Type: null,
Remake: null
Remake: null,
IsCopyOrigenalForms: this.rowData.ReadingTool === 3 ? false : null
}
this.loading = false
}).catch(() => { this.loading = false })
@@ -482,7 +495,7 @@ export default {
if (!valid) return
this.loading = true
this.btnLoading = true
if (!this.ApplyforReasonForm.IsCopyOrigenalForms || !this.isTumor) {
if (!this.ApplyforReasonForm.IsCopyOrigenalForms) {
this.ApplyforReasonForm.IsCopyFollowForms = false
}
var params = {
@@ -496,6 +509,7 @@ export default {
// IsCopyLesionAnswer: this.ApplyforReasonForm.IsCopyOrigenalForms,
RequestReReadingReason: this.ApplyforReasonForm.Type === 2 ? this.ApplyforReasonForm.RequestReReadingReason : this.$t('trials:readTask:option:errorRecords') // ''
}
console.log(params)
applyReReading(params).then(res => {
this.loading = false
this.btnLoading = false
@@ -1,94 +1,127 @@
<template>
<BaseContainer>
<div slot="search-container">
<el-form :inline="true">
<!-- 受试者编号 -->
<el-form-item :label="$t('trials:pendingReadingTasks:table:subjectCode')">
<el-input v-model="searchData.SubjectCode" style="width: 130px" clearable />
</el-form-item>
<el-form-item>
<!-- 查询 -->
<el-button type="primary" icon="el-icon-search" @click="handleSearch">
{{ $t('common:button:search') }}
</el-button>
<!-- 重置 -->
<el-button type="primary" icon="el-icon-refresh-left" @click="handleReset">
{{ $t('common:button:reset') }}
</el-button>
</el-form-item>
</el-form>
</div>
<div slot="main-container">
<el-table v-adaptive="{ bottomOffset: 75 }" :data="list" stripe height="100" @sort-change="handleSortChange"
v-loading="loading">
<el-table-column type="index" width="40" align="left" />
<el-table-column prop="IsUrgent" :label="$t('trials:consistencyCheck:table:isUrgent')"
show-overflow-tooltip min-width="100">
<template slot-scope="scope">
<el-tag :type="scope.row.IsUrgent
? 'danger' : 'primary'
">{{ $fd('YesOrNo', scope.row.IsUrgent) }}</el-tag>
</template>
</el-table-column>
<!-- 受试者编号 -->
<el-table-column prop="SubjectCode" min-width="100"
:label="$t('trials:pendingReadingTasks:table:subjectCode')" show-overflow-tooltip
sortable="custom" />
<!-- 访视数量 -->
<el-table-column prop="VisitCount" :label="$t('trials:pendingReadingTasks:table:VisitCount')"
show-overflow-tooltip sortable="custom" />
<!-- dicom检查数量 -->
<!-- <el-table-column prop="DicomStudyCount" :label="$t('trials:pendingReadingTasks:table:DicomStudyCount')"
<div>
<el-tabs type="border-card" v-model="activeName">
<template v-for="item in tabPaneList">
<el-tab-pane :label="$t(`uploadDicomAndNonedicom:label:TargetSection_${item}`)" :name="item">
<BaseContainer v-if="activeName == item">
<div slot="search-container">
<el-form :inline="true">
<!-- 受试者编号 -->
<el-form-item :label="$t('trials:pendingReadingTasks:table:subjectCode')">
<el-input v-model="searchData.SubjectCode" style="width: 130px" clearable />
</el-form-item>
<el-form-item>
<!-- 查询 -->
<el-button type="primary" icon="el-icon-search" @click="handleSearch">
{{ $t('common:button:search') }}
</el-button>
<!-- 重置 -->
<el-button type="primary" icon="el-icon-refresh-left" @click="handleReset">
{{ $t('common:button:reset') }}
</el-button>
</el-form-item>
</el-form>
</div>
<div slot="main-container">
<el-table v-adaptive="{ bottomOffset: 85 }" :data="list" stripe height="100"
@sort-change="handleSortChange" v-loading="loading">
<el-table-column type="index" width="40" align="left" />
<el-table-column prop="IsUrgent" :label="$t('trials:consistencyCheck:table:isUrgent')"
show-overflow-tooltip min-width="100">
<template slot-scope="scope">
<el-tag :type="scope.row.IsUrgent
? 'danger' : 'primary'
">{{ $fd('YesOrNo', scope.row.IsUrgent) }}</el-tag>
</template>
</el-table-column>
<!-- 受试者编号 -->
<el-table-column prop="SubjectCode" min-width="100"
:label="$t('trials:pendingReadingTasks:table:subjectCode')" show-overflow-tooltip
sortable="custom" />
<!-- 访视数量 -->
<el-table-column prop="VisitCount"
:label="$t('trials:pendingReadingTasks:table:VisitCount')" show-overflow-tooltip
sortable="custom" />
<!-- dicom检查数量 -->
<!-- <el-table-column prop="DicomStudyCount" :label="$t('trials:pendingReadingTasks:table:DicomStudyCount')"
show-overflow-tooltip sortable="custom" /> -->
<!-- 非dicom检查数量 -->
<!-- <el-table-column prop="NoneDicomStudyCount"
<!-- 非dicom检查数量 -->
<!-- <el-table-column prop="NoneDicomStudyCount"
:label="$t('trials:pendingReadingTasks:table:NoneDicomStudyCount')" show-overflow-tooltip
sortable="custom" /> -->
<!-- 标记访视数量 -->
<el-table-column prop="MarkVisitCount" :label="$t('trials:pendingReadingTasks:table:MarkVisitCount')"
show-overflow-tooltip>
<template slot-scope="scope">
<span>{{ `${scope.row.MarkIVUSVisitCount || 0}/${scope.row.MarkOCTVisitCount || 0}` }}</span>
</template>
</el-table-column>
<!-- 标记dicom检查数量 -->
<!-- <el-table-column prop="MarkDicomStudyCount"
<!-- 标记访视数量 -->
<el-table-column prop="MarkVisitCount"
:label="$t('trials:pendingReadingTasks:table:MarkVisitCount')" show-overflow-tooltip
sortable="custom">
<template slot-scope="scope">
<span>{{ scope.row.MarkIVUSVisitCount || 0
}}</span>
</template>
</el-table-column>
<!-- 标记访视数量 -->
<el-table-column prop="MarkVisitCount"
:label="$t('trials:pendingReadingTasks:table:MarkOCTVisitCount')"
show-overflow-tooltip sortable="custom">
<template slot-scope="scope">
<span>{{ scope.row.MarkOCTVisitCount ||
0
}}</span>
</template>
</el-table-column>
<!-- 标记dicom检查数量 -->
<!-- <el-table-column prop="MarkDicomStudyCount"
:label="$t('trials:pendingReadingTasks:table:MarkDicomStudyCount')" show-overflow-tooltip
sortable="custom" /> -->
<!-- 标记非dicom检查数量 -->
<!-- <el-table-column prop="MarkNoneDicomStudyCount"
<!-- 标记非dicom检查数量 -->
<!-- <el-table-column prop="MarkNoneDicomStudyCount"
:label="$t('trials:pendingReadingTasks:table:MarkNoneDicomStudyCount')" show-overflow-tooltip
sortable="custom" /> -->
<el-table-column :label="$t('common:action:action')" width="100" fixed="right">
<template slot-scope="scope">
<!-- 阅片 -->
<!-- <el-button :disabled="scope.row.ExistReadingApply" circle :title="scope.row.ExistReadingApply
<!-- 下载时间 -->
<el-table-column prop="LatestDownloadTime" min-width="100"
:label="$t('trials:pendingReadingTasks:table:latestDownloadTime')"
show-overflow-tooltip sortable="custom" />
<!-- 上传时间 -->
<el-table-column prop="LatestUploadTime" min-width="100"
:label="$t('trials:pendingReadingTasks:table:latestUploadTime')"
show-overflow-tooltip sortable="custom" />
<el-table-column :label="$t('common:action:action')" width="100" fixed="right">
<template slot-scope="scope">
<!-- 阅片 -->
<!-- <el-button :disabled="scope.row.ExistReadingApply" circle :title="scope.row.ExistReadingApply
? $t(
'trials:pendingReadingTasks:button:ExistReadingApply'
)
: $t('trials:pendingReadingTasks:button:review')
" icon="el-icon-edit-outline" @click="handleReadImage(scope.row)" /> -->
<!-- 上传 -->
<el-button v-hasPermi="['role:ir', 'role:pm', 'role:apm']" circle icon="el-icon-upload2"
:title="$t('trials:pendingReadingTasks:button:upload')"
@click="openUploadImage(scope.row, 'upload')" />
<!-- 下载 -->
<el-button v-hasPermi="['role:ir', 'role:pm', 'role:apm']" circle icon="el-icon-download"
:title="$t('trials:pendingReadingTasks:button:download')"
@click="openUploadImage(scope.row, 'download')" />
</template>
</el-table-column>
</el-table>
<pagination class="page" :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize"
@pagination="getList" />
</div>
<upload-dicom-and-nonedicom v-if="uploadImageVisible" :SubjectId="uploadSubjectId"
:SubjectCode="uploadSubjectCode" :Criterion="uploadTrialCriterion" :visible.sync="uploadImageVisible"
:IsImageSegment="true" :forbid="forbid" />
<download-dicom-and-nonedicom v-if="downloadImageVisible" :SubjectId="uploadSubjectId"
:SubjectCode="uploadSubjectCode" :Criterion="uploadTrialCriterion" :visible.sync="downloadImageVisible"
:IsImageSegment="true" :forbid="forbid" />
</BaseContainer>
<!-- 上传 -->
<el-button v-hasPermi="['role:ir', 'role:pm', 'role:apm']" circle
icon="el-icon-upload2"
:title="$t('trials:pendingReadingTasks:button:upload')"
@click="openUploadImage(scope.row, 'upload')" />
<!-- 下载 -->
<el-button v-hasPermi="['role:ir', 'role:pm', 'role:apm']" circle
icon="el-icon-download"
:title="$t('trials:pendingReadingTasks:button:download')"
@click="openUploadImage(scope.row, 'download')" />
</template>
</el-table-column>
</el-table>
<pagination class="page" :total="total" :page.sync="searchData.PageIndex"
:limit.sync="searchData.PageSize" @pagination="getList" />
</div>
<upload-dicom-and-nonedicom v-if="uploadImageVisible" :SubjectId="uploadSubjectId"
:SubjectCode="uploadSubjectCode" :Criterion="uploadTrialCriterion"
:visible.sync="uploadImageVisible" :IsImageSegment="true" :forbid="forbid" />
<download-dicom-and-nonedicom v-if="downloadImageVisible" :SubjectId="uploadSubjectId"
:SubjectCode="uploadSubjectCode" :Criterion="uploadTrialCriterion"
:visible.sync="downloadImageVisible" :IsImageSegment="true" :forbid="forbid" />
</BaseContainer>
</el-tab-pane>
</template>
</el-tabs>
</div>
</template>
<script>
import BaseContainer from '@/components/BaseContainer'
@@ -99,6 +132,7 @@ import { getTrialSubjectVisitMarkList } from "@/api/trials"
const searchDataDefault = () => {
return {
SubjectCode: '',
State: 0,
PageIndex: 1,
PageSize: 20,
}
@@ -129,6 +163,8 @@ export default {
},
data() {
return {
activeName: '0',
tabPaneList: ['0', '1', '2'],
searchData: searchDataDefault(),
loading: false,
list: [],
@@ -153,6 +189,9 @@ export default {
if (!v) {
this.getList()
}
},
activeName() {
this.getList()
}
},
mounted() {
@@ -171,6 +210,7 @@ export default {
try {
this.searchData.TrialId = this.$route.query.trialId
this.searchData.TrialReadingCriterionId = this.TrialReadingCriterionId
this.searchData.State = this.activeName
this.loading = true
let res = await getTrialSubjectVisitMarkList(this.searchData)
this.loading = false
@@ -78,7 +78,7 @@
{{ scope.row.SuggesteFinishedTime.split(':')[0] + ':00:00' }}
</template>
</el-table-column>
<el-table-column :label="$t('common:action:action')" width="250" fixed="right">
<el-table-column :label="$t('common:action:action')" width="250" >
<template slot-scope="scope">
<!-- 阅片 -->
<el-button :disabled="scope.row.ExistReadingApply" circle :title="scope.row.ExistReadingApply
@@ -88,21 +88,21 @@
: $t('trials:pendingReadingTasks:button:review')
" icon="el-icon-edit-outline" @click="handleReadImage(scope.row)" />
<!-- 上传 -->
<el-button v-if="
<!-- <el-button v-if="
item.ImageUploadEnum > 0 &&
item.IsReadingTaskViewInOrder > 1 &&
!scope.row.IsSubjectJudge
" v-hasPermi="['role:ir']" circle icon="el-icon-upload2"
:title="$t('trials:pendingReadingTasks:button:upload')"
@click="openUploadImage(scope.row, item, 'upload')" />
@click="openUploadImage(scope.row, item, 'upload')" /> -->
<!-- 下载 -->
<el-button v-if="
<!-- <el-button v-if="
item.ImageDownloadEnum > 0 &&
item.IsReadingTaskViewInOrder > 1 &&
!scope.row.IsSubjectJudge
" v-hasPermi="['role:ir']" circle icon="el-icon-download"
:title="$t('trials:pendingReadingTasks:button:download')"
@click="openUploadImage(scope.row, item, 'download')" />
@click="openUploadImage(scope.row, item, 'download')" /> -->
</template>
</el-table-column>
</el-table>
@@ -234,6 +234,7 @@ export default {
watch: {
TrialReadingCriterionId(v) {
if (v) {
this.searchData = searchDataDefault()
this.getList()
}
},
@@ -1,6 +1,6 @@
<template>
<BaseContainer class="reread-task">
<el-tabs v-model="TrialReadingCriterionId" type="border-card">
<el-tabs v-if="TrialReadingCriterionId" v-model="TrialReadingCriterionId" type="border-card">
<el-tab-pane v-for="item of trialCriterionList" :key="item.TrialReadingCriterionId" :label="item.TrialReadingCriterionName" :name="item.TrialReadingCriterionId">
<div v-if="TrialReadingCriterionId === item.TrialReadingCriterionId">
<div slot="search-container">
@@ -35,7 +35,7 @@
</el-select>
</el-form-item>
<!-- 受试者编号 -->
<el-form-item :label="$t('trials:rereadTask:table:subjectCode')">
<el-form-item v-if="otherInfo && otherInfo.IsReadingTaskViewInOrder !== 0" :label="$t('trials:rereadTask:table:subjectCode')">
<el-input v-model="searchData.SubjectCode" style="width:100px;" />
</el-form-item>
<!-- <el-form-item style="margin-bottom:10px" label="访视/阅片期名称">
@@ -45,14 +45,14 @@
clearable
/>
</el-form-item> -->
<!-- 任务编号 -->
<!-- <el-form-item :label="$t('trials:rereadTask:table:taskCode')">
<!-- 任务名称 -->
<el-form-item :label="$t('trials:rereadTask:table:taskBlindName')">
<el-input
v-model="searchData.TaskCode"
v-model="searchData.TaskBlindName"
style="width:130px;"
clearable
/>
</el-form-item> -->
</el-form-item>
<el-form-item :label="$t('trials:rereadTask:table:taskState')">
<el-select v-model="searchData.TaskState" clearable style="width:120px;">
<el-option v-for="i of $d.TaskState" :key="'TaskState' + i.label" :value="i.value" :label="i.label" />
@@ -82,6 +82,7 @@
</div>
<div slot="main-container">
<el-table
:ref="TrialReadingCriterionId"
v-adaptive="{bottomOffset:75}"
v-loading="loading"
:data="list"
@@ -121,6 +122,7 @@
/>
<!-- 受试者编号 -->
<el-table-column
v-if="otherInfo && otherInfo.IsReadingTaskViewInOrder !== 0"
prop="OriginalReReadingTask.SubjectCode"
min-width="90"
:label="$t('trials:rereadTask:table:subjectCode')"
@@ -303,6 +305,7 @@ const searchDataDefault = () => {
return {
IsUrgent: null,
SubjectCode: '',
TaskBlindName: '',
TaskCode: '',
PageIndex: 1,
PageSize: 20,
@@ -330,12 +333,14 @@ export default {
},
ConfirmReReadingVisible: false,
trialCriterionList: [],
TrialReadingCriterionId: '0'
TrialReadingCriterionId: '',
otherInfo: null
}
},
watch: {
TrialReadingCriterionId(v) {
if (v) {
this.searchData = searchDataDefault()
this.getList()
}
}
@@ -373,7 +378,11 @@ export default {
getIRReReadingTaskList(this.searchData).then(res => {
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
this.otherInfo = res.OtherInfo
this.loading = false
this.$nextTick(() => {
this.$refs[this.TrialReadingCriterionId][0].doLayout()
})
}).catch(() => { this.loading = false })
},
handleSearch() {
@@ -192,8 +192,8 @@
<svg-icon style="cursor: pointer;" icon-class="documentation" class="svg-icon"
@click.stop="viewCD(v.taskInfo.VisitTaskId)" />
</div>
<h2 v-if="taskInfo && taskInfo.IsReadingShowSubjectInfo && v.taskInfo" class="subject-info">
{{ `${taskInfo.SubjectCode} ${v.taskInfo.TaskBlindName} ` }}
<h2 class="subject-info">
{{ taskInfo && taskInfo.IsReadingShowSubjectInfo && v.taskInfo ? taskInfo.SubjectCode + ' ' + v.taskInfo.TaskBlindName : v.taskInfo.TaskBlindName }}
</h2>
<div v-if="v.currentFileName">{{ v.currentFileName }}</div>
</div>
@@ -171,6 +171,21 @@ export default {
document.addEventListener("click", this.foo);
},
methods: {
setDefaultRelatedStudyInfo(visitTaskInfo) {
if (!visitTaskInfo) return
const studyList = visitTaskInfo.StudyList || []
if (studyList.length === 0) return
const firstStudy = studyList[0]
const fileList = firstStudy.NoneDicomStudyFileList || []
if (fileList.length === 0) return
this.relatedStudyInfo = {
fileInfo: fileList[0],
visitTaskInfo,
fileList,
fileIndex: 0,
studyId: firstStudy.Id
}
},
handleReadingChart(row) {
let { e, data } = row
let obj = Object.assign({}, data)
@@ -236,7 +251,7 @@ export default {
return i
})
this.$set(this.visitTaskList[taskIdx], 'Annotations', annotations)
this.$refs[visitTaskId][0].initCurrentMaredFiles()
this.$refs[visitTaskId][0].initCurrentMaredFiles(annotations)
this.$refs.fileViewer.resetAnnotations({ annotations, visitTaskId })
},
setReadingTaskState(state) {
@@ -272,6 +287,9 @@ export default {
const idx = res.Result.findIndex(i => i.IsCurrentTask)
if (idx > -1) {
await this.setActiveTaskVisitId(res.Result[idx].VisitTaskId)
if (this.taskInfo.IsReadingTaskViewInOrder === 0) {
this.setDefaultRelatedStudyInfo(this.visitTaskList[idx])
}
this.$nextTick(() => {
this.$refs[res.Result[idx].VisitTaskId][0].setInitActiveFile()
})
@@ -282,23 +300,15 @@ export default {
if (i > -1) {
await this.getReadingImageFile(res.Result[i].VisitTaskId, i)
await this.getAnnotations(res.Result[i].VisitTaskId, i)
const studyList = this.visitTaskList[i].StudyList
if (studyList.length > 0) {
const fileInfo = studyList[0].NoneDicomStudyFileList[0]
this.relatedStudyInfo = { fileInfo, visitTaskInfo: this.visitTaskList[i], fileList: studyList[0].NoneDicomStudyFileList, fileIndex: 0, studyId: studyList[0].Id }
if (!this.selectArr.includes(res.Result[i].VisitTaskId)) {
this.selectArr.push(res.Result[i].VisitTaskId)
}
this.setDefaultRelatedStudyInfo(this.visitTaskList[i])
if (!this.selectArr.includes(res.Result[i].VisitTaskId)) {
this.selectArr.push(res.Result[i].VisitTaskId)
}
}
}
if (this.taskInfo.IsReadingTaskViewInOrder === 2) {
//
const studyList = this.visitTaskList[idx].StudyList
if (studyList.length > 0) {
const fileInfo = studyList[0].NoneDicomStudyFileList[0]
this.relatedStudyInfo = { fileInfo, visitTaskInfo: this.visitTaskList[idx], fileList: studyList[0].NoneDicomStudyFileList, fileIndex: 0, studyId: studyList[0].Id }
}
this.setDefaultRelatedStudyInfo(this.visitTaskList[idx])
}
if (this.readingTaskState < 2) {
this.$refs[res.Result[idx].VisitTaskId][0].initCurrentMaredFiles()
@@ -4,7 +4,10 @@
<div v-if="taskInfo && taskInfo.IsReadingShowSubjectInfo" :title="taskInfo.SubjectCode">
{{ taskInfo.SubjectCode }}
</div>
<div v-if="taskInfo && taskInfo.IsReadingShowSubjectInfo" :title="visitTaskInfo.TaskBlindName">
<div :title="visitTaskInfo.TaskBlindName">
<span v-if="taskInfo && !taskInfo.IsReadingShowSubjectInfo">
{{ $t('trials:reading:title:taskName') }}
</span>
{{ visitTaskInfo.TaskBlindName }}
</div>
</div>
@@ -15,12 +18,13 @@
<div v-if="!study.IsCriticalSequence" class="dicom-desc">
<!-- <div v-if="taskInfo && taskInfo.IsShowStudyName">{{ study.StudyName }}</div> -->
<div style="text-overflow: ellipsis;overflow: hidden;">
<span :title="study.CodeView">{{ study.CodeView }}</span>
<span v-if="taskInfo && taskInfo.IsReadingTaskViewInOrder !== 0" :title="study.CodeView">{{
study.CodeView }}</span>
<span v-if="taskInfo && taskInfo.IsShowStudyName" :title="study.StudyName" style="margin-left: 5px;">{{
study.StudyName }}</span>
</div>
<div style="text-overflow: ellipsis;overflow: hidden;">
<span :title="study.BodyPart">{{ getBodyPart(study.BodyPart, study.BodyPartForEditOther) }}</span>
<span :title="study.DisplayBodyPart">{{ study.DisplayBodyPart }}</span>
<span style="margin-left: 5px;" :title="study.Modality">{{ study.Modality }}</span>
</div>
</div>
@@ -91,21 +95,48 @@ export default {
},
async mounted() {
this.taskInfo = JSON.parse(localStorage.getItem('taskInfo'))
this.studyList = this.visitTaskInfo.StudyList
this.BodyPart.Bodypart = await this.$getBodyPart(this.$route.query.trialId)
this.studyList = this.decorateStudyList(this.visitTaskInfo.StudyList || [])
this.initCurrentMaredFiles(this.visitTaskInfo.Annotations)
if (this.studyList.length === 0) return
this.$nextTick(() => {
this.activeStudy(this.studyList[0].Id)
})
this.BodyPart.Bodypart = await this.$getBodyPart(this.$route.query.trialId)
},
watch: {
activeFileIndex() {
this.scrollElementToTop(this.$refs[`noneDicomRef_${this.activeFileIndex}`][0], {
offset: 50
})
if (this.$refs[`noneDicomRef_${this.activeFileIndex}`]) {
this.scrollElementToTop(this.$refs[`noneDicomRef_${this.activeFileIndex}`][0], {
offset: 50
})
}
},
'visitTaskInfo.Annotations': {
handler(annotations) {
this.initCurrentMaredFiles(annotations)
},
deep: true
}
},
methods: {
buildCurrentMarkedFiles(annotations = []) {
return annotations.reduce((result, item) => {
const fileId = item?.NoneDicomFileId
if (!fileId) return result
if (!Object.hasOwn(result, fileId)) {
result[fileId] = { count: 1 }
} else {
result[fileId].count++
}
return result
}, {})
},
decorateStudyList(studyList = []) {
return studyList.map(study => ({
...study,
DisplayBodyPart: this.getBodyPart(study.BodyPart, study.BodyPartForEditOther)
}))
},
scrollElementToTop(element, options = {}) {
const container = this.$refs['studyBox_ps']
if (!container || !element) return
@@ -122,6 +153,7 @@ export default {
},
getBodyPart(bodyPart, other) {
if (!bodyPart && !other) return ''
if (!bodyPart) return other || ''
var separator = ','
if (bodyPart.indexOf('|') > -1) {
separator = '|'
@@ -142,6 +174,9 @@ export default {
},
//
setInitActiveFile() {
if (this.studyList.length === 0) {
this.studyList = this.decorateStudyList(this.visitTaskInfo.StudyList || [])
}
if (this.studyList.length === 0) return
this.$nextTick(() => {
this.activeStudy(this.studyList[0].Id)
@@ -172,8 +207,9 @@ export default {
this.activeFileIndex = fIndex
},
activeStudy(id) {
if (this.activeNames.indexOf(id) > -1) return
this.activeNames.push(id)
const studyId = `${id}`
if (this.activeNames.indexOf(studyId) > -1) return
this.activeNames.push(studyId)
},
handleChange(v) {
console.log(v)
@@ -187,6 +223,9 @@ export default {
if (type === 'remove') {
if (Object.hasOwn(this.currentMarkedFiles, fileId)) {
this.currentMarkedFiles[fileId].count--
if (this.currentMarkedFiles[fileId].count <= 0) {
this.$delete(this.currentMarkedFiles, fileId)
}
}
} else if (type === 'add') {
if (!Object.hasOwn(this.currentMarkedFiles, fileId)) {
@@ -197,15 +236,8 @@ export default {
}
},
initCurrentMaredFiles() {
this.currentMarkedFiles = {}
this.visitTaskInfo.Annotations.map(i => {
if (!Object.hasOwn(this.currentMarkedFiles, i.NoneDicomFileId)) {
this.$set(this.currentMarkedFiles, i.NoneDicomFileId, { count: 1 })
} else {
this.currentMarkedFiles[i.NoneDicomFileId].count++
}
})
initCurrentMaredFiles(annotations = this.visitTaskInfo.Annotations || []) {
this.currentMarkedFiles = this.buildCurrentMarkedFiles(annotations)
},
}
}
@@ -227,7 +259,13 @@ export default {
margin: 0;
text-align: center;
background-color: #4c4c4c;
height: 50px;
min-height: 50px;
height: auto;
flex-shrink: 0;
display: flex;
flex-direction: column;
justify-content: center;
word-break: break-word;
}
.dicom-desc {
@@ -1,18 +1,60 @@
<!-- eslint-disable -->
<!-- eslint-disable vue/no-unused-vars -->
<!-- eslint-disable vue/no-template-shadow -->
/* eslint-disable */
<template>
<el-form v-loading="loading">
<el-form v-loading="loading" :label-width="'100px'">
<!-- 仲裁对象 -->
<el-form-item :label="$t('trials:adRules:title:arbitrationRule')" :label-width="'180px'">
<el-radio-group v-model="JudgyInfo.ArbitrationRule" :disabled="OtherInfo.IsSign">
<el-form-item :label="$t('trials:adRules:title:arbitrationRule')">
<el-radio-group v-model="JudgyInfo.ArbitrationRule" :disabled="isSign">
<el-radio v-for="item of $d.ArbitrationRule" :key="item.id"
:disabled="!JudgyInfo.IsReadingTaskViewInOrder || true" :label="item.value" @change="changeArbitrationRule">
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-collapse v-model="activeNames" style="border-top:none;">
<!-- 仲裁模式JudgeModeEnum 整体结果择一判定指标组合判定-->
<el-form-item :label="$t('trials:adRules:title:judgeMode')">
<el-radio-group v-model="JudgyInfo.JudgeModeEnum" :disabled="isSign">
<el-radio
v-for="item of $d.JudgeMode"
:key="item.id"
:disabled="JudgyInfo.ArbitrationRule !== 1"
:label="item.value"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- 裁判规则JudgeRuleEnum 访视一致规则访视差异规则-->
<el-form-item :label="$t('trials:adRules:title:judgeRule')">
<el-radio-group v-model="JudgyInfo.JudgeRuleEnum" :disabled="isSign">
<el-radio
v-for="item of $d.JudgeRule"
:key="item.id"
:label="item.value"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item>
<el-button
v-hasPermi="['trials:trials-panel:setting:reading-unit:edit']"
type="primary"
size="small"
@click="saveJugeRule"
v-show="!isSign">
{{ $t('common:button:save') }}
</el-button>
</el-form-item>
<ArbitrationRulesByVisit
v-if="judgeRuleEnum === 1"
ref="visitRules"
:trial-reading-criterion-id="trialReadingCriterionId"
:isSign="isSign"
/>
<el-collapse v-else-if="judgeRuleEnum === 0" v-model="activeNames" style="border-top:none;">
<el-collapse-item v-for="(item, index) of QuestionList" :key="item.ReadingQuestionTrialId"
style="position: relative;padding:0 10px;" :name="item.ReadingQuestionTrialId">
<div slot="title">
@@ -21,24 +63,28 @@
<div style="position: relative">
<el-button v-hasPermi="['trials:trials-panel:setting:reading-unit:edit']"
style="position: absolute; right: 0; top: 0; z-index: 10" size="mini" type="primary"
:disabled="OtherInfo.IsSign" @click="apply(item.ReadingQuestionTrialId, index)">{{ $t('common:button:save')
:disabled="isSign" @click="apply(item.ReadingQuestionTrialId, index)">{{ $t('common:button:save')
}}</el-button>
<el-button v-hasPermi="['trials:trials-panel:setting:reading-unit:edit']"
style="position: absolute; right: 60px; top: 0; z-index: 10" size="mini" type="primary"
:disabled="OtherInfo.IsSign" @click="reset(item.ReadingQuestionTrialId, index)">{{ $t('common:button:reset')
:disabled="isSign" @click="reset(item.ReadingQuestionTrialId, index)">{{ $t('common:button:reset')
}}</el-button>
<!-- 产生裁判阅片的条件 -->
<el-form-item :label-width="'280px'" :label="$t('trials:adRules:title:judgeTypeEnum')" style="width: 95%">
<el-radio-group v-model="QuestionList[index].JudgeType" @input="(val) => JudgeTypeChange(val, index)">
<template v-for="item of $d.JudgeTypeEnum">
<el-radio style="margin-bottom: 5px" :key="item.id" :disabled="OtherInfo.IsSign ||
!hasPermi(['trials:trials-panel:setting:reading-unit:edit'])
" :label="item.value" v-if="
item.value < 4 ||
(item.value > 3 &&
(QuestionList[index].Type === 'number' ||
QuestionList[index].Type === 'calculation'))
">
<el-radio
style="margin-bottom: 5px"
:key="item.id"
:disabled="isSign || !hasPermi(['trials:trials-panel:setting:reading-unit:edit'])"
:label="item.value"
v-if="
(['number', 'calculation'].includes(QuestionList[index].Type)
? [1, 4, 5]
: [1, 2, 3]
).includes(item.value)
"
>
{{ item.label }}
</el-radio>
</template>
@@ -48,7 +94,7 @@
<div v-if="QuestionList[index].JudgeType === 3" style="text-align: right; margin: 10px 0">
<!-- 添加规则 -->
<el-button v-hasPermi="['trials:trials-panel:setting:reading-unit:edit']" size="mini" type="primary"
:disabled="OtherInfo.IsSign" @click="addGroup(index, null)">{{ $t('trials:adRules:button:addRule')
:disabled="isSign" @click="addGroup(index, null)">{{ $t('trials:adRules:button:addRule')
}}</el-button>
</div>
<el-table v-if="QuestionList[index].JudgeType === 3" v-loading="loading"
@@ -87,10 +133,10 @@
<template slot-scope="scope">
<!-- 编辑 -->
<el-button icon="el-icon-edit" circle :title="$t('common:button:edit')" size="mini"
:disabled="OtherInfo.IsSign" @click="addGroup(index, scope.$index)" />
:disabled="isSign" @click="addGroup(index, scope.$index)" />
<!-- 删除 -->
<el-button icon="el-icon-delete" circle :title="$t('common:button:delete')" size="mini"
:disabled="OtherInfo.IsSign" @click="tagClose(index, scope.$index)" />
:disabled="isSign" @click="tagClose(index, scope.$index)" />
</template>
</el-table-column>
</el-table>
@@ -103,12 +149,12 @@
">
<el-checkbox-group v-if="QuestionList[index].QuestionGenre === 3" v-model="QuestionList[index].grouping">
<template v-for="item of $d[QuestionList[index].DictionaryCode]">
<el-checkbox v-if="item.value !== -1" :key="item.id" :disabled="OtherInfo.IsSign" :label="item.value">{{
<el-checkbox v-if="item.value !== -1" :key="item.id" :disabled="isSign" :label="item.value">{{
item.label }}</el-checkbox>
</template>
</el-checkbox-group>
<el-checkbox-group v-else v-model="QuestionList[index].grouping">
<el-checkbox v-for="item of item.TypeValue.split('|')" :key="item" :disabled="OtherInfo.IsSign"
<el-checkbox v-for="item of item.TypeValue.split('|')" :key="item" :disabled="isSign"
:label="item">{{
item }}</el-checkbox>
</el-checkbox-group>
@@ -135,7 +181,7 @@
.toString()
.replaceAll(',', '|')
}}</el-tag>
<el-button size="mini" type="primary" :disabled="OtherInfo.IsSign" @click="addGroup2(index)">
<el-button size="mini" type="primary" :disabled="isSign" @click="addGroup2(index)">
<!-- 添加分组 -->
{{ $t('trials:adRules:title:addGroup') }}
</el-button>
@@ -181,7 +227,7 @@
)
" prop="JudgeDifferenceType">
<el-select v-model="QuestionList[index].JudgeDifferenceType" placeholder="请选择"
:disabled="OtherInfo.IsSign">
:disabled="isSign">
<el-option v-for="item of $d.JudgeDifferenceType" :key="item.id" :label="item.label"
:value="item.value">
</el-option>
@@ -193,7 +239,7 @@
" prop="JudgeDifferenceValue">
<div style="display: flex">
<el-input v-model="QuestionList[index].JudgeDifferenceValue" clearable
:disabled="OtherInfo.IsSign"></el-input>
:disabled="isSign"></el-input>
<span style="margin-left: 10px" v-if="QuestionList[index].JudgeType !== 5 && QuestionList[index].Unit !== 0">
{{ `${QuestionList[index].Unit === 4 ? QuestionList[index].CustomUnit : $fd('ValueUnit', QuestionList[index].Unit)}`}}
</span>
@@ -203,8 +249,6 @@
</div>
</el-collapse-item>
</el-collapse>
<!-- </el-tab-pane>-->
<!-- </el-tabs>-->
<!-- 选择答案 -->
<el-dialog v-if="QuestionVisible" :title="$t('trials:adRules:title:selectAnswer')" :visible.sync="QuestionVisible"
width="800px" :close-on-click-modal="false" custom-class="base-dialog-wrapper" append-to-body>
@@ -212,40 +256,50 @@
<!-- 阅片人A -->
<el-form-item label-width="110px" :label="$t('trials:adRules:title:answerGroupA')">
<el-checkbox-group v-model="QuestionList[selectIndex].groupingA">
<template v-for="item of QuestionList[selectIndex].TypeValue.split('|')">
<el-checkbox v-if="QuestionList[selectIndex].QuestionGenre !== 3" :key="item" :label="item" :disabled="QuestionList[selectIndex].groupingB.length
? ~QuestionList[selectIndex].groupingB.indexOf(item)
: false
">{{ item }}</el-checkbox>
<template v-for="item of QuestionList[selectIndex].TypeValue.split('|') || []">
<el-checkbox
v-if="QuestionList[selectIndex].QuestionGenre !== 3"
:key="item"
:label="item"
:disabled="QuestionList[selectIndex]?.groupingB?.includes(item) ?? false"
>
{{ item }}
</el-checkbox>
</template>
<template v-for="item of $d[QuestionList[selectIndex].DictionaryCode]">
<el-checkbox v-if="
QuestionList[selectIndex].QuestionGenre === 3 &&
item.value !== -1
" :key="item.id" :label="item.value" :disabled="QuestionList[selectIndex].groupingB.length
? ~QuestionList[selectIndex].groupingB.indexOf(item.value)
: false
">{{ item.label }}</el-checkbox>
<el-checkbox
v-if="QuestionList[selectIndex].QuestionGenre === 3 && item.value !== -1"
:key="item.id"
:label="item.value"
:disabled="QuestionList[selectIndex]?.groupingB?.includes(item.value) ?? false"
>
{{ item.label }}
</el-checkbox>
</template>
</el-checkbox-group>
</el-form-item>
<!-- 阅片人B -->
<el-form-item label-width="110px" :label="$t('trials:adRules:title:answerGroupB')">
<el-checkbox-group v-model="QuestionList[selectIndex].groupingB">
<template v-for="item of QuestionList[selectIndex].TypeValue.split('|')">
<el-checkbox v-if="QuestionList[selectIndex].QuestionGenre !== 3" :key="item" :label="item" :disabled="QuestionList[selectIndex].groupingA.length
? ~QuestionList[selectIndex].groupingA.indexOf(item)
: false
">{{ item }}</el-checkbox>
<template v-for="item of QuestionList[selectIndex].TypeValue.split('|') || []">
<el-checkbox
v-if="QuestionList[selectIndex].QuestionGenre !== 3"
:key="item"
:label="item"
:disabled="QuestionList[selectIndex]?.groupingA?.includes(item) ?? false"
>
{{ item }}
</el-checkbox>
</template>
<template v-for="item of $d[QuestionList[selectIndex].DictionaryCode]">
<el-checkbox v-if="
QuestionList[selectIndex].QuestionGenre === 3 &&
item.value !== -1
" :key="item.id" :label="item.value" :disabled="QuestionList[selectIndex].groupingA.length
? ~QuestionList[selectIndex].groupingA.indexOf(item.value)
: false
">{{ item.label }}</el-checkbox>
<el-checkbox
v-if="QuestionList[selectIndex].QuestionGenre === 3 && item.value !== -1"
:key="item.id"
:label="item.value"
:disabled="QuestionList[selectIndex]?.groupingA?.includes(item.value) ?? false"
>
{{ item.label }}
</el-checkbox>
</template>
</el-checkbox-group>
</el-form-item>
@@ -255,8 +309,8 @@
<el-button v-hasPermi="['trials:trials-panel:setting:reading-unit:edit']" :disabled="btnLoading" size="small"
type="primary" @click="
QuestionVisible = false
$set(QuestionList[selectIndex], 'groupingA', [])
$set(QuestionList[selectIndex], 'groupingB', [])
$set(QuestionList[selectIndex], 'groupingA', [])
$set(QuestionList[selectIndex], 'groupingB', [])
">
{{ $t('common:button:cancel') }}
</el-button>
@@ -269,13 +323,17 @@
</el-form>
</template>
<script>
/* eslint-disable */
import {
getTrialConfirmCriterionList,
getTrialCriterionJudgeQuestionList,
setTrialCriterionJudgeQuestionAnswerGroup,
} from '@/api/trials/reading'
import { setTrialJudgyInfo, getTrialJudgyInfo } from '@/api/trials/setting'
import { setTrialJudgyInfo, getTrialJudgyInfo, setTrialJudgeRuleInfo } from '@/api/trials/setting'
import ArbitrationRulesByVisit from './ArbitrationRulesByVisit'
export default {
components: {
ArbitrationRulesByVisit,
},
props: {
trialReadingCriterionId: {
type: String,
@@ -294,9 +352,7 @@ export default {
activeNames: null,
indexA: null,
type: null,
OtherInfo: {
IsSign: true,
},
isSign: true,
JudgyInfo: {},
JudgeDifferenceValueQRules: {
JudgeDifferenceType: [
@@ -339,47 +395,38 @@ export default {
},
],
},
judgeRuleEnum: -1,
savedJudgyInfo: {
JudgeModeEnum: null,
JudgeRuleEnum: null,
},
}
},
watch: {
TrialReadingCriterionId(v) {
async trialReadingCriterionId(v) {
if (v === null) return
this.loading = true
getTrialCriterionJudgeQuestionList({
TrialId: this.$route.query.trialId,
// ReadingQuestionCriterionTrialId: v,
TrialReadingCriterionId: this.trialReadingCriterionId,
})
.then((res) => {
this.loading = false
this.QuestionList = res.Result
this.OtherInfo = res.OtherInfo
this.activeNames = this.QuestionList.map(
(v) => v.ReadingQuestionTrialId
)
this.QuestionList.forEach((v) => {
this.$set(v, 'grouping', [])
this.$set(v, 'groupingA', [])
this.$set(v, 'groupingB', [])
this.$set(
v,
'AnswerGroupList',
Object.assign([], v.AnswerCombination)
)
this.$set(v, 'AnswerGroup2List', Object.assign([], v.AnswerGroup))
})
})
.catch(() => {
this.btnLoading = false
this.loading = false
})
await this.initData()
},
},
mounted() {
this.getList()
this.getTrialJudgyInfo()
async mounted() {
await this.initData()
},
methods: {
resetJudgeTypeRelatedFields(question = {}) {
this.$set(question, 'AnswerGroup2List', [])
this.$set(question, 'AnswerGroupList', [])
this.$set(question, 'grouping', [])
this.$set(question, 'groupingA', [])
this.$set(question, 'groupingB', [])
this.$set(question, 'JudgeDifferenceValue', 0)
this.$set(question, 'JudgeDifferenceType', 0)
},
async initData() {
await this.getTrialJudgyInfo()
if (this.judgeRuleEnum !== 1) {
await this.getList(false)
}
},
//
changeArbitrationRule(value) {
if (value !== 1) {
@@ -387,12 +434,16 @@ export default {
item.JudgeDifferenceValue = 0
item.JudgeDifferenceType = 0
})
if (this.$refs.visitRules) {
this.$refs.visitRules.changeArbitrationRule(value)
}
}
},
JudgeTypeChange(value, index) {
this.resetJudgeTypeRelatedFields(this.QuestionList[index])
this.$nextTick(() => {
if (value === 4 || value === 5) {
if (this.$refs['JudgeDifferenceValue' + value + index][0]) {
if (this.$refs['JudgeDifferenceValue' + value + index] && this.$refs['JudgeDifferenceValue' + value + index][0]) {
this.$refs['JudgeDifferenceValue' + value + index][0].resetFields()
// this.QuestionList[index].JudgeDifferenceValue = 0;
}
@@ -400,25 +451,26 @@ export default {
})
},
saveAllSync() {
return new Promise((resolve, reject) => {
if (this.judgeRuleEnum === 1 && this.$refs.visitRules) {
return Promise.resolve(true)
// return this.$refs.visitRules.saveAllSync()
}
return new Promise((resolve) => {
var arr = []
this.QuestionList.forEach((v, i) => {
arr.push(this.applySync(v.ReadingQuestionTrialId, i))
})
Promise.all(arr)
.then((res) => {
console.log(res)
.then(() => {
resolve(true)
})
.catch((res) => {
console.log('进入catch')
.catch(() => {
resolve(false)
})
})
},
applySync(ReadingQuestionTrialId, index) {
return new Promise(async (resolve, reject) => {
console.log(this.QuestionList[index].JudgeType)
if (this.QuestionList[index].JudgeType === 0) {
reject(false)
return
@@ -442,7 +494,7 @@ export default {
this.QuestionList[index].JudgeType === 5
) {
try {
let validate = await this.$refs[
const validate = await this.$refs[
'JudgeDifferenceValue' +
this.QuestionList[index].JudgeType +
index
@@ -493,19 +545,55 @@ export default {
this.loading = false
})
},
getTrialJudgyInfo() {
this.loading = true
getTrialJudgyInfo({
TrialId: this.$route.query.trialId,
TrialReadingCriterionId: this.trialReadingCriterionId,
})
.then((res) => {
this.JudgyInfo = res.Result
this.loading = false
})
.catch((v) => {
this.loading = false
async getTrialJudgyInfo() {
try {
this.loading = true
const res = await getTrialJudgyInfo({
TrialId: this.$route.query.trialId,
TrialReadingCriterionId: this.trialReadingCriterionId,
})
this.JudgyInfo = res.Result
this.judgeRuleEnum = this.JudgyInfo.JudgeRuleEnum
this.isSign = res.Result.IsSigned
} catch (e) {
console.log(e)
} finally {
this.loading = false
}
},
async saveJugeRule(showMessage = true) {
try {
this.loading = true
const prevJudgeRuleEnum = this.judgeRuleEnum
const params = {
trialReadingCriterionId: this.trialReadingCriterionId,
judgeModeEnum: this.JudgyInfo.JudgeModeEnum,
judgeRuleEnum: this.JudgyInfo.JudgeRuleEnum,
}
const res = await setTrialJudgeRuleInfo(params)
if (res.IsSuccess) {
if (showMessage) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
this.judgeRuleEnum = this.JudgyInfo.JudgeRuleEnum
if (prevJudgeRuleEnum !== this.judgeRuleEnum) {
if (this.judgeRuleEnum === 1) {
await this.$nextTick()
if (this.$refs.visitRules) {
await this.$refs.visitRules.initData()
}
} else {
await this.getList(false)
}
}
}
return true
} catch (e) {
console.log(e)
return false
} finally {
this.loading = false
}
},
tagClose2(index, indexA) {
// ''
@@ -593,7 +681,7 @@ export default {
AnswerCombination: [],
JudgeType: 0,
JudgeDifferenceValue: 0,
JudgeDifferenceType: 0,
JudgeDifferenceType: 0
})
.then((res) => {
this.$set(this.QuestionList[index], 'AnswerGroup2List', [])
@@ -635,7 +723,7 @@ export default {
this.QuestionList[index].JudgeType === 5
) {
try {
let validate = await this.$refs[
const validate = await this.$refs[
'JudgeDifferenceValue' + this.QuestionList[index].JudgeType + index
][0].validate()
if (!validate) return
@@ -655,7 +743,7 @@ export default {
),
JudgeDifferenceType: Number(
this.QuestionList[index].JudgeDifferenceType
),
)
})
.then((res) => {
this.$message.success(this.$t('trials:adRules:message:msg9')) // ''
@@ -671,18 +759,23 @@ export default {
this.ReadingQuestionCriterionTrialId =
this.CriterionList[this.index].ReadingQuestionCriterionTrialId
},
getList() {
async getList(needGetTrialJudgyInfo = true) {
this.loading = true
getTrialCriterionJudgeQuestionList({
if (needGetTrialJudgyInfo) {
await this.getTrialJudgyInfo()
}
if (this.judgeRuleEnum === 1 && this.$refs.visitRules) {
return this.$refs.visitRules.getList()
}
return getTrialCriterionJudgeQuestionList({
TrialId: this.$route.query.trialId,
TrialReadingCriterionId: this.trialReadingCriterionId,
})
.then((res) => {
this.loading = false
this.QuestionList = res.Result
this.OtherInfo = res.OtherInfo
this.activeNames = this.QuestionList.map(
(v) => v.ReadingQuestionTrialId
(item) => item.ReadingQuestionTrialId
)
this.QuestionList.forEach((v) => {
this.$set(v, 'grouping', [])
@@ -0,0 +1,955 @@
<template>
<div v-loading="loading">
<el-tabs v-model="activeVisitTab" class="visit-tabs" type="border-card">
<el-tab-pane v-for="visit in visitPlanOptions" :key="visit.Id"
:label="visit.VisitName" :name="visit.Id">
<div v-show="!isSign" class="visit-tabs-actions">
<!-- 批量保存 -->
<el-button
v-hasPermi="['trials:trials-panel:setting:reading-unit:edit']"
:disabled="btnLoading || questionListMap[visit.Id] && questionListMap[visit.Id].length === 0"
:loading="btnLoading"
size="small"
type="primary"
@click="handleBatchSave(visit.Id, visit.VisitNum)"
>
{{ $t('common:button:batchSave') }}
</el-button>
<!-- 复制 -->
<el-button
v-show="visitPlanOptions.length > 1"
v-hasPermi="['trials:trials-panel:setting:reading-unit:edit']"
:disabled="btnLoading || questionListMap[visit.Id] && questionListMap[visit.Id].length === 0"
:loading="btnLoading"
size="small"
type="primary"
@click="handleCopy(visit.VisitNum)"
>
{{ $t('common:button:copy') }}
</el-button>
<!-- 应用到随访 -->
<el-button
v-show="visitPlanOptions.length > 1 && visit.VisitNum === 0"
v-hasPermi="['trials:trials-panel:setting:reading-unit:edit']"
:disabled="btnLoading || questionListMap[visit.Id] && questionListMap[visit.Id].length === 0"
:loading="btnLoading"
size="small"
type="primary"
@click="applyToFollowup"
>
{{ $t('trials:adRules:button:applyToFollowup') }}
</el-button>
</div>
<el-collapse v-if="questionListMap[visit.Id]" v-model="visitActiveNamesMap[visit.Id]" style="border-top:none;">
<el-collapse-item v-for="(item, index) of questionListMap[visit.Id]"
:key="item.ReadingQuestionTrialId"
style="position: relative;padding:0 10px;" :name="item.ReadingQuestionTrialId">
<div slot="title">
{{ item.PageName }}Q{{ index + 1 }}{{ item.QuestionName }}
</div>
<div style="position: relative">
<el-button v-hasPermi="['trials:trials-panel:setting:reading-unit:edit']"
style="position: absolute; right: 0; top: 0; z-index: 10" size="mini" type="primary"
:disabled="isSign"
@click="visitApply(item.ReadingQuestionTrialId, index, visit.Id, visit.VisitNum)">{{ $t('common:button:save')
}}</el-button>
<el-button v-hasPermi="['trials:trials-panel:setting:reading-unit:edit']"
style="position: absolute; right: 60px; top: 0; z-index: 10" size="mini" type="primary"
:disabled="isSign"
@click="visitReset(item.ReadingQuestionTrialId, index, visit.Id, visit.VisitNum)">{{ $t('common:button:reset')
}}</el-button>
<el-form-item :label-width="'280px'" :label="$t('trials:adRules:title:judgeTypeEnum')" style="width: 95%">
<el-radio-group
v-model="questionListMap[visit.Id][index].JudgeType"
@input="(val) => visitJudgeTypeChange(val, index, visit.Id)"
>
<template v-for="judgeTypeItem of $d.JudgeTypeEnum">
<el-radio
style="margin-bottom: 5px"
:key="judgeTypeItem.id"
:disabled="isSign || !hasPermi(['trials:trials-panel:setting:reading-unit:edit'])"
:label="judgeTypeItem.value"
v-if="
(['number', 'calculation'].includes(questionListMap[visit.Id][index].Type)
? [1, 4, 5, 6]
: [1, 2, 3, 6]
).includes(judgeTypeItem.value)
"
>
{{ judgeTypeItem.label }}
</el-radio>
</template>
</el-radio-group>
</el-form-item>
</div>
<div v-if="questionListMap[visit.Id][index].JudgeType === 3" style="text-align: right; margin: 10px 0">
<el-button v-hasPermi="['trials:trials-panel:setting:reading-unit:edit']" size="mini" type="primary"
:disabled="isSign"
@click="visitAddGroup(index, null, visit.Id)">{{ $t('trials:adRules:button:addRule')
}}</el-button>
</div>
<el-table v-if="questionListMap[visit.Id][index].JudgeType === 3" v-loading="loading"
:data="questionListMap[visit.Id][index].AnswerGroupList" border stripe>
<el-table-column prop="AnswerGroupA" :label="$t('trials:adRules:title:order')" width="160">
<template slot-scope="scope">
{{ scope.$index + 1 }}
</template>
</el-table-column>
<el-table-column prop="AnswerGroupA" :label="$t('trials:adRules:title:answerGroupA')" min-width="100">
<template slot-scope="scope">
{{
questionListMap[visit.Id][index].QuestionGenre === 3
? scope.row.AnswerGroupA.map((v) =>
$fd(questionListMap[visit.Id][index].DictionaryCode, parseInt(v))
).toString()
: scope.row.AnswerGroupA.toString()
}}
</template>
</el-table-column>
<el-table-column prop="AnswerGroupB" :label="$t('trials:adRules:title:answerGroupB')" min-width="100">
<template slot-scope="scope">
{{
questionListMap[visit.Id][index].QuestionGenre === 3
? scope.row.AnswerGroupB.map((v) =>
$fd(questionListMap[visit.Id][index].DictionaryCode, parseInt(v))
).toString()
: scope.row.AnswerGroupB.toString()
}}
</template>
</el-table-column>
<el-table-column prop="AnswerGroupB" :label="$t('common:action:action')" min-width="80">
<template slot-scope="scope">
<el-button icon="el-icon-edit" circle :title="$t('common:button:edit')" size="mini"
:disabled="isSign"
@click="visitAddGroup(index, scope.$index, visit.Id)" />
<el-button icon="el-icon-delete" circle :title="$t('common:button:delete')" size="mini"
:disabled="isSign"
@click="visitTagClose(index, scope.$index, visit.Id)" />
</template>
</el-table-column>
</el-table>
<div v-if="questionListMap[visit.Id][index].JudgeType === 2">
<div style="
background: #f6f6f6;
border-radius: 20px;
padding: 15px 20px;
margin-top: 10px;
">
<el-checkbox-group v-if="questionListMap[visit.Id][index].QuestionGenre === 3"
v-model="questionListMap[visit.Id][index].grouping"
>
<template v-for="dictItem of $d[questionListMap[visit.Id][index].DictionaryCode]">
<el-checkbox v-if="dictItem.value !== -1" :key="dictItem.id" :disabled="isSign"
:label="dictItem.value">{{ dictItem.label }}</el-checkbox>
</template>
</el-checkbox-group>
<el-checkbox-group v-else v-model="questionListMap[visit.Id][index].grouping">
<el-checkbox v-for="answerItem of item.TypeValue.split('|')" :key="answerItem"
:disabled="isSign" :label="answerItem">{{ answerItem }}</el-checkbox>
</el-checkbox-group>
<div style="margin-top: 20px">
{{ $t('trials:adRules:title:selectAnswerGroup') }}
<el-tag v-if="
questionListMap[visit.Id][index].grouping.length > 0 &&
questionListMap[visit.Id][index].QuestionGenre !== 3
">{{
questionListMap[visit.Id][index].grouping.toString().replaceAll(',', '|')
}}</el-tag>
<el-tag v-if="
questionListMap[visit.Id][index].grouping.length > 0 &&
questionListMap[visit.Id][index].QuestionGenre === 3
">{{
questionListMap[visit.Id][index].grouping
.map(
(v) =>
$d[questionListMap[visit.Id][index].DictionaryCode].find(
(v1) => v1.value === v
).label
)
.toString()
.replaceAll(',', '|')
}}</el-tag>
<el-button size="mini" type="primary" :disabled="isSign"
@click="visitAddGroup2(index, visit.Id)">
{{ $t('trials:adRules:title:addGroup') }}</el-button>
</div>
</div>
<div v-if="questionListMap[visit.Id][index].QuestionGenre !== 3" style="margin-top: 20px">
{{ $t('trials:adRules:title:group') }}
<el-tag v-for="(itemA, indexA) of questionListMap[visit.Id][index].AnswerGroup2List"
:key="visit.Id+indexA" closable
style="margin-right: 10px" @close="() => visitTagClose2(index, indexA, visit.Id)">{{ itemA }}</el-tag>
</div>
<div v-if="questionListMap[visit.Id][index].QuestionGenre === 3" style="margin-top: 20px">
{{ $t('trials:adRules:title:group') }}
<el-tag v-for="(itemA, indexA) of questionListMap[visit.Id][index].AnswerGroup2List"
:key="visit.Id+indexA" closable
style="margin-right: 10px" @close="() => visitTagClose2(index, indexA, visit.Id)">
{{
itemA
.split('|')
.map((v) =>
$fd(questionListMap[visit.Id][index].DictionaryCode, parseInt(v))
)
.join('|')
}}
</el-tag>
</div>
</div>
<div v-if="
questionListMap[visit.Id][index].JudgeType === 4 ||
questionListMap[visit.Id][index].JudgeType === 5
">
<el-form :inline="true" :model="questionListMap[visit.Id][index]" class="demo-form-inline"
:ref="`JudgeDifferenceValue${questionListMap[visit.Id][index].JudgeType}_${visit.Id}_${index}`"
:rules="JudgeDifferenceValueQRules">
<el-form-item :label="$t(
`trials:trials-panel:setting:reading-unit:JudgeDifferenceType`
)
" prop="JudgeDifferenceType">
<el-select
v-model="questionListMap[visit.Id][index].JudgeDifferenceType"
:disabled="isSign">
<el-option v-for="judgeDiffItem of $d.JudgeDifferenceType" :key="judgeDiffItem.id"
:label="judgeDiffItem.label" :value="judgeDiffItem.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item :label="$t(
`trials:trials-panel:setting:reading-unit:JudgeDifferenceValue${questionListMap[visit.Id][index].JudgeType}`
)
" prop="JudgeDifferenceValue">
<div style="display: flex">
<el-input v-model="questionListMap[visit.Id][index].JudgeDifferenceValue" clearable
:disabled="isSign"></el-input>
<span style="margin-left: 10px"
v-if="questionListMap[visit.Id][index].JudgeType !== 5 && questionListMap[visit.Id][index].Unit !== 0">
{{ `${questionListMap[visit.Id][index].Unit === 4 ? questionListMap[visit.Id][index].CustomUnit : $fd('ValueUnit', questionListMap[visit.Id][index].Unit)}` }}
</span>
</div>
</el-form-item>
</el-form>
</div>
</el-collapse-item>
</el-collapse>
</el-tab-pane>
</el-tabs>
<el-dialog v-if="questionVisible" :title="$t('trials:adRules:title:selectAnswer')" :visible.sync="questionVisible"
width="800px" :close-on-click-modal="false" custom-class="base-dialog-wrapper" append-to-body>
<div class="base-dialog-body">
<el-form-item label-width="110px" :label="$t('trials:adRules:title:answerGroupA')">
<el-checkbox-group v-model="questionList[selectIndex].groupingA">
<template v-for="item of questionList[selectIndex].TypeValue.split('|') || []">
<el-checkbox
v-if="questionList[selectIndex].QuestionGenre !== 3"
:key="item"
:label="item"
:disabled="questionList[selectIndex]?.groupingB?.includes(item) ?? false"
>
{{ item }}
</el-checkbox>
</template>
<template v-for="item of $d[questionList[selectIndex].DictionaryCode]">
<el-checkbox
v-if="questionList[selectIndex].QuestionGenre === 3 && item.value !== -1"
:key="item.id"
:label="item.value"
:disabled="questionList[selectIndex]?.groupingB?.includes(item.value) ?? false"
>{{ item.label }}</el-checkbox>
</template>
</el-checkbox-group>
</el-form-item>
<el-form-item label-width="110px" :label="$t('trials:adRules:title:answerGroupB')">
<el-checkbox-group v-model="questionList[selectIndex].groupingB">
<template v-for="item of questionList[selectIndex].TypeValue.split('|') || []">
<el-checkbox
v-if="questionList[selectIndex].QuestionGenre !== 3"
:key="item"
:label="item"
:disabled="questionList[selectIndex]?.groupingA?.includes(item) ?? false"
>
{{ item }}
</el-checkbox>
</template>
<template v-for="item of $d[questionList[selectIndex].DictionaryCode]">
<el-checkbox
v-if="questionList[selectIndex].QuestionGenre === 3 && item.value !== -1"
:key="item.id"
:label="item.value"
:disabled="questionList[selectIndex]?.groupingA?.includes(item.value) ?? false"
>
{{ item.label }}
</el-checkbox>
</template>
</el-checkbox-group>
</el-form-item>
</div>
<div class="base-dialog-footer" style="text-align: right; margin-top: 10px">
<el-button v-hasPermi="['trials:trials-panel:setting:reading-unit:edit']" :disabled="btnLoading" size="small"
type="primary" @click="
questionVisible = false
$set(questionList[selectIndex], 'groupingA', [])
$set(questionList[selectIndex], 'groupingB', [])
">
{{ $t('common:button:cancel') }}
</el-button>
<el-button v-hasPermi="['trials:trials-panel:setting:reading-unit:edit']" :disabled="btnLoading" size="small"
type="primary" @click="save">
{{ $t('common:button:save') }}
</el-button>
</div>
</el-dialog>
<el-dialog
v-if="copyVisible"
:title="$t('common:button:copy')"
:visible.sync="copyVisible"
width="500px"
:close-on-click-modal="false"
custom-class="base-dialog-wrapper"
append-to-body
>
<div class="base-dialog-body">
<el-form label-width="100px">
<el-form-item :label="$t('trials:trials-panel:setting:reading-unit:targetVisitNum')">
<el-select v-model="copySourceVisitNum" style="width: 100%" filterable>
<el-option
v-for="visit in copySourceVisitOptions"
:key="visit.Id"
:label="visit.VisitName"
:value="visit.VisitNum"
/>
</el-select>
</el-form-item>
</el-form>
</div>
<div class="base-dialog-footer" style="text-align: right; margin-top: 10px">
<el-button
v-hasPermi="['trials:trials-panel:setting:reading-unit:edit']"
:disabled="btnLoading"
size="small"
@click="closeCopyDialog"
>
{{ $t('common:button:cancel') }}
</el-button>
<el-button
v-hasPermi="['trials:trials-panel:setting:reading-unit:edit']"
:disabled="btnLoading"
:loading="btnLoading"
size="small"
type="primary"
@click="confirmCopy"
>
{{ $t('common:button:confirm') }}
</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
/* eslint-disable */
import {
getTrialQuestionVisitJudgeRule,
setTrialQuestionVisitJudgeRule,
copyTrialQuestionVisitJudgeRule,
applyBaseLineTrialQuestionVisitJudgeRule
} from '@/api/trials/reading'
import { getTrialVisitStageSelect } from '@/api/trials'
export default {
props: {
trialReadingCriterionId: {
type: String,
default: '',
},
isSign: {
type: Boolean,
required: true
}
},
data() {
return {
btnLoading: false,
loading: false,
activeVisitTab: '',
visitActiveNamesMap: {},
questionListMap: {},
visitPlanOptions: [],
questionVisible: false,
copyVisible: false,
copyTargetVisitNum: null,
copySourceVisitNum: null,
selectIndex: 0,
indexA: null,
selectVisitKey: '',
type: null,
OtherInfo: {
IsSign: true,
},
JudgeDifferenceValueQRules: {
JudgeDifferenceType: [
{
required: true,
message: this.$t(
'trials:trials-list:setitng:JudgeDifferenceTypeRequired'
),
trigger: 'blur',
},
],
JudgeDifferenceValue: [
{
required: true,
message: this.$t(
'trials:trials-list:setitng:JudgeDifferenceValueRequired'
),
trigger: 'blur',
},
{
pattern: /^[0-9]+(.[0-9]{2})?$/,
message: this.$t('trials:trials-list:setitng:JudgeDifferenceValue'),
trigger: 'blur',
},
{
validator: (rule, value, callback) => {
if (value <= 0) {
callback(
new Error(
this.$t(
'trials:trials-list:setitng:JudgeDifferenceValueMin'
)
)
)
} else {
callback()
}
},
trigger: 'blur',
},
],
},
}
},
computed: {
questionList() {
return this.questionListMap[this.selectVisitKey]
},
copySourceVisitOptions() {
return this.visitPlanOptions.filter((visit) => visit.VisitNum !== this.copyTargetVisitNum)
}
},
watch: {
async activeVisitTab(v) {
if (!v) return
if (this.questionListMap[v]) {
return
}
await this.getList(v)
},
},
async mounted() {
await this.initData()
},
methods: {
async initData() {
this.questionListMap = {}
this.visitActiveNamesMap = {}
this.activeVisitTab = ''
await this.getVisitPlanOptions()
},
resetVisitJudgeTypeRelatedFields(question = {}) {
this.$set(question, 'AnswerGroup2List', [])
this.$set(question, 'AnswerGroupList', [])
this.$set(question, 'grouping', [])
this.$set(question, 'groupingA', [])
this.$set(question, 'groupingB', [])
this.$set(question, 'JudgeDifferenceValue', null)
this.$set(question, 'JudgeDifferenceType', 0)
},
normalizeQuestionList(questionList = []) {
return questionList.map((item) => ({
...item,
grouping: [],
groupingA: [],
groupingB: [],
AnswerGroupList: Array.isArray(item.AnswerCombination)
? item.AnswerCombination.map((group) => ({
...group,
AnswerGroupA: Array.isArray(group.AnswerGroupA)
? [...group.AnswerGroupA]
: group.AnswerGroupA,
AnswerGroupB: Array.isArray(group.AnswerGroupB)
? [...group.AnswerGroupB]
: group.AnswerGroupB
}))
: [],
AnswerGroup2List: Array.isArray(item.AnswerGroup)
? [...item.AnswerGroup]
: []
}))
},
closeCopyDialog() {
this.copyVisible = false
this.copyTargetVisitNum = null
this.copySourceVisitNum = null
},
async getVisitPlanOptions() {
try {
const trialId = this.$route.query.trialId
const res = await getTrialVisitStageSelect(trialId)
if (res.IsSuccess) {
this.visitPlanOptions = res.Result || []
if (!this.activeVisitTab && this.visitPlanOptions.length > 0) {
this.activeVisitTab = this.visitPlanOptions[0].Id
}
const outOfPlan = {
"Id": "00000000-0000-0000-0000-000000000000",
"VisitNum": -1,
"VisitName": "Out of Plan",
"IsBaseLine": false
}
const isExists = this.visitPlanOptions.some(x => x.Id === outOfPlan.Id);
if (!isExists) {
this.visitPlanOptions.push(outOfPlan);
}
}
} catch (e) {
console.log(e)
}
},
getVisitById(visitKey) {
return this.visitPlanOptions.find((visit) => visit.Id === visitKey)
},
async getList(visitKey = this.activeVisitTab) {
const visit = this.getVisitById(visitKey)
if (!visit) return
try {
this.loading = true
let params = {
trialId: this.$route.query.trialId,
trialReadingCriterionId: this.trialReadingCriterionId,
visitNum: visit.VisitNum
}
const res = await getTrialQuestionVisitJudgeRule(params)
const questionList = this.normalizeQuestionList(res.Result)
this.$set(this.questionListMap, visitKey, questionList)
this.$set(
this.visitActiveNamesMap,
visitKey,
questionList.map((item) => item.ReadingQuestionTrialId)
)
} catch (e) {
console.log(e)
} finally {
this.loading = false
}
},
changeArbitrationRule(value) {
if (value !== 1) {
Object.values(this.questionListMap).forEach((questionList) => {
questionList.forEach((item) => {
item.JudgeDifferenceValue = 0
item.JudgeDifferenceType = 0
})
})
}
},
visitJudgeTypeChange(value, index, visitKey) {
this.resetVisitJudgeTypeRelatedFields(this.questionListMap[visitKey][index])
this.$nextTick(() => {
if (value === 4 || value === 5) {
const refName = `JudgeDifferenceValue${value}_${visitKey}_${index}`
if (this.$refs[refName] && this.$refs[refName][0]) {
this.$refs[refName][0].resetFields()
}
}
})
},
// async saveAllSync(showMessage = false) {
// try {
// this.btnLoading = true
// this.loading = true
// for (let i = 0; i < this.visitPlanOptions.length; i++) {
// const visit = this.visitPlanOptions[i]
// if (!this.questionListMap[visit.Id]) {
// await this.getList(visit.Id)
// }
// const questionList = this.questionListMap[visit.Id] || []
// if (questionList.length === 0) {
// continue
// }
// for (let index = 0; index < questionList.length; index++) {
// if (questionList[index].JudgeType === 0) {
// this.activeVisitTab = visit.Id
// this.$alert(this.$t('trials:adRules:message:msg8'))
// return false
// }
// if (
// questionList[index].JudgeType === 2 &&
// questionList[index].AnswerGroup2List.length === 0
// ) {
// this.activeVisitTab = visit.Id
// this.$alert(this.$t('trials:adRules:message:msg8'))
// return false
// }
// if (
// questionList[index].JudgeType === 3 &&
// questionList[index].AnswerGroupList.length === 0
// ) {
// this.activeVisitTab = visit.Id
// this.$alert(this.$t('trials:adRules:message:msg8'))
// return false
// }
// if (
// questionList[index].JudgeType === 4 ||
// questionList[index].JudgeType === 5
// ) {
// this.activeVisitTab = visit.Id
// await this.$nextTick()
// const refName = `JudgeDifferenceValue${questionList[index].JudgeType}_${visit.Id}_${index}`
// try {
// const validate = await this.$refs[refName][0].validate()
// if (!validate) return false
// } catch (err) {
// return false
// }
// }
// }
// let params = {
// TrialReadingCriterionId: this.trialReadingCriterionId,
// VisitNum: visit.VisitNum,
// QuestionList: questionList.map((item) => {
// return {
// ReadingQuestionTrialId: item.ReadingQuestionTrialId,
// AnswerGroup: item.AnswerGroup2List,
// AnswerCombination: item.AnswerGroupList,
// JudgeType: item.JudgeType,
// JudgeDifferenceValue: item.JudgeDifferenceValue,
// JudgeDifferenceType: item.JudgeDifferenceType
// }
// })
// }
// let res = await setTrialQuestionVisitJudgeRule(params)
// if (!res.IsSuccess) {
// return false
// }
// }
// if (showMessage) {
// this.$message.success(this.$t('common:message:savedSuccessfully'))
// }
// return true
// } catch (e) {
// console.log(e)
// return false
// } finally {
// this.btnLoading = false
// this.loading = false
// }
// },
async handleBatchSave(visitKey, visitNum) {
if (!visitKey) return
const questionList = this.questionListMap[visitKey] || []
if (questionList.length === 0) return
try {
this.btnLoading = true
this.loading = true
for (let index = 0; index < questionList.length; index++) {
if (questionList[index].JudgeType === 0) {
this.$alert(this.$t('trials:adRules:message:msg8'))
return
}
if (
questionList[index].JudgeType === 2 &&
questionList[index].AnswerGroup2List.length === 0
) {
this.$alert(this.$t('trials:adRules:message:msg8'))
return
}
if (
questionList[index].JudgeType === 3 &&
questionList[index].AnswerGroupList.length === 0
) {
this.$alert(this.$t('trials:adRules:message:msg8'))
return
}
if (
questionList[index].JudgeType === 4 ||
questionList[index].JudgeType === 5
) {
await this.$nextTick()
const refName = `JudgeDifferenceValue${questionList[index].JudgeType}_${visitKey}_${index}`
try {
const validate = await this.$refs[refName][0].validate()
if (!validate) return
} catch (err) {
return
}
}
}
let params = {
TrialReadingCriterionId: this.trialReadingCriterionId,
VisitNum: visitNum,
QuestionList: questionList.map((item) => {
return {
ReadingQuestionTrialId: item.ReadingQuestionTrialId,
AnswerGroup: item.AnswerGroup2List,
AnswerCombination: item.AnswerGroupList,
JudgeType: item.JudgeType,
JudgeDifferenceValue: item.JudgeDifferenceValue,
JudgeDifferenceType: item.JudgeDifferenceType
}
})
}
let res = await setTrialQuestionVisitJudgeRule(params)
if (res.IsSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
} catch (e) {
console.log(e)
} finally {
this.btnLoading = false
this.loading = false
}
},
save() {
const questionList = this.questionList
let index = this.selectIndex
let indexA = this.indexA
if (questionList[index].groupingA.length === 0) {
this.$alert(this.$t('trials:adRules:message:msg2'))
return
}
if (questionList[index].groupingB.length === 0) {
this.$alert(this.$t('trials:adRules:message:msg3'))
return
}
if (this.type === 'add') {
questionList[index].AnswerGroupList.push({
AnswerGroupA: questionList[index].groupingA,
AnswerGroupB: questionList[index].groupingB
})
} else {
this.$set(
questionList[index].AnswerGroupList[indexA],
'AnswerGroupA',
questionList[index].groupingA
)
this.$set(
questionList[index].AnswerGroupList[indexA],
'AnswerGroupB',
questionList[index].groupingB
)
}
this.$set(questionList[index], 'groupingA', [])
this.$set(questionList[index], 'groupingB', [])
this.$message.success(this.$t('trials:adRules:message:msg4'))
this.questionVisible = false
},
handleCopy(visitNum) {
this.copyTargetVisitNum = visitNum
this.copyVisible = true
},
async confirmCopy() {
if (!this.copySourceVisitNum) {
this.$alert(this.$t('trials:setting:reading-unit:msg1'))
return
}
try {
this.btnLoading = true
this.loading = true
const params = {
trialReadingCriterionId: this.trialReadingCriterionId,
sourceVisitNum: this.copySourceVisitNum,
targetVisitNum: this.copyTargetVisitNum
}
const res = await copyTrialQuestionVisitJudgeRule(params)
if (res.IsSuccess) {
await this.getList(this.activeVisitTab)
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.closeCopyDialog()
}
} catch (e) {
console.log(e)
} finally {
this.btnLoading = false
this.loading = false
}
},
async applyToFollowup() {
try {
await this.$confirm(
this.$t('trials:adRules:message:msg10'),
{
type: 'warning',
distinguishCancelAndClose: true,
}
)
this.btnLoading = true
this.loading = true
const res = await applyBaseLineTrialQuestionVisitJudgeRule({
trialReadingCriterionId: this.trialReadingCriterionId
})
if (res.IsSuccess) {
this.questionListMap = {}
this.visitActiveNamesMap = {}
if (this.activeVisitTab) {
await this.getList(this.activeVisitTab)
}
this.$message.success(this.$t('trials:adRules:message:msg9'))
}
} catch (e) {
if (e === 'cancel') return
console.log(e)
} finally {
this.btnLoading = false
this.loading = false
}
},
visitTagClose2(index, indexA, visitKey) {
this.$confirm(this.$t('trials:adRules:message:msg1')).then(() => {
this.questionListMap[visitKey][index].AnswerGroup2List.splice(indexA, 1)
})
},
visitTagClose(index, indexA, visitKey) {
this.$confirm(this.$t('trials:adRules:message:msg1')).then(() => {
this.questionListMap[visitKey][index].AnswerGroupList.splice(indexA, 1)
})
},
visitAddGroup2(index, visitKey) {
const questionList = this.questionListMap[visitKey]
if (questionList[index].grouping.length === 0) {
this.$alert(this.$t('trials:adRules:message:msg5'))
return
}
let grouping = questionList[index].grouping
.toString()
.replaceAll(',', '|')
questionList[index].AnswerGroup2List.push(`|${grouping}|`)
this.$set(questionList[index], 'grouping', [])
},
visitAddGroup(index, indexA, visitKey) {
const questionList = this.questionListMap[visitKey]
this.selectIndex = index
this.selectVisitKey = visitKey
this.type = 'add'
if (indexA !== null) {
this.indexA = indexA
this.type = 'edit'
this.$set(
questionList[index],
'groupingA',
questionList[index].AnswerGroupList[indexA].AnswerGroupA
)
this.$set(
questionList[index],
'groupingB',
questionList[index].AnswerGroupList[indexA].AnswerGroupB
)
}
this.questionVisible = true
},
async visitReset(ReadingQuestionTrialId, index, visitKey, visitNum) {
const questionList = this.questionListMap[visitKey]
this.btnLoading = true
this.loading = true
try {
await this.$confirm(this.$t('trials:adRules:message:msg6'))
const params = {
TrialReadingCriterionId: this.trialReadingCriterionId,
VisitNum: visitNum,
QuestionList: [
{
ReadingQuestionTrialId: ReadingQuestionTrialId,
AnswerGroup: [],
AnswerCombination: [],
JudgeType: 0,
JudgeDifferenceValue: 0,
JudgeDifferenceType: 0
}
]
}
await setTrialQuestionVisitJudgeRule(params)
this.$set(questionList[index], 'AnswerGroup2List', [])
this.$set(questionList[index], 'AnswerGroupList', [])
this.$set(questionList[index], 'JudgeType', 0)
this.$set(questionList[index], 'JudgeDifferenceValue', 0)
this.$set(questionList[index], 'JudgeDifferenceType', 0)
this.$message.success(this.$t('trials:adRules:message:msg7'))
} catch(e) {
console.log(e)
} finally {
this.btnLoading = false
this.loading = false
}
},
async visitApply(ReadingQuestionTrialId, index, visitKey, visitNum) {
try {
const questionList = this.questionListMap[visitKey]
if (questionList[index].JudgeType === 0) {
this.$alert(this.$t('trials:adRules:message:msg8'))
return
}
if (
questionList[index].JudgeType === 2 &&
questionList[index].AnswerGroup2List.length === 0
) {
this.$alert(this.$t('trials:adRules:message:msg8'))
return
}
if (
questionList[index].JudgeType === 3 &&
questionList[index].AnswerGroupList.length === 0
) {
this.$alert(this.$t('trials:adRules:message:msg8'))
return
}
if (
questionList[index].JudgeType === 4 ||
questionList[index].JudgeType === 5
) {
const refName = `JudgeDifferenceValue${questionList[index].JudgeType}_${visitKey}_${index}`
const validate = await this.$refs[refName][0].validate()
if (!validate) return
}
this.btnLoading = true
this.loading = true
let params = {
TrialReadingCriterionId: this.trialReadingCriterionId,
VisitNum: visitNum,
QuestionList: [
{
ReadingQuestionTrialId: ReadingQuestionTrialId,
AnswerGroup: questionList[index].AnswerGroup2List,
AnswerCombination: questionList[index].AnswerGroupList,
JudgeType: questionList[index].JudgeType,
JudgeDifferenceValue: questionList[index].JudgeDifferenceValue,
JudgeDifferenceType: questionList[index].JudgeDifferenceType
}
]
}
let res = await setTrialQuestionVisitJudgeRule(params)
if (res.IsSuccess) {
this.$message.success(this.$t('trials:adRules:message:msg9'))
}
} catch(e) {
console.log(e)
} finally {
this.btnLoading = false
this.loading = false
}
}
},
}
</script>
<style scoped>
.visit-tabs-actions {
display: flex;
justify-content: flex-end;
margin-bottom: 10px;
padding: 0 10px;
}
.visit-tabs ::v-deep .el-tabs__content {
min-height: 520px;
padding-top: 20px !important;
}
</style>
@@ -222,6 +222,9 @@
form.IseCRFShowInDicomReading = false
} else {
form.ReadingTaskViewEnum = 2
form.IsReadingShowSubjectInfo = false
form.IsReadingShowPreviousResults = false
form.IseCRFShowInDicomReading = true
}
if (CriterionType !== 10 && (v === 0 || v === 2)) {
form.IsReadingPeriod = false
@@ -279,7 +282,7 @@
" prop="IsReadingShowSubjectInfo">
<el-radio-group v-model="form.IsReadingShowSubjectInfo" :disabled="isConfirm ||
!hasPermi(['trials:trials-panel:setting:reading-unit:edit']) ||
!!form.IsReadingTaskViewInOrder
form.IsReadingTaskViewInOrder >= 0
">
<el-radio v-for="item of $d.YesOrNo" :key="'IsReadingShowSubjectInfo' + item.value" :label="item.value">
{{ item.label }}
@@ -293,7 +296,7 @@
" prop="IsReadingShowPreviousResults">
<el-radio-group v-model="form.IsReadingShowPreviousResults" :disabled="isConfirm ||
!hasPermi(['trials:trials-panel:setting:reading-unit:edit']) ||
!!form.IsReadingTaskViewInOrder
form.IsReadingTaskViewInOrder >= 0
" @change="
(v) => {
if (!v) {
@@ -240,7 +240,6 @@ export default {
if (ReadingTool === 3) {
this.readingSegmentTools = getCustomizeStandardsSegmentDicomTools(readingSegmentTools)
}
console.log(this.readingSegmentTools, 'this.readingSegmentTools')
},
setIsClinicalReading(isClinicalReading) {
this.isClinicalReading = isClinicalReading;
@@ -300,11 +299,15 @@ export default {
this.$refs["arbitrationRules" + this.TrialReadingCriterionId]
.length
) {
var arbitrationRules = await this.$refs[
const arbitrationRulesRef = this.$refs[
"arbitrationRules" + this.TrialReadingCriterionId
][0].saveAllSync();
][0];
var judgeRuleSaved = await arbitrationRulesRef.saveJugeRule(false);
var arbitrationRules = judgeRuleSaved
? await arbitrationRulesRef.saveAllSync()
: false;
isCheckList.push({
isCheck: arbitrationRules,
isCheck: judgeRuleSaved && arbitrationRules,
msg: this.$t("trials:readingUnit:adRules"), // ''
});
}
@@ -448,9 +451,6 @@ export default {
this.$refs[
"arbitrationRules" + this.TrialReadingCriterionId
][0].getList();
this.$refs[
"arbitrationRules" + this.TrialReadingCriterionId
][0].getTrialJudgyInfo();
}
},
},
@@ -13,10 +13,13 @@
</el-form-item>
</el-form>
<el-table :data="tableList" border style="width: 100%">
<el-table-column v-for="item in tableKey" :key="item.key" :prop="item.key" :label="item.title">
<el-table-column v-for="(item, index) in tableKey" :key="item.key" :prop="item.key" :label="item.title">
<template slot-scope="scope">
<span :class="{ IsHighlight: scope.row.IsHighlight.includes(item.key) }">{{ scope.row[item.key]
}}</span>
<el-button icon="el-icon-view" circle v-if="index > 0 && scope.row[item.key]" size="mini"
style="margin-left: 5px;" :title="$t('common:button:view')"
@click="lookReadingResults(scope.row.VisitTaskId[item.key])"></el-button>
</template>
</el-table-column>
</el-table>
@@ -49,6 +52,7 @@ import {
} from '@/api/trials'
import * as echarts from 'echarts/core';
import { LineChart } from 'echarts/charts';
import { getToken } from '@/utils/auth'
import {
TitleComponent,
TooltipComponent,
@@ -108,13 +112,33 @@ export default {
tableKey: [],
tableList: [],
R1ChartList: [],
R2ChartList: []
R2ChartList: [],
openWindow: null
}
},
mounted() {
this.getTrialCriterionList()
},
methods: {
//
lookReadingResults(id) {
if (this.openWindow) {
this.openWindow.close()
}
var token = getToken()
var path = null
let trialCriterion = this.trialCriterionList.find(item => item.TrialReadingCriterionId === this.TrialReadingCriterionId)
let trialId = this.$route.query.trialId
if (trialCriterion.ReadingTool === 0 || trialCriterion.ReadingTool === 2 || trialCriterion.ReadingTool === 3) {
path = `/readingDicoms?TrialReadingCriterionId=${this.TrialReadingCriterionId}&trialId=${trialId}&subjectCode=${this.data.Code}&subjectId=${this.data.Id}&visitTaskId=${id}&isReadingTaskViewInOrder=${trialCriterion.IsReadingTaskViewInOrder}&criterionType=${trialCriterion.CriterionType}&readingTool=${trialCriterion.ReadingTool}&TokenKey=${token}`
} else {
path = `/noneDicomReading?TrialReadingCriterionId=${this.TrialReadingCriterionId}&trialId=${trialId}&subjectCode=${this.data.Code}&subjectId=${this.data.Id}&visitTaskId=${id}&isReadingTaskViewInOrder=${trialCriterion.IsReadingTaskViewInOrder}&criterionType=${trialCriterion.CriterionType}&readingTool=${trialCriterion.ReadingTool}&TokenKey=${token}`
}
// const routeData = this.$router.resolve({
// path: `/readingPage?subjectId=${row.SubjectId}&trialId=${row.TrialId}&visitTaskId=${row.Id}&TokenKey=${token}`
// })
this.openWindow = window.open(path, '_blank')
},
handleChange(v) {
this.getList()
},
@@ -159,12 +183,14 @@ export default {
this.tableList = []
if (Evaluation[1] && Evaluation[1].length > 0) {
let obj = {
IsHighlight: []
IsHighlight: [],
VisitTaskId: {}
}
this.tableKey.forEach((item, index) => {
if (index === 0) {
obj[item.key] = 'R1'
} else {
obj.VisitTaskId[item.key] = Evaluation[1][index - 1].VisitTaskId
obj[item.key] = Evaluation[1][index - 1].DictionaryCode ? this.$fd(Evaluation[1][index - 1].DictionaryCode, parseFloat(Evaluation[1][index - 1].Value)) : Evaluation[1][index - 1].Value
if (Evaluation[1][index - 1].IsHighlight) {
obj.IsHighlight.push(item.key)
@@ -176,13 +202,17 @@ export default {
}
if (Evaluation[2] && Evaluation[2].length > 0) {
let obj = {
IsHighlight: []
IsHighlight: [],
VisitTaskId: {}
}
this.tableKey.forEach((item, index) => {
obj[item.key] = index === 0 ? 'R2' : Evaluation[2][index - 1].DictionaryCode ? this.$fd(Evaluation[2][index - 1].DictionaryCode, parseFloat(Evaluation[2][index - 1].Value)) : Evaluation[2][index - 1].Value
if (index > 0 && Evaluation[2][index - 1].IsHighlight) {
obj.IsHighlight.push(item.key)
}
if (index > 0) {
obj.VisitTaskId[item.key] = Evaluation[2][index - 1].VisitTaskId
}
})
this.tableList.push(obj)
}
@@ -331,7 +361,7 @@ export default {
}
.IsHighlight {
color: red;
color: #00f;
}
.chartBox {
@@ -9,10 +9,10 @@
</el-radio-group>
</el-form-item>
<!-- Out Visit Date -->
<el-form-item v-if="form.Status == 2" :label="$t('trials:subject:table:outVisitDate')" prop="VisitOverTime">
<!-- <el-form-item v-if="form.Status == 2" :label="$t('trials:subject:table:outVisitDate')" prop="VisitOverTime">
<el-date-picker v-model="form.VisitOverTime" type="date" :picker-options="pickerOption"
value-format="yyyy-MM-dd" format="yyyy-MM-dd" />
</el-form-item>
</el-form-item> -->
<!-- 末次访视 -->
<el-form-item v-if="form.Status === 2" :label="$t('trials:subject:table:finalSubjectVisit')"
prop="FinalSubjectVisitId">
@@ -21,14 +21,43 @@
<el-option v-for="item in subjectVisitOptions" :key="item.Id" :label="item.VisitName" :value="item.Id" />
</el-select>
</el-form-item>
<!-- 访视中原因-->
<el-form-item
v-if="(form.Status < data.Status && form.Status === 1) || (form.BackReason)"
:label="$t('trials:subject:table:outVisitReason')"
prop="BackReason"
:rules="[
{ required: this.form.Status < this.data.Status, message: this.$t('common:ruleMessage:specify'), trigger: ['blur'] },
{ max: 500, message: `${this.$t('common:ruleMessage:maxLength')} 500`, trigger: ['blur'] }
]"
>
<el-input v-model="form.BackReason" type="textarea" autosize />
</el-form-item>
<!-- 结束访视的原因 -->
<el-form-item v-if="form.Status === 2" :label="$t('trials:subject:table:outVisitReason')" prop="Reason">
<el-form-item
v-if="form.Status === 2"
:label="$t('trials:subject:table:outVisitReason')"
prop="Reason"
:rules="[
{ required: this.form.Status < this.data.Status, message: this.$t('common:ruleMessage:specify'), trigger: ['blur'] },
{ max: 500, message: `${this.$t('common:ruleMessage:maxLength')} 500`, trigger: ['blur'] }
]"
>
<el-input v-model="form.Reason" type="textarea" autosize />
</el-form-item>
<!-- 访视中止的原因 -->
<el-form-item v-if="form.Status === 3" :label="$t('trials:subject:table:outVisitReason')" prop="SuspendReason">
<el-form-item
v-if="form.Status === 3"
:label="$t('trials:subject:table:outVisitReason')"
prop="SuspendReason"
:rules="[
{ required: this.form.Status < this.data.Status, message: this.$t('common:ruleMessage:specify'), trigger: ['blur'] },
{ max: 500, message: `${this.$t('common:ruleMessage:maxLength')} 500`, trigger: ['blur'] }
]"
>
<el-input v-model="form.SuspendReason" type="textarea" autosize />
</el-form-item>
</div>
<div class="base-dialog-footer" style="text-align:right;margin-top:10px;">
<el-form-item style="text-align:right;">
@@ -61,16 +90,16 @@ export default {
Status: 1,
OutEnrollmentTime: '',
Reason: '',
VisitOverTime: '',
// VisitOverTime: '',
FinalSubjectVisitId: '',
SuspendReason: ''
SuspendReason: '',
BackReason: ''
},
rules: {
OutEnrollmentTime: [{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur'] }],
FinalSubjectVisitId: [{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur'] }],
Reason: [{ max: 500, message: `${this.$t('common:ruleMessage:maxLength')} 500` }],
SuspendReason: [{ max: 500, message: `${this.$t('common:ruleMessage:maxLength')} 500` }],
VisitOverTime: [{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur'] }]
// SuspendReason: [{ max: 500, message: `${this.$t('common:ruleMessage:maxLength')} 500` }],
// VisitOverTime: [{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur'] }]
},
pickerOption: {
disabledDate: time => {
@@ -91,6 +120,8 @@ export default {
methods: {
handleChange() {
if (this.form.Status !== 3) this.form.SuspendReason = ''
if (this.form.Status !== 2) this.form.Reason = ''
if (this.form.Status !== 1) this.form.BackReason = ''
},
async initForm() {
this.loading = true
@@ -116,7 +147,7 @@ export default {
this.form.TrialId = this.trialId
this.form.TrialSiteId = this.data.TrialSiteId
if (this.form.Status !== 2) {
this.form.VisitOverTime = ''
// this.form.VisitOverTime = ''
this.form.FinalSubjectVisitId = ''
}
updateSubjectStatus(this.form).then(res => {
@@ -644,7 +644,7 @@
<el-input v-model="studyForm.Modalities" disabled />
</el-form-item>
<!-- 检查类型 -->
<el-form-item v-else :label="$t('trials:audit:table:modality')" prop="Modalities" :rules="[
<el-form-item v-else :label="$t('trials:audit:table:modality')" prop="Modality" :rules="[
{
required: true,
message: $t('common:ruleMessage:specify'),
+8 -1
View File
@@ -135,11 +135,18 @@ module.exports = defineConfig({
test: /\.wasm$/,
type: 'asset/resource'
}
]
],
exprContextCritical: false,
},
optimization: {
splitChunks: {
chunks: 'all',
minSize: 30000, //字节 引入的文件大于30kb才进行分割
maxSize: 5000000, //50kb,尝试将大于50kb的文件拆分成n个50kb的文件
minChunks: 1, //模块至少使用次数
maxAsyncRequests: 30, //同时加载的模块数量最多是5个,只分割出同时引入的前5个文件
maxInitialRequests: 30, //首页加载的时候引入的文件最多3个
automaticNameDelimiter: '_', //缓存组和生成文件名称之间的连接符
cacheGroups: {
libs: { // 第三方库
name: 'chunk-libs',