Compare commits

...

399 Commits

Author SHA1 Message Date
caiyiling 46aa64365f Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is running Details
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 Details
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 Details
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 Details
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 Details
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 Details
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 Details
2026-06-01 14:51:19 +08:00
wangxiaoshuang 6e6e2d11d0 影像上传列表受试者状态查询字段变更
continuous-integration/drone/push Build is passing Details
2026-05-29 17:29:22 +08:00
wangxiaoshuang 6b8711232e 1
continuous-integration/drone/push Build is passing Details
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 Details
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 Details
2026-05-28 15:02:18 +08:00
caiyiling 016a5e93ea 1
continuous-integration/drone/push Build is passing Details
2026-05-28 10:45:29 +08:00
caiyiling e4d731222b 1
continuous-integration/drone/push Build is passing Details
2026-05-27 17:55:00 +08:00
caiyiling aeeba1dfb7 1
continuous-integration/drone/push Build is passing Details
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 Details
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 Details
2026-05-27 09:43:18 +08:00
wangxiaoshuang 2778b34f14 1
continuous-integration/drone/push Build is passing Details
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 Details
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 Details
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 Details
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 Details
2026-05-26 10:19:37 +08:00
caiyiling a30fd58d79 多帧图像预览更改;数据同步检查列表补充排序字段
continuous-integration/drone/push Build is passing Details
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 Details
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 Details
2026-05-25 10:38:56 +08:00
caiyiling d6713ba9a4 circleROI测量数据更改
continuous-integration/drone/push Build is passing Details
2026-05-25 09:16:31 +08:00
caiyiling 0e252b0377 添加自定义MIPJumpToClickTool工具
continuous-integration/drone/push Build is passing Details
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 Details
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 Details
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 Details
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 Details
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 Details
2026-05-21 15:31:54 +08:00
wangxiaoshuang b6877d4fe8 1
continuous-integration/drone/push Build is passing Details
2026-05-21 15:01:43 +08:00
wangxiaoshuang b53f566160 系统配置、中心调研配置
continuous-integration/drone/push Build is passing Details
2026-05-21 14:41:33 +08:00
caiyiling c767004a2d 1
continuous-integration/drone/push Build is running Details
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 Details
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 Details
2026-05-21 09:34:42 +08:00
wangxiaoshuang c65ad92d39 中心调研设备调研修改
continuous-integration/drone/push Build is passing Details
2026-05-20 17:41:08 +08:00
wangxiaoshuang 36d5661044 分割标记与表格问题绑定修改
continuous-integration/drone/push Build is passing Details
2026-05-20 14:58:38 +08:00
wangxiaoshuang feb94dc832 1
continuous-integration/drone/push Build is passing Details
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 Details
2026-05-20 09:01:43 +08:00
caiyiling 04014ab524 其他类型文件同步数据更改
continuous-integration/drone/push Build is passing Details
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 Details
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 Details
2026-05-18 15:28:56 +08:00
wangxiaoshuang 730a375509 1
continuous-integration/drone/push Build is passing Details
2026-05-15 17:01:08 +08:00
wangxiaoshuang d34b4b796c 关于弹框修改
continuous-integration/drone/push Build is passing Details
2026-05-15 11:35:23 +08:00
wangxiaoshuang 427f060935 1
continuous-integration/drone/push Build is passing Details
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 Details
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 Details
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 Details
2026-05-14 09:39:59 +08:00
wangxiaoshuang 16e2fbc8ff 1
continuous-integration/drone/push Build is passing Details
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 Details
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 Details
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 Details
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 Details
2026-05-13 09:36:52 +08:00
wangxiaoshuang 511640f66f 分割恢复时清除分割片段弹框
continuous-integration/drone/push Build is passing Details
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 Details
2026-05-12 14:09:50 +08:00
wangxiaoshuang b59e0c12f6 1
continuous-integration/drone/push Build is passing Details
2026-05-12 11:27:56 +08:00
wangxiaoshuang fed2677689 分割分组重命名时传入文件大小
continuous-integration/drone/push Build is passing Details
2026-05-12 10:55:07 +08:00
wangxiaoshuang a55278e323 分割标记橡皮檫工具问题解决
continuous-integration/drone/push Build is passing Details
2026-05-12 10:29:35 +08:00
wangxiaoshuang 8f808d73b5 反馈修改
continuous-integration/drone/push Build is passing Details
2026-05-11 14:15:58 +08:00
wangxiaoshuang 717c5a6529 分割恢复问题解决
continuous-integration/drone/push Build is passing Details
2026-05-11 13:58:51 +08:00
wangxiaoshuang ec676d8d2b 数据匿名样式调整
continuous-integration/drone/push Build is passing Details
2026-05-11 13:26:03 +08:00
wangxiaoshuang 2ce14c6d72 热键s下载
continuous-integration/drone/push Build is passing Details
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 Details
2026-05-11 10:30:27 +08:00
wangxiaoshuang 4a17946598 分割恢复部分问题解决
continuous-integration/drone/push Build is passing Details
2026-05-09 18:00:26 +08:00
wangxiaoshuang d6f36cbbda 数据匿名按钮样式修改
continuous-integration/drone/push Build is passing Details
2026-05-09 15:33:35 +08:00
wangxiaoshuang e6a15e35f6 看板页面的地图需要展示
continuous-integration/drone/push Build is passing Details
2026-05-09 14:33:15 +08:00
wangxiaoshuang 27a9ccd3c2 数据匿名多个编辑选框应用时清除
continuous-integration/drone/push Build is failing Details
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 Details
2026-05-09 09:45:16 +08:00
caiyiling 9da7c1a159 1
continuous-integration/drone/push Build is passing Details
2026-05-08 17:21:33 +08:00
caiyiling 197f17fc99 1
continuous-integration/drone/push Build is passing Details
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 Details
2026-05-08 15:00:03 +08:00
caiyiling c9057e12cd 多帧影像切换bug修复
continuous-integration/drone/push Build is passing Details
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 Details
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 Details
2026-05-08 11:38:56 +08:00
caiyiling 64f0998896 样式调整
continuous-integration/drone/push Build is passing Details
2026-05-08 11:33:39 +08:00
caiyiling 04d9a53a0e 临床数据状态重置更改
continuous-integration/drone/push Build is passing Details
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 Details
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 Details
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 Details
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 Details
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 Details
2026-05-07 13:59:31 +08:00
caiyiling 09e9c62f3c pt临床数据弹窗取消点击modal关闭Dialog
continuous-integration/drone/push Build is passing Details
2026-05-07 13:52:28 +08:00
caiyiling af97e2b4ed crc修改pt临床数据更改
continuous-integration/drone/push Build is passing Details
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 Details
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 Details
2026-05-07 10:20:53 +08:00
wangxiaoshuang 49c7482d76 匿名数据部分问题解决
continuous-integration/drone/push Build is pending Details
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 Details
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 Details
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 Details
2026-05-06 15:32:51 +08:00
wangxiaoshuang 2f92bf6dfb pet、mr分割视图重置调窗问题
continuous-integration/drone/push Build is passing Details
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 Details
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 Details
2026-05-06 14:33:15 +08:00
wangxiaoshuang 1f20834dd3 个性化配置热键tab,高度加高一屏展示所有热键
continuous-integration/drone/push Build is passing Details
2026-05-06 13:35:00 +08:00
wangxiaoshuang f34665fcb9 体积分割对象的统计值,Count应该为整型
continuous-integration/drone/push Build is running Details
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 Details
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 Details
2026-05-06 13:21:45 +08:00
caiyiling 6a65b6507f 1
continuous-integration/drone/push Build is passing Details
2026-05-06 11:38:18 +08:00
caiyiling d08df877d7 样式更改
continuous-integration/drone/push Build is passing Details
2026-05-06 11:23:57 +08:00
caiyiling 10a43d4c04 自动添加肿瘤学阅片期bug修复
continuous-integration/drone/push Build is passing Details
2026-05-06 10:23:56 +08:00
wangxiaoshuang a87769a60f 1
continuous-integration/drone/push Build is passing Details
2026-04-30 17:31:34 +08:00
wangxiaoshuang 4d808ed87c 历史访视分割组显示按钮
continuous-integration/drone/push Build is passing Details
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 Details
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 Details
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 Details
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 Details
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 Details
# 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 Details
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 Details
2026-04-30 15:48:18 +08:00
wangxiaoshuang cd2df6e576 分割标记缓存问题
continuous-integration/drone/push Build is passing Details
2026-04-30 15:24:33 +08:00
wangxiaoshuang 52f9c17234 1
continuous-integration/drone/push Build is passing Details
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 Details
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 Details
2026-04-30 14:35:17 +08:00
caiyiling 212bd65bc1 阅片期临床数据添加更新功能
continuous-integration/drone/push Build is passing Details
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 Details
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 Details
2026-04-30 09:12:03 +08:00
wangxiaoshuang 728f3238ca 防止内存泄漏
continuous-integration/drone/push Build is passing Details
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 Details
2026-04-29 16:58:41 +08:00
wangxiaoshuang ecf4c8a2c7 1
continuous-integration/drone/push Build is passing Details
2026-04-29 16:17:49 +08:00
wangxiaoshuang c532babf98 视口变动时重新选中当前选择分割
continuous-integration/drone/push Build is passing Details
2026-04-29 15:56:18 +08:00
wangxiaoshuang 2fa54eaac6 保存分割时去除同步提示
continuous-integration/drone/push Build is passing Details
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 Details
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 Details
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 Details
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 Details
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 Details
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 Details
2026-04-29 13:30:42 +08:00
caiyiling c373b59f23 质控通过时pt临床数据可再次修改
continuous-integration/drone/push Build is passing Details
2026-04-29 12:41:00 +08:00
caiyiling 73e8f24c3d 阅片页面图标更改;适应图像逻辑更改;petct工具更改
continuous-integration/drone/push Build is passing Details
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 Details
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 Details
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 Details
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 Details
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 Details
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 Details
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 Details
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 Details
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 Details
2026-04-28 13:33:15 +08:00
caiyiling 6804d909b8 more图标title更改
continuous-integration/drone/push Build is passing Details
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 Details
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 Details
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 Details
2026-04-28 11:23:49 +08:00
caiyiling 5a01f10957 1
continuous-integration/drone/push Build is passing Details
2026-04-28 11:16:55 +08:00
caiyiling 4ed0ebc913 数据同步页面国际化
continuous-integration/drone/push Build is passing Details
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 Details
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 Details
2026-04-27 16:14:43 +08:00
wangxiaoshuang 7dcc524bd3 切换分割与ecrf表时显示全部分割标记
continuous-integration/drone/push Build is passing Details
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 Details
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 Details
2026-04-27 11:49:07 +08:00
wangxiaoshuang 91386589ae 删除分组后,会出现分段的气泡窗口
continuous-integration/drone/push Build is passing Details
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 Details
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 Details
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 Details
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 Details
2026-04-24 16:03:23 +08:00
caiyiling 6c88af0a03 质控更改
continuous-integration/drone/push Build is passing Details
2026-04-24 15:52:07 +08:00
caiyiling 26412931a9 样式冲突更改
continuous-integration/drone/push Build is passing Details
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 Details
# 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 Details
2026-04-24 14:33:16 +08:00
caiyiling ab372a9c29 PT临床数据更改
continuous-integration/drone/push Build is running Details
2026-04-24 14:30:36 +08:00
caiyiling f7dd68f7f3 pt临床数据更改
continuous-integration/drone/push Build is passing Details
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 Details
# 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 Details
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 Details
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 Details
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 Details
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 Details
2026-04-22 11:01:12 +08:00
wangxiaoshuang 25e348f428 上传问题解决
continuous-integration/drone/push Build is passing Details
2026-04-22 10:15:08 +08:00
caiyiling c101302cca 指控添加跳过功能
continuous-integration/drone/push Build is passing Details
2026-04-21 17:04:57 +08:00
caiyiling c4fd8c3ea3 融合视口crosshairsTool更改;数据同步页面更改;项目中培训文档路径更改
continuous-integration/drone/push Build is passing Details
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 Details
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 Details
2026-04-21 13:15:18 +08:00
caiyiling ca2e9e0253 数据同步列表更改
continuous-integration/drone/push Build is passing Details
2026-04-21 11:14:00 +08:00
caiyiling b4c06990f6 审批信息补充
continuous-integration/drone/push Build is passing Details
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 Details
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 Details
2026-04-20 17:59:45 +08:00
caiyiling 040f66f182 定圆工具更改及国际化全局替换IRC逻辑更改
continuous-integration/drone/push Build is passing Details
2026-04-20 15:20:46 +08:00
wangxiaoshuang 7afbc360e5 新增的分段后,点击计算长短径保存,刷新页面后计算值全部变为0
continuous-integration/drone/push Build is passing Details
2026-04-17 15:52:55 +08:00
wangxiaoshuang 74c5d032fb 分割清除内存问题
continuous-integration/drone/push Build is passing Details
2026-04-17 11:49:00 +08:00
wangxiaoshuang 43aa371c36 分段显隐时对应长短径标记同步
continuous-integration/drone/push Build is passing Details
2026-04-17 11:33:35 +08:00
wangxiaoshuang 398ec90fae 分割问题查看、更改按钮位置变更
continuous-integration/drone/push Build is passing Details
2026-04-17 11:28:34 +08:00
wangxiaoshuang 890910a763 分割部分问题修复
continuous-integration/drone/push Build is passing Details
2026-04-17 11:17:25 +08:00
wangxiaoshuang 3b5ebfcba1 1
continuous-integration/drone/push Build is passing Details
2026-04-17 10:00:31 +08:00
wangxiaoshuang b971235164 体数据播放功能修改
continuous-integration/drone/push Build is passing Details
2026-04-16 17:55:33 +08:00
wangxiaoshuang e53a18f212 直方图修改
continuous-integration/drone/push Build is passing Details
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 Details
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 Details
2026-04-16 15:19:37 +08:00
wangxiaoshuang 11000ef9f8 重置问题时清空问题与分割标记绑定关系
continuous-integration/drone/push Build is passing Details
2026-04-16 13:42:49 +08:00
wangxiaoshuang 57be74aea8 部分分割问题修复
continuous-integration/drone/push Build is running Details
2026-04-16 13:39:52 +08:00
wangxiaoshuang 8724eeeafb 分割部分问题解决
continuous-integration/drone/push Build is passing Details
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 Details
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 Details
2026-04-16 09:41:23 +08:00
wangxiaoshuang 7001bc9ec0 分割部分问题解决
continuous-integration/drone/push Build is passing Details
2026-04-15 17:21:19 +08:00
wangxiaoshuang c12833d283 切换分割分组时添加限制
continuous-integration/drone/push Build is passing Details
2026-04-15 16:48:38 +08:00
wangxiaoshuang 22baa213ae 新建若干分段后保存,保存卡死
continuous-integration/drone/push Build is passing Details
2026-04-15 16:00:49 +08:00
wangxiaoshuang 09c1c3ff12 分割部分问题修复
continuous-integration/drone/push Build is passing Details
2026-04-15 15:00:13 +08:00
wangxiaoshuang 94f43f4b11 用橡皮擦除分割标记后,绑定的分割问题显示为-Infinity
continuous-integration/drone/push Build is passing Details
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 Details
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 Details
2026-04-14 21:54:27 +08:00
caiyiling 266698cc79 定圆工具配置及功能实现
continuous-integration/drone/push Build is passing Details
2026-04-14 21:19:00 +08:00
wangxiaoshuang eac6b25b9e 分割部分问题修复
continuous-integration/drone/push Build is passing Details
2026-04-14 18:01:21 +08:00
wangxiaoshuang 47d7fb00e2 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing Details
2026-04-14 16:19:41 +08:00
wangxiaoshuang 312ab754e1 分割标记渲染结构变更 2026-04-14 16:19:36 +08:00
caiyiling 0ff5a21d04 同步列表更改
continuous-integration/drone/push Build is passing Details
2026-04-14 13:05:18 +08:00
wangxiaoshuang 5a7fa1fb77 分割部分问题解决
continuous-integration/drone/push Build is passing Details
2026-04-09 18:08:20 +08:00
wangxiaoshuang 5289c156c0 1
continuous-integration/drone/push Build is passing Details
2026-04-09 17:28:59 +08:00
wangxiaoshuang a68c27fd43 分割部分问题解决
continuous-integration/drone/push Build is running Details
2026-04-09 17:19:12 +08:00
wangxiaoshuang 641c2e6ade 直方图取值及样式修改
continuous-integration/drone/push Build is passing Details
2026-04-08 16:22:29 +08:00
wangxiaoshuang 5b4cc097bc 分割视图添加直方图
continuous-integration/drone/push Build is passing Details
2026-04-08 14:58:26 +08:00
wangxiaoshuang b1de662677 aws插件版本问题
continuous-integration/drone/push Build is passing Details
2026-04-07 17:11:04 +08:00
caiyiling d2d1063ed5 数据同步模块更改
continuous-integration/drone/push Build is passing Details
2026-04-03 17:27:12 +08:00
wangxiaoshuang 2bc4b32d3a Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing Details
2026-04-03 14:02:17 +08:00
wangxiaoshuang 366ec62cd7 1 2026-04-03 14:02:05 +08:00
caiyiling 866e2e5c6b Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is failing Details
2026-04-03 13:49:35 +08:00
caiyiling 16e2db37cd 文件级别列表更改 2026-04-03 13:48:38 +08:00
wangxiaoshuang ee6c5bf8a5 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is pending Details
2026-04-03 13:45:48 +08:00
wangxiaoshuang 48cfe99f12 echarts版本升级v4=>v6 2026-04-03 13:45:39 +08:00
caiyiling f65264a2a6 1
continuous-integration/drone/push Build is running Details
2026-04-03 13:45:02 +08:00
caiyiling 769c5f8440 任务级别列表更改
continuous-integration/drone/push Build is running Details
2026-04-03 13:42:14 +08:00
caiyiling 6321bddfd3 数据同步更改
continuous-integration/drone/push Build is passing Details
2026-04-03 13:33:46 +08:00
caiyiling e30fb2a041 1
continuous-integration/drone/push Build is passing Details
2026-04-02 17:37:24 +08:00
caiyiling 478adcde21 1
continuous-integration/drone/push Build is running Details
2026-04-02 17:35:20 +08:00
caiyiling 34ce5142f2 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is running Details
2026-04-02 17:33:03 +08:00
caiyiling 3b993bcd46 数据同步更改 2026-04-02 17:32:35 +08:00
wangxiaoshuang 3f2e39f46c 当加载Volume体数据时,页面显示的现在速度需要按照Series来计算。现在一直都是0。
continuous-integration/drone/push Build is passing Details
2026-04-02 15:01:32 +08:00
wangxiaoshuang d317402f85 1
continuous-integration/drone/push Build is passing Details
2026-04-02 14:40:24 +08:00
wangxiaoshuang 6cb63bdc59 浏览器窗口大小调整后,渲染的标记、比例尺等位置偏移
continuous-integration/drone/push Build is failing Details
2026-04-02 14:36:15 +08:00
wangxiaoshuang 8db641db16 分割重命名时默认值填入
continuous-integration/drone/push Build is failing Details
2026-04-02 14:07:58 +08:00
wangxiaoshuang 9da9515930 aws插件版本升级3.7.4=>3.726
continuous-integration/drone/push Build is passing Details
2026-04-02 09:33:38 +08:00
wangxiaoshuang 2be119de6b 分割橡皮檫国际化
continuous-integration/drone/push Build is passing Details
2026-04-01 15:23:20 +08:00
wangxiaoshuang c9f9615488 mpr切换单视口时分割列表报错问题
continuous-integration/drone/push Build is passing Details
2026-04-01 14:53:14 +08:00
wangxiaoshuang be137b3840 分割字段变更SegmentMumber=>SegmentNumber
continuous-integration/drone/push Build is passing Details
2026-04-01 12:21:07 +08:00
wangxiaoshuang d80101f86b 分割标记与分割段绑定错误问题
continuous-integration/drone/push Build is passing Details
2026-04-01 11:30:59 +08:00
wangxiaoshuang 3c0b084f7e 分割文件保存分割段与分割标记不一致问题
continuous-integration/drone/push Build is passing Details
2026-04-01 09:58:45 +08:00
wangxiaoshuang 8a88a64c36 比例尺页面放大缩小报错问题
continuous-integration/drone/push Build is failing Details
2026-04-01 09:35:36 +08:00
wangxiaoshuang 3d538d6e67 1
continuous-integration/drone/push Build is passing Details
2026-03-31 17:59:10 +08:00
wangxiaoshuang 67d2a83ec8 1
continuous-integration/drone/push Build is passing Details
2026-03-31 17:38:36 +08:00
wangxiaoshuang 2efb229c96 1
continuous-integration/drone/push Build is passing Details
2026-03-31 17:13:29 +08:00
wangxiaoshuang c930480336 体数据放大变形问题解决
continuous-integration/drone/push Build is passing Details
2026-03-31 16:47:16 +08:00
wangxiaoshuang b39ea3a032 分割标记数据值计算完成后才可保存
continuous-integration/drone/push Build is passing Details
2026-03-31 16:30:37 +08:00
wangxiaoshuang ff5aea17a5 样式修改
continuous-integration/drone/push Build is passing Details
2026-03-31 15:43:34 +08:00
wangxiaoshuang 477b6935ff 分割窗口放大页面跳转与分割片段默认跳转功能互不干扰
continuous-integration/drone/push Build is running Details
2026-03-31 15:41:45 +08:00
wangxiaoshuang 8b6432ad14 分割标记长短径的小数位数需要一致
continuous-integration/drone/push Build is passing Details
2026-03-31 15:23:23 +08:00
wangxiaoshuang ca35fe6df4 部分问题修复
continuous-integration/drone/push Build is passing Details
2026-03-31 15:17:13 +08:00
wangxiaoshuang 307c02725f 分割对象的体素统计以及长短径计算优化、分割组添加状态
continuous-integration/drone/push Build is passing Details
2026-03-31 13:47:06 +08:00
caiyiling 72d0494baa Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2026-03-31 10:07:50 +08:00
caiyiling d1017bc565 MIM关闭医学审核任务逻辑更改 2026-03-31 10:07:17 +08:00
wangxiaoshuang 76e6ef087f 分割标记的颜色需要随机,不能重复
continuous-integration/drone/push Build is passing Details
2026-03-30 17:33:52 +08:00
wangxiaoshuang c5c9e4364c 【dicom分割】分段菜单按钮点击后,再点击另一个分段菜单按钮,上一个展示的弹窗应该消失
continuous-integration/drone/push Build is passing Details
2026-03-30 17:17:17 +08:00
wangxiaoshuang 920d0b21c6 【dicom分割】当前分组选择为分组2,分组2没有标记数据,当切入mpr模式时,此时显示了分组1的标记,这里影像标记应该与右侧分割选择组同步
continuous-integration/drone/push Build is passing Details
2026-03-30 16:50:25 +08:00
wangxiaoshuang 3fe16b4166 体数据阅片四角信息
continuous-integration/drone/push Build is passing Details
2026-03-30 16:19:05 +08:00
wangxiaoshuang bc2d5463ff MPR视图的:十字线和窗宽窗位
continuous-integration/drone/push Build is passing Details
2026-03-30 16:10:56 +08:00
caiyiling c63811d55c Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2026-03-30 15:45:52 +08:00
caiyiling 832fa82d4b 融合伪彩更改 2026-03-30 15:45:16 +08:00
wangxiaoshuang b3ae801fe9 没有分割数据的Segmentation,从二维进入MPR时,会显示3个
continuous-integration/drone/push Build is passing Details
2026-03-30 14:36:43 +08:00
caiyiling 0dbd74270d Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2026-03-30 14:14:18 +08:00
caiyiling aae5968d74 nm/ct融合更改 2026-03-30 14:13:52 +08:00
wangxiaoshuang 79a7ad0cf7 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing Details
# Conflicts:
#	src/views/trials/trials-panel/reading/dicoms3D/components/VolumeViewport.vue
2026-03-30 13:29:16 +08:00
wangxiaoshuang a35309d9b5 普通标记在多个三维序列渲染问题 2026-03-30 13:27:24 +08:00
caiyiling a987ac086b 1
continuous-integration/drone/push Build is passing Details
2026-03-30 10:57:27 +08:00
caiyiling fdc2bc5f8a 1
continuous-integration/drone/push Build is running Details
2026-03-30 10:54:07 +08:00
caiyiling 84f68c79f0 1
continuous-integration/drone/push Build is running Details
2026-03-30 10:52:28 +08:00
caiyiling b77dc34523 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is running Details
2026-03-30 10:49:44 +08:00
caiyiling 91554e0e30 1 2026-03-30 10:48:04 +08:00
wangxiaoshuang 2efb7fd36a 普通标记在多个序列渲染问题
continuous-integration/drone/push Build is passing Details
2026-03-27 18:01:41 +08:00
caiyiling dbdec93efc Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is running Details
2026-03-27 17:59:51 +08:00
caiyiling e631ce73f2 伪彩组件更改 2026-03-27 17:59:35 +08:00
wangxiaoshuang 5931dcdc67 mpr加载
continuous-integration/drone/push Build is passing Details
2026-03-27 16:40:45 +08:00
wangxiaoshuang b55ffc0821 mpr加载
continuous-integration/drone/push Build is passing Details
2026-03-27 16:32:34 +08:00
caiyiling 8600054405 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2026-03-27 16:24:54 +08:00
caiyiling 9a88539294 融合视口调节透明度功能更改 2026-03-27 16:24:24 +08:00
wangxiaoshuang 09211c8841 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing Details
# Conflicts:
#	src/views/trials/trials-panel/reading/dicoms3D/components/PetCtViewport.vue
2026-03-27 15:47:24 +08:00
wangxiaoshuang 2724e0267f 三维调窗、分割保存 2026-03-27 15:46:40 +08:00
caiyiling a0e355d69b Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2026-03-27 13:53:45 +08:00
caiyiling 04a0a1ccc3 SPECT多帧图像融合更改 2026-03-27 13:52:59 +08:00
wangxiaoshuang c3e967acaa 分割标记序号修改
continuous-integration/drone/push Build is passing Details
2026-03-27 10:50:18 +08:00
wangxiaoshuang 740a0aa221 删除分割标记同步问题
continuous-integration/drone/push Build is passing Details
2026-03-27 10:31:06 +08:00
wangxiaoshuang e8f95d09ba 1
continuous-integration/drone/push Build is passing Details
2026-03-26 17:49:39 +08:00
wangxiaoshuang 2c305af49e 【dicom分割】删除已绑定问题的分割标记时,eicrf页面没有刷新状态
continuous-integration/drone/push Build is failing Details
2026-03-26 17:40:22 +08:00
wangxiaoshuang 5c76bda1e7 分割添加阈值工具
continuous-integration/drone/push Build is passing Details
2026-03-26 16:05:34 +08:00
wangxiaoshuang 0273601f7b Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing Details
2026-03-25 17:46:45 +08:00
wangxiaoshuang c3a154f762 分割长短径工具问题修复 2026-03-25 17:46:31 +08:00
caiyiling cdb97f6405 w/l预设模板移除CT Bone1
continuous-integration/drone/push Build is passing Details
2026-03-25 17:31:59 +08:00
caiyiling 4794429a57 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2026-03-25 16:51:31 +08:00
caiyiling 77a633d699 SPECT融合更改 2026-03-25 16:45:44 +08:00
wangxiaoshuang 64d1ebf350 导入分割数据与分段进行绑定
continuous-integration/drone/push Build is passing Details
2026-03-25 16:44:55 +08:00
wangxiaoshuang 5336cb5353 分割标记删除进行提示
continuous-integration/drone/push Build is passing Details
2026-03-25 14:11:52 +08:00
wangxiaoshuang 0a288b9f47 分割标记重命名问题修改
continuous-integration/drone/push Build is passing Details
2026-03-25 14:03:04 +08:00
wangxiaoshuang 674d483cbf Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing Details
2026-03-25 13:52:23 +08:00
wangxiaoshuang cbcb396441 分割标记跳转修改 2026-03-25 13:52:14 +08:00
caiyiling 9a3e972723 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2026-03-25 11:00:20 +08:00
caiyiling b593615e9e Lugano标准融合页面伪彩更改 2026-03-25 10:59:55 +08:00
wangxiaoshuang d30c9a5f3b 图像滚动问题解决
continuous-integration/drone/push Build is passing Details
2026-03-25 09:26:11 +08:00
caiyiling dd3d738eed Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing Details
2026-03-25 09:10:25 +08:00
caiyiling d9edaf4c27 OCT标准FCT平均值计算逻辑更改 2026-03-25 09:09:35 +08:00
wangxiaoshuang a5e0dd89ff 1
continuous-integration/drone/push Build is running Details
2026-03-25 09:07:21 +08:00
wangxiaoshuang 73f8f816b7 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is failing Details
2026-03-24 17:56:10 +08:00
wangxiaoshuang 19d5144b70 问题绑定分割标记后查看 2026-03-24 17:55:57 +08:00
caiyiling e0ecd3f4ac Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is running Details
2026-03-24 17:55:31 +08:00
caiyiling 30e0e374bc 调窗更改 2026-03-24 17:54:59 +08:00
wangxiaoshuang 25b909c660 分割默认工具功能修复
continuous-integration/drone/push Build is passing Details
2026-03-24 16:45:44 +08:00
caiyiling 5cb3e8e606 同步调窗更改
continuous-integration/drone/push Build is passing Details
2026-03-24 15:46:25 +08:00
wangxiaoshuang 619af2e874 分割问题绑定标记显示问题
continuous-integration/drone/push Build is passing Details
2026-03-24 15:27:17 +08:00
wangxiaoshuang 43f1736b05 1
continuous-integration/drone/push Build is passing Details
2026-03-24 15:12:49 +08:00
wangxiaoshuang c9be5678bd Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is running Details
2026-03-24 15:09:51 +08:00
wangxiaoshuang 4e5ecb3938 分割添加轮廓工具 2026-03-24 15:09:45 +08:00
caiyiling 7870c3d74b CT/NM融合更改
continuous-integration/drone/push Build is passing Details
2026-03-24 14:54:46 +08:00
wangxiaoshuang d07adc4948 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing Details
# Conflicts:
#	src/views/trials/trials-panel/reading/dicoms3D/components/ReadPage.vue
#	src/views/trials/trials-panel/reading/dicoms3D/components/StudyList.vue
2026-03-23 15:10:29 +08:00
wangxiaoshuang 266886fa64 阅片添加分割工具 2026-03-23 10:01:22 +08:00
caiyiling e012eba0c2 阅片页面样式更改
continuous-integration/drone/push Build is passing Details
2026-03-19 16:54:25 +08:00
caiyiling 75d296405b cornerstone版本升级
continuous-integration/drone/push Build is passing Details
2026-03-19 15:12:54 +08:00
caiyiling 07c92c48a4 融合图像筛选更改
continuous-integration/drone/push Build is passing Details
2026-03-13 14:11:52 +08:00
hang b5427dd17b 修改打包文件配置
continuous-integration/drone/push Build is passing Details
2026-03-13 13:49:27 +08:00
hang 385643643e uat合并main 导致打包文件被覆盖,重新编写 2026-03-13 13:47:23 +08:00
caiyiling 756f1293aa 融合图像补充模态为NM类型的影像 2026-03-13 13:35:38 +08:00
caiyiling eee19f0ae9 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main 2026-03-13 13:20:27 +08:00
caiyiling 78c0910db7 选择融合图像时扩充NM模态的影像 2026-03-13 13:18:24 +08:00
234 changed files with 30229 additions and 17112 deletions

View File

@ -1,7 +1,7 @@
kind: pipeline kind: pipeline
type: ssh type: ssh
name: ssh-linux-test-irc-publish name: ssh-linux-uat-irc-publish
platform: platform:
os: Linux os: Linux
@ -33,6 +33,38 @@ trigger:
- uat - uat
---
kind: pipeline
type: ssh
name: ssh-linux-test-irc-publish
platform:
os: Linux
arch: 386
clone:
disable: true #禁用默认克隆
server:
host: 106.14.89.110
user: root
password:
from_secret: local_pwd
steps:
- name: publish-test-irc-web
commands:
- bash /opt/1panel/xc-deploy-new/devops-center/build-vue-then-publish.sh IRC_Web main
- name: notify-wecom
commands:
- echo $DRONE_COMMIT_AUTHOR "$DRONE_COMMIT_MESSAGE"
- bash /opt/1panel/xc-deploy-new/devops-center/drone-notify-wecom.sh "$DRONE_BUILD_STATUS" "$DRONE_REPO_NAME" "$DRONE_BRANCH" "$DRONE_BUILD_NUMBER" "4355b98e-1e72-4678-8dfb-2fc6ad0bf449" "$DRONE_COMMIT_MESSAGE" "$DRONE_COMMIT_AUTHOR" "Test_IRC_WEB" "irc.test.extimaging.com"
when:
status:
- success
- failure
trigger:
branch:
- main

11203
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -9,24 +9,25 @@
"build:usa": "vue-cli-service build --mode usa", "build:usa": "vue-cli-service build --mode usa",
"build:usa_prod": "vue-cli-service build --mode usa_prod", "build:usa_prod": "vue-cli-service build --mode usa_prod",
"pre": "vue-cli-service build --mode pre", "pre": "vue-cli-service build --mode pre",
"postinstall": "patch-package",
"i18n": "npm run i18n:zh && npm run i18n:en", "i18n": "npm run i18n:zh && npm run i18n:en",
"i18n:zh": "node i18nGenerate.js lang=zh keyCol=5 valCol=6", "i18n:zh": "node i18nGenerate.js lang=zh keyCol=5 valCol=6",
"i18n:en": "node i18nGenerate.js lang=en keyCol=5 valCol=7" "i18n:en": "node i18nGenerate.js lang=en keyCol=5 valCol=7"
}, },
"dependencies": { "dependencies": {
"@aws-sdk/client-s3": "^3.701.0", "@aws-sdk/client-s3": "3.726.1",
"@cornerstonejs/adapters": "^2.19.7", "@cornerstonejs/adapters": "^4.19.2",
"@cornerstonejs/calculate-suv": "^1.1.0", "@cornerstonejs/calculate-suv": "^1.1.0",
"@cornerstonejs/core": "^2.19.7", "@cornerstonejs/core": "^4.19.2",
"@cornerstonejs/dicom-image-loader": "^2.19.7", "@cornerstonejs/dicom-image-loader": "^4.19.2",
"@cornerstonejs/tools": "^2.19.7", "@cornerstonejs/polymorphic-segmentation": "^4.19.2",
"@cornerstonejs/tools": "^4.19.2",
"@fingerprintjs/fingerprintjs": "^4.6.2", "@fingerprintjs/fingerprintjs": "^4.6.2",
"@icr/polyseg-wasm": "^0.4.0", "@icr/polyseg-wasm": "^0.4.0",
"@microsoft/signalr": "^8.0.7", "@microsoft/signalr": "^8.0.7",
"@riophae/vue-treeselect": "^0.4.0", "@riophae/vue-treeselect": "^0.4.0",
"@vue-office/docx": "^1.6.2", "@vue-office/docx": "^1.6.2",
"@vue-office/excel": "^1.7.11", "@vue-office/excel": "^1.7.11",
"@vue/composition-api": "^1.7.2",
"ali-oss": "^6.17.1", "ali-oss": "^6.17.1",
"axios": "^0.18.1", "axios": "^0.18.1",
"core-js": "^3.8.3", "core-js": "^3.8.3",
@ -38,7 +39,8 @@
"dcmjs": "^0.29.8", "dcmjs": "^0.29.8",
"dicom-parser": "^1.8.9", "dicom-parser": "^1.8.9",
"dicomedit": "^0.1.0", "dicomedit": "^0.1.0",
"echarts": "^4.8.0", "echarts": "^6.0.0",
"echarts-map": "^3.0.1",
"element-ui": "^2.15.14", "element-ui": "^2.15.14",
"exceljs": "^4.4.0", "exceljs": "^4.4.0",
"file-saver": "^2.0.5", "file-saver": "^2.0.5",
@ -60,11 +62,10 @@
"screenfull": "^6.0.2", "screenfull": "^6.0.2",
"sortablejs": "^1.15.5", "sortablejs": "^1.15.5",
"streamsaver": "^2.0.6", "streamsaver": "^2.0.6",
"svg-sprite-loader": "^4.1.3",
"svgo": "^1.2.2", "svgo": "^1.2.2",
"v-viewer": "^1.7.4", "v-viewer": "^1.7.4",
"vcrontab": "^0.3.5", "vcrontab": "^0.3.5",
"vue": "^2.6.14", "vue": "2.7.16",
"vue-clipboard2": "^0.3.3", "vue-clipboard2": "^0.3.3",
"vue-contextmenujs": "^1.4.11", "vue-contextmenujs": "^1.4.11",
"vue-count-to": "^1.0.13", "vue-count-to": "^1.0.13",
@ -95,12 +96,14 @@
"html-webpack-plugin": "^5.6.3", "html-webpack-plugin": "^5.6.3",
"mini-css-extract-plugin": "^2.9.2", "mini-css-extract-plugin": "^2.9.2",
"node-polyfill-webpack-plugin": "^4.0.0", "node-polyfill-webpack-plugin": "^4.0.0",
"patch-package": "^8.0.0",
"path-browserify": "^1.0.1", "path-browserify": "^1.0.1",
"process": "^0.11.10", "process": "^0.11.10",
"sass": "^1.63.2", "sass": "^1.63.2",
"sass-loader": "^10.4.1", "sass-loader": "^16.0.7",
"svg-sprite-loader": "^6.0.11",
"terser-webpack-plugin": "^5.3.10", "terser-webpack-plugin": "^5.3.10",
"vue-template-compiler": "^2.6.14", "vue-template-compiler": "2.7.16",
"webpack": "^5.96.1", "webpack": "^5.96.1",
"webpack-bundle-analyzer": "^4.10.2" "webpack-bundle-analyzer": "^4.10.2"
}, },

View File

@ -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
public/config.js Normal file
View File

@ -0,0 +1,3 @@
window.config = {
timeZone: false
}

View File

@ -67,6 +67,7 @@
num = e.data num = e.data
} }
</script> </script>
<script src="/config.js"></script>
</head> </head>
<body> <body>
<noscript> <noscript>

View File

@ -1,5 +1,5 @@
<template> <template>
<div id="app" style="position: relative"> <div id="app" style="position: relative" :class="{ mask: isLock }">
<router-view /> <router-view />
<div v-show="show" v-if="$route.matched.length > 0" v-adaptive @click="openI18n" style=" <div v-show="show" v-if="$route.matched.length > 0" v-adaptive @click="openI18n" style="
position: fixed; position: fixed;
@ -63,7 +63,7 @@
" size="mini"></el-input> " size="mini"></el-input>
</template> </template>
</el-table-column> </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"> <template slot-scope="scope">
<el-select v-model="scope.row.State" clearable filterable size="mini"> <el-select v-model="scope.row.State" clearable filterable size="mini">
<el-option v-for="item of $d.InternationalizationKeyState" <el-option v-for="item of $d.InternationalizationKeyState"
@ -83,7 +83,7 @@
</div> </div>
</el-drawer> </el-drawer>
<feedBack v-if="$route.matched.length > 0" /> <feedBack v-if="$route.matched.length > 0" />
<!-- <timeTag /> --> <timeTag v-if="config.timeZone" />
</div> </div>
</template> </template>
@ -94,14 +94,15 @@ import {
} from '@/api/dictionary/dictionary' } from '@/api/dictionary/dictionary'
import { getTrialExtralConfig } from '@/api/trials' import { getTrialExtralConfig } from '@/api/trials'
import feedBack from '@/views/trials/trials-layout/components/feedBack' import feedBack from '@/views/trials/trials-layout/components/feedBack'
// import timeTag from '@/components/timeTag' import timeTag from '@/components/timeTag'
import Vue from 'vue' import Vue from 'vue'
import i18n from './lang' import i18n from './lang'
import DicomEvent from '@/views/trials/trials-panel/reading/dicoms/components/DicomEvent'
export default { export default {
name: 'App', name: 'App',
components: { components: {
feedBack, feedBack,
// timeTag timeTag
}, },
data() { data() {
return { return {
@ -112,12 +113,17 @@ export default {
arr: [], arr: [],
il8nExternal: false, il8nExternal: false,
State: null, State: null,
config: window.config,
isLock: false
} }
}, },
mounted() { mounted() {
// this.show = process.env.VUE_APP_OSS_PATH === '/test/dist' this.show = process.env.VUE_APP_OSS_PATH === '/test/dist'
this.show = false
Vue.prototype.$openI18n = this.openI18n Vue.prototype.$openI18n = this.openI18n
// this.getIsLock()
// DicomEvent.$on("isLock", (isLock) => {
// this.isLock = isLock
// })
}, },
// watch: { // watch: {
// '$route.query': { // '$route.query': {
@ -149,17 +155,30 @@ export default {
// }, // },
// }, // },
methods: { methods: {
getIsLock() {
if (zzSessionStorage.getItem('isLock') === 'true') {
this.isLock = true
} else {
this.isLock = false
}
},
// //
handleSortByColumn(column) { handleSortByColumn(column) {
if (column.order === 'ascending') { const { prop, order } = column
this.tableData.sort((a, b) => if (!prop || !order) {
a[column.prop].localeCompare(b[column.prop]) this.tableData = [...this.tableData]
) return
} else {
this.tableData.sort((a, b) =>
b[column.prop].localeCompare(a[column.prop])
)
} }
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() { handleStateChange() {
this.tableData.forEach((item) => { this.tableData.forEach((item) => {
@ -301,6 +320,23 @@ export default {
</script> </script>
<style lang="scss"> <style lang="scss">
.mask {
position: relative;
filter: blur(2px);
user-select: none;
}
.mask::after {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
content: '';
display: block;
background: rgba(255, 253, 253, 0.2);
}
$light_gray: #606266; $light_gray: #606266;
.el-tooltip__popper { .el-tooltip__popper {

View File

@ -391,3 +391,35 @@ export function getReSendEmail(data) {
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
})
}

41
src/api/file.js Normal file
View File

@ -0,0 +1,41 @@
import request from '@/utils/request'
export function addOrUpdateFileUploadRecord(params) {
return request({
url: `/FileUploadRecord/addOrUpdateFileUploadRecord`,
method: 'post',
data: params
})
}
export function batchAddSyncFileTask(params) {
return request({
url: `/FileUploadRecord/batchAddSyncFileTask`,
method: 'post',
data: params
})
}
export function getSubjectUploadRecordList(params) {
return request({
url: `/FileUploadRecord/getSubjectUploadRecordList`,
method: 'post',
data: params
})
}
export function getFileUploadRecordList(params) {
return request({
url: `/FileUploadRecord/getFileUploadRecordList`,
method: 'post',
data: params
})
}
export function getUploadFileSyncRecordList(params) {
return request({
url: `/FileUploadRecord/getUploadFileSyncRecordList`,
method: 'post',
data: params
})
}

View File

@ -319,3 +319,132 @@ export function getReportsChartData(param) {
data: param data: param
}) })
} }
// 分割相关
// 获取分割组
export function getSegmentationList(param) {
return request({
url: `/Segmentation/getSegmentationList`,
method: 'post',
data: param
})
}
// 新增修改分割组
export function addOrUpdateSegmentation(param) {
return request({
url: `/Segmentation/addOrUpdateSegmentation`,
method: 'post',
data: param
})
}
// 删除分割组
export function deleteSegmentation(segmentationId) {
return request({
url: `/Segmentation/deleteSegmentation/${segmentationId}`,
method: 'delete'
})
}
// 获取分割
export function getSegmentList(param) {
return request({
url: `/Segmentation/getSegmentList`,
method: 'post',
data: param
})
}
// 新增修改分割
export function addOrUpdateSegment(param) {
return request({
url: `/Segmentation/addOrUpdateSegment`,
method: 'post',
data: param
})
}
// 删除分割
export function deleteSegment(segmentId) {
return request({
url: `/Segmentation/deleteSegment/${segmentId}`,
method: 'delete'
})
}
// 获取分割绑定关系
export function getSegmentBindingList(param) {
return request({
url: `/Segmentation/getSegmentBindingList`,
method: 'post',
data: param
})
}
// 保存分割绑定关系(可用作修改删除)
export function saveSegmentBindingAndAnswer(param) {
return request({
url: `/Segmentation/saveSegmentBindingAndAnswer`,
method: 'post',
data: param
})
}
// 获取表格问题配置
export function getReadingTableQuestionTrialById(params) {
return request({
url: `/ReadingQuestion/getReadingTableQuestionTrialById`,
method: 'post',
params
})
}
// 获取外层问题配置
export function getReadingQuestionTrialById(params) {
return request({
url: `/ReadingQuestion/getReadingQuestionTrialById`,
method: 'post',
params
})
}
// 修改分割组保存状态
export function changeSegmentationSavedStatus(data) {
return request({
url: `/Segmentation/changeSegmentationSavedStatus`,
method: 'post',
data
})
}
// 修改分割片段锁定状态
export function lockOrUnLockSegment(data) {
return request({
url: `/Segmentation/lockOrUnLockSegment`,
method: 'post',
data
})
}
// 图像数据匿名
export function studyMaskImage(data) {
return request({
url: `/Study/studyMaskImage`,
method: 'post',
data
})
}
// 撤销匿名
export function studyUndoMaskImage(data) {
return request({
url: `/Study/studyUndoMaskImage`,
method: 'post',
data
})
}
// 获取分割组历史版本
export function getSegmentationVersionList(data) {
return request({
url: `/Segmentation/getSegmentationVersionList`,
method: 'post',
data
})
}
// 恢复分割历史版本
export function restoreSegmentationVersion(data) {
return request({
url: `/Segmentation/restoreSegmentationVersion`,
method: 'post',
data
})
}

View File

@ -4431,3 +4431,47 @@ export function updateImageResizePath(data) {
data data
}) })
} }
// 获取PET图像上患者信息
export function getPatientInfo(data) {
return request({
url: `/Study/getPatientInfo`,
method: 'post',
data
})
}
//编辑患者基本信息
export function editPatientInfo(data) {
return request({
url: `/Study/editPatientInfo`,
method: 'post',
data
})
}
//修正患者基本信息
export function amendmentPatientInfo(data) {
return request({
url: `/Inspection/Study/amendmentPatientInfo`,
method: 'post',
data
})
}
//修改临床数据后 将签名状态变更为未签名
export function updateReadModuleClinicalData(data) {
return request({
url: `/ReadingClinicalData/updateReadModuleClinicalData`,
method: 'post',
data
})
}
export function getTrialUnreadVisitList(params) {
return request({
url: `/DownloadAndUpload/getTrialUnreadVisitList`,
method: 'get',
params,
})
}

View File

@ -6,6 +6,7 @@
<div>Series #{{ dicomInfo.series }}</div> <div>Series #{{ dicomInfo.series }}</div>
<div>Image #{{ dicomInfo.frame }}</div> <div>Image #{{ dicomInfo.frame }}</div>
<div>{{ dicomInfo.modality }}</div> <div>{{ dicomInfo.modality }}</div>
<div v-if="isComparison" style="font-size: 30px;color: #428bca;">{{ tip }}</div>
</div> </div>
<div v-show="dicomInfo.series" class="info-image"> <div v-show="dicomInfo.series" class="info-image">
<!-- <div> <!-- <div>
@ -15,7 +16,7 @@
<div v-show="dicomInfo.thick">Slice Thickness {{ dicomInfo.thick }}mm</div> <div v-show="dicomInfo.thick">Slice Thickness {{ dicomInfo.thick }}mm</div>
<div>WW/WC {{ dicomInfo.wwwc }}</div> <div>WW/WC {{ dicomInfo.wwwc }}</div>
<div>Zoom {{ dicomInfo.zoom }}</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="toolState.clipPlaying">FPS {{ dicomInfo.fps }}</div> -->
<div v-show="mousePosition.mo"> <div v-show="mousePosition.mo">
Pos: {{ mousePosition.x ? mousePosition.x.toFixed(0) : '' }}, {{ mousePosition.y ? mousePosition.y.toFixed(0) : Pos: {{ mousePosition.x ? mousePosition.x.toFixed(0) : '' }}, {{ mousePosition.y ? mousePosition.y.toFixed(0) :
@ -23,17 +24,11 @@
</div> </div>
<div <div
v-if="(dicomInfo.modality === 'CT' || dicomInfo.modality === 'DR' || dicomInfo.modality === 'CR') && mousePosition.mo"> v-if="(dicomInfo.modality === 'CT' || dicomInfo.modality === 'DR' || dicomInfo.modality === 'CR') && mousePosition.mo">
HU: {{ mousePosition.mo }} HU: {{ mousePosition.mo }}</div>
</div> <div v-else-if="(dicomInfo.modality === 'PT' && mousePosition.suv)">SUVbw(g/ml): {{ mousePosition.suv.toFixed(3)
<div v-else-if="(dicomInfo.modality === 'PT' && mousePosition.suv)"> }}</div>
SUVbw(g/ml): {{ mousePosition.suv.toFixed(3) }} <div v-else-if="mousePosition.mo">Density: {{ mousePosition.mo }}</div>
</div> <div>W*H: {{ dicomInfo.size }}</div>
<div v-else-if="mousePosition.mo">
Density: {{ mousePosition.mo }}
</div>
<div>
W*H: {{ dicomInfo.size }}
</div>
<div>Zoom: {{ dicomInfo.zoom }}</div> <div>Zoom: {{ dicomInfo.zoom }}</div>
</div> </div>
@ -56,24 +51,17 @@
style="z-index:10;background: #9e9e9e;height: 20px;width: 100%;position: absolute;top: 0;cursor: move" style="z-index:10;background: #9e9e9e;height: 20px;width: 100%;position: absolute;top: 0;cursor: move"
@mousedown="sliderMousedown($event)" /> @mousedown="sliderMousedown($event)" />
</div> </div>
<div style="position: absolute;left: 50%;top: 15px;color: #f44336;"> <div style="position: absolute;left: 50%;top: 15px;color: #f44336;">{{ markers.top }}</div>
{{ markers.top }} <div style="position: absolute;top: 50%;right: 15px;color: #f44336;">{{ markers.right }}</div>
</div>
<div style="position: absolute;top: 50%;right: 15px;color: #f44336;">
{{ markers.right }}
</div>
<div style="position: absolute;left: 50%;bottom: 15px;color: #f44336;"> <div style="position: absolute;left: 50%;bottom: 15px;color: #f44336;">{{ markers.bottom }}</div>
{{ markers.bottom }} <div style="position: absolute;top: 50%;left: 15px;color: #f44336;">{{ markers.left }}</div>
</div>
<div style="position: absolute;top: 50%;left: 15px;color: #f44336;">
{{ markers.left }}
</div>
<div class="info-instance"> <div class="info-instance">
<svg-icon icon-class="IsMasked" style="font-size:20px;" v-show="dicomInfo.IsMasked" />
<!-- <div v-show="dicomInfo.pixel"> <!-- <div v-show="dicomInfo.pixel">
Pixel: {{ dicomInfo.pixel }}mm Pixel: {{ dicomInfo.pixel }}mm
</div> --> </div>-->
<div v-show="dicomInfo.location">Location: {{ dicomInfo.location }}</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.thick">Slice Thickness: {{ dicomInfo.thick }}mm</div>
<div v-show="dicomInfo.wwwc">WW/WL: {{ dicomInfo.wwwc }}</div> <div v-show="dicomInfo.wwwc">WW/WL: {{ dicomInfo.wwwc }}</div>
@ -92,15 +80,18 @@
import * as cornerstone from 'cornerstone-core' import * as cornerstone from 'cornerstone-core'
import * as cornerstoneMath from 'cornerstone-math' import * as cornerstoneMath from 'cornerstone-math'
import * as cornerstoneTools from 'cornerstone-tools' import * as cornerstoneTools from 'cornerstone-tools'
import metaDataProvider from '@/utils/metaDataProvider'
const scroll = cornerstoneTools.import('util/scrollToIndex') const scroll = cornerstoneTools.import('util/scrollToIndex')
import Hammer from 'hammerjs' import Hammer from 'hammerjs'
import getOrientationString from '@/views/trials/trials-panel/reading/dicoms/tools/OrientationMarkers/getOrientationString' 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 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 requestPoolManager from '@/utils/request-pool'
import ScaleOverlayTool from '@/views/trials/trials-panel/reading/dicoms/tools/ScaleOverlay/ScaleOverlayTool' 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.cornerstone = cornerstone
cornerstoneTools.external.Hammer = Hammer cornerstoneTools.external.Hammer = Hammer
cornerstoneTools.external.cornerstoneMath = cornerstoneMath cornerstoneTools.external.cornerstoneMath = cornerstoneMath
@ -110,6 +101,7 @@ cornerstoneTools.toolColors.setActiveColor('rgb(0, 255, 0)')
// cornerstoneTools.init({ showSVGCursors: true }) // cornerstoneTools.init({ showSVGCursors: true })
cornerstoneTools.init() cornerstoneTools.init()
const ToolStateManager = cornerstoneTools.globalImageIdSpecificToolStateManager const ToolStateManager = cornerstoneTools.globalImageIdSpecificToolStateManager
console.log(cornerstoneTools, 'cornerstoneTools')
import DicomTags from './DicomTags' import DicomTags from './DicomTags'
export default { export default {
name: 'DicomCanvas', name: 'DicomCanvas',
@ -117,6 +109,12 @@ export default {
computed: { computed: {
NSTip() { NSTip() {
return `${this.$store.state.trials.downloadSize}, NS: ${this.$store.state.trials.downloadTip}` return `${this.$store.state.trials.downloadSize}, NS: ${this.$store.state.trials.downloadTip}`
},
},
props: {
isComparison: {
type: Boolean,
default: false
} }
}, },
data() { data() {
@ -130,7 +128,7 @@ export default {
seriesNumber: '', seriesNumber: '',
imageIds: [], imageIds: [],
currentImageIdIndex: 0, currentImageIdIndex: 0,
firstImageLoading: false firstImageLoading: false,
// preventCache: true // preventCache: true
}, },
dicomInfo: { dicomInfo: {
@ -150,14 +148,15 @@ export default {
wwwc: '', wwwc: '',
zoom: 0, zoom: 0,
location: '', location: '',
fps: 5 fps: 5,
IsMasked: false
}, },
toolState: { toolState: {
initialized: false, initialized: false,
activeTool: 'none', activeTool: 'none',
dicomInfoVisible: false, dicomInfoVisible: false,
clipPlaying: false, clipPlaying: false,
viewportInvert: false viewportInvert: false,
}, },
loadImagePromise: null, loadImagePromise: null,
AnnotationSync: null, AnnotationSync: null,
@ -168,18 +167,21 @@ export default {
sliderInfo: { sliderInfo: {
oldB: null, oldB: null,
oldM: null, oldM: null,
isMove: false isMove: false,
}, },
mousePosition: { x: '', y: '', mo: '' }, mousePosition: { x: '', y: '', mo: '' },
markers: { top: '', right: '', bottom: '', left: '' }, markers: { top: '', right: '', bottom: '', left: '' },
orientationMarkers: [], orientationMarkers: [],
originalMarkers: [], originalMarkers: [],
dcmTag: { visible: false, title: this.$t('trials:dicom-tag:title') } dcmTag: { visible: false, title: this.$t('trials:dicom-tag:title') },
tip: ''
} }
}, },
mounted() { mounted() {
this.type = this.$router.currentRoute.query.type ? this.$router.currentRoute.query.type : '' this.type = this.$router.currentRoute.query.type
? this.$router.currentRoute.query.type
: ''
this.canvas = this.$refs.canvas this.canvas = this.$refs.canvas
this.canvas.addEventListener('cornerstonenewimage', this.onNewImage) this.canvas.addEventListener('cornerstonenewimage', this.onNewImage)
this.canvas.addEventListener( this.canvas.addEventListener(
@ -200,17 +202,28 @@ export default {
document.addEventListener('mousemove', (e) => { document.addEventListener('mousemove', (e) => {
this.sliderMousemove(e) this.sliderMousemove(e)
}) })
this.canvas.addEventListener('cornerstonetoolsstackscroll', this.stackScrollCallback) this.canvas.addEventListener(
'cornerstonetoolsstackscroll',
this.stackScrollCallback
)
}, },
methods: { methods: {
loadImageStack(dicomSeries) { loadImageStack(dicomSeries, text = '') {
this.tip = text
this.$nextTick(() => { this.$nextTick(() => {
if (!dicomSeries || !Array.isArray(dicomSeries.imageIds) || dicomSeries.imageIds.length === 0) {
return
}
this.series = dicomSeries this.series = dicomSeries
this.stack.seriesId = dicomSeries.seriesId this.stack.seriesId = dicomSeries.seriesId
this.stack.seriesNumber = dicomSeries.seriesNumber this.stack.seriesNumber = dicomSeries.seriesNumber
this.stack.imageIds = dicomSeries.imageIds 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.firstImageLoading = true
this.stack.description = dicomSeries.description this.stack.description = dicomSeries.description
this.toolState.viewportInvert = false this.toolState.viewportInvert = false
@ -221,18 +234,27 @@ export default {
// this.stack.instanceId = instanceId // this.stack.instanceId = instanceId
this.toolState.clipPlaying = false this.toolState.clipPlaying = false
const element = this.$refs.canvas const element = this.$refs.canvas
if (!isMetaDataProviderAdded) {
// metaDataProvider SUV 退 DICOM
cornerstone.metaData.addProvider(metaDataProvider, 100000)
isMetaDataProviderAdded = true
}
cornerstone.enable(element) cornerstone.enable(element)
cornerstoneTools.stopClip(this.canvas) cornerstoneTools.stopClip(this.canvas)
this.toolState.clipPlaying = false this.toolState.clipPlaying = false
this.loading = true this.loading = true
cornerstone.loadAndCacheImage(this.stack.imageIds[this.stack.currentImageIdIndex]) cornerstone
.then(image => { .loadAndCacheImage(
this.stack.imageIds[this.stack.currentImageIdIndex]
)
.then((image) => {
this.loading = false this.loading = false
if (this.stack.imageIds.indexOf(image.imageId) !== -1) { if (this.stack.imageIds.indexOf(image.imageId) !== -1) {
this.onFirstImageLoaded(image) this.onFirstImageLoaded(image)
} }
}).catch((error) => { })
.catch((error) => {
this.loading = false this.loading = false
if (error.error && error.error.message) { if (error.error && error.error.message) {
this.$alert(error.error.message) this.$alert(error.error.message)
@ -255,15 +277,17 @@ export default {
const apiTool = cornerstoneTools[`${toolName}Tool`] const apiTool = cornerstoneTools[`${toolName}Tool`]
if (apiTool) { if (apiTool) {
const toolAlreadyAddedToElement = cornerstoneTools.getToolForElement( const toolAlreadyAddedToElement =
element, cornerstoneTools.getToolForElement(element, apiTool)
apiTool
)
if (!toolAlreadyAddedToElement) { if (!toolAlreadyAddedToElement) {
if (toolName === 'RectangleRoi') { 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') { } else if (toolName === 'EllipticalRoi') {
cornerstoneTools.addToolForElement(element, apiTool, { configuration: { showMinMax: true } }) cornerstoneTools.addToolForElement(element, apiTool, {
configuration: { showMinMax: true },
})
} else { } else {
cornerstoneTools.addToolForElement(element, apiTool) cornerstoneTools.addToolForElement(element, apiTool)
} }
@ -287,8 +311,28 @@ export default {
false false
) )
}) })
if (!cornerstoneTools.getToolForElement(element, cornerstoneTools.WwwcRegionTool)) { if (
cornerstoneTools.addToolForElement(element, cornerstoneTools.WwwcRegionTool) !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 ( if (
!cornerstoneTools.getToolForElement( !cornerstoneTools.getToolForElement(
@ -347,20 +391,27 @@ export default {
// var instanceId = image.imageId.split('/')[image.imageId.split('/').length - 1] // var instanceId = image.imageId.split('/')[image.imageId.split('/').length - 1]
// instanceId = instanceId.split('.')[0] // instanceId = instanceId.split('.')[0]
// this.stack.instanceId = instanceId // this.stack.instanceId = instanceId
this.height = (this.stack.currentImageIdIndex) * 100 / (this.stack.imageIds.length - 1) this.height = this.getStackHeightPercent()
this.resetWwwc() this.resetWwwc()
}, },
onNewImage(e) { onNewImage(e) {
e.detail.enabledElement.options = {} e.detail.enabledElement.options = {}
var data = e.detail.image.data var data = e.detail.image.data
this.dicomInfo.hospital = data.string('x00080080') 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('x00100020')
this.dicomInfo.pid = data.string('x00120040') this.dicomInfo.pid = data.string('x00120040')
this.dicomInfo.name = data.string('x00100010') this.dicomInfo.name = data.string('x00100010')
this.dicomInfo.age = data.string('x00101010') this.dicomInfo.age = data.string('x00101010')
this.dicomInfo.sex = data.string('x00100040') this.dicomInfo.sex = data.string('x00100040')
this.dicomInfo.acc = data.string('x00080050') // this.dicomInfo.acc = data.string('x00080050') //
this.dicomInfo.modality = data.string('x00080060') this.dicomInfo.modality = (data.string('x00080060') || '').trim()
this.dicomInfo.time = this.formatDicomDateTime( this.dicomInfo.time = this.formatDicomDateTime(
data.string('x00080020'), data.string('x00080020'),
data.string('x00080030') data.string('x00080030')
@ -379,21 +430,32 @@ export default {
if (this.dicomInfo.thick) { if (this.dicomInfo.thick) {
this.dicomInfo.thick = this.dicomInfo.thick.toFixed(2) 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 if (newImageIdIndex === -1) return
this.stack.currentImageIdIndex = newImageIdIndex this.stack.currentImageIdIndex = newImageIdIndex
this.stack.imageIdIndex = newImageIdIndex this.stack.imageIdIndex = newImageIdIndex
this.series.imageIdIndex = newImageIdIndex this.series.imageIdIndex = newImageIdIndex
this.height = (this.stack.currentImageIdIndex) * 100 / (this.stack.imageIds.length - 1) this.height = this.getStackHeightPercent()
this.resetWwwc() },
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) { stackScrollCallback(e) {
const { detail } = e const { detail } = e
if (this.isScrollSync) { 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.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() // var priority = new Date(new Date().setHours(23, 59, 59, 999)).getTime()
// requestPoolManager.loadAndCacheImagePlus(this.stack.imageIds[this.stack.currentImageIdIndex], this.stack.seriesId, priority) // requestPoolManager.loadAndCacheImagePlus(this.stack.imageIds[this.stack.currentImageIdIndex], this.stack.seriesId, priority)
@ -408,7 +470,9 @@ export default {
if (date) { if (date) {
date = `${date.substr(0, 4)}-${date.substr(4, 2)}-${date.substr(6, 2)}` 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` return time ? `${date} ${time}` : `${date} 00:00:00`
}, },
@ -453,7 +517,7 @@ export default {
top: oppositeColumn, top: oppositeColumn,
bottom: column, bottom: column,
left: oppositeRow, left: oppositeRow,
right: row right: row,
} }
if (!markers) { if (!markers) {
return return
@ -500,13 +564,7 @@ export default {
if (image.color) { if (image.color) {
stats.storedPixels = this.getRGBPixels(element, x, y, 1, 1) stats.storedPixels = this.getRGBPixels(element, x, y, 1, 1)
} else { } else {
stats.storedPixels = cornerstone.getStoredPixels( stats.storedPixels = cornerstone.getStoredPixels(element, x, y, 1, 1)
element,
x,
y,
1,
1
)
stats.sp = stats.storedPixels[0] stats.sp = stats.storedPixels[0]
stats.mo = stats.sp * image.slope + image.intercept stats.mo = stats.sp * image.slope + image.intercept
stats.suv = calculateSUV(image, stats.sp) stats.suv = calculateSUV(image, stats.sp)
@ -533,7 +591,8 @@ export default {
if (enabledElement.image.color) { if (enabledElement.image.color) {
for (row = 0; row < height; row++) { for (row = 0; row < height; row++) {
for (column = 0; column < width; column++) { 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 red = pixelData[spIndex]
const green = pixelData[spIndex + 1] const green = pixelData[spIndex + 1]
const blue = pixelData[spIndex + 2] const blue = pixelData[spIndex + 2]
@ -551,7 +610,8 @@ export default {
}, },
sliderMousedown(e) { sliderMousedown(e) {
var boxHeight = this.$refs['sliderBox'].clientHeight 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.oldM = e.clientY
this.sliderInfo.isMove = true this.sliderInfo.isMove = true
e.stopImmediatePropagation() e.stopImmediatePropagation()
@ -564,9 +624,14 @@ export default {
var boxHeight = this.$refs['sliderBox'].clientHeight var boxHeight = this.$refs['sliderBox'].clientHeight
if (PX < 0) return if (PX < 0) return
if (PX > boxHeight) return if (PX > boxHeight) return
var height = PX * 100 / boxHeight var height = (PX * 100) / boxHeight
var index = Math.trunc(this.stack.imageIds.length * this.height / 100) 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 index =
index > this.stack.imageIds.length
? this.stack.imageIds.length
: index < 0
? 0
: index
// if (!cornerstone.imageCache.getImageLoadObject(this.stack.imageIds[index])) return // if (!cornerstone.imageCache.getImageLoadObject(this.stack.imageIds[index])) return
this.height = height this.height = height
if (this.stack.currentImageIdIndex !== index) { if (this.stack.currentImageIdIndex !== index) {
@ -578,9 +643,9 @@ export default {
}, },
goViewer(e) { goViewer(e) {
// console.log(this.$refs['sliderBox'].clientHeight) // 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 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) scroll(this.canvas, index)
}, },
onClipStopped() { onClipStopped() {
@ -673,10 +738,7 @@ export default {
} }
this.toolState.clipPlaying = true this.toolState.clipPlaying = true
cornerstoneTools.playClip(this.canvas, this.dicomInfo.fps) cornerstoneTools.playClip(this.canvas, this.dicomInfo.fps)
cornerstoneTools.getToolState( cornerstoneTools.getToolState(this.canvas, 'playClip').data[0].loop = true
this.canvas,
'playClip'
).data[0].loop = true
}, },
setFps(fps) { setFps(fps) {
this.dicomInfo.fps = fps this.dicomInfo.fps = fps
@ -687,6 +749,7 @@ export default {
var viewport = cornerstone.getViewport(this.canvas) var viewport = cornerstone.getViewport(this.canvas)
// viewport.invert = false // viewport.invert = false
var image = cornerstone.getImage(this.canvas) var image = cornerstone.getImage(this.canvas)
if (!viewport || !image) return
viewport.voi.windowWidth = image.windowWidth viewport.voi.windowWidth = image.windowWidth
viewport.voi.windowCenter = image.windowCenter viewport.voi.windowCenter = image.windowCenter
cornerstone.setViewport(this.canvas, viewport) cornerstone.setViewport(this.canvas, viewport)
@ -712,8 +775,8 @@ export default {
invert: false, invert: false,
preventZoomOutsideImage: false, preventZoomOutsideImage: false,
minScale: 0.1, minScale: 0.1,
maxScale: 20.0 maxScale: 20.0,
} },
}) })
cornerstoneTools.setToolActive('Zoom', { mouseButtonMask: 1 }) cornerstoneTools.setToolActive('Zoom', { mouseButtonMask: 1 })
this.toolState.activeTool = 'zoom' this.toolState.activeTool = 'zoom'
@ -797,9 +860,120 @@ export default {
setToolPassive(toolName) { setToolPassive(toolName) {
cornerstoneTools.setToolPassiveForElement(this.canvas, toolName) cornerstoneTools.setToolPassiveForElement(this.canvas, toolName)
}, },
async reloadImage(newImageId = null) {
// 1. imageIdimageId
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) { setToolActive(toolName) {
cornerstoneTools.setToolActiveForElement(this.canvas, toolName, { cornerstoneTools.setToolActiveForElement(this.canvas, toolName, {
mouseButtonMask: 1 mouseButtonMask: 1,
}) })
}, },
setAllToolsPassive() { setAllToolsPassive() {
@ -833,7 +1007,7 @@ export default {
) )
} }
cornerstoneTools.setToolEnabledForElement(this.canvas, 'ReferenceLines', { cornerstoneTools.setToolEnabledForElement(this.canvas, 'ReferenceLines', {
synchronizationContext: synchronizer synchronizationContext: synchronizer,
}) })
// cornerstoneTools.addTool(cornerstoneTools.CrosshairsTool) // cornerstoneTools.addTool(cornerstoneTools.CrosshairsTool)
@ -864,7 +1038,7 @@ export default {
} }
cornerstoneTools.setToolActiveForElement(this.canvas, toolName, { cornerstoneTools.setToolActiveForElement(this.canvas, toolName, {
mouseButtonMask: 1, mouseButtonMask: 1,
synchronizationContext: synchronizer synchronizationContext: synchronizer,
}) })
}, },
disabledViewPortToolSync(synchronizer, toolName) { disabledViewPortToolSync(synchronizer, toolName) {
@ -1095,7 +1269,7 @@ export default {
'CobbAngle', 'CobbAngle',
'Angle', 'Angle',
'Bidirectional', 'Bidirectional',
'FreehandRoi' 'FreehandRoi',
] ]
for (let i = 0; i < toolROITypes.length; i++) { for (let i = 0; i < toolROITypes.length; i++) {
const toolROIType = toolROITypes[i] const toolROIType = toolROITypes[i]
@ -1115,12 +1289,12 @@ export default {
removeLabel(item) { removeLabel(item) {
const promise = scroll(this.canvas, item.data.imageIdIndex) const promise = scroll(this.canvas, item.data.imageIdIndex)
const scope = this const scope = this
Promise.all([promise]).then(res => { Promise.all([promise]).then((res) => {
cornerstoneTools.removeToolState(scope.canvas, item.type, item.data) cornerstoneTools.removeToolState(scope.canvas, item.type, item.data)
cornerstone.updateImage(scope.canvas) cornerstone.updateImage(scope.canvas)
}) })
} },
} },
} }
</script> </script>

File diff suppressed because it is too large Load Diff

View File

@ -74,14 +74,16 @@ export default {
zzSessionStorage.setItem('lastWorkbench', `${v.path}${query ? '?' : ''}${query}`) zzSessionStorage.setItem('lastWorkbench', `${v.path}${query ? '?' : ''}${query}`)
var firstGoIn = this.trialsRouter.children.find(v => { return v.name === 'TrialsPanel' }).children[0] var firstGoIn = this.trialsRouter.children.find(v => { return v.name === 'TrialsPanel' }).children[0]
if (this.trialsTab === '/trials/trials-panel') { if (this.trialsTab === '/trials/trials-panel') {
this.$router.replace({ path: `${firstGoIn.path}${query ? '?' : ''}${query}` }) var targetPath = firstGoIn.path
if (firstGoIn.children && firstGoIn.children.length > 0) { if (firstGoIn.children && firstGoIn.children.length > 0) {
this.$router.replace({ path: `${firstGoIn.children[0].path}${query ? '?' : ''}${query}` }) targetPath = firstGoIn.children[0].path
} }
this.$router.replace({ path: `${targetPath}${query ? '?' : ''}${query}` })
} }
// document.cookie = 'TrialId=' + this.$route.query.trialId + ';path=/' // document.cookie = 'TrialId=' + this.$route.query.trialId + ';path=/'
}, },
trialId(v) { trialId(v) {
if (!this.$route.query.trialId) return
getUserDocumentList({ TrialId: this.$route.query.trialId }).then(async res => { getUserDocumentList({ TrialId: this.$route.query.trialId }).then(async res => {
var total = res.OtherInfo.NeedSignCount var total = res.OtherInfo.NeedSignCount
var TrialStatusStr = res.OtherInfo.TrialStatusStr var TrialStatusStr = res.OtherInfo.TrialStatusStr
@ -99,6 +101,7 @@ export default {
if (~url.indexOf('?')) { if (~url.indexOf('?')) {
query = url.split('?')[1] query = url.split('?')[1]
} }
if (this.$route.query.trialId) {
getUserDocumentList({ TrialId: this.$route.query.trialId }).then(async res => { getUserDocumentList({ TrialId: this.$route.query.trialId }).then(async res => {
var total = res.OtherInfo.NeedSignCount var total = res.OtherInfo.NeedSignCount
var TrialStatusStr = res.OtherInfo.TrialStatusStr var TrialStatusStr = res.OtherInfo.TrialStatusStr
@ -108,14 +111,16 @@ export default {
await store.dispatch('user/setTrialStatusStr', TrialStatusStr) await store.dispatch('user/setTrialStatusStr', TrialStatusStr)
this.TrialConfig = res.OtherInfo.TrialConfig this.TrialConfig = res.OtherInfo.TrialConfig
}) })
}
this.selectedTab() this.selectedTab()
this.getTrialList() this.getTrialList()
var firstGoIn = this.trialsRouter.children.find(v => { return v.name === 'TrialsPanel' }).children[0] var firstGoIn = this.trialsRouter.children.find(v => { return v.name === 'TrialsPanel' }).children[0]
if (this.trialsTab === '/trials/trials-panel') { if (this.trialsTab === '/trials/trials-panel') {
this.$router.replace({ path: `${firstGoIn.path}${query ? '?' : ''}${query}` }) var targetPath = firstGoIn.path
if (firstGoIn.children && firstGoIn.children.length > 0) { if (firstGoIn.children && firstGoIn.children.length > 0) {
this.$router.replace({ path: `${firstGoIn.children[0].path}${query ? '?' : ''}${query}` }) targetPath = firstGoIn.children[0].path
} }
this.$router.replace({ path: `${targetPath}${query ? '?' : ''}${query}` })
} }
}, },
methods: { methods: {
@ -130,6 +135,9 @@ export default {
let chartList = [ let chartList = [
'/trials/trials-panel/trial-summary/report-forms' '/trials/trials-panel/trial-summary/report-forms'
] ]
let dataSyncList = [
`/trials/trials-panel/trial-summary/data-sync`
]
var qualityList = [ var qualityList = [
'/trials/trials-panel/visit/crc-question', '/trials/trials-panel/visit/crc-question',
'/trials/trials-panel/visit/qc-check', '/trials/trials-panel/visit/qc-check',
@ -145,6 +153,9 @@ export default {
'/trials/trials-panel/trial-summary/image-inspect', '/trials/trials-panel/trial-summary/image-inspect',
'/trials/trials-panel/trial-summary/push-record', '/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'])) { 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 isShow = false
} }
@ -201,15 +212,19 @@ export default {
if (~url.indexOf('?')) { if (~url.indexOf('?')) {
query = url.split('?')[1] query = url.split('?')[1]
} }
var trialsPanelList = this.trialsRouter.children.find(item => { return item.name === 'TrialsPanel' }).children
var currentRoute = trialsPanelList.find(c => { return c.path === v.name })
if (currentRoute && currentRoute.tabHiddn) {
this.$router.push({ path: `${v.name}${query ? '?' : ''}${query}` }) this.$router.push({ path: `${v.name}${query ? '?' : ''}${query}` })
var trialsPanelList = this.trialsRouter.children.find(v => { return v.name === 'TrialsPanel' }).children
var isHasChild = trialsPanelList.find(c => { return c.path === v.name }) && trialsPanelList.find(c => { return c.path === v.name }).children
if (trialsPanelList.find(c => { return c.path === v.name }) && trialsPanelList.find(c => { return c.path === v.name }).tabHiddn) {
return return
} }
var isHasChild = currentRoute && currentRoute.children
if (isHasChild && isHasChild.length > 0) { if (isHasChild && isHasChild.length > 0) {
this.trialsTabChild = isHasChild[0].path this.trialsTabChild = isHasChild[0].path
this.$router.push({ path: `${this.trialsTabChild}${query ? '?' : ''}${query}` }) this.$router.push({ path: `${this.trialsTabChild}${query ? '?' : ''}${query}` })
} else {
this.$router.push({ path: `${v.name}${query ? '?' : ''}${query}` })
} }
} catch (e) { } catch (e) {
console.log(e) console.log(e)

View File

@ -2,36 +2,21 @@
<!--FEEDBACK--> <!--FEEDBACK-->
<div v-if="visible" @click.stop="() => false" class="feedBack-box"> <div v-if="visible" @click.stop="() => false" class="feedBack-box">
<div class="feedBack-box-modal"></div> <div class="feedBack-box-modal"></div>
<el-dialog <el-dialog :visible.sync="visible" v-dialogDrag width="800px" :close-on-click-modal="false" @close="cancel"
:visible.sync="visible" :modal="false">
v-dialogDrag
width="800px"
:close-on-click-modal="false"
@close="cancel"
:modal="false"
>
<div slot="title"> <div slot="title">
{{ title }} {{ title }}
</div> </div>
<!-- 项目详情 --> <!-- 项目详情 -->
<div class="trialsBox" v-if="visitTaskId || SubjectVisitId"> <div class="trialsBox" v-if="visitTaskId || SubjectVisitId">
<el-form <el-form label-position="right" :model="form" :inline="true" class="trialsForm" v-if="type === 'detail'">
label-position="right"
:model="form"
:inline="true"
class="trialsForm"
v-if="type === 'detail'"
>
<el-form-item :label="$t('feedBack:trials:code')" style="width: 40%"> <el-form-item :label="$t('feedBack:trials:code')" style="width: 40%">
<span>{{ form.TrialCode }}</span> <span>{{ form.TrialCode }}</span>
</el-form-item> </el-form-item>
<el-form-item :label="$t('feedBack:trials:name')" style="width: 40%"> <el-form-item :label="$t('feedBack:trials:name')" style="width: 40%">
<span>{{ form.ExperimentName }}</span> <span>{{ form.ExperimentName }}</span>
</el-form-item> </el-form-item>
<el-form-item <el-form-item :label="$t('feedBack:trials:siteCode')" style="width: 40%">
:label="$t('feedBack:trials:siteCode')"
style="width: 40%"
>
<span>{{ form.TrialSiteCode }}</span> <span>{{ form.TrialSiteCode }}</span>
</el-form-item> </el-form-item>
<el-form-item :label="$t('feedBack:trials:visit')" style="width: 40%"> <el-form-item :label="$t('feedBack:trials:visit')" style="width: 40%">
@ -39,47 +24,21 @@
</el-form-item> </el-form-item>
</el-form> </el-form>
</div> </div>
<el-form <el-form ref="feedBackForm" label-position="right" :model="form" :rules="rules" label-width="100px">
ref="feedBackForm"
label-position="right"
:model="form"
:rules="rules"
label-width="100px"
>
<!-- 影像异常tip --> <!-- 影像异常tip -->
<p class="tip" v-if="type === 'imgfail'"> <p class="tip" v-if="type === 'imgfail'">
<i <i class="el-icon-warning-outline" style="color: #f56c6c; font-size: 24px"></i>
class="el-icon-warning-outline"
style="color: #f56c6c; font-size: 24px"
></i>
<span>{{ $t('feedBack:imgfail:tip') }}</span> <span>{{ $t('feedBack:imgfail:tip') }}</span>
</p> </p>
<!-- 问题反馈 --> <!-- 问题反馈 -->
<el-form-item <el-form-item :label="$t('feedBack:form:feedBack')" prop="QuestionType" v-if="type === 'feedback' && trialId">
:label="$t('feedBack:form:feedBack')" <el-select v-model="form.QuestionType" style="width: 100%" popper-class="feedBack-select-box">
prop="QuestionType" <el-option v-for="item in QuestionTypeOptions" :key="item.id" :label="item.label" :value="item.value">
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-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
<!-- 问题反馈 --> <!-- 问题反馈 -->
<el-form-item <el-form-item :label="$t('feedBack:form:feedBack')" prop="QuestionType" v-if="type === 'detail'">
:label="$t('feedBack:form:feedBack')"
prop="QuestionType"
v-if="type === 'detail'"
>
<span>{{ <span>{{
QuestionTypeOptions.filter( QuestionTypeOptions.filter(
(item) => item.value === form.QuestionType (item) => item.value === form.QuestionType
@ -87,43 +46,22 @@
}}</span> }}</span>
</el-form-item> </el-form-item>
<!-- 问题描述 --> <!-- 问题描述 -->
<el-form-item <el-form-item :label="$t('feedBack:form:description')" prop="QuestionDescription">
:label="$t('feedBack:form:description')" <el-input v-model="form.QuestionDescription" type="textarea" :rows="4" :maxlength="500"
prop="QuestionDescription" :disabled="type === 'detail'" />
>
<el-input
v-model="form.QuestionDescription"
type="textarea"
:rows="4"
:maxlength="500"
:disabled="type === 'detail'"
/>
</el-form-item> </el-form-item>
<!-- 截图 --> <!-- 截图 -->
<el-form-item :label="$t('feedBack:form:screenshot')" prop="screenshot"> <el-form-item :label="$t('feedBack:form:screenshot')" prop="screenshot">
<uploadImage <uploadImage :path.sync="form.ScreenshotList" :isUpload.sync="loading" :trialId="trialId"
:path.sync="form.ScreenshotList" :disabled="type === 'detail'" ref="uploadImage" />
:isUpload.sync="loading"
:trialId="trialId"
:disabled="type === 'detail'"
ref="uploadImage"
/>
</el-form-item> </el-form-item>
<!-- 反馈时间 --> <!-- 反馈时间 -->
<el-form-item <el-form-item :label="$t('feedBack:form:time')" prop="screenshot" v-if="type === 'detail'">
:label="$t('feedBack:form:time')"
prop="screenshot"
v-if="type === 'detail'"
>
<span>{{ form.CreateTime }}</span> <span>{{ form.CreateTime }}</span>
</el-form-item> </el-form-item>
<!-- 状态 --> <!-- 状态 -->
<el-form-item <el-form-item :label="$t('feedBack:form:status')" prop="screenshot" v-if="type === 'detail' && level >= 7">
:label="$t('feedBack:form:status')" <!-- <el-switch
prop="screenshot"
v-if="type === 'detail' && level > 7"
>
<el-switch
v-model="form.State" v-model="form.State"
active-color="#13ce66" active-color="#13ce66"
inactive-color="#ff4949" inactive-color="#ff4949"
@ -134,21 +72,27 @@
:disabled="level < 8 || !isStateChange" :disabled="level < 8 || !isStateChange"
@change="changeState" @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-item>
</el-form> </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') }} {{ $t('feedBack:button:cancel') }}
</el-button> </el-button>
<!-- 保存 --> <!-- 保存 -->
<el-button <el-button type="primary" size="small" @click.stop="save" :loading="loading"
type="primary" v-if="type !== 'detail' || level >= 8">
size="small"
@click.stop="save"
:loading="loading"
>
{{ $t('feedBack:button:save') }} {{ $t('feedBack:button:save') }}
</el-button> </el-button>
</div> </div>
@ -190,6 +134,7 @@ export default {
SubjectVisitId: null, SubjectVisitId: null,
ScreenshotList: [], ScreenshotList: [],
ScreenshotListStr: null, ScreenshotListStr: null,
Reason: null
}, },
rules: { rules: {
QuestionType: [ QuestionType: [
@ -307,6 +252,7 @@ export default {
// //
async changeState() { async changeState() {
if (this.isImgfail) return if (this.isImgfail) return
if (this.form.State !== 1 && this.form.State !== 0) return false
try { try {
let data = { let data = {
IdList: [this.Id], IdList: [this.Id],
@ -381,6 +327,10 @@ export default {
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
::v-deep.el-popper {
z-index: 4000 !important;
}
.tip { .tip {
width: 86%; width: 86%;
margin: auto; margin: auto;
@ -389,12 +339,14 @@ export default {
padding: 0 10px; padding: 0 10px;
display: flex; display: flex;
align-items: center; align-items: center;
// border-radius: 5px; // border-radius: 5px;
// background-color: #eee; // background-color: #eee;
i { i {
margin-right: 5px; margin-right: 5px;
} }
} }
.trialsBox { .trialsBox {
margin: auto; margin: auto;
margin-bottom: 20px; margin-bottom: 20px;
@ -405,11 +357,13 @@ export default {
border-radius: 5px; border-radius: 5px;
background-color: #eee; background-color: #eee;
} }
.trialsForm { .trialsForm {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
flex-wrap: wrap; flex-wrap: wrap;
.el-form-item { .el-form-item {
margin-bottom: 10px; margin-bottom: 10px;
} }

View File

@ -8,20 +8,49 @@
<script> <script>
import { getReportsChartData } from "@/api/reading" import { getReportsChartData } from "@/api/reading"
let echarts = require('echarts/lib/echarts'); import * as echarts from 'echarts/core';
import { LineChart } from 'echarts/charts';
import {
TitleComponent,
TooltipComponent,
GridComponent,
DataZoomComponent,
LegendComponent,
DatasetComponent,
// (filter, sort)
TransformComponent
} from 'echarts/components';
//
import { LabelLayout, UniversalTransition } from 'echarts/features';
// Canvas CanvasRenderer SVGRenderer
import { CanvasRenderer } from 'echarts/renderers';
echarts.use([
TitleComponent,
TooltipComponent,
GridComponent,
DatasetComponent,
TransformComponent,
DataZoomComponent,
LegendComponent,
LineChart,
LabelLayout,
UniversalTransition,
CanvasRenderer
]);
// let echarts = require('echarts/lib/echarts');
// //
// require('echarts/lib/chart/bar'); // require('echarts/lib/chart/bar');
require('echarts/lib/chart/line'); // require('echarts/lib/chart/line');
// require('echarts/lib/chart/pie'); // require('echarts/lib/chart/pie');
// require('echarts/lib/chart/scatter'); // require('echarts/lib/chart/scatter');
// //
require('echarts/lib/component/tooltip'); // require('echarts/lib/component/tooltip');
require('echarts/lib/component/title'); // require('echarts/lib/component/title');
require('echarts/lib/component/legend'); // require('echarts/lib/component/legend');
require('echarts/lib/component/grid'); // require('echarts/lib/component/grid');
require('echarts/lib/component/dataZoom'); // require('echarts/lib/component/dataZoom');
export default { export default {
name: "readingChart", name: "readingChart",
props: { props: {
@ -33,7 +62,8 @@ export default {
zIndex: 9, zIndex: 9,
chart: null, chart: null,
loading: false, loading: false,
key: 'readingChart' key: 'readingChart',
isInteger: false
}; };
}, },
methods: { methods: {
@ -72,12 +102,13 @@ export default {
let res = await getReportsChartData(params) let res = await getReportsChartData(params)
this.loading = false this.loading = false
if (res.IsSuccess) { if (res.IsSuccess) {
this.isInteger = res.Result.Type === 'number' && res.Result.ValueType === 0
let LatestScanDateList = res.Result.LatestScanDateList.map(item => item.split(" ")[0]) let LatestScanDateList = res.Result.LatestScanDateList.map(item => item.split(" ")[0])
let obj = { let obj = {
title: QuestionName, title: QuestionName,
xAxisData: LatestScanDateList || [], xAxisData: LatestScanDateList || [],
series: [], 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, visitName: res.Result.VisitTaskNameList,
min: null, min: null,
max: null max: null
@ -94,43 +125,6 @@ export default {
type: 'line' 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) this.initChart(obj)
} }
} catch (err) { } catch (err) {
@ -151,7 +145,13 @@ export default {
text: obj.title, text: obj.title,
textStyle: { textStyle: {
color: "#fff" color: "#fff"
} },
left: 0,
top: 0
},
grid: {
left: 50,
bottom: 30
}, },
tooltip: { tooltip: {
trigger: 'axis', trigger: 'axis',
@ -193,6 +193,7 @@ export default {
} }
}, },
axisLine: { axisLine: {
show: true,
lineStyle: { lineStyle: {
color: '#fff', color: '#fff',
} }
@ -200,6 +201,9 @@ export default {
}, },
series: obj.series series: obj.series
}; };
if (this.isInteger) {
option.yAxis.minInterval = 1
}
// 4. 使 // 4. 使
this.chart.setOption(option); this.chart.setOption(option);
}, },

View File

@ -116,7 +116,7 @@ export default {
display: none !important; /* 隐藏原生 radio 输入,但仍然允许交互 */ display: none !important; /* 隐藏原生 radio 输入,但仍然允许交互 */
} }
::v-deep.el-radio:focus:not(.is-focus):not(:active):not(.is-disabled) ::v-deep .el-radio:focus:not(.is-focus):not(:active):not(.is-disabled)
.el-radio__inner { .el-radio__inner {
box-shadow: none !important; box-shadow: none !important;
} }

View File

@ -70,16 +70,13 @@
<div style="margin: 10px 0" class="top"> <div style="margin: 10px 0" class="top">
<span>{{ $t('upload:dicom:uploadTitle') }}</span> <span>{{ $t('upload:dicom:uploadTitle') }}</span>
<div class="btnBox" v-if="!forbid"> <div class="btnBox" v-if="!forbid">
<span style="margin-right: 10px"> <span style="margin-right: 10px">{{ $store.state.trials.uploadTip }}</span>
{{ $store.state.trials.uploadTip }}
</span>
<form id="inputForm" ref="uploadForm" enctype="multipart/form-data"> <form id="inputForm" ref="uploadForm" enctype="multipart/form-data">
<div class="form-group"> <div class="form-group">
<div id="directoryInputWrapper" class="btn btn-link file-input"> <div id="directoryInputWrapper" class="btn btn-link file-input">
<el-button type="primary" :disabled="btnLoading" :loading="btnLoading" size="mini"> <el-button type="primary" :disabled="btnLoading" :loading="btnLoading" size="mini">{{
{{ $t('upload:dicom:button:batchUpload') }} $t('upload:dicom:button:batchUpload') }}</el-button>
</el-button> <input type="file" name="file" ref="pathClear" :disabled="btnLoading" webkitdirectory multiple title
<input type="file" name="file" ref="pathClear" :disabled="btnLoading" webkitdirectory multiple title=""
@change="beginScanFiles($event)" /> @change="beginScanFiles($event)" />
</div> </div>
</div> </div>
@ -94,8 +91,10 @@
<template slot="header"> <template slot="header">
<el-tooltip placement="top"> <el-tooltip placement="top">
<div slot="content"> <div slot="content">
{{ $t('trials:uploadDicomList:table:studyDetail1') }}<br /> {{ $t('trials:uploadDicomList:table:studyDetail1') }}
{{ $t('trials:uploadDicomList:table:studyDetail2') }}<br /> <br />
{{ $t('trials:uploadDicomList:table:studyDetail2') }}
<br />
{{ $t('trials:uploadedDicoms:table:studyDate') }} {{ $t('trials:uploadedDicoms:table:studyDate') }}
</div> </div>
<span>{{ $t('trials:uploadDicomList:table:studyInfo') }}</span> <span>{{ $t('trials:uploadDicomList:table:studyInfo') }}</span>
@ -105,13 +104,15 @@
<div style="line-height: 15px"> <div style="line-height: 15px">
<div> <div>
<div> <div>
<span v-if="scope.row.dicomInfo.accNumber"><span style="font-weight: 500">Acc:</span> <span v-if="scope.row.dicomInfo.accNumber">
{{ scope.row.dicomInfo.accNumber }}</span> <span style="font-weight: 500">Acc:</span>
{{ scope.row.dicomInfo.accNumber }}
</span>
<span v-else style="color: #f44336">N/A</span> <span v-else style="color: #f44336">N/A</span>
</div> </div>
<div style="display: inline-block; margin-right: 2px"> <div style="display: inline-block; margin-right: 2px">
<span v-if="scope.row.dicomInfo.modality.length > 0"> <span v-if="scope.row.dicomInfo.modality.length > 0">{{ scope.row.dicomInfo.modality.join('')
{{ scope.row.dicomInfo.modality.join('、') }},</span> }},</span>
<span v-else style="color: #f44336">N/A,</span> <span v-else style="color: #f44336">N/A,</span>
</div> </div>
<div style="display: inline-block; margin-right: 2px"> <div style="display: inline-block; margin-right: 2px">
@ -126,20 +127,15 @@
<div> <div>
<div style="display: inline-block; margin-right: 2px"> <div style="display: inline-block; margin-right: 2px">
<span v-if="scope.row.dicomInfo.bodyPart"> <span v-if="scope.row.dicomInfo.bodyPart">{{ scope.row.dicomInfo.bodyPart }},</span>
{{ scope.row.dicomInfo.bodyPart }}, <span v-else style="color: #f44336">N/A,</span>
</span>
<span v-else style="color: #f44336">N/A, </span>
</div> </div>
<div style="display: inline-block"> <div style="display: inline-block">
<span v-if="scope.row.dicomInfo.description"> <span v-if="scope.row.dicomInfo.description">{{ scope.row.dicomInfo.description }}</span>
{{ scope.row.dicomInfo.description }}</span>
<span v-else style="color: #f44336">N/A</span> <span v-else style="color: #f44336">N/A</span>
</div> </div>
</div> </div>
<div> <div>{{ scope.row.dicomInfo.studyTime }}</div>
{{ scope.row.dicomInfo.studyTime }}
</div>
</div> </div>
</template> </template>
</el-table-column> </el-table-column>
@ -147,8 +143,10 @@
<template slot="header"> <template slot="header">
<el-tooltip placement="top"> <el-tooltip placement="top">
<div slot="content"> <div slot="content">
{{ $t('trials:uploadDicomList:table:pId') }}<br /> {{ $t('trials:uploadDicomList:table:pId') }}
{{ $t('trials:uploadDicomList:table:patientName') }}<br /> <br />
{{ $t('trials:uploadDicomList:table:patientName') }}
<br />
{{ $t('trials:uploadDicomList:table:pInfo') }} {{ $t('trials:uploadDicomList:table:pInfo') }}
</div> </div>
<span>{{ $t('trials:uploadDicomList:table:patientInfo') }}</span> <span>{{ $t('trials:uploadDicomList:table:patientInfo') }}</span>
@ -157,8 +155,11 @@
<template slot-scope="scope"> <template slot-scope="scope">
<div style="line-height: 15px"> <div style="line-height: 15px">
<div> <div>
<span v-if="scope.row.dicomInfo.patientId"><span style="font-weight: 500">PID: </span>{{ <span v-if="scope.row.dicomInfo.patientId">
scope.row.dicomInfo.patientId }}</span> <span style="font-weight: 500">PID:</span>
{{
scope.row.dicomInfo.patientId }}
</span>
<span v-else style="color: #f44336">N/A</span> <span v-else style="color: #f44336">N/A</span>
</div> </div>
<div> <div>
@ -227,8 +228,7 @@
<span v-if=" <span v-if="
!scope.row.dicomInfo.failedFileCount && !scope.row.dicomInfo.failedFileCount &&
!scope.row.dicomInfo.isInit !scope.row.dicomInfo.isInit
"> ">{{ $t('trials:uploadDicomList:table:status1') }}</span>
{{ $t('trials:uploadDicomList:table:status1') }}</span>
<span style="color: #409eff" v-else-if=" <span style="color: #409eff" v-else-if="
!scope.row.dicomInfo.failedFileCount && !scope.row.dicomInfo.failedFileCount &&
scope.row.dicomInfo.isInit && scope.row.dicomInfo.isInit &&
@ -246,9 +246,11 @@
scope.row.uploadState.record && scope.row.uploadState.record &&
scope.row.uploadState.record.fileCount === 0 scope.row.uploadState.record.fileCount === 0
">{{ $t('trials:uploadDicomList:table:status5') }}</span> ">{{ $t('trials:uploadDicomList:table:status5') }}</span>
<span style="color: #f66" v-else>{{ <span style="color: #f66" v-else>
{{
$t('trials:uploadDicomList:table:Failed') $t('trials:uploadDicomList:table:Failed')
}}</span> }}
</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column :label="$t('trials:uploadDicomList:table:record')" min-width="140" show-overflow-tooltip> <el-table-column :label="$t('trials:uploadDicomList:table:record')" min-width="140" show-overflow-tooltip>
@ -259,42 +261,42 @@
{{ $t('trials:uploadDicomList:table:Existed') }}: {{ $t('trials:uploadDicomList:table:Existed') }}:
<div v-if="scope.row.uploadState.record.Existed.length"> <div v-if="scope.row.uploadState.record.Existed.length">
<div v-for="item of scope.row.uploadState.record.Existed" :key="item" <div v-for="item of scope.row.uploadState.record.Existed" :key="item"
style="font-size: 12px; color: #baa72a"> style="font-size: 12px; color: #baa72a">{{ item }}</div>
{{ item }}
</div>
</div> </div>
<div v-else>&nbsp;</div> <div v-else>&nbsp;</div>
{{ $t('trials:uploadDicomList:table:Uploaded') }}: {{ $t('trials:uploadDicomList:table:Uploaded') }}:
<div v-if="scope.row.uploadState.record.Uploaded.length"> <div v-if="scope.row.uploadState.record.Uploaded.length">
<div v-for="item of scope.row.uploadState.record.Uploaded" :key="item" <div v-for="item of scope.row.uploadState.record.Uploaded" :key="item"
style="font-size: 12px; color: #24b837"> style="font-size: 12px; color: #24b837">{{ item }}</div>
{{ item }}
</div>
</div> </div>
<div v-else>&nbsp;</div> <div v-else>&nbsp;</div>
<br /> <br />
{{ $t('trials:uploadDicomList:table:Failed') }}: {{ $t('trials:uploadDicomList:table:Failed') }}:
<div v-if="scope.row.uploadState.record.Failed.length"> <div v-if="scope.row.uploadState.record.Failed.length">
<div v-for="item of scope.row.uploadState.record.Failed" :key="item" <div v-for="item of scope.row.uploadState.record.Failed" :key="item"
style="font-size: 12px; color: #f66"> style="font-size: 12px; color: #f66">{{ item }}</div>
{{ item }}
</div>
</div> </div>
<div v-else>&nbsp;</div> <div v-else>&nbsp;</div>
</div> </div>
</div> </div>
<el-button size="mini" style="cursor: pointer"> <el-button size="mini" style="cursor: pointer">
<span style="font-size: 12px; color: #baa72a">{{ <span style="font-size: 12px; color: #baa72a">
{{
scope.row.uploadState.record.Existed.length scope.row.uploadState.record.Existed.length
}}</span> }}
</span>
/ /
<span style="font-size: 12px; color: #24b837">{{ <span style="font-size: 12px; color: #24b837">
{{
scope.row.uploadState.record.Uploaded.length scope.row.uploadState.record.Uploaded.length
}}</span> }}
</span>
/ /
<span style="font-size: 12px; color: #f66">{{ <span style="font-size: 12px; color: #f66">
{{
scope.row.uploadState.record.Failed.length scope.row.uploadState.record.Failed.length
}}</span> }}
</span>
</el-button> </el-button>
</el-tooltip> </el-tooltip>
</template> </template>
@ -335,6 +337,7 @@ cornerstoneWADOImageLoader.external.cornerstone = cornerstone
import { convertBytes } from '@/utils/dicom-character-set' import { convertBytes } from '@/utils/dicom-character-set'
import { parseDicom } from '@/utils/parseDicom.js' import { parseDicom } from '@/utils/parseDicom.js'
import { dcmUpload } from '@/utils/dcmUpload/dcmUpload' import { dcmUpload } from '@/utils/dcmUpload/dcmUpload'
import dcmjs from '@/utils/dcmUpload/dcmjs'
import store from '@/store' import store from '@/store'
import { getToken } from '@/utils/auth' import { getToken } from '@/utils/auth'
export default { export default {
@ -373,7 +376,7 @@ export default {
IsImageSegment: { IsImageSegment: {
type: Boolean, type: Boolean,
default: false, default: false,
} },
}, },
components: { components: {
'study-view': studyView, 'study-view': studyView,
@ -455,8 +458,10 @@ export default {
} }
if (this.IsImageSegment) { if (this.IsImageSegment) {
params.IsImageSegmentLabel = true params.IsImageSegmentLabel = true
} } else if (
else if (this.Criterion.CriterionType == 19 || this.Criterion.CriterionType == 20) { this.Criterion.CriterionType == 19 ||
this.Criterion.CriterionType == 20
) {
params.IsImageSegmentLabel = false params.IsImageSegmentLabel = false
} }
this.loading = true this.loading = true
@ -543,7 +548,8 @@ export default {
this.openIsUpload = isUpload this.openIsUpload = isUpload
this.openSubjectVisitId = item.SubjectVisitId || item.SourceSubjectVisitId this.openSubjectVisitId = item.SubjectVisitId || item.SourceSubjectVisitId
this.openVisitTaskId = item.VisitTaskId this.openVisitTaskId = item.VisitTaskId
this.model_cfg.title = `${item.SubjectCode || ''} > ${this.IsImageSegment ? item.VisitName : item.TaskBlindName}` this.model_cfg.title = `${item.SubjectCode || ''} > ${this.IsImageSegment ? item.VisitName : item.TaskBlindName
}`
this.modelList = item[list] this.modelList = item[list]
this.model_cfg.visible = true this.model_cfg.visible = true
}, },
@ -645,7 +651,10 @@ export default {
for (let i = 0; i < checkFiles.length; i++) { for (let i = 0; i < checkFiles.length; i++) {
let item = checkFiles[i] let item = checkFiles[i]
var dicom = await parseDicom(item, ['StudyInstanceUid', 'Modality']) 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 this.hasOtherStudy = true
checkFiles.splice(i, 1) checkFiles.splice(i, 1)
i-- i--
@ -712,12 +721,13 @@ export default {
var studyUid = data.string('x0020000d') var studyUid = data.string('x0020000d')
if (!studyUid) return resolve() if (!studyUid) return resolve()
var pixelDataElement = data.elements.x7fe00010 var pixelDataElement = data.elements.x7fe00010
if (!pixelDataElement && modality !== 'SR') return resolve() if (!pixelDataElement && modality !== 'SR' && modality !== 'ECG')
return resolve()
var studyIndex = 0 var studyIndex = 0
while ( while (
studyIndex < scope.uploadQueues.length && studyIndex < scope.uploadQueues.length &&
scope.uploadQueues[studyIndex].dicomInfo.studyUid !== studyUid && scope.uploadQueues[studyIndex].dicomInfo.studyUid !== studyUid &&
(pixelDataElement || modality === 'SR') && (pixelDataElement || modality === 'SR' || modality === 'ECG') &&
modality != '' modality != ''
) { ) {
++studyIndex ++studyIndex
@ -809,6 +819,39 @@ export default {
}, },
}) })
} }
if (
!scope.uploadQueues[studyIndex].dicomInfo.RadionuclideTotalDose
) {
let dataset = dcmjs.data.DicomMessage.readFile(e.target.result)
if (
dataset.dict['00540016'] &&
dataset.dict['00540016'].Value &&
dataset.dict['00540016'].Value[0]
) {
let RadionuclideTotalDose = dataset.dict['00540016'].Value[0][
'00181074'
]
? dataset.dict['00540016'].Value[0]['00181074'].Value[0]
: null
let RadionuclideHalfLife = dataset.dict['00540016'].Value[0][
'00181075'
]
? dataset.dict['00540016'].Value[0]['00181075'].Value[0]
: null
let RadiopharmaceuticalStartTime = dataset.dict['00540016']
.Value[0]['00181072']
? dataset.dict['00540016'].Value[0]['00181072'].Value[0]
: null
scope.uploadQueues[studyIndex].dicomInfo.RadionuclideTotalDose =
RadionuclideTotalDose
scope.uploadQueues[studyIndex].dicomInfo.RadionuclideHalfLife =
RadionuclideHalfLife
scope.uploadQueues[
studyIndex
].dicomInfo.RadiopharmaceuticalStartTime =
RadiopharmaceuticalStartTime
}
}
var modality = scope.uploadQueues[studyIndex].dicomInfo.modality var modality = scope.uploadQueues[studyIndex].dicomInfo.modality
var currentModality = data.string('x00080060') var currentModality = data.string('x00080060')
if (!(modality.indexOf(currentModality) > -1)) { if (!(modality.indexOf(currentModality) > -1)) {
@ -876,8 +919,9 @@ export default {
) )
seriesItem = { seriesItem = {
seriesUid: seriesUid, seriesUid: seriesUid,
RadiopharmaceuticalInformationSequence: data.string('x00540016') || "", RadiopharmaceuticalInformationSequence:
AcquisitionDate: data.string('x00080022') || "", data.string('x00540016') || '',
AcquisitionDate: data.string('x00080022') || '',
DicomSeriesDate: data.string('x00080021'), DicomSeriesDate: data.string('x00080021'),
DicomSeriesTime: data.string('x00080031'), DicomSeriesTime: data.string('x00080031'),
@ -1085,16 +1129,20 @@ export default {
var scope = this var scope = this
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
try { try {
let subjectVisitId = null; let subjectVisitId = null
let dicomInfo = {}
if (scope.VisitTaskId) { if (scope.VisitTaskId) {
scope.StudyInstanceUidList.forEach(item => { scope.StudyInstanceUidList.forEach((item) => {
if (item.VisitTaskId === scope.VisitTaskId) { if (item.VisitTaskId === scope.VisitTaskId) {
subjectVisitId = item.SourceSubjectVisitId subjectVisitId = item.SourceSubjectVisitId
} }
}) })
} else { } else {
scope.StudyInstanceUidList.forEach(item => { scope.StudyInstanceUidList.forEach((item) => {
if (item.StudyInstanceUid === scope.uploadQueues[index].dicomInfo.studyUid) { if (
item.StudyInstanceUid ===
scope.uploadQueues[index].dicomInfo.studyUid
) {
subjectVisitId = item.SourceSubjectVisitId subjectVisitId = item.SourceSubjectVisitId
} }
}) })
@ -1112,7 +1160,7 @@ export default {
.then(async (res) => { .then(async (res) => {
scope.uploadQueues[index].dicomInfo.failedFileCount = 0 scope.uploadQueues[index].dicomInfo.failedFileCount = 0
scope.$set(scope.uploadQueues[index].dicomInfo, 'isInit', true) 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 seriesNum = scope.uploadQueues[index].seriesList.length
let fileNum = scope.uploadQueues[index].fileList.length let fileNum = scope.uploadQueues[index].fileList.length
let seriesList = scope.uploadQueues[index].seriesList let seriesList = scope.uploadQueues[index].seriesList
@ -1144,6 +1192,11 @@ export default {
failedFileCount: 0, failedFileCount: 0,
RecordPath: null, RecordPath: null,
study: { study: {
RadionuclideTotalDose: dicomInfo.RadionuclideTotalDose,
RadionuclideHalfLife: dicomInfo.RadionuclideHalfLife,
RadiopharmaceuticalStartTime:
dicomInfo.RadiopharmaceuticalStartTime,
studyId: dicomInfo.studyId, studyId: dicomInfo.studyId,
studyInstanceUid: dicomInfo.studyUid, studyInstanceUid: dicomInfo.studyUid,
studyTime: dicomInfo.studyTime, studyTime: dicomInfo.studyTime,
@ -1153,8 +1206,8 @@ export default {
institutionName: dicomInfo.institutionName, institutionName: dicomInfo.institutionName,
patientId: dicomInfo.patientId, patientId: dicomInfo.patientId,
patientName: '', patientName: '',
patientAge: '', patientAge: dicomInfo.patientAge,
patientSex: dicomInfo.patientSex, patientSex: config?.DicomStoreInfo.SubjectSex || dicomInfo.patientSex,
accessionNumber: dicomInfo.accNumber, accessionNumber: dicomInfo.accNumber,
patientBirthDate: '', patientBirthDate: '',
acquisitionTime: dicomInfo.acquisitionTime, acquisitionTime: dicomInfo.acquisitionTime,
@ -1174,6 +1227,7 @@ export default {
}, },
} }
let arr = [] let arr = []
let uploadBatchId = scope.$guid()
for (let i = 0; i < seriesList.length; i++) { for (let i = 0; i < seriesList.length; i++) {
let v = seriesList[i] let v = seriesList[i]
let instanceList = [] let instanceList = []
@ -1196,8 +1250,10 @@ export default {
seriesInstanceUid: v.seriesUid, seriesInstanceUid: v.seriesUid,
SOPClassUID: o.SOPClassUID, SOPClassUID: o.SOPClassUID,
TransferSytaxUID: o.TransferSytaxUID, TransferSytaxUID: o.TransferSytaxUID,
MediaStorageSOPInstanceUID: o.MediaStorageSOPInstanceUID, MediaStorageSOPInstanceUID:
MediaStorageSOPClassUID: o.MediaStorageSOPClassUID, o.MediaStorageSOPInstanceUID,
MediaStorageSOPClassUID:
o.MediaStorageSOPClassUID,
sopInstanceUid: o.instanceUid, sopInstanceUid: o.instanceUid,
instanceNumber: o.instanceNumber, instanceNumber: o.instanceNumber,
instanceTime: o.instanceTime, instanceTime: o.instanceTime,
@ -1214,14 +1270,17 @@ export default {
path: o.myPath, path: o.myPath,
FileSize: o.FileSize, FileSize: o.FileSize,
PhotometricInterpretation: o.PhotometricInterpretation, PhotometricInterpretation:
o.PhotometricInterpretation,
BitsAllocated: o.BitsAllocated, BitsAllocated: o.BitsAllocated,
PixelRepresentation: o.PixelRepresentation, PixelRepresentation: o.PixelRepresentation,
RescaleIntercept: o.RescaleIntercept, RescaleIntercept: o.RescaleIntercept,
RescaleSlope: o.RescaleSlope, RescaleSlope: o.RescaleSlope,
ImagePositionPatient: o.ImagePositionPatient, ImagePositionPatient: o.ImagePositionPatient,
ImageOrientationPatient: o.ImageOrientationPatient, ImageOrientationPatient:
SequenceOfUltrasoundRegions: o.SequenceOfUltrasoundRegions, o.ImageOrientationPatient,
SequenceOfUltrasoundRegions:
o.SequenceOfUltrasoundRegions,
FrameTime: o.FrameTime, FrameTime: o.FrameTime,
CorrectedImage: o.CorrectedImage, CorrectedImage: o.CorrectedImage,
Units: o.Units, Units: o.Units,
@ -1266,6 +1325,16 @@ export default {
) { ) {
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) { if (!res || !res.url) {
@ -1289,7 +1358,17 @@ export default {
let OSSclient = scope.OSSclient let OSSclient = scope.OSSclient
let seriesRes = await OSSclient.put( let seriesRes = await OSSclient.put(
thumbnailPath, thumbnailPath,
blob blob,
{
fileName: `${v.seriesUid}.jpg`,
fileSize: blob.size,
fileType: 'image/jpeg',
uploadBatchId: uploadBatchId,
batchDataType: 6,
trialId: params.trialId,
subjectId: params.subjectId,
subjectVisitId: params.subjectVisitId,
}
) )
if (seriesRes && seriesRes.url) { if (seriesRes && seriesRes.url) {
ImageResizePath = scope.$getObjectName( ImageResizePath = scope.$getObjectName(
@ -1308,8 +1387,10 @@ export default {
sopInstanceUid: o.instanceUid, sopInstanceUid: o.instanceUid,
SOPClassUID: o.SOPClassUID, SOPClassUID: o.SOPClassUID,
TransferSytaxUID: o.TransferSytaxUID, TransferSytaxUID: o.TransferSytaxUID,
MediaStorageSOPInstanceUID: o.MediaStorageSOPInstanceUID, MediaStorageSOPInstanceUID:
MediaStorageSOPClassUID: o.MediaStorageSOPClassUID, o.MediaStorageSOPInstanceUID,
MediaStorageSOPClassUID:
o.MediaStorageSOPClassUID,
instanceNumber: o.instanceNumber, instanceNumber: o.instanceNumber,
instanceTime: o.instanceTime, instanceTime: o.instanceTime,
imageRows: o.imageRows, imageRows: o.imageRows,
@ -1325,14 +1406,17 @@ export default {
path: scope.$getObjectName(res.url), path: scope.$getObjectName(res.url),
FileSize: o.FileSize, FileSize: o.FileSize,
PhotometricInterpretation: o.PhotometricInterpretation, PhotometricInterpretation:
o.PhotometricInterpretation,
BitsAllocated: o.BitsAllocated, BitsAllocated: o.BitsAllocated,
PixelRepresentation: o.PixelRepresentation, PixelRepresentation: o.PixelRepresentation,
RescaleIntercept: o.RescaleIntercept, RescaleIntercept: o.RescaleIntercept,
RescaleSlope: o.RescaleSlope, RescaleSlope: o.RescaleSlope,
ImagePositionPatient: o.ImagePositionPatient, ImagePositionPatient: o.ImagePositionPatient,
ImageOrientationPatient: o.ImageOrientationPatient, ImageOrientationPatient:
SequenceOfUltrasoundRegions: o.SequenceOfUltrasoundRegions, o.ImageOrientationPatient,
SequenceOfUltrasoundRegions:
o.SequenceOfUltrasoundRegions,
FrameTime: o.FrameTime, FrameTime: o.FrameTime,
CorrectedImage: o.CorrectedImage, CorrectedImage: o.CorrectedImage,
Units: o.Units, Units: o.Units,
@ -1387,7 +1471,8 @@ export default {
instanceList: instanceList, instanceList: instanceList,
ImageResizePath: ImageResizePath, ImageResizePath: ImageResizePath,
RadiopharmaceuticalInformationSequence: v.RadiopharmaceuticalInformationSequence, RadiopharmaceuticalInformationSequence:
v.RadiopharmaceuticalInformationSequence,
AcquisitionDate: v.AcquisitionDate, AcquisitionDate: v.AcquisitionDate,
}) })
} }
@ -1430,7 +1515,16 @@ export default {
} }
let OSSclient = scope.OSSclient let OSSclient = scope.OSSclient
try { try {
let seriesRes = await OSSclient.put(thumbnailPath, blob) let seriesRes = await OSSclient.put(thumbnailPath, blob, {
fileName: `${v.seriesUid}.jpg`,
fileSize: blob.size,
fileType: 'image/jpeg',
uploadBatchId: uploadBatchId,
batchDataType: 6,
trialId: params.trialId,
subjectId: params.subjectId,
subjectVisitId: params.subjectVisitId,
})
if (seriesRes && seriesRes.url) { if (seriesRes && seriesRes.url) {
o.ImageResizePath = scope.$getObjectName(seriesRes.url) o.ImageResizePath = scope.$getObjectName(seriesRes.url)
} }
@ -1448,6 +1542,7 @@ export default {
if (scope.IsImageSegment) { if (scope.IsImageSegment) {
params.IsImageSegmentLabel = true params.IsImageSegmentLabel = true
} }
params.UploadBatchId = uploadBatchId
addOrUpdateArchiveTaskStudy(params) addOrUpdateArchiveTaskStudy(params)
.then((res) => { .then((res) => {
if (dicomInfo.failedFileCount === dicomInfo.fileCount) { if (dicomInfo.failedFileCount === dicomInfo.fileCount) {
@ -1542,7 +1637,9 @@ export default {
var token = getToken() var token = getToken()
let trialId = this.$route.query.trialId let trialId = this.$route.query.trialId
const routeData = this.$router.resolve({ const routeData = this.$router.resolve({
path: `/showvisitdicoms?page=upload&trialId=${trialId}&visitTaskId=${this.IsImageSegment ? 'undefined' : row.VisitTaskId}&subjectVisitId=${row.SourceSubjectVisitId}&isReading=1&TokenKey=${token}`, path: `/showvisitdicoms?page=upload&trialId=${trialId}&visitTaskId=${this.IsImageSegment ? 'undefined' : row.VisitTaskId
}&subjectVisitId=${row.SourceSubjectVisitId
}&isReading=1&TokenKey=${token}`,
}) })
this.open = window.open(routeData.href, '_blank') this.open = window.open(routeData.href, '_blank')
}, },

View File

@ -86,16 +86,16 @@ export default {
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
::v-deep.el-tabs--left .el-tabs__header.is-left { ::v-deep .el-tabs--left .el-tabs__header.is-left {
margin-right: 0; margin-right: 0;
} }
.uploadDicomAndNonedicom { .uploadDicomAndNonedicom {
::v-deep.el-tabs--border-card>.el-tabs__header .el-tabs__item { ::v-deep .el-tabs--border-card>.el-tabs__header .el-tabs__item {
color: #909399; color: #909399;
} }
::v-deep.el-tabs--border-card>.el-tabs__header .el-tabs__item.is-active { ::v-deep .el-tabs--border-card>.el-tabs__header .el-tabs__item.is-active {
color: #428bca; color: #428bca;
background-color: #fff; background-color: #fff;
border-right-color: #dcdfe6; border-right-color: #dcdfe6;

View File

@ -548,8 +548,9 @@ export default {
}) })
if (res.IsSuccess) { if (res.IsSuccess) {
this.studyMonitorId = res.Result this.studyMonitorId = res.Result
let uploadBatchId = this.$guid()
for (let i = 0; i < num; i++) { for (let i = 0; i < num; i++) {
funArr.push(this.handleUploadTask(this.selectArr, i)) funArr.push(this.handleUploadTask(this.selectArr, i, uploadBatchId))
} }
if (funArr.length > 0) { if (funArr.length > 0) {
let res = await Promise.all(funArr) let res = await Promise.all(funArr)
@ -560,7 +561,7 @@ export default {
} }
}, },
// //
async handleUploadTask(arr, index) { async handleUploadTask(arr, index, uploadBatchId) {
if (!this.uploadVisible) return if (!this.uploadVisible) return
let file = this.fileList.filter((item) => item.id === arr[index].id)[0] let file = this.fileList.filter((item) => item.id === arr[index].id)[0]
file.status = 1 file.status = 1
@ -576,7 +577,7 @@ export default {
} }
file.curPath = path file.curPath = path
const fileData = await this.fileToBlob(file.file) const fileData = await this.fileToBlob(file.file)
let res = await this.fileToOss(path, fileData, file) let res = await this.fileToOss(path, fileData, file, uploadBatchId)
if (res) { if (res) {
file.status = 2 file.status = 2
this.successFileList.push({ this.successFileList.push({
@ -610,13 +611,13 @@ export default {
} }
let ind = arr.findIndex((item) => item.status === 0) let ind = arr.findIndex((item) => item.status === 0)
if (ind >= 0) { if (ind >= 0) {
return this.handleUploadTask(arr, ind) return this.handleUploadTask(arr, ind, uploadBatchId)
} else { } else {
return false return false
} }
}, },
// fileoss // fileoss
async fileToOss(path, file, item) { async fileToOss(path, file, item, uploadBatchId) {
try { try {
let res = await this.OSSclient.multipartUpload( let res = await this.OSSclient.multipartUpload(
{ {
@ -629,6 +630,17 @@ export default {
if (item.uploadFileSize > file.fileSize) { if (item.uploadFileSize > file.fileSize) {
item.uploadFileSize = file.fileSize > 0 ? file.fileSize : 1 item.uploadFileSize = file.fileSize > 0 ? file.fileSize : 1
} }
},
{
fileName: item.name,
fileSize: item.size,
fileType: item.fileType,
uploadBatchId: uploadBatchId,
batchDataType: 7,
trialId: this.$route.query.trialId,
subjectId: this.currentRow.SubjectId,
subjectVisitId: this.currentRow.SourceSubjectVisitId,
studyCode: this.SubjectCode
} }
) )
if (res) { if (res) {

View File

@ -0,0 +1,16 @@
import resize from './resize'
const install = function (Vue) {
// 绑定v-adaptive指令
Vue.directive('resize', resize)
}
// if (window.Vue) {
// window['adaptive'] = adaptive
// // eslint-disable-next-line no-undef
// Vue.use(install)
// }
resize.install = install
export default resize

View File

@ -0,0 +1,19 @@
const map = new WeakMap()
const ob = new ResizeObserver(entries => {
for (const entry of entries) {
const handler = map.get(entry.target)
if (handler) {
const { inlineSize, blockSize } = entry.contentBoxSize[0]
handler({ width: inlineSize, height: blockSize })
}
}
})
export default {
bind(el, binding) {
map.set(el, binding.value)
ob.observe(el)
},
unbind(el) {
ob.unobserve(el)
}
}

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

View File

@ -0,0 +1 @@
<svg width="24px" height="24px" viewBox="0 0 24 24" class="h-6 w-6"><g id="tool-seg-brush" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><rect id="Rectangle" x="0" y="0" width="24" height="24"></rect><path d="M3.24640621,21.8286833 C3.09173375,21.7551472 2.99513748,21.5971513 3.00018895,21.4259625 C3.00524042,21.2547737 3.11098486,21.1027485 3.26972426,21.0384606 C5.3260304,20.2059201 4.66362518,17.8620247 5.27421252,16.6088957 C6.02197747,15.1026514 7.84114758,14.4766383 9.35746132,15.2037675 C13.9485253,17.4422999 8.48346644,24.3211232 3.24640621,21.8286833 Z" id="Path" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path><path d="M21.5968442,2.302022 C21.1256527,1.8826075 20.4094461,1.90228142 19.9619901,2.34693083 L9.69255027,12.5887345 C11.0536437,13.0051578 12.1843437,13.9616637 12.8206229,15.2349008 L21.7410706,3.93687606 C22.1347378,3.44008272 22.0714081,2.72222021 21.5968442,2.302022 Z" id="Path" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

12
src/icons/svg/contour.svg Normal file
View File

@ -0,0 +1,12 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" class="h-6 w-6" color="#fff">
<path
d="M10.3945 12.6113C10.043 12.6113 9.78516 12.3945 9.78516 12.0371C9.78516 11.6855 10.043 11.4688 10.3945 11.4688H13.2012C13.5527 11.4688 13.8164 11.6855 13.8164 12.0371C13.8164 12.3945 13.5527 12.6113 13.2012 12.6113H10.3945Z"
fill="currentColor"></path>
<path opacity="0.5"
d="M18.312 6.84473C21.8473 8.88583 22.9225 13.6424 20.7134 17.4687C18.5042 21.2951 13.8473 22.7422 10.312 20.7011C9.51661 20.2419 8.84737 19.6439 8.31042 18.9512C11.3012 19.3598 14.4827 17.8251 16.1946 14.8598C17.9067 11.8945 17.6457 8.3712 15.7963 5.98538C16.6645 6.10409 17.5167 6.38556 18.312 6.84473Z"
fill="currentColor"></path>
<circle cx="13.7852" cy="13.4688" r="8" stroke="currentColor"></circle>
<path
d="M4.50586 8.22266C4.14844 8.22266 3.92578 7.9707 3.92578 7.60742V5.68555H2.10352C1.74609 5.68555 1.5 5.46875 1.5 5.11719C1.5 4.76562 1.74609 4.54297 2.10352 4.54297H3.92578V2.61523C3.92578 2.25195 4.14844 2 4.50586 2C4.85742 2 5.08008 2.25195 5.08008 2.61523V4.54297H6.89648C7.25391 4.54297 7.50586 4.76562 7.50586 5.11719C7.50586 5.46875 7.25391 5.68555 6.89648 5.68555H5.08008V7.60742C5.08008 7.9707 4.85742 8.22266 4.50586 8.22266Z"
fill="currentColor"></path>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

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

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
src/icons/svg/fill.svg Normal file
View File

@ -0,0 +1 @@
<svg width="18px" height="18px" viewBox="0 0 18 18" class="text-primary"><g id="view-fill" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><g id="Group-13"><rect id="Rectangle" x="0" y="0" width="18" height="18"></rect><rect id="Rectangle" fill="currentColor" x="2" y="2" width="14" height="14" rx="1"></rect></g></g></svg>

After

Width:  |  Height:  |  Size: 338 B

View File

@ -0,0 +1 @@
<svg width="18px" height="18px" viewBox="0 0 18 18" class="text-primary"><g id="view-outline-fill" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><g id="Group-13"><rect id="Rectangle" x="0" y="0" width="18" height="18"></rect><rect id="Rectangle" stroke="currentColor" x="1.5" y="1.5" width="15" height="15" rx="1"></rect><rect id="Rectangle" fill="currentColor" x="3.5" y="3.5" width="11" height="11" rx="1"></rect></g></g></svg>

After

Width:  |  Height:  |  Size: 446 B

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

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

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="1775115523115" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5347" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M458.105263 970.105263h-215.578947V264.084211h215.578947V970.105263z m-161.68421-53.894737h107.789473V317.978947h-107.789473V916.210526z" fill="#ffffff" p-id="5348"></path><path d="M296.421053 970.105263h-215.578948V479.663158h215.578948V970.105263z m-161.684211-53.894737h107.789474V533.557895h-107.789474V916.210526zM943.157895 970.105263h-215.578948V479.663158h215.578948V970.105263z m-161.684211-53.894737h107.789474V533.557895h-107.789474V916.210526zM619.789474 970.105263h-215.578948V53.894737h215.578948v916.210526z m-161.684211-53.894737h107.789474V107.789474h-107.789474v808.421052z" fill="#ffffff" p-id="5349"></path><path d="M781.473684 970.105263h-215.578947V264.084211h215.578947V970.105263z m-161.68421-53.894737h107.789473V317.978947h-107.789473V916.210526z" fill="#ffffff" p-id="5350"></path></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

1
src/icons/svg/jumpto.svg Normal file
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="1774331916694" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1627" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M198.4 960A134.4 134.4 0 0 1 64 825.6V198.4A134.4 134.4 0 0 1 198.4 64h268.8a44.8 44.8 0 0 1 44.8 44.8 44.8 44.8 0 0 1-44.8 44.8H198.4a44.8 44.8 0 0 0-44.8 44.8v627.2a44.8 44.8 0 0 0 44.8 44.8h627.2a44.8 44.8 0 0 0 44.8-44.8V556.8a44.8 44.8 0 0 1 44.8-44.8 44.8 44.8 0 0 1 44.8 44.8v268.8A134.4 134.4 0 0 1 825.6 960z m281.984-416.384a44.8 44.8 0 0 1 0-63.296L807.232 153.6H646.464a44.8 44.8 0 0 1-44.8-44.8 44.8 44.8 0 0 1 44.8-44.8h267.264a44.8 44.8 0 0 1 28.288 8.896l1.408 1.152 0.64 0.512 1.024 0.96a41.216 41.216 0 0 1 3.136 3.072l0.896 1.088 0.512 0.64 1.152 1.344a44.8 44.8 0 0 1 8.832 28.288V377.28a44.8 44.8 0 0 1-44.8 44.8 44.8 44.8 0 0 1-44.8-44.8V216.512l-326.4 326.4a44.8 44.8 0 0 1-32 13.248A44.8 44.8 0 0 1 480 543.616z" p-id="1628" fill="#2c2c2c"></path></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1 @@
<svg width="18px" height="18px" viewBox="0 0 18 18" class="text-primary"><g id="view-outline" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><g id="Group-13"><rect id="Rectangle" x="0" y="0" width="18" height="18"></rect><rect id="Rectangle" stroke="currentColor" x="1.5" y="1.5" width="15" height="15" rx="1"></rect></g></g></svg>

After

Width:  |  Height:  |  Size: 347 B

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

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

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

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

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

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

View File

@ -0,0 +1 @@
<svg width="24px" height="24px" viewBox="0 0 24 24" class="h-6 w-6"><g id="tool-seg-threshold" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><rect id="Rectangle" x="0" y="0" width="24" height="24"></rect><g id="Group" transform="translate(2.4184, 14.3673)" stroke="currentColor" stroke-width="1.5"><line x1="16.5816327" y1="2.13265306" x2="19.0816327" y2="2.13265306" id="Line-2" stroke-linecap="round"></line><line x1="0.0816326531" y1="2.13265306" x2="12.0816327" y2="2.13265306" id="Line-2" stroke-linecap="round"></line><circle id="Oval" cx="14.2244898" cy="2.09183673" r="2.09183673"></circle></g><g id="Group" transform="translate(11.7092, 7.4592) scale(-1, 1) translate(-11.7092, -7.4592)translate(1.9184, 5.3673)" stroke="currentColor" stroke-width="1.5"><line x1="16" y1="2.13265306" x2="19.0816327" y2="2.13265306" id="Line-2" stroke-linecap="round"></line><line x1="0.0816326531" y1="2.13265306" x2="11" y2="2.13265306" id="Line-2" stroke-linecap="round"></line><circle id="Oval" cx="13.2244898" cy="2.09183673" r="2.09183673"></circle></g></g></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -56,8 +56,10 @@ export default {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "~@/styles/mixin.scss"; @use "@/styles/mixin.scss" as *;
@import "~@/styles/variables.module.scss"; @use "@/styles/variables.module.scss" as *;
// @import "~@/styles/mixin.scss";
// @import "~@/styles/variables.module.scss";
.app-wrapper { .app-wrapper {
@include clearfix; @include clearfix;

View File

@ -1,5 +1,6 @@
import Vue from 'vue' 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 'normalize.css/normalize.css' // A modern alternative to CSS resets
// import { createVersionPolling } from "@/utils/version-polling.esm.js"; // import { createVersionPolling } from "@/utils/version-polling.esm.js";
import ElementUI, { MessageBox } from 'element-ui' import ElementUI, { MessageBox } from 'element-ui'
@ -70,6 +71,9 @@ import FB from '@/components/feedBack/index'
Vue.use(FB) Vue.use(FB)
import FBT from '@/components/feedBackTable/index' import FBT from '@/components/feedBackTable/index'
Vue.use(FBT) Vue.use(FBT)
import resize from '@/directive/resize/index'
// 表格自适应指令
Vue.use(resize)
import adaptive from '@/directive/adaptive/index' import adaptive from '@/directive/adaptive/index'
// 表格自适应指令 // 表格自适应指令
Vue.use(adaptive) Vue.use(adaptive)
@ -334,8 +338,10 @@ async function VueInit() {
}) })
} }
let CompanyInfo = JSON.parse(localStorage.getItem('CompanyInfo')) let CompanyInfo = JSON.parse(localStorage.getItem('CompanyInfo'))
if (CompanyInfo && CompanyInfo.SystemShortName && text.toUpperCase() !== "CIRCLE") { if (CompanyInfo && CompanyInfo.SystemShortName && (text.toUpperCase() !== "CIRCLE")) {
let test = new RegExp('IRC', 'ig') // 精准匹配独立单词 IRC
// let test = new RegExp('IRC', 'ig')
let test = new RegExp('\\bIRC\\b', 'ig')
text = text.replace(test, CompanyInfo.SystemShortName) text = text.replace(test, CompanyInfo.SystemShortName)
} }
// return i18n.t(key) // return i18n.t(key)
@ -446,6 +452,7 @@ async function VueInit() {
isOpen = false isOpen = false
isLock = null isLock = null
zzSessionStorage.removeItem('isLock') zzSessionStorage.removeItem('isLock')
// DicomEvent.$emit('isLock', false)
router.push("/login") router.push("/login")
}).then(() => { }).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!', { // _vm.$alert(lang === 'zh' ? '由于您长时间未操作,为保护您的数据安全已强制将您下线,如果需要继续操作请重新登陆!' : 'No operation for a long time non-operation, you have been forced logout to protect data security. If continue to operate, please login again!', {
@ -457,7 +464,8 @@ async function VueInit() {
}) })
} : () => { }, process.env.VUE_APP_LOGOUT_FOR_TIME, } : () => { }, process.env.VUE_APP_LOGOUT_FOR_TIME,
eval(process.env.VUE_APP_LOCK_FOR_PERMISSION) ? () => { eval(process.env.VUE_APP_LOCK_FOR_PERMISSION)
? () => {
var lang = zzSessionStorage.getItem('lang') ? zzSessionStorage.getItem('lang') : 'zh' var lang = zzSessionStorage.getItem('lang') ? zzSessionStorage.getItem('lang') : 'zh'
if (_vm.$store.state.trials.unlock || WHITELIST.includes(_vm.$route.path)) { if (_vm.$store.state.trials.unlock || WHITELIST.includes(_vm.$route.path)) {
count = 0; count = 0;
@ -465,6 +473,7 @@ async function VueInit() {
if (_vm.$route.path === '/login') { if (_vm.$route.path === '/login') {
zzSessionStorage.removeItem('lastWorkbench') zzSessionStorage.removeItem('lastWorkbench')
zzSessionStorage.removeItem('isLock') zzSessionStorage.removeItem('isLock')
// DicomEvent.$emit('isLock', false)
isLock = null isLock = null
} }
return return
@ -474,6 +483,7 @@ async function VueInit() {
} }
isOpen = true isOpen = true
zzSessionStorage.setItem('isLock', 'true') zzSessionStorage.setItem('isLock', 'true')
// DicomEvent.$emit('isLock', true)
_vm.$msgbox({ _vm.$msgbox({
title: _vm.$t("env:lock:msgBox:title"), title: _vm.$t("env:lock:msgBox:title"),
confirmButtonText: _vm.$t("env:lock:msgBox:confirmButtonText"), confirmButtonText: _vm.$t("env:lock:msgBox:confirmButtonText"),
@ -505,7 +515,8 @@ async function VueInit() {
isOpen = false isOpen = false
count = 0; count = 0;
isLock = null isLock = null
zzSessionStorage.removeItem('isLock') // zzSessionStorage.removeItem('isLock')
// DicomEvent.$emit('isLock', false)
localStorage.setItem('count', '0') localStorage.setItem('count', '0')
document.querySelector('#my_username').value = null document.querySelector('#my_username').value = null
document.querySelector('#my_password').value = null document.querySelector('#my_password').value = null
@ -610,7 +621,9 @@ async function VueInit() {
]) ])
]) ])
}) })
} : () => { }, process.env.VUE_APP_LOCK_FOR_TIME) } : () => { },
process.env.VUE_APP_LOCK_FOR_TIME
)
} }
VueInit() VueInit()
// createVersionPolling({ // createVersionPolling({

View File

@ -156,12 +156,6 @@ export const constantRoutes = [
hidden: true, hidden: true,
component: () => import('@/views/trials/trials-panel/reading/dicoms/components/Fusion/PetCt') component: () => import('@/views/trials/trials-panel/reading/dicoms/components/Fusion/PetCt')
}, },
{
path: '/fusion',
name: 'fusion',
hidden: true,
component: () => import('@/views/trials/trials-panel/reading/dicoms/components/Fusion/demo/index')
},
{ {
path: '/historyScreenshot', path: '/historyScreenshot',

View File

@ -79,7 +79,7 @@ function getQuestions(questions) {
answerObj.angle = angle answerObj.angle = angle
answerObj.saveTypeEnum = isNaN(parseFloat(angle)) ? 1 : 2 answerObj.saveTypeEnum = isNaN(parseFloat(angle)) ? 1 : 2
} }
} else if (criterionType === 21) { } else if (criterionType === 21 || criterionType === 22) {
// MRI-PDFF // MRI-PDFF
let isMeasurable = getQuestionAnswer(item.TableQuestions.Questions, 1105, answerObj) let isMeasurable = getQuestionAnswer(item.TableQuestions.Questions, 1105, answerObj)
answerObj.isMeasurable = isMeasurable answerObj.isMeasurable = isMeasurable
@ -699,7 +699,7 @@ const actions = {
var measureData = state.visitTaskList[index].MeasureData var measureData = state.visitTaskList[index].MeasureData
// var idx = measureData.findIndex(item => item.MeasureData.uuid === obj.data.MeasureData.data.uuid) // var idx = measureData.findIndex(item => item.MeasureData.uuid === obj.data.MeasureData.data.uuid)
if (criterionType === 21) { if (criterionType === 21 || criterionType === 22) {
let i = measureData.findIndex(i => i.TableQuestionId === obj.data.TableQuestionId) let i = measureData.findIndex(i => i.TableQuestionId === obj.data.TableQuestionId)
if (i > -1) { if (i > -1) {
for (const k in state.visitTaskList[index].MeasureData[i]) { for (const k in state.visitTaskList[index].MeasureData[i]) {
@ -932,6 +932,12 @@ const actions = {
data.IsDicom = study.IsDicom data.IsDicom = study.IsDicom
data.PreviewImageCount = 0 data.PreviewImageCount = 0
data.IsCriticalSequence = study.IsCriticalSequence 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 = [] var seriesList = []
study.SeriesList.forEach((series, seriesIndex) => { study.SeriesList.forEach((series, seriesIndex) => {
const imageIds = [] const imageIds = []

View File

@ -1,15 +1,16 @@
@import "./variables.module.scss"; @use "./variables.module.scss";
@import "./mixin.scss"; @use "./mixin.scss";
@import "./transition.scss"; @use "./transition.scss";
@import "./element-ui.scss"; @use "./element-ui.scss";
@import "./sidebar.scss"; @use "./sidebar.scss";
body { body {
height: 100%; height: 100%;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", "微软雅黑", Arial, sans-serif; font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB",
"Microsoft YaHei", "微软雅黑", Arial, sans-serif;
// font-family:"Helvetica Neue", Helvetica, Arial, sans-serif; // font-family:"Helvetica Neue", Helvetica, Arial, sans-serif;
// font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", // font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
// "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial, // "Hiragino Sans GB", "Microsoft YaHei", "Helvetica Neue", Helvetica, Arial,
@ -205,139 +206,143 @@ body .el-table th.gutter {
.el-descriptions { .el-descriptions {
box-sizing: border-box; box-sizing: border-box;
font-size: 14px; font-size: 14px;
color: #303133 color: #303133;
} }
.el-descriptions__header { .el-descriptions__header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
margin-bottom: 20px margin-bottom: 20px;
} }
.el-descriptions__title { .el-descriptions__title {
font-size: 16px; font-size: 16px;
font-weight: 700 font-weight: 700;
} }
.el-descriptions__body { .el-descriptions__body {
color: #606266; color: #606266;
background-color: #fff background-color: #fff;
} }
.el-descriptions__body .el-descriptions__table { .el-descriptions__body .el-descriptions__table {
border-collapse: collapse; border-collapse: collapse;
width: 100%; width: 100%;
table-layout: fixed table-layout: fixed;
} }
.el-descriptions__body .el-descriptions__table .el-descriptions-item__cell { .el-descriptions__body .el-descriptions__table .el-descriptions-item__cell {
box-sizing: border-box; box-sizing: border-box;
text-align: left; text-align: left;
font-weight: 400; font-weight: 400;
line-height: 1.5 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 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 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 text-align: right;
} }
.el-descriptions .is-bordered { .el-descriptions .is-bordered {
table-layout: auto table-layout: auto;
} }
.el-descriptions .is-bordered .el-descriptions-item__cell { .el-descriptions .is-bordered .el-descriptions-item__cell {
border: 1px solid #ebeef5; border: 1px solid #ebeef5;
padding: 12px 10px padding: 12px 10px;
} }
.el-descriptions :not(.is-bordered) .el-descriptions-item__cell { .el-descriptions :not(.is-bordered) .el-descriptions-item__cell {
padding-bottom: 12px padding-bottom: 12px;
} }
.el-descriptions--medium.is-bordered .el-descriptions-item__cell { .el-descriptions--medium.is-bordered .el-descriptions-item__cell {
padding: 10px padding: 10px;
} }
.el-descriptions--medium:not(.is-bordered) .el-descriptions-item__cell { .el-descriptions--medium:not(.is-bordered) .el-descriptions-item__cell {
padding-bottom: 10px padding-bottom: 10px;
} }
.el-descriptions--small { .el-descriptions--small {
font-size: 12px font-size: 12px;
} }
.el-descriptions--small.is-bordered .el-descriptions-item__cell { .el-descriptions--small.is-bordered .el-descriptions-item__cell {
padding: 8px 10px padding: 8px 10px;
} }
.el-descriptions--small:not(.is-bordered) .el-descriptions-item__cell { .el-descriptions--small:not(.is-bordered) .el-descriptions-item__cell {
padding-bottom: 8px padding-bottom: 8px;
} }
.el-descriptions--mini { .el-descriptions--mini {
font-size: 12px font-size: 12px;
} }
.el-descriptions--mini.is-bordered .el-descriptions-item__cell { .el-descriptions--mini.is-bordered .el-descriptions-item__cell {
padding: 6px 10px padding: 6px 10px;
} }
.el-descriptions--mini:not(.is-bordered) .el-descriptions-item__cell { .el-descriptions--mini:not(.is-bordered) .el-descriptions-item__cell {
padding-bottom: 6px padding-bottom: 6px;
} }
.el-descriptions-item { .el-descriptions-item {
vertical-align: top vertical-align: top;
} }
.el-descriptions-item__container { .el-descriptions-item__container {
display: flex display: flex;
} }
.el-descriptions-item__container .el-descriptions-item__content, .el-descriptions-item__container .el-descriptions-item__content,
.el-descriptions-item__container .el-descriptions-item__label { .el-descriptions-item__container .el-descriptions-item__label {
display: inline-flex; display: inline-flex;
align-items: baseline align-items: baseline;
} }
.el-descriptions-item__container .el-descriptions-item__content { .el-descriptions-item__container .el-descriptions-item__content {
flex: 1 flex: 1;
} }
.el-descriptions-item__label.has-colon:after { .el-descriptions-item__label.has-colon:after {
content: ":"; content: ":";
position: relative; position: relative;
top: -.5px top: -0.5px;
} }
.el-descriptions-item__label.is-bordered-label { .el-descriptions-item__label.is-bordered-label {
font-weight: 700; font-weight: 700;
color: #909399; color: #909399;
background: #fafafa background: #fafafa;
} }
.el-descriptions-item__label:not(.is-bordered-label) { .el-descriptions-item__label:not(.is-bordered-label) {
margin-right: 10px margin-right: 10px;
} }
.el-descriptions-item__content { .el-descriptions-item__content {
word-break: break-word; word-break: break-word;
overflow-wrap: break-word overflow-wrap: break-word;
} }
.feedBack-box { .feedBack-box {
.el-dialog__wrapper { .el-dialog__wrapper {
z-index: 3999 !important; z-index: 3999 !important;
} }
.el-popper {
z-index: 4000 !important;
}
} }
.feedBack-select-box { .feedBack-select-box {
@ -350,7 +355,7 @@ body .el-table th.gutter {
top: 0; top: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
opacity: .5; opacity: 0.5;
background: #000; background: #000;
z-index: 3999; z-index: 3999;
} }

View File

@ -1,6 +1,6 @@
import Vue from 'vue' import Vue from 'vue'
import { anonymization } from './anonymization' import { anonymization } from './anonymization'
export const dcmUpload = async function (data, config, progressFn) { export const dcmUpload = async function (data, config, progressFn, fileInfo) {
return new Promise(async resolve => { return new Promise(async resolve => {
try { try {
// let blob = await encoder(file, config) // let blob = await encoder(file, config)
@ -8,7 +8,8 @@ export const dcmUpload = async function (data, config, progressFn) {
if (config) { if (config) {
blob = await anonymization(data.file, config) blob = await anonymization(data.file, config)
} }
let res = await Vue.prototype.OSSclient.multipartUpload(Object.assign(data, { file: blob.blob }), progressFn)
let res = await Vue.prototype.OSSclient.multipartUpload(Object.assign(data, { file: blob.blob }), progressFn, fileInfo)
resolve({ resolve({
...res, ...res,
image: blob.pixelDataElement image: blob.pixelDataElement

View File

@ -8366,7 +8366,7 @@
for (var i = 0; i < valarr.length; i++) { for (var i = 0; i < valarr.length; i++) {
var checkValue = valarr[i], var checkValue = valarr[i],
checklen = lengths[i], checklen = lengths[i] || 0,
isString = false, isString = false,
displaylen = checklen; displaylen = checklen;

View File

@ -0,0 +1,343 @@
import getNumberValues from "./getNumberValues";
import isNMReconstructable from "./isNMReconstructable";
/**
* Get a subpart of Image Type dicom tag defined by index
* @param {*} dataSet
* @param {*} index 0 based index of the subtype
*/
function getImageTypeSubItemFromDataset(dataSet, index) {
const imageType = dataSet.string("x00080008");
if (imageType) {
const subTypes = imageType.split("\\");
if (subTypes.length > index) {
return subTypes[index];
}
}
return undefined;
}
// DICOM 标准中针对 Enhanced CT/MR/PET 等“增强型多帧”Enhanced Multi-frame影像 强制要求为每一帧显式记录真实的独立空间坐标 。
// - 它们的数据通常存放在: Per-frame Functional Groups Sequence (5200,9230) -> 对应的 Frame Item -> Plane Position Sequence (0020,9113) -> ImagePositionPatient (0020,0032) 。
function getSharedFunctionalGroupsDataSet(dataSet) {
const seq = dataSet?.elements?.x52009229;
if (!seq?.items?.length) {
return;
}
return seq.items[0].dataSet;
}
function getPerFrameFunctionalGroupsDataSet(dataSet, frameIndex) {
const seq = dataSet?.elements?.x52009230;
if (!seq?.items?.length) {
return;
}
if (
!Number.isInteger(frameIndex) ||
frameIndex < 0 ||
frameIndex >= seq.items.length
) {
return;
}
return seq.items[frameIndex].dataSet;
}
function getOrientationFromPlaneOrientationSequence(dataSet) {
const seq = dataSet?.elements?.x00209116;
if (!seq?.items?.length) {
return;
}
return getNumberValues(seq.items[0].dataSet, "x00200037", 6);
}
function getPositionFromPlanePositionSequence(dataSet) {
const seq = dataSet?.elements?.x00209113;
if (!seq?.items?.length) {
return;
}
return getNumberValues(seq.items[0].dataSet, "x00200032", 3);
}
function getPixelSpacingFromPixelMeasuresSequence(dataSet) {
const seq = dataSet?.elements?.x00289110;
if (!seq?.items?.length) {
return;
}
return getNumberValues(seq.items[0].dataSet, "x00280030", 2);
}
function getSliceThicknessFromPixelMeasuresSequence(dataSet) {
const seq = dataSet?.elements?.x00289110;
if (!seq?.items?.length) {
return;
}
if (!seq.items[0]?.dataSet?.elements?.x00180050) {
return;
}
return seq.items[0].dataSet.floatString("x00180050");
}
/**
* Extracts the orientation from NM multiframe dataset, if image type
* equal to RECON TOMO or RECON GATED TOMO
* @param {*} dataSet
* @returns
*/
function extractOrientationFromNMMultiframeDataset(dataSet) {
let imageOrientationPatient;
const modality = dataSet.string("x00080060");
if (modality?.includes("NM")) {
const imageSubType = getImageTypeSubItemFromDataset(dataSet, 2);
if (imageSubType && isNMReconstructable(imageSubType)) {
if (dataSet.elements.x00540022) {
imageOrientationPatient = getNumberValues(
dataSet.elements.x00540022.items[0].dataSet,
"x00200037",
6
);
}
}
}
return imageOrientationPatient;
}
/**
* Extracts the position from NM multiframe dataset, if image type
* equal to RECON TOMO or RECON GATED TOMO
* @param {*} dataSet
* @returns
*/
function extractPositionFromNMMultiframeDataset(dataSet) {
let imagePositionPatient;
const modality = dataSet.string("x00080060");
if (modality?.includes("NM")) {
const imageSubType = getImageTypeSubItemFromDataset(dataSet, 2);
if (imageSubType && isNMReconstructable(imageSubType)) {
if (dataSet.elements.x00540022) {
imagePositionPatient = getNumberValues(
dataSet.elements.x00540022.items[0].dataSet,
"x00200032",
3
);
}
}
}
return imagePositionPatient;
}
function isMultiFrame(dataSet) {
const numberOfFrames =
dataSet.string("x00280008") || dataSet.uint16("x00280008");
return numberOfFrames !== undefined && Number(numberOfFrames) > 1;
}
/**
* Extract orientation information from a dataset. It tries to get the orientation
* from the Detector Information Sequence (for NM images) if image type equal
* to RECON TOMO or RECON GATED TOMO
* @param {*} dataSet
* @returns
*/
function extractOrientationFromDataset(dataSet) {
// let imageOrientationPatient;
// if (isMultiFrame(dataSet)) {
// const perFrameDataSet = getPerFrameFunctionalGroupsDataSet(
// dataSet,
// frameIndex
// );
// imageOrientationPatient =
// getOrientationFromPlaneOrientationSequence(perFrameDataSet);
// if (!imageOrientationPatient) {
// const sharedDataSet = getSharedFunctionalGroupsDataSet(dataSet);
// imageOrientationPatient =
// getOrientationFromPlaneOrientationSequence(sharedDataSet);
// }
// }
// if (!imageOrientationPatient) {
// imageOrientationPatient = getNumberValues(dataSet, "x00200037", 6);
// }
let imageOrientationPatient = getNumberValues(dataSet, "x00200037", 6)
// Trying to get the orientation from the Plane Orientation Sequence
if (!imageOrientationPatient && dataSet.elements.x00209116) {
imageOrientationPatient = getNumberValues(
dataSet.elements.x00209116.items[0].dataSet,
"x00200037",
6
);
}
// If orientation not valid to this point, trying to get the orientation
// from the Detector Information Sequence (for NM images) with image type
// equal to RECON TOMO or RECON GATED TOMO
if (!imageOrientationPatient) {
imageOrientationPatient =
extractOrientationFromNMMultiframeDataset(dataSet);
}
return imageOrientationPatient;
}
/**
* Extract position information from a dataset. It tries to get the position
* from the Detector Information Sequence (for NM images) if image type equal
* to RECON TOMO or RECON GATED TOMO
* @param {*} dataSet
* @returns
*/
function extractPositionFromDataset(dataSet) {
// let imagePositionPatient;
// if (isMultiFrame(dataSet)) {
// const perFrameDataSet = getPerFrameFunctionalGroupsDataSet(
// dataSet,
// frameIndex
// );
// imagePositionPatient =
// getPositionFromPlanePositionSequence(perFrameDataSet);
// if (!imagePositionPatient) {
// const sharedDataSet = getSharedFunctionalGroupsDataSet(dataSet);
// imagePositionPatient =
// getPositionFromPlanePositionSequence(sharedDataSet);
// }
// }
// if (!imagePositionPatient) {
// imagePositionPatient = getNumberValues(dataSet, "x00200032", 3);
// }
let imagePositionPatient = getNumberValues(dataSet, 'x00200032', 3);
// Trying to get the position from the Plane Position Sequence
if (!imagePositionPatient && dataSet.elements.x00209113) {
imagePositionPatient = getNumberValues(
dataSet.elements.x00209113.items[0].dataSet,
"x00200032",
3
);
}
// If position not valid to this point, trying to get the position
// from the Detector Information Sequence (for NM images)
if (!imagePositionPatient) {
imagePositionPatient = extractPositionFromNMMultiframeDataset(dataSet);
}
return imagePositionPatient;
}
/**
* Extract the pixelSpacing information. If exists, extracts this information
* from Pixel Measures Sequence
* @param {*} dataSet
* @returns
*/
function extractSpacingFromDataset(dataSet) {
// let pixelSpacing = getNumberValues(dataSet, 'x00280030', 2);
// // If pixelSpacing not valid to this point, trying to get the spacing
// // from the Pixel Measures Sequence
// if (!pixelSpacing && dataSet.elements.x00289110) {
// pixelSpacing = getNumberValues(
// dataSet.elements.x00289110.items[0].dataSet,
// 'x00280030',
// 2
// );
// }
// return pixelSpacing;
let rowPixelSpacing = null;
let columnPixelSpacing = null;
// let pixelSpacing;
// if (isMultiFrame(dataSet)) {
// const perFrameDataSet = getPerFrameFunctionalGroupsDataSet(
// dataSet,
// frameIndex
// );
// const sharedDataSet = getSharedFunctionalGroupsDataSet(dataSet);
// pixelSpacing =
// getPixelSpacingFromPixelMeasuresSequence(perFrameDataSet) ||
// getPixelSpacingFromPixelMeasuresSequence(sharedDataSet);
// }
// if (!pixelSpacing) {
// pixelSpacing =
// getNumberValues(dataSet, "x00280030", 2) ||
// getPixelSpacingFromPixelMeasuresSequence(dataSet);
// }
let pixelSpacing = getNumberValues(dataSet, 'x00280030', 2);
const imagePixelSpacing = getNumberValues(dataSet, "x00181164", 2);
const estimatedRadiographicMagnificationFactor = getNumberValues(
dataSet,
"x00181114",
2
);
if (pixelSpacing) {
rowPixelSpacing = pixelSpacing[0];
columnPixelSpacing = pixelSpacing[1];
} else if (imagePixelSpacing && estimatedRadiographicMagnificationFactor) {
rowPixelSpacing =
imagePixelSpacing[0] / estimatedRadiographicMagnificationFactor[0];
columnPixelSpacing =
imagePixelSpacing[1] / estimatedRadiographicMagnificationFactor[1];
} else if (imagePixelSpacing && !estimatedRadiographicMagnificationFactor) {
rowPixelSpacing = imagePixelSpacing[0];
columnPixelSpacing = imagePixelSpacing[1];
}
return rowPixelSpacing !== null
? [rowPixelSpacing, columnPixelSpacing]
: undefined;
}
/**
* Extract the sliceThickness information. If exists, extracts this information
* from Pixel Measures Sequence
* @param {*} dataSet
* @returns
*/
function extractSliceThicknessFromDataset(dataSet) {
let sliceThickness;
if (dataSet.elements.x00180050) {
sliceThickness = dataSet.floatString('x00180050');
}
else if (dataSet.elements.x00289110 &&
dataSet.elements.x00289110.items.length &&
dataSet.elements.x00289110.items[0].dataSet.elements.x00180050) {
sliceThickness =
dataSet.elements.x00289110.items[0].dataSet.floatString('x00180050');
}
// if (sliceThickness === undefined && isMultiFrame(dataSet)) {
// const perFrameDataSet = getPerFrameFunctionalGroupsDataSet(
// dataSet,
// frameIndex
// );
// const sharedDataSet = getSharedFunctionalGroupsDataSet(dataSet);
// sliceThickness =
// getSliceThicknessFromPixelMeasuresSequence(perFrameDataSet) ||
// getSliceThicknessFromPixelMeasuresSequence(sharedDataSet);
// }
return sliceThickness;
}
export {
getImageTypeSubItemFromDataset,
extractOrientationFromDataset,
extractPositionFromDataset,
extractSpacingFromDataset,
extractSliceThicknessFromDataset,
};

View File

@ -0,0 +1,24 @@
function getNumberValues(
dataSet,
tag,
minimumLength
){
const values = [];
const valueAsString = dataSet.string(tag);
if (!valueAsString) {
return;
}
const split = valueAsString.split('\\');
if (minimumLength && split.length < minimumLength) {
return;
}
for (let i = 0; i < split.length; i++) {
values.push(parseFloat(split[i]));
}
return values;
}
export default getNumberValues;

View File

@ -0,0 +1,3 @@
export default function isNMReconstructable(imageSubType) {
return imageSubType === 'RECON TOMO' || imageSubType === 'RECON GATED TOMO';
}

View File

@ -1,25 +1,123 @@
import * as cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader' import * as cornerstoneWADOImageLoader from "cornerstone-wado-image-loader";
function parseImageId(imageId) { import store from "@/store";
// build a url by parsing out the url scheme and frame index from the imageId import { getPTClinicalDataForInstance } from "@/utils/ptClinicalDataCache";
const firstColonIndex = imageId.indexOf(':'); import {
getImageTypeSubItemFromDataset,
extractOrientationFromDataset,
extractPositionFromDataset,
extractSpacingFromDataset,
extractSliceThicknessFromDataset,
} from "./extractPositioningFromDataset";
let url = imageId.substring(firstColonIndex + 1); function toNumber(val) {
const frameIndex = url.indexOf('frame='); if (val === undefined || val === null || val === "") return null;
const n = typeof val === "number" ? val : parseFloat(val);
return Number.isFinite(n) ? n : null;
}
let frame; function normalizeDicomTime(value) {
if (value === undefined || value === null || value === "") return null;
const num = toNumber(value);
if (num != null) {
return String(Math.floor(num)).padStart(6, "0");
}
if (typeof value !== "string") return null;
const trimmed = value.trim();
if (!trimmed) return null;
const plain = trimmed.replace(/:/g, "");
const parts = plain.split(".");
const main = parts[0];
const fractional = parts[1];
if (!/^\d+$/.test(main)) {
return trimmed;
}
const normalizedMain = main.padStart(6, "0");
return fractional ? `${normalizedMain}.${fractional}` : normalizedMain;
}
if (frameIndex !== -1) { function getPTClinicalOverrideFromImageId(imageId) {
const frameStr = url.substr(frameIndex + 6); 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");
frame = parseInt(frameStr, 10); // 场景1trials/dicomsimageId 带 visitTaskId + idx优先使用列表中的最新 study 值
url = url.substr(0, frameIndex - 1); 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;
}
}
}
} }
return { // 场景2src/components/DicomimageId 仅带 instanceId回退到实例缓存
scheme: imageId.substr(0, firstColonIndex), const instanceId = params.get("instanceId");
url, if (instanceId) {
frame, const cached = getPTClinicalDataForInstance(instanceId);
}; if (cached) return cached;
}
return null;
} catch (e) {
return null;
}
}
function parseImageId(imageId) {
// 兼容 frame 参数不在最后的情况:只移除 frame 参数本身,保留 instanceId/visitTaskId/idx 等其余 query
const firstColonIndex = imageId.indexOf(":");
const scheme = imageId.substr(0, firstColonIndex);
const urlPart = imageId.substring(firstColonIndex + 1);
let url = urlPart;
let frame;
const qIndex = urlPart.indexOf("?");
if (qIndex !== -1) {
const base = urlPart.slice(0, qIndex);
const query = urlPart.slice(qIndex + 1);
const parts = query.split("&").filter(Boolean);
const preservedParts = [];
parts.forEach((part) => {
const eqIndex = part.indexOf("=");
const key = eqIndex === -1 ? part : part.slice(0, eqIndex);
const value = eqIndex === -1 ? "" : part.slice(eqIndex + 1);
if (key === "frame") {
const n = value !== "" ? parseInt(value, 10) : NaN;
if (Number.isFinite(n)) {
frame = n;
}
return;
}
// 保留原始 query 片段,避免 URLSearchParams 把 | 编码成 %7C 导致 dataSetCache key 对不上
preservedParts.push(part);
});
url = preservedParts.length ? `${base}?${preservedParts.join("&")}` : base;
}
return { scheme, url, frame };
} }
function getNumberValues(dataSet, tag, minimumLength) { function getNumberValues(dataSet, tag, minimumLength) {
const values = []; const values = [];
@ -28,7 +126,7 @@ function getNumberValues(dataSet, tag, minimumLength) {
if (!valueAsString) { if (!valueAsString) {
return; return;
} }
const split = valueAsString.split('\\'); const split = valueAsString.split("\\");
if (minimumLength && split.length < minimumLength) { if (minimumLength && split.length < minimumLength) {
return; return;
@ -67,28 +165,31 @@ function getLutData(lutDataSet, tag, lutDescriptor) {
return lut; return lut;
} }
function populateSmallestLargestPixelValues(dataSet, imagePixelModule) { function populateSmallestLargestPixelValues(dataSet, imagePixelModule) {
const pixelRepresentation = dataSet.uint16('x00280103'); const pixelRepresentation = dataSet.uint16("x00280103");
if (pixelRepresentation === 0) { if (pixelRepresentation === 0) {
imagePixelModule.smallestPixelValue = dataSet.uint16('x00280106'); imagePixelModule.smallestPixelValue = dataSet.uint16("x00280106");
imagePixelModule.largestPixelValue = dataSet.uint16('x00280107'); imagePixelModule.largestPixelValue = dataSet.uint16("x00280107");
} else { } else {
imagePixelModule.smallestPixelValue = dataSet.int16('x00280106'); imagePixelModule.smallestPixelValue = dataSet.int16("x00280106");
imagePixelModule.largestPixelValue = dataSet.int16('x00280107'); 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) { function populatePaletteColorLut(dataSet, imagePixelModule) {
imagePixelModule.redPaletteColorLookupTableDescriptor = getLutDescriptor( imagePixelModule.redPaletteColorLookupTableDescriptor = getLutDescriptor(
dataSet, dataSet,
'x00281101' "x00281101"
); );
imagePixelModule.greenPaletteColorLookupTableDescriptor = getLutDescriptor( imagePixelModule.greenPaletteColorLookupTableDescriptor = getLutDescriptor(
dataSet, dataSet,
'x00281102' "x00281102"
); );
imagePixelModule.bluePaletteColorLookupTableDescriptor = getLutDescriptor( imagePixelModule.bluePaletteColorLookupTableDescriptor = getLutDescriptor(
dataSet, dataSet,
'x00281103' "x00281103"
); );
// The first Palette Color Lookup Table Descriptor value is the number of entries in the lookup table. // The first Palette Color Lookup Table Descriptor value is the number of entries in the lookup table.
@ -127,33 +228,196 @@ function populatePaletteColorLut(dataSet, imagePixelModule) {
imagePixelModule.redPaletteColorLookupTableData = getLutData( imagePixelModule.redPaletteColorLookupTableData = getLutData(
dataSet, dataSet,
'x00281201', "x00281201",
imagePixelModule.redPaletteColorLookupTableDescriptor imagePixelModule.redPaletteColorLookupTableDescriptor
); );
imagePixelModule.greenPaletteColorLookupTableData = getLutData( imagePixelModule.greenPaletteColorLookupTableData = getLutData(
dataSet, dataSet,
'x00281202', "x00281202",
imagePixelModule.greenPaletteColorLookupTableDescriptor imagePixelModule.greenPaletteColorLookupTableDescriptor
); );
imagePixelModule.bluePaletteColorLookupTableData = getLutData( imagePixelModule.bluePaletteColorLookupTableData = getLutData(
dataSet, dataSet,
'x00281203', "x00281203",
imagePixelModule.bluePaletteColorLookupTableDescriptor imagePixelModule.bluePaletteColorLookupTableDescriptor
); );
} }
function getSpacingBetweenSlices(dataSet) {
if (dataSet?.elements?.x00180088) {
return dataSet.floatString("x00180088");
}
const pixelMeasuresSequence = dataSet?.elements?.x00289110;
if (
pixelMeasuresSequence?.items?.length &&
pixelMeasuresSequence.items[0]?.dataSet?.elements?.x00180088
) {
return pixelMeasuresSequence.items[0].dataSet.floatString("x00180088");
}
}
function metaDataProvider(type, imageId) { function metaDataProvider(type, imageId) {
const parsedImageId = parseImageId(imageId); const parsedImageId = parseImageId(imageId);
const dataSet = cornerstoneWADOImageLoader.wadouri.dataSetCacheManager.get(parsedImageId.url); const dataSet = cornerstoneWADOImageLoader.wadouri.dataSetCacheManager.get(
parsedImageId.url
);
if (!dataSet) { if (!dataSet) {
return; return;
} }
if (type === 'imagePlaneModule') { const ptOverride = getPTClinicalOverrideFromImageId(imageId);
const imageOrientationPatient = getNumberValues(dataSet, 'x00200037', 6); if (type === "generalSeriesModule") {
const imagePositionPatient = getNumberValues(dataSet, 'x00200032', 3); const { dicomParser } = cornerstoneWADOImageLoader.external || {};
const pixelSpacing = getNumberValues(dataSet, 'x00280030', 2); const overrideTime = normalizeDicomTime(ptOverride?.AcquisitionTime);
const imagePixelSpacing = getNumberValues(dataSet, 'x00181164', 2); const seriesTimeRaw =
const estimatedRadiographicMagnificationFactor = getNumberValues(dataSet, 'x00181114', 2); overrideTime ||
dataSet.string("x00080031") ||
dataSet.string("x00080032") ||
"";
const acquisitionTimeRaw =
overrideTime ||
dataSet.string("x00080032") ||
dataSet.string("x00080031") ||
"";
return {
modality: dataSet.string("x00080060"),
seriesInstanceUID: dataSet.string("x0020000e"),
seriesNumber: dataSet.intString("x00200011"),
studyInstanceUID: dataSet.string("x0020000d"),
seriesDate: dicomParser.parseDA(dataSet.string("x00080021")),
// 2D SUV 计算读取的是 seriesTime这里同步使用用户录入采集时间
// seriesTime: dicomParser.parseTM(String(seriesTimeRaw)),
seriesTime: dicomParser.parseTM(dataSet.string('x00080031') || ''),
acquisitionDate: dicomParser.parseDA(dataSet.string("x00080022") || ""),
acquisitionTime: dicomParser.parseTM(String(acquisitionTimeRaw)),
};
}
if (type === "patientStudyModule") {
const weightOverride = ptOverride?.PatientWeight;
const weight =
weightOverride
? toNumber(weightOverride)
: dataSet.floatString("x00101030");
return {
patientAge: dataSet.intString("x00101010"),
patientSize: dataSet.floatString("x00101020"),
patientWeight: weight,
};
}
if (type === "petIsotopeModule") {
const { dicomParser } = cornerstoneWADOImageLoader.external || {};
const radiopharmaceuticalInfo = dataSet.elements.x00540016;
if (radiopharmaceuticalInfo === undefined) {
return;
}
const firstRadiopharmaceuticalInfoDataSet =
radiopharmaceuticalInfo.items[0].dataSet;
const startTimeRaw =
firstRadiopharmaceuticalInfoDataSet.string("x00181072") || "";
const totalDoseRaw =
firstRadiopharmaceuticalInfoDataSet.floatString("x00181074");
const halfLifeRaw =
firstRadiopharmaceuticalInfoDataSet.floatString("x00181075");
let startTimeValue = normalizeDicomTime(
ptOverride?.RadiopharmaceuticalStartTime
);
if (!startTimeValue && startTimeRaw) {
startTimeValue = normalizeDicomTime(startTimeRaw) || startTimeRaw;
}
const radiopharmaceuticalStartTime = dicomParser.parseTM(
String(startTimeValue)
);
const overrideTotalDose =
ptOverride?.RadionuclideTotalDose != null &&
ptOverride.RadionuclideTotalDose !== ""
? toNumber(ptOverride.RadionuclideTotalDose)
: null;
const overrideHalfLife =
ptOverride?.RadionuclideHalfLife != null &&
ptOverride.RadionuclideHalfLife !== ""
? toNumber(ptOverride.RadionuclideHalfLife)
: null;
const radionuclideTotalDose =
overrideTotalDose != null ? overrideTotalDose : toNumber(totalDoseRaw);
const radionuclideHalfLife =
overrideHalfLife != null ? overrideHalfLife : toNumber(halfLifeRaw);
return {
radiopharmaceuticalInfo: {
radiopharmaceuticalStartTime,
radionuclideTotalDose,
radionuclideHalfLife,
},
};
}
if (type === "imagePlaneModule") {
// const imageOrientationPatient = getNumberValues(dataSet, 'x00200037', 6);
// const imagePositionPatient = getNumberValues(dataSet, 'x00200032', 3);
// const pixelSpacing = getNumberValues(dataSet, 'x00280030', 2);
const imagePixelSpacing = getNumberValues(dataSet, "x00181164", 2);
const frameIndex =
parsedImageId.frame !== undefined ? parsedImageId.frame - 1 : undefined;
const imageOrientationPatient = extractOrientationFromDataset(
dataSet,
frameIndex
);
let imagePositionPatient = extractPositionFromDataset(dataSet, frameIndex);
const pixelSpacing = extractSpacingFromDataset(dataSet, frameIndex);
const sliceThickness = extractSliceThicknessFromDataset(
dataSet,
frameIndex
);
const modality = dataSet.string("x00080060");
if (
modality &&
modality.includes("NM") &&
parsedImageId.frame !== undefined &&
parsedImageId.frame > 1
) {
const spacingBetweenSlices = getSpacingBetweenSlices(dataSet);
const step =
spacingBetweenSlices !== undefined
? spacingBetweenSlices
: sliceThickness;
if (
imageOrientationPatient &&
imagePositionPatient &&
step !== undefined &&
frameIndex !== undefined
) {
const rowCosines = [
parseFloat(imageOrientationPatient[0]),
parseFloat(imageOrientationPatient[1]),
parseFloat(imageOrientationPatient[2]),
];
const columnCosines = [
parseFloat(imageOrientationPatient[3]),
parseFloat(imageOrientationPatient[4]),
parseFloat(imageOrientationPatient[5]),
];
const normal = [
rowCosines[1] * columnCosines[2] - rowCosines[2] * columnCosines[1],
rowCosines[2] * columnCosines[0] - rowCosines[0] * columnCosines[2],
rowCosines[0] * columnCosines[1] - rowCosines[1] * columnCosines[0],
];
const offset = frameIndex * parseFloat(step);
imagePositionPatient = [
parseFloat(imagePositionPatient[0]) + normal[0] * offset,
parseFloat(imagePositionPatient[1]) + normal[1] * offset,
parseFloat(imagePositionPatient[2]) + normal[2] * offset,
];
}
}
const estimatedRadiographicMagnificationFactor = getNumberValues(
dataSet,
"x00181114",
2
);
let columnPixelSpacing = null; let columnPixelSpacing = null;
let rowPixelSpacing = null; let rowPixelSpacing = null;
@ -162,8 +426,10 @@ function metaDataProvider(type, imageId) {
rowPixelSpacing = pixelSpacing[0]; rowPixelSpacing = pixelSpacing[0];
columnPixelSpacing = pixelSpacing[1]; columnPixelSpacing = pixelSpacing[1];
} else if (imagePixelSpacing && estimatedRadiographicMagnificationFactor) { } else if (imagePixelSpacing && estimatedRadiographicMagnificationFactor) {
rowPixelSpacing = imagePixelSpacing[0] / estimatedRadiographicMagnificationFactor[0]; rowPixelSpacing =
columnPixelSpacing = imagePixelSpacing[1] / estimatedRadiographicMagnificationFactor[1]; imagePixelSpacing[0] / estimatedRadiographicMagnificationFactor[0];
columnPixelSpacing =
imagePixelSpacing[1] / estimatedRadiographicMagnificationFactor[1];
} else if (imagePixelSpacing && !estimatedRadiographicMagnificationFactor) { } else if (imagePixelSpacing && !estimatedRadiographicMagnificationFactor) {
rowPixelSpacing = imagePixelSpacing[0]; rowPixelSpacing = imagePixelSpacing[0];
columnPixelSpacing = imagePixelSpacing[1]; columnPixelSpacing = imagePixelSpacing[1];
@ -187,43 +453,48 @@ function metaDataProvider(type, imageId) {
} }
return { return {
frameOfReferenceUID: dataSet.string('x00200052'), frameOfReferenceUID: dataSet.string("x00200052"),
rows: dataSet.uint16('x00280010'), rows: dataSet.uint16("x00280010"),
columns: dataSet.uint16('x00280011'), columns: dataSet.uint16("x00280011"),
imageOrientationPatient, imageOrientationPatient,
rowCosines, rowCosines,
columnCosines, columnCosines,
imagePositionPatient, imagePositionPatient,
sliceThickness: dataSet.floatString('x00180050'), sliceThickness,
sliceLocation: dataSet.floatString('x00201041'), sliceLocation: dataSet.floatString("x00201041"),
pixelSpacing, pixelSpacing,
rowPixelSpacing, rowPixelSpacing,
columnPixelSpacing, columnPixelSpacing,
}; };
} }
if (type === 'imagePixelModule') { if (type === "imagePixelModule") {
const imagePixelModule = { const imagePixelModule = {
samplesPerPixel: dataSet.uint16('x00280002'), samplesPerPixel: dataSet.uint16("x00280002"),
photometricInterpretation: dataSet.string('x00280004'), photometricInterpretation: dataSet.string("x00280004"),
rows: dataSet.uint16('x00280010'), rows: dataSet.uint16("x00280010"),
columns: dataSet.uint16('x00280011'), columns: dataSet.uint16("x00280011"),
bitsAllocated: dataSet.uint16('x00280100'), bitsAllocated: dataSet.uint16("x00280100"),
bitsStored: dataSet.uint16('x00280101'), bitsStored: dataSet.uint16("x00280101"),
highBit: dataSet.uint16('x00280102'), highBit: dataSet.uint16("x00280102"),
pixelRepresentation: dataSet.uint16('x00280103'), pixelRepresentation: dataSet.uint16("x00280103"),
planarConfiguration: dataSet.uint16('x00280006'), planarConfiguration: dataSet.uint16("x00280006"),
pixelAspectRatio: dataSet.string('x00280034'), pixelAspectRatio: dataSet.string("x00280034"),
}; };
populateSmallestLargestPixelValues(dataSet, imagePixelModule); populateSmallestLargestPixelValues(dataSet, imagePixelModule);
if ( if (
imagePixelModule.photometricInterpretation === 'PALETTE COLOR' && imagePixelModule.photometricInterpretation === "PALETTE COLOR" &&
dataSet.elements.x00281101 dataSet.elements.x00281101
) { ) {
populatePaletteColorLut(dataSet, imagePixelModule); populatePaletteColorLut(dataSet, imagePixelModule);
} }
return imagePixelModule; return imagePixelModule;
} }
// if (type === "multiframeModule") {
// return {
// NumberOfFrames: dataSet.uint16("x00280008") || dataSet.string("x00280008")
// };
// }
// if (type === 'imagePixelModule') { // if (type === 'imagePixelModule') {
// return { // return {
// samplesPerPixel: dataSet.uint16('x00280002'), // samplesPerPixel: dataSet.uint16('x00280002'),

View File

@ -5,6 +5,7 @@ const stream = require('stream')
import Vue from 'vue' import Vue from 'vue'
import { customerHttp, OSSclose } from "@/utils/multipartUpload/oss" import { customerHttp, OSSclose } from "@/utils/multipartUpload/oss"
import { exist, AWSclose } from "@/utils/multipartUpload/aws" import { exist, AWSclose } from "@/utils/multipartUpload/aws"
import { addOrUpdateFileUploadRecord } from '@/api/file'
const { GetObjectStoreToken } = require('../api/user.js') const { GetObjectStoreToken } = require('../api/user.js')
const { const {
S3Client, S3Client,
@ -22,7 +23,6 @@ async function ossGenerateSTS() {
res = await GetObjectStoreToken() res = await GetObjectStoreToken()
localStorage.setItem('stsToken', JSON.stringify(res)) localStorage.setItem('stsToken', JSON.stringify(res))
} }
// res.Result.ObjectStoreUse = 'AWS'; // res.Result.ObjectStoreUse = 'AWS';
Vue.prototype.OSSclientConfig = { ...res.Result[res.Result.ObjectStoreUse] } Vue.prototype.OSSclientConfig = { ...res.Result[res.Result.ObjectStoreUse] }
Vue.prototype.OSSclientConfig.ObjectStoreUse = res.Result.ObjectStoreUse; Vue.prototype.OSSclientConfig.ObjectStoreUse = res.Result.ObjectStoreUse;
@ -34,7 +34,8 @@ async function ossGenerateSTS() {
Vue.prototype.OSSclientConfig.timeout = 10 * 60 * 1000 Vue.prototype.OSSclientConfig.timeout = 10 * 60 * 1000
let OSSclient = new OSS(Vue.prototype.OSSclientConfig) let OSSclient = new OSS(Vue.prototype.OSSclientConfig)
Vue.prototype.OSSclient = { Vue.prototype.OSSclient = {
put: async function (objectName, object) { put: async function (objectName, object, fileInfo = {}) {
OSSclient = await RefreshClient(OSSclient) OSSclient = await RefreshClient(OSSclient)
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
try { try {
@ -52,6 +53,20 @@ async function ossGenerateSTS() {
} }
let res = await OSSclient.put(objectName, object) let res = await OSSclient.put(objectName, object)
if (res && res.url) { if (res && res.url) {
const urlParams = new URLSearchParams(window.location.search)
const trialId = urlParams.get('trialId')
if (Object.keys(fileInfo).length !== 0) {
fileInfo.fileType = mimeTypeToExt(fileInfo.fileType)
let params = Object.assign({path: objectName}, fileInfo)
addOrUpdateFileUploadRecord(params)
} else if (trialId) {
const fileName = objectName.split('/').pop()
const fileType = fileName.includes('.')
? fileName.split('.').pop().toLowerCase()
: ''
let params = { trialId, path: objectName, fileName, fileType }
addOrUpdateFileUploadRecord(params)
}
resolve({ resolve({
name: objectName, name: objectName,
url: res.url url: res.url
@ -65,7 +80,7 @@ async function ossGenerateSTS() {
} }
}) })
}, },
multipartUpload: async (data, progress) => { multipartUpload: async (data, progress, fileInfo = {}) => {
OSSclient = await RefreshClient(OSSclient) OSSclient = await RefreshClient(OSSclient)
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
try { try {
@ -95,6 +110,21 @@ async function ossGenerateSTS() {
} }
let res = await customerHttp(OSSclient, data, progress); let res = await customerHttp(OSSclient, data, progress);
if (res) { if (res) {
const urlParams = new URLSearchParams(window.location.search)
const trialId = urlParams.get('trialId')
if (Object.keys(fileInfo).length !== 0) {
fileInfo.fileType = mimeTypeToExt(fileInfo.fileType)
let params = Object.assign({path: data.path}, fileInfo)
addOrUpdateFileUploadRecord(params)
} else if (trialId) {
const fileName = data.path.split('/').pop()
const fileType = fileName.includes('.')
? fileName.split('.').pop().toLowerCase()
: ''
let params = { trialId, path: data.path, fileName, fileType }
addOrUpdateFileUploadRecord(params)
}
resolve({ resolve({
name: data.path, name: data.path,
url: Vue.prototype.OSSclientConfig.viewEndpoint + decodeUtf8(res.name) url: Vue.prototype.OSSclientConfig.viewEndpoint + decodeUtf8(res.name)
@ -169,17 +199,17 @@ async function ossGenerateSTS() {
} }
}); });
Vue.prototype.OSSclient = { Vue.prototype.OSSclient = {
put: async function (objectName, object) { put: async function (objectName, object, fileInfo = {}) {
let data = { let data = {
file: object, file: object,
path: objectName path: objectName
} }
aws = await RefreshClient(aws); aws = await RefreshClient(aws);
return uploadAWS(aws, data, () => { }); return uploadAWS(aws, data, () => { }, fileInfo);
}, },
multipartUpload: async (data, progress) => { multipartUpload: async (data, progress, fileInfo = {}) => {
aws = await RefreshClient(aws); aws = await RefreshClient(aws);
return uploadAWS(aws, data, progress); return uploadAWS(aws, data, progress, fileInfo);
}, },
close: () => { close: () => {
AWSclose(); AWSclose();
@ -189,7 +219,7 @@ async function ossGenerateSTS() {
return return
} }
// AWS上传函数 // AWS上传函数
function uploadAWS(aws, data, progress) { function uploadAWS(aws, data, progress, fileInfo) {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
try { try {
const { file, path } = data; const { file, path } = data;
@ -211,6 +241,21 @@ function uploadAWS(aws, data, progress) {
data.path = data.path.replace(`/${bucketName}/`, ''); data.path = data.path.replace(`/${bucketName}/`, '');
await exist(aws, bucketName, data, progress, (path, status) => { await exist(aws, bucketName, data, progress, (path, status) => {
if (status === 'success') { if (status === 'success') {
const urlParams = new URLSearchParams(window.location.search)
const trialId = urlParams.get('trialId')
if (Object.keys(fileInfo).length !== 0) {
fileInfo.fileType = mimeTypeToExt(fileInfo.fileType)
let params = Object.assign({path: decodeUtf8(curPath)}, fileInfo)
addOrUpdateFileUploadRecord(params)
} else if (trialId) {
const fileName = decodeUtf8(curPath).split('/').pop()
const fileType = fileName.includes('.')
? fileName.split('.').pop().toLowerCase()
: ''
let params = { trialId, path: decodeUtf8(curPath), fileName, fileType }
addOrUpdateFileUploadRecord(params)
}
resolve({ resolve({
name: decodeUtf8(curPath), name: decodeUtf8(curPath),
url: Vue.prototype.OSSclientConfig.viewEndpoint + decodeUtf8(curPath) url: Vue.prototype.OSSclientConfig.viewEndpoint + decodeUtf8(curPath)
@ -320,5 +365,37 @@ function isCredentialsExpired(credentials) {
return expireDate.getTime() - now.getTime() <= 300000; 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 export const OSSclient = ossGenerateSTS

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()
}

View File

@ -28,7 +28,7 @@ async function executeTask() {
task.callback({ success: true, res: result }) task.callback({ success: true, res: result })
}).catch((error) => { }).catch((error) => {
task.callback({ success: true, err: error }) task.callback({ success: false, err: error })
}) })
}) })
// let starLoadTime = performance.now() // let starLoadTime = performance.now()

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版 // 高版本的谷歌edge不支持跨屏需要降低浏览器版本86.0版
let fulls = ''
if (noreferrer) {
fulls = 'noopener,noreferrer'
}
if (!window.getScreens && !window.getScreenDetails) { if (!window.getScreens && !window.getScreenDetails) {
console.log('你的浏览器版本不支持多屏展示功能!'); console.log('你的浏览器版本不支持多屏展示功能!');
return window.open(url, name); return window.open(url, name, fulls);
} }
let permission = await getPermission() let permission = await getPermission()
if (!permission) { if (!permission) {
// alert('使用多屏功能请先进行授权') // alert('使用多屏功能请先进行授权')
if (Skip) { if (Skip) {
return window.open(url, name); return window.open(url, name, fulls);
} }
return false return false
} }
@ -72,14 +76,13 @@ export async function openWindow(url, name, Skip = false) {
// 判断是否2个屏幕 // 判断是否2个屏幕
if (multiScreen.length < 2) { if (multiScreen.length < 2) {
console.log('请接入多个显示屏!'); console.log('请接入多个显示屏!');
return window.open(url, name); return window.open(url, name, fulls);
} }
console.log(screen, 'screen') console.log(screen, 'screen')
// 获取当前屏幕availLeft信息和所有信息比对取另一个屏幕数据 // 获取当前屏幕availLeft信息和所有信息比对取另一个屏幕数据
let currentAvailLeft = screen.availLeft ? screen.availLeft : '0' let currentAvailLeft = screen.availLeft ? screen.availLeft : '0'
let newCurr = multiScreen.find((t) => t.availLeft != currentAvailLeft) let newCurr = multiScreen.find((t) => t.availLeft != currentAvailLeft)
console.log(newCurr, 'newCurr') console.log(newCurr, 'newCurr')
let fulls = ''
for (let key in newCurr) { for (let key in newCurr) {
fulls += `${key}=${(newCurr[key] || newCurr[key] === 0) ? newCurr[key] : 0},` fulls += `${key}=${(newCurr[key] || newCurr[key] === 0) ? newCurr[key] : 0},`
} }

View File

@ -2,9 +2,10 @@
<div ref="mapbox" class="map-wrapper" /> <div ref="mapbox" class="map-wrapper" />
</template> </template>
<script> <script>
import echarts from 'echarts' 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' import { fontSize } from 'utils/fontsize'
echarts.registerMap('world', worldMap);
export default { export default {
props: { props: {
area: { type: String, default: '' }, area: { type: String, default: '' },
@ -14,7 +15,7 @@ export default {
watch: { watch: {
area() { area() {
const that = this const that = this
this.$nextTick(function() { this.$nextTick(function () {
that.myChart.resize() that.myChart.resize()
}) })
} }
@ -56,7 +57,7 @@ export default {
} }
] ]
] ]
var convertData = function(data) { var convertData = function (data) {
var res = [] var res = []
for (var i = 0; i < data.length; i++) { for (var i = 0; i < data.length; i++) {
var dataItem = data[i] var dataItem = data[i]
@ -76,8 +77,8 @@ export default {
} }
return res return res
} }
var series = [] var series = [];
;[['Shanghai', BJData]].forEach(function(item, i) { [['Shanghai', BJData]].forEach(function (item, i) {
series.push( series.push(
{ {
type: 'lines', type: 'lines',
@ -125,11 +126,11 @@ export default {
} }
}, },
symbol: 'circle', symbol: 'circle',
symbolSize: function(val) { symbolSize: function (val) {
return fontSize(0.08) // return fontSize(0.08) //
}, },
data: item[1].map(function(dataItem) { data: item[1].map(function (dataItem) {
return { return {
name: dataItem[0].name, name: dataItem[0].name,
value: geoCoordMap[dataItem[0].name] value: geoCoordMap[dataItem[0].name]
@ -161,7 +162,7 @@ export default {
tooltip: { tooltip: {
trigger: 'item', trigger: 'item',
fontSize: fontSize(0.16), fontSize: fontSize(0.16),
formatter: function(params) { formatter: function (params) {
if (typeof params.value === 'undefined') { if (typeof params.value === 'undefined') {
return '' return ''
} else { } else {

View File

@ -2,7 +2,7 @@
<div ref="trials_stats" style="width:100%;height:100%;" /> <div ref="trials_stats" style="width:100%;height:100%;" />
</template> </template>
<script> <script>
import echarts from 'echarts' import * as echarts from 'echarts'
import { getTrialCountRank } from '@/api/dashboard' import { getTrialCountRank } from '@/api/dashboard'
import { fontSize } from 'utils/fontsize' import { fontSize } from 'utils/fontsize'
const Count = 5 const Count = 5

View File

@ -2,7 +2,7 @@
<div ref="enroll_stats" style="width:100%;height:100%;" /> <div ref="enroll_stats" style="width:100%;height:100%;" />
</template> </template>
<script> <script>
import echarts from 'echarts' import * as echarts from 'echarts'
import { getEnrollDataByQuarter } from '@/api/dashboard' import { getEnrollDataByQuarter } from '@/api/dashboard'
import { fontSize } from 'utils/fontsize' import { fontSize } from 'utils/fontsize'
const Count = 6 const Count = 6

View File

@ -2,7 +2,7 @@
<div ref="reviewers_stats" style="width:100%;height:100%;padding:5px;" /> <div ref="reviewers_stats" style="width:100%;height:100%;padding:5px;" />
</template> </template>
<script> <script>
import echarts from 'echarts' import * as echarts from 'echarts'
import { getReviewersByRank } from '@/api/dashboard' import { getReviewersByRank } from '@/api/dashboard'
import { fontSize } from 'utils/fontsize' import { fontSize } from 'utils/fontsize'
export default { export default {

View File

@ -2,7 +2,7 @@
<div ref="monthly_reading_stats" style="width:100%;height:100%;" /> <div ref="monthly_reading_stats" style="width:100%;height:100%;" />
</template> </template>
<script> <script>
import echarts from 'echarts' import * as echarts from 'echarts'
import { getReadingDataByMonth } from '@/api/dashboard' import { getReadingDataByMonth } from '@/api/dashboard'
import { fontSize } from 'utils/fontsize' import { fontSize } from 'utils/fontsize'
const Count = 6 const Count = 6

View File

@ -2,7 +2,7 @@
<div ref="reading_rank" class="reading-rank" /> <div ref="reading_rank" class="reading-rank" />
</template> </template>
<script> <script>
import echarts from 'echarts' import * as echarts from 'echarts'
import { getReadingDataRank } from '@/api/dashboard' import { getReadingDataRank } from '@/api/dashboard'
import { fontSize } from 'utils/fontsize' import { fontSize } from 'utils/fontsize'
const Count = 5 const Count = 5

View File

@ -2,7 +2,7 @@
<div ref="reading_stats" style="width:100%;height:100%;padding-top:5px;" /> <div ref="reading_stats" style="width:100%;height:100%;padding-top:5px;" />
</template> </template>
<script> <script>
import echarts from 'echarts' import * as echarts from 'echarts'
import { getReadingDataByType } from '@/api/dashboard' import { getReadingDataByType } from '@/api/dashboard'
import { fontSize } from 'utils/fontsize' import { fontSize } from 'utils/fontsize'
export default { export default {

View File

@ -55,6 +55,7 @@
v-for="(instance, idx) in item.instanceInfoList" v-for="(instance, idx) in item.instanceInfoList"
:key="instance.InstanceUid" :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'}" :style="{'margin-bottom':idx<item.instanceInfoList.length-1? '5px':'0px'}"
@click="showMultiFrames(item, index, instance)" @click="showMultiFrames(item, index, instance)"
> >
@ -136,7 +137,8 @@ export default {
studyTitle: '', studyTitle: '',
seriesCount: 0, seriesCount: 0,
seriesList: [], seriesList: [],
currentSeriesIndex: -1 currentSeriesIndex: -1,
activeInstanceUid: null
} }
}, },
@ -231,6 +233,7 @@ export default {
}, },
showMultiFrames(series, seriesIndex, instanceInfo) { showMultiFrames(series, seriesIndex, instanceInfo) {
this.currentSeriesIndex = seriesIndex this.currentSeriesIndex = seriesIndex
this.activeInstanceUid = instanceInfo.InstanceUid
const imageIds = [] const imageIds = []
if (instanceInfo.NumberOfFrames && instanceInfo.NumberOfFrames > 1) { if (instanceInfo.NumberOfFrames && instanceInfo.NumberOfFrames > 1) {
for (let j = 0; j < instanceInfo.NumberOfFrames; j++) { for (let j = 0; j < instanceInfo.NumberOfFrames; j++) {
@ -252,6 +255,7 @@ export default {
showSeriesImage(seriesIndex) { showSeriesImage(seriesIndex) {
// if (seriesIndex === this.currentSeriesIndex) return; // if (seriesIndex === this.currentSeriesIndex) return;
this.currentSeriesIndex = seriesIndex this.currentSeriesIndex = seriesIndex
this.activeInstanceUid = null
this.$refs.dicomViewer.loadImageStack(this.seriesList[seriesIndex]) this.$refs.dicomViewer.loadImageStack(this.seriesList[seriesIndex])
}, },
closeDialog() { closeDialog() {
@ -429,4 +433,8 @@ export default {
border-color: #213a54 !important; border-color: #213a54 !important;
background-color: #213a54; background-color: #213a54;
} }
.frame_content_active {
border-color: #213a54 !important;
background-color: #213a54;
}
</style> </style>

View File

@ -53,6 +53,7 @@
<div class="frame_list"> <div class="frame_list">
<div v-for="(instance, idx) in item.instanceInfoList" :key="instance.Id" <div v-for="(instance, idx) in item.instanceInfoList" :key="instance.Id"
class="frame_content" class="frame_content"
:class="{ 'frame_content_active': activeInstanceId === instance.Id }"
:style="{ 'margin-bottom': idx < item.instanceInfoList.length - 1 ? '5px' : '0px' }" :style="{ 'margin-bottom': idx < item.instanceInfoList.length - 1 ? '5px' : '0px' }"
@click="showMultiFrames(item, index, instance)"> @click="showMultiFrames(item, index, instance)">
<div> <div>
@ -72,17 +73,17 @@
</div> </div>
</div> </div>
<i slot="reference" class="el-icon-connection" style="font-size: 15px;cursor: pointer;" <i slot="reference" class="el-icon-connection" style="font-size: 15px;cursor: pointer;"
@click="popperClick(seriesList, item)" /> @click.stop="popperClick(seriesList, item)" />
</el-popover> </el-popover>
</div> </div>
</div> </div>
<div v-show="item.instanceCount" style="padding: 1px;"> <div v-if="item.instanceCount" style="padding: 1px;">
{{ item.modality }}: {{ item.instanceCount }} image {{ item.modality }}: {{ item.instanceCount }} image
</div> </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) }} T: {{ parseFloat(item.sliceThickness).toFixed(2) }}
</div> </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;"> style="width: 120px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;padding: 1x;">
{{ item.description }} {{ item.description }}
</div> </div>
@ -123,12 +124,12 @@
</div> </div>
</div> </div>
<div class="viewerContent"> <div class="viewerContent">
<dicom-viewer id="dicomViewer" ref="dicomViewer" style="height:100%" /> <dicom-viewer id="dicomViewer" ref="dicomViewer" style="height:100%" :loading.sync="loading"
:modality="modality" :Comparison.sync="isComparison" @loadStudy="loadStudy" />
</div> </div>
<!-- <div class="viewerRightSidePanel"> <!-- <div class="viewerRightSidePanel">
<dicom-tools /> <dicom-tools />
</div> --> </div> -->
</div> </div>
</div> </div>
@ -202,7 +203,9 @@ export default {
isFromCRCUpload: false, isFromCRCUpload: false,
isReading: null, isReading: null,
activeSeriesId: null, activeSeriesId: null,
isPacs: false isPacs: false,
isComparison: false,
activeInstanceId: null
} }
}, },
created: function () { created: function () {
@ -307,7 +310,7 @@ export default {
}) })
}) })
}, },
async loadStudy() { async loadStudy(seriesId = null) {
let params = {} let params = {}
if (this.isPacs) { if (this.isPacs) {
params.IsPacs = true params.IsPacs = true
@ -327,7 +330,7 @@ export default {
isReading = `?IsPacs=true` isReading = `?IsPacs=true`
} }
const url = `/series/list/${this.studyId}${isReading}` const url = `/series/list/${this.studyId}${isReading}`
this.getSeriesList(url) this.getSeriesList(url, seriesId)
} }
}, },
async loadPatientStudy() { async loadPatientStudy() {
@ -392,7 +395,7 @@ export default {
console.log(err) console.log(err)
} }
}, },
async getSeriesList(url) { async getSeriesList(url, seriesId = null) {
try { try {
const data = await getSeriesList(url) const data = await getSeriesList(url)
if (data.IsSuccess) { if (data.IsSuccess) {
@ -431,12 +434,12 @@ export default {
isDeleted: item.IsDeleted, isDeleted: item.IsDeleted,
previewImageUrl: item.ImageResizePath ? this.OSSclientConfig.basePath + item.ImageResizePath : `/api/series/preview/${item.Id}`, previewImageUrl: item.ImageResizePath ? this.OSSclientConfig.basePath + item.ImageResizePath : `/api/series/preview/${item.Id}`,
instanceCount: item.InstanceCount, instanceCount: item.InstanceCount,
prefetchInstanceCount: 0, prefetchInstanceCount: seriesId ? item.InstanceInfoList.length * 100 : 0,
hasLabel: item.HasLabel, hasLabel: item.HasLabel,
keySeries: item.KeySeries, keySeries: item.KeySeries,
tpCode: this.tpCode, tpCode: this.tpCode,
loadStatus: false, loadStatus: false,
imageloadedArr: [], imageloadedArr: seriesId ? imageIds : [],
isExistMutiFrames: item.IsExistMutiFrames, isExistMutiFrames: item.IsExistMutiFrames,
isShowPopper: false, isShowPopper: false,
subjectCode: item.SubjectCode, subjectCode: item.SubjectCode,
@ -446,8 +449,14 @@ export default {
this.seriesList = seriesList this.seriesList = seriesList
if (this.seriesList.length > 0) { if (this.seriesList.length > 0) {
this.loadAllImages() this.loadAllImages()
this.$refs.dicomViewer.loadImageStack(this.seriesList[0], this.labels[this.tpCode]) let index = 0;
this.firstInstanceId = this.seriesList[0].imageIds[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) { } catch (e) {
@ -527,7 +536,9 @@ export default {
} }
}, },
showSeriesImage(e, seriesIndex, series) { showSeriesImage(e, seriesIndex, series) {
if (this.isComparison) return false
this.activeSeriesId = series.seriesId this.activeSeriesId = series.seriesId
this.activeInstanceId = null
workSpeedclose(true) workSpeedclose(true)
// if (seriesIndex === this.currentSeriesIndex) return // if (seriesIndex === this.currentSeriesIndex) return
const element = e.currentTarget const element = e.currentTarget
@ -576,6 +587,7 @@ export default {
}, },
showMultiFrames(series, seriesIndex, instanceInfo) { showMultiFrames(series, seriesIndex, instanceInfo) {
this.currentSeriesIndex = seriesIndex this.currentSeriesIndex = seriesIndex
this.activeInstanceId = instanceInfo.Id
const imageIds = [] const imageIds = []
if (instanceInfo.NumberOfFrames && instanceInfo.NumberOfFrames > 1) { if (instanceInfo.NumberOfFrames && instanceInfo.NumberOfFrames > 1) {
for (let j = 0; j < instanceInfo.NumberOfFrames; j++) { for (let j = 0; j < instanceInfo.NumberOfFrames; j++) {
@ -587,6 +599,7 @@ export default {
const seriesInfo = { const seriesInfo = {
trialId: series.trialId, trialId: series.trialId,
subjectVisitId: series.subjectVisitId, subjectVisitId: series.subjectVisitId,
instanceInfoList: series.instanceInfoList,
studyId: series.studyId, studyId: series.studyId,
imageIds: imageIds, imageIds: imageIds,
seriesId: series.seriesId, seriesId: series.seriesId,
@ -920,6 +933,7 @@ export default {
} }
if (this.visitTaskId === params.visitTaskId) { if (this.visitTaskId === params.visitTaskId) {
const seriesIndex = params.seriesIndex const seriesIndex = params.seriesIndex
if (!this.seriesList[seriesIndex]) return false
var prefetchInstanceCount = this.seriesList[seriesIndex].prefetchInstanceCount var prefetchInstanceCount = this.seriesList[seriesIndex].prefetchInstanceCount
var instanceCount = this.seriesList[seriesIndex].instanceCount var instanceCount = this.seriesList[seriesIndex].instanceCount
if (!this.activeSeriesId) { if (!this.activeSeriesId) {
@ -965,8 +979,8 @@ export default {
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss">
.imageBox { .viewerContainer .imageBox {
position: relative; position: relative;
i { i {
@ -1183,6 +1197,10 @@ export default {
border-color: #213a54 !important; border-color: #213a54 !important;
background-color: #213a54; background-color: #213a54;
} }
.frame_content_active {
border-color: #213a54 !important;
background-color: #213a54;
}
/* .viewerRightSidePanel { /* .viewerRightSidePanel {
width: 300px; width: 300px;

View File

@ -19,25 +19,26 @@
<el-collapse-item v-for="(study, index) in studyList" :key="`${study.StudyId}`" <el-collapse-item v-for="(study, index) in studyList" :key="`${study.StudyId}`"
:name="`${study.StudyId}`"> :name="`${study.StudyId}`">
<template slot="title"> <template slot="title">
<div class="collapse-title-wrapper">
<div class="text-desc"> <div class="text-desc">
{{ study.StudyCode }} {{ study.StudyCode }}
</div> </div>
<!-- <div v-show="study.Description" class="text-desc"> <!-- <div v-show="study.Description" class="text-desc">
{{ study.Description }} {{ study.Description }}
</div> --> </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"> 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;"> style="width: 50px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;padding: 1x;">
{{ study.Description }} {{ study.Description }}
</div> </div>
</el-tooltip> </el-tooltip> -->
<div v-show="study.SeriesCount" class="text-desc"> <div v-if="study.SeriesCount" class="text-desc collapse-title-extra">
{{ study.Modalities }} : {{ study.SeriesCount }} Series {{ study.Modalities }} : {{ study.SeriesCount }} Series
</div> </div>
</div>
</template> </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 }} {{ study.Description }}
</div> </div>
<div v-for="(series, i) in study.SeriesList" :key="i" <div v-for="(series, i) in study.SeriesList" :key="i"
@ -65,6 +66,7 @@
<div class="frame_list"> <div class="frame_list">
<div v-for="(instance, idx) in series.instanceInfoList" :key="instance.Id" <div v-for="(instance, idx) in series.instanceInfoList" :key="instance.Id"
class="frame_content" class="frame_content"
:class="{ 'frame_content_active': activeInstanceId === instance.Id }"
:style="{ 'margin-bottom': idx < series.instanceInfoList.length - 1 ? '5px' : '0px' }" :style="{ 'margin-bottom': idx < series.instanceInfoList.length - 1 ? '5px' : '0px' }"
@click="showMultiFrames(index, series, i, instance)"> @click="showMultiFrames(index, series, i, instance)">
<!-- <div> <!-- <div>
@ -97,12 +99,12 @@
</div> </div>
<i slot="reference" class="el-icon-connection" <i slot="reference" class="el-icon-connection"
style="font-size: 15px;cursor: pointer;" style="font-size: 15px;cursor: pointer;"
@click="popperClick(studyList, series)" /> @click.stop="popperClick(studyList, series)" />
</el-popover> </el-popover>
</div> </div>
</div> </div>
<div v-show="series.InstanceCount" style="padding: 1px;"> <div v-show="series.instanceCount" style="padding: 1px;">
{{ series.modality }}: {{ series.instanceCount }} image {{ series.modality }}: {{ series.instanceCount }} image
</div> </div>
<div v-show="series.sliceThickness" style="padding: 1px;"> <div v-show="series.sliceThickness" style="padding: 1px;">
@ -158,8 +160,7 @@
<div class="viewerSidethumbinner"> <div class="viewerSidethumbinner">
<el-collapse v-model="relationActiveName" @change="handelRelationActiveChange"> <el-collapse v-model="relationActiveName" @change="handelRelationActiveChange">
<template v-for="item in relationStudyListByVisitName"> <div v-for="item in relationStudyListByVisitName" :key="`${item.VisitName}`">
<div :key="`${item.VisitName}`">
<div v-show="item.VisitName" class="text-desc" style="background-color: #1f1f1f;"> <div v-show="item.VisitName" class="text-desc" style="background-color: #1f1f1f;">
{{ item.VisitName }} {{ item.VisitName }}
</div> </div>
@ -167,7 +168,7 @@
<el-collapse-item :key="`${study.StudyId}`" :name="`${study.StudyId}`" <el-collapse-item :key="`${study.StudyId}`" :name="`${study.StudyId}`"
v-if="study.VisitName === item.VisitName"> v-if="study.VisitName === item.VisitName">
<template slot="title"> <template slot="title">
<div class="collapse-title-wrapper">
<div class="text-desc"> <div class="text-desc">
{{ study.StudyCode }} {{ study.StudyCode }}
</div> </div>
@ -175,9 +176,10 @@
{{ study.Description }} {{ study.Description }}
</div> --> </div> -->
<div v-show="study.SeriesCount" class="text-desc"> <div v-show="study.SeriesCount" class="text-desc collapse-title-extra">
{{ study.Modalities }} : {{ study.SeriesCount }} Series {{ study.Modalities }} : {{ study.SeriesCount }} Series
</div> </div>
</div>
</template> </template>
<div v-show="study.Description" class="text-desc" style="background-color: #1f1f1f;"> <div v-show="study.Description" class="text-desc" style="background-color: #1f1f1f;">
{{ study.Description }} {{ study.Description }}
@ -208,6 +210,7 @@
<div class="frame_list"> <div class="frame_list">
<div v-for="(instance, idx) in seriesItem.instanceInfoList" <div v-for="(instance, idx) in seriesItem.instanceInfoList"
:key="instance.Id" class="frame_content" :key="instance.Id" class="frame_content"
:class="{ 'frame_content_active': activeInstanceId === instance.Id }"
:style="{ 'margin-bottom': idx < seriesItem.instanceInfoList.length - 1 ? '5px' : '0px' }" :style="{ 'margin-bottom': idx < seriesItem.instanceInfoList.length - 1 ? '5px' : '0px' }"
@click="showMultiFrames(studyIndex, seriesItem, index, instance)"> @click="showMultiFrames(studyIndex, seriesItem, index, instance)">
<div> <div>
@ -258,8 +261,6 @@
</template> </template>
</div> </div>
</template>
</el-collapse> </el-collapse>
</div> </div>
@ -330,7 +331,8 @@ export default {
isFromCRCUpload: false, isFromCRCUpload: false,
visitTaskId: null, visitTaskId: null,
page: '', page: '',
activeSeriesId: null activeSeriesId: null,
activeInstanceId: null
} }
}, },
mounted() { mounted() {
@ -349,20 +351,18 @@ export default {
this.isFromCRCUpload = !!this.$router.currentRoute.query.isFromCRCUpload this.isFromCRCUpload = !!this.$router.currentRoute.query.isFromCRCUpload
this.visitTaskId = this.$router.currentRoute.query.visitTaskId this.visitTaskId = this.$router.currentRoute.query.visitTaskId
this.page = this.$route.query.page this.page = this.$route.query.page
this.beforeUnloadHandler = () => {
cornerstone.imageCache.purgeCache()
requestPoolManager.resetRequestPool()
}
// cornerstone.events.addEventListener('cornerstoneimageloaded', this.cornerstoneImageLoaded) // cornerstone.events.addEventListener('cornerstoneimageloaded', this.cornerstoneImageLoaded)
this.getStudiesInfo() this.getStudiesInfo()
cornerstone.events.addEventListener('cornerstoneimageloadprogress', this.cornerstoneimageloadprogress) cornerstone.events.addEventListener('cornerstoneimageloadprogress', this.cornerstoneimageloadprogress)
window.addEventListener('beforeunload', e => { window.addEventListener('beforeunload', this.beforeUnloadHandler)
cornerstone.imageCache.purgeCache()
requestPoolManager.resetRequestPool()
})
}, },
beforeDestroy() { beforeDestroy() {
requestPoolManager.stopTaskTimer() requestPoolManager.stopTaskTimer()
window.removeEventListener('beforeunload', e => { window.removeEventListener('beforeunload', this.beforeUnloadHandler)
cornerstone.imageCache.purgeCache()
requestPoolManager.resetRequestPool()
})
workSpeedclose(true) workSpeedclose(true)
}, },
methods: { methods: {
@ -492,14 +492,17 @@ export default {
data.SeriesList = seriesList data.SeriesList = seriesList
this.studyList.push(data) this.studyList.push(data)
}) })
if (this.studyList.length > 0) { const firstStudy = this.studyList.find(study => Array.isArray(study.SeriesList) && study.SeriesList.length > 0)
this.$refs.dicomViewer.loadImageStack(this.studyList[0].SeriesList[0]) if (firstStudy) {
const imageId = this.studyList[0].SeriesList[0].imageIds[0] 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] let instanceId = imageId.split('/')[imageId.split('/').length - 1]
instanceId = instanceId.split('.')[0] instanceId = instanceId.split('.')[0]
this.firstInstanceId = instanceId this.firstInstanceId = instanceId
this.activeNames = [this.studyList[0].StudyId] this.activeNames = [firstStudy.StudyId]
this.loadImages(this.studyList[0].SeriesList[0], 0) this.loadImages(firstSeries, 0)
} }
} }
} catch (e) { } catch (e) {
@ -524,6 +527,7 @@ export default {
}, },
showSeriesImage(e, studyIndex, seriesIndex, series) { showSeriesImage(e, studyIndex, seriesIndex, series) {
this.activeSeriesId = series.seriesId this.activeSeriesId = series.seriesId
this.activeInstanceId = null
workSpeedclose(true) workSpeedclose(true)
const element = e.currentTarget const element = e.currentTarget
const elements = document.querySelectorAll('[series-type]') const elements = document.querySelectorAll('[series-type]')
@ -565,6 +569,7 @@ export default {
}, },
showMultiFrames(studyIndex, series, seriesIndex, instanceInfo) { showMultiFrames(studyIndex, series, seriesIndex, instanceInfo) {
this.currentSeriesIndex = seriesIndex this.currentSeriesIndex = seriesIndex
this.activeInstanceId = instanceInfo.Id
const imageIds = [] const imageIds = []
if (instanceInfo.NumberOfFrames && instanceInfo.NumberOfFrames > 1) { if (instanceInfo.NumberOfFrames && instanceInfo.NumberOfFrames > 1) {
for (let j = 0; j < instanceInfo.NumberOfFrames; j++) { for (let j = 0; j < instanceInfo.NumberOfFrames; j++) {
@ -576,6 +581,7 @@ export default {
const seriesInfo = { const seriesInfo = {
trialId: series.trialId, trialId: series.trialId,
subjectVisitId: series.subjectVisitId, subjectVisitId: series.subjectVisitId,
instanceInfoList: series.instanceInfoList,
studyId: series.studyId, studyId: series.studyId,
imageIds: imageIds, imageIds: imageIds,
seriesId: series.seriesId, seriesId: series.seriesId,
@ -601,7 +607,11 @@ export default {
} }
if (!isAddToTakPool) { if (!isAddToTakPool) {
var priority = parseInt(new Date().getTime()) 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 => { series.instanceInfoList.map(image => {
this.imageList.push({ imageId: image.ImageId, seriesId: series.seriesId, priority }) this.imageList.push({ imageId: image.ImageId, seriesId: series.seriesId, priority })
}) })
@ -883,6 +893,7 @@ export default {
}, },
showRelationSeriesImage(e, series, studyIndex, index) { showRelationSeriesImage(e, series, studyIndex, index) {
this.activeSeriesId = series.seriesId this.activeSeriesId = series.seriesId
this.activeInstanceId = null
workSpeedclose(true) workSpeedclose(true)
this.currentRelationIndex = index this.currentRelationIndex = index
const element = e.currentTarget const element = e.currentTarget
@ -940,7 +951,7 @@ export default {
if (this.imageList.length > 0) { if (this.imageList.length > 0) {
requestPoolManager.startTaskTimer() requestPoolManager.startTaskTimer()
this.imageList.map(image => { this.imageList.map(image => {
requestPoolManager.loadAndCacheImagePlus(image.imageId, image.seriesId, image.priority) requestPoolManager.loadAndCacheImagePlus(image.imageId, image.seriesId, image.priority).catch(() => {})
}) })
requestPoolManager.sortTaskPool() requestPoolManager.sortTaskPool()
this.imageList = [] this.imageList = []
@ -1012,7 +1023,7 @@ export default {
</script> </script>
<style lang="scss"> <style lang="scss">
.imageBox { .viewerContainer .imageBox {
position: relative; position: relative;
i { i {
@ -1102,6 +1113,7 @@ export default {
padding: 0; padding: 0;
margin-right: 2px; margin-right: 2px;
color: #D0D0D0; color: #D0D0D0;
overflow-x: hidden;
overflow-y: auto; overflow-y: auto;
} }
@ -1110,6 +1122,8 @@ export default {
word-break: break-all; word-break: break-all;
display: table; display: table;
width: 100%; width: 100%;
box-sizing: border-box;
overflow-x: hidden;
border: 1px solid #3e3f3a; border: 1px solid #3e3f3a;
} }
@ -1138,14 +1152,20 @@ export default {
.viewerContainer .viewerLeftSidePanel .viewernavigatorwrapper { .viewerContainer .viewerLeftSidePanel .viewernavigatorwrapper {
display: flex; display: flex;
width: 220px; width: 220px;
/* height: 84px; */ min-height: 85px;
padding: 1px 2px 1px 2px; padding: 1px 2px 1px 2px;
margin: 2px 0 1px 1px; margin: 2px 0 1px 1px;
align-items: flex-start;
/* border-radius: 2px; /* border-radius: 2px;
border: 1px solid #404040; */ border: 1px solid #404040; */
} }
.viewerContainer .viewerLeftSidePanel .viewernavigatorwrapper .imageBox {
align-self: center;
flex-shrink: 0;
}
.viewerContainer .viewernavigatorwrapper .el-progress__text { .viewerContainer .viewernavigatorwrapper .el-progress__text {
display: none; display: none;
} }
@ -1168,9 +1188,19 @@ export default {
/* width: 120px; /* width: 120px;
height: 80px; */ height: 80px; */
flex: 1; flex: 1;
min-width: 0;
padding: 3px 1px 3px 4px; padding: 3px 1px 3px 4px;
vertical-align: top; vertical-align: top;
font-size: 12px; 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 { .viewerContainer .viewerLeftSidePanel .viewerlabelwrapper {
@ -1200,12 +1230,24 @@ export default {
.viewerContainer .el-collapse { .viewerContainer .el-collapse {
border: none; border: none;
width: 100%;
min-width: 0;
box-sizing: border-box;
overflow-x: hidden;
} }
.viewerContainer .el-collapse-item { .viewerContainer .el-collapse-item {
background-color: #585453 !important; background-color: #585453 !important;
color: #ddd; 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 { .viewerContainer .el-collapse-item__content {
@ -1218,9 +1260,42 @@ export default {
background-color: #585453 !important; background-color: #585453 !important;
color: #ddd; color: #ddd;
border-bottom-color: #5a5a5a; border-bottom-color: #5a5a5a;
width: 100%;
min-width: 0;
box-sizing: border-box;
padding-left: 5px; padding-left: 5px;
height: 40px; height: auto;
min-height: 30px;
line-height: 20px; 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 { .instance_frame_wrapper {
@ -1263,6 +1338,10 @@ export default {
border-color: #213a54 !important; border-color: #213a54 !important;
background-color: #213a54; background-color: #213a54;
} }
.frame_content_active {
border-color: #213a54 !important;
background-color: #213a54;
}
/* .viewerRightSidePanel { /* .viewerRightSidePanel {
width: 300px; width: 300px;

View File

@ -22,6 +22,11 @@
</el-upload> </el-upload>
</div> </div>
</el-form-item> </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')" <el-form-item :label="$t('dictionary:signature:form:NeedConfirmedUserTypeIdList')"
prop="NeedConfirmedUserTypeIdList"> prop="NeedConfirmedUserTypeIdList">
<el-select v-model="form.NeedConfirmedUserTypeIdList" style="width: 100%" multiple> <el-select v-model="form.NeedConfirmedUserTypeIdList" style="width: 100%" multiple>
@ -83,6 +88,7 @@ export default {
DocUserSignType: 0, DocUserSignType: 0,
CurrentStaffTrainDays: 1, CurrentStaffTrainDays: 1,
NewStaffTrainDays: 14, NewStaffTrainDays: 14,
DocLanguageType: null
}, },
rules: { rules: {
FileTypeId: [ FileTypeId: [
@ -92,6 +98,13 @@ export default {
trigger: ['blur'], trigger: ['blur'],
}, },
], ],
DocLanguageType: [
{
required: true,
message: this.$t('common:ruleMessage:select'),
trigger: ['blur'],
},
],
SignViewMinimumMinutes: [ SignViewMinimumMinutes: [
{ {
required: true, required: true,
@ -143,6 +156,9 @@ export default {
this.form.DocUserSignType = this.data.DocUserSignType this.form.DocUserSignType = this.data.DocUserSignType
this.form.CurrentStaffTrainDays = this.data.CurrentStaffTrainDays this.form.CurrentStaffTrainDays = this.data.CurrentStaffTrainDays
this.form.NewStaffTrainDays = this.data.NewStaffTrainDays 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 this.loading = false
}, },

View File

@ -26,6 +26,11 @@
<el-form-item :label="$t('dictionary:signature:search:Name')"> <el-form-item :label="$t('dictionary:signature:search:Name')">
<el-input v-model="searchData.Name" style="width: 130px" clearable /> <el-input v-model="searchData.Name" style="width: 130px" clearable />
</el-form-item> </el-form-item>
<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-form-item :label="$t('dictionary:signature:table:NeedConfirmedUserTypes')">
<el-select v-model="searchData.UserTypeId" style="width: 150px" clearable> <el-select v-model="searchData.UserTypeId" style="width: 150px" clearable>
<el-option v-for="item of userTypeOptions" :key="item.Id" :label="item.UserTypeShortName" <el-option v-for="item of userTypeOptions" :key="item.Id" :label="item.UserTypeShortName"
@ -90,6 +95,12 @@
</el-button> </el-button>
</template> </template>
</el-table-column> </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')" <el-table-column prop="SignViewMinimumMinutes" :label="$t('dictionary:signature:table:SignViewMinimumMinutes')"
show-overflow-tooltip sortable="custom" min-width="200px" /> show-overflow-tooltip sortable="custom" min-width="200px" />
<el-table-column prop="CurrentStaffTrainDays" :label="$t('dictionary:signature:table:CurrentStaffTrainDays')" <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" <el-dialog v-if="editVisible" :visible.sync="editVisible" :close-on-click-modal="false" :title="title"
width="600px" custom-class="base-dialog-wrapper"> 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> </el-dialog>
<!--附件列表--> <!--附件列表-->
<attachmentList v-if="config.visible" :config="config" :rowData="currentRow" :SystemDocumentId="SystemDocumentId" <attachmentList v-if="config.visible" :config="config" :rowData="currentRow" :SystemDocumentId="SystemDocumentId"
@ -200,6 +211,7 @@ const searchDataDefault = () => {
IsDeleted: null, IsDeleted: null,
DocUserSignType: null, DocUserSignType: null,
UserTypeId: null, UserTypeId: null,
DocLanguageType: null,
Name: '', Name: '',
PageIndex: 1, PageIndex: 1,
PageSize: 20, PageSize: 20,

View File

@ -1,13 +1,6 @@
<template> <template>
<el-form <el-form ref="siteForm" v-loading="loading" :model="form" :rules="rules" class="demo-ruleForm" size="small"
ref="siteForm" label-width="150px">
v-loading="loading"
:model="form"
:rules="rules"
class="demo-ruleForm"
size="small"
label-width="150px"
>
<div class="base-dialog-body"> <div class="base-dialog-body">
<!-- Site Code --> <!-- Site Code -->
<el-form-item :label="$t('institutions:sites:label:siteCode')" v-if="form.Id" prop="SiteCode"> <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>
<!-- 中心名称 --> <!-- 中心名称 -->
<el-form-item <el-form-item :label="$t('institutions:sites:label:siteName')" prop="SiteName">
:label="$t('institutions:sites:label:siteName')"
prop="SiteName"
>
<el-input v-model="form.SiteName" /> <el-input v-model="form.SiteName" />
</el-form-item> </el-form-item>
<!-- 中心名称CN --> <!-- 中心名称CN -->
<el-form-item <el-form-item :label="$t('institutions:sites:label:siteNameCN')" prop="SiteNameCN">
:label="$t('institutions:sites:label:siteNameCN')"
prop="SiteNameCN"
>
<el-input v-model="form.SiteNameCN" /> <el-input v-model="form.SiteNameCN" />
</el-form-item> </el-form-item>
<!-- Alias Name --> <!-- Alias Name -->
@ -38,7 +25,9 @@
</el-form-item> --> </el-form-item> -->
<!-- Country --> <!-- Country -->
<el-form-item :label="$t('institutions:sites:label:country')" prop="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> </el-form-item>
<!-- City --> <!-- City -->
<el-form-item :label="$t('institutions:sites:label:city')" prop="City"> <el-form-item :label="$t('institutions:sites:label:city')" prop="City">
@ -50,17 +39,8 @@
</el-form-item> </el-form-item>
<!-- Affiliated Hospital --> <!-- Affiliated Hospital -->
<el-form-item :label="$t('institutions:sites:label:affiliatedHospital')"> <el-form-item :label="$t('institutions:sites:label:affiliatedHospital')">
<el-select <el-select v-model="form.HospitalId" clearable style="width: 100%">
v-model="form.HospitalId" <el-option v-for="item in hospitalList" :key="item.Id" :label="item.HospitalName" :value="item.Id" />
clearable
style="width: 100%"
>
<el-option
v-for="item in hospitalList"
:key="item.Id"
:label="item.HospitalName"
:value="item.Id"
/>
</el-select> </el-select>
</el-form-item> </el-form-item>
<!-- Director Name --> <!-- Director Name -->
@ -82,20 +62,10 @@
</div> </div>
<div class="base-dialog-footer" style="text-align: right; margin-top: 10px"> <div class="base-dialog-footer" style="text-align: right; margin-top: 10px">
<el-form-item> <el-form-item>
<el-button <el-button :disabled="btnLoading" size="small" type="primary" @click="handleCancel">{{
:disabled="btnLoading" $t('common:button:cancel') }}</el-button>
size="small" <el-button size="small" type="primary" :loading="btnLoading" @click="handleSave">{{ $t('common:button:save')
type="primary" }}</el-button>
@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> </el-form-item>
</div> </div>
</el-form> </el-form>

View File

@ -22,7 +22,7 @@
<el-input v-model="password.UserType" disabled /> <el-input v-model="password.UserType" disabled />
</el-form-item> --> </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-input v-model="password.NewUserName" :disabled="isUpdate" />
</el-form-item> </el-form-item>
<!-- 新密码 --> <!-- 新密码 -->
@ -77,7 +77,7 @@ export default {
callback( callback(
lang === 'zh' lang === 'zh'
? new Error( ? new Error(
'1新建账号用户名字符长度最小为4个字符最大为16个字符只可使用字母、数字、下划线' '新建账号用户名字符长度最小为4个字符最大为16个字符只可使用字母、数字、下划线'
) )
: new Error( : 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.'

View File

@ -4,122 +4,57 @@
<el-form :inline="true"> <el-form :inline="true">
<!--项目编号/实验名称--> <!--项目编号/实验名称-->
<el-form-item :label="$t('feedBack:search:trials')" v-if="level > 8"> <el-form-item :label="$t('feedBack:search:trials')" v-if="level > 8">
<el-input <el-input v-model="searchData.TrialKeyInfo" clearable style="width: 150px"></el-input>
v-model="searchData.TrialKeyInfo"
clearable
style="width: 150px"
></el-input>
</el-form-item> </el-form-item>
<!--中心--> <!--中心-->
<el-form-item :label="$t('feedBack:search:site')" v-if="level > 7"> <el-form-item :label="$t('feedBack:search:site')" v-if="level > 7">
<el-input <el-input v-model="searchData.TrialSiteCode" clearable style="width: 150px"></el-input>
v-model="searchData.TrialSiteCode"
clearable
style="width: 150px"
></el-input>
</el-form-item> </el-form-item>
<!--受试者访视--> <!--受试者访视-->
<el-form-item <el-form-item :label="$t('feedBack:search:subjectVisit')" v-if="level > 7">
:label="$t('feedBack:search:subjectVisit')" <el-input v-model="searchData.SubejctAndVisitKeyInfo" clearable style="width: 150px"></el-input>
v-if="level > 7"
>
<el-input
v-model="searchData.SubejctAndVisitKeyInfo"
clearable
style="width: 150px"
></el-input>
</el-form-item> </el-form-item>
<!--角色--> <!--角色-->
<el-form-item :label="$t('feedBack:search:role')" v-if="level > 7"> <el-form-item :label="$t('feedBack:search:role')" v-if="level > 7">
<el-select <el-select v-model="searchData.UserTypeEnum" clearable style="width: 150px"
v-model="searchData.UserTypeEnum" popper-class="feedBack-select-box">
clearable <el-option v-for="item of UserTypeOptins" :key="item.value" :label="item.label" :value="item.value" />
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-select>
</el-form-item> </el-form-item>
<!--反馈人--> <!--反馈人-->
<el-form-item :label="$t('feedBack:search:user')" v-if="level > 7"> <el-form-item :label="$t('feedBack:search:user')" v-if="level > 7">
<el-input <el-input v-model="searchData.FeedBackUserKeyInfo" clearable style="width: 150px"></el-input>
v-model="searchData.FeedBackUserKeyInfo"
clearable
style="width: 150px"
></el-input>
</el-form-item> </el-form-item>
<!--问题类型--> <!--问题类型-->
<el-form-item :label="$t('feedBack:search:questionType')"> <el-form-item :label="$t('feedBack:search:questionType')">
<el-select <el-select v-model="searchData.QuestionType" clearable style="width: 150px"
v-model="searchData.QuestionType" popper-class="feedBack-select-box">
clearable <el-option v-for="item of QuestionTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
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-select>
</el-form-item> </el-form-item>
<!--问题描述--> <!--问题描述-->
<el-form-item :label="$t('feedBack:search:description')"> <el-form-item :label="$t('feedBack:search:description')">
<el-input <el-input v-model="searchData.QuestionDescription" clearable style="width: 150px"></el-input>
v-model="searchData.QuestionDescription"
clearable
style="width: 150px"
></el-input>
</el-form-item> </el-form-item>
<!--状态--> <!--状态-->
<el-form-item :label="$t('feedBack:search:status')" v-if="level > 7"> <el-form-item :label="$t('feedBack:search:status')" v-if="level > 7">
<el-select <el-select v-model="searchData.State" clearable style="width: 150px" popper-class="feedBack-select-box">
v-model="searchData.State" <el-option v-for="item of $d.FeedBackStatus" :key="item.value" :label="item.label" :value="item.value" />
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-select>
</el-form-item> </el-form-item>
<!--反馈日期--> <!--反馈日期-->
<el-form-item :label="$t('feedBack:search:time')"> <el-form-item :label="$t('feedBack:search:time')">
<el-date-picker <el-date-picker v-model="datetimerange" type="datetimerange" :default-time="['00:00:00', '23:59:59']"
v-model="datetimerange" :start-placeholder="$t('feedBack:search:beginTime')" :end-placeholder="$t('feedBack:search:endTime')"
type="datetimerange" value-format="yyyy-MM-dd HH:mm:ss" @change="handleDatetimeChange" style="width: 250px"
:default-time="['00:00:00', '23:59:59']" popper-class="feedBack-select-box" />
: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-form-item> <el-form-item>
<el-button <el-button type="primary" icon="el-icon-search" @click="getList">
type="primary"
icon="el-icon-search"
@click="getList"
>
{{ $t('common:button:search') }} {{ $t('common:button:search') }}
</el-button> </el-button>
<!-- 重置 --> <!-- 重置 -->
<el-button <el-button icon="el-icon-refresh-left" @click="handleReset">
icon="el-icon-refresh-left"
@click="handleReset"
>
{{ $t('common:button:reset') }} {{ $t('common:button:reset') }}
</el-button> </el-button>
<!-- <el-button <!-- <el-button
@ -131,97 +66,32 @@
{{ $t("common:button:export") }} {{ $t("common:button:export") }}
</el-button> --> </el-button> -->
</el-form-item> </el-form-item>
<el-button <el-button type="primary" @click="resolve" style="float: right" v-if="level > 7"
type="primary" :disabled="tableSelectData.length <= 0">
@click="resolve"
style="float: right"
v-if="level > 7"
:disabled="tableSelectData.length <= 0"
>
{{ $t('feedBack:button:resolve') }} {{ $t('feedBack:button:resolve') }}
</el-button> </el-button>
</el-form> </el-form>
<el-table <el-table v-loading="loading" v-adaptive="{ bottomOffset: 45 }" height="100" :data="list" class="table"
v-loading="loading" @selection-change="handleSelectChange" @sort-change="handleSortByColumn"
v-adaptive="{ bottomOffset: 45 }" :default-sort="{ prop: 'CreateTime', order: 'descending' }">
height="100" <el-table-column type="selection" align="center" width="45" v-if="level > 7" :selectable="handleSelectTable" />
: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 type="index" width="50" />
<el-table-column <el-table-column :label="$t('feedBack:form:trialCode')" prop="TrialCode" min-width="100" show-overflow-tooltip
:label="$t('feedBack:form:trialCode')" sortable="custom" v-if="level > 8" />
prop="TrialCode" <el-table-column :label="$t('feedBack:form:study')" prop="ExperimentName" min-width="140" show-overflow-tooltip
min-width="100" sortable="custom" v-if="level > 8" />
show-overflow-tooltip <el-table-column :label="$t('feedBack:form:siteCode')" prop="TrialSiteCode" min-width="100"
sortable="custom" show-overflow-tooltip sortable="custom" v-if="level > 7" />
v-if="level > 8" <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 <el-table-column :label="$t('feedBack:form:subjectVisit')" prop="SubjectVisitName" min-width="120"
:label="$t('feedBack:form:study')" show-overflow-tooltip sortable="custom" v-if="level > 7" />
prop="ExperimentName" <el-table-column :label="$t('feedBack:form:role')" prop="FeedBackUserName" min-width="100" show-overflow-tooltip
min-width="140" sortable="custom" v-if="level > 7" />
show-overflow-tooltip <el-table-column :label="$t('feedBack:form:user')" prop="FeedBackFullName" min-width="140" show-overflow-tooltip
sortable="custom" sortable="custom" v-if="level > 7" />
v-if="level > 8" <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: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"> <template slot-scope="scope">
<span>{{ <span>{{
QuestionTypeOptions.filter( QuestionTypeOptions.filter(
@ -230,49 +100,22 @@
}}</span> }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column :label="$t('feedBack:form:description')" prop="QuestionDescription" min-width="200"
:label="$t('feedBack:form:description')" show-overflow-tooltip sortable="custom" />
prop="QuestionDescription" <el-table-column :label="$t('feedBack:form:time')" prop="CreateTime" min-width="180" show-overflow-tooltip
min-width="200" sortable="custom" />
show-overflow-tooltip <el-table-column :label="$t('feedBack:form:status')" prop="State" min-width="120" show-overflow-tooltip
sortable="custom" sortable="custom" v-if="level > 7">
/>
<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"> <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) $fd('FeedBackStatus', scope.row.State)
}}</el-tag> }}</el-tag>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column :label="$t('feedBack:form:uploadTime')" prop="UpdateTime" min-width="180" show-overflow-tooltip
:label="$t('feedBack:form:uploadTime')" sortable="custom" v-if="level > 7" />
prop="UpdateTime" <el-table-column :label="$t('common:action:action')" fixed="right" prop="UserTypeShortName" width="100"
min-width="180" show-overflow-tooltip>
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"> <template slot-scope="scope">
<el-button type="text" @click="getDetail(scope.row)"> <el-button type="text" @click="getDetail(scope.row)">
{{ $t('common:button:view') }} {{ $t('common:button:view') }}
@ -281,12 +124,8 @@
</el-table-column> </el-table-column>
</el-table> </el-table>
<div class="pagination" style="text-align: right; margin-top: 5px"> <div class="pagination" style="text-align: right; margin-top: 5px">
<pagination <pagination :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize"
:total="total" @pagination="getList" />
:page.sync="searchData.PageIndex"
:limit.sync="searchData.PageSize"
@pagination="getList"
/>
</div> </div>
</div> </div>
</div> </div>
@ -434,7 +273,7 @@ export default {
} }
}, },
// //
report() {}, report() { },
// //
handleSelectChange(selection) { handleSelectChange(selection) {
// console.log(selection, "handleSelectChange"); // console.log(selection, "handleSelectChange");
@ -472,31 +311,37 @@ export default {
display: flex; display: flex;
padding: 10px; padding: 10px;
border-radius: 5px; border-radius: 5px;
.left { .left {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: 0; width: 0;
flex-grow: 4; flex-grow: 4;
// border-right: 1px solid #ccc; // border-right: 1px solid #ccc;
.filter-container { .filter-container {
display: flex; display: flex;
align-items: center; align-items: center;
margin: 5px; margin: 5px;
} }
.data-table { .data-table {
flex: 1; flex: 1;
padding: 5px 0px; padding: 5px 0px;
} }
.pagination-container { .pagination-container {
text-align: right; text-align: right;
} }
} }
.right { .right {
width: 0; width: 0;
flex-grow: 6; flex-grow: 6;
overflow-y: auto; overflow-y: auto;
border-right: 1px solid #ccc; border-right: 1px solid #ccc;
} }
.selected-row { .selected-row {
background-color: cadetblue; background-color: cadetblue;
} }

View File

@ -152,7 +152,12 @@
<!-- {{ $t('login:title:system_title_about') }} --> <!-- {{ $t('login:title:system_title_about') }} -->
<svg-icon icon-class="login-logo" style="width: 250px; height: 71px" /> <svg-icon icon-class="login-logo" style="width: 250px; height: 71px" />
</p> </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"> <p style="margin-bottom: 20px; margin-top: 0">
V{{ $version.IsEnv_US ? $version.Version_US : $version.Version }} V{{ $version.IsEnv_US ? $version.Version_US : $version.Version }}
</p> </p>
@ -167,12 +172,12 @@
Copyright © {{ new Date().getFullYear() }} Shanghai Extensive Imaging Copyright © {{ new Date().getFullYear() }} Shanghai Extensive Imaging
Inc. Inc.
</p> </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="" /> <img style="width: 180px" src="@/assets/zzlogo-usa.png" alt="" />
</div> </div>
<div style="margin-bottom: 20px" v-else> <div style="margin-bottom: 20px" v-else>
<img style="width: 180px" src="@/assets/zzlogo2.png" alt="" /> <img style="width: 180px" src="@/assets/zzlogo2.png" alt="" />
</div> </div> -->
</div> </div>
<div slot="footer" class="dialog-footer"> <div slot="footer" class="dialog-footer">
<el-button type="primary" size="mini" @click="aboutVisible = false"> <el-button type="primary" size="mini" @click="aboutVisible = false">

View File

@ -278,7 +278,7 @@ export default {
padding: 5px 20px; padding: 5px 20px;
margin-top: 5px; margin-top: 5px;
background: #fff; background: #fff;
::v-deep.el-form-item { ::v-deep .el-form-item {
padding-bottom: 20px; padding-bottom: 20px;
} }
@ -292,10 +292,10 @@ export default {
} }
} }
.form-label-width{ .form-label-width{
::v-deep.el-form-item__label{ ::v-deep .el-form-item__label{
width: 140px; width: 140px;
} }
::v-deep.el-form-item__content{ ::v-deep .el-form-item__content{
margin-left: 140px; margin-left: 140px;
} }
} }
@ -304,7 +304,7 @@ export default {
text-align: center; text-align: center;
} }
::v-deep.el-form-item { ::v-deep .el-form-item {
margin-bottom: 0px; margin-bottom: 0px;
padding-top: 5px; padding-top: 5px;
border-bottom: 1px solid #f5f7fa; border-bottom: 1px solid #f5f7fa;

View File

@ -156,7 +156,7 @@ export default {
<style lang="scss" scoped> <style lang="scss" scoped>
.equipment_form_content{ .equipment_form_content{
padding: 0 10px; padding: 0 10px;
::v-deep.el-form-item { ::v-deep .el-form-item {
margin-bottom: 0px; margin-bottom: 0px;
padding: 5px 0 20px 0; padding: 5px 0 20px 0;
border-bottom: 1px solid #f5f7fa; border-bottom: 1px solid #f5f7fa;

View File

@ -295,7 +295,7 @@ export default {
<style lang="scss" scoped> <style lang="scss" scoped>
.participants_form_content{ .participants_form_content{
padding: 0 10px; padding: 0 10px;
::v-deep.el-form-item { ::v-deep .el-form-item {
margin-bottom: 0px; margin-bottom: 0px;
padding: 5px 0 20px 0; padding: 5px 0 20px 0;
border-bottom: 1px solid #f5f7fa; border-bottom: 1px solid #f5f7fa;

View File

@ -309,7 +309,7 @@ export default {
<style lang="scss" scoped> <style lang="scss" scoped>
.participants_form_content{ .participants_form_content{
padding: 0 10px; padding: 0 10px;
::v-deep.el-form-item { ::v-deep .el-form-item {
margin-bottom: 0px; margin-bottom: 0px;
padding: 5px 0 20px 0; padding: 5px 0 20px 0;
border-bottom: 1px solid #f5f7fa; border-bottom: 1px solid #f5f7fa;

View File

@ -381,7 +381,7 @@ export default {
padding: 5px 20px; padding: 5px 20px;
margin-top: 5px; margin-top: 5px;
background: #fff; background: #fff;
::v-deep.el-form-item { ::v-deep .el-form-item {
padding-bottom: 20px; padding-bottom: 20px;
} }
} }
@ -398,7 +398,7 @@ export default {
text-align: center; text-align: center;
} }
::v-deep.el-form-item { ::v-deep .el-form-item {
margin-bottom: 0px; margin-bottom: 0px;
padding-top: 5px; padding-top: 5px;
border-bottom: 1px solid #f5f7fa; border-bottom: 1px solid #f5f7fa;

View File

@ -49,9 +49,52 @@
<el-input-number v-model="form.AverageEngravingCycle" <el-input-number v-model="form.AverageEngravingCycle"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" controls-position="right" :min="0" /> :disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" controls-position="right" :min="0" />
</el-form-item> </el-form-item>
<!-- MRI-PDFF 是否为本中心该适应症的常规诊疗检查项目 -->
<el-form-item v-if="!notShowFieldList.includes('IsRoutineMRIPDEE')"
:label="$t('trials:researchForm:form:IsRoutineMRIPDEE')" prop="IsRoutineMRIPDEE">
<el-radio-group v-model="form.IsRoutineMRIPDEE" :disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory">
<el-radio v-for="item of $d.YesOrNo" :key="`IsRoutineMRIPDEE${item.value}`" :label="item.value">{{
item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<!-- MRI-PDFF 检查的检测周期含单次检查时长预约等待时长等 -->
<el-form-item
v-if="!notShowFieldList.includes('MRIPDFFScanTime') || !notShowFieldList.includes('MRIPDFFLeadTime') || !notShowFieldList.includes('MRIPDFFOther')"
:label="$t('trials:researchForm:form:IsMRIPDFF')">
</el-form-item>
<!-- 单次检查时长分钟 -->
<el-form-item v-if="!notShowFieldList.includes('MRIPDFFScanTime')"
:label="$t('trials:researchForm:form:MRIPDFFScanTime')">
<el-input-number v-model="form.MRIPDFFScanTime" :disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory"
controls-position="right" :min="0" />
</el-form-item>
<!-- 平均预约等待时长 -->
<el-form-item v-if="!notShowFieldList.includes('MRIPDFFLeadTime')"
:label="$t('trials:researchForm:form:MRIPDFFLeadTime')">
<el-input-number v-model="form.MRIPDFFLeadTime" :disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory"
controls-position="right" :min="0" />
</el-form-item>
<!-- 特殊情况备注-->
<el-form-item v-if="!notShowFieldList.includes('MRIPDFFOther')"
:label="$t('trials:researchForm:form:MRIPDFFOther')">
<el-input v-model="form.MRIPDFFOther" type="textarea" :autosize="{ minRows: 2, maxRows: 4 }"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" />
</el-form-item>
<!-- 如已选择研究者评估项目是否会授权影像科老师参与本试验如不单独授权是否可在试验中保持 1-2 名固定技师操作-->
<el-form-item
v-if="!notShowFieldList.includes('IsAuthorizeRadiologistsParticipate') || !notShowFieldList.includes('AssignFixedTechnologists')"
:label="$t('trials:researchForm:form:IsAuthorize')" prop="IsAuthorize">
<el-radio-group v-model="form.IsAuthorize" :disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory"
@input="handleIsAuthorizeInput">
<el-radio label="IsAuthorizeRadiologistsParticipate">{{
$t('trials:researchForm:form:IsAuthorizeRadiologistsParticipate') }}</el-radio>
<el-radio label="AssignFixedTechnologists">{{
$t('trials:researchForm:form:AssignFixedTechnologists') }}</el-radio>
</el-radio-group>
</el-form-item>
<!-- 请确认参与本项目影像采集的影像技师具备对应的资质技师证对应设备的大型设备上岗证 --> <!-- 请确认参与本项目影像采集的影像技师具备对应的资质技师证对应设备的大型设备上岗证 -->
<el-form-item v-if="!notShowFieldList.includes('IsConfirmImagingTechnologist')" <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" <el-radio-group v-model="form.IsConfirmImagingTechnologist"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory"> :disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory">
<el-radio v-for="item of $d.YesOrNo" :key="`IsConfirmImagingTechnologist${item.value}`" :label="item.value">{{ <el-radio v-for="item of $d.YesOrNo" :key="`IsConfirmImagingTechnologist${item.value}`" :label="item.value">{{
@ -60,7 +103,7 @@
</el-form-item> </el-form-item>
<!-- 原因 --> <!-- 原因 -->
<el-form-item v-if="!notShowFieldList.includes('NotConfirmReson') && form.IsConfirmImagingTechnologist === false" <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 }" <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" />
</el-form-item> </el-form-item>
@ -74,7 +117,7 @@
</el-radio-group> </el-radio-group>
</el-form-item> </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')" /> <span slot="label" v-html="$t('trials:researchForm:form:isFollowStudyParam')" />
<el-radio-group v-model="form.IsFollowStudyParameters" <el-radio-group v-model="form.IsFollowStudyParameters"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" style="margin-right: 10px;"> :disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" style="margin-right: 10px;">
@ -86,11 +129,28 @@
</el-button> </el-button>
</el-form-item> </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')" /> <span slot="label" v-html="$t('trials:researchForm:form:notFollowStudyParam')" />
<el-input v-model="form.NotFollowReson" type="textarea" :autosize="{ minRows: 2, maxRows: 4 }" <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" />
</el-form-item> </el-form-item>
<!-- 是否严格按照影像手册参数完成刻盘 -->
<el-form-item v-if="!notShowFieldList.includes('ISStrictManualBurnFlag')" prop="ISStrictManualBurnFlag">
<span slot="label" v-html="$t('trials:researchForm:form:ISStrictManualBurnFlag')" />
<el-radio-group v-model="form.ISStrictManualBurnFlag"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" style="margin-right: 10px;">
<el-radio v-for="item of $d.YesOrNo" :key="`ISStrictManualBurnFlag${item.value}`" :label="item.value">{{
item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<!-- 不能严格按照影像手册参数完成刻盘原因 -->
<el-form-item v-if="!notShowFieldList.includes('NotStrictManualBurnFlagReason') && !form.ISStrictManualBurnFlag"
prop="NotStrictManualBurnFlagReason">
<span slot="label" v-html="$t('trials:researchForm:form:NotStrictManualBurnFlagReason')" />
<el-input v-model="form.NotStrictManualBurnFlagReason" type="textarea" :autosize="{ minRows: 2, maxRows: 4 }"
:disabled="!(state === 0 && userTypeEnumInt === 0) || isHistory" />
</el-form-item>
<el-form-item> <el-form-item>
<!-- 保存 --> <!-- 保存 -->
<el-button v-if="state === 0 && userTypeEnumInt === 0 && !isHistory" type="primary" :loading="btnLoading" <el-button v-if="state === 0 && userTypeEnumInt === 0 && !isHistory" type="primary" :loading="btnLoading"
@ -152,6 +212,15 @@ export default {
Phone: '', // Phone: '', //
Email: '', // Email: '', //
AverageEngravingCycle: '', AverageEngravingCycle: '',
IsRoutineMRIPDEE: '',
MRIPDFFScanTime: '',
MRIPDFFLeadTime: '',
MRIPDFFOther: '',
IsAuthorize: '',
IsAuthorizeRadiologistsParticipate: '',
AssignFixedTechnologists: '',
ISStrictManualBurnFlag: '',
NotStrictManualBurnFlagReason: '',
IsConfirmImagingTechnologist: '', IsConfirmImagingTechnologist: '',
NotConfirmReson: '', NotConfirmReson: '',
EfficacyEvaluatorType: '', EfficacyEvaluatorType: '',
@ -162,6 +231,30 @@ export default {
TrialSiteId: [ TrialSiteId: [
{ required: true, message: this.$t('trials:researchForm:formRule:specify'), trigger: 'blur' } { 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: [ UserName: [
{ required: true, validator: (rule, value, callback) => { !value ? callback(new Error(this.$t('trials:researchForm:formRule:specify'))) : callback() }, trigger: 'blur' }, { required: true, validator: (rule, value, callback) => { !value ? callback(new Error(this.$t('trials:researchForm:formRule:specify'))) : callback() }, trigger: 'blur' },
{ min: 0, max: 50, message: this.$t('trials:researchForm:formRule:maxLength'), trigger: ['blur', 'change'] } { min: 0, max: 50, message: this.$t('trials:researchForm:formRule:maxLength'), trigger: ['blur', 'change'] }
@ -188,6 +281,11 @@ export default {
} }
}, },
methods: { methods: {
handleIsAuthorizeInput(label) {
this.form.IsAuthorizeRadiologistsParticipate = false
this.form.AssignFixedTechnologists = false
this.form[label] = true
},
async viewManual() { async viewManual() {
try { try {
let data = { let data = {
@ -231,6 +329,14 @@ export default {
phone: this.form.Phone, phone: this.form.Phone,
email: this.form.Email, email: this.form.Email,
averageEngravingCycle: this.form.AverageEngravingCycle, 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, isConfirmImagingTechnologist: this.form.IsConfirmImagingTechnologist,
notConfirmReson: this.form.NotConfirmReson, notConfirmReson: this.form.NotConfirmReson,
efficacyEvaluatorType: this.form.EfficacyEvaluatorType, efficacyEvaluatorType: this.form.EfficacyEvaluatorType,
@ -275,6 +381,16 @@ export default {
this.form.Phone = trialSiteSurvey.Phone // this.form.Phone = trialSiteSurvey.Phone //
this.form.Email = trialSiteSurvey.Email // this.form.Email = trialSiteSurvey.Email //
this.form.AverageEngravingCycle = trialSiteSurvey.AverageEngravingCycle 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.IsConfirmImagingTechnologist = trialSiteSurvey.IsConfirmImagingTechnologist
this.form.NotConfirmReson = trialSiteSurvey.NotConfirmReson this.form.NotConfirmReson = trialSiteSurvey.NotConfirmReson
this.form.EfficacyEvaluatorType = trialSiteSurvey.EfficacyEvaluatorType this.form.EfficacyEvaluatorType = trialSiteSurvey.EfficacyEvaluatorType

View File

@ -1,40 +1,98 @@
<template> <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"> <div class="base-dialog-body">
<!-- 扫描设备 --> <!-- 扫描设备 -->
<el-form-item :label="$t('trials:equiptResearch:form:equipment')" prop="EquipmentTypeId"> <el-form-item :label="$t('trials:equiptResearch:form:equipment')" prop="EquipmentTypeEnum"
<el-select v-if="EquipmentControlFieldList.includes('EquipmentTypeEnum')">
v-model="form.EquipmentTypeId" <div style="display: flex;align-items: center;">
style="width:100%" <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"
<!-- <el-option :value="item.value" />
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-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>
<!-- 扫描参数 --> <!-- 扫描参数 -->
<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-input v-model="form.Parameters" />
</el-form-item> </el-form-item>
<!-- 扫描仪器制造商名称 --> <!-- 扫描仪器制造商名称 -->
<el-form-item :label="$t('trials:equiptResearch:form:manufacturer')"> <el-form-item :label="$t('trials:equiptResearch:form:manufacturer')"
<el-input v-model="form.ManufacturerName" /> 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>
<!-- 扫描仪型号 --> <!-- 扫描仪型号 -->
<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-input v-model="form.ScannerType" />
</el-form-item> </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-input v-model="form.Note" />
</el-form-item> </el-form-item>
</div> </div>
@ -69,26 +127,94 @@ export default {
type: String, type: String,
default: '' default: ''
}, },
isShowParameters: { EquipmentControlFieldList: {
type: Boolean, type: Array,
default: false default: () => {
return []
}
} }
}, },
data() { data() {
return { return {
form: { form: {
Id: '', Id: '',
EquipmentTypeId: '', EquipmentTypeEnum: null,
Parameters: '', OtherEquipmentType: null,
ManufacturerName: '', Parameters: null,
ScannerType: '', Note: null,
Note: '', ManufacturerType: null,
ManufacturerName: null,
ScannerType: null,
MagneticFieldStrengthType: null,
BodyCoilChannelCount: null,
HasDedicatedPdfFatQuantificationSequence: null,
PdfFatQuantificationSequenceType: null,
OtherSequenceSpecification: null,
HasT2R2Correction: null,
CanFullyExportPdfParameterMapsAndRawDicom: null,
TrialSiteSurveyId: '' TrialSiteSurveyId: ''
}, },
rules: { 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'] } { 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, btnLoading: false,
dictionaryList: {} dictionaryList: {}

View File

@ -2,63 +2,88 @@
<div class="equipment-wrapper"> <div class="equipment-wrapper">
<div class="header-wrapper"> <div class="header-wrapper">
<!-- 新增 --> <!-- 新增 -->
<el-button <el-button v-if="state === 0 && userTypeEnumInt === 0 && !isHistory && EquipmentControlFieldList.length > 0"
v-if="state === 0 && userTypeEnumInt === 0 && !isHistory" size="small" type="primary" @click="handleAdd">
size="small"
type="primary"
@click="handleAdd"
>
{{ $t('common:button:add') }} {{ $t('common:button:add') }}
</el-button> </el-button>
</div> </div>
<el-table <el-table v-loading="loading" :data="list" border style="width: 100%">
v-loading="loading"
:data="list"
border
style="width: 100%"
>
<!-- 扫描设备 --> <!-- 扫描设备 -->
<el-table-column <el-table-column v-if="EquipmentControlFieldList.includes('EquipmentTypeEnum')" prop="EquipmentTypeEnum"
prop="EquipmentType" :label="$t('trials:equiptResearch:form:equipment')" min-width="120" show-overflow-tooltip>
:label="$t('trials:equiptResearch:form:equipment')" <template slot-scope="scope">
min-width="120" {{ scope.row.OtherEquipmentType ? scope.row.OtherEquipmentType :
show-overflow-tooltip $fd('SiteSurvey_ScanEquipmentType', scope.row.EquipmentTypeEnum) }}
/> </template>
</el-table-column>
<!-- 扫描参数 --> <!-- 扫描参数 -->
<el-table-column <el-table-column v-if="EquipmentControlFieldList.includes('Parameters')" prop="Parameters"
v-if="isShowParameters" :label="$t('trials:equiptResearch:form:param')" min-width="100" show-overflow-tooltip />
prop="Parameters"
:label="$t('trials:equiptResearch:form:param')"
min-width="100"
show-overflow-tooltip
/>
<!-- 扫描仪器制造商名称 --> <!-- 扫描仪器制造商名称 -->
<el-table-column <el-table-column v-if="EquipmentControlFieldList.includes('ManufacturerType')" min-width="120"
min-width="120" prop="ManufacturerType" :label="$t('trials:equiptResearch:form:manufacturer')" show-overflow-tooltip>
prop="ManufacturerName" <template slot-scope="scope">
:label="$t('trials:equiptResearch:form:manufacturer')" {{ scope.row.ManufacturerName ? scope.row.ManufacturerName : $fd('ManufacturerType',
show-overflow-tooltip scope.row.ManufacturerType) }}
/> </template>
</el-table-column>
<!-- 扫描仪型号 --> <!-- 扫描仪型号 -->
<el-table-column <el-table-column v-if="EquipmentControlFieldList.includes('ScannerType')" min-width="120" prop="ScannerType"
min-width="120" :label="$t('trials:equiptResearch:form:model')" show-overflow-tooltip />
prop="ScannerType" <!-- 磁场强度 -->
:label="$t('trials:equiptResearch:form:model')" <el-table-column v-if="EquipmentControlFieldList.includes('MagneticFieldStrengthType')" min-width="120"
show-overflow-tooltip 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 <el-table-column v-if="EquipmentControlFieldList.includes('Note')" min-width="120" prop="Note"
min-width="120" :label="$t('trials:equiptResearch:form:precautions')" show-overflow-tooltip />
prop="Note" <el-table-column v-if="state === 0 && userTypeEnumInt === 0 && !isHistory" fixed="right"
:label="$t('trials:equiptResearch:form:precautions')" :label="$t('common:action:action')" width="100">
show-overflow-tooltip
/>
<el-table-column
v-if="state === 0 && userTypeEnumInt === 0 && !isHistory"
fixed="right"
:label="$t('common:action:action')"
width="100"
>
<template slot-scope="scope"> <template slot-scope="scope">
<!-- 编辑 --> <!-- 编辑 -->
<el-button type="text" size="small" @click="handleEdit(scope.row)"> <el-button type="text" size="small" @click="handleEdit(scope.row)">
@ -73,16 +98,10 @@
</el-table> </el-table>
<!-- 新增/编辑人员信息 --> <!-- 新增/编辑人员信息 -->
<el-dialog <el-dialog v-if="editVisible" :visible.sync="editVisible" :close-on-click-modal="false" :title="title" width="800px"
v-if="editVisible" custom-class="base-dialog-wrapper" :append-to-body="userTypeEnumInt !== 0">
:visible.sync="editVisible" <EquipmentForm :data="rowData" :trial-site-survey-equipment-type="trialSiteSurveyEquipmentType"
:close-on-click-modal="false" :EquipmentControlFieldList="EquipmentControlFieldList" @getList="getList" @close="closeDialog" />
: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> </el-dialog>
</div> </div>
@ -115,7 +134,7 @@ export default {
state: null, state: null,
trialSiteSurveyId: '', trialSiteSurveyId: '',
trialId: '', trialId: '',
isShowParameters: false EquipmentControlFieldList: []
} }
}, },
mounted() { mounted() {
@ -160,10 +179,14 @@ export default {
this.$message.success(this.$t('trials:equiptResearch:message:delSuccessfully')) this.$message.success(this.$t('trials:equiptResearch:message:delSuccessfully'))
} }
}).catch(() => { this.loading = false }) }).catch(() => { this.loading = false })
}).catch(() => {}) }).catch(() => { })
}, },
initList(TrialSiteEquipmentSurveyList, trialSiteSurvey, isShowParameters) { initList(TrialSiteEquipmentSurveyList, trialSiteSurvey, EquipmentControlFieldList) {
this.isShowParameters = isShowParameters this.EquipmentControlFieldList = []
EquipmentControlFieldList.forEach(item => {
this.EquipmentControlFieldList.push(item.FiledName)
})
console.log(this.EquipmentControlFieldList, 'this.EquipmentControlFieldList')
this.list = TrialSiteEquipmentSurveyList this.list = TrialSiteEquipmentSurveyList
this.state = trialSiteSurvey.State this.state = trialSiteSurvey.State
this.$forceUpdate() this.$forceUpdate()
@ -176,8 +199,8 @@ export default {
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.equipment-wrapper{ .equipment-wrapper {
.header-wrapper{ .header-wrapper {
text-align: right; text-align: right;
margin-bottom: 10px; margin-bottom: 10px;
} }

View File

@ -175,7 +175,7 @@ export default {
this.$refs['historicalParticipant'].initList(historicalArr, res.Result.TrialSiteSurvey) this.$refs['historicalParticipant'].initList(historicalArr, res.Result.TrialSiteSurvey)
this.$refs['researchParticipants'].initList(newArr, res.Result.TrialSiteSurvey) this.$refs['researchParticipants'].initList(newArr, res.Result.TrialSiteSurvey)
// this.$refs['researchParticipants'].initList(res.Result.TrialSiteUserSurveyList, 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.isExistIncorrect = res.Result.TrialSiteUserSurveyList.every(item => item.IsCorrect === false)
} }
this.loading = false this.loading = false

View File

@ -209,7 +209,7 @@ export default {
this.$refs['historicalParticipant'].initList(historicalArr, res.Result.TrialSiteSurvey) this.$refs['historicalParticipant'].initList(historicalArr, res.Result.TrialSiteSurvey)
this.$refs['researchParticipants'].initList(newArr, 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.isExistIncorrect = res.Result.TrialSiteUserSurveyList.every(item => item.IsCorrect === false)
} }
this.loading = false this.loading = false
@ -357,7 +357,7 @@ export default {
overflow-y: auto; overflow-y: auto;
} }
::v-deep.el-card__body { ::v-deep .el-card__body {
padding: 10px; padding: 10px;
} }
@ -365,13 +365,13 @@ export default {
// padding: 10px 20px 20px 20px; // padding: 10px 20px 20px 20px;
// } // }
::v-deep.full-dialog-container { ::v-deep .full-dialog-container {
.el-dialog__body { .el-dialog__body {
height: calc(100% - 80px); height: calc(100% - 80px);
} }
} }
::v-deep.dialog-container { ::v-deep .dialog-container {
// margin-top: 50px !important; // margin-top: 50px !important;
width: 75%; width: 75%;
height: 80%; height: 80%;

View File

@ -4,7 +4,7 @@
<h2 style="text-align:center;"> <h2 style="text-align:center;">
<!-- 中心调研表 --> <!-- 中心调研表 -->
{{ $t('trials:researchForm:title:question') }} {{ $t('trials:researchForm:title:question') }}
<!-- <TopLang style="position: fixed;top: 40px;right: 40px" /> --> <TopLang style="position: fixed;top: 40px;right: 40px" />
</h2> </h2>
<el-card shadow="hover"> <el-card shadow="hover">
<el-form ref="resetForm" v-loading="loading" :model="form" label-width="150px" style="width:80%;margin:0 auto;" <el-form ref="resetForm" v-loading="loading" :model="form" label-width="150px" style="width:80%;margin:0 auto;"

View File

@ -26,7 +26,7 @@
<script> <script>
import { mapGetters, mapMutations } from 'vuex' import { mapGetters, mapMutations } from 'vuex'
import { changeURLStatic } from '@/utils/history.js'
export default { export default {
name: 'TopLang', name: 'TopLang',
@ -44,7 +44,8 @@ export default {
this.$i18n.locale = lang this.$i18n.locale = lang
this.setLanguage(lang) this.setLanguage(lang)
this.$updateDictionary() this.$updateDictionary()
// window.location.reload() changeURLStatic('lang', lang)
window.location.reload()
} }
} }
} }

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>

View File

@ -1,42 +1,21 @@
<template> <template>
<el-form <el-form ref="userForm" size="small" :model="user" :rules="userFormRules" label-width="150px" style="width: 800px">
ref="userForm"
size="small"
:model="user"
:rules="userFormRules"
label-width="150px"
style="width: 800px"
>
<el-card class="Basic" shadow="never" size="small"> <el-card class="Basic" shadow="never" size="small">
<div slot="header" class="clearfix"> <div slot="header" class="clearfix">
<span>{{ $t('system:userlist:title:Information') }}</span> <span>{{ $t('system:userlist:title:Information') }}</span>
</div> </div>
<el-form-item <el-form-item v-if="user.UserCode" :label="$t('system:userlist:table:S/N')" prop="UserCode">
v-if="user.UserCode"
:label="$t('system:userlist:table:S/N')"
prop="UserCode"
>
<el-input v-model="user.UserCode" disabled /> <el-input v-model="user.UserCode" disabled />
</el-form-item> </el-form-item>
<el-form-item <el-form-item :label="$t('system:userlist:table:UserName')" class="my_new_pwd" prop="UserName">
:label="$t('system:userlist:table:UserName')"
class="my_new_pwd"
prop="UserName"
>
<el-input v-model="user.UserName" /> <el-input v-model="user.UserName" />
</el-form-item> </el-form-item>
<el-form-item <el-form-item :label="$t('system:userlist:table:LastName')" prop="LastName">
:label="$t('system:userlist:table:LastName')"
prop="LastName"
>
<el-input v-model="user.LastName" /> <el-input v-model="user.LastName" />
</el-form-item> </el-form-item>
<el-form-item <el-form-item :label="$t('system:userlist:table:FirstName')" prop="FirstName">
:label="$t('system:userlist:table:FirstName')"
prop="FirstName"
>
<el-input v-model="user.FirstName" /> <el-input v-model="user.FirstName" />
</el-form-item> </el-form-item>
<!-- <el-form-item :label="$t('system:userlist:table:Gender')" prop="Sex" style="margin-right:40px;"> <!-- <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-form-item :label="$t('system:userlist:table:Phone')" prop="Phone">
<el-input v-model="user.Phone" /> <el-input v-model="user.Phone" />
</el-form-item> </el-form-item>
<el-form-item <el-form-item v-if="type == 1" :label="$t('system:userlist:table:Disable')">
v-if="type == 1" <el-switch v-model="user.Status" :active-value="1" :inactive-value="0" />
:label="$t('system:userlist:table:Disable')"
>
<el-switch
v-model="user.Status"
:active-value="1"
:inactive-value="0"
/>
</el-form-item> </el-form-item>
<el-form-item :label="$t('system:userlist:table:IsTestUser')"> <el-form-item :label="$t('system:userlist:table:IsTestUser')">
<el-radio-group v-model="user.IsTestUser"> <el-radio-group v-model="user.IsTestUser">
<el-radio <el-radio v-for="item of $d.YesOrNo" :key="item.id" :label="item.value">{{ item.label }}</el-radio>
v-for="item of $d.YesOrNo"
:key="item.id"
:label="item.value"
>{{ item.label }}</el-radio
>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item <el-form-item :label="$t('system:userlist:table:UserWorkLanguage')" prop="UserWorkLanguage">
:label="$t('system:userlist:table:UserType')" <el-select v-model="user.UserWorkLanguage" style="width: 100%">
prop="Roles" <el-option v-for="item of $d.UserWorkLanguage" :key="item.id" :label="item.label" :value="item.value" />
v-if="type === 0" </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"> <div style="display: flex; align-items: center">
<el-select <el-select ref="userType" v-model="user.Roles" size="small" placeholder="Please select" multiple
ref="userType" style="width: 100%" :disabled="user.CanEditUserType === false || type === 1" @change="handleChange">
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"> <template v-for="userType of userTypeOptions">
<el-option <el-option v-if="![4, 6, 20].includes(userType.UserTypeEnum)" :key="userType.Id"
v-if="![4, 6, 20].includes(userType.UserTypeEnum)" :label="userType.UserType" :value="userType.Id" />
:key="userType.Id"
:label="userType.UserType"
:value="userType.Id"
/>
</template> </template>
</el-select> </el-select>
</div> </div>
</el-form-item> </el-form-item>
<el-form-item <el-form-item :label="$t('system:userlist:table:UserType')" prop="userTypeId" v-else>
:label="$t('system:userlist:table:UserType')"
prop="userTypeId"
v-else
>
<div style="display: flex; align-items: center"> <div style="display: flex; align-items: center">
<el-select <el-select ref="userType" v-model="Roles" size="small" placeholder="Please select" multiple
ref="userType" style="width: 100%" :disabled="user.CanEditUserType === false || type === 1" @change="handleChange">
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"> <template v-for="userType of userTypeOptions">
<el-option <el-option v-if="![4, 6, 20].includes(userType.UserTypeEnum)" :key="userType.Id"
v-if="![4, 6, 20].includes(userType.UserTypeEnum)" :label="userType.UserType" :value="userType.Id" />
:key="userType.Id"
:label="userType.UserType"
:value="userType.Id"
/>
</template> </template>
</el-select> </el-select>
<el-button <el-button type="primary" size="small" style="margin-left: 5px" v-if="type === 1" @click.stop="openRoleList">
type="primary"
size="small"
style="margin-left: 5px"
v-if="type === 1"
@click.stop="openRoleList"
>
{{ $t('system:userlist:button:roles') }} {{ $t('system:userlist:button:roles') }}
</el-button> </el-button>
</div> </div>
</el-form-item> </el-form-item>
</el-card> </el-card>
<el-card <el-card class="Affiliation" shadow="never" style="margin-top: 10px" size="small">
class="Affiliation"
shadow="never"
style="margin-top: 10px"
size="small"
>
<div slot="header" class="clearfix"> <div slot="header" class="clearfix">
<span>{{ $t('system:userlist:title:Affiliation') }}</span> <span>{{ $t('system:userlist:title:Affiliation') }}</span>
</div> </div>
<el-form-item prop="IsZhiZhun"> <el-form-item prop="IsZhiZhun">
<el-radio-group <el-radio-group v-model="user.IsZhiZhun" @change="OrgnizationTypeChanged">
v-model="user.IsZhiZhun"
@change="OrgnizationTypeChanged"
>
<el-radio :label="true">{{ <el-radio :label="true">{{
$t('system:userlist:title:Internal') $t('system:userlist:title:Internal')
}}</el-radio> }}</el-radio>
@ -158,43 +84,22 @@
}}</el-radio> }}</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<el-form-item <el-form-item v-show="user.IsZhiZhun === false" :label="$t('system:userlist:table:OrganizationName')">
v-show="user.IsZhiZhun === false"
:label="$t('system:userlist:table:OrganizationName')"
>
<el-input v-model="user.OrganizationName" /> <el-input v-model="user.OrganizationName" />
</el-form-item> </el-form-item>
<el-form-item <el-form-item :label="$t('system:userlist:table:Department')" prop="DepartmentName">
:label="$t('system:userlist:table:Department')"
prop="DepartmentName"
>
<el-input v-model="user.DepartmentName" /> <el-input v-model="user.DepartmentName" />
</el-form-item> </el-form-item>
<el-form-item <el-form-item :label="$t('system:userlist:table:Position')" prop="PositionName">
:label="$t('system:userlist:table:Position')"
prop="PositionName"
>
<el-input v-model="user.PositionName" /> <el-input v-model="user.PositionName" />
</el-form-item> </el-form-item>
</el-card> </el-card>
<el-form-item> <el-form-item>
<el-button <el-button type="primary" size="small" :disabled="isDisabled" style="margin: 10px 15px" @click="handleSave">{{
type="primary" $t('common:button:save') }}</el-button>
size="small"
:disabled="isDisabled"
style="margin: 10px 15px"
@click="handleSave"
>{{ $t('common:button:save') }}</el-button
>
</el-form-item> </el-form-item>
<roleList <roleList v-if="visible" :visible.sync="visible" :userId="userId" :list="userRoleList"
v-if="visible" :userTypeOptions="userTypeOptions" @getRoleList="getRoleList" />
:visible.sync="visible"
:userId="userId"
:list="userRoleList"
:userTypeOptions="userTypeOptions"
@getRoleList="getRoleList"
/>
</el-form> </el-form>
</template> </template>
<script> <script>
@ -365,7 +270,7 @@ export default {
callback( callback(
lang === 'zh' lang === 'zh'
? new Error( ? new Error(
'1新建账号用户名字符长度最小为4个字符最大为16个字符只可使用字母、数字、下划线' '新建账号用户名字符长度最小为4个字符最大为16个字符只可使用字母、数字、下划线'
) )
: new Error( : 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.'
@ -379,6 +284,7 @@ export default {
userRoleList: [], userRoleList: [],
Roles: [], Roles: [],
user: { user: {
UserWorkLanguage: null,
Roles: [], Roles: [],
UserRoleList: [], UserRoleList: [],
UserName: '', UserName: '',
@ -399,6 +305,13 @@ export default {
UserName: [ UserName: [
{ required: true, validator: validatePassword, trigger: 'blur' }, { required: true, validator: validatePassword, trigger: 'blur' },
], ],
UserWorkLanguage: [
{
required: true,
message: 'Please Select',
trigger: ['blur', 'change'],
},
],
Roles: [ Roles: [
{ {
required: true, required: true,

View File

@ -1,55 +1,30 @@
<template> <template>
<box-content> <box-content>
<div class="search" style="position: relative"> <div class="search" style="position: relative">
<SearchForm <SearchForm :that="this" :search-data="searchData" :search-form="searchForm" :search-handle="searchHandle"
:that="this" @search="handleSearch" @reset="handleReset" @new="handleAddUser" />
:search-data="searchData"
:search-form="searchForm"
:search-handle="searchHandle"
@search="handleSearch"
@reset="handleReset"
@new="handleAddUser"
/>
</div> </div>
<base-table <base-table v-loading="loading" :columns="columns" :list="users" :search-data="searchData" :total="total"
v-loading="loading" @getList="getList" @editCb="handleEditUser" @sendCb="addNewUserSendEmail" @deleteCb="handleDeleteUser"
:columns="columns" @sortByColumn="sortByColumn">
:list="users"
:search-data="searchData"
:total="total"
@getList="getList"
@editCb="handleEditUser"
@sendCb="addNewUserSendEmail"
@deleteCb="handleDeleteUser"
@sortByColumn="sortByColumn"
>
<!-- 选择自定义slot --> <!-- 选择自定义slot -->
<template slot="tip-slot" slot-scope="{ scope }"> <template slot="tip-slot" slot-scope="{ scope }">
<i <i v-if="
v-if="
diffTime(scope.row.LastLoginTime) >= 90 && diffTime(scope.row.LastLoginTime) >= 90 &&
diffTime(scope.row.LastChangePassWordTime) >= 90 diffTime(scope.row.LastChangePassWordTime) >= 90
" " class="el-icon-warning" style="color: #f56c6c"
class="el-icon-warning" :title="$t('system:userlist:tip:overTimeAndoverTimePassWord')" />
style="color: #f56c6c" <i v-else-if="diffTime(scope.row.LastLoginTime) >= 90" class="el-icon-warning" style="color: #f56c6c"
:title="$t('system:userlist:tip:overTimeAndoverTimePassWord')" :title="$t('system:userlist:tip:overTime')" />
/> <i v-else-if="diffTime(scope.row.LastChangePassWordTime) >= 90" class="el-icon-warning" style="color: #f56c6c"
<i :title="$t('system:userlist:tip:overTimePassWord')" />
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>
<template slot="genderSlot" slot-scope="{ scope }"> <template slot="genderSlot" slot-scope="{ scope }">
{{ scope.row.Sex ? 'Male' : 'Female' }} {{ scope.row.Sex ? 'Male' : 'Female' }}
</template> </template>
<template slot="UserWorkLanguageSlot" slot-scope="{ scope }">
{{ $fd('UserWorkLanguage', scope.row.UserWorkLanguage) }}
</template>
<template slot="UserTypeSlot" slot-scope="{ scope }"> <template slot="UserTypeSlot" slot-scope="{ scope }">
{{ {{
Array.isArray(scope.row.UserRoleList) && Array.isArray(scope.row.UserRoleList) &&
@ -61,7 +36,7 @@
}} }}
</template> </template>
<template slot="roleSlot" slot-scope="{ scope }"> <template slot="roleSlot" slot-scope="{ scope }">
{{ scope.row.RoleNameList.map((role) => role.RoleName).join(', ') }} {{scope.row.RoleNameList.map((role) => role.RoleName).join(', ')}}
</template> </template>
<template slot="isZhiZhunSlot" slot-scope="{ scope }"> <template slot="isZhiZhunSlot" slot-scope="{ scope }">
{{ {{
@ -155,6 +130,14 @@ export default {
sortable: 'custom', sortable: 'custom',
showOverflowTooltip: true, showOverflowTooltip: true,
}, },
{
// prop: 'UserWorkLanguage',
label: this.$t('system:userlist:table:UserWorkLanguage'),
minWidth: 120,
slot: 'UserWorkLanguageSlot',
sortable: 'custom',
showOverflowTooltip: true,
},
// { // {
// prop: 'Sex', // prop: 'Sex',
// label: this.$t('system:userlist:table:Gender'), // label: this.$t('system:userlist:table:Gender'),
@ -287,6 +270,15 @@ export default {
width: '120px', width: '120px',
placeholder: '', placeholder: '',
}, },
{
type: 'Select',
label: this.$t('system:userlist:label:UserWorkLanguage'),
prop: 'UserWorkLanguage',
width: '100px',
options: [], //
props: { label: 'label', value: 'value' }, //
placeholder: '',
},
{ {
type: 'Input', type: 'Input',
label: this.$t('system:userlist:label:Phone'), label: this.$t('system:userlist:label:Phone'),
@ -476,8 +468,10 @@ export default {
// //
async getInfo() { async getInfo() {
const res = await getUserTypeList() const res = await getUserTypeList()
const index = this.findItemIndex('UserType') let index = this.findItemIndex('UserType')
this.$set(this.searchForm[index], 'options', res.Result) this.$set(this.searchForm[index], 'options', res.Result)
index = this.findItemIndex('UserWorkLanguage')
this.$set(this.searchForm[index], 'options', this.$d.UserWorkLanguage)
}, },
handleAddUser() { handleAddUser() {
this.$router.push({ path: '/system/user/add' }) this.$router.push({ path: '/system/user/add' })

View File

@ -1,25 +1,145 @@
<template> <template>
<div> <div>
<!-- <label>行数: </label> <el-input v-model="input" placeholder="" clearable></el-input>
<input v-model.number="rows" type="number" min="1" max="3" /> <el-input v-model="pageIndex" placeholder="" clearable></el-input>
<label>列数: </label> <el-input v-model="pageSize" placeholder="" clearable></el-input>
<input v-model.number="cols" type="number" min="1" max="3" /> --> <el-button type="primary" size="small" :disabled="!input" @click.stop="getTrialUnreadVisitList">下载</el-button>
<GridLayout :rows="rows" :cols="cols" />
</div> </div>
</template> </template>
<script> <script>
import GridLayout from './GridLayout.vue'; import { downLoadFile } from '@/utils/stream.js'
import { getTrialUnreadVisitList, getExportSubjectVisitImageList } from '@/api/trials'
export default { export default {
components: { // components: {
GridLayout, // GridLayout,
}, // },
data() { data() {
return { return {
rows: 2, input: '08deab4a-f842-a7a4-0242-0a0001000000',
cols: 2, 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> </script>

View File

@ -44,7 +44,7 @@ export default {
overflow: hidden; overflow: hidden;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
::v-deep.el-card__body { ::v-deep .el-card__body {
padding: 0px; padding: 0px;
} }
.trials-tab{ .trials-tab{

View File

@ -28,8 +28,10 @@ export default {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@import "~@/styles/mixin.scss"; // @import "~@/styles/mixin.scss";
@import "~@/styles/variables.module.scss"; // @import "~@/styles/variables.module.scss";
@use "@/styles/mixin.scss" as *;
@use "@/styles/variables.module.scss" as *;
.trials-wrapper { .trials-wrapper {
@include clearfix; @include clearfix;

View File

@ -17,11 +17,17 @@
</el-form-item> </el-form-item>
<!-- Sponsor --> <!-- Sponsor -->
<el-form-item :label="$t('trials:trials-list:table:sponsor')"> <el-form-item :label="$t('trials:trials-list:table:sponsor')" v-if="IsZhiZhun">
<el-select v-model="searchData.SponsorId" style="width: 150px" clearable> <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-option v-for="item in sponsorList" :key="item.Id" :label="item.SponsorName" :value="item.Id" />
</el-select> </el-select>
</el-form-item> </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-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> <el-select v-model="searchData.CriterionType" style="width: 150px" clearable>
@ -347,6 +353,7 @@ import {
import { getTrialList_Export } from '@/api/export' import { getTrialList_Export } from '@/api/export'
import store from '@/store' import store from '@/store'
import { mapGetters } from 'vuex' import { mapGetters } from 'vuex'
import { getUser } from '@/api/admin'
import BaseContainer from '@/components/BaseContainer' import BaseContainer from '@/components/BaseContainer'
import Pagination from '@/components/Pagination' import Pagination from '@/components/Pagination'
import TrialForm from './components/TrialForm' import TrialForm from './components/TrialForm'
@ -372,6 +379,7 @@ const searchDataDefault = () => {
PageSize: 20, PageSize: 20,
Asc: false, Asc: false,
SortField: '', SortField: '',
TrialStatusStr: null,
CriterionType: null, CriterionType: null,
PM_EMail: null, PM_EMail: null,
} }
@ -410,6 +418,7 @@ export default {
{ value: 'III' }, { value: 'III' },
{ value: 'IV' }, { value: 'IV' },
], ],
IsZhiZhun: false,
expeditedOption: this.$d.TrialExpeditedState, expeditedOption: this.$d.TrialExpeditedState,
beginPickerOption: { beginPickerOption: {
disabledDate: (time) => { disabledDate: (time) => {
@ -439,6 +448,7 @@ export default {
}, },
created() { created() {
this.initPage() this.initPage()
this.getUser()
}, },
mounted() { mounted() {
this.$EventBus.$on('reload', (data) => { this.$EventBus.$on('reload', (data) => {
@ -446,6 +456,11 @@ export default {
}) })
}, },
methods: { methods: {
getUser() {
getUser().then(res => {
this.IsZhiZhun = res.Result.IsZhiZhun
})
},
initPage() { initPage() {
this.getList() this.getList()
store.dispatch('global/getSponsorList') store.dispatch('global/getSponsorList')

View File

@ -5,77 +5,42 @@
<!-- 用户基本信息 --> <!-- 用户基本信息 -->
{{ $t("trials:trials-myinfo:title:basicInfo") }} {{ $t("trials:trials-myinfo:title:basicInfo") }}
</div> </div>
<el-form <el-form ref="userForm" label-position="right" :model="user" :rules="userFormRules" label-width="120px">
ref="userForm"
label-position="right"
:model="user"
:rules="userFormRules"
label-width="120px"
>
<el-form-item v-if="user.Code" label="ID: " prop="Code"> <el-form-item v-if="user.Code" label="ID: " prop="Code">
<el-input v-model="user.Code" disabled /> <el-input v-model="user.Code" disabled />
</el-form-item> </el-form-item>
<!-- --> <!-- -->
<el-form-item <el-form-item :disabled="user.UserTypeEnum === 8" :label="$t('trials:trials-myinfo:form:surname')"
:disabled="user.UserTypeEnum === 8" prop="LastName">
:label="$t('trials:trials-myinfo:form:surname')" <el-input v-model="user.LastName" :placeholder="$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>
<!-- --> <!-- -->
<el-form-item <el-form-item :disabled="user.UserTypeEnum === 8" :label="$t('trials:trials-myinfo:form:givenname')"
:disabled="user.UserTypeEnum === 8" prop="FirstName">
:label="$t('trials:trials-myinfo:form:givenname')" <el-input v-model="user.FirstName" :placeholder="$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>
<!-- 单位 --> <!-- 单位 -->
<el-form-item <el-form-item :label="$t('trials:trials-myinfo:form:organization')" prop="OrganizationName">
:label="$t('trials:trials-myinfo:form:organization')" <el-input :disabled="user.IsZhiZhun" v-model="user.OrganizationName"
prop="OrganizationName" :placeholder="$t('trials:trials-myinfo:form:organization')" />
>
<el-input
:disabled="user.IsZhiZhun"
v-model="user.OrganizationName"
:placeholder="$t('trials:trials-myinfo:form:organization')"
/>
</el-form-item> </el-form-item>
<!-- 部门 --> <!-- 部门 -->
<el-form-item <el-form-item :label="$t('trials:trials-myinfo:form:department')" prop="DepartmentName">
:label="$t('trials:trials-myinfo:form:department')" <el-input v-model="user.DepartmentName" :placeholder="$t('trials:trials-myinfo:form:organization')" />
prop="DepartmentName"
>
<el-input
v-model="user.DepartmentName"
:placeholder="$t('trials:trials-myinfo:form:organization')"
/>
</el-form-item> </el-form-item>
<!-- 职位 --> <!-- 职位 -->
<el-form-item <el-form-item :label="$t('trials:trials-myinfo:form:position')" prop="PositionName">
:label="$t('trials:trials-myinfo:form:position')" <el-input v-model="user.PositionName" :placeholder="$t('trials:trials-myinfo:form:position')" />
prop="PositionName" </el-form-item>
> <!-- 工作语言 -->
<el-input <el-form-item :label="$t('trials:trials-myinfo:form:UserWorkLanguage')" prop="UserWorkLanguage">
v-model="user.PositionName" <el-select v-model="user.UserWorkLanguage" style="width: 100%">
:placeholder="$t('trials:trials-myinfo:form:position')" <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>
</el-form> </el-form>
<!-- 保存 --> <!-- 保存 -->
<el-button <el-button class="trial-info-btn" type="primary" size="small" @click="handleSave">
class="trial-info-btn"
type="primary"
size="small"
@click="handleSave"
>
{{ $t("trials:trials-myinfo:button:save") }} {{ $t("trials:trials-myinfo:button:save") }}
</el-button> </el-button>
</div> </div>
@ -102,6 +67,13 @@ export default {
data() { data() {
return { return {
userFormRules: { userFormRules: {
UserWorkLanguage: [
{
required: true,
message: this.$t("common:ruleMessage:select"),
trigger: ["blur", "change"],
},
],
UserName: [ UserName: [
{ {
required: true, required: true,

View File

@ -27,23 +27,110 @@
<span v-html="$t('trials:researchForm:form:isFollowStudyParam')"></span> <span v-html="$t('trials:researchForm:form:isFollowStudyParam')"></span>
</el-checkbox> </el-checkbox>
</el-form-item> </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> </el-form>
<div style="margin-bottom: 20px;font-weight: bold;"> <div style="margin-bottom: 20px;font-weight: bold;">
{{ $t("trials:researchRecord:ImageManual:TableConfiguration") }} {{ $t("trials:researchRecord:ImageManual:TableConfiguration") }}
</div> </div>
<el-form size="small" :model="form" style="width:80%"> <el-form size="small" :model="form" style="width:80%">
<el-form-item :label="$t('trials:researchRecord:ImageManual:Equipment')"> <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 :label="item.value" v-for="item in $d.YesOrNo" :key="item.id">{{ item.label
}}</el-radio> }}</el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
</el-form> </el-form>
<div style="margin-bottom: 20px;font-weight: bold;">
{{ $t("trials:researchRecord:ImageManual:EquipmentConfig") }}
</div>
<el-form size="small" :model="EquipmentForm" style="width:80%" v-if="form.IsCloseEquipmentSurvey">
<el-form-item>
<el-checkbox v-model="EquipmentForm.EquipmentTypeEnum">
{{ $t('trials:researchForm:form:EquipmentTypeEnum') }}
</el-checkbox>
</el-form-item>
<el-form-item>
<el-checkbox v-model="EquipmentForm.Parameters">
{{ $t('trials:researchForm:form:Parameters') }}
</el-checkbox>
</el-form-item>
<el-form-item>
<el-checkbox v-model="EquipmentForm.ManufacturerType">
{{ $t('trials:researchForm:form:ManufacturerType') }}
</el-checkbox>
</el-form-item>
<el-form-item>
<el-checkbox v-model="EquipmentForm.ScannerType">
{{ $t('trials:researchForm:form:ScannerType') }}
</el-checkbox>
</el-form-item>
<el-form-item>
<el-checkbox v-model="EquipmentForm.MagneticFieldStrengthType">
{{ $t('trials:researchForm:form:MagneticFieldStrengthType') }}
</el-checkbox>
</el-form-item>
<el-form-item>
<el-checkbox v-model="EquipmentForm.BodyCoilChannelCount">
{{ $t('trials:researchForm:form:BodyCoilChannelCount') }}
</el-checkbox>
</el-form-item>
<el-form-item>
<el-checkbox v-model="EquipmentForm.HasDedicatedPdfFatQuantificationSequence"
@change="EquipmentForm.PdfFatQuantificationSequenceType = false">
{{ $t('trials:researchForm:form:HasDedicatedPdfFatQuantificationSequence') }}
</el-checkbox>
</el-form-item>
<el-form-item v-if="EquipmentForm.HasDedicatedPdfFatQuantificationSequence">
<el-checkbox v-model="EquipmentForm.PdfFatQuantificationSequenceType">
{{ $t('trials:researchForm:form:PdfFatQuantificationSequenceType') }}
</el-checkbox>
</el-form-item>
<el-form-item>
<el-checkbox v-model="EquipmentForm.HasT2R2Correction">
{{ $t('trials:researchForm:form:HasT2R2Correction') }}
</el-checkbox>
</el-form-item>
<el-form-item>
<el-checkbox v-model="EquipmentForm.CanFullyExportPdfParameterMapsAndRawDicom">
{{ $t('trials:researchForm:form:CanFullyExportPdfParameterMapsAndRawDicom') }}
</el-checkbox>
</el-form-item>
<el-form-item>
<el-checkbox v-model="EquipmentForm.Note">
{{ $t('trials:researchForm:form:Note') }}
</el-checkbox>
</el-form-item>
</el-form>
<div style="margin-bottom: 20px;font-weight: bold;"> <div style="margin-bottom: 20px;font-weight: bold;">
{{ $t("trials:researchRecord:ImageManual:Precautions") }} {{ $t("trials:researchRecord:ImageManual:Precautions") }}
</div> </div>
<el-input type="textarea" :autosize="{ minRows: 4, maxRows: 99 }" placeholder="" <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"> <div style="text-align: right;margin: 20px;" class="btnBox">
<el-button type="primary" :loading="loading" size="small" @click="handleSave"> <el-button type="primary" :loading="loading" size="small" @click="handleSave">
{{ $t('common:button:save') }} {{ $t('common:button:save') }}
@ -80,7 +167,30 @@ export default {
ReplaceContentCN: '', ReplaceContentCN: '',
IsOpenLostVistRead: false, IsOpenLostVistRead: false,
IsSupportQCDownloadImage: false, IsSupportQCDownloadImage: false,
IsCloseEquipmentSurvey: true IsCloseEquipmentSurvey: true,
IsRoutineMRIPDEE: false,
IsMRIPDFF: false,
MRIPDFFScanTime: false,
MRIPDFFLeadTime: false,
MRIPDFFOther: false,
IsAuthorize: false,
IsAuthorizeRadiologistsParticipate: false,
AssignFixedTechnologists: false,
ISStrictManualBurnFlag: false,
NotStrictManualBurnFlagReason: false
},
EquipmentForm: {
Parameters: false,
Note: false,
EquipmentTypeEnum: false,
ManufacturerType: false,
ScannerType: false,
MagneticFieldStrengthType: false,
BodyCoilChannelCount: false,
HasDedicatedPdfFatQuantificationSequence: false,
PdfFatQuantificationSequenceType: false,
HasT2R2Correction: false,
CanFullyExportPdfParameterMapsAndRawDicom: false,
}, },
obj: { obj: {
AverageEngravingCycle: false, AverageEngravingCycle: false,
@ -89,6 +199,14 @@ export default {
EfficacyEvaluatorType: false, EfficacyEvaluatorType: false,
IsFollowStudyParameters: false, IsFollowStudyParameters: false,
NotFollowReson: false, NotFollowReson: false,
IsRoutineMRIPDEE: false,
MRIPDFFScanTime: false,
MRIPDFFLeadTime: false,
MRIPDFFOther: false,
IsAuthorizeRadiologistsParticipate: false,
AssignFixedTechnologists: false,
ISStrictManualBurnFlag: false,
NotStrictManualBurnFlagReason: false,
}, },
loading: false loading: false
} }
@ -100,9 +218,21 @@ export default {
handleChange(val, key) { handleChange(val, key) {
if (key === 'IsConfirmImagingTechnologist') this.form.NotConfirmReson = val if (key === 'IsConfirmImagingTechnologist') this.form.NotConfirmReson = val
if (key === 'IsFollowStudyParameters') this.form.NotFollowReson = 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) { handleInput(val) {
this.form.ReplaceContentCN = val Object.keys(this.EquipmentForm).forEach(key => {
this.EquipmentForm[key] = false
})
}, },
async getInfo() { async getInfo() {
try { try {
@ -115,12 +245,22 @@ export default {
this.form[key] = true this.form[key] = true
if (key === 'ReplaceContent' || key === 'ReplaceContentCN') this.form[key] = '' 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.IsOpenLostVistRead = res.Result.IsOpenLostVistRead
this.form.IsSupportQCDownloadImage = res.Result.IsSupportQCDownloadImage this.form.IsSupportQCDownloadImage = res.Result.IsSupportQCDownloadImage
this.form.IsCloseEquipmentSurvey = !res.Result.IsCloseEquipmentSurvey this.form.IsCloseEquipmentSurvey = !res.Result.IsCloseEquipmentSurvey
if (Array.isArray(res.Result.NotShowFieldList) && res.Result.NotShowFieldList.length > 0) { if (Array.isArray(res.Result.NotShowFieldList) && res.Result.NotShowFieldList.length > 0) {
res.Result.NotShowFieldList.forEach(key => { res.Result.NotShowFieldList.forEach(key => {
this.form[key] = false 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) { if (Array.isArray(res.Result.ModifyFiledList) && res.Result.ModifyFiledList.length > 0) {
@ -128,8 +268,15 @@ export default {
this.form.ReplaceContentCN = res.Result.ModifyFiledList[0].ReplaceContentCN.split('<p>').join('').split('</p>').filter(item => isNaN(item)).join("\n") this.form.ReplaceContentCN = res.Result.ModifyFiledList[0].ReplaceContentCN.split('<p>').join('').split('</p>').filter(item => isNaN(item)).join("\n")
} else { } else {
let arr = [this.$t('trials:equiptResearch:form:item1'), this.$t('trials:equiptResearch:form:item2'), this.$t('trials:equiptResearch:form:item3'), this.$t('trials:equiptResearch:form:item4')] let arr = [this.$t('trials:equiptResearch:form:item1'), this.$t('trials:equiptResearch:form:item2'), this.$t('trials:equiptResearch:form:item3'), this.$t('trials:equiptResearch:form:item4')]
let arr2 = [this.$t('trials:equiptResearch:form:item5'), this.$t('trials:equiptResearch:form:item6'), this.$t('trials:equiptResearch:form:item7'), this.$t('trials:equiptResearch:form:item8')]
if (this.$i18n.locale !== 'zh') {
this.form.ReplaceContentCN = arr2.join("\n")
this.form.ReplaceContent = arr.join("\n") this.form.ReplaceContent = arr.join("\n")
} else {
this.form.ReplaceContentCN = arr.join("\n") this.form.ReplaceContentCN = arr.join("\n")
this.form.ReplaceContent = arr2.join("\n")
}
} }
} }
} catch (err) { } catch (err) {
@ -168,7 +315,8 @@ export default {
IsSupportQCDownloadImage: this.form.IsSupportQCDownloadImage, IsSupportQCDownloadImage: this.form.IsSupportQCDownloadImage,
IsCloseEquipmentSurvey: !this.form.IsCloseEquipmentSurvey, IsCloseEquipmentSurvey: !this.form.IsCloseEquipmentSurvey,
ModifyFiledList: [], ModifyFiledList: [],
NotShowFieldList: [] NotShowFieldList: [],
EquipmentControlFieldList: []
} }
Object.keys(this.obj).forEach(key => { Object.keys(this.obj).forEach(key => {
if (!this.form[key]) data.NotShowFieldList.push(key) if (!this.form[key]) data.NotShowFieldList.push(key)
@ -187,6 +335,16 @@ export default {
ReplaceContentCN: ReplaceContentCN, ReplaceContentCN: ReplaceContentCN,
} }
data.ModifyFiledList.push(obj) 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 this.loading = true
let res = await updateTrialExtralConfig(param, data) let res = await updateTrialExtralConfig(param, data)
this.loading = false this.loading = false

View File

@ -1,11 +1,7 @@
<template> <template>
<div class="adReview_wrapper"> <div class="adReview_wrapper">
<el-card <el-card v-if="isReadingShowSubjectInfo" shadow="never" :body-style="{ padding: '10px' }"
v-if="isReadingShowSubjectInfo" style="margin-bottom: 10px">
shadow="never"
:body-style="{ padding: '10px' }"
style="margin-bottom: 10px"
>
<h4> <h4>
<!-- 受试者 --> <!-- 受试者 -->
{{ $t("trials:adReview:title:subject") }} {{ $t("trials:adReview:title:subject") }}
@ -17,14 +13,11 @@
<!-- <div slot="header" class="clearfix"> <!-- <div slot="header" class="clearfix">
<span style="font-weight: bold;">评估结果</span> <span style="font-weight: bold;">评估结果</span>
</div> --> </div> -->
<div <div slot="header" style="
slot="header"
style="
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
" ">
>
<!-- 评估结果 --> <!-- 评估结果 -->
<div style="font-weight: bold"> <div style="font-weight: bold">
{{ $t("trials:adReview:title:result") }} {{ $t("trials:adReview:title:result") }}
@ -39,62 +32,45 @@
<el-table :data="adInfo.VisitInfoList" style="width: 100%"> <el-table :data="adInfo.VisitInfoList" style="width: 100%">
<!-- 访视名称 --> <!-- 访视名称 -->
<el-table-column <el-table-column prop="VisitName" :label="$t('trials:adReview:table:visitName')" show-overflow-tooltip
prop="VisitName" width="150" />
:label="$t('trials:adReview:table:visitName')"
show-overflow-tooltip
width="150"
/>
<!-- 评估结果 --> <!-- 评估结果 -->
<el-table-column <el-table-column v-for="j in judgeQuestion" :key="j.armEnum" :label="j.armEnum === 1
v-for="j in judgeQuestion"
:key="j.armEnum"
:label="
j.armEnum === 1
? $t('trials:adReview:table:viewR1') ? $t('trials:adReview:table:viewR1')
: j.armEnum === 2 : j.armEnum === 2
? $t('trials:adReview:table:viewR2') ? $t('trials:adReview:table:viewR2')
: $fd('ArmEnum', j.armEnum) : $fd('ArmEnum', j.armEnum)
" " align="center" prop="">
align="center"
prop=""
>
<template> <template>
<el-table-column <el-table-column v-for="(qs, i) in j.judgeQuestionList" :key="i" prop="" :label="qs" show-overflow-tooltip
v-for="(qs, i) in j.judgeQuestionList" width="150">
:key="i"
prop=""
:label="qs"
show-overflow-tooltip
width="150"
>
<template slot-scope="scope"> <template slot-scope="scope">
<div v-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].QuestionType === 1"> <div v-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].QuestionType === 1">
<span v-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].DictionaryCode"> <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>
<span v-else> <span v-else>
{{ scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer }} {{ scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer }}
</span> </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>
<div v-else-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].QuestionType === 2"> <div v-else-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].QuestionType === 2">
<div v-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer"> <div v-if="scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer">
<span> <span>
{{ $fd("YesOrNo",scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer) }} {{ $fd("YesOrNo", scope.row.VisitTaskInfoList[j.index].JudgeQuestionList[i].Answer) }}
</span> </span>
<!-- 查看详情 --> <!-- 查看详情 -->
<el-button <el-button type="text" style="margin-left: 5px" @click="
type="text"
style="margin-left: 5px"
@click="
handleViewDetail( handleViewDetail(
scope.row.VisitTaskInfoList[j.index].GlobalVisitTaskId scope.row.VisitTaskInfoList[j.index].GlobalVisitTaskId
) )
" ">
>
{{ $t("trials:adReview:table:view") }} {{ $t("trials:adReview:table:view") }}
</el-button> </el-button>
</div> </div>
@ -119,60 +95,34 @@
</el-table-column> </el-table-column>
<!-- 查看详情 --> <!-- 查看详情 -->
<el-table-column <el-table-column :label="criterionType === 10
:label="
criterionType === 10
? $t('trials:adReview:table:visitInfoview') ? $t('trials:adReview:table:visitInfoview')
: $t('trials:adReview:table:view') : $t('trials:adReview:table:view')
" " width="200" :fixed="isFixed ? 'right' : false">
width="200"
:fixed="isFixed ? 'right' : false"
>
<template slot-scope="scope"> <template slot-scope="scope">
<!-- 查看R1详情 --> <!-- 查看R1详情 -->
<el-button <el-button type="text" :title="$t('trials:adReview:table:viewR1')" @click="handleView(scope.row, 1)">
type="text"
:title="$t('trials:adReview:table:viewR1')"
@click="handleView(scope.row, 1)"
>
R1 R1
</el-button> </el-button>
<!-- 查看R2详情 --> <!-- 查看R2详情 -->
<el-button <el-button type="text" :title="$t('trials:adReview:table:viewR2')" @click="handleView(scope.row, 2)">
type="text"
:title="$t('trials:adReview:table:viewR2')"
@click="handleView(scope.row, 2)"
>
R2 R2
</el-button> </el-button>
</template> </template>
</el-table-column> </el-table-column>
<!-- 全局详情 PCWG --> <!-- 全局详情 PCWG -->
<el-table-column <el-table-column v-if="criterionType === 10" prop="VisitName" :label="$t('trials:adReview:table:glInfo')"
v-if="criterionType === 10" show-overflow-tooltip width="150">
prop="VisitName"
:label="$t('trials:adReview:table:glInfo')"
show-overflow-tooltip
width="150"
>
<template slot-scope="scope"> <template slot-scope="scope">
<!-- 查看R1详情 --> <!-- 查看R1详情 -->
<el-button <el-button v-if="scope.$index === adInfo.VisitInfoList.length - 1" type="text"
v-if="scope.$index === adInfo.VisitInfoList.length - 1" :title="$t('trials:adReview:table:viewR1')" @click="handleViewGl(scope.row, 1)">
type="text"
:title="$t('trials:adReview:table:viewR1')"
@click="handleViewGl(scope.row, 1)"
>
R1 R1
</el-button> </el-button>
<!-- 查看R2详情 --> <!-- 查看R2详情 -->
<el-button <el-button v-if="scope.$index === adInfo.VisitInfoList.length - 1" type="text"
v-if="scope.$index === adInfo.VisitInfoList.length - 1" :title="$t('trials:adReview:table:viewR2')" @click="handleViewGl(scope.row, 2)">
type="text"
:title="$t('trials:adReview:table:viewR2')"
@click="handleViewGl(scope.row, 2)"
>
R2 R2
</el-button> </el-button>
</template> </template>
@ -186,120 +136,64 @@
$t("trials:adReview:title:adResult") $t("trials:adReview:title:adResult")
}}</span> }}</span>
</div> </div>
<el-form <el-form ref="adForm" v-loading="loading" :model="adForm" style="width: 800px" label-width="100">
ref="adForm"
v-loading="loading"
:model="adForm"
style="width: 800px"
label-width="100"
>
<!-- 选择阅片人 --> <!-- 选择阅片人 -->
<el-form-item <el-form-item :label="$t('trials:adReview:title:choseReader')" prop="judgeResultTaskId" :rules="[
:label="$t('trials:adReview:title:choseReader')"
prop="judgeResultTaskId"
:rules="[
{ required: true, message: this.$t('common:ruleMessage:select') }, { required: true, message: this.$t('common:ruleMessage:select') },
]" ]">
> <el-radio-group v-model="adForm.judgeResultTaskId" :disabled="adInfo.ReadingTaskState >= 2">
<el-radio-group <el-radio v-for="t in visitTaskArmList" :key="t.VisitTaskId" :label="t.VisitTaskId"
v-model="adForm.judgeResultTaskId" @change="handleVisitTaskArmChange">
:disabled="adInfo.ReadingTaskState >= 2"
>
<el-radio
v-for="t in visitTaskArmList"
:key="t.VisitTaskId"
:label="t.VisitTaskId"
@change="handleVisitTaskArmChange"
>
{{ $fd("ArmEnum", t.ArmEnum) }} {{ $fd("ArmEnum", t.ArmEnum) }}
</el-radio> </el-radio>
</el-radio-group> </el-radio-group>
</el-form-item> </el-form-item>
<!-- 裁判原因 --> <!-- 裁判原因 -->
<el-form-item <el-form-item :label="$t('trials:adReview:title:adReason')" prop="judgeResultRemark" :rules="[
:label="$t('trials:adReview:title:adReason')"
prop="judgeResultRemark"
:rules="[
{ required: true, message: this.$t('common:ruleMessage:specify') }, { required: true, message: this.$t('common:ruleMessage:specify') },
{ {
max: 500, max: 500,
message: `${this.$t('common:ruleMessage:maxLength')} 500`, message: `${this.$t('common:ruleMessage:maxLength')} 500`,
trigger: ['blur', 'change'], trigger: ['blur', 'change'],
}, },
]" ]">
>
<div style="position: relative"> <div style="position: relative">
<div <div style="
style="
position: absolute; position: absolute;
left: 0; left: 0;
top: 30px; top: 30px;
color: #606266; color: #606266;
font-size: 13px; font-size: 13px;
" ">
>
{{ remark }} {{ remark }}
</div> </div>
<el-input <el-input v-model="adForm.judgeResultRemark" type="textarea" :autosize="{ minRows: 4, maxRows: 6 }"
v-model="adForm.judgeResultRemark" :disabled="adInfo.ReadingTaskState >= 2" style="margin-top: 25px" />
type="textarea"
:autosize="{ minRows: 4, maxRows: 6 }"
:disabled="adInfo.ReadingTaskState >= 2"
style="margin-top: 25px"
/>
</div> </div>
</el-form-item> </el-form-item>
<!-- 截图说明 --> <!-- 截图说明 -->
<el-form-item :label="$t('trials:adReview:title:screenShot')"> <el-form-item :label="$t('trials:adReview:title:screenShot')">
<el-upload <el-upload action :accept="accept" :on-preview="handlePictureCardPreview" :before-upload="handleBeforeUpload"
action :http-request="uploadScreenshot" list-type="picture-card" :on-remove="handleRemove" :file-list="fileList"
:accept="accept" :class="{ disabled: adInfo.ReadingTaskState >= 2 }" :disabled="adInfo.ReadingTaskState >= 2">
: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" /> <i slot="default" class="el-icon-plus" />
<div <div slot="file" slot-scope="{ file }" style="width: 100%; height: 100%">
slot="file" <viewer :ref="file.url" :images="images" style="
slot-scope="{ file }"
style="width: 100%; height: 100%"
>
<viewer
:ref="file.url"
:images="images"
style="
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
width: 100%; width: 100%;
height: 100%; height: 100%;
" ">
> <img class="el-upload-list__item-thumbnail" :src="OSSclientConfig.basePath + file.url" alt=""
<img crossorigin="anonymous" style="max-width: 100%; max-height: 100%">
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-actions">
<span <span class="el-upload-list__item-preview" @click="handlePictureCardPreview(file)">
class="el-upload-list__item-preview"
@click="handlePictureCardPreview(file)"
>
<i class="el-icon-zoom-in" /> <i class="el-icon-zoom-in" />
</span> </span>
<span <span v-if="adInfo.ReadingTaskState < 2" class="el-upload-list__item-delete"
v-if="adInfo.ReadingTaskState < 2" @click="handleRemove(file)">
class="el-upload-list__item-delete"
@click="handleRemove(file)"
>
<i class="el-icon-delete" /> <i class="el-icon-delete" />
</span> </span>
</span> </span>
@ -324,10 +218,7 @@
</div> </div>
</el-card> </el-card>
<el-card <el-card v-if="isReadingShowPreviousResults" :body-style="{ padding: '10px' }">
v-if="isReadingShowPreviousResults"
:body-style="{ padding: '10px' }"
>
<div slot="header" class="clearfix"> <div slot="header" class="clearfix">
<!-- 既往裁判评估 --> <!-- 既往裁判评估 -->
<span style="font-weight: bold">{{ <span style="font-weight: bold">{{
@ -335,24 +226,12 @@
}}</span> }}</span>
</div> </div>
<el-table <el-table v-loading="priorLoading" :data="priorADList" style="width: 100%">
v-loading="priorLoading"
:data="priorADList"
style="width: 100%"
>
<!-- 裁判阅片 --> <!-- 裁判阅片 -->
<el-table-column <el-table-column prop="TaskBlindName" :label="$t('trials:adReview:table:adReading')" show-overflow-tooltip
prop="TaskBlindName" width="200" />
:label="$t('trials:adReview:table:adReading')" <el-table-column prop="JudgeResultArm" :label="$t('trials:adReview:table:adResult')" show-overflow-tooltip
show-overflow-tooltip width="200">
width="200"
/>
<el-table-column
prop="JudgeResultArm"
:label="$t('trials:adReview:table:adResult')"
show-overflow-tooltip
width="200"
>
<template slot-scope="scope"> <template slot-scope="scope">
{{ $fd("ArmEnum", scope.row.JudgeResultArm) }} {{ $fd("ArmEnum", scope.row.JudgeResultArm) }}
</template> </template>
@ -360,35 +239,22 @@
<el-table-column :label="$t('common:action:action')" width="200"> <el-table-column :label="$t('common:action:action')" width="200">
<template slot-scope="scope"> <template slot-scope="scope">
<!-- 查看详情 --> <!-- 查看详情 -->
<el-button <el-button circle :title="$t('trials:adReview:table:view')" icon="el-icon-view"
circle @click="handleViewDetail(scope.row.VisitTaskId)" />
:title="$t('trials:adReview:table:view')"
icon="el-icon-view"
@click="handleViewDetail(scope.row.VisitTaskId)"
/>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
</el-card> </el-card>
<!-- 签名框 --> <!-- 签名框 -->
<el-dialog <el-dialog v-if="signVisible" :visible.sync="signVisible" :close-on-click-modal="false" width="600px"
v-if="signVisible" custom-class="base-dialog-wrapper">
:visible.sync="signVisible"
:close-on-click-modal="false"
width="600px"
custom-class="base-dialog-wrapper"
>
<div slot="title"> <div slot="title">
<span style="font-size: 18px">{{ $t("common:dialogTitle:sign") }}</span> <span style="font-size: 18px">{{ $t("common:dialogTitle:sign") }}</span>
<span style="font-size: 12px; margin-left: 5px">{{ <span style="font-size: 12px; margin-left: 5px">{{
`(${$t("common:label:sign")}${currentUser})` `(${$t("common:label:sign")}${currentUser})`
}}</span> }}</span>
</div> </div>
<SignForm <SignForm ref="signForm" :sign-code-enum="signCode" @closeDialog="closeSignDialog" />
ref="signForm"
:sign-code-enum="signCode"
@closeDialog="closeSignDialog"
/>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
@ -447,6 +313,10 @@ export default {
isExistsClinicalData: { isExistsClinicalData: {
type: Boolean, type: Boolean,
required: true required: true
},
imageToolType: {
type: Number,
required: true
} }
}, },
data() { data() {
@ -685,7 +555,7 @@ export default {
// window.opener.postMessage('refreshTaskList', window.location) // window.opener.postMessage('refreshTaskList', window.location)
// //
this.adInfo.ReadingTaskState = 2 this.adInfo.ReadingTaskState = 2
const res = await getAutoCutNextTask() const res = await getAutoCutNextTask({imageToolType: this.imageToolType})
var isAutoTask = res.Result.AutoCutNextTask var isAutoTask = res.Result.AutoCutNextTask
if (isAutoTask) { if (isAutoTask) {
// store.dispatch('reading/resetVisitTasks') // store.dispatch('reading/resetVisitTasks')
@ -729,17 +599,13 @@ export default {
this.$router.currentRoute.query.TrialReadingCriterionId this.$router.currentRoute.query.TrialReadingCriterionId
var path = '' var path = ''
if (readingTool === 0 || readingTool === 2 ) { if (readingTool === 0 || readingTool === 2 || readingTool === 3) {
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${ path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.trialId
this.trialId }&subjectCode=${this.subjectCode}&subjectId=${this.subjectId
}&subjectCode=${this.subjectCode}&subjectId=${
this.subjectId
}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}` }&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
} else { } else {
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${ path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.trialId
this.trialId }&subjectCode=${this.subjectCode}&subjectId=${this.subjectId
}&subjectCode=${this.subjectCode}&subjectId=${
this.subjectId
}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}` }&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
} }
var routeData = this.$router.resolve({ path }) var routeData = this.$router.resolve({ path })
@ -766,21 +632,15 @@ export default {
this.$router.currentRoute.query.TrialReadingCriterionId this.$router.currentRoute.query.TrialReadingCriterionId
var path = '' var path = ''
if (readingTool === 0 || readingTool === 2 ) { if (readingTool === 0 || readingTool === 2 || readingTool === 3) {
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${ path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.trialId
this.trialId }&subjectCode=${this.subjectCode}&subjectId=${this.subjectId
}&subjectCode=${this.subjectCode}&subjectId=${ }&visitTaskId=${task.VisitTaskId
this.subjectId
}&visitTaskId=${
task.VisitTaskId
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}` }&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
} else { } else {
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${ path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.trialId
this.trialId }&subjectCode=${this.subjectCode}&subjectId=${this.subjectId
}&subjectCode=${this.subjectCode}&subjectId=${ }&visitTaskId=${task.VisitTaskId
this.subjectId
}&visitTaskId=${
task.VisitTaskId
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}` }&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
} }
var routeData = this.$router.resolve({ path }) var routeData = this.$router.resolve({ path })
@ -801,21 +661,15 @@ export default {
this.$router.currentRoute.query.TrialReadingCriterionId this.$router.currentRoute.query.TrialReadingCriterionId
var path = '' var path = ''
if (readingTool === 0 || readingTool === 2 ) { if (readingTool === 0 || readingTool === 2 || readingTool === 3) {
path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${ path = `/readingDicoms?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.trialId
this.trialId }&subjectCode=${this.subjectCode}&subjectId=${this.subjectId
}&subjectCode=${this.subjectCode}&subjectId=${ }&visitTaskId=${task.GlobalVisitTaskId
this.subjectId
}&visitTaskId=${
task.GlobalVisitTaskId
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}` }&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
} else { } else {
path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${ path = `/noneDicomReading?TrialReadingCriterionId=${trialReadingCriterionId}&trialId=${this.trialId
this.trialId }&subjectCode=${this.subjectCode}&subjectId=${this.subjectId
}&subjectCode=${this.subjectCode}&subjectId=${ }&visitTaskId=${task.GlobalVisitTaskId
this.subjectId
}&visitTaskId=${
task.GlobalVisitTaskId
}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}` }&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}&key=${new Date().getTime()}`
} }
var routeData = this.$router.resolve({ path }) var routeData = this.$router.resolve({ path })
@ -932,17 +786,21 @@ export default {
width: 100%; width: 100%;
height: 100%; height: 100%;
overflow-y: auto; overflow-y: auto;
.box-mr { .box-mr {
margin: 10px 0; margin: 10px 0;
} }
.disabled { .disabled {
::v-deep .el-upload--picture-card { ::v-deep .el-upload--picture-card {
display: none; display: none;
} }
} }
::v-deep .el-upload-list__item { ::v-deep .el-upload-list__item {
transition: none !important; transition: none !important;
} }
::v-deep .el-upload-list__item-thumbnail { ::v-deep .el-upload-list__item-thumbnail {
/* 图片在方框内显示长边 */ /* 图片在方框内显示长边 */
object-fit: scale-down !important; object-fit: scale-down !important;

View File

@ -146,6 +146,7 @@ import BidirectionalTool from '@/views/trials/trials-panel/reading/dicoms/tools/
import ArrowAnnotateTool from '@/views/trials/trials-panel/reading/dicoms/tools/ArrowAnnotate/ArrowAnnotateTool' import ArrowAnnotateTool from '@/views/trials/trials-panel/reading/dicoms/tools/ArrowAnnotate/ArrowAnnotateTool'
import RectangleRoiTool from '@/views/trials/trials-panel/reading/dicoms/tools/RectangleRoi/RectangleRoiTool' import RectangleRoiTool from '@/views/trials/trials-panel/reading/dicoms/tools/RectangleRoi/RectangleRoiTool'
import ProbeTool from '@/views/trials/trials-panel/reading/dicoms/tools/Probe/ProbeTool' import ProbeTool from '@/views/trials/trials-panel/reading/dicoms/tools/Probe/ProbeTool'
import FixedCircleRoiTool from '@/views/trials/trials-panel/reading/dicoms/tools/FixedCircleRoi/FixedCircleRoiTool'
// import OrientationMarkersTool from '@/views/trials/trials-panel/reading/dicoms/tools/OrientationMarkers/OrientationMarkersTool' // import OrientationMarkersTool from '@/views/trials/trials-panel/reading/dicoms/tools/OrientationMarkers/OrientationMarkersTool'
import ScaleOverlayTool from '@/views/trials/trials-panel/reading/dicoms/tools/ScaleOverlay/ScaleOverlayTool' import ScaleOverlayTool from '@/views/trials/trials-panel/reading/dicoms/tools/ScaleOverlay/ScaleOverlayTool'
import getOrientationString from '@/views/trials/trials-panel/reading/dicoms/tools/OrientationMarkers/getOrientationString' import getOrientationString from '@/views/trials/trials-panel/reading/dicoms/tools/OrientationMarkers/getOrientationString'
@ -260,7 +261,7 @@ export default {
series: '', series: '',
ToolStateManager: null, ToolStateManager: null,
renderedMeasured: [], renderedMeasured: [],
measuredTools: ['Length', 'Bidirectional', 'ArrowAnnotate', 'RectangleRoi', 'Probe'], measuredTools: ['Length', 'Bidirectional', 'ArrowAnnotate', 'RectangleRoi', 'Probe', 'FixedCircleRoi'],
measureData: [], measureData: [],
selectedLesion: null, selectedLesion: null,
activeTool: 0, // 0:enable 1:passive 2:active activeTool: 0, // 0:enable 1:passive 2:active
@ -584,6 +585,13 @@ export default {
e.stopPropagation() e.stopPropagation()
e.preventDefault() e.preventDefault()
} }
} else if (this.CriterionType === 22 && this.activeToolName === 'Probe' && this.readingTaskState < 2) {
if (!(e.detail.image.imageFrame.photometricInterpretation === 'MONOCHROME1' || e.detail.image.imageFrame.photometricInterpretation === 'MONOCHROME2')) {
this.$alert(this.$t('trials:MRIPDFF:message:message5'))
e.stopImmediatePropagation()
e.stopPropagation()
e.preventDefault()
}
} }
}, },
pointNearTool(e) { pointNearTool(e) {
@ -593,7 +601,9 @@ export default {
var toolType = this.measuredTools[m] var toolType = this.measuredTools[m]
const toolState = ToolStateManager.getImageIdToolState(image.imageId, toolType) const toolState = ToolStateManager.getImageIdToolState(image.imageId, toolType)
if (toolState) { if (toolState) {
var toolObj = new cornerstoneTools[`${toolType}Tool`]() const toolClass = this.getToolClassByType(toolType)
if (!toolClass) continue
var toolObj = new toolClass()
var i = toolState.data.findIndex(data => toolObj.pointNearTool(element, data, currentPoints.canvas, 'mouse')) var i = toolState.data.findIndex(data => toolObj.pointNearTool(element, data, currentPoints.canvas, 'mouse'))
if (i > -1 && this.disabledMarks.length > 0 && this.disabledMarks.indexOf(toolState.data[i].remark) > -1) { if (i > -1 && this.disabledMarks.length > 0 && this.disabledMarks.indexOf(toolState.data[i].remark) > -1) {
return true return true
@ -783,7 +793,9 @@ export default {
var toolType = this.measuredTools[m] var toolType = this.measuredTools[m]
const toolState = ToolStateManager.getImageIdToolState(e.detail.image.imageId, toolType) const toolState = ToolStateManager.getImageIdToolState(e.detail.image.imageId, toolType)
if (toolState) { if (toolState) {
var toolObj = new cornerstoneTools[`${toolType}Tool`]() const toolClass = this.getToolClassByType(toolType)
if (!toolClass) continue
var toolObj = new toolClass()
var i = toolState.data.findIndex(data => toolObj.pointNearTool(element, data, currentPoints.canvas, 'mouse')) var i = toolState.data.findIndex(data => toolObj.pointNearTool(element, data, currentPoints.canvas, 'mouse'))
if (i > -1) { if (i > -1) {
@ -1019,7 +1031,9 @@ export default {
var toolType = this.measuredTools[t] var toolType = this.measuredTools[t]
const toolState = ToolStateManager.getImageIdToolState(e.detail.image.imageId, toolType) const toolState = ToolStateManager.getImageIdToolState(e.detail.image.imageId, toolType)
if (!toolState) continue if (!toolState) continue
var toolObj = new cornerstoneTools[`${toolType}Tool`]() const toolClass = this.getToolClassByType(toolType)
if (!toolClass) continue
var toolObj = new toolClass()
var i = toolState.data.findIndex(data => toolObj.pointNearTool(element, data, currentPoints.canvas, 'mouse')) var i = toolState.data.findIndex(data => toolObj.pointNearTool(element, data, currentPoints.canvas, 'mouse'))
if (i > -1) { if (i > -1) {
var idx = this.measureData.findIndex(item => item.MeasureData && item.MeasureData.data && item.MeasureData.data.uuid === toolState.data[i].uuid) var idx = this.measureData.findIndex(item => item.MeasureData && item.MeasureData.data && item.MeasureData.data.uuid === toolState.data[i].uuid)
@ -1046,7 +1060,7 @@ export default {
measureData.wc = Math.round(viewport.voi.windowCenter) measureData.wc = Math.round(viewport.voi.windowCenter)
measureData.data.active = false measureData.data.active = false
var criterionType = parseInt(localStorage.getItem('CriterionType')) var criterionType = parseInt(localStorage.getItem('CriterionType'))
if (criterionType === 21) { if (criterionType === 21 || criterionType === 22) {
measureData.tableQuestionId = this.measureData[idx].TableQuestionId measureData.tableQuestionId = this.measureData[idx].TableQuestionId
} }
this.$emit('modifyMeasureData', { measureData, questionInfo }) this.$emit('modifyMeasureData', { measureData, questionInfo })
@ -1057,6 +1071,15 @@ export default {
} }
} }
}, },
getToolClassByType(toolType) {
if (toolType === 'Length') return LengthTool
if (toolType === 'Bidirectional') return BidirectionalTool
if (toolType === 'ArrowAnnotate') return ArrowAnnotateTool
if (toolType === 'RectangleRoi') return RectangleRoiTool
if (toolType === 'Probe') return ProbeTool
if (toolType === 'FixedCircleRoi') return FixedCircleRoiTool
return cornerstoneTools[`${toolType}Tool`]
},
loadImageStack(dicomSeries) { loadImageStack(dicomSeries) {
return new Promise(resolve => { return new Promise(resolve => {
this.isInitWwwc = true this.isInitWwwc = true
@ -1134,7 +1157,8 @@ export default {
// resolve() // resolve()
// }) // })
this.loading = true this.loading = true
cornerstone.metaData.addProvider(metaDataProvider, 1); cornerstone.metaData.removeProvider(metaDataProvider)
cornerstone.metaData.addProvider(metaDataProvider, 100000)
cornerstone.loadAndCacheImage(this.stack.imageIds[this.stack.currentImageIdIndex]) cornerstone.loadAndCacheImage(this.stack.imageIds[this.stack.currentImageIdIndex])
.then(async image => { .then(async image => {
if (this.stack.imageIds.indexOf(image.imageId) !== -1) { if (this.stack.imageIds.indexOf(image.imageId) !== -1) {
@ -1168,8 +1192,22 @@ export default {
// Add the tool // Add the tool
const toolName = toolBtn.getAttribute('data-tool') const toolName = toolBtn.getAttribute('data-tool')
const apiTool = cornerstoneTools[`${toolName}Tool`] const apiTool = cornerstoneTools[`${toolName}Tool`]
if (apiTool) { let toolClass = apiTool
const toolAlreadyAddedToElement = cornerstoneTools.getToolForElement(element, apiTool) if (toolName === 'Length') {
toolClass = LengthTool
} else if (toolName === 'Bidirectional') {
toolClass = BidirectionalTool
} else if (toolName === 'ArrowAnnotate') {
toolClass = ArrowAnnotateTool
} else if (toolName === 'RectangleRoi') {
toolClass = RectangleRoiTool
} else if (toolName === 'Probe' && (parseInt(localStorage.getItem('CriterionType')) === 21)) {
toolClass = ProbeTool
} else if (toolName === 'Probe' && (parseInt(localStorage.getItem('CriterionType')) === 22)) {
toolClass = FixedCircleRoiTool
}
if (toolClass) {
const toolAlreadyAddedToElement = cornerstoneTools.getToolForElement(element, toolClass)
if (!toolAlreadyAddedToElement) { if (!toolAlreadyAddedToElement) {
if (toolName === 'Length') { if (toolName === 'Length') {
@ -1182,8 +1220,10 @@ export default {
cornerstoneTools.addToolForElement(element, ArrowAnnotateTool, { configuration: { allowEmptyLabel: true, handleRadius: false, drawHandlesOnHover: true, hideHandlesIfMoving: true } }) cornerstoneTools.addToolForElement(element, ArrowAnnotateTool, { configuration: { allowEmptyLabel: true, handleRadius: false, drawHandlesOnHover: true, hideHandlesIfMoving: true } })
} else if (toolName === 'RectangleRoi') { } else if (toolName === 'RectangleRoi') {
cornerstoneTools.addToolForElement(element, RectangleRoiTool, { configuration: { allowEmptyLabel: true, handleRadius: false, drawHandlesOnHover: true, hideHandlesIfMoving: true } }) cornerstoneTools.addToolForElement(element, RectangleRoiTool, { configuration: { allowEmptyLabel: true, handleRadius: false, drawHandlesOnHover: true, hideHandlesIfMoving: true } })
} else if (toolName === 'Probe' && parseInt(localStorage.getItem('CriterionType')) === 21) { } else if (toolName === 'Probe' && (parseInt(localStorage.getItem('CriterionType')) === 21)) {
cornerstoneTools.addToolForElement(element, ProbeTool, { configuration: { fixedRadius: 5, handleRadius: true, drawHandlesOnHover: true, hideHandlesIfMoving: true, digits: this.digitPlaces } }) cornerstoneTools.addToolForElement(element, ProbeTool, { configuration: { fixedRadius: 5, handleRadius: true, drawHandlesOnHover: true, hideHandlesIfMoving: true, digits: this.digitPlaces } })
} else if (toolName === 'Probe' && parseInt(localStorage.getItem('CriterionType')) === 22) {
cornerstoneTools.addToolForElement(element, ProbeTool, { configuration: { fixedRadius: 12, unit: 'mm', handleRadius: true, drawHandlesOnHover: true, hideHandlesIfMoving: true, digits: this.digitPlaces } })
} else { } else {
cornerstoneTools.addToolForElement(element, apiTool) cornerstoneTools.addToolForElement(element, apiTool)
} }
@ -1527,7 +1567,7 @@ export default {
measureData.wc = Math.round(viewport.voi.windowCenter) measureData.wc = Math.round(viewport.voi.windowCenter)
measureData.data.active = false measureData.data.active = false
var criterionType = parseInt(localStorage.getItem('CriterionType')) var criterionType = parseInt(localStorage.getItem('CriterionType'))
if (criterionType === 21) { if (criterionType === 21 || criterionType === 22) {
measureData.tableQuestionId = this.measureData[idx].TableQuestionId measureData.tableQuestionId = this.measureData[idx].TableQuestionId
} }
this.$emit('modifyMeasureData', { measureData, questionInfo }) this.$emit('modifyMeasureData', { measureData, questionInfo })
@ -1981,7 +2021,8 @@ export default {
'CobbAngle', 'CobbAngle',
'Angle', 'Angle',
'Bidirectional', 'Bidirectional',
'FreehandRoi' 'FreehandRoi',
'FixedCircleRoi'
] ]
for (let i = 0; i < toolROITypes.length; i++) { for (let i = 0; i < toolROITypes.length; i++) {
const toolROIType = toolROITypes[i] const toolROIType = toolROITypes[i]

View File

@ -492,6 +492,10 @@
:question-form-change-state="questionFormChangeState" :question-form-change-num="questionFormChangeNum" :question-form-change-state="questionFormChangeState" :question-form-change-num="questionFormChangeNum"
:is-show="isShow" :is-reading-show-subject-info="isReadingShowSubjectInfo" :is-show="isShow" :is-reading-show-subject-info="isReadingShowSubjectInfo"
@handleReadingChart="handleReadingChart" /> @handleReadingChart="handleReadingChart" />
<MRIPDFFAdvance v-else-if="CriterionType === 22" ref="measurementList"
:question-form-change-state="questionFormChangeState" :question-form-change-num="questionFormChangeNum"
:is-show="isShow" :is-reading-show-subject-info="isReadingShowSubjectInfo"
@handleReadingChart="handleReadingChart" />
<h2 v-else style="color:#ddd"> <h2 v-else style="color:#ddd">
Developing... Developing...
</h2> </h2>
@ -539,7 +543,7 @@
</el-tab-pane> </el-tab-pane>
<!-- 其他 --> <!-- 其他 -->
<el-tab-pane :label="$t('trials:reading:tab:others')" name="3"> <el-tab-pane :label="$t('trials:reading:tab:others')" name="3">
<Others v-if="activeName === '3'" /> <Others v-if="activeName === '3'" :imageToolType="1"/>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
@ -689,6 +693,7 @@ import LuganoWithoutPETQuestionList from './LuganoWithoutPET/QuestionList'
import IVUSList from './IVUS/QuestionList' import IVUSList from './IVUS/QuestionList'
import OCTList from './OCT/QuestionList' import OCTList from './OCT/QuestionList'
import MRIPDFF from './MRIPDFF/QuestionList' import MRIPDFF from './MRIPDFF/QuestionList'
import MRIPDFFAdvance from './MRIPDFFAdvance/QuestionList'
import CustomWwwcForm from './CustomWwwcForm' import CustomWwwcForm from './CustomWwwcForm'
import Manuals from './Manuals' import Manuals from './Manuals'
import Hotkeys from './Hotkeys' import Hotkeys from './Hotkeys'
@ -725,6 +730,7 @@ export default {
IVUSList, IVUSList,
OCTList, OCTList,
MRIPDFF, MRIPDFF,
MRIPDFFAdvance,
'download-dicom-and-nonedicom': downloadDicomAndNonedicom, 'download-dicom-and-nonedicom': downloadDicomAndNonedicom,
'upload-dicom-and-nonedicom': uploadDicomAndNonedicom, 'upload-dicom-and-nonedicom': uploadDicomAndNonedicom,
SignForm SignForm
@ -1012,6 +1018,10 @@ export default {
this.measuredTools = [{ this.measuredTools = [{
toolName: 'Probe', text: this.$t('trials:reading:button:circle'), icon: 'oval', isDisabled: false, disabledReason: '' toolName: 'Probe', text: this.$t('trials:reading:button:circle'), icon: 'oval', isDisabled: false, disabledReason: ''
}] }]
} else if (this.CriterionType === 22) {
this.measuredTools = [{
toolName: 'Probe', text: this.$t('trials:reading:button:circle'), icon: 'oval', isDisabled: false, disabledReason: ''
}]
} }
this.rotateList[0] = '1' this.rotateList[0] = '1'
this.colorList[0] = '' this.colorList[0] = ''
@ -1041,30 +1051,23 @@ export default {
console.log('getMeasureData') console.log('getMeasureData')
}) })
DicomEvent.$on('getScreenshots', async (measuredData, callback) => { DicomEvent.$on('getScreenshots', async (measuredData, callback) => {
if (this.currentDicomCanvasIndex > -1) { if (this.currentDicomCanvasIndex <= -1 || typeof callback !== 'function') return
const shouldRelocate = this.shouldRelocateBeforeScreenshot(measuredData)
if (shouldRelocate) {
await this.imageLocation(measuredData) await this.imageLocation(measuredData)
if (!measuredData.isMarked) { }
if (!measuredData || !measuredData.isMarked) {
callback() callback()
return return
} }
setTimeout(async () => { const base64Str = await this.captureActiveViewportScreenshot()
// var base64Str = this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].getScreenshots()
const divForDownloadViewport = document.querySelector(
`div[data-canvas-uid="dicomCanvas${this.currentDicomCanvasIndex}"]`
)
var canvas = await html2canvas(divForDownloadViewport)
var base64Str = canvas.toDataURL('image/png', 1)
console.log('getScreenshots')
callback(base64Str) callback(base64Str)
}, 50)
}
}) })
DicomEvent.$on('imageLocation', async (measuredData) => { DicomEvent.$on('imageLocation', async (measuredData) => {
return new Promise(async resolve => { return new Promise(async resolve => {
if (!measuredData) return if (!measuredData) return
await this.imageLocation(measuredData)
console.log('imageLocation') await this.imageLocation(measuredData)
resolve() resolve()
}) })
}) })
@ -1126,7 +1129,12 @@ export default {
DicomEvent.$on('addAnnotation', async obj => { DicomEvent.$on('addAnnotation', async obj => {
this.tmpData = Object.assign({}, obj.question) this.tmpData = Object.assign({}, obj.question)
// await this.imageLocation(obj.locateInfo) // await this.imageLocation(obj.locateInfo)
if (this.CriterionType === 21) {
this.setToolActive('Probe', true, null, 'tableQuestion') this.setToolActive('Probe', true, null, 'tableQuestion')
} else if (this.CriterionType === 22) {
this.setToolActive('Probe', true, null, 'tableQuestion')
}
}) })
window.addEventListener('beforeunload', () => { window.addEventListener('beforeunload', () => {
if (this.petctWindow) { if (this.petctWindow) {
@ -1230,7 +1238,7 @@ export default {
async getHotKeys() { async getHotKeys() {
// const loading = this.$loading({ fullscreen: true }) // const loading = this.$loading({ fullscreen: true })
try { try {
const res = await getDoctorShortcutKey({ imageToolType: 0 }) const res = await getDoctorShortcutKey({ imageToolType: 1 })
res.Result.map(item => { res.Result.map(item => {
this.hotKeyList.push({ id: item.Id, altKey: item.AltKey, ctrlKey: item.CtrlKey, shiftKey: item.ShiftKey, metaKey: item.MetaKey, key: item.Keyboardkey, code: item.Code, text: item.Text, shortcutKeyEnum: item.ShortcutKeyEnum }) this.hotKeyList.push({ id: item.Id, altKey: item.AltKey, ctrlKey: item.CtrlKey, shiftKey: item.ShiftKey, metaKey: item.MetaKey, key: item.Keyboardkey, code: item.Code, text: item.Text, shortcutKeyEnum: item.ShortcutKeyEnum })
}) })
@ -1695,6 +1703,43 @@ export default {
} }
}) })
}, },
shouldRelocateBeforeScreenshot(measuredData) {
if (!measuredData) return false
const currentCanvas = this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`] && this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0]
if (!currentCanvas || !currentCanvas.stack) return true
const currentStack = currentCanvas.stack
if (currentStack.visitTaskId !== measuredData.visitTaskId) return true
// imageLocation
const targetSeries = this.getSeriesInfoByMark(measuredData.visitTaskId, measuredData)
if (!targetSeries) return false
if (currentStack.seriesId !== targetSeries.seriesId) return true
if (typeof targetSeries.imageIdIndex === 'number' &&
targetSeries.imageIdIndex !== currentStack.currentImageIdIndex) {
return true
}
return false
},
async captureActiveViewportScreenshot() {
// const currentCanvas = this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`] && this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0]
await this.$nextTick()
const divForDownloadViewport = document.querySelector(
`div[data-canvas-uid="dicomCanvas${this.currentDicomCanvasIndex}"]`
)
if (divForDownloadViewport) {
try {
const canvas = await html2canvas(divForDownloadViewport, {
logging: false,
useCORS: true,
scale: 1
})
return canvas.toDataURL('image/png')
} catch (e) {
console.log(e)
}
}
return ''
},
setToolToTarget(obj) { setToolToTarget(obj) {
if (obj.readingTaskState < 2 && obj.markTool && !obj.isMarked) { if (obj.readingTaskState < 2 && obj.markTool && !obj.isMarked) {
if (this.activeTool) { if (this.activeTool) {
@ -2160,7 +2205,7 @@ export default {
}, },
// //
setMeasureData(data) { setMeasureData(data) {
if (this.CriterionType === 21) { if (this.CriterionType === 21 || this.CriterionType === 22) {
if (this.tmpData) { if (this.tmpData) {
data.tableQuestionId = this.tmpData.Id data.tableQuestionId = this.tmpData.Id
data.tableQuestionMark = this.tmpData.QuestionMark data.tableQuestionMark = this.tmpData.QuestionMark
@ -2174,7 +2219,7 @@ export default {
}, },
// //
modifyMeasureData(data) { modifyMeasureData(data) {
if (this.CriterionType === 21 && data.measureData.tableQuestionId) { if ((this.CriterionType === 21 || this.CriterionType === 22) && data.measureData.tableQuestionId) {
this.$refs['measurementList'].modifyMeasuredData(data) this.$refs['measurementList'].modifyMeasuredData(data)
} else { } else {
this.$refs['measurementList'].modifyMeasuredData(data) this.$refs['measurementList'].modifyMeasuredData(data)
@ -2341,7 +2386,7 @@ export default {
this.readingTaskState = 2 this.readingTaskState = 2
await store.dispatch('reading/setVisitTaskReadingTaskState', { visitTaskId: this.visitTaskId, readingTaskState: 2 }) await store.dispatch('reading/setVisitTaskReadingTaskState', { visitTaskId: this.visitTaskId, readingTaskState: 2 })
await store.dispatch('reading/setCurrentReadingTaskState', 2) await store.dispatch('reading/setCurrentReadingTaskState', 2)
const res = await getAutoCutNextTask() const res = await getAutoCutNextTask({imageToolType: 1})
var isAutoTask = res.Result.AutoCutNextTask var isAutoTask = res.Result.AutoCutNextTask
if (isAutoTask) { if (isAutoTask) {
window.location.reload() window.location.reload()
@ -2928,12 +2973,12 @@ export default {
} }
.series-table { .series-table {
::v-deep.el-table { ::v-deep .el-table {
background-color: #1e1e1e !important; background-color: #1e1e1e !important;
color: #dfdfdf; color: #dfdfdf;
} }
::v-deep.el-table td.el-table__cell, ::v-deep .el-table td.el-table__cell,
.el-table th.el-table__cell.is-leaf { .el-table th.el-table__cell.is-leaf {
border-bottom: 1px solid #dfdfdf; border-bottom: 1px solid #dfdfdf;
} }
@ -2944,14 +2989,14 @@ export default {
background-color: #1e1e1e; background-color: #1e1e1e;
} }
::v-deep.el-table__header-wrapper { ::v-deep .el-table__header-wrapper {
th { th {
background-color: #1e1e1e !important; background-color: #1e1e1e !important;
color: #dfdfdf; color: #dfdfdf;
} }
} }
::v-deep.el-table__body-wrapper { ::v-deep .el-table__body-wrapper {
tr { tr {
background-color: #1e1e1e !important; background-color: #1e1e1e !important;
color: #dfdfdf; color: #dfdfdf;
@ -2962,7 +3007,7 @@ export default {
} }
} }
::v-deep.el-table__empty-block { ::v-deep .el-table__empty-block {
background-color: #1e1e1e !important; background-color: #1e1e1e !important;
} }
} }

View File

@ -319,23 +319,23 @@ export default {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.series-table{ .series-table{
::v-deep.el-table{ ::v-deep .el-table{
background-color: #1e1e1e !important; background-color: #1e1e1e !important;
color: #dfdfdf; color: #dfdfdf;
} }
::v-deep.el-table td.el-table__cell, .el-table th.el-table__cell.is-leaf{ ::v-deep .el-table td.el-table__cell, .el-table th.el-table__cell.is-leaf{
border-bottom: 1px solid #dfdfdf; border-bottom: 1px solid #dfdfdf;
} }
.el-table--border::after, .el-table--group::after, .el-table::before{ .el-table--border::after, .el-table--group::after, .el-table::before{
background-color: #1e1e1e; background-color: #1e1e1e;
} }
::v-deep.el-table__header-wrapper{ ::v-deep .el-table__header-wrapper{
th{ th{
background-color: #1e1e1e !important; background-color: #1e1e1e !important;
color: #dfdfdf; color: #dfdfdf;
} }
} }
::v-deep.el-table__body-wrapper{ ::v-deep .el-table__body-wrapper{
tr{ tr{
background-color: #1e1e1e !important; background-color: #1e1e1e !important;
color: #dfdfdf; color: #dfdfdf;
@ -344,7 +344,7 @@ export default {
background-color: #1e1e1e !important; background-color: #1e1e1e !important;
} }
} }
::v-deep.el-table__empty-block{ ::v-deep .el-table__empty-block{
background-color: #1e1e1e !important; background-color: #1e1e1e !important;
} }
} }

View File

@ -99,6 +99,15 @@
</div> </div>
</div> </div>
</el-tooltip> </el-tooltip>
<el-tooltip v-if="isFusion" class="item" effect="dark" :content="$t('trials:reading:button:crosshairsPosition')" placement="bottom">
<div class="tool-wrapper">
<div class="icon" :class="[activeTool === 'FusionJumpToPointTool' ? 'tool_active' : '']"
@click.prevent="setFusionJumpToolActive">
<svg-icon icon-class="position" class="svg-icon" />
</div>
<div class="text">{{ $t('trials:reading:button:crosshairsPosition') }}</div>
</div>
</el-tooltip>
<!-- 椭圆oval --> <!-- 椭圆oval -->
<template v-for="tool in measuredTools"> <template v-for="tool in measuredTools">
<el-tooltip v-if="isCurrentTask && readingTaskState !== 2" :key="tool.toolName" class="item" effect="dark" <el-tooltip v-if="isCurrentTask && readingTaskState !== 2" :key="tool.toolName" class="item" effect="dark"
@ -140,46 +149,8 @@
</div> </div>
</el-tooltip> </el-tooltip>
<!-- 伪彩 --> <!-- 伪彩 -->
<el-tooltip class="item" effect="dark" :content="$t('trials:lugano:button:colormap')" placement="bottom"> <colorMap v-show="isFusion" ref="colorMap" :unit="fusionOverlayModality === 'NM' ? '' : 'g/ml'"
<div class="colorBar" style="display:flex;justify-content: flex-start;align-items: center;position: relative;" :modality="fusionOverlayModality" @setColorMap="setColorMap" @voiChange="voiChange" />
@mouseleave="isSlideMoving = false">
<div class="tool-wrapper" style="margin-right:0px" @click.stop="showColorBarPanel($event)"
@mouseleave="handleColorBarMouseout">
<div>
<!-- <canvas id="colorBarCanvas" /> -->
<div class="dropdown">
<div id="colorBar" class="icon" style="display: flex;align-items: center;width:266px">
<canvas id="colorBarCanvas" />
</div>
<!-- 伪彩 -->
<div class="text">{{ $t('trials:lugano:button:colormap') }}</div>
<div class="dropdown-content" style="width:266px">
<ul>
<li v-for="(colorMap, index) in colorMaps" :key="colorMap"
style="display: flex;align-items: center;margin-bottom:5px;padding:0 5px;justify-content: space-between;"
:class="{ activeLi: rgbPresetName === colorMap }" @click="setColorMap(colorMap)">
<canvas :id="`colorBarCanvas${index}`" />
<span style="margin-left:5px;font-size: 10px;">{{ colorMap }}</span>
</li>
</ul>
</div>
</div>
</div>
</div>
<div style="margin-left:-1px;border: 1px solid #424242;">
<el-input v-model="range" size="mini" style="width:120px" maxlength="3"
oninput="if(value){value=value.replace(/[^\d]/g,'')} if(value<=0){value=''}" @change="upperRangeChange">
<template slot="append">g/ml</template>
</el-input>
</div>
<div id="slider" style="position: absolute;left: 6px;top:5px;cursor: pointer;">
<div id="sliderBox" class="slider" style="height: 17px;width: 10px;background-color: #909399;" />
<div id="slider-position" style="color:#ddd;font-size: 12px;">0</div>
</div>
</div>
</el-tooltip>
<!-- 截屏 --> <!-- 截屏 -->
<!-- <el-tooltip class="item" effect="dark" :content="`${$t('trials:reading:button:screenShot')}`" placement="bottom"> <!-- <el-tooltip class="item" effect="dark" :content="`${$t('trials:reading:button:screenShot')}`" placement="bottom">
@ -218,17 +189,17 @@
:is-reading-show-subject-info="isReadingShowSubjectInfo" :series-info="ctSeries" :is-reading-show-subject-info="isReadingShowSubjectInfo" :series-info="ctSeries"
:rendering-engine-id="renderingEngineId" viewport-id="CT_AXIAL" :volume="ctVolume" :rendering-engine-id="renderingEngineId" viewport-id="CT_AXIAL" :volume="ctVolume"
:measure-datas="measureDatas" :style="1 === activeIndex ? viewportStyle : {}" :measure-datas="measureDatas" :style="1 === activeIndex ? viewportStyle : {}"
@upperRangeChange="upperRangeChange" /> @fusionSlicePointChange="handleFusionSlicePointChange" />
<Viewport ref="PT_AXIAL" :index="2" :active-index="activeIndex" <Viewport ref="PT_AXIAL" :index="2" :active-index="activeIndex"
:is-reading-show-subject-info="isReadingShowSubjectInfo" :series-info="petSeries" :is-reading-show-subject-info="isReadingShowSubjectInfo" :series-info="petSeries"
:rendering-engine-id="renderingEngineId" viewport-id="PT_AXIAL" :volume="ptVolume" :rendering-engine-id="renderingEngineId" viewport-id="PT_AXIAL" :volume="ptVolume"
:measure-datas="measureDatas" :style="2 === activeIndex ? viewportStyle : {}" :measure-datas="measureDatas" :style="2 === activeIndex ? viewportStyle : {}"
@upperRangeChange="upperRangeChange" /> @upperRangeChange="upperRangeChange" @fusionSlicePointChange="handleFusionSlicePointChange" />
<Viewport ref="FUSION_AXIAL" :index="3" :active-index="activeIndex" <Viewport ref="FUSION_AXIAL" :index="3" :active-index="activeIndex"
:is-reading-show-subject-info="isReadingShowSubjectInfo" :series-info="petSeries" :is-reading-show-subject-info="isReadingShowSubjectInfo" :series-info="petSeries" :ct-series-info="ctSeries"
:rendering-engine-id="renderingEngineId" viewport-id="FUSION_AXIAL" :volume="ptVolume" :rendering-engine-id="renderingEngineId" viewport-id="FUSION_AXIAL" :volume="ptVolume"
:measure-datas="measureDatas" :rgb-preset-name="rgbPresetName" :style="3 === activeIndex ? viewportStyle : {}" :measure-datas="measureDatas" :rgb-preset-name="rgbPresetName" :style="3 === activeIndex ? viewportStyle : {}"
@upperRangeChange="upperRangeChange" /> @upperRangeChange="upperRangeChange" @fusionSlicePointChange="handleFusionSlicePointChange" />
<Viewport ref="PET_MIP_CORONAL" :index="4" :active-index="activeIndex" <Viewport ref="PET_MIP_CORONAL" :index="4" :active-index="activeIndex"
:is-reading-show-subject-info="isReadingShowSubjectInfo" :series-info="petSeries" :is-reading-show-subject-info="isReadingShowSubjectInfo" :series-info="petSeries"
:rendering-engine-id="renderingEngineId" viewport-id="PET_MIP_CORONAL" :measure-datas="measureDatas" :rendering-engine-id="renderingEngineId" viewport-id="PET_MIP_CORONAL" :measure-datas="measureDatas"
@ -298,6 +269,7 @@ import CustomWwwcForm from './../CustomWwwcForm'
import FusionForm from './FusionForm.vue' import FusionForm from './FusionForm.vue'
import { getTableAnswerRowInfoList, getDicomSeriesInfo } from '@/api/trials' import { getTableAnswerRowInfoList, getDicomSeriesInfo } from '@/api/trials'
import FusionEvent from './FusionEvent' import FusionEvent from './FusionEvent'
import FusionJumpToPointTool from '../../../dicoms3D/components/tools/FusionJumpToPointTool'
// import { ColorMaps } from '@kitware/vtk.js/Common/Core/ColorMaps' // import { ColorMaps } from '@kitware/vtk.js/Common/Core/ColorMaps'
import vtkColorMaps from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps' import vtkColorMaps from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction/ColorMaps'
import vtkColorTransferFunction from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction' import vtkColorTransferFunction from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction'
@ -306,6 +278,7 @@ import vtkPiecewiseFunction from '@kitware/vtk.js/Common/DataModel/PiecewiseFunc
import { mat4, vec3 } from 'gl-matrix' import { mat4, vec3 } from 'gl-matrix'
import html2canvas from 'html2canvas' import html2canvas from 'html2canvas'
import readingChart from '@/components/readingChart' import readingChart from '@/components/readingChart'
import colorMap from '@/views/trials/trials-panel/reading/dicoms3D/components/colorMap.vue'
// import vtkColorTransferFunction from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction' // import vtkColorTransferFunction from '@kitware/vtk.js/Rendering/Core/ColorTransferFunction'
// import vtkMath from '@kitware/vtk.js/Common/Core/Math' // import vtkMath from '@kitware/vtk.js/Common/Core/Math'
// import CircleROITool from './tools/CircleROITool' // import CircleROITool from './tools/CircleROITool'
@ -320,7 +293,6 @@ const {
// StackScrollMouseWheelTool, // StackScrollMouseWheelTool,
StackScrollTool, StackScrollTool,
synchronizers, synchronizers,
MIPJumpToClickTool,
// VolumeRotateMouseWheelTool, // VolumeRotateMouseWheelTool,
VolumeRotateTool, VolumeRotateTool,
OrientationMarkerTool, OrientationMarkerTool,
@ -331,6 +303,7 @@ const {
CrosshairsTool, CrosshairsTool,
TrackballRotateTool, TrackballRotateTool,
ProbeTool, ProbeTool,
MIPJumpToClickTool,
ScaleOverlayTool, ScaleOverlayTool,
utilities, utilities,
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
@ -395,7 +368,8 @@ export default {
TableQuestions, TableQuestions,
CustomWwwcForm, CustomWwwcForm,
FusionForm, FusionForm,
readingChart readingChart,
colorMap
}, },
data() { data() {
return { return {
@ -441,6 +415,8 @@ export default {
upper: 6, upper: 6,
isSlideMoving: false, isSlideMoving: false,
viewportStyle: {}, viewportStyle: {},
isFusion: true,
fusionOverlayModality: 'PT',
defaultWwwc: [ defaultWwwc: [
{ label: this.$t('trials:reading:button:wwwcDefault'), val: -1, ww: null, wc: null }, // { label: this.$t('trials:reading:button:wwwcDefault'), val: -1, ww: null, wc: null }, //
{ label: this.$t('trials:reading:button:wwwcCustom'), val: 0, ww: null, wc: null }, // { label: this.$t('trials:reading:button:wwwcCustom'), val: 0, ww: null, wc: null }, //
@ -458,7 +434,15 @@ export default {
activeCanvasWW: null, activeCanvasWW: null,
activeCanvasWC: null, activeCanvasWC: null,
fusion: { visible: false }, // fusion: { visible: false }, //
screenshotWindow: null screenshotWindow: null,
hasVoiChanged: false,
lastUpper: null,
fusionCrosshairStyle: {
lineWidth: 2,
lineLength: 20,
centerHoleSize: 20,
},
fusionJumpEnabled: false
// initFirstAnnotation:false // initFirstAnnotation:false
} }
}, },
@ -485,9 +469,6 @@ export default {
mounted() { mounted() {
document.documentElement.style.userSelect = 'none' document.documentElement.style.userSelect = 'none'
window.addEventListener('message', this.receiveMsg) window.addEventListener('message', this.receiveMsg)
console.log(cornerstoneTools)
console.log(cornerstone3D)
this.$i18n.locale = this.$route.query.lang this.$i18n.locale = this.$route.query.lang
this.setLanguage(this.$route.query.lang) this.setLanguage(this.$route.query.lang)
this.readingTaskState = parseInt(this.$route.query.readingTaskState) this.readingTaskState = parseInt(this.$route.query.readingTaskState)
@ -513,11 +494,11 @@ export default {
this.colorMaps = getColormapNames() this.colorMaps = getColormapNames()
this.colorMaps.unshift('hsv') this.colorMaps.unshift('hsv')
this.$nextTick(() => { this.$nextTick(() => {
this.renderColorMaps() // this.renderColorMaps()
this.handleElementsClick() this.handleElementsClick()
this.initPage() this.initPage()
this.upperRangeChange(this.range) // this.upperRangeChange(this.range)
this.initSlider() // this.initSlider()
}) })
FusionEvent.$on('getAnnotations', () => { FusionEvent.$on('getAnnotations', () => {
@ -656,6 +637,7 @@ export default {
this.setUpToolGroups() this.setUpToolGroups()
this.setUpSynchronizers() this.setUpSynchronizers()
this.dispatchFusionCenterPoint()
// this.$refs['CT_AXIAL'].scroll(0) // this.$refs['CT_AXIAL'].scroll(0)
// this.$refs['PT_AXIAL'].scroll(0) // this.$refs['PT_AXIAL'].scroll(0)
// this.$refs['FUSION_AXIAL'].scroll(0) // this.$refs['FUSION_AXIAL'].scroll(0)
@ -744,6 +726,7 @@ export default {
return new Promise(resolve => { return new Promise(resolve => {
getDicomSeriesInfo({ seriesId }).then(res => { getDicomSeriesInfo({ seriesId }).then(res => {
var series = res.Result var series = res.Result
this.fusionOverlayModality = series.Modality
var imageIds = [] var imageIds = []
var instanceList = [] var instanceList = []
series.InstanceInfoList.forEach(instance => { series.InstanceInfoList.forEach(instance => {
@ -866,6 +849,9 @@ export default {
const { detail } = e const { detail } = e
const { annotation } = detail const { annotation } = detail
const { metadata } = annotation const { metadata } = annotation
if (!metadata || metadata.toolName === FusionJumpToPointTool.toolName) {
return
}
const measureData = {} const measureData = {}
measureData.frame = 0 measureData.frame = 0
measureData.data = annotation measureData.data = annotation
@ -883,6 +869,9 @@ export default {
const { detail } = e const { detail } = e
const { annotation } = detail const { annotation } = detail
const { metadata, data } = annotation const { metadata, data } = annotation
if (!metadata || metadata.toolName === FusionJumpToPointTool.toolName) {
return
}
// var i = this.initAnnotations.findIndex(i => i.orderMark === data.remark && !i.isInit) // var i = this.initAnnotations.findIndex(i => i.orderMark === data.remark && !i.isInit)
// if (i > -1) { // if (i > -1) {
// console.log(data.remark) // console.log(data.remark)
@ -1006,6 +995,7 @@ export default {
cornerstoneTools.addTool(WindowLevelTool) cornerstoneTools.addTool(WindowLevelTool)
cornerstoneTools.addTool(ZoomTool) cornerstoneTools.addTool(ZoomTool)
cornerstoneTools.addTool(StackScrollTool) cornerstoneTools.addTool(StackScrollTool)
cornerstoneTools.addTool(FusionJumpToPointTool)
cornerstoneTools.addTool(MIPJumpToClickTool) cornerstoneTools.addTool(MIPJumpToClickTool)
cornerstoneTools.addTool(VolumeRotateTool) cornerstoneTools.addTool(VolumeRotateTool)
cornerstoneTools.addTool(EllipticalROITool) cornerstoneTools.addTool(EllipticalROITool)
@ -1038,6 +1028,7 @@ export default {
toolGroup.addTool(ProbeTool.toolName) toolGroup.addTool(ProbeTool.toolName)
toolGroup.addTool(ScaleOverlayTool.toolName) toolGroup.addTool(ScaleOverlayTool.toolName)
toolGroup.addTool(OrientationMarkerTool.toolName) toolGroup.addTool(OrientationMarkerTool.toolName)
toolGroup.addTool(FusionJumpToPointTool.toolName, this.getFusionJumpToolConfiguration())
}) })
fusionToolGroup.addTool(PanTool.toolName) fusionToolGroup.addTool(PanTool.toolName)
@ -1053,11 +1044,12 @@ export default {
fusionToolGroup.addTool(ProbeTool.toolName) fusionToolGroup.addTool(ProbeTool.toolName)
fusionToolGroup.addTool(ScaleOverlayTool.toolName) fusionToolGroup.addTool(ScaleOverlayTool.toolName)
fusionToolGroup.addTool(OrientationMarkerTool.toolName) fusionToolGroup.addTool(OrientationMarkerTool.toolName)
fusionToolGroup.addTool(FusionJumpToPointTool.toolName, this.getFusionJumpToolConfiguration())
// Here is the difference in the toolGroups used, that we need to specify the // Here is the difference in the toolGroups used, that we need to specify the
// volume to use for the WindowLevelTool for the fusion viewports // volume to use for the WindowLevelTool for the fusion viewports
fusionToolGroup.addTool(WindowLevelTool.toolName, { fusionToolGroup.addTool(WindowLevelTool.toolName, {
volumeId: ptVolumeId volumeId: ptVolumeId2
}); });
[ctToolGroup, ptToolGroup, fusionToolGroup].forEach((toolGroup) => { [ctToolGroup, ptToolGroup, fusionToolGroup].forEach((toolGroup) => {
@ -1104,6 +1096,7 @@ export default {
true // overwrite true // overwrite
) )
toolGroup.setToolEnabled(ScaleOverlayTool.toolName) toolGroup.setToolEnabled(ScaleOverlayTool.toolName)
// toolGroup.setToolConfiguration(OrientationMarkerTool.toolName, { // toolGroup.setToolConfiguration(OrientationMarkerTool.toolName, {
// orientationWidget: { // orientationWidget: {
// enabled: true, // enabled: true,
@ -1155,26 +1148,215 @@ export default {
mipToolGroup.setToolActive(VolumeRotateTool.toolName, { mipToolGroup.setToolActive(VolumeRotateTool.toolName, {
bindings: [{ mouseButton: MouseBindings.Wheel }], bindings: [{ mouseButton: MouseBindings.Wheel }],
}); });
mipToolGroup.addTool('MIPJumpToClickTool', { mipToolGroup.addTool(PanTool.toolName)
// mipToolGroup.addTool(ZoomTool.toolName)
toolGroupId: ptToolGroupId mipToolGroup.setToolActive(PanTool.toolName, {
bindings: [{ mouseButton: MouseBindings.Auxiliary }]
})
mipToolGroup.setToolActive(ZoomTool.toolName, {
bindings: [{ mouseButton: MouseBindings.Secondary }]
})
mipToolGroup.addTool(FusionJumpToPointTool.toolName, this.getFusionJumpToolConfiguration())
mipToolGroup.addTool(MIPJumpToClickTool.toolName, {
targetViewportIds: this.getFusionJumpToolConfiguration().targetViewportIds
}) })
// Set the initial state of the tools, here we set one tool active on left click. // Set the initial state of the tools, here we set one tool active on left click.
// This means left click will draw that tool. // This means left click will draw that tool.
mipToolGroup.setToolActive('MIPJumpToClickTool', {
bindings: [
{
mouseButton: MouseBindings.Primary // Left ClickR
}
]
})
// As the Stack Scroll mouse wheel is a tool using the `mouseWheelCallback` // As the Stack Scroll mouse wheel is a tool using the `mouseWheelCallback`
// hook instead of mouse buttons, it does not need to assign any mouse button. // hook instead of mouse buttons, it does not need to assign any mouse button.
// mipToolGroup.setToolActive('VolumeRotateMouseWheel') // mipToolGroup.setToolActive('VolumeRotateMouseWheel')
// mipToolGroup.addTool(OrientationMarkerTool.toolName) // mipToolGroup.addTool(OrientationMarkerTool.toolName)
// mipToolGroup.setToolActive(OrientationMarkerTool.toolName) // mipToolGroup.setToolActive(OrientationMarkerTool.toolName)
mipToolGroup.addViewport(viewportIds.PETMIP.CORONAL, renderingEngineId) mipToolGroup.addViewport(viewportIds.PETMIP.CORONAL, renderingEngineId)
this.setFusionMipClickEnabled(!this.fusionJumpEnabled && this.isFusion)
},
getFusionJumpToolConfiguration() {
return {
targetViewportIds: [
viewportIds.CT.AXIAL,
viewportIds.PT.AXIAL,
viewportIds.FUSION.AXIAL,
viewportIds.PETMIP.CORONAL
],
useBrightestPoint: true,
jumpToTargetViewports: true,
dispatchEventName: 'fusion-mip-point-selected',
getReferenceLineColor: this.setFusionCrosshairsToolLineColor,
style: this.fusionCrosshairStyle,
referenceLinesCenterGapRadius: this.fusionCrosshairStyle.centerHoleSize,
minimal: {
enabled: true,
lineLengthInPx: this.fusionCrosshairStyle.lineLength,
},
mipViewportIds: [viewportIds.PETMIP.CORONAL]
}
},
setFusionCrosshairsToolLineColor(viewportId) {
const colors = {
[viewportIds.CT.AXIAL]: '#0000ff',
[viewportIds.PT.AXIAL]: '#0000ff',
[viewportIds.FUSION.AXIAL]: '#0000ff',
[viewportIds.PETMIP.CORONAL]: '#ff0000'
}
return colors[viewportId] || '#0000ff'
},
handleFusionSlicePointChange({ viewportId, worldPoint }) {
if (!this.fusionJumpEnabled) return
if (!this.isFusion) return
if (!viewportId || !Array.isArray(worldPoint) || worldPoint.length < 3) return
if (viewportId === viewportIds.PETMIP.CORONAL) return
const activeViewportId = this.activeIndex === 1
? viewportIds.CT.AXIAL
: this.activeIndex === 2
? viewportIds.PT.AXIAL
: this.activeIndex === 3
? viewportIds.FUSION.AXIAL
: viewportIds.PETMIP.CORONAL
if (viewportId !== activeViewportId) return
const renderEngine = getRenderingEngine(renderingEngineId)
if (!renderEngine) return
const toolGroupCandidates = [fusionToolGroupId, mipToolGroupUID, ptToolGroupId, ctToolGroupId]
let instance = null
for (const toolGroupId of toolGroupCandidates) {
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
if (!toolGroup || !toolGroup.getToolInstance) continue
instance = toolGroup.getToolInstance(FusionJumpToPointTool.toolName)
if (instance && instance.setPoint) break
}
if (!instance || !instance.setPoint) return
instance.setPoint(worldPoint, viewportId, renderEngine.id, {
jumpToTargetViewports: false,
dispatchEvent: false,
})
},
dispatchFusionCenterPoint(retryCount = 0) {
if (!this.fusionJumpEnabled) return
const renderEngine = getRenderingEngine(renderingEngineId)
if (!renderEngine) return
const toolGroupCandidates = [fusionToolGroupId, mipToolGroupUID, ptToolGroupId, ctToolGroupId]
let instance = null
for (const toolGroupId of toolGroupCandidates) {
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
if (!toolGroup || !toolGroup.getToolInstance) continue
instance = toolGroup.getToolInstance(FusionJumpToPointTool.toolName)
if (instance && instance.setPoint) break
}
if (!instance || !instance.setPoint) {
if (retryCount < 10) {
setTimeout(() => {
this.dispatchFusionCenterPoint(retryCount + 1)
}, 120)
}
return
}
const candidates = [viewportIds.FUSION.AXIAL, viewportIds.PT.AXIAL, viewportIds.CT.AXIAL]
for (const viewportId of candidates) {
const viewport = renderEngine.getViewport(viewportId)
if (!viewport) continue
const width = viewport.element ? viewport.element.clientWidth : null
const height = viewport.element ? viewport.element.clientHeight : null
let worldPoint = null
if (width && height && viewport.canvasToWorld) {
worldPoint = viewport.canvasToWorld([width / 2, height / 2])
}
if ((!worldPoint || worldPoint.length < 3) && viewport.getCamera) {
const camera = viewport.getCamera()
if (camera && camera.focalPoint) {
worldPoint = camera.focalPoint
}
}
if (!worldPoint || worldPoint.length < 3) continue
instance.setPoint(worldPoint, viewportId, renderEngine.id, {
jumpToTargetViewports: true,
dispatchEvent: false,
})
return
}
if (retryCount < 10) {
setTimeout(() => {
this.dispatchFusionCenterPoint(retryCount + 1)
}, 120)
}
},
setFusionJumpToolActive() {
if (this.activeTool === FusionJumpToPointTool.toolName) {
this.setFusionJumpEnabled(false)
this.activeTool = ''
return
}
if (this.activeTool) {
this.setToolMode('passive', this.activeTool)
}
this.setFusionJumpEnabled(true)
this.activeTool = FusionJumpToPointTool.toolName
},
setFusionMipClickEnabled(enabled) {
const toolGroup = ToolGroupManager.getToolGroup(mipToolGroupUID)
if (!toolGroup || !toolGroup.hasTool(MIPJumpToClickTool.toolName)) return
if (enabled && this.isFusion) {
toolGroup.setToolActive(MIPJumpToClickTool.toolName, {
bindings: [{ mouseButton: MouseBindings.Primary }]
})
} else {
toolGroup.setToolDisabled(MIPJumpToClickTool.toolName)
}
},
setFusionJumpEnabled(enabled) {
this.fusionJumpEnabled = !!enabled
const toolGroupIds = [ctToolGroupId, ptToolGroupId, fusionToolGroupId, mipToolGroupUID]
if (enabled) {
this.setFusionMipClickEnabled(false)
toolGroupIds.forEach((toolGroupId) => {
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
if (!toolGroup || !toolGroup.hasTool(FusionJumpToPointTool.toolName)) return
toolGroup.setToolActive(FusionJumpToPointTool.toolName, {
bindings: [{ mouseButton: MouseBindings.Primary }]
})
})
this.dispatchFusionCenterPoint()
return
}
this.setFusionMipClickEnabled(this.isFusion)
toolGroupIds.forEach((toolGroupId) => {
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
if (!toolGroup || !toolGroup.hasTool(FusionJumpToPointTool.toolName)) return
toolGroup.setToolDisabled(FusionJumpToPointTool.toolName)
})
this.clearFusionJumpToPointAnnotations()
},
clearFusionJumpToPointAnnotations() {
const annotations = cornerstoneTools.annotation.state.getAllAnnotations() || []
const removeList = []
for (let i = 0; i < annotations.length; i++) {
const item = annotations[i]
if (!item || !item.metadata) continue
if (item.metadata.toolName !== FusionJumpToPointTool.toolName) continue
removeList.push(item)
}
if (!removeList.length) return
removeList.forEach(i => {
cornerstoneTools.annotation.state.removeAnnotation(i.annotationUID)
})
const renderEngine = getRenderingEngine(renderingEngineId)
if (!renderEngine) return
const viewportIdsToRender = [
viewportIds.CT.AXIAL,
viewportIds.PT.AXIAL,
viewportIds.FUSION.AXIAL,
viewportIds.PETMIP.CORONAL
]
viewportIdsToRender.forEach(viewportId => {
const viewport = renderEngine.getViewport(viewportId)
if (viewport && viewport.render) {
viewport.render()
}
})
}, },
getTextLines(data, targetId) { getTextLines(data, targetId) {
const cachedVolumeStats = data.cachedStats[targetId] const cachedVolumeStats = data.cachedStats[targetId]
@ -1238,15 +1420,23 @@ export default {
}, },
// () // ()
setBasicToolActive(toolName) { setBasicToolActive(toolName) {
var toolGroupIds = [ctToolGroupId, ptToolGroupId, fusionToolGroupId] var toolGroupIds = [ctToolGroupId, ptToolGroupId, fusionToolGroupId, mipToolGroupUID]
if (toolName !== FusionJumpToPointTool.toolName && this.fusionJumpEnabled) {
this.setFusionJumpEnabled(false)
}
toolGroupIds.forEach((toolGroupId) => { toolGroupIds.forEach((toolGroupId) => {
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId) const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
if (!toolGroup || !toolGroup.hasTool(toolName)) return
if (this.activeTool === toolName) { if (this.activeTool === toolName) {
if (toolGroup.hasTool(toolName)) {
toolGroup.setToolPassive(toolName) toolGroup.setToolPassive(toolName)
}
} else { } else {
if (this.activeTool) { if (this.activeTool) {
if (toolGroup.hasTool(this.activeTool)) {
toolGroup.setToolPassive(this.activeTool) toolGroup.setToolPassive(this.activeTool)
} }
}
var bindings = [] var bindings = []
if (toolName === 'Pan') { if (toolName === 'Pan') {
bindings = [ bindings = [
@ -1328,9 +1518,10 @@ export default {
} }
}, },
setToolMode(mode, toolName) { setToolMode(mode, toolName) {
var toolGroupIds = [ctToolGroupId, ptToolGroupId, fusionToolGroupId] var toolGroupIds = [ctToolGroupId, ptToolGroupId, fusionToolGroupId, mipToolGroupUID]
toolGroupIds.forEach((toolGroupId) => { toolGroupIds.forEach((toolGroupId) => {
const toolGroup = ToolGroupManager.getToolGroup(toolGroupId) const toolGroup = ToolGroupManager.getToolGroup(toolGroupId)
if (!toolGroup || !toolGroup.hasTool(toolName)) return
toolGroup.setToolPassive(toolName) toolGroup.setToolPassive(toolName)
if (mode === 'enabled') { if (mode === 'enabled') {
toolGroup.setToolEnabled(toolName) toolGroup.setToolEnabled(toolName)
@ -1627,148 +1818,141 @@ export default {
var slider = document.getElementById('slider') var slider = document.getElementById('slider')
var sliderBox = document.getElementById('sliderBox') var sliderBox = document.getElementById('sliderBox')
var container = document.getElementById('colorBarCanvas') var container = document.getElementById('colorBarCanvas')
slider.addEventListener('mousedown', () => {
if (!slider || !sliderBox || !container) return
slider.addEventListener('mousedown', (e) => {
this.isSlideMoving = true this.isSlideMoving = true
e.stopPropagation()
// document.onselectstart = function() { return false }// // document.onselectstart = function() { return false }//
// document.ondragstart = function() { return false } // document.ondragstart = function() { return false }
}) })
document.addEventListener('mousemove', (e) => { document.addEventListener('mousemove', (e) => {
if (this.isSlideMoving) { if (this.isSlideMoving) {
var containerWidth = container.clientWidth var colorBarContainer = document.getElementById('colorBar')
if (!colorBarContainer) return
var containerWidth = colorBarContainer.clientWidth
var sliderWidth = sliderBox.clientWidth var sliderWidth = sliderBox.clientWidth
var maxLeft = containerWidth - sliderWidth var maxLeft = containerWidth - sliderWidth
var left = e.clientX - container.getBoundingClientRect().left
var position = null //
position = left var rect = colorBarContainer.getBoundingClientRect()
var left = e.clientX - rect.left - (sliderWidth / 2)
if (left < 0) { if (left < 0) {
left = 6 left = 0
position = 0
} else if (left > maxLeft) { } else if (left > maxLeft) {
left = maxLeft + 6 left = maxLeft
position = maxLeft
} }
slider.style.left = left + 'px' slider.style.left = left + 'px'
var positionValue = document.getElementById('slider-position') var positionValue = document.getElementById('slider-position')
var upper = this.range var upper = this.range
position = parseInt((position / maxLeft) * upper) var position = parseInt((left / maxLeft) * upper)
if (positionValue) {
positionValue.textContent = position positionValue.textContent = position
}
this.upper = position this.upper = position
if (this.sliderTimeout) {
cancelAnimationFrame(this.sliderTimeout)
}
this.sliderTimeout = requestAnimationFrame(() => {
this.voiChange(position) this.voiChange(position)
})
} }
}) })
document.addEventListener('mouseup', () => { document.addEventListener('mouseup', (e) => {
if (this.isSlideMoving) {
this.isSlideMoving = false this.isSlideMoving = false
if (this.sliderTimeout) {
cancelAnimationFrame(this.sliderTimeout)
}
this.voiChange(this.upper)
}
// document.onselectstart = null // document.onselectstart = null
// document.ondragstart = null // document.ondragstart = null
}) })
}, },
upperRangeChange(v) {
if (v === 0 || v < this.upper) {
return
}
var sliderBox = document.getElementById('sliderBox')
var container = document.getElementById('colorBarCanvas')
var containerWidth = container.clientWidth
var sliderWidth = sliderBox.clientWidth
var maxLeft = containerWidth - sliderWidth
var left = (this.upper / this.range) * maxLeft
if (left < 0) {
left = 6
} else if (left >= maxLeft) {
left = maxLeft + 6
}
var slider = document.getElementById('slider')
slider.style.left = left + 'px'
var positionValue = document.getElementById('slider-position')
positionValue.textContent = this.upper
},
renderColorMaps() {
this.createColorBar(this.rgbPresetName, 'colorBarCanvas', 256, 15)
this.$refs['FUSION_AXIAL'].setPreset(this.rgbPresetName)
this.colorMaps.forEach((e, index) => {
this.createColorBar(e, `colorBarCanvas${index}`, 180, 15)
})
},
getVOIRange(viewportId, volumeId) {
const defaultImageRange = { lower: -1000, upper: 1000 }
const viewport = (
renderingEngine.getViewport(viewportId)
)
const volumeActor = volumeId
? viewport.getActor(volumeId)
: viewport.getDefaultActor()
if (!volumeActor || !csUtils.isImageActor(volumeActor)) {
return defaultImageRange
}
const voiRange = (volumeActor.actor)
.getProperty()
.getRGBTransferFunction(0)
.getRange()
return voiRange[0] === 0 && voiRange[1] === 0
? defaultImageRange
: { lower: voiRange[0], upper: voiRange[1] }
},
voiChange(v) { voiChange(v) {
// console.log(this.lastUpper, this.hasVoiChanged, v)
if (this.lastUpper === v && this.hasVoiChanged) return;
this.lastUpper = v;
this.hasVoiChanged = true;
let viewportIds = ['FUSION_AXIAL', 'PT_AXIAL', 'PET_MIP_CORONAL'] let viewportIds = ['FUSION_AXIAL', 'PT_AXIAL', 'PET_MIP_CORONAL']
viewportIds.map(viewportId => { viewportIds.map(viewportId => {
// const volumeId = viewportId === 'viewportId' ? ptVolumeId : ctVolumeId // const volumeId = viewportId === 'viewportId' ? ptVolumeId : ctVolumeId
const volumeId = ptVolumeId const volumeId = viewportId === 'FUSION_AXIAL' ? ptVolumeId2 : ptVolumeId
const voiRange = { lower: 0, upper: v } const voiRange = { lower: 0, upper: v }
const viewport = ( const viewport = (
renderingEngine.getViewport(viewportId) renderingEngine.getViewport(viewportId)
) )
if (!viewport) return if (!viewport) return
// const volumeId = viewport.getVolumeId()
// const viewportsContainingVolumeUID = csUtils.getViewportsWithVolumeId(
// volumeId,
// viewport.renderingEngineId
// )
viewport.setProperties({ voiRange }, volumeId) viewport.setProperties({ voiRange }, volumeId)
viewport.render() viewport.render()
this.$refs[viewport.id].setWwWc() this.$refs[viewport.id] && this.$refs[viewport.id].setWwWc()
// viewportsContainingVolumeUID.forEach((vp) => {
// vp.render()
// this.$refs[vp.id].setWwWc()
// console.log(vp.id)
// })
}) })
// colorMap
if (this.$refs.colorMap) {
this.$refs.colorMap.changeVoi(v)
}
}, },
// async setColorMap(rgbPresetName) {
// this.rgbPresetName = rgbPresetName
// if (this.$refs.FUSION_AXIAL) {
// this.$refs.FUSION_AXIAL.setPreset(this.rgbPresetName)
// this.$refs.FUSION_AXIAL.renderColorBar(this.rgbPresetName)
// }
// const viewport = getRenderingEngine(renderingEngineId)?.getViewport('FUSION_AXIAL')
// if (!viewport) return
// const actorEntry = viewport.getActors()?.find(e => String(e.uid || '').includes('PT_VOLUME_ID2'))
// if (!actorEntry?.actor) return
// const preset = rgbPresetName === 'hsv' ? vtkColorMaps.getPresetByName(rgbPresetName) : getColormap(rgbPresetName)
// const cfun = vtkColorTransferFunction.newInstance()
// const rgbPoints = preset?.RGBPoints
// if (Array.isArray(rgbPoints)) {
// for (let i = 0; i < rgbPoints.length; i += 4) {
// cfun.addRGBPoint(rgbPoints[i], rgbPoints[i + 1], rgbPoints[i + 2], rgbPoints[i + 3])
// }
// }
// cfun.setMappingRange(0, 5)
// actorEntry.actor.getProperty().setRGBTransferFunction(0, cfun)
// const ofun = vtkPiecewiseFunction.newInstance()
// ofun.addPoint(0, 0.0)
// ofun.addPoint(0.1, 0.9)
// ofun.addPoint(5, 1.0)
// actorEntry.actor.getProperty().setScalarOpacity(0, ofun)
// viewport.render()
// },
async setColorMap(rgbPresetName) { async setColorMap(rgbPresetName) {
this.rgbPresetName = rgbPresetName this.rgbPresetName = rgbPresetName
let viewports = ['FUSION_AXIAL', 'PT_AXIAL', 'PET_MIP_CORONAL'] let viewports = ['FUSION_AXIAL']
viewports.map(v => { viewports.map(v => {
this.$refs[v].setPreset(this.rgbPresetName) this.$refs[v] && this.$refs[v].setPreset(this.rgbPresetName)
this.$refs[v].renderColorBar(this.rgbPresetName) this.$refs[v] && this.$refs[v].setColorMap(rgbPresetName)
this.createColorBar(this.rgbPresetName, 'colorBarCanvas', 256, 15)
const renderingEngine = getRenderingEngine(renderingEngineId)
const viewport = (
renderingEngine.getViewport(v)
)
viewport.setProperties({ colormap: { name: rgbPresetName } }, ptVolumeId)
viewport.render()
}) })
// this.$refs['FUSION_AXIAL'].setPreset(this.rgbPresetName) },
// this.$refs['FUSION_AXIAL'].renderColorBar(this.rgbPresetName) upperRangeChange(v) {
// this.createColorBar(this.rgbPresetName, 'colorBarCanvas', 256, 15) if (this.upper === v) return
// const renderingEngine = getRenderingEngine(renderingEngineId) this.upper = v;
// const viewport = ( if (v === 0) {
// renderingEngine.getViewport(viewportIds.FUSION.AXIAL) return
// ) }
this.voiChange(v)
// viewport.setProperties({ colormap: { name: rgbPresetName } }, ptVolumeId) // colorMap
// // viewport.setProperties({ colormap: { name: rgbPresetName }}, ctVolumeId) if (this.$refs.colorMap) {
// viewport.render() this.$refs.colorMap.changeVoi(v)
// document.onselectstart = function() { return false }// }
// document.ondragstart = function() { return false }
}, },
setPetColorMap(volumeInfo) { setPetColorMap(volumeInfo) {
const { volumeActor } = volumeInfo const { volumeActor } = volumeInfo
@ -1979,17 +2163,24 @@ export default {
// document.onselectstart = function() { return false }// // document.onselectstart = function() { return false }//
// document.ondragstart = function() { return false } // document.ondragstart = function() { return false }
}, },
setWwwc(v) { // setWwwc(v) {
// this.changeMapperRange(v.wc, v.ww) // // this.changeMapperRange(v.wc, v.ww)
const renderingEngine = getRenderingEngine(renderingEngineId) // const renderingEngine = getRenderingEngine(renderingEngineId)
let viewportId = this.activeIndex === 1 ? 'CT_AXIAL' : this.activeIndex === 2 ? 'PT_AXIAL' : this.activeIndex === 3 ? 'FUSION_AXIAL' : 'PET_MIP_CORONAL' // let viewportId = this.activeIndex === 1 ? 'CT_AXIAL' : this.activeIndex === 2 ? 'PT_AXIAL' : this.activeIndex === 3 ? 'FUSION_AXIAL' : 'PET_MIP_CORONAL'
const viewport = renderingEngine.getViewport(viewportId) // const viewport = renderingEngine.getViewport(viewportId)
const lower = v.wc - v.ww / 2 // const lower = v.wc - v.ww / 2
const upper = v.wc + v.ww / 2 - 1 // const upper = v.wc + v.ww / 2 - 1
viewport.setProperties({ voiRange: { upper: upper, lower: lower } }) // viewport.setProperties({ voiRange: { upper: upper, lower: lower } })
viewport.render() // viewport.render()
this.customWwc.visible = false // if (viewportId !== 'CT_AXIAL') {
}, // const nextUpper = Math.round(upper)
// this.upper = nextUpper
// if (this.$refs.colorMap) {
// this.$refs.colorMap.changeVoi(nextUpper)
// }
// }
// this.customWwc.visible = false
// },
changeMapperRange(wc, ww) { changeMapperRange(wc, ww) {
var lower = wc - ww / 2.0 var lower = wc - ww / 2.0
var upper = wc + ww / 2.0 var upper = wc + ww / 2.0
@ -2262,7 +2453,7 @@ export default {
background-color: #000; background-color: #000;
padding: 5px 2px; padding: 5px 2px;
::v-deep.el-dialog { ::v-deep .el-dialog {
background: #1e1e1e; background: #1e1e1e;
border: 1px solid #ddd; border: 1px solid #ddd;
color: #ddd; color: #ddd;

View File

@ -172,6 +172,13 @@ export default {
beforeDestroy() { beforeDestroy() {
}, },
methods: { methods: {
getQuestionFormRef(refName) {
const ref = this.$refs[refName]
if (!ref) {
return null
}
return Array.isArray(ref) ? ref[0] : ref
},
handleReadingChart(e) { handleReadingChart(e) {
this.$emit('handleReadingChart', e) this.$emit('handleReadingChart', e)
}, },
@ -191,7 +198,8 @@ export default {
this.tableQuestions.forEach(item => { this.tableQuestions.forEach(item => {
item.TableQuestions.Answers.forEach(i => { item.TableQuestions.Answers.forEach(i => {
var refName = `${item.Id}_${i.RowIndex}` var refName = `${item.Id}_${i.RowIndex}`
this.$refs[refName] && this.$refs[refName][0].initForm(isRerender) const questionFormRef = this.getQuestionFormRef(refName)
questionFormRef && questionFormRef.initForm(isRerender)
}) })
}) })
}) })
@ -271,7 +279,8 @@ export default {
this.tableQuestions.forEach(item => { this.tableQuestions.forEach(item => {
item.TableQuestions.Answers.forEach(i => { item.TableQuestions.Answers.forEach(i => {
var refName = `${item.Id}_${i.RowIndex}` var refName = `${item.Id}_${i.RowIndex}`
this.$refs[refName] && this.$refs[refName][0].initForm() const questionFormRef = this.getQuestionFormRef(refName)
questionFormRef && questionFormRef.initForm()
}) })
}) })
}) })
@ -379,7 +388,8 @@ export default {
this.tableQuestions.forEach(item => { this.tableQuestions.forEach(item => {
item.TableQuestions.Answers.forEach(i => { item.TableQuestions.Answers.forEach(i => {
var refName = `${item.Id}_${i.RowIndex}` var refName = `${item.Id}_${i.RowIndex}`
this.$refs[refName] && this.$refs[refName][0].initForm() const questionFormRef = this.getQuestionFormRef(refName)
questionFormRef && questionFormRef.initForm()
}) })
}) })
}) })
@ -391,7 +401,8 @@ export default {
var rowIndex = String(this.unSaveTargets[0].rowIndex) var rowIndex = String(this.unSaveTargets[0].rowIndex)
var questionId = this.unSaveTargets[0].questionId var questionId = this.unSaveTargets[0].questionId
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}` const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
if (rowIndex === this.activeItem.activeRowIndex && questionId === this.activeItem.activeCollapseId && !this.$refs[refName][0].questionForm.OtherMeasureData) { const questionFormRef = this.getQuestionFormRef(refName)
if (rowIndex === this.activeItem.activeRowIndex && questionId === this.activeItem.activeCollapseId && questionFormRef && !questionFormRef.questionForm.OtherMeasureData) {
if (toolName === 'CircleROI') { if (toolName === 'CircleROI') {
return { isCanActiveTool: true, reason: '' } return { isCanActiveTool: true, reason: '' }
} else { } else {
@ -522,7 +533,8 @@ export default {
if (item.TableQuestions && item.TableQuestions.Answers) { if (item.TableQuestions && item.TableQuestions.Answers) {
item.TableQuestions.Answers.map(t => { item.TableQuestions.Answers.map(t => {
const refName = `${item.Id}_${t.RowIndex}` const refName = `${item.Id}_${t.RowIndex}`
if (this.$refs[refName] && this.$refs[refName][0] && this.$refs[refName][0].questionForm.saveTypeEnum !== 2) { const questionFormRef = this.getQuestionFormRef(refName)
if (questionFormRef && questionFormRef.questionForm.saveTypeEnum !== 2) {
var lessionName = this.getLesionName(item.OrderMark, t.RowIndex) var lessionName = this.getLesionName(item.OrderMark, t.RowIndex)
arr.push({ lessionName: lessionName, rowIndex: t.RowIndex, questionId: item.Id }) arr.push({ lessionName: lessionName, rowIndex: t.RowIndex, questionId: item.Id })
} }
@ -552,11 +564,15 @@ export default {
this.activeItem.activeCollapseId = arr[0] this.activeItem.activeCollapseId = arr[0]
this.$nextTick(() => { this.$nextTick(() => {
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}` const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
if (this.$refs[refName][0].questionForm.IsDicomReading !== false) { const questionFormRef = this.getQuestionFormRef(refName)
var markTool = 'CircleROI' if (!questionFormRef) {
var isMarked = !!this.$refs[refName][0].questionForm.OtherMeasureData return
} }
FusionEvent.$emit('imageLocation', { otherMeasureData: this.$refs[refName][0].questionForm.OtherMeasureData, markTool, isMarked }) if (questionFormRef.questionForm.IsDicomReading !== false) {
var markTool = 'CircleROI'
var isMarked = !!questionFormRef.questionForm.OtherMeasureData
}
FusionEvent.$emit('imageLocation', { otherMeasureData: questionFormRef.questionForm.OtherMeasureData, markTool, isMarked })
}) })
} else { } else {
this.activeItem.activeRowIndex = null this.activeItem.activeRowIndex = null
@ -576,7 +592,10 @@ export default {
}, },
collapseRightClick(e, obj, activeCollapseId, activeRowIndex) { collapseRightClick(e, obj, activeCollapseId, activeRowIndex) {
const refName = `${activeCollapseId}_${activeRowIndex}` const refName = `${activeCollapseId}_${activeRowIndex}`
FusionEvent.$emit('imageLocation', { otherMeasureData: this.$refs[refName][0].questionForm.OtherMeasureData }) const questionFormRef = this.getQuestionFormRef(refName)
if (questionFormRef) {
FusionEvent.$emit('imageLocation', { otherMeasureData: questionFormRef.questionForm.OtherMeasureData })
}
e.stopImmediatePropagation() e.stopImmediatePropagation()
e.stopPropagation() e.stopPropagation()
@ -621,8 +640,12 @@ export default {
this.activeName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}` this.activeName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}` const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
this.$nextTick(() => { this.$nextTick(() => {
const questionFormRef = this.getQuestionFormRef(refName)
if (!questionFormRef) {
return
}
if (deleteInfo) { if (deleteInfo) {
this.$refs[refName][0].setDeleteInfo(deleteInfo) questionFormRef.setDeleteInfo(deleteInfo)
} }
if (questionsObj.otherMeasureData) { if (questionsObj.otherMeasureData) {
const measureData = {} const measureData = {}
@ -631,7 +654,7 @@ export default {
measureData.type = questionsObj.otherMeasureData.metadata.toolName measureData.type = questionsObj.otherMeasureData.metadata.toolName
measureData.suvMax = questionsObj.suvMax measureData.suvMax = questionsObj.suvMax
this.$refs[refName][0].setMeasureData(measureData) questionFormRef.setMeasureData(measureData)
} }
}) })
}, },
@ -719,21 +742,28 @@ export default {
this.activeItem.activeRowIndex = String(measureObj.questionInfo.RowIndex) this.activeItem.activeRowIndex = String(measureObj.questionInfo.RowIndex)
this.activeName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}` this.activeName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}` const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
if (this.$refs[refName]) { const questionFormRef = this.getQuestionFormRef(refName)
this.$refs[refName][0].setMeasureData(measureObj.measureData) if (questionFormRef) {
questionFormRef.setMeasureData(measureObj.measureData)
} }
} }
}, },
clearMeasuredData() { clearMeasuredData() {
if (this.activeItem.activeCollapseId && this.activeItem.activeRowIndex && this.activeName) { if (this.activeItem.activeCollapseId && this.activeItem.activeRowIndex && this.activeName) {
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}` const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
this.$refs[refName][0].clearMeasurement() const questionFormRef = this.getQuestionFormRef(refName)
if (questionFormRef) {
questionFormRef.clearMeasurement()
}
} }
}, },
setOutsideMeasuredData(annotation) { setOutsideMeasuredData(annotation) {
if (this.activeItem.activeCollapseId && this.activeItem.activeRowIndex && this.activeName) { if (this.activeItem.activeCollapseId && this.activeItem.activeRowIndex && this.activeName) {
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}` const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
this.$refs[refName][0].setOutsideMeasuredData(annotation) const questionFormRef = this.getQuestionFormRef(refName)
if (questionFormRef) {
questionFormRef.setOutsideMeasuredData(annotation)
}
} }
}, },
// //
@ -779,8 +809,22 @@ export default {
// //
this.$nextTick(() => { this.$nextTick(() => {
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}` const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
if (!this.$refs[refName][0].questionForm.OtherMeasureData) { const questionFormRef = this.getQuestionFormRef(refName)
this.$refs[refName][0].setMeasureData(measureData) if (!questionFormRef) {
if (this.isBaseLineTask) {
var idx = this.tableQuestions.findIndex(item => item.LesionType === 0)
if (this.tableQuestions[idx].TableQuestions.Answers.length < this.tableQuestions[idx].MaxQuestionCount && (measureData.type === 'CircleROI')) {
this.createTTarget(measureData)
} else {
this.createNTTarget(measureData)
}
} else {
this.createNLTarget(measureData)
}
return
}
if (!questionFormRef.questionForm.OtherMeasureData) {
questionFormRef.setMeasureData(measureData)
} else { } else {
if (this.isBaseLineTask) { if (this.isBaseLineTask) {
var idx = this.tableQuestions.findIndex(item => item.LesionType === 0) var idx = this.tableQuestions.findIndex(item => item.LesionType === 0)

View File

@ -19,6 +19,8 @@
</div> </div>
<div v-if="index !== 4">Image: #{{ `${seriesInfo.imageIdIndex + 1} / ${seriesInfo.imageMaxLength}` }}</div> <div v-if="index !== 4">Image: #{{ `${seriesInfo.imageIdIndex + 1} / ${seriesInfo.imageMaxLength}` }}</div>
<div v-if="index === 1 || index === 2">{{ seriesInfo.modality }}</div> <div v-if="index === 1 || index === 2">{{ seriesInfo.modality }}</div>
<div v-if="index === 3">{{ ctSeriesInfo ? ctSeriesInfo.modality + ' / ' + seriesInfo.modality : seriesInfo.modality }}</div>
<div v-if="index === 4">MIP</div>
</div> </div>
<!-- 描述信息 --> <!-- 描述信息 -->
<div <div
@ -79,7 +81,7 @@
{{ markers.left }} {{ markers.left }}
</div> </div>
<div v-if="presetName" class="color_bar"> <div v-if="presetName" class="color_bar">
<canvas id="colorBar_Canvas" /> <canvas ref="colorBarCanvas" />
</div> </div>
<div v-if="index === 4" id="rotateBar" ref="rotateBar" class="rotate_slider_box" @click.stop="clickRotate($event)"> <div v-if="index === 4" id="rotateBar" ref="rotateBar" class="rotate_slider_box" @click.stop="clickRotate($event)">
<div id="rotateSlider" :style="{left: rotateBarLeft + 'px'}" class="box" @click.stop.prevent="() => {return}" @mousedown.stop="rotateBarMousedown($event)" /> <div id="rotateSlider" :style="{left: rotateBarLeft + 'px'}" class="box" @click.stop.prevent="() => {return}" @mousedown.stop="rotateBarMousedown($event)" />
@ -135,6 +137,12 @@ export default {
return {} return {}
} }
}, },
ctSeriesInfo: {
type: Object,
default() {
return null
}
},
renderingEngineId: { renderingEngineId: {
type: String, type: String,
required: true required: true
@ -154,6 +162,10 @@ export default {
default() { default() {
return [] return []
} }
},
rgbPresetName: {
type: String,
default: ''
} }
}, },
data() { data() {
@ -195,12 +207,21 @@ export default {
handler(v) { handler(v) {
console.log('activeIndex ', v) console.log('activeIndex ', v)
} }
},
rgbPresetName: {
immediate: true,
handler(v) {
this.presetName = v || ''
if (!this.presetName) return
this.$nextTick(() => {
this.renderColorBar(this.presetName)
})
}
} }
}, },
mounted() { mounted() {
const digitPlaces = parseInt(this.$route.query.digitPlaces) const digitPlaces = parseInt(this.$route.query.digitPlaces)
this.digitPlaces = digitPlaces === -1 ? 2 : digitPlaces this.digitPlaces = digitPlaces === -1 ? 2 : digitPlaces
console.log(toolsUtilities)
this.subjectCode = this.$route.query.subjectCode this.subjectCode = this.$route.query.subjectCode
var element = document.getElementById(`viewport${this.index}`) var element = document.getElementById(`viewport${this.index}`)
element.addEventListener(VOLUME_NEW_IMAGE, this.handleVolumeNewImage) element.addEventListener(VOLUME_NEW_IMAGE, this.handleVolumeNewImage)
@ -265,6 +286,24 @@ export default {
this.renderColorBar(this.presetName) this.renderColorBar(this.presetName)
} }
} }
if (this.index !== 4 && viewport) {
const width = viewport.element.clientWidth
const height = viewport.element.clientHeight
let worldPoint = null
if (width && height && viewport.canvasToWorld) {
worldPoint = viewport.canvasToWorld([width / 2, height / 2])
}
if ((!worldPoint || worldPoint.length < 3) && viewport.getCamera) {
worldPoint = viewport.getCamera()?.focalPoint
}
if (Array.isArray(worldPoint) && worldPoint.length >= 3) {
this.$emit('fusionSlicePointChange', {
viewportId: this.viewportId,
worldPoint: [...worldPoint]
})
}
}
}, },
getOrientationMarker() { getOrientationMarker() {
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
@ -328,7 +367,7 @@ export default {
let properties = viewport.getProperties() let properties = viewport.getProperties()
if (this.index === 3) { if (this.index === 3) {
// const volumeId = viewport.getVolumeId() // const volumeId = viewport.getVolumeId()
const volumeId = `cornerstoneStreamingImageVolume:PT_VOLUME_ID` const volumeId = `cornerstoneStreamingImageVolume:PT_VOLUME_ID2`
properties = viewport.getProperties(volumeId) properties = viewport.getProperties(volumeId)
} }
if (properties && properties.voiRange) { if (properties && properties.voiRange) {
@ -338,6 +377,10 @@ export default {
upper upper
) )
this.imageInfo.wwwc = `${Math.round(windowWidth)}/${Math.round(windowCenter)}` this.imageInfo.wwwc = `${Math.round(windowWidth)}/${Math.round(windowCenter)}`
// PTMIP (CT)
if (this.index !== 1) {
this.$emit('upperRangeChange', Math.round(upper))
}
} }
}, },
handleMouseMove(e) { handleMouseMove(e) {
@ -545,7 +588,8 @@ export default {
colorMap = getColormap(presetName) colorMap = getColormap(presetName)
} }
const rgbPoints = colorMap.RGBPoints const rgbPoints = colorMap.RGBPoints
const canvas = document.getElementById('colorBar_Canvas') const canvas = this.$refs.colorBarCanvas
if (!canvas) return
const ctx = canvas.getContext('2d') const ctx = canvas.getContext('2d')
const canvasWidth = 160 const canvasWidth = 160
const canvasHeight = 5 const canvasHeight = 5
@ -567,10 +611,17 @@ export default {
ctx.fillStyle = gradient ctx.fillStyle = gradient
ctx.fillRect(0, 0, rectWidth, rectHeight) ctx.fillRect(0, 0, rectWidth, rectHeight)
}, },
setColorMap(presetName) {
const renderingEngine = getRenderingEngine(this.renderingEngineId)
const viewport = renderingEngine.getViewport(this.viewportId)
let volumeId = `cornerstoneStreamingImageVolume:PT_VOLUME_ID`
viewport.setProperties({ colormap: { name: presetName } }, volumeId)
viewport.render()
},
setWwWc() { setWwWc() {
let properties = viewport.getProperties() let properties = viewport.getProperties()
if (this.index === 3) { if (this.index === 3) {
properties = viewport.getProperties(`cornerstoneStreamingImageVolume:PT_VOLUME_ID`) properties = viewport.getProperties(`cornerstoneStreamingImageVolume:PT_VOLUME_ID2`)
} }
if (properties && properties.voiRange) { if (properties && properties.voiRange) {
var { lower, upper } = properties.voiRange var { lower, upper } = properties.voiRange
@ -582,7 +633,10 @@ export default {
upper upper
) )
this.imageInfo.wwwc = `${Math.round(windowWidth)}/${Math.round(windowCenter)}` this.imageInfo.wwwc = `${Math.round(windowWidth)}/${Math.round(windowCenter)}`
this.$emit('upperRangeChange', Math.round(windowWidth)) // PTMIP (CT)
if (this.index !== 1) {
this.$emit('upperRangeChange', Math.round(upper))
}
} }
}, },
async getScreenshots() { async getScreenshots() {

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