Compare commits

..

292 Commits

Author SHA1 Message Date
DESKTOP-6C3NK6N\WXS 83a14956e3 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is failing Details
2024-10-08 13:15:54 +08:00
DESKTOP-6C3NK6N\WXS ef2e7d4cf8 上传记录列表查询字段变更 2024-10-08 13:15:49 +08:00
caiyiling 9e7ec09719 测量值与单位间添加空格
continuous-integration/drone/push Build is running Details
2024-10-08 13:12:50 +08:00
DESKTOP-6C3NK6N\WXS 19f9c8012c Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing Details
2024-09-30 13:43:09 +08:00
DESKTOP-6C3NK6N\WXS 506307690d 新增部位去掉前后空格 2024-09-30 13:42:54 +08:00
caiyiling b945db8a47 oct和ivus标准更改
continuous-integration/drone/push Build is passing Details
2024-09-30 12:08:33 +08:00
DESKTOP-6C3NK6N\WXS 7bb0213a8c Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing Details
2024-09-30 10:22:00 +08:00
DESKTOP-6C3NK6N\WXS a75367480d 后处理上传添加限制 2024-09-30 10:21:47 +08:00
caiyiling 64fd549d12 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2024-09-29 17:20:03 +08:00
caiyiling f0ec643205 dicom阅片更改 2024-09-29 17:17:47 +08:00
DESKTOP-6C3NK6N\WXS 34c05a9728 版本信息修改
continuous-integration/drone/push Build is passing Details
2024-09-29 14:04:12 +08:00
DESKTOP-6C3NK6N\WXS 49241bacfa 反馈图片名称问题
continuous-integration/drone/push Build is passing Details
2024-09-29 10:56:51 +08:00
DESKTOP-6C3NK6N\WXS 3bb0250699 apm意见反馈详情权限
continuous-integration/drone/push Build is passing Details
2024-09-29 10:38:50 +08:00
DESKTOP-6C3NK6N\WXS 47dd21ea7e 权限配置
continuous-integration/drone/push Build is passing Details
2024-09-29 10:25:19 +08:00
DESKTOP-6C3NK6N\WXS 64d268b6e8 后处理影像可以编辑检查类型
continuous-integration/drone/push Build is passing Details
2024-09-29 09:17:07 +08:00
caiyiling 463ecee3d8 下载影像预览后处理影像更改
continuous-integration/drone/push Build is passing Details
2024-09-27 17:00:42 +08:00
DESKTOP-6C3NK6N\WXS 9936d8c942 1
continuous-integration/drone/push Build is passing Details
2024-09-27 16:55:11 +08:00
caiyiling 1e132e0046 1
continuous-integration/drone/push Build is passing Details
2024-09-27 16:39:29 +08:00
caiyiling 95097caac2 1
continuous-integration/drone/push Build is passing Details
2024-09-27 16:27:43 +08:00
DESKTOP-6C3NK6N\WXS dcde93b4f8 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is running Details
2024-09-27 16:23:55 +08:00
DESKTOP-6C3NK6N\WXS e690aadd8a 1 2024-09-27 16:23:42 +08:00
caiyiling 7874f69bfe 预览影像更改
continuous-integration/drone/push Build is running Details
2024-09-27 16:23:19 +08:00
caiyiling bdb45c7b4e 1
continuous-integration/drone/push Build is passing Details
2024-09-27 16:16:18 +08:00
DESKTOP-6C3NK6N\WXS 0a4683386c 1
continuous-integration/drone/push Build is passing Details
2024-09-27 16:08:25 +08:00
DESKTOP-6C3NK6N\WXS c73297003f 1
continuous-integration/drone/push Build is passing Details
2024-09-27 14:48:07 +08:00
caiyiling 4faff7e85c 1
continuous-integration/drone/push Build is running Details
2024-09-27 14:43:03 +08:00
caiyiling 595e10a77a 系统标准配置更改及查看后处理影像修改
continuous-integration/drone/push Build is passing Details
2024-09-27 10:01:17 +08:00
caiyiling 360826ef28 1
continuous-integration/drone/push Build is passing Details
2024-09-26 17:21:56 +08:00
caiyiling cd7d261adf 阅片样式调整
continuous-integration/drone/push Build is passing Details
2024-09-26 16:54:55 +08:00
caiyiling 3ed09d16f6 自定义阅片样式更改
continuous-integration/drone/push Build is passing Details
2024-09-26 15:21:43 +08:00
caiyiling 2a016cf5f6 自定义标准阅片更改
continuous-integration/drone/push Build is passing Details
2024-09-26 15:12:28 +08:00
caiyiling 2d996941a2 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2024-09-26 14:49:41 +08:00
caiyiling 2ac92aa8b3 裁判阅片更改 2024-09-26 14:49:25 +08:00
DESKTOP-6C3NK6N\WXS e8a21fa5a3 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing Details
2024-09-26 14:33:14 +08:00
DESKTOP-6C3NK6N\WXS 771554c9b2 标准配置列表新增标准类型字段 2024-09-26 14:33:08 +08:00
caiyiling 3b7271d3a8 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2024-09-26 14:02:34 +08:00
caiyiling d2ab510a3c 裁判阅片更改 2024-09-26 14:02:20 +08:00
DESKTOP-6C3NK6N\WXS 7e8af2b4e5 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing Details
2024-09-26 13:43:09 +08:00
DESKTOP-6C3NK6N\WXS f9f3c55ea1 新增权限配置 2024-09-26 13:42:57 +08:00
caiyiling 988317d0cd 系统标准添加报告页显示在阅片页配置
continuous-integration/drone/push Build is running Details
2024-09-26 13:37:25 +08:00
DESKTOP-6C3NK6N\WXS a7c7c3dfc5 自动分类可以选择是否为裁判问题
continuous-integration/drone/push Build is passing Details
2024-09-26 11:26:01 +08:00
DESKTOP-6C3NK6N\WXS 17d6e233c8 非dicom文件预览展示大小
continuous-integration/drone/push Build is running Details
2024-09-26 11:21:43 +08:00
DESKTOP-6C3NK6N\WXS c9a754a85f 非dicom文件上传限制改为2GB
continuous-integration/drone/push Build is passing Details
2024-09-26 10:57:30 +08:00
DESKTOP-6C3NK6N\WXS a1addde722 crc上传问题及ir下载subjectId和code获取
continuous-integration/drone/push Build is passing Details
2024-09-26 10:43:00 +08:00
DESKTOP-6C3NK6N\WXS 001faf1f79 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing Details
2024-09-26 10:14:01 +08:00
DESKTOP-6C3NK6N\WXS 8d26368bcb 接收记录总共用时字段更改 2024-09-26 10:13:52 +08:00
caiyiling 3c8ea04457 测量值列表去除序号
continuous-integration/drone/push Build is passing Details
2024-09-25 17:30:05 +08:00
caiyiling 112c78a832 1
continuous-integration/drone/push Build is passing Details
2024-09-25 17:10:22 +08:00
caiyiling 590949e207 1
continuous-integration/drone/push Build is passing Details
2024-09-25 15:45:34 +08:00
caiyiling 8a47f23fe7 OCT标准更改
continuous-integration/drone/push Build is passing Details
2024-09-25 15:29:18 +08:00
caiyiling 2093308763 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2024-09-25 13:36:22 +08:00
caiyiling 8fae8528c6 OCT标准更改 2024-09-25 13:36:00 +08:00
DESKTOP-6C3NK6N\WXS 3d5229d3c0 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing Details
2024-09-24 17:03:29 +08:00
DESKTOP-6C3NK6N\WXS 386db94fdb 上传页面提示语根据阅片方式进行区分 2024-09-24 17:03:16 +08:00
caiyiling f80bd64ede Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2024-09-24 16:36:41 +08:00
caiyiling 339be24cc3 阅片检查渲染初始化更改 2024-09-24 16:36:05 +08:00
DESKTOP-6C3NK6N\WXS d30953bb98 阅片页面上传数据后页面刷新
continuous-integration/drone/push Build is passing Details
2024-09-24 16:28:31 +08:00
DESKTOP-6C3NK6N\WXS dc60a1ca4a Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing Details
2024-09-24 14:02:17 +08:00
DESKTOP-6C3NK6N\WXS 2d673a414b 上传具体任务时检验绑定任务id 2024-09-24 14:02:09 +08:00
caiyiling 06c56889d0 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2024-09-24 13:28:16 +08:00
caiyiling 68179f4214 ivus测量值添加导入数据功能 2024-09-24 13:27:47 +08:00
DESKTOP-6C3NK6N\WXS 2b5ee56e6b 上传列表不同阅片规则根据任务id过滤
continuous-integration/drone/push Build is passing Details
2024-09-24 11:02:01 +08:00
DESKTOP-6C3NK6N\WXS 3d2f6d0c7e Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing Details
2024-09-24 10:09:39 +08:00
DESKTOP-6C3NK6N\WXS 9752fd6946 阅片单元配置修改 2024-09-24 10:09:26 +08:00
caiyiling 0dcee2c84b Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2024-09-23 16:48:51 +08:00
caiyiling 43f49a36c0 ivus添加下载模板及oct标准更改 2024-09-23 16:47:57 +08:00
DESKTOP-6C3NK6N\WXS 16d5299ce0 账号设置密码修改样式调整
continuous-integration/drone/push Build is running Details
2024-09-23 16:45:55 +08:00
DESKTOP-6C3NK6N\WXS 16485f0c56 系统文档签署提交参数不传入项目id
continuous-integration/drone/push Build is passing Details
2024-09-23 15:49:24 +08:00
DESKTOP-6C3NK6N\WXS 692fd7bc97 版本管理新增LILI版本
continuous-integration/drone/push Build is passing Details
2024-09-23 15:17:16 +08:00
DESKTOP-6C3NK6N\WXS f17f1d6a6b 忘记密码校验
continuous-integration/drone/push Build is passing Details
2024-09-23 15:02:32 +08:00
DESKTOP-6C3NK6N\WXS b74337f2f0 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing Details
2024-09-23 11:39:42 +08:00
DESKTOP-6C3NK6N\WXS 37e65339f3 文件签署提交参数判断 2024-09-23 11:39:29 +08:00
caiyiling b4d4fffd71 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2024-09-23 11:30:35 +08:00
caiyiling f175838052 ivus标准更改 2024-09-23 11:30:15 +08:00
DESKTOP-6C3NK6N\WXS 80aa18eff2 文件签署提交删除参数
continuous-integration/drone/push Build is passing Details
2024-09-23 11:18:13 +08:00
DESKTOP-6C3NK6N\WXS 966e90498d 个人中心修改按钮校验
continuous-integration/drone/push Build is passing Details
2024-09-23 10:16:21 +08:00
DESKTOP-6C3NK6N\WXS ca1b95201c 个人中心账号设置添加校验
continuous-integration/drone/push Build is passing Details
2024-09-23 10:03:23 +08:00
DESKTOP-6C3NK6N\WXS f2e6ecce8e Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing Details
2024-09-20 17:02:17 +08:00
DESKTOP-6C3NK6N\WXS 3b06046c69 校验规则修改 2024-09-20 17:02:02 +08:00
caiyiling d67bc1e02d Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2024-09-20 16:46:13 +08:00
caiyiling 3c35d2656d 1 2024-09-20 16:46:00 +08:00
DESKTOP-6C3NK6N\WXS d6048e4cdd Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is running Details
2024-09-20 16:45:29 +08:00
DESKTOP-6C3NK6N\WXS d80fdb36e1 监控页面调整 2024-09-20 16:45:16 +08:00
caiyiling 40642a4fe6 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2024-09-20 16:21:15 +08:00
caiyiling e550e5e31c 中心调研表更改 2024-09-20 16:20:51 +08:00
DESKTOP-6C3NK6N\WXS 5bf4c3cf5d 提示语遮挡
continuous-integration/drone/push Build is passing Details
2024-09-20 15:41:03 +08:00
DESKTOP-6C3NK6N\WXS d86eee6825 初始化用户页面表格调整
continuous-integration/drone/push Build is passing Details
2024-09-20 15:16:08 +08:00
DESKTOP-6C3NK6N\WXS 76a70e819d Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is running Details
2024-09-20 15:12:24 +08:00
DESKTOP-6C3NK6N\WXS a512ed551b 中心ae列表权限 2024-09-20 15:12:06 +08:00
caiyiling c5383dd11d Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2024-09-20 15:07:29 +08:00
caiyiling 23378a663b ivus标准更改 2024-09-20 15:07:11 +08:00
DESKTOP-6C3NK6N\WXS 693e240578 敏感数据加密
continuous-integration/drone/push Build is passing Details
2024-09-20 10:15:21 +08:00
DESKTOP-6C3NK6N\WXS 0144ae91e8 加秘测试
continuous-integration/drone/push Build is passing Details
2024-09-20 09:53:33 +08:00
DESKTOP-6C3NK6N\WXS 449e999d43 加密测试
continuous-integration/drone/push Build is passing Details
2024-09-19 17:34:15 +08:00
DESKTOP-6C3NK6N\WXS c77ca02cbc 1
continuous-integration/drone/push Build is passing Details
2024-09-19 17:27:31 +08:00
DESKTOP-6C3NK6N\WXS 8300cbaccf 1
continuous-integration/drone/push Build is passing Details
2024-09-19 16:49:20 +08:00
DESKTOP-6C3NK6N\WXS 14b0a45817 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing Details
2024-09-19 16:17:17 +08:00
DESKTOP-6C3NK6N\WXS b3ba7e7e40 敏感信息加密 2024-09-19 16:17:01 +08:00
caiyiling c348260b45 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2024-09-19 15:40:28 +08:00
caiyiling 4fbd4e28fe ivus标准更改及系统标准配置更改 2024-09-19 15:40:12 +08:00
DESKTOP-6C3NK6N\WXS d7b11c02e5 根据不同的阅片标准打包下载文件
continuous-integration/drone/push Build is passing Details
2024-09-19 10:20:19 +08:00
DESKTOP-6C3NK6N\WXS d0281abfee ir待阅列表受试者随机显示上传下载按钮
continuous-integration/drone/push Build is passing Details
2024-09-18 16:41:37 +08:00
DESKTOP-6C3NK6N\WXS 2c268cbd4f 公司信息改为后端配置
continuous-integration/drone/push Build is running Details
2024-09-18 16:38:09 +08:00
DESKTOP-6C3NK6N\WXS 857abb2c0d aws上传路径变更
continuous-integration/drone/push Build is passing Details
2024-09-18 13:51:03 +08:00
DESKTOP-6C3NK6N\WXS 371a3acff2 入项资格确认书的pdf预览方式调整
continuous-integration/drone/push Build is passing Details
2024-09-18 13:28:27 +08:00
DESKTOP-6C3NK6N\WXS 3dddbafd04 非dicom影像图片上传后文件数量预览按钮位置对齐
continuous-integration/drone/push Build is passing Details
2024-09-18 11:48:37 +08:00
DESKTOP-6C3NK6N\WXS 86dde0a9e8 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is running Details
2024-09-18 11:44:41 +08:00
DESKTOP-6C3NK6N\WXS aee2a78205 长时间未操作锁定弹框密码添加展示按钮 2024-09-18 11:44:26 +08:00
caiyiling 4733c0fd8f Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2024-09-18 11:30:06 +08:00
caiyiling 54b3e0399c 系统标准问题类型扩展 2024-09-18 11:29:39 +08:00
DESKTOP-6C3NK6N\WXS 8f6434f123 接收影像检查列表排序部分字段去除
continuous-integration/drone/push Build is passing Details
2024-09-18 11:17:04 +08:00
DESKTOP-6C3NK6N\WXS c25183073a 忘记密码国际化换行
continuous-integration/drone/push Build is passing Details
2024-09-18 11:04:07 +08:00
DESKTOP-6C3NK6N\WXS 904a764ea5 上传显示问题修改
continuous-integration/drone/push Build is passing Details
2024-09-14 17:59:13 +08:00
DESKTOP-6C3NK6N\WXS 248f40b5be 接收记录排序更改
continuous-integration/drone/push Build is passing Details
2024-09-14 16:27:18 +08:00
DESKTOP-6C3NK6N\WXS c50c6c7bc7 1
continuous-integration/drone/push Build is passing Details
2024-09-14 16:13:37 +08:00
DESKTOP-6C3NK6N\WXS 1ef8eed500 独立阅片人上传限制
continuous-integration/drone/push Build is running Details
2024-09-14 16:12:59 +08:00
DESKTOP-6C3NK6N\WXS 9310337f16 反馈字段兼容
continuous-integration/drone/push Build is passing Details
2024-09-14 11:36:20 +08:00
caiyiling 37529cef33 非Dicom图片预览更改
continuous-integration/drone/push Build is passing Details
2024-09-14 10:39:33 +08:00
DESKTOP-6C3NK6N\WXS 42ed756c4e 上传临床数据按钮修改
continuous-integration/drone/push Build is passing Details
2024-09-14 10:26:12 +08:00
DESKTOP-6C3NK6N\WXS 97d786684b ir上传国际化修改
continuous-integration/drone/push Build is passing Details
2024-09-14 09:56:32 +08:00
DESKTOP-6C3NK6N\WXS 17f0a5d977 下载记录查询条件新增
continuous-integration/drone/push Build is running Details
2024-09-14 09:54:06 +08:00
DESKTOP-6C3NK6N\WXS 75a063acd9 路由白名单添加、iqc下载提示消息权限
continuous-integration/drone/push Build is passing Details
2024-09-13 17:39:04 +08:00
caiyiling 5df08f52fc Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2024-09-13 15:04:32 +08:00
caiyiling 3a5de3fadf 下拉框添加清除按钮 2024-09-13 15:04:04 +08:00
DESKTOP-6C3NK6N\WXS be32fd8e15 临时凭证获取
continuous-integration/drone/push Build is passing Details
2024-09-13 13:06:38 +08:00
caiyiling 8047ba7f5f Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2024-09-13 10:37:24 +08:00
caiyiling fbbd4ff805 1 2024-09-13 10:37:16 +08:00
DESKTOP-6C3NK6N\WXS 25d8ee6bc5 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is failing Details
2024-09-13 10:23:37 +08:00
DESKTOP-6C3NK6N\WXS 0f29a2971c 临时凭证获取 2024-09-13 10:23:24 +08:00
caiyiling 2b71341d4c 部分文档不显示水印修复
continuous-integration/drone/push Build is running Details
2024-09-13 10:22:24 +08:00
DESKTOP-6C3NK6N\WXS fa395f54b2 公司水印
continuous-integration/drone/push Build is running Details
2024-09-13 10:19:31 +08:00
DESKTOP-6C3NK6N\WXS 70d9623c63 从PACS选择影像页,外层页面没有拖拽条
continuous-integration/drone/push Build is passing Details
2024-09-13 09:22:30 +08:00
DESKTOP-6C3NK6N\WXS 82447c58de 独立阅片人上传影像页面国际化问题
continuous-integration/drone/push Build is running Details
2024-09-13 09:20:25 +08:00
DESKTOP-6C3NK6N\WXS 672cb9b2a2 国际化修改
continuous-integration/drone/push Build is passing Details
2024-09-12 17:40:15 +08:00
DESKTOP-6C3NK6N\WXS b06dcdc98f 仲裁规则显示
continuous-integration/drone/push Build is passing Details
2024-09-12 15:24:38 +08:00
DESKTOP-6C3NK6N\WXS b1096a8aa9 测试代码去除
continuous-integration/drone/push Build is passing Details
2024-09-12 15:11:31 +08:00
DESKTOP-6C3NK6N\WXS 66a1654bfe ir下载预览非dicom影像问题
continuous-integration/drone/push Build is passing Details
2024-09-12 14:39:28 +08:00
DESKTOP-6C3NK6N\WXS 986bce36e6 上传、下载添加提示;非dicom添加上传监控
continuous-integration/drone/push Build is passing Details
2024-09-12 14:22:52 +08:00
DESKTOP-6C3NK6N\WXS e845fdaad4 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing Details
# Conflicts:
#	src/views/trials/trials-panel/visit/qc-check/components/qualityAssurance.vue
2024-09-12 10:48:25 +08:00
DESKTOP-6C3NK6N\WXS b762d3d0dd 下载记录 2024-09-12 10:46:33 +08:00
caiyiling dfb9392cda Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2024-09-11 17:02:30 +08:00
caiyiling 86c05070b3 1 2024-09-11 17:02:17 +08:00
he b8478e6451 Merge branch 'main' of http://192.168.3.68:2000/XCKJ/irc_web
continuous-integration/drone/push Build is running Details
2024-09-11 17:00:56 +08:00
he 4552caf82f 后端测试 加了一个接口备注 vue.config 加了一个接口备注 2024-09-11 17:00:48 +08:00
caiyiling 9e83d51037 一致性分析上传模板更改
continuous-integration/drone/push Build is passing Details
2024-09-11 16:22:12 +08:00
caiyiling dd0cd1ec64 稽查更改
continuous-integration/drone/push Build is passing Details
2024-09-11 16:07:28 +08:00
caiyiling db5e5da06a 稽查渲染bug修复
continuous-integration/drone/push Build is passing Details
2024-09-11 14:36:45 +08:00
caiyiling e6bd4576e6 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2024-09-11 13:04:25 +08:00
caiyiling 8abcfcbe1a qc质控文本格式更改 2024-09-11 13:03:47 +08:00
DESKTOP-6C3NK6N\WXS 9653e4478b 医学审核下载
continuous-integration/drone/push Build is passing Details
2024-09-10 18:32:46 +08:00
caiyiling 9402501c0b Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is running Details
2024-09-10 18:29:37 +08:00
caiyiling d5c0e15f46 阅片页面下载影像更改 2024-09-10 18:29:22 +08:00
DESKTOP-6C3NK6N\WXS 860d932a5d Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing Details
2024-09-10 16:18:08 +08:00
DESKTOP-6C3NK6N\WXS 0e485a8984 新开页签跳转问题 2024-09-10 16:17:53 +08:00
caiyiling 95998f0dea 阅片页面渲染影像更改
continuous-integration/drone/push Build is passing Details
2024-09-10 16:06:32 +08:00
caiyiling 8c7c82cf5e Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is failing Details
2024-09-10 15:24:20 +08:00
caiyiling bf701bf3d8 质控页面回复内容格式化 2024-09-10 15:23:56 +08:00
DESKTOP-6C3NK6N\WXS 2ce7454b47 ir上传问题解决
continuous-integration/drone/push Build is running Details
2024-09-10 15:21:01 +08:00
caiyiling ce079f923a 国际化补充
continuous-integration/drone/push Build is failing Details
2024-09-10 14:18:11 +08:00
caiyiling 60d3b25041 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is running Details
2024-09-10 14:13:17 +08:00
caiyiling 3d171caf1b dicom字典更改 2024-09-10 14:12:59 +08:00
DESKTOP-6C3NK6N\WXS 472599c663 上传检验提示信息拆分
continuous-integration/drone/push Build is running Details
2024-09-10 11:06:29 +08:00
caiyiling 3a659cd9bc Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2024-09-10 10:59:22 +08:00
caiyiling ed7d2502d1 稽查渲染问题修复 2024-09-10 10:59:02 +08:00
DESKTOP-6C3NK6N\WXS 689c149a3a Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing Details
2024-09-10 10:30:50 +08:00
DESKTOP-6C3NK6N\WXS 5b3ae0c076 仲裁规则添加单位 2024-09-10 10:30:36 +08:00
caiyiling 4eba3b2cb3 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is running Details
2024-09-10 10:25:42 +08:00
caiyiling e2ebedb9e9 系统标准器官配置更改 2024-09-10 10:25:17 +08:00
DESKTOP-6C3NK6N\WXS 77c3f0244e 下载问题解决
continuous-integration/drone/push Build is passing Details
2024-09-09 17:47:57 +08:00
DESKTOP-6C3NK6N\WXS 3fc7d70b3f 1
continuous-integration/drone/push Build is passing Details
2024-09-09 17:15:04 +08:00
DESKTOP-6C3NK6N\WXS c2aa83f4b8 ir上传文件初始化问题
continuous-integration/drone/push Build is passing Details
2024-09-09 16:55:33 +08:00
DESKTOP-6C3NK6N\WXS 7c77a42e90 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing Details
2024-09-09 16:31:10 +08:00
DESKTOP-6C3NK6N\WXS 08b8f8d107 ir下载打包文件目录修改 2024-09-09 16:31:04 +08:00
caiyiling 1fca941f64 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2024-09-09 16:05:38 +08:00
caiyiling d06afd42af 1 2024-09-09 16:05:21 +08:00
DESKTOP-6C3NK6N\WXS cbee69b4c0 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing Details
2024-09-09 15:48:06 +08:00
DESKTOP-6C3NK6N\WXS 39c4a13f5e 数据上传模板添加字段文件名称(CN) 2024-09-09 15:47:53 +08:00
caiyiling 7fed65dbe3 数值问题格式化更改
continuous-integration/drone/push Build is passing Details
2024-09-09 15:31:01 +08:00
DESKTOP-6C3NK6N\WXS 15cad74264 ir上传预览添加参数
continuous-integration/drone/push Build is passing Details
2024-09-09 15:21:04 +08:00
caiyiling b1c8f02271 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is running Details
2024-09-09 15:16:42 +08:00
caiyiling b95cc82997 1 2024-09-09 15:16:24 +08:00
DESKTOP-6C3NK6N\WXS 59c342ccd1 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is running Details
2024-09-09 15:15:12 +08:00
DESKTOP-6C3NK6N\WXS eba09135eb ir下载目录结构变更 2024-09-09 15:14:43 +08:00
caiyiling 4f4934523f Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2024-09-09 15:00:19 +08:00
caiyiling 99bb929123 阅片更改 2024-09-09 14:59:57 +08:00
DESKTOP-6C3NK6N\WXS 1538690604 下载改为全局loading
continuous-integration/drone/push Build is running Details
2024-09-09 14:57:52 +08:00
DESKTOP-6C3NK6N\WXS bfb1c03c63 1
continuous-integration/drone/push Build is passing Details
2024-09-09 13:56:29 +08:00
DESKTOP-6C3NK6N\WXS e05986ac9f 上传速率显示位置调整
continuous-integration/drone/push Build is passing Details
2024-09-09 13:45:06 +08:00
DESKTOP-6C3NK6N\WXS 4988155499 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing Details
2024-09-09 13:39:13 +08:00
DESKTOP-6C3NK6N\WXS e024c6c0b1 上传、下载检查详情受试者id未展示 2024-09-09 13:39:00 +08:00
caiyiling aa526aad8c Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is running Details
2024-09-09 13:36:12 +08:00
caiyiling 84bc101065 测量值精度更改 2024-09-09 13:35:56 +08:00
DESKTOP-6C3NK6N\WXS adf6bdc44a 阅片影像筛选配置:按钮名称修改
continuous-integration/drone/push Build is passing Details
2024-09-09 13:29:44 +08:00
caiyiling ec7851eb9d Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2024-09-09 13:22:06 +08:00
caiyiling 8831847850 OCT标准更改 2024-09-09 13:21:48 +08:00
DESKTOP-6C3NK6N\WXS fa137ac535 仲裁规则修改
continuous-integration/drone/push Build is passing Details
2024-09-09 11:41:00 +08:00
DESKTOP-6C3NK6N\WXS 1209a3962b 下载跨域问题解决
continuous-integration/drone/push Build is passing Details
2024-09-09 11:08:44 +08:00
DESKTOP-6C3NK6N\WXS ccf3b823d8 非dicom文件名生成guid
continuous-integration/drone/push Build is passing Details
2024-09-06 20:23:19 +08:00
DESKTOP-6C3NK6N\WXS e043e85630 上传名称格式化问题
continuous-integration/drone/push Build is passing Details
2024-09-06 20:09:26 +08:00
caiyiling 342c9325bf 上传影像bug修复
continuous-integration/drone/push Build is passing Details
2024-09-06 18:26:42 +08:00
caiyiling 249448b15e Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2024-09-06 18:01:42 +08:00
caiyiling 4569f16050 IVUS自动计算更改 2024-09-06 18:01:21 +08:00
DESKTOP-6C3NK6N\WXS 07b2341f93 ir任务列表下载权限问题
continuous-integration/drone/push Build is passing Details
2024-09-06 17:54:34 +08:00
DESKTOP-6C3NK6N\WXS e47c75937c 上传速率位置更改
continuous-integration/drone/push Build is passing Details
2024-09-06 17:41:40 +08:00
caiyiling f39050e220 数值组件精度修改
continuous-integration/drone/push Build is running Details
2024-09-06 17:38:47 +08:00
caiyiling f31e23d314 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2024-09-06 17:28:49 +08:00
caiyiling 8b12d6a576 IVUS标准更改 2024-09-06 17:28:15 +08:00
DESKTOP-6C3NK6N\WXS 605bf04e43 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing Details
2024-09-06 17:18:01 +08:00
DESKTOP-6C3NK6N\WXS f380028c22 样式修改 2024-09-06 17:17:21 +08:00
caiyiling 9a040024d9 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2024-09-06 16:55:40 +08:00
caiyiling 04f68d8733 IVUS标准更改 2024-09-06 16:55:17 +08:00
DESKTOP-6C3NK6N\WXS b62a36b63a 裁判阅片待阅列表不显示上传下载按钮
continuous-integration/drone/push Build is passing Details
2024-09-06 16:05:57 +08:00
caiyiling 3dfe5b57a1 阅片页面上传影像按钮控制
continuous-integration/drone/push Build is running Details
2024-09-06 16:04:21 +08:00
DESKTOP-6C3NK6N\WXS 6a3068f942 系统标准添加上传下载
continuous-integration/drone/push Build is passing Details
2024-09-06 15:54:17 +08:00
caiyiling f58fa6011b Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2024-09-06 15:37:11 +08:00
caiyiling d42252ea78 IVUS标准更改 2024-09-06 15:36:53 +08:00
DESKTOP-6C3NK6N\WXS 6bf4a1b70d 已完成阅片的阅片页不允许上传
continuous-integration/drone/push Build is running Details
2024-09-06 15:33:40 +08:00
DESKTOP-6C3NK6N\WXS 5bed446055 系统标准数值问题可设为裁判问题
continuous-integration/drone/push Build is passing Details
2024-09-06 15:13:46 +08:00
DESKTOP-6C3NK6N\WXS 5d8c389090 阅片单元配置:增加阅片过程下载影像、阅片过程上传影像不区分阅片标准
continuous-integration/drone/push Build is passing Details
2024-09-06 14:52:28 +08:00
DESKTOP-6C3NK6N\WXS 5bfc4a9a82 上传锁定问题
continuous-integration/drone/push Build is passing Details
2024-09-06 14:41:34 +08:00
DESKTOP-6C3NK6N\WXS cb00ec7ac2 ir下载预览
continuous-integration/drone/push Build is passing Details
2024-09-06 14:26:17 +08:00
DESKTOP-6C3NK6N\WXS dc5c1720c7 CRC上传影像前端页面优化
continuous-integration/drone/push Build is passing Details
2024-09-06 14:10:57 +08:00
DESKTOP-6C3NK6N\WXS 73a4135fd6 重置密码页面,标签换行
continuous-integration/drone/push Build is passing Details
2024-09-06 14:04:00 +08:00
DESKTOP-6C3NK6N\WXS f2af36b89a 临床数据上传页面,按钮对齐
continuous-integration/drone/push Build is passing Details
2024-09-06 13:57:00 +08:00
DESKTOP-6C3NK6N\WXS 4f0f124378 网速监控大小写转换
continuous-integration/drone/push Build is passing Details
2024-09-06 13:36:25 +08:00
DESKTOP-6C3NK6N\WXS c618738725 上传下载问题修复
continuous-integration/drone/push Build is passing Details
2024-09-06 13:22:42 +08:00
caiyiling 1274af3e48 影像质量评估设置为不正常重置病灶不可评估过滤标准
continuous-integration/drone/push Build is passing Details
2024-09-06 11:36:58 +08:00
caiyiling b7a19c2581 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2024-09-06 11:19:28 +08:00
caiyiling 842e959e2d 系统标准更改 2024-09-06 11:19:06 +08:00
DESKTOP-6C3NK6N\WXS 0590742dac ir上传数据删除
continuous-integration/drone/push Build is passing Details
2024-09-06 11:09:14 +08:00
DESKTOP-6C3NK6N\WXS f6b90c4c05 ir上传dicom校验
continuous-integration/drone/push Build is passing Details
2024-09-06 11:03:05 +08:00
DESKTOP-6C3NK6N\WXS e52378d5d4 ir上传dicom
continuous-integration/drone/push Build is passing Details
2024-09-05 18:01:05 +08:00
DESKTOP-6C3NK6N\WXS f52b2f151b ir上传dicom
continuous-integration/drone/push Build is passing Details
2024-09-05 17:17:47 +08:00
caiyiling 9bb856b50a 系统标准更改
continuous-integration/drone/push Build is passing Details
2024-09-05 16:17:57 +08:00
caiyiling 780c49959e Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2024-09-05 16:12:05 +08:00
caiyiling a470d961df ivus标准更改 2024-09-05 16:11:42 +08:00
DESKTOP-6C3NK6N\WXS 84b1a2a4fa Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is failing Details
2024-09-05 13:55:28 +08:00
DESKTOP-6C3NK6N\WXS e2026177a1 ir任务列表非dicom上传 2024-09-05 13:55:09 +08:00
caiyiling c114903dba 1
continuous-integration/drone/push Build is passing Details
2024-09-05 13:50:49 +08:00
caiyiling 915c7c88fb Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2024-09-05 13:39:14 +08:00
caiyiling 64bd3033b5 非dicom文件预览样式更改 2024-09-05 13:38:57 +08:00
DESKTOP-6C3NK6N\WXS c1fbbc524f 非dicom文件名有特殊符号,预览无法正常显示
continuous-integration/drone/push Build is passing Details
2024-09-05 13:15:07 +08:00
caiyiling 6f86700dbc lugano关闭跨源隔离检测
continuous-integration/drone/push Build is passing Details
2024-09-05 11:49:37 +08:00
caiyiling 85ed9fd9b7 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is running Details
2024-09-05 11:48:14 +08:00
caiyiling 002cd19053 1 2024-09-05 11:48:04 +08:00
DESKTOP-6C3NK6N\WXS e24b1cc722 ir上传
continuous-integration/drone/push Build is passing Details
2024-09-05 10:44:32 +08:00
DESKTOP-6C3NK6N\WXS 205b420074 1
continuous-integration/drone/push Build is passing Details
2024-09-05 09:09:13 +08:00
caiyiling d42e446953 1
continuous-integration/drone/push Build is passing Details
2024-09-04 18:01:22 +08:00
caiyiling 3be34d89ca 1
continuous-integration/drone/push Build is running Details
2024-09-04 17:57:48 +08:00
caiyiling 67b04c2a51 1
continuous-integration/drone/push Build is passing Details
2024-09-04 17:24:24 +08:00
caiyiling 65acb7ea05 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2024-09-04 16:53:13 +08:00
caiyiling d25fd68db7 1 2024-09-04 16:53:03 +08:00
DESKTOP-6C3NK6N\WXS 433064c353 下载问题
continuous-integration/drone/push Build is passing Details
2024-09-04 16:32:54 +08:00
DESKTOP-6C3NK6N\WXS 0e766300f0 1
continuous-integration/drone/push Build is passing Details
2024-09-04 15:04:56 +08:00
DESKTOP-6C3NK6N\WXS 581980f111 config测试
continuous-integration/drone/push Build is passing Details
2024-09-04 14:57:07 +08:00
DESKTOP-6C3NK6N\WXS 7aaede5503 aws分片上传
continuous-integration/drone/push Build is passing Details
2024-09-04 14:50:17 +08:00
caiyiling ff7305c3a0 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2024-09-04 14:29:33 +08:00
caiyiling 6f03bbe14c 阅片跟踪影像退回逻辑限制 2024-09-04 14:28:49 +08:00
DESKTOP-6C3NK6N\WXS 6b645d3161 临床数据不显示
continuous-integration/drone/push Build is passing Details
2024-09-04 13:50:44 +08:00
caiyiling 4db4aed4dd 医学审核问题配置优化
continuous-integration/drone/push Build is passing Details
2024-09-04 13:15:44 +08:00
caiyiling b368df6bfb 关键序列中多帧影像只显示带有标记的帧
continuous-integration/drone/push Build is passing Details
2024-09-04 11:38:48 +08:00
DESKTOP-6C3NK6N\WXS 7aa21c5ca1 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing Details
2024-09-04 11:09:45 +08:00
DESKTOP-6C3NK6N\WXS 45c4bc3b3a 标准对象定义 2024-09-04 11:09:29 +08:00
caiyiling 5de6a69bc8 1
continuous-integration/drone/push Build is passing Details
2024-09-04 10:24:44 +08:00
caiyiling 324bc1a536 稽查更改
continuous-integration/drone/push Build is passing Details
2024-09-04 10:14:14 +08:00
caiyiling 2628d2ec5f 1
continuous-integration/drone/push Build is passing Details
2024-09-04 09:39:05 +08:00
caiyiling 9d8bd9c910 新增稽查配置项
continuous-integration/drone/push Build is running Details
2024-09-04 09:36:56 +08:00
caiyiling 615c91b197 非dicom报告页数值类型问题精度bug修复
continuous-integration/drone/push Build is running Details
2024-09-04 09:32:31 +08:00
DESKTOP-6C3NK6N\WXS a7c0ae6d7a Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing Details
2024-09-04 09:10:05 +08:00
DESKTOP-6C3NK6N\WXS 400b13e7a6 ir阅片下载 2024-09-04 09:09:56 +08:00
caiyiling a14f144066 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2024-09-03 18:05:11 +08:00
caiyiling 655753d1c6 数值问题更改 2024-09-03 18:04:59 +08:00
DESKTOP-6C3NK6N\WXS 64e936ad6d Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing Details
2024-09-03 17:48:31 +08:00
DESKTOP-6C3NK6N\WXS 6712cad721 ir任务列表下载 2024-09-03 17:48:24 +08:00
caiyiling fed2c1a842 非DICOM预览文件样式更改
continuous-integration/drone/push Build is passing Details
2024-09-03 16:15:23 +08:00
caiyiling 2303c04857 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2024-09-03 15:32:41 +08:00
caiyiling beeadfbdeb 非dicom阅片添加报告页面 2024-09-03 15:32:00 +08:00
DESKTOP-6C3NK6N\WXS b866ffdd49 iqc下载文件
continuous-integration/drone/push Build is passing Details
2024-09-03 15:23:22 +08:00
DESKTOP-6C3NK6N\WXS 8ad50b5618 上传网速检测修改 2024-09-03 15:22:44 +08:00
DESKTOP-6C3NK6N\WXS e517556808 crc上传dicom添加FileSize
continuous-integration/drone/push Build is passing Details
2024-09-03 14:12:39 +08:00
DESKTOP-6C3NK6N\WXS 1a8653b47e crc上传下载
continuous-integration/drone/push Build is passing Details
2024-09-03 13:15:50 +08:00
caiyiling bfe86e6868 项目列表详情按钮添加权限控制
continuous-integration/drone/push Build is running Details
2024-09-03 13:11:44 +08:00
caiyiling f0eda2bc66 预览图片修改
continuous-integration/drone/push Build is passing Details
2024-09-02 14:12:26 +08:00
caiyiling 8f274aad89 获取tag属性乱码优化
continuous-integration/drone/push Build is passing Details
2024-08-30 11:10:19 +08:00
caiyiling 709063a26e 在影像上传预览和质控预览工具中,增加DICOM标签查看工具
continuous-integration/drone/push Build is passing Details
2024-08-30 10:25:32 +08:00
caiyiling 9e786c53b8 1
continuous-integration/drone/push Build is passing Details
2024-08-28 15:46:59 +08:00
caiyiling 37520fccb6 稽查更改
continuous-integration/drone/push Build is passing Details
2024-08-28 15:35:19 +08:00
caiyiling f3b36b9127 稽查修改
continuous-integration/drone/push Build is passing Details
2024-08-28 15:06:15 +08:00
caiyiling 870354b4b2 稽查配置更改
continuous-integration/drone/push Build is passing Details
2024-08-28 11:40:18 +08:00
caiyiling f838df51a5 阅片单元配置附加评估逻辑及阅片问题验证更改
continuous-integration/drone/push Build is passing Details
2024-08-28 10:21:21 +08:00
caiyiling 0a6a98c33c 稽查国际化更改
continuous-integration/drone/push Build is passing Details
2024-08-27 10:21:59 +08:00
caiyiling 28c357a702 稽查配置更改
continuous-integration/drone/push Build is passing Details
2024-08-27 10:11:00 +08:00
150 changed files with 21782 additions and 6516 deletions

View File

@ -27,11 +27,13 @@
"axios": "0.18.1",
"babel-eslint": "7.2.3",
"copy-webpack-plugin": "^4.5.2",
"@aws-sdk/client-s3": "^3.370.0",
"core-js": "^3.8.3",
"cornerstone-core": "^2.6.1",
"cornerstone-math": "^0.1.10",
"cornerstone-tools": "^6.0.10",
"cornerstone-wado-image-loader": "^4.13.2",
"crypto-js": "^4.2.0",
"dcmjs": "^0.29.8",
"dicom-parser": "^1.8.9",
"dicomedit": "^0.1.0",
@ -42,6 +44,7 @@
"hammerjs": "^2.0.8",
"html2canvas": "^1.4.1",
"js-md5": "^0.7.3",
"jsencrypt": "^3.3.2",
"jszip": "^3.7.1",
"moment": "^2.27.0",
"node-polyfill-webpack-plugin": "^2.0.1",

167
public/mitm.html Normal file
View File

@ -0,0 +1,167 @@
<!--
mitm.html is the lite "man in the middle"
This is only meant to signal the opener's messageChannel to
the service worker - when that is done this mitm can be closed
but it's better to keep it alive since this also stops the sw
from restarting
The service worker is capable of intercepting all request and fork their
own "fake" response - wish we are going to craft
when the worker then receives a stream then the worker will tell the opener
to open up a link that will start the download
-->
<script>
// This will prevent the sw from restarting
let keepAlive = () => {
keepAlive = () => {}
var ping = location.href.substr(0, location.href.lastIndexOf('/')) + '/ping'
var interval = setInterval(() => {
if (sw) {
sw.postMessage('ping')
} else {
fetch(ping).then(res => res.text(!res.ok && clearInterval(interval)))
}
}, 10000)
}
// message event is the first thing we need to setup a listner for
// don't want the opener to do a random timeout - instead they can listen for
// the ready event
// but since we need to wait for the Service Worker registration, we store the
// message for later
let messages = []
window.onmessage = evt => messages.push(evt)
let sw = null
let scope = ''
function registerWorker() {
return navigator.serviceWorker.getRegistration('./').then(swReg => {
return swReg || navigator.serviceWorker.register('sw.js', { scope: './' })
}).then(swReg => {
const swRegTmp = swReg.installing || swReg.waiting
scope = swReg.scope
return (sw = swReg.active) || new Promise(resolve => {
swRegTmp.addEventListener('statechange', fn = () => {
if (swRegTmp.state === 'activated') {
swRegTmp.removeEventListener('statechange', fn)
sw = swReg.active
resolve()
}
})
})
})
}
// Now that we have the Service Worker registered we can process messages
function onMessage (event) {
let { data, ports, origin } = event
// It's important to have a messageChannel, don't want to interfere
// with other simultaneous downloads
if (!ports || !ports.length) {
throw new TypeError("[StreamSaver] You didn't send a messageChannel")
}
if (typeof data !== 'object') {
throw new TypeError("[StreamSaver] You didn't send a object")
}
// the default public service worker for StreamSaver is shared among others.
// so all download links needs to be prefixed to avoid any other conflict
data.origin = origin
// if we ever (in some feature versoin of streamsaver) would like to
// redirect back to the page of who initiated a http request
data.referrer = data.referrer || document.referrer || origin
// pass along version for possible backwards compatibility in sw.js
data.streamSaverVersion = new URLSearchParams(location.search).get('version')
if (data.streamSaverVersion === '1.2.0') {
console.warn('[StreamSaver] please update streamsaver')
}
/** @since v2.0.0 */
if (!data.headers) {
console.warn("[StreamSaver] pass `data.headers` that you would like to pass along to the service worker\nit should be a 2D array or a key/val object that fetch's Headers api accepts")
} else {
// test if it's correct
// should thorw a typeError if not
new Headers(data.headers)
}
/** @since v2.0.0 */
if (typeof data.filename === 'string') {
console.warn("[StreamSaver] You shouldn't send `data.filename` anymore. It should be included in the Content-Disposition header option")
// Do what File constructor do with fileNames
data.filename = data.filename.replace(/\//g, ':')
}
/** @since v2.0.0 */
if (data.size) {
console.warn("[StreamSaver] You shouldn't send `data.size` anymore. It should be included in the content-length header option")
}
/** @since v2.0.0 */
if (data.readableStream) {
console.warn("[StreamSaver] You should send the readableStream in the messageChannel, not throught mitm")
}
/** @since v2.0.0 */
if (!data.pathname) {
console.warn("[StreamSaver] Please send `data.pathname` (eg: /pictures/summer.jpg)")
data.pathname = Math.random().toString().slice(-6) + '/' + data.filename
}
// remove all leading slashes
data.pathname = data.pathname.replace(/^\/+/g, '')
// remove protocol
let org = origin.replace(/(^\w+:|^)\/\//, '')
// set the absolute pathname to the download url.
data.url = new URL(`${scope + org}/${data.pathname}`).toString()
if (!data.url.startsWith(`${scope + org}/`)) {
throw new TypeError('[StreamSaver] bad `data.pathname`')
}
// This sends the message data as well as transferring
// messageChannel.port2 to the service worker. The service worker can
// then use the transferred port to reply via postMessage(), which
// will in turn trigger the onmessage handler on messageChannel.port1.
const transferable = data.readableStream
? [ ports[0], data.readableStream ]
: [ ports[0] ]
if (!(data.readableStream || data.transferringReadable)) {
keepAlive()
}
return sw.postMessage(data, transferable)
}
if (window.opener) {
// The opener can't listen to onload event, so we need to help em out!
// (telling them that we are ready to accept postMessage's)
window.opener.postMessage('StreamSaver::loadedPopup', '*')
}
if (navigator.serviceWorker) {
registerWorker().then(() => {
window.onmessage = onMessage
messages.forEach(window.onmessage)
})
}
// FF v102 just started to supports transferable streams, but still needs to ping sw.js
// even tough the service worker dose not have to do any kind of work and listen to any
// messages... #305
keepAlive()
</script>

130
public/sw.js Normal file
View File

@ -0,0 +1,130 @@
/* global self ReadableStream Response */
self.addEventListener('install', () => {
self.skipWaiting()
})
self.addEventListener('activate', event => {
event.waitUntil(self.clients.claim())
})
const map = new Map()
// This should be called once per download
// Each event has a dataChannel that the data will be piped through
self.onmessage = event => {
// We send a heartbeat every x second to keep the
// service worker alive if a transferable stream is not sent
if (event.data === 'ping') {
return
}
const data = event.data
const downloadUrl = data.url || self.registration.scope + Math.random() + '/' + (typeof data === 'string' ? data : data.filename)
const port = event.ports[0]
const metadata = new Array(3) // [stream, data, port]
metadata[1] = data
metadata[2] = port
// Note to self:
// old streamsaver v1.2.0 might still use `readableStream`...
// but v2.0.0 will always transfer the stream through MessageChannel #94
if (event.data.readableStream) {
metadata[0] = event.data.readableStream
} else if (event.data.transferringReadable) {
port.onmessage = evt => {
port.onmessage = null
metadata[0] = evt.data.readableStream
}
} else {
metadata[0] = createStream(port)
}
map.set(downloadUrl, metadata)
port.postMessage({ download: downloadUrl })
}
function createStream (port) {
// ReadableStream is only supported by chrome 52
return new ReadableStream({
start (controller) {
// When we receive data on the messageChannel, we write
port.onmessage = ({ data }) => {
if (data === 'end') {
return controller.close()
}
if (data === 'abort') {
controller.error('Aborted the download')
return
}
controller.enqueue(data)
}
},
cancel (reason) {
console.log('user aborted', reason)
port.postMessage({ abort: true })
}
})
}
self.onfetch = event => {
const url = event.request.url
// this only works for Firefox
if (url.endsWith('/ping')) {
return event.respondWith(new Response('pong'))
}
const hijacke = map.get(url)
if (!hijacke) return null
const [ stream, data, port ] = hijacke
map.delete(url)
// Not comfortable letting any user control all headers
// so we only copy over the length & disposition
const responseHeaders = new Headers({
'Content-Type': 'application/octet-stream; charset=utf-8',
// To be on the safe side, The link can be opened in a iframe.
// but octet-stream should stop it.
'Content-Security-Policy': "default-src 'none'",
'X-Content-Security-Policy': "default-src 'none'",
'X-WebKit-CSP': "default-src 'none'",
'X-XSS-Protection': '1; mode=block',
'Cross-Origin-Embedder-Policy': 'require-corp'
})
let headers = new Headers(data.headers || {})
if (headers.has('Content-Length')) {
responseHeaders.set('Content-Length', headers.get('Content-Length'))
}
if (headers.has('Content-Disposition')) {
responseHeaders.set('Content-Disposition', headers.get('Content-Disposition'))
}
// data, data.filename and size should not be used anymore
if (data.size) {
console.warn('Depricated')
responseHeaders.set('Content-Length', data.size)
}
let fileName = typeof data === 'string' ? data : data.filename
if (fileName) {
console.warn('Depricated')
// Make filename RFC5987 compatible
fileName = encodeURIComponent(fileName).replace(/['()]/g, escape).replace(/\*/g, '%2A')
responseHeaders.set('Content-Disposition', "attachment; filename*=UTF-8''" + fileName)
}
event.respondWith(new Response(stream, { headers: responseHeaders }))
port.postMessage({ debug: 'Download started' })
}

View File

@ -8,11 +8,11 @@ export function requestPackageAndAnonymizImage(params) {
})
}
// 获取影像上传列表
export function getSubjectImageUploadList(params) {
export function getSubjectImageUploadList(data) {
return request({
url: '/DownloadAndUpload/getSubjectImageUploadList',
method: 'get',
params
method: 'post',
data
})
}
// 预上传
@ -39,4 +39,68 @@ export function deleteTaskStudy(params) {
method: 'delete',
params
})
}
// 获取iqc下载文件信息
export function getCRCUploadedStudyInfo(data) {
return request({
url: '/DownloadAndUpload/getCRCUploadedStudyInfo',
method: 'post',
data
})
}
// 获取ir阅片和任务列表
export function getSubjectImageDownloadSelectList(data) {
return request({
url: '/DownloadAndUpload/getSubjectImageDownloadSelectList',
method: 'post',
data
})
}
// 获取ir阅片和任务下载文件信息
export function getIRReadingDownloadStudyInfo(data) {
return request({
url: '/DownloadAndUpload/getIRReadingDownloadStudyInfo',
method: 'post',
data
})
}
// 获取ir阅片和任务非dicom上传列表
export function getIRUploadTaskNoneDicomStudyList(data) {
return request({
url: '/DownloadAndUpload/getIRUploadTaskNoneDicomStudyList',
method: 'post',
data
})
}
// 校验上传影像
export function verifyIRStudyAllowUpload(data) {
return request({
url: '/DownloadAndUpload/verifyIRStudyAllowUpload',
method: 'post',
data
})
}
// 影像下载成功确认
export function downloadImageSuccess(params) {
return request({
url: '/DownloadAndUpload/downloadImageSuccess',
method: 'post',
params
})
}
// 影像下载记录列表
export function getTrialDownloadList(data) {
return request({
url: '/DownloadAndUpload/getTrialDownloadList',
method: 'post',
data
})
}
// 修改后处理检查类型
export function updateTaskStudyModality(params) {
return request({
url: '/DownloadAndUpload/updateTaskStudyModality',
method: 'put',
params
})
}

View File

@ -1,5 +1,5 @@
import request from '@/utils/request'
import requestDownload from '@/utils/request-download'
export function addBaseLineLesion(param) {
return request({
url: '/report/addBaseLineLesion',
@ -133,9 +133,11 @@ export function AddAdjudicationReport(param) {
})
}
export function getVisitStudyList(trialId, subjectVisitId, isReading) {
export function getVisitStudyList(trialId, subjectVisitId, isReading, visitTaskId) {
let url = `/SubjectVisit/getVisitStudyList/${trialId}/${subjectVisitId}/${isReading}`
url = visitTaskId ? `${url}?visitTaskId=${visitTaskId}` : url
return request({
url: `/SubjectVisit/getVisitStudyList/${trialId}/${subjectVisitId}/${isReading}`,
url: url,
method: 'get'
})
}
@ -194,3 +196,58 @@ export function resetReadingTask(param) {
data: param
})
}
export function getTaskUploadedDicomStudyList(param) {
return request({
url: `/DownloadAndUpload/getTaskUploadedDicomStudyList`,
method: 'post',
data: param
})
}
export function getIVUSTemplate(param) {
return requestDownload({
url: '/IVUSCalculate/getIVUSTemplate',
method: 'post',
responseType: 'blob',
data: param
})
}
export function uploadIVUSTemplate(param) {
return request({
url: `/IVUSCalculate/uploadIVUSTemplate`,
method: 'post',
data: param
})
}
export function getOCTFCTTemplate(param) {
return requestDownload({
url: '/OCTCalculate/getOCTFCTTemplate',
method: 'post',
responseType: 'blob',
data: param
})
}
export function uploadOCTFCTTemplate(param) {
return request({
url: `/OCTCalculate/uploadOCTFCTTemplate`,
method: 'post',
data: param
})
}
export function getOCTLipidAngleTemplate(param) {
return requestDownload({
url: '/OCTCalculate/getOCTLipidAngleTemplate',
method: 'post',
responseType: 'blob',
data: param
})
}
export function uploadOCTLipidAngleTemplate(param) {
return request({
url: `/OCTCalculate/uploadOCTLipidAngleTemplate`,
method: 'post',
data: param
})
}

View File

@ -1364,9 +1364,9 @@ export function getForwardList(param) {
})
}
export function getNoneDicomStudyList(subjectVisitId, sudyId = '', isFilterZip = false) {
export function getNoneDicomStudyList(subjectVisitId, sudyId = '', isFilterZip = false, visitTaskId = '') {
return request({
url: `/NoneDicomStudy/getNoneDicomStudyList?subjectVisitId=${subjectVisitId}&nonedicomStudyId=${sudyId}&isFilterZip=${isFilterZip}`,
url: `/NoneDicomStudy/getNoneDicomStudyList?subjectVisitId=${subjectVisitId}&nonedicomStudyId=${sudyId}&isFilterZip=${isFilterZip}&visitTaskId=${visitTaskId}`,
method: 'get'
})
}

View File

@ -4,7 +4,8 @@ export function login(data) {
return request({
url: '/user/login',
method: 'post',
data
data,
ENCRYPT: true
})
}
export function loginOut(params) {
@ -185,3 +186,10 @@ export function sendMFAEmail(params) {
params
})
}
// 获取公钥
export function getPublicKey() {
return request({
url: `/user/getPublicKey`,
method: 'get',
})
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 KiB

After

Width:  |  Height:  |  Size: 61 KiB

View File

@ -81,16 +81,25 @@
<!-- <div v-show="stack.firstImageLoading" class="load-indicator">
Loading Series #{{ stack.seriesNumber }}...
</div>-->
<el-dialog
v-if="dcmTag.visible"
:visible.sync="dcmTag.visible"
:close-on-click-modal="false"
:title="dcmTag.title"
width="1000px"
custom-class="base-dialog-wrapper"
append-to-body
>
<DicomTags :image-id="stack.imageIds[stack.currentImageIdIndex]" @close="dcmTag.visible = false" />
</el-dialog>
</div>
</template>
<script>
import Vue from 'vue'
import Contextmenu from 'vue-contextmenujs'
Vue.use(Contextmenu)
import * as cornerstone from 'cornerstone-core'
import * as cornerstoneMath from 'cornerstone-math'
import * as cornerstoneTools from 'cornerstone-tools'
const scroll = cornerstoneTools.import('util/scrollToIndex')
import Hammer from 'hammerjs'
import getOrientationString from '@/views/trials/trials-panel/reading/dicoms/tools/OrientationMarkers/getOrientationString'
@ -108,8 +117,10 @@ cornerstoneTools.toolColors.setActiveColor('rgb(0, 255, 0)')
// cornerstoneTools.init({ showSVGCursors: true })
cornerstoneTools.init()
const ToolStateManager = cornerstoneTools.globalImageIdSpecificToolStateManager
import DicomTags from './DicomTags'
export default {
name: 'DicomCanvas',
components: { DicomTags },
data() {
return {
loading: false,
@ -164,7 +175,8 @@ export default {
mousePosition: { x: '', y: '', mo: '' },
markers: { top: '', right: '', bottom: '', left: '' },
orientationMarkers: [],
originalMarkers: []
originalMarkers: [],
dcmTag: { visible: false, title: this.$t('trials:dicom-tag:title') }
}
},
@ -215,7 +227,7 @@ export default {
cornerstoneTools.stopClip(this.canvas)
this.toolState.clipPlaying = false
this.loading = true
cornerstone.loadAndCacheImage(this.stack.imageIds[this.stack.currentImageIdIndex])
.then(image => {
this.loading = false
@ -364,8 +376,8 @@ export default {
if (this.dicomInfo.thick) {
this.dicomInfo.thick = this.dicomInfo.thick.toFixed(2)
}
let newImageIdIndex = this.stack.imageIds.findIndex(i=>i===e.detail.image.imageId)
if(newImageIdIndex === -1) return
const newImageIdIndex = this.stack.imageIds.findIndex(i => i === e.detail.image.imageId)
if (newImageIdIndex === -1) return
this.stack.currentImageIdIndex = newImageIdIndex
this.stack.imageIdIndex = newImageIdIndex
this.series.imageIdIndex = newImageIdIndex
@ -441,7 +453,7 @@ export default {
if (!markers) {
return
}
this.orientationMarkers = [oppositeColumn, row, column, oppositeRow]
this.originalMarkers = [oppositeColumn, row, column, oppositeRow]
this.setMarkers()
@ -636,7 +648,7 @@ export default {
enabledElement.renderingTools.renderCanvasData = renderCanvasData
},
scrollPage(offset) {
if(this.loading) return
if (this.loading) return
var index = this.stack.currentImageIdIndex + offset
if (index < 0) index = 0
else if (index >= this.stack.imageIds.length) {
@ -648,7 +660,7 @@ export default {
},
toggleClipPlay() {
if(this.loading) return
if (this.loading) return
if (this.toolState.clipPlaying) {
cornerstoneTools.stopClip(this.canvas)
this.toolState.clipPlaying = false
@ -668,7 +680,7 @@ export default {
resetWwwc() {
this.toolState.viewportInvert = false
var viewport = cornerstone.getViewport(this.canvas)
viewport.invert = false
// viewport.invert = false
var image = cornerstone.getImage(this.canvas)
viewport.voi.windowWidth = image.windowWidth
viewport.voi.windowCenter = image.windowCenter
@ -707,7 +719,7 @@ export default {
this.orientationMarkers = [...this.originalMarkers]
this.setMarkers()
}
var viewport = cornerstone.getViewport(this.canvas)
viewport.hflip = false
viewport.vflip = false
@ -747,6 +759,9 @@ export default {
var uid = cornerstone.getImage(this.canvas).data.string('x00080018')
cornerstoneTools.SaveAs(this.canvas, `${uid}.png`)
},
showTags() {
this.dcmTag.visible = true
},
fitToWindow() {
if (this.stack.seriesNumber) {
cornerstone.fitToWindow(this.canvas)

View File

@ -0,0 +1,287 @@
<template>
<div class="dcm-tag">
<el-input
v-model="search"
size="mini"
:placeholder="$t('trials:dicom-tag:keywords')"
style="width:200px"
/>
<el-table
:data="filterList(list)"
row-key="id"
default-expand-all
:tree-props="{children: 'child', hasChildren: 'hasChildren'}"
:default-sort="{prop: 'tagCode', order: 'ascending'}"
height="500"
>
<el-table-column
prop="tagCode"
label="Tag"
min-width="120"
sortable
/>
<el-table-column
prop="tagName"
label="Description"
min-width="150"
show-overflow-tooltip
sortable
/>
<el-table-column
prop="vr"
label="VR"
min-width="50"
show-overflow-tooltip
sortable
/>
<el-table-column
prop="tagLength"
label="Length"
min-width="80"
show-overflow-tooltip
sortable
/>
<el-table-column
prop="value"
label="Value"
min-width="200"
show-overflow-tooltip
sortable
/>
</el-table>
</div>
</template>
<script>
import TAG_DICT from './dataDictionary'
import * as cornerstone from 'cornerstone-core'
import dicomParser from 'dicom-parser'
export default {
name: 'DicomTags',
props: {
imageId: {
type: String,
required: true
}
},
data() {
return {
list: [],
idx: 0,
search: ''
}
},
async mounted() {
const image = await cornerstone.loadAndCacheImage(this.imageId)
var dataSet = dicomParser.parseDicom(image.data.byteArray)
var output = []
this.dumpDataSet(dataSet, output)
this.list = output
},
methods: {
filterList(list) {
if (list.length === 0) return []
if (!this.search) {
return list
} else {
const search = isNaN(parseFloat(this.search)) ? this.search.toLowerCase() : String(this.search)
const arr = list.filter(data => {
if (data.tagCode && data.tagCode.toLowerCase().includes(search)) {
return data
} else if (data.tagName && data.tagName.toLowerCase().includes(search)) {
return data
} else if (data.value) {
let v = ''
if (!isNaN(parseFloat(data.value))) {
v = String(data.value)
} else {
v = data.value.toLowerCase()
}
if (v.includes(search)) {
return data
}
}
})
return arr
}
},
dumpDataSet(dataSet, output) {
try {
for (const propertyName in dataSet.elements) {
const elementObject = {}
const element = dataSet.elements[propertyName]
const tag = this.getTag(element.tag)
elementObject.id = `${this.idx++}${new Date().getTime()}`
if (!tag) {
const group = element.tag.substring(1, 5)
const el = element.tag.substring(5, 9)
elementObject.tagCode = ('(' + group + ',' + el + ')').toUpperCase()
} else {
elementObject.tagCode = tag ? tag.tag : ''
elementObject.tagName = tag ? tag.name : ''
}
elementObject.tagLength = element.length
elementObject.value = ''
elementObject.child = []
if (element.items) {
element.items.forEach(item => {
const childOutput = []
this.dumpDataSet(item.dataSet, childOutput)
elementObject.child.push(...childOutput)
})
} else if (element.fragments) {
//
} else {
var vr
if (element.vr !== undefined) {
vr = element.vr
} else {
if (tag !== undefined) {
vr = tag.vr
}
}
elementObject.vr = vr
if (element.length < 128) {
// const str = dataSet.string(propertyName)
// if (elementObject.tagCode === 'x00280010') {
// console.log(str)
// }
// const stringIsAscii = this.isASCII(str)
// if (stringIsAscii && str !== undefined) {
// elementObject.value = str
// }
if (element.vr === undefined && tag === undefined) {
if (element.length === 2) {
elementObject.value = dataSet.uint16(propertyName)
} else if (element.length === 4) {
elementObject.value = dataSet.uint32(propertyName)
}
const str = dataSet.string(propertyName)
const stringIsAscii = this.isASCII(str)
if (stringIsAscii) {
if (str !== undefined) {
elementObject.value = str
}
} else {
if (element.length !== 2 && element.length !== 4) {
// elementObject.value = 'binary data'
}
}
} else {
if (this.isStringVr(vr)) {
const str = dataSet.string(propertyName)
const stringIsAscii = this.isASCII(str)
if (stringIsAscii) {
if (str !== undefined) {
elementObject.value = str
}
} else {
if (element.length !== 2 && element.length !== 4) {
// elementObject.value = 'binary data'
}
}
} else if (vr === 'US') {
let text = dataSet.uint16(propertyName)
for (let i = 1; i < dataSet.elements[propertyName].length / 2; i++) {
text += '\\' + dataSet.uint16(propertyName, i)
}
elementObject.value = text
} else if (vr === 'SS') {
let text = dataSet.int16(propertyName)
for (let i = 1; i < dataSet.elements[propertyName].length / 2; i++) {
text += '\\' + dataSet.int16(propertyName, i)
}
elementObject.value = text
} else if (vr === 'UL') {
let text = dataSet.uint32(propertyName)
for (let i = 1; i < dataSet.elements[propertyName].length / 4; i++) {
text += '\\' + dataSet.uint32(propertyName, i)
}
elementObject.value = text
} else if (vr === 'SL') {
let text = dataSet.int32(propertyName)
for (let i = 1; i < dataSet.elements[propertyName].length / 4; i++) {
text += '\\' + dataSet.int32(propertyName, i)
}
elementObject.value = text
} else if (vr === 'FD') {
let text = dataSet.double(propertyName)
for (let i = 1; i < dataSet.elements[propertyName].length / 8; i++) {
text += '\\' + dataSet.double(propertyName, i)
}
elementObject.value = text
} else if (vr === 'FL') {
let text = dataSet.float(propertyName)
for (let i = 1; i < dataSet.elements[propertyName].length / 4; i++) {
text += '\\' + dataSet.float(propertyName, i)
}
elementObject.value = text
} else if (vr === 'OB' || vr === 'OW' || vr === 'UN' || vr === 'OF' || vr === 'UT') {
if (element.length === 2) {
elementObject.value = dataSet.uint16(propertyName)
} else if (element.length === 4) {
elementObject.value = dataSet.uint32(propertyName)
} else {
}
} else if (vr === 'AT') {
// var group = dataSet.uint16(propertyName, 0);
// var groupHexStr = ("0000" + group.toString(16)).substr(-4);
// var element = dataSet.uint16(propertyName, 1);
// var elementHexStr = ("0000" + element.toString(16)).substr(-4);
// text += "x" + groupHexStr + elementHexStr;
} else if (vr === 'SQ') {
} else {
// no display code for VR yet, sorry!
}
}
}
}
output.push(elementObject)
}
} catch (err) {
const ex = {
exception: err,
output: output
}
console.log(ex)
}
},
getTag(tag) {
var group = tag.substring(1, 5)
var element = tag.substring(5, 9)
var tagIndex = ('(' + group + ',' + element + ')').toUpperCase()
var attr = TAG_DICT[tagIndex]
return attr
},
isASCII(str) {
return /^[\x00-\x7F]*$/.test(str)
},
isStringVr(vr) {
if (vr === 'AT' || vr === 'FL' || vr === 'FD' || vr === 'OB' || vr === 'OF' || vr === 'OW' || vr === 'SI' || vr === 'SQ' || vr === 'SS' || vr === 'UL' || vr === 'US') {
return false
}
return true
}
}
}
</script>
<style lang="scss" scoped>
.dcm-tag{
// user-select: none;
::-webkit-scrollbar {
width: 8px;
height: 15px;
}
::-webkit-scrollbar-thumb {
border-radius: 10px;
background: #d0d0d0;
}
}
</style>

View File

@ -182,7 +182,7 @@
<svg-icon icon-class="fitToImage" style="font-size:20px;" />
</button>
<!-- <button title="旋转" class="btn-link dropdown" data-tool="Rotate" @click="setToolActive($event,'Rotate')"> -->
</div>
</div>
<!-- 测量标注 -->
@ -233,6 +233,10 @@
<button :title="$t('trials:dicom-show:image')" class="btn-link" @click="currentDicomCanvas.saveImage()">
<svg-icon icon-class="image" style="font-size:20px;" />
</button>
<!-- 标签 -->
<button :title="$t('trials:dicom-show:tags')" class="btn-link" @click="currentDicomCanvas.showTags()">
<svg-icon icon-class="dictionary" style="font-size:20px;" />
</button>
</div>
</div>
<div class="measureTool-wrapper">
@ -405,7 +409,7 @@ export default {
loadImageStack(dicomSeries) {
this.currentDicomCanvas.toolState.clipPlaying = false
this.$nextTick(() => {
let series = Object.assign({}, dicomSeries)
const series = Object.assign({}, dicomSeries)
this.currentDicomCanvas.loadImageStack(series)
})
},
@ -416,7 +420,7 @@ export default {
Array.from(elements).forEach((element, index) => {
const canvasIndex = element.getAttribute('data-index')
if (index < seriesList.length && element.style.display !== 'none') {
let series = Object.assign({}, seriesList[index])
const series = Object.assign({}, seriesList[index])
this.$refs[`dicomCanvas${canvasIndex}`].loadImageStack(series)
}
})

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,7 @@
/>
<!-- <embed v-else-if="fileType.indexOf('pdf') !== -1" :src="filePath+'#toolbar=0'" style="width: 100%; height: 100%"> -->
<!-- <iframe v-else-if="fileType.indexOf('pdf') !== -1" :src="filePath+'#toolbar=0'" width="100%" height="100%" frameborder="0" /> -->
<iframe v-else-if="fileType.indexOf('pdf') !== -1" :src="`/static/pdfjs/web/viewer.html?file=${OSSclientConfig.basePath}${filePath}?userName=${currentUser}`" width="100%" height="100%" frameborder="0" crossorigin="anonymous" />
<iframe v-else-if="fileType.indexOf('pdf') !== -1" :src="`/static/pdfjs/web/viewer.html?file=${OSSclientConfig.basePath}${filePath}?userName=${currentUser}&COMPANY=${COMPANY}`" width="100%" height="100%" frameborder="0" crossorigin="anonymous" />
<!-- <pdf-->
<!-- v-else-if="fileType.indexOf('pdf') !== -1"-->
<!-- :src="`/static/pdfjs/web/viewer.html?file=${filePath}`">-->
@ -40,7 +40,8 @@ export default {
},
data() {
return {
currentUser: zzSessionStorage.getItem('userName')
currentUser: zzSessionStorage.getItem('userName'),
COMPANY:process.env.VUE_APP_COMPANY_NAME
}
},
mounted() {

View File

@ -0,0 +1,511 @@
<template>
<el-dialog
:visible.sync="visible"
:fullscreen="true"
:close-on-click-modal="false"
:before-close="beforeClose"
:append-to-body="true"
v-loading="btnLoading"
class="downloadDicomAndNonedicom"
>
<span slot="title">{{ title }}</span>
<div class="top">
<span>{{ $t('download:top:title') }}</span>
<div class="btnBox">
<el-button
type="primary"
size="mini"
v-if="hasDicom"
@click.stop="getIRReadingDownloadStudyInfo('dicom')"
>
{{ $t('download:button:downloadDicom') }}
</el-button>
<el-button
type="primary"
size="mini"
v-if="hasNonedicom"
@click.stop="getIRReadingDownloadStudyInfo('noneDicom')"
>
{{ $t('download:button:downloadNonedicom') }}
</el-button>
<el-button
type="primary"
size="mini"
v-if="hasDicom || hasNonedicom"
@click.stop="getIRReadingDownloadStudyInfo('all')"
>
{{ $t('download:button:downloadAll') }}
</el-button>
</div>
</div>
<div class="tip">
<i class="el-icon-warning-outline"></i>
<div v-html="$t('download:tip:message')"></div>
</div>
<!--上传列表@selection-change="handleSelectionChange"-->
<el-table
ref="dicomFilesTable"
v-adaptive="{ bottomOffset: 85 }"
height="100"
:data="list"
:loading="loading"
class="dicomFiles-table"
@sort-change="handleSortByColumn"
:default-sort="{ prop: 'TaskBlindName', order: 'descending' }"
>
<!-- <el-table-column
type="selection"
width="55"
:selectable="handleSelectable"
/> -->
<el-table-column type="index" width="40" />
<!--受试者-->
<el-table-column
:label="$t('download:table:subjectCode')"
min-width="130"
prop="SubjectCode"
show-overflow-tooltip
/>
<!--任务名称-->
<el-table-column
:label="$t('download:table:taskName')"
min-width="130"
show-overflow-tooltip
prop="TaskBlindName"
sortable="custom"
/>
<!--检查类型-->
<el-table-column
:label="$t('download:table:studyType')"
min-width="130"
show-overflow-tooltip
prop="IsDicom"
sortable="custom"
>
<template slot-scope="scope">
<span>{{ $fd('IsDicom', scope.row.IsDicom) }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('download:table:orginalStudyListNum')"
min-width="150"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-button
v-if="
(scope.row.IsDicom &&
scope.row.DicomStudyList &&
scope.row.DicomStudyList.length >= 1) ||
(!scope.row.IsDicom &&
scope.row.NoneDicomStudyList &&
scope.row.NoneDicomStudyList.length >= 1)
"
type="text"
@click="handleOpenDialog(scope.row)"
>
<span>{{
scope.row.IsDicom
? scope.row.DicomStudyList.length
: scope.row.NoneDicomStudyList.length
}}</span>
</el-button>
<span v-else>0</span>
</template>
</el-table-column>
<el-table-column
:label="$t('common:action:action')"
fixed="right"
width="150"
>
<template slot-scope="scope">
<!--预览--->
<el-button
circle
icon="el-icon-view"
:title="$t('download:button:preview')"
@click.stop="preview(scope.row)"
/>
<!--下载--->
<el-button
circle
icon="el-icon-download"
:title="$t('download:button:download')"
@click.stop="getIRReadingDownloadStudyInfo('one', scope.row)"
/>
</template>
</el-table-column>
</el-table>
<study-view
v-if="model_cfg.visible"
:model_cfg="model_cfg"
:modelList="modelList"
:bodyPart="bodyPart"
:IsDicom="IsDicom"
:visitTaskId="modelTaskId"
/>
</el-dialog>
</template>
<script>
import {
getSubjectImageDownloadSelectList,
getIRReadingDownloadStudyInfo,
downloadImageSuccess,
} from '@/api/load.js'
import studyView from '@/components/uploadDicomAndNonedicom/study-view.vue'
import store from '@/store'
import { downLoadFile } from '@/utils/stream.js'
import { getToken } from '@/utils/auth'
let defaultSearchData = () => {
return {
SubjectId: null,
TrialReadingCriterionId: null,
SubjectCode: null,
Asc: false,
SortField: 'TaskBlindName',
}
}
export default {
name: 'downloadDicomAndNonedicom',
components: {
'study-view': studyView,
},
props: {
visible: {
required: true,
type: Boolean,
default: false,
},
SubjectId: {
type: String,
default: '',
},
TaskId: {
type: String,
default: '',
},
SubjectCode: {
type: String,
default: '',
},
Criterion: {
type: Object,
default: () => {
return {}
},
},
},
data() {
return {
title: null,
loading: false,
list: [],
searchData: defaultSearchData(),
btnLoading: false,
hasDicom: false,
hasNonedicom: false,
//
model_cfg: {
visible: false,
showClose: true,
width: '1000px',
title: '',
appendToBody: true,
},
modelList: [],
IsDicom: true,
open: null,
downloadId: null,
IsReadingTaskViewInOrder: 0, //
bodyPart: [],
modelTaskId: null,
}
},
async mounted() {
this.getList()
this.bodyPart = await this.$getBodyPart(this.$route.query.trialId)
this.title = `Download Images${this.SubjectCode}${this.Criterion.TrialReadingCriterionName}`
},
beforeDestroy() {
store.dispatch('trials/setUnLock', false)
},
methods: {
beforeClose() {
this.$emit('update:visible', false)
},
//
async getList() {
try {
this.searchData.SubjectId = this.SubjectId
this.searchData.TrialReadingCriterionId =
this.Criterion.TrialReadingCriterionId
this.searchData.SubjectCode = this.SubjectCode
if (this.TaskId) {
this.searchData.VisitTaskId = this.TaskId
}
this.loading = true
let res = await getSubjectImageDownloadSelectList(this.searchData)
this.loading = false
if (res.IsSuccess) {
this.list = res.Result
this.hasDicom = this.list.some((item) => item.IsDicom)
this.hasNonedicom = this.list.some((item) => !item.IsDicom)
}
} catch (err) {
this.loading = false
console.log(err)
}
},
//
async getIRReadingDownloadStudyInfo(type, row) {
try {
let data = {
SubjectId: this.SubjectId || this.list[0].SubjectId,
TrialReadingCriterionId: this.Criterion.TrialReadingCriterionId,
SubjectCode: this.SubjectCode || this.list[0].SubjectCode,
SubjectVisitTaskList: [],
DicomStudyIdList: [],
NoneDicomStudyIdList: [],
}
if (type === 'dicom' || type === 'all') {
this.list.forEach((item) => {
if (
item.IsDicom &&
item.DicomStudyList &&
item.DicomStudyList.length > 0
) {
data.SubjectVisitTaskList.push({
SubjectvisitId: item.SourceSubjectVisitId,
TaskId: item.VisitTaskId,
})
let arr = item.DicomStudyList.map((d) => d.Id)
data.DicomStudyIdList = [...data.DicomStudyIdList, ...arr]
}
})
}
if (type === 'noneDicom' || type === 'all') {
this.list.forEach((item) => {
if (
!item.IsDicom &&
item.NoneDicomStudyList &&
item.NoneDicomStudyList.length > 0
) {
data.SubjectVisitTaskList.push({
SubjectvisitId: item.SourceSubjectVisitId,
TaskId: item.VisitTaskId,
})
let arr = item.NoneDicomStudyList.map((d) => d.Id)
data.NoneDicomStudyIdList = [...data.NoneDicomStudyIdList, ...arr]
}
})
}
if (type === 'one') {
if (
row.IsDicom &&
row.DicomStudyList &&
row.DicomStudyList.length > 0
) {
data.SubjectVisitTaskList.push({
SubjectvisitId: row.SourceSubjectVisitId,
TaskId: row.VisitTaskId,
})
let arr = row.DicomStudyList.map((d) => d.Id)
data.DicomStudyIdList = [...data.DicomStudyIdList, ...arr]
}
if (
!row.IsDicom &&
row.NoneDicomStudyList &&
row.NoneDicomStudyList.length > 0
) {
data.SubjectVisitTaskList.push({
SubjectvisitId: row.SourceSubjectVisitId,
TaskId: row.VisitTaskId,
})
let arr = row.NoneDicomStudyList.map((d) => d.Id)
data.NoneDicomStudyIdList = [...data.NoneDicomStudyIdList, ...arr]
}
}
this.btnLoading = true
let res = await getIRReadingDownloadStudyInfo(data)
this.btnLoading = false
if (res.IsSuccess) {
this.downloadId = res.OtherInfo.PreDownloadId
this.IsReadingTaskViewInOrder = res.OtherInfo.IsReadingTaskViewInOrder
this.downloadImage(res.Result)
}
} catch (err) {
this.btnLoading = false
console.log(err)
}
},
//
async downloadImage(data) {
try {
let { files, name, fileType } = this.formatDownloadFile(data)
let res = await downLoadFile(files, name, 'zip')
if (res && this.downloadId) {
this.downloadImageSuccess()
}
} catch (err) {
console.log(err)
}
},
//
formatDownloadFile(list) {
let files = [],
name = `${list[0].SubjectCode}_${new Date().getTime()}.zip`
if (this.IsReadingTaskViewInOrder === 1) {
name = `${list[0].SubjectCode}_${list[0].TaskBlindName}.zip`
}
if (this.IsReadingTaskViewInOrder === 0) {
name = `${list[0].TaskBlindName}.zip`
}
list.forEach((data) => {
if (data.StudyList && data.StudyList.length > 0) {
let StudyList = data.StudyList
StudyList.forEach((study) => {
if (study.SeriesList.length > 0) {
study.SeriesList.forEach((series) => {
if (series.InstancePathList.length > 0) {
series.InstancePathList.forEach((instance) => {
let fileName = instance.Path.split('/').pop()
let obj = {
name: `${data.SubjectCode}/${
data.TaskBlindName
}/${this.$fd('IsDicom', true)}/${
study.StudyCode
}/${fileName}`,
url: this.OSSclientConfig.basePath + instance.Path,
}
if (this.IsReadingTaskViewInOrder === 0) {
obj = {
name: `${data.TaskBlindName}/${this.$fd(
'IsDicom',
true
)}/${fileName}`,
url: this.OSSclientConfig.basePath + instance.Path,
}
}
files.push(obj)
})
}
})
}
})
}
if (data.NoneDicomStudyList && data.NoneDicomStudyList.length > 0) {
let NoneDicomStudyList = data.NoneDicomStudyList
//
NoneDicomStudyList.forEach((study) => {
if (study.FileList.length > 0) {
study.FileList.forEach((item) => {
let obj = {
name: `${data.SubjectCode}/${data.TaskBlindName}/${this.$fd(
'IsDicom',
false
)}/${study.StudyCode}/${item.FileName}`,
url: this.OSSclientConfig.basePath + item.Path,
}
if (this.IsReadingTaskViewInOrder === 0) {
obj = {
name: `${data.TaskBlindName}/${this.$fd(
'IsDicom',
false
)}/${item.FileName}`,
url: this.OSSclientConfig.basePath + item.Path,
}
}
files.push(obj)
})
}
})
}
})
return { files, name }
},
//
async downloadImageSuccess() {
try {
let params = {
TrialImageDownloadId: this.downloadId,
}
await downloadImageSuccess(params)
} catch (err) {
console.log(err)
}
},
handleOpenDialog(item) {
this.model_cfg.title = `${item.SubjectCode || ''} > ${item.TaskBlindName}`
if (item.IsDicom) {
this.modelList = item.DicomStudyList
} else {
this.modelList = item.NoneDicomStudyList
this.modelList.forEach((data) => {
data.SourceSubjectVisitId = item.SourceSubjectVisitId
})
}
this.modelTaskId = item.VisitTaskId
this.IsDicom = item.IsDicom
this.model_cfg.visible = true
},
//
handleSortByColumn(column) {
if (column.order === 'ascending') {
this.searchData.Asc = true
} else {
this.searchData.Asc = false
}
this.searchData.SortField = column.prop
this.searchData.PageIndex = 1
this.getList()
},
preview(row) {
if (!row.IsDicom) {
this.handlePreviewNoneDicomFiles(row)
} else {
this.handleViewReadingImages(row)
}
},
// Dicom
handlePreviewNoneDicomFiles(row) {
if (this.open) {
this.open.close()
}
let trialId = this.$route.query.trialId
var token = getToken()
const routeData = this.$router.resolve({
path: `/showNoneDicoms?trialId=${trialId}&subjectVisitId=${row.SourceSubjectVisitId}&TokenKey=${token}`,
})
this.open = window.open(routeData.href, '_blank')
},
//
handleViewReadingImages(row) {
if (this.open) {
this.open.close()
}
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}`,
})
this.open = window.open(routeData.href, '_blank')
},
},
}
</script>
<style lang="scss" scoped>
.top {
display: flex;
align-items: center;
justify-content: space-between;
margin: 10px 0;
}
.tip {
display: flex;
align-items: flex-start;
margin-top: 5px;
i {
margin: 3px 5px 0 0;
}
}
</style>

View File

@ -52,7 +52,7 @@
class="el-icon-warning-outline"
style="color: #f56c6c; font-size: 24px"
></i>
<span>{{ $t("feedBack:imgfail:tip") }}</span>
<span>{{ $t('feedBack:imgfail:tip') }}</span>
</p>
<!-- 问题反馈 -->
<el-form-item
@ -140,7 +140,7 @@
<div slot="footer" v-if="type !== 'detail' || isImgfail">
<!-- 取消 -->
<el-button size="small" @click.stop="cancel">
{{ $t("feedBack:button:cancel") }}
{{ $t('feedBack:button:cancel') }}
</el-button>
<!-- 保存 -->
<el-button
@ -149,21 +149,21 @@
@click.stop="save"
:loading="loading"
>
{{ $t("feedBack:button:save") }}
{{ $t('feedBack:button:save') }}
</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import uploadImage from "./uploadImage.vue";
import uploadImage from './uploadImage.vue'
import {
addOrUpdateUserFeedBack,
getUserFeedBackInfo,
batchUpdateFeedBackState,
} from "@/api/trials.js";
} from '@/api/trials.js'
export default {
name: "FB",
name: 'FB',
components: { uploadImage },
data() {
return {
@ -195,131 +195,131 @@ export default {
QuestionType: [
{
required: true,
message: this.$t("common:ruleMessage:specify"),
trigger: "blur",
message: this.$t('common:ruleMessage:specify'),
trigger: 'blur',
},
],
QuestionDescription: [
{
required: true,
message: this.$t("common:ruleMessage:specify"),
trigger: "blur",
message: this.$t('common:ruleMessage:specify'),
trigger: 'blur',
},
{
validator: (rule, value, callback) => {
if (value.length < 5) {
callback(
new Error(this.$t("feedBack:ruleMessage:lengthLimitMin5"))
);
new Error(this.$t('feedBack:ruleMessage:lengthLimitMin5'))
)
}
callback();
callback()
},
trigger: "blur",
trigger: 'blur',
},
],
},
isUpload: false,
};
}
},
computed: {
level() {
if (this.hasPermi(["role:dev", "role:admin"])) {
return 9;
if (this.hasPermi(['role:dev', 'role:admin'])) {
return 9
}
if (this.hasPermi(["role:pm"])) {
return 8;
if (this.hasPermi(['role:pm', 'role:apm'])) {
return 8
}
if (this.hasPermi(["role:ir", "role:crc"])) {
return 7;
if (this.hasPermi(['role:ir', 'role:crc'])) {
return 7
}
return 0;
return 0
},
QuestionTypeOptions() {
if (this.level > 7) {
return [
...this.$d.FeedBackTypeToCRC.filter((item) => item.value > 0),
...this.$d.FeedBackTypeToIR,
];
]
}
if (this.hasPermi(["role:ir"])) {
return this.$d.FeedBackTypeToIR;
if (this.hasPermi(['role:ir'])) {
return this.$d.FeedBackTypeToIR
}
if (this.hasPermi(["role:crc"])) {
return this.$d.FeedBackTypeToCRC;
if (this.hasPermi(['role:crc'])) {
return this.$d.FeedBackTypeToCRC
}
return [];
return []
},
},
methods: {
open(data) {
let { type, trialId, Id, visitTaskId, SubjectVisitId } = data;
this.type = type;
this.trialId = trialId;
this.Id = Id;
this.visitTaskId = visitTaskId;
this.SubjectVisitId = SubjectVisitId;
let { type, trialId, Id, visitTaskId, SubjectVisitId } = data
this.type = type
this.trialId = trialId
this.Id = Id
this.visitTaskId = visitTaskId
this.SubjectVisitId = SubjectVisitId
if (visitTaskId) {
this.isImgfail = true;
this.isImgfail = true
}
this.setTypeOption();
this.setTypeOption()
if (!Id) {
this.title = this.setTitle();
this.title = this.setTitle()
}
if (Id || visitTaskId) {
this.getInfo(Id, visitTaskId);
this.getInfo(Id, visitTaskId)
}
this.visible = true;
this.visible = true
},
cancel() {
this.visible = false;
this.$emit("closed");
this.visible = false
this.$emit('closed')
},
async save() {
try {
let validate = await this.$refs.feedBackForm.validate();
if (!validate) return;
let validate = await this.$refs.feedBackForm.validate()
if (!validate) return
if (this.trialId) {
this.form.TrialId = this.trialId;
this.form.TrialId = this.trialId
}
if (this.visitTaskId) {
this.form.VisitTaskId = this.visitTaskId;
this.form.VisitTaskId = this.visitTaskId
}
if (this.SubjectVisitId) {
this.form.SubjectVisitId = this.SubjectVisitId;
this.form.SubjectVisitId = this.SubjectVisitId
}
if (this.Id) {
this.form.Id = this.Id;
this.form.Id = this.Id
}
this.loading = true;
let res = await addOrUpdateUserFeedBack(this.form);
this.loading = false;
this.loading = true
let res = await addOrUpdateUserFeedBack(this.form)
this.loading = false
if (res.IsSuccess) {
this.$emit("success");
this.$message.success(this.$t("feedBack:save:success"));
this.cancel();
this.$emit('success')
this.$message.success(this.$t('feedBack:save:success'))
this.cancel()
}
} catch (err) {
this.loading = false;
console.log(err);
this.loading = false
console.log(err)
}
},
//
async changeState() {
if (this.isImgfail) return;
if (this.isImgfail) return
try {
let data = {
IdList: [this.Id],
State: this.form.State,
};
let res = await batchUpdateFeedBackState(data);
}
let res = await batchUpdateFeedBackState(data)
if (res.IsSuccess) {
this.$message.success(this.$t("feedBack:changeState:success"));
this.$emit("success");
this.$message.success(this.$t('feedBack:changeState:success'))
this.$emit('success')
// this.cancel();
this.getInfo(this.Id, this.visitTaskId);
this.getInfo(this.Id, this.visitTaskId)
}
} catch (err) {
console.log(err);
console.log(err)
}
},
//
@ -328,56 +328,56 @@ export default {
let data = {
Id,
visitTaskId,
};
let res = await getUserFeedBackInfo(data);
}
let res = await getUserFeedBackInfo(data)
if (res.IsSuccess && res.Result) {
this.form = res.Result;
this.Id = res.Result.Id;
this.form = res.Result
this.form.ScreenshotList = this.form.ScreenshotList || []
this.Id = res.Result.Id
if (res.Result.State > 0) {
this.isStateChange = false;
this.isStateChange = false
}
if (res.Result.VisitTaskId) {
this.visitTaskId = res.Result.VisitTaskId;
this.visitTaskId = res.Result.VisitTaskId
}
if (res.Result.SubjectVisitId) {
this.SubjectVisitId = res.Result.SubjectVisitId;
this.SubjectVisitId = res.Result.SubjectVisitId
}
let code = this.$fd("UserType", res.Result.UserTypeEnum);
this.title = this.setTitle(code, res.Result.FeedBackFullName);
let code = this.$fd('UserType', res.Result.UserTypeEnum)
this.title = this.setTitle(code, res.Result.FeedBackFullName)
if (visitTaskId) {
code = `${res.Result.SubjectCode}-${res.Result.SubjectVisitName}`;
this.title = this.setTitle(code, res.Result.FeedBackFullName);
code = `${res.Result.SubjectCode}-${res.Result.SubjectVisitName}`
this.title = this.setTitle(code, res.Result.FeedBackFullName)
}
this.$refs.uploadImage.initFileList(res.Result.ScreenshotList);
this.$refs.uploadImage.initFileList(res.Result.ScreenshotList)
}
} catch (err) {
console.log(err);
console.log(err)
}
},
setTypeOption() {
if (!this.trialId) return (this.options = []);
if (this.hasPermi(["role:ir"]))
return (this.option = this.$d.FeedBackTypeToIR);
if (this.hasPermi(["role:crc"]))
return (this.option = this.$d.FeedBackTypeToCRC);
if (!this.trialId) return (this.options = [])
if (this.hasPermi(['role:ir']))
return (this.option = this.$d.FeedBackTypeToIR)
if (this.hasPermi(['role:crc']))
return (this.option = this.$d.FeedBackTypeToCRC)
},
setTitle(code, name) {
console.log(code, name);
if (this.hasPermi(["role:pm"]) && this.visitTaskId) {
return `${this.$t("feedBack:form:title:pm2")}(${code}/${name})`;
if (this.hasPermi(['role:pm']) && this.visitTaskId) {
return `${this.$t('feedBack:form:title:pm2')}(${code}/${name})`
}
if (this.hasPermi(["role:pm", "role:dev", "role:admin"])) {
return `${this.$t("feedBack:form:title:pm")}(${code},${name})`;
if (this.hasPermi(['role:pm', 'role:dev', 'role:admin'])) {
return `${this.$t('feedBack:form:title:pm')}(${code},${name})`
}
if (this.hasPermi(["role:ir", "role:crc"]) && this.type === "detail") {
return `${this.$t("feedBack:form:detail:title")}`;
if (this.hasPermi(['role:ir', 'role:crc']) && this.type === 'detail') {
return `${this.$t('feedBack:form:detail:title')}`
}
if (this.hasPermi(["role:ir", "role:crc"])) {
return `${this.$t("feedBack:form:title")}`;
if (this.hasPermi(['role:ir', 'role:crc'])) {
return `${this.$t('feedBack:form:title')}`
}
},
},
};
}
</script>
<style lang="scss" scoped>
.tip {

View File

@ -58,14 +58,14 @@
</div>
</template>
<script>
const type = "Statement of Work";
const type = 'Statement of Work'
export default {
name: "UploadImage",
name: 'UploadImage',
props: {
path: {
required: true,
default: () => {
return [];
return []
},
},
disabled: {
@ -78,14 +78,14 @@ export default {
},
trialId: {
type: String,
default: "",
default: '',
},
},
data() {
return {
fileList: [],
btnDisabled: false,
};
}
},
methods: {
remove(file, fileList) {
@ -93,115 +93,113 @@ export default {
},
fileToBlob(file) {
// FileReader
const reader = new FileReader();
const reader = new FileReader()
return new Promise((resolve) => {
// FileReader load
reader.addEventListener("load", (e) => {
let blob;
if (typeof e.target.result === "object") {
blob = new Blob([e.target.result]);
reader.addEventListener('load', (e) => {
let blob
if (typeof e.target.result === 'object') {
blob = new Blob([e.target.result])
} else {
blob = e.target.result;
blob = e.target.result
}
resolve(blob);
});
resolve(blob)
})
// FileReader ArrayBuffer File
reader.readAsArrayBuffer(file);
});
reader.readAsArrayBuffer(file)
})
},
// oss
async uploadToOSS(name, file) {
try {
let defaultPath = "/System/FeedBack/";
let defaultPath = '/System/FeedBack/'
if (this.trialId) {
defaultPath = `/${this.trialId}/FeedBack/`;
defaultPath = `/${this.trialId}/FeedBack/`
}
let res = await this.OSSclient.put(`${defaultPath}${name}`, file);
return res;
let res = await this.OSSclient.put(`${defaultPath}${name}`, file)
return res
} catch (err) {
console.log(err);
return false;
console.log(err)
return false
}
},
initFileList(list) {
if (list && list.length > 0) {
this.fileList = [];
this.fileList = []
list.forEach((item, index) => {
let name = item.split("/");
let name = item.split('/')
this.fileList.push({
name: name[name.length - 1],
path: item,
fullPath: this.OSSclientConfig.basePath + item,
url: this.OSSclientConfig.basePath + item,
uid: `${name[name.length - 1]}${index}`,
});
});
console.log(this.fileList);
})
})
}
},
async uploadFile(param) {
var fileName = param.file.name;
this.$emit("update:isUpload", true);
this.btnDisabled = true;
var fileName = param.file.name
this.$emit('update:isUpload', true)
this.btnDisabled = true
this.fileList.push({
url: param.file.url,
path: fileName,
uid: param.file.uid,
});
let file = await this.fileToBlob(param.file);
let res = await this.uploadToOSS(fileName, file);
this.btnDisabled = false;
let index = this.fileList.findIndex(
(item) => item.uid === param.file.uid
);
})
let file = await this.fileToBlob(param.file)
let res = await this.uploadToOSS(fileName, file)
this.btnDisabled = false
let index = this.fileList.findIndex((item) => item.uid === param.file.uid)
if (!res) {
if (index >= 0) {
this.fileList.splice(index, 1);
this.fileList.splice(index, 1)
}
return this.$emit("update:isUpload", false);
return this.$emit('update:isUpload', false)
}
this.fileList[index].url = this.OSSclientConfig.basePath + res.name;
this.fileList[index].path = res.name;
this.$emit("update:path", [...this.path, res.name]);
this.$emit("update:isUpload", false);
return false;
let name = this.$getObjectName(res.url)
this.fileList[index].url = this.OSSclientConfig.basePath + name
this.fileList[index].path = name
this.$emit('update:path', [...this.path, res.name])
this.$emit('update:isUpload', false)
return false
},
beforeUpload(file, fileList) {
const isValidFile = this.fileValid(file.name, ["png", "jpg", "jpeg"]);
const isValidFile = this.fileValid(file.name, ['png', 'jpg', 'jpeg'])
if (isValidFile) {
// this.fileList = [];
} else {
this.$alert(this.$t("feedBack:uploadImg:format"));
return false;
this.$alert(this.$t('feedBack:uploadImg:format'))
return false
}
},
handlePreview(file) {
file.fullPath ? window.open(file.fullPath, "_blank") : "";
file.fullPath ? window.open(file.fullPath, '_blank') : ''
},
handleExceed(files, fileList) {
this.$message.warning(`Upload is currently limited to 1 file`);
this.$message.warning(`Upload is currently limited to 1 file`)
},
fileValid(fileName, typeArr) {
var extendName = fileName
.substring(fileName.lastIndexOf(".") + 1)
.toLocaleLowerCase();
.substring(fileName.lastIndexOf('.') + 1)
.toLocaleLowerCase()
if (typeArr.indexOf(extendName) > -1) {
return true;
return true
} else {
return false;
return false
}
},
handlePictureCardPreview(file) {
this.$refs[file.url].$viewer.show();
this.$refs[file.url].$viewer.show()
},
handleRemove(file) {
let index = this.fileList.findIndex((item) => item.uid === file.uid);
this.fileList.splice(index, 1);
let arr = this.fileList.map((item) => item.path);
this.$emit("update:path", arr);
let index = this.fileList.findIndex((item) => item.uid === file.uid)
this.fileList.splice(index, 1)
let arr = this.fileList.map((item) => item.path)
this.$emit('update:path', arr)
},
},
};
}
</script>
<style>
.upload-container .el-upload--text {

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,117 @@
<template>
<el-dialog
:visible.sync="visible"
:fullscreen="true"
:close-on-click-modal="false"
:before-close="beforeClose"
: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"
>
<dicomFile
v-if="activeName === 'dicom'"
:SubjectId="SubjectId"
:SubjectCode="SubjectCode"
:Criterion="Criterion"
:TaskId="VisitTaskId"
:isUpload.sync="isUpload"
:isReadingTaskViewInOrder="isReadingTaskViewInOrder"
/>
</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"
/>
</el-tab-pane>
</el-tabs>
</el-dialog>
</template>
<script>
import dicomFile from './dicomFile.vue'
import nonedicomFile from './nonedicomFile.vue'
import store from '@/store'
export default {
name: 'uploadDicomAndNonedicom',
components: { dicomFile, nonedicomFile },
props: {
visible: {
required: true,
type: Boolean,
default: false,
},
SubjectId: {
type: String,
default: '',
},
SubjectCode: {
type: String,
default: '',
},
Criterion: {
type: Object,
default: () => {
return {}
},
},
VisitTaskId: {
type: String,
default: '',
},
isReadingTaskViewInOrder: {
type: Number,
default: 0,
},
},
data() {
return {
title: '',
activeName: 'dicom',
isUpload: false,
}
},
mounted() {
this.title = `Upload Images${this.SubjectCode}${this.Criterion.TrialReadingCriterionName}`
store.dispatch('trials/setUnLock', true)
},
methods: {
beforeClose() {
if (
this.$route.path !== '/trials/trials-panel/reading/readingTask' &&
this.isUpload
) {
return window.location.reload()
}
this.$emit('update:visible', false)
},
},
}
</script>
<style lang="scss" scoped>
::v-deep.el-tabs--left .el-tabs__header.is-left {
margin-right: 0;
}
.uploadDicomAndNonedicom {
::v-deep.el-tabs--border-card > .el-tabs__header .el-tabs__item {
color: #909399;
}
::v-deep.el-tabs--border-card > .el-tabs__header .el-tabs__item.is-active {
color: #428bca;
background-color: #fff;
border-right-color: #dcdfe6;
border-left-color: #dcdfe6;
}
}
</style>

View File

@ -0,0 +1,919 @@
<template>
<div class="nonedicomFile">
<div class="top">
<span>{{ $t('upload:nonedicom:title') }}</span>
<div class="tip">
<i class="el-icon-warning-outline"></i>
<div v-html="$t('upload:nonedicom:tip:message')"></div>
</div>
</div>
<!--检查列表-->
<el-table
:data="list"
style="width: 100%"
v-adaptive="{ bottomOffset: 60 }"
:loading="loading"
@sort-change="handleSortByColumn"
:default-sort="{ prop: 'TaskBlindName', order: 'descending' }"
>
<!--受试者-->
<el-table-column
prop="SubjectCode"
:label="$t('upload:nonedicom:table:subject')"
/>
<!--任务名称-->
<el-table-column
prop="TaskBlindName"
:label="$t('upload:nonedicom:table:taskName')"
sortable="custom"
/>
<!--检查类型-->
<el-table-column
prop="Modality"
:label="$t('upload:nonedicom:table:molityType')"
sortable="custom"
>
</el-table-column>
<!--检查部位-->
<el-table-column
prop="BodyPart"
:label="$t('upload:nonedicom:table:bodyPart')"
sortable="custom"
/>
<!--原文件数-->
<el-table-column
prop="FileCount"
:label="$t('upload:nonedicom:table:fileCount')"
>
<template slot-scope="scope">
<el-popover
v-if="scope.row.FileCount"
trigger="click"
placement="bottom"
>
<el-table :data="scope.row.FileList" height="300" size="small">
<!-- 文件名称 -->
<el-table-column
prop="FileName"
:label="$t('trials:audit:table:nonDicomsFileName')"
width="200"
/>
<el-table-column
prop="FileSize"
:label="$t('trials:audit:table:nonDicomsFileSize')"
width="100"
>
<template slot-scope="scope">
<span>{{
scope.row.FileSize && scope.row.FileSize > 0
? `${(scope.row.FileSize / 1024 / 1024).toFixed(2)}MB`
: 0
}}</span>
</template>
</el-table-column>
<el-table-column :label="$t('common:action:action')" width="120">
<template slot-scope="files">
<!-- 预览 -->
<viewer
:ref="files.row.Path"
style="margin: 0 10px"
:images="[`${OSSclientConfig.basePath}${files.row.Path}`]"
>
<el-button
circle
icon="el-icon-view"
:title="$t('upload:nonedicom:button:preview')"
:disabled="
files.row.FileType &&
files.row.FileType.indexOf('zip') >= 0
"
@click.native.prevent="previewFile(files.row)"
/>
<img
v-show="false"
crossorigin="anonymous"
:src="`${OSSclientConfig.basePath}${files.row.Path}`"
alt="Image"
/>
</viewer>
</template>
</el-table-column>
</el-table>
<div slot="reference" class="name-wrapper">
<el-button type="text">
{{ scope.row.FileCount }}
</el-button>
</div>
</el-popover>
<span v-else>{{ scope.row.FileCount }}</span>
</template>
</el-table-column>
<!--后处理文件数-->
<el-table-column
prop="UploadedFileCount"
:label="$t('upload:nonedicom:table:uploadFileCount')"
>
<template slot-scope="scope">
<el-popover
v-if="scope.row.UploadedFileCount"
trigger="click"
placement="bottom"
>
<el-table
:data="scope.row.UploadedFileList"
height="300"
size="small"
>
<!-- 文件名称 -->
<el-table-column
prop="FileName"
:label="$t('trials:audit:table:nonDicomsFileName')"
width="200"
/>
<el-table-column
prop="FileSize"
:label="$t('trials:audit:table:nonDicomsFileSize')"
width="100"
>
<template slot-scope="scope">
<span>{{
scope.row.FileSize && scope.row.FileSize > 0
? `${(scope.row.FileSize / 1024 / 1024).toFixed(2)}MB`
: 0
}}</span>
</template>
</el-table-column>
<el-table-column :label="$t('common:action:action')" width="120">
<template slot-scope="files">
<!-- 预览 -->
<viewer
:ref="files.row.Path"
style="margin: 0 10px"
:images="[`${OSSclientConfig.basePath}${files.row.Path}`]"
>
<el-button
circle
icon="el-icon-view"
:title="$t('upload:nonedicom:button:preview')"
:disabled="
files.row.FileType &&
files.row.FileType.indexOf('zip') >= 0
"
@click.native.prevent="previewFile(files.row)"
/>
<img
v-show="false"
crossorigin="anonymous"
:src="`${OSSclientConfig.basePath}${files.row.Path}`"
alt="Image"
/>
</viewer>
</template>
</el-table-column>
</el-table>
<div slot="reference" class="name-wrapper">
<el-button type="text">
{{ scope.row.UploadedFileCount }}
</el-button>
</div>
</el-popover>
<span v-else>{{ scope.row.UploadedFileCount }}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('common:action:action')"
fixed="right"
width="180"
>
<template slot-scope="scope">
<!--预览--->
<el-button
circle
:disabled="scope.row.UploadedFileCount <= 0"
icon="el-icon-view"
:title="$t('upload:nonedicom:button:preview')"
@click.stop="handlePreviewNoneDicomFiles(scope.row)"
/>
<!--上传--->
<el-button
circle
icon="el-icon-upload2"
:title="$t('upload:nonedicom:button:upload')"
@click.native.prevent="handleUpload(scope.row)"
/>
<!--删除--->
<el-button
:disabled="
scope.row.UploadedFileCount <= 0 ||
scope.row.ReadingTaskState === 2
"
circle
icon="el-icon-delete"
:title="$t('upload:nonedicom:button:delete')"
@click.stop="remove(scope.row)"
/>
</template>
</el-table-column>
</el-table>
<!-- 预览单个图像 -->
<el-dialog
v-if="imgObj.visible"
:visible.sync="imgObj.visible"
:title="$t('upload:nonedicom:dialogTitle:preview')"
append-to-body
width="565px"
>
<div
v-loading="imgObj.loading"
class="base-modal-body"
style="border: 2px solid #ccc; padding: 10px"
>
<el-image
:src="`${OSSclientConfig.basePath}${imgObj.url}`"
crossorigin="anonymous"
fit="fit"
style="height: 500px; width: 500px"
@error="imgObj.loading = false"
@load="imgObj.loading = false"
/>
</div>
</el-dialog>
<!-- 上传非dicom文件 -->
<el-dialog
v-if="uploadVisible"
:visible.sync="uploadVisible"
:close-on-click-modal="false"
:title="$t('trials:uploadNonDicoms:dialogTitle:upload')"
width="800px"
append-to-body
custom-class="base-dialog-wrapper"
@close="resetFileDiaolg"
>
<!-- 多文件上传 -->
<form id="inputForm" ref="uploadForm">
<el-divider content-position="left">{{
$t('trials:uploadNonDicoms:label:fileType')
}}</el-divider>
<div class="form-group">
<div class="upload" style="margin-right: 10px">
<input
multiple="multiple"
webkitdirectory=""
directory
accept="*/*"
type="file"
name="uploadFolder"
class="select-file"
title=""
@change="beginScanFiles($event)"
v-if="!btnLoading"
/>
<div class="btn-select">
{{ $t('trials:uploadNonDicoms:button:selectFolder') }}
</div>
</div>
<div class="upload">
<input
class="select-file"
multiple=""
:accept="faccept.join(',')"
type="file"
name="uploadFile"
title=""
@change="beginScanFiles($event)"
v-if="!btnLoading"
/>
<div class="btn-select">
{{ $t('trials:uploadNonDicoms:button:select') }}
</div>
</div>
<!-- <span style="margin-left: 10px">{{
$store.state.trials.uploadTip
}}</span> -->
</div>
</form>
<!-- 文件列表 -->
<el-table
ref="filesTable"
:data="fileList"
class="dicomFiles-table"
height="300"
@selection-change="handleSelectionChange"
>
<el-table-column
type="selection"
width="55"
:selectable="(row, index) => row.status !== 2 && !btnLoading"
/>
<el-table-column type="index" width="50" />
<!-- 文件名称 -->
<el-table-column
prop="name"
:label="$t('trials:uploadNonDicoms:table:fileName')"
min-width="100"
/>
<!-- 文件大小 -->
<el-table-column
prop="size"
:label="$t('trials:uploadNonDicoms:table:fileSize')"
>
<template slot-scope="scope">
<span>{{
scope.row.size && scope.row.size > 0
? `${(scope.row.size / 1024 / 1024).toFixed(2)}MB`
: 0
}}</span>
</template>
</el-table-column>
<!-- 文件类型 -->
<el-table-column
prop="type"
:label="$t('trials:uploadNonDicoms:table:fileType')"
/>
<!-- 上传状态 -->
<el-table-column
prop="status"
:label="$t('trials:uploadNonDicoms:table:uploadStatus')"
min-width="100"
>
<template slot-scope="scope">
<el-tag
:type="['warning', 'info', 'success', 'danger'][scope.row.status]"
v-if="scope.row.status || scope.row.status === 0"
>{{ $fd('NoneDicomUploadStatus', scope.row.status) }}
</el-tag>
</template>
</el-table-column>
<el-table-column
:label="$t('trials:uploadNonDicoms:table:failedFileCount')"
min-width="150"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-progress
color="#409eff"
:percentage="
((scope.row.uploadFileSize * 100) / scope.row.size).toFixed(2) *
1
"
/>
</template>
</el-table-column>
<el-table-column :label="$t('common:action:action')">
<template slot-scope="scope">
<el-button
size="mini"
icon="el-icon-delete"
circle
:disabled="btnLoading"
:title="$t('trials:crcUpload:action:delete')"
@click="handleRemoveFile(scope.row)"
/>
</template>
</el-table-column>
</el-table>
<div style="text-align: right; padding: 10px 0px">
<span style="margin-right: 10px">{{
$store.state.trials.uploadTip
}}</span>
<el-button
size="small"
type="primary"
:disabled="selectArr.length == 0"
:loading="btnLoading"
@click="beginUpload"
>
{{ $t('trials:uploadNonDicoms:action:upload') }}
</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {
getIRUploadTaskNoneDicomStudyList,
deleteTaskStudy,
} from '@/api/load.js'
import { preArchiveStudy, uploadNoneDicomFile } from '@/api/trials'
import store from '@/store'
import { getToken } from '@/utils/auth'
let defaultSearchData = () => {
return {
SubjectId: null,
TrialReadingCriterionId: null,
SubjectCode: null,
Asc: false,
SortField: 'TaskBlindName',
}
}
export default {
name: 'nonedicomFile',
props: {
SubjectId: {
type: String,
default: '',
},
SubjectCode: {
type: String,
default: '',
},
Criterion: {
type: Object,
default: () => {
return {}
},
},
VisitTaskId: {
type: String,
default: '',
},
isUpload: {
type: Boolean,
default: false,
},
},
data() {
return {
loading: false,
list: [],
searchData: defaultSearchData(),
imgObj: { url: '', visible: false, loading: false },
uploadVisible: false,
fileList: [],
selectArr: [],
successFileList: [],
isFail: false,
faccept: ['.jpg', '.jpeg', '.png', '.bmp', '.zip'],
fCount: 0,
btnLoading: false,
currentRow: {},
studyMonitorId: null,
open: null,
}
},
watch: {
isFail() {
if (this.isFail) {
this.$confirm(
this.$t('trials:visit:crcUpload:nomDicomFiles:failUpload'),
{
type: 'warning',
distinguishCancelAndClose: true,
confirmButtonText: this.$t('common:button:confirm'),
cancelButtonText: this.$t('common:button:cancel'),
}
)
.then(() => {
this.beginUpload()
})
.catch((err) => {
this.btnLoading = false
console.log(err)
})
}
},
btnLoading() {
store.dispatch('trials/setUnLock', this.btnLoading)
},
},
created() {
this.getList()
},
beforeDestroy() {
store.dispatch('trials/setUnLock', false)
},
methods: {
async getList() {
try {
this.searchData.SubjectId = this.SubjectId
this.searchData.TrialReadingCriterionId =
this.Criterion.TrialReadingCriterionId
this.searchData.SubjectCode = this.SubjectCode
if (this.VisitTaskId) {
this.searchData.VisitTaskId = this.VisitTaskId
}
this.loading = true
let res = await getIRUploadTaskNoneDicomStudyList(this.searchData)
this.loading = false
if (res.IsSuccess) {
this.list = res.Result
}
} catch (err) {
this.loading = false
console.log(err)
}
},
//
beginScanFiles(e) {
var files = [...e.target.files]
var sameFiles = []
files.forEach((file) => {
var extendName = file.name
.substring(file.name.lastIndexOf('.'))
.toLocaleLowerCase()
if (
this.faccept.indexOf(extendName) !== -1 &&
this.fileList.findIndex((v) => v.name === file.name) > -1
) {
sameFiles.push(file.name)
}
})
var scope = this
if (sameFiles.length > 0) {
const h = this.$createElement
var msg = this.$t(
'trials:uploadNonDicoms:message:exsitSameFile'
).replace('xxx', sameFiles.join(', '))
this.$msgbox({
message: h('div', { style: 'maxHeight:300px;overflow: auto;' }, [
h('p', null, msg),
h(
'p',
null,
this.$t('trials:uploadNonDicoms:message:isContinueUpload')
),
]),
type: 'warning',
showCancelButton: true,
})
.then(() => {
scope.pendingUploadQuene(files)
})
.catch(() => {
scope.resetUploadForm()
})
} else {
scope.pendingUploadQuene(files)
}
},
pendingUploadQuene(files) {
for (var i = 0; i < files.length; ++i) {
const fileName = files[i].name
var extendName = fileName
.substring(fileName.lastIndexOf('.'))
.toLocaleLowerCase()
if (this.faccept.indexOf(extendName) !== -1) {
files[i].id = `${files[i].lastModified}${files[i].name}`
let obj = {
name: files[i].name,
size: files[i].size,
type: extendName.split('.')[1],
status: 0,
file: files[i],
id: `${files[i].lastModified}${files[i].name}`,
fileType: files[i].type,
uploadFileSize: 0,
}
this.fileList.push(obj)
this.$refs.filesTable.toggleRowSelection(obj, true)
}
}
this.resetUploadForm()
},
handleRemoveFile(row) {
this.$confirm(this.$t('trials:uploadNonDicoms:message:delete'), {
type: 'warning',
distinguishCancelAndClose: true,
})
.then(() => {
this.fileList.splice(
this.fileList.findIndex((item) => item.id === row.id),
1
)
let flag = this.successFileList.some((item) => item.id === row.id)
if (flag) {
this.successFileList.splice(
this.successFileList.findIndex((item) => item.id === row.id),
1
)
}
})
.catch(() => {})
},
resetUploadForm() {
this.$nextTick(() => {
this.$refs.uploadForm.reset()
})
},
//
handleUpload(row) {
this.fileList = []
this.uploadVisible = true
this.fCount = 0
this.currentRow = { ...row }
},
resetFileDiaolg() {
this.btnLoading = false
this.uploadVisible = false
this.selectArr = []
this.successFileList = []
this.OSSclient.close()
},
//
previewFile(row) {
// window.open(row.FullFilePath, '_blank')
// this.imgObj.url = row.FullFilePath || row.Path
// this.imgObj.loading = true
// this.imgObj.visible = true
this.$refs[row.Path].$viewer.show()
},
// Dicom
handlePreviewNoneDicomFiles(row) {
if (this.open) {
this.open.close()
}
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}`,
})
this.open = window.open(routeData.href, '_blank')
},
//
async remove(item) {
try {
let confirm = await this.$confirm(
this.$t('upload:nonedicom:confirm:delMessage'),
{
type: 'warning',
distinguishCancelAndClose: true,
confirmButtonText: this.$t('common:button:confirm'),
cancelButtonText: this.$t('common:button:cancel'),
}
)
if (confirm !== 'confirm') return
let params = {
VisitTaskId: item.VisitTaskId,
IsDicom: false,
NoneDicomStudyId: item.Id,
}
let res = await deleteTaskStudy(params)
if (res.IsSuccess) {
this.getList()
this.$emit('update:isUpload', true)
this.$message.success(
this.$t('trials:uploadImage:message:delSuccess')
)
}
} catch (err) {
console.log(err)
}
},
//
handleSortByColumn(column) {
if (column.order === 'ascending') {
this.searchData.Asc = true
} else {
this.searchData.Asc = false
}
this.searchData.SortField = column.prop
this.searchData.PageIndex = 1
this.getList()
},
//
handleSelectionChange(selection) {
this.selectArr = selection
},
//
async beginUpload() {
this.btnLoading = true
this.isFail = false
const fileMaxSize = 1024 * 1024 * 1024 * 2 // 1G
var currentFilesSize = 0
this.selectArr.forEach((item) => {
currentFilesSize += item.size
})
if (currentFilesSize / fileMaxSize > 1) {
// 'Upload file size cannot exceed 1G'
this.$alert(this.$t('trials:uploadNonDicoms:message:uploadSize'))
this.btnLoading = false
} else {
this.selectArr.forEach((item) => (item.status = 0))
let num = this.selectArr.length > 6 ? 6 : this.selectArr.length
let funArr = []
let res = await preArchiveStudy({
subjectVisitId: this.currentRow.SourceSubjectVisitId,
isDicom: false,
FileCount: this.selectArr.length,
})
if (res.IsSuccess) {
this.studyMonitorId = res.Result
for (let i = 0; i < num; i++) {
funArr.push(this.handleUploadTask(this.selectArr, i))
}
if (funArr.length > 0) {
let res = await Promise.all(funArr)
}
} else {
this.isFail = true
}
}
},
//
async handleUploadTask(arr, index) {
if (!this.uploadVisible) return
let file = this.fileList.filter((item) => item.id === arr[index].id)[0]
file.status = 1
let path = `/${this.$route.query.trialId}/TaskImage/${
this.currentRow.SubjectId
}/${this.currentRow.VisitTaskId}/${this.$guid()}${file.name
.substring(file.name.lastIndexOf('.'))
.toLocaleLowerCase()}`
file.curPath = path
const fileData = await this.fileToBlob(file.file)
let res = await this.fileToOss(path, fileData, file)
if (res) {
file.status = 2
this.successFileList.push({
fileName: file.name,
filePath: this.$getObjectName(res.url),
fileFize: file.size,
fileType: file.fileType,
})
let flag = arr.every((item) => item.status === 2)
if (flag) {
let RecordPath = await this.uploadRecord(arr)
return this.submitFile(this.successFileList, RecordPath)
}
} else {
file.status = 3
}
let flag = arr.every((item) => item.status > 1)
if (flag) {
let failFileList = arr.filter((item) => item.status === 3)
if (failFileList && failFileList.length > 0) {
let RecordPath = await this.uploadRecord(arr)
this.$refs.filesTable.clearSelection()
failFileList.forEach((row) => {
row.uploadFileSize = 0
this.$refs.filesTable.toggleRowSelection(row)
})
this.isFail = true
this.submitFile(this.successFileList, RecordPath, true)
return false
}
}
let ind = arr.findIndex((item) => item.status === 0)
if (ind >= 0) {
return this.handleUploadTask(arr, ind)
} else {
return false
}
},
// fileoss
async fileToOss(path, file, item) {
try {
let res = await this.OSSclient.multipartUpload(
{
path,
file,
speed: true,
},
(percentage, checkpoint, lastPer) => {
item.uploadFileSize += checkpoint.size * (percentage - lastPer)
if (item.uploadFileSize > file.fileSize) {
item.uploadFileSize = file.fileSize
}
}
)
if (res) {
return res
} else {
return false
}
} catch (err) {
console.log(err)
return false
}
},
// Dicom
submitFile(uploadedFileList, RecordPath, isReLoad = false) {
if (!this.uploadVisible) return
this.btnLoading = true
var params = {
trialId: this.$route.query.trialId,
subjectVisitId: this.currentRow.SourceSubjectVisitId,
noneDicomStudyId: this.currentRow.Id,
studyMonitorId: this.studyMonitorId,
VisitTaskId: this.currentRow.VisitTaskId,
uploadedFileList: uploadedFileList,
}
if (RecordPath) {
params.RecordPath = RecordPath.path
params.FailedFileCount = RecordPath.Record.Failed.length
}
uploadNoneDicomFile(params)
.then((res) => {
if (!isReLoad) {
this.resetFileDiaolg()
}
this.getList()
this.$emit('update:isUpload', true)
//
this.$emit('getList')
this.$message.success(
this.$t('trials:uploadNonDicoms:message:uploadedSuccessfully')
)
})
.catch(() => {
this.btnLoading = false
})
},
//
uploadRecord(arr) {
return new Promise(async (resolve) => {
try {
let Record = {
Failed: [],
Existed: [],
Uploaded: [],
FileCount: arr.length,
}
arr.forEach((item) => {
let file = this.fileList.find((data) => data.id === item.id)
if (file.status === 2) {
Record.Uploaded.push(file.curPath)
}
if (file.status === 3) {
Record.Failed.push(file.curPath)
}
})
let text = JSON.stringify(Record)
let logJsonBlob = this.generateTxtFile(text)
let logJsonObjectName = `/${this.$route.query.trialId}/TaskImage/${this.currentRow.SubjectId}/${this.currentRow.VisitTaskId}/${this.studyMonitorId}.txt`
let logRes
try {
logRes = await this.OSSclient.put(logJsonObjectName, logJsonBlob)
if (logRes && logRes.url) {
resolve({ path: this.$getObjectName(logRes.url), Record })
} else {
resolve(false)
}
} catch (e) {
resolve(false)
}
} catch (err) {
console.log(err)
resolve(false)
}
})
},
generateTxtFile(text) {
let blob = new Blob(['\ufeff', text], { type: 'text/plain' })
return blob
},
},
}
</script>
<style lang="scss" scoped>
.top {
margin: 10px 0;
}
.upload {
display: inline-block;
height: 30px;
width: 90px;
padding: 2px 10px;
line-height: 23px;
position: relative;
text-decoration: none;
border-radius: 3px;
overflow: hidden;
text-align: center;
background: #428bca;
border-color: #428bca;
color: #fff;
.select-file {
height: 30px;
width: 90px;
position: absolute;
overflow: hidden;
left: 0;
top: 0;
opacity: 0;
font-size: 0;
}
.btn-select {
//
width: 90px;
height: 30px;
line-height: 30px;
text-align: center;
cursor: pointer;
border-radius: 24px;
overflow: hidden;
position: absolute;
top: 0;
left: 0;
pointer-events: none; //pointer-events:none穿
}
}
.tip {
display: flex;
align-items: flex-start;
margin-top: 5px;
i {
margin: 3px 5px 0 0;
}
}
</style>

View File

@ -0,0 +1,291 @@
<template>
<div v-if="model_cfg.visible">
<base-model :config="model_cfg">
<div slot="dialog-body">
<el-table :data="modelList" style="width: 100%" height="300">
<!--检查编号-->
<el-table-column
prop="StudyCode"
:label="$t('trials:uploadImage:table:StudyCode')"
/>
<!--检查类型-->
<el-table-column
prop="ModalityForEdit"
:label="$t('trials:uploadImage:table:ModalityForEdit')"
v-if="IsDicom"
/>
<!--检查模态-->
<el-table-column
prop="Modalities"
:label="$t('trials:uploadImage:table:Modalities')"
v-if="IsDicom"
/>
<!--检查部位-->
<el-table-column
prop="BodyPartForEdit"
:label="$t('trials:uploadImage:table:BodyPartForEdit')"
v-if="IsDicom"
>
<template slot-scope="scope">
<span>{{
$fd(
'Bodypart',
scope.row.BodyPartForEdit,
'Code',
{ Bodypart: bodyPart },
'Name'
)
}}</span>
</template>
</el-table-column>
<!--序列数量-->
<el-table-column
prop="SeriesCount"
:label="$t('trials:uploadImage:table:SeriesCount')"
v-if="IsDicom"
/>
<!--图像数量-->
<el-table-column
prop="InstanceCount"
:label="$t('trials:uploadImage:table:InstanceCount')"
v-if="IsDicom"
/>
<!--检查时间-->
<el-table-column
prop="StudyTime"
:label="$t('trials:uploadImage:table:StudyTime')"
v-if="IsDicom"
min-width="130"
/>
<!--检查模态-->
<el-table-column
prop="Modality"
:label="$t('trials:uploadImage:table:Modalities')"
v-if="!IsDicom"
/>
<!--检查部位-->
<el-table-column
prop="BodyPart"
:label="$t('trials:uploadImage:table:BodyPartForEdit')"
v-if="!IsDicom"
>
<template slot-scope="scope">
<span>{{
$fd(
'Bodypart',
scope.row.BodyPart,
'Code',
{ Bodypart: bodyPart },
'Name'
)
}}</span>
</template>
</el-table-column>
<!--文件数量-->
<el-table-column
prop="FileCount"
:label="$t('trials:uploadImage:table:FileCount')"
v-if="!IsDicom"
/>
<!--检查时间-->
<el-table-column
prop="ImageDate"
:label="$t('trials:uploadImage:table:StudyTime')"
v-if="!IsDicom"
min-width="130"
/>
<el-table-column
:label="$t('common:action:action')"
fixed="right"
width="80"
>
<template slot-scope="scope">
<!--预览--->
<el-button
circle
icon="el-icon-view"
:title="$t('trials:uploadImage:button:preview')"
v-if="!isUpload"
@click.stop="preview(scope.row)"
/>
<!--编辑--->
<el-button
circle
icon="el-icon-edit-outline"
:title="$t('trials:uploadImage:button:edit')"
v-else
@click.stop="openEdit(scope.row)"
/>
</template>
</el-table-column>
</el-table>
</div>
</base-model>
<!--新增检查部位-->
<base-model v-if="editModality_model.visible" :config="editModality_model">
<template slot="dialog-body">
<el-form
ref="editModalityform"
:inline="true"
:model="form"
class="demo-form-inline"
:rules="rules"
>
<el-form-item
:label="$t('trials:uploadImage:form:ModalityForEdit')"
prop="Modality"
label-width="150px"
>
<el-select v-model="form.Modality" placeholder="">
<el-option
v-for="item in TrialModality"
:key="item"
:label="item"
:value="item"
>
</el-option>
</el-select>
</el-form-item>
</el-form>
</template>
<template slot="dialog-footer">
<el-button
type="primary"
@click="updateTaskStudyModality"
:loading="loading"
>
{{ $t('common:button:confirm') }}
</el-button>
<el-button
@click="editModality_model.visible = false"
:loading="loading"
>
{{ $t('common:button:cancel') }}
</el-button>
</template>
</base-model>
</div>
</template>
<script>
import baseModel from '@/components/BaseModel'
import { getToken } from '@/utils/auth'
import { updateTaskStudyModality } from '@/api/load.js'
export default {
name: 'studyView',
props: {
model_cfg: {
type: Object,
default: () => {
return {}
},
},
bodyPart: {
type: Array,
default: () => {
return []
},
},
modelList: {
type: Array,
default: () => {
return []
},
},
TrialModality: {
type: Array,
default: () => {
return []
},
},
visitTaskId: {
type: String,
},
IsDicom: {
required: true,
type: Boolean,
default: true,
},
isUpload: {
required: true,
type: Boolean,
default: false,
},
},
components: {
'base-model': baseModel,
},
data() {
return {
form: {
Modality: null,
TaskStudyId: null,
},
editModality_model: {
visible: false,
title: this.$t('trials:uploadImage:button:edit'),
width: '500px',
appendToBody: true,
},
rules: {
Modality: [
{
required: true,
message: this.$t('trials:uploadImage:format:notModality'),
trigger: ['blur', 'change'],
},
],
},
loading: false,
}
},
methods: {
//
preview(row) {
let routeData = null
if (this.IsDicom) {
var token = getToken()
routeData = this.$router.resolve({
path: `/showdicom?studyId=${row.Id}&TokenKey=${token}&type=Study&visitTaskId=${this.visitTaskId}`,
})
} else {
let trialId = this.$route.query.trialId
var token = getToken()
routeData = this.$router.resolve({
path: `/showNoneDicoms?trialId=${trialId}&subjectVisitId=${row.SourceSubjectVisitId}&studyId=${row.Id}&TokenKey=${token}`,
})
}
window.open(routeData.href, '_blank')
},
//
openEdit(row) {
this.form.TaskStudyId = row.Id
this.form.Modality = row.ModalityForEdit
this.editModality_model.visible = true
},
//
async updateTaskStudyModality() {
try {
let validate = await this.$refs.editModalityform.validate()
if (!validate) return false
this.loading = true
let res = await updateTaskStudyModality(this.form)
this.loading = false
if (res.IsSuccess) {
this.modelList.some((item) => {
if (this.form.TaskStudyId === item.Id) {
item.ModalityForEdit = this.form.Modality
}
return this.form.TaskStudyId === item.Id
})
this.editModality_model.visible = false
this.$emit('getList')
}
} catch (err) {
console.log(err)
}
},
},
}
</script>
<style lang="scss" scoped>
</style>

View File

@ -102,7 +102,6 @@
import uploadList from "./components/upload-list.vue";
import studyView from "./components/study-view.vue";
import { getSubjectImageUploadList, deleteTaskStudy } from "@/api/load.js";
import { downloadImage } from "@/utils/uploadZip.js";
import store from "@/store";
export default {
name: "uploadImage",
@ -591,7 +590,7 @@ export default {
files.push(obj);
}
store.dispatch("trials/setUnLock", true);
let res = await downloadImage(zipName, files);
// let res = await downloadImage(zipName, files);
store.dispatch("trials/setUnLock", false);
},
},

View File

@ -28,25 +28,22 @@ Vue.use(permission)
import Viewer from 'v-viewer'
import './assets/css/viewer.css'
Vue.use(Viewer)
Viewer.setDefaults({
Options: {
'inline': true,
'button': true,
'navbar': true,
'title': true,
'toolbar': true,
'tooltip': true,
'movable': true,
'zoomable': true,
'rotatable': true,
'scalable': true,
'transition': true,
'keyboard': true,
'url': 'data-source'
// navbar: true, //底部缩略图
toolbar: {
zoomIn: true,
zoomOut: true,
reset: true,
prev: true,
next: true,
rotateLeft: true,
rotateRight: true,
flipHorizontal: true,
flipVertical: true,
}
})
Vue.use(Viewer)
import hasPermi from './directive/permission'
Vue.use(hasPermi)
@ -148,6 +145,12 @@ Vue.config.productionTip = false
Vue.prototype.$upload = () => {
_vm.$forceUpdate()
}
Vue.prototype.$guid = () => {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
Vue.prototype.fileToBlob = (file) => {
// 创建 FileReader 对象
const reader = new FileReader()
@ -252,6 +255,19 @@ async function VueInit() {
} catch (e) {
}
}
Vue.prototype.$company = (isShort = true) => {
let companyName = null
if (localStorage.getItem('CompanyInfo')) {
let CompanyInfo = JSON.parse(localStorage.getItem('CompanyInfo'))
let isZh = zzSessionStorage.getItem('lang') === 'zh' || _vm.$i18n.locale == 'zh'
if (isZh) {
companyName = isShort ? CompanyInfo.CompanyShortNameCN : CompanyInfo.CompanyNameCN
} else {
companyName = isShort ? CompanyInfo.CompanyShortName : CompanyInfo.CompanyName
}
}
return companyName;
}
Vue.prototype.$updateDictionary = function () {
Vue.prototype.$d = function (code) {
var dictInfo = res.Result
@ -296,7 +312,8 @@ async function VueInit() {
data: {
unlock: {
my_username: null,
my_password: null
my_password: null,
view: false
}
},
render: h => h(App)
@ -368,7 +385,7 @@ async function VueInit() {
// process.env.VUE_APP_LOGOUT_FOR_TIME
// eval(process.env.VUE_APP_LOCK_FOR_PERMISSION)
// process.env.VUE_APP_LOCK_FOR_TIME
window.VUE_APP_COMPANY_NAME = process.env.VUE_APP_COMPANY_NAME;
// window.VUE_APP_COMPANY_NAME = process.env.VUE_APP_COMPANY_NAME;
waitOperate(eval(process.env.VUE_APP_LOGOUT_FOR_PERMISSION) ? () => {
var lang = zzSessionStorage.getItem('lang') ? zzSessionStorage.getItem('lang') : 'zh'
if (_vm.$store.state.trials.unlock || _vm.$route.path === '/ReviewersResearchForm' || _vm.$route.path === '/ReviewersResearch' || _vm.$route.path === '/login' || _vm.$route.path === '/researchForm' || _vm.$route.path === '/researchDetail_m' || _vm.$route.path === '/researchLogin_m' || _vm.$route.path === '/researchLogin' || _vm.$route.path === '/email-recompose' || _vm.$route.path === '/recompose' || _vm.$route.path === '/resetpassword' || _vm.$route.path === '/error') {
@ -440,7 +457,8 @@ async function VueInit() {
_vm.$message.success(_vm.$t("env:lock:msgBox:lockSuccess"))
_vm.unlock = {
my_username: null,
my_password: null
my_password: null,
view: false
}
isOpen = false
count = 0;
@ -504,16 +522,21 @@ async function VueInit() {
]),
h('el-form-item', {
props: { label: _vm.$t("env:lock:msgBox:form:Password") },
attrs: {
style: "position: relative;"
}
}, [
h('input', {
props: {
value: _vm.unlock.my_password
},
ref: "unlock_my_password_input",
attrs: {
id: 'my_password',
class: 'el-input__inner',
type: 'password',
autocomplete: 'new-password'
type: _vm.unlock.view ? 'text' : 'password',
autocomplete: 'new-password',
style: "padding-right:25px"
},
on: {
change: (event) => {
@ -523,7 +546,24 @@ async function VueInit() {
_vm.unlock.my_password = event.target.value
}
}
})
}),
h('i', {
attrs: {
id: 'my_password_view',
class: "el-icon-view",
style: "cursor: pointer;position: absolute;top:35%;right:10px"
},
on: {
click: (event) => {
_vm.unlock.view = !_vm.unlock.view
if (_vm.unlock.view) {
_vm.$refs['unlock_my_password_input'].type = "text"
} else {
_vm.$refs['unlock_my_password_input'].type = "password"
}
},
}
}),
])
])
])

View File

@ -6,11 +6,12 @@ import 'nprogress/nprogress.css'
import { getToken } from '@/utils/auth'
import Vue from 'vue'
import { OSSclient } from './utils/oss'
import WHITELIST from "./utils/whiteList"
// import getPageTitle from '@/utils/get-page-title'
NProgress.configure({ showSpinner: false })
const whiteList = ['/ReviewersResearch', '/login', '/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 = WHITELIST
store.state.trials.whiteList = whiteList;
router.beforeEach(async (to, from, next) => {
NProgress.start()
@ -25,15 +26,15 @@ router.beforeEach(async (to, from, next) => {
if (to.path === '/login' || to.path === '/recompose' || to.path === '/email-recompose' || to.path === '/error' || to.path === '/ReviewersResearchForm' || to.path === '/ReviewersResearch') {
if (to.path === '/ReviewersResearch') {
await this.$store.dispatch('user/logout')
OSSclient()
await OSSclient()
}
if (to.path === '/ReviewersResearchForm') {
OSSclient()
await OSSclient()
}
next()
NProgress.done()
} else {
OSSclient()
await OSSclient()
const hasGetUserInfo = store.getters.userId
if (hasGetUserInfo) {
next()
@ -66,7 +67,7 @@ router.beforeEach(async (to, from, next) => {
} else {
// 在免登录whiteList中直接进入
if (to.path === '/readingDicoms' || to.path === '/noneDicomReading') {
OSSclient()
await OSSclient()
}
next()
}

View File

@ -35,26 +35,50 @@ const getDefaultState = () => {
}
}
function getQuestions(questions) {
const criterionType = parseInt(localStorage.getItem('CriterionType'))
questions.forEach(item => {
if (item.Type === 'table' && item.TableQuestions && item.TableQuestions.Answers.length > 0) {
if ((item.Type === 'table' || item.Type === 'basicTable') && item.TableQuestions && item.TableQuestions.Answers.length > 0) {
item.TableQuestions.Answers.forEach(answerObj => {
answerObj.lesionPart = getQuestionAnswer(item.TableQuestions.Questions, 8, answerObj)
answerObj.loctation = getQuestionAnswer(item.TableQuestions.Questions, 6, answerObj)
answerObj.lesionLength = getQuestionAnswer(item.TableQuestions.Questions, 0, answerObj)
answerObj.lesionShort = getQuestionAnswer(item.TableQuestions.Questions, 1, answerObj)
answerObj.isDicomReading = answerObj.IsDicomReading === 'True'
var isLymphLesion = getQuestionAnswer(item.TableQuestions.Questions, 2, answerObj)
isLymphLesion = isLymphLesion ? parseInt(isLymphLesion) : null
answerObj.isLymphLesion = isLymphLesion
answerObj.lesionState = getQuestionAnswer(item.TableQuestions.Questions, 7, answerObj)
var lesionNum = getQuestionAnswer(item.TableQuestions.Questions, 11, answerObj)
answerObj.lesionNum = lesionNum
if (answerObj.RowId) {
var idx = item.TableQuestions.Questions.findIndex(i => i.QuestionMark === 11)
if (idx > -1) {
if (criterionType === 10) {
// pcwg
var lesionNum = getQuestionAnswer(item.TableQuestions.Questions, 11, answerObj)
answerObj.lesionNum = lesionNum
answerObj.saveTypeEnum = isNaN(parseInt(answerObj.lesionNum)) ? 1 : 2
} else if (criterionType === 19) {
// ivus
answerObj.area1 = getQuestionAnswer(item.TableQuestions.Questions, 1001, answerObj)
answerObj.area2 = getQuestionAnswer(item.TableQuestions.Questions, 1002, answerObj)
const v = getQuestionAnswer(item.TableQuestions.Questions, 1003, answerObj)
answerObj.diff = v
answerObj.saveTypeEnum = isNaN(parseFloat(v)) ? 1 : 2
} else if (criterionType === 20) {
// oct
if (item.LesionType === 101) {
answerObj.l1 = getQuestionAnswer(item.TableQuestions.Questions, 1011, answerObj)
answerObj.l2 = getQuestionAnswer(item.TableQuestions.Questions, 1012, answerObj)
answerObj.l3 = getQuestionAnswer(item.TableQuestions.Questions, 1013, answerObj)
const min = getQuestionAnswer(item.TableQuestions.Questions, 1014, answerObj)
answerObj.min = min
const mean = getQuestionAnswer(item.TableQuestions.Questions, 1015, answerObj)
answerObj.mean = mean
answerObj.saveTypeEnum = (isNaN(parseFloat(min)) || isNaN(parseFloat(mean))) ? 1 : 2
} else if (item.LesionType === 103) {
const angle = getQuestionAnswer(item.TableQuestions.Questions, 1016, answerObj)
answerObj.angle = angle
answerObj.saveTypeEnum = isNaN(parseFloat(angle)) ? 1 : 2
}
} else {
answerObj.lesionPart = getQuestionAnswer(item.TableQuestions.Questions, 8, answerObj)
answerObj.loctation = getQuestionAnswer(item.TableQuestions.Questions, 6, answerObj)
answerObj.lesionLength = getQuestionAnswer(item.TableQuestions.Questions, 0, answerObj)
answerObj.lesionShort = getQuestionAnswer(item.TableQuestions.Questions, 1, answerObj)
let isLymphLesion = getQuestionAnswer(item.TableQuestions.Questions, 2, answerObj)
isLymphLesion = isLymphLesion ? parseInt(isLymphLesion) : null
answerObj.isLymphLesion = isLymphLesion
answerObj.lesionState = getQuestionAnswer(item.TableQuestions.Questions, 7, answerObj)
answerObj.saveTypeEnum = isNaN(parseInt(answerObj.lesionState)) ? 1 : 2
}
} else {
@ -70,7 +94,7 @@ function getQuestions(questions) {
}
function findQuestionAndRemoveLesion(questions, obj) {
for (var i = 0; i < questions.length; i++) {
if (questions[i].Type === 'table' && questions[i].TableQuestions && (questions[i].LesionType === obj.lesionType) && questions[i].TableQuestions.Answers.length > 0) {
if ((questions[i].Type === 'table' || questions[i].Type === 'basicTable') && questions[i].TableQuestions && (questions[i].LesionType === obj.lesionType) && questions[i].TableQuestions.Answers.length > 0) {
var idx = questions[i].TableQuestions.Answers.findIndex(i => String(i.RowIndex) === String(obj.rowIndex))
if (idx > -1) {
questions[i].TableQuestions.Answers.splice(idx, 1)
@ -86,7 +110,7 @@ function findQuestionAndRemoveLesion(questions, obj) {
function findQuestionAndUpdateLesion(questions, obj) {
for (var i = 0; i < questions.length; i++) {
var item = questions[i]
if (item.Type === 'table' && item.Id === obj.questionId) {
if ((item.Type === 'table' || item.Type === 'basicTable') && item.Id === obj.questionId) {
var idx = item.TableQuestions.Answers.findIndex(i => i.RowIndex === obj.rowIndex)
item.TableQuestions.Answers[idx].isLymphLesion = obj.isLymphLesion
item.TableQuestions.Answers[idx].loctation = obj.lesionOrgan
@ -115,10 +139,10 @@ function findQuestionAndUpdateLesion(questions, obj) {
}
function findQuestionAndAddLesion(questions, obj) {
for (var i = 0; i < questions.length; i++) {
if (questions[i].Type === 'table' && questions[i].TableQuestions && (questions[i].LesionType === obj.lesionType)) {
if ((questions[i].Type === 'table' || questions[i].Type === 'basicTable') && questions[i].TableQuestions && (questions[i].LesionType === obj.lesionType)) {
var sourceObj = {}
questions[i].TableQuestions.Questions.forEach(item => {
sourceObj[item.Id] = ''
sourceObj[item.Id] = null
})
var targetObj = Object.assign(sourceObj, obj.lesionObj)
targetObj.IsCurrentTaskAdd = 'True'
@ -811,9 +835,16 @@ const actions = {
// }
series.InstanceInfoList.forEach(instance => {
if (instance.NumberOfFrames && instance.NumberOfFrames > 1) {
for (let i = 0; i < instance.NumberOfFrames; i++) {
const imageId = `wadouri:${localStorage.getItem('location') !== 'USA' ? Vue.prototype.OSSclientConfig.basePath : Vue.prototype.OSSclientConfig.basePath}${instance.Path}?frame=${i}&instanceId=${instance.Id}&visitTaskId=${obj.visitTaskId}&idx=${studyIndex}|${seriesIndex}|${index}`
imageIds.push(imageId)
if (study.IsCriticalSequence && instance.KeyFramesList.length > 0) {
instance.KeyFramesList.map(i => {
const imageId = `wadouri:${localStorage.getItem('location') !== 'USA' ? Vue.prototype.OSSclientConfig.basePath : Vue.prototype.OSSclientConfig.basePath}${instance.Path}?frame=${i}&instanceId=${instance.Id}&visitTaskId=${obj.visitTaskId}&idx=${studyIndex}|${seriesIndex}|${index}`
imageIds.push(imageId)
})
} else {
for (let i = 0; i < instance.NumberOfFrames; i++) {
const imageId = `wadouri:${localStorage.getItem('location') !== 'USA' ? Vue.prototype.OSSclientConfig.basePath : Vue.prototype.OSSclientConfig.basePath}${instance.Path}?frame=${i}&instanceId=${instance.Id}&visitTaskId=${obj.visitTaskId}&idx=${studyIndex}|${seriesIndex}|${index}`
imageIds.push(imageId)
}
}
instance.ImageId = `wadouri:${localStorage.getItem('location') !== 'USA' ? Vue.prototype.OSSclientConfig.basePath : Vue.prototype.OSSclientConfig.basePath}${instance.Path}?frame=${0}&instanceId=${instance.Id}&visitTaskId=${obj.visitTaskId}&idx=${studyIndex}|${seriesIndex}|${index}`
} else {
@ -1053,7 +1084,10 @@ const actions = {
return new Promise(resolve => {
var isReadingTaskViewInOrder = localStorage.getItem('isReadingTaskViewInOrder')
if (parseInt(isReadingTaskViewInOrder) === 2) {
if (!state.lastCanvasTaskId) state.lastCanvasTaskId = taskId
if (!state.lastCanvasTaskId) {
console.log('setLastCanvasTaskId')
state.lastCanvasTaskId = taskId
}
} else {
state.lastCanvasTaskId = taskId
}

View File

@ -9,7 +9,7 @@ const getDefaultState = () => {
studyListQuery: null,
unlock: false,
config: {},
uploadTip: null,
uploadTip: '0.00KB/s',
timer: null,
whiteList: [],
checkTaskId: null

View File

@ -97,6 +97,7 @@ const actions = {
zzSessionStorage.setItem('my_username', username.trim())
zzSessionStorage.setItem('my_password', md5(password))
zzSessionStorage.setItem('my_EMail', response.Result.BasicInfo.EMail)
localStorage.setItem('CompanyInfo', JSON.stringify(response.Result.CompanyInfo))
const data = response.Result
if (data.BasicInfo.IsFirstAdd || data.BasicInfo.LoginState === 1) {
try {

View File

@ -1,14 +1,14 @@
import Vue from 'vue'
import { anonymization } from './anonymization'
export const dcmUpload = async function (name, file, config) {
export const dcmUpload = async function (data, config, progressFn) {
return new Promise(async resolve => {
try {
// let blob = await encoder(file, config)
let blob = await fileToBlob(file)
let blob = await fileToBlob(data.file)
if (config) {
blob = await anonymization(file, config)
blob = await anonymization(data.file, config)
}
let res = await Vue.prototype.OSSclient.put(name, blob.blob)
let res = await Vue.prototype.OSSclient.multipartUpload(Object.assign(data, { file: blob.blob }), progressFn)
resolve({
...res,
image: blob.pixelDataElement
@ -19,7 +19,7 @@ export const dcmUpload = async function (name, file, config) {
} catch (e) {
console.log(file, 'warning')
console.log(data.file, 'warning')
resolve(false)
console.log(e)
}

View File

@ -0,0 +1,44 @@
// crypto.js
import CryptoJS from 'crypto-js';
import { JSEncrypt } from "jsencrypt";
import { getPublicKey } from "@/api/user.js"
export const Crypto = {
AES: {
encrypt: function (plaintext, secretKey) {
return CryptoJS.AES.encrypt(plaintext, CryptoJS.enc.Utf8.parse(secretKey), {
mode: CryptoJS.mode.ECB,
padding: CryptoJS.pad.Pkcs7,
iv: ''
}).ciphertext.toString(CryptoJS.enc.Base64);
},
decrypt: function (ciphertext, secretKey) {
const bytes = CryptoJS.AES.decrypt(ciphertext, secretKey);
return bytes.toString(CryptoJS.enc.Utf8);
}
}
};
export const Encrypt = {
encrypt: async function (plaintext) {
let PublicKey = null;
if (sessionStorage.getItem('PublicKey')) {
PublicKey = atob(sessionStorage.getItem('PublicKey'));
} else {
try {
let res = await getPublicKey();
if (res.IsSuccess) {
PublicKey = atob(res.Result)
sessionStorage.setItem('PublicKey', res.Result)
} else {
return false;
}
} catch (err) {
console.log(err)
}
}
let encryptor = new JSEncrypt()
encryptor.setPublicKey(PublicKey)
return encryptor.encrypt(plaintext)
},
};

View File

@ -0,0 +1,30 @@
import { Encrypt, Crypto } from "./crypto";
export const encryptConfig = async (config) => {
let secretKey = randomRange(32, 32);
let encryptSecretKey = await Encrypt.encrypt(secretKey); // 密钥进行非对称加密
if (encryptSecretKey) {
config.headers['X-Encrypted-Key'] = encryptSecretKey;
}
if (config.data && Object.prototype.toString.call(config.data) === '[object Object]') {
Object.keys(config.data).forEach(async key => {
config.data[key] = Crypto.AES.encrypt(config.data[key], secretKey)
console.log(config.data[key], 'KEY')
// config.data[key] = await Encrypt.encrypt(config.data[key])
})
}
return config;
}
const randomRange = (min, max, charStr) => {
var returnStr = "",
range;
if (typeof max == 'string') {
charStr = max;
}
range = ((max && typeof max == 'number') ? Math.round(Math.random() * (max - min)) + min : min);
charStr = charStr || 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for (var i = 0; i < range; i++) {
var index = Math.round(Math.random() * (charStr.length - 1));
returnStr += charStr.substring(index, index + 1);
}
return returnStr;
}

View File

@ -0,0 +1,457 @@
const {
CreateMultipartUploadCommand,
UploadPartCommand,
CompleteMultipartUploadCommand,
ListMultipartUploadsCommand,//bucket中正在上传的文件列表
ListPartsCommand,//列出文件已上传的分片
GetObjectCommand,//获取文件
} = require("@aws-sdk/client-s3");
import SparkMD5 from "./spark-md5.min.js";
import store from "@/store";
let timer = null, // 网速定时器
bytesReceivedPerSecond = {}; // 时间节点上传文件总量
export function AWSclose() {
if (timer) {
clearInterval(timer);
timer = null;
store.state.trials.uploadTip = '0KB/s'
}
bytesReceivedPerSecond = {};
}
//查询文件是否存在于bucket或者正在上传
export async function exist(s3, bucket, fileInformation, progressFn, changeStatus) {
// 拿到上传到的file
const File = fileInformation.file;
// 拿到上传的size
const uploadFileSize = File.size; // 这里拿到的单位是字节(uploadFileSize/ 1024 / 1024
// = 多少兆)
// 设置每一片的大小,shardSize 指定上传的每个分片的大小范围为100 KB~5 GB。
// 分片标准为5MB,文件总大小大于5GB分片为20MB
let shardSize = 5 * 1024 * 1024;
if (uploadFileSize < shardSize) {
shardSize = uploadFileSize;
}
if (uploadFileSize > 5 * 1024 * 1024 * 1024) {
shardSize = 20 * 1024 * 1024;
}
fileInformation = Object.assign({
shardSize,
sharding: []
}, fileInformation)
if (fileInformation.speed) {
setTimer();
}
// 1、查询该文件是否已上传到bucket
//判断sharding里面是否有东西有东西证明已经上传过分片了不需要再进行检测
if (fileInformation.sharding.length === 0) {
let existBucket = await existInBucket({ s3, bucket, fileInformation: fileInformation });
if (existBucket === 'true') {
progressFn(1, fileInformation.file, 0);
changeStatus(fileInformation.path, 'success');//直接告诉前端,状态
return;
} else if (existBucket === 'same key') {
console.log(fileInformation.path + " bucket中存在同名不同内容的文件");
} else if (existBucket === 'not exist') {
console.log(fileInformation.path + " bucket中不存在该文件");
}
//2、查询该文件是否存在上传事件
let upload = await existUpload({ s3, bucket: bucket, fileInformation: fileInformation });
if (upload.code === 0) {
//存在该上传事件并且已经上传了多个分片
console.log(fileInformation.path + " 存在上传事件,并已经上传多个分片");
//将分片存入sharding
const uploadId = upload.uploadId;
let parts = upload.parts;
let SIZE = 0;
for (let i = 0; i < parts.length; i++) {
SIZE += parts[i].Size;
fileInformation.sharding.push({ ETag: parts[i].ETag, PartNumber: parts[i].PartNumber, Size: parts[i].Size, UploadId: uploadId });
}
progressFn(SIZE / uploadFileSize, { fileSize: uploadFileSize }, 0);//告诉前端,加入分片
//重新上传
await uploadFile({ fileInformation: fileInformation, uploadId: uploadId, bucket, changeStatus, getSuspend, progressFn });
} else if (upload.code === 1) {
// //重名但是不同文件
console.log('err 重名文件')
changeStatus(fileInformation.path, 'same key');
} else if (upload.code === 2) {
//没有上传事件
console.log(fileInformation.path + " 不存在上传事件");
//建立分段上传事件
const connect = await createMultipartUpload({ s3, bucket: bucket, key: fileInformation.path, type: fileInformation.file.type });
//上传整个文件
await uploadFile({ s3, fileInformation: fileInformation, uploadId: connect.UploadId, bucket: bucket, changeStatus, progressFn });
}
} else {
//分片组里面有东西
//重新上传
await uploadFile({ s3, fileInformation: fileInformation, uploadId: fileInformation.sharding[0].UploadId, bucket, changeStatus, progressFn });
}
}
//上传文件未上传的所有分片
async function uploadFile({ s3, fileInformation, uploadId, bucket, changeStatus, progressFn }) {// file:上传文件, uploadId parts:已上传的分片
const chunkCount = Math.ceil(fileInformation.file.size / fileInformation.shardSize)//总分片数
//循环切片并上传
for (let i = 0; i < chunkCount; i++) {
let start = i * fileInformation.shardSize;//文件分片开始位置
let end = Math.min(fileInformation.file.size, start + fileInformation.shardSize)//文件分片结束位置
let _chunkFile = fileInformation.file.slice(start, end);//切片文件 即 待上传文件分片
//判断parts中是否存在该分片
let res1 = fileInformation.sharding.filter((part) => {
return part.PartNumber === (i + 1);
});
if (res1.length === 0) {
//不包含该分片
const upload = await uploadPart({ s3, f: _chunkFile, uploadId: uploadId, key: fileInformation.path, bucket: bucket, num: i + 1 });//将分片上传
//判断sharding中是否存在该分片如果不存在的话才判错
let res2 = fileInformation.sharding.filter((part) => {
return part.PartNumber === (i + 1);
});
if (res2.length === 0) {
if (upload !== 'err') {//上传分片成功,并且没有暂停上传
//判断是否存在该分片
//判断parts中是否存在该分片
let res3 = fileInformation.sharding.filter((part) => {
return part.PartNumber === (i + 1);
});
if (res3.length === 0) {
let LASTSIZE = fileInformation.sharding.reduce((sum, item) => sum + item.Size, 0)
fileInformation.sharding.push({ ETag: upload.ETag, PartNumber: i + 1, Size: _chunkFile.size, UploadId: uploadId });//上传成功存到sharding
let SIZE = fileInformation.sharding.reduce((sum, item) => sum + item.Size, 0)
let lastPercentage = LASTSIZE / fileInformation.file.size, percentage = SIZE / fileInformation.file.size;
progressFn(percentage, fileInformation.file, lastPercentage);
if (fileInformation.speed) {
let time = new Date().getTime();
let timeList = Object.keys(bytesReceivedPerSecond).sort((a, b) => a - b);
let bytesTime = timeList.find(item => time - item < 1000);
if (bytesTime) {
bytesReceivedPerSecond[bytesTime] += fileInformation.file.size * (percentage - lastPercentage);
} else {
console.log("未查询到时间")
if (timeList.length > 0) {
bytesReceivedPerSecond[timeList[timeList.length - 1]] += fileInformation.file.size * (percentage - lastPercentage);
} else {
bytesReceivedPerSecond[time] = fileInformation.file.size * (percentage - lastPercentage);
}
}
}
}
} else if (upload === 'err') {
changeStatus(fileInformation.path, 'err');
return;
}
}
}
}//for
if (fileInformation.sharding.length === chunkCount) {
//合并分片
const complete = await completeMultipartUpload({ s3, bucket: bucket, key: fileInformation.path, sharding: fileInformation.sharding, uploadId: uploadId });
if (complete !== 'err') {
changeStatus(fileInformation.path, 'success');//通知前端,上传成功
} else {
changeStatus(fileInformation.path, 'err');//通知前端,上传失败
}
}
}
// 判断该文件是否已经存在于bucket
// bucket file:上传文件
// 返回值 'same key':同名不同文件 'not exist':不存在该文件 'true':该文件已存在bucket中
async function existInBucket({ s3, bucket, fileInformation }) {
if (s3 === null) {
return console.log("未创建s3客户端请先调用init事件");
}
//getObject 每次最多传回767448b的数据所以要分段请求
let bucketFileUniArray = [];
// 分段
let count = Math.ceil(fileInformation.file.size / 767448);
if (count > 4) {
count = 4;
}
for (let i = 0; i < count; i++) {
const obj = await getObject({ s3, bucket: bucket, fileInformation: fileInformation, count: i });
if (obj !== 'err') {
//获取文件的文件体 计算某个分片的md5
const fileBody = obj.Body;
let fileUnitArray = await fileBody.transformToByteArray();
bucketFileUniArray = [...bucketFileUniArray, ...fileUnitArray];
} else {
return 'not exist';
}
}
let bucketFileBufferArray = new Uint8Array(bucketFileUniArray);
// 将传入文件的fileReader 转成 arrayBuffer
let fileArrayBuff = null;
fileArrayBuff = await new Promise((resolve) => {
let fileReader = new FileReader();
fileReader.readAsArrayBuffer(fileInformation.file.slice(0, count * 767448));
fileReader.onload = (e) => {
resolve(e.target.result);
};
});
if (fileArrayBuff.byteLength > count * 767448) {
fileArrayBuff = fileArrayBuff.slice(0, count * 767448);
}
let bodyMD5 = await getMD5({ arrayBuffer: bucketFileBufferArray.buffer });
let fileMD5 = await getMD5({ arrayBuffer: fileArrayBuff });
if (bodyMD5 === fileMD5) {
//证明是同一个文件 秒传
return 'true';
} else {
return 'same key';
}
}
//判断该文件是否正在上传
// bucket:bucket file:上传文件
//返回值 'not exist upload':不存在上传事件 'same key':同名不同文件
async function existUpload({ s3, bucket, fileInformation }) {
//判断该文件是否有上传事件
const listUploads = await listMultipartUploadsCommand({ s3, bucket: bucket, key: fileInformation.path });
if (listUploads !== 'err') {
if (listUploads.Uploads !== undefined && listUploads.Uploads.length > 0) {
//存在上传事件 获取上传的第一个分片的eTag计算传入文件md5相比较是否相同
const uploads = listUploads.Uploads;
for (const one in uploads) {//可能存在多个连接
let uploadOne = uploads[one];
const uploadId = uploadOne.UploadId;//UploadId
const key = uploadOne.Key;//key
//查询该文件已上传分片
const listParts = await listPartsCommand({ s3, bucket: bucket, key: key, uploadId: uploadId });
if (listParts !== 'err') {
if (listParts.Parts !== undefined && listParts.Parts.length !== 0) {
//存在分片
let etag = listParts.Parts[0].ETag;
//计算文件的第一个分片的md5
let fileSlice = null;
if (fileInformation.file.size > fileInformation.shardSize) {
fileSlice = fileInformation.file.slice(0, fileInformation.shardSize);
} else {
fileSlice = fileInformation.file;
}
let fileMD5 = await new Promise((resolve) => {
const fileReader = new FileReader();
var spark = new SparkMD5.ArrayBuffer();
fileReader.readAsArrayBuffer(fileSlice);
fileReader.onload = (e) => {
spark.append(e.target.result);
var m = spark.end();
resolve(m);
};
});
if (etag.split('"')[1] === fileMD5) {
//是同一个文件上传
return {
code: 0,
message: 'true',
uploadId: uploadId,
key: key,
parts: listParts.Parts
}
} else {
//同名不同文件
continue;
}
} else {
//该文件有进行上传,但没有上传完成一个分片
continue;
}
} else {
//有连接,没上传分片
continue;
}
}//for
return {
code: 1,
message: 'same key'
}
} else {
//无连接
return {
code: 2,
message: 'not exist upload'
};
}
} else {
//无连接
return {
code: 2,
message: 'not exist upload'
};
}
}
//计算arrayBuffer的md5值
async function getMD5({ arrayBuffer }) {
return await new Promise((resolve) => {
const spark = new SparkMD5.ArrayBuffer();
spark.append(arrayBuffer);
const m = spark.end();
resolve(m);
});
}
//建立文件上传事件
async function createMultipartUpload({ s3, bucket, key, type }) {//bucket:bucket key:文件名 type文件类型
if (s3 === null) {
return console.log("未创建s3客户端请先调用init事件");
}
const params = {
Bucket: bucket,
Key: key,
ContentType: type
};
const res = async () => {
try {
return await s3.send(new CreateMultipartUploadCommand(params));
} catch (err) {
console.log('建立上传事件失败:', err.message)
return 'err';
}
}
return res();
}
//上传一个分片
async function uploadPart({ s3, f, uploadId, key, bucket, num }) { //f:文件分片num分片标号
if (s3 === null) {
return console.log("未创建s3客户端请先调用init事件");
}
const params = {
Bucket: bucket,
Key: key,
PartNumber: num,
UploadId: uploadId,
Body: f,
// ContentDisposition: "attachment; filename=hahaha.dcm"
};
const res = async () => {
try {
return await s3.send(new UploadPartCommand(params));
} catch (err) {
console.log('上传分片第 ' + num + ' 片错误信息', err.message)
return 'err';
}
}
return res();
}
//将分片合并
async function completeMultipartUpload({ s3, bucket, key, sharding, uploadId }) {
if (s3 === null) {
return console.log("未创建s3客户端请先调用init事件");
}
let parts = [];
for (let i = 0; i < sharding.length; i++) {
parts.push({
"ETag": sharding[i].ETag,
"PartNumber": sharding[i].PartNumber,
})
}
const params = {
Bucket: bucket,
Key: key,
MultipartUpload: {
Parts: parts
},
UploadId: uploadId
};
const res = async () => {
try {
return await s3.send(new CompleteMultipartUploadCommand(params))
} catch (err) {
console.log("合并分片失败: ", err.message);
return 'err';
}
}
return res();
}
//查询某个文件已经上传的所有分片
async function listPartsCommand({ s3, bucket, key, uploadId }) {
if (s3 === null) {
return console.log("未创建s3客户端请先调用init事件");
}
const params = {
Bucket: bucket,
Key: key,
UploadId: uploadId
};
const res = async () => {
try {
return await s3.send(new ListPartsCommand(params));
} catch (err) {
console.log("查询该文件已上传分片失败: " + err.message);
return 'err';
}
}
return res();
}
//查询该文件是否存在上传事件
async function listMultipartUploadsCommand({ s3, bucket, key }) {
if (s3 === null) {
return console.log("未创建s3客户端请先调用init事件");
}
const params = {
Bucket: bucket,
Delimiter: '',
MaxUploads: 1000,
Prefix: key
};
const res = async () => {
try {
return await s3.send(new ListMultipartUploadsCommand(params));
} catch (err) {
console.log("查询 " + key + " 文件是否存在上传事件失败: " + err.message);
return 'err';
}
}
return res();
}
//获取文件
async function getObject({ s3, bucket, fileInformation, count }) {
//一次请求最多 767448
if (s3 === null) {
return console.log("未创建s3客户端请先调用init事件");
}
let byte1 = ((count + 1) * 767448 - 1) > fileInformation.file.size ? fileInformation.file.size : ((count + 1) * 767448 - 1);
let byte2 = (count * 767448) > fileInformation.file.size ? fileInformation.file.size : (count * 767448);
let range = "bytes=" + byte2 + "-" + byte1;
const params = {
Bucket: bucket,
Key: fileInformation.path,
Range: range
};
const res = async () => {
try {
return await s3.send(new GetObjectCommand(params));
} catch (err) {
console.log('获取 ' + fileInformation.path + ' 文件失败:', err.message);
return 'err';
}
}
return res();
}
function setTimer() {
if (timer) return false;
timer = setInterval(() => {
let timeList = Object.keys(bytesReceivedPerSecond).sort((a, b) => a - b);
if (timeList.length > 0) {
let totalBytes = timeList.reduce((sum, bytes) => sum + bytesReceivedPerSecond[bytes], 0) / (5 * 1024);
let unit = 'KB/s';
if (totalBytes > 1024) {
totalBytes = totalBytes / 1024;
unit = "MB/s";
}
store.state.trials.uploadTip = totalBytes.toFixed(2) + unit;
}
if (timeList.length >= 5) {
delete bytesReceivedPerSecond[timeList[0]]
}
let time = new Date().getTime();
bytesReceivedPerSecond[time] = 0;
}, 1000)
}

View File

@ -0,0 +1,161 @@
import moment from "moment";
import store from "@/store";
let savaData = {},
checkData = {}, // 当前上传的节点文件和上一次提交进度
timer = null, // 网速定时器
bytesReceivedPerSecond = {}; // 时间节点上传文件总量
export function OSSclose() {
if (timer) {
clearInterval(timer);
timer = null;
store.state.trials.uploadTip = '0KB/s'
}
bytesReceivedPerSecond = {};
savaData = {};
checkData = {};
saveFinishedData(savaData);
}
export async function customerHttp(OSSclient, data, progressFn) {
// 拿到上传到的file
const uploadFile = data.file;
// 拿到上传的size
const uploadFileSize = uploadFile.size; // 这里拿到的单位是字节(uploadFileSize/ 1024 / 1024
// = 多少兆)
// 设置每一片的大小,partSize 指定上传的每个分片的大小范围为100 KB~5 GB。
// 分片标准为5MB,文件总大小大于5GB分片为20MB
let partSize = 5 * 1024 * 1024;
if (uploadFileSize < partSize) {
partSize = uploadFileSize;
}
if (uploadFileSize > 5 * 1024 * 1024 * 1024) {
partSize = 20 * 1024 * 1024;
}
// 设置所有的文件上传所有的唯一的saveFileId
const saveFileId = `${uploadFileSize}_${data.path}`;
if (data.speed) {
setTimer();
}
initPage();
let res = await multipartUpload(OSSclient, partSize, saveFileId, uploadFile, data, progressFn);
return res;
}
async function multipartUpload(OSSclient, partSize, saveFileId, uploadFile, data, progressFn) {
try {
// object-name目前我是用的uploadFile.name,其实也是要根据你们的项目而定,
// 有没有具体的规定, 要不要加项目名, 要不要加对应的环境;
// 上传的参数
const uploadParams = {
partSize,
progress: (percentage, checkpoint) => {
savaData[saveFileId] = checkpoint;
if (!checkData[saveFileId]) {
checkData[saveFileId] = 0
}
if (data.speed) {
let time = new Date().getTime();
let timeList = Object.keys(bytesReceivedPerSecond).sort((a, b) => a - b);
let bytesTime = timeList.find(item => time - item < 1000);
if (bytesTime) {
bytesReceivedPerSecond[bytesTime] += data.file.size * (percentage - checkData[saveFileId]);
} else {
// console.log("未查询到时间")
if (timeList.length > 0) {
bytesReceivedPerSecond[timeList[timeList.length - 1]] += data.file.size * (percentage - checkData[saveFileId]);
} else {
bytesReceivedPerSecond[time] = data.file.size * (percentage - checkData[saveFileId]);
}
}
}
savaData["lastSaveTime"] = new Date();
progressFn(percentage, data.file, checkData[saveFileId])
checkData[saveFileId] = percentage;
if (percentage === 1) {
delete checkData[saveFileId]
}
// 在上传过程中,把已经上传的数据存储下来
saveFinishedData(savaData);
},
// headers: {
// "Content-Disposition": `attachment; filename=hahaha.dcm`,
// "Cache-Control": "public, no-cache"
// }
};
// 断点续传
await resumeUpload(uploadParams, saveFileId);
const res = await OSSclient.multipartUpload(
data.path,
uploadFile,
uploadParams
);
if (res.res.status === 200) {
// 重新去掉某个缓存进行设置
delete savaData[saveFileId];
saveFinishedData(savaData);
} else if (res.res.status === 404) {
delete savaData[saveFileId];
saveFinishedData(savaData);
}
return res;
} catch (e) {
console.log(e);
delete savaData[saveFileId];
saveFinishedData(savaData);
// 捕获超时异常。
if (e.code === "ConnectionTimeoutError") {
console.log("TimeoutError");
// do ConnectionTimeoutError operation
}
return false;
}
}
async function resumeUpload(uploadParams, saveFileId) {
if (localStorage.getItem("upload-function-name")) {
const obj = JSON.parse(localStorage.getItem("upload-function-name"));
if (Object.keys(obj).includes(saveFileId)) {
uploadParams.checkpoint = obj[saveFileId];
}
}
}
// 存储到内存
function saveFinishedData(finishedData) {
localStorage.setItem(
"upload-function-name",
JSON.stringify(finishedData)
);
}
function initPage() {
// 判断是不是有缓存
const localData = localStorage.getItem("upload-function-name");
if (!localData) return;
savaData = JSON.parse(localData);
// 当前时间 > 存储时间(1000 * 60 * 60表示1h,意思就是这些数据你要存多久,
// 可以是1h也可以是多少天,随意)
if (
moment(new Date()).diff(moment(savaData.lastSaveTime)) >
1000 * 60 * 60
) {
localStorage.removeItem("upload-function-name");
}
}
function setTimer() {
if (timer) return false;
timer = setInterval(() => {
// console.log(Object.assign({}, bytesReceivedPerSecond))
let timeList = Object.keys(bytesReceivedPerSecond).sort((a, b) => a - b);
if (timeList.length > 0) {
let totalBytes = timeList.reduce((sum, bytes) => sum + bytesReceivedPerSecond[bytes], 0) / (5 * 1024);
let unit = 'KB/s';
if (totalBytes > 1024) {
totalBytes = totalBytes / 1024;
unit = "MB/s";
}
store.state.trials.uploadTip = totalBytes.toFixed(2) + unit;
}
if (timeList.length >= 5) {
delete bytesReceivedPerSecond[timeList[0]]
}
let time = new Date().getTime();
bytesReceivedPerSecond[time] = 0;
}, 1000)
}

File diff suppressed because one or more lines are too long

View File

@ -3,30 +3,39 @@ const router = require('@/router');
const Minio = require('minio')
const stream = require('stream')
import Vue from 'vue'
import { customerHttp, OSSclose } from "@/utils/multipartUpload/oss"
import { exist, AWSclose } from "@/utils/multipartUpload/aws"
const { GetObjectStoreToken } = require('../api/user.js')
const {
S3Client,
} = require("@aws-sdk/client-s3");
Vue.prototype.OSSclientConfig = {
}
function blobToBuffer(blob, fileName) {
return new Promise((resolve, reject) => {
const file = new File([blob], fileName);
resolve(file)
})
}
async function ossGenerateSTS() {
let res = await GetObjectStoreToken()
let res = null;
if (localStorage.getItem('stsToken')) {
res = JSON.parse(localStorage.getItem('stsToken'))
} else {
res = await GetObjectStoreToken()
localStorage.setItem('stsToken', JSON.stringify(res))
}
// res.Result.ObjectStoreUse = 'AWS';
Vue.prototype.OSSclientConfig = { ...res.Result[res.Result.ObjectStoreUse] }
Vue.prototype.OSSclientConfig.ObjectStoreUse = res.Result.ObjectStoreUse;
Vue.prototype.OSSclientConfig.basePath = Vue.prototype.OSSclientConfig.viewEndpoint
switch (res.Result.ObjectStoreUse) {
case 'AliyunOSS':
Vue.prototype.OSSclientConfig.bucket = Vue.prototype.OSSclientConfig.bucketName
Vue.prototype.OSSclientConfig.stsToken = Vue.prototype.OSSclientConfig.securityToken
Vue.prototype.OSSclientConfig.timeout = 10 * 60 * 1000
let OSSclient = new OSS(Vue.prototype.OSSclientConfig)
Vue.prototype.OSSclient = {
put: function (objectName, object) {
put: async function (objectName, object) {
OSSclient = await RefreshClient(OSSclient)
return new Promise(async (resolve, reject) => {
try {
let _vm = router.default.app
@ -49,6 +58,46 @@ async function ossGenerateSTS() {
reject()
}
})
},
multipartUpload: async (data, progress) => {
OSSclient = await RefreshClient(OSSclient)
return new Promise(async (resolve, reject) => {
try {
const { file, path } = data;
if (!file || !path) return reject('file and path be required');
let config = await getSTSToken(Vue.prototype.OSSclientConfig.expiration);
if (config) {
Vue.prototype.OSSclientConfig = { ...config.Result[config.Result.ObjectStoreUse] }
Vue.prototype.OSSclientConfig.ObjectStoreUse = config.Result.ObjectStoreUse;
Vue.prototype.OSSclientConfig.basePath = Vue.prototype.OSSclientConfig.viewEndpoint;
Vue.prototype.OSSclientConfig.bucket = Vue.prototype.OSSclientConfig.bucketName
Vue.prototype.OSSclientConfig.stsToken = Vue.prototype.OSSclientConfig.securityToken
Vue.prototype.OSSclientConfig.timeout = 10 * 60 * 1000
OSSclient = new OSS(Vue.prototype.OSSclientConfig);
}
let _vm = router.default.app
if (_vm._route.path !== '/trials/trials-panel/visit/crc-upload') {
var objectItem = data.path.split('/')
objectItem[objectItem.length - 1] = new Date().getTime() + '_' + objectItem[objectItem.length - 1]
data.path = objectItem.join('/')
}
let res = await customerHttp(OSSclient, data, progress);
if (res) {
resolve({
name: data.path,
url: Vue.prototype.OSSclientConfig.viewEndpoint + decodeUtf8(res.name)
})
} else {
reject()
}
} catch (err) {
console.log(err)
reject(err)
}
})
},
close: () => {
OSSclose();
}
}
break
@ -86,49 +135,121 @@ async function ossGenerateSTS() {
console.log(e)
}
})
},
close: () => {
return false
}
}
break
case 'AWS':
let aws = new Minio.Client(Vue.prototype.OSSclientConfig);
Vue.prototype.OSSclientConfig.bucket = Vue.prototype.OSSclientConfig.bucketName
let aws = new S3Client({
endpoint: Vue.prototype.OSSclientConfig.viewEndpoint,
region: Vue.prototype.OSSclientConfig.region,
s3ForcePathStyle: true,
signatureVersion: 'v4',
forcePathStyle: true,
// SessionToken: '',
credentials: {
accessKeyId: Vue.prototype.OSSclientConfig.accessKeyId,
secretAccessKey: Vue.prototype.OSSclientConfig.secretAccessKey,
sessionToken: Vue.prototype.OSSclientConfig.sessionToken
}
});
Vue.prototype.OSSclient = {
put: function (objectName, object) {
return new Promise(async (resolve, reject) => {
try {
var name = objectName.split('/')[objectName.split('/').length - 1]
let _vm = router.default.app
if (_vm._route.path !== '/trials/trials-panel/visit/crc-upload') {
var objectItem = objectName.split('/')
objectItem[objectItem.length - 1] = new Date().getTime() + '_' + objectItem[objectItem.length - 1]
objectName = objectItem.join('/')
}
const reader = new FileReader();
reader.onload = (ex) => {
const bufferStream = new stream.PassThrough()
bufferStream.end(Buffer.from(ex.target.result))
aws.putObject(Vue.prototype.OSSclientConfig.bucketName, objectName, bufferStream, function (err, etag) {
if (err) {
console.log(err)
reject()
} else {
console.log(objectName);
resolve({
name: objectName,
url: Vue.prototype.OSSclientConfig.viewEndpoint + decodeUtf8(objectName)
})
}
})
};
reader.readAsArrayBuffer(object);
} catch (e) {
console.log(e)
}
})
put: async function (objectName, object) {
let data = {
file: object,
path: objectName
}
aws = await RefreshClient(aws);
return uploadAWS(aws, data, () => { });
},
multipartUpload: async (data, progress) => {
aws = await RefreshClient(aws);
return uploadAWS(aws, data, progress);
},
close: () => {
AWSclose();
}
}
}
return
}
// AWS上传函数
function uploadAWS(aws, data, progress) {
return new Promise(async (resolve, reject) => {
try {
const { file, path } = data;
if (!file || !path) return reject('file and path be required');
let _vm = router.default.app
if (_vm._route.path !== '/trials/trials-panel/visit/crc-upload') {
var objectItem = data.path.split('/')
objectItem[objectItem.length - 1] = new Date().getTime() + '_' + objectItem[objectItem.length - 1]
data.path = objectItem.join('/')
}
let bucketName = data.path.split("/")[1] || Vue.prototype.OSSclientConfig.bucket
let curPath = data.path;
data.path = data.path.replace(`/${bucketName}/`, '');
await exist(aws, bucketName, data, progress, (path, status) => {
if (status === 'success') {
resolve({
name: decodeUtf8(curPath),
url: Vue.prototype.OSSclientConfig.viewEndpoint + decodeUtf8(curPath)
})
} else {
reject()
}
});
} catch (err) {
console.log(err)
reject(err)
}
})
}
// client过期刷新
async function RefreshClient(client) {
let config = await getSTSToken(Vue.prototype.OSSclientConfig.expiration);
if (config) {
// config.Result.ObjectStoreUse = 'AWS'
switch (config.Result.ObjectStoreUse) {
case 'AliyunOSS': {
Vue.prototype.OSSclientConfig = { ...config.Result[config.Result.ObjectStoreUse] }
Vue.prototype.OSSclientConfig.ObjectStoreUse = config.Result.ObjectStoreUse;
Vue.prototype.OSSclientConfig.basePath = Vue.prototype.OSSclientConfig.viewEndpoint;
Vue.prototype.OSSclientConfig.bucket = Vue.prototype.OSSclientConfig.bucketName
Vue.prototype.OSSclientConfig.stsToken = Vue.prototype.OSSclientConfig.securityToken
Vue.prototype.OSSclientConfig.timeout = 10 * 60 * 1000
return new OSS(Vue.prototype.OSSclientConfig);
}
case "MinIO": {
return client;
}
case "AWS": {
Vue.prototype.OSSclientConfig = { ...config.Result[config.Result.ObjectStoreUse] }
Vue.prototype.OSSclientConfig.ObjectStoreUse = config.Result.ObjectStoreUse;
Vue.prototype.OSSclientConfig.basePath = Vue.prototype.OSSclientConfig.viewEndpoint;
Vue.prototype.OSSclientConfig.bucket = Vue.prototype.OSSclientConfig.bucketName
return new S3Client({
endpoint: Vue.prototype.OSSclientConfig.viewEndpoint,
region: Vue.prototype.OSSclientConfig.region,
s3ForcePathStyle: true,
signatureVersion: 'v4',
forcePathStyle: true,
credentials: {
accessKeyId: Vue.prototype.OSSclientConfig.accessKeyId,
secretAccessKey: Vue.prototype.OSSclientConfig.secretAccessKey,
sessionToken: Vue.prototype.OSSclientConfig.sessionToken
}
});
}
}
} else {
return client;
}
}
function decodeUtf8(bytes) {
let str = bytes.split('?');
let str2 = str[0].split('/');
@ -138,6 +259,48 @@ function decodeUtf8(bytes) {
str2.pop();
return str2.join("/") + '/' + name;
}
const queue = []
let loading = false;
// 获取凭证
function getSTSToken(credentials) {
return new Promise(async (resolve, reject) => {
let isExpired = isCredentialsExpired(credentials);
if (isExpired) {
if (loading) {
queue.push({ resolve, reject })
}
if (!loading) {
loading = true;
let res = await GetObjectStoreToken();
loading = false;
localStorage.setItem('stsToken', JSON.stringify(res))
resolve(res)
let p = queue.shift();
while (p) {
p.resolve(res)
p = queue.shift();
}
}
} else {
// console.log("凭证未过期");
resolve(false)
}
})
}
/**
* oss判断临时凭证是否到期
**/
function isCredentialsExpired(credentials) {
if (!credentials) {
return true;
}
const expireDate = new Date(credentials);
const now = new Date();
// 如果有效期不足五分钟,视为过期。
return expireDate.getTime() - now.getTime() <= 300000;
}
export const OSSclient = ossGenerateSTS

View File

@ -113,10 +113,14 @@ export const parseDicom = (file, name = false) => {
}
})
uintKey.forEach(key => {
res[key] = data.uint16(dicom[key])
if (res.hasOwnProperty(key)) {
res[key] = data.uint16(dicom[key])
}
})
intStringKey.forEach(key => {
res[key] = data.intString(dicom[key])
if (res.hasOwnProperty(key)) {
res[key] = data.intString(dicom[key])
}
})
defaultKey.forEach(key => {
if (!res[key] && res.hasOwnProperty(key)) {

View File

@ -2,9 +2,9 @@ import axios from 'axios'
import { Message, MessageBox, Alert } from 'element-ui'
import store from '@/store'
import router from '@/router'
import Vue from 'vue'
import WHITELIST from "./whiteList"
import moment from 'moment-timezone';
console.log(moment.tz.guess())
import { encryptConfig } from "@/utils/encrypt"
axios.defaults.withCredentials = false
const service = axios.create({
baseURL: '/api',
@ -16,18 +16,28 @@ var path
// var lang = store.state.lang.language
service.interceptors.request.use(
config => {
async config => {
path = router && router.app && router.app._route && router.app._route.path
config.headers['Content-Type'] = 'application/json;charset=UTF-8'
var language = zzSessionStorage.getItem('lang')
config.headers['Accept-Language'] = language === 'en' ? 'en-US,en;q=0.5' : 'zh-CN,zh;q=0.9'
config.headers['TimeZoneId'] = moment.tz.guess()
if (store.getters.token) {
if (config.ENCRYPT) {
try {
config = await encryptConfig(config)
} catch (err) {
console.log(err)
}
}
if (store.getters.token && !config.clearToken) {
config.headers.Authorization = `Bearer ${store.getters.token}`
}
try {
if (eval(process.env.VUE_APP_LOGIN_FOR_PERMISSION)) {
if (!path || path === '/ReviewersResearchForm' || path === '/ReviewersResearch' || path === '/login' || path === '/researchForm' || path === '/researchDetail_m' || path === '/researchLogin_m' || path === '/researchLogin' || path === '/email-recompose' || path === '/recompose' || path === '/resetpassword' || path === '/error') {
// if (!path || path === '/ReviewersResearchForm' || path === '/ReviewersResearch' || path === '/login' || path === '/researchForm' || path === '/researchDetail_m' || path === '/researchLogin_m' || path === '/researchLogin' || path === '/email-recompose' || path === '/recompose' || path === '/resetpassword' || path === '/error') {
// return config
// }
if (!path || WHITELIST.some(item => item === path)) {
return config
}
if (store.state.user.userId !== zzSessionStorage.getItem('userId')) {

106
src/utils/stream.js Normal file
View File

@ -0,0 +1,106 @@
import streamSaver from "streamsaver";
import "streamsaver/examples/zip-stream.js";
import store from '@/store'
streamSaver.mitm = `${window.location.origin}/mitm.html?version=2.0.0`
// 下载文件并压缩
function zipFiles(zipName, files) {
return new Promise((resolve) => {
try {
console.log("同步下载打包开始时间:" + new Date());
store.dispatch('trials/setUnLock', true)
files = formatFiles(files)
// 创建压缩文件输出流
const zipFileOutputStream = streamSaver.createWriteStream(zipName);
// 创建下载文件流
const fileIterator = files.values();
const readableZipStream = new ZIP({
async pull(ctrl) {
const fileInfo = fileIterator.next();
if (fileInfo.done) {//迭代终止
ctrl.close();
} else {
let { name, url } = fileInfo.value;
url = decodeUtf8(url);
return fetch(url).then(res => {
ctrl.enqueue({
name,
stream: () => res.body
});
})
}
}
});
if (window.WritableStream && readableZipStream.pipeTo) {
// 开始下载
readableZipStream
.pipeTo(zipFileOutputStream)
.then(() => {
console.log("同步下载打包结束时间:" + new Date());
store.dispatch('trials/setUnLock', false)
resolve(true)
}).catch(err => {
console.log(err);
resolve(false)
});
} else {
resolve(false)
}
} catch (err) {
console.log(err);
resolve(false)
}
})
}
// 下载文件并修改名称
async function updateFile(file, name) {
return new Promise(async resolve => {
try {
store.dispatch('trials/setUnLock', true)
const fileOutputStream = streamSaver.createWriteStream(name);
file = decodeUtf8(file);
let res = await fetch(file);
res.body.pipeTo(fileOutputStream).then(() => {
store.dispatch('trials/setUnLock', true)
resolve(true)
}).catch(err => {
console.log(err)
resolve(false)
});
} catch (err) {
console.log(err)
resolve(false)
}
})
}
// 同名文件修改名称
function formatFiles(files) {
let fileObj = {};
files.forEach(file => {
let arr = Object.keys(fileObj);
if (!~arr.indexOf(file.name)) {
fileObj[file.name] = 1;
} else {
let name = file.name;
file.name = name.split(".")[0] + `(${fileObj[name]})` + name
.substring(name.lastIndexOf('.'))
.toLocaleLowerCase()
fileObj[name]++;
}
})
return files;
}
function decodeUtf8(bytes) {
let str = bytes.split('?');
let str2 = str[0].split('/');
let name = str2[str2.length - 1];
name = encodeURIComponent(name);
str.shift();
str2.pop();
return str2.join("/") + '/' + name;
}
export async function downLoadFile(file, name, type = 'file') {
if (type === 'zip') return await zipFiles(name, file);
return await updateFile(file, name)
}

View File

@ -1,168 +0,0 @@
import Vue from 'vue';
import store from "@/store";
import {
requestPackageAndAnonymizImage,
} from "@/api/load.js";
import streamSaver from "streamsaver";
import "streamsaver/examples/zip-stream.js"
let flag = {};
export const resetFlag = () => {
flag = {};
store.state.trials.uploadTip = null;
if (store.state.trials.timer) {
clearInterval(store.state.trials.timer);
store.state.trials.timer = null;
}
store.dispatch("trials/setUnLock", false);
}
export const downloadImage = async (id, id2, IsDicom = true) => {
// if (flag[`${id2}_${IsDicom}`]) return Vue.prototype.$message.warning(Vue.prototype.$t('trials:upload:tip:uploading'));
if (flag[`${id2}_${IsDicom}`]) return false;
flag[`${id2}_${IsDicom}`] = true
try {
let params = {
TrialId: id,
SubjectVisitId: id2,
IsDicom: IsDicom
}
store.dispatch("trials/setUnLock", true);
let res = await requestPackageAndAnonymizImage(params);
if (res.IsSuccess) {
if (!res.Result) {
flag[`${id2}_${IsDicom}`] = false;
// Vue.prototype.$message.warning(Vue.prototype.$t("trials:upload:message:not"))
let message = Vue.prototype.$t('trials:upload:tip:uploading').replace("xxx", res.OtherInfo.FileName);
store.state.trials.uploadTip = message;
if (!store.state.trials.timer) {
store.state.trials.timer = setInterval(() => {
downloadImage(id, id2, IsDicom);
}, 2000);
}
return false;
}
if (store.state.trials.timer) {
clearInterval(store.state.trials.timer);
store.state.trials.timer = null;
}
let fileName = res.Result.split("/").pop();
let href = Vue.prototype.OSSclientConfig.basePath + res.Result;
if (fileName !== res.OtherInfo.FileName) {
let message = Vue.prototype.$t('trials:upload:tip:uploading').replace("xxx", res.OtherInfo.FileName);
store.state.trials.uploadTip = message;
// Vue.prototype.$message.success(Vue.prototype.$t("trials:upload:message:startUpload"));
return download(href, res.OtherInfo.FileName, { id2, IsDicom });
}
let a = document.createElement("a");
a.download = res.OtherInfo.FileName;
a.href = href;
a.click();
URL.revokeObjectURL(href);
let timer = setTimeout(() => {
a = null;
href = null;
timer = null;
}, 500)
store.state.trials.uploadTip = null;
return true;
} else {
flag[`${id2}_${IsDicom}`] = false;
return false;
}
} catch (err) {
flag[`${id2}_${IsDicom}`] = false;
console.log(err);
}
};
export const fileDownload = (content, filename) => {
const eleLink = document.createElement("a");
eleLink.download = filename;
eleLink.style.display = "none";
const blob = new Blob([content]);
eleLink.href = URL.createObjectURL(blob);
document.body.appendChild(eleLink);
eleLink.click();
document.body.removeChild(eleLink);
};
let download = async (downloadUrl, downloadFileName, res) => {
const blob = await getBlob(downloadUrl);
flag[`${res.id2}_${res.IsDicom}`] = false;
store.state.trials.uploadTip = null;
store.dispatch("trials/setUnLock", false);
saveAsB(blob, downloadFileName);
return true;
}
let getBlob = (url) => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'blob';
xhr.onload = () => {
if (xhr.status === 200) {
resolve(xhr.response);
} else {
reject(new Error(`Request failed with status ${xhr.status}`));
}
};
xhr.onerror = () => {
reject(new Error('Request failed'));
};
xhr.send();
});
}
let saveAsB = (blob, filename) => {
const link = document.createElement('a');
const body = document.body;
link.href = window.URL.createObjectURL(blob);
link.download = filename;
// hide the link
link.style.display = 'none';
body.appendChild(link);
link.click();
body.removeChild(link);
window.URL.revokeObjectURL(link.href);
}
// 前端流式打包下载
const handleBatchDown = async (item, zip) => {
return new Promise((resolve) => {
console.log("同步下载打包开始时间:" + new Date());
// 创建压缩文件输出流
const zipFileOutputStream = streamSaver.createWriteStream(zipName);
// 创建下载文件流
const fileIterator = files.values();
const readableZipStream = new window.ZIP({
async pull(ctrl) {
const fileInfo = fileIterator.next();
if (fileInfo.done) {//迭代终止
ctrl.close();
} else {
const { name, url } = fileInfo.value;
return fetch(url).then(res => {
ctrl.enqueue({
name,
stream: () => res.body
})
})
}
}
});
if (window.WritableStream && readableZipStream.pipeTo) {
// 开始下载
readableZipStream
.pipeTo(zipFileOutputStream)
.then(() => { console.log("同步下载打包结束时间:" + new Date()); resolve(true) })
} else {
resolve(false);
}
})
};

2
src/utils/whiteList.js Normal file
View File

@ -0,0 +1,2 @@
const WHITELIST = ['/', 'researchDetail_m', '/researchForm', '/ReviewersResearch', '/login', '/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']
export default WHITELIST

View File

@ -251,7 +251,7 @@ export default {
},
async loadPatientStudy() {
try {
let data = await getPatientSeriesList(this.studyId);
const data = await getPatientSeriesList(this.studyId)
if (data.IsSuccess) {
const { Result } = data
var seriesList = []

View File

@ -6,7 +6,7 @@
<div class="sidePanelThumbs">
<el-tabs v-model="activeName" type="border-card" @tab-click="handleTabClick">
<el-tab-pane :label="$t('trials:dicom-show:currentVisit')" name="current-study">
<div class="text-desc" style="background:rgb(55 55 55);">
<div v-if="visitInfo" class="text-desc" style="background:rgb(55 55 55);">
{{ visitInfo }}
</div>
<div class="viewerSidethumbs ps" style="position: relative;">
@ -143,7 +143,7 @@
</div>
</div>
</el-tab-pane>
<el-tab-pane :label="$t('trials:dicom-show:relatedVisit')" name="relation-study" class="pane-relation-wrapper">
<el-tab-pane :label="$t('trials:dicom-show:relatedVisit')" name="relation-study" class="pane-relation-wrapper" v-if="!visitTaskId">
<div class="viewerSidethumbinner">
<el-collapse v-model="relationActiveName" @change="handelRelationActiveChange">
<el-collapse-item v-for="(study,studyIndex) in relationStudyList" :key="`${study.StudyId}`" :name="`${study.StudyId}`">
@ -261,6 +261,7 @@ import * as cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader'
import dicomViewer from '@/components/Dicom/DicomViewer'
import { getVisitStudyList, getAllRelationStudyList, getSeriesList } from '@/api/reading'
import { setSeriesStatus } from '@/api/trials'
import { getTaskUploadedDicomStudyList } from '@/api/reading'
import requestPoolManager from '@/utils/request-pool'
import store from '@/store'
import { changeURLStatic } from '@/utils/history.js'
@ -301,7 +302,9 @@ export default {
relationActiveName: [],
showSeriesList: [],
currentLoadIns: [],
isFromCRCUpload: false
isFromCRCUpload: false,
visitTaskId: null,
page: ''
}
},
mounted() {
@ -318,6 +321,8 @@ export default {
this.isReading = this.$router.currentRoute.query.isReading ? this.$router.currentRoute.query.isReading * 1 : 0
this.showDelete = parseInt(this.$router.currentRoute.query.showDelete)
this.isFromCRCUpload = !!this.$router.currentRoute.query.isFromCRCUpload
this.visitTaskId = this.$router.currentRoute.query.visitTaskId
this.page = this.$route.query.page
// cornerstone.events.addEventListener('cornerstoneimageloaded', this.cornerstoneImageLoaded)
this.getStudiesInfo()
cornerstone.events.addEventListener('cornerstoneimageloadprogress', this.cornerstoneimageloadprogress)
@ -338,7 +343,14 @@ export default {
async getStudiesInfo() {
this.studyList = []
try {
const res = await getVisitStudyList(this.trialId, this.subjectVisitId, this.isReading)
let res = null
if (this.page === 'upload') {
res = await getTaskUploadedDicomStudyList({ visitTaskId: this.visitTaskId })
} else if (this.page === 'download'){
res = await getVisitStudyList(this.trialId, this.subjectVisitId, this.isReading, this.visitTaskId)
} else {
res = await getVisitStudyList(this.trialId, this.subjectVisitId, this.isReading)
}
if (res.IsSuccess) {
res.Result.forEach((study, studyIndex) => {
const data = {}

View File

@ -3,7 +3,7 @@
ref="sysTemplateFrom"
v-loading="loading"
:model="form"
label-width="90px"
label-width="140px"
size="small"
:rules="rules"
class="upload-temporary-file"
@ -15,7 +15,7 @@
<el-form-item label="业务场景: " prop="BusinessScenarioEnum">
<el-select
v-model="form.BusinessScenarioEnum"
style="width:100%;"
style="width: 100%"
size="small"
filterable
>
@ -44,12 +44,14 @@
:on-exceed="handleExceed"
:disabled="!form.FileTypeEnum || !form.BusinessScenarioEnum"
>
<el-button size="small" type="primary" :disabled="!form.FileTypeEnum || !form.BusinessScenarioEnum" :loading="btnLoading">Select</el-button>
<span
slot="tip"
style="margin-left:10px;"
class="el-upload__tip"
<el-button
size="small"
type="primary"
:disabled="!form.FileTypeEnum || !form.BusinessScenarioEnum"
:loading="btnLoading"
>Select</el-button
>
<span slot="tip" style="margin-left: 10px" class="el-upload__tip">
(must be in xlsx/xls/doc/docx format)
</span>
</el-upload>
@ -58,37 +60,61 @@
<el-form-item label="文件名" prop="Name">
<el-input v-model="form.Name" />
</el-form-item>
<el-form-item
:label="$t('dictionary:attachment:export:form:nameCN')"
prop="NameCN"
>
<el-input v-model="form.NameCN" />
</el-form-item>
<el-form-item v-if="form.Id !== ''" label="是否废除: ">
<el-radio-group v-model="form.IsDeleted">
<el-radio v-for="item of $d.YesOrNo" :label="item.value">{{item.label}}</el-radio>
<el-radio
v-for="item of $d.YesOrNo"
:label="item.value"
:key="item.id"
>{{ item.label }}</el-radio
>
</el-radio-group>
</el-form-item>
<el-form-item label="描述">
<el-input
v-model="form.Description"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"
:autosize="{ minRows: 2, maxRows: 4 }"
placeholder="请输入内容"
/>
</el-form-item>
</div>
<div class="base-dialog-footer" style="text-align:right;margin-top:10px;">
<el-form-item style="text-align:right;">
<el-button size="small" type="primary" :disabled="!form.FileTypeEnum || !form.BusinessScenarioEnum" :loading="saveBtnLoading" @click="handleSave">Save</el-button>
<div class="base-dialog-footer" style="text-align: right; margin-top: 10px">
<el-form-item style="text-align: right">
<el-button
size="small"
type="primary"
:disabled="!form.FileTypeEnum || !form.BusinessScenarioEnum"
:loading="saveBtnLoading"
@click="handleSave"
>Save</el-button
>
</el-form-item>
</div>
</el-form>
</template>
<script>
import { addOrUpdateCommonDocument, uploadCommonDoc, Upload } from '@/api/dictionary'
import {
addOrUpdateCommonDocument,
uploadCommonDoc,
Upload,
} from '@/api/dictionary'
import { getBasicDataSelects } from '@/api/dictionary/dictionary'
export default {
name: 'TemplateForm',
props: {
data: {
type: Object,
default() { return {} }
}
default() {
return {}
},
},
},
data() {
@ -99,20 +125,30 @@ export default {
FileTypeEnum: null,
BusinessScenarioEnum: null,
Name: '',
NameCN: '',
Path: '',
Description: '',
IsDeleted: false
IsDeleted: false,
},
rules: {
Code: [{ required: true, message: 'Please specify', trigger: ['blur'] }],
Name: [{ required: true, message: 'Please specify', trigger: ['blur'] }],
BusinessScenarioEnum: [{ required: true, message: 'Please select', trigger: ['blur'] }]
Code: [
{ required: true, message: 'Please specify', trigger: ['blur'] },
],
Name: [
{ required: true, message: 'Please specify', trigger: ['blur'] },
],
NameCN: [
{ required: true, message: 'Please specify', trigger: ['blur'] },
],
BusinessScenarioEnum: [
{ required: true, message: 'Please select', trigger: ['blur'] },
],
},
fileList: [],
btnLoading: false,
saveBtnLoading: false,
loading: false,
dictionaryList: {}
dictionaryList: {},
}
},
mounted() {
@ -128,8 +164,8 @@ export default {
name: this.data.Name,
path: this.data.Path,
url: this.data.Path,
type: this.data.Type
}
type: this.data.Type,
},
]
}
for (const k in this.form) {
@ -142,12 +178,14 @@ export default {
//
getDicData() {
this.loading = true
getBasicDataSelects(['Common_File_ModuleType', 'Common_File_Type']).then(res => {
this.dictionaryList = { ...res.Result }
this.loading = false
}).catch(() => {
this.loading = false
})
getBasicDataSelects(['Common_File_ModuleType', 'Common_File_Type'])
.then((res) => {
this.dictionaryList = { ...res.Result }
this.loading = false
})
.catch(() => {
this.loading = false
})
},
beforeUpload(file) {
//
@ -165,35 +203,43 @@ export default {
const formData = new FormData()
formData.append('file', param.file)
this.form.FileName = param.file.name
Upload(formData, 1).then(res => {
this.fileList.push({ name: param.file.name, path: res.Result.FilePath, fullPath: res.Result.FullFilePath, url: res.Result.FilePath })
Upload(formData, 1).then((res) => {
this.fileList.push({
name: param.file.name,
path: res.Result.FilePath,
fullPath: res.Result.FullFilePath,
url: res.Result.FilePath,
})
this.form.Path = res.Result.FilePath
this.form.Name = param.file.name
this.form.NameCN = param.file.name
this.loading = false
this.btnLoading = false
})
},
handleSave() {
this.$refs.sysTemplateFrom.validate(valid => {
this.$refs.sysTemplateFrom.validate((valid) => {
if (!valid) return
if (!this.form.Name) {
this.$alert('Please select file.')
return
}
this.saveBtnLoading = true
addOrUpdateCommonDocument(this.form).then(res => {
this.saveBtnLoading = false
this.$emit('closeDialog')
this.$emit('getList')
this.$message.success('Uploaded successfully')
}).catch(() => {
this.saveBtnLoading = false
})
addOrUpdateCommonDocument(this.form)
.then((res) => {
this.saveBtnLoading = false
this.$emit('closeDialog')
this.$emit('getList')
this.$message.success('Uploaded successfully')
})
.catch(() => {
this.saveBtnLoading = false
})
})
},
handleRemoveFile() {
this.fileList = []
this.form.Path = ''
this.form.NameCN = ''
this.form.Name = ''
},
handlePreview(file) {
@ -206,19 +252,21 @@ export default {
},
checkFileSuffix(fileName) {
var typeArr = ['xls', 'xlsx', 'doc', 'docx']
var extendName = fileName.substring(fileName.lastIndexOf('.') + 1).toLocaleLowerCase()
var extendName = fileName
.substring(fileName.lastIndexOf('.') + 1)
.toLocaleLowerCase()
if (typeArr.indexOf(extendName) !== -1) {
return true
} else {
return false
}
}
}
},
},
}
</script>
<style lang="scss">
.upload-temporary-file{
.upload-temporary-file {
.upload-container .el-upload--text {
border: none;
width: 80px;
@ -231,8 +279,8 @@ export default {
color: #428bca;
font-size: 13px;
}
.account_item_clear{
.el-tag__close{
.account_item_clear {
.el-tag__close {
display: none !important;
}
}

View File

@ -5,7 +5,7 @@
<el-form-item label="业务场景">
<el-select
v-model="searchData.BusinessScenarioEnum"
style="width:150px;"
style="width: 150px"
>
<el-option
v-for="item of $d.Common_File_BusinessScenario"
@ -13,15 +13,10 @@
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="文件名称">
<el-input
v-model="searchData.Name"
style="width:130px;"
clearable
/>
<el-input v-model="searchData.Name" style="width: 130px" clearable />
</el-form-item>
<el-form-item>
@ -30,24 +25,28 @@
{{ $t('common:button:search') }}
</el-button>
<!-- 重置 -->
<el-button type="primary" icon="el-icon-refresh-left" @click="handleReset">
<el-button
type="primary"
icon="el-icon-refresh-left"
@click="handleReset"
>
{{ $t('common:button:reset') }}
</el-button>
</el-form-item>
<el-button
type="primary"
icon="el-icon-plus"
style="float: right"
size="small"
@click="handleAdd"
>
{{ $t('common:button:new') }}
</el-button>
</el-form>
<el-button
type="primary"
icon="el-icon-plus"
style="margin-left:auto;"
size="small"
@click="handleAdd"
>
{{ $t('common:button:new') }}
</el-button>
</template>
<template slot="main-container">
<el-table
v-adaptive="{bottomOffset:60}"
v-adaptive="{ bottomOffset: 60 }"
v-loading="loading"
:data="list"
stripe
@ -69,9 +68,13 @@
sortable="custom"
>
<template slot-scope="scope">
{{ $fd('Common_File_BusinessScenario',scope.row.BusinessScenarioEnum) }}
{{
$fd(
'Common_File_BusinessScenario',
scope.row.BusinessScenarioEnum
)
}}
</template>
</el-table-column>
<el-table-column
@ -80,6 +83,12 @@
show-overflow-tooltip
sortable="custom"
/>
<el-table-column
prop="NameCN"
:label="$t('dictionary:attachment:upload:table:nameCN')"
show-overflow-tooltip
sortable="custom"
/>
<el-table-column
prop="Description"
label="描述"
@ -92,8 +101,12 @@
sortable="custom"
>
<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>
<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
@ -105,29 +118,26 @@
<el-table-column label="Action">
<template slot-scope="scope">
<el-button
type="text"
@click="handleDownload(scope.row)"
>
<el-button type="text" @click="handleDownload(scope.row)">
下载
</el-button>
<el-button
type="text"
@click="handleEdit(scope.row)"
>
<el-button type="text" @click="handleEdit(scope.row)">
编辑
</el-button>
<el-button
type="text"
@click="handleDelete(scope.row)"
>
<el-button type="text" @click="handleDelete(scope.row)">
删除
</el-button>
</template>
</el-table-column>
</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"
/>
<!-- 新增/编辑 -->
<el-dialog
@ -138,13 +148,21 @@
width="600px"
custom-class="base-dialog-wrapper"
>
<TemplateForm :data="currentRow" @closeDialog="closeDialog" @getList="getList" />
<TemplateForm
:data="currentRow"
@closeDialog="closeDialog"
@getList="getList"
/>
</el-dialog>
</template>
</BaseContainer>
</template>
<script>
import { getCommonDocumentList, DownloadCommonDoc, deleteCommonDocument } from '@/api/dictionary'
import {
getCommonDocumentList,
DownloadCommonDoc,
deleteCommonDocument,
} from '@/api/dictionary'
import BaseContainer from '@/components/BaseContainer'
import Pagination from '@/components/Pagination'
import TemplateForm from './TemplateForm'
@ -155,7 +173,7 @@ const searchDataDefault = () => {
BusinessScenarioEnum: null,
Name: '',
PageIndex: 1,
PageSize: 20
PageSize: 20,
}
}
export default {
@ -168,7 +186,7 @@ export default {
total: 0,
currentRow: {},
editDialog: { title: '', visible: false },
loading: false
loading: false,
}
},
mounted() {
@ -185,13 +203,15 @@ export default {
},
getList() {
this.loading = true
getCommonDocumentList(this.searchData).then(res => {
this.loading = false
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
}).catch(() => {
this.loading = false
})
getCommonDocumentList(this.searchData)
.then((res) => {
this.loading = false
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
})
.catch(() => {
this.loading = false
})
},
//
handleAdd() {
@ -202,9 +222,13 @@ export default {
//
handleDownload(row) {
this.loading = true
DownloadCommonDoc(row.Code).then(data => {
this.loading = false
}).catch(() => { this.loading = false })
DownloadCommonDoc(row.Code)
.then((data) => {
this.loading = false
})
.catch(() => {
this.loading = false
})
},
//
handleEdit(row) {
@ -233,7 +257,12 @@ export default {
this.searchData.SortField = column.prop
this.searchData.PageIndex = 1
this.getList()
}
}
},
},
}
</script>
<style lang="scss" scoped>
::v-deep .search {
display: block;
}
</style>

View File

@ -49,7 +49,7 @@
<el-table-column prop="Identification" label="标识" :show-overflow-tooltip="true" min-width="200px" />
<el-table-column prop="OptTypeValueCN" label="操作类型" show-overflow-tooltip width="100px" />
<el-table-column prop="ObjectTypeValueCN" label="对象类型" show-overflow-tooltip min-width="100px" />
<el-table-column prop="ChildrenTypeValueCN" label="数据类型" show-overflow-tooltip min-width="100px" show-overflow-tooltip />
<el-table-column prop="ChildrenTypeValueCN" label="数据类型" show-overflow-tooltip min-width="100px"/>
<el-table-column
prop="Sort"
label="显示顺序"
@ -110,7 +110,7 @@
<!-- 添加或修改菜单对话框 -->
<el-dialog :title="title" top="100px" :close-on-click-modal="false" id="check_config" :visible.sync="open" :width="form.DataType === 'Table' ? '1280px' : '680px'" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="100px">
<el-form ref="form" :model="form" :rules="rules" label-width="120px">
<el-row>
<el-col v-show="title !== '复制'" :span="24">
<el-divider content-position="left">基本信息</el-divider>
@ -142,11 +142,16 @@
<el-input v-model="form.Identification" placeholder="请输入标识" />
</el-form-item>
</el-col>
<el-col v-show="form.ConfigType === 'C' && title !== '复制'" :span="24">
<el-col v-show="form.ConfigType === 'C' && title !== '复制'" :span="12">
<el-form-item label="字段key">
<el-input v-model="form.Code" placeholder="请输入字段key" />
</el-form-item>
</el-col>
<el-col v-show="form.ConfigType === 'C' && title !== '复制'" :span="12">
<el-form-item label="字段key英文">
<el-input v-model="form.CodeEn" placeholder="请输入字段key英文" />
</el-form-item>
</el-col>
<el-col v-show="form.ConfigType === 'C' && title !== '复制'" :span="12">
<el-form-item label="字段英文">
<el-input v-model="form.Value" placeholder="请输入字段英文" />
@ -372,6 +377,16 @@
<el-input :disabled="!scope.row.IsFixedColumn" v-model="scope.row.FixedColumnName" placeholder="固定列名"/>
</template>
</el-table-column>
<el-table-column
prop="FixedColumnEnName"
min-width="120"
label="固定列名EN"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-input :disabled="!scope.row.IsFixedColumn" v-model="scope.row.FixedColumnEnName" placeholder="固定列名EN"/>
</template>
</el-table-column>
<el-table-column
prop="ColumnName"
min-width="120"
@ -382,6 +397,16 @@
<el-input :disabled="scope.row.IsFixedColumn" v-model="scope.row.ColumnName" placeholder="列字段名"/>
</template>
</el-table-column>
<el-table-column
prop="ColumnEnName"
min-width="120"
label="列字段名En"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-input :disabled="scope.row.IsFixedColumn" v-model="scope.row.ColumnEnName" placeholder="列字段名En"/>
</template>
</el-table-column>
<el-table-column
prop="ColumnValue"
min-width="120"
@ -415,6 +440,16 @@
<el-input :disabled="!scope.row.IsMerge" v-model="scope.row.MergeColumnName" placeholder="合并组"/>
</template>
</el-table-column>
<el-table-column
prop="MergeColumnEnName"
min-width="120"
label="合并组EN"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-input :disabled="!scope.row.IsMerge" v-model="scope.row.MergeColumnEnName" placeholder="合并组EN"/>
</template>
</el-table-column>
<el-table-column
prop="IsPicture"
min-width="120"
@ -489,6 +524,14 @@
<el-input v-model="form.ChildDataLabel" placeholder="请输入子数据Lable" />
</el-form-item>
</el-col>
<el-col v-show="form.ConfigType === 'C' && title !== '复制' && form.DataType === 'Array'" :span="12">
<el-form-item>
<span slot="label">
子数据LableEN
</span>
<el-input v-model="form.ChildDataEnLabel" placeholder="请输入子数据LableEN" />
</el-form-item>
</el-col>
<el-col v-show="form.ConfigType === 'C' && title !== '复制' && form.DataType === 'Array'" :span="12">
<el-form-item>
<span slot="label">
@ -559,6 +602,11 @@
<el-input v-model="form.ForeignKeyText" placeholder="请输入数据库字段to" />
</el-form-item>
</el-col>
<el-col v-show="form.ConfigType === 'C' && title !== '复制' && form.EnumType === 'Foreign'" :span="12">
<el-form-item label="字段toEN">
<el-input v-model="form.ForeignKeyEnText" placeholder="请输入数据库字段toEN" />
</el-form-item>
</el-col>
</el-row>
</el-col>
<el-col v-show="title !== '复制'" :span="24">
@ -643,6 +691,7 @@
height="100"
>
<el-table-column prop="Code" label="字段名" min-width="120px" :show-overflow-tooltip="true" />
<el-table-column prop="CodeEn" label="字段名英文" min-width="120px" :show-overflow-tooltip="true" />
<el-table-column prop="Value" label="字段英文" min-width="120px" :show-overflow-tooltip="true" />
<el-table-column prop="ValueCN" label="字段中文" min-width="120px" :show-overflow-tooltip="true" />
<el-table-column
@ -821,10 +870,13 @@ export default {
ListName: null,
IsFixedColumn: false,
FixedColumnName: null,
FixedColumnEnName: null,
ColumnName: null,
ColumnEnName: null,
ColumnValue: null,
IsMerge: false,
MergeColumnName: null,
MergeColumnEnName: null,
IsPicture: false,
IsDynamicTranslate: false,
IsNeedTransalate: false,
@ -1097,6 +1149,7 @@ export default {
IsShowParent: 0,
Sort: 0,
Code: null,
CodeEn: null,
Value: null,
ValueCN: null,
EnumType: '',
@ -1111,6 +1164,7 @@ export default {
IsSpecialType: false,
DataType: '',
ChildDataLabel: null,
ChildDataEnLabel: null,
ChildDataValue: null,
DateType: null,
DictionaryCode: null,
@ -1118,6 +1172,7 @@ export default {
ForeignKeyTableName: null,
ForeignKeyValue: null,
ForeignKeyText: null,
ForeignKeyEnText: null,
TableConfigList: [],
UrlConfig: {
RoutePath: null,

View File

@ -213,7 +213,6 @@ export default {
}).then(res => {
this.loading = false
this.list = res.Result
console.log(this.$d.GlobalAssessType)
})
},
handleAdd() {
@ -265,7 +264,7 @@ export default {
}
</script>
<style scoped>
<style lang="scss" scoped>
/deep/ .el-form-item__content{
width: calc(100% - 110px);
}

View File

@ -62,7 +62,7 @@
<template slot-scope="scope">
<div>
<el-radio-group v-model="scope.row.CrterionDictionaryGroup" @change="(v) => {crterionDictionaryGroupChange(v, scope.row)}" size="mini">
<el-radio-button v-for="item of $d.CrterionDictionaryGroup" :label="item.value">{{item.label}}</el-radio-button>
<el-radio-button v-for="item of $d.CrterionDictionaryGroup" :label="item.value" :key="item.id">{{item.label}}</el-radio-button>
</el-radio-group>
</div>
</template>
@ -158,7 +158,6 @@ export default {
},
mounted() {
this.getList()
console.log(this.$d[this.parentCode])
},
methods: {
crterionDictionaryGroupChange(v, row) {
@ -231,7 +230,7 @@ export default {
}
</script>
<style scoped>
<style lang="scss" scoped>
/deep/ .el-form-item__content{
width: calc(100% - 110px);
}

View File

@ -10,7 +10,15 @@
>
<div class="base-dialog-body">
<el-form-item label="类型" prop="Type">
<el-select v-model="form.Type" clearable @change="((val)=>{typeChange(val, form)})">
<el-select
v-model="form.Type"
clearable
@change="
(val) => {
typeChange(val, form)
}
"
>
<el-option
v-for="item of $d.Criterion_Question_Type"
:key="item.value"
@ -31,10 +39,7 @@
v-if="form.Type === 'group'"
prop="GroupClassify"
>
<el-select
v-model="form.GroupClassify"
clearable
>
<el-select v-model="form.GroupClassify" clearable>
<el-option
v-for="item of groupClassifyList"
:key="item.Id"
@ -70,13 +75,9 @@
v-if="form.Type === 'number' || form.Type === 'calculation'"
label="数值类型"
prop="ValueType"
:rules="[
{ required: true, message: '请选择'}
]"
:rules="[{ required: true, message: '请选择' }]"
>
<el-radio-group
v-model="form.ValueType"
>
<el-radio-group v-model="form.ValueType">
<el-radio
v-for="item of $d.ValueType"
:key="item.id"
@ -92,9 +93,7 @@
v-if="form.Type === 'number' || form.Type === 'calculation'"
label="单位"
>
<el-radio-group
v-model="form.Unit"
>
<el-radio-group v-model="form.Unit">
<el-radio
v-for="item of $d.ValueUnit"
:key="item.id"
@ -109,15 +108,26 @@
label="表格类型标识"
prop="LesionType"
>
<el-select
v-model="form.LesionType"
clearable
>
<el-select v-model="form.LesionType" clearable>
<el-option
v-for="item of lesionTypes"
:key="item.Code"
:value="parseInt(item.Code)"
:label="$fd('LesionType',parseInt(item.Code))"
:label="$fd('LesionType', parseInt(item.Code))"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="form.Type === 'basicTable'"
label="表格类型标识"
prop="LesionType"
>
<el-select v-model="form.LesionType" clearable>
<el-option
v-for="item of lesionTypeList"
:key="item.Id"
:value="parseInt(item.Code)"
:label="item.ValueCN"
/>
</el-select>
</el-form-item>
@ -133,25 +143,27 @@
</el-form-item> -->
<!-- 选项类型 -->
<el-form-item
v-if="form.Type === 'select' || form.Type === 'radio' || form.Type === 'calculation'"
v-if="
form.Type === 'select' ||
form.Type === 'radio' ||
form.Type === 'calculation'
"
label="选项类型"
prop="QuestionGenre"
:rules="[
{ required: form.Type !== 'calculation', message: '请选择'}
]"
:rules="[{ required: form.Type !== 'calculation', message: '请选择' }]"
>
<el-radio-group
v-model="form.QuestionGenre"
@change="((val)=>{questionGenreChange(val, form)})"
@change="
(val) => {
questionGenreChange(val, form)
}
"
>
<el-radio
:label="-1"
>
</el-radio>
<el-radio :label="-1"> </el-radio>
<el-radio
v-for="item of $d.TableQuestionType"
v-show="item.value===0 || item.value===3"
v-show="item.value === 0 || item.value === 3"
:key="item.id"
:label="item.value"
>
@ -183,13 +195,8 @@
/>
</el-select>
</el-form-item>
<el-form-item
label="转化显示类型"
prop="ConvertShowType"
>
<el-radio-group
v-model="form.ConvertShowType"
>
<el-form-item label="转化显示类型" prop="ConvertShowType">
<el-radio-group v-model="form.ConvertShowType">
<el-radio
v-for="item of $d.ConvertShowType"
:key="item.id"
@ -200,11 +207,19 @@
</el-radio-group>
</el-form-item>
<el-form-item
v-if="form.QuestionGenre === 3 "
v-if="form.QuestionGenre === 3"
label="关联字典"
prop="DictionaryCode"
>
<el-select v-model="form.DictionaryCode" clearable @change="() => {form.DefaultValue = null}">
<el-select
v-model="form.DictionaryCode"
clearable
@change="
() => {
form.DefaultValue = null
}
"
>
<el-option
v-for="item of dicList"
:key="item.Id"
@ -213,10 +228,7 @@
/>
</el-select>
</el-form-item>
<el-form-item
v-if="form.QuestionGenre === 3 "
label="默认值"
>
<el-form-item v-if="form.QuestionGenre === 3" label="默认值">
<el-select v-model="form.DefaultValue" clearable>
<el-option
v-for="item of form.DictionaryCode ? $d[form.DictionaryCode] : []"
@ -226,10 +238,7 @@
/>
</el-select>
</el-form-item>
<el-form-item
v-if="form.QuestionGenre === 0 "
label="默认值"
>
<el-form-item v-if="form.QuestionGenre === 0" label="默认值">
<el-select v-model="form.DefaultValue" clearable>
<el-option
v-for="item of form.TypeValue ? form.TypeValue.split('|') : []"
@ -240,10 +249,18 @@
</el-select>
</el-form-item>
<el-form-item v-if="form.Type !== 'group'" label="是否显示" prop="ShowQuestion">
<el-form-item
v-if="form.Type !== 'group'"
label="是否显示"
prop="ShowQuestion"
>
<el-radio-group
v-model="form.ShowQuestion"
@change="((val)=>{isShowQuestionChange(val, form)})"
@change="
(val) => {
isShowQuestionChange(val, form)
}
"
>
<el-radio
v-for="item of $d.ShowQuestion"
@ -257,14 +274,18 @@
<!-- 显示时依赖父问题 -->
<el-form-item
v-if="form.Type !== 'group' && form.ShowQuestion===1"
v-if="form.Type !== 'group' && form.ShowQuestion === 1"
label="显示依赖父问题"
prop="ParentId"
>
<el-select
v-model="form.ParentId"
clearable
@change="((val)=>{parentQuestionChange(val, form)})"
@change="
(val) => {
parentQuestionChange(val, form)
}
"
>
<el-option
v-for="item of parentOptions"
@ -275,15 +296,19 @@
</el-select>
</el-form-item>
<!-- 显示时依赖父问题触发值 -->
<el-form-item v-if="form.ParentId && form.ShowQuestion===1" label="显示触发值" prop="ParentTriggerValueList">
<!-- <el-select v-model="form.ParentTriggerValue" clearable>-->
<!-- <el-option-->
<!-- v-for="item of parentTriggerValOptions"-->
<!-- :key="item.id"-->
<!-- :label="item.label"-->
<!-- :value="item.value.toString()"-->
<!-- />-->
<!-- </el-select>-->
<el-form-item
v-if="form.ParentId && form.ShowQuestion === 1"
label="显示触发值"
prop="ParentTriggerValueList"
>
<!-- <el-select v-model="form.ParentTriggerValue" clearable>-->
<!-- <el-option-->
<!-- v-for="item of parentTriggerValOptions"-->
<!-- :key="item.id"-->
<!-- :label="item.label"-->
<!-- :value="item.value.toString()"-->
<!-- />-->
<!-- </el-select>-->
<el-select v-model="form.ParentTriggerValueList" clearable multiple>
<el-option
v-for="item of parentTriggerValOptions"
@ -293,16 +318,44 @@
/>
</el-select>
</el-form-item>
<el-form-item v-if="form.Type === 'select' || form.Type === 'radio' || form.Type === 'calculation'" label="是否裁判问题">
<el-radio-group v-model="form.IsJudgeQuestion" @change="((val)=>{isJudgeQuestionChange(val, form)})">
<el-radio v-for="item of $d.YesOrNo" :key="`YesOrNo${item.value}`" :label="item.value">{{ item.label }}</el-radio>
<el-form-item
v-if="
form.Type === 'select' ||
form.Type === 'radio' ||
form.Type === 'calculation' ||
form.Type === 'number'
"
label="是否裁判问题"
>
<el-radio-group
v-model="form.IsJudgeQuestion"
@change="
(val) => {
isJudgeQuestionChange(val, form)
}
"
>
<el-radio
v-for="item of $d.YesOrNo"
:key="`YesOrNo${item.value}`"
:label="item.value"
>{{ item.label }}</el-radio
>
</el-radio-group>
</el-form-item>
<el-form-item v-if="form.Type !== 'group' && form.Type !== 'table'" label="是否必填" prop="IsRequired">
<el-form-item
v-if="form.Type !== 'group' && form.Type !== 'table'"
label="是否必填"
prop="IsRequired"
>
<el-radio-group
v-model="form.IsRequired"
:disabled="form.IsJudgeQuestion===true || form.ShowQuestion===2"
@change="((val)=>{isRequiredChange(val, form)})"
:disabled="form.IsJudgeQuestion === true || form.ShowQuestion === 2"
@change="
(val) => {
isRequiredChange(val, form)
}
"
>
<el-radio
v-for="item of $d.QuestionRequired"
@ -323,7 +376,11 @@
<el-select
v-model="form.RelevanceId"
clearable
@change="((val)=>{relevanceQuestionChange(val, form)})"
@change="
(val) => {
relevanceQuestionChange(val, form)
}
"
>
<el-option
v-for="item of parentOptions"
@ -339,14 +396,14 @@
label="必填触发值"
prop="RelevanceValueList"
>
<!-- <el-select v-model="form.RelevanceValue" clearable>-->
<!-- <el-option-->
<!-- v-for="item of reParentTriggerValOptions"-->
<!-- :key="item.id"-->
<!-- :label="item.label"-->
<!-- :value="item.value.toString()"-->
<!-- />-->
<!-- </el-select>-->
<!-- <el-select v-model="form.RelevanceValue" clearable>-->
<!-- <el-option-->
<!-- v-for="item of reParentTriggerValOptions"-->
<!-- :key="item.id"-->
<!-- :label="item.label"-->
<!-- :value="item.value.toString()"-->
<!-- />-->
<!-- </el-select>-->
<el-select v-model="form.RelevanceValueList" clearable multiple>
<el-option
v-for="item of reParentTriggerValOptions"
@ -385,7 +442,11 @@
</el-form-item> -->
<el-form-item v-if="form.Type !== 'group'" label="问题分组">
<el-select v-model="form.GroupId" clearable :disabled="isParentExistGroup">
<el-select
v-model="form.GroupId"
clearable
:disabled="isParentExistGroup"
>
<el-option
v-for="group of groupOptions"
:key="group.GroupId"
@ -396,12 +457,22 @@
</el-form-item>
<el-form-item label="限制编辑">
<el-radio-group v-model="form.LimitEdit">
<el-radio v-for="item of $d.LimitEdit" :key="`LimitEdit${item.value}`" :label="item.value">{{ item.label }}</el-radio>
<el-radio
v-for="item of $d.LimitEdit"
:key="`LimitEdit${item.value}`"
:label="item.value"
>{{ item.label }}</el-radio
>
</el-radio-group>
</el-form-item>
<el-form-item label="全局阅片是否显示">
<el-radio-group v-model="form.GlobalReadingShowType">
<el-radio v-for="item of $d.GlobalReadingShowType" :key="item.id" :label="item.value">{{ item.label }}</el-radio>
<el-radio
v-for="item of $d.GlobalReadingShowType"
:key="item.id"
:label="item.value"
>{{ item.label }}</el-radio
>
</el-radio-group>
</el-form-item>
<el-form-item
@ -409,10 +480,7 @@
v-if="form.Type !== 'group'"
prop="QuestionType"
>
<el-select
v-model="form.QuestionType"
clearable
>
<el-select v-model="form.QuestionType" clearable>
<el-option
v-for="item of CriterionDictionaryList.QuestionType"
:key="item.Id"
@ -428,9 +496,18 @@
</el-radio-group>
</el-form-item> -->
<el-form-item v-if="form.Type === 'upload'" label="最大上传个数">
<el-input-number v-model="form.ImageCount" controls-position="right" :min="1" :max="10" />
<el-input-number
v-model="form.ImageCount"
controls-position="right"
:min="1"
:max="10"
/>
</el-form-item>
<el-form-item v-if="form.Type === 'select' || form.Type === 'radio'" label="高亮标记值" prop="HighlightAnswerList">
<el-form-item
v-if="form.Type === 'select' || form.Type === 'radio'"
label="高亮标记值"
prop="HighlightAnswerList"
>
<el-select v-model="form.HighlightAnswerList" clearable multiple>
<template v-if="form.TypeValue">
<el-option
@ -471,20 +548,23 @@
<el-form-item label="是否在阅片页面显示">
<el-radio-group v-model="form.IsShowInDicom">
<el-radio v-for="item of $d.YesOrNo" :key="`YesOrNo${item.value}`" :label="item.value">{{ item.label }}</el-radio>
<el-radio
v-for="item of $d.YesOrNo"
:key="`YesOrNo${item.value}`"
:label="item.value"
>{{ item.label }}</el-radio
>
</el-radio-group>
</el-form-item>
<el-form-item
:label="$t('trials:readingUnit:qsList:title:Remark')"
prop="Remark"
>
<el-input
v-model="form.Remark"
/>
<el-input v-model="form.Remark" />
</el-form-item>
</div>
<div class="base-dialog-footer" style="text-align:right;margin-top:10px;">
<div class="base-dialog-footer" style="text-align: right; margin-top: 10px">
<el-form-item>
<!-- 取消 -->
<el-button
@ -496,7 +576,12 @@
{{ $t('common:button:cancel') }}
</el-button>
<!-- 保存 -->
<el-button size="small" type="primary" :loading="btnLoading" @click="save">
<el-button
size="small"
type="primary"
:loading="btnLoading"
@click="save"
>
{{ $t('common:button:save') }}
</el-button>
</el-form-item>
@ -504,20 +589,31 @@
</el-form>
</template>
<script>
import { getBasicConfigSelect, addOrUpdateReadingQuestionSystem, getSystemCriterionOtherQuestion, getSystemGroupNameList, getCriterionDictionary, getCriterionDictionaryList } from '@/api/dictionary'
import {
getBasicConfigSelect,
addOrUpdateReadingQuestionSystem,
getSystemCriterionOtherQuestion,
getSystemGroupNameList,
getCriterionDictionary,
getCriterionDictionaryList,
} from '@/api/dictionary'
import { getCriterionLesionType } from '@/api/trials'
export default {
name: 'AddOrUpdateClinicalData',
props: {
data: {
type: Object,
default() { return {} }
default() {
return {}
},
},
type: {
type: String,
default() { return '' }
}
},
default() {
return ''
},
},
},
data() {
var validateTypeVal = (rule, value, callback) => {
if (value === '') {
@ -553,7 +649,7 @@ export default {
IsJudgeQuestion: false,
GroupName: '',
GroupEnName: '',
GroupClassify:null,
GroupClassify: null,
Remark: '',
ImageCount: 1,
RelevanceId: '',
@ -570,32 +666,38 @@ export default {
GroupId: null,
ConvertShowType: 0,
QuestionClassify: null,
HighlightAnswerList: []
HighlightAnswerList: [],
},
rules: {
Type: [
{ required: true, message: '请选择', trigger: 'blur' }
Type: [{ required: true, message: '请选择', trigger: 'blur' }],
LesionType: [{ required: true, message: '请选择', trigger: 'blur' }],
QuestionName: [
{ required: true, message: '请注明', trigger: 'blur' },
{ max: 300, message: '最大长度为 300' },
],
LesionType: [
{ required: true, message: '请选择', trigger: 'blur' }
],
QuestionName: [{ required: true, message: '请注明', trigger: 'blur' },
{ max: 300, message: '最大长度为 300' }],
QuestionGenre: [{ required: true, message: '请选择', trigger: 'blur' }],
TypeValue: [{ required: true, message: '请注明', trigger: 'blur' },
TypeValue: [
{ required: true, message: '请注明', trigger: 'blur' },
{ validator: validateTypeVal, trigger: 'blur' },
{ max: 500, message: '最大长度为 500' }],
DictionaryCode: [{ required: true, message: '请选择', trigger: 'blur' }],
{ max: 500, message: '最大长度为 500' },
],
DictionaryCode: [
{ required: true, message: '请选择', trigger: 'blur' },
],
ShowQuestion: [{ required: true, message: '请选择', trigger: 'blur' }],
IsRequired: [{ required: true, message: '请选择', trigger: 'blur' }],
ParentId: [{ required: true, message: '请选择', trigger: 'blur' }],
ParentTriggerValueList: [
{ required: true, message: '请选择', trigger: 'blur' }
{ required: true, message: '请选择', trigger: 'blur' },
],
RelevanceId: [{ required: true, message: '请选择', trigger: 'blur' }],
RelevanceValueList: [{ required: true, message: '请选择', trigger: 'blur' }],
GroupName: [{ required: true, message: '请注明', trigger: 'blur' },
{ max: 50, message: '最大长度为 50' }]
RelevanceValueList: [
{ required: true, message: '请选择', trigger: 'blur' },
],
GroupName: [
{ required: true, message: '请注明', trigger: 'blur' },
{ max: 50, message: '最大长度为 50' },
],
},
loading: false,
btnLoading: false,
@ -606,8 +708,9 @@ export default {
lesionTypes: [],
dicList: [],
CriterionDictionaryList: [],
groupClassifyList:[],
highlightAnswers: []
groupClassifyList: [],
highlightAnswers: [],
lesionTypeList: []
}
},
watch: {
@ -622,14 +725,14 @@ export default {
}
let res = await getCriterionDictionary({
ReadingCriterionId: this.data.ReadingQuestionCriterionSystemId,
DictionaryCode: this.form.DictionaryCode
DictionaryCode: this.form.DictionaryCode,
})
this.highlightAnswers = res.Result[this.form.DictionaryCode]
} catch (e) {
console.log(e)
}
}
}
},
},
},
mounted() {
this.initForm()
@ -638,22 +741,31 @@ export default {
getCriterionDictionary() {
getCriterionDictionary({
ReadingCriterionId: this.data.ReadingQuestionCriterionSystemId,
DictionaryCode: 'QuestionType'
}).then(res => {
DictionaryCode: 'QuestionType',
}).then((res) => {
this.CriterionDictionaryList = res.Result
})
},
getGroupClassifyDictionary() {
getCriterionDictionary({
ReadingCriterionId: this.data.ReadingQuestionCriterionSystemId,
DictionaryCode: 'GroupClassify'
}).then(res => {
DictionaryCode: 'GroupClassify',
}).then((res) => {
this.groupClassifyList = res.Result.GroupClassify
})
},
getLesionTypeDictionary() {
getCriterionDictionary({
ReadingCriterionId: this.data.ReadingQuestionCriterionSystemId,
DictionaryCode: 'LesionType',
}).then((res) => {
this.lesionTypeList = res.Result.LesionType
})
},
async initForm() {
await this.getGroupOptions()
await this.getParentQuestions()
await this.getLesionTypeDictionary()
await this.getLesionType()
if (Object.keys(this.data).length > 0) {
for (const k in this.form) {
@ -661,37 +773,47 @@ export default {
this.form[k] = this.data[k]
}
}
if (this.form.ParentId !== '' && this.form.ParentId !== null && this.form.GroupName !== '') {
if (
this.form.ParentId !== '' &&
this.form.ParentId !== null &&
this.form.GroupName !== ''
) {
this.isParentExistGroup = true
}
if (this.form.ParentId) {
var index = this.parentOptions.findIndex(item => {
var index = this.parentOptions.findIndex((item) => {
return item.QuestionId === this.form.ParentId
})
if (index !== -1) {
if (this.parentOptions[index].QuestionGenre === 3) {
this.parentTriggerValOptions = this.$d[this.parentOptions[index].DictionaryCode]
this.parentTriggerValOptions =
this.$d[this.parentOptions[index].DictionaryCode]
} else {
const options = []
this.parentOptions[index].TypeValue.split('|').forEach((item, index) => {
options.push({ id: index, label: item, value: item })
})
this.parentOptions[index].TypeValue.split('|').forEach(
(item, index) => {
options.push({ id: index, label: item, value: item })
}
)
this.parentTriggerValOptions = options
}
}
}
if (this.form.RelevanceId) {
var index = this.parentOptions.findIndex(item => {
var index = this.parentOptions.findIndex((item) => {
return item.QuestionId === this.form.RelevanceId
})
if (index !== -1) {
if (this.parentOptions[index].QuestionGenre === 3) {
this.reParentTriggerValOptions = this.$d[this.parentOptions[index].DictionaryCode]
this.reParentTriggerValOptions =
this.$d[this.parentOptions[index].DictionaryCode]
} else {
const options = []
this.parentOptions[index].TypeValue.split('|').forEach((item, index) => {
options.push({ id: index, label: item, value: item })
})
this.parentOptions[index].TypeValue.split('|').forEach(
(item, index) => {
options.push({ id: index, label: item, value: item })
}
)
this.reParentTriggerValOptions = options
}
}
@ -701,18 +823,19 @@ export default {
this.getGroupClassifyDictionary()
},
save() {
this.$refs.clinicalDataForm.validate(valid => {
this.$refs.clinicalDataForm.validate((valid) => {
if (!valid) return
this.btnLoading = true
if (this.form.Type !== 'upload') {
this.form.ImageCount = 0
}
addOrUpdateReadingQuestionSystem(this.form).then(res => {
this.btnLoading = false
this.$emit('getList')
this.$emit('close')
this.$message.success(this.$t('common:message:savedSuccessfully'))
})
addOrUpdateReadingQuestionSystem(this.form)
.then((res) => {
this.btnLoading = false
this.$emit('getList')
this.$emit('close')
this.$message.success(this.$t('common:message:savedSuccessfully'))
})
.catch(() => {
this.btnLoading = false
})
@ -722,33 +845,38 @@ export default {
return new Promise((resolve, reject) => {
this.loading = true
var param = {
criterionId: this.data.ReadingQuestionCriterionSystemId
criterionId: this.data.ReadingQuestionCriterionSystemId,
}
getSystemGroupNameList(param).then(res => {
this.groupOptions = res.Result
this.loading = false
resolve()
}).catch(() => {
this.loading = false
reject()
})
getSystemGroupNameList(param)
.then((res) => {
this.groupOptions = res.Result
this.loading = false
resolve()
})
.catch(() => {
this.loading = false
reject()
})
})
},
getParentQuestions() {
return new Promise((resolve, reject) => {
this.loading = true
var param = {
readingQuestionCriterionSystemId: this.data.ReadingQuestionCriterionSystemId,
id: this.data.Id
readingQuestionCriterionSystemId:
this.data.ReadingQuestionCriterionSystemId,
id: this.data.Id,
}
getSystemCriterionOtherQuestion(param).then(res => {
this.parentOptions = res.Result
this.loading = false
resolve()
}).catch(() => {
this.loading = false
reject()
})
getSystemCriterionOtherQuestion(param)
.then((res) => {
this.parentOptions = res.Result
this.loading = false
resolve()
})
.catch(() => {
this.loading = false
reject()
})
})
},
questionGenreChange(val, form) {
@ -819,17 +947,20 @@ export default {
parentQuestionChange(val, form) {
this.isParentExistGroup = false
if (val) {
var index = this.parentOptions.findIndex(item => {
var index = this.parentOptions.findIndex((item) => {
return item.QuestionId === val
})
if (index !== -1) {
if (this.parentOptions[index].QuestionGenre === 3) {
this.parentTriggerValOptions = this.$d[this.parentOptions[index].DictionaryCode]
this.parentTriggerValOptions =
this.$d[this.parentOptions[index].DictionaryCode]
} else {
var options = []
this.parentOptions[index].TypeValue.split('|').forEach((item, index) => {
options.push({ id: index, label: item, value: item })
})
this.parentOptions[index].TypeValue.split('|').forEach(
(item, index) => {
options.push({ id: index, label: item, value: item })
}
)
this.parentTriggerValOptions = options
}
if (this.parentOptions[index].GroupName) {
@ -845,17 +976,20 @@ export default {
},
relevanceQuestionChange(val, form) {
if (val) {
var index = this.parentOptions.findIndex(item => {
var index = this.parentOptions.findIndex((item) => {
return item.QuestionId === val
})
if (index !== -1) {
if (this.parentOptions[index].QuestionGenre === 3) {
this.reParentTriggerValOptions = this.$d[this.parentOptions[index].DictionaryCode]
this.reParentTriggerValOptions =
this.$d[this.parentOptions[index].DictionaryCode]
} else {
var options = []
this.parentOptions[index].TypeValue.split('|').forEach((item, index) => {
options.push({ id: index, label: item, value: item })
})
this.parentOptions[index].TypeValue.split('|').forEach(
(item, index) => {
options.push({ id: index, label: item, value: item })
}
)
this.reParentTriggerValOptions = options
}
}
@ -871,23 +1005,24 @@ export default {
return new Promise((resolve, reject) => {
this.loading = true
var param = {
SystemCriterionId: this.data.ReadingQuestionCriterionSystemId
SystemCriterionId: this.data.ReadingQuestionCriterionSystemId,
}
getCriterionLesionType(param).then(res => {
this.lesionTypes = res.Result
this.loading = false
resolve()
}).catch(() => {
this.loading = false
reject()
})
getCriterionLesionType(param)
.then((res) => {
this.lesionTypes = res.Result
this.loading = false
resolve()
})
.catch(() => {
this.loading = false
reject()
})
})
},
close() {
this.$emit('close')
}
}
},
},
}
</script>
<style lang="scss" scoped>

View File

@ -170,7 +170,7 @@
<el-button
type="primary"
size="mini"
:disabled="scope.row.Type !== 'table'"
:disabled="!(scope.row.Type === 'table' || scope.row.Type === 'basicTable')"
@click="handleConfig(scope.row)"
>
表格问题

View File

@ -69,7 +69,7 @@
>{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="序号">
<el-form-item label="序号" prop="ShowOrder">
<el-input v-model="form.ShowOrder" type="number" />
</el-form-item>
<!-- 描述 -->
@ -135,7 +135,8 @@ export default {
Part: [{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: ['blur'] }],
TULOC: [{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: ['blur'] }],
Remark: [{ max: 500, message: `${this.$t('common:ruleMessage:maxLength')} 500`, trigger: ['blur', 'change'] }],
IsLymphNodes: [{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur'] }]
IsLymphNodes: [{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur'] }],
ShowOrder: [{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: ['blur'] }]
},
lesionTypes: [],
loading: false,

View File

@ -5,7 +5,7 @@
<el-form :inline="true" size="mini">
<el-form-item label="器官类型">
<el-select v-model="searchData.OrganType" clearable style="width:130px;">
<el-option v-for="item of CriterionDictionaryOrganTypeList.OrganType" :key="item.Id" :value="parseInt(item.Code)" :label="item.ValueCN" />
<el-option v-for="item of CriterionDictionaryOrganTypeList && CriterionDictionaryOrganTypeList.OrganType" :key="item.Id" :value="parseInt(item.Code)" :label="item.ValueCN" />
</el-select>
</el-form-item>
<el-form-item label="部位">

View File

@ -355,11 +355,11 @@
<el-radio v-for="item of $d.LimitEdit" :key="item.value" :label="item.value">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="全局阅片是否显示">
<!-- <el-form-item label="全局阅片是否显示">
<el-radio-group v-model="form.GlobalReadingShowType">
<el-radio v-for="item of $d.GlobalReadingShowType" :key="item.id" :label="item.value">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
</el-radio-group>
</el-form-item> -->
</div>
<div class="base-dialog-footer" style="text-align:right;margin-top:10px;">
<el-form-item>
@ -445,7 +445,7 @@ export default {
DependParentId: '',
QuestionMark: null,
LimitEdit: 0,
GlobalReadingShowType: null,
// GlobalReadingShowType: null,
QuestionClassify: null,
HighlightAnswerList: []

View File

@ -1,11 +1,29 @@
<template>
<div style="display: flex;justify-content: center">
<div style="width: 600px;text-align: center;border: 1px solid #e6e6e6;margin-top:40px;padding:10px;">
<div class="trial-myinfo-head" style="font-size: 30px;line-height: 120px;">
<div style="display: flex; justify-content: center">
<div
style="
width: 600px;
text-align: center;
border: 1px solid #e6e6e6;
margin-top: 40px;
padding: 10px;
"
>
<div
class="trial-myinfo-head"
style="font-size: 30px; line-height: 120px"
>
<!-- 首次登录修改密码 -->
{{ $t('recompose:title:init') }}
</div>
<el-form ref="passwordForm" v-loading="loading" label-position="right" :model="password" :rules="passwordFormRules" label-width="200px">
<el-form
ref="passwordForm"
v-loading="loading"
label-position="right"
:model="password"
:rules="passwordFormRules"
:label-width="$i18n.locale == 'zh' ? '100px' : '200px'"
>
<!-- 邮箱 -->
<el-form-item :label="$t('recompose:form:email')" prop="Email">
<el-input v-model="password.Email" disabled />
@ -19,70 +37,135 @@
<el-input v-model="password.NewUserName" />
</el-form-item>
<!-- 新密码 -->
<el-form-item :label="$t('recompose:form:newPassword')" prop="NewPassWord">
<el-input v-model="password.NewPassWord" type="password" show-password auto-complete="new-password" />
<el-form-item
:label="$t('recompose:form:newPassword')"
prop="NewPassWord"
>
<el-input
v-model="password.NewPassWord"
type="password"
show-password
auto-complete="new-password"
/>
</el-form-item>
<!-- 确认密码 -->
<el-form-item :label="$t('recompose:form:confirmPassword')" prop="ConfirmPassWord">
<el-input v-model="password.ConfirmPassWord" type="password" show-password auto-complete="new-password" />
<el-form-item
:label="$t('recompose:form:confirmPassword')"
prop="ConfirmPassWord"
>
<el-input
v-model="password.ConfirmPassWord"
type="password"
show-password
auto-complete="new-password"
/>
</el-form-item>
<el-form-item style="text-align:right">
<el-form-item style="text-align: right">
<!-- 取消 -->
<el-button
size="small"
@click="cancel"
>
<el-button size="small" @click="cancel">
{{ $t('recompose:button:cancel') }}
</el-button>
<!-- 保存 -->
<el-button
type="primary"
size="small"
@click="save"
>
<el-button type="primary" size="small" @click="save">
{{ $t('recompose:button:save') }}
</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
import { initSetUserNameAndPwd } from '@/api/admin.js'
import md5 from 'js-md5'
import {mapGetters, mapMutations} from "vuex";
import { mapGetters, mapMutations } from 'vuex'
export default {
data() {
return {
password: {
NewUserName: null,
UserId: null
UserId: null,
},
passwordFormRules: {
NewUserName: [{ required: true, validator: (rule, value, callback) => {!value ? callback(new Error(this.$t('trials:researchForm:formRule:specify'))) : callback()}, trigger: 'blur' }],
NewUserName: [
{
required: true,
validator: (rule, value, callback) => {
var lang = zzSessionStorage.getItem('lang')
? zzSessionStorage.getItem('lang')
: 'zh'
/* eslint-disable */
var reg1 = /^[a-zA-Z0-9_]{4,16}$/ //8
console.log(!reg1.test(value))
if (!reg1.test(value)) {
callback(
lang === 'zh'
? new Error(
'1新建账号用户名字符长度最小为4个字符最大为16个字符只可使用字母、数字、下划线'
)
: new Error(
'For a new account, the username must have:1) At least 4 characters;2) At most 16 characters;3)Only letters, numbers, and underscores are allowed.'
)
)
} else {
callback()
}
},
trigger: 'blur',
},
],
NewPassWord: [
{ required: true, validator: (rule, value, callback) => {!value ? callback(new Error(this.$t('trials:researchForm:formRule:specify'))) : callback()}, trigger: 'blur' },
{
required: true,
validator: (rule, value, callback) => {
!value
? callback(
new Error(this.$t('trials:researchForm:formRule:specify'))
)
: callback()
},
trigger: 'blur',
},
{
required: true,
trigger: 'blur',
validator: this.$validatePassword
validator: this.$validatePassword,
},
],
ConfirmPassWord: [
{ required: true, validator: (rule, value, callback) => {!value ? callback(new Error(this.$t('trials:researchForm:formRule:specify'))) : callback()}, trigger: 'blur' }
]
{
required: true,
validator: (rule, value, callback) => {
!value
? callback(
new Error(this.$t('trials:researchForm:formRule:specify'))
)
: callback()
},
trigger: 'blur',
},
{
required: true,
trigger: 'blur',
validator: (rule, value, callback) => {
value !== this.password.NewPassWord
? callback(
new Error(
this.$t(
'trials:researchForm:formRule:NewPassWordAndConfirmPassWord'
)
)
)
: callback()
},
},
],
},
userId: null,
loading: false
loading: false,
}
},
computed: {
...mapGetters([
'asyncRoutes',
'routes',
'language'
])
...mapGetters(['asyncRoutes', 'routes', 'language']),
},
mounted() {
this.password.UserId = this.$route.query.UserId
@ -112,7 +195,7 @@ export default {
this.$updateDictionary()
},
save() {
this.$refs.passwordForm.validate(valid => {
this.$refs.passwordForm.validate((valid) => {
if (valid) {
if (this.password.NewPassWord !== this.password.ConfirmPassWord) {
//
@ -123,26 +206,32 @@ export default {
NewUserName: this.password.NewUserName,
UserId: this.password.UserId,
NewPwd: md5(this.password.NewPassWord),
access_token: this.password.access_token
access_token: this.password.access_token,
}
this.loading = true
initSetUserNameAndPwd(param).then(res => {
this.loading = false
if (res.IsSuccess) {
// ,
this.$message.success(this.$t('recompose:message:updatedSuccessfully'))
setTimeout(() => {
this.logout()
}, 500)
}
}).catch(() => { this.loading = false })
initSetUserNameAndPwd(param)
.then((res) => {
this.loading = false
if (res.IsSuccess) {
// ,
this.$message.success(
this.$t('recompose:message:updatedSuccessfully')
)
setTimeout(() => {
this.logout()
}, 500)
}
})
.catch(() => {
this.loading = false
})
}
})
},
cancel() {
this.$refs['passwordForm'].resetFields()
}
}
},
},
}
</script>
<style>
@ -165,13 +254,16 @@ export default {
<style scoped>
input:-webkit-autofill {
-webkit-text-fill-color: #ededed !important;
box-shadow: 0 0 0px 1000px transparent inset !important;
background-color:transparent;
box-shadow: 0 0 0px 1000px transparent inset !important;
background-color: transparent;
background-image: none;
transition: background-color 50000s ease-in-out 0s;
}
input {
background-color:transparent;
background-color: transparent;
caret-color: #fff;
}
.el-form-item {
margin-bottom: 30px;
}
</style>

View File

@ -114,7 +114,7 @@
size="mini"
@click="getList"
>
{{ $t("common:button:search") }}
{{ $t('common:button:search') }}
</el-button>
<!-- 重置 -->
<el-button
@ -122,7 +122,7 @@
icon="el-icon-refresh-left"
@click="handleReset"
>
{{ $t("common:button:reset") }}
{{ $t('common:button:reset') }}
</el-button>
<!-- <el-button
type="primary"
@ -141,7 +141,7 @@
v-if="level > 7"
:disabled="tableSelectData.length <= 0"
>
{{ $t("feedBack:button:resolve") }}
{{ $t('feedBack:button:resolve') }}
</el-button>
</el-form>
<el-table
@ -257,7 +257,7 @@
>
<template slot-scope="scope">
<el-tag :type="['danger', 'success'][scope.row.State]">{{
$fd("FeedBackStatus", scope.row.State)
$fd('FeedBackStatus', scope.row.State)
}}</el-tag>
</template>
</el-table-column>
@ -278,7 +278,7 @@
>
<template slot-scope="scope">
<el-button size="mini" type="text" @click="getDetail(scope.row)">
{{ $t("common:button:view") }}
{{ $t('common:button:view') }}
</el-button>
</template>
</el-table-column>
@ -295,8 +295,8 @@
</div>
</template>
<script>
import Pagination from "@/components/Pagination";
import { batchUpdateFeedBackState, getUserFeedBackList } from "@/api/trials.js";
import Pagination from '@/components/Pagination'
import { batchUpdateFeedBackState, getUserFeedBackList } from '@/api/trials.js'
const searchDataDefault = () => {
return {
TrialKeyInfo: null,
@ -312,23 +312,23 @@ const searchDataDefault = () => {
PageIndex: 1,
PageSize: 20,
Asc: false,
SortField: "CreateTime",
};
};
SortField: 'CreateTime',
}
}
export default {
components: { Pagination },
props: {
trialId: {
type: String,
default: "",
default: '',
},
},
dicts: [
"NoticeApplicableTrial",
"NoteLevel",
"NoteType",
"NoticeState",
"NoticeMode",
'NoticeApplicableTrial',
'NoteLevel',
'NoteType',
'NoticeState',
'NoticeMode',
],
data() {
return {
@ -340,100 +340,100 @@ export default {
searchData: searchDataDefault(),
tableSelectData: [],
UserTypeOptins: [
{ label: "CRC", value: 2 },
{ label: "IR", value: 13 },
{ label: 'CRC', value: 2 },
{ label: 'IR', value: 13 },
],
};
}
},
computed: {
level() {
if (this.hasPermi(["role:dev", "role:admin"])) {
return 9;
if (this.hasPermi(['role:dev', 'role:admin'])) {
return 9
}
if (this.hasPermi(["role:pm"])) {
return 8;
if (this.hasPermi(['role:pm', 'role:apm'])) {
return 8
}
if (this.hasPermi(["role:ir", "role:crc"])) {
return 7;
if (this.hasPermi(['role:ir', 'role:crc'])) {
return 7
}
return 0;
return 0
},
QuestionTypeOptions() {
if (this.level > 7) {
return [
...this.$d.FeedBackTypeToCRC.filter((item) => item.value > 0),
...this.$d.FeedBackTypeToIR,
];
]
}
if (this.hasPermi(["role:ir"])) {
return this.$d.FeedBackTypeToIR;
if (this.hasPermi(['role:ir'])) {
return this.$d.FeedBackTypeToIR
}
if (this.hasPermi(["role:crc"])) {
return this.$d.FeedBackTypeToCRC;
if (this.hasPermi(['role:crc'])) {
return this.$d.FeedBackTypeToCRC
}
return [];
return []
},
},
created() {
this.getList();
this.getList()
},
methods: {
// CheckBox
handleSelectTable(row) {
return row.State < 1;
return row.State < 1
},
async getList() {
try {
if (this.trialId) {
this.searchData.TrialId = this.trialId;
this.searchData.TrialId = this.trialId
}
this.loading = true;
let res = await getUserFeedBackList(this.searchData);
this.loading = false;
this.loading = true
let res = await getUserFeedBackList(this.searchData)
this.loading = false
if (res.IsSuccess) {
this.list = res.Result.CurrentPageData;
this.total = res.Result.TotalCount;
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
}
} catch (err) {
this.loading = false;
console.log(err);
this.loading = false
console.log(err)
}
},
handleDatetimeChange(val) {
if (val) {
this.searchData.BeginCreatime = val[0];
this.searchData.EndCreatime = val[1];
this.searchData.BeginCreatime = val[0]
this.searchData.EndCreatime = val[1]
} else {
this.searchData.BeginCreatime = "";
this.searchData.EndCreatime = "";
this.searchData.BeginCreatime = ''
this.searchData.EndCreatime = ''
}
},
//
handleReset() {
this.datetimerange = null;
this.handleDatetimeChange();
this.searchData = searchDataDefault();
this.getList();
this.datetimerange = null
this.handleDatetimeChange()
this.searchData = searchDataDefault()
this.getList()
},
//
async resolve() {
try {
let confirm = await this.$confirm(this.$t("feedBack:resolve:tip"), {
type: "warning",
let confirm = await this.$confirm(this.$t('feedBack:resolve:tip'), {
type: 'warning',
distinguishCancelAndClose: true,
});
if (!confirm) return;
})
if (!confirm) return
let data = {
IdList: this.tableSelectData.map((item) => item.Id),
state: 1,
};
let res = await batchUpdateFeedBackState(data);
}
let res = await batchUpdateFeedBackState(data)
if (res.IsSuccess) {
this.$message.success(this.$t("feedBack:resolve:success"));
this.getList();
this.$message.success(this.$t('feedBack:resolve:success'))
this.getList()
}
} catch (err) {
console.log(err);
console.log(err)
}
},
//
@ -441,32 +441,32 @@ export default {
//
handleSelectChange(selection) {
// console.log(selection, "handleSelectChange");
this.tableSelectData = selection;
this.tableSelectData = selection
},
//
handleSortByColumn(column) {
if (column.order === "ascending") {
this.searchData.Asc = true;
if (column.order === 'ascending') {
this.searchData.Asc = true
} else {
this.searchData.Asc = false;
this.searchData.Asc = false
}
this.searchData.SortField = column.prop;
this.searchData.PageIndex = 1;
this.getList();
this.searchData.SortField = column.prop
this.searchData.PageIndex = 1
this.getList()
},
//
getDetail(row) {
this.$FB({
type: "detail",
type: 'detail',
Id: row.Id,
callBack: () => {
this.getList();
this.$emit("success");
this.getList()
this.$emit('success')
},
});
})
},
},
};
}
</script>
<style lang="scss">
.role {

View File

@ -61,16 +61,16 @@
:disabled="form.VerificationCode === '' || form.EmailOrPhone === ''"
@click="verifyCode"
>
{{ $t("passwordReset:button:verify") }}
{{ $t('passwordReset:button:verify') }}
</el-button>
</div>
<div class="box-wrapper" v-if="active === 1">
<div class="box-wrapper" v-show="active === 1">
<el-form
ref="resetForm"
ref="resetForm2"
v-loading="formLoading"
:model="form"
label-width="100px"
:rules="rules"
:label-width="$i18n.locale === 'en' ? '180px' : '100px'"
:rules="rules2"
class="demo-ruleForm"
size="small"
>
@ -131,7 +131,7 @@
<div style="width: 50%; margin: auto; display: flex">
<!-- 取消 -->
<el-button size="small" @click="onCancel" style="width: 46%">
{{ $t("passwordReset:button:cancel") }}
{{ $t('passwordReset:button:cancel') }}
</el-button>
<!-- 提交 -->
<el-button
@ -140,17 +140,17 @@
@click="onSubmit"
style="width: 46%"
>
{{ $t("passwordReset:button:submit") }}
{{ $t('passwordReset:button:submit') }}
</el-button>
</div>
</div>
<div class="box-wrapper flexBox" v-if="active === 3">
<svg-icon icon-class="resetSuccess" style="width: 300px; height: 300px" />
<p style="width: 100%; text-align: center">
{{ $t("resetPassword:successTip") }}
{{ $t('resetPassword:successTip') }}
</p>
<el-button size="small" type="primary" @click="goBack">
{{ $t("passwordReset:button:back") }}
{{ $t('passwordReset:button:back') }}
</el-button>
</div>
</div>
@ -160,32 +160,30 @@ import {
anonymousSendVerificationCode,
verifyAnonymousVerifyCode,
anonymousSetPassword,
} from "@/api/user";
import md5 from "js-md5";
import Img from "@/assets/icons/svg/resetSuccess.svg";
var timer = "";
var countdown = 60;
} from '@/api/user'
import md5 from 'js-md5'
import Img from '@/assets/icons/svg/resetSuccess.svg'
var timer = ''
var countdown = 60
export default {
data() {
var validateEmail = (rule, value, callback) => {
if (value === "") {
if (value === '') {
//
callback(new Error(this.$t("passwordReset:formRule:email")));
callback(new Error(this.$t('passwordReset:formRule:email')))
} else {
var reg =
/^[A-Za-z0-9]+([_\.][A-Za-z0-9]+)*@([A-Za-z0-9\-]+\.)+[A-Za-z]{2,6}$/;
/^[A-Za-z0-9]+([_\.][A-Za-z0-9]+)*@([A-Za-z0-9\-]+\.)+[A-Za-z]{2,6}$/
if (this.form.EmailOrPhone && reg.test(this.form.EmailOrPhone)) {
this.sendDisabled = false;
callback();
this.sendDisabled = false
callback()
} else {
//
callback(
new Error(this.$t("passwordReset:formRule:passwordinvalid"))
);
this.sendDisabled = true;
callback(new Error(this.$t('passwordReset:formRule:passwordinvalid')))
this.sendDisabled = true
}
}
};
}
// var validatePass = (rule, value, callback) => {
// if (value === '') {
// //
@ -198,181 +196,206 @@ export default {
// }
// }
var validatePass2 = (rule, value, callback) => {
if (value === "" || value === undefined) {
if (value === '' || value === undefined) {
//
callback(new Error(this.$t("passwordReset:formRule:confirmPassword")));
callback(new Error(this.$t('passwordReset:formRule:confirmPassword')))
} else if (value !== undefined && value !== this.form.NewPwd) {
//
callback(new Error(this.$t("passwordReset:formRule:passwordsDiffer")));
callback(new Error(this.$t('passwordReset:formRule:passwordsDiffer')))
} else {
callback();
callback()
}
};
}
return {
active: 0,
Img,
form: {
EmailOrPhone: "",
VerificationCode: "",
NewPwd: "",
UserId: "",
UserType: "",
EmailOrPhone: '',
VerificationCode: '',
NewPwd: '',
CheckPass: '',
UserId: '',
UserType: '',
},
users: [],
CheckPass: "",
CheckPass: '',
rules: {
EmailOrPhone: [
{
required: true,
validator: validateEmail,
trigger: ["blur", "change"],
trigger: ['blur', 'change'],
},
],
VerificationCode: [
{
required: true,
message: this.$t("common:ruleMessage:specify"),
trigger: ["blur", "change"],
message: this.$t('common:ruleMessage:specify'),
trigger: ['blur', 'change'],
},
],
NewPwd: [
{
required: true,
validator: this.$validatePassword,
trigger: ["blur", "change"],
trigger: ['blur', 'change'],
},
],
CheckPass: [
{
required: true,
validator: validatePass2,
trigger: ["blur", "change"],
trigger: ['blur', 'change'],
},
],
UserId: [
{
required: true,
message: this.$t("common:ruleMessage:select"),
trigger: ["blur", "change"],
message: this.$t('common:ruleMessage:select'),
trigger: ['blur', 'change'],
},
],
},
rules2: {
NewPwd: [
{
required: true,
validator: this.$validatePassword,
trigger: ['blur', 'change'],
},
],
CheckPass: [
{
required: true,
validator: validatePass2,
trigger: ['blur', 'change'],
},
],
UserId: [
{
required: true,
message: this.$t('common:ruleMessage:select'),
trigger: ['blur', 'change'],
},
],
},
sendDisabled: true,
sendTitle: this.$t("passwordReset:button:send"),
sendTitle: this.$t('passwordReset:button:send'),
formLoading: false,
};
}
},
methods: {
onSubmit() {
this.$refs["resetForm"].validate((valid) => {
this.$refs['resetForm2'].validate((valid) => {
if (valid) {
this.formLoading = true;
this.formLoading = true
anonymousSetPassword(this.form.UserId, md5(this.form.NewPwd))
.then((res) => {
if (res.IsSuccess) {
//
this.$message.success(
this.$t("passwordReset:message:updatedSuccessfully")
);
this.formLoading = false;
this.active = 3;
this.$t('passwordReset:message:updatedSuccessfully')
)
this.formLoading = false
this.active = 3
// this.goBack();
} else {
this.$alert(res.ErrorMessage);
this.$alert(res.ErrorMessage)
}
})
.catch(() => {
this.formLoading = false;
});
this.formLoading = false
})
}
});
})
},
handleSendCode() {
const that = this;
this.sendDisabled = true;
const that = this
this.sendDisabled = true
// var isReviewer = JSON.parse(zzSessionStorage.getItem('IsReviewer'))
anonymousSendVerificationCode(this.form.EmailOrPhone)
.then((res) => {
if (res.IsSuccess) {
let msg = this.$t('passwordReset:message:tip:sendCode').replace("xxx", this.form.EmailOrPhone)
let msg = this.$t('passwordReset:message:tip:sendCode').replace(
'xxx',
this.form.EmailOrPhone
)
this.$message.success(msg)
that.settime(that);
that.settime(that)
} else {
that.$alert(res.ErrorMessage);
that.$alert(res.ErrorMessage)
}
})
.catch(() => {
this.sendDisabled = false;
});
this.sendDisabled = false
})
},
verifyCode() {
this.formLoading = true;
this.formLoading = true
verifyAnonymousVerifyCode(
this.form.EmailOrPhone,
this.form.VerificationCode
)
.then((res) => {
this.formLoading = false;
this.users = res.Result;
this.formLoading = false
this.users = res.Result
if (this.users.length === 1) {
this.form.UserId = this.users[0].UserId;
this.form.UserType = this.users[0].UserType;
this.form.UserId = this.users[0].UserId
this.form.UserType = this.users[0].UserType
}
//
this.$message.success(
this.$t("passwordReset:message:verifiedSuccessfully")
);
this.active = 1;
this.$t('passwordReset:message:verifiedSuccessfully')
)
this.active = 1
})
.catch(() => {
this.formLoading = false;
});
this.formLoading = false
})
},
handleEmailChange() {
var reg =
/^[A-Za-z0-9]+([_\.][A-Za-z0-9]+)*@([A-Za-z0-9\-]+\.)+[A-Za-z]{2,6}$/;
/^[A-Za-z0-9]+([_\.][A-Za-z0-9]+)*@([A-Za-z0-9\-]+\.)+[A-Za-z]{2,6}$/
if (this.form.EmailOrPhone && reg.test(this.form.EmailOrPhone)) {
this.sendDisabled = false;
this.sendDisabled = false
}
},
handleUserChange(val) {
const seleted = this.users.findIndex((user) => user.UserId === val);
const seleted = this.users.findIndex((user) => user.UserId === val)
if (seleted > -1) {
this.form.UserType = this.users[seleted].UserType;
this.form.UserType = this.users[seleted].UserType
}
},
settime(obj) {
if (countdown === 0) {
obj.sendDisabled = false;
obj.sendTitle = this.$t("passwordReset:button:send");
countdown = 60;
clearTimeout(timer);
return;
obj.sendDisabled = false
obj.sendTitle = this.$t('passwordReset:button:send')
countdown = 60
clearTimeout(timer)
return
} else {
obj.sendDisabled = true;
obj.sendTitle = `${this.$t(
"passwordReset:button:wait"
)}(${countdown}s)`;
countdown--;
obj.sendDisabled = true
obj.sendTitle = `${this.$t('passwordReset:button:wait')}(${countdown}s)`
countdown--
// eslint-disable-next-line no-self-assign
countdown = countdown;
countdown = countdown
timer = setTimeout(function () {
obj.settime(obj);
}, 1000);
obj.settime(obj)
}, 1000)
}
},
onCancel() {
this.$refs["resetForm"].resetFields();
this.$refs['resetForm'].resetFields()
Object.keys(this.form).forEach((key) => {
this.form[key] = "";
});
this.active = 0;
this.form[key] = ''
})
this.active = 0
},
goBack() {
this.$router.push("/login");
this.$router.push('/login')
},
},
};
}
</script>
<style>
.reset-wrapper {

View File

@ -34,7 +34,7 @@
style="width: 300px; height: 94px"
/>
</div>
<div class="title" v-else>{{ $t("login:title:system") }}</div>
<div class="title" v-else>{{ $t('login:title:system') }}</div>
</div>
<el-form
ref="loginForm"
@ -112,7 +112,7 @@
size="medium"
@click.native.prevent="handleLogin"
>
{{ $t("login:button:login") }}
{{ $t('login:button:login') }}
</el-button>
<div style="text-align: right">
<TopLang
@ -128,7 +128,7 @@
@click.native.prevent="handleResetPwd"
style="color: rgb(0, 147, 221)"
>
{{ $t("login:button:forgetPassword") }}
{{ $t('login:button:forgetPassword') }}
</el-button>
</div>
</el-form>
@ -181,10 +181,12 @@
>
<h1 style="text-align: center; margin-bottom: 20px">关于</h1>
<p style="margin-bottom: 20px" v-if="NODE_ENV === 'usa'">
{{ $t("login:title:system_title") }}
{{ $t('login:title:system_title') }}
</p>
<p style="margin-bottom: 20px" v-else>{{ $t('login:title:system') }}</p>
<p style="margin-bottom: 20px">
V{{ $version.IsEnv_US ? $version.Version_US : $version.Version }}
</p>
<p style="margin-bottom: 20px" v-else>{{ $t("login:title:system") }}</p>
<p style="margin-bottom: 20px">V{{ $version.Version }}</p>
<p style="margin-bottom: 20px" v-if="language === 'zh'">
Copyright © {{ new Date().getFullYear() }} 上海展影医疗科技有限公司
版权所有
@ -208,14 +210,14 @@
</template>
<script>
import { mapGetters, mapMutations } from "vuex";
import TopLang from "./topLang";
import { mapGetters, mapMutations } from 'vuex'
import TopLang from './topLang'
// import NoticeMarquee from '../trials/trials-layout/components/noticeMarquee'
import Vcode from "vue-puzzle-vcode";
import browserTip from "@/views/dictionary/template/browser/tip.vue";
import Img1 from "@/assets/pic-2.png";
import Vcode from 'vue-puzzle-vcode'
import browserTip from '@/views/dictionary/template/browser/tip.vue'
import Img1 from '@/assets/pic-2.png'
export default {
name: "Login",
name: 'Login',
components: { TopLang, Vcode, browserTip },
data() {
return {
@ -223,18 +225,18 @@ export default {
VUE_APP_OSS_CONFIG_REGION: process.env.VUE_APP_OSS_CONFIG_REGION,
aboutVisible: false,
loginForm: {
username: "",
password: "",
username: '',
password: '',
UserId: null,
},
loginRules: {
username: [
{
required: true,
message: this.$t("login:formRule:userName"),
trigger: "blur",
message: this.$t('login:formRule:userName'),
trigger: 'blur',
},
{ max: 20, message: `${this.$t("common:ruleMessage:maxLength")} 20` },
{ max: 20, message: `${this.$t('common:ruleMessage:maxLength')} 20` },
],
password: [
// {
@ -244,29 +246,29 @@ export default {
// },
{
required: true,
message: this.$t("login:formRule:password"),
trigger: "blur",
message: this.$t('login:formRule:password'),
trigger: 'blur',
},
{ max: 20, message: `${this.$t("common:ruleMessage:maxLength")} 20` },
{ max: 20, message: `${this.$t('common:ruleMessage:maxLength')} 20` },
],
},
loading: false,
passwordType: "password",
passwordType: 'password',
loginType: null,
location: null,
isShow: false,
showCode: false,
Img1,
};
}
},
computed: {
...mapGetters(["asyncRoutes", "routes", "language"]),
...mapGetters(['asyncRoutes', 'routes', 'language']),
},
watch: {
"$i18n.locale": {
'$i18n.locale': {
handler() {
if (this.$refs.browserTip) {
this.$refs.browserTip.open();
this.$refs.browserTip.open()
}
},
deep: true,
@ -274,88 +276,88 @@ export default {
},
},
mounted() {
this.loginType = this.$route.query.loginType;
this.location = this.$route.query.location;
zzSessionStorage.setItem("loginType", this.loginType);
localStorage.setItem("location", this.location);
if (process.env.VUE_APP_OSS_CONFIG_REGION === "oss-us-west-1") {
this.$i18n.locale = "en";
this.setLanguage("en");
this.$updateDictionary();
this.loginType = this.$route.query.loginType
this.location = this.$route.query.location
zzSessionStorage.setItem('loginType', this.loginType)
localStorage.setItem('location', this.location)
if (process.env.VUE_APP_OSS_CONFIG_REGION === 'oss-us-west-1') {
this.$i18n.locale = 'en'
this.setLanguage('en')
this.$updateDictionary()
} else {
if (this.location === "USA" || this.NODE_ENV === "usa") {
this.$i18n.locale = "en";
this.setLanguage("en");
this.$updateDictionary();
if (this.location === 'USA' || this.NODE_ENV === 'usa') {
this.$i18n.locale = 'en'
this.setLanguage('en')
this.$updateDictionary()
} else {
// this.$i18n.locale = 'zh'
// this.setLanguage('zh')
// this.$updateDictionary()
}
}
this.$refs.browserTip.open();
this.$refs.browserTip.open()
},
methods: {
...mapMutations({ setLanguage: "lang/setLanguage" }),
...mapMutations({ setLanguage: 'lang/setLanguage' }),
openAbout() {
this.aboutVisible = true;
this.aboutVisible = true
},
showPwd() {
if (this.passwordType === "password") {
this.passwordType = "";
if (this.passwordType === 'password') {
this.passwordType = ''
} else {
this.passwordType = "password";
this.passwordType = 'password'
}
this.$nextTick(() => {
this.$refs.password.focus();
});
this.$refs.password.focus()
})
},
handleLogin() {
this.loginType = this.$route.query.loginType;
this.loginType = this.$route.query.loginType
this.$refs.loginForm.validate((valid) => {
if (valid) {
if (this.showCode) {
this.isShow = true;
this.isShow = true
} else {
this.onSuccess();
this.onSuccess()
}
} else {
// console.log('error submit!!')
return false;
return false
}
});
})
},
loginIn(Id) {
this.loading = true;
this.showCode = false;
if (Id) this.loginForm.UserId = Id;
this.loading = true
this.showCode = false
if (Id) this.loginForm.UserId = Id
this.$store
.dispatch("user/login", this.loginForm)
.dispatch('user/login', this.loginForm)
.then((res) => {
if (res.BasicInfo.IsFirstAdd) {
// ,
this.$message.success(this.$t("login:message:login1"));
this.$message.success(this.$t('login:message:login1'))
setTimeout(() => {
this.$router.push({
path: `/recompose?userName=${this.loginForm.username}`,
});
}, 500);
return;
})
}, 500)
return
} else if (res.BasicInfo.LoginState === 1) {
//
this.$alert(
this.$t("login:message:login3"),
this.$t("common:title:warning"),
this.$t('login:message:login3'),
this.$t('common:title:warning'),
{
callback: (action) => {
this.$router.push({
path: `/recompose?userName=${this.loginForm.username}`,
});
return;
})
return
},
}
);
return;
)
return
} else if (res.IsMFA) {
this.$MFA({
UserId: res.BasicInfo.Id,
@ -363,55 +365,55 @@ export default {
username: this.loginForm.username,
callBack: this.loginIn,
cancelBack: () => {
this.loading = false;
this.loading = false
},
});
return;
})
return
} else if (res.BasicInfo.LoginState === 2) {
// IP'
// this.$alert(this.$t('login:message:login4'), this.$t('common:title:warning'))
this.$message.warning(this.$t("login:message:login4"));
this.$message.warning(this.$t('login:message:login4'))
}
this.$store.dispatch("permission/generateRoutes").then((res) => {
this.loading = false;
this.$store.dispatch('permission/generateRoutes').then((res) => {
this.loading = false
if (res && res.length > 0) {
this.$store.dispatch("global/getNoticeList");
this.$router.addRoutes(res);
if (this.loginType === "DevOps") {
this.$router.replace({ path: res[0].path });
return;
this.$store.dispatch('global/getNoticeList')
this.$router.addRoutes(res)
if (this.loginType === 'DevOps') {
this.$router.replace({ path: res[0].path })
return
}
if (this.hasPermi(["role:radmin"])) {
this.$router.replace({ path: res[0].path });
return;
if (this.hasPermi(['role:radmin'])) {
this.$router.replace({ path: res[0].path })
return
}
if (
this.hasPermi(["role:air", "role:rpm", "role:rcrc", "role:rir"])
this.hasPermi(['role:air', 'role:rpm', 'role:rcrc', 'role:rir'])
) {
this.$router.replace({ path: "/trials/trials-list" });
this.$router.replace({ path: '/trials/trials-list' })
} else {
this.$router.replace({ path: "/trials" });
this.$router.replace({ path: '/trials' })
}
} else {
//
this.$message.warning(this.$t("login:message:login2"));
this.$message.warning(this.$t('login:message:login2'))
}
});
})
})
.catch(() => {
this.showCode = true;
this.loading = false;
});
this.showCode = true
this.loading = false
})
},
onSuccess() {
this.isShow = false;
this.loginIn();
this.isShow = false
this.loginIn()
},
handleResetPwd() {
this.$router.push({ name: "Resetpassword" });
this.$router.push({ name: 'Resetpassword' })
},
},
};
}
</script>
<style lang="scss">

View File

@ -2,52 +2,65 @@
<div v-loading="loading" class="img-container">
<el-card class="box-card left">
<div class="title">
{{$t('trials:none-dicom-show:fileList')}}
{{ $t('trials:none-dicom-show:fileList') }}
</div>
<div class="left-content">
<!-- 检查层级 -->
<div v-for="(study,i) in studyList" :key="study.CodeView">
<div v-for="(study, i) in studyList" :key="study.CodeView">
<div class="study-desc">
<span>{{ study.CodeView }}</span>
<span style="margin:0 5px">{{ study.Modality }}</span>
<span style="margin: 0 5px">{{ study.Modality }}</span>
<span>{{ getBodyPart(study.BodyPart) }}</span>
</div>
<!-- 文件层级 -->
<div v-if="study.NoneDicomStudyFileList.length === 0" class="empty-text">
<slot name="empty">暂无数据</slot>
<div
v-if="study.NoneDicomStudyFileList.length === 0"
class="empty-text"
>
<slot name="empty">{{ $t('trials:audit:message:noData') }}</slot>
</div>
<div v-else id="imgList" style="height:100%;overflow: hidden">
<div v-else id="imgList" style="height: 100%; overflow: hidden">
<div
v-for="(item,j) in study.NoneDicomStudyFileList"
v-for="(item, j) in study.NoneDicomStudyFileList"
:id="`img${item.Id}`"
:key="item.Id"
:class="{
'is-boxActive': item.Id === currentFileId
'is-boxActive': item.Id === currentFileId,
}"
class="img-box"
@click="selected(item,i,j,true)"
@click="selected(item, i, j, true)"
>
{{ `${j+1}. ${item.FileName}` }}
<div v-if="item.FileName.length < 15" class="img-text">
{{ `${j + 1}. ${item.FileName}` }}
</div>
<el-tooltip v-else :content="item.FileName" placement="bottom">
<div class="img-text">
{{ `${j + 1}. ${item.FileName}` }}
</div>
</el-tooltip>
</div>
</div>
</div>
</div>
</el-card>
<!-- 预览图像 -->
<el-card class="box-card right">
<div style="width:100%;height: 100%;">
<Preview v-if="previewImage.imgList.length > 0" ref="previewImage" style="width:100%;" :preview-image="previewImage" :value="currentStudyFileIndex" @selectedImg="selectedImg" />
<div style="width: 100%; height: 100%">
<Preview
v-if="previewImage.imgList.length > 0"
ref="previewImage"
style="width: 100%"
:preview-image="previewImage"
:value="currentStudyFileIndex"
@selectedImg="selectedImg"
/>
</div>
</el-card>
<!-- <el-card class="box-card" style="width:300px;height:100%;padding: 10px;margin-left:10px;">
<CheckForm />
</el-card> -->
</div>
</template>
<script>
@ -59,7 +72,7 @@ import Preview from './components/preview'
export default {
name: 'Notice',
components: {
Preview
Preview,
// CheckForm
},
data() {
@ -76,18 +89,18 @@ export default {
popup: true, //
studyCode: '',
modality: '',
bodyPart: ''
bodyPart: '',
},
previewVisible: false,
studyList: [],
subjectVisitId: '',
sudyId: '',
loading: false,
bp:[]
bp: [],
}
},
async created(){
this.bp = await this.$getBodyPart(this.$route.query.trialId);
async created() {
this.bp = await this.$getBodyPart(this.$route.query.trialId)
},
async mounted() {
if (this.$router.currentRoute.query.TokenKey) {
@ -112,31 +125,47 @@ export default {
separator = ''
}
var arr = bodyPart.split(separator)
var newArr = arr.map(i => {
return this.$fd('Bodypart', i.trim(),'Code',{Bodypart:this.bp},'Name')
var newArr = arr.map((i) => {
return this.$fd(
'Bodypart',
i.trim(),
'Code',
{ Bodypart: this.bp },
'Name'
)
})
console.log(newArr,this.bp)
console.log(newArr, this.bp)
return newArr.join(' | ')
},
// Dicom
getNoneDicomList() {
this.loading = true
getNoneDicomStudyList(this.subjectVisitId, this.studyId).then(res => {
this.studyList = res.Result
this.loading = false
const studyIndex = this.studyList.findIndex(item => {
return item.NoneDicomStudyFileList.length > 0
getNoneDicomStudyList(
this.subjectVisitId,
this.studyId,
false,
this.$route.query.visitTaskId
)
.then((res) => {
this.studyList = res.Result
this.loading = false
const studyIndex = this.studyList.findIndex((item) => {
return item.NoneDicomStudyFileList.length > 0
})
if (studyIndex > -1) {
var fileObj = this.studyList[studyIndex]['NoneDicomStudyFileList']
this.selected(fileObj[0], studyIndex, 0, true)
}
})
.catch(() => {
this.loading = false
})
if (studyIndex > -1) {
var fileObj = this.studyList[studyIndex]['NoneDicomStudyFileList']
this.selected(fileObj[0], studyIndex, 0, true)
}
}).catch(() => { this.loading = false })
},
selected(file, studyIndex, fileIndex, isChangeSub = false) {
this.currentFileId = file.Id
this.currentStudyIndex = studyIndex
this.previewImage.imgList = this.studyList[studyIndex].NoneDicomStudyFileList
this.previewImage.imgList =
this.studyList[studyIndex].NoneDicomStudyFileList
this.currentStudyFileIndex = fileIndex
this.previewImage.index = fileIndex
this.previewImage.studyCode = this.studyList[studyIndex].CodeView
@ -151,11 +180,17 @@ export default {
selectedImg(fileIndex) {
if (this.studyList.length > 0) {
this.currentStudyFileIndex = fileIndex
this.currentFileId = this.studyList[this.currentStudyIndex].NoneDicomStudyFileList[fileIndex].Id
this.currentFileId =
this.studyList[this.currentStudyIndex].NoneDicomStudyFileList[
fileIndex
].Id
this.previewImage.index = fileIndex
this.previewImage.studyCode = this.studyList[this.currentStudyIndex].CodeView
this.previewImage.bodyPart = this.studyList[this.currentStudyIndex].BodyPart
this.previewImage.modality = this.studyList[this.currentStudyIndex].Modality
this.previewImage.studyCode =
this.studyList[this.currentStudyIndex].CodeView
this.previewImage.bodyPart =
this.studyList[this.currentStudyIndex].BodyPart
this.previewImage.modality =
this.studyList[this.currentStudyIndex].Modality
this.$nextTick(() => {
const target = document.getElementById(`img${this.currentFileId}`)
const parent = document.getElementsByClassName('left-content')[0]
@ -165,14 +200,13 @@ export default {
},
preview() {
this.previewVisible = true
}
}
},
},
}
</script>
<style lang="scss" scoped>
.img-container{
.img-container {
position: relative;
width: 100%;
height: 100%;
@ -186,27 +220,27 @@ export default {
border-radius: 10px;
background: #d0d0d0;
}
/deep/ .el-card__body{
/deep/ .el-card__body {
padding: 0px;
}
}
.study-desc{
.study-desc {
padding: 15px 5px;
line-height: 20px;
background-color: #d5d5d5;
font-weight: 500;
}
.left{
width:220px;
.left {
width: 220px;
height: 100%;
/deep/ .el-card__body{
/deep/ .el-card__body {
height: 100%;
width: 100%;
display: flex;
flex-direction: column;
}
.title{
.title {
height: 40px;
line-height: 40px;
border: 1ppx solid;
@ -215,7 +249,7 @@ export default {
background-color: #4e4e4e;
color: #ffffff;
}
.left-content{
.left-content {
flex: 1;
overflow-y: auto;
}
@ -231,21 +265,26 @@ export default {
// overflow-y: auto;
// padding: 0;
// }
.img-box{
.img-box {
// position: relative;
display: inline-block;
box-sizing: border-box;
border-bottom: 2px solid #f3f3f3;
width: 180px;
height: 50px;
line-height: 50px;
cursor: pointer;
// margin-bottom: 5px;
padding-left: 5px;
text-overflow:ellipsis;
}
.img-text {
display: inline-block;
width: 200px;
height: 50px;
line-height: 50px;
overflow: hidden;
text-overflow: ellipsis; /* 用省略号表示溢出的文本 */
white-space: nowrap;
}
.img-box:nth-last-child(1){
.img-box:nth-last-child(1) {
margin-bottom: 0px;
}
.is-boxActive {
@ -256,14 +295,13 @@ export default {
opacity: 0;
}
}
.right{
.right {
flex: 1;
height: 100%;
margin-left: 10px;
/deep/ .el-card__body{
/deep/ .el-card__body {
height: 100%;
width: 100%;
}
}
</style>

View File

@ -1,76 +1,130 @@
<template>
<div style="display: flex;justify-content: center">
<div style="width: 600px;text-align: center;border: 1px solid #e6e6e6;margin-top:40px;padding:10px;">
<div class="trial-myinfo-head" style="font-size: 30px;line-height: 120px;">
<div style="display: flex; justify-content: center">
<div
style="
width: 600px;
text-align: center;
border: 1px solid #e6e6e6;
margin-top: 40px;
padding: 10px;
"
>
<div
class="trial-myinfo-head"
style="font-size: 30px; line-height: 120px"
>
<!-- 首次登录修改密码 -->
{{ $t('recompose:title:init') }}
</div>
<el-form ref="passwordForm" v-loading="loading" label-position="right" :model="password" :rules="passwordFormRules" label-width="120px">
<el-form
ref="passwordForm"
v-loading="loading"
label-position="right"
:model="password"
:rules="passwordFormRules"
:label-width="$i18n.locale === 'en' ? '180px' : '120px'"
>
<!-- 用户名 -->
<el-form-item :label="$t('recompose:form:userName')" prop="NewUserName">
<el-input v-model="password.NewUserName" />
</el-form-item>
<!-- 旧密码 -->
<el-form-item :label="$t('recompose:form:oldPassword')" prop="OldPassWord">
<el-input v-model="password.OldPassWord" type="password" show-password auto-complete="new-password" />
<el-form-item
:label="$t('recompose:form:oldPassword')"
prop="OldPassWord"
>
<el-input
v-model="password.OldPassWord"
type="password"
show-password
auto-complete="new-password"
/>
</el-form-item>
<!-- 新密码 -->
<el-form-item :label="$t('recompose:form:newPassword')" prop="NewPassWord">
<el-input v-model="password.NewPassWord" type="password" show-password auto-complete="new-password" />
<el-form-item
:label="$t('recompose:form:newPassword')"
prop="NewPassWord"
>
<el-input
v-model="password.NewPassWord"
type="password"
show-password
auto-complete="new-password"
/>
</el-form-item>
<!-- 确认密码 -->
<el-form-item :label="$t('recompose:form:confirmPassword')" prop="ConfirmPassWord">
<el-input v-model="password.ConfirmPassWord" type="password" show-password auto-complete="new-password" />
<el-form-item
:label="$t('recompose:form:confirmPassword')"
prop="ConfirmPassWord"
>
<el-input
v-model="password.ConfirmPassWord"
type="password"
show-password
auto-complete="new-password"
/>
</el-form-item>
<el-form-item style="text-align:right">
<el-form-item style="text-align: right">
<!-- 取消 -->
<el-button
size="small"
@click="cancel"
>
<el-button size="small" @click="cancel">
{{ $t('recompose:button:cancel') }}
</el-button>
<!-- 保存 -->
<el-button
type="primary"
size="small"
@click="save"
>
<el-button type="primary" size="small" @click="save">
{{ $t('recompose:button:save') }}
</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
import { modifyPassword } from '@/api/admin.js'
import md5 from 'js-md5'
import {mapMutations} from "vuex";
import { mapMutations } from 'vuex'
export default {
data() {
return {
password: {
NewUserName: null
NewUserName: null,
},
passwordFormRules: {
NewUserName: [{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }],
OldPassWord: [{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }],
NewUserName: [
{
required: true,
message: this.$t('common:ruleMessage:specify'),
trigger: 'blur',
},
],
OldPassWord: [
{
required: true,
message: this.$t('common:ruleMessage:specify'),
trigger: 'blur',
},
],
NewPassWord: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
{
required: true,
message: this.$t('common:ruleMessage:specify'),
trigger: 'blur',
},
{
required: true,
trigger: 'blur',
validator: this.$validatePassword
validator: this.$validatePassword,
},
],
ConfirmPassWord: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
]
{
required: true,
message: this.$t('common:ruleMessage:specify'),
trigger: 'blur',
},
],
},
userId: null,
loading: false
loading: false,
}
},
mounted() {
@ -78,7 +132,6 @@ export default {
if (!this.password.NewUserName) {
// 使
this.$alert(this.$t('recompose:message:warning'))
}
},
methods: {
@ -96,7 +149,7 @@ export default {
this.$updateDictionary()
},
save() {
this.$refs.passwordForm.validate(valid => {
this.$refs.passwordForm.validate((valid) => {
if (valid) {
if (this.password.NewPassWord !== this.password.ConfirmPassWord) {
//
@ -106,58 +159,64 @@ export default {
const param = {
NewUserName: this.password.NewUserName,
NewPassWord: md5(this.password.NewPassWord),
OldPassWord: md5(this.password.OldPassWord)
OldPassWord: md5(this.password.OldPassWord),
}
this.loading = true
modifyPassword(param).then(res => {
this.loading = false
if (res.IsSuccess) {
// ,
this.$message.success(this.$t('recompose:message:updatedSuccessfully'))
setTimeout(() => {
this.logout()
}, 500)
}
}).catch(() => { this.loading = false })
modifyPassword(param)
.then((res) => {
this.loading = false
if (res.IsSuccess) {
// ,
this.$message.success(
this.$t('recompose:message:updatedSuccessfully')
)
setTimeout(() => {
this.logout()
}, 500)
}
})
.catch(() => {
this.loading = false
})
}
})
},
cancel() {
this.$refs['passwordForm'].resetFields()
}
}
},
},
}
</script>
<style>
.reset-wrapper {
padding: 20px;
}
.reset-wrapper .el-page-header {
line-height: 50px;
border: 1px solid #ebeef5;
border-radius: 4px;
background-color: #fff;
}
.reset-wrapper .box-wrapper {
width: 60%;
margin: 20px auto;
padding: 10px;
color: #303133;
}
.reset-wrapper {
padding: 20px;
}
.reset-wrapper .el-page-header {
line-height: 50px;
border: 1px solid #ebeef5;
border-radius: 4px;
background-color: #fff;
}
.reset-wrapper .box-wrapper {
width: 60%;
margin: 20px auto;
padding: 10px;
color: #303133;
}
</style>
<style scoped>
::v-deep .is-error{
::v-deep .is-error {
margin-bottom: 40px;
}
input:-webkit-autofill {
-webkit-text-fill-color: #ededed !important;
box-shadow: 0 0 0px 1000px transparent inset !important;
background-color:transparent;
box-shadow: 0 0 0px 1000px transparent inset !important;
background-color: transparent;
background-image: none;
transition: background-color 50000s ease-in-out 0s;
}
input {
background-color:transparent;
background-color: transparent;
caret-color: #fff;
}
</style>

View File

@ -38,7 +38,7 @@
<div class="login_content">
<el-form-item
:label="$t('trials:researchForm:form:siteName')"
prop="SiteId"
prop="TrialSiteId"
>
<el-select
v-model="form.TrialSiteId"

View File

@ -34,7 +34,7 @@
<el-input v-model="form.IndicationType" disabled />
</el-form-item>
<!-- 中心名称 -->
<el-form-item :label="$t('trials:researchForm:form:siteName')" prop="SiteId">
<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"

View File

@ -147,7 +147,7 @@
>
{{
$fd(
"InternationalizationType",
'InternationalizationType',
scope.row.InternationalizationType
)
}}
@ -158,7 +158,7 @@
>
{{
$fd(
"InternationalizationType",
'InternationalizationType',
scope.row.InternationalizationType
)
}}
@ -166,7 +166,7 @@
<el-tag v-else>
{{
$fd(
"InternationalizationType",
'InternationalizationType',
scope.row.InternationalizationType
)
}}
@ -176,7 +176,7 @@
<el-table-column
label="功能模块/服务名"
prop="Module"
min-width="120"
min-width="140"
show-overflow-tooltip
sortable="custom"
/>
@ -210,13 +210,13 @@
>
<template slot-scope="scope">
<el-tag v-if="scope.row.State === 0" type="danger">
{{ $fd("InternationalizationKeyState", scope.row.State) }}
{{ $fd('InternationalizationKeyState', scope.row.State) }}
</el-tag>
<el-tag v-else-if="scope.row.State === 1" type="primary">
{{ $fd("InternationalizationKeyState", scope.row.State) }}
{{ $fd('InternationalizationKeyState', scope.row.State) }}
</el-tag>
<el-tag v-else>
{{ $fd("InternationalizationKeyState", scope.row.State) }}
{{ $fd('InternationalizationKeyState', scope.row.State) }}
</el-tag>
</template>
</el-table-column>
@ -241,7 +241,13 @@
show-overflow-tooltip
sortable="custom"
/>
<el-table-column
label="创建时间"
prop="CreateTime"
width="150"
show-overflow-tooltip
sortable="custom"
/>
<el-table-column
label="操作"
fixed="right"
@ -289,11 +295,11 @@ import {
deleteInternationalization,
getPublishVersionSelect,
batchUpdateInternationalInfo,
} from "@/api/admin";
import Pagination from "@/components/Pagination";
import I18nForm from "./components/I18nForm.vue";
import BatchAddForm from "./components/BatchAddForm.vue";
import moment from "moment";
} from '@/api/admin'
import Pagination from '@/components/Pagination'
import I18nForm from './components/I18nForm.vue'
import BatchAddForm from './components/BatchAddForm.vue'
import moment from 'moment'
const searchDataDefault = () => {
return {
Description: null,
@ -304,13 +310,13 @@ const searchDataDefault = () => {
Module: null,
PublishLogId: null,
Asc: true,
SortField: "",
SortField: '',
PageIndex: 1,
PageSize: 20,
};
};
}
}
export default {
name: "I18n",
name: 'I18n',
components: { Pagination, I18nForm, BatchAddForm },
data() {
return {
@ -321,140 +327,140 @@ export default {
loading: false,
PublishVersionList: [],
selectTableList: [],
};
}
},
mounted() {
this.getList();
this.getPublishVersionSelect();
this.getList()
this.getPublishVersionSelect()
},
methods: {
//
async getPublishVersionSelect() {
try {
let res = await getPublishVersionSelect();
let res = await getPublishVersionSelect()
if (res.IsSuccess) {
this.PublishVersionList = res.Result;
this.PublishVersionList = res.Result
}
} catch (err) {
console.log(err);
console.log(err)
}
},
getList() {
this.loading = true;
this.loading = true
getInternationalizationList(this.searchData)
.then((res) => {
this.loading = false;
this.list = res.Result.CurrentPageData;
this.total = res.Result.TotalCount;
this.loading = false
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
})
.catch(() => {
this.loading = false;
});
this.loading = false
})
},
//
handleBatchUpdate() {
this.$nextTick(() => {
this.$refs["i18nForm"].openDialog(
"批量更新",
this.$refs['i18nForm'].openDialog(
'批量更新',
{},
this.PublishVersionList,
"batch"
);
});
'batch'
)
})
},
async batch(row) {
let { PublishLogId, State } = row;
let { PublishLogId, State } = row
let data = {
PublishLogId,
State,
IdList: this.selectTableList.map((item) => item.Id),
};
}
try {
let res = await batchUpdateInternationalInfo(data);
let res = await batchUpdateInternationalInfo(data)
if (res.IsSuccess) {
this.$message.success(this.$t("common:message:updatedSuccessfully"));
this.getList();
this.$message.success(this.$t('common:message:updatedSuccessfully'))
this.getList()
this.$nextTick(() => {
this.$refs["i18nForm"].handleCancle();
});
this.$refs['i18nForm'].handleCancle()
})
}
} catch (err) {
console.log(err);
console.log(err)
}
},
handleAdd() {
this.$nextTick(() => {
this.$refs["i18nForm"].openDialog(
"新增",
this.$refs['i18nForm'].openDialog(
'新增',
{},
this.PublishVersionList,
"add"
);
});
'add'
)
})
},
handleBatchAdd() {
this.$nextTick(() => {
this.$refs["batcnAddForm"].openDialog(
"批量新增",
this.$refs['batcnAddForm'].openDialog(
'批量新增',
{},
this.PublishVersionList
);
});
)
})
},
handleEdit(row) {
this.$nextTick(() => {
this.$refs["i18nForm"].openDialog(
"编辑",
this.$refs['i18nForm'].openDialog(
'编辑',
row,
this.PublishVersionList,
"update"
);
});
'update'
)
})
},
//
handleReset() {
this.searchData = searchDataDefault();
this.getList();
this.searchData = searchDataDefault()
this.getList()
},
//
handleDelete(row) {
this.$confirm("是否确认删除?", {
type: "warning",
this.$confirm('是否确认删除?', {
type: 'warning',
distinguishCancelAndClose: true,
}).then(() => {
this.loading = true;
this.loading = true
deleteInternationalization(row.Id)
.then((res) => {
this.loading = false;
this.loading = false
if (res.IsSuccess) {
this.getList();
this.getList()
this.$message.success(
this.$t("common:message:deletedSuccessfully")
);
this.$t('common:message:deletedSuccessfully')
)
}
})
.catch(() => {
this.loading = false;
});
});
this.loading = false
})
})
},
//
handleSortByColumn(column) {
if (column.order === "ascending") {
this.searchData.Asc = true;
if (column.order === 'ascending') {
this.searchData.Asc = true
} else {
this.searchData.Asc = false;
this.searchData.Asc = false
}
this.searchData.SortField = column.prop;
this.searchData.PageIndex = 1;
this.getList();
this.searchData.SortField = column.prop
this.searchData.PageIndex = 1
this.getList()
},
//
handleSelectionChange(val) {
this.selectTableList = val;
this.selectTableList = val
},
},
};
}
</script>
<style lang="scss">
.log {

View File

@ -9,10 +9,16 @@
label-width="120px"
size="small"
>
<el-form-item label="发布版本" prop="Version">
<el-form-item :label="$t('system:log:form:version')" prop="Version">
<el-input v-model="form.Version" />
</el-form-item>
<el-form-item label="更新功能" prop="UpdateContent">
<el-form-item :label="$t('system:log:form:versionUS')" prop="Version">
<el-input v-model="form.Version_US" />
</el-form-item>
<el-form-item
:label="$t('system:log:form:updateContent')"
prop="UpdateContent"
>
<el-input
v-model="form.UpdateContent"
type="textarea"
@ -20,8 +26,8 @@
:autosize="{ minRows: 4, maxRows: 5 }"
/>
</el-form-item>
<el-form-item label="发布状态" prop="State">
<el-select v-model="form.State" placeholder="请选择">
<el-form-item :label="$t('system:log:form:State')" prop="State">
<el-select v-model="form.State" placeholder="">
<el-option
v-for="item in $d.PublishStatus"
:key="item.id"
@ -31,15 +37,18 @@
</el-option>
</el-select>
</el-form-item>
<el-form-item label="发布日期">
<el-form-item :label="$t('system:log:form:PublishTime')">
<el-date-picker
v-model="form.PublishTime"
type="date"
value-format="yyyy-MM-dd HH:mm:ss"
/>
</el-form-item>
<el-form-item label="是否当前版本" prop="IsCurrentVersion">
<el-select v-model="form.IsCurrentVersion" placeholder="请选择">
<el-form-item
:label="$t('system:log:form:IsCurrentVersion')"
prop="IsCurrentVersion"
>
<el-select v-model="form.IsCurrentVersion" placeholder="">
<el-option
v-for="item in $d.YesOrNo"
:key="item.id"
@ -52,30 +61,31 @@
</el-form>
</template>
<template slot="dialog-footer">
<el-button size="small" type="primary" @click="handleCancle"
>取消</el-button
>
<el-button size="small" type="primary" @click="handleSave"
>保存</el-button
>
<el-button size="small" type="primary" @click="handleCancle">
{{ $t('common:button:cancel') }}
</el-button>
<el-button size="small" type="primary" @click="handleSave">
{{ $t('common:button:save') }}
</el-button>
</template>
</base-model>
</template>
<script>
import { addOrUpdatePublishLog } from "@/api/admin";
import BaseModel from "@/components/BaseModel";
import { addOrUpdatePublishLog } from '@/api/admin'
import BaseModel from '@/components/BaseModel'
const formDataDefault = () => {
return {
Id: null,
Version: null,
Version_US: null,
PublishTime: null,
UpdateContent: null,
State: null,
IsCurrentVersion: null,
};
};
}
}
export default {
name: "LogForm",
name: 'LogForm',
components: { BaseModel },
data() {
return {
@ -83,69 +93,87 @@ export default {
form: formDataDefault(),
rules: {
Version: [
{ required: true, message: "请注明", trigger: "blur" },
{ max: 50, message: `${this.$t("common:ruleMessage:maxLength")} 50` },
{
required: true,
message: this.$t('common:ruleMessage:specify'),
trigger: 'blur',
},
{ max: 50, message: `${this.$t('common:ruleMessage:maxLength')} 50` },
],
// PublishTime: [
// { required: true, message: '', trigger: 'blur' }
// ],
IsCurrentVersion: [
{ required: true, message: "请选择", trigger: "blur" },
{
required: true,
message: this.$t('common:ruleMessage:select'),
trigger: 'blur',
},
],
State: [
{
required: true,
message: this.$t('common:ruleMessage:select'),
trigger: 'blur',
},
],
State: [{ required: true, message: "请选择", trigger: "blur" }],
UpdateContent: [
{ required: true, message: "请注明", trigger: "blur" },
{
required: true,
message: this.$t('common:ruleMessage:specify'),
trigger: 'blur',
},
{
max: 1000,
message: `${this.$t("common:ruleMessage:maxLength")} 500`,
message: `${this.$t('common:ruleMessage:maxLength')} 500`,
},
],
},
model_cfg: {
visible: false,
showClose: true,
width: "500px",
title: "",
width: '500px',
title: '',
appendToBody: true,
},
};
}
},
mounted() {},
methods: {
openDialog(title, data) {
this.model_cfg.visible = true;
this.model_cfg.title = title;
this.model_cfg.visible = true
this.model_cfg.title = title
if (Object.keys(data).length > 0) {
for (const k in this.form) {
if (data.hasOwnProperty(k)) {
this.form[k] = data[k];
this.form[k] = data[k]
}
}
} else {
this.form = formDataDefault();
this.form = formDataDefault()
}
},
handleSave() {
this.$refs.LogForm.validate((valid) => {
if (!valid) return;
this.loading = true;
if (!valid) return
this.loading = true
addOrUpdatePublishLog(this.form)
.then((res) => {
this.loading = false;
this.$message.success(this.$t("common:message:savedSuccessfully"));
this.model_cfg.visible = false;
this.$emit("getList");
this.loading = false
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.model_cfg.visible = false
this.$emit('getList')
})
.catch(() => {
this.loading = false;
});
});
this.loading = false
})
})
},
handleCancle() {
this.model_cfg.visible = false;
this.model_cfg.visible = false
},
},
};
}
</script>
<style lang="scss" scoped>
::v-deep .el-select,

View File

@ -2,9 +2,12 @@
<div class="log">
<div ref="leftContainer" class="left">
<el-form :inline="true">
<el-form-item label="发布版本" prop="Version">
<el-form-item :label="$t('system:log:table:version')" prop="Version">
<el-input v-model="searchData.Version" size="small" clearable />
</el-form-item>
<el-form-item :label="$t('system:log:table:versionUS')" prop="Version">
<el-input v-model="searchData.Version_US" size="small" clearable />
</el-form-item>
<el-form-item label="更新功能" prop="UpdateContent">
<el-input v-model="searchData.UpdateContent" size="small" clearable />
</el-form-item>
@ -38,30 +41,37 @@
>
<el-table-column type="index" width="50" />
<el-table-column
label="发布版本"
:label="$t('system:log:table:version')"
prop="Version"
min-width="90"
show-overflow-tooltip
sortable="custom"
/>
<el-table-column
label="更新功能"
:label="$t('system:log:table:versionUS')"
prop="Version_US"
min-width="90"
show-overflow-tooltip
sortable="custom"
/>
<el-table-column
:label="$t('system:log:table:UpdateContent')"
prop="UpdateContent"
min-width="150"
show-overflow-tooltip
/>
<el-table-column
label="发布状态"
:label="$t('system:log:table:State')"
prop="State"
min-width="150"
show-overflow-tooltip
>
<template slot-scope="scope">
<span>{{ $fd("PublishStatus", scope.row.State) }}</span>
<span>{{ $fd('PublishStatus', scope.row.State) }}</span>
</template>
</el-table-column>
<el-table-column
label="发布日期"
:label="$t('system:log:table:PublishTime')"
prop="PublishTime"
min-width="90"
show-overflow-tooltip
@ -70,23 +80,23 @@
<template slot-scope="scope">
{{
scope.row.PublishTime
? moment(scope.row.PublishTime).format("YYYY-MM-DD")
: ""
? moment(scope.row.PublishTime).format('YYYY-MM-DD')
: ''
}}
</template>
</el-table-column>
<el-table-column
label="是否当前版本"
:label="$t('system:log:table:IsCurrentVersion')"
prop="IsCurrentVersion"
min-width="150"
show-overflow-tooltip
>
<template slot-scope="scope">
<span>{{ $fd("YesOrNo", scope.row.IsCurrentVersion) }}</span>
<span>{{ $fd('YesOrNo', scope.row.IsCurrentVersion) }}</span>
</template>
</el-table-column>
<el-table-column
label="操作"
:label="$t('common:action:action')"
fixed="right"
prop=""
min-width="200"
@ -99,7 +109,7 @@
icon="el-icon-edit-outline"
@click="handleEdit(scope.row)"
>
编辑
{{ $t('common:button:edit') }}
</el-button>
<el-button
size="mini"
@ -107,7 +117,7 @@
icon="el-icon-delete"
@click="handleDelete(scope.row)"
>
删除
{{ $t('common:button:delete') }}
</el-button>
</template>
</el-table-column>
@ -125,20 +135,21 @@
</div>
</template>
<script>
import { getPublishLogList, deletePublishLog } from "@/api/admin";
import Pagination from "@/components/Pagination";
import LogForm from "./components/LogForm.vue";
import moment from "moment";
import { getPublishLogList, deletePublishLog } from '@/api/admin'
import Pagination from '@/components/Pagination'
import LogForm from './components/LogForm.vue'
import moment from 'moment'
const searchDataDefault = () => {
return {
Version: null,
Version_US: null,
UpdateContent: null,
Asc: false,
SortField: "PublishTime",
SortField: 'PublishTime',
PageIndex: 1,
PageSize: 20,
};
};
}
}
export default {
components: { Pagination, LogForm },
data() {
@ -148,74 +159,74 @@ export default {
list: [],
total: 0,
loading: false,
};
}
},
mounted() {
this.getList();
this.getList()
},
methods: {
getList() {
this.loading = true;
this.loading = true
getPublishLogList(this.searchData)
.then((res) => {
this.loading = false;
this.list = res.Result.CurrentPageData;
this.total = res.Result.TotalCount;
this.loading = false
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
})
.catch(() => {
this.loading = false;
});
this.loading = false
})
},
handleAdd() {
this.$nextTick(() => {
this.$refs["LogForm"].openDialog("新增", {});
});
this.$refs['LogForm'].openDialog('新增', {})
})
},
handleEdit(row) {
this.$nextTick(() => {
this.$refs["LogForm"].openDialog("编辑", row);
});
this.$refs['LogForm'].openDialog('编辑', row)
})
},
//
handleReset() {
this.searchData = searchDataDefault();
this.getList();
this.searchData = searchDataDefault()
this.getList()
},
//
handleDelete(row) {
this.$confirm("是否确认删除?", {
type: "warning",
this.$confirm('是否确认删除?', {
type: 'warning',
distinguishCancelAndClose: true,
}).then(() => {
this.loading = true;
this.loading = true
deletePublishLog(row.Id)
.then((res) => {
this.loading = false;
this.loading = false
if (res.IsSuccess) {
this.getList();
this.getList()
this.$message.success(
this.$t("common:message:deletedSuccessfully")
);
this.$t('common:message:deletedSuccessfully')
)
}
})
.catch(() => {
this.loading = false;
});
});
this.loading = false
})
})
},
//
handleSortByColumn(column) {
if (column.order === "ascending") {
this.searchData.Asc = true;
if (column.order === 'ascending') {
this.searchData.Asc = true
} else {
this.searchData.Asc = false;
this.searchData.Asc = false
}
this.searchData.SortField = column.prop;
this.searchData.PageIndex = 1;
this.getList();
this.searchData.SortField = column.prop
this.searchData.PageIndex = 1
this.getList()
},
},
};
}
</script>
<style lang="scss">
.log {

View File

@ -41,8 +41,8 @@
</el-form-item>
<!-- 阅片标准 -->
<el-form-item
:label="$t('trials:trials-list:table:IR_ReadingCriterionList')"
v-if="hasPermi(['role:ir'])"
:label="$t('trials:trials-list:table:IR_ReadingCriterionList')"
>
<el-select
v-model="searchData.CriterionType"
@ -59,8 +59,8 @@
</el-form-item>
<!-- 联系人 -->
<el-form-item
:label="$t('trials:trials-list:table:IR_PMEmailList')"
v-if="hasPermi(['role:ir'])"
:label="$t('trials:trials-list:table:IR_PMEmailList')"
>
<el-input
v-model="searchData.PM_EMail"
@ -293,9 +293,10 @@
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSelectSearch"
>Search</el-button
>
<el-button
type="primary"
@click="handleSelectSearch"
>Search</el-button>
<el-button type="primary" @click="handleReset">Reset</el-button>
<el-button type="primary" @click="isShow = false">Back</el-button>
</el-form-item>
@ -357,23 +358,19 @@
<el-tag
v-if="scope.row.TrialStatusStr === 'Initializing'"
type="info"
>{{ $fd("TrialStatusEnum", scope.row.TrialStatusStr) }}</el-tag
>
>{{ $fd("TrialStatusEnum", scope.row.TrialStatusStr) }}</el-tag>
<el-tag
v-if="scope.row.TrialStatusStr === 'Ongoing'"
type="primary"
>{{ $fd("TrialStatusEnum", scope.row.TrialStatusStr) }}</el-tag
>
>{{ $fd("TrialStatusEnum", scope.row.TrialStatusStr) }}</el-tag>
<el-tag
v-if="scope.row.TrialStatusStr === 'Completed'"
type="warning"
>{{ $fd("TrialStatusEnum", scope.row.TrialStatusStr) }}</el-tag
>
>{{ $fd("TrialStatusEnum", scope.row.TrialStatusStr) }}</el-tag>
<el-tag
v-if="scope.row.TrialStatusStr === 'Stopped'"
type="danger"
>{{ $fd("TrialStatusEnum", scope.row.TrialStatusStr) }}</el-tag
>
>{{ $fd("TrialStatusEnum", scope.row.TrialStatusStr) }}</el-tag>
</template>
</el-table-column>
<el-table-column
@ -582,7 +579,7 @@
:disabled="
(scope.row.TrialStatusStr === 'Initializing' &&
!hasPermi(['role:pm'])) ||
scope.row.IsDeleted
scope.row.IsDeleted || ((scope.row.TrialStatusStr === 'Completed' || scope.row.TrialStatusStr === 'Stopped') && !(hasPermi(['role:qa']) || hasPermi(['role:ea']) || hasPermi(['role:pm'])))
"
:title="$t('trials:trials-list:action:panel')"
@click.stop="handleDetail(scope.row)"
@ -612,7 +609,7 @@
icon="el-icon-delete"
:disabled="
scope.row.IsDeleted ||
scope.row.TrialStatusStr !== 'Initializing'
scope.row.TrialStatusStr !== 'Initializing'
"
:title="$t('trials:trials-list:action:abolition')"
@click.stop="handleAbandon(scope.row)"
@ -693,54 +690,54 @@
import {
abandonTrial,
ifTrialCanOngoing,
getTrialToBeDoneList,
} from "@/api/trials";
import { getTrialList_Export } from "@/api/export";
import store from "@/store";
import { mapGetters } from "vuex";
import BaseContainer from "@/components/BaseContainer";
import Pagination from "@/components/Pagination";
import TrialForm from "./components/TrialForm";
import TrialStatusForm from "./components/TrialStatusForm";
import DoneList from "./components/DoneList";
getTrialToBeDoneList
} from '@/api/trials'
import { getTrialList_Export } from '@/api/export'
import store from '@/store'
import { mapGetters } from 'vuex'
import BaseContainer from '@/components/BaseContainer'
import Pagination from '@/components/Pagination'
import TrialForm from './components/TrialForm'
import TrialStatusForm from './components/TrialStatusForm'
import DoneList from './components/DoneList'
const searchDataDefault = () => {
return {
Code: "",
Code: '',
CriterionIds: [],
SponsorId: "",
SponsorId: '',
ReviewTypeIds: [],
CROId: "",
Expedited: "",
Indication: "",
Phase: "",
CROId: '',
Expedited: '',
Indication: '',
Phase: '',
ModalityIds: [],
BeginDate: "",
EndDate: "",
AttendedReviewerType: "",
ResearchProgramNo: "",
ExperimentName: "",
BeginDate: '',
EndDate: '',
AttendedReviewerType: '',
ResearchProgramNo: '',
ExperimentName: '',
PageIndex: 1,
PageSize: 20,
Asc: false,
SortField: "",
SortField: '',
CriterionType: null,
PM_EMail: null,
};
};
PM_EMail: null
}
}
export default {
name: "Trials",
name: 'Trials',
components: {
Pagination,
BaseContainer,
TrialForm,
TrialStatusForm,
DoneList,
DoneList
},
dicts: ["ReadingStandard", "ReviewType", "ReadingType"],
dicts: ['ReadingStandard', 'ReviewType', 'ReadingType'],
data() {
return {
exportLoading: false,
userTypeEnumInt: zzSessionStorage.getItem("userTypeEnumInt") * 1,
userTypeEnumInt: zzSessionStorage.getItem('userTypeEnumInt') * 1,
doneDialogVisible: false,
doneTitle: null,
selectArr: [],
@ -750,26 +747,26 @@ export default {
total: 0,
isShow: false,
dialogVisible: false,
title: "",
currentId: "",
title: '',
currentId: '',
statusVisible: false,
currentRow: {},
currentUser: zzSessionStorage.getItem("userName"),
currentUser: zzSessionStorage.getItem('userName'),
phaseOptions: [
{ value: "I" },
{ value: "II" },
{ value: "III" },
{ value: "IV" },
{ value: 'I' },
{ value: 'II' },
{ value: 'III' },
{ value: 'IV' }
],
expeditedOption: this.$d.TrialExpeditedState,
beginPickerOption: {
disabledDate: (time) => {
if (this.searchData.EndDate) {
return time.getTime() > new Date(this.searchData.EndDate).getTime();
return time.getTime() > new Date(this.searchData.EndDate).getTime()
} else {
return time.getTime() > Date.now();
return time.getTime() > Date.now()
}
},
}
},
endpickerOption: {
disabledDate: (time) => {
@ -777,306 +774,189 @@ export default {
return (
time.getTime() > Date.now() ||
time.getTime() <= new Date(this.searchData.BeginDate).getTime()
);
)
} else {
return time.getTime() > Date.now();
return time.getTime() > Date.now()
}
},
},
};
}
}
}
},
computed: {
...mapGetters(["sponsorList", "croList"]),
...mapGetters(['sponsorList', 'croList'])
},
created() {
this.initPage();
this.initPage()
},
methods: {
initPage() {
this.getList();
store.dispatch("global/getSponsorList");
store.dispatch("global/getCROList");
this.getList()
store.dispatch('global/getSponsorList')
store.dispatch('global/getCROList')
},
//
getList() {
this.listLoading = true;
this.listLoading = true
getTrialToBeDoneList(this.searchData)
.then((res) => {
this.list = res.Result.CurrentPageData;
this.total = res.Result.TotalCount;
this.listLoading = false;
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
this.listLoading = false
})
.catch(() => {
this.listLoading = false;
});
this.listLoading = false
})
},
//
handleSearch() {
this.searchData.PageIndex = 1;
this.getList();
this.searchData.PageIndex = 1
this.getList()
},
//
handleSelectSearch() {
this.searchData.PageIndex = 1;
this.getList();
this.isShow = false;
this.searchData.PageIndex = 1
this.getList()
this.isShow = false
},
//
handleReset() {
this.searchData = searchDataDefault();
this.getList();
this.searchData = searchDataDefault()
this.getList()
},
//
handleNew() {
// this.$router.push({ name: 'CreateTrial' })
this.title = this.$t("trials:trials-list:dialogTitle:new");
this.currentId = "";
this.dialogVisible = true;
this.title = this.$t('trials:trials-list:dialogTitle:new')
this.currentId = ''
this.dialogVisible = true
},
//
handleEdit(row) {
this.title = this.$t("trials:trials-list:dialogTitle:edit");
this.currentId = row.Id;
this.dialogVisible = true;
this.title = this.$t('trials:trials-list:dialogTitle:edit')
this.currentId = row.Id
this.dialogVisible = true
},
handleCommission(row) {
this.doneTitle = this.$t("trials:trials-list:dialogTitle:doneTitle");
this.currentId = row.Id;
this.doneDialogVisible = true;
this.doneTitle = this.$t('trials:trials-list:dialogTitle:doneTitle')
this.currentId = row.Id
this.doneDialogVisible = true
},
closeDialog() {
this.dialogVisible = false;
this.dialogVisible = false
},
//
handleStatus(row) {
if (row.TrialStatusStr === "Initializing") {
this.listLoading = true;
if (row.TrialStatusStr === 'Initializing') {
this.listLoading = true
ifTrialCanOngoing(row.Id)
.then((res) => {
this.listLoading = false;
this.listLoading = false
if (res.Result) {
this.currentRow = { ...row };
this.statusVisible = true;
this.currentRow = { ...row }
this.statusVisible = true
} else {
this.$confirm(res.ErrorMessage, {
type: "warning",
type: 'warning',
showCancelButton: false,
callback: (action) => {},
});
callback: (action) => {}
})
}
})
.catch(() => {
this.listLoading = false;
});
this.listLoading = false
})
} else {
this.currentRow = { ...row };
this.statusVisible = true;
this.currentRow = { ...row }
this.statusVisible = true
}
},
closeStatusDialog() {
this.statusVisible = false;
this.statusVisible = false
},
//
handleAbandon(row) {
this.$confirm(this.$t("trials:trials-list:message:abolition"), {
type: "warning",
distinguishCancelAndClose: true,
this.$confirm(this.$t('trials:trials-list:message:abolition'), {
type: 'warning',
distinguishCancelAndClose: true
})
.then(() => {
this.currentRow = { ...row };
this.abandonTrial();
this.currentRow = { ...row }
this.abandonTrial()
})
.catch(() => {});
.catch(() => {})
},
//
abandonTrial() {
this.listLoading = true;
this.listLoading = true
abandonTrial(this.currentRow.Id, true)
.then((res) => {
this.listLoading = false;
this.listLoading = false
if (res.IsSuccess) {
this.getList();
this.getList()
this.$message.success(
this.$t("trials:trials-list:message:abolitionSuccessfully")
);
this.$t('trials:trials-list:message:abolitionSuccessfully')
)
}
})
.catch(() => {
this.listLoading = false;
});
this.listLoading = false
})
},
rowClick(row, col) {
if (
(row.TrialStatusStr === "Initializing" &&
!this.hasPermi(["role:pm"])) ||
row.IsDeleted
)
return;
if ((row.TrialStatusStr === 'Initializing' && !this.hasPermi(['role:pm'])) || row.IsDeleted) {
return
} else if ((row.TrialStatusStr === 'Completed' || row.TrialStatusStr === 'Stopped') && !(this.hasPermi(['role:qa']) || this.hasPermi(['role:ea']) || this.hasPermi(['role:pm']))) {
return
}
this.$router.push({
path: `/trials/trials-panel?trialId=${row.Id}&trialCode=${row.TrialCode}&researchProgramNo=${row.ResearchProgramNo}`,
});
path: `/trials/trials-panel?trialId=${row.Id}&trialCode=${row.TrialCode}&researchProgramNo=${row.ResearchProgramNo}`
})
},
// panel
handleDetail(row) {
this.$router.push({
path: `/trials/trials-panel?trialId=${row.Id}&trialCode=${row.TrialCode}&researchProgramNo=${row.ResearchProgramNo}`,
});
path: `/trials/trials-panel?trialId=${row.Id}&trialCode=${row.TrialCode}&researchProgramNo=${row.ResearchProgramNo}`
})
},
//
handleSelectChange(val) {
const arr = [];
const arr = []
for (let index = 0; index < val.length; index++) {
arr.push(val[index]);
arr.push(val[index])
}
this.selectArr = arr;
this.selectArr = arr
},
//
handleSortChange(column) {
if (column.order === "ascending") {
this.searchData.Asc = true;
if (column.order === 'ascending') {
this.searchData.Asc = true
} else {
this.searchData.Asc = false;
this.searchData.Asc = false
}
if (column.prop === "Criterion") {
this.searchData.SortField = "CriterionId";
if (column.prop === 'Criterion') {
this.searchData.SortField = 'CriterionId'
} else {
this.searchData.SortField = column.prop;
this.searchData.SortField = column.prop
}
this.searchData.PageIndex = 1;
this.getList();
this.searchData.PageIndex = 1
this.getList()
},
// Excel
handleExportTrial() {
this.exportLoading = true;
let data = {
...this.searchData,
};
data.TrialIdList = this.selectArr.map((item) => item.Id);
this.exportLoading = true
const data = {
...this.searchData
}
data.TrialIdList = this.selectArr.map((item) => item.Id)
return getTrialList_Export(data)
.then((res) => {
this.exportLoading = false;
this.exportLoading = false
})
.catch(() => {
this.exportLoading = false;
});
this.selectArr.forEach((element, index) => {
// element.ExpeditedStr = element.Expedited === 0 ? 'No' : element.Expedited === 1 ? '24H' : '48H'
// element.ModalityListStr = element.ModalityList.join(', ')
// element.CreateTimeStr = element.CreateTime
// element.Criterion = element.CriterionList.join(', ')
element.Deleted = element.IsDeleted ? "Yes" : "No";
element.Index = index + 1;
});
var workbook = new Excel.Workbook();
var sheet = workbook.addWorksheet("Trials");
sheet.properties.defaultRowHeight = 22;
// sheet.columns = [
// { key: 'Index', width: 5 },
// { key: 'Code', width: 15 },
// { key: 'ExpeditedStr', width: 13 },
// { key: 'TrialStatusStr', width: 10 },
// { key: 'Indication', width: 25 },
// { key: 'Phase', width: 10 },
// { key: 'ReviewType', width: 20 },
// { key: 'Criterion', width: 15 },
// { key: 'ModalityListStr', width: 30 },
// { key: 'CRO', width: 10 },
// { key: 'Sponsor', width: 20 },
// { key: 'CreateTimeStr', width: 18 }
// ]
sheet.columns = [
{ key: "Index", width: 5 },
{ key: "TrialCode", width: 25 },
{ key: "ExperimentName", width: 25 },
{ key: "ResearchProgramNo", width: 25 },
{ key: "Sponsor", width: 25 },
{ key: "Deleted", width: 10 },
{ key: "CreateTime", width: 25 },
];
//
sheet.mergeCells("A1", "G2");
sheet.getCell("A1").value = "Trials";
sheet.getCell("A1").alignment = {
vertical: "middle",
horizontal: "center",
};
sheet.getCell("A1").font = {
name: "SimSun",
family: 4,
size: 13,
bold: true,
};
sheet.mergeCells("A3", "G3");
var now = new Date();
sheet.getCell("A3").value = now.toLocaleDateString();
sheet.getCell("A3").alignment = {
vertical: "middle",
horizontal: "right",
};
sheet.getRow(4).values = [
"NO.",
"Trial ID",
"试验名称",
"研究方案号",
"申办方",
"是否废除",
"Date Created",
];
sheet.getRow(4).font = {
name: "SimSun",
family: 4,
size: 11,
bold: true,
};
sheet.getRow(4).alignment = { vertical: "middle", horizontal: "left" };
sheet.addRows(this.selectArr);
sheet.eachRow((row, number) => {
if (number > 3) {
row.eachCell((cell, rowNumber) => {
cell.alignment = { vertical: "center", horizontal: "left" };
cell.border = {
top: { style: "thin" },
left: { style: "thin" },
bottom: { style: "thin" },
right: { style: "thin" },
};
});
}
});
workbook.xlsx
.writeBuffer({
base64: true,
this.exportLoading = false
})
.then(function (xls64) {
var data = new Blob([xls64], {
type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
});
if ("msSaveOrOpenBlob" in navigator) {
// ie使
window.navigator.msSaveOrOpenBlob(data, "Trials" + ".xlsx");
} else {
var a = document.createElement("a");
var url = URL.createObjectURL(data);
a.href = url;
a.download = "Trials" + ".xlsx";
document.body.appendChild(a);
a.click();
setTimeout(function () {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 0);
}
});
},
},
};
}
}
}
</script>

View File

@ -3,14 +3,19 @@
<div class="trial-myinfo-right-box">
<div class="trial-myinfo-head">
<!-- 账号信息 -->
{{ $t("trials:trials-myinfo:title:accountInfo") }}
{{ $t('trials:trials-myinfo:title:accountInfo') }}
</div>
<el-form label-position="right" label-width="100px">
<el-form
label-position="right"
label-width="100px"
:rules="rule"
:model="userForm"
ref="userFormRef"
>
<!-- 用户名 -->
<el-form-item
:label="$t('trials:trials-myinfo:form:userName')"
style="margin-bottom: 5px"
prop="UserName"
>
<span>{{ user.UserName }}</span>
</el-form-item>
@ -27,18 +32,18 @@
size="small"
@click="setNewUserName"
>
{{ $t("trials:trials-myinfo:button:update") }}
{{ $t('trials:trials-myinfo:button:update') }}
</el-button>
</el-form-item>
<!-- 电话 -->
<el-form-item
:label="$t('trials:trials-myinfo:form:phone')"
style="margin-bottom: 5px"
prop="UserName"
prop="Phone"
>
<span>{{ user.Phone }}</span>
</el-form-item>
<el-form-item label="" style="position: relative" prop="UserName">
<el-form-item label="" style="position: relative" prop="Phone">
<el-input
v-model="userForm.Phone"
:placeholder="$t('trials:trials-myinfo:form:phone')"
@ -51,14 +56,14 @@
size="small"
@click="setNewPhone"
>
{{ $t("trials:trials-myinfo:button:update") }}
{{ $t('trials:trials-myinfo:button:update') }}
</el-button>
</el-form-item>
<!-- 邮箱 -->
<el-form-item
:label="$t('trials:trials-myinfo:form:email')"
style="margin-bottom: 5px"
prop="UserName"
prop="EMail"
>
<span>{{ user.EMail }}</span>
</el-form-item>
@ -100,7 +105,7 @@
size="small"
@click="setNewEmail"
>
{{ $t("trials:trials-myinfo:button:update") }}
{{ $t('trials:trials-myinfo:button:update') }}
</el-button>
</el-form-item>
</el-form>
@ -114,19 +119,19 @@ import {
setNewEmail,
setNewPhone,
setNewUserName,
} from "@/api/system/user.js";
import { removeToken } from "@/utils/auth";
import password from "./password.vue";
var timer = "";
var countdown = 60;
} from '@/api/system/user.js'
import { removeToken } from '@/utils/auth'
import password from './password.vue'
var timer = ''
var countdown = 60
export default {
name: "account",
name: 'account',
components: { password },
props: {
user: {
required: true,
default: () => {
return {};
return {}
},
},
IsCanConnectInternet: {
@ -138,108 +143,138 @@ export default {
return {
userForm: {},
sendDisabled: true,
sendTitle: this.$t("trials:trials-myinfo:button:getVCode"),
};
sendTitle: this.$t('trials:trials-myinfo:button:getVCode'),
rule: {
UserName: [
{
validator: (rule, value, callback) => {
if (!value) return callback()
var lang = zzSessionStorage.getItem('lang')
? zzSessionStorage.getItem('lang')
: 'zh'
/* eslint-disable */
var reg1 = /^[a-zA-Z0-9_]{4,16}$/ //8
if (!reg1.test(value)) {
callback(
lang === 'zh'
? new Error(
'1用户名字符长度最小为4个字符最大为16个字符只可使用字母、数字、下划线'
)
: new Error(
'the username must have:1) At least 4 characters;2) At most 16 characters;3)Only letters, numbers, and underscores are allowed.'
)
)
} else {
callback()
}
},
trigger: 'blur',
},
],
},
}
},
methods: {
setNewEmail() {
setNewEmail(this.userForm.EMail, this.userForm.VerificationCode).then(
() => {
this.userForm.EMail = "";
this.userForm.VerificationCode = "";
this.userForm.EMail = ''
this.userForm.VerificationCode = ''
this.$message.success(
this.$t("trials:trials-myinfo:message:updateSuccessfully")
);
this.$emit("getUserInfo");
this.$t('trials:trials-myinfo:message:updateSuccessfully')
)
this.$emit('getUserInfo')
}
);
)
},
async setNewUserName() {
try {
let validate = await this.$refs.userFormRef.validate()
if (!validate) return false
let confirm = await this.$confirm(
this.$t("trials:trials-myInfo:confirmMessage:updateUserName"),
this.$t('trials:trials-myInfo:confirmMessage:updateUserName'),
{
type: "warning",
type: 'warning',
distinguishCancelAndClose: true,
confirmButtonText: this.$t("common:button:confirm"),
cancelButtonText: this.$t("common:button:cancel"),
confirmButtonText: this.$t('common:button:confirm'),
cancelButtonText: this.$t('common:button:cancel'),
}
);
if (confirm !== "confirm") return;
let res = await setNewUserName(this.userForm.UserName);
)
if (confirm !== 'confirm') return
let res = await setNewUserName(this.userForm.UserName)
if (res.IsSuccess) {
this.userForm.UserName = "";
this.userForm.UserName = ''
this.$message.success(
this.$t("trials:trials-myinfo:message:updateSuccessfully")
);
this.$emit("getUserInfo");
removeToken();
this.logout();
this.$t('trials:trials-myinfo:message:updateSuccessfully')
)
this.$emit('getUserInfo')
removeToken()
this.logout()
}
} catch (err) {
console.log(err);
console.log(err)
}
},
setNewPhone() {
setNewPhone(this.userForm.Phone).then(() => {
this.userForm.Phone = "";
this.userForm.Phone = ''
this.$message.success(
this.$t("trials:trials-myinfo:message:updateSuccessfully")
);
this.$emit("getUserInfo");
});
this.$t('trials:trials-myinfo:message:updateSuccessfully')
)
this.$emit('getUserInfo')
})
},
handleEmailChange() {
var reg =
/^[A-Za-z0-9]+([_\.][A-Za-z0-9]+)*@([A-Za-z0-9\-]+\.)+[A-Za-z]{2,6}$/;
/^[A-Za-z0-9]+([_\.][A-Za-z0-9]+)*@([A-Za-z0-9\-]+\.)+[A-Za-z]{2,6}$/
if (this.userForm.EMail && reg.test(this.userForm.EMail)) {
this.sendDisabled = false;
this.sendDisabled = false
} else {
this.sendDisabled = true;
this.sendDisabled = true
}
},
sendVerificationCode() {
sendVerificationCode(this.userForm.EMail).then(() => {
this.settime(this);
this.settime(this)
//
this.$message.success(
this.$t("trials:trials-myinfo:message:sendSuccessfully")
);
});
this.$t('trials:trials-myinfo:message:sendSuccessfully')
)
})
},
settime(obj) {
if (countdown === 0) {
obj.sendDisabled = false;
obj.sendTitle = this.$t("trials:trials-myinfo:button:getVCode"); // ''
countdown = 60;
clearTimeout(timer);
return;
obj.sendDisabled = false
obj.sendTitle = this.$t('trials:trials-myinfo:button:getVCode') // ''
countdown = 60
clearTimeout(timer)
return
} else {
obj.sendDisabled = true;
obj.sendDisabled = true
obj.sendTitle = `${this.$t(
"trials:trials-myinfo:button:wait"
)}(${countdown}s)`;
countdown--;
'trials:trials-myinfo:button:wait'
)}(${countdown}s)`
countdown--
// eslint-disable-next-line no-self-assign
countdown = countdown;
countdown = countdown
timer = setTimeout(function () {
obj.settime(obj);
}, 1000);
obj.settime(obj)
}, 1000)
}
},
async logout() {
/* eslint-disable */
var loginType = zzSessionStorage.getItem("loginType");
await this.$store.dispatch("user/logout");
var loginType = zzSessionStorage.getItem('loginType')
await this.$store.dispatch('user/logout')
if (loginType) {
this.$router.push(`/login?loginType=${loginType}`);
this.$router.push(`/login?loginType=${loginType}`)
} else {
this.$router.push(`/login`);
this.$router.push(`/login`)
}
this.$i18n.locale = "zh";
this.setLanguage("zh");
this.$updateDictionary();
this.$i18n.locale = 'zh'
this.setLanguage('zh')
this.$updateDictionary()
},
},
};
}
</script>

View File

@ -2,7 +2,7 @@
<div class="trial-myinfo-right-box">
<div class="trial-myinfo-head">
<!-- 修改密码 -->
{{ $t("trials:trials-myinfo:title:updatePaasord") }}
{{ $t('trials:trials-myinfo:title:updatePaasord') }}
</div>
<el-form
ref="passwordForm"
@ -52,17 +52,17 @@
</el-form-item>
</el-form>
<el-button type="primary" size="small" class="trial-info-btn" @click="save">
{{ $t("trials:trials-myinfo:button:save") }}
{{ $t('trials:trials-myinfo:button:save') }}
</el-button>
</div>
</template>
<script>
import md5 from "js-md5";
import { mapGetters, mapMutations } from "vuex";
import { modifyPassword } from "@/api/admin.js";
import { removeToken } from "@/utils/auth";
import md5 from 'js-md5'
import { mapGetters, mapMutations } from 'vuex'
import { modifyPassword } from '@/api/admin.js'
import { removeToken } from '@/utils/auth'
export default {
name: "password",
name: 'password',
data() {
return {
password: {},
@ -70,91 +70,96 @@ export default {
OldPassWord: [
{
required: true,
message: this.$t("common:ruleMessage:specify"),
trigger: "blur",
message: this.$t('common:ruleMessage:specify'),
trigger: 'blur',
},
],
NewPassWord: [
{
required: true,
message: this.$t("common:ruleMessage:specify"),
trigger: "blur",
message: this.$t('common:ruleMessage:specify'),
trigger: 'blur',
},
{
required: true,
trigger: "blur",
trigger: 'blur',
validator: this.$validatePassword,
},
],
ConfirmPassWord: [
{
required: true,
message: this.$t("common:ruleMessage:specify"),
trigger: "blur",
message: this.$t('common:ruleMessage:specify'),
trigger: 'blur',
},
{
required: true,
trigger: "blur",
trigger: 'blur',
validator: this.$validatePassword,
},
],
},
};
}
},
computed: {
...mapGetters(["userId"]),
...mapGetters(['userId']),
},
methods: {
...mapMutations({ setLanguage: "lang/setLanguage" }),
...mapMutations({ setLanguage: 'lang/setLanguage' }),
async save() {
try {
let validate = await this.$refs.passwordForm.validate();
if (!validate) return;
let validate = await this.$refs.passwordForm.validate()
if (!validate) return
if (this.password.NewPassWord !== this.password.ConfirmPassWord) {
this.$alert(this.$t("passwordReset:formRule:passwordsDiffer"));
return;
this.$alert(this.$t('passwordReset:formRule:passwordsDiffer'))
return
}
let confirm = await this.$confirm(
this.$t("trials:trials-myInfo:confirmMessage:updatePassWord"),
this.$t('trials:trials-myInfo:confirmMessage:updatePassWord'),
{
type: "warning",
type: 'warning',
distinguishCancelAndClose: true,
confirmButtonText: this.$t("common:button:confirm"),
cancelButtonText: this.$t("common:button:cancel"),
confirmButtonText: this.$t('common:button:confirm'),
cancelButtonText: this.$t('common:button:cancel'),
}
);
if (confirm !== "confirm") return;
)
if (confirm !== 'confirm') return
const param = {
UserId: this.userId,
NewPassWord: md5(this.password.NewPassWord),
OldPassWord: md5(this.password.OldPassWord),
};
let res = await modifyPassword(param);
}
let res = await modifyPassword(param)
if (res.IsSuccess) {
// ,
this.$message.success(
this.$t("trials:trials-myinfo:message:modifyPWSuccessfully")
);
removeToken();
this.logout();
this.$t('trials:trials-myinfo:message:modifyPWSuccessfully')
)
removeToken()
this.logout()
}
} catch (err) {
console.log(err);
console.log(err)
}
},
async logout() {
/* eslint-disable */
var loginType = zzSessionStorage.getItem("loginType");
await this.$store.dispatch("user/logout");
var loginType = zzSessionStorage.getItem('loginType')
await this.$store.dispatch('user/logout')
if (loginType) {
this.$router.push(`/login?loginType=${loginType}`);
this.$router.push(`/login?loginType=${loginType}`)
} else {
this.$router.push(`/login`);
this.$router.push(`/login`)
}
this.$i18n.locale = "zh";
this.setLanguage("zh");
this.$updateDictionary();
this.$i18n.locale = 'zh'
this.setLanguage('zh')
this.$updateDictionary()
},
},
};
</script>
}
</script>
<style lang="scss" scoped>
.el-form-item {
margin-bottom: 30px;
}
</style>

View File

@ -5,7 +5,7 @@
<el-form :inline="true">
<!-- 文件类型 -->
<el-form-item :label="$t('trials:signRecords:table:fileType')">
<el-select v-model="searchData.FileTypeId" style="width: 150px">
<el-select v-model="searchData.FileTypeId" clearable style="width: 150px">
<el-option
v-for="item of typeOptions"
:key="item.FileTypeId"

View File

@ -1,19 +1,22 @@
<template>
<div v-loading="loading" class="sign-form-wrapper">
<div class="sign-form-body">
<h4 v-if="signText" style="color:red">*{{ signText }}</h4>
<h4 v-if="signText" style="color: red">*{{ signText }}</h4>
<el-form
ref="signForm"
:model="signForm"
size="small"
label-width="100px"
>
<el-form-item
:label="$t('common:form:sign:userName')"
prop="UserName"
:rules="[
{ required: true, message: $t('common:ruleMessage:specify'), trigger: 'blur' }
{
required: true,
message: $t('common:ruleMessage:specify'),
trigger: 'blur',
},
]"
>
<el-input v-model="signForm.UserName" />
@ -22,18 +25,36 @@
:label="$t('common:form:sign:password')"
prop="Password"
:rules="[
{ required: true, message: $t('common:ruleMessage:specify'), trigger: 'blur' }
{
required: true,
message: $t('common:ruleMessage:specify'),
trigger: 'blur',
},
]"
>
<el-input v-model="signForm.Password" show-password auto-complete="new-password" />
<el-input
v-model="signForm.Password"
show-password
auto-complete="new-password"
/>
</el-form-item>
</el-form>
</div>
<div slot="footer" class="dialog-footer sign-form-footer">
<el-button :disabled="btnLoading" size="small" type="primary" @click="handleclose">
<el-button
:disabled="btnLoading"
size="small"
type="primary"
@click="handleclose"
>
{{ $t('common:button:cancel') }}
</el-button>
<el-button :loading="btnLoading" size="small" type="primary" @click="userConfirm">
<el-button
:loading="btnLoading"
size="small"
type="primary"
@click="userConfirm"
>
{{ $t('common:button:sign') }}
</el-button>
</div>
@ -49,48 +70,54 @@ export default {
props: {
fileName: {
type: String,
required: true
required: true,
},
isSystemDoc: {
type: Boolean,
required: true
required: true,
},
documentId: {
type: String,
required: true
required: true,
},
trialId: {
type: String,
default: ''
}
default: '',
},
},
data() {
return {
signForm: {
UserName: '',
Password: ''
Password: '',
},
signText: '',
btnLoading: false,
loading: false
loading: false,
}
},
mounted() {
this.loading = true
getSystemBasicData(attachmentSignCode).then(res => {
var text
if (this.$i18n.locale === 'zh') {
text = res.Result.ValueCN
} else {
text = res.Result.Value
}
this.signText = text.replace('xxx', this.fileName)
this.loading = false
}).catch(() => { this.loading = false })
getSystemBasicData(attachmentSignCode)
.then((res) => {
var text
if (this.$i18n.locale === 'zh') {
text = res.Result.ValueCN
} else {
text = res.Result.Value
}
this.signText = text.replace('xxx', this.fileName)
this.loading = false
})
.catch(() => {
this.loading = false
})
},
methods: {
userConfirm() {
const currentUser = zzSessionStorage.getItem('userName').toLocaleLowerCase()
const currentUser = zzSessionStorage
.getItem('userName')
.toLocaleLowerCase()
this.$refs.signForm.validate((valid) => {
console.log(valid)
if (!valid) return
@ -102,44 +129,52 @@ export default {
this.btnLoading = true
var params = {
data: {
trialId: this.trialId,
// trialId: this.trialId,
documentId: this.documentId,
isSystemDoc: this.isSystemDoc
isSystemDoc: this.isSystemDoc,
},
signInfo: {
UserName: this.signForm.UserName,
PassWord: md5(this.signForm.Password),
TrialId: this.trialId,
// c: this.trialId,
SignCode: '',
SignText: this.signText,
SignCodeId: '',
SubjectVisitId: ''
}
SubjectVisitId: '',
},
}
userConfirm(params).then(res => {
if (res.IsSuccess) {
//
this.$message.success(this.$t('common:message:signSuccessfully'))
this.$emit('closeDialog', true)
}
}).catch(() => { this.btnLoading = false })
if (!this.isSystemDoc) {
params.data.TrialId = this.trialId
params.signInfo.TrialId = this.trialId
}
userConfirm(params)
.then((res) => {
if (res.IsSuccess) {
//
this.$message.success(this.$t('common:message:signSuccessfully'))
this.$emit('closeDialog', true)
}
})
.catch(() => {
this.btnLoading = false
})
})
},
handleclose() {
this.$emit('closeDialog', false)
}
}
},
},
}
</script>
<style lang="scss" scoped>
.sign-form-wrapper{
.sign-form-body{
padding:10px;
.sign-form-wrapper {
.sign-form-body {
padding: 10px;
border: 1px solid #e0e0e0;
max-height:650px;
max-height: 650px;
overflow-y: auto;
}
.sign-form-footer{
.sign-form-footer {
margin-top: 10px;
text-align: right;
}

View File

@ -7,6 +7,7 @@
<el-form-item :label="$t('trials:self-attachment:table:fileType')">
<el-select
v-model="searchData.FileTypeId"
clearable
style="width:150px;"
>
<el-option

View File

@ -15,7 +15,7 @@
<el-table
ref="myTable"
v-loading="listLoading"
v-adaptive="{bottomOffset:55}"
v-adaptive="{ bottomOffset: 55 }"
:data="list"
stripe
height="100"
@ -32,7 +32,15 @@
v-if="!hasPermi(['role:ea'])"
>
<template slot-scope="scope">
<el-button type="text" @click="go(`/trialsResume?doctorId=${scope.row.DoctorId}&token=${token}`)">{{ scope.row.LastName }} / {{ scope.row.FirstName }}</el-button>
<el-button
type="text"
@click="
go(
`/trialsResume?doctorId=${scope.row.DoctorId}&token=${token}`
)
"
>{{ scope.row.LastName }} / {{ scope.row.FirstName }}</el-button
>
</template>
</el-table-column>
<!-- Name CN -->
@ -48,7 +56,7 @@
<el-table-column
prop="UserName"
:label="$t('trials:enrolledReviews:table:userName')"
:width="hasPermi(['role:ea'])?300:150"
:width="hasPermi(['role:ea']) ? 300 : 150"
sortable="custom"
show-overflow-tooltip
/>
@ -56,12 +64,16 @@
<el-table-column
prop="EnrollTimeStr"
:label="$t('trials:enrolledReviews:table:enrollmentTime')"
:width="hasPermi(['role:ea'])?320:130"
:width="hasPermi(['role:ea']) ? 320 : 130"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
{{ scope.row.EnrollTime?moment(scope.row.EnrollTime).format('YYYY-MM-DD'):'' }}
{{
scope.row.EnrollTime
? moment(scope.row.EnrollTime).format('YYYY-MM-DD')
: ''
}}
</template>
</el-table-column>
<el-table-column
@ -78,8 +90,16 @@
:inactive-value="false"
:active-text="$fd('IsEnable', true)"
:inactive-text="$fd('IsEnable', false)"
@change="(v) => {return isEnableChange(scope.row, v)}"
:disabled="!hasPermi(['trials:trials-panel:enrolled-reviewers:list:edit'])"
@change="
(v) => {
return isEnableChange(scope.row, v)
}
"
:disabled="
!hasPermi([
'trials:trials-panel:enrolled-reviewers:list:edit',
])
"
/>
<!-- <span :style="{color:scope.row.IsEnable?'#409eff':'#dcdfe6'}"> {{$fd('IsEnable', scope.row.IsEnable)}}</span> -->
</div>
@ -98,16 +118,46 @@
width="300"
>
<template slot-scope="scope">
<span v-for="item of scope.row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === ite.TrialReadingCriterionId}) ? scope.row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === ite.TrialReadingCriterionId}).ReadingCategorys : []" :key="item.TrialReadingCriterionId" style="margin:5px 5px 5px 0;">
<el-tag v-if="item === 1" type="primary">{{ $fd('ReadingCategory', item) + ' & ' + $fd('ReadingCategory', 2) }}</el-tag>
<!-- <el-tag v-if="item === 2" type="info">{{ $fd('ReadingCategory', item) }}</el-tag>-->
<el-tag v-if="item === 4" type="danger">{{ $fd('ReadingCategory', item) }}</el-tag>
<el-tag v-if="item === 5" type="warning">{{ $fd('ReadingCategory', item) }}</el-tag>
</span>
<el-button
v-hasPermi="['trials:trials-panel:enrolled-reviewers:list:edit']"
<span
v-for="item of scope.row.CriterionCategoryList.find((v) => {
return (
v.TrialReadingCriterionId === ite.TrialReadingCriterionId
)
})
? scope.row.CriterionCategoryList.find((v) => {
return (
v.TrialReadingCriterionId ===
ite.TrialReadingCriterionId
)
}).ReadingCategorys
: []"
:key="item.TrialReadingCriterionId"
style="margin: 5px 5px 5px 0"
>
<el-tag v-if="item === 1" type="primary">{{
$fd('ReadingCategory', item) +
' & ' +
$fd('ReadingCategory', 2)
}}</el-tag>
<!-- <el-tag v-if="item === 2" type="info">{{ $fd('ReadingCategory', item) }}</el-tag>-->
<el-tag v-if="item === 4" type="danger">{{
$fd('ReadingCategory', item)
}}</el-tag>
<el-tag v-if="item === 5" type="warning">{{
$fd('ReadingCategory', item)
}}</el-tag>
</span>
<el-button
v-hasPermi="[
'trials:trials-panel:enrolled-reviewers:list:edit',
]"
type="text"
@click="openSetEnrollReadingCategory(scope.row, ite.TrialReadingCriterionId)"
@click="
openSetEnrollReadingCategory(
scope.row,
ite.TrialReadingCriterionId
)
"
>
{{ $t('trials:enrolledReviews:button:config') }}
</el-button>
@ -120,13 +170,27 @@
>
<template slot-scope="scope">
<el-switch
v-model="scope.row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === ite.TrialReadingCriterionId}).IsJoinAnalysis"
v-model="
scope.row.CriterionCategoryList.find((v) => {
return (
v.TrialReadingCriterionId === ite.TrialReadingCriterionId
)
}).IsJoinAnalysis
"
:active-value="true"
:inactive-value="false"
:active-text="$fd('YesOrNo', true)"
:inactive-text="$fd('YesOrNo', false)"
@change="(v) => {return isConsistencyChange(scope.row, ite, v, true)}"
:disabled="!hasPermi(['trials:trials-panel:enrolled-reviewers:list:edit'])"
@change="
(v) => {
return isConsistencyChange(scope.row, ite, v, true)
}
"
:disabled="
!hasPermi([
'trials:trials-panel:enrolled-reviewers:list:edit',
])
"
/>
<!-- <span v-if="scope.row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === ite.TrialReadingCriterionId}).IsJoinAnalysis"> {{$fd('YesOrNo', scope.row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === ite.TrialReadingCriterionId}).IsJoinAnalysis)}}</span>
<span v-else> {{$fd('YesOrNo',false)}}</span> -->
@ -138,8 +202,20 @@
show-overflow-tooltip
>
<template slot-scope="scope">
<div v-if="scope.row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === ite.TrialReadingCriterionId}).StatementCriterionFileList.length > 0">
<el-button type="text" @click="preview(scope.row,ite.TrialReadingCriterionId)">View</el-button>
<div
v-if="
scope.row.CriterionCategoryList.find((v) => {
return (
v.TrialReadingCriterionId === ite.TrialReadingCriterionId
)
}).StatementCriterionFileList.length > 0
"
>
<el-button
type="text"
@click="preview(scope.row, ite.TrialReadingCriterionId)"
>View</el-button
>
<!-- <el-button type="text" @click="windowOpen(scope.row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === ite.TrialReadingCriterionId}).StatementCriterionFileList[0].FilePath)">View</el-button> -->
</div>
<div v-else>
@ -147,10 +223,19 @@
<!-- 上传 -->
<el-button
v-if="ite.CriterionType === 0"
v-hasPermi="['trials:trials-panel:enrolled-reviewers:list:edit']"
v-hasPermi="[
'trials:trials-panel:enrolled-reviewers:list:edit',
]"
type="text"
style="margin-left: 10px"
@click="addCol(0, scope.row, ite, $t('trials:enrolledReviews:message:SOW'))"
@click="
addCol(
0,
scope.row,
ite,
$t('trials:enrolledReviews:message:SOW')
)
"
>
{{ $t('trials:enrolledReviews:button:upload') }}
</el-button>
@ -163,18 +248,40 @@
show-overflow-tooltip
>
<template slot-scope="scope">
<div v-if="scope.row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === ite.TrialReadingCriterionId}).AcknowledgementCriterionFileList.length > 0">
<el-button type="text" @click="windowOpen(scope.row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === ite.TrialReadingCriterionId}).AcknowledgementCriterionFileList[0].FilePath)">View</el-button>
<div
v-if="
scope.row.CriterionCategoryList.find((v) => {
return (
v.TrialReadingCriterionId === ite.TrialReadingCriterionId
)
}).AcknowledgementCriterionFileList.length > 0
"
>
<el-button
type="text"
@click="preview(scope.row, ite.TrialReadingCriterionId)"
>
View
</el-button>
</div>
<div v-else>
<span>{{ $t('trials:enrolledReviews:label:notUpload') }}</span>
<!-- 上传 -->
<el-button
v-if="ite.CriterionType === 0"
v-hasPermi="['trials:trials-panel:enrolled-reviewers:list:edit']"
v-hasPermi="[
'trials:trials-panel:enrolled-reviewers:list:edit',
]"
type="text"
style="margin-left: 10px"
@click="addCol(1, scope.row, ite, $t('trials:enrolledReviews:message:SOW'))"
@click="
addCol(
1,
scope.row,
ite,
$t('trials:enrolledReviews:message:SOW')
)
"
>
{{ $t('trials:enrolledReviews:button:upload') }}
</el-button>
@ -188,30 +295,47 @@
>
<template slot-scope="scope">
<div>
{{ scope.row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === ite.TrialReadingCriterionId}).PendingCount }}
{{
scope.row.CriterionCategoryList.find((v) => {
return (
v.TrialReadingCriterionId === ite.TrialReadingCriterionId
)
}).PendingCount
}}
/
{{ scope.row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === ite.TrialReadingCriterionId}).ComplectedCount }}
{{
scope.row.CriterionCategoryList.find((v) => {
return (
v.TrialReadingCriterionId === ite.TrialReadingCriterionId
)
}).ComplectedCount
}}
/
{{ scope.row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === ite.TrialReadingCriterionId}).TotalCount }}
{{
scope.row.CriterionCategoryList.find((v) => {
return (
v.TrialReadingCriterionId === ite.TrialReadingCriterionId
)
}).TotalCount
}}
</div>
</template>
</el-table-column>
</el-table-column>
</el-table>
</template>
<!-- 上传SOW -->
<!-- <base-model :config="sow_model">-->
<!-- <template slot="dialog-body">-->
<!-- <upload-acksow-->
<!-- ref="uploadAckSow"-->
<!-- :trial-id="listQuery.TrialId"-->
<!-- :doctor-id="doctorId"-->
<!-- @getFileList="getFileList"-->
<!-- />-->
<!-- </template>-->
<!-- </base-model>-->
<!-- <base-model :config="sow_model">-->
<!-- <template slot="dialog-body">-->
<!-- <upload-acksow-->
<!-- ref="uploadAckSow"-->
<!-- :trial-id="listQuery.TrialId"-->
<!-- :doctor-id="doctorId"-->
<!-- @getFileList="getFileList"-->
<!-- />-->
<!-- </template>-->
<!-- </base-model>-->
<!-- 修改 Reading Type -->
<base-model :config="readingType_model">
@ -258,25 +382,42 @@
allow-create
multiple
default-first-option
style="width: 50%;"
style="width: 50%"
>
<template v-for="item of $d.ReadingCategory">
<el-option v-if="item.value === 1" :key="item.id" :value="1" :label="$fd('ReadingCategory', 1) + ' & ' + $fd('ReadingCategory', 2)" />
<el-option
v-if="item.value === 1"
:key="item.id"
:value="1"
:label="
$fd('ReadingCategory', 1) + ' & ' + $fd('ReadingCategory', 2)
"
/>
</template>
<template v-for="item of $d.ReadingCategory">
<el-option v-if="item.value === 4 && IsArbitrationReading" :key="item.id" :value="item.value" :label="$fd('ReadingCategory', 4)" />
<el-option
v-if="item.value === 4 && IsArbitrationReading"
:key="item.id"
:value="item.value"
:label="$fd('ReadingCategory', 4)"
/>
</template>
<template v-for="item of $d.ReadingCategory">
<el-option v-if="item.value === 5 && IsOncologyReading" :key="item.id" :value="item.value" :label="$fd('ReadingCategory', 5)" />
<el-option
v-if="item.value === 5 && IsOncologyReading"
:key="item.id"
:value="item.value"
:label="$fd('ReadingCategory', 5)"
/>
</template>
</el-select>
</template>
<template slot="dialog-footer">
<div style="margin-top:30px;">
<div style="margin-top: 30px">
<el-button
:disabled="readingCategory_model.btnLoading"
type="primary"
@click="readingCategory_model.visible = false"
:disabled="readingCategory_model.btnLoading"
type="primary"
@click="readingCategory_model.visible = false"
>
{{ $t('common:button:cancel') }}
</el-button>
@ -298,17 +439,23 @@
:model="updateStatusForm"
:rules="updateStatusFormrules"
>
<el-form-item :label="$t('trials:enrolledReviews:label:enrollmentStatus')" prop="Status">
<el-select
v-model="updateStatusForm.Status"
style="width:60%;"
>
<el-form-item
:label="$t('trials:enrolledReviews:label:enrollmentStatus')"
prop="Status"
>
<el-select v-model="updateStatusForm.Status" style="width: 60%">
<!-- 0回退1出组 -->
<el-option v-for="item of $d.OutOrInEnrollment" :key="`OutOrInEnrollment${item.value}`" :value="item.value" :label="item.label" :disabled="item.value === 0 && currentWorkload>0" />
<el-option
v-for="item of $d.OutOrInEnrollment"
:key="`OutOrInEnrollment${item.value}`"
:value="item.value"
:label="item.label"
:disabled="item.value === 0 && currentWorkload > 0"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="updateStatusForm.Status== 1"
v-if="updateStatusForm.Status == 1"
:label="$t('trials:enrolledReviews:label:outOfEnrollmentTime')"
prop="OutEnrollmentTime"
>
@ -346,10 +493,16 @@
label-width="120px"
size="small"
>
<el-form-item :label="$t('trials:enrolledReviews:table:criterionName')" prop="CriterionType">
<el-form-item
:label="$t('trials:enrolledReviews:table:criterionName')"
prop="CriterionType"
>
<el-input v-model="form.CriterionName" disabled />
</el-form-item>
<el-form-item :label="$t('trials:enrolledReviews:table:file')" prop="FilePath">
<el-form-item
:label="$t('trials:enrolledReviews:table:file')"
prop="FilePath"
>
<el-upload
class="upload-demo"
action
@ -358,19 +511,35 @@
:on-preview="handlePreview2"
:on-remove="handleRemoveFile2"
:show-file-list="true"
accept=".pdf"
:limit="1"
:file-list="fileList"
>
<el-button size="small" type="primary" :disabled="fileList.length > 0">{{$t('common:button:upload')}}</el-button>
<el-button
size="small"
type="primary"
:disabled="fileList.length > 0"
>{{ $t('common:button:upload') }}</el-button
>
</el-upload>
</el-form-item>
</el-form>
</template>
<template slot="dialog-footer">
<el-button :disabled="btnLoading" size="small" type="primary" @click="handleCancle">
<el-button
:disabled="btnLoading"
size="small"
type="primary"
@click="handleCancle"
>
{{ $t('common:button:cancel') }}
</el-button>
<el-button size="small" type="primary" :loading="btnLoading" @click="handleSave">
<el-button
size="small"
type="primary"
:loading="btnLoading"
@click="handleSave"
>
{{ $t('common:button:save') }}
</el-button>
</template>
@ -384,7 +553,7 @@ import {
updateReviewerReadingType,
enrollBackOrOut,
setEnrollReadingCategory,
batchSetCriterionJoinJoinAnalysis
batchSetCriterionJoinJoinAnalysis,
} from '@/api/trials'
import { addDoctorCriterionFile } from '@/api/reviewers'
import { uploadFile } from '@/api/attachment'
@ -405,7 +574,7 @@ export default {
PageIndex: 1,
PageSize: 100,
Asc: false,
SortField: ''
SortField: '',
},
doctorId: '',
currentRow: {},
@ -415,41 +584,59 @@ export default {
currentClickRowData: '',
btnLoading: false,
readingType: '',
model_cfg: { visible: false, showClose: true, width: '600px', title: '', appendToBody: true },
model_cfg: {
visible: false,
showClose: true,
width: '600px',
title: '',
appendToBody: true,
},
sow_model: {
visible: false,
title: this.$t('trials:enrolledReviews:dialogTitle:signedAckOfSow'),
showClose: true,
width: '500px'
width: '500px',
},
readingType_model: {
visible: false,
title: this.$t('trials:enrolledReviews:dialogTitle:changeReadingType'),
showClose: true,
width: '500px',
btnLoading: false
btnLoading: false,
},
readingCategory_model: {
visible: false,
title: this.$t('trials:enrolledReviews:dialogTitle:changeReadingType'),
showClose: true,
width: '600px',
btnLoading: false
btnLoading: false,
},
reviewerStatus_model: {
visible: false,
title: this.$t('trials:enrolledReviews:dialogTitle:changeStatus'),
showClose: true,
width: '500px',
btnLoading: false
btnLoading: false,
},
updateStatusForm: {
Status: '',
OutEnrollmentTime: ''
OutEnrollmentTime: '',
},
updateStatusFormrules: {
Status: [{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur', 'change'] }],
OutEnrollmentTime: [{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur', 'change'] }]
Status: [
{
required: true,
message: this.$t('common:ruleMessage:select'),
trigger: ['blur', 'change'],
},
],
OutEnrollmentTime: [
{
required: true,
message: this.$t('common:ruleMessage:select'),
trigger: ['blur', 'change'],
},
],
},
token: store.getters.token,
ReadingCategorys: [],
@ -460,15 +647,25 @@ export default {
fileList: [],
form: {},
rowData: {},
moment
moment,
}
},
computed: {
currentWorkload() {
if (Object.keys(this.currentRow).length === 0) return 0
var total = this.currentRow.Adjudication + this.currentRow.AdjudicationIn24H + this.currentRow.AdjudicationIn48H + this.currentRow.Timepoint + this.currentRow.TimepointIn24H + this.currentRow.TimepointIn48H + this.currentRow.TrainingTimes + this.currentRow.RefresherTraining + this.currentRow.Global + this.currentRow.Downtime
var total =
this.currentRow.Adjudication +
this.currentRow.AdjudicationIn24H +
this.currentRow.AdjudicationIn48H +
this.currentRow.Timepoint +
this.currentRow.TimepointIn24H +
this.currentRow.TimepointIn48H +
this.currentRow.TrainingTimes +
this.currentRow.RefresherTraining +
this.currentRow.Global +
this.currentRow.Downtime
return total
}
},
},
mounted() {
this.trialId = this.$route.query.trialCode
@ -477,14 +674,16 @@ export default {
},
methods: {
//
preview(row,TrialReadingCriterionId){
let file = row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === TrialReadingCriterionId}).StatementCriterionFileList[0];
let arr = file.FilePath.split(".");
preview(row, TrialReadingCriterionId) {
let file = row.CriterionCategoryList.find((v) => {
return v.TrialReadingCriterionId === TrialReadingCriterionId
}).StatementCriterionFileList[0]
let arr = file.FilePath.split('.')
this.$preview({
path:file.FilePath,
type:arr[arr.length - 1],
title:file.FileName
path: file.FilePath,
type: arr[arr.length - 1],
title: file.FileName,
})
},
isConsistencyChange(row, ite, value, showMessage) {
@ -492,39 +691,50 @@ export default {
},
batchSetCriterionJoinJoinAnalysis(row, ite, value, showMessage) {
let params
if (row.DoctorCriterionStatusList && row.DoctorCriterionStatusList.length > 0) {
if (
row.DoctorCriterionStatusList &&
row.DoctorCriterionStatusList.length > 0
) {
if (ite) {
let item = row.CriterionCategoryList.find(v => v.TrialReadingCriterionId === ite.TrialReadingCriterionId)
let item = row.CriterionCategoryList.find(
(v) => v.TrialReadingCriterionId === ite.TrialReadingCriterionId
)
if (item) {
params = [{
Id: item.Id,
EnrollId: item.EnrollId,
TrialReadingCriterionId: item.TrialReadingCriterionId,
IsJoinAnalysis: value
}]
params = [
{
Id: item.Id,
EnrollId: item.EnrollId,
TrialReadingCriterionId: item.TrialReadingCriterionId,
IsJoinAnalysis: value,
},
]
}
} else {
params = row.DoctorCriterionStatusList.map(v => {
params = row.DoctorCriterionStatusList.map((v) => {
return {
Id: v.Id,
EnrollId: v.EnrollId,
TrialReadingCriterionId: v.TrialReadingCriterionId,
IsJoinAnalysis: v.IsJoinAnalysis
IsJoinAnalysis: v.IsJoinAnalysis,
}
})
}
} else {
params = row.CriterionCategoryList.map(v => {
params = row.CriterionCategoryList.map((v) => {
return {
EnrollId: v.EnrollId,
TrialReadingCriterionId: v.TrialReadingCriterionId,
IsJoinAnalysis: value
IsJoinAnalysis: value,
}
})
}
batchSetCriterionJoinJoinAnalysis(params).then(res => {
batchSetCriterionJoinJoinAnalysis(params).then((res) => {
if (showMessage) {
this.$message.success(this.$t('trials:enrolledReviews:message:batchSetCriterionJoinJoinAnalysis1'))
this.$message.success(
this.$t(
'trials:enrolledReviews:message:batchSetCriterionJoinJoinAnalysis1'
)
)
}
this.initPage()
})
@ -532,17 +742,25 @@ export default {
go(path) {
window.open(path)
},
beforeUpload() {
beforeUpload(file) {
if (this.fileList.length > 0) {
// 1
this.$alert(this.$t('trials:enrolledReviews:message:uploadSowWaring1'))
return
return false
}
const fileName = file.name
var extendName = fileName
.substring(fileName.lastIndexOf('.'))
.toLocaleLowerCase()
if (extendName !== '.pdf') {
this.$alert(this.$t('trials:enrolledReviews:message:uploadSowWaring2'))
return false
}
},
handleSave() {
this.$refs['DictionaryTypeConfigForm'].validate(valid => {
this.$refs['DictionaryTypeConfigForm'].validate((valid) => {
if (!valid) return
addDoctorCriterionFile(this.form).then(res => {
addDoctorCriterionFile(this.form).then((res) => {
//
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.initPage()
@ -560,11 +778,19 @@ export default {
this.btnLoading = true
var fileName = param.file.name
let file = await this.fileToBlob(param.file)
let res = await this.OSSclient.put(`/SystemData/reviewer/${this.form.FileType === 0 ? '既往阅片情况声明' : '入项资格确认书'}/${this.rowData.DoctorId}/${fileName}`, file)
let res = await this.OSSclient.put(
`/SystemData/reviewer/${
this.form.FileType === 0 ? '既往阅片情况声明' : '入项资格确认书'
}/${this.rowData.DoctorId}/${fileName}`,
file
)
this.form.FileName = param.file.name
this.form.FilePath = this.$getObjectName(res.url)
param.file.url = this.$getObjectName(res.url)
this.$set(this.fileList, 0, { name: this.form.FileName, url: this.$getObjectName(res.url) })
this.$set(this.fileList, 0, {
name: this.form.FileName,
url: this.$getObjectName(res.url),
})
this.btnLoading = false
// this.btnLoading = true
@ -587,7 +813,7 @@ export default {
this.fileList = []
},
addCol(fileType, row, item) {
this.rowData = {...row}
this.rowData = { ...row }
this.model_cfg.visible = true
this.form = {
Remark: null,
@ -599,10 +825,13 @@ export default {
DoctorId: row.DoctorId,
CriterionType: item.CriterionType,
CriterionName: item.TrialReadingCriterionName,
TrialReadingCriterionId: item.TrialReadingCriterionId
TrialReadingCriterionId: item.TrialReadingCriterionId,
}
this.handleRemoveFile2()
this.model_cfg.title = fileType === 0 ? this.$t('trials:enrolledReviews:message:SOW') : this.$t('trials:enrolledReviews:message:EQC')// '' : ''
this.model_cfg.title =
fileType === 0
? this.$t('trials:enrolledReviews:message:SOW')
: this.$t('trials:enrolledReviews:message:EQC') // '' : ''
},
windowOpen(url) {
console.log(url)
@ -614,14 +843,16 @@ export default {
updateTrialReviewerState({
TrialId: this.listQuery.TrialId,
IsEnable: v,
EnrollId: row.EnrollId
}).then(res => {
// ''
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.listLoading = false
}).catch(() => {
this.listLoading = false
EnrollId: row.EnrollId,
})
.then((res) => {
// ''
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.listLoading = false
})
.catch(() => {
this.listLoading = false
})
},
handleUpdateReadingCategory() {
this.readingCategory_model.btnLoading = true
@ -630,19 +861,20 @@ export default {
TrialId: this.$route.query.trialId,
EnrollId: this.currentRow.EnrollId,
ReadingCategorys: Object.assign([], this.ReadingCategorys),
TrialReadingCriterionId: this.TrialReadingCriterionId
TrialReadingCriterionId: this.TrialReadingCriterionId,
}
if (~this.ReadingCategorys.indexOf(1)) {
params.ReadingCategorys.push(2)
}
setEnrollReadingCategory(params)
.then(res => {
.then((res) => {
this.readingCategory_model.visible = false
this.readingCategory_model.btnLoading = false
this.readingCategory_model.showClose = true
this.initPage()
this.$message.success(this.$t('common:message:savedSuccessfully'))
}).catch(_ => {
})
.catch((_) => {
this.readingCategory_model.btnLoading = false
this.readingCategory_model.showClose = true
})
@ -653,16 +885,20 @@ export default {
this.currentRow = row
this.TrialReadingCriterionId = TrialReadingCriterionId
this.readingCategory_model.visible = true
var o = row.CriterionCategoryList.find(v => {
var o = row.CriterionCategoryList.find((v) => {
return v.TrialReadingCriterionId === TrialReadingCriterionId
}) ? row.CriterionCategoryList.find(v => {
return v.TrialReadingCriterionId === TrialReadingCriterionId
}) : null
var i = row.TrialReadingCriterionList.find(v => {
})
? row.CriterionCategoryList.find((v) => {
return v.TrialReadingCriterionId === TrialReadingCriterionId
})
: null
var i = row.TrialReadingCriterionList.find((v) => {
return v.TrialReadingCriterionId === TrialReadingCriterionId
}) ? row.TrialReadingCriterionList.find(v => {
return v.TrialReadingCriterionId === TrialReadingCriterionId
}) : null
})
? row.TrialReadingCriterionList.find((v) => {
return v.TrialReadingCriterionId === TrialReadingCriterionId
})
: null
if (i) {
this.IsArbitrationReading = i.IsArbitrationReading
this.IsOncologyReading = i.IsOncologyReading
@ -678,48 +914,56 @@ export default {
//
initPage() {
this.listLoading = true
getTrialEnrollmentWorkloadStats(this.listQuery).then((res) => {
this.listLoading = false
let resArray = res.Result.CurrentPageData
try {
resArray.forEach(v => {
if (v.DoctorCriterionStatusList && v.DoctorCriterionStatusList.length === 0) {
this.isConsistencyChange(v, null,true, false)
}
v.CriterionCategoryList.forEach(v1 => {
let o = v.DoctorCriterionStatusList.find(v2 => {
return v2.TrialReadingCriterionId === v1.TrialReadingCriterionId
})
if (o) {
this.$set(v1, 'Id', o.Id)
this.$set(v1, 'IsJoinAnalysis', o.IsJoinAnalysis)
getTrialEnrollmentWorkloadStats(this.listQuery)
.then((res) => {
this.listLoading = false
let resArray = res.Result.CurrentPageData
try {
resArray.forEach((v) => {
if (
v.DoctorCriterionStatusList &&
v.DoctorCriterionStatusList.length === 0
) {
this.isConsistencyChange(v, null, true, false)
}
v.CriterionCategoryList.forEach((v1) => {
let o = v.DoctorCriterionStatusList.find((v2) => {
return (
v2.TrialReadingCriterionId === v1.TrialReadingCriterionId
)
})
if (o) {
this.$set(v1, 'Id', o.Id)
this.$set(v1, 'IsJoinAnalysis', o.IsJoinAnalysis)
}
})
v.DoctorCriterionStatusList.push({})
})
v.DoctorCriterionStatusList.push({})
})
} catch (e) {
console.log(e)
}
this.list = resArray
if (this.list.length > 0) {
this.TrialReadingCriterionList = this.list[0].TrialReadingCriterionList
}
this.total = res.Result.TotalCount
this.$nextTick(() => {
// myTableref
if (this.$refs.myTable && this.$refs.myTable.doLayout) {
this.$refs.myTable.doLayout()
} catch (e) {
console.log(e)
}
this.list = resArray
if (this.list.length > 0) {
this.TrialReadingCriterionList =
this.list[0].TrialReadingCriterionList
}
this.total = res.Result.TotalCount
this.$nextTick(() => {
// myTableref
if (this.$refs.myTable && this.$refs.myTable.doLayout) {
this.$refs.myTable.doLayout()
}
})
})
.catch(() => {
this.listLoading = false
})
}).catch(() => {
this.listLoading = false
})
},
// Enroll
handleEnroll() {
this.$router.push({
path: `/trials/trials-panel/enrolled-reviewers/enroll?trialId=${this.listQuery.TrialId}&trialCode=${this.trialId}`
path: `/trials/trials-panel/enrolled-reviewers/enroll?trialId=${this.listQuery.TrialId}&trialCode=${this.trialId}`,
})
},
@ -731,12 +975,16 @@ export default {
//
handleUpdateReviewerStatus() {
this.$refs.updateStatusForm.validate(valid => {
this.$refs.updateStatusForm.validate((valid) => {
if (!valid) return
this.reviewerStatus_model.btnLoading = true
this.reviewerStatus_model.showClose = false
enrollBackOrOut(this.listQuery.TrialId, this.currentRow.DoctorId, this.updateStatusForm.Status)
.then(res => {
enrollBackOrOut(
this.listQuery.TrialId,
this.currentRow.DoctorId,
this.updateStatusForm.Status
)
.then((res) => {
this.$nextTick(() => {
this.$refs['updateStatusForm'].resetFields()
})
@ -765,14 +1013,19 @@ export default {
handleUpdateReadingType() {
this.readingType_model.btnLoading = true
this.readingType_model.showClose = false
updateReviewerReadingType(this.listQuery.TrialId, this.currentRow.DoctorId, this.readingType)
.then(res => {
updateReviewerReadingType(
this.listQuery.TrialId,
this.currentRow.DoctorId,
this.readingType
)
.then((res) => {
this.readingType_model.visible = false
this.readingType_model.btnLoading = false
this.readingType_model.showClose = true
this.initPage()
this.$message.success(this.$t('common:message:savedSuccessfully'))
}).catch(_ => {
})
.catch((_) => {
this.readingType_model.btnLoading = false
this.readingType_model.showClose = true
})
@ -791,17 +1044,15 @@ export default {
this.initPage()
},
handleTipCloseOK() {
zzSessionStorage.setItem(
'ReviewerName',
this.currentClickRowData.FirstName +
' ' +
this.currentClickRowData.LastName
' ' +
this.currentClickRowData.LastName
)
this.$router.push({
path: `/trials/trials-panel/enrolled-reviewers/stats?doctorId=${this.currentClickRowData.DoctorId}&trialId=${this.listQuery.TrialId}&trialCode=${this.trialId}&doctorName=${this.currentClickRowData.LastName}/${this.currentClickRowData.FirstName}`
path: `/trials/trials-panel/enrolled-reviewers/stats?doctorId=${this.currentClickRowData.DoctorId}&trialId=${this.listQuery.TrialId}&trialCode=${this.trialId}&doctorName=${this.currentClickRowData.LastName}/${this.currentClickRowData.FirstName}`,
})
},
@ -827,18 +1078,18 @@ export default {
}
})
return sums
}
}
},
},
}
</script>
<style lang="scss">
.enroll-list {
height: 100%;
.readingCategory_model{
.base-modal-body{
min-height: 150px;
padding-bottom: 30px !important;
}
.enroll-list {
height: 100%;
.readingCategory_model {
.base-modal-body {
min-height: 150px;
padding-bottom: 30px !important;
}
}
}
</style>

View File

@ -54,8 +54,8 @@
j.armEnum === 1
? $t('trials:adReview:table:viewR1')
: j.armEnum === 2
? $t('trials:adReview:table:viewR2')
: $fd('ArmEnum', j.armEnum)
? $t('trials:adReview:table:viewR2')
: $fd('ArmEnum', j.armEnum)
"
align="center"
prop=""
@ -70,54 +70,21 @@
width="150"
>
<template slot-scope="scope">
<div
v-if="
scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i]
.QuestionType === 1
"
>
<span
v-if="
scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i]
.DictionaryCode
"
>{{
$fd(
scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[
i
].DictionaryCode,
parseInt(
scope.row.VisitTaskInfoList[j.index]
.JudgeQuestionList[i].Answer
)
)
}}</span
>
<span v-else>{{
scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i]
.Answer
}}</span>
<div v-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].QuestionType === 1">
<span v-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].DictionaryCode">
{{ $fd(scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].DictionaryCode,parseInt(scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer))
}}
</span>
<span v-else>
{{ scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer }}
</span>
<span v-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Unit && !isNaN(parseFloat(scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer))">{{ $fd('ValueUnit', parseInt(scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Unit)) }}</span>
</div>
<div
v-else-if="
scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i]
.QuestionType === 2
"
>
<div
v-if="
scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i]
.Answer
"
>
<span>{{
$fd(
"YesOrNo",
scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[
i
].Answer
)
}}</span>
<div v-else-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].QuestionType === 2">
<div v-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer">
<span>
{{ $fd("YesOrNo",scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer) }}
</span>
<!-- 查看详情 -->
<el-button
type="text"
@ -144,10 +111,7 @@
</div>
</div>
<div v-else>
{{
scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i]
.Answer
}}
{{ scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer }}
</div>
</template>
</el-table-column>
@ -321,7 +285,7 @@
alt=""
crossorigin="anonymous"
style="max-width: 100%; max-height: 100%"
/>
>
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-preview"
@ -435,88 +399,87 @@ import {
// uploadJudgeTaskImage,
saveJudgeVisitTaskResult,
submitJudgeVisitTaskResult,
getReadingPastResultList,
} from "@/api/trials";
import { getAutoCutNextTask } from "@/api/user";
import { setSkipReadingCache } from "@/api/reading";
import const_ from "@/const/sign-code";
import { getToken } from "@/utils/auth";
import SignForm from "@/views/trials/components/newSignForm";
import DicomEvent from "@/views/trials/trials-panel/reading/dicoms/components/DicomEvent";
getReadingPastResultList
} from '@/api/trials'
import { getAutoCutNextTask } from '@/api/user'
import { setSkipReadingCache } from '@/api/reading'
import const_ from '@/const/sign-code'
import { getToken } from '@/utils/auth'
import SignForm from '@/views/trials/components/newSignForm'
import DicomEvent from '@/views/trials/trials-panel/reading/dicoms/components/DicomEvent'
// import store from '@/store'
import { changeURLStatic } from "@/utils/history.js";
import Viewer from "v-viewer";
import { changeURLStatic } from '@/utils/history.js'
export default {
name: "AdReview",
name: 'AdReview',
components: { SignForm },
props: {
trialId: {
type: String,
required: true,
required: true
},
subjectId: {
type: String,
required: true,
required: true
},
visitTaskId: {
type: String,
required: true,
required: true
},
readingCategory: {
type: Number,
required: true,
required: true
},
subjectCode: {
type: String,
required: true,
required: true
},
taskBlindName: {
type: String,
required: true,
required: true
},
isReadingShowSubjectInfo: {
type: Boolean,
required: true,
required: true
},
isReadingShowPreviousResults: {
type: Boolean,
required: true,
required: true
},
isExistsClinicalData: {
type: Boolean,
required: true,
},
required: true
}
},
data() {
return {
adInfo: {},
judgeQuestion: [],
adForm: {
visitTaskId: "",
judgeResultTaskId: "",
judgeResultRemark: "",
visitTaskId: '',
judgeResultTaskId: '',
judgeResultRemark: '',
// judgeResultImagePath: ''
judgeResultImagePathList: [],
judgeResultImagePathList: []
},
currentUser: zzSessionStorage.getItem("userName"),
currentUser: zzSessionStorage.getItem('userName'),
signVisible: false,
signCode: null,
accept: ".png,.jpg,.jpeg",
accept: '.png,.jpg,.jpeg',
imgVisible: false,
imageUrl: "",
imageUrl: '',
uploadDisabled: false,
fileList: [],
loading: false,
visitTaskArmList: [],
priorADList: [],
priorLoading: false,
judgeResultArmEnum: "",
judgeResultArmEnum: '',
criterionType: null,
openWindow: null,
isFixed: false,
images: [],
remark: "",
};
remark: ''
}
},
// watch: {
// visitTaskId: {
@ -532,261 +495,260 @@ export default {
// }
// },
mounted() {
this.initializeViewer();
this.criterionType = parseInt(this.$route.query.criterionType);
this.getAdInfo();
this.criterionType = parseInt(this.$route.query.criterionType)
this.getAdInfo()
if (this.isReadingShowPreviousResults) {
this.getPriorAdList();
this.getPriorAdList()
}
DicomEvent.$on("resetOpenWindow", () => {
DicomEvent.$on('resetOpenWindow', () => {
if (this.openWindow) {
this.openWindow.close();
this.openWindow.close()
}
});
})
},
beforeDestroy() {
DicomEvent.$off("resetOpenWindow");
DicomEvent.$off('resetOpenWindow')
if (this.openWindow) {
this.openWindow.close();
this.openWindow.close()
}
},
methods: {
async getAdInfo() {
this.loading = true;
this.loading = true
try {
const res = await getJudgeReadingInfo({
visitTaskId: this.visitTaskId,
});
visitTaskId: this.visitTaskId
})
if (res.IsSuccess) {
var judgeQS = [];
var judgeQS = []
if (res.Result.VisitInfoList.length > 0) {
res.Result.VisitInfoList[0].VisitTaskInfoList.map((v, index) => {
var qsObj = {
armEnum: v.ArmEnum,
judgeQuestionList: [],
index: index,
};
index: index
}
v.JudgeQuestionList.map((q) => {
if (q.QuestionType === 1) {
qsObj.judgeQuestionList.push(q.QuestionName);
qsObj.judgeQuestionList.push(q.QuestionName)
} else if (q.QuestionType === 3 && this.criterionType === 10) {
qsObj.judgeQuestionList.push(
this.$t("trials:globalReview:table:visitRemark")
);
this.$t('trials:globalReview:table:visitRemark')
)
} else {
qsObj.judgeQuestionList.push(
this.$fd("JudgeReadingQuestionType", q.QuestionType)
);
this.$fd('JudgeReadingQuestionType', q.QuestionType)
)
}
});
judgeQS.push(qsObj);
});
})
judgeQS.push(qsObj)
})
}
this.judgeQuestion = judgeQS;
this.judgeQuestion = judgeQS
this.isFixed =
this.judgeQuestion.length > 0 &&
this.judgeQuestion[0].judgeQuestionList.length > 4;
this.adInfo = res.Result;
this.adForm.judgeResultTaskId = res.Result.JudgeResultTaskId;
this.judgeQuestion[0].judgeQuestionList.length > 4
this.adInfo = res.Result
this.adForm.judgeResultTaskId = res.Result.JudgeResultTaskId
this.fileList = [];
this.fileList = []
if (res.Result.JudgeResultImagePathList) {
res.Result.JudgeResultImagePathList.map((url) => {
if (url) {
this.fileList.push({ name: "", url: url });
this.fileList.push({ name: '', url: url })
}
});
})
this.adForm.judgeResultImagePathList =
res.Result.JudgeResultImagePathList;
res.Result.JudgeResultImagePathList
}
this.visitTaskArmList = res.Result.VisitTaskArmList;
this.visitTaskArmList = res.Result.VisitTaskArmList
var i = this.visitTaskArmList.findIndex(
(i) => i.VisitTaskId === this.adForm.judgeResultTaskId
);
)
if (i > -1) {
// R1
this.judgeResultArmEnum = this.visitTaskArmList[i].ArmEnum;
var msg = "";
this.judgeResultArmEnum = this.visitTaskArmList[i].ArmEnum
var msg = ''
if (this.judgeResultArmEnum === 1) {
msg = this.$t("trials:adReview:title:msg1");
msg = this.$t('trials:adReview:title:msg1')
} else if (this.judgeResultArmEnum === 2) {
msg = this.$t("trials:adReview:title:msg3");
msg = this.$t('trials:adReview:title:msg3')
}
this.remark = msg;
this.adForm.judgeResultRemark = res.Result.JudgeResultRemark;
this.remark = msg
this.adForm.judgeResultRemark = res.Result.JudgeResultRemark
}
}
this.loading = false;
this.loading = false
} catch (e) {
this.loading = false;
this.loading = false
}
},
async getPriorAdList() {
this.priorLoading = true;
this.priorLoading = true
try {
const res = await getReadingPastResultList({
visitTaskId: this.visitTaskId,
});
visitTaskId: this.visitTaskId
})
if (res.IsSuccess) {
this.priorADList = res.Result;
this.priorADList = res.Result
}
this.priorLoading = false;
this.priorLoading = false
} catch (e) {
this.priorLoading = false;
this.priorLoading = false
}
},
handleVisitTaskArmChange(v) {
var i = this.visitTaskArmList.findIndex((i) => i.VisitTaskId === v);
var i = this.visitTaskArmList.findIndex((i) => i.VisitTaskId === v)
if (i > -1) {
// R1
this.judgeResultArmEnum = this.visitTaskArmList[i].ArmEnum;
var msg = "";
this.judgeResultArmEnum = this.visitTaskArmList[i].ArmEnum
var msg = ''
if (this.judgeResultArmEnum === 1) {
msg = this.$t("trials:adReview:title:msg1");
msg = this.$t('trials:adReview:title:msg1')
} else {
msg = this.$t("trials:adReview:title:msg3");
msg = this.$t('trials:adReview:title:msg3')
}
// this.adForm.judgeResultRemark = `${this.$fd('ArmEnum', this.judgeResultArmEnum)}`
this.remark = msg;
this.adForm.judgeResultRemark = "";
this.remark = msg
this.adForm.judgeResultRemark = ''
} else {
this.judgeResultArmEnum = "";
this.remark = "";
this.adForm.judgeResultRemark = "";
this.judgeResultArmEnum = ''
this.remark = ''
this.adForm.judgeResultRemark = ''
}
},
previewCD() {
var token = getToken();
var token = getToken()
const routeData = this.$router.resolve({
path: `/clinicalData?subjectId=${this.subjectId}&trialId=${this.trialId}&visitTaskId=${this.visitTaskId}&TokenKey=${token}`,
});
window.open(routeData.href, "_blank");
path: `/clinicalData?subjectId=${this.subjectId}&trialId=${this.trialId}&visitTaskId=${this.visitTaskId}&TokenKey=${token}`
})
window.open(routeData.href, '_blank')
},
async handleSave() {
const valid = await this.$refs["adForm"].validate();
if (!valid) return;
this.loading = true;
var paths = [];
const valid = await this.$refs['adForm'].validate()
if (!valid) return
this.loading = true
var paths = []
this.fileList.map((file) => {
if (file.url) {
paths.push(file.url);
paths.push(file.url)
}
});
this.adForm.judgeResultImagePathList = paths;
this.adForm.visitTaskId = this.visitTaskId;
})
this.adForm.judgeResultImagePathList = paths
this.adForm.visitTaskId = this.visitTaskId
try {
await saveJudgeVisitTaskResult(this.adForm);
this.$message.success(this.$t("common:message:savedSuccessfully"));
this.loading = false;
await saveJudgeVisitTaskResult(this.adForm)
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.loading = false
} catch (e) {
this.loading = false;
this.loading = false
}
},
async handleSubmit() {
const valid = await this.$refs["adForm"].validate();
if (!valid) return;
const { ImageAssessmentReportConfirmation } = const_.processSignature;
this.signCode = ImageAssessmentReportConfirmation;
this.signVisible = true;
const valid = await this.$refs['adForm'].validate()
if (!valid) return
const { ImageAssessmentReportConfirmation } = const_.processSignature
this.signCode = ImageAssessmentReportConfirmation
this.signVisible = true
},
//
closeSignDialog(isSign, signInfo) {
if (isSign) {
this.signConfirm(signInfo);
this.signConfirm(signInfo)
} else {
this.signVisible = false;
this.signVisible = false
}
},
//
async signConfirm(signInfo) {
this.loading = true;
var paths = [];
this.loading = true
var paths = []
this.fileList.map((file) => {
paths.push(file.url);
});
paths.push(file.url)
})
var params = {
data: {
visitTaskId: this.visitTaskId,
judgeResultTaskId: this.adForm.judgeResultTaskId,
judgeResultRemark: this.adForm.judgeResultRemark,
judgeResultImagePathList: paths,
judgeResultImagePathList: paths
},
signInfo: signInfo,
};
signInfo: signInfo
}
try {
const res = await submitJudgeVisitTaskResult(params);
const res = await submitJudgeVisitTaskResult(params)
if (res.IsSuccess) {
this.$message.success(this.$t("common:message:savedSuccessfully"));
this.isEdit = false;
this.$refs["signForm"].btnLoading = false;
this.signVisible = false;
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.isEdit = false
this.$refs['signForm'].btnLoading = false
this.signVisible = false
// window.location.reload()
// window.opener.postMessage('refreshTaskList', window.location)
//
this.adInfo.ReadingTaskState = 2;
const res = await getAutoCutNextTask();
var isAutoTask = res.Result.AutoCutNextTask;
this.adInfo.ReadingTaskState = 2
const res = await getAutoCutNextTask()
var isAutoTask = res.Result.AutoCutNextTask
if (isAutoTask) {
// store.dispatch('reading/resetVisitTasks')
window.location.reload();
window.location.reload()
} else {
// ''
const confirm = await this.$confirm(
this.$t("trials:adReview:title:msg2"),
this.$t('trials:adReview:title:msg2'),
{
type: "warning",
distinguishCancelAndClose: true,
type: 'warning',
distinguishCancelAndClose: true
}
);
if (confirm === "confirm") {
)
if (confirm === 'confirm') {
// store.dispatch('reading/resetVisitTasks')
// DicomEvent.$emit('getNextTask')
window.location.reload();
window.location.reload()
} else {
changeURLStatic("visitTaskId", this.visitTaskId);
changeURLStatic('visitTaskId', this.visitTaskId)
}
}
window.opener.postMessage("refreshTaskList", window.location);
window.opener.postMessage('refreshTaskList', window.location)
}
this.loading = false;
this.loading = false
} catch (e) {
this.loading = false;
this.$refs["signForm"].btnLoading = false;
this.loading = false
this.$refs['signForm'].btnLoading = false
}
},
handleViewDetail(visitTaskId) {
if (this.openWindow) {
this.openWindow.close();
this.openWindow.close()
}
var token = getToken();
var criterionType = parseInt(localStorage.getItem("CriterionType"));
var readingTool = this.$router.currentRoute.query.readingTool;
readingTool = isNaN(parseInt(readingTool)) ? null : parseInt(readingTool);
var token = getToken()
var criterionType = parseInt(localStorage.getItem('CriterionType'))
var readingTool = this.$router.currentRoute.query.readingTool
readingTool = isNaN(parseInt(readingTool)) ? null : parseInt(readingTool)
var isReadingTaskViewInOrder =
this.$router.currentRoute.query.isReadingTaskViewInOrder;
this.$router.currentRoute.query.isReadingTaskViewInOrder
var trialReadingCriterionId =
this.$router.currentRoute.query.TrialReadingCriterionId;
this.$router.currentRoute.query.TrialReadingCriterionId
var path = "";
var path = ''
if (readingTool === 0) {
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${
this.trialId
}&subjectCode=${this.subjectCode}&subjectId=${
this.subjectId
}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`;
}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
} else {
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${
this.trialId
}&subjectCode=${this.subjectCode}&subjectId=${
this.subjectId
}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`;
}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
}
var routeData = this.$router.resolve({ path });
this.openWindow = window.open(routeData.href, "_blank");
var routeData = this.$router.resolve({ path })
this.openWindow = window.open(routeData.href, '_blank')
},
handleView(row, armEnum) {
if (this.openWindow) {
this.openWindow.close();
this.openWindow.close()
}
// var token = getToken()
// var task = row.VisitTaskInfoList.find(item => item.ArmEnum === armEnum)
@ -794,17 +756,17 @@ export default {
// path: `/readingPage?trialId=${this.trialId}&visitTaskId=${task.VisitTaskId}&TokenKey=${token}&isReadingShowPreviousResults=false`
// })
// window.open(routeData.href, '_blank')
var token = getToken();
var task = row.VisitTaskInfoList.find((item) => item.ArmEnum === armEnum);
var criterionType = this.$router.currentRoute.query.criterionType;
var readingTool = this.$router.currentRoute.query.readingTool;
readingTool = isNaN(parseInt(readingTool)) ? null : parseInt(readingTool);
var token = getToken()
var task = row.VisitTaskInfoList.find((item) => item.ArmEnum === armEnum)
var criterionType = this.$router.currentRoute.query.criterionType
var readingTool = this.$router.currentRoute.query.readingTool
readingTool = isNaN(parseInt(readingTool)) ? null : parseInt(readingTool)
var isReadingTaskViewInOrder =
this.$router.currentRoute.query.isReadingTaskViewInOrder;
this.$router.currentRoute.query.isReadingTaskViewInOrder
var trialReadingCriterionId =
this.$router.currentRoute.query.TrialReadingCriterionId;
this.$router.currentRoute.query.TrialReadingCriterionId
var path = "";
var path = ''
if (readingTool === 0) {
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${
this.trialId
@ -812,7 +774,7 @@ export default {
this.subjectId
}&visitTaskId=${
task.VisitTaskId
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`;
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
} else {
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${
this.trialId
@ -820,26 +782,26 @@ export default {
this.subjectId
}&visitTaskId=${
task.VisitTaskId
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`;
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
}
var routeData = this.$router.resolve({ path });
this.openWindow = window.open(routeData.href, "_blank");
var routeData = this.$router.resolve({ path })
this.openWindow = window.open(routeData.href, '_blank')
},
handleViewGl(row, armEnum) {
if (this.openWindow) {
this.openWindow.close();
this.openWindow.close()
}
var token = getToken();
var task = row.VisitTaskInfoList.find((item) => item.ArmEnum === armEnum);
var criterionType = this.$router.currentRoute.query.criterionType;
var readingTool = this.$router.currentRoute.query.readingTool;
readingTool = isNaN(parseInt(readingTool)) ? null : parseInt(readingTool);
var token = getToken()
var task = row.VisitTaskInfoList.find((item) => item.ArmEnum === armEnum)
var criterionType = this.$router.currentRoute.query.criterionType
var readingTool = this.$router.currentRoute.query.readingTool
readingTool = isNaN(parseInt(readingTool)) ? null : parseInt(readingTool)
var isReadingTaskViewInOrder =
this.$router.currentRoute.query.isReadingTaskViewInOrder;
this.$router.currentRoute.query.isReadingTaskViewInOrder
var trialReadingCriterionId =
this.$router.currentRoute.query.TrialReadingCriterionId;
this.$router.currentRoute.query.TrialReadingCriterionId
var path = "";
var path = ''
if (readingTool === 0) {
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${
this.trialId
@ -847,7 +809,7 @@ export default {
this.subjectId
}&visitTaskId=${
task.GlobalVisitTaskId
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`;
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
} else {
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${
this.trialId
@ -855,10 +817,10 @@ export default {
this.subjectId
}&visitTaskId=${
task.GlobalVisitTaskId
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`;
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
}
var routeData = this.$router.resolve({ path });
this.openWindow = window.open(routeData.href, "_blank");
var routeData = this.$router.resolve({ path })
this.openWindow = window.open(routeData.href, '_blank')
},
// uploadScreenshot(param) {
// this.loading = true
@ -876,105 +838,93 @@ export default {
// })
// },
async uploadScreenshot(param) {
this.loading = true;
this.uploadDisabled = false;
var trialId = this.$route.query.trialId;
var file = await this.fileToBlob(param.file);
this.loading = true
this.uploadDisabled = false
var trialId = this.$route.query.trialId
var file = await this.fileToBlob(param.file)
const res = await this.OSSclient.put(
`/${trialId}/Read/${this.subjectId}/visit/${param.file.name}`,
file
);
console.log(res);
)
console.log(res)
this.fileList.push({
name: param.file.name,
url: this.$getObjectName(res.url),
});
this.loading = false;
this.uploadDisabled = true;
url: this.$getObjectName(res.url)
})
this.loading = false
this.uploadDisabled = true
},
handleBeforeUpload(file) {
//
if (this.checkFileSuffix(file.name)) {
return true;
return true
} else {
const msg = this.$t("trials:adReview:title:msg4").replace(
"xxx",
const msg = this.$t('trials:adReview:title:msg4').replace(
'xxx',
this.accept
);
this.$alert(msg);
return false;
)
this.$alert(msg)
return false
}
},
checkFileSuffix(fileName) {
var index = fileName.lastIndexOf(".");
var suffix = fileName.substring(index + 1, fileName.length);
var index = fileName.lastIndexOf('.')
var suffix = fileName.substring(index + 1, fileName.length)
if (
this.accept.toLocaleLowerCase().search(suffix.toLocaleLowerCase()) ===
-1
) {
return false;
return false
} else {
return true;
return true
}
},
//
removeImage() {
this.imageUrl = "";
this.fileList = [];
this.adForm.judgeResultImagePath = "";
this.imageUrl = ''
this.fileList = []
this.adForm.judgeResultImagePath = ''
},
//
handlePictureCardPreview(file) {
this.images = this.fileList.map(
(f) => this.OSSclientConfig.basePath + f.url
);
)
// this.imageUrl = this.OSSclientConfig.basePath + file.url
this.$refs[file.url].$viewer.show();
this.$refs[file.url].$viewer.show()
},
//
handleRemove(file, fileList) {
var idx = this.fileList.findIndex((i) => i.url === file.url);
if (idx === -1) return;
this.fileList.splice(idx, 1);
var idx = this.fileList.findIndex((i) => i.url === file.url)
if (idx === -1) return
this.fileList.splice(idx, 1)
},
async skipTask() {
try {
//
const confirm = await this.$confirm(
this.$t("trials:readingReport:message:skipConfirm"),
this.$t('trials:readingReport:message:skipConfirm'),
{
type: "warning",
distinguishCancelAndClose: true,
type: 'warning',
distinguishCancelAndClose: true
}
);
if (confirm !== "confirm") return;
this.loading = true;
)
if (confirm !== 'confirm') return
this.loading = true
const res = await setSkipReadingCache({
visitTaskId: this.visitTaskId,
});
this.loading = false;
visitTaskId: this.visitTaskId
})
this.loading = false
if (res.IsSuccess) {
window.location.reload();
window.location.reload()
}
} catch (e) {
this.loading = false;
console.log(e);
this.loading = false
console.log(e)
}
},
initializeViewer() {
Viewer.setDefaults({
toolbar: {
zoomIn: true,
zoomOut: true,
rotateLeft: true,
rotateRight: true,
flipHorizontal: true,
flipVertical: true,
},
});
},
},
};
}
}
}
</script>
<style lang="scss" scoped>
.adReview_wrapper {

View File

@ -41,7 +41,7 @@
</div>
<div class="right-wrapper">
<div v-if="selected.type===1" class="right-content">
<iframe :src="`/static/pdfjs/web/viewer.html?file=${OSSclientConfig.basePath}${selected.filePath}?userName=${currentUser}`" width="100%" height="100%" frameborder="0" />
<iframe :src="`/static/pdfjs/web/viewer.html?file=${OSSclientConfig.basePath}${selected.filePath}?userName=${currentUser}&COMPANY=${COMPANY}`" width="100%" height="100%" frameborder="0" />
</div>
<div v-else-if="selected.type===2" class="right-content" style="padding: 10px">
<clinicalDataQuestions
@ -216,7 +216,8 @@ export default {
otherInfo: {},
moment,
loading: false,
currentUser: zzSessionStorage.getItem('userName')
currentUser: zzSessionStorage.getItem('userName'),
COMPANY:process.env.VUE_APP_COMPANY_NAME
}
},
mounted() {

View File

@ -1039,7 +1039,7 @@ export default {
loadImageStack(dicomSeries) {
return new Promise(resolve => {
this.isCurrentTask = dicomSeries.isCurrentTask
this.isBaseline = dicomSeries.isBaseline
this.isBaseline = dicomSeries.isBaseLineTask
this.readingTaskState = dicomSeries.readingTaskState
if (this.isCurrentTask && this.readingTaskState < 2) {
this.activeTool = 1
@ -1088,16 +1088,16 @@ export default {
var frame = imageInfo.frame
this.stack.frame = this.stack.isExistMutiFrames ? parseInt(frame) : null
this.stack.instanceId = instanceId
const element = this.$refs.canvas
cornerstone.enable(element)
element.tabIndex = 0
element.focus()
const ToolStateManager = cornerstoneTools.globalImageIdSpecificToolStateManager
ToolStateManager.clearImageIdToolState(dicomSeries.imageIds)
this.toggleClipPlay(false)
this.toolState.viewportInvert = false
this.toolState.dicomInfoVisible = false
const element = this.$refs.canvas
cornerstone.enable(element)
element.tabIndex = 0
element.focus()
var scope = this
// var p = parseInt(new Date().getTime())
// requestPoolManager.loadAndCacheImagePlus(this.stack.imageIds[this.stack.currentImageIdIndex], this.stack.seriesId, p*100).then(image=>{
@ -1654,7 +1654,7 @@ export default {
resetWwwc() {
this.toolState.viewportInvert = false
var viewport = cornerstone.getViewport(this.canvas)
viewport.invert = false
// viewport.invert = false
var image = cornerstone.getImage(this.canvas)
viewport.voi.windowWidth = image.windowWidth
viewport.voi.windowCenter = image.windowCenter

View File

@ -352,7 +352,44 @@
<div class="text">{{ $t('trials:reading:button:reset') }}</div>
</div>
</el-tooltip>
<el-tooltip
v-if="trialCriterion.ImageUploadEnum > 0 && currentReadingTaskState < 2"
v-hasPermi="['role:ir']"
class="item"
effect="dark"
:content="$t('trials:reading:button:upload')"
placement="bottom"
>
<div class="tool-wrapper">
<div class="icon" @click.prevent="openUploadImage('upload')">
<i class="el-icon-upload2 svg-icon" />
</div>
<div class="text">{{ $t('trials:reading:button:upload') }}</div>
</div>
</el-tooltip>
<el-tooltip
v-if="trialCriterion.ImageDownloadEnum > 0"
v-hasPermi="[
'role:ir',
'role:mim',
'role:mc',
'role:pm',
'role:apm',
'role:ea',
'role:qa',
]"
class="item"
effect="dark"
:content="$t('trials:reading:button:download')"
placement="bottom"
>
<div class="tool-wrapper">
<div class="icon" @click.prevent="openUploadImage('download')">
<i class="el-icon-download svg-icon" />
</div>
<div class="text">{{ $t('trials:reading:button:download') }}</div>
</div>
</el-tooltip>
<div style="margin-left:auto;">
<div style="padding:5px">
<!-- 手册 -->
@ -397,49 +434,88 @@
</div>
</div>
<div ref="form-container" class="form-container">
<!-- 激活canvas测量数据 -->
<div class="form-wrapper">
<RecistBMQuestionList
v-if="CriterionType === 17"
ref="measurementList"
:question-form-change-state="questionFormChangeState"
:question-form-change-num="questionFormChangeNum"
:is-show="isShow"
:is-reading-show-subject-info="isReadingShowSubjectInfo"
/>
<IRecistQuestionList
v-else-if="CriterionType === 3"
ref="measurementList"
:question-form-change-state="questionFormChangeState"
:question-form-change-num="questionFormChangeNum"
:is-show="isShow"
:is-reading-show-subject-info="isReadingShowSubjectInfo"
/>
<PCWGQuestionList
v-else-if="CriterionType === 10"
ref="measurementList"
:question-form-change-state="questionFormChangeState"
:question-form-change-num="questionFormChangeNum"
:is-show="isShow"
:is-reading-show-subject-info="isReadingShowSubjectInfo"
/>
<LuganoQuestionList
v-else-if="CriterionType === 2"
ref="measurementList"
:question-form-change-state="questionFormChangeState"
:question-form-change-num="questionFormChangeNum"
:is-show="isShow"
:is-reading-show-subject-info="isReadingShowSubjectInfo"
/>
<RecistBMQuestionList
v-if="CriterionType === 17"
ref="measurementList"
:question-form-change-state="questionFormChangeState"
:question-form-change-num="questionFormChangeNum"
:is-show="isShow"
:is-reading-show-subject-info="isReadingShowSubjectInfo"
/>
<IRecistQuestionList
v-else-if="CriterionType === 3"
ref="measurementList"
:question-form-change-state="questionFormChangeState"
:question-form-change-num="questionFormChangeNum"
:is-show="isShow"
:is-reading-show-subject-info="isReadingShowSubjectInfo"
/>
<PCWGQuestionList
v-else-if="CriterionType === 10"
ref="measurementList"
:question-form-change-state="questionFormChangeState"
:question-form-change-num="questionFormChangeNum"
:is-show="isShow"
:is-reading-show-subject-info="isReadingShowSubjectInfo"
/>
<LuganoQuestionList
v-else-if="CriterionType === 2"
ref="measurementList"
:question-form-change-state="questionFormChangeState"
:question-form-change-num="questionFormChangeNum"
:is-show="isShow"
:is-reading-show-subject-info="isReadingShowSubjectInfo"
/>
<RecistQuestionList
v-else-if="CriterionType === 1"
ref="measurementList"
:question-form-change-state="questionFormChangeState"
:question-form-change-num="questionFormChangeNum"
:is-show="isShow"
:is-reading-show-subject-info="isReadingShowSubjectInfo"
/>
<IVUSList
v-else-if="CriterionType === 19"
ref="measurementList"
:question-form-change-state="questionFormChangeState"
:question-form-change-num="questionFormChangeNum"
:is-show="isShow"
:is-reading-show-subject-info="isReadingShowSubjectInfo"
/>
<OCTList
v-else-if="CriterionType === 20"
ref="measurementList"
:question-form-change-state="questionFormChangeState"
:question-form-change-num="questionFormChangeNum"
:is-show="isShow"
:is-reading-show-subject-info="isReadingShowSubjectInfo"
/>
<h2 v-else style="color:#ddd">
Developing...
</h2>
</div>
<div v-if="iseCRFShowInDicomReading && currentReadingTaskState < 2" class="form-footer">
<el-button
type="primary"
size="small"
@click="skipTask"
>
<!-- 跳过 -->
{{ $t('trials:readingReport:button:skip') }}
</el-button>
<el-button
type="primary"
size="small"
@click="submit"
>
<!-- 提交 -->
{{ $t('common:button:submit') }}
</el-button>
</div>
<RecistQuestionList
v-else-if="CriterionType !== 17"
ref="measurementList"
:question-form-change-state="questionFormChangeState"
:question-form-change-num="questionFormChangeNum"
:is-show="isShow"
:is-reading-show-subject-info="isReadingShowSubjectInfo"
/>
</div>
</div>
@ -655,6 +731,37 @@
</el-form-item>
</el-form>
</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"
/>
<download-dicom-and-nonedicom
v-if="downloadImageVisible"
:subject-id="uploadSubjectId"
:subject-code="uploadSubjectCode"
:criterion="uploadTrialCriterion"
:task-id="taskId"
:visible.sync="downloadImageVisible"
/>
<!-- 签名框 -->
<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;user-select: text !important;">{{ `(${$t('common:label:sign')}${ currentUser })` }}</span>
</div>
<SignForm ref="signForm" :sign-code-enum="signCode" @closeDialog="closeSignDialog" />
</el-dialog>
</div>
</template>
<script>
@ -677,6 +784,8 @@ import RecistBMQuestionList from './RecistBM/QuestionList'
import IRecistQuestionList from './IRecist/QuestionList'
import PCWGQuestionList from './PCWG/QuestionList'
import LuganoQuestionList from './Lugano/QuestionList'
import IVUSList from './IVUS/QuestionList'
import OCTList from './OCT/QuestionList'
import CustomWwwcForm from './CustomWwwcForm'
import Manuals from './Manuals'
import Hotkeys from './Hotkeys'
@ -684,6 +793,14 @@ import WL from './WL'
import Others from './Others'
import DicomEvent from './DicomEvent'
import html2canvas from 'html2canvas'
import uploadDicomAndNonedicom from '@/components/uploadDicomAndNonedicom'
import downloadDicomAndNonedicom from '@/components/downloadDicomAndNonedicom'
import SignForm from '@/views/trials/components/newSignForm'
import { getCriterionReadingInfo, verifyVisitTaskQuestions, submitDicomVisitTask } from '@/api/trials'
import { setSkipReadingCache } from '@/api/reading'
import { getAutoCutNextTask } from '@/api/user'
import const_ from '@/const/sign-code'
import { changeURLStatic } from '@/utils/history.js'
export default {
name: 'DicomViewer',
components: {
@ -698,7 +815,13 @@ export default {
PCWGQuestionList,
RecistBMQuestionList,
IRecistQuestionList,
LuganoQuestionList },
LuganoQuestionList,
IVUSList,
OCTList,
'download-dicom-and-nonedicom': downloadDicomAndNonedicom,
'upload-dicom-and-nonedicom': uploadDicomAndNonedicom,
SignForm
},
props: {
isShow: {
type: Boolean,
@ -724,6 +847,10 @@ export default {
type: Boolean,
required: true
},
iseCRFShowInDicomReading: {
type: Boolean,
required: true
},
questionFormChangeState: {
type: Boolean,
default() {
@ -839,7 +966,19 @@ export default {
activeCanvasWC: null,
activeTaskInfo: {},
clipPlaying: false,
fps: 15
fps: 15,
trialCriterion: {},
//
downloadImageVisible: false,
uploadImageVisible: false,
uploadSubjectId: null,
uploadSubjectCode: null,
uploadTrialCriterion: {},
uploadStatus: 'upload',
taskId: '',
signVisible: false,
signCode: null,
currentUser: zzSessionStorage.getItem('userName')
}
},
@ -912,6 +1051,7 @@ export default {
mounted() {
this.getHotKeys()
this.getWwcTpl()
this.getTrialCriterion()
// cornerstone.imageCache.setMaximumSizeBytes(0)
// const maximumSizeInBytes = 1024 * 1024 * 1024 // 1 GB
@ -923,6 +1063,10 @@ export default {
this.measuredTools = [{ toolName: 'ArrowAnnotate', text: this.$t('trials:reading:button:arrowAnnotate'), icon: 'arrow', isDisabled: false, disabledReason: '' }, { toolName: 'RectangleRoi', text: this.$t('trials:reading:button:rectangle'), icon: 'rectangle', isDisabled: false, disabledReason: '' }]
} else if (this.CriterionType === 17) {
this.measuredTools = [{ toolName: 'Length', text: this.$t('trials:reading:button:length'), icon: 'length', isDisabled: false, disabledReason: '' }, { toolName: 'RectangleRoi', text: this.$t('trials:reading:button:rectangle'), icon: 'rectangle', isDisabled: false, disabledReason: '' }, { toolName: 'ArrowAnnotate', text: this.$t('trials:reading:button:arrowAnnotate'), icon: 'arrow', isDisabled: false, disabledReason: '' }]
} else if (this.CriterionType === 19) {
this.measuredTools = []
} else if (this.CriterionType === 20) {
this.measuredTools = []
}
this.rotateList[0] = '1'
this.colorList[0] = ''
@ -1062,6 +1206,27 @@ export default {
})
},
methods: {
getTrialCriterion() {
getCriterionReadingInfo({
TrialId: this.trialId || this.$route.query.trialId,
TrialReadingCriterionId: this.trialReadingCriterionId || this.$route.query.TrialReadingCriterionId
})
.then((res) => {
this.trialCriterion = res.Result
})
.catch(() => {})
},
openUploadImage(status) {
const idx = this.visitTaskList.findIndex(i => i.IsCurrentTask)
if (idx > -1) {
this.taskId = this.visitTaskList[idx].VisitTaskId
}
this.uploadSubjectCode = this.$route.query.subjectCode||localStorage.getItem("subjectCode")
this.uploadSubjectId = this.$route.query.subjectId||localStorage.getItem("subjectId")
this.uploadTrialCriterion = this.trialCriterion
this.uploadStatus = status
this[`${status}ImageVisible`] = true
},
async getWwcTpl() {
// const loading = this.$loading({ fullscreen: true })
try {
@ -2070,17 +2235,119 @@ export default {
this.isFullscreen = false
this.manualsDialog.visible = true
},
async skipTask() {
//
const confirm = await this.$confirm(
this.$t('trials:readingReport:message:skipConfirm'),
{
type: 'warning',
distinguishCancelAndClose: true
}
)
if (confirm !== 'confirm') return
// const loading = this.$loading({ fullscreen: true })
try {
const idx = this.visitTaskList.findIndex(i => i.IsCurrentTask)
if (idx > -1) {
var visitTaskId = this.visitTaskList[idx].VisitTaskId
const res = await setSkipReadingCache({ visitTaskId: visitTaskId })
if (res.IsSuccess) {
window.location.reload()
}
}
// loading.close()
} catch (e) {
// loading.close()
console.log(e)
}
},
async submit() {
const loading = this.$loading({ fullscreen: true })
try {
const idx = this.visitTaskList.findIndex(i => i.IsCurrentTask)
if (idx > -1) {
var visitTaskId = this.visitTaskList[idx].VisitTaskId
this.taskId = visitTaskId
await verifyVisitTaskQuestions({ visitTaskId: visitTaskId })
const { ImageAssessmentReportConfirmation } = const_.processSignature
this.signCode = ImageAssessmentReportConfirmation
this.signVisible = true
}
loading.close()
} catch (e) {
loading.close()
}
},
//
closeSignDialog(isSign, signInfo) {
if (isSign) {
this.signConfirm(signInfo)
} else {
this.signVisible = false
}
},
//
async signConfirm(signInfo) {
this.loading = true
try {
var params = {
data: {
visitTaskId: this.taskId
},
signInfo: signInfo
}
const res = await submitDicomVisitTask(params)
if (res.IsSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
if (this.$refs['signForm']) {
this.$refs['signForm'].btnLoading = false
}
this.signVisible = false
//
this.readingTaskState = 2
await store.dispatch('reading/setVisitTaskReadingTaskState', { visitTaskId: this.visitTaskId, readingTaskState: 2 })
await store.dispatch('reading/setCurrentReadingTaskState', 2)
const res = await getAutoCutNextTask()
var isAutoTask = res.Result.AutoCutNextTask
if (isAutoTask) {
window.location.reload()
} else {
// ''
this.$confirm(this.$t('trials:readingReport:message:msg4'), {
type: 'warning',
distinguishCancelAndClose: true
})
.then(() => {
window.location.reload()
})
.catch(action => {
changeURLStatic('visitTaskId', this.visitTaskId)
})
}
window.opener.postMessage('refreshTaskList', window.location)
}
this.loading = false
} catch (e) {
this.loading = false
if (this.$refs['signForm'] && this.$refs['signForm'].btnLoading) {
this.$refs['signForm'].btnLoading = false
}
}
},
handleFusion() {
// https
if (!window.isSecureContext) {
this.$alert(this.$t('components:uploadvideo:message:xf3'))
return
}
//
if (!window.crossOriginIsolated) {
this.$alert(this.$t('components:uploadvideo:message:xf4'))
return
}
// if (!window.isSecureContext) {
// this.$alert(this.$t('components:uploadvideo:message:xf3'))
// return
// }
// //
// if (!window.crossOriginIsolated) {
// this.$alert(this.$t('components:uploadvideo:message:xf4'))
// return
// }
if (this.imageQualityIssues && parseInt(this.imageQualityIssues) === 6) {
this.$alert(this.$t('trials:lugano:message:fusionWarn'))
return
@ -2400,9 +2667,22 @@ export default {
.form-container{
// box-sizing: border-box;
width: 350px;
height: 100%;
// height: 100vh;
border: 1px solid #727272;
// overflow-y: auto;
// position: relative;
display: flex;
flex-direction: column;
}
.form-wrapper{
flex-grow: 1;
height: 0;
overflow: hidden;
}
.form-footer{
background: #000;
padding: 10px 0;
text-align: center;
}
.viewer-container{
box-sizing: border-box;

View File

@ -1,7 +1,7 @@
<template>
<div class="measurement-wrapper" :style="{'height':height+10+'px'}">
<div class="measurement-wrapper">
<div class="container" :style="{'height':height+'px'}">
<div class="container">
<div class="basic-info">
<h3 v-if="isReadingShowSubjectInfo">
<span v-if="subjectCode">{{ subjectCode }} </span>
@ -203,7 +203,6 @@ export default {
},
data() {
return {
height: window.innerHeight - 140,
questions: [],
activeName: '',
activeItem: {
@ -269,8 +268,6 @@ export default {
DicomEvent.$on('split', measureData => {
this.handleSplit(measureData.RowId, measureData.QuestionId)
})
window.addEventListener('resize', this.setHeight)
},
beforeDestroy() {
DicomEvent.$off('setCollapseActive')
@ -457,9 +454,6 @@ export default {
} catch (e) { console.log(e) }
})
},
setHeight() {
this.height = window.innerHeight - 140
},
getQuestionAnswer(questions, questionMark, answers) {
var idx = questions.findIndex(i => i.QuestionMark === questionMark)
if (idx > -1) {
@ -1183,6 +1177,7 @@ export default {
</script>
<style lang="scss" scoped>
.measurement-wrapper{
height: 100%;
overflow-y: auto;
// overflow: hidden;

View File

@ -0,0 +1,874 @@
<template>
<div class="measurement-wrapper">
<div class="container">
<div class="basic-info">
<h3 v-if="isReadingShowSubjectInfo">
<span v-if="subjectCode">{{ subjectCode }} </span>
<span style="margin-left:5px;">{{ taskBlindName }}</span>
</h3>
<div v-if="readingTaskState < 2">
<el-tooltip class="item" effect="dark" :content="$t('trials:dicomReading:message:confirmReset')" placement="bottom">
<i
class="el-icon-refresh-left"
@click="resetMeasuredData"
/>
</el-tooltip>
</div>
</div>
<!-- 影像质量问题 -->
<div class="lesions">
<Questions
ref="ecrf"
:question-form-change-state="true"
:question-form-change-num="0"
:is-qulity-issues="false"
:group-classify="1"
/>
</div>
<!-- 测量问题 -->
<template v-if="questions.length > 0">
<div v-for="(qs,index) in questions" :key="index" v-loading="loading" class="lesions lesions_wrapper" style="margin-bottom: 10px">
<h4 v-if="qs.Type === 'group'" style="color: #ddd;padding: 5px 0px;margin: 0;">
{{ language==='en'?qs.GroupEnName:qs.GroupName }}
</h4>
<div class="lesion_list">
<el-form
v-if="questions.length > 0"
:ref="`questions${index}`"
size="small"
:model="questionForm"
>
<div class="table-wrapper">
<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 === 102 && readingTaskState < 2">
<div class="add-icon" @click.prevent="downloadTpl">
<i class="el-icon-download" />
</div>
<div class="add-icon" style="margin: 0 5px;" @click.prevent="uploadTpl(item.LesionType)">
<i class="el-icon-upload2" />
</div>
<div class="add-icon" @click.prevent="handleAddOrEdit('add',item)">
<i class="el-icon-plus" />
</div>
</div>
</div>
<el-table
v-if="item.Type === 'basicTable' && item.TableQuestions"
:ref="item.Id"
:data="item.TableQuestions.Answers"
>
<!-- <el-table-column :label="$t('CustomizeQuestionFormItem:label:OrderMark')" width="60px" show-overflow-tooltip>
<template slot-scope="scope">
{{ getLesionName(item.OrderMark,scope.row.RowIndex) }}
</template>
</el-table-column> -->
<!-- <el-table-column
type="index"
width="40px"
/> -->
<el-table-column
v-for="q of item.TableQuestions.Questions"
:key="q.Id"
:prop="q.Id"
:label="q.QuestionName"
show-overflow-tooltip
:render-header="renderHeader"
>
<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">
{{ `${$fd(q.DictionaryCode, parseInt(scope.row[q.Id]))}` }}
</span>
<span v-else>
{{ `${scope.row[q.Id]}` }}
</span>
</template>
</el-table-column>
<el-table-column
v-if="readingTaskState < 2 && item.LesionType === 102"
:label="$t('common:action:action')"
width="90px"
fixed="right"
>
<template slot-scope="scope">
<el-button type="text" size="mini" @click="handleAddOrEdit('edit',item, scope.$index)">
{{ $t('common:button:edit') }}
</el-button>
<el-button type="text" size="mini" @click="handleDelete(item, scope.$index)">
{{ $t('common:button:delete') }}
</el-button>
</template>
</el-table-column>
</el-table>
<template v-else>
<!-- 数值 -->
<el-form-item
v-if="item.ShowQuestion!==2"
:key="item.Id"
:label="`${item.QuestionName}`"
:prop="item.Id"
:rules="[
{ required: (item.IsRequired === 0 || (item.IsRequired ===1 && item.RelevanceId && (item.RelevanceValue.includes(questionForm[item.RelevanceId])))) && item.Type!=='group' && item.Type!=='summary',
message:['radio', 'select', 'checkbox'].includes(item.Type) ? $t('common:ruleMessage:select') : $t('common:ruleMessage:specify'), trigger: ['blur','change']},
]"
>
<!-- 数值类型 -->
<template v-if="item.Type==='textarea'">
<el-input
v-model="questionForm[qs.Id]"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"
:disabled="!isCurrentTask || readingTaskState>=2"
@change="((val)=>{formItemChange(val, qs)})"
/>
</template>
</el-form-item>
</template>
</div>
<div
v-if="isCurrentTask && readingTaskState<2 && qs.GroupClassify === 5"
class="base-dialog-footer"
style="text-align:right;margin-top:10px;"
>
<!-- 保存 -->
<el-button
type="primary"
size="mini"
:disabled="!formChanged"
@click="handleSave(index)"
>
{{ $t('common:button:save') }}
</el-button>
</div>
</div>
</el-form>
</div>
</div>
</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="ecrf3"
:question-form-change-state="true"
:question-form-change-num="0"
:is-qulity-issues="false"
:group-classify="5"
/>
<!-- 新增编辑弹窗 -->
<el-dialog
v-if="addOrEdit.visible"
:visible.sync="addOrEdit.visible"
:close-on-click-modal="false"
:title="addOrEdit.title"
width="500px"
>
<el-form
ref="tableQsForm"
v-loading="loading"
:model="qsForm"
size="small"
>
<QuestionTableFormItem
v-for="item in qsList"
:key="item.Id"
:question="item"
:question-form="qsForm"
:reading-task-state="readingTaskState"
@setFormItemData="setFormItemData"
@resetFormItemData="resetFormItemData"
/>
<el-form-item style="text-align: right">
<el-button
size="small"
@click="addOrEdit.visible = false"
>
{{ $t('common:button:cancel') }}
</el-button>
<!-- 保存 -->
<el-button size="small" class="my_upload_btn" @click="saveFormData">
{{ $t('common:button:save') }}
</el-button>
</el-form-item>
</el-form>
</el-dialog>
<!-- 导入 -->
<el-dialog
v-if="upload.visible"
:visible.sync="upload.visible"
:close-on-click-modal="false"
:title="upload.title"
width="500px"
>
<UploadExcel
:visit-task-id="visitTaskId"
@close="uploadDlgClose"
/>
</el-dialog>
</div>
</div>
</template>
<script>
import { saveTaskQuestion, submitTableQuestion, deleteReadingRowAnswer } from '@/api/trials'
import { resetReadingTask, getIVUSTemplate } from '@/api/reading'
import DicomEvent from './../DicomEvent'
import store from '@/store'
import { mapGetters } from 'vuex'
import Questions from './../Questions'
import QuestionTableFormItem from './QuestionTableFormItem'
import UploadExcel from './UploadExcel'
export default {
name: 'MeasurementList',
components: {
Questions,
QuestionTableFormItem,
UploadExcel
},
props: {
isShow: {
type: Boolean,
required: true
},
isReadingShowSubjectInfo: {
type: Boolean,
required: true
},
questionFormChangeState: {
type: Boolean,
default() {
return false
}
},
questionFormChangeNum: {
type: Number,
default() {
return 0
}
}
},
data() {
return {
questions: [],
visitTaskId: '',
isCurrentTask: false,
loading: false,
readingTaskState: 2,
isBaseLineTask: false,
taskBlindName: '',
tableQuestions: [],
CriterionType: null,
subjectCode: '',
questionForm: {},
formChanged: false,
digitPlaces: 2,
addOrEdit: { visible: false, title: '' },
upload: { visible: false, title: '' },
qsList: [],
answersList: [],
qsForm: {},
diffId: '',
eemId: '',
lumenId: ''
}
},
computed: {
...mapGetters(['visitTaskList', 'language', 'lastCanvasTaskId', 'currentReadingTaskState'])
},
watch: {
lastCanvasTaskId: {
immediate: true,
handler(val) {
if (val) {
this.initList()
}
}
},
currentReadingTaskState: {
immediate: true,
handler(val) {
if (val) {
this.readingTaskState = val
}
}
}
},
mounted() {
// this.subjectCode = this.$router.currentRoute.query.subjectCode
this.subjectCode = localStorage.getItem('subjectCode')
this.digitPlaces = Number(localStorage.getItem('digitPlaces'))
window.addEventListener('message', this.receiveMsg)
this.CriterionType = parseInt(localStorage.getItem('CriterionType'))
DicomEvent.$on('setCollapseActive', measureData => {
this.setCollapseActive(measureData)
console.log('setCollapseActive')
})
DicomEvent.$on('getAllUnSaveLesions', (callback) => {
callback([])
})
DicomEvent.$on('refreshQuestions', _ => {
this.refreshQuestions()
})
},
beforeDestroy() {
DicomEvent.$off('setCollapseActive')
DicomEvent.$off('getUnSaveTarget')
DicomEvent.$off('refreshQuestions')
},
methods: {
async initList() {
var i = this.visitTaskList.findIndex(i => i.VisitTaskId === this.lastCanvasTaskId)
if (i > -1) {
this.visitTaskId = this.visitTaskList[i].VisitTaskId
this.taskBlindName = this.visitTaskList[i].TaskBlindName
this.readingTaskState = this.visitTaskList[i].ReadingTaskState
this.isBaseLineTask = this.visitTaskList[i].IsBaseLineTask
this.isCurrentTask = this.visitTaskList[i].IsCurrentTask
if (!this.visitTaskList[i].IsInit) {
var loading = this.$loading({ fullscreen: true })
var triald = this.trialId = this.$router.currentRoute.query.trialId
if (!this.visitTaskList[i].studyListInit) {
await store.dispatch('reading/getStudyInfo', { trialId: triald, subjectVisitId: this.visitTaskList[i].VisitId, visitTaskId: this.visitTaskList[i].VisitTaskId, taskBlindName: this.visitTaskList[i].TaskBlindName })
}
if (!this.visitTaskList[i].readingQuestionsInit) {
await store.dispatch('reading/getReadingQuestionAndAnswer', { trialId: triald, visitTaskId: this.visitTaskList[i].VisitTaskId })
}
if (!this.visitTaskList[i].questionsInit) {
await store.dispatch('reading/getDicomReadingQuestionAnswer', { trialId: triald, visitTaskId: this.visitTaskList[i].VisitTaskId })
}
await store.dispatch('reading/setStatus', { visitTaskId: this.visitTaskList[i].VisitTaskId })
loading.close()
}
this.questions = this.visitTaskList[i].ReadingQuestions
this.setQuestionForm(this.questions)
this.$nextTick(() => {
this.$refs['ecrf'].getQuestions(this.visitTaskId)
this.$refs['ecrf2'].getQuestions(this.visitTaskId)
this.$refs['ecrf3'].getQuestions(this.visitTaskId)
})
}
},
setQuestionForm(questions) {
const type = ['number', 'radio', 'select', 'input', 'textarea', 'calculation']
questions.forEach(item => {
if (type.includes(item.Type)) {
const answer = item.Type === 'number' || item.Type === 'calculation' ? isNaN(parseFloat(item.Answer)) ? null : parseFloat(item.Answer) : item.Answer
this.$set(this.questionForm, item.Id, answer)
}
if (item.Childrens.length > 0) {
this.setQuestionForm(item.Childrens)
}
})
},
async resetQuestions(obj) {
this.setQuestions(this.questions, obj)
await store.dispatch('reading/setReadingQuestionAndAnswer', { questions: this.questions, visitTaskId: this.visitTaskId })
this.getTableQuestions()
},
setQuestions(questions, obj) {
questions.forEach(item => {
if ((item.Type === 'table' || item.Type === 'basicTable') && item.Id === obj.questionId) {
var idx = item.TableQuestions.Answers.findIndex(i => i.RowIndex === obj.rowIndex)
item.TableQuestions.Answers[idx].saveTypeEnum = obj.saveTypeEnum
item.TableQuestions.Answers[idx].area1 = obj.area1
item.TableQuestions.Answers[idx].area2 = obj.area2
item.TableQuestions.Answers[idx].diff = obj.diff
for (const i in obj.anwsers) {
item.TableQuestions.Answers[idx][i] = String(obj.anwsers[i])
}
}
if (item.Childrens.length > 0) {
this.setQuestions(item.Childrens, obj)
}
})
},
getQuestions(questions) {
questions.forEach(item => {
if ((item.Type === 'table' || item.Type === 'basicTable') && item.TableQuestions && item.TableQuestions.Answers.length > 0) {
item.TableQuestions.Answers.forEach(answerObj => {
if (answerObj.RowId) {
var diffVal = this.getQuestionAnswer(item.TableQuestions.Questions, 1003, answerObj)
this.$set(answerObj, 'saveTypeEnum', isNaN(parseFloat(diffVal)) ? 1 : 2)
} else {
this.$set(answerObj, 'saveTypeEnum', 0)
}
})
}
if (item.Childrens.length > 0) {
this.getQuestions(item.Childrens)
}
})
return questions
},
getReadingQuestionAndAnswer() {
return new Promise(async resolve => {
try {
const loading = this.$loading({ fullscreen: true })
await store.dispatch('reading/refreshReadingQuestionAndAnswer', { trialId: this.$router.currentRoute.query.trialId, visitTaskId: this.visitTaskId }).then(() => {
var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
if (idx > -1) {
if (this.visitTaskList[idx].ReadingQuestions.length > 0) {
this.questions = this.visitTaskList[idx].ReadingQuestions
this.setQuestionForm(this.questions)
}
this.readingTaskState = this.visitTaskList[idx].ReadingTaskState
this.isBaseLineTask = this.visitTaskList[idx].IsBaseLineTask
this.isCurrentTask = this.visitTaskList[idx].IsCurrentTask
}
})
loading.close()
resolve()
} catch (e) { console.log(e) }
})
},
getQuestionAnswer(questions, questionMark, answers) {
var idx = questions.findIndex(i => i.QuestionMark === questionMark)
if (idx > -1) {
var questionId = questions[idx].Id
return answers[questionId]
} else {
return ''
}
},
isCanActiveTool(toolName) {
return { isCanActiveTool: true, reason: '' }
},
getMaxRowIndex(answerList) {
if (answerList.length > 0) {
var arr = []
answerList.forEach(item => {
var rowIndex = item.RowIndex
arr.push(parseInt(rowIndex))
})
var max = Math.max.apply(null, arr)
return max
} else {
return 0
}
},
getLesionName(orderMark, rowIndex) {
var lessionName = ''
var rowIndexArr = rowIndex.split('.')
var x = parseInt(rowIndexArr[0])
var y = parseInt(rowIndexArr[1])
if (y > 0) {
y = String.fromCharCode(parseInt(rowIndexArr[1]) - 1 + 65 + 32)
lessionName = `${orderMark}${String(x).padStart(2, '0')}${y}`
} else {
lessionName = `${orderMark}${String(x).padStart(2, '0')}`
}
return lessionName
},
modifyMeasuredData(measureObj) {
},
//
setMeasuredData(measureData) {
},
formItemChange() {
this.formChanged = true
},
limitBlur(qId, valueType) {
const value = this.questionForm[qId]
if (isNaN(parseInt(value))) {
this.$set(this.questionForm, qId, '')
} else {
if (valueType === 0) {
this.$set(this.questionForm, qId, parseInt(value))
} else if (valueType === 3) {
this.$set(this.questionForm, qId, parseFloat(value))
} else {
this.$set(this.questionForm, qId, this.numberToFixed(value))
}
}
},
numberToFixed(v) {
if (this.digitPlaces > -1) {
return isNaN(parseFloat(v)) ? null : `${parseFloat(v).toFixed(this.digitPlaces)}`
} else {
return v
}
},
async handleSave(index) {
const refName = `questions${index}`
const valid = await this.$refs[refName][0].validate()
if (!valid) return
const loading = this.$loading({ fullscreen: true })
var answers = []
for (const k in this.questionForm) {
answers.push({ id: k, answer: this.questionForm[k] })
}
var params = {
visitTaskId: this.visitTaskId,
answers: answers
}
try {
await saveTaskQuestion(8, params)
this.$message.success(this.$t('common:message:savedSuccessfully'))
loading.close()
DicomEvent.$emit('getReportInfo', true)
this.refreshQuestions()
this.formChanged = false
} catch (e) {
console.log(e)
loading.close()
}
},
getECRFQuestions(obj) {
this.$refs['ecrf'].getQuestions(obj.visitTaskId)
this.$refs['ecrf2'].getQuestions(obj.visitTaskId)
this.$refs['ecrf3'].getQuestions(obj.visitTaskId)
},
async resetMeasuredData() {
const confirm = await this.$confirm(
this.$t('trials:dicomReading:message:confirmReset1'),
this.$t('trials:dicomReading:message:confirmReset2'),
{
type: 'warning',
distinguishCancelAndClose: true
}
)
if (confirm !== 'confirm') return
const loading = this.$loading({ fullscreen: true })
try {
const res = await resetReadingTask({ visitTaskId: this.visitTaskId })
this.loading = false
if (res.IsSuccess) {
await this.getReadingQuestionAndAnswer(this.visitTaskId)
const triald = this.$router.currentRoute.query.trialId
await store.dispatch('reading/refreshDicomReadingQuestionAnswer', { trialId: triald, visitTaskId: this.visitTaskId })
this.$refs['ecrf'].getQuestions(this.visitTaskId, true)
this.$refs['ecrf2'].getQuestions(this.visitTaskId, true)
this.$refs['ecrf3'].getQuestions(this.visitTaskId, true)
DicomEvent.$emit('getReportInfo', true)
}
loading.close()
} catch (e) {
loading.close()
console.log(e)
}
},
async refreshQuestions() {
const triald = this.$router.currentRoute.query.trialId
await this.getReadingQuestionAndAnswer(this.visitTaskId)
await store.dispatch('reading/refreshDicomReadingQuestionAnswer', { trialId: triald, visitTaskId: this.visitTaskId })
this.$refs['ecrf'].getQuestions(this.visitTaskId, true)
this.$refs['ecrf2'].getQuestions(this.visitTaskId, true)
this.$refs['ecrf3'].getQuestions(this.visitTaskId, true)
},
receiveMsg(event) {
},
resetFormItemData(v) {
this.qsForm[v] = null
},
setFormItemData(obj) {
this.qsForm[obj.key] = obj.val
if (obj.key === this.eemId || obj.key === this.lumenId) {
const eem = this.qsForm[this.eemId]
const lumen = this.qsForm[this.lumenId]
if (isNaN(parseFloat(eem)) || isNaN(parseFloat(lumen))) {
this.$set(this.qsForm, this.diffId, null)
} else {
const diff = parseFloat(eem) - parseFloat(lumen)
this.$set(this.qsForm, this.diffId, this.numberToFixed(diff))
}
}
},
handleAddOrEdit(type, row, index) {
this.addOrEdit.title = row.QuestionName
this.qsList = row.TableQuestions.Questions
this.qsForm = {}
this.qsForm.questionId = row.Id
this.qsForm.rowId = type === 'add' ? null : row.TableQuestions.Answers && row.TableQuestions.Answers[index] ? row.TableQuestions.Answers[index].RowId : null
if (type === 'add') {
var maxIndex = this.getMaxRowIndex(row.TableQuestions.Answers)
this.qsForm.rowIndex = `${maxIndex + 1}.00`
} else {
this.qsForm.rowIndex = row.TableQuestions.Answers && row.TableQuestions.Answers[index] ? row.TableQuestions.Answers[index].RowIndex : null
}
row.TableQuestions.Questions.map(i => {
if (i.QuestionMark === 1001) {
this.eemId = i.Id
}
if (i.QuestionMark === 1002) {
this.lumenId = i.Id
}
if (i.QuestionMark === 1003) {
this.diffId = i.Id
}
let v = null
if (type === 'add') {
v = i.Type === 'number' ? undefined : null
} else {
v = row.TableQuestions.Answers && row.TableQuestions.Answers[index] ? row.TableQuestions.Answers[index][i.Id] : null
}
this.$set(this.qsForm, i.Id, v)
})
this.addOrEdit.visible = true
},
async saveFormData() {
const valid = await this.$refs.tableQsForm.validate()
if (!valid) return
const loading = this.$loading({ fullscreen: true })
try {
var answers = []
var reg = new RegExp(/^[0-9a-zA-Z]{8}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{12}$/)
for (const k in this.qsForm) {
if (reg.test(k)) {
if (answers.findIndex(i => i.tableQuestionId === k) === -1) {
answers.push({ tableQuestionId: k, answer: this.qsForm[k] })
}
}
}
var params = {
questionId: this.qsForm.questionId,
rowId: this.qsForm.rowId,
rowIndex: this.qsForm.rowIndex,
visitTaskId: this.visitTaskId,
trialId: this.trialId,
answerList: answers
}
const res = await submitTableQuestion(params)
if (res.IsSuccess) {
// this.refreshQuestions()
await this.getReadingQuestionAndAnswer(this.visitTaskId)
this.$refs['ecrf'].getQuestions(this.visitTaskId, true)
this.$refs['ecrf2'].getQuestions(this.visitTaskId, true)
this.$refs['ecrf3'].getQuestions(this.visitTaskId, true)
DicomEvent.$emit('getReportInfo', true)
//
this.addOrEdit.visible = false
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
loading.close()
} catch (e) {
console.log(e)
loading.close()
}
},
async handleDelete(row, index) {
const loading = this.$loading({ fullscreen: true })
try {
const confirm = await this.$confirm(
this.$t('trials:reading:warnning:msg48'),
{
type: 'warning',
distinguishCancelAndClose: true
}
)
if (confirm !== 'confirm') return
if (row.TableQuestions.Answers[index].RowId) {
var param = {
visitTaskId: this.visitTaskId,
questionId: row.Id,
rowId: row.TableQuestions.Answers[index].RowId
}
const res = await deleteReadingRowAnswer(param)
if (res.IsSuccess) {
await this.getReadingQuestionAndAnswer(this.visitTaskId)
DicomEvent.$emit('getReportInfo', true)
// ''
this.$message.success(this.$t('common:message:deletedSuccessfully'))
}
}
loading.close()
} catch (e) {
loading.close()
console.log(e)
}
},
uploadTpl(lesionType) {
this.upload.title = `导入( ${this.$fd('LesionType', lesionType)} `
this.upload.visible = true
},
async downloadTpl() {
try {
const params = {
visitTaskId: this.visitTaskId
}
await getIVUSTemplate(params)
} catch (e) {
console.log(e)
}
},
async uploadDlgClose() {
await this.getReadingQuestionAndAnswer(this.visitTaskId)
DicomEvent.$emit('getReportInfo', true)
this.upload.visible = false
},
renderHeader(h, { column, $index }) {
const span = document.createElement('span')
span.innerText = column.label
document.body.appendChild(span)
const w = span.getBoundingClientRect().width
column.minWidth = w > 65 ? w + 10 : w > 60 ? w + 15 : w + 20
document.body.removeChild(span)
return h('span', column.label)
}
}
}
</script>
<style lang="scss" scoped>
.measurement-wrapper{
height: 100%;
overflow-y: auto;
.container{
padding: 10px;
.basic-info{
display: flex;
justify-content: space-between;
align-items: center;
h3{
color: #ddd;
padding: 5px 0px;
margin: 0;
}
i{
color: #fff;
font-size: 22px;
font-weight: bold;
cursor: pointer;
}
}
}
.title{
padding: 5px;
font-weight: bold;
color: #ddd;
font-size: 15px;
}
.add-icon{
display: inline-block;
padding: 5px;
font-weight: bold;
color: #ddd;
font-size: 15px;
border: 1px solid #938b8b;
margin-bottom: 2px;
cursor: pointer;
}
.add-icon:hover{
background-color: #607d8b;
}
.flex-row{
display: flex;
flex-direction: row;
justify-content: space-between;
background-color: #424242;
}
.lesion_list{
position: relative;
/deep/ .el-form-item__label{
color: #c3c3c3;
text-align: left;
}
/deep/ .el-input .el-input__inner{
background-color: transparent;
color: #ddd;
border: 1px solid #5e5e5e;
}
/deep/ .el-textarea__inner{
background-color: transparent;
color: #ddd;
border: 1px solid #5e5e5e;
}
/deep/ .el-form-item{
display: flex;
flex-direction: column;
justify-content: flex-start;
}
/deep/ .el-form-item__content{
flex: 1;
}
/deep/ .el-input.is-disabled .el-input__inner{
background-color: #646464a1;
}
/deep/ .el-select.is-disabled .el-input__inner{
background-color: #646464a1;
}
.el-form-item__content
.el-select{
width: 100%;
}
}
.table-wrapper {
::-webkit-scrollbar {
width: 5px;
height: 8px;
}
::-webkit-scrollbar-thumb {
border-radius: 10px;
background: #d0d0d0;
}
/deep/ .el-table,
.el-table__expanded-cell {
background-color: #000;
color: #fff;
border-color: #444444;
}
/deep/ .el-table th,
.el-table tr {
background-color: #000;
color: #fff;
border-color: #444444;
}
/deep/ .el-table__body tr > td {
background-color: #000 !important;
color: #fff;
border-color: #444444;
}
/deep/ .el-table__body tr:hover > td {
background-color: #858282 !important;
color: #fff;
border-color: #444444;
}
/deep/ .el-table--border th.gutter:last-of-type {
border: none;
}
/deep/ .el-table__fixed-right {
height: 100% !important;
}
// /deep/ .el-table .cell {
// line-height: 20px;
// }
/deep/ .el-table__cell {
padding: 5px 0;
}
/deep/.el-table__fixed-right-patch {
background-color: #000 !important;
border-color: #444444;
}
/deep/.el-table__fixed-body-wrapper tr:hover > td {
background-color: #000 !important;
}
/deep/.el-table--scrollable-x .el-table__body-wrapper {
z-index: 2;
}
}
}
</style>

View File

@ -0,0 +1,297 @@
<template>
<div>
<div
v-if="!!question.GroupName && question.Type==='group'"
>
<h4 style="color: #ddd;padding: 5px 0px;margin: 0;">
{{ language==='en'?question.GroupEnName:question.GroupName }}
</h4>
</div>
<template v-else>
<el-form-item
v-if="(question.ShowQuestion===1 && question.ParentTriggerValueList.includes(String(questionForm[question.ParentId]))) || question.ShowQuestion===0 "
:label="`${question.QuestionName}`"
:prop="question.Id"
:rules="[
{ required: (question.IsRequired === 0 || (question.IsRequired ===1 && question.RelevanceId && (questionForm[question.RelevanceId] === question.RelevanceValue))) && question.Type!=='group' && question.Type!=='summary',
message: ['radio', 'select', 'checkbox'].includes(question.Type) ? $t('common:ruleMessage:select') : $t('common:ruleMessage:specify'), trigger: ['blur', 'change']},
]"
:class="[question.Type==='group' ? 'mb' : (question.Type==='upload' || question.QuestionName.length > 15) ?'uploadWrapper' : '']"
>
<!-- 输入框 -->
<el-input
v-if="question.Type==='input'"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2"
@change="((val)=>{formItemChange(val, question)})"
/>
<!-- 多行文本输入框 -->
<el-input
v-else-if="question.Type==='textarea'"
v-model="questionForm[question.Id]"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"
maxlength="500"
:disabled="readingTaskState >= 2"
@change="((val)=>{formItemChange(val, question)})"
/>
<!-- 下拉框 -->
<el-select
v-else-if="question.Type==='select'"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2 || ((question.TableQuestionType === 2 || question.QuestionGenre === 2) && !!question.DictionaryCode)"
clearable
@change="((val)=>{formItemChange(val, question)})"
>
<template v-if="question.TableQuestionType === 1">
<el-option
v-for="item in organList"
:key="item.Id"
:label="item[question.DataTableColumn]"
:value="item[question.DataTableColumn]"
/>
</template>
<template v-else-if="question.TableQuestionType === 3 || question.QuestionGenre === 3">
<el-option
v-for="item of $d[question.DictionaryCode]"
:key="item.id"
:value="String(item.value)"
:label="item.label"
/>
</template>
<template v-else-if="(question.TableQuestionType === 2 || question.QuestionGenre === 2) && question.DictionaryCode">
<el-option
v-for="item of $d[question.DictionaryCode]"
:key="item.id"
:value="String(item.value)"
:label="item.label"
/>
</template>
<template v-else>
<el-option
v-for="val in question.TypeValue.split('|')"
:key="val"
:label="val"
:value="val"
/>
</template>
</el-select>
<!-- 单选 -->
<el-radio-group
v-else-if="question.Type==='radio'"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2"
@change="((val)=>{formItemChange(val, question)})"
>
<template v-if="question.DictionaryCode">
<el-radio
v-for="item of $d[question.DictionaryCode]"
:key="item.id"
:label="String(item.value)"
>
{{ item.label }}
</el-radio>
</template>
<template v-else-if="question.TypeValue">
<el-radio
v-for="val in question.TypeValue.split('|')"
:key="val"
:label="val"
>
{{ val }}
</el-radio>
</template>
</el-radio-group>
<!-- 复选框 -->
<el-checkbox-group
v-else-if="question.Type==='checkbox'"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2"
@change="((val)=>{formItemChange(val, question)})"
>
<el-checkbox
v-for="val in question.TypeValue.split('|')"
:key="val"
:label="val"
>
{{ val }}
</el-checkbox>
</el-checkbox-group>
<!-- 数值 -->
<!-- <template v-else-if="question.Type==='number'">
<el-input-number
v-if="question.ValueType === 0"
v-model="questionForm[question.Id]"
:precision="0"
:disabled="readingTaskState >= 2"
style="width: 100%"
@change="((val)=>{formItemChange(val, question)})"
/>
<el-input-number
v-else-if="question.ValueType === 3"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2"
style="width: 100%"
@change="((val)=>{formItemChange(val, question)})"
/>
<el-input-number
v-else-if="question.ValueType === 1 || question.ValueType === 2"
v-model="questionForm[question.Id]"
:precision="digitPlaces"
:disabled="readingTaskState >= 2"
style="width: 100%"
@change="((val)=>{formItemChange(val, question)})"
/>
</template> -->
<!-- 数值类型 -->
<el-input
v-else-if="question.Type==='number'"
v-model="questionForm[question.Id]"
:disabled="readingTaskState>=2 "
type="number"
@change="((val)=>{formItemChange(val, question)})"
>
<template v-if="question.Unit" slot="append">
{{ $fd('ValueUnit', parseInt(question.Unit)) }}
</template>
</el-input>
<el-input
v-else-if="question.Type==='calculation'"
v-model="questionForm[question.Id]"
disabled
@change="((val)=>{formItemChange(val, question)})"
>
<template v-if="question.Unit" slot="append">
{{ $fd('ValueUnit', parseInt(question.Unit)) }}
</template>
</el-input>
</el-form-item>
</template>
<QuestionTableFormItem
v-for="item in question.Childrens"
:key="item.Id"
:question="item"
:question-form="questionForm"
:reading-task-state="readingTaskState"
@setFormItemData="setFormItemData"
@resetFormItemData="resetFormItemData"
/>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'QuestionTableFormItem',
props: {
questionForm: {
type: Object,
default() {
return {}
}
},
question: {
type: Object,
default() {
return []
}
},
readingTaskState: {
type: Number,
required: true
}
},
data() {
return {
digitPlaces: 2
}
},
computed: {
...mapGetters(['language'])
},
mounted() {
this.digitPlaces = Number(localStorage.getItem('digitPlaces'))
},
methods: {
formItemChange(v, question) {
if (question.Childrens && question.Childrens.length > 0) {
this.resetChild(question.Childrens)
} else {
if (question.Type === 'number') {
v = this.limitBlur(question.ValueType, v)
}
this.$emit('setFormItemData', { key: question.Id, val: v })
}
},
resetChild(obj) {
obj.forEach(i => {
this.$emit('resetFormItemData', i.Id)
if (i.Childrens && i.Childrens.length > 0) {
this.resetChild(i.Childrens)
}
})
},
limitBlur(valueType, value) {
if (isNaN(parseInt(value))) {
value = null
} else {
if (valueType === 0) {
value = parseInt(value)
} else if (valueType === 3) {
value = parseFloat(value)
} else {
value = this.numberToFixed(value)
}
}
return value
},
numberToFixed(v) {
if (this.digitPlaces > -1) {
return isNaN(parseFloat(v)) ? null : `${parseFloat(v).toFixed(this.digitPlaces)}`
} else {
return v
}
},
resetFormItemData(v) {
this.$emit('resetFormItemData', v)
},
setFormItemData(obj) {
this.$emit('setFormItemData', obj)
},
limitNumbersEvent(e) {
//
let val = e.target.value
if (val.length > 6) val = val.slice(0, 6)
e.target.value = (val.match(/^\d*(\.?\d{0,3})/g)[0]) || null
}
}
}
</script>
<style lang="scss" scoped>
.mb{
margin-bottom: 0px;
}
.disabled{
/deep/ .el-upload--picture-card {
display: none;
}
}
.uploadWrapper{
display: flex;
flex-direction: column !important;
align-items: flex-start;
}
/deep/ .el-input.is-disabled .el-input__inner{
background-color: #646464a1;
}
/deep/ .el-input-group__append, .el-input-group__prepend{
padding: 0 10px;
}
/deep/ .el-form-item__content {
width: 100%;
}
/deep/ .el-select.is-disabled .el-input__inner{
background-color: #646464a1;
}
</style>

View File

@ -0,0 +1,117 @@
<template>
<el-form
ref="uploadExcel"
class="upload-excel-file"
>
<!-- 文件 -->
<el-form-item :label="$t('trials:consistencyCheck:label:file')">
<div class="upload-container">
<el-upload
class="upload-demo"
action
accept=".xlsx,.xls,.csv"
:before-upload="beforeUpload"
:http-request="handleUploadFile"
:on-preview="handlePreview"
:show-file-list="true"
:limit="1"
:on-exceed="handleExceed"
>
<el-button size="small" type="primary">
{{ $t('trials:consistencyCheck:dialogButton:upload') }}
</el-button>
<span
slot="tip"
style="margin-left:10px;"
class="el-upload__tip"
>
({{ $t('trials:consistencyCheck:message:excelFileOnly') }})
</span>
</el-upload>
</div>
</el-form-item>
</el-form>
</template>
<script>
import { uploadIVUSTemplate } from '@/api/reading'
export default {
props: {
visitTaskId: {
type: String,
required: true
}
},
data() {
return {
}
},
methods: {
beforeUpload(file) {
//
if (this.checkFileSuffix(file.name)) {
return true
} else {
// Must be xls or xlsx format
this.$alert(this.$t('trials:consistencyCheck:message:xlsx'))
return false
}
},
async handleUploadFile(param) {
const loading = this.$loading({ fullscreen: true })
try {
var data = new FormData()
data.append('file', param.file)
data.append('visitTaskId', this.visitTaskId)
await uploadIVUSTemplate(data)
this.$emit('close')
this.$message.success('导入成功!')
loading.close()
} catch (e) {
loading.close()
console.log(e)
}
},
handlePreview(file) {
if (file.fullPath) {
window.open(file.fullPath, '_blank')
}
},
handleExceed(files, fileList) {
// Upload is currently limited to 1 file
this.$message.warning(this.$t('trials:consistencyCheck:message:onlyOneFile'))
},
checkFileSuffix(fileName) {
var typeArr = ['xls', 'xlsx', 'csv']
var extendName = fileName.substring(fileName.lastIndexOf('.') + 1).toLocaleLowerCase()
if (typeArr.indexOf(extendName) !== -1) {
return true
} else {
return false
}
}
}
}
</script>
<style lang="scss">
.upload-excel-file{
.upload-container .el-upload--text {
border: none;
width: 80px;
height: 40px;
}
.upload-container .el-input--small {
margin-bottom: 5px;
}
.upload-container .el-icon-circle-check {
color: #428bca;
font-size: 13px;
}
.account_item_clear{
.el-tag__close{
display: none !important;
}
}
}
</style>

View File

@ -276,7 +276,6 @@ import { submitTableQuestion, deleteReadingRowAnswer, getIsSuvMaxLesion } from '
// import { uploadPrintscreen } from '@/api/reading'
import DicomEvent from './../DicomEvent'
import store from '@/store'
import Viewer from 'v-viewer'
export default {
name: 'MeasurementForm',
props: {
@ -353,7 +352,6 @@ export default {
}
},
mounted() {
this.initializeViewer()
this.trialId = this.$route.query.trialId
this.initForm()
DicomEvent.$on('handleImageQualityAbnormal', () => {
@ -380,11 +378,6 @@ export default {
}
}
},
initializeViewer() {
Viewer.setDefaults({
toolbar: { zoomIn: true, zoomOut: true, rotateLeft: true, rotateRight: true, flipHorizontal: true, flipVertical: true }
})
},
previewImage() {
this.$refs.viewer[0].$viewer.show()
},

View File

@ -1,7 +1,7 @@
<template>
<div class="measurement-wrapper" :style="{'height':height+10+'px'}">
<div class="measurement-wrapper">
<div class="container" :style="{'height':height+'px'}">
<div class="container">
<div class="basic-info">
<h3 v-if="isReadingShowSubjectInfo">
<span v-if="subjectCode">{{ subjectCode }} </span>
@ -262,7 +262,6 @@ export default {
},
data() {
return {
height: window.innerHeight - 140,
questions: [],
activeName: '',
activeItem: {
@ -359,8 +358,6 @@ export default {
}
callback(true)
})
window.addEventListener('resize', this.setHeight)
},
beforeDestroy() {
DicomEvent.$off('setCollapseActive')
@ -573,9 +570,6 @@ export default {
} catch (e) { console.log(e) }
})
},
setHeight() {
this.height = window.innerHeight - 140
},
getQuestionAnswer(questions, questionMark, answers) {
var idx = questions.findIndex(i => i.QuestionMark === questionMark)
if (idx > -1) {
@ -1347,9 +1341,8 @@ export default {
</script>
<style lang="scss" scoped>
.measurement-wrapper{
height: 100%;
overflow-y: auto;
// overflow: hidden;
.container{
padding: 10px;
.basic-info{

View File

@ -21,7 +21,7 @@
</div>
<div class="right-wrapper">
<div class="right-content">
<iframe v-if="selected.filePath" :src="`/static/pdfjs/web/viewer.html?file=${OSSclientConfig.basePath}${selected.filePath}?userName=${currentUser}`" width="100%" height="100%" frameborder="0" />
<iframe v-if="selected.filePath" :src="`/static/pdfjs/web/viewer.html?file=${OSSclientConfig.basePath}${selected.filePath}?userName=${currentUser}&COMPANY=${COMPANY}`" width="100%" height="100%" frameborder="0" />
</div>
</div>
</div>
@ -44,7 +44,8 @@ export default {
},
fileList: [],
loading: false,
currentUser: zzSessionStorage.getItem('userName')
currentUser: zzSessionStorage.getItem('userName'),
COMPANY:process.env.VUE_APP_COMPANY_NAME
}
},
mounted() {

View File

@ -0,0 +1,887 @@
<template>
<div class="measurement-wrapper">
<div class="container">
<div class="basic-info">
<h3 v-if="isReadingShowSubjectInfo">
<span v-if="subjectCode">{{ subjectCode }} </span>
<span style="margin-left:5px;">{{ taskBlindName }}</span>
</h3>
<div v-if="readingTaskState < 2">
<el-tooltip class="item" effect="dark" :content="$t('trials:dicomReading:message:confirmReset')" placement="bottom">
<i
class="el-icon-refresh-left"
@click="resetMeasuredData"
/>
</el-tooltip>
</div>
</div>
<!-- 影像质量问题 -->
<div class="lesions">
<Questions
ref="ecrf"
:question-form-change-state="true"
:question-form-change-num="0"
:is-qulity-issues="false"
:group-classify="1"
/>
</div>
<!-- 测量问题 -->
<template v-if="questions.length > 0">
<div v-for="(qs,index) in questions" :key="index" v-loading="loading" class="lesions lesions_wrapper" style="margin-bottom: 10px">
<h4 v-if="qs.Type === 'group'" style="color: #ddd;padding: 5px 0px;margin: 0;">
{{ language==='en'?qs.GroupEnName:qs.GroupName }}
</h4>
<div class="lesion_list">
<el-form
v-if="questions.length > 0"
:ref="`questions${index}`"
size="small"
:model="questionForm"
>
<div class="table-wrapper">
<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 === 112 || item.LesionType === 111) && readingTaskState < 2">
<div class="add-icon" @click.prevent="downloadTpl(item.LesionType)">
<i class="el-icon-download" />
</div>
<div class="add-icon" style="margin: 0 5px;" @click.prevent="uploadTpl(item.LesionType)">
<i class="el-icon-upload2" />
</div>
<div class="add-icon" @click.prevent="handleAddOrEdit('add',item)">
<i class="el-icon-plus" />
</div>
</div>
</div>
<el-table
v-if="item.Type === 'basicTable' && item.TableQuestions"
:ref="item.Id"
:data="item.TableQuestions.Answers"
max-height="600"
>
<!-- <el-table-column :label="$t('CustomizeQuestionFormItem:label:OrderMark')" width="60px" show-overflow-tooltip>
<template slot-scope="scope">
{{ getLesionName(item.OrderMark,scope.row.RowIndex) }}
</template>
</el-table-column> -->
<!-- <el-table-column
type="index"
width="40px"
/> -->
<el-table-column
v-for="q of item.TableQuestions.Questions"
:key="q.Id"
:prop="q.Id"
:label="q.QuestionName"
show-overflow-tooltip
:render-header="renderHeader"
>
<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">
{{ `${$fd(q.DictionaryCode, parseInt(scope.row[q.Id]))}` }}
</span>
<span v-else>
{{ `${scope.row[q.Id]}` }}
</span>
</template>
</el-table-column>
<el-table-column
v-if="readingTaskState < 2"
:label="$t('common:action:action')"
width="90px"
fixed="right"
>
<template slot-scope="scope">
<el-button type="text" size="mini" @click="handleAddOrEdit('edit',item, scope.$index)">
{{ $t('common:button:edit') }}
</el-button>
<el-button v-if="item.LesionType === 112 || item.LesionType === 111" type="text" size="mini" @click="handleDelete(item, scope.$index)">
{{ $t('common:button:delete') }}
</el-button>
</template>
</el-table-column>
</el-table>
<template v-else>
<!-- 数值 -->
<el-form-item
v-if="item.ShowQuestion!==2"
:key="item.Id"
:label="`${item.QuestionName}`"
:prop="item.Id"
:rules="[
{ required: (item.IsRequired === 0 || (item.IsRequired ===1 && item.RelevanceId && (item.RelevanceValue.includes(questionForm[item.RelevanceId])))) && item.Type!=='group' && item.Type!=='summary',
message:['radio', 'select', 'checkbox'].includes(item.Type) ? $t('common:ruleMessage:select') : $t('common:ruleMessage:specify'), trigger: ['blur','change']},
]"
>
<!-- 数值类型 -->
<template v-if="item.Type==='textarea'">
<el-input
v-model="questionForm[qs.Id]"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"
:disabled="!isCurrentTask || readingTaskState>=2"
@change="((val)=>{formItemChange(val, qs)})"
/>
</template>
</el-form-item>
</template>
</div>
<div
v-if="isCurrentTask && readingTaskState<2 && qs.GroupClassify === 5"
class="base-dialog-footer"
style="text-align:right;margin-top:10px;"
>
<!-- 保存 -->
<el-button
type="primary"
size="mini"
:disabled="!formChanged"
@click="handleSave(index)"
>
{{ $t('common:button:save') }}
</el-button>
</div>
</div>
</el-form>
</div>
</div>
</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="ecrf3"
:question-form-change-state="true"
:question-form-change-num="0"
:is-qulity-issues="false"
:group-classify="5"
/>
<el-dialog
v-if="addOrEdit.visible"
:visible.sync="addOrEdit.visible"
:close-on-click-modal="false"
:title="addOrEdit.title"
width="500px"
>
<el-form
ref="tableQsForm"
v-loading="loading"
:model="qsForm"
size="small"
>
<QuestionTableFormItem
v-for="item in qsList"
:key="item.Id"
:question="item"
:question-form="qsForm"
:reading-task-state="readingTaskState"
@setFormItemData="setFormItemData"
@resetFormItemData="resetFormItemData"
/>
<el-form-item style="text-align: right">
<el-button
size="small"
@click="addOrEdit.visible = false"
>
{{ $t('common:button:cancel') }}
</el-button>
<!-- 保存 -->
<el-button size="small" class="my_upload_btn" @click="saveFormData">
{{ $t('common:button:save') }}
</el-button>
</el-form-item>
</el-form>
</el-dialog>
<!-- 导入 -->
<el-dialog
v-if="upload.visible"
:visible.sync="upload.visible"
:close-on-click-modal="false"
:title="upload.title"
width="500px"
>
<UploadExcel
:visit-task-id="visitTaskId"
:lesion-type="upload.lesionType"
@close="uploadDlgClose"
/>
</el-dialog>
</div>
</div>
</template>
<script>
import { saveTaskQuestion, submitTableQuestion, deleteReadingRowAnswer } from '@/api/trials'
import { resetReadingTask, getOCTFCTTemplate, getOCTLipidAngleTemplate } from '@/api/reading'
import DicomEvent from './../DicomEvent'
import store from '@/store'
import { mapGetters } from 'vuex'
import Questions from './../Questions'
import QuestionTableFormItem from './QuestionTableFormItem'
import UploadExcel from './UploadExcel'
export default {
name: 'MeasurementList',
components: {
Questions,
QuestionTableFormItem,
UploadExcel
},
props: {
isShow: {
type: Boolean,
required: true
},
isReadingShowSubjectInfo: {
type: Boolean,
required: true
},
questionFormChangeState: {
type: Boolean,
default() {
return false
}
},
questionFormChangeNum: {
type: Number,
default() {
return 0
}
}
},
data() {
return {
questions: [],
visitTaskId: '',
isCurrentTask: false,
loading: false,
readingTaskState: 2,
isBaseLineTask: false,
taskBlindName: '',
tableQuestions: [],
CriterionType: null,
subjectCode: '',
questionForm: {},
formChanged: false,
digitPlaces: 2,
addOrEdit: { visible: false, title: '', lesionType: null },
upload: { visible: false, title: '', lesionType: null },
qsList: [],
answersList: [],
qsForm: {},
m1Id: '',
m2Id: '',
m3Id: '',
avgId: ''
}
},
computed: {
...mapGetters(['visitTaskList', 'language', 'lastCanvasTaskId', 'currentReadingTaskState'])
},
watch: {
lastCanvasTaskId: {
immediate: true,
handler(val) {
if (val) {
this.initList()
}
}
},
currentReadingTaskState: {
immediate: true,
handler(val) {
if (val) {
this.readingTaskState = val
}
}
}
},
mounted() {
// this.subjectCode = this.$router.currentRoute.query.subjectCode
this.subjectCode = localStorage.getItem('subjectCode')
this.digitPlaces = Number(localStorage.getItem('digitPlaces'))
window.addEventListener('message', this.receiveMsg)
this.CriterionType = parseInt(localStorage.getItem('CriterionType'))
DicomEvent.$on('setCollapseActive', measureData => {
this.setCollapseActive(measureData)
console.log('setCollapseActive')
})
DicomEvent.$on('getAllUnSaveLesions', (callback) => {
callback([])
})
DicomEvent.$on('refreshQuestions', _ => {
this.refreshQuestions()
})
},
beforeDestroy() {
DicomEvent.$off('setCollapseActive')
DicomEvent.$off('getUnSaveTarget')
DicomEvent.$off('refreshQuestions')
},
methods: {
async initList() {
var i = this.visitTaskList.findIndex(i => i.VisitTaskId === this.lastCanvasTaskId)
if (i > -1) {
this.visitTaskId = this.visitTaskList[i].VisitTaskId
this.taskBlindName = this.visitTaskList[i].TaskBlindName
this.readingTaskState = this.visitTaskList[i].ReadingTaskState
this.isBaseLineTask = this.visitTaskList[i].IsBaseLineTask
this.isCurrentTask = this.visitTaskList[i].IsCurrentTask
if (!this.visitTaskList[i].IsInit) {
var loading = this.$loading({ fullscreen: true })
var triald = this.trialId = this.$router.currentRoute.query.trialId
if (!this.visitTaskList[i].studyListInit) {
await store.dispatch('reading/getStudyInfo', { trialId: triald, subjectVisitId: this.visitTaskList[i].VisitId, visitTaskId: this.visitTaskList[i].VisitTaskId, taskBlindName: this.visitTaskList[i].TaskBlindName })
}
if (!this.visitTaskList[i].readingQuestionsInit) {
await store.dispatch('reading/getReadingQuestionAndAnswer', { trialId: triald, visitTaskId: this.visitTaskList[i].VisitTaskId })
}
if (!this.visitTaskList[i].questionsInit) {
await store.dispatch('reading/getDicomReadingQuestionAnswer', { trialId: triald, visitTaskId: this.visitTaskList[i].VisitTaskId })
}
await store.dispatch('reading/setStatus', { visitTaskId: this.visitTaskList[i].VisitTaskId })
loading.close()
}
this.questions = this.visitTaskList[i].ReadingQuestions
this.setQuestionForm(this.questions)
this.$nextTick(() => {
this.$refs['ecrf'].getQuestions(this.visitTaskId)
this.$refs['ecrf2'].getQuestions(this.visitTaskId)
this.$refs['ecrf3'].getQuestions(this.visitTaskId)
})
}
},
setQuestionForm(questions) {
const type = ['number', 'radio', 'select', 'input', 'textarea', 'calculation']
questions.forEach(item => {
if (type.includes(item.Type)) {
const answer = item.Type === 'number' || item.Type === 'calculation' ? isNaN(parseFloat(item.Answer)) ? null : parseFloat(item.Answer) : item.Answer
this.$set(this.questionForm, item.Id, answer)
}
if (item.Childrens.length > 0) {
this.setQuestionForm(item.Childrens)
}
})
},
async resetQuestions(obj) {
this.setQuestions(this.questions, obj)
await store.dispatch('reading/setReadingQuestionAndAnswer', { questions: this.questions, visitTaskId: this.visitTaskId })
this.getTableQuestions()
},
setQuestions(questions, obj) {
questions.forEach(item => {
if ((item.Type === 'table' || item.Type === 'basicTable') && item.Id === obj.questionId) {
var idx = item.TableQuestions.Answers.findIndex(i => i.RowIndex === obj.rowIndex)
item.TableQuestions.Answers[idx].saveTypeEnum = obj.saveTypeEnum
item.TableQuestions.Answers[idx].area1 = obj.area1
item.TableQuestions.Answers[idx].area2 = obj.area2
item.TableQuestions.Answers[idx].diff = obj.diff
for (const i in obj.anwsers) {
item.TableQuestions.Answers[idx][i] = String(obj.anwsers[i])
}
}
if (item.Childrens.length > 0) {
this.setQuestions(item.Childrens, obj)
}
})
},
getQuestions(questions) {
questions.forEach(item => {
if ((item.Type === 'table' || item.Type === 'basicTable') && item.TableQuestions && item.TableQuestions.Answers.length > 0) {
item.TableQuestions.Answers.forEach(answerObj => {
if (answerObj.RowId) {
var diffVal = this.getQuestionAnswer(item.TableQuestions.Questions, 1003, answerObj)
this.$set(answerObj, 'saveTypeEnum', isNaN(parseFloat(diffVal)) ? 1 : 2)
} else {
this.$set(answerObj, 'saveTypeEnum', 0)
}
})
}
if (item.Childrens.length > 0) {
this.getQuestions(item.Childrens)
}
})
return questions
},
getReadingQuestionAndAnswer() {
return new Promise(async resolve => {
try {
const loading = this.$loading({ fullscreen: true })
await store.dispatch('reading/refreshReadingQuestionAndAnswer', { trialId: this.$router.currentRoute.query.trialId, visitTaskId: this.visitTaskId }).then(() => {
var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
if (idx > -1) {
if (this.visitTaskList[idx].ReadingQuestions.length > 0) {
this.questions = this.visitTaskList[idx].ReadingQuestions
this.setQuestionForm(this.questions)
}
this.readingTaskState = this.visitTaskList[idx].ReadingTaskState
this.isBaseLineTask = this.visitTaskList[idx].IsBaseLineTask
this.isCurrentTask = this.visitTaskList[idx].IsCurrentTask
}
})
loading.close()
resolve()
} catch (e) { console.log(e) }
})
},
getQuestionAnswer(questions, questionMark, answers) {
var idx = questions.findIndex(i => i.QuestionMark === questionMark)
if (idx > -1) {
var questionId = questions[idx].Id
return answers[questionId]
} else {
return ''
}
},
isCanActiveTool(toolName) {
return { isCanActiveTool: true, reason: '' }
},
getMaxRowIndex(answerList) {
if (answerList.length > 0) {
var arr = []
answerList.forEach(item => {
var rowIndex = item.RowIndex
arr.push(parseInt(rowIndex))
})
var max = Math.max.apply(null, arr)
return max
} else {
return 0
}
},
getLesionName(orderMark, rowIndex) {
var lessionName = ''
var rowIndexArr = rowIndex.split('.')
var x = parseInt(rowIndexArr[0])
var y = parseInt(rowIndexArr[1])
if (y > 0) {
y = String.fromCharCode(parseInt(rowIndexArr[1]) - 1 + 65 + 32)
lessionName = `${orderMark}${String(x).padStart(2, '0')}${y}`
} else {
lessionName = `${orderMark}${String(x).padStart(2, '0')}`
}
return lessionName
},
modifyMeasuredData(measureObj) {
},
//
setMeasuredData(measureData) {
},
formItemChange() {
this.formChanged = true
},
limitBlur(qId, valueType) {
const value = this.questionForm[qId]
if (isNaN(parseInt(value))) return
if (valueType === 0) {
this.$set(this.questionForm, qId, parseInt(value))
} else if (valueType === 3) {
this.$set(this.questionForm, qId, parseFloat(value))
} else {
this.$set(this.questionForm, qId, this.numberToFixed(value))
}
},
numberToFixed(v) {
if (this.digitPlaces > -1) {
return isNaN(parseFloat(v)) ? null : `${parseFloat(v).toFixed(this.digitPlaces)}`
} else {
return v
}
},
async handleSave(index) {
const refName = `questions${index}`
const valid = await this.$refs[refName][0].validate()
if (!valid) return
const loading = this.$loading({ fullscreen: true })
var answers = []
for (const k in this.questionForm) {
answers.push({ id: k, answer: this.questionForm[k] })
}
var params = {
visitTaskId: this.visitTaskId,
answers: answers
}
try {
await saveTaskQuestion(8, params)
this.$message.success(this.$t('common:message:savedSuccessfully'))
loading.close()
DicomEvent.$emit('getReportInfo', true)
this.refreshQuestions()
this.formChanged = false
} catch (e) {
console.log(e)
loading.close()
}
},
getECRFQuestions(obj) {
this.$refs['ecrf'].getQuestions(obj.visitTaskId)
this.$refs['ecrf2'].getQuestions(obj.visitTaskId)
this.$refs['ecrf3'].getQuestions(obj.visitTaskId)
},
async resetMeasuredData() {
const confirm = await this.$confirm(
this.$t('trials:dicomReading:message:confirmReset1'),
this.$t('trials:dicomReading:message:confirmReset2'),
{
type: 'warning',
distinguishCancelAndClose: true
}
)
if (confirm !== 'confirm') return
const loading = this.$loading({ fullscreen: true })
try {
const res = await resetReadingTask({ visitTaskId: this.visitTaskId })
this.loading = false
if (res.IsSuccess) {
await this.getReadingQuestionAndAnswer(this.visitTaskId)
const triald = this.$router.currentRoute.query.trialId
await store.dispatch('reading/refreshDicomReadingQuestionAnswer', { trialId: triald, visitTaskId: this.visitTaskId })
this.$refs['ecrf'].getQuestions(this.visitTaskId, true)
this.$refs['ecrf2'].getQuestions(this.visitTaskId, true)
this.$refs['ecrf3'].getQuestions(this.visitTaskId, true)
DicomEvent.$emit('getReportInfo', true)
}
loading.close()
} catch (e) {
loading.close()
console.log(e)
}
},
async refreshQuestions() {
const triald = this.$router.currentRoute.query.trialId
await this.getReadingQuestionAndAnswer(this.visitTaskId)
await store.dispatch('reading/refreshDicomReadingQuestionAnswer', { trialId: triald, visitTaskId: this.visitTaskId })
this.$refs['ecrf'].getQuestions(this.visitTaskId, true)
this.$refs['ecrf2'].getQuestions(this.visitTaskId, true)
this.$refs['ecrf3'].getQuestions(this.visitTaskId, true)
},
receiveMsg(event) {
},
resetFormItemData(v) {
this.qsForm[v] = null
},
setFormItemData(obj) {
this.qsForm[obj.key] = obj.val
if (obj.key === this.m1Id || obj.key === this.m2Id || obj.key === this.m3Id) {
const m1 = this.qsForm[this.m1Id]
const m2 = this.qsForm[this.m2Id]
const m3 = this.qsForm[this.m3Id]
if (isNaN(parseFloat(m1)) || isNaN(parseFloat(m2)) || isNaN(parseFloat(m3))) {
this.$set(this.qsForm, this.avgId, null)
} else {
const avg = (parseFloat(m1) + parseFloat(m2) + parseFloat(m3)) / 3
this.$set(this.qsForm, this.avgId, this.numberToFixed(avg))
}
}
},
handleAddOrEdit(type, row, index) {
this.addOrEdit.title = row.QuestionName
this.qsList = row.TableQuestions.Questions
this.qsForm = {}
this.qsForm.questionId = row.Id
this.qsForm.rowId = type === 'add' ? null : row.TableQuestions.Answers && row.TableQuestions.Answers[index] ? row.TableQuestions.Answers[index].RowId : null
if (type === 'add') {
var maxIndex = this.getMaxRowIndex(row.TableQuestions.Answers)
this.qsForm.rowIndex = `${maxIndex + 1}.00`
} else {
this.qsForm.rowIndex = row.TableQuestions.Answers && row.TableQuestions.Answers[index] ? row.TableQuestions.Answers[index].RowIndex : null
}
row.TableQuestions.Questions.map(i => {
if (i.QuestionMark === 1011) {
this.m1Id = i.Id
}
if (i.QuestionMark === 1012) {
this.m2Id = i.Id
}
if (i.QuestionMark === 1013) {
this.m3Id = i.Id
}
if (i.QuestionMark === 1015) {
this.avgId = i.Id
}
let v = null
if (type === 'add') {
v = i.Type === 'number' ? undefined : null
} else {
v = row.TableQuestions.Answers && row.TableQuestions.Answers[index] ? row.TableQuestions.Answers[index][i.Id] : null
}
this.$set(this.qsForm, i.Id, v)
})
this.addOrEdit.visible = true
this.addOrEdit.lesionType = row.LesionType
},
async saveFormData() {
const valid = await this.$refs.tableQsForm.validate()
if (!valid) return
const loading = this.$loading({ fullscreen: true })
try {
var answers = []
var reg = new RegExp(/^[0-9a-zA-Z]{8}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{12}$/)
for (const k in this.qsForm) {
if (reg.test(k)) {
if (answers.findIndex(i => i.tableQuestionId === k) === -1) {
answers.push({ tableQuestionId: k, answer: this.qsForm[k] })
}
}
}
var params = {
questionId: this.qsForm.questionId,
rowId: this.qsForm.rowId,
rowIndex: this.qsForm.rowIndex,
visitTaskId: this.visitTaskId,
trialId: this.trialId,
answerList: answers
}
if (this.addOrEdit.lesionType === 101) {
params.computationTrigger = 9
}
const res = await submitTableQuestion(params)
if (res.IsSuccess) {
// this.refreshQuestions()
await this.getReadingQuestionAndAnswer(this.visitTaskId)
this.$refs['ecrf'].getQuestions(this.visitTaskId, true)
this.$refs['ecrf2'].getQuestions(this.visitTaskId, true)
this.$refs['ecrf3'].getQuestions(this.visitTaskId, true)
DicomEvent.$emit('getReportInfo', true)
//
this.addOrEdit.visible = false
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
loading.close()
} catch (e) {
console.log(e)
loading.close()
}
},
async handleDelete(row, index) {
const loading = this.$loading({ fullscreen: true })
try {
const confirm = await this.$confirm(
this.$t('trials:reading:warnning:msg48'),
{
type: 'warning',
distinguishCancelAndClose: true
}
)
if (confirm !== 'confirm') return
if (row.TableQuestions.Answers[index].RowId) {
var param = {
visitTaskId: this.visitTaskId,
questionId: row.Id,
rowId: row.TableQuestions.Answers[index].RowId
}
const res = await deleteReadingRowAnswer(param)
if (res.IsSuccess) {
await this.getReadingQuestionAndAnswer(this.visitTaskId)
DicomEvent.$emit('getReportInfo', true)
// ''
this.$message.success(this.$t('common:message:deletedSuccessfully'))
}
}
loading.close()
} catch (e) {
loading.close()
console.log(e)
}
},
uploadTpl(lesionType) {
this.upload.lesionType = lesionType
this.upload.title = `导入( ${this.$fd('LesionType', lesionType)} `
this.upload.visible = true
},
async downloadTpl(lesionType) {
try {
const params = {
visitTaskId: this.visitTaskId
}
if (lesionType === 112) {
await getOCTFCTTemplate(params)
} else {
await getOCTLipidAngleTemplate(params)
}
} catch (e) {
console.log(e)
}
},
async uploadDlgClose() {
await this.getReadingQuestionAndAnswer(this.visitTaskId)
DicomEvent.$emit('getReportInfo', true)
this.upload.visible = false
},
renderHeader(h, { column, $index }) {
const span = document.createElement('span')
span.innerText = column.label
document.body.appendChild(span)
const w = span.getBoundingClientRect().width
column.minWidth = w > 65 ? w + 10 : w > 60 ? w + 15 : w + 30
document.body.removeChild(span)
return h('span', column.label)
}
}
}
</script>
<style lang="scss" scoped>
.measurement-wrapper{
height: 100%;
overflow-y: auto;
// overflow: hidden;
.container{
padding: 10px;
.basic-info{
display: flex;
justify-content: space-between;
align-items: center;
h3{
color: #ddd;
padding: 5px 0px;
margin: 0;
}
i{
color: #fff;
font-size: 22px;
font-weight: bold;
cursor: pointer;
}
}
}
.title{
padding: 5px;
font-weight: bold;
color: #ddd;
font-size: 15px;
}
.add-icon{
display: inline-block;
padding: 5px;
font-weight: bold;
color: #ddd;
font-size: 15px;
border: 1px solid #938b8b;
margin-bottom: 2px;
cursor: pointer;
}
.add-icon:hover{
background-color: #607d8b;
}
.flex-row{
display: flex;
flex-direction: row;
justify-content: space-between;
background-color: #424242;
}
.lesion_list{
position: relative;
/deep/ .el-form-item__label{
color: #c3c3c3;
text-align: left;
}
/deep/ .el-input .el-input__inner{
background-color: transparent;
color: #ddd;
border: 1px solid #5e5e5e;
}
/deep/ .el-textarea__inner{
background-color: transparent;
color: #ddd;
border: 1px solid #5e5e5e;
}
/deep/ .el-form-item{
display: flex;
flex-direction: column;
justify-content: flex-start;
}
/deep/ .el-form-item__content{
flex: 1;
}
/deep/ .el-input.is-disabled .el-input__inner{
background-color: #646464a1;
}
/deep/ .el-select.is-disabled .el-input__inner{
background-color: #646464a1;
}
.el-form-item__content
.el-select{
width: 100%;
}
}
.table-wrapper {
::-webkit-scrollbar {
width: 5px;
height: 8px;
}
::-webkit-scrollbar-thumb {
border-radius: 10px;
background: #d0d0d0;
}
/deep/ .el-table,
.el-table__expanded-cell {
background-color: #000;
color: #fff;
border-color: #444444;
}
/deep/ .el-table th,
.el-table tr {
background-color: #000;
color: #fff;
border-color: #444444;
}
/deep/ .el-table__body tr > td {
background-color: #000 !important;
color: #fff;
border-color: #444444;
}
/deep/ .el-table__body tr:hover > td {
background-color: #858282 !important;
color: #fff;
border-color: #444444;
}
/deep/ .el-table--border th.gutter:last-of-type {
border: none;
}
/deep/ .el-table__fixed-right {
height: 100% !important;
}
// /deep/ .el-table .cell {
// line-height: 20px;
// }
/deep/ .el-table__cell {
padding: 5px 0;
}
/deep/.el-table__fixed-right-patch {
background-color: #000 !important;
border-color: #444444;
}
/deep/.el-table__fixed-body-wrapper tr:hover > td {
background-color: #000 !important;
}
/deep/.el-table--scrollable-x .el-table__body-wrapper {
z-index: 2;
}
}
}
</style>

View File

@ -0,0 +1,297 @@
<template>
<div>
<div
v-if="!!question.GroupName && question.Type==='group'"
>
<h4 style="color: #ddd;padding: 5px 0px;margin: 0;">
{{ language==='en'?question.GroupEnName:question.GroupName }}
</h4>
</div>
<template v-else>
<el-form-item
v-if="(question.ShowQuestion===1 && question.ParentTriggerValueList.includes(String(questionForm[question.ParentId]))) || question.ShowQuestion===0 "
:label="`${question.QuestionName}`"
:prop="question.Id"
:rules="[
{ required: (question.IsRequired === 0 || (question.IsRequired ===1 && question.RelevanceId && (questionForm[question.RelevanceId] === question.RelevanceValue))) && question.Type!=='group' && question.Type!=='summary',
message: ['radio', 'select', 'checkbox'].includes(question.Type) ? $t('common:ruleMessage:select') : $t('common:ruleMessage:specify'), trigger: ['blur', 'change']},
]"
:class="[question.Type==='group' ? 'mb' : (question.Type==='upload' || question.QuestionName.length > 15) ?'uploadWrapper' : '']"
>
<!-- 输入框 -->
<el-input
v-if="question.Type==='input'"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2"
@change="((val)=>{formItemChange(val, question)})"
/>
<!-- 多行文本输入框 -->
<el-input
v-else-if="question.Type==='textarea'"
v-model="questionForm[question.Id]"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"
maxlength="500"
:disabled="readingTaskState >= 2"
@change="((val)=>{formItemChange(val, question)})"
/>
<!-- 下拉框 -->
<el-select
v-else-if="question.Type==='select'"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2 || ((question.TableQuestionType === 2 || question.QuestionGenre === 2) && !!question.DictionaryCode)"
clearable
@change="((val)=>{formItemChange(val, question)})"
>
<template v-if="question.TableQuestionType === 1">
<el-option
v-for="item in organList"
:key="item.Id"
:label="item[question.DataTableColumn]"
:value="item[question.DataTableColumn]"
/>
</template>
<template v-else-if="question.TableQuestionType === 3 || question.QuestionGenre === 3">
<el-option
v-for="item of $d[question.DictionaryCode]"
:key="item.id"
:value="String(item.value)"
:label="item.label"
/>
</template>
<template v-else-if="(question.TableQuestionType === 2 || question.QuestionGenre === 2) && question.DictionaryCode">
<el-option
v-for="item of $d[question.DictionaryCode]"
:key="item.id"
:value="String(item.value)"
:label="item.label"
/>
</template>
<template v-else>
<el-option
v-for="val in question.TypeValue.split('|')"
:key="val"
:label="val"
:value="val"
/>
</template>
</el-select>
<!-- 单选 -->
<el-radio-group
v-else-if="question.Type==='radio'"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2"
@change="((val)=>{formItemChange(val, question)})"
>
<template v-if="question.DictionaryCode">
<el-radio
v-for="item of $d[question.DictionaryCode]"
:key="item.id"
:label="String(item.value)"
>
{{ item.label }}
</el-radio>
</template>
<template v-else-if="question.TypeValue">
<el-radio
v-for="val in question.TypeValue.split('|')"
:key="val"
:label="val"
>
{{ val }}
</el-radio>
</template>
</el-radio-group>
<!-- 复选框 -->
<el-checkbox-group
v-else-if="question.Type==='checkbox'"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2"
@change="((val)=>{formItemChange(val, question)})"
>
<el-checkbox
v-for="val in question.TypeValue.split('|')"
:key="val"
:label="val"
>
{{ val }}
</el-checkbox>
</el-checkbox-group>
<!-- 数值 -->
<!-- <template v-else-if="question.Type==='number'">
<el-input-number
v-if="question.ValueType === 0"
v-model="questionForm[question.Id]"
:precision="0"
:disabled="readingTaskState >= 2"
style="width: 100%"
@change="((val)=>{formItemChange(val, question)})"
/>
<el-input-number
v-else-if="question.ValueType === 3"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2"
style="width: 100%"
@change="((val)=>{formItemChange(val, question)})"
/>
<el-input-number
v-else-if="question.ValueType === 1 || question.ValueType === 2"
v-model="questionForm[question.Id]"
:precision="digitPlaces"
:disabled="readingTaskState >= 2"
style="width: 100%"
@change="((val)=>{formItemChange(val, question)})"
/>
</template> -->
<!-- 数值类型 -->
<el-input
v-else-if="question.Type==='number'"
v-model="questionForm[question.Id]"
:disabled="readingTaskState>=2 "
type="number"
@change="((val)=>{formItemChange(val, question)})"
>
<template v-if="question.Unit" slot="append">
{{ $fd('ValueUnit', parseInt(question.Unit)) }}
</template>
</el-input>
<el-input
v-else-if="question.Type==='calculation'"
v-model="questionForm[question.Id]"
disabled
@change="((val)=>{formItemChange(val, question)})"
>
<template v-if="question.Unit" slot="append">
{{ $fd('ValueUnit', parseInt(question.Unit)) }}
</template>
</el-input>
</el-form-item>
</template>
<QuestionTableFormItem
v-for="item in question.Childrens"
:key="item.Id"
:question="item"
:question-form="questionForm"
:reading-task-state="readingTaskState"
@setFormItemData="setFormItemData"
@resetFormItemData="resetFormItemData"
/>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'QuestionTableFormItem',
props: {
questionForm: {
type: Object,
default() {
return {}
}
},
question: {
type: Object,
default() {
return []
}
},
readingTaskState: {
type: Number,
required: true
}
},
data() {
return {
digitPlaces: 2
}
},
computed: {
...mapGetters(['language'])
},
mounted() {
this.digitPlaces = Number(localStorage.getItem('digitPlaces'))
},
methods: {
formItemChange(v, question) {
if (question.Childrens && question.Childrens.length > 0) {
this.resetChild(question.Childrens)
} else {
if (question.Type === 'number') {
v = this.limitBlur(question.ValueType, v)
}
this.$emit('setFormItemData', { key: question.Id, val: v })
}
},
resetChild(obj) {
obj.forEach(i => {
this.$emit('resetFormItemData', i.Id)
if (i.Childrens && i.Childrens.length > 0) {
this.resetChild(i.Childrens)
}
})
},
limitBlur(valueType, value) {
if (isNaN(parseInt(value))) {
value = null
} else {
if (valueType === 0) {
value = parseInt(value)
} else if (valueType === 3) {
value = parseFloat(value)
} else {
value = this.numberToFixed(value)
}
}
return value
},
numberToFixed(v) {
if (this.digitPlaces > -1) {
return isNaN(parseFloat(v)) ? null : `${parseFloat(v).toFixed(this.digitPlaces)}`
} else {
return v
}
},
resetFormItemData(v) {
this.$emit('resetFormItemData', v)
},
setFormItemData(obj) {
this.$emit('setFormItemData', obj)
},
limitNumbersEvent(e) {
//
let val = e.target.value
if (val.length > 6) val = val.slice(0, 6)
e.target.value = (val.match(/^\d*(\.?\d{0,3})/g)[0]) || null
}
}
}
</script>
<style lang="scss" scoped>
.mb{
margin-bottom: 0px;
}
.disabled{
/deep/ .el-upload--picture-card {
display: none;
}
}
.uploadWrapper{
display: flex;
flex-direction: column !important;
align-items: flex-start;
}
/deep/ .el-input.is-disabled .el-input__inner{
background-color: #646464a1;
}
/deep/ .el-input-group__append, .el-input-group__prepend{
padding: 0 10px;
}
/deep/ .el-form-item__content {
width: 100%;
}
/deep/ .el-select.is-disabled .el-input__inner{
background-color: #646464a1;
}
</style>

View File

@ -0,0 +1,125 @@
<template>
<el-form
ref="uploadExcel"
class="upload-excel-file"
>
<!-- 文件 -->
<el-form-item :label="$t('trials:consistencyCheck:label:file')">
<div class="upload-container">
<el-upload
class="upload-demo"
action
accept=".xlsx,.xls,.csv"
:before-upload="beforeUpload"
:http-request="handleUploadFile"
:on-preview="handlePreview"
:show-file-list="true"
:limit="1"
:on-exceed="handleExceed"
>
<el-button size="small" type="primary">
{{ $t('trials:consistencyCheck:dialogButton:upload') }}
</el-button>
<span
slot="tip"
style="margin-left:10px;"
class="el-upload__tip"
>
({{ $t('trials:consistencyCheck:message:excelFileOnly') }})
</span>
</el-upload>
</div>
</el-form-item>
</el-form>
</template>
<script>
import { uploadOCTFCTTemplate, uploadOCTLipidAngleTemplate } from '@/api/reading'
export default {
props: {
visitTaskId: {
type: String,
required: true
},
lesionType: {
type: Number,
required: true
}
},
data() {
return {
}
},
methods: {
beforeUpload(file) {
//
if (this.checkFileSuffix(file.name)) {
return true
} else {
// Must be xls or xlsx format
this.$alert(this.$t('trials:consistencyCheck:message:xlsx'))
return false
}
},
async handleUploadFile(param) {
const loading = this.$loading({ fullscreen: true })
try {
var data = new FormData()
data.append('file', param.file)
data.append('visitTaskId', this.visitTaskId)
if (this.lesionType === 112) {
await uploadOCTFCTTemplate(data)
} else {
await uploadOCTLipidAngleTemplate(data)
}
this.$emit('close')
this.$message.success('导入成功!')
loading.close()
} catch (e) {
loading.close()
console.log(e)
}
},
handlePreview(file) {
if (file.fullPath) {
window.open(file.fullPath, '_blank')
}
},
handleExceed(files, fileList) {
// Upload is currently limited to 1 file
this.$message.warning(this.$t('trials:consistencyCheck:message:onlyOneFile'))
},
checkFileSuffix(fileName) {
var typeArr = ['xls', 'xlsx', 'csv']
var extendName = fileName.substring(fileName.lastIndexOf('.') + 1).toLocaleLowerCase()
if (typeArr.indexOf(extendName) !== -1) {
return true
} else {
return false
}
}
}
}
</script>
<style lang="scss">
.upload-excel-file{
.upload-container .el-upload--text {
border: none;
width: 80px;
height: 40px;
}
.upload-container .el-input--small {
margin-bottom: 5px;
}
.upload-container .el-icon-circle-check {
color: #428bca;
font-size: 13px;
}
.account_item_clear{
.el-tag__close{
display: none !important;
}
}
}
</style>

View File

@ -1,7 +1,7 @@
<template>
<div class="measurement-wrapper" :style="{'height':height+10+'px'}">
<div class="measurement-wrapper">
<div class="container" :style="{'height':height+'px'}">
<div class="container">
<div class="basic-info">
<h3 v-if="isReadingShowSubjectInfo">
<span v-if="subjectCode">{{ subjectCode }} </span>
@ -147,7 +147,6 @@ export default {
},
data() {
return {
height: window.innerHeight - 140,
questions: [],
activeName: '',
activeItem: {
@ -212,8 +211,6 @@ export default {
callback(list)
console.log('getAllUnSaveLesions')
})
window.addEventListener('resize', this.setHeight)
},
beforeDestroy() {
DicomEvent.$off('setCollapseActive')
@ -802,6 +799,7 @@ export default {
<style lang="scss" scoped>
.measurement-wrapper{
height: 100%;
overflow-y: auto;
// overflow: hidden;

View File

@ -24,7 +24,7 @@
{ required: (question.IsRequired === 0 || (question.IsRequired ===1 && question.RelevanceId && (questionForm[question.RelevanceId] === question.RelevanceValue))) && question.Type!=='group' && question.Type!=='summary',
message: ['radio', 'select', 'checkbox'].includes(question.Type) ? $t('common:ruleMessage:select') : $t('common:ruleMessage:specify'), trigger: ['blur', 'change']},
]"
:class="[question.Type==='group'?'mb':question.Type==='upload'?'uploadWrapper':'']"
:class="[question.Type==='group' ? 'mb' : (question.Type==='upload' || question.QuestionName.length > 15) ?'uploadWrapper' : '']"
>
<template v-if="question.QuestionType === 60 || question.QuestionType === 61">
<div style="display: flex;flex-direction: row;justify-content: flex-start;align-items: center;">
@ -211,16 +211,40 @@
</el-checkbox>
</el-checkbox-group>
<!-- 数值 -->
<el-input-number
<!-- <el-input-number
v-else-if="question.Type==='number'"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2 || isFirstChangeTask"
/>
:precision="2"
/> -->
<template v-else-if="question.Type==='number'">
<el-input-number
v-if="question.ValueType === 0"
v-model="questionForm[question.Id]"
:precision="0"
:disabled="readingTaskState >= 2 || isFirstChangeTask"
/>
<el-input-number
v-else-if="question.ValueType === 3"
v-model="questionForm[question.Id]"
:disabled="readingTaskState >= 2 || isFirstChangeTask"
/>
<el-input-number
v-else-if="question.ValueType === 1 || question.ValueType === 2"
v-model="questionForm[question.Id]"
:precision="digitPlaces"
:disabled="readingTaskState >= 2 || isFirstChangeTask"
/>
</template>
<el-input
v-else-if="question.Type==='calculation'"
v-model="questionForm[question.Id]"
disabled
/>
>
<template v-if="question.Unit" slot="append">
{{ $fd('ValueUnit', parseInt(question.Unit)) }}
</template>
</el-input>
<!-- 上传图像 -->
<el-upload
v-else-if="question.Type==='upload'"
@ -326,7 +350,8 @@ export default {
accept: '.png,.jpg,.jpeg',
imgVisible: false,
imageUrl: '',
urls: []
urls: [],
digitPlaces: 2
}
},
computed: {
@ -352,6 +377,8 @@ export default {
})
}
}
var digitPlaces = Number(localStorage.getItem('digitPlaces'))
this.digitPlaces = digitPlaces === -1 ? this.digitPlaces : digitPlaces
},
methods: {
formItemChange(v, question) {
@ -461,7 +488,7 @@ export default {
}
.uploadWrapper{
display: flex;
flex-direction: column;
flex-direction: column !important;
align-items: flex-start;
}
/deep/ .el-input.is-disabled .el-input__inner{
@ -470,6 +497,9 @@ export default {
/deep/ .el-input-group__append, .el-input-group__prepend{
padding: 0 10px;
}
/deep/ .el-form-item__content {
width: 100%;
}
/deep/ .el-select.is-disabled .el-input__inner{
background-color: #646464a1;
}

View File

@ -88,7 +88,7 @@ export default {
spleenInfo: null,
calculateSpleenStatus: '',
formChanged: false,
digitPlaces: null
digitPlaces: 2
}
},
computed: {
@ -374,7 +374,8 @@ export default {
DicomEvent.$emit('getReportInfo', true)
// DicomEvent.$emit('readingPageUpdate', {})
var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
if (idx > -1 && !this.visitTaskList[idx].IsBaseLineTask) {
const criterionType = parseInt(localStorage.getItem('CriterionType'))
if (idx > -1 && !this.visitTaskList[idx].IsBaseLineTask && (criterionType === 1 || criterionType === 2 || criterionType === 3 || criterionType === 17)) {
if (parseInt(imageQuality) === 2) {
this.$confirm(this.$t('trials:reading:warnning:unableEvaluate'), '', {
type: 'warning'
@ -856,6 +857,7 @@ export default {
.ecrf-wrapper{
/deep/ .el-form-item__label{
color: #c3c3c3;
text-align: left;
}
/deep/ .el-input__inner{
background-color: transparent;

View File

@ -57,6 +57,7 @@
:is-reading-show-subject-info="isReadingShowSubjectInfo"
:is-reading-task-view-in-order="isReadingTaskViewInOrder"
:is-exists-manual="isExistsManual"
:iseCRFShowInDicomReading="iseCRFShowInDicomReading"
@previewCD="previewCD"
/>
</div>
@ -136,6 +137,10 @@ export default {
type: Boolean,
required: true
},
iseCRFShowInDicomReading: {
type: Boolean,
required: true
},
questionFormChangeState: {
type: Boolean,
default() {

View File

@ -1,7 +1,7 @@
<template>
<div class="measurement-wrapper" :style="{'height':height+10+'px'}">
<div class="measurement-wrapper">
<div class="container" :style="{'height':height+'px'}">
<div class="container">
<div class="basic-info">
<h3 v-if="isReadingShowSubjectInfo">
<span v-if="subjectCode">{{ subjectCode }} </span>
@ -169,7 +169,6 @@ export default {
},
data() {
return {
height: window.innerHeight - 140,
questions: [],
activeName: '',
activeItem: {
@ -234,8 +233,6 @@ export default {
DicomEvent.$on('split', measureData => {
this.handleSplit(measureData.RowId, measureData.QuestionId)
})
window.addEventListener('resize', this.setHeight)
},
beforeDestroy() {
DicomEvent.$off('setCollapseActive')
@ -416,9 +413,6 @@ export default {
} catch (e) { console.log(e) }
})
},
setHeight() {
this.height = window.innerHeight - 140
},
getQuestionAnswer(questions, questionMark, answers) {
var idx = questions.findIndex(i => i.QuestionMark === questionMark)
if (idx > -1) {
@ -1065,6 +1059,7 @@ export default {
</script>
<style lang="scss" scoped>
.measurement-wrapper{
height: 100%;
overflow-y: auto;
// overflow: hidden;

View File

@ -1,7 +1,7 @@
<template>
<div class="measurement-wrapper" :style="{'height':height+10+'px'}">
<div class="measurement-wrapper">
<div class="container" :style="{'height':height+'px'}">
<div class="container">
<div class="basic-info">
<h3 v-if="isReadingShowSubjectInfo">
<span v-if="subjectCode">{{ subjectCode }} </span>
@ -165,7 +165,6 @@ export default {
},
data() {
return {
height: window.innerHeight - 140,
questions: [],
activeName: '',
activeItem: {
@ -229,8 +228,6 @@ export default {
DicomEvent.$on('split', measureData => {
this.handleSplit(measureData.RowId, measureData.QuestionId)
})
window.addEventListener('resize', this.setHeight)
},
beforeDestroy() {
DicomEvent.$off('loadMeasurementList')
@ -409,9 +406,6 @@ export default {
} catch (e) { console.log(e) }
})
},
setHeight() {
this.height = window.innerHeight - 140
},
getQuestionAnswer(questions, questionMark, answers) {
var idx = questions.findIndex(i => i.QuestionMark === questionMark)
if (idx > -1) {
@ -994,6 +988,7 @@ export default {
</script>
<style lang="scss" scoped>
.measurement-wrapper{
height: 100%;
overflow-y: auto;
// overflow: hidden;

View File

@ -102,7 +102,7 @@
</div>
</template>
<template slot-scope="scope">
<span :style="{color: ((scope.row.isLymphNodes === 1 && scope.row.QuestionMark === 1) || (scope.row.isLymphNodes === 0 && scope.row.QuestionMark === 0)) && (scope.row.LesionType === 0 || scope.row.LesionType === 5) || (scope.row.QuestionMark === 12) || scope.row.HighlightAnswerList.includes(`${scope.row.Answers[task.VisitTaskId]}`) ? '#f66' : '#fff'}" >
<span :style="{color: ((scope.row.isLymphNodes === 1 && scope.row.QuestionMark === 1) || (scope.row.isLymphNodes === 0 && scope.row.QuestionMark === 0)) && (scope.row.LesionType === 0 || scope.row.LesionType === 5) || (scope.row.QuestionMark === 12) || scope.row.HighlightAnswerList.includes(`${scope.row.Answers[task.VisitTaskId]}`) ? '#f66' : '#fff'}">
<template v-if="task.VisitTaskId === visitTaskId && readingTaskState < 2 && [13,14,15,42].includes(scope.row.QuestionType)">
<!-- 是否存在疾病基线时可修改 -->
<template v-if="task.IsBaseLine && scope.row.QuestionType=== 15">

View File

@ -77,7 +77,7 @@
>
<div>
<div>{{ instance.InstanceNumber }}</div>
<div>{{ `${instance.NumberOfFrames > 0 ? instance.NumberOfFrames : 1} frame` }}</div>
<div>{{ `${instance.NumberOfFrames > 0 ? instance.KeyFramesList.length > 0 ? instance.KeyFramesList.length : instance.NumberOfFrames : 1} frame` }}</div>
</div>
</div>
@ -294,7 +294,14 @@ export default {
store.dispatch('reading/setImageloadedInfo', item)
}
})
var i = res.findIndex(s => s.isCurrentTask)
var i = -1
var isReadingTaskViewInOrder = parseInt(this.$router.currentRoute.query.isReadingTaskViewInOrder)
if (isReadingTaskViewInOrder === 2) {
//
i = res.length === 2 ? 1 : -1
} else {
i = res.findIndex(s => s.isCurrentTask)
}
if (i > -1) {
var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
if (idx > -1) {
@ -342,8 +349,9 @@ export default {
this.studyList = this.visitTaskList[idx].StudyList
var sIdx = this.studyList.findIndex(s => s.IsDicom)
if (sIdx > -1) {
this.studyIndex = sIdx
this.seriesIndex = 0
console.log(this.studyIndex, this.studyIndex)
// this.studyIndex = sIdx
// c = 0
this.activeNames = [`${this.studyList[sIdx].StudyId}`]
//
const i = this.studyList.findIndex(i => i.IsCriticalSequence)
@ -377,8 +385,23 @@ export default {
var isReadingTaskViewInOrder = parseInt(this.$router.currentRoute.query.isReadingTaskViewInOrder)
var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
this.studyList = this.visitTaskList[idx].StudyList
if (this.visitTaskList[idx].IsBaseLineTask || isReadingTaskViewInOrder !== 1) {
// 线
const studyList = this.visitTaskList[idx].StudyList.filter(i => i.IsDicom)
const seriesArr = studyList.map(s => s.SeriesList).flat()
if (isReadingTaskViewInOrder === 2) {
//
if (seriesArr.length === 1) {
seriesList.push(seriesArr[0], seriesArr[0])
this.studyIndex = seriesArr[0].studyIndex
this.seriesIndex = seriesArr[0].seriesIndex
this.activeNames = [`${seriesArr[0].studyId}`]
} else if (seriesArr.length > 1) {
seriesList.push(seriesArr[0], seriesArr[1])
this.studyIndex = seriesArr[1].studyIndex
this.seriesIndex = seriesArr[1].seriesIndex
this.activeNames = [`${seriesArr[1].studyId}`]
}
} else if (this.visitTaskList[idx].IsBaseLineTask || isReadingTaskViewInOrder === 0) {
// 线
const obj = this.getFirstMarkedSeries(this.visitTaskList[idx].MeasureData, [...this.visitTaskList[idx].StudyList])
if (Object.keys(obj).length !== 0) {
this.studyIndex = obj.studyIndex
@ -387,9 +410,13 @@ export default {
this.activeNames = [`${this.studyList[ this.studyIndex].StudyId}`]
this.studyList[ obj.studyIndex].SeriesList[obj.seriesIndex].isFirstRender = true
} else {
//
if (this.studyList.length > 0) {
this.activeNames = [`${this.studyList[0].StudyId}`]
//
this.studyIndex = seriesArr[0].studyIndex
this.seriesIndex = seriesArr[0].seriesIndex
this.activeNames = [`${seriesArr[0].studyId}`]
seriesList.push(seriesArr[0])
// this.activeNames = [`${this.studyList[0].StudyId}`]
}
// DicomEvent.$emit('loadMeasurementList', { visitTaskId: this.visitTaskList[idx].VisitTaskId, taskBlindName: this.visitTaskList[idx].TaskBlindName })
@ -413,6 +440,7 @@ export default {
const firstObj = this.getFirstMarkedSeries(this.visitTaskList[bsIdx].MeasureData, [...this.visitTaskList[bsIdx].StudyList])
seriesList.push(firstObj.series)
const secondObj = this.getSecondMarkedSeries(firstObj, { ...this.visitTaskList[idx] })
this.studyIndex = secondObj.studyIndex
this.seriesIndex = secondObj.seriesIndex
@ -552,22 +580,20 @@ export default {
obj.series = seriesObj.series
obj.seriesId = seriesObj.series.seriesId
obj.isMarked = false
} else {
const sIdx = studyList.findIndex(s => s.IsDicom)
if (sIdx > -1) {
// 5
const series = studyList[sIdx].SeriesList[0]
var imageIdIndex = series.imageIds.length > 1 ? Math.floor(series.imageIds.length / 2) - 1 : 0
obj.studyIndex = sIdx
obj.seriesIndex = 0
obj.series = series
obj.series.imageIdIndex = imageIdIndex >= 0 ? imageIdIndex : 0
obj.seriesId = series.seriesId
obj.isMarked = false
}
}
}
if (Object.keys(obj).length === 0) {
const sIdx = studyList.findIndex(s => s.IsDicom)
if (sIdx > -1) {
obj.studyIndex = sIdx
obj.seriesIndex = 0
obj.series = studyList[obj.studyIndex].SeriesList[obj.seriesIndex]
const imageIdIndex = Math.floor(obj.series.imageIds.length / 2)
obj.series.imageIdIndex = imageIdIndex >= 0 ? imageIdIndex : 0
// obj.seriesId = series.seriesId
obj.isMarked = false
}
}
return obj
},
strSimilarity2Number(s, t) {
@ -668,7 +694,11 @@ export default {
this.currentSeriesIndex = seriesIndex
var idx = this.visitTaskIdx
const imageIds = []
if (instanceInfo.NumberOfFrames && instanceInfo.NumberOfFrames > 1) {
if (instanceInfo.KeyFramesList.length > 0) {
instanceInfo.KeyFramesList.map(i => {
imageIds.push(`wadouri:${localStorage.getItem('location') !== 'USA' ? this.OSSclientConfig.basePath : this.OSSclientConfig.basePath}${instanceInfo.Path}?frame=${i}&instanceId=${instanceInfo.Id}&visitTaskId=${this.visitTaskId}&idx=${studyIndex}|${seriesIndex}|${idx}`)
})
} else if (instanceInfo.NumberOfFrames && instanceInfo.NumberOfFrames > 1) {
for (let j = 0; j < instanceInfo.NumberOfFrames; j++) {
imageIds.push(`wadouri:${localStorage.getItem('location') !== 'USA' ? this.OSSclientConfig.basePath : this.OSSclientConfig.basePath}${instanceInfo.Path}?frame=${j}&instanceId=${instanceInfo.Id}&visitTaskId=${this.visitTaskId}&idx=${studyIndex}|${seriesIndex}|${idx}`)
}

View File

@ -1064,16 +1064,15 @@ export default {
var frame = imageInfo.frame
this.stack.frame = this.stack.isExistMutiFrames ? parseInt(frame) : null
this.stack.instanceId = instanceId
const element = this.$refs.canvas
cornerstone.enable(element)
element.tabIndex = 0
element.focus()
const ToolStateManager = cornerstoneTools.globalImageIdSpecificToolStateManager
ToolStateManager.clearImageIdToolState(dicomSeries.imageIds)
this.toggleClipPlay(false)
this.toolState.viewportInvert = false
this.toolState.dicomInfoVisible = false
const element = this.$refs.canvas
cornerstone.enable(element)
element.tabIndex = 0
element.focus()
var scope = this
// var p = parseInt(new Date().getTime())
// requestPoolManager.loadAndCacheImagePlus(this.stack.imageIds[this.stack.currentImageIdIndex], this.stack.seriesId, p*100).then(image=>{
@ -1637,7 +1636,7 @@ export default {
resetWwwc() {
this.toolState.viewportInvert = false
var viewport = cornerstone.getViewport(this.canvas)
viewport.invert = false
// viewport.invert = false
var image = cornerstone.getImage(this.canvas)
viewport.voi.windowWidth = image.windowWidth
viewport.voi.windowCenter = image.windowCenter

View File

@ -106,7 +106,8 @@
v-if="item.val === 1"
class="divider"
content-position="center"
>{{ ` ${$t("trials:reading:title:preset")}` }}</el-divider>
>{{ ` ${$t('trials:reading:title:preset')}` }}</el-divider
>
</li>
</ul>
</div>
@ -163,7 +164,7 @@
<svg-icon icon-class="magnifier" class="svg-icon" />
</div>
<!-- 缩放 -->
<div class="text">{{ $t("trials:reading:button:zoom") }}</div>
<div class="text">{{ $t('trials:reading:button:zoom') }}</div>
</div>
</el-tooltip>
@ -183,7 +184,7 @@
<svg-icon icon-class="move" class="svg-icon" />
</div>
<!-- 移动 -->
<div class="text">{{ $t("trials:reading:button:move") }}</div>
<div class="text">{{ $t('trials:reading:button:move') }}</div>
</div>
</el-tooltip>
@ -210,7 +211,7 @@
</div>
<!-- 旋转 -->
<div class="text">
{{ $t("trials:reading:button:rotate")
{{ $t('trials:reading:button:rotate')
}}<i class="el-icon-caret-bottom" />
</div>
<div class="dropdown-content">
@ -223,7 +224,8 @@
<a
href="#"
@click.prevent="setDicomCanvasRotate(rotate.val)"
>{{ rotate.label }}</a>
>{{ rotate.label }}</a
>
</li>
</ul>
</div>
@ -258,8 +260,8 @@
<div class="text">
{{
fitType === 1
? $t("trials:reading:button:fitWindow")
: $t("trials:reading:button:fitImage")
? $t('trials:reading:button:fitWindow')
: $t('trials:reading:button:fitImage')
}}
</div>
</div>
@ -282,7 +284,7 @@
</div>
<!-- 同步 -->
<div class="text">
{{ $t("trials:reading:button:imageIndexSync") }}
{{ $t('trials:reading:button:imageIndexSync') }}
</div>
</div>
</el-tooltip>
@ -307,7 +309,7 @@
<svg-icon icon-class="image" class="svg-icon" />
</div>
<!-- 截屏 -->
<div class="text">{{ $t("trials:reading:button:screenShot") }}</div>
<div class="text">{{ $t('trials:reading:button:screenShot') }}</div>
</div>
</el-tooltip>
@ -316,7 +318,7 @@
<div slot="content">
<div v-if="tool.disabledReason">
<h4 style="margin: 0; padding-bottom: 2px">{{ tool.text }}</h4>
<br>{{ tool.disabledReason }}
<br />{{ tool.disabledReason }}
</div>
<div v-else>{{ tool.text }}</div>
</div>
@ -365,7 +367,7 @@
<svg-icon icon-class="clear" class="svg-icon" />
</div>
<!-- 缩放 -->
<div class="text">{{ $t("trials:dicom-show:Eraser") }}</div>
<div class="text">{{ $t('trials:dicom-show:Eraser') }}</div>
</div>
</el-tooltip>
<div class="tool-frame">
@ -467,11 +469,13 @@
<div class="icon" @click.prevent="resetViewport">
<svg-icon icon-class="refresh" class="svg-icon" />
</div>
<div class="text">{{ $t("trials:reading:button:reset") }}</div>
<div class="text">{{ $t('trials:reading:button:reset') }}</div>
</div>
</el-tooltip>
<el-tooltip
v-if="trialCriterion.ImageUploadEnum > 0"
v-if="
trialCriterion.ImageUploadEnum > 0 && currentReadingTaskState < 2
"
v-hasPermi="['role:ir']"
class="item"
effect="dark"
@ -482,7 +486,7 @@
<div class="icon" @click.prevent="openUploadImage('upload')">
<i class="el-icon-upload2 svg-icon" />
</div>
<div class="text">{{ $t("trials:reading:button:upload") }}</div>
<div class="text">{{ $t('trials:reading:button:upload') }}</div>
</div>
</el-tooltip>
<el-tooltip
@ -505,7 +509,7 @@
<div class="icon" @click.prevent="openUploadImage('download')">
<i class="el-icon-download svg-icon" />
</div>
<div class="text">{{ $t("trials:reading:button:download") }}</div>
<div class="text">{{ $t('trials:reading:button:download') }}</div>
</div>
</el-tooltip>
@ -515,17 +519,19 @@
v-if="isExistsManual"
type="text"
@click="previewManuals"
>{{ $t("trials:reading:button:handbooks") }}</el-button>
>{{ $t('trials:reading:button:handbooks') }}</el-button
>
<!-- 临床数据 -->
<el-button
v-if="isExistsClinicalData"
type="text"
@click="previewCD"
>{{ $t("trials:reading:button:clinicalData") }}</el-button>
>{{ $t('trials:reading:button:clinicalData') }}</el-button
>
<!-- <el-button v-if="isExistsNoDicomFile" type="text" @click="previewNoneDicoms">Dicom</el-button> -->
<el-button type="text" @click="previewConfig">{{
$t("trials:reading:button:customCfg")
$t('trials:reading:button:customCfg')
}}</el-button>
</div>
</div>
@ -625,7 +631,7 @@
:fullscreen="isFullscreen"
>
<span slot="title" class="dialog-footer">
<span>{{ $t("trials:reading:button:handbooks") }}</span>
<span>{{ $t('trials:reading:button:handbooks') }}</span>
<span style="position: absolute; right: 20px; font-size: 20px">
<svg-icon
:icon-class="isFullscreen ? 'exit-fullscreen' : 'fullscreen'"
@ -646,12 +652,29 @@
<!-- <button :title="$t('trials:dicom-show:Eraser')" class="btn-link" data-tool="Eraser" @click="setToolActive($event,'Eraser')">-->
<!-- <svg-icon icon-class="clear" style="font-size:20px;" />-->
<!-- </button>-->
<upload-image
<!-- <upload-image
v-if="uploadImageVisible"
:visible.sync="uploadImageVisible"
:subject-id="subjectId"
:criterion="trialCriterion"
:status="uploadStatus"
/> -->
<upload-dicom-and-nonedicom
v-if="uploadImageVisible"
:SubjectId="uploadSubjectId"
:SubjectCode="uploadSubjectCode"
:Criterion="uploadTrialCriterion"
:visible.sync="uploadImageVisible"
:VisitTaskId="taskId"
:isReadingTaskViewInOrder="isReadingTaskViewInOrder"
/>
<download-dicom-and-nonedicom
v-if="downloadImageVisible"
:SubjectId="uploadSubjectId"
:SubjectCode="uploadSubjectCode"
:Criterion="uploadTrialCriterion"
:TaskId="taskId"
:visible.sync="downloadImageVisible"
/>
</div>
</template>
@ -676,7 +699,8 @@ import DicomEvent from './../components/DicomEvent'
import { mapGetters } from 'vuex'
import store from '@/store'
import { getDoctorShortcutKey, getUserWLTemplateList } from '@/api/user'
import uploadImage from '@/components/uploadImage'
import uploadDicomAndNonedicom from '@/components/uploadDicomAndNonedicom'
import downloadDicomAndNonedicom from '@/components/downloadDicomAndNonedicom'
import { getCriterionReadingInfo } from '@/api/trials'
export default {
name: 'DicomViewer',
@ -688,48 +712,49 @@ export default {
Others,
Manuals,
MeasurementList,
'upload-image': uploadImage
'download-dicom-and-nonedicom': downloadDicomAndNonedicom,
'upload-dicom-and-nonedicom': uploadDicomAndNonedicom,
},
props: {
isExistsClinicalData: {
type: Boolean,
default: false
default: false,
},
isExistsNoDicomFile: {
type: Boolean,
default: false
default: false,
},
isExistsManual: {
type: Boolean,
required: true
required: true,
},
isReadingShowSubjectInfo: {
type: Boolean,
required: true
required: true,
},
isReadingTaskViewInOrder: {
type: Number,
required: true
required: true,
},
IseCRFShowInDicomReading: {
type: Boolean,
required: true
required: true,
},
subjectId: {
type: String,
required: true,
default: ''
default: '',
},
trialReadingCriterionId: {
type: String,
required: true,
default: ''
default: '',
},
trialId: {
type: String,
required: true,
default: ''
}
default: '',
},
},
data() {
return {
@ -738,7 +763,7 @@ export default {
{ index: 0, row: 1, col: 1, name: 'A' },
{ index: 1, row: 1, col: 2, name: 'A|A' },
{ index: 2, row: 1, col: 2, name: 'A|B' },
{ index: 3, row: 2, col: 2, name: 'A|A|A|A' }
{ index: 3, row: 2, col: 2, name: 'A|A|A|A' },
// { index: 2, row: 2, col: 1 },
// { index: 3, row: 2, col: 2 }
],
@ -747,7 +772,7 @@ export default {
{ label: this.$t('trials:reading:button:rotateVertical'), val: 2 }, //
{ label: this.$t('trials:reading:button:rotateHorizontal'), val: 3 }, //
{ label: this.$t('trials:reading:button:rotateTurnLeft'), val: 4 }, // 90
{ label: this.$t('trials:reading:button:rotateTurnRight'), val: 5 } // 90
{ label: this.$t('trials:reading:button:rotateTurnRight'), val: 5 }, // 90
],
maxCanvas: 1,
layoutRow: 1,
@ -755,8 +780,8 @@ export default {
currentDicomCanvasIndex: 0,
currentDicomCanvas: {
toolState: {
clipPlaying: false
}
clipPlaying: false,
},
},
colormapsList: [],
rotateList: [],
@ -773,7 +798,7 @@ export default {
text: this.$t('trials:reading:button:length'),
icon: 'length',
isDisabled: false,
disabledReason: ''
disabledReason: '',
},
//
{
@ -781,7 +806,7 @@ export default {
text: this.$t('trials:reading:button:bidirectional'),
icon: 'bidirection',
isDisabled: false,
disabledReason: ''
disabledReason: '',
},
//
{
@ -789,7 +814,7 @@ export default {
text: this.$t('trials:reading:button:rectangle'),
icon: 'rectangle',
isDisabled: false,
disabledReason: ''
disabledReason: '',
},
//
{
@ -797,7 +822,7 @@ export default {
text: this.$t('trials:reading:button:elliptical'),
icon: 'oval',
isDisabled: false,
disabledReason: ''
disabledReason: '',
},
//
{
@ -805,8 +830,8 @@ export default {
text: this.$t('trials:reading:button:arrowAnnotate'),
icon: 'arrow',
isDisabled: false,
disabledReason: ''
}
disabledReason: '',
},
],
fitType: 0,
@ -818,25 +843,25 @@ export default {
label: this.$t('trials:reading:button:wwwcDefault'),
val: -1,
ww: null,
wc: null
wc: null,
}, //
{
label: this.$t('trials:reading:button:wwwcCustom'),
val: 0,
ww: null,
wc: null
wc: null,
}, //
{
label: this.$t('trials:reading:button:wwwcRegion'),
val: 1,
ww: null,
wc: null
wc: null,
}, //
{ label: 'CT Brain', wc: 40, ww: 80 },
{ label: 'CT Lungs', wc: -400, ww: 1500 },
{ label: 'CT Abdomen', wc: 60, ww: 400 },
{ label: 'CT Liver', wc: 40, ww: 400 },
{ label: 'CT Bone', wc: 300, ww: 1500 }
{ label: 'CT Bone', wc: 300, ww: 1500 },
],
activeSeries: {},
seriesStack: [],
@ -844,13 +869,13 @@ export default {
isScrollSync: false,
imageIndexSync: {
sourceCanvas: '',
targetCanvas: []
targetCanvas: [],
},
isFirstRender: false,
customWwc: { visible: false, title: null }, //
personalConfigDialog: {
visible: false,
title: this.$t('trials:reading:button:customCfg')
title: this.$t('trials:reading:button:customCfg'),
}, //
layout: '',
isFirstNotLinked: false,
@ -861,15 +886,20 @@ export default {
manualsDialog: { visible: false },
clipPlaying: false,
fps: 15,
//
uploadImageVisible: false,
trialCriterion: {},
uploadStatus: 'upload'
//
downloadImageVisible: false,
uploadImageVisible: false,
uploadSubjectId: null,
uploadSubjectCode: null,
uploadTrialCriterion: {},
uploadStatus: 'upload',
taskId: '',
}
},
computed: {
...mapGetters(['visitTaskList', 'currentReadingTaskState'])
...mapGetters(['visitTaskList', 'currentReadingTaskState']),
},
watch: {
currentReadingTaskState: {
@ -888,7 +918,7 @@ export default {
this.activeTool = ''
}
}
}
},
},
currentDicomCanvasIndex: {
immediate: true,
@ -905,8 +935,8 @@ export default {
this.clipPlaying = false
this.fps = 15
}
}
}
},
},
},
mounted() {
this.getTrialCriterion()
@ -919,7 +949,7 @@ export default {
// cornerstone.imageCache.setMaximumSizeBytes(maximumSizeInBytes)
this.customWwc = {
visible: false,
title: this.$t('trials:reading:dagTitle:wwwcCustom')
title: this.$t('trials:reading:dagTitle:wwwcCustom'),
}
this.CriterionType = parseInt(localStorage.getItem('CriterionType'))
if (this.CriterionType === 10) {
@ -929,15 +959,15 @@ export default {
text: this.$t('trials:reading:button:arrowAnnotate'),
icon: 'arrow',
isDisabled: false,
disabledReason: ''
disabledReason: '',
},
{
toolName: 'RectangleRoi',
text: this.$t('trials:reading:button:rectangle'),
icon: 'rectangle',
isDisabled: false,
disabledReason: ''
}
disabledReason: '',
},
]
} else if (this.CriterionType === 17) {
this.measuredTools = [
@ -946,22 +976,22 @@ export default {
text: this.$t('trials:reading:button:length'),
icon: 'length',
isDisabled: false,
disabledReason: ''
disabledReason: '',
},
{
toolName: 'RectangleRoi',
text: this.$t('trials:reading:button:rectangle'),
icon: 'rectangle',
isDisabled: false,
disabledReason: ''
disabledReason: '',
},
{
toolName: 'ArrowAnnotate',
text: this.$t('trials:reading:button:arrowAnnotate'),
icon: 'arrow',
isDisabled: false,
disabledReason: ''
}
disabledReason: '',
},
]
}
this.rotateList[0] = '1'
@ -976,7 +1006,7 @@ export default {
window.addEventListener('resize', this.setCanvasStyle)
// DicomEvent.$on('updateImage', (instanceId) => {
// for (let i = 0; i < this.maxCanvas; i++) {
// var stack = this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].stack
// if (stack.studyId) {
@ -993,7 +1023,7 @@ export default {
}
console.log('getMeasureData')
})
DicomEvent.$on('getScreenshots', (callback) => {
var base64Str =
this.$refs[
@ -1041,7 +1071,7 @@ export default {
getTrialCriterion() {
getCriterionReadingInfo({
TrialId: this.trialId,
TrialReadingCriterionId: this.trialReadingCriterionId
TrialReadingCriterionId: this.trialReadingCriterionId,
})
.then((res) => {
this.trialCriterion = res.Result
@ -1049,26 +1079,32 @@ export default {
.catch(() => {})
},
openUploadImage(status) {
const idx = this.visitTaskList.findIndex((i) => i.IsCurrentTask)
if (idx > -1) {
this.taskId = this.visitTaskList[idx].VisitTaskId
}
this.uploadSubjectCode = this.$route.query.subjectCode||localStorage.getItem("subjectCode")
this.uploadSubjectId = this.$route.query.subjectId||localStorage.getItem("subjectId")
this.uploadTrialCriterion = this.trialCriterion
this.uploadStatus = status
this.uploadImageVisible = true
this[`${status}ImageVisible`] = true
},
async getWwcTpl() {
// const loading = this.$loading({ fullscreen: true })
try {
let res = await getUserWLTemplateList()
this.customWwcTpl = []
res.Result.map((i) => {
this.customWwcTpl.push({
label: i.TemplateName,
wc: i.WL,
ww: i.WW
})
res.Result.map((i) => {
this.customWwcTpl.push({
label: i.TemplateName,
wc: i.WL,
ww: i.WW,
})
this.wwwcArr = [...this.defaultWwwc, ...this.customWwcTpl]
} catch(e) {
})
this.wwwcArr = [...this.defaultWwwc, ...this.customWwcTpl]
} catch (e) {
console.log(e)
}
},
async getHotKeys() {
// const loading = this.$loading({ fullscreen: true })
@ -1084,11 +1120,11 @@ export default {
key: item.Keyboardkey,
code: item.Code,
text: item.Text,
shortcutKeyEnum: item.ShortcutKeyEnum
shortcutKeyEnum: item.ShortcutKeyEnum,
})
})
this.bindHotKey()
} catch(e) {
} catch (e) {
console.log(e)
}
},
@ -1105,7 +1141,7 @@ export default {
key: item.keys.controlKey.key,
code: item.keys.controlKey.code,
text: item.keys.text,
shortcutKeyEnum: item.label
shortcutKeyEnum: item.label,
})
})
@ -1148,7 +1184,7 @@ export default {
DicomEvent.$emit('selectSeries', {
seriesId: series.seriesId,
studyId: series.studyId,
offset: -1
offset: -1,
})
} else if (shortcutKeyEnum === 4) {
//
@ -1157,7 +1193,7 @@ export default {
DicomEvent.$emit('selectSeries', {
seriesId: series.seriesId,
studyId: series.studyId,
offset: 1
offset: 1,
})
} else if (shortcutKeyEnum === 5) {
//
@ -1287,7 +1323,7 @@ export default {
top: '72px',
left: '205px',
right: '350px',
zIndex: 10
zIndex: 10,
}
this.canvasW = window.innerWidth - 570 + 'px'
this.canvasH = window.innerHeight - 130 + 'px'
@ -1387,10 +1423,7 @@ export default {
// this.$refs['measurementList'].initPage(dicomSeries)
store.dispatch('reading/setActiveSeries', dicomSeries)
if (this.currentDicomCanvasIndex === this.maxCanvas - 1) {
store.dispatch(
'reading/setLastCanvasTaskId',
dicomSeries.visitTaskId
)
store.dispatch('reading/setLastCanvasTaskId', dicomSeries.visitTaskId)
}
})
},
@ -1421,10 +1454,7 @@ export default {
store.dispatch('reading/setActiveSeries', dicomSeries)
if (this.currentDicomCanvasIndex === this.maxCanvas - 1) {
store.dispatch(
'reading/setLastCanvasTaskId',
dicomSeries.visitTaskId
)
store.dispatch('reading/setLastCanvasTaskId', dicomSeries.visitTaskId)
}
})
},
@ -1474,7 +1504,7 @@ export default {
visitTaskId = this.visitTaskList[i].VisitTaskId
}
this.getFirstSeries(activeSeries, visitTaskId).then(
async(baseSerires) => {
async (baseSerires) => {
this.seriesStack = [baseSerires, activeSeries]
this.currentDicomCanvasIndex = 1
store.dispatch(
@ -1502,7 +1532,7 @@ export default {
activeSeries,
activeSeries,
activeSeries,
activeSeries
activeSeries,
]
this.currentDicomCanvasIndex = 3
store.dispatch(
@ -1521,9 +1551,7 @@ export default {
for (let i = 0; i < this.maxCanvas && i < seriesStack.length; i++) {
this.canvasObj[i] = seriesStack[i]
let s = Object.assign({}, seriesStack[i])
promiseArr.push(
this.$refs[`dicomCanvas${i}`][0].loadImageStack(s)
)
promiseArr.push(this.$refs[`dicomCanvas${i}`][0].loadImageStack(s))
}
Promise.all(promiseArr)
.then(() => {
@ -1585,24 +1613,24 @@ export default {
trialId: this.trialId,
subjectVisitId: this.visitTaskList[taskIdx].VisitId,
visitTaskId: this.visitTaskList[taskIdx].VisitTaskId,
taskBlindName: this.visitTaskList[taskIdx].TaskBlindName
taskBlindName: this.visitTaskList[taskIdx].TaskBlindName,
})
}
if (!this.visitTaskList[taskIdx].readingQuestionsInit) {
await store.dispatch('reading/getReadingQuestionAndAnswer', {
trialId: this.trialId,
visitTaskId: this.visitTaskList[taskIdx].VisitTaskId
visitTaskId: this.visitTaskList[taskIdx].VisitTaskId,
})
}
if (!this.visitTaskList[taskIdx].questionsInit) {
await store.dispatch('reading/getDicomReadingQuestionAnswer', {
trialId: this.trialId,
visitTaskId: this.visitTaskList[taskIdx].VisitTaskId
visitTaskId: this.visitTaskList[taskIdx].VisitTaskId,
})
}
await store.dispatch('reading/setStatus', {
visitTaskId: this.visitTaskList[taskIdx].VisitTaskId
visitTaskId: this.visitTaskList[taskIdx].VisitTaskId,
})
}
firstAddSeries = this.getSeriesInfoByMark(firstAddVisitTaskId, obj)
@ -1696,8 +1724,7 @@ export default {
var studyList = this.visitTaskList[index].StudyList
var studyId = this.visitTaskList[index].MeasureData[idx].StudyId
var seriesId = this.visitTaskList[index].MeasureData[idx].SeriesId
var instanceId =
this.visitTaskList[index].MeasureData[idx].InstanceId
var instanceId = this.visitTaskList[index].MeasureData[idx].InstanceId
var studyIdx = studyList.findIndex(
(study) => study.StudyId === studyId
)
@ -1706,10 +1733,15 @@ export default {
(s) => s.seriesId === seriesId
)
if (seriesIdx > -1) {
var series = studyList[studyIdx].SeriesList[seriesIdx];
let frame = this.visitTaskList[index].MeasureData[idx].MeasureData.frame
let filterStr = series.isExistMutiFrames ? `frame=${frame}&instanceId=${instanceId}` : `instanceId=${instanceId}`
var instanceIdx = series.imageIds.findIndex(imageId => imageId.includes(filterStr))
var series = studyList[studyIdx].SeriesList[seriesIdx]
let frame =
this.visitTaskList[index].MeasureData[idx].MeasureData.frame
let filterStr = series.isExistMutiFrames
? `frame=${frame}&instanceId=${instanceId}`
: `instanceId=${instanceId}`
var instanceIdx = series.imageIds.findIndex((imageId) =>
imageId.includes(filterStr)
)
// var instanceIdx = series.instanceList.findIndex(
// (imageId) => !!~imageId.indexOf(instanceId)
// );
@ -1738,16 +1770,14 @@ export default {
i.description,
baseSeries.description
),
index
index,
}
})
similarArr = similarArr.sort((a, b) => {
return b.similar - a.similar
})
const i =
similarArr[0] && similarArr[0].similar > 0.85
? similarArr[0].index
: -1
similarArr[0] && similarArr[0].similar > 0.85 ? similarArr[0].index : -1
if (i > -1) {
obj = seriesList[i]
}
@ -1822,7 +1852,7 @@ export default {
trialId: this.trialId,
subjectVisitId: this.visitTaskList[index].VisitId,
visitTaskId: this.visitTaskList[index].VisitTaskId,
taskBlindName: this.visitTaskList[index].TaskBlindName
taskBlindName: this.visitTaskList[index].TaskBlindName,
})
await store.dispatch(
@ -1841,16 +1871,14 @@ export default {
i.description,
baseSeries.description
),
index
index,
}
})
similarArr = similarArr.sort((a, b) => {
return b.similar - a.similar
})
const i =
similarArr[0] && similarArr[0].similar > 0.85
? similarArr[0].index
: -1
similarArr[0] && similarArr[0].similar > 0.85 ? similarArr[0].index : -1
if (i > -1) {
obj = seriesList[i]
}
@ -2025,9 +2053,7 @@ export default {
},
//
enter(e, toolName) {
var i = this.measuredTools.findIndex(
(item) => item.toolName === toolName
)
var i = this.measuredTools.findIndex((item) => item.toolName === toolName)
if (i === -1) return
var isCurrentTask =
this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0]
@ -2089,8 +2115,11 @@ export default {
`dicomCanvas${this.currentDicomCanvasIndex}`
][0].setToolActive(toolName)
}
} else if (dicomSeries.isCurrentTask &&
isMeasuredTool && dicomSeries.readingTaskState >= 2) {
} else if (
dicomSeries.isCurrentTask &&
isMeasuredTool &&
dicomSeries.readingTaskState >= 2
) {
if (this.activeTool === toolName) {
this.$refs[
`dicomCanvas${this.currentDicomCanvasIndex}`
@ -2162,9 +2191,7 @@ export default {
][0].fitToWindow()
} else if (toolName === 'fitToImage') {
this.fitType = 1
this.$refs[
`dicomCanvas${this.currentDicomCanvasIndex}`
][0].fitToImage()
this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].fitToImage()
}
this.activeTool = toolName
},
@ -2181,7 +2208,9 @@ export default {
const isLoaded = this.getSeriesLoadStatus()
if (!isLoaded) return
this.clipPlaying = isPlay
this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].setFps(this.fps)
this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].setFps(
this.fps
)
this.$refs[
`dicomCanvas${this.currentDicomCanvasIndex}`
][0].toggleClipPlay(isPlay)
@ -2297,8 +2326,8 @@ export default {
previewManuals() {
this.isFullscreen = false
this.manualsDialog.visible = true
}
}
},
},
}
</script>
<style lang="scss" scoped>

View File

@ -25,14 +25,14 @@
</div>
<div class="questions-footer">
<i class="el-icon-question feedback-icon" @click="openFeedBackTable" />
<el-button v-if="readingTaskState<2" style="margin-right: 10px" size="small" @click="handleSave(true)">
<el-button v-if="readingTaskState<2" style="margin-right: 10px" type="primary" size="small" @click="handleSave(true)">
{{$t('common:button:save')}}
</el-button>
<el-button v-if="readingTaskState<2 && IseCRFShowInDicomReading" type="primary" size="small" @click="skipTask">
<el-button v-if="readingTaskState<2 && IseCRFShowInDicomReading" type="primary" style="margin-right: 10px" size="small" @click="skipTask">
<!-- 跳过 -->
{{ $t('trials:readingReport:button:skip') }}
</el-button>
<el-button style="margin-right: 10px" size="small" v-if="readingTaskState<2 && IseCRFShowInDicomReading" @click="handleConfirm">
<el-button style="margin-right: 10px" type="primary" size="small" v-if="readingTaskState<2 && IseCRFShowInDicomReading" @click="handleConfirm">
{{ $t('common:button:submit') }}</el-button>
</div>
<!-- 签名框 -->
@ -244,6 +244,7 @@ export default {
params.StudyId = params.questionInfo.StudyId
params.SeriesId = params.questionInfo.SeriesId
params.InstanceId = params.questionInfo.InstanceId
params.NumberOfFrames = measureData.frame
submitCustomTag(params).then(() => {
this.measuredDataVisible = false
DicomEvent.$emit('updateImage')
@ -282,6 +283,7 @@ export default {
} else {
let params = JSON.parse(JSON.stringify(this.measureData))
params.measureData = JSON.stringify(this.measureData)
params.NumberOfFrames = this.measureData.frame
submitCustomTag(params).then(async (res) => {
this.measuredDataVisible = false
this.form.measuredDataName = ''

View File

@ -239,7 +239,7 @@
<!-- </span>-->
<!-- </div>-->
</el-upload>
<el-dialog
<!-- <el-dialog
v-if="question.Type==='upload'"
append-to-body
:visible.sync="imgVisible"
@ -250,7 +250,20 @@
加载中<span class="dot">...</span>
</div>
</el-image>
</el-dialog>
</el-dialog> -->
<viewer
v-if="question.Type==='upload' && imgVisible"
:ref="imageUrl"
style="margin:0 10px;"
:images="[imageUrl]"
>
<img
v-show="false"
crossorigin="anonymous"
:src="imageUrl"
alt="Image"
>
</viewer>
</el-form-item>
</template>
@ -801,6 +814,9 @@ export default {
}else{
this.imageUrl = this.OSSclientConfig.basePath + file.url
this.imgVisible = true
this.$nextTick(()=>{
this.$refs[this.imageUrl].$viewer.show()
})
}
},
//

View File

@ -138,27 +138,41 @@
>
<i slot="default" class="el-icon-plus" />
<div slot="file" slot-scope="{file}">
<img
class="el-upload-list__item-thumbnail"
:src="OSSclientConfig.basePath + file.url"
alt=""
<viewer
:ref="file.url"
:images="[imageUrl]"
style="
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
"
>
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-preview"
@click="handlePictureCardPreview(file)"
>
<i class="el-icon-zoom-in" />
</span>
<img
class="el-upload-list__item-thumbnail"
:src="OSSclientConfig.basePath + file.url"
crossOrigin="anonymous"
alt=""
style="max-width: 100%; max-height: 100%"
/>
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-preview"
@click="handlePictureCardPreview(file)"
>
<i class="el-icon-zoom-in" />
</span>
<span
v-if="readingTaskState < 2"
class="el-upload-list__item-delete"
@click="handleRemove(file)"
>
<i class="el-icon-delete" />
<span
v-if="readingTaskState < 2"
class="el-upload-list__item-delete"
@click="handleRemove(file)"
>
<i class="el-icon-delete" />
</span>
</span>
</span>
</viewer>
</div>
</el-upload>
<el-dialog
@ -308,7 +322,7 @@ export default {
window.open(this.OSSclientConfig.basePath + file.url,'_blank')
}else{
this.imageUrl = this.OSSclientConfig.basePath + file.url
this.imgVisible = true
this.$refs[file.url].$viewer.show();
}
},
//

View File

@ -148,27 +148,41 @@
>
<i slot="default" class="el-icon-plus" />
<div slot="file" slot-scope="{file}">
<img
class="el-upload-list__item-thumbnail"
:src="OSSclientConfig.basePath + file.url"
alt=""
<viewer
:ref="file.url"
:images="[imageUrl]"
style="
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
"
>
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-preview"
@click="handlePictureCardPreview(file)"
>
<i class="el-icon-zoom-in" />
</span>
<img
class="el-upload-list__item-thumbnail"
:src="OSSclientConfig.basePath + file.url"
crossOrigin="anonymous"
alt=""
style="max-width: 100%; max-height: 100%"
/>
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-preview"
@click="handlePictureCardPreview(file)"
>
<i class="el-icon-zoom-in" />
</span>
<span
v-if="readingTaskState < 2"
class="el-upload-list__item-delete"
@click="handleRemove(file)"
>
<i class="el-icon-delete" />
<span
v-if="readingTaskState < 2"
class="el-upload-list__item-delete"
@click="handleRemove(file)"
>
<i class="el-icon-delete" />
</span>
</span>
</span>
</viewer>
</div>
</el-upload>
<el-dialog
@ -492,7 +506,8 @@ export default {
window.open(this.OSSclientConfig.basePath + file.url,'_blank')
}else{
this.imageUrl = this.OSSclientConfig.basePath + file.url
this.imgVisible = true
// this.imgVisible = true
this.$refs[file.url].$viewer.show()
}
},

View File

@ -82,7 +82,7 @@ export default {
var answers = []
for (const k in this.questionForm) {
if (this.questionForm[k] instanceof Array) {} else {
answers.push({ id: k, answer: this.questionForm[k].toString() })
answers.push({ id: k, answer: this.questionForm[k] })
}
}
var params = {

View File

@ -14,41 +14,43 @@
>
<i slot="default" class="el-icon-plus" />
<div slot="file" slot-scope="{file}">
<img
class="el-upload-list__item-thumbnail"
crossOrigin="Anonymous"
:src="OSSclientConfig.basePath + file.url"
alt=""
<viewer
:ref="file.url"
:images="[imageUrl]"
style="
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
"
>
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-preview"
@click="handlePictureCardPreview(file)"
>
<i class="el-icon-zoom-in" />
</span>
<img
class="el-upload-list__item-thumbnail"
:src="OSSclientConfig.basePath + file.url"
crossOrigin="anonymous"
alt=""
style="max-width: 100%; max-height: 100%"
/>
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-preview"
@click="handlePictureCardPreview(file)"
>
<i class="el-icon-zoom-in" />
</span>
<span
v-if="readingTaskState < 2"
class="el-upload-list__item-delete"
@click="handleRemove(file)"
>
<i class="el-icon-delete" />
</span>
</span>
<span
v-if="readingTaskState < 2"
class="el-upload-list__item-delete"
@click="handleRemove(file)"
>
<i class="el-icon-delete" />
</span>
</span>
</viewer>
</div>
</el-upload>
<el-dialog
append-to-body
:visible.sync="imgVisible"
width="600px"
>
<el-image :src="imageUrl" width="100%" crossOrigin="Anonymous">
<div slot="placeholder" class="image-slot">
加载中<span class="dot">...</span>
</div>
</el-image>
</el-dialog>
</div>
</template>
@ -142,7 +144,8 @@ name: "CustomizeReportPageUpload",
window.open(this.OSSclientConfig.basePath + file.url,'_blank')
}else{
this.imageUrl = this.OSSclientConfig.basePath + file.url
this.imgVisible = true
// this.imgVisible = true
this.$refs[file.url].$viewer.show()
}
},
//

View File

@ -87,7 +87,7 @@
</div> -->
<div>
<div>{{ instance.InstanceNumber }}</div>
<div>{{ `${instance.NumberOfFrames > 0 ? instance.NumberOfFrames : 1} frame` }}</div>
<div>{{ `${instance.NumberOfFrames > 0 ? instance.KeyFramesList.length > 0 ? instance.KeyFramesList.length : instance.NumberOfFrames : 1} frame` }}</div>
</div>
</div>
@ -279,7 +279,14 @@ export default {
store.dispatch('reading/setImageloadedInfo', item)
}
})
var i = res.findIndex(s => s.isCurrentTask)
var i = -1
var isReadingTaskViewInOrder = parseInt(this.$router.currentRoute.query.isReadingTaskViewInOrder)
if (isReadingTaskViewInOrder === 2) {
//
i = res.length === 2 ? 1 : -1
} else {
i = res.findIndex(s => s.isCurrentTask)
}
if (i > -1) {
var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
if (idx > -1) {
@ -356,7 +363,22 @@ export default {
var isReadingTaskViewInOrder = JSON.parse(this.$router.currentRoute.query.isReadingTaskViewInOrder)
var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
this.studyList = this.visitTaskList[idx].StudyList
if (this.visitTaskList[idx].IsBaseLineTask || isReadingTaskViewInOrder !== 1) {
if (isReadingTaskViewInOrder === 2) {
//
const studyList = this.visitTaskList[idx].StudyList.filter(i => i.IsDicom)
const seriesArr = studyList.map(s => s.SeriesList).flat()
if (seriesArr.length === 1) {
seriesList.push(seriesArr[0], seriesArr[0])
this.studyIndex = seriesArr[0].studyIndex
this.seriesIndex = seriesArr[0].seriesIndex
this.activeNames = [`${seriesArr[0].studyId}`]
} else if (seriesArr.length > 1) {
seriesList.push(seriesArr[0], seriesArr[1])
this.studyIndex = seriesArr[1].studyIndex
this.seriesIndex = seriesArr[1].seriesIndex
this.activeNames = [`${seriesArr[1].studyId}`]
}
} else if (this.visitTaskList[idx].IsBaseLineTask || isReadingTaskViewInOrder === 0){
// 线
const obj = this.getFirstMarkedSeries(this.visitTaskList[idx].MeasureData, [...this.visitTaskList[idx].StudyList])
if (Object.keys(obj).length !== 0) {
@ -533,19 +555,18 @@ export default {
obj.series = seriesObj.series
obj.seriesId = seriesObj.series.seriesId
obj.isMarked = false
} else {
const sIdx = studyList.findIndex(s => s.IsDicom)
if (sIdx > -1) {
// 5
const series = studyList[sIdx].SeriesList[0]
var imageIdIndex = series.imageIds.length > 1 ? Math.floor(series.imageIds.length / 2) - 1 : 0
obj.studyIndex = sIdx
obj.seriesIndex = 0
obj.series = series
obj.series.imageIdIndex = imageIdIndex >= 0 ? imageIdIndex : 0
obj.seriesId = series.seriesId
obj.isMarked = false
}
}
}
if (Object.keys(obj).length === 0) {
const sIdx = studyList.findIndex(s => s.IsDicom)
if (sIdx > -1) {
obj.studyIndex = sIdx
obj.seriesIndex = 0
obj.series = studyList[obj.studyIndex].SeriesList[obj.seriesIndex]
const imageIdIndex = Math.floor(obj.series.imageIds.length / 2)
obj.series.imageIdIndex = imageIdIndex >= 0 ? imageIdIndex : 0
// obj.seriesId = series.seriesId
obj.isMarked = false
}
}
@ -652,7 +673,11 @@ export default {
this.studyList[studyIndex].SeriesList[seriesIndex].measureData = this.measureData
var dicomStatck = Object.assign({},this.studyList[studyIndex].SeriesList[seriesIndex])
const imageIds = []
if (instanceInfo.NumberOfFrames && instanceInfo.NumberOfFrames > 1) {
if (instanceInfo.KeyFramesList.length > 0) {
instanceInfo.KeyFramesList.map(i => {
imageIds.push(`wadouri:${localStorage.getItem('location') !== 'USA' ? this.OSSclientConfig.basePath : this.OSSclientConfig.basePath}${instanceInfo.Path}?frame=${i}&instanceId=${instanceInfo.Id}&visitTaskId=${this.visitTaskId}&idx=${studyIndex}|${seriesIndex}|${idx}`)
})
} else if (instanceInfo.NumberOfFrames && instanceInfo.NumberOfFrames > 1) {
for (let j = 0; j < instanceInfo.NumberOfFrames; j++) {
imageIds.push(`wadouri:${localStorage.getItem('location') !== 'USA' ? this.OSSclientConfig.basePath : this.OSSclientConfig.basePath}${instanceInfo.Path}?frame=${j}&instanceId=${instanceInfo.Id}&visitTaskId=${this.visitTaskId}&idx=${studyIndex}|${seriesIndex}|${idx}`)
}

View File

@ -21,7 +21,7 @@
</div>
<div class="right-wrapper">
<div class="right-content">
<iframe v-if="selected.filePath" :src="`/static/pdfjs/web/viewer.html?file=${OSSclientConfig.basePath}${selected.filePath}?userName=${currentUser}`" width="100%" height="100%" frameborder="0" />
<iframe v-if="selected.filePath" :src="`/static/pdfjs/web/viewer.html?file=${OSSclientConfig.basePath}${selected.filePath}?userName=${currentUser}&COMPANY=${COMPANY}`" width="100%" height="100%" frameborder="0" />
</div>
</div>
</div>
@ -44,7 +44,8 @@ export default {
},
fileList: [],
loading: false,
currentUser: zzSessionStorage.getItem('userName')
currentUser: zzSessionStorage.getItem('userName'),
COMPANY:process.env.VUE_APP_COMPANY_NAME
}
},
mounted() {

Some files were not shown because too many files have changed in this diff Show More