493 Commits

Author SHA1 Message Date
caiyiling 7d9cededbf Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2024-11-12 16:25:15 +08:00
caiyiling b691219db8 重阅跟踪更改 2024-11-12 16:24:49 +08:00
wangxiaoshuang f34faa7db3 简历问题修复
continuous-integration/drone/push Build is passing
2024-11-12 14:59:46 +08:00
wangxiaoshuang ede1455bc4 1
continuous-integration/drone/push Build is passing
2024-11-12 14:18:00 +08:00
wangxiaoshuang 1e36464182 邮件模板新增查询条件
continuous-integration/drone/push Build is passing
2024-11-12 14:02:42 +08:00
wangxiaoshuang 6a52f9571d Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is running
2024-11-12 13:59:40 +08:00
wangxiaoshuang bd85918f8d 简历预览适应症显示问题 2024-11-12 13:59:27 +08:00
caiyiling 23069c7ef7 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2024-11-12 13:19:36 +08:00
caiyiling d866153ccc 非dicom阅片更改 2024-11-12 13:19:12 +08:00
wangxiaoshuang cb4d44ef74 1
continuous-integration/drone/push Build is passing
2024-11-12 11:47:24 +08:00
wangxiaoshuang e99e69d0dd 1
continuous-integration/drone/push Build is passing
2024-11-12 11:20:15 +08:00
wangxiaoshuang 55bb2493e4 手机号校验修改
continuous-integration/drone/push Build is passing
2024-11-12 11:13:45 +08:00
caiyiling 041c23daf6 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2024-11-12 10:50:34 +08:00
caiyiling 7c01819ced 自动计算bug修复 2024-11-12 10:50:15 +08:00
wangxiaoshuang ff97099c3f 简历预览文件方式变更
continuous-integration/drone/push Build is passing
2024-11-12 10:32:58 +08:00
wangxiaoshuang bff239179f 简历列表按钮调整
continuous-integration/drone/push Build is passing
2024-11-12 10:13:01 +08:00
caiyiling 237e3eed60 1
continuous-integration/drone/push Build is passing
2024-11-11 16:34:38 +08:00
caiyiling 1bc78dffc1 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2024-11-11 15:52:01 +08:00
caiyiling e21d3b331a 自定义标准与运算更改 2024-11-11 15:51:32 +08:00
wangxiaoshuang 6992dc6a2b Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2024-11-11 15:24:26 +08:00
wangxiaoshuang d53130e994 1 2024-11-11 15:24:19 +08:00
caiyiling 631c903135 1
continuous-integration/drone/push Build is running
2024-11-11 15:17:57 +08:00
caiyiling 994c97479b 自动分类补充数值类型
continuous-integration/drone/push Build is running
2024-11-11 15:16:14 +08:00
caiyiling 33b172fb66 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is running
2024-11-11 15:11:58 +08:00
caiyiling 01f6049f21 1 2024-11-11 15:11:40 +08:00
wangxiaoshuang 07fd3ab863 项目中添加模板时,搜索条件展示的用户类型只显示与业务相关的用户类型
continuous-integration/drone/push Build is passing
2024-11-11 15:05:39 +08:00
caiyiling c51dedbd1e Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2024-11-11 14:41:38 +08:00
caiyiling 5619412556 自定义标准运算类型补充 2024-11-11 14:40:21 +08:00
wangxiaoshuang 34267bc007 附件浏览组件统一
continuous-integration/drone/push Build is passing
2024-11-11 13:24:28 +08:00
wangxiaoshuang 286ce9656c 休假数据计算
continuous-integration/drone/push Build is failing
2024-11-11 11:27:10 +08:00
wangxiaoshuang db856c6aa9 支付信息数据校验
continuous-integration/drone/push Build is passing
2024-11-11 11:08:16 +08:00
wangxiaoshuang 682d07cc6d 编辑历史人员的临床试验经历报错
continuous-integration/drone/push Build is passing
2024-11-11 10:42:44 +08:00
wangxiaoshuang af27788e4c 简历中英文管理
continuous-integration/drone/push Build is running
2024-11-11 10:40:24 +08:00
caiyiling bc6f15388d 自动分类问题更改
continuous-integration/drone/push Build is passing
2024-11-08 17:47:01 +08:00
caiyiling 6714d2a3c2 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2024-11-08 10:17:39 +08:00
caiyiling 76616e257f 自定义全局阅片更改 2024-11-08 10:17:13 +08:00
wangxiaoshuang 1d35efd4aa 术语配置默认同步字典
continuous-integration/drone/push Build is passing
2024-11-07 16:04:26 +08:00
wangxiaoshuang de0051a8c3 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web 2024-11-07 15:59:03 +08:00
wangxiaoshuang 8c5297e401 术语配置修改 2024-11-07 15:58:42 +08:00
caiyiling 55b39a6b29 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2024-11-07 15:07:14 +08:00
caiyiling ceb1425908 自定义保存表格问题更改 2024-11-07 15:06:52 +08:00
wangxiaoshuang 093cffa298 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2024-11-07 13:35:01 +08:00
wangxiaoshuang eb5561b611 术语配置 2024-11-07 13:34:44 +08:00
caiyiling cea867bdaf Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2024-11-07 11:35:58 +08:00
caiyiling d1958e3bef 系统标准预览更改 2024-11-07 11:35:32 +08:00
wangxiaoshuang a79796df4c 简历bug修复
continuous-integration/drone/push Build is passing
2024-11-06 14:40:46 +08:00
wangxiaoshuang 327623e8b1 1
continuous-integration/drone/push Build is passing
2024-11-06 14:22:07 +08:00
wangxiaoshuang 19c25d369e 简历增加性别展示
continuous-integration/drone/push Build is running
2024-11-06 14:19:53 +08:00
wangxiaoshuang ea9c1ca90d 邮件设置默认pd/入组添加
continuous-integration/drone/push Build is passing
2024-11-06 14:08:36 +08:00
wangxiaoshuang 9bd6c1f433 bug修复
continuous-integration/drone/push Build is passing
2024-11-06 09:13:37 +08:00
wangxiaoshuang 62d5b39036 术语配置测试
continuous-integration/drone/push Build is passing
2024-11-04 17:58:26 +08:00
wangxiaoshuang 03e390013e Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2024-11-04 17:47:21 +08:00
wangxiaoshuang b65b93a5ab 简历预览区分系统、项目 2024-11-04 17:47:10 +08:00
caiyiling 94d626a453 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is running
2024-11-04 17:46:34 +08:00
caiyiling 13d0f6d796 系统标准更改 2024-11-04 17:46:13 +08:00
wangxiaoshuang 0c90ffb514 1
continuous-integration/drone/push Build is passing
2024-11-04 17:32:46 +08:00
wangxiaoshuang 7a5693a468 1
continuous-integration/drone/push Build is passing
2024-11-04 17:20:23 +08:00
wangxiaoshuang b8baa1b5d8 1
continuous-integration/drone/push Build is running
2024-11-04 17:15:25 +08:00
wangxiaoshuang c02308dd23 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2024-11-04 16:38:06 +08:00
wangxiaoshuang 49a915fb89 工作社会兼职保存 2024-11-04 16:37:00 +08:00
caiyiling 07b5296a4b Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is running
2024-11-04 16:34:52 +08:00
caiyiling 5eb00c4111 问题标识添加清除按钮 2024-11-04 16:34:20 +08:00
wangxiaoshuang 39b5333f9c 支付信息保存
continuous-integration/drone/push Build is passing
2024-11-04 15:46:10 +08:00
wangxiaoshuang 79473f6638 阅片人列表操作栏固定
continuous-integration/drone/push Build is passing
2024-11-04 15:24:37 +08:00
wangxiaoshuang 7952c83b52 协议国际化
continuous-integration/drone/push Build is passing
2024-11-04 14:29:48 +08:00
wangxiaoshuang 9671722e1a GCP证书保存新增字段
continuous-integration/drone/push Build is running
2024-11-04 14:28:02 +08:00
wangxiaoshuang fe18b885f1 国际化
continuous-integration/drone/push Build is passing
2024-11-04 13:45:55 +08:00
wangxiaoshuang 25a3a0e371 概述与支付信息保存添加成功提示 2024-11-04 13:43:49 +08:00
wangxiaoshuang e947c99f71 1
continuous-integration/drone/push Build is passing
2024-11-04 13:18:21 +08:00
wangxiaoshuang f672ac3500 项目阅片人新增问题修复
continuous-integration/drone/push Build is running
2024-11-04 13:14:15 +08:00
wangxiaoshuang 13418ddad3 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2024-11-04 11:39:57 +08:00
wangxiaoshuang 296a455f0d 1 2024-11-04 11:39:42 +08:00
caiyiling 31047f2e30 附件文件类型设置更改
continuous-integration/drone/push Build is passing
2024-11-04 11:30:26 +08:00
caiyiling a3e518d960 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2024-11-04 11:14:45 +08:00
caiyiling e326cba5e0 附件文件类型设置更改 2024-11-04 11:14:23 +08:00
wangxiaoshuang 8cbb7a8333 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is running
2024-11-04 11:10:49 +08:00
wangxiaoshuang 5c700f055f 邮件配置更改 2024-11-04 11:10:37 +08:00
caiyiling d292b655d3 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is running
2024-11-04 11:08:50 +08:00
caiyiling ab362937f3 自定义标准附件上传更改 2024-11-04 11:08:32 +08:00
wangxiaoshuang a8d090e32c 邮件配置更改
continuous-integration/drone/push Build is running
2024-11-04 11:03:51 +08:00
caiyiling 716d0e3513 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2024-11-04 10:41:09 +08:00
caiyiling 8d84dde45e 自定义标准更改 2024-11-04 10:39:57 +08:00
wangxiaoshuang 6743811bd6 1
continuous-integration/drone/push Build is passing
2024-11-01 11:40:50 +08:00
wangxiaoshuang 71f856029c 测试禁止账号保存
continuous-integration/drone/push Build is passing
2024-11-01 11:23:35 +08:00
wangxiaoshuang 96d2a99f17 测试禁止账号保存
continuous-integration/drone/push Build is passing
2024-11-01 10:50:50 +08:00
wangxiaoshuang 7b7d4a7d6d 测试禁止账号保存
continuous-integration/drone/push Build is passing
2024-11-01 10:34:13 +08:00
wangxiaoshuang 0413a791c9 测试禁止账号保存
continuous-integration/drone/push Build is passing
2024-11-01 10:18:57 +08:00
wangxiaoshuang 8802e56549 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2024-11-01 10:09:56 +08:00
wangxiaoshuang 3f9edfa2a0 一致性分析导出 2024-11-01 10:09:45 +08:00
caiyiling eccf51cea9 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2024-11-01 09:42:51 +08:00
caiyiling 11627af629 pet-ct融合默认伪彩方案更改 2024-11-01 09:42:11 +08:00
wangxiaoshuang fadd24480d 1
continuous-integration/drone/push Build is passing
2024-10-31 14:15:59 +08:00
wangxiaoshuang 6b7d3da4a4 IQC质疑 字段优化】最新回复人 的取值改为 用户的姓名
continuous-integration/drone/push Build is passing
2024-10-31 13:58:53 +08:00
wangxiaoshuang 48ebd8e76d 访视-影像上传拍片日期字段优化
continuous-integration/drone/push Build is passing
2024-10-31 11:47:16 +08:00
caiyiling bb4193730e 表格问题自动计算更改
continuous-integration/drone/push Build is passing
2024-10-30 13:31:54 +08:00
caiyiling a19d756879 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2024-10-30 10:43:41 +08:00
caiyiling 94e924a753 分类问题补充属性 2024-10-30 10:43:10 +08:00
wangxiaoshuang 3633de8c35 简历临床试验经历添加适应症类型
continuous-integration/drone/push Build is passing
2024-10-30 10:32:42 +08:00
wangxiaoshuang 4400d31bca 项目配置新增邮件模板添加查询条件
continuous-integration/drone/push Build is passing
2024-10-30 09:54:43 +08:00
wangxiaoshuang 2a11d3f012 简历临床实验经历区分项目和系统
continuous-integration/drone/push Build is passing
2024-10-29 16:57:10 +08:00
wangxiaoshuang ae599f4ada Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2024-10-29 15:47:10 +08:00
wangxiaoshuang 19a6371008 国际化列表导出 2024-10-29 15:46:56 +08:00
caiyiling 7ebfcd0827 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2024-10-29 15:23:56 +08:00
caiyiling 0158ab2e96 表格问题运算类型更改 2024-10-29 15:23:34 +08:00
wangxiaoshuang ee9a30f6d0 1
continuous-integration/drone/push Build is passing
2024-10-29 14:43:00 +08:00
wangxiaoshuang 2d938d077e 简历部分接口添加项目id
continuous-integration/drone/push Build is passing
2024-10-29 14:16:30 +08:00
wangxiaoshuang 9f9255d315 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2024-10-29 13:50:48 +08:00
wangxiaoshuang 0624446d2c 简历增加假期新增、预览 2024-10-29 13:50:44 +08:00
caiyiling 0458b2d73d Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2024-10-29 10:39:59 +08:00
caiyiling 1abb330d7d 1 2024-10-29 10:39:46 +08:00
wangxiaoshuang 7343036ebf 1
continuous-integration/drone/push Build is passing
2024-10-29 10:27:20 +08:00
caiyiling 5edf2f8790 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2024-10-29 09:41:08 +08:00
caiyiling fb4fc347f3 分类问题允许关联多个问题 2024-10-29 09:40:40 +08:00
wangxiaoshuang c1c6f5a6c7 简历部分问题修复
continuous-integration/drone/push Build is passing
2024-10-28 17:03:16 +08:00
caiyiling 7360be1726 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2024-10-28 15:48:09 +08:00
caiyiling ec8c9df6b6 自定义标准分类问题允许编辑 2024-10-28 15:47:46 +08:00
wangxiaoshuang c18587b4f5 简历部分弹框遮罩层遮挡问题
continuous-integration/drone/push Build is passing
2024-10-28 13:55:49 +08:00
wangxiaoshuang 7eff02eb9b 1
continuous-integration/drone/push Build is passing
2024-10-25 15:26:21 +08:00
wangxiaoshuang 4c8f2675ad 1
continuous-integration/drone/push Build is running
2024-10-25 15:22:54 +08:00
wangxiaoshuang 67dd01c0be 1
continuous-integration/drone/push Build is passing
2024-10-25 13:44:06 +08:00
wangxiaoshuang 925451ea65 账号列表展示账号最后一次登录时间
continuous-integration/drone/push Build is running
2024-10-25 13:42:49 +08:00
caiyiling 70fcd15ab7 自定义阅片更改
continuous-integration/drone/push Build is passing
2024-10-25 13:36:46 +08:00
caiyiling 9c156ec1e6 非dicom阅片更改
continuous-integration/drone/push Build is passing
2024-10-25 10:27:26 +08:00
caiyiling dd57f24996 1
continuous-integration/drone/push Build is passing
2024-10-24 18:04:27 +08:00
caiyiling cc520270ac Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2024-10-24 17:26:23 +08:00
caiyiling 9f9a799832 自动分类问题补充及预览更改 2024-10-24 17:25:34 +08:00
wangxiaoshuang 1422a6f406 系统发布页背景图替换
continuous-integration/drone/push Build is passing
2024-10-24 15:52:47 +08:00
wangxiaoshuang f7124d35b0 pm简历采集
continuous-integration/drone/push Build is passing
2024-10-24 15:27:42 +08:00
wangxiaoshuang fabd26536c 1
continuous-integration/drone/push Build is passing
2024-10-24 11:31:13 +08:00
wangxiaoshuang a83b094a46 pm简历新增tab
continuous-integration/drone/push Build is passing
2024-10-24 11:18:03 +08:00
wangxiaoshuang 6c46a58f15 邮件新增延迟字段
continuous-integration/drone/push Build is passing
2024-10-24 10:49:53 +08:00
wangxiaoshuang 6f6959627f ir个人中心添加简历入口
continuous-integration/drone/push Build is passing
2024-10-23 18:00:18 +08:00
caiyiling f1f27f405b Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2024-10-23 16:53:28 +08:00
caiyiling 21a1eaf20a 1 2024-10-23 16:53:08 +08:00
wangxiaoshuang f7aaaf7900 简历接口对接
continuous-integration/drone/push Build is passing
2024-10-23 14:53:50 +08:00
caiyiling a3dcb7054a 表单样式更改
continuous-integration/drone/push Build is passing
2024-10-23 14:09:11 +08:00
caiyiling 917d32bf46 ecrf默认值更改
continuous-integration/drone/push Build is passing
2024-10-23 13:46:27 +08:00
caiyiling 99cd18c8e5 1
continuous-integration/drone/push Build is passing
2024-10-23 13:04:13 +08:00
caiyiling 58f223a028 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2024-10-23 11:14:00 +08:00
caiyiling 3c3a670ae4 标准配置更改 2024-10-23 11:13:28 +08:00
wangxiaoshuang b124178355 简历部分接口对接
continuous-integration/drone/push Build is passing
2024-10-23 10:12:55 +08:00
caiyiling 28be57dfee 导出标识根据标准过滤
continuous-integration/drone/push Build is failing
2024-10-22 17:41:36 +08:00
caiyiling 06c7398c1b 表格子问题添加导出标识
continuous-integration/drone/push Build is passing
2024-10-22 16:55:09 +08:00
caiyiling 11c7070799 系统配置bug更改
continuous-integration/drone/push Build is passing
2024-10-22 15:57:33 +08:00
caiyiling 0633e775d4 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2024-10-22 15:10:17 +08:00
caiyiling c18c083840 是否复制前值选项维护 2024-10-22 15:09:44 +08:00
wangxiaoshuang 20ff8d2a81 简历部分接口对接
continuous-integration/drone/push Build is running
2024-10-22 15:06:50 +08:00
wangxiaoshuang 6b1bfe0c0b 阅片跟踪导出
continuous-integration/drone/push Build is passing
2024-10-22 14:53:09 +08:00
caiyiling 5d13328c74 系统标准添加小结题型
continuous-integration/drone/push Build is passing
2024-10-22 14:48:12 +08:00
caiyiling bb830e2cb4 表格类型配置bug修复
continuous-integration/drone/push Build is passing
2024-10-22 14:28:27 +08:00
caiyiling ee4bd5ec8e ecrf配置更改
continuous-integration/drone/push Build is passing
2024-10-22 14:16:44 +08:00
caiyiling 86020e4f16 系统标准更改
continuous-integration/drone/push Build is passing
2024-10-22 13:46:20 +08:00
caiyiling d78ccb6e98 1
continuous-integration/drone/push Build is passing
2024-10-22 13:30:44 +08:00
caiyiling b23e9efe06 1
continuous-integration/drone/push Build is passing
2024-10-22 13:17:33 +08:00
caiyiling 14d1866138 1
continuous-integration/drone/push Build is running
2024-10-22 13:16:30 +08:00
caiyiling cc4afc8d37 选项类型默认值更改
continuous-integration/drone/push Build is passing
2024-10-22 11:13:28 +08:00
caiyiling 5a1b4b181a Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2024-10-22 10:12:43 +08:00
caiyiling fe8b0a524b ecrf配置页面优化 2024-10-22 10:12:18 +08:00
wangxiaoshuang d6861f400e 简历部分接口对接
continuous-integration/drone/push Build is passing
2024-10-21 17:55:57 +08:00
caiyiling 8281737ddc 阅片页上传下载页面信息更改
continuous-integration/drone/push Build is passing
2024-10-21 15:42:18 +08:00
caiyiling 9bfb233270 小结题型优化
continuous-integration/drone/push Build is passing
2024-10-21 15:24:25 +08:00
caiyiling bbc2d43335 阅片标准优化
continuous-integration/drone/push Build is passing
2024-10-21 14:55:26 +08:00
wangxiaoshuang 3694c51288 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2024-10-18 17:24:32 +08:00
wangxiaoshuang 219e39afb3 简历页新增支付方式 2024-10-18 17:24:26 +08:00
caiyiling e255f8c859 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2024-10-18 14:34:21 +08:00
caiyiling 4b84c7df3b 自动计算算法补充 2024-10-18 14:34:01 +08:00
wangxiaoshuang 9c98cfb668 1
continuous-integration/drone/push Build is passing
2024-10-18 14:07:36 +08:00
wangxiaoshuang bc15ac1cd3 事件列表新增创建时间
continuous-integration/drone/push Build is passing
2024-10-18 13:33:27 +08:00
wangxiaoshuang 6288770c26 1
continuous-integration/drone/push Build is passing
2024-10-18 11:08:59 +08:00
wangxiaoshuang 93c64e04f2 1
continuous-integration/drone/push Build is running
2024-10-18 11:08:22 +08:00
DESKTOP-6C3NK6N\WXS df638f5a73 pm新增简历采集功能
continuous-integration/drone/push Build is passing
2024-10-17 16:08:51 +08:00
caiyiling b0195b8d21 自定义阅片配置更改
continuous-integration/drone/push Build is passing
2024-10-17 15:39:34 +08:00
caiyiling f5954817fb Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2024-10-17 14:51:15 +08:00
caiyiling 77f8a80fd6 ecrf配置更改 2024-10-17 14:50:57 +08:00
DESKTOP-6C3NK6N\WXS 2b7a100d73 cron问题修复
continuous-integration/drone/push Build is passing
2024-10-17 13:57:30 +08:00
DESKTOP-6C3NK6N\WXS 07a4c66fcc 英文环境logo
continuous-integration/drone/push Build is passing
2024-10-17 09:46:06 +08:00
DESKTOP-6C3NK6N\WXS 143b6e9fc1 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is running
2024-10-16 16:26:08 +08:00
DESKTOP-6C3NK6N\WXS 6d943f94d8 1 2024-10-16 16:25:54 +08:00
caiyiling a6b68ea599 1
continuous-integration/drone/push Build is running
2024-10-16 16:22:02 +08:00
caiyiling 8c68866aca Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is running
2024-10-16 16:21:19 +08:00
caiyiling ccdedbee2f 影像浏览页工具更改 2024-10-16 16:20:34 +08:00
DESKTOP-6C3NK6N\WXS 3b7ea6491e lili添加关于页面
continuous-integration/drone/push Build is passing
2024-10-16 16:13:19 +08:00
DESKTOP-6C3NK6N\WXS 12e1454be4 中心调研表权限问题
continuous-integration/drone/push Build is passing
2024-10-16 15:47:20 +08:00
DESKTOP-6C3NK6N\WXS 9582e0a5a7 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is running
2024-10-16 15:41:21 +08:00
DESKTOP-6C3NK6N\WXS 9a77ce849b 简历预览页 2024-10-16 15:41:14 +08:00
DESKTOP-6C3NK6N\WXS 7beec24a7c 新增事件菜单 2024-10-16 15:41:01 +08:00
caiyiling a564225623 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2024-10-16 14:28:37 +08:00
caiyiling 8790c8f814 本地影像预览显示信息更改 2024-10-16 14:28:15 +08:00
DESKTOP-6C3NK6N\WXS 4c1ed950e5 简历新页面
continuous-integration/drone/push Build is passing
2024-10-15 17:50:15 +08:00
caiyiling 4f1e951a83 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2024-10-15 15:31:35 +08:00
caiyiling a3e544f6e4 阅片标准配置CRF更改 2024-10-15 15:30:36 +08:00
DESKTOP-6C3NK6N\WXS 292151f7e1 简历新页面
continuous-integration/drone/push Build is passing
2024-10-14 16:28:20 +08:00
DESKTOP-6C3NK6N\WXS c3b7fd7072 邮件管理默认排序修改
continuous-integration/drone/push Build is passing
2024-10-12 10:48:10 +08:00
DESKTOP-6C3NK6N\WXS 109470ca1a 邮件管理模板添加预览功能
continuous-integration/drone/push Build is passing
2024-10-12 10:31:36 +08:00
caiyiling 98c42c7b6a Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2024-10-12 09:14:15 +08:00
caiyiling 75ca85a994 阅片单元规则约束更改 2024-10-12 09:13:50 +08:00
DESKTOP-6C3NK6N\WXS e8f80646f1 预览高度调整
continuous-integration/drone/push Build is passing
2024-10-11 14:58:08 +08:00
DESKTOP-6C3NK6N\WXS bc70c64898 数据上传、导出模板列表添加预览功能
continuous-integration/drone/push Build is passing
2024-10-11 14:39:36 +08:00
DESKTOP-6C3NK6N\WXS d114690114 下载记录、接收记录、接收影像检查列表增加导出功能
continuous-integration/drone/push Build is passing
2024-10-11 10:47:12 +08:00
DESKTOP-6C3NK6N\WXS 705585da38 模板列表接口重复请求问题
continuous-integration/drone/push Build is passing
2024-10-10 13:57:30 +08:00
DESKTOP-6C3NK6N\WXS f58eb07323 历史文件大小为空时不显示
continuous-integration/drone/push Build is passing
2024-10-09 16:51:50 +08:00
DESKTOP-6C3NK6N\WXS 83a14956e3 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is failing
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
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
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
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
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
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
2024-09-29 14:04:12 +08:00
DESKTOP-6C3NK6N\WXS 49241bacfa 反馈图片名称问题
continuous-integration/drone/push Build is passing
2024-09-29 10:56:51 +08:00
DESKTOP-6C3NK6N\WXS 3bb0250699 apm意见反馈详情权限
continuous-integration/drone/push Build is passing
2024-09-29 10:38:50 +08:00
DESKTOP-6C3NK6N\WXS 47dd21ea7e 权限配置
continuous-integration/drone/push Build is passing
2024-09-29 10:25:19 +08:00
DESKTOP-6C3NK6N\WXS 64d268b6e8 后处理影像可以编辑检查类型
continuous-integration/drone/push Build is passing
2024-09-29 09:17:07 +08:00
caiyiling 463ecee3d8 下载影像预览后处理影像更改
continuous-integration/drone/push Build is passing
2024-09-27 17:00:42 +08:00
DESKTOP-6C3NK6N\WXS 9936d8c942 1
continuous-integration/drone/push Build is passing
2024-09-27 16:55:11 +08:00
caiyiling 1e132e0046 1
continuous-integration/drone/push Build is passing
2024-09-27 16:39:29 +08:00
caiyiling 95097caac2 1
continuous-integration/drone/push Build is passing
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
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
2024-09-27 16:23:19 +08:00
caiyiling bdb45c7b4e 1
continuous-integration/drone/push Build is passing
2024-09-27 16:16:18 +08:00
DESKTOP-6C3NK6N\WXS 0a4683386c 1
continuous-integration/drone/push Build is passing
2024-09-27 16:08:25 +08:00
DESKTOP-6C3NK6N\WXS c73297003f 1
continuous-integration/drone/push Build is passing
2024-09-27 14:48:07 +08:00
caiyiling 4faff7e85c 1
continuous-integration/drone/push Build is running
2024-09-27 14:43:03 +08:00
caiyiling 595e10a77a 系统标准配置更改及查看后处理影像修改
continuous-integration/drone/push Build is passing
2024-09-27 10:01:17 +08:00
caiyiling 360826ef28 1
continuous-integration/drone/push Build is passing
2024-09-26 17:21:56 +08:00
caiyiling cd7d261adf 阅片样式调整
continuous-integration/drone/push Build is passing
2024-09-26 16:54:55 +08:00
caiyiling 3ed09d16f6 自定义阅片样式更改
continuous-integration/drone/push Build is passing
2024-09-26 15:21:43 +08:00
caiyiling 2a016cf5f6 自定义标准阅片更改
continuous-integration/drone/push Build is passing
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
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
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
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
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
2024-09-26 13:37:25 +08:00
DESKTOP-6C3NK6N\WXS a7c7c3dfc5 自动分类可以选择是否为裁判问题
continuous-integration/drone/push Build is passing
2024-09-26 11:26:01 +08:00
DESKTOP-6C3NK6N\WXS 17d6e233c8 非dicom文件预览展示大小
continuous-integration/drone/push Build is running
2024-09-26 11:21:43 +08:00
DESKTOP-6C3NK6N\WXS c9a754a85f 非dicom文件上传限制改为2GB
continuous-integration/drone/push Build is passing
2024-09-26 10:57:30 +08:00
DESKTOP-6C3NK6N\WXS a1addde722 crc上传问题及ir下载subjectId和code获取
continuous-integration/drone/push Build is passing
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
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
2024-09-25 17:30:05 +08:00
caiyiling 112c78a832 1
continuous-integration/drone/push Build is passing
2024-09-25 17:10:22 +08:00
caiyiling 590949e207 1
continuous-integration/drone/push Build is passing
2024-09-25 15:45:34 +08:00
caiyiling 8a47f23fe7 OCT标准更改
continuous-integration/drone/push Build is passing
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
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
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
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
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
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
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
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
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
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
2024-09-23 16:45:55 +08:00
DESKTOP-6C3NK6N\WXS 16485f0c56 系统文档签署提交参数不传入项目id
continuous-integration/drone/push Build is passing
2024-09-23 15:49:24 +08:00
DESKTOP-6C3NK6N\WXS 692fd7bc97 版本管理新增LILI版本
continuous-integration/drone/push Build is passing
2024-09-23 15:17:16 +08:00
DESKTOP-6C3NK6N\WXS f17f1d6a6b 忘记密码校验
continuous-integration/drone/push Build is passing
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
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
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
2024-09-23 11:18:13 +08:00
DESKTOP-6C3NK6N\WXS 966e90498d 个人中心修改按钮校验
continuous-integration/drone/push Build is passing
2024-09-23 10:16:21 +08:00
DESKTOP-6C3NK6N\WXS ca1b95201c 个人中心账号设置添加校验
continuous-integration/drone/push Build is passing
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
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
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
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
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
2024-09-20 15:41:03 +08:00
DESKTOP-6C3NK6N\WXS d86eee6825 初始化用户页面表格调整
continuous-integration/drone/push Build is passing
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
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
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
2024-09-20 10:15:21 +08:00
DESKTOP-6C3NK6N\WXS 0144ae91e8 加秘测试
continuous-integration/drone/push Build is passing
2024-09-20 09:53:33 +08:00
DESKTOP-6C3NK6N\WXS 449e999d43 加密测试
continuous-integration/drone/push Build is passing
2024-09-19 17:34:15 +08:00
DESKTOP-6C3NK6N\WXS c77ca02cbc 1
continuous-integration/drone/push Build is passing
2024-09-19 17:27:31 +08:00
DESKTOP-6C3NK6N\WXS 8300cbaccf 1
continuous-integration/drone/push Build is passing
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
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
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
2024-09-19 10:20:19 +08:00
DESKTOP-6C3NK6N\WXS d0281abfee ir待阅列表受试者随机显示上传下载按钮
continuous-integration/drone/push Build is passing
2024-09-18 16:41:37 +08:00
DESKTOP-6C3NK6N\WXS 2c268cbd4f 公司信息改为后端配置
continuous-integration/drone/push Build is running
2024-09-18 16:38:09 +08:00
DESKTOP-6C3NK6N\WXS 857abb2c0d aws上传路径变更
continuous-integration/drone/push Build is passing
2024-09-18 13:51:03 +08:00
DESKTOP-6C3NK6N\WXS 371a3acff2 入项资格确认书的pdf预览方式调整
continuous-integration/drone/push Build is passing
2024-09-18 13:28:27 +08:00
DESKTOP-6C3NK6N\WXS 3dddbafd04 非dicom影像图片上传后文件数量预览按钮位置对齐
continuous-integration/drone/push Build is passing
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
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
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
2024-09-18 11:17:04 +08:00
DESKTOP-6C3NK6N\WXS c25183073a 忘记密码国际化换行
continuous-integration/drone/push Build is passing
2024-09-18 11:04:07 +08:00
DESKTOP-6C3NK6N\WXS 904a764ea5 上传显示问题修改
continuous-integration/drone/push Build is passing
2024-09-14 17:59:13 +08:00
DESKTOP-6C3NK6N\WXS 248f40b5be 接收记录排序更改
continuous-integration/drone/push Build is passing
2024-09-14 16:27:18 +08:00
DESKTOP-6C3NK6N\WXS c50c6c7bc7 1
continuous-integration/drone/push Build is passing
2024-09-14 16:13:37 +08:00
DESKTOP-6C3NK6N\WXS 1ef8eed500 独立阅片人上传限制
continuous-integration/drone/push Build is running
2024-09-14 16:12:59 +08:00
DESKTOP-6C3NK6N\WXS 9310337f16 反馈字段兼容
continuous-integration/drone/push Build is passing
2024-09-14 11:36:20 +08:00
caiyiling 37529cef33 非Dicom图片预览更改
continuous-integration/drone/push Build is passing
2024-09-14 10:39:33 +08:00
DESKTOP-6C3NK6N\WXS 42ed756c4e 上传临床数据按钮修改
continuous-integration/drone/push Build is passing
2024-09-14 10:26:12 +08:00
DESKTOP-6C3NK6N\WXS 97d786684b ir上传国际化修改
continuous-integration/drone/push Build is passing
2024-09-14 09:56:32 +08:00
DESKTOP-6C3NK6N\WXS 17f0a5d977 下载记录查询条件新增
continuous-integration/drone/push Build is running
2024-09-14 09:54:06 +08:00
DESKTOP-6C3NK6N\WXS 75a063acd9 路由白名单添加、iqc下载提示消息权限
continuous-integration/drone/push Build is passing
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
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
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
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
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
2024-09-13 10:22:24 +08:00
DESKTOP-6C3NK6N\WXS fa395f54b2 公司水印
continuous-integration/drone/push Build is running
2024-09-13 10:19:31 +08:00
DESKTOP-6C3NK6N\WXS 70d9623c63 从PACS选择影像页,外层页面没有拖拽条
continuous-integration/drone/push Build is passing
2024-09-13 09:22:30 +08:00
DESKTOP-6C3NK6N\WXS 82447c58de 独立阅片人上传影像页面国际化问题
continuous-integration/drone/push Build is running
2024-09-13 09:20:25 +08:00
DESKTOP-6C3NK6N\WXS 672cb9b2a2 国际化修改
continuous-integration/drone/push Build is passing
2024-09-12 17:40:15 +08:00
DESKTOP-6C3NK6N\WXS b06dcdc98f 仲裁规则显示
continuous-integration/drone/push Build is passing
2024-09-12 15:24:38 +08:00
DESKTOP-6C3NK6N\WXS b1096a8aa9 测试代码去除
continuous-integration/drone/push Build is passing
2024-09-12 15:11:31 +08:00
DESKTOP-6C3NK6N\WXS 66a1654bfe ir下载预览非dicom影像问题
continuous-integration/drone/push Build is passing
2024-09-12 14:39:28 +08:00
DESKTOP-6C3NK6N\WXS 986bce36e6 上传、下载添加提示;非dicom添加上传监控
continuous-integration/drone/push Build is passing
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
# 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
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
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
2024-09-11 16:22:12 +08:00
caiyiling dd0cd1ec64 稽查更改
continuous-integration/drone/push Build is passing
2024-09-11 16:07:28 +08:00
caiyiling db5e5da06a 稽查渲染bug修复
continuous-integration/drone/push Build is passing
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
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
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
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
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
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
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
2024-09-10 15:21:01 +08:00
caiyiling ce079f923a 国际化补充
continuous-integration/drone/push Build is failing
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
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
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
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
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
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
2024-09-09 17:47:57 +08:00
DESKTOP-6C3NK6N\WXS 3fc7d70b3f 1
continuous-integration/drone/push Build is passing
2024-09-09 17:15:04 +08:00
DESKTOP-6C3NK6N\WXS c2aa83f4b8 ir上传文件初始化问题
continuous-integration/drone/push Build is passing
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
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
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
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
2024-09-09 15:31:01 +08:00
DESKTOP-6C3NK6N\WXS 15cad74264 ir上传预览添加参数
continuous-integration/drone/push Build is passing
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
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
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
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
2024-09-09 14:57:52 +08:00
DESKTOP-6C3NK6N\WXS bfb1c03c63 1
continuous-integration/drone/push Build is passing
2024-09-09 13:56:29 +08:00
DESKTOP-6C3NK6N\WXS e05986ac9f 上传速率显示位置调整
continuous-integration/drone/push Build is passing
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
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
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
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
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
2024-09-09 11:41:00 +08:00
DESKTOP-6C3NK6N\WXS 1209a3962b 下载跨域问题解决
continuous-integration/drone/push Build is passing
2024-09-09 11:08:44 +08:00
DESKTOP-6C3NK6N\WXS ccf3b823d8 非dicom文件名生成guid
continuous-integration/drone/push Build is passing
2024-09-06 20:23:19 +08:00
DESKTOP-6C3NK6N\WXS e043e85630 上传名称格式化问题
continuous-integration/drone/push Build is passing
2024-09-06 20:09:26 +08:00
caiyiling 342c9325bf 上传影像bug修复
continuous-integration/drone/push Build is passing
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
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
2024-09-06 17:54:34 +08:00
DESKTOP-6C3NK6N\WXS e47c75937c 上传速率位置更改
continuous-integration/drone/push Build is passing
2024-09-06 17:41:40 +08:00
caiyiling f39050e220 数值组件精度修改
continuous-integration/drone/push Build is running
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
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
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
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
2024-09-06 16:05:57 +08:00
caiyiling 3dfe5b57a1 阅片页面上传影像按钮控制
continuous-integration/drone/push Build is running
2024-09-06 16:04:21 +08:00
DESKTOP-6C3NK6N\WXS 6a3068f942 系统标准添加上传下载
continuous-integration/drone/push Build is passing
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
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
2024-09-06 15:33:40 +08:00
DESKTOP-6C3NK6N\WXS 5bed446055 系统标准数值问题可设为裁判问题
continuous-integration/drone/push Build is passing
2024-09-06 15:13:46 +08:00
DESKTOP-6C3NK6N\WXS 5d8c389090 阅片单元配置:增加阅片过程下载影像、阅片过程上传影像不区分阅片标准
continuous-integration/drone/push Build is passing
2024-09-06 14:52:28 +08:00
DESKTOP-6C3NK6N\WXS 5bfc4a9a82 上传锁定问题
continuous-integration/drone/push Build is passing
2024-09-06 14:41:34 +08:00
DESKTOP-6C3NK6N\WXS cb00ec7ac2 ir下载预览
continuous-integration/drone/push Build is passing
2024-09-06 14:26:17 +08:00
DESKTOP-6C3NK6N\WXS dc5c1720c7 CRC上传影像前端页面优化
continuous-integration/drone/push Build is passing
2024-09-06 14:10:57 +08:00
DESKTOP-6C3NK6N\WXS 73a4135fd6 重置密码页面,标签换行
continuous-integration/drone/push Build is passing
2024-09-06 14:04:00 +08:00
DESKTOP-6C3NK6N\WXS f2af36b89a 临床数据上传页面,按钮对齐
continuous-integration/drone/push Build is passing
2024-09-06 13:57:00 +08:00
DESKTOP-6C3NK6N\WXS 4f0f124378 网速监控大小写转换
continuous-integration/drone/push Build is passing
2024-09-06 13:36:25 +08:00
DESKTOP-6C3NK6N\WXS c618738725 上传下载问题修复
continuous-integration/drone/push Build is passing
2024-09-06 13:22:42 +08:00
caiyiling 1274af3e48 影像质量评估设置为不正常重置病灶不可评估过滤标准
continuous-integration/drone/push Build is passing
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
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
2024-09-06 11:09:14 +08:00
DESKTOP-6C3NK6N\WXS f6b90c4c05 ir上传dicom校验
continuous-integration/drone/push Build is passing
2024-09-06 11:03:05 +08:00
DESKTOP-6C3NK6N\WXS e52378d5d4 ir上传dicom
continuous-integration/drone/push Build is passing
2024-09-05 18:01:05 +08:00
DESKTOP-6C3NK6N\WXS f52b2f151b ir上传dicom
continuous-integration/drone/push Build is passing
2024-09-05 17:17:47 +08:00
caiyiling 9bb856b50a 系统标准更改
continuous-integration/drone/push Build is passing
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
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
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
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
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
2024-09-05 13:15:07 +08:00
caiyiling 6f86700dbc lugano关闭跨源隔离检测
continuous-integration/drone/push Build is passing
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
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
2024-09-05 10:44:32 +08:00
DESKTOP-6C3NK6N\WXS 205b420074 1
continuous-integration/drone/push Build is passing
2024-09-05 09:09:13 +08:00
caiyiling d42e446953 1
continuous-integration/drone/push Build is passing
2024-09-04 18:01:22 +08:00
caiyiling 3be34d89ca 1
continuous-integration/drone/push Build is running
2024-09-04 17:57:48 +08:00
caiyiling 67b04c2a51 1
continuous-integration/drone/push Build is passing
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
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
2024-09-04 16:32:54 +08:00
DESKTOP-6C3NK6N\WXS 0e766300f0 1
continuous-integration/drone/push Build is passing
2024-09-04 15:04:56 +08:00
DESKTOP-6C3NK6N\WXS 581980f111 config测试
continuous-integration/drone/push Build is passing
2024-09-04 14:57:07 +08:00
DESKTOP-6C3NK6N\WXS 7aaede5503 aws分片上传
continuous-integration/drone/push Build is passing
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
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
2024-09-04 13:50:44 +08:00
caiyiling 4db4aed4dd 医学审核问题配置优化
continuous-integration/drone/push Build is passing
2024-09-04 13:15:44 +08:00
caiyiling b368df6bfb 关键序列中多帧影像只显示带有标记的帧
continuous-integration/drone/push Build is passing
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
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
2024-09-04 10:24:44 +08:00
caiyiling 324bc1a536 稽查更改
continuous-integration/drone/push Build is passing
2024-09-04 10:14:14 +08:00
caiyiling 2628d2ec5f 1
continuous-integration/drone/push Build is passing
2024-09-04 09:39:05 +08:00
caiyiling 9d8bd9c910 新增稽查配置项
continuous-integration/drone/push Build is running
2024-09-04 09:36:56 +08:00
caiyiling 615c91b197 非dicom报告页数值类型问题精度bug修复
continuous-integration/drone/push Build is running
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
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
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
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
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
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
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
2024-09-03 14:12:39 +08:00
DESKTOP-6C3NK6N\WXS 1a8653b47e crc上传下载
continuous-integration/drone/push Build is passing
2024-09-03 13:15:50 +08:00
caiyiling bfe86e6868 项目列表详情按钮添加权限控制
continuous-integration/drone/push Build is running
2024-09-03 13:11:44 +08:00
caiyiling f0eda2bc66 预览图片修改
continuous-integration/drone/push Build is passing
2024-09-02 14:12:26 +08:00
caiyiling 8f274aad89 获取tag属性乱码优化
continuous-integration/drone/push Build is passing
2024-08-30 11:10:19 +08:00
caiyiling 709063a26e 在影像上传预览和质控预览工具中,增加DICOM标签查看工具
continuous-integration/drone/push Build is passing
2024-08-30 10:25:32 +08:00
caiyiling 9e786c53b8 1
continuous-integration/drone/push Build is passing
2024-08-28 15:46:59 +08:00
caiyiling 37520fccb6 稽查更改
continuous-integration/drone/push Build is passing
2024-08-28 15:35:19 +08:00
caiyiling f3b36b9127 稽查修改
continuous-integration/drone/push Build is passing
2024-08-28 15:06:15 +08:00
caiyiling 870354b4b2 稽查配置更改
continuous-integration/drone/push Build is passing
2024-08-28 11:40:18 +08:00
caiyiling f838df51a5 阅片单元配置附加评估逻辑及阅片问题验证更改
continuous-integration/drone/push Build is passing
2024-08-28 10:21:21 +08:00
caiyiling 0a6a98c33c 稽查国际化更改
continuous-integration/drone/push Build is passing
2024-08-27 10:21:59 +08:00
caiyiling 28c357a702 稽查配置更改
continuous-integration/drone/push Build is passing
2024-08-27 10:11:00 +08:00
241 changed files with 43521 additions and 11273 deletions
+1943 -8
View File
File diff suppressed because it is too large Load Diff
+7
View File
@@ -14,6 +14,7 @@
"i18n:en": "node i18nGenerate.js lang=en keyCol=5 valCol=7"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.370.0",
"@cornerstonejs/calculate-suv": "^1.1.0",
"@cornerstonejs/core": "^1.27.4",
"@cornerstonejs/dicom-image-loader": "^1.27.4",
@@ -23,6 +24,9 @@
"@ffmpeg/ffmpeg": "^0.10.1",
"@microsoft/signalr": "^6.0.8",
"@riophae/vue-treeselect": "0.4.0",
"@vue-office/docx": "^1.6.2",
"@vue-office/excel": "^1.7.11",
"@vue/composition-api": "^1.7.2",
"ali-oss": "^6.17.1",
"axios": "0.18.1",
"babel-eslint": "7.2.3",
@@ -32,6 +36,7 @@
"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 +47,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",
@@ -62,6 +68,7 @@
"vue-clipboard2": "^0.3.1",
"vue-contextmenujs": "^1.3.13",
"vue-count-to": "^1.0.13",
"vue-demi": "^0.14.6",
"vue-i18n": "^8.7.0",
"vue-pdf": "^4.3.0",
"vue-puzzle-vcode": "^1.1.10",
+9 -4
View File
@@ -23,8 +23,8 @@
<img src="./error_assets/zzlogo2.png" alt="">
<img v-show="false" src="./error_assets/zzlogo3.png" alt="">
</div>
<div class="login-image">
<img src="./error_assets/login-bg.png">
<div class="login-image login-image-usa">
<img src="./error_assets/login-bg.svg" style="max-width: 500px;max-height: 300px;">
</div>
</div>
<div class="login-r">
@@ -148,7 +148,7 @@
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
width: 1200px;
width: 1100px;
height: 600px;
box-sizing: border-box;
background: rgb(255, 255, 255);
@@ -179,7 +179,12 @@
.login-container .login-body .login-l .login-image img{
height: 100%;
}
.login-container .login-body .login-l .login-image-usa {
width: 100%;
display: flex;
align-items: center;
justify-content: flex-end;
}
.login-container .login-body .login-r{
position: relative;
float: left;
+9 -4
View File
@@ -23,8 +23,8 @@
<img src="./error_assets/zzlogo3.png" alt="">
<img v-show="false" src="./error_assets/zzlogo3.png" alt="">
</div>
<div class="login-image">
<img src="./error_assets/login-bg.png">
<div class="login-image login-image-usa">
<img src="./error_assets/login-bg.svg" style="max-width: 500px;max-height: 300px;">
</div>
</div>
<div class="login-r">
@@ -104,7 +104,7 @@
left: 50%;
top: 50%;
transform: translate(-50%,-50%);
width: 1200px;
width: 1100px;
height: 600px;
box-sizing: border-box;
background: rgb(255, 255, 255);
@@ -135,7 +135,12 @@
.login-container .login-body .login-l .login-image img{
height: 100%;
}
.login-container .login-body .login-l .login-image-usa {
width: 100%;
display: flex;
align-items: center;
justify-content: flex-end;
}
.login-container .login-body .login-r{
position: relative;
float: left;
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 98 KiB

+1
View File
@@ -5,6 +5,7 @@
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<meta http-equiv="pragram" content="no-cache">
<meta name="autocomplete" content="off">
<meta http-equiv="Expires" content="0">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-control" content="no-store,no-cache,must-revalidate">
+167
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
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' })
}
+81 -71
View File
@@ -37,7 +37,12 @@
height="100"
style="width: 100%"
>
<el-table-column prop="Code" label="标签" width="300">
<el-table-column
prop="Code"
label="标签"
width="300"
show-overflow-tooltip
>
</el-table-column>
<!-- <el-table-column-->
<!-- prop="Description"-->
@@ -54,7 +59,7 @@
v-model="scope.row.Value"
@input="
(e) => {
$set(scope.row, 'Value', e);
$set(scope.row, 'Value', e)
}
"
size="mini"
@@ -67,7 +72,7 @@
v-model="scope.row.ValueCN"
@input="
(e) => {
$set(scope.row, 'ValueCN', e);
$set(scope.row, 'ValueCN', e)
}
"
size="mini"
@@ -90,13 +95,13 @@
import {
batchAddOrUpdateFrontInternationalization,
getFrontInternationalizationList,
} from "@/api/dictionary/dictionary";
import { getTrialExtralConfig } from "@/api/trials";
import feedBack from "@/views/trials/trials-layout/components/feedBack";
import Vue from "vue";
import i18n from "./lang";
} from '@/api/dictionary/dictionary'
import { getTrialExtralConfig } from '@/api/trials'
import feedBack from '@/views/trials/trials-layout/components/feedBack'
import Vue from 'vue'
import i18n from './lang'
export default {
name: "App",
name: 'App',
components: { feedBack },
data() {
return {
@@ -105,38 +110,43 @@ export default {
show: false,
key: null,
arr: [],
};
}
},
mounted() {
this.show = process.env.VUE_APP_OSS_PATH === "/test/dist";
},
watch: {
"$route.query": {
async handler() {
if (
this.$route.query.trialId &&
this.$route.query.trialId !==
this.$store.state.trials.config.trialId &&
this.$store.state.trials.whiteList.indexOf(this.$route.path) === -1
) {
let res = await getTrialExtralConfig({
TrialId: this.$route.query.trialId,
});
if (res.IsSuccess) {
this.$store.dispatch("trials/setConfig", {
trialId: this.$route.query.trialId,
...res.Result,
});
}
}
},
immediate: true,
deep: true,
},
this.show = process.env.VUE_APP_OSS_PATH === '/test/dist'
},
// watch: {
// '$route.query': {
// async handler() {
// if (!this.$route.query.trialId) {
// this.$store.dispatch('trials/setConfig', {})
// }
// if (
// this.$route.query.trialId &&
// this.$route.query.trialId !==
// this.$store.state.trials.config.trialId &&
// this.$store.state.trials.whiteList.indexOf(this.$route.path) === -1
// ) {
// let res = await getTrialExtralConfig({
// TrialId: this.$route.query.trialId,
// })
// console.log(222222222222)
// res.Result.aa = { 阅片人: '采集人', 申办方: 'aaa' }
// if (res.IsSuccess) {
// this.$store.dispatch('trials/setConfig', {
// trialId: this.$route.query.trialId,
// ...res.Result,
// })
// }
// }
// },
// immediate: true,
// deep: true,
// },
// },
methods: {
changeValue(target, attr, e) {
this.$set(target, attr, e);
this.$set(target, attr, e)
},
keyChange(v) {
if (this.key) {
@@ -148,65 +158,65 @@ export default {
~v.Value.indexOf(this.key) ||
~v.ValueCN.indexOf(this.key)
)
);
)
} else {
this.tableData = Object.assign([], this.arr);
this.tableData = Object.assign([], this.arr)
}
},
handleSave() {
this.$confirm("确定修改当前页面国际化内容?").then(() => {
this.$confirm('确定修改当前页面国际化内容?').then(() => {
batchAddOrUpdateFrontInternationalization(this.tableData).then(
async (res) => {
var zhMessages = {},
enMessages = {};
var Internationalization = await getFrontInternationalizationList();
Vue.prototype.$tl = Internationalization.Result;
enMessages = {}
var Internationalization = await getFrontInternationalizationList()
Vue.prototype.$tl = Internationalization.Result
this.tableData.forEach((v) => {
// zhMessages[v.Description + '_' + v.Code] = v.ValueCN
// enMessages[v.Description + '_' + v.Code] = v.Value
zhMessages[v.Code] = v.ValueCN;
enMessages[v.Code] = v.Value;
});
i18n.mergeLocaleMessage("zh", zhMessages);
i18n.mergeLocaleMessage("en", enMessages);
this.drawer = false;
this.$message.success("国际化修改成功");
zhMessages[v.Code] = v.ValueCN
enMessages[v.Code] = v.Value
})
i18n.mergeLocaleMessage('zh', zhMessages)
i18n.mergeLocaleMessage('en', enMessages)
this.drawer = false
this.$message.success('国际化修改成功')
}
);
});
)
})
},
openI18n() {
this.tableData = [];
this.key = null;
this.drawer = true;
let arr = [];
this.tableData = []
this.key = null
this.drawer = true
let arr = []
let tableData = this.$tl.map((v) => {
let a = { ...v };
let a = { ...v }
// if (!a.Description) {
// a.Description = this.$route.path
// }
return a;
});
return a
})
tableData = tableData.filter((v) => {
// return ~this.$path.indexOf(v.Description + '_' + v.Code)
return ~this.$path.indexOf(v.Code);
});
return ~this.$path.indexOf(v.Code)
})
this.$path.forEach((v) => {
let o = tableData.find((a) => {
return a.Code === v;
});
return a.Code === v
})
if (o) {
arr.push(o);
arr.push(o)
} else {
arr.push({
Code: v,
Description: null,
Value: null,
ValueCN: null,
});
})
}
});
this.arr = arr;
})
this.arr = arr
if (this.key) {
this.tableData = Object.assign(
[],
@@ -216,15 +226,15 @@ export default {
~v.Value.indexOf(this.key) ||
~v.ValueCN.indexOf(this.key)
)
);
)
} else {
this.tableData = Object.assign([], this.arr);
this.tableData = Object.assign([], this.arr)
}
// console.log(JSON.stringify(this.$path))
// console.log(JSON.stringify(this.tableData))
},
},
};
}
</script>
<style lang="scss">
@@ -249,7 +259,7 @@ input::-webkit-inner-spin-button {
-webkit-appearance: none !important;
}
input[type="number"] {
input[type='number'] {
-moz-appearance: textfield !important;
}
@@ -275,7 +285,7 @@ textarea {
color: $light_gray;
text-align: center;
font-weight: bold;
font-family: "Times New Roman";
font-family: 'Times New Roman';
text-shadow: 1px 0.5px 1.5px #666;
}
.title-logo {
+24
View File
@@ -293,3 +293,27 @@ export function batchUpdateInternationalInfo(data) {
data
})
}
// 邮件事件消息列表
export function getEventStoreRecordList(data) {
return request({
url: `/EventStoreRecord/getEventStoreRecordList`,
method: 'post',
data
})
}
// 重新发布事件消息
export function rePublishEvent(params) {
return request({
url: `/EventStoreRecord/rePublishEvent`,
method: 'get',
params
})
}
// userId获取doctorId
export function useUserIDGetDoctorID(data) {
return request({
url: `/TrialSiteSurvey/useUserIDGetDoctorID`,
method: 'post',
data
})
}
+24
View File
@@ -1045,4 +1045,28 @@ export function getExploreRecommentInfo(params) {
method: 'get',
params
})
}
export function getSystemCalculateQuestions(params) {
return request({
url: `/ReadingQuestion/getSystemCalculateQuestions`,
method: 'post',
data: params
})
}
export function getSystemCalculateTableQuestions(params) {
return request({
url: `/ReadingQuestion/getSystemCalculateTableQuestions`,
method: 'post',
data: params
})
}
// 添加pd/入组默认邮件
export function batchAddEnrollOrPdEmailConfig(params) {
return request({
url: `/TrialEmailNoticeConfig/batchAddEnrollOrPdEmailConfig`,
method: 'post',
params
})
}
+70
View File
@@ -187,3 +187,73 @@ export function qCVisitList_Export(data) {
data
})
}
// 导出下载记录
export function getTrialDownloadList_Export(data) {
return requestDownload({
url: `/ExcelExport/getTrialDownloadList_Export`,
responseType: 'blob',
method: 'post',
data
})
}
// 导出接收记录
export function getSCPImageUploadList_Export(data) {
return requestDownload({
url: `/ExcelExport/getSCPImageUploadList_Export`,
responseType: 'blob',
method: 'post',
data
})
}
// 导出接收检查影像记录
export function getPatientList_Export(data) {
return requestDownload({
url: `/ExcelExport/getPatientList_Export`,
responseType: 'blob',
method: 'post',
data
})
}
export function getCommonEvaluationList_Export(data) {
return requestDownload({
url: `/ExcelExport/getCommonEvaluationList_Export`,
responseType: 'blob',
method: 'post',
data
})
}
export function getCommonJudgeRatioList_Export(data) {
return requestDownload({
url: `/ExcelExport/getCommonJudgeRatioList_Export`,
responseType: 'blob',
method: 'post',
data
})
}
// 导出国际化列表
export function GetInternationalizationList_Export(data) {
return requestDownload({
url: `/ExcelExport/GetInternationalizationList_Export`,
responseType: 'blob',
method: 'post',
data
})
}
// 导出一致性分析
export function GetAnalysisTaskList_Export(data) {
return requestDownload({
url: `/ExcelExport/GetAnalysisTaskList_Export`,
responseType: 'blob',
method: 'post',
data
})
}
// 导出邮件配置
export function GetEmailNoticeConfigList_Export(data) {
return requestDownload({
url: `/ExcelExport/GetEmailNoticeConfigList_Export`,
responseType: 'blob',
method: 'post',
data
})
}
+67 -3
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
})
}
+60 -3
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
})
}
+2 -1
View File
@@ -12,7 +12,8 @@ export function verifySendCode(param) {
return request({
url: '/TrialSiteSurvey/verifySendCode',
method: 'post',
data: param
data: param,
clearToken: true
})
}
+59 -6
View File
@@ -138,10 +138,11 @@ export function addOrUpdateResearchPublication(param) {
})
}
export function getTrialExperience(doctorId) {
export function getTrialExperience(data) {
return request({
url: `/trialExperience/getTrialExperience/${doctorId}`,
method: 'get'
url: `/trialExperience/getTrialExperience`,
method: 'post',
data
})
}
@@ -236,10 +237,14 @@ export function downloadByAttachmentId(doctorId, attachmentIds) {
})
}
export function getDetail(doctorId) {
export function getDetail(doctorId, TrialId) {
return request({
url: `/doctor/getDetail/${doctorId}`,
method: 'get'
url: `/doctor/getDetail`,
method: 'post',
data: {
DoctorId: doctorId,
TrialId
}
})
}
@@ -290,3 +295,51 @@ export function verifyEmialGetDoctorInfo(param) {
data: param
})
}
// 新增或编辑基本信息
export function addOrUpdateDoctorBasicInfoAndEmployment(param) {
return request({
url: `/Doctor/addOrUpdateDoctorBasicInfoAndEmployment`,
method: 'post',
data: param
})
}
// 新增或编辑概述
export function updateGneralSituation(param) {
return request({
url: `/Doctor/updateGneralSituation`,
method: 'post',
data: param
})
}
// 新增或编辑支付方式
export function updatePaymentMode(param) {
return request({
url: `/Doctor/updatePaymentMode`,
method: 'post',
data: param
})
}
// 新增或编辑Publication
export function addOrUpdateResearchPublicationInfo(param) {
return request({
url: `/ResearchPublication/addOrUpdateResearchPublicationInfo`,
method: 'post',
data: param
})
}
// 发送简历采集邮件
export function doctorSendEmail(param) {
return request({
url: `/Doctor/sendEmail`,
method: 'post',
data: param
})
}
// 获取医生是否休假
export function getIsVacation(param) {
return request({
url: `/Vacation/getIsVacation`,
method: 'post',
data: param
})
}
+2 -2
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'
})
}
+9 -1
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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

+60 -21
View File
@@ -2,7 +2,7 @@
<el-row>
<el-col class="m-b-10" :span="24">
<el-table
v-adaptive="{bottomOffset}"
v-adaptive="{ bottomOffset }"
:data="list"
:height="tableHeight"
v-bind="$attrs"
@@ -15,15 +15,44 @@
>
<template v-for="(column, index) in columns">
<slot name="front-slot" />
<el-table-column v-if="column.type === 'tip'" :key="index" width="35">
<template slot-scope="scope">
<span v-if="column.slot">
<!-- 具名slot -->
<slot v-if="column.slot" :name="column.slot" :scope="scope" />
</span>
</template>
</el-table-column>
<!-- 序号 -->
<el-table-column v-if="column.type === 'selection'" :key="index" type="selection" width="55" />
<el-table-column
v-else-if="column.type === 'selection'"
:key="index"
type="selection"
width="55"
/>
<!-- 复选框 -->
<el-table-column v-else-if="column.type === 'index'" :key="index" type="index" width="50" />
<el-table-column
v-else-if="column.type === 'index'"
:key="index"
type="index"
width="50"
/>
<!-- 具体内容 -->
<el-table-column v-else :key="index" align="left" :label="column.label" :width="column.width" :min-width="column.minWidth" :show-overflow-tooltip="column.showOverflowTooltip || false" :sortable="column.sortable || false" :prop="column.prop">
<el-table-column
v-else
:key="index"
align="left"
:label="column.label"
:width="column.width"
:min-width="column.minWidth"
:show-overflow-tooltip="column.showOverflowTooltip || false"
:sortable="column.sortable || false"
:prop="column.prop"
>
<template slot-scope="scope">
<!-- 仅仅显示文字 -->
<span v-if="!column.hidden"> <!-- 如果hidden为true的时候 那么当前格可以不显示可以选择显示自定义的slot-->
<span v-if="!column.hidden">
<!-- 如果hidden为true的时候 那么当前格可以不显示可以选择显示自定义的slot-->
<!-- 操作按钮 -->
<span v-if="column.type === 'operate'">
<!-- <a v-for="(operate, i) in column.operates" :key="i" href="javascript:void(0)" class="operate-button" @click="handleClick(operate, scope.row)">
@@ -31,7 +60,13 @@
&nbsp;&nbsp;
</a> -->
<span v-for="(operate, i) in column.operates" :key="i">
<el-button :size="operate.size || 'mini'" :type="operate.type || 'primary'" style="margin-right:5px;" @click="handleClick(operate, scope.row)">{{ operate.name }}</el-button>
<el-button
:size="operate.size || 'mini'"
:type="operate.type || 'primary'"
style="margin-right: 5px"
@click="handleClick(operate, scope.row)"
>{{ operate.name }}</el-button
>
</span>
</span>
<span v-else>
@@ -52,7 +87,12 @@
</el-col>
<!-- 分页部分 -->
<el-col v-if="!hiddenPage" :span="24" class="page">
<pagination :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize" @pagination="pagination" />
<pagination
:total="total"
:page.sync="searchData.PageIndex"
:limit.sync="searchData.PageSize"
@pagination="pagination"
/>
</el-col>
</el-row>
</template>
@@ -96,45 +136,44 @@ export default {
// 核心数据
list: {
type: Array,
default: () => []
default: () => [],
},
// columns
columns: {
type: Array,
required: true,
default: () => []
default: () => [],
},
// is hidden page for table
hiddenPage: {
type: Boolean,
default: false
default: false,
},
bottomOffset: {
type: Number,
default: 45
default: 45,
},
searchData: {
type: Object,
default: () => {}
default: () => {},
},
total: {
type: Number,
default: 0
default: 0,
},
highlightCurrentRow: {
type: Boolean,
default: false
}
default: false,
},
},
data() {
return {
}
return {}
},
computed: {
tableHeight() {
return 100
}
},
},
methods: {
// 处理点击事件
@@ -157,12 +196,12 @@ export default {
},
pagination() {
this.$emit('getList')
}
}
},
},
}
</script>
<style lang="scss" scoped>
.page{
.page {
padding-top: 3px;
text-align: right;
}
+35 -14
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
@@ -250,7 +262,13 @@ export default {
apiTool
)
if (!toolAlreadyAddedToElement) {
cornerstoneTools.addToolForElement(element, apiTool)
if (toolName === 'RectangleRoi') {
cornerstoneTools.addToolForElement(element, apiTool, { configuration: { showMinMax: true, showStatsText: true}})
} else if (toolName === 'EllipticalRoi') {
cornerstoneTools.addToolForElement(element, apiTool, { configuration: { showMinMax: true}})
} else {
cornerstoneTools.addToolForElement(element, apiTool)
}
}
}
// Setup button listener
@@ -310,7 +328,7 @@ export default {
// cornerstoneTools.addStackStateManager(this.canvas, ['stack', 'stackPrefetch', 'playClip'])
cornerstoneTools.addToolState(this.canvas, 'stack', this.stack)
// cornerstoneTools.stackPrefetch.enable(this.canvas)
cornerstone.updateImage(element, true)
cornerstone.updateImage(element, true)
// cornerstoneTools.stackPrefetch.setConfiguration({ maxImagesToPrefetch: Infinity,
// preserveExistingPool: true })
// cornerstoneTools.stackPrefetch.enable(this.canvas)
@@ -364,8 +382,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 +459,7 @@ export default {
if (!markers) {
return
}
this.orientationMarkers = [oppositeColumn, row, column, oppositeRow]
this.originalMarkers = [oppositeColumn, row, column, oppositeRow]
this.setMarkers()
@@ -636,7 +654,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 +666,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 +686,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 +725,7 @@ export default {
this.orientationMarkers = [...this.originalMarkers]
this.setMarkers()
}
var viewport = cornerstone.getViewport(this.canvas)
viewport.hflip = false
viewport.vflip = false
@@ -747,6 +765,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)
+287
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>
+7 -3
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
+3 -2
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() {
@@ -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>
+90 -90
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 {
+56 -58
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
@@ -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>
@@ -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`
: ''
}}</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`
: ''
}}</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`
: ''
}}</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
}
},
// file上传到oss
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>
@@ -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>
+1 -2
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);
},
},
+77 -30
View File
@@ -15,7 +15,7 @@ import '@/styles/index.scss' // global css
import App from './App'
import store from './store'
import router from './router'
import WHITELIST from "./utils/whiteList"
import '@/icons' // icon
import '@/permission' // permission control
import md5 from 'js-md5'
@@ -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
@@ -279,13 +295,20 @@ async function VueInit() {
}
Vue.prototype.$path = []
var t = function (key) {
// if (![Vue.prototype.toPath + '_' + key].includes(Vue.prototype.$path)) {
if (!~Vue.prototype.$path.indexOf(key)) {
// Vue.prototype.$path.push(Vue.prototype.toPath + '_' + key)
Vue.prototype.$path.push(key)
}
// return i18n.t(Vue.prototype.toPath + '_' + key)
return i18n.t(key)
let text = i18n.t(key)
if (Array.isArray(store.state.trials.config.TrialObjectNameList) && store.state.trials.config.TrialObjectNameList.length > 0) {
let arr = JSON.parse(JSON.stringify(store.state.trials.config.TrialObjectNameList))
arr.sort((a, b) => b.Name.length - a.Name.length)
arr.forEach(item => {
let test = new RegExp(item.Name, 'ig')
text = text.replace(test, item.TrialName)
})
}
// return i18n.t(key)
return text;
}
Vue.prototype.$t = t
_vm = new Vue({
@@ -296,7 +319,8 @@ async function VueInit() {
data: {
unlock: {
my_username: null,
my_password: null
my_password: null,
view: false
}
},
render: h => h(App)
@@ -307,7 +331,7 @@ async function VueInit() {
if (eval(process.env.VUE_APP_LOGIN_FOR_PERMISSION)) {
setInterval(() => {
var lang = zzSessionStorage.getItem('lang') ? zzSessionStorage.getItem('lang') : 'zh'
if (_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') {
if (WHITELIST.includes(_vm.$route.path)) {
return
}
if (_vm.$store.state.user.userId !== zzSessionStorage.getItem('userId')) {
@@ -368,10 +392,10 @@ 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') {
if (_vm.$store.state.trials.unlock || WHITELIST.includes(_vm.$route.path)) {
count = 0;
localStorage.setItem('count', '0')
return
@@ -400,7 +424,7 @@ async function VueInit() {
} : () => { }, process.env.VUE_APP_LOGOUT_FOR_TIME,
eval(process.env.VUE_APP_LOCK_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') {
if (_vm.$store.state.trials.unlock || WHITELIST.includes(_vm.$route.path)) {
count = 0;
localStorage.setItem('count', '0')
if (_vm.$route.path === '/login') {
@@ -440,7 +464,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 +529,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 +553,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"
}
},
}
}),
])
])
])
+46 -7
View File
@@ -6,17 +6,56 @@ import 'nprogress/nprogress.css'
import { getToken } from '@/utils/auth'
import Vue from 'vue'
import { OSSclient } from './utils/oss'
import WHITELIST from "./utils/whiteList"
import { getTrialExtralConfig } from '@/api/trials'
// 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()
// 设置页面标题
// document.title = getPageTitle(to.meta.title)
if (!to.query.trialId) {
store.dispatch('trials/setConfig', {})
}
if (
to.query.trialId &&
to.query.trialId !==
store.state.trials.config.trialId &&
store.state.trials.whiteList.indexOf(to.path) === -1
) {
console.log(to.query.path)
let res = await getTrialExtralConfig({
TrialId: to.query.trialId,
})
if (res.IsSuccess) {
store.dispatch('trials/setConfig', {
trialId: to.query.trialId,
...res.Result,
})
}
} if (!to.query.trialId) {
store.dispatch('trials/setConfig', {})
}
if (
to.query.trialId &&
to.query.trialId !==
store.state.trials.config.trialId &&
store.state.trials.whiteList.indexOf(to.path) === -1
) {
let res = await getTrialExtralConfig({
TrialId: to.query.trialId,
})
if (res.IsSuccess) {
store.dispatch('trials/setConfig', {
trialId: to.query.trialId,
...res.Result,
})
}
}
// 确定用户是否已登录
const hasToken = getToken()
Vue.prototype.toPath = to.path
@@ -24,16 +63,16 @@ router.beforeEach(async (to, from, next) => {
if (hasToken) {
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 store.dispatch('user/logout')
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 +105,7 @@ router.beforeEach(async (to, from, next) => {
} else {
// 在免登录whiteList中,直接进入
if (to.path === '/readingDicoms' || to.path === '/noneDicomReading') {
OSSclient()
await OSSclient()
}
next()
}
+5
View File
@@ -223,6 +223,11 @@ export const constantRoutes = [
component: () => import('@/views/reviewers/ReviewersResearch'),
hidden: true
},
{
path: '/curriculumVitae',
component: () => import('@/views/reviewers/curriculumVitae/index'),
hidden: true
},
{
path: '/ReviewersResearchForm',
component: () => import('@/views/reviewers/ReviewersResearchForm'),
+55 -21
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
}
+1 -1
View File
@@ -9,7 +9,7 @@ const getDefaultState = () => {
studyListQuery: null,
unlock: false,
config: {},
uploadTip: null,
uploadTip: '0.00KB/s',
timer: null,
whiteList: [],
checkTaskId: null
+1
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 {
+3 -1
View File
@@ -149,6 +149,7 @@ body .el-table th.gutter {
}
.el-dialog__body {
margin-top: 15px;
height: calc(100% - 70px);
padding: 0 20px;
@@ -347,6 +348,7 @@ body .el-table th.gutter {
background: #000;
z-index: 3999;
}
.el-message-box__wrapper{
.el-message-box__wrapper {
z-index: 9999 !important;
}
+5 -5
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)
}
+44
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)
},
};
+30
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;
}
+457
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)
}
+161
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
+204 -41
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
+6 -2
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)) {
+1 -2
View File
@@ -14,8 +14,7 @@ service.interceptors.request.use(
config => {
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'
console.log(config.headers)
config.headers['Accept-Language'] = language === 'en'?'en-US,en;q=0.5':'zh-CN,zh;q=0.9'
if (store.getters.token) {
config.headers.Authorization = `Bearer ${store.getters.token}`
}
+15 -5
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
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)
}
-168
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
View File
@@ -0,0 +1,2 @@
const WHITELIST = ['/', "/curriculumVitae", '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
+1 -1
View File
@@ -43,7 +43,7 @@
<div
v-show="item.instanceCount"
style="padding: 1px;"
>{{ item.modality }} : {{ item.instanceCount }} Series
>{{ item.modality }} : {{ item.instanceCount }} image
</div>
</div>
</div>
+1 -1
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 = []
+16 -4
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 = {}
@@ -26,7 +26,7 @@
<el-form-item>
<!-- 查询 -->
<el-button type="primary" icon="el-icon-search" @click="handleSearch">
{{ $t("common:button:search") }}
{{ $t('common:button:search') }}
</el-button>
<!-- 重置 -->
<el-button
@@ -34,7 +34,7 @@
icon="el-icon-refresh-left"
@click="handleReset"
>
{{ $t("common:button:reset") }}
{{ $t('common:button:reset') }}
</el-button>
</el-form-item>
<el-button
@@ -44,7 +44,7 @@
size="small"
@click="handleAdd"
>
{{ $t("common:button:new") }}
{{ $t('common:button:new') }}
</el-button>
</el-form>
</template>
@@ -74,7 +74,7 @@
<template slot-scope="scope">
{{
$fd(
"Common_File_BusinessScenario",
'Common_File_BusinessScenario',
scope.row.BusinessScenarioEnum
)
}}
@@ -106,10 +106,10 @@
>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsDeleted" type="danger">{{
$fd("YesOrNo", scope.row.IsDeleted)
$fd('YesOrNo', scope.row.IsDeleted)
}}</el-tag>
<el-tag v-else type="primary">{{
$fd("YesOrNo", scope.row.IsDeleted)
$fd('YesOrNo', scope.row.IsDeleted)
}}</el-tag>
</template>
</el-table-column>
@@ -122,14 +122,17 @@
<el-table-column :label="$t('common:action:action')">
<template slot-scope="scope">
<el-button type="text" @click="PreviewFile(scope.row)">
{{ $t('common:button:preview') }}
</el-button>
<el-button type="text" @click="handleDownload(scope.row)">
{{ $t("common:button:download") }}
{{ $t('common:button:download') }}
</el-button>
<el-button type="text" @click="handleEdit(scope.row)">
{{ $t("common:button:edit") }}
{{ $t('common:button:edit') }}
</el-button>
<el-button type="text" @click="handleDelete(scope.row)">
{{ $t("common:button:delete") }}
{{ $t('common:button:delete') }}
</el-button>
</template>
</el-table-column>
@@ -166,22 +169,22 @@ import {
getCommonDocumentList,
DownloadCommonDoc,
deleteCommonDocument,
} from "@/api/dictionary";
import BaseContainer from "@/components/BaseContainer";
import Pagination from "@/components/Pagination";
import TemplateForm from "./TemplateForm";
const FileTypeEnum = 2;
} from '@/api/dictionary'
import BaseContainer from '@/components/BaseContainer'
import Pagination from '@/components/Pagination'
import TemplateForm from './TemplateForm'
const FileTypeEnum = 2
const searchDataDefault = () => {
return {
FileTypeEnum: FileTypeEnum,
BusinessScenarioEnum: null,
Name: "",
Name: '',
PageIndex: 1,
PageSize: 20,
};
};
}
}
export default {
name: "ExportTemplate",
name: 'ExportTemplate',
components: { BaseContainer, Pagination, TemplateForm },
data() {
return {
@@ -189,86 +192,97 @@ export default {
list: [],
total: 0,
currentRow: {},
editDialog: { title: "", visible: false },
editDialog: { title: '', visible: false },
loading: false,
};
}
},
mounted() {
this.getList();
this.getList()
},
methods: {
PreviewFile(row) {
let basePath = window.location.origin
if (window.location.protocol !== 'https:') {
basePath = 'https://irc.test.extimaging.com'
}
let data = {
name: row.NameCN,
path: basePath + row.Path,
}
this.$emit('PreviewFile', data)
},
handleDelete(row) {
this.$confirm(
this.$t("dictionary:attachment:export:confirm:delete")
this.$t('dictionary:attachment:export:confirm:delete')
).then(() => {
deleteCommonDocument(row.Id).then(() => {
this.$message.success(this.$t("common:message:deletedSuccessfully"));
this.getList();
});
});
this.$message.success(this.$t('common:message:deletedSuccessfully'))
this.getList()
})
})
},
getList() {
this.loading = true;
this.loading = true
getCommonDocumentList(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.editDialog.title = "Add";
this.currentRow = { FileTypeEnum: FileTypeEnum };
this.editDialog.visible = true;
this.editDialog.title = 'Add'
this.currentRow = { FileTypeEnum: FileTypeEnum }
this.editDialog.visible = true
},
//
handleDownload(row) {
this.loading = true;
this.loading = true
DownloadCommonDoc(row.Code)
.then((data) => {
this.loading = false;
this.loading = false
})
.catch(() => {
this.loading = false;
});
this.loading = false
})
},
//
handleEdit(row) {
this.editDialog.title = "Edit";
this.currentRow = { ...row };
this.editDialog.visible = true;
this.editDialog.title = 'Edit'
this.currentRow = { ...row }
this.editDialog.visible = true
},
handleSearch() {
this.searchData.PageIndex = 1;
this.getList();
this.searchData.PageIndex = 1
this.getList()
},
handleReset() {
this.searchData = searchDataDefault();
this.getList();
this.searchData = searchDataDefault()
this.getList()
},
closeDialog() {
this.editDialog.visible = false;
this.editDialog.visible = false
},
//
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
}
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" scoped>
::v-deep .search{
::v-deep .search {
display: block;
}
</style>
@@ -12,7 +12,7 @@
<el-form-item label="文件类型: " prop="FileTypeId">
<el-select
v-model="form.FileTypeId"
style="width:100%;"
style="width: 100%"
size="small"
filterable
>
@@ -41,21 +41,26 @@
:on-exceed="handleExceed"
:disabled="form.Type === ''"
>
<el-button size="small" type="primary" :disabled="form.FileTypeId === ''" :loading="btnLoading">Select</el-button>
<span
slot="tip"
style="margin-left:10px;"
class="el-upload__tip"
<el-button
size="small"
type="primary"
:disabled="form.FileTypeId === ''"
:loading="btnLoading"
>Select</el-button
>
<span slot="tip" style="margin-left: 10px" class="el-upload__tip">
(must be in pdf format)
</span>
</el-upload>
</div>
</el-form-item>
<el-form-item label="需要签署的用户类型: " prop="NeedConfirmedUserTypeIdList">
<el-form-item
label="需要签署的用户类型: "
prop="NeedConfirmedUserTypeIdList"
>
<el-select
v-model="form.NeedConfirmedUserTypeIdList"
style="width:100%;"
style="width: 100%"
multiple
>
<el-option
@@ -66,7 +71,18 @@
/>
</el-select>
</el-form-item>
<el-form-item label="查看最短时间(分钟): " prop="SignViewMinimumMinutes">
<el-form-item label="是否仅内部签署: " prop="DocUserSignType">
<el-switch
v-model="form.DocUserSignType"
:active-value="1"
:inactive-value="0"
>
</el-switch>
</el-form-item>
<el-form-item
label="查看最短时间(分钟): "
prop="SignViewMinimumMinutes"
>
<el-input-number
v-model="form.SignViewMinimumMinutes"
controls-position="right"
@@ -81,9 +97,16 @@
</el-radio-group>
</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.FileTypeId === '' || form.Name === ''" :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.FileTypeId === '' || form.Name === ''"
:loading="saveBtnLoading"
@click="handleSave"
>Save</el-button
>
</el-form-item>
</div>
</el-form>
@@ -96,8 +119,10 @@ export default {
props: {
data: {
type: Object,
default() { return {} }
}
default() {
return {}
},
},
},
data() {
@@ -108,19 +133,26 @@ export default {
Name: '',
Path: '',
IsDeleted: false,
SignViewMinimumMinutes: null
SignViewMinimumMinutes: null,
DocUserSignType: 0,
},
rules: {
FileTypeId: [{ required: true, message: 'Please select', trigger: ['blur'] }],
SignViewMinimumMinutes: [{ required: true, message: 'Please specify', trigger: ['change'] }],
NeedConfirmedUserTypeIdList: [{ required: true, message: 'Please select', trigger: ['blur'] }]
FileTypeId: [
{ required: true, message: 'Please select', trigger: ['blur'] },
],
SignViewMinimumMinutes: [
{ required: true, message: 'Please specify', trigger: ['change'] },
],
NeedConfirmedUserTypeIdList: [
{ required: true, message: 'Please select', trigger: ['blur'] },
],
},
fileList: [],
userTypeOptions: [],
btnLoading: false,
saveBtnLoading: false,
loading: false,
dictionaryList: {}
dictionaryList: {},
}
},
mounted() {
@@ -137,8 +169,8 @@ export default {
{
name: this.data.Name,
url: this.data.Path,
path: this.data.Path
}
path: this.data.Path,
},
]
}
this.form.Id = this.data.Id
@@ -147,25 +179,31 @@ export default {
this.form.Path = this.data.Path
this.form.IsDeleted = this.data.IsDeleted
this.form.SignViewMinimumMinutes = this.data.SignViewMinimumMinutes
this.form.DocUserSignType = this.data.DocUserSignType
}
this.loading = false
},
//
getDicData() {
getBasicDataSelects(['Sys_Document']).then(res => {
this.dictionaryList = { ...res.Result }
}).catch(() => {
})
getBasicDataSelects(['Sys_Document'])
.then((res) => {
this.dictionaryList = { ...res.Result }
})
.catch(() => {})
},
//
getUserType() {
getTrialUserTypeList().then(res => {
this.userTypeOptions = res.Result
if (this.form.Id) {
this.form.NeedConfirmedUserTypeIdList = this.data.NeedConfirmedUserTypeIds
}
}).catch(() => { this.loading = false })
getTrialUserTypeList()
.then((res) => {
this.userTypeOptions = res.Result
if (this.form.Id) {
this.form.NeedConfirmedUserTypeIdList =
this.data.NeedConfirmedUserTypeIds
}
})
.catch(() => {
this.loading = false
})
},
beforeUpload(file) {
//
@@ -181,28 +219,37 @@ export default {
async handleUploadFile(param) {
this.loading = true
var file = await this.fileToBlob(param.file)
const res = await this.OSSclient.put(`/System/DocumentToSign/${param.file.name}${new Date().getTime()}`, file)
this.fileList.push({ name: param.file.name, path: this.$getObjectName(res.url), url: this.$getObjectName(res.url) })
const res = await this.OSSclient.put(
`/System/DocumentToSign/${param.file.name}${new Date().getTime()}`,
file
)
this.fileList.push({
name: param.file.name,
path: this.$getObjectName(res.url),
url: this.$getObjectName(res.url),
})
this.form.Path = this.$getObjectName(res.url)
this.form.Name = param.file.name
this.loading = false
},
handleSave() {
this.$refs.sysAttachmentFrom.validate(valid => {
this.$refs.sysAttachmentFrom.validate((valid) => {
if (!valid) return
if (!this.form.Name) {
this.$alert('Please select file.')
return
}
this.saveBtnLoading = true
addOrUpdateSystemDocument(this.form).then(res => {
this.saveBtnLoading = false
this.$emit('closeDialog')
this.$emit('getList')
this.$message.success('Uploaded successfully')
}).catch(() => {
this.saveBtnLoading = false
})
addOrUpdateSystemDocument(this.form)
.then((res) => {
this.saveBtnLoading = false
this.$emit('closeDialog')
this.$emit('getList')
this.$message.success('Uploaded successfully')
})
.catch(() => {
this.saveBtnLoading = false
})
})
},
handleRemoveFile() {
@@ -220,19 +267,21 @@ export default {
},
checkFileSuffix(fileName) {
var typeArr = ['pdf']
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;
@@ -245,8 +294,8 @@ export default {
color: #428bca;
font-size: 13px;
}
.account_item_clear{
.el-tag__close{
.account_item_clear {
.el-tag__close {
display: none !important;
}
}
@@ -2,27 +2,18 @@
<BaseContainer>
<template slot="search-container">
<el-form :inline="true" size="small">
<el-form-item label="文件类型:">
<el-select
v-model="searchData.FileTypeId"
style="width:150px;"
>
<el-select v-model="searchData.FileTypeId" style="width: 150px">
<el-option
v-for="item of dict.type.Sys_Document"
:key="item.value"
: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>
@@ -31,7 +22,11 @@
{{ $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>
@@ -39,7 +34,7 @@
<el-button
type="primary"
icon="el-icon-plus"
style="margin-left:auto;"
style="margin-left: auto"
size="small"
@click="handleAdd"
>
@@ -48,7 +43,7 @@
</template>
<template slot="main-container">
<el-table
v-adaptive="{bottomOffset:60}"
v-adaptive="{ bottomOffset: 60 }"
v-loading="loading"
:data="list"
stripe
@@ -81,7 +76,11 @@
show-overflow-tooltip
>
<template slot-scope="scope">
{{ scope.row.NeedConfirmedUserTypes?scope.row.NeedConfirmedUserTypes.join(', '):'' }}
{{
scope.row.NeedConfirmedUserTypes
? scope.row.NeedConfirmedUserTypes.join(', ')
: ''
}}
</template>
</el-table-column>
<el-table-column
@@ -91,7 +90,17 @@
sortable="custom"
>
<template slot-scope="scope">
{{ scope.row.IsDeleted?'Yes':'No' }}
{{ scope.row.IsDeleted ? 'Yes' : 'No' }}
</template>
</el-table-column>
<el-table-column
prop="DocUserSignType"
label="是否仅内部签署"
show-overflow-tooltip
sortable="custom"
>
<template slot-scope="scope">
{{ $fd('ReadingYesOrNo', Number(scope.row.DocUserSignType)) }}
</template>
</el-table-column>
<el-table-column
@@ -103,16 +112,10 @@
<el-table-column label="Action">
<template slot-scope="scope">
<el-button
type="text"
@click="handlePreview(scope.row)"
>
<el-button type="text" @click="handlePreview(scope.row)">
预览
</el-button>
<el-button
type="text"
@click="handleEdit(scope.row)"
>
<el-button type="text" @click="handleEdit(scope.row)">
编辑
</el-button>
<el-button
@@ -122,17 +125,20 @@
>
废除
</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
@@ -143,7 +149,11 @@
width="600px"
custom-class="base-dialog-wrapper"
>
<TemplateForm :data="currentRow" @closeDialog="closeDialog" @getList="getList" />
<TemplateForm
:data="currentRow"
@closeDialog="closeDialog"
@getList="getList"
/>
</el-dialog>
<!-- 预览文件 -->
@@ -155,10 +165,16 @@
append-to-body
custom-class="base-dialog-wrapper"
>
<div class="base-modal-body" style="border:2px solid #ccc;padding: 10px">
<PreviewFile v-if="previewVisible" :file-path="currentPath" :file-type="currentType" />
<div
class="base-modal-body"
style="border: 2px solid #ccc; padding: 10px"
>
<PreviewFile
v-if="previewVisible"
:file-path="currentPath"
:file-type="currentType"
/>
</div>
</el-dialog>
</template>
</BaseContainer>
@@ -175,7 +191,7 @@ const searchDataDefault = () => {
FileTypeId: '',
Name: '',
PageIndex: 1,
PageSize: 20
PageSize: 20,
}
}
export default {
@@ -193,7 +209,7 @@ export default {
editVisible: false,
previewVisible: false,
title: '',
loading: false
loading: false,
}
},
mounted() {
@@ -203,22 +219,24 @@ export default {
//
getList() {
this.loading = true
getSystemDocumentList(this.searchData).then(res => {
this.loading = false
const { CurrentPageData, TotalCount } = res.Result
CurrentPageData.forEach(item => {
item.NeedConfirmedUserTypes = []
item.NeedConfirmedUserTypeIds = []
item.NeedConfirmedUserTypeList.forEach((i) => {
item.NeedConfirmedUserTypes.push(i.UserTypeShortName)
item.NeedConfirmedUserTypeIds.push(i.NeedConfirmUserTypeId)
getSystemDocumentList(this.searchData)
.then((res) => {
this.loading = false
const { CurrentPageData, TotalCount } = res.Result
CurrentPageData.forEach((item) => {
item.NeedConfirmedUserTypes = []
item.NeedConfirmedUserTypeIds = []
item.NeedConfirmedUserTypeList.forEach((i) => {
item.NeedConfirmedUserTypes.push(i.UserTypeShortName)
item.NeedConfirmedUserTypeIds.push(i.NeedConfirmUserTypeId)
})
})
this.list = CurrentPageData
this.total = TotalCount
})
.catch(() => {
this.loading = false
})
this.list = CurrentPageData
this.total = TotalCount
}).catch(() => {
this.loading = false
})
},
//
handleAdd() {
@@ -230,7 +248,9 @@ export default {
handlePreview(row) {
const { Name, FullFilePath } = row
this.currentPath = FullFilePath
this.currentType = row.Name ? Name.substring(Name.lastIndexOf('.') + 1).toLocaleLowerCase() : ''
this.currentType = row.Name
? Name.substring(Name.lastIndexOf('.') + 1).toLocaleLowerCase()
: ''
this.previewVisible = true
},
//
@@ -244,39 +264,47 @@ export default {
this.$confirm('是否确认废除此文件?', {
type: 'warning',
distinguishCancelAndClose: true,
})
.then(() => {
this.loading = true
userAbandonDoc(row.Id, true)
.then(res => {
.then((res) => {
this.loading = false
if (res.IsSuccess) {
this.getList()
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.$message.success(
this.$t('common:message:savedSuccessfully')
)
}
}).catch(() => { this.loading = false })
}).catch(() => {})
})
.catch(() => {
this.loading = false
})
})
.catch(() => {})
},
//
handleDelete(row) {
this.$confirm(this.$t('trials:staffResearch:message:confirmDel'), {
type: 'warning',
distinguishCancelAndClose: true
distinguishCancelAndClose: true,
}).then(() => {
this.loading = true
deleteSystemDocument(row.Id)
.then((res) => {
this.loading = false
if (res.IsSuccess) {
this.list.splice(
this.list.findIndex((item) => item.Id === row.Id),
1
)
this.$message.success('删除成功!')
}
})
.catch(() => {
this.loading = false
})
})
.then(() => {
this.loading = true
deleteSystemDocument(row.Id)
.then(res => {
this.loading = false
if (res.IsSuccess) {
this.list.splice(this.list.findIndex(item => item.Id === row.Id), 1)
this.$message.success('删除成功!')
}
}).catch(() => {
this.loading = false
})
})
},
//
closeDialog() {
@@ -305,7 +333,7 @@ export default {
this.searchData.SortField = column.prop
this.searchData.PageIndex = 1
this.getList()
}
}
},
},
}
</script>
@@ -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;
}
}
@@ -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,29 @@
<el-table-column label="Action">
<template slot-scope="scope">
<el-button
type="text"
@click="handleDownload(scope.row)"
>
<el-button type="text" @click="PreviewFile(scope.row)">
{{ $t('common:button:preview') }}
</el-button>
<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 +151,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 +176,7 @@ const searchDataDefault = () => {
BusinessScenarioEnum: null,
Name: '',
PageIndex: 1,
PageSize: 20
PageSize: 20,
}
}
export default {
@@ -168,7 +189,7 @@ export default {
total: 0,
currentRow: {},
editDialog: { title: '', visible: false },
loading: false
loading: false,
}
},
mounted() {
@@ -183,15 +204,28 @@ export default {
})
})
},
PreviewFile(row) {
let basePath = window.location.origin
if (window.location.protocol !== 'https:') {
basePath = 'https://irc.test.extimaging.com'
}
let data = {
name: row.NameCN,
path: basePath + row.Path,
}
this.$emit('PreviewFile', data)
},
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 +236,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 +271,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>
+106 -16
View File
@@ -1,14 +1,54 @@
<template>
<div class="attachment-wrapper">
<el-tabs v-model="activeTab" @tab-click="clickTab">
<el-tab-pane v-for="item in $d.Common_File_Type" :key="item.value" :label="item.label" :name="String(item.value)">
<UploadTemplate v-if="activeTab === '1'" />
<ExportTemplate v-if="activeTab === '2'" />
<EmailTemplate v-if="activeTab === '3'" />
<CommonTemplate v-if="activeTab === '4'" />
<SignatureTemplate v-if="activeTab === '5'" />
<el-tab-pane
v-for="item in $d.Common_File_Type"
:key="item.value"
:label="item.label"
:name="String(item.value)"
>
<UploadTemplate
v-if="activeTab === '1' && item.value == activeTab"
@PreviewFile="PreviewFile"
/>
<ExportTemplate
v-if="activeTab === '2' && item.value == activeTab"
@PreviewFile="PreviewFile"
/>
<EmailTemplate v-if="activeTab === '3' && item.value == activeTab" />
<CommonTemplate v-if="activeTab === '4' && item.value == activeTab" />
<SignatureTemplate
v-if="activeTab === '5' && item.value == activeTab"
/>
</el-tab-pane>
</el-tabs>
<!-- 预览 -->
<el-dialog
v-if="Preview.visible"
:visible.sync="Preview.visible"
:close-on-click-modal="false"
:fullscreen="true"
:title="Preview.title"
width="600px"
custom-class="base-dialog-wrapper"
>
<vue-office-docx
v-if="docxTypes.includes(Preview.type)"
:src="Preview.path"
style="height: calc(100vh - 70px)"
@rendered="renderedHandler"
@error="errorHandler"
/>
<vue-office-excel
v-else-if="excelTypes.includes(Preview.type)"
:src="Preview.path"
:options="options"
style="height: calc(100vh - 70px)"
@rendered="renderedHandler"
@error="errorHandler"
/>
<PreviewFile v-else :file-path="Preview.path" :file-type="Preview.type" />
</el-dialog>
</div>
</template>
<script>
@@ -17,6 +57,11 @@ import ExportTemplate from './components/ExportTemplate'
import EmailTemplate from './components/EmailTemplate'
import CommonTemplate from './components/CommonTemplate'
import SignatureTemplate from './components/SignatureTemplate'
import VueOfficeDocx from '@vue-office/docx'
import '@vue-office/docx/lib/index.css'
import VueOfficeExcel from '@vue-office/excel'
import '@vue-office/excel/lib/index.css'
import PreviewFile from '@/components/PreviewFile/index'
export default {
name: 'Attachment',
components: {
@@ -24,11 +69,35 @@ export default {
ExportTemplate,
EmailTemplate,
CommonTemplate,
SignatureTemplate
SignatureTemplate,
VueOfficeDocx,
VueOfficeExcel,
PreviewFile,
},
data() {
return {
activeTab: null
activeTab: null,
Preview: {
visible: false,
title: '',
type: null,
path: null,
},
excelTypes: ['xlsx', 'xls'],
docxTypes: ['docx', 'doc'],
options: {
xls: false, //xlsxfalsexlstrue
minColLength: 0, // excelxlsx0.
minRowLength: 0, // excelxlsx0.
widthOffset: 10, // Npx
heightOffset: 10, // Npx
beforeTransformData: (workbookData) => {
return workbookData
}, //exceljsexcelexcelvalue
transformData: (workbookData) => {
return workbookData
}, //exceltransformDatatext
},
}
},
mounted() {
@@ -40,25 +109,46 @@ export default {
},
methods: {
clickTab(tab, event) {
this.$router.push({ path: `/dictionary/attachment?tabActive=${tab.name}` })
}
}
this.$router.push({
path: `/dictionary/attachment?tabActive=${tab.name}`,
})
},
PreviewFile(data) {
if (!data.path) return false
let type = data.path
.substring(data.path.lastIndexOf('.') + 1)
.toLocaleLowerCase()
this.Preview.path = data.path
this.Preview.type = type
this.Preview.title = data.name
if (this.excelTypes.includes(type)) {
this.options.xls = type === 'xls'
}
this.Preview.visible = true
},
renderedHandler() {
console.log('渲染完成')
},
errorHandler() {
console.log('渲染失败')
},
},
}
</script>
<style lang="scss">
.attachment-wrapper{
.el-tabs{
.attachment-wrapper {
.el-tabs {
height: 100%;
display: flex;
flex-direction: column;
}
.el-tabs__header {
height: 40px;
margin-bottom:5px;
margin-bottom: 5px;
}
.el-tabs__content{
.el-tabs__content {
flex: 1;
.el-tab-pane{
.el-tab-pane {
height: 100%;
}
}
+58 -3
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,
@@ -14,11 +14,17 @@
</el-form-item>
<!-- 中心名称 -->
<el-form-item :label="$t('trials:customSite:form:siteName')" prop="SiteName">
<el-form-item
:label="$t('trials:customSite:form:siteName')"
prop="SiteName"
>
<el-input v-model="form.SiteName" />
</el-form-item>
<!-- 中心名称CN -->
<el-form-item :label="$t('trials:customSite:form:siteName') + 'CN'" prop="SiteNameCN">
<el-form-item
:label="$t('trials:customSite:form:siteName') + 'CN'"
prop="SiteNameCN"
>
<el-input v-model="form.SiteNameCN" />
</el-form-item>
@@ -46,7 +52,7 @@
v-model="form.HospitalId"
clearable
placeholder="Please select"
style="width:100%;"
style="width: 100%"
>
<el-option
v-for="item in hospitalList"
@@ -73,10 +79,22 @@
<el-input v-model="form.ContactPhone" />
</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 :disabled="btnLoading" size="small" type="primary" @click="handleCancel">Cancel</el-button>
<el-button size="small" type="primary" :loading="btnLoading" @click="handleSave">Save</el-button>
<el-button
:disabled="btnLoading"
size="small"
type="primary"
@click="handleCancel"
>Cancel</el-button
>
<el-button
size="small"
type="primary"
:loading="btnLoading"
@click="handleSave"
>Save</el-button
>
</el-form-item>
</div>
</el-form>
@@ -91,8 +109,8 @@ export default {
type: Object,
default() {
return {}
}
}
},
},
},
data() {
return {
@@ -111,50 +129,42 @@ export default {
ContactName: '',
ContactPhone: '',
UniqueCode: '',
Address: ''
Address: '',
},
rules: {
SiteName: [
{ required: true, message: 'Please specify', trigger: 'blur' },
{ max: 50, message: 'The maximum length is 50' }
// { max: 50, message: 'The maximum length is 50' }
],
SiteNameCN: [
{ required: true, message: 'Please specify', trigger: 'blur' },
{ max: 50, message: 'The maximum length is 50' }
// { max: 50, message: 'The maximum length is 50' }
],
SiteCode: [
{ required: true, message: 'Please specify', trigger: 'blur' },
{ max: 50, message: 'The maximum length is 50' }
{ max: 50, message: 'The maximum length is 50' },
],
Country: [
{ required: true, message: 'Please specify', trigger: 'blur' },
{ max: 50, message: 'The maximum length is 50' }
{ max: 50, message: 'The maximum length is 50' },
],
City: [
{ required: true, message: 'Please specify', trigger: 'blur' },
{ max: 50, message: 'The maximum length is 50' }
{ max: 50, message: 'The maximum length is 50' },
],
// HospitalId: [
// { required: true, message: 'Please specify', trigger: 'blur' }
// ],
Address: [
{ required: true, message: 'Please specify', trigger: 'blur' }
{ required: true, message: 'Please specify', trigger: 'blur' },
],
DirectorName: [
{ max: 50, message: 'The maximum length is 50' }
],
DirectorPhone: [
{ max: 50, message: 'The maximum length is 50' }
],
ContactName: [
{ max: 50, message: 'The maximum length is 50' }
],
ContactPhone: [
{ max: 50, message: 'The maximum length is 50' }
]
DirectorName: [{ max: 50, message: 'The maximum length is 50' }],
DirectorPhone: [{ max: 50, message: 'The maximum length is 50' }],
ContactName: [{ max: 50, message: 'The maximum length is 50' }],
ContactPhone: [{ max: 50, message: 'The maximum length is 50' }],
},
loading: false,
show: false
show: false,
}
},
mounted() {
@@ -163,20 +173,22 @@ export default {
},
methods: {
handleSave() {
this.$refs.siteForm.validate(valid => {
this.$refs.siteForm.validate((valid) => {
if (!valid) return
this.btnLoading = true
addOrUpdateSite(this.form).then(res => {
this.btnLoading = false
if (res.IsSuccess) {
this.$message.success('Saved successfully')
this.$refs['siteForm'].resetFields()
this.$emit('getList')
this.$emit('close')
}
}).catch(() => {
this.btnLoading = false
})
addOrUpdateSite(this.form)
.then((res) => {
this.btnLoading = false
if (res.IsSuccess) {
this.$message.success('Saved successfully')
this.$refs['siteForm'].resetFields()
this.$emit('getList')
this.$emit('close')
}
})
.catch(() => {
this.btnLoading = false
})
})
},
handleCancel() {
@@ -189,8 +201,7 @@ export default {
if (Object.keys(this.data).length && this.data.Id) {
this.form = { ...this.data }
}
}
}
},
},
}
</script>
@@ -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);
}
@@ -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);
}
@@ -13,14 +13,14 @@
{{ question.GroupName }}
</div>
<div
v-else-if="question.Type==='table'"
v-else-if="question.Type==='table' || question.Type==='basicTable'"
style="font-weight: bold;font-size: 14px;margin: 5px 0px;"
>
{{ question.QuestionName }}
</div>
</div>
<template v-else>
<el-form-item
v-if="(question.ShowQuestion===1 && question.ParentTriggerValueList.includes(questionForm[question.ParentId])) || question.ShowQuestion===0"
v-if="(question.ShowQuestion===1 && question.ParentTriggerValueList.includes(String(questionForm[question.ParentId]))) || question.ShowQuestion===0"
:label="`${question.QuestionName}`"
:prop="question.Id"
:rules="[
@@ -139,14 +139,29 @@
/>
<!-- 数值 -->
<!-- :precision="2" :step="0.1" :max="10" -->
<el-input-number
<!-- <el-input-number
v-if="question.Type==='number'"
v-model="questionForm[question.Id]"
/> -->
<el-input
v-if="question.Type==='number'"
v-model="questionForm[question.Id]"
type="number"
:disabled="question.DataSource === 1"
>
<template v-if="question.Unit" slot="append">{{$fd('ValueUnit', question.Unit)}}</template>
</el-input>
<!-- 自动分类 -->
<el-input
v-if="question.Type==='calss'"
v-model="questionForm[question.Id]"
disabled
/>
<!-- 上传图像 -->
<el-upload
v-if="question.Type==='upload'"
:action="accept"
action
:accept="question.FileType"
:limit="question.ImageCount"
:on-preview="handlePictureCardPreview"
:before-upload="handleBeforeUpload"
File diff suppressed because it is too large Load Diff
@@ -107,7 +107,7 @@
<el-table-column
prop="IsShowInDicom"
label="是否在阅片页面显示"
width="140"
width="150"
show-overflow-tooltip
>
<template slot-scope="scope">
@@ -115,13 +115,13 @@
</template>
</el-table-column>
<!-- 注释 -->
<el-table-column
<!-- <el-table-column
prop="Remark"
:label="$t('trials:readingUnit:qsList:title:Remark')"
width="140"
show-overflow-tooltip
>
</el-table-column>
</el-table-column> -->
<el-table-column
prop="GlobalReadingShowType"
label="是否在全局阅片显示"
@@ -132,6 +132,39 @@
{{ $fd('GlobalReadingShowType', scope.row.GlobalReadingShowType) }}
</template>
</el-table-column>
<!-- 导出标识 -->
<!-- <el-table-column
prop="ExportIdentification"
:label="$t('trials:readingUnit:qsList:title:ExportIdentification')"
width="160"
show-overflow-tooltip
>
<template slot-scope="scope">
{{ $fd('ExportIdentification', scope.row.ExportIdentification) }}
</template>
</el-table-column> -->
<!-- 问题标识 -->
<el-table-column
prop="QuestionType"
label="问题标识"
width="140"
show-overflow-tooltip
>
<template slot-scope="scope">
{{ $fd('QuestionType', scope.row.QuestionType) }}
</template>
</el-table-column>
<!-- 导出目标表格 -->
<el-table-column
prop="ExportResult"
:label="$t('trials:readingUnit:qsList:title:ExportResult')"
width="160"
show-overflow-tooltip
>
<template slot-scope="scope">
{{ getStringResult(scope.row.ExportResult, 'ExportResult') }}
</template>
</el-table-column>
<!-- 限制编辑 -->
<el-table-column
prop="LimitEdit"
@@ -170,7 +203,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)"
>
表格问题
@@ -201,7 +234,7 @@
:visible.sync="addOrEdit.visible"
:close-on-click-modal="false"
:title="`${addOrEdit.title}`"
width="600px"
width="650px"
append-to-body
custom-class="base-dialog-wrapper"
>
@@ -209,6 +242,8 @@
ref="addOrEdit"
:data="rowData"
:type="type"
:criterionType="data.CriterionType"
:criterionId="data.Id"
@close="addOrEdit.visible = false"
@getList="getList"
/>
@@ -241,6 +276,7 @@
>
<TableQsList
:criterion-id="criterionId"
:criterionType="data.CriterionType"
:reading-question-id="rowData.Id"
:is-complete-config="data.IsCompleteConfig"
:lesion-type="rowData.LesionType"
@@ -316,7 +352,7 @@ export default {
}).catch(() => { this.loading = false })
},
handleAdd() {
this.rowData = { ReadingQuestionCriterionSystemId: this.criterionId, Id: '' }
this.rowData = { ReadingQuestionCriterionSystemId: this.criterionId, Id: ''}
this.type = 'add'
this.addOrEdit.title = '添加'
this.addOrEdit.visible = true
@@ -355,6 +391,11 @@ export default {
this.config.title = `${this.$fd('LesionType', row.LesionType)}${this.$t('trials:readingUnit:qsList:title:tableQs')}`
this.config.visible = true
},
getStringResult(arr, dictionaryCode) {
let newArr = arr.map(i => this.$fd(dictionaryCode, i))
let delimiter = this.$i18n.locale === 'zh' ? '' : ', '
return newArr.join(delimiter)
},
//
handleSearch() {
this.getList()
@@ -1,5 +1,5 @@
<template>
<div v-loading="loading" style="min-height:400px;">
<div v-loading="loading" style="min-height:400px;padding:20px">
<el-form
v-if="isRender"
ref="questions"
@@ -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,
@@ -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="部位">
File diff suppressed because it is too large Load Diff
@@ -27,12 +27,12 @@
/>
<el-table-column
prop="QuestionName"
label="名称"
label="问题名称"
show-overflow-tooltip
/>
<el-table-column
prop="QuestionEnName"
label="名称(EN)"
label="问题名称(EN)"
show-overflow-tooltip
/>
<el-table-column
@@ -44,7 +44,7 @@
{{ $fd('Criterion_Question_Type',scope.row.Type) }}
</template>
</el-table-column>
<el-table-column
<!-- <el-table-column
prop="TableQuestionType"
label="选项类型"
show-overflow-tooltip
@@ -60,20 +60,7 @@
show-overflow-tooltip
min-width="110"
/>
<el-table-column
prop="DependShowOrder"
label="关联问题"
show-overflow-tooltip
/>
<el-table-column
prop="DataTableColumn"
label="关联字段"
show-overflow-tooltip
>
<template slot-scope="scope">
{{ $fd('OrganColumn',scope.row.DataTableColumn) }}
</template>
</el-table-column>
-->
<el-table-column
prop="ShowQuestion"
label="是否显示"
@@ -94,7 +81,7 @@
{{ $fd('QuestionRequired',scope.row.IsRequired) }}
</template>
</el-table-column>
<el-table-column
<!-- <el-table-column
prop="IsJudgeQuestion"
label="是否裁判问题"
show-overflow-tooltip
@@ -102,8 +89,8 @@
<template slot-scope="scope">
{{ $fd('YesOrNo',scope.row.IsJudgeQuestion) }}
</template>
</el-table-column>
<el-table-column
</el-table-column> -->
<!-- <el-table-column
prop="ParentQuestionShowOrder"
label="显示依赖父问题"
min-width="90"
@@ -124,8 +111,64 @@
prop="RelevanceValue"
label="必填触发值"
show-overflow-tooltip
/> -->
<!-- 导出标识 -->
<!-- <el-table-column
prop="ExportIdentification"
:label="$t('trials:readingUnit:qsList:title:ExportIdentification')"
show-overflow-tooltip
>
<template slot-scope="scope">
{{ $fd('ExportIdentification', scope.row.ExportIdentification) }}
</template>
</el-table-column> -->
<!-- 问题标识 -->
<el-table-column
prop="QuestionMark"
label="问题标识"
width="140"
show-overflow-tooltip
>
<template slot-scope="scope">
{{ $fd('QuestionMark', scope.row.QuestionMark) }}
</template>
</el-table-column>
<!-- 导出目标表格 -->
<el-table-column
prop="ExportResult"
:label="$t('trials:readingUnit:qsList:title:ExportResult')"
show-overflow-tooltip
>
<template slot-scope="scope">
{{ getStringResult(scope.row.ExportResult, 'ExportResult') }}
</template>
</el-table-column>
<!-- 限制编辑 -->
<el-table-column
prop="LimitEdit"
:label="$t('trials:readingUnit:qsList:title:limitEdit')"
width="160"
show-overflow-tooltip
>
<template slot-scope="scope">
{{ $fd('LimitEdit', scope.row.LimitEdit) }}
</template>
</el-table-column>
<!-- <el-table-column
prop="DependShowOrder"
label="关联问题"
show-overflow-tooltip
/>
<el-table-column
prop="DataTableColumn"
label="关联字段"
show-overflow-tooltip
>
<template slot-scope="scope">
{{ $fd('OrganColumn',scope.row.DataTableColumn) }}
</template>
</el-table-column> -->
<!-- <el-table-column
prop="ImageCount"
label="最大上传个数"
show-overflow-tooltip
@@ -134,6 +177,7 @@
{{ scope.row.Type === 'upload'?scope.row.ImageCount:'' }}
</template>
</el-table-column>
-->
<el-table-column
prop="MaxRowCount"
label="最大行数"
@@ -184,7 +228,7 @@
:visible.sync="addOrEdit.visible"
:close-on-click-modal="false"
:title="addOrEdit.title"
width="600px"
width="650px"
append-to-body
custom-class="base-dialog-wrapper"
>
@@ -194,6 +238,7 @@
:type="type"
:reading-question-id="readingQuestionId"
:criterion-id="criterionId"
:criterionType="criterionType"
@close="addOrEdit.visible = false"
@getList="getList"
/>
@@ -225,7 +270,11 @@ export default {
criterionId: {
type: String,
required: true
}
},
criterionType: {
type: Number,
required: true
},
},
data() {
@@ -285,6 +334,11 @@ export default {
}).catch(() => { this.loading = false })
})
},
getStringResult(arr, dictionaryCode) {
let newArr = arr.map(i => this.$fd(dictionaryCode, i))
let delimiter = this.$i18n.locale === 'zh' ? '' : ', '
return newArr.join(delimiter)
},
//
handleSearch() {
this.getList()
@@ -23,6 +23,17 @@
<el-input style="width: 300px" v-model="form.EmailTopic" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="邮件延时发送s数" prop="EmailDelaySeconds">
<el-input
v-model.number="form.EmailDelaySeconds"
style="width: 300px"
type="number"
clearable
>
</el-input>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="业务层级" prop="BusinessLevelEnum">
<el-select v-model="form.BusinessLevelEnum" clearable class="mr">
@@ -59,7 +70,7 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" v-if="systemLevel !== 1">
<el-col :span="12">
<el-form-item label="收件人" prop="ToUserTypeList">
<el-select
v-model="form.ToUserTypeList"
@@ -76,7 +87,7 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" v-if="!systemLevel">
<el-col :span="12">
<el-form-item label="抄送人" prop="CopyUserTypeList">
<el-select
v-model="form.CopyUserTypeList"
@@ -93,13 +104,13 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" v-if="!systemLevel">
<el-col :span="12">
<el-form-item label="加急状态" prop="EmailUrgentEnum">
<el-select
v-model="form.EmailUrgentEnum"
@change="
(v) => {
v === 1 ? (form.EmailCron = null) : null;
v === 1 ? (form.EmailCron = null) : null
}
"
clearable
@@ -114,7 +125,7 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" v-if="!systemLevel">
<el-col :span="12">
<el-form-item
label="发送周期和时间"
v-if="form.EmailUrgentEnum !== 1"
@@ -124,7 +135,7 @@
<el-button type="primary" @click="showDialog">生成 cron</el-button>
</el-form-item>
</el-col>
<el-col :span="12" v-if="!systemLevel">
<el-col :span="12">
<el-form-item label="是否需要回执" prop="IsReturnRequired">
<el-radio-group v-model="form.IsReturnRequired">
<el-radio :label="true"></el-radio>
@@ -132,7 +143,7 @@
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12" v-if="!systemLevel">
<el-col :span="12">
<el-form-item label="是否自动发送" prop="IsAutoSend">
<el-radio-group v-model="form.IsAutoSend">
<el-radio :label="true"></el-radio>
@@ -140,13 +151,13 @@
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12" v-if="!systemLevel">
<el-col :span="12" v-if="systemLevel">
<el-form-item label="是否区分标准" prop="IsDistinguishCriteria">
<el-radio-group
v-model="form.IsDistinguishCriteria"
@change="
() => {
form.CriterionTypeEnum = null;
form.CriterionTypeEnum = null
}
"
>
@@ -155,7 +166,7 @@
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12" v-if="!systemLevel">
<el-col :span="12">
<el-form-item
:label="$t('trials:auditRecord:table:criterion')"
v-if="form.IsDistinguishCriteria"
@@ -171,7 +182,7 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" v-if="!systemLevel">
<el-col :span="12">
<el-form-item label="是否启用" prop="IsEnable">
<el-radio-group v-model="form.IsEnable">
<el-radio :label="true"></el-radio>
@@ -179,7 +190,7 @@
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12" v-if="!systemLevel">
<el-col :span="12">
<el-form-item label="附件(CN" prop="IsEnable">
<el-upload
class="upload-demo"
@@ -196,12 +207,12 @@
size="small"
type="primary"
:disabled="fileListCN.length > 0"
>{{ $t("common:button:upload") }}</el-button
>{{ $t('common:button:upload') }}</el-button
>
</el-upload>
</el-form-item>
</el-col>
<el-col :span="12" v-if="!systemLevel">
<el-col :span="12">
<el-form-item label="附件(EN" prop="IsEnable">
<el-upload
class="upload-demo"
@@ -218,25 +229,49 @@
size="small"
type="primary"
:disabled="fileListEN.length > 0"
>{{ $t("common:button:upload") }}</el-button
>{{ $t('common:button:upload') }}</el-button
>
</el-upload>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="邮件内容模版(CN" prop="EmailHtmlContentCN">
<el-form-item
label="邮件内容模版(CN"
prop="EmailHtmlContentCN"
style="position: relative"
>
<el-input
v-model="form.EmailHtmlContentCN"
type="textarea"
:autosize="{ minRows: 8, maxRows: 8 }"
/>
<el-button
:disabled="!form.EmailHtmlContentCN"
type="text"
@click="PreviewHTML(form.EmailHtmlContentCN)"
style="position: absolute; left: -50px; top: 30px"
>
{{ $t('common:button:preview') }}
</el-button>
</el-form-item>
<el-form-item label="邮件内容模版(EN" prop="EmailHtmlContent">
<el-form-item
label="邮件内容模版(EN"
prop="EmailHtmlContent"
style="position: relative"
>
<el-input
v-model="form.EmailHtmlContent"
type="textarea"
:autosize="{ minRows: 8, maxRows: 8 }"
/>
<el-button
:disabled="!form.EmailHtmlContent"
type="text"
@click="PreviewHTML(form.EmailHtmlContent)"
style="position: absolute; left: -50px; top: 30px"
>
{{ $t('common:button:preview') }}
</el-button>
</el-form-item>
<div
class="base-dialog-footer"
@@ -259,219 +294,234 @@
</el-form>
</template>
<script>
import { Upload, addOrUpdateEmailNoticeConfigList } from "@/api/dictionary";
import { Upload, addOrUpdateEmailNoticeConfigList } from '@/api/dictionary'
import vcrontab from 'vcrontab'
export default {
props: {
data: {
type: Object,
default() {
return {};
return {}
},
},
systemLevel: {
type: Number,
default() {
return 0;
return 0
},
},
isDistinguishCriteria: {
type: Boolean,
default() {
return false
},
},
},
components: { vcrontab },
data() {
return {
expression: "",
expression: '',
showCron: false,
form: {
Id: "",
Code: "",
EmailTopicCN: "",
EmailTopic: "",
BusinessLevelEnum: "",
BusinessModuleEnum: "",
ToUserTypeList: "",
CopyUserTypeList: "",
EmailUrgentEnum: "",
EmailCron: "",
Id: '',
Code: '',
EmailTopicCN: '',
EmailTopic: '',
BusinessLevelEnum: '',
BusinessModuleEnum: '',
ToUserTypeList: '',
CopyUserTypeList: '',
EmailUrgentEnum: '',
EmailCron: '',
BusinessScenarioEnum: null,
EmailHtmlContentCN: "",
EmailHtmlContent: "",
AttachPath: "",
AttachName: "",
AttachNameCN: "",
AttachCNPath: "",
EmailHtmlContentCN: '',
EmailHtmlContent: '',
AttachPath: '',
AttachName: '',
AttachNameCN: '',
AttachCNPath: '',
IsDistinguishCriteria: true,
IsReturnRequired: true,
IsUrgent: true,
IsEnable: true,
IsAutoSend: true,
CriterionTypeEnum: null,
EmailDelaySeconds: null,
},
rules: {
Code: [{ required: true, message: "Please select", trigger: ["blur"] }],
Code: [{ required: true, message: 'Please select', trigger: ['blur'] }],
BusinessScenarioEnum: [
{ required: true, message: "Please select", trigger: ["blur"] },
{ required: true, message: 'Please select', trigger: ['blur'] },
],
EmailTopicCN: [
{ required: true, message: "Please select", trigger: ["blur"] },
{ required: true, message: 'Please select', trigger: ['blur'] },
],
EmailTopic: [
{ required: true, message: "Please select", trigger: ["blur"] },
{ required: true, message: 'Please select', trigger: ['blur'] },
],
BusinessLevelEnum: [
{ required: true, message: "Please select", trigger: ["blur"] },
{ required: true, message: 'Please select', trigger: ['blur'] },
],
ToUserTypeList: [
{ required: true, message: "Please select", trigger: ["blur"] },
{ required: true, message: 'Please select', trigger: ['blur'] },
],
EmailCron: [
{ required: true, message: "Please select", trigger: ["blur"] },
{ required: true, message: 'Please select', trigger: ['blur'] },
],
EmailUrgentEnum: [
{ required: true, message: "Please select", trigger: ["blur"] },
{ required: true, message: 'Please select', trigger: ['blur'] },
],
EmailHtmlContentCN: [
{ required: true, message: "Please select", trigger: ["blur"] },
{ required: true, message: 'Please select', trigger: ['blur'] },
],
EmailHtmlContent: [
{ required: true, message: "Please select", trigger: ["blur"] },
{ required: true, message: 'Please select', trigger: ['blur'] },
],
CriterionTypeEnum: [
{ required: true, message: "Please select", trigger: ["blur"] },
{ required: true, message: 'Please select', trigger: ['blur'] },
],
},
scenarioOption: [],
fileListCN: [],
fileListEN: [],
loading: false,
};
}
},
mounted() {
this.initForm();
this.initForm()
if (this.systemLevel) {
if (this.systemLevel === 1) {
this.form.ToUserTypeList = [];
this.form.ToUserTypeList = []
}
this.form.CopyUserTypeList = [];
this.form.EmailUrgentEnum = "3";
this.form.IsReturnRequired = false;
this.form.IsDistinguishCriteria = false;
this.form.CopyUserTypeList = []
this.form.EmailUrgentEnum = 3
this.form.IsReturnRequired = false
this.form.IsDistinguishCriteria = false
} else {
this.form.IsDistinguishCriteria = this.isDistinguishCriteria
}
},
methods: {
showDialog() {
this.expression = this.form.EmailCron; // cron UI
this.showCron = true;
this.expression = this.form.EmailCron // cron UI
this.showCron = true
},
crontabFill(value) {
this.form.EmailCron = value;
this.form.EmailCron = value
},
handleRemoveFileCN() {
this.form.AttachNameCN = null;
this.form.AttachCNPath = null;
this.fileListCN = [];
this.form.AttachNameCN = null
this.form.AttachCNPath = null
this.fileListCN = []
},
beforeUploadCN() {
if (this.fileListCN.length > 0) {
this.$alert("最多只能传一个附件");
return;
this.$alert('最多只能传一个附件')
return
}
},
handlePreviewCN(row, r2) {
if (row.fullPath) {
window.open(row.fullPath, "_blank");
window.open(row.fullPath, '_blank')
}
},
handleUploadFileCN(param) {
this.btnLoading = true;
const formData = new FormData();
formData.append("file", param.file);
this.form.AttachNameCN = param.file.name;
this.btnLoading = true
const formData = new FormData()
formData.append('file', param.file)
this.form.AttachNameCN = param.file.name
Upload(formData, 3).then((res) => {
this.form.AttachCNPath = res.Result.FilePath;
this.btnLoading = false;
this.form.AttachCNPath = res.Result.FilePath
this.btnLoading = false
this.fileListCN.push({
name: param.file.name,
path: res.Result.FilePath,
fullPath: res.Result.FullFilePath,
url: res.Result.FilePath,
});
});
})
})
},
handleUploadFileEN(param) {
this.btnLoading = true;
const formData = new FormData();
formData.append("file", param.file);
this.form.AttachName = param.file.name;
this.btnLoading = true
const formData = new FormData()
formData.append('file', param.file)
this.form.AttachName = param.file.name
Upload(formData, 3).then((res) => {
this.form.AttachPath = res.Result.FilePath;
this.btnLoading = false;
this.form.AttachPath = res.Result.FilePath
this.btnLoading = false
this.fileListEN.push({
name: param.file.name,
path: res.Result.FilePath,
fullPath: res.Result.FullFilePath,
url: res.Result.FilePath,
});
});
})
})
},
handleRemoveFileEN() {
this.form.AttachName = null;
this.form.AttachPath = null;
this.fileListEN = [];
this.form.AttachName = null
this.form.AttachPath = null
this.fileListEN = []
},
beforeUploadEN() {
if (this.fileListEN.length > 0) {
this.$alert("最多只能传一个附件");
return;
this.$alert('最多只能传一个附件')
return
}
},
handlePreviewEN(row, r2) {
if (row.fullPath) {
window.open(row.fullPath, "_blank");
window.open(row.fullPath, '_blank')
}
},
async initForm() {
this.loading = true;
this.loading = true
// await this.getScenarios()
for (const k in this.form) {
if (this.data.hasOwnProperty(k)) {
this.form[k] = this.data[k];
this.form[k] = this.data[k]
}
if (k === "AttachPath" && this.form[k]) {
if (k === 'AttachPath' && this.form[k]) {
this.fileListEN.push({
name: this.data.AttachName,
path: this.data.AttachPath,
fullPath: this.data.AttachPath,
url: this.data.AttachPath,
});
})
}
if (k === "AttachCNPath" && this.form[k]) {
if (k === 'AttachCNPath' && this.form[k]) {
this.fileListCN.push({
name: this.data.AttachNameCN,
path: this.data.AttachCNPath,
fullPath: this.data.AttachCNPath,
url: this.data.AttachCNPath,
});
})
}
}
this.loading = false;
this.loading = false
},
//
handleSave() {
this.$refs.emailForm.validate((valid) => {
if (!valid) return;
this.loading = true;
this.form.systemLevel = this.systemLevel;
if (!valid) return
this.loading = true
this.form.systemLevel = this.systemLevel
addOrUpdateEmailNoticeConfigList(this.form)
.then((res) => {
this.loading = false;
this.$emit("closeDialog");
this.$emit("getList");
this.$message.success("Saved successfully");
this.loading = false
this.$emit('closeDialog')
this.$emit('getList')
this.$message.success('Saved successfully')
})
.catch(() => {
this.loading = false;
});
});
this.loading = false
})
})
},
//
PreviewHTML(html) {
this.$emit('PreviewHTML', html)
},
},
};
}
</script>
@@ -1,13 +1,8 @@
<template>
<box-content>
<div class="search">
<el-form
v-if="!systemLevel"
:inline="true"
size="mini"
class="base-search-form"
>
<el-form-item label="是否需要回执:">
<el-form :inline="true" size="mini" class="base-search-form">
<el-form-item label="是否需要回执:" v-if="!systemLevel">
<el-select
v-model="searchData.IsReturnRequired"
clearable
@@ -21,7 +16,7 @@
/>
</el-select>
</el-form-item>
<el-form-item label="是否加急:">
<el-form-item label="是否加急:" v-if="!systemLevel">
<el-select
v-model="searchData.IsUrgent"
clearable
@@ -35,7 +30,7 @@
/>
</el-select>
</el-form-item>
<el-form-item label="是否启用:">
<el-form-item label="是否启用:" v-if="!systemLevel">
<el-select
v-model="searchData.IsEnable"
clearable
@@ -51,7 +46,7 @@
</el-form-item>
<el-form-item
:label="$t('trials:auditRecord:table:criterion')"
v-if="isDistinguishCriteria"
v-if="isDistinguishCriteria && !systemLevel"
>
<el-select
v-model="searchData.CriterionTypeEnum"
@@ -66,12 +61,76 @@
/>
</el-select>
</el-form-item>
<el-form-item :label="$t('trials:emailManageCfg:form:BusinessModule')">
<el-select
v-model="searchData.BusinessModuleEnum"
clearable
style="width: 100px"
>
<el-option
v-for="item of $d.BusinessModule"
:value="item.value"
:label="item.label"
:key="item.id"
/>
</el-select>
</el-form-item>
<el-form-item
:label="$t('trials:emailManageCfg:form:BusinessScenario')"
>
<el-select
v-model="searchData.BusinessScenarioEnum"
clearable
style="width: 100px"
>
<el-option
v-for="item of $d.Email_BusinessScenario"
:value="item.value"
:label="item.label"
:key="item.id"
/>
</el-select>
</el-form-item>
<el-form-item :label="$t('trials:emailManageCfg:form:ToUserType')">
<el-select
v-model="searchData.ToUserType"
clearable
style="width: 100px"
>
<el-option
v-for="item of UserTypeList"
:value="item.value"
:label="item.label"
:key="item.id"
/>
</el-select>
</el-form-item>
<el-form-item
:label="$t('trials:emailManageCfg:form:EmailUrgent')"
v-if="systemLevel === 1"
>
<el-select
v-model="searchData.EmailUrgentEnum"
clearable
style="width: 100px"
>
<el-option
v-for="item of $d.EmailUrgent"
:value="item.value"
:label="item.label"
:key="item.id"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button type="primary" @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
<span style="margin-left: auto">
<el-button type="primary" size="mini" @click="handleExport">
{{ $t('common:button:export') }}
</el-button>
<!-- <el-button
type="primary"
size="mini"
@@ -111,7 +170,7 @@
min-width="120"
>
<template slot-scope="scope">
{{ $fd("CriterionType", scope.row.CriterionTypeEnum) }}
{{ $fd('CriterionType', scope.row.CriterionTypeEnum) }}
</template>
</el-table-column>
<el-table-column
@@ -136,7 +195,7 @@
min-width="100"
>
<template slot-scope="scope">
{{ $fd("BusinessLevel", scope.row.BusinessLevelEnum) }}
{{ $fd('BusinessLevel', scope.row.BusinessLevelEnum) }}
</template>
</el-table-column>
<el-table-column
@@ -147,7 +206,7 @@
min-width="100"
>
<template slot-scope="scope">
{{ $fd("BusinessModule", scope.row.BusinessModuleEnum) }}
{{ $fd('BusinessModule', scope.row.BusinessModuleEnum) }}
</template>
</el-table-column>
<el-table-column
@@ -158,64 +217,62 @@
min-width="130"
>
<template slot-scope="scope">
{{ $fd("Email_BusinessScenario", scope.row.BusinessScenarioEnum) }}
{{ $fd('Email_BusinessScenario', scope.row.BusinessScenarioEnum) }}
</template>
</el-table-column>
<!-- 收件人 -->
<!-- 收件人v-if="systemLevel !== 1" -->
<el-table-column
prop="ToUserTypeList"
:label="$t('trials:emailManageCfg:title:toUserTypeList')"
show-overflow-tooltip
min-width="100"
v-if="systemLevel !== 1"
>
<template slot-scope="scope">
{{
scope.row.ToUserTypeList.length > 0
? scope.row.ToUserTypeList.map((v) => $fd("UserType", v)).join(
"、"
? scope.row.ToUserTypeList.map((v) => $fd('UserType', v)).join(
'、'
)
: ""
: ''
}}
</template>
</el-table-column>
<!-- 抄送人 -->
<!-- 抄送人v-if="!systemLevel" -->
<el-table-column
prop="CopyUserTypeList"
:label="$t('trials:emailManageCfg:title:copyUserTypeList')"
show-overflow-tooltip
min-width="100"
v-if="!systemLevel"
>
<template slot-scope="scope">
{{
scope.row.CopyUserTypeList.length > 0
? scope.row.CopyUserTypeList.map((v) => $fd("UserType", v)).join(
"、"
? scope.row.CopyUserTypeList.map((v) => $fd('UserType', v)).join(
'、'
)
: ""
: ''
}}
</template>
</el-table-column>
<!---v-if="!systemLevel"-->
<el-table-column
prop="EmailUrgentEnum"
label="加急状态"
sortable="custom"
show-overflow-tooltip
min-width="100"
v-if="!systemLevel"
>
<template slot-scope="scope">
{{ $fd("EmailUrgent", scope.row.EmailUrgentEnum) }}
{{ $fd('EmailUrgent', scope.row.EmailUrgentEnum) }}
</template>
</el-table-column>
<!---v-if="!systemLevel"-->
<el-table-column
prop="EmailCron"
label="发送周期和时间"
sortable="custom"
show-overflow-tooltip
min-width="160"
v-if="!systemLevel"
/>
<!-- 附件 -->
<el-table-column
@@ -246,10 +303,10 @@
>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsReturnRequired" type="danger">
{{ $fd("YesOrNo", scope.row.IsReturnRequired) }}
{{ $fd('YesOrNo', scope.row.IsReturnRequired) }}
</el-tag>
<el-tag v-else type="primary">{{
$fd("YesOrNo", scope.row.IsReturnRequired)
$fd('YesOrNo', scope.row.IsReturnRequired)
}}</el-tag>
</template>
</el-table-column>
@@ -264,10 +321,10 @@
>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsAutoSend" type="danger">
{{ $fd("YesOrNo", scope.row.IsAutoSend) }}
{{ $fd('YesOrNo', scope.row.IsAutoSend) }}
</el-tag>
<el-tag v-else type="primary">{{
$fd("YesOrNo", scope.row.IsAutoSend)
$fd('YesOrNo', scope.row.IsAutoSend)
}}</el-tag>
</template>
</el-table-column>
@@ -281,10 +338,10 @@
>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsEnable" type="danger">
{{ $fd("YesOrNo", scope.row.IsEnable) }}
{{ $fd('YesOrNo', scope.row.IsEnable) }}
</el-tag>
<el-tag v-else type="primary">{{
$fd("YesOrNo", scope.row.IsEnable)
$fd('YesOrNo', scope.row.IsEnable)
}}</el-tag>
</template>
</el-table-column>
@@ -338,8 +395,10 @@
<EmailForm
:data="rowData"
:system-level="systemLevel"
:isDistinguishCriteria="isDistinguishCriteria"
@closeDialog="closeDialog"
@getList="getList"
@PreviewHTML="preview"
/>
</el-dialog>
@@ -359,131 +418,173 @@
@getList="getList"
/>
</el-dialog>
<!-- 预览模板 -->
<el-dialog
v-if="previewVisible"
:visible.sync="previewVisible"
:close-on-click-modal="false"
title="预览"
fullscreen
custom-class="base-dialog-wrapper"
>
<div v-html="previewHTML"></div>
</el-dialog>
</box-content>
</template>
<script>
import {
getEmailNoticeConfigList,
deleteEmailNoticeConfig,
} from "@/api/dictionary";
import BoxContent from "@/components/BoxContent";
import Pagination from "@/components/Pagination";
import EmailForm from "./EmailForm";
import AttachmentList from "./AttachmentList";
} from '@/api/dictionary'
import { GetEmailNoticeConfigList_Export } from '@/api/export'
import BoxContent from '@/components/BoxContent'
import Pagination from '@/components/Pagination'
import EmailForm from './EmailForm'
import AttachmentList from './AttachmentList'
const searchDataDefault = () => {
return {
IsReturnRequired: "",
IsUrgent: "",
IsEnable: "",
IsReturnRequired: '',
IsUrgent: '',
IsEnable: '',
CriterionTypeEnum: null,
BusinessModuleEnum: null,
BusinessScenarioEnum: null,
EmailUrgentEnum: null,
ToUserType: null,
PageIndex: 1,
PageSize: 20,
};
};
SortField: 'Code',
Asc: true,
}
}
export default {
name: "EmailList",
name: 'EmailList',
components: { BoxContent, Pagination, EmailForm, AttachmentList },
props: {
isDistinguishCriteria: {
type: Boolean,
default() {
return false;
return false
},
},
systemLevel: {
type: Number,
default() {
return 0;
return 0
},
},
},
data() {
return {
activeTab: "0",
activeTab: '0',
searchData: searchDataDefault(),
loading: false,
list: [],
total: 0,
rowData: {},
title: "",
title: '',
editVisible: false,
attachmentVisible: false,
};
previewVisible: false,
previewHTML: null,
}
},
computed: {
UserTypeList() {
if (this.systemLevel === 1) {
return this.$d.UserType
} else {
return this.$d.UserType.filter(
(item) => ![7, 31, 32, 21, 26, 27].includes(item.value)
)
}
},
},
mounted() {
this.getList();
this.getList()
},
methods: {
//
getList() {
this.loading = true;
this.searchData.IsDistinguishCriteria = this.isDistinguishCriteria;
this.searchData.systemLevel = this.systemLevel;
this.loading = true
this.searchData.IsDistinguishCriteria = this.isDistinguishCriteria
this.searchData.systemLevel = this.systemLevel
getEmailNoticeConfigList(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.rowData = {};
this.title = "新增";
this.editVisible = true;
this.rowData = {}
this.title = '新增'
this.editVisible = true
},
//
handleEdit(row) {
this.rowData = { ...row };
this.title = "编辑";
this.editVisible = true;
this.rowData = { ...row }
this.title = '编辑'
this.editVisible = true
},
//
handleDelete(row) {
this.$confirm(this.$t("trials:staffResearch:message:confirmDel"), {
type: "warning",
this.$confirm(this.$t('trials:staffResearch:message:confirmDel'), {
type: 'warning',
distinguishCancelAndClose: true,
}).then(() => {
deleteEmailNoticeConfig(row.Id).then((res) => {
if (res.IsSuccess) {
this.getList();
this.$message.success("删除成功!");
this.getList()
this.$message.success('删除成功!')
}
});
});
})
})
},
handleDetail(row) {
this.rowData = { ...row };
this.attachmentVisible = true;
this.rowData = { ...row }
this.attachmentVisible = true
},
//
handleSearch() {
this.searchData.PageIndex = 1;
this.getList();
this.searchData.PageIndex = 1
this.getList()
},
//
handleReset() {
this.searchData = searchDataDefault();
this.getList();
this.searchData = searchDataDefault()
this.getList()
},
//
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()
},
//
closeDialog() {
this.editVisible = false;
this.editVisible = false
},
preview(html) {
this.previewHTML = html
this.previewVisible = true
},
//
handleExport() {
// this.searchData.IsDistinguishCriteria = this.isDistinguishCriteria
// this.searchData.systemLevel = this.systemLevel
// return GetEmailNoticeConfigList_Export(this.searchData)
return GetEmailNoticeConfigList_Export(this.searchData)
},
},
};
}
</script>
+48 -48
View File
@@ -29,13 +29,13 @@
></EmailList>
</el-tab-pane>
<!-- 系统相关-分角色 -->
<el-tab-pane :label="$t('trials:emailManageCfg:title:systemDisRole')" name="3">
<!-- <el-tab-pane :label="$t('trials:emailManageCfg:title:systemDisRole')" name="3">
<EmailList
v-if="activeTab === '3'"
:is-distinguish-criteria="false"
:system-level="2"
></EmailList>
</el-tab-pane>
</el-tab-pane> -->
</el-tabs>
</box-content>
</template>
@@ -43,107 +43,107 @@
import {
getEmailNoticeConfigList,
deleteEmailNoticeConfig,
} from "@/api/dictionary";
import BoxContent from "@/components/BoxContent";
import Pagination from "@/components/Pagination";
import EmailList from "./components/EmailList";
} from '@/api/dictionary'
import BoxContent from '@/components/BoxContent'
import Pagination from '@/components/Pagination'
import EmailList from './components/EmailList'
const searchDataDefault = () => {
return {
IsReturnRequired: "",
IsUrgent: "",
IsEnable: "",
IsReturnRequired: '',
IsUrgent: '',
IsEnable: '',
PageIndex: 1,
PageSize: 20,
};
};
}
}
export default {
name: "EmailListIndex",
name: 'EmailListIndex',
components: { BoxContent, Pagination, EmailList },
data() {
return {
activeTab: "0",
activeTab: '0',
searchData: searchDataDefault(),
loading: false,
list: [],
total: 0,
rowData: {},
title: "",
title: '',
editVisible: false,
attachmentVisible: false,
};
}
},
mounted() {
this.getList();
this.getList()
},
methods: {
//
getList() {
this.loading = true;
this.loading = true
getEmailNoticeConfigList(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.rowData = {};
this.title = "新增";
this.editVisible = true;
this.rowData = {}
this.title = '新增'
this.editVisible = true
},
//
handleEdit(row) {
this.rowData = { ...row };
this.title = "编辑";
this.editVisible = true;
this.rowData = { ...row }
this.title = '编辑'
this.editVisible = true
},
//
handleDelete(row) {
this.$confirm(this.$t("trials:staffResearch:message:confirmDel"), {
type: "warning",
this.$confirm(this.$t('trials:staffResearch:message:confirmDel'), {
type: 'warning',
distinguishCancelAndClose: true,
}).then(() => {
deleteEmailNoticeConfig(row.Id).then((res) => {
if (res.IsSuccess) {
this.getList();
this.$message.success("删除成功!");
this.getList()
this.$message.success('删除成功!')
}
});
});
})
})
},
handleDetail(row) {
this.rowData = { ...row };
this.attachmentVisible = true;
this.rowData = { ...row }
this.attachmentVisible = true
},
//
handleSearch() {
this.searchData.PageIndex = 1;
this.getList();
this.searchData.PageIndex = 1
this.getList()
},
//
handleReset() {
this.searchData = searchDataDefault();
this.getList();
this.searchData = searchDataDefault()
this.getList()
},
//
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()
},
//
closeDialog() {
this.editVisible = false;
this.editVisible = false
},
},
};
}
</script>
+141 -49
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>
+71 -71
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 {
+117 -94
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 {
+202 -96
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"
@@ -83,7 +83,7 @@
<svg-icon icon-class="password" />
</span>
<!-- password -->
<el-input
<!-- <el-input
:key="passwordType"
ref="password"
v-model="loginForm.password"
@@ -91,6 +91,8 @@
:type="passwordType"
:placeholder="$t('login:form:password')"
name="password"
auto-complete="new-password"
autocomplete="new-password"
tabindex="2"
@keyup.enter.native="handleLogin"
/>
@@ -98,6 +100,21 @@
<svg-icon
:icon-class="passwordType === 'password' ? 'eye' : 'eye-open'"
/>
</span> -->
<el-input
:key="passwordType"
ref="password"
v-model="pwdCover"
size="small"
type="text"
:placeholder="$t('login:form:password')"
name="password"
@input="setPassword"
tabindex="2"
@keyup.enter.native="handleLogin"
/>
<span class="show-pwd" @click="hidePassword">
<svg-icon :icon-class="!isShowPassword ? 'eye' : 'eye-open'" />
</span>
</el-form-item>
<!-- Login -->
@@ -112,7 +129,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 +145,7 @@
@click.native.prevent="handleResetPwd"
style="color: rgb(0, 147, 221)"
>
{{ $t("login:button:forgetPassword") }}
{{ $t('login:button:forgetPassword') }}
</el-button>
</div>
</el-form>
@@ -155,6 +172,14 @@
<span style="color: #428bca">关于</span>
</a>
</div>
<div v-if="NODE_ENV === 'usa'" class="login-footer">
<span>
© {{ new Date().getFullYear() }} Elevate lmaging Inc. info@{{ host }}
</span>
<a @click="openAbout">
<span style="color: #428bca">About</span>
</a>
</div>
<Vcode
:show="isShow"
:fail-text="$t('login:button:failText')"
@@ -179,28 +204,42 @@
text-align: center;
"
>
<h1 style="text-align: center; margin-bottom: 20px">关于</h1>
<h1
style="text-align: center; margin-bottom: 20px"
v-if="NODE_ENV === 'usa'"
>
About
</h1>
<h1 style="text-align: center; margin-bottom: 20px" v-else>关于</h1>
<p style="margin-bottom: 20px" v-if="NODE_ENV === 'usa'">
{{ $t("login:title:system_title") }}
{{ $t('login:title:system_title_about') }}
</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() }} 上海展影医疗科技有限公司
版权所有
</p>
<p style="margin-bottom: 20px" v-else-if="NODE_ENV === 'usa'">
© {{ new Date().getFullYear() }} Elevate Imaging Inc.
</p>
<p style="margin-bottom: 20px" v-else>
Copyright © {{ new Date().getFullYear() }} Shanghai Extensive Imaging
Inc.
</p>
<div style="margin-bottom: 20px">
<div style="margin-bottom: 20px" v-if="NODE_ENV === 'usa'">
<img style="width: 180px" src="@/assets/zzlogo-usa.png" alt="" />
</div>
<div style="margin-bottom: 20px" v-else>
<img style="width: 180px" src="@/assets/zzlogo2.png" alt="" />
</div>
</div>
<div slot="footer" class="dialog-footer">
<el-button type="primary" size="mini" @click="aboutVisible = false"
>关闭</el-button
>
<el-button type="primary" size="mini" @click="aboutVisible = false">
{{ $t('common:button:close') }}
</el-button>
</div>
</el-dialog>
<browserTip ref="browserTip" />
@@ -208,14 +247,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 +262,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 +283,34 @@ 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',
pwdCover: null,
isShowPassword: false,
loginType: null,
location: null,
isShow: false,
showCode: false,
Img1,
};
}
},
computed: {
...mapGetters(["asyncRoutes", "routes", "language"]),
...mapGetters(['asyncRoutes', 'routes', 'language']),
host() {
return window.location.host
},
},
watch: {
"$i18n.locale": {
'$i18n.locale': {
handler() {
if (this.$refs.browserTip) {
this.$refs.browserTip.open();
this.$refs.browserTip.open()
}
},
deep: true,
@@ -274,88 +318,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 +407,117 @@ 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' })
},
//
setPassword(val) {
console.log(val)
if (this.isShowPassword) {
this.loginForm.password = val
} else {
let reg =
/^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[-_.@^+\$!%#*?&\$~])[A-Za-z0-9-~_.@^+\$~!%#*?&]{8,32}$/g //
let nDot = /[^●]/g //
let index = -1 //
let lastChar = void 0 //
let realArr = this.loginForm.password.split('') //
let coverArr = val.split('') //
let coverLen = val.length //
let realLen = this.loginForm.password.length //
//
coverArr.forEach((el, idx) => {
if (nDot.test(el)) {
index = idx
lastChar = el
}
})
//
// if (lastChar && !reg.test(lastChar)) {
// coverArr.splice(index, 1)
// this.pwdCover = coverArr.join('')
// console.log(111111111111111, lastChar)
// // return
// }
if (coverLen - realLen <= 2) {
if (realLen < coverLen) {
//
realArr.splice(index, 0, lastChar)
} else if (coverLen <= realLen && index !== -1) {
//
realArr.splice(index, realLen - (coverLen - 1), lastChar)
} else {
// val password val
let pos = document.getElementById('pwd').selectionEnd //
realArr.splice(pos, realLen - coverLen)
}
}
// pwdCover
this.pwdCover = val.replace(/\S/g, '●')
let v = val.replace(/●/g, '')
this.loginForm.password =
coverLen - realLen >= 2 ? realArr.join('') + v : realArr.join('')
}
},
//
hidePassword() {
if (!this.isShowPassword) {
// console.log("");
this.isShowPassword = true
this.pwdCover = this.loginForm.password
} else {
// console.log("");
this.isShowPassword = false
this.pwdCover = this.pwdCover.replace(/\S/g, '●')
}
},
},
};
}
</script>
<style lang="scss">
+97 -59
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>
+123 -64
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>
+1 -1
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"
+1 -1
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"
+127 -61
View File
@@ -1,10 +1,10 @@
<template>
<div class="question-login-wrapper">
<div class="box-wrapper">
<h2 style="text-align:center;">
<h2 style="text-align: center">
<!-- 中心调研表 -->
<!-- 独立阅片人信息填写 -->
{{$t("trials:researchForm:form:title")}}
{{ $t('trials:researchForm:form:title') }}
</h2>
<el-card shadow="hover" style="padding-top: 40px">
<el-form
@@ -12,30 +12,44 @@
v-loading="loading"
:model="form"
label-width="150px"
style="width:80%;margin:0 auto;"
style="width: 80%; margin: 0 auto"
:rules="rules"
class="demo-ruleForm"
size="small"
>
<!-- 联系邮箱 -->
<el-form-item :label="$t('trials:researchForm:form:contactorEmail')" prop="EmailOrPhone">
<el-input v-model="form.EmailOrPhone" autocomplete="new-password" @change="handleEmailChange" />
<el-form-item
:label="$t('trials:researchForm:form:contactorEmail')"
prop="EmailOrPhone"
>
<el-input
v-model="form.EmailOrPhone"
autocomplete="new-password"
@change="handleEmailChange"
/>
</el-form-item>
<!-- 验证码 -->
<el-form-item :label="$t('trials:researchForm:form:verifyCode')" required>
<el-form-item
:label="$t('trials:researchForm:form:verifyCode')"
required
>
<el-col :span="20">
<el-form-item prop="VerificationCode">
<el-input v-model="form.VerificationCode" autocomplete="new-password" />
<el-input
v-model="form.VerificationCode"
autocomplete="new-password"
/>
</el-form-item>
</el-col>
<el-col :span="4">
<el-button
size="small"
type="primary"
style="width:100%;"
style="width: 100%"
:disabled="sendDisabled"
@click="handleSendCode"
>{{ sendTitle }}</el-button>
>{{ sendTitle }}</el-button
>
</el-col>
</el-form-item>
<el-form-item>
@@ -44,10 +58,14 @@
{{ $t('common:button:cancel') }}
</el-button>
<!-- 提交 -->
<el-button size="small" type="primary" :loading="btnLoading" @click="onSubmit">
<el-button
size="small"
type="primary"
:loading="btnLoading"
@click="onSubmit"
>
{{ $t('common:button:submit') }}
</el-button>
</el-form-item>
</el-form>
</el-card>
@@ -55,17 +73,23 @@
</div>
</template>
<script>
import { sendVerifyCode, verifySendCode, getTrialSurveyInitInfo } from '@/api/research'
import {
sendVerifyCode,
verifySendCode,
getTrialSurveyInitInfo,
} from '@/api/research'
import { verifyEmialGetDoctorInfo, sendEmialVerifyCode } from '@/api/reviewers'
import { login, getUserMenuTree, getUserPermissions } from '@/api/user'
import store from '@/store'
import { mapMutations } from 'vuex'
import { mapMutations } from 'vuex'
export default {
data() {
var checkPhone = (rule, value, callback) => {
const phoneReg = /^1[3|4|5|7|8][0-9]{9}$/
if (!value) {
return callback(new Error(this.$t('trials:researchForm:formRule:specify')))
return callback(
new Error(this.$t('trials:researchForm:formRule:specify'))
)
}
setTimeout(() => {
if (!Number.isInteger(+value)) {
@@ -83,7 +107,8 @@ export default {
if (value === '') {
callback(new Error(this.$t('trials:researchForm:formRule:specify')))
} else {
var reg = /^[A-Za-z0-9]+([_\.][A-Za-z0-9]+)*@([A-Za-z0-9\-]+\.)+[A-Za-z]{2,6}$/
var reg =
/^[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()
@@ -97,8 +122,12 @@ export default {
if (value === '') {
callback(new Error(this.$t('trials:researchForm:formRule:specify')))
} else {
var reg = /^[A-Za-z0-9]+([_\.][A-Za-z0-9]+)*@([A-Za-z0-9\-]+\.)+[A-Za-z]{2,6}$/
if (this.form.ReplaceUserEmailOrPhone && reg.test(this.form.ReplaceUserEmailOrPhone)) {
var reg =
/^[A-Za-z0-9]+([_\.][A-Za-z0-9]+)*@([A-Za-z0-9\-]+\.)+[A-Za-z]{2,6}$/
if (
this.form.ReplaceUserEmailOrPhone &&
reg.test(this.form.ReplaceUserEmailOrPhone)
) {
callback()
} else {
callback(new Error(this.$t('trials:researchForm:formRule:email')))
@@ -120,26 +149,51 @@ export default {
TrialSiteCode: '',
IsUpdate: false,
ReplaceUserEmailOrPhone: '',
VerificationCode: ''
VerificationCode: '',
},
rules: {
SiteId: [
{ required: true, message: this.$t('trials:researchForm:formRule:specify'), trigger: ['blur'] }
{
required: true,
message: this.$t('trials:researchForm:formRule:specify'),
trigger: ['blur'],
},
],
UserName: [
{ required: true, message: this.$t('trials:researchForm:formRule:specify'), trigger: ['blur'] },
{ min: 0, max: 50, message: this.$t('trials:researchForm:formRule:maxLength'), trigger: 'blur' }
],
Phone: [
{ required: true, validator: checkPhone, trigger: ['blur'] }
{
required: true,
message: this.$t('trials:researchForm:formRule:specify'),
trigger: ['blur'],
},
{
min: 0,
max: 50,
message: this.$t('trials:researchForm:formRule:maxLength'),
trigger: 'blur',
},
],
Phone: [{ required: true, validator: checkPhone, trigger: ['blur'] }],
EmailOrPhone: [
{ required: true, validator: validateEmail, trigger: ['blur', 'change'] }
{
required: true,
validator: validateEmail,
trigger: ['blur', 'change'],
},
],
ReplaceUserEmailOrPhone: [
{ required: true, validator: validateReplaceEmail, trigger: ['blur'] }
{
required: true,
validator: validateReplaceEmail,
trigger: ['blur'],
},
],
VerificationCode: [
{
required: true,
message: this.$t('trials:researchForm:formRule:specify'),
trigger: ['blur'],
},
],
VerificationCode: [{ required: true, message: this.$t('trials:researchForm:formRule:specify'), trigger: ['blur'] }]
},
siteOptions: [],
loading: false,
@@ -148,20 +202,19 @@ export default {
sendTitle: this.$t('trials:researchForm:button:send'),
count: '',
timer: null,
isHaveSiteSurveyRecord: false
isHaveSiteSurveyRecord: false,
}
},
created(){
created() {
this.$i18n.locale = this.$route.query.lang
this.setLanguage(this.$route.query.lang)
},
mounted() {
},
mounted() {},
methods: {
...mapMutations({ setLanguage: 'lang/setLanguage' }),
//
onSubmit() {
this.$refs['resetForm'].validate(valid => {
this.$refs['resetForm'].validate((valid) => {
if (valid) {
//
this.btnLoading = true
@@ -173,21 +226,29 @@ export default {
emailOrPhone: this.form.EmailOrPhone,
verificationCode: this.form.VerificationCode,
}
verifyEmialGetDoctorInfo(param).then(async res => {
this.btnLoading = false
zzSessionStorage.clear()
store.dispatch('user/setToken', res.Result.Token)
zzSessionStorage.setItem('TokenKey', res.Result.Token)
var permissions = await getUserPermissions()
var menuTree = await getUserMenuTree()
store.dispatch('user/setTree', menuTree.Result)
store.dispatch('user/setPermissions', permissions.Result)
// this.$router.push({ path: `/researchForm?trialId=${this.trialId}&trialSiteSurveyId=${res.Result.TrialSiteSurveyId}` })
this.$router.push({path: `/ReviewersResearchForm?Id=${res.Result.DoctorId ? res.Result.DoctorId : ''}&tabActive=BasicInfo&ReviewStatus=${res.Result.ReviewStatus}&lang=${this.$route.query.lang}`})
}).catch(() => {
this.btnLoading = false
this.sendDisabled = false
})
verifyEmialGetDoctorInfo(param)
.then(async (res) => {
this.btnLoading = false
zzSessionStorage.clear()
store.dispatch('user/setToken', res.Result.Token)
zzSessionStorage.setItem('TokenKey', res.Result.Token)
var permissions = await getUserPermissions()
var menuTree = await getUserMenuTree()
store.dispatch('user/setTree', menuTree.Result)
store.dispatch('user/setPermissions', permissions.Result)
// this.$router.push({ path: `/researchForm?trialId=${this.trialId}&trialSiteSurveyId=${res.Result.TrialSiteSurveyId}` })
this.$router.push({
path: `/curriculumVitae?Id=${
res.Result.DoctorId ? res.Result.DoctorId : ''
}&tabActive=BasicInfo&ReviewStatus=${
res.Result.ReviewStatus
}&lang=${this.$route.query.lang}`,
})
})
.catch(() => {
this.btnLoading = false
this.sendDisabled = false
})
} else {
return false
}
@@ -197,21 +258,24 @@ export default {
handleSendCode() {
this.sendDisabled = true
const param = {
Email: this.form.EmailOrPhone
Email: this.form.EmailOrPhone,
}
sendEmialVerifyCode(param).then(res => {
if (res.IsSuccess) {
this.getCode()
} else {
this.$alert(res.ErrorMessage)
}
}).catch(() => {
this.sendDisabled = false
})
sendEmialVerifyCode(param)
.then((res) => {
if (res.IsSuccess) {
this.getCode()
} else {
this.$alert(res.ErrorMessage)
}
})
.catch(() => {
this.sendDisabled = false
})
},
// change
handleEmailChange() {
var reg = /^[A-Za-z0-9]+([_\.][A-Za-z0-9]+)*@([A-Za-z0-9\-]+\.)+[A-Za-z]{2,6}$/
var reg =
/^[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
} else {
@@ -226,7 +290,9 @@ export default {
this.timer = setInterval(() => {
if (this.count > 0 && this.count <= TIME_COUNT) {
this.count--
this.sendTitle = `${this.$t('trials:researchForm:button:send')}(${this.count}s)`
this.sendTitle = `${this.$t('trials:researchForm:button:send')}(${
this.count
}s)`
this.sendDisabled = true
} else {
this.sendDisabled = false
@@ -239,8 +305,8 @@ export default {
},
onCancel() {
this.$refs['resetForm'].resetFields()
}
}
},
},
}
</script>
<style lang="scss" scoped>
+315 -72
View File
@@ -1,45 +1,92 @@
<template>
<div v-loading="loading" class="resumes-container">
<div class="resume-content" v-if="!$route.query.ReviewStatus">
<p>{{$t('system:Agreements:title:Consultant Agreement')}}</p>
<p>{{ $t('system:Agreements:title:Consultant Agreement') }}</p>
<div class="upload-content">
<upload-files ref="uploadAgreement" :disabled="$route.query.ReviewStatus === '1'" :doctor-id="doctorId" type="Consultant Agreement" accept=".pdf" @getFileList="getFileList" />
<upload-files
ref="uploadAgreement"
:disabled="$route.query.ReviewStatus === '1'"
:doctor-id="doctorId"
type="Consultant Agreement"
accept=".pdf"
@getFileList="getFileList"
/>
</div>
<p>{{ $t('system:tip:file:pdf') }}</p>
</div>
<div v-if="!$route.query.ReviewStatus">
<el-table :data="agreementList" size="small">
<el-table-column type="index" width="40" />
<el-table-column prop="FileName" :label="$t('system:Agreements:table:Consultant Agreement')" width="300" />
<el-table-column prop="CreateTime" :label="$t('system:Agreements:table:Upload Time')" width="150" />
<el-table-column
prop="FileName"
:label="$t('system:Agreements:table:Consultant Agreement')"
width="300"
/>
<el-table-column
prop="CreateTime"
:label="$t('system:Agreements:table:Upload Time')"
width="150"
/>
<el-table-column :label="$t('common:action:action')" width="400">
<template slot-scope="scope">
<el-button type="text" size="small" @click="handlePreview(scope.row)">{{$t('trials:enrolledReviews:button:view')}}</el-button>
<el-button type="text" size="small" :disabled="$route.query.ReviewStatus === '1'" @click="handleRemoveFile(scope.row)">{{$t('common:button:delete')}}</el-button>
<el-button
type="text"
size="small"
@click="handlePreview(scope.row)"
>{{ $t('trials:enrolledReviews:button:view') }}</el-button
>
<el-button
type="text"
size="small"
:disabled="$route.query.ReviewStatus === '1'"
@click="handleRemoveFile(scope.row)"
>{{ $t('common:button:delete') }}</el-button
>
</template>
</el-table-column>
</el-table>
</div>
<div style="margin-top:20px">
<div style="margin-top: 20px">
<span style="margin-right: 20px">
{{$t('trials:enrolledReviews:message:SOW')}}
{{ $t('trials:enrolledReviews:message:SOW') }}
</span>
<el-button size="small" type="primary" :disabled="$route.query.ReviewStatus === '1'" @click="addCol(0, $t('trials:enrolledReviews:message:SOW'))">
{{ $t('common:button:upload') }}</el-button>
<el-button size="small" type="primary" @click="handleDownload('Reviewer_SOW_Template')">
<el-button
size="small"
type="primary"
:disabled="$route.query.ReviewStatus === '1'"
@click="addCol(0, $t('trials:enrolledReviews:message:SOW'))"
>
{{ $t('common:button:upload') }}</el-button
>
<el-button
size="small"
type="primary"
@click="handleDownload('Reviewer_SOW_Template')"
>
<i class="el-icon-view el-icon-download"></i>
{{ $t('common:button:template') }}
</el-button>
</div>
<el-table :data="sowList" size="small">
<el-table-column type="index" width="40" />
<el-table-column :label="$t('system:Agreements:table:Criterion Type')" width="120">
<el-table-column
:label="$t('system:Agreements:table:Criterion Type')"
width="120"
>
<template slot-scope="scope">
{{ scope.row.CriterionName }}
</template>
</el-table-column>
<el-table-column prop="FileName" :label="$t('system:Agreements:table:Statement of Work')" width="400" />
<el-table-column prop="IsEnable" :label="$t('system:Agreements:table:Is Enable')" width="100">
<el-table-column
prop="FileName"
:label="$t('system:Agreements:table:Statement of Work')"
width="400"
/>
<el-table-column
prop="IsEnable"
:label="$t('system:Agreements:table:Is Enable')"
width="100"
>
<template slot-scope="scope">
<el-switch
v-model="scope.row.IsEnable"
@@ -49,32 +96,81 @@
/>
</template>
</el-table-column>
<el-table-column prop="Remark" :label="$t('system:Agreements:Remark')" width="200" />
<el-table-column prop="CreateTime" :label="$t('system:Agreements:Upload Time')" width="150" />
<el-table-column
prop="Remark"
:label="$t('system:Agreements:Remark')"
width="200"
/>
<el-table-column
prop="CreateTime"
:label="$t('system:Agreements:Upload Time')"
width="150"
/>
<el-table-column :label="$t('common:action:action')" width="400">
<template slot-scope="scope">
<el-button type="text" size="small" :disabled="$route.query.ReviewStatus === '1'" @click="handlePreview3(scope.row)">{{$t('trials:enrolledReviews:button:view')}}</el-button>
<el-button type="text" size="small" :disabled="$route.query.ReviewStatus === '1'" @click="handleEditCol(scope.row, 0, $t('trials:enrolledReviews:message:SOW'))">
{{ $t('common:button:edit') }}</el-button>
<el-button type="text" size="small" :disabled="$route.query.ReviewStatus === '1'" @click="handleRemoveFile3(scope.row)">{{ $t('common:button:delete') }}</el-button>
<el-button
type="text"
size="small"
:disabled="$route.query.ReviewStatus === '1'"
@click="handlePreview3(scope.row)"
>{{ $t('trials:enrolledReviews:button:view') }}</el-button
>
<el-button
type="text"
size="small"
:disabled="$route.query.ReviewStatus === '1'"
@click="
handleEditCol(
scope.row,
0,
$t('trials:enrolledReviews:message:SOW')
)
"
>
{{ $t('common:button:edit') }}</el-button
>
<el-button
type="text"
size="small"
:disabled="$route.query.ReviewStatus === '1'"
@click="handleRemoveFile3(scope.row)"
>{{ $t('common:button:delete') }}</el-button
>
</template>
</el-table-column>
</el-table>
<div style="margin-top:20px" v-if="!$route.query.ReviewStatus">
<div style="margin-top: 20px" v-if="!$route.query.ReviewStatus">
<span style="margin-right: 20px">
{{$t('trials:enrolledReviews:message:EQC')}}
{{ $t('trials:enrolledReviews:message:EQC') }}
</span>
<el-button size="small" type="primary" :disabled="$route.query.ReviewStatus === '1'" @click="addCol(1, $t('trials:enrolledReviews:message:EQC'))">{{$t('common:button:upload')}}</el-button>
<el-button
size="small"
type="primary"
:disabled="$route.query.ReviewStatus === '1'"
@click="addCol(1, $t('trials:enrolledReviews:message:EQC'))"
>{{ $t('common:button:upload') }}</el-button
>
</div>
<el-table :data="ackSowList" size="small" v-if="!$route.query.ReviewStatus">
<el-table-column type="index" width="40" />
<el-table-column :label="$t('system:Agreements:table:Criterion Type')" width="120">
<el-table-column
:label="$t('system:Agreements:table:Criterion Type')"
width="120"
>
<template slot-scope="scope">
{{ scope.row.CriterionName }}
</template>
</el-table-column>
<el-table-column prop="FileName" :label="$t('system:Agreements:table:Acknowledgement of SOW')" width="400" />
<el-table-column prop="IsEnable" :label="$t('system:Agreements:table:Is Enable')" width="100">
<el-table-column
prop="FileName"
:label="$t('system:Agreements:table:Acknowledgement of SOW')"
width="400"
/>
<el-table-column
prop="IsEnable"
:label="$t('system:Agreements:table:Is Enable')"
width="100"
>
<template slot-scope="scope">
<el-switch
v-model="scope.row.IsEnable"
@@ -84,13 +180,45 @@
/>
</template>
</el-table-column>
<el-table-column prop="Remark" :label="$t('system:Agreements:table:Remark')" width="200" />
<el-table-column prop="CreateTime" :label="$t('system:Agreements:table:Upload Time')" width="150" />
<el-table-column label="Action" width="400">
<el-table-column
prop="Remark"
:label="$t('system:Agreements:table:Remark')"
width="200"
/>
<el-table-column
prop="CreateTime"
:label="$t('system:Agreements:table:Upload Time')"
width="150"
/>
<el-table-column :label="$t('common:action:action')" width="400">
<template slot-scope="scope">
<el-button type="text" size="small" :disabled="$route.query.ReviewStatus === '1'" @click="handlePreview3(scope.row)">{{$t('common:button:view')}}</el-button>
<el-button type="text" size="small" :disabled="$route.query.ReviewStatus === '1'" @click="handleEditCol(scope.row, 1, $t('trials:enrolledReviews:message:EQC'))">{{$t('common:button:edit')}}</el-button>
<el-button type="text" size="small" :disabled="$route.query.ReviewStatus === '1'" @click="handleRemoveFile3(scope.row)">{{$t('common:button:delete')}}</el-button>
<el-button
type="text"
size="small"
:disabled="$route.query.ReviewStatus === '1'"
@click="handlePreview3(scope.row)"
>{{ $t('common:button:view') }}</el-button
>
<el-button
type="text"
size="small"
:disabled="$route.query.ReviewStatus === '1'"
@click="
handleEditCol(
scope.row,
1,
$t('trials:enrolledReviews:message:EQC')
)
"
>{{ $t('common:button:edit') }}</el-button
>
<el-button
type="text"
size="small"
:disabled="$route.query.ReviewStatus === '1'"
@click="handleRemoveFile3(scope.row)"
>{{ $t('common:button:delete') }}</el-button
>
</template>
</el-table-column>
</el-table>
@@ -103,7 +231,10 @@
label-width="120px"
size="small"
>
<el-form-item :label="$t('system:Agreements:label:Criterion Type')" prop="CriterionType">
<el-form-item
:label="$t('system:Agreements:label:Criterion Type')"
prop="CriterionType"
>
<el-select v-model="form.CriterionType">
<el-option
v-for="item of $d.CriterionType"
@@ -113,7 +244,10 @@
/>
</el-select>
</el-form-item>
<el-form-item :label="$t('system:Agreements:label:File')" prop="FilePath">
<el-form-item
:label="$t('system:Agreements:label:File')"
prop="FilePath"
>
<el-upload
class="upload-demo"
action
@@ -123,14 +257,24 @@
:on-remove="handleRemoveFile2"
:show-file-list="true"
:limit="1"
accept=".pdf"
: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
>
<span>{{ $t('system:tip:file:pdf') }}</span>
</el-upload>
</el-form-item>
<el-form-item :label="$t('system:Agreements:label:Remark')">
<el-input v-model="form.Remark" type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"></el-input>
<el-input
v-model="form.Remark"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4 }"
></el-input>
</el-form-item>
<el-form-item :label="$t('system:Agreements:label:Is Enable')">
<el-switch
@@ -142,8 +286,20 @@
</el-form>
</template>
<template slot="dialog-footer">
<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">{{ $t('common:button:save') }}</el-button>
<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"
>{{ $t('common:button:save') }}</el-button
>
</template>
</BaseModel>
</div>
@@ -151,25 +307,43 @@
<script>
import { uploadFile } from '@/api/attachment'
import UploadFiles from '@/components/UploadFiles'
import { getDoctorSowList, getDoctorAckSowList, addDoctorCriterionFile, deleteDoctorCriterionFile, getDoctorCriterionFile } from '@/api/reviewers'
import {
getDoctorSowList,
getDoctorAckSowList,
addDoctorCriterionFile,
deleteDoctorCriterionFile,
getDoctorCriterionFile,
} from '@/api/reviewers'
import BaseModel from '@/components/BaseModel'
import { DownloadCommonDoc } from '@/api/dictionary'
export default {
name: 'Agreements',
components: {
UploadFiles, BaseModel
UploadFiles,
BaseModel,
},
props: {
reviewerId: {
type: String,
},
},
data() {
return {
doctorId: this.$route.query.Id,
doctorId: this.$route.query.Id || this.reviewerId,
agreementList: [],
sowList: [],
ackSowList: [],
loading: false,
isOpen: false,
btnLoading: false,
model_cfg: { visible: false, showClose: true, width: '600px', title: '', appendToBody: true },
model_cfg: {
visible: false,
showClose: true,
width: '600px',
title: '',
appendToBody: true,
},
form: {
Remark: null,
FileType: null,
@@ -177,24 +351,49 @@ export default {
FilePath: null,
DoctorId: null,
CriterionType: null,
IsEnable: false
IsEnable: false,
},
rules: {
CriterionType: [{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: ['blur'] }],
FilePath: [{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: ['blur'] }],
CriterionType: [
{
required: true,
message: this.$t('common:ruleMessage:specify'),
trigger: ['blur'],
},
],
FilePath: [
{
required: true,
message: this.$t('common:ruleMessage:specify'),
trigger: ['blur'],
},
],
},
fileList: []
fileList: [],
accept: '.pdf',
}
},
watch: {
reviewerId() {
if (this.reviewerId) {
this.doctorId = this.reviewerId
this.initSowList()
}
},
},
mounted() {
this.initSowList()
},
methods: {
handleDownload(code) {
this.loading = true
DownloadCommonDoc(code).then(data => {
this.loading = false
}).catch(() => { this.loading = false })
DownloadCommonDoc(code)
.then((data) => {
this.loading = false
})
.catch(() => {
this.loading = false
})
},
handleEditCol(row, fileType, title) {
this.model_cfg.visible = true
@@ -206,7 +405,7 @@ export default {
handleRemoveFile3(row) {
this.$confirm('确定删除?').then(() => {
deleteDoctorCriterionFile({
Id: row.Id
Id: row.Id,
}).then(() => {
this.initSowList()
this.$message.success('删除成功')
@@ -218,41 +417,79 @@ export default {
this.form.FilePath = null
this.fileList = []
},
beforeUpload() {
beforeUpload(row) {
if (this.fileList.length > 0) {
this.$alert('最多只能传一个附件')
return
}
if (!this.checkFileSuffix(row.name)) {
this.$message.warning(`Must be in .pdf format`)
return false
}
},
handlePreview3(row) {
return this.$preview({
path: row.FilePath,
type: 'pdf',
title: row.FileName,
})
if (row.FilePath) {
window.open(this.OSSclientConfig.basePath + row.FilePath, '_blank')
}
},
handlePreview2(row, r2) {
return this.$preview({
path: row.fullPath,
type: 'pdf',
title: row.FileName,
})
if (row.fullPath) {
window.open(this.OSSclientConfig.basePath + row.fullPath, '_blank')
}
},
checkFileSuffix(fileName) {
var index = fileName.lastIndexOf('.')
var suffix = fileName.substring(index + 1, fileName.length)
return (
this.accept.toLocaleLowerCase().search(suffix.toLocaleLowerCase()) === 1
)
},
async handleUploadFile(param) {
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.doctorId}/${fileName}`, file)
this.form.FileName = param.file.name
this.form.FilePath = this.$getObjectName(res.url)
this.fileList[0] = { name: this.$getObjectName(res.url), path: this.$getObjectName(res.url), fullPath: this.$getObjectName(res.url), url: res.url }
this.btnLoading = false
try {
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.doctorId}/${fileName}`,
file
)
this.form.FileName = param.file.name
this.form.FilePath = this.$getObjectName(res.url)
this.fileList[0] = {
name: this.$getObjectName(res.url),
path: this.$getObjectName(res.url),
fullPath: this.$getObjectName(res.url),
url: res.url,
}
this.btnLoading = false
} catch (err) {
console.log(err)
}
},
handleCancle() {
this.model_cfg.visible = false
},
handleSave() {
this.$refs['DictionaryTypeConfigForm'].validate(valid => {
this.$refs['DictionaryTypeConfigForm'].validate((valid) => {
if (!valid) return
this.form.DoctorId = this.doctorId
this.form.CriterionName = this.$fd('CriterionType', this.form.CriterionType)
addDoctorCriterionFile(this.form).then(res => {
this.form.CriterionName = this.$fd(
'CriterionType',
this.form.CriterionType
)
addDoctorCriterionFile(this.form).then((res) => {
this.$message.success('添加成功')
this.initSowList()
this.handleCancle()
@@ -267,7 +504,8 @@ export default {
FileName: null,
FilePath: null,
DoctorId: null,
CriterionType: null
CriterionType: null,
IsEnable: false,
}
this.handleRemoveFile2()
this.form.FileType = fileType
@@ -277,18 +515,24 @@ export default {
if (!this.doctorId) return
getDoctorCriterionFile({
fileType: 0,
DoctorId: this.doctorId
}).then(res => {
DoctorId: this.doctorId,
}).then((res) => {
this.sowList = res.Result
})
getDoctorCriterionFile({
fileType: 1,
DoctorId: this.doctorId
}).then(res => {
DoctorId: this.doctorId,
}).then((res) => {
this.ackSowList = res.Result
})
},
handlePreview(row) {
console.log(row)
return this.$preview({
path: row.FullPath,
type: 'pdf',
title: row.FileName,
})
if (row.FullPath) {
window.open(this.OSSclientConfig.basePath + row.FullPath, '_blank')
}
@@ -298,12 +542,12 @@ export default {
},
getFileList(fileList) {
this.agreementList = fileList
}
}
},
},
}
</script>
<style lang="scss" scoped>
.resumes-container{
.resumes-container {
p {
float: left;
font-size: 13px;
@@ -314,5 +558,4 @@ export default {
float: left;
}
}
</style>
+129 -55
View File
@@ -15,31 +15,62 @@
>
<el-row>
<el-col :span="12">
<el-form-item v-if="basicInfo.ReviewerCode" :label="$t('system:reviewer:label:Id')">
<el-input v-model="basicInfo.ReviewerCode" disabled size="small" />
<el-form-item
v-if="basicInfo.ReviewerCode"
:label="$t('system:reviewer:label:Id')"
>
<el-input
v-model="basicInfo.ReviewerCode"
disabled
size="small"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item :label="$t('system:reviewer:label:LastName')" prop="LastName">
<el-input v-model="basicInfo.LastName" :disabled="$route.query.ReviewStatus === '1'" size="small" />
<el-form-item
:label="$t('system:reviewer:label:LastName')"
prop="LastName"
>
<el-input
v-model="basicInfo.LastName"
:disabled="$route.query.ReviewStatus === '1'"
size="small"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="$t('system:reviewer:label:FirstName')" prop="FirstName">
<el-input v-model="basicInfo.FirstName" :disabled="$route.query.ReviewStatus === '1'" size="small" />
<el-form-item
:label="$t('system:reviewer:label:FirstName')"
prop="FirstName"
>
<el-input
v-model="basicInfo.FirstName"
:disabled="$route.query.ReviewStatus === '1'"
size="small"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item :label="$t('system:reviewer:label:NameCN')" prop="ChineseName">
<el-input v-model="basicInfo.ChineseName" :disabled="$route.query.ReviewStatus === '1'" size="small" />
<el-form-item
:label="$t('system:reviewer:label:NameCN')"
prop="ChineseName"
>
<el-input
v-model="basicInfo.ChineseName"
:disabled="$route.query.ReviewStatus === '1'"
size="small"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="$t('system:reviewer:label:Gender')" prop="Sex">
<el-form-item
:label="$t('system:reviewer:label:Gender')"
prop="Sex"
>
<el-select
v-model="basicInfo.Sex"
prop="Sex"
@@ -59,12 +90,22 @@
</el-row>
<el-row>
<el-col :span="12">
<el-form-item :label="$t('system:reviewer:label:Phone')" prop="Phone">
<el-input :disabled="$route.query.ReviewStatus === '1'" v-model="basicInfo.Phone" size="small" />
<el-form-item
:label="$t('system:reviewer:label:Phone')"
prop="Phone"
>
<el-input
:disabled="$route.query.ReviewStatus === '1'"
v-model="basicInfo.Phone"
size="small"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="$t('system:reviewer:label:Title')" prop="TitleIds">
<el-form-item
:label="$t('system:reviewer:label:Title')"
prop="TitleIds"
>
<el-select
:disabled="$route.query.ReviewStatus === '1'"
v-model="basicInfo.TitleIds[0]"
@@ -89,15 +130,22 @@
</el-row>
<el-row>
<el-col :span="12">
<el-form-item :label="$t('system:reviewer:label:Email')" prop="EMail">
<el-input v-model="basicInfo.EMail" :disabled="$route.query.ReviewStatus === '1'" size="small" />
<el-form-item
:label="$t('system:reviewer:label:Email')"
prop="EMail"
>
<el-input
v-model="basicInfo.EMail"
:disabled="$route.query.ReviewStatus === '1'"
size="small"
/>
</el-form-item>
</el-col>
<!-- <el-col :span="12">-->
<!-- <el-form-item label="WeChat: " prop="WeChat">-->
<!-- <el-input v-model="basicInfo.WeChat" :disabled="$route.query.ReviewStatus === '1'" size="small" />-->
<!-- </el-form-item>-->
<!-- </el-col>-->
<!-- <el-col :span="12">-->
<!-- <el-form-item label="WeChat: " prop="WeChat">-->
<!-- <el-input v-model="basicInfo.WeChat" :disabled="$route.query.ReviewStatus === '1'" size="small" />-->
<!-- </el-form-item>-->
<!-- </el-col>-->
<el-col :span="12">
<!-- <el-form-item label="Payment Type: " prop="Nation">
<el-radio-group v-model="basicInfo.Nation">
@@ -106,7 +154,10 @@
</el-radio-group>
</el-form-item> -->
<el-form-item :label="$t('system:reviewer:label:Country')" prop="Nation">
<el-form-item
:label="$t('system:reviewer:label:Country')"
prop="Nation"
>
<el-select
:disabled="$route.query.ReviewStatus === '1'"
v-model="basicInfo.Nation"
@@ -114,13 +165,18 @@
clearable
class="mr"
>
<el-option v-for="item of $d.AttendedReviewerType" :value="item.value" :label="item.label" :key="item.id"/>
<el-option
v-for="item of $d.AttendedReviewerType"
:value="item.value"
:label="item.label"
:key="item.id"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<!-- <el-row>-->
<!-- </el-row>-->
<!-- <el-row>-->
<!-- </el-row>-->
<el-row>
<el-form-item style="margin-left: 120px">
<el-button
@@ -129,7 +185,9 @@
:loading="isDisabled"
size="small"
@click="handleSave"
> {{ $t('common:button:save') }}</el-button>
>
{{ $t('common:button:save') }}</el-button
>
</el-form-item>
</el-row>
</el-form>
@@ -142,6 +200,15 @@ import { getBasicInfo, addOrUpdateDoctorBasicInfo } from '@/api/reviewers'
import { changeURLStatic } from '@/utils/history.js'
import { getBasicDataSelects } from '@/api/dictionary/dictionary'
export default {
props: {
reviewerId: {
type: String,
},
isSystem: {
type: Boolean,
default: true,
},
},
data() {
return {
basicInfo: {
@@ -156,58 +223,58 @@ export default {
EMail: '',
WeChat: '',
Introduction: '',
Nation: 0
Nation: 0,
},
rules: {
FirstName: [
{
required: true,
message: 'Please enter FirstName',
trigger: 'blur'
trigger: 'blur',
},
{ max: 50, message: 'The maximum length is 50' }
{ max: 50, message: 'The maximum length is 50' },
],
LastName: [
{ required: true, message: 'Please enter LastName', trigger: 'blur' },
{ max: 50, message: 'The maximum length is 50' }
{ max: 50, message: 'The maximum length is 50' },
],
ChineseName: [{ max: 50, message: 'The maximum length is 50' }],
Sex: [
{ required: true, message: 'Please select gender', trigger: 'blur' }
{ required: true, message: 'Please select gender', trigger: 'blur' },
],
TitleIds: [
{ required: true, message: 'Please select Title', trigger: 'blur' }
{ required: true, message: 'Please select Title', trigger: 'blur' },
],
Phone: [
{
required: true,
message: 'Please enter phone number',
trigger: 'blur'
trigger: 'blur',
},
{ max: 20, min: 7, message: 'The length is 7 to 20' }
{ max: 20, min: 7, message: 'The length is 7 to 20' },
],
EMail: [
{
required: true,
message: 'Please input the email address',
trigger: 'blur'
trigger: 'blur',
},
{
type: 'email',
message: 'Please input the correct email address',
trigger: 'blur,change'
trigger: 'blur,change',
},
{ max: 50, message: 'The maximum length is 50' }
{ max: 50, message: 'The maximum length is 50' },
],
WeChat: [{ max: 50, message: 'The maximum length is 50' }]
WeChat: [{ max: 50, message: 'The maximum length is 50' }],
},
isDisabled: false,
loading: false,
genderOptions: [
{ label: 'Male', value: 0 },
{ label: 'Female', value: 1 }
{ label: 'Female', value: 1 },
],
dictionaryList: {}
dictionaryList: {},
}
},
mounted() {
@@ -227,7 +294,12 @@ export default {
this.$message.success('Saved successfully')
if (!this.id) {
this.basicInfo.Id = res.Result.Id
changeURLStatic('Id', res.Result.Id)
if (this.isSystem) {
changeURLStatic('Id', res.Result.Id)
}
if (!this.reviewerId) {
this.$emit('update:reviewerId', res.Result.Id)
}
this.basicInfo.ReviewerCode = res.Result.ReviewerCode
}
})
@@ -241,27 +313,29 @@ export default {
async initForm() {
await this.getDicData()
const id = this.$route.query.Id
const id = this.$route.query.Id || this.reviewerId
if (id) {
this.loading = true
getBasicInfo(id).then((res) => {
// eslint-disable-next-line no-unused-vars
const { TitleList, ...param } = res.Result
this.basicInfo = param
this.loading = false
}).catch(() => {
this.loading = false
})
getBasicInfo(id)
.then((res) => {
// eslint-disable-next-line no-unused-vars
const { TitleList, ...param } = res.Result
this.basicInfo = param
this.loading = false
})
.catch(() => {
this.loading = false
})
}
},
getDicData() {
getBasicDataSelects(['Title']).then(res => {
this.dictionaryList = { ...res.Result }
}).catch(() => {
})
}
}
getBasicDataSelects(['Title'])
.then((res) => {
this.dictionaryList = { ...res.Result }
})
.catch(() => {})
},
},
}
</script>
<style lang="scss" scoped>
+91 -59
View File
@@ -2,73 +2,90 @@
<div class="credentials-container">
<div class="clearfix">
<el-card class="e-card">
<p class="title">{{$t('system:Credentials:title:Diploma of the highest medical degree')}}
<p class="title">
{{
$t('system:Credentials:title:Diploma of the highest medical degree')
}}
</p>
<upload-file :doctor-id="doctorId" type="Diploma of the highest medical degree" />
<!-- <el-button size="mini" type="primary" style="position: absolute;bottom:10px;right: 10px;" @click="handleDownload('Reviewer_DiplomaOfTheHighestMedicalDegree_Template')">-->
<!-- <i class="el-icon-view el-icon-download"></i>-->
<!-- Template-->
<!-- </el-button>-->
<upload-file
:doctor-id="doctorId"
type="Diploma of the highest medical degree"
/>
<!-- <el-button size="mini" type="primary" style="position: absolute;bottom:10px;right: 10px;" @click="handleDownload('Reviewer_DiplomaOfTheHighestMedicalDegree_Template')">-->
<!-- <i class="el-icon-view el-icon-download"></i>-->
<!-- Template-->
<!-- </el-button>-->
</el-card>
<el-card class="e-card">
<p class="title">{{$t('system:Credentials:title:Medical Qualification Certificate')}}
<!-- <el-button size="mini" type="primary" style="position: absolute;bottom:10px;right: 10px;" @click="handleDownload('Reviewer_MedicalQualificationCertificate_Template')">-->
<!-- <i class="el-icon-view el-icon-download"></i>-->
<!-- Template-->
<!-- </el-button>-->
<p class="title">
{{ $t('system:Credentials:title:Medical Qualification Certificate') }}
<!-- <el-button size="mini" type="primary" style="position: absolute;bottom:10px;right: 10px;" @click="handleDownload('Reviewer_MedicalQualificationCertificate_Template')">-->
<!-- <i class="el-icon-view el-icon-download"></i>-->
<!-- Template-->
<!-- </el-button>-->
</p>
<upload-file :doctor-id="doctorId" type="Medical Qualification Certificate" />
<upload-file
:doctor-id="doctorId"
type="Medical Qualification Certificate"
/>
</el-card>
<el-card class="e-card">
<p class="title">{{$t('system:Credentials:title:Practice License')}}
<!-- <el-button size="mini" type="primary" style="position: absolute;bottom:10px;right: 10px;" @click="handleDownload('Reviewer_PracticeLicense_Template')">-->
<!-- <i class="el-icon-view el-icon-download"></i>-->
<!-- Template-->
<!-- </el-button>-->
<p class="title">
{{ $t('system:Credentials:title:Practice License') }}
<!-- <el-button size="mini" type="primary" style="position: absolute;bottom:10px;right: 10px;" @click="handleDownload('Reviewer_PracticeLicense_Template')">-->
<!-- <i class="el-icon-view el-icon-download"></i>-->
<!-- Template-->
<!-- </el-button>-->
</p>
<upload-file :doctor-id="doctorId" type="Practice License" />
</el-card>
</div>
<h6 style="line-height:30px;font-size:13px;">{{$t('system:Credentials:title:Modality Certificate')}}</h6>
<h6 style="line-height: 30px; font-size: 13px">
{{ $t('system:Credentials:title:Modality Certificate') }}
</h6>
<div class="clearfix">
<el-card class="e-card">
<p class="title">CT
<!-- <el-button size="mini" type="primary" style="position: absolute;bottom:10px;right: 10px;" @click="handleDownload('Reviewer_CT_Template')">-->
<!-- <i class="el-icon-view el-icon-download"></i>-->
<!-- Template-->
<!-- </el-button>-->
<p class="title">
CT
<!-- <el-button size="mini" type="primary" style="position: absolute;bottom:10px;right: 10px;" @click="handleDownload('Reviewer_CT_Template')">-->
<!-- <i class="el-icon-view el-icon-download"></i>-->
<!-- Template-->
<!-- </el-button>-->
</p>
<upload-file :doctor-id="doctorId" type="Modality Certificate(CT)" />
</el-card>
<el-card class="e-card">
<p class="title">MRI
<!-- <el-button size="mini" type="primary" style="position: absolute;bottom:10px;right: 10px;" @click="handleDownload('Reviewer_MRI_Template')">-->
<!-- <i class="el-icon-view el-icon-download"></i>-->
<!-- Template-->
<!-- </el-button>-->
<p class="title">
MRI
<!-- <el-button size="mini" type="primary" style="position: absolute;bottom:10px;right: 10px;" @click="handleDownload('Reviewer_MRI_Template')">-->
<!-- <i class="el-icon-view el-icon-download"></i>-->
<!-- Template-->
<!-- </el-button>-->
</p>
<upload-file :doctor-id="doctorId" type="Modality Certificate(MRI)" />
</el-card>
<el-card class="e-card">
<p class="title">NM
<!-- <el-button size="mini" type="primary" style="position: absolute;bottom:10px;right: 10px;" @click="handleDownload('Reviewer_NM_Template')">-->
<!-- <i class="el-icon-view el-icon-download"></i>-->
<!-- Template-->
<!-- </el-button>-->
<p class="title">
NM
<!-- <el-button size="mini" type="primary" style="position: absolute;bottom:10px;right: 10px;" @click="handleDownload('Reviewer_NM_Template')">-->
<!-- <i class="el-icon-view el-icon-download"></i>-->
<!-- Template-->
<!-- </el-button>-->
</p>
<upload-file :doctor-id="doctorId" type="Modality Certificate(NM)" />
</el-card>
<el-card class="e-card">
<p class="title">US
<!-- <el-button size="mini" type="primary" style="position: absolute;bottom:10px;right: 10px;" @click="handleDownload('Reviewer_US_Template')">-->
<!-- <i class="el-icon-view el-icon-download"></i>-->
<!-- Template-->
<!-- </el-button>-->
<p class="title">
US
<!-- <el-button size="mini" type="primary" style="position: absolute;bottom:10px;right: 10px;" @click="handleDownload('Reviewer_US_Template')">-->
<!-- <i class="el-icon-view el-icon-download"></i>-->
<!-- Template-->
<!-- </el-button>-->
</p>
<upload-file :doctor-id="doctorId" type="Modality Certificate(US)" />
</el-card>
@@ -80,41 +97,56 @@ import UploadFile from '@/components/UploadFile'
import { DownloadCommonDoc } from '@/api/dictionary'
export default {
components: {
UploadFile
UploadFile,
},
props: {
reviewerId: {
type: String,
},
},
data() {
return {
doctorId: this.$route.query.Id
doctorId: this.$route.query.Id || this.reviewerId,
}
},
watch: {
reviewerId() {
if (this.reviewerId) {
this.doctorId = this.reviewerId
}
},
},
methods: {
handleDownload(code) {
this.loading = true
DownloadCommonDoc(code).then(data => {
this.loading = false
}).catch(() => { this.loading = false })
}
}
DownloadCommonDoc(code)
.then((data) => {
this.loading = false
})
.catch(() => {
this.loading = false
})
},
},
}
</script>
<style lang="scss">
.credentials-container{
.credentials-container {
.e-card {
width: 32.5%;
height: 180px;
font-size: 12px;
position: relative;
float: left;
margin: 0px 10px 5px 0px;
.title{
font-size: 13px;
margin-bottom: 15px;
font-weight: bold;
width: 32.5%;
height: 180px;
font-size: 12px;
position: relative;
float: left;
margin: 0px 10px 5px 0px;
.title {
font-size: 13px;
margin-bottom: 15px;
font-weight: bold;
}
}
h6{
margin:0;
h6 {
margin: 0;
}
}
</style>
File diff suppressed because it is too large Load Diff
+264 -169
View File
@@ -1,7 +1,7 @@
<template>
<div class="form-container">
<el-card class="box-card">
<div style="width:80%;">
<div style="width: 80%">
<el-form
ref="employmentForm"
v-loading="loading"
@@ -10,20 +10,22 @@
class="demo-ruleForm"
label-width="150px"
>
<el-row>
<el-col :span="14">
<el-form-item :label="$t('system:reviewer:label:Hospital')" prop="HospitalId">
<el-form-item
:label="$t('system:reviewer:label:Hospital')"
prop="HospitalId"
>
<el-select
v-model="employmentForm.HospitalId"
placeholder="select"
:disabled="$route.query.ReviewStatus === '1'"
style="width:100%;"
style="width: 100%"
size="small"
@change="handleHospitalChange"
>
<el-option
v-for="(item,index) in hospitalList"
v-for="(item, index) in hospitalList"
:key="index"
:label="item.HospitalName"
:value="item.Id"
@@ -39,7 +41,9 @@
<el-row>
<el-col :span="14">
<el-form-item :label="$t('system:reviewer:label:AffiliatedUniversity')">
<el-form-item
:label="$t('system:reviewer:label:AffiliatedUniversity')"
>
<el-input
:disabled="$route.query.ReviewStatus === '1'"
v-model="UniversityAffiliated"
@@ -53,33 +57,48 @@
<el-row>
<el-col :span="14">
<el-form-item :label="$t('system:reviewer:label:City')">
<el-input :disabled="$route.query.ReviewStatus === '1'" v-model="City" size="small" />
<el-input
:disabled="$route.query.ReviewStatus === '1'"
v-model="City"
size="small"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="14">
<el-form-item :label="$t('system:reviewer:label:State/Province')">
<el-input :disabled="$route.query.ReviewStatus === '1'" v-model="Province" size="small" />
<el-input
:disabled="$route.query.ReviewStatus === '1'"
v-model="Province"
size="small"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="14">
<el-form-item :label="$t('system:reviewer:label:Country')">
<el-input :disabled="$route.query.ReviewStatus === '1'" v-model="Country" size="small" />
<el-input
:disabled="$route.query.ReviewStatus === '1'"
v-model="Country"
size="small"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="14">
<el-form-item :label="$t('system:reviewer:label:Department')" prop="DepartmentId">
<el-form-item
:label="$t('system:reviewer:label:Department')"
prop="DepartmentId"
>
<el-select
v-model="employmentForm.DepartmentId"
placeholder="select"
:disabled="$route.query.ReviewStatus === '1'"
style="width:100%;"
style="width: 100%"
size="small"
>
<!-- <el-option
@@ -99,7 +118,11 @@
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item v-if="employmentForm.DepartmentId===otherId" class="other-item" prop="DepartmentOther">
<el-form-item
v-if="employmentForm.DepartmentId === otherId"
class="other-item"
prop="DepartmentOther"
>
<el-input
:disabled="$route.query.ReviewStatus === '1'"
v-model="employmentForm.DepartmentOther"
@@ -109,7 +132,11 @@
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item v-if="employmentForm.DepartmentId===otherId" class="other-item" prop="DepartmentOtherCN">
<el-form-item
v-if="employmentForm.DepartmentId === otherId"
class="other-item"
prop="DepartmentOtherCN"
>
<el-input
:disabled="$route.query.ReviewStatus === '1'"
v-model="employmentForm.DepartmentOtherCN"
@@ -122,13 +149,16 @@
<el-row>
<el-col :span="14">
<el-form-item :label="$t('system:reviewer:label:Rank')" prop="RankId">
<el-form-item
:label="$t('system:reviewer:label:Rank')"
prop="RankId"
>
<el-select
:disabled="$route.query.ReviewStatus === '1'"
v-model="employmentForm.RankId"
placeholder="select"
@change="RankChange"
style="width:100%;"
style="width: 100%"
size="small"
>
<!-- <el-option
@@ -143,12 +173,19 @@
:label="item.label"
:value="item.id"
/>
<!-- <el-option label="Other" :value="otherId" />-->
<!-- <el-option label="Other" :value="otherId" />-->
</el-select>
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item v-if="$fd('Rank', employmentForm.RankId, 'id') === '其它' || $fd('Rank', employmentForm.RankId, 'id') === 'Other'" class="other-item" prop="RankOther">
<el-form-item
v-if="
$fd('Rank', employmentForm.RankId, 'id') === '其它' ||
$fd('Rank', employmentForm.RankId, 'id') === 'Other'
"
class="other-item"
prop="RankOther"
>
<el-input
:disabled="$route.query.ReviewStatus === '1'"
v-model="employmentForm.RankOther"
@@ -158,7 +195,14 @@
</el-form-item>
</el-col>
<el-col :span="5">
<el-form-item v-if="$fd('Rank', employmentForm.RankId, 'id') === '其它' || $fd('Rank', employmentForm.RankId, 'id') === 'Other'" class="other-item" prop="RankOtherCN">
<el-form-item
v-if="
$fd('Rank', employmentForm.RankId, 'id') === '其它' ||
$fd('Rank', employmentForm.RankId, 'id') === 'Other'
"
class="other-item"
prop="RankOtherCN"
>
<el-input
:disabled="$route.query.ReviewStatus === '1'"
v-model="employmentForm.RankOtherCN"
@@ -168,99 +212,50 @@
</el-form-item>
</el-col>
</el-row>
<!-- <el-row>-->
<!-- <el-col :span="14">-->
<!-- <el-form-item label="Physician: " prop="PhysicianId" >-->
<!-- <el-select-->
<!-- v-model="employmentForm.PhysicianId"-->
<!-- placeholder="select"-->
<!-- :disabled="$route.query.ReviewStatus === '1'"-->
<!-- @change="PhysicianChange"-->
<!-- style="width:100%;"-->
<!-- size="small"-->
<!-- >-->
<!-- <el-option-->
<!-- v-for="item of $d.PhysicianOriginal"-->
<!-- :key="item.id"-->
<!-- :label="item.raw.Value"-->
<!-- :value="item.id"-->
<!-- />-->
<!-- </el-select>-->
<!-- </el-form-item>-->
<!-- </el-col>-->
<!-- <el-col :span="5">-->
<!-- <el-form-item v-if="$fd('PhysicianOriginal', employmentForm.PhysicianId, 'id') === '其它' || $fd('PhysicianOriginal', employmentForm.PhysicianId, 'id') === 'Other'" class="other-item" prop="Physician">-->
<!-- <el-input-->
<!-- :disabled="$route.query.ReviewStatus === '1'"-->
<!-- v-model="employmentForm.Physician"-->
<!-- placeholder="Please specify"-->
<!-- size="small"-->
<!-- />-->
<!-- </el-form-item>-->
<!-- </el-col>-->
<!-- <el-col :span="5">-->
<!-- <el-form-item v-if="$fd('PhysicianOriginal', employmentForm.PhysicianId, 'id') === '其它' || $fd('PhysicianOriginal', employmentForm.PhysicianId, 'id') === 'Other'" class="other-item" prop="PhysicianCN">-->
<!-- <el-input-->
<!-- :disabled="$route.query.ReviewStatus === '1'"-->
<!-- v-model="employmentForm.PhysicianCN"-->
<!-- placeholder="请用中文注明"-->
<!-- size="small"-->
<!-- />-->
<!-- </el-form-item>-->
<!-- </el-col>-->
<!-- </el-row>-->
<!-- <el-row>-->
<!-- <el-col :span="14">-->
<!-- <el-form-item label="Position: " prop="PositionId">-->
<!-- <el-select-->
<!-- :disabled="$route.query.ReviewStatus === '1'"-->
<!-- v-model="employmentForm.PositionId"-->
<!-- placeholder="select"-->
<!-- style="width:100%;"-->
<!-- size="small"-->
<!-- >-->
<!-- &lt;!&ndash; <el-option-->
<!-- v-for="(key,value) of dictionaryList.Position"-->
<!-- :key="key"-->
<!-- :label="key"-->
<!-- :value="value"-->
<!-- /> &ndash;&gt;-->
<!-- <el-option-->
<!-- v-for="item of dictionaryList.Position"-->
<!-- :key="item.Id"-->
<!-- :label="item.Value"-->
<!-- :value="item.Id"-->
<!-- />-->
<!-- <el-option label="Other" :value="otherId" />-->
<!-- <el-option label="None" value="f30a074b-2b47-4a92-97ec-e15086d37883" />-->
<!-- </el-select>-->
<!-- </el-form-item>-->
<!-- </el-col>-->
<!-- <el-col :span="5">-->
<!-- <el-form-item v-if="employmentForm.PositionId===otherId" class="other-item" prop="PositionOther">-->
<!-- <el-input-->
<!-- :disabled="$route.query.ReviewStatus === '1'"-->
<!-- v-model="employmentForm.PositionOther"-->
<!-- placeholder="Please specify"-->
<!-- size="small"-->
<!-- />-->
<!-- </el-form-item>-->
<!-- </el-col>-->
<!-- <el-col :span="5">-->
<!-- <el-form-item v-if="employmentForm.PositionId===otherId" class="other-item" prop="PositionOtherCN">-->
<!-- <el-input-->
<!-- :disabled="$route.query.ReviewStatus === '1'"-->
<!-- v-model="employmentForm.PositionOtherCN"-->
<!-- placeholder="请用中文注明"-->
<!-- size="small"-->
<!-- />-->
<!-- </el-form-item>-->
<!-- </el-col>-->
<!-- </el-row>-->
<el-row>
<el-col :span="14">
<el-form-item
:label="$t('system:reviewer:label:WorkPartTime')"
prop="WorkPartTime"
>
<el-input
:disabled="$route.query.ReviewStatus === '1'"
v-model="employmentForm.WorkPartTime"
type="textarea"
:rows="2"
:placeholder="
$t('curriculumVitae:info:form:placeholder:partTimeJob')
"
size="small"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="14">
<el-form-item prop="WorkPartTimeEn">
<el-input
:disabled="$route.query.ReviewStatus === '1'"
v-model="employmentForm.WorkPartTimeEn"
type="textarea"
:rows="2"
:placeholder="
$t('curriculumVitae:info:form:placeholder:partTimeJobEN')
"
size="small"
/>
</el-form-item>
</el-col>
</el-row>
<el-form-item>
<el-button type="primary" :disabled="isDisabled || $route.query.ReviewStatus === '1'" size="small" @click="handleSave">{{ $t('common:button:save') }}</el-button>
<el-button
type="primary"
:disabled="isDisabled || $route.query.ReviewStatus === '1'"
size="small"
@click="handleSave"
>{{ $t('common:button:save') }}</el-button
>
</el-form-item>
</el-form>
</div>
@@ -274,6 +269,11 @@ import store from '@/store'
import { mapGetters } from 'vuex'
export default {
name: 'Employment',
props: {
reviewerId: {
type: String,
},
},
data() {
const RankIndication = (rule, value, callback) => {
if (value === '其它' || value === 'Other') {
@@ -293,7 +293,7 @@ export default {
}
return {
employmentForm: {
Id: this.$route.query.Id,
Id: this.$route.query.Id || this.reviewerId,
DepartmentId: '',
DepartmentOther: '',
DepartmentOtherCN: '',
@@ -307,46 +307,94 @@ export default {
PhysicianId: '',
Physician: '',
PhysicianCN: '',
PhysicianOriginal: null
PhysicianOriginal: null,
WorkPartTime: null,
WorkPartTimeEn: null,
},
UniversityAffiliated: '',
City: '',
Province: '',
Country: '',
employmentRules: {
DepartmentId: [{ required: true, message: 'Please select department', trigger: 'blur' }],
DepartmentId: [
{
required: true,
message: 'Please select department',
trigger: 'blur',
},
],
DepartmentOther: [
{ required: true, message: 'Please specify', trigger: 'blur' },
{ max: 50, message: 'The maximum length is 50' }
{ max: 50, message: 'The maximum length is 50' },
],
DepartmentOtherCN: [{ max: 50, message: 'The maximum length is 50' }],
RankId: [{ required: true, message: 'Please select rank', trigger: 'blur' }],
RankOther: [{ required: true, message: 'Please select rank', trigger: 'blur' }, { max: 50, message: 'The maximum length is 50' }],
RankOtherCN: [{ required: true, message: 'Please select rank', trigger: 'blur' }, { max: 50, message: 'The maximum length is 50' }],
PhysicianId: [{ required: true, message: 'Please select Physician', trigger: 'blur' }],
Physician: [{ required: true, message: 'Please select Physician', trigger: 'blur' }, { max: 50, message: 'The maximum length is 50' }],
PhysicianCN: [{ required: true, message: 'Please select Physician', trigger: 'blur' }, { max: 50, message: 'The maximum length is 50' }],
PositionId: [{ required: true, message: 'Please select position', trigger: 'blur' }],
RankId: [
{ required: true, message: 'Please select rank', trigger: 'blur' },
],
RankOther: [
{ required: true, message: 'Please select rank', trigger: 'blur' },
{ max: 50, message: 'The maximum length is 50' },
],
RankOtherCN: [
{ required: true, message: 'Please select rank', trigger: 'blur' },
{ max: 50, message: 'The maximum length is 50' },
],
PhysicianId: [
{
required: true,
message: 'Please select Physician',
trigger: 'blur',
},
],
Physician: [
{
required: true,
message: 'Please select Physician',
trigger: 'blur',
},
{ max: 50, message: 'The maximum length is 50' },
],
PhysicianCN: [
{
required: true,
message: 'Please select Physician',
trigger: 'blur',
},
{ max: 50, message: 'The maximum length is 50' },
],
PositionId: [
{
required: true,
message: 'Please select position',
trigger: 'blur',
},
],
PositionOther: [{ max: 50, message: 'The maximum length is 50' }],
PositionOtherCN: [{ max: 50, message: 'The maximum length is 50' }],
HospitalId: [{ required: true, message: 'Please select hospital', trigger: 'blur' }]
HospitalId: [
{
required: true,
message: 'Please select hospital',
trigger: 'blur',
},
],
},
isDisabled: false,
selectId: '00000000-0000-0000-0000-000000000000',
otherId: 'ef84e9cb-f1a6-49d7-b6da-34be2c12abd5',
loading: false,
dictionaryList: {}
dictionaryList: {},
}
},
computed: {
...mapGetters(['hospitalList'])
...mapGetters(['hospitalList']),
},
mounted() {
this.initEmployment()
},
methods: {
RankChange(val) {
var o = this.$d.Rank.find(v => {
var o = this.$d.Rank.find((v) => {
return v.id === val
})
if (o.label === '其它' || o.label === 'Other') {
@@ -358,7 +406,7 @@ export default {
}
},
PhysicianChange(val) {
var o = this.$d.PhysicianOriginal.find(v => {
var o = this.$d.PhysicianOriginal.find((v) => {
return v.id === val
})
if (o.label === '其它' || o.label === 'Other') {
@@ -370,7 +418,7 @@ export default {
}
},
handleSave() {
this.$refs.employmentForm.validate(valid => {
this.$refs.employmentForm.validate((valid) => {
if (valid) {
this.loading = true
this.isDisabled = true
@@ -378,62 +426,109 @@ export default {
// var o = this.$d.PhysicianOriginal.find(v => {
// return v.id === this.employmentForm.PhysicianId
// })
param.Id = this.$route.query.Id
param.Id = this.$route.query.Id || this.reviewerId
param.DepartmentId = this.employmentForm.DepartmentId
param.DepartmentOther = this.employmentForm.DepartmentId === this.otherId ? this.employmentForm.DepartmentOther : ''
param.DepartmentOtherCN = this.employmentForm.DepartmentId === this.otherId ? this.employmentForm.DepartmentOtherCN : ''
param.WorkPartTime = this.employmentForm.WorkPartTime
param.WorkPartTimeEn = this.employmentForm.WorkPartTimeEn
param.DepartmentOther =
this.employmentForm.DepartmentId === this.otherId
? this.employmentForm.DepartmentOther
: ''
param.DepartmentOtherCN =
this.employmentForm.DepartmentId === this.otherId
? this.employmentForm.DepartmentOtherCN
: ''
param.RankId = this.employmentForm.RankId
param.RankOther = this.employmentForm.RankId === this.otherId ? this.employmentForm.RankOther : ''
param.RankOtherCN = this.employmentForm.RankId === this.otherId ? this.employmentForm.RankOtherCN : ''
param.RankOther =
this.employmentForm.RankId === this.otherId
? this.employmentForm.RankOther
: ''
param.RankOtherCN =
this.employmentForm.RankId === this.otherId
? this.employmentForm.RankOtherCN
: ''
// param.PhysicianId = this.employmentForm.PhysicianId
// param.Physician = o.label !== '' && o.label !== 'Other' ? o.raw.Value : this.employmentForm.Physician
// param.PhysicianCN = o.label !== '' && o.label !== 'Other' ? o.raw.ValueCN : this.employmentForm.PhysicianCN
param.PositionId = this.employmentForm.PositionId
param.PositionOther = this.employmentForm.PositionId === this.otherId ? this.employmentForm.PositionOther : ''
param.PositionOtherCN = this.employmentForm.PositionId === this.otherId ? this.employmentForm.PositionOtherCN : ''
param.PositionOther =
this.employmentForm.PositionId === this.otherId
? this.employmentForm.PositionOther
: ''
param.PositionOtherCN =
this.employmentForm.PositionId === this.otherId
? this.employmentForm.PositionOtherCN
: ''
param.HospitalId = this.employmentForm.HospitalId
updateEmploymentInfo(param).then(res => {
this.isDisabled = false
this.loading = false
this.$message.success('Saved successfully')
if (!this.id) {
this.employmentForm.Id = res.Result
}
}).catch(() => {
this.isDisabled = false
this.loading = false
})
updateEmploymentInfo(param)
.then((res) => {
this.isDisabled = false
this.loading = false
this.$message.success('Saved successfully')
if (!this.id) {
this.employmentForm.Id = res.Result
}
})
.catch(() => {
this.isDisabled = false
this.loading = false
})
}
})
},
initForm() {
const id = this.$route.query.Id
const id = this.$route.query.Id || this.reviewerId
if (id) {
this.loading = true
getEmploymentInfo(id).then(res => {
const { PhysicianId, Physician, PhysicianCN, DepartmentId, DepartmentOther, DepartmentOtherCN, RankId, RankOther, RankOtherCN, PositionId, PositionOther, PositionOtherCN, HospitalId } = res.Result
this.employmentForm.Id = id
this.employmentForm.DepartmentId = DepartmentId === this.selectId ? '' : DepartmentId
this.employmentForm.DepartmentOther = DepartmentOther
this.employmentForm.DepartmentOtherCN = DepartmentOtherCN
this.employmentForm.RankId = RankId === this.selectId ? '' : RankId
this.employmentForm.RankOther = RankOther
this.employmentForm.RankOtherCN = RankOtherCN
this.employmentForm.PositionId = PositionId === this.selectId ? '' : PositionId
this.employmentForm.PositionOther = PositionOther
this.employmentForm.PhysicianId = PhysicianId
this.employmentForm.Physician = Physician
this.employmentForm.PhysicianCN = PhysicianCN
this.employmentForm.PositionOtherCN = PositionOtherCN
this.employmentForm.HospitalId = HospitalId === this.selectId ? '' : HospitalId
this.handleHospitalChange(this.employmentForm.HospitalId)
this.loading = false
}).catch(() => { this.loading = false })
getEmploymentInfo(id)
.then((res) => {
const {
PhysicianId,
Physician,
PhysicianCN,
DepartmentId,
DepartmentOther,
DepartmentOtherCN,
RankId,
RankOther,
RankOtherCN,
PositionId,
PositionOther,
PositionOtherCN,
HospitalId,
WorkPartTime,
WorkPartTimeEn,
} = res.Result
this.employmentForm.WorkPartTime = WorkPartTime
this.employmentForm.WorkPartTimeEn = WorkPartTimeEn
this.employmentForm.Id = id
this.employmentForm.DepartmentId =
DepartmentId === this.selectId ? '' : DepartmentId
this.employmentForm.DepartmentOther = DepartmentOther
this.employmentForm.DepartmentOtherCN = DepartmentOtherCN
this.employmentForm.RankId = RankId === this.selectId ? '' : RankId
this.employmentForm.RankOther = RankOther
this.employmentForm.RankOtherCN = RankOtherCN
this.employmentForm.PositionId =
PositionId === this.selectId ? '' : PositionId
this.employmentForm.PositionOther = PositionOther
this.employmentForm.PhysicianId = PhysicianId
this.employmentForm.Physician = Physician
this.employmentForm.PhysicianCN = PhysicianCN
this.employmentForm.PositionOtherCN = PositionOtherCN
this.employmentForm.HospitalId =
HospitalId === this.selectId ? '' : HospitalId
this.handleHospitalChange(this.employmentForm.HospitalId)
this.loading = false
})
.catch(() => {
this.loading = false
})
}
},
handleHospitalChange(value) {
const item = this.hospitalList.filter(item => item.Id === value)
const item = this.hospitalList.filter((item) => item.Id === value)
if (item.length) {
this.UniversityAffiliated = item[0].UniversityAffiliated
this.City = item[0].City
@@ -447,15 +542,15 @@ export default {
this.initForm()
},
getDicData() {
getBasicDataSelects(['Department', 'Rank', 'Position']).then(res => {
getBasicDataSelects(['Department', 'Rank', 'Position']).then((res) => {
this.dictionaryList = { ...res.Result }
})
}
}
},
},
}
</script>
<style lang="scss">
.other-item .el-form-item__content{
.other-item .el-form-item__content {
margin-left: 80px !important;
}
</style>
+151 -55
View File
@@ -1,11 +1,43 @@
<template>
<div class="gcp-container">
<el-radio-group v-model="GCP" style="margin-bottom:20px">
<el-radio :label="1">{{ $t('system:GcpCertificate:radio:Yes') }}</el-radio>
<el-radio-group v-model="GCP" style="margin-bottom: 20px">
<el-radio :label="1">{{
$t('system:GcpCertificate:radio:Yes')
}}</el-radio>
<el-radio :label="0">{{ $t('system:GcpCertificate:radio:No') }}</el-radio>
</el-radio-group>
<el-form
ref="certificateFrom"
v-loading="saveBtnLoading"
:model="certificateForm"
:rules="certificateRules"
label-width="80px"
size="small"
>
<el-form-item
:label="$t('curriculumVitae:clinicalTrials:form:CertificateTime')"
prop="GCPTime"
v-if="GCP"
>
<el-date-picker
v-model="certificateForm.GCPTime"
type="date"
placeholder=""
value-format="yyyy-MM-dd"
format="yyyy-MM-dd"
>
</el-date-picker>
</el-form-item>
<el-form-item
:label="$t('curriculumVitae:clinicalTrials:form:certificateHospital')"
prop="GCPAgencies"
v-if="GCP"
>
<el-input v-model="certificateForm.GCPAgencies" clearable></el-input>
</el-form-item>
</el-form>
<div v-if="GCP" class="upload-container">
<!-- <upload-file :doctor-id="doctorId" type="GCP" />-->
<!-- <upload-file :doctor-id="doctorId" type="GCP" />-->
<el-upload
class="upload-demo"
action
@@ -19,12 +51,23 @@
:on-exceed="handleExceed"
accept=".pdf"
>
<el-button size="small" type="primary" :loading="btnDisabled">{{ $t('system:GcpCertificate:upload:Upload') }}</el-button>
<span slot="tip" style="margin-left:10px;" class="el-upload__tip">{{ $t('system:tip:file:pdf') }}</span>
<el-button size="small" type="primary" :loading="btnDisabled">{{
$t('system:GcpCertificate:upload:Upload')
}}</el-button>
<span slot="tip" style="margin-left: 10px" class="el-upload__tip">{{
$t('system:tip:file:pdf')
}}</span>
</el-upload>
</div>
<div>
<el-button :loading="saveBtnLoading" :disabled="(fileList.length === 0 && GCP === 1)" type="primary" size="small" style="margin-top:20px" @click="handleSaveGCP">
<el-button
:loading="saveBtnLoading"
:disabled="fileList.length === 0 && GCP === 1"
type="primary"
size="small"
style="margin-top: 20px"
@click="handleSaveGCP"
>
{{ $t('common:button:save') }}
</el-button>
</div>
@@ -32,26 +75,43 @@
</template>
<script>
import UploadFile from '@/components/UploadFile'
import { uploadFile, deleteAttachment, getAttachmentByType, saveAttachments } from '@/api/attachment'
import {
uploadFile,
deleteAttachment,
getAttachmentByType,
saveAttachments,
} from '@/api/attachment'
import { updateGcpExperience } from '@/api/reviewers'
const defaultCertificateForm = () => {
return {
GCPTime: '',
GCPAgencies: '',
}
}
export default {
name: 'GcpCertificate',
components: {
UploadFile
UploadFile,
},
props: {
doctorId: {
type: String,
required: true
required: true,
},
gcp: {
type: Number,
default: 0
default: 0,
},
gcpId: {
type: String,
default: ''
}
default: '',
},
DATA: {
type: Object,
default: () => {
return {}
},
},
},
data() {
return {
@@ -59,7 +119,9 @@ export default {
btnDisabled: false,
saveBtnLoading: false,
GCP: 0,
GCPID: ''
GCPID: '',
certificateForm: defaultCertificateForm(),
certificateRules: {},
}
},
watch: {
@@ -68,7 +130,16 @@ export default {
},
gcpId(val) {
this.GCPID = val
}
},
DATA: {
handler() {
Object.keys(this.certificateForm).forEach((key) => {
this.certificateForm[key] = this.DATA[key]
})
},
immediate: true,
deep: true,
},
},
mounted() {
this.initFileList()
@@ -76,48 +147,53 @@ export default {
methods: {
handleSaveGCP() {
if (this.GCP && this.GCPID === '') {
this.$message.info(this.$t('reviewers:GCPCertificate:message:uploadValid'))
this.$message.info(
this.$t('reviewers:GCPCertificate:message:uploadValid')
)
return
}
const param = {
Id: this.doctorId,
GCP: this.GCP,
GCPId: this.GCP ? this.GCPID : ''
GCPId: this.GCP ? this.GCPID : '',
GCPTime: this.GCP ? this.certificateForm.GCPTime : '',
GCPAgencies: this.GCP ? this.certificateForm.GCPAgencies : '',
}
this.saveBtnLoading = true
updateGcpExperience(param).then(res => {
if (this.GCP === 0) {
this.fileList = []
}
this.saveBtnLoading = false
this.$message.success(this.$t('common:message:savedSuccessfully'))
}).catch(() => {
this.saveBtnLoading = false
})
updateGcpExperience(param)
.then((res) => {
if (this.GCP === 0) {
this.fileList = []
}
this.saveBtnLoading = false
this.$message.success(this.$t('common:message:savedSuccessfully'))
})
.catch(() => {
this.saveBtnLoading = false
})
},
initFileList() {
if(!this.doctorId) return
getAttachmentByType(this.doctorId, 'GCP')
.then(res => {
if (res.IsSuccess) {
if (res.Result.length > 0) {
this.fileList = this.formatterFileList(res.Result)
} else {
this.fileList = []
}
if (!this.doctorId) return
getAttachmentByType(this.doctorId, 'GCP').then((res) => {
if (res.IsSuccess) {
if (res.Result.length > 0) {
this.fileList = this.formatterFileList(res.Result)
} else {
this.fileList = []
}
})
}
})
},
formatterFileList(list) {
var arr = []
list.forEach(item => {
list.forEach((item) => {
var data = {
name: item.FileName,
path: item.Path,
fullPath: item.FullPath,
id: item.Id,
type: item.Type
type: item.Type,
}
arr.push(data)
})
@@ -127,8 +203,17 @@ export default {
var fileName = param.file.name
this.btnDisabled = true
const file = await this.fileToBlob(param.file)
const res = await this.OSSclient.put(`/SystemData/reviewer/GCP/${this.doctorId}/${fileName}`, file)
this.fileList.push({ name: fileName, path: this.$getObjectName(res.url), fullPath: this.$getObjectName(res.url),url: this.$getObjectName(res.url), type: 'GCP' })
const res = await this.OSSclient.put(
`/SystemData/reviewer/GCP/${this.doctorId}/${fileName}`,
file
)
this.fileList.push({
name: fileName,
path: this.$getObjectName(res.url),
fullPath: this.$getObjectName(res.url),
url: this.$getObjectName(res.url),
type: 'GCP',
})
this.saveFile()
},
// uploadFile(param) {
@@ -147,15 +232,24 @@ export default {
// },
saveFile() {
const { name, path, fullPath, type } = this.fileList[0]
const param = [{ DoctorId: this.doctorId, Type: type, Path: path, FullPath: fullPath, FileName: name }]
saveAttachments(param).then(res => {
this.btnDisabled = false
if (res.IsSuccess) {
this.fileList[0].id = res.Result[0].Id
this.GCPID = res.Result[0].Id
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
})
const param = [
{
DoctorId: this.doctorId,
Type: type,
Path: path,
FullPath: fullPath,
FileName: name,
},
]
saveAttachments(param)
.then((res) => {
this.btnDisabled = false
if (res.IsSuccess) {
this.fileList[0].id = res.Result[0].Id
this.GCPID = res.Result[0].Id
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
})
.catch(() => {
this.btnDisabled = false
})
@@ -177,19 +271,19 @@ export default {
handleRemoveFile(file, fileList) {
if (file && file.status === 'success') {
if (file.id) {
deleteAttachment(file.id, file.path).then(res => {
deleteAttachment(file.id, file.path).then((res) => {
if (res.IsSuccess) {
this.fileList = []
this.GCPID = ''
this.$message({
message: this.$t('common:message:deletedSuccessfully'),
type: 'success'
type: 'success',
})
}
})
} else {
this.fileList = []
this.GCPID = ''
this.GCPID = ''
}
}
},
@@ -202,18 +296,20 @@ export default {
this.$message.warning(`Upload is currently limited to 1 file`)
},
fileValid(fileName, typeArr) {
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">
.gcp-container{
.gcp-container {
.upload-container .el-upload--text {
border: none;
width: 80px;
+182
View File
@@ -0,0 +1,182 @@
<template>
<div class="form-container basic-form">
<el-card class="box-card">
<div style="width: 1000px">
<!-- v-loading="loading" -->
<el-form
ref="payForm"
v-loading="loading"
:inline="true"
:rules="rules"
:model="form"
class="demo-ruleForm"
label-width="120px"
size="small"
>
<div>{{ $t('system:reviewer:title:pay') }}</div>
<el-row>
<el-col :span="12">
<el-form-item
:label="$t('system:reviewer:label:BankNum')"
prop="BankNum"
>
<el-input v-model="form.BankNum" clearable size="small" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
:label="$t('system:reviewer:label:BankName')"
prop="BankName"
>
<el-input v-model="form.BankName" clearable size="small" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item
:label="$t('system:reviewer:label:OpeningBank')"
prop="OpeningBank"
>
<el-input v-model="form.OpeningBank" clearable size="small" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item
:label="$t('system:reviewer:label:IdCard')"
prop="IdCard"
>
<el-input v-model="form.IdCard" clearable size="small" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item
:label="$t('system:reviewer:label:BankPhoneNum')"
prop="BankPhoneNum"
>
<el-input v-model="form.BankPhoneNum" clearable size="small" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-form-item style="margin-left: 120px">
<el-button
:disabled="$route.query.ReviewStatus === '1'"
type="primary"
:loading="loading"
size="small"
@click="handleSave"
>
{{ $t('common:button:save') }}</el-button
>
</el-form-item>
</el-row>
</el-form>
</div>
</el-card>
</div>
</template>
<script>
import { getDetail, updatePaymentMode } from '@/api/reviewers'
export default {
props: {
reviewerId: {
type: String,
},
},
data() {
return {
loading: false,
form: {
BankNum: '',
BankName: '',
OpeningBank: '',
IdCard: '',
BankPhoneNum: '',
},
rules: {
BankNum: [
{
validator: (rule, value, callback) => {
let reg = new RegExp(/^[A-Za-z0-9]+$/, 'ig')
if (value && !reg.test(value)) {
callback(new Error(this.$t('common:ruleMessage:pattern')))
} else {
callback()
}
},
trigger: 'blur',
},
],
IdCard: [
{
validator: (rule, value, callback) => {
let reg = new RegExp(/(^\d{15}$)|(^\d{17}([0-9]|X)$)/, 'ig')
if (value && !reg.test(value)) {
callback(new Error(this.$t('common:ruleMessage:pattern')))
} else {
callback()
}
},
trigger: 'blur',
},
],
BankPhoneNum: [
{
validator: (rule, value, callback) => {
let reg = new RegExp(/^\d{11}$/, 'i')
if (value && !reg.test(value)) {
callback(new Error(this.$t('common:ruleMessage:pattern')))
} else {
callback()
}
},
trigger: 'blur',
},
],
},
}
},
mounted() {
this.getDetail()
},
methods: {
async handleSave() {
try {
let validate = await this.$refs.payForm.validate()
if (!validate) return false
this.form.Id = this.$route.query.Id || this.reviewerId
this.loading = true
let res = await updatePaymentMode(this.form)
this.loading = false
if (res.IsSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
} catch (err) {
this.loading = false
console.log(err)
}
},
//
async getDetail() {
try {
let id = this.$route.query.Id || this.reviewerId
if (!id) return false
this.loading = true
let res = await getDetail(id)
this.loading = false
if (res.IsSuccess) {
Object.keys(this.form).forEach((key) => {
this.form[key] = res.Result.PaymentModeInfo[key]
})
}
} catch (err) {
this.loading = false
console.log(err)
}
},
},
}
</script>
@@ -1,55 +1,119 @@
<template>
<div class="form-container" style="width:80%;">
<el-form ref="researchForm" v-loading="loading" label-width="140px" :model="researchForm" size="small">
<div class="form-container" style="width: 80%">
<el-form
ref="researchForm"
v-loading="loading"
label-width="140px"
:model="researchForm"
size="small"
>
<div class="title">{{ $t('system:reviewer:title:Research') }}</div>
<el-form-item :label="$t('system:reviewer:label:Field of Research')">
<el-row type="flex" justify="space-between">
<el-col :span="11">
<el-input :disabled="$route.query.ReviewStatus === '1'" v-model="researchForm.Research" type="textarea" rows="5" placeholder="Please specify in English" size="small" />
<el-input
:disabled="$route.query.ReviewStatus === '1'"
v-model="researchForm.Research"
type="textarea"
rows="5"
placeholder="Please specify in English"
size="small"
/>
</el-col>
<el-col :span="11">
<el-input :disabled="$route.query.ReviewStatus === '1'" v-model="researchForm.ResearchCN" type="textarea" rows="5" placeholder="请用中文注明" size="small" />
<el-input
:disabled="$route.query.ReviewStatus === '1'"
v-model="researchForm.ResearchCN"
type="textarea"
rows="5"
placeholder="请用中文注明"
size="small"
/>
</el-col>
</el-row>
</el-form-item>
<el-form-item :label="$t('system:reviewer:label:Grants')">
<el-row type="flex" justify="space-between">
<el-col :span="11">
<el-input :disabled="$route.query.ReviewStatus === '1'" v-model="researchForm.Grants" type="textarea" rows="5" placeholder="Please specify in English" size="small" />
<el-input
:disabled="$route.query.ReviewStatus === '1'"
v-model="researchForm.Grants"
type="textarea"
rows="5"
placeholder="Please specify in English"
size="small"
/>
</el-col>
<el-col :span="11">
<el-input :disabled="$route.query.ReviewStatus === '1'" v-model="researchForm.GrantsCN" type="textarea" rows="5" placeholder="请用中文注明" size="small" />
<el-input
:disabled="$route.query.ReviewStatus === '1'"
v-model="researchForm.GrantsCN"
type="textarea"
rows="5"
placeholder="请用中文注明"
size="small"
/>
</el-col>
</el-row>
</el-form-item>
<div class="title">{{ $t('system:reviewer:title:Publications') }}</div>
<el-form-item :label="$t('system:reviewer:label:Publications')">
<el-row type="flex" justify="space-between">
<el-col :span="11">
<el-input :disabled="$route.query.ReviewStatus === '1'" v-model="researchForm.Publications" type="textarea" rows="5" placeholder="Please specify in English" size="small" />
</el-col>
<el-col :span="11">
<!-- <el-input v-model="researchForm.PublicationsCN" type="textarea" rows="5" placeholder="请用中文注明" size="small" /> -->
<el-col :span="24">
<el-input
:disabled="$route.query.ReviewStatus === '1'"
v-model="researchForm.Publications"
type="textarea"
rows="5"
placeholder="Please specify in English"
size="small"
/>
</el-col>
</el-row>
</el-form-item>
<div class="title">{{ $t('system:reviewer:title:AH') }}</div>
<el-form-item :label="$t('system:reviewer:label:Awards & Honors')">
<el-row type="flex" justify="space-between">
<el-col :span="11">
<el-input :disabled="$route.query.ReviewStatus === '1'" v-model="researchForm.AwardsHonors" type="textarea" rows="5" placeholder="Please specify in English" size="small" />
<el-input
:disabled="$route.query.ReviewStatus === '1'"
v-model="researchForm.AwardsHonors"
type="textarea"
rows="5"
placeholder="Please specify in English"
size="small"
/>
</el-col>
<el-col :span="11">
<el-input :disabled="$route.query.ReviewStatus === '1'" v-model="researchForm.AwardsHonorsCN" type="textarea" rows="5" placeholder="请用中文注明" size="small" />
<el-input
:disabled="$route.query.ReviewStatus === '1'"
v-model="researchForm.AwardsHonorsCN"
type="textarea"
rows="5"
placeholder="请用中文注明"
size="small"
/>
</el-col>
</el-row>
</el-form-item>
<el-form-item>
<el-button :disabled="$route.query.ReviewStatus === '1'" type="primary" :loading="isDisabled" @click="handleSave">
{{ $t('common:button:save') }}</el-button>
<el-button
:disabled="$route.query.ReviewStatus === '1'"
type="primary"
:loading="isDisabled"
@click="handleSave"
>
{{ $t('common:button:save') }}</el-button
>
</el-form-item>
</el-form>
</div>
</template>
<script>
import { getResearchPublication, addOrUpdateResearchPublication } from '@/api/reviewers'
import {
getResearchPublication,
addOrUpdateResearchPublication,
} from '@/api/reviewers'
export default {
name: 'ResearchPublication',
props: {
@@ -57,8 +121,11 @@ export default {
type: String,
default() {
return ''
}
}
},
},
reviewerId: {
type: String,
},
},
data() {
return {
@@ -69,10 +136,10 @@ export default {
GrantsCN: '',
Publications: '',
AwardsHonors: '',
AwardsHonorsCN: ''
AwardsHonorsCN: '',
},
loading: false,
isDisabled: false
isDisabled: false,
}
},
mounted() {
@@ -80,9 +147,9 @@ export default {
},
methods: {
initForm() {
const id = this.$route.query.Id
const id = this.$route.query.Id || this.reviewerId
if (id) {
getResearchPublication(id).then(res => {
getResearchPublication(id).then((res) => {
if (res.Result) {
this.researchForm.Research = res.Result.Research
this.researchForm.ResearchCN = res.Result.ResearchCN
@@ -99,19 +166,31 @@ export default {
handleSave() {
this.loading = true
this.isDisabled = true
this.researchForm.DoctorId = this.$route.query.Id
addOrUpdateResearchPublication(this.researchForm).then(res => {
this.loading = false
this.isDisabled = false
this.$message.success('Saved successfully')
if (!this.researchForm.Id) {
this.researchForm.Id = res.Result
}
}).catch(() => {
this.loading = false
this.isDisabled = false
})
}
}
this.researchForm.DoctorId = this.$route.query.Id || this.reviewerId
addOrUpdateResearchPublication(this.researchForm)
.then((res) => {
this.loading = false
this.isDisabled = false
this.$message.success('Saved successfully')
if (!this.researchForm.Id) {
this.researchForm.Id = res.Result
}
})
.catch(() => {
this.loading = false
this.isDisabled = false
})
},
},
}
</script>
<style lang="scss" scoped>
.title {
width: 100%;
background-color: #eee;
line-height: 30px;
padding-left: 10px;
border-radius: 3px;
margin-bottom: 20px;
}
</style>
+177 -77
View File
@@ -7,14 +7,19 @@
<div class="uploadFile-container">
<el-upload
class="upload-demo"
:data="{language:2}"
:data="{ language: 2 }"
action
:show-file-list="false"
:http-request="handleUploadFile"
:file-list="resumeList"
accept=".doc,.docx"
>
<el-button size="small" type="primary" :disabled="isDisabled || $route.query.ReviewStatus === '1'">{{ $t('system:GcpCertificate:upload:Upload') }}</el-button>
<el-button
size="small"
type="primary"
:disabled="isDisabled || $route.query.ReviewStatus === '1'"
>{{ $t('system:GcpCertificate:upload:Upload') }}</el-button
>
</el-upload>
</div>
</div>
@@ -24,15 +29,25 @@
<div class="resumeTbl">
<el-table :data="resumeListEN" size="small">
<el-table-column type="index" width="40" />
<el-table-column prop="FileName" :label="$t('system:Resumes:table:Resume')" width="180" show-overflow-tooltip />
<el-table-column prop="CreateTime" :label="$t('system:Resumes:table:Upload Time')" width="150" show-overflow-tooltip />
<el-table-column
prop="FileName"
:label="$t('system:Resumes:table:Resume')"
width="180"
show-overflow-tooltip
/>
<el-table-column
prop="CreateTime"
:label="$t('system:Resumes:table:Upload Time')"
width="150"
show-overflow-tooltip
/>
<el-table-column
prop="IsOfficial"
:label="$t('system:Resumes:table:Official')"
width="100"
>
<template slot-scope="scope">
{{ scope.row.IsOfficial === true?'Yes':'No' }}
{{ scope.row.IsOfficial === true ? 'Yes' : 'No' }}
</template>
</el-table-column>
<el-table-column
@@ -42,31 +57,44 @@
show-overflow-tooltip
>
<template slot-scope="scope">
{{ scope.row.Language===1?'中文':'English' }}
{{ scope.row.Language === 1 ? '中文' : 'English' }}
<!-- <el-button size="small" type="text" @click="handleLanguageClick(scope.row)">{{ scope.row.Language===1?'CH':scope.row.Language===2?'EN':'Setting' }}</el-button> -->
</template>
</el-table-column>
<el-table-column :label="$t('common:action:action')" width="400">
<template slot-scope="scope">
<el-button type="text" size="small" @click="handlePreview(scope.row)">{{ $t('common:button:download') }}</el-button>
<el-button type="text" size="small" :disabled="$route.query.ReviewStatus === '1'" @click="handleRemoveFile(scope.row)">{{ $t('common:button:delete') }}</el-button>
<el-button
type="text"
size="small"
:disabled="scope.row.IsOfficial || $route.query.ReviewStatus === '1'"
@click="handlePreview(scope.row)"
>{{ $t('common:button:download') }}</el-button
>
<el-button
type="text"
size="small"
:disabled="$route.query.ReviewStatus === '1'"
@click="handleRemoveFile(scope.row)"
>{{ $t('common:button:delete') }}</el-button
>
<el-button
type="text"
size="small"
:disabled="
scope.row.IsOfficial || $route.query.ReviewStatus === '1'
"
@click="handleSetOfiical(scope.row)"
>{{ $t('system:Resumes:button:Set as Official') }}</el-button>
>{{ $t('system:Resumes:button:Set as Official') }}</el-button
>
</template>
</el-table-column>
</el-table>
</div>
<div class="resume-content" style="margin-top:10px;">
<div class="resume-content" style="margin-top: 10px">
<p>{{ $t('system:Resumes:title:ResumeCN') }}</p>
<div class="upload-content">
<div class="uploadFile-container">
<el-upload
:data="{language:1}"
:data="{ language: 1 }"
class="upload-demo"
action
:show-file-list="false"
@@ -74,7 +102,12 @@
:file-list="resumeList"
accept=".doc, .docx"
>
<el-button size="small" type="primary" :disabled="isDisabled || $route.query.ReviewStatus === '1'">{{ $t('system:GcpCertificate:upload:Upload') }}</el-button>
<el-button
size="small"
type="primary"
:disabled="isDisabled || $route.query.ReviewStatus === '1'"
>{{ $t('system:GcpCertificate:upload:Upload') }}</el-button
>
</el-upload>
</div>
</div>
@@ -83,15 +116,25 @@
<div class="resumeTbl">
<el-table :data="resumeListCN" size="small">
<el-table-column type="index" width="40" />
<el-table-column prop="FileName" :label="$t('system:Resumes:table:Resume')" width="180" show-overflow-tooltip />
<el-table-column prop="CreateTime" :label="$t('system:Resumes:table:Upload Time')" width="150" show-overflow-tooltip />
<el-table-column
prop="FileName"
:label="$t('system:Resumes:table:Resume')"
width="180"
show-overflow-tooltip
/>
<el-table-column
prop="CreateTime"
:label="$t('system:Resumes:table:Upload Time')"
width="150"
show-overflow-tooltip
/>
<el-table-column
prop="IsOfficial"
:label="$t('system:Resumes:table:Official')"
width="100"
>
<template slot-scope="scope">
{{ scope.row.IsOfficial === true?'Yes':'No' }}
{{ scope.row.IsOfficial === true ? 'Yes' : 'No' }}
</template>
</el-table-column>
<el-table-column
@@ -101,20 +144,34 @@
show-overflow-tooltip
>
<template slot-scope="scope">
{{ scope.row.Language===1?'中文':'English' }}
{{ scope.row.Language === 1 ? '中文' : 'English' }}
<!-- <el-button size="small" type="text" @click="handleLanguageClick(scope.row)">{{ scope.row.Language===1?'CH':scope.row.Language===2?'EN':'Setting' }}</el-button> -->
</template>
</el-table-column>
<el-table-column :label="$t('common:action:action')" width="400">
<template slot-scope="scope">
<el-button type="text" size="small" @click="handlePreview(scope.row)">{{ $t('common:button:download') }}</el-button>
<el-button type="text" size="small" :disabled="$route.query.ReviewStatus === '1'" @click="handleRemoveFile(scope.row)">{{ $t('common:button:delete') }}</el-button>
<el-button
type="text"
size="small"
:disabled="scope.row.IsOfficial || $route.query.ReviewStatus === '1'"
@click="handlePreview(scope.row)"
>{{ $t('common:button:download') }}</el-button
>
<el-button
type="text"
size="small"
:disabled="$route.query.ReviewStatus === '1'"
@click="handleRemoveFile(scope.row)"
>{{ $t('common:button:delete') }}</el-button
>
<el-button
type="text"
size="small"
:disabled="
scope.row.IsOfficial || $route.query.ReviewStatus === '1'
"
@click="handleSetOfiical(scope.row)"
>{{ $t('system:Resumes:button:Set as Official') }}</el-button>
>{{ $t('system:Resumes:button:Set as Official') }}</el-button
>
</template>
</el-table-column>
</el-table>
@@ -126,31 +183,49 @@
size="small"
>
<span>Language: </span>
<el-radio-group :disabled="$route.query.ReviewStatus === '1'" v-model="language">
<el-radio-group
:disabled="$route.query.ReviewStatus === '1'"
v-model="language"
>
<el-radio :label="2">English</el-radio>
<el-radio :label="1">中文</el-radio>
</el-radio-group>
<span slot="footer" class="dialog-footer">
<el-button size="small" :disabled="$route.query.ReviewStatus === '1'" @click="dialogVisible = false">Cancel</el-button>
<el-button type="primary" :disabled="$route.query.ReviewStatus === '1'" size="small" @click="setResumeLanguage">Ok</el-button>
<el-button
size="small"
:disabled="$route.query.ReviewStatus === '1'"
@click="dialogVisible = false"
>Cancel</el-button
>
<el-button
type="primary"
:disabled="$route.query.ReviewStatus === '1'"
size="small"
@click="setResumeLanguage"
>Ok</el-button
>
</span>
</el-dialog>
</div>
</template>
<script>
import { getAttachmentByType, uploadFile, saveAttachments, deleteAttachment, setOfficial, setLanguageForResume } from '@/api/attachment'
import {
getAttachmentByType,
uploadFile,
saveAttachments,
deleteAttachment,
setOfficial,
setLanguageForResume,
} from '@/api/attachment'
export default {
props: {
id: {
reviewerId: {
type: String,
default() {
return ''
}
}
},
},
data() {
return {
doctorId: this.$route.query.Id,
doctorId: this.$route.query.Id || this.reviewerId,
resumeList: [],
resumeListEN: [],
resumeListCN: [],
@@ -159,25 +234,37 @@ export default {
currentRow: {},
repeat: false,
isDisabled: false,
loading: false
loading: false,
}
},
created() {
if(!this.doctorId) return
watch: {
reviewerId() {
if (this.reviewerId) {
this.doctorId = this.reviewerId
this.getResumeList()
}
},
},
mounted() {
if (!this.doctorId) return
this.getResumeList()
},
methods: {
getResumeList() {
this.loading = true
getAttachmentByType(this.doctorId, 'Resume').then(res => {
if (res.IsSuccess) {
if (res.Result.length > 0) {
this.resumeList = res.Result
this.filterByLanguage()
getAttachmentByType(this.doctorId, 'Resume')
.then((res) => {
if (res.IsSuccess) {
if (res.Result.length > 0) {
this.resumeList = res.Result
this.filterByLanguage()
}
}
}
this.loading = false
}).catch(() => { this.loading = false })
this.loading = false
})
.catch(() => {
this.loading = false
})
},
handleUploadFile(param) {
this.repeat = false
@@ -186,20 +273,22 @@ export default {
//
if (this.checkFileSuffix(param.file.name) > -1) {
//
const isRepeat = this.resumeList.some(item => item.FileName === param.file.name)
const isRepeat = this.resumeList.some(
(item) => item.FileName === param.file.name
)
if (isRepeat) {
this.$confirm('Override the existing resume?', {
type: 'warning',
distinguishCancelAndClose: true,
confirmButtonText: 'OK',
cancelButtonText: 'Cancel'
cancelButtonText: 'Cancel',
})
.then(() => {
//
this.repeat = true
this.uploadFile(param.file, param.data.language)
})
.catch(action => {})
.catch((action) => {})
} else {
this.uploadFile(param.file, param.data.language)
}
@@ -212,7 +301,10 @@ export default {
var fileName = file.name
var files = this.resumeList
file = await this.fileToBlob(file)
let res = await this.OSSclient.put(`/SystemData/reviewer/Resume/${this.doctorId}/${fileName}`, file)
let res = await this.OSSclient.put(
`/SystemData/reviewer/Resume/${this.doctorId}/${fileName}`,
file
)
if (this.repeat) {
const index = files.findIndex((item, index) => {
return item.FileName === fileName
@@ -228,26 +320,28 @@ export default {
Path: this.$getObjectName(res.url),
FullPath: this.$getObjectName(res.url),
FileName: fileName,
Language: language
Language: language,
}
files.push(fileData)
}
this.saveUploadFiles(files)
},
saveUploadFiles(files) {
saveAttachments(files).then(res => {
this.resumeList = res.Result
this.filterByLanguage()
this.isDisabled = false
this.$message.success('Uploaded successfully')
}).catch(() => {
this.isDisabled = false
})
saveAttachments(files)
.then((res) => {
this.resumeList = res.Result
this.filterByLanguage()
this.isDisabled = false
this.$message.success('Uploaded successfully')
})
.catch(() => {
this.isDisabled = false
})
},
filterByLanguage() {
this.resumeListEN = []
this.resumeListCN = []
this.resumeList.map(resume => {
this.resumeList.map((resume) => {
if (resume.Language === 2) {
this.resumeListEN.push(resume)
} else if (resume.Language === 1) {
@@ -258,7 +352,9 @@ export default {
checkFileSuffix(fileName) {
const accept = '.doc, .docx'
var index = fileName.lastIndexOf('.')
var suffix = fileName.substring(index + 1, fileName.length).toLocaleLowerCase()
var suffix = fileName
.substring(index + 1, fileName.length)
.toLocaleLowerCase()
return accept.search(suffix)
},
handlePreview(row) {
@@ -270,29 +366,31 @@ export default {
this.$confirm(this.$t('trials:uploadedDicoms:message:deleteMes'), {
type: 'warning',
distinguishCancelAndClose: true,
}).then(() => {
deleteAttachment(row.Id, row.Path)
.then(res => {
})
.then(() => {
deleteAttachment(row.Id, row.Path).then((res) => {
if (res.IsSuccess) {
this.resumeList.splice(this.resumeList.findIndex(item => item.Id === row.Id), 1)
this.resumeList.splice(
this.resumeList.findIndex((item) => item.Id === row.Id),
1
)
this.filterByLanguage()
this.$message({
message: this.$t('common:message:deletedSuccessfully'),
type: 'success'
type: 'success',
})
}
})
})
.catch(action => {})
})
.catch((action) => {})
},
handleSetOfiical(row) {
setOfficial(this.doctorId, row.Id, row.Language).then(res => {
setOfficial(this.doctorId, row.Id, row.Language).then((res) => {
if (res.IsSuccess) {
this.getResumeList()
this.$message({
message: 'Saved successfully!',
type: 'success'
type: 'success',
})
}
})
@@ -303,23 +401,27 @@ export default {
this.currentRow = row
},
setResumeLanguage() {
setLanguageForResume(this.doctorId, this.currentRow.Id, this.language).then(res => {
setLanguageForResume(
this.doctorId,
this.currentRow.Id,
this.language
).then((res) => {
if (res.IsSuccess) {
this.dialogVisible = false
this.getResumeList()
this.$message({
message: 'Saved successfully!',
type: 'success'
type: 'success',
})
}
})
}
}
},
},
}
</script>
<style lang="scss">
.resumes-container{
.resume-content{
.resumes-container {
.resume-content {
p {
float: left;
font-size: 13px;
@@ -341,11 +443,9 @@ export default {
.upload-content {
float: left;
}
}
.el-dialog__body{
.el-dialog__body {
padding: 10px 20px;
}
}
</style>
+206 -90
View File
@@ -1,80 +1,144 @@
<template>
<div v-loading="loading" class="check-container form-container">
<el-form ref="checkForm" :model="checkForm" label-width="200px" size="small" :rules="rules">
<el-form
ref="checkForm"
:model="checkForm"
label-width="200px"
size="small"
:rules="rules"
>
<div class="title-wrapper">
<p>{{$t('system:Setting:title:Blinded information') }}</p>
<p>{{ $t('system:Setting:title:Blinded information') }}</p>
</div>
<div class="check-content">
<el-form-item :label="$t('system:Setting:label:Blind Name')" prop="BlindName">
<el-input
v-model="checkForm.BlindName"
style="width:300px"
/>
<el-form-item
:label="$t('system:Setting:label:Blind Name')"
prop="BlindName"
>
<el-input v-model="checkForm.BlindName" style="width: 300px" />
</el-form-item>
<el-form-item :label="$t('system:Setting:label:Blind NameCN')" prop="BlindNameCN">
<el-input
v-model="checkForm.BlindNameCN"
style="width:300px"
/>
<el-form-item
:label="$t('system:Setting:label:Blind NameCN')"
prop="BlindNameCN"
>
<el-input v-model="checkForm.BlindNameCN" style="width: 300px" />
</el-form-item>
<el-form-item :label="$t('system:Setting:label:Blind Publications')" prop="BlindPublications">
<el-input v-model="checkForm.BlindPublications" type="textarea" rows="8" style="width:60%;" size="small" />
<el-form-item
:label="$t('system:Setting:label:Blind Publications')"
prop="BlindPublications"
>
<el-input
v-model="checkForm.BlindPublications"
type="textarea"
rows="8"
style="width: 60%"
size="small"
/>
</el-form-item>
</div>
<div class="title-wrapper">
<p>{{$t('system:Setting:title:Blinded Setting') }}</p>
<p>{{ $t('system:Setting:title:Blinded Setting') }}</p>
</div>
<div class="check-content">
<el-form-item :label="$t('system:Setting:label:Information Confirmed?')">
<el-radio-group v-model="checkForm.ReviewStatus" @change="handleChange">
<el-radio :label="1">{{$t('system:Setting:label:Information Confirmed?:Yes')}}</el-radio>
<el-radio :label="2">{{$t('system:Setting:label:Information Confirmed?:No')}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('system:Setting:label:Contractor Status?')">
<el-radio-group v-model="checkForm.CooperateStatus" @change="handleChange">
<el-radio :label="1">{{$t('system:Setting:label:Information Confirmed?:Yes')}}</el-radio>
<el-radio :label="2">{{$t('system:Setting:label:Information Confirmed?:No')}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
:label="$t('system:Setting:label:Information Confirmed?')"
>
<el-radio-group
v-model="checkForm.ReviewStatus"
@change="handleChange"
>
<el-radio :label="1">{{
$t('system:Setting:label:Information Confirmed?:Yes')
}}</el-radio>
<el-radio :label="2">{{
$t('system:Setting:label:Information Confirmed?:No')
}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('system:Setting:label:Contractor Status?')">
<el-radio-group
v-model="checkForm.CooperateStatus"
@change="handleChange"
>
<el-radio :label="1">{{
$t('system:Setting:label:Information Confirmed?:Yes')
}}</el-radio>
<el-radio :label="2">{{
$t('system:Setting:label:Information Confirmed?:No')
}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('system:Setting:label:Accepting New Trials?')">
<el-radio-group v-model="checkForm.AcceptingNewTrial" :disabled="radioDisabled">
<el-radio :label="true">{{$t('system:Setting:label:Information Confirmed?:Yes')}}</el-radio>
<el-radio :label="false">{{$t('system:Setting:label:Information Confirmed?:No')}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('system:Setting:label:Accepting New Trials?')">
<el-radio-group
v-model="checkForm.AcceptingNewTrial"
:disabled="radioDisabled"
>
<el-radio :label="true">{{
$t('system:Setting:label:Information Confirmed?:Yes')
}}</el-radio>
<el-radio :label="false">{{
$t('system:Setting:label:Information Confirmed?:No')
}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('system:Setting:label:Actively Reading?')">
<el-radio-group v-model="checkForm.ActivelyReading" :disabled="radioDisabled">
<el-radio :label="true">{{$t('system:Setting:label:Information Confirmed?:Yes')}}</el-radio>
<el-radio :label="false">{{$t('system:Setting:label:Information Confirmed?:No')}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('system:Setting:label:Actively Reading?')">
<el-radio-group
v-model="checkForm.ActivelyReading"
:disabled="radioDisabled"
>
<el-radio :label="true">{{
$t('system:Setting:label:Information Confirmed?:Yes')
}}</el-radio>
<el-radio :label="false">{{
$t('system:Setting:label:Information Confirmed?:No')
}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('system:Setting:label:Is Virtual?')">
<el-radio-group v-model="checkForm.IsVirtual" :disabled="radioDisabled">
<el-radio :label="true">{{$t('system:Setting:label:Information Confirmed?:Yes')}}</el-radio>
<el-radio :label="false">{{$t('system:Setting:label:Information Confirmed?:No')}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('system:Setting:label:Is Virtual?')">
<el-radio-group
v-model="checkForm.IsVirtual"
:disabled="radioDisabled"
>
<el-radio :label="true">{{
$t('system:Setting:label:Information Confirmed?:Yes')
}}</el-radio>
<el-radio :label="false">{{
$t('system:Setting:label:Information Confirmed?:No')
}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('system:Setting:label:On Vacation:')">
<span style="font-size:12px;margin-right:20px;">{{ checkForm.InHoliday }}</span>
<el-button type="text" @click="handleView">{{ $t('system:Setting:Planned Vacation') }}</el-button>
</el-form-item>
<el-form-item :label="$t('system:Setting:label:Comment:')" prop="AdminComment">
<el-input
v-model="checkForm.AdminComment"
type="textarea"
style="width:60%;"
rows="8"
/>
</el-form-item>
<el-form-item :label="$t('system:Setting:label:On Vacation:')">
<span style="font-size: 12px; margin-right: 20px">{{
InHoliday
}}</span>
<el-button type="text" @click="handleView">{{
$t('system:Setting:Planned Vacation')
}}</el-button>
</el-form-item>
<el-form-item
:label="$t('system:Setting:label:Comment:')"
prop="AdminComment"
>
<el-input
v-model="checkForm.AdminComment"
type="textarea"
style="width: 60%"
rows="8"
/>
</el-form-item>
<el-form-item style="margin-top:20px">
<el-button type="primary" :disabled="isDisabled" @click="handleSave">{{ $t('common:button:save') }}</el-button>
</el-form-item>
<el-form-item style="margin-top: 20px">
<el-button
type="primary"
:disabled="isDisabled"
@click="handleSave"
>{{ $t('common:button:save') }}</el-button
>
</el-form-item>
</div>
</el-form>
<el-dialog
@@ -83,6 +147,7 @@
width="50%"
:close-on-click-modal="false"
@close="closeDialog"
append-to-body
>
<div>
<el-date-picker
@@ -96,7 +161,13 @@
:end-placeholder="$t('system:Setting:label:End Date')"
size="small"
/>
<el-button style="margin-left:10px;" type="primary" size="small" @click="handleAddHoliday">{{ $t('common:button:add') }}</el-button>
<el-button
style="margin-left: 10px"
type="primary"
size="small"
@click="handleAddHoliday"
>{{ $t('common:button:add') }}</el-button
>
</div>
<el-table v-loading="loading2" :data="gridData" size="small">
<el-table-column type="index" />
@@ -112,13 +183,19 @@
min-width="120"
:formatter="endTimeFormatter"
/>
<el-table-column :label="$t('common:action:action')" fixed="right" min-width="200">
<el-table-column
:label="$t('common:action:action')"
fixed="right"
min-width="200"
>
<template slot-scope="scope">
<el-button type="text" @click="handleDelete(scope.row)">{{ $t('common:button:delete') }}</el-button>
<el-button type="text" @click="handleDelete(scope.row)">{{
$t('common:button:delete')
}}</el-button>
</template>
</el-table-column>
</el-table>
<div class="pagination" style="padding: 10px 0;text-align: right">
<div class="pagination" style="padding: 10px 0; text-align: right">
<el-pagination
background
layout="total,sizes,prev, pager, next"
@@ -134,17 +211,21 @@
</div>
</template>
<script>
import { getAuditState, updateAuditResume, getVacationList, addOrUpdateVacation, deleteVacation } from '@/api/reviewers'
import {
getAuditState,
updateAuditResume,
getVacationList,
addOrUpdateVacation,
deleteVacation,
getIsVacation,
} from '@/api/reviewers'
import { fmtDate } from '@/utils/formatter'
export default {
name: 'Setting',
props: {
id: {
reviewerId: {
type: String,
default() {
return ''
}
}
},
},
data() {
return {
@@ -165,9 +246,11 @@ export default {
},
rules: {
AdminComment: [{ max: 500, message: 'The maximum length is 500' }],
BlindName: [{ required: true, message: 'Please specify', trigger: 'blur' }],
BlindName: [
{ required: true, message: 'Please specify', trigger: 'blur' },
],
},
doctorId: this.$route.query.Id,
doctorId: this.$route.query.Id || this.reviewerId,
isDisabled: false,
radioDisabled: false,
dialogVisible: false,
@@ -176,16 +259,39 @@ export default {
pageIndex: 1,
pageSize: 5,
totalItems: 0,
loading2: false
loading2: false,
InHoliday: '',
}
},
watch: {
reviewerId() {
if (this.reviewerId) {
this.doctorId = this.reviewerId
}
},
},
mounted() {
this.initForm()
this.getIsVacation()
},
methods: {
//
async getIsVacation() {
try {
let data = {
DoctorId: this.doctorId,
}
let res = await getIsVacation(data)
if (res.IsSuccess) {
this.InHoliday = res.Result.IsVacation ? 'Yes' : 'No'
}
} catch (err) {
console.log(err)
}
},
initForm() {
if (!this.doctorId) return
getAuditState(this.doctorId).then(res => {
getAuditState(this.doctorId).then((res) => {
if (res.Result) {
this.checkForm = res.Result
this.checkForm.InHoliday = res.Result.InHoliday ? 'Yes' : 'No'
@@ -193,7 +299,7 @@ export default {
})
},
handleSave() {
this.$refs.checkForm.validate(valid => {
this.$refs.checkForm.validate((valid) => {
if (valid) {
this.isDisabled = true
const param = {}
@@ -201,11 +307,15 @@ export default {
param.InHoliday = this.checkForm.InHoliday === 'Yes'
param.ResumeStatus = param.ReviewStatus
updateAuditResume(param)
.then(res => {
.then((res) => {
if (res.IsSuccess) {
if (res.Result) { this.checkForm.Id = res.Result }
if (res.Result) {
this.checkForm.Id = res.Result
}
this.isDisabled = false
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.$message.success(
this.$t('common:message:savedSuccessfully')
)
}
})
.catch(() => {
@@ -221,7 +331,7 @@ export default {
initHolidayList() {
this.loading2 = true
getVacationList(this.doctorId, this.pageIndex, this.pageSize)
.then(res => {
.then((res) => {
if (res.IsSuccess) {
this.gridData = res.Result.CurrentPageData
this.totalItems = res.Result.TotalCount
@@ -237,11 +347,11 @@ export default {
const param = {
DoctorId: this.doctorId,
StartDate: this.daterange[0],
EndDate: this.daterange[1]
EndDate: this.daterange[1],
}
this.loading2 = true
addOrUpdateVacation(param)
.then(res => {
.then((res) => {
if (res.IsSuccess) {
param.Id = res.Result
this.gridData.push(param)
@@ -266,14 +376,18 @@ export default {
.then(() => {
this.loading2 = true
deleteVacation(row.Id)
.then(res => {
.then((res) => {
if (res.IsSuccess) {
var index = this.gridData.findIndex((item) => item.Id === row.Id)
var index = this.gridData.findIndex(
(item) => item.Id === row.Id
)
if (index !== -1) {
this.gridData.splice(index, 1)
this.totalItems = this.totalItems - 1
}
this.$message.success(this.$t('common:message:deletedSuccessfully'))
this.$message.success(
this.$t('common:message:deletedSuccessfully')
)
}
this.loading2 = false
})
@@ -281,13 +395,16 @@ export default {
this.loading2 = false
})
})
.catch(action => {})
.catch((action) => {})
},
closeDialog() {
this.initForm()
this.getIsVacation()
},
handleChange() {
if (this.checkForm.ReviewStatus === 2 || this.checkForm.cooperateStatus === 2) {
if (
this.checkForm.ReviewStatus === 2 ||
this.checkForm.cooperateStatus === 2
) {
this.checkForm.ActivelyReading = false
this.checkForm.AcceptingNewTrial = false
this.radioDisabled = true
@@ -319,8 +436,7 @@ export default {
handleSizeChange(val) {
this.pageSize = val
this.initHolidayList()
}
}
},
},
}
</script>
+149 -59
View File
@@ -1,7 +1,7 @@
<template>
<div class="form-container">
<el-card class="box-card">
<div style="width:80%;">
<div style="width: 80%">
<el-form
ref="specialtyForm"
v-loading="loading"
@@ -12,12 +12,15 @@
>
<el-row>
<el-col :span="12">
<el-form-item :label="$t('system:reviewer:label:Specialty')" prop="SpecialityId">
<el-form-item
:label="$t('system:reviewer:label:Specialty')"
prop="SpecialityId"
>
<el-select
v-model="specialtyForm.SpecialityId"
:disabled="$route.query.ReviewStatus === '1'"
placeholder="Please select"
style="width:100%;"
style="width: 100%"
size="small"
>
<!-- <el-option
@@ -37,7 +40,11 @@
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item v-if="specialtyForm.SpecialityId==otherId" class="other-item" prop="SpecialityOther">
<el-form-item
v-if="specialtyForm.SpecialityId == otherId"
class="other-item"
prop="SpecialityOther"
>
<el-input
:disabled="$route.query.ReviewStatus === '1'"
v-model="specialtyForm.SpecialityOther"
@@ -47,7 +54,11 @@
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item v-if="specialtyForm.SpecialityId==otherId" prop="SpecialityOtherCN" class="other-item">
<el-form-item
v-if="specialtyForm.SpecialityId == otherId"
prop="SpecialityOtherCN"
class="other-item"
>
<el-input
:disabled="$route.query.ReviewStatus === '1'"
v-model="specialtyForm.SpecialityOtherCN"
@@ -59,13 +70,16 @@
</el-row>
<el-row>
<el-col :span="12">
<el-form-item :label="$t('system:reviewer:label:Subspeciality')" prop="SubspecialityIds">
<el-form-item
:label="$t('system:reviewer:label:Subspeciality')"
prop="SubspecialityIds"
>
<el-select
v-model="specialtyForm.SubspecialityIds"
:disabled="$route.query.ReviewStatus === '1'"
placeholder="select"
multiple
style="width:100%;"
style="width: 100%"
size="small"
>
<!-- <el-option
@@ -86,7 +100,10 @@
</el-col>
<el-col :span="6">
<el-form-item
v-if="specialtyForm.SubspecialityIds && specialtyForm.SubspecialityIds.indexOf(otherId)>-1"
v-if="
specialtyForm.SubspecialityIds &&
specialtyForm.SubspecialityIds.indexOf(otherId) > -1
"
class="other-item"
prop="SubspecialityOther"
>
@@ -100,7 +117,10 @@
</el-col>
<el-col :span="6">
<el-form-item
v-if="specialtyForm.SubspecialityIds && specialtyForm.SubspecialityIds.indexOf(otherId)>-1"
v-if="
specialtyForm.SubspecialityIds &&
specialtyForm.SubspecialityIds.indexOf(otherId) > -1
"
class="other-item"
prop="SubspecialityOtherCN"
>
@@ -116,12 +136,15 @@
<el-row>
<el-col :span="12">
<el-form-item :label="$t('system:reviewer:label:Modality')" prop="ReadingTypeIds">
<el-form-item
:label="$t('system:reviewer:label:Modality')"
prop="ReadingTypeIds"
>
<el-select
:disabled="$route.query.ReviewStatus === '1'"
v-model="specialtyForm.ReadingTypeIds"
placeholder="Please select"
style="width:100%;"
style="width: 100%"
multiple
size="small"
>
@@ -143,7 +166,10 @@
</el-col>
<el-col :span="6">
<el-form-item
v-if="specialtyForm.ReadingTypeIds && specialtyForm.ReadingTypeIds.indexOf(otherId)>-1"
v-if="
specialtyForm.ReadingTypeIds &&
specialtyForm.ReadingTypeIds.indexOf(otherId) > -1
"
prop="ReadingTypeOther"
class="other-item"
>
@@ -157,7 +183,10 @@
</el-col>
<el-col :span="6">
<el-form-item
v-if="specialtyForm.ReadingTypeIds && specialtyForm.ReadingTypeIds.indexOf(otherId)>-1"
v-if="
specialtyForm.ReadingTypeIds &&
specialtyForm.ReadingTypeIds.indexOf(otherId) > -1
"
prop="ReadingTypeOtherCN"
class="other-item"
>
@@ -171,8 +200,14 @@
</el-col>
</el-row>
<el-form-item>
<el-button type="primary" :disabled="isDisabled || $route.query.ReviewStatus === '1'" size="small" @click="handleSave">
{{ $t('common:button:save') }}</el-button>
<el-button
type="primary"
:disabled="isDisabled || $route.query.ReviewStatus === '1'"
size="small"
@click="handleSave"
>
{{ $t('common:button:save') }}</el-button
>
</el-form-item>
</el-form>
</div>
@@ -184,6 +219,11 @@ import { getSpecialtyInfo, updateSpecialtyInfo } from '@/api/reviewers'
import { getBasicDataSelects } from '@/api/dictionary/dictionary'
export default {
name: 'Specialty',
props: {
reviewerId: {
type: String,
},
},
data() {
return {
specialtyForm: {
@@ -195,37 +235,53 @@ export default {
SubspecialityOtherCN: '',
SpecialityId: '',
SpecialityOther: '',
SpecialityOtherCN: ''
SpecialityOtherCN: '',
},
specialtyRules: {
SpecialityId: [{ required: true, message: 'Please select speciality', trigger: 'blur' }],
SpecialityId: [
{
required: true,
message: 'Please select speciality',
trigger: 'blur',
},
],
SpecialityOther: [
{ required: true, message: 'Please specify', trigger: 'blur' },
{ max: 50, message: 'The maximum length is 50' }
{ max: 50, message: 'The maximum length is 50' },
],
SpecialityOtherCN: [{ max: 50, message: 'The maximum length is 50' }],
ReadingTypeIds: [{ required: true, message: 'Please select clinical reading type', trigger: 'blur' }],
ReadingTypeIds: [
{
required: true,
message: 'Please select clinical reading type',
trigger: 'blur',
},
],
ReadingTypeOther: [
{ required: true, message: 'Please specify', trigger: 'blur' },
{ max: 50, message: 'The maximum length is 50' }
{ max: 50, message: 'The maximum length is 50' },
],
ReadingTypeOtherCN: [
{ max: 50, message: 'The maximum length is 50' }
ReadingTypeOtherCN: [{ max: 50, message: 'The maximum length is 50' }],
SubspecialityIds: [
{
required: true,
message: 'Please select subspecialty',
trigger: 'blur',
},
],
SubspecialityIds: [{ required: true, message: 'Please select subspecialty', trigger: 'blur' }],
SubspecialityOther: [
{ required: true, message: 'Please specify', trigger: 'blur' },
{ max: 50, message: 'The maximum length is 50' }
{ max: 50, message: 'The maximum length is 50' },
],
SubspecialityOtherCN: [
{ max: 50, message: 'The maximum length is 50' }
]
{ max: 50, message: 'The maximum length is 50' },
],
},
selectId: '00000000-0000-0000-0000-000000000000',
otherId: 'ef84e9cb-f1a6-49d7-b6da-34be2c12abd5',
isDisabled: false,
loading: false,
dictionaryList: {}
dictionaryList: {},
}
},
mounted() {
@@ -233,25 +289,44 @@ export default {
},
methods: {
handleSave() {
this.$refs.specialtyForm.validate(valid => {
this.$refs.specialtyForm.validate((valid) => {
if (valid) {
this.isDisabled = true
const params = {}
params.ReadingTypeIds = this.specialtyForm.ReadingTypeIds
params.ReadingTypeOther = params.ReadingTypeIds.indexOf(this.otherId) > -1 ? this.specialtyForm.ReadingTypeOther : ''
params.ReadingTypeOtherCN = params.ReadingTypeIds.indexOf(this.otherId) > -1 ? this.specialtyForm.ReadingTypeOtherCN : ''
params.ReadingTypeOther =
params.ReadingTypeIds.indexOf(this.otherId) > -1
? this.specialtyForm.ReadingTypeOther
: ''
params.ReadingTypeOtherCN =
params.ReadingTypeIds.indexOf(this.otherId) > -1
? this.specialtyForm.ReadingTypeOtherCN
: ''
params.SubspecialityIds = this.specialtyForm.SubspecialityIds
params.SubspecialityOther = params.SubspecialityIds.indexOf(this.otherId) > -1 ? this.specialtyForm.SubspecialityOther : ''
params.SubspecialityOtherCN = params.SubspecialityIds.indexOf(this.otherId) > -1 ? this.specialtyForm.SubspecialityOtherCN : ''
params.SubspecialityOther =
params.SubspecialityIds.indexOf(this.otherId) > -1
? this.specialtyForm.SubspecialityOther
: ''
params.SubspecialityOtherCN =
params.SubspecialityIds.indexOf(this.otherId) > -1
? this.specialtyForm.SubspecialityOtherCN
: ''
params.SpecialityId = this.specialtyForm.SpecialityId
params.SpecialityOther = params.SpecialityId === this.otherId ? this.specialtyForm.SpecialityOther : ''
params.SpecialityOtherCN = params.SpecialityId === this.otherId ? this.specialtyForm.SpecialityOtherCN : ''
params.Id = this.$route.query.Id
updateSpecialtyInfo(params).then(res => {
this.isDisabled = false
this.$message.success('Saved successfully')
this.specialtyForm.Id = res.Result
})
params.SpecialityOther =
params.SpecialityId === this.otherId
? this.specialtyForm.SpecialityOther
: ''
params.SpecialityOtherCN =
params.SpecialityId === this.otherId
? this.specialtyForm.SpecialityOtherCN
: ''
params.Id = this.$route.query.Id || this.reviewerId
updateSpecialtyInfo(params)
.then((res) => {
this.isDisabled = false
this.$message.success('Saved successfully')
this.specialtyForm.Id = res.Result
})
.catch(() => {
this.isDisabled = false
})
@@ -260,20 +335,33 @@ export default {
},
initForm() {
this.loading = true
const id = this.$route.query.Id
const id = this.$route.query.Id || this.reviewerId
if (id) {
getSpecialtyInfo(id).then(res => {
this.specialtyForm.ReadingTypeIds = res.Result.ReadingTypeIds.length > 0 ? res.Result.ReadingTypeIds : []
this.specialtyForm.ReadingTypeOther = res.Result.ReadingTypeOther
this.specialtyForm.ReadingTypeOtherCN = res.Result.ReadingTypeOtherCN
this.specialtyForm.SubspecialityIds = res.Result.SubspecialityIds
this.specialtyForm.SubspecialityOther = res.Result.SubspecialityOther
this.specialtyForm.SubspecialityOtherCN = res.Result.SubspecialityOtherCN
this.specialtyForm.SpecialityId = res.Result.SpecialityId === this.selectId ? '' : res.Result.SpecialityId
this.specialtyForm.SpecialityOther = res.Result.SpecialityOther
this.specialtyForm.SpecialityOtherCN = res.Result.SpecialityOtherCN
this.loading = false
}).catch(() => { this.loading = false })
getSpecialtyInfo(id)
.then((res) => {
this.specialtyForm.ReadingTypeIds =
res.Result.ReadingTypeIds.length > 0
? res.Result.ReadingTypeIds
: []
this.specialtyForm.ReadingTypeOther = res.Result.ReadingTypeOther
this.specialtyForm.ReadingTypeOtherCN =
res.Result.ReadingTypeOtherCN
this.specialtyForm.SubspecialityIds = res.Result.SubspecialityIds
this.specialtyForm.SubspecialityOther =
res.Result.SubspecialityOther
this.specialtyForm.SubspecialityOtherCN =
res.Result.SubspecialityOtherCN
this.specialtyForm.SpecialityId =
res.Result.SpecialityId === this.selectId
? ''
: res.Result.SpecialityId
this.specialtyForm.SpecialityOther = res.Result.SpecialityOther
this.specialtyForm.SpecialityOtherCN = res.Result.SpecialityOtherCN
this.loading = false
})
.catch(() => {
this.loading = false
})
}
},
async initSpecialty() {
@@ -281,15 +369,17 @@ export default {
this.initForm()
},
getDicData() {
getBasicDataSelects(['Department', 'Subspeciality', 'ReadingType']).then(res => {
this.dictionaryList = { ...res.Result }
})
}
}
getBasicDataSelects(['Department', 'Subspeciality', 'ReadingType']).then(
(res) => {
this.dictionaryList = { ...res.Result }
}
)
},
},
}
</script>
<style lang="scss">
.other-item .el-form-item__content{
.other-item .el-form-item__content {
margin-left: 80px !important;
}
</style>
@@ -0,0 +1,136 @@
<template>
<div class="form-container">
<el-card class="box-card">
<div style="width: 80%">
<el-form
ref="summarizeFrom"
v-loading="loading"
:rules="rules"
:model="form"
class="demo-ruleForm"
label-width="150px"
>
<el-row>
<el-col :span="14">
<el-form-item
:label="$t('system:reviewer:label:Summarize')"
prop="Summarize"
>
<el-input
:disabled="$route.query.ReviewStatus === '1'"
v-model="form.Summarize"
type="textarea"
:rows="2"
:placeholder="
$t('curriculumVitae:summarize:form:placeholder:summarize')
"
size="small"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="14">
<el-form-item prop="SummarizeEn">
<el-input
:disabled="$route.query.ReviewStatus === '1'"
v-model="form.SummarizeEn"
type="textarea"
:rows="2"
:placeholder="
$t('curriculumVitae:summarize:form:placeholder:summarizeEN')
"
size="small"
/>
</el-form-item>
</el-col>
</el-row>
<el-form-item>
<el-button
type="primary"
:disabled="$route.query.ReviewStatus === '1'"
size="small"
:loading="loading"
@click="handleSave"
>
{{ $t('common:button:save') }}
</el-button>
</el-form-item>
</el-form>
</div>
</el-card>
</div>
</template>
<script>
import { getDetail, updateGneralSituation } from '@/api/reviewers'
export default {
props: {
reviewerId: {
type: String,
},
},
data() {
return {
loading: false,
form: {
Summarize: null,
SummarizeEn: null,
},
rules: {},
}
},
mounted() {
this.getDetail()
},
methods: {
async handleSave() {
try {
let validate = await this.$refs.summarizeFrom.validate()
if (!validate) return false
this.form.Id = this.$route.query.Id || this.reviewerId
this.loading = true
if (this.$route.query.trialId) {
this.form.TrialId = this.$route.query.trialId
}
let res = await updateGneralSituation(this.form)
this.loading = false
if (res.IsSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
} catch (err) {
this.loading = false
console.log(err)
}
},
//
async getDetail() {
try {
let id = this.$route.query.Id || this.reviewerId
if (!id) return false
this.loading = true
let res = await getDetail(id, this.$route.query.trialId)
this.loading = false
if (res.IsSuccess) {
Object.keys(this.form).forEach((key) => {
this.form[key] = res.Result.SummarizeInfo[key]
})
}
} catch (err) {
this.loading = false
console.log(err)
}
},
},
}
</script>
<style lang="scss" scoped>
.basic-form {
.el-input--small {
width: 220px !important;
}
.el-select--small {
width: 220px !important;
}
}
</style>
@@ -1,10 +1,16 @@
<template>
<div class="form-container">
<div class="title-wrapper">
<p>{{$t('system:TrialExperience:title:Clinical Trial Experience')}}</p>
<el-button :disabled="$route.query.ReviewStatus === '1'" class="add" size="small" @click="handleAddClinicalTrial">{{$t('common:button:add')}}</el-button>
<p>{{ $t('system:TrialExperience:title:Clinical Trial Experience') }}</p>
<el-button
:disabled="$route.query.ReviewStatus === '1'"
class="add"
size="small"
@click="handleAddClinicalTrial"
>{{ $t('common:button:add') }}</el-button
>
</div>
<div style="padding:0 40px;">
<div style="padding: 0 40px">
<el-table
ref="clinicalTrialTbl"
v-loading="loading"
@@ -13,39 +19,110 @@
size="small"
>
<el-table-column type="index" width="40" />
<el-table-column prop="Phase" :label="$t('system:TrialExperience:title:Phase')" min-width="50" />
<el-table-column prop="EvaluationCriteriaList" :label="$t('system:TrialExperience:title:Review Criteria')" min-width="100">
<el-table-column
prop="Phase"
:label="$t('system:TrialExperience:title:Phase')"
min-width="50"
>
<template slot-scope="scope">
{{ scope.row.EvaluationCriteriaList.length>0? scope.row.EvaluationCriteriaList.join(', '):'' }}
{{
scope.row.OtherStages ? scope.row.OtherStages : scope.row.Phase
}}
</template>
</el-table-column>
<el-table-column prop="EvaluationCriteriaList" :label="$t('system:TrialExperience:title:Starting And Ending Date')" min-width="100">
<el-table-column
prop="EvaluationCriteriaList"
:label="$t('system:TrialExperience:title:Review Criteria')"
min-width="100"
>
<template slot-scope="scope">
<span v-if="scope.row.StartTime">{{ scope.row.StartTime.split('-')[0] }}-{{scope.row.EndTime ? scope.row.EndTime.split('-')[0] : '至今'}}</span>
{{
scope.row.EvaluationCriteriaList.length > 0
? scope.row.EvaluationCriteriaList.join(', ')
: ''
}}
<span v-if="scope.row.OtherCriterion">
{{
scope.row.EvaluationCriteriaList.length > 0
? `, ${scope.row.OtherCriterion}`
: scope.row.OtherCriterion
}}
</span>
</template>
</el-table-column>
<el-table-column prop="VisitReadingCount" :label="$t('system:TrialExperience:title:Visit Reading Count')" min-width="70" />
<el-table-column prop="EvaluationContent" :label="$t('system:TrialExperience:Indication')" min-width="70" />
<el-table-column :label="$t('system:TrialExperience:Operation')" min-width="200">
<el-table-column
prop="EvaluationCriteriaList"
:label="$t('system:TrialExperience:title:Starting And Ending Date')"
min-width="100"
>
<template slot-scope="scope">
<el-button type="text" size="small" :disabled="$route.query.ReviewStatus === '1'" @click="handleEdit(scope.row)">
{{ $t('common:button:edit') }}</el-button>
<el-button type="text" size="small" :disabled="$route.query.ReviewStatus === '1'" @click="handleDel(scope.row)">{{ $t('common:button:delete') }}</el-button>
<span v-if="scope.row.StartTime"
>{{ scope.row.StartTime.split('-')[0] }} ~ {{
scope.row.EndTime ? scope.row.EndTime.split('-')[0] : '至今'
}}</span
>
</template>
</el-table-column>
<el-table-column
prop="VisitReadingCount"
:label="$t('system:TrialExperience:title:Visit Reading Count')"
min-width="70"
/>
<el-table-column
prop="EvaluationContent"
:label="$t('system:TrialExperience:Indication')"
min-width="70"
>
<template slot-scope="scope">
<span v-if="scope.row.EvaluationContent">
{{ scope.row.EvaluationContent }}
</span>
<span v-else>
{{ $fd('Indication', scope.row.IndicationEnum) }}
</span>
</template>
</el-table-column>
<el-table-column
:label="$t('system:TrialExperience:Operation')"
min-width="200"
>
<template slot-scope="scope">
<el-button
type="text"
size="small"
:disabled="$route.query.ReviewStatus === '1'"
@click="handleEdit(scope.row)"
>
{{ $t('common:button:edit') }}
</el-button>
<el-button
type="text"
size="small"
:disabled="$route.query.ReviewStatus === '1'"
@click="handleDel(scope.row)"
>
{{ $t('common:button:delete') }}
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="title-wrapper" style="margin-top:10px;">
<p>{{$t('system:TrialExperience:title:GCP Certificate')}}</p>
<div class="title-wrapper" style="margin-top: 10px">
<p>{{ $t('system:TrialExperience:title:GCP Certificate') }}</p>
</div>
<div style="margin-left:40px;width:40%">
<gcp-certificate :doctor-id="doctorId" :gcp="GCP" :gcp-id="GCPID" />
<div style="margin-left: 40px; width: 40%">
<gcp-certificate
:doctor-id="doctorId"
:gcp="GCP"
:gcp-id="GCPID"
:DATA="GCPData"
/>
</div>
<div class="title-wrapper" style="margin-top:10px;">
<p>{{$t('system:TrialExperience:title:Other Relevant Experience')}}</p>
<div class="title-wrapper" style="margin-top: 10px">
<p>{{ $t('system:TrialExperience:title:Other Relevant Experience') }}</p>
</div>
<div style="padding:0 40px;">
<div style="padding: 0 40px">
<el-row type="flex" justify="space-between">
<el-col :span="11">
<el-input
@@ -57,13 +134,34 @@
/>
</el-col>
<el-col :span="11">
<el-input v-model="OtherClinicalExperienceCN" :disabled="$route.query.ReviewStatus === '1'" type="textarea" rows="5" placeholder="请用中文注明" size="small" />
<el-input
v-model="OtherClinicalExperienceCN"
:disabled="$route.query.ReviewStatus === '1'"
type="textarea"
rows="5"
placeholder="请用中文注明"
size="small"
/>
</el-col>
</el-row>
<el-button type="primary" size="small" style="margin-top:20px" :disabled="isBtnDisabled || $route.query.ReviewStatus === '1'" @click="handleSaveOtherClinical">
{{ $t('common:button:save') }}</el-button>
<el-button
type="primary"
size="small"
style="margin-top: 20px"
:disabled="isBtnDisabled || $route.query.ReviewStatus === '1'"
@click="handleSaveOtherClinical"
>
{{ $t('common:button:save') }}</el-button
>
</div>
<el-dialog v-if="clinicalTrialDialogVisible" :title="clinicalTrialDialogTitle" :visible.sync="clinicalTrialDialogVisible" width="480px" :close-on-click-modal="false">
<el-dialog
v-if="clinicalTrialDialogVisible"
:title="clinicalTrialDialogTitle"
:visible.sync="clinicalTrialDialogVisible"
width="480px"
:close-on-click-modal="false"
append-to-body
>
<el-form
ref="clinicalTrialForm"
label-width="150px"
@@ -72,14 +170,15 @@
:model="clinicalTrialForm"
size="small"
>
<el-form-item :label="$t('system:TrialExperience:label:Phase')" prop="PhaseId">
<el-select v-model="clinicalTrialForm.PhaseId">
<!-- <el-option
v-for="(key,value) of dictionaryList.Phase"
:key="value"
:label="key"
:value="value"
/> -->
<el-form-item
:label="$t('system:TrialExperience:label:Phase')"
prop="PhaseId"
>
<el-select
v-model="clinicalTrialForm.PhaseId"
clearable
placeholder=""
>
<el-option
v-for="item of dictionaryList.Trial_Phase"
:key="item.Id"
@@ -88,52 +187,135 @@
/>
</el-select>
</el-form-item>
<el-form-item :label="$t('system:TrialExperience:label:Review Criteria')" prop="EvaluationCriteriaIdList">
<el-form-item prop="OtherStages" v-if="Trial_Phase_isOther">
<el-input
v-model="clinicalTrialForm.OtherStages"
clearable
:placeholder="
$t('curriculumVitae:clinicalTrials:placeholder:byStagesOther')
"
></el-input>
</el-form-item>
<el-form-item
:label="$t('system:TrialExperience:label:Review Criteria')"
prop="EvaluationCriteriaIdList"
>
<el-select
v-model="clinicalTrialForm.EvaluationCriteriaIdList"
multiple
>
<!-- <el-option v-for="(key,value) of dictionaryList.ReadingStandard" :key="value" :label="key" :value="value" /> -->
<!-- <el-option v-for="(key,value) of dictionaryList.CriterionType" :key="value" :label="key" :value="value" /> -->
<el-option
v-for="item of dictionaryList.ReadingStandard"
v-for="item in CriterionType"
:key="item.Id"
:label="item.Value"
:value="item.Id"
/>
</el-select>
</el-form-item>
<el-form-item :label="$t('system:TrialExperience:label:Start Time')" prop="StartTime">
<el-form-item prop="OtherCriterion" v-if="ReadingStandard_isOther">
<el-input
v-model="clinicalTrialForm.OtherCriterion"
clearable
:placeholder="
$t('curriculumVitae:clinicalTrials:placeholder:criterionOther')
"
></el-input>
</el-form-item>
<el-form-item
:label="$t('system:TrialExperience:label:Start Time')"
prop="StartTime"
>
<el-date-picker
v-model="clinicalTrialForm.StartTime"
type="year"
value-format="yyyy"
>
>
</el-date-picker>
</el-form-item>
<el-form-item :label="$t('system:TrialExperience:label:End Time')" prop="EndTime">
<el-form-item
:label="$t('system:TrialExperience:label:End Time')"
prop="EndTime"
>
<el-date-picker
v-model="clinicalTrialForm.EndTime"
type="year"
value-format="yyyy"
>
>
</el-date-picker>
</el-form-item>
<el-form-item :label="$t('system:TrialExperience:Visit Reading Count')" prop="VisitReadingCount">
<el-input style="width: 100%" oninput="value=value.replace(/[^\d]/g,'')" v-model="clinicalTrialForm.VisitReadingCount" controls-position="right" :min="0" />
<el-form-item
:label="$t('system:TrialExperience:Visit Reading Count')"
prop="VisitReadingCount"
>
<el-input
style="width: 100%"
oninput="value=value.replace(/[^\d]/g,'')"
v-model="clinicalTrialForm.VisitReadingCount"
controls-position="right"
:min="0"
/>
</el-form-item>
<el-form-item :label="$t('system:TrialExperience:Indication')" prop="EvaluationContent">
<el-input v-model="clinicalTrialForm.EvaluationContent" />
<!-- 适应症类型 IndicationType -->
<el-form-item
:label="$t('system:TrialExperience:indicationType')"
prop="IndicationTypeId"
>
<el-select
v-model="clinicalTrialForm.IndicationTypeId"
@change="handleIndicationTypeChange"
>
<el-option
v-for="item of $d.IndicationType"
:key="item.id"
:label="item.label"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item
:label="$t('system:TrialExperience:Indication')"
prop="IndicationEnum"
>
<el-select
v-model="clinicalTrialForm.IndicationEnum"
:disabled="!clinicalTrialForm.IndicationTypeId"
>
<el-option
v-for="item of IndicationOptions"
:key="item.id"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item prop="EvaluationContent" v-if="IndicationEnum_isOther">
<el-input
v-model="clinicalTrialForm.EvaluationContent"
clearable
></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button type="primary" size="small" :loading="isDisabled" @click="handleSave">{{ $t('common:button:save') }}</el-button>
<el-button
type="primary"
size="small"
:loading="isDisabled"
@click="handleSave"
>{{ $t('common:button:save') }}</el-button
>
</span>
</el-dialog>
</div>
</template>
<script>
// import UploadFile from '@/components/UploadFile'
import { getTrialExperience, addOrUpdateTrialExperience, deleteTrialExperience, updateOtherExperience } from '@/api/reviewers'
import {
getTrialExperience,
addOrUpdateTrialExperience,
deleteTrialExperience,
updateOtherExperience,
} from '@/api/reviewers'
import { getBasicDataSelects } from '@/api/dictionary/dictionary'
import GcpCertificate from './GcpCertificate.vue'
import moment from 'moment'
@@ -145,11 +327,20 @@ const getClinicalTrialDefault = () => {
VisitReadingCount: 0,
StartTime: null,
EndTime: null,
OtherStages: '',
OtherCriterion: '',
IndicationEnum: null,
IndicationTypeId: null,
}
}
export default {
name: 'TrialExperience',
components: { GcpCertificate },
props: {
reviewerId: {
type: String,
},
},
data() {
return {
loading: false,
@@ -159,54 +350,177 @@ export default {
clinicalTrialForm: getClinicalTrialDefault(),
clinicalTrialRules: {
PhaseId: [
{ required: true, message: 'Please select', trigger: ['blur', 'change'] }
{
required: true,
message: 'Please select',
trigger: ['blur', 'change'],
},
],
EvaluationCriteriaIdList: [
{ required: true, message: 'Please select', trigger: ['blur', 'change'] }
{
required: true,
message: 'Please select',
trigger: ['blur', 'change'],
},
],
VisitReadingCount: [
{ required: true, message: 'Please specify', trigger: 'blur' },
],
EvaluationContent: [
{ required: true, message: 'Please specify', trigger: 'blur' },
{ max: 300, message: 'The maximum length is 300' }
{ max: 300, message: 'The maximum length is 300' },
],
IndicationEnum: [
{ required: true, message: 'Please select', trigger: 'blur' },
],
StartTime: [
{ required: true, message: 'Please specify', trigger: 'blur' },
],
OtherStages: [
{
required: true,
message: 'Please input',
trigger: ['blur', 'change'],
},
],
OtherCriterion: [
{
required: true,
message: 'Please input',
trigger: ['blur', 'change'],
},
],
},
GCP: 0,
GCPID: '',
OtherClinicalExperience: '',
OtherClinicalExperienceCN: '',
isDisabled: false,
doctorId: this.$route.query.Id,
doctorId: this.$route.query.Id || this.reviewerId,
isBtnDisabled: false,
selectId: '00000000-0000-0000-0000-000000000000',
dictionaryList: {}
dictionaryList: {},
otherId: 'ef84e9cb-f1a6-49d7-b6da-34be2c12abd5',
GCPData: {},
}
},
computed: {
IndicationOptions() {
console.log(
this.$d.IndicationType,
this.clinicalTrialForm.IndicationTypeId
)
if (!this.clinicalTrialForm.IndicationTypeId) return []
let indicationGroupingArr = this.$d.IndicationType.filter(
(item) => item.id === this.clinicalTrialForm.IndicationTypeId
)
let indicationGrouping = null
if (
Array.isArray(indicationGroupingArr) &&
indicationGroupingArr.length > 0
) {
indicationGrouping = indicationGroupingArr[0].raw.ChildGroup
}
let arr =
this.$d.Indication.filter(
(item) => indicationGrouping === item.raw.ChildGroup
) || []
return arr
},
IndicationEnum_isOther() {
if (!this.clinicalTrialForm.IndicationEnum) return false
let value = this.$d.Indication.find(
(item) => item.value === this.clinicalTrialForm.IndicationEnum
).label
return value === '其它' || value === 'Other'
},
Trial_Phase_isOther() {
if (!this.clinicalTrialForm.PhaseId) return false
let value = this.dictionaryList.Trial_Phase.find(
(item) => item.Id === this.clinicalTrialForm.PhaseId
).Value
return value === '其它' || value === 'Other'
},
ReadingStandard_isOther() {
if (
!this.clinicalTrialForm.EvaluationCriteriaIdList ||
this.clinicalTrialForm.EvaluationCriteriaIdList.length <= 0
)
return false
return this.clinicalTrialForm.EvaluationCriteriaIdList.includes(
this.otherId
)
},
CriterionType() {
if (!this.dictionaryList.CriterionType) return []
return [
...this.dictionaryList.CriterionType,
{
Id: this.otherId,
Value: 'Other',
},
]
},
},
watch: {
reviewerId() {
if (this.reviewerId) {
this.doctorId = this.reviewerId
this.initForm()
}
},
Trial_Phase_isOther() {
if (!this.Trial_Phase_isOther) {
this.clinicalTrialForm.OtherStages = null
}
},
ReadingStandard_isOther() {
if (!this.ReadingStandard_isOther) {
this.clinicalTrialForm.OtherCriterion = null
}
},
IndicationEnum_isOther() {
if (!this.IndicationEnum_isOther) {
this.clinicalTrialForm.EvaluationContent = null
}
},
},
created() {
this.initForm()
},
methods: {
async initForm() {
await this.getDicData()
const id = this.$route.query.Id
const id = this.$route.query.Id || this.reviewerId
if (id) {
this.loading = true
getTrialExperience(id).then(res => {
this.clinicalTrialList = res.Result.ClinicalTrialExperienceList
this.GCP = res.Result.GCP
this.GCPID = res.Result.GCPId
this.OtherClinicalExperience = res.Result.OtherClinicalExperience
this.OtherClinicalExperienceCN = res.Result.OtherClinicalExperienceCN
this.loading = false
}).catch(() => { this.loading = false })
let data = {
DoctorId: id,
}
if (this.$route.query.trialId) {
data.TrialId = this.$route.query.trialId
}
getTrialExperience(data)
.then((res) => {
this.clinicalTrialList = res.Result.ClinicalTrialExperienceList
this.GCP = res.Result.GCP
this.GCPID = res.Result.GCPId
this.GCPData = {
GCPAgencies: res.Result.GCPAgencies,
GCPTime: res.Result.GCPTime,
}
this.OtherClinicalExperience = res.Result.OtherClinicalExperience
this.OtherClinicalExperienceCN =
res.Result.OtherClinicalExperienceCN
this.loading = false
})
.catch(() => {
this.loading = false
})
}
},
getDicData() {
getBasicDataSelects(['Trial_Phase', 'ReadingStandard']).then(res => {
getBasicDataSelects(['Trial_Phase', 'CriterionType']).then((res) => {
this.dictionaryList = { ...res.Result }
})
},
@@ -217,7 +531,19 @@ export default {
this.clinicalTrialForm = Object.assign({}, getClinicalTrialDefault())
},
handleEdit(row) {
const { Id, PhaseId, EvaluationCriteriaIdList, EvaluationContent, StartTime, EndTime, VisitReadingCount } = row
const {
Id,
PhaseId,
EvaluationCriteriaIdList,
EvaluationContent,
StartTime,
EndTime,
VisitReadingCount,
OtherStages,
OtherCriterion,
IndicationEnum,
IndicationTypeId,
} = row
this.clinicalTrialForm = Object.assign({}, getClinicalTrialDefault())
this.clinicalTrialDialogTitle = 'Edit'
this.clinicalTrialDialogVisible = true
@@ -228,37 +554,52 @@ export default {
this.clinicalTrialForm.VisitReadingCount = VisitReadingCount
this.clinicalTrialForm.EvaluationCriteriaIdList = EvaluationCriteriaIdList
this.clinicalTrialForm.EvaluationContent = EvaluationContent
this.clinicalTrialForm.OtherStages = OtherStages
this.clinicalTrialForm.OtherCriterion = OtherCriterion
this.clinicalTrialForm.IndicationEnum = IndicationEnum
this.clinicalTrialForm.IndicationTypeId = IndicationTypeId
},
handleSave() {
this.$refs.clinicalTrialForm.validate(valid => {
this.$refs.clinicalTrialForm.validate((valid) => {
if (valid) {
this.isDisabled = true
this.clinicalTrialForm.DoctorId = this.$route.query.Id
this.clinicalTrialForm.DoctorId =
this.$route.query.Id || this.reviewerId
var dateTime
if (this.clinicalTrialForm.StartTime) {
dateTime = new Date(this.clinicalTrialForm.StartTime)
this.clinicalTrialForm.StartTime = moment(new Date(dateTime.setDate(dateTime.getDate()+1))).format('YYYY') + "-01"
this.clinicalTrialForm.StartTime =
moment(new Date(dateTime.setDate(dateTime.getDate() + 1))).format(
'YYYY'
) + '-01'
}
if (this.clinicalTrialForm.EndTime) {
dateTime = new Date(this.clinicalTrialForm.EndTime)
this.clinicalTrialForm.EndTime = moment(new Date(dateTime.setDate(dateTime.getDate()+1))).format("YYYY") + "-01"
this.clinicalTrialForm.EndTime =
moment(new Date(dateTime.setDate(dateTime.getDate() + 1))).format(
'YYYY'
) + '-01'
}
addOrUpdateTrialExperience(this.clinicalTrialForm).then(res => {
this.isDisabled = false
this.initForm()
if (this.clinicalTrialForm.Id) {
this.$message({
message: 'Updated successfully',
type: 'success'
})
} else {
this.$message({
message: 'Addedd successfully',
type: 'success'
})
}
this.clinicalTrialDialogVisible = false
})
if (this.$route.query.trialId) {
this.clinicalTrialForm.TrialId = this.$route.query.trialId
}
addOrUpdateTrialExperience(this.clinicalTrialForm)
.then((res) => {
this.isDisabled = false
this.initForm()
if (this.clinicalTrialForm.Id) {
this.$message({
message: 'Updated successfully',
type: 'success',
})
} else {
this.$message({
message: 'Addedd successfully',
type: 'success',
})
}
this.clinicalTrialDialogVisible = false
})
.catch(() => {
this.isDisabled = false
})
@@ -269,20 +610,28 @@ export default {
this.$confirm('Confirm to delete?', {
type: 'warning',
distinguishCancelAndClose: true,
})
.then(() => {
deleteTrialExperience(row.Id)
.then(res => {
this.clinicalTrialList.splice(this.clinicalTrialList.findIndex(item => item.Id === row.Id), 1)
this.$message.success('Deleted successfully !')
})
deleteTrialExperience(row.Id).then((res) => {
this.clinicalTrialList.splice(
this.clinicalTrialList.findIndex((item) => item.Id === row.Id),
1
)
this.$message.success('Deleted successfully !')
})
})
.catch(action => {})
.catch((action) => {})
},
handleSaveOtherClinical() {
const param = { DoctorId: this.doctorId, OtherClinicalExperience: this.OtherClinicalExperience, OtherClinicalExperienceCN: this.OtherClinicalExperienceCN }
updateOtherExperience(param).then(res => {
const param = {
DoctorId: this.doctorId,
OtherClinicalExperience: this.OtherClinicalExperience,
OtherClinicalExperienceCN: this.OtherClinicalExperienceCN,
}
if (this.$route.query.trialId) {
param.TrialId = this.$route.query.trialId
}
updateOtherExperience(param).then((res) => {
if (res.IsSuccess) {
if (this.id) {
this.$message.success('Updated successfully')
@@ -291,7 +640,11 @@ export default {
}
}
})
}
}
},
handleIndicationTypeChange(val) {
this.clinicalTrialForm.EvaluationContent = null
this.clinicalTrialForm.IndicationEnum = null
},
},
}
</script>

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