455 Commits

Author SHA1 Message Date
wangxiaoshuang 9ecaf5e267 1 2026-08-24 09:45:13 +08:00
wangxiaoshuang 1ca4fb7dd4 影像替换与补充 2026-08-21 11:34:52 +08:00
caiyiling cb2fed1ad2 1 2026-08-17 14:29:26 +08:00
caiyiling 4c642cd814 上传更改 2026-07-31 17:20:20 +08:00
caiyiling 9dcbad7616 数据处理更改 2026-07-31 16:40:37 +08:00
caiyiling 197b3213e7 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main 2026-07-29 16:06:05 +08:00
caiyiling 3bfda749d8 质控预览非DICOM文件处理zip类型的数据 2026-07-29 16:05:06 +08:00
wangxiaoshuang 6f911d0d74 锁定蒙版添加白名单
continuous-integration/drone/push Build is passing
2026-07-29 15:53:17 +08:00
caiyiling 78f4a78994 测量数据预览按钮控制更改
continuous-integration/drone/push Build is passing
2026-07-29 09:53:43 +08:00
caiyiling 4e1911223d 1
continuous-integration/drone/push Build is passing
2026-07-28 09:17:40 +08:00
caiyiling 7dd02e84aa 样式调整
continuous-integration/drone/push Build is passing
2026-07-27 15:38:03 +08:00
caiyiling 7f260799d8 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-07-27 15:22:13 +08:00
caiyiling 06c7a8126b IVUS & OCT测量表格添加新开页签预览功能 2026-07-27 15:21:30 +08:00
wangxiaoshuang 78624d9cce 阅片中上传影像需要在下载影像后才能上传
continuous-integration/drone/push Build is passing
2026-07-27 14:28:55 +08:00
caiyiling 6ecc0e58e9 重置标记更改;标记定位更改
continuous-integration/drone/push Build is passing
2026-07-16 15:40:28 +08:00
wangxiaoshuang 84a81e1851 临床数据输入框禁用样式修改
continuous-integration/drone/push Build is passing
2026-07-16 14:12:04 +08:00
wangxiaoshuang b09209af31 完全随机分割问题绑定弹框不显示检查编号显示检查模态
continuous-integration/drone/push Build is passing
2026-07-16 13:30:48 +08:00
caiyiling 9892070ccc 播放功能修改
continuous-integration/drone/push Build is passing
2026-07-16 11:43:10 +08:00
wangxiaoshuang 63cbc8746f 完全随机融合弹框不显示检查号显示检查模态
continuous-integration/drone/push Build is passing
2026-07-16 10:54:55 +08:00
wangxiaoshuang fe64feac80 路由守卫判断条件修改
continuous-integration/drone/push Build was killed
2026-07-15 14:49:51 +08:00
wangxiaoshuang dce7fd9737 Merge branch 'uat'
continuous-integration/drone/push Build is passing
2026-07-14 17:54:02 +08:00
wangxiaoshuang 6187c585ad 接口频繁请求不提示
continuous-integration/drone/push Build is passing
2026-07-14 17:53:39 +08:00
wangxiaoshuang 22ac496d7b 用户编辑页添加国际化
continuous-integration/drone/push Build is passing
2026-07-14 11:50:46 +08:00
caiyiling 45c0e5030b Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-07-10 17:54:12 +08:00
caiyiling 36e23b8135 通知列表添加默认排序 2026-07-10 17:53:50 +08:00
wangxiaoshuang b530f68446 1
continuous-integration/drone/push Build is passing
2026-07-10 17:11:39 +08:00
wangxiaoshuang 383bfabae8 1
continuous-integration/drone/push Build is passing
2026-07-10 16:59:18 +08:00
wangxiaoshuang 196ed7f993 完全随机ir上传下载页面及预览页屏蔽受试者编号和项目编号
continuous-integration/drone/push Build is passing
2026-07-10 16:53:41 +08:00
caiyiling 70cc0027cd 受试者状态更改弹窗移除结束访视日期
continuous-integration/drone/push Build is passing
2026-07-10 16:21:44 +08:00
caiyiling 975b7d39a7 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-07-10 15:06:24 +08:00
caiyiling 21bcd1fddb 只有顺序阅片的时显示是否复制后续任务阅片表单 2026-07-10 15:05:39 +08:00
wangxiaoshuang 04bbde8583 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web 2026-07-10 13:55:06 +08:00
wangxiaoshuang f3c4bedbfd 趋势图提示红色改为蓝色 2026-07-10 13:54:54 +08:00
caiyiling 835aedc800 1
continuous-integration/drone/push Build is passing
2026-07-10 13:40:57 +08:00
caiyiling 9026b1d7fd 自定义标准完全随机阅片更改
continuous-integration/drone/push Build is passing
2026-07-10 13:12:44 +08:00
caiyiling b8f96efa66 列表调整
continuous-integration/drone/push Build is passing
2026-07-10 11:18:05 +08:00
caiyiling 4c4f68dbd0 完全随机阅片规则更改
continuous-integration/drone/push Build is passing
2026-07-10 11:12:55 +08:00
caiyiling 11c56d2f7a 1
continuous-integration/drone/push Build is passing
2026-07-09 16:52:58 +08:00
caiyiling a72521304e Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-07-09 16:33:50 +08:00
caiyiling 8baf6cb88f IR阅片模块相关页面添加任务名称搜索条件 2026-07-09 16:33:14 +08:00
wangxiaoshuang 71e04c3bf1 中心调研移动端确认弹框样式修改
continuous-integration/drone/push Build is passing
2026-07-09 15:17:44 +08:00
caiyiling 1af9b48622 完全随机IR阅片列表及阅片页面更改
continuous-integration/drone/push Build is pending
2026-07-09 14:17:02 +08:00
wangxiaoshuang 447b74fa55 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2026-07-09 13:29:51 +08:00
wangxiaoshuang c633fbcf87 外层问题配置为表格问题不显示时阅片时不展示表格 2026-07-09 13:29:45 +08:00
caiyiling 1cc57ad1d6 修复保存申请重阅表单异常问题
continuous-integration/drone/push Build is passing
2026-07-09 09:30:19 +08:00
caiyiling f282fdfabc 是否复制后续表单去除标准限制
continuous-integration/drone/push Build is passing
2026-07-08 16:46:50 +08:00
caiyiling d77c6ecf03 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-07-08 16:04:27 +08:00
caiyiling 1db75cf4f7 MRI-PDFF报告页数据更改 2026-07-08 16:03:55 +08:00
wangxiaoshuang 7bc70bfd6c 【中心调研】中心调研表手机扫描二维码进入时,同pc一样也需要增加验证。上传文件功能也要能正常使用
continuous-integration/drone/push Build is passing
2026-07-07 17:35:33 +08:00
wangxiaoshuang 69ba30e5a9 【趋势图】趋势图访视结果后增加查看按钮,可以查看已阅结果
continuous-integration/drone/push Build is passing
2026-07-07 10:25:32 +08:00
wangxiaoshuang 18482ddaa9 【中心调研】选择项目方案号生成验证码,项目方案号中有特殊符号,生成的验证码中未显示特殊符号
continuous-integration/drone/push Build is passing
2026-07-07 09:38:14 +08:00
caiyiling 14e2afcfee Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is pending
2026-07-06 17:22:30 +08:00
caiyiling 39dadbbf47 受试者任务状态变更原因更改 2026-07-06 17:20:59 +08:00
wangxiaoshuang d0ba340fea 去除分割上传文件按钮
continuous-integration/drone/push Build is passing
2026-07-06 11:44:37 +08:00
wangxiaoshuang 28ac64747b 锁定模糊度修改
continuous-integration/drone/push Build is passing
2026-07-03 10:00:28 +08:00
wangxiaoshuang 5e014120c1 非dicom匿名矩形工具问题修复
continuous-integration/drone/push Build is passing
2026-07-02 17:03:53 +08:00
wangxiaoshuang 76a937066d 中心调研表状态已锁定pm也不允许修改
continuous-integration/drone/push Build is passing
2026-07-02 16:09:30 +08:00
caiyiling 414af5db9e 路由更改
continuous-integration/drone/push Build is passing
2026-07-02 14:53:50 +08:00
caiyiling 8cc04c8bcf Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-07-02 14:47:26 +08:00
caiyiling 3592fe7ec7 列宽调整 2026-07-02 14:47:14 +08:00
wangxiaoshuang 22705f6035 中心调研表在选择仅上传文件时进行联系人和联系人电话必填校验
continuous-integration/drone/push Build is pending
2026-07-02 14:16:10 +08:00
caiyiling 698dca94d4 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-07-02 14:06:40 +08:00
caiyiling e1f3669aab 列宽调整 2026-07-02 14:06:17 +08:00
wangxiaoshuang 8f2109b4cd 中心调研表提交校验修改
continuous-integration/drone/push Build is passing
2026-07-02 13:51:44 +08:00
wangxiaoshuang 433cfdc123 【中心调研】中文环境登录后,中心调研表是英文的
continuous-integration/drone/push Build is passing
2026-07-02 11:50:02 +08:00
wangxiaoshuang a278b032e2 非dicom匿名对比添加匿名前、匿名后提示 2026-07-02 10:50:11 +08:00
wangxiaoshuang 2ec96a82f0 像素匿名按钮显示条件修改
continuous-integration/drone/push Build is passing
2026-07-02 10:37:28 +08:00
wangxiaoshuang b7b763651f 完全随机判断修改
continuous-integration/drone/push Build is passing
2026-07-02 10:01:09 +08:00
wangxiaoshuang a07cf5f4d0 1
continuous-integration/drone/push Build is passing
2026-07-01 18:01:14 +08:00
wangxiaoshuang 77f0c91e74 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web 2026-07-01 17:58:46 +08:00
wangxiaoshuang 54e9e6f6ad 阅片中上传影像后,查看后处理的影像时,关联访视的Tab需要处理 2026-07-01 17:58:36 +08:00
caiyiling ba71d73407 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-07-01 17:46:34 +08:00
caiyiling 9a64fd1bfd 拦截搜索表单默认提交事件 2026-07-01 17:44:00 +08:00
wangxiaoshuang 9b7d9d3c88 中心调研链接有效期为空时验证码不展示
continuous-integration/drone/push Build is passing
2026-07-01 17:32:37 +08:00
wangxiaoshuang 5ec234c9dd 中心调研支持上传文件
continuous-integration/drone/push Build is passing
2026-07-01 17:24:25 +08:00
caiyiling 4709125107 非dicom图像质控视口信息更改;添加适应窗口、适应图像功能
continuous-integration/drone/push Build is passing
2026-07-01 16:59:24 +08:00
caiyiling 0bbf537ddf 非dicom质控检查列表添加排序
continuous-integration/drone/push Build is passing
2026-07-01 11:03:20 +08:00
caiyiling aba5dec904 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-07-01 10:01:30 +08:00
caiyiling b04530ddd2 阅片保存病灶逻辑更改 2026-07-01 10:00:29 +08:00
wangxiaoshuang 27bacd95d2 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web 2026-07-01 09:23:43 +08:00
wangxiaoshuang 9385b1f671 非DICOM阅片影像中存储在标记信息,需要在质控时进行处理 2026-07-01 09:23:33 +08:00
caiyiling cdcc98efeb 1
continuous-integration/drone/push Build is passing
2026-07-01 09:09:11 +08:00
caiyiling bb00ac10f8 阅片完成状态下radio样式更改;阅片中提交和保存优化;
continuous-integration/drone/push Build is passing
2026-06-30 16:00:52 +08:00
caiyiling e184b5255d Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-06-30 09:53:47 +08:00
caiyiling 095536ca33 反馈按钮更改 2026-06-30 09:53:29 +08:00
wangxiaoshuang 83a1d2d5a4 1
continuous-integration/drone/push Build is passing
2026-06-30 09:34:47 +08:00
caiyiling e91ce47f28 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-06-29 17:57:05 +08:00
caiyiling 02b153c524 历史自定义阅片样式及反馈按钮显隐维护 2026-06-29 17:56:27 +08:00
wangxiaoshuang 2b93fd18ff 阅片中上传影像后,查看后处理的影像时,关联访视的Tab需要处理
continuous-integration/drone/push Build is passing
2026-06-29 17:25:04 +08:00
caiyiling c80e7ca5a9 当浏览器窗口的大小有改变时,适应窗口/1:1分辨率图像效果保持不变
continuous-integration/drone/push Build is passing
2026-06-29 17:19:29 +08:00
caiyiling e7562fbe83 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main 2026-06-29 16:29:13 +08:00
caiyiling 92da42f010 QC质控查看非dicom文件页面更改 2026-06-29 16:28:33 +08:00
wangxiaoshuang 92bc105281 管理员重置账号密码后,用户登录后重新设置密码的页面存在问题
continuous-integration/drone/push Build is passing
2026-06-29 16:09:28 +08:00
wangxiaoshuang 105bd48201 Merge branch 'uat'
continuous-integration/drone/push Build is passing
2026-06-29 13:26:45 +08:00
wangxiaoshuang d57c3b5157 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is running
2026-06-29 11:41:37 +08:00
wangxiaoshuang 30fdd91764 非DICOM阅片页面样式问题 2026-06-29 11:41:17 +08:00
caiyiling f03db6f995 阅片页面下拉工具框大小调整
continuous-integration/drone/push Build is passing
2026-06-29 11:12:00 +08:00
caiyiling 022a2005f1 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-06-25 16:52:37 +08:00
caiyiling e64498858d 受试者编号列宽调整 2026-06-25 16:52:12 +08:00
wangxiaoshuang ffbf12f845 分割球体工具图标更改
continuous-integration/drone/push Build is passing
2026-06-24 15:17:18 +08:00
wangxiaoshuang c0523e2855 【中心调研】调研表过期修改,跳转至过期页面
continuous-integration/drone/push Build is passing
2026-06-24 15:13:21 +08:00
caiyiling 794ebeb42c 1
continuous-integration/drone/push Build is pending
2026-06-24 13:46:55 +08:00
caiyiling ffdf3d2376 非dicom标记更改
continuous-integration/drone/push Build is passing
2026-06-24 13:36:40 +08:00
caiyiling f69a297b2b 角色列表移除IsInternal属性
continuous-integration/drone/push Build is passing
2026-06-24 11:07:58 +08:00
caiyiling 39bd20e880 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-06-24 10:49:46 +08:00
caiyiling 8a5be1a0ea 1 2026-06-24 10:49:20 +08:00
wangxiaoshuang b6f8b41615 阅片页面阅片问题点击保存后,无法提交了
continuous-integration/drone/push Build is passing
2026-06-23 15:10:27 +08:00
wangxiaoshuang bddcadeb89 sulpeak计算公式修改
continuous-integration/drone/push Build is passing
2026-06-23 10:31:10 +08:00
wangxiaoshuang 2e388db02c 分割新工具禁用修改
continuous-integration/drone/push Build is passing
2026-06-22 17:43:22 +08:00
wangxiaoshuang 370f99eabc 分割球体工具触发自动计算修改
continuous-integration/drone/push Build is passing
2026-06-22 16:58:39 +08:00
wangxiaoshuang e2bd9feae6 中心调研表分享信息修改
continuous-integration/drone/push Build is passing
2026-06-22 15:17:00 +08:00
wangxiaoshuang abbc15cb09 增加对于SULpeak测量的支持
continuous-integration/drone/push Build is passing
2026-06-22 15:08:51 +08:00
wangxiaoshuang 87f088ac05 中兴调研表链接过期判断
continuous-integration/drone/push Build is passing
2026-06-18 17:55:23 +08:00
wangxiaoshuang 0971f2b922 1 2026-06-18 17:22:50 +08:00
wangxiaoshuang e93be4cf9c 中心调研表国际化判断
continuous-integration/drone/push Build is passing
2026-06-18 17:05:54 +08:00
wangxiaoshuang 2fca310327 【项目权限】切换角色后进入了没有权限的项目
continuous-integration/drone/push Build is passing
2026-06-18 13:55:09 +08:00
wangxiaoshuang 97411b7eaf 中心调研二维码和链接的时间有效性
continuous-integration/drone/push Build is passing
2026-06-18 13:31:20 +08:00
caiyiling 0631af4d9b 序列切换bug修复
continuous-integration/drone/push Build is passing
2026-06-18 12:34:39 +08:00
caiyiling 2794cabdc8 关键序列面板展示信息补充 2026-06-18 11:28:50 +08:00
caiyiling 8203b3d738 关键序列逻辑更改
continuous-integration/drone/push Build is passing
2026-06-18 09:58:04 +08:00
caiyiling 4d16ebb488 受试者状态变更表单更改
continuous-integration/drone/push Build is running
2026-06-17 16:44:37 +08:00
caiyiling b241682fa9 补充定圆工具标注复制功能
continuous-integration/drone/push Build is passing
2026-06-17 13:31:00 +08:00
wangxiaoshuang cb16658190 Merge branch 'uat'
continuous-integration/drone/push Build is passing
# Conflicts:
#	src/views/trials/trials-panel/reading/read-task/index.vue
2026-06-17 09:23:22 +08:00
wangxiaoshuang 8b766672b5 中心调研表选择中心时可选择禁用中心
continuous-integration/drone/push Build is passing
2026-06-16 17:16:07 +08:00
wangxiaoshuang b7d45456a6 演示项目可以添加虚拟阅片人
continuous-integration/drone/push Build is passing
2026-06-16 14:23:22 +08:00
wangxiaoshuang a4241bccf7 获取字典接口统一添加trialId
continuous-integration/drone/push Build is passing
2026-06-16 14:22:17 +08:00
wangxiaoshuang 212a545aa9 演示项目可以添加虚拟阅片人
continuous-integration/drone/push Build is passing
2026-06-16 13:45:00 +08:00
caiyiling 11c4df2852 阅片页面注意事项更改
continuous-integration/drone/push Build is passing
2026-06-16 13:33:53 +08:00
wangxiaoshuang 05bcb982dd 影像汇总关键图下载可下载分割文件
continuous-integration/drone/push Build is passing
2026-06-16 13:30:41 +08:00
wangxiaoshuang af62056962 Merge branch 'uat'
continuous-integration/drone/push Build is passing
2026-06-16 10:26:50 +08:00
caiyiling de7cd64c9b 关键序列更改;lugano标准交互更改 2026-06-15 13:53:04 +08:00
wangxiaoshuang 83f73db692 中心调研表设备调研备注改为非必填
continuous-integration/drone/push Build is passing
2026-06-15 09:27:41 +08:00
caiyiling 71418c393e 图标更改
continuous-integration/drone/push Build is running
2026-06-11 15:48:53 +08:00
caiyiling 6629fc0a17 1
continuous-integration/drone/push Build is pending
2026-06-11 13:25:00 +08:00
caiyiling b7c00a0b70 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-06-11 13:18:42 +08:00
caiyiling 3907123c1d 系统通知功能优化 2026-06-11 13:18:28 +08:00
wangxiaoshuang e1822126bb 当裁判阅片完成后,列表中高亮或加粗显示最近一次裁判选中的阅片结果
continuous-integration/drone/push Build is passing
2026-06-11 11:36:47 +08:00
caiyiling 05e9bbb050 当前访视增加关键序列
continuous-integration/drone/push Build is passing
2026-06-10 15:59:55 +08:00
caiyiling 6b1fffb800 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-06-10 13:58:43 +08:00
caiyiling d0d8819ab4 关键序列更改 2026-06-10 13:58:25 +08:00
wangxiaoshuang 0e430ef8c9 Merge branch 'uat'
continuous-integration/drone/push Build is running
2026-06-10 13:58:19 +08:00
wangxiaoshuang 0b87a1845c 表格问题字段默认值
continuous-integration/drone/push Build is passing
2026-06-10 13:57:57 +08:00
wangxiaoshuang e7a13be6e4 分割标记边框调节刻度设置为1
continuous-integration/drone/push Build is passing
2026-06-10 10:39:20 +08:00
caiyiling f7b5ab0281 1
continuous-integration/drone/push Build is passing
2026-06-10 10:33:33 +08:00
caiyiling e3be28ca17 非dicom阅片截图文件名称更改
continuous-integration/drone/push Build is running
2026-06-10 10:31:35 +08:00
wangxiaoshuang ee34e6cdc2 受试者阅片结果趋势图优化
continuous-integration/drone/push Build is passing
2026-06-09 16:39:22 +08:00
wangxiaoshuang 8c83897c6e 阅片人页面的工作量统计,改为二级查询 2026-06-09 16:15:26 +08:00
caiyiling 3a6956c031 列宽调整
continuous-integration/drone/push Build is passing
2026-06-09 15:18:53 +08:00
caiyiling 73e7b4c3fa Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-06-09 15:09:29 +08:00
caiyiling 7f9080398a 稽查列表更改 2026-06-09 15:08:54 +08:00
wangxiaoshuang 4c7c0cce47 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is running
2026-06-09 14:57:41 +08:00
wangxiaoshuang eda4b31dda DICOM分割工具的个性化配置 2026-06-09 14:57:38 +08:00
caiyiling dc84d0e96f Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is pending
2026-06-09 13:43:46 +08:00
caiyiling 7fea4b938f 非DICOM阅片工具-支持视频阅片 2026-06-09 13:43:20 +08:00
wangxiaoshuang 7325760c23 在账号长时间未访问,锁定或者退出时,不能展示页面数据
continuous-integration/drone/push Build is passing
2026-06-09 13:39:02 +08:00
wangxiaoshuang 1a26d3885a 1
continuous-integration/drone/push Build is passing
2026-06-08 15:42:58 +08:00
caiyiling f9c9d0be6d Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is running
2026-06-08 14:37:22 +08:00
caiyiling ebfbe27d0c 全局阅片、裁判阅片时不显示复制后续访视数据 2026-06-08 14:36:51 +08:00
wangxiaoshuang 37257da722 1
continuous-integration/drone/push Build is passing
2026-06-08 14:11:09 +08:00
wangxiaoshuang 6d214727b5 Merge branch 'uat'
continuous-integration/drone/push Build is passing
2026-06-08 13:24:38 +08:00
wangxiaoshuang 1db50cff3b 质控搜索条件字段变更
continuous-integration/drone/push Build is passing
2026-06-08 13:23:09 +08:00
wangxiaoshuang 6d17a1f3cd Merge branch 'uat'
continuous-integration/drone/push Build is passing
2026-06-08 09:06:08 +08:00
wangxiaoshuang f38f1f465a 自动发布
continuous-integration/drone/push Build is passing
2026-06-05 21:53:04 +08:00
wangxiaoshuang db627afeda 1 2026-06-05 18:15:19 +08:00
caiyiling fef7215bdb Merge branch 'uat' of https://gitea.frp.extimaging.com/XCKJ/irc_web into uat
continuous-integration/drone/push Build was killed
2026-06-05 15:04:21 +08:00
caiyiling 0718a70f91 Merge branch 'main' into uat 2026-06-05 15:02:32 +08:00
wangxiaoshuang da21972c63 Merge branch 'main' into uat
continuous-integration/drone/push Build is pending
2026-06-05 15:01:31 +08:00
wangxiaoshuang 361ab4e2fd 已阅片访视表格添加查看按钮
continuous-integration/drone/push Build is pending
2026-06-05 15:01:07 +08:00
caiyiling 7a52507d27 阅片页面检查列表更改
continuous-integration/drone/push Build is passing
2026-06-05 14:58:46 +08:00
caiyiling 83dc28008f Merge branch 'main' into uat
continuous-integration/drone/push Build is failing
2026-06-04 17:38:19 +08:00
caiyiling 323b76036a mri-pdff标注清除修改
continuous-integration/drone/push Build is passing
2026-06-04 17:00:02 +08:00
wangxiaoshuang 4b7bb8df95 表格问题增加属性 2026-06-04 14:59:05 +08:00
caiyiling 46aa64365f Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is running
2026-06-03 10:24:47 +08:00
caiyiling d2fae40100 上传预览多帧更改 2026-06-03 10:24:24 +08:00
wangxiaoshuang 4030b1c95b Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2026-06-02 14:57:26 +08:00
wangxiaoshuang d689c23f5a 分割工具半径区间增加 2026-06-02 14:57:22 +08:00
caiyiling 4a7f8a0a93 非dicom自定义阅片报告页更改
continuous-integration/drone/push Build is passing
2026-06-02 11:38:09 +08:00
caiyiling 7daa7fda0c 是否可测量限制更改 2026-06-02 11:14:52 +08:00
caiyiling b25426711a 1
continuous-integration/drone/push Build is passing
2026-06-02 10:59:46 +08:00
caiyiling f06f7827cf Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-06-02 10:46:42 +08:00
caiyiling 3ea46b5d1e MRI-PDFF肝脏分段保存接口更改;自定义标准报告更改 2026-06-02 10:46:02 +08:00
wangxiaoshuang 72634c785d Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web 2026-06-02 10:24:03 +08:00
wangxiaoshuang 2dbf6a1fb3 分割画笔工具添加半径区间选项 2026-06-02 10:23:53 +08:00
caiyiling 755f305389 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-06-02 10:14:13 +08:00
caiyiling 29a6870fb5 Enhanced MRI-PDFF标准更改 2026-06-02 10:13:30 +08:00
wangxiaoshuang 2d99e074b8 后处理上传处理
continuous-integration/drone/push Build is passing
2026-06-01 14:51:19 +08:00
wangxiaoshuang 6e6e2d11d0 影像上传列表受试者状态查询字段变更
continuous-integration/drone/push Build is passing
2026-05-29 17:29:22 +08:00
wangxiaoshuang 6b8711232e 1
continuous-integration/drone/push Build is passing
2026-05-29 10:13:55 +08:00
wangxiaoshuang eb0b9d6fb8 中心调研表配置修改 2026-05-29 10:08:57 +08:00
wangxiaoshuang 7ef1cafeff 1
continuous-integration/drone/push Build is passing
2026-05-29 09:58:23 +08:00
wangxiaoshuang 3ac3e41d0d 中心调研部分字段添加必填标识 2026-05-29 09:13:44 +08:00
wangxiaoshuang 1763e49650 中心调研表添加字段
continuous-integration/drone/push Build is passing
2026-05-28 15:02:18 +08:00
caiyiling 016a5e93ea 1
continuous-integration/drone/push Build is passing
2026-05-28 10:45:29 +08:00
caiyiling e4d731222b 1
continuous-integration/drone/push Build is passing
2026-05-27 17:55:00 +08:00
caiyiling aeeba1dfb7 1
continuous-integration/drone/push Build is passing
2026-05-27 15:37:55 +08:00
caiyiling d7ebed0b05 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-05-27 15:06:56 +08:00
caiyiling 312e85d9b3 融合视口标注绑定信息更改 2026-05-27 15:06:19 +08:00
wangxiaoshuang 3ee32c2e56 质控风险控制修改
continuous-integration/drone/push Build is passing
2026-05-27 09:43:18 +08:00
wangxiaoshuang 2778b34f14 1
continuous-integration/drone/push Build is passing
2026-05-26 17:52:27 +08:00
wangxiaoshuang 6736121309 1 2026-05-26 14:10:50 +08:00
wangxiaoshuang e48c00f817 影像质控时,可以下载阅片影像
continuous-integration/drone/push Build is pending
2026-05-26 13:45:06 +08:00
wangxiaoshuang e5f7ad3b31 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2026-05-26 10:40:01 +08:00
wangxiaoshuang b7a0a336dd 部分问题解决 2026-05-26 10:39:52 +08:00
caiyiling d83a086087 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is pending
2026-05-26 10:24:43 +08:00
caiyiling d03ce19b02 阅片页面多帧预览高亮更改 2026-05-26 10:24:16 +08:00
wangxiaoshuang a2668b39be 部分问题解决
continuous-integration/drone/push Build is passing
2026-05-26 10:19:37 +08:00
caiyiling a30fd58d79 多帧图像预览更改;数据同步检查列表补充排序字段
continuous-integration/drone/push Build is passing
2026-05-26 09:40:04 +08:00
caiyiling 31d0872653 SPECT测量更改 2026-05-25 13:26:06 +08:00
caiyiling e63e38222e Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-05-25 13:17:17 +08:00
caiyiling 65d7a95d5f 1 2026-05-25 13:17:04 +08:00
wangxiaoshuang 298619717a 项目配置修改
continuous-integration/drone/push Build is passing
2026-05-25 10:38:56 +08:00
caiyiling d6713ba9a4 circleROI测量数据更改
continuous-integration/drone/push Build is passing
2026-05-25 09:16:31 +08:00
caiyiling 0e252b0377 添加自定义MIPJumpToClickTool工具
continuous-integration/drone/push Build is passing
2026-05-22 17:58:08 +08:00
caiyiling 52ab454860 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-05-22 17:10:28 +08:00
caiyiling 0e2e57446b 预览访视影像样式更改;融合视口工具优化; 2026-05-22 17:09:47 +08:00
wangxiaoshuang 94b0354fe4 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2026-05-22 15:25:25 +08:00
wangxiaoshuang 93ed9943fa 中心调研修改 2026-05-22 15:25:21 +08:00
caiyiling e04e94a792 total数值绑定更改
continuous-integration/drone/push Build is passing
2026-05-22 13:57:58 +08:00
caiyiling b7c57c3c27 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-05-22 12:29:04 +08:00
caiyiling 015eb78b70 lugano阅片标准页面十字线工具更改 2026-05-22 12:28:28 +08:00
wangxiaoshuang 04580ed1bb 项目逻辑配置中增加:质控时,可下载影像、访视失访时,后续访视可阅片
continuous-integration/drone/push Build is passing
2026-05-21 15:31:54 +08:00
wangxiaoshuang b6877d4fe8 1
continuous-integration/drone/push Build is passing
2026-05-21 15:01:43 +08:00
wangxiaoshuang b53f566160 系统配置、中心调研配置
continuous-integration/drone/push Build is passing
2026-05-21 14:41:33 +08:00
caiyiling c767004a2d 1
continuous-integration/drone/push Build is running
2026-05-21 13:53:02 +08:00
caiyiling 3a546e6e89 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is running
2026-05-21 13:52:42 +08:00
caiyiling 6af2f0555e 十字线功能优化;MR图像显示默认使用图像的窗宽窗位; 2026-05-21 13:41:39 +08:00
wangxiaoshuang a82526e21d 中心调研配置切换设备调研时初始化采集参数
continuous-integration/drone/push Build is pending
2026-05-21 09:34:42 +08:00
wangxiaoshuang c65ad92d39 中心调研设备调研修改
continuous-integration/drone/push Build is passing
2026-05-20 17:41:08 +08:00
wangxiaoshuang 36d5661044 分割标记与表格问题绑定修改
continuous-integration/drone/push Build is passing
2026-05-20 14:58:38 +08:00
wangxiaoshuang feb94dc832 1
continuous-integration/drone/push Build is passing
2026-05-20 09:31:13 +08:00
wangxiaoshuang 756750783c Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web 2026-05-20 09:12:22 +08:00
wangxiaoshuang db3f7b4cde 分割标记与表格问题进行绑定 2026-05-20 09:12:19 +08:00
caiyiling a9d62cf28d 任务级别列表更改
continuous-integration/drone/push Build is passing
2026-05-20 09:01:43 +08:00
caiyiling 04014ab524 其他类型文件同步数据更改
continuous-integration/drone/push Build is passing
2026-05-19 15:26:06 +08:00
caiyiling 2a68b40875 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is pending
2026-05-19 13:32:40 +08:00
caiyiling 375a7e7071 1 2026-05-19 13:32:23 +08:00
wangxiaoshuang efc3b03392 分割部分功能按钮添加国际化
continuous-integration/drone/push Build is passing
2026-05-18 15:28:56 +08:00
wangxiaoshuang 730a375509 1
continuous-integration/drone/push Build is passing
2026-05-15 17:01:08 +08:00
wangxiaoshuang d34b4b796c 关于弹框修改
continuous-integration/drone/push Build is passing
2026-05-15 11:35:23 +08:00
wangxiaoshuang 427f060935 1
continuous-integration/drone/push Build is passing
2026-05-15 10:17:38 +08:00
wangxiaoshuang 6a10cb1afe Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2026-05-15 09:55:47 +08:00
wangxiaoshuang 962ac2e693 时区显示增加配置 2026-05-15 09:55:42 +08:00
caiyiling 3567f2d625 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-05-14 15:12:58 +08:00
caiyiling bd980cd867 1 2026-05-14 15:12:36 +08:00
wangxiaoshuang 284432ffa4 支持在个人中心修改工作语言
continuous-integration/drone/push Build is passing
2026-05-14 09:39:59 +08:00
wangxiaoshuang 16e2fbc8ff 1
continuous-integration/drone/push Build is passing
2026-05-13 16:22:20 +08:00
wangxiaoshuang ac6be424ee 视口为单视口且不在mpr、融合模式时双击放大视口功能屏蔽 2026-05-13 15:37:23 +08:00
wangxiaoshuang 351b2a627f 退出mpr模式后去除对mpr窗口监听
continuous-integration/drone/push Build is pending
2026-05-13 15:30:58 +08:00
caiyiling dd919f123e Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-05-13 14:07:49 +08:00
caiyiling 3a33abf02e 数据同步检查列表默认排序字段更改 2026-05-13 14:06:00 +08:00
wangxiaoshuang 917373e081 分割恢复提示语修改
continuous-integration/drone/push Build is passing
2026-05-13 11:23:55 +08:00
wangxiaoshuang 632463327c 多帧像素匿名和其他影像全序列应用匿名自动更新缩略图 2026-05-13 09:46:16 +08:00
wangxiaoshuang 59a14c0e37 多个分割分组恢复分割报错问题
continuous-integration/drone/push Build is passing
2026-05-13 09:36:52 +08:00
wangxiaoshuang 511640f66f 分割恢复时清除分割片段弹框
continuous-integration/drone/push Build is passing
2026-05-12 15:12:06 +08:00
wangxiaoshuang c26f5df102 国际多中心的项目,用户UI的语言匹配问题 2026-05-12 14:52:44 +08:00
caiyiling 98e9af71a3 解决Photometric Interpretation为YBR_FULL_422编码格式的dicom文件渲染异常问题
continuous-integration/drone/push Build is passing
2026-05-12 14:09:50 +08:00
wangxiaoshuang b59e0c12f6 1
continuous-integration/drone/push Build is passing
2026-05-12 11:27:56 +08:00
wangxiaoshuang fed2677689 分割分组重命名时传入文件大小
continuous-integration/drone/push Build is passing
2026-05-12 10:55:07 +08:00
wangxiaoshuang a55278e323 分割标记橡皮檫工具问题解决
continuous-integration/drone/push Build is passing
2026-05-12 10:29:35 +08:00
wangxiaoshuang 8f808d73b5 反馈修改
continuous-integration/drone/push Build is passing
2026-05-11 14:15:58 +08:00
wangxiaoshuang 717c5a6529 分割恢复问题解决
continuous-integration/drone/push Build is passing
2026-05-11 13:58:51 +08:00
wangxiaoshuang ec676d8d2b 数据匿名样式调整
continuous-integration/drone/push Build is passing
2026-05-11 13:26:03 +08:00
wangxiaoshuang 2ce14c6d72 热键s下载
continuous-integration/drone/push Build is passing
2026-05-11 11:17:03 +08:00
wangxiaoshuang 10b5bba0dd 分割恢复优化 2026-05-11 10:35:14 +08:00
wangxiaoshuang 159a36cd70 图标功能修改
continuous-integration/drone/push Build is pending
2026-05-11 10:30:27 +08:00
wangxiaoshuang 4a17946598 分割恢复部分问题解决
continuous-integration/drone/push Build is passing
2026-05-09 18:00:26 +08:00
wangxiaoshuang d6f36cbbda 数据匿名按钮样式修改
continuous-integration/drone/push Build is passing
2026-05-09 15:33:35 +08:00
wangxiaoshuang e6a15e35f6 看板页面的地图需要展示
continuous-integration/drone/push Build is passing
2026-05-09 14:33:15 +08:00
wangxiaoshuang 27a9ccd3c2 数据匿名多个编辑选框应用时清除
continuous-integration/drone/push Build is failing
2026-05-09 10:56:21 +08:00
wangxiaoshuang e8dda21459 数据匿名回复全部判断条件变更 2026-05-09 10:29:23 +08:00
wangxiaoshuang 7ebbfb9621 数据匿名应用时清除遮盖标记
continuous-integration/drone/push Build is passing
2026-05-09 09:45:16 +08:00
caiyiling 9da7c1a159 1
continuous-integration/drone/push Build is passing
2026-05-08 17:21:33 +08:00
caiyiling 197f17fc99 1
continuous-integration/drone/push Build is passing
2026-05-08 15:28:10 +08:00
caiyiling d680c57482 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main 2026-05-08 15:04:58 +08:00
caiyiling 7266d0a4f5 截图更改及长短径工具更改 2026-05-08 15:04:26 +08:00
wangxiaoshuang 3e19e85a8d 提示语添加限制条件
continuous-integration/drone/push Build is pending
2026-05-08 15:00:03 +08:00
caiyiling c9057e12cd 多帧影像切换bug修复
continuous-integration/drone/push Build is passing
2026-05-08 13:56:09 +08:00
caiyiling 10bac01371 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-05-08 11:39:40 +08:00
caiyiling 67d54aedb7 1 2026-05-08 11:39:30 +08:00
wangxiaoshuang 1a21aa38f3 下载、数据匿名问题修改
continuous-integration/drone/push Build is pending
2026-05-08 11:38:56 +08:00
caiyiling 64f0998896 样式调整
continuous-integration/drone/push Build is passing
2026-05-08 11:33:39 +08:00
caiyiling 04d9a53a0e 临床数据状态重置更改
continuous-integration/drone/push Build is passing
2026-05-08 10:22:06 +08:00
wangxiaoshuang fd0412004e Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2026-05-07 17:43:35 +08:00
wangxiaoshuang 64ef1e513c 影像匿名多帧优化 2026-05-07 17:43:28 +08:00
caiyiling a83349387a 注射时间、成像时间输入添加限制
continuous-integration/drone/push Build is passing
2026-05-07 17:23:53 +08:00
caiyiling 9500842628 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-05-07 16:41:17 +08:00
caiyiling 998e340919 临床数据更新状态按钮功能更改 2026-05-07 16:40:33 +08:00
wangxiaoshuang 753127381f 1
continuous-integration/drone/push Build is passing
2026-05-07 16:05:42 +08:00
wangxiaoshuang a5c3dac047 下载失败跳过进入下一个任务 2026-05-07 16:01:09 +08:00
wangxiaoshuang a7337a347b Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web 2026-05-07 15:20:18 +08:00
wangxiaoshuang 85b88d4564 下载文件 2026-05-07 15:20:13 +08:00
wangxiaoshuang 763e00a6ca 分割数据恢复 2026-05-07 15:20:01 +08:00
caiyiling 6a345e35cd 表格列宽调整
continuous-integration/drone/push Build was killed
2026-05-07 13:59:31 +08:00
caiyiling 09e9c62f3c pt临床数据弹窗取消点击modal关闭Dialog
continuous-integration/drone/push Build is passing
2026-05-07 13:52:28 +08:00
caiyiling af97e2b4ed crc修改pt临床数据更改
continuous-integration/drone/push Build is passing
2026-05-07 12:16:01 +08:00
caiyiling e0dd443d84 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-05-07 11:10:45 +08:00
caiyiling 1c0cdde9ad 质控临床数据更改 2026-05-07 11:08:57 +08:00
wangxiaoshuang e100d45bc1 文件上传信息获取
continuous-integration/drone/push Build is passing
2026-05-07 10:20:53 +08:00
wangxiaoshuang 49c7482d76 匿名数据部分问题解决
continuous-integration/drone/push Build is pending
2026-05-06 17:55:11 +08:00
wangxiaoshuang 22b77ff70f Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2026-05-06 17:01:43 +08:00
wangxiaoshuang 3a35f4a966 匿名数据部分问题解决 2026-05-06 17:01:31 +08:00
caiyiling 8ff9e3b232 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-05-06 16:11:35 +08:00
caiyiling e189080e5c 样式更改 2026-05-06 16:11:18 +08:00
wangxiaoshuang 07b1e5d1f9 匿名失败进行提示
continuous-integration/drone/push Build is passing
2026-05-06 15:32:51 +08:00
wangxiaoshuang 2f92bf6dfb pet、mr分割视图重置调窗问题
continuous-integration/drone/push Build is passing
2026-05-06 15:01:47 +08:00
wangxiaoshuang 391e696d8a Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2026-05-06 14:38:30 +08:00
wangxiaoshuang 9ff7928b16 数据匿名更改 2026-05-06 14:38:26 +08:00
caiyiling 3c0971e961 国际化排序更改
continuous-integration/drone/push Build is running
2026-05-06 14:33:15 +08:00
wangxiaoshuang 1f20834dd3 个性化配置热键tab,高度加高一屏展示所有热键
continuous-integration/drone/push Build is passing
2026-05-06 13:35:00 +08:00
wangxiaoshuang f34665fcb9 体积分割对象的统计值,Count应该为整型
continuous-integration/drone/push Build is running
2026-05-06 13:32:33 +08:00
wangxiaoshuang 00e03c782d Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2026-05-06 13:28:04 +08:00
wangxiaoshuang 5e6c8e6936 AB视口,A视口放大后进入mpr,只显示了一个视口,需要显示mpr的全部视口 2026-05-06 13:27:53 +08:00
wangxiaoshuang cd2c56c510 适应窗口调整后,影像页数应该不变 2026-05-06 13:21:49 +08:00
caiyiling ec7cb78018 lugano标准融合修复colormap bar
continuous-integration/drone/push Build is passing
2026-05-06 13:21:45 +08:00
caiyiling 6a65b6507f 1
continuous-integration/drone/push Build is passing
2026-05-06 11:38:18 +08:00
caiyiling d08df877d7 样式更改
continuous-integration/drone/push Build is passing
2026-05-06 11:23:57 +08:00
caiyiling 10a43d4c04 自动添加肿瘤学阅片期bug修复
continuous-integration/drone/push Build is passing
2026-05-06 10:23:56 +08:00
wangxiaoshuang a87769a60f 1
continuous-integration/drone/push Build is passing
2026-04-30 17:31:34 +08:00
wangxiaoshuang 4d808ed87c 历史访视分割组显示按钮
continuous-integration/drone/push Build is passing
2026-04-30 17:08:15 +08:00
wangxiaoshuang 0a3a4e3bb4 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2026-04-30 17:03:19 +08:00
wangxiaoshuang 0aab18924b 切换序列提示保存分割 2026-04-30 17:03:06 +08:00
caiyiling 2bf9b21f40 1
continuous-integration/drone/push Build is passing
2026-04-30 16:54:34 +08:00
caiyiling 06d0ce2290 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is running
2026-04-30 16:45:03 +08:00
caiyiling 1b6e53819c 融合页面工具优化 2026-04-30 16:44:00 +08:00
wangxiaoshuang a59509dd7d 新增分割片段名规则变更
continuous-integration/drone/push Build is passing
2026-04-30 16:26:14 +08:00
wangxiaoshuang d88f7c43b2 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
# Conflicts:
#	src/views/trials/trials-panel/reading/dicoms3D/components/Segmentations.vue
2026-04-30 16:01:34 +08:00
wangxiaoshuang c3543e19c8 1 2026-04-30 15:58:54 +08:00
caiyiling 72b1a3906d Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is running
2026-04-30 15:58:45 +08:00
caiyiling 06794447ec 阅片页面pt数据,放在检查级别显示 2026-04-30 15:51:57 +08:00
wangxiaoshuang 00c5570692 分割长短经按钮修改
continuous-integration/drone/push Build is passing
2026-04-30 15:48:18 +08:00
wangxiaoshuang cd2df6e576 分割标记缓存问题
continuous-integration/drone/push Build is passing
2026-04-30 15:24:33 +08:00
wangxiaoshuang 52f9c17234 1
continuous-integration/drone/push Build is passing
2026-04-30 15:15:05 +08:00
wangxiaoshuang 0b78561331 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2026-04-30 14:54:21 +08:00
wangxiaoshuang c12da4de3e 分割部分问题解决 2026-04-30 14:54:16 +08:00
caiyiling 4f16060a46 样式未隔离导致阅片人编辑页,右侧排版错位
continuous-integration/drone/push Build is passing
2026-04-30 14:35:17 +08:00
caiyiling 212bd65bc1 阅片期临床数据添加更新功能
continuous-integration/drone/push Build is passing
2026-04-30 14:25:48 +08:00
wangxiaoshuang afc35daffa Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web 2026-04-30 13:41:48 +08:00
caiyiling 5bf8e7ecf6 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-04-30 13:36:04 +08:00
caiyiling 192f7a3316 自动增加肿瘤学阅片期 2026-04-30 13:35:52 +08:00
wangxiaoshuang d88535b75e 【dicom分割】有的影像无法渲染,需要增加提示 2026-04-30 11:46:57 +08:00
wangxiaoshuang 128cdc0f75 【dicom分割】直方图x轴坐标应该是从小到大,现在坐标数显示异常 2026-04-30 11:23:31 +08:00
wangxiaoshuang 4a8b1da094 部分问题修复
continuous-integration/drone/push Build is passing
2026-04-30 09:12:03 +08:00
wangxiaoshuang 728f3238ca 防止内存泄漏
continuous-integration/drone/push Build is passing
2026-04-29 17:35:25 +08:00
wangxiaoshuang e245057bb2 切换视口快速提交监测 2026-04-29 17:34:12 +08:00
wangxiaoshuang 7bf80c10eb 切换视口重置分割分组和分割片段
continuous-integration/drone/push Build is passing
2026-04-29 16:58:41 +08:00
wangxiaoshuang ecf4c8a2c7 1
continuous-integration/drone/push Build is passing
2026-04-29 16:17:49 +08:00
wangxiaoshuang c532babf98 视口变动时重新选中当前选择分割
continuous-integration/drone/push Build is passing
2026-04-29 15:56:18 +08:00
wangxiaoshuang 2fa54eaac6 保存分割时去除同步提示
continuous-integration/drone/push Build is passing
2026-04-29 15:42:08 +08:00
wangxiaoshuang 50de867772 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2026-04-29 15:04:52 +08:00
wangxiaoshuang 24a48e3d23 切换ab视图时ecrf表单未刷新 2026-04-29 15:04:47 +08:00
caiyiling 13429f45ed 1
continuous-integration/drone/push Build is passing
2026-04-29 14:34:11 +08:00
caiyiling 8545e8f1d6 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-04-29 14:15:59 +08:00
caiyiling 619a7b2ee3 质控通过后编辑临床数据信息更改 2026-04-29 14:15:33 +08:00
wangxiaoshuang 544f83d118 1
continuous-integration/drone/push Build is passing
2026-04-29 14:07:05 +08:00
wangxiaoshuang 1541e3d353 退出MPR时ecrf表显示不正确 2026-04-29 14:06:55 +08:00
wangxiaoshuang 7a454d09ff Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2026-04-29 13:42:20 +08:00
wangxiaoshuang 06f52f4f24 分割数据不一致问题 2026-04-29 13:42:16 +08:00
caiyiling 877e47ccb1 阅片规则单位更改
continuous-integration/drone/push Build is passing
2026-04-29 13:30:42 +08:00
caiyiling c373b59f23 质控通过时pt临床数据可再次修改
continuous-integration/drone/push Build is passing
2026-04-29 12:41:00 +08:00
caiyiling 73e8f24c3d 阅片页面图标更改;适应图像逻辑更改;petct工具更改
continuous-integration/drone/push Build is passing
2026-04-29 11:48:21 +08:00
wangxiaoshuang 1e57777d3b Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2026-04-28 15:41:13 +08:00
wangxiaoshuang aca7f07d00 分割列表样式调整 2026-04-28 15:41:09 +08:00
caiyiling 35e52fbdcf 融合标记跳转更改
continuous-integration/drone/push Build is passing
2026-04-28 15:33:51 +08:00
wangxiaoshuang aaeadbc440 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2026-04-28 15:08:49 +08:00
wangxiaoshuang 60d3dfa6e7 1 2026-04-28 15:08:45 +08:00
caiyiling 417fefb04b 1
continuous-integration/drone/push Build is passing
2026-04-28 14:13:51 +08:00
caiyiling 27646e2c46 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is running
2026-04-28 14:07:44 +08:00
wangxiaoshuang 48b154bf87 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is running
2026-04-28 14:07:09 +08:00
wangxiaoshuang acfa552cbe mpr十字线颜色更改 2026-04-28 14:07:05 +08:00
caiyiling aa6a226ce1 添加日志 2026-04-28 14:06:30 +08:00
caiyiling 09144f2b98 1
continuous-integration/drone/push Build is passing
2026-04-28 13:59:43 +08:00
caiyiling 9b8c6788bf Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-04-28 13:39:52 +08:00
caiyiling 4aa1a05ea9 定位工具更改 2026-04-28 13:39:18 +08:00
wangxiaoshuang 45790141df 分割导出按钮宽度调整
continuous-integration/drone/push Build is passing
2026-04-28 13:33:15 +08:00
caiyiling 6804d909b8 more图标title更改
continuous-integration/drone/push Build is passing
2026-04-28 13:23:26 +08:00
caiyiling 1a1bf6810c Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-04-28 13:13:30 +08:00
caiyiling c9e7719c71 测量工具图标更改 2026-04-28 13:13:08 +08:00
wangxiaoshuang 07b23fce1d Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2026-04-28 11:44:34 +08:00
wangxiaoshuang 9f5cbad22e 分割及mpr部分问题解决 2026-04-28 11:44:30 +08:00
caiyiling 3cf4f95770 1
continuous-integration/drone/push Build is passing
2026-04-28 11:23:49 +08:00
caiyiling 5a01f10957 1
continuous-integration/drone/push Build is passing
2026-04-28 11:16:55 +08:00
caiyiling 4ed0ebc913 数据同步页面国际化
continuous-integration/drone/push Build is passing
2026-04-28 10:18:31 +08:00
caiyiling 9971c3ecca Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-04-28 09:15:22 +08:00
caiyiling 3c10ae73dd 融合视口十字线定位工具更改 2026-04-28 09:14:54 +08:00
wangxiaoshuang 5cbd13cc3d 1
continuous-integration/drone/push Build is passing
2026-04-27 16:14:43 +08:00
wangxiaoshuang 7dcc524bd3 切换分割与ecrf表时显示全部分割标记
continuous-integration/drone/push Build is passing
2026-04-27 15:37:59 +08:00
wangxiaoshuang bc4639a238 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2026-04-27 13:12:39 +08:00
wangxiaoshuang 0f25958d9b 分割部分问题解决 2026-04-27 13:12:35 +08:00
caiyiling ff0d204612 融合视口新增定位工具
continuous-integration/drone/push Build is passing
2026-04-27 11:49:07 +08:00
wangxiaoshuang 91386589ae 删除分组后,会出现分段的气泡窗口
continuous-integration/drone/push Build is passing
2026-04-27 11:16:03 +08:00
wangxiaoshuang b2a9f279a1 窗口联动关闭 2026-04-27 11:15:49 +08:00
wangxiaoshuang b4fc5d970f 项目列表查询条件
continuous-integration/drone/push Build is passing
2026-04-24 17:06:36 +08:00
wangxiaoshuang e046bffdbd EICS关于页面 2026-04-24 16:55:14 +08:00
wangxiaoshuang 38ea9ddb0b Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2026-04-24 16:49:46 +08:00
wangxiaoshuang 1194e56139 对于问题反馈列表增加的状态增加:无法解决。 2026-04-24 16:49:35 +08:00
caiyiling 741c723bab Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-04-24 16:10:51 +08:00
caiyiling fea0047f58 数据同步更改 2026-04-24 16:10:35 +08:00
wangxiaoshuang 5a943fbefe 分割问题解决
continuous-integration/drone/push Build is passing
2026-04-24 16:03:23 +08:00
caiyiling 6c88af0a03 质控更改
continuous-integration/drone/push Build is passing
2026-04-24 15:52:07 +08:00
caiyiling 26412931a9 样式冲突更改
continuous-integration/drone/push Build is passing
2026-04-24 14:49:32 +08:00
wangxiaoshuang 5a1b64a023 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
# Conflicts:
#	src/components/Dicom/DicomViewer.vue
2026-04-24 14:41:39 +08:00
wangxiaoshuang 64d8c853b3 质控像素匿名 2026-04-24 14:37:36 +08:00
caiyiling 0261fb04c6 1
continuous-integration/drone/push Build is passing
2026-04-24 14:33:16 +08:00
caiyiling ab372a9c29 PT临床数据更改
continuous-integration/drone/push Build is running
2026-04-24 14:30:36 +08:00
caiyiling f7dd68f7f3 pt临床数据更改
continuous-integration/drone/push Build is passing
2026-04-23 16:32:25 +08:00
wangxiaoshuang 946203f124 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
# Conflicts:
#	src/components/Dicom/DicomViewer.vue
#	src/views/dicom-show/dicom-study.vue
2026-04-23 15:53:04 +08:00
wangxiaoshuang f0eb814492 质控数据匿名 2026-04-23 15:50:15 +08:00
caiyiling 6c36c48b82 自动切换任务更改;PT序列支持修改检查的患者数据
continuous-integration/drone/push Build is passing
2026-04-23 11:39:07 +08:00
wangxiaoshuang 47b05526a2 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web 2026-04-22 15:06:47 +08:00
wangxiaoshuang f7afca6e73 注释(未完成) 2026-04-22 15:06:39 +08:00
caiyiling 04aa61658c Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-04-22 13:41:44 +08:00
caiyiling 86c506bc1d 非dicom阅片页面快捷键功能补充 2026-04-22 13:41:03 +08:00
wangxiaoshuang 06959b4465 双屏问题修复
continuous-integration/drone/push Build is running
2026-04-22 13:37:57 +08:00
caiyiling 838ab975cc Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-04-22 11:09:56 +08:00
caiyiling 7aa33672a9 质控更改 2026-04-22 11:08:48 +08:00
wangxiaoshuang 81297954d3 查看历史访视黑屏问题
continuous-integration/drone/push Build is passing
2026-04-22 11:01:12 +08:00
wangxiaoshuang 25e348f428 上传问题解决
continuous-integration/drone/push Build is passing
2026-04-22 10:15:08 +08:00
caiyiling c101302cca 指控添加跳过功能
continuous-integration/drone/push Build is passing
2026-04-21 17:04:57 +08:00
caiyiling c4fd8c3ea3 融合视口crosshairsTool更改;数据同步页面更改;项目中培训文档路径更改
continuous-integration/drone/push Build is passing
2026-04-21 16:49:40 +08:00
caiyiling a7e7419a20 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is failing
2026-04-21 13:16:14 +08:00
caiyiling 55da1d9219 数据同步 2026-04-21 13:15:23 +08:00
wangxiaoshuang 2a842fd2f8 上传影像添加字段
continuous-integration/drone/push Build is running
2026-04-21 13:15:18 +08:00
caiyiling ca2e9e0253 数据同步列表更改
continuous-integration/drone/push Build is passing
2026-04-21 11:14:00 +08:00
caiyiling b4c06990f6 审批信息补充
continuous-integration/drone/push Build is passing
2026-04-21 10:17:49 +08:00
caiyiling 599fad48ba Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-04-21 09:33:26 +08:00
caiyiling 72b5d515ba 融合页面crosshairsTool更改 2026-04-21 09:32:41 +08:00
wangxiaoshuang 608bd42836 查看历史访视在新窗口打开
continuous-integration/drone/push Build is passing
2026-04-20 17:59:45 +08:00
caiyiling 040f66f182 定圆工具更改及国际化全局替换IRC逻辑更改
continuous-integration/drone/push Build is passing
2026-04-20 15:20:46 +08:00
wangxiaoshuang 7afbc360e5 新增的分段后,点击计算长短径保存,刷新页面后计算值全部变为0
continuous-integration/drone/push Build is passing
2026-04-17 15:52:55 +08:00
wangxiaoshuang 74c5d032fb 分割清除内存问题
continuous-integration/drone/push Build is passing
2026-04-17 11:49:00 +08:00
wangxiaoshuang 43aa371c36 分段显隐时对应长短径标记同步
continuous-integration/drone/push Build is passing
2026-04-17 11:33:35 +08:00
wangxiaoshuang 398ec90fae 分割问题查看、更改按钮位置变更
continuous-integration/drone/push Build is passing
2026-04-17 11:28:34 +08:00
wangxiaoshuang 890910a763 分割部分问题修复
continuous-integration/drone/push Build is passing
2026-04-17 11:17:25 +08:00
wangxiaoshuang 3b5ebfcba1 1
continuous-integration/drone/push Build is passing
2026-04-17 10:00:31 +08:00
wangxiaoshuang b971235164 体数据播放功能修改
continuous-integration/drone/push Build is passing
2026-04-16 17:55:33 +08:00
wangxiaoshuang e53a18f212 直方图修改
continuous-integration/drone/push Build is passing
2026-04-16 16:00:06 +08:00
wangxiaoshuang 774f43a6d0 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2026-04-16 15:22:03 +08:00
wangxiaoshuang 88f274ab89 分割部分问题修复 2026-04-16 15:21:53 +08:00
caiyiling 3345a989ae 定圆工具默认值更改
continuous-integration/drone/push Build is running
2026-04-16 15:19:37 +08:00
wangxiaoshuang 11000ef9f8 重置问题时清空问题与分割标记绑定关系
continuous-integration/drone/push Build is passing
2026-04-16 13:42:49 +08:00
wangxiaoshuang 57be74aea8 部分分割问题修复
continuous-integration/drone/push Build is running
2026-04-16 13:39:52 +08:00
wangxiaoshuang 8724eeeafb 分割部分问题解决
continuous-integration/drone/push Build is passing
2026-04-16 11:32:48 +08:00
caiyiling 7c71a6d342 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2026-04-16 10:03:46 +08:00
caiyiling cb04be1b56 融合视口添加crosshairsTool工具 2026-04-16 10:02:06 +08:00
wangxiaoshuang 63709b414d 阅片单元分割组配置修改
continuous-integration/drone/push Build is passing
2026-04-16 09:41:23 +08:00
wangxiaoshuang 7001bc9ec0 分割部分问题解决
continuous-integration/drone/push Build is passing
2026-04-15 17:21:19 +08:00
wangxiaoshuang c12833d283 切换分割分组时添加限制
continuous-integration/drone/push Build is passing
2026-04-15 16:48:38 +08:00
wangxiaoshuang 22baa213ae 新建若干分段后保存,保存卡死
continuous-integration/drone/push Build is passing
2026-04-15 16:00:49 +08:00
wangxiaoshuang 09c1c3ff12 分割部分问题修复
continuous-integration/drone/push Build is passing
2026-04-15 15:00:13 +08:00
wangxiaoshuang 94f43f4b11 用橡皮擦除分割标记后,绑定的分割问题显示为-Infinity
continuous-integration/drone/push Build is passing
2026-04-15 14:24:49 +08:00
wangxiaoshuang 1a42cfd47c Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is running
2026-04-15 14:22:59 +08:00
wangxiaoshuang b6b918efbf 创建分割分组时,分组和分割分段的名称可以设置默认值、部分分割问题解决 2026-04-15 14:22:50 +08:00
caiyiling a16d86d942 数据同步列表更改
continuous-integration/drone/push Build is passing
2026-04-14 21:54:27 +08:00
caiyiling 266698cc79 定圆工具配置及功能实现
continuous-integration/drone/push Build is passing
2026-04-14 21:19:00 +08:00
wangxiaoshuang eac6b25b9e 分割部分问题修复
continuous-integration/drone/push Build is passing
2026-04-14 18:01:21 +08:00
255 changed files with 25783 additions and 10788 deletions
+787 -901
View File
File diff suppressed because it is too large Load Diff
+5 -2
View File
@@ -9,6 +9,7 @@
"build:usa": "vue-cli-service build --mode usa",
"build:usa_prod": "vue-cli-service build --mode usa_prod",
"pre": "vue-cli-service build --mode pre",
"postinstall": "patch-package",
"i18n": "npm run i18n:zh && npm run i18n:en",
"i18n:zh": "node i18nGenerate.js lang=zh keyCol=5 valCol=6",
"i18n:en": "node i18nGenerate.js lang=en keyCol=5 valCol=7"
@@ -16,10 +17,10 @@
"dependencies": {
"@aws-sdk/client-s3": "3.726.1",
"@cornerstonejs/adapters": "^4.19.2",
"@cornerstonejs/polymorphic-segmentation": "4.19.2",
"@cornerstonejs/calculate-suv": "^1.1.0",
"@cornerstonejs/core": "^4.19.2",
"@cornerstonejs/dicom-image-loader": "^4.19.2",
"@cornerstonejs/polymorphic-segmentation": "^4.19.2",
"@cornerstonejs/tools": "^4.19.2",
"@fingerprintjs/fingerprintjs": "^4.6.2",
"@icr/polyseg-wasm": "^0.4.0",
@@ -39,6 +40,7 @@
"dicom-parser": "^1.8.9",
"dicomedit": "^0.1.0",
"echarts": "^6.0.0",
"echarts-map": "^3.0.1",
"element-ui": "^2.15.14",
"exceljs": "^4.4.0",
"file-saver": "^2.0.5",
@@ -94,6 +96,7 @@
"html-webpack-plugin": "^5.6.3",
"mini-css-extract-plugin": "^2.9.2",
"node-polyfill-webpack-plugin": "^4.0.0",
"patch-package": "^8.0.0",
"path-browserify": "^1.0.1",
"process": "^0.11.10",
"sass": "^1.63.2",
@@ -125,4 +128,4 @@
"not dead",
"not op_mini all"
]
}
}
@@ -0,0 +1,16 @@
diff --git a/node_modules/@cornerstonejs/dicom-image-loader/dist/esm/imageLoader/isColorConversionRequired.js b/node_modules/@cornerstonejs/dicom-image-loader/dist/esm/imageLoader/isColorConversionRequired.js
index 33469a3..f39736f 100644
--- a/node_modules/@cornerstonejs/dicom-image-loader/dist/esm/imageLoader/isColorConversionRequired.js
+++ b/node_modules/@cornerstonejs/dicom-image-loader/dist/esm/imageLoader/isColorConversionRequired.js
@@ -14,10 +14,7 @@ export default function isColorConversionRequired(imageFrame) {
(3 * Math.ceil(columns / 2) + Math.floor(columns / 2)) * rows);
}
else if (photometricInterpretation.endsWith('422')) {
- return (pixelDataLength ===
- (3 * Math.ceil(columns / 2) + Math.floor(columns / 2)) *
- Math.ceil(rows / 2) +
- Math.floor(rows / 2) * columns);
+ return pixelDataLength === rows * columns * 2;
}
else {
return photometricInterpretation !== 'RGB' || planarConfiguration === 1;
+3
View File
@@ -0,0 +1,3 @@
window.config = {
timeZone: false
}
+1
View File
@@ -67,6 +67,7 @@
num = e.data
}
</script>
<script src="/config.js"></script>
</head>
<body>
<noscript>
+51 -13
View File
@@ -1,5 +1,5 @@
<template>
<div id="app" style="position: relative">
<div id="app" style="position: relative" :class="{ mask: isLock }">
<router-view />
<div v-show="show" v-if="$route.matched.length > 0" v-adaptive @click="openI18n" style="
position: fixed;
@@ -63,7 +63,7 @@
" size="mini"></el-input>
</template>
</el-table-column>
<el-table-column prop="ValueCN" :label="$t('il8n:table:state')" sortable="custom">
<el-table-column prop="State" :label="$t('il8n:table:state')" sortable="custom">
<template slot-scope="scope">
<el-select v-model="scope.row.State" clearable filterable size="mini">
<el-option v-for="item of $d.InternationalizationKeyState"
@@ -83,7 +83,7 @@
</div>
</el-drawer>
<feedBack v-if="$route.matched.length > 0" />
<!-- <timeTag /> -->
<timeTag v-if="config.timeZone" />
</div>
</template>
@@ -94,14 +94,16 @@ import {
} from '@/api/dictionary/dictionary'
import { getTrialExtralConfig } from '@/api/trials'
import feedBack from '@/views/trials/trials-layout/components/feedBack'
// import timeTag from '@/components/timeTag'
import timeTag from '@/components/timeTag'
import Vue from 'vue'
import i18n from './lang'
import DicomEvent from '@/views/trials/trials-panel/reading/dicoms/components/DicomEvent'
import WHITELIST from "./utils/whiteList"
export default {
name: 'App',
components: {
feedBack,
// timeTag
timeTag
},
data() {
return {
@@ -112,11 +114,17 @@ export default {
arr: [],
il8nExternal: false,
State: null,
config: window.config,
isLock: false
}
},
mounted() {
this.show = process.env.VUE_APP_OSS_PATH === '/test/dist'
Vue.prototype.$openI18n = this.openI18n
this.getIsLock()
DicomEvent.$on("isLock", (isLock) => {
this.isLock = isLock
})
},
// watch: {
// '$route.query': {
@@ -148,17 +156,30 @@ export default {
// },
// },
methods: {
getIsLock() {
if (zzSessionStorage.getItem('isLock') === 'true' && !WHITELIST.includes(this.$route.path)) {
this.isLock = true
} else {
this.isLock = false
}
},
// 排序
handleSortByColumn(column) {
if (column.order === 'ascending') {
this.tableData.sort((a, b) =>
a[column.prop].localeCompare(b[column.prop])
)
} else {
this.tableData.sort((a, b) =>
b[column.prop].localeCompare(a[column.prop])
)
const { prop, order } = column
if (!prop || !order) {
this.tableData = [...this.tableData]
return
}
const collator = new Intl.Collator('zh-Hans-CN', {
numeric: true,
sensitivity: 'base',
})
this.tableData.sort((a, b) => {
const result = collator.compare(a[prop], b[prop])
return order === 'ascending' ? result : -result
})
},
handleStateChange() {
this.tableData.forEach((item) => {
@@ -300,6 +321,23 @@ export default {
</script>
<style lang="scss">
.mask {
position: relative;
filter: blur(5px);
user-select: none;
}
.mask::after {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
content: '';
display: block;
background: rgba(255, 253, 253, 0.5);
}
$light_gray: #606266;
.el-tooltip__popper {
+32
View File
@@ -391,3 +391,35 @@ export function getReSendEmail(data) {
data
})
}
// 获取系统基础配置
export function getSystemBasicConfigInfo() {
return request({
url: `/DeployConfig/getSystemBasicConfigInfo`,
method: 'get',
})
}
// 更新系统基础配置
export function updateSystemBasicConfig(data) {
return request({
url: `/DeployConfig/updateSystemBasicConfig`,
method: 'post',
data
})
}
// 获取系统邮件配置
export function getEmailConfigInfo() {
return request({
url: `/DeployConfig/getEmailConfigInfo`,
method: 'get',
})
}
// 更新系统邮件配置
export function updateSystemEmailConfig(data) {
return request({
url: `/DeployConfig/updateSystemEmailConfig`,
method: 'post',
data
})
}
+102 -1
View File
@@ -407,4 +407,105 @@ export function changeSegmentationSavedStatus(data) {
method: 'post',
data
})
}
}
// 修改分割片段锁定状态
export function lockOrUnLockSegment(data) {
return request({
url: `/Segmentation/lockOrUnLockSegment`,
method: 'post',
data
})
}
// 图像数据匿名
export function studyMaskImage(data) {
return request({
url: `/Study/studyMaskImage`,
method: 'post',
data
})
}
// 撤销匿名
export function studyUndoMaskImage(data) {
return request({
url: `/Study/studyUndoMaskImage`,
method: 'post',
data
})
}
// 非dicom图像数据匿名
export function noneDicomStudyMaskImage(data) {
return request({
url: `/NoneDicomStudy/noneDicomStudyMaskImage`,
method: 'post',
data
})
}
// 非dicom图像数据撤销匿名
export function noneDicomStudyUndoMaskImage(data) {
return request({
url: `/NoneDicomStudy/noneDicomStudyUndoMaskImage`,
method: 'post',
data
})
}
// 获取分割组历史版本
export function getSegmentationVersionList(data) {
return request({
url: `/Segmentation/getSegmentationVersionList`,
method: 'post',
data
})
}
// 恢复分割历史版本
export function restoreSegmentationVersion(data) {
return request({
url: `/Segmentation/restoreSegmentationVersion`,
method: 'post',
data
})
}
// 获取标记信息
export function getMarkList(data) {
return request({
url: `/ReadingImageTask/getMarkList`,
method: 'post',
data
})
}
export function getNoneDicomMarkList(data) {
return request({
url: `/ReadingImageTask/getNoneDicomMarkList`,
method: 'post',
data
})
}
export function getFilterTableQuestion(data) {
return request({
url: `/ReadingImageTask/getFilterTableQuestion`,
method: 'post',
data
})
}
export function deleteImageCache(params) {
return request({
url: `/Study/deleteImageCache`,
method: 'delete',
params
})
}
export function getAnonymizeInfo(params) {
return request({
url: `/Study/getAnonymizeInfo`,
method: 'get',
params
})
}
export function updateModality(params) {
return request({
url: `/Study/updateModality`,
method: 'put',
params
})
}
+115 -3
View File
@@ -357,10 +357,11 @@ export function getSubjectList(param) {
})
}
export function getTrialSiteSelect(trialId) {
export function getTrialSiteSelect(trialId, params) {
return request({
url: `/trialMaintenance/getTrialSiteSelect/${trialId}`,
method: 'get'
method: 'get',
params
})
}
@@ -4430,4 +4431,115 @@ export function updateImageResizePath(data) {
method: 'post',
data
})
}
}
// 获取PET图像上患者信息
export function getPatientInfo(data) {
return request({
url: `/Study/getPatientInfo`,
method: 'post',
data
})
}
//编辑患者基本信息
export function editPatientInfo(data) {
return request({
url: `/Study/editPatientInfo`,
method: 'post',
data
})
}
//修正患者基本信息
export function amendmentPatientInfo(data) {
return request({
url: `/Inspection/Study/amendmentPatientInfo`,
method: 'post',
data
})
}
//修改临床数据后 将签名状态变更为未签名
export function updateReadModuleClinicalData(data) {
return request({
url: `/ReadingClinicalData/updateReadModuleClinicalData`,
method: 'post',
data
})
}
export function getTrialUnreadVisitList(params) {
return request({
url: `/DownloadAndUpload/getTrialUnreadVisitList`,
method: 'get',
params,
})
}
export function getDoctorUserTrialReadingStat(data) {
return request({
url: `/DoctorWorkload/getDoctorUserTrialReadingStat`,
method: 'post',
data,
})
}
// 设置项目链接过期时间
export function setTrialLinkExpirationTime(data) {
return request({
url: `/TrialConfig/setTrialLinkExpirationTime`,
method: 'post',
data,
})
}
// 获取项目链接过期时间
export function getTrialLinkExpirationTime(data) {
return request({
url: `/TrialConfig/getTrialLinkExpirationTime`,
method: 'post',
data,
})
}
// 获取项目链接时间是否过期
export function getLinkLinkExpirationTime(data) {
return request({
url: `/TrialConfig/getLinkLinkExpirationTime`,
method: 'post',
data,
})
}
// 获取项目链接code是否有效
export function getLinkVerificationCodeIsEffective(data) {
return request({
url: `/TrialConfig/getLinkVerificationCodeIsEffective`,
method: 'post',
data,
})
}
// 上传通用文件记录
export function addOrUpdateCommonUploadRecord(data) {
return request({
url: `/FileUploadRecord/addOrUpdateCommonUploadRecord`,
method: 'post',
data,
})
}
export function getInspectionById(data) {
return request({
url: `/Inspection/getInspectionById`,
method: 'post',
data,
})
}
export function setJsonDetail(data) {
return request({
url: `/Inspection/setJsonDetail`,
method: 'post',
data,
})
}
+266 -84
View File
@@ -6,6 +6,7 @@
<div>Series #{{ dicomInfo.series }}</div>
<div>Image #{{ dicomInfo.frame }}</div>
<div>{{ dicomInfo.modality }}</div>
<div v-if="isComparison" style="font-size: 30px;color: #428bca;">{{ tip }}</div>
</div>
<div v-show="dicomInfo.series" class="info-image">
<!-- <div>
@@ -15,7 +16,7 @@
<div v-show="dicomInfo.thick">Slice Thickness {{ dicomInfo.thick }}mm</div>
<div>WW/WC {{ dicomInfo.wwwc }}</div>
<div>Zoom {{ dicomInfo.zoom }}</div>
<div v-show="dicomInfo.location">Location {{ dicomInfo.location }}mm</div> -->
<div v-show="dicomInfo.location">Location {{ dicomInfo.location }}mm</div>-->
<!-- <div v-show="toolState.clipPlaying">FPS {{ dicomInfo.fps }}</div> -->
<div v-show="mousePosition.mo">
Pos: {{ mousePosition.x ? mousePosition.x.toFixed(0) : '' }}, {{ mousePosition.y ? mousePosition.y.toFixed(0) :
@@ -23,24 +24,18 @@
</div>
<div
v-if="(dicomInfo.modality === 'CT' || dicomInfo.modality === 'DR' || dicomInfo.modality === 'CR') && mousePosition.mo">
HU: {{ mousePosition.mo }}
</div>
<div v-else-if="(dicomInfo.modality === 'PT' && mousePosition.suv)">
SUVbw(g/ml): {{ mousePosition.suv.toFixed(3) }}
</div>
<div v-else-if="mousePosition.mo">
Density: {{ mousePosition.mo }}
</div>
<div>
W*H: {{ dicomInfo.size }}
</div>
HU: {{ mousePosition.mo }}</div>
<div v-else-if="(dicomInfo.modality === 'PT' && mousePosition.suv)">SUVbw(g/ml): {{ mousePosition.suv.toFixed(3)
}}</div>
<div v-else-if="mousePosition.mo">Density: {{ mousePosition.mo }}</div>
<div>W*H: {{ dicomInfo.size }}</div>
<div>Zoom: {{ dicomInfo.zoom }}</div>
</div>
<div class="info-subject">
<div v-if="series.subjectCode">{{ series.subjectCode }}</div>
<div v-if="series.visitName">{{ series.visitName }}</div>
<div v-if="series.subjectCode && IsReadingTaskViewInOrder !== 0">{{ series.subjectCode }}</div>
<div v-if="series.visitName && IsReadingTaskViewInOrder !== 0">{{ series.visitName }}</div>
<div>{{ stack.description }}</div>
<!-- <div>{{ dicomInfo.hospital }}</div> -->
<!-- <div v-show="dicomInfo.pid">{{ dicomInfo.pid }}</div> -->
@@ -56,24 +51,17 @@
style="z-index:10;background: #9e9e9e;height: 20px;width: 100%;position: absolute;top: 0;cursor: move"
@mousedown="sliderMousedown($event)" />
</div>
<div style="position: absolute;left: 50%;top: 15px;color: #f44336;">
{{ markers.top }}
</div>
<div style="position: absolute;top: 50%;right: 15px;color: #f44336;">
{{ markers.right }}
</div>
<div style="position: absolute;left: 50%;top: 15px;color: #f44336;">{{ markers.top }}</div>
<div style="position: absolute;top: 50%;right: 15px;color: #f44336;">{{ markers.right }}</div>
<div style="position: absolute;left: 50%;bottom: 15px;color: #f44336;">
{{ markers.bottom }}
</div>
<div style="position: absolute;top: 50%;left: 15px;color: #f44336;">
{{ markers.left }}
</div>
<div style="position: absolute;left: 50%;bottom: 15px;color: #f44336;">{{ markers.bottom }}</div>
<div style="position: absolute;top: 50%;left: 15px;color: #f44336;">{{ markers.left }}</div>
<div class="info-instance">
<svg-icon icon-class="IsMasked" style="font-size:20px;" v-show="dicomInfo.IsMasked" />
<!-- <div v-show="dicomInfo.pixel">
Pixel: {{ dicomInfo.pixel }}mm
</div> -->
</div>-->
<div v-show="dicomInfo.location">Location: {{ dicomInfo.location }}</div>
<div v-show="dicomInfo.thick">Slice Thickness: {{ dicomInfo.thick }}mm</div>
<div v-show="dicomInfo.wwwc">WW/WL: {{ dicomInfo.wwwc }}</div>
@@ -92,15 +80,18 @@
import * as cornerstone from 'cornerstone-core'
import * as cornerstoneMath from 'cornerstone-math'
import * as cornerstoneTools from 'cornerstone-tools'
import metaDataProvider from '@/utils/metaDataProvider'
const scroll = cornerstoneTools.import('util/scrollToIndex')
import Hammer from 'hammerjs'
import getOrientationString from '@/views/trials/trials-panel/reading/dicoms/tools/OrientationMarkers/getOrientationString'
import invertOrientationString from '@/views/trials/trials-panel/reading/dicoms/tools/OrientationMarkers/invertOrientationString'
import calculateSUV from '@/views/trials/trials-panel/reading/dicoms/tools/calculateSUV'
const calculateSUV = cornerstoneTools.import('util/calculateSUV')
// import requestPoolManager from '@/utils/request-pool'
import ScaleOverlayTool from '@/views/trials/trials-panel/reading/dicoms/tools/ScaleOverlay/ScaleOverlayTool'
import Note_RectangleRoiTool from '@/views/trials/trials-panel/reading/dicoms/tools/RectangleRoi/Note_RectangleRoiTool'
let isMetaDataProviderAdded = false
cornerstoneTools.external.cornerstone = cornerstone
cornerstoneTools.external.Hammer = Hammer
cornerstoneTools.external.cornerstoneMath = cornerstoneMath
@@ -110,6 +101,7 @@ cornerstoneTools.toolColors.setActiveColor('rgb(0, 255, 0)')
// cornerstoneTools.init({ showSVGCursors: true })
cornerstoneTools.init()
const ToolStateManager = cornerstoneTools.globalImageIdSpecificToolStateManager
console.log(cornerstoneTools, 'cornerstoneTools')
import DicomTags from './DicomTags'
export default {
name: 'DicomCanvas',
@@ -117,6 +109,12 @@ export default {
computed: {
NSTip() {
return `${this.$store.state.trials.downloadSize}, NS: ${this.$store.state.trials.downloadTip}`
},
},
props: {
isComparison: {
type: Boolean,
default: false
}
},
data() {
@@ -130,7 +128,7 @@ export default {
seriesNumber: '',
imageIds: [],
currentImageIdIndex: 0,
firstImageLoading: false
firstImageLoading: false,
// preventCache: true
},
dicomInfo: {
@@ -150,14 +148,15 @@ export default {
wwwc: '',
zoom: 0,
location: '',
fps: 5
fps: 5,
IsMasked: false
},
toolState: {
initialized: false,
activeTool: 'none',
dicomInfoVisible: false,
clipPlaying: false,
viewportInvert: false
viewportInvert: false,
},
loadImagePromise: null,
AnnotationSync: null,
@@ -168,18 +167,25 @@ export default {
sliderInfo: {
oldB: null,
oldM: null,
isMove: false
isMove: false,
},
mousePosition: { x: '', y: '', mo: '' },
markers: { top: '', right: '', bottom: '', left: '' },
orientationMarkers: [],
originalMarkers: [],
dcmTag: { visible: false, title: this.$t('trials:dicom-tag:title') }
dcmTag: { visible: false, title: this.$t('trials:dicom-tag:title') },
tip: '',
IsReadingTaskViewInOrder: 2
}
},
mounted() {
this.type = this.$router.currentRoute.query.type ? this.$router.currentRoute.query.type : ''
this.type = this.$router.currentRoute.query.type
? this.$router.currentRoute.query.type
: ''
if (this.$router.currentRoute.query.IsReadingTaskViewInOrder) {
this.IsReadingTaskViewInOrder = Number(this.$router.currentRoute.query.IsReadingTaskViewInOrder)
}
this.canvas = this.$refs.canvas
this.canvas.addEventListener('cornerstonenewimage', this.onNewImage)
this.canvas.addEventListener(
@@ -200,17 +206,32 @@ export default {
document.addEventListener('mousemove', (e) => {
this.sliderMousemove(e)
})
this.canvas.addEventListener('cornerstonetoolsstackscroll', this.stackScrollCallback)
this.canvas.addEventListener(
'cornerstonetoolsstackscroll',
this.stackScrollCallback
)
},
methods: {
loadImageStack(dicomSeries) {
getInfo() {
var image = cornerstone.getImage(this.canvas)
return image
},
loadImageStack(dicomSeries, text = '') {
this.tip = text
this.$nextTick(() => {
if (!dicomSeries || !Array.isArray(dicomSeries.imageIds) || dicomSeries.imageIds.length === 0) {
return
}
this.series = dicomSeries
this.stack.seriesId = dicomSeries.seriesId
this.stack.seriesNumber = dicomSeries.seriesNumber
this.stack.imageIds = dicomSeries.imageIds
this.stack.currentImageIdIndex = dicomSeries.imageIdIndex && dicomSeries.imageIdIndex < dicomSeries.imageIds.length ? dicomSeries.imageIdIndex : 0
this.stack.currentImageIdIndex =
dicomSeries.imageIdIndex &&
dicomSeries.imageIdIndex < dicomSeries.imageIds.length
? dicomSeries.imageIdIndex
: 0
this.stack.firstImageLoading = true
this.stack.description = dicomSeries.description
this.toolState.viewportInvert = false
@@ -221,18 +242,27 @@ export default {
// this.stack.instanceId = instanceId
this.toolState.clipPlaying = false
const element = this.$refs.canvas
if (!isMetaDataProviderAdded) {
// 注册自定义 metaDataProvider:统一 SUV 口径(优先接口,缺失回退 DICOM)
cornerstone.metaData.addProvider(metaDataProvider, 100000)
isMetaDataProviderAdded = true
}
cornerstone.enable(element)
cornerstoneTools.stopClip(this.canvas)
this.toolState.clipPlaying = false
this.loading = true
cornerstone.loadAndCacheImage(this.stack.imageIds[this.stack.currentImageIdIndex])
.then(image => {
cornerstone
.loadAndCacheImage(
this.stack.imageIds[this.stack.currentImageIdIndex]
)
.then((image) => {
this.loading = false
if (this.stack.imageIds.indexOf(image.imageId) !== -1) {
this.onFirstImageLoaded(image)
}
}).catch((error) => {
})
.catch((error) => {
this.loading = false
if (error.error && error.error.message) {
this.$alert(error.error.message)
@@ -255,15 +285,17 @@ export default {
const apiTool = cornerstoneTools[`${toolName}Tool`]
if (apiTool) {
const toolAlreadyAddedToElement = cornerstoneTools.getToolForElement(
element,
apiTool
)
const toolAlreadyAddedToElement =
cornerstoneTools.getToolForElement(element, apiTool)
if (!toolAlreadyAddedToElement) {
if (toolName === 'RectangleRoi') {
cornerstoneTools.addToolForElement(element, apiTool, { configuration: { showMinMax: true, showStatsText: true } })
cornerstoneTools.addToolForElement(element, apiTool, {
configuration: { showMinMax: true, showStatsText: true },
})
} else if (toolName === 'EllipticalRoi') {
cornerstoneTools.addToolForElement(element, apiTool, { configuration: { showMinMax: true } })
cornerstoneTools.addToolForElement(element, apiTool, {
configuration: { showMinMax: true },
})
} else {
cornerstoneTools.addToolForElement(element, apiTool)
}
@@ -287,8 +319,28 @@ export default {
false
)
})
if (!cornerstoneTools.getToolForElement(element, cornerstoneTools.WwwcRegionTool)) {
cornerstoneTools.addToolForElement(element, cornerstoneTools.WwwcRegionTool)
if (
!cornerstoneTools.getToolForElement(element, Note_RectangleRoiTool)
) {
cornerstoneTools.addToolForElement(element, Note_RectangleRoiTool, {
configuration: {
color: '#f00',
lineWidth: 2,
drawHandles: false,
fillColor: 'rgba(0, 0, 0, 1)',
},
})
}
if (
!cornerstoneTools.getToolForElement(
element,
cornerstoneTools.WwwcRegionTool
)
) {
cornerstoneTools.addToolForElement(
element,
cornerstoneTools.WwwcRegionTool
)
}
if (
!cornerstoneTools.getToolForElement(
@@ -347,20 +399,27 @@ export default {
// var instanceId = image.imageId.split('/')[image.imageId.split('/').length - 1]
// instanceId = instanceId.split('.')[0]
// this.stack.instanceId = instanceId
this.height = (this.stack.currentImageIdIndex) * 100 / (this.stack.imageIds.length - 1)
this.height = this.getStackHeightPercent()
this.resetWwwc()
},
onNewImage(e) {
e.detail.enabledElement.options = {}
var data = e.detail.image.data
this.dicomInfo.hospital = data.string('x00080080')
// let instanceInfo = this.series.instanceInfoList.find(item => item.ImageId === e.detail.image.imageId)
let instanceInfo = this.series?.instanceInfoList?.find(item => {
let s1 = item.ImageId ? item.ImageId.split("?")[0] : ''
let s2 = e.detail.image.imageId ? e.detail.image.imageId.split("?")[0] : ''
return s1 === s2
})
this.dicomInfo.IsMasked = instanceInfo ? instanceInfo.IsMasked : false
// this.dicomInfo.pid = data.string('x00100020')
this.dicomInfo.pid = data.string('x00120040')
this.dicomInfo.name = data.string('x00100010')
this.dicomInfo.age = data.string('x00101010')
this.dicomInfo.sex = data.string('x00100040')
this.dicomInfo.acc = data.string('x00080050') // 登记号
this.dicomInfo.modality = data.string('x00080060')
this.dicomInfo.modality = (data.string('x00080060') || '').trim()
this.dicomInfo.time = this.formatDicomDateTime(
data.string('x00080020'),
data.string('x00080030')
@@ -379,21 +438,32 @@ export default {
if (this.dicomInfo.thick) {
this.dicomInfo.thick = this.dicomInfo.thick.toFixed(2)
}
const newImageIdIndex = this.stack.imageIds.findIndex(i => i === e.detail.image.imageId)
const newImageIdIndex = this.stack.imageIds.findIndex(
(i) => i === e.detail.image.imageId
)
if (newImageIdIndex === -1) return
this.stack.currentImageIdIndex = newImageIdIndex
this.stack.imageIdIndex = newImageIdIndex
this.series.imageIdIndex = newImageIdIndex
this.height = (this.stack.currentImageIdIndex) * 100 / (this.stack.imageIds.length - 1)
this.resetWwwc()
this.height = this.getStackHeightPercent()
},
getStackHeightPercent() {
const imageCount = Array.isArray(this.stack.imageIds) ? this.stack.imageIds.length : 0
if (imageCount <= 1) {
return 0
}
return (this.stack.currentImageIdIndex * 100) / (imageCount - 1)
},
stackScrollCallback(e) {
const { detail } = e
if (this.isScrollSync) {
this.$emit('scrollSync', { canvasIndex: this.canvasIndex, direction: detail.direction })
this.$emit('scrollSync', {
canvasIndex: this.canvasIndex,
direction: detail.direction,
})
}
this.stack.currentImageIdIndex = e.detail.newImageIdIndex
this.height = (this.stack.currentImageIdIndex) * 100 / (this.stack.imageIds.length - 1)
this.height = this.getStackHeightPercent()
// var priority = new Date(new Date().setHours(23, 59, 59, 999)).getTime()
// requestPoolManager.loadAndCacheImagePlus(this.stack.imageIds[this.stack.currentImageIdIndex], this.stack.seriesId, priority)
@@ -408,7 +478,9 @@ export default {
if (date) {
date = `${date.substr(0, 4)}-${date.substr(4, 2)}-${date.substr(6, 2)}`
}
if (time) { time = `${time.substr(0, 2)}:${time.substr(2, 2)}:${time.substr(4, 2)}` }
if (time) {
time = `${time.substr(0, 2)}:${time.substr(2, 2)}:${time.substr(4, 2)}`
}
return time ? `${date} ${time}` : `${date} 00:00:00`
},
@@ -453,7 +525,7 @@ export default {
top: oppositeColumn,
bottom: column,
left: oppositeRow,
right: row
right: row,
}
if (!markers) {
return
@@ -500,13 +572,7 @@ export default {
if (image.color) {
stats.storedPixels = this.getRGBPixels(element, x, y, 1, 1)
} else {
stats.storedPixels = cornerstone.getStoredPixels(
element,
x,
y,
1,
1
)
stats.storedPixels = cornerstone.getStoredPixels(element, x, y, 1, 1)
stats.sp = stats.storedPixels[0]
stats.mo = stats.sp * image.slope + image.intercept
stats.suv = calculateSUV(image, stats.sp)
@@ -533,7 +599,8 @@ export default {
if (enabledElement.image.color) {
for (row = 0; row < height; row++) {
for (column = 0; column < width; column++) {
spIndex = ((row + y) * enabledElement.image.columns + (column + x)) * 4
spIndex =
((row + y) * enabledElement.image.columns + (column + x)) * 4
const red = pixelData[spIndex]
const green = pixelData[spIndex + 1]
const blue = pixelData[spIndex + 2]
@@ -551,7 +618,8 @@ export default {
},
sliderMousedown(e) {
var boxHeight = this.$refs['sliderBox'].clientHeight
this.sliderInfo.oldB = parseInt(e.srcElement.style.top) * boxHeight / 100
this.sliderInfo.oldB =
(parseInt(e.srcElement.style.top) * boxHeight) / 100
this.sliderInfo.oldM = e.clientY
this.sliderInfo.isMove = true
e.stopImmediatePropagation()
@@ -564,9 +632,14 @@ export default {
var boxHeight = this.$refs['sliderBox'].clientHeight
if (PX < 0) return
if (PX > boxHeight) return
var height = PX * 100 / boxHeight
var index = Math.trunc(this.stack.imageIds.length * this.height / 100)
index = index > this.stack.imageIds.length ? this.stack.imageIds.length : index < 0 ? 0 : index
var height = (PX * 100) / boxHeight
var index = Math.trunc((this.stack.imageIds.length * this.height) / 100)
index =
index > this.stack.imageIds.length
? this.stack.imageIds.length
: index < 0
? 0
: index
// if (!cornerstone.imageCache.getImageLoadObject(this.stack.imageIds[index])) return
this.height = height
if (this.stack.currentImageIdIndex !== index) {
@@ -578,9 +651,9 @@ export default {
},
goViewer(e) {
// console.log(this.$refs['sliderBox'].clientHeight)
var height = e.offsetY * 100 / this.$refs['sliderBox'].clientHeight
var height = (e.offsetY * 100) / this.$refs['sliderBox'].clientHeight
this.height = height
var index = Math.trunc(this.stack.imageIds.length * this.height / 100)
var index = Math.trunc((this.stack.imageIds.length * this.height) / 100)
scroll(this.canvas, index)
},
onClipStopped() {
@@ -673,10 +746,7 @@ export default {
}
this.toolState.clipPlaying = true
cornerstoneTools.playClip(this.canvas, this.dicomInfo.fps)
cornerstoneTools.getToolState(
this.canvas,
'playClip'
).data[0].loop = true
cornerstoneTools.getToolState(this.canvas, 'playClip').data[0].loop = true
},
setFps(fps) {
this.dicomInfo.fps = fps
@@ -687,6 +757,7 @@ export default {
var viewport = cornerstone.getViewport(this.canvas)
// viewport.invert = false
var image = cornerstone.getImage(this.canvas)
if (!viewport || !image) return
viewport.voi.windowWidth = image.windowWidth
viewport.voi.windowCenter = image.windowCenter
cornerstone.setViewport(this.canvas, viewport)
@@ -712,8 +783,8 @@ export default {
invert: false,
preventZoomOutsideImage: false,
minScale: 0.1,
maxScale: 20.0
}
maxScale: 20.0,
},
})
cornerstoneTools.setToolActive('Zoom', { mouseButtonMask: 1 })
this.toolState.activeTool = 'zoom'
@@ -797,9 +868,120 @@ export default {
setToolPassive(toolName) {
cornerstoneTools.setToolPassiveForElement(this.canvas, toolName)
},
async reloadImage(newImageId = null) {
// 1. 获取当前imageId(如果未指定新imageId
let element = this.canvas
this.stack.imageIds.splice(this.stack.currentImageIdIndex, 1, newImageId)
const currentImageId =
newImageId || cornerstone.getImage(element)?.imageId
if (!currentImageId) {
console.error('没有找到可用的imageId')
return
}
// 2. 清除该影像关联的工具状态(标注数据)
this.clearToolStateForImage(element, currentImageId)
// 3. 从缓存中移除该影像
this.removeImageFromCache(currentImageId)
// 4. 重新加载并渲染
await this.loadAndRenderImage(element, currentImageId)
},
clearToolStateForImage(element, imageId) {
// 获取全局状态管理器
const globalToolStateManager =
cornerstoneTools.globalImageIdSpecificToolStateManager
if (globalToolStateManager) {
// 方式1:清空该影像的全部工具数据
globalToolStateManager.clearImageIdToolState(imageId)
console.log(`已清除影像 ${imageId} 的所有标注数据`)
}
// 方式2:如果使用元素级别的状态管理器
const elementToolStateManager =
cornerstoneTools.getElementToolStateManager(element)
if (elementToolStateManager && elementToolStateManager.get(element)) {
// 清空该元素上当前显示影像的数据
elementToolStateManager.clear(element)
}
},
removeImageFromCache(imageId) {
const imageCache = cornerstone.imageCache
if (imageCache && imageCache[imageId]) {
// 从缓存中移除
delete imageCache[imageId]
console.log(`已从缓存中移除影像: ${imageId}`)
}
// 也可以使用官方API(如果可用)
if (typeof cornerstone.imageCache.removeImage === 'function') {
cornerstone.imageCache.removeImageLoadObject(imageId)
}
},
async loadAndRenderImage(element, imageId) {
try {
// 1. 清除当前画布内容(可选)
const canvas = element.querySelector('canvas')
if (canvas) {
const ctx = canvas.getContext('2d')
ctx.clearRect(0, 0, canvas.width, canvas.height)
}
// 2. 重新加载图像(强制从源头重新获取)
const image = await cornerstone.loadAndCacheImage(imageId)
// 3. 获取或创建视口设置
let viewport = cornerstone.getViewport(element)
if (!viewport) {
viewport = cornerstone.getDefaultViewport(element, image)
}
// 4. 显示图像
cornerstone.displayImage(element, image, viewport)
// 5. 触发重绘
cornerstone.updateImage(element)
console.log(`影像 ${imageId} 重新加载并渲染完成`)
return image
} catch (error) {
console.error('加载图像失败:', error)
throw error
}
},
getNote_RectangleRoi() {
return new Promise(resolve => {
let toolInfo = cornerstoneTools.getToolState(this.canvas, 'Note_RectangleRoi')
let image = cornerstone.getImage(this.canvas)
resolve({ toolInfo, image })
})
// console.log(
// cornerstoneTools.getToolState(this.canvas, 'Note_RectangleRoi')
// )
// console.log(cornerstone.getImage(this.canvas))
// let image = cornerstone.getImage(this.canvas)
// // cornerstone.imageCache.removeImageLoadObject(image.imageId)
// this.reloadImage(this.canvas, image.imageId)
},
removeNote_RectangleRoi() {
const toolState = cornerstoneTools.getToolState(this.canvas, 'Note_RectangleRoi');
if (toolState && toolState.data.length > 0) {
let arr = toolState.data.map(item => item)
arr.forEach(item => {
cornerstoneTools.removeToolState(this.canvas, 'Note_RectangleRoi', item);
})
cornerstone.updateImage(this.canvas);
}
},
setToolActive(toolName) {
cornerstoneTools.setToolActiveForElement(this.canvas, toolName, {
mouseButtonMask: 1
mouseButtonMask: 1,
})
},
setAllToolsPassive() {
@@ -833,7 +1015,7 @@ export default {
)
}
cornerstoneTools.setToolEnabledForElement(this.canvas, 'ReferenceLines', {
synchronizationContext: synchronizer
synchronizationContext: synchronizer,
})
// cornerstoneTools.addTool(cornerstoneTools.CrosshairsTool)
@@ -864,7 +1046,7 @@ export default {
}
cornerstoneTools.setToolActiveForElement(this.canvas, toolName, {
mouseButtonMask: 1,
synchronizationContext: synchronizer
synchronizationContext: synchronizer,
})
},
disabledViewPortToolSync(synchronizer, toolName) {
@@ -1095,7 +1277,7 @@ export default {
'CobbAngle',
'Angle',
'Bidirectional',
'FreehandRoi'
'FreehandRoi',
]
for (let i = 0; i < toolROITypes.length; i++) {
const toolROIType = toolROITypes[i]
@@ -1115,12 +1297,12 @@ export default {
removeLabel(item) {
const promise = scroll(this.canvas, item.data.imageIdIndex)
const scope = this
Promise.all([promise]).then(res => {
Promise.all([promise]).then((res) => {
cornerstoneTools.removeToolState(scope.canvas, item.type, item.data)
cornerstone.updateImage(scope.canvas)
})
}
}
},
},
}
</script>
File diff suppressed because it is too large Load Diff
+28 -11
View File
@@ -56,7 +56,8 @@ export default {
TrialConfig: {},
TrialStatusStr: null,
trialId: null,
IsAdditionalAssessment: false
IsAdditionalAssessment: false,
isReload: false
}
},
computed: {
@@ -71,7 +72,11 @@ export default {
if (~url.indexOf('?')) {
query = url.split('?')[1]
}
zzSessionStorage.setItem('lastWorkbench', `${v.path}${query ? '?' : ''}${query}`)
if (!this.isReload) {
zzSessionStorage.setItem('lastWorkbench', `${v.path}${query ? '?' : ''}${query}`)
} else {
this.isReload = false
}
var firstGoIn = this.trialsRouter.children.find(v => { return v.name === 'TrialsPanel' }).children[0]
if (this.trialsTab === '/trials/trials-panel') {
var targetPath = firstGoIn.path
@@ -83,6 +88,7 @@ export default {
// document.cookie = 'TrialId=' + this.$route.query.trialId + ';path=/'
},
trialId(v) {
if (!this.$route.query.trialId) return
getUserDocumentList({ TrialId: this.$route.query.trialId }).then(async res => {
var total = res.OtherInfo.NeedSignCount
var TrialStatusStr = res.OtherInfo.TrialStatusStr
@@ -95,20 +101,25 @@ export default {
}
},
mounted() {
this.$EventBus.$on("reload", () => {
this.isReload = true
})
var query = ``
var url = window.location.href
if (~url.indexOf('?')) {
query = url.split('?')[1]
}
getUserDocumentList({ TrialId: this.$route.query.trialId }).then(async res => {
var total = res.OtherInfo.NeedSignCount
var TrialStatusStr = res.OtherInfo.TrialStatusStr
this.IsAdditionalAssessment = res.OtherInfo.IsAdditionalAssessment
this.TrialStatusStr = TrialStatusStr
await store.dispatch('user/setTotalNeedSignTrialDocCount', total)
await store.dispatch('user/setTrialStatusStr', TrialStatusStr)
this.TrialConfig = res.OtherInfo.TrialConfig
})
if (this.$route.query.trialId) {
getUserDocumentList({ TrialId: this.$route.query.trialId }).then(async res => {
var total = res.OtherInfo.NeedSignCount
var TrialStatusStr = res.OtherInfo.TrialStatusStr
this.IsAdditionalAssessment = res.OtherInfo.IsAdditionalAssessment
this.TrialStatusStr = TrialStatusStr
await store.dispatch('user/setTotalNeedSignTrialDocCount', total)
await store.dispatch('user/setTrialStatusStr', TrialStatusStr)
this.TrialConfig = res.OtherInfo.TrialConfig
})
}
this.selectedTab()
this.getTrialList()
var firstGoIn = this.trialsRouter.children.find(v => { return v.name === 'TrialsPanel' }).children[0]
@@ -132,6 +143,9 @@ export default {
let chartList = [
'/trials/trials-panel/trial-summary/report-forms'
]
let dataSyncList = [
`/trials/trials-panel/trial-summary/data-sync`
]
var qualityList = [
'/trials/trials-panel/visit/crc-question',
'/trials/trials-panel/visit/qc-check',
@@ -147,6 +161,9 @@ export default {
'/trials/trials-panel/trial-summary/image-inspect',
'/trials/trials-panel/trial-summary/push-record',
]
if (this.$store.state.trials.config.TrialDataStoreType !== 1 && ~dataSyncList.indexOf(path)) {
isShow = false
}
if (!this.$store.state.trials.config.IsExternalViewTrialChart && ~chartList.indexOf(path) && this.hasPermi(['role:cmm', 'role:cpm', 'role:ea', 'role:mc', 'role:smm', 'role:spm'])) {
isShow = false
}
@@ -33,7 +33,7 @@
<el-table-column type="index" width="40" />
<!--受试者-->
<el-table-column :label="$t('download:table:subjectCode')" min-width="130" prop="SubjectCode"
show-overflow-tooltip />
show-overflow-tooltip v-if="isReadingTaskViewInOrder !== 0" />
<!--访视名称-->
<el-table-column prop="VisitName" :label="$t('download:table:VisitName')" sortable v-if="IsImageSegment" />
<!--任务名称-->
@@ -78,7 +78,7 @@
</el-table>
<study-view v-if="model_cfg.visible" :model_cfg="model_cfg" :modelList="modelList" :bodyPart="bodyPart"
:subjectVisitId="modelSubjectVisitId" :IsDicom="IsDicom" :isDownload="true" :visitTaskId="modelTaskId"
:IsImageSegment="IsImageSegment" :Criterion="Criterion" />
:IsImageSegment="IsImageSegment" :Criterion="Criterion" :IsReadingTaskViewInOrder="isReadingTaskViewInOrder" />
</el-dialog>
</template>
<script>
@@ -136,6 +136,14 @@ export default {
IsImageSegment: {
type: Boolean,
default: false,
},
isReadingTaskViewInOrder: {
type: Number,
default: 2,
},
isReading: {
type: Boolean,
default: false,
}
},
data() {
@@ -170,6 +178,7 @@ export default {
this.getList()
this.title = `Download Images${this.SubjectCode}${this.Criterion.TrialReadingCriterionName}`
if (this.IsImageSegment) this.title = `Download Images${this.SubjectCode}`
if (this.isReadingTaskViewInOrder === 0) this.title = ''
},
beforeDestroy() {
store.dispatch('trials/setUnLock', false)
@@ -437,6 +446,9 @@ export default {
let params = {
TrialImageDownloadId: this.downloadId,
}
if (this.isReading) {
params.VisitTaskId = this.TaskId
}
await downloadImageSuccess(params)
} catch (err) {
console.log(err)
@@ -447,6 +459,9 @@ export default {
if (this.IsImageSegment) {
this.model_cfg.title = `${item.SubjectCode || ''} > ${item.VisitName}`
}
if (this.isReadingTaskViewInOrder === 0) {
this.model_cfg.title = ''
}
if (item.IsDicom) {
this.modelList = item.DicomStudyList
} else {
@@ -488,11 +503,11 @@ export default {
let routeData = null
if (!this.IsImageSegment && (this.Criterion.CriterionType == 19 || this.Criterion.CriterionType == 20)) {
routeData = this.$router.resolve({
path: `/showNoneDicoms?trialId=${trialId}&isImageSegmentLabel=${false}&visitTaskId=${row.VisitTaskId}&subjectVisitId=${row.SourceSubjectVisitId}&TokenKey=${token}&isReading=true`,
path: `/showNoneDicoms?trialId=${trialId}&isImageSegmentLabel=${false}&visitTaskId=${row.VisitTaskId}&subjectVisitId=${row.SourceSubjectVisitId}&TokenKey=${token}&isReading=true&IsReadingTaskViewInOrder=${this.isReadingTaskViewInOrder}`,
})
} else {
routeData = this.$router.resolve({
path: `/showNoneDicoms?trialId=${trialId}&visitTaskId=${row.VisitTaskId}&subjectVisitId=${row.SourceSubjectVisitId}&TokenKey=${token}&isReading=true`,
path: `/showNoneDicoms?trialId=${trialId}&visitTaskId=${row.VisitTaskId}&subjectVisitId=${row.SourceSubjectVisitId}&TokenKey=${token}&isReading=true&IsReadingTaskViewInOrder=${this.isReadingTaskViewInOrder}`,
})
}
this.open = window.open(routeData.href, '_blank')
@@ -505,7 +520,7 @@ export default {
var token = getToken()
let trialId = this.$route.query.trialId
const routeData = this.$router.resolve({
path: `/showvisitdicoms?page=download&trialId=${trialId}&visitTaskId=${row.VisitTaskId}&subjectVisitId=${row.SourceSubjectVisitId}&isReading=1&TokenKey=${token}`,
path: `/showvisitdicoms?page=download&trialId=${trialId}&visitTaskId=${row.VisitTaskId}&subjectVisitId=${row.SourceSubjectVisitId}&isReading=1&TokenKey=${token}&IsReadingTaskViewInOrder=${this.isReadingTaskViewInOrder}`,
})
this.open = window.open(routeData.href, '_blank')
},
+43 -89
View File
@@ -2,36 +2,21 @@
<!--FEEDBACK-->
<div v-if="visible" @click.stop="() => false" class="feedBack-box">
<div class="feedBack-box-modal"></div>
<el-dialog
:visible.sync="visible"
v-dialogDrag
width="800px"
:close-on-click-modal="false"
@close="cancel"
:modal="false"
>
<el-dialog :visible.sync="visible" v-dialogDrag width="800px" :close-on-click-modal="false" @close="cancel"
:modal="false">
<div slot="title">
{{ title }}
</div>
<!-- 项目详情 -->
<div class="trialsBox" v-if="visitTaskId || SubjectVisitId">
<el-form
label-position="right"
:model="form"
:inline="true"
class="trialsForm"
v-if="type === 'detail'"
>
<el-form label-position="right" :model="form" :inline="true" class="trialsForm" v-if="type === 'detail'">
<el-form-item :label="$t('feedBack:trials:code')" style="width: 40%">
<span>{{ form.TrialCode }}</span>
</el-form-item>
<el-form-item :label="$t('feedBack:trials:name')" style="width: 40%">
<span>{{ form.ExperimentName }}</span>
</el-form-item>
<el-form-item
:label="$t('feedBack:trials:siteCode')"
style="width: 40%"
>
<el-form-item :label="$t('feedBack:trials:siteCode')" style="width: 40%">
<span>{{ form.TrialSiteCode }}</span>
</el-form-item>
<el-form-item :label="$t('feedBack:trials:visit')" style="width: 40%">
@@ -39,47 +24,21 @@
</el-form-item>
</el-form>
</div>
<el-form
ref="feedBackForm"
label-position="right"
:model="form"
:rules="rules"
label-width="100px"
>
<el-form ref="feedBackForm" label-position="right" :model="form" :rules="rules" label-width="100px">
<!-- 影像异常tip -->
<p class="tip" v-if="type === 'imgfail'">
<i
class="el-icon-warning-outline"
style="color: #f56c6c; font-size: 24px"
></i>
<i class="el-icon-warning-outline" style="color: #f56c6c; font-size: 24px"></i>
<span>{{ $t('feedBack:imgfail:tip') }}</span>
</p>
<!-- 问题反馈 -->
<el-form-item
:label="$t('feedBack:form:feedBack')"
prop="QuestionType"
v-if="type === 'feedback' && trialId"
>
<el-select
v-model="form.QuestionType"
style="width: 100%"
popper-class="feedBack-select-box"
>
<el-option
v-for="item in QuestionTypeOptions"
:key="item.id"
:label="item.label"
:value="item.value"
>
<el-form-item :label="$t('feedBack:form:feedBack')" prop="QuestionType" v-if="type === 'feedback' && trialId">
<el-select v-model="form.QuestionType" style="width: 100%" popper-class="feedBack-select-box">
<el-option v-for="item in QuestionTypeOptions" :key="item.id" :label="item.label" :value="item.value">
</el-option>
</el-select>
</el-form-item>
<!-- 问题反馈 -->
<el-form-item
:label="$t('feedBack:form:feedBack')"
prop="QuestionType"
v-if="type === 'detail'"
>
<el-form-item :label="$t('feedBack:form:feedBack')" prop="QuestionType" v-if="type === 'detail'">
<span>{{
QuestionTypeOptions.filter(
(item) => item.value === form.QuestionType
@@ -87,43 +46,22 @@
}}</span>
</el-form-item>
<!-- 问题描述 -->
<el-form-item
:label="$t('feedBack:form:description')"
prop="QuestionDescription"
>
<el-input
v-model="form.QuestionDescription"
type="textarea"
:rows="4"
:maxlength="500"
:disabled="type === 'detail'"
/>
<el-form-item :label="$t('feedBack:form:description')" prop="QuestionDescription">
<el-input v-model="form.QuestionDescription" type="textarea" :rows="4" :maxlength="500"
:disabled="type === 'detail'" />
</el-form-item>
<!-- 截图 -->
<el-form-item :label="$t('feedBack:form:screenshot')" prop="screenshot">
<uploadImage
:path.sync="form.ScreenshotList"
:isUpload.sync="loading"
:trialId="trialId"
:disabled="type === 'detail'"
ref="uploadImage"
/>
<uploadImage :path.sync="form.ScreenshotList" :isUpload.sync="loading" :trialId="trialId"
:disabled="type === 'detail'" ref="uploadImage" />
</el-form-item>
<!-- 反馈时间 -->
<el-form-item
:label="$t('feedBack:form:time')"
prop="screenshot"
v-if="type === 'detail'"
>
<el-form-item :label="$t('feedBack:form:time')" prop="screenshot" v-if="type === 'detail'">
<span>{{ form.CreateTime }}</span>
</el-form-item>
<!-- 状态 -->
<el-form-item
:label="$t('feedBack:form:status')"
prop="screenshot"
v-if="type === 'detail' && level > 7"
>
<el-switch
<el-form-item :label="$t('feedBack:form:status')" prop="screenshot" v-if="type === 'detail' && level >= 7">
<!-- <el-switch
v-model="form.State"
active-color="#13ce66"
inactive-color="#ff4949"
@@ -134,21 +72,27 @@
:disabled="level < 8 || !isStateChange"
@change="changeState"
>
</el-switch>
</el-switch> -->
<el-select v-model="form.State" :popper-append-to-body="false" :disabled="level < 8">
<el-option v-for="item in $d.FeedBackStatus" :key="item.id" :label="item.label"
:value="item.value"></el-option>
</el-select>
</el-form-item>
<!-- 无法解决原因 -->
<el-form-item :label="$t('feedBack:form:Reason')" prop="Reason"
v-if="type === 'detail' && level >= 7 && form.State === 2">
<el-input v-model="form.Reason" type="textarea" :rows="4" :maxlength="500" :disabled="level < 8" />
</el-form-item>
</el-form>
<div slot="footer" v-if="type !== 'detail' || isImgfail">
<!--type !== 'detail'-->
<div slot="footer" v-if="level >= 7 || isImgfail">
<!-- 取消 -->
<el-button size="small" @click.stop="cancel">
<el-button size="small" @click.stop="cancel" v-if="type !== 'detail' || level >= 8">
{{ $t('feedBack:button:cancel') }}
</el-button>
<!-- 保存 -->
<el-button
type="primary"
size="small"
@click.stop="save"
:loading="loading"
>
<el-button type="primary" size="small" @click.stop="save" :loading="loading"
v-if="type !== 'detail' || level >= 8">
{{ $t('feedBack:button:save') }}
</el-button>
</div>
@@ -190,6 +134,7 @@ export default {
SubjectVisitId: null,
ScreenshotList: [],
ScreenshotListStr: null,
Reason: null
},
rules: {
QuestionType: [
@@ -307,6 +252,7 @@ export default {
// 修改状态
async changeState() {
if (this.isImgfail) return
if (this.form.State !== 1 && this.form.State !== 0) return false
try {
let data = {
IdList: [this.Id],
@@ -381,6 +327,10 @@ export default {
}
</script>
<style lang="scss" scoped>
::v-deep.el-popper {
z-index: 4000 !important;
}
.tip {
width: 86%;
margin: auto;
@@ -389,12 +339,14 @@ export default {
padding: 0 10px;
display: flex;
align-items: center;
// border-radius: 5px;
// background-color: #eee;
i {
margin-right: 5px;
}
}
.trialsBox {
margin: auto;
margin-bottom: 20px;
@@ -405,11 +357,13 @@ export default {
border-radius: 5px;
background-color: #eee;
}
.trialsForm {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
.el-form-item {
margin-bottom: 10px;
}
+7 -39
View File
@@ -62,7 +62,8 @@ export default {
zIndex: 9,
chart: null,
loading: false,
key: 'readingChart'
key: 'readingChart',
isInteger: false
};
},
methods: {
@@ -101,12 +102,13 @@ export default {
let res = await getReportsChartData(params)
this.loading = false
if (res.IsSuccess) {
this.isInteger = res.Result.Type === 'number' && res.Result.ValueType === 0
let LatestScanDateList = res.Result.LatestScanDateList.map(item => item.split(" ")[0])
let obj = {
title: QuestionName,
xAxisData: LatestScanDateList || [],
series: [],
unit: this.$fd("ValueUnit", res.Result.Unit),
unit: res.Result.Unit === 0 ? '' : res.Result.Unit === 4 ? res.Result.CustomUnit : this.$fd("ValueUnit", res.Result.Unit),
visitName: res.Result.VisitTaskNameList,
min: null,
max: null
@@ -123,43 +125,6 @@ export default {
type: 'line'
})
});
// if (Array.isArray(res.Result.LatestScanDateList) && res.Result.LatestScanDateList.length >= 2) {
// let hours = moment(res.Result.LatestScanDateList[res.Result.LatestScanDateList.length - 1]).diff(moment(res.Result.LatestScanDateList[0]), 'hours');
// let days = moment(res.Result.LatestScanDateList[res.Result.LatestScanDateList.length - 1]).diff(moment(res.Result.LatestScanDateList[0]), 'days');
// let months = moment(res.Result.LatestScanDateList[res.Result.LatestScanDateList.length - 1]).diff(moment(res.Result.LatestScanDateList[0]), 'months');
// console.log(hours, 'hours')
// console.log(days, 'days')
// console.log(months, 'months')
// if (hours < 24) {
// obj.min = moment(res.Result.LatestScanDateList[0]).format('YYYY-MM-DD') + ' 00:00:00'
// obj.max = moment(res.Result.LatestScanDateList[0]).format('YYYY-MM-DD') + ' 23:59:59'
// }
// if (days >= 1 && days <= 7) {
// obj.min = moment(res.Result.LatestScanDateList[0]).format('YYYY-MM-DD') + ' 00:00:00'
// obj.max = moment(res.Result.LatestScanDateList[0]).add(7, 'days').format('YYYY-MM-DD') + ' 23:59:59'
// }
// if (days > 7 && days < 30) {
// obj.min = moment(res.Result.LatestScanDateList[0]).startOf('month').format('YYYY-MM-DD') + ' 00:00:00'
// obj.max = moment(res.Result.LatestScanDateList[0]).endOf('month').format('YYYY-MM-DD') + ' 23:59:59'
// }
// if (months >= 1 && months <= 3) {
// obj.min = moment(res.Result.LatestScanDateList[0]).startOf('month').format('YYYY-MM-DD') + ' 00:00:00'
// obj.max = moment(res.Result.LatestScanDateList[0]).add(4, 'months').startOf('month').format('YYYY-MM-DD') + ' 23:59:59'
// }
// if (months > 3 && months <= 6) {
// obj.min = moment(res.Result.LatestScanDateList[0]).startOf('month').format('YYYY-MM-DD') + ' 00:00:00'
// obj.max = moment(res.Result.LatestScanDateList[0]).add(7, 'months').startOf('month').format('YYYY-MM-DD') + ' 23:59:59'
// }
// if (months > 6 && months <= 12) {
// obj.min = moment(res.Result.LatestScanDateList[0]).startOf('month').format('YYYY-MM-DD') + ' 00:00:00'
// obj.max = moment(res.Result.LatestScanDateList[0]).add(13, 'months').startOf('month').format('YYYY-MM-DD') + ' 23:59:59'
// }
// if (months > 12) {
// obj.min = moment(res.Result.LatestScanDateList[0]).startOf('month').format('YYYY-MM-DD') + ' 00:00:00'
// obj.max = moment(res.Result.LatestScanDateList[res.Result.LatestScanDateList.length - 1]).add(1, 'months').startOf('month').format('YYYY-MM-DD') + ' 23:59:59'
// }
// }
// console.log(obj)
this.initChart(obj)
}
} catch (err) {
@@ -236,6 +201,9 @@ export default {
},
series: obj.series
};
if (this.isInteger) {
option.yAxis.minInterval = 1
}
// 4. 使用配置项渲染图表
this.chart.setOption(option);
},
@@ -10,7 +10,8 @@
<!--检查列表-->
<el-table :data="list" style="width: 100%" height="300" :loading="loading">
<!--受试者-->
<el-table-column prop="SubjectCode" :label="$t('upload:dicom:table:subjectCode')" sortable />
<el-table-column prop="SubjectCode" :label="$t('upload:dicom:table:subjectCode')" sortable
v-if="isReadingTaskViewInOrder !== 0" />
<!--访视名称-->
<el-table-column prop="VisitName" :label="$t('download:table:VisitName')" v-if="IsImageSegment" sortable />
<!--任务名称-->
@@ -42,7 +43,8 @@
<template slot-scope="scope">
<div class="btnBox">
<!--上传--->
<form id="inputForm" :ref="`uploadForm_${scope.row.Id}`" enctype="multipart/form-data" v-if="!forbid">
<form id="inputForm" :ref="`uploadForm_${scope.row.Id}`" enctype="multipart/form-data"
v-if="!forbid && (!isReading || isDownloaded)">
<div class="form-group" style="margin-right: 10px">
<div :id="`directoryInputWrapper_${scope.row.Id}`" class="btn btn-link file-input">
<el-button circle icon="el-icon-upload2" :disabled="btnLoading" :loading="btnLoading"
@@ -59,27 +61,22 @@
scope.row.UploadStudyList.length <= 0
" @click.stop="handleViewReadingImages(scope.row)" :title="$t('upload:dicom:button:preview')" />
<!--删除--->
<el-button circle :disabled="!scope.row.UploadStudyList ||
scope.row.UploadStudyList.length <= 0 ||
scope.row.ReadingTaskState === 2
" icon="el-icon-delete" :title="$t('upload:dicom:button:delete')" @click.stop="remove(scope.row)" />
<el-button circle icon="el-icon-delete" :title="$t('upload:dicom:button:delete')"
@click.stop="remove(scope.row)" />
</div>
</template>
</el-table-column>
</el-table>
<div style="margin: 10px 0" class="top">
<span>{{ $t('upload:dicom:uploadTitle') }}</span>
<div class="btnBox" v-if="!forbid">
<span style="margin-right: 10px">
{{ $store.state.trials.uploadTip }}
</span>
<div class="btnBox" v-if="!forbid && (!isReading || isDownloaded)">
<span style="margin-right: 10px">{{ $store.state.trials.uploadTip }}</span>
<form id="inputForm" ref="uploadForm" enctype="multipart/form-data">
<div class="form-group">
<div id="directoryInputWrapper" class="btn btn-link file-input">
<el-button type="primary" :disabled="btnLoading" :loading="btnLoading" size="mini">
{{ $t('upload:dicom:button:batchUpload') }}
</el-button>
<input type="file" name="file" ref="pathClear" :disabled="btnLoading" webkitdirectory multiple title=""
<el-button type="primary" :disabled="btnLoading" :loading="btnLoading" size="mini">{{
$t('upload:dicom:button:batchUpload') }}</el-button>
<input type="file" name="file" ref="pathClear" :disabled="btnLoading" webkitdirectory multiple title
@change="beginScanFiles($event)" />
</div>
</div>
@@ -94,8 +91,10 @@
<template slot="header">
<el-tooltip placement="top">
<div slot="content">
{{ $t('trials:uploadDicomList:table:studyDetail1') }}<br />
{{ $t('trials:uploadDicomList:table:studyDetail2') }}<br />
{{ $t('trials:uploadDicomList:table:studyDetail1') }}
<br />
{{ $t('trials:uploadDicomList:table:studyDetail2') }}
<br />
{{ $t('trials:uploadedDicoms:table:studyDate') }}
</div>
<span>{{ $t('trials:uploadDicomList:table:studyInfo') }}</span>
@@ -105,13 +104,15 @@
<div style="line-height: 15px">
<div>
<div>
<span v-if="scope.row.dicomInfo.accNumber"><span style="font-weight: 500">Acc:</span>
{{ scope.row.dicomInfo.accNumber }}</span>
<span v-if="scope.row.dicomInfo.accNumber">
<span style="font-weight: 500">Acc:</span>
{{ scope.row.dicomInfo.accNumber }}
</span>
<span v-else style="color: #f44336">N/A</span>
</div>
<div style="display: inline-block; margin-right: 2px">
<span v-if="scope.row.dicomInfo.modality.length > 0">
{{ scope.row.dicomInfo.modality.join('、') }},</span>
<span v-if="scope.row.dicomInfo.modality.length > 0">{{ scope.row.dicomInfo.modality.join('、')
}},</span>
<span v-else style="color: #f44336">N/A,</span>
</div>
<div style="display: inline-block; margin-right: 2px">
@@ -126,20 +127,15 @@
<div>
<div style="display: inline-block; margin-right: 2px">
<span v-if="scope.row.dicomInfo.bodyPart">
{{ scope.row.dicomInfo.bodyPart }},
</span>
<span v-else style="color: #f44336">N/A, </span>
<span v-if="scope.row.dicomInfo.bodyPart">{{ scope.row.dicomInfo.bodyPart }},</span>
<span v-else style="color: #f44336">N/A,</span>
</div>
<div style="display: inline-block">
<span v-if="scope.row.dicomInfo.description">
{{ scope.row.dicomInfo.description }}</span>
<span v-if="scope.row.dicomInfo.description">{{ scope.row.dicomInfo.description }}</span>
<span v-else style="color: #f44336">N/A</span>
</div>
</div>
<div>
{{ scope.row.dicomInfo.studyTime }}
</div>
<div>{{ scope.row.dicomInfo.studyTime }}</div>
</div>
</template>
</el-table-column>
@@ -147,8 +143,10 @@
<template slot="header">
<el-tooltip placement="top">
<div slot="content">
{{ $t('trials:uploadDicomList:table:pId') }}<br />
{{ $t('trials:uploadDicomList:table:patientName') }}<br />
{{ $t('trials:uploadDicomList:table:pId') }}
<br />
{{ $t('trials:uploadDicomList:table:patientName') }}
<br />
{{ $t('trials:uploadDicomList:table:pInfo') }}
</div>
<span>{{ $t('trials:uploadDicomList:table:patientInfo') }}</span>
@@ -157,8 +155,11 @@
<template slot-scope="scope">
<div style="line-height: 15px">
<div>
<span v-if="scope.row.dicomInfo.patientId"><span style="font-weight: 500">PID: </span>{{
scope.row.dicomInfo.patientId }}</span>
<span v-if="scope.row.dicomInfo.patientId">
<span style="font-weight: 500">PID:</span>
{{
scope.row.dicomInfo.patientId }}
</span>
<span v-else style="color: #f44336">N/A</span>
</div>
<div>
@@ -227,8 +228,7 @@
<span v-if="
!scope.row.dicomInfo.failedFileCount &&
!scope.row.dicomInfo.isInit
">
{{ $t('trials:uploadDicomList:table:status1') }}</span>
">{{ $t('trials:uploadDicomList:table:status1') }}</span>
<span style="color: #409eff" v-else-if="
!scope.row.dicomInfo.failedFileCount &&
scope.row.dicomInfo.isInit &&
@@ -246,9 +246,11 @@
scope.row.uploadState.record &&
scope.row.uploadState.record.fileCount === 0
">{{ $t('trials:uploadDicomList:table:status5') }}</span>
<span style="color: #f66" v-else>{{
$t('trials:uploadDicomList:table:Failed')
}}</span>
<span style="color: #f66" v-else>
{{
$t('trials:uploadDicomList:table:Failed')
}}
</span>
</template>
</el-table-column>
<el-table-column :label="$t('trials:uploadDicomList:table:record')" min-width="140" show-overflow-tooltip>
@@ -259,42 +261,42 @@
{{ $t('trials:uploadDicomList:table:Existed') }}:
<div v-if="scope.row.uploadState.record.Existed.length">
<div v-for="item of scope.row.uploadState.record.Existed" :key="item"
style="font-size: 12px; color: #baa72a">
{{ item }}
</div>
style="font-size: 12px; color: #baa72a">{{ item }}</div>
</div>
<div v-else>&nbsp;</div>
{{ $t('trials:uploadDicomList:table:Uploaded') }}:
<div v-if="scope.row.uploadState.record.Uploaded.length">
<div v-for="item of scope.row.uploadState.record.Uploaded" :key="item"
style="font-size: 12px; color: #24b837">
{{ item }}
</div>
style="font-size: 12px; color: #24b837">{{ item }}</div>
</div>
<div v-else>&nbsp;</div>
<br />
{{ $t('trials:uploadDicomList:table:Failed') }}:
<div v-if="scope.row.uploadState.record.Failed.length">
<div v-for="item of scope.row.uploadState.record.Failed" :key="item"
style="font-size: 12px; color: #f66">
{{ item }}
</div>
style="font-size: 12px; color: #f66">{{ item }}</div>
</div>
<div v-else>&nbsp;</div>
</div>
</div>
<el-button size="mini" style="cursor: pointer">
<span style="font-size: 12px; color: #baa72a">{{
scope.row.uploadState.record.Existed.length
}}</span>
<span style="font-size: 12px; color: #baa72a">
{{
scope.row.uploadState.record.Existed.length
}}
</span>
/
<span style="font-size: 12px; color: #24b837">{{
scope.row.uploadState.record.Uploaded.length
}}</span>
<span style="font-size: 12px; color: #24b837">
{{
scope.row.uploadState.record.Uploaded.length
}}
</span>
/
<span style="font-size: 12px; color: #f66">{{
scope.row.uploadState.record.Failed.length
}}</span>
<span style="font-size: 12px; color: #f66">
{{
scope.row.uploadState.record.Failed.length
}}
</span>
</el-button>
</el-tooltip>
</template>
@@ -302,7 +304,8 @@
</el-table>
<study-view v-if="model_cfg.visible" :model_cfg="model_cfg" :IsDicom="true" :bodyPart="bodyPart"
:subjectVisitId="openSubjectVisitId" :modelList="modelList" :isUpload="openIsUpload"
:visitTaskId="openVisitTaskId" :TrialModality="TrialModality" @getList="getList" />
:visitTaskId="openVisitTaskId" :TrialModality="TrialModality" :IsReadingTaskViewInOrder="IsReadingTaskViewInOrder"
@getList="getList" />
</div>
</template>
<script>
@@ -335,6 +338,7 @@ cornerstoneWADOImageLoader.external.cornerstone = cornerstone
import { convertBytes } from '@/utils/dicom-character-set'
import { parseDicom } from '@/utils/parseDicom.js'
import { dcmUpload } from '@/utils/dcmUpload/dcmUpload'
import dcmjs from '@/utils/dcmUpload/dcmjs'
import store from '@/store'
import { getToken } from '@/utils/auth'
export default {
@@ -373,6 +377,14 @@ export default {
IsImageSegment: {
type: Boolean,
default: false,
},
isReading: {
type: Boolean,
default: false,
},
StudyInstanceUID: {
type: String,
default: '',
}
},
components: {
@@ -423,6 +435,8 @@ export default {
openSubjectVisitId: null,
openVisitTaskId: null,
TrialModality: [],
IsReadingTaskViewInOrder: 2,
isDownloaded: false
}
},
async mounted() {
@@ -455,15 +469,19 @@ export default {
}
if (this.IsImageSegment) {
params.IsImageSegmentLabel = true
}
else if (this.Criterion.CriterionType == 19 || this.Criterion.CriterionType == 20) {
} else if (
this.Criterion.CriterionType == 19 ||
this.Criterion.CriterionType == 20
) {
params.IsImageSegmentLabel = false
}
this.loading = true
let res = await getSubjectImageUploadList(params)
this.loading = false
if (res.IsSuccess) {
this.isDownloaded = res.OtherInfo.IsIRImageDownloaded
this.TrialModality = res.OtherInfo.TrialModality.split('|')
this.IsReadingTaskViewInOrder = res.OtherInfo.IsReadingTaskViewInOrder || res.OtherInfo.IsReadingTaskViewInOrder === 0 ? res.OtherInfo.IsReadingTaskViewInOrder : 2
this.StudyInstanceUidList = []
this.SopInstanceUidList = []
this.UploadStudyList = []
@@ -543,7 +561,9 @@ export default {
this.openIsUpload = isUpload
this.openSubjectVisitId = item.SubjectVisitId || item.SourceSubjectVisitId
this.openVisitTaskId = item.VisitTaskId
this.model_cfg.title = `${item.SubjectCode || ''} > ${this.IsImageSegment ? item.VisitName : item.TaskBlindName}`
this.model_cfg.title = `${item.SubjectCode || ''} > ${this.IsImageSegment ? item.VisitName : item.TaskBlindName
}`
if (this.isReadingTaskViewInOrder === 0) this.model_cfg.title = ''
this.modelList = item[list]
this.model_cfg.visible = true
},
@@ -645,7 +665,10 @@ export default {
for (let i = 0; i < checkFiles.length; i++) {
let item = checkFiles[i]
var dicom = await parseDicom(item, ['StudyInstanceUid', 'Modality'])
if (!!~this.errStudyUidList.indexOf(dicom.StudyInstanceUid) || (this.IsImageSegment && dicom.Modality !== 'IVUS')) {
if (
!!~this.errStudyUidList.indexOf(dicom.StudyInstanceUid) ||
(this.IsImageSegment && dicom.Modality !== 'IVUS')
) {
this.hasOtherStudy = true
checkFiles.splice(i, 1)
i--
@@ -712,7 +735,8 @@ export default {
var studyUid = data.string('x0020000d')
if (!studyUid) return resolve()
var pixelDataElement = data.elements.x7fe00010
if (!pixelDataElement && modality !== 'SR' && modality !== 'ECG') return resolve()
if (!pixelDataElement && modality !== 'SR' && modality !== 'ECG')
return resolve()
var studyIndex = 0
while (
studyIndex < scope.uploadQueues.length &&
@@ -809,6 +833,39 @@ export default {
},
})
}
if (
!scope.uploadQueues[studyIndex].dicomInfo.RadionuclideTotalDose
) {
let dataset = dcmjs.data.DicomMessage.readFile(e.target.result)
if (
dataset.dict['00540016'] &&
dataset.dict['00540016'].Value &&
dataset.dict['00540016'].Value[0]
) {
let RadionuclideTotalDose = dataset.dict['00540016'].Value[0][
'00181074'
]
? dataset.dict['00540016'].Value[0]['00181074'].Value[0]
: null
let RadionuclideHalfLife = dataset.dict['00540016'].Value[0][
'00181075'
]
? dataset.dict['00540016'].Value[0]['00181075'].Value[0]
: null
let RadiopharmaceuticalStartTime = dataset.dict['00540016']
.Value[0]['00181072']
? dataset.dict['00540016'].Value[0]['00181072'].Value[0]
: null
scope.uploadQueues[studyIndex].dicomInfo.RadionuclideTotalDose =
RadionuclideTotalDose
scope.uploadQueues[studyIndex].dicomInfo.RadionuclideHalfLife =
RadionuclideHalfLife
scope.uploadQueues[
studyIndex
].dicomInfo.RadiopharmaceuticalStartTime =
RadiopharmaceuticalStartTime
}
}
var modality = scope.uploadQueues[studyIndex].dicomInfo.modality
var currentModality = data.string('x00080060')
if (!(modality.indexOf(currentModality) > -1)) {
@@ -876,8 +933,9 @@ export default {
)
seriesItem = {
seriesUid: seriesUid,
RadiopharmaceuticalInformationSequence: data.string('x00540016') || "",
AcquisitionDate: data.string('x00080022') || "",
RadiopharmaceuticalInformationSequence:
data.string('x00540016') || '',
AcquisitionDate: data.string('x00080022') || '',
DicomSeriesDate: data.string('x00080021'),
DicomSeriesTime: data.string('x00080031'),
@@ -899,7 +957,7 @@ export default {
var instanceItem = instanceList.find(function (item) {
return item.instanceUid === instanceUid
})
if (!instanceItem) {
if (!false) {
var date = data.string('x00080023')
var time = data.string('x00080033')
var instanceTime = ''
@@ -1028,11 +1086,12 @@ export default {
async verifyStudy() {
this.btnLoading = true
var studyList = []
let scope = this
this.selectArr.forEach((item) => {
item.dicomInfo.uploadFileSize = 0
if (!item.uploadState.selected) {
studyList.push({
studyInstanceUid: item.dicomInfo.studyUid,
studyInstanceUid: scope.isReading && scope.StudyInstanceUID ? scope.StudyInstanceUID : item.dicomInfo.studyUid,
studyDate: item.dicomInfo.studyTime,
})
}
@@ -1085,16 +1144,20 @@ export default {
var scope = this
return new Promise(function (resolve, reject) {
try {
let subjectVisitId = null;
let subjectVisitId = null
let dicomInfo = {}
if (scope.VisitTaskId) {
scope.StudyInstanceUidList.forEach(item => {
scope.StudyInstanceUidList.forEach((item) => {
if (item.VisitTaskId === scope.VisitTaskId) {
subjectVisitId = item.SourceSubjectVisitId
}
})
} else {
scope.StudyInstanceUidList.forEach(item => {
if (item.StudyInstanceUid === scope.uploadQueues[index].dicomInfo.studyUid) {
scope.StudyInstanceUidList.forEach((item) => {
if (
item.StudyInstanceUid ===
scope.uploadQueues[index].dicomInfo.studyUid
) {
subjectVisitId = item.SourceSubjectVisitId
}
})
@@ -1112,7 +1175,7 @@ export default {
.then(async (res) => {
scope.uploadQueues[index].dicomInfo.failedFileCount = 0
scope.$set(scope.uploadQueues[index].dicomInfo, 'isInit', true)
let dicomInfo = scope.uploadQueues[index].dicomInfo
dicomInfo = scope.uploadQueues[index].dicomInfo
let seriesNum = scope.uploadQueues[index].seriesList.length
let fileNum = scope.uploadQueues[index].fileList.length
let seriesList = scope.uploadQueues[index].seriesList
@@ -1123,7 +1186,7 @@ export default {
let t = setInterval(() => {
dicomUploadInProgress({
trialId: scope.trialId,
studyInstanceUid: dicomInfo.studyUid,
studyInstanceUid: scope.isReading && scope.StudyInstanceUID ? scope.StudyInstanceUID : dicomInfo.studyUid,
VisitTaskId: dicomInfo.visitTaskId,
}).then((res) => {
console.log(dicomInfo.visitTaskId)
@@ -1144,8 +1207,13 @@ export default {
failedFileCount: 0,
RecordPath: null,
study: {
RadionuclideTotalDose: dicomInfo.RadionuclideTotalDose,
RadionuclideHalfLife: dicomInfo.RadionuclideHalfLife,
RadiopharmaceuticalStartTime:
dicomInfo.RadiopharmaceuticalStartTime,
studyId: dicomInfo.studyId,
studyInstanceUid: dicomInfo.studyUid,
studyInstanceUid: scope.isReading && scope.StudyInstanceUID ? scope.StudyInstanceUID : dicomInfo.studyUid,
studyTime: dicomInfo.studyTime,
description: dicomInfo.description,
seriesCount: dicomInfo.seriesNum,
@@ -1153,8 +1221,8 @@ export default {
institutionName: dicomInfo.institutionName,
patientId: dicomInfo.patientId,
patientName: '',
patientAge: '',
patientSex: dicomInfo.patientSex,
patientAge: dicomInfo.patientAge,
patientSex: config?.DicomStoreInfo.SubjectSex || dicomInfo.patientSex,
accessionNumber: dicomInfo.accNumber,
patientBirthDate: '',
acquisitionTime: dicomInfo.acquisitionTime,
@@ -1186,20 +1254,171 @@ export default {
try {
let o = v.instanceList[ii]
let name = `${v.instanceList[ii].file.webkitRelativePath}_${v.instanceList[ii].instanceUid}`
if (o.isReUpload) {
dicomInfo.failedFileCount++
dicomInfo.uploadFileSize += o.file.size
Record.Existed.push(name)
Record.FileCount++
} else if (o.myPath) {
// if (o.isReUpload) {
// dicomInfo.failedFileCount++
// dicomInfo.uploadFileSize += o.file.size
// Record.Existed.push(name)
// Record.FileCount++
// }
// else if (o.myPath) {
// instanceList.push({
// studyInstanceUid: dicomInfo.studyUid,
// seriesInstanceUid: v.seriesUid,
// SOPClassUID: o.SOPClassUID,
// TransferSytaxUID: o.TransferSytaxUID,
// MediaStorageSOPInstanceUID:
// o.MediaStorageSOPInstanceUID,
// MediaStorageSOPClassUID:
// o.MediaStorageSOPClassUID,
// sopInstanceUid: o.instanceUid,
// instanceNumber: o.instanceNumber,
// instanceTime: o.instanceTime,
// imageRows: o.imageRows,
// imageColumns: o.imageColumns,
// sliceLocation: o.sliceLocation,
// sliceThickness: o.sliceThickness,
// numberOfFrames: o.numberOfFrames,
// pixelSpacing: o.pixelSpacing,
// imagerPixelSpacing: o.imagerPixelSpacing,
// frameOfReferenceUID: o.frameOfReferenceUID,
// windowCenter: o.windowCenter,
// windowWidth: o.windowWidth,
// path: o.myPath,
// FileSize: o.FileSize,
// PhotometricInterpretation:
// o.PhotometricInterpretation,
// BitsAllocated: o.BitsAllocated,
// PixelRepresentation: o.PixelRepresentation,
// RescaleIntercept: o.RescaleIntercept,
// RescaleSlope: o.RescaleSlope,
// ImagePositionPatient: o.ImagePositionPatient,
// ImageOrientationPatient:
// o.ImageOrientationPatient,
// SequenceOfUltrasoundRegions:
// o.SequenceOfUltrasoundRegions,
// FrameTime: o.FrameTime,
// CorrectedImage: o.CorrectedImage,
// Units: o.Units,
// DecayCorrection: o.DecayCorrection,
// EncapsulatedDocument: o.EncapsulatedDocument,
// })
// Record.Uploaded.push(name)
// dicomInfo.failedFileCount++
// Record.FileCount++
// }
// else {
let path = `/${params.trialId}/Image/${params.subjectId
}/${params.subjectVisitId}/${dicomInfo.visitTaskId
}/${scope.getGuid(
dicomInfo.studyUid +
v.seriesUid +
o.instanceUid +
params.trialId
)}`
if (scope.IsImageSegment) {
path = `/${params.trialId}/Image/${params.subjectId
}/${params.subjectVisitId}/AnnotationImage/${dicomInfo.visitTaskId
}/${scope.getGuid(
dicomInfo.studyUid +
v.seriesUid +
o.instanceUid +
params.trialId
)}`
}
if (scope.isClose) return
let res = await dcmUpload(
{
path: path,
file: o.file,
speed: true,
},
scope.isReading && scope.StudyInstanceUID ? {
AnonymizeFixedList: [
{
Element: '000D',
Group: '0020',
ReplaceValue: scope.StudyInstanceUID,
Id: 'StudyInstanceUID'
}
],
AnonymizeNotFixedList: [],
DicomStoreInfo: {}
} : null,
(percentage, checkpoint, lastPer) => {
dicomInfo.uploadFileSize +=
checkpoint.size * (percentage - lastPer)
if (
dicomInfo.uploadFileSize > dicomInfo.fileSize
) {
dicomInfo.uploadFileSize = dicomInfo.fileSize
}
},
{
fileName: o.file.name,
fileSize: o.file.size,
fileType: 'application/dicom',
uploadBatchId: uploadBatchId,
batchDataType: 5,
trialId: params.trialId,
subjectId: params.subjectId,
subjectVisitId: params.subjectVisitId,
}
)
if (!res || !res.url) {
params.failedFileCount++
} else {
if (ii === 0 && o.modality !== 'SR') {
try {
let fileId =
cornerstoneWADOImageLoader.wadouri.fileManager.add(
o.file
)
let blob = await scope.dicomToPng(
fileId,
o.imageColumns,
o.imageRows
)
let thumbnailPath = `/${params.trialId}/Image/${params.subjectId}/${params.subjectVisitId}/${dicomInfo.visitTaskId}/${dicomInfo.studyUid}/${v.seriesUid}.jpg`
if (scope.IsImageSegment) {
thumbnailPath = `/${params.trialId}/Image/${params.subjectId}/${params.subjectVisitId}/AnnotationImage/${dicomInfo.visitTaskId}/${dicomInfo.studyUid}/${v.seriesUid}.jpg`
}
let OSSclient = scope.OSSclient
let seriesRes = await OSSclient.put(
thumbnailPath,
blob,
{
fileName: `${v.seriesUid}.jpg`,
fileSize: blob.size,
fileType: 'image/jpeg',
uploadBatchId: uploadBatchId,
batchDataType: 6,
trialId: params.trialId,
subjectId: params.subjectId,
subjectVisitId: params.subjectVisitId,
}
)
if (seriesRes && seriesRes.url) {
ImageResizePath = scope.$getObjectName(
seriesRes.url
)
}
} catch (e) {
console.log(e)
}
}
}
if (res && res.url) {
instanceList.push({
studyInstanceUid: dicomInfo.studyUid,
studyInstanceUid: scope.isReading && scope.StudyInstanceUID ? scope.StudyInstanceUID : dicomInfo.studyUid,
seriesInstanceUid: v.seriesUid,
sopInstanceUid: o.instanceUid,
SOPClassUID: o.SOPClassUID,
TransferSytaxUID: o.TransferSytaxUID,
MediaStorageSOPInstanceUID: o.MediaStorageSOPInstanceUID,
MediaStorageSOPClassUID: o.MediaStorageSOPClassUID,
sopInstanceUid: o.instanceUid,
MediaStorageSOPInstanceUID:
o.MediaStorageSOPInstanceUID,
MediaStorageSOPClassUID:
o.MediaStorageSOPClassUID,
instanceNumber: o.instanceNumber,
instanceTime: o.instanceTime,
imageRows: o.imageRows,
@@ -1212,163 +1431,35 @@ export default {
frameOfReferenceUID: o.frameOfReferenceUID,
windowCenter: o.windowCenter,
windowWidth: o.windowWidth,
path: o.myPath,
path: scope.$getObjectName(res.url),
FileSize: o.FileSize,
PhotometricInterpretation: o.PhotometricInterpretation,
PhotometricInterpretation:
o.PhotometricInterpretation,
BitsAllocated: o.BitsAllocated,
PixelRepresentation: o.PixelRepresentation,
RescaleIntercept: o.RescaleIntercept,
RescaleSlope: o.RescaleSlope,
ImagePositionPatient: o.ImagePositionPatient,
ImageOrientationPatient: o.ImageOrientationPatient,
SequenceOfUltrasoundRegions: o.SequenceOfUltrasoundRegions,
ImageOrientationPatient:
o.ImageOrientationPatient,
SequenceOfUltrasoundRegions:
o.SequenceOfUltrasoundRegions,
FrameTime: o.FrameTime,
CorrectedImage: o.CorrectedImage,
Units: o.Units,
DecayCorrection: o.DecayCorrection,
EncapsulatedDocument: o.EncapsulatedDocument,
})
o.myPath = scope.$getObjectName(res.url)
Record.Uploaded.push(name)
dicomInfo.failedFileCount++
Record.FileCount++
} else {
let path = `/${params.trialId}/Image/${params.subjectId
}/${params.subjectVisitId}/${dicomInfo.visitTaskId
}/${scope.getGuid(
dicomInfo.studyUid +
v.seriesUid +
o.instanceUid +
params.trialId
)}`
if (scope.IsImageSegment) {
path = `/${params.trialId}/Image/${params.subjectId
}/${params.subjectVisitId}/AnnotationImage/${dicomInfo.visitTaskId
}/${scope.getGuid(
dicomInfo.studyUid +
v.seriesUid +
o.instanceUid +
params.trialId
)}`
}
if (scope.isClose) return
let res = await dcmUpload(
{
path: path,
file: o.file,
speed: true,
},
null,
(percentage, checkpoint, lastPer) => {
dicomInfo.uploadFileSize +=
checkpoint.size * (percentage - lastPer)
if (
dicomInfo.uploadFileSize > dicomInfo.fileSize
) {
dicomInfo.uploadFileSize = dicomInfo.fileSize
}
},
{
fileName: o.file.name,
fileSize: o.file.size,
fileType: 'application/dicom',
uploadBatchId: uploadBatchId,
batchDataType: 5,
trialId: params.trialId,
subjectId: params.subjectId,
subjectVisitId: params.subjectVisitId
}
)
if (!res || !res.url) {
params.failedFileCount++
} else {
if (ii === 0 && o.modality !== 'SR') {
try {
let fileId =
cornerstoneWADOImageLoader.wadouri.fileManager.add(
o.file
)
let blob = await scope.dicomToPng(
fileId,
o.imageColumns,
o.imageRows
)
let thumbnailPath = `/${params.trialId}/Image/${params.subjectId}/${params.subjectVisitId}/${dicomInfo.visitTaskId}/${dicomInfo.studyUid}/${v.seriesUid}.jpg`
if (scope.IsImageSegment) {
thumbnailPath = `/${params.trialId}/Image/${params.subjectId}/${params.subjectVisitId}/AnnotationImage/${dicomInfo.visitTaskId}/${dicomInfo.studyUid}/${v.seriesUid}.jpg`
}
let OSSclient = scope.OSSclient
let seriesRes = await OSSclient.put(
thumbnailPath,
blob,
{
fileName: `${v.seriesUid}.jpg`,
fileSize: blob.size,
fileType: 'image/jpeg',
uploadBatchId: uploadBatchId,
batchDataType: 6,
trialId: params.trialId,
subjectId: params.subjectId,
subjectVisitId: params.subjectVisitId
}
)
if (seriesRes && seriesRes.url) {
ImageResizePath = scope.$getObjectName(
seriesRes.url
)
}
} catch (e) {
console.log(e)
}
}
}
if (res && res.url) {
instanceList.push({
studyInstanceUid: dicomInfo.studyUid,
seriesInstanceUid: v.seriesUid,
sopInstanceUid: o.instanceUid,
SOPClassUID: o.SOPClassUID,
TransferSytaxUID: o.TransferSytaxUID,
MediaStorageSOPInstanceUID: o.MediaStorageSOPInstanceUID,
MediaStorageSOPClassUID: o.MediaStorageSOPClassUID,
instanceNumber: o.instanceNumber,
instanceTime: o.instanceTime,
imageRows: o.imageRows,
imageColumns: o.imageColumns,
sliceLocation: o.sliceLocation,
sliceThickness: o.sliceThickness,
numberOfFrames: o.numberOfFrames,
pixelSpacing: o.pixelSpacing,
imagerPixelSpacing: o.imagerPixelSpacing,
frameOfReferenceUID: o.frameOfReferenceUID,
windowCenter: o.windowCenter,
windowWidth: o.windowWidth,
path: scope.$getObjectName(res.url),
FileSize: o.FileSize,
PhotometricInterpretation: o.PhotometricInterpretation,
BitsAllocated: o.BitsAllocated,
PixelRepresentation: o.PixelRepresentation,
RescaleIntercept: o.RescaleIntercept,
RescaleSlope: o.RescaleSlope,
ImagePositionPatient: o.ImagePositionPatient,
ImageOrientationPatient: o.ImageOrientationPatient,
SequenceOfUltrasoundRegions: o.SequenceOfUltrasoundRegions,
FrameTime: o.FrameTime,
CorrectedImage: o.CorrectedImage,
Units: o.Units,
DecayCorrection: o.DecayCorrection,
EncapsulatedDocument: o.EncapsulatedDocument,
})
o.myPath = scope.$getObjectName(res.url)
Record.Uploaded.push(name)
dicomInfo.failedFileCount++
Record.FileCount++
} else {
Record.Failed.push(name)
Record.FileCount++
}
Record.Failed.push(name)
Record.FileCount++
}
// }
resolve1()
} catch (e) {
console.log(e)
@@ -1386,7 +1477,7 @@ export default {
}
}
params.study.seriesList.push({
studyInstanceUid: dicomInfo.studyUid,
studyInstanceUid: scope.isReading && scope.StudyInstanceUID ? scope.StudyInstanceUID : dicomInfo.studyUid,
seriesInstanceUid: v.seriesUid,
seriesNumber: v.seriesNumber,
seriesTime: v.seriesTime,
@@ -1408,7 +1499,8 @@ export default {
instanceList: instanceList,
ImageResizePath: ImageResizePath,
RadiopharmaceuticalInformationSequence: v.RadiopharmaceuticalInformationSequence,
RadiopharmaceuticalInformationSequence:
v.RadiopharmaceuticalInformationSequence,
AcquisitionDate: v.AcquisitionDate,
})
}
@@ -1451,20 +1543,16 @@ export default {
}
let OSSclient = scope.OSSclient
try {
let seriesRes = await OSSclient.put(
thumbnailPath,
blob,
{
fileName: `${v.seriesUid}.jpg`,
fileSize: blob.size,
fileType: 'image/jpeg',
uploadBatchId: uploadBatchId,
batchDataType: 6,
trialId: params.trialId,
subjectId: params.subjectId,
subjectVisitId: params.subjectVisitId
}
)
let seriesRes = await OSSclient.put(thumbnailPath, blob, {
fileName: `${v.seriesUid}.jpg`,
fileSize: blob.size,
fileType: 'image/jpeg',
uploadBatchId: uploadBatchId,
batchDataType: 6,
trialId: params.trialId,
subjectId: params.subjectId,
subjectVisitId: params.subjectVisitId,
})
if (seriesRes && seriesRes.url) {
o.ImageResizePath = scope.$getObjectName(seriesRes.url)
}
@@ -1577,7 +1665,9 @@ export default {
var token = getToken()
let trialId = this.$route.query.trialId
const routeData = this.$router.resolve({
path: `/showvisitdicoms?page=upload&trialId=${trialId}&visitTaskId=${this.IsImageSegment ? 'undefined' : row.VisitTaskId}&subjectVisitId=${row.SourceSubjectVisitId}&isReading=1&TokenKey=${token}`,
path: `/showvisitdicoms?page=upload&trialId=${trialId}&visitTaskId=${this.IsImageSegment ? 'undefined' : row.VisitTaskId
}&subjectVisitId=${row.SourceSubjectVisitId
}&isReading=1&TokenKey=${token}&IsReadingTaskViewInOrder=${this.isReadingTaskViewInOrder}`,
})
this.open = window.open(routeData.href, '_blank')
},
@@ -6,12 +6,13 @@
<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" :IsImageSegment="IsImageSegment" :forbid="forbid" />
:isReadingTaskViewInOrder="isReadingTaskViewInOrder" :IsImageSegment="IsImageSegment" :forbid="forbid"
:isReading="isReading" :StudyInstanceUID="StudyInstanceUID" />
</el-tab-pane>
<el-tab-pane :label="$t('uploadDicomAndNonedicom:label:nonedicom')" name="nonedicom">
<nonedicomFile v-if="activeName === 'nonedicom'" :SubjectId="SubjectId" :SubjectCode="SubjectCode"
:Criterion="Criterion" :VisitTaskId="VisitTaskId" :isUpload.sync="isUpload" :IsImageSegment="IsImageSegment"
:forbid="forbid" />
:isReadingTaskViewInOrder="isReadingTaskViewInOrder" :forbid="forbid" :isReading="isReading" />
</el-tab-pane>
</el-tabs>
</el-dialog>
@@ -58,6 +59,14 @@ export default {
IsImageSegment: {
type: Boolean,
default: false,
},
isReading: {
type: Boolean,
default: false,
},
StudyInstanceUID: {
type: String,
default: '',
}
},
data() {
@@ -70,6 +79,7 @@ export default {
mounted() {
this.title = `Upload Images${this.SubjectCode}${this.Criterion.TrialReadingCriterionName}`
if (this.IsImageSegment) this.title = `Upload Images${this.SubjectCode}`
if (this.isReadingTaskViewInOrder === 0) this.title = ''
store.dispatch('trials/setUnLock', true)
},
methods: {
@@ -14,7 +14,8 @@
<el-table :data="list" style="width: 100%" v-adaptive="{ bottomOffset: 60 }" :loading="loading"
:default-sort="{ prop: 'TaskBlindName', order: 'descending' }">
<!--受试者 sortable="custom"-->
<el-table-column prop="SubjectCode" :label="$t('upload:nonedicom:table:subject')" />
<el-table-column prop="SubjectCode" :label="$t('upload:nonedicom:table:subject')"
v-if="isReadingTaskViewInOrder !== 0" />
<!--访视名称-->
<el-table-column prop="VisitName" :label="$t('upload:nonedicom:table:VisitName')" sortable
v-if="IsImageSegment" />
@@ -110,11 +111,9 @@
:title="$t('upload:nonedicom:button:preview')" @click.stop="handlePreviewNoneDicomFiles(scope.row)" />
<!--上传--->
<el-button circle icon="el-icon-upload2" :title="$t('upload:nonedicom:button:upload')" v-if="!forbid"
@click.native.prevent="handleUpload(scope.row)" />
@click.native.prevent="handleUpload(scope.row)" :disabled="isReading && !isDownloaded" />
<!--删除--->
<el-button :disabled="scope.row.UploadedFileCount <= 0 ||
scope.row.ReadingTaskState === 2
" circle icon="el-icon-delete" :title="$t('upload:nonedicom:button:delete')"
<el-button circle icon="el-icon-delete" :title="$t('upload:nonedicom:button:delete')"
@click.stop="remove(scope.row)" />
</template>
</el-table-column>
@@ -265,6 +264,14 @@ export default {
type: Boolean,
default: false,
},
isReadingTaskViewInOrder: {
type: Number,
default: 0,
},
isReading: {
type: Boolean,
default: false,
}
},
data() {
return {
@@ -286,7 +293,8 @@ export default {
BodyPart: {},
relationInfo: {
ImageFormatList: []
}
},
isDownloaded: false
}
},
watch: {
@@ -343,6 +351,7 @@ export default {
if (res.IsSuccess) {
this.list = res.Result
this.relationInfo = res.OtherInfo
this.isDownloaded = res.OtherInfo.IsIRImageDownloaded
this.faccept = []
this.relationInfo.ImageFormatList.forEach((item) => {
this.faccept.push(`.${item}`)
@@ -475,7 +484,7 @@ export default {
let trialId = this.$route.query.trialId
var token = getToken()
const routeData = this.$router.resolve({
path: `/showNoneDicoms?trialId=${trialId}&subjectVisitId=${row.SourceSubjectVisitId}&studyId=${row.Id}&visitTaskId=${row.VisitTaskId}&TokenKey=${token}`,
path: `/showNoneDicoms?trialId=${trialId}&subjectVisitId=${row.SourceSubjectVisitId}&studyId=${row.Id}&visitTaskId=${row.VisitTaskId}&TokenKey=${token}&IsReadingTaskViewInOrder=${this.isReadingTaskViewInOrder}`,
})
this.open = window.open(routeData.href, '_blank')
},
@@ -7,7 +7,8 @@
</el-button>
<el-table :data="modelList" style="width: 100%" height="300">
<!--检查编号-->
<el-table-column prop="StudyCode" :label="$t('trials:uploadImage:table:StudyCode')" />
<el-table-column prop="StudyCode" :label="$t('trials:uploadImage:table:StudyCode')"
v-if="IsReadingTaskViewInOrder !== 0" />
<!--检查类型-->
<el-table-column prop="ModalityForEdit" :label="$t('trials:uploadImage:table:ModalityForEdit')"
v-if="IsDicom" />
@@ -139,6 +140,10 @@ export default {
return {}
},
},
IsReadingTaskViewInOrder: {
type: Number,
default: 2,
}
},
components: {
'base-model': baseModel,
@@ -176,18 +181,18 @@ export default {
var token = getToken()
let trialId = this.$route.query.trialId
routeData = this.$router.resolve({
path: `/showvisitdicoms?trialId=${trialId}&visitTaskId=${this.visitTaskId}&subjectVisitId=${this.subjectVisitId}&isReading=1&TokenKey=${token}`,
path: `/showvisitdicoms?trialId=${trialId}&visitTaskId=${this.visitTaskId}&subjectVisitId=${this.subjectVisitId}&isReading=1&TokenKey=${token}&IsReadingTaskViewInOrder=${this.IsReadingTaskViewInOrder}`,
})
} else {
let trialId = this.$route.query.trialId
var token = getToken()
if (!this.IsImageSegment && (this.Criterion.CriterionType == 19 || this.Criterion.CriterionType == 20)) {
routeData = this.$router.resolve({
path: `/showNoneDicoms?trialId=${trialId}&isImageSegmentLabel=${false}&visitTaskId=${this.visitTaskId}&subjectVisitId=${this.subjectVisitId}&TokenKey=${token}&isReading=true`,
path: `/showNoneDicoms?trialId=${trialId}&isImageSegmentLabel=${false}&visitTaskId=${this.visitTaskId}&subjectVisitId=${this.subjectVisitId}&TokenKey=${token}&isReading=true&IsReadingTaskViewInOrder=${this.IsReadingTaskViewInOrder}`,
})
} else {
routeData = this.$router.resolve({
path: `/showNoneDicoms?trialId=${trialId}&visitTaskId=${this.visitTaskId}&subjectVisitId=${this.subjectVisitId}&TokenKey=${token}&isReading=true`,
path: `/showNoneDicoms?trialId=${trialId}&visitTaskId=${this.visitTaskId}&subjectVisitId=${this.subjectVisitId}&TokenKey=${token}&isReading=true&IsReadingTaskViewInOrder=${this.IsReadingTaskViewInOrder}`,
})
}
+1
View File
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1777010831812" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1646" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512 896c-230.058667 0-421.461333-165.546667-461.610667-384a468.565333 468.565333 0 0 1 142.506667-258.816L89.6 149.973333A42.666667 42.666667 0 1 1 149.930667 89.6L934.4 874.112a42.666667 42.666667 0 0 1-60.373333 60.330667l-111.061334-111.061334A466.901333 466.901333 0 0 1 512 896zM253.226667 313.6A382.506667 382.506667 0 0 0 137.514667 512a384.213333 384.213333 0 0 0 563.242666 249.088l-86.528-86.528A192 192 0 0 1 349.44 409.770667L253.226667 313.6z m297.770666 297.728l-138.325333-138.325333a106.666667 106.666667 0 0 0 138.282667 138.282666l0.042666 0.042667z m285.312 22.357333l10.24-18.218666A381.226667 381.226667 0 0 0 886.485333 512a384.213333 384.213333 0 0 0-440.661333-292.949333c-2.944 0.512-9.173333 1.877333-18.730667 4.096a42.88 42.88 0 1 1-19.2-83.541334A470.912 470.912 0 0 1 512 128c230.058667 0 421.461333 165.546667 461.610667 384a467.072 467.072 0 0 1-57.685334 153.856 94.933333 94.933333 0 0 1-8.021333 11.178667 44.373333 44.373333 0 0 1-58.794667 9.088 39.552 39.552 0 0 1-12.8-52.437334z m-188.501333-257.493333a192 192 0 0 1 55.850667 147.626667l-203.52-203.477334a192 192 0 0 1 147.626666 55.893334z" fill="#e6e6e6" p-id="1647"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

+1 -1
View File
@@ -1 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1751350116150" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="20392" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M870.4 819.2a51.2 51.2 0 1 1 0 102.4 51.2 51.2 0 0 1 0-102.4zM512 179.2a332.8 332.8 0 1 1 0 665.6 332.8 332.8 0 0 1 0-665.6z m0 51.2a281.6 281.6 0 1 0 0 563.2 281.6 281.6 0 0 0 0-563.2zM153.6 102.4a51.2 51.2 0 1 1 0 102.4 51.2 51.2 0 0 1 0-102.4z" fill="#ADAEB8" p-id="20393"></path></svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1597744457882" class="icon" viewBox="0 0 1170 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10125" width="285.64453125" height="250" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"></style></defs><path d="M926.216214 404.588947l1.511115-151.84272H1079.570048c18.035893 0 33.098301-15.062408 33.098301-33.0983a33.390775 33.390775 0 0 0-33.098301-33.098301H927.727329V33.195792a33.390775 33.390775 0 0 0-33.049555-33.049555 33.390775 33.390775 0 0 0-33.0983 33.049555c0 3.022231 0 4.533346 1.511115 7.555577v144.287142H711.24787a33.390775 33.390775 0 0 0-33.098301 33.147046c0 18.035893 15.062408 33.049555 33.098301 33.049555H863.090589v139.802542c0 3.022231-1.46237 4.533346-1.46237 7.506831 0 18.035893 15.013662 33.098301 33.049555 33.098301 15.013662 1.46237 28.564955-10.529062 31.53844-27.053839zM1032.969201 790.995763a29.393631 29.393631 0 0 0-22.520493 10.480316 546.000095 546.000095 0 0 1-117.282049 88.717095 29.101157 29.101157 0 0 0-15.062408 25.591469c0 16.524777 13.551292 30.027324 30.07607 30.027324 4.533346 0 10.529062-1.46237 13.551292-2.973485 1.46237 0 1.46237-1.511115 1.46237-1.511115a523.235874 523.235874 0 0 0 127.811111-97.735041c6.044461-5.995716 10.529062-13.551292 10.529061-22.569238a28.028752 28.028752 0 0 0-28.564954-30.027325zM793.920503 935.282906c-2.973485 0-7.506831 0-10.529062 1.511115-46.600847 15.062408-96.223925 24.080354-145.798258 27.053839-15.062408 1.511115-25.59147 15.062408-25.591469 30.07607 0 16.573523 13.551292 30.07607 30.07607 30.07607 55.618794-4.4846 108.215357-13.502547 159.398295-30.07607a28.95492 28.95492 0 0 0 22.520494-28.564954 30.173561 30.173561 0 0 0-30.07607-30.07607zM535.324796 960.874375c-12.040177-1.511115-24.031609-1.511115-36.071785-3.02223a564.962155 564.962155 0 0 1-49.623078-7.506832c-230.030752-45.089732-389.380302-202.976912-389.380302-380.362355 0-139.851288 99.19741-269.124768 260.106822-339.805969 13.502547-5.995716 27.053839-12.040177 40.556386-16.573523 10.529062-4.4846 22.569239-7.506831 33.0983-10.480317 12.040177-4.533346 21.058123-15.062408 21.058124-28.564954a30.173561 30.173561 0 0 0-30.07607-30.07607 25.396487 25.396487 0 0 0-15.062408 4.4846c-10.529062 3.022231-19.498262 6.044461-28.564955 9.017947l-45.089732 18.035893C114.357309 254.257343 0.097491 406.148808 0.097491 568.471843c0 207.510258 180.407674 387.917932 439.00338 440.563241l54.107679 9.017946c12.040177 1.46237 25.59147 3.022231 37.631646 3.022231h1.46237c16.573523 0 30.07607-13.551292 30.07607-30.07607a30.758509 30.758509 0 0 0-27.05384-30.07607zM1169.798258 569.982958a30.173561 30.173561 0 0 0-30.07607-30.07607 30.173561 30.173561 0 0 0-30.07607 30.07607v1.46237c0 46.649593-10.529062 93.25044-33.049555 136.829057-1.511115 3.022231-1.511115 7.506831-1.511115 12.040177 0 16.573523 13.551292 30.07607 30.07607 30.07607 13.502547 0 24.031609-9.017946 28.564954-21.058124a361.985243 361.985243 0 0 0 36.071786-157.88718v-1.46237z" p-id="10126"></path><path d="M1052.516209 297.835959c-5.995716-4.533346-13.502547-9.017946-22.520493-9.017946a30.173561 30.173561 0 0 0-30.07607 30.07607c0 7.506831 2.973485 15.013662 9.017946 21.009378a375.14657 375.14657 0 0 1 79.699148 118.793164c4.4846 10.529062 15.013662 19.547008 27.053839 19.547008 16.524777 0 30.07607-13.551292 30.07607-30.07607 0-4.4846-1.511115-7.506831-3.02223-12.040177a422.039891 422.039891 0 0 0-90.179464-138.291427zM666.158138 182.065026h4.4846c16.573523 0 30.07607-13.551292 30.07607-30.07607 0-15.013662-12.040177-28.564955-27.053839-28.564954v-1.46237a761.602133 761.602133 0 0 0-154.86495-1.559861h-3.022231a30.173561 30.173561 0 0 0-30.07607 30.07607c0 16.573523 13.551292 30.07607 30.07607 30.07607h3.022231v1.462369c46.600847-5.94697 97.735041-4.4846 147.358119 0z" p-id="10127"></path></svg>

Before

Width:  |  Height:  |  Size: 623 B

After

Width:  |  Height:  |  Size: 3.8 KiB

+1
View File
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1777014169186" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4736" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M1024 739.560727V1024H739.560727v-84.898909h199.540364v-199.540364H1024z m-939.101091 0v199.540364h199.540364V1024H0V739.560727h84.898909zM824.878545 199.121455v625.75709H199.121455V199.121455h625.75709z m-85.317818 85.317818H284.439273v455.121454h455.121454V284.439273zM1024 0v284.439273h-84.898909V84.898909h-199.540364V0H1024zM284.439273 0v84.898909H84.898909v199.540364H0V0h284.439273z" fill="#e6e6e6" p-id="4737"></path></svg>

After

Width:  |  Height:  |  Size: 764 B

+1 -1
View File
@@ -1 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1609205172733" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2919" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M320 640v256H256v-192H64v-64h256z m640 0v64h-192v192h-64v-256h256zM512 384a128 128 0 1 1 0 256 128 128 0 0 1 0-256zM320 128v256H64V320h192V128h64z m448 0v192h192v64h-256V128h64z" p-id="2920"></path></svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1777426164195" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5541" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M316 672h60c4.4 0 8-3.6 8-8V360c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v304c0 4.4 3.6 8 8 8zM512 622c22.1 0 40-17.9 40-39 0-23.1-17.9-41-40-41s-40 17.9-40 41c0 21.1 17.9 39 40 39zM512 482c22.1 0 40-17.9 40-39 0-23.1-17.9-41-40-41s-40 17.9-40 41c0 21.1 17.9 39 40 39z" p-id="5542"></path><path d="M880 112H144c-17.7 0-32 14.3-32 32v736c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V144c0-17.7-14.3-32-32-32z m-40 728H184V184h656v656z" p-id="5543"></path><path d="M648 672h60c4.4 0 8-3.6 8-8V360c0-4.4-3.6-8-8-8h-60c-4.4 0-8 3.6-8 8v304c0 4.4 3.6 8 8 8z" p-id="5544"></path></svg>

Before

Width:  |  Height:  |  Size: 581 B

After

Width:  |  Height:  |  Size: 908 B

+1
View File
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1777352764855" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10736" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M587.808 383.392l-102.208 102.208A37.472 37.472 0 0 0 512 549.408a36.288 36.288 0 0 0 26.272-11.104l315.84-315.232a454.976 454.976 0 1 0 43.52 61.888l-54.688 54.752a372.704 372.704 0 1 1-94.816-116.928l-106.56 106.496-53.792 54.144z" p-id="10737"></path></svg>

After

Width:  |  Height:  |  Size: 594 B

+1 -1
View File
@@ -1 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1597744457882" class="icon" viewBox="0 0 1170 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10125" width="285.64453125" height="250" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"></style></defs><path d="M926.216214 404.588947l1.511115-151.84272H1079.570048c18.035893 0 33.098301-15.062408 33.098301-33.0983a33.390775 33.390775 0 0 0-33.098301-33.098301H927.727329V33.195792a33.390775 33.390775 0 0 0-33.049555-33.049555 33.390775 33.390775 0 0 0-33.0983 33.049555c0 3.022231 0 4.533346 1.511115 7.555577v144.287142H711.24787a33.390775 33.390775 0 0 0-33.098301 33.147046c0 18.035893 15.062408 33.049555 33.098301 33.049555H863.090589v139.802542c0 3.022231-1.46237 4.533346-1.46237 7.506831 0 18.035893 15.013662 33.098301 33.049555 33.098301 15.013662 1.46237 28.564955-10.529062 31.53844-27.053839zM1032.969201 790.995763a29.393631 29.393631 0 0 0-22.520493 10.480316 546.000095 546.000095 0 0 1-117.282049 88.717095 29.101157 29.101157 0 0 0-15.062408 25.591469c0 16.524777 13.551292 30.027324 30.07607 30.027324 4.533346 0 10.529062-1.46237 13.551292-2.973485 1.46237 0 1.46237-1.511115 1.46237-1.511115a523.235874 523.235874 0 0 0 127.811111-97.735041c6.044461-5.995716 10.529062-13.551292 10.529061-22.569238a28.028752 28.028752 0 0 0-28.564954-30.027325zM793.920503 935.282906c-2.973485 0-7.506831 0-10.529062 1.511115-46.600847 15.062408-96.223925 24.080354-145.798258 27.053839-15.062408 1.511115-25.59147 15.062408-25.591469 30.07607 0 16.573523 13.551292 30.07607 30.07607 30.07607 55.618794-4.4846 108.215357-13.502547 159.398295-30.07607a28.95492 28.95492 0 0 0 22.520494-28.564954 30.173561 30.173561 0 0 0-30.07607-30.07607zM535.324796 960.874375c-12.040177-1.511115-24.031609-1.511115-36.071785-3.02223a564.962155 564.962155 0 0 1-49.623078-7.506832c-230.030752-45.089732-389.380302-202.976912-389.380302-380.362355 0-139.851288 99.19741-269.124768 260.106822-339.805969 13.502547-5.995716 27.053839-12.040177 40.556386-16.573523 10.529062-4.4846 22.569239-7.506831 33.0983-10.480317 12.040177-4.533346 21.058123-15.062408 21.058124-28.564954a30.173561 30.173561 0 0 0-30.07607-30.07607 25.396487 25.396487 0 0 0-15.062408 4.4846c-10.529062 3.022231-19.498262 6.044461-28.564955 9.017947l-45.089732 18.035893C114.357309 254.257343 0.097491 406.148808 0.097491 568.471843c0 207.510258 180.407674 387.917932 439.00338 440.563241l54.107679 9.017946c12.040177 1.46237 25.59147 3.022231 37.631646 3.022231h1.46237c16.573523 0 30.07607-13.551292 30.07607-30.07607a30.758509 30.758509 0 0 0-27.05384-30.07607zM1169.798258 569.982958a30.173561 30.173561 0 0 0-30.07607-30.07607 30.173561 30.173561 0 0 0-30.07607 30.07607v1.46237c0 46.649593-10.529062 93.25044-33.049555 136.829057-1.511115 3.022231-1.511115 7.506831-1.511115 12.040177 0 16.573523 13.551292 30.07607 30.07607 30.07607 13.502547 0 24.031609-9.017946 28.564954-21.058124a361.985243 361.985243 0 0 0 36.071786-157.88718v-1.46237z" p-id="10126"></path><path d="M1052.516209 297.835959c-5.995716-4.533346-13.502547-9.017946-22.520493-9.017946a30.173561 30.173561 0 0 0-30.07607 30.07607c0 7.506831 2.973485 15.013662 9.017946 21.009378a375.14657 375.14657 0 0 1 79.699148 118.793164c4.4846 10.529062 15.013662 19.547008 27.053839 19.547008 16.524777 0 30.07607-13.551292 30.07607-30.07607 0-4.4846-1.511115-7.506831-3.02223-12.040177a422.039891 422.039891 0 0 0-90.179464-138.291427zM666.158138 182.065026h4.4846c16.573523 0 30.07607-13.551292 30.07607-30.07607 0-15.013662-12.040177-28.564955-27.053839-28.564954v-1.46237a761.602133 761.602133 0 0 0-154.86495-1.559861h-3.022231a30.173561 30.173561 0 0 0-30.07607 30.07607c0 16.573523 13.551292 30.07607 30.07607 30.07607h3.022231v1.462369c46.600847-5.94697 97.735041-4.4846 147.358119 0z" p-id="10127"></path></svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1777352032068" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5387" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M924.8 337.8c-22.6-53.4-54.9-101.3-96-142.4s-89-73.4-142.4-96C631.2 76.1 572.5 64.2 512 64.2S392.9 76.1 337.6 99.4c-53.4 22.6-101.3 54.9-142.4 96s-73.4 89-96 142.4C75.9 393.1 64 451.8 64 512.2c0 60.5 11.8 119.1 35.2 174.4 22.6 53.4 54.9 101.3 96 142.4 41.1 41.1 89 73.4 142.4 96 55.3 23.4 113.9 35.2 174.4 35.2s119.1-11.8 174.4-35.2c53.4-22.6 101.3-54.9 142.4-96 41.1-41.1 73.4-89 96-142.4 23.4-55.3 35.2-113.9 35.2-174.4 0-60.4-11.8-119.1-35.2-174.4zM780.3 780.5C708.6 852.2 613.4 891.6 512 891.6c-101.3 0-196.6-39.5-268.3-111.1S132.6 613.6 132.6 512.2c0-101.3 39.5-196.6 111.1-268.3s167-111.1 268.3-111.1c101.3 0 196.6 39.5 268.3 111.1s111.1 166.9 111.1 268.3S852 708.8 780.3 780.5z" p-id="5388"></path><path d="M512 512.2m-106.2 0a106.2 106.2 0 1 0 212.4 0 106.2 106.2 0 1 0-212.4 0Z" p-id="5389"></path></svg>

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

+1
View File
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1779246817161" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5348" width="200" height="200" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M473 71a8 8 0 0 1 8-8h64a8 8 0 0 1 8 8v77.161C722.848 166.622 857.812 301.289 876.729 471H952a8 8 0 0 1 8 8v64a8 8 0 0 1-8 8h-75.053C858.871 721.652 723.515 857.305 553 875.839V951a8 8 0 0 1-8 8h-64a8 8 0 0 1-8-8v-75.161C302.818 857.341 167.659 722.182 149.161 552H72a8 8 0 0 1-8-8v-64a8 8 0 0 1 8-8h77.161C167.659 301.818 302.818 166.659 473 148.161V71z m326 441c0-157.953-128.047-286-286-286S227 354.047 227 512s128.047 286 286 286 286-128.047 286-286z m-286 60c33.137 0 60-26.863 60-60s-26.863-60-60-60-60 26.863-60 60 26.863 60 60 60z" fill="#ffffff" p-id="5349"></path></svg>

After

Width:  |  Height:  |  Size: 913 B

+1
View File
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1777432778355" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2607" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M954.026667 913.6128H600.746667a35.908267 35.908267 0 0 1-35.771734-35.771733V116.736a35.703467 35.703467 0 0 1 68.266667-15.018667l353.143467 761.0368a35.566933 35.566933 0 0 1-2.2528 34.269867 35.362133 35.362133 0 0 1-30.173867 16.5888z" fill="#FFFFFF" p-id="2608"></path><path d="M465.373867 913.544533H112.162133a35.6352 35.6352 0 0 1-32.494933-50.722133L432.878933 101.717333a35.6352 35.6352 0 0 1 40.277334-19.8656 35.566933 35.566933 0 0 1 27.989333 34.952534V877.909333a35.771733 35.771733 0 0 1-35.771733 35.6352z" fill="#FFFFFF" fill-opacity=".4" p-id="2609"></path></svg>

After

Width:  |  Height:  |  Size: 916 B

+1
View File
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1777426622063" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8288" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M667.69962 294.974152l0 589.688383 316.384787 0L667.69962 294.974152zM632.522446 884.662535 632.522446 572.426231 34.882973 884.662535 632.522446 884.662535zM510.16777 357.862457l-137.677663-31.323403c0 0 97.523127-217.392192 349.512279-98.842169 0 0-196.516746-248.618381-487.190965 67.571978l-137.677663-31.273261 158.803819 250.082732L510.16777 357.862457z" p-id="8289"></path></svg>

After

Width:  |  Height:  |  Size: 719 B

+1
View File
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1777426691300" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2490" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M34.884 883.64h316.385V293.95L34.884 883.64z m949.201 0L386.446 571.402v312.236h597.64zM763.03 513.053l158.805-250.083-137.678 31.274c-290.674-316.19-487.191-67.572-487.191-67.572 251.99-118.55 349.512 98.842 349.512 98.842l-137.677 31.323L763.03 513.054z" fill="" p-id="2491"></path></svg>

After

Width:  |  Height:  |  Size: 623 B

+1
View File
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1777432945307" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4263" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M106.496 928.426667V575.2832c0-19.729067 16.042667-35.771733 35.771733-35.771733h761.0368a35.703467 35.703467 0 0 1 15.086934 68.266666L157.354667 960.9216a35.566933 35.566933 0 0 1-34.269867-2.321067 35.362133 35.362133 0 0 1-16.5888-30.173866z" fill="#FFFFFF" p-id="4264"></path><path d="M106.496 439.842133V86.6304a35.6352 35.6352 0 0 1 50.7904-32.494933l761.0368 353.211733a35.6352 35.6352 0 0 1 19.933867 40.209067 35.566933 35.566933 0 0 1-34.952534 27.989333H142.199467a35.771733 35.771733 0 0 1-35.703467-35.703467z" fill="#FFFFFF" fill-opacity=".4" p-id="4265"></path></svg>

After

Width:  |  Height:  |  Size: 916 B

+1
View File
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1782284218093" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4982" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512 51.2a460.8 460.8 0 1 1 0 921.6 460.8 460.8 0 0 1 0-921.6z m0 51.2a409.6 409.6 0 1 0 0 819.2 409.6 409.6 0 0 0 0-819.2z" fill="#ffffff" p-id="4983"></path><path d="M512 358.4c70.4 0 137.1136 5.2736 196.7616 14.6432l27.0336 4.6592C877.1584 403.9168 972.8 454.2464 972.8 512c0 57.7536-95.6416 108.032-237.0048 134.2976l-27.0336 4.608A1275.1872 1275.1872 0 0 1 512 665.6c-70.4 0-137.1136-5.2736-196.7616-14.6432l-27.0336-4.6592C146.8416 620.0832 51.2 569.7536 51.2 512c0-57.7536 95.6416-108.032 237.0048-134.2976l27.0336-4.608c55.0912-8.704 116.1728-13.824 180.5824-14.592L512 358.4z m0 51.2c-31.5904 0-62.3104 1.1776-91.8528 3.328l-29.0816 2.5088-28.2112 3.072c-23.1424 2.7648-45.2096 6.144-66.048 9.984l-24.4224 4.7616-23.0912 5.12-21.76 5.5296C150.3744 464.5376 102.4 490.8544 102.4 512c0 21.1456 47.9744 47.4624 125.1328 68.096l21.76 5.4784 23.0912 5.1712 24.4224 4.7616c20.8384 3.84 42.9056 7.168 66.048 9.984l28.16 3.072 29.1328 2.5088a1261.4656 1261.4656 0 0 0 183.7056 0l29.0816-2.5088 28.2112-3.072c23.1424-2.7648 45.2096-6.144 66.048-9.984l24.4224-4.7616 23.0912-5.12 21.76-5.5296C873.6256 559.4624 921.6 533.1456 921.6 512c0-21.1456-47.9744-47.4624-125.1328-68.096l-21.76-5.4784-23.0912-5.1712-24.4224-4.7616c-20.8384-3.84-42.9056-7.168-66.048-9.984l-28.16-3.072-29.1328-2.5088c-24.576-1.792-50.0736-2.9184-76.1344-3.2256L512 409.6z" fill="#ffffff" p-id="4984"></path></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

+10 -11
View File
@@ -2,11 +2,7 @@
<div class="navbar navbar-flex-wrapper">
<div class="leftMenu">
<div class="navbar-flex-wrapper">
<hamburger
:is-active="sidebar.opened"
class="hamburger-container"
@toggleClick="toggleSideBar"
/>
<hamburger :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
<breadcrumb class="breadcrumb-container" />
</div>
@@ -51,12 +47,8 @@
<TopLang></TopLang>
</div>
</div>
<toggleRole
v-if="toggleRoleVisible"
:visible.sync="toggleRoleVisible"
:loading="toggleRoleLoading"
@save="loginByRole"
/>
<toggleRole v-if="toggleRoleVisible" :visible.sync="toggleRoleVisible" :loading="toggleRoleLoading"
@save="loginByRole" />
</div>
</template>
@@ -192,12 +184,14 @@ export default {
flex-direction: row;
justify-content: space-between;
}
.navbar {
height: 50px;
overflow: hidden;
// position: relative;
background: #fff;
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
.hamburger-container {
line-height: 46px;
height: 100%;
@@ -219,6 +213,7 @@ export default {
// float: right;
height: 100%;
line-height: 65px;
// position: relative;
&:focus {
outline: none;
@@ -245,6 +240,7 @@ export default {
.avatar-container {
display: inline-block;
margin-right: 200px;
.user-avatar {
margin-top: 5px;
width: 40px;
@@ -252,16 +248,19 @@ export default {
border-radius: 10px;
}
}
.dropdown-container {
// margin-left: 30px;
// position: absolute;
// right: 20px;
// top: 0px;
cursor: pointer;
.el-icon-caret-bottom {
font-size: 12px;
}
}
.el-dropdown-link {
color: #999c9e;
cursor: pointer;
+158 -148
View File
@@ -1,5 +1,6 @@
import Vue from 'vue'
import DicomEvent from '@/views/trials/trials-panel/reading/dicoms/components/DicomEvent'
import 'normalize.css/normalize.css' // A modern alternative to CSS resets
// import { createVersionPolling } from "@/utils/version-polling.esm.js";
import ElementUI, { MessageBox } from 'element-ui'
@@ -337,8 +338,10 @@ async function VueInit() {
})
}
let CompanyInfo = JSON.parse(localStorage.getItem('CompanyInfo'))
if (CompanyInfo && CompanyInfo.SystemShortName && text.toUpperCase() !== "CIRCLE") {
let test = new RegExp('IRC', 'ig')
if (CompanyInfo && CompanyInfo.SystemShortName && (text.toUpperCase() !== "CIRCLE")) {
// 精准匹配独立单词 IRC
// let test = new RegExp('IRC', 'ig')
let test = new RegExp('\\bIRC\\b', 'ig')
text = text.replace(test, CompanyInfo.SystemShortName)
}
// return i18n.t(key)
@@ -449,6 +452,7 @@ async function VueInit() {
isOpen = false
isLock = null
zzSessionStorage.removeItem('isLock')
DicomEvent.$emit('isLock', false)
router.push("/login")
}).then(() => {
// _vm.$alert(lang === 'zh' ? '由于您长时间未操作,为保护您的数据安全已强制将您下线,如果需要继续操作请重新登陆!' : 'No operation for a long time non-operation, you have been forced logout to protect data security. If continue to operate, please login again!', {
@@ -460,160 +464,166 @@ 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 || WHITELIST.includes(_vm.$route.path)) {
count = 0;
localStorage.setItem('count', '0')
if (_vm.$route.path === '/login') {
zzSessionStorage.removeItem('lastWorkbench')
zzSessionStorage.removeItem('isLock')
isLock = null
}
return
}
if (isOpen) {
return
}
isOpen = true
zzSessionStorage.setItem('isLock', 'true')
_vm.$msgbox({
title: _vm.$t("env:lock:msgBox:title"),
confirmButtonText: _vm.$t("env:lock:msgBox:confirmButtonText"),
showClose: false,
beforeClose: (action, instance, done) => {
if (action === 'confirm') {
if (!_vm.unlock.my_username) {
_vm.$message.warning(_vm.$t("env:lock:msgBox:iupUser"))
return
}
if (!_vm.unlock.my_password) {
_vm.$message.warning(_vm.$t("env:lock:msgBox:inpPassword"))
return
}
var my_username = zzSessionStorage.getItem('my_username')
var my_password = zzSessionStorage.getItem('my_password')
let my_userid = zzSessionStorage.getItem('userId')
let my_EMail = zzSessionStorage.getItem('my_EMail') || ''
if (md5(_vm.unlock.my_password) === my_password && (my_username === _vm.unlock.my_username || my_EMail.toUpperCase() === _vm.unlock.my_username.toUpperCase())) {
resetReadingRestTime().then(() => {
})
const closeLock = (_vm) => {
_vm.$message.success(_vm.$t("env:lock:msgBox:lockSuccess"))
_vm.unlock = {
my_username: null,
my_password: null,
view: false
}
isOpen = false
count = 0;
isLock = null
zzSessionStorage.removeItem('isLock')
localStorage.setItem('count', '0')
document.querySelector('#my_username').value = null
document.querySelector('#my_password').value = null
setTimeout(() => {
done()
}, 500)
}
// if (eval(process.env.VUE_APP_LOCK_FOR_PERMISSION_MFA)) {
// sendMFAEmail({ UserId: my_userid, MfaType: 1 }).then((res) => {
// done();
// Vue.prototype.$MFA({
// status: "lock",
// UserId: my_userid,
// EMail: res.Result,
// username: my_username,
// callBack: () => {
// closeLock(_vm)
// },
// })
// })
// } else {
closeLock(_vm)
// }
} else {
// console.log(111)
_vm.$message.error(_vm.$t('env:lock:msgBox:userFail'))
}
eval(process.env.VUE_APP_LOCK_FOR_PERMISSION)
? () => {
var lang = zzSessionStorage.getItem('lang') ? zzSessionStorage.getItem('lang') : 'zh'
if (_vm.$store.state.trials.unlock || WHITELIST.includes(_vm.$route.path)) {
count = 0;
localStorage.setItem('count', '0')
if (_vm.$route.path === '/login') {
zzSessionStorage.removeItem('lastWorkbench')
zzSessionStorage.removeItem('isLock')
DicomEvent.$emit('isLock', false)
isLock = null
}
},
message: h('div', {}, [
h('el-form', {
props: { labelWidth: "80px" }
}, [
h('el-form-item', {
props: { label: _vm.$t("env:lock:msgBox:form:username") },
}, [
h('input', {
props: {
value: _vm.unlock.my_username
},
attrs: {
id: 'my_username',
class: 'el-input__inner',
autocomplete: 'new-password'
},
on: {
change: (event) => {
_vm.unlock.my_username = event.target.value
},
input: (event) => {
_vm.unlock.my_username = event.target.value
}
}
})
]),
h('el-form-item', {
props: { label: _vm.$t("env:lock:msgBox:form:Password") },
attrs: {
style: "position: relative;"
return
}
if (isOpen) {
return
}
isOpen = true
zzSessionStorage.setItem('isLock', 'true')
DicomEvent.$emit('isLock', true)
_vm.$msgbox({
title: _vm.$t("env:lock:msgBox:title"),
confirmButtonText: _vm.$t("env:lock:msgBox:confirmButtonText"),
showClose: false,
beforeClose: (action, instance, done) => {
if (action === 'confirm') {
if (!_vm.unlock.my_username) {
_vm.$message.warning(_vm.$t("env:lock:msgBox:iupUser"))
return
}
}, [
h('input', {
props: {
value: _vm.unlock.my_password
},
ref: "unlock_my_password_input",
attrs: {
id: 'my_password',
class: 'el-input__inner',
type: _vm.unlock.view ? 'text' : 'password',
autocomplete: 'new-password',
style: "padding-right:25px"
},
on: {
change: (event) => {
_vm.unlock.my_password = event.target.value
},
input: (event) => {
_vm.unlock.my_password = event.target.value
if (!_vm.unlock.my_password) {
_vm.$message.warning(_vm.$t("env:lock:msgBox:inpPassword"))
return
}
var my_username = zzSessionStorage.getItem('my_username')
var my_password = zzSessionStorage.getItem('my_password')
let my_userid = zzSessionStorage.getItem('userId')
let my_EMail = zzSessionStorage.getItem('my_EMail') || ''
if (md5(_vm.unlock.my_password) === my_password && (my_username === _vm.unlock.my_username || my_EMail.toUpperCase() === _vm.unlock.my_username.toUpperCase())) {
resetReadingRestTime().then(() => {
})
const closeLock = (_vm) => {
_vm.$message.success(_vm.$t("env:lock:msgBox:lockSuccess"))
_vm.unlock = {
my_username: null,
my_password: null,
view: false
}
isOpen = false
count = 0;
isLock = null
zzSessionStorage.removeItem('isLock')
DicomEvent.$emit('isLock', false)
localStorage.setItem('count', '0')
document.querySelector('#my_username').value = null
document.querySelector('#my_password').value = null
setTimeout(() => {
done()
}, 500)
}
}),
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"
}
// if (eval(process.env.VUE_APP_LOCK_FOR_PERMISSION_MFA)) {
// sendMFAEmail({ UserId: my_userid, MfaType: 1 }).then((res) => {
// done();
// Vue.prototype.$MFA({
// status: "lock",
// UserId: my_userid,
// EMail: res.Result,
// username: my_username,
// callBack: () => {
// closeLock(_vm)
// },
// })
// })
// } else {
closeLock(_vm)
// }
} else {
// console.log(111)
_vm.$message.error(_vm.$t('env:lock:msgBox:userFail'))
}
}
},
message: h('div', {}, [
h('el-form', {
props: { labelWidth: "80px" }
}, [
h('el-form-item', {
props: { label: _vm.$t("env:lock:msgBox:form:username") },
}, [
h('input', {
props: {
value: _vm.unlock.my_username
},
attrs: {
id: 'my_username',
class: 'el-input__inner',
autocomplete: 'new-password'
},
on: {
change: (event) => {
_vm.unlock.my_username = event.target.value
},
input: (event) => {
_vm.unlock.my_username = event.target.value
}
}
})
]),
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: _vm.unlock.view ? 'text' : 'password',
autocomplete: 'new-password',
style: "padding-right:25px"
},
on: {
change: (event) => {
_vm.unlock.my_password = event.target.value
},
input: (event) => {
_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"
}
},
}
}),
])
])
])
])
})
} : () => { }, process.env.VUE_APP_LOCK_FOR_TIME)
})
} : () => { },
process.env.VUE_APP_LOCK_FOR_TIME
)
}
VueInit()
// createVersionPolling({
+9 -4
View File
@@ -15,7 +15,7 @@ NProgress.configure({ showSpinner: false })
const whiteList = WHITELIST
store.state.trials.whiteList = whiteList
router.beforeEach(async(to, from, next) => {
router.beforeEach(async (to, from, next) => {
NProgress.start()
// 设置页面标题
// document.title = getPageTitle(to.meta.title) store.state.trials.whiteList.indexOf(to.path) === -1
@@ -60,7 +60,7 @@ router.beforeEach(async(to, from, next) => {
Vue.prototype.toPath = to.path
Vue.prototype.$path = []
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 === '/login' || to.path === '/recompose' || to.path === '/email-recompose' || to.path === '/link_expired' || to.path === '/error' || to.path === '/ReviewersResearchForm' || to.path === '/ReviewersResearch') {
if (to.path === '/ReviewersResearch') {
await OSSclient()
await store.dispatch('user/logout')
@@ -92,8 +92,13 @@ router.beforeEach(async(to, from, next) => {
await store.dispatch('user/getUserInfo')
const accessRoutes = await store.dispatch('permission/generateRoutes')
resetRouter()
router.addRoutes(accessRoutes)
next({ ...to, replace: true })
if (accessRoutes.length > 0) {
router.addRoutes(accessRoutes)
next({ ...to, replace: true })
} else {
next()
}
} catch (error) {
console.log(error)
// 删除token并进入登录页面以重新登录
+15
View File
@@ -128,6 +128,11 @@ export const constantRoutes = [
hidden: true,
component: () => import('@/views/none-dicom-show/index')
},
{
path: '/none_dicoms',
hidden: true,
component: () => import('@/views/none-dicoms/index')
},
{
path: '/noneDicomReading',
name: 'noneDicomReading',
@@ -144,6 +149,11 @@ export const constantRoutes = [
hidden: true,
component: () => import('@/views/trials/trials-panel/reading/dicoms/none-dicoms')
},
{
path: '/ecrfList',
hidden: true,
component: () => import('@/views/trials/trials-panel/reading/dicoms/components/TableList')
},
{
path: '/readingPage',
name: 'readingPage',
@@ -278,6 +288,11 @@ export const constantRoutes = [
component: () => import('@/views/error'),
hidden: true
},
{
path: '/link_expired',
component: () => import('@/views/linkExpired'),
hidden: true
},
{
path: '/404',
component: () => import('@/views/404'),
+8 -1
View File
@@ -128,7 +128,14 @@ const actions = {
if (response.IsSuccess) {
var noticeList = []
response.Result.forEach(el => {
noticeList.push({ Id: el.Id, Content: el.NoticeContent, NoticeTypeEnum: el.NoticeTypeEnum })
noticeList.push({
Id: el.Id,
Content: el.NoticeContent,
NoticeTypeEnum: el.NoticeTypeEnum,
PublishedTime: el.PublishedTime,
FileName: el.FileName,
Path: el.Path
})
})
commit('SET_NOTICE', noticeList)
+7 -1
View File
@@ -79,7 +79,7 @@ function getQuestions(questions) {
answerObj.angle = angle
answerObj.saveTypeEnum = isNaN(parseFloat(angle)) ? 1 : 2
}
} else if (criterionType === 21) {
} else if (criterionType === 21 || criterionType === 22) {
// MRI-PDFF
let isMeasurable = getQuestionAnswer(item.TableQuestions.Questions, 1105, answerObj)
answerObj.isMeasurable = isMeasurable
@@ -932,6 +932,12 @@ const actions = {
data.IsDicom = study.IsDicom
data.PreviewImageCount = 0
data.IsCriticalSequence = study.IsCriticalSequence
data.PatientSex = study.PatientSex
data.PatientWeight = study.PatientWeight
data.RadionuclideHalfLife = study.RadionuclideHalfLife
data.RadionuclideTotalDose = study.RadionuclideTotalDose
data.RadiopharmaceuticalStartTime = study.RadiopharmaceuticalStartTime
data.AcquisitionTime = study.AcquisitionTime
var seriesList = []
study.SeriesList.forEach((series, seriesIndex) => {
const imageIds = []
+1 -1
View File
@@ -121,7 +121,7 @@ const actions = {
localStorage.setItem('CompanyInfo', JSON.stringify(response.Result.CompanyInfo))
const data = response.Result
commit('SET_ROLES', data.BasicInfo.AccountList)
if (data.BasicInfo.IsFirstAdd || data.BasicInfo.LoginState === 1) {
if (data.BasicInfo.IsFirstAdd || data.BasicInfo.LoginState === 1 || data.BasicInfo.IsNeedResetPwd) {
try {
zzSessionStorage.setItem('userId', data.BasicInfo.Id)
commit('SET_TOKEN', data.JWTStr)
+11 -9
View File
@@ -239,21 +239,15 @@ body .el-table th.gutter {
line-height: 1.5;
}
.el-descriptions__body
.el-descriptions__table
.el-descriptions-item__cell.is-left {
.el-descriptions__body .el-descriptions__table .el-descriptions-item__cell.is-left {
text-align: left;
}
.el-descriptions__body
.el-descriptions__table
.el-descriptions-item__cell.is-center {
.el-descriptions__body .el-descriptions__table .el-descriptions-item__cell.is-center {
text-align: center;
}
.el-descriptions__body
.el-descriptions__table
.el-descriptions-item__cell.is-right {
.el-descriptions__body .el-descriptions__table .el-descriptions-item__cell.is-right {
text-align: right;
}
@@ -345,6 +339,10 @@ body .el-table th.gutter {
.el-dialog__wrapper {
z-index: 3999 !important;
}
.el-popper {
z-index: 4000 !important;
}
}
.feedBack-select-box {
@@ -379,3 +377,7 @@ body .el-table th.gutter {
vertical-align: -0.4em !important;
cursor: pointer;
}
.mobile_confirm {
width: 200px;
}
+5 -2
View File
@@ -6,6 +6,7 @@ export const anonymization = function (file, config) {
try {
const reader = new FileReader()
let AnonymizeFixedList = config.AnonymizeFixedList
console.log(AnonymizeFixedList, 'AnonymizeFixedList')
let AnonymizeNotFixedList = config.AnonymizeNotFixedList
let DicomStoreInfo = config.DicomStoreInfo
reader.onload = async (event) => {
@@ -15,9 +16,11 @@ export const anonymization = function (file, config) {
let dataset = dcmjs.data.DicomMessage.readFile(buffer)
for (var i = 0; i < AnonymizeFixedList.length; i++) {
let AnonymizeFixed = AnonymizeFixedList[i]
if (!dataset.dict.hasOwnProperty(`${AnonymizeFixed.Group + AnonymizeFixed.Element}`)) continue
if (!dataset.dict.hasOwnProperty(`${AnonymizeFixed.Group + AnonymizeFixed.Element}`) && !dataset.meta.hasOwnProperty(`${AnonymizeFixed.Group + AnonymizeFixed.Element}`)) continue
if (dataset.dict[AnonymizeFixed.Group + AnonymizeFixed.Element]) {
dataset.dict[AnonymizeFixed.Group + AnonymizeFixed.Element].Value[0] = AnonymizeFixed.ReplaceValue
} else if (dataset.meta[AnonymizeFixed.Group + AnonymizeFixed.Element]) {
dataset.meta[AnonymizeFixed.Group + AnonymizeFixed.Element].Value[0] = AnonymizeFixed.ReplaceValue
} else {
dataset.dict[AnonymizeFixed.Group + AnonymizeFixed.Element] = {
vr: AnonymizeFixed.ValueRepresentation,
@@ -60,7 +63,7 @@ export const anonymization = function (file, config) {
let newDicomFile = dataset.write() // fragmentMultiframe 原始数据是否进行分割
const bufferArray = new Uint8Array(newDicomFile)
const blob = new Blob([bufferArray], { type: 'application/octet-stream' })
resolve({ blob, pixelDataElement })
resolve({ blob, pixelDataElement, Modality: dataset.dict['00080060'].Value[0] })
} catch (err) {
console.log(file, 'warning')
console.log(err)
+2 -1
View File
@@ -12,7 +12,8 @@ export const dcmUpload = async function (data, config, progressFn, fileInfo) {
let res = await Vue.prototype.OSSclient.multipartUpload(Object.assign(data, { file: blob.blob }), progressFn, fileInfo)
resolve({
...res,
image: blob.pixelDataElement
image: blob.pixelDataElement,
Modality: blob.Modality
})
// let OSSclientA = await OSSclient
// let blob = await encoder(file)
+269 -60
View File
@@ -1,32 +1,123 @@
import * as cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader'
import * as cornerstoneWADOImageLoader from "cornerstone-wado-image-loader";
import store from "@/store";
import { getPTClinicalDataForInstance } from "@/utils/ptClinicalDataCache";
import {
getImageTypeSubItemFromDataset,
extractOrientationFromDataset,
extractPositionFromDataset,
extractSpacingFromDataset,
extractSliceThicknessFromDataset,
} from './extractPositioningFromDataset';
} from "./extractPositioningFromDataset";
function toNumber(val) {
if (val === undefined || val === null || val === "") return null;
const n = typeof val === "number" ? val : parseFloat(val);
return Number.isFinite(n) ? n : null;
}
function normalizeDicomTime(value) {
if (value === undefined || value === null || value === "") return null;
const num = toNumber(value);
if (num != null) {
return String(Math.floor(num)).padStart(6, "0");
}
if (typeof value !== "string") return null;
const trimmed = value.trim();
if (!trimmed) return null;
const plain = trimmed.replace(/:/g, "");
const parts = plain.split(".");
const main = parts[0];
const fractional = parts[1];
if (!/^\d+$/.test(main)) {
return trimmed;
}
const normalizedMain = main.padStart(6, "0");
return fractional ? `${normalizedMain}.${fractional}` : normalizedMain;
}
function getPTClinicalOverrideFromImageId(imageId) {
try {
const qIndex = imageId.indexOf("?");
if (qIndex === -1) return null;
const params = new URLSearchParams(imageId.slice(qIndex + 1));
const visitTaskId = params.get("visitTaskId");
const idx = params.get("idx");
// 场景1trials/dicomsimageId 带 visitTaskId + idx,优先使用列表中的最新 study 值
if (visitTaskId && idx) {
const parts = idx.split("|");
const studyIndex = toNumber(parts[0]);
if (Number.isInteger(studyIndex) && studyIndex >= 0) {
const visitTaskList = store.state.reading.visitTaskList;
if (Array.isArray(visitTaskList)) {
const visitTaskInfo = visitTaskList.find(
(v) => v && String(v.VisitTaskId) === visitTaskId
);
const study = visitTaskInfo?.StudyList?.[studyIndex];
if (
study &&
!(
study.PatientWeight == null &&
study.RadionuclideTotalDose == null &&
study.RadionuclideHalfLife == null &&
study.RadiopharmaceuticalStartTime == null &&
study.AcquisitionTime == null &&
study.PatientSex == null
)
) {
return study;
}
}
}
}
// 场景2src/components/DicomimageId 仅带 instanceId,回退到实例缓存
const instanceId = params.get("instanceId");
if (instanceId) {
const cached = getPTClinicalDataForInstance(instanceId);
if (cached) return cached;
}
return null;
} catch (e) {
return null;
}
}
function parseImageId(imageId) {
// build a url by parsing out the url scheme and frame index from the imageId
const firstColonIndex = imageId.indexOf(':');
let url = imageId.substring(firstColonIndex + 1);
const frameIndex = url.indexOf('frame=');
// 兼容 frame 参数不在最后的情况:只移除 frame 参数本身,保留 instanceId/visitTaskId/idx 等其余 query
const firstColonIndex = imageId.indexOf(":");
const scheme = imageId.substr(0, firstColonIndex);
const urlPart = imageId.substring(firstColonIndex + 1);
let url = urlPart;
let frame;
const qIndex = urlPart.indexOf("?");
if (qIndex !== -1) {
const base = urlPart.slice(0, qIndex);
const query = urlPart.slice(qIndex + 1);
const parts = query.split("&").filter(Boolean);
const preservedParts = [];
if (frameIndex !== -1) {
const frameStr = url.substr(frameIndex + 6);
parts.forEach((part) => {
const eqIndex = part.indexOf("=");
const key = eqIndex === -1 ? part : part.slice(0, eqIndex);
const value = eqIndex === -1 ? "" : part.slice(eqIndex + 1);
frame = parseInt(frameStr, 10);
url = url.substr(0, frameIndex - 1);
if (key === "frame") {
const n = value !== "" ? parseInt(value, 10) : NaN;
if (Number.isFinite(n)) {
frame = n;
}
return;
}
// 保留原始 query 片段,避免 URLSearchParams 把 | 编码成 %7C 导致 dataSetCache key 对不上
preservedParts.push(part);
});
url = preservedParts.length ? `${base}?${preservedParts.join("&")}` : base;
}
return {
scheme: imageId.substr(0, firstColonIndex),
url,
frame,
};
return { scheme, url, frame };
}
function getNumberValues(dataSet, tag, minimumLength) {
const values = [];
@@ -35,7 +126,7 @@ function getNumberValues(dataSet, tag, minimumLength) {
if (!valueAsString) {
return;
}
const split = valueAsString.split('\\');
const split = valueAsString.split("\\");
if (minimumLength && split.length < minimumLength) {
return;
@@ -74,28 +165,31 @@ function getLutData(lutDataSet, tag, lutDescriptor) {
return lut;
}
function populateSmallestLargestPixelValues(dataSet, imagePixelModule) {
const pixelRepresentation = dataSet.uint16('x00280103');
const pixelRepresentation = dataSet.uint16("x00280103");
if (pixelRepresentation === 0) {
imagePixelModule.smallestPixelValue = dataSet.uint16('x00280106');
imagePixelModule.largestPixelValue = dataSet.uint16('x00280107');
imagePixelModule.smallestPixelValue = dataSet.uint16("x00280106");
imagePixelModule.largestPixelValue = dataSet.uint16("x00280107");
} else {
imagePixelModule.smallestPixelValue = dataSet.int16('x00280106');
imagePixelModule.largestPixelValue = dataSet.int16('x00280107');
imagePixelModule.smallestPixelValue = dataSet.int16("x00280106");
imagePixelModule.largestPixelValue = dataSet.int16("x00280107");
}
imagePixelModule.largestPixelValue = imagePixelModule.largestPixelValue === 0 ? undefined : imagePixelModule.largestPixelValue;
imagePixelModule.largestPixelValue =
imagePixelModule.largestPixelValue === 0
? undefined
: imagePixelModule.largestPixelValue;
}
function populatePaletteColorLut(dataSet, imagePixelModule) {
imagePixelModule.redPaletteColorLookupTableDescriptor = getLutDescriptor(
dataSet,
'x00281101'
"x00281101"
);
imagePixelModule.greenPaletteColorLookupTableDescriptor = getLutDescriptor(
dataSet,
'x00281102'
"x00281102"
);
imagePixelModule.bluePaletteColorLookupTableDescriptor = getLutDescriptor(
dataSet,
'x00281103'
"x00281103"
);
// The first Palette Color Lookup Table Descriptor value is the number of entries in the lookup table.
@@ -134,56 +228,165 @@ function populatePaletteColorLut(dataSet, imagePixelModule) {
imagePixelModule.redPaletteColorLookupTableData = getLutData(
dataSet,
'x00281201',
"x00281201",
imagePixelModule.redPaletteColorLookupTableDescriptor
);
imagePixelModule.greenPaletteColorLookupTableData = getLutData(
dataSet,
'x00281202',
"x00281202",
imagePixelModule.greenPaletteColorLookupTableDescriptor
);
imagePixelModule.bluePaletteColorLookupTableData = getLutData(
dataSet,
'x00281203',
"x00281203",
imagePixelModule.bluePaletteColorLookupTableDescriptor
);
}
function getSpacingBetweenSlices(dataSet) {
if (dataSet?.elements?.x00180088) {
return dataSet.floatString('x00180088');
return dataSet.floatString("x00180088");
}
const pixelMeasuresSequence = dataSet?.elements?.x00289110;
if (
pixelMeasuresSequence?.items?.length &&
pixelMeasuresSequence.items[0]?.dataSet?.elements?.x00180088
) {
return pixelMeasuresSequence.items[0].dataSet.floatString('x00180088');
return pixelMeasuresSequence.items[0].dataSet.floatString("x00180088");
}
}
function metaDataProvider(type, imageId) {
const parsedImageId = parseImageId(imageId);
const dataSet = cornerstoneWADOImageLoader.wadouri.dataSetCacheManager.get(parsedImageId.url);
const dataSet = cornerstoneWADOImageLoader.wadouri.dataSetCacheManager.get(
parsedImageId.url
);
if (!dataSet) {
return;
}
if (type === 'imagePlaneModule') {
const ptOverride = getPTClinicalOverrideFromImageId(imageId);
if (type === "generalSeriesModule") {
const { dicomParser } = cornerstoneWADOImageLoader.external || {};
const overrideTime = normalizeDicomTime(ptOverride?.AcquisitionTime);
const seriesTimeRaw =
overrideTime ||
dataSet.string("x00080031") ||
dataSet.string("x00080032") ||
"";
const acquisitionTimeRaw =
overrideTime ||
dataSet.string("x00080032") ||
dataSet.string("x00080031") ||
"";
return {
modality: dataSet.string("x00080060"),
seriesInstanceUID: dataSet.string("x0020000e"),
seriesNumber: dataSet.intString("x00200011"),
studyInstanceUID: dataSet.string("x0020000d"),
seriesDate: dicomParser.parseDA(dataSet.string("x00080021")),
// 2D SUV 计算读取的是 seriesTime,这里同步使用用户录入采集时间
// seriesTime: dicomParser.parseTM(String(seriesTimeRaw)),
seriesTime: dicomParser.parseTM(dataSet.string('x00080031') || ''),
acquisitionDate: dicomParser.parseDA(dataSet.string("x00080022") || ""),
acquisitionTime: dicomParser.parseTM(String(acquisitionTimeRaw)),
};
}
if (type === "patientStudyModule") {
const weightOverride = ptOverride?.PatientWeight;
const weight =
weightOverride
? toNumber(weightOverride)
: dataSet.floatString("x00101030");
return {
patientAge: dataSet.intString("x00101010"),
patientSize: dataSet.floatString("x00101020"),
patientWeight: weight,
};
}
if (type === "petIsotopeModule") {
const { dicomParser } = cornerstoneWADOImageLoader.external || {};
const radiopharmaceuticalInfo = dataSet.elements.x00540016;
if (radiopharmaceuticalInfo === undefined) {
return;
}
const firstRadiopharmaceuticalInfoDataSet =
radiopharmaceuticalInfo.items[0].dataSet;
const startTimeRaw =
firstRadiopharmaceuticalInfoDataSet.string("x00181072") || "";
const totalDoseRaw =
firstRadiopharmaceuticalInfoDataSet.floatString("x00181074");
const halfLifeRaw =
firstRadiopharmaceuticalInfoDataSet.floatString("x00181075");
let startTimeValue = normalizeDicomTime(
ptOverride?.RadiopharmaceuticalStartTime
);
if (!startTimeValue && startTimeRaw) {
startTimeValue = normalizeDicomTime(startTimeRaw) || startTimeRaw;
}
const radiopharmaceuticalStartTime = dicomParser.parseTM(
String(startTimeValue)
);
const overrideTotalDose =
ptOverride?.RadionuclideTotalDose != null &&
ptOverride.RadionuclideTotalDose !== ""
? toNumber(ptOverride.RadionuclideTotalDose)
: null;
const overrideHalfLife =
ptOverride?.RadionuclideHalfLife != null &&
ptOverride.RadionuclideHalfLife !== ""
? toNumber(ptOverride.RadionuclideHalfLife)
: null;
const radionuclideTotalDose =
overrideTotalDose != null ? overrideTotalDose : toNumber(totalDoseRaw);
const radionuclideHalfLife =
overrideHalfLife != null ? overrideHalfLife : toNumber(halfLifeRaw);
return {
radiopharmaceuticalInfo: {
radiopharmaceuticalStartTime,
radionuclideTotalDose,
radionuclideHalfLife,
},
};
}
if (type === "imagePlaneModule") {
// const imageOrientationPatient = getNumberValues(dataSet, 'x00200037', 6);
// const imagePositionPatient = getNumberValues(dataSet, 'x00200032', 3);
// const pixelSpacing = getNumberValues(dataSet, 'x00280030', 2);
const imagePixelSpacing = getNumberValues(dataSet, 'x00181164', 2);
const frameIndex = parsedImageId.frame !== undefined ? parsedImageId.frame - 1 : undefined;
const imageOrientationPatient = extractOrientationFromDataset(dataSet, frameIndex);
const imagePixelSpacing = getNumberValues(dataSet, "x00181164", 2);
const frameIndex =
parsedImageId.frame !== undefined ? parsedImageId.frame - 1 : undefined;
const imageOrientationPatient = extractOrientationFromDataset(
dataSet,
frameIndex
);
let imagePositionPatient = extractPositionFromDataset(dataSet, frameIndex);
const pixelSpacing = extractSpacingFromDataset(dataSet, frameIndex);
const sliceThickness = extractSliceThicknessFromDataset(dataSet, frameIndex);
const sliceThickness = extractSliceThicknessFromDataset(
dataSet,
frameIndex
);
const modality = dataSet.string("x00080060");
if (modality && modality.includes('NM') && parsedImageId.frame !== undefined && parsedImageId.frame > 1) {
if (
modality &&
modality.includes("NM") &&
parsedImageId.frame !== undefined &&
parsedImageId.frame > 1
) {
const spacingBetweenSlices = getSpacingBetweenSlices(dataSet);
const step = spacingBetweenSlices !== undefined ? spacingBetweenSlices : sliceThickness;
if (imageOrientationPatient && imagePositionPatient && step !== undefined && frameIndex !== undefined) {
const step =
spacingBetweenSlices !== undefined
? spacingBetweenSlices
: sliceThickness;
if (
imageOrientationPatient &&
imagePositionPatient &&
step !== undefined &&
frameIndex !== undefined
) {
const rowCosines = [
parseFloat(imageOrientationPatient[0]),
parseFloat(imageOrientationPatient[1]),
@@ -210,7 +413,11 @@ function metaDataProvider(type, imageId) {
}
}
const estimatedRadiographicMagnificationFactor = getNumberValues(dataSet, 'x00181114', 2);
const estimatedRadiographicMagnificationFactor = getNumberValues(
dataSet,
"x00181114",
2
);
let columnPixelSpacing = null;
let rowPixelSpacing = null;
@@ -219,8 +426,10 @@ function metaDataProvider(type, imageId) {
rowPixelSpacing = pixelSpacing[0];
columnPixelSpacing = pixelSpacing[1];
} else if (imagePixelSpacing && estimatedRadiographicMagnificationFactor) {
rowPixelSpacing = imagePixelSpacing[0] / estimatedRadiographicMagnificationFactor[0];
columnPixelSpacing = imagePixelSpacing[1] / estimatedRadiographicMagnificationFactor[1];
rowPixelSpacing =
imagePixelSpacing[0] / estimatedRadiographicMagnificationFactor[0];
columnPixelSpacing =
imagePixelSpacing[1] / estimatedRadiographicMagnificationFactor[1];
} else if (imagePixelSpacing && !estimatedRadiographicMagnificationFactor) {
rowPixelSpacing = imagePixelSpacing[0];
columnPixelSpacing = imagePixelSpacing[1];
@@ -244,37 +453,37 @@ function metaDataProvider(type, imageId) {
}
return {
frameOfReferenceUID: dataSet.string('x00200052'),
rows: dataSet.uint16('x00280010'),
columns: dataSet.uint16('x00280011'),
frameOfReferenceUID: dataSet.string("x00200052"),
rows: dataSet.uint16("x00280010"),
columns: dataSet.uint16("x00280011"),
imageOrientationPatient,
rowCosines,
columnCosines,
imagePositionPatient,
sliceThickness,
sliceLocation: dataSet.floatString('x00201041'),
sliceLocation: dataSet.floatString("x00201041"),
pixelSpacing,
rowPixelSpacing,
columnPixelSpacing,
};
}
if (type === 'imagePixelModule') {
if (type === "imagePixelModule") {
const imagePixelModule = {
samplesPerPixel: dataSet.uint16('x00280002'),
photometricInterpretation: dataSet.string('x00280004'),
rows: dataSet.uint16('x00280010'),
columns: dataSet.uint16('x00280011'),
bitsAllocated: dataSet.uint16('x00280100'),
bitsStored: dataSet.uint16('x00280101'),
highBit: dataSet.uint16('x00280102'),
pixelRepresentation: dataSet.uint16('x00280103'),
planarConfiguration: dataSet.uint16('x00280006'),
pixelAspectRatio: dataSet.string('x00280034'),
samplesPerPixel: dataSet.uint16("x00280002"),
photometricInterpretation: dataSet.string("x00280004"),
rows: dataSet.uint16("x00280010"),
columns: dataSet.uint16("x00280011"),
bitsAllocated: dataSet.uint16("x00280100"),
bitsStored: dataSet.uint16("x00280101"),
highBit: dataSet.uint16("x00280102"),
pixelRepresentation: dataSet.uint16("x00280103"),
planarConfiguration: dataSet.uint16("x00280006"),
pixelAspectRatio: dataSet.string("x00280034"),
};
populateSmallestLargestPixelValues(dataSet, imagePixelModule);
if (
imagePixelModule.photometricInterpretation === 'PALETTE COLOR' &&
imagePixelModule.photometricInterpretation === "PALETTE COLOR" &&
dataSet.elements.x00281101
) {
populatePaletteColorLut(dataSet, imagePixelModule);
+58 -11
View File
@@ -40,7 +40,7 @@ async function ossGenerateSTS() {
return new Promise(async (resolve, reject) => {
try {
let _vm = router.default.app
if (_vm._route.path !== '/trials/trials-panel/visit/crc-upload') {
if (_vm._route.path !== '/trials/trials-panel/visit/crc-upload' && _vm._route.path !== '/readingDicoms' && _vm._route.path !== '/showdicom') {
var objectItem = objectName.split('/')
// objectItem[objectItem.length - 1] = new Date().getTime() + '_' + objectItem[objectItem.length - 1]
objectItem[objectItem.length - 1] = `${objectItem[objectItem.length - 1].substring(
@@ -56,10 +56,15 @@ async function ossGenerateSTS() {
const urlParams = new URLSearchParams(window.location.search)
const trialId = urlParams.get('trialId')
if (Object.keys(fileInfo).length !== 0) {
let params = Object.assign({path: objectName}, fileInfo)
fileInfo.fileType = mimeTypeToExt(fileInfo.fileType)
let params = Object.assign({ path: objectName }, fileInfo)
addOrUpdateFileUploadRecord(params)
} else if (trialId) {
let params = { trialId }
const fileName = objectName.split('/').pop()
const fileType = fileName.includes('.')
? fileName.split('.').pop().toLowerCase()
: ''
let params = { trialId, path: objectName, fileName, fileType }
addOrUpdateFileUploadRecord(params)
}
resolve({
@@ -92,7 +97,7 @@ async function ossGenerateSTS() {
OSSclient = new OSS(Vue.prototype.OSSclientConfig);
}
let _vm = router.default.app
if (_vm._route.path !== '/trials/trials-panel/visit/crc-upload') {
if (_vm._route.path !== '/trials/trials-panel/visit/crc-upload' && _vm._route.path !== '/readingDicoms' && _vm._route.path !== '/showdicom') {
var objectItem = data.path.split('/')
// objectItem[objectItem.length - 1] = new Date().getTime() + '_' + objectItem[objectItem.length - 1]
objectItem[objectItem.length - 1] = `${objectItem[objectItem.length - 1].substring(
@@ -108,13 +113,18 @@ async function ossGenerateSTS() {
const urlParams = new URLSearchParams(window.location.search)
const trialId = urlParams.get('trialId')
if (Object.keys(fileInfo).length !== 0) {
let params = Object.assign({path: data.path}, fileInfo)
fileInfo.fileType = mimeTypeToExt(fileInfo.fileType)
let params = Object.assign({ path: data.path }, fileInfo)
addOrUpdateFileUploadRecord(params)
} else if (trialId) {
let params = { trialId }
const fileName = data.path.split('/').pop()
const fileType = fileName.includes('.')
? fileName.split('.').pop().toLowerCase()
: ''
let params = { trialId, path: data.path, fileName, fileType }
addOrUpdateFileUploadRecord(params)
}
resolve({
name: data.path,
url: Vue.prototype.OSSclientConfig.viewEndpoint + decodeUtf8(res.name)
@@ -141,7 +151,7 @@ async function ossGenerateSTS() {
try {
var name = objectName.split('/')[objectName.split('/').length - 1]
let _vm = router.default.app
if (_vm._route.path !== '/trials/trials-panel/visit/crc-upload') {
if (_vm._route.path !== '/trials/trials-panel/visit/crc-upload' && _vm._route.path !== '/readingDicoms' && _vm._route.path !== '/showdicom') {
var objectItem = objectName.split('/')
objectItem[objectItem.length - 1] = new Date().getTime() + '_' + objectItem[objectItem.length - 1]
objectName = objectItem.join('/')
@@ -215,7 +225,7 @@ function uploadAWS(aws, data, progress, fileInfo) {
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') {
if (_vm._route.path !== '/trials/trials-panel/visit/crc-upload' && _vm._route.path !== '/readingDicoms' && _vm._route.path !== '/showdicom') {
var objectItem = data.path.split('/')
// objectItem[objectItem.length - 1] = new Date().getTime() + '_' + objectItem[objectItem.length - 1]
objectItem[objectItem.length - 1] = `${objectItem[objectItem.length - 1].substring(
@@ -235,10 +245,15 @@ function uploadAWS(aws, data, progress, fileInfo) {
const trialId = urlParams.get('trialId')
if (Object.keys(fileInfo).length !== 0) {
let params = Object.assign({path: decodeUtf8(curPath)}, fileInfo)
fileInfo.fileType = mimeTypeToExt(fileInfo.fileType)
let params = Object.assign({ path: decodeUtf8(curPath) }, fileInfo)
addOrUpdateFileUploadRecord(params)
} else if (trialId) {
let params = { trialId }
const fileName = decodeUtf8(curPath).split('/').pop()
const fileType = fileName.includes('.')
? fileName.split('.').pop().toLowerCase()
: ''
let params = { trialId, path: decodeUtf8(curPath), fileName, fileType }
addOrUpdateFileUploadRecord(params)
}
resolve({
@@ -350,5 +365,37 @@ function isCredentialsExpired(credentials) {
return expireDate.getTime() - now.getTime() <= 300000;
}
function mimeTypeToExt(mimeType) {
const map = {
// 图片
'image/jpeg': 'jpg',
'image/jpg': 'jpg',
'image/png': 'png',
'image/gif': 'gif',
'image/webp': 'webp',
'image/svg+xml': 'svg',
'image/bmp': 'bmp',
// 文档
'application/dicom': 'dcm',
'application/pdf': 'pdf',
'application/msword': 'doc',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'docx',
'application/vnd.ms-excel': 'xls',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'xlsx',
'application/vnd.ms-powerpoint': 'ppt',
'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'pptx',
'text/plain': 'txt',
// 音频/视频
'audio/mpeg': 'mp3',
'audio/wav': 'wav',
'video/mp4': 'mp4',
'video/mpeg': 'mpeg',
// 压缩包
'application/zip': 'zip',
'application/x-rar-compressed': 'rar',
'application/x-7z-compressed': '7z',
};
return map[mimeType] || ''; // 找不到返回空字符串
}
export const OSSclient = ossGenerateSTS
+31
View File
@@ -0,0 +1,31 @@
// PT 临床数据缓存:用于统一 SUV 口径(优先接口,缺失则回退 DICOM 元数据)
const instanceIdToClinicalData = new Map()
function normalizeId(id) {
if (id === undefined || id === null) return null
const s = String(id).trim()
return s ? s : null
}
export function setPTClinicalDataForInstance(instanceId, clinicalData) {
const key = normalizeId(instanceId)
if (!key) return
if (!clinicalData || typeof clinicalData !== 'object') return
instanceIdToClinicalData.set(key, { ...clinicalData })
}
export function getPTClinicalDataForInstance(instanceId) {
const key = normalizeId(instanceId)
if (!key) return null
return instanceIdToClinicalData.get(key) || null
}
export function deletePTClinicalDataForInstance(instanceId) {
const key = normalizeId(instanceId)
if (!key) return
instanceIdToClinicalData.delete(key)
}
export function clearPTClinicalDataCache() {
instanceIdToClinicalData.clear()
}
+1 -1
View File
@@ -28,7 +28,7 @@ async function executeTask() {
task.callback({ success: true, res: result })
}).catch((error) => {
task.callback({ success: true, err: error })
task.callback({ success: false, err: error })
})
})
// let starLoadTime = performance.now()
+13
View File
@@ -21,6 +21,16 @@ var path
service.interceptors.request.use(
async config => {
if (config.url.includes("/Dictionary/getBasicDataSelect")) {
let query = router && router.app && router.app._route && router.app._route.query
if (query.trialId) {
let str = config.url.split("?")
config.url = `${str[0]}?TrialId=${query.trialId}`
if (str[1]) {
config.url += `&${str[1]}`
}
}
}
path = router && router.app && router.app._route && router.app._route.path
config.headers['Content-Type'] = 'application/json;charset=UTF-8'
// config.baseURL = process.env.NODE_ENV === 'prod' ? "https://api.irc.extimaging.com" : '/api'
@@ -108,6 +118,9 @@ service.interceptors.response.use(
if (res.IsSuccess) {
return Promise.resolve(res)
} else if (res.IsSuccess === false) {
if (res.Code === -4) {
return Promise.resolve(res)
}
if (res.Code !== 5) {
MessageBox.confirm(res.ErrorMessage, {
type: 'warning',
+8 -5
View File
@@ -52,18 +52,22 @@ function getScreen() {
})
}
export async function openWindow(url, name, Skip = false) {
export async function openWindow(url, name, Skip = false, noreferrer = false) {
// 判断浏览器是否兼容
// 高版本的谷歌,edge不支持跨屏,需要降低浏览器版本86.0版
let fulls = ''
if (noreferrer) {
fulls = 'noopener,noreferrer'
}
if (!window.getScreens && !window.getScreenDetails) {
console.log('你的浏览器版本不支持多屏展示功能!');
return window.open(url, name);
return window.open(url, name, fulls);
}
let permission = await getPermission()
if (!permission) {
// alert('使用多屏功能请先进行授权')
if (Skip) {
return window.open(url, name);
return window.open(url, name, fulls);
}
return false
}
@@ -72,14 +76,13 @@ export async function openWindow(url, name, Skip = false) {
// 判断是否2个屏幕
if (multiScreen.length < 2) {
console.log('请接入多个显示屏!');
return window.open(url, name);
return window.open(url, name, fulls);
}
console.log(screen, 'screen')
// 获取当前屏幕availLeft信息和所有信息比对,取另一个屏幕数据
let currentAvailLeft = screen.availLeft ? screen.availLeft : '0'
let newCurr = multiScreen.find((t) => t.availLeft != currentAvailLeft)
console.log(newCurr, 'newCurr')
let fulls = ''
for (let key in newCurr) {
fulls += `${key}=${(newCurr[key] || newCurr[key] === 0) ? newCurr[key] : 0},`
}
+1 -1
View File
@@ -1,2 +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']
const WHITELIST = ['/', "/curriculumVitae", 'researchDetail_m', '/researchForm', '/ReviewersResearch', '/login', '/link_expired', '/error', '/resetpassword', '/recompose', '/email-recompose', '/trialStats', '/showdicom', '/imagesShare', '/audit', '/preview', '/researchLogin', '/researchLogin_m', '/blindResumeInfo', '/trialsResume', '/joinVerify', '/showNoneDicoms', '/noneDicomReading', '/clinicalData', '/readingDicoms', '/readingPage', '/visitDicomReview', '/visitNondicomReview', '/globalReview', '/adReview', '/oncologyReview', '/nonedicoms', '/ecrfList']
export default WHITELIST
@@ -3,8 +3,9 @@
</template>
<script>
import * as echarts from 'echarts'
// import 'modules/echarts/map/js/world.js'
import worldMap from 'echarts-map/json/world.json';
import { fontSize } from 'utils/fontsize'
echarts.registerMap('world', worldMap);
export default {
props: {
area: { type: String, default: '' },
@@ -76,68 +77,68 @@ export default {
}
return res
}
var series = []
;[['Shanghai', BJData]].forEach(function (item, i) {
series.push(
{
type: 'lines',
zlevel: 1,
effect: {
show: true,
color: '#fff',
period: 6,
trailLength: 0.7,
symbolSize: fontSize(0.05)
},
lineStyle: {
normal: {
width: fontSize(0.03),
color: '#9ae5fc',
opacity: 0.02,
curveness: -0.2
}
},
data: convertData(item[1])
var series = [];
[['Shanghai', BJData]].forEach(function (item, i) {
series.push(
{
type: 'lines',
zlevel: 1,
effect: {
show: true,
color: '#fff',
period: 6,
trailLength: 0.7,
symbolSize: fontSize(0.05)
},
lineStyle: {
normal: {
width: fontSize(0.03),
color: '#9ae5fc',
opacity: 0.02,
curveness: -0.2
}
},
{
type: 'effectScatter',
coordinateSystem: 'geo',
zlevel: 3,
rippleEffect: {
//
period: 5, //
brushType: 'fill', // stroke, fill
scale: 5 //
},
label: {
normal: {
show: true,
color: '#fce630',
position: 'right', //
offset: [10, 0], //
fontSize: fontSize(0.16),
formatter: '{b}' //
},
emphasis: {
show: true
}
},
symbol: 'circle',
symbolSize: function (val) {
return fontSize(0.08) //
},
data: convertData(item[1])
},
data: item[1].map(function (dataItem) {
return {
name: dataItem[0].name,
value: geoCoordMap[dataItem[0].name]
}
})
}
)
})
{
type: 'effectScatter',
coordinateSystem: 'geo',
zlevel: 3,
rippleEffect: {
//
period: 5, //
brushType: 'fill', // stroke, fill
scale: 5 //
},
label: {
normal: {
show: true,
color: '#fce630',
position: 'right', //
offset: [10, 0], //
fontSize: fontSize(0.16),
formatter: '{b}' //
},
emphasis: {
show: true
}
},
symbol: 'circle',
symbolSize: function (val) {
return fontSize(0.08) //
},
data: item[1].map(function (dataItem) {
return {
name: dataItem[0].name,
value: geoCoordMap[dataItem[0].name]
}
})
}
)
})
var option = {
// backgroundColor: '#fff',
title: {
+10 -2
View File
@@ -54,7 +54,8 @@
<div
v-for="(instance, idx) in item.instanceInfoList"
:key="instance.InstanceUid"
class="frame_content"
class="frame_content"
:class="{ 'frame_content_active': activeInstanceUid === instance.InstanceUid }"
:style="{'margin-bottom':idx<item.instanceInfoList.length-1? '5px':'0px'}"
@click="showMultiFrames(item, index, instance)"
>
@@ -136,7 +137,8 @@ export default {
studyTitle: '',
seriesCount: 0,
seriesList: [],
currentSeriesIndex: -1
currentSeriesIndex: -1,
activeInstanceUid: null
}
},
@@ -231,6 +233,7 @@ export default {
},
showMultiFrames(series, seriesIndex, instanceInfo) {
this.currentSeriesIndex = seriesIndex
this.activeInstanceUid = instanceInfo.InstanceUid
const imageIds = []
if (instanceInfo.NumberOfFrames && instanceInfo.NumberOfFrames > 1) {
for (let j = 0; j < instanceInfo.NumberOfFrames; j++) {
@@ -252,6 +255,7 @@ export default {
showSeriesImage(seriesIndex) {
// if (seriesIndex === this.currentSeriesIndex) return;
this.currentSeriesIndex = seriesIndex
this.activeInstanceUid = null
this.$refs.dicomViewer.loadImageStack(this.seriesList[seriesIndex])
},
closeDialog() {
@@ -429,4 +433,8 @@ export default {
border-color: #213a54 !important;
background-color: #213a54;
}
.frame_content_active {
border-color: #213a54 !important;
background-color: #213a54;
}
</style>
+44 -17
View File
@@ -53,6 +53,7 @@
<div class="frame_list">
<div v-for="(instance, idx) in item.instanceInfoList" :key="instance.Id"
class="frame_content"
:class="{ 'frame_content_active': activeInstanceId === instance.Id }"
:style="{ 'margin-bottom': idx < item.instanceInfoList.length - 1 ? '5px' : '0px' }"
@click="showMultiFrames(item, index, instance)">
<div>
@@ -72,17 +73,17 @@
</div>
</div>
<i slot="reference" class="el-icon-connection" style="font-size: 15px;cursor: pointer;"
@click="popperClick(seriesList, item)" />
@click.stop="popperClick(seriesList, item)" />
</el-popover>
</div>
</div>
<div v-show="item.instanceCount" style="padding: 1px;">
<div v-if="item.instanceCount" style="padding: 1px;">
{{ item.modality }}: {{ item.instanceCount }} image
</div>
<div v-show="!item.keySeries && item.sliceThickness" style="padding: 1px;">
<div v-if="!item.keySeries && item.sliceThickness" style="padding: 1px;">
T: {{ parseFloat(item.sliceThickness).toFixed(2) }}
</div>
<div v-show="!item.keySeries && item.description"
<div v-if="!item.keySeries && item.description"
style="width: 120px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;padding: 1x;">
{{ item.description }}
</div>
@@ -123,12 +124,13 @@
</div>
</div>
<div class="viewerContent">
<dicom-viewer id="dicomViewer" ref="dicomViewer" style="height:100%" />
<dicom-viewer id="dicomViewer" ref="dicomViewer" style="height:100%" :loading.sync="loading"
:modality="modality" :Comparison.sync="isComparison" :SeriesList="seriesList"
:currentSeriesIndex="currentSeriesIndex" @loadStudy="loadStudy" />
</div>
<!-- <div class="viewerRightSidePanel">
<dicom-tools />
</div> -->
</div>
</div>
@@ -178,7 +180,7 @@ export default {
description: '',
seriesCount: 0,
seriesList: [],
currentSeriesIndex: -1,
currentSeriesIndex: 0,
arr: [],
activeName: 'first',
tpList: [],
@@ -202,7 +204,9 @@ export default {
isFromCRCUpload: false,
isReading: null,
activeSeriesId: null,
isPacs: false
isPacs: false,
isComparison: false,
activeInstanceId: null
}
},
created: function () {
@@ -250,6 +254,11 @@ export default {
})
workSpeedclose(true)
},
watch: {
currentSeriesIndex() {
console.log(this.currentSeriesIndex, 'currentSeriesIndex')
}
},
methods: {
async updateImageResizePath(data) {
try {
@@ -307,7 +316,7 @@ export default {
})
})
},
async loadStudy() {
async loadStudy(seriesId = null) {
let params = {}
if (this.isPacs) {
params.IsPacs = true
@@ -327,7 +336,7 @@ export default {
isReading = `?IsPacs=true`
}
const url = `/series/list/${this.studyId}${isReading}`
this.getSeriesList(url)
this.getSeriesList(url, seriesId)
}
},
async loadPatientStudy() {
@@ -350,13 +359,14 @@ export default {
i.ImageId = imageId
}
})
var subjectVisitId = this.$router.currentRoute.query.subjectVisitId
var subjectVisitId = this.$router.currentRoute.query.subjectVisitId ? this.$router.currentRoute.query.subjectVisitId : item.SubjectVisitId
var studyId = this.$router.currentRoute.query.studyId
var trialId = this.$router.currentRoute.query.trialId
seriesList.push({
trialId,
subjectVisitId,
studyId,
subjectId: item.SubjectId,
imageIds: imageIds,
instanceInfoList: item.InstanceInfoList,
seriesId: item.Id,
@@ -392,7 +402,7 @@ export default {
console.log(err)
}
},
async getSeriesList(url) {
async getSeriesList(url, seriesId = null) {
try {
const data = await getSeriesList(url)
if (data.IsSuccess) {
@@ -412,12 +422,13 @@ export default {
i.ImageId = imageId
}
})
var subjectVisitId = this.$router.currentRoute.query.subjectVisitId
var subjectVisitId = this.$router.currentRoute.query.subjectVisitId ? this.$router.currentRoute.query.subjectVisitId : item.SubjectVisitId
var studyId = this.$router.currentRoute.query.studyId
var trialId = this.$router.currentRoute.query.trialId
seriesList.push({
trialId,
subjectVisitId,
subjectId: item.SubjectId,
studyId,
imageIds: imageIds,
instanceInfoList: item.InstanceInfoList,
@@ -431,12 +442,12 @@ export default {
isDeleted: item.IsDeleted,
previewImageUrl: item.ImageResizePath ? this.OSSclientConfig.basePath + item.ImageResizePath : `/api/series/preview/${item.Id}`,
instanceCount: item.InstanceCount,
prefetchInstanceCount: 0,
prefetchInstanceCount: seriesId ? item.InstanceInfoList.length * 100 : 0,
hasLabel: item.HasLabel,
keySeries: item.KeySeries,
tpCode: this.tpCode,
loadStatus: false,
imageloadedArr: [],
imageloadedArr: seriesId ? imageIds : [],
isExistMutiFrames: item.IsExistMutiFrames,
isShowPopper: false,
subjectCode: item.SubjectCode,
@@ -446,8 +457,14 @@ export default {
this.seriesList = seriesList
if (this.seriesList.length > 0) {
this.loadAllImages()
this.$refs.dicomViewer.loadImageStack(this.seriesList[0], this.labels[this.tpCode])
this.firstInstanceId = this.seriesList[0].imageIds[0]
let index = 0;
if (seriesId) {
index = this.seriesList.findIndex(item => item.seriesId === seriesId)
this.refreshImage(this.seriesList[index])
}
this.$refs.dicomViewer.loadImageStack(this.seriesList[index], this.labels[this.tpCode])
this.firstInstanceId = this.seriesList[index].imageIds[0]
}
}
} catch (e) {
@@ -527,7 +544,9 @@ export default {
}
},
showSeriesImage(e, seriesIndex, series) {
if (this.isComparison) return false
this.activeSeriesId = series.seriesId
this.activeInstanceId = null
workSpeedclose(true)
// if (seriesIndex === this.currentSeriesIndex) return
const element = e.currentTarget
@@ -576,6 +595,7 @@ export default {
},
showMultiFrames(series, seriesIndex, instanceInfo) {
this.currentSeriesIndex = seriesIndex
this.activeInstanceId = instanceInfo.Id
const imageIds = []
if (instanceInfo.NumberOfFrames && instanceInfo.NumberOfFrames > 1) {
for (let j = 0; j < instanceInfo.NumberOfFrames; j++) {
@@ -587,6 +607,7 @@ export default {
const seriesInfo = {
trialId: series.trialId,
subjectVisitId: series.subjectVisitId,
instanceInfoList: series.instanceInfoList,
studyId: series.studyId,
imageIds: imageIds,
seriesId: series.seriesId,
@@ -920,6 +941,7 @@ export default {
}
if (this.visitTaskId === params.visitTaskId) {
const seriesIndex = params.seriesIndex
if (!this.seriesList[seriesIndex]) return false
var prefetchInstanceCount = this.seriesList[seriesIndex].prefetchInstanceCount
var instanceCount = this.seriesList[seriesIndex].instanceCount
if (!this.activeSeriesId) {
@@ -1184,6 +1206,11 @@ export default {
background-color: #213a54;
}
.frame_content_active {
border-color: #213a54 !important;
background-color: #213a54;
}
/* .viewerRightSidePanel {
width: 300px;
height: 100%;
+133 -46
View File
@@ -19,25 +19,26 @@
<el-collapse-item v-for="(study, index) in studyList" :key="`${study.StudyId}`"
:name="`${study.StudyId}`">
<template slot="title">
<div class="text-desc">
{{ study.StudyCode }}
</div>
<!-- <div v-show="study.Description" class="text-desc">
<div class="collapse-title-wrapper">
<div class="text-desc" v-if="IsReadingTaskViewInOrder !== 0">
{{ study.StudyCode }}
</div>
<!-- <div v-show="study.Description" class="text-desc">
{{ study.Description }}
</div> -->
<el-tooltip v-show="study.Description" class="item" effect="dark" :content="study.Description"
<!-- <el-tooltip v-show="study.Description" class="item" effect="dark" :content="study.Description"
placement="bottom">
<div v-show="study.Description"
<div v-if="study.Description"
style="width: 50px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;padding: 1x;">
{{ study.Description }}
</div>
</el-tooltip>
<div v-show="study.SeriesCount" class="text-desc">
{{ study.Modalities }} : {{ study.SeriesCount }} Series
</el-tooltip> -->
<div v-if="study.SeriesCount" class="text-desc collapse-title-extra">
{{ study.Modalities }} : {{ study.SeriesCount }} Series
</div>
</div>
</template>
<div v-show="study.Description" class="text-desc" style="background-color: #1f1f1f;">
<div v-if="study.Description" class="text-desc" style="background-color: #1f1f1f;">
{{ study.Description }}
</div>
<div v-for="(series, i) in study.SeriesList" :key="i"
@@ -65,6 +66,7 @@
<div class="frame_list">
<div v-for="(instance, idx) in series.instanceInfoList" :key="instance.Id"
class="frame_content"
:class="{ 'frame_content_active': activeInstanceId === instance.Id }"
:style="{ 'margin-bottom': idx < series.instanceInfoList.length - 1 ? '5px' : '0px' }"
@click="showMultiFrames(index, series, i, instance)">
<!-- <div>
@@ -97,12 +99,12 @@
</div>
<i slot="reference" class="el-icon-connection"
style="font-size: 15px;cursor: pointer;"
@click="popperClick(studyList, series)" />
@click.stop="popperClick(studyList, series)" />
</el-popover>
</div>
</div>
<div v-show="series.InstanceCount" style="padding: 1px;">
<div v-show="series.instanceCount" style="padding: 1px;">
{{ series.modality }}: {{ series.instanceCount }} image
</div>
<div v-show="series.sliceThickness" style="padding: 1px;">
@@ -153,8 +155,8 @@
</div>
</div>
</el-tab-pane>
<el-tab-pane v-show="!visitTaskId" :label="$t('trials:dicom-show:relatedVisit')" name="relation-study"
class="pane-relation-wrapper">
<el-tab-pane v-show="!visitTaskId" v-if="IsReadingTaskViewInOrder >= 2"
:label="$t('trials:dicom-show:relatedVisit')" name="relation-study" class="pane-relation-wrapper">
<div class="viewerSidethumbinner">
<el-collapse v-model="relationActiveName" @change="handelRelationActiveChange">
@@ -166,16 +168,17 @@
<el-collapse-item :key="`${study.StudyId}`" :name="`${study.StudyId}`"
v-if="study.VisitName === item.VisitName">
<template slot="title">
<div class="text-desc">
{{ study.StudyCode }}
</div>
<!-- <div v-show="study.Description" class="text-desc">
<div class="collapse-title-wrapper">
<div class="text-desc" v-if="IsReadingTaskViewInOrder !== 0">
{{ study.StudyCode }}
</div>
<!-- <div v-show="study.Description" class="text-desc">
{{ study.Description }}
</div> -->
<div v-show="study.SeriesCount" class="text-desc">
{{ study.Modalities }} : {{ study.SeriesCount }} Series
<div v-show="study.SeriesCount" class="text-desc collapse-title-extra">
{{ study.Modalities }} : {{ study.SeriesCount }} Series
</div>
</div>
</template>
<div v-show="study.Description" class="text-desc" style="background-color: #1f1f1f;">
@@ -184,19 +187,18 @@
<div class="viewerSidethumbs ps" style="position: relative;">
<div class="viewerSidethumbinner">
<div v-for="(seriesItem, index) in study.seriesList" :key="seriesItem.seriesId">
<div class="viewernavigatorwrapper"
style="position: relative;border:1px solid #434343;" series-type="relation"
<div class="viewernavigatorwrapper" style="position: relative;border:1px solid #434343;"
series-type="relation"
@click="showRelationSeriesImage($event, seriesItem, studyIndex, index)">
<div class="imageBox" style="width: 72px;height:72px;">
<img class="image-preview" :src="seriesItem.previewImageUrl"
crossorigin="anonymous" alt="" style="width: 72px;height:72px;" fit="fill" />
<img class="image-preview" :src="seriesItem.previewImageUrl" crossorigin="anonymous"
alt="" style="width: 72px;height:72px;" fit="fill" />
<i class="el-icon-refresh" :title="$t('tip:refreshImage')"
@click.stop="refreshImage(item)"></i>
</div>
<div class="viewernavitextwrapper">
<div
style="padding: 1px 5px 1px 1px;display: flex;justify-content: space-between;">
<div style="padding: 1px 5px 1px 1px;display: flex;justify-content: space-between;">
<div v-if="seriesItem.keySeries" style="color:red">
Key Images
</div>
@@ -207,6 +209,7 @@
<div class="frame_list">
<div v-for="(instance, idx) in seriesItem.instanceInfoList"
:key="instance.Id" class="frame_content"
:class="{ 'frame_content_active': activeInstanceId === instance.Id }"
:style="{ 'margin-bottom': idx < seriesItem.instanceInfoList.length - 1 ? '5px' : '0px' }"
@click="showMultiFrames(studyIndex, seriesItem, index, instance)">
<div>
@@ -327,7 +330,9 @@ export default {
isFromCRCUpload: false,
visitTaskId: null,
page: '',
activeSeriesId: null
activeSeriesId: null,
activeInstanceId: null,
IsReadingTaskViewInOrder: 2
}
},
mounted() {
@@ -346,20 +351,22 @@ export default {
this.isFromCRCUpload = !!this.$router.currentRoute.query.isFromCRCUpload
this.visitTaskId = this.$router.currentRoute.query.visitTaskId
this.page = this.$route.query.page
if (this.$router.currentRoute.query.IsReadingTaskViewInOrder) {
this.IsReadingTaskViewInOrder = Number(this.$router.currentRoute.query.IsReadingTaskViewInOrder)
}
console.log(this.IsReadingTaskViewInOrder)
this.beforeUnloadHandler = () => {
cornerstone.imageCache.purgeCache()
requestPoolManager.resetRequestPool()
}
// cornerstone.events.addEventListener('cornerstoneimageloaded', this.cornerstoneImageLoaded)
this.getStudiesInfo()
cornerstone.events.addEventListener('cornerstoneimageloadprogress', this.cornerstoneimageloadprogress)
window.addEventListener('beforeunload', e => {
cornerstone.imageCache.purgeCache()
requestPoolManager.resetRequestPool()
})
window.addEventListener('beforeunload', this.beforeUnloadHandler)
},
beforeDestroy() {
requestPoolManager.stopTaskTimer()
window.removeEventListener('beforeunload', e => {
cornerstone.imageCache.purgeCache()
requestPoolManager.resetRequestPool()
})
window.removeEventListener('beforeunload', this.beforeUnloadHandler)
workSpeedclose(true)
},
methods: {
@@ -489,14 +496,17 @@ export default {
data.SeriesList = seriesList
this.studyList.push(data)
})
if (this.studyList.length > 0) {
this.$refs.dicomViewer.loadImageStack(this.studyList[0].SeriesList[0])
const imageId = this.studyList[0].SeriesList[0].imageIds[0]
const firstStudy = this.studyList.find(study => Array.isArray(study.SeriesList) && study.SeriesList.length > 0)
if (firstStudy) {
const firstSeries = firstStudy.SeriesList[0]
if (!Array.isArray(firstSeries.imageIds) || firstSeries.imageIds.length === 0) return
this.$refs.dicomViewer.loadImageStack(firstSeries)
const imageId = firstSeries.imageIds[0]
let instanceId = imageId.split('/')[imageId.split('/').length - 1]
instanceId = instanceId.split('.')[0]
this.firstInstanceId = instanceId
this.activeNames = [this.studyList[0].StudyId]
this.loadImages(this.studyList[0].SeriesList[0], 0)
this.activeNames = [firstStudy.StudyId]
this.loadImages(firstSeries, 0)
}
}
} catch (e) {
@@ -521,6 +531,7 @@ export default {
},
showSeriesImage(e, studyIndex, seriesIndex, series) {
this.activeSeriesId = series.seriesId
this.activeInstanceId = null
workSpeedclose(true)
const element = e.currentTarget
const elements = document.querySelectorAll('[series-type]')
@@ -562,6 +573,7 @@ export default {
},
showMultiFrames(studyIndex, series, seriesIndex, instanceInfo) {
this.currentSeriesIndex = seriesIndex
this.activeInstanceId = instanceInfo.Id
const imageIds = []
if (instanceInfo.NumberOfFrames && instanceInfo.NumberOfFrames > 1) {
for (let j = 0; j < instanceInfo.NumberOfFrames; j++) {
@@ -573,6 +585,7 @@ export default {
const seriesInfo = {
trialId: series.trialId,
subjectVisitId: series.subjectVisitId,
instanceInfoList: series.instanceInfoList,
studyId: series.studyId,
imageIds: imageIds,
seriesId: series.seriesId,
@@ -598,7 +611,11 @@ export default {
}
if (!isAddToTakPool) {
var priority = parseInt(new Date().getTime())
if (series.isExistMutiFrames) {
if (series.isExistMutiFrames && imageIds.length > 1) {
imageIds.map(imageId => {
this.imageList.push({ imageId, seriesId: series.seriesId, priority })
})
} else if (series.isExistMutiFrames) {
series.instanceInfoList.map(image => {
this.imageList.push({ imageId: image.ImageId, seriesId: series.seriesId, priority })
})
@@ -880,6 +897,7 @@ export default {
},
showRelationSeriesImage(e, series, studyIndex, index) {
this.activeSeriesId = series.seriesId
this.activeInstanceId = null
workSpeedclose(true)
this.currentRelationIndex = index
const element = e.currentTarget
@@ -937,7 +955,7 @@ export default {
if (this.imageList.length > 0) {
requestPoolManager.startTaskTimer()
this.imageList.map(image => {
requestPoolManager.loadAndCacheImagePlus(image.imageId, image.seriesId, image.priority)
requestPoolManager.loadAndCacheImagePlus(image.imageId, image.seriesId, image.priority).catch(() => { })
})
requestPoolManager.sortTaskPool()
this.imageList = []
@@ -1099,6 +1117,7 @@ export default {
padding: 0;
margin-right: 2px;
color: #D0D0D0;
overflow-x: hidden;
overflow-y: auto;
}
@@ -1107,6 +1126,8 @@ export default {
word-break: break-all;
display: table;
width: 100%;
box-sizing: border-box;
overflow-x: hidden;
border: 1px solid #3e3f3a;
}
@@ -1135,14 +1156,20 @@ export default {
.viewerContainer .viewerLeftSidePanel .viewernavigatorwrapper {
display: flex;
width: 220px;
/* height: 84px; */
min-height: 85px;
padding: 1px 2px 1px 2px;
margin: 2px 0 1px 1px;
align-items: flex-start;
/* border-radius: 2px;
border: 1px solid #404040; */
}
.viewerContainer .viewerLeftSidePanel .viewernavigatorwrapper .imageBox {
align-self: center;
flex-shrink: 0;
}
.viewerContainer .viewernavigatorwrapper .el-progress__text {
display: none;
}
@@ -1165,9 +1192,19 @@ export default {
/* width: 120px;
height: 80px; */
flex: 1;
min-width: 0;
padding: 3px 1px 3px 4px;
vertical-align: top;
font-size: 12px;
line-height: 1.4;
overflow: hidden;
}
.viewerContainer .viewerLeftSidePanel .viewernavitextwrapper>div {
max-width: 100%;
white-space: normal;
word-break: break-word;
overflow-wrap: anywhere;
}
.viewerContainer .viewerLeftSidePanel .viewerlabelwrapper {
@@ -1197,12 +1234,24 @@ export default {
.viewerContainer .el-collapse {
border: none;
width: 100%;
min-width: 0;
box-sizing: border-box;
overflow-x: hidden;
}
.viewerContainer .el-collapse-item {
background-color: #585453 !important;
color: #ddd;
width: 100%;
min-width: 0;
box-sizing: border-box;
}
.viewerContainer .el-collapse-item__wrap {
width: 100%;
min-width: 0;
box-sizing: border-box;
}
.viewerContainer .el-collapse-item__content {
@@ -1215,9 +1264,42 @@ export default {
background-color: #585453 !important;
color: #ddd;
border-bottom-color: #5a5a5a;
width: 100%;
min-width: 0;
box-sizing: border-box;
padding-left: 5px;
height: 40px;
height: auto;
min-height: 30px;
line-height: 20px;
align-items: flex-start;
padding-top: 4px;
padding-bottom: 4px;
}
.viewerContainer .collapse-title-wrapper {
display: flex;
flex: 1;
min-width: 0;
flex-wrap: wrap;
align-items: center;
column-gap: 6px;
row-gap: 2px;
line-height: 16px;
padding-right: 8px;
}
.viewerContainer .collapse-title-wrapper .text-desc {
flex: 0 1 auto;
min-width: 0;
max-width: 100%;
white-space: normal;
word-break: break-word;
overflow-wrap: anywhere;
}
.viewerContainer .collapse-title-wrapper .collapse-title-extra {
flex: 0 1 auto;
text-align: left;
}
.instance_frame_wrapper {
@@ -1261,6 +1343,11 @@ export default {
background-color: #213a54;
}
.frame_content_active {
border-color: #213a54 !important;
background-color: #213a54;
}
/* .viewerRightSidePanel {
width: 300px;
height: 100%;
@@ -22,6 +22,11 @@
</el-upload>
</div>
</el-form-item>
<el-form-item :label="$t('dictionary:signature:form:DocLanguageType')" prop="DocLanguageType">
<el-select v-model="form.DocLanguageType" style="width: 100%">
<el-option v-for="item of $d.DocLanguageType" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item :label="$t('dictionary:signature:form:NeedConfirmedUserTypeIdList')"
prop="NeedConfirmedUserTypeIdList">
<el-select v-model="form.NeedConfirmedUserTypeIdList" style="width: 100%" multiple>
@@ -83,6 +88,7 @@ export default {
DocUserSignType: 0,
CurrentStaffTrainDays: 1,
NewStaffTrainDays: 14,
DocLanguageType: null
},
rules: {
FileTypeId: [
@@ -92,6 +98,13 @@ export default {
trigger: ['blur'],
},
],
DocLanguageType: [
{
required: true,
message: this.$t('common:ruleMessage:select'),
trigger: ['blur'],
},
],
SignViewMinimumMinutes: [
{
required: true,
@@ -143,6 +156,9 @@ export default {
this.form.DocUserSignType = this.data.DocUserSignType
this.form.CurrentStaffTrainDays = this.data.CurrentStaffTrainDays
this.form.NewStaffTrainDays = this.data.NewStaffTrainDays
this.form.DocLanguageType = this.data.DocLanguageType
} else {
this.form.DocLanguageType = this.$i18n.locale !== 'zh' ? 1 : 0
}
this.loading = false
},
@@ -26,6 +26,11 @@
<el-form-item :label="$t('dictionary:signature:search:Name')">
<el-input v-model="searchData.Name" style="width: 130px" clearable />
</el-form-item>
<el-form-item :label="$t('dictionary:signature:search:DocLanguageType')">
<el-select v-model="searchData.DocLanguageType" style="width: 150px" clearable>
<el-option v-for="item of $d.DocLanguageType" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item :label="$t('dictionary:signature:table:NeedConfirmedUserTypes')">
<el-select v-model="searchData.UserTypeId" style="width: 150px" clearable>
<el-option v-for="item of userTypeOptions" :key="item.Id" :label="item.UserTypeShortName"
@@ -90,6 +95,12 @@
</el-button>
</template>
</el-table-column>
<el-table-column prop="DocLanguageType" :label="$t('dictionary:signature:table:DocLanguageType')"
show-overflow-tooltip sortable="custom" min-width="120px">
<template slot-scope="scope">
<span>{{ $fd('DocLanguageType', scope.row.DocLanguageType) }}</span>
</template>
</el-table-column>
<el-table-column prop="SignViewMinimumMinutes" :label="$t('dictionary:signature:table:SignViewMinimumMinutes')"
show-overflow-tooltip sortable="custom" min-width="200px" />
<el-table-column prop="CurrentStaffTrainDays" :label="$t('dictionary:signature:table:CurrentStaffTrainDays')"
@@ -161,7 +172,7 @@
<!-- 新增/编辑 -->
<el-dialog v-if="editVisible" :visible.sync="editVisible" :close-on-click-modal="false" :title="title"
width="600px" custom-class="base-dialog-wrapper">
<TemplateForm :data="currentRow" @closeDialog="closeDialog" @getList="getList" />
<TemplateForm :data="currentRow" @closeDialog="closeDialog" v-if="editVisible" @getList="getList" />
</el-dialog>
<!--附件列表-->
<attachmentList v-if="config.visible" :config="config" :rowData="currentRow" :SystemDocumentId="SystemDocumentId"
@@ -200,6 +211,7 @@ const searchDataDefault = () => {
IsDeleted: null,
DocUserSignType: null,
UserTypeId: null,
DocLanguageType: null,
Name: '',
PageIndex: 1,
PageSize: 20,
+25 -11
View File
@@ -26,11 +26,19 @@
<el-table v-if="refreshTable" v-adaptive="{ bottomOffset: 45 }" v-loading="loading" height="100" :data="menuList"
:default-expand-all="isExpandAll" row-key="Id" :tree-props="{ children: 'Children', hasChildren: 'hasChildren' }">
<!-- 模块 -->
<el-table-column prop="ModuleTypeValueCN" :label="$t('audit:label:moduleType')" show-overflow-tooltip align="left" min-width="230px" />
<el-table-column v-if="language === 'en'" prop="ModuleTypeValue" :label="$t('audit:label:moduleType')" show-overflow-tooltip align="left" min-width="230px">
</el-table-column>
<el-table-column v-else prop="ModuleTypeValueCN" :label="$t('audit:label:moduleType')" show-overflow-tooltip align="left" min-width="230px">
</el-table-column>
<!-- 操作名称 -->
<el-table-column prop="Description" :label="$t('audit:label:desc')" :show-overflow-tooltip="true" min-width="200px">
<el-table-column v-if="language === 'en'" prop="Description" :label="$t('audit:label:desc')" :show-overflow-tooltip="true" min-width="200px">
<template slot-scope="scope">
<span>{{ scope.row.DescriptionCN }}{{ scope.row.Description }}</span>
<span>{{ scope.row.Description }}</span>
</template>
</el-table-column>
<el-table-column v-else prop="DescriptionCN" :label="$t('audit:label:desc')" :show-overflow-tooltip="true" min-width="200px">
<template slot-scope="scope">
<span>{{ scope.row.DescriptionCN }}</span>
</template>
</el-table-column>
<!-- 其他 -->
@@ -61,17 +69,20 @@
</template>
</el-table-column>
<!-- 标识 -->
<el-table-column prop="Identification" :label="$t('audit:label:identification')" :show-overflow-tooltip="true" min-width="200px" />
<el-table-column prop="Identification" :label="$t('audit:label:identification')" :show-overflow-tooltip="true" min-width="120px" />
<!-- 操作类型 -->
<el-table-column prop="OptTypeValueCN" :label="$t('audit:label:optType')" show-overflow-tooltip width="100px" />
<el-table-column v-if="language === 'en'" prop="OptTypeValue" :label="$t('audit:label:optType')" show-overflow-tooltip min-width="110px" />
<el-table-column v-else prop="OptTypeValueCN" :label="$t('audit:label:optType')" show-overflow-tooltip min-width="110px" />
<!-- 对象类型 -->
<el-table-column prop="ObjectTypeValueCN" :label="$t('audit:label:objectType')" show-overflow-tooltip min-width="100px" />
<el-table-column v-if="language === 'en'" prop="ObjectTypeValue" :label="$t('audit:label:objectType')" show-overflow-tooltip width="100px" />
<el-table-column v-else prop="ObjectTypeValueCN" :label="$t('audit:label:objectType')" show-overflow-tooltip min-width="100px" />
<!-- 数据类型 -->
<el-table-column prop="ChildrenTypeValueCN" :label="$t('audit:label:dataType')" show-overflow-tooltip min-width="100px" />
<el-table-column v-if="language === 'en'" prop="ChildrenTypeValue" :label="$t('audit:label:dataType')" show-overflow-tooltip min-width="100px" />
<el-table-column v-else prop="ChildrenTypeValueCN" :label="$t('audit:label:dataType')" show-overflow-tooltip min-width="100px" />
<!-- 显示顺序 -->
<el-table-column prop="Sort" :label="$t('audit:label:sort')" min-width="80" show-overflow-tooltip />
<el-table-column prop="Sort" :label="$t('audit:label:sort')" min-width="100" show-overflow-tooltip />
<!-- 启用 -->
<el-table-column :label="$t('audit:label:isEnable')" align="center" prop="CreateTime" min-width="80">
<el-table-column :label="$t('audit:label:isEnable')" align="center" prop="CreateTime" min-width="90">
<!-- 启用/禁用 -->
<template slot-scope="scope">
<!-- <el-tag v-if="scope.row.IsEnable" size="success">启用</el-tag>
@@ -81,7 +92,7 @@
</el-tag>
</template>
</el-table-column>
<el-table-column :label="$t('common:action:action')" align="center" fixed="right" min-width="340" class-name="small-padding fixed-width">
<el-table-column :label="$t('common:action:action')" align="center" fixed="right" width="290" class-name="small-padding fixed-width">
<template slot-scope="scope">
<!-- 上移 -->
<el-button
@@ -659,7 +670,7 @@ import Treeselect from '@riophae/vue-treeselect'
import '@riophae/vue-treeselect/dist/vue-treeselect.css'
import Sortable from 'sortablejs'
import BaseModel from "@/components/BaseModel";
import { mapGetters } from 'vuex'
export default {
name: 'CheckConfig',
dicts: ['OptType', 'ObjectType', 'ModuleType', 'ChildrenType'],
@@ -715,6 +726,9 @@ export default {
this.getList();
this.getDictionaryCodeList();
},
computed: {
...mapGetters(['language'])
},
methods: {
querySearch(queryString, cb) {
var DictionaryCodeList = this.DictionaryCodeList;
@@ -1,7 +1,7 @@
<template>
<box-content>
<div class="search">
<el-form :inline="true" class="base-search-form">
<el-form :inline="true" class="base-search-form" @submit.native.prevent="">
<!-- CRO Name -->
<el-form-item :label="$t('institutions:cros:label:croName')">
<el-input v-model="searchData.CROName" style="width: 100px" />
@@ -1,13 +1,6 @@
<template>
<el-form
ref="siteForm"
v-loading="loading"
:model="form"
:rules="rules"
class="demo-ruleForm"
size="small"
label-width="150px"
>
<el-form ref="siteForm" v-loading="loading" :model="form" :rules="rules" class="demo-ruleForm" size="small"
label-width="150px">
<div class="base-dialog-body">
<!-- Site Code -->
<el-form-item :label="$t('institutions:sites:label:siteCode')" v-if="form.Id" prop="SiteCode">
@@ -15,17 +8,11 @@
</el-form-item>
<!-- 中心名称 -->
<el-form-item
:label="$t('institutions:sites:label:siteName')"
prop="SiteName"
>
<el-form-item :label="$t('institutions:sites:label:siteName')" prop="SiteName">
<el-input v-model="form.SiteName" />
</el-form-item>
<!-- 中心名称CN -->
<el-form-item
:label="$t('institutions:sites:label:siteNameCN')"
prop="SiteNameCN"
>
<el-form-item :label="$t('institutions:sites:label:siteNameCN')" prop="SiteNameCN">
<el-input v-model="form.SiteNameCN" />
</el-form-item>
<!-- Alias Name -->
@@ -38,7 +25,9 @@
</el-form-item> -->
<!-- Country -->
<el-form-item :label="$t('institutions:sites:label:country')" prop="Country">
<el-input v-model="form.Country" />
<el-select v-model="form.Country" style="width: 100%">
<el-option v-for="item of $d.SiteCountry" :key="item.id" :label="item.label" :value="item.label" />
</el-select>
</el-form-item>
<!-- City -->
<el-form-item :label="$t('institutions:sites:label:city')" prop="City">
@@ -50,17 +39,8 @@
</el-form-item>
<!-- Affiliated Hospital -->
<el-form-item :label="$t('institutions:sites:label:affiliatedHospital')">
<el-select
v-model="form.HospitalId"
clearable
style="width: 100%"
>
<el-option
v-for="item in hospitalList"
:key="item.Id"
:label="item.HospitalName"
:value="item.Id"
/>
<el-select v-model="form.HospitalId" clearable style="width: 100%">
<el-option v-for="item in hospitalList" :key="item.Id" :label="item.HospitalName" :value="item.Id" />
</el-select>
</el-form-item>
<!-- Director Name -->
@@ -82,20 +62,10 @@
</div>
<div class="base-dialog-footer" style="text-align: right; margin-top: 10px">
<el-form-item>
<el-button
:disabled="btnLoading"
size="small"
type="primary"
@click="handleCancel"
>{{ $t('common:button:cancel') }}</el-button
>
<el-button
size="small"
type="primary"
:loading="btnLoading"
@click="handleSave"
>{{ $t('common:button:save') }}</el-button
>
<el-button :disabled="btnLoading" size="small" type="primary" @click="handleCancel">{{
$t('common:button:cancel') }}</el-button>
<el-button size="small" type="primary" :loading="btnLoading" @click="handleSave">{{ $t('common:button:save')
}}</el-button>
</el-form-item>
</div>
</el-form>
@@ -1,7 +1,7 @@
<template>
<box-content>
<div class="search">
<el-form :inline="true" class="base-search-form">
<el-form :inline="true" class="base-search-form" @submit.native.prevent="">
<!-- Sponsor Name -->
<el-form-item :label="$t('institutions:sponsors:label:sponsorName')">
<el-input v-model="searchData.SponsorName" style="width: 100px" />
+2 -2
View File
@@ -22,7 +22,7 @@
<el-input v-model="password.UserType" disabled />
</el-form-item> -->
<!-- 用户名 -->
<el-form-item :label="$t('recompose:form:userName')" prop="NewUserName">
<el-form-item class="my_new_pwd" :label="$t('recompose:form:userName')" prop="NewUserName">
<el-input v-model="password.NewUserName" :disabled="isUpdate" />
</el-form-item>
<!-- 新密码 -->
@@ -77,7 +77,7 @@ export default {
callback(
lang === 'zh'
? new Error(
'1新建账号,用户名字符长度最小为4个字符,最大为16个字符,只可使用字母、数字、下划线;'
'新建账号,用户名字符长度最小为4个字符,最大为16个字符,只可使用字母、数字、下划线;'
)
: new Error(
'For a new account, the username must have:1) At least 4 characters;2) At most 16 characters;3)Only letters, numbers, and underscores are allowed.'
+62 -217
View File
@@ -4,122 +4,57 @@
<el-form :inline="true">
<!--项目编号/实验名称-->
<el-form-item :label="$t('feedBack:search:trials')" v-if="level > 8">
<el-input
v-model="searchData.TrialKeyInfo"
clearable
style="width: 150px"
></el-input>
<el-input v-model="searchData.TrialKeyInfo" clearable style="width: 150px"></el-input>
</el-form-item>
<!--中心-->
<el-form-item :label="$t('feedBack:search:site')" v-if="level > 7">
<el-input
v-model="searchData.TrialSiteCode"
clearable
style="width: 150px"
></el-input>
<el-input v-model="searchData.TrialSiteCode" clearable style="width: 150px"></el-input>
</el-form-item>
<!--受试者访视-->
<el-form-item
:label="$t('feedBack:search:subjectVisit')"
v-if="level > 7"
>
<el-input
v-model="searchData.SubejctAndVisitKeyInfo"
clearable
style="width: 150px"
></el-input>
<el-form-item :label="$t('feedBack:search:subjectVisit')" v-if="level > 7">
<el-input v-model="searchData.SubejctAndVisitKeyInfo" clearable style="width: 150px"></el-input>
</el-form-item>
<!--角色-->
<el-form-item :label="$t('feedBack:search:role')" v-if="level > 7">
<el-select
v-model="searchData.UserTypeEnum"
clearable
style="width: 150px"
popper-class="feedBack-select-box"
>
<el-option
v-for="item of UserTypeOptins"
:key="item.value"
:label="item.label"
:value="item.value"
/>
<el-select v-model="searchData.UserTypeEnum" clearable style="width: 150px"
popper-class="feedBack-select-box">
<el-option v-for="item of UserTypeOptins" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!--反馈人-->
<el-form-item :label="$t('feedBack:search:user')" v-if="level > 7">
<el-input
v-model="searchData.FeedBackUserKeyInfo"
clearable
style="width: 150px"
></el-input>
<el-input v-model="searchData.FeedBackUserKeyInfo" clearable style="width: 150px"></el-input>
</el-form-item>
<!--问题类型-->
<el-form-item :label="$t('feedBack:search:questionType')">
<el-select
v-model="searchData.QuestionType"
clearable
style="width: 150px"
popper-class="feedBack-select-box"
>
<el-option
v-for="item of QuestionTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
<el-select v-model="searchData.QuestionType" clearable style="width: 150px"
popper-class="feedBack-select-box">
<el-option v-for="item of QuestionTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!--问题描述-->
<el-form-item :label="$t('feedBack:search:description')">
<el-input
v-model="searchData.QuestionDescription"
clearable
style="width: 150px"
></el-input>
<el-input v-model="searchData.QuestionDescription" clearable style="width: 150px"></el-input>
</el-form-item>
<!--状态-->
<el-form-item :label="$t('feedBack:search:status')" v-if="level > 7">
<el-select
v-model="searchData.State"
clearable
style="width: 150px"
popper-class="feedBack-select-box"
>
<el-option
v-for="item of $d.FeedBackStatus"
:key="item.value"
:label="item.label"
:value="item.value"
/>
<el-select v-model="searchData.State" clearable style="width: 150px" popper-class="feedBack-select-box">
<el-option v-for="item of $d.FeedBackStatus" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!--反馈日期-->
<el-form-item :label="$t('feedBack:search:time')">
<el-date-picker
v-model="datetimerange"
type="datetimerange"
:default-time="['00:00:00', '23:59:59']"
:start-placeholder="$t('feedBack:search:beginTime')"
:end-placeholder="$t('feedBack:search:endTime')"
value-format="yyyy-MM-dd HH:mm:ss"
@change="handleDatetimeChange"
style="width: 250px"
popper-class="feedBack-select-box"
/>
<el-date-picker v-model="datetimerange" type="datetimerange" :default-time="['00:00:00', '23:59:59']"
:start-placeholder="$t('feedBack:search:beginTime')" :end-placeholder="$t('feedBack:search:endTime')"
value-format="yyyy-MM-dd HH:mm:ss" @change="handleDatetimeChange" style="width: 250px"
popper-class="feedBack-select-box" />
</el-form-item>
<el-form-item>
<el-button
type="primary"
icon="el-icon-search"
@click="getList"
>
<el-button type="primary" icon="el-icon-search" @click="getList">
{{ $t('common:button:search') }}
</el-button>
<!-- 重置 -->
<el-button
icon="el-icon-refresh-left"
@click="handleReset"
>
<el-button icon="el-icon-refresh-left" @click="handleReset">
{{ $t('common:button:reset') }}
</el-button>
<!-- <el-button
@@ -131,97 +66,32 @@
{{ $t("common:button:export") }}
</el-button> -->
</el-form-item>
<el-button
type="primary"
@click="resolve"
style="float: right"
v-if="level > 7"
:disabled="tableSelectData.length <= 0"
>
<el-button type="primary" @click="resolve" style="float: right" v-if="level > 7"
:disabled="tableSelectData.length <= 0">
{{ $t('feedBack:button:resolve') }}
</el-button>
</el-form>
<el-table
v-loading="loading"
v-adaptive="{ bottomOffset: 45 }"
height="100"
:data="list"
class="table"
@selection-change="handleSelectChange"
@sort-change="handleSortByColumn"
:default-sort="{ prop: 'CreateTime', order: 'descending' }"
>
<el-table-column
type="selection"
align="center"
width="45"
v-if="level > 7"
:selectable="handleSelectTable"
/>
<el-table v-loading="loading" v-adaptive="{ bottomOffset: 45 }" height="100" :data="list" class="table"
@selection-change="handleSelectChange" @sort-change="handleSortByColumn"
:default-sort="{ prop: 'CreateTime', order: 'descending' }">
<el-table-column type="selection" align="center" width="45" v-if="level > 7" :selectable="handleSelectTable" />
<el-table-column type="index" width="50" />
<el-table-column
:label="$t('feedBack:form:trialCode')"
prop="TrialCode"
min-width="100"
show-overflow-tooltip
sortable="custom"
v-if="level > 8"
/>
<el-table-column
:label="$t('feedBack:form:study')"
prop="ExperimentName"
min-width="140"
show-overflow-tooltip
sortable="custom"
v-if="level > 8"
/>
<el-table-column
:label="$t('feedBack:form:siteCode')"
prop="TrialSiteCode"
min-width="100"
show-overflow-tooltip
sortable="custom"
v-if="level > 7"
/>
<el-table-column
:label="$t('feedBack:form:SubjectCode')"
prop="SubjectCode"
min-width="120"
show-overflow-tooltip
sortable="custom"
v-if="level > 7"
/>
<el-table-column
:label="$t('feedBack:form:subjectVisit')"
prop="SubjectVisitName"
min-width="120"
show-overflow-tooltip
sortable="custom"
v-if="level > 7"
/>
<el-table-column
:label="$t('feedBack:form:role')"
prop="FeedBackUserName"
min-width="100"
show-overflow-tooltip
sortable="custom"
v-if="level > 7"
/>
<el-table-column
:label="$t('feedBack:form:user')"
prop="FeedBackFullName"
min-width="140"
show-overflow-tooltip
sortable="custom"
v-if="level > 7"
/>
<el-table-column
:label="$t('feedBack:form:questionType')"
prop="QuestionType"
min-width="180"
show-overflow-tooltip
sortable="custom"
>
<el-table-column :label="$t('feedBack:form:trialCode')" prop="TrialCode" min-width="100" show-overflow-tooltip
sortable="custom" v-if="level > 8" />
<el-table-column :label="$t('feedBack:form:study')" prop="ExperimentName" min-width="140" show-overflow-tooltip
sortable="custom" v-if="level > 8" />
<el-table-column :label="$t('feedBack:form:siteCode')" prop="TrialSiteCode" min-width="100"
show-overflow-tooltip sortable="custom" v-if="level > 7" />
<el-table-column :label="$t('feedBack:form:SubjectCode')" prop="SubjectCode" min-width="120"
show-overflow-tooltip sortable="custom" v-if="level > 7" />
<el-table-column :label="$t('feedBack:form:subjectVisit')" prop="SubjectVisitName" min-width="120"
show-overflow-tooltip sortable="custom" v-if="level > 7" />
<el-table-column :label="$t('feedBack:form:role')" prop="FeedBackUserName" min-width="100" show-overflow-tooltip
sortable="custom" v-if="level > 7" />
<el-table-column :label="$t('feedBack:form:user')" prop="FeedBackFullName" min-width="140" show-overflow-tooltip
sortable="custom" v-if="level > 7" />
<el-table-column :label="$t('feedBack:form:questionType')" prop="QuestionType" min-width="180"
show-overflow-tooltip sortable="custom">
<template slot-scope="scope">
<span>{{
QuestionTypeOptions.filter(
@@ -230,49 +100,22 @@
}}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('feedBack:form:description')"
prop="QuestionDescription"
min-width="200"
show-overflow-tooltip
sortable="custom"
/>
<el-table-column
:label="$t('feedBack:form:time')"
prop="CreateTime"
min-width="180"
show-overflow-tooltip
sortable="custom"
/>
<el-table-column
:label="$t('feedBack:form:status')"
prop="State"
min-width="120"
show-overflow-tooltip
sortable="custom"
v-if="level > 7"
>
<el-table-column :label="$t('feedBack:form:description')" prop="QuestionDescription" min-width="200"
show-overflow-tooltip sortable="custom" />
<el-table-column :label="$t('feedBack:form:time')" prop="CreateTime" min-width="180" show-overflow-tooltip
sortable="custom" />
<el-table-column :label="$t('feedBack:form:status')" prop="State" min-width="120" show-overflow-tooltip
sortable="custom" v-if="level > 7">
<template slot-scope="scope">
<el-tag :type="['danger', 'success'][scope.row.State]">{{
<el-tag :type="['danger', 'success', 'danger'][scope.row.State]">{{
$fd('FeedBackStatus', scope.row.State)
}}</el-tag>
</template>
</el-table-column>
<el-table-column
:label="$t('feedBack:form:uploadTime')"
prop="UpdateTime"
min-width="180"
show-overflow-tooltip
sortable="custom"
v-if="level > 7"
/>
<el-table-column
:label="$t('common:action:action')"
fixed="right"
prop="UserTypeShortName"
width="100"
show-overflow-tooltip
>
<el-table-column :label="$t('feedBack:form:uploadTime')" prop="UpdateTime" min-width="180" show-overflow-tooltip
sortable="custom" v-if="level > 7" />
<el-table-column :label="$t('common:action:action')" fixed="right" prop="UserTypeShortName" width="100"
show-overflow-tooltip>
<template slot-scope="scope">
<el-button type="text" @click="getDetail(scope.row)">
{{ $t('common:button:view') }}
@@ -281,12 +124,8 @@
</el-table-column>
</el-table>
<div class="pagination" style="text-align: right; margin-top: 5px">
<pagination
:total="total"
:page.sync="searchData.PageIndex"
:limit.sync="searchData.PageSize"
@pagination="getList"
/>
<pagination :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize"
@pagination="getList" />
</div>
</div>
</div>
@@ -434,7 +273,7 @@ export default {
}
},
//
report() {},
report() { },
//
handleSelectChange(selection) {
// console.log(selection, "handleSelectChange");
@@ -472,31 +311,37 @@ export default {
display: flex;
padding: 10px;
border-radius: 5px;
.left {
display: flex;
flex-direction: column;
width: 0;
flex-grow: 4;
// border-right: 1px solid #ccc;
.filter-container {
display: flex;
align-items: center;
margin: 5px;
}
.data-table {
flex: 1;
padding: 5px 0px;
}
.pagination-container {
text-align: right;
}
}
.right {
width: 0;
flex-grow: 6;
overflow-y: auto;
border-right: 1px solid #ccc;
}
.selected-row {
background-color: cadetblue;
}
+276
View File
@@ -0,0 +1,276 @@
<template>
<div class="wscn-http404-container">
<div class="wscn-http404" style="display: flex;align-items: center;flex-wrap: wrap;justify-content: center;">
<div class="pic-404">
<!-- <img class="pic-404__parent" src="@/assets/login-bg.png" alt="404"> -->
<svg-icon icon-class="login-bg" style="width: 100%; height: 100%" />
<img class="pic-404__child left" src="@/assets/404_images/404_cloud.png" alt="404">
<img class="pic-404__child mid" src="@/assets/404_images/404_cloud.png" alt="404">
<img class="pic-404__child right" src="@/assets/404_images/404_cloud.png" alt="404">
</div>
<div class="bullshit">
<div class="bullshit__oops" style="color: #428bca">{{ $t('trials:researchForm:title:IsExpired') }}!</div>
<!-- <div class="bullshit__headline">{{ $t('trials:researchForm:title:IsExpired') }}</div> -->
<!-- <div class="bullshit__return-home" style="background: #428bca" @click="$router.push({ path: '/login' })">{{
$t('common:title:goHome') }}</div> -->
</div>
</div>
</div>
</template>
<script>
import { mapGetters, mapMutations } from 'vuex'
export default {
computed: {
...mapGetters([
'asyncRoutes',
'routes',
'language'
]),
},
mounted() {
let lang = this.$route.query.lang
if (!lang) {
const language = navigator.language
lang = 'en'
if (language.includes("zh")) {
lang = 'zh'
}
}
this.$i18n.locale = lang
this.setLanguage(lang)
this.$updateDictionary()
},
methods: {
...mapMutations({ setLanguage: 'lang/setLanguage' }),
}
}
</script>
<style lang="scss" scoped>
.wscn-http404-container {
transform: translate(-50%, -50%);
position: absolute;
top: 40%;
left: 50%;
width: 100%;
}
.wscn-http404 {
position: relative;
width: 100%;
padding: 0 50px;
overflow: hidden;
.pic-404 {
position: relative;
float: left;
width: 100%;
max-width: 500px;
height: 300px;
overflow: hidden;
margin-right: 10px;
&__parent {
width: 100%;
}
&__child {
position: absolute;
&.left {
width: 80px;
top: 17px;
left: 220px;
opacity: 0;
animation-name: cloudLeft;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
&.mid {
width: 46px;
top: 10px;
left: 420px;
opacity: 0;
animation-name: cloudMid;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1.2s;
}
&.right {
width: 62px;
top: 100px;
left: 500px;
opacity: 0;
animation-name: cloudRight;
animation-duration: 2s;
animation-timing-function: linear;
animation-fill-mode: forwards;
animation-delay: 1s;
}
@keyframes cloudLeft {
0% {
top: 17px;
left: 220px;
opacity: 0;
}
20% {
top: 33px;
left: 188px;
opacity: 1;
}
80% {
top: 81px;
left: 92px;
opacity: 1;
}
100% {
top: 97px;
left: 60px;
opacity: 0;
}
}
@keyframes cloudMid {
0% {
top: 10px;
left: 420px;
opacity: 0;
}
20% {
top: 40px;
left: 360px;
opacity: 1;
}
70% {
top: 130px;
left: 180px;
opacity: 1;
}
100% {
top: 160px;
left: 120px;
opacity: 0;
}
}
@keyframes cloudRight {
0% {
top: 100px;
left: 500px;
opacity: 0;
}
20% {
top: 120px;
left: 460px;
opacity: 1;
}
80% {
top: 180px;
left: 340px;
opacity: 1;
}
100% {
top: 200px;
left: 300px;
opacity: 0;
}
}
}
}
.bullshit {
position: relative;
float: left;
width: 300px;
padding: 30px 0;
overflow: hidden;
&__oops {
font-size: 32px;
font-weight: bold;
line-height: 40px;
color: #1482f0;
opacity: 0;
margin-bottom: 20px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-fill-mode: forwards;
text-align: center;
}
&__headline {
font-size: 20px;
line-height: 24px;
color: #222;
font-weight: bold;
opacity: 0;
margin-bottom: 10px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.1s;
animation-fill-mode: forwards;
}
&__info {
font-size: 13px;
line-height: 21px;
color: grey;
opacity: 0;
margin-bottom: 30px;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.2s;
animation-fill-mode: forwards;
}
&__return-home {
display: block;
float: left;
width: 110px;
height: 36px;
background: #1482f0;
border-radius: 100px;
text-align: center;
color: #ffffff;
opacity: 0;
font-size: 14px;
line-height: 36px;
cursor: pointer;
animation-name: slideUp;
animation-duration: 0.5s;
animation-delay: 0.3s;
animation-fill-mode: forwards;
}
@keyframes slideUp {
0% {
transform: translateY(60px);
opacity: 0;
}
100% {
transform: translateY(0);
opacity: 1;
}
}
}
}
</style>
+17 -3
View File
@@ -152,7 +152,12 @@
<!-- {{ $t('login:title:system_title_about') }} -->
<svg-icon icon-class="login-logo" style="width: 250px; height: 71px" />
</p>
<p style="margin-bottom: 0px" v-else>{{ $t('login:title:system') }}</p>
<div style="margin-bottom: 0px" v-else>
<!-- {{ $t('login:title:system_title_about') }} -->
<img src="@/assets/system.png" alt=""
:style="{ width: isEN ? '180px' : '200px', height: isEN ? '60px' : '65px' }" />
<p style="margin-bottom: 0px">{{ $t('login:title:system') }}</p>
</div>
<p style="margin-bottom: 20px; margin-top: 0">
V{{ $version.IsEnv_US ? $version.Version_US : $version.Version }}
</p>
@@ -167,12 +172,12 @@
Copyright © {{ new Date().getFullYear() }} Shanghai Extensive Imaging
Inc.
</p>
<div style="margin-bottom: 20px" v-if="NODE_ENV === 'usa'">
<!-- <div style="margin-bottom: 20px" v-if="NODE_ENV === 'usa'">
<img style="width: 180px" src="@/assets/zzlogo-usa.png" alt="" />
</div>
<div style="margin-bottom: 20px" v-else>
<img style="width: 180px" src="@/assets/zzlogo2.png" alt="" />
</div>
</div> -->
</div>
<div slot="footer" class="dialog-footer">
<el-button type="primary" size="mini" @click="aboutVisible = false">
@@ -369,6 +374,15 @@ export default {
})
}, 500)
return
} else if (res.BasicInfo.IsNeedResetPwd) {
// ,
this.$message.success(this.$t('login:message:login5'))
setTimeout(() => {
this.$router.push({
path: `/user-recompose?UserId=${res.BasicInfo.IdentityUserId}&Email=${res.BasicInfo.EMail}&UserName=${res.BasicInfo.UserName}&lang=${this.$i18n.locale}&access_token=${res.JWTStr}&isUpdate=1`,
})
}, 500)
return
} else if (res.BasicInfo.NeedChangePassWord) {
//
this.$alert(
+5 -1
View File
@@ -153,7 +153,8 @@ export default {
isAudit: false,
activeNames: [],
Asc: false
Asc: false,
IsReadingTaskViewInOrder: 2
}
},
async created() {
@@ -165,6 +166,9 @@ export default {
store.dispatch('user/setToken', this.$router.currentRoute.query.TokenKey)
changeURLStatic('TokenKey', '')
}
if (this.$router.currentRoute.query.IsReadingTaskViewInOrder) {
this.IsReadingTaskViewInOrder = Number(this.$router.currentRoute.query.IsReadingTaskViewInOrder)
}
this.subjectVisitId = this.$router.currentRoute.query.subjectVisitId
this.studyId = this.$router.currentRoute.query.studyId
this.getNoneDicomList()
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,516 @@
<template>
<div v-loading="loading" class="none-dicom-study-wrapper">
<div class="study-info">
<div style="cursor: pointer;" @click.stop="sortFile">
<i :class="['el-icon-caret-top', asc === false ? '' : 'icon_check']"
style="display: block;margin-bottom: -5px;"></i>
<i :class="['el-icon-caret-bottom', asc === true ? '' : 'icon_check']"
style="display: block; margin-top: -5px;"></i>
</div>
<div>{{ $t('trials:none-dicom-show:fileList') }} </div>
</div>
<div class="ps" ref="studyBox_ps">
<el-collapse v-model="activeNames">
<el-collapse-item v-for="(study, index) in studyList" :key="`${study.Id}`" :name="`${study.Id}`">
<template slot="title">
<div class="dicom-desc">
<div style="text-overflow: ellipsis;overflow: hidden;">
<span :title="study.CodeView">{{ study.CodeView }}</span>
<span v-if="otherInfo && otherInfo.IsShowStudyName" :title="study.StudyName" style="margin-left: 5px;">
{{ study.StudyName }}
</span>
</div>
<div style="text-overflow: ellipsis;overflow: hidden;">
<span :title="study.BodyPart">{{ getBodyPart(study.BodyPart, study.BodyPartForEditOther) }}</span>
<span style="margin-left: 5px;" :title="study.Modality">{{ study.Modality }}</span>
</div>
</div>
</template>
<div class="file-list-container">
<div v-for="(k, i) in study.NoneDicomStudyFileList" :key="i" style="position:relative;margin-top:1px;"
@click="selectFile(study, index, i, k)" :ref="`noneDicomRef_${index}_${i}`">
<div :class="{ 'file-active': index === activeStudyIndex && i === activeFileIndex }" class="file-wrapper">
<div class="file-main">
<div class="file-image">
<el-image
v-if="k.FileType === 'image/jpeg' || k.FileType === 'image/jpg' || k.FileType === 'image/bmp' || k.FileType === 'image/png'"
style="width: 100%;height: 100%;"
:src="`${OSSclientConfig.basePath}${k.Path}?x-oss-process=image/resize,w_50,h_50/format,png`"
fit="contain" crossorigin="anonymous" />
<el-image v-else-if="k.FileType === 'application/pdf'" style="width: 100%;height: 100%;" :src="pdf"
fit="contain" crossorigin="anonymous" />
<el-image v-else-if="
!!~k.FileType.indexOf('mp4')
" style="width: 100%; height: 100%" :src="mp4" fit="contain" crossorigin="anonymous" />
<el-image v-else-if="
!!~k.FileType.indexOf('zip')
" style="width: 100%; height: 100%" :src="zipImg" fit="contain" crossorigin="anonymous" />
</div>
<div class="file-text" :title="k.FileName">
{{ k.FileName }}
</div>
</div>
<div v-if="isQcCheck" class="switchBox">
<div class="item">
<span>{{ $t('trials:audit:table:isReading') }}</span>
<el-switch v-model="k.IsReading" :disabled="k.IsDeleted || isAudit"
:active-text="$fd('YesOrNo', true)" :inactive-text="$fd('YesOrNo', false)"
@change="changeReadingStatus($event, study, k)" />
</div>
<div class="item">
<span>{{ $t('trials:audit:table:isDelete') }}</span>
<el-switch v-model="k.IsDeleted" :disabled="isAudit" :active-text="$fd('YesOrNo', true)"
:inactive-text="$fd('YesOrNo', false)" @change="changeDeleteStatus($event, study, k)" />
</div>
</div>
</div>
</div>
</div>
</el-collapse-item>
</el-collapse>
</div>
</div>
</template>
<script>
import { getNoneDicomStudyList, setNodicomStudyState } from '@/api/trials'
import FileNameSorter from "@/utils/customSort"
import pdf from '@/assets/pdf.png'
import mp4 from '@/assets/mp4.png'
import zipImg from '@/assets/zip.jpg'
export default {
name: 'StudyList',
props: {
visitTaskInfo: {
type: Object,
default() {
return {}
}
},
isComparison: {
type: Boolean,
default: false
}
},
data() {
return {
loading: false,
activeNames: [],
activeStudyId: '',
activeFileId: '',
activeStudyIndex: -1,
activeFileIndex: -1,
studyList: [],
pdf,
mp4,
zipImg,
BodyPart: {},
subjectVisitId: '',
studyId: '',
otherInfo: null,
criterionType: null,
isQcCheck: false,
isAudit: false,
asc: false
}
},
async mounted() {
this.subjectVisitId = this.$route.query.subjectVisitId
this.studyId = this.$route.query.studyId
this.isQcCheck = !!this.$route.query.isQcCheck
this.getNoneDicoms()
// this.$nextTick(() => {
// this.activeStudy(this.studyList[0].Id)
// })
this.BodyPart.Bodypart = await this.$getBodyPart(this.$route.query.trialId)
},
methods: {
scrollActiveFileIntoView() {
this.$nextTick(() => {
const refKey = `noneDicomRef_${this.activeStudyIndex}_${this.activeFileIndex}`
const targetRef = this.$refs[refKey]
const element = Array.isArray(targetRef) ? targetRef[0] : targetRef
if (!element) return
this.scrollElementToVisible(element)
})
},
scrollElementToVisible(element, options = {}) {
const container = this.$refs['studyBox_ps']
if (!container || !element) return
const elementTop = element.offsetTop
const elementBottom = elementTop + element.offsetHeight
const visibleTop = container.scrollTop
const visibleBottom = visibleTop + container.clientHeight
if (elementTop >= visibleTop && elementBottom <= visibleBottom) return
const offset = options.offset != null
? options.offset
: Math.max((container.clientHeight - element.offsetHeight) / 2, 0)
const targetPosition = Math.max(elementTop - offset, 0)
container.scrollTo({
top: targetPosition,
behavior: options.behavior || 'smooth'
})
},
async getNoneDicoms() {
const loading = this.$loading({ fullscreen: true })
try {
let res = await getNoneDicomStudyList(
this.subjectVisitId,
this.studyId,
false,
this.$route.query.visitTaskId,
!!this.$route.query.isReading,
this.$route.query.isImageSegmentLabel,
)
this.studyList = res.Result
this.studyList.forEach(study => {
if (study.NoneDicomStudyFileList && study.NoneDicomStudyFileList.length > 0) {
study.NoneDicomStudyFileList.forEach(file => {
if (file.Path.includes('.MaskNoneDicom_')) {
file.IsBeMark = true
} else {
file.IsBeMark = false
}
})
}
})
this.otherInfo = res.OtherInfo
this.isAudit = res.OtherInfo.AuditState * 1 > 6
const studyIndex = this.studyList.findIndex((item) => {
return item.NoneDicomStudyFileList.length > 0
})
if (studyIndex > -1) {
this.selectFile(this.studyList[studyIndex], studyIndex, 0)
let studyId = this.studyList[studyIndex].Id
this.activeNames = [studyId]
}
if (this.criterionType === 19 || this.criterionType === 20) {
this.sortFile()
}
loading.close()
} catch (e) {
console.log(e)
loading.close()
}
},
selected() {
},
sortFile() {
this.asc = !this.asc
this.studyList.forEach(study => {
if (study.NoneDicomStudyFileList.length > 0) {
study.NoneDicomStudyFileList = FileNameSorter.sortFileNames(study.NoneDicomStudyFileList, {
direction: this.asc ? 'asc' : 'desc',
key: 'FileName'
})
}
})
this.activeStudyIndex = this.studyList.findIndex(item => item.Id === this.activeStudyId)
if (this.activeStudyIndex === -1) return
this.activeFileIndex = this.studyList[this.activeStudyIndex].NoneDicomStudyFileList.findIndex(item => item.Id === this.activeFileId)
},
changeReadingStatus(callback, row, file) {
let statusStr = ''
if (callback) {
statusStr = this.$t('trials:audit:label:setSeriesReading')
file.IsReading = false
} else {
statusStr = this.$t('trials:audit:label:setSeriesNotReading')
file.IsReading = true
}
let message = this.$t('trials:audit:message:changeSeriesStatus').replace('xxx', statusStr)
message = message.replace('yyy', this.$fd('YesOrNo', !file.IsReading))
this.$confirm(message, {
distinguishCancelAndClose: true,
type: 'warning',
})
.then(() => {
const state = file.IsReading ? 1 : 2
this.loading = true
const params = {
SubjectVisitId: row.SubjectVisitId,
NoneDicomStudyId: row.Id,
State: state,
NoneDicomStudyFileId: file.Id,
}
return setNodicomStudyState(params)
.then((res) => {
this.loading = false
if (res.IsSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
file.IsReading = !file.IsReading
}
})
.catch(() => {
this.loading = false
})
})
.catch(() => { })
},
changeDeleteStatus(callback, row, file) {
let statusStr = ''
if (callback) {
statusStr = this.$t('trials:audit:label:setSeriesDeleted')
file.IsDeleted = false
} else {
statusStr = this.$t('trials:audit:label:setSeriesNotDelete')
file.IsDeleted = true
}
let message = this.$t('trials:audit:message:changeSeriesStatus').replace('xxx', statusStr)
message = message.replace('yyy', this.$fd('YesOrNo', !file.IsDeleted))
this.$confirm(message, {
distinguishCancelAndClose: true,
type: 'warning',
})
.then(() => {
const state = file.IsDeleted ? 5 : 4
this.loading = true
const params = {
SubjectVisitId: row.SubjectVisitId,
NoneDicomStudyId: row.Id,
State: state,
NoneDicomStudyFileId: file.Id,
}
return setNodicomStudyState(params)
.then((res) => {
this.loading = false
if (res.IsSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
file.IsDeleted = !file.IsDeleted
}
})
.catch(() => {
this.loading = false
})
})
.catch(() => { })
},
getBodyPart(bodyPart, other) {
if (!bodyPart && !other) return ''
var separator = ','
if (bodyPart.indexOf('|') > -1) {
separator = '|'
} else if (bodyPart.indexOf(',') > -1) {
separator = ','
} else if (bodyPart.indexOf('') > -1) {
separator = ''
}
var arr = bodyPart.split(separator)
var newArr = arr.map((i) => {
return this.$fd('Bodypart', i.trim(), 'Code', this.BodyPart, 'Name')
})
if (other) {
newArr.push(other)
}
newArr = newArr.filter(Boolean)
return newArr.join(' | ')
},
//
selectFile(study, studyIndex, fileIndex, isReset = false) {
if (this.isComparison) return false
this.activeStudyIndex = studyIndex
this.activeFileIndex = fileIndex
this.activeStudyId = study.Id
const fileList = study.NoneDicomStudyFileList
this.activeFileId = fileList[fileIndex].Id
this.$emit('selectFile', { fileInfo: fileList[fileIndex], fileList, visitTaskInfo: this.visitTaskInfo, studyId: study.Id, isReset })
this.scrollActiveFileIntoView()
},
activeImage(obj) {
if (!obj || this.studyList.length === 0) return
const study = this.studyList.find(i => i.Id === obj.studyId)
const studyIndex = this.studyList.findIndex(i => i.Id === obj.studyId)
if (studyIndex === -1) return
this.activeStudy(`${obj.studyId}`)
this.activeStudyIndex = studyIndex
this.activeStudyId = obj.studyId
const fileList = this.studyList[studyIndex].NoneDicomStudyFileList || []
const fileIndex = fileList.findIndex(f => f.Path === obj.path)
if (fileIndex === -1) return
this.activeFileIndex = fileIndex
this.activeFileId = fileList[fileIndex].Id
this.selectFile(study, studyIndex, fileIndex)
this.scrollActiveFileIntoView()
},
replaceImage(obj) {
if (!obj || this.studyList.length === 0) return
const study = this.studyList.find(i => i.Id === obj.studyId)
const studyIndex = this.studyList.findIndex(i => i.Id === obj.studyId)
if (studyIndex === -1) return
this.activeStudy(`${obj.studyId}`)
this.activeStudyIndex = studyIndex
this.activeStudyId = obj.studyId
const fileList = this.studyList[studyIndex].NoneDicomStudyFileList || []
const fileIndex = fileList.findIndex(f => f.Path === obj.path)
const file = fileList.find(f => f.Path === obj.path)
if (fileIndex === -1) return
this.activeFileIndex = fileIndex
this.activeFileId = fileList[fileIndex].Id
file.Path = obj.file.Path
file.FullFilePath = obj.file.Path
file.IsBeMark = obj.IsBeMark
this.selectFile(study, studyIndex, fileIndex, true)
},
activeStudy(id) {
const studyId = `${id}`
if (this.activeNames.indexOf(studyId) > -1) return
this.activeNames.push(studyId)
}
}
}
</script>
<style lang="scss" scoped>
.none-dicom-study-wrapper {
width: 100%;
height: 100%;
overflow-y: hidden;
overflow-x: hidden;
display: flex;
flex-direction: column;
.study-info {
display: flex;
justify-content: center;
align-items: center;
font-size: 16px;
font-weight: bold;
color: #ddd;
background-color: #4c4c4c;
line-height: 40px;
user-select: none;
}
.icon_check {
color: #409EFF;
}
.dicom-desc {
font-weight: bold;
font-size: 13px;
text-align: left;
color: #d0d0d0;
padding: 2px 8px 2px 2px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.ps {
flex: 1;
overflow-anchor: none;
touch-action: auto;
overflow-y: auto;
}
.file-active {
// background: #16477b;
background-color: #183553 !important;
border: 1px solid #23527b !important;
}
::v-deep .el-progress__text {
color: #ccc;
font-size: 12px;
}
::v-deep .el-switch__label {
color: #ccc;
}
.file-list-container {
width: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
.file-wrapper {
width: 100%;
padding: 5px;
display: flex;
flex-direction: column;
align-items: stretch;
cursor: pointer;
background-color: #3a3a3a;
// color: #ddd;
// white-space: nowrap;
// overflow: hidden;
// text-overflow: ellipsis;
.file-main {
display: flex;
flex-direction: row;
align-items: center;
min-width: 0;
}
.el-progress__text {
display: none;
}
.el-progress-bar {
padding-right: 0px;
}
.file-image {
width: 50px;
height: 50px;
}
.file-text {
flex: 1;
padding-left: 5px;
color: #ddd;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
.switchBox {
width: 100%;
margin-top: 6px;
color: #ddd;
.item {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 5px;
}
.item:last-child {
margin-bottom: 0;
}
}
::v-deep .el-collapse {
border: none;
.el-collapse-item {
background-color: #000 !important;
color: #ddd;
}
.el-collapse-item__content {
padding-bottom: 0px;
background-color: #000 !important;
}
.el-collapse-item__header {
background-color: #353535 !important;
color: #ddd;
border-bottom-color: #5a5a5a;
padding-left: 5px;
height: 60px;
line-height: 20px;
}
}
::v-deep .el-progress-bar__inner {
transition: width 0s ease;
}
}
</style>
+140
View File
@@ -0,0 +1,140 @@
<template>
<div class="read-page-container">
<!-- 检查列表 -->
<div class="left-panel">
<div class="study-info">
<study-list ref="studyList" :isComparison="isComparison" @selectFile="selectFile" />
</div>
</div>
<!-- 图像 -->
<div class="middle-panel">
<file-viewer ref="fileViewer" :isComparison.sync="isComparison" @toggleImage="toggleImage"
@selectFile="selectFile" @replaceImage="replaceImage" />
</div>
</div>
</template>
<script>
import store from '@/store'
import { changeURLStatic } from '@/utils/history.js'
import StudyList from './components/study-list'
import FileViewer from './components/file-viewer'
export default {
name: 'NoneDicoms',
components: {
StudyList,
FileViewer
},
data() {
return {
isComparison: false
}
},
mounted() {
if (this.$route.query.TokenKey) {
store.dispatch('user/setToken', this.$route.query.TokenKey)
changeURLStatic('TokenKey', '')
}
},
methods: {
toggleImage(obj) {
this.$refs['studyList'].activeImage(obj)
},
selectFile(obj) {
if (obj.fileInfo && obj.fileList && obj.studyId) {
this.$refs['fileViewer'].setActiveCanvasImages(obj)
return
}
},
replaceImage(obj) {
this.$refs['studyList'].replaceImage(obj)
}
}
}
</script>
<style lang="scss" scoped>
.read-page-container {
box-sizing: border-box;
height: 100%;
display: flex;
background-color: #444;
padding: 5px;
::-webkit-scrollbar {
width: 5px;
height: 5px;
}
::-webkit-scrollbar-thumb {
border-radius: 10px;
background: #d0d0d0;
}
.left-panel {
display: flex;
width: 230px;
border: 1px solid #727272;
color: #fff;
::-webkit-scrollbar {
width: 3px;
height: 3px;
}
::-webkit-scrollbar-thumb {
border-radius: 10px;
background: #d0d0d0;
}
.study-info {
width: 100%;
border-left: 1px solid #727272;
}
}
.middle-panel {
flex: 1;
border: 1px solid #727272;
margin: 0 5px;
}
// .right-panel {
// width: 400px;
// border: 1px solid #727272;
// padding: 0 10px;
// overflow-y: auto;
// .text-info {
// display: flex;
// align-items: center;
// justify-content: space-between;
// color: #ddd;
// }
// }
::v-deep .dialog-container {
margin-top: 50px !important;
width: 75%;
height: 80%;
}
::v-deep .el-dialog__body {
padding: 20px 20px 0 20px;
height: calc(100% - 70px);
}
.el-dialog__header {
position: relative;
}
.full-dialog-container {
::v-deep .is-fullscreen .el-dialog__body {
height: calc(100% - 70px);
}
}
}
</style>
@@ -25,19 +25,10 @@
<div class="login_content">
<div class="form-label-width">
<el-form-item :label="$t('trials:researchForm:form:siteName')" prop="TrialSiteId">
<el-select
v-model="form.TrialSiteId"
filterable
style="width:100%;"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory"
@change="handleSiteChange"
>
<el-option
v-for="(item,index) of siteOptions"
:key="index"
:label="item.TrialSiteAliasName"
:value="item.TrialSiteId"
/>
<el-select v-model="form.TrialSiteId" filterable style="width:100%;"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" @change="handleSiteChange">
<el-option v-for="(item, index) of siteOptions" :key="index" :label="item.TrialSiteAliasName"
:value="item.TrialSiteId" />
</el-select>
</el-form-item>
<!-- 中心编号 -->
@@ -49,74 +40,149 @@
<el-input v-model="form.UserName" :disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" />
</el-form-item>
<!-- 联系电话 -->
<el-form-item
:label="$t('trials:researchForm:form:contactorPhone')"
prop="Phone"
>
<el-form-item :label="$t('trials:researchForm:form:contactorPhone')" prop="Phone">
<el-input v-model="form.Phone" :disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" />
</el-form-item>
<!-- 联系邮箱 -->
<el-form-item :label="$t('trials:researchForm:form:contactorEmail')">
<el-form-item :label="$t('trials:researchForm:form:contactorEmail')" prop="Email">
<el-input v-model="form.Email" disabled />
</el-form-item>
<el-form-item :label="$t('trials:researchForm:form:CommonUploadRecordId')
" v-if="IsSupportUploadFile" class="file">
<el-input v-model="form.SiteSurveyFile.FileName" type="textarea" :autosize="{ minRows: 1, maxRows: 3 }"
style="margin-right: 5px;" disabled />
<div class="upload" v-if="!(!(state === 0 && userTypeEnumInt === 0) || isHistory)">
<input accept=".pdf,.docx,.doc" type="file" name="uploadFolder" class="select-file" title=""
@change="beginScanFiles($event)" />
<div class="btn-select">
{{ $t('dictionary:template:basicData:button:selectFile') }}
</div>
</div>
<!-- <el-button type="primary" @click="viewManualFile"
:disabled="!this.form.SiteSurveyFile || !this.form.SiteSurveyFile.Path">{{
$t('trials:researchForm:form:preview') }}</el-button> -->
<el-button type="primary" @click="downLoad"
:disabled="!this.form.SiteSurveyFile || !this.form.SiteSurveyFile.Path">{{
$t('trials:researchForm:form:download') }}</el-button>
</el-form-item>
<!-- 平均刻盘周期 -->
<el-form-item v-if="!notShowFieldList.includes('AverageEngravingCycle')" :label="$t('trials:researchForm:form:engravingCycle')">
<el-input-number v-model="form.AverageEngravingCycle" :disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" controls-position="right" :min="0" style="width:100%;" />
<el-form-item v-if="!notShowFieldList.includes('AverageEngravingCycle')"
:label="$t('trials:researchForm:form:engravingCycle')">
<el-input-number v-model="form.AverageEngravingCycle"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" controls-position="right" :min="0"
style="width:100%;" />
</el-form-item>
</div>
<!-- MRI-PDFF 是否为本中心该适应症的常规诊疗检查项目 -->
<el-form-item v-if="!notShowFieldList.includes('IsRoutineMRIPDEE')"
:label="$t('trials:researchForm:form:IsRoutineMRIPDEE')" prop="IsRoutineMRIPDEE">
<el-radio-group v-model="form.IsRoutineMRIPDEE"
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory)">
<el-radio v-for="item of $d.YesOrNo" :key="`IsRoutineMRIPDEE${item.value}`" :label="item.value">{{
item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<!-- MRI-PDFF 检查的检测周期含单次检查时长预约等待时长等 -->
<el-form-item
v-if="!notShowFieldList.includes('MRIPDFFScanTime') || !notShowFieldList.includes('MRIPDFFLeadTime') || !notShowFieldList.includes('MRIPDFFOther')"
:label="$t('trials:researchForm:form:IsMRIPDFF')">
</el-form-item>
<!-- 单次检查时长分钟 -->
<el-form-item v-if="!notShowFieldList.includes('MRIPDFFScanTime')"
:label="$t('trials:researchForm:form:MRIPDFFScanTime')">
<el-input-number v-model="form.MRIPDFFScanTime"
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory)" controls-position="right" :min="0" />
</el-form-item>
<!-- 平均预约等待时长 -->
<el-form-item v-if="!notShowFieldList.includes('MRIPDFFLeadTime')"
:label="$t('trials:researchForm:form:MRIPDFFLeadTime')">
<el-input-number v-model="form.MRIPDFFLeadTime"
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory)" controls-position="right" :min="0" />
</el-form-item>
<!-- 特殊情况备注-->
<el-form-item v-if="!notShowFieldList.includes('MRIPDFFOther')"
:label="$t('trials:researchForm:form:MRIPDFFOther')">
<el-input v-model="form.MRIPDFFOther" type="textarea" :autosize="{ minRows: 2, maxRows: 4 }"
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory)" />
</el-form-item>
<!-- 如已选择研究者评估项目是否会授权影像科老师参与本试验如不单独授权是否可在试验中保持 1-2 名固定技师操作-->
<el-form-item
v-if="!notShowFieldList.includes('IsAuthorizeRadiologistsParticipate') || !notShowFieldList.includes('AssignFixedTechnologists')"
:label="$t('trials:researchForm:form:IsAuthorize')" prop="IsAuthorize">
<el-radio-group v-model="form.IsAuthorize" :disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory)"
@input="handleIsAuthorizeInput">
<el-radio label="IsAuthorizeRadiologistsParticipate">{{
$t('trials:researchForm:form:IsAuthorizeRadiologistsParticipate') }}</el-radio>
<el-radio label="AssignFixedTechnologists">{{
$t('trials:researchForm:form:AssignFixedTechnologists') }}</el-radio>
</el-radio-group>
</el-form-item>
<!-- 请确认参与本项目影像采集的影像技师具备对应的资质技师证对应设备的大型设备上岗证 -->
<el-form-item v-if="!notShowFieldList.includes('IsConfirmImagingTechnologist')" :label="$t('trials:researchForm:form:isQualified')">
<el-radio-group v-model="form.IsConfirmImagingTechnologist" :disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory">
<el-radio
v-for="item of $d.YesOrNo"
:key="`IsConfirmImagingTechnologist${item.value}`"
:label="item.value"
>{{ item.label }}</el-radio>
<el-form-item v-if="!notShowFieldList.includes('IsConfirmImagingTechnologist')"
:label="$t('trials:researchForm:form:isQualified')" prop="IsConfirmImagingTechnologist">
<el-radio-group v-model="form.IsConfirmImagingTechnologist"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory">
<el-radio v-for="item of $d.YesOrNo" :key="`IsConfirmImagingTechnologist${item.value}`"
:label="item.value">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<!-- 原因 -->
<el-form-item
v-if="!notShowFieldList.includes('NotConfirmReson') && form.IsConfirmImagingTechnologist === false"
:label="$t('trials:researchForm:form:notQualifiedReason')"
>
<el-input
v-model="form.NotConfirmReson"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory"
/>
:label="$t('trials:researchForm:form:notQualifiedReason')" prop="NotConfirmReson">
<el-input v-model="form.NotConfirmReson" type="textarea" :autosize="{ minRows: 2, maxRows: 4 }"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" />
</el-form-item>
<!-- 研究单位疗效评估人员类型 -->
<el-form-item v-if="!notShowFieldList.includes('EfficacyEvaluatorType')" :label="$t('trials:researchForm:form:staffType')">
<el-radio-group v-model="form.EfficacyEvaluatorType" :disabled="!(state === 0 && userTypeEnumInt === 0)|| isHistory">
<el-radio v-for="item of $d.EfficacyEvaluatorType" :key="`EfficacyEvaluatorType${item.value}`" :label="item.value">{{ item.label }}</el-radio>
<el-form-item v-if="!notShowFieldList.includes('EfficacyEvaluatorType')"
:label="$t('trials:researchForm:form:staffType')">
<el-radio-group v-model="form.EfficacyEvaluatorType"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory">
<el-radio v-for="item of $d.EfficacyEvaluatorType" :key="`EfficacyEvaluatorType${item.value}`"
:label="item.value">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<!-- 是否严格按照研究单位影像手册参数完成图像采集 -->
<el-form-item v-if="!notShowFieldList.includes('IsFollowStudyParameters')">
<el-form-item v-if="!notShowFieldList.includes('IsFollowStudyParameters')" prop="IsFollowStudyParameters">
<span slot="label" v-html="$t('trials:researchForm:form:isFollowStudyParam')" />
<el-radio-group v-model="form.IsFollowStudyParameters" :disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory">
<el-radio v-for="item of $d.YesOrNo" :key="`IsFollowStudyParameters${item.value}`" :label="item.value">{{ item.label }}</el-radio>
<el-radio-group v-model="form.IsFollowStudyParameters"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory">
<el-radio v-for="item of $d.YesOrNo" :key="`IsFollowStudyParameters${item.value}`" :label="item.value">{{
item.label }}</el-radio>
</el-radio-group>
<el-button type="primary" size="small" style="margin-left: 5px;" @click="viewManual">
{{ $t('trials:researchForm:button:viewManual') }}
</el-button>
</el-form-item>
<!-- 不能严格按照研究单位影像手册参数采集图像原因 -->
<el-form-item
v-if="!notShowFieldList.includes('NotFollowReson') && !form.IsFollowStudyParameters"
>
<el-form-item v-if="!notShowFieldList.includes('NotFollowReson') && !form.IsFollowStudyParameters"
prop="NotFollowReson">
<span slot="label" v-html="$t('trials:researchForm:form:notFollowStudyParam')" />
<el-input
v-model="form.NotFollowReson"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory"
/>
<el-input v-model="form.NotFollowReson" type="textarea" :autosize="{ minRows: 2, maxRows: 4 }"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" />
</el-form-item>
<!-- 是否严格按照影像手册参数完成刻盘 -->
<el-form-item v-if="!notShowFieldList.includes('ISStrictManualBurnFlag')" prop="ISStrictManualBurnFlag">
<span slot="label" v-html="$t('trials:researchForm:form:ISStrictManualBurnFlag')" />
<el-radio-group v-model="form.ISStrictManualBurnFlag"
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory)" style="margin-right: 10px;">
<el-radio v-for="item of $d.YesOrNo" :key="`ISStrictManualBurnFlag${item.value}`" :label="item.value">{{
item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<!-- 不能严格按照影像手册参数完成刻盘原因 -->
<el-form-item v-if="!notShowFieldList.includes('NotStrictManualBurnFlagReason') && !form.ISStrictManualBurnFlag"
prop="NotStrictManualBurnFlagReason">
<span slot="label" v-html="$t('trials:researchForm:form:NotStrictManualBurnFlagReason')" />
<el-input v-model="form.NotStrictManualBurnFlagReason" type="textarea" :autosize="{ minRows: 2, maxRows: 4 }"
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory)" />
</el-form-item>
</div>
</el-form>
</div>
</template>
<script>
import { getTrialSiteSelect } from '@/api/trials'
import { getTrialSiteSelect, getTrialDocumentList, addOrUpdateCommonUploadRecord } from '@/api/trials'
import { addOrUpdateTrialSiteSurvey } from '@/api/research'
export default {
name: 'ResearchBasicInfo',
@@ -124,7 +190,15 @@ export default {
isHistory: {
type: Boolean,
default: false
}
},
IsOnlyUploadFile: {
type: Boolean,
default: false
},
IsSupportUploadFile: {
type: Boolean,
default: false
},
},
data() {
var checkPhone = (rule, value, callback) => {
@@ -156,16 +230,51 @@ export default {
Phone: '', //
Email: '', //
AverageEngravingCycle: '',
IsRoutineMRIPDEE: '',
MRIPDFFScanTime: '',
MRIPDFFLeadTime: '',
MRIPDFFOther: '',
IsAuthorize: '',
IsAuthorizeRadiologistsParticipate: '',
AssignFixedTechnologists: '',
ISStrictManualBurnFlag: '',
NotStrictManualBurnFlagReason: '',
IsConfirmImagingTechnologist: '',
NotConfirmReson: '',
EfficacyEvaluatorType: '',
IsFollowStudyParameters: '',
NotFollowReson: ''
NotFollowReson: '',
CommonUploadRecordId: null,
SiteSurveyFile: []
},
rules: {
TrialSiteId: [
{ required: true, message: this.$t('trials:researchForm:formRule:specify'), trigger: 'blur' }
],
IsRoutineMRIPDEE: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: 'blur' }
],
IsAuthorize: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: 'blur' }
],
ISStrictManualBurnFlag: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: 'blur' }
],
IsFollowStudyParameters: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: 'blur' }
],
IsConfirmImagingTechnologist: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: 'blur' }
],
NotConfirmReson: [
{ required: true, message: this.$t('trials:researchForm:formRule:specify'), trigger: 'blur' }
],
NotFollowReson: [
{ required: true, message: this.$t('trials:researchForm:formRule:specify'), trigger: 'blur' }
],
NotStrictManualBurnFlagReason: [
{ required: true, message: this.$t('trials:researchForm:formRule:specify'), trigger: 'blur' }
],
UserName: [
{ required: true, validator: (rule, value, callback) => { !value ? callback(new Error(this.$t('trials:researchForm:formRule:specify'))) : callback() }, trigger: 'blur' },
{ min: 0, max: 50, message: this.$t('trials:researchForm:formRule:maxLength'), trigger: ['blur', 'change'] }
@@ -186,14 +295,173 @@ export default {
state: null,
userTypeEnumInt: zzSessionStorage.getItem('userTypeEnumInt') * 1,
isShow: false,
notShowFieldList: []
notShowFieldList: [],
perview_visible: false,
ExternalList: []
}
},
methods: {
async beginScanFiles(e) {
try {
let file = e.target.files[0]
let name = file.name
if (!this.checkFileSuffix(name)) return this.$message.warning(this.$t("trials:researchRecord:ImageManual:message:checkFileSuffix"))
this.loading = true
file = await this.fileToBlob(file)
let scope = this
var index = name.lastIndexOf('.')
var type = name.substring(index + 1, name.length)
let res = await this.OSSclient.put(
`/${this.$route.query.trialId}/InspectionUpload/SiteSurvey/${this.form.TrialSiteId}/${scope.getGuid(Date.now() + '_' + name.split('.')[0])}.${type}`,
file
)
let data = {
Path: this.$getObjectName(res.url),
FileName: name,
FileSize: file.size,
FileType: type
}
res = await addOrUpdateCommonUploadRecord(data)
this.loading = false
if (res.IsSuccess) {
this.form.CommonUploadRecordId = res.Result
data.Id = res.Result
this.form.SiteSurveyFile = data
}
} catch (err) {
console.log(err)
this.loading = false;
}
},
checkFileSuffix(fileName) {
var index = fileName.lastIndexOf('.')
var suffix = fileName.substring(index + 1, fileName.length)
if ('.pdf'.toLocaleLowerCase().search(suffix.toLocaleLowerCase()) === -1 && '.docx'.toLocaleLowerCase().search(suffix.toLocaleLowerCase()) === -1 && '.doc'.toLocaleLowerCase().search(suffix.toLocaleLowerCase()) === -1) {
return false
} else {
return true
}
},
handleIsAuthorizeInput(label) {
this.form.IsAuthorizeRadiologistsParticipate = false
this.form.AssignFixedTechnologists = false
this.form[label] = true
},
async viewManualFile() {
if (!this.form.SiteSurveyFile || !this.form.SiteSurveyFile.Path) return this.$message.warning(this.$t("trials:researchForm:message:notFile"))
this.$preview({
path: this.form.SiteSurveyFile.Path,
type: this.form.SiteSurveyFile.FileType,
title: this.form.SiteSurveyFile.Path.FileName,
})
},
async downLoad() {
if (!this.form.SiteSurveyFile || !this.form.SiteSurveyFile.Path) return this.$message.warning(this.$t("trials:researchForm:message:notFile"))
let link = document.createElement('a')
link.href = this.OSSclientConfig.basePath + this.form.SiteSurveyFile.Path;
link.target = '_blank'
link.download = this.form.SiteSurveyFile.FileName
link.style.display = 'none'
document.body.append(link)
link.click()
document.body.removeChild(link)
link = null
},
async viewManual() {
try {
let data = {
PageIndex: 1,
PageSize: 20,
TrialId: this.$route.query.trialId,
IsPublish: true,
FileTypeCode: 4,
IsDeleted: false
}
let res = await getTrialDocumentList(data)
if (res.IsSuccess) {
const { CurrentPageData } = res.Result
if (CurrentPageData.length <= 0) return this.$message.warning(this.$t("trials:researchRecord:ImageManual:message:noImageManual"))
this.ExternalList = []
CurrentPageData.forEach(item => {
let obj = {
FilePath: item.Path,
FileFormat: 'pdf',
Name: item.Name
}
this.ExternalList.push(obj)
});
// this.perview_visible = true
this.ExternalList.forEach(item => {
let link = document.createElement('a')
link.href = this.OSSclientConfig.basePath + item.FilePath;
link.target = '_blank'
link.download = item.Name
link.style.display = 'none'
document.body.append(link)
link.click()
document.body.removeChild(link)
link = null
})
}
} catch (err) {
console.log(err)
}
},
//
handleSave(isAutoCommit) {
return new Promise(async(resolve) => {
handleSave(isAutoCommit, isCheck = false) {
return new Promise(async (resolve) => {
isCheck = false
try {
if (this.IsOnlyUploadFile && !isCheck) {
if (!this.form.UserName) {
this.$message.warning(this.$t("trials:researchForm:message:notUserName"))
return resolve(false)
}
if (!this.form.Phone) {
this.$message.warning(this.$t("trials:researchForm:message:notPhone"))
return resolve(false)
}
if (!this.form.CommonUploadRecordId) {
this.$message.warning(this.$t("trials:researchForm:message:notCommonUploadRecordId"))
return resolve(false)
}
this.btnLoading = true
const param = {
id: this.form.Id,
trialId: this.$route.query.trialId,
trialSiteId: this.form.TrialSiteId,
userName: this.form.UserName,
phone: this.form.Phone,
email: this.form.Email,
averageEngravingCycle: this.form.AverageEngravingCycle,
IsRoutineMRIPDEE: this.form.IsRoutineMRIPDEE,
MRIPDFFScanTime: this.form.MRIPDFFScanTime,
MRIPDFFLeadTime: this.form.MRIPDFFLeadTime,
MRIPDFFOther: this.form.MRIPDFFOther,
IsAuthorizeRadiologistsParticipate: this.form.IsAuthorizeRadiologistsParticipate,
AssignFixedTechnologists: this.form.AssignFixedTechnologists,
ISStrictManualBurnFlag: this.form.ISStrictManualBurnFlag,
NotStrictManualBurnFlagReason: this.form.NotStrictManualBurnFlagReason,
isConfirmImagingTechnologist: this.form.IsConfirmImagingTechnologist,
notConfirmReson: this.form.NotConfirmReson,
efficacyEvaluatorType: this.form.EfficacyEvaluatorType,
isFollowStudyParameters: this.form.IsFollowStudyParameters,
notFollowReson: this.form.NotFollowReson,
CommonUploadRecordId: this.form.CommonUploadRecordId
}
addOrUpdateTrialSiteSurvey(param).then(res => {
this.btnLoading = false
if (res.IsSuccess && !isAutoCommit) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
resolve(true)
}).catch(() => {
this.btnLoading = false
reject(false)
})
return resolve(true)
}
const valid = await this.$refs['researchBSForm'].validate()
if (valid) {
this.loading = true
@@ -205,11 +473,20 @@ export default {
phone: this.form.Phone,
email: this.form.Email,
averageEngravingCycle: this.form.AverageEngravingCycle,
IsRoutineMRIPDEE: this.form.IsRoutineMRIPDEE,
MRIPDFFScanTime: this.form.MRIPDFFScanTime,
MRIPDFFLeadTime: this.form.MRIPDFFLeadTime,
MRIPDFFOther: this.form.MRIPDFFOther,
IsAuthorizeRadiologistsParticipate: this.form.IsAuthorizeRadiologistsParticipate,
AssignFixedTechnologists: this.form.AssignFixedTechnologists,
ISStrictManualBurnFlag: this.form.ISStrictManualBurnFlag,
NotStrictManualBurnFlagReason: this.form.NotStrictManualBurnFlagReason,
isConfirmImagingTechnologist: this.form.IsConfirmImagingTechnologist,
notConfirmReson: this.form.NotConfirmReson,
efficacyEvaluatorType: this.form.EfficacyEvaluatorType,
isFollowStudyParameters: this.form.IsFollowStudyParameters,
notFollowReson: this.form.NotFollowReson
notFollowReson: this.form.NotFollowReson,
CommonUploadRecordId: this.form.CommonUploadRecordId
}
const res = await addOrUpdateTrialSiteSurvey(param)
this.loading = false
@@ -232,7 +509,7 @@ export default {
//
async initForm(trialInfo, trialSiteSurvey, notShowFieldList) {
// site
const { Result } = await getTrialSiteSelect(this.$route.query.trialId)
const { Result } = await getTrialSiteSelect(this.$route.query.trialId, { IgnoreDisable: true })
this.siteOptions = Result
this.form.Id = trialSiteSurvey.Id
this.form.Sponsor = trialInfo.Sponsor //
@@ -246,7 +523,20 @@ export default {
this.form.UserName = trialSiteSurvey.UserName //
this.form.Phone = trialSiteSurvey.Phone //
this.form.Email = trialSiteSurvey.Email //
this.form.CommonUploadRecordId = trialSiteSurvey.CommonUploadRecordId
this.form.SiteSurveyFile = trialSiteSurvey.SiteSurveyFile || {}
this.form.fileStr = this.form.SiteSurveyFile.FileName
this.form.AverageEngravingCycle = trialSiteSurvey.AverageEngravingCycle
this.form.IsRoutineMRIPDEE = trialSiteSurvey.IsRoutineMRIPDEE
this.form.MRIPDFFScanTime = trialSiteSurvey.MRIPDFFScanTime
this.form.MRIPDFFLeadTime = trialSiteSurvey.MRIPDFFLeadTime
this.form.MRIPDFFOther = trialSiteSurvey.MRIPDFFOther
this.form.IsAuthorizeRadiologistsParticipate = trialSiteSurvey.IsAuthorizeRadiologistsParticipate
this.form.AssignFixedTechnologists = trialSiteSurvey.AssignFixedTechnologists
if (this.form.IsAuthorizeRadiologistsParticipate) this.form.IsAuthorize = 'IsAuthorizeRadiologistsParticipate'
if (this.form.AssignFixedTechnologists) this.form.IsAuthorize = 'AssignFixedTechnologists'
this.form.ISStrictManualBurnFlag = trialSiteSurvey.ISStrictManualBurnFlag
this.form.NotStrictManualBurnFlagReason = trialSiteSurvey.NotStrictManualBurnFlagReason
this.form.IsConfirmImagingTechnologist = trialSiteSurvey.IsConfirmImagingTechnologist
this.form.NotConfirmReson = trialSiteSurvey.NotConfirmReson
this.form.EfficacyEvaluatorType = trialSiteSurvey.EfficacyEvaluatorType
@@ -267,39 +557,46 @@ export default {
}
</script>
<style lang="scss" scoped>
.research_bs_content{
.research_bs_content {
.basic_content{
.basic_content {
padding: 0 20px;
background: #fff;
}
.login_content{
.login_content {
padding: 5px 20px;
margin-top: 5px;
background: #fff;
::v-deep .el-form-item {
padding-bottom: 20px;
}
}
.code_content{
display:flex;
.code_content {
display: flex;
flex-direction: row;
justify-content: flex-start;
.el-input{
.el-input {
margin-right: 10px;
}
}
.form-label-width{
::v-deep .el-form-item__label{
.form-label-width {
::v-deep .el-form-item__label {
width: 140px;
}
::v-deep .el-form-item__content{
::v-deep .el-form-item__content {
margin-left: 140px;
}
}
.submit_content{
.submit_content {
margin-top: 20px;
text-align: center;
}
@@ -308,10 +605,61 @@ export default {
margin-bottom: 0px;
padding-top: 5px;
border-bottom: 1px solid #f5f7fa;
.el-form-item__content{
.el-form-item__content {
color: #82848a;
}
}
}
.upload {
display: inline-block;
height: 36px;
width: 90px;
padding: 0 10px;
line-height: 23px;
position: relative;
text-decoration: none;
border-radius: 3px;
overflow: hidden;
text-align: center;
background: #428bca;
border-color: #428bca;
color: #fff;
margin-right: 5px;
.select-file {
height: 36px;
width: 90px;
position: absolute;
overflow: hidden;
left: 0;
top: 0;
opacity: 0;
font-size: 0;
}
.btn-select {
//
// width: 70px;
height: 36px;
line-height: 36px;
text-align: center;
cursor: pointer;
border-radius: 24px;
overflow: hidden;
position: absolute;
top: 0;
left: 10px;
pointer-events: none; //pointer-events:none穿
}
}
.file {
::v-deep .el-form-item__content {
display: flex;
align-items: center;
}
}
</style>
@@ -1,59 +1,107 @@
<template>
<div
v-loading="loading"
class="equipment_form_content"
>
<el-form
ref="equipmentForm"
:model="form"
:rules="rules"
label-position="left"
>
<div v-loading="loading" class="equipment_form_content">
<el-form ref="equipmentForm" :model="form" :rules="rules" label-position="left">
<!-- 扫描设备 -->
<el-form-item :label="$t('trials:equiptResearch:form:equipment')" prop="EquipmentTypeId">
<el-select
v-model="form.EquipmentTypeId"
style="width:100%"
>
<el-option
v-for="item of $d.SiteSurvey_ScanEquipmentType"
:key="item.id"
:label="item.label"
:value="item.id"
/>
</el-select>
<el-form-item :label="$t('trials:equiptResearch:form:equipment')" prop="EquipmentTypeEnum"
v-if="EquipmentControlFieldList.includes('EquipmentTypeEnum')">
<div style="display: flex;align-items: center;">
<el-select v-model="form.EquipmentTypeEnum" style="width:100%" @change="form.OtherEquipmentType = null">
<el-option v-for="item of $d.SiteSurvey_ScanEquipmentType" :key="item.id" :label="item.label"
:value="item.value" />
</el-select>
<el-input placeholder="" v-model="form.OtherEquipmentType" style="margin-left: 10px;"
v-if="form.EquipmentTypeEnum == '-1'" clearable>
</el-input>
</div>
</el-form-item>
<!-- 扫描参数 -->
<el-form-item v-if="isShowParameters" :label="$t('trials:equiptResearch:form:param')">
<el-form-item :label="$t('trials:equiptResearch:form:param')"
v-if="EquipmentControlFieldList.includes('Parameters')" prop="Parameters">
<el-input v-model="form.Parameters" />
</el-form-item>
<!-- 扫描仪器制造商名称 -->
<el-form-item :label="$t('trials:equiptResearch:form:manufacturer')">
<el-input v-model="form.ManufacturerName" />
<el-form-item :label="$t('trials:equiptResearch:form:manufacturer')"
v-if="EquipmentControlFieldList.includes('ManufacturerType')" prop="ManufacturerType">
<div style="display: flex;align-items: center;">
<el-select v-model="form.ManufacturerType" style="width:100%" @change="form.ManufacturerName = null">
<el-option v-for="item of $d.ManufacturerType" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
<el-input placeholder="" v-model="form.ManufacturerName" style="margin-left: 10px;"
v-if="form.ManufacturerType == '-1'" clearable>
</el-input>
</div>
</el-form-item>
<!-- 扫描仪型号 -->
<el-form-item :label="$t('trials:equiptResearch:form:model')">
<el-form-item :label="$t('trials:equiptResearch:form:model')"
v-if="EquipmentControlFieldList.includes('ScannerType')" prop="ScannerType">
<el-input v-model="form.ScannerType" />
</el-form-item>
<!-- 磁场强度 -->
<el-form-item :label="$t('trials:equiptResearch:form:MagneticFieldStrengthType')"
v-if="EquipmentControlFieldList.includes('MagneticFieldStrengthType')" prop="MagneticFieldStrengthType">
<el-select v-model="form.MagneticFieldStrengthType" style="width:100%">
<el-option v-for="item of $d.MagneticFieldStrengthType" :key="item.id" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<!-- 体部线圈通道数 -->
<el-form-item :label="$t('trials:equiptResearch:form:BodyCoilChannelCount')"
v-if="EquipmentControlFieldList.includes('BodyCoilChannelCount')" prop="BodyCoilChannelCount">
<el-select v-model="form.BodyCoilChannelCount" style="width:100%">
<el-option v-for="item of $d.BodyCoilChannelCount" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- 是否具备专用的PDFF脂肪定量序列CSE-MRI序列 -->
<el-form-item :label="$t('trials:equiptResearch:form:HasDedicatedPdfFatQuantificationSequence')"
v-if="EquipmentControlFieldList.includes('HasDedicatedPdfFatQuantificationSequence')"
prop="HasDedicatedPdfFatQuantificationSequence">
<el-select v-model="form.HasDedicatedPdfFatQuantificationSequence" style="width:100%"
@change="form.PdfFatQuantificationSequenceType = null, form.OtherSequenceSpecification = null">
<el-option v-for="item of $d.YesOrNo" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- PDFF脂肪定量序列 -->
<el-form-item :label="$t('trials:equiptResearch:form:PdfFatQuantificationSequenceType')"
prop="PdfFatQuantificationSequenceType"
v-if="form.HasDedicatedPdfFatQuantificationSequence && EquipmentControlFieldList.includes('PdfFatQuantificationSequenceType')">
<div style="display: flex;align-items: center;">
<el-select v-model="form.PdfFatQuantificationSequenceType" style="width:100%"
@change="form.OtherSequenceSpecification = null">
<el-option v-for="item of $d.PdfFatQuantificationSequenceType" :key="item.id" :label="item.label"
:value="item.value" />
</el-select>
<el-input placeholder="" v-model="form.OtherSequenceSpecification" style="margin-left: 10px;"
v-if="form.PdfFatQuantificationSequenceType == '-1'" clearable>
</el-input>
</div>
</el-form-item>
<!-- 是否包含 T2/R2 校正用于铁沉积校正 -->
<el-form-item :label="$t('trials:equiptResearch:form:HasT2R2Correction')" prop="HasT2R2Correction"
v-if="EquipmentControlFieldList.includes('HasT2R2Correction')">
<el-select v-model="form.HasT2R2Correction" style="width:100%">
<el-option v-for="item of $d.YesOrNo" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- 是否可完整导出 PDFF 参数图及全部原始 DICOM 数据 -->
<el-form-item :label="$t('trials:equiptResearch:form:CanFullyExportPdfParameterMapsAndRawDicom')"
prop="CanFullyExportPdfParameterMapsAndRawDicom"
v-if="EquipmentControlFieldList.includes('CanFullyExportPdfParameterMapsAndRawDicom')">
<el-select v-model="form.CanFullyExportPdfParameterMapsAndRawDicom" style="width:100%">
<el-option v-for="item of $d.YesOrNo" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- 备注 -->
<el-form-item :label="$t('trials:equiptResearch:form:remark')">
<el-form-item :label="$t('trials:equiptResearch:form:remark')" prop="Note"
v-if="EquipmentControlFieldList.includes('Note')">
<el-input v-model="form.Note" />
</el-form-item>
<div style="text-align: center;padding:20px 0px;">
<!-- 取消 -->
<el-button
size="large"
type="primary"
@click="handleCancel"
>
<el-button size="large" type="primary" @click="handleCancel">
{{ $t("common:button:cancel") }}
</el-button>
<!-- 保存 -->
<el-button
size="large"
type="primary"
@click="handleSave"
>
<el-button size="large" type="primary" @click="handleSave">
{{ $t("common:button:save") }}
</el-button>
</div>
@@ -76,29 +124,96 @@ export default {
type: String,
default: ''
},
isShowParameters: {
type: Boolean,
default: false
EquipmentControlFieldList: {
type: Array,
default: () => {
return []
}
}
},
data() {
return {
form: {
Id: '',
EquipmentTypeId: '',
Parameters: '',
ManufacturerName: '',
ScannerType: '',
Note: '',
EquipmentTypeEnum: null,
OtherEquipmentType: null,
Parameters: null,
Note: null,
ManufacturerType: null,
ManufacturerName: null,
ScannerType: null,
MagneticFieldStrengthType: null,
BodyCoilChannelCount: null,
HasDedicatedPdfFatQuantificationSequence: null,
PdfFatQuantificationSequenceType: null,
OtherSequenceSpecification: null,
HasT2R2Correction: null,
CanFullyExportPdfParameterMapsAndRawDicom: null,
TrialSiteSurveyId: ''
},
rules: {
EquipmentTypeId: [
EquipmentTypeEnum: [
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] },
{
validator: (rule, value, callback) => {
if (this.form.EquipmentTypeEnum === -1 && !this.form.OtherEquipmentType) {
callback(this.$t('common:ruleMessage:specify'));
} else {
callback()
}
}, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change']
},
],
Parameters: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change'] }
],
// Note: [
// { required: true, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change'] }
// ],
ManufacturerType: [
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] },
{
validator: (rule, value, callback) => {
if (this.form.ManufacturerType === -1 && !this.form.ManufacturerName) {
callback(this.$t('common:ruleMessage:specify'));
} else {
callback()
}
}, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change']
},
],
ScannerType: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change'] }
],
MagneticFieldStrengthType: [
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] }
]
],
BodyCoilChannelCount: [
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] }
],
HasDedicatedPdfFatQuantificationSequence: [
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] }
],
PdfFatQuantificationSequenceType: [
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] },
{
validator: (rule, value, callback) => {
if (this.form.PdfFatQuantificationSequenceType === -1 && !this.form.OtherSequenceSpecification) {
callback(this.$t('common:ruleMessage:specify'));
} else {
callback()
}
}, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change']
},
],
HasT2R2Correction: [
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] }
],
CanFullyExportPdfParameterMapsAndRawDicom: [
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] }
],
},
loading: false,
dictionaryList: {}
}
},
mounted() {
@@ -154,13 +269,15 @@ export default {
}
</script>
<style lang="scss" scoped>
.equipment_form_content{
.equipment_form_content {
padding: 0 10px;
::v-deep .el-form-item {
margin-bottom: 0px;
padding: 5px 0 20px 0;
border-bottom: 1px solid #f5f7fa;
.el-form-item__content{
.el-form-item__content {
color: #82848a;
}
}
@@ -13,18 +13,22 @@
<i class="el-icon-receiving" />
</div>
<div class="p_info">
<div
class="p_info_basic"
:style="{maxWidth:w+'px','white-space': 'nowrap',
overflow: 'hidden',
'text-overflow': 'ellipsis'}"
>
<div class="p_text">{{ item.EquipmentType }}</div>
<div v-if="isShowParameters" class="p_text" style="margin-left:10px">{{ item.Parameters }}</div>
<div class="p_text" style="margin-left:10px">{{ item.ManufacturerName }}</div>
<div class="p_info_basic" :style="{
maxWidth: w + 'px', 'white-space': 'nowrap',
overflow: 'hidden',
'text-overflow': 'ellipsis'
}">
<div class="p_text" v-if="EquipmentControlFieldList.includes('EquipmentTypeEnum')">
{{ item.OtherEquipmentType ? item.OtherEquipmentType :
$fd('SiteSurvey_ScanEquipmentType', item.EquipmentTypeEnum) }}</div>
<div v-if="EquipmentControlFieldList.includes('Parameters')" class="p_text" style="margin-left:10px">{{
item.Parameters }}</div>
<div class="p_text" style="margin-left:10px" v-if="EquipmentControlFieldList.includes('ManufacturerType')">{{
item.ManufacturerName ? item.ManufacturerName : $fd('ManufacturerType',
item.ManufacturerType) }}</div>
</div>
<div class="p_text">{{ item.ScannerType }}</div>
<div class="p_text">{{ item.Note }}</div>
<div class="p_text" v-if="EquipmentControlFieldList.includes('ScannerType')">{{ item.ScannerType }}</div>
<div class="p_text" v-if="EquipmentControlFieldList.includes('Note')">{{ item.Note }}</div>
</div>
<div v-if="state === 0 && userTypeEnumInt === 0 && !isHistory" class="p_func">
<el-button type="text" @click="handleEdit(item)">
@@ -36,19 +40,9 @@
</div>
</div>
<!-- 添加/编辑设备信息 -->
<el-drawer
:title="title"
:visible.sync="formVisible"
direction="btt"
size="70%"
>
<EquipmentForm
v-if="formVisible"
:equipment-info="equipmentInfo"
:is-show-parameters="isShowParameters"
@getList="getList"
@close="close"
/>
<el-drawer :title="title" :visible.sync="formVisible" direction="btt" size="70%">
<EquipmentForm v-if="formVisible" :equipment-info="equipmentInfo"
:EquipmentControlFieldList="EquipmentControlFieldList" @getList="getList" @close="close" />
</el-drawer>
</div>
</template>
@@ -81,7 +75,7 @@ export default {
state: null,
trialSiteSurveyId: '',
trialId: '',
isShowParameters: false
EquipmentControlFieldList: []
}
},
mounted() {
@@ -142,8 +136,11 @@ export default {
console.log(e)
}
},
initList(TrialSiteEquipmentSurveyList, trialSiteSurvey, isShowParameters) {
this.isShowParameters = isShowParameters
initList(TrialSiteEquipmentSurveyList, trialSiteSurvey, EquipmentControlFieldList) {
this.EquipmentControlFieldList = []
EquipmentControlFieldList.forEach(item => {
this.EquipmentControlFieldList.push(item.FiledName)
})
this.list = TrialSiteEquipmentSurveyList
this.state = trialSiteSurvey.State
this.$forceUpdate()
@@ -157,63 +154,70 @@ export default {
}
</script>
<style lang="scss" scoped>
.equipments_content{
.equipments_content {
background: #fff;
padding: 10px;
.title{
.title {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.equipment_info{
.equipment_info {
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
border-top: 1px solid #f5f7fa;
.p_icon{
.p_icon {
width: 70px;
font-size: 25px;
font-weight: bold;
text-align: center;
}
.p_info{
.p_info {
position: relative;
flex:1;
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-evenly;
// border-right: 1px solid #f5f7fa;
font-size: 13px;
}
.p_info:after {
content: '';
position: absolute;
top: 50%;
transform: translateY(-50%);
right: 0;
height: 50px;
width: 1px;
background-color: #f5f7fa;
content: '';
position: absolute;
top: 50%;
transform: translateY(-50%);
right: 0;
height: 50px;
width: 1px;
background-color: #f5f7fa;
}
.p_info_basic{
.p_info_basic {
display: flex;
flex-direction: row;
align-items: center;
}
.p_text{
.p_text {
line-height: 25px;
color: #82848a;
}
.p_func{
.p_func {
width: 80px;
padding: 0 10px;
text-align: center;
}
}
}
</style>
+27 -24
View File
@@ -4,30 +4,22 @@
<div class="d_content">
<!-- 项目基本信息 -->
<BasicInfo ref="basicInfo" />
<BasicInfo ref="basicInfo" :IsOnlyUploadFile="IsOnlyUploadFile" :IsSupportUploadFile="IsSupportUploadFile" />
<!-- 历史人员 -->
<HistoricalParticipants ref="historicalParticipant" class="mt5" />
<!-- 新增人员 -->
<Participants ref="participants" class="mt5" />
<!-- 设备调研 -->
<Equipments ref="equipments" class="mt5" />
<Equipments ref="equipments" class="mt5" v-if="!siteSurveyNoteInfo.IsCloseEquipmentSurvey" />
<!-- 备注 -->
<Notes ref="notes" class="mt5" />
</div>
<div class="d_footer">
<div class="d_func_row">
<div
v-if="state === 0 && userTypeEnumInt === 0"
class="d_func"
@click="handleSave"
>
<div v-if="state === 0 && userTypeEnumInt === 0" class="d_func" @click="handleSave">
{{ $t('common:button:save') }}
</div>
<div
v-if="(state === 0 && userTypeEnumInt === 0)"
class="d_func"
@click="handleSubmit('submit')"
>
<div v-if="(state === 0 && userTypeEnumInt === 0)" class="d_func" @click="handleSubmit('submit')">
{{ $t('trials:researchForm:button:submit') }}
</div>
</div>
@@ -58,7 +50,9 @@ export default {
userTypeEnumInt: 0,
rejectVisible: false,
rejectForm: { reason: '' },
siteSurveyNoteInfo: null
siteSurveyNoteInfo: {},
IsSupportUploadFile: false,
IsOnlyUploadFile: false
}
},
mounted() {
@@ -78,6 +72,9 @@ export default {
if (res.Result.SiteSurveyFiledConfig && res.Result.SiteSurveyFiledConfig.ModifyFiledList.length > 0) {
this.siteSurveyNoteInfo = res.Result.SiteSurveyFiledConfig.ModifyFiledList.find(i => i.NeedModifyFiled === 'SiteSurveyNote')
}
this.IsSupportUploadFile = res.Result.SiteSurveyFiledConfig.IsSupportUploadFile
this.IsOnlyUploadFile = res.Result.SiteSurveyFiledConfig.IsOnlyUploadFile
this.siteSurveyNoteInfo.IsCloseEquipmentSurvey = res.Result.SiteSurveyFiledConfig.IsCloseEquipmentSurvey
this.state = res.Result.TrialSiteSurvey.State
this.$refs['basicInfo'].initForm(res.Result.TrialInfo, res.Result.TrialSiteSurvey, res.Result.SiteSurveyFiledConfig ? res.Result.SiteSurveyFiledConfig.NotShowFieldList : null)
var historicalArr = []
@@ -92,7 +89,7 @@ export default {
this.$refs['historicalParticipant'].initList(historicalArr, res.Result.TrialSiteSurvey)
this.$refs['participants'].initList(newArr, res.Result.TrialSiteSurvey)
this.$refs['equipments'].initList(res.Result.TrialSiteEquipmentSurveyList, res.Result.TrialSiteSurvey, !(res.Result.SiteSurveyFiledConfig && res.Result.SiteSurveyFiledConfig.ModifyFiledList.length > 0))
this.$refs['equipments'].initList(res.Result.TrialSiteEquipmentSurveyList, res.Result.TrialSiteSurvey, res.Result.SiteSurveyFiledConfig.EquipmentControlFieldList)
this.$refs['notes'].initPage(this.siteSurveyNoteInfo)
}
this.loading = false
@@ -129,7 +126,8 @@ export default {
this.userTypeEnumInt === 0 ? this.$t('trials:researchForm:message:submitWarning') : this.$t('trials:researchForm:message:submitWarning2'),
{
type: 'warning',
distinguishCancelAndClose: true
distinguishCancelAndClose: true,
customClass: "mobile_confirm"
}
)
if (confirm !== 'confirm') return
@@ -160,9 +158,10 @@ export default {
}
</script>
<style lang="scss" scoped>
.research_detal_wrapper{
background-color:#f5f7fa;
.d_title{
.research_detal_wrapper {
background-color: #f5f7fa;
.d_title {
margin-bottom: 5px;
line-height: 80px;
font-size: 28px;
@@ -173,10 +172,11 @@ export default {
top: 0;
z-index: 10;
}
// .d_content{
// }
.d_footer{
.d_footer {
position: sticky;
bottom: 0;
z-index: 10;
@@ -185,12 +185,14 @@ export default {
text-align: center;
// padding: 20px;
}
.d_func_row{
.d_func_row {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.d_func{
.d_func {
flex: 1;
margin: 20px;
color: #fff;
@@ -207,15 +209,16 @@ export default {
line-height: 1;
white-space: nowrap;
cursor: pointer;
&:hover{
&:hover {
background: #68a2d5;
border-color: #68a2d5;
color: #fff;
}
}
.mt5{
.mt5 {
margin-top: 5px
}
}
</style>
+240 -140
View File
@@ -1,131 +1,88 @@
<template>
<div class="research_login_m_content">
<div class="title">{{ $t('trials:researchForm:title:question') }}</div>
<el-form
ref="loginForm"
v-loading="loading"
label-position="left"
label-width="120px"
:model="form"
:rules="rules"
>
<div class="basic_content">
<!-- 项目编号 -->
<el-form-item
:label="$t('trials:researchForm:form:trialId')"
>
<span>{{ form.TrialCode }}</span>
</el-form-item>
<!-- 试验方案号 -->
<el-form-item
:label="$t('trials:researchForm:form:researchNo')"
>
<span>{{ form.ResearchProgramNo }}</span>
</el-form-item>
<!-- 试验名称 -->
<el-form-item
:label="$t('trials:researchForm:form:researchName')"
>
<span>{{ form.ExperimentName }}</span>
</el-form-item>
<!-- 适应症类型 -->
<el-form-item
:label="$t('trials:researchForm:form:decleareType')"
>
<span>{{ form.IndicationType }}</span>
</el-form-item>
<div v-if="IsExpired"></div>
<div v-else-if="verify" class="LinkVerificationCode_content">
<el-form ref="codeForm" v-loading="loading" label-position="top" label-width="120px" :model="codeForm"
:rules="code_rules">
<div class="basic_content">
<!-- 项目编号 -->
<el-form-item :label="$t('trials:researchForm:message:LinkVerificationCode')" prop="LinkVerificationCode">
<el-input v-model="codeForm.LinkVerificationCode" autocomplete="new-password" />
</el-form-item>
</div>
</el-form>
<div class="submit_content">
<el-button size="large" type="primary" @click="getLinkVerificationCodeIsEffective">
{{ $t('common:button:submit') }}
</el-button>
</div>
<div class="login_content">
<el-form-item
:label="$t('trials:researchForm:form:siteName')"
prop="TrialSiteId"
>
<el-select
v-model="form.TrialSiteId"
filterable
style="width:100%;"
@change="handleSiteChange"
>
<el-option
v-for="(item,index) of siteOptions"
:key="index"
:label="item.TrialSiteAliasName"
:value="item.TrialSiteId"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="form.TrialSiteId && isHaveSiteSurveyRecord"
label=""
style="text-align:right;"
>
<!-- 更新调研表 -->
<el-link
v-if="!form.IsUpdate"
type="primary"
@click="form.IsUpdate = true"
>
{{ $t('trials:researchForm:button:updateQsForm') }}
</el-link>
<!-- 取消更新调研表 -->
<el-link
v-else
type="primary"
@click="form.IsUpdate = false;form.ReplaceUserEmailOrPhone=''"
>
{{ $t('trials:researchForm:button:cancelUpdateQsForm') }}
</el-link>
</el-form-item>
</div>
<template v-else>
<div class="title">{{ $t('trials:researchForm:title:question') }}</div>
<el-form ref="loginForm" v-loading="loading" label-position="left" label-width="120px" :model="form"
:rules="rules">
<div class="basic_content">
<!-- 项目编号 -->
<el-form-item :label="$t('trials:researchForm:form:trialId')">
<span>{{ form.TrialCode }}</span>
</el-form-item>
<!-- 试验方案号 -->
<el-form-item :label="$t('trials:researchForm:form:researchNo')">
<span>{{ form.ResearchProgramNo }}</span>
</el-form-item>
<!-- 试验名称 -->
<el-form-item :label="$t('trials:researchForm:form:researchName')">
<span>{{ form.ExperimentName }}</span>
</el-form-item>
<!-- 适应症类型 -->
<el-form-item :label="$t('trials:researchForm:form:decleareType')">
<span>{{ form.IndicationType }}</span>
</el-form-item>
</div>
<div class="login_content">
<el-form-item :label="$t('trials:researchForm:form:siteName')" prop="TrialSiteId">
<el-select v-model="form.TrialSiteId" filterable style="width:100%;" @change="handleSiteChange">
<el-option v-for="(item, index) of siteOptions" :key="index" :label="item.TrialSiteAliasName"
:value="item.TrialSiteId" />
</el-select>
</el-form-item>
<el-form-item v-if="form.TrialSiteId && isHaveSiteSurveyRecord" label="" style="text-align:right;">
<!-- 更新调研表 -->
<el-link v-if="!form.IsUpdate" type="primary" @click="form.IsUpdate = true">
{{ $t('trials:researchForm:button:updateQsForm') }}
</el-link>
<!-- 取消更新调研表 -->
<el-link v-else type="primary" @click="form.IsUpdate = false; form.ReplaceUserEmailOrPhone = ''">
{{ $t('trials:researchForm:button:cancelUpdateQsForm') }}
</el-link>
</el-form-item>
<!-- 原调研表填写人邮箱 -->
<el-form-item
v-if="form.IsUpdate"
:label="$t('trials:researchForm:form:originalEmail')"
prop="ReplaceUserEmailOrPhone"
>
<el-input
v-model="form.ReplaceUserEmailOrPhone"
autocomplete="new-password"
/>
</el-form-item>
<!-- 联系邮箱 -->
<el-form-item
:label="$t('trials:researchForm:form:contactorEmail')"
prop="EmailOrPhone"
>
<el-input
v-model="form.EmailOrPhone"
autocomplete="new-password"
/>
</el-form-item>
<el-form-item
:label="$t('trials:researchForm:form:verifyCode')"
>
<div class="code_content">
<el-input
v-model="form.VerificationCode"
autocomplete="new-password"
/>
<el-button
type="primary"
:disabled="sendDisabled || !form.EmailOrPhone || count > 0"
@click="handleSendCode"
>
{{ this.$t('trials:researchForm:button:send') }} {{ sendTitle ? `${sendTitle}` : null }}
<!-- 原调研表填写人邮箱 -->
<el-form-item v-if="form.IsUpdate" :label="$t('trials:researchForm:form:originalEmail')"
prop="ReplaceUserEmailOrPhone">
<el-input v-model="form.ReplaceUserEmailOrPhone" autocomplete="new-password" />
</el-form-item>
<!-- 联系邮箱 -->
<el-form-item :label="$t('trials:researchForm:form:contactorEmail')" prop="EmailOrPhone">
<el-input v-model="form.EmailOrPhone" autocomplete="new-password" />
</el-form-item>
<el-form-item :label="$t('trials:researchForm:form:verifyCode')">
<div class="code_content">
<el-input v-model="form.VerificationCode" autocomplete="new-password" />
<el-button type="primary" :disabled="sendDisabled || !form.EmailOrPhone || count > 0"
@click="handleSendCode">
{{ this.$t('trials:researchForm:button:send') }} {{ sendTitle ? `${sendTitle}` : null }}
</el-button>
</div>
</el-form-item>
<div class="submit_content">
<el-button size="large" type="primary" @click="onSubmit">
{{ $t('common:button:submit') }}
</el-button>
</div>
</el-form-item>
<div class="submit_content">
<el-button
size="large"
type="primary"
@click="onSubmit"
>
{{ $t('common:button:submit') }}
</el-button>
</div>
</div>
</el-form>
</el-form>
</template>
</div>
</template>
<script>
@@ -133,6 +90,10 @@ import { sendVerifyCode, verifySendCode, getTrialSurveyInitInfo } from '@/api/re
import { getUserMenuTree, getUserPermissions } from '@/api/user'
import store from '@/store'
import { mapGetters, mapMutations } from 'vuex'
import {
getLinkLinkExpirationTime,
getLinkVerificationCodeIsEffective
} from '@/api/trials'
export default {
name: 'ResearchMobileLogin',
data() {
@@ -185,6 +146,16 @@ export default {
}
return {
trialId: '',
IsExpired: true,
verify: true,
codeForm: {
LinkVerificationCode: null,
},
code_rules: {
LinkVerificationCode: [
{ required: true, message: this.$t('trials:researchForm:formRule:specify'), trigger: ['blur'] }
],
},
form: {
Sponsor: null, //
ResearchProgramNo: null, //
@@ -239,16 +210,119 @@ export default {
])
},
mounted() {
this.$i18n.locale = this.$route.query.lang
this.setLanguage(this.$route.query.lang)
this.$updateDictionary()
if (this.$route.query.trialId) {
this.trialId = this.$route.query.trialId
this.initPage()
let lang = this.$route.query.lang
if (!lang) {
const language = navigator.language
lang = 'en'
if (language.includes("zh")) {
lang = 'zh'
}
}
this.$i18n.locale = lang
this.setLanguage(lang)
this.$updateDictionary()
this.getLinkTimeIsExpired()
},
methods: {
...mapMutations({ setLanguage: 'lang/setLanguage' }),
async getLinkTimeIsExpired() {
try {
let data = {
TrialId: this.$route.query.trialId,
}
let res = await getLinkLinkExpirationTime(data)
if (res.IsSuccess) {
if (res.Result.IsExpired) {
return this.$router.replace('/link_expired')
// return this.$confirm(this.$t("trials:researchForm:confirm:linkIsExpired"), '', {
// type: 'warning'
// })
// return this.IsExpired = true
}
// this.customPrompt()
this.IsExpired = false
}
} catch (err) {
console.log(err)
}
},
async getLinkVerificationCodeIsEffective() {
try {
let validate = await this.$refs.codeForm.validate()
if (!validate) return false
let data = {
TrialId: this.$route.query.trialId,
LinkVerificationCode: this.codeForm.LinkVerificationCode
}
let res = await getLinkVerificationCodeIsEffective(data)
if (res.IsSuccess) {
if (!res.Result.IsEffective) {
return false
}
this.verify = false
if (this.$route.query.trialId) {
this.trialId = this.$route.query.trialId
this.initPage()
}
if (this.$route.query.isUpload) {
this.isUpload = true
this.form.IsUpdate = true
let { email, oldEMail, trialSiteId } = this.$route.query
if (trialSiteId) this.form.TrialSiteId = trialSiteId
if (oldEMail) this.form.ReplaceUserEmailOrPhone = oldEMail
if (email && email !== 'null') {
this.form.EmailOrPhone = email
} else {
this.form.EmailOrPhone = oldEMail
}
if ((email && email !== 'null') || (oldEMail && oldEMail !== 'null')) {
this.isNeedUpload = false
}
}
}
return true
} catch (err) {
return false
console.log(err)
}
},
async customPrompt(name) {
try {
const that = this
//
let message = this.$t('trials:researchForm:message:LinkVerificationCode')
const { value } = await this.$prompt(message, '', {
showClose: false,
cancelButtonText: this.$t('common:button:cancel'),
confirmButtonText: this.$t('trials:researchForm:button:saveLinkCode'),
showCancelButton: false,
closeOnClickModal: false,
closeOnPressEscape: false,
inputValue: name,
beforeClose: async (action, instance, done) => {
if (action === 'confirm') {
const value = instance.inputValue
if (!value) {
that.$message.error(this.$t('trials:researchForm:error:noValue'))
} else {
let flag = await this.getLinkVerificationCodeIsEffective(value)
if (!flag) {
that.$message.error(this.$t('trials:researchForm:confirm:isEffective'))
} else {
done()
}
}
} else {
done()
}
}
})
this.getLinkVerificationCodeIsEffective(value)
} catch (err) {
console.log(err)
return null
}
},
//
async initPage() {
try {
@@ -292,8 +366,18 @@ export default {
if (res.IsSuccess) {
zzSessionStorage.clear()
this.$i18n.locale = this.$route.query.lang
this.setLanguage(this.$route.query.lang)
// this.$i18n.locale = this.$route.query.lang
// this.setLanguage(this.$route.query.lang)
let lang = this.$route.query.lang
if (!lang) {
const language = navigator.language
lang = 'en'
if (language.includes("zh")) {
lang = 'zh'
}
}
this.$i18n.locale = lang
this.setLanguage(lang)
store.dispatch('user/setToken', res.Result.Token)
zzSessionStorage.setItem('TokenKey', res.Result.Token)
var permissions = await getUserPermissions()
@@ -363,9 +447,10 @@ export default {
}
</script>
<style lang="scss" scoped>
.research_login_m_content{
background-color:#f5f7fa;
.title{
.research_login_m_content {
background-color: #f5f7fa;
.title {
margin-bottom: 5px;
line-height: 80px;
font-size: 28px;
@@ -373,27 +458,33 @@ export default {
text-align: center;
background: #fff;
}
.basic_content{
.basic_content {
padding: 0 20px;
background: #fff;
}
.login_content{
.login_content {
padding: 5px 20px;
margin-top: 5px;
background: #fff;
::v-deep .el-form-item {
padding-bottom: 20px;
}
}
.code_content{
display:flex;
.code_content {
display: flex;
flex-direction: row;
justify-content: flex-start;
.el-input{
.el-input {
margin-right: 10px;
}
}
.submit_content{
.submit_content {
margin-top: 20px;
text-align: center;
}
@@ -402,11 +493,20 @@ export default {
margin-bottom: 0px;
padding-top: 5px;
border-bottom: 1px solid #f5f7fa;
.el-form-item__content{
.el-form-item__content {
color: #82848a;
}
}
}
.LinkVerificationCode_content {
position: fixed;
top: 40%;
bottom: 40%;
right: 0;
left: 0;
}
</style>
+336 -21
View File
@@ -1,6 +1,6 @@
<template>
<el-form v-if="isShow" ref="researchBSForm" size="small" :model="form" :rules="rules" style="width:80%"
label-position="left">
label-position="left" v-loading="loading">
<!-- 项目编号 -->
<el-form-item :label="$t('trials:researchForm:form:trialId')">
<el-input v-model="form.TrialCode" disabled />
@@ -20,7 +20,7 @@
<!-- 中心名称 -->
<el-form-item :label="$t('trials:researchForm:form:siteName')" prop="TrialSiteId">
<el-select v-model="form.TrialSiteId" filterable style="width:100%;"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" @change="handleSiteChange">
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory) && !isPM" @change="handleSiteChange">
<el-option v-for="(item, index) of siteOptions" :key="index" :label="item.TrialSiteAliasName"
:value="item.TrialSiteId" />
</el-select>
@@ -31,53 +31,117 @@
</el-form-item>
<!-- 联系人 -->
<el-form-item :label="$t('trials:researchForm:form:contactor')" prop="UserName">
<el-input v-model="form.UserName" :disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" />
<el-input v-model="form.UserName" :disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory) && !isPM" />
</el-form-item>
<!-- 联系电话 -->
<el-form-item :label="$t('trials:researchForm:form:contactorPhone')" prop="Phone">
<el-input v-model="form.Phone" :disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" />
<el-input v-model="form.Phone" :disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory) && !isPM" />
</el-form-item>
<!-- 联系邮箱 -->
<el-form-item :label="$t('trials:researchForm:form:contactorEmail')" prop="Email">
<el-input v-model="form.Email" disabled />
</el-form-item>
<el-form-item :label="$t('trials:researchForm:form:CommonUploadRecordId')
" v-if="IsSupportUploadFile" class="file">
<el-input v-model="form.SiteSurveyFile.FileName" type="textarea" :autosize="{ minRows: 1, maxRows: 3 }"
style="width: 30%;margin-right: 5px;" disabled />
<div class="upload" v-if="!(!(state === 0 && userTypeEnumInt === 0) || isHistory)">
<input accept=".pdf,.docx,.doc" type="file" name="uploadFolder" class="select-file" title=""
@change="beginScanFiles($event)" />
<div class="btn-select">
{{ $t('dictionary:template:basicData:button:selectFile') }}
</div>
</div>
<el-button type="primary" @click="viewManualFile"
:disabled="!this.form.SiteSurveyFile || !this.form.SiteSurveyFile.Path">{{
$t('trials:researchForm:form:preview') }}</el-button>
<el-button type="primary" @click="downLoad"
:disabled="!this.form.SiteSurveyFile || !this.form.SiteSurveyFile.Path">{{
$t('trials:researchForm:form:download') }}</el-button>
</el-form-item>
<!-- <el-divider /> -->
<!-- 平均刻盘周期 -->
<el-form-item v-if="!notShowFieldList.includes('AverageEngravingCycle')"
:label="$t('trials:researchForm:form:engravingCycle')">
<el-input-number v-model="form.AverageEngravingCycle"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" controls-position="right" :min="0" />
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory) && !isPM" controls-position="right"
:min="0" />
</el-form-item>
<!-- MRI-PDFF 是否为本中心该适应症的常规诊疗检查项目 -->
<el-form-item v-if="!notShowFieldList.includes('IsRoutineMRIPDEE')"
:label="$t('trials:researchForm:form:IsRoutineMRIPDEE')" prop="IsRoutineMRIPDEE">
<el-radio-group v-model="form.IsRoutineMRIPDEE"
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory) && !isPM">
<el-radio v-for="item of $d.YesOrNo" :key="`IsRoutineMRIPDEE${item.value}`" :label="item.value">{{
item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<!-- MRI-PDFF 检查的检测周期含单次检查时长预约等待时长等 -->
<el-form-item
v-if="!notShowFieldList.includes('MRIPDFFScanTime') || !notShowFieldList.includes('MRIPDFFLeadTime') || !notShowFieldList.includes('MRIPDFFOther')"
:label="$t('trials:researchForm:form:IsMRIPDFF')">
</el-form-item>
<!-- 单次检查时长分钟 -->
<el-form-item v-if="!notShowFieldList.includes('MRIPDFFScanTime')"
:label="$t('trials:researchForm:form:MRIPDFFScanTime')">
<el-input-number v-model="form.MRIPDFFScanTime"
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory) && !isPM" controls-position="right"
:min="0" />
</el-form-item>
<!-- 平均预约等待时长 -->
<el-form-item v-if="!notShowFieldList.includes('MRIPDFFLeadTime')"
:label="$t('trials:researchForm:form:MRIPDFFLeadTime')">
<el-input-number v-model="form.MRIPDFFLeadTime"
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory) && !isPM" controls-position="right"
:min="0" />
</el-form-item>
<!-- 特殊情况备注-->
<el-form-item v-if="!notShowFieldList.includes('MRIPDFFOther')"
:label="$t('trials:researchForm:form:MRIPDFFOther')">
<el-input v-model="form.MRIPDFFOther" type="textarea" :autosize="{ minRows: 2, maxRows: 4 }"
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory) && !isPM" />
</el-form-item>
<!-- 如已选择研究者评估项目是否会授权影像科老师参与本试验如不单独授权是否可在试验中保持 1-2 名固定技师操作-->
<el-form-item
v-if="!notShowFieldList.includes('IsAuthorizeRadiologistsParticipate') || !notShowFieldList.includes('AssignFixedTechnologists')"
:label="$t('trials:researchForm:form:IsAuthorize')" prop="IsAuthorize">
<el-radio-group v-model="form.IsAuthorize"
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory) && !isPM" @input="handleIsAuthorizeInput">
<el-radio label="IsAuthorizeRadiologistsParticipate">{{
$t('trials:researchForm:form:IsAuthorizeRadiologistsParticipate') }}</el-radio>
<el-radio label="AssignFixedTechnologists">{{
$t('trials:researchForm:form:AssignFixedTechnologists') }}</el-radio>
</el-radio-group>
</el-form-item>
<!-- 请确认参与本项目影像采集的影像技师具备对应的资质技师证对应设备的大型设备上岗证 -->
<el-form-item v-if="!notShowFieldList.includes('IsConfirmImagingTechnologist')"
:label="$t('trials:researchForm:form:isQualified')">
:label="$t('trials:researchForm:form:isQualified')" prop="IsConfirmImagingTechnologist">
<el-radio-group v-model="form.IsConfirmImagingTechnologist"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory">
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory) && !isPM">
<el-radio v-for="item of $d.YesOrNo" :key="`IsConfirmImagingTechnologist${item.value}`" :label="item.value">{{
item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<!-- 原因 -->
<el-form-item v-if="!notShowFieldList.includes('NotConfirmReson') && form.IsConfirmImagingTechnologist === false"
:label="$t('trials:researchForm:form:notQualifiedReason')">
:label="$t('trials:researchForm:form:notQualifiedReason')" prop="NotConfirmReson">
<el-input v-model="form.NotConfirmReson" type="textarea" :autosize="{ minRows: 2, maxRows: 4 }"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" />
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory) && !isPM" />
</el-form-item>
<!-- 研究单位疗效评估人员类型 -->
<el-form-item v-if="!notShowFieldList.includes('EfficacyEvaluatorType')"
:label="$t('trials:researchForm:form:staffType')">
<el-radio-group v-model="form.EfficacyEvaluatorType"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory">
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory) && !isPM">
<el-radio v-for="item of $d.EfficacyEvaluatorType" :key="`EfficacyEvaluatorType${item.value}`"
:label="item.value">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<!-- 是否严格按照研究单位影像手册参数完成图像采集 -->
<el-form-item v-if="!notShowFieldList.includes('IsFollowStudyParameters')">
<el-form-item v-if="!notShowFieldList.includes('IsFollowStudyParameters')" prop="IsFollowStudyParameters">
<span slot="label" v-html="$t('trials:researchForm:form:isFollowStudyParam')" />
<el-radio-group v-model="form.IsFollowStudyParameters"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" style="margin-right: 10px;">
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory) && !isPM" style="margin-right: 10px;">
<el-radio v-for="item of $d.YesOrNo" :key="`IsFollowStudyParameters${item.value}`" :label="item.value">{{
item.label }}</el-radio>
</el-radio-group>
@@ -86,10 +150,27 @@
</el-button>
</el-form-item>
<!-- 不能严格按照研究单位影像手册参数采集图像原因 -->
<el-form-item v-if="!notShowFieldList.includes('NotFollowReson') && !form.IsFollowStudyParameters">
<el-form-item v-if="!notShowFieldList.includes('NotFollowReson') && !form.IsFollowStudyParameters"
prop="NotFollowReson">
<span slot="label" v-html="$t('trials:researchForm:form:notFollowStudyParam')" />
<el-input v-model="form.NotFollowReson" type="textarea" :autosize="{ minRows: 2, maxRows: 4 }"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" />
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory) && !isPM" />
</el-form-item>
<!-- 是否严格按照影像手册参数完成刻盘 -->
<el-form-item v-if="!notShowFieldList.includes('ISStrictManualBurnFlag')" prop="ISStrictManualBurnFlag">
<span slot="label" v-html="$t('trials:researchForm:form:ISStrictManualBurnFlag')" />
<el-radio-group v-model="form.ISStrictManualBurnFlag"
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory) && !isPM" style="margin-right: 10px;">
<el-radio v-for="item of $d.YesOrNo" :key="`ISStrictManualBurnFlag${item.value}`" :label="item.value">{{
item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<!-- 不能严格按照影像手册参数完成刻盘原因 -->
<el-form-item v-if="!notShowFieldList.includes('NotStrictManualBurnFlagReason') && !form.ISStrictManualBurnFlag"
prop="NotStrictManualBurnFlagReason">
<span slot="label" v-html="$t('trials:researchForm:form:NotStrictManualBurnFlagReason')" />
<el-input v-model="form.NotStrictManualBurnFlagReason" type="textarea" :autosize="{ minRows: 2, maxRows: 4 }"
:disabled="(!(state === 0 && userTypeEnumInt === 0) || isHistory) && !isPM" />
</el-form-item>
<el-form-item>
<!-- 保存 -->
@@ -103,9 +184,10 @@
</el-form>
</template>
<script>
import { getTrialSiteSelect, getTrialDocumentList } from '@/api/trials'
import { getTrialSiteSelect, getTrialDocumentList, addOrUpdateCommonUploadRecord } from '@/api/trials'
import { addOrUpdateTrialSiteSurvey } from '@/api/research'
import attachmentPreview from '@/views/dictionary/attachment/components/SignatureTemplate/attachmentPreview'
import { downLoadFile } from '@/utils/stream.js'
export default {
name: 'QuestionForm',
components: { attachmentPreview },
@@ -113,7 +195,19 @@ export default {
isHistory: {
type: Boolean,
default: false
}
},
IsOnlyUploadFile: {
type: Boolean,
default: false
},
IsSupportUploadFile: {
type: Boolean,
default: false
},
isPM: {
type: Boolean,
default: false
},
},
data() {
var checkPhone = (rule, value, callback) => {
@@ -138,6 +232,7 @@ export default {
}
}
return {
loading: false,
form: {
Id: '',
Sponsor: '', //
@@ -152,16 +247,51 @@ export default {
Phone: '', //
Email: '', //
AverageEngravingCycle: '',
IsRoutineMRIPDEE: '',
MRIPDFFScanTime: '',
MRIPDFFLeadTime: '',
MRIPDFFOther: '',
IsAuthorize: '',
IsAuthorizeRadiologistsParticipate: '',
AssignFixedTechnologists: '',
ISStrictManualBurnFlag: '',
NotStrictManualBurnFlagReason: '',
IsConfirmImagingTechnologist: '',
NotConfirmReson: '',
EfficacyEvaluatorType: '',
IsFollowStudyParameters: '',
NotFollowReson: ''
NotFollowReson: '',
CommonUploadRecordId: null,
SiteSurveyFile: []
},
rules: {
TrialSiteId: [
{ required: true, message: this.$t('trials:researchForm:formRule:specify'), trigger: 'blur' }
],
IsRoutineMRIPDEE: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: 'blur' }
],
IsAuthorize: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: 'blur' }
],
ISStrictManualBurnFlag: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: 'blur' }
],
IsFollowStudyParameters: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: 'blur' }
],
IsConfirmImagingTechnologist: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: 'blur' }
],
NotConfirmReson: [
{ required: true, message: this.$t('trials:researchForm:formRule:specify'), trigger: 'blur' }
],
NotFollowReson: [
{ required: true, message: this.$t('trials:researchForm:formRule:specify'), trigger: 'blur' }
],
NotStrictManualBurnFlagReason: [
{ required: true, message: this.$t('trials:researchForm:formRule:specify'), trigger: 'blur' }
],
UserName: [
{ required: true, validator: (rule, value, callback) => { !value ? callback(new Error(this.$t('trials:researchForm:formRule:specify'))) : callback() }, trigger: 'blur' },
{ min: 0, max: 50, message: this.$t('trials:researchForm:formRule:maxLength'), trigger: ['blur', 'change'] }
@@ -188,6 +318,68 @@ export default {
}
},
methods: {
async beginScanFiles(e) {
try {
let file = e.target.files[0]
let name = file.name
if (!this.checkFileSuffix(name)) return this.$message.warning(this.$t("trials:researchRecord:ImageManual:message:checkFileSuffix"))
this.loading = true
file = await this.fileToBlob(file)
let scope = this
var index = name.lastIndexOf('.')
var type = name.substring(index + 1, name.length)
let res = await this.OSSclient.put(
`/${this.$route.query.trialId}/InspectionUpload/SiteSurvey/${this.form.TrialSiteId}/${scope.getGuid(Date.now() + '_' + name.split('.')[0])}.${type}`,
file
)
let data = {
Path: this.$getObjectName(res.url),
FileName: name,
FileSize: file.size,
FileType: type
}
res = await addOrUpdateCommonUploadRecord(data)
this.loading = false
if (res.IsSuccess) {
this.form.CommonUploadRecordId = res.Result
data.Id = res.Result
this.form.SiteSurveyFile = data
}
} catch (err) {
console.log(err)
this.loading = false;
}
},
checkFileSuffix(fileName) {
var index = fileName.lastIndexOf('.')
var suffix = fileName.substring(index + 1, fileName.length)
if ('.pdf'.toLocaleLowerCase().search(suffix.toLocaleLowerCase()) === -1 && '.docx'.toLocaleLowerCase().search(suffix.toLocaleLowerCase()) === -1 && '.doc'.toLocaleLowerCase().search(suffix.toLocaleLowerCase()) === -1) {
return false
} else {
return true
}
},
handleIsAuthorizeInput(label) {
this.form.IsAuthorizeRadiologistsParticipate = false
this.form.AssignFixedTechnologists = false
this.form[label] = true
},
async viewManualFile() {
if (!this.form.SiteSurveyFile || !this.form.SiteSurveyFile.Path) return this.$message.warning(this.$t("trials:researchForm:message:notFile"))
this.$preview({
path: this.form.SiteSurveyFile.Path,
type: this.form.SiteSurveyFile.FileType,
title: this.form.SiteSurveyFile.Path.FileName,
})
},
async downLoad() {
if (!this.form.SiteSurveyFile || !this.form.SiteSurveyFile.Path) return this.$message.warning(this.$t("trials:researchForm:message:notFile"))
return await downLoadFile(
this.OSSclientConfig.basePath + this.form.SiteSurveyFile.Path,
this.form.SiteSurveyFile.FileName
)
},
async viewManual() {
try {
let data = {
@@ -218,8 +410,58 @@ export default {
}
},
//
handleSave(isAutoCommit) {
handleSave(isAutoCommit, isCheck = false) {
return new Promise((resolve, reject) => {
isCheck = false
if (this.IsOnlyUploadFile && !isCheck) {
if (!this.form.UserName) {
this.$message.warning(this.$t("trials:researchForm:message:notUserName"))
return resolve(false)
}
if (!this.form.Phone) {
this.$message.warning(this.$t("trials:researchForm:message:notPhone"))
return resolve(false)
}
if (!this.form.CommonUploadRecordId) {
this.$message.warning(this.$t("trials:researchForm:message:notCommonUploadRecordId"))
return resolve(false)
}
this.btnLoading = true
const param = {
id: this.form.Id,
trialId: this.$route.query.trialId,
trialSiteId: this.form.TrialSiteId,
userName: this.form.UserName,
phone: this.form.Phone,
email: this.form.Email,
averageEngravingCycle: this.form.AverageEngravingCycle,
IsRoutineMRIPDEE: this.form.IsRoutineMRIPDEE,
MRIPDFFScanTime: this.form.MRIPDFFScanTime,
MRIPDFFLeadTime: this.form.MRIPDFFLeadTime,
MRIPDFFOther: this.form.MRIPDFFOther,
IsAuthorizeRadiologistsParticipate: this.form.IsAuthorizeRadiologistsParticipate,
AssignFixedTechnologists: this.form.AssignFixedTechnologists,
ISStrictManualBurnFlag: this.form.ISStrictManualBurnFlag,
NotStrictManualBurnFlagReason: this.form.NotStrictManualBurnFlagReason,
isConfirmImagingTechnologist: this.form.IsConfirmImagingTechnologist,
notConfirmReson: this.form.NotConfirmReson,
efficacyEvaluatorType: this.form.EfficacyEvaluatorType,
isFollowStudyParameters: this.form.IsFollowStudyParameters,
notFollowReson: this.form.NotFollowReson,
CommonUploadRecordId: this.form.CommonUploadRecordId
}
addOrUpdateTrialSiteSurvey(param).then(res => {
this.btnLoading = false
if (res.IsSuccess && !isAutoCommit) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
resolve(true)
}).catch(() => {
this.btnLoading = false
reject(false)
})
return resolve(true)
}
this.$refs['researchBSForm'].validate(valid => {
if (valid) {
this.btnLoading = true
@@ -231,11 +473,20 @@ export default {
phone: this.form.Phone,
email: this.form.Email,
averageEngravingCycle: this.form.AverageEngravingCycle,
IsRoutineMRIPDEE: this.form.IsRoutineMRIPDEE,
MRIPDFFScanTime: this.form.MRIPDFFScanTime,
MRIPDFFLeadTime: this.form.MRIPDFFLeadTime,
MRIPDFFOther: this.form.MRIPDFFOther,
IsAuthorizeRadiologistsParticipate: this.form.IsAuthorizeRadiologistsParticipate,
AssignFixedTechnologists: this.form.AssignFixedTechnologists,
ISStrictManualBurnFlag: this.form.ISStrictManualBurnFlag,
NotStrictManualBurnFlagReason: this.form.NotStrictManualBurnFlagReason,
isConfirmImagingTechnologist: this.form.IsConfirmImagingTechnologist,
notConfirmReson: this.form.NotConfirmReson,
efficacyEvaluatorType: this.form.EfficacyEvaluatorType,
isFollowStudyParameters: this.form.IsFollowStudyParameters,
notFollowReson: this.form.NotFollowReson
notFollowReson: this.form.NotFollowReson,
CommonUploadRecordId: this.form.CommonUploadRecordId
}
addOrUpdateTrialSiteSurvey(param).then(res => {
this.btnLoading = false
@@ -260,7 +511,7 @@ export default {
//
async initForm(trialInfo, trialSiteSurvey, notShowFieldList) {
// site
const { Result } = await getTrialSiteSelect(this.$route.query.trialId)
const { Result } = await getTrialSiteSelect(this.$route.query.trialId, { IgnoreDisable: true })
this.siteOptions = Result
this.form.Id = trialSiteSurvey.Id
this.form.Sponsor = trialInfo.Sponsor //
@@ -274,7 +525,20 @@ export default {
this.form.UserName = trialSiteSurvey.UserName //
this.form.Phone = trialSiteSurvey.Phone //
this.form.Email = trialSiteSurvey.Email //
this.form.CommonUploadRecordId = trialSiteSurvey.CommonUploadRecordId
this.form.SiteSurveyFile = trialSiteSurvey.SiteSurveyFile || {}
this.form.fileStr = this.form.SiteSurveyFile.FileName
this.form.AverageEngravingCycle = trialSiteSurvey.AverageEngravingCycle
this.form.IsRoutineMRIPDEE = trialSiteSurvey.IsRoutineMRIPDEE
this.form.MRIPDFFScanTime = trialSiteSurvey.MRIPDFFScanTime
this.form.MRIPDFFLeadTime = trialSiteSurvey.MRIPDFFLeadTime
this.form.MRIPDFFOther = trialSiteSurvey.MRIPDFFOther
this.form.IsAuthorizeRadiologistsParticipate = trialSiteSurvey.IsAuthorizeRadiologistsParticipate
this.form.AssignFixedTechnologists = trialSiteSurvey.AssignFixedTechnologists
if (this.form.IsAuthorizeRadiologistsParticipate) this.form.IsAuthorize = 'IsAuthorizeRadiologistsParticipate'
if (this.form.AssignFixedTechnologists) this.form.IsAuthorize = 'AssignFixedTechnologists'
this.form.ISStrictManualBurnFlag = trialSiteSurvey.ISStrictManualBurnFlag
this.form.NotStrictManualBurnFlagReason = trialSiteSurvey.NotStrictManualBurnFlagReason
this.form.IsConfirmImagingTechnologist = trialSiteSurvey.IsConfirmImagingTechnologist
this.form.NotConfirmReson = trialSiteSurvey.NotConfirmReson
this.form.EfficacyEvaluatorType = trialSiteSurvey.EfficacyEvaluatorType
@@ -294,3 +558,54 @@ export default {
}
}
</script>
<style lang="scss" scoped>
.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;
margin-right: 5px;
.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穿
}
}
.file {
::v-deep .el-form-item__content {
display: flex;
align-items: center;
}
}
</style>
+163 -33
View File
@@ -1,40 +1,98 @@
<template>
<el-form ref="equipmentForm" :model="form" :rules="rules" label-width="150px">
<el-form ref="equipmentForm" :model="form" :rules="rules" label-width="380px">
<div class="base-dialog-body">
<!-- 扫描设备 -->
<el-form-item :label="$t('trials:equiptResearch:form:equipment')" prop="EquipmentTypeId">
<el-select
v-model="form.EquipmentTypeId"
style="width:100%"
>
<!-- <el-option
v-for="item of dictionaryList.SiteSurvey_ScanEquipmentType"
:key="item.Id"
:label="item.Value"
:value="item.Id"
/> -->
<el-option
v-for="item of $d.SiteSurvey_ScanEquipmentType"
:key="item.id"
:label="item.label"
:value="item.id"
/>
</el-select>
<el-form-item :label="$t('trials:equiptResearch:form:equipment')" prop="EquipmentTypeEnum"
v-if="EquipmentControlFieldList.includes('EquipmentTypeEnum')">
<div style="display: flex;align-items: center;">
<el-select v-model="form.EquipmentTypeEnum" style="width:100%" @change="form.OtherEquipmentType = null">
<el-option v-for="item of $d.SiteSurvey_ScanEquipmentType" :key="item.id" :label="item.label"
:value="item.value" />
</el-select>
<el-input placeholder="" v-model="form.OtherEquipmentType" style="margin-left: 10px;"
v-if="form.EquipmentTypeEnum == '-1'" clearable>
</el-input>
</div>
</el-form-item>
<!-- 扫描参数 -->
<el-form-item v-if="isShowParameters" :label="$t('trials:equiptResearch:form:param')">
<el-form-item :label="$t('trials:equiptResearch:form:param')"
v-if="EquipmentControlFieldList.includes('Parameters')" prop="Parameters">
<el-input v-model="form.Parameters" />
</el-form-item>
<!-- 扫描仪器制造商名称 -->
<el-form-item :label="$t('trials:equiptResearch:form:manufacturer')">
<el-input v-model="form.ManufacturerName" />
<el-form-item :label="$t('trials:equiptResearch:form:manufacturer')"
v-if="EquipmentControlFieldList.includes('ManufacturerType')" prop="ManufacturerType">
<div style="display: flex;align-items: center;">
<el-select v-model="form.ManufacturerType" style="width:100%" @change="form.ManufacturerName = null">
<el-option v-for="item of $d.ManufacturerType" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
<el-input placeholder="" v-model="form.ManufacturerName" style="margin-left: 10px;"
v-if="form.ManufacturerType == '-1'" clearable>
</el-input>
</div>
</el-form-item>
<!-- 扫描仪型号 -->
<el-form-item :label="$t('trials:equiptResearch:form:model')">
<el-form-item :label="$t('trials:equiptResearch:form:model')"
v-if="EquipmentControlFieldList.includes('ScannerType')" prop="ScannerType">
<el-input v-model="form.ScannerType" />
</el-form-item>
<!-- 磁场强度 -->
<el-form-item :label="$t('trials:equiptResearch:form:MagneticFieldStrengthType')"
v-if="EquipmentControlFieldList.includes('MagneticFieldStrengthType')" prop="MagneticFieldStrengthType">
<el-select v-model="form.MagneticFieldStrengthType" style="width:100%">
<el-option v-for="item of $d.MagneticFieldStrengthType" :key="item.id" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<!-- 体部线圈通道数 -->
<el-form-item :label="$t('trials:equiptResearch:form:BodyCoilChannelCount')"
v-if="EquipmentControlFieldList.includes('BodyCoilChannelCount')" prop="BodyCoilChannelCount">
<el-select v-model="form.BodyCoilChannelCount" style="width:100%">
<el-option v-for="item of $d.BodyCoilChannelCount" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- 是否具备专用的PDFF脂肪定量序列CSE-MRI序列 -->
<el-form-item :label="$t('trials:equiptResearch:form:HasDedicatedPdfFatQuantificationSequence')"
v-if="EquipmentControlFieldList.includes('HasDedicatedPdfFatQuantificationSequence')"
prop="HasDedicatedPdfFatQuantificationSequence">
<el-select v-model="form.HasDedicatedPdfFatQuantificationSequence" style="width:100%"
@change="form.PdfFatQuantificationSequenceType = null, form.OtherSequenceSpecification = null">
<el-option v-for="item of $d.YesOrNo" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- PDFF脂肪定量序列 -->
<el-form-item :label="$t('trials:equiptResearch:form:PdfFatQuantificationSequenceType')"
prop="PdfFatQuantificationSequenceType"
v-if="form.HasDedicatedPdfFatQuantificationSequence && EquipmentControlFieldList.includes('PdfFatQuantificationSequenceType')">
<div style="display: flex;align-items: center;">
<el-select v-model="form.PdfFatQuantificationSequenceType" style="width:100%"
@change="form.OtherSequenceSpecification = null">
<el-option v-for="item of $d.PdfFatQuantificationSequenceType" :key="item.id" :label="item.label"
:value="item.value" />
</el-select>
<el-input placeholder="" v-model="form.OtherSequenceSpecification" style="margin-left: 10px;"
v-if="form.PdfFatQuantificationSequenceType == '-1'" clearable>
</el-input>
</div>
</el-form-item>
<!-- 是否包含 T2/R2 校正用于铁沉积校正 -->
<el-form-item :label="$t('trials:equiptResearch:form:HasT2R2Correction')" prop="HasT2R2Correction"
v-if="EquipmentControlFieldList.includes('HasT2R2Correction')">
<el-select v-model="form.HasT2R2Correction" style="width:100%">
<el-option v-for="item of $d.YesOrNo" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- 是否可完整导出 PDFF 参数图及全部原始 DICOM 数据 -->
<el-form-item :label="$t('trials:equiptResearch:form:CanFullyExportPdfParameterMapsAndRawDicom')"
prop="CanFullyExportPdfParameterMapsAndRawDicom"
v-if="EquipmentControlFieldList.includes('CanFullyExportPdfParameterMapsAndRawDicom')">
<el-select v-model="form.CanFullyExportPdfParameterMapsAndRawDicom" style="width:100%">
<el-option v-for="item of $d.YesOrNo" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- 备注 -->
<el-form-item :label="$t('trials:equiptResearch:form:remark')">
<el-form-item :label="$t('trials:equiptResearch:form:remark')" prop="Note"
v-if="EquipmentControlFieldList.includes('Note')">
<el-input v-model="form.Note" />
</el-form-item>
</div>
@@ -69,26 +127,98 @@ export default {
type: String,
default: ''
},
isShowParameters: {
EquipmentControlFieldList: {
type: Array,
default: () => {
return []
}
},
isPM: {
type: Boolean,
default: false
}
},
},
data() {
return {
form: {
Id: '',
EquipmentTypeId: '',
Parameters: '',
ManufacturerName: '',
ScannerType: '',
Note: '',
EquipmentTypeEnum: null,
OtherEquipmentType: null,
Parameters: null,
Note: null,
ManufacturerType: null,
ManufacturerName: null,
ScannerType: null,
MagneticFieldStrengthType: null,
BodyCoilChannelCount: null,
HasDedicatedPdfFatQuantificationSequence: null,
PdfFatQuantificationSequenceType: null,
OtherSequenceSpecification: null,
HasT2R2Correction: null,
CanFullyExportPdfParameterMapsAndRawDicom: null,
TrialSiteSurveyId: ''
},
rules: {
EquipmentTypeId: [
EquipmentTypeEnum: [
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] },
{
validator: (rule, value, callback) => {
if (this.form.EquipmentTypeEnum === -1 && !this.form.OtherEquipmentType) {
callback(this.$t('common:ruleMessage:specify'));
} else {
callback()
}
}, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change']
},
],
Parameters: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change'] }
],
// Note: [
// { required: true, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change'] }
// ],
ManufacturerType: [
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] },
{
validator: (rule, value, callback) => {
if (this.form.ManufacturerType === -1 && !this.form.ManufacturerName) {
callback(this.$t('common:ruleMessage:specify'));
} else {
callback()
}
}, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change']
},
],
ScannerType: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change'] }
],
MagneticFieldStrengthType: [
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] }
]
],
BodyCoilChannelCount: [
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] }
],
HasDedicatedPdfFatQuantificationSequence: [
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] }
],
PdfFatQuantificationSequenceType: [
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] },
{
validator: (rule, value, callback) => {
if (this.form.PdfFatQuantificationSequenceType === -1 && !this.form.OtherSequenceSpecification) {
callback(this.$t('common:ruleMessage:specify'));
} else {
callback()
}
}, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change']
},
],
HasT2R2Correction: [
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] }
],
CanFullyExportPdfParameterMapsAndRawDicom: [
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] }
],
},
btnLoading: false,
dictionaryList: {}
+92 -65
View File
@@ -3,62 +3,88 @@
<div class="header-wrapper">
<!-- 新增 -->
<el-button
v-if="state === 0 && userTypeEnumInt === 0 && !isHistory"
size="small"
type="primary"
@click="handleAdd"
>
v-if="((state === 0 && userTypeEnumInt === 0 && !isHistory) || isPM) && EquipmentControlFieldList.length > 0"
size="small" type="primary" @click="handleAdd">
{{ $t('common:button:add') }}
</el-button>
</div>
<el-table
v-loading="loading"
:data="list"
border
style="width: 100%"
>
<el-table v-loading="loading" :data="list" border style="width: 100%">
<!-- 扫描设备 -->
<el-table-column
prop="EquipmentType"
:label="$t('trials:equiptResearch:form:equipment')"
min-width="120"
show-overflow-tooltip
/>
<el-table-column v-if="EquipmentControlFieldList.includes('EquipmentTypeEnum')" prop="EquipmentTypeEnum"
:label="$t('trials:equiptResearch:form:equipment')" min-width="120" show-overflow-tooltip>
<template slot-scope="scope">
{{ scope.row.OtherEquipmentType ? scope.row.OtherEquipmentType :
$fd('SiteSurvey_ScanEquipmentType', scope.row.EquipmentTypeEnum) }}
</template>
</el-table-column>
<!-- 扫描参数 -->
<el-table-column
v-if="isShowParameters"
prop="Parameters"
:label="$t('trials:equiptResearch:form:param')"
min-width="100"
show-overflow-tooltip
/>
<el-table-column v-if="EquipmentControlFieldList.includes('Parameters')" prop="Parameters"
:label="$t('trials:equiptResearch:form:param')" min-width="100" show-overflow-tooltip />
<!-- 扫描仪器制造商名称 -->
<el-table-column
min-width="120"
prop="ManufacturerName"
:label="$t('trials:equiptResearch:form:manufacturer')"
show-overflow-tooltip
/>
<el-table-column v-if="EquipmentControlFieldList.includes('ManufacturerType')" min-width="120"
prop="ManufacturerType" :label="$t('trials:equiptResearch:form:manufacturer')" show-overflow-tooltip>
<template slot-scope="scope">
{{ scope.row.ManufacturerName ? scope.row.ManufacturerName : $fd('ManufacturerType',
scope.row.ManufacturerType) }}
</template>
</el-table-column>
<!-- 扫描仪型号 -->
<el-table-column
min-width="120"
prop="ScannerType"
:label="$t('trials:equiptResearch:form:model')"
show-overflow-tooltip
/>
<el-table-column v-if="EquipmentControlFieldList.includes('ScannerType')" min-width="120" prop="ScannerType"
:label="$t('trials:equiptResearch:form:model')" show-overflow-tooltip />
<!-- 磁场强度 -->
<el-table-column v-if="EquipmentControlFieldList.includes('MagneticFieldStrengthType')" min-width="120"
prop="MagneticFieldStrengthType" :label="$t('trials:equiptResearch:form:MagneticFieldStrengthType')"
show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('MagneticFieldStrengthType', scope.row.MagneticFieldStrengthType) }}
</template>
</el-table-column>
<!-- 体部线圈通道数 -->
<el-table-column v-if="EquipmentControlFieldList.includes('BodyCoilChannelCount')" min-width="120"
prop="BodyCoilChannelCount" :label="$t('trials:equiptResearch:form:BodyCoilChannelCount')"
show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('BodyCoilChannelCount', scope.row.BodyCoilChannelCount) }}
</template>
</el-table-column>
<!-- 是否具备专用的PDFF脂肪定量序列CSE-MRI序列 -->
<el-table-column v-if="EquipmentControlFieldList.includes('HasDedicatedPdfFatQuantificationSequence')"
min-width="120" prop="HasDedicatedPdfFatQuantificationSequence"
:label="$t('trials:equiptResearch:form:HasDedicatedPdfFatQuantificationSequence')" show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('YesOrNo', scope.row.HasDedicatedPdfFatQuantificationSequence) }}
</template>
</el-table-column>
<!-- 专用的PDFF脂肪定量序列类型 -->
<el-table-column v-if="EquipmentControlFieldList.includes('PdfFatQuantificationSequenceType')" min-width="120"
prop="PdfFatQuantificationSequenceType"
:label="$t('trials:equiptResearch:form:PdfFatQuantificationSequenceType')" show-overflow-tooltip><template
slot-scope="scope">
{{ scope.row.OtherSequenceSpecification ? scope.row.OtherSequenceSpecification :
$fd('PdfFatQuantificationSequenceType', scope.row.PdfFatQuantificationSequenceType) }}
</template>
</el-table-column>
<!-- 是否包含 T2/R2 校正用于铁沉积校正 -->
<el-table-column v-if="EquipmentControlFieldList.includes('HasT2R2Correction')" min-width="120"
prop="HasT2R2Correction" :label="$t('trials:equiptResearch:form:HasT2R2Correction')" show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('YesOrNo', scope.row.HasT2R2Correction) }}
</template>
</el-table-column>
<!-- 是否可完整导出 PDFF 参数图及全部原始 DICOM 数据 -->
<el-table-column v-if="EquipmentControlFieldList.includes('CanFullyExportPdfParameterMapsAndRawDicom')"
prop="CanFullyExportPdfParameterMapsAndRawDicom"
:label="$t('trials:equiptResearch:form:CanFullyExportPdfParameterMapsAndRawDicom')" min-width="120"
show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('YesOrNo', scope.row.CanFullyExportPdfParameterMapsAndRawDicom) }}
</template>
</el-table-column>
<!-- 备注 -->
<el-table-column
min-width="120"
prop="Note"
:label="$t('trials:equiptResearch:form:precautions')"
show-overflow-tooltip
/>
<el-table-column
v-if="state === 0 && userTypeEnumInt === 0 && !isHistory"
fixed="right"
:label="$t('common:action:action')"
width="100"
>
<el-table-column v-if="EquipmentControlFieldList.includes('Note')" min-width="120" prop="Note"
:label="$t('trials:equiptResearch:form:precautions')" show-overflow-tooltip />
<el-table-column v-if="(state === 0 && userTypeEnumInt === 0 && !isHistory) || isPM" fixed="right"
:label="$t('common:action:action')" width="100">
<template slot-scope="scope">
<!-- 编辑 -->
<el-button type="text" size="small" @click="handleEdit(scope.row)">
@@ -73,16 +99,10 @@
</el-table>
<!-- 新增/编辑人员信息 -->
<el-dialog
v-if="editVisible"
:visible.sync="editVisible"
:close-on-click-modal="false"
:title="title"
width="600px"
custom-class="base-dialog-wrapper"
:append-to-body="userTypeEnumInt !== 0"
>
<EquipmentForm :data="rowData" :trial-site-survey-equipment-type="trialSiteSurveyEquipmentType" :is-show-parameters="isShowParameters" @getList="getList" @close="closeDialog" />
<el-dialog v-if="editVisible" :visible.sync="editVisible" :close-on-click-modal="false" :title="title" width="800px"
custom-class="base-dialog-wrapper" :append-to-body="userTypeEnumInt !== 0">
<EquipmentForm :data="rowData" :trial-site-survey-equipment-type="trialSiteSurveyEquipmentType"
:EquipmentControlFieldList="EquipmentControlFieldList" @getList="getList" @close="closeDialog" />
</el-dialog>
</div>
@@ -102,7 +122,11 @@ export default {
isHistory: {
type: Boolean,
default: false
}
},
isPM: {
type: Boolean,
default: false
},
},
data() {
return {
@@ -115,7 +139,7 @@ export default {
state: null,
trialSiteSurveyId: '',
trialId: '',
isShowParameters: false
EquipmentControlFieldList: []
}
},
mounted() {
@@ -160,10 +184,13 @@ export default {
this.$message.success(this.$t('trials:equiptResearch:message:delSuccessfully'))
}
}).catch(() => { this.loading = false })
}).catch(() => {})
}).catch(() => { })
},
initList(TrialSiteEquipmentSurveyList, trialSiteSurvey, isShowParameters) {
this.isShowParameters = isShowParameters
initList(TrialSiteEquipmentSurveyList, trialSiteSurvey, EquipmentControlFieldList) {
this.EquipmentControlFieldList = []
EquipmentControlFieldList.forEach(item => {
this.EquipmentControlFieldList.push(item.FiledName)
})
this.list = TrialSiteEquipmentSurveyList
this.state = trialSiteSurvey.State
this.$forceUpdate()
@@ -176,8 +203,8 @@ export default {
}
</script>
<style lang="scss" scoped>
.equipment-wrapper{
.header-wrapper{
.equipment-wrapper {
.header-wrapper {
text-align: right;
margin-bottom: 10px;
}
@@ -1,18 +1,11 @@
<template>
<div class="particiapant-wrapper">
<div class="header-wrapper" />
<el-table
v-loading="loading"
:data="list"
border
style="width: 100%"
>
<el-table-column
prop="date"
:label="$t('trials:staffResearch:form:name')"
>
<el-table v-loading="loading" :data="list" border style="width: 100%">
<el-table-column prop="date" :label="$t('trials:staffResearch:form:name')">
<template slot-scope="scope">
<el-tooltip v-if="scope.row.ErrorMessage" class="item" effect="dark" :content="scope.row.ErrorMessage" placement="bottom">
<el-tooltip v-if="scope.row.ErrorMessage" class="item" effect="dark" :content="scope.row.ErrorMessage"
placement="bottom">
<i class="el-icon-warning" style="color:red" />
</el-tooltip>
@@ -29,20 +22,11 @@
</template>
</el-table-column> -->
<!-- 电话号码 -->
<el-table-column
prop="Phone"
:label="$t('trials:staffResearch:form:phone')"
/>
<el-table-column prop="Phone" :label="$t('trials:staffResearch:form:phone')" />
<!-- 邮箱 -->
<el-table-column
prop="Email"
:label="$t('trials:staffResearch:form:email')"
/>
<el-table-column prop="Email" :label="$t('trials:staffResearch:form:email')" />
<!-- 单位 -->
<el-table-column
prop="OrganizationName"
:label="$t('trials:staffResearch:form:organization')"
/>
<el-table-column prop="OrganizationName" :label="$t('trials:staffResearch:form:organization')" />
<!-- 是否生成账号 -->
<!-- <el-table-column
prop="IsGenerateAccount"
@@ -53,27 +37,20 @@
</template>
</el-table-column> -->
<!-- 用户类型 -->
<el-table-column
prop="UserType"
:label="$t('trials:staffResearch:form:userType')"
/>
<el-table-column prop="UserType" :label="$t('trials:staffResearch:form:userType')" />
<!-- 原状态 -->
<el-table-column
prop="IsHistoryUserOriginDeleted"
:label="$t('trials:staffResearch:form:originalState')"
>
<el-table-column prop="IsHistoryUserOriginDeleted" :label="$t('trials:staffResearch:form:originalState')">
<template slot-scope="scope">
<el-tag v-if="scope.row.IsHistoryUserOriginDeleted" type="info">{{ $fd('IsUserExitTrial', scope.row.IsHistoryUserOriginDeleted) }}</el-tag>
<el-tag v-if="scope.row.IsHistoryUserOriginDeleted" type="info">{{ $fd('IsUserExitTrial',
scope.row.IsHistoryUserOriginDeleted) }}</el-tag>
<el-tag v-else type="danger">{{ $fd('IsUserExitTrial', scope.row.IsHistoryUserOriginDeleted) }}</el-tag>
</template>
</el-table-column>
<!-- 更新状态 -->
<el-table-column
prop="IsHistoryUserDeleted"
:label="$t('trials:staffResearch:form:updateState')"
>
<el-table-column prop="IsHistoryUserDeleted" :label="$t('trials:staffResearch:form:updateState')">
<template slot-scope="scope">
<el-tag v-if="scope.row.IsHistoryUserDeleted" type="info">{{ $fd('IsUserExitTrial', scope.row.IsHistoryUserDeleted) }}</el-tag>
<el-tag v-if="scope.row.IsHistoryUserDeleted" type="info">{{ $fd('IsUserExitTrial',
scope.row.IsHistoryUserDeleted) }}</el-tag>
<el-tag v-else type="danger">{{ $fd('IsUserExitTrial', scope.row.IsHistoryUserDeleted) }}</el-tag>
</template>
</el-table-column>
@@ -88,14 +65,10 @@
<el-tag v-else type="danger">{{ $fd('IsJoin', scope.row.IsJoin) }}</el-tag>
</template>
</el-table-column> -->
<el-table-column
v-if="state === 0 && (userTypeEnumInt===0 ) && !isHistory"
fixed="right"
:label="$t('common:action:action')"
width="100"
>
<el-table-column v-if="(state === 0 && (userTypeEnumInt === 0) && !isHistory) || isPM" fixed="right"
:label="$t('common:action:action')" width="100">
<template slot-scope="scope">
<el-button v-if="userTypeEnumInt===0" type="text" size="small" @click="handleEdit(scope.row)">
<el-button v-if="userTypeEnumInt === 0 || isPM" type="text" size="small" @click="handleEdit(scope.row)">
{{ $t('common:button:edit') }}
</el-button>
</template>
@@ -103,16 +76,9 @@
</el-table>
<!-- 新增/编辑人员信息 -->
<el-dialog
v-if="editVisible"
:visible.sync="editVisible"
:close-on-click-modal="false"
:title="title"
width="900px"
custom-class="base-dialog-wrapper"
:append-to-body="userTypeEnumInt !== 0"
>
<HistoricalParticipantForm :state="state" :data="rowData" @getList="getList" @close="closeDialog" />
<el-dialog v-if="editVisible" :visible.sync="editVisible" :close-on-click-modal="false" :title="title" width="900px"
custom-class="base-dialog-wrapper" :append-to-body="userTypeEnumInt !== 0">
<HistoricalParticipantForm :state="state" :isPM="isPM" :data="rowData" @getList="getList" @close="closeDialog" />
</el-dialog>
</div>
@@ -128,7 +94,11 @@ export default {
isHistory: {
type: Boolean,
default: false
}
},
isPM: {
type: Boolean,
default: false
},
},
data() {
return {
@@ -199,8 +169,8 @@ export default {
}
</script>
<style lang="scss" scoped>
.particiapant-wrapper{
.header-wrapper{
.particiapant-wrapper {
.header-wrapper {
text-align: right;
margin-bottom: 10px;
}
@@ -3,43 +3,32 @@
<div class="base-dialog-body">
<!-- -->
<el-form-item :label="$t('trials:staffResearch:form:lastName')" prop="LastName">
<el-input v-model="form.LastName" :disabled="!(state === 0 && userTypeEnumInt === 0)" style="width: 200px" />
<el-input v-model="form.LastName" :disabled="(!(state === 0 && userTypeEnumInt === 0))&&!isPM" style="width: 200px" />
</el-form-item>
<!-- -->
<el-form-item :label="$t('trials:staffResearch:form:firstName')" prop="FirstName">
<el-input v-model="form.FirstName" :disabled="!(state === 0 && userTypeEnumInt === 0)" style="width: 200px" />
<el-input v-model="form.FirstName" :disabled="(!(state === 0 && userTypeEnumInt === 0))&&!isPM" style="width: 200px" />
</el-form-item>
<!-- 角色 -->
<el-form-item :label="$t('trials:staffResearch:form:role')" prop="TrialRoleCode">
<el-select
v-model="form.TrialRoleCode"
style="width: 200px"
:disabled="!(state === 0 && userTypeEnumInt === 0)"
@change="handleTrialRoleChange"
>
<el-option
v-for="item of $d.SiteSurvey_UserRoles"
:key="item.id"
:label="item.label"
:value="parseInt(item.value)"
/>
<el-select v-model="form.TrialRoleCode" style="width: 200px" :disabled="(!(state === 0 && userTypeEnumInt === 0))&&!isPM"
@change="handleTrialRoleChange">
<el-option v-for="item of $d.SiteSurvey_UserRoles" :key="item.id" :label="item.label"
:value="parseInt(item.value)" />
</el-select>
</el-form-item>
<!-- 电话号码 -->
<el-form-item :label="$t('trials:staffResearch:form:phone')" prop="Phone">
<el-input v-model="form.Phone" :disabled="!(state === 0 && userTypeEnumInt === 0)" style="width: 200px" />
<el-input v-model="form.Phone" :disabled="(!(state === 0 && userTypeEnumInt === 0))&&!isPM" style="width: 200px" />
</el-form-item>
<!-- 邮箱 -->
<el-form-item :label="$t('trials:staffResearch:form:email')" prop="Email">
<el-input v-model="form.Email" :disabled="!(state === 0 && userTypeEnumInt === 0)" style="width: 200px" />
<el-input v-model="form.Email" :disabled="(!(state === 0 && userTypeEnumInt === 0))&&!isPM" style="width: 200px" />
</el-form-item>
<!-- 单位 -->
<el-form-item :label="$t('trials:staffResearch:form:organization')" prop="OrganizationName">
<el-input
v-model="form.OrganizationName"
:disabled="!(state === 0 && userTypeEnumInt === 0)"
style="width: 200px"
/>
<el-input v-model="form.OrganizationName" :disabled="(!(state === 0 && userTypeEnumInt === 0))&&!isPM"
style="width: 200px" />
</el-form-item>
<!-- <el-row>
&lt;!&ndash; 是否生成账号 &ndash;&gt;
@@ -112,7 +101,11 @@ export default {
state: {
type: Number,
required: true
}
},
isPM: {
type: Boolean,
default: false
},
},
data() {
// var checkPhone = (rule, value, callback) => {
@@ -2,27 +2,16 @@
<div class="particiapant-wrapper">
<div class="header-wrapper">
<!-- 新增 -->
<el-button
v-if="state === 0 && userTypeEnumInt === 0 && !isHistory"
size="small"
type="primary"
@click="handleAdd"
>
<el-button v-if="(state === 0 && userTypeEnumInt === 0 && !isHistory) || isPM" size="small" type="primary"
@click="handleAdd">
{{ $t('common:button:add') }}
</el-button>
</div>
<el-table
v-loading="loading"
:data="list"
border
style="width: 100%"
>
<el-table-column
prop="date"
:label="$t('trials:staffResearch:form:name')"
>
<el-table v-loading="loading" :data="list" border style="width: 100%">
<el-table-column prop="date" :label="$t('trials:staffResearch:form:name')">
<template slot-scope="scope">
<el-tooltip v-if="scope.row.ErrorMessage" class="item" effect="dark" :content="scope.row.ErrorMessage" placement="bottom">
<el-tooltip v-if="scope.row.ErrorMessage" class="item" effect="dark" :content="scope.row.ErrorMessage"
placement="bottom">
<i class="el-icon-warning" style="color:red" />
</el-tooltip>
@@ -30,29 +19,17 @@
</template>
</el-table-column>
<!-- 姓名 -->
<el-table-column
prop="TrialRoleCode"
:label="$t('trials:staffResearch:form:role')"
>
<el-table-column prop="TrialRoleCode" :label="$t('trials:staffResearch:form:role')">
<template slot-scope="scope">
{{ $fd('SiteSurvey_UserRoles',scope.row.TrialRoleCode) }}
{{ $fd('SiteSurvey_UserRoles', scope.row.TrialRoleCode) }}
</template>
</el-table-column>
<!-- 电话号码 -->
<el-table-column
prop="Phone"
:label="$t('trials:staffResearch:form:phone')"
/>
<el-table-column prop="Phone" :label="$t('trials:staffResearch:form:phone')" />
<!-- 邮箱 -->
<el-table-column
prop="Email"
:label="$t('trials:staffResearch:form:email')"
/>
<el-table-column prop="Email" :label="$t('trials:staffResearch:form:email')" />
<!-- 单位 -->
<el-table-column
prop="OrganizationName"
:label="$t('trials:staffResearch:form:organization')"
/>
<el-table-column prop="OrganizationName" :label="$t('trials:staffResearch:form:organization')" />
<!-- 是否生成账号 -->
<!-- <el-table-column
prop="IsGenerateAccount"
@@ -68,33 +45,28 @@
:label="$t('trials:staffResearch:form:userType')"
/>-->
<!-- 状态 -->
<el-table-column
v-if="hasPermi(['role:pm','role:apm'])"
prop="State"
:label="$t('trials:staffResearch:form:status')"
>
<el-table-column v-if="hasPermi(['role:pm', 'role:apm'])" prop="State"
:label="$t('trials:staffResearch:form:status')">
<template slot-scope="scope">
<el-tag v-if="scope.row.IsJoin" type="info">{{ $fd('IsJoin', scope.row.IsJoin) }}</el-tag>
<el-tag v-else type="danger">{{ $fd('IsJoin', scope.row.IsJoin) }}</el-tag>
</template>
</el-table-column>
<!-- v-if="state !== 3 && (userTypeEnumInt===0 || hasPermi(['role:pm','role:apm']))" -->
<el-table-column
v-if="state === 0 && (userTypeEnumInt===0 ) && !isHistory"
fixed="right"
:label="$t('common:action:action')"
width="100"
>
<el-table-column v-if="(state === 0 && (userTypeEnumInt === 0) && !isHistory) || isPM" fixed="right"
:label="$t('common:action:action')" width="100">
<template slot-scope="scope">
<!-- 编辑 -->
<!-- <el-button type="text" size="small" :disabled="scope.row.IsGenerateSuccess || (state !== 0 && userTypeEnumInt===0) || (state !== 2 &&hasPermi(['role:pm']))" @click="handleEdit(scope.row)">
{{ $t('common:button:edit') }}
</el-button> -->
<el-button v-if="userTypeEnumInt===0" type="text" size="small" :disabled="state!==0 || scope.row.IsGenerateSuccess" @click="handleEdit(scope.row)">
<el-button v-if="userTypeEnumInt === 0 || isPM" type="text" size="small"
:disabled="(state !== 0 || scope.row.IsGenerateSuccess) && !isPM" @click="handleEdit(scope.row)">
{{ $t('common:button:edit') }}
</el-button>
<!-- 删除 -->
<el-button v-if="userTypeEnumInt===0" type="text" size="small" :disabled="state!==0 || scope.row.IsGenerateSuccess" @click="handleDelete(scope.row)">
<el-button v-if="userTypeEnumInt === 0 || isPM" type="text" size="small"
:disabled="(state !== 0 || scope.row.IsGenerateSuccess) && !isPM" @click="handleDelete(scope.row)">
{{ $t('common:button:delete') }}
</el-button>
</template>
@@ -102,16 +74,9 @@
</el-table>
<!-- 新增/编辑人员信息 -->
<el-dialog
v-if="editVisible"
:visible.sync="editVisible"
:close-on-click-modal="false"
:title="title"
width="900px"
custom-class="base-dialog-wrapper"
:append-to-body="userTypeEnumInt !== 0"
>
<ParticipantForm :state="state" :data="rowData" @getList="getList" @close="closeDialog" />
<el-dialog v-if="editVisible" :visible.sync="editVisible" :close-on-click-modal="false" :title="title" width="900px"
custom-class="base-dialog-wrapper" :append-to-body="userTypeEnumInt !== 0">
<ParticipantForm :state="state" :isPM="isPM" :data="rowData" @getList="getList" @close="closeDialog" />
</el-dialog>
</div>
@@ -127,7 +92,11 @@ export default {
isHistory: {
type: Boolean,
default: false
}
},
isPM: {
type: Boolean,
default: false
},
},
data() {
return {
@@ -197,7 +166,7 @@ export default {
this.$message.success(this.$t('trials:staffResearch:message:delSuccessfully'))
}
}).catch(() => { this.loading = false })
}).catch(() => {})
}).catch(() => { })
},
isExistIncorrect() {
return this.list.some(item => item.IsCorrect === false)
@@ -224,8 +193,8 @@ export default {
}
</script>
<style lang="scss" scoped>
.particiapant-wrapper{
.header-wrapper{
.particiapant-wrapper {
.header-wrapper {
text-align: right;
margin-bottom: 10px;
}
@@ -13,7 +13,8 @@
<div class="center-wrapper">
<!-- 基本信息 -->
<el-card shadow="hover">
<BaseInfo ref="baseResearchInfo" :is-history="true" />
<BaseInfo ref="baseResearchInfo" :is-history="true" :IsOnlyUploadFile="IsOnlyUploadFile"
:IsSupportUploadFile="IsSupportUploadFile" />
</el-card>
<!-- 人员调查 -->
@@ -139,7 +140,9 @@ export default {
btnLoading: false,
isFullscreen: false,
historyVisible: false,
siteSurveyNoteInfo: {}
siteSurveyNoteInfo: {},
IsSupportUploadFile: false,
IsOnlyUploadFile: false
}
},
mounted() {
@@ -154,6 +157,8 @@ export default {
this.loading = true
getSiteSurveyInfo(this.trialId, this.trialSiteSurveyId).then(res => {
if (res.Result) {
this.IsSupportUploadFile = res.Result.SiteSurveyFiledConfig.IsSupportUploadFile
this.IsOnlyUploadFile = res.Result.SiteSurveyFiledConfig.IsOnlyUploadFile
// this.trialSiteSurveyEquipmentType = res.Result.TrialInfo.TrialSiteSurveyEquipmentType
// this.trialSiteSurveyUserRoles = res.Result.TrialInfo.TrialSiteSurveyUserRoles
if (res.Result.SiteSurveyFiledConfig && res.Result.SiteSurveyFiledConfig.ModifyFiledList.length > 0) {
@@ -175,7 +180,7 @@ export default {
this.$refs['historicalParticipant'].initList(historicalArr, res.Result.TrialSiteSurvey)
this.$refs['researchParticipants'].initList(newArr, res.Result.TrialSiteSurvey)
// this.$refs['researchParticipants'].initList(res.Result.TrialSiteUserSurveyList, res.Result.TrialSiteSurvey)
this.$refs['researchEquipments'].initList(res.Result.TrialSiteEquipmentSurveyList, res.Result.TrialSiteSurvey, !(res.Result.SiteSurveyFiledConfig && res.Result.SiteSurveyFiledConfig.ModifyFiledList.length > 0))
this.$refs['researchEquipments'].initList(res.Result.TrialSiteEquipmentSurveyList, res.Result.TrialSiteSurvey, res.Result.SiteSurveyFiledConfig.EquipmentControlFieldList)
this.isExistIncorrect = res.Result.TrialSiteUserSurveyList.every(item => item.IsCorrect === false)
}
this.loading = false
+56 -39
View File
@@ -45,26 +45,27 @@
<div class="center-wrapper">
<!-- 基本信息 -->
<el-card shadow="hover">
<BaseInfo ref="baseResearchInfo" :isPreview="isPreview" />
<BaseInfo ref="baseResearchInfo" :isPreview="isPreview" :IsOnlyUploadFile="IsOnlyUploadFile"
:IsSupportUploadFile="IsSupportUploadFile" :isPM="isPM" />
</el-card>
<!-- 人员调查 -->
<el-card shadow="hover" class="mt10">
<!-- 历史人员 -->
<h4>{{ $t('trials:staffResearch:title:historicalStaff') }}</h4>
<HistoricalParticipant ref="historicalParticipant" :isPreview="isPreview" @refreshPage="refreshPage"
@getList="initPage" />
<HistoricalParticipant ref="historicalParticipant" :isPreview="isPreview" :isPM="isPM"
@refreshPage="refreshPage" @getList="initPage" />
<!-- <h4>{{ $t('trials:staffResearch:title:staff') }}</h4> -->
<!-- 新增人员 -->
<h4>{{ $t('trials:staffResearch:title:newStaff') }}</h4>
<ParticipantList ref="researchParticipants" :isPreview="isPreview" @refreshPage="refreshPage"
<ParticipantList ref="researchParticipants" :isPreview="isPreview" :isPM="isPM" @refreshPage="refreshPage"
@getList="initPage" />
</el-card>
<!-- 设备调研 -->
<el-card shadow="hover" class="mt10" v-if="!siteSurveyNoteInfo.IsCloseEquipmentSurvey">
<h4>{{ $t('trials:equiptResearch:title:equiptResearch') }}</h4>
<EquipmentList ref="researchEquipments" :isPreview="isPreview" />
<EquipmentList ref="researchEquipments" :isPreview="isPreview" :isPM="isPM" />
</el-card>
<!-- 其他信息调研 -->
@@ -146,7 +147,7 @@ import BaseInfo from './components/BaseInfo'
import HistoricalParticipant from './components/HistoricalParticipant'
import ParticipantList from './components/ParticipantList'
import EquipmentList from './components/EquipmentList'
import { mapMutations } from 'vuex'
import TopLang from './topLang'
import HistoricalRecord from './components/HistoricalRecord'
export default {
@@ -172,17 +173,36 @@ export default {
btnLoading: false,
isFullscreen: false,
historyVisible: false,
siteSurveyNoteInfo: {}
siteSurveyNoteInfo: {},
IsSupportUploadFile: false,
IsOnlyUploadFile: false
}
},
mounted() {
let lang = this.$route.query.lang
if (!lang) {
const language = navigator.language
lang = 'en'
if (language.includes("zh")) {
lang = 'zh'
}
}
this.$i18n.locale = lang
this.setLanguage(lang)
this.$updateDictionary()
this.trialSiteSurveyId = getQueryString('trialSiteSurveyId')
this.initPage()
if (zzSessionStorage.getItem('userTypeEnumInt')) {
this.userTypeEnumInt = zzSessionStorage.getItem('userTypeEnumInt') * 1
}
},
computed: {
isPM() {
return this.hasPermi(['role:pm', 'role:spm', 'role:cpm', 'role:apm']) && this.state !== 3
}
},
methods: {
...mapMutations({ setLanguage: 'lang/setLanguage' }),
//
initPage() {
this.loading = true
@@ -193,6 +213,8 @@ export default {
if (res.Result.SiteSurveyFiledConfig && res.Result.SiteSurveyFiledConfig.ModifyFiledList.length > 0) {
this.siteSurveyNoteInfo = res.Result.SiteSurveyFiledConfig.ModifyFiledList.find(i => i.NeedModifyFiled === 'SiteSurveyNote')
}
this.IsSupportUploadFile = res.Result.SiteSurveyFiledConfig.IsSupportUploadFile
this.IsOnlyUploadFile = res.Result.SiteSurveyFiledConfig.IsOnlyUploadFile
this.siteSurveyNoteInfo.IsCloseEquipmentSurvey = res.Result.SiteSurveyFiledConfig.IsCloseEquipmentSurvey
this.state = res.Result.TrialSiteSurvey.State
this.siteId = res.Result.TrialSiteSurvey.TrialSiteId
@@ -209,7 +231,7 @@ export default {
this.$refs['historicalParticipant'].initList(historicalArr, res.Result.TrialSiteSurvey)
this.$refs['researchParticipants'].initList(newArr, res.Result.TrialSiteSurvey)
this.$refs['researchEquipments'].initList(res.Result.TrialSiteEquipmentSurveyList, res.Result.TrialSiteSurvey, !(res.Result.SiteSurveyFiledConfig && res.Result.SiteSurveyFiledConfig.ModifyFiledList.length > 0))
this.$refs['researchEquipments'].initList(res.Result.TrialSiteEquipmentSurveyList, res.Result.TrialSiteSurvey, res.Result.SiteSurveyFiledConfig.EquipmentControlFieldList)
this.isExistIncorrect = res.Result.TrialSiteUserSurveyList.every(item => item.IsCorrect === false)
}
this.loading = false
@@ -219,7 +241,7 @@ export default {
handleSubmit(type) {
if (this.userTypeEnumInt === 0) {
this.loading = true
this.$refs['baseResearchInfo'].handleSave(true).then(res => {
this.$refs['baseResearchInfo'].handleSave(true, type === 'approve').then(res => {
this.loading = false
if (res) {
this.submit(type)
@@ -259,38 +281,33 @@ export default {
},
//
generateAccount() {
//
this.$confirm(this.$t('trials:researchForm:message:auditWarning'), {
type: 'warning',
distinguishCancelAndClose: true
}).then(() => {
// 1crc1SR
// var list = this.$refs['researchParticipants'].list
// var cIdx = list.findIndex(i => i.UserTypeEnum === 5)
// var sIdx = list.findIndex(i => i.UserTypeEnum === 9)
// if (cIdx === -1 || sIdx === -1) {
// this.$alert(this.$t('trials:researchForm:message:saveWarning2'))
// return
// }
this.loading = true
var param = {
TrialId: this.trialId,
TrialSiteSurveyId: this.trialSiteSurveyId,
LoginUrl: `${location.protocol}//${location.host}/login`,
BaseUrl: `${location.protocol}//${location.host}/login`,
RouteUrl: `${location.protocol}//${location.host}/email-recompose`
}
trialSurveySubmit(param).then((res) => {
this.loading = false
if (res.IsSuccess) {
this.initPage()
this.$emit('refreshPage')
this.$message.success(this.$t('common:message:approvedSuccessfully'))
this.$refs['baseResearchInfo'].handleSave(true, true).then(res => {
this.$confirm(this.$t('trials:researchForm:message:auditWarning'), {
type: 'warning',
distinguishCancelAndClose: true
}).then(() => {
if (res) {
var param = {
TrialId: this.trialId,
TrialSiteSurveyId: this.trialSiteSurveyId,
LoginUrl: `${location.protocol}//${location.host}/login`,
BaseUrl: `${location.protocol}//${location.host}/login`,
RouteUrl: `${location.protocol}//${location.host}/email-recompose`
}
this.loading = false
trialSurveySubmit(param).then((res) => {
this.loading = false
if (res.IsSuccess) {
this.initPage()
this.$emit('refreshPage')
this.$message.success(this.$t('common:message:approvedSuccessfully'))
}
}).catch(() => {
this.loading = false
})
}
}).catch(() => {
this.loading = false
})
}).catch(() => { })
})
},
//
handleReject() {
+126 -26
View File
@@ -1,10 +1,11 @@
<template>
<div class="question-login-wrapper">
<div class="box-wrapper">
<div v-if="verify"></div>
<div class="box-wrapper" v-else>
<h2 style="text-align:center;">
<!-- 中心调研表 -->
{{ $t('trials:researchForm:title:question') }}
<!-- <TopLang style="position: fixed;top: 40px;right: 40px" /> -->
<TopLang style="position: fixed;top: 40px;right: 40px" />
</h2>
<el-card shadow="hover">
<el-form ref="resetForm" v-loading="loading" :model="form" label-width="150px" style="width:80%;margin:0 auto;"
@@ -91,7 +92,10 @@ import { getUserMenuTree, getUserPermissions } from '@/api/user'
import store from '@/store'
import TopLang from './topLang'
import { mapGetters, mapMutations } from 'vuex'
import {
getLinkLinkExpirationTime,
getLinkVerificationCodeIsEffective
} from '@/api/trials'
export default {
components: { TopLang },
data() {
@@ -146,6 +150,7 @@ export default {
}
return {
trialId: '',
verify: true,
form: {
Sponsor: null, //
ResearchProgramNo: null, //
@@ -202,31 +207,116 @@ export default {
])
},
mounted() {
this.$i18n.locale = this.$route.query.lang
this.setLanguage(this.$route.query.lang)
let lang = this.$route.query.lang
if (!lang) {
const language = navigator.language
lang = 'en'
if (language.includes("zh")) {
lang = 'zh'
}
}
this.$i18n.locale = lang
this.setLanguage(lang)
this.$updateDictionary()
if (this.$route.query.trialId) {
this.trialId = this.$route.query.trialId
this.initPage()
}
if (this.$route.query.isUpload) {
this.isUpload = true
this.form.IsUpdate = true
let { email, oldEMail, trialSiteId } = this.$route.query
if (trialSiteId) this.form.TrialSiteId = trialSiteId
if (oldEMail) this.form.ReplaceUserEmailOrPhone = oldEMail
if (email && email !== 'null') {
this.form.EmailOrPhone = email
} else {
this.form.EmailOrPhone = oldEMail
}
if ((email && email !== 'null') || (oldEMail && oldEMail !== 'null')) {
this.isNeedUpload = false
}
}
this.getLinkTimeIsExpired()
},
methods: {
...mapMutations({ setLanguage: 'lang/setLanguage' }),
async getLinkTimeIsExpired() {
try {
let data = {
TrialId: this.$route.query.trialId,
}
let res = await getLinkLinkExpirationTime(data)
if (res.IsSuccess) {
if (res.Result.IsExpired) {
return this.$router.replace('/link_expired')
// return this.$confirm(this.$t("trials:researchForm:confirm:linkIsExpired"), '', {
// type: 'warning'
// })
// return this.IsExpired = true
}
this.customPrompt()
}
} catch (err) {
console.log(err)
}
},
async getLinkVerificationCodeIsEffective(value) {
try {
let data = {
TrialId: this.$route.query.trialId,
LinkVerificationCode: value
}
let res = await getLinkVerificationCodeIsEffective(data)
if (res.IsSuccess) {
if (!res.Result.IsEffective) {
return false
}
this.verify = false
if (this.$route.query.trialId) {
this.trialId = this.$route.query.trialId
this.initPage()
}
if (this.$route.query.isUpload) {
this.isUpload = true
this.form.IsUpdate = true
let { email, oldEMail, trialSiteId } = this.$route.query
if (trialSiteId) this.form.TrialSiteId = trialSiteId
if (oldEMail) this.form.ReplaceUserEmailOrPhone = oldEMail
if (email && email !== 'null') {
this.form.EmailOrPhone = email
} else {
this.form.EmailOrPhone = oldEMail
}
if ((email && email !== 'null') || (oldEMail && oldEMail !== 'null')) {
this.isNeedUpload = false
}
}
}
return true
} catch (err) {
return false
console.log(err)
}
},
async customPrompt(name) {
try {
const that = this
//
let message = this.$t('trials:researchForm:message:LinkVerificationCode')
const { value } = await this.$prompt(message, '', {
showClose: false,
cancelButtonText: this.$t('common:button:cancel'),
confirmButtonText: this.$t('trials:researchForm:button:saveLinkCode'),
showCancelButton: false,
closeOnClickModal: false,
closeOnPressEscape: false,
inputValue: name,
beforeClose: async (action, instance, done) => {
if (action === 'confirm') {
const value = instance.inputValue
if (!value) {
that.$message.error(this.$t('trials:researchForm:error:noValue'))
} else {
let flag = await this.getLinkVerificationCodeIsEffective(value)
if (!flag) {
that.$message.error(this.$t('trials:researchForm:confirm:isEffective'))
} else {
done()
}
}
} else {
done()
}
}
})
this.getLinkVerificationCodeIsEffective(value)
} catch (err) {
console.log(err)
return null
}
},
//
async initPage() {
this.loading = true
@@ -267,8 +357,18 @@ export default {
}
verifySendCode(param).then(async res => {
zzSessionStorage.clear()
this.$i18n.locale = this.$route.query.lang
this.setLanguage(this.$route.query.lang)
// this.$i18n.locale = this.$route.query.lang
// this.setLanguage(this.$route.query.lang)
let lang = this.$route.query.lang
if (!lang) {
const language = navigator.language
lang = 'en'
if (language.includes("zh")) {
lang = 'zh'
}
}
this.$i18n.locale = lang
this.setLanguage(lang)
store.dispatch('user/setToken', res.Result.Token)
zzSessionStorage.setItem('TokenKey', res.Result.Token)
zzSessionStorage.setItem('userId', res.Result.UserRoleId);
+3 -2
View File
@@ -26,7 +26,7 @@
<script>
import { mapGetters, mapMutations } from 'vuex'
import { changeURLStatic } from '@/utils/history.js'
export default {
name: 'TopLang',
@@ -44,7 +44,8 @@ export default {
this.$i18n.locale = lang
this.setLanguage(lang)
this.$updateDictionary()
// window.location.reload()
changeURLStatic('lang', lang)
window.location.reload()
}
}
}
+423
View File
@@ -0,0 +1,423 @@
<template>
<div class="systemConfig" v-loading="loading">
<div ref="leftContainer" class="left" style="padding: 20px;">
<el-form :inline="true" :model="form" :rules="rules" ref="systemConfigForm"
style="display: flex;align-items: center;justify-content: space-between;flex-wrap: wrap;">
<h3>{{ $t("system:config:BasicSystemConfig") }}</h3>
<el-form-item :label="$t('system:config:QcRiskControl')" style="width: 45%;">
<el-select v-model="form.BasicSystemConfig.QCRiskControl" placeholder="">
<el-option v-for="item in $d.YesOrNo" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item :label="$t('system:config:OpenUserComplexPassword')" style="width: 45%;">
<el-select v-model="form.BasicSystemConfig.OpenUserComplexPassword" placeholder="">
<el-option v-for="item in $d.YesOrNo" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item :label="$t('system:config:OpenSignDocumentBeforeWork')" style="width: 45%;">
<el-select v-model="form.BasicSystemConfig.OpenSignDocumentBeforeWork" placeholder="">
<el-option v-for="item in $d.YesOrNo" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item :label="$t('system:config:OpenTrialRelationDelete')" style="width: 45%;">
<el-select v-model="form.BasicSystemConfig.OpenTrialRelationDelete" placeholder="">
<el-option v-for="item in $d.YesOrNo" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item :label="$t('system:config:OpenLoginLimit')" style="width: 45%;">
<el-select v-model="form.BasicSystemConfig.OpenLoginLimit" placeholder="">
<el-option v-for="item in $d.YesOrNo" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item :label="$t('system:config:LoginMaxFailCount')" style="width: 45%;"
prop="BasicSystemConfig.LoginMaxFailCount">
<el-input v-model="form.BasicSystemConfig.LoginMaxFailCount" clearable type="number" />
</el-form-item>
<el-form-item :label="$t('system:config:LoginFailLockMinutes')" style="width: 45%;"
prop="BasicSystemConfig.LoginFailLockMinutes">
<el-input v-model="form.BasicSystemConfig.LoginFailLockMinutes" clearable type="number" />
</el-form-item>
<el-form-item :label="$t('system:config:AutoLoginOutMinutes')" style="width: 45%;"
prop="BasicSystemConfig.AutoLoginOutMinutes">
<el-input v-model="form.BasicSystemConfig.AutoLoginOutMinutes" clearable type="number" />
</el-form-item>
<el-form-item :label="$t('system:config:OpenLoginMFA')" style="width: 45%;">
<el-select v-model="form.BasicSystemConfig.OpenLoginMFA" placeholder="">
<el-option v-for="item in $d.YesOrNo" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item :label="$t('system:config:ContinuousReadingTimeMin')" style="width: 45%;"
prop="BasicSystemConfig.ContinuousReadingTimeMin">
<el-input v-model="form.BasicSystemConfig.ContinuousReadingTimeMin" clearable type="number" />
</el-form-item>
<el-form-item :label="$t('system:config:ReadingRestTimeMin')" style="width: 45%;"
prop="BasicSystemConfig.ReadingRestTimeMin">
<el-input v-model="form.BasicSystemConfig.ReadingRestTimeMin" clearable type="number" />
</el-form-item>
<el-form-item :label="$t('system:config:IsNeedChangePassWord')" style="width: 45%;">
<el-select v-model="form.BasicSystemConfig.IsNeedChangePassWord" placeholder="">
<el-option v-for="item in $d.YesOrNo" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item :label="$t('system:config:ChangePassWordDays')" style="width: 45%;"
prop="BasicSystemConfig.ChangePassWordDays">
<el-input v-model="form.BasicSystemConfig.ChangePassWordDays" clearable type="number" />
</el-form-item>
<el-form-item :label="$t('system:config:TemplateType')" style="width: 45%;"
prop="BasicSystemConfig.TemplateType">
<el-select v-model="form.BasicSystemConfig.TemplateType" placeholder="">
<el-option v-for="item in $d.SysTemplateType" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item :label="$t('system:config:ThirdPdfUrl')" style="width: 45%;" prop="BasicSystemConfig.ThirdPdfUrl">
<el-input v-model="form.BasicSystemConfig.ThirdPdfUrl" clearable />
</el-form-item>
<el-form-item :label="$t('system:config:UserMFAVerifyMinutes')" style="width: 45%;"
prop="BasicSystemConfig.UserMFAVerifyMinutes">
<el-input v-model="form.BasicSystemConfig.UserMFAVerifyMinutes" clearable type="number" />
</el-form-item>
<h3>{{ $t("system:config:SystemEmailSendConfig") }}</h3>
<el-form-item :label="$t('system:config:Host')" style="width: 45%;" prop="SystemEmailSendConfig.Host">
<el-input v-model="form.SystemEmailSendConfig.Host" clearable />
</el-form-item>
<el-form-item :label="$t('system:config:Port')" style="width: 45%;" prop="SystemEmailSendConfig.Port">
<el-input v-model="form.SystemEmailSendConfig.Port" clearable type="number" />
</el-form-item>
<el-form-item :label="$t('system:config:Imap')" style="width: 45%;" prop="SystemEmailSendConfig.Imap">
<el-input v-model="form.SystemEmailSendConfig.Imap" clearable />
</el-form-item>
<el-form-item :label="$t('system:config:ImapPort')" style="width: 45%;" prop="SystemEmailSendConfig.ImapPort">
<el-input v-model="form.SystemEmailSendConfig.ImapPort" clearable type="number" />
</el-form-item>
<el-form-item :label="$t('system:config:FromEmail')" style="width: 45%;" prop="SystemEmailSendConfig.FromEmail">
<el-input v-model="form.SystemEmailSendConfig.FromEmail" clearable />
</el-form-item>
<el-form-item :label="$t('system:config:FromName')" style="width: 45%;" prop="SystemEmailSendConfig.FromName">
<el-input v-model="form.SystemEmailSendConfig.FromName" clearable />
</el-form-item>
<el-form-item :label="$t('system:config:AuthorizationCode')" style="width: 45%;"
prop="SystemEmailSendConfig.AuthorizationCode">
<el-input v-model="form.SystemEmailSendConfig.AuthorizationCode" clearable />
</el-form-item>
<el-form-item :label="$t('system:config:SiteUrl')" style="width: 45%;" prop="SystemEmailSendConfig.SiteUrl">
<el-input v-model="form.SystemEmailSendConfig.SiteUrl" clearable />
</el-form-item>
<el-form-item :label="$t('system:config:SystemShortName')" style="width: 45%;"
prop="SystemEmailSendConfig.SystemShortName">
<el-input v-model="form.SystemEmailSendConfig.SystemShortName" clearable />
</el-form-item>
<el-form-item :label="$t('system:config:OrganizationName')" style="width: 45%;"
prop="SystemEmailSendConfig.OrganizationName">
<el-input v-model="form.SystemEmailSendConfig.OrganizationName" clearable />
</el-form-item>
<el-form-item :label="$t('system:config:OrganizationNameCN')" style="width: 45%;"
prop="SystemEmailSendConfig.OrganizationNameCN">
<el-input v-model="form.SystemEmailSendConfig.OrganizationNameCN" clearable />
</el-form-item>
<el-form-item :label="$t('system:config:PlatformName')" style="width: 45%;"
prop="SystemEmailSendConfig.PlatformName">
<el-input v-model="form.SystemEmailSendConfig.PlatformName" clearable />
</el-form-item>
<el-form-item :label="$t('system:config:PlatformNameCN')" style="width: 45%;"
prop="SystemEmailSendConfig.PlatformNameCN">
<el-input v-model="form.SystemEmailSendConfig.PlatformNameCN" clearable />
</el-form-item>
<el-form-item :label="$t('system:config:CompanyName')" style="width: 45%;"
prop="SystemEmailSendConfig.CompanyName">
<el-input v-model="form.SystemEmailSendConfig.CompanyName" clearable />
</el-form-item>
<el-form-item :label="$t('system:config:CompanyNameCN')" style="width: 45%;"
prop="SystemEmailSendConfig.CompanyNameCN">
<el-input v-model="form.SystemEmailSendConfig.CompanyNameCN" clearable />
</el-form-item>
<el-form-item :label="$t('system:config:CompanyShortName')" style="width: 45%;"
prop="SystemEmailSendConfig.CompanyShortName">
<el-input v-model="form.SystemEmailSendConfig.CompanyShortName" clearable />
</el-form-item>
<el-form-item :label="$t('system:config:CompanyShortNameCN')" style="width: 45%;"
prop="SystemEmailSendConfig.CompanyShortNameCN">
<el-input v-model="form.SystemEmailSendConfig.CompanyShortNameCN" clearable />
</el-form-item>
<el-form-item :label="$t('system:config:IsEnv_US')" style="width: 45%;">
<el-select v-model="form.SystemEmailSendConfig.IsEnv_US" placeholder="">
<el-option v-for="item in $d.YesOrNo" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item :label="$t('system:config:EmailRegexStr')" style="width: 45%;"
prop="SystemEmailSendConfig.EmailRegexStr">
<el-input v-model="form.SystemEmailSendConfig.EmailRegexStr" clearable />
</el-form-item>
<el-form-item style="width: 100%;text-align: right;">
<el-button type="primary" size="small" @click="handleSave">
{{ $t('common:button:save') }}
</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
import { getSystemBasicConfigInfo, getEmailConfigInfo, updateSystemBasicConfig, updateSystemEmailConfig } from '@/api/admin'
export default {
name: "systemConfig",
data() {
return {
rules: {
'BasicSystemConfig.LoginMaxFailCount': [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
{ type: 'number', message: this.$t('systemConfig:ruleMessage:mustNumber'), trigger: 'blur' },
],
'BasicSystemConfig.LoginFailLockMinutes': [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
{ type: 'number', message: this.$t('systemConfig:ruleMessage:mustNumber'), trigger: 'blur' },
],
'BasicSystemConfig.AutoLoginOutMinutes': [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
{ type: 'number', message: this.$t('systemConfig:ruleMessage:mustNumber'), trigger: 'blur' },
],
'BasicSystemConfig.ContinuousReadingTimeMin': [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
{ type: 'number', message: this.$t('systemConfig:ruleMessage:mustNumber'), trigger: 'blur' },
],
'BasicSystemConfig.ReadingRestTimeMin': [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
{ type: 'number', message: this.$t('systemConfig:ruleMessage:mustNumber'), trigger: 'blur' },
],
'BasicSystemConfig.ChangePassWordDays': [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
{ type: 'number', message: this.$t('systemConfig:ruleMessage:mustNumber'), trigger: 'blur' },
],
'BasicSystemConfig.TemplateType': [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
{ type: 'number', message: this.$t('systemConfig:ruleMessage:mustNumber'), trigger: 'blur' },
],
'BasicSystemConfig.UserMFAVerifyMinutes': [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
{ type: 'number', message: this.$t('systemConfig:ruleMessage:mustNumber'), trigger: 'blur' },
],
'BasicSystemConfig.ThirdPdfUrl': [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
],
'SystemEmailSendConfig.Host': [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
],
'SystemEmailSendConfig.Port': [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
{ type: 'number', message: this.$t('systemConfig:ruleMessage:mustNumber'), trigger: 'blur' },
],
'SystemEmailSendConfig.Imap': [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
],
'SystemEmailSendConfig.ImapPort': [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
{ type: 'number', message: this.$t('systemConfig:ruleMessage:mustNumber'), trigger: 'blur' },
],
'SystemEmailSendConfig.FromEmail': [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
],
'SystemEmailSendConfig.FromName': [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
],
'SystemEmailSendConfig.AuthorizationCode': [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
],
'SystemEmailSendConfig.SiteUrl': [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
],
'SystemEmailSendConfig.SystemShortName': [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
],
'SystemEmailSendConfig.OrganizationName': [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
],
'SystemEmailSendConfig.OrganizationNameCN': [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
],
'SystemEmailSendConfig.PlatformName': [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
],
'SystemEmailSendConfig.PlatformNameCN': [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
],
'SystemEmailSendConfig.CompanyName': [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
],
'SystemEmailSendConfig.CompanyNameCN': [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
],
'SystemEmailSendConfig.CompanyShortName': [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
],
'SystemEmailSendConfig.CompanyShortNameCN': [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
],
'SystemEmailSendConfig.EmailRegexStr': [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
],
},
form: {
"BasicSystemConfig": {
//
"QCRiskControl": true,
//
"OpenUserComplexPassword": false,
//
"OpenSignDocumentBeforeWork": false,
//
"OpenTrialRelationDelete": false,
//()
"OpenLoginLimit": false,
//
"LoginMaxFailCount": 5,
//()
"LoginFailLockMinutes": 1,
//()
"AutoLoginOutMinutes": 10,
// (MFA)
"OpenLoginMFA": false,
// ()
"ContinuousReadingTimeMin": 120,
//()
"ReadingRestTimeMin": 10,
//
"IsNeedChangePassWord": true,
// ()
"ChangePassWordDays": 90,
//1Elevate(LiLi)2Extensive()
"TemplateType": 2,
// PDF
"ThirdPdfUrl": "http://106.14.89.110:30088/api/v1/convert/file/pdf",
//MFA
"UserMFAVerifyMinutes": 1440
},
// ('
"SystemEmailSendConfig": {
//sMTP
"Host": "smtp.qiye.aliyun.com",
//SMTP
"Port": 465,
"Imap": "imap.qiye.aliyun.com",
"ImapPort": 993,
//
"FromEmail": "test@extimaging.com",
//
"FromName": "Test IRC Imaging System",
//SMTP
"AuthorizationCode": "SHzyyl2021",
//访
"SiteUrl": "http://irc.test.extimaging.com/login",
//-使
"SystemShortName": "IRC",
// -
"OrganizationName": "ExtImaging",
//
"OrganizationNameCN": "ExtImaging",
"PlatformName": "EICS",
"PlatformNameCN": "展影云平台",
//
"CompanyName": "Extensive Imaging",
// ,
"CompanyNameCN": "上海展影医疗科技有限公司",
//
"CompanyShortName": "Extensive Imaging",
//
"CompanyShortNameCN": "展影医疗",
// 便1i1liirc "
"IsEnv_US": false,
//
"EmailRegexStr": "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$",
//
// "CronEmailDefaultCulture": "zh-CN"
}
},
loading: false
}
},
mounted() {
this.getInfo()
},
methods: {
handleSave() {
this.loading = true
Promise.all([this.updateSystemBasicConfig(), this.updateSystemEmailConfig()]).then(res => {
this.loading = false
this.getInfo()
})
},
getInfo() {
this.loading = true
Promise.all([this.getSystemBasicConfigInfo(), this.getEmailConfigInfo()]).then(res => {
this.loading = false
})
},
async updateSystemBasicConfig() {
try {
let validate = await this.$refs.systemConfigForm.validate()
if (!validate) return false
let data = {}
Object.keys(this.form.BasicSystemConfig).forEach(key => {
data[key] = this.form.BasicSystemConfig[key]
})
let res = await updateSystemBasicConfig(data)
if (res.IsSuccess) {
}
} catch (err) {
console.log(err)
}
},
async updateSystemEmailConfig() {
try {
let validate = await this.$refs.systemConfigForm.validate()
if (!validate) return false
let data = {}
Object.keys(this.form.SystemEmailSendConfig).forEach(key => {
data[key] = this.form.SystemEmailSendConfig[key]
})
let res = await updateSystemEmailConfig(data)
if (res.IsSuccess) {
}
} catch (err) {
console.log(err)
}
},
async getSystemBasicConfigInfo() {
try {
let res = await getSystemBasicConfigInfo()
if (res.IsSuccess) {
Object.keys(this.form.BasicSystemConfig).forEach(key => {
this.form.BasicSystemConfig[key] = res.Result[key]
})
}
} catch (err) {
this.loading = false
console.log(err)
}
},
async getEmailConfigInfo() {
try {
let res = await getEmailConfigInfo()
if (res.IsSuccess) {
Object.keys(this.form.SystemEmailSendConfig).forEach(key => {
this.form.SystemEmailSendConfig[key] = res.Result[key]
})
}
} catch (err) {
this.loading = false
console.log(err)
}
}
}
}
</script>
<style lang="scss" scoped>
.systemConfig {
overflow-y: auto;
h3 {
width: 100%;
}
}
</style>
+76 -10
View File
@@ -4,30 +4,38 @@
<el-form ref="NoticeForm" :model="form" :rules="rules" label-width="180px" size="small">
<el-form-item :label="$t('system:notice:label:NoticeLevel')" prop="NoticeLevelEnum">
<el-select v-model="form.NoticeLevelEnum" clearable size="small">
<el-option v-for="item of dict.type.NoteLevel" :key="item.value" :label="item.label"
<el-option v-for="item of $d.NoteLevel" :key="item.value" :label="item.label"
:value="item.raw.Code * 1" />
</el-select>
</el-form-item>
<el-form-item :label="$t('system:notice:label:NoticeType')" prop="NoticeTypeEnum">
<el-select v-model="form.NoticeTypeEnum" clearable size="small">
<el-option v-for="item of dict.type.NoteType" :key="item.value" :label="item.label"
<el-option v-for="item of $d.NoteType" :key="item.value" :label="item.label"
:value="item.raw.Code * 1" />
</el-select>
</el-form-item>
<el-form-item :label="$t('system:notice:label:ApplicableProject')" prop="ApplicableProjectEnum">
<el-select v-model="form.ApplicableProjectEnum" clearable size="small">
<el-option v-for="item of dict.type.NoticeApplicableTrial" :key="item.value" :label="item.label"
<el-option v-for="item of $d.NoticeApplicableTrial" :key="item.value" :label="item.label"
:value="item.raw.Code * 1" />
</el-select>
</el-form-item>
<el-form-item :label="$t('system:notice:label:NoticeUserTypeIdList')" prop="NoticeUserTypeIdList">
<el-select v-model="form.NoticeUserTypeIdList" multiple clearable size="small">
<el-option v-for="item of roleList" :key="item.Id" :label="item.UserTypeShortName" :value="item.Id" />
</el-select>
<div class="role-checkbox-group">
<div class="role-checkbox-actions">
<el-button size="small" type="text" @click="handleSelectAllRoles">全选</el-button>
<el-button size="small" type="text" @click="handleClearRoles">清空</el-button>
</div>
<el-checkbox-group v-model="form.NoticeUserTypeIdList" class="role-checkbox-list">
<el-checkbox v-for="item of roleList" :key="item.Id" :label="item.Id">
{{ item.UserTypeShortName }}
</el-checkbox>
</el-checkbox-group>
</div>
</el-form-item>
<el-form-item :label="$t('system:notice:label:NoticeMode')" prop="NoticeModeEnum">
<el-select v-model="form.NoticeModeEnum" clearable size="small">
<el-option v-for="item of dict.type.NoticeMode" :key="item.value" :label="item.label"
<el-option v-for="item of $d.NoticeMode" :key="item.value" :label="item.label"
:value="item.raw.Code * 1" />
</el-select>
</el-form-item>
@@ -35,13 +43,14 @@
<el-date-picker v-model="form.StartDate" type="datetime" />
</el-form-item>
<el-form-item :label="$t('system:notice:label:EndDate')" prop="EndDate">
<el-date-picker v-model="form.EndDate" type="datetime" :default-time="'23:59:59'" />
<el-date-picker v-model="form.EndDate" type="datetime" :default-time="'23:59:59'"
@change="handleEndDateChange" />
</el-form-item>
<el-form-item v-if="!form.Id" :label="$t('system:notice:label:IsPush')" prop="IsPush">
<el-switch v-model="form.IsPush" :active-value="true" :inactive-value="false" />
</el-form-item>
<el-form-item :label="$t('system:notice:label:NoticeContent')" prop="NoticeContent">
<el-input v-model="form.NoticeContent" type="textarea" :autosize="{ minRows: 2, maxRows: 4 }" />
<el-input v-model="form.NoticeContent" type="textarea" :autosize="{ minRows: 4, maxRows: 8 }" />
</el-form-item>
<el-form-item :label="$t('system:notice:label:file')">
<el-upload class="upload-demo" action :before-upload="beforeUpload" :http-request="handleUploadFile"
@@ -106,12 +115,44 @@ export default {
NoticeUserTypeIdList: [{ required: true, message: 'Please specify', trigger: 'blur' }]
},
fileList: [],
model_cfg: { visible: false, showClose: true, width: '600px', title: '', appendToBody: true }
model_cfg: {
visible: false,
showClose: true,
width: '800px',
title: '',
appendToBody: true,
bodyStyle: {
maxHeight: '60vh',
overflowY: 'auto'
}
}
}
},
mounted() {
},
methods: {
handleSelectAllRoles() {
this.form.NoticeUserTypeIdList = this.roleList.map(item => item.Id)
},
handleClearRoles() {
this.form.NoticeUserTypeIdList = []
},
handleEndDateChange(value) {
this.form.EndDate = this.normalizeEndDate(value)
},
normalizeEndDate(value) {
if (!value) {
return value
}
const date = new Date(value)
if (Number.isNaN(date.getTime())) {
return value
}
if (date.getHours() === 0 && date.getMinutes() === 0 && date.getSeconds() === 0) {
date.setHours(23, 59, 59, 0)
}
return date
},
handleRemoveFile() {
this.form.FileName = null
this.form.Path = null
@@ -147,6 +188,7 @@ export default {
this.fileList[0] = { name: this.form.FileName, path: this.form.FilePath, fullPath: this.form.FullFilePath }
}
this.$set(this.form, 'NoticeUserTypeIdList', this.form.NoticeUserTypeList.map(v => v.Id))
this.form.EndDate = this.normalizeEndDate(this.form.EndDate)
} else {
this.form = formDataDefault()
}
@@ -177,3 +219,27 @@ export default {
}
}
</script>
<style scoped>
.role-checkbox-group {
display: flex;
flex-direction: column;
}
.role-checkbox-actions {
margin-bottom: 8px;
}
.role-checkbox-actions .el-button + .el-button {
margin-left: 12px;
}
.role-checkbox-list {
display: flex;
flex-wrap: wrap;
gap: 8px 16px;
}
.role-checkbox-list .el-checkbox {
margin-right: 0;
}
</style>
+36 -17
View File
@@ -57,14 +57,21 @@
<el-button type="primary" icon="el-icon-plus" @click="handleAdd">{{ $t('common:button:add') }}</el-button>
</el-form-item>
</el-form>
<el-table v-loading="loading" v-adaptive="{bottomOffset:45}" height="100" :data="list" class="table">
<el-table
v-loading="loading"
v-adaptive="{bottomOffset:45}"
height="100"
:data="list"
class="table"
@sort-change="handleSortChange"
>
<el-table-column type="index" width="50" />
<el-table-column :label="$t('system:notice:table:NoticeLevel')" prop="NoticeLevelEnum" min-width="120" show-overflow-tooltip>
<el-table-column :label="$t('system:notice:table:NoticeLevel')" prop="NoticeLevelEnum" min-width="120" show-overflow-tooltip sortable="custom">
<template slot-scope="scope">
{{ $d.NoteLevel.find(v => {return v.raw.Code * 1 === scope.row.NoticeLevelEnum}) ? $d.NoteLevel.find(v => {return v.raw.Code * 1 === scope.row.NoticeLevelEnum}).label : '' }}
</template>
</el-table-column>
<el-table-column :label="$t('system:notice:table:NoticeType')" prop="NoticeTypeEnum" min-width="100" show-overflow-tooltip>
<el-table-column :label="$t('system:notice:table:NoticeType')" prop="NoticeTypeEnum" min-width="100" show-overflow-tooltip sortable="custom">
<template slot-scope="scope">
{{ $d.NoteType.find(v => {return v.raw.Code * 1 === scope.row.NoticeTypeEnum}) ? $d.NoteType.find(v => {return v.raw.Code * 1 === scope.row.NoticeTypeEnum}).label : '' }}
</template>
@@ -74,46 +81,47 @@
{{ scope.row.NoticeContent }}
</template>
</el-table-column>
<el-table-column :label="$t('system:notice:table:NoticeState')" prop="NoticeStateEnum" min-width="100" show-overflow-tooltip>
<el-table-column :label="$t('system:notice:table:NoticeState')" prop="NoticeStateEnum" min-width="100" show-overflow-tooltip sortable="custom">
<template slot-scope="scope">
<el-tag :type="scope.row.ActualNoticeStateEnum === 0 ? 'info' : scope.row.ActualNoticeStateEnum === 1 ? 'success' : 'danger'">
{{ $d.NoticeState.find(v => {return v.raw.Code * 1 === scope.row.ActualNoticeStateEnum}) ? $d.NoticeState.find(v => {return v.raw.Code * 1 === scope.row.ActualNoticeStateEnum}).label : '' }}
</el-tag>
</template>
</el-table-column>
<el-table-column :label="$t('system:notice:table:ApplicableProject')" prop="ApplicableProjectEnum" min-width="100" show-overflow-tooltip>
<el-table-column :label="$t('system:notice:table:ApplicableProject')" prop="ApplicableProjectEnum" min-width="100" show-overflow-tooltip sortable="custom">
<template slot-scope="scope">
{{ $d.NoticeApplicableTrial.find(v => {return v.raw.Code * 1 === scope.row.ApplicableProjectEnum}) ? $d.NoticeApplicableTrial.find(v => {return v.raw.Code * 1 === scope.row.ApplicableProjectEnum}).label : '' }}
</template>
</el-table-column>
<el-table-column :label="$t('system:notice:table:NoticeUserTypeList')" prop="NoticeUserTypeList" min-width="80" show-overflow-tooltip>
<el-table-column :label="$t('system:notice:table:NoticeUserTypeList')" prop="NoticeUserTypeList" min-width="100" show-overflow-tooltip>
<template slot-scope="scope">
{{ scope.row.NoticeUserTypeList.map(v => v.UserTypeShortName).toString() }}
</template>
</el-table-column>
<el-table-column :label="$t('system:notice:table:NoticeMode')" prop="NoticeModeEnum" min-width="100" show-overflow-tooltip>
<el-table-column :label="$t('system:notice:table:NoticeMode')" prop="NoticeModeEnum" min-width="100" show-overflow-tooltip sortable="custom">
<template slot-scope="scope">
{{ dict.type.NoticeMode.find(v => {return v.raw.Code * 1 === scope.row.NoticeModeEnum}) ? $d.NoticeMode.find(v => {return v.raw.Code * 1 === scope.row.NoticeModeEnum}).label : '' }}
</template>
</el-table-column>
<el-table-column :label="$t('system:notice:table:StartDate')" prop="StartDate" min-width="240" show-overflow-tooltip>
<el-table-column :label="$t('system:notice:table:StartDate')" prop="StartDate" min-width="160" show-overflow-tooltip sortable="custom">
<template slot-scope="scope">
{{ scope.row.StartDate }}-{{ scope.row.EndDate }}
{{ scope.row.StartDate }}
</template>
</el-table-column>
<el-table-column :label="$t('system:notice:table:PublishUserName')" prop="PublishUserName" min-width="100" show-overflow-tooltip />
<el-table-column :label="$t('system:notice:table:PublishedTime')" prop="PublishedTime" min-width="160" show-overflow-tooltip />
<el-table-column :label="$t('system:notice:table:UpdateTime')" prop="UpdateTime" min-width="160" show-overflow-tooltip>
<el-table-column :label="$t('system:notice:table:UpdateTime')" prop="EndDate" min-width="160" show-overflow-tooltip sortable="custom">
<template slot-scope="scope">
{{ scope.row.StartDate ? scope.row.StartDate: scope.row.CreateTime }}
{{ scope.row.EndDate }}
</template>
</el-table-column>
<el-table-column :label="$t('system:notice:table:FileName')" fixed="right" prop="FileName" min-width="120" show-overflow-tooltip>
<el-table-column :label="$t('system:notice:table:PublishUserName')" prop="PublishUserName" min-width="100" show-overflow-tooltip sortable="custom"/>
<el-table-column :label="$t('system:notice:table:PublishedTime')" prop="PublishedTime" min-width="160" show-overflow-tooltip sortable="custom"/>
<el-table-column :label="$t('system:notice:table:FileName')" fixed="right" prop="FileName" min-width="120" show-overflow-tooltip sortable="custom">
<template slot-scope="scope">
<a :href="OSSclientConfig.basePath + scope.row.FullFilePath" target="_blank" style="color:#428bca">{{ scope.row.FileName }}</a>
</template>
</el-table-column>
<el-table-column :label="$t('common:action:action')" fixed="right" prop="UserTypeShortName" min-width="200" show-overflow-tooltip>
<el-table-column :label="$t('common:action:action')" fixed="right" prop="UserTypeShortName" min-width="120" show-overflow-tooltip>
<template slot-scope="scope">
<el-button
type="text"
@@ -146,8 +154,8 @@ import NoticeForm from './components/from'
import Pagination from '@/components/Pagination'
const searchDataDefault = () => {
return {
Asc: true,
SortField: '',
Asc: false,
SortField: 'PublishedTime',
NoticeContent: null,
FileName: null,
NoticeTypeEnum: null,
@@ -236,6 +244,17 @@ export default {
}).catch(() => {
this.loading = false
})
},
//
handleSortChange(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()
}
}
}
@@ -16,7 +16,7 @@
<el-form-item label="Shortname: " prop="UserTypeShortName">
<el-input v-model="form.UserTypeShortName" />
</el-form-item>
<el-form-item
<!-- <el-form-item
:label="$t('system:role:form:isInternal')"
prop="IsInternal"
>
@@ -28,7 +28,7 @@
>{{ item.label }}</el-radio
>
</el-radio-group>
</el-form-item>
</el-form-item> -->
<el-form-item :label="$t('system:role:form:IsEnable')" prop="IsEnable">
<el-switch
v-model="form.IsEnable"
+62 -149
View File
@@ -1,42 +1,21 @@
<template>
<el-form
ref="userForm"
size="small"
:model="user"
:rules="userFormRules"
label-width="150px"
style="width: 800px"
>
<el-form ref="userForm" size="small" :model="user" :rules="userFormRules" label-width="150px" style="width: 800px">
<el-card class="Basic" shadow="never" size="small">
<div slot="header" class="clearfix">
<span>{{ $t('system:userlist:title:Information') }}</span>
</div>
<el-form-item
v-if="user.UserCode"
:label="$t('system:userlist:table:S/N')"
prop="UserCode"
>
<el-form-item v-if="user.UserCode" :label="$t('system:userlist:table:S/N')" prop="UserCode">
<el-input v-model="user.UserCode" disabled />
</el-form-item>
<el-form-item
:label="$t('system:userlist:table:UserName')"
class="my_new_pwd"
prop="UserName"
>
<el-form-item :label="$t('system:userlist:table:UserName')" class="my_new_pwd" prop="UserName">
<el-input v-model="user.UserName" />
</el-form-item>
<el-form-item
:label="$t('system:userlist:table:LastName')"
prop="LastName"
>
<el-form-item :label="$t('system:userlist:table:LastName')" prop="LastName">
<el-input v-model="user.LastName" />
</el-form-item>
<el-form-item
:label="$t('system:userlist:table:FirstName')"
prop="FirstName"
>
<el-form-item :label="$t('system:userlist:table:FirstName')" prop="FirstName">
<el-input v-model="user.FirstName" />
</el-form-item>
<!-- <el-form-item :label="$t('system:userlist:table:Gender')" prop="Sex" style="margin-right:40px;">
@@ -51,105 +30,52 @@
<el-form-item :label="$t('system:userlist:table:Phone')" prop="Phone">
<el-input v-model="user.Phone" />
</el-form-item>
<el-form-item
v-if="type == 1"
:label="$t('system:userlist:table:Disable')"
>
<el-switch
v-model="user.Status"
:active-value="1"
:inactive-value="0"
/>
<el-form-item v-if="type == 1" :label="$t('system:userlist:table:Disable')">
<el-switch v-model="user.Status" :active-value="1" :inactive-value="0" />
</el-form-item>
<el-form-item :label="$t('system:userlist:table:IsTestUser')">
<el-radio-group v-model="user.IsTestUser">
<el-radio
v-for="item of $d.YesOrNo"
:key="item.id"
:label="item.value"
>{{ item.label }}</el-radio
>
<el-radio v-for="item of $d.YesOrNo" :key="item.id" :label="item.value">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
:label="$t('system:userlist:table:UserType')"
prop="Roles"
v-if="type === 0"
>
<el-form-item :label="$t('system:userlist:table:UserWorkLanguage')" prop="UserWorkLanguage">
<el-select v-model="user.UserWorkLanguage" style="width: 100%">
<el-option v-for="item of $d.UserWorkLanguage" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item :label="$t('system:userlist:table:UserType')" prop="Roles" v-if="type === 0">
<div style="display: flex; align-items: center">
<el-select
ref="userType"
v-model="user.Roles"
size="small"
placeholder="Please select"
multiple
style="width: 100%"
:disabled="user.CanEditUserType === false || type === 1"
@change="handleChange"
>
<el-select ref="userType" v-model="user.Roles" size="small" placeholder="Please select" multiple
style="width: 100%" :disabled="user.CanEditUserType === false || type === 1" @change="handleChange">
<template v-for="userType of userTypeOptions">
<el-option
v-if="![4, 6, 20].includes(userType.UserTypeEnum)"
:key="userType.Id"
:label="userType.UserType"
:value="userType.Id"
/>
<el-option v-if="![4, 6, 20].includes(userType.UserTypeEnum)" :key="userType.Id"
:label="userType.UserType" :value="userType.Id" />
</template>
</el-select>
</div>
</el-form-item>
<el-form-item
:label="$t('system:userlist:table:UserType')"
prop="userTypeId"
v-else
>
<el-form-item :label="$t('system:userlist:table:UserType')" prop="userTypeId" v-else>
<div style="display: flex; align-items: center">
<el-select
ref="userType"
v-model="Roles"
size="small"
placeholder="Please select"
multiple
style="width: 100%"
:disabled="user.CanEditUserType === false || type === 1"
@change="handleChange"
>
<el-select ref="userType" v-model="Roles" size="small" placeholder="Please select" multiple
style="width: 100%" :disabled="user.CanEditUserType === false || type === 1" @change="handleChange">
<template v-for="userType of userTypeOptions">
<el-option
v-if="![4, 6, 20].includes(userType.UserTypeEnum)"
:key="userType.Id"
:label="userType.UserType"
:value="userType.Id"
/>
<el-option v-if="![4, 6, 20].includes(userType.UserTypeEnum)" :key="userType.Id"
:label="userType.UserType" :value="userType.Id" />
</template>
</el-select>
<el-button
type="primary"
size="small"
style="margin-left: 5px"
v-if="type === 1"
@click.stop="openRoleList"
>
<el-button type="primary" size="small" style="margin-left: 5px" v-if="type === 1" @click.stop="openRoleList">
{{ $t('system:userlist:button:roles') }}
</el-button>
</div>
</el-form-item>
</el-card>
<el-card
class="Affiliation"
shadow="never"
style="margin-top: 10px"
size="small"
>
<el-card class="Affiliation" shadow="never" style="margin-top: 10px" size="small">
<div slot="header" class="clearfix">
<span>{{ $t('system:userlist:title:Affiliation') }}</span>
</div>
<el-form-item prop="IsZhiZhun">
<el-radio-group
v-model="user.IsZhiZhun"
@change="OrgnizationTypeChanged"
>
<el-radio-group v-model="user.IsZhiZhun" @change="OrgnizationTypeChanged">
<el-radio :label="true">{{
$t('system:userlist:title:Internal')
}}</el-radio>
@@ -158,43 +84,22 @@
}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
v-show="user.IsZhiZhun === false"
:label="$t('system:userlist:table:OrganizationName')"
>
<el-form-item v-show="user.IsZhiZhun === false" :label="$t('system:userlist:table:OrganizationName')">
<el-input v-model="user.OrganizationName" />
</el-form-item>
<el-form-item
:label="$t('system:userlist:table:Department')"
prop="DepartmentName"
>
<el-form-item :label="$t('system:userlist:table:Department')" prop="DepartmentName">
<el-input v-model="user.DepartmentName" />
</el-form-item>
<el-form-item
:label="$t('system:userlist:table:Position')"
prop="PositionName"
>
<el-form-item :label="$t('system:userlist:table:Position')" prop="PositionName">
<el-input v-model="user.PositionName" />
</el-form-item>
</el-card>
<el-form-item>
<el-button
type="primary"
size="small"
:disabled="isDisabled"
style="margin: 10px 15px"
@click="handleSave"
>{{ $t('common:button:save') }}</el-button
>
<el-button type="primary" size="small" :disabled="isDisabled" style="margin: 10px 15px" @click="handleSave">{{
$t('common:button:save') }}</el-button>
</el-form-item>
<roleList
v-if="visible"
:visible.sync="visible"
:userId="userId"
:list="userRoleList"
:userTypeOptions="userTypeOptions"
@getRoleList="getRoleList"
/>
<roleList v-if="visible" :visible.sync="visible" :userId="userId" :list="userRoleList"
:userTypeOptions="userTypeOptions" @getRoleList="getRoleList" />
</el-form>
</template>
<script>
@@ -273,7 +178,7 @@ export default {
updateUser(this.user)
.then((res) => {
this.isDisabled = false
this.$message.success('Updated successfully')
this.$message.success(this.$t("common:message:updatedSuccessfully"))
})
.catch(() => {
this.isDisabled = false
@@ -285,7 +190,7 @@ export default {
this.user.Id = res.Result.Id
this.user.UserCode = res.Result.UserCode
this.$emit('getUserId', res.Result.Id)
this.$message.success('Added successfully')
this.$message.success(this.$t("common:message:addedSuccessfully"))
this.$router.push({ path: '/system/user/list' })
})
.catch(() => {
@@ -365,11 +270,11 @@ export default {
callback(
lang === 'zh'
? new Error(
'1新建账号,用户名字符长度最小为4个字符,最大为16个字符,只可使用字母、数字、下划线;'
)
'新建账号,用户名字符长度最小为4个字符,最大为16个字符,只可使用字母、数字、下划线;'
)
: new Error(
'For a new account, the username must have:1) At least 4 characters;2) At most 16 characters;3)Only letters, numbers, and underscores are allowed.'
)
'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()
@@ -379,6 +284,7 @@ export default {
userRoleList: [],
Roles: [],
user: {
UserWorkLanguage: null,
Roles: [],
UserRoleList: [],
UserName: '',
@@ -399,59 +305,66 @@ export default {
UserName: [
{ required: true, validator: validatePassword, trigger: 'blur' },
],
UserWorkLanguage: [
{
required: true,
message: this.$t("common:ruleMessage:select"),
trigger: ['blur', 'change'],
},
],
Roles: [
{
required: true,
message: 'Please Select',
message: this.$t("common:ruleMessage:select"),
trigger: ['blur', 'change'],
},
],
IsZhiZhun: [
{
required: true,
message: 'Please Select',
message: this.$t("common:ruleMessage:select"),
trigger: ['blur', 'change'],
},
],
OrganizationName: [
{ required: true, message: 'Please specify', trigger: 'blur' },
{ required: true, message: this.$t("common:ruleMessage:specify"), trigger: 'blur' },
],
LastName: [
{ required: true, message: 'Please specify', trigger: 'blur' },
{ max: 50, message: 'The maximum length is 50' },
{ required: true, message: this.$t("common:ruleMessage:specify"), trigger: 'blur' },
{ max: 50, message: this.$t("trials:researchForm:formRule:maxLength") },
],
FirstName: [
{ required: true, message: 'Please specify', trigger: 'blur' },
{ max: 50, message: 'The maximum length is 50' },
{ required: true, message: this.$t("common:ruleMessage:specify"), trigger: 'blur' },
{ max: 50, message: this.$t("trials:researchForm:formRule:maxLength") },
],
Phone: [
{
max: 20,
min: 7,
message: 'The length is 7 to 20',
message: this.$t("system:userInfo:ruleMessage:length7to20"),
trigger: ['blur'],
},
],
EMail: [
{
required: true,
message: 'Please input the email address',
message: this.$t("common:ruleMessage:specify"),
trigger: 'blur',
},
{
// type: 'email',
pattern: new RegExp(this.$reg().EmailRegexStr),
message: 'Please input the correct email address',
message: this.$t("trials:site:ruleMessage:correctEmail"),
trigger: ['blur'],
},
{ max: 50, message: 'The maximum length is 50' },
{ max: 50, message: this.$t("trials:researchForm:formRule:maxLength") },
],
Sex: [{ required: true, message: 'Please specify', trigger: 'blur' }],
Sex: [{ required: true, message: this.$t("common:ruleMessage:specify"), trigger: 'blur' }],
Status: [
{ required: true, message: 'Please specify', trigger: 'blur' },
{ required: true, message: this.$t("common:ruleMessage:specify"), trigger: 'blur' },
],
DepartmentName: [{ max: 50, message: 'The maximum length is 50' }],
PositionName: [{ max: 50, message: 'The maximum length is 50' }],
DepartmentName: [{ max: 50, message: this.$t("trials:researchForm:formRule:maxLength") }],
PositionName: [{ max: 50, message: this.$t("trials:researchForm:formRule:maxLength") }],
},
userTypeOptions: [],
isDisabled: false,
+41 -47
View File
@@ -1,67 +1,42 @@
<template>
<box-content>
<div class="search" style="position: relative">
<SearchForm
:that="this"
:search-data="searchData"
:search-form="searchForm"
:search-handle="searchHandle"
@search="handleSearch"
@reset="handleReset"
@new="handleAddUser"
/>
<SearchForm :that="this" :search-data="searchData" :search-form="searchForm" :search-handle="searchHandle"
@search="handleSearch" @reset="handleReset" @new="handleAddUser" />
</div>
<base-table
v-loading="loading"
:columns="columns"
:list="users"
:search-data="searchData"
:total="total"
@getList="getList"
@editCb="handleEditUser"
@sendCb="addNewUserSendEmail"
@deleteCb="handleDeleteUser"
@sortByColumn="sortByColumn"
>
<base-table v-loading="loading" :columns="columns" :list="users" :search-data="searchData" :total="total"
@getList="getList" @editCb="handleEditUser" @sendCb="addNewUserSendEmail" @deleteCb="handleDeleteUser"
@sortByColumn="sortByColumn">
<!-- 选择自定义slot -->
<template slot="tip-slot" slot-scope="{ scope }">
<i
v-if="
diffTime(scope.row.LastLoginTime) >= 90 &&
diffTime(scope.row.LastChangePassWordTime) >= 90
"
class="el-icon-warning"
style="color: #f56c6c"
:title="$t('system:userlist:tip:overTimeAndoverTimePassWord')"
/>
<i
v-else-if="diffTime(scope.row.LastLoginTime) >= 90"
class="el-icon-warning"
style="color: #f56c6c"
:title="$t('system:userlist:tip:overTime')"
/>
<i
v-else-if="diffTime(scope.row.LastChangePassWordTime) >= 90"
class="el-icon-warning"
style="color: #f56c6c"
:title="$t('system:userlist:tip:overTimePassWord')"
/>
<i v-if="
diffTime(scope.row.LastLoginTime) >= 90 &&
diffTime(scope.row.LastChangePassWordTime) >= 90
" class="el-icon-warning" style="color: #f56c6c"
:title="$t('system:userlist:tip:overTimeAndoverTimePassWord')" />
<i v-else-if="diffTime(scope.row.LastLoginTime) >= 90" class="el-icon-warning" style="color: #f56c6c"
:title="$t('system:userlist:tip:overTime')" />
<i v-else-if="diffTime(scope.row.LastChangePassWordTime) >= 90" class="el-icon-warning" style="color: #f56c6c"
:title="$t('system:userlist:tip:overTimePassWord')" />
</template>
<template slot="genderSlot" slot-scope="{ scope }">
{{ scope.row.Sex ? 'Male' : 'Female' }}
</template>
<template slot="UserWorkLanguageSlot" slot-scope="{ scope }">
{{ $fd('UserWorkLanguage', scope.row.UserWorkLanguage) }}
</template>
<template slot="UserTypeSlot" slot-scope="{ scope }">
{{
Array.isArray(scope.row.UserRoleList) &&
scope.row.UserRoleList.length > 0
scope.row.UserRoleList.length > 0
? scope.row.UserRoleList.filter((item) => !item.IsUserRoleDisabled)
.map((item) => item.UserTypeShortName)
.join(', ')
.map((item) => item.UserTypeShortName)
.join(', ')
: ''
}}
</template>
<template slot="roleSlot" slot-scope="{ scope }">
{{ scope.row.RoleNameList.map((role) => role.RoleName).join(', ') }}
{{scope.row.RoleNameList.map((role) => role.RoleName).join(', ')}}
</template>
<template slot="isZhiZhunSlot" slot-scope="{ scope }">
{{
@@ -155,6 +130,14 @@ export default {
sortable: 'custom',
showOverflowTooltip: true,
},
{
// prop: 'UserWorkLanguage',
label: this.$t('system:userlist:table:UserWorkLanguage'),
minWidth: 120,
slot: 'UserWorkLanguageSlot',
sortable: 'custom',
showOverflowTooltip: true,
},
// {
// prop: 'Sex',
// label: this.$t('system:userlist:table:Gender'),
@@ -287,6 +270,15 @@ export default {
width: '120px',
placeholder: '',
},
{
type: 'Select',
label: this.$t('system:userlist:label:UserWorkLanguage'),
prop: 'UserWorkLanguage',
width: '100px',
options: [], //
props: { label: 'label', value: 'value' }, //
placeholder: '',
},
{
type: 'Input',
label: this.$t('system:userlist:label:Phone'),
@@ -476,8 +468,10 @@ export default {
//
async getInfo() {
const res = await getUserTypeList()
const index = this.findItemIndex('UserType')
let index = this.findItemIndex('UserType')
this.$set(this.searchForm[index], 'options', res.Result)
index = this.findItemIndex('UserWorkLanguage')
this.$set(this.searchForm[index], 'options', this.$d.UserWorkLanguage)
},
handleAddUser() {
this.$router.push({ path: '/system/user/add' })
+132 -12
View File
@@ -1,25 +1,145 @@
<template>
<div>
<!-- <label>行数: </label>
<input v-model.number="rows" type="number" min="1" max="3" />
<label>列数: </label>
<input v-model.number="cols" type="number" min="1" max="3" /> -->
<GridLayout :rows="rows" :cols="cols" />
<el-input v-model="input" placeholder="" clearable></el-input>
<el-input v-model="pageIndex" placeholder="" clearable></el-input>
<el-input v-model="pageSize" placeholder="" clearable></el-input>
<el-button type="primary" size="small" :disabled="!input" @click.stop="getTrialUnreadVisitList">下载</el-button>
</div>
</template>
<script>
import GridLayout from './GridLayout.vue';
import { downLoadFile } from '@/utils/stream.js'
import { getTrialUnreadVisitList, getExportSubjectVisitImageList } from '@/api/trials'
export default {
components: {
GridLayout,
},
// components: {
// GridLayout,
// },
data() {
return {
rows: 2,
cols: 2,
input: '08deab4a-f842-a7a4-0242-0a0001000000',
pageIndex: 1,
pageSize: 250
};
},
methods: {
async getTrialUnreadVisitList() {
let params = {
TrialId: this.input,
PageIndex: this.pageIndex,
PageSize: this.pageSize,
}
let res = await getTrialUnreadVisitList(params)
if (res.IsSuccess) {
let arr = res.Result
this.LoopDownload(arr, 0)
}
},
async LoopDownload(arr, index) {
if (index >= arr.length) return false
let res = await this.handleExportImage(false, true, arr[index])
console.log(res, arr[index], 'res')
index++
this.LoopDownload(arr, index)
},
async handleExportImage(IsKeyImage = false, IsExportReading = false, SubjectVisitId) {
try {
let data = {
TrialId: this.input,
IsKeyImage,
IsExportReading
}
data.SubjectVisitIdList = [SubjectVisitId]
// if (!IsKeyImage) {
// let confirm = await this.$confirm(this.$t('trials:imageSummary:confirm:space').replace('xxx', this.image_size.CheckImageSize))
// if (!confirm) return false
// }
let res = await getExportSubjectVisitImageList(data)
if (res.IsSuccess) {
return this.downLoad(IsKeyImage, res.Result, IsExportReading)
}
return false
} catch (err) {
return false
console.log(err)
}
},
//
async downLoad(IsKeyImage = false, row, IsExportReading = false) {
try {
let { files, name } = this.formatDownloadFile(IsKeyImage, row, IsExportReading)
return await downLoadFile(files, name, 'zip')
// }
} catch (err) {
return false
console.log(err)
}
},
//
formatDownloadFile(IsKeyImage = false, row, IsExportReading = false) {
let files = [],
name = `${this.$route.query.researchProgramNo}_${this.$t('trials:imageSummary:downloadname:dicom')}_${Date.now()}.zip`;
if (IsExportReading) {
name = `${row.VisitList[0].SubjectCode}_${row.VisitList[0].VisitName}.zip`;
}
if (!IsKeyImage) {
//ID/ID/访/Study ID_Study Date_Modality/
row.VisitList.forEach(visit => {
if (visit.StudyList && visit.StudyList.length > 0) {
visit.StudyList.forEach(study => {
let arr = []
study.SeriesList.forEach(item => {
if (!arr.includes(item.Modality)) {
arr.push(item.Modality)
}
})
let str = arr.join('_')
let time = study.StudyTime.split(' ')[0]
if (study.StudyDIRPath && !study.dirHas) {
study.dirHas = true
let obj = {
name: `${study.StudyCode}_${time}_${str}/DICOMDIR`,
url: this.OSSclientConfig.basePath + study.StudyDIRPath,
}
files.push(obj)
}
if (study.SeriesList && study.SeriesList.length > 0) {
study.SeriesList.forEach(serie => {
if (serie.InstanceList && serie.InstanceList.length > 0) {
serie.InstanceList.forEach(instance => {
let instanceArr = instance.Path.split("/")
let fileName = instance.FileName || instanceArr[instanceArr.length - 1]
let obj = {
name: `${study.StudyCode}_${time}_${str}/IMAGE/${fileName}`,
url: this.OSSclientConfig.basePath + instance.Path,
IsEncapsulated: instance.IsEncapsulated
}
files.push(obj)
})
}
})
}
})
}
if (visit.NoneDicomStudyList && visit.NoneDicomStudyList.length > 0) {
visit.NoneDicomStudyList.forEach(noneDicomStudy => {
if (noneDicomStudy.FileList && noneDicomStudy.FileList.length > 0) {
noneDicomStudy.FileList.forEach(file => {
let time = noneDicomStudy.ImageDate.split(' ')[0]
let obj = {
name: `${noneDicomStudy.StudyCode}_${time}_${noneDicomStudy.Modality}/${file.FileName}`,
url: this.OSSclientConfig.basePath + file.Path,
}
files.push(obj)
})
}
})
}
})
}
return { files, name }
},
}
};
</script>
@@ -0,0 +1,177 @@
<template>
<el-dialog
class="notice-detail-dialog"
:visible.sync="dialogVisible"
:title="title"
width="700px"
append-to-body
:close-on-click-modal="false"
@closed="handleClosed"
>
<div class="notice-detail-dialog__body">
<div v-if="publishedTime" class="notice-detail-dialog__meta">
<span class="notice-detail-dialog__time">{{ publishedTime }}</span>
</div>
<div class="notice-detail-dialog__content">
{{ content || '-' }}
</div>
<div v-if="filePath" class="notice-detail-dialog__attachment">
<div class="notice-detail-dialog__attachment-label">{{ $t('trials:notice:table:appendix') }}</div>
<div class="notice-detail-dialog__attachment-card">
<i class="el-icon-paperclip" />
<el-button size="small" type="text" @click="$emit('open-file', filePath)">
{{ fileName}}
</el-button>
</div>
</div>
</div>
<span v-if="showConfirmButton" slot="footer" class="notice-detail-dialog__footer">
<el-button size="small" @click="handleClose">{{ $t('common:button:close') }}</el-button>
<el-button
size="small"
type="primary"
:loading="confirmLoading"
@click="$emit('confirm')"
>
{{ $t('common:button:confirm') }}
</el-button>
</span>
</el-dialog>
</template>
<script>
export default {
name: 'NoticeDetailDialog',
props: {
visible: {
type: Boolean,
default: false,
},
title: {
type: String,
default: '',
},
content: {
type: String,
default: '',
},
publishedTime: {
type: String,
default: '',
},
fileName: {
type: String,
default: '',
},
filePath: {
type: String,
default: '',
},
showConfirmButton: {
type: Boolean,
default: false,
},
confirmLoading: {
type: Boolean,
default: false,
},
},
computed: {
dialogVisible: {
get() {
return this.visible
},
set(value) {
this.$emit('update:visible', value)
},
},
},
methods: {
handleClose() {
this.dialogVisible = false
},
handleClosed() {
this.$emit('closed')
},
},
}
</script>
<style lang="scss" scoped>
.notice-detail-dialog {
::v-deep .el-dialog__header {
padding: 18px 20px 10px;
border-bottom: 1px solid #ebeef5;
}
::v-deep .el-dialog__body {
padding: 0;
}
::v-deep .el-dialog__footer {
padding: 12px 20px 18px;
border-top: 1px solid #ebeef5;
}
}
.notice-detail-dialog__body {
max-height: 70vh;
overflow-y: auto;
padding: 18px 20px;
}
.notice-detail-dialog__meta {
margin-bottom: 12px;
color: #909399;
font-size: 13px;
}
.notice-detail-dialog__content {
padding: 14px 16px;
border: 1px solid #ebeef5;
border-radius: 8px;
background-color: #f8f9fb;
white-space: pre-wrap;
word-break: break-word;
line-height: 1.7;
color: #303133;
}
.notice-detail-dialog__attachment {
margin-top: 16px;
}
.notice-detail-dialog__attachment-label {
margin-bottom: 8px;
color: #606266;
font-size: 13px;
}
.notice-detail-dialog__attachment-card {
display: flex;
align-items: center;
padding: 12px 14px;
border: 1px solid #ebeef5;
border-radius: 8px;
background-color: #fff;
}
.notice-detail-dialog__attachment-card .el-icon-paperclip {
margin-right: 8px;
color: #409eff;
font-size: 16px;
}
.notice-detail-dialog__attachment-name {
flex: 1;
min-width: 0;
margin-right: 12px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: #303133;
}
</style>
@@ -7,16 +7,45 @@
{{ item.Content }}
</span>
</marquee>
<notice-detail-dialog
:visible.sync="detailDialog.visible"
:title="detailDialog.title"
:content="detailDialog.content"
:published-time="detailDialog.publishedTime"
:file-name="detailDialog.fileName"
:file-path="detailDialog.filePath"
:show-confirm-button="detailDialog.showConfirmButton"
:confirm-loading="detailDialog.confirmLoading"
@confirm="handleDetailConfirm"
@open-file="downLoadFile"
@closed="handleDetailClosed"
/>
</div>
</template>
<script>
import { setSystemNoticeHaveRead } from '@/api/global'
import { getBasicDataSelects } from '@/api/dictionary/dictionary'
import NoticeDetailDialog from '@/views/trials/components/NoticeDetailDialog'
const detailDialogDefault = () => {
return {
visible: false,
title: '',
content: '',
publishedTime: '',
fileName: '',
filePath: '',
showConfirmButton: false,
confirmLoading: false,
noticeId: null
}
}
export default {
name: 'NoticeMarquee',
components: { NoticeDetailDialog },
data() {
return {
noteType: []
noteType: [],
detailDialog: detailDialogDefault()
}
},
computed: {
@@ -40,25 +69,41 @@ export default {
if (i > -1) {
currentNoticeType = this.noteType[i].Value
}
const h = this.$createElement
this.$msgbox({
title: currentNoticeType,
message: h('span', null, item.Content),
beforeClose: (action, instance, done) => {
if (action === 'confirm') {
instance.confirmButtonLoading = true
setSystemNoticeHaveRead(item.Id).then(async res => {
if (res.IsSuccess) {
await this.$store.dispatch('global/getNoticeList')
}
instance.confirmButtonLoading = false
done()
}).catch(() => { instance.confirmButtonLoading = false })
} else {
done()
}
}
})
this.detailDialog = {
visible: true,
title: currentNoticeType,
content: item.Content || '',
publishedTime: item.PublishedTime || '',
fileName: item.FileName || '',
filePath: item.Path || '',
showConfirmButton: true,
confirmLoading: false,
noticeId: item.Id
}
},
async handleDetailConfirm() {
if (!this.detailDialog.noticeId) {
return
}
this.detailDialog.confirmLoading = true
try {
const res = await setSystemNoticeHaveRead(this.detailDialog.noticeId)
if (res.IsSuccess) {
await this.$store.dispatch('global/getNoticeList')
this.detailDialog.visible = false
}
} catch (err) {
console.log(err)
} finally {
this.detailDialog.confirmLoading = false
}
},
handleDetailClosed() {
this.detailDialog = detailDialogDefault()
},
downLoadFile(filePath) {
if (!filePath) return
window.open(this.OSSclientConfig.basePath + filePath, '_blank')
},
getDicData() {
getBasicDataSelects(['NoteType']).then(res => {
@@ -1,13 +1,8 @@
<template>
<div class="trials-navbar" style="position: relative">
<div class="leftMenu">
<img
v-if="NODE_ENV === 'usa'"
src="@/assets/title-logo.png"
alt=""
class="title-logo"
/>
<img v-else src="@/assets/system.png" alt="" style="width: 150px;height: 50px;margin-bottom: -7px;"/>
<img v-if="NODE_ENV === 'usa'" src="@/assets/title-logo.png" alt="" class="title-logo" />
<img v-else src="@/assets/system.png" alt="" style="width: 150px;height: 50px;margin-bottom: -7px;" />
<span style="white-space: nowrap" v-if="NODE_ENV !== 'usa'">
<!-- 中心影像系统EICS -->
{{ $t('trials:trials:title:eics') }}
@@ -17,53 +12,38 @@
<NoticeMarquee />
</div>
<div class="right-menu">
<el-menu
:default-active="activeIndex"
class="el-menu-demo"
mode="horizontal"
size="mini"
@select="handleSelect"
>
<el-menu-item
v-if="
!hasPermi([
'role:air',
'role:rpm',
'role:radmin',
'role:rcrc',
'role:rir',
])
"
index="1"
>
<el-menu :default-active="activeIndex" class="el-menu-demo" mode="horizontal" size="mini" @select="handleSelect">
<el-menu-item v-if="
!hasPermi([
'role:air',
'role:rpm',
'role:radmin',
'role:rcrc',
'role:rir',
])
" index="1">
<i class="el-icon-odometer" />
<!-- 工作台 -->
<span slot="title">{{ $t('trials:menuTitle:workbench') }}</span>
</el-menu-item>
<el-menu-item
v-if="!hasPermi(['role:zys', 'role:zyss', 'role:zybs'])"
index="2"
:disabled="TotalNeedSignSystemDocCount !== 0"
>
<el-menu-item v-if="!hasPermi(['role:zys', 'role:zyss', 'role:zybs'])" index="2"
:disabled="TotalNeedSignSystemDocCount !== 0">
<i class="el-icon-box" />
<!-- 我的项目 -->
<span slot="title">
{{ $t('trials:tab:trials') }}
</span>
</el-menu-item>
<el-menu-item
v-if="
!hasPermi([
'role:air',
'role:rpm',
'role:radmin',
'role:rcrc',
'role:rir',
'role:zys',
])
"
index="3"
>
<el-menu-item v-if="
!hasPermi([
'role:air',
'role:rpm',
'role:radmin',
'role:rcrc',
'role:rir',
'role:zys',
])
" index="3">
<i class="el-icon-chat-dot-square" />
<!-- 通知消息 -->
<span slot="title">{{ $t('trials:tab:notice') }}</span>
@@ -81,11 +61,8 @@
$t('trials:trials-myinfo:title:accountInfo')
}}</el-menu-item>
<!-- 管理后台 -->
<el-menu-item
v-if="hasPermi(['role:dev', 'role:oa', 'role:admin'])"
index="4-4"
>{{ $t('trials:trials-myinfo:title:system') }}</el-menu-item
>
<el-menu-item v-if="hasPermi(['role:dev', 'role:oa', 'role:admin'])" index="4-4">{{
$t('trials:trials-myinfo:title:system') }}</el-menu-item>
<!-- 切换角色 -->
<el-menu-item index="4-5" v-if="hasRole">{{
$t('trials:trials-myinfo:title:toggleRole')
@@ -96,18 +73,12 @@
}}</el-menu-item>
</el-submenu>
</el-menu>
<TopLang
v-if="
VUE_APP_OSS_CONFIG_REGION !== 'oss-us-west-1' && NODE_ENV !== 'usa'
"
/>
<TopLang v-if="
VUE_APP_OSS_CONFIG_REGION !== 'oss-us-west-1' && NODE_ENV !== 'usa'
" />
</div>
<toggleRole
v-if="toggleRoleVisible"
:visible.sync="toggleRoleVisible"
:loading="toggleRoleLoading"
@save="loginByRole"
/>
<toggleRole v-if="toggleRoleVisible" :visible.sync="toggleRoleVisible" :loading="toggleRoleLoading"
@save="loginByRole" />
</div>
</template>
@@ -313,37 +284,45 @@ export default {
overflow: hidden;
background: #fff;
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
.el-breadcrumb__item {
font-size: 16px;
}
.leftMenu {
display: flex;
align-items: flex-end;
padding-left: 20px;
padding-bottom: 20px;
img {
height: 28px;
margin-right: 10px;
}
.title-logo {
height: 36px;
}
span {
font-size: 20px;
position: relative;
bottom: -3px;
}
}
.right-menu,
.center-menu {
display: flex;
flex-direction: row;
justify-content: space-between;
height: 100%;
// line-height: 70px;
&:focus {
outline: none;
}
.right-menu-item {
display: inline-block;
padding: 0 8px;
@@ -361,9 +340,11 @@ export default {
}
}
}
.avatar-container {
display: inline-block;
margin-right: 200px;
.user-avatar {
margin-top: 5px;
width: 40px;
@@ -371,29 +352,34 @@ export default {
border-radius: 10px;
}
}
.dropdown-container {
cursor: pointer;
.el-icon-caret-bottom {
font-size: 12px;
}
}
.el-dropdown-link {
color: #999c9e;
cursor: pointer;
}
}
.right-menu {
.el-menu-item.is-active {
font-weight: bolder;
}
}
.center-menu {
width: calc(100% - 1200px);
}
}
</style>
<style>
.el-menu--horizontal > .my_info.el-submenu .el-submenu__icon-arrow {
.el-menu--horizontal>.my_info.el-submenu .el-submenu__icon-arrow {
margin-top: 0;
}
</style>
+17 -2
View File
@@ -17,11 +17,17 @@
</el-form-item>
<!-- Sponsor -->
<el-form-item :label="$t('trials:trials-list:table:sponsor')">
<el-select v-model="searchData.SponsorId" style="width: 150px" clearable>
<el-form-item :label="$t('trials:trials-list:table:sponsor')" v-if="IsZhiZhun">
<el-select v-model="searchData.SponsorId" style="width: 150px" clearable filterable>
<el-option v-for="item in sponsorList" :key="item.Id" :label="item.SponsorName" :value="item.Id" />
</el-select>
</el-form-item>
<!-- 状态 -->
<el-form-item :label="$t('trials:trials-list:table:TrialStatusStr')" v-if="IsZhiZhun">
<el-select v-model="searchData.TrialStatusStr" style="width: 150px" clearable>
<el-option v-for="item in $d.TrialStatusEnum" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- 阅片标准 -->
<el-form-item v-if="hasPermi(['role:ir'])" :label="$t('trials:trials-list:table:IR_ReadingCriterionList')">
<el-select v-model="searchData.CriterionType" style="width: 150px" clearable>
@@ -347,6 +353,7 @@ import {
import { getTrialList_Export } from '@/api/export'
import store from '@/store'
import { mapGetters } from 'vuex'
import { getUser } from '@/api/admin'
import BaseContainer from '@/components/BaseContainer'
import Pagination from '@/components/Pagination'
import TrialForm from './components/TrialForm'
@@ -372,6 +379,7 @@ const searchDataDefault = () => {
PageSize: 20,
Asc: false,
SortField: '',
TrialStatusStr: null,
CriterionType: null,
PM_EMail: null,
}
@@ -410,6 +418,7 @@ export default {
{ value: 'III' },
{ value: 'IV' },
],
IsZhiZhun: false,
expeditedOption: this.$d.TrialExpeditedState,
beginPickerOption: {
disabledDate: (time) => {
@@ -439,6 +448,7 @@ export default {
},
created() {
this.initPage()
this.getUser()
},
mounted() {
this.$EventBus.$on('reload', (data) => {
@@ -446,6 +456,11 @@ export default {
})
},
methods: {
getUser() {
getUser().then(res => {
this.IsZhiZhun = res.Result.IsZhiZhun
})
},
initPage() {
this.getList()
store.dispatch('global/getSponsorList')
+28 -56
View File
@@ -5,77 +5,42 @@
<!-- 用户基本信息 -->
{{ $t("trials:trials-myinfo:title:basicInfo") }}
</div>
<el-form
ref="userForm"
label-position="right"
:model="user"
:rules="userFormRules"
label-width="120px"
>
<el-form ref="userForm" label-position="right" :model="user" :rules="userFormRules" label-width="120px">
<el-form-item v-if="user.Code" label="ID: " prop="Code">
<el-input v-model="user.Code" disabled />
</el-form-item>
<!-- -->
<el-form-item
:disabled="user.UserTypeEnum === 8"
:label="$t('trials:trials-myinfo:form:surname')"
prop="LastName"
>
<el-input
v-model="user.LastName"
:placeholder="$t('trials:trials-myinfo:form:surname')"
/>
<el-form-item :disabled="user.UserTypeEnum === 8" :label="$t('trials:trials-myinfo:form:surname')"
prop="LastName">
<el-input v-model="user.LastName" :placeholder="$t('trials:trials-myinfo:form:surname')" />
</el-form-item>
<!-- -->
<el-form-item
:disabled="user.UserTypeEnum === 8"
:label="$t('trials:trials-myinfo:form:givenname')"
prop="FirstName"
>
<el-input
v-model="user.FirstName"
:placeholder="$t('trials:trials-myinfo:form:givenname')"
/>
<el-form-item :disabled="user.UserTypeEnum === 8" :label="$t('trials:trials-myinfo:form:givenname')"
prop="FirstName">
<el-input v-model="user.FirstName" :placeholder="$t('trials:trials-myinfo:form:givenname')" />
</el-form-item>
<!-- 单位 -->
<el-form-item
:label="$t('trials:trials-myinfo:form:organization')"
prop="OrganizationName"
>
<el-input
:disabled="user.IsZhiZhun"
v-model="user.OrganizationName"
:placeholder="$t('trials:trials-myinfo:form:organization')"
/>
<el-form-item :label="$t('trials:trials-myinfo:form:organization')" prop="OrganizationName">
<el-input :disabled="user.IsZhiZhun" v-model="user.OrganizationName"
:placeholder="$t('trials:trials-myinfo:form:organization')" />
</el-form-item>
<!-- 部门 -->
<el-form-item
:label="$t('trials:trials-myinfo:form:department')"
prop="DepartmentName"
>
<el-input
v-model="user.DepartmentName"
:placeholder="$t('trials:trials-myinfo:form:organization')"
/>
<el-form-item :label="$t('trials:trials-myinfo:form:department')" prop="DepartmentName">
<el-input v-model="user.DepartmentName" :placeholder="$t('trials:trials-myinfo:form:organization')" />
</el-form-item>
<!-- 职位 -->
<el-form-item
:label="$t('trials:trials-myinfo:form:position')"
prop="PositionName"
>
<el-input
v-model="user.PositionName"
:placeholder="$t('trials:trials-myinfo:form:position')"
/>
<el-form-item :label="$t('trials:trials-myinfo:form:position')" prop="PositionName">
<el-input v-model="user.PositionName" :placeholder="$t('trials:trials-myinfo:form:position')" />
</el-form-item>
<!-- 工作语言 -->
<el-form-item :label="$t('trials:trials-myinfo:form:UserWorkLanguage')" prop="UserWorkLanguage">
<el-select v-model="user.UserWorkLanguage" style="width: 100%">
<el-option v-for="item of $d.UserWorkLanguage" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-form>
<!-- 保存 -->
<el-button
class="trial-info-btn"
type="primary"
size="small"
@click="handleSave"
>
<el-button class="trial-info-btn" type="primary" size="small" @click="handleSave">
{{ $t("trials:trials-myinfo:button:save") }}
</el-button>
</div>
@@ -102,6 +67,13 @@ export default {
data() {
return {
userFormRules: {
UserWorkLanguage: [
{
required: true,
message: this.$t("common:ruleMessage:select"),
trigger: ["blur", "change"],
},
],
UserName: [
{
required: true,
+65 -31
View File
@@ -172,6 +172,19 @@
:limit.sync="searchData.PageSize"
@pagination="getList"
/>
<notice-detail-dialog
:visible.sync="detailDialog.visible"
:title="detailDialog.title"
:content="detailDialog.content"
:published-time="detailDialog.publishedTime"
:file-name="detailDialog.fileName"
:file-path="detailDialog.filePath"
:show-confirm-button="detailDialog.showConfirmButton"
:confirm-loading="detailDialog.confirmLoading"
@confirm="handleDetailConfirm"
@open-file="downLoadFile"
@closed="handleDetailClosed"
/>
</template>
</BaseContainer>
</template>
@@ -179,6 +192,7 @@
import { getUserSystemNoticeList, setSystemNoticeHaveRead } from '@/api/global'
import BaseContainer from '@/components/BaseContainer'
import Pagination from '@/components/Pagination'
import NoticeDetailDialog from '@/views/trials/components/NoticeDetailDialog'
const searchDataDefault = () => {
return {
NoticeContent: '',
@@ -193,9 +207,22 @@ const searchDataDefault = () => {
PageSize: 20,
}
}
const detailDialogDefault = () => {
return {
visible: false,
title: '',
content: '',
publishedTime: '',
fileName: '',
filePath: '',
showConfirmButton: false,
confirmLoading: false,
noticeId: null,
}
}
export default {
name: 'TrialsNotice',
components: { BaseContainer, Pagination },
components: { BaseContainer, Pagination, NoticeDetailDialog },
dicts: ['NoteType', 'NoticeState', 'NoticeMode'],
data() {
return {
@@ -203,6 +230,7 @@ export default {
list: [],
total: 0,
loading: false,
detailDialog: detailDialogDefault(),
}
},
mounted() {
@@ -222,38 +250,39 @@ export default {
})
},
showDetail(row) {
var currentNoticeType = this.$fd('NoteType', row.NoticeTypeEnum)
if (row.IsRead || row.ActualNoticeStateEnum !== 1) {
this.$alert(row.NoticeContent, currentNoticeType, {
showConfirmButton: false,
})
} else {
const h = this.$createElement
this.$msgbox({
title: currentNoticeType,
message: h('span', null, row.NoticeContent),
beforeClose: (action, instance, done) => {
if (action === 'confirm') {
instance.confirmButtonLoading = true
setSystemNoticeHaveRead(row.Id)
.then(async (res) => {
if (res.IsSuccess) {
await this.$store.dispatch('global/getNoticeList')
this.getList()
}
instance.confirmButtonLoading = false
done()
})
.catch(() => {
instance.confirmButtonLoading = false
})
} else {
done()
}
},
})
this.detailDialog = {
visible: true,
title: this.$fd('NoteType', row.NoticeTypeEnum),
content: row.NoticeContent || '',
publishedTime: row.PublishedTime || '',
fileName: row.FileName || '',
filePath: row.Path || '',
showConfirmButton: !row.IsRead && row.ActualNoticeStateEnum === 1,
confirmLoading: false,
noticeId: row.Id
}
},
async handleDetailConfirm() {
if (!this.detailDialog.noticeId) {
return
}
this.detailDialog.confirmLoading = true
try {
const res = await setSystemNoticeHaveRead(this.detailDialog.noticeId)
if (res.IsSuccess) {
await this.$store.dispatch('global/getNoticeList')
this.detailDialog.visible = false
this.getList()
}
} catch (err) {
console.log(err)
} finally {
this.detailDialog.confirmLoading = false
}
},
handleDetailClosed() {
this.detailDialog = detailDialogDefault()
},
downLoadFile(filePath) {
if (!filePath) return
window.open(this.OSSclientConfig.basePath + filePath, '_blank')
@@ -281,4 +310,9 @@ export default {
}
</script>
<style lang="scss" scoped>
.notice-content-text {
white-space: pre-wrap;
word-break: break-word;
line-height: 1.5;
}
</style>
@@ -482,7 +482,7 @@ export default {
}
},
handleSelectTable(row) {
return row.DoctorTrialState !== 1 && row.ResumeStatus === 1 && row.AcceptingNewTrial && !row.IsVirtual && !row.IsVacation && row.CooperateStatus === 1
return row.DoctorTrialState !== 1 && row.ResumeStatus === 1 && row.AcceptingNewTrial && ((!row.IsVirtual && this.$store.state.trials.config.TrialType !== 0) || (row.IsVirtual && this.$store.state.trials.config.TrialType === 0)) && !row.IsVacation && row.CooperateStatus === 1
},
handleDetail(row) {
const { href } = this.$router.resolve({
@@ -665,7 +665,7 @@ export default {
async audit(row) {
try {
this.rowData = Object.assign({}, row)
this.rowData.title = `${row.ApplicantName} (${this.$fd('RequestReReadingType', row.RequestReReadingType)}) ${this.$t('trials:spmAudit:title:imageBack').replace('xxx', row.OriginalReReadingTask.SubjectCode).replace('yyy', row.OriginalReReadingTask.TaskName)}`
this.rowData.title = `${row.ApplicantName} (${this.$fd('RequestReReadingType', row.RequestReReadingType)}) ${this.$t('trials:spmAudit:title:imageBack').replace('xxx', row.OriginalReReadingTask.SubjectCode).replace('yyy', row.OriginalReReadingTask.TaskBlindName)}`
Object.keys(this.form).forEach(key => {
this.form[key] = null
})
@@ -1,6 +1,25 @@
<template>
<div class="ImageManual">
<div class="config">
<div style="margin-bottom: 20px;font-weight: bold;">
{{ $t("trials:researchRecord:ImageManual:uploadFile") }}
</div>
<el-form size="small" :model="uploadForm" style="width:80%">
<el-form-item :label="$t('trials:researchRecord:ImageManual:IsSupportUploadFile')">
<el-radio-group v-model="uploadForm.IsSupportUploadFile"
@input="uploadForm.IsOnlyUploadFile = false">
<el-radio :label="item.value" v-for="item in $d.YesOrNo" :key="item.id">{{ item.label
}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('trials:researchRecord:ImageManual:IsOnlyUploadFile')"
v-if="uploadForm.IsSupportUploadFile">
<el-radio-group v-model="uploadForm.IsOnlyUploadFile">
<el-radio :label="item.value" v-for="item in $d.YesOrNo" :key="item.id">{{ item.label
}}</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<div style="margin-bottom: 20px;font-weight: bold;">
{{ $t("trials:researchRecord:ImageManual:BasicQuestion") }}
</div>
@@ -27,23 +46,110 @@
<span v-html="$t('trials:researchForm:form:isFollowStudyParam')"></span>
</el-checkbox>
</el-form-item>
<el-form-item>
<el-checkbox v-model="form.IsRoutineMRIPDEE">
<span v-html="$t('trials:researchForm:form:IsRoutineMRIPDEE')"></span>
</el-checkbox>
</el-form-item>
<el-form-item>
<el-checkbox v-model="form.IsMRIPDFF" @change="(val) => handleChange(val, 'IsMRIPDFF')">
<span v-html="$t('trials:researchForm:form:IsMRIPDFF')"></span>
</el-checkbox>
</el-form-item>
<el-form-item>
<el-checkbox v-model="form.IsAuthorize" @change="(val) => handleChange(val, 'IsAuthorize')">
<span v-html="$t('trials:researchForm:form:IsAuthorize')"></span>
</el-checkbox>
</el-form-item>
<el-form-item>
<el-checkbox v-model="form.ISStrictManualBurnFlag"
@change="(val) => handleChange(val, 'ISStrictManualBurnFlag')">
<span v-html="$t('trials:researchForm:form:ISStrictManualBurnFlag')"></span>
</el-checkbox>
</el-form-item>
</el-form>
<div style="margin-bottom: 20px;font-weight: bold;">
{{ $t("trials:researchRecord:ImageManual:TableConfiguration") }}
</div>
<el-form size="small" :model="form" style="width:80%">
<el-form-item :label="$t('trials:researchRecord:ImageManual:Equipment')">
<el-radio-group v-model="form.IsCloseEquipmentSurvey">
<el-radio-group v-model="form.IsCloseEquipmentSurvey" @input="handleInput">
<el-radio :label="item.value" v-for="item in $d.YesOrNo" :key="item.id">{{ item.label
}}</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<div style="margin-bottom: 20px;font-weight: bold;" v-if="form.IsCloseEquipmentSurvey">
{{ $t("trials:researchRecord:ImageManual:EquipmentConfig") }}
</div>
<el-form size="small" :model="EquipmentForm" style="width:80%" v-if="form.IsCloseEquipmentSurvey">
<el-form-item>
<el-checkbox v-model="EquipmentForm.EquipmentTypeEnum">
{{ $t('trials:researchForm:form:EquipmentTypeEnum') }}
</el-checkbox>
</el-form-item>
<el-form-item>
<el-checkbox v-model="EquipmentForm.Parameters">
{{ $t('trials:researchForm:form:Parameters') }}
</el-checkbox>
</el-form-item>
<el-form-item>
<el-checkbox v-model="EquipmentForm.ManufacturerType">
{{ $t('trials:researchForm:form:ManufacturerType') }}
</el-checkbox>
</el-form-item>
<el-form-item>
<el-checkbox v-model="EquipmentForm.ScannerType">
{{ $t('trials:researchForm:form:ScannerType') }}
</el-checkbox>
</el-form-item>
<el-form-item>
<el-checkbox v-model="EquipmentForm.MagneticFieldStrengthType">
{{ $t('trials:researchForm:form:MagneticFieldStrengthType') }}
</el-checkbox>
</el-form-item>
<el-form-item>
<el-checkbox v-model="EquipmentForm.BodyCoilChannelCount">
{{ $t('trials:researchForm:form:BodyCoilChannelCount') }}
</el-checkbox>
</el-form-item>
<el-form-item>
<el-checkbox v-model="EquipmentForm.HasDedicatedPdfFatQuantificationSequence"
@change="EquipmentForm.PdfFatQuantificationSequenceType = false">
{{ $t('trials:researchForm:form:HasDedicatedPdfFatQuantificationSequence') }}
</el-checkbox>
</el-form-item>
<el-form-item v-if="EquipmentForm.HasDedicatedPdfFatQuantificationSequence">
<el-checkbox v-model="EquipmentForm.PdfFatQuantificationSequenceType">
{{ $t('trials:researchForm:form:PdfFatQuantificationSequenceType') }}
</el-checkbox>
</el-form-item>
<el-form-item>
<el-checkbox v-model="EquipmentForm.HasT2R2Correction">
{{ $t('trials:researchForm:form:HasT2R2Correction') }}
</el-checkbox>
</el-form-item>
<el-form-item>
<el-checkbox v-model="EquipmentForm.CanFullyExportPdfParameterMapsAndRawDicom">
{{ $t('trials:researchForm:form:CanFullyExportPdfParameterMapsAndRawDicom') }}
</el-checkbox>
</el-form-item>
<el-form-item>
<el-checkbox v-model="EquipmentForm.Note">
{{ $t('trials:researchForm:form:Note') }}
</el-checkbox>
</el-form-item>
</el-form>
<div style="margin-bottom: 20px;font-weight: bold;">
{{ $t("trials:researchRecord:ImageManual:Precautions") }}
</div>
<el-input type="textarea" :autosize="{ minRows: 4, maxRows: 99 }" placeholder=""
v-model="form.ReplaceContent" @input="handleInput" style="width: 70%;" />
v-model="form.ReplaceContentCN" style="width: 70%;" />
<div style="margin: 20px 0;font-weight: bold;">
{{ $t("trials:researchRecord:ImageManual:PrecautionsUS") }}
</div>
<el-input type="textarea" :autosize="{ minRows: 4, maxRows: 99 }" placeholder=""
v-model="form.ReplaceContent" style="width: 70%;" />
<div style="text-align: right;margin: 20px;" class="btnBox">
<el-button type="primary" :loading="loading" size="small" @click="handleSave">
{{ $t('common:button:save') }}
@@ -80,7 +186,34 @@ export default {
ReplaceContentCN: '',
IsOpenLostVistRead: false,
IsSupportQCDownloadImage: false,
IsCloseEquipmentSurvey: true
IsCloseEquipmentSurvey: true,
IsRoutineMRIPDEE: false,
IsMRIPDFF: false,
MRIPDFFScanTime: false,
MRIPDFFLeadTime: false,
MRIPDFFOther: false,
IsAuthorize: false,
IsAuthorizeRadiologistsParticipate: false,
AssignFixedTechnologists: false,
ISStrictManualBurnFlag: false,
NotStrictManualBurnFlagReason: false
},
EquipmentForm: {
Parameters: false,
Note: false,
EquipmentTypeEnum: false,
ManufacturerType: false,
ScannerType: false,
MagneticFieldStrengthType: false,
BodyCoilChannelCount: false,
HasDedicatedPdfFatQuantificationSequence: false,
PdfFatQuantificationSequenceType: false,
HasT2R2Correction: false,
CanFullyExportPdfParameterMapsAndRawDicom: false,
},
uploadForm: {
IsSupportUploadFile: false,
IsOnlyUploadFile: false
},
obj: {
AverageEngravingCycle: false,
@@ -89,6 +222,14 @@ export default {
EfficacyEvaluatorType: false,
IsFollowStudyParameters: false,
NotFollowReson: false,
IsRoutineMRIPDEE: false,
MRIPDFFScanTime: false,
MRIPDFFLeadTime: false,
MRIPDFFOther: false,
IsAuthorizeRadiologistsParticipate: false,
AssignFixedTechnologists: false,
ISStrictManualBurnFlag: false,
NotStrictManualBurnFlagReason: false,
},
loading: false
}
@@ -100,9 +241,21 @@ export default {
handleChange(val, key) {
if (key === 'IsConfirmImagingTechnologist') this.form.NotConfirmReson = val
if (key === 'IsFollowStudyParameters') this.form.NotFollowReson = val
if (key === 'IsMRIPDFF') {
this.form.MRIPDFFScanTime = val
this.form.MRIPDFFLeadTime = val
this.form.MRIPDFFOther = val
}
if (key === 'IsAuthorize') {
this.form.IsAuthorizeRadiologistsParticipate = val
this.form.AssignFixedTechnologists = val
}
if (key === 'ISStrictManualBurnFlag') this.form.NotStrictManualBurnFlagReason = val
},
handleInput(val) {
this.form.ReplaceContentCN = val
Object.keys(this.EquipmentForm).forEach(key => {
this.EquipmentForm[key] = false
})
},
async getInfo() {
try {
@@ -111,16 +264,28 @@ export default {
}
let res = await getTrialExtralConfig(param)
if (res.IsSuccess) {
this.uploadForm.IsOnlyUploadFile = res.Result.IsOnlyUploadFile
this.uploadForm.IsSupportUploadFile = res.Result.IsSupportUploadFile
Object.keys(this.form).forEach(key => {
this.form[key] = true
if (key === 'ReplaceContent' || key === 'ReplaceContentCN') this.form[key] = ''
})
Object.keys(this.EquipmentForm).forEach(key => {
this.EquipmentForm[key] = false
})
res.Result.EquipmentControlFieldList.forEach(item => {
if (this.EquipmentForm.hasOwnProperty(item.FiledName)) {
this.EquipmentForm[item.FiledName] = true
}
})
this.form.IsOpenLostVistRead = res.Result.IsOpenLostVistRead
this.form.IsSupportQCDownloadImage = res.Result.IsSupportQCDownloadImage
this.form.IsCloseEquipmentSurvey = !res.Result.IsCloseEquipmentSurvey
if (Array.isArray(res.Result.NotShowFieldList) && res.Result.NotShowFieldList.length > 0) {
res.Result.NotShowFieldList.forEach(key => {
this.form[key] = false
if (['MRIPDFFScanTime', 'MRIPDFFLeadTime', 'MRIPDFFOther'].includes(key)) this.form.IsMRIPDFF = false
if (['IsAuthorizeRadiologistsParticipate', 'AssignFixedTechnologists'].includes(key)) this.form.IsAuthorize = false
})
}
if (Array.isArray(res.Result.ModifyFiledList) && res.Result.ModifyFiledList.length > 0) {
@@ -128,8 +293,15 @@ export default {
this.form.ReplaceContentCN = res.Result.ModifyFiledList[0].ReplaceContentCN.split('<p>').join('').split('</p>').filter(item => isNaN(item)).join("\n")
} else {
let arr = [this.$t('trials:equiptResearch:form:item1'), this.$t('trials:equiptResearch:form:item2'), this.$t('trials:equiptResearch:form:item3'), this.$t('trials:equiptResearch:form:item4')]
this.form.ReplaceContent = arr.join("\n")
this.form.ReplaceContentCN = arr.join("\n")
let arr2 = [this.$t('trials:equiptResearch:form:item5'), this.$t('trials:equiptResearch:form:item6'), this.$t('trials:equiptResearch:form:item7'), this.$t('trials:equiptResearch:form:item8')]
if (this.$i18n.locale !== 'zh') {
this.form.ReplaceContentCN = arr2.join("\n")
this.form.ReplaceContent = arr.join("\n")
} else {
this.form.ReplaceContentCN = arr.join("\n")
this.form.ReplaceContent = arr2.join("\n")
}
}
}
} catch (err) {
@@ -167,8 +339,11 @@ export default {
IsOpenLostVistRead: this.form.IsOpenLostVistRead,
IsSupportQCDownloadImage: this.form.IsSupportQCDownloadImage,
IsCloseEquipmentSurvey: !this.form.IsCloseEquipmentSurvey,
IsOnlyUploadFile: this.uploadForm.IsOnlyUploadFile,
IsSupportUploadFile: this.uploadForm.IsSupportUploadFile,
ModifyFiledList: [],
NotShowFieldList: []
NotShowFieldList: [],
EquipmentControlFieldList: []
}
Object.keys(this.obj).forEach(key => {
if (!this.form[key]) data.NotShowFieldList.push(key)
@@ -187,6 +362,16 @@ export default {
ReplaceContentCN: ReplaceContentCN,
}
data.ModifyFiledList.push(obj)
Object.keys(this.EquipmentForm).forEach((key, index) => {
if (this.EquipmentForm[key]) {
let obj = {
FiledName: key,
IsView: true,
ShowOrder: index,
}
data.EquipmentControlFieldList.push(obj)
}
})
this.loading = true
let res = await updateTrialExtralConfig(param, data)
this.loading = false
@@ -184,44 +184,79 @@
</el-dialog>
<!-- 调查表链接 -->
<base-model :config="share_model">
<template slot="dialog-body">
<template slot="dialog-body" v-loading="shareLoading">
<el-button size="small" type="primary" style="margin-bottom: 10px;" @click.stop="openImageManual">{{
$t('trials:researchRecord:label:edit')
}}</el-button>
<div style="width: 100%; display: flex">
<div class="shareLink">
<!-- <div>
<i style="color: #428bca" class="el-icon-success" />
<span>{{
$t('trials:researchRecord:message:createLinkSuccessfully')
}}</span>
</div> -->
<div style="margin: 10px 0">
<!-- 链接 -->
{{ $t('trials:researchRecord:label:link') }}
<el-input ref="shareLink" v-model="shareLink" readonly type="textarea" autosize />
</div>
<div>
<!-- 复制链接 -->
<el-button type="primary" round @click="copyLink" class="shareLinkBtn">
<div class="date">
<el-form :model="shareForm" :rules="rules" ref="shareForm" label-width="100px">
<el-form-item :label="$t('trials:researchRecord:label:ExpirationDays')" prop="ExpirationDays">
<el-radio-group v-model="shareForm.ExpirationDays" @input="shareForm.OtherExpirationDays = null">
<el-radio :label="1">{{ $t('trials:researchRecord:label:day1') }}</el-radio>
<el-radio :label="7">{{ $t('trials:researchRecord:label:day7') }}</el-radio>
<el-radio :label="15">{{ $t('trials:researchRecord:label:day15') }}</el-radio>
<el-radio :label="`default`">{{ $t('trials:researchRecord:label:default') }}
<el-input placeholder="" type="number" @input="handleInput" v-model="shareForm.OtherExpirationDays"
clearable size="mini" style="width: 60px;" class="dayInput" />
<span style="margin-left: 10px;">{{ $t('trials:researchRecord:label:day') }}</span>
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('trials:researchRecord:label:LinkVerificationCode')" prop="LinkVerificationCode">
<el-radio-group v-model="shareForm.LinkVerificationCode"
@input="shareForm.OtherLinkVerificationCode = null">
<el-radio label="researchProgramNo">{{ $t('trials:researchRecord:label:researchProgramNo')
}}</el-radio>
<el-radio label="default">{{ $t('trials:researchRecord:label:default') }}
<el-input placeholder="" type="number" v-model="shareForm.OtherLinkVerificationCode" clearable
size="mini" style="width: 100px;" />
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<el-button type="primary" round @click="setLink" style="float: right;">
{{ $t('trials:researchRecord:button:setLink') }}
</el-button>
</div>
<div class="share">
<div class="shareLink">
<h3>{{ $t('trials:researchRecord:label:link') }}</h3>
<el-input ref="shareLink" v-model="shareLink" readonly type="textarea" autosize :rows="3" />
<div class="dateBox">
<span>{{ $t('trials:researchRecord:label:linkVerificationCode') }}</span>
<span>{{ LinkVerificationCode }}</span>
</div>
<div class="dateBox">
<span>{{ $t('trials:researchRecord:label:ValidityPeriod') }}</span>
<span>{{ validityPeriod }}</span>
<span style="color: red;" v-if="isExpired">({{ $t('trials:researchRecord:label:Expired') }})</span>
</div>
<el-button type="primary" round @click="copyLink" class="shareLinkBtn"
:disabled="!validityPeriod || isExpired">
{{ $t('trials:researchRecord:button:copyLink') }}
</el-button>
</div>
</div>
<div class="shareCode">
<div class="qrCode">
<div id="qrcode" ref="qrcode"></div>
</div>
<div class="codeBtnBox">
<el-button @click="handleCopyImg" type="primary" round>{{
$t('trials:researchRecord:button:copyCode')
}}</el-button>
<el-button @click="savePic" round>{{
$t('trials:researchRecord:button:savePic')
}}</el-button>
<div class="shareCode">
<h3>{{ $t('trials:researchRecord:label:shareCode') }}</h3>
<div style="display: flex;align-items: center;justify-content: space-between;">
<div class="qrCodeBox">
<div id="qrcode" ref="qrcode"></div>
</div>
<div class="codeBtnBox">
<el-button @click="handleCopyImg" type="primary" round :disabled="!validityPeriod || isExpired">{{
$t('trials:researchRecord:button:copyCode')
}}</el-button>
<el-button @click="savePic" round :disabled="!validityPeriod || isExpired">{{
$t('trials:researchRecord:button:savePic')
}}</el-button>
</div>
</div>
</div>
</div>
</div>
</template>
</base-model>
</BaseContainer>
@@ -231,6 +266,9 @@ import {
getTrialSiteSurveyList,
getTrialSiteSelect,
abandonSiteSurvey,
setTrialLinkExpirationTime,
getTrialLinkExpirationTime,
getLinkLinkExpirationTime,
} from '@/api/trials'
import { changeURLStatic } from '@/utils/history.js'
import BaseContainer from '@/components/BaseContainer'
@@ -272,14 +310,51 @@ export default {
share_model: {
visible: false,
title: this.$t('trials:researchRecord:title:shark'),
width: '800px',
width: '1000px',
},
shareLink: '',
researchState: this.$d.ResearchRecord,
qrcode: null,
validityPeriod: null,
LinkVerificationCode: null,
isExpired: false,
shareLoading: false,
shareForm: {
ExpirationDays: null,
OtherExpirationDays: null,
LinkVerificationCode: null,
OtherLinkVerificationCode: null,
},
rules: {
ExpirationDays: [
{ required: true, message: this.$t("common:ruleMessage:select"), trigger: 'blur' },
{
validator: (rule, value, callback) => {
if (value === 'default' && !this.shareForm.OtherExpirationDays) {
callback(new Error(this.$t("common:ruleMessage:specify")));
} else {
callback()
}
}, trigger: 'blur'
}
],
LinkVerificationCode: [
{ required: true, message: this.$t("common:ruleMessage:select"), trigger: 'blur' },
{
validator: (rule, value, callback) => {
if (value === 'default' && !this.shareForm.OtherLinkVerificationCode) {
callback(new Error(this.$t("common:ruleMessage:specify")));
} else {
callback()
}
}, trigger: 'blur'
}
],
},
ImageManualVisible: false,
trialSiteSurveyId: null
trialSiteSurveyId: null,
}
},
mounted() {
@@ -287,6 +362,81 @@ export default {
this.getSite()
},
methods: {
async getLinkTimeIsExpired() {
try {
let data = {
TrialId: this.$route.query.trialId,
}
this.shareLoading = true
let res = await getLinkLinkExpirationTime(data)
this.shareLoading = false
if (res.IsSuccess) {
this.isExpired = res.Result.IsIsExpired
}
} catch (err) {
this.shareLoading = false
console.log(err)
}
},
async getLinkTime() {
try {
let data = {
TrialId: this.$route.query.trialId,
}
this.shareLoading = true
let res = await getTrialLinkExpirationTime(data)
this.shareLoading = false
if (res.IsSuccess) {
this.validityPeriod = res.Result.LinkExpirationTime
this.LinkVerificationCode = res.Result.LinkVerificationCode || res.Result.ResearchProgramNo
if (!this.validityPeriod) {
this.LinkVerificationCode = null
}
if (!this.validityPeriod) return false
this.getLinkTimeIsExpired()
this.shareLink = `${location.protocol}//${location.host}/researchLogin?trialId=${this.$route.query.trialId}`
this.$nextTick(() => {
this.creatQrCode()
})
}
} catch (err) {
this.shareLoading = false
console.log(err)
}
},
async setLink() {
try {
let validate = await this.$refs.shareForm.validate()
if (!validate) return false
let data = {
TrialId: this.$route.query.trialId,
ExpirationDays: this.shareForm.ExpirationDays,
LinkVerificationCode: this.shareForm.LinkVerificationCode
}
if (this.shareForm.LinkVerificationCode === 'researchProgramNo') {
data.LinkVerificationCode = null
}
if (this.shareForm.LinkVerificationCode === 'default') {
data.LinkVerificationCode = this.shareForm.OtherLinkVerificationCode
}
if (this.shareForm.ExpirationDays === 'default') {
data.ExpirationDays = this.shareForm.OtherExpirationDays
}
this.shareLoading = true
let res = await setTrialLinkExpirationTime(data)
this.shareLoading = false
if (res.IsSuccess) {
this.getLinkTime()
}
} catch (err) {
this.shareLoading = false
console.log(err)
}
},
handleInput(val) {
//
this.shareForm.OtherExpirationDays = val.replace(/[^\d]/g, '').replace(/^0+/, '')
},
openImageManual() {
// if (!this.trialSiteSurveyId) return false
this.ImageManualVisible = true
@@ -344,7 +494,7 @@ export default {
//
this.$copyText(
`${this.$t('trials:researchRecord:message:researchFormLink')}: ${this.shareLink
}`
}\n${this.$t('trials:researchRecord:label:linkVerificationCode')}${this.LinkVerificationCode}\n${this.$t('trials:researchRecord:label:ValidityPeriod')}${this.validityPeriod}`
)
.then((res) => {
//
@@ -415,13 +565,18 @@ export default {
},
//
showResearchLink() {
const trialId = this.trialId
this.shareLink = `${location.protocol}//${location.host}/researchLogin?trialId=${trialId}&lang=${this.$i18n.locale}`
this.shareForm = {
ExpirationDays: null,
OtherExpirationDays: null,
LinkVerificationCode: null,
OtherLinkVerificationCode: null,
}
this.validityPeriod = null
this.isExpired = false
this.share_model.visible = true
this.getLinkTime()
// &lang=${this.$i18n.locale}
// this.trialSiteSurveyId = this.list[0].Id
this.$nextTick(() => {
this.creatQrCode()
})
},
//
handleReset() {
@@ -462,41 +617,54 @@ export default {
}
</script>
<style lang="scss" scoped>
.shareLink {
padding-right: 20px;
width: 50%;
position: relative;
.date,
.share {
width: 55%;
height: 100%;
}
.shareLinkBtn {
position: absolute;
bottom: 0px;
left: 0px;
.dayInput {
::v-deep .el-input__inner {
padding-left: 5px;
}
}
.shareCode {
width: 50%;
border-left: 1px solid #eee;
.date {
padding-right: 10px;
}
.share {
width: 45%;
padding-left: 5%;
border-left: 1px solid #f0f0f0;
box-sizing: border-box;
}
.shareLinkBtn {
float: right;
}
.qrCodeBox {
width: 220px;
height: 220px;
display: flex;
border: 1px solid #c0c4cc;
border-radius: 5px;
box-shadow: 1px 1px 5px #c0c4cc;
align-items: center;
justify-content: center;
flex-wrap: wrap;
}
.qrCode {
width: 220px;
height: 220px;
display: flex;
border: 1px solid #c0c4cc;
border-radius: 5px;
box-shadow: 1px 1px 5px #c0c4cc;
align-items: center;
justify-content: center;
}
.dateBox {
margin: 10px;
}
.codeBtnBox {
margin-top: 20px;
width: 100%;
display: flex;
justify-content: space-around;
.codeBtnBox {
::v-deep .el-button {
display: block;
margin: 10px auto;
}
}
</style>
@@ -217,6 +217,14 @@
)
}).TotalCount
}}
<el-button type="text" @click="
statistics(
scope.row,
ite.TrialReadingCriterionId
)
">
{{ $t('trials:enrolledReviews:button:Statistics') }}
</el-button>
</div>
</template>
</el-table-column>
@@ -351,6 +359,7 @@ import {
enrollBackOrOut,
setEnrollReadingCategory,
batchSetCriterionJoinJoinAnalysis,
getDoctorUserTrialReadingStat
} from '@/api/trials'
import { addDoctorCriterionFile } from '@/api/reviewers'
import { uploadFile } from '@/api/attachment'
@@ -470,6 +479,28 @@ export default {
this.initPage()
},
methods: {
async statistics(row, id) {
try {
let data = {
TrialId: this.$route.query.trialId,
TrialReadingCriterionId: id,
DoctoruserIdList: [row.DoctorUserId]
}
let res = await getDoctorUserTrialReadingStat(data)
if (res.IsSuccess && res.Result[0]) {
row.CriterionCategoryList.some((v) => {
if (v.TrialReadingCriterionId === id) {
v.PendingCount = res.Result[0].PendingCount
v.ComplectedCount = res.Result[0].ComplectedCount
v.TotalCount = res.Result[0].TotalCount
}
return v.TrialReadingCriterionId === id
})
}
} catch (err) {
console.log(err)
}
},
//
preview(row, TrialReadingCriterionId, key) {
let file = row.CriterionCategoryList.find((v) => {
@@ -1,11 +1,7 @@
<template>
<div class="adReview_wrapper">
<el-card
v-if="isReadingShowSubjectInfo"
shadow="never"
:body-style="{ padding: '10px' }"
style="margin-bottom: 10px"
>
<el-card v-if="isReadingShowSubjectInfo" shadow="never" :body-style="{ padding: '10px' }"
style="margin-bottom: 10px">
<h4>
<!-- 受试者 -->
{{ $t("trials:adReview:title:subject") }}
@@ -17,14 +13,11 @@
<!-- <div slot="header" class="clearfix">
<span style="font-weight: bold;">评估结果</span>
</div> -->
<div
slot="header"
style="
<div slot="header" style="
display: flex;
flex-direction: row;
justify-content: space-between;
"
>
">
<!-- 评估结果 -->
<div style="font-weight: bold">
{{ $t("trials:adReview:title:result") }}
@@ -39,62 +32,45 @@
<el-table :data="adInfo.VisitInfoList" style="width: 100%">
<!-- 访视名称 -->
<el-table-column
prop="VisitName"
:label="$t('trials:adReview:table:visitName')"
show-overflow-tooltip
width="150"
/>
<el-table-column prop="VisitName" :label="$t('trials:adReview:table:visitName')" show-overflow-tooltip
width="150" />
<!-- 评估结果 -->
<el-table-column
v-for="j in judgeQuestion"
:key="j.armEnum"
:label="
j.armEnum === 1
? $t('trials:adReview:table:viewR1')
: j.armEnum === 2
? $t('trials:adReview:table:viewR2')
: $fd('ArmEnum', j.armEnum)
"
align="center"
prop=""
>
<el-table-column v-for="j in judgeQuestion" :key="j.armEnum" :label="j.armEnum === 1
? $t('trials:adReview:table:viewR1')
: j.armEnum === 2
? $t('trials:adReview:table:viewR2')
: $fd('ArmEnum', j.armEnum)
" align="center" prop="">
<template>
<el-table-column
v-for="(qs, i) in j.judgeQuestionList"
:key="i"
prop=""
:label="qs"
show-overflow-tooltip
width="150"
>
<el-table-column v-for="(qs, i) in j.judgeQuestionList" :key="i" prop="" :label="qs" show-overflow-tooltip
width="150">
<template slot-scope="scope">
<div v-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].QuestionType === 1">
<span v-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].DictionaryCode">
{{ $fd(scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].DictionaryCode,parseInt(scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer))
{{
$fd(scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].DictionaryCode,
parseInt(scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer))
}}
</span>
<span v-else>
{{ scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer }}
</span>
<span v-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Unit && !isNaN(parseFloat(scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer))">{{ $fd('ValueUnit', parseInt(scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Unit)) }}</span>
<span
v-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Unit && !isNaN(parseFloat(scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer))">{{
$fd('ValueUnit', parseInt(scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Unit)) }}</span>
</div>
<div v-else-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].QuestionType === 2">
<div v-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer">
<span>
{{ $fd("YesOrNo",scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer) }}
{{ $fd("YesOrNo", scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer) }}
</span>
<!-- 查看详情 -->
<el-button
type="text"
style="margin-left: 5px"
@click="
handleViewDetail(
scope.row.VisitTaskInfoList[j.index].GlobalVisitTaskId
)
"
>
<el-button type="text" style="margin-left: 5px" @click="
handleViewDetail(
scope.row.VisitTaskInfoList[j.index].GlobalVisitTaskId
)
">
{{ $t("trials:adReview:table:view") }}
</el-button>
</div>
@@ -119,60 +95,34 @@
</el-table-column>
<!-- 查看详情 -->
<el-table-column
:label="
criterionType === 10
? $t('trials:adReview:table:visitInfoview')
: $t('trials:adReview:table:view')
"
width="200"
:fixed="isFixed ? 'right' : false"
>
<el-table-column :label="criterionType === 10
? $t('trials:adReview:table:visitInfoview')
: $t('trials:adReview:table:view')
" width="200" :fixed="isFixed ? 'right' : false">
<template slot-scope="scope">
<!-- 查看R1详情 -->
<el-button
type="text"
:title="$t('trials:adReview:table:viewR1')"
@click="handleView(scope.row, 1)"
>
<el-button type="text" :title="$t('trials:adReview:table:viewR1')" @click="handleView(scope.row, 1)">
R1
</el-button>
<!-- 查看R2详情 -->
<el-button
type="text"
:title="$t('trials:adReview:table:viewR2')"
@click="handleView(scope.row, 2)"
>
<el-button type="text" :title="$t('trials:adReview:table:viewR2')" @click="handleView(scope.row, 2)">
R2
</el-button>
</template>
</el-table-column>
<!-- 全局详情 PCWG -->
<el-table-column
v-if="criterionType === 10"
prop="VisitName"
:label="$t('trials:adReview:table:glInfo')"
show-overflow-tooltip
width="150"
>
<el-table-column v-if="criterionType === 10" prop="VisitName" :label="$t('trials:adReview:table:glInfo')"
show-overflow-tooltip width="150">
<template slot-scope="scope">
<!-- 查看R1详情 -->
<el-button
v-if="scope.$index === adInfo.VisitInfoList.length - 1"
type="text"
:title="$t('trials:adReview:table:viewR1')"
@click="handleViewGl(scope.row, 1)"
>
<el-button v-if="scope.$index === adInfo.VisitInfoList.length - 1" type="text"
:title="$t('trials:adReview:table:viewR1')" @click="handleViewGl(scope.row, 1)">
R1
</el-button>
<!-- 查看R2详情 -->
<el-button
v-if="scope.$index === adInfo.VisitInfoList.length - 1"
type="text"
:title="$t('trials:adReview:table:viewR2')"
@click="handleViewGl(scope.row, 2)"
>
<el-button v-if="scope.$index === adInfo.VisitInfoList.length - 1" type="text"
:title="$t('trials:adReview:table:viewR2')" @click="handleViewGl(scope.row, 2)">
R2
</el-button>
</template>
@@ -186,120 +136,64 @@
$t("trials:adReview:title:adResult")
}}</span>
</div>
<el-form
ref="adForm"
v-loading="loading"
:model="adForm"
style="width: 800px"
label-width="100"
>
<el-form ref="adForm" v-loading="loading" :model="adForm" style="width: 800px" label-width="100">
<!-- 选择阅片人 -->
<el-form-item
:label="$t('trials:adReview:title:choseReader')"
prop="judgeResultTaskId"
:rules="[
{ required: true, message: this.$t('common:ruleMessage:select') },
]"
>
<el-radio-group
v-model="adForm.judgeResultTaskId"
:disabled="adInfo.ReadingTaskState >= 2"
>
<el-radio
v-for="t in visitTaskArmList"
:key="t.VisitTaskId"
:label="t.VisitTaskId"
@change="handleVisitTaskArmChange"
>
<el-form-item :label="$t('trials:adReview:title:choseReader')" prop="judgeResultTaskId" :rules="[
{ required: true, message: this.$t('common:ruleMessage:select') },
]">
<el-radio-group v-model="adForm.judgeResultTaskId" :disabled="adInfo.ReadingTaskState >= 2">
<el-radio v-for="t in visitTaskArmList" :key="t.VisitTaskId" :label="t.VisitTaskId"
@change="handleVisitTaskArmChange">
{{ $fd("ArmEnum", t.ArmEnum) }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- 裁判原因 -->
<el-form-item
:label="$t('trials:adReview:title:adReason')"
prop="judgeResultRemark"
:rules="[
{ required: true, message: this.$t('common:ruleMessage:specify') },
{
max: 500,
message: `${this.$t('common:ruleMessage:maxLength')} 500`,
trigger: ['blur', 'change'],
},
]"
>
<el-form-item :label="$t('trials:adReview:title:adReason')" prop="judgeResultRemark" :rules="[
{ required: true, message: this.$t('common:ruleMessage:specify') },
{
max: 500,
message: `${this.$t('common:ruleMessage:maxLength')} 500`,
trigger: ['blur', 'change'],
},
]">
<div style="position: relative">
<div
style="
<div style="
position: absolute;
left: 0;
top: 30px;
color: #606266;
font-size: 13px;
"
>
">
{{ remark }}
</div>
<el-input
v-model="adForm.judgeResultRemark"
type="textarea"
:autosize="{ minRows: 4, maxRows: 6 }"
:disabled="adInfo.ReadingTaskState >= 2"
style="margin-top: 25px"
/>
<el-input v-model="adForm.judgeResultRemark" type="textarea" :autosize="{ minRows: 4, maxRows: 6 }"
:disabled="adInfo.ReadingTaskState >= 2" style="margin-top: 25px" />
</div>
</el-form-item>
<!-- 截图说明 -->
<el-form-item :label="$t('trials:adReview:title:screenShot')">
<el-upload
action
:accept="accept"
:on-preview="handlePictureCardPreview"
:before-upload="handleBeforeUpload"
:http-request="uploadScreenshot"
list-type="picture-card"
:on-remove="handleRemove"
:file-list="fileList"
:class="{ disabled: adInfo.ReadingTaskState >= 2 }"
:disabled="adInfo.ReadingTaskState >= 2"
>
<el-upload action :accept="accept" :on-preview="handlePictureCardPreview" :before-upload="handleBeforeUpload"
:http-request="uploadScreenshot" list-type="picture-card" :on-remove="handleRemove" :file-list="fileList"
:class="{ disabled: adInfo.ReadingTaskState >= 2 }" :disabled="adInfo.ReadingTaskState >= 2">
<i slot="default" class="el-icon-plus" />
<div
slot="file"
slot-scope="{ file }"
style="width: 100%; height: 100%"
>
<viewer
:ref="file.url"
:images="images"
style="
<div slot="file" slot-scope="{ file }" style="width: 100%; height: 100%">
<viewer :ref="file.url" :images="images" style="
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
"
>
<img
class="el-upload-list__item-thumbnail"
:src="OSSclientConfig.basePath + file.url"
alt=""
crossorigin="anonymous"
style="max-width: 100%; max-height: 100%"
>
">
<img class="el-upload-list__item-thumbnail" :src="OSSclientConfig.basePath + file.url" alt=""
crossorigin="anonymous" style="max-width: 100%; max-height: 100%">
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-preview"
@click="handlePictureCardPreview(file)"
>
<span class="el-upload-list__item-preview" @click="handlePictureCardPreview(file)">
<i class="el-icon-zoom-in" />
</span>
<span
v-if="adInfo.ReadingTaskState < 2"
class="el-upload-list__item-delete"
@click="handleRemove(file)"
>
<span v-if="adInfo.ReadingTaskState < 2" class="el-upload-list__item-delete"
@click="handleRemove(file)">
<i class="el-icon-delete" />
</span>
</span>
@@ -314,20 +208,17 @@
{{ $t("trials:readingReport:button:skip") }}
</el-button>
<!-- 保存 -->
<el-button type="primary" @click="handleSave">{{
<el-button type="primary" :loading="submitLockAction === 'save'" :disabled="!!submitLockAction" @click="handleSave">{{
$t("common:button:save")
}}</el-button>
<!-- 提交 -->
<el-button type="primary" @click="handleSubmit">{{
<el-button type="primary" :loading="submitLockAction === 'submit'" :disabled="!!submitLockAction" @click="handleSubmit">{{
$t("common:button:submit")
}}</el-button>
</div>
</el-card>
<el-card
v-if="isReadingShowPreviousResults"
:body-style="{ padding: '10px' }"
>
<el-card v-if="isReadingShowPreviousResults" :body-style="{ padding: '10px' }">
<div slot="header" class="clearfix">
<!-- 既往裁判评估 -->
<span style="font-weight: bold">{{
@@ -335,24 +226,12 @@
}}</span>
</div>
<el-table
v-loading="priorLoading"
:data="priorADList"
style="width: 100%"
>
<el-table v-loading="priorLoading" :data="priorADList" style="width: 100%">
<!-- 裁判阅片 -->
<el-table-column
prop="TaskBlindName"
:label="$t('trials:adReview:table:adReading')"
show-overflow-tooltip
width="200"
/>
<el-table-column
prop="JudgeResultArm"
:label="$t('trials:adReview:table:adResult')"
show-overflow-tooltip
width="200"
>
<el-table-column prop="TaskBlindName" :label="$t('trials:adReview:table:adReading')" show-overflow-tooltip
width="200" />
<el-table-column prop="JudgeResultArm" :label="$t('trials:adReview:table:adResult')" show-overflow-tooltip
width="200">
<template slot-scope="scope">
{{ $fd("ArmEnum", scope.row.JudgeResultArm) }}
</template>
@@ -360,35 +239,22 @@
<el-table-column :label="$t('common:action:action')" width="200">
<template slot-scope="scope">
<!-- 查看详情 -->
<el-button
circle
:title="$t('trials:adReview:table:view')"
icon="el-icon-view"
@click="handleViewDetail(scope.row.VisitTaskId)"
/>
<el-button circle :title="$t('trials:adReview:table:view')" icon="el-icon-view"
@click="handleViewDetail(scope.row.VisitTaskId)" />
</template>
</el-table-column>
</el-table>
</el-card>
<!-- 签名框 -->
<el-dialog
v-if="signVisible"
:visible.sync="signVisible"
:close-on-click-modal="false"
width="600px"
custom-class="base-dialog-wrapper"
>
<el-dialog v-if="signVisible" :visible.sync="signVisible" :close-on-click-modal="false" width="600px"
custom-class="base-dialog-wrapper">
<div slot="title">
<span style="font-size: 18px">{{ $t("common:dialogTitle:sign") }}</span>
<span style="font-size: 12px; margin-left: 5px">{{
`(${$t("common:label:sign")}${currentUser})`
}}</span>
</div>
<SignForm
ref="signForm"
:sign-code-enum="signCode"
@closeDialog="closeSignDialog"
/>
<SignForm ref="signForm" :sign-code-enum="signCode" @closeDialog="closeSignDialog" />
</el-dialog>
</div>
</template>
@@ -447,6 +313,10 @@ export default {
isExistsClinicalData: {
type: Boolean,
required: true
},
imageToolType: {
type: Number,
required: true
}
},
data() {
@@ -477,7 +347,8 @@ export default {
openWindow: null,
isFixed: false,
images: [],
remark: ''
remark: '',
submitLockAction: ''
}
},
// watch: {
@@ -624,31 +495,43 @@ export default {
window.open(routeData.href, '_blank')
},
async handleSave() {
const valid = await this.$refs['adForm'].validate()
if (!valid) return
if (this.submitLockAction) return false
this.submitLockAction = 'save'
this.loading = true
var paths = []
this.fileList.map((file) => {
if (file.url) {
paths.push(file.url)
}
})
this.adForm.judgeResultImagePathList = paths
this.adForm.visitTaskId = this.visitTaskId
try {
const valid = await this.$refs['adForm'].validate()
if (!valid) return false
var paths = []
this.fileList.map((file) => {
if (file.url) {
paths.push(file.url)
}
})
this.adForm.judgeResultImagePathList = paths
this.adForm.visitTaskId = this.visitTaskId
await saveJudgeVisitTaskResult(this.adForm)
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.loading = false
return true
} catch (e) {
return false
} finally {
this.loading = false
this.submitLockAction = ''
}
},
async handleSubmit() {
const valid = await this.$refs['adForm'].validate()
if (!valid) return
const { ImageAssessmentReportConfirmation } = const_.processSignature
this.signCode = ImageAssessmentReportConfirmation
this.signVisible = true
if (this.submitLockAction) return false
this.submitLockAction = 'submit'
try {
const valid = await this.$refs['adForm'].validate()
if (!valid) return false
const { ImageAssessmentReportConfirmation } = const_.processSignature
this.signCode = ImageAssessmentReportConfirmation
this.signVisible = true
return true
} finally {
this.submitLockAction = ''
}
},
//
closeSignDialog(isSign, signInfo) {
@@ -683,9 +566,9 @@ export default {
this.signVisible = false
// window.location.reload()
// window.opener.postMessage('refreshTaskList', window.location)
//
//
this.adInfo.ReadingTaskState = 2
const res = await getAutoCutNextTask()
const res = await getAutoCutNextTask({imageToolType: this.imageToolType})
var isAutoTask = res.Result.AutoCutNextTask
if (isAutoTask) {
// store.dispatch('reading/resetVisitTasks')
@@ -729,18 +612,14 @@ export default {
this.$router.currentRoute.query.TrialReadingCriterionId
var path = ''
if (readingTool === 0 || readingTool === 2 ) {
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${
this.trialId
}&subjectCode=${this.subjectCode}&subjectId=${
this.subjectId
}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
if (readingTool === 0 || readingTool === 2 || readingTool === 3) {
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.trialId
}&subjectCode=${this.subjectCode}&subjectId=${this.subjectId
}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
} else {
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${
this.trialId
}&subjectCode=${this.subjectCode}&subjectId=${
this.subjectId
}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.trialId
}&subjectCode=${this.subjectCode}&subjectId=${this.subjectId
}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
}
var routeData = this.$router.resolve({ path })
this.openWindow = window.open(routeData.href, '_blank')
@@ -766,22 +645,16 @@ export default {
this.$router.currentRoute.query.TrialReadingCriterionId
var path = ''
if (readingTool === 0 || readingTool === 2 ) {
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${
this.trialId
}&subjectCode=${this.subjectCode}&subjectId=${
this.subjectId
}&visitTaskId=${
task.VisitTaskId
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
if (readingTool === 0 || readingTool === 2 || readingTool === 3) {
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.trialId
}&subjectCode=${this.subjectCode}&subjectId=${this.subjectId
}&visitTaskId=${task.VisitTaskId
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
} else {
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${
this.trialId
}&subjectCode=${this.subjectCode}&subjectId=${
this.subjectId
}&visitTaskId=${
task.VisitTaskId
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.trialId
}&subjectCode=${this.subjectCode}&subjectId=${this.subjectId
}&visitTaskId=${task.VisitTaskId
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
}
var routeData = this.$router.resolve({ path })
this.openWindow = window.open(routeData.href, '_blank')
@@ -801,22 +674,16 @@ export default {
this.$router.currentRoute.query.TrialReadingCriterionId
var path = ''
if (readingTool === 0 || readingTool === 2 ) {
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${
this.trialId
}&subjectCode=${this.subjectCode}&subjectId=${
this.subjectId
}&visitTaskId=${
task.GlobalVisitTaskId
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
if (readingTool === 0 || readingTool === 2 || readingTool === 3) {
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.trialId
}&subjectCode=${this.subjectCode}&subjectId=${this.subjectId
}&visitTaskId=${task.GlobalVisitTaskId
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
} else {
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${
this.trialId
}&subjectCode=${this.subjectCode}&subjectId=${
this.subjectId
}&visitTaskId=${
task.GlobalVisitTaskId
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.trialId
}&subjectCode=${this.subjectCode}&subjectId=${this.subjectId
}&visitTaskId=${task.GlobalVisitTaskId
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
}
var routeData = this.$router.resolve({ path })
this.openWindow = window.open(routeData.href, '_blank')
@@ -932,17 +799,21 @@ export default {
width: 100%;
height: 100%;
overflow-y: auto;
.box-mr {
margin: 10px 0;
}
.disabled {
::v-deep .el-upload--picture-card {
display: none;
}
}
::v-deep .el-upload-list__item {
transition: none !important;
}
::v-deep .el-upload-list__item-thumbnail {
/* 图片在方框内显示长边 */
object-fit: scale-down !important;

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