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
type: ssh
name: ssh-linux-test-irc-publish
name: ssh-linux-uat-irc-publish
platform:
os: Linux
@ -33,6 +33,38 @@ trigger:
- 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

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

View File

@ -1,5 +1,5 @@
<template>
<div id="app" style="position: relative">
<div id="app" style="position: relative" :class="{ mask: isLock }">
<router-view />
<div v-show="show" v-if="$route.matched.length > 0" v-adaptive @click="openI18n" style="
position: fixed;
@ -63,7 +63,7 @@
" size="mini"></el-input>
</template>
</el-table-column>
<el-table-column prop="ValueCN" :label="$t('il8n:table:state')" sortable="custom">
<el-table-column prop="State" :label="$t('il8n:table:state')" sortable="custom">
<template slot-scope="scope">
<el-select v-model="scope.row.State" clearable filterable size="mini">
<el-option v-for="item of $d.InternationalizationKeyState"
@ -83,7 +83,7 @@
</div>
</el-drawer>
<feedBack v-if="$route.matched.length > 0" />
<!-- <timeTag /> -->
<timeTag v-if="config.timeZone" />
</div>
</template>
@ -94,14 +94,15 @@ import {
} from '@/api/dictionary/dictionary'
import { getTrialExtralConfig } from '@/api/trials'
import feedBack from '@/views/trials/trials-layout/components/feedBack'
// import timeTag from '@/components/timeTag'
import timeTag from '@/components/timeTag'
import Vue from 'vue'
import i18n from './lang'
import DicomEvent from '@/views/trials/trials-panel/reading/dicoms/components/DicomEvent'
export default {
name: 'App',
components: {
feedBack,
// timeTag
timeTag
},
data() {
return {
@ -112,12 +113,17 @@ export default {
arr: [],
il8nExternal: false,
State: null,
config: window.config,
isLock: false
}
},
mounted() {
// this.show = process.env.VUE_APP_OSS_PATH === '/test/dist'
this.show = false
this.show = process.env.VUE_APP_OSS_PATH === '/test/dist'
Vue.prototype.$openI18n = this.openI18n
// this.getIsLock()
// DicomEvent.$on("isLock", (isLock) => {
// this.isLock = isLock
// })
},
// watch: {
// '$route.query': {
@ -149,17 +155,30 @@ export default {
// },
// },
methods: {
getIsLock() {
if (zzSessionStorage.getItem('isLock') === 'true') {
this.isLock = true
} else {
this.isLock = false
}
},
//
handleSortByColumn(column) {
if (column.order === 'ascending') {
this.tableData.sort((a, b) =>
a[column.prop].localeCompare(b[column.prop])
)
} else {
this.tableData.sort((a, b) =>
b[column.prop].localeCompare(a[column.prop])
)
const { prop, order } = column
if (!prop || !order) {
this.tableData = [...this.tableData]
return
}
const collator = new Intl.Collator('zh-Hans-CN', {
numeric: true,
sensitivity: 'base',
})
this.tableData.sort((a, b) => {
const result = collator.compare(a[prop], b[prop])
return order === 'ascending' ? result : -result
})
},
handleStateChange() {
this.tableData.forEach((item) => {
@ -301,6 +320,23 @@ export default {
</script>
<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;
.el-tooltip__popper {

View File

@ -391,3 +391,35 @@ export function getReSendEmail(data) {
data
})
}
// 获取系统基础配置
export function getSystemBasicConfigInfo() {
return request({
url: `/DeployConfig/getSystemBasicConfigInfo`,
method: 'get',
})
}
// 更新系统基础配置
export function updateSystemBasicConfig(data) {
return request({
url: `/DeployConfig/updateSystemBasicConfig`,
method: 'post',
data
})
}
// 获取系统邮件配置
export function getEmailConfigInfo() {
return request({
url: `/DeployConfig/getEmailConfigInfo`,
method: 'get',
})
}
// 更新系统邮件配置
export function updateSystemEmailConfig(data) {
return request({
url: `/DeployConfig/updateSystemEmailConfig`,
method: 'post',
data
})
}

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
})
}
// 分割相关
// 获取分割组
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

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@ -8,20 +8,49 @@
<script>
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/line');
// require('echarts/lib/chart/line');
// require('echarts/lib/chart/pie');
// require('echarts/lib/chart/scatter');
//
require('echarts/lib/component/tooltip');
require('echarts/lib/component/title');
require('echarts/lib/component/legend');
require('echarts/lib/component/grid');
require('echarts/lib/component/dataZoom');
// require('echarts/lib/component/tooltip');
// require('echarts/lib/component/title');
// require('echarts/lib/component/legend');
// require('echarts/lib/component/grid');
// require('echarts/lib/component/dataZoom');
export default {
name: "readingChart",
props: {
@ -33,7 +62,8 @@ export default {
zIndex: 9,
chart: null,
loading: false,
key: 'readingChart'
key: 'readingChart',
isInteger: false
};
},
methods: {
@ -72,12 +102,13 @@ export default {
let res = await getReportsChartData(params)
this.loading = false
if (res.IsSuccess) {
this.isInteger = res.Result.Type === 'number' && res.Result.ValueType === 0
let LatestScanDateList = res.Result.LatestScanDateList.map(item => item.split(" ")[0])
let obj = {
title: QuestionName,
xAxisData: LatestScanDateList || [],
series: [],
unit: this.$fd("ValueUnit", res.Result.Unit),
unit: res.Result.Unit === 0 ? '' : res.Result.Unit === 4 ? res.Result.CustomUnit : this.$fd("ValueUnit", res.Result.Unit),
visitName: res.Result.VisitTaskNameList,
min: null,
max: null
@ -94,43 +125,6 @@ export default {
type: 'line'
})
});
// if (Array.isArray(res.Result.LatestScanDateList) && res.Result.LatestScanDateList.length >= 2) {
// let hours = moment(res.Result.LatestScanDateList[res.Result.LatestScanDateList.length - 1]).diff(moment(res.Result.LatestScanDateList[0]), 'hours');
// let days = moment(res.Result.LatestScanDateList[res.Result.LatestScanDateList.length - 1]).diff(moment(res.Result.LatestScanDateList[0]), 'days');
// let months = moment(res.Result.LatestScanDateList[res.Result.LatestScanDateList.length - 1]).diff(moment(res.Result.LatestScanDateList[0]), 'months');
// console.log(hours, 'hours')
// console.log(days, 'days')
// console.log(months, 'months')
// if (hours < 24) {
// obj.min = moment(res.Result.LatestScanDateList[0]).format('YYYY-MM-DD') + ' 00:00:00'
// obj.max = moment(res.Result.LatestScanDateList[0]).format('YYYY-MM-DD') + ' 23:59:59'
// }
// if (days >= 1 && days <= 7) {
// obj.min = moment(res.Result.LatestScanDateList[0]).format('YYYY-MM-DD') + ' 00:00:00'
// obj.max = moment(res.Result.LatestScanDateList[0]).add(7, 'days').format('YYYY-MM-DD') + ' 23:59:59'
// }
// if (days > 7 && days < 30) {
// obj.min = moment(res.Result.LatestScanDateList[0]).startOf('month').format('YYYY-MM-DD') + ' 00:00:00'
// obj.max = moment(res.Result.LatestScanDateList[0]).endOf('month').format('YYYY-MM-DD') + ' 23:59:59'
// }
// if (months >= 1 && months <= 3) {
// obj.min = moment(res.Result.LatestScanDateList[0]).startOf('month').format('YYYY-MM-DD') + ' 00:00:00'
// obj.max = moment(res.Result.LatestScanDateList[0]).add(4, 'months').startOf('month').format('YYYY-MM-DD') + ' 23:59:59'
// }
// if (months > 3 && months <= 6) {
// obj.min = moment(res.Result.LatestScanDateList[0]).startOf('month').format('YYYY-MM-DD') + ' 00:00:00'
// obj.max = moment(res.Result.LatestScanDateList[0]).add(7, 'months').startOf('month').format('YYYY-MM-DD') + ' 23:59:59'
// }
// if (months > 6 && months <= 12) {
// obj.min = moment(res.Result.LatestScanDateList[0]).startOf('month').format('YYYY-MM-DD') + ' 00:00:00'
// obj.max = moment(res.Result.LatestScanDateList[0]).add(13, 'months').startOf('month').format('YYYY-MM-DD') + ' 23:59:59'
// }
// if (months > 12) {
// obj.min = moment(res.Result.LatestScanDateList[0]).startOf('month').format('YYYY-MM-DD') + ' 00:00:00'
// obj.max = moment(res.Result.LatestScanDateList[res.Result.LatestScanDateList.length - 1]).add(1, 'months').startOf('month').format('YYYY-MM-DD') + ' 23:59:59'
// }
// }
// console.log(obj)
this.initChart(obj)
}
} catch (err) {
@ -151,7 +145,13 @@ export default {
text: obj.title,
textStyle: {
color: "#fff"
}
},
left: 0,
top: 0
},
grid: {
left: 50,
bottom: 30
},
tooltip: {
trigger: 'axis',
@ -193,6 +193,7 @@ export default {
}
},
axisLine: {
show: true,
lineStyle: {
color: '#fff',
}
@ -200,6 +201,9 @@ export default {
},
series: obj.series
};
if (this.isInteger) {
option.yAxis.minInterval = 1
}
// 4. 使
this.chart.setOption(option);
},

View File

@ -116,7 +116,7 @@ export default {
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 {
box-shadow: none !important;
}

View File

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

View File

@ -86,16 +86,16 @@ export default {
}
</script>
<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;
}
.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;
}
::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;
background-color: #fff;
border-right-color: #dcdfe6;

View File

@ -548,8 +548,9 @@ export default {
})
if (res.IsSuccess) {
this.studyMonitorId = res.Result
let uploadBatchId = this.$guid()
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) {
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
let file = this.fileList.filter((item) => item.id === arr[index].id)[0]
file.status = 1
@ -576,7 +577,7 @@ export default {
}
file.curPath = path
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) {
file.status = 2
this.successFileList.push({
@ -610,13 +611,13 @@ export default {
}
let ind = arr.findIndex((item) => item.status === 0)
if (ind >= 0) {
return this.handleUploadTask(arr, ind)
return this.handleUploadTask(arr, ind, uploadBatchId)
} else {
return false
}
},
// fileoss
async fileToOss(path, file, item) {
async fileToOss(path, file, item, uploadBatchId) {
try {
let res = await this.OSSclient.multipartUpload(
{
@ -629,6 +630,17 @@ export default {
if (item.uploadFileSize > file.fileSize) {
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) {

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>
<style lang="scss" scoped>
@import "~@/styles/mixin.scss";
@import "~@/styles/variables.module.scss";
@use "@/styles/mixin.scss" as *;
@use "@/styles/variables.module.scss" as *;
// @import "~@/styles/mixin.scss";
// @import "~@/styles/variables.module.scss";
.app-wrapper {
@include clearfix;

View File

@ -1,5 +1,6 @@
import Vue from 'vue'
// import DicomEvent from '@/views/trials/trials-panel/reading/dicoms/components/DicomEvent'
import 'normalize.css/normalize.css' // A modern alternative to CSS resets
// import { createVersionPolling } from "@/utils/version-polling.esm.js";
import ElementUI, { MessageBox } from 'element-ui'
@ -70,6 +71,9 @@ import FB from '@/components/feedBack/index'
Vue.use(FB)
import FBT from '@/components/feedBackTable/index'
Vue.use(FBT)
import resize from '@/directive/resize/index'
// 表格自适应指令
Vue.use(resize)
import adaptive from '@/directive/adaptive/index'
// 表格自适应指令
Vue.use(adaptive)
@ -334,8 +338,10 @@ async function VueInit() {
})
}
let CompanyInfo = JSON.parse(localStorage.getItem('CompanyInfo'))
if (CompanyInfo && CompanyInfo.SystemShortName && text.toUpperCase() !== "CIRCLE") {
let test = new RegExp('IRC', 'ig')
if (CompanyInfo && CompanyInfo.SystemShortName && (text.toUpperCase() !== "CIRCLE")) {
// 精准匹配独立单词 IRC
// let test = new RegExp('IRC', 'ig')
let test = new RegExp('\\bIRC\\b', 'ig')
text = text.replace(test, CompanyInfo.SystemShortName)
}
// return i18n.t(key)
@ -446,6 +452,7 @@ async function VueInit() {
isOpen = false
isLock = null
zzSessionStorage.removeItem('isLock')
// DicomEvent.$emit('isLock', false)
router.push("/login")
}).then(() => {
// _vm.$alert(lang === 'zh' ? '由于您长时间未操作,为保护您的数据安全已强制将您下线,如果需要继续操作请重新登陆!' : 'No operation for a long time non-operation, you have been forced logout to protect data security. If continue to operate, please login again!', {
@ -457,160 +464,166 @@ async function VueInit() {
})
} : () => { }, process.env.VUE_APP_LOGOUT_FOR_TIME,
eval(process.env.VUE_APP_LOCK_FOR_PERMISSION) ? () => {
var lang = zzSessionStorage.getItem('lang') ? zzSessionStorage.getItem('lang') : 'zh'
if (_vm.$store.state.trials.unlock || WHITELIST.includes(_vm.$route.path)) {
count = 0;
localStorage.setItem('count', '0')
if (_vm.$route.path === '/login') {
zzSessionStorage.removeItem('lastWorkbench')
zzSessionStorage.removeItem('isLock')
isLock = null
}
return
}
if (isOpen) {
return
}
isOpen = true
zzSessionStorage.setItem('isLock', 'true')
_vm.$msgbox({
title: _vm.$t("env:lock:msgBox:title"),
confirmButtonText: _vm.$t("env:lock:msgBox:confirmButtonText"),
showClose: false,
beforeClose: (action, instance, done) => {
if (action === 'confirm') {
if (!_vm.unlock.my_username) {
_vm.$message.warning(_vm.$t("env:lock:msgBox:iupUser"))
return
}
if (!_vm.unlock.my_password) {
_vm.$message.warning(_vm.$t("env:lock:msgBox:inpPassword"))
return
}
var my_username = zzSessionStorage.getItem('my_username')
var my_password = zzSessionStorage.getItem('my_password')
let my_userid = zzSessionStorage.getItem('userId')
let my_EMail = zzSessionStorage.getItem('my_EMail') || ''
if (md5(_vm.unlock.my_password) === my_password && (my_username === _vm.unlock.my_username || my_EMail.toUpperCase() === _vm.unlock.my_username.toUpperCase())) {
resetReadingRestTime().then(() => {
})
const closeLock = (_vm) => {
_vm.$message.success(_vm.$t("env:lock:msgBox:lockSuccess"))
_vm.unlock = {
my_username: null,
my_password: null,
view: false
}
isOpen = false
count = 0;
isLock = null
zzSessionStorage.removeItem('isLock')
localStorage.setItem('count', '0')
document.querySelector('#my_username').value = null
document.querySelector('#my_password').value = null
setTimeout(() => {
done()
}, 500)
}
// if (eval(process.env.VUE_APP_LOCK_FOR_PERMISSION_MFA)) {
// sendMFAEmail({ UserId: my_userid, MfaType: 1 }).then((res) => {
// done();
// Vue.prototype.$MFA({
// status: "lock",
// UserId: my_userid,
// EMail: res.Result,
// username: my_username,
// callBack: () => {
// closeLock(_vm)
// },
// })
// })
// } else {
closeLock(_vm)
// }
} else {
// console.log(111)
_vm.$message.error(_vm.$t('env:lock:msgBox:userFail'))
}
eval(process.env.VUE_APP_LOCK_FOR_PERMISSION)
? () => {
var lang = zzSessionStorage.getItem('lang') ? zzSessionStorage.getItem('lang') : 'zh'
if (_vm.$store.state.trials.unlock || WHITELIST.includes(_vm.$route.path)) {
count = 0;
localStorage.setItem('count', '0')
if (_vm.$route.path === '/login') {
zzSessionStorage.removeItem('lastWorkbench')
zzSessionStorage.removeItem('isLock')
// DicomEvent.$emit('isLock', false)
isLock = null
}
},
message: h('div', {}, [
h('el-form', {
props: { labelWidth: "80px" }
}, [
h('el-form-item', {
props: { label: _vm.$t("env:lock:msgBox:form:username") },
}, [
h('input', {
props: {
value: _vm.unlock.my_username
},
attrs: {
id: 'my_username',
class: 'el-input__inner',
autocomplete: 'new-password'
},
on: {
change: (event) => {
_vm.unlock.my_username = event.target.value
},
input: (event) => {
_vm.unlock.my_username = event.target.value
}
}
})
]),
h('el-form-item', {
props: { label: _vm.$t("env:lock:msgBox:form:Password") },
attrs: {
style: "position: relative;"
return
}
if (isOpen) {
return
}
isOpen = true
zzSessionStorage.setItem('isLock', 'true')
// DicomEvent.$emit('isLock', true)
_vm.$msgbox({
title: _vm.$t("env:lock:msgBox:title"),
confirmButtonText: _vm.$t("env:lock:msgBox:confirmButtonText"),
showClose: false,
beforeClose: (action, instance, done) => {
if (action === 'confirm') {
if (!_vm.unlock.my_username) {
_vm.$message.warning(_vm.$t("env:lock:msgBox:iupUser"))
return
}
}, [
h('input', {
props: {
value: _vm.unlock.my_password
},
ref: "unlock_my_password_input",
attrs: {
id: 'my_password',
class: 'el-input__inner',
type: _vm.unlock.view ? 'text' : 'password',
autocomplete: 'new-password',
style: "padding-right:25px"
},
on: {
change: (event) => {
_vm.unlock.my_password = event.target.value
},
input: (event) => {
_vm.unlock.my_password = event.target.value
if (!_vm.unlock.my_password) {
_vm.$message.warning(_vm.$t("env:lock:msgBox:inpPassword"))
return
}
var my_username = zzSessionStorage.getItem('my_username')
var my_password = zzSessionStorage.getItem('my_password')
let my_userid = zzSessionStorage.getItem('userId')
let my_EMail = zzSessionStorage.getItem('my_EMail') || ''
if (md5(_vm.unlock.my_password) === my_password && (my_username === _vm.unlock.my_username || my_EMail.toUpperCase() === _vm.unlock.my_username.toUpperCase())) {
resetReadingRestTime().then(() => {
})
const closeLock = (_vm) => {
_vm.$message.success(_vm.$t("env:lock:msgBox:lockSuccess"))
_vm.unlock = {
my_username: null,
my_password: null,
view: false
}
isOpen = false
count = 0;
isLock = null
// zzSessionStorage.removeItem('isLock')
// DicomEvent.$emit('isLock', false)
localStorage.setItem('count', '0')
document.querySelector('#my_username').value = null
document.querySelector('#my_password').value = null
setTimeout(() => {
done()
}, 500)
}
}),
h('i', {
attrs: {
id: 'my_password_view',
class: "el-icon-view",
style: "cursor: pointer;position: absolute;top:35%;right:10px"
},
on: {
click: (event) => {
_vm.unlock.view = !_vm.unlock.view
if (_vm.unlock.view) {
_vm.$refs['unlock_my_password_input'].type = "text"
} else {
_vm.$refs['unlock_my_password_input'].type = "password"
}
// if (eval(process.env.VUE_APP_LOCK_FOR_PERMISSION_MFA)) {
// sendMFAEmail({ UserId: my_userid, MfaType: 1 }).then((res) => {
// done();
// Vue.prototype.$MFA({
// status: "lock",
// UserId: my_userid,
// EMail: res.Result,
// username: my_username,
// callBack: () => {
// closeLock(_vm)
// },
// })
// })
// } else {
closeLock(_vm)
// }
} else {
// console.log(111)
_vm.$message.error(_vm.$t('env:lock:msgBox:userFail'))
}
}
},
message: h('div', {}, [
h('el-form', {
props: { labelWidth: "80px" }
}, [
h('el-form-item', {
props: { label: _vm.$t("env:lock:msgBox:form:username") },
}, [
h('input', {
props: {
value: _vm.unlock.my_username
},
attrs: {
id: 'my_username',
class: 'el-input__inner',
autocomplete: 'new-password'
},
on: {
change: (event) => {
_vm.unlock.my_username = event.target.value
},
input: (event) => {
_vm.unlock.my_username = event.target.value
}
}
})
]),
h('el-form-item', {
props: { label: _vm.$t("env:lock:msgBox:form:Password") },
attrs: {
style: "position: relative;"
}
}),
}, [
h('input', {
props: {
value: _vm.unlock.my_password
},
ref: "unlock_my_password_input",
attrs: {
id: 'my_password',
class: 'el-input__inner',
type: _vm.unlock.view ? 'text' : 'password',
autocomplete: 'new-password',
style: "padding-right:25px"
},
on: {
change: (event) => {
_vm.unlock.my_password = event.target.value
},
input: (event) => {
_vm.unlock.my_password = event.target.value
}
}
}),
h('i', {
attrs: {
id: 'my_password_view',
class: "el-icon-view",
style: "cursor: pointer;position: absolute;top:35%;right:10px"
},
on: {
click: (event) => {
_vm.unlock.view = !_vm.unlock.view
if (_vm.unlock.view) {
_vm.$refs['unlock_my_password_input'].type = "text"
} else {
_vm.$refs['unlock_my_password_input'].type = "password"
}
},
}
}),
])
])
])
])
})
} : () => { }, process.env.VUE_APP_LOCK_FOR_TIME)
})
} : () => { },
process.env.VUE_APP_LOCK_FOR_TIME
)
}
VueInit()
// createVersionPolling({

View File

@ -156,12 +156,6 @@ export const constantRoutes = [
hidden: true,
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',

View File

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

View File

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

View File

@ -1,6 +1,6 @@
import Vue from 'vue'
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 => {
try {
// let blob = await encoder(file, config)
@ -8,7 +8,8 @@ export const dcmUpload = async function (data, config, progressFn) {
if (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({
...res,
image: blob.pixelDataElement

View File

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

View File

@ -5,6 +5,7 @@ const stream = require('stream')
import Vue from 'vue'
import { customerHttp, OSSclose } from "@/utils/multipartUpload/oss"
import { exist, AWSclose } from "@/utils/multipartUpload/aws"
import { addOrUpdateFileUploadRecord } from '@/api/file'
const { GetObjectStoreToken } = require('../api/user.js')
const {
S3Client,
@ -22,7 +23,6 @@ async function ossGenerateSTS() {
res = await GetObjectStoreToken()
localStorage.setItem('stsToken', JSON.stringify(res))
}
// res.Result.ObjectStoreUse = 'AWS';
Vue.prototype.OSSclientConfig = { ...res.Result[res.Result.ObjectStoreUse] }
Vue.prototype.OSSclientConfig.ObjectStoreUse = res.Result.ObjectStoreUse;
@ -34,7 +34,8 @@ async function ossGenerateSTS() {
Vue.prototype.OSSclientConfig.timeout = 10 * 60 * 1000
let OSSclient = new OSS(Vue.prototype.OSSclientConfig)
Vue.prototype.OSSclient = {
put: async function (objectName, object) {
put: async function (objectName, object, fileInfo = {}) {
OSSclient = await RefreshClient(OSSclient)
return new Promise(async (resolve, reject) => {
try {
@ -52,6 +53,20 @@ async function ossGenerateSTS() {
}
let res = await OSSclient.put(objectName, object)
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({
name: objectName,
url: res.url
@ -65,7 +80,7 @@ async function ossGenerateSTS() {
}
})
},
multipartUpload: async (data, progress) => {
multipartUpload: async (data, progress, fileInfo = {}) => {
OSSclient = await RefreshClient(OSSclient)
return new Promise(async (resolve, reject) => {
try {
@ -95,6 +110,21 @@ async function ossGenerateSTS() {
}
let res = await customerHttp(OSSclient, data, progress);
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({
name: data.path,
url: Vue.prototype.OSSclientConfig.viewEndpoint + decodeUtf8(res.name)
@ -169,17 +199,17 @@ async function ossGenerateSTS() {
}
});
Vue.prototype.OSSclient = {
put: async function (objectName, object) {
put: async function (objectName, object, fileInfo = {}) {
let data = {
file: object,
path: objectName
}
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);
return uploadAWS(aws, data, progress);
return uploadAWS(aws, data, progress, fileInfo);
},
close: () => {
AWSclose();
@ -189,7 +219,7 @@ async function ossGenerateSTS() {
return
}
// AWS上传函数
function uploadAWS(aws, data, progress) {
function uploadAWS(aws, data, progress, fileInfo) {
return new Promise(async (resolve, reject) => {
try {
const { file, path } = data;
@ -211,6 +241,21 @@ function uploadAWS(aws, data, progress) {
data.path = data.path.replace(`/${bucketName}/`, '');
await exist(aws, bucketName, data, progress, (path, status) => {
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({
name: decodeUtf8(curPath),
url: Vue.prototype.OSSclientConfig.viewEndpoint + decodeUtf8(curPath)
@ -320,5 +365,37 @@ function isCredentialsExpired(credentials) {
return expireDate.getTime() - now.getTime() <= 300000;
}
function mimeTypeToExt(mimeType) {
const map = {
// 图片
'image/jpeg': 'jpg',
'image/jpg': 'jpg',
'image/png': 'png',
'image/gif': 'gif',
'image/webp': 'webp',
'image/svg+xml': 'svg',
'image/bmp': 'bmp',
// 文档
'application/dicom': 'dcm',
'application/pdf': 'pdf',
'application/msword': 'doc',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'docx',
'application/vnd.ms-excel': 'xls',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'xlsx',
'application/vnd.ms-powerpoint': 'ppt',
'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'pptx',
'text/plain': 'txt',
// 音频/视频
'audio/mpeg': 'mp3',
'audio/wav': 'wav',
'video/mp4': 'mp4',
'video/mpeg': 'mpeg',
// 压缩包
'application/zip': 'zip',
'application/x-rar-compressed': 'rar',
'application/x-7z-compressed': '7z',
};
return map[mimeType] || ''; // 找不到返回空字符串
}
export const OSSclient = ossGenerateSTS

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -22,7 +22,7 @@
<el-input v-model="password.UserType" disabled />
</el-form-item> -->
<!-- 用户名 -->
<el-form-item :label="$t('recompose:form:userName')" prop="NewUserName">
<el-form-item class="my_new_pwd" :label="$t('recompose:form:userName')" prop="NewUserName">
<el-input v-model="password.NewUserName" :disabled="isUpdate" />
</el-form-item>
<!-- 新密码 -->
@ -77,7 +77,7 @@ export default {
callback(
lang === 'zh'
? new Error(
'1新建账号用户名字符长度最小为4个字符最大为16个字符只可使用字母、数字、下划线'
'新建账号用户名字符长度最小为4个字符最大为16个字符只可使用字母、数字、下划线'
)
: new Error(
'For a new account, the username must have:1) At least 4 characters;2) At most 16 characters;3)Only letters, numbers, and underscores are allowed.'

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,40 +1,98 @@
<template>
<el-form ref="equipmentForm" :model="form" :rules="rules" label-width="150px">
<el-form ref="equipmentForm" :model="form" :rules="rules" label-width="380px">
<div class="base-dialog-body">
<!-- 扫描设备 -->
<el-form-item :label="$t('trials:equiptResearch:form:equipment')" prop="EquipmentTypeId">
<el-select
v-model="form.EquipmentTypeId"
style="width:100%"
>
<!-- <el-option
v-for="item of dictionaryList.SiteSurvey_ScanEquipmentType"
:key="item.Id"
:label="item.Value"
:value="item.Id"
/> -->
<el-option
v-for="item of $d.SiteSurvey_ScanEquipmentType"
:key="item.id"
:label="item.label"
:value="item.id"
/>
</el-select>
<el-form-item :label="$t('trials:equiptResearch:form:equipment')" prop="EquipmentTypeEnum"
v-if="EquipmentControlFieldList.includes('EquipmentTypeEnum')">
<div style="display: flex;align-items: center;">
<el-select v-model="form.EquipmentTypeEnum" style="width:100%" @change="form.OtherEquipmentType = null">
<el-option v-for="item of $d.SiteSurvey_ScanEquipmentType" :key="item.id" :label="item.label"
:value="item.value" />
</el-select>
<el-input placeholder="" v-model="form.OtherEquipmentType" style="margin-left: 10px;"
v-if="form.EquipmentTypeEnum == '-1'" clearable>
</el-input>
</div>
</el-form-item>
<!-- 扫描参数 -->
<el-form-item v-if="isShowParameters" :label="$t('trials:equiptResearch:form:param')">
<el-form-item :label="$t('trials:equiptResearch:form:param')"
v-if="EquipmentControlFieldList.includes('Parameters')" prop="Parameters">
<el-input v-model="form.Parameters" />
</el-form-item>
<!-- 扫描仪器制造商名称 -->
<el-form-item :label="$t('trials:equiptResearch:form:manufacturer')">
<el-input v-model="form.ManufacturerName" />
<el-form-item :label="$t('trials:equiptResearch:form:manufacturer')"
v-if="EquipmentControlFieldList.includes('ManufacturerType')" prop="ManufacturerType">
<div style="display: flex;align-items: center;">
<el-select v-model="form.ManufacturerType" style="width:100%" @change="form.ManufacturerName = null">
<el-option v-for="item of $d.ManufacturerType" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
<el-input placeholder="" v-model="form.ManufacturerName" style="margin-left: 10px;"
v-if="form.ManufacturerType == '-1'" clearable>
</el-input>
</div>
</el-form-item>
<!-- 扫描仪型号 -->
<el-form-item :label="$t('trials:equiptResearch:form:model')">
<el-form-item :label="$t('trials:equiptResearch:form:model')"
v-if="EquipmentControlFieldList.includes('ScannerType')" prop="ScannerType">
<el-input v-model="form.ScannerType" />
</el-form-item>
<!-- 磁场强度 -->
<el-form-item :label="$t('trials:equiptResearch:form:MagneticFieldStrengthType')"
v-if="EquipmentControlFieldList.includes('MagneticFieldStrengthType')" prop="MagneticFieldStrengthType">
<el-select v-model="form.MagneticFieldStrengthType" style="width:100%">
<el-option v-for="item of $d.MagneticFieldStrengthType" :key="item.id" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<!-- 体部线圈通道数 -->
<el-form-item :label="$t('trials:equiptResearch:form:BodyCoilChannelCount')"
v-if="EquipmentControlFieldList.includes('BodyCoilChannelCount')" prop="BodyCoilChannelCount">
<el-select v-model="form.BodyCoilChannelCount" style="width:100%">
<el-option v-for="item of $d.BodyCoilChannelCount" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- 是否具备专用的PDFF脂肪定量序列CSE-MRI序列 -->
<el-form-item :label="$t('trials:equiptResearch:form:HasDedicatedPdfFatQuantificationSequence')"
v-if="EquipmentControlFieldList.includes('HasDedicatedPdfFatQuantificationSequence')"
prop="HasDedicatedPdfFatQuantificationSequence">
<el-select v-model="form.HasDedicatedPdfFatQuantificationSequence" style="width:100%"
@change="form.PdfFatQuantificationSequenceType = null, form.OtherSequenceSpecification = null">
<el-option v-for="item of $d.YesOrNo" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- PDFF脂肪定量序列 -->
<el-form-item :label="$t('trials:equiptResearch:form:PdfFatQuantificationSequenceType')"
prop="PdfFatQuantificationSequenceType"
v-if="form.HasDedicatedPdfFatQuantificationSequence && EquipmentControlFieldList.includes('PdfFatQuantificationSequenceType')">
<div style="display: flex;align-items: center;">
<el-select v-model="form.PdfFatQuantificationSequenceType" style="width:100%"
@change="form.OtherSequenceSpecification = null">
<el-option v-for="item of $d.PdfFatQuantificationSequenceType" :key="item.id" :label="item.label"
:value="item.value" />
</el-select>
<el-input placeholder="" v-model="form.OtherSequenceSpecification" style="margin-left: 10px;"
v-if="form.PdfFatQuantificationSequenceType == '-1'" clearable>
</el-input>
</div>
</el-form-item>
<!-- 是否包含 T2/R2 校正用于铁沉积校正 -->
<el-form-item :label="$t('trials:equiptResearch:form:HasT2R2Correction')" prop="HasT2R2Correction"
v-if="EquipmentControlFieldList.includes('HasT2R2Correction')">
<el-select v-model="form.HasT2R2Correction" style="width:100%">
<el-option v-for="item of $d.YesOrNo" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- 是否可完整导出 PDFF 参数图及全部原始 DICOM 数据 -->
<el-form-item :label="$t('trials:equiptResearch:form:CanFullyExportPdfParameterMapsAndRawDicom')"
prop="CanFullyExportPdfParameterMapsAndRawDicom"
v-if="EquipmentControlFieldList.includes('CanFullyExportPdfParameterMapsAndRawDicom')">
<el-select v-model="form.CanFullyExportPdfParameterMapsAndRawDicom" style="width:100%">
<el-option v-for="item of $d.YesOrNo" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- 备注 -->
<el-form-item :label="$t('trials:equiptResearch:form:remark')">
<el-form-item :label="$t('trials:equiptResearch:form:remark')" prop="Note"
v-if="EquipmentControlFieldList.includes('Note')">
<el-input v-model="form.Note" />
</el-form-item>
</div>
@ -69,26 +127,94 @@ export default {
type: String,
default: ''
},
isShowParameters: {
type: Boolean,
default: false
EquipmentControlFieldList: {
type: Array,
default: () => {
return []
}
}
},
data() {
return {
form: {
Id: '',
EquipmentTypeId: '',
Parameters: '',
ManufacturerName: '',
ScannerType: '',
Note: '',
EquipmentTypeEnum: null,
OtherEquipmentType: null,
Parameters: null,
Note: null,
ManufacturerType: null,
ManufacturerName: null,
ScannerType: null,
MagneticFieldStrengthType: null,
BodyCoilChannelCount: null,
HasDedicatedPdfFatQuantificationSequence: null,
PdfFatQuantificationSequenceType: null,
OtherSequenceSpecification: null,
HasT2R2Correction: null,
CanFullyExportPdfParameterMapsAndRawDicom: null,
TrialSiteSurveyId: ''
},
rules: {
EquipmentTypeId: [
EquipmentTypeEnum: [
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] },
{
validator: (rule, value, callback) => {
if (this.form.EquipmentTypeEnum === -1 && !this.form.OtherEquipmentType) {
callback(this.$t('common:ruleMessage:specify'));
} else {
callback()
}
}, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change']
},
],
Parameters: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change'] }
],
Note: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change'] }
],
ManufacturerType: [
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] },
{
validator: (rule, value, callback) => {
if (this.form.ManufacturerType === -1 && !this.form.ManufacturerName) {
callback(this.$t('common:ruleMessage:specify'));
} else {
callback()
}
}, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change']
},
],
ScannerType: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change'] }
],
MagneticFieldStrengthType: [
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] }
]
],
BodyCoilChannelCount: [
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] }
],
HasDedicatedPdfFatQuantificationSequence: [
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] }
],
PdfFatQuantificationSequenceType: [
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] },
{
validator: (rule, value, callback) => {
if (this.form.PdfFatQuantificationSequenceType === -1 && !this.form.OtherSequenceSpecification) {
callback(this.$t('common:ruleMessage:specify'));
} else {
callback()
}
}, message: this.$t('common:ruleMessage:specify'), trigger: ['blur', 'change']
},
],
HasT2R2Correction: [
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] }
],
CanFullyExportPdfParameterMapsAndRawDicom: [
{ required: true, message: this.$t('trials:researchForm:formRule:select'), trigger: ['blur', 'change'] }
],
},
btnLoading: false,
dictionaryList: {}

View File

@ -2,63 +2,88 @@
<div class="equipment-wrapper">
<div class="header-wrapper">
<!-- 新增 -->
<el-button
v-if="state === 0 && userTypeEnumInt === 0 && !isHistory"
size="small"
type="primary"
@click="handleAdd"
>
<el-button v-if="state === 0 && userTypeEnumInt === 0 && !isHistory && EquipmentControlFieldList.length > 0"
size="small" type="primary" @click="handleAdd">
{{ $t('common:button:add') }}
</el-button>
</div>
<el-table
v-loading="loading"
:data="list"
border
style="width: 100%"
>
<el-table v-loading="loading" :data="list" border style="width: 100%">
<!-- 扫描设备 -->
<el-table-column
prop="EquipmentType"
:label="$t('trials:equiptResearch:form:equipment')"
min-width="120"
show-overflow-tooltip
/>
<el-table-column v-if="EquipmentControlFieldList.includes('EquipmentTypeEnum')" prop="EquipmentTypeEnum"
:label="$t('trials:equiptResearch:form:equipment')" min-width="120" show-overflow-tooltip>
<template slot-scope="scope">
{{ scope.row.OtherEquipmentType ? scope.row.OtherEquipmentType :
$fd('SiteSurvey_ScanEquipmentType', scope.row.EquipmentTypeEnum) }}
</template>
</el-table-column>
<!-- 扫描参数 -->
<el-table-column
v-if="isShowParameters"
prop="Parameters"
:label="$t('trials:equiptResearch:form:param')"
min-width="100"
show-overflow-tooltip
/>
<el-table-column v-if="EquipmentControlFieldList.includes('Parameters')" prop="Parameters"
:label="$t('trials:equiptResearch:form:param')" min-width="100" show-overflow-tooltip />
<!-- 扫描仪器制造商名称 -->
<el-table-column
min-width="120"
prop="ManufacturerName"
:label="$t('trials:equiptResearch:form:manufacturer')"
show-overflow-tooltip
/>
<el-table-column v-if="EquipmentControlFieldList.includes('ManufacturerType')" min-width="120"
prop="ManufacturerType" :label="$t('trials:equiptResearch:form:manufacturer')" show-overflow-tooltip>
<template slot-scope="scope">
{{ scope.row.ManufacturerName ? scope.row.ManufacturerName : $fd('ManufacturerType',
scope.row.ManufacturerType) }}
</template>
</el-table-column>
<!-- 扫描仪型号 -->
<el-table-column
min-width="120"
prop="ScannerType"
:label="$t('trials:equiptResearch:form:model')"
show-overflow-tooltip
/>
<el-table-column v-if="EquipmentControlFieldList.includes('ScannerType')" min-width="120" prop="ScannerType"
:label="$t('trials:equiptResearch:form:model')" show-overflow-tooltip />
<!-- 磁场强度 -->
<el-table-column v-if="EquipmentControlFieldList.includes('MagneticFieldStrengthType')" min-width="120"
prop="MagneticFieldStrengthType" :label="$t('trials:equiptResearch:form:MagneticFieldStrengthType')"
show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('MagneticFieldStrengthType', scope.row.MagneticFieldStrengthType) }}
</template>
</el-table-column>
<!-- 体部线圈通道数 -->
<el-table-column v-if="EquipmentControlFieldList.includes('BodyCoilChannelCount')" min-width="120"
prop="BodyCoilChannelCount" :label="$t('trials:equiptResearch:form:BodyCoilChannelCount')"
show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('BodyCoilChannelCount', scope.row.BodyCoilChannelCount) }}
</template>
</el-table-column>
<!-- 是否具备专用的PDFF脂肪定量序列CSE-MRI序列 -->
<el-table-column v-if="EquipmentControlFieldList.includes('HasDedicatedPdfFatQuantificationSequence')"
min-width="120" prop="HasDedicatedPdfFatQuantificationSequence"
:label="$t('trials:equiptResearch:form:HasDedicatedPdfFatQuantificationSequence')" show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('YesOrNo', scope.row.HasDedicatedPdfFatQuantificationSequence) }}
</template>
</el-table-column>
<!-- 专用的PDFF脂肪定量序列类型 -->
<el-table-column v-if="EquipmentControlFieldList.includes('PdfFatQuantificationSequenceType')" min-width="120"
prop="PdfFatQuantificationSequenceType"
:label="$t('trials:equiptResearch:form:PdfFatQuantificationSequenceType')" show-overflow-tooltip><template
slot-scope="scope">
{{ scope.row.OtherSequenceSpecification ? scope.row.OtherSequenceSpecification :
$fd('PdfFatQuantificationSequenceType', scope.row.PdfFatQuantificationSequenceType) }}
</template>
</el-table-column>
<!-- 是否包含 T2/R2 校正用于铁沉积校正 -->
<el-table-column v-if="EquipmentControlFieldList.includes('HasT2R2Correction')" min-width="120"
prop="HasT2R2Correction" :label="$t('trials:equiptResearch:form:HasT2R2Correction')" show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('YesOrNo', scope.row.HasT2R2Correction) }}
</template>
</el-table-column>
<!-- 是否可完整导出 PDFF 参数图及全部原始 DICOM 数据 -->
<el-table-column v-if="EquipmentControlFieldList.includes('CanFullyExportPdfParameterMapsAndRawDicom')"
prop="CanFullyExportPdfParameterMapsAndRawDicom"
:label="$t('trials:equiptResearch:form:CanFullyExportPdfParameterMapsAndRawDicom')" min-width="120"
show-overflow-tooltip>
<template slot-scope="scope">
{{ $fd('YesOrNo', scope.row.CanFullyExportPdfParameterMapsAndRawDicom) }}
</template>
</el-table-column>
<!-- 备注 -->
<el-table-column
min-width="120"
prop="Note"
:label="$t('trials:equiptResearch:form:precautions')"
show-overflow-tooltip
/>
<el-table-column
v-if="state === 0 && userTypeEnumInt === 0 && !isHistory"
fixed="right"
:label="$t('common:action:action')"
width="100"
>
<el-table-column v-if="EquipmentControlFieldList.includes('Note')" min-width="120" prop="Note"
:label="$t('trials:equiptResearch:form:precautions')" show-overflow-tooltip />
<el-table-column v-if="state === 0 && userTypeEnumInt === 0 && !isHistory" fixed="right"
:label="$t('common:action:action')" width="100">
<template slot-scope="scope">
<!-- 编辑 -->
<el-button type="text" size="small" @click="handleEdit(scope.row)">
@ -73,16 +98,10 @@
</el-table>
<!-- 新增/编辑人员信息 -->
<el-dialog
v-if="editVisible"
:visible.sync="editVisible"
:close-on-click-modal="false"
:title="title"
width="600px"
custom-class="base-dialog-wrapper"
:append-to-body="userTypeEnumInt !== 0"
>
<EquipmentForm :data="rowData" :trial-site-survey-equipment-type="trialSiteSurveyEquipmentType" :is-show-parameters="isShowParameters" @getList="getList" @close="closeDialog" />
<el-dialog v-if="editVisible" :visible.sync="editVisible" :close-on-click-modal="false" :title="title" width="800px"
custom-class="base-dialog-wrapper" :append-to-body="userTypeEnumInt !== 0">
<EquipmentForm :data="rowData" :trial-site-survey-equipment-type="trialSiteSurveyEquipmentType"
:EquipmentControlFieldList="EquipmentControlFieldList" @getList="getList" @close="closeDialog" />
</el-dialog>
</div>
@ -115,7 +134,7 @@ export default {
state: null,
trialSiteSurveyId: '',
trialId: '',
isShowParameters: false
EquipmentControlFieldList: []
}
},
mounted() {
@ -160,10 +179,14 @@ export default {
this.$message.success(this.$t('trials:equiptResearch:message:delSuccessfully'))
}
}).catch(() => { this.loading = false })
}).catch(() => {})
}).catch(() => { })
},
initList(TrialSiteEquipmentSurveyList, trialSiteSurvey, isShowParameters) {
this.isShowParameters = isShowParameters
initList(TrialSiteEquipmentSurveyList, trialSiteSurvey, EquipmentControlFieldList) {
this.EquipmentControlFieldList = []
EquipmentControlFieldList.forEach(item => {
this.EquipmentControlFieldList.push(item.FiledName)
})
console.log(this.EquipmentControlFieldList, 'this.EquipmentControlFieldList')
this.list = TrialSiteEquipmentSurveyList
this.state = trialSiteSurvey.State
this.$forceUpdate()
@ -176,8 +199,8 @@ export default {
}
</script>
<style lang="scss" scoped>
.equipment-wrapper{
.header-wrapper{
.equipment-wrapper {
.header-wrapper {
text-align: right;
margin-bottom: 10px;
}

View File

@ -125,7 +125,7 @@ export default {
type: String,
default: ''
}
},
},
data() {
return {
trialId: this.$route.query.trialId,
@ -175,7 +175,7 @@ export default {
this.$refs['historicalParticipant'].initList(historicalArr, res.Result.TrialSiteSurvey)
this.$refs['researchParticipants'].initList(newArr, res.Result.TrialSiteSurvey)
// this.$refs['researchParticipants'].initList(res.Result.TrialSiteUserSurveyList, res.Result.TrialSiteSurvey)
this.$refs['researchEquipments'].initList(res.Result.TrialSiteEquipmentSurveyList, res.Result.TrialSiteSurvey, !(res.Result.SiteSurveyFiledConfig && res.Result.SiteSurveyFiledConfig.ModifyFiledList.length > 0))
this.$refs['researchEquipments'].initList(res.Result.TrialSiteEquipmentSurveyList, res.Result.TrialSiteSurvey, res.Result.SiteSurveyFiledConfig.EquipmentControlFieldList)
this.isExistIncorrect = res.Result.TrialSiteUserSurveyList.every(item => item.IsCorrect === false)
}
this.loading = false

View File

@ -8,7 +8,7 @@
<!-- <TopLang style="position: fixed;top: 40px;right: 40px" /> -->
<div style="display: flex;justify-content: space-between;" v-if="!isPreview">
<div>{{ $t('trials:researchForm:title:researchSurveyStatus') }} <el-tag>{{ $fd('ResearchRecord', state)
}}</el-tag></div>
}}</el-tag></div>
<div>
<!-- 提交 -->
<el-button v-if="(state === 0 && userTypeEnumInt === 0)" type="primary" size="small"
@ -209,7 +209,7 @@ export default {
this.$refs['historicalParticipant'].initList(historicalArr, res.Result.TrialSiteSurvey)
this.$refs['researchParticipants'].initList(newArr, res.Result.TrialSiteSurvey)
this.$refs['researchEquipments'].initList(res.Result.TrialSiteEquipmentSurveyList, res.Result.TrialSiteSurvey, !(res.Result.SiteSurveyFiledConfig && res.Result.SiteSurveyFiledConfig.ModifyFiledList.length > 0))
this.$refs['researchEquipments'].initList(res.Result.TrialSiteEquipmentSurveyList, res.Result.TrialSiteSurvey, res.Result.SiteSurveyFiledConfig.EquipmentControlFieldList)
this.isExistIncorrect = res.Result.TrialSiteUserSurveyList.every(item => item.IsCorrect === false)
}
this.loading = false
@ -357,7 +357,7 @@ export default {
overflow-y: auto;
}
::v-deep.el-card__body {
::v-deep .el-card__body {
padding: 10px;
}
@ -365,13 +365,13 @@ export default {
// padding: 10px 20px 20px 20px;
// }
::v-deep.full-dialog-container {
::v-deep .full-dialog-container {
.el-dialog__body {
height: calc(100% - 80px);
}
}
::v-deep.dialog-container {
::v-deep .dialog-container {
// margin-top: 50px !important;
width: 75%;
height: 80%;

View File

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

View File

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

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

View File

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

View File

@ -1,25 +1,145 @@
<template>
<div>
<!-- <label>行数: </label>
<input v-model.number="rows" type="number" min="1" max="3" />
<label>列数: </label>
<input v-model.number="cols" type="number" min="1" max="3" /> -->
<GridLayout :rows="rows" :cols="cols" />
<el-input v-model="input" placeholder="" clearable></el-input>
<el-input v-model="pageIndex" placeholder="" clearable></el-input>
<el-input v-model="pageSize" placeholder="" clearable></el-input>
<el-button type="primary" size="small" :disabled="!input" @click.stop="getTrialUnreadVisitList">下载</el-button>
</div>
</template>
<script>
import GridLayout from './GridLayout.vue';
import { downLoadFile } from '@/utils/stream.js'
import { getTrialUnreadVisitList, getExportSubjectVisitImageList } from '@/api/trials'
export default {
components: {
GridLayout,
},
// components: {
// GridLayout,
// },
data() {
return {
rows: 2,
cols: 2,
input: '08deab4a-f842-a7a4-0242-0a0001000000',
pageIndex: 1,
pageSize: 250
};
},
methods: {
async getTrialUnreadVisitList() {
let params = {
TrialId: this.input,
PageIndex: this.pageIndex,
PageSize: this.pageSize,
}
let res = await getTrialUnreadVisitList(params)
if (res.IsSuccess) {
let arr = res.Result
this.LoopDownload(arr, 0)
}
},
async LoopDownload(arr, index) {
if (index >= arr.length) return false
let res = await this.handleExportImage(false, true, arr[index])
console.log(res, arr[index], 'res')
index++
this.LoopDownload(arr, index)
},
async handleExportImage(IsKeyImage = false, IsExportReading = false, SubjectVisitId) {
try {
let data = {
TrialId: this.input,
IsKeyImage,
IsExportReading
}
data.SubjectVisitIdList = [SubjectVisitId]
// if (!IsKeyImage) {
// let confirm = await this.$confirm(this.$t('trials:imageSummary:confirm:space').replace('xxx', this.image_size.CheckImageSize))
// if (!confirm) return false
// }
let res = await getExportSubjectVisitImageList(data)
if (res.IsSuccess) {
return this.downLoad(IsKeyImage, res.Result, IsExportReading)
}
return false
} catch (err) {
return false
console.log(err)
}
},
//
async downLoad(IsKeyImage = false, row, IsExportReading = false) {
try {
let { files, name } = this.formatDownloadFile(IsKeyImage, row, IsExportReading)
return await downLoadFile(files, name, 'zip')
// }
} catch (err) {
return false
console.log(err)
}
},
//
formatDownloadFile(IsKeyImage = false, row, IsExportReading = false) {
let files = [],
name = `${this.$route.query.researchProgramNo}_${this.$t('trials:imageSummary:downloadname:dicom')}_${Date.now()}.zip`;
if (IsExportReading) {
name = `${row.VisitList[0].SubjectCode}_${row.VisitList[0].VisitName}.zip`;
}
if (!IsKeyImage) {
//ID/ID/访/Study ID_Study Date_Modality/
row.VisitList.forEach(visit => {
if (visit.StudyList && visit.StudyList.length > 0) {
visit.StudyList.forEach(study => {
let arr = []
study.SeriesList.forEach(item => {
if (!arr.includes(item.Modality)) {
arr.push(item.Modality)
}
})
let str = arr.join('_')
let time = study.StudyTime.split(' ')[0]
if (study.StudyDIRPath && !study.dirHas) {
study.dirHas = true
let obj = {
name: `${study.StudyCode}_${time}_${str}/DICOMDIR`,
url: this.OSSclientConfig.basePath + study.StudyDIRPath,
}
files.push(obj)
}
if (study.SeriesList && study.SeriesList.length > 0) {
study.SeriesList.forEach(serie => {
if (serie.InstanceList && serie.InstanceList.length > 0) {
serie.InstanceList.forEach(instance => {
let instanceArr = instance.Path.split("/")
let fileName = instance.FileName || instanceArr[instanceArr.length - 1]
let obj = {
name: `${study.StudyCode}_${time}_${str}/IMAGE/${fileName}`,
url: this.OSSclientConfig.basePath + instance.Path,
IsEncapsulated: instance.IsEncapsulated
}
files.push(obj)
})
}
})
}
})
}
if (visit.NoneDicomStudyList && visit.NoneDicomStudyList.length > 0) {
visit.NoneDicomStudyList.forEach(noneDicomStudy => {
if (noneDicomStudy.FileList && noneDicomStudy.FileList.length > 0) {
noneDicomStudy.FileList.forEach(file => {
let time = noneDicomStudy.ImageDate.split(' ')[0]
let obj = {
name: `${noneDicomStudy.StudyCode}_${time}_${noneDicomStudy.Modality}/${file.FileName}`,
url: this.OSSclientConfig.basePath + file.Path,
}
files.push(obj)
})
}
})
}
})
}
return { files, name }
},
}
};
</script>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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 RectangleRoiTool from '@/views/trials/trials-panel/reading/dicoms/tools/RectangleRoi/RectangleRoiTool'
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 ScaleOverlayTool from '@/views/trials/trials-panel/reading/dicoms/tools/ScaleOverlay/ScaleOverlayTool'
import getOrientationString from '@/views/trials/trials-panel/reading/dicoms/tools/OrientationMarkers/getOrientationString'
@ -260,7 +261,7 @@ export default {
series: '',
ToolStateManager: null,
renderedMeasured: [],
measuredTools: ['Length', 'Bidirectional', 'ArrowAnnotate', 'RectangleRoi', 'Probe'],
measuredTools: ['Length', 'Bidirectional', 'ArrowAnnotate', 'RectangleRoi', 'Probe', 'FixedCircleRoi'],
measureData: [],
selectedLesion: null,
activeTool: 0, // 0:enable 1:passive 2:active
@ -584,6 +585,13 @@ export default {
e.stopPropagation()
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) {
@ -593,7 +601,9 @@ export default {
var toolType = this.measuredTools[m]
const toolState = ToolStateManager.getImageIdToolState(image.imageId, toolType)
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'))
if (i > -1 && this.disabledMarks.length > 0 && this.disabledMarks.indexOf(toolState.data[i].remark) > -1) {
return true
@ -783,7 +793,9 @@ export default {
var toolType = this.measuredTools[m]
const toolState = ToolStateManager.getImageIdToolState(e.detail.image.imageId, toolType)
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'))
if (i > -1) {
@ -1019,7 +1031,9 @@ export default {
var toolType = this.measuredTools[t]
const toolState = ToolStateManager.getImageIdToolState(e.detail.image.imageId, toolType)
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'))
if (i > -1) {
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.data.active = false
var criterionType = parseInt(localStorage.getItem('CriterionType'))
if (criterionType === 21) {
if (criterionType === 21 || criterionType === 22) {
measureData.tableQuestionId = this.measureData[idx].TableQuestionId
}
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) {
return new Promise(resolve => {
this.isInitWwwc = true
@ -1134,7 +1157,8 @@ export default {
// resolve()
// })
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])
.then(async image => {
if (this.stack.imageIds.indexOf(image.imageId) !== -1) {
@ -1168,8 +1192,22 @@ export default {
// Add the tool
const toolName = toolBtn.getAttribute('data-tool')
const apiTool = cornerstoneTools[`${toolName}Tool`]
if (apiTool) {
const toolAlreadyAddedToElement = cornerstoneTools.getToolForElement(element, apiTool)
let toolClass = 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 (toolName === 'Length') {
@ -1182,8 +1220,10 @@ export default {
cornerstoneTools.addToolForElement(element, ArrowAnnotateTool, { configuration: { allowEmptyLabel: true, handleRadius: false, drawHandlesOnHover: true, hideHandlesIfMoving: true } })
} else if (toolName === 'RectangleRoi') {
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 } })
} 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 {
cornerstoneTools.addToolForElement(element, apiTool)
}
@ -1527,7 +1567,7 @@ export default {
measureData.wc = Math.round(viewport.voi.windowCenter)
measureData.data.active = false
var criterionType = parseInt(localStorage.getItem('CriterionType'))
if (criterionType === 21) {
if (criterionType === 21 || criterionType === 22) {
measureData.tableQuestionId = this.measureData[idx].TableQuestionId
}
this.$emit('modifyMeasureData', { measureData, questionInfo })
@ -1981,7 +2021,8 @@ export default {
'CobbAngle',
'Angle',
'Bidirectional',
'FreehandRoi'
'FreehandRoi',
'FixedCircleRoi'
]
for (let i = 0; i < toolROITypes.length; i++) {
const toolROIType = toolROITypes[i]

View File

@ -492,6 +492,10 @@
:question-form-change-state="questionFormChangeState" :question-form-change-num="questionFormChangeNum"
:is-show="isShow" :is-reading-show-subject-info="isReadingShowSubjectInfo"
@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">
Developing...
</h2>
@ -539,7 +543,7 @@
</el-tab-pane>
<!-- 其他 -->
<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-tabs>
@ -689,6 +693,7 @@ import LuganoWithoutPETQuestionList from './LuganoWithoutPET/QuestionList'
import IVUSList from './IVUS/QuestionList'
import OCTList from './OCT/QuestionList'
import MRIPDFF from './MRIPDFF/QuestionList'
import MRIPDFFAdvance from './MRIPDFFAdvance/QuestionList'
import CustomWwwcForm from './CustomWwwcForm'
import Manuals from './Manuals'
import Hotkeys from './Hotkeys'
@ -725,6 +730,7 @@ export default {
IVUSList,
OCTList,
MRIPDFF,
MRIPDFFAdvance,
'download-dicom-and-nonedicom': downloadDicomAndNonedicom,
'upload-dicom-and-nonedicom': uploadDicomAndNonedicom,
SignForm
@ -1012,6 +1018,10 @@ export default {
this.measuredTools = [{
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.colorList[0] = ''
@ -1041,30 +1051,23 @@ export default {
console.log('getMeasureData')
})
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)
if (!measuredData.isMarked) {
callback()
return
}
setTimeout(async () => {
// 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)
}, 50)
}
if (!measuredData || !measuredData.isMarked) {
callback()
return
}
const base64Str = await this.captureActiveViewportScreenshot()
callback(base64Str)
})
DicomEvent.$on('imageLocation', async (measuredData) => {
return new Promise(async resolve => {
if (!measuredData) return
await this.imageLocation(measuredData)
console.log('imageLocation')
resolve()
})
})
@ -1126,7 +1129,12 @@ export default {
DicomEvent.$on('addAnnotation', async obj => {
this.tmpData = Object.assign({}, obj.question)
// await this.imageLocation(obj.locateInfo)
this.setToolActive('Probe', true, null, 'tableQuestion')
if (this.CriterionType === 21) {
this.setToolActive('Probe', true, null, 'tableQuestion')
} else if (this.CriterionType === 22) {
this.setToolActive('Probe', true, null, 'tableQuestion')
}
})
window.addEventListener('beforeunload', () => {
if (this.petctWindow) {
@ -1230,7 +1238,7 @@ export default {
async getHotKeys() {
// const loading = this.$loading({ fullscreen: true })
try {
const res = await getDoctorShortcutKey({ imageToolType: 0 })
const res = await getDoctorShortcutKey({ imageToolType: 1 })
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 })
})
@ -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) {
if (obj.readingTaskState < 2 && obj.markTool && !obj.isMarked) {
if (this.activeTool) {
@ -2160,7 +2205,7 @@ export default {
},
//
setMeasureData(data) {
if (this.CriterionType === 21) {
if (this.CriterionType === 21 || this.CriterionType === 22) {
if (this.tmpData) {
data.tableQuestionId = this.tmpData.Id
data.tableQuestionMark = this.tmpData.QuestionMark
@ -2174,7 +2219,7 @@ export default {
},
//
modifyMeasureData(data) {
if (this.CriterionType === 21 && data.measureData.tableQuestionId) {
if ((this.CriterionType === 21 || this.CriterionType === 22) && data.measureData.tableQuestionId) {
this.$refs['measurementList'].modifyMeasuredData(data)
} else {
this.$refs['measurementList'].modifyMeasuredData(data)
@ -2341,7 +2386,7 @@ export default {
this.readingTaskState = 2
await store.dispatch('reading/setVisitTaskReadingTaskState', { visitTaskId: this.visitTaskId, readingTaskState: 2 })
await store.dispatch('reading/setCurrentReadingTaskState', 2)
const res = await getAutoCutNextTask()
const res = await getAutoCutNextTask({imageToolType: 1})
var isAutoTask = res.Result.AutoCutNextTask
if (isAutoTask) {
window.location.reload()
@ -2928,12 +2973,12 @@ export default {
}
.series-table {
::v-deep.el-table {
::v-deep .el-table {
background-color: #1e1e1e !important;
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 {
border-bottom: 1px solid #dfdfdf;
}
@ -2944,14 +2989,14 @@ export default {
background-color: #1e1e1e;
}
::v-deep.el-table__header-wrapper {
::v-deep .el-table__header-wrapper {
th {
background-color: #1e1e1e !important;
color: #dfdfdf;
}
}
::v-deep.el-table__body-wrapper {
::v-deep .el-table__body-wrapper {
tr {
background-color: #1e1e1e !important;
color: #dfdfdf;
@ -2962,7 +3007,7 @@ export default {
}
}
::v-deep.el-table__empty-block {
::v-deep .el-table__empty-block {
background-color: #1e1e1e !important;
}
}

View File

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

View File

@ -172,6 +172,13 @@ export default {
beforeDestroy() {
},
methods: {
getQuestionFormRef(refName) {
const ref = this.$refs[refName]
if (!ref) {
return null
}
return Array.isArray(ref) ? ref[0] : ref
},
handleReadingChart(e) {
this.$emit('handleReadingChart', e)
},
@ -191,7 +198,8 @@ export default {
this.tableQuestions.forEach(item => {
item.TableQuestions.Answers.forEach(i => {
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 => {
item.TableQuestions.Answers.forEach(i => {
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 => {
item.TableQuestions.Answers.forEach(i => {
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 questionId = this.unSaveTargets[0].questionId
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') {
return { isCanActiveTool: true, reason: '' }
} else {
@ -522,7 +533,8 @@ export default {
if (item.TableQuestions && item.TableQuestions.Answers) {
item.TableQuestions.Answers.map(t => {
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)
arr.push({ lessionName: lessionName, rowIndex: t.RowIndex, questionId: item.Id })
}
@ -552,11 +564,15 @@ export default {
this.activeItem.activeCollapseId = arr[0]
this.$nextTick(() => {
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
if (this.$refs[refName][0].questionForm.IsDicomReading !== false) {
const questionFormRef = this.getQuestionFormRef(refName)
if (!questionFormRef) {
return
}
if (questionFormRef.questionForm.IsDicomReading !== false) {
var markTool = 'CircleROI'
var isMarked = !!this.$refs[refName][0].questionForm.OtherMeasureData
var isMarked = !!questionFormRef.questionForm.OtherMeasureData
}
FusionEvent.$emit('imageLocation', { otherMeasureData: this.$refs[refName][0].questionForm.OtherMeasureData, markTool, isMarked })
FusionEvent.$emit('imageLocation', { otherMeasureData: questionFormRef.questionForm.OtherMeasureData, markTool, isMarked })
})
} else {
this.activeItem.activeRowIndex = null
@ -576,7 +592,10 @@ export default {
},
collapseRightClick(e, obj, 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.stopPropagation()
@ -621,8 +640,12 @@ export default {
this.activeName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
this.$nextTick(() => {
const questionFormRef = this.getQuestionFormRef(refName)
if (!questionFormRef) {
return
}
if (deleteInfo) {
this.$refs[refName][0].setDeleteInfo(deleteInfo)
questionFormRef.setDeleteInfo(deleteInfo)
}
if (questionsObj.otherMeasureData) {
const measureData = {}
@ -631,7 +654,7 @@ export default {
measureData.type = questionsObj.otherMeasureData.metadata.toolName
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.activeName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
if (this.$refs[refName]) {
this.$refs[refName][0].setMeasureData(measureObj.measureData)
const questionFormRef = this.getQuestionFormRef(refName)
if (questionFormRef) {
questionFormRef.setMeasureData(measureObj.measureData)
}
}
},
clearMeasuredData() {
if (this.activeItem.activeCollapseId && this.activeItem.activeRowIndex && this.activeName) {
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
this.$refs[refName][0].clearMeasurement()
const questionFormRef = this.getQuestionFormRef(refName)
if (questionFormRef) {
questionFormRef.clearMeasurement()
}
}
},
setOutsideMeasuredData(annotation) {
if (this.activeItem.activeCollapseId && this.activeItem.activeRowIndex && this.activeName) {
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(() => {
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
if (!this.$refs[refName][0].questionForm.OtherMeasureData) {
this.$refs[refName][0].setMeasureData(measureData)
const questionFormRef = this.getQuestionFormRef(refName)
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 {
if (this.isBaseLineTask) {
var idx = this.tableQuestions.findIndex(item => item.LesionType === 0)

View File

@ -6,7 +6,7 @@
>
<div :id="`viewport${index}`" ref="viewportCanvas" style="height: 100%;width:100%" />
<!-- 序列信息 -->
<div
<div
v-if="seriesInfo.taskBlindName"
class="seriesInfo_wrapper"
:style="{color:index%2 == 1 ? '#ddd' : '#666'}"
@ -19,6 +19,8 @@
</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 === 3">{{ ctSeriesInfo ? ctSeriesInfo.modality + ' / ' + seriesInfo.modality : seriesInfo.modality }}</div>
<div v-if="index === 4">MIP</div>
</div>
<!-- 描述信息 -->
<div
@ -79,7 +81,7 @@
{{ markers.left }}
</div>
<div v-if="presetName" class="color_bar">
<canvas id="colorBar_Canvas" />
<canvas ref="colorBarCanvas" />
</div>
<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)" />
@ -135,6 +137,12 @@ export default {
return {}
}
},
ctSeriesInfo: {
type: Object,
default() {
return null
}
},
renderingEngineId: {
type: String,
required: true
@ -154,6 +162,10 @@ export default {
default() {
return []
}
},
rgbPresetName: {
type: String,
default: ''
}
},
data() {
@ -195,12 +207,21 @@ export default {
handler(v) {
console.log('activeIndex ', v)
}
},
rgbPresetName: {
immediate: true,
handler(v) {
this.presetName = v || ''
if (!this.presetName) return
this.$nextTick(() => {
this.renderColorBar(this.presetName)
})
}
}
},
mounted() {
const digitPlaces = parseInt(this.$route.query.digitPlaces)
this.digitPlaces = digitPlaces === -1 ? 2 : digitPlaces
console.log(toolsUtilities)
this.subjectCode = this.$route.query.subjectCode
var element = document.getElementById(`viewport${this.index}`)
element.addEventListener(VOLUME_NEW_IMAGE, this.handleVolumeNewImage)
@ -265,6 +286,24 @@ export default {
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() {
// eslint-disable-next-line no-unused-vars
@ -328,7 +367,7 @@ export default {
let properties = viewport.getProperties()
if (this.index === 3) {
// const volumeId = viewport.getVolumeId()
const volumeId = `cornerstoneStreamingImageVolume:PT_VOLUME_ID`
const volumeId = `cornerstoneStreamingImageVolume:PT_VOLUME_ID2`
properties = viewport.getProperties(volumeId)
}
if (properties && properties.voiRange) {
@ -338,6 +377,10 @@ export default {
upper
)
this.imageInfo.wwwc = `${Math.round(windowWidth)}/${Math.round(windowCenter)}`
// PTMIP (CT)
if (this.index !== 1) {
this.$emit('upperRangeChange', Math.round(upper))
}
}
},
handleMouseMove(e) {
@ -545,7 +588,8 @@ export default {
colorMap = getColormap(presetName)
}
const rgbPoints = colorMap.RGBPoints
const canvas = document.getElementById('colorBar_Canvas')
const canvas = this.$refs.colorBarCanvas
if (!canvas) return
const ctx = canvas.getContext('2d')
const canvasWidth = 160
const canvasHeight = 5
@ -567,10 +611,17 @@ export default {
ctx.fillStyle = gradient
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() {
let properties = viewport.getProperties()
if (this.index === 3) {
properties = viewport.getProperties(`cornerstoneStreamingImageVolume:PT_VOLUME_ID`)
properties = viewport.getProperties(`cornerstoneStreamingImageVolume:PT_VOLUME_ID2`)
}
if (properties && properties.voiRange) {
var { lower, upper } = properties.voiRange
@ -582,7 +633,10 @@ export default {
upper
)
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() {

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