Compare commits

...

285 Commits

Author SHA1 Message Date
  zhaowei f1dcf328a6 merge dev-zw 9 months ago
  zhaowei c64f0910b3 Merge branch 'dev-zw' of https://gitlink.org.cn/ci4s/ci4sManagement-cloud into dev-zw 9 months ago
  zhaowei 7e27418804 别的页面之间跳转到首页 9 months ago
  zhaowei 7644ab134f feat: 首页切换背景 9 months ago
  cp3hnu 6db85a07b8 Merge pull request '合并' (#283) from dev-zw-notification into dev-zw 9 months ago
  zhaowei 122843610f fix: 修复系统管理不能滚动 9 months ago
  zhaowei 65f53d7d6a feat: 添加首页动效 9 months ago
  zhaowei ad440d455c feat: 添加首页动效 9 months ago
  zhaowei c9af8494ff feat: 云际实验参数 9 months ago
  zhaowei db8280fa13 fix: 修复流水线模板数据集、模型、镜像删除时,数据还存在 9 months ago
  zhaowei 55005ba795 feat: 修改云际登录用户名和密码 9 months ago
  zhaowei 29bc60670b chore: 解决冲突忘记保存了 9 months ago
  cp3hnu 58d4740bda Merge pull request '合并' (#279) from dev-zw-home into dev-zw 9 months ago
  zhaowei b18f69e17a chore: merge dev-zw 9 months ago
  cp3hnu 02bc18770c Merge pull request '合并' (#278) from dev-zw-components into dev-zw 9 months ago
  zhaowei 8b04b30ba2 Merge branch 'dev-zw-components' of https://gitlink.org.cn/ci4s/ci4sManagement-cloud into dev-zw-components 9 months ago
  zhaowei e6a5f77333 chore: 优化ts类型 9 months ago
  zhaowei 971399a79a style: 修改标题的字体 9 months ago
  zhaowei 056a3d67e2 chore: merge dev-zw-components 9 months ago
  zhaowei cde1d4c083 feat: 首页改版 9 months ago
  zhaowei 70d70f2c08 chore: proxy忘记保存了 9 months ago
  zhaowei f9953cf5b5 feat: 合并模型、数据集版本自增 10 months ago
  zhaowei d0f50a944e chore: 修改/.gitignore 9 months ago
  zhaowei 7e744d4a16 feat: 调整系统的计算资源hook 9 months ago
  zhaowei 2692d54b2d feat: 封装云际组件 10 months ago
  cp3hnu 670922438a Merge pull request '合并到dev-zw' (#273) from dev-zw-components into dev-zw 10 months ago
  zhaowei 7251927ccb feat: 模型、数据集版本自增 10 months ago
  zhaowei 1d3c41f62c feat: 集成qiankun 10 months ago
  zhaowei 7b65ee0847 style: 创建实验表单label和控件居中对齐 10 months ago
  zhaowei e59ff1eae3 feat: 创建服务时,模型数据调整 10 months ago
  zhaowei 7b0f26a767 feat: 实验适配新组件模板 10 months ago
  zhaowei 11c0c9ee4d fix: 编辑流水线时,传所有参数 10 months ago
  zhaowei 67a1e8b17b feat: 首页接口联调 10 months ago
  zhaowei 746297a3e0 fix: 修复没有打开过drawer,无法获取全局参数的问题 10 months ago
  zhaowei c0781e31bb feat: 流水线组件调整 10 months ago
  zhaowei 9c4ead5ea3 feat: 首页资源统计接口联调 10 months ago
  zhaowei de3d98b1e1 feat: 修改代码配置 10 months ago
  zhaowei 55f86f3e77 feat: 流水线组件重构 10 months ago
  zhaowei 8d41ff43a1 fix: FormList重名验证优化 10 months ago
  zhaowei 654ade05f7 feat: 修改调整地址 10 months ago
  zhaowei 75b59f6ffe feat: 项目里跳转到首页调整 10 months ago
  zhaowei 24ca5ffa4c feat: 完成首页 10 months ago
  zhaowei f1c7d0ad15 fix:注释掉修改头像 11 months ago
  zhaowei 431903b1de feat: 领域知识图谱菜单移动到多形态资源库 11 months ago
  zhaowei 4220ad1323 feat: 调整用户头像的大小 11 months ago
  zhaowei 6f9354ddc0 feat: 重新设计个人中心 11 months ago
  zhaowei 03136fe4c2 feat: 重新设计个人中心 11 months ago
  zhaowei 4fe48f99d1 feat: app 启动优化 11 months ago
  zhaowei 8fda69e19a feat: 开发环境添加代码配置 11 months ago
  zhaowei 3a3f98d0f1 feat: 代码选择分页数组 11 months ago
  zhaowei e24f5c4876 feat: 代码选择分页数量为18 11 months ago
  zhaowei 29cfff97ec feat: 代码选择分页数量为21 11 months ago
  zhaowei cff33a8e62 feat: 代码选择分页数量为20 11 months ago
  zhaowei 2fdbf62b2b feat: 代码配置选择回显选中的 11 months ago
  zhaowei 5fb95438bd feat: 修复token失效之后,重新登录,创建表单成功后返回到登录界面的问题 11 months ago
  zhaowei 8f015d4d3f feat: 添加缺失值填充 11 months ago
  zhaowei 08a1d46102 feat: 数据集 & 模型可以编辑版本描述 11 months ago
  zhaowei af78389c11 chore: 修改自动机器学习特征预处理算法中文描述 11 months ago
  zhaowei 3518879e12 feat: 选择模型添加版本描述 11 months ago
  zhaowei e91aade6af feat: 修改使用指南 1 year ago
  zhaowei b11b4d9f78 fix: 数据集、模型版本不能是origin 1 year ago
  zhaowei e8af394523 fix: 超参数寻优添加可视化对比iframe 1 year ago
  zhaowei eafca94b60 fix: 导出到数据集添加is_public参数 1 year ago
  zhaowei cd4071149b fix: 导出到数据集添加owner参数 1 year ago
  zhaowei f11582bc64 fix: 数据集和模型回退时分页没有设置 1 year ago
  zhaowei fddb63d293 fix: 流水线模板配置参数修改,历史实验实例配置参数变换 1 year ago
  zhaowei fd7f0008c8 Merge branch 'dev-check' into dev-zw 1 year ago
  zhaowei 531beedac6 chore: merge 1 year ago
  zhaowei 0e8efbb692 fix: 工作空间添加代码配置和服务数量 1 year ago
  chenzhihang 742be0bb03 Merge branch 'dev-check' of https://gitlink.org.cn/ci4s/ci4sManagement-cloud into dev-check 1 year ago
  zhaowei 512aa61b05 fix: 调整工作空间快速开始按钮的偏移 1 year ago
  chenzhihang b6273098c6 新增代码,服务统计 1 year ago
  zhaowei 921bc1d49d fix: 调整工作空间样式 1 year ago
  zhaowei 78eb6ee12f fix: 修复控制参数转成object的问题 1 year ago
  cp3hnu 57a0ec1040 Merge pull request '合并' (#258) from dev-zw into dev-check 1 year ago
  cp3hnu 154c834223 Merge pull request '合并' (#257) from dev-zw into dev-check 1 year ago
  zhaowei 560ff3411c fix: 实验隐藏流水线控制参数 1 year ago
  zhaowei bd269cffbf fix: 流水线隐藏参数 1 year ago
  cp3hnu 6646aff405 Merge pull request '合并' (#256) from dev-zw into dev-check 1 year ago
  zhaowei 1b497e1743 fix: 自动机器学习添加算法描述 1 year ago
  cp3hnu ff69531b22 Merge pull request '合并' (#255) from dev-zw into dev-check 1 year ago
  chenzhihang 399d611607 优化用户 1 year ago
  chenzhihang 8e024c9813 优化用户 1 year ago
  chenzhihang 5c9f59887c 优化实验 1 year ago
  chenzhihang b3e5eb08e9 优化实验 1 year ago
  chenzhihang 6efe25a3f0 Merge branch 'dev-check' of https://gitlink.org.cn/ci4s/ci4sManagement-cloud into dev-check 1 year ago
  chenzhihang 548016091e 优化实验 1 year ago
  cp3hnu 5d61ade863 Merge pull request '合并' (#254) from dev-zw into dev-check 1 year ago
  zhaowei 458621fe91 fix: 自主机器学习改为自动机器学习 1 year ago
  chenzhihang 55b49047c3 Merge branch 'dev-check' of https://gitlink.org.cn/ci4s/ci4sManagement-cloud into dev-check 1 year ago
  chenzhihang 597e16f81f 优化实验 1 year ago
  zhaowei aef75f4a37 fix: 修改角色 1 year ago
  cp3hnu 153b7de3ec Merge pull request '合并' (#253) from dev-zw into dev-check 1 year ago
  chenzhihang 4f415719f8 优化主动学习 1 year ago
  chenzhihang aa410acdfe 优化 1 year ago
  chenzhihang 4252f1710d 优化项目分页查询 1 year ago
  chenzhihang 23af257179 优化项目分页查询 1 year ago
  chenzhihang f0b70feca7 优化项目分页查询 1 year ago
  chenzhihang b014c9ce92 优化项目分页查询 1 year ago
  chenzhihang 976fb1dce5 优化项目分页查询 1 year ago
  chenzhihang 3a5845b623 优化项目分页查询 1 year ago
  chenzhihang b811bb51cd 优化项目分页查询 1 year ago
  chenzhihang 90c958b974 优化项目分页查询 1 year ago
  chenzhihang 19a4d6aed3 优化项目分页查询 1 year ago
  chenzhihang 6e27e5da0d 优化项目分页查询 1 year ago
  chenzhihang 3d0ea6603f 优化项目分页查询 1 year ago
  chenzhihang dc7e8dc801 优化项目分页查询 1 year ago
  chenzhihang 8e1f5fc587 优化 1 year ago
  chenzhihang 746314e5d2 优化 1 year ago
  chenzhihang d169d3d9db 优化 1 year ago
  chenzhihang 7a939f3f29 优化 1 year ago
  chenzhihang dc6cf41651 Merge branch 'dev-check' of https://gitlink.org.cn/ci4s/ci4sManagement-cloud into dev-check 1 year ago
  chenzhihang 17c1c86043 测试 1 year ago
  cp3hnu a46a948760 Merge pull request '合并' (#252) from dev-zw into dev-check 1 year ago
  zhaowei 40ca029363 fix:自动机器学习添加mlp算法 1 year ago
  zhaowei 767a208732 fix:工作空间实验运行时长动态变化 1 year ago
  cp3hnu b314476ac3 Merge pull request '合并' (#251) from dev-zw into dev-check 1 year ago
  chenzhihang 39c0c2c01a 优化 1 year ago
  chenzhihang 873dd0ed5e Merge branch 'dev-check' of https://gitlink.org.cn/ci4s/ci4sManagement-cloud into dev-check 1 year ago
  chenzhihang 2a101d7f47 优化 1 year ago
  cp3hnu bcff86269b Merge pull request '合并' (#250) from dev-zw into dev-check 1 year ago
  zhaowei b2b74686ca fix: 自动机器学习创建时间改为更新时间 1 year ago
  chenzhihang 316cca31a4 优化排序 1 year ago
  zhaowei d1c41934b0 fix: 预测有两个loading 1 year ago
  zhaowei 274b8612e9 fix: 流水线模型部署服务版本验证 1 year ago
  chenzhihang df7460a01b Merge branch 'dev-check' of https://gitlink.org.cn/ci4s/ci4sManagement-cloud into dev-check 1 year ago
  chenzhihang 48d46a0723 优化用户 1 year ago
  zhaowei 8f72953683 fix: 服务日志样式错误 1 year ago
  cp3hnu 62bd8049ba Merge pull request '合并' (#249) from dev-zw into dev-check 1 year ago
  zhaowei 1ec43a60cf fix: 添加预测加载状态 1 year ago
  cp3hnu c42cf77939 Merge pull request '合并' (#248) from dev-zw into dev-check 1 year ago
  chenzhihang 85c8a3e6dc Merge branch 'dev-check' of https://gitlink.org.cn/ci4s/ci4sManagement-cloud into dev-check 1 year ago
  chenzhihang 96bf3351db 优化积分扣除结束 1 year ago
  cp3hnu b2ac5877d0 Merge pull request '合并' (#247) from dev-zw into dev-check 1 year ago
  zhaowei 0bace90a23 fix: 全局参数删除脱敏的配置 1 year ago
  cp3hnu 4c73d4339d Merge branch 'dev-zw' of code.gitlink.org.cn:ci4s/ci4sManagement-cloud into dev-zw 1 year ago
  cp3hnu ef9a78b167 fix: 最近更新时间 1 year ago
  chenzhihang 68ad21fadf 优化 1 year ago
  chenzhihang 696f939295 优化 1 year ago
  chenzhihang cc98a699d4 优化 1 year ago
  chenzhihang c11c728c71 Merge branch 'dev-check' of https://gitlink.org.cn/ci4s/ci4sManagement-cloud into dev-check 1 year ago
  chenzhihang 31334c3a11 优化 1 year ago
  cp3hnu 3066853aeb Merge pull request '合并' (#246) from dev-zw into dev-check 1 year ago
  zhaowei f3f9846dff fix: 模型指标对比图错误 1 year ago
  cp3hnu 7715ee272d Merge pull request '合并' (#245) from dev-zw into dev-check 1 year ago
  zhaowei 65c588ac8a fix: 集成模型数量>=1 1 year ago
  zhaowei 34e2b8bb05 fix: mlp 显示成tablenet 1 year ago
  chenzhihang ff07d4c4a8 优化用户 1 year ago
  chenzhihang 6ad1910cb0 Merge branch 'dev-check' of https://gitlink.org.cn/ci4s/ci4sManagement-cloud into dev-check 1 year ago
  chenzhihang 879c2e5802 优化更新ray 1 year ago
  chenzhihang 50dce4003f 优化用户 1 year ago
  cp3hnu e4385544b1 Merge pull request '合并dev-zw' (#244) from dev-zw into dev-check 1 year ago
  cp3hnu 4a6f4d2120 fix: 退出登录获取label-studio地址 1 year ago
  cp3hnu 8ef236f5b4 feat: 修改服务调用指南 1 year ago
  chenzhihang a5914e67c8 优化 1 year ago
  chenzhihang cccb2ae8c2 测试登录 1 year ago
  chenzhihang e5978fde0c 测试登录 1 year ago
  chenzhihang e76a0d7544 测试登录 1 year ago
  chenzhihang d19f0cfa82 优化 1 year ago
  chenzhihang d3508e6eba Merge branch 'dev-check' of https://gitlink.org.cn/ci4s/ci4sManagement-cloud into dev-check 1 year ago
  chenzhihang b42a63f209 优化 1 year ago
  cp3hnu 603b943699 Merge pull request '合并dev' (#243) from dev into dev-check 1 year ago
  cp3hnu c8fc258089 Merge pull request '合并dev-zw' (#242) from dev-zw into dev 1 year ago
  cp3hnu 53fe983462 fix: 用户管理界面无法退出登录 1 year ago
  chenzhihang 1b63a74ce0 优化 1 year ago
  chenzhihang c4a3275358 优化 1 year ago
  chenzhihang fc026e9d15 优化 1 year ago
  chenzhihang 9d3e0ad5f3 Merge branch 'dev-check' of https://gitlink.org.cn/ci4s/ci4sManagement-cloud into dev-check 1 year ago
  chenzhihang a2555f6ac3 优化 1 year ago
  cp3hnu fefc545dad Merge pull request '合并dev' (#241) from dev into dev-check 1 year ago
  cp3hnu b419ab9485 Merge pull request '合并dev-zw' (#240) from dev-zw into dev 1 year ago
  cp3hnu 16d4b476f2 fix: 分配用户创建时间为null 1 year ago
  cp3hnu b8049721df fix: 退出登录两次 1 year ago
  chenzhihang 272ec6ef97 优化 1 year ago
  chenzhihang 46eff25e01 优化 1 year ago
  chenzhihang dcc591cacd Merge branch 'dev-check' of https://gitlink.org.cn/ci4s/ci4sManagement-cloud into dev-check 1 year ago
  chenzhihang 4da7042f61 优化 1 year ago
  chenzhihang 236ac4da0f 优化 1 year ago
  cp3hnu 2b0c11525b test: 添加 giturl 的测试 1 year ago
  cp3hnu 67a2b41240 Merge pull request '合并dev' (#239) from dev into dev-check 1 year ago
  cp3hnu 7de2295191 Merge pull request '合并dev-zw' (#238) from dev-zw into dev 1 year ago
  cp3hnu 0ac624ed2a fix: 实验无法查看更多 1 year ago
  chenzhihang 277ed0a710 优化积分扣除结束 1 year ago
  chenzhihang 2aaa8a9fe7 Merge branch 'dev-check' of https://gitlink.org.cn/ci4s/ci4sManagement-cloud into dev-check 1 year ago
  chenzhihang 19b797189d 优化积分扣除 1 year ago
  cp3hnu 2d5538251e Merge pull request '合并dev' (#237) from dev into dev-check 1 year ago
  cp3hnu b142d5985d Merge pull request '合并dev-zw' (#236) from dev-zw into dev 1 year ago
  cp3hnu 14a9629167 fix: 添加iframe 加载失败日志 1 year ago
  chenzhihang 48fdf61dff 优化积分扣除 1 year ago
  chenzhihang 48851a2d4b 优化积分查询 1 year ago
  cp3hnu 5fe010f52d fix: 服务只有运行中才显示预测 1 year ago
  cp3hnu e6f74dc513 Merge pull request '合并dev' (#235) from dev into dev-check 1 year ago
  cp3hnu e4ffcea914 Merge pull request '合并dev' (#234) from dev-zw into dev 1 year ago
  cp3hnu 44673f39ed fix: 实验状态不同步 1 year ago
  chenzhihang 88227a85cb 优化运行开发环境 1 year ago
  chenzhihang a0d17e6dd3 优化查询pod状态 1 year ago
  chenzhihang adf3b8d02a 优化查询pod状态 1 year ago
  chenzhihang 596aa80315 优化查询pod状态 1 year ago
  chenzhihang 7b146651c7 优化创建pod 1 year ago
  chenzhihang eb50e76a54 优化实验状态查询 1 year ago
  chenzhihang a2f1c0532b 优化dvc 1 year ago
  chenzhihang 694f142b3f 优化积分扣除,优化dvc 1 year ago
  chenzhihang cdceefcb24 优化实验状态查询 1 year ago
  chenzhihang 1cfbd5185c 优化积分扣除 1 year ago
  chenzhihang bee9f43762 优化 1 year ago
  chenzhihang aa3909a047 优化 1 year ago
  chenzhihang 111ade2f49 优化项目排序 1 year ago
  chenzhihang 6670aa0658 优化项目排序 1 year ago
  chenzhihang 6748c46d6f 优化项目排序 1 year ago
  chenzhihang d6ed84ac59 Merge branch 'dev-check' of https://gitlink.org.cn/ci4s/ci4sManagement-cloud into dev-check 1 year ago
  chenzhihang 5a7a188478 优化积分更新 1 year ago
  cp3hnu 5d641cb61b Merge pull request '合并dev' (#233) from dev into dev-check 1 year ago
  chenzhihang 6809f62b9c 优化 1 year ago
  cp3hnu 8110739002 Merge pull request '合并dev-zw' (#232) from dev-zw into dev 1 year ago
  cp3hnu 1fd40f927c fix: 用户账号支持4-15位 1 year ago
  chenzhihang b02d15662f Merge branch 'dev-check' of https://gitlink.org.cn/ci4s/ci4sManagement-cloud into dev-check 1 year ago
  chenzhihang 20b1e78df7 优化 1 year ago
  cp3hnu d7ddcefb96 fix: 自动机器学习创建时间有误 1 year ago
  cp3hnu 1f08241ca9 Merge pull request '合并dev' (#231) from dev into dev-check 1 year ago
  cp3hnu b4873208c2 Merge pull request '合并dev-zw' (#230) from dev-zw into dev 1 year ago
  chenzhihang 25381c26ae 优化 1 year ago
  chenzhihang 567263b11a Merge remote-tracking branch 'origin/dev' into dev-check 1 year ago
  chenzhihang cdb3fafd14 Merge branch 'dev' of https://gitlink.org.cn/ci4s/ci4sManagement-cloud into dev 1 year ago
  chenzhihang 25dec31eaa 优化 1 year ago
  cp3hnu 5b8a10006d feat: 删除机器人 1 year ago
  zhaowei b0d8a19975 feat: 快速开始删除开发环境 1 year ago
  cp3hnu f5d4158532 Merge pull request '合并dev' (#229) from dev into dev-check 1 year ago
  cp3hnu dcb73eacf9 Merge pull request '合并dev-zw' (#228) from dev-zw into dev 1 year ago
  fanshuai 84dbb1e8fb 修改BUG 【开发环境】新创建开发环境处于列表底部,未处于列表前列 1 year ago
  cp3hnu 95cfbefda4 feat: 自动机器学习运行&获取tensorboard状态 1 year ago
  fanshuai ae86b2a835 Merge remote-tracking branch 'origin/dev-check' into dev-check 1 year ago
  fanshuai 68ee173591 修改BUG 【工作空间】AI资产卡片数据统计错误 1 year ago
  chenzhihang 71edeb6922 优化状态更新 1 year ago
  chenzhihang a4b82fb9f5 优化状态更新 1 year ago
  cp3hnu 9a89988e95 fix: 删除“构建中”状态镜像版本,构建成功/失败状态返回后重新显示在列表 1 year ago
  cp3hnu b9f4c48ea6 fix: 位于大于筛选结果的页码,点击左侧边栏筛选,页面提示暂无数据 1 year ago
  chenzhihang 8270406d3c 优化http请求 1 year ago
  chenzhihang 870fbce684 优化查询代码配置bug 1 year ago
  cp3hnu 7a4852908b fix: 添加镜像版本描述 1 year ago
  chenzhihang ce0d898af8 优化积分扣除 1 year ago
  chenzhihang b5fd8eb031 Merge branch 'dev-check' of https://gitlink.org.cn/ci4s/ci4sManagement-cloud into dev-check 1 year ago
  chenzhihang b9b0db8442 优化异常提示 1 year ago
  chenzhihang a2cf8fe75e 优化 1 year ago
  cp3hnu 6bb5a9e8e6 Merge pull request '合并dev' (#227) from dev into dev-check 1 year ago
  cp3hnu aa980985d5 Merge pull request '合并dev-zw' (#226) from dev-zw into dev 1 year ago
  cp3hnu b4d99f8e5c fix: 代码配置30202改为30203 1 year ago
  chenzhihang 5d49e2d1b5 优化镜像版本描述 1 year ago
  chenzhihang 20ce4e4758 优化镜像版本描述 1 year ago
  chenzhihang b994fb3f31 优化 1 year ago
  chenzhihang c0320a2a68 优化 1 year ago
  chenzhihang 41c2faf9cb 优化 1 year ago
  cp3hnu e92ac40694 fix: 列表运行时长和详情运行时长不一致 1 year ago
  chenzhihang 45990fa2b7 优化 1 year ago
  cp3hnu 9c62812424 fix: 列表运行时长和详情运行时长不一致 1 year ago
  chenzhihang d1928d702b 优化 1 year ago
  chenzhihang eea826de2c 优化构建镜像 1 year ago
  chenzhihang 55fd9d7271 优化代码配置 1 year ago
  chenzhihang 3905841db7 优化状态更新 1 year ago
  chenzhihang 0a7be4e261 优化结束时间 1 year ago
  chenzhihang 47f2c80a00 Merge branch 'dev-check' of https://gitlink.org.cn/ci4s/ci4sManagement-cloud into dev-check 1 year ago
  chenzhihang 9def5a4d7a 优化状态更新 1 year ago
  cp3hnu 7fc016c3a0 Merge pull request '合并dev' (#225) from dev into dev-check 1 year ago
  cp3hnu 260812125a Merge pull request '合并dev-zw' (#224) from dev-zw into dev 1 year ago
  cp3hnu 22f85fb3ae fix: 实验取流水线节点 1 year ago
  chenzhihang 765f37ab7c 优化主动学习 1 year ago
  chenzhihang 57296d364e 优化主动学习 1 year ago
  chenzhihang 04a9f8f125 优化主动学习 1 year ago
  chenzhihang 640c49f507 优化 1 year ago
  chenzhihang 85c92e8fae 优化主动学习 1 year ago
  chenzhihang 7b51ed5bc6 优化主动学习 1 year ago
  chenzhihang b78b0075cd 优化主动学习 1 year ago
  chenzhihang e4dd26c87d 优化 1 year ago
  chenzhihang f5f3aca3e5 Merge branch 'dev-check' of https://gitlink.org.cn/ci4s/ci4sManagement-cloud into dev-check 1 year ago
  chenzhihang 5aa9cdc62f 优化 1 year ago
  cp3hnu 5d60751a66 feat: 内嵌tesorboard 1 year ago
  cp3hnu 0e54f6e95d Merge pull request '合并dev' (#223) from dev into dev-check 1 year ago
  cp3hnu bc18fb14c2 Merge pull request '合并dev-zw' (#222) from dev-zw into dev 1 year ago
  cp3hnu c103375c8c fix: 自动机器学习日志一片空白 1 year ago
  cp3hnu ebd1d8680a fix: 全选时可以选中运行的实验实例 1 year ago
  chenzhihang ac95c975a5 Merge branch 'dev-check' of https://gitlink.org.cn/ci4s/ci4sManagement-cloud into dev-check 1 year ago
  chenzhihang c16c9d75a3 Merge remote-tracking branch 'origin/dev' into dev-check 1 year ago
  cp3hnu b6f0ffbfe1 style: 调整样式 1 year ago
  cp3hnu 039ad81fc7 Merge pull request '合并dev' (#221) from dev into dev-check 1 year ago
  cp3hnu e553a21c2f Merge pull request '合并dev-zw' (#220) from dev-zw into dev 1 year ago
  zhaowei c719141676 feat: 验收 1 year ago
100 changed files with 3300 additions and 1282 deletions
Split View
  1. +1
    -0
      .gitignore
  2. +12
    -1
      k8s/template-yaml/k8s-5auth.yaml
  3. +17
    -0
      react-ui/config/config.ts
  4. +4
    -4
      react-ui/config/proxy.ts
  5. +75
    -16
      react-ui/config/routes.ts
  6. +1294
    -0
      react-ui/mock/components.ts
  7. +600
    -0
      react-ui/mock/home.ts
  8. +0
    -115
      react-ui/mock/notices.ts
  9. +0
    -324
      react-ui/mock/requestRecord.mock.js
  10. +0
    -203
      react-ui/mock/user.ts
  11. +5
    -3
      react-ui/package.json
  12. BIN
      react-ui/public/assets/材料科研软件平台使用文档-v1.0.pdf
  13. BIN
      react-ui/public/assets/材料科研软件平台使用文档.pdf
  14. BIN
      react-ui/public/fonts/WenYiHei.ttf
  15. BIN
      react-ui/public/fonts/YouSheBiaoTiHei.ttf
  16. +14
    -1
      react-ui/public/fonts/font.css
  17. +1
    -1
      react-ui/public/mockServiceWorker.js
  18. +52
    -0
      react-ui/public/scripts/resize-breakpoint.js
  19. +44
    -0
      react-ui/public/scripts/resize.js
  20. +45
    -53
      react-ui/src/app.tsx
  21. BIN
      react-ui/src/assets/img/home/app-logo-dark.png
  22. BIN
      react-ui/src/assets/img/home/app-logo.png
  23. BIN
      react-ui/src/assets/img/home/code-arrow.png
  24. BIN
      react-ui/src/assets/img/home/code-bg.png
  25. BIN
      react-ui/src/assets/img/home/code-item-bg-hover.png
  26. BIN
      react-ui/src/assets/img/home/code-item-bg.png
  27. BIN
      react-ui/src/assets/img/home/code.png
  28. BIN
      react-ui/src/assets/img/home/dataset-arrow-right.png
  29. BIN
      react-ui/src/assets/img/home/dataset-bg.png
  30. BIN
      react-ui/src/assets/img/home/dataset-item-bg.png
  31. BIN
      react-ui/src/assets/img/home/dataset.png
  32. BIN
      react-ui/src/assets/img/home/default-avatar.png
  33. BIN
      react-ui/src/assets/img/home/footer-bg.png
  34. BIN
      react-ui/src/assets/img/home/header-bg-mini.png
  35. BIN
      react-ui/src/assets/img/home/header-bg.png
  36. BIN
      react-ui/src/assets/img/home/image.png
  37. BIN
      react-ui/src/assets/img/home/mirror-arrow.png
  38. BIN
      react-ui/src/assets/img/home/mirror-version.png
  39. BIN
      react-ui/src/assets/img/home/model-between-dataset.png
  40. BIN
      react-ui/src/assets/img/home/model-bg.png
  41. BIN
      react-ui/src/assets/img/home/model-item-bg-hover.png
  42. BIN
      react-ui/src/assets/img/home/model-item-bg.png
  43. BIN
      react-ui/src/assets/img/home/model-item-hot-hover.png
  44. BIN
      react-ui/src/assets/img/home/model-item-hot.png
  45. BIN
      react-ui/src/assets/img/home/model.png
  46. BIN
      react-ui/src/assets/img/home/right-arrow-hover.png
  47. BIN
      react-ui/src/assets/img/home/right-arrow.png
  48. BIN
      react-ui/src/assets/img/home/service-bg.png
  49. BIN
      react-ui/src/assets/img/home/service.png
  50. BIN
      react-ui/src/assets/img/home/service1.png
  51. BIN
      react-ui/src/assets/img/home/service2.png
  52. BIN
      react-ui/src/assets/img/home/service3.png
  53. BIN
      react-ui/src/assets/img/home/service4.png
  54. BIN
      react-ui/src/assets/img/home/statistics-bg.png
  55. BIN
      react-ui/src/assets/img/home/timestamp.png
  56. BIN
      react-ui/src/assets/img/home/user-avatar-big.png
  57. BIN
      react-ui/src/assets/img/home/user-avatar.png
  58. BIN
      react-ui/src/assets/img/home/路径 17816@2x (1).png
  59. BIN
      react-ui/src/assets/img/home/路径 17816@2x.png
  60. +22
    -3
      react-ui/src/components/CodeConfigItem/index.less
  61. +38
    -11
      react-ui/src/components/CodeConfigItem/index.tsx
  62. +10
    -16
      react-ui/src/components/CodeSelect/index.tsx
  63. +1
    -0
      react-ui/src/components/CodeSelectorModal/index.less
  64. +84
    -17
      react-ui/src/components/CodeSelectorModal/index.tsx
  65. +2
    -1
      react-ui/src/components/ErrorBoundary/index.tsx
  66. +20
    -0
      react-ui/src/components/FormInfo/index.tsx
  67. +45
    -14
      react-ui/src/components/IFramePage/index.tsx
  68. +4
    -1
      react-ui/src/components/ParameterInput/index.tsx
  69. +83
    -39
      react-ui/src/components/ParameterSelect/config.tsx
  70. +103
    -41
      react-ui/src/components/ParameterSelect/index.tsx
  71. +29
    -44
      react-ui/src/components/ResourceSelect/index.tsx
  72. +28
    -17
      react-ui/src/components/ResourceSelectorModal/config.tsx
  73. +16
    -1
      react-ui/src/components/ResourceSelectorModal/index.less
  74. +48
    -24
      react-ui/src/components/ResourceSelectorModal/index.tsx
  75. +44
    -21
      react-ui/src/components/RightContent/AvatarDropdown.tsx
  76. +10
    -1
      react-ui/src/components/RunDuration/index.tsx
  77. +11
    -3
      react-ui/src/enums/index.ts
  78. +2
    -0
      react-ui/src/enums/pagesEnums.ts
  79. +26
    -0
      react-ui/src/global.tsx
  80. +71
    -47
      react-ui/src/hooks/useComputingResource.ts
  81. +22
    -10
      react-ui/src/hooks/useSSE.ts
  82. +21
    -24
      react-ui/src/overrides.less
  83. +2
    -1
      react-ui/src/pages/404.tsx
  84. +8
    -10
      react-ui/src/pages/ActiveLearn/Instance/index.tsx
  85. +7
    -11
      react-ui/src/pages/ActiveLearn/components/ActiveLearnBasic/index.tsx
  86. +14
    -4
      react-ui/src/pages/ActiveLearn/components/CreateForm/ExecuteConfig.tsx
  87. +4
    -0
      react-ui/src/pages/ActiveLearn/components/CreateForm/utils.ts
  88. +7
    -2
      react-ui/src/pages/ActiveLearn/components/ExperimentLog/index.tsx
  89. +6
    -1
      react-ui/src/pages/Authorize/index.tsx
  90. +11
    -9
      react-ui/src/pages/AutoML/Instance/index.tsx
  91. +1
    -1
      react-ui/src/pages/AutoML/List/index.tsx
  92. +46
    -11
      react-ui/src/pages/AutoML/components/AutoMLBasic/index.tsx
  93. +6
    -67
      react-ui/src/pages/AutoML/components/CreateForm/ExecuteConfig.tsx
  94. +85
    -0
      react-ui/src/pages/AutoML/components/CreateForm/utils.ts
  95. +0
    -4
      react-ui/src/pages/AutoML/components/ExperimentInstanceList/index.less
  96. +16
    -8
      react-ui/src/pages/AutoML/components/ExperimentInstanceList/index.tsx
  97. +35
    -32
      react-ui/src/pages/AutoML/components/ExperimentInstanceList/instance.tsx
  98. +14
    -3
      react-ui/src/pages/AutoML/components/ExperimentList/config.ts
  99. +127
    -62
      react-ui/src/pages/AutoML/components/ExperimentList/index.tsx
  100. +7
    -0
      react-ui/src/pages/AutoML/components/ExperimentLog/empty.tsx

+ 1
- 0
.gitignore View File

@@ -65,3 +65,4 @@ mvnw
/react-ui/types/tsconfig.tsbuildinfo
/react-ui/storybook-static
/react-ui/.storybook/scripts
/react-ui/dist.zip

+ 12
- 1
k8s/template-yaml/k8s-5auth.yaml View File

@@ -18,6 +18,11 @@ spec:
image: ${k8s-5auth-image}
ports:
- containerPort: 9200
env:
- name: TZ
value: Asia/Shanghai
- name: JAVA_TOOL_OPTIONS
value: "-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=*:5005"

---
apiVersion: v1
@@ -28,9 +33,15 @@ metadata:
spec:
type: NodePort
ports:
- port: 9200
- name: http
port: 9200
nodePort: 31206
protocol: TCP
- name: debug
nodePort: 31221
port: 5005
protocol: TCP
targetPort: 5005
selector:
app: ci4s-auth


+ 17
- 0
react-ui/config/config.ts View File

@@ -127,6 +127,8 @@ export default defineConfig({
headScripts: [
// 解决首次加载时白屏的问题
{ src: '/scripts/loading.js', async: true },
{ src: '/scripts/resize.js', async: true },
// { src: '/scripts/resize-breakpoint.js', async: true },
],
// links: [
// {
@@ -157,4 +159,19 @@ export default defineConfig({
javascriptEnabled: true,
},
valtio: {},
qiankun: {
master: {
sandbox: true,
apps: [
{
name: 'app1',
entry: '//localhost:7001',
},
{
name: 'app2',
entry: '//localhost:3000',
},
],
},
},
});

+ 4
- 4
react-ui/config/proxy.ts View File

@@ -20,14 +20,14 @@ export default {
// localhost:8000/api/** -> https://preview.pro.ant.design/api/**
'/api/': {
// 要代理的地址
// target: 'http://172.20.32.197:31213', // 开发环境
target: 'http://172.20.32.197:31213', // 开发环境
// target: 'http://172.20.32.235:31213', // 测试环境
target: 'http://172.20.32.44:8082',
// target: 'http://172.20.32.150:8082',
// target: 'http://172.20.32.127:8082',
// target: 'http://172.20.32.164:8082',
// 配置了这个可以从 http 代理到 https
// 依赖 origin 的功能可能需要这个,比如 cookie
changeOrigin: true,
pathRewrite: { '^/api': '' },
// pathRewrite: { '^/api': '' },
},
'/profile/avatar/': {
target: 'http://172.20.32.235:31213',


+ 75
- 16
react-ui/config/routes.ts View File

@@ -13,7 +13,20 @@
export default [
{
path: '/',
redirect: '/workspace',
redirect: '/home',
},
{
name: '首页',
path: '/home',
layout: false,
routes: [
{
name: '首页',
path: '',
key: 'home',
component: './Home/index',
},
],
},
{
name: '工作空间',
@@ -141,12 +154,23 @@ export default [
{
name: '实验对比',
path: 'compare',
component: './Experiment/Comparison/index',
routes: [
{
name: '实验对比',
path: '',
component: './Experiment/Comparison/index',
},
{
name: '可视化对比',
path: 'compare-visual',
component: './Experiment/Aim/index',
},
],
},
{
name: '实验可视化对比',
path: 'compare-visual',
component: './Experiment/Aim/index',
name: '可视化',
path: 'visual',
component: './Experiment/Tensorboard/index',
},
],
},
@@ -218,7 +242,18 @@ export default [
{
name: '实验实例详情',
path: 'instance/:experimentId/:id',
component: './HyperParameter/Instance/index',
routes: [
{
name: '实验实例详情',
path: '',
component: './HyperParameter/Instance/index',
},
{
name: '可视化对比',
path: 'compare-visual',
component: './HyperParameter/Aim/index',
},
],
},
],
},
@@ -395,6 +430,18 @@ export default [
},
],
},
{
name: '知识图谱',
path: 'knowledge',
routes: [
{
name: '知识图谱',
path: '',
key: 'knowledge',
component: './Knowledge/index',
},
],
},
],
},
{
@@ -550,29 +597,41 @@ export default [
],
},
{
name: '算力积分',
path: '/points',
name: 'mixed',
path: '/mixed',
routes: [
{
name: '算力积分',
name: '父子页面混合',
path: '',
key: 'points',
component: './Points/index',
key: 'mixed',
component: './Mixed/index',
},
],
},
{
name: '知识图谱',
path: '/knowledge',
name: '算力积分',
path: '/points',
routes: [
{
name: '知识图谱',
name: '算力积分',
path: '',
key: 'knowledge',
component: './Knowledge/index',
key: 'points',
component: './Points/index',
},
],
},
{
path: '/app1/*',
name: '子应用1',
microApp: 'app1',
layout: true,
},
{
path: '/app2/*',
name: '子应用2',
microApp: 'app2',
layout: true,
},
{
path: '*',
layout: false,


+ 1294
- 0
react-ui/mock/components.ts
File diff suppressed because it is too large
View File


+ 600
- 0
react-ui/mock/home.ts View File

@@ -0,0 +1,600 @@
import { defineMock } from 'umi';

export default defineMock({
'GET /api/mmp/workspace/getPublicDatasets': {
msg: '操作成功',
code: 200,
data: {
content: [
{
name: 'R1蒸馏模型数学推理能力测试集',
identifier: 'public_dataset_20250519163052',
description:
'共728道数学推理题目,包括:\nMATH-500:一组具有挑战性的高中数学竞赛问题数据集,涵盖七个科目(如初等代数、代数、数论)共500道题。\nGPQA-Diamond:该数据集包含物理、化学和生物学子领域的硕士水平多项选择题,共198道题。\nAIME-2024:美国邀请数学竞赛的数据集,包含30道数学题。',
is_public: true,
time_ago: '2个月前',
full_last_update_time: '2025-06-23T14:36:48.000+08:00',
id: 91,
visits: 1,
praises_count: 1,
create_by: 'fanshuai',
owner: 'fanshuai',
},
{
name: '有机化学VLM',
identifier: 'public_dataset_20250527113008',
description: 'Dataset Card for "Chemistry_text_to_image"',
is_public: true,
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:06:32.000+08:00',
id: 134,
visits: 2,
praises_count: 1,
create_by: 'fanshuai',
owner: 'fanshuai',
},
{
name: 'OQMD 开源量子材料数据集',
identifier: 'public_dataset_20250527141950',
description:
'QMD 包含了通过密度泛函理论 (DFT) 计算得到的超过 1,226,781 种材料的热力学和结构性质。数据库中的数据来源于无机晶体结构数据库 (ICSD),包括了近 300,000 种化合物的 DFT 总能量计算以及常见晶体结构的修饰',
is_public: true,
time_ago: '2个月前',
full_last_update_time: '2025-06-23T14:38:43.000+08:00',
id: 136,
visits: 5,
praises_count: 1,
create_by: 'fanshuai',
owner: 'fanshuai',
},
{
name: '不可降解和可生物降解的材料数据集',
identifier: 'public_dataset_20250527142930',
description:
'此数据集包含大约 256K 图像(156K 原始数据),代表两类:可生物降解和不可生物降解。\n可生物降解,包含可被微生物自然分解的材料,如食物、植物、水果等。这种材料的废物可以加工成堆肥。\n不可生物降解,包含无法自然分解的材料,例如塑料、金属、无机元素等。这种材料的废料将被回收成新材料。',
is_public: true,
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:06:11.000+08:00',
id: 137,
visits: 2,
praises_count: 1,
create_by: 'fanshuai',
owner: 'fanshuai',
},
{
name: '金属有机框架材料预测',
identifier: 'public_dataset_20250527143028',
description:
'金属有机框架 (MOF) 是一类通过金属离子(或金属簇)和有机配体之间的配位键连接的结晶材料。MOF 材料具有多孔结构、高度可调和巨大的比表面积,使其在吸附、储气、分离、催化等领域具有广泛的应用潜力。预测合成是指通过计算机模拟和机器学习方法对新型 MOF 材料的合成路线和条件进行预测和设计。\n',
is_public: true,
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:06:01.000+08:00',
id: 138,
visits: 2,
praises_count: 1,
create_by: 'fanshuai',
owner: 'fanshuai',
},
{
name: '纤维增强复合材料的弹性特性',
identifier: 'public_dataset_20250527144649',
description:
'纤维增强复合材料弹性特性数据集主要包含其力学性能参数,如弹性模量(纵向、横向)、剪切模量、泊松比以及应力-应变关系等。数据通常通过实验测试(拉伸、压缩、弯曲试验)或计算模拟(有限元分析、细观力学模型)获得,涵盖不同纤维类型(碳纤维、玻璃纤维、芳纶等)、基体材料(环氧树脂、热塑性塑料等)及铺层方式(单向、编织、多轴向)的组合。',
is_public: true,
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:05:24.000+08:00',
id: 142,
visits: 2,
praises_count: 1,
create_by: 'fanshuai',
owner: 'fanshuai',
},
{
name: 'OCR 合成材料',
identifier: 'public_dataset_20250527144821',
description:
'OCR(光学字符识别)合成材料数据集是用于训练和评估文本识别模型的专用数据集,主要包含人工生成的文本图像,模拟真实场景中的材料标签、说明书、包装文字等。这类数据集通常涵盖多种字体、背景、光照条件、扭曲变形及噪声干扰,以提高模型鲁棒性。数据可能包含金属、塑料、复合材料等工业材料的名称、参数(如成分、规格、批次号)及安全标识',
is_public: true,
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:05:18.000+08:00',
id: 143,
visits: 2,
praises_count: 1,
create_by: 'fanshuai',
owner: 'fanshuai',
},
{
name: '钙钛矿稳定性',
identifier: 'public_dataset_20250527145953',
description:
'这个钙钛矿稳定性数据集给出了潜在钙钛矿材料成分相对于用 DFT 计算的凸包的能量。钙钛矿数据集还包括包含钙钛矿结构中 A 位点、B 位点和 X 位点信息的列,以便对数据进行更高级的分组。',
is_public: true,
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:04:30.000+08:00',
id: 148,
visits: 2,
praises_count: 1,
create_by: 'fanshuai',
owner: 'fanshuai',
},
{
name: '纳米颗粒毒性数据集',
identifier: 'public_dataset_20250527150856',
description:
'该数据集是一个毒性数据集,由几列组成,捕获了纳米颗粒 (NPs) 的各种属性及其毒理学影响。该数据集包含与纳米颗粒 (NPs) 及其特性相关的各种特征,这些特征可能与毒性分类有关',
is_public: true,
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:04:41.000+08:00',
id: 149,
visits: 2,
praises_count: 1,
create_by: 'fanshuai',
owner: 'fanshuai',
},
{
name: '3D多模态医疗数据集-分割-fanshuai',
identifier: 'public_dataset_20250519151852',
description: '大规模通用 3D 医疗图像分割数据集 (M3D-Seg)',
is_public: true,
time_ago: '3个月前',
full_last_update_time: '2025-05-22T10:20:52.000+08:00',
id: 82,
visits: 0,
praises_count: 0,
create_by: 'fanshuai',
owner: 'fanshuai',
},
{
name: '中文基于满血DeepSeek-R1蒸馏数据集',
identifier: 'public_dataset_20250519161406',
description:
'注意:提供了直接SFT使用的版本。将数据中的思考和答案整合成output字段,大部分SFT代码框架均可直接直接加载训练。\n本数据集为中文开源蒸馏满血R1的数据集,数据集中不仅包含math数据,还包括大量的通用类型数据,总数量为110K。\n为什么开源这个数据?\nR1的效果十分强大,并且基于R1蒸馏数据SFT的小模型也展现出了强大的效果,但检索发现,大部分开源的R1蒸馏数据集均为英文数',
is_public: true,
time_ago: '3个月前',
full_last_update_time: '2025-05-19T16:14:06.000+08:00',
id: 88,
visits: 0,
praises_count: 0,
create_by: 'fanshuai',
owner: 'fanshuai',
},
{
name: '中文Text2SQL数据集',
identifier: 'public_dataset_20250519165142',
description:
'同时包含用于训练和测试表格问答预训练模型的数据,数据集包含500条训练数据和100条测试数据。\n表格问答预训练模型的训练和测试数据,支持中文,支持通用领域的表格问答。另外,也可以从本model card中,点击数据集文件panel,然后点击数据文件选项,即可下载trian.zip和test.zip文件',
is_public: true,
time_ago: '3个月前',
full_last_update_time: '2025-05-19T16:51:43.000+08:00',
id: 93,
visits: 0,
praises_count: 0,
create_by: 'fanshuai',
owner: 'fanshuai',
},
{
name: 'MatPES',
identifier: 'public_dataset_20250521090336',
description:
'使用元素周期表几乎完全覆盖的势能面数据集来训练基础 电位 (FP),即机器学习原子间电位 (MLIP),几乎完全覆盖了周期性 桌子。MatPES 是材料虚拟实验室和材料项目的一项倡议,旨在解决此类材料 PES 数据集中的关键缺陷。',
is_public: true,
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:10:21.000+08:00',
id: 100,
visits: 2,
praises_count: 0,
create_by: 'fanshuai',
owner: 'fanshuai',
},
{
name: 'innovation_contest/innov202305100905418',
identifier: 'public_dataset_20250526093119',
description:
'1. 赛题解读PPT;2.根据流动状态分开的数据集,方便选手测试自己模型的变状态泛化性能\n新的数据中输入输出与均在一个文件夹中\n新的数据集中,模型文件简称对应的状态如下:\nCBFS 曲线后台阶 雷诺数Re=13700\nCDN 收缩扩张管道 Re=12600\nduct 方管 Re在文件名中包含,比如duct_Re1100.csv代表Re=1100\nperhill 周期山 Re=5600,文件名后面',
is_public: true,
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:08:27.000+08:00',
id: 122,
visits: 3,
praises_count: 0,
create_by: 'fanshuai',
owner: 'fanshuai',
},
{
name: 'WIDERFace',
identifier: 'public_dataset_20250526094839',
description:
'32,203张图像,并对393,703张像样本图像中所描述的在尺度、姿势和遮挡方面具有高度可变性的面孔进行标记。较宽的人脸数据集基于61个事件类进行组织。',
is_public: true,
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:08:18.000+08:00',
id: 123,
visits: 2,
praises_count: 0,
create_by: 'fanshuai',
owner: 'fanshuai',
},
{
name: 'StanfordSentimentTreebank',
identifier: 'public_dataset_20250526095521',
description:
'用于情感分析的数据集,其中包含11855个句子的语法分析树中215154个短语的细粒度情感标签,并为情感组成提出了新挑战。',
is_public: true,
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:08:12.000+08:00',
id: 124,
visits: 2,
praises_count: 0,
create_by: 'fanshuai',
owner: 'fanshuai',
},
{
name: 'COCO',
identifier: 'public_dataset_20250526100341',
description:
'COCO是大规模的对象检测,分割和字幕数据集。 它包含:330K图像(标为> 200K),150万个对象实例,80个对象类别。',
is_public: true,
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:07:56.000+08:00',
id: 125,
visits: 2,
praises_count: 0,
create_by: 'fanshuai',
owner: 'fanshuai',
},
{
name: 'BillionWords',
identifier: 'public_dataset_20250526101006',
description: '该项目的目的是为语言建模实验提供标准的培训和测试设置,包含10亿字。',
is_public: true,
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:07:47.000+08:00',
id: 126,
visits: 2,
praises_count: 0,
create_by: 'fanshuai',
owner: 'fanshuai',
},
{
name: 'car_ims',
identifier: 'public_dataset_20250527084401',
description:
'斯坦福汽车数据集包含196类汽车的16,185张图像。数据被分为8,144个训练图像和8,041个测试图像,其中每个类别已大致分为50-50个分割。',
is_public: true,
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:07:20.000+08:00',
id: 128,
visits: 2,
praises_count: 0,
create_by: 'fanshuai',
owner: 'fanshuai',
},
{
name: 'IU-xrays',
identifier: 'public_dataset_20250527093542',
description: '放射图像',
is_public: true,
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:07:12.000+08:00',
id: 129,
visits: 2,
praises_count: 0,
create_by: 'fanshuai',
owner: 'fanshuai',
},
],
pageable: {
sort: {
sorted: false,
unsorted: true,
empty: true,
},
pageNumber: 0,
pageSize: 20,
offset: 0,
unpaged: false,
paged: true,
},
last: false,
totalElements: 39,
totalPages: 2,
first: true,
number: 0,
sort: {
sorted: false,
unsorted: true,
empty: true,
},
numberOfElements: 20,
size: 20,
empty: false,
},
},
'GET /api/mmp/workspace/getPublicModels': {
msg: '操作成功',
code: 200,
data: {
content: [
{
id: 109,
name: '介电',
create_by: 'ceshi',
description: '介电材料模型',
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:09:54.000+08:00',
owner: 'ceshi',
identifier: 'public_model_20250522110231',
is_public: true,
praises_count: 2,
},
{
id: 156,
name: 'ChatGLM2-6B',
create_by: 'fanshuai',
description:
'ChatGLM2-6B 是开源中英双语对话模型 ChatGLM-6B 的第二代版本,在保留了初代模型对话流畅、部署门槛较低等众多优秀特性的基础之上',
time_ago: '2个月前',
full_last_update_time: '2025-06-20T16:09:02.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250528093916',
is_public: true,
praises_count: 1,
},
{
id: 155,
name: '鹏城·脑海(原鹏城·盘古)α-2.6B-CPU',
create_by: 'fanshuai',
description:
'「鹏城·盘古α」由以鹏城实验室为首的技术团队联合攻关,首次基于“鹏城云脑Ⅱ”和国产MindSpore框架的自动混合并行模式实现在2048卡算力集群上的大规模分布式训练,训练出业界首个2000亿参数以中文为核心的预训练生成语言模型。鹏城·盘古α预训练模型支持丰富的场景应用,在知识问答、知识检索、知识推理、阅读理解等文本生成领域表现突出,具备很强的小样本学习能力。',
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:03:36.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250528093254',
is_public: true,
praises_count: 0,
},
{
id: 157,
name: 'ernie-3.0-base-zh',
create_by: 'fanshuai',
description: '大规模知识增强预训练,用于语言理解和生成',
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:03:08.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250528094825',
is_public: true,
praises_count: 0,
},
{
id: 158,
name: 'FastChat-T5',
create_by: 'fanshuai',
description:
'FastChat-T5是一款开源聊天机器人,通过微调Flan-t5-xl (3B参数)并基于从ShareGPT.收集的用户共享对话进行训练。它基于编码器-解码器变压器架构,能够自回归生成对用户输入的响应。',
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:03:19.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250528101831',
is_public: true,
praises_count: 0,
},
{
id: 159,
name: 'Kolors-IP-Adapter-Plus',
create_by: 'fanshuai',
description: '基于Kolors-Basemodel提供了IP-Adapter-Plus的权重和推理代码',
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:03:01.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250528102217',
is_public: true,
praises_count: 0,
},
{
id: 160,
name: 'Florence-2-base',
create_by: 'fanshuai',
description:
'Florence-2是一款先进的视觉基础模型,采用提示式方法处理广泛的视觉和视觉-语言任务。Florence-2能够通过简单的文本提示来执行诸如字幕生成、物体检测和分割等任务。该模型利用了包含54亿个注释的FLD-5B数据集,这些注释覆盖了1.26亿张图像,从而掌握了多任务学习。模型的序列到序列架构使其在零样本和微调设置中表现出色,证明了其作为竞争性视觉基础模型的实力。',
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:02:55.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250528102750',
is_public: true,
praises_count: 0,
},
{
id: 161,
name: 'E5-base',
create_by: 'fanshuai',
description: '弱监督对比预训练的文本嵌入。',
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:02:48.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250528103059',
is_public: true,
praises_count: 0,
},
{
id: 162,
name: 'Mini-InternVL-Chat',
create_by: 'fanshuai',
description:
'使用了与InternVL 1相同的数据来训练这个较小的模型。此外,由于较小模型的训练成本较低,我们在训练时采用了8K的上下文长度。',
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:02:39.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250528105945',
is_public: true,
praises_count: 0,
},
{
id: 163,
name: 'Verdict-Classifier',
create_by: 'fanshuai',
description:
'该模型是基于xlm-roberta-base的微调版本,基于谷歌事实核查工具API提供的2,500条去重多语言判决,并通过谷歌云翻译API转换成65种语言',
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:02:27.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250528110545',
is_public: true,
praises_count: 0,
},
{
id: 164,
name: 'Text2Vec-Base-Multilingual',
create_by: 'fanshuai',
description:
'这是一个CoSENT(余弦句子)模型,它将句子映射到一个384维的密集向量空间,并可用于任务,例如句子嵌入、文本匹配或语义搜索。',
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:02:19.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250528110858',
is_public: true,
praises_count: 0,
},
{
id: 167,
name: 'Latex-OCR',
create_by: 'fanshuai',
description: '识别图像中的数学公式并转换为Latex源码。',
time_ago: '3个月前',
full_last_update_time: '2025-05-28T18:02:09.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250528112153',
is_public: true,
praises_count: 0,
},
{
id: 169,
name: 'XLNet',
create_by: 'fanshuai',
description:
'XLNet是一种基于新型广义置换语言建模目标的新型无监督语言表示学习方法。此外,XLNet采用Transformer-XL作为骨干模型,在处理长上下文的语言任务中表现出色。总体而言,XLNet在包括问答、自然语言推理、情感分析和文档排序在内的多种下游语言任务中取得了最先进的(SOTA)成果。',
time_ago: '3个月前',
full_last_update_time: '2025-05-28T17:29:05.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250528172905',
is_public: true,
praises_count: 0,
},
{
id: 173,
name: ' GTE-base',
create_by: 'fanshuai',
description:
'GTE模型由阿里巴巴达摩学院训练。这些模型主要基于BERT框架,目前提供三种不同规模的版本,分别是GTE-large、GTE-base和GTE-small。GTE模型在大规模的相关文本对语料库上进行训练,涵盖了广泛的领域和场景。这使得GTE模型能够应用于文本嵌入的多种下游任务,如信息检索、语义文本相似性分析、文本重排序等。',
time_ago: '3个月前',
full_last_update_time: '2025-05-29T09:14:15.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250529091415',
is_public: true,
praises_count: 0,
},
{
id: 174,
name: 'Tiny-lm',
create_by: 'fanshuai',
description:
'此仓库提供了一个小型的1600万参数语言模型,该模型基于英文和日文维基百科数据训练。',
time_ago: '3个月前',
full_last_update_time: '2025-05-29T09:18:57.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250529091857',
is_public: true,
praises_count: 0,
},
{
id: 175,
name: "Snowflake's Arctic-embed-s",
create_by: 'fanshuai',
description:
'snowflake-arctic-embed是一套文本嵌入模型,专注于创建高性能的高质量检索模型。',
time_ago: '3个月前',
full_last_update_time: '2025-05-29T09:23:58.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250529092358',
is_public: true,
praises_count: 0,
},
{
id: 176,
name: 'ViTMatte model',
create_by: 'fanshuai',
description:
'ViTMatte是一种简单的图像抠图方法,旨在准确估计图像中的前景物体。该模型由一个Vision Transformer(ViT)和一个轻量级头部组成。',
time_ago: '3个月前',
full_last_update_time: '2025-05-29T09:41:07.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250529094107',
is_public: true,
praises_count: 0,
},
{
id: 177,
name: 'Wartortle',
create_by: 'fanshuai',
description: '此模型专为语义自动补全功能而设计。',
time_ago: '3个月前',
full_last_update_time: '2025-05-29T09:44:43.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250529094443',
is_public: true,
praises_count: 0,
},
{
id: 179,
name: 'Cerebras-GPT',
create_by: 'fanshuai',
description:
'Cerebras-GPT系列的发布旨在通过开放架构和数据集促进对大型语言模型(LLM)扩展规律的研究,并展示在Cerebras软硬件栈上训练LLM的简便性和可扩展性。所有Cerebras-GPT模型均可在Hugging Face.上获取。',
time_ago: '3个月前',
full_last_update_time: '2025-05-29T10:01:52.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250529100152',
is_public: true,
praises_count: 0,
},
{
id: 180,
name: 'Qwen2-1.5B-Instruct-AWQ',
create_by: 'fanshuai',
description:
'Qwen2是Qwen大语言模型系列的最新成员。我们为Qwen2推出了多个基础语言模型和指令调优语言模型,参数规模从0.5亿到72亿不等,其中包括一个专家混合模型。本仓库包含1.5亿参数的指令调优Qwen2模型。',
time_ago: '3个月前',
full_last_update_time: '2025-05-29T10:13:53.000+08:00',
owner: 'fanshuai',
identifier: 'public_model_20250529101353',
is_public: true,
praises_count: 0,
},
],
pageable: {
sort: {
sorted: false,
unsorted: true,
empty: true,
},
pageNumber: 0,
pageSize: 20,
offset: 0,
unpaged: false,
paged: true,
},
last: false,
totalElements: 28,
totalPages: 2,
first: true,
number: 0,
sort: {
sorted: false,
unsorted: true,
empty: true,
},
numberOfElements: 20,
size: 20,
empty: false,
},
},
});

+ 0
- 115
react-ui/mock/notices.ts View File

@@ -1,115 +0,0 @@
import { Request, Response } from 'express';

const getNotices = (req: Request, res: Response) => {
res.json({
data: [
{
id: '000000001',
avatar:
'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/MSbDR4FR2MUAAAAAAAAAAAAAFl94AQBr',
title: '你收到了 14 份新周报',
datetime: '2017-08-09',
type: 'notification',
},
{
id: '000000002',
avatar:
'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/hX-PTavYIq4AAAAAAAAAAAAAFl94AQBr',
title: '你推荐的 曲妮妮 已通过第三轮面试',
datetime: '2017-08-08',
type: 'notification',
},
{
id: '000000003',
avatar:
'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/jHX5R5l3QjQAAAAAAAAAAAAAFl94AQBr',
title: '这种模板可以区分多种通知类型',
datetime: '2017-08-07',
read: true,
type: 'notification',
},
{
id: '000000004',
avatar:
'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/Wr4mQqx6jfwAAAAAAAAAAAAAFl94AQBr',
title: '左侧图标用于区分不同的类型',
datetime: '2017-08-07',
type: 'notification',
},
{
id: '000000005',
avatar:
'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/Mzj_TbcWUj4AAAAAAAAAAAAAFl94AQBr',
title: '内容不要超过两行字,超出时自动截断',
datetime: '2017-08-07',
type: 'notification',
},
{
id: '000000006',
avatar:
'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/eXLzRbPqQE4AAAAAAAAAAAAAFl94AQBr',
title: '曲丽丽 评论了你',
description: '描述信息描述信息描述信息',
datetime: '2017-08-07',
type: 'message',
clickClose: true,
},
{
id: '000000007',
avatar:
'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/w5mRQY2AmEEAAAAAAAAAAAAAFl94AQBr',
title: '朱偏右 回复了你',
description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
datetime: '2017-08-07',
type: 'message',
clickClose: true,
},
{
id: '000000008',
avatar:
'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/wPadR5M9918AAAAAAAAAAAAAFl94AQBr',
title: '标题',
description: '这种模板用于提醒谁与你发生了互动,左侧放『谁』的头像',
datetime: '2017-08-07',
type: 'message',
clickClose: true,
},
{
id: '000000009',
title: '任务名称',
description: '任务需要在 2017-01-12 20:00 前启动',
extra: '未开始',
status: 'todo',
type: 'event',
},
{
id: '000000010',
title: '第三方紧急代码变更',
description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
extra: '马上到期',
status: 'urgent',
type: 'event',
},
{
id: '000000011',
title: '信息安全考试',
description: '指派竹尔于 2017-01-09 前完成更新并发布',
extra: '已耗时 8 天',
status: 'doing',
type: 'event',
},
{
id: '000000012',
title: 'ABCD 版本发布',
description: '冠霖提交于 2017-01-06,需在 2017-01-07 前完成代码变更任务',
extra: '进行中',
status: 'processing',
type: 'event',
},
],
});
};

export default {
'GET /api/notices': getNotices,
};

+ 0
- 324
react-ui/mock/requestRecord.mock.js View File

@@ -1,324 +0,0 @@
module.exports = {
'GET /api/currentUser': {
data: {
name: 'Serati Ma',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png',
userid: '00000001',
email: 'antdesign@alipay.com',
signature: '海纳百川,有容乃大',
title: '交互专家',
group: '蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED',
tags: [
{ key: '0', label: '很有想法的' },
{ key: '1', label: '专注设计' },
{ key: '2', label: '辣~' },
{ key: '3', label: '大长腿' },
{ key: '4', label: '川妹子' },
{ key: '5', label: '海纳百川' },
],
notifyCount: 12,
unreadCount: 11,
country: 'China',
geographic: {
province: { label: '浙江省', key: '330000' },
city: { label: '杭州市', key: '330100' },
},
address: '西湖区工专路 77 号',
phone: '0752-268888888',
},
},
'GET /api/rule': {
data: [
{
key: 99,
disabled: false,
href: 'https://ant.design',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
name: 'TradeCode 99',
owner: '曲丽丽',
desc: '这是一段描述',
callNo: 503,
status: '0',
updatedAt: '2022-12-06T05:00:57.040Z',
createdAt: '2022-12-06T05:00:57.040Z',
progress: 81,
},
{
key: 98,
disabled: false,
href: 'https://ant.design',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
name: 'TradeCode 98',
owner: '曲丽丽',
desc: '这是一段描述',
callNo: 164,
status: '0',
updatedAt: '2022-12-06T05:00:57.040Z',
createdAt: '2022-12-06T05:00:57.040Z',
progress: 12,
},
{
key: 97,
disabled: false,
href: 'https://ant.design',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
name: 'TradeCode 97',
owner: '曲丽丽',
desc: '这是一段描述',
callNo: 174,
status: '1',
updatedAt: '2022-12-06T05:00:57.040Z',
createdAt: '2022-12-06T05:00:57.040Z',
progress: 81,
},
{
key: 96,
disabled: true,
href: 'https://ant.design',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
name: 'TradeCode 96',
owner: '曲丽丽',
desc: '这是一段描述',
callNo: 914,
status: '0',
updatedAt: '2022-12-06T05:00:57.040Z',
createdAt: '2022-12-06T05:00:57.040Z',
progress: 7,
},
{
key: 95,
disabled: false,
href: 'https://ant.design',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
name: 'TradeCode 95',
owner: '曲丽丽',
desc: '这是一段描述',
callNo: 698,
status: '2',
updatedAt: '2022-12-06T05:00:57.040Z',
createdAt: '2022-12-06T05:00:57.040Z',
progress: 82,
},
{
key: 94,
disabled: false,
href: 'https://ant.design',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
name: 'TradeCode 94',
owner: '曲丽丽',
desc: '这是一段描述',
callNo: 488,
status: '1',
updatedAt: '2022-12-06T05:00:57.040Z',
createdAt: '2022-12-06T05:00:57.040Z',
progress: 14,
},
{
key: 93,
disabled: false,
href: 'https://ant.design',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
name: 'TradeCode 93',
owner: '曲丽丽',
desc: '这是一段描述',
callNo: 580,
status: '2',
updatedAt: '2022-12-06T05:00:57.040Z',
createdAt: '2022-12-06T05:00:57.040Z',
progress: 77,
},
{
key: 92,
disabled: false,
href: 'https://ant.design',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
name: 'TradeCode 92',
owner: '曲丽丽',
desc: '这是一段描述',
callNo: 244,
status: '3',
updatedAt: '2022-12-06T05:00:57.040Z',
createdAt: '2022-12-06T05:00:57.040Z',
progress: 58,
},
{
key: 91,
disabled: false,
href: 'https://ant.design',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
name: 'TradeCode 91',
owner: '曲丽丽',
desc: '这是一段描述',
callNo: 959,
status: '0',
updatedAt: '2022-12-06T05:00:57.040Z',
createdAt: '2022-12-06T05:00:57.040Z',
progress: 66,
},
{
key: 90,
disabled: true,
href: 'https://ant.design',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
name: 'TradeCode 90',
owner: '曲丽丽',
desc: '这是一段描述',
callNo: 958,
status: '0',
updatedAt: '2022-12-06T05:00:57.040Z',
createdAt: '2022-12-06T05:00:57.040Z',
progress: 72,
},
{
key: 89,
disabled: false,
href: 'https://ant.design',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
name: 'TradeCode 89',
owner: '曲丽丽',
desc: '这是一段描述',
callNo: 301,
status: '2',
updatedAt: '2022-12-06T05:00:57.040Z',
createdAt: '2022-12-06T05:00:57.040Z',
progress: 2,
},
{
key: 88,
disabled: false,
href: 'https://ant.design',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
name: 'TradeCode 88',
owner: '曲丽丽',
desc: '这是一段描述',
callNo: 277,
status: '1',
updatedAt: '2022-12-06T05:00:57.040Z',
createdAt: '2022-12-06T05:00:57.040Z',
progress: 12,
},
{
key: 87,
disabled: false,
href: 'https://ant.design',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
name: 'TradeCode 87',
owner: '曲丽丽',
desc: '这是一段描述',
callNo: 810,
status: '1',
updatedAt: '2022-12-06T05:00:57.040Z',
createdAt: '2022-12-06T05:00:57.040Z',
progress: 82,
},
{
key: 86,
disabled: false,
href: 'https://ant.design',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
name: 'TradeCode 86',
owner: '曲丽丽',
desc: '这是一段描述',
callNo: 780,
status: '3',
updatedAt: '2022-12-06T05:00:57.040Z',
createdAt: '2022-12-06T05:00:57.040Z',
progress: 22,
},
{
key: 85,
disabled: false,
href: 'https://ant.design',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
name: 'TradeCode 85',
owner: '曲丽丽',
desc: '这是一段描述',
callNo: 705,
status: '3',
updatedAt: '2022-12-06T05:00:57.040Z',
createdAt: '2022-12-06T05:00:57.040Z',
progress: 12,
},
{
key: 84,
disabled: true,
href: 'https://ant.design',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
name: 'TradeCode 84',
owner: '曲丽丽',
desc: '这是一段描述',
callNo: 203,
status: '0',
updatedAt: '2022-12-06T05:00:57.040Z',
createdAt: '2022-12-06T05:00:57.040Z',
progress: 79,
},
{
key: 83,
disabled: false,
href: 'https://ant.design',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
name: 'TradeCode 83',
owner: '曲丽丽',
desc: '这是一段描述',
callNo: 491,
status: '2',
updatedAt: '2022-12-06T05:00:57.040Z',
createdAt: '2022-12-06T05:00:57.040Z',
progress: 59,
},
{
key: 82,
disabled: false,
href: 'https://ant.design',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
name: 'TradeCode 82',
owner: '曲丽丽',
desc: '这是一段描述',
callNo: 73,
status: '0',
updatedAt: '2022-12-06T05:00:57.040Z',
createdAt: '2022-12-06T05:00:57.040Z',
progress: 100,
},
{
key: 81,
disabled: false,
href: 'https://ant.design',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/udxAbMEhpwthVVcjLXik.png',
name: 'TradeCode 81',
owner: '曲丽丽',
desc: '这是一段描述',
callNo: 406,
status: '3',
updatedAt: '2022-12-06T05:00:57.040Z',
createdAt: '2022-12-06T05:00:57.040Z',
progress: 61,
},
{
key: 80,
disabled: false,
href: 'https://ant.design',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/eeHMaZBwmTvLdIwMfBpg.png',
name: 'TradeCode 80',
owner: '曲丽丽',
desc: '这是一段描述',
callNo: 112,
status: '2',
updatedAt: '2022-12-06T05:00:57.040Z',
createdAt: '2022-12-06T05:00:57.040Z',
progress: 20,
},
],
total: 100,
success: true,
pageSize: 20,
current: 1,
},
'POST /api/login/outLogin': { data: {}, success: true },
'POST /api/login/account': {
status: 'ok',
type: 'account',
currentAuthority: 'admin',
},
};

+ 0
- 203
react-ui/mock/user.ts View File

@@ -1,203 +0,0 @@
import { Request, Response } from 'express';

const waitTime = (time: number = 100) => {
return new Promise((resolve) => {
setTimeout(() => {
resolve(true);
}, time);
});
};

async function getFakeCaptcha(req: Request, res: Response) {
await waitTime(2000);
return res.json('captcha-xxx');
}

const { ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION } = process.env;

/**
* 当前用户的权限,如果为空代表没登录
* current user access, if is '', user need login
* 如果是 pro 的预览,默认是有权限的
*/
let access = ANT_DESIGN_PRO_ONLY_DO_NOT_USE_IN_YOUR_PRODUCTION === 'site' ? 'admin' : '';

const getAccess = () => {
return access;
};

// 代码中会兼容本地 service mock 以及部署站点的静态数据
export default {
// 支持值为 Object 和 Array
'GET /api/currentUser': (req: Request, res: Response) => {
if (!getAccess()) {
res.status(401).send({
data: {
isLogin: false,
},
errorCode: '401',
errorMessage: '请先登录!',
success: true,
});
return;
}
res.send({
success: true,
data: {
name: 'Serati Ma',
avatar: 'https://gw.alipayobjects.com/zos/antfincdn/XAosXuNZyF/BiazfanxmamNRoxxVxka.png',
userid: '00000001',
email: 'antdesign@alipay.com',
signature: '海纳百川,有容乃大',
title: '交互专家',
group: '蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED',
tags: [
{
key: '0',
label: '很有想法的',
},
{
key: '1',
label: '专注设计',
},
{
key: '2',
label: '辣~',
},
{
key: '3',
label: '大长腿',
},
{
key: '4',
label: '川妹子',
},
{
key: '5',
label: '海纳百川',
},
],
notifyCount: 12,
unreadCount: 11,
country: 'China',
access: getAccess(),
geographic: {
province: {
label: '浙江省',
key: '330000',
},
city: {
label: '杭州市',
key: '330100',
},
},
address: '西湖区工专路 77 号',
phone: '0752-268888888',
},
});
},
// GET POST 可省略
'GET /api/users': [
{
key: '1',
name: 'John Brown',
age: 32,
address: 'New York No. 1 Lake Park',
},
{
key: '2',
name: 'Jim Green',
age: 42,
address: 'London No. 1 Lake Park',
},
{
key: '3',
name: 'Joe Black',
age: 32,
address: 'Sidney No. 1 Lake Park',
},
],
'POST /api/login/account': async (req: Request, res: Response) => {
const { password, username, type } = req.body;
await waitTime(2000);
if (password === 'ant.design' && username === 'admin') {
res.send({
status: 'ok',
type,
currentAuthority: 'admin',
});
access = 'admin';
return;
}
if (password === 'ant.design' && username === 'user') {
res.send({
status: 'ok',
type,
currentAuthority: 'user',
});
access = 'user';
return;
}
if (type === 'mobile') {
res.send({
status: 'ok',
type,
currentAuthority: 'admin',
});
access = 'admin';
return;
}

res.send({
status: 'error',
type,
currentAuthority: 'guest',
});
access = 'guest';
},
'POST /api/login/outLogin': (req: Request, res: Response) => {
access = '';
res.send({ data: {}, success: true });
},
'POST /api/register': (req: Request, res: Response) => {
res.send({ status: 'ok', currentAuthority: 'user', success: true });
},
'GET /api/500': (req: Request, res: Response) => {
res.status(500).send({
timestamp: 1513932555104,
status: 500,
error: 'error',
message: 'error',
path: '/base/category/list',
});
},
'GET /api/404': (req: Request, res: Response) => {
res.status(404).send({
timestamp: 1513932643431,
status: 404,
error: 'Not Found',
message: 'No message available',
path: '/base/category/list/2121212',
});
},
'GET /api/403': (req: Request, res: Response) => {
res.status(403).send({
timestamp: 1513932555104,
status: 403,
error: 'Forbidden',
message: 'Forbidden',
path: '/base/category/list',
});
},
'GET /api/401': (req: Request, res: Response) => {
res.status(401).send({
timestamp: 1513932555104,
status: 401,
error: 'Unauthorized',
message: 'Unauthorized',
path: '/base/category/list',
});
},

'GET /api/login/captcha': getFakeCaptcha,
};

+ 5
- 3
react-ui/package.json View File

@@ -1,5 +1,5 @@
{
"name": "cl-model",
"name": "ci4s",
"version": "1.0.0",
"private": true,
"description": "",
@@ -8,7 +8,7 @@
"build": "max build",
"deploy": "npm run build && npm run gh-pages",
"dev": "npm run start:dev",
"dev-no-sso": "cross-env NO_SSO=true npm run start:dev",
"dev-no-sso": "cross-env NO_SSO=true npm run start:mock",
"docker-hub:build": "docker build -f Dockerfile.hub -t ant-design-pro ./",
"docker-prod:build": "docker-compose -f ./docker/docker-compose.yml build",
"docker-prod:dev": "docker-compose -f ./docker/docker-compose.yml up",
@@ -35,7 +35,7 @@
"serve": "umi-serve",
"start": "cross-env UMI_ENV=dev max dev",
"start:dev": "cross-env REACT_APP_ENV=dev MOCK=none UMI_ENV=dev UMI_DEV_SERVER_COMPRESS=none max dev",
"start:mock": "cross-env REACT_APP_ENV=dev UMI_ENV=dev max dev",
"start:mock": "cross-env REACT_APP_ENV=dev UMI_ENV=dev UMI_DEV_SERVER_COMPRESS=none max dev",
"start:pre": "cross-env REACT_APP_ENV=pre UMI_ENV=dev max dev",
"start:test": "cross-env REACT_APP_ENV=test MOCK=none UMI_ENV=dev max dev",
"storybook": "storybook dev -p 6006",
@@ -75,6 +75,7 @@
"fabric": "^5.3.0",
"highlight.js": "^11.7.0",
"lodash": "^4.17.21",
"motion": "~12.23.12",
"omit.js": "^2.0.2",
"pnpm": "^8.9.0",
"query-string": "^8.1.0",
@@ -82,6 +83,7 @@
"rc-util": "^5.30.0",
"react": "^18.2.0",
"react-activation": "^0.12.4",
"react-countup": "~6.5.3",
"react-cropper": "^2.3.3",
"react-dev-inspector": "^1.8.1",
"react-dom": "^18.2.0",


BIN
react-ui/public/assets/材料科研软件平台使用文档-v1.0.pdf View File


BIN
react-ui/public/assets/材料科研软件平台使用文档.pdf View File


BIN
react-ui/public/fonts/WenYiHei.ttf View File


BIN
react-ui/public/fonts/YouSheBiaoTiHei.ttf View File


+ 14
- 1
react-ui/public/fonts/font.css View File

@@ -1,6 +1,6 @@
@font-face {
font-family: Alibaba;
src: url('./ALIBABA-PUHUITI-MEDIUM.TTF');
src: url('./ALIBABA-PUHUITI-REGULAR.TTF');
font-display: swap;
}

@@ -10,4 +10,17 @@
url('./DingTalk-JinBuTi.woff') format('woff'), /* 兼容性较好的 woff */
url('./DingTalk-JinBuTi.ttf') format('truetype'); /* ttf 作为备选 */
font-display: swap; /* 优化页面加载时的字体显示 */
}

@font-face {
font-family: 'WenYiHei';
src: url('./WenYiHei.ttf');
font-display: swap; /* 优化页面加载时的字体显示 */
}


@font-face {
font-family: 'YouSheBiaoTiHei';
src: url('./YouSheBiaoTiHei.ttf');
font-display: swap; /* 优化页面加载时的字体显示 */
}

+ 1
- 1
react-ui/public/mockServiceWorker.js View File

@@ -8,7 +8,7 @@
* - Please do NOT serve this file on production.
*/

const PACKAGE_VERSION = '2.7.0'
const PACKAGE_VERSION = '2.7.1'
const INTEGRITY_CHECKSUM = '00729d72e3b82faf54ca8b9621dbb96f'
const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
const activeClientIds = new Set()


+ 52
- 0
react-ui/public/scripts/resize-breakpoint.js View File

@@ -0,0 +1,52 @@
(function (doc, win) {
'use strict';

// 配置项
const config = {
// 断点设置(单位:px)
// 1440 1560 1680 1800 1920 2040 2160 2280 2400 2520
breakpoints: [
{ minWidth: 2520, fontSize: 22 }, // 21
{ minWidth: 2280, fontSize: 20 }, // 19
{ minWidth: 2040, fontSize: 18 }, // 17
{ minWidth: 1800, fontSize: 16 }, // 15
{ minWidth: 1560, fontSize: 14 }, // 13
{ minWidth: 0, fontSize: 12 },
],
delay: 300 // 防抖延迟(ms)
};

const docEl = doc.documentElement;
const resizeEvt = 'orientationchange' in win ? 'orientationchange' : 'resize';
let resizeTimeout;

// 计算当前宽度对应的字体大小
function calculateFontSize() {
const clientWidth = docEl.clientWidth || win.innerWidth;
if (!clientWidth) return;

// 从大到小匹配断点
const targetBreakpoint = config.breakpoints.find(
bp => clientWidth >= bp.minWidth
);

// 设置字体大小
docEl.style.fontSize = targetBreakpoint.fontSize + 'px';

// 调试输出(可选)
console.debug('[REM-Resize]',
'Width:', clientWidth + 'px',
'Font-Size:', targetBreakpoint.fontSize + 'px');
}

// 防抖处理
function handleResize() {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(calculateFontSize, config.delay);
}

calculateFontSize();

// 初始化监听
win.addEventListener(resizeEvt, handleResize, false);
})(document, window);

+ 44
- 0
react-ui/public/scripts/resize.js View File

@@ -0,0 +1,44 @@
// rem-resize.js
(function (doc, win) {
'use strict';

// 配置项
const config = {
designWidth: 1920, // 设计稿宽度
baseFontSize: 16, // 基础字体大小(设计稿下1rem = 16px)
minFontSize: 12, // 最小字体限制
maxFontSize: 24, // 最大字体限制
delay: 300, // 窗口变化时的延迟执行(ms)
};

const docEl = doc.documentElement;
const resizeEvt = 'orientationchange' in win ? 'orientationchange' : 'resize';
let resizeTimeout;

function calculateFontSize() {
const clientWidth = docEl.clientWidth || win.innerWidth;
if (!clientWidth) return;

const fontSize = Math.min(
Math.max((clientWidth / config.designWidth) * config.baseFontSize, config.minFontSize),
config.maxFontSize,
);

docEl.style.fontSize = fontSize + 'px';

// 可选:调试输出
if (win.console) {
console.debug('[REM-Resize]', 'width:', clientWidth, 'font-size:', fontSize + 'px');
}
}

function resizeHandler() {
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(calculateFontSize, config.delay);
}

calculateFontSize();

// 初始化监听
win.addEventListener(resizeEvt, resizeHandler, false);
})(document, window);

+ 45
- 53
react-ui/src/app.tsx View File

@@ -4,6 +4,7 @@ import { type GlobalInitialState } from '@/types';
import { menuItemRender } from '@/utils/menuRender';
import type { Settings as LayoutSettings } from '@ant-design/pro-components';
import { RuntimeConfig, history } from '@umijs/max';
import { useState } from 'react';
import { RuntimeAntdConfig } from 'umi';
import defaultSettings from '../config/defaultSettings';
import '../public/fonts/font.css';
@@ -20,8 +21,10 @@ import {
setRemoteMenu,
} from './services/session';
import './styles/menu.less';
import { needAuth } from './utils';
import { gotoLoginPage } from './utils/ui';
import { isLoginPage, needAuth } from './utils';
import { HomeUrl } from './utils/constant';
import { closeAllModals } from './utils/modal';
import { gotoHomePage } from './utils/ui';
export { requestConfig as request } from './requestConfig';

/**
@@ -29,28 +32,25 @@ export { requestConfig as request } from './requestConfig';
*/
export async function getInitialState(): Promise<GlobalInitialState> {
const fetchUserInfo = async () => {
globalGetSeverTime();
try {
globalGetSeverTime();
const response = await getUserInfo();
return {
...response.user,
avatar: response.user.avatar || require('@/assets/img/avatar-default.png'),
permissions: response.permissions,
roles: response.roles,
roleNames: response.user.roles,
roleNames: response.roles,
} as API.CurrentUser;
} catch (error) {
console.error('getInitialState', error);
gotoLoginPage();
// gotoLoginPage(true);
gotoHomePage();
}
return undefined;
};

// 如果不是登录页面,执行
const { location } = history;

// console.log('getInitialState', needAuth(location.pathname));
if (needAuth(location.pathname)) {
const token = getAccessToken();
if (token) {
const currentUser = await fetchUserInfo();
return {
fetchUserInfo,
@@ -71,9 +71,6 @@ export const layout: RuntimeConfig['layout'] = ({ initialState }) => {
return {
ErrorBoundary: ErrorBoundary,
rightContentRender: false,
waterMarkProps: {
// content: initialState?.currentUser?.nickName,
},
menu: {
locale: false,
// 每当 initialState?.currentUser?.userid 发生修改时重新执行 request
@@ -84,45 +81,9 @@ export const layout: RuntimeConfig['layout'] = ({ initialState }) => {
if (!initialState?.currentUser?.userId) {
return [];
}
// console.log('get menus')
// initialState.currentUser 中包含了所有用户信息
// console.log('get routers')
// setInitialState((preInitialState) => ({
// ...preInitialState,
// menus,
// }));
return getRemoteMenu();
},
},
onPageChange: () => {
const { location } = history;
// 如果没有登录,重定向到 login
if (!initialState?.currentUser && needAuth(location.pathname)) {
gotoLoginPage();
}
},
layoutBgImgList: [
{
src: 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/D2LWSqNny4sAAAAAAAAAAAAAFl94AQBr',
left: 85,
bottom: 100,
height: '303px',
},
{
src: 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/C2TWRpJpiC0AAAAAAAAAAAAAFl94AQBr',
bottom: -68,
right: -45,
height: '303px',
},
{
src: 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/F6vSTbj8KpYAAAAAAAAAAAAAFl94AQBr',
bottom: 0,
left: 0,
width: '331px',
},
],
// 自定义 403 页面
// unAccessible: <div>unAccessible</div>,
childrenRender: (children) => {
// 增加一个 loading 的状态
// if (initialState?.loading) return <PageLoading />;
@@ -159,10 +120,28 @@ export const layout: RuntimeConfig['layout'] = ({ initialState }) => {
};

export const onRouteChange: RuntimeConfig['onRouteChange'] = async (e) => {
// console.log('onRouteChange');

// 路由切换时,尤其是回退时,关闭打开的弹框
closeAllModals();

const { location } = e;
const pathname = location.pathname;
const token = getAccessToken();
// 没有 token,跳转到登录页面
if (!token && needAuth(pathname)) {
gotoHomePage();
return;
}

// 有 token, 登录页面直接跳转到首页
if (token && isLoginPage(pathname)) {
history.push(HomeUrl);
}

const menus = getRemoteMenu();
// console.log('onRouteChange', menus);
if (menus === null && needAuth(location.pathname)) {
// 没有菜单,刷新页面
if (menus === null && needAuth(pathname)) {
history.go(0);
}
};
@@ -179,10 +158,12 @@ export const patchClientRoutes: RuntimeConfig['patchClientRoutes'] = (e) => {
export function render(oldRender: () => void) {
// console.log('render');
const token = getAccessToken();
if (!token || token?.length === 0) {
if (!token) {
oldRender();
return;
}

// 有 token,获取路由
getRoutersInfo()
.then((res) => {
setRemoteMenu(res);
@@ -193,6 +174,17 @@ export function render(oldRender: () => void) {
});
}

export const useQiankunStateForSlave = () => {
const [globalState, setGlobalState] = useState<any>({
slogan: 'Hello MicroFrontend',
});

return {
globalState,
setGlobalState,
};
};

// 主题修改
export const antd: RuntimeAntdConfig = (memo) => {
memo.theme ??= {};


BIN
react-ui/src/assets/img/home/app-logo-dark.png View File

Before After
Width: 58  |  Height: 41  |  Size: 2.2 kB

BIN
react-ui/src/assets/img/home/app-logo.png View File

Before After
Width: 55  |  Height: 39  |  Size: 1.6 kB

BIN
react-ui/src/assets/img/home/code-arrow.png View File

Before After
Width: 36  |  Height: 36  |  Size: 497 B

BIN
react-ui/src/assets/img/home/code-bg.png View File

Before After
Width: 3840  |  Height: 1786  |  Size: 5.3 MB

BIN
react-ui/src/assets/img/home/code-item-bg-hover.png View File

Before After
Width: 906  |  Height: 344  |  Size: 116 kB

BIN
react-ui/src/assets/img/home/code-item-bg.png View File

Before After
Width: 1359  |  Height: 516  |  Size: 114 kB

BIN
react-ui/src/assets/img/home/code.png View File

Before After
Width: 120  |  Height: 120  |  Size: 9.7 kB

BIN
react-ui/src/assets/img/home/dataset-arrow-right.png View File

Before After
Width: 50  |  Height: 34  |  Size: 978 B

BIN
react-ui/src/assets/img/home/dataset-bg.png View File

Before After
Width: 3840  |  Height: 2612  |  Size: 504 kB

BIN
react-ui/src/assets/img/home/dataset-item-bg.png View File

Before After
Width: 906  |  Height: 344  |  Size: 77 kB

BIN
react-ui/src/assets/img/home/dataset.png View File

Before After
Width: 120  |  Height: 120  |  Size: 9.9 kB

BIN
react-ui/src/assets/img/home/default-avatar.png View File

Before After
Width: 72  |  Height: 72  |  Size: 3.9 kB

BIN
react-ui/src/assets/img/home/footer-bg.png View File

Before After
Width: 3840  |  Height: 626  |  Size: 434 kB

BIN
react-ui/src/assets/img/home/header-bg-mini.png View File

Before After
Width: 2800  |  Height: 116  |  Size: 172 kB

BIN
react-ui/src/assets/img/home/header-bg.png View File

Before After
Width: 3840  |  Height: 1034  |  Size: 2.7 MB

BIN
react-ui/src/assets/img/home/image.png View File

Before After
Width: 120  |  Height: 120  |  Size: 10 kB

BIN
react-ui/src/assets/img/home/mirror-arrow.png View File

Before After
Width: 26  |  Height: 26  |  Size: 309 B

BIN
react-ui/src/assets/img/home/mirror-version.png View File

Before After
Width: 28  |  Height: 28  |  Size: 915 B

BIN
react-ui/src/assets/img/home/model-between-dataset.png View File

Before After
Width: 3120  |  Height: 268  |  Size: 58 kB

BIN
react-ui/src/assets/img/home/model-bg.png View File

Before After
Width: 3840  |  Height: 1748  |  Size: 717 kB

BIN
react-ui/src/assets/img/home/model-item-bg-hover.png View File

Before After
Width: 906  |  Height: 416  |  Size: 325 kB

BIN
react-ui/src/assets/img/home/model-item-bg.png View File

Before After
Width: 906  |  Height: 416  |  Size: 106 kB

BIN
react-ui/src/assets/img/home/model-item-hot-hover.png View File

Before After
Width: 150  |  Height: 64  |  Size: 12 kB

BIN
react-ui/src/assets/img/home/model-item-hot.png View File

Before After
Width: 150  |  Height: 64  |  Size: 14 kB

BIN
react-ui/src/assets/img/home/model.png View File

Before After
Width: 120  |  Height: 120  |  Size: 9.6 kB

BIN
react-ui/src/assets/img/home/right-arrow-hover.png View File

Before After
Width: 50  |  Height: 34  |  Size: 996 B

BIN
react-ui/src/assets/img/home/right-arrow.png View File

Before After
Width: 35  |  Height: 35  |  Size: 691 B

BIN
react-ui/src/assets/img/home/service-bg.png View File

Before After
Width: 3840  |  Height: 1376  |  Size: 473 kB

BIN
react-ui/src/assets/img/home/service.png View File

Before After
Width: 120  |  Height: 120  |  Size: 11 kB

BIN
react-ui/src/assets/img/home/service1.png View File

Before After
Width: 590  |  Height: 342  |  Size: 275 kB

BIN
react-ui/src/assets/img/home/service2.png View File

Before After
Width: 590  |  Height: 342  |  Size: 369 kB

BIN
react-ui/src/assets/img/home/service3.png View File

Before After
Width: 590  |  Height: 342  |  Size: 383 kB

BIN
react-ui/src/assets/img/home/service4.png View File

Before After
Width: 590  |  Height: 342  |  Size: 193 kB

BIN
react-ui/src/assets/img/home/statistics-bg.png View File

Before After
Width: 2802  |  Height: 258  |  Size: 100 kB

BIN
react-ui/src/assets/img/home/timestamp.png View File

Before After
Width: 32  |  Height: 32  |  Size: 2.4 kB

BIN
react-ui/src/assets/img/home/user-avatar-big.png View File

Before After
Width: 88  |  Height: 88  |  Size: 15 kB

BIN
react-ui/src/assets/img/home/user-avatar.png View File

Before After
Width: 42  |  Height: 42  |  Size: 4.2 kB

BIN
react-ui/src/assets/img/home/路径 17816@2x (1).png View File

Before After
Width: 146  |  Height: 64  |  Size: 7.2 kB

BIN
react-ui/src/assets/img/home/路径 17816@2x.png View File

Before After
Width: 146  |  Height: 64  |  Size: 9.1 kB

+ 22
- 3
react-ui/src/components/CodeConfigItem/index.less View File

@@ -1,11 +1,22 @@
.code-config-item {
position: relative;
width: calc(25% - 7.5px);
width: calc(33.33% - 7px);
padding: 15px;
background-color: .addAlpha(@primary-color, 0.04) [];
border: 1px solid transparent;
border-radius: 4px;
cursor: pointer;

&__checkbox {
flex: 1;
min-width: 0;

:global {
.ant-checkbox + span {
flex: 1;
min-width: 0;
}
}
}

&__name {
margin-right: 8px;
@@ -38,6 +49,8 @@
margin-bottom: 10px !important;
color: @text-color-secondary;
font-size: 13px;
cursor: pointer;
word-break: break-all;
}

&__branch {
@@ -46,11 +59,17 @@
}

&:hover {
background-color: .addAlpha(@primary-color, 0.08) [];
}

&--active {
border-color: @primary-color;
box-shadow: 0px 0px 6px 1px rgba(0, 0, 0, 0.1);
}

&:hover &__name {
&--active &__name {
color: @primary-color;
}


}

+ 38
- 11
react-ui/src/components/CodeConfigItem/index.tsx View File

@@ -1,25 +1,51 @@
import { type CodeConfigData } from '@/pages/CodeConfig/List';
import { Flex, Typography } from 'antd';
import { getGitUrl } from '@/utils';
import { Checkbox, Flex, Typography } from 'antd';
import { type CheckboxChangeEvent } from 'antd/es/checkbox';
import classNames from 'classnames';
import { useState } from 'react';
import styles from './index.less';

type CodeConfigItemProps = {
item: CodeConfigData;
onClick?: (item: CodeConfigData) => void;
checked: boolean;
onChange?: (item: CodeConfigData, checked: boolean) => void;
};

function CodeConfigItem({ item, onClick }: CodeConfigItemProps) {
function CodeConfigItem({ item, checked, onChange }: CodeConfigItemProps) {
const [isEllipsis, setIsEllipsis] = useState(false);

const openProject = (e: React.MouseEvent<HTMLElement, MouseEvent>) => {
e.stopPropagation();
const { git_url, git_branch } = item;
const url = getGitUrl(git_url, git_branch);
window.open(url, '_blank');
};

const handleChange = (e: CheckboxChangeEvent) => {
onChange?.(item, e.target.checked);
};

return (
<div className={styles['code-config-item']} onClick={() => onClick?.(item)}>
<div
id={`code-config-item-${item.id}`}
className={classNames(styles['code-config-item'], {
[styles['code-config-item--active']]: checked,
})}
>
<Flex justify="space-between" align="center" style={{ marginBottom: '15px' }}>
<Typography.Paragraph
className={styles['code-config-item__name']}
ellipsis={{ tooltip: item.code_repo_name }}
<Checkbox
className={styles['code-config-item__checkbox']}
checked={checked}
onChange={handleChange}
>
{item.code_repo_name}
</Typography.Paragraph>
<Typography.Paragraph
className={styles['code-config-item__name']}
ellipsis={{ tooltip: item.code_repo_name }}
>
{item.code_repo_name}
</Typography.Paragraph>
</Checkbox>
<div
className={classNames(
styles['code-config-item__tag'],
@@ -35,9 +61,10 @@ function CodeConfigItem({ item, onClick }: CodeConfigItemProps) {
className={styles['code-config-item__url']}
ellipsis={{
rows: 2,
tooltip: isEllipsis ? item.git_url : false, // 仅当省略时显示 tooltip
onEllipsis: (ellipsis) => setIsEllipsis(ellipsis),
tooltip: isEllipsis ? item.git_url : false,
onEllipsis: (ellipsis) => setIsEllipsis(ellipsis), // 必须这样,不然不能省略
}}
onClick={openProject}
>
{item.git_url}
</Typography.Paragraph>


+ 10
- 16
react-ui/src/components/CodeSelect/index.tsx View File

@@ -4,7 +4,7 @@
* @Description: 流水线选择代码配置表单
*/

import CodeSelectorModal from '@/components/CodeSelectorModal';
import CodeSelectorModal, { CodeConfigData } from '@/components/CodeSelectorModal';
import KFIcon from '@/components/KFIcon';
import { openAntdModal } from '@/utils/modal';
import { Button } from 'antd';
@@ -18,7 +18,9 @@ export {
type ParameterInputValue,
} from '../ParameterInput';

type CodeSelectProps = ParameterInputProps;
export interface CodeSelectProps extends ParameterInputProps {
value?: CodeConfigData;
}

/** 代码配置选择表单组件 */
function CodeSelect({
@@ -32,26 +34,18 @@ function CodeSelect({
}: CodeSelectProps) {
// 选择代码配置
const selectResource = () => {
const defaultSelected: CodeConfigData | undefined =
value && typeof value === 'object' ? value : undefined;
const { close } = openAntdModal(CodeSelectorModal, {
defaultSelected: defaultSelected,
onOk: (res) => {
if (res) {
const { id, code_repo_name, git_url, git_branch, git_user_name, git_password, ssh_key } =
res;
const jsonObj = {
id,
name: code_repo_name,
code_path: git_url,
branch: git_branch,
username: git_user_name,
password: git_password,
ssh_private_key: ssh_key,
};
const jsonObjStr = JSON.stringify(jsonObj);
const { code_repo_name } = res;
onChange?.({
value: jsonObjStr,
...res,
value: code_repo_name,
showValue: code_repo_name,
fromSelect: true,
...jsonObj,
});
} else {
onChange?.(undefined);


+ 1
- 0
react-ui/src/components/CodeSelectorModal/index.less View File

@@ -17,6 +17,7 @@
margin-bottom: 30px;
overflow-x: hidden;
overflow-y: auto;
padding-bottom: 10px;
}

&__empty {


+ 84
- 17
react-ui/src/components/CodeSelectorModal/index.tsx View File

@@ -7,7 +7,8 @@
import KFIcon from '@/components/KFIcon';
import KFModal from '@/components/KFModal';
import { type CodeConfigData } from '@/pages/CodeConfig/List';
import { getCodeConfigListReq } from '@/services/codeConfig';
import { getCodeConfigListReq, getCodeConfigPageNumReq } from '@/services/codeConfig';
import { CustomPartial } from '@/types';
import { to } from '@/utils/promise';
import type { ModalProps, PaginationProps } from 'antd';
import { Empty, Input, Pagination } from 'antd';
@@ -17,24 +18,68 @@ import './index.less';

export { type CodeConfigData };

export type SelectCodeData = CustomPartial<
CodeConfigData,
| 'id'
| 'code_repo_name'
| 'git_url'
| 'git_branch'
| 'git_user_name'
| 'git_password'
| 'ssh_key'
| 'is_public'
>;

export interface CodeSelectorModalProps extends Omit<ModalProps, 'onOk'> {
onOk?: (params: CodeConfigData | undefined) => void;
defaultSelected?: SelectCodeData;
onOk?: (params: SelectCodeData | undefined) => void;
}

/** 选择代码配置的弹窗,推荐使用函数的方式打开 */
function CodeSelectorModal({ onOk, ...rest }: CodeSelectorModalProps) {
function CodeSelectorModal({ defaultSelected, onOk, ...rest }: CodeSelectorModalProps) {
const DefaultPageSize = 18;
const [dataList, setDataList] = useState<CodeConfigData[]>([]);
const [total, setTotal] = useState(0);
const [pagination, setPagination] = useState<PaginationProps>({
current: 1,
pageSize: 20,
});
const [searchText, setSearchText] = useState<string | undefined>(undefined);
const [inputText, setInputText] = useState<string | undefined>(undefined);
const [selected, setSelected] = useState(defaultSelected);
const [isScrolled, setIsScrolled] = useState(false);
const [pagination, setPagination] = useState<PaginationProps>({
current: defaultSelected?.id ? 0 : 1, // 为 0 时,不请求,等待接口返回选中的代码配置在第几页
pageSize: DefaultPageSize,
});

useEffect(() => {
const getCodeConfigPageNum = async (id: number, size: number) => {
const [res] = await to(
getCodeConfigPageNumReq(id, {
size,
}),
);
if (res) {
setPagination({
current: typeof res.data === 'number' ? Math.max(0, res.data) + 1 : 1,
pageSize: DefaultPageSize,
});
} else {
setPagination({
current: 1,
pageSize: DefaultPageSize,
});
}
};
if (defaultSelected?.id) {
getCodeConfigPageNum(defaultSelected?.id, DefaultPageSize);
}
}, [defaultSelected?.id]);

useEffect(() => {
// 获取数据请求
const getDataList = async () => {
// 为 0 时,不请求,等待接口返回选中的代码配置在第几页
if (pagination.current === 0) {
return;
}
const params = {
page: pagination.current! - 1,
size: pagination.pageSize,
@@ -50,6 +95,16 @@ function CodeSelectorModal({ onOk, ...rest }: CodeSelectorModalProps) {
getDataList();
}, [pagination, searchText]);

useEffect(() => {
if (dataList.length > 0 && !isScrolled && defaultSelected?.id) {
const selectedItem = document.getElementById(`code-config-item-${defaultSelected?.id}`);
if (selectedItem) {
selectedItem.scrollIntoView();
}
setIsScrolled(true);
}
}, [isScrolled, dataList, defaultSelected?.id]);

// 搜索
const handleSearch = (value: string) => {
setSearchText(value);
@@ -59,8 +114,12 @@ function CodeSelectorModal({ onOk, ...rest }: CodeSelectorModalProps) {
}));
};

const handleClick = (item: CodeConfigData) => {
onOk?.(item);
const handleChange = (item: CodeConfigData, checked: boolean) => {
if (checked) {
setSelected(item);
} else {
setSelected(undefined);
}
};

// 分页切换
@@ -77,7 +136,7 @@ function CodeSelectorModal({ onOk, ...rest }: CodeSelectorModalProps) {
title="选择代码配置"
image={require('@/assets/img/modal-code-config.png')}
width={920}
footer={null}
onOk={() => onOk?.(selected)}
destroyOnClose
>
<div className="kf-code-selector-modal">
@@ -93,23 +152,31 @@ function CodeSelectorModal({ onOk, ...rest }: CodeSelectorModalProps) {
prefix={
<KFIcon type="icon-sousuo" color="rgba(22,100,255,0.4" style={{ marginLeft: '10px' }} />
}
// prefix={
// <Icon icon="local:magnifying-glass" style={{ marginLeft: '10px', marginTop: '2px' }} />
// }
/>
{dataList?.length !== 0 ? (
<>
<div className="kf-code-selector-modal__content">
{dataList?.map((item) => (
<CodeConfigItem item={item} key={item.id} onClick={handleClick} />
<CodeConfigItem
item={item}
key={item.id}
checked={item.id === selected?.id}
onChange={handleChange}
/>
))}
</div>
<Pagination
align="center"
align="end"
total={total}
showSizeChanger
defaultPageSize={20}
pageSizeOptions={[20, 40, 60, 80, 100]}
defaultPageSize={DefaultPageSize}
pageSizeOptions={[
DefaultPageSize,
2 * DefaultPageSize,
3 * DefaultPageSize,
4 * DefaultPageSize,
5 * DefaultPageSize,
]}
showQuickJumper
onChange={handlePageChange}
{...pagination}


+ 2
- 1
react-ui/src/components/ErrorBoundary/index.tsx View File

@@ -1,4 +1,5 @@
import KFEmpty, { EmptyType } from '@/components/KFEmpty';
import { HomeUrl } from '@/utils/constant';
import { Button } from 'antd';
import { Component, ReactNode } from 'react';

@@ -55,7 +56,7 @@ function ErrorBoundaryFallback({ error }: { error: Error | null }) {
<Button
type="default"
onClick={() => {
window.history.pushState({}, '', '/');
window.history.pushState({}, '', HomeUrl);
window.location.reload();
}}
>


+ 20
- 0
react-ui/src/components/FormInfo/index.tsx View File

@@ -1,3 +1,4 @@
import { PipelineGlobalParamType, type PipelineGlobalParam } from '@/types';
import { formatEnum } from '@/utils/format';
import { Typography, type SelectProps } from 'antd';
import classNames from 'classnames';
@@ -16,6 +17,8 @@ type FormInfoProps = {
options?: SelectProps['options'];
/** 自定义节点 label、value 的字段 */
fieldNames?: SelectProps['fieldNames'];
/** 全局参数 */
globalParams?: PipelineGlobalParam[] | null;
/** 自定义类名 */
className?: string;
/** 自定义样式 */
@@ -32,12 +35,29 @@ function FormInfo({
select = false,
options,
fieldNames,
globalParams,
className,
style,
}: FormInfoProps) {
let showValue = value;
if (value && typeof value === 'object' && valuePropName) {
showValue = value[valuePropName];
const reg = /^\$\{(.*)\}$/;
if (value.fromSelect && Array.isArray(globalParams) && globalParams.length > 0) {
const match = reg.exec(showValue);
if (match) {
const paramName = match[1];
const foundParam = globalParams.find((v) => v.param_name === paramName);
if (foundParam) {
showValue =
foundParam.param_type === PipelineGlobalParamType.Boolean // 布尔类型转换
? foundParam.param_value
? 'true'
: 'false'
: foundParam.param_value;
}
}
}
} else if (select === true && options) {
let _options: SelectProps['options'] = options;
if (fieldNames) {


+ 45
- 14
react-ui/src/components/IFramePage/index.tsx View File

@@ -1,12 +1,11 @@
import FullScreenFrame from '@/components/FullScreenFrame';
import KFSpin from '@/components/KFSpin';
import { getKnowledgeGraphUrl, getLabelStudioUrl } from '@/services/developmentEnvironment';
import Loading from '@/utils/loading';
import { to } from '@/utils/promise';
import SessionStorage from '@/utils/sessionStorage';
import { FloatButton } from 'antd';
import classNames from 'classnames';
import { useEffect, useState } from 'react';
import { createPortal } from 'react-dom';
import './index.less';

export enum IframePageType {
@@ -54,9 +53,13 @@ const getRequestAPI = (type: IframePageType): (() => Promise<any>) => {

type IframePageProps = {
/** 子系统 */
type: IframePageType;
type?: IframePageType;
/** url */
url?: string;
/** 是否可以在页签上打开 */
openInTab?: boolean;
/** 是否显示加载 */
showLoading?: boolean;
/** 自定义样式类名 */
className?: string;
/** 自定义样式 */
@@ -64,32 +67,60 @@ type IframePageProps = {
};

/** 系统内嵌 iframe,目前系统有数据标注、应用开发、开发环境、GitLink 四个子系统,使用时可以添加其他子系统 */
function IframePage({ type, openInTab = false, className, style }: IframePageProps) {
function IframePage({
type,
url,
showLoading = true,
openInTab = false,
className,
style,
}: IframePageProps) {
const [iframeUrl, setIframeUrl] = useState('');
const [loading, setLoading] = useState(false);
// const [loading, setLoading] = useState(false);

useEffect(() => {
const requestIframeUrl = async () => {
setLoading(true);
const requestIframeUrl = async (type: IframePageType) => {
if (showLoading) {
Loading.show();
}
const [res] = await to(getRequestAPI(type)());
if (res && res.data) {
setIframeUrl(res.data);
} else {
setLoading(false);
if (showLoading) {
Loading.hide();
}
}
};

requestIframeUrl();
}, [type]);
if (type) {
requestIframeUrl(type);
} else if (url) {
if (showLoading) {
Loading.show();
}

setIframeUrl(url);
}
}, [type, url, showLoading]);

const handleLoad = () => {
if (showLoading) {
Loading.hide();
}
};

const hideLoading = () => {
setLoading(false);
const handleError = (error?: React.SyntheticEvent<HTMLIFrameElement, Event>) => {
console.log('error', error);
if (showLoading) {
Loading.hide();
}
};

return (
<div className={classNames('kf-iframe-page', className)} style={style}>
{loading && createPortal(<KFSpin size="large" />, document.body)}
<FullScreenFrame url={iframeUrl} onLoad={hideLoading} onError={hideLoading} />
{/* {loading && createPortal(<KFSpin size="large" />, document.body)} */}
{iframeUrl && <FullScreenFrame url={iframeUrl} onLoad={handleLoad} onError={handleError} />}
{openInTab && <FloatButton onClick={() => window.open(iframeUrl, '_blank')} />}
</div>
);


+ 4
- 1
react-ui/src/components/ParameterInput/index.tsx View File

@@ -9,6 +9,7 @@ import { CloseOutlined } from '@ant-design/icons';
import { ConfigProvider, Form, Input, Typography } from 'antd';
import { RuleObject } from 'antd/es/form';
import classNames from 'classnames';
import { ReactNode } from 'react';
import './index.less';

// 如果值是对象时的类型
@@ -55,6 +56,8 @@ export interface ParameterInputProps {
disabled?: boolean;
/** 元素 id */
id?: string;
/** 带标签的 input,设置后置标签 */
addonAfter?: ReactNode;
}

function ParameterInput({
@@ -75,7 +78,7 @@ function ParameterInput({
const valueObj =
typeof value === 'string' ? { value: value, fromSelect: false, showValue: value } : value;
if (valueObj && !valueObj.showValue) {
valueObj.showValue = valueObj.value;
valueObj.showValue = typeof valueObj.value === 'string' ? valueObj.value : '';
}
const isSelect = valueObj?.fromSelect;
const placeholder = valueObj?.placeholder || rest?.placeholder;


+ 83
- 39
react-ui/src/components/ParameterSelect/config.tsx View File

@@ -1,29 +1,34 @@
import { filterResourceStandard, resourceFieldNames } from '@/hooks/useComputingResource';
import { DatasetData, ModelData } from '@/pages/Dataset/config';
import { ServiceData } from '@/pages/ModelDeployment/types';
import { getDatasetList, getModelList } from '@/services/dataset/index.js';
import { getServiceListReq } from '@/services/modelDeployment';
import type { JCCResourceImage, JCCResourceStandard, JCCResourceType } from '@/state/jcdResource';
import { filterResourceStandard, resourceFieldNames } from '@/state/systemResource';
import { type SelectProps } from 'antd';
import { pick } from 'lodash';

// id 从 number 转换为 string
const convertId = (item: any) => ({
...item,
id: JSON.stringify({
id: `${item.id}`,
name: item.name,
identifier: item.identifier,
owner: item.owner,
}),
});

export type SelectPropsConfig = {
getOptions: () => Promise<any>; // 获取下拉数据
getOptions?: () => Promise<any>; // 获取下拉数据
fieldNames?: SelectProps['fieldNames']; // 下拉数据字段
optionFilterProp?: SelectProps['optionFilterProp']; // 过滤字段名
filterOption?: SelectProps['filterOption']; // 过滤函数
isObjectValue: boolean; // value 是对象
getValue?: (value: any) => string | number; // 对象类型时,获取其值
getLabel?: (value: any) => string; // 对象类型时,获取其 label
};

export const paramSelectConfig: Record<string, SelectPropsConfig> = {
export const ParameterSelectTypeList = [
'dataset',
'model',
'service',
'resource',
'remote-image',
'remote-resource-type',
'remote-resource',
] as const;

export type ParameterSelectDataType = (typeof ParameterSelectTypeList)[number];

export const paramSelectConfig: Record<ParameterSelectDataType, SelectPropsConfig> = {
dataset: {
getOptions: async () => {
const res = await getDatasetList({
@@ -31,13 +36,16 @@ export const paramSelectConfig: Record<string, SelectPropsConfig> = {
size: 1000,
is_public: false,
});
return res?.data?.content?.map(convertId) ?? [];
return res?.data?.content ?? [];
},
fieldNames: {
label: 'name',
value: 'id',
optionFilterProp: 'label',
getValue: (value: DatasetData) => {
return value.id;
},
getLabel: (value: DatasetData) => {
return value.name;
},
optionFilterProp: 'name',
isObjectValue: true,
},
model: {
getOptions: async () => {
@@ -46,13 +54,16 @@ export const paramSelectConfig: Record<string, SelectPropsConfig> = {
size: 1000,
is_public: false,
});
return res?.data?.content?.map(convertId) ?? [];
return res?.data?.content ?? [];
},
optionFilterProp: 'label',
getValue: (value: ModelData) => {
return value.id;
},
fieldNames: {
label: 'name',
value: 'id',
getLabel: (value: ModelData) => {
return value.name;
},
optionFilterProp: 'name',
isObjectValue: true,
},
service: {
getOptions: async () => {
@@ -60,25 +71,58 @@ export const paramSelectConfig: Record<string, SelectPropsConfig> = {
page: 0,
size: 1000,
});
return (
res?.data?.content?.map((item: ServiceData) => ({
label: item.service_name,
value: JSON.stringify(pick(item, ['id', 'service_name'])),
})) ?? []
);
},
fieldNames: {
label: 'label',
value: 'value',
return res?.data?.content ?? [];
},
optionFilterProp: 'label',
getValue: (value: ServiceData) => {
return value.id;
},
getLabel: (value: ServiceData) => {
return value.service_name;
},
isObjectValue: true,
},
resource: {
getOptions: async () => {
// 不需要这个函数
return [];
},
fieldNames: resourceFieldNames,
filterOption: filterResourceStandard as SelectProps['filterOption'],
isObjectValue: false,
},
'remote-resource-type': {
optionFilterProp: 'label',
isObjectValue: false,
getValue: (value: JCCResourceType) => {
return value.value;
},
getLabel: (value: JCCResourceType) => {
return value.label;
},
},
'remote-image': {
optionFilterProp: 'label',
getValue: (value: JCCResourceImage) => {
return value.imageID;
},
getLabel: (value: JCCResourceImage) => {
return value.name;
},
isObjectValue: true,
},
'remote-resource': {
optionFilterProp: 'label',
getValue: (value: JCCResourceStandard) => {
return value.id;
},
getLabel: (value: JCCResourceStandard) => {
const cpu = value.baseResourceSpecs.find((v) => v.type === 'CPU');
const ram = value.baseResourceSpecs.find((v) => v.type === 'MEMORY' && v.name === 'RAM');
const vram = value.baseResourceSpecs.find((v) => v.type === 'MEMORY' && v.name === 'VRAM');
const cpuText = cpu ? `CPU:${cpu.availableValue}, ` : '';
const ramText = ram ? `内存: ${ram.availableValue}${ram.availableUnit?.toUpperCase()}` : '';
const vramText = vram
? `(显存${vram.availableValue}${vram.availableUnit?.toUpperCase()})`
: '';
return `${value.type}: ${value.availableCount}*${value.name}${vramText}, ${cpuText}${ramText}`;
},
isObjectValue: true,
},
};

+ 103
- 41
react-ui/src/components/ParameterSelect/index.tsx View File

@@ -4,19 +4,25 @@
* @Description: 参数下拉选择组件,支持资源规格、数据集、模型、服务
*/

import { useComputingResource } from '@/hooks/useComputingResource';
import jccResourceState, { getResourceTypes } from '@/state/jcdResource';
import systemResourceState, { getSystemResources } from '@/state/systemResource';
import { to } from '@/utils/promise';
import { useSnapshot } from '@umijs/max';
import { Select, type SelectProps } from 'antd';
import { useEffect, useState } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import FormInfo from '../FormInfo';
import { paramSelectConfig } from './config';
import { paramSelectConfig, type ParameterSelectDataType } from './config';

export { ParameterSelectTypeList, type ParameterSelectDataType } from './config';

export type ParameterSelectObject = {
value: any;
[key: string]: any;
};

export type ParameterSelectDataType = 'dataset' | 'model' | 'service' | 'resource';
type SelectOptions = SelectProps['options'];

const identityFunc = (value: any) => value;

export interface ParameterSelectProps extends SelectProps {
/** 类型 */
@@ -25,8 +31,6 @@ export interface ParameterSelectProps extends SelectProps {
display?: boolean;
/** 值,支持对象,对象必须包含 value */
value?: string | ParameterSelectObject;
/** 用于流水线, 流水线资源规格要求 id 为字符串 */
isPipeline?: boolean;
/** 修改后回调 */
onChange?: (value: string | ParameterSelectObject) => void;
}
@@ -36,69 +40,127 @@ function ParameterSelect({
dataType,
display = false,
value,
isPipeline = false,
onChange,
...rest
}: ParameterSelectProps) {
const [options, setOptions] = useState<SelectProps['options']>([]);
const [options, setOptions] = useState<SelectOptions>([]);
const propsConfig = paramSelectConfig[dataType];
const valueText = typeof value === 'object' && value !== null ? value.value : value;
const [resourceStandardList] = useComputingResource();
const computingResource = isPipeline
? resourceStandardList.map((v) => ({
...v,
id: String(v.id),
}))
: resourceStandardList;
const {
getLabel = identityFunc,
getValue = identityFunc,
getOptions,
filterOption,
fieldNames,
optionFilterProp,
isObjectValue,
} = propsConfig;
const selectValue = typeof value === 'object' && value !== null ? value.value : value;
// 数据集、模型、服务,对象转换成 json 字符串
const valueText =
typeof selectValue === 'object' && selectValue !== null ? getValue(selectValue) : selectValue;
const jccResourceSnap = useSnapshot(jccResourceState);
const systemResourceSnap = useSnapshot(systemResourceState);

const objectOptions = useMemo(() => {
return dataType === 'remote-resource-type'
? jccResourceSnap.types
: dataType === 'remote-image'
? jccResourceSnap.images
: dataType === 'remote-resource'
? jccResourceSnap.resources
: options;
}, [dataType, options, jccResourceSnap.types, jccResourceSnap.images, jccResourceSnap.resources]);

// 将对象类型转换为 Select Options
const converObjectToOptions = useCallback(
(v: any) => {
return {
label: getLabel(v),
value: getValue(v),
};
},
[getLabel, getValue],
);

// 数据集、模型、服务获取数据后,进行转换
const objectSelectOptions = useMemo(() => {
return objectOptions?.map(converObjectToOptions);
}, [converObjectToOptions, objectOptions]);

// 快速得到选中的对象
const valueMap = useMemo(() => {
const map = new Map<string | number, any>();
objectOptions?.forEach((v) => {
map.set(getValue(v), v);
});

return map;
}, [objectOptions, getValue]);

useEffect(() => {
// 获取下拉数据
const getSelectOptions = async () => {
if (!propsConfig) {
return;
}
const getOptions = propsConfig.getOptions;
const [res] = await to(getOptions());
if (res) {
setOptions(res);
if (getOptions) {
const [res] = await to(getOptions());
if (res) {
setOptions(res);
}
} else if (dataType === 'remote-resource-type') {
if (jccResourceSnap.types.length === 0) {
getResourceTypes();
}
} else if (dataType === 'resource') {
getSystemResources();
}
};

getSelectOptions();
}, [propsConfig]);
}, [getOptions, dataType, getResourceTypes, jccResourceSnap.types]);

const selectOptions = dataType === 'resource' ? computingResource : options;
const selectOptions = (
dataType === 'resource' ? systemResourceSnap.resources : objectSelectOptions
) as SelectOptions;

const handleChange = (text: string) => {
if (typeof value === 'object' && value !== null) {
onChange?.({
...value,
value: text,
});
// 数据集、模型、服务,转换成对象
if (isObjectValue) {
// 设置为 null 是因为 ant g6 bug
// 如果值为 undefined 时, graph.changeData(data) 会保留前面的值
const selectValue = text ? valueMap.get(text) : null;
if (typeof value === 'object' && value !== null) {
onChange?.({
...value,
value: selectValue,
});
} else {
onChange?.(selectValue);
}
} else {
onChange?.(text);
const selectValue = text ? text : '';
if (typeof value === 'object' && value !== null) {
onChange?.({
...value,
value: selectValue,
});
} else {
onChange?.(selectValue);
}
}
};

// 只用于展示,FormInfo 组件带有 Tooltip
if (display) {
return (
<FormInfo
select
value={valueText}
options={selectOptions}
fieldNames={propsConfig?.fieldNames}
></FormInfo>
<FormInfo select value={valueText} options={selectOptions} fieldNames={fieldNames}></FormInfo>
);
}

return (
<Select
{...rest}
filterOption={propsConfig?.filterOption}
options={selectOptions}
fieldNames={propsConfig?.fieldNames}
optionFilterProp={propsConfig?.optionFilterProp}
fieldNames={fieldNames}
optionFilterProp={optionFilterProp}
filterOption={filterOption}
value={valueText}
onChange={handleChange}
showSearch


+ 29
- 44
react-ui/src/components/ResourceSelect/index.tsx View File

@@ -10,10 +10,10 @@ import ResourceSelectorModal, {
ResourceSelectorType,
selectorTypeConfig,
} from '@/components/ResourceSelectorModal';
import { CommonTabKeys } from '@/enums';
import { openAntdModal } from '@/utils/modal';
import { Button, ConfigProvider } from 'antd';
import classNames from 'classnames';
import { pick } from 'lodash';
import ParameterInput, { type ParameterInputProps } from '../ParameterInput';
import './index.less';

@@ -27,6 +27,8 @@ export { ResourceSelectorType, selectorTypeConfig, type ResourceSelectorResponse
interface ResourceSelectProps extends ParameterInputProps {
/** 类型,数据集、模型、镜像 */
type: ResourceSelectorType;
/** 值 */
value?: ResourceSelectorResponse;
}

// 获取选择数据集、模型、镜像后面按钮 icon
@@ -47,68 +49,51 @@ function ResourceSelect({
}: ResourceSelectProps) {
const { componentSize } = ConfigProvider.useConfig();
const mySize = size || componentSize;
let selectedResource: ResourceSelectorResponse | undefined = undefined;
if (
value &&
typeof value === 'object' &&
value.activeTab &&
value.id &&
value.name &&
value.version &&
value.path &&
(type === ResourceSelectorType.Mirror || (value.identifier && value.owner))
) {
selectedResource = pick(value, [
'activeTab',
'id',
'identifier',
'name',
'owner',
'version',
'path',
]) as ResourceSelectorResponse;
let defaultActiveTab: CommonTabKeys | undefined,
defaultExpandedKeys: string[] = [],
defaultCheckedKeys: string[] = [];
if (value && typeof value === 'object') {
defaultActiveTab = value.activeTab;
if (type === ResourceSelectorType.Mirror) {
if (value.image_id && value.id) {
defaultExpandedKeys = [`${value.image_id}`];
defaultCheckedKeys = [`${value.image_id}-${value.id}`];
}
} else if (value.id && value.version) {
defaultExpandedKeys = [value.id];
defaultCheckedKeys = [`${value.id}-${value.version}`];
}
}

// 选择数据集、模型、镜像
const selectResource = () => {
const { close } = openAntdModal(ResourceSelectorModal, {
type,
defaultExpandedKeys: selectedResource ? [selectedResource.id] : [],
defaultCheckedKeys: selectedResource
? [`${selectedResource.id}-${selectedResource.version}`]
: [],
defaultActiveTab: selectedResource?.activeTab,
defaultExpandedKeys: defaultExpandedKeys,
defaultCheckedKeys: defaultCheckedKeys,
defaultActiveTab: defaultActiveTab,
onOk: (res) => {
if (res) {
const { activeTab, id, name, version, path, identifier, owner } = res;
if (type === ResourceSelectorType.Mirror) {
const { activeTab, ...rest } = res;
const { url } = rest;
onChange?.({
value: path,
showValue: path,
...rest,
value: url,
showValue: url,
fromSelect: true,
activeTab,
id,
name,
version,
path,
});
} else {
const jsonObj = {
id,
name,
version,
path,
identifier,
owner,
};
const jsonObjStr = JSON.stringify(jsonObj);
const { activeTab, ...rest } = res;
const { name, version } = rest;
const showValue = `${name}:${version}`;
onChange?.({
value: jsonObjStr,
...rest,
value: showValue,
showValue,
fromSelect: true,
activeTab,
...jsonObj,
});
}
} else {


+ 28
- 17
react-ui/src/components/ResourceSelectorModal/config.tsx View File

@@ -1,8 +1,8 @@
import datasetImg from '@/assets/img/modal-select-dataset.png';
import mirrorImg from '@/assets/img/modal-select-mirror.png';
import modelImg from '@/assets/img/modal-select-model.png';
import { AvailableRange, CommonTabKeys } from '@/enums';
import { ResourceData, ResourceVersionData } from '@/pages/Dataset/config';
import { AvailableRange, CommonTabKeys, MirrorVersionStatus } from '@/enums';
import { DatasetData, ModelData, ResourceData, ResourceVersionData } from '@/pages/Dataset/config';
import { MirrorVersionData } from '@/pages/Mirror/Info';
import { MirrorData } from '@/pages/Mirror/List';
import {
@@ -24,11 +24,11 @@ export enum ResourceSelectorType {
}

// 数据集、模型列表转为树形结构
const convertDatasetToTreeData = (list: ResourceData[]): TreeDataNode[] => {
const convertDatasetToTreeData = (list: ResourceData[], isPublic: boolean): TreeDataNode[] => {
return list.map((v) => ({
...v,
key: `${v.id}`,
title: v.name,
title: isPublic ? `${v.name} (${v.owner})` : v.name,
isLeaf: false,
checkable: false,
}));
@@ -52,9 +52,9 @@ const convertResourceVersionToTreeData = (
): TreeDataNode[] => {
return list.map((item: ResourceVersionData) => ({
...pick(info, ['id', 'name', 'owner', 'identifier', 'is_public']),
version: item.name,
title: item.name,
key: `${parentId}-${item.name}`,
title: item.name,
version: item.name,
isLeaf: true,
checkable: true,
}));
@@ -66,9 +66,9 @@ const convertMirrorVersionToTreeData = (
list: MirrorVersionData[],
): TreeDataNode[] => {
return list.map((item: MirrorVersionData) => ({
url: item.url,
title: item.tag_name,
...item,
key: `${parentId}-${item.id}`,
title: item.tag_name,
isLeaf: true,
checkable: true,
}));
@@ -106,7 +106,7 @@ export class DatasetSelector implements SelectorTypeInfo {
const res = await getDatasetList({ is_public: isPublic, page: 0, size: 2000 });
if (res && res.data) {
const list = res.data.content || [];
return convertDatasetToTreeData(list);
return convertDatasetToTreeData(list, isPublic);
} else {
return Promise.reject('获取数据集列表失败');
}
@@ -125,11 +125,16 @@ export class DatasetSelector implements SelectorTypeInfo {
const params = pick(parentNode, ['owner', 'identifier', 'id', 'name', 'version', 'is_public']);
const res = await getDatasetInfo(params);
if (res && res.data) {
const path = res.data.relative_paths || '';
const list = res.data.dataset_version_vos || [];
const dataset = res.data as DatasetData;
const {
relative_paths: path = '',
dataset_version_vos: list = [],
version_desc: versionDesc = '',
} = dataset;
return {
path,
content: list,
versionDesc,
};
} else {
return Promise.reject('获取数据集文件列表失败');
@@ -158,7 +163,7 @@ export class ModelSelector implements SelectorTypeInfo {
const res = await getModelList({ is_public: isPublic, page: 0, size: 2000 });
if (res && res.data) {
const list = res.data.content || [];
return convertDatasetToTreeData(list);
return convertDatasetToTreeData(list, isPublic);
} else {
return Promise.reject('获取模型列表失败');
}
@@ -177,11 +182,17 @@ export class ModelSelector implements SelectorTypeInfo {
const params = pick(parentNode, ['owner', 'identifier', 'id', 'name', 'version', 'is_public']);
const res = await getModelInfo(params);
if (res && res.data) {
const path = res.data.relative_paths || '';
const list = res.data.model_version_vos || [];
const model = res.data as ModelData;
const {
relative_paths: path = '',
model_version_vos: list = [],
version_desc: versionDesc = '',
} = model;

return {
path,
content: list,
versionDesc,
};
} else {
return Promise.reject('获取模型文件列表失败');
@@ -224,8 +235,7 @@ export class MirrorSelector implements SelectorTypeInfo {
image_id: parentKey,
page: 0,
size: 2000,
status: 'available',
state: 1,
status: MirrorVersionStatus.Available,
});
if (res && res.data) {
const list = res.data.content || [];
@@ -236,7 +246,7 @@ export class MirrorSelector implements SelectorTypeInfo {
}

async getFiles(_parentKey: string, parentNode: MirrorVersionData) {
const { url } = parentNode;
const { url, description } = parentNode;
return {
path: url,
content: [
@@ -245,6 +255,7 @@ export class MirrorSelector implements SelectorTypeInfo {
file_name: `${url}`,
},
],
versionDesc: description,
};
}
}


+ 16
- 1
react-ui/src/components/ResourceSelectorModal/index.less View File

@@ -65,7 +65,7 @@
border-bottom: 1px solid rgba(22, 100, 255, 0.1);
}
&__files {
height: calc(100% - 75px);
height: calc(100% - 61px);
overflow-y: auto;

&__file {
@@ -76,7 +76,22 @@
word-break: break-all;
background: rgba(4, 3, 3, 0.06);
border-radius: 4px;

&:last-child {
margin-bottom: 0;
}
}
}
&__desc {
margin-bottom: 10px;
padding: 10px;
overflow-y: auto;
color: @text-color-secondary;
font-size: 13px;
word-break: break-all;
background: rgba(4, 3, 3, 0.06);
border-radius: 4px;
max-height: calc(100% - 61px);
}
}
}

+ 48
- 24
react-ui/src/components/ResourceSelectorModal/index.tsx View File

@@ -8,6 +8,7 @@ import KFIcon from '@/components/KFIcon';
import KFModal from '@/components/KFModal';
import { CommonTabKeys } from '@/enums';
import { ResourceFileData } from '@/pages/Dataset/config';
import { type MirrorVersionData } from '@/pages/Mirror/Info';
import { to } from '@/utils/promise';
import type { GetRef, ModalProps, TreeDataNode, TreeProps } from 'antd';
import { Input, Tabs, Tree } from 'antd';
@@ -19,13 +20,13 @@ export { ResourceSelectorType, selectorTypeConfig };
// 选择数据集、模型、镜像的返回类型
export type ResourceSelectorResponse = {
activeTab: CommonTabKeys; // 是我的还是公开的
id: string; // 数据集\模型\镜像 id
name: string; // 数据集\模型\镜像 name
version: string; // 数据集\模型\镜像版本
path: string; // 数据集\模型\镜像版本路径
identifier: string; // 数据集\模型 identifier,镜像这个字段为空
owner: string; // 数据集\模型 owner,镜像这个字段为空
};
id: string; // 数据集\模型 id
name: string; // 数据集\模型 name
identifier: string; // 数据集\模型 identifier
owner: string; // 数据集\模型 owner
version: string; // 数据集\模型 version
path: string; // 数据集\模型 版本路径
} & MirrorVersionData;

export interface ResourceSelectorModalProps extends Omit<ModalProps, 'onOk'> {
/** 类型,数据集、模型、镜像 */
@@ -84,6 +85,7 @@ function ResourceSelectorModal({
const [loadedKeys, setLoadedKeys] = useState<React.Key[]>([]);
const [originTreeData, setOriginTreeData] = useState<TreeDataNode[]>([]);
const [files, setFiles] = useState<ResourceFileData[]>([]);
const [versionDesc, setVersionDesc] = useState<string | undefined>(undefined);
const [versionPath, setVersionPath] = useState('');
const [searchText, setSearchText] = useState('');
const [firstLoadList, setFirstLoadList] = useState(false);
@@ -119,6 +121,7 @@ function ResourceSelectorModal({
setCheckedKeys([]);
setLoadedKeys([]);
setFiles([]);
setVersionDesc(undefined);
setVersionPath('');
setSearchText('');
getTreeData();
@@ -169,9 +172,11 @@ function ResourceSelectorModal({
if (res) {
setVersionPath(res.path);
setFiles(res.content);
setVersionDesc(res.versionDesc);
} else {
setVersionPath('');
setFiles([]);
setVersionDesc(undefined);
}
};

@@ -201,6 +206,7 @@ function ResourceSelectorModal({
} else {
setVersionPath('');
setFiles([]);
setVersionDesc(undefined);
}
};

@@ -236,15 +242,22 @@ function ResourceSelectorModal({
const name = (treeNode?.title ?? '') as string;
const identifier = (treeNode?.identifier ?? '') as string;
const owner = (treeNode?.owner ?? '') as string;
const res = {
id,
name,
path: versionPath,
version,
identifier,
owner,
activeTab: activeTab,
};
const childNode = treeNode.children.filter((v: TreeDataNode) => v.key === last)[0];
const res =
type === ResourceSelectorType.Mirror
? {
activeTab: activeTab,
...childNode,
}
: {
activeTab: activeTab,
id: Number(id),
name,
path: versionPath,
version,
identifier,
owner,
};
onOk?.(res);
} else {
onOk?.(undefined);
@@ -253,8 +266,9 @@ function ResourceSelectorModal({

const title = `选择${config.name}`;
const palceholder = `请输入${config.name}名称`;
const fileLen = files.length > 0 ? `(${files.length})` : '';
const fileTitle =
type === ResourceSelectorType.Mirror ? '已选镜像' : `已选${config.name}文件(${files.length})`;
type === ResourceSelectorType.Mirror ? '镜像地址' : `${config.name}版本文件${fileLen}`;
const tabItems = config.tabItems;
const titleImg = config.modalIcon;

@@ -312,14 +326,24 @@ function ResourceSelectorModal({
/>
</div>
<div className={styles['model-selector__right']}>
<div className={styles['model-selector__right__title']}>{fileTitle}</div>
<div className={styles['model-selector__right__files']}>
{files.map((v) => (
<div key={v.url} className={styles['model-selector__right__files__file']}>
{v.file_name}
</div>
))}
<div style={{ height: '50%' }}>
<div className={styles['model-selector__right__title']}>{fileTitle}</div>
<div className={styles['model-selector__right__files']}>
{files.map((v) => (
<div key={v.url} className={styles['model-selector__right__files__file']}>
{v.file_name}
</div>
))}
</div>
</div>
{versionDesc && (
<div style={{ height: '50%' }}>
<div
className={styles['model-selector__right__title']}
>{`${config.name}版本描述`}</div>
<div className={styles['model-selector__right__desc']}>{versionDesc}</div>
</div>
)}
</div>
</div>
</div>


+ 44
- 21
react-ui/src/components/RightContent/AvatarDropdown.tsx View File

@@ -1,22 +1,25 @@
import { clearSessionToken } from '@/access';
import DefaultAvatar from '@/assets/img/avatar-default.png';
import { getLabelStudioUrl } from '@/services/developmentEnvironment';
import { setRemoteMenu } from '@/services/session';
import { logout } from '@/services/system/auth';
import { ClientInfo } from '@/types';
import { sleep } from '@/utils/promise';
import { sleep, to } from '@/utils/promise';
import SessionStorage from '@/utils/sessionStorage';
import { gotoLoginPage, oauthLogout } from '@/utils/ui';
import { LogoutOutlined, UserOutlined } from '@ant-design/icons';
import { setAlpha } from '@ant-design/pro-components';
import { useEmotionCss } from '@ant-design/use-emotion-css';
import { history, useModel } from '@umijs/max';
import { useModel, useNavigate } from '@umijs/max';
import { Avatar, Spin } from 'antd';
import type { MenuInfo } from 'rc-menu/lib/interface';
import React, { useCallback } from 'react';
import React from 'react';
import { flushSync } from 'react-dom';
import HeaderDropdown from '../HeaderDropdown';

export type GlobalHeaderRightProps = {
menu?: boolean;
isHome?: boolean;
};

const Name = () => {
@@ -55,24 +58,47 @@ const AvatarLogo = () => {
},
};
});
return <Avatar size="small" className={avatarClassName} src={currentUser?.avatar} alt="avatar" />;
return (
<Avatar
size="small"
className={avatarClassName}
src={currentUser?.avatar || DefaultAvatar}
alt="avatar"
/>
);
};

const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu }) => {
const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu, isHome = false }) => {
const navigate = useNavigate();
/**
* 退出登录,并且将当前的 url 保存
*/
const loginOut = async () => {
oauthLogout('http://172.20.32.197:31209/oauth/logout');
// const { origin } = location;
const [res] = await to(getLabelStudioUrl());
if (res && res.data) {
oauthLogout(`${res.data}/oauth/logout`);
}
// 至少 1 秒后跳转,希望子系统能完成注销
await Promise.all([logout(), sleep(1000)]);
clearSessionToken();
setRemoteMenu(null);
gotoLoginPage();
// 退出 oauth2
const clientInfo: ClientInfo = SessionStorage.getItem(SessionStorage.clientInfoKey, true);
if (clientInfo) {
const { logoutUri } = clientInfo;
location.replace(logoutUri);
// if (isHome) {
// setTimeout(() => {
// location.replace(origin);
// }, 1);
// }
} else {
if (isHome) {
location.reload();
} else {
gotoLoginPage(true);
}
}
};
const actionClassName = useEmotionCss(({ token }) => {
@@ -92,20 +118,17 @@ const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu }) => {
});
const { initialState, setInitialState } = useModel('@@initialState');

const onMenuClick = useCallback(
(event: MenuInfo) => {
const { key } = event;
if (key === 'logout') {
flushSync(() => {
setInitialState((s) => ({ ...s, currentUser: undefined }));
});
loginOut();
return;
}
history.push(`/account/${key}`);
},
[setInitialState],
);
const onMenuClick = (event: MenuInfo) => {
const { key } = event;
if (key === 'logout') {
flushSync(() => {
setInitialState((s) => ({ ...s, currentUser: undefined }));
});
loginOut();
return;
}
navigate(`/account/${key}`);
};

const loading = (
<span className={actionClassName}>


+ 10
- 1
react-ui/src/components/RunDuration/index.tsx View File

@@ -10,13 +10,21 @@ type RunDurationProps = {
};
function RunDuration({ createTime, finishTime, className, style }: RunDurationProps) {
const [now] = useServerTime();
const [currentTime, setCurrentTime] = useState<Date>(now());
const [currentTime, setCurrentTime] = useState<Date>(finishTime ? new Date(finishTime) : now());

// console.log(
// 'currentTime',
// new Date(createTime ?? 0),
// currentTime,
// (currentTime.getTime() - new Date(createTime ?? 0).getTime()) / 1000,
// );

// 定时刷新耗时
useEffect(() => {
if (finishTime) {
setCurrentTime(new Date(finishTime));
} else {
setCurrentTime(now());
const timer = setInterval(() => {
setCurrentTime(now());
}, 1000);
@@ -25,6 +33,7 @@ function RunDuration({ createTime, finishTime, className, style }: RunDurationPr
};
}
}, [finishTime, now]);

return (
<span className={className} style={style}>
{elapsedTime(createTime, currentTime)}


+ 11
- 3
react-ui/src/enums/index.ts View File

@@ -33,7 +33,7 @@ export enum TensorBoardStatus {
Unknown = 'Unknown', // 未知
Pending = 'Pending', // 启动中
Running = 'Running', // 运行中
Terminated = 'Terminated', // 未启动或者已终止
Terminated = 'Terminated', // 未启动
Failed = 'Failed', // 失败
}

@@ -95,8 +95,8 @@ export enum AutoMLType {

export const autoMLTypeOptions = [
{ label: '表格', value: AutoMLType.Table },
{ label: '文本分类', value: AutoMLType.Text },
{ label: '视频分类', value: AutoMLType.Video },
{ label: '文本', value: AutoMLType.Text },
{ label: '视频', value: AutoMLType.Video },
];

// 自动化任务类型
@@ -163,3 +163,11 @@ export enum AutoMLTrailStatus {
CANCELLED = 'CANCELLED', // 取消
MEMOUT = 'MEMOUT', // 内存溢出
}

// 流水线组件类型
export enum ComponentType {
Ref = 'ref',
Select = 'select',
Map = 'map',
Str = 'str',
}

+ 2
- 0
react-ui/src/enums/pagesEnums.ts View File

@@ -1,4 +1,6 @@
export enum PageEnum {
Root = '/',
LOGIN = '/user/login',
Authorize = '/authorize',
Home = '/home',
}

+ 26
- 0
react-ui/src/global.tsx View File

@@ -19,6 +19,30 @@ const clearCache = () => {
}
};

const doubleText = () => {
if (process.env.NODE_ENV === 'development') {
document.body.addEventListener(
'click',
(e) => {
const target = e.target;
if (
// e.altKey &&
e.ctrlKey &&
target &&
target.innerText &&
target.nodeType === Node.ELEMENT_NODE
) {
e.stopPropagation();
e.preventDefault();
const times = 2;
target.innerText = target.innerText.repeat(times);
}
},
true,
);
}
};

// if pwa is true
if (pwa) {
// Notify user if offline now
@@ -89,3 +113,5 @@ if (pwa) {

clearCache();
}

doubleText();

+ 71
- 47
react-ui/src/hooks/useComputingResource.ts View File

@@ -4,66 +4,90 @@
* @Description: 资源规格 hook
*/

import { getComputingResourceReq } from '@/services/pipeline';
import { ComputingResource } from '@/types';
import { to } from '@/utils/promise';
import { type SelectProps } from 'antd';
import { useCallback, useEffect, useState } from 'react';
// import { getComputingResourceReq } from '@/services/pipeline';
// import { ComputingResource } from '@/types';
// import { to } from '@/utils/promise';
// import { type SelectProps } from 'antd';
// import { useCallback, useEffect, useState } from 'react';

const computingResource: ComputingResource[] = [];
// const computingResource: ComputingResource[] = [];

/** 过滤资源规格 */
export const filterResourceStandard: SelectProps<string, ComputingResource>['filterOption'] = (
input: string,
option?: ComputingResource,
) => {
return (
option?.computing_resource?.toLocaleLowerCase()?.includes(input.toLocaleLowerCase()) ?? false
);
};
// /** 过滤资源规格 */
// export const filterResourceStandard: SelectProps<string, ComputingResource>['filterOption'] = (
// input: string,
// option?: ComputingResource,
// ) => {
// return (
// option?.computing_resource?.toLocaleLowerCase()?.includes(input.toLocaleLowerCase()) ?? false
// );
// };

/** 资源规格字段 */
export const resourceFieldNames = {
label: 'description',
value: 'id',
};
// /** 资源规格字段 */
// export const resourceFieldNames = {
// label: 'description',
// value: 'id',
// };

/** 获取资源规格 */
export function useComputingResource() {
const [resourceStandardList, setResourceStandardList] = useState<ComputingResource[]>([]);
// /** 获取资源规格 */
// export function useComputingResource() {
// const [resourceStandardList, setResourceStandardList] = useState<ComputingResource[]>([]);

useEffect(() => {
// 获取资源规格列表数据
const getComputingResource = async () => {
const params = {
page: 0,
size: 1000,
resource_type: '',
};
const [res] = await to(getComputingResourceReq(params));
if (res && res.data && Array.isArray(res.data.content)) {
setResourceStandardList(res.data.content);
computingResource.splice(0, computingResource.length, ...res.data.content);
}
};
// useEffect(() => {
// // 获取资源规格列表数据
// const getComputingResource = async () => {
// const params = {
// page: 0,
// size: 1000,
// resource_type: '',
// };
// const [res] = await to(getComputingResourceReq(params));
// if (res && res.data && Array.isArray(res.data.content)) {
// setResourceStandardList(res.data.content);
// computingResource.splice(0, computingResource.length, ...res.data.content);
// }
// };

// if (computingResource.length > 0) {
// setResourceStandardList(computingResource);
// } else {
// getComputingResource();
// }
// }, []);

if (computingResource.length > 0) {
setResourceStandardList(computingResource);
} else {
getComputingResource();
}
// // 根据 standard 获取 description
// const getDescription = useCallback(
// (id?: string | number) => {
// if (!id) {
// return undefined;
// }
// return resourceStandardList.find((item) => Number(item.id) === Number(id))?.description;
// },
// [resourceStandardList],
// );

// return [resourceStandardList, getDescription] as const;
// }

import state, { getSystemResources } from '@/state/systemResource';
import { useSnapshot } from '@umijs/max';
import { useCallback, useEffect } from 'react';

export const useSystemResource = () => {
useEffect(() => {
getSystemResources();
}, []);

// 根据 standard 获取 description
const snap = useSnapshot(state);
/* 根据 standard 获取 description */
const getDescription = useCallback(
(id?: string | number) => {
if (!id) {
return undefined;
}
return resourceStandardList.find((item) => Number(item.id) === Number(id))?.description;
return snap.resources.find((item) => Number(item.id) === Number(id))?.description;
},
[resourceStandardList],
[snap.resources],
);

return [resourceStandardList, getDescription] as const;
}
return getDescription;
};

+ 22
- 10
react-ui/src/hooks/useSSE.ts View File

@@ -1,11 +1,24 @@
import { parseJsonText } from '@/utils';
import { useEffect } from 'react';
import { ExperimentStatus } from '@/enums';
import { NodeStatus } from '@/types';
import { parseJsonText } from '@/utils';
import { useEffect } from 'react';

export type MessageHandler = (experimentInsId: number, status: string, finishedAt: string, nodes: Record<string, NodeStatus>) => void
export const useSSE = (experimentInsId: number, status: ExperimentStatus, name: string, namespace: string, onMessage: MessageHandler) => {
const isRunning = status === ExperimentStatus.Pending || status === ExperimentStatus.Running
export type MessageHandler = (
experimentId: number,
experimentInsId: number,
status: string,
finishTime: string,
nodes: Record<string, NodeStatus>,
) => void;
export const useSSE = (
experimentId: number,
experimentInsId: number,
status: ExperimentStatus,
name: string,
namespace: string,
onMessage: MessageHandler,
) => {
const isRunning = status === ExperimentStatus.Pending || status === ExperimentStatus.Running;
useEffect(() => {
if (isRunning) {
const { origin } = location;
@@ -22,8 +35,8 @@ export const useSSE = (experimentInsId: number, status: ExperimentStatus, name:
const dataJson = parseJsonText(data);
const statusData = dataJson?.result?.object?.status;
if (statusData) {
const { finishedAt, phase, nodes } = statusData;
onMessage(experimentInsId, phase, finishedAt, nodes);
const { finishedAt, phase, nodes } = statusData;
onMessage(experimentId, experimentInsId, phase, finishedAt, nodes);
}
};

@@ -33,8 +46,7 @@ export const useSSE = (experimentInsId: number, status: ExperimentStatus, name:

return () => {
evtSource.close();
}
};
}
}, [experimentInsId, isRunning, name, namespace, onMessage]);
}, [experimentId, experimentInsId, isRunning, name, namespace, onMessage]);
};

+ 21
- 24
react-ui/src/overrides.less View File

@@ -220,35 +220,32 @@
height: 100%;
.ant-pro-grid-content-children {
height: 100%;
.ant-pro-layout-watermark-wrapper {
.ant-pro-page-container-children-container {
height: 100%;
.ant-pro-page-container-children-container {
padding: 0;
.ant-pro-table {
display: flex;
flex-direction: column;
height: 100%;
padding: 0;
.ant-pro-table {
display: flex;
flex-direction: column;
height: 100%;
.ant-pro-card.ant-pro-table-search {
flex: none;
height: auto;
}
.ant-pro-card {
flex: 1;
min-height: 0;
.ant-pro-card-body {
height: 100%;
.ant-table-wrapper {
height: calc(100% - 64px);
.ant-spin-nested-loading {
.ant-pro-card.ant-pro-table-search {
flex: none;
height: auto;
}
.ant-pro-card {
flex: 1;
min-height: 0;
.ant-pro-card-body {
height: 100%;
.ant-table-wrapper {
height: calc(100% - 64px - 64px);
.ant-spin-nested-loading {
height: 100%;
.ant-spin-container {
height: 100%;
.ant-spin-container {
.ant-table-fixed-header {
height: 100%;
.ant-table-fixed-header {
.ant-table-container {
height: 100%;
.ant-table-container {
height: 100%;
}
}
}
}


+ 2
- 1
react-ui/src/pages/404.tsx View File

@@ -1,4 +1,5 @@
import KFEmpty, { EmptyType } from '@/components/KFEmpty';
import { HomeUrl } from '@/utils/constant';
import { useNavigate } from '@umijs/max';

const NoFoundPage = () => {
@@ -12,7 +13,7 @@ const NoFoundPage = () => {
content={'很抱歉,您访问的页面地址有误,\n或者该页面不存在。'}
hasFooter={true}
buttonTitle="返回首页"
onButtonClick={() => navigate('/')}
onButtonClick={() => navigate(HomeUrl)}
></KFEmpty>
);
};


+ 8
- 10
react-ui/src/pages/ActiveLearn/Instance/index.tsx View File

@@ -51,13 +51,12 @@ function ActiveLearnInstance() {
const [res] = await to(getActiveLearnInsReq(instanceId));
if (res && res.data) {
const info = res.data as ActiveLearnInstanceData;
const { param, node_status, argo_ins_name, argo_ins_ns, status, create_time } = info;
const { param, node_status, argo_ins_name, argo_ins_ns, status } = info;
// 解析配置参数
const paramJson = parseJsonText(param);
if (paramJson) {
setExperimentInfo({
...paramJson.data,
create_time,
});
}

@@ -69,7 +68,7 @@ function ActiveLearnInstance() {
return;
}

// 进行节点状态
// 设置总 workflow 状态
const nodeStatusJson = parseJsonText(node_status);
if (nodeStatusJson) {
setNodes(nodeStatusJson);
@@ -106,18 +105,17 @@ function ActiveLearnInstance() {
if (dataJson) {
const nodes = dataJson?.result?.object?.status?.nodes;
if (nodes) {
// 节点
// 设置节点
setNodes(nodes);

// 设置总 workflow 状态
const workflowStatus = Object.values(nodes).find((node: any) =>
node.displayName.startsWith(NodePrefix),
) as NodeStatus;

// 设置工作流状态
if (workflowStatus) {
setWorkflowStatus(workflowStatus);

// 实验结束,关闭 SSE
// 实验结束,关闭 SSE,获取实验实例结果
if (
workflowStatus.phase !== ExperimentStatus.Pending &&
workflowStatus.phase !== ExperimentStatus.Running
@@ -152,8 +150,8 @@ function ActiveLearnInstance() {
<ActiveLearnBasic
className={styles['active-learn-instance__basic']}
info={experimentInfo}
runStatus={workflowStatus}
instanceStatus={instanceInfo?.status}
workflowStatus={workflowStatus}
instanceStatus={instanceInfo?.status as ExperimentStatus}
isInstance
/>
),
@@ -181,7 +179,7 @@ function ActiveLearnInstance() {
},
{
key: TabKeys.History,
label: '训练列表',
label: '运行列表',
icon: <KFIcon type="icon-Trialliebiao" />,
children: (
<ExperimentHistory


+ 7
- 11
react-ui/src/pages/ActiveLearn/components/ActiveLearnBasic/index.tsx View File

@@ -1,6 +1,6 @@
import ConfigInfo, { type BasicInfoData } from '@/components/ConfigInfo';
import { AutoMLTaskType, autoMLTaskTypeOptions, ExperimentStatus } from '@/enums';
import { useComputingResource } from '@/hooks/useComputingResource';
import { useSystemResource } from '@/hooks/useComputingResource';
import {
classifierAlgorithms,
FrameworkType,
@@ -28,18 +28,18 @@ type BasicInfoProps = {
info?: ActiveLearnData;
className?: string;
isInstance?: boolean;
runStatus?: NodeStatus;
workflowStatus?: NodeStatus;
instanceStatus?: ExperimentStatus;
};

function BasicInfo({
info,
className,
runStatus,
workflowStatus,
instanceStatus,
isInstance = false,
}: BasicInfoProps) {
const getResourceDescription = useComputingResource()[1];
const getResourceDescription = useSystemResource();
const basicDatas: BasicInfoData[] = useMemo(() => {
if (!info) {
return [];
@@ -154,7 +154,7 @@ function BasicInfo({
value: info.dataset_py,
},
{
label: '数据集类名',
label: '数据集处理类名',
value: info.dataset_class_name,
},
{
@@ -212,12 +212,8 @@ function BasicInfo({

return (
<div className={classNames(styles['active-learn-basic'], className)}>
{isInstance && runStatus && (
<ExperimentRunBasic
create_time={info?.create_time}
runStatus={runStatus}
instanceStatus={instanceStatus}
/>
{isInstance && workflowStatus && (
<ExperimentRunBasic workflowStatus={workflowStatus} instanceStatus={instanceStatus} />
)}
{!isInstance && (
<ConfigInfo


+ 14
- 4
react-ui/src/pages/ActiveLearn/components/CreateForm/ExecuteConfig.tsx View File

@@ -17,6 +17,11 @@ import {

function ExecuteConfig() {
const form = Form.useFormInstance();
const task_type = Form.useWatch('task_type', form);
const queryStrategiesOptions =
task_type === AutoMLTaskType.Classification
? queryStrategies.slice(0, 2)
: queryStrategies.slice(2);
return (
<>
<SubAreaTitle
@@ -101,16 +106,16 @@ function ExecuteConfig() {
<Row gutter={8}>
<Col span={10}>
<Form.Item
label="数据集类名"
label="数据集处理类名"
name="dataset_class_name"
rules={[
{
required: true,
message: '请输入数据集类名',
message: '请输入数据集处理类名',
},
]}
>
<Input placeholder="请输入数据集类名" maxLength={64} showCount allowClear />
<Input placeholder="请输入数据集处理类名" maxLength={64} showCount allowClear />
</Form.Item>
</Col>
</Row>
@@ -488,7 +493,12 @@ function ExecuteConfig() {
},
]}
>
<Select placeholder="请选择查询策略" options={queryStrategies} showSearch allowClear />
<Select
placeholder="请选择查询策略"
options={queryStrategiesOptions}
showSearch
allowClear
/>
</Form.Item>
</Col>
</Row>


+ 4
- 0
react-ui/src/pages/ActiveLearn/components/CreateForm/utils.ts View File

@@ -87,4 +87,8 @@ export const queryStrategies = [
label: 'upper_confidence_bound',
value: 'upper_confidence_bound',
},
{
label: 'probability_of_improvement',
value: 'probability_of_improvement',
},
];

+ 7
- 2
react-ui/src/pages/ActiveLearn/components/ExperimentLog/index.tsx View File

@@ -1,5 +1,6 @@
import { ExperimentStatus } from '@/enums';
import { ActiveLearnInstanceData } from '@/pages/ActiveLearn/types';
import EmptyLog from '@/pages/AutoML/components/ExperimentLog/empty';
import LogList from '@/pages/Experiment/components/LogList';
import { NodeStatus } from '@/types';
import { Tabs } from 'antd';
@@ -64,7 +65,7 @@ function ExperimentLog({ instanceInfo, nodes }: ExperimentLogProps) {
// icon: <KFIcon type="icon-rizhi1" />,
children: (
<div className={styles['experiment-log__tabs__log']}>
{trainCloneNodeStatus && (
{trainCloneNodeStatus ? (
<LogList
instanceName={instanceInfo.argo_ins_name}
instanceNamespace={instanceInfo.argo_ins_ns}
@@ -73,6 +74,8 @@ function ExperimentLog({ instanceInfo, nodes }: ExperimentLogProps) {
instanceNodeStartTime={trainCloneNodeStatus.startedAt}
instanceNodeStatus={trainCloneNodeStatus.phase as ExperimentStatus}
></LogList>
) : (
<EmptyLog />
)}
</div>
),
@@ -83,7 +86,7 @@ function ExperimentLog({ instanceInfo, nodes }: ExperimentLogProps) {
// icon: <KFIcon type="icon-rizhi1" />,
children: (
<div className={styles['experiment-log__tabs__log']}>
{hpoNodeStatus && (
{hpoNodeStatus ? (
<LogList
instanceName={instanceInfo.argo_ins_name}
instanceNamespace={instanceInfo.argo_ins_ns}
@@ -92,6 +95,8 @@ function ExperimentLog({ instanceInfo, nodes }: ExperimentLogProps) {
instanceNodeStartTime={hpoNodeStatus.startedAt}
instanceNodeStatus={hpoNodeStatus.phase as ExperimentStatus}
></LogList>
) : (
<EmptyLog />
)}
</div>
),


+ 6
- 1
react-ui/src/pages/Authorize/index.tsx View File

@@ -1,6 +1,7 @@
import { setSessionToken } from '@/access';
import { loginByOauth2Req } from '@/services/auth';
import { to } from '@/utils/promise';
import SessionStorage from '@/utils/sessionStorage';
import { history, useModel, useSearchParams } from '@umijs/max';
import { message } from 'antd';
import { useCallback, useEffect } from 'react';
@@ -36,7 +37,11 @@ function Authorize() {
setSessionToken(access_token, access_token, expires_in);
message.success('登录成功!');
await fetchUserInfo();
history.push(redirect || '/');
const redierctUrl = SessionStorage.getItem(SessionStorage.redirectUrl);
console.log('redirect', redirect);
console.log('redierctUrl', redierctUrl);
history.replace(redirect || redierctUrl || '/workspace');
// SessionStorage.removeItem(SessionStorage.redirectUrl);
}
}, [fetchUserInfo, redirect, code]);



+ 11
- 9
react-ui/src/pages/AutoML/Instance/index.tsx View File

@@ -51,7 +51,7 @@ function AutoMLInstance() {
const [res] = await to(getExperimentInsReq(instanceId));
if (res && res.data) {
const info = res.data as AutoMLInstanceData;
const { param, node_status, argo_ins_name, argo_ins_ns, status, create_time, type } = info;
const { param, node_status, argo_ins_name, argo_ins_ns, status, type } = info;

setType(type);
// 解析配置参数
@@ -59,7 +59,6 @@ function AutoMLInstance() {
if (paramJson) {
setAutoMLInfo({
...paramJson.data,
create_time,
type,
});
}
@@ -95,7 +94,10 @@ function AutoMLInstance() {
};

const setupSSE = (name: string, namespace: string) => {
const { origin } = location;
let { origin } = location;
if (process.env.NODE_ENV === 'development') {
origin = 'http://172.20.32.235:31213';
}
const params = encodeURIComponent(`metadata.namespace=${namespace},metadata.name=${name}`);
const evtSource = new EventSource(
`${origin}/api/v1/realtimeStatus?listOptions.fieldSelector=${params}`,
@@ -110,17 +112,17 @@ function AutoMLInstance() {
if (dataJson) {
const nodes = dataJson?.result?.object?.status?.nodes;
if (nodes) {
// 节点
// 设置节点
setNodes(nodes);

// 设置总 workflow 状态
const workflowStatus = Object.values(nodes).find((node: any) =>
node.displayName.startsWith(NodePrefix),
) as NodeStatus;

if (workflowStatus) {
setWorkflowStatus(workflowStatus);

// 实验结束,关闭 SSE
// 实验结束,关闭 SSE,获取实验实例结果
if (
workflowStatus.phase !== ExperimentStatus.Pending &&
workflowStatus.phase !== ExperimentStatus.Running
@@ -155,8 +157,8 @@ function AutoMLInstance() {
<AutoMLBasic
className={styles['auto-ml-instance__basic']}
info={autoMLInfo}
runStatus={workflowStatus}
instanceStatus={instanceInfo?.status}
workflowStatus={workflowStatus}
instanceStatus={instanceInfo?.status as ExperimentStatus}
isInstance
/>
),
@@ -202,7 +204,7 @@ function AutoMLInstance() {
}
: {
key: TabKeys.History,
label: '试验列表',
label: '运行列表',
icon: <KFIcon type="icon-Trialliebiao" />,
children: (
<ExperimentHistory


+ 1
- 1
react-ui/src/pages/AutoML/List/index.tsx View File

@@ -1,7 +1,7 @@
/*
* @Author: 赵伟
* @Date: 2024-04-16 13:58:08
* @Description: 自机器学习列表
* @Description: 自机器学习列表
*/

import ExperimentList, { ExperimentListType } from '../components/ExperimentList';


+ 46
- 11
react-ui/src/pages/AutoML/components/AutoMLBasic/index.tsx View File

@@ -6,11 +6,22 @@ import {
autoMLEnsembleClassOptions,
autoMLTaskTypeOptions,
} from '@/enums';
import { useComputingResource } from '@/hooks/useComputingResource';
import { useSystemResource } from '@/hooks/useComputingResource';
import {
classificationAlgorithms,
featureAlgorithms,
regressorAlgorithms,
} from '@/pages/AutoML/components/CreateForm/utils';
import { AutoMLData } from '@/pages/AutoML/types';
import { type NodeStatus } from '@/types';
import { parseJsonText } from '@/utils';
import { formatBoolean, formatDataset, formatDate, formatEnum } from '@/utils/format';
import {
formatBoolean,
formatDataset,
formatDate,
formatEnum,
type EnumOptions,
} from '@/utils/format';
import classNames from 'classnames';
import { useMemo } from 'react';
import ExperimentRunBasic from '../ExperimentRunBasic';
@@ -21,6 +32,7 @@ const formatOptimizeMode = (value: boolean) => {
return value ? '越大越好' : '越小越好';
};

// 格式化权重
const formatMetricsWeight = (value: string) => {
if (!value) {
return '--';
@@ -34,22 +46,37 @@ const formatMetricsWeight = (value: string) => {
.join('\n');
};

// 格式化算法
const formatAlgorithm = (algorithms: EnumOptions[]) => {
return (value: string) => {
if (!value) {
return '--';
}
const list = value
.split(',')
.filter((v) => v !== '')
.map((v) => v.trim());
return list.map((v) => formatEnum(algorithms)(v)).join(',');
};
};

type AutoMLBasicProps = {
info?: AutoMLData;
className?: string;
isInstance?: boolean;
runStatus?: NodeStatus;
workflowStatus?: NodeStatus;
instanceStatus?: ExperimentStatus;
instanceCreateTime?: string;
};

function AutoMLBasic({
info,
className,
runStatus,
workflowStatus,
instanceStatus,
isInstance = false,
}: AutoMLBasicProps) {
const getResourceDescription = useComputingResource()[1];
const getResourceDescription = useSystemResource();
const basicDatas: BasicInfoData[] = useMemo(() => {
if (!info) {
return [];
@@ -95,10 +122,12 @@ function AutoMLBasic({
{
label: '特征预处理算法',
value: info.include_feature_preprocessor,
format: formatAlgorithm(featureAlgorithms),
},
{
label: '排除的特征预处理算法',
value: info.exclude_feature_preprocessor,
format: formatAlgorithm(featureAlgorithms),
},
{
label: info.task_type === AutoMLTaskType.Regression ? '回归算法' : '分类算法',
@@ -106,6 +135,11 @@ function AutoMLBasic({
info.task_type === AutoMLTaskType.Regression
? info.include_regressor
: info.include_classifier,
format: formatAlgorithm(
info.task_type === AutoMLTaskType.Regression
? regressorAlgorithms
: classificationAlgorithms,
),
},
{
label: info.task_type === AutoMLTaskType.Regression ? '排除的回归算法' : '排除的分类算法',
@@ -113,6 +147,11 @@ function AutoMLBasic({
info.task_type === AutoMLTaskType.Regression
? info.exclude_regressor
: info.exclude_classifier,
format: formatAlgorithm(
info.task_type === AutoMLTaskType.Regression
? regressorAlgorithms
: classificationAlgorithms,
),
},
{
label: '集成方式',
@@ -292,12 +331,8 @@ function AutoMLBasic({

return (
<div className={classNames(styles['auto-ml-basic'], className)}>
{isInstance && runStatus && (
<ExperimentRunBasic
create_time={info?.create_time}
runStatus={runStatus}
instanceStatus={instanceStatus}
/>
{isInstance && workflowStatus && (
<ExperimentRunBasic workflowStatus={workflowStatus} instanceStatus={instanceStatus} />
)}
{!isInstance && (
<ConfigInfo


+ 6
- 67
react-ui/src/pages/AutoML/components/CreateForm/ExecuteConfig.tsx View File

@@ -8,69 +8,7 @@ import {
autoMLTaskTypeOptions,
} from '@/enums';
import { Col, Form, InputNumber, Radio, Row, Select, Switch } from 'antd';

// 分类算法
const classificationAlgorithms = [
'adaboost',
'bernoulli_nb',
'decision_tree',
'extra_trees',
'gaussian_nb',
'gradient_boosting',
'k_nearest_neighbors',
'lda',
'liblinear_svc',
'libsvm_svc',
'mlp',
'multinomial_nb',
'passive_aggressive',
'qda',
'random_forest',
'sgd',
'LightGBMClassification',
'XGBoostClassification',
'StackingClassification',
].map((name) => ({ label: name, value: name }));

// 回归算法
const regressorAlgorithms = [
'adaboost',
'ard_regression',
'decision_tree',
'extra_trees',
'gaussian_process',
'gradient_boosting',
'k_nearest_neighbors',
'liblinear_svr',
'libsvm_svr',
'mlp',
'random_forest',
'sgd',
'LightGBMRegression',
'XGBoostRegression',
].map((name) => ({ label: name, value: name }));

// 特征预处理算法
const featureAlgorithms = [
'densifier',
'extra_trees_preproc_for_classification',
'extra_trees_preproc_for_regression',
'fast_ica',
'feature_agglomeration',
'kernel_pca',
'kitchen_sinks',
'liblinear_svc_preprocessor',
'no_preprocessing',
'nystroem_sampler',
'pca',
'polynomial',
'random_trees_embedding',
'select_percentile_classification',
'select_percentile_regression',
'select_rates_classification',
'select_rates_regression',
'truncatedSVD',
].map((name) => ({ label: name, value: name }));
import { classificationAlgorithms, featureAlgorithms, regressorAlgorithms } from './utils';

// 分类指标
export const classificationMetrics = [
@@ -280,9 +218,9 @@ function ExecuteConfig() {
<Form.Item
label="集成模型数量"
name="ensemble_size"
tooltip="集成模型数量,如果设置为0,则没有集成。默认50"
tooltip="集成模型数量,必须是大于等于1的整数,默认50"
>
<InputNumber placeholder="请输入集成模型数量" min={0} precision={0} />
<InputNumber placeholder="请输入集成模型数量" min={1} precision={0} />
</Form.Item>
</Col>
</Row>
@@ -292,7 +230,7 @@ function ExecuteConfig() {
<Form.Item
label="集成最佳模型数量"
name="ensemble_nbest"
tooltip="仅集成最佳的N个模型"
tooltip="仅集成最佳的N个模型,必须是大于等于1的整数"
>
<InputNumber placeholder="请输入集成最佳模型数量" min={1} precision={0} />
</Form.Item>
@@ -419,6 +357,7 @@ function ExecuteConfig() {
<Form.Item
label="交叉验证折数"
name="folds"
tooltip="交叉验证折数必须是大于等于2的整数"
rules={[
{
required: true,
@@ -426,7 +365,7 @@ function ExecuteConfig() {
},
]}
>
<InputNumber placeholder="请输入交叉验证折数" min={1} precision={0} />
<InputNumber placeholder="请输入交叉验证折数" min={2} precision={0} />
</Form.Item>
</Col>
</Row>


+ 85
- 0
react-ui/src/pages/AutoML/components/CreateForm/utils.ts View File

@@ -0,0 +1,85 @@
// 分类算法
export const classificationAlgorithms = [
{ label: 'adaboost (自适应提升算法)', value: 'adaboost' },
{ label: 'bernoulli_nb (伯努利朴素贝叶斯)', value: 'bernoulli_nb' },
{ label: 'decision_tree (决策树)', value: 'decision_tree' },
{ label: 'extra_trees (极端随机树)', value: 'extra_trees' },
{ label: 'gaussian_nb (高斯朴素贝叶斯)', value: 'gaussian_nb' },
{ label: 'gradient_boosting (梯度提升)', value: 'gradient_boosting' },
{ label: 'k_nearest_neighbors (k近邻)', value: 'k_nearest_neighbors' },
{ label: 'lda (线性判别分析)', value: 'lda' },
{ label: 'liblinear_svc (liblinear支持向量分类)', value: 'liblinear_svc' },
{ label: 'libsvm_svc (libsvm支持向量分类)', value: 'libsvm_svc' },
{ label: 'mlp (多层感知器)', value: 'mlp' },
{ label: 'multinomial_nb (多项式朴素贝叶斯)', value: 'multinomial_nb' },
{ label: 'passive_aggressive (被动攻击算法)', value: 'passive_aggressive' },
{ label: 'qda (二次判别式分析)', value: 'qda' },
{ label: 'random_forest (随机森林)', value: 'random_forest' },
{ label: 'sgd (随机梯度下降)', value: 'sgd' },
{ label: 'tablenet (表格网络)', value: 'tablenet' },
{ label: 'LightGBMClassification (轻量梯度提升机分类)', value: 'LightGBMClassification' },
{ label: 'XGBoostClassification (极端梯度提升机分类)', value: 'XGBoostClassification' },
{ label: 'StackingClassification (堆叠泛化)', value: 'StackingClassification' },
];

// 回归算法
export const regressorAlgorithms = [
{ label: 'adaboost (自适应提升算法)', value: 'adaboost' },
{ label: 'ard_regression (自动相关性确定回归)', value: 'ard_regression' },
{ label: 'decision_tree (决策树)', value: 'decision_tree' },
{ label: 'extra_trees (极端随机树)', value: 'extra_trees' },
{ label: 'gaussian_process (高斯过程回归)', value: 'gaussian_process' },
{ label: 'gradient_boosting (梯度提升)', value: 'gradient_boosting' },
{ label: 'k_nearest_neighbors (梯度提升)', value: 'k_nearest_neighbors' },
{ label: 'liblinear_svr (liblinear支持向量回归)', value: 'liblinear_svr' },
{ label: 'libsvm_svr (libsvm支持向量回归)', value: 'libsvm_svr' },
{ label: 'mlp (多层感知器)', value: 'mlp' },
{ label: 'random_forest (随机森林)', value: 'random_forest' },
{ label: 'sgd (随机梯度下降)', value: 'sgd' },
{ label: 'LightGBMRegression (轻量梯度提升机回归)', value: 'LightGBMRegression' },
{ label: 'XGBoostRegression (极端梯度提升机回归)', value: 'XGBoostRegression' },
];

// 特征预处理算法
export const featureAlgorithms = [
{ label: 'densifier (特征变换-数据增稠)', value: 'densifier' },
{
label: 'extra_trees_preproc_for_classification (特征选择-分类任务极端随机树)',
value: 'extra_trees_preproc_for_classification',
},
{
label: 'extra_trees_preproc_for_regression (特征选择-回归任务极端随机树)',
value: 'extra_trees_preproc_for_regression',
},
{ label: 'fast_ica (特征选择-快速独立成分分析)', value: 'fast_ica' },
{ label: 'feature_agglomeration (特征变换-特征聚合)', value: 'feature_agglomeration' },
{ label: 'kernel_pca (特征选择-核主成分分析)', value: 'kernel_pca' },
{ label: 'kitchen_sinks (特征变换-随机特征映射)', value: 'kitchen_sinks' },
{
label: 'liblinear_svc_preprocessor (特征选择-线性svc预处理器)',
value: 'liblinear_svc_preprocessor',
},
{ label: 'miss_value_impute (缺失值填充)', value: 'miss_value_impute' },
{ label: 'no_preprocessing (无预处理)', value: 'no_preprocessing' },
{ label: 'nystroem_sampler (特征变换-尼斯特罗姆采样器)', value: 'nystroem_sampler' },
{ label: 'pca (特征选择-主成分分析)', value: 'pca' },
{ label: 'polynomial (特征变换-多项式特征扩展)', value: 'polynomial' },
{ label: 'random_trees_embedding (特征变换-随机森林特征嵌入)', value: 'random_trees_embedding' },
{
label: 'select_percentile_classification 特征选择-基于百分位的分类特征选择)',
value: 'select_percentile_classification',
},
{
label: 'select_percentile_regression (特征选择-基于百分位的回归特征选择)',
value: 'select_percentile_regression',
},
{
label: 'select_rates_classification (特征选择-基于比率的分类特征选择)',
value: 'select_rates_classification',
},
{
label: 'select_rates_regression (特征选择-基于比率的回归特征选择)',
value: 'select_rates_regression',
},
{ label: 'truncatedSVD (特征变换-截断奇异值分解)', value: 'truncatedSVD' },
];

+ 0
- 4
react-ui/src/pages/AutoML/components/ExperimentInstanceList/index.less View File

@@ -54,10 +54,6 @@
display: flex;
align-items: center;
width: 200px;

.statusIcon {
visibility: visible;
}
}
}



+ 16
- 8
react-ui/src/pages/AutoML/components/ExperimentInstanceList/index.tsx View File

@@ -34,7 +34,14 @@ function ExperimentInstanceList({
}: ExperimentInstanceListProps) {
const { message } = App.useApp();
const allIntanceIds = useMemo(() => {
return experimentInsList?.map((item) => item.id) || [];
return (
experimentInsList
?.filter(
(item) =>
item.status !== ExperimentStatus.Running && item.status !== ExperimentStatus.Pending,
)
.map((item) => item.id) || []
);
}, [experimentInsList]);
const [
selectedIns,
@@ -126,7 +133,12 @@ function ExperimentInstanceList({
<div>
<div className={styles.tableExpandBox} style={{ paddingBottom: '16px' }}>
<div className={styles.check}>
<Checkbox checked={checked} indeterminate={indeterminate} onChange={checkAll}></Checkbox>
<Checkbox
checked={checked}
indeterminate={indeterminate}
disabled={allIntanceIds.length === 0}
onChange={checkAll}
></Checkbox>
</div>
<div className={styles.index}>序号</div>
<div className={styles.description}>运行时长</div>
@@ -171,12 +183,8 @@ function ExperimentInstanceList({
{index + 1}
</a>
<ExperimentInstanceComponent
create_time={item.create_time}
finish_time={item.finish_time}
status={item.status as ExperimentStatus}
argo_ins_name={item.argo_ins_name}
argo_ins_ns={item.argo_ins_ns}
experimentInsId={item.id}
experimentId={item[config['idInsProperty'] as keyof ExperimentInstance] as number}
instance={item}
></ExperimentInstanceComponent>
<div className={styles.operation}>
<Button


+ 35
- 32
react-ui/src/pages/AutoML/components/ExperimentInstanceList/instance.tsx View File

@@ -2,67 +2,70 @@ import RunDuration from '@/components/RunDuration';
import { ExperimentStatus } from '@/enums';
import { useSSE, type MessageHandler } from '@/hooks/useSSE';
import { experimentStatusInfo } from '@/pages/Experiment/status';
import { ExperimentInstance, NodeStatus } from '@/types';
import { ExperimentCompleted } from '@/utils/constant';
import { formatDate } from '@/utils/date';
import { getWorkflowStatus } from '@/utils/experiment';
import { Typography } from 'antd';
import React, { useCallback } from 'react';
import styles from './index.less';

type ExperimentInstanceProps = {
create_time?: string;
finish_time?: string;
status: ExperimentStatus;
argo_ins_name: string;
argo_ins_ns: string;
experimentInsId: number;
type ExperimentInstanceComponentProps = {
experimentId: number;
instance: ExperimentInstance;
};

function ExperimentInstance({
create_time,
finish_time,
status,
argo_ins_name,
argo_ins_ns,
experimentInsId,
}: ExperimentInstanceProps) {
function ExperimentInstanceComponent({ experimentId, instance }: ExperimentInstanceComponentProps) {
const { id, argo_ins_name, argo_ins_ns, node_status } = instance;
const workflowStatus = getWorkflowStatus(node_status) as NodeStatus | undefined;
const status = instance.status as ExperimentStatus;
const createTime = workflowStatus?.startedAt;
const finishTime = workflowStatus?.finishedAt;
const statusInfo = experimentStatusInfo[status];
const handleSSEMessage: MessageHandler = useCallback(
(experimentInsId: number, status: string, finish_time: string) => {
(experimentId: number, experimentInsId: number, status: string, finishTime: string) => {
window.postMessage({
type: ExperimentCompleted,
payload: {
id: experimentInsId,
experimentId,
experimentInsId,
status,
finish_time,
finishTime,
},
});
},
[],
);
useSSE(experimentInsId, status, argo_ins_name, argo_ins_ns, handleSSEMessage);
useSSE(experimentId, id, status, argo_ins_name, argo_ins_ns, handleSSEMessage);

return (
<React.Fragment>
<div className={styles.description}>
<RunDuration createTime={create_time} finishTime={finish_time} />
<RunDuration createTime={createTime} finishTime={finishTime} />
</div>
<div className={styles.startTime}>
<Typography.Text ellipsis={{ tooltip: formatDate(create_time) }}>
{formatDate(create_time)}
<Typography.Text ellipsis={{ tooltip: formatDate(createTime) }}>
{formatDate(createTime)}
</Typography.Text>
</div>
<div className={styles.statusBox}>
<img
style={{ width: '17px', marginRight: '7px' }}
src={experimentStatusInfo[status]?.icon}
draggable={false}
alt=""
/>
<span style={{ color: experimentStatusInfo[status]?.color }} className={styles.statusIcon}>
{experimentStatusInfo[status]?.label}
</span>
{statusInfo ? (
<>
<img
style={{ width: '17px', marginRight: '7px' }}
src={statusInfo.icon}
draggable={false}
alt=""
/>
<span style={{ color: statusInfo.color }}>{statusInfo.label}</span>
</>
) : (
'--'
)}
</div>
</React.Fragment>
);
}

export default ExperimentInstance;
export default ExperimentInstanceComponent;

+ 14
- 3
react-ui/src/pages/AutoML/components/ExperimentList/config.ts View File

@@ -8,6 +8,7 @@ import {
batchDeleteActiveLearnInsReq,
deleteActiveLearnInsReq,
deleteActiveLearnReq,
editActiveLearnInsReq,
getActiveLearnInsListReq,
getActiveLearnListReq,
runActiveLearnReq,
@@ -17,6 +18,7 @@ import {
batchDeleteExperimentInsReq,
deleteAutoMLReq,
deleteExperimentInsReq,
editExperimentInsReq,
getAutoMLListReq,
getExperimentInsListReq,
runAutoMLReq,
@@ -26,6 +28,7 @@ import {
batchDeleteRayInsReq,
deleteRayInsReq,
deleteRayReq,
editRayInsReq,
getRayInsListReq,
getRayListReq,
runRayReq,
@@ -39,18 +42,20 @@ export enum ExperimentListType {
}

type ExperimentListInfo = {
getListReq: (params: any) => Promise<any>; // 获取列表
getInsListReq: (params: any) => Promise<any>; // 获取实例列表
getListReq: (params: any, skipLoading?: boolean) => Promise<any>; // 获取列表
getInsListReq: (params: any, skipLoading?: boolean) => Promise<any>; // 获取实例列表
deleteRecordReq: (params: any) => Promise<any>; // 删除
runRecordReq: (params: any) => Promise<any>; // 运行
deleteInsReq: (params: any) => Promise<any>; // 删除实例
batchDeleteInsReq: (params: any) => Promise<any>; // 批量删除实例
stopInsReq: (params: any) => Promise<any>; // 终止实例
editInsReq: (params: any) => Promise<any>; // 编辑实例
title: string; // 标题
pathPrefix: string; // 路由路径前缀
idProperty: string; // ID属性
nameProperty: string; // 名称属性
descProperty: string; // 描述属性
idInsProperty: string; // 实例返回的ID属性
};

export const experimentListConfig: Record<ExperimentListType, ExperimentListInfo> = {
@@ -62,11 +67,13 @@ export const experimentListConfig: Record<ExperimentListType, ExperimentListInfo
deleteInsReq: deleteExperimentInsReq,
batchDeleteInsReq: batchDeleteExperimentInsReq,
stopInsReq: stopExperimentInsReq,
title: '自主机器学习',
editInsReq: editExperimentInsReq,
title: '自动机器学习',
pathPrefix: 'automl',
nameProperty: 'name',
descProperty: 'description',
idProperty: 'machineLearnId',
idInsProperty: 'machine_learn_id',
},
[ExperimentListType.HyperParameter]: {
getListReq: getRayListReq,
@@ -76,11 +83,13 @@ export const experimentListConfig: Record<ExperimentListType, ExperimentListInfo
deleteInsReq: deleteRayInsReq,
batchDeleteInsReq: batchDeleteRayInsReq,
stopInsReq: stopRayInsReq,
editInsReq: editRayInsReq,
title: '超参数自动寻优',
pathPrefix: 'hyperparameter',
nameProperty: 'name',
descProperty: 'description',
idProperty: 'rayId',
idInsProperty: 'ray_id',
},
[ExperimentListType.ActiveLearn]: {
getListReq: getActiveLearnListReq,
@@ -90,10 +99,12 @@ export const experimentListConfig: Record<ExperimentListType, ExperimentListInfo
deleteInsReq: deleteActiveLearnInsReq,
batchDeleteInsReq: batchDeleteActiveLearnInsReq,
stopInsReq: stopActiveLearnInsReq,
editInsReq: editActiveLearnInsReq,
title: '自动学习',
pathPrefix: 'active-learn',
nameProperty: 'name',
descProperty: 'description',
idProperty: 'activeLearnId',
idInsProperty: 'active_learn_id',
},
};

+ 127
- 62
react-ui/src/pages/AutoML/components/ExperimentList/index.tsx View File

@@ -30,7 +30,7 @@ import {
} from 'antd';
import { type SearchProps } from 'antd/es/input';
import classNames from 'classnames';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useCallback, useEffect, useState } from 'react';
import ExperimentInstanceList from '../ExperimentInstanceList';
import { ExperimentListType, experimentListConfig } from './config';
import styles from './index.less';
@@ -52,7 +52,6 @@ function ExperimentList({ type }: ExperimentListProps) {
const [experimentInsList, setExperimentInsList] = useState<ExperimentInstanceData[]>([]);
const [expandedRowKeys, setExpandedRowKeys] = useState<number[]>([]);
const [experimentInsTotal, setExperimentInsTotal] = useState(0);
const [now] = useServerTime();
const [pagination, setPagination] = useState<TablePaginationConfig>(
cacheState?.pagination ?? {
current: 1,
@@ -60,38 +59,37 @@ function ExperimentList({ type }: ExperimentListProps) {
},
);
const config = experimentListConfig[type];
const timerRef = useRef<ReturnType<typeof window.setTimeout> | undefined>();

// 获取自主机器学习或超参数自动优化列表
const getAutoMLList = useCallback(async () => {
const params: Record<string, any> = {
page: pagination.current! - 1,
size: pagination.pageSize,
[config.nameProperty]: searchText || undefined,
};
const request = config.getListReq;
const [res] = await to(request(params));
if (res && res.data) {
const { content = [], totalElements = 0 } = res.data;
setTableData(content);
setTotal(totalElements);
}
}, [pagination, searchText, config]);
const [now] = useServerTime();

useEffect(() => {
getAutoMLList();
}, [getAutoMLList]);
// 获取实验列表
const getExperimentList = useCallback(
async (skipLoading: boolean = false) => {
const params: Record<string, any> = {
page: pagination.current! - 1,
size: pagination.pageSize,
[config.nameProperty]: searchText || undefined,
};
const request = config.getListReq;
const [res] = await to(request(params, skipLoading));
if (res && res.data) {
const { content = [], totalElements = 0 } = res.data;
setTableData(content);
setTotal(totalElements);
}
},
[pagination, searchText, config],
);

// 获取实验实例列表
const getExperimentInsList = useCallback(
async (recordId: number, page: number, size: number) => {
async (recordId: number, page: number, size: number, skipLoading: boolean = false) => {
const params = {
[config.idProperty]: recordId,
page: page,
size: size,
};
const request = config.getInsListReq;
const [res] = await to(request(params));
const [res] = await to(request(params, skipLoading));
if (res && res.data) {
const { content = [], totalElements = 0 } = res.data;
try {
@@ -111,59 +109,115 @@ function ExperimentList({ type }: ExperimentListProps) {

// 刷新实验列表状态,
// TODO: 目前是直接刷新实验列表,后续需要优化,只刷新状态
const refreshExperimentList = useCallback(() => {
getAutoMLList();
}, [getAutoMLList]);
const refreshExperimentList = useCallback(
(skipLoading: boolean = false) => {
getExperimentList(skipLoading);
},
[getExperimentList],
);

// 刷新实验实例列表
const refreshExperimentIns = useCallback(
(experimentId: number) => {
(experimentId: number, skipLoading: boolean = false) => {
const length = experimentInsList.length;
getExperimentInsList(experimentId, 0, length);
getExperimentInsList(experimentId, 0, length, skipLoading);
},
[getExperimentInsList, experimentInsList],
);

// 新增,删除版本时,重置分页,然后刷新版本列表
// 更新实验实例状态
const editExperimentIns = useCallback(
async (
experimentId: number,
experimentInsId: number,
status: ExperimentStatus,
argo_ins_name: string,
argo_ins_ns: string,
) => {
const params = {
[config.idInsProperty]: experimentId,
id: experimentInsId,
status: status,
argo_ins_name,
argo_ins_ns,
};
const request = config.editInsReq;
const [res] = await to(request(params));
if (res && res.data) {
refreshExperimentIns(experimentId, true);
refreshExperimentList(true);
}
},
[config, refreshExperimentIns, refreshExperimentList],
);

// 获取实验列表
useEffect(() => {
getExperimentList();
}, [getExperimentList]);

// expandedRowKeys 变化
useEffect(() => {
if (expandedRowKeys.length > 0) {
getExperimentInsList(expandedRowKeys[0], 0, 5);
refreshExperimentList();
}
}, [expandedRowKeys, getExperimentInsList, refreshExperimentList]);

// 实验实例状态变化
useEffect(() => {
const handleMessage = (e: MessageEvent) => {
const { type, payload } = e.data;
if (type === ExperimentCompleted) {
const { id, status, finish_time } = payload;
const { experimentId, experimentInsId, status /*finishTime*/ } = payload;
const currentIns = experimentInsList.find((v) => v.id === experimentInsId);
// console.log(
// '实验实例状态变化',
// currentIns?.status,
// status,
// experimentId,
// experimentInsId,
// finishTime,
// );

if (
!currentIns ||
currentIns.status === ExperimentStatus.Terminated ||
currentIns.status === status
) {
return;
}

// 修改实例的状态和结束时间
setExperimentInsList((prev) =>
prev.map((v) =>
v.id === id
? {
...v,
status: status,
finish_time: finish_time,
}
: v,
),
// refreshExperimentList(true);
// refreshExperimentIns(experimentId);
editExperimentIns(
experimentId,
experimentInsId,
status,
currentIns.argo_ins_name,
currentIns.argo_ins_ns,
);

if (timerRef.current) {
clearTimeout(timerRef.current);
timerRef.current = undefined;
}

timerRef.current = setTimeout(() => {
refreshExperimentList();
}, 10000);
// 修改实例的状态和结束时间
// setExperimentInsList((prev) =>
// prev.map((v) =>
// v.id === experimentInsId
// ? {
// ...v,
// status: status,
// finish_time: finishTime,
// }
// : v,
// ),
// );
}
};

window.addEventListener('message', handleMessage);
return () => {
window.removeEventListener('message', handleMessage);
if (timerRef.current) {
clearTimeout(timerRef.current);
timerRef.current = undefined;
}
};
}, [refreshExperimentList]);
}, [experimentInsList, editExperimentIns]);

// 搜索
const onSearch: SearchProps['onSearch'] = (value) => {
@@ -207,6 +261,7 @@ function ExperimentList({ type }: ExperimentListProps) {
setCacheState({
pagination,
searchText,
expandedRowKeys,
});

if (record) {
@@ -225,6 +280,7 @@ function ExperimentList({ type }: ExperimentListProps) {
setCacheState({
pagination,
searchText,
expandedRowKeys,
});

navigate(`info/${record.id}`);
@@ -237,8 +293,8 @@ function ExperimentList({ type }: ExperimentListProps) {
if (res) {
message.success('运行成功');
setExpandedRowKeys([record.id]);
refreshExperimentList();
getExperimentInsList(record.id, 0, 5);
// getExperimentInsList(record.id, 0, 5);
// refreshExperimentList();
}
};

@@ -248,8 +304,8 @@ function ExperimentList({ type }: ExperimentListProps) {
setExperimentInsList([]);
if (expanded) {
setExpandedRowKeys([record.id]);
getExperimentInsList(record.id, 0, 5);
refreshExperimentList();
// getExperimentInsList(record.id, 0, 5);
// refreshExperimentList();
} else {
setExpandedRowKeys([]);
}
@@ -257,6 +313,11 @@ function ExperimentList({ type }: ExperimentListProps) {

// 跳转到实验实例详情
const gotoInstanceInfo = (autoML: AutoMLData, record: ExperimentInstanceData) => {
setCacheState({
pagination,
searchText,
expandedRowKeys,
});
navigate(`instance/${autoML.id}/${record.id}`);
};

@@ -269,8 +330,7 @@ function ExperimentList({ type }: ExperimentListProps) {

// 实验实例终止
const handleInstanceTerminate = async (experimentIns: ExperimentInstanceData) => {
// 刷新实验列表
refreshExperimentList();
// 修改实例的状态和结束时间
setExperimentInsList((prevList) => {
return prevList.map((item) => {
if (item.id === experimentIns.id) {
@@ -283,6 +343,11 @@ function ExperimentList({ type }: ExperimentListProps) {
return item;
});
});
// 刷新实验列表和实例列表
refreshExperimentList(true);
if (expandedRowKeys.length > 0) {
refreshExperimentIns(expandedRowKeys[0]);
}
};
// --------------------------- Table ---------------------------
// 分页切换
@@ -330,7 +395,7 @@ function ExperimentList({ type }: ExperimentListProps) {
},
...diffColumns,
{
title: '创建时间',
title: '更新时间',
dataIndex: 'update_time',
key: 'update_time',
width: '20%',


+ 7
- 0
react-ui/src/pages/AutoML/components/ExperimentLog/empty.tsx View File

@@ -0,0 +1,7 @@
import styles from './index.less';

function EmptyLog() {
return <div className={styles['empty-log']}>暂无日志</div>;
}

export default EmptyLog;

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

Loading…
Cancel
Save