Browse Source

Merge pull request '合并' (#286) from dev-zw into dev-opt

dev-opt
cp3hnu 10 months ago
parent
commit
a39f479012
100 changed files with 1576 additions and 1094 deletions
  1. +1
    -0
      .gitignore
  2. +17
    -0
      react-ui/config/config.ts
  3. +2
    -3
      react-ui/config/defaultSettings.ts
  4. +23
    -3
      react-ui/config/proxy.ts
  5. +49
    -1
      react-ui/config/routes.ts
  6. +600
    -0
      react-ui/mock/home.ts
  7. +0
    -115
      react-ui/mock/notices.ts
  8. +0
    -324
      react-ui/mock/requestRecord.mock.js
  9. +0
    -203
      react-ui/mock/user.ts
  10. +6
    -1
      react-ui/package.json
  11. BIN
      react-ui/public/fonts/WenYiHei.ttf
  12. BIN
      react-ui/public/fonts/YouSheBiaoTiHei.ttf
  13. +14
    -1
      react-ui/public/fonts/font.css
  14. +1
    -1
      react-ui/public/mockServiceWorker.js
  15. +52
    -0
      react-ui/public/scripts/resize-breakpoint.js
  16. +44
    -0
      react-ui/public/scripts/resize.js
  17. +38
    -15
      react-ui/src/app.tsx
  18. BIN
      react-ui/src/assets/img/home/app-logo-dark.png
  19. BIN
      react-ui/src/assets/img/home/app-logo.png
  20. BIN
      react-ui/src/assets/img/home/code-arrow.png
  21. BIN
      react-ui/src/assets/img/home/code-bg.png
  22. BIN
      react-ui/src/assets/img/home/code-item-bg-hover.png
  23. BIN
      react-ui/src/assets/img/home/code-item-bg.png
  24. BIN
      react-ui/src/assets/img/home/code.png
  25. BIN
      react-ui/src/assets/img/home/dataset-arrow-right.png
  26. BIN
      react-ui/src/assets/img/home/dataset-bg.png
  27. BIN
      react-ui/src/assets/img/home/dataset-item-bg.png
  28. BIN
      react-ui/src/assets/img/home/dataset.png
  29. BIN
      react-ui/src/assets/img/home/default-avatar.png
  30. BIN
      react-ui/src/assets/img/home/footer-bg.png
  31. BIN
      react-ui/src/assets/img/home/header-bg-mini.png
  32. BIN
      react-ui/src/assets/img/home/header-bg.png
  33. BIN
      react-ui/src/assets/img/home/image.png
  34. BIN
      react-ui/src/assets/img/home/mirror-arrow.png
  35. BIN
      react-ui/src/assets/img/home/mirror-version.png
  36. BIN
      react-ui/src/assets/img/home/model-between-dataset.png
  37. BIN
      react-ui/src/assets/img/home/model-bg.png
  38. BIN
      react-ui/src/assets/img/home/model-item-bg-hover.png
  39. BIN
      react-ui/src/assets/img/home/model-item-bg-hover2.png
  40. BIN
      react-ui/src/assets/img/home/model-item-bg.png
  41. BIN
      react-ui/src/assets/img/home/model-item-hot-hover.png
  42. BIN
      react-ui/src/assets/img/home/model-item-hot.png
  43. BIN
      react-ui/src/assets/img/home/model.png
  44. BIN
      react-ui/src/assets/img/home/right-arrow-hover.png
  45. BIN
      react-ui/src/assets/img/home/right-arrow.png
  46. BIN
      react-ui/src/assets/img/home/service-bg.png
  47. BIN
      react-ui/src/assets/img/home/service.png
  48. BIN
      react-ui/src/assets/img/home/service1.png
  49. BIN
      react-ui/src/assets/img/home/service2.png
  50. BIN
      react-ui/src/assets/img/home/service3.png
  51. BIN
      react-ui/src/assets/img/home/service4.png
  52. BIN
      react-ui/src/assets/img/home/statistics-bg.png
  53. BIN
      react-ui/src/assets/img/home/timestamp.png
  54. BIN
      react-ui/src/assets/img/home/user-avatar-big.png
  55. BIN
      react-ui/src/assets/img/home/user-avatar.png
  56. BIN
      react-ui/src/assets/img/home/路径 17816@2x (1).png
  57. BIN
      react-ui/src/assets/img/home/路径 17816@2x.png
  58. BIN
      react-ui/src/assets/img/message/at-hover.png
  59. BIN
      react-ui/src/assets/img/message/at.png
  60. BIN
      react-ui/src/assets/img/message/content-bg.png
  61. BIN
      react-ui/src/assets/img/message/menu-bg.png
  62. BIN
      react-ui/src/assets/img/message/message-bg.png
  63. BIN
      react-ui/src/assets/img/message/red-point.png
  64. BIN
      react-ui/src/assets/img/message/system-hover.png
  65. BIN
      react-ui/src/assets/img/message/system.png
  66. BIN
      react-ui/src/assets/img/message/trumpet-hover.png
  67. BIN
      react-ui/src/assets/img/message/trumpet.png
  68. +2
    -1
      react-ui/src/components/ErrorBoundary/index.tsx
  69. +25
    -0
      react-ui/src/components/KFButton/index.less
  70. +75
    -0
      react-ui/src/components/KFButton/index.tsx
  71. +18
    -0
      react-ui/src/components/MessageBroadcast/index.less
  72. +47
    -0
      react-ui/src/components/MessageBroadcast/index.tsx
  73. +14
    -0
      react-ui/src/components/PageContainer/index.less
  74. +16
    -0
      react-ui/src/components/PageContainer/index.tsx
  75. +11
    -8
      react-ui/src/components/ParameterSelect/config.tsx
  76. +7
    -6
      react-ui/src/components/ParameterSelect/index.tsx
  77. +26
    -17
      react-ui/src/components/RightContent/AvatarDropdown.tsx
  78. +5
    -6
      react-ui/src/components/RightContent/index.less
  79. +15
    -12
      react-ui/src/components/RightContent/index.tsx
  80. +26
    -0
      react-ui/src/enums/index.ts
  81. +2
    -0
      react-ui/src/enums/pagesEnums.ts
  82. +1
    -9
      react-ui/src/global.less
  83. +26
    -0
      react-ui/src/global.tsx
  84. +3
    -1
      react-ui/src/hooks/useServerTime.ts
  85. +21
    -24
      react-ui/src/overrides.less
  86. +2
    -1
      react-ui/src/pages/404.tsx
  87. +6
    -1
      react-ui/src/pages/Authorize/index.tsx
  88. +24
    -17
      react-ui/src/pages/AutoML/components/CreateForm/utils.ts
  89. +7
    -121
      react-ui/src/pages/Dataset/components/AddDatasetModal/index.tsx
  90. +7
    -113
      react-ui/src/pages/Dataset/components/AddModelModal/index.tsx
  91. +20
    -2
      react-ui/src/pages/Dataset/components/AddVersionModal/index.tsx
  92. +121
    -0
      react-ui/src/pages/Dataset/components/EditVersionModal/index.tsx
  93. +7
    -0
      react-ui/src/pages/Dataset/components/ResourceInfo/index.less
  94. +117
    -54
      react-ui/src/pages/Dataset/components/ResourceInfo/index.tsx
  95. +1
    -0
      react-ui/src/pages/Dataset/components/ResourceVersion/index.tsx
  96. +15
    -0
      react-ui/src/pages/Dataset/config.tsx
  97. +12
    -2
      react-ui/src/pages/Experiment/components/ExperimentParameter/index.tsx
  98. +13
    -32
      react-ui/src/pages/Experiment/components/ExportModelModal/index.tsx
  99. +13
    -0
      react-ui/src/pages/Home/components/BlockTitle/index.less
  100. +24
    -0
      react-ui/src/pages/Home/components/BlockTitle/index.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

+ 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',
},
],
},
},
});

+ 2
- 3
react-ui/config/defaultSettings.ts View File

@@ -9,9 +9,8 @@ const Settings: ProLayoutProps & {
} = {
locale: 'zh-CN',
navTheme: 'light',
// 拂晓蓝
colorPrimary: '#1664ff',
// layout: 'mix',
colorPrimary: '#514cf9',
layout: 'mix',
contentWidth: 'Fluid',
fixedHeader: false,
fixSiderbar: false,


+ 23
- 3
react-ui/config/proxy.ts View File

@@ -22,17 +22,37 @@ export default {
// 要代理的地址
// 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.164:8082',
target: 'http://36.103.199.74:31213', // 公网环境
// 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',
changeOrigin: true,
},
'/proxyApi/skybox-flames/': {
target: 'http://36.103.199.74:30009/',
// target: 'http://172.31.164.103:30009/',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/proxyApi\/skybox-flames/, ''),
},
'/proxyApi/skybox-flames/flames/api/v1': {
target: 'http://36.103.199.74:30009/',
// target: 'http://172.31.164.103:30009/',
changeOrigin: true,
rewrite: (path) =>
path.replace(
/^\/proxyApi\/skybox-flames\/flames\/api\/v1/,
'/flames/api/v1'
),
},
'/proxyApi/': {
target: 'http://36.103.199.74:30009/',
// target: 'http://172.31.164.103:30009/',
},
},

/**


+ 49
- 1
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: '工作空间',
@@ -25,6 +38,12 @@ export default [
key: 'workspace',
component: './Workspace/index',
},
{
name: '消息中心',
path: 'message',
key: 'message',
component: './Message/index',
},
],
},
{
@@ -569,6 +588,11 @@ export default [
},
],
},
{
name: '审核管理',
path: 'approval',
component: './System/Approval',
},
],
},
{
@@ -583,6 +607,18 @@ export default [
},
],
},
{
name: 'mixed',
path: '/mixed',
routes: [
{
name: '父子页面混合',
path: '',
key: 'mixed',
component: './Mixed/index',
},
],
},
{
name: '算力积分',
path: '/points',
@@ -595,6 +631,18 @@ export default [
},
],
},
{
path: '/app1/*',
name: '子应用1',
microApp: 'app1',
layout: true,
},
{
path: '/app2/*',
name: '子应用2',
microApp: 'app2',
layout: true,
},
{
path: '*',
layout: false,


+ 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,
};

+ 6
- 1
react-ui/package.json View File

@@ -1,5 +1,5 @@
{
"name": "cl-model",
"name": "ci4s",
"version": "1.0.0",
"private": true,
"description": "",
@@ -60,14 +60,17 @@
"not ie <= 10"
],
"dependencies": {
"@ant-design/colors": "~7.2.1",
"@ant-design/icons": "^5.0.0",
"@ant-design/pro-components": "^2.4.4",
"@ant-design/use-emotion-css": "1.0.4",
"@antv/g6": "^4.8.24",
"@antv/hierarchy": "^0.6.12",
"@ctrl/tinycolor": "~4.1.0",
"@types/crypto-js": "^4.2.2",
"@umijs/route-utils": "^4.0.1",
"antd": "~5.21.4",
"antd-style": "~3.7.1",
"caniuse-lite": "~1.0.30001707",
"classnames": "^2.3.2",
"crypto-js": "^4.2.0",
@@ -75,6 +78,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 +86,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/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);

+ 38
- 15
react-ui/src/app.tsx View File

@@ -4,11 +4,13 @@ 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';
import { getAccessToken } from './access';
import ErrorBoundary from './components/ErrorBoundary';
import PageContainer from './components/PageContainer';
import './dayjsConfig';
import { removeAllPageCacheState } from './hooks/useCacheState';
import { globalGetSeverTime } from './hooks/useServerTime';
@@ -20,9 +22,10 @@ import {
setRemoteMenu,
} from './services/session';
import './styles/menu.less';
import { needAuth } from './utils';
import { isLoginPage, needAuth } from './utils';
import { HomeUrl } from './utils/constant';
import { closeAllModals } from './utils/modal';
import { gotoLoginPage } from './utils/ui';
import { gotoHomePage } from './utils/ui';
export { requestConfig as request } from './requestConfig';

/**
@@ -41,7 +44,8 @@ export async function getInitialState(): Promise<GlobalInitialState> {
} as API.CurrentUser;
} catch (error) {
console.error('getInitialState', error);
gotoLoginPage();
// gotoLoginPage(true);
gotoHomePage();
}
return undefined;
};
@@ -67,7 +71,7 @@ export async function getInitialState(): Promise<GlobalInitialState> {
export const layout: RuntimeConfig['layout'] = ({ initialState }) => {
return {
ErrorBoundary: ErrorBoundary,
rightContentRender: false,
rightContentRender: () => <RightContent></RightContent>,
menu: {
locale: false,
// 每当 initialState?.currentUser?.userid 发生修改时重新执行 request
@@ -84,12 +88,7 @@ export const layout: RuntimeConfig['layout'] = ({ initialState }) => {
childrenRender: (children) => {
// 增加一个 loading 的状态
// if (initialState?.loading) return <PageLoading />;
return (
<div className="kf-page-container">
<RightContent></RightContent>
<div className="kf-page-container__content">{children}</div>
</div>
);
return <PageContainer>{children}</PageContainer>;
},
collapsedButtonRender: false,
collapsed: initialState?.collapsed,
@@ -110,6 +109,11 @@ export const layout: RuntimeConfig['layout'] = ({ initialState }) => {
colorBgMenuItemSelected: 'rgba(197, 232, 255, 0.8)',
colorMenuBackground: themes['siderBGColor'],
},
header: {
colorBgHeader: '#3a3da5',
heightLayoutHeader: 60,
colorHeaderTitle: 'white',
},
},
menuItemRender: menuItemRender(false),
subMenuItemRender: menuItemRender(true),
@@ -123,21 +127,22 @@ export const onRouteChange: RuntimeConfig['onRouteChange'] = async (e) => {
closeAllModals();

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

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

const menus = getRemoteMenu();
// 没有菜单,刷新页面
if (menus === null && needAuth(location.pathname)) {
if (menus === null && needAuth(pathname)) {
history.go(0);
}
};
@@ -170,16 +175,34 @@ 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 ??= {};
memo.theme.token = {
colorPrimary: themes['primaryColor'],
colorPrimaryHover: themes['primaryHoverColor'],
colorPrimaryActive: themes['primaryActiveColor'],
colorPrimaryText: themes['primaryColor'],
colorPrimaryTextHover: themes['primaryHoverColor'],
colorPrimaryTextActive: themes['primaryActiveColor'],
// colorPrimaryBg: 'rgba(81, 76, 249, 0.07)',
colorSuccess: themes['successColor'],
colorError: themes['errorColor'],
colorWarning: themes['warningColor'],
colorLink: themes['primaryColor'],
colorText: themes['textColor'],
fontSize: parseInt(themes['fontSize']),
controlHeightLG: 46,
};
memo.theme.components ??= {};


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-hover2.png View File

Before After
Width: 894  |  Height: 416  |  Size: 142 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

BIN
react-ui/src/assets/img/message/at-hover.png View File

Before After
Width: 36  |  Height: 36  |  Size: 2.0 kB

BIN
react-ui/src/assets/img/message/at.png View File

Before After
Width: 36  |  Height: 36  |  Size: 1.9 kB

BIN
react-ui/src/assets/img/message/content-bg.png View File

Before After
Width: 2368  |  Height: 1830  |  Size: 514 kB

BIN
react-ui/src/assets/img/message/menu-bg.png View File

Before After
Width: 392  |  Height: 1830  |  Size: 107 kB

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

Before After
Width: 3840  |  Height: 2040  |  Size: 1.6 MB

BIN
react-ui/src/assets/img/message/red-point.png View File

Before After
Width: 11  |  Height: 11  |  Size: 343 B

BIN
react-ui/src/assets/img/message/system-hover.png View File

Before After
Width: 36  |  Height: 36  |  Size: 1.2 kB

BIN
react-ui/src/assets/img/message/system.png View File

Before After
Width: 36  |  Height: 36  |  Size: 1.3 kB

BIN
react-ui/src/assets/img/message/trumpet-hover.png View File

Before After
Width: 66  |  Height: 66  |  Size: 1.8 kB

BIN
react-ui/src/assets/img/message/trumpet.png View File

Before After
Width: 66  |  Height: 66  |  Size: 1.7 kB

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


+ 25
- 0
react-ui/src/components/KFButton/index.less View File

@@ -0,0 +1,25 @@
body {
.kf-primary-button.ant-btn-color-primary.ant-btn-variant-link {
background-color: .addAlpha(@primary-color, 0.07) [] !important;
}

.kf-default-button.ant-btn-color-default.ant-btn-variant-link {
background-color: .addAlpha(@text-color-secondary, 0.07) [] !important;
}

.kf-danger-button.ant-btn-color-danger.ant-btn-variant-link {
background-color: .addAlpha(@error-color, 0.07) [] !important;
}

.ant-btn-color-default.ant-btn-variant-link.kf-default-button:not(:disabled):not(
.ant-btn-disabled
):hover {
color: .addAlpha(@text-color-secondary, 0.5) [];
}

.ant-btn-color-default.ant-btn-variant-link.kf-default-button:not(:disabled):not(
.ant-btn-disabled
):active {
color: @text-color-secondary;
}
}

+ 75
- 0
react-ui/src/components/KFButton/index.tsx View File

@@ -0,0 +1,75 @@
import themes from '@/styles/theme.less';
import { addAlpha, derivePrimaryStates } from '@/utils/color';
import { Button, ButtonProps } from 'antd';
import { createStyles } from 'antd-style';
import './index.less';

type KFColor = 'primary' | 'default' | 'danger';

export interface KFButtonProps extends ButtonProps {
kfColor?: KFColor;
}

const useStyles = createStyles(({ token, css }) => ({
primary: css`
color: ${token.colorPrimary} !important;
background-color: ${addAlpha(themes['primaryColor'], 0.07)} !important;

&:hover {
color: ${token.colorPrimaryHover} !important;
}

&:active {
color: ${token.colorPrimaryActive} !important;
}
`,
default: css`
color: ${themes['textColorSecondary']} !important;
background-color: ${addAlpha(themes['textColorSecondary'], 0.07)} !important;

&:hover {
color: ${derivePrimaryStates(themes['textColorSecondary']).colorPrimaryHover} !important;
}

&:active {
color: ${derivePrimaryStates(themes['textColorSecondary']).colorPrimaryActive} !important;
}
`,
danger: css`
color: ${themes['errorColor']} !important;
background-color: ${addAlpha(themes['errorColor'], 0.07)} !important;

&:hover {
color: ${derivePrimaryStates(themes['errorColor']).colorPrimaryHover} !important;
}

&:active {
color: ${derivePrimaryStates(themes['errorColor']).colorPrimaryActive} !important;
}
`,
}));

function KFButton({ kfColor = 'default', className, ...rest }: KFButtonProps) {
const { styles, cx } = useStyles();

let style = '';
switch (kfColor) {
case 'primary':
style = styles.primary;
break;
case 'default':
style = styles.default;
break;
case 'danger':
style = styles.danger;
break;
default:
break;
}

return (
<Button {...rest} className={cx(className, style)} color={kfColor} variant="link"></Button>
);
}

export default KFButton;

+ 18
- 0
react-ui/src/components/MessageBroadcast/index.less View File

@@ -0,0 +1,18 @@
.message-broadcast {
position: relative;
width: 32px;
height: 32px;
cursor: pointer;
.backgroundFullImage(url(@/assets/img/message/trumpet.png));

&:hover {
background-image: url(@/assets/img/message/trumpet-hover.png);
}

&__red-point {
position: absolute;
top: 8px;
left: 18px;
width: 6px;
}
}

+ 47
- 0
react-ui/src/components/MessageBroadcast/index.tsx View File

@@ -0,0 +1,47 @@
import RedPointImg from '@/assets/img/message/red-point.png';
import { getMessageCountReq } from '@/services/message';
import { to } from '@/utils/promise';
import { useModel, useNavigate } from '@umijs/max';
import { useCallback, useEffect, useState } from 'react';
import styles from './index.less';

function MessageBroadcast() {
const { initialState } = useModel('@@initialState');
const { currentUser } = initialState || {};
const { userId } = currentUser || {};
const [total, setTotal] = useState<number>(0);
const navigate = useNavigate();

const getMessageCount = useCallback(async () => {
if (!userId) return;
const params: Record<string, any> = {
receiver: userId,
type: -1,
};
const [res] = await to(getMessageCountReq(params));
if (res && res.data) {
const { unread_total } = res.data;
setTotal(unread_total);
}
}, [userId]);

useEffect(() => {
const interval = setInterval(() => {
getMessageCount();
}, 60 * 1000);
getMessageCount();
return () => {
clearInterval(interval);
};
}, [getMessageCount]);

return (
<div className={styles['message-broadcast']} onClick={() => navigate('/workspace/message')}>
{total > 0 && (
<img className={styles['message-broadcast__red-point']} src={RedPointImg}></img>
)}
</div>
);
}

export default MessageBroadcast;

+ 14
- 0
react-ui/src/components/PageContainer/index.less View File

@@ -0,0 +1,14 @@
.kf-page-container {
height: 100%;

&__breadcrumb {
display: flex;
align-items: center;
height: 70px;
padding-left: 30px;
}

&__content {
height: calc(100% - 70px);
}
}

+ 16
- 0
react-ui/src/components/PageContainer/index.tsx View File

@@ -0,0 +1,16 @@
import { ProBreadcrumb } from '@ant-design/pro-components';
import React from 'react';
import './index.less';

function PageContainer({ children }: { children: React.ReactNode }) {
return (
<div className="kf-page-container">
<div className="kf-page-container__breadcrumb">
<ProBreadcrumb></ProBreadcrumb>
</div>
<div className="kf-page-container__content">{children}</div>
</div>
);
}

export default PageContainer;

+ 11
- 8
react-ui/src/components/ParameterSelect/config.tsx View File

@@ -16,14 +16,17 @@ export type SelectPropsConfig = {
getLabel?: (value: any) => string; // 对象类型时,获取其 label
};

export type ParameterSelectDataType =
| 'dataset'
| 'model'
| 'service'
| 'resource'
| 'remote-image'
| 'remote-resource-type'
| 'remote-resource';
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: {


+ 7
- 6
react-ui/src/components/ParameterSelect/index.tsx View File

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

import jccResourceState from '@/state/jcdResource';
import jccResourceState, { getResourceTypes } from '@/state/jcdResource';
import systemResourceState, { getSystemResources } from '@/state/systemResource';
import { to } from '@/utils/promise';
import { useSnapshot } from '@umijs/max';
@@ -13,7 +13,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
import FormInfo from '../FormInfo';
import { paramSelectConfig, type ParameterSelectDataType } from './config';

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

export type ParameterSelectObject = {
value: any;
@@ -59,7 +59,6 @@ function ParameterSelect({
const valueText =
typeof selectValue === 'object' && selectValue !== null ? getValue(selectValue) : selectValue;
const jccResourceSnap = useSnapshot(jccResourceState);
const { getResourceTypes } = jccResourceSnap;
const systemResourceSnap = useSnapshot(systemResourceState);

const objectOptions = useMemo(() => {
@@ -107,13 +106,15 @@ function ParameterSelect({
setOptions(res);
}
} else if (dataType === 'remote-resource-type') {
getResourceTypes();
if (jccResourceSnap.types.length === 0) {
getResourceTypes();
}
} else if (dataType === 'resource') {
getSystemResources();
}
};
getSelectOptions();
}, [getOptions, dataType, getResourceTypes]);
}, [getOptions, dataType, getResourceTypes, jccResourceSnap.types]);

const selectOptions = (
dataType === 'resource' ? systemResourceSnap.resources : objectSelectOptions
@@ -122,7 +123,7 @@ function ParameterSelect({
const handleChange = (text: string) => {
// 数据集、模型、服务,转换成对象
if (isObjectValue) {
// 设置为 null 是因为 antv g6 bug
// 设置为 null 是因为 ant g6 bug
// 如果值为 undefined 时, graph.changeData(data) 会保留前面的值
const selectValue = text ? valueMap.get(text) : null;
if (typeof value === 'object' && value !== null) {


+ 26
- 17
react-ui/src/components/RightContent/AvatarDropdown.tsx View File

@@ -13,12 +13,13 @@ import { useEmotionCss } from '@ant-design/use-emotion-css';
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 = () => {
@@ -33,6 +34,7 @@ const Name = () => {
lineHeight: '48px',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
color: 'white',
[`@media only screen and (max-width: ${token.screenMD}px)`]: {
display: 'none',
},
@@ -67,12 +69,13 @@ const AvatarLogo = () => {
);
};

const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu }) => {
const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu, isHome = false }) => {
const navigate = useNavigate();
/**
* 退出登录,并且将当前的 url 保存
*/
const loginOut = async () => {
// const { origin } = location;
const [res] = await to(getLabelStudioUrl());
if (res && res.data) {
oauthLogout(`${res.data}/oauth/logout`);
@@ -86,8 +89,17 @@ const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu }) => {
if (clientInfo) {
const { logoutUri } = clientInfo;
location.replace(logoutUri);
// if (isHome) {
// setTimeout(() => {
// location.replace(origin);
// }, 1);
// }
} else {
gotoLoginPage();
if (isHome) {
location.reload();
} else {
gotoLoginPage(true);
}
}
};
const actionClassName = useEmotionCss(({ token }) => {
@@ -107,20 +119,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;
}
navigate(`/account/${key}`);
},
[setInitialState, navigate],
);
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}>


+ 5
- 6
react-ui/src/components/RightContent/index.less View File

@@ -1,10 +1,9 @@
.right-content {
display: flex;
gap: 8px;
gap: 10px;
align-items: center;
height: 55px;
margin-right: -10px;
padding: 0 16px;
background-color: white;
border-bottom: 1px solid #e9edf0;
height: 60px;
padding: 0 14px;
// background-color: white;
// border-bottom: 1px solid #e9edf0;
}

+ 15
- 12
react-ui/src/components/RightContent/index.tsx View File

@@ -1,8 +1,8 @@
import KFIcon from '@/components/KFIcon';
import { ProBreadcrumb } from '@ant-design/pro-components';
// import KFIcon from '@/components/KFIcon';
// import { ProBreadcrumb } from '@ant-design/pro-components';
import { useModel } from '@umijs/max';
import { Button } from 'antd';
import React from 'react';
import MessageBroadcast from '../MessageBroadcast';
import Avatar from './AvatarDropdown';
import styles from './index.less';
// import { SelectLang } from '@umijs/max';
@@ -16,12 +16,12 @@ const GlobalHeaderRight: React.FC = () => {
return null;
}

const handleMenuCollapse = () => {
setInitialState((preInitialState) => ({
...preInitialState,
collapsed: !preInitialState?.collapsed,
}));
};
// const handleMenuCollapse = () => {
// setInitialState((preInitialState) => ({
// ...preInitialState,
// collapsed: !preInitialState?.collapsed,
// }));
// };

return (
<div className={styles['right-content']}>
@@ -34,16 +34,19 @@ const GlobalHeaderRight: React.FC = () => {
<QuestionCircleOutlined />
</span> */}

<Button
{/* <Button
type="text"
style={{ marginRight: '4px' }}
icon={<KFIcon type="icon-collapsed" font={18} style={{ verticalAlign: '-3px' }} />}
onClick={handleMenuCollapse}
></Button>
></Button> */}

<ProBreadcrumb></ProBreadcrumb>
{/* <ProBreadcrumb></ProBreadcrumb> */}

<MessageBroadcast />

<Avatar menu={true} />

{/* <SelectLang className={actionClassName} /> */}
</div>
);


+ 26
- 0
react-ui/src/enums/index.ts View File

@@ -171,3 +171,29 @@ export enum ComponentType {
Map = 'map',
Str = 'str',
}

// 消息类型
export enum MessageType {
System = 1,
Mine = 2,
}

// 消息状态
export enum MessageStatus {
All = -1,
UnRead = 1,
Readed = 2,
}

// 审核状态
export enum ApprovalStatus {
Pending = 0,
Agree = 1,
Reject = 2,
}

export const approvalStatusOptions = [
{ label: '待审核', value: ApprovalStatus.Pending },
{ label: '通过', value: ApprovalStatus.Agree },
{ label: '拒绝', value: ApprovalStatus.Reject },
];

+ 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',
}

+ 1
- 9
react-ui/src/global.less View File

@@ -76,7 +76,7 @@ body {
}
.ant-pro-layout .ant-layout-sider.ant-pro-sider {
height: 100vh;
// padding-top: 56px;
padding-top: 60px;
}
.ant-pro-layout .ant-pro-layout-container {
height: 100vh;
@@ -137,14 +137,6 @@ ol {
}
}

.kf-page-container {
height: 100%;

&__content {
height: calc(100% - 55px);
}
}

.kf-menu-collapsed {
position: fixed;
top: 0;


+ 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();

+ 3
- 1
react-ui/src/hooks/useServerTime.ts View File

@@ -10,6 +10,7 @@ import { useCallback, useEffect, useState } from 'react';

let globalTimeOffset: number | undefined = undefined;

// 获取服务器时间偏移
export const globalGetSeverTime = async () => {
const requestStartTime = Date.now();
const [res] = await to(getSeverTimeReq());
@@ -23,7 +24,8 @@ export const globalGetSeverTime = async () => {
}
};

export const now = () => {
// 服务器的当前时间
export const serverNow = () => {
return new Date(Date.now() + (globalTimeOffset ?? 0));
};



+ 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>
);
};


+ 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.replace(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]);



+ 24
- 17
react-ui/src/pages/AutoML/components/CreateForm/utils.ts View File

@@ -42,37 +42,44 @@ export const regressorAlgorithms = [

// 特征预处理算法
export const featureAlgorithms = [
{ label: 'densifier (数据增稠)', value: 'densifier' },
{ label: 'densifier (特征变换-数据增稠)', value: 'densifier' },
{
label: 'extra_trees_preproc_for_classification (分类任务极端随机树)',
label: 'extra_trees_preproc_for_classification (特征选择-分类任务极端随机树)',
value: 'extra_trees_preproc_for_classification',
},
{
label: 'extra_trees_preproc_for_regression (回归任务极端随机树)',
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: '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: '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 (基于百分位的分类特征选择)',
label: 'select_percentile_classification 特征选择-基于百分位的分类特征选择)',
value: 'select_percentile_classification',
},
{
label: 'select_percentile_regression (基于百分位的回归特征选择)',
label: 'select_percentile_regression (特征选择-基于百分位的回归特征选择)',
value: 'select_percentile_regression',
},
{
label: 'select_rates_classification (基于比率的分类特征选择)',
label: 'select_rates_classification (特征选择-基于比率的分类特征选择)',
value: 'select_rates_classification',
},
{ label: 'select_rates_regression (基于比率的回归特征选择)', value: 'select_rates_regression' },
{ label: 'truncatedSVD (截断奇异值分解)', value: 'truncatedSVD' },
{
label: 'select_rates_regression (特征选择-基于比率的回归特征选择)',
value: 'select_rates_regression',
},
{ label: 'truncatedSVD (特征变换-截断奇异值分解)', value: 'truncatedSVD' },
];

+ 7
- 121
react-ui/src/pages/Dataset/components/AddDatasetModal/index.tsx View File

@@ -1,30 +1,8 @@
import { getAccessToken } from '@/access';
import KFIcon from '@/components/KFIcon';
import KFModal from '@/components/KFModal';
import { CategoryData, DataSource, ResourceType, resourceConfig } from '@/pages/Dataset/config';
import { CategoryData, DataSource } from '@/pages/Dataset/config';
import { addDataset } from '@/services/dataset/index.js';
import { to } from '@/utils/promise';
import {
getFileListFromEvent,
limitUploadFileType,
removeUploadedFile,
validateUploadFiles,
} from '@/utils/ui';
import {
Button,
Form,
Input,
Radio,
Select,
Upload,
UploadFile,
message,
type ModalProps,
type UploadProps,
} from 'antd';
import { omit } from 'lodash';
import { useState } from 'react';
import styles from './index.less';
import { Form, Input, Radio, Select, message, type ModalProps } from 'antd';

interface AddDatasetModalProps extends Omit<ModalProps, 'onOk'> {
typeList: CategoryData[];
@@ -33,20 +11,6 @@ interface AddDatasetModalProps extends Omit<ModalProps, 'onOk'> {
}

function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalProps) {
const [uuid] = useState(Date.now());

// 上传组件参数
const uploadProps: UploadProps = {
action: resourceConfig[ResourceType.Dataset].uploadAction,
headers: {
Authorization: getAccessToken() || '',
},
defaultFileList: [],
accept: '.zip,.tgz',
beforeUpload: limitUploadFileType('zip,tgz'),
onRemove: removeUploadedFile,
};

// 上传请求
const createDataset = async (params: any) => {
const [res] = await to(addDataset(params));
@@ -58,22 +22,11 @@ function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalPr

// 提交
const onFinish = (formData: any) => {
const fileList: UploadFile[] = formData['fileList'] ?? [];
if (validateUploadFiles(fileList)) {
const params = {
...omit(formData, ['fileList']),
dataset_source: DataSource.Create,
dataset_version_vos: fileList.map((item) => {
const data = item.response?.data?.[0] ?? {};
return {
file_name: data.fileName,
file_size: data.fileSize,
url: data.url,
};
}),
};
createDataset(params);
}
const params = {
...formData,
dataset_source: DataSource.Create,
};
createDataset(params);
};

return (
@@ -108,32 +61,6 @@ function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalPr
>
<Input placeholder="请输入数据名称" showCount allowClear maxLength={40} />
</Form.Item>
<Form.Item
label="数据集版本"
name="version"
rules={[
{
required: true,
message: '请输入数据集版本',
},
{
pattern: /^[a-zA-Z0-9._-]+$/,
message: '数据集版本只支持字母、数字、点(.)、下划线(_)、中横线(-)',
},
{
validator: (_rule, value) => {
if (value === 'master') {
return Promise.reject(`数据集版本不能为 master`);
} else if (value === 'origin') {
return Promise.reject(`数据集版本不能为 origin`);
}
return Promise.resolve();
},
},
]}
>
<Input placeholder="请输入数据集版本" showCount allowClear maxLength={64} />
</Form.Item>
<Form.Item label="数据集分类" name="data_type">
<Select
allowClear
@@ -172,24 +99,6 @@ function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalPr
allowClear
/>
</Form.Item>
<Form.Item
label="版本描述"
name="version_desc"
rules={[
{
required: true,
message: '请输入版本描述',
},
]}
>
<Input.TextArea
placeholder="请输入版本描述"
autoSize={{ minRows: 2, maxRows: 6 }}
maxLength={200}
showCount
allowClear
/>
</Form.Item>
<Form.Item
label="可见性"
name="is_public"
@@ -200,29 +109,6 @@ function AddDatasetModal({ typeList, tagList, onOk, ...rest }: AddDatasetModalPr
<Radio value={true}>公开</Radio>
</Radio.Group>
</Form.Item>
<Form.Item
label="数据集文件"
name="fileList"
valuePropName="fileList"
getValueFromEvent={getFileListFromEvent}
rules={[
{
required: true,
message: '请上传数据集文件',
},
]}
>
<Upload {...uploadProps} data={{ uuid: uuid }}>
<Button
className={styles['upload-button']}
type="default"
icon={<KFIcon type="icon-shangchuan" />}
>
上传文件
</Button>
<div className={styles['upload-tip']}>只允许上传 .zip 和 .tgz 格式文件</div>
</Upload>
</Form.Item>
</Form>
</KFModal>
);


+ 7
- 113
react-ui/src/pages/Dataset/components/AddModelModal/index.tsx View File

@@ -1,25 +1,8 @@
import { getAccessToken } from '@/access';
import KFIcon from '@/components/KFIcon';
import KFModal from '@/components/KFModal';
import { CategoryData, DataSource, ResourceType, resourceConfig } from '@/pages/Dataset/config';
import { CategoryData, DataSource } from '@/pages/Dataset/config';
import { addModel } from '@/services/dataset/index.js';
import { to } from '@/utils/promise';
import { getFileListFromEvent, removeUploadedFile, validateUploadFiles } from '@/utils/ui';
import {
Button,
Form,
Input,
Radio,
Select,
Upload,
UploadFile,
message,
type ModalProps,
type UploadProps,
} from 'antd';
import { omit } from 'lodash';
import { useState } from 'react';
import styles from '../AddDatasetModal/index.less';
import { Form, Input, Radio, Select, message, type ModalProps } from 'antd';

interface AddModelModalProps extends Omit<ModalProps, 'onOk'> {
typeList: CategoryData[];
@@ -28,18 +11,6 @@ interface AddModelModalProps extends Omit<ModalProps, 'onOk'> {
}

function AddModelModal({ typeList, tagList, onOk, ...rest }: AddModelModalProps) {
const [uuid] = useState(Date.now());

// 上传组件参数
const uploadProps: UploadProps = {
action: resourceConfig[ResourceType.Model].uploadAction,
headers: {
Authorization: getAccessToken() || '',
},
defaultFileList: [],
onRemove: removeUploadedFile,
};

// 上传请求
const createModel = async (params: any) => {
const [res] = await to(addModel(params));
@@ -51,22 +22,11 @@ function AddModelModal({ typeList, tagList, onOk, ...rest }: AddModelModalProps)

// 提交
const onFinish = (formData: any) => {
const fileList: UploadFile[] = formData['fileList'] ?? [];
if (validateUploadFiles(fileList)) {
const params = {
...omit(formData, ['fileList']),
model_source: DataSource.Create,
model_version_vos: fileList.map((item) => {
const data = item.response?.data?.[0] ?? {};
return {
file_name: data.fileName,
file_size: data.fileSize,
url: data.url,
};
}),
};
createModel(params);
}
const params = {
...formData,
model_source: DataSource.Create,
};
createModel(params);
};

return (
@@ -99,32 +59,6 @@ function AddModelModal({ typeList, tagList, onOk, ...rest }: AddModelModalProps)
>
<Input placeholder="请输入模型名称" showCount allowClear maxLength={40} />
</Form.Item>
<Form.Item
label="模型版本"
name="version"
rules={[
{
required: true,
message: '请输入模型版本',
},
{
pattern: /^[a-zA-Z0-9._-]+$/,
message: '模型版本只支持字母、数字、点(.)、下划线(_)、中横线(-)',
},
{
validator: (_rule, value) => {
if (value === 'master') {
return Promise.reject(`模型版本不能为 master`);
} else if (value === 'origin') {
return Promise.reject(`模型版本不能为 origin`);
}
return Promise.resolve();
},
},
]}
>
<Input placeholder="请输入模型版本" showCount allowClear maxLength={64} />
</Form.Item>
<Form.Item label="模型框架" name="model_type">
<Select
allowClear
@@ -163,24 +97,6 @@ function AddModelModal({ typeList, tagList, onOk, ...rest }: AddModelModalProps)
allowClear
/>
</Form.Item>
<Form.Item
label="版本描述"
name="version_desc"
rules={[
{
required: true,
message: '请输入版本描述',
},
]}
>
<Input.TextArea
placeholder="请输入版本描述"
autoSize={{ minRows: 2, maxRows: 6 }}
maxLength={200}
showCount
allowClear
/>
</Form.Item>
<Form.Item
label="可见性"
name="is_public"
@@ -191,28 +107,6 @@ function AddModelModal({ typeList, tagList, onOk, ...rest }: AddModelModalProps)
<Radio value={true}>公开</Radio>
</Radio.Group>
</Form.Item>
<Form.Item
label="模型文件"
name="fileList"
valuePropName="fileList"
getValueFromEvent={getFileListFromEvent}
rules={[
{
required: true,
message: '请上传模型文件',
},
]}
>
<Upload {...uploadProps} data={{ uuid: uuid }}>
<Button
className={styles['upload-button']}
type="default"
icon={<KFIcon type="icon-shangchuan" />}
>
上传文件
</Button>
</Upload>
</Form.Item>
</Form>
</KFModal>
);


+ 20
- 2
react-ui/src/pages/Dataset/components/AddVersionModal/index.tsx View File

@@ -15,7 +15,7 @@ import {
type UploadProps,
} from 'antd';
import { omit } from 'lodash';
import { useState } from 'react';
import { useEffect, useState } from 'react';
import styles from '../AddDatasetModal/index.less';

interface AddVersionModalProps extends Omit<ModalProps, 'onOk'> {
@@ -40,6 +40,23 @@ function AddVersionModal({
}: AddVersionModalProps) {
const [uuid] = useState(Date.now());
const config = resourceConfig[resourceType];
const [form] = Form.useForm();

useEffect(() => {
const getNextVersion = async () => {
const request = config.getNextVersion;
const params = {
identifier,
owner,
};
const [res] = await to(request(params));
if (res && res.data) {
const nextVersion = res.data;
form.setFieldValue('version', nextVersion);
}
};
getNextVersion();
}, [identifier, owner, config, form]);

// 上传组件参数
const uploadProps: UploadProps = {
@@ -109,6 +126,7 @@ function AddVersionModal({
}}
onFinish={onFinish}
autoComplete="off"
form={form}
>
<Form.Item
label={`${name}名称`}
@@ -146,7 +164,7 @@ function AddVersionModal({
},
]}
>
<Input placeholder={`请输入${name}版本`} maxLength={64} showCount allowClear />
<Input placeholder={`请输入${name}版本`} maxLength={64} showCount allowClear disabled />
</Form.Item>
<Form.Item
label="版本描述"


+ 121
- 0
react-ui/src/pages/Dataset/components/EditVersionModal/index.tsx View File

@@ -0,0 +1,121 @@
import KFModal from '@/components/KFModal';
import { DataSource, ResourceData, ResourceType, resourceConfig } from '@/pages/Dataset/config';
import { to } from '@/utils/promise';
import { Form, Input, message, type ModalProps } from 'antd';

interface EditVersionModalProps extends Omit<ModalProps, 'onOk'> {
resourceType: ResourceType;
resourceVersion: ResourceData;
onOk: () => void;
}

function EditVersionModal({ resourceType, resourceVersion, onOk, ...rest }: EditVersionModalProps) {
const config = resourceConfig[resourceType];
const { name: resoureName, version, version_desc } = resourceVersion;

// 修改请求
const editDatasetVersion = async (params: any) => {
const request = config.editVersion;
const [res] = await to(request(params));
if (res) {
message.success('编辑成功');
onOk?.();
}
};

// 提交
const onFinish = (formData: any) => {
const params = {
...resourceVersion,
...formData,
[config.sourceParamKey]: DataSource.Create,
};
editDatasetVersion(params);
};

const name = config.name;

return (
<KFModal
{...rest}
title="编辑版本"
image={require('@/assets/img/create-experiment.png')}
width={825}
okButtonProps={{
htmlType: 'submit',
form: 'form',
}}
>
<Form
name="form"
layout="vertical"
initialValues={{
name: resoureName,
version: version,
version_desc: version_desc,
}}
onFinish={onFinish}
autoComplete="off"
>
<Form.Item
label={`${name}名称`}
name="name"
rules={[
{
required: true,
message: `请输入${name}名称`,
},
]}
>
<Input disabled placeholder={`请输入${name}名称`} />
</Form.Item>
<Form.Item
label={`${name}版本`}
name="version"
rules={[
{
required: true,
message: `请输入${name}版本`,
},
{
pattern: /^[a-zA-Z0-9._-]+$/,
message: `${name}版本只支持字母、数字、点(.)、下划线(_)、中横线(-)`,
},
{
validator: (_rule, value) => {
if (value === 'master') {
return Promise.reject(`${name}版本不能为 master`);
} else if (value === 'origin') {
return Promise.reject(`${name}版本不能为 origin`);
}
return Promise.resolve();
},
},
]}
>
<Input placeholder={`请输入${name}版本`} maxLength={64} showCount allowClear disabled />
</Form.Item>
<Form.Item
label="版本描述"
name="version_desc"
rules={[
{
required: true,
message: '请输入版本描述',
},
]}
>
<Input.TextArea
placeholder="请输入版本描述"
autoSize={{ minRows: 2, maxRows: 6 }}
maxLength={200}
showCount
allowClear
/>
</Form.Item>
</Form>
</KFModal>
);
}

export default EditVersionModal;

+ 7
- 0
react-ui/src/pages/Dataset/components/ResourceInfo/index.less View File

@@ -28,8 +28,15 @@
border-radius: 4px;
}

&__desc {
margin-bottom: 0 !important;
color: @text-color;
font-size: @font-size;
}

&__praise {
display: flex;
flex: none;
align-items: center;
justify-content: center;
width: 70px;


+ 117
- 54
react-ui/src/pages/Dataset/components/ResourceInfo/index.tsx View File

@@ -4,6 +4,7 @@
* @Description: 数据集、模型详情
*/

import KFEmpty, { EmptyType } from '@/components/KFEmpty';
import KFIcon from '@/components/KFIcon';
import {
ResourceData,
@@ -19,10 +20,11 @@ import { openAntdModal } from '@/utils/modal';
import { to } from '@/utils/promise';
import { modalConfirm } from '@/utils/ui';
import { useParams, useSearchParams } from '@umijs/max';
import { App, Button, Flex, Select, Tabs } from 'antd';
import { App, Button, Flex, Select, Tabs, Typography } from 'antd';
import classNames from 'classnames';
import { useCallback, useEffect, useState } from 'react';
import AddVersionModal from '../AddVersionModal';
import EditVersionModal from '../EditVersionModal';
import ResourceIntro from '../ResourceIntro';
import ResourceVersion from '../ResourceVersion';
import VersionCompareModal from '../VersionCompareModal';
@@ -61,21 +63,24 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => {
const { message } = App.useApp();

// 获取详情
const getResourceDetail = useCallback(async () => {
const params = {
id: resourceId,
owner,
name,
identifier,
version,
is_public,
};
const request = config.getInfo;
const [res] = await to(request(params));
if (res && res.data) {
setInfo(res.data);
}
}, [config, resourceId, owner, name, identifier, version, is_public]);
const getResourceDetail = useCallback(
async (version?: string) => {
const params = {
id: resourceId,
owner,
name,
identifier,
version,
is_public,
};
const request = config.getInfo;
const [res] = await to(request(params));
if (res && res.data) {
setInfo(res.data);
}
},
[config, resourceId, owner, name, identifier, is_public],
);

// 获取版本列表
const getVersionList = useCallback(
@@ -100,14 +105,15 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => {
}
} else {
setVersion(undefined);
getResourceDetail(undefined);
}
},
[config, owner, identifier, versionParam],
[config, owner, identifier, versionParam, getResourceDetail],
);

useEffect(() => {
if (version) {
getResourceDetail();
getResourceDetail(version);
}
}, [version, getResourceDetail]);

@@ -116,7 +122,7 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => {
}, [getVersionList]);

// 新建版本
const showModal = () => {
const showAddVersionModal = () => {
const { close } = openAntdModal(AddVersionModal, {
resourceType: resourceType,
resourceId: resourceId,
@@ -132,6 +138,18 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => {
});
};

// 版本编辑
const showEditVersionModal = () => {
const { close } = openAntdModal(EditVersionModal, {
resourceType: resourceType,
resourceVersion: info,
onOk: () => {
getResourceDetail();
close();
},
});
};

// 选择版本
const showVersionSelector = () => {
const { close } = openAntdModal(VersionSelectorModal, {
@@ -205,6 +223,21 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => {
}
};

// 处理发布
const handlePublish = async () => {
const request = config.publish;
const params = {
id: resourceId,
owner,
name,
identifier,
};
const [res] = await to(request(params));
if (res) {
message.success('操作成功');
}
};

const items = [
{
key: ResourceInfoTabKeys.Introduction,
@@ -264,6 +297,7 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => {
{(info[tagPropertyName] as string) || '--'}
</div>
)}

<div
className={classNames(styles['resource-info__top__praise'], {
[styles['resource-info__top__praise--praised']]: info.praised,
@@ -277,45 +311,74 @@ const ResourceInfo = ({ resourceType }: ResourceInfoProps) => {
/>
<span>{info.praises_count}</span>
</div>
</Flex>
<Flex align="center">
<span style={{ marginRight: '10px' }}>版本号:</span>
<Select
placeholder="请选择版本号"
style={{ width: '160px', marginRight: '20px' }}
value={version}
onChange={handleVersionChange}
fieldNames={{ label: 'name', value: 'name' }}
options={versionList}
/>
<Button type="default" onClick={showModal} icon={<KFIcon type="icon-xinjian2" />}>
创建新版本
</Button>
<Button
type="default"
style={{ marginLeft: '20px' }}
icon={<KFIcon type="icon-banbenduibi" />}
onClick={showVersionSelector}
>
版本对比
</Button>
<Button
type="default"
style={{ marginLeft: '20px' }}
onClick={handleDelete}
icon={<KFIcon type="icon-shanchu" />}
disabled={!version}
danger
>
删除版本
<Button type="default" onClick={handlePublish}>
发布
</Button>
</Flex>
{version ? (
<Flex align="center">
<span style={{ marginRight: '10px' }}>版本号:</span>
<Select
placeholder="请选择版本号"
style={{ width: '160px', marginRight: '20px' }}
value={version}
onChange={handleVersionChange}
fieldNames={{ label: 'name', value: 'name' }}
options={versionList}
/>
<Button
type="default"
onClick={showAddVersionModal}
icon={<KFIcon type="icon-xinjian2" />}
>
创建新版本
</Button>
<Button
type="default"
style={{ marginLeft: '20px' }}
icon={<KFIcon type="icon-banbenduibi" />}
onClick={showVersionSelector}
>
版本对比
</Button>
<Button
type="default"
style={{ marginLeft: '20px' }}
onClick={handleDelete}
icon={<KFIcon type="icon-shanchu" />}
danger
>
删除版本
</Button>
</Flex>
) : (
<Typography.Paragraph
className={styles['resource-info__top__desc']}
ellipsis={{ tooltip: info.description }}
>
{info.description ?? '暂无描述'}
</Typography.Paragraph>
)}
</div>
<div className={styles['resource-info__bottom']}>
<Tabs activeKey={activeTab} items={items} onChange={(key) => setActiveTab(key)}></Tabs>
<div className={styles['resource-info__bottom__legend']}>
{activeTab === ResourceInfoTabKeys.Evolution && <GraphLegend />}
</div>
{version ? (
<>
<Tabs activeKey={activeTab} items={items} onChange={(key) => setActiveTab(key)}></Tabs>
<div className={styles['resource-info__bottom__legend']}>
{activeTab === ResourceInfoTabKeys.Evolution && <GraphLegend />}
</div>
</>
) : (
<KFEmpty
style={{ height: '100%' }}
type={EmptyType.NoData}
title="暂无版本"
content={`请创建${config.name}版本`}
hasFooter={true}
buttonTitle="创建版本"
onButtonClick={showAddVersionModal}
/>
)}
</div>
</div>
);


+ 1
- 0
react-ui/src/pages/Dataset/components/ResourceVersion/index.tsx View File

@@ -34,6 +34,7 @@ function ResourceVersion({ resourceType, info }: ResourceVersionProps) {
id: info.id,
version: info.version,
identifier: info.identifier,
owner: info.owner,
is_public: info.is_public,
});
};


+ 15
- 0
react-ui/src/pages/Dataset/config.tsx View File

@@ -9,12 +9,18 @@ import {
deleteDatasetVersion,
deleteModel,
deleteModelVersion,
editDatasetVersion,
editModelVersion,
getDatasetInfo,
getDatasetList,
getDatasetNextVersionReq,
getDatasetVersionList,
getModelInfo,
getModelList,
getModelNextVersionReq,
getModelVersionList,
publishDatasetReq,
publishModelReq,
} from '@/services/dataset/index.js';
import { limitUploadFileType } from '@/utils/ui';
import type { TabsProps, UploadFile } from 'antd';
@@ -36,9 +42,12 @@ type ResourceTypeInfo = {
getVersions: (params: any) => Promise<any>; // 获取版本列表
deleteRecord: (params: any) => Promise<any>; // 删除
addVersion: (params: any) => Promise<any>; // 新增版本
editVersion: (params: any) => Promise<any>; // 编辑版本
deleteVersion: (params: any) => Promise<any>; // 删除版本
getInfo: (params: any) => Promise<any>; // 获取详情
compareVersion: (params: any) => Promise<any>; // 版本对比
getNextVersion: (params: any) => Promise<any>; // 获取下一个版本
publish: (params: any) => Promise<any>; // 发布
name: string; // 名称
typeParamKey: 'data_type' | 'model_type'; // 类型参数名称,获取资源列表接口使用
tagParamKey: 'data_tag' | 'model_tag'; // 标签参数名称,获取资源列表接口使用
@@ -65,9 +74,12 @@ export const resourceConfig: Record<ResourceType, ResourceTypeInfo> = {
getVersions: getDatasetVersionList,
deleteRecord: deleteDataset,
addVersion: addDatasetVersion,
editVersion: editDatasetVersion,
deleteVersion: deleteDatasetVersion,
getInfo: getDatasetInfo,
compareVersion: compareDatasetVersion,
getNextVersion: getDatasetNextVersionReq,
publish: publishDatasetReq,
name: '数据集',
typeParamKey: 'data_type',
tagParamKey: 'data_tag',
@@ -103,9 +115,12 @@ export const resourceConfig: Record<ResourceType, ResourceTypeInfo> = {
getVersions: getModelVersionList,
deleteRecord: deleteModel,
addVersion: addModelVersion,
editVersion: editModelVersion,
deleteVersion: deleteModelVersion,
getInfo: getModelInfo,
compareVersion: compareModelVersion,
getNextVersion: getModelNextVersionReq,
publish: publishModelReq,
name: '模型',
typeParamKey: 'model_type',
tagParamKey: 'model_tag',


+ 12
- 2
react-ui/src/pages/Experiment/components/ExperimentParameter/index.tsx View File

@@ -1,7 +1,11 @@
import FormInfo from '@/components/FormInfo';
import ParameterSelect, { type ParameterSelectDataType } from '@/components/ParameterSelect';
import ParameterSelect, {
type ParameterSelectDataType,
ParameterSelectTypeList,
} from '@/components/ParameterSelect';
import SubAreaTitle from '@/components/SubAreaTitle';
import { ComponentType } from '@/enums';
import { setCurrentType } from '@/state/jcdResource';
import type {
PipelineGlobalParam,
PipelineNodeModelParameter,
@@ -16,6 +20,12 @@ type ExperimentParameterProps = {
};

function ExperimentParameter({ nodeData, globalParams }: ExperimentParameterProps) {
// 云际组件,设置 store 当前资源类型
if (nodeData.id.startsWith('remote-task')) {
const resourceType = nodeData.in_parameters['--resource_type'].value;
setCurrentType(resourceType);
}

// 表单组件
const getFormComponent = (
item: { key: string; value: PipelineNodeModelParameter },
@@ -65,7 +75,7 @@ function ExperimentParameter({ nodeData, globalParams }: ExperimentParameterProp
</Form.List>
)}
{item.value.type === ComponentType.Select &&
(['dataset', 'model', 'service', 'resource'].includes(item.value.item_type) ? (
(ParameterSelectTypeList.includes(item.value.item_type as ParameterSelectDataType) ? (
<ParameterSelect dataType={item.value.item_type as ParameterSelectDataType} display />
) : null)}
{item.value.type !== ComponentType.Map && item.value.type !== ComponentType.Select && (


+ 13
- 32
react-ui/src/pages/Experiment/components/ExportModelModal/index.tsx View File

@@ -3,12 +3,10 @@ import KFModal from '@/components/KFModal';
import {
DataSource,
ResourceType,
ResourceVersionData,
resourceConfig,
type ResourceData,
} from '@/pages/Dataset/config';
import { to } from '@/utils/promise';
import { InfoCircleOutlined } from '@ant-design/icons';
import { Form, Input, ModalProps, Select } from 'antd';
import { pick } from 'lodash';
import { useEffect, useState } from 'react';
@@ -44,7 +42,6 @@ function ExportModelModal({
}: ExportModelModalProps) {
const [form] = Form.useForm();
const [resources, setResources] = useState<ResourceData[]>([]);
const [versions, setVersions] = useState<ResourceVersionData[]>([]);
const config = resourceConfig[resourceType];

const layout = {
@@ -77,35 +74,24 @@ function ExportModelModal({
return undefined;
};

// 版本 tooltip
const getTooltip = () => {
const id = form.getFieldValue('id');
const resource = getSelectedResource(id);
const name = resource?.name ?? '';
const versionNames = versions.map((item: ResourceVersionData) => item.name).join('、');
const tooltip =
versions.length > 0 ? `${name}有以下版本:\n${versionNames}\n注意不能重复` : undefined;
return tooltip;
};

// 处理数据集、模型选择变化
const handleResourceChange = (id: number | undefined) => {
if (id) {
getRecourceVersions(id);
getRecourceNextVersion(id);
} else {
setVersions([]);
form.setFieldValue('version', '');
}
};

// 获取数据集、模型版本列表
const getRecourceVersions = async (id: number) => {
// 获取数据集、模型下一个版本
const getRecourceNextVersion = async (id: number) => {
const resource = getSelectedResource(id);
if (!resource) {
return;
}
const [res] = await to(config.getVersions(pick(resource, ['identifier', 'owner'])));
const [res] = await to(config.getNextVersion(pick(resource, ['identifier', 'owner'])));
if (res && res.data) {
setVersions(res.data);
form.setFieldValue('version', res.data);
}
};

@@ -184,15 +170,6 @@ function ExportModelModal({
<Form.Item
label={`${config.name}版本`}
name="version"
tooltip={
getTooltip()
? {
overlayClassName: styles['export-model-modal__tooltip'],
title: getTooltip(),
icon: <InfoCircleOutlined />,
}
: undefined
}
rules={[
{ required: true, message: `请输入${config.name}版本` },
{
@@ -205,8 +182,6 @@ function ExportModelModal({
return Promise.reject(`${config.name}版本不能为 master`);
} else if (value === 'origin') {
return Promise.reject(`${config.name}版本不能为 origin`);
} else if (value && versions.map((item) => item.name).includes(value)) {
return Promise.reject(`${config.name}版本已存在`);
} else {
return Promise.resolve();
}
@@ -214,7 +189,13 @@ function ExportModelModal({
},
]}
>
<Input placeholder={`请输入${config.name}版本`} maxLength={64} showCount allowClear />
<Input
placeholder={`请输入${config.name}版本`}
maxLength={64}
showCount
allowClear
disabled
/>
</Form.Item>
<Form.Item
label="版本描述"


+ 13
- 0
react-ui/src/pages/Home/components/BlockTitle/index.less View File

@@ -0,0 +1,13 @@
.block-title {
display: flex;
align-items: center;
justify-content: center;
width: 100%;

&__title {
color: @home-text-color;
font-weight: 500;
font-size: 2.25rem;
text-align: center;
}
}

+ 24
- 0
react-ui/src/pages/Home/components/BlockTitle/index.tsx View File

@@ -0,0 +1,24 @@
import classNames from 'classnames';
import ViewMore from '../ViewMore';
import styles from './index.less';

type BlockTitleProps = {
/** 自定义类名 */
className?: string;
/** 自定义样式 */
style?: React.CSSProperties;
/** 标题 */
title: string;
onClick?: () => void;
};

function BlockTitle({ title, onClick, className, style }: BlockTitleProps) {
return (
<div className={classNames(styles['block-title'], className)} style={style}>
<div className={styles['block-title__title']}>{title}</div>
<ViewMore onClick={onClick}></ViewMore>
</div>
);
}

export default BlockTitle;

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

Loading…
Cancel
Save