Browse Source

Merge pull request '合并' (#279) from dev-zw-home into dev-zw

pull/280/head
cp3hnu 5 months ago
parent
commit
58d4740bda
91 changed files with 2418 additions and 678 deletions
  1. +2
    -0
      react-ui/config/config.ts
  2. +6
    -2
      react-ui/config/proxy.ts
  3. +14
    -1
      react-ui/config/routes.ts
  4. +600
    -0
      react-ui/mock/home.ts
  5. +0
    -115
      react-ui/mock/notices.ts
  6. +0
    -324
      react-ui/mock/requestRecord.mock.js
  7. +0
    -203
      react-ui/mock/user.ts
  8. BIN
      react-ui/public/fonts/WenYiHei.ttf
  9. BIN
      react-ui/public/fonts/YouSheBiaoTiHei.ttf
  10. +14
    -1
      react-ui/public/fonts/font.css
  11. +52
    -0
      react-ui/public/scripts/resize-breakpoint.js
  12. +44
    -0
      react-ui/public/scripts/resize.js
  13. +9
    -7
      react-ui/src/app.tsx
  14. BIN
      react-ui/src/assets/img/home/app-logo-dark.png
  15. BIN
      react-ui/src/assets/img/home/app-logo.png
  16. BIN
      react-ui/src/assets/img/home/code-arrow.png
  17. BIN
      react-ui/src/assets/img/home/code-bg.png
  18. BIN
      react-ui/src/assets/img/home/code-item-bg-hover.png
  19. BIN
      react-ui/src/assets/img/home/code-item-bg.png
  20. BIN
      react-ui/src/assets/img/home/code.png
  21. BIN
      react-ui/src/assets/img/home/dataset-arrow-right.png
  22. BIN
      react-ui/src/assets/img/home/dataset-bg.png
  23. BIN
      react-ui/src/assets/img/home/dataset-item-bg.png
  24. BIN
      react-ui/src/assets/img/home/dataset.png
  25. BIN
      react-ui/src/assets/img/home/default-avatar.png
  26. BIN
      react-ui/src/assets/img/home/footer-bg.png
  27. BIN
      react-ui/src/assets/img/home/header-bg.png
  28. BIN
      react-ui/src/assets/img/home/image.png
  29. BIN
      react-ui/src/assets/img/home/mirror-arrow.png
  30. BIN
      react-ui/src/assets/img/home/mirror-version.png
  31. BIN
      react-ui/src/assets/img/home/model-between-dataset.png
  32. BIN
      react-ui/src/assets/img/home/model-bg.png
  33. BIN
      react-ui/src/assets/img/home/model-item-bg-hover.png
  34. BIN
      react-ui/src/assets/img/home/model-item-bg.png
  35. BIN
      react-ui/src/assets/img/home/model-item-hot-hover.png
  36. BIN
      react-ui/src/assets/img/home/model-item-hot.png
  37. BIN
      react-ui/src/assets/img/home/model.png
  38. BIN
      react-ui/src/assets/img/home/right-arrow-hover.png
  39. BIN
      react-ui/src/assets/img/home/right-arrow.png
  40. BIN
      react-ui/src/assets/img/home/service-bg.png
  41. BIN
      react-ui/src/assets/img/home/service.png
  42. BIN
      react-ui/src/assets/img/home/service1.png
  43. BIN
      react-ui/src/assets/img/home/service2.png
  44. BIN
      react-ui/src/assets/img/home/service3.png
  45. BIN
      react-ui/src/assets/img/home/service4.png
  46. BIN
      react-ui/src/assets/img/home/statistics-bg.png
  47. BIN
      react-ui/src/assets/img/home/timestamp.png
  48. BIN
      react-ui/src/assets/img/home/user-avatar-big.png
  49. BIN
      react-ui/src/assets/img/home/user-avatar.png
  50. BIN
      react-ui/src/assets/img/home/路径 17816@2x (1).png
  51. BIN
      react-ui/src/assets/img/home/路径 17816@2x.png
  52. +2
    -1
      react-ui/src/components/ErrorBoundary/index.tsx
  53. +25
    -17
      react-ui/src/components/RightContent/AvatarDropdown.tsx
  54. +2
    -0
      react-ui/src/enums/pagesEnums.ts
  55. +26
    -0
      react-ui/src/global.tsx
  56. +2
    -1
      react-ui/src/pages/404.tsx
  57. +6
    -1
      react-ui/src/pages/Authorize/index.tsx
  58. +13
    -0
      react-ui/src/pages/Home/components/BlockTitle/index.less
  59. +24
    -0
      react-ui/src/pages/Home/components/BlockTitle/index.tsx
  60. +151
    -0
      react-ui/src/pages/Home/components/CodeConfig/index.less
  61. +95
    -0
      react-ui/src/pages/Home/components/CodeConfig/index.tsx
  62. +92
    -0
      react-ui/src/pages/Home/components/Dataset/index.less
  63. +74
    -0
      react-ui/src/pages/Home/components/Dataset/index.tsx
  64. +44
    -0
      react-ui/src/pages/Home/components/Footer/index.less
  65. +33
    -0
      react-ui/src/pages/Home/components/Footer/index.tsx
  66. +53
    -0
      react-ui/src/pages/Home/components/Intro/index.less
  67. +24
    -0
      react-ui/src/pages/Home/components/Intro/index.tsx
  68. +110
    -0
      react-ui/src/pages/Home/components/Mirror/index.less
  69. +83
    -0
      react-ui/src/pages/Home/components/Mirror/index.tsx
  70. +104
    -0
      react-ui/src/pages/Home/components/Model/index.less
  71. +75
    -0
      react-ui/src/pages/Home/components/Model/index.tsx
  72. +43
    -0
      react-ui/src/pages/Home/components/NavBar/index.less
  73. +77
    -0
      react-ui/src/pages/Home/components/NavBar/index.tsx
  74. +75
    -0
      react-ui/src/pages/Home/components/Service/index.less
  75. +70
    -0
      react-ui/src/pages/Home/components/Service/index.tsx
  76. +31
    -0
      react-ui/src/pages/Home/components/Statistics/index.less
  77. +97
    -0
      react-ui/src/pages/Home/components/Statistics/index.tsx
  78. +50
    -0
      react-ui/src/pages/Home/components/ViewMore/index.less
  79. +31
    -0
      react-ui/src/pages/Home/components/ViewMore/index.tsx
  80. +21
    -0
      react-ui/src/pages/Home/index.less
  81. +31
    -0
      react-ui/src/pages/Home/index.tsx
  82. +2
    -0
      react-ui/src/pages/Mirror/List/index.tsx
  83. +1
    -1
      react-ui/src/pages/ModelDeployment/types.ts
  84. +6
    -0
      react-ui/src/pages/User/Login/index.tsx
  85. +2
    -1
      react-ui/src/pages/User/Login/login.tsx
  86. +2
    -1
      react-ui/src/pages/missingPage.jsx
  87. +67
    -0
      react-ui/src/services/home/index.ts
  88. +11
    -1
      react-ui/src/styles/theme.less
  89. +3
    -0
      react-ui/src/utils/constant.ts
  90. +14
    -1
      react-ui/src/utils/index.ts
  91. +5
    -0
      react-ui/src/utils/sessionStorage.ts

+ 2
- 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: [
// {


+ 6
- 2
react-ui/config/proxy.ts View File

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


+ 14
- 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: '工作空间',


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

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; /* 优化页面加载时的字体显示 */
}

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

+ 9
- 7
react-ui/src/app.tsx View File

@@ -20,7 +20,8 @@ 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';
export { requestConfig as request } from './requestConfig';
@@ -41,7 +42,7 @@ export async function getInitialState(): Promise<GlobalInitialState> {
} as API.CurrentUser;
} catch (error) {
console.error('getInitialState', error);
gotoLoginPage();
gotoLoginPage(true);
}
return undefined;
};
@@ -123,21 +124,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)) {
gotoLoginPage(false);
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);
}
};


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: 3840  |  Height: 1786  |  Size: 5.3 MB

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

+ 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
- 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 = () => {
@@ -67,12 +68,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 +88,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 +118,17 @@ const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu }) => {
});
const { initialState, setInitialState } = useModel('@@initialState');

const onMenuClick = useCallback(
(event: MenuInfo) => {
const { key } = event;
if (key === 'logout') {
flushSync(() => {
setInitialState((s) => ({ ...s, currentUser: undefined }));
});
loginOut();
return;
}
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}>


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

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

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

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

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

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

clearCache();
}

doubleText();

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



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

+ 151
- 0
react-ui/src/pages/Home/components/CodeConfig/index.less View File

@@ -0,0 +1,151 @@
.code {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
padding: 4.375rem 16.25rem 9.375rem;
.backgroundFullImage(url(@/assets/img/home/code-bg.png));

&__item {
position: relative;
width: calc((100% - 2 * 1.25rem) / 3);
padding: 1.625rem;
color: @home-text-color-tertiary;
font-size: 0.8125rem;
cursor: pointer;

&__title {
flex: 1;
color: @home-text-color;
font-size: 1rem;
.singleLine();
}

&__arrow {
display: none;
width: 0.875rem;
margin-left: 0.5rem;
}

&:hover &__arrow {
display: block;
}

// &__type {
// width: 3.25rem;
// height: 1.375rem;
// color: white;
// font-size: 0.875rem;
// line-height: 1.375rem;
// text-align: center;
// border-radius: 0.75rem;

// &--public {
// background: linear-gradient(120.77deg, @primary-color 0%, #79ffa7 100%);
// }

// &--private {
// background: linear-gradient(127.67deg, #ffb716 0%, #e079ff 100%);
// }
// }

// &:hover &__type--public {
// color: @primary-color;
// background: linear-gradient(120.77deg, #ffffff 0%, #d0ffe0 100%);
// }

// &:hover &__type--private {
// color: @primary-color;
// background: linear-gradient(127.67deg, #e079ff 0%, #ffb716 100%);
// }

&__desc {
height: 2.75rem;
margin-bottom: 0.875rem;
color: @home-text-color-secondary;
font-size: 0.875rem;
line-height: 1.375rem;
.multiLine(2);
}

&__user-avatar {
flex: none;
width: 1.5rem;
height: 1.5rem;
margin-right: 0.875rem;
}

&__user {
.singleLine();
}

&__user-divider {
flex: none;
height: 0.625rem;
margin-right: 0.75rem;
margin-left: 0.75rem;
background-color: @home-divider-color;
}

&__timestamp-icon {
flex: none;
width: 1rem;
height: 1rem;
margin-right: 0.375rem;
}

&__timestamp {
flex: none;
}
}

&__item--first {
background-color: transparent;
border-radius: 1rem;
box-shadow: 0px 0px 0.75rem rgba(33, 73, 212, 0.15);
.backgroundFullImage(url(@/assets/img/home/code-item-bg.png));

&:hover {
.backgroundFullImage(url(@/assets/img/home/code-item-bg-hover.png));
color: white;
box-shadow: 0px 0px 0.75rem rgba(33, 73, 212, 0.15);
}
}

&__item--first &__item__arrow {
display: none !important;
}

&__second-line {
position: relative;
margin-top: 1.625rem;
background-color: white;
border-radius: 1rem;
box-shadow: 0px 0px 0.75rem rgba(33, 73, 212, 0.15);

&__divider {
position: absolute;
top: 1.625rem;
bottom: 1.625rem;
left: calc((100% - 2 * 1.25rem) / 3 + 0.625rem);
border-left: 1px dashed rgba(146, 164, 201, 0.56);

&&--second {
right: calc((100% - 2 * 1.25rem) / 3 + 0.625rem);
left: auto;
}
}
}

&__item--first:hover &__item__title {
color: white;
}

&__item--first:hover &__item__desc {
color: white;
}

&__item--second:hover &__item__title {
color: @primary-color;
}
}

+ 95
- 0
react-ui/src/pages/Home/components/CodeConfig/index.tsx View File

@@ -0,0 +1,95 @@
import { CodeConfigData } from '@/components/CodeSelectorModal';
import { getPublicCodeConfigsReq } from '@/services/home';
import { getGitUrl } from '@/utils';
import { formatDate } from '@/utils/date';
import { to } from '@/utils/promise';
import { useNavigate } from '@umijs/max';
import { Divider, Flex } from 'antd';
import classNames from 'classnames';
import { useEffect, useState } from 'react';
import BlockTitle from '../BlockTitle';
import styles from './index.less';

function CodeConfig() {
const navigate = useNavigate();
const [codeCofigs, setCodeConfigs] = useState<CodeConfigData[]>([]);

useEffect(() => {
const getPublicCodeConfigs = async () => {
const [res] = await to(getPublicCodeConfigsReq());
if (res && res.data) {
const { content = [] } = res.data;
setCodeConfigs(content.slice(0, 9));
}
};

getPublicCodeConfigs();
}, []);

const createItem = (item: CodeConfigData, className: string) => {
return (
<div
className={classNames(styles['code__item'], className)}
key={item.id}
onClick={() => {
const url = getGitUrl(item.git_url, item.git_branch);
window.open(url, '_blank');
}}
>
<Flex align="center" style={{ marginBottom: '1rem' }}>
<div className={styles['code__item__title']}>{item.code_repo_name}</div>
<img
className={styles['code__item__arrow']}
src={require('@/assets/img/home/code-arrow.png')}
></img>
</Flex>
<div className={styles['code__item__desc']}>{item.git_url}</div>
<Flex align="center">
<img
src={require('@/assets/img/home/user-avatar.png')}
className={styles['code__item__user-avatar']}
></img>
<div className={styles['code__item__user']}>{item.create_by}</div>
<Divider type="vertical" className={styles['code__item__user-divider']}></Divider>
<img
src={require('@/assets/img/home/timestamp.png')}
className={styles['code__item__timestamp-icon']}
></img>
<div className={styles['code__item__timestamp']}>{formatDate(item.create_time)}</div>
<div></div>
</Flex>
</div>
);
};

return (
<div className={styles.code}>
<BlockTitle
title="热门代码配置"
style={{ marginBottom: '5.25rem' }}
onClick={() => navigate('/dataset/codeConfig')}
></BlockTitle>
<Flex align="center" style={{ width: '100%' }} wrap gap="1.625rem 1.25rem">
{codeCofigs.slice(0, 3).map((item) => createItem(item, styles['code__item--first']))}
</Flex>
<Flex
align="center"
className={styles['code__second-line']}
style={{ width: '100%' }}
wrap
gap="0 1.25rem"
>
{codeCofigs.slice(3).map((item) => createItem(item, styles['code__item--second']))}
<div className={styles['code__second-line__divider']}></div>
<div
className={classNames(
styles['code__second-line__divider'],
styles['code__second-line__divider--second'],
)}
></div>
</Flex>
</div>
);
}

export default CodeConfig;

+ 92
- 0
react-ui/src/pages/Home/components/Dataset/index.less View File

@@ -0,0 +1,92 @@
.dataset {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
padding: 0 16.25rem 11.625rem;

&__item {
position: relative;
width: calc((100% - 3 * 1.25rem) / 4);
padding: 1.625rem;
color: @home-text-color-tertiary;
font-size: 0.8125rem;
background-color: transparent;
border-radius: 1rem;
box-shadow: 0px 0px 0.75rem rgba(33, 73, 212, 0.06);
cursor: pointer;
.backgroundFullImage(url(@/assets/img/home/dataset-item-bg.png));

&:nth-child(1),
&:nth-child(2),
&:nth-child(3) {
width: calc((100% - 2 * 1.25rem) / 3);
}
&:hover {
outline: 2px solid @primary-color;
box-shadow: 0px 0px 0.75rem rgba(33, 73, 212, 0.15);
}

&__title {
flex: 1;
color: @home-text-color;
font-size: 1rem;
.singleLine();
}

&:hover &__title {
color: @primary-color;
}

&__hot {
width: 3.25rem;
height: 1.375rem;
margin-left: 0.5rem;
color: white;
font-size: 0.875rem;
line-height: 1.375rem;
text-align: center;
background: linear-gradient(127.67deg, @primary-color 0%, #e079ff 100%);
border-radius: 0.6875rem;
}

&__desc {
height: 2.75rem;
margin-bottom: 0.875rem;
color: @home-text-color-secondary;
font-size: 0.875rem;
line-height: 1.375rem;
.multiLine(2);
}

&__user-avatar {
flex: none;
width: 1.5rem;
height: 1.5rem;
margin-right: 0.875rem;
}

&__user {
.singleLine();
}

&__user-divider {
flex: none;
height: 0.625rem;
margin-right: 0.75rem;
margin-left: 0.75rem;
background-color: @home-divider-color;
}

&__timestamp-icon {
flex: none;
width: 1rem;
height: 1rem;
margin-right: 0.375rem;
}

&__timestamp {
flex: none;
}
}
}

+ 74
- 0
react-ui/src/pages/Home/components/Dataset/index.tsx View File

@@ -0,0 +1,74 @@
import { DatasetData } from '@/pages/Dataset/config';
import { getPublicDatasetsReq } from '@/services/home';
import { to } from '@/utils/promise';
import { useNavigate } from '@umijs/max';
import { Divider, Flex } from 'antd';
import { useEffect, useState } from 'react';
import BlockTitle from '../BlockTitle';
import styles from './index.less';

function DatasetBlock() {
const navigate = useNavigate();
const [datasetData, setDatasetData] = useState<DatasetData[]>([]);

useEffect(() => {
const getPublicDatasets = async () => {
const [res] = await to(getPublicDatasetsReq());
if (res && res.data) {
const { content = [] } = res.data;
setDatasetData(content.slice(0, 7));
}
};

getPublicDatasets();
}, []);

return (
<div className={styles.dataset}>
<BlockTitle
title="热门数据集"
style={{ marginBottom: '3.875rem' }}
onClick={() => navigate('/dataset/dataset')}
></BlockTitle>
<Flex align="center" style={{ width: '100%' }} wrap gap="1.625rem 1.25rem">
{datasetData.map((item, index) => {
return (
<div
className={styles['dataset__item']}
key={item.id}
onClick={() => {
navigate(
`/dataset/dataset/info/${item.id}?name=${item.name}&owner=${item.owner}&identifier=${item.identifier}&is_public=${item.is_public}`,
);
}}
>
<Flex align="center" style={{ marginBottom: '1rem' }}>
<div className={styles['dataset__item__title']}>{item.name}</div>
{index < 3 && <div className={styles['dataset__item__hot']}>HOT</div>}
</Flex>
<div className={styles['dataset__item__desc']}>{item.description}</div>
<Flex align="center">
<img
src={require('@/assets/img/home/user-avatar.png')}
className={styles['dataset__item__user-avatar']}
></img>
<div className={styles['dataset__item__user']}>{item.create_by}</div>
<Divider
type="vertical"
className={styles['dataset__item__user-divider']}
></Divider>
<img
src={require('@/assets/img/home/timestamp.png')}
className={styles['dataset__item__timestamp-icon']}
></img>
<div className={styles['dataset__item__timestamp']}>{item.time_ago}更新</div>
</Flex>
</div>
);
})}
</Flex>
</div>
);
}

export default DatasetBlock;

+ 44
- 0
react-ui/src/pages/Home/components/Footer/index.less View File

@@ -0,0 +1,44 @@
.footer {
width: 100%;
padding: 4.375rem 15.625rem 1.25rem;
color: .addAlpha(@home-text-color, 0.6) [];
font-size: 0.9375rem;
font-size: 0.75rem;
.backgroundFullImage(url(@/assets/img/home/footer-bg.png));

&__app-logo {
width: 1.875rem;
margin-right: 0.625rem;
}

&__app-name {
margin-right: 5rem;
color: @home-text-color;
font-size: 1.5rem;
font-family: WenYiHei;
}

&__about-us {
width: 24.75rem;
}

&__contact {
align-self: start;
}

&__title {
margin-bottom: 1rem;
color: @home-text-color;
font-size: 0.875rem;
}

&__desc {
line-height: 1.5rem;
}

&__copyright {
width: 100%;
color: @home-text-color-secondary;
text-align: center;
}
}

+ 33
- 0
react-ui/src/pages/Home/components/Footer/index.tsx View File

@@ -0,0 +1,33 @@
import { Divider, Flex } from 'antd';
import styles from './index.less';

function Footer() {
return (
<div className={styles['footer']}>
<Flex align="center" justify="space-between" style={{ marginBottom: '44px' }}>
<Flex align="center">
<img
src={require('@/assets/img/home/app-logo-dark.png')}
className={styles['footer__app-logo']}
></img>
<span className={styles['footer__app-name']}>智能材料科研平台</span>
</Flex>
<div className={styles['footer__about-us']}>
<div className={styles['footer__title']}>关于我们</div>
<div className={styles['footer__desc']}>
我是关于我们的文案简介内容我是关于我们的文案简介内容我是关于我们的文案简介内容我是关于我们的文案简介内容我是关于我们的文案简介内容我是关于我们的文案简介内容我是关于我们的文案简介内容我是关于我们的文案简介内容
</div>
</div>
<div className={styles['footer__contact']}>
<div className={styles['footer__title']}>联系我们</div>
<div style={{ marginBottom: '1rem' }}>邮箱:xxxx@163.com</div>
<div>地址:湖南省长沙市岳麓区中电软件园</div>
</div>
</Flex>
<Divider></Divider>
<div className={styles['footer__copyright']}>© 2025 国防科技大学所有</div>
</div>
);
}

export default Footer;

+ 53
- 0
react-ui/src/pages/Home/components/Intro/index.less View File

@@ -0,0 +1,53 @@
.intro {
position: relative;
z-index: 10;
display: flex;
flex-direction: column;
align-items: center;
background-image: url(@/assets/img/home/header-bg.png);
background-repeat: no-repeat;
background-position: left top;
background-size: 100% 100%;

&__title {
margin-bottom: 1.125rem;
color: #ffffff;
font-weight: 400;
font-size: 2.375rem;
font-family: WenYiHei;
}

&__desc {
width: 54.5rem;
margin-bottom: 1.875rem;
color: #ffffff;
font-size: 1rem;
line-height: 1.75rem;
text-align: center;
}

&__button {
margin-bottom: 4.375rem;
padding: 0.75rem 2.375rem;
color: #ffffff;
font-size: 1rem;
text-align: center;
background: linear-gradient(
136.87deg,
rgba(57, 217, 255, 0.51) 0%,
rgba(255, 255, 255, 0.01) 48.54%,
rgba(255, 149, 247, 0.33) 100%
);
border: 1px solid rgba(255, 255, 255, 0.38);
border-radius: 0.5rem;
cursor: pointer;

&:hover {
background: linear-gradient(
108.54deg,
rgba(183, 131, 255, 0.81) 3.72%,
rgba(119, 208, 255, 0.31) 98.01%
);
}
}
}

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

@@ -0,0 +1,24 @@
import { useNavigate } from '@umijs/max';
import NavBar from '../NavBar';
import StatisticsBlock from '../Statistics';
import styles from './index.less';

function IntroBlock() {
const navigate = useNavigate();
return (
<div className={styles.intro}>
<NavBar></NavBar>
<div className={styles['intro__title']}>智能材料科研平台</div>
<div className={styles['intro__desc']}>
智能材料科研平台是用于材料研究和开发的技术平台,它旨在提供实验数据收集、分析和可视化等功能,
以支持材料工程师、科学家和研究人员在材料设计、性能评估和工艺优化方面的工作。
</div>
<div className={styles['intro__button']} onClick={() => navigate('/workspace')}>
开始使用
</div>
<StatisticsBlock></StatisticsBlock>
</div>
);
}

export default IntroBlock;

+ 110
- 0
react-ui/src/pages/Home/components/Mirror/index.less View File

@@ -0,0 +1,110 @@
.mirror {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
padding: 0 16.25rem 11.625rem;

&__item {
position: relative;
width: calc((100% - 2 * 1.25rem) / 4);
padding: 1.625rem;
color: @home-text-color-tertiary;
font-size: 0.8125rem;
background-color: transparent;
border-radius: 1.125rem;
box-shadow: 0px 0.1875rem 0.75rem rgba(41, 50, 225, 0.09);
cursor: pointer;
.backgroundFullImage(url(@/assets/img/home/dataset-item-bg.png));

&:nth-child(2),
&:nth-child(4) {
width: calc((100% - 2 * 1.25rem) / 2);
}
&:hover {
outline: 2px solid @primary-color;
box-shadow: 0px 0.1875rem 0.75rem rgba(41, 50, 225, 0.09);
}

&__title {
flex: 1;
margin-right: 0.5rem;
color: @home-text-color;
font-size: 1rem;
.singleLine();
}

&:hover &__title {
color: @primary-color;
}

&__arrow {
display: none;
width: 0.75rem;
height: 0.75rem;
}

&:hover &__arrow {
display: block;
}

&__desc {
height: 2.75rem;
margin-bottom: 0.875rem;
color: @home-text-color-secondary;
font-size: 0.875rem;
line-height: 1.375rem;
.multiLine(2);
}

&__version {
width: fit-content;
margin-bottom: 1.625rem;
padding: 0.375rem 0.625rem;
color: @primary-color;
font-size: 0.875rem;
background: linear-gradient(
90deg,
.addAlpha(@primary-color, 0.1) [] 0%,
.addAlpha(#c7daff, 0.1) [] 100%
);
border-radius: 0.25rem;

&__img {
width: 0.875rem;
height: 0.875rem;
margin-right: 0.375rem;
}
}

&__user-avatar {
flex: none;
width: 1.5rem;
height: 1.5rem;
margin-right: 0.875rem;
}

&__user {
.singleLine();
}

&__user-divider {
flex: none;
height: 0.625rem;
margin-right: 0.75rem;
margin-left: 0.75rem;
background-color: @home-divider-color;
}

&__timestamp-icon {
flex: none;
width: 1rem;
height: 1rem;
margin-right: 0.375rem;
}

&__timestamp {
flex: none;
}
}
}

+ 83
- 0
react-ui/src/pages/Home/components/Mirror/index.tsx View File

@@ -0,0 +1,83 @@
import { MirrorData } from '@/pages/Mirror/List';
import { getPublicImagesReq } from '@/services/home';
import { formatDate } from '@/utils/date';
import { to } from '@/utils/promise';
import { useNavigate } from '@umijs/max';
import { Divider, Flex } from 'antd';
import { useEffect, useState } from 'react';
import BlockTitle from '../BlockTitle';
import styles from './index.less';

function MirrorBlock() {
const navigate = useNavigate();
const [mirrorData, setMirrirData] = useState<MirrorData[]>([]);

useEffect(() => {
const getPublicImages = async () => {
const [res] = await to(getPublicImagesReq());
if (res && res.data) {
const { content = [] } = res.data;
setMirrirData(content.slice(0, 6));
}
};

getPublicImages();
}, []);

return (
<div className={styles.mirror}>
<BlockTitle
title="热门镜像"
style={{ marginBottom: '5.25rem' }}
onClick={() => navigate('/dataset/mirror')}
></BlockTitle>
<Flex align="center" style={{ width: '100%' }} wrap gap="1.625rem 1.25rem">
{mirrorData.map((item) => {
return (
<div
className={styles['mirror__item']}
key={item.id}
onClick={() => {
navigate(`/dataset/mirror/info/${item.id}`);
}}
>
<Flex align="center" style={{ marginBottom: '1rem' }}>
<img
src={require('@/assets/img/home/user-avatar.png')}
className={styles['mirror__item__user-avatar']}
></img>
<div className={styles['mirror__item__title']}>{item.name}</div>
<img
className={styles['mirror__item__arrow']}
src={require('@/assets/img/home/mirror-arrow.png')}
></img>
</Flex>
<div className={styles['mirror__item__desc']}>{item.description}</div>
<Flex align="center" className={styles['mirror__item__version']}>
<img
src={require('@/assets/img/home/mirror-version.png')}
className={styles['mirror__item__version__img']}
></img>
<span>{`版本数:${item.version_count}`}</span>
</Flex>
<Flex align="center">
<div className={styles['mirror__item__user']}>{item.create_by}</div>
<Divider type="vertical" className={styles['mirror__item__user-divider']}></Divider>
<img
src={require('@/assets/img/home/timestamp.png')}
className={styles['mirror__item__timestamp-icon']}
></img>
<div className={styles['mirror__item__timestamp']}>
{formatDate(item.create_time)}
</div>
<div></div>
</Flex>
</div>
);
})}
</Flex>
</div>
);
}

export default MirrorBlock;

+ 104
- 0
react-ui/src/pages/Home/components/Model/index.less View File

@@ -0,0 +1,104 @@
.model {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
margin-top: 1.25rem;
padding: 4.125rem 16.25rem 14.75rem;
.backgroundFullImage(url(@/assets/img/home/model-bg.png));

&__item {
position: relative;
width: calc((100% - 2 * 1.25rem) / 3);
padding: 1.875rem 1.25rem;
color: @home-text-color-tertiary;
font-size: 0.8125rem;
background-color: transparent;
border-radius: 1rem;
box-shadow: 0px 0.0625rem 0.75rem rgba(33, 73, 212, 0.09);
cursor: pointer;
.backgroundFullImage(url(@/assets/img/home/model-item-bg.png));

&:hover {
color: white;
.backgroundFullImage(url(@/assets/img/home/model-item-bg-hover.png));
}

&__hot {
.backgroundFullImage(url(@/assets/img/home/model-item-hot.png));
position: absolute;
top: 0;
right: 0;
width: 4.625rem;
height: 2rem;
}

&:hover &__hot {
.backgroundFullImage(url(@/assets/img/home/model-item-hot-hover.png));
}

&__user-avatar {
flex: none;
width: 2.75rem;
height: 2.75rem;
margin-right: 0.875rem;
}

&__title {
margin-bottom: 0.625rem;
color: @home-text-color;
font-size: 1rem;
.singleLine();
}

&:hover &__title {
color: white;
}

&__desc {
height: 2.75rem;
margin-bottom: 0.875rem;
font-size: 0.875rem;
line-height: 1.375rem;
.multiLine(2);
}

&__user {
.singleLine();
}

&__user-divider {
flex: none;
height: 0.625rem;
margin-right: 0.75rem;
margin-left: 0.75rem;
background-color: @home-divider-color;
}

&__timestamp {
flex: none;
}

&:hover &__user-divider {
background-color: white;
}

// &__category {
// padding: 0.25rem 0.625rem;
// color: @primary-color;
// font-size: 0.8125rem;
// background-color: .addAlpha(@primary-color, 0.07) [];
// border-radius: 0.25rem;

// &:nth-child(2) {
// color: rgba(28, 153, 7, 1);
// background-color: rgba(28, 153, 7, 0.07);
// }
// }

// &:hover &__category {
// color: @home-text-color;
// background-color: white;
// }
}
}

+ 75
- 0
react-ui/src/pages/Home/components/Model/index.tsx View File

@@ -0,0 +1,75 @@
import { ModelData } from '@/pages/Dataset/config';
import { getPublicModelsReq } from '@/services/home';
import { to } from '@/utils/promise';
import { useNavigate } from '@umijs/max';
import { Divider, Flex } from 'antd';
import { useEffect, useState } from 'react';
import BlockTitle from '../BlockTitle';
import styles from './index.less';

function ModelBlock() {
const navigate = useNavigate();
const [modelData, setModelData] = useState<ModelData[]>([]);

useEffect(() => {
const getPublicModels = async () => {
const [res] = await to(getPublicModelsReq());
if (res && res.data) {
const { content = [] } = res.data;
setModelData(content.slice(0, 6));
}
};

getPublicModels();
}, []);

return (
<div className={styles.model}>
<BlockTitle
title="热门模型"
style={{ marginBottom: '5.25rem' }}
onClick={() => navigate('/dataset/model')}
></BlockTitle>
<Flex align="center" style={{ width: '100%' }} wrap gap="1.625rem 1.25rem">
{modelData.map((item, index) => {
return (
<Flex
className={styles['model__item']}
key={item.id}
onClick={() => {
navigate(
`/dataset/model/info/${item.id}?name=${item.name}&owner=${item.owner}&identifier=${item.identifier}&is_public=${item.is_public}`,
);
}}
>
<img
src={require('@/assets/img/home/user-avatar.png')}
className={styles['model__item__user-avatar']}
></img>
{index < 3 && <div className={styles['model__item__hot']}></div>}
<div style={{ flex: 1, minWidth: 0 }}>
<div className={styles['model__item__title']}>{item.name}</div>
<div className={styles['model__item__desc']}>{item.description}</div>
<Flex align="center">
<div className={styles['model__item__user']}>{item.create_by}</div>
<Divider
type="vertical"
className={styles['model__item__user-divider']}
></Divider>
<div className={styles['model__item__timestamp']}>{item.time_ago}更新</div>
<div></div>
</Flex>
{/* <Flex align="center" style={{ marginTop: '1.25rem' }} gap={'0 0.625rem'}>
<div className={styles['model__item__category']}>电池开发</div>
<div className={styles['model__item__category']}>材料研发</div>
</Flex> */}
</div>
</Flex>
);
})}
</Flex>
</div>
);
}

export default ModelBlock;

+ 43
- 0
react-ui/src/pages/Home/components/NavBar/index.less View File

@@ -0,0 +1,43 @@
.nav-bar {
display: flex;
align-items: center;
width: 100%;
margin-bottom: 4.375rem;
padding: 1.25rem 15.625rem;
color: white;
font-size: 0.9375rem;

&__app-logo {
width: 1.75rem;
margin-right: 0.625rem;
}

&__app-name {
margin-right: 6.625rem;
font-size: 1.375rem;
font-family: WenYiHei;
}

&__menu-item {
margin-right: 3.125rem;
cursor: pointer;

&:last-of-type {
margin-right: 0;
}
}

&__avatar {
width: 2rem;
height: 2rem;
margin-right: 0;
margin-left: auto;
cursor: pointer;
}

&__login {
margin-right: 0;
margin-left: auto;
cursor: pointer;
}
}

+ 77
- 0
react-ui/src/pages/Home/components/NavBar/index.tsx View File

@@ -0,0 +1,77 @@
import { getAccessToken } from '@/access';
import Avatar from '@/components/RightContent/AvatarDropdown';
import { useNavigate } from '@umijs/max';
import { Flex } from 'antd';
import styles from './index.less';

function NavBar() {
const navigate = useNavigate();
const token = getAccessToken();

const gotoPage = (page: string) => {
switch (page) {
case 'service':
navigate('/dataset/modelDeployment');
break;

case 'model':
navigate('/dataset/model');
break;

case 'dataset':
navigate('/dataset/dataset');
break;

case 'mirror':
navigate('/dataset/mirror');
break;

case 'codeConfig':
navigate('/dataset/codeConfig');
break;

case 'login':
navigate('/user/login');
break;

default:
break;
}
};

return (
<div className={styles['nav-bar']}>
<Flex align="center">
<img
src={require('@/assets/img/home/app-logo.png')}
className={styles['nav-bar__app-logo']}
></img>
<span className={styles['nav-bar__app-name']}>智能材料科研平台</span>
</Flex>
<div className={styles['nav-bar__menu-item']} onClick={() => gotoPage('service')}>
服务
</div>
<div className={styles['nav-bar__menu-item']} onClick={() => gotoPage('model')}>
模型
</div>
<div className={styles['nav-bar__menu-item']} onClick={() => gotoPage('dataset')}>
数据集
</div>
<div className={styles['nav-bar__menu-item']} onClick={() => gotoPage('mirror')}>
镜像
</div>
<div className={styles['nav-bar__menu-item']} onClick={() => gotoPage('codeConfig')}>
代码配置
</div>
{token ? (
<Avatar menu isHome />
) : (
<div className={styles['nav-bar__login']} onClick={() => gotoPage('login')}>
登录
</div>
)}
</div>
);
}

export default NavBar;

+ 75
- 0
react-ui/src/pages/Home/components/Service/index.less View File

@@ -0,0 +1,75 @@
.service {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
padding: 5.625rem 16.25rem 6.25rem;
.backgroundFullImage(url(@/assets/img/home/service-bg.png));

&__item {
width: 25%;
padding: 1.25rem 1.25rem 1.5625rem;
background: #ffffff;
border-radius: 1.25rem;
box-shadow: 0rem 0rem 0.75rem rgba(33, 73, 212, 0.06);
cursor: pointer;

&:hover {
box-shadow: 0rem 0rem 0.75rem rgba(33, 73, 212, 0.12);
}

&__image-container {
width: 18.4375rem;
height: 10.6875rem;
margin-bottom: 0.75rem;
overflow: hidden;
border-radius: 1.875rem;
}

&__image {
width: 100%;
height: 100%;
transform: scale(1);
transition: transform 0.3s linear;
}

&:hover &__image {
transform: scale(1.1);
}

&__title {
height: 2.625rem;
margin-bottom: 0.875rem;
color: @home-text-color;
font-size: 0.9375rem;
.multiLine(2);
}

&:hover &__title {
color: @primary-color;
}

&__user-avatar {
flex: none;
width: 1.3125rem;
height: 1.3125rem;
margin-right: 0.5rem;
}

&__user {
margin-right: 0.5rem;
color: #191919;
font-size: 0.875rem;
.singleLine();
}

&__date {
flex: none;
margin-right: 0;
margin-left: auto;
color: @text-color-tertiary;
font-size: 0.8125rem;
}
}
}

+ 70
- 0
react-ui/src/pages/Home/components/Service/index.tsx View File

@@ -0,0 +1,70 @@
import ServiceImg1 from '@/assets/img/home/service1.png';
import ServiceImg2 from '@/assets/img/home/service2.png';
import ServiceImg3 from '@/assets/img/home/service3.png';
import ServiceImg4 from '@/assets/img/home/service4.png';
import { type ServiceData } from '@/pages/ModelDeployment/types';
import { getPublicServicesReq } from '@/services/home';
import { formatDate } from '@/utils/date';
import { to } from '@/utils/promise';
import { useNavigate } from '@umijs/max';
import { Flex } from 'antd';
import { useEffect, useState } from 'react';
import BlockTitle from '../BlockTitle';
import styles from './index.less';

function ServiceBlock() {
const navigate = useNavigate();
const [serviceData, setServiceData] = useState<ServiceData[]>([]);
const images = [ServiceImg1, ServiceImg2, ServiceImg3, ServiceImg4];

useEffect(() => {
const getPublicServices = async () => {
const [res] = await to(getPublicServicesReq());
if (res && res.data) {
const { content = [] } = res.data;
setServiceData(content.slice(0, 4));
}
};

getPublicServices();
}, []);
return (
<div className={styles.service}>
<BlockTitle
title="热门APP"
style={{ marginBottom: '5.25rem' }}
onClick={() => navigate('/dataset/modelDeployment')}
></BlockTitle>
<Flex align="center" style={{ width: '100%' }} gap={'0 1.125rem'}>
{serviceData.map((item, index) => {
return (
<div
className={styles['service__item']}
key={item.id}
onClick={() => navigate(`/dataset/modelDeployment/serviceInfo/${item.id}`)}
>
<div className={styles['service__item__image-container']}>
<img
src={images[index]}
draggable={false}
className={styles['service__item__image']}
></img>
</div>
<div className={styles['service__item__title']}>{item.service_name}</div>
<Flex align="center">
<img
src={require('@/assets/img/home/user-avatar.png')}
className={styles['service__item__user-avatar']}
></img>
<div className={styles['service__item__user']}>{item.create_by}</div>
<div className={styles['service__item__date']}>{formatDate(item.create_time)}</div>
</Flex>
</div>
);
})}
</Flex>
</div>
);
}

export default ServiceBlock;

+ 31
- 0
react-ui/src/pages/Home/components/Statistics/index.less View File

@@ -0,0 +1,31 @@
.statistics {
display: flex;
align-items: center;
justify-content: space-evenly;
width: 87.5rem;
padding: 2.125rem 0 1.625rem;
.backgroundFullImage(url(@/assets/img/home/statistics-bg.png));

&__item {
display: flex;
align-items: center;

&__icon {
width: 3.75rem;
height: 3.75rem;
margin-right: 1rem;
}

&__count {
color: @home-text-color;
font-size: 2.25rem;
font-family: YouSheBiaoTiHei;
line-height: 3rem;
}

&__name {
color: @home-text-color-secondary;
font-size: 0.875rem;
}
}
}

+ 97
- 0
react-ui/src/pages/Home/components/Statistics/index.tsx View File

@@ -0,0 +1,97 @@
import CodeIcon from '@/assets/img/home/code.png';
import DatasetIcon from '@/assets/img/home/dataset.png';
import ImageIcon from '@/assets/img/home/image.png';
import ModelIcon from '@/assets/img/home/model.png';
import ServiceIcon from '@/assets/img/home/service.png';
import { getAssetPublicCountReq } from '@/services/home';
import { to } from '@/utils/promise';
import { useEffect, useState } from 'react';
import styles from './index.less';

function StatisticsBlock() {
const [assetCounts, setAssetCounts] = useState<
{ title: string; value: number | undefined; icon: string }[]
>([
{
title: '数据集',
value: undefined,
icon: DatasetIcon,
},
{
title: '模型',
value: undefined,
icon: ModelIcon,
},
{
title: '镜像',
value: undefined,
icon: ImageIcon,
},
{
title: '代码配置',
value: undefined,
icon: CodeIcon,
},
{
title: '服务',
value: undefined,
icon: ServiceIcon,
},
]);
useEffect(() => {
const getAssetPublicCount = async () => {
const [res] = await to(getAssetPublicCountReq());
if (res && res.data) {
const { dataset, image, model, codeConfig, service } = res.data;
const items = [
{
title: '数据集',
value: dataset,
icon: DatasetIcon,
},
{
title: '模型',
value: model,
icon: ModelIcon,
},
{
title: '镜像',
value: image,
icon: ImageIcon,
},
{
title: '代码配置',
value: codeConfig,
icon: CodeIcon,
},
{
title: '服务',
value: service,
icon: ServiceIcon,
},
];
setAssetCounts(items);
}
};

getAssetPublicCount();
}, []);

return (
<div className={styles['statistics']}>
{assetCounts.map((item) => {
return (
<div key={item.title} className={styles['statistics__item']}>
<img src={item.icon} draggable={false} className={styles['statistics__item__icon']} />
<div>
<div className={styles['statistics__item__count']}>{item.value ?? '--'}</div>
<div className={styles['statistics__item__name']}>{item.title}</div>
</div>
</div>
);
})}
</div>
);
}

export default StatisticsBlock;

+ 50
- 0
react-ui/src/pages/Home/components/ViewMore/index.less View File

@@ -0,0 +1,50 @@
.view-more {
position: absolute;
right: 16.25rem;
display: flex;
align-items: center;
color: .addAlpha(@home-text-color, 0.7) [];
font-size: 0.9375rem;
cursor: pointer;
transition: color 0.3s;
&:hover {
color: @home-text-color;
}

&__img-container {
position: relative;
width: 1.125rem;
height: 1.125rem;
margin-left: 0.75rem;
transition: width 0.3s ease-in-out;
}

&:hover &__img-container {
width: 1.625rem;
}

&__img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
transition: opacity 0.3s ease-in-out;

&--first {
opacity: 1;
}

&--second {
opacity: 0;
}
}

&:hover &__img--first {
opacity: 0;
}

&:hover &__img--second {
opacity: 1;
}
}

+ 31
- 0
react-ui/src/pages/Home/components/ViewMore/index.tsx View File

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

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

function ViewMore({ className, style, onClick }: ViewMoreProps) {
return (
<div className={classNames(styles['view-more'], className)} style={style} onClick={onClick}>
<span>查看更多</span>
<div className={styles['view-more__img-container']}>
<img
className={classNames(styles['view-more__img'], styles['view-more__img--first'])}
src={require('@/assets/img/home/right-arrow.png')}
></img>
<img
className={classNames(styles['view-more__img'], styles['view-more__img--second'])}
src={require('@/assets/img/home/right-arrow-hover.png')}
></img>
</div>
</div>
);
}

export default ViewMore;

+ 21
- 0
react-ui/src/pages/Home/index.less View File

@@ -0,0 +1,21 @@
.home {
height: 100%;
overflow-y: auto;
font-family: Alibaba;

&__separator {
position: relative;
z-index: 10;
display: block;
width: 97.5rem;
height: 8.375rem;
margin: -10.75rem auto 0;
}

&__dataset-mirror {
background-image: url(@/assets/img/home/dataset-bg.png);
background-repeat: no-repeat;
background-position: top 6.25rem left;
background-size: 100% 100%;
}
}

+ 31
- 0
react-ui/src/pages/Home/index.tsx View File

@@ -0,0 +1,31 @@
import CodeConfig from './components/CodeConfig';
import DatasetBlock from './components/Dataset';
import Footer from './components/Footer';
import IntroBlock from './components/Intro';
import MirrorBlock from './components/Mirror';
import ModelBlock from './components/Model';
import ServiceBlock from './components/Service';
import styles from './index.less';

function Home() {
return (
<div className={styles.home}>
<IntroBlock></IntroBlock>
<ServiceBlock></ServiceBlock>
<ModelBlock></ModelBlock>
<img
src={require('@/assets/img/home/model-between-dataset.png')}
draggable={false}
className={styles['home__separator']}
></img>
<div className={styles['home__dataset-mirror']}>
<DatasetBlock></DatasetBlock>
<MirrorBlock></MirrorBlock>
</div>
<CodeConfig></CodeConfig>
<Footer></Footer>
</div>
);
}

export default Home;

+ 2
- 0
react-ui/src/pages/Mirror/List/index.tsx View File

@@ -47,6 +47,8 @@ export type MirrorData = {
name: string;
description: string;
create_time: string;
create_by: string;
version_count: number;
};

function MirrorList() {


+ 1
- 1
react-ui/src/pages/ModelDeployment/types.ts View File

@@ -8,7 +8,7 @@ export type ServiceData = {
service_type_name: string; // 服务类型中文
description: string; // 描述
version_count: number; // 版本数量
created_by: string;
create_by: string;
create_time: string;
update_by: string;
update_time: string;


+ 6
- 0
react-ui/src/pages/User/Login/index.tsx View File

@@ -15,6 +15,12 @@ const Login = () => {
if (res && res.data) {
const clientInfo = res.data;
SessionStorage.setItem(SessionStorage.clientInfoKey, clientInfo, true);
const urlParams = new URL(window.location.href).searchParams;
const redirectUrl = urlParams.get('redirect');
console.log('login redirectUrl', redirectUrl);
if (redirectUrl) {
SessionStorage.setItem(SessionStorage.redirectUrl, redirectUrl);
}
gotoOAuth2();
}
};


+ 2
- 1
react-ui/src/pages/User/Login/login.tsx View File

@@ -1,6 +1,7 @@
import { clearSessionToken, setSessionToken } from '@/access';
import { getCaptchaImg, login } from '@/services/system/auth';
import { parseJsonText } from '@/utils';
import { HomeUrl } from '@/utils/constant';
import { safeInvoke } from '@/utils/functional';
import LocalStorage from '@/utils/localStorage';
import { to } from '@/utils/promise';
@@ -97,7 +98,7 @@ const Login = () => {

await fetchUserInfo();
const urlParams = new URL(window.location.href).searchParams;
history.replace(urlParams.get('redirect') || '/');
history.replace(urlParams.get('redirect') || HomeUrl);
} else {
if (error?.data?.code === 500 && error?.data?.msg === '验证码错误') {
captchaInputRef.current?.focus({


+ 2
- 1
react-ui/src/pages/missingPage.jsx View File

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

const MissingPage = () => {
const navigate = useNavigate();
@@ -12,7 +13,7 @@ const MissingPage = () => {
content={'很抱歉,您访问的正在开发中,\n请耐心等待。'}
hasFooter={true}
buttonTitle="返回首页"
onButtonClick={() => navigate('/')}
onButtonClick={() => navigate(HomeUrl)}
></KFEmpty>
);
};


+ 67
- 0
react-ui/src/services/home/index.ts View File

@@ -0,0 +1,67 @@
/*
* @Author: 赵伟
* @Date: 2025-07-21 14:29:44
* @Description: 首页
*/

import { request } from '@umijs/max';

// 获取公开的资源数量
export function getAssetPublicCountReq() {
return request('/api/mmp/workspace/assetPublicCount', {
method: 'GET',
headers: {
isToken: false,
},
});
}

// 获取公开服务
export function getPublicServicesReq() {
return request('/api/mmp/workspace/getPublicServices', {
method: 'GET',
headers: {
isToken: false,
},
});
}

// 获取公开模型
export function getPublicModelsReq() {
return request('/api/mmp/workspace/getPublicModels', {
method: 'GET',
headers: {
isToken: false,
},
});
}

// 获取公开数据集
export function getPublicDatasetsReq() {
return request('/api/mmp/workspace/getPublicDatasets', {
method: 'GET',
headers: {
isToken: false,
},
});
}

// 获取代码配置
export function getPublicCodeConfigsReq() {
return request('/api/mmp/workspace/getPublicCodeConfigs', {
method: 'GET',
headers: {
isToken: false,
},
});
}

// 获取公开镜像
export function getPublicImagesReq() {
return request('/api/mmp/workspace/getPublicImages', {
method: 'GET',
headers: {
isToken: false,
},
});
}

+ 11
- 1
react-ui/src/styles/theme.less View File

@@ -27,6 +27,11 @@
@heading-color: rgba(0, 0, 0, 0.85);
@input-icon-hover-color: rgba(0, 0, 0, 0.85);

@home-text-color: #020814;
@home-text-color-secondary: @text-color-secondary;
@home-text-color-tertiary: #8284a4;
@home-divider-color: #d8d8d8;

@workspace-background: linear-gradient(
179.03deg,
rgba(138, 138, 138, 0.06) 0%,
@@ -51,6 +56,11 @@
@result: rgba(@red, @green, @blue, @alpha);
}

// 转换为 rem
.rem(@px) {
@result: (@px / 16) * 1rem;
}

// 混合
// 单行
.singleLine() {
@@ -73,7 +83,7 @@
.backgroundFullImage(@url) {
background-image: @url;
background-repeat: no-repeat;
background-position: top center;
background-position: top left;
background-size: 100% 100%;
}



+ 3
- 0
react-ui/src/utils/constant.ts View File

@@ -20,3 +20,6 @@ export const ServiceCreatedMessage = 'serviceCreated';

// 实验完成
export const ExperimentCompleted = 'ExperimentCompleted';

// 首页地址
export const HomeUrl = '/workspace';

+ 14
- 1
react-ui/src/utils/index.ts View File

@@ -7,6 +7,9 @@
import { PageEnum } from '@/enums/pagesEnums';
import G6 from '@antv/g6';

// 白名单,不需要登录
const whiteList = [PageEnum.Root, PageEnum.LOGIN, PageEnum.Authorize, PageEnum.Home];

/**
* 生成 8 位随机数
* @returns 8 位随机数
@@ -295,7 +298,17 @@ export const getGitUrl = (url?: string, branch?: string): string => {
* @return {boolean} true if the pathname needs to be authorized, false otherwise
*/
export const needAuth = (pathname: string): boolean => {
return pathname !== PageEnum.LOGIN && pathname !== PageEnum.Authorize;
return !whiteList.includes(pathname as PageEnum);
};

/**
* 是否是登录页
*
* @param {string} pathname - the pathname to be checked
* @return {boolean} true if the pathname needs to be authorized, false otherwise
*/
export const isLoginPage = (pathname: string): boolean => {
return pathname === PageEnum.LOGIN || pathname === PageEnum.Authorize;
};

/**


+ 5
- 0
react-ui/src/utils/sessionStorage.ts View File

@@ -13,8 +13,13 @@ export default class SessionStorage {
static readonly aimUrlKey = 'aim-url';
/** tensorBoard url */
static readonly tensorBoardUrlKey = 'tensor-board-url';
<<<<<<< HEAD
/** 登录之前的地址 */
static readonly redirectUrl = 'redirect-url';
=======
// /** 云际系统 Token */
// static readonly jccTokenKey = 'jcc-token';
>>>>>>> dev-zw

/**
* 获取 SessionStorage 值


Loading…
Cancel
Save