452 Commits

Author SHA1 Message Date
wangxiaoshuang 47d7fb00e2 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
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
2026-04-14 13:05:18 +08:00
wangxiaoshuang 5a7fa1fb77 分割部分问题解决
continuous-integration/drone/push Build is passing
2026-04-09 18:08:20 +08:00
wangxiaoshuang 5289c156c0 1
continuous-integration/drone/push Build is passing
2026-04-09 17:28:59 +08:00
wangxiaoshuang a68c27fd43 分割部分问题解决
continuous-integration/drone/push Build is running
2026-04-09 17:19:12 +08:00
wangxiaoshuang 641c2e6ade 直方图取值及样式修改
continuous-integration/drone/push Build is passing
2026-04-08 16:22:29 +08:00
wangxiaoshuang 5b4cc097bc 分割视图添加直方图
continuous-integration/drone/push Build is passing
2026-04-08 14:58:26 +08:00
wangxiaoshuang b1de662677 aws插件版本问题
continuous-integration/drone/push Build is passing
2026-04-07 17:11:04 +08:00
caiyiling d2d1063ed5 数据同步模块更改
continuous-integration/drone/push Build is passing
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
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
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
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
2026-04-03 13:45:02 +08:00
caiyiling 769c5f8440 任务级别列表更改
continuous-integration/drone/push Build is running
2026-04-03 13:42:14 +08:00
caiyiling 6321bddfd3 数据同步更改
continuous-integration/drone/push Build is passing
2026-04-03 13:33:46 +08:00
caiyiling e30fb2a041 1
continuous-integration/drone/push Build is passing
2026-04-02 17:37:24 +08:00
caiyiling 478adcde21 1
continuous-integration/drone/push Build is running
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
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
2026-04-02 15:01:32 +08:00
wangxiaoshuang d317402f85 1
continuous-integration/drone/push Build is passing
2026-04-02 14:40:24 +08:00
wangxiaoshuang 6cb63bdc59 浏览器窗口大小调整后,渲染的标记、比例尺等位置偏移
continuous-integration/drone/push Build is failing
2026-04-02 14:36:15 +08:00
wangxiaoshuang 8db641db16 分割重命名时默认值填入
continuous-integration/drone/push Build is failing
2026-04-02 14:07:58 +08:00
wangxiaoshuang 9da9515930 aws插件版本升级3.7.4=>3.726
continuous-integration/drone/push Build is passing
2026-04-02 09:33:38 +08:00
wangxiaoshuang 2be119de6b 分割橡皮檫国际化
continuous-integration/drone/push Build is passing
2026-04-01 15:23:20 +08:00
wangxiaoshuang c9f9615488 mpr切换单视口时分割列表报错问题
continuous-integration/drone/push Build is passing
2026-04-01 14:53:14 +08:00
wangxiaoshuang be137b3840 分割字段变更SegmentMumber=>SegmentNumber
continuous-integration/drone/push Build is passing
2026-04-01 12:21:07 +08:00
wangxiaoshuang d80101f86b 分割标记与分割段绑定错误问题
continuous-integration/drone/push Build is passing
2026-04-01 11:30:59 +08:00
wangxiaoshuang 3c0b084f7e 分割文件保存分割段与分割标记不一致问题
continuous-integration/drone/push Build is passing
2026-04-01 09:58:45 +08:00
wangxiaoshuang 8a88a64c36 比例尺页面放大缩小报错问题
continuous-integration/drone/push Build is failing
2026-04-01 09:35:36 +08:00
wangxiaoshuang 3d538d6e67 1
continuous-integration/drone/push Build is passing
2026-03-31 17:59:10 +08:00
wangxiaoshuang 67d2a83ec8 1
continuous-integration/drone/push Build is passing
2026-03-31 17:38:36 +08:00
wangxiaoshuang 2efb229c96 1
continuous-integration/drone/push Build is passing
2026-03-31 17:13:29 +08:00
wangxiaoshuang c930480336 体数据放大变形问题解决
continuous-integration/drone/push Build is passing
2026-03-31 16:47:16 +08:00
wangxiaoshuang b39ea3a032 分割标记数据值计算完成后才可保存
continuous-integration/drone/push Build is passing
2026-03-31 16:30:37 +08:00
wangxiaoshuang ff5aea17a5 样式修改
continuous-integration/drone/push Build is passing
2026-03-31 15:43:34 +08:00
wangxiaoshuang 477b6935ff 分割窗口放大页面跳转与分割片段默认跳转功能互不干扰
continuous-integration/drone/push Build is running
2026-03-31 15:41:45 +08:00
wangxiaoshuang 8b6432ad14 分割标记长短径的小数位数需要一致
continuous-integration/drone/push Build is passing
2026-03-31 15:23:23 +08:00
wangxiaoshuang ca35fe6df4 部分问题修复
continuous-integration/drone/push Build is passing
2026-03-31 15:17:13 +08:00
wangxiaoshuang 307c02725f 分割对象的体素统计以及长短径计算优化、分割组添加状态
continuous-integration/drone/push Build is passing
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
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
2026-03-30 17:33:52 +08:00
wangxiaoshuang c5c9e4364c 【dicom分割】分段菜单按钮点击后,再点击另一个分段菜单按钮,上一个展示的弹窗应该消失
continuous-integration/drone/push Build is passing
2026-03-30 17:17:17 +08:00
wangxiaoshuang 920d0b21c6 【dicom分割】当前分组选择为分组2,分组2没有标记数据,当切入mpr模式时,此时显示了分组1的标记,这里影像标记应该与右侧分割选择组同步
continuous-integration/drone/push Build is passing
2026-03-30 16:50:25 +08:00
wangxiaoshuang 3fe16b4166 体数据阅片四角信息
continuous-integration/drone/push Build is passing
2026-03-30 16:19:05 +08:00
wangxiaoshuang bc2d5463ff MPR视图的:十字线和窗宽窗位
continuous-integration/drone/push Build is passing
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
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
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
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
# 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
2026-03-30 10:57:27 +08:00
caiyiling fdc2bc5f8a 1
continuous-integration/drone/push Build is running
2026-03-30 10:54:07 +08:00
caiyiling 84f68c79f0 1
continuous-integration/drone/push Build is running
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
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
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
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
2026-03-27 16:40:45 +08:00
wangxiaoshuang b55ffc0821 mpr加载
continuous-integration/drone/push Build is passing
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
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
# 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
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
2026-03-27 10:50:18 +08:00
wangxiaoshuang 740a0aa221 删除分割标记同步问题
continuous-integration/drone/push Build is passing
2026-03-27 10:31:06 +08:00
wangxiaoshuang e8f95d09ba 1
continuous-integration/drone/push Build is passing
2026-03-26 17:49:39 +08:00
wangxiaoshuang 2c305af49e 【dicom分割】删除已绑定问题的分割标记时,eicrf页面没有刷新状态
continuous-integration/drone/push Build is failing
2026-03-26 17:40:22 +08:00
wangxiaoshuang 5c76bda1e7 分割添加阈值工具
continuous-integration/drone/push Build is passing
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
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
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
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
2026-03-25 16:44:55 +08:00
wangxiaoshuang 5336cb5353 分割标记删除进行提示
continuous-integration/drone/push Build is passing
2026-03-25 14:11:52 +08:00
wangxiaoshuang 0a288b9f47 分割标记重命名问题修改
continuous-integration/drone/push Build is passing
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
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
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
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
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
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
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
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
2026-03-24 16:45:44 +08:00
caiyiling 5cb3e8e606 同步调窗更改
continuous-integration/drone/push Build is passing
2026-03-24 15:46:25 +08:00
wangxiaoshuang 619af2e874 分割问题绑定标记显示问题
continuous-integration/drone/push Build is passing
2026-03-24 15:27:17 +08:00
wangxiaoshuang 43f1736b05 1
continuous-integration/drone/push Build is passing
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
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
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
# 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
2026-03-19 16:54:25 +08:00
caiyiling 75d296405b cornerstone版本升级
continuous-integration/drone/push Build is passing
2026-03-19 15:12:54 +08:00
caiyiling 07c92c48a4 融合图像筛选更改
continuous-integration/drone/push Build is passing
2026-03-13 14:11:52 +08:00
hang b5427dd17b 修改打包文件配置
continuous-integration/drone/push Build is passing
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
wangxiaoshuang 18a71725f9 自动化脚本变更
continuous-integration/drone/push Build is passing
2026-03-06 09:44:56 +08:00
wangxiaoshuang 87332ae9d8 Merge branch 'main' into uat
continuous-integration/drone/push Build is passing
2026-03-05 17:53:07 +08:00
caiyiling 401762dca7 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-03-05 17:07:34 +08:00
caiyiling 7b694d4afb 对OCT标准中自动计算类型问题国际化翻译 2026-03-05 17:06:19 +08:00
hang 6e285d900b irc-uat 使用uat分支以uat 模式自动打包发布增加
continuous-integration/drone/push Build is passing
2026-03-05 14:55:01 +08:00
hang c5d3b44c2c irc web 提交自动打包发布uat分支
continuous-integration/drone/push Build was killed
2026-03-05 14:52:28 +08:00
hang cf4dbc2914 irc uat分支,自动打包配置修改 2026-03-05 14:49:41 +08:00
wangxiaoshuang 3cef5afebd logo更改
continuous-integration/drone/push Build is passing
2026-03-05 10:11:50 +08:00
caiyiling 060b61eafb ivus和oct阅片页面样式调整
continuous-integration/drone/push Build is passing
2026-03-04 14:50:02 +08:00
caiyiling 0d3571f672 导入模板更改
continuous-integration/drone/push Build is passing
2026-03-04 13:31:28 +08:00
caiyiling 8b3bd101b6 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-03-04 11:27:38 +08:00
caiyiling da252ae4df 数值问题答案为NE保存bug修复 2026-03-04 11:26:48 +08:00
wangxiaoshuang fdb7251b62 v2.0.0发布遗留问题
continuous-integration/drone/push Build is passing
2026-03-04 11:23:15 +08:00
caiyiling 1f51bbd178 OCT导入测量值获取模板接口变更
continuous-integration/drone/push Build is passing
2026-03-04 10:17:06 +08:00
caiyiling 597ca07fcf Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-03-03 16:50:28 +08:00
caiyiling b43c75dd9f oct表格问题更改 2026-03-03 16:50:02 +08:00
wangxiaoshuang 94f3e6c757 代码合并
continuous-integration/drone/push Build is passing
2026-03-03 14:15:04 +08:00
wangxiaoshuang 1d95cd15f2 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web 2026-03-03 14:14:46 +08:00
wangxiaoshuang a1f9283262 Merge branch 'uat' 2026-03-03 13:54:59 +08:00
caiyiling a0abba71dd 1
continuous-integration/drone/push Build is passing
2026-03-03 12:41:50 +08:00
caiyiling 02d85e19a6 多选问题保存报错更改
continuous-integration/drone/push Build is passing
2026-03-03 11:36:36 +08:00
caiyiling 9ad916f860 表格问题标识更改
continuous-integration/drone/push Build is passing
2026-03-03 11:04:51 +08:00
caiyiling e0094c01d8 1
continuous-integration/drone/push Build is passing
2026-03-03 10:52:59 +08:00
caiyiling 0eb2135185 IVUS数值问题提示语更改
continuous-integration/drone/push Build is running
2026-03-03 10:50:37 +08:00
caiyiling 8ddfe34f72 IVUS标准更改
continuous-integration/drone/push Build is passing
2026-03-03 10:26:02 +08:00
caiyiling c603d10ff5 IVUS & OCT 移除测量值触发自动计算更改
continuous-integration/drone/push Build is passing
2026-03-03 09:41:56 +08:00
caiyiling 3fca2e76cb 自动计算ROI测量值更改
continuous-integration/drone/push Build is passing
2026-03-02 11:13:29 +08:00
caiyiling c0ae4732fc 数值问题更改
continuous-integration/drone/push Build is passing
2026-02-28 13:16:05 +08:00
caiyiling 4ec0a178b4 IVUS & OCT标准更改
continuous-integration/drone/push Build is passing
2026-02-28 12:57:17 +08:00
wangxiaoshuang 4057f1893f 非dicom测量值与比例尺转化问题
continuous-integration/drone/push Build is passing
2026-02-12 20:50:00 -05:00
wangxiaoshuang 5c05a11ec4 阅片人预览文件修改
continuous-integration/drone/push Build is passing
2026-02-10 04:44:30 -05:00
wangxiaoshuang eec23b9d42 登录邮箱问题
continuous-integration/drone/push Build is passing
2026-02-10 02:40:06 -05:00
wangxiaoshuang f7d01f6ad7 部分问题修复
continuous-integration/drone/push Build is passing
2026-02-10 10:31:18 +08:00
wangxiaoshuang cb46f89ee1 DICOM阅片和非DICOM阅片工具,CRF问题与影像标记绑定时,信息明确下
continuous-integration/drone/push Build is passing
2026-02-06 10:45:19 +08:00
wangxiaoshuang b0ad500f7a 90天后未修改密码,跳转到修改账户界面,管理员重置密码跳转界面,不应该为初始化账户信息,应为修改账户信息
continuous-integration/drone/push Build is passing
2026-02-06 10:21:34 +08:00
wangxiaoshuang 0327937f02 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is failing
2026-02-06 09:40:58 +08:00
wangxiaoshuang 2f3eab6119 【质控风险控制】提交质控任务后,提示跳转进入下个任务,跳转到下个任务不会触发风险控制 2026-02-06 09:40:45 +08:00
hang 575840490f git 全局设置userName
continuous-integration/drone/push Build is passing
2026-02-05 20:30:50 +08:00
zhouhang 9b013059ba 继续测试用户名
continuous-integration/drone/push Build is failing
2026-02-05 20:03:43 +08:00
zhouhang 987831ff22 为啥没有用户名?
continuous-integration/drone/push Build is failing
2026-02-05 20:00:13 +08:00
zhouhang 7691fd12ac 全局设置git用户名测试 用户名显示
continuous-integration/drone/push Build is failing
2026-02-05 19:55:40 +08:00
hang 0cecb91984 工程统一前端打包,并增加微信通知测试
continuous-integration/drone/push Build is passing
2026-02-05 19:46:01 +08:00
caiyiling 849142bd23 简历批量下载更改
continuous-integration/drone/push Build is passing
2026-02-05 14:41:28 +08:00
caiyiling 231a993f8b 1
continuous-integration/drone/push Build is passing
2026-01-30 14:36:28 +08:00
caiyiling ab1a2889bc Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is failing
2026-01-30 14:34:17 +08:00
wangxiaoshuang f04f0ce929 非DICOM阅片,切换最右边的访视时,CRF表中的访视名称切换了,但是问题的内容没有切换
continuous-integration/drone/push Build is failing
2026-02-05 10:46:41 +08:00
wangxiaoshuang bc3e00c8b4 重置密码接口修改
continuous-integration/drone/push Build is failing
2026-02-05 10:21:06 +08:00
wangxiaoshuang 953033b768 非dicom阅片鼠标滚动翻页时取消选中工具
continuous-integration/drone/push Build is failing
2026-02-05 10:17:00 +08:00
wangxiaoshuang c337f95693 代码合并
continuous-integration/drone/push Build is failing
2026-02-02 16:06:47 +08:00
wangxiaoshuang d2021a281e Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web 2026-02-02 16:05:05 +08:00
wangxiaoshuang 66ae27cc56 非dicom报告页展示有误 2026-02-02 16:04:52 +08:00
caiyiling 3137bca8f6 标注标记更改
continuous-integration/drone/push Build is passing
2026-02-02 15:08:00 +08:00
wangxiaoshuang e35f6c5471 非dicom阅片更改比例尺弹框去除取消按钮
continuous-integration/drone/push Build is passing
2026-02-02 15:02:41 +08:00
wangxiaoshuang 46b532b858 非dicom长短径工具与问题进行绑定值错误问题
continuous-integration/drone/push Build is passing
2026-02-02 14:06:04 +08:00
wangxiaoshuang 5a8e1e5af7 系统时间的定位,应该根据显示器的分别率可以适配
continuous-integration/drone/push Build is passing
2026-02-02 11:04:44 +08:00
wangxiaoshuang 9b9c83797a 冲突解决
continuous-integration/drone/push Build is passing
2026-02-02 10:26:30 +08:00
wangxiaoshuang 8a4b4fc9fb Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web 2026-02-02 10:19:51 +08:00
wangxiaoshuang 91d8d1797a 非diocm阅片部分问题解决 2026-02-02 10:19:39 +08:00
caiyiling 0d2bfa8d82 1
continuous-integration/drone/push Build is passing
2026-01-30 18:08:55 +08:00
caiyiling 742b60bba2 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is running
2026-01-30 18:06:28 +08:00
caiyiling 536fc7bbc8 1 2026-01-30 18:02:52 +08:00
wangxiaoshuang 6f0f7a7d3a mp4缩略图修改
continuous-integration/drone/push Build is passing
2026-01-30 16:10:42 +08:00
wangxiaoshuang 4ed0feb1da 非dicom阅片修改
continuous-integration/drone/push Build is passing
2026-01-30 16:02:43 +08:00
wangxiaoshuang 45cc714942 阅片修改
continuous-integration/drone/push Build is passing
2026-01-30 15:22:55 +08:00
caiyiling 147b5cadfa 非dicom阅片切换访视ecrf表同步更新 2026-01-30 14:30:10 +08:00
wangxiaoshuang d34f1f462a 阅片规则下拉框显示修改
continuous-integration/drone/push Build is passing
2026-01-30 14:19:49 +08:00
wangxiaoshuang c89aa8bd87 登录页title修改
continuous-integration/drone/push Build is passing
2026-01-30 13:55:05 +08:00
wangxiaoshuang 98324c27e6 1
continuous-integration/drone/push Build is passing
2026-01-30 10:56:47 +08:00
wangxiaoshuang ae1014c70b crc上传添加MP4预览
continuous-integration/drone/push Build is passing
2026-01-30 09:38:05 +08:00
wangxiaoshuang 7dc7236c16 阅片人筛选列表查询修改
continuous-integration/drone/push Build is passing
2026-01-29 16:30:49 +08:00
wangxiaoshuang b7a5015e21 1
continuous-integration/drone/push Build is passing
2026-01-29 11:38:33 +08:00
wangxiaoshuang e491265bb4 1
continuous-integration/drone/push Build is passing
2026-01-28 17:48:45 +08:00
wangxiaoshuang 8a4a035b2c 添加影像指控风险配置字段
continuous-integration/drone/push Build is passing
2026-01-28 16:10:54 +08:00
wangxiaoshuang 4e0fbb3f31 邮件配置发件人禁止编辑
continuous-integration/drone/push Build is passing
2026-01-28 15:11:23 +08:00
wangxiaoshuang d44aeac36d 肿瘤学阅片跳过按钮显示修改
continuous-integration/drone/push Build is passing
2026-01-28 13:22:11 +08:00
wangxiaoshuang f6eb5b8cd5 暂时去除i18n
continuous-integration/drone/push Build is passing
2026-01-28 10:58:18 +08:00
wangxiaoshuang 71e00ecf5b Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2026-01-27 15:05:05 +08:00
wangxiaoshuang fdc2572477 时间样式调整 2026-01-27 15:04:51 +08:00
caiyiling 7ab9d0f285 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-01-27 13:50:28 +08:00
caiyiling 64b8214985 1 2026-01-27 13:50:14 +08:00
wangxiaoshuang ba6bcebe1f 添加悬浮时间
continuous-integration/drone/push Build is passing
2026-01-26 17:08:01 +08:00
wangxiaoshuang 8703b28470 新增更新缩略图功能 2026-01-26 11:41:11 +08:00
caiyiling dc8fb450fb 非DICOM页面添加关键图像和标注图像的标识,阅片工具添加定圆工具
continuous-integration/drone/push Build is passing
2026-01-23 15:40:50 +08:00
wangxiaoshuang 377cdb968d 实施阅片预览
continuous-integration/drone/push Build is passing
2026-01-23 10:15:35 +08:00
wangxiaoshuang 8c84cb5101 1
continuous-integration/drone/push Build is passing
2026-01-23 09:25:59 +08:00
wangxiaoshuang 5a0cc41d5a 非必填的CRF问题,影像标记为非必选。如果画了标记,没有保存时,访视可以提交。提交后,值存在,标记不存在
continuous-integration/drone/push Build is passing
2026-01-22 17:36:33 +08:00
wangxiaoshuang 86a1312b60 dicom阅片添加椭圆工具
continuous-integration/drone/push Build is passing
2026-01-22 16:57:24 +08:00
wangxiaoshuang a3ca9fe4d0 【实时报告】受试者页面发送实时报告,阅片标准未显示,发送报告时报错
continuous-integration/drone/push Build is passing
2026-01-22 15:44:26 +08:00
wangxiaoshuang c6770e71a5 1
continuous-integration/drone/push Build is passing
2026-01-21 15:44:22 +08:00
wangxiaoshuang c70970a6f6 非dicom长短径问题修改
continuous-integration/drone/push Build is passing
2026-01-21 10:23:03 +08:00
wangxiaoshuang d059d016a6 下载文件中文乱码问题
continuous-integration/drone/push Build is passing
2026-01-19 17:51:11 +08:00
wangxiaoshuang 44519c5f8e 【阅片单元】当阅片模式选择完全随机和受试者内随机时,禁用存在阅片期、全局阅片、肿瘤学阅片,且全部置为否,除了PCWG
continuous-integration/drone/push Build is passing
2026-01-19 16:59:08 +08:00
wangxiaoshuang f3a4a7cd09 分别支持PET/CT和PET/MR融合
continuous-integration/drone/push Build is passing
2026-01-19 16:40:43 +08:00
wangxiaoshuang 463e4728d0 mpr和nm时切换访视视口切换为单视口
continuous-integration/drone/push Build is passing
2026-01-19 16:34:10 +08:00
wangxiaoshuang 5d04fb869d 阅片单元阅片工具的测量工具选择后,保存阅片规则。但是在CRF配置时,不能选择选择的影像工具,需要刷新页面内后才行
continuous-integration/drone/push Build is passing
2026-01-19 16:00:26 +08:00
wangxiaoshuang ec83609139 下载文件支持zip64
continuous-integration/drone/push Build is passing
2026-01-19 15:53:11 +08:00
wangxiaoshuang a5824cecac 影像无法阅片问题修复
continuous-integration/drone/push Build is passing
2026-01-17 12:21:44 +08:00
wangxiaoshuang 416f8f82d6 修复阅片页面影像缺失像素间的距离tag导致测量异常(暂时退回)
continuous-integration/drone/push Build is passing
2026-01-17 11:24:04 +08:00
wangxiaoshuang f0a7090ab4 阅片临床数据问题
continuous-integration/drone/push Build is passing
2026-01-17 00:10:31 +08:00
wangxiaoshuang a13ddf70b4 邮件通知更新调研表去除更新调研表按钮
continuous-integration/drone/push Build is passing
2026-01-16 17:47:20 +08:00
wangxiaoshuang ecf4c74259 非DICOM阅片工具,增加图像标识信息
continuous-integration/drone/push Build is passing
2026-01-16 15:40:00 +08:00
wangxiaoshuang e712333a79 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2026-01-16 11:36:25 +08:00
wangxiaoshuang 6900cc8a25 浏览器指纹校验 2026-01-16 11:36:10 +08:00
caiyiling d5acb0a27c Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-01-16 10:31:23 +08:00
wangxiaoshuang 0f845df8dd CRC在影像上传时录入的是英文的部位,但是在Study页面和Reading页面显示的部位却为中文
continuous-integration/drone/push Build is running
2026-01-16 10:29:57 +08:00
caiyiling 0b2cdd12a7 修复阅片页面影像缺失像素间的距离tag导致测量异常 2026-01-16 10:29:37 +08:00
wangxiaoshuang 785ca4ad47 非dicom阅片检查部位翻译问题
continuous-integration/drone/push Build is passing
2026-01-15 16:54:29 +08:00
wangxiaoshuang 20ad1c894a DICOM上传影像页面,预览本地影像的页面,增加标题
continuous-integration/drone/push Build is passing
2026-01-15 16:16:54 +08:00
wangxiaoshuang 95aa3e0be7 【受试者】受试者-受试者页面,操作按钮趋势图悬浮提示语错误
continuous-integration/drone/push Build is passing
2026-01-15 16:12:51 +08:00
wangxiaoshuang 46fdee6c01 SPM发送有限让中心人员更新人员信息,中心人员的邮件中进入中心调研链接页面,交互错误
continuous-integration/drone/push Build is running
2026-01-15 16:10:58 +08:00
wangxiaoshuang f997cec4fb 非DICOM阅片工具支持测量与标注
continuous-integration/drone/push Build is passing
2026-01-15 16:02:19 +08:00
caiyiling 1586643fc2 自定义阅片分类问题自动计算bug修复
continuous-integration/drone/push Build is passing
2026-01-15 12:26:24 +08:00
wangxiaoshuang 5ad0da7a4a 阅片单元配置外层问题ABS无法选择
continuous-integration/drone/push Build is passing
2026-01-15 10:56:45 +08:00
wangxiaoshuang 16c0e84ca0 阅片问题处理
continuous-integration/drone/push Build is passing
2026-01-14 16:56:48 +08:00
wangxiaoshuang e58fbdcab6 非dicom阅片添加视频查看
continuous-integration/drone/push Build is passing
2026-01-08 15:17:18 +08:00
caiyiling e17a89e151 管理后台国际化补充
continuous-integration/drone/push Build is passing
2026-01-08 12:34:47 +08:00
caiyiling bf35797282 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web into main
continuous-integration/drone/push Build is passing
2026-01-07 13:31:42 +08:00
caiyiling 21bdd0b07e 菜单国际化更改 2026-01-07 13:31:19 +08:00
wangxiaoshuang 47798750a4 项目配置不显示图表后去除项目报表tab
continuous-integration/drone/push Build is passing
2026-01-07 11:31:37 +08:00
wangxiaoshuang 8d1d5cb43a 图像不需要查看原始数据,数据显示不对
continuous-integration/drone/push Build is passing
2026-01-07 10:39:18 +08:00
wangxiaoshuang ef08a5277d 1
continuous-integration/drone/push Build is passing
2026-01-06 13:46:30 +08:00
wangxiaoshuang 98a3f9c5b5 受试者图表只显示肿瘤标准
continuous-integration/drone/push Build is passing
2026-01-06 13:36:38 +08:00
wangxiaoshuang a0b39e7a0b 项目报表修改
continuous-integration/drone/push Build is passing
2026-01-06 11:06:11 +08:00
wangxiaoshuang 1191b97688 临床数据为空时下载模板按钮置灰
continuous-integration/drone/push Build is passing
2026-01-05 17:13:02 +08:00
wangxiaoshuang c662e6cc3a 项目报表PCWG3去除ORR
continuous-integration/drone/push Build is passing
2026-01-05 16:37:48 +08:00
wangxiaoshuang 30b5ee24a0 项目报表质疑统计接口添加项目I查询字段
continuous-integration/drone/push Build is passing
2026-01-05 15:54:56 +08:00
wangxiaoshuang 2781d02afa 1
continuous-integration/drone/push Build is passing
2026-01-05 15:41:11 +08:00
wangxiaoshuang 034f9528e5 Merge branch 'main' of https://gitea.frp.extimaging.com/XCKJ/irc_web
continuous-integration/drone/push Build is passing
2026-01-05 15:24:07 +08:00
wangxiaoshuang d8712f26cd 项目报表添加ORR分布、项目报表添加权限 2026-01-05 15:23:30 +08:00
caiyiling feab37c4ac 1
continuous-integration/drone/push Build is passing
2026-01-05 09:32:50 +08:00
wangxiaoshuang 443a31df97 1
continuous-integration/drone/push Build encountered an error
2026-01-04 16:38:53 +08:00
wangxiaoshuang 99955b2eff 报告页面:问题值出现了 【Object object】的字段
continuous-integration/drone/push Build is passing
2026-01-04 13:13:07 +08:00
wangxiaoshuang e36ae0404d 1
continuous-integration/drone/push Build is passing
2025-12-31 16:09:03 +08:00
wangxiaoshuang 65edc78152 项目报表柱状图上显示数字
continuous-integration/drone/push Build is passing
2025-12-31 15:56:54 +08:00
wangxiaoshuang 78f2f530c4 影像质控页面,列表下方是空白的。应该是需要铺满底部。
continuous-integration/drone/push Build is passing
2025-12-31 15:51:40 +08:00
wangxiaoshuang fdc23361f9 磁共振的图像在做MPR时,重建的体数据,在各个轴向上的图像数量不正确
continuous-integration/drone/push Build is running
2025-12-31 15:50:01 +08:00
wangxiaoshuang b4db6b4990 mRECIST HCC标准在评估报告未通过验证时,弹了签名框
continuous-integration/drone/push Build is passing
2025-12-31 11:29:10 +08:00
wangxiaoshuang a45ab4edf8 提示框优化
continuous-integration/drone/push Build is passing
2025-12-31 11:04:18 +08:00
wangxiaoshuang 2f20696827 Lugano2014病灶使用箭头工具时设置长短经为0
continuous-integration/drone/push Build is passing
2025-12-31 10:53:53 +08:00
wangxiaoshuang 2ffffd2bf0 部分问题修复
continuous-integration/drone/push Build is passing
2025-12-31 10:09:53 +08:00
wangxiaoshuang d21c01076d 阅片跟踪添加靶段标注
continuous-integration/drone/push Build encountered an error
2025-12-30 17:05:27 +08:00
wangxiaoshuang 8d0f0094ba 图表修改
continuous-integration/drone/push Build is passing
2025-12-30 15:25:01 +08:00
wangxiaoshuang 08dba4b364 项目汇总图表需要优化
continuous-integration/drone/push Build is passing
2025-12-30 14:00:40 +08:00
wangxiaoshuang 2716b379b5 项目的邮件模板中,增加查询条件:邮件主题
continuous-integration/drone/push Build is passing
2025-12-30 13:22:34 +08:00
wangxiaoshuang 5216bf9f62 受试者页面添加图表查看入口
continuous-integration/drone/push Build is passing
2025-12-30 11:44:10 +08:00
wangxiaoshuang 040814c3d6 项目报表修改
continuous-integration/drone/push Build is passing
2025-12-30 10:24:50 +08:00
wangxiaoshuang 264f00f5a8 1
continuous-integration/drone/push Build is passing
2025-12-30 09:16:31 +08:00
wangxiaoshuang 3f48bc0ba5 项目报表修改
continuous-integration/drone/push Build is passing
2025-12-29 16:01:24 +08:00
wangxiaoshuang 2c47541737 电脑及浏览器兼容性检测
continuous-integration/drone/push Build is passing
2025-12-29 14:53:28 +08:00
wangxiaoshuang 0c2ed13cd4 【LiLi & EICS】项目数据汇总统计分析
continuous-integration/drone/push Build is passing
2025-12-29 13:55:00 +08:00
wangxiaoshuang f1c36ea688 阅片更改
continuous-integration/drone/push Build is passing
2025-12-29 13:11:34 +08:00
wangxiaoshuang 9568186497 中心调研退出调用接口 2025-12-29 13:11:15 +08:00
wangxiaoshuang a48dc114e8 部分问题修复
continuous-integration/drone/push Build is passing
2025-12-29 09:13:58 +08:00
wangxiaoshuang abb224a8fb Merge branch 'uat'
continuous-integration/drone/push Build is failing
# Conflicts:
#	src/api/reading.js
#	src/api/trials.js
#	src/components/PreviewFile/index.vue
#	src/views/dictionary/template/components/CriterionQuestionForm.vue
#	src/views/dictionary/template/components/TableQsForm.vue
#	src/views/dictionary/template/email/components/EmailList.vue
#	src/views/trials/trials-panel/reading/dicoms/components/DicomViewer.vue
#	src/views/trials/trials-panel/reading/dicoms/components/IVUS/QuestionTableFormItem.vue
#	src/views/trials/trials-panel/reading/dicoms/components/OCT/QuestionList.vue
#	src/views/trials/trials-panel/reading/dicoms/components/OCT/QuestionTableFormItem.vue
#	src/views/trials/trials-panel/reading/dicoms/components/QuestionItem.vue
#	src/views/trials/trials-panel/reading/dicoms/components/Questions.vue
#	src/views/trials/trials-panel/reading/dicoms/components/ReadPage.vue
#	src/views/trials/trials-panel/reading/dicoms/components/Recist/QuestionForm.vue
#	src/views/trials/trials-panel/reading/dicoms/components/Recist/QuestionList.vue
#	src/views/trials/trials-panel/reading/dicoms/components/ReportPage.vue
#	src/views/trials/trials-panel/reading/dicoms3D/components/ReadPage.vue
#	src/views/trials/trials-panel/reading/dicoms3D/components/Recist/QuestionFormItem.vue
#	src/views/trials/trials-panel/reading/dicoms3D/components/Recist/QuestionList.vue
#	src/views/trials/trials-panel/reading/dicoms3D/components/Recist/TableQuestionFormItem.vue
#	src/views/trials/trials-panel/reading/dicoms3D/components/ReportPage.vue
#	src/views/trials/trials-panel/reading/dicoms3D/components/customize/QuestionFormItem.vue
#	src/views/trials/trials-panel/reading/dicoms3D/components/customize/QuestionList.vue
#	src/views/trials/trials-panel/reading/dicoms3D/components/customize/QuestionTableFormItem.vue
#	src/views/trials/trials-panel/reading/mim-medical-audit/components/GlobalReview.vue
#	src/views/trials/trials-panel/reading/reading-task/index.vue
#	src/views/trials/trials-panel/reading/reading-tracking/components/exportList.vue
#	src/views/trials/trials-panel/setting/reading-unit/components/TableQsForm.vue
2025-12-26 17:09:35 +08:00
wangxiaoshuang 754fe5f797 1
continuous-integration/drone/push Build is passing
2025-12-26 15:48:40 +08:00
wangxiaoshuang f72cab082a 中心调研表配置样式更改
continuous-integration/drone/push Build is passing
2025-12-26 15:27:46 +08:00
wangxiaoshuang 03c7aa3d99 1
continuous-integration/drone/push Build is passing
2025-12-26 15:08:41 +08:00
wangxiaoshuang 24c7c61398 1
continuous-integration/drone/push Build is passing
2025-12-26 15:00:51 +08:00
wangxiaoshuang 281c900e0c 阅片单元的基础数据中,字典需要有描述
continuous-integration/drone/push Build is passing
2025-12-26 14:51:25 +08:00
wangxiaoshuang 63772f46f8 【邮件管理】后台邮件管理中,是否加急筛选项中选项应该是紧急、普通加急、不加急
continuous-integration/drone/push Build is passing
2025-12-26 14:45:22 +08:00
wangxiaoshuang accac374ff 中心调研表的编辑页面,页面布局需要调整
continuous-integration/drone/push Build is passing
2025-12-26 14:42:27 +08:00
wangxiaoshuang 4ee7906dcd 被融合病灶不允许画标记
continuous-integration/drone/push Build is passing
2025-12-26 14:14:55 +08:00
wangxiaoshuang 3dc99d096d 部分问题修复
continuous-integration/drone/push Build is passing
2025-12-26 13:46:21 +08:00
wangxiaoshuang 096e11e9fd irecist 转变任务不显示融合按钮
continuous-integration/drone/push Build is passing
2025-12-26 13:24:26 +08:00
wangxiaoshuang cce6122e9e 已融合的消失的病灶不允许画标记
continuous-integration/drone/push Build is passing
2025-12-26 13:10:47 +08:00
wangxiaoshuang 6f3ca769a6 被融合的病灶状态不可更改
continuous-integration/drone/push Build is passing
2025-12-25 15:38:29 +08:00
wangxiaoshuang da41fa1139 融合图像伪彩问题
continuous-integration/drone/push Build is passing
2025-12-25 15:14:52 +08:00
wangxiaoshuang 11940798e8 1
continuous-integration/drone/push Build is passing
2025-12-24 17:39:07 +08:00
wangxiaoshuang ef46e4cb08 mpr视图时禁止切换多视图
continuous-integration/drone/push Build is passing
2025-12-24 17:13:31 +08:00
wangxiaoshuang b2119c3acd 表格问题标记绑定时未触发自动计算
continuous-integration/drone/push Build is passing
2025-12-24 17:06:30 +08:00
wangxiaoshuang e7fdee1147 MPR修改
continuous-integration/drone/push Build is passing
2025-12-24 16:45:22 +08:00
wangxiaoshuang b5f327fc3c MPR增加影像加载前的验证:验证条件:序列的文件数量>5,影像具备imageOrientationPatient,imagePositionPatient
continuous-integration/drone/push Build is passing
2025-12-24 16:24:23 +08:00
wangxiaoshuang 93b7477aa6 阅片人申请重阅时:是否复制后续访视阅片表单的选项,需要根据阅片标准设置
continuous-integration/drone/push Build is passing
2025-12-24 15:09:41 +08:00
wangxiaoshuang 73e9f30f7e 【病灶融合】当前访视,融合病灶融合后,分裂按钮需要隐藏
continuous-integration/drone/push Build is passing
2025-12-24 14:29:48 +08:00
wangxiaoshuang 35cc08e53c 【病灶融合】融合按钮显示的位置其他标准调整与lugano一致
continuous-integration/drone/push Build is passing
2025-12-24 14:26:37 +08:00
wangxiaoshuang 97dda302e1 融合页面交互工具存在问题
continuous-integration/drone/push Build is passing
2025-12-24 14:22:30 +08:00
wangxiaoshuang f59b205324 自定义融合阅片标记问题绑定修改
continuous-integration/drone/push Build is passing
2025-12-24 13:35:26 +08:00
wangxiaoshuang 7328ea55df 阅片临床数据弹框修改
continuous-integration/drone/push Build is passing
2025-12-24 11:47:23 +08:00
wangxiaoshuang 5773a93df3 部分问题修复
continuous-integration/drone/push Build is passing
2025-12-24 11:18:17 +08:00
wangxiaoshuang e6e9ab03a3 1
continuous-integration/drone/push Build is passing
2025-12-24 11:10:08 +08:00
wangxiaoshuang a663f6fa4e 1
continuous-integration/drone/push Build is passing
2025-12-24 10:54:57 +08:00
wangxiaoshuang 80fe969fe1 1
continuous-integration/drone/push Build is passing
2025-12-24 10:36:32 +08:00
wangxiaoshuang bdbca21550 邮件日志收件人、抄送人显示修改
continuous-integration/drone/push Build is passing
2025-12-24 10:17:45 +08:00
wangxiaoshuang 3901ceec45 中心调研模板设备字段修改
continuous-integration/drone/push Build is passing
2025-12-24 10:06:09 +08:00
wangxiaoshuang 2f8b88b51f 1
continuous-integration/drone/push Build is passing
2025-12-23 17:43:53 +08:00
wangxiaoshuang bed842ec88 1
continuous-integration/drone/push Build is passing
2025-12-23 17:28:27 +08:00
wangxiaoshuang 72d9872b20 申请重阅添加保留后续访视数据的选项
continuous-integration/drone/push Build is passing
2025-12-23 17:13:15 +08:00
wangxiaoshuang bdca45a505 影像汇总文件下载
continuous-integration/drone/push Build is passing
2025-12-23 16:36:22 +08:00
wangxiaoshuang 68fa0edfe5 申请重阅添加保留后续访视数据的选项
continuous-integration/drone/push Build is running
2025-12-23 16:19:54 +08:00
wangxiaoshuang 17ad103c34 【邮件日志】邮件日志页面相关问题
continuous-integration/drone/push Build is passing
2025-12-23 14:55:12 +08:00
wangxiaoshuang 7bebb8900e 1
continuous-integration/drone/push Build is passing
2025-12-22 17:10:52 +08:00
wangxiaoshuang fc72e141bf 1
continuous-integration/drone/push Build is passing
2025-12-22 16:29:54 +08:00
wangxiaoshuang 3eb28d2baa dicom预览问题
continuous-integration/drone/push Build is passing
2025-12-22 15:52:50 +08:00
wangxiaoshuang 7892b8cf01 发件人在新建项目时需要自动生成
continuous-integration/drone/push Build is passing
2025-12-22 14:15:45 +08:00
wangxiaoshuang 9f5fc1dc9d 同步邮件添加参数
continuous-integration/drone/push Build is passing
2025-12-18 17:15:46 +08:00
wangxiaoshuang 4b5fee5739 mpr添加十字准星
continuous-integration/drone/push Build is running
2025-12-18 17:13:13 +08:00
wangxiaoshuang f5143fe11c PET-CT融合页面,测量值默认显示SUV
continuous-integration/drone/push Build is passing
2025-12-18 14:35:20 +08:00
wangxiaoshuang 363ffa58b3 影像质控风险管理
continuous-integration/drone/push Build is passing
2025-12-18 13:38:02 +08:00
wangxiaoshuang 4e46bbac84 项目自定义标准-NM DICOM工具可配置测量标注工具,影像标记可以与CRF问题关联
continuous-integration/drone/push Build is passing
2025-12-18 11:36:50 +08:00
wangxiaoshuang fb8135d985 【LiLi】中心人员定期管理
continuous-integration/drone/push Build is passing
2025-12-18 11:28:54 +08:00
wangxiaoshuang e5233c8894 PET-CT融合页面,测量值和问题没有关联
continuous-integration/drone/push Build is passing
2025-12-15 17:13:34 +08:00
wangxiaoshuang 4143eb6279 部分问题修复
continuous-integration/drone/push Build is passing
2025-12-15 14:34:42 +08:00
wangxiaoshuang f3d9b31313 病灶融合
continuous-integration/drone/push Build is passing
2025-12-12 17:24:39 +08:00
wangxiaoshuang 726f6499f9 项目报表
continuous-integration/drone/push Build is passing
2025-12-12 16:58:50 +08:00
wangxiaoshuang aab9c4576c 病灶融合功能
continuous-integration/drone/push Build is passing
2025-12-11 16:33:31 +08:00
wangxiaoshuang b661a51ec2 下载进度提示 2025-12-11 16:24:37 +08:00
wangxiaoshuang edb03fef23 隐私政策和用户协议更新日期和生效日期格式修改
continuous-integration/drone/push Build is passing
2025-12-11 11:36:54 +08:00
wangxiaoshuang 7ca31d8a5e 自动计算可以配置高亮显示值
continuous-integration/drone/push Build is passing
2025-12-11 11:18:59 +08:00
wangxiaoshuang 5ec5eda344 【lugano2014】消失状态可使用的工具调整
continuous-integration/drone/push Build is passing
2025-12-08 16:43:25 +08:00
wangxiaoshuang 61a1b1b1c2 【国际化】一致性核查和人员中心管理的上传页面的上传文件文字的国际化需要相互独立
continuous-integration/drone/push Build is passing
2025-12-08 16:26:25 +08:00
wangxiaoshuang f93274080a 邮件日志重发记录
continuous-integration/drone/push Build is passing
2025-12-08 16:21:53 +08:00
wangxiaoshuang fbb9899b55 系统标准报告高亮显示修改
continuous-integration/drone/push Build is passing
2025-12-08 15:39:26 +08:00
wangxiaoshuang f7af9999e4 邮件日志接口添加项目Id 2025-12-08 15:39:03 +08:00
wangxiaoshuang d64bea0d27 1
continuous-integration/drone/push Build is passing
2025-12-05 09:12:30 +08:00
wangxiaoshuang 3a7eadb41c mpr图标修改
continuous-integration/drone/push Build is passing
2025-12-04 14:57:44 +08:00
wangxiaoshuang 1b25f6538e MPR调窗同步
continuous-integration/drone/push Build is passing
2025-12-04 14:43:35 +08:00
wangxiaoshuang dbc84b2f58 1
continuous-integration/drone/push Build is passing
2025-12-04 13:43:23 +08:00
wangxiaoshuang 9d601b4a46 工作台时间显示问题
continuous-integration/drone/push Build is running
2025-12-04 13:40:44 +08:00
wangxiaoshuang 09e5bcb3b7 mpr查看标记和标记截图问题 2025-12-04 13:40:23 +08:00
wangxiaoshuang ae536a7fc4 mpr阅片
continuous-integration/drone/push Build is passing
2025-12-03 16:52:52 +08:00
wangxiaoshuang 1b6f48a5a5 1
continuous-integration/drone/push Build is passing
2025-12-03 11:13:02 +08:00
wangxiaoshuang d57c080916 部分问题修复
continuous-integration/drone/push Build is passing
2025-12-02 17:52:47 +08:00
wangxiaoshuang c2cb5b1999 部分问题修复
continuous-integration/drone/push Build is passing
2025-12-02 17:12:49 +08:00
wangxiaoshuang cc5ac4c2a9 自定义dicom阅片添加mpr
continuous-integration/drone/push Build is passing
2025-12-02 16:28:42 +08:00
wangxiaoshuang 653d3c6228 拖拽上传文件夹未读取全部文件修复
continuous-integration/drone/push Build is passing
2025-12-02 13:31:29 +08:00
wangxiaoshuang eefb42c3f6 阅片图像修改
continuous-integration/drone/push Build is passing
2025-12-02 09:12:53 +08:00
wangxiaoshuang ba6cb87648 阅片融合分组区分颜色
continuous-integration/drone/push Build is passing
2025-11-28 16:30:29 +08:00
wangxiaoshuang 17f738aa5d 1
continuous-integration/drone/push Build is passing
2025-11-28 14:34:09 +08:00
wangxiaoshuang 7a91ccac80 淋巴结非靶病灶和淋巴结新病灶在状态为消失时且长短径不符合标准的不允许保存
continuous-integration/drone/push Build is passing
2025-11-28 10:50:43 +08:00
wangxiaoshuang b57076d41d 阅片问题配置修改
continuous-integration/drone/push Build is passing
2025-11-26 16:20:22 +08:00
wangxiaoshuang 633a169467 中心调研编辑注意事项添加默认值
continuous-integration/drone/push Build is passing
2025-11-25 14:28:58 +08:00
wangxiaoshuang f2d50e7465 【邮件日志】邮件日志中收件人、抄送人未显示,发送时间错误
continuous-integration/drone/push Build is passing
2025-11-25 14:23:15 +08:00
wangxiaoshuang e65e6e2208 淋巴结非靶病灶和淋巴结新病灶在状态为消失时,允许测量
continuous-integration/drone/push Build is passing
2025-11-25 14:15:47 +08:00
wangxiaoshuang 196a20cbc0 【培训管理】新建培训文档,未发布直接下线,发布按钮仍可以点击,需要禁用
continuous-integration/drone/push Build is passing
2025-11-25 10:36:20 +08:00
wangxiaoshuang f002fed9e2 在查看多帧文件时,序列的图像数字出现了:31/1了。
continuous-integration/drone/push Build is passing
2025-11-25 10:30:52 +08:00
wangxiaoshuang 423b71c702 鼠标滚动时,切换图像,图像可以循环的从头到尾
continuous-integration/drone/push Build is passing
2025-11-24 17:12:10 +08:00
wangxiaoshuang f79084b599 阅片图表修改
continuous-integration/drone/push Build is passing
2025-11-24 15:27:15 +08:00
wangxiaoshuang ca99572f19 工作台的时间格式
continuous-integration/drone/push Build is passing
2025-11-24 13:29:59 +08:00
wangxiaoshuang 0d9db63f88 阅片图表显示修改
continuous-integration/drone/push Build is passing
2025-11-21 14:55:13 +08:00
wangxiaoshuang 3f9beb6dee 1
continuous-integration/drone/push Build is passing
2025-11-20 18:00:01 +08:00
wangxiaoshuang 3243b33b33 阅片报告中数值的统计与可视化
continuous-integration/drone/push Build is passing
2025-11-20 15:48:40 +08:00
wangxiaoshuang ca44352b9e 阅片图表展示配置
continuous-integration/drone/push Build is passing
2025-11-20 10:49:22 +08:00
wangxiaoshuang e5d86abf92 阅片报告中数值的统计与可视化(未完成)
continuous-integration/drone/push Build is passing
2025-11-19 13:13:29 +08:00
wangxiaoshuang 8dedd1068a 阅片关键点添加查看全部
continuous-integration/drone/push Build is passing
2025-11-17 13:28:51 +08:00
wangxiaoshuang f58e52585f 阅片人简历采集验证码按钮样式修改
continuous-integration/drone/push Build is passing
2025-11-17 13:21:48 +08:00
wangxiaoshuang 1dd153448c 1
continuous-integration/drone/push Build is passing
2025-11-17 11:26:39 +08:00
wangxiaoshuang b60b0f8de7 1
continuous-integration/drone/push Build is passing
2025-11-17 11:07:10 +08:00
wangxiaoshuang 9de73b9d44 【验证码优化】等待时按钮需要置为灰色
continuous-integration/drone/push Build is passing
2025-11-14 17:17:16 +08:00
wangxiaoshuang b55245a83a 项目阅片关键点添加、首次进入阅片弹框提示阅片关键点
continuous-integration/drone/push Build is passing
2025-11-14 16:03:32 +08:00
wangxiaoshuang cf1af92c2f 阅片关键点系统添加
continuous-integration/drone/push Build is passing
2025-11-14 11:43:12 +08:00
wangxiaoshuang c7740493f8 1
continuous-integration/drone/push Build is passing
2025-11-13 15:26:45 +08:00
wangxiaoshuang cca8a0df9d 验证码发送与确认页面优化
continuous-integration/drone/push Build is passing
2025-11-12 15:31:00 +08:00
wangxiaoshuang a75f4a38f6 登录进行勾选
continuous-integration/drone/push Build is passing
2025-11-11 16:00:01 +08:00
wangxiaoshuang 49b3b056e3 中心调研与中心影像手册的逻辑关系
continuous-integration/drone/push Build is passing
2025-11-11 15:56:26 +08:00
wangxiaoshuang 937ab0899e 影像归档时,增加影像信息
continuous-integration/drone/push Build is passing
2025-11-10 15:56:20 +08:00
wangxiaoshuang f7d7d2b742 1
continuous-integration/drone/push Build is passing
2025-11-10 14:22:26 +08:00
wangxiaoshuang 7c9cde00a9 1
continuous-integration/drone/push Build is passing
2025-11-10 13:45:01 +08:00
wangxiaoshuang b56834e8ed 人员核对提醒按钮只需要pm显示
continuous-integration/drone/push Build is passing
2025-11-10 11:36:26 +08:00
wangxiaoshuang 29e785243e 阅片页面增加章程和阅片手册的入口
continuous-integration/drone/push Build is passing
2025-11-07 14:38:18 +08:00
wangxiaoshuang 7d57f6b5f6 阅片比例尺工具问题修复
continuous-integration/drone/push Build is passing
2025-11-07 13:23:18 +08:00
wangxiaoshuang ecb146bd67 1
continuous-integration/drone/push Build is passing
2025-11-06 16:10:26 +08:00
wangxiaoshuang 795423182d 邮件日志及状态管理
continuous-integration/drone/push Build is passing
2025-11-06 16:07:07 +08:00
wangxiaoshuang 5dd13b58c5 1
continuous-integration/drone/push Build is passing
2025-11-06 13:43:11 +08:00
wangxiaoshuang bbd8e8b510 1
continuous-integration/drone/push Build is passing
2025-11-06 10:04:14 +08:00
wangxiaoshuang 6c02793d2f 【LiLi】支持影像拖拽上传
continuous-integration/drone/push Build is passing
2025-11-05 16:33:03 +08:00
wangxiaoshuang 4f5fb17bb8 已有阅片期修改左侧条件时,右侧查询结果需要清空或刷新
continuous-integration/drone/push Build is passing
2025-11-05 14:45:42 +08:00
wangxiaoshuang 5f89d7d10c 影像阅片时,支持双屏、竖屏
continuous-integration/drone/push Build is passing
2025-11-05 14:21:23 +08:00
wangxiaoshuang 0002bd0165 去除列表项指定中心
continuous-integration/drone/push Build is passing
2025-11-05 13:58:12 +08:00
wangxiaoshuang 5cd5e3c34d 1
continuous-integration/drone/push Build is passing
2025-11-05 13:18:12 +08:00
wangxiaoshuang 9c0ca0a831 非dicom阅片配置矩形标记可绑定项修改
continuous-integration/drone/push Build is passing
2025-11-04 16:46:07 +08:00
wangxiaoshuang 181eaed3f5 批量设置阅片期
continuous-integration/drone/push Build is passing
2025-11-04 16:17:39 +08:00
wangxiaoshuang b88657fda0 1
continuous-integration/drone/push Build is passing
2025-11-04 09:14:52 +08:00
wangxiaoshuang d4aa02e0e5 系统标准阅片页面竖屏适配
continuous-integration/drone/push Build is passing
2025-11-03 15:59:50 +08:00
wangxiaoshuang d13a5edfda 全屏显示按钮,没有作用
continuous-integration/drone/push Build is passing
2025-11-03 14:39:51 +08:00
wangxiaoshuang b6ee9d5f62 【LiLi】工作台的时间格式
continuous-integration/drone/push Build is passing
2025-11-03 14:10:05 +08:00
wangxiaoshuang 134f2471a6 【自定义非dicom】增加比例尺后,表格中的数据没有刷新
continuous-integration/drone/push Build is passing
2025-10-31 17:12:13 +08:00
wangxiaoshuang 746278681d 1
continuous-integration/drone/push Build is passing
2025-10-31 09:16:47 +08:00
wangxiaoshuang 081263d34f 1
continuous-integration/drone/push Build is passing
2025-10-30 15:28:48 +08:00
wangxiaoshuang 199fe6aed3 【自定义非dicom】标记已与问题绑定,删除比例尺后,问题中的值应该恢复为px
continuous-integration/drone/push Build is passing
2025-10-30 15:04:28 +08:00
wangxiaoshuang 273932f9cc 【自定义dicom】表格数值问题移除标记后保存报错
continuous-integration/drone/push Build is passing
2025-10-30 14:32:33 +08:00
wangxiaoshuang 3832312041 标记值不正确
continuous-integration/drone/push Build is passing
2025-10-30 14:24:59 +08:00
wangxiaoshuang b6500c62c5 非dicom标记保存修改
continuous-integration/drone/push Build is passing
2025-10-30 13:20:21 +08:00
wangxiaoshuang 42839a925d 【阅片单元】数值问题配置测量工具,选项中不应该有箭头问题
continuous-integration/drone/push Build is passing
2025-10-30 11:09:44 +08:00
wangxiaoshuang 342f135d23 【自定义非dicom】绑定问题显示的小数取值与标记显示的值有差异
continuous-integration/drone/push Build is passing
2025-10-30 10:58:29 +08:00
wangxiaoshuang 7f47936f8a 【自定义dicom】数值问题中多边形工具的测量值,不应该有radius和max,缺少perimeter
continuous-integration/drone/push Build is passing
2025-10-30 10:55:09 +08:00
wangxiaoshuang a7b9218952 dicom阅片比例尺修改
continuous-integration/drone/push Build is passing
2025-10-30 10:48:54 +08:00
wangxiaoshuang 8590808cb9 1
continuous-integration/drone/push Build is passing
2025-10-29 16:20:48 +08:00
wangxiaoshuang 5394a2cde5 非dicom角度工具添加注释
continuous-integration/drone/push Build is passing
2025-10-29 16:04:08 +08:00
wangxiaoshuang fe0ba42736 【非dicom】重置crf时,需要清除所有标记,包括标尺
continuous-integration/drone/push Build is passing
2025-10-29 14:05:55 +08:00
wangxiaoshuang b6bc22af72 非dicom阅片问题修改
continuous-integration/drone/push Build is passing
2025-10-28 17:55:48 +08:00
wangxiaoshuang 9b67760ffd 中心人员定期管理
continuous-integration/drone/push Build is passing
2025-10-28 15:35:54 +08:00
wangxiaoshuang aee12347dc 非dicom阅片标记绑定问题
continuous-integration/drone/push Build is passing
2025-10-27 15:25:13 +08:00
wangxiaoshuang 7426be2e50 非dicom阅片问题绑定标记
continuous-integration/drone/push Build is passing
2025-10-24 17:58:10 +08:00
wangxiaoshuang 5565b1389d 隐私政策样式调整
continuous-integration/drone/push Build is passing
2025-10-24 15:53:10 +08:00
wangxiaoshuang 0e029369a3 隐私政策修改
continuous-integration/drone/push Build is passing
2025-10-24 11:23:19 +08:00
wangxiaoshuang 5d09bd2159 全量核查文件下载问题修改
continuous-integration/drone/push Build is passing
2025-10-24 09:49:16 +08:00
wangxiaoshuang 98757b6c06 1
continuous-integration/drone/push Build is passing
2025-10-23 16:28:39 +08:00
wangxiaoshuang c197c077a7 非dicom阅片工具配置修改
continuous-integration/drone/push Build is passing
2025-10-23 15:49:59 +08:00
wangxiaoshuang 2ce7fa7552 一致性核查结果,在做全量核查时,对于已经核查过的数据也进行校验
continuous-integration/drone/push Build is passing
2025-10-21 17:35:17 +08:00
wangxiaoshuang 5456d7a40b 阅片重置窗宽窗位不重置反色
continuous-integration/drone/push Build is passing
2025-10-21 16:46:01 +08:00
wangxiaoshuang 8299fd8958 隐私政策去除删除功能 2025-10-21 16:45:31 +08:00
wangxiaoshuang 06b505544b 1
continuous-integration/drone/push Build is passing
2025-10-21 16:09:39 +08:00
wangxiaoshuang 19d472bfb3 用户协议和数据隐私采集说明
continuous-integration/drone/push Build is passing
2025-10-21 16:04:23 +08:00
wangxiaoshuang 62f601a7be 接口变更
continuous-integration/drone/push Build is passing
2025-10-21 13:36:59 +08:00
wangxiaoshuang 689527b61e 错误字段修改
continuous-integration/drone/push Build is passing
2025-10-21 13:26:13 +08:00
wangxiaoshuang 7478472e33 1
continuous-integration/drone/push Build is passing
2025-10-21 13:09:58 +08:00
wangxiaoshuang 7f48e37779 1
continuous-integration/drone/push Build is passing
2025-10-21 11:48:41 +08:00
wangxiaoshuang 2a27e1fbae 【Lugano2014 无pet】报告页的整体肿瘤评估结果需要可以修改结果,为下拉框
continuous-integration/drone/push Build is passing
2025-10-21 11:38:35 +08:00
wangxiaoshuang 02a5e2fdc2 MFA登录状态持久管理和页面优化
continuous-integration/drone/push Build is passing
2025-10-20 14:59:58 +08:00
wangxiaoshuang 0f6da34395 1
continuous-integration/drone/push Build is passing
2025-10-17 15:14:15 +08:00
wangxiaoshuang 2c567e3530 培训记录受训人查询条件修改
continuous-integration/drone/push Build is passing
2025-10-17 13:58:58 +08:00
wangxiaoshuang b7f113c109 【Lugano2014 no pet】阅片报告整体肿瘤评估结果在全局阅片时有修改,鼠标悬浮在结果上时需要显示全局修改的结果
continuous-integration/drone/push Build is passing
2025-10-17 10:03:48 +08:00
wangxiaoshuang 7c79e4b1f3 项目信息编辑,申办方和CRO选择后无法清空,增加删除按钮
continuous-integration/drone/push Build is passing
2025-10-17 10:00:11 +08:00
wangxiaoshuang 5528188a8b 非dicom阅片问题标记绑定(待完成)
continuous-integration/drone/push Build is passing
2025-10-16 18:41:01 +08:00
wangxiaoshuang c0aa89c6ca 调窗开启后,无法进行调窗
continuous-integration/drone/push Build is passing
2025-10-16 15:41:24 +08:00
wangxiaoshuang 5bbbdb0a97 Merge branch 'uat'
continuous-integration/drone/push Build is passing
2025-10-16 14:55:03 +08:00
wangxiaoshuang cbcc11fe97 非dicom阅片单元配置
continuous-integration/drone/push Build is running
2025-10-15 17:10:18 +08:00
wangxiaoshuang 7f20bfecdc 新标准全局阅片修改
continuous-integration/drone/push Build is passing
2025-10-15 15:37:46 +08:00
wangxiaoshuang f58c116382 阅片期列表数量选择添加100、500、1000
continuous-integration/drone/push Build is passing
2025-10-15 14:47:41 +08:00
wangxiaoshuang b44d832241 1
continuous-integration/drone/push Build is passing
2025-10-15 14:32:15 +08:00
wangxiaoshuang bf2a2818ce Merge branch 'uat'
continuous-integration/drone/push Build is passing
2025-10-15 09:54:33 +08:00
wangxiaoshuang 58c33bc008 是否预设添加限制下拉框、单选框
continuous-integration/drone/push Build is passing
2025-10-14 14:48:17 +08:00
wangxiaoshuang 1e49dedd23 Lugano 2014 without FDG-PET
continuous-integration/drone/push Build is passing
2025-10-14 13:33:15 +08:00
wangxiaoshuang 3c661ae86a 1
continuous-integration/drone/push Build is passing
2025-10-14 11:13:48 +08:00
wangxiaoshuang 7238b12424 Lugano 2014全局更新类型,配置时数据被清空
continuous-integration/drone/push Build is passing
2025-10-13 16:17:48 +08:00
wangxiaoshuang 341a707c80 独立阅片人简历查看修改
continuous-integration/drone/push Build is passing
2025-10-13 14:01:46 +08:00
wangxiaoshuang 877535f579 1
continuous-integration/drone/push Build is passing
2025-10-13 10:20:09 +08:00
wangxiaoshuang 602f942e9e 附加评估问题修改
continuous-integration/drone/push Build is passing
2025-10-13 10:01:22 +08:00
wangxiaoshuang a804ba40f0 阅片单元新增配置是否预设
continuous-integration/drone/push Build is passing
2025-10-10 17:26:04 +08:00
wangxiaoshuang 38eef44d79 表单固定列
continuous-integration/drone/push Build is passing
2025-10-10 16:47:44 +08:00
wangxiaoshuang ff1b4f6f21 阅片比例尺 2025-10-10 16:47:25 +08:00
wangxiaoshuang ef336eafe6 登录隐私政策 2025-10-10 16:46:50 +08:00
wangxiaoshuang 553014be79 稽查记录的子项,增加属性:指定标准显示
continuous-integration/drone/push Build is passing
2025-10-10 15:19:17 +08:00
wangxiaoshuang e7491501cf 阅片人筛选-选择,编辑阅片人简历时,需要可以修改自动生成的临床试验经历
continuous-integration/drone/push Build is passing
2025-10-10 15:12:27 +08:00
wangxiaoshuang d64d5a523d 在报告页面增加附件评估结果评估和查看入口
continuous-integration/drone/push Build is passing
2025-10-10 11:26:18 +08:00
wangxiaoshuang b2e7eeaeb4 增加受试者中止状态筛选项
continuous-integration/drone/push Build is passing
2025-10-10 11:05:29 +08:00
307 changed files with 40027 additions and 23834 deletions
+28 -19
View File
@@ -1,3 +1,4 @@
kind: pipeline
type: ssh
name: ssh-linux-uat-irc-publish
@@ -10,23 +11,29 @@ clone:
disable: true #禁用默认克隆
server:
host: 47.117.164.182
host: 101.132.253.119
user: root
password:
from_secret: local_pwd
steps:
- name: uat-branch-publish-irc-vue
commands:
- cd /opt/1panel/vue-devops/Uat_IRC/Uat-To-Uat
- sh uat-to-uat.sh v${DRONE_BUILD_NUMBER}
- name: publish-uat-irc-web
commands:
- bash /opt/1panel/xc-deploy-new/devops-center/build-vue-then-publish.sh IRC_Web uat --mode uat
- 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" "Uat_IRC_WEB" "irc.uat.extimaging.com"
when:
status:
- success
- failure
trigger:
branch:
- uat
---
---
kind: pipeline
type: ssh
name: ssh-linux-test-irc-publish
@@ -45,17 +52,19 @@ server:
from_secret: local_pwd
steps:
- name: publish-test-irc-vue
commands:
- echo start publish test-irc-vue
- cd /opt/1panel/hang/vue/test-irc
- sh test-irc.sh v${DRONE_BUILD_NUMBER}
- 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
- main
+5328 -6019
View File
File diff suppressed because it is too large Load Diff
+13 -12
View File
@@ -14,18 +14,19 @@
"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/polymorphic-segmentation": "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/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",
@@ -37,7 +38,7 @@
"dcmjs": "^0.29.8",
"dicom-parser": "^1.8.9",
"dicomedit": "^0.1.0",
"echarts": "^4.8.0",
"echarts": "^6.0.0",
"element-ui": "^2.15.14",
"exceljs": "^4.4.0",
"file-saver": "^2.0.5",
@@ -59,11 +60,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",
@@ -97,9 +97,10 @@
"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"
},
@@ -124,4 +125,4 @@
"not dead",
"not op_mini all"
]
}
}
+1 -1
View File
@@ -10,7 +10,7 @@
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-control" content="no-store,no-cache,must-revalidate">
<meta http-equiv="Cache" content="no-cache">
<title>IRC Imaging System</title>
<title>EIC lmaging Trial Management System</title>
</head>
<body>
+2 -2
View File
@@ -31,7 +31,7 @@
<div class="title-container" style="display: flex;padding-top:180px;justify-content: flex-start;height: 100%;box-sizing: border-box">
<!-- IRC Management System -->
<!-- <div class="title" style="text-align: center;font-size: 28px;margin-top: 100px;padding-right: 120px"></div>-->
<!-- <div class="title" v-show="false">IRC Imaging System</div>-->
<!-- <div class="title" v-show="false">EIC lmaging Trial Management System</div>-->
<div style="color: #0a84ff;text-align: left;font-size: 28px">
{{message}}
</div>
@@ -63,7 +63,7 @@
<div style="margin: 0 auto;width: 600px;line-height: 28px;text-align: center" >
<h1 style="text-align: center;margin-bottom: 20px">关于</h1>
<p style="margin-bottom: 20px">
IRC Imaging System
EIC lmaging Trial Management System
</p>
<p style="margin-bottom: 20px">
V1.3.0.001
+1 -1
View File
@@ -31,7 +31,7 @@
<div class="title-container" style="display: flex;padding-top:180px;justify-content: flex-start;height: 100%;box-sizing: border-box">
<!-- IRC Management System -->
<!-- <div class="title" style="text-align: center;font-size: 28px;margin-top: 100px;padding-right: 120px"></div>-->
<!-- <div class="title" v-show="false">IRC Imaging System</div>-->
<!-- <div class="title" v-show="false">EIC lmaging Trial Management System</div>-->
<div style="color: #0a84ff;text-align: left;font-size: 28px">
{{message}}
</div>
BIN
View File
Binary file not shown.
+45 -100
View File
@@ -1,12 +1,7 @@
<template>
<div id="app" style="position: relative">
<router-view />
<div
v-show="show"
v-if="$route.matched.length > 0"
v-adaptive
@click="openI18n"
style="
<div v-show="show" v-if="$route.matched.length > 0" v-adaptive @click="openI18n" style="
position: fixed;
bottom: 50px;
left: 50px;
@@ -19,58 +14,27 @@
color: #fff;
border-radius: 50%;
cursor: pointer;
"
>
">
i18n
</div>
<el-drawer
:title="$t('il8n:title')"
:visible.sync="drawer"
direction="rtl"
size="80%"
>
<el-drawer :title="$t('il8n:title')" :visible.sync="drawer" direction="rtl" size="80%">
<div style="width: 800px">
<el-form
label-width="100px"
@submit.native.prevent
size="small"
:inline="true"
class="demo-form-inline"
>
<el-form label-width="100px" @submit.native.prevent size="small" :inline="true" class="demo-form-inline">
<el-form-item :label="$t('il8n:search:keyword')">
<el-input v-model="key" @input="keyChange" />
</el-form-item>
<el-form-item :label="$t('il8n:search:state')">
<el-select
v-model="State"
clearable
filterable
@change="handleStateChange"
>
<el-option
v-for="item of $d.InternationalizationKeyState"
:key="'InternationalizationKeyState' + item.value"
:label="item.label"
:value="item.value"
/>
<el-select v-model="State" clearable filterable @change="handleStateChange">
<el-option v-for="item of $d.InternationalizationKeyState"
:key="'InternationalizationKeyState' + item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-form>
</div>
<el-table
:data="tableData"
v-adaptive="{ bottomOffset: 50 }"
height="100"
style="width: 100%"
@sort-change="handleSortByColumn"
>
<el-table-column
prop="Code"
:label="$t('il8n:table:label')"
width="300"
show-overflow-tooltip
sortable="custom"
>
<el-table :data="tableData" v-adaptive="{ bottomOffset: 50 }" height="100" style="width: 100%"
@sort-change="handleSortByColumn">
<el-table-column prop="Code" :label="$t('il8n:table:label')" width="300" show-overflow-tooltip
sortable="custom">
</el-table-column>
<!-- <el-table-column-->
<!-- prop="Description"-->
@@ -81,71 +45,37 @@
<!-- {{scope.row.Description}}-->
<!-- </template>-->
<!-- </el-table-column>-->
<el-table-column
prop="Value"
:label="$t('il8n:table:en')"
sortable="custom"
>
<el-table-column prop="Value" :label="$t('il8n:table:en')" sortable="custom">
<template slot-scope="scope">
<el-input
v-model="scope.row.Value"
@input="
(e) => {
$set(scope.row, 'Value', e)
}
"
size="mini"
></el-input>
<el-input v-model="scope.row.Value" @input="
(e) => {
$set(scope.row, 'Value', e)
}
" size="mini"></el-input>
</template>
</el-table-column>
<el-table-column
prop="ValueCN"
:label="$t('il8n:table:cn')"
sortable="custom"
>
<el-table-column prop="ValueCN" :label="$t('il8n:table:cn')" sortable="custom">
<template slot-scope="scope">
<el-input
v-model="scope.row.ValueCN"
@input="
(e) => {
$set(scope.row, 'ValueCN', e)
}
"
size="mini"
></el-input>
<el-input v-model="scope.row.ValueCN" @input="
(e) => {
$set(scope.row, 'ValueCN', e)
}
" size="mini"></el-input>
</template>
</el-table-column>
<el-table-column
prop="ValueCN"
:label="$t('il8n:table:state')"
sortable="custom"
>
<el-table-column prop="ValueCN" :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"
:key="'InternationalizationKeyState' + item.value"
:label="item.label"
:value="item.value"
/>
<el-select v-model="scope.row.State" clearable filterable size="mini">
<el-option v-for="item of $d.InternationalizationKeyState"
:key="'InternationalizationKeyState' + item.value" :label="item.label" :value="item.value" />
</el-select>
</template>
</el-table-column>
<el-table-column
prop="Version"
:label="$t('il8n:table:Version')"
sortable="custom"
>
<el-table-column prop="Version" :label="$t('il8n:table:Version')" sortable="custom">
</el-table-column>
</el-table>
<div style="text-align: right; padding-top: 10px; padding-right: 10px">
<el-button size="mini" @click="drawer = false"
>{{ $t('common:button:cancel') }}
<el-button size="mini" @click="drawer = false">{{ $t('common:button:cancel') }}
</el-button>
<el-button size="mini" type="primary" @click="handleSave">{{
$t('common:button:save')
@@ -153,6 +83,7 @@
</div>
</el-drawer>
<feedBack v-if="$route.matched.length > 0" />
<!-- <timeTag /> -->
</div>
</template>
@@ -163,11 +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 Vue from 'vue'
import i18n from './lang'
export default {
name: 'App',
components: { feedBack },
components: {
feedBack,
// timeTag
},
data() {
return {
drawer: false,
@@ -366,13 +301,16 @@ export default {
<style lang="scss">
$light_gray: #606266;
.el-tooltip__popper {
max-width: 400px;
}
.my_multiple {
.el-input--medium .el-input__inner {
height: 36px !important;
}
.el-select__tags {
flex-wrap: nowrap;
overflow: hidden;
@@ -381,6 +319,7 @@ $light_gray: #606266;
display: block;
}
}
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
-webkit-appearance: none !important;
@@ -393,6 +332,7 @@ input[type='number'] {
.viewer-fixed.viewer-container {
z-index: 10000;
}
textarea {
white-space: break-spaces;
word-break: normal;
@@ -401,12 +341,15 @@ textarea {
* {
word-break: normal !important;
}
.box-body .el-button.is-circle:not(.is-disabled) i:before {
color: #428bca;
}
.box-body .el-button.is-circle i.el-icon-question:before {
color: #fff;
}
.system-title {
font-size: 35px;
color: $light_gray;
@@ -415,9 +358,11 @@ textarea {
font-family: 'Times New Roman';
text-shadow: 1px 0.5px 1.5px #666;
}
.title-logo {
height: 40px;
}
.title-logo {
height: 40px;
}
+44 -3
View File
@@ -89,10 +89,11 @@ export function UpdateUserRole(param) {
})
}
export function resetPassword(userId) {
export function resetPassword(data) {
return request({
url: `/user/resetPassword/${userId}`,
method: 'get'
url: `/user/resetPassword`,
method: 'post',
data
})
}
@@ -350,3 +351,43 @@ export function getUserJoinedTrialList(data) {
data
})
}
// 邮件日志-日志列表
export function getEmailLogList(data) {
return request({
url: `/EmailLog/getEmailLogList`,
method: 'post',
data
})
}
// 邮件日志-日志详情
export function getEmailInfo(data) {
return request({
url: `/EmailLog/getEmailInfo`,
method: 'post',
data
})
}
// 邮件日志-重发邮件
export function resendEmail(data) {
return request({
url: `/EmailLog/resendEmail`,
method: 'post',
data
})
}
// 邮件日志-同步邮件
export function synchronizationEmail(data) {
return request({
url: `/EmailLog/synchronizationEmail`,
method: 'post',
data
})
}
// 邮件日志-重发列表
export function getReSendEmail(data) {
return request({
url: `/EmailLog/getReSendEmail`,
method: 'post',
data
})
}
+71
View File
@@ -1403,3 +1403,74 @@ export function batchUpdateEmail(data) {
data
})
}
// 隐私政策和用户协议-获取列表
export function getUserAgreementList(data) {
return request({
url: `/UserAgreement/getUserAgreementList`,
method: 'post',
data
})
}
// 隐私政策和用户协议-设置当前版本
export function setCurrentVersion(data) {
return request({
url: `/UserAgreement/setCurrentVersion`,
method: 'post',
data
})
}
// 隐私政策和用户协议-新增或修改
export function addOrUpdateUserAgreement(data) {
return request({
url: `/UserAgreement/addOrUpdateUserAgreement`,
method: 'post',
data
})
}
// 隐私政策和用户协议-删除
export function deleteUserAgreement(id) {
return request({
url: `/UserAgreement/deleteUserAgreement/${id}`,
method: 'delete',
})
}
// 隐私政策和用户协议-详情
export function getUserAgreementById(data) {
return request({
url: `/UserAgreement/getUserAgreementById`,
method: 'post',
data
})
}
// 隐私政策和用户协议-当前版本
export function getCurrentVersionUserAgreements(data) {
return request({
url: `/UserAgreement/getCurrentVersionUserAgreements`,
method: 'post',
data
})
}
// 获取系统阅片关键点文件列表
export function getSystemCriterionKeyFileList(data) {
return request({
url: `/SystemCriterionKeyFile/getSystemCriterionKeyFileList`,
method: 'post',
data
})
}
// 新增/修改系统阅片关键点文件
export function addOrUpdateSystemCriterionKeyFile(data) {
return request({
url: `/SystemCriterionKeyFile/addOrUpdateSystemCriterionKeyFile`,
method: 'post',
data
})
}
// 删除系统阅片关键点文件
export function deleteSystemCriterionKeyFile(systemCriterionKeyFileId) {
return request({
url: `/SystemCriterionKeyFile/deleteSystemCriterionKeyFile/${systemCriterionKeyFileId}`,
method: 'delete'
})
}
+9 -1
View File
@@ -239,7 +239,7 @@ export function getPatientList_Export(data) {
data
})
}
export function getTumor_CDISC_Export(data) {
export function getTumor_CDISC_IVUSOCT_Export(data) {
return requestDownload({
url: `/IVUS_OCTExport/getTumor_CDISC_Export`,
responseType: 'blob',
@@ -263,6 +263,14 @@ export function getCommonJudgeRatioList_Export(data) {
data
})
}
export function getTumor_CDISC_Export(data) {
return requestDownload({
url: `/Tumor_CDISC_Export/getTumor_CDISC_Export`,
responseType: 'blob',
method: 'post',
data
})
}
// 导出国际化列表
export function GetInternationalizationList_Export(data) {
return requestDownload({
+41
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
})
}
+105
View File
@@ -295,6 +295,22 @@ export function readingImport(param) {
})
}
// 非dicom阅片保存标记
export function saveAnswerAndBindingNoneDicomMark(param) {
return request({
url: `/ReadingImageTask/saveAnswerAndBindingNoneDicomMark`,
method: 'post',
data: param
})
}
// 非dicom按比例修改答案
export function changePlottingScaleChangeAnswer(param) {
return request({
url: `/ReadingImageTask/changePlottingScaleChangeAnswer`,
method: 'post',
data: param
})
}
// 阅片获取图表数据
export function getReportsChartData(param) {
return request({
@@ -303,3 +319,92 @@ 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
})
}
+5 -2
View File
@@ -26,8 +26,11 @@ export function getTrialSurveyInitInfo(trialId) {
export function getSiteSurveyInfo(trialId, id) {
return request({
url: `/TrialSiteSurvey/getSiteSurveyInfo/${trialId}/${id}`,
method: 'get'
url: `/TrialSiteSurvey/getSiteSurveyInfo/${trialId}`,
method: 'get',
params: {
TrialsiteSurveyId: id
}
})
}
+147 -7
View File
@@ -1203,7 +1203,13 @@ export function getConsistencyVerificationList(param) {
data: param
})
}
export function getConsistencyCheckFileList(param) {
return request({
url: `/QCList/getConsistencyCheckFileList`,
method: 'post',
data: param
})
}
export function getCheckChallengeDialogList(subjectVisitId) {
return request({
url: `/QCList/getCheckChallengeDialogList/${subjectVisitId}`,
@@ -1256,11 +1262,11 @@ export function getCRCVisitChallengeAndDialog(subjectVisitId, trialQCProcess) {
})
}
export function uploadVisitCheckExcel(trialId, file) {
export function uploadVisitCheckExcel(trialId, file, isFullCheck = false) {
const formData = new FormData()
formData.append('file', file)
return request({
url: `/QCOperation/UploadVisitCheckExcel/${trialId}`,
url: `/QCOperation/UploadVisitCheckExcel/${trialId}?isFullCheck=${isFullCheck}`,
method: 'post',
data: formData
})
@@ -2833,7 +2839,13 @@ export function getSplitPPdSum(param) {
data: param
})
}
export function getSplitPPdSumNoPet(param) {
return request({
url: `/LuganoWithoutPETCalculate/getSplitPPdSum`,
method: 'post',
data: param
})
}
export function submitDicomVisitTask(param) {
return request({
url: `/Inspection/ReadingImageTask/SubmitDicomVisitTask`,
@@ -2872,7 +2884,13 @@ export function getIsSuvMaxLesion(param) {
data: param
})
}
export function getIsSuvMaxLesionNoPet(param) {
return request({
url: `/LuganoWithoutPETCalculate/getIsSuvMaxLesion`,
method: 'post',
data: param
})
}
export function getCanChooseNotMerge(param) {
return request({
url: `/LuganoCalculate/getCanChooseNotMerge`,
@@ -2880,6 +2898,13 @@ export function getCanChooseNotMerge(param) {
data: param
})
}
export function getCanChooseNotMergeNoPet(param) {
return request({
url: `/LuganoWithoutPETCalculate/getCanChooseNotMerge`,
method: 'post',
data: param
})
}
export function saveTaskQuestion(saveQuestionType, param) {
return request({
url: `/SaveTaskQuestion/${saveQuestionType}`,
@@ -3603,13 +3628,24 @@ export function getSplenicState(visitTaskId, spleenLength) {
method: 'post'
})
}
export function getSplenicStateNoPet(visitTaskId, spleenLength) {
return request({
url: `/LuganoWithoutPETCalculate/getSplenicState?visitTaskId=${visitTaskId}&spleenLength=${spleenLength}`,
method: 'post'
})
}
export function getSplenicVerify(visitTaskId) {
return request({
url: `/LuganoCalculate/getSplenicVerify?visitTaskId=${visitTaskId}`,
method: 'post'
})
}
export function getSplenicVerifyNoPet(visitTaskId) {
return request({
url: `/LuganoWithoutPETCalculate/getSplenicVerify?visitTaskId=${visitTaskId}`,
method: 'post'
})
}
export function uploadTrialSiteSurveyUser(trialId, baseUrl, routeUrl, param) {
return request({
url: `/TrialSiteSurvey/UploadTrialSiteSurveyUser?trialId=${trialId}&baseUrl=${baseUrl}&routeUrl=${routeUrl}`,
@@ -4282,7 +4318,103 @@ export function deleteAuditRecord(auditRecordId) {
method: 'delete'
})
}
// 核对中心人员发送邮件
export function sendCheckSiteSurveyUserEmail(data) {
return request({
url: `/TrialSiteSurvey/sendCheckSiteSurveyUserEmail`,
method: 'post',
data
})
}
// 更新中心人员发送邮件
export function sendUpdateSiteSurveyUserEmail(data) {
return request({
url: `/TrialSiteSurvey/sendUpdateSiteSurveyUserEmail`,
method: 'post',
data
})
}
// 更新中心人员发送邮件
export function getTrialSiteLatestSurvey(params) {
return request({
url: `/TrialSiteSurvey/getTrialSiteLatestSurvey`,
method: 'get',
params
})
}
// 阅片期计划列表
export function getReadModulePageList(data) {
return request({
url: `/ReadModule/getReadModulePageList`,
method: 'post',
data
})
}
// 移除阅片期
export function setBatchRemoveReadingPlan(data) {
return request({
url: `/ReadingPeriodSet/setBatchRemoveReadingPlan`,
method: 'post',
data
})
}
// 更新项目额外json配置
export function updateTrialExtralConfig(params, data) {
return request({
url: `/TrialConfig/updateTrialExtralConfig`,
method: 'put',
params,
data
})
}
// 获取项目标准阅片关键点列表
export function getTrialCriterionKeyFileList(data) {
return request({
url: `/TrialCriterionKeyFile/getTrialCriterionKeyFileList`,
method: 'post',
data
})
}
// 设置已阅读关键文件
export function setReadKeyFile(data) {
return request({
url: `/ReadingImageTask/setReadKeyFile`,
method: 'post',
data
})
}
// 项目报表-访视完成度
export function getTrialVisitFinishedStatList(data) {
return request({
url: `/TrialStat/getTrialVisitFinishedStatList`,
method: 'post',
data
})
}
// 项目报表-质疑统计表
export function getTrialQuestionStatList(data) {
return request({
url: `/TrialStat/getTrialQuestionStatList`,
method: 'post',
data
})
}
// 项目报表-疗效统计表
export function getTrialEfficacyEvaluationStatList(data) {
return request({
url: `/TrialStat/getTrialEfficacyEvaluationStatList`,
method: 'post',
data
})
}
// 受试者图表
export function getReportsChartSummary(data) {
return request({
url: `/ReadingImageTask/getReportsChartSummary`,
method: 'post',
data
})
}
// 靶段标注列表
export function getTrialSubjectVisitMarkList(data) {
return request({
@@ -4290,4 +4422,12 @@ export function getTrialSubjectVisitMarkList(data) {
method: 'post',
data
})
}
// 更新缩略图
export function updateImageResizePath(data) {
return request({
url: `/Series/updateImageResizePath`,
method: 'post',
data
})
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 KiB

+37
View File
@@ -0,0 +1,37 @@
import Vue from "vue";
import AGRCOMP from "./index.vue";
const MFAConstructor = Vue.extend(AGRCOMP);
let MFAINSTANCELIST = [];
const AGR = options => {
const { Id, IsEn_Us, callBack, cancelBack } = options;
if (!Id) throw `Id is requred.but ${Id}`
const id = `AGR${new Date().getTime()}`;
const instance = new MFAConstructor();
MFAINSTANCELIST.push(instance)
instance.id = id;
instance.vm = instance.$mount();
if (instance.vm.visible) return;
document.body.appendChild(instance.vm.$el);
instance.vm.open({ Id, IsEn_Us });
instance.vm.$on("success", (Id) => {
if (callBack) callBack(Id)
});
instance.vm.$on("closed", () => {
if (cancelBack) cancelBack();
document.body.removeChild(instance.vm.$el);
instance.vm.$destroy();
let index = MFAINSTANCELIST.findIndex(item => item.id === id);
MFAINSTANCELIST.splice(index, 1)
});
return instance.vm;
}
AGR.close = () => {
if (MFAINSTANCELIST.length <= 0) return;
MFAINSTANCELIST.forEach(item => {
document.body.removeChild(item.vm.$el);
item.vm.$destroy();
})
MFAINSTANCELIST = [];
}
export default AGR;
+7
View File
@@ -0,0 +1,7 @@
import AGRCOMP from "./index.vue";
import AGR from "./fun";
export default Vue => {
Vue.component(AGRCOMP.name, AGRCOMP);
Vue.prototype.$AGR = AGR;
};
+65
View File
@@ -0,0 +1,65 @@
<template>
<!--AGR-->
<el-dialog v-if="visible" :visible.sync="visible" width="60%" :close-on-click-modal="false" append-to-body center
:show-close="true" @close="cancel">
<!-- <div slot="title">
{{ agreement.FileName }}
</div> -->
<div v-html="agreement.FileEnContent" class="content" v-if="IsEn_Us"></div>
<div v-html="agreement.FileContent" class="content" v-else></div>
</el-dialog>
</template>
<script>
import {
getUserAgreementById
} from '@/api/dictionary'
export default {
name: "AGR",
data() {
return {
visible: false,
agreement: {},
IsEn_Us: false
}
},
mounted() {
},
methods: {
async getAgreement(Id) {
try {
let data = {
Id
}
let res = await getUserAgreementById(data)
if (res.IsSuccess) {
this.agreement = res.Result
}
} catch (err) {
console.log(err)
}
},
open(data) {
let { Id, IsEn_Us } = data;
this.IsEn_Us = IsEn_Us
this.getAgreement(Id)
this.visible = true
},
cancel() {
this.visible = false;
this.$emit("closed");
},
},
};
</script>
<style lang="scss" scoped>
::v-deep .el-dialog__body {
height: 500px;
}
.content {
height: 100%;
overflow-y: auto;
}
</style>
+12 -3
View File
@@ -2,9 +2,17 @@
<el-breadcrumb class="app-breadcrumb" separator-class="el-icon-arrow-right">
<transition-group name="breadcrumb">
<el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path">
<span v-if="item.redirect==='noRedirect'||index==levelList.length-1" class="no-redirect">{{ item.meta.title }}</span>
<span v-else-if="item.name === 'TrialsPanel'" class="no-redirect">{{ item.meta.title }}</span>
<a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a>
<span v-if="item.redirect==='noRedirect'||index==levelList.length-1" class="no-redirect">
<!-- {{ item.meta.title }} -->
{{ item.meta.i18n ? $t(item.meta.i18n) : item.meta.title }}
</span>
<span v-else-if="item.name === 'TrialsPanel'" class="no-redirect">
<!-- {{ item.meta.title }} -->
{{ item.meta.i18n ? $t(item.meta.i18n) : item.meta.title }}
</span>
<a v-else @click.prevent="handleLink(item)">
{{ item.meta.i18n ? $t(item.meta.i18n) : item.meta.title }}
</a>
</el-breadcrumb-item>
</transition-group>
</el-breadcrumb>
@@ -35,6 +43,7 @@ export default {
item.meta.title = this.$route.query.trialCode // 暂时处理
}
})
this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
},
isDashboard(route) {
+44 -36
View File
@@ -1,25 +1,11 @@
<template>
<!--MFA-->
<el-dialog
v-if="visible"
:visible.sync="visible"
width="540px"
:close-on-click-modal="false"
append-to-body
center
:show-close="status === 'login'"
@close="cancel"
>
<el-dialog v-if="visible" :visible.sync="visible" width="540px" :close-on-click-modal="false" append-to-body center
:show-close="status === 'login'" @close="cancel">
<div slot="title">
{{ status === "login" ? $t("mfa:title") : $t("mfa:lock:title") }}
</div>
<el-form
ref="mfaForm"
label-position="right"
:model="form"
:rules="rules"
label-width="100px"
>
<el-form ref="mfaForm" label-position="right" :model="form" :rules="rules" label-width="100px">
<!-- 邮箱 -->
<p class="tip_mfa">
<i class="el-icon-warning" style="color: #409eff"></i>
@@ -31,18 +17,13 @@
</el-form-item>
<!-- 验证码 -->
<el-form-item :label="$t('mfa:form:MFACode')" prop="Code">
<el-input
:placeholder="$t('mfa:form:input:placeholder:Codes')"
v-model="form.Code"
style="width: 240px; margin-right: 10px"
/>
<el-button
type="primary"
size="small"
@click.stop="sendMFACode"
:disabled="flag || sendFlag"
>{{ flag ? `${second}s` : $t("mfa:form:sendMFACode") }}</el-button
>
<div style="display: flex;justify-content: space-between;width: 90%;">
<el-input :placeholder="$t('mfa:form:input:placeholder:Codes')" v-model="form.Code" />
<el-button size="small" @click.stop="sendMFACode" :disabled="flag || sendFlag" style="margin-left: 10px;"
class="codeBtn">{{
flag ?
`${$t("mfa:form:sendMFACodeCountDown")} (${second}s)` : $t("mfa:form:sendMFACode") }}</el-button>
</div>
</el-form-item>
</el-form>
<div slot="footer">
@@ -51,19 +32,16 @@
{{ $t("mfa:button:cancel") }}
</el-button> -->
<!-- 保存 -->
<el-button
type="primary"
size="small"
@click="save"
:loading="loading"
style="width: 80%"
>
<el-button type="primary" size="small" @click="save" :loading="loading" style="width: 80%">
{{
status === "login"
? $t("mfa:button:save")
: $t("mfa:lock:button:save")
}}
</el-button>
<p style="text-align: left;font-size: 14px;margin:10px auto;width: 80%;">
<el-checkbox v-model="form.isRemember" /><span style="margin-left: 10px;">{{ $t("mfa:tip:noLogin") }}</span>
</p>
</div>
</el-dialog>
</template>
@@ -86,6 +64,7 @@ export default {
IdentityUserId: null,
EMail: null,
username: null,
isRemember: true
},
rules: {
Code: [
@@ -112,6 +91,18 @@ export default {
},
};
},
mounted() {
this.flag = true;
this.second = 60;
this.timer = setInterval(() => {
this.second--;
if (this.second <= 0) {
this.flag = false;
clearInterval(this.timer);
this.timer = null;
}
}, 1000);
},
methods: {
open(data) {
let { UserId, status, username, EMail } = data;
@@ -189,6 +180,21 @@ export default {
};
</script>
<style lang="scss" scoped>
.codeBtn {
color: #409EFF;
border-color: #409EFF;
}
.codeBtn.is-disabled,
.codeBtn.is-disabled:focus,
.codeBtn.is-disabled:hover {
color: #c0c4cc;
cursor: not-allowed;
background-image: none;
background-color: #fff;
border-color: #ebeef5;
}
.tip_mfa {
width: 86%;
margin: auto;
@@ -198,10 +204,12 @@ export default {
line-height: 30px;
border-radius: 5px;
background-color: #eee;
i {
margin-right: 5px;
}
}
::v-deep .el-dialog__header {
font-weight: bold;
}
+1 -1
View File
@@ -54,7 +54,7 @@ export default {
currentUser: zzSessionStorage.getItem('userName'),
COMPANY: process.env.VUE_APP_COMPANY_NAME,
onlyOffice_url: process.env.VUE_APP_ONLYOFFICE_URL,
window
window,
}
},
computed: {
+3 -3
View File
@@ -22,7 +22,7 @@ export default {
},
methods: {
click() {
if (!screenfull.enabled) {
if (!screenfull.isEnabled) {
this.$message({
message: 'you browser can not work',
type: 'warning'
@@ -35,12 +35,12 @@ export default {
this.isFullscreen = screenfull.isFullscreen
},
init() {
if (screenfull.enabled) {
if (screenfull.isEnabled) {
screenfull.on('change', this.change)
}
},
destroy() {
if (screenfull.enabled) {
if (screenfull.isEnabled) {
screenfull.off('change', this.change)
}
}
+104 -71
View File
@@ -2,19 +2,21 @@
<template>
<div class="trialsTab">
<el-tabs v-model="trialsTab" @tab-click="clickTab">
<el-tab-pane v-for="item of trialsRouter.children.find(v => {return v.name == 'TrialsPanel'}).children" :key="`tab${item.path}`" :disabled="TotalNeedSignTrialDocCount !== 0 && item.path !== '/trials/trials-panel/attachments'" :label="$t(item.LanguageMark)" :name="item.path">
<el-tabs v-if="!item.tabHiddn" v-model="trialsTabChild" @tab-click="clickTab" style="background-color: #f5f7fa;">
<template v-for="item1 of item.children">
<el-tab-pane
v-if="TrialConfig && isShow(item1.path)"
:key="`tab1${item1.path}`"
:disabled="TotalNeedSignTrialDocCount !== 0 && item1.path !== '/trials/trials-panel/attachments/self-attachment' || (TrialStatusStr === 'Initializing' && (item1.path === '/trials/trials-panel/setting/personnel-manage' || item1.path === '/trials/trials-panel/setting/qc-question' || item1.path === '/trials/trials-panel/setting/reading-unit' || item1.path === '/trials/trials-panel/setting/medical-audit' || item1.path === '/trials/trials-panel/setting/email-manage'))"
:label="$t(item1.LanguageMark)"
:name="item1.path"
/>
</template>
</el-tabs>
</el-tab-pane>
<template v-for="item of trialsRouter.children.find(v => { return v.name == 'TrialsPanel' }).children">
<el-tab-pane :key="`tab${item.path}`"
:disabled="TotalNeedSignTrialDocCount !== 0 && item.path !== '/trials/trials-panel/attachments'"
:label="$t(item.LanguageMark)" :name="item.path" v-if="isShowNode(item)">
<el-tabs v-if="!item.tabHiddn" v-model="trialsTabChild" @tab-click="clickTab"
style="background-color: #f5f7fa;">
<template v-for="item1 of item.children">
<el-tab-pane v-if="TrialConfig && isShow(item1.path)" :key="`tab1${item1.path}`"
:disabled="TotalNeedSignTrialDocCount !== 0 && item1.path !== '/trials/trials-panel/attachments/self-attachment' || (TrialStatusStr === 'Initializing' && (item1.path === '/trials/trials-panel/setting/personnel-manage' || item1.path === '/trials/trials-panel/setting/qc-question' || item1.path === '/trials/trials-panel/setting/reading-unit' || item1.path === '/trials/trials-panel/setting/medical-audit' || item1.path === '/trials/trials-panel/setting/email-manage'))"
:label="$t(item1.LanguageMark)" :name="item1.path" />
</template>
</el-tabs>
</el-tab-pane>
</template>
</el-tabs>
<div style="position: absolute;top: 0;right: 0;display: flex;justify-content: space-between;align-items: center">
<trials-select />
@@ -72,10 +74,11 @@ 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=/'
},
@@ -83,7 +86,7 @@ export default {
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.IsAdditionalAssessment = res.OtherInfo.IsAdditionalAssessment
this.TrialStatusStr = TrialStatusStr
await store.dispatch('user/setTotalNeedSignTrialDocCount', total)
await store.dispatch('user/setTrialStatusStr', TrialStatusStr)
@@ -100,7 +103,7 @@ export default {
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.IsAdditionalAssessment = res.OtherInfo.IsAdditionalAssessment
this.TrialStatusStr = TrialStatusStr
await store.dispatch('user/setTotalNeedSignTrialDocCount', total)
await store.dispatch('user/setTrialStatusStr', TrialStatusStr)
@@ -110,15 +113,25 @@ export default {
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: {
isShow: function(path) {
isShowNode(item) {
if (item.path === '/trials/trials-panel/trial-summary' && item.children.length === 1 && item.children[0].path === '/trials/trials-panel/trial-summary/report-forms' && !this.$store.state.trials.config.IsExternalViewTrialChart && this.hasPermi(['role:cmm', 'role:cpm', 'role:ea', 'role:mc', 'role:smm', 'role:spm'])) {
return false
}
return true
},
isShow: function (path) {
var isShow = true
let chartList = [
'/trials/trials-panel/trial-summary/report-forms'
]
var qualityList = [
'/trials/trials-panel/visit/crc-question',
'/trials/trials-panel/visit/qc-check',
@@ -134,6 +147,9 @@ export default {
'/trials/trials-panel/trial-summary/image-inspect',
'/trials/trials-panel/trial-summary/push-record',
]
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
}
if (this.TrialConfig.QCProcessEnum === 0 && ~qualityList.indexOf(path)) {
isShow = false
}
@@ -187,15 +203,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)
@@ -205,56 +225,69 @@ export default {
}
</script>
<style lang="scss">
.trialsTab{
.my_icon_box{
padding:0 20px 0 0;
transition: color,transform .3s;
cursor: pointer;
color: #666;
display: flex;
justify-content: center;
align-items: center;
&:hover{
color: #428bca;
}
span{
margin-left: 3px;
font-size: 14px;
}
.trialsTab {
.my_icon_box {
padding: 0 20px 0 0;
transition: color, transform .3s;
cursor: pointer;
color: #666;
display: flex;
justify-content: center;
align-items: center;
&:hover {
color: #428bca;
}
.el-tabs__header{
margin-bottom: 3px;
}
.el-tabs__item.is-active{
font-weight: bold;
}
position: relative;
.el-input--medium .el-input__inner{
height: 44px;line-height: 44px;width: 280px;
border: none;
text-align: right;
span {
margin-left: 3px;
font-size: 14px;
}
}
.base-model-wrapper{
.el-dialog__header{
padding: 10px;
.el-dialog__headerbtn{
top: 10px;
right: 10px;
}
}
.el-dialog__body{
padding:10px 10px 10px 10px;
.base-modal-body{
min-height: 100px;
max-height:650px;
overflow-y: auto;
padding: 10px;
border: 1px solid #e0e0e0;
}
}
.el-dialog__footer{
padding: 10px;
.el-tabs__header {
margin-bottom: 3px;
}
.el-tabs__item.is-active {
font-weight: bold;
}
position: relative;
.el-input--medium .el-input__inner {
height: 44px;
line-height: 44px;
width: 280px;
border: none;
text-align: right;
}
}
.base-model-wrapper {
.el-dialog__header {
padding: 10px;
.el-dialog__headerbtn {
top: 10px;
right: 10px;
}
}
.el-dialog__body {
padding: 10px 10px 10px 10px;
.base-modal-body {
min-height: 100px;
max-height: 650px;
overflow-y: auto;
padding: 10px;
border: 1px solid #e0e0e0;
}
}
.el-dialog__footer {
padding: 10px;
}
}
</style>
@@ -68,7 +68,7 @@
<el-table-column :label="$t('common:action:action')" fixed="right" width="150">
<template slot-scope="scope">
<!--预览--->
<el-button circle icon="el-icon-view" :title="$t('download:button:preview')"
<el-button circle icon="el-icon-view" :title="$t('download:button:preview')" :disabled="forbid"
@click.stop="preview(scope.row)" />
<!--下载--->
<el-button circle icon="el-icon-download" :title="$t('download:button:download')"
@@ -111,6 +111,10 @@ export default {
type: Boolean,
default: false,
},
forbid: {
type: Boolean,
default: false,
},
SubjectId: {
type: String,
default: '',
+44 -9
View File
@@ -8,21 +8,49 @@
<script>
import { getReportsChartData } from "@/api/reading"
import moment from "moment"
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: {
@@ -152,7 +180,13 @@ export default {
text: obj.title,
textStyle: {
color: "#fff"
}
},
left: 0,
top: 0
},
grid: {
left: 50,
bottom: 30
},
tooltip: {
trigger: 'axis',
@@ -194,6 +228,7 @@ export default {
}
},
axisLine: {
show: true,
lineStyle: {
color: '#fff',
}
+134
View File
@@ -0,0 +1,134 @@
<template>
<div id="timeTag" :style="`color:${suggestionTextColor};background-color: inherit;`">{{ time }}</div>
</template>
<script>
import moment from 'moment'
export default {
name: "timeTag",
data() {
return {
time: '',
timer: null,
suggestionTextColor: '#909399',
resizeObserver: null
}
},
created() {
this.getTime()
},
async mounted() {
this.resizeObserver = new ResizeObserver(entries => {
entries.forEach(entry => {
// 获取元素的新尺寸
// const { width, height } = entry.contentRect;
// console.log(`元素宽度:${width}px, 高度:${height}px`);
// 在此可以将宽高值保存到 data 中
// this.elementWidth = width;
// this.elementHeight = height;
this.dragDoc()
});
});
const body = document.getElementsByTagName('body')[0]
// 开始监听指定元素
this.resizeObserver.observe(body);
this.dragDoc()
this.setColor()
},
methods: {
getTime() {
if (this.timer) {
clearInterval(this.timer)
this.timer = null
}
this.timer = setInterval(async () => {
this.time = moment(new Date()).format('YYYY-MM-DD HH:mm:ss Z z')
}, 1000)
},
dragDoc() {
const box = document.querySelector('#timeTag')
const body = document.getElementsByTagName('body')[0]
let maxLeft = body.offsetWidth - box.offsetWidth
let maxTop = body.offsetHeight - box.offsetHeight - 30
box.style.cssText += `left:${maxLeft}px;top:7px;`
const sty = (function () {
if (window.document.currentStyle) {
return (dom, attr) => dom.currentStyle[attr]
} else {
return (dom, attr) => getComputedStyle(dom, false)[attr]
}
})()
box.onmousedown = (e) => {
// 鼠标按下,计算当前元素距离可视区的距离
const disX = e.clientX - box.offsetLeft
const disY = e.clientY - box.offsetTop
// 获取到的值带px 正则匹配替换
let styL = sty(box, 'left')
let styT = sty(box, 'top')
// 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
if (styL.includes('%')) {
styL = +document.body.clientWidth * (+styL.replace(/\%/g, '') / 100)
styT = +document.body.clientHeight * (+styT.replace(/\%/g, '') / 100)
} else {
styL = +styL.replace(/\px/g, '')
styT = +styT.replace(/\px/g, '')
}
const oldMousemove = document.onmousemove
document.onmousemove = function (e) {
oldMousemove(e)
// 通过事件委托,计算移动的距离
let left = e.clientX - disX
let top = e.clientY - disY
if (left < 0) {
left = 0
}
if (left > maxLeft) {
left = maxLeft
}
if (top < 0) {
top = 0
}
if (top > maxTop) {
top = maxTop
}
// 移动当前元素
box.style.cssText += `;left:${left}px;top:${top}px;`
}
document.onmouseup = function (e) {
document.onmousemove = oldMousemove
document.onmouseup = null
}
}
},
setColor() {
this.suggestionTextColor = "#909399"
let pathList = ['/showvisitdicoms', '/showdicom', '/readingDicoms', '/petct', '/noneDicomReading']
if (pathList.includes(window.location.pathname)) {
this.suggestionTextColor = "#fff"
}
}
},
destroyed() {
if (this.timer) {
clearInterval(this.timer)
this.timer = null
}
if (this.resizeObserver) {
this.resizeObserver.disconnect();
}
}
}
</script>
<style lang="scss" scoped>
#timeTag {
position: fixed;
z-index: 99999;
cursor: move;
width: 195px;
font-size: 14px;
}
</style>
+1 -1
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;
}
@@ -42,7 +42,7 @@
<template slot-scope="scope">
<div class="btnBox">
<!--上传--->
<form id="inputForm" :ref="`uploadForm_${scope.row.Id}`" enctype="multipart/form-data">
<form id="inputForm" :ref="`uploadForm_${scope.row.Id}`" enctype="multipart/form-data" v-if="!forbid">
<div class="form-group" style="margin-right: 10px">
<div :id="`directoryInputWrapper_${scope.row.Id}`" class="btn btn-link file-input">
<el-button circle icon="el-icon-upload2" :disabled="btnLoading" :loading="btnLoading"
@@ -69,7 +69,7 @@
</el-table>
<div style="margin: 10px 0" class="top">
<span>{{ $t('upload:dicom:uploadTitle') }}</span>
<div class="btnBox">
<div class="btnBox" v-if="!forbid">
<span style="margin-right: 10px">
{{ $store.state.trials.uploadTip }}
</span>
@@ -354,6 +354,10 @@ export default {
return {}
},
},
forbid: {
type: Boolean,
default: false,
},
TaskId: {
type: String,
default: '',
@@ -708,12 +712,12 @@ 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
@@ -753,6 +757,13 @@ export default {
fileList: [],
dicomInfo: {
studyId: data.string('x00200010'),
Manufacturer: data.string('x00080070') || '',
ManufacturerModelName: data.string('x000801090') || '',
DeviceSerialNumber: data.string('x00181000') || '',
DeviceUID: data.string('x00181002') || '',
SoftwareVersions: data.string('x00181020') || '',
PatientWeight: data.string('x00101030') || '',
DicomStudyDate: data.string('x00080020'),
DicomStudyTime: data.string('x00080030'),
studyUid: studyUid,
@@ -865,6 +876,9 @@ export default {
)
seriesItem = {
seriesUid: seriesUid,
RadiopharmaceuticalInformationSequence: data.string('x00540016') || "",
AcquisitionDate: data.string('x00080022') || "",
DicomSeriesDate: data.string('x00080021'),
DicomSeriesTime: data.string('x00080031'),
seriesNumber: data.intString('x00200011') || 1,
@@ -905,6 +919,20 @@ export default {
instanceTime = time ? `${date} ${time}` : `${date} 00:00:00`
}
instanceItem = {
PhotometricInterpretation: data.string('x00280004') || '',
BitsAllocated: data.uint16('x00280100') || '',
PixelRepresentation: data.uint16('x00280103') || '',
RescaleIntercept: data.string('x00281052') || '',
RescaleSlope: data.string('x00281053') || '',
ImagePositionPatient: data.string('x00200032') || '',
ImageOrientationPatient: data.string('x00200037') || '',
SequenceOfUltrasoundRegions: data.string('x00186011') || '',
FrameTime: data.string('x00181063') || '',
CorrectedImage: data.string('x00280051') || '',
Units: data.string('x00541001') || '',
DecayCorrection: data.string('x00541102') || '',
EncapsulatedDocument: data.string('x00420011') || '',
instanceUid: instanceUid,
SOPClassUID: data.string('x00080016'),
TransferSytaxUID: data.string('x00020010'),
@@ -1136,9 +1164,17 @@ export default {
DicomStudyDate: dicomInfo.DicomStudyDate,
DicomStudyTime: dicomInfo.DicomStudyTime,
seriesList: [],
Manufacturer: dicomInfo.Manufacturer,
ManufacturerModelName: dicomInfo.ManufacturerModelName,
DeviceSerialNumber: dicomInfo.DeviceSerialNumber,
DeviceUID: dicomInfo.DeviceUID,
SoftwareVersions: dicomInfo.SoftwareVersions,
PatientWeight: dicomInfo.PatientWeight,
},
}
let arr = []
let uploadBatchId = scope.$guid()
for (let i = 0; i < seriesList.length; i++) {
let v = seriesList[i]
let instanceList = []
@@ -1178,6 +1214,20 @@ export default {
windowWidth: o.windowWidth,
path: o.myPath,
FileSize: o.FileSize,
PhotometricInterpretation: o.PhotometricInterpretation,
BitsAllocated: o.BitsAllocated,
PixelRepresentation: o.PixelRepresentation,
RescaleIntercept: o.RescaleIntercept,
RescaleSlope: o.RescaleSlope,
ImagePositionPatient: o.ImagePositionPatient,
ImageOrientationPatient: o.ImageOrientationPatient,
SequenceOfUltrasoundRegions: o.SequenceOfUltrasoundRegions,
FrameTime: o.FrameTime,
CorrectedImage: o.CorrectedImage,
Units: o.Units,
DecayCorrection: o.DecayCorrection,
EncapsulatedDocument: o.EncapsulatedDocument,
})
Record.Uploaded.push(name)
dicomInfo.failedFileCount++
@@ -1217,6 +1267,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) {
@@ -1240,7 +1300,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(
@@ -1275,6 +1345,20 @@ export default {
windowWidth: o.windowWidth,
path: scope.$getObjectName(res.url),
FileSize: o.FileSize,
PhotometricInterpretation: o.PhotometricInterpretation,
BitsAllocated: o.BitsAllocated,
PixelRepresentation: o.PixelRepresentation,
RescaleIntercept: o.RescaleIntercept,
RescaleSlope: o.RescaleSlope,
ImagePositionPatient: o.ImagePositionPatient,
ImageOrientationPatient: o.ImageOrientationPatient,
SequenceOfUltrasoundRegions: o.SequenceOfUltrasoundRegions,
FrameTime: o.FrameTime,
CorrectedImage: o.CorrectedImage,
Units: o.Units,
DecayCorrection: o.DecayCorrection,
EncapsulatedDocument: o.EncapsulatedDocument,
})
o.myPath = scope.$getObjectName(res.url)
Record.Uploaded.push(name)
@@ -1323,6 +1407,9 @@ export default {
bodyPartExamined: dicomInfo.bodyPart,
instanceList: instanceList,
ImageResizePath: ImageResizePath,
RadiopharmaceuticalInformationSequence: v.RadiopharmaceuticalInformationSequence,
AcquisitionDate: v.AcquisitionDate,
})
}
let text = JSON.stringify(Record)
@@ -1364,7 +1451,20 @@ 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)
}
@@ -1382,6 +1482,7 @@ export default {
if (scope.IsImageSegment) {
params.IsImageSegmentLabel = true
}
params.UploadBatchId = uploadBatchId
addOrUpdateArchiveTaskStudy(params)
.then((res) => {
if (dicomInfo.failedFileCount === dicomInfo.fileCount) {
@@ -6,12 +6,12 @@
<el-tab-pane :label="$t('uploadDicomAndNonedicom:label:dicom')" name="dicom">
<dicomFile v-if="activeName === 'dicom'" :SubjectId="SubjectId" :SubjectCode="SubjectCode"
:Criterion="Criterion" :TaskId="VisitTaskId" :isUpload.sync="isUpload"
:isReadingTaskViewInOrder="isReadingTaskViewInOrder" :IsImageSegment="IsImageSegment" />
:isReadingTaskViewInOrder="isReadingTaskViewInOrder" :IsImageSegment="IsImageSegment" :forbid="forbid" />
</el-tab-pane>
<el-tab-pane :label="$t('uploadDicomAndNonedicom:label:nonedicom')" name="nonedicom">
<nonedicomFile v-if="activeName === 'nonedicom'" :SubjectId="SubjectId" :SubjectCode="SubjectCode"
:Criterion="Criterion" :VisitTaskId="VisitTaskId" :isUpload.sync="isUpload"
:IsImageSegment="IsImageSegment" />
:Criterion="Criterion" :VisitTaskId="VisitTaskId" :isUpload.sync="isUpload" :IsImageSegment="IsImageSegment"
:forbid="forbid" />
</el-tab-pane>
</el-tabs>
</el-dialog>
@@ -29,6 +29,10 @@ export default {
type: Boolean,
default: false,
},
forbid: {
type: Boolean,
default: false,
},
SubjectId: {
type: String,
default: '',
@@ -82,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;
@@ -109,7 +109,7 @@
<el-button circle :disabled="scope.row.UploadedFileCount <= 0" icon="el-icon-view"
:title="$t('upload:nonedicom:button:preview')" @click.stop="handlePreviewNoneDicomFiles(scope.row)" />
<!--上传--->
<el-button circle icon="el-icon-upload2" :title="$t('upload:nonedicom:button:upload')"
<el-button circle icon="el-icon-upload2" :title="$t('upload:nonedicom:button:upload')" v-if="!forbid"
@click.native.prevent="handleUpload(scope.row)" />
<!--删除--->
<el-button :disabled="scope.row.UploadedFileCount <= 0 ||
@@ -239,6 +239,10 @@ export default {
type: String,
default: '',
},
forbid: {
type: Boolean,
default: false,
},
SubjectCode: {
type: String,
default: '',
@@ -544,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)
@@ -556,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
@@ -572,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({
@@ -606,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
}
},
// file上传到oss
async fileToOss(path, file, item) {
async fileToOss(path, file, item, uploadBatchId) {
try {
let res = await this.OSSclient.multipartUpload(
{
@@ -625,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) {
+16
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
+19
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)
}
}
+1
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

+1
View File
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1762494352859" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="22987" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M896 832a128 128 0 0 1-128 128H256a128 128 0 0 1-128-128V192a128 128 0 0 1 128-128h512a128 128 0 0 1 128 128v640zM192 768v64a64 64 0 0 0 56.512 63.552L256 896h512a64 64 0 0 0 63.552-56.512L832 832v-64H192z m448-640H512v206.912l64-38.4 64 38.464V128z m-448 576h640V192a64 64 0 0 0-56.512-63.552L768 128h-64v263.488a32 32 0 0 1-48.448 27.456L576 371.2l-79.552 47.744A32 32 0 0 1 448 391.424V128H256a64 64 0 0 0-63.552 56.512L192 192v512z" fill="#e6e6e6" p-id="22988"></path></svg>

After

Width:  |  Height:  |  Size: 812 B

+12
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

+1
View File
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1766048712120" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6026" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M924.520311 484.074997c7.439436 0 13.884218 2.726088 19.336394 8.16496 5.437849 5.452175 8.16496 11.897981 8.16496 19.336394 0 7.452739-2.726088 13.898545-8.16496 19.336394-5.452175 5.452175-11.897981 8.16496-19.336394 8.16496l-83.363639 0c-3.437286 40.110533-13.617136 77.777411-30.508851 113.012913-16.906042 35.236526-38.673811 66.175165-65.315588 92.816942s-57.58144 48.422849-92.816942 65.315588c-35.236526 16.906042-72.915683 26.790156-113.012913 29.650297l0 84.223217c0 7.439436-2.726088 13.884218-8.16496 19.336394-5.451152 5.437849-11.896958 8.16496-19.336394 8.16496-7.452739 0-13.898545-2.726088-19.336394-8.16496-5.452175-5.452175-8.16496-11.897981-8.16496-19.336394l0-84.223217c-40.110533-2.860141-77.777411-12.743232-113.012913-29.650297-35.236526-16.892739-66.175165-38.673811-92.816942-65.315588s-48.422849-57.58144-65.315588-92.816942c-16.906042-35.236526-27.071565-72.90238-30.508851-113.012913L99.479689 539.077705c-7.452739 0-13.898545-2.711761-19.336394-8.16496-5.452175-5.437849-8.16496-11.883655-8.16496-19.336394 0-7.439436 2.712785-13.884218 8.16496-19.336394 5.437849-5.437849 11.883655-8.16496 19.336394-8.16496l83.363639 0c3.437286-40.09723 13.602809-77.777411 30.508851-113.012913 16.892739-35.236526 38.673811-66.175165 65.315588-92.816942s57.58144-48.409546 92.816942-65.315588c35.236526-16.892739 72.90238-26.77583 113.012913-29.650297L484.497623 99.05604c0-7.439436 2.712785-13.885242 8.16496-19.336394 5.437849-5.437849 11.883655-8.16496 19.336394-8.16496 7.440459 0 13.885242 2.726088 19.336394 8.16496 5.437849 5.452175 8.16496 11.896958 8.16496 19.336394l0 84.223217c40.098253 2.874467 77.777411 12.757558 113.012913 29.650297 35.236526 16.906042 66.175165 38.673811 92.816942 65.315588s48.409546 57.58144 65.315588 92.816942c16.892739 35.236526 27.071565 72.916706 30.508851 113.012913L924.520311 484.074997zM484.498646 238.281965c-32.657794 2.874467-63.167668 11.320836-91.527576 25.352411-28.360931 14.046924-53.431933 31.946596-75.198679 53.713342-21.781072 21.781072-39.680744 46.837748-53.713342 75.198679-14.045901 28.360931-22.49227 58.869782-25.352411 91.527576L484.498646 484.073974 484.498646 238.281965zM238.70459 539.077705c2.860141 32.657794 11.30651 63.166645 25.352411 91.527576 14.032598 28.360931 31.933293 53.431933 53.713342 75.198679 21.766746 21.781072 46.837748 39.680744 75.198679 53.713342 28.360931 14.046924 58.869782 22.49227 91.527576 25.352411L484.496599 539.077705 238.70459 539.077705zM539.501354 484.074997l245.793032 0c-2.874467-32.657794-11.320836-63.167668-25.352411-91.527576-14.046924-28.360931-31.946596-53.417607-53.713342-75.198679-21.780049-21.766746-46.837748-39.667441-75.198679-53.713342-28.360931-14.032598-58.869782-22.478967-91.527576-25.352411L539.502377 484.074997zM539.501354 784.870738c32.657794-2.860141 63.167668-11.30651 91.527576-25.352411 28.360931-14.032598 53.41863-31.93227 75.198679-53.713342 21.766746-21.766746 39.667441-46.837748 53.713342-75.198679 14.032598-28.360931 22.478967-58.869782 25.352411-91.527576L539.501354 539.078729 539.501354 784.870738z" p-id="6027" fill="#ffffff"></path></svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 418 B

After

Width:  |  Height:  |  Size: 5.2 KiB

+1
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

+1
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

+1
View File
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="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
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="1762485648235" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10985" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M543.378286 515.949714V104.521143a31.451429 31.451429 0 0 0-62.976 0v411.501714a125.44 125.44 0 0 0 0.292571 242.834286 31.670857 31.670857 0 0 0-0.292571 3.949714v156.745143a31.451429 31.451429 0 0 0 62.902857 0v-156.745143a31.597714 31.597714 0 0 0-0.219429-3.876571 125.513143 125.513143 0 0 0 0.292572-242.980572z m12.946285 165.814857a62.683429 62.683429 0 1 1-88.649142-88.722285 62.683429 62.683429 0 0 1 88.649142 88.722285zM229.522286 202.459429a31.597714 31.597714 0 0 0 0.292571-3.949715V104.594286a31.305143 31.305143 0 1 0-62.610286 0v93.988571c0 1.389714 0 2.706286 0.146286 4.022857a125.44 125.44 0 0 0 0 242.907429 31.670857 31.670857 0 0 0-0.146286 3.876571v470.235429a31.305143 31.305143 0 1 0 62.610286 0V449.316571a31.597714 31.597714 0 0 0-0.292571-3.803428 125.44 125.44 0 0 0 0-243.053714z m13.312 165.888a62.683429 62.683429 0 1 1-88.649143-88.722286 62.683429 62.683429 0 0 1 88.649143 88.722286zM950.857143 324.022857c0-58.587429-40.228571-107.739429-94.500572-121.563428a31.670857 31.670857 0 0 0 0.146286-3.949715V104.594286a31.305143 31.305143 0 0 0-62.610286 0v93.988571c0 1.389714 0.146286 2.706286 0.292572 4.022857a125.44 125.44 0 0 0 0 242.907429 31.670857 31.670857 0 0 0-0.292572 3.949714v470.162286a31.305143 31.305143 0 1 0 62.610286 0V449.389714c0-1.316571 0-2.56-0.146286-3.876571A125.513143 125.513143 0 0 0 950.857143 324.022857z m-81.042286 44.324572a62.683429 62.683429 0 1 1-88.649143-88.722286 62.683429 62.683429 0 0 1 88.649143 88.722286z" p-id="10986" fill="#e6e6e6"></path></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+1
View File
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="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

+1
View File
@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1764830990216" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5566" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M192 170.666667a21.333333 21.333333 0 0 0-21.333333 21.333333v640a21.333333 21.333333 0 0 0 21.333333 21.333333h640a21.333333 21.333333 0 0 0 21.333333-21.333333v-640a21.333333 21.333333 0 0 0-21.333333-21.333333h-640zM85.333333 192A106.666667 106.666667 0 0 1 192 85.333333h640A106.666667 106.666667 0 0 1 938.666667 192v640a106.666667 106.666667 0 0 1-106.666667 106.666667h-640A106.666667 106.666667 0 0 1 85.333333 832v-640z" fill="#ffffff" p-id="5567"></path><path d="M173.653333 170.666667c-1.621333 5.034667-2.986667 12.373333-2.986666 21.333333v640c0 8.96 1.365333 16.298667 2.986666 21.333333h292.693334c1.621333-5.034667 2.986667-12.373333 2.986666-21.333333v-640c0-8.96-1.365333-16.298667-2.986666-21.333333H173.653333zM99.157333 127.658667C107.306667 111.530667 126.293333 85.333333 160 85.333333h320c33.706667 0 52.736 26.197333 60.8 42.325334 9.301333 18.688 13.866667 41.472 13.866667 64.341333v640c0 22.869333-4.565333 45.653333-13.866667 64.341333-8.106667 16.128-27.093333 42.325333-60.8 42.325334h-320c-33.706667 0-52.736-26.197333-60.8-42.325334C89.856 877.653333 85.333333 854.869333 85.333333 832v-640c0-22.869333 4.522667-45.653333 13.866667-64.341333z" fill="#ffffff" p-id="5568"></path><path d="M170.666667 170.666667v298.666666h298.666666V170.666667H170.666667z m-85.333334-10.666667C85.333333 118.784 118.784 85.333333 160 85.333333h320c41.216 0 74.666667 33.450667 74.666667 74.666667v320A74.666667 74.666667 0 0 1 480 554.666667h-320A74.666667 74.666667 0 0 1 85.333333 480v-320z" fill="#ffffff" p-id="5569"></path></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

+1
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

+1
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

+2 -2
View File
@@ -1608,7 +1608,7 @@ Enroll: Enrolled Confirmation`,
'trials:notice:table:appendix': `Attachment`,
'trials:notice:table:isRead': `Readed`,
'trials:notice:action:detail': `Details`,
'login:title:system': `IRC Imaging System`,
'login:title:system': `EIC lmaging Trial Management System`,
'login:form:userName': `User ID`,
'login:form:password': `Password`,
'login:button:login': `Login`,
@@ -1672,7 +1672,7 @@ Enroll: Enrolled Confirmation`,
'trials:tab:trials': `My Trials`,
'trials:trials:title:back': `Return`,
'trials:trials:title:backTrialList': `Return to trial list`,
'trials:trials:title:eics': `IRC Imaging System`,
'trials:trials:title:eics': `EIC lmaging Trial Management System`,
'trials:tab:reading': `Reads`,
'trials:tab:pendingReadingTasks': `Pending`,
'trials:pendingReadingTasks:table:subjectCode': `Subject ID`,
+1 -1
View File
@@ -218,7 +218,7 @@ export default {
.right-menu {
// float: right;
height: 100%;
line-height: 50px;
line-height: 65px;
// position: relative;
&:focus {
outline: none;
@@ -3,14 +3,16 @@
<template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow">
<app-link v-if="!onlyOneChild.hidden" :to="resolvePath(onlyOneChild.path)">
<el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}" @click="handleClick(onlyOneChild.meta.title)">
<item :icon="onlyOneChild.meta ? (onlyOneChild.meta.icon||(item.meta&&item.meta.icon)) : null" :title="onlyOneChild.meta ? onlyOneChild.meta.title : null" />
<!-- :title="onlyOneChild.meta ? onlyOneChild.meta.title : null" -->
<item :icon="onlyOneChild.meta ? (onlyOneChild.meta.icon||(item.meta&&item.meta.icon)) : null" :title="onlyOneChild.meta.i18n ? $t(onlyOneChild.meta.i18n) : null" />
</el-menu-item>
</app-link>
</template>
<el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>
<template slot="title">
<item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" />
<!-- :title="item.meta.title" -->
<item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.i18n ? $t(item.meta.i18n) : null" />
</template>
<sidebar-item
v-for="child in item.children"
+2 -2
View File
@@ -1,11 +1,11 @@
<template>
<el-dropdown
style="height:50px;line-height: 60px;"
style="height:50px;line-height: 65px;"
@command="handleSetLanguage"
>
<span class="el-dropdown-link">
<svg-icon icon-class="language" style="font-size:25px;margin:0 10px;" />
<svg-icon icon-class="language" style="font-size:20px;margin:0 10px;" />
<!-- {{ language==='zh'?'语言:中文':'Language: English' }} -->
<!-- <i class="el-icon-arrow-down el-icon--right" /> -->
</span>
+4 -2
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;
+7 -1
View File
@@ -26,8 +26,9 @@ Vue.use(VueClipboard)
import permission from './utils/permission'
Vue.use(permission)
import { formatSize } from "./utils"
import { formatSize, guidToColor } from "./utils"
Vue.prototype.$FormatSize = formatSize
Vue.prototype.$GuidToColor = guidToColor
import Viewer from 'v-viewer'
import './assets/css/viewer.css'
@@ -61,12 +62,17 @@ import Onlyoffice from '@/components/Preview_onlyoffice/index'
Vue.use(Onlyoffice)
import Video from '@/components/Preview_video/index'
Vue.use(Video)
import AGR from '@/components/AGR/index'
Vue.use(AGR)
import MFA from '@/components/MFA/index'
Vue.use(MFA)
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)
+6 -6
View File
@@ -79,6 +79,12 @@ export const constantRoutes = [
name: 'emailRecompose',
hidden: true
},
{
path: '/user-recompose',
component: () => import('@/views/email-recompose/index'),
name: 'userRecompose',
hidden: true
},
{
path: '/joinVerify',
component: () => import('@/views/joinVerify/index'),
@@ -150,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',
+1 -1
View File
@@ -1,7 +1,7 @@
// eslint-disable-next-line no-undef
module.exports = {
title: 'IRC Imaging System',
title: 'EIC lmaging Trial Management System',
/**
* @type {boolean} true | false
+1
View File
@@ -39,6 +39,7 @@ function filterAsyncRouter(asyncRouterMap = [], lastRouter = false, type = false
// eslint-disable-next-line
route.meta = eval('(' + route.Meta + ')')
route.meta.icon = route.MenuIcon
route.meta.i18n = route.LanguageMark
delete route.Meta
}
delete route.MenuIcon
+8
View File
@@ -276,6 +276,14 @@ const actions = {
async logout({ commit, state }) {
try {
removeToken() // must remove token first
let data = {
IdentityUserId: zzSessionStorage.getItem('identityUserId')
}
if (zzSessionStorage.getItem('userId') && zzSessionStorage.getItem('userId') !== 'undefined') {
data.UserRoleId = zzSessionStorage.getItem('userId')
} else {
data.UserRoleId = null
}
await loginOut({
UserRoleId: zzSessionStorage.getItem('userId'),
IdentityUserId: zzSessionStorage.getItem('identityUserId'),
+46 -39
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,133 +206,139 @@ 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
.el-descriptions__body
.el-descriptions__table
.el-descriptions-item__cell.is-left {
text-align: left;
}
.el-descriptions__body .el-descriptions__table .el-descriptions-item__cell.is-center {
text-align: center
.el-descriptions__body
.el-descriptions__table
.el-descriptions-item__cell.is-center {
text-align: center;
}
.el-descriptions__body .el-descriptions__table .el-descriptions-item__cell.is-right {
text-align: right
.el-descriptions__body
.el-descriptions__table
.el-descriptions-item__cell.is-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 {
@@ -350,7 +357,7 @@ body .el-table th.gutter {
top: 0;
width: 100%;
height: 100%;
opacity: .5;
opacity: 0.5;
background: #000;
z-index: 3999;
}
@@ -371,4 +378,4 @@ body .el-table th.gutter {
height: 20px !important;
vertical-align: -0.4em !important;
cursor: pointer;
}
}
+3 -2
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
+1 -1
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;
+343
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,
};
+32
View File
@@ -0,0 +1,32 @@
import FingerprintJS from '@fingerprintjs/fingerprintjs';
export const customAgent = () => {
return new Promise(async resolve => {
const fpPromise = await FingerprintJS.load({
monitoring: false
});
const result = await fpPromise.get({
products: ['fonts', 'screen', 'canvas'],
extendedData: true,
debug: false
})
// const filteredComponents = Object.fromEntries(
// Object.entries(result.components)
// .filter(([key, value]) => value.confidence > 0.5)
// );
resolve({
...result,
});
// fpPromise
// .then(fp => fp.get({
// products: ['fonts', 'screen', 'canvas'],
// extendedData: true,
// debug: true
// }))
// .then(result => {
// // 自定义数据转换
// });
});
}
+1 -1
View File
@@ -1,6 +1,6 @@
import defaultSettings from '@/settings'
const title = defaultSettings.title || 'IRC Imaging System'
const title = defaultSettings.title || 'EIC lmaging Trial Management System'
export default function getPageTitle(pageTitle) {
if (pageTitle) {
+24
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;
+106
View File
@@ -196,4 +196,110 @@ export function workSpeedclose(isForce = false) {
lastPercentage = 0;
imageId = null;
percentageById = {};
}
function readDirectoryEntries(directoryReader) {
return new Promise((resolve, reject) => {
let entries = [];
function readBatch() {
directoryReader.readEntries(
(results) => {
if (results.length) {
entries = entries.concat(results);
readBatch();
} else {
resolve(entries);
}
},
(error) => reject(error)
);
}
readBatch();
});
}
export async function readEntry(entry) {
const files = [];
// 如果是文件夹,创建读取器并递归读取其内容
if (entry.isDirectory) {
const directoryReader = entry.createReader();
const entries = await readDirectoryEntries(directoryReader)
// 递归读取文件夹内的每一项
for (const subEntry of entries) {
const subFiles = await readEntry(subEntry);
files.push(...subFiles);
}
}
// 如果是文件,则将其转换为File对象
else if (entry.isFile) {
const file = await new Promise((resolve, reject) => {
entry.file(resolve, reject); // entry.file()是异步的
});
files.push(file);
}
return files;
}
// 使用FNV-1a哈希算法确保相同GUID产生相同结果
function fnv1aHash(str) {
const FNV_OFFSET_BASIS = 2166136261;
const FNV_PRIME = 16777619;
let hash = FNV_OFFSET_BASIS;
for (let i = 0; i < str.length; i++) {
hash ^= str.charCodeAt(i);
hash = (hash * FNV_PRIME) >>> 0; // 使用无符号右移确保结果为无符号32位整数
}
return hash;
}
// RGB转十六进制
function rgbToHex(r, g, b) {
return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase();
}
// HSL转RGB函数
function hslToRgb(h, s, l) {
let r, g, b;
if (s === 0) {
r = g = b = l; // 灰色
} else {
const hue2rgb = (p, q, t) => {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1 / 6) return p + (q - p) * 6 * t;
if (t < 1 / 2) return q;
if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
return p;
};
const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
const p = 2 * l - q;
r = hue2rgb(p, q, h + 1 / 3);
g = hue2rgb(p, q, h);
b = hue2rgb(p, q, h - 1 / 3);
}
return { r, g, b };
}
export function guidToColor(guid) {
// 移除GUID中的连字符和花括号(如果有)
let cleanGuid = guid.replace(/[{}()-]/g, '');
// 计算GUID的哈希值
const hash = fnv1aHash(cleanGuid);
// 使用哈希值生成HLS颜色(确保高区分度)
// 将哈希值映射到0-1之间
const h = (hash & 0xFFFF) / 0xFFFF; // 使用前16位作为色相
const s = ((hash >> 16) & 0xFF) / 0xFF * 0.6 + 0.4; // 饱和度在0.4-1.0之间
const l = ((hash >> 24) & 0xFF) / 0xFF * 0.4 + 0.4; // 亮度在0.3-0.7之间,避免太暗或太亮
// 返回RGB对象
let rgb = hslToRgb(h, s, l);
let obj = {
r: Math.round(rgb.r * 255),
g: Math.round(rgb.g * 255),
b: Math.round(rgb.b * 255)
}
let str = rgbToHex(obj.r, obj.g, obj.b)
return str;
}
+3
View File
@@ -0,0 +1,3 @@
export default function isNMReconstructable(imageSubType) {
return imageSubType === 'RECON TOMO' || imageSubType === 'RECON GATED TOMO';
}
+67 -5
View File
@@ -1,4 +1,11 @@
import * as cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader'
import {
getImageTypeSubItemFromDataset,
extractOrientationFromDataset,
extractPositionFromDataset,
extractSpacingFromDataset,
extractSliceThicknessFromDataset,
} from './extractPositioningFromDataset';
function parseImageId(imageId) {
// build a url by parsing out the url scheme and frame index from the imageId
const firstColonIndex = imageId.indexOf(':');
@@ -141,6 +148,19 @@ function populatePaletteColorLut(dataSet, imagePixelModule) {
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);
@@ -149,10 +169,47 @@ function metaDataProvider(type, imageId) {
return;
}
if (type === 'imagePlaneModule') {
const imageOrientationPatient = getNumberValues(dataSet, 'x00200037', 6);
const imagePositionPatient = getNumberValues(dataSet, 'x00200032', 3);
const pixelSpacing = getNumberValues(dataSet, 'x00280030', 2);
// 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;
@@ -194,7 +251,7 @@ function metaDataProvider(type, imageId) {
rowCosines,
columnCosines,
imagePositionPatient,
sliceThickness: dataSet.floatString('x00180050'),
sliceThickness,
sliceLocation: dataSet.floatString('x00201041'),
pixelSpacing,
rowPixelSpacing,
@@ -224,6 +281,11 @@ function metaDataProvider(type, imageId) {
}
return imagePixelModule;
}
// if (type === "multiframeModule") {
// return {
// NumberOfFrames: dataSet.uint16("x00280008") || dataSet.string("x00280008")
// };
// }
// if (type === 'imagePixelModule') {
// return {
// samplesPerPixel: dataSet.uint16('x00280002'),
@@ -249,4 +311,4 @@ function metaDataProvider(type, imageId) {
// }
// }
}
export default metaDataProvider;
export default metaDataProvider;
+38 -8
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,15 @@ 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) {
let params = Object.assign({path: objectName}, fileInfo)
addOrUpdateFileUploadRecord(params)
} else if (trialId) {
let params = { trialId }
addOrUpdateFileUploadRecord(params)
}
resolve({
name: objectName,
url: res.url
@@ -65,7 +75,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 +105,16 @@ 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) {
let params = Object.assign({path: data.path}, fileInfo)
addOrUpdateFileUploadRecord(params)
} else if (trialId) {
let params = { trialId }
addOrUpdateFileUploadRecord(params)
}
resolve({
name: data.path,
url: Vue.prototype.OSSclientConfig.viewEndpoint + decodeUtf8(res.name)
@@ -169,17 +189,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 +209,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 +231,16 @@ 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) {
let params = Object.assign({path: decodeUtf8(curPath)}, fileInfo)
addOrUpdateFileUploadRecord(params)
} else if (trialId) {
let params = { trialId }
addOrUpdateFileUploadRecord(params)
}
resolve({
name: decodeUtf8(curPath),
url: Vue.prototype.OSSclientConfig.viewEndpoint + decodeUtf8(curPath)
+36
View File
@@ -5,8 +5,10 @@ import router from '@/router'
import WHITELIST from "./whiteList"
import moment from 'moment-timezone';
import { encryptConfig } from "@/utils/encrypt"
import SystemInfo from "@/utils/systemInfo";
const ROUTER = require('@/router');
axios.defaults.withCredentials = false
const systemInfo = new SystemInfo();
const service = axios.create({
baseURL: '/api',
// baseURL: process.env.NODE_ENV === 'prod' ? "https://api.irc.extimaging.com" : '/api',
@@ -26,6 +28,40 @@ service.interceptors.request.use(
var language = zzSessionStorage.getItem('lang')
config.headers['Accept-Language'] = language === 'en' ? 'en-US,en;q=0.5' : 'zh-CN,zh;q=0.9'
config.headers['TimeZoneId'] = moment.tz.guess()
const allInfo = systemInfo.getAllInfo();
let obj = {
browser: {
name: allInfo.browser.name,
version: allInfo.browser.version,
},
platform: allInfo.platform,
os: {
name: allInfo.os.name,
version: allInfo.os.version
},
hardware: {
logicalCores: allInfo.hardware.logicalCores,
deviceMemory: allInfo.hardware.deviceMemory
},
webgl: {
version: allInfo.webgl.version,
renderer: allInfo.webgl.renderer,
vendor: allInfo.webgl.vendor,
shadingLanguage: allInfo.webgl.shadingLanguage,
gpuType: {
type: allInfo.webgl.gpuType.type,
discrete: allInfo.webgl.gpuType.discrete,
},
performance: {
tier: allInfo.webgl.performance.tier,
},
extensions: {
length: allInfo.webgl.extensions.length,
}
}
}
let guid = await systemInfo.generateGuid(obj)
config.headers['BrowserFingerprint'] = guid
if (config.ENCRYPT) {
try {
config = await encryptConfig(config)
+88
View File
@@ -0,0 +1,88 @@
async function queryWindowManagement() {
return await navigator.permissions.query({ name: 'window-management' });;
}
async function getPermission() {
const permission = await queryWindowManagement();
console.log(permission.state, 'permission.state')
if (permission.state === "granted") { // 已经授权
return true
} else if (permission.state === "prompt") { // 询问是否授权
// 请求授权
if (navigator.permissions.request) {
navigator.permissions.request({ name: 'window-management' })
}
return false
} else if (permission.state === "denied") {
// 权限被拒绝
return false
}
}
function getScreen() {
return new Promise((resolve, reject) => {
let multiScreen = []
if (window.getScreenDetails) {
let getScreensProsime1 = window.getScreenDetails();
getScreensProsime1.then((c) => {
if (c instanceof Array) {
multiScreen = c
} else if (c instanceof Object) {
multiScreen = c.screens
} else {
multiScreen = []
}
resolve(multiScreen)
}).catch((err) => {
reject(err)
});
} else if (window.getScreens) {
let getScreensProsime = window.getScreens();
getScreensProsime.then((c) => {
if (c instanceof Array) {
multiScreen = c
} else if (c instanceof Object) {
multiScreen = c.screens
} else {
multiScreen = []
}
resolve(multiScreen)
}).catch((err) => {
reject(err)
})
}
})
}
export async function openWindow(url, name, Skip = false) {
// 判断浏览器是否兼容
// 高版本的谷歌,edge不支持跨屏,需要降低浏览器版本86.0版
if (!window.getScreens && !window.getScreenDetails) {
console.log('你的浏览器版本不支持多屏展示功能!');
return window.open(url, name);
}
let permission = await getPermission()
if (!permission) {
// alert('使用多屏功能请先进行授权')
if (Skip) {
return window.open(url, name);
}
return false
}
let multiScreen = await getScreen()
console.log(multiScreen, 'multiScreen')
// 判断是否2个屏幕
if (multiScreen.length < 2) {
console.log('请接入多个显示屏!');
return window.open(url, name);
}
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},`
}
console.log(fulls, 'fulls')
return window.open(url, name, fulls);
}
+1 -1
View File
@@ -1,5 +1,5 @@
import streamSaver from "streamsaver";
import "streamsaver/examples/zip-stream.js";
import "./zip-stream.js";
import store from '@/store'
import dcmjs from './dcmUpload/dcmjs'
streamSaver.mitm = `${window.location.origin}/mitm.html?version=2.0.0`
+686
View File
@@ -0,0 +1,686 @@
// 浏览器信息检测
class SystemInfo {
constructor() {
this.info = {
browser: this.getBrowserInfo(),
os: this.getOSInfo(),
hardware: this.getHardwareInfo(),
webgl: this.getWebGLInfo()
};
}
// 获取浏览器信息
getBrowserInfo() {
const ua = navigator.userAgent;
let browser = {
name: 'Unknown',
version: 'Unknown',
fullVersion: navigator.appVersion
};
// 检测浏览器类型
if (ua.includes('Firefox')) {
browser.name = 'Firefox';
browser.version = this.extractVersion(ua, 'Firefox');
} else if (ua.includes('Chrome') && !ua.includes('Edg') && !ua.includes('OPR')) {
browser.name = 'Chrome';
browser.version = this.extractVersion(ua, 'Chrome');
} else if (ua.includes('Safari') && !ua.includes('Chrome')) {
browser.name = 'Safari';
browser.version = this.extractVersion(ua, 'Version');
} else if (ua.includes('Edg')) {
browser.name = 'Edge';
browser.version = this.extractVersion(ua, 'Edg');
} else if (ua.includes('OPR') || ua.includes('Opera')) {
browser.name = 'Opera';
browser.version = this.extractVersion(ua, 'OPR') || this.extractVersion(ua, 'Opera');
} else if (ua.includes('Trident') || ua.includes('MSIE')) {
browser.name = 'Internet Explorer';
browser.version = this.extractVersion(ua, 'MSIE') || this.extractVersion(ua, 'rv:');
}
return browser;
}
// 提取版本号
extractVersion(userAgent, browserName) {
const match = userAgent.match(new RegExp(`${browserName}/([0-9]+(\\.[0-9]+)?)`));
return match ? match[1] : 'Unknown';
}
// 获取操作系统信息
getOSInfo() {
const ua = navigator.userAgent;
let os = {
name: 'Unknown',
version: 'Unknown',
architecture: this.getArchitecture()
};
if (ua.includes('Windows')) {
os.name = 'Windows';
if (ua.includes('Windows NT 10.0')) os.version = '10';
else if (ua.includes('Windows NT 6.3')) os.version = '8.1';
else if (ua.includes('Windows NT 6.2')) os.version = '8';
else if (ua.includes('Windows NT 6.1')) os.version = '7';
else if (ua.includes('Windows NT 6.0')) os.version = 'Vista';
else if (ua.includes('Windows NT 5.1')) os.version = 'XP';
} else if (ua.includes('Mac OS X')) {
os.name = 'macOS';
const match = ua.match(/Mac OS X (\d+[._]\d+)/);
if (match) os.version = match[1].replace('_', '.');
} else if (ua.includes('Linux')) {
os.name = 'Linux';
} else if (ua.includes('Android')) {
os.name = 'Android';
const match = ua.match(/Android ([0-9.]+)/);
if (match) os.version = match[1];
} else if (ua.includes('like Mac')) {
os.name = 'iOS';
const match = ua.match(/OS (\d+[_]\d+)/);
if (match) os.version = match[1].replace('_', '.');
}
return os;
}
// 获取系统架构
getArchitecture() {
if (navigator.userAgent.includes('x64') || navigator.userAgent.includes('x86_64')) {
return '64-bit';
} else if (navigator.userAgent.includes('x86') || navigator.userAgent.includes('i686')) {
return '32-bit';
} else if (navigator.userAgent.includes('ARM')) {
return 'ARM';
} else if (navigator.userAgent.includes('Win64')) {
return '64-bit';
}
return 'Unknown';
}
// 获取硬件信息
getHardwareInfo() {
return {
logicalCores: navigator.hardwareConcurrency || 'Unknown',
deviceMemory: navigator.deviceMemory ? `${navigator.deviceMemory} GB` : 'Unknown',
maxTouchPoints: navigator.maxTouchPoints || 0
};
}
// 获取WebGL和显卡信息
getWebGLInfo() {
const webglInfo = {
webgl1: this.getWebGLContextInfo('webgl'),
webgl2: this.getWebGLContextInfo('webgl2'),
supported: false,
renderer: 'Unknown',
vendor: 'Unknown',
version: 'Unknown',
shadingLanguage: 'Unknown',
extensions: []
};
const canvas = document.createElement('canvas');
let gl = null;
// 尝试获取WebGL2上下文
try {
gl = canvas.getContext('webgl2') || canvas.getContext('experimental-webgl2');
} catch (e) {
console.log('WebGL2 not supported:', e.message);
}
// 如果WebGL2不可用,尝试WebGL1
if (!gl) {
try {
gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
} catch (e) {
console.log('WebGL not supported:', e.message);
}
}
if (gl) {
webglInfo.supported = true;
// 获取显卡信息
const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
if (debugInfo) {
webglInfo.renderer = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL) || 'Unknown';
webglInfo.vendor = gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL) || 'Unknown';
}
// 获取WebGL版本信息
webglInfo.version = gl.getParameter(gl.VERSION) || 'Unknown';
webglInfo.shadingLanguage = gl.getParameter(gl.SHADING_LANGUAGE_VERSION) || 'Unknown';
// 获取支持的扩展
try {
webglInfo.extensions = gl.getSupportedExtensions() || [];
} catch (e) {
console.log('无法获取WebGL扩展:', e.message);
}
// 检查GPU性能级别
webglInfo.performance = this.getGPUPerformanceInfo(gl);
// 检查内存信息(如果支持)
webglInfo.memoryInfo = this.getGPUMemoryInfo(gl);
// 检查是否使用集成显卡
if (webglInfo.renderer) {
webglInfo.gpuType = this.detectGPUType(webglInfo.renderer);
}
}
return webglInfo;
}
// 获取WebGL上下文的具体信息
getWebGLContextInfo(contextType) {
const canvas = document.createElement('canvas');
let gl = null;
try {
gl = canvas.getContext(contextType) ||
canvas.getContext(`experimental-${contextType}`);
} catch (e) {
return { supported: false, error: e.message };
}
if (!gl) {
return { supported: false };
}
return {
supported: true,
context: gl
};
}
// 获取GPU性能信息
getGPUPerformanceInfo(gl) {
const info = { tier: 'unknown', features: [] };
// 检查是否支持高性能特性
try {
// 检查帧缓冲
if (gl.checkFramebufferStatus) {
info.features.push('framebuffer');
}
// 检查浮点纹理
const floatExt = gl.getExtension('OES_texture_float') ||
gl.getExtension('EXT_color_buffer_float');
if (floatExt) info.features.push('float_textures');
// 检查多重采样
const msaaExt = gl.getExtension('WEBGL_multisampled_render_to_texture');
if (msaaExt) info.features.push('msaa');
// 检查实例化渲染
const instancingExt = gl.getExtension('ANGLE_instanced_arrays') ||
gl.getExtension('WEBGL_draw_buffers');
if (instancingExt) info.features.push('instancing');
// 尝试检测性能级别(基于支持的扩展)
if (info.features.includes('float_textures') &&
info.features.includes('msaa') &&
info.features.includes('instancing')) {
info.tier = 'high';
} else if (info.features.length >= 2) {
info.tier = 'medium';
} else {
info.tier = 'low';
}
} catch (e) {
console.log('获取GPU性能信息失败:', e);
}
return info;
}
// 获取GPU内存信息
getGPUMemoryInfo(gl) {
const memoryInfo = { estimatedMemory: 'Unknown' };
// 尝试通过扩展获取内存信息
try {
// 检查是否支持内存信息扩展
const memoryExt = gl.getExtension('WEBGL_debug_renderer_info');
if (memoryExt) {
// 一些浏览器会通过UNMASKED_RENDERER_WEBGL暴露内存信息
const renderer = gl.getParameter(memoryExt.UNMASKED_RENDERER_WEBGL);
if (renderer) {
// 尝试从渲染器字符串中提取内存信息
const match = renderer.match(/(\d+)\s*MB|(\d+)\s*GB|VRAM\s*:\s*(\d+)/i);
if (match) {
memoryInfo.estimatedMemory = match[0];
}
}
}
// 通过创建大纹理测试内存
memoryInfo.maxTextureSize = gl.getParameter(gl.MAX_TEXTURE_SIZE);
memoryInfo.maxRenderbufferSize = gl.getParameter(gl.MAX_RENDERBUFFER_SIZE);
} catch (e) {
// 静默失败
}
return memoryInfo;
}
// 检测GPU类型
detectGPUType(renderer) {
const r = renderer.toLowerCase();
if (r.includes('nvidia') || r.includes('geforce') || r.includes('gtx') || r.includes('rtx')) {
return { type: 'NVIDIA', discrete: true };
} else if (r.includes('amd') || r.includes('radeon') || r.includes('rx')) {
return { type: 'AMD', discrete: true };
} else if (r.includes('intel') || r.includes('hd graphics') || r.includes('iris') || r.includes('uhd')) {
return { type: 'Intel', discrete: false };
} else if (r.includes('apple') || r.includes('apple gpu') || r.includes('apple m')) {
return { type: 'Apple Silicon', discrete: false };
} else if (r.includes('mali') || r.includes('adreno') || r.includes('powervr')) {
return { type: 'Mobile GPU', discrete: false };
} else if (r.includes('microsoft') || r.includes('basic')) {
return { type: 'Software Renderer', discrete: false };
}
return { type: 'Unknown', discrete: false };
}
// 获取屏幕信息
getScreenInfo() {
return {
width: window.screen.width,
height: window.screen.height,
colorDepth: window.screen.colorDepth,
pixelDepth: window.screen.pixelDepth,
devicePixelRatio: window.devicePixelRatio || 1,
orientation: window.screen.orientation ? window.screen.orientation.type : 'unknown'
};
}
// 获取所有系统信息
getAllInfo() {
return {
...this.info,
screen: this.getScreenInfo(),
userAgent: navigator.userAgent,
platform: navigator.platform,
language: navigator.language,
languages: navigator.languages,
cookiesEnabled: navigator.cookieEnabled,
online: navigator.onLine,
javaEnabled: navigator.javaEnabled ? navigator.javaEnabled() : false,
pdfViewerEnabled: navigator.pdfViewerEnabled || false,
doNotTrack: navigator.doNotTrack || 'unknown',
timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
dateTimeFormat: new Date().toString()
};
}
// 格式化输出
formatAsText() {
const info = this.getAllInfo();
let output = '========== 系统信息 ==========\n\n';
// 浏览器信息
output += '=== 浏览器信息 ===\n';
output += `浏览器: ${info.browser.name} ${info.browser.version}\n`;
output += `完整版本: ${info.browser.fullVersion}\n`;
output += `用户代理: ${info.userAgent}\n\n`;
// 操作系统信息
output += '=== 操作系统信息 ===\n';
output += `系统: ${info.os.name} ${info.os.version}\n`;
output += `架构: ${info.os.architecture}\n`;
output += `平台: ${info.platform}\n\n`;
// 硬件信息
output += '=== 硬件信息 ===\n';
output += `逻辑核心数: ${info.hardware.logicalCores}\n`;
output += `设备内存: ${info.hardware.deviceMemory}\n`;
output += `最大触摸点数: ${info.hardware.maxTouchPoints}\n\n`;
// 屏幕信息
output += '=== 屏幕信息 ===\n';
output += `分辨率: ${info.screen.width} × ${info.screen.height}\n`;
output += `设备像素比: ${info.screen.devicePixelRatio}\n`;
output += `颜色深度: ${info.screen.colorDepth}\n`;
output += `像素深度: ${info.screen.pixelDepth}\n`;
output += `方向: ${info.screen.orientation}\n\n`;
// WebGL信息
output += '=== WebGL 信息 ===\n';
output += `WebGL 支持: ${info.webgl.supported ? '是' : '否'}\n`;
if (info.webgl.supported) {
output += `WebGL 版本: ${info.webgl.version}\n`;
output += `显卡型号: ${info.webgl.renderer}\n`;
output += `显卡厂商: ${info.webgl.vendor}\n`;
output += `着色语言: ${info.webgl.shadingLanguage}\n`;
if (info.webgl.gpuType) {
output += `GPU 类型: ${info.webgl.gpuType.type}\n`;
output += `独立显卡: ${info.webgl.gpuType.discrete ? '是' : '否'}\n`;
}
output += `性能等级: ${info.webgl.performance.tier}\n`;
output += `支持特性: ${info.webgl.performance.features.join(', ')}\n`;
if (info.webgl.memoryInfo.estimatedMemory !== 'Unknown') {
output += `显存估计: ${info.webgl.memoryInfo.estimatedMemory}\n`;
}
if (info.webgl.memoryInfo.maxTextureSize) {
output += `最大纹理尺寸: ${info.webgl.memoryInfo.maxTextureSize}\n`;
}
output += `WebGL 扩展数: ${info.webgl.extensions.length}\n`;
}
// 其他信息
output += '\n=== 其他信息 ===\n';
output += `语言: ${info.language}\n`;
output += `支持语言: ${info.languages.join(', ')}\n`;
output += `时区: ${info.timezone}\n`;
output += `Cookie 支持: ${info.cookiesEnabled ? '是' : '否'}\n`;
output += `在线状态: ${info.online ? '在线' : '离线'}\n`;
output += `Java 支持: ${info.javaEnabled ? '是' : '否'}\n`;
output += `PDF 查看器: ${info.pdfViewerEnabled ? '支持' : '不支持'}\n`;
output += `Do Not Track: ${info.doNotTrack}\n`;
output += `当前时间: ${info.dateTimeFormat}\n`;
return output;
}
// 创建HTML显示
createInfoHTML() {
const info = this.getAllInfo();
let html = `
<div class="system-info">
<h2>系统信息</h2>
<div class="info-section">
<h3>🌐 浏览器信息</h3>
<table>
<tr><td>浏览器</td><td>${info.browser.name} ${info.browser.version}</td></tr>
<tr><td>用户代理</td><td><code>${info.userAgent}</code></td></tr>
<tr><td>平台</td><td>${info.platform}</td></tr>
</table>
</div>
<div class="info-section">
<h3>💻 操作系统</h3>
<table>
<tr><td>系统</td><td>${info.os.name} ${info.os.version}</td></tr>
<tr><td>架构</td><td>${info.os.architecture}</td></tr>
<tr><td>语言</td><td>${info.language}</td></tr>
<tr><td>时区</td><td>${info.timezone}</td></tr>
</table>
</div>
<div class="info-section">
<h3>🖥 硬件信息</h3>
<table>
<tr><td>CPU 核心数</td><td>${info.hardware.logicalCores}</td></tr>
<tr><td>设备内存</td><td>${info.hardware.deviceMemory}</td></tr>
<tr><td>屏幕分辨率</td><td>${info.screen.width} × ${info.screen.height}</td></tr>
<tr><td>设备像素比</td><td>${info.screen.devicePixelRatio}</td></tr>
</table>
</div>
`;
if (info.webgl.supported) {
html += `
<div class="info-section">
<h3>🎮 WebGL & 显卡信息</h3>
<table>
<tr><td>WebGL 支持</td><td> </td></tr>
<tr><td>WebGL 版本</td><td>${info.webgl.version}</td></tr>
<tr><td>显卡型号</td><td><strong>${info.webgl.renderer}</strong></td></tr>
<tr><td>显卡厂商</td><td>${info.webgl.vendor}</td></tr>
<tr><td>GPU 类型</td><td>${info.webgl.gpuType.type} ${info.webgl.gpuType.discrete ? '' : ''}</td></tr>
<tr><td>着色语言</td><td>${info.webgl.shadingLanguage}</td></tr>
<tr><td>性能等级</td><td><span class="perf-${info.webgl.performance.tier}">${info.webgl.performance.tier}</span></td></tr>
<tr><td>WebGL 扩展数</td><td>${info.webgl.extensions.length} </td></tr>
`;
if (info.webgl.memoryInfo.estimatedMemory !== 'Unknown') {
html += `<tr><td>显存估计</td><td>${info.webgl.memoryInfo.estimatedMemory}</td></tr>`;
}
html += `</table></div>`;
} else {
html += `
<div class="info-section">
<h3>🎮 WebGL & 显卡信息</h3>
<p style="color: red;"> WebGL 不支持</p>
</div>
`;
}
html += `</div>`;
return html;
}
deepSortObject(obj) {
if (obj === null || typeof obj !== 'object') {
return obj;
}
if (Array.isArray(obj)) {
return obj.map(deepSortObject);
}
const sorted = {};
Object.keys(obj).sort().forEach(key => {
sorted[key] = this.deepSortObject(obj[key]);
});
return sorted;
}
getCanonicalString(obj) {
if (typeof obj !== 'object' || obj === null) {
return JSON.stringify(obj);
}
const sorted = this.deepSortObject(obj);
return JSON.stringify(sorted);
}
// 简单哈希函数
simpleHash(str) {
let hash = 0;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash;
}
return hash;
}
// 简单SHA-256模拟
simpleSHA256(str) {
let hash = 0;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash;
}
const hashArray = [];
for (let i = 0; i < 8; i++) {
hashArray[i] = (hash + i * 2654435761) >>> 0;
}
return hashArray;
}
// 格式化哈希为GUID
formatHashAsGuid(hashArray) {
const hexParts = hashArray.map(h => h.toString(16).padStart(8, '0'));
return `${hexParts[0]}${hexParts[1].substring(0, 4)}-${hexParts[1].substring(4)}-${hexParts[2].substring(0, 4)}-${hexParts[3].substring(0, 4)}-${hexParts[3].substring(4)}${hexParts[4]}${hexParts[5]}`;
}
// 1. 确定性哈希算法
objectToGuidDeterministic(obj) {
const canonicalString = this.getCanonicalString(obj);
const hash = this.simpleSHA256(canonicalString);
return this.formatHashAsGuid(hash).toUpperCase();
}
// 2. 伪随机算法
getSeedFromObject(obj) {
const str = JSON.stringify(obj);
return this.simpleHash(str);
}
createSeededRandom(seed) {
let state = seed;
return function () {
state = (state * 1664525 + 1013904223) >>> 0;
return (state & 0x3FFFFFFF) / 0x3FFFFFFF;
};
}
generateVersion4Guid(randomFn) {
let guid = '';
for (let i = 0; i < 32; i++) {
if (i === 8 || i === 12 || i === 16 || i === 20) {
guid += '-';
}
if (i === 12) {
guid += '4';
} else if (i === 16) {
const variant = Math.floor(randomFn() * 4);
guid += (8 + variant).toString(16).toUpperCase();
} else {
const randomHex = Math.floor(randomFn() * 16).toString(16);
guid += randomHex;
}
}
return guid.toUpperCase();
}
objectToGuidPseudorandom(obj) {
const seed = this.getSeedFromObject(obj);
const prng = this.createSeededRandom(seed);
return this.generateVersion4Guid(prng);
}
// 3. Crypto API算法(同步版本)
objectToGuidCryptoSync(obj) {
try {
const str = this.getCanonicalString(obj);
let hash = 0;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash;
}
const bytes = new Array(16);
for (let i = 0; i < 16; i++) {
bytes[i] = (hash + i * 2654435761) & 0xFF;
}
bytes[6] = (bytes[6] & 0x0F) | 0x40;
bytes[8] = (bytes[8] & 0x3F) | 0x80;
return bytes.map(b => b.toString(16).padStart(2, '0'))
.join('')
.replace(/(.{8})(.{4})(.{4})(.{4})(.{12})/, '$1-$2-$3-$4-$5')
.toUpperCase();
} catch (error) {
return this.objectToGuidDeterministic(obj);
}
}
// 4. 组合算法
combineComponents(deterministicHash, namespaceHash, timestamp, randomComponent) {
const hashDigits = deterministicHash.replace(/-/g, '');
const bytes = new Array(16);
for (let i = 0; i < 16; i++) {
const hashByte = parseInt(hashDigits.substr(i * 2, 2), 16) || 0;
const nsByte = (namespaceHash >> (i * 2)) & 0xFF;
const timeByte = (timestamp >> (i * 2)) & 0xFF;
const randomByte = parseInt(randomComponent.substr(i % 8, 1), 16) || 0;
bytes[i] = (hashByte ^ nsByte ^ timeByte ^ randomByte) & 0xFF;
}
bytes[6] = (bytes[6] & 0x0F) | 0x40;
bytes[8] = (bytes[8] & 0x3F) | 0x80;
return bytes.map(b => b.toString(16).padStart(2, '0'))
.join('')
.replace(/(.{8})(.{4})(.{4})(.{4})(.{12})/, '$1-$2-$3-$4-$5')
.toUpperCase();
}
objectToGuidCombined(obj, namespace = '') {
const deterministicHash = this.objectToGuidDeterministic(obj);
const namespaceHash = namespace ? this.simpleHash(namespace) : 0;
const timestamp = Date.now();
const randomComponent = Math.random().toString(16).substr(2, 8);
return this.combineComponents(deterministicHash, namespaceHash, timestamp, randomComponent);
}
// 主生成函数
async generateGuid(inputText, inputType, algorithmType = 'deterministic') {
let obj;
try {
if (inputType === 'json') {
obj = JSON.parse(inputText);
} else {
obj = inputText
}
} catch (error) {
return error;
}
let guid;
let algorithmName;
try {
switch (algorithmType) {
case 'deterministic':
guid = this.objectToGuidDeterministic(obj);
algorithmName = '确定性哈希算法';
break;
case 'pseudorandom':
guid = this.objectToGuidPseudorandom(obj);
algorithmName = '伪随机算法';
break;
case 'crypto':
guid = this.objectToGuidCryptoSync(obj);
algorithmName = 'Crypto API算法';
break;
case 'combined':
guid = this.objectToGuidCombined(obj, 'example-namespace');
algorithmName = '组合算法';
break;
default:
guid = '未知算法';
algorithmName = '未知';
}
return guid;
} catch (error) {
return error
}
}
}
if (typeof module !== 'undefined' && module.exports) {
module.exports = SystemInfo;
} else if (typeof window !== 'undefined') {
window.SystemInfo = SystemInfo;
}
+382
View File
@@ -0,0 +1,382 @@
class Crc32 {
constructor() {
this.crc = -1
}
append(data) {
var crc = this.crc | 0; var table = this.table
for (var offset = 0, len = data.length | 0; offset < len; offset++) {
crc = (crc >>> 8) ^ table[(crc ^ data[offset]) & 0xFF]
}
this.crc = crc
}
get() {
return ~this.crc
}
}
Crc32.prototype.table = (() => {
var i; var j; var t; var table = []
for (i = 0; i < 256; i++) {
t = i
for (j = 0; j < 8; j++) {
t = (t & 1)
? (t >>> 1) ^ 0xEDB88320
: t >>> 1
}
table[i] = t
}
return table
})()
const getDataHelper = byteLength => {
var uint8 = new Uint8Array(byteLength)
return {
array: uint8,
view: new DataView(uint8.buffer)
}
}
const ZIP_SIGNATURE_LOCAL = 0x04034b50
const ZIP_SIGNATURE_CENTRAL = 0x02014b50
const ZIP_SIGNATURE_EOCD = 0x06054b50
const ZIP_SIGNATURE_ZIP64_EOCD = 0x06064b50
const ZIP_SIGNATURE_ZIP64_LOCATOR = 0x07064b50
const ZIP64_MAGIC = 0xFFFFFFFF
function u16(view, offset, value) { view.setUint16(offset, value, true) }
function u32(view, offset, value) { view.setUint32(offset, value >>> 0, true) }
function concatUint8(chunks, total) {
const out = new Uint8Array(total)
let off = 0
for (const c of chunks) {
out.set(c, off)
off += c.length
}
return out
}
function createWriter(underlyingSource) {
const files = Object.create(null)
const filenames = []
const encoder = new TextEncoder() // 保持UTF-8编码
let offset = 0 // bytes written to output so far
let activeZipIndex = 0
let ctrl
let activeZipObject
let closed = false
function processNextChunk() {
if (!activeZipObject) return
// directory entry: just local header + immediate footer
if (activeZipObject.directory) {
if (!activeZipObject.headerWritten) {
activeZipObject.writeLocalHeader()
activeZipObject.headerWritten = true
}
if (!activeZipObject.footerWritten) {
activeZipObject.writeDataDescriptor() // will be zeros
activeZipObject.footerWritten = true
next()
}
return
}
if (!activeZipObject.reader) {
if (!activeZipObject.fileLike || !activeZipObject.fileLike.stream) {
next()
return
}
activeZipObject.crc = new Crc32()
activeZipObject.reader = activeZipObject.fileLike.stream().getReader()
activeZipObject.writeLocalHeader()
activeZipObject.headerWritten = true
return
}
return activeZipObject.reader.read().then(({ done, value }) => {
if (done) {
activeZipObject.writeDataDescriptor()
activeZipObject.footerWritten = true
next()
return
}
const chunk = value instanceof Uint8Array ? value : new Uint8Array(value)
activeZipObject.crc.append(chunk)
activeZipObject.uncompressedLength += chunk.length
activeZipObject.compressedLength += chunk.length
ctrl.enqueue(chunk)
offset += chunk.length
})
}
function next() {
activeZipIndex++
activeZipObject = files[filenames[activeZipIndex]]
if (activeZipObject) {
processNextChunk()
} else if (closed) {
closeZip()
}
}
function dosDateTime(date) {
const dt = new Date(date)
const time = ((dt.getHours() << 11) | (dt.getMinutes() << 5) | (dt.getSeconds() / 2)) & 0xFFFF
const d = (((dt.getFullYear() - 1980) << 9) | ((dt.getMonth() + 1) << 5) | dt.getDate()) & 0xFFFF
return { time, date: d }
}
const zipWriter = {
enqueue(fileLike) {
if (closed) throw new TypeError('Cannot enqueue after close()')
let name = String(fileLike.name || '').trim()
if (!name) throw new Error('Missing file name')
if (fileLike.directory && !name.endsWith('/')) name += '/'
if (files[name]) throw new Error('File already exists.')
const nameBuf = encoder.encode(name) // 保持UTF-8编码
const commentBuf = encoder.encode(fileLike.comment || '')
const { time, date } = dosDateTime(typeof fileLike.lastModified === 'undefined' ? Date.now() : fileLike.lastModified)
const zipObject = (files[name] = {
fileLike,
directory: !!fileLike.directory,
nameBuf,
comment: commentBuf,
time,
date,
// tracked
offset: 0,
crc: null,
compressedLength: 0,
uncompressedLength: 0,
headerWritten: false,
footerWritten: false,
reader: null,
// whether sizes/offset require ZIP64
needsZip64() {
return (
this.uncompressedLength > ZIP64_MAGIC ||
this.compressedLength > ZIP64_MAGIC ||
this.offset > ZIP64_MAGIC
)
},
writeLocalHeader() {
this.offset = offset
// 关键修改:设置通用位标记,第11位(0x0800)表示文件名使用UTF-8编码
// 保留原0x0008(数据描述符存在),新增0x0800(UTF-8编码)
const generalPurposeBitFlag = 0x0808
const compressionMethod = 0 // store
const versionNeeded = this.needsZip64() ? 45 : 20 // 适配ZIP64
// We always use data descriptor; write zeros for crc/sizes in local header
const local = getDataHelper(30 + this.nameBuf.length)
u32(local.view, 0, ZIP_SIGNATURE_LOCAL)
u16(local.view, 4, versionNeeded)
u16(local.view, 6, generalPurposeBitFlag) // 应用UTF-8标记
u16(local.view, 8, compressionMethod)
u16(local.view, 10, this.time)
u16(local.view, 12, this.date)
u32(local.view, 14, 0)
u32(local.view, 18, 0)
u32(local.view, 22, 0)
u16(local.view, 26, this.nameBuf.length)
u16(local.view, 28, 0) // extra length
local.array.set(this.nameBuf, 30)
ctrl.enqueue(local.array)
offset += local.array.length
},
writeDataDescriptor() {
// data descriptor: optional signature + crc32 + sizes (32-bit or 64-bit)
const crc = this.crc ? this.crc.get() >>> 0 : 0
const useZip64 = this.needsZip64()
if (!useZip64) {
const dd = getDataHelper(16)
u32(dd.view, 0, 0x08074b50)
u32(dd.view, 4, crc)
u32(dd.view, 8, this.compressedLength)
u32(dd.view, 12, this.uncompressedLength)
ctrl.enqueue(dd.array)
offset += dd.array.length
} else {
// 24 bytes: sig + crc32 + compSize(8) + uncompSize(8)
const dd = getDataHelper(24)
u32(dd.view, 0, 0x08074b50)
u32(dd.view, 4, crc)
// BigInt writes
dd.view.setBigUint64(8, BigInt(this.compressedLength), true)
dd.view.setBigUint64(16, BigInt(this.uncompressedLength), true)
ctrl.enqueue(dd.array)
offset += dd.array.length
}
}
})
filenames.push(name)
if (!activeZipObject) {
activeZipObject = zipObject
processNextChunk()
}
},
close() {
if (closed) throw new TypeError('Cannot close twice')
closed = true
if (!activeZipObject) closeZip()
}
}
function closeZip() {
// Build central directory in memory then enqueue once.
const cdChunks = []
let cdSize = 0
for (const name of filenames) {
const file = files[name]
const useZip64 = file.needsZip64()
const versionMadeBy = useZip64 ? 45 : 20
const versionNeeded = useZip64 ? 45 : 20
// 关键修改:中央目录也需要设置UTF-8标记
const generalPurposeBitFlag = 0x0808
const compressionMethod = 0
const crc = file.crc ? file.crc.get() >>> 0 : 0
const compressed32 = useZip64 ? ZIP64_MAGIC : file.compressedLength
const uncompressed32 = useZip64 ? ZIP64_MAGIC : file.uncompressedLength
const offset32 = useZip64 ? ZIP64_MAGIC : file.offset
// ZIP64 extra field if needed
let extra = new Uint8Array(0)
if (useZip64) {
// headerId(2)=0x0001, dataSize(2)=24, uncompressed(8), compressed(8), offset(8)
extra = new Uint8Array(4 + 24)
const ev = new DataView(extra.buffer)
u16(ev, 0, 0x0001)
u16(ev, 2, 24)
ev.setBigUint64(4, BigInt(file.uncompressedLength), true)
ev.setBigUint64(12, BigInt(file.compressedLength), true)
ev.setBigUint64(20, BigInt(file.offset), true)
}
const headerLen = 46 + file.nameBuf.length + extra.length + file.comment.length
const cd = getDataHelper(headerLen)
u32(cd.view, 0, ZIP_SIGNATURE_CENTRAL)
u16(cd.view, 4, versionMadeBy)
u16(cd.view, 6, versionNeeded)
u16(cd.view, 8, generalPurposeBitFlag) // 中央目录应用UTF-8标记
u16(cd.view, 10, compressionMethod)
u16(cd.view, 12, file.time)
u16(cd.view, 14, file.date)
u32(cd.view, 16, crc)
u32(cd.view, 20, compressed32)
u32(cd.view, 24, uncompressed32)
u16(cd.view, 28, file.nameBuf.length)
u16(cd.view, 30, extra.length)
u16(cd.view, 32, file.comment.length)
u16(cd.view, 34, 0) // disk number start
u16(cd.view, 36, 0) // internal attrs
// external file attrs: mark directory
u32(cd.view, 38, file.directory ? 0x10 : 0)
u32(cd.view, 42, offset32)
cd.array.set(file.nameBuf, 46)
if (extra.length) cd.array.set(extra, 46 + file.nameBuf.length)
if (file.comment.length) cd.array.set(file.comment, 46 + file.nameBuf.length + extra.length)
cdChunks.push(cd.array)
cdSize += cd.array.length
}
const centralDirectoryOffset = offset
const centralDirectory = concatUint8(cdChunks, cdSize)
ctrl.enqueue(centralDirectory)
offset += centralDirectory.length
const entries = filenames.length
const needsZip64ForArchive =
entries > 0xFFFF ||
centralDirectoryOffset > ZIP64_MAGIC ||
cdSize > ZIP64_MAGIC ||
offset > ZIP64_MAGIC
const tailChunks = []
let tailSize = 0
if (needsZip64ForArchive) {
// ZIP64 EOCD
const zip64eocd = getDataHelper(56)
u32(zip64eocd.view, 0, ZIP_SIGNATURE_ZIP64_EOCD)
zip64eocd.view.setBigUint64(4, BigInt(44), true) // remaining size
u16(zip64eocd.view, 12, 45)
u16(zip64eocd.view, 14, 45)
u32(zip64eocd.view, 16, 0)
u32(zip64eocd.view, 20, 0)
zip64eocd.view.setBigUint64(24, BigInt(entries), true)
zip64eocd.view.setBigUint64(32, BigInt(entries), true)
zip64eocd.view.setBigUint64(40, BigInt(cdSize), true)
zip64eocd.view.setBigUint64(48, BigInt(centralDirectoryOffset), true)
// ZIP64 locator
const locator = getDataHelper(20)
u32(locator.view, 0, ZIP_SIGNATURE_ZIP64_LOCATOR)
u32(locator.view, 4, 0)
locator.view.setBigUint64(8, BigInt(centralDirectoryOffset + cdSize), true) // offset of zip64eocd
u32(locator.view, 16, 1)
tailChunks.push(zip64eocd.array, locator.array)
tailSize += zip64eocd.array.length + locator.array.length
}
// EOCD
const eocd = getDataHelper(22)
u32(eocd.view, 0, ZIP_SIGNATURE_EOCD)
u16(eocd.view, 4, 0)
u16(eocd.view, 6, 0)
u16(eocd.view, 8, Math.min(entries, 0xFFFF))
u16(eocd.view, 10, Math.min(entries, 0xFFFF))
u32(eocd.view, 12, needsZip64ForArchive ? ZIP64_MAGIC : cdSize)
u32(eocd.view, 16, needsZip64ForArchive ? ZIP64_MAGIC : centralDirectoryOffset)
u16(eocd.view, 20, 0) // comment length
tailChunks.push(eocd.array)
tailSize += eocd.array.length
ctrl.enqueue(concatUint8(tailChunks, tailSize))
ctrl.close()
}
return new ReadableStream({
start: c => {
ctrl = c
underlyingSource.start && Promise.resolve(underlyingSource.start(zipWriter))
},
pull() {
return processNextChunk() || (
underlyingSource.pull &&
Promise.resolve(underlyingSource.pull(zipWriter))
)
}
})
}
window.ZIP = createWriter
+5 -1
View File
@@ -23,7 +23,11 @@ export default {
})
.then(() => {
resetPassword(this.userId).then(res => {
let data = {
BaseUrl: `${location.protocol}//${location.host}/login`,
IdentityUserId: this.userId
}
resetPassword(data).then(res => {
if (res.IsSuccess) {
this.$message({
message: 'Reset password successfully',
+1 -1
View File
@@ -112,7 +112,7 @@ export default {
this.currentTime = moment().format('YYYY-MM-DD HH:mm:ss')
},
handleTitleClick() {
if (!screenfull.enabled) {
if (!screenfull.isEnabled) {
this.$message({
message: 'Your browser can not work',
type: 'warning'
@@ -2,8 +2,8 @@
<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 'modules/echarts/map/js/world.js'
import { fontSize } from 'utils/fontsize'
export default {
props: {
@@ -14,7 +14,7 @@ export default {
watch: {
area() {
const that = this
this.$nextTick(function() {
this.$nextTick(function () {
that.myChart.resize()
})
}
@@ -56,7 +56,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]
@@ -77,67 +77,67 @@ export default {
return res
}
var series = []
;[['Shanghai', BJData]].forEach(function(item, i) {
series.push(
{
type: 'lines',
zlevel: 1,
effect: {
show: true,
color: '#fff',
period: 6,
trailLength: 0.7,
symbolSize: fontSize(0.05)
},
lineStyle: {
normal: {
width: fontSize(0.03),
color: '#9ae5fc',
opacity: 0.02,
curveness: -0.2
}
},
data: convertData(item[1])
},
{
type: 'effectScatter',
coordinateSystem: 'geo',
zlevel: 3,
rippleEffect: {
//
period: 5, //
brushType: 'fill', // stroke, fill
scale: 5 //
},
label: {
normal: {
;[['Shanghai', BJData]].forEach(function (item, i) {
series.push(
{
type: 'lines',
zlevel: 1,
effect: {
show: true,
color: '#fce630',
position: 'right', //
offset: [10, 0], //
fontSize: fontSize(0.16),
formatter: '{b}' //
color: '#fff',
period: 6,
trailLength: 0.7,
symbolSize: fontSize(0.05)
},
emphasis: {
show: true
}
},
symbol: 'circle',
symbolSize: function(val) {
return fontSize(0.08) //
lineStyle: {
normal: {
width: fontSize(0.03),
color: '#9ae5fc',
opacity: 0.02,
curveness: -0.2
}
},
data: convertData(item[1])
},
data: item[1].map(function(dataItem) {
return {
name: dataItem[0].name,
value: geoCoordMap[dataItem[0].name]
}
})
}
)
})
{
type: 'effectScatter',
coordinateSystem: 'geo',
zlevel: 3,
rippleEffect: {
//
period: 5, //
brushType: 'fill', // stroke, fill
scale: 5 //
},
label: {
normal: {
show: true,
color: '#fce630',
position: 'right', //
offset: [10, 0], //
fontSize: fontSize(0.16),
formatter: '{b}' //
},
emphasis: {
show: true
}
},
symbol: 'circle',
symbolSize: function (val) {
return fontSize(0.08) //
},
data: item[1].map(function (dataItem) {
return {
name: dataItem[0].name,
value: geoCoordMap[dataItem[0].name]
}
})
}
)
})
var option = {
// backgroundColor: '#fff',
title: {
@@ -161,7 +161,7 @@ export default {
tooltip: {
trigger: 'item',
fontSize: fontSize(0.16),
formatter: function(params) {
formatter: function (params) {
if (typeof params.value === 'undefined') {
return ''
} else {
@@ -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
@@ -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
@@ -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 {
@@ -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
@@ -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
@@ -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 {
+98 -23
View File
@@ -29,8 +29,12 @@
:src="item.previewImageUrl"
fit="fill"
/> -->
<img class="image-preview" :src="item.previewImageUrl" crossorigin="anonymous" alt=""
style="width: 72px;height:72px;" fit="fill">
<div class="imageBox" style="width: 72px;height:72px;">
<img class="image-preview" :src="item.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="item.keySeries" style="color:red">
@@ -83,33 +87,36 @@
{{ item.description }}
</div>
<div v-if="!item.keySeries" style="padding: 1px;">
{{ item.imageloadedArr.length }}/{{ item.instanceCount }}
{{ item.imageloadedArr.length <= item.instanceCount ? item.imageloadedArr.length :
item.instanceCount }}/{{ item.instanceCount }} </div>
</div>
</div>
</div>
<div v-if="showDelete" style="display: flex;flex-direction: row;justify-content: space-between;"
@click.stop="">
<div v-if="showDelete" style="display: flex;flex-direction: row;justify-content: space-between;"
@click.stop="">
<div>
<span style="font-size: 12px;margin-right: 5px">{{ $t('trials:audit:table:isReading')
}}</span>
<el-switch v-model="item.isReading" size="mini" @change="changeReadingStatus($event, item)" />
<div>
<span style="font-size: 12px;margin-right: 5px">{{ $t('trials:audit:table:isReading')
}}</span>
<el-switch v-model="item.isReading" size="mini"
@change="changeReadingStatus($event, item)" />
</div>
<div>
<span style="font-size: 12px;margin-right: 5px">{{ $t('trials:audit:table:isDelete')
}}</span>
<el-switch v-model="item.isDeleted" size="mini"
@change="changeDeleteStatus($event, item)" />
</div>
</div>
<div>
<span style="font-size: 12px;margin-right: 5px">{{ $t('trials:audit:table:isDelete') }}</span>
<el-switch v-model="item.isDeleted" size="mini" @change="changeDeleteStatus($event, item)" />
<div
v-if="item.prefetchInstanceCount > 0 && item.prefetchInstanceCount < item.instanceCount * 100">
<el-progress
:percentage="parseInt((item.prefetchInstanceCount / item.instanceCount).toFixed(2))" />
</div>
</div>
<div
v-if="item.prefetchInstanceCount > 0 && item.prefetchInstanceCount < item.instanceCount * 100">
<el-progress
:percentage="parseInt((item.prefetchInstanceCount / item.instanceCount).toFixed(2))" />
</div>
</div>
</div>
</div>
</div>
</el-tab-pane>
</el-tabs>
</div>
@@ -133,7 +140,7 @@ import * as cornerstone from 'cornerstone-core'
import * as cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader'
import dicomViewer from '@/components/Dicom/DicomViewer'
import { getStudyInfo, getSeriesList } from '@/api/reading'
import { getInstanceList, getPatientSeriesList, setSeriesStatus, setInstanceStatus } from '@/api/trials'
import { getInstanceList, getPatientSeriesList, setSeriesStatus, setInstanceStatus, updateImageResizePath } from '@/api/trials'
import requestPoolManager from '@/utils/request-pool'
import store from '@/store'
@@ -244,6 +251,62 @@ export default {
workSpeedclose(true)
},
methods: {
async updateImageResizePath(data) {
try {
let res = await updateImageResizePath(data)
if (res.IsSuccess) {
this.$message.success(this.$t("message:tip:updateImageResizePath:success"))
}
} catch (err) {
console.log(err)
}
},
async refreshImage(item) {
let thumbnailPath = item.previewImageUrl.split(this.OSSclientConfig.basePath)[1]
let blob = await this.dicomToPng(item.imageIds[0])
let OSSclient = this.OSSclient
try {
let seriesRes = await OSSclient.put(thumbnailPath, blob)
if (seriesRes && seriesRes.url) {
let path = this.$getObjectName(seriesRes.url)
item.previewImageUrl = seriesRes.url
let data = {
seriesId: item.seriesId,
ImageResizePath: path
}
this.updateImageResizePath(data)
}
} catch (e) {
}
},
dicomToPng(imageId) {
return new Promise((resolve) => {
cornerstone.loadImage(imageId).then(async (image) => {
let width = image.columns, height = image.rows;
let canvas = document.createElement('canvas')
canvas.width = (width * 60) / height
canvas.height = 60
if (image) {
cornerstone.renderToCanvas(canvas, image)
// Canvas PNG
let blob = await this.canvasToBlob(canvas)
resolve(blob)
} else {
resolve()
}
})
}).catch((reason) => {
reason()
})
},
canvasToBlob(canvas) {
return new Promise((resolve) => {
canvas.toBlob((blob) => {
resolve(blob)
})
})
},
async loadStudy() {
let params = {}
if (this.isPacs) {
@@ -891,6 +954,7 @@ export default {
//
this.seriesList[seriesIndex].loadStatus = true
workSpeedclose()
localStorage.setItem("QCRiskControl", 1)
if (!this.isFromCRCUpload) {
this.loadAllImages()
}
@@ -901,7 +965,18 @@ export default {
}
</script>
<style>
<style lang="scss">
.viewerContainer .imageBox {
position: relative;
i {
position: absolute;
top: 0;
left: 0;
cursor: pointer;
}
}
.viewerContainer .el-tabs--border-card {
background: none;
border: none;
+184 -111
View File
@@ -45,8 +45,12 @@
style="position: relative;margin-bottom:5px;border-radius: 2px;border: 1px solid #404040;"
series-type="current" @click="showSeriesImage($event, index, i, series)">
<div class="viewernavigatorwrapper">
<img class="image-preview" :src="series.previewImageUrl" crossorigin="anonymous" alt=""
style="width: 85px;height:85px;" fit="fill">
<div class="imageBox" style="width: 72px;height:72px;">
<img class="image-preview" :src="series.previewImageUrl" crossorigin="anonymous" alt=""
style="width: 72px;height:72px;" fit="fill" />
<i class="el-icon-refresh" :title="$t('tip:refreshImage')"
@click.stop="refreshImage(series)"></i>
</div>
<div class="viewernavitextwrapper">
<div style="padding: 1px 5px 1px 1px;display: flex;justify-content: space-between;">
<div>#{{ series.seriesNumber }}</div>
@@ -113,35 +117,35 @@
</div>
</el-tooltip>
<div style="padding: 1px;">
{{ series.imageloadedArr.length }}/{{ series.instanceCount }}
{{ series.imageloadedArr.length <= series.instanceCount ? series.imageloadedArr.length :
series.instanceCount }}/{{ series.instanceCount }} </div>
<div />
</div>
<div />
</div>
</div>
<div v-if="showDelete"
style="display: flex;flex-direction: row;justify-content: space-between;" @click.stop="">
<div v-if="showDelete"
style="display: flex;flex-direction: row;justify-content: space-between;" @click.stop="">
<div>
<span style="font-size: 12px;">{{ $t('trials:audit:table:isReading') }}</span>
<el-switch v-model="series.isReading" size="mini"
@change="changeReadingStatus($event, series)" />
<div>
<span style="font-size: 12px;">{{ $t('trials:audit:table:isReading') }}</span>
<el-switch v-model="series.isReading" size="mini"
@change="changeReadingStatus($event, series)" />
</div>
<div>
<span style="font-size: 12px;">{{ $t('trials:audit:table:isDelete') }}</span>
<el-switch v-model="series.isDeleted" size="mini"
@change="changeDeleteStatus($event, series)" />
</div>
</div>
<div>
<span style="font-size: 12px;">{{ $t('trials:audit:table:isDelete') }}</span>
<el-switch v-model="series.isDeleted" size="mini"
@change="changeDeleteStatus($event, series)" />
</div>
</div>
<!-- <div style="position: absolute;bottom: -10px;left: 0;width: 100%;">
<!-- <div style="position: absolute;bottom: -10px;left: 0;width: 100%;">
<el-progress v-if="series.prefetchInstanceCount>0 && series.prefetchInstanceCount<series.instanceCount" :percentage="Number(series.prefetchInstanceCount/series.instanceCount)*100" />
</div> -->
<div
v-if="series.prefetchInstanceCount > 0 && series.prefetchInstanceCount < series.instanceCount * 100">
<el-progress
:percentage="parseInt((series.prefetchInstanceCount / series.instanceCount).toFixed(2))" />
</div>
<div
v-if="series.prefetchInstanceCount > 0 && series.prefetchInstanceCount < series.instanceCount * 100">
<el-progress
:percentage="parseInt((series.prefetchInstanceCount / series.instanceCount).toFixed(2))" />
</div>
</div>
</div>
</el-collapse-item>
</el-collapse>
@@ -154,104 +158,105 @@
<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="text-desc">
{{ study.StudyCode }}
</div>
<!-- <div v-show="study.Description" class="text-desc">
{{ study.Description }}
</div> -->
<div v-show="study.SeriesCount" class="text-desc">
{{ study.Modalities }} : {{ study.SeriesCount }} Series
</div>
</template>
<div v-show="study.Description" class="text-desc" style="background-color: #1f1f1f;">
{{ study.Description }}
<div class="text-desc">
{{ study.StudyCode }}
</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)">
<img class="image-preview" :src="seriesItem.previewImageUrl" crossorigin="anonymous"
alt="" style="width: 85px;height:85px;" fit="fill">
<!-- <div v-show="study.Description" class="text-desc">
{{ study.Description }}
</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>
<div v-show="study.SeriesCount" class="text-desc">
{{ 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>
</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>
@@ -273,7 +278,7 @@ import * as cornerstone from 'cornerstone-core'
import * as cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader'
import dicomViewer from '@/components/Dicom/DicomViewer'
import { getVisitStudyList, getAllRelationStudyList, getSeriesList } from '@/api/reading'
import { setSeriesStatus, setInstanceStatus } from '@/api/trials'
import { setSeriesStatus, setInstanceStatus, updateImageResizePath } from '@/api/trials'
import { getTaskUploadedDicomStudyList } from '@/api/reading'
import requestPoolManager from '@/utils/request-pool'
import store from '@/store'
@@ -358,6 +363,62 @@ export default {
workSpeedclose(true)
},
methods: {
async updateImageResizePath(data) {
try {
let res = await updateImageResizePath(data)
if (res.IsSuccess) {
this.$message.success(this.$t("message:tip:updateImageResizePath:success"))
}
} catch (err) {
console.log(err)
}
},
async refreshImage(item) {
let thumbnailPath = item.previewImageUrl.split(this.OSSclientConfig.basePath)[1]
let blob = await this.dicomToPng(item.imageIds[0])
let OSSclient = this.OSSclient
try {
let seriesRes = await OSSclient.put(thumbnailPath, blob)
if (seriesRes && seriesRes.url) {
let path = this.$getObjectName(seriesRes.url)
item.previewImageUrl = seriesRes.url
let data = {
seriesId: item.seriesId,
ImageResizePath: path
}
this.updateImageResizePath(data)
}
} catch (e) {
}
},
dicomToPng(imageId) {
return new Promise((resolve) => {
cornerstone.loadImage(imageId).then(async (image) => {
let width = image.columns, height = image.rows;
let canvas = document.createElement('canvas')
canvas.width = (width * 60) / height
canvas.height = 60
if (image) {
cornerstone.renderToCanvas(canvas, image)
// Canvas PNG
let blob = await this.canvasToBlob(canvas)
resolve(blob)
} else {
resolve()
}
})
}).catch((reason) => {
reason()
})
},
canvasToBlob(canvas) {
return new Promise((resolve) => {
canvas.toBlob((blob) => {
resolve(blob)
})
})
},
// 访
async getStudiesInfo() {
this.studyList = []
@@ -940,13 +1001,25 @@ export default {
this.loadAllImages()
}
workSpeedclose()
localStorage.setItem("QCRiskControl", 1)
}
}
}
}
</script>
<style>
<style lang="scss">
.viewerContainer .imageBox {
position: relative;
i {
position: absolute;
top: 0;
left: 0;
cursor: pointer;
}
}
.viewerContainer .el-tabs--border-card {
background: none;
border: none;
@@ -106,6 +106,16 @@ export default {
isTrial: {
type: Boolean,
default: false
},
isExternal: {
type: Boolean,
default: false
},
ExternalList: {
type: Array,
default: () => {
return []
}
}
},
data() {
@@ -122,7 +132,8 @@ export default {
rowData: {},
list: [],
searchData: defaultSearchData(),
title: this.$t('dictionary:signature:fileList')
title: this.$t('dictionary:signature:fileList'),
loading: false
}
},
watch: {
@@ -131,6 +142,16 @@ export default {
this.getList()
},
immediate: true,
},
isExternal: {
handler() {
if (this.isExternal) {
this.list = this.ExternalList
this.rowData = this.list[0] || {}
this.title = this.$t('dictionary:signature:view')
}
},
immediate: true
}
},
methods: {
@@ -145,6 +166,7 @@ export default {
},
async getList() {
try {
if (this.isExternal) return
if (!this.SystemDocumentId) return false
this.loading = true
if (!this.isTrial) {
+83 -28
View File
@@ -1,75 +1,118 @@
<template>
<div class="app-container my_menu">
<el-form :inline="true">
<el-form-item label="模块">
<el-select v-model="queryParams.ModuleTypeId" placeholder="模块" clearable>
<!-- 模块 -->
<el-form-item :label="$t('audit:label:moduleType')">
<el-select v-model="queryParams.ModuleTypeId" clearable>
<el-option v-for="item of dict.type.ModuleType" :key="`ModuleTypeId${item.value}`" :value="item.value"
:label="item.raw.ValueCN" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button type="primary" icon="el-icon-plus" @click="handleAdd">新增</el-button>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">
{{ $t('common:button:search') }}
</el-button>
<el-button type="primary" icon="el-icon-plus" @click="handleAdd">
{{ $t('common:button:new') }}
</el-button>
</el-form-item>
</el-form>
<div>
<el-button type="info" plain icon="el-icon-sort" size="mini" @click="toggleExpandAll">展开/折叠</el-button>
<!-- 展开/折叠 -->
<el-button type="info" plain icon="el-icon-sort" size="mini" @click="toggleExpandAll">
{{ $t('audit:button:fold') }}
</el-button>
</div>
<el-table v-if="refreshTable" v-adaptive="{ bottomOffset: 45 }" v-loading="loading" height="100" :data="menuList"
:default-expand-all="isExpandAll" row-key="Id" :tree-props="{ children: 'Children', hasChildren: 'hasChildren' }">
<el-table-column prop="ModuleTypeValueCN" label="模块" show-overflow-tooltip align="left" min-width="230px" />
<el-table-column prop="Description" label="操作名称" :show-overflow-tooltip="true" min-width="200px">
<!-- 模块 -->
<el-table-column prop="ModuleTypeValueCN" :label="$t('audit:label:moduleType')" show-overflow-tooltip align="left" min-width="230px" />
<!-- 操作名称 -->
<el-table-column prop="Description" :label="$t('audit:label:desc')" :show-overflow-tooltip="true" min-width="200px">
<template slot-scope="scope">
<span>{{ scope.row.DescriptionCN }}{{ scope.row.Description }}</span>
</template>
</el-table-column>
<el-table-column prop="DescriptionCN" label="其他" :show-overflow-tooltip="true" min-width="140px">
<!-- 其他 -->
<el-table-column prop="DescriptionCN" :label="$t('audit:label:descCN')" :show-overflow-tooltip="true" min-width="140px">
<template slot-scope="scope">
<!-- 被禁用 -->
<!-- -->
<span v-if="!scope.row.IsEnable"
style="cursor:pointer;line-height:20px;text-align:center;font-size: 12px;display: inline-block;width: 20px;height: 20px;border-radius: 50%;background: #DC143C;color: #fff;"
title="被禁用"></span>
:title="$t('audit:label:disable')">{{ $t('audit:label:disable1') }}</span>
<!-- 需要电签 -->
<span v-if="scope.row.IsHaveSign"
style="cursor:pointer;line-height:20px;text-align:center;font-size: 12px;display: inline-block;width: 20px;height: 20px;border-radius: 50%;background: #428bca;color: #fff;"
title="需要电签"></span>
:title="$t('audit:label:needSign')">{{ $t('audit:label:needSign1') }}</span>
<!-- 需要原因 -->
<span v-if="scope.row.IsHaveReason"
style="cursor:pointer;line-height:20px;text-align:center;font-size: 12px;display: inline-block;width: 20px;height: 20px;border-radius: 50%;background: #fab6b6;color: #fff;"
title="需要原因"></span>
:title="$t('audit:label:needReason')">{{ $t('audit:label:needReason1') }}</span>
<!-- 已经记录 -->
<span v-if="scope.row.IsFinish"
style="cursor:pointer;line-height:20px;text-align:center;font-size: 12px;display: inline-block;width: 20px;height: 20px;border-radius: 50%;background:#3CB371;color: #fff;"
title="已经记录"></span>
:title="$t('audit:label:isFinish')">{{ $t('audit:label:isFinish1') }}</span>
<!-- 核对完 -->
<span v-if="scope.row.IsJoinPlan"
style="cursor:pointer;line-height:20px;text-align:center;font-size: 12px;display: inline-block;width: 20px;height: 20px;border-radius: 50%;background: #FFA500;color: #fff;"
title="核对完"></span>
:title="$t('audit:label:isJoinPlan')">{{ $t('audit:label:isJoinPlan1') }}</span>
</template>
</el-table-column>
<el-table-column prop="Identification" label="标识" :show-overflow-tooltip="true" min-width="200px" />
<el-table-column prop="OptTypeValueCN" label="操作类型" show-overflow-tooltip width="100px" />
<el-table-column prop="ObjectTypeValueCN" label="对象类型" show-overflow-tooltip min-width="100px" />
<el-table-column prop="ChildrenTypeValueCN" label="数据类型" show-overflow-tooltip min-width="100px" />
<el-table-column prop="Sort" label="显示顺序" min-width="80" show-overflow-tooltip />
<el-table-column label="启用" align="center" prop="CreateTime" min-width="80">
<!-- 标识 -->
<el-table-column prop="Identification" :label="$t('audit:label:identification')" :show-overflow-tooltip="true" min-width="200px" />
<!-- 操作类型 -->
<el-table-column prop="OptTypeValueCN" :label="$t('audit:label:optType')" show-overflow-tooltip width="100px" />
<!-- 对象类型 -->
<el-table-column prop="ObjectTypeValueCN" :label="$t('audit:label:objectType')" show-overflow-tooltip min-width="100px" />
<!-- 数据类型 -->
<el-table-column prop="ChildrenTypeValueCN" :label="$t('audit:label:dataType')" show-overflow-tooltip min-width="100px" />
<!-- 显示顺序 -->
<el-table-column prop="Sort" :label="$t('audit:label:sort')" min-width="80" show-overflow-tooltip />
<!-- 启用 -->
<el-table-column :label="$t('audit:label:isEnable')" align="center" prop="CreateTime" min-width="80">
<!-- 启用/禁用 -->
<template slot-scope="scope">
<el-tag v-if="scope.row.IsEnable" size="success">启用</el-tag>
<el-tag v-else size="danger">禁用</el-tag>
<!-- <el-tag v-if="scope.row.IsEnable" size="success">启用</el-tag>
<el-tag v-else size="danger">禁用</el-tag> -->
<el-tag :type="!scope.row.IsEnable ? 'info' : ''">
{{ $fd('IsEnable', scope.row.IsEnable) }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" align="center" fixed="right" min-width="340" class-name="small-padding fixed-width">
<el-table-column :label="$t('common:action:action')" align="center" fixed="right" min-width="340" class-name="small-padding fixed-width">
<template slot-scope="scope">
<!-- 上移 -->
<el-button
v-if="scope.row.ModuleTypeValueCN && scope.row.ObjectTypeValueCN && scope.row.OptTypeValueCN && scope.row.ChildrenTypeValueCN"
type="text" @click="handleUpMove(scope.row)">上移</el-button>
<el-button type="text" @click="handleUpdate(scope.row)">修改</el-button>
type="text" @click="handleUpMove(scope.row)"
>
{{ $t('audit:button:upward') }}
</el-button>
<!-- 修改 -->
<el-button type="text" @click="handleUpdate(scope.row)">
{{ $t('audit:button:modify') }}
</el-button>
<!-- 新增 -->
<el-button
v-if="!(scope.row.ModuleTypeValueCN && scope.row.ObjectTypeValueCN && scope.row.OptTypeValueCN && scope.row.ChildrenTypeValueCN)"
type="text" @click="handleAdd(scope.row)">新增</el-button>
type="text" @click="handleAdd(scope.row)">{{ $t('common:button:new') }}</el-button>
<!-- Children -->
<el-button
v-if="scope.row.ModuleTypeValueCN && scope.row.ObjectTypeValueCN && scope.row.OptTypeValueCN && scope.row.ChildrenTypeValueCN"
type="text" @click="handleChildren(scope.row)">Children</el-button>
type="text" @click="handleChildren(scope.row)"
>
{{ $t('audit:button:children') }}
</el-button>
<!-- 复制到 -->
<el-button
v-if="scope.row.ModuleTypeValueCN && scope.row.ObjectTypeValueCN && scope.row.OptTypeValueCN && scope.row.ChildrenTypeValueCN"
type="text" @click="handleCopy(scope.row)">复制到</el-button>
<el-button type="text" @click="handleDelete(scope.row)">删除</el-button>
type="text" @click="handleCopy(scope.row)">
{{ $t('audit:button:copyTo') }}
</el-button>
<!-- 删除 -->
<el-button type="text" @click="handleDelete(scope.row)">{{ $t('common:button:delete') }}</el-button>
</template>
</el-table-column>
</el-table>
@@ -191,6 +234,17 @@
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="24">
<el-form-item>
<span slot="label">
适用的标准
</span>
<el-select v-model="form.ApplyCriterionList" placeholder="" clearable multiple>
<el-option v-for="item in $d.CriterionType" :key="item.id" :label="item.label" :value="item.value">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col v-show="form.ConfigType === 'C' && title !== '复制'" :span="12">
<el-form-item>
<span slot="label">
@@ -1008,6 +1062,7 @@ export default {
ValueCN: null,
EnumType: '',
IsShowByTrialConfig: false,
ApplyCriterionList: [],
TrialConfigRelyFieldName: null,
Identification: null,
IsForwardPosition: false,
@@ -3,11 +3,11 @@
<!-- 搜索框 -->
<div class="search">
<el-form :inline="true" class="base-search-form">
<el-form-item label="字典表名称:">
<el-form-item :label="$t('dic:all:label:code')">
<el-input v-model="searchData.Code" clearable style="width:120px;" />
</el-form-item>
<el-form-item label="字典分组:">
<el-select v-model="searchData.ConfigTypeId" placeholder="字典分组" clearable size="small">
<el-form-item :label="$t('dic:all:label:group')">
<el-select v-model="searchData.ConfigTypeId" clearable size="small">
<el-option v-for="item of basicDicList" :key="item.Id" :value="item.Id" :label="item.Code" />
</el-select>
</el-form-item>
@@ -29,7 +29,9 @@
</el-form-item>
</el-form>
<span style="margin-left:auto">
<el-button type="primary" @click="handleAdd">新建</el-button>
<el-button type="primary" @click="handleAdd">
{{ $t('common:button:new') }}
</el-button>
</span>
</div>
<el-table
@@ -44,33 +46,33 @@
<el-table-column type="index" width="60" />
<el-table-column
prop="Code"
label="字典表名称"
:label="$t('dic:all:label:code')"
sortable="custom"
min-width="180"
show-overflow-tooltip
/>
<el-table-column
prop="ConfigType"
label="字典分组"
:label="$t('dic:all:label:group')"
sortable="custom"
min-width="180"
show-overflow-tooltip
/>
<el-table-column
prop="Description"
label="描述"
:label="$t('dic:all:label:desc')"
sortable="custom"
min-width="180"
show-overflow-tooltip
/>
<el-table-column
prop="ShowOrder"
label="显示顺序"
:label="$t('dic:all:label:showOrder')"
sortable="custom"
min-width="180"
show-overflow-tooltip
/>
<el-table-column label="是否可用" width="100" fixed="right">
<el-table-column :label="$t('dic:all:label:isEnable')" width="100" fixed="right">
<template slot-scope="scope">
<el-switch
v-model="scope.row.IsEnable"
@@ -80,25 +82,25 @@
/>
</template>
</el-table-column>
<el-table-column label="操作" width="200" fixed="right">
<el-table-column :label="$t('common:action:action')" width="200" fixed="right">
<template slot-scope="scope">
<el-button
type="text"
@click="handleEdit(scope.row)"
>
编辑
{{ $t('common:action:edit') }}
</el-button>
<el-button
type="text"
@click="handleChild(scope.row)"
>
子项
{{ $t('dic:all:label:childItem') }}
</el-button>
<el-button
type="text"
@click="handleDelete(scope.row)"
>
删除
{{ $t('common:button:delete') }}
</el-button>
</template>
</el-table-column>
@@ -174,7 +176,7 @@ export default {
switchChange(event, item) {
this.loading = true
addOrUpdateBasicDic(item).then(res => {
this.$message.success('Saved successfully!')
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.loading = false
}).catch(() => {
this.loading = false
@@ -182,12 +184,12 @@ export default {
},
handleEdit(row) {
this.$nextTick(() => {
this.$refs['DictionaryTypeConfigForm'].openDialog('DictionaryTypeConfig', row)
this.$refs['DictionaryTypeConfigForm'].openDialog(this.$t('common:action:edit'), row)
})
},
handleAdd() {
this.$nextTick(() => {
this.$refs['DictionaryTypeConfigForm'].openDialog('DictionaryTypeConfig', { DataTypeEnum: 3 })
this.$refs['DictionaryTypeConfigForm'].openDialog(this.$t('common:button:new'), { DataTypeEnum: 3 })
})
},
handleDelete(row) {
@@ -4,32 +4,39 @@
<div class="search">
<el-form :inline="true" size="mini" class="base-search-form" />
<span style="margin-left:auto">
<el-button type="primary" size="mini" @click="handleAdd">新建</el-button>
<el-button type="primary" size="mini" @click="handleAdd">{{ $t('common:button:new') }}</el-button>
</span>
</div>
<el-table v-loading="loading" v-adaptive="{ bottomOffset: 45 }" :data="list" stripe size="small" height="100"
@sort-change="handleSortByColumn">
<el-table-column type="index" width="60" />
<el-table-column v-if="!~$route.path.indexOf('select')" prop="Code" label="字典键值" min-width="180"
<!-- 字典键值 -->
<el-table-column v-if="!~$route.path.indexOf('select')" prop="Code" :label="$t('dic:all:label:keyCode')" min-width="180"
show-overflow-tooltip sortable='custom' />
<el-table-column prop="ValueCN" label="中文值" min-width="180" show-overflow-tooltip sortable='custom' />
<el-table-column prop="Value" label="英文值" min-width="180" show-overflow-tooltip sortable='custom' />
<el-table-column prop="ChildGroup" label="分组" min-width="180" show-overflow-tooltip sortable='custom' />
<el-table-column prop="ShowOrder" label="显示顺序" min-width="180" show-overflow-tooltip sortable='custom' />
<el-table-column prop="Description" label="描述" min-width="180" show-overflow-tooltip sortable='custom' />
<el-table-column label="是否可用" width="100" fixed="right">
<!-- 中文值 -->
<el-table-column prop="ValueCN" :label="$t('dic:all:label:valueCN')" min-width="180" show-overflow-tooltip sortable='custom' />
<!-- 英文值 -->
<el-table-column prop="Value" :label="$t('dic:all:label:valueEN')" min-width="180" show-overflow-tooltip sortable='custom' />
<!-- 分组 -->
<el-table-column prop="ChildGroup" :label="$t('dic:all:label:childGroup')" min-width="180" show-overflow-tooltip sortable='custom' />
<!-- 显示顺序 -->
<el-table-column prop="ShowOrder" :label="$t('dic:all:label:showOrder')" min-width="180" show-overflow-tooltip sortable='custom' />
<!-- 描述 -->
<el-table-column prop="Description" :label="$t('dic:all:label:desc')" min-width="180" show-overflow-tooltip sortable='custom' />
<!-- 是否可用 -->
<el-table-column :label="$t('dic:all:label:isEnable')" width="100" fixed="right">
<template slot-scope="scope">
<el-switch v-model="scope.row.IsEnable" :active-value="true" :inactive-value="false"
@change="(event) => { return switchChange(event, scope.row) }" />
</template>
</el-table-column>
<el-table-column label="操作" width="150" fixed="right">
<el-table-column :label="$t('common:action:action')" width="150" fixed="right">
<template slot-scope="scope">
<el-button type="text" @click="handleEdit(scope.row)">
编辑
{{ $t('common:action:edit') }}
</el-button>
<el-button type="text" @click="handleDelete(scope.row)">
删除
{{ $t('common:button:delete') }}
</el-button>
</template>
</el-table-column>
@@ -101,7 +108,7 @@ export default {
switchChange(event, item) {
this.loading = true
addOrUpdateBasicDic(item).then(res => {
this.$message.success('Saved successfully!')
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.loading = false
}).catch(() => {
this.loading = false
@@ -109,12 +116,12 @@ export default {
},
handleEdit(row) {
this.$nextTick(() => {
this.$refs['DictionaryChildAddOrUpdateForm'].openDialog('DictionaryChild Edit', row, { DataTypeEnum: this.parent.DataTypeEnum })
this.$refs['DictionaryChildAddOrUpdateForm'].openDialog(this.$t('common:action:edit'), row, { DataTypeEnum: this.parent.DataTypeEnum })
})
},
handleAdd() {
this.$nextTick(() => {
this.$refs['DictionaryChildAddOrUpdateForm'].openDialog('DictionaryChild Add', {}, { ConfigTypeId: this.parent.ConfigTypeId, parentId: this.parent.Id, DataTypeEnum: this.parent.DataTypeEnum })
this.$refs['DictionaryChildAddOrUpdateForm'].openDialog(this.$t('common:button:new'), {}, { ConfigTypeId: this.parent.ConfigTypeId, parentId: this.parent.Id, DataTypeEnum: this.parent.DataTypeEnum })
})
},
handleDelete(row) {
@@ -8,25 +8,25 @@
label-width="120px"
size="small"
>
<el-form-item label="Enum Value: " prop="Code">
<el-form-item :label="$t('dic:all:label:keyCode')" prop="Code">
<el-input v-model="form.Code" />
</el-form-item>
<el-form-item label="Value: " prop="Code">
<el-form-item :label="$t('dic:all:label:valueCN')" prop="Code">
<el-input v-model="form.ValueCN" />
</el-form-item>
<el-form-item label="Value EN: " prop="Code">
<el-form-item :label="$t('dic:all:label:valueEN')" prop="Code">
<el-input v-model="form.Value" />
</el-form-item>
<el-form-item label="ChildGroup: " prop="Code">
<el-form-item :label="$t('dic:all:label:childGroup')" prop="Code">
<el-input v-model="form.ChildGroup" />
</el-form-item>
<el-form-item label="ShowOrder: " prop="Code">
<el-form-item :label="$t('dic:all:label:showOrder')" prop="Code">
<el-input-number v-model="form.ShowOrder" :min="0" />
</el-form-item>
<el-form-item label="Description: " prop="Code">
<el-form-item :label="$t('dic:all:label:desc')" prop="Code">
<el-input v-model="form.Description" />
</el-form-item>
<el-form-item label="Is Enable: ">
<el-form-item :label="$t('dic:all:label:isEnable')">
<el-switch
v-model="form.IsEnable"
:active-value="true"
@@ -36,8 +36,12 @@
</el-form>
</template>
<template slot="dialog-footer">
<el-button :disabled="btnLoading" size="small" type="primary" @click="handleCancle">Cancel</el-button>
<el-button size="small" type="primary" :loading="btnLoading" @click="handleSave">Save</el-button>
<el-button :disabled="btnLoading" size="small" type="primary" @click="handleCancle">
{{ $t('common:button:cancel') }}
</el-button>
<el-button size="small" type="primary" :loading="btnLoading" @click="handleSave">
{{ $t('common:button:save') }}
</el-button>
</template>
</base-model>
</template>
@@ -90,7 +94,7 @@ export default {
this.btnLoading = true
addOrUpdateBasicDic(this.form).then(res => {
this.btnLoading = false
this.$message.success('Saved successfully!')
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.model_cfg.visible = false
this.$emit('getList')
}).catch(() => {
@@ -7,8 +7,9 @@
:rules="rules"
label-width="120px"
size="small"
>
<el-form-item label="ConfigTypeId: " prop="Code">
>
<!-- 字典分组 -->
<el-form-item :label="$t('dic:all:label:group')" prop="Code">
<el-select v-model="form.ConfigTypeId" filterable style="width:100%;">
<el-option
v-for="(item,index) of basicDicList"
@@ -18,7 +19,8 @@
/>
</el-select>
</el-form-item>
<el-form-item label="TypeEnum: " prop="DataTypeEnum">
<!-- 类型 -->
<el-form-item :label="$t('dic:all:label:dataType')" prop="DataTypeEnum">
<el-select v-model="form.DataTypeEnum" filterable style="width:100%;">
<el-option
:label="`config`"
@@ -38,16 +40,19 @@
/>
</el-select>
</el-form-item>
<el-form-item label="Code: " prop="Code">
<!-- 字典表名称 -->
<el-form-item :label="$t('dic:all:label:code')" prop="Code">
<el-input v-model="form.Code" />
</el-form-item>
<!--<el-form-item label="KeyName: " prop="Code">-->
<!--<el-input v-model="form.KeyName" />-->
<!--</el-form-item>-->
<el-form-item label="ShowOrder: " prop="Code">
<!-- 显示顺序 -->
<el-form-item :label="$t('dic:all:label:showOrder')" prop="Code">
<el-input-number v-model="form.ShowOrder" :min="0" />
</el-form-item>
<el-form-item label="Description: " prop="Code">
<!-- 描述 -->
<el-form-item :label="$t('dic:all:label:desc')" prop="Code">
<el-input v-model="form.Description" />
</el-form-item>
<!-- <el-form-item label="Value: " prop="Code">
@@ -56,7 +61,8 @@
<el-form-item label="ValueCN: " prop="Code">
<el-input v-model="form.ValueCN" />
</el-form-item> -->
<el-form-item label="Is Enable: ">
<!-- 是否启用 -->
<el-form-item :label="$t('dic:all:label:isEnable')">
<el-switch
v-model="form.IsEnable"
:active-value="true"
@@ -66,8 +72,12 @@
</el-form>
</template>
<template slot="dialog-footer">
<el-button :disabled="btnLoading" size="small" type="primary" @click="handleCancle">Cancel</el-button>
<el-button size="small" type="primary" :loading="btnLoading" @click="handleSave">Save</el-button>
<el-button :disabled="btnLoading" size="small" type="primary" @click="handleCancle">
{{ $t('common:button:cancel') }}
</el-button>
<el-button size="small" type="primary" :loading="btnLoading" @click="handleSave">
{{ $t('common:button:save') }}
</el-button>
</template>
</base-model>
</template>
@@ -103,8 +113,8 @@ export default {
btnLoading: false,
form: formDataDefault(),
rules: {
Code: [{ required: true, message: 'Please specify', trigger: 'blur' }],
DataTypeEnum: [{ required: true, message: 'Please specify', trigger: 'blur' }]
Code: [{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }],
DataTypeEnum: [{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }]
},
model_cfg: { visible: false, showClose: true, width: '600px', title: '', appendToBody: true }
}
@@ -132,7 +142,7 @@ export default {
this.btnLoading = true
addOrUpdateBasicDic(this.form).then(res => {
this.btnLoading = false
this.$message.success('Saved successfully!')
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.model_cfg.visible = false
this.$emit('getList')
}).catch(() => {
@@ -3,11 +3,11 @@
<!-- 搜索框 -->
<div class="search">
<el-form :inline="true" class="base-search-form">
<el-form-item label="字典表名称:">
<el-form-item :label="$t('dic:all:label:code')">
<el-input v-model="searchData.Code" clearable style="width:120px;" />
</el-form-item>
<el-form-item label="字典分组:">
<el-select v-model="searchData.ConfigTypeId" placeholder="字典分组" clearable size="small">
<el-form-item :label="$t('dic:all:label:group')">
<el-select v-model="searchData.ConfigTypeId" clearable size="small">
<el-option v-for="item of basicDicList" :key="item.Id" :value="item.Id" :label="item.Code" />
</el-select>
</el-form-item>
@@ -29,7 +29,9 @@
</el-form-item>
</el-form>
<span style="margin-left:auto">
<el-button type="primary" @click="handleAdd">新建</el-button>
<el-button type="primary" @click="handleAdd">
{{ $t('common:button:new') }}
</el-button>
</span>
</div>
<el-table
@@ -44,33 +46,33 @@
<el-table-column type="index" width="60" />
<el-table-column
prop="Code"
label="字典表名称"
:label="$t('dic:all:label:code')"
sortable="custom"
min-width="180"
show-overflow-tooltip
/>
<el-table-column
prop="ConfigType"
label="字典分组"
:label="$t('dic:all:label:group')"
sortable="custom"
min-width="180"
show-overflow-tooltip
/>
<el-table-column
prop="Description"
label="描述"
:label="$t('dic:all:label:desc')"
sortable="custom"
min-width="180"
show-overflow-tooltip
/>
<el-table-column
prop="ShowOrder"
label="显示顺序"
:label="$t('dic:all:label:showOrder')"
sortable="custom"
min-width="180"
show-overflow-tooltip
/>
<el-table-column label="是否可用" width="100" fixed="right">
<el-table-column :label="$t('dic:all:label:isEnable')" width="100" fixed="right">
<template slot-scope="scope">
<el-switch
v-model="scope.row.IsEnable"
@@ -80,25 +82,25 @@
/>
</template>
</el-table-column>
<el-table-column label="操作" width="200" fixed="right">
<el-table-column :label="$t('common:action:action')" width="200" fixed="right">
<template slot-scope="scope">
<el-button
type="text"
@click="handleEdit(scope.row)"
>
编辑
{{ $t('common:action:edit') }}
</el-button>
<el-button
type="text"
@click="handleChild(scope.row)"
>
子项
{{ $t('dic:all:label:childItem') }}
</el-button>
<el-button
type="text"
@click="handleDelete(scope.row)"
>
删除
{{ $t('common:button:delete') }}
</el-button>
</template>
</el-table-column>
@@ -174,7 +176,7 @@ export default {
switchChange(event, item) {
this.loading = true
addOrUpdateBasicDic(item).then(res => {
this.$message.success('Saved successfully!')
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.loading = false
}).catch(() => {
this.loading = false
@@ -182,12 +184,12 @@ export default {
},
handleEdit(row) {
this.$nextTick(() => {
this.$refs['DictionaryTypeConfigForm'].openDialog('DictionaryTypeConfig', row)
this.$refs['DictionaryTypeConfigForm'].openDialog(this.$t('common:action:edit'), row)
})
},
handleAdd() {
this.$nextTick(() => {
this.$refs['DictionaryTypeConfigForm'].openDialog('DictionaryTypeConfig', { DataTypeEnum: 2 })
this.$refs['DictionaryTypeConfigForm'].openDialog(this.$t('common:button:new'), { DataTypeEnum: 2 })
})
},
handleDelete(row) {
+10 -5
View File
@@ -1,19 +1,24 @@
<template>
<div>
<el-tabs v-model="activeTab" @tab-click="handleClick">
<el-tab-pane label="全部" name="/dictionary/newDictionary/list">
<!-- 全部 -->
<el-tab-pane :label="$t('dic:label:all')" name="/dictionary/newDictionary/list">
<dictList v-if="activeTab == '/dictionary/newDictionary/list'" />
</el-tab-pane>
<el-tab-pane label="下拉框字典" name="/dictionary/newDictionary/select">
<!-- 下拉框字典 -->
<el-tab-pane :label="$t('dic:label:select')" name="/dictionary/newDictionary/select">
<dictSelect v-if="activeTab == '/dictionary/newDictionary/select'" />
</el-tab-pane>
<el-tab-pane label="枚举字典" name="/dictionary/newDictionary/enum">
<!-- 枚举字典 -->
<el-tab-pane :label="$t('dic:label:enum')" name="/dictionary/newDictionary/enum">
<dictEnum v-if="activeTab == '/dictionary/newDictionary/enum'" />
</el-tab-pane>
<el-tab-pane label="布尔字典" name="/dictionary/newDictionary/bool">
<!-- 布尔字典 -->
<el-tab-pane :label="$t('dic:label:bool')" name="/dictionary/newDictionary/bool">
<dictBool v-if="activeTab == '/dictionary/newDictionary/bool'" />
</el-tab-pane>
<el-tab-pane label="字典类型配置" name="/dictionary/newDictionary/config">
<!-- 字典类型配置 -->
<el-tab-pane :label="$t('dic:label:config')" name="/dictionary/newDictionary/config">
<dictConfig v-if="activeTab == '/dictionary/newDictionary/config'" />
</el-tab-pane>
</el-tabs>
@@ -8,7 +8,8 @@
label-width="120px"
size="small"
>
<el-form-item label="ConfigTypeId: " prop="Code">
<!-- 字典分组 -->
<el-form-item :label="$t('dic:all:label:group')" prop="Code">
<el-select v-model="form.ConfigTypeId" filterable style="width:100%;">
<el-option
v-for="(item,index) of basicDicList"
@@ -18,7 +19,8 @@
/>
</el-select>
</el-form-item>
<el-form-item label="DataTypeEnum: " prop="DataTypeEnum">
<!-- 类型 -->
<el-form-item :label="$t('dic:all:label:dataType')" prop="DataTypeEnum">
<el-select v-model="form.DataTypeEnum" filterable style="width:100%;">
<el-option
:label="`config`"
@@ -38,16 +40,19 @@
/>
</el-select>
</el-form-item>
<el-form-item label="Code: " prop="Code">
<!-- 字典表名称 -->
<el-form-item :label="$t('dic:all:label:code')" prop="Code">
<el-input v-model="form.Code" />
</el-form-item>
<!--<el-form-item label="KeyName: " prop="Code">-->
<!--<el-input v-model="form.KeyName" />-->
<!--</el-form-item>-->
<el-form-item label="ShowOrder: " prop="Code">
<!-- 显示顺序 -->
<el-form-item :label="$t('dic:all:label:showOrder')" prop="Code">
<el-input-number v-model="form.ShowOrder" :min="0" />
</el-form-item>
<el-form-item label="Description: " prop="Code">
<!-- 描述 -->
<el-form-item :label="$t('dic:all:label:desc')" prop="Code">
<el-input v-model="form.Description" />
</el-form-item>
<!-- <el-form-item label="Value: " prop="Code">
@@ -56,7 +61,8 @@
<el-form-item label="ValueCN: " prop="Code">
<el-input v-model="form.ValueCN" />
</el-form-item> -->
<el-form-item label="Is Enable: ">
<!-- 是否启用 -->
<el-form-item :label="$t('dic:all:label:isEnable')">
<el-switch
v-model="form.IsEnable"
:active-value="true"
@@ -66,8 +72,12 @@
</el-form>
</template>
<template slot="dialog-footer">
<el-button :disabled="btnLoading" size="small" type="primary" @click="handleCancle">Cancel</el-button>
<el-button size="small" type="primary" :loading="btnLoading" @click="handleSave">Save</el-button>
<el-button :disabled="btnLoading" size="small" type="primary" @click="handleCancle">
{{ $t('common:button:cancel') }}
</el-button>
<el-button size="small" type="primary" :loading="btnLoading" @click="handleSave">
{{ $t('common:button:save') }}
</el-button>
</template>
</base-model>
</template>
@@ -103,8 +113,8 @@ export default {
btnLoading: false,
form: formDataDefault(),
rules: {
Code: [{ required: true, message: 'Please specify', trigger: 'blur' }],
DataTypeEnum: [{ required: true, message: 'Please specify', trigger: 'blur' }]
Code: [{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }],
DataTypeEnum: [{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }]
},
model_cfg: { visible: false, showClose: true, width: '600px', title: '', appendToBody: true }
}
@@ -130,7 +140,7 @@ export default {
this.btnLoading = true
addOrUpdateBasicDic(this.form).then(res => {
this.btnLoading = false
this.$message.success('Saved successfully!')
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.model_cfg.visible = false
this.$emit('getList')
}).catch(() => {
@@ -3,15 +3,18 @@
<!-- 搜索框 -->
<div class="search">
<el-form :inline="true" class="base-search-form">
<el-form-item label="字典表名称:">
<!-- 字典表名称 -->
<el-form-item :label="$t('dic:all:label:code')">
<el-input v-model="searchData.Code" clearable style="width:120px;" />
</el-form-item>
<el-form-item label="字典分组:">
<el-select v-model="searchData.ConfigTypeId" placeholder="字典分组" clearable size="small">
<!-- 字典分组 -->
<el-form-item :label="$t('dic:all:label:group')">
<el-select v-model="searchData.ConfigTypeId" clearable size="small">
<el-option v-for="item of basicDicList" :key="item.Id" :value="item.Id" :label="item.Code" />
</el-select>
</el-form-item>
<el-form-item label="关键字:">
<!-- 关键字 -->
<el-form-item :label="$t('dic:all:label:keyWord')">
<el-input v-model="searchData.KeyInfo" clearable style="width:120px;" />
</el-form-item>
<el-form-item>
@@ -32,8 +35,14 @@
</el-form-item>
</el-form>
<span style="margin-left:auto">
<el-button type="primary" @click="handleAdd">新建</el-button>
<el-button type="primary" @click="handleBatchAdd">批量新建</el-button>
<!-- 新建 -->
<el-button type="primary" @click="handleAdd">
{{ $t('common:button:new') }}
</el-button>
<!-- 批量新建 -->
<el-button type="primary" @click="handleBatchAdd">
{{ $t('common:button:batchNew') }}
</el-button>
</span>
</div>
<el-table
@@ -46,16 +55,18 @@
@sort-change="handleSortByColumn"
>
<el-table-column type="index" width="60" />
<!-- 字典表名称 -->
<el-table-column
prop="Code"
label="字典表名称"
:label="$t('dic:all:label:code')"
sortable="custom"
min-width="180"
show-overflow-tooltip
/>
<!-- 类型 -->
<el-table-column
prop="DataTypeEnum"
label="类型"
:label="$t('dic:all:label:dataType')"
sortable="custom"
min-width="180"
show-overflow-tooltip
@@ -64,28 +75,32 @@
{{ DataTypeEnumList[scope.row.DataTypeEnum] }}
</template>
</el-table-column>
<!-- 字典分组 -->
<el-table-column
prop="ConfigType"
label="字典分组"
:label="$t('dic:all:label:group')"
sortable="custom"
min-width="180"
show-overflow-tooltip
/>
<!-- 描述 -->
<el-table-column
prop="Description"
label="描述"
:label="$t('dic:all:label:desc')"
sortable="custom"
min-width="180"
show-overflow-tooltip
/>
<!-- 显示顺序 -->
<el-table-column
prop="ShowOrder"
label="显示顺序"
:label="$t('dic:all:label:showOrder')"
sortable="custom"
min-width="180"
show-overflow-tooltip
/>
<el-table-column label="是否可用" width="100" fixed="right">
<!-- 是否可用 -->
<el-table-column :label="$t('dic:all:label:isEnable')" width="100" fixed="right">
<template slot-scope="scope">
<el-switch
v-model="scope.row.IsEnable"
@@ -95,25 +110,28 @@
/>
</template>
</el-table-column>
<el-table-column label="操作" width="300" fixed="right">
<el-table-column :label="$t('common:action:action')" width="300" fixed="right">
<template slot-scope="scope">
<!-- 编辑 -->
<el-button
type="text"
@click="handleEdit(scope.row)"
>
编辑
{{ $t('common:action:edit') }}
</el-button>
<!-- 子项 -->
<el-button
type="text"
@click="handleChild(scope.row)"
>
子项
{{ $t('dic:all:label:childItem') }}
</el-button>
<!-- 删除 -->
<el-button
type="text"
@click="handleDelete(scope.row)"
>
删除
{{ $t('common:button:delete') }}
</el-button>
</template>
</el-table-column>
@@ -201,17 +219,17 @@ export default {
},
handleEdit(row) {
this.$nextTick(() => {
this.$refs['DictionaryTypeConfigForm'].openDialog('DictionaryTypeConfig', row)
this.$refs['DictionaryTypeConfigForm'].openDialog(this.$t('common:action:edit'), row)
})
},
handleAdd() {
this.$nextTick(() => {
this.$refs['DictionaryTypeConfigForm'].openDialog('DictionaryTypeConfig', {})
this.$refs['DictionaryTypeConfigForm'].openDialog(this.$t('common:button:new'), {})
})
},
handleBatchAdd() {
this.$nextTick(() => {
this.$refs['batchAddForm'].openDialog('批量新建', {})
this.$refs['batchAddForm'].openDialog(this.$t('common:button:batchNew'), {})
})
},
handleDelete(row) {
@@ -3,11 +3,11 @@
<!-- 搜索框 -->
<div class="search">
<el-form :inline="true" class="base-search-form">
<el-form-item label="字典表名称:">
<el-form-item :label="$t('dic:all:label:code')">
<el-input v-model="searchData.Code" clearable style="width:120px;" />
</el-form-item>
<el-form-item label="字典分组:">
<el-select v-model="searchData.ConfigTypeId" placeholder="字典分组" clearable size="small">
<el-form-item :label="$t('dic:all:label:group')">
<el-select v-model="searchData.ConfigTypeId" clearable size="small">
<el-option v-for="item of basicDicList" :key="item.Id" :value="item.Id" :label="item.Code" />
</el-select>
</el-form-item>
@@ -29,8 +29,13 @@
</el-form-item>
</el-form>
<span style="margin-left:auto">
<el-button type="primary" @click="handleSetting">配置字典分组</el-button>
<el-button type="primary" @click="handleAdd">新建</el-button>
<!-- 配置字典分组 -->
<el-button type="primary" @click="handleSetting">
{{ $t('dic:select:button:setting') }}
</el-button>
<el-button type="primary" @click="handleAdd">
{{ $t('common:button:new') }}
</el-button>
</span>
</div>
<el-table
@@ -45,33 +50,33 @@
<el-table-column type="index" width="60" />
<el-table-column
prop="Code"
label="字典表名称"
:label="$t('dic:all:label:code')"
sortable="custom"
min-width="180"
show-overflow-tooltip
/>
<el-table-column
prop="ConfigType"
label="字典分组"
:label="$t('dic:all:label:group')"
sortable="custom"
min-width="180"
show-overflow-tooltip
/>
<el-table-column
prop="Description"
label="描述"
:label="$t('dic:all:label:desc')"
sortable="custom"
min-width="180"
show-overflow-tooltip
/>
<el-table-column
prop="ShowOrder"
label="显示顺序"
:label="$t('dic:all:label:showOrder')"
sortable="custom"
min-width="180"
show-overflow-tooltip
/>
<el-table-column label="是否可用" width="100" fixed="right">
<el-table-column :label="$t('dic:all:label:isEnable')" width="100" fixed="right">
<template slot-scope="scope">
<el-switch
v-model="scope.row.IsEnable"
@@ -81,25 +86,25 @@
/>
</template>
</el-table-column>
<el-table-column label="操作" width="300" fixed="right">
<el-table-column :label="$t('common:action:action')" width="300" fixed="right">
<template slot-scope="scope">
<el-button
type="text"
@click="handleEdit(scope.row)"
>
编辑
{{ $t('common:action:edit') }}
</el-button>
<el-button
type="text"
@click="handleChild(scope.row)"
>
子项
{{ $t('dic:all:label:childItem') }}
</el-button>
<el-button
type="text"
@click="handleDelete(scope.row)"
>
删除
{{ $t('common:button:delete') }}
</el-button>
</template>
</el-table-column>
@@ -175,7 +180,7 @@ export default {
switchChange(event, item) {
this.loading = true
addOrUpdateBasicDic(item).then(res => {
this.$message.success('Saved successfully!')
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.loading = false
}).catch(() => {
this.loading = false
@@ -183,12 +188,12 @@ export default {
},
handleEdit(row) {
this.$nextTick(() => {
this.$refs['DictionaryTypeConfigForm'].openDialog('DictionaryTypeConfig', row)
this.$refs['DictionaryTypeConfigForm'].openDialog(this.$t('common:action:edit'), row)
})
},
handleAdd() {
this.$nextTick(() => {
this.$refs['DictionaryTypeConfigForm'].openDialog('DictionaryTypeConfig', { DataTypeEnum: 1 })
this.$refs['DictionaryTypeConfigForm'].openDialog(this.$t('common:button:new'), { DataTypeEnum: 1 })
})
},
handleDelete(row) {
@@ -8,13 +8,13 @@
label-width="150px"
size="small"
>
<el-form-item label="Dictionary Type: " prop="Code">
<el-form-item :label="$t('dic:all:label:group')"" prop="Code">
<el-input v-model="form.Code" />
</el-form-item>
<el-form-item label="Description: " prop="Code">
<el-form-item :label="$t('dic:all:label:desc')">
<el-input v-model="form.Description" />
</el-form-item>
<el-form-item label="Is Enable: ">
<el-form-item :label="$t('dic:all:label:isEnable')">
<el-switch
v-model="form.IsEnable"
:active-value="true"
@@ -24,8 +24,12 @@
</el-form>
</template>
<template slot="dialog-footer">
<el-button :disabled="btnLoading" size="small" type="primary" @click="handleCancle">Cancel</el-button>
<el-button size="small" type="primary" :loading="btnLoading" @click="handleSave">Save</el-button>
<el-button :disabled="btnLoading" size="small" type="primary" @click="handleCancle">
{{ $t('common:button:cancel') }}
</el-button>
<el-button size="small" type="primary" :loading="btnLoading" @click="handleSave">
{{ $t('common:button:save') }}
</el-button>
</template>
</base-model>
</template>
@@ -52,7 +56,7 @@ export default {
btnLoading: false,
form: formDataDefault(),
rules: {
Code: [{ required: true, message: 'Please specify', trigger: 'blur' }]
Code: [{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }]
},
model_cfg: { visible: false, showClose: true, width: '600px', title: '' }
}
@@ -3,10 +3,10 @@
<!-- 搜索框 -->
<div class="search">
<el-form :inline="true" class="base-search-form">
<el-form-item label="Code:">
<el-form-item :label="$t('dic:all:label:group')">
<el-input v-model="searchData.Code" clearable style="width:120px;" />
</el-form-item>
<el-form-item label="KeyName:">
<el-form-item :label="$t('dic:all:label:keyWord')">
<el-input v-model="searchData.KeyName" clearable style="width:120px;" />
</el-form-item>
<el-form-item>
@@ -27,7 +27,9 @@
</el-form-item>
</el-form>
<span style="margin-left:auto">
<el-button type="primary" @click="handleAdd">New</el-button>
<el-button type="primary" @click="handleAdd">
{{ $t('common:button:new') }}
</el-button>
</span>
</div>
<el-table
@@ -41,15 +43,15 @@
<el-table-column type="index" width="60" />
<el-table-column
prop="Code"
label="Dictionary Type"
:label="$t('dic:all:label:group')"
show-overflow-tooltip
/>
<el-table-column
prop="Description"
label="Description"
:label="$t('dic:all:label:desc')"
show-overflow-tooltip
/>
<el-table-column label="Is Enable" width="100" fixed="right">
<el-table-column :label="$t('dic:all:label:isEnable')" width="100" fixed="right">
<template slot-scope="scope">
<el-switch
v-model="scope.row.IsEnable"
@@ -59,19 +61,19 @@
/>
</template>
</el-table-column>
<el-table-column label="Action" width="200" fixed="right">
<el-table-column :label="$t('common:action:action')" width="200" fixed="right">
<template slot-scope="scope">
<el-button
type="text"
@click="handleEdit(scope.row)"
>
编辑
{{ $t('common:action:edit') }}
</el-button>
<el-button
type="text"
@click="handleDelete(scope.row)"
>
删除
{{ $t('common:button:delete') }}
</el-button>
</template>
</el-table-column>
@@ -115,7 +117,7 @@ export default {
switchChange(event, item) {
this.loading = true
addOrUpdateBasicDic(item).then(res => {
this.$message.success('Saved successfully!')
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.loading = false
}).catch(() => {
this.loading = false
@@ -123,12 +125,12 @@ export default {
},
handleEdit(row) {
this.$nextTick(() => {
this.$refs['DictionaryTypeConfigForm'].openDialog('DictionaryTypeConfig', row)
this.$refs['DictionaryTypeConfigForm'].openDialog(this.$t('common:action:edit'), row)
})
},
handleAdd() {
this.$nextTick(() => {
this.$refs['DictionaryTypeConfigForm'].openDialog('DictionaryTypeConfig', {})
this.$refs['DictionaryTypeConfigForm'].openDialog(this.$t('common:button:new'), {})
})
},
handleDelete(row) {
@@ -0,0 +1,194 @@
<template>
<el-form ref="sysAgreementFrom" v-loading="loading" :model="form" label-width="140px" size="small" :rules="rules"
class="upload-temporary-file">
<div class="base-dialog-body">
<el-row>
<el-col :span="12">
<el-form-item :label="$t('dictionary:agreement:table:UserAgreementTypeEnum')" prop="UserAgreementTypeEnum"
style="width: 48%">
<el-select v-model="form.UserAgreementTypeEnum" style="width: 100%" size="small" filterable>
<el-option v-for="item of $d.UserAgreementType" :key="item.id" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="$t('dictionary:agreement:table:FileName')" prop="FileName" style="width: 48%">
<el-input v-model="form.FileName" clearable style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item :label="$t('dictionary:agreement:table:FileVersion')" prop="FileVersion" style="width: 48%">
<el-input v-model="form.FileVersion" clearable style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item :label="$t('dictionary:agreement:table:UpdateDate')" prop="UpdateDate" style="width: 48%">
<el-date-picker v-model="form.UpdateDate" type="date"
:placeholder="$t('trials:seletctedReviews:table:selectionTime')" value-format="yyyy-MM-dd"
format="yyyy-MM-dd" clearable style="width: 100%;">
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item :label="$t('dictionary:agreement:table:EffectiveDate')" prop="EffectiveDate" style="width: 48%">
<el-date-picker v-model="form.EffectiveDate" type="date"
:placeholder="$t('trials:seletctedReviews:table:selectionTime')" value-format="yyyy-MM-dd"
format="yyyy-MM-dd" clearable style="width: 100%;">
</el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-form-item :label="$t('dictionary:agreement:table:FileContent')" prop="FileContent">
<div class="html_temp">
<prism-editor class="my-editor" v-model="form.FileContent" :highlight="highlighter" :line-numbers="true"
style="width: 50%;max-height: 350px;"></prism-editor>
<div v-html="form.FileContent" style="width: 45%;" class="content"></div>
</div>
</el-form-item>
<el-form-item :label="$t('dictionary:agreement:table:FileContentEn')" prop="FileEnContent">
<div class="html_temp">
<prism-editor class="my-editor" v-model="form.FileEnContent" :highlight="highlighter" :line-numbers="true"
style="width: 50%;max-height: 350px;"></prism-editor>
<div v-html="form.FileEnContent" style="width: 45%;" class="content"></div>
</div>
</el-form-item>
</div>
<div class="base-dialog-footer" style="text-align: right; margin-top: 10px">
<el-form-item style="text-align: right">
<el-button size="small" type="primary" :loading="saveBtnLoading" @click="handleSave">{{ $t('common:button:save')
}}</el-button>
</el-form-item>
</div>
</el-form>
</template>
<script>
import {
addOrUpdateUserAgreement,
} from '@/api/dictionary'
import { PrismEditor } from 'vue-prism-editor';
import 'vue-prism-editor/dist/prismeditor.min.css';
import { highlight, languages } from 'prismjs/components/prism-core';
import 'prismjs/components/prism-clike';
import 'prismjs/components/prism-javascript';
import 'prismjs/themes/prism.css';
export default {
name: 'TemplateForm',
props: {
data: {
type: Object,
default() {
return {}
},
},
},
components: { PrismEditor },
data() {
return {
form: {
Id: '',
UserAgreementTypeEnum: '',
FileName: '',
FileVersion: null,
FileContent: '\n\n\n\n',
FileEnContent: `\n\n\n\n`,
UpdateDate: '',
EffectiveDate: '',
IsCurrentVersion: false
},
rules: {
UserAgreementTypeEnum: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur'] },
],
FileName: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: ['blur'] },
],
FileVersion: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: ['blur'] },
],
FileContent: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: ['blur'] },
],
FileEnContent: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: ['blur'] },
],
UpdateDate: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur'] },
],
EffectiveDate: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur'] },
],
},
saveBtnLoading: false,
loading: false,
dictionaryList: {},
}
},
mounted() {
this.initForm()
},
methods: {
highlighter(code) {
return highlight(code, languages.js);
},
async initForm() {
if (Object.keys(this.data).length > 0) {
for (const k in this.form) {
if (this.data.hasOwnProperty(k)) {
this.form[k] = this.data[k]
}
}
}
},
handleSave() {
this.$refs.sysAgreementFrom.validate((valid) => {
if (!valid) return
this.saveBtnLoading = true
addOrUpdateUserAgreement(this.form)
.then((res) => {
this.saveBtnLoading = false
this.$emit('closeDialog')
this.$emit('getList')
this.$message.success(this.$t('common:message:savedSuccessfully'))
})
.catch(() => {
this.saveBtnLoading = false
})
})
},
},
}
</script>
<style lang="scss" scoped>
.html_temp {
display: flex;
justify-content: space-between;
width: 100%;
.my-editor {
border: 1px solid #333;
}
}
.base-dialog-body {
max-height: calc(100% - 60px);
}
.upload-temporary-file {
overflow-y: auto;
height: 100%;
}
.content {
max-height: 350px;
overflow-y: auto;
}
</style>
@@ -0,0 +1,313 @@
<template>
<BaseContainer>
<template slot="search-container">
<el-form :inline="true" size="small">
<!-- 文件类型 -->
<el-form-item :label="$t('dictionary:agreement:table:UserAgreementTypeEnum')">
<el-select v-model="searchData.UserAgreementTypeEnum" style="width: 150px">
<el-option v-for="item of $d.UserAgreementType" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- 文件名称 -->
<el-form-item :label="$t('dictionary:agreement:table:FileName')">
<el-input v-model="searchData.FileName" style="width: 130px" clearable />
</el-form-item>
<!-- 版本 -->
<el-form-item :label="$t('dictionary:agreement:table:FileVersion')">
<el-input v-model="searchData.FileVersion" style="width: 130px" clearable />
</el-form-item>
<!--更新日期-->
<el-form-item :label="$t('dictionary:agreement:table:UpdateDate')">
<el-date-picker v-model="UpdateDate" @change="changeTimeList('UpdateDate')" value-format="yyyy-MM-dd"
type="daterange">
</el-date-picker>
</el-form-item>
<!--生效日期-->
<el-form-item :label="$t('dictionary:agreement:table:EffectiveDate')">
<el-date-picker v-model="EffectiveDate" @change="changeTimeList('EffectiveDate')" value-format="yyyy-MM-dd"
type="daterange">
</el-date-picker>
</el-form-item>
<!-- 当前版本 -->
<el-form-item :label="$t('dictionary:agreement:table:IsCurrentVersion')">
<el-select v-model="searchData.IsCurrentVersion" style="width: 150px">
<el-option v-for="item of $d.YesOrNo" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item>
<!-- 查询 -->
<el-button type="primary" icon="el-icon-search" @click="handleSearch">
{{ $t('common:button:search') }}
</el-button>
<!-- 重置 -->
<el-button type="primary" icon="el-icon-refresh-left" @click="handleReset">
{{ $t('common:button:reset') }}
</el-button>
</el-form-item>
<el-button type="primary" style="float: right" size="small" @click="handleAdd">
{{ $t('common:button:new') }}
</el-button>
</el-form>
</template>
<template slot="main-container">
<el-table v-adaptive="{ bottomOffset: 60 }" v-loading="loading" :data="list" stripe height="100"
@sort-change="handleSortChange">
<el-table-column type="index" width="40" />
<!-- 文件类型 -->
<el-table-column prop="UserAgreementTypeEnum" :label="$t('dictionary:agreement:table:UserAgreementTypeEnum')"
show-overflow-tooltip sortable="custom">
<template slot-scope="scope">
{{
$fd(
'UserAgreementType',
scope.row.UserAgreementTypeEnum
)
}}
</template>
</el-table-column>
<!-- 文件名称 -->
<el-table-column prop="FileName" :label="$t('dictionary:agreement:table:FileName')" show-overflow-tooltip
sortable="custom" />
<!-- 版本 -->
<el-table-column prop="FileVersion" :label="$t('dictionary:agreement:table:FileVersion')" show-overflow-tooltip
sortable="custom" />
<!--内容-->
<el-table-column prop="FileContent" :label="$t('dictionary:agreement:table:FileContent')">
<template slot-scope="scope">
<el-button type="text" size="small" @click.stop="view(scope.row)">{{ $t('dictionary:agreement:button:view')
}}</el-button>
</template>
</el-table-column>
<!-- 更新日期 -->
<el-table-column prop="UpdateDate" :label="$t('dictionary:agreement:table:UpdateDate')" show-overflow-tooltip
sortable="custom">
<template slot-scope="scope">
<span>{{ scope.row.UpdateDate ? scope.row.UpdateDate.split(' ')[0] : '' }}</span>
</template>
</el-table-column>
<!-- 生效日期 -->
<el-table-column prop="EffectiveDate" :label="$t('dictionary:agreement:table:EffectiveDate')"
show-overflow-tooltip sortable="custom">
<template slot-scope="scope">
<span>{{ scope.row.EffectiveDate ? scope.row.EffectiveDate.split(' ')[0] : '' }}</span>
</template>
</el-table-column>
<!-- 当前版本 -->
<el-table-column prop="IsCurrentVersion" :label="$t('dictionary:agreement:table:IsCurrentVersion')"
show-overflow-tooltip sortable="custom">
<template slot-scope="scope">
{{
$fd(
'YesOrNo',
scope.row.IsCurrentVersion
)
}}
</template>
</el-table-column>
<!-- 创建日期 -->
<el-table-column prop="CreateTime" :label="$t('dictionary:agreement:table:CreateTime')" show-overflow-tooltip
sortable="custom" />
<!-- 修改日期 -->
<el-table-column prop="UpdateTime" :label="$t('dictionary:agreement:table:UpdateTime')" show-overflow-tooltip
sortable="custom" />
<el-table-column :label="$t('common:action:action')" width="300">
<template slot-scope="scope">
<el-button type="text" :disabled="scope.row.IsCurrentVersion" @click="setCurrentVersion(scope.row)">
{{ $t('dictionary:agreement:button:setCurrentVersion') }}
</el-button>
<el-button type="text" @click="handleEdit(scope.row)">
{{ $t('common:button:edit') }}
</el-button>
<!-- <el-button type="text" @click="handleDelete(scope.row)">
{{ $t('common:button:delete') }}
</el-button> -->
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination class="page" :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize"
@pagination="getList" />
<!-- 新增/编辑 -->
<el-dialog v-if="editDialog.visible" :visible.sync="editDialog.visible" :close-on-click-modal="false"
:title="editDialog.title" custom-class="base-dialog-wrapper" :fullscreen="true">
<TemplateForm :data="currentRow" @closeDialog="closeDialog" @getList="getList" />
</el-dialog>
</template>
</BaseContainer>
</template>
<script>
import {
getUserAgreementList,
deleteUserAgreement,
setCurrentVersion
} from '@/api/dictionary'
import BaseContainer from '@/components/BaseContainer'
import Pagination from '@/components/Pagination'
import TemplateForm from './TemplateForm'
const searchDataDefault = () => {
return {
StartEffectiveDate: null,
EndEffectiveDate: null,
FileName: null,
FileVersion: null,
IsCurrentVersion: null,
StartUpdateDate: null,
EndUpdateDate: null,
UserAgreementTypeEnum: null,
Asc: false,
SortField: "",
PageIndex: 1,
PageSize: 20,
}
}
export default {
name: 'Agreement',
components: { BaseContainer, Pagination, TemplateForm },
data() {
return {
searchData: searchDataDefault(),
list: [],
total: 0,
currentRow: {},
editDialog: { title: '', visible: false },
loading: false,
UpdateDate: [],
EffectiveDate: []
}
},
mounted() {
this.getList()
},
computed: {
isEN() {
return this.$i18n.locale !== 'zh'
},
},
methods: {
async setCurrentVersion(row) {
try {
let data = {
Id: row.Id
}
this.loading = true
let res = await setCurrentVersion(data)
this.loading = false
if (res.IsSuccess) {
this.getList()
}
} catch (err) {
this.loading = false
console.log(err)
}
},
view(row) {
this.$AGR({
Id: row.Id,
IsEn_Us: this.isEN
})
},
changeTimeList(key) {
if (this[key]) {
this.searchData[`Start${key}`] = this[key][0]
this.searchData[`End${key}`] = this[key][1]
} else {
this.searchData.BeginCreateTime = null
this.searchData.EndCreateTime = null
}
},
handleDelete(row) {
//
this.$confirm(this.$t('dictionary:agreement:message:msg1')).then(() => {
deleteUserAgreement(row.Id).then(() => {
this.$message.success(this.$t('common:message:deletedSuccessfully'))
this.getList()
})
}).catch(() => { })
},
PreviewFile(row) {
let basePath = window.location.origin
if (window.location.protocol !== 'https:') {
basePath = 'https://irc.test.extimaging.com'
}
let data = {
name: row.NameCN,
path: basePath + row.Path,
}
this.$emit('PreviewFile', data)
},
getList() {
this.loading = true
getUserAgreementList(this.searchData)
.then((res) => {
this.loading = false
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
})
.catch(() => {
this.loading = false
})
},
//
handleAdd() {
this.editDialog.title = this.$t('common:button:new')
this.currentRow = {}
this.editDialog.visible = true
},
//
async handleDownload(row) {
try {
this.loading = true
let fileName = this.isEN ? row.Name : row.NameCN;
let type = fileName
.substring(fileName.lastIndexOf('.'))
.toLocaleLowerCase()
if (!type) {
let extendName = row.Path
.substring(row.Path.lastIndexOf('.'))
.toLocaleLowerCase()
fileName += extendName
}
let res = await downLoadFile(this.OSSclientConfig.basePath + row.Path, fileName)
this.loading = false
} catch (err) {
this.loading = false
}
},
//
handleEdit(row) {
this.editDialog.title = this.$t('common:button:edit')
this.currentRow = { ...row }
this.editDialog.visible = true
},
handleSearch() {
this.searchData.PageIndex = 1
this.getList()
},
handleReset() {
this.searchData = searchDataDefault()
this.getList()
},
closeDialog() {
this.editDialog.visible = false
},
//
handleSortChange(column) {
if (column.order === 'ascending') {
this.searchData.Asc = true
} else {
this.searchData.Asc = false
}
this.searchData.SortField = column.prop
this.searchData.PageIndex = 1
this.getList()
},
},
}
</script>
<style lang="scss" scoped>
::v-deep .search {
display: block;
}
</style>
+34 -13
View File
@@ -6,12 +6,13 @@
</template>
<script>
import { getExploreRecommentInfo } from "@/api/dictionary";
import SystemInfo from "@/utils/systemInfo";
export default {
name: "browserTip",
data() {
return {
form: {},
tip: this.$t("browser:tip:changeBorwser"),
tip: '',
visible: false,
};
},
@@ -36,6 +37,7 @@ export default {
// console.log(type, No);
if (type !== "Chrome" && type !== "Edge") {
this.tip = this.$t("browser:tip:changeBorwser");
// this.getSystemInfo()
return (this.visible = true);
}
let res = await this.getInfo();
@@ -51,12 +53,30 @@ export default {
this.tip += "、";
}
});
// this.getSystemInfo()
return (this.visible = true);
}
// this.getSystemInfo()
} catch (err) {
console.log(err);
}
},
getSystemInfo() {
const systemInfo = new SystemInfo();
const allInfo = systemInfo.getAllInfo();
let deviceMemory = allInfo.hardware.deviceMemory; //
let { width, height } = allInfo.screen; //
// let discrete = allInfo.webgl.gpuType.discrete; //
// let estimatedMemory = allInfo.webgl.memoryInfo.estimatedMemory; //
// parseFloat(deviceMemory) < 16 ||
if (width < 1920 || height < 1080) {
if (this.tip) {
this.tip += `<br/>`
}
this.tip += `<span>${this.$t("browser:tip:Configuration")}</span>`
this.visible = true
}
},
getExplore() {
var Sys = {};
var ua = navigator.userAgent.toLowerCase();
@@ -64,18 +84,18 @@ export default {
(s = ua.match(/rv:([\d.]+)\) like gecko/))
? (Sys.ie = s[1])
: (s = ua.match(/msie ([\d\.]+)/))
? (Sys.ie = s[1])
: (s = ua.match(/edg\/([\d\.]+)/) || ua.match(/edge\/([\d\.]+)/))
? (Sys.edge = s[1])
: (s = ua.match(/firefox\/([\d\.]+)/))
? (Sys.firefox = s[1])
: (s = ua.match(/(?:opera|opr).([\d\.]+)/))
? (Sys.opera = s[1])
: (s = ua.match(/chrome\/([\d\.]+)/))
? (Sys.chrome = s[1])
: (s = ua.match(/version\/([\d\.]+).*safari/))
? (Sys.safari = s[1])
: 0;
? (Sys.ie = s[1])
: (s = ua.match(/edg\/([\d\.]+)/) || ua.match(/edge\/([\d\.]+)/))
? (Sys.edge = s[1])
: (s = ua.match(/firefox\/([\d\.]+)/))
? (Sys.firefox = s[1])
: (s = ua.match(/(?:opera|opr).([\d\.]+)/))
? (Sys.opera = s[1])
: (s = ua.match(/chrome\/([\d\.]+)/))
? (Sys.chrome = s[1])
: (s = ua.match(/version\/([\d\.]+).*safari/))
? (Sys.safari = s[1])
: 0;
//
if (Sys.ie) return "IE: " + Sys.ie;
if (Sys.edge) return "Edge: " + Sys.edge;
@@ -100,6 +120,7 @@ export default {
align-items: center;
padding: 0 20px;
color: #555;
i {
color: red;
font-size: 24px;

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