diff --git a/.gitignore b/.gitignore index 54c883c5..41d080fe 100644 --- a/.gitignore +++ b/.gitignore @@ -65,3 +65,4 @@ mvnw /react-ui/types/tsconfig.tsbuildinfo /react-ui/storybook-static /react-ui/.storybook/scripts +/react-ui/dist.zip diff --git a/react-ui/mock/components.ts b/react-ui/mock/components.ts new file mode 100644 index 00000000..ab5b5f1f --- /dev/null +++ b/react-ui/mock/components.ts @@ -0,0 +1,1294 @@ +import { defineMock } from 'umi'; + +export default defineMock({ + 'GET /api/mmp/workflow/235': { + code: 200, + msg: '操作成功', + data: { + id: 233, + name: '分布式训练', + description: 'aa', + dag: { + nodes: [ + { + id: 'git-clone-c0724278', + category_id: 1, + component_name: 'git-clone', + component_label: '代码拉取组件', + task_info: { + image: { + type: 'ref', + item_type: 'image', + label: '镜像', + value: null, + visible: false, + editable: false, + require: 1, + default: '', + condition: '', + description: '克隆代码的镜像', + placeholder: '请选择镜像', + rulers: {}, + }, + working_directory: { + type: 'str', + item_type: '', + label: '工作目录', + value: '', + visible: false, + editable: false, + require: 1, + default: '', + placeholder: '请输入工作目录', + condition: '', + description: '容器内的工作目录', + rulers: {}, + }, + command: { + type: 'str', + item_type: '', + label: '启动命令', + value: '', + visible: false, + editable: false, + require: 1, + default: '', + placeholder: '请输入启动命令', + description: '启动命令,不包括运行参数', + rulers: '', + }, + run_args: { + type: 'map', + item_type: '', + label: '运行参数', + value: [], + visible: false, + editable: false, + require: 0, + default: '', + placeholder: '', + condition: '', + description: '运行命令的参数', + rulers: '', + }, + resources_standard: { + type: 'select', + item_type: 'resource', + label: '资源', + value: {}, + visible: false, + editable: false, + require: 1, + default: '', + placeholder: '', + condition: '', + description: '资源规格', + rulers: {}, + }, + }, + + control_strategy: { + retry_times: { + type: 'str', + item_type: '', + label: '重试次数', + require: 0, + default: '', + placeholder: '', + describe: '任务重试次数', + visible: true, + editable: false, + condition: '', + value: '', + rulers: {}, + }, + max_run_times: { + type: 'str', + item_type: '', + label: '最大运行时间', + require: 0, + default: '', + placeholder: '', + describe: '最大运行时间', + editable: false, + visible: true, + condition: '', + value: '', + rulers: {}, + }, + }, + + in_parameters: { + '--code_config': { + type: 'ref', + item_type: 'code', + label: '代码配置', + require: 1, + default: '', + placeholder: '私有仓库填写ssh地址,公有仓库填写https git地址', + describe: + '代码配置,支持私有仓库和公有仓库,私有仓库填写ssh地址,公有仓库填写https git地址', + editable: false, + visible: true, + condition: '', + value: { + id: 21, + code_repo_name: '原子掺杂识别', + code_repo_vis: 1, + is_public: true, + git_url: 'https://gitlink.org.cn/somunslotus/material-atom-predict.git', + git_branch: 'master', + verify_mode: null, + git_user_name: null, + git_password: null, + ssh_key: null, + create_by: 'admin', + create_time: '2025-03-12T16:46:08.000+08:00', + update_by: 'admin', + update_time: '2025-03-14T14:59:19.000+08:00', + state: 1, + }, + rulers: {}, + showValue: '原子掺杂识别', + fromSelect: true, + }, + }, + + out_parameters: { + '--code_output': { + type: 'str', + item_type: 'code', + label: '代码保存路径', + require: 1, + default: '/code', + editable: false, + visible: true, + placeholder: '代码保存路径', + describe: '代码保存路径', + condition: '', + showValue: '/mycode', + value: '/mycode', + rulers: {}, + fromSelect: false, + }, + }, + + available_range: 0, + description: '代码拉取组件', + icon_path: 'component-icon-1', + create_by: 'admin', + create_time: '2024-09-02T06:08:06.000+08:00', + update_by: 'admin', + update_time: '2024-09-02T06:08:06.000+08:00', + state: 1, + env_variables: [], + x: 612, + y: 215, + label: '代码拉取', + img: '/assets/images/component-icon-1.png', + isCluster: false, + formError: false, + type: 'rect-node', + size: [110, 36], + labelCfg: { + style: { + fill: 'transparent', + fontSize: 0, + boxShadow: '0px 0px 12px rgba(75, 84, 137, 0.05)', + overflow: 'hidden', + x: -20, + y: 0, + textAlign: 'left', + textBaseline: 'middle', + }, + }, + style: { + active: { + fill: 'rgb(247, 250, 255)', + stroke: 'rgb(95, 149, 255)', + lineWidth: 2, + shadowColor: 'rgb(95, 149, 255)', + shadowBlur: 10, + }, + selected: { + fill: 'rgb(255, 255, 255)', + stroke: 'rgb(95, 149, 255)', + lineWidth: 4, + shadowColor: 'rgb(95, 149, 255)', + shadowBlur: 10, + 'text-shape': { + fontWeight: 500, + }, + }, + highlight: { + fill: 'rgb(223, 234, 255)', + stroke: '#4572d9', + lineWidth: 2, + 'text-shape': { + fontWeight: 500, + }, + }, + inactive: { + fill: 'rgb(247, 250, 255)', + stroke: 'rgb(191, 213, 255)', + lineWidth: 1, + }, + disable: { + fill: 'rgb(250, 250, 250)', + stroke: 'rgb(224, 224, 224)', + lineWidth: 1, + }, + fill: '#fff', + stroke: 'transparent', + cursor: 'pointer', + radius: 8, + shadowColor: 'rgba(75, 84, 137, 0.4)', + shadowBlur: 6, + shadowOffsetX: 0, + shadowOffsetY: 0, + overflow: 'hidden', + lineWidth: 0.5, + }, + depth: 0, + }, + { + id: 'model-train-39e9bc7c', + category_id: 2, + component_name: 'model-train', + component_label: '模型训练', + task_info: { + image: { + type: 'ref', + item_type: 'image', + label: '镜像', + value: {}, + visible: true, + editable: true, + require: 1, + default: '', + condition: '', + description: '镜像', + placeholder: '', + rulers: {}, + }, + working_directory: { + type: 'str', + item_type: '', + label: '工作目录', + value: '{{git-clone-c0724278.--code_output}}', + visible: true, + editable: true, + require: 1, + default: '', + placeholder: '', + condition: '', + description: '容器内的工作目录', + rulers: {}, + }, + command: { + type: 'str', + item_type: '', + label: '启动命令', + value: 'conda run -n atom-predict python recognize_dophant/egnn/train_pl_vor.py', + visible: true, + editable: true, + require: 1, + default: '', + placeholder: '', + description: '启动命令,不包括运行参数', + rulers: '', + }, + run_args: { + type: 'map', + item_type: '', + label: '运行参数', + value: [], + visible: true, + editable: true, + require: 0, + default: '', + placeholder: '', + condition: '', + description: '运行命令的参数', + rulers: '', + }, + resources_standard: { + type: 'select', + item_type: 'resource', + label: '资源', + value: { + id: 30, + resource_id: 4, + computing_resource: 'CPU', + standard: { + name: 'CPU', + value: { + gpu: 0, + cpu: 4, + memory: '8GB', + }, + }, + description: 'GPU: 0, CPU:4, 内存: 8GB', + cpu_cores: 4, + memory_gb: 8, + gpu_memory_gb: 0, + gpu_nums: 0, + credit_per_hour: 2.0, + labels: 'accelertor=cpu', + create_by: 'admin', + create_time: '2024-04-19T11:39:40.000+08:00', + update_by: 'admin', + update_time: '2024-04-19T11:39:40.000+08:00', + state: 1, + }, + visible: true, + editable: true, + require: 1, + default: '', + placeholder: '', + condition: '', + description: '资源规格', + rulers: {}, + }, + }, + + control_strategy: { + retry_times: { + type: 'str', + item_type: '', + label: '重试次数', + require: 0, + default: '', + placeholder: '', + describe: '任务重试次数', + visible: true, + editable: true, + condition: '', + value: '', + rulers: {}, + }, + max_run_times: { + type: 'str', + item_type: '', + label: '最大运行时间', + require: 0, + default: '', + placeholder: '', + describe: '最大运行时间', + editable: true, + visible: true, + condition: '', + value: '', + rulers: {}, + }, + }, + in_parameters: { + '--dataset': { + type: 'ref', + item_type: 'dataset', + label: '选择数据集', + require: 1, + default: '', + placeholder: '', + describe: '选择数据集', + condition: '', + visible: true, + editable: true, + value: { + id: '73', + name: '原子掺杂识别场景测试', + version: 'v1', + path: 'fanshuai/datasets/73/fanshuai_dataset_20250519103524/v1/dataset', + identifier: 'fanshuai_dataset_20250519103524', + owner: 'fanshuai', + }, + rulers: {}, + showValue: '原子掺杂识别场景测试:v1', + fromSelect: true, + activeTab: 'Private', + expandedKeys: ['73'], + checkedKeys: ['73-v1'], + }, + '--model_name': { + type: 'ref', + item_type: 'model', + label: '选择模型', + require: 0, + default: '', + placeholder: '', + describe: '最大运行时间', + editable: true, + visible: true, + condition: '', + value: { + id: '39', + name: '原子参杂识别模型', + version: 'v1', + path: 'fanshuai/model/39/fanshuai_model_20250513113514/v1/model', + identifier: 'fanshuai_model_20250513113514', + owner: 'fanshuai', + }, + rulers: {}, + showValue: '原子参杂识别模型:v1', + fromSelect: true, + activeTab: 'Private', + expandedKeys: ['39'], + checkedKeys: ['39-v1'], + }, + }, + out_parameters: { + '--model_output': { + type: 'str', + item_type: '', + label: '模型输出路径', + require: 1, + showValue: '/model', + default: '', + placeholder: '', + describe: '模型输出路径', + editable: true, + visible: true, + rulers: {}, + condition: '', + value: '/model', + fromSelect: false, + }, + }, + available_range: 1, + description: '通用模型训练组件介绍', + icon_path: 'component-icon-2', + create_by: 'admin', + create_time: '2024-05-28T07:33:53.000+08:00', + update_by: 'admin', + update_time: '2024-05-28T07:33:53.000+08:00', + state: 1, + env_variables: [], + x: 596, + y: 348, + label: '模型训练', + img: '/assets/images/component-icon-2.png', + isCluster: false, + formError: false, + type: 'rect-node', + size: [110, 36], + labelCfg: { + style: { + fill: 'transparent', + fontSize: 0, + boxShadow: '0px 0px 12px rgba(75, 84, 137, 0.05)', + overflow: 'hidden', + x: -20, + y: 0, + textAlign: 'left', + textBaseline: 'middle', + }, + }, + style: { + active: { + fill: 'rgb(247, 250, 255)', + stroke: 'rgb(95, 149, 255)', + lineWidth: 2, + shadowColor: 'rgb(95, 149, 255)', + shadowBlur: 10, + }, + selected: { + fill: 'rgb(255, 255, 255)', + stroke: 'rgb(95, 149, 255)', + lineWidth: 4, + shadowColor: 'rgb(95, 149, 255)', + shadowBlur: 10, + 'text-shape': { + fontWeight: 500, + }, + }, + highlight: { + fill: 'rgb(223, 234, 255)', + stroke: '#4572d9', + lineWidth: 2, + 'text-shape': { + fontWeight: 500, + }, + }, + inactive: { + fill: 'rgb(247, 250, 255)', + stroke: 'rgb(191, 213, 255)', + lineWidth: 1, + }, + disable: { + fill: 'rgb(250, 250, 250)', + stroke: 'rgb(224, 224, 224)', + lineWidth: 1, + }, + fill: '#fff', + stroke: 'transparent', + cursor: 'pointer', + radius: 8, + shadowColor: 'rgba(75, 84, 137, 0.4)', + shadowBlur: 6, + shadowOffsetX: 0, + shadowOffsetY: 0, + overflow: 'hidden', + lineWidth: 0.5, + }, + depth: 0, + }, + { + id: 'model-evaluate-c5b68e7c', + category_id: 4, + component_name: 'model-evaluate', + component_label: '模型测试', + task_info: { + image: { + type: 'ref', + item_type: 'image', + label: '镜像', + value: { + id: 15, + image_id: 17, + version: 'v1', + description: null, + url: '172.20.32.187/machine-learning/atom-egnn:v2', + tag_name: 'v1', + file_size: '125MB', + status: 'Available', + create_by: 'fanshuai', + create_time: '2024-04-18T00:00:00.000+08:00', + update_by: 'admin', + update_time: '2024-04-18T00:00:00.000+08:00', + state: 1, + host_ip: null, + }, + visible: false, + editable: false, + require: 0, + default: '', + condition: '', + description: '镜像', + placeholder: '', + rulers: {}, + }, + working_directory: { + type: 'str', + item_type: '', + label: '工作目录', + value: '{{git-clone-c0724278.--code_output}}', + visible: false, + editable: false, + require: 0, + default: '', + placeholder: '', + condition: '', + description: '容器内的工作目录', + rulers: {}, + }, + command: { + type: 'str', + item_type: '', + label: '启动命令', + value: 'conda run -n atom-predict python recognize_dophant/egnn/test_pl_vor.py', + visible: false, + editable: false, + require: 0, + default: '', + placeholder: '', + description: '启动命令,不包括运行参数', + rulers: '', + }, + run_args: { + type: 'map', + item_type: '', + label: '运行参数', + value: [], + visible: false, + editable: false, + require: 0, + default: '', + placeholder: '', + condition: '', + description: '运行命令的参数', + rulers: '', + }, + resources_standard: { + type: 'select', + item_type: 'resource', + label: '资源', + value: { + id: 30, + resource_id: 4, + computing_resource: 'CPU', + standard: { + name: 'CPU', + value: { + gpu: 0, + cpu: 4, + memory: '8GB', + }, + }, + description: 'GPU: 0, CPU:4, 内存: 8GB', + cpu_cores: 4, + memory_gb: 8, + gpu_memory_gb: 0, + gpu_nums: 0, + credit_per_hour: 2.0, + labels: 'accelertor=cpu', + create_by: 'admin', + create_time: '2024-04-19T11:39:40.000+08:00', + update_by: 'admin', + update_time: '2024-04-19T11:39:40.000+08:00', + state: 1, + }, + visible: true, + editable: true, + require: 1, + default: '', + placeholder: '', + condition: '', + description: '资源规格', + rulers: {}, + }, + }, + + control_strategy: { + retry_times: { + type: 'str', + item_type: '', + label: '重试次数', + require: 0, + default: '', + placeholder: '', + describe: '任务重试次数', + visible: true, + editable: true, + condition: '', + value: '', + rulers: {}, + }, + max_run_times: { + type: 'str', + item_type: '', + label: '最大运行时间', + require: 0, + default: '', + placeholder: '', + describe: '最大运行时间', + editable: true, + visible: true, + condition: '', + value: '', + rulers: {}, + }, + }, + + in_parameters: { + '--dataset': { + type: 'ref', + item_type: 'dataset', + label: '选择数据集', + require: 1, + default: '', + placeholder: '', + describe: '选择数据集', + condition: '', + editable: true, + visible: true, + value: { + id: '74', + name: '原子掺杂识别模型测试数据集', + version: 'v2', + path: 'fanshuai/datasets/74/fanshuai_dataset_20250519103749/v2/dataset', + identifier: 'fanshuai_dataset_20250519103749', + owner: 'fanshuai', + }, + rulers: {}, + showValue: '原子掺杂识别模型测试数据集:v2', + fromSelect: true, + activeTab: 'Private', + expandedKeys: ['74'], + checkedKeys: ['74-v2'], + }, + '--model_name': { + type: 'ref', + item_type: 'model', + label: '选择模型', + require: 1, + editable: true, + visible: true, + rulers: {}, + default: '', + placeholder: '', + describe: '这里是这个参数的描述和备注', + condition: '', + value: '{{model-train-39e9bc7c.--model_output}}', + showValue: '{{model-train-39e9bc7c.--model_output}}', + fromSelect: true, + }, + }, + out_parameters: { + '--model_output': { + type: 'str', + item_type: '', + label: '模型测试结果路径', + editable: true, + visible: true, + rulers: {}, + default: '', + placeholder: '', + describe: '这里是这个参数的描述和备注', + condition: '', + require: 1, + showValue: '/result', + value: '/result', + fromSelect: false, + }, + }, + available_range: 1, + description: '模型测试', + icon_path: 'component-icon-4', + create_by: 'admin', + create_time: '2024-05-24T07:00:10.000+08:00', + update_by: 'admin', + update_time: '2024-05-24T07:00:10.000+08:00', + state: 1, + env_variables: [], + x: 600, + y: 460, + label: '模型评估', + img: '/assets/images/component-icon-4.png', + isCluster: false, + formError: false, + type: 'rect-node', + size: [110, 36], + labelCfg: { + style: { + fill: 'transparent', + fontSize: 0, + boxShadow: '0px 0px 12px rgba(75, 84, 137, 0.05)', + overflow: 'hidden', + x: -20, + y: 0, + textAlign: 'left', + textBaseline: 'middle', + }, + }, + style: { + active: { + fill: 'rgb(247, 250, 255)', + stroke: 'rgb(95, 149, 255)', + lineWidth: 2, + shadowColor: 'rgb(95, 149, 255)', + shadowBlur: 10, + }, + selected: { + fill: 'rgb(255, 255, 255)', + stroke: 'rgb(95, 149, 255)', + lineWidth: 4, + shadowColor: 'rgb(95, 149, 255)', + shadowBlur: 10, + 'text-shape': { + fontWeight: 500, + }, + }, + highlight: { + fill: 'rgb(223, 234, 255)', + stroke: '#4572d9', + lineWidth: 2, + 'text-shape': { + fontWeight: 500, + }, + }, + inactive: { + fill: 'rgb(247, 250, 255)', + stroke: 'rgb(191, 213, 255)', + lineWidth: 1, + }, + disable: { + fill: 'rgb(250, 250, 250)', + stroke: 'rgb(224, 224, 224)', + lineWidth: 1, + }, + fill: '#fff', + stroke: 'transparent', + cursor: 'pointer', + radius: 8, + shadowColor: 'rgba(75, 84, 137, 0.4)', + shadowBlur: 6, + shadowOffsetX: 0, + shadowOffsetY: 0, + overflow: 'hidden', + lineWidth: 0.5, + }, + depth: 0, + }, + { + id: 'model-export-edcf438e', + category_id: 6, + component_name: 'model-export', + component_label: '模型导出', + task_info: { + image: { + type: 'ref', + item_type: 'image', + label: '镜像', + value: {}, + visible: true, + editable: true, + require: 1, + default: '', + condition: '', + description: '镜像', + placeholder: '', + rulers: {}, + }, + working_directory: { + type: 'str', + item_type: '', + label: '工作目录', + value: '{{git-clone-c0724278.--code_output}}', + visible: true, + editable: true, + require: 1, + default: '', + placeholder: '', + condition: '', + description: '容器内的工作目录', + rulers: {}, + }, + command: { + type: 'str', + item_type: '', + label: '启动命令', + value: 'conda run -n atom-predict python recognize_dophant/egnn/test_pl_vor.py', + visible: true, + editable: true, + require: 1, + default: '', + placeholder: '', + description: '启动命令,不包括运行参数', + rulers: '', + }, + run_args: { + type: 'map', + item_type: '', + label: '运行参数', + value: [], + visible: true, + editable: true, + require: 0, + default: '', + placeholder: '', + condition: '', + description: '运行命令的参数', + rulers: '', + }, + resources_standard: { + type: 'select', + item_type: 'resource', + label: '资源', + value: {}, + visible: false, + editable: false, + require: 1, + default: '', + placeholder: '', + condition: '', + description: '资源规格', + rulers: {}, + }, + }, + control_strategy: { + retry_times: { + type: 'str', + item_type: '', + label: '重试次数', + require: 0, + default: '', + placeholder: '', + describe: '任务重试次数', + visible: true, + editable: true, + condition: '', + value: '', + rulers: {}, + }, + max_run_times: { + type: 'str', + item_type: '', + label: '最大运行时间', + require: 0, + default: '', + placeholder: '', + describe: '最大运行时间', + editable: true, + visible: true, + condition: '', + value: '', + rulers: {}, + }, + }, + + in_parameters: { + '--model_source': { + type: 'str', + item_type: '', + label: '模型来源', + require: 1, + default: '', + placeholder: '模型来源', + describe: '模型来源', + editable: true, + visible: true, + condition: '', + value: '{{model-train-39e9bc7c.--model_output}}', + rulers: {}, + fromSelect: true, + showValue: '{{model-train-39e9bc7c.--model_output}}', + }, + '--model_id': { + type: 'select', + item_type: 'model', + label: '导出到模型', + require: 1, + default: '', + placeholder: '', + describe: '导出到模型', + editable: true, + visible: true, + condition: '', + value: { + id: '76', + name: '原子掺杂识别模型场景测试', + identifier: 'fanshuai_model_20250519105223', + owner: 'fanshuai', + }, + rulers: {}, + }, + '--version': { + type: 'str', + item_type: '', + label: '模型版本', + require: 1, + choice: [], + default: '1', + placeholder: '', + describe: '模型版本', + editable: false, + condition: '', + showValue: '${model_version}', + value: '${model_version}', + fromSelect: false, + }, + '--description': { + type: 'str', + item_type: '', + label: '版本描述', + require: 1, + choice: [], + default: '', + placeholder: '版本描述', + describe: '版本描述', + editable: false, + condition: '', + showValue: '流水线自动导出', + value: '流水线自动导出', + fromSelect: false, + }, + }, + available_range: 0, + description: '模型导出', + icon_path: 'component-icon-8', + create_by: 'admin', + create_time: '2024-05-29T01:12:01.000+08:00', + update_by: 'admin', + update_time: '2024-05-29T09:11:55.000+08:00', + state: 1, + env_variables: [], + x: 592, + y: 581, + label: '模型导出', + img: '/assets/images/component-icon-8.png', + isCluster: false, + formError: false, + type: 'rect-node', + size: [110, 36], + labelCfg: { + style: { + fill: 'transparent', + fontSize: 0, + boxShadow: '0px 0px 12px rgba(75, 84, 137, 0.05)', + overflow: 'hidden', + x: -20, + y: 0, + textAlign: 'left', + textBaseline: 'middle', + }, + }, + style: { + active: { + fill: 'rgb(247, 250, 255)', + stroke: 'rgb(95, 149, 255)', + lineWidth: 2, + shadowColor: 'rgb(95, 149, 255)', + shadowBlur: 10, + }, + selected: { + fill: 'rgb(255, 255, 255)', + stroke: 'rgb(95, 149, 255)', + lineWidth: 4, + shadowColor: 'rgb(95, 149, 255)', + shadowBlur: 10, + 'text-shape': { + fontWeight: 500, + }, + }, + highlight: { + fill: 'rgb(223, 234, 255)', + stroke: '#4572d9', + lineWidth: 2, + 'text-shape': { + fontWeight: 500, + }, + }, + inactive: { + fill: 'rgb(247, 250, 255)', + stroke: 'rgb(191, 213, 255)', + lineWidth: 1, + }, + disable: { + fill: 'rgb(250, 250, 250)', + stroke: 'rgb(224, 224, 224)', + lineWidth: 1, + }, + fill: '#fff', + stroke: 'transparent', + cursor: 'pointer', + radius: 8, + shadowColor: 'rgba(75, 84, 137, 0.4)', + shadowBlur: 6, + shadowOffsetX: 0, + shadowOffsetY: 0, + overflow: 'hidden', + lineWidth: 0.5, + }, + depth: 0, + }, + ], + edges: [ + { + source: 'git-clone-c0724278', + target: 'model-train-39e9bc7c', + style: { + endArrow: { + path: 'M 6,0 L 9,-1.5 L 9,1.5 Z', + d: 4.5, + fill: '#CDD0DC', + }, + cursor: 'pointer', + lineWidth: 1, + lineAppendWidth: 4, + opacity: 1, + stroke: '#CDD0DC', + radius: 1, + active: { + stroke: 'rgb(95, 149, 255)', + lineWidth: 1, + }, + selected: { + stroke: 'rgb(95, 149, 255)', + lineWidth: 2, + shadowColor: 'rgb(95, 149, 255)', + shadowBlur: 10, + 'text-shape': { + fontWeight: 500, + }, + }, + highlight: { + stroke: 'rgb(95, 149, 255)', + lineWidth: 2, + 'text-shape': { + fontWeight: 500, + }, + }, + inactive: { + stroke: 'rgb(234, 234, 234)', + lineWidth: 1, + }, + disable: { + stroke: 'rgb(245, 245, 245)', + lineWidth: 1, + }, + }, + labelCfg: { + autoRotate: true, + style: { + fontSize: 10, + fill: '#FFF', + }, + }, + id: 'edge-0.163955357654560491741769193740', + startPoint: { + x: 612, + y: 233.25, + anchorIndex: 1, + }, + endPoint: { + x: 596, + y: 329.75, + anchorIndex: 0, + }, + sourceAnchor: 1, + targetAnchor: 0, + type: 'cubic-vertical', + curvePosition: [0.5, 0.5], + minCurveOffset: [0, 0], + depth: 0, + }, + { + source: 'model-train-39e9bc7c', + target: 'model-evaluate-c5b68e7c', + style: { + endArrow: { + path: 'M 6,0 L 9,-1.5 L 9,1.5 Z', + d: 4.5, + fill: '#CDD0DC', + }, + cursor: 'pointer', + lineWidth: 1, + lineAppendWidth: 4, + opacity: 1, + stroke: '#CDD0DC', + radius: 1, + active: { + stroke: 'rgb(95, 149, 255)', + lineWidth: 1, + }, + selected: { + stroke: 'rgb(95, 149, 255)', + lineWidth: 2, + shadowColor: 'rgb(95, 149, 255)', + shadowBlur: 10, + 'text-shape': { + fontWeight: 500, + }, + }, + highlight: { + stroke: 'rgb(95, 149, 255)', + lineWidth: 2, + 'text-shape': { + fontWeight: 500, + }, + }, + inactive: { + stroke: 'rgb(234, 234, 234)', + lineWidth: 1, + }, + disable: { + stroke: 'rgb(245, 245, 245)', + lineWidth: 1, + }, + }, + labelCfg: { + autoRotate: true, + style: { + fontSize: 10, + fill: '#FFF', + }, + }, + id: 'edge-0.150681812754051241741769198063', + startPoint: { + x: 596, + y: 366.25, + anchorIndex: 1, + }, + endPoint: { + x: 600, + y: 441.75, + anchorIndex: 0, + }, + sourceAnchor: 1, + targetAnchor: 0, + type: 'cubic-vertical', + curvePosition: [0.5, 0.5], + minCurveOffset: [0, 0], + depth: 0, + }, + { + source: 'model-evaluate-c5b68e7c', + target: 'model-export-edcf438e', + style: { + endArrow: { + path: 'M 6,0 L 9,-1.5 L 9,1.5 Z', + d: 4.5, + fill: '#CDD0DC', + }, + cursor: 'pointer', + lineWidth: 1, + lineAppendWidth: 4, + opacity: 1, + stroke: '#CDD0DC', + radius: 1, + active: { + stroke: 'rgb(95, 149, 255)', + lineWidth: 1, + }, + selected: { + stroke: 'rgb(95, 149, 255)', + lineWidth: 2, + shadowColor: 'rgb(95, 149, 255)', + shadowBlur: 10, + 'text-shape': { + fontWeight: 500, + }, + }, + highlight: { + stroke: 'rgb(95, 149, 255)', + lineWidth: 2, + 'text-shape': { + fontWeight: 500, + }, + }, + inactive: { + stroke: 'rgb(234, 234, 234)', + lineWidth: 1, + }, + disable: { + stroke: 'rgb(245, 245, 245)', + lineWidth: 1, + }, + }, + labelCfg: { + autoRotate: true, + style: { + fontSize: 10, + fill: '#FFF', + }, + }, + id: 'edge-0.40652053406360491741769205906', + startPoint: { + x: 600, + y: 478.25, + anchorIndex: 1, + }, + endPoint: { + x: 592, + y: 562.75, + anchorIndex: 0, + }, + sourceAnchor: 1, + targetAnchor: 0, + type: 'cubic-vertical', + curvePosition: [0.5, 0.5], + minCurveOffset: [0, 0], + depth: 0, + }, + ], + combos: [], + }, + global_param: null, + create_by: 'fanshuai', + create_time: '2025-06-19T19:50:47.000+08:00', + update_by: 'fanshuai', + update_time: '2025-06-24T09:28:28.000+08:00', + state: 1, + }, + }, +}); diff --git a/react-ui/mock/home.ts b/react-ui/mock/home.ts new file mode 100644 index 00000000..25039391 --- /dev/null +++ b/react-ui/mock/home.ts @@ -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, + }, + }, +}); diff --git a/react-ui/mock/notices.ts b/react-ui/mock/notices.ts deleted file mode 100644 index 616c9218..00000000 --- a/react-ui/mock/notices.ts +++ /dev/null @@ -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, -}; diff --git a/react-ui/mock/requestRecord.mock.js b/react-ui/mock/requestRecord.mock.js deleted file mode 100644 index 6c59e198..00000000 --- a/react-ui/mock/requestRecord.mock.js +++ /dev/null @@ -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', - }, -}; diff --git a/react-ui/mock/user.ts b/react-ui/mock/user.ts deleted file mode 100644 index 75edd340..00000000 --- a/react-ui/mock/user.ts +++ /dev/null @@ -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, -}; diff --git a/react-ui/package.json b/react-ui/package.json index 953b6329..5f317010 100644 --- a/react-ui/package.json +++ b/react-ui/package.json @@ -8,7 +8,7 @@ "build": "max build", "deploy": "npm run build && npm run gh-pages", "dev": "npm run start:dev", - "dev-no-sso": "cross-env NO_SSO=true npm run start:dev", + "dev-no-sso": "cross-env NO_SSO=true npm run start:mock", "docker-hub:build": "docker build -f Dockerfile.hub -t ant-design-pro ./", "docker-prod:build": "docker-compose -f ./docker/docker-compose.yml build", "docker-prod:dev": "docker-compose -f ./docker/docker-compose.yml up", @@ -35,7 +35,7 @@ "serve": "umi-serve", "start": "cross-env UMI_ENV=dev max dev", "start:dev": "cross-env REACT_APP_ENV=dev MOCK=none UMI_ENV=dev UMI_DEV_SERVER_COMPRESS=none max dev", - "start:mock": "cross-env REACT_APP_ENV=dev UMI_ENV=dev max dev", + "start:mock": "cross-env REACT_APP_ENV=dev UMI_ENV=dev UMI_DEV_SERVER_COMPRESS=none max dev", "start:pre": "cross-env REACT_APP_ENV=pre UMI_ENV=dev max dev", "start:test": "cross-env REACT_APP_ENV=test MOCK=none UMI_ENV=dev max dev", "storybook": "storybook dev -p 6006", @@ -75,6 +75,7 @@ "fabric": "^5.3.0", "highlight.js": "^11.7.0", "lodash": "^4.17.21", + "motion": "~12.23.12", "omit.js": "^2.0.2", "pnpm": "^8.9.0", "query-string": "^8.1.0", @@ -82,6 +83,7 @@ "rc-util": "^5.30.0", "react": "^18.2.0", "react-activation": "^0.12.4", + "react-countup": "~6.5.3", "react-cropper": "^2.3.3", "react-dev-inspector": "^1.8.1", "react-dom": "^18.2.0", diff --git a/react-ui/public/mockServiceWorker.js b/react-ui/public/mockServiceWorker.js index ec47a9a5..7f2f4b72 100644 --- a/react-ui/public/mockServiceWorker.js +++ b/react-ui/public/mockServiceWorker.js @@ -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() diff --git a/react-ui/src/app.tsx b/react-ui/src/app.tsx index 5df18e05..3eda3284 100644 --- a/react-ui/src/app.tsx +++ b/react-ui/src/app.tsx @@ -24,7 +24,7 @@ import './styles/menu.less'; 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'; /** @@ -43,7 +43,8 @@ export async function getInitialState(): Promise { } as API.CurrentUser; } catch (error) { console.error('getInitialState', error); - gotoLoginPage(true); + // gotoLoginPage(true); + gotoHomePage(); } return undefined; }; @@ -129,7 +130,7 @@ export const onRouteChange: RuntimeConfig['onRouteChange'] = async (e) => { const token = getAccessToken(); // 没有 token,跳转到登录页面 if (!token && needAuth(pathname)) { - gotoLoginPage(false); + gotoHomePage(); return; } diff --git a/react-ui/src/assets/img/home/app-logo-dark.png b/react-ui/src/assets/img/home/app-logo-dark.png new file mode 100644 index 00000000..ff9f95da Binary files /dev/null and b/react-ui/src/assets/img/home/app-logo-dark.png differ diff --git a/react-ui/src/assets/img/home/app-logo.png b/react-ui/src/assets/img/home/app-logo.png new file mode 100644 index 00000000..e5342fcb Binary files /dev/null and b/react-ui/src/assets/img/home/app-logo.png differ diff --git a/react-ui/src/assets/img/home/code-arrow.png b/react-ui/src/assets/img/home/code-arrow.png new file mode 100644 index 00000000..7446812d Binary files /dev/null and b/react-ui/src/assets/img/home/code-arrow.png differ diff --git a/react-ui/src/assets/img/home/code-bg.png b/react-ui/src/assets/img/home/code-bg.png new file mode 100644 index 00000000..194c033a Binary files /dev/null and b/react-ui/src/assets/img/home/code-bg.png differ diff --git a/react-ui/src/assets/img/home/code-item-bg-hover.png b/react-ui/src/assets/img/home/code-item-bg-hover.png new file mode 100644 index 00000000..d0b2fc65 Binary files /dev/null and b/react-ui/src/assets/img/home/code-item-bg-hover.png differ diff --git a/react-ui/src/assets/img/home/code-item-bg.png b/react-ui/src/assets/img/home/code-item-bg.png new file mode 100644 index 00000000..325500e9 Binary files /dev/null and b/react-ui/src/assets/img/home/code-item-bg.png differ diff --git a/react-ui/src/assets/img/home/code.png b/react-ui/src/assets/img/home/code.png index 510ed463..604107f9 100644 Binary files a/react-ui/src/assets/img/home/code.png and b/react-ui/src/assets/img/home/code.png differ diff --git a/react-ui/src/assets/img/home/dataset-bg.png b/react-ui/src/assets/img/home/dataset-bg.png new file mode 100644 index 00000000..c822ebf4 Binary files /dev/null and b/react-ui/src/assets/img/home/dataset-bg.png differ diff --git a/react-ui/src/assets/img/home/dataset-item-bg-hover.png b/react-ui/src/assets/img/home/dataset-item-bg-hover.png deleted file mode 100644 index 946aed2a..00000000 Binary files a/react-ui/src/assets/img/home/dataset-item-bg-hover.png and /dev/null differ diff --git a/react-ui/src/assets/img/home/dataset-item-bg.png b/react-ui/src/assets/img/home/dataset-item-bg.png index dd87e156..241b4f4b 100644 Binary files a/react-ui/src/assets/img/home/dataset-item-bg.png and b/react-ui/src/assets/img/home/dataset-item-bg.png differ diff --git a/react-ui/src/assets/img/home/dataset.png b/react-ui/src/assets/img/home/dataset.png index 44589180..fcd669ca 100644 Binary files a/react-ui/src/assets/img/home/dataset.png and b/react-ui/src/assets/img/home/dataset.png differ diff --git a/react-ui/src/assets/img/home/default-avatar.png b/react-ui/src/assets/img/home/default-avatar.png new file mode 100644 index 00000000..66dc36ca Binary files /dev/null and b/react-ui/src/assets/img/home/default-avatar.png differ diff --git a/react-ui/src/assets/img/home/footer-bg.png b/react-ui/src/assets/img/home/footer-bg.png new file mode 100644 index 00000000..618a77b5 Binary files /dev/null and b/react-ui/src/assets/img/home/footer-bg.png differ diff --git a/react-ui/src/assets/img/home/header-bg-mini.png b/react-ui/src/assets/img/home/header-bg-mini.png new file mode 100644 index 00000000..22632022 Binary files /dev/null and b/react-ui/src/assets/img/home/header-bg-mini.png differ diff --git a/react-ui/src/assets/img/home/header-bg.png b/react-ui/src/assets/img/home/header-bg.png index d5ee7796..db0d3c17 100644 Binary files a/react-ui/src/assets/img/home/header-bg.png and b/react-ui/src/assets/img/home/header-bg.png differ diff --git a/react-ui/src/assets/img/home/image.png b/react-ui/src/assets/img/home/image.png index 5985d29e..fac6397a 100644 Binary files a/react-ui/src/assets/img/home/image.png and b/react-ui/src/assets/img/home/image.png differ diff --git a/react-ui/src/assets/img/home/mirror-arrow.png b/react-ui/src/assets/img/home/mirror-arrow.png new file mode 100644 index 00000000..0cd71d90 Binary files /dev/null and b/react-ui/src/assets/img/home/mirror-arrow.png differ diff --git a/react-ui/src/assets/img/home/mirror-version.png b/react-ui/src/assets/img/home/mirror-version.png new file mode 100644 index 00000000..b3d33cfc Binary files /dev/null and b/react-ui/src/assets/img/home/mirror-version.png differ diff --git a/react-ui/src/assets/img/home/model-bg.png b/react-ui/src/assets/img/home/model-bg.png index 260e4020..2238efa0 100644 Binary files a/react-ui/src/assets/img/home/model-bg.png and b/react-ui/src/assets/img/home/model-bg.png differ diff --git a/react-ui/src/assets/img/home/model-item-bg-hover.png b/react-ui/src/assets/img/home/model-item-bg-hover.png index 70f48227..e428905c 100644 Binary files a/react-ui/src/assets/img/home/model-item-bg-hover.png and b/react-ui/src/assets/img/home/model-item-bg-hover.png differ diff --git a/react-ui/src/assets/img/home/model-item-bg.png b/react-ui/src/assets/img/home/model-item-bg.png index a1ed78db..61e96565 100644 Binary files a/react-ui/src/assets/img/home/model-item-bg.png and b/react-ui/src/assets/img/home/model-item-bg.png differ diff --git a/react-ui/src/assets/img/home/model-item-hot-hover.png b/react-ui/src/assets/img/home/model-item-hot-hover.png new file mode 100644 index 00000000..4fcc6c27 Binary files /dev/null and b/react-ui/src/assets/img/home/model-item-hot-hover.png differ diff --git a/react-ui/src/assets/img/home/model-item-hot.png b/react-ui/src/assets/img/home/model-item-hot.png new file mode 100644 index 00000000..bda601b1 Binary files /dev/null and b/react-ui/src/assets/img/home/model-item-hot.png differ diff --git a/react-ui/src/assets/img/home/model.png b/react-ui/src/assets/img/home/model.png index a3f0e352..7383692e 100644 Binary files a/react-ui/src/assets/img/home/model.png and b/react-ui/src/assets/img/home/model.png differ diff --git a/react-ui/src/assets/img/home/service.png b/react-ui/src/assets/img/home/service.png index e38216b1..a62fb0be 100644 Binary files a/react-ui/src/assets/img/home/service.png and b/react-ui/src/assets/img/home/service.png differ diff --git a/react-ui/src/assets/img/home/statistics-bg.png b/react-ui/src/assets/img/home/statistics-bg.png index 858c1d11..0386de44 100644 Binary files a/react-ui/src/assets/img/home/statistics-bg.png and b/react-ui/src/assets/img/home/statistics-bg.png differ diff --git a/react-ui/src/assets/img/home/图像 638@2x.png b/react-ui/src/assets/img/home/timestamp.png similarity index 100% rename from react-ui/src/assets/img/home/图像 638@2x.png rename to react-ui/src/assets/img/home/timestamp.png diff --git a/react-ui/src/assets/img/home/底座.png b/react-ui/src/assets/img/home/底座.png deleted file mode 100644 index cf94435b..00000000 Binary files a/react-ui/src/assets/img/home/底座.png and /dev/null differ diff --git a/react-ui/src/assets/img/home/底部(2)@2x.png b/react-ui/src/assets/img/home/底部(2)@2x.png deleted file mode 100644 index e6259fc7..00000000 Binary files a/react-ui/src/assets/img/home/底部(2)@2x.png and /dev/null differ diff --git a/react-ui/src/assets/img/home/更多(1)@2x.png b/react-ui/src/assets/img/home/更多(1)@2x.png deleted file mode 100644 index 53679a87..00000000 Binary files a/react-ui/src/assets/img/home/更多(1)@2x.png and /dev/null differ diff --git a/react-ui/src/assets/img/home/点赞icon(1)@2x.png b/react-ui/src/assets/img/home/点赞icon(1)@2x.png deleted file mode 100644 index 61ce455d..00000000 Binary files a/react-ui/src/assets/img/home/点赞icon(1)@2x.png and /dev/null differ diff --git a/react-ui/src/assets/img/home/点赞icon(10)@2x.png b/react-ui/src/assets/img/home/点赞icon(10)@2x.png deleted file mode 100644 index b0d95597..00000000 Binary files a/react-ui/src/assets/img/home/点赞icon(10)@2x.png and /dev/null differ diff --git a/react-ui/src/assets/img/home/点赞icon(11)@2x.png b/react-ui/src/assets/img/home/点赞icon(11)@2x.png deleted file mode 100644 index 61ce455d..00000000 Binary files a/react-ui/src/assets/img/home/点赞icon(11)@2x.png and /dev/null differ diff --git a/react-ui/src/assets/img/home/点赞icon(2)@2x.png b/react-ui/src/assets/img/home/点赞icon(2)@2x.png deleted file mode 100644 index b0d95597..00000000 Binary files a/react-ui/src/assets/img/home/点赞icon(2)@2x.png and /dev/null differ diff --git a/react-ui/src/assets/img/home/点赞icon(3)@2x.png b/react-ui/src/assets/img/home/点赞icon(3)@2x.png deleted file mode 100644 index 61ce455d..00000000 Binary files a/react-ui/src/assets/img/home/点赞icon(3)@2x.png and /dev/null differ diff --git a/react-ui/src/assets/img/home/点赞icon(4)@2x.png b/react-ui/src/assets/img/home/点赞icon(4)@2x.png deleted file mode 100644 index b0d95597..00000000 Binary files a/react-ui/src/assets/img/home/点赞icon(4)@2x.png and /dev/null differ diff --git a/react-ui/src/assets/img/home/点赞icon(5)@2x.png b/react-ui/src/assets/img/home/点赞icon(5)@2x.png deleted file mode 100644 index db564104..00000000 Binary files a/react-ui/src/assets/img/home/点赞icon(5)@2x.png and /dev/null differ diff --git a/react-ui/src/assets/img/home/点赞icon(6)@2x.png b/react-ui/src/assets/img/home/点赞icon(6)@2x.png deleted file mode 100644 index b0d95597..00000000 Binary files a/react-ui/src/assets/img/home/点赞icon(6)@2x.png and /dev/null differ diff --git a/react-ui/src/assets/img/home/点赞icon(7)@2x.png b/react-ui/src/assets/img/home/点赞icon(7)@2x.png deleted file mode 100644 index 61ce455d..00000000 Binary files a/react-ui/src/assets/img/home/点赞icon(7)@2x.png and /dev/null differ diff --git a/react-ui/src/assets/img/home/点赞icon(8)@2x.png b/react-ui/src/assets/img/home/点赞icon(8)@2x.png deleted file mode 100644 index b0d95597..00000000 Binary files a/react-ui/src/assets/img/home/点赞icon(8)@2x.png and /dev/null differ diff --git a/react-ui/src/assets/img/home/点赞icon(9)@2x.png b/react-ui/src/assets/img/home/点赞icon(9)@2x.png deleted file mode 100644 index 61ce455d..00000000 Binary files a/react-ui/src/assets/img/home/点赞icon(9)@2x.png and /dev/null differ diff --git a/react-ui/src/assets/img/home/点赞icon@2x.png b/react-ui/src/assets/img/home/点赞icon@2x.png deleted file mode 100644 index b0d95597..00000000 Binary files a/react-ui/src/assets/img/home/点赞icon@2x.png and /dev/null differ diff --git a/react-ui/src/assets/img/home/路径 17816@2x (1).png b/react-ui/src/assets/img/home/路径 17816@2x (1).png new file mode 100644 index 00000000..a57d2466 Binary files /dev/null and b/react-ui/src/assets/img/home/路径 17816@2x (1).png differ diff --git a/react-ui/src/assets/img/home/路径 17816@2x.png b/react-ui/src/assets/img/home/路径 17816@2x.png new file mode 100644 index 00000000..f4db7070 Binary files /dev/null and b/react-ui/src/assets/img/home/路径 17816@2x.png differ diff --git a/react-ui/src/assets/img/home/默认头像男@2x.png b/react-ui/src/assets/img/home/默认头像男@2x.png deleted file mode 100644 index 40488e57..00000000 Binary files a/react-ui/src/assets/img/home/默认头像男@2x.png and /dev/null differ diff --git a/react-ui/src/components/CodeSelect/index.tsx b/react-ui/src/components/CodeSelect/index.tsx index 12f52c07..2ea28ccb 100644 --- a/react-ui/src/components/CodeSelect/index.tsx +++ b/react-ui/src/components/CodeSelect/index.tsx @@ -4,7 +4,7 @@ * @Description: 流水线选择代码配置表单 */ -import CodeSelectorModal from '@/components/CodeSelectorModal'; +import CodeSelectorModal, { CodeConfigData } from '@/components/CodeSelectorModal'; import KFIcon from '@/components/KFIcon'; import { openAntdModal } from '@/utils/modal'; import { Button } from 'antd'; @@ -18,19 +18,9 @@ export { type ParameterInputValue, } from '../ParameterInput'; -export type CodeSelectProps = ParameterInputProps; - -// 服务的需要的代码配置数据格式 -export type ServerCodeData = { - id: number; - name: string; - code_path: string; - branch: string; - username: string; - password: string; - ssh_private_key: string; - is_public: boolean; -}; +export interface CodeSelectProps extends ParameterInputProps { + value?: CodeConfigData; +} /** 代码配置选择表单组件 */ function CodeSelect({ @@ -44,50 +34,18 @@ function CodeSelect({ }: CodeSelectProps) { // 选择代码配置 const selectResource = () => { - const codeData = value as ServerCodeData; - const defaultSelected = - value && typeof value === 'object' - ? { - id: codeData.id, - code_repo_name: codeData.name, - git_url: codeData.code_path, - git_branch: codeData.branch, - git_user_name: codeData.username, - git_password: codeData.password, - ssh_key: codeData.ssh_private_key, - is_public: codeData.is_public, - } - : undefined; + const defaultSelected: CodeConfigData | undefined = + value && typeof value === 'object' ? value : undefined; const { close } = openAntdModal(CodeSelectorModal, { defaultSelected: defaultSelected, onOk: (res) => { if (res) { - const { - id, - code_repo_name, - git_url, - git_branch, - git_user_name, - git_password, - ssh_key, - is_public, - } = res; - const jsonObj = { - id, - name: code_repo_name, - code_path: git_url, - branch: git_branch, - username: git_user_name, - password: git_password, - ssh_private_key: ssh_key, - is_public, - }; - const jsonObjStr = JSON.stringify(jsonObj); + const { code_repo_name } = res; onChange?.({ - value: jsonObjStr, + ...res, + value: code_repo_name, showValue: code_repo_name, fromSelect: true, - ...jsonObj, }); } else { onChange?.(undefined); diff --git a/react-ui/src/components/FormInfo/index.tsx b/react-ui/src/components/FormInfo/index.tsx index d33d615a..6416e6e5 100644 --- a/react-ui/src/components/FormInfo/index.tsx +++ b/react-ui/src/components/FormInfo/index.tsx @@ -1,3 +1,4 @@ +import { PipelineGlobalParamType, type PipelineGlobalParam } from '@/types'; import { formatEnum } from '@/utils/format'; import { Typography, type SelectProps } from 'antd'; import classNames from 'classnames'; @@ -16,6 +17,8 @@ type FormInfoProps = { options?: SelectProps['options']; /** 自定义节点 label、value 的字段 */ fieldNames?: SelectProps['fieldNames']; + /** 全局参数 */ + globalParams?: PipelineGlobalParam[] | null; /** 自定义类名 */ className?: string; /** 自定义样式 */ @@ -32,12 +35,29 @@ function FormInfo({ select = false, options, fieldNames, + globalParams, className, style, }: FormInfoProps) { let showValue = value; if (value && typeof value === 'object' && valuePropName) { showValue = value[valuePropName]; + const reg = /^\$\{(.*)\}$/; + if (value.fromSelect && Array.isArray(globalParams) && globalParams.length > 0) { + const match = reg.exec(showValue); + if (match) { + const paramName = match[1]; + const foundParam = globalParams.find((v) => v.param_name === paramName); + if (foundParam) { + showValue = + foundParam.param_type === PipelineGlobalParamType.Boolean // 布尔类型转换 + ? foundParam.param_value + ? 'true' + : 'false' + : foundParam.param_value; + } + } + } } else if (select === true && options) { let _options: SelectProps['options'] = options; if (fieldNames) { diff --git a/react-ui/src/components/ParameterInput/index.tsx b/react-ui/src/components/ParameterInput/index.tsx index 08cf8649..fef6b900 100644 --- a/react-ui/src/components/ParameterInput/index.tsx +++ b/react-ui/src/components/ParameterInput/index.tsx @@ -9,6 +9,7 @@ import { CloseOutlined } from '@ant-design/icons'; import { ConfigProvider, Form, Input, Typography } from 'antd'; import { RuleObject } from 'antd/es/form'; import classNames from 'classnames'; +import { ReactNode } from 'react'; import './index.less'; // 如果值是对象时的类型 @@ -55,6 +56,8 @@ export interface ParameterInputProps { disabled?: boolean; /** 元素 id */ id?: string; + /** 带标签的 input,设置后置标签 */ + addonAfter?: ReactNode; } function ParameterInput({ @@ -75,7 +78,7 @@ function ParameterInput({ const valueObj = typeof value === 'string' ? { value: value, fromSelect: false, showValue: value } : value; if (valueObj && !valueObj.showValue) { - valueObj.showValue = valueObj.value; + valueObj.showValue = typeof valueObj.value === 'string' ? valueObj.value : ''; } const isSelect = valueObj?.fromSelect; const placeholder = valueObj?.placeholder || rest?.placeholder; diff --git a/react-ui/src/components/ParameterSelect/config.tsx b/react-ui/src/components/ParameterSelect/config.tsx index 662a7981..71b1ff6b 100644 --- a/react-ui/src/components/ParameterSelect/config.tsx +++ b/react-ui/src/components/ParameterSelect/config.tsx @@ -1,29 +1,34 @@ -import { filterResourceStandard, resourceFieldNames } from '@/hooks/useComputingResource'; +import { DatasetData, ModelData } from '@/pages/Dataset/config'; import { ServiceData } from '@/pages/ModelDeployment/types'; import { getDatasetList, getModelList } from '@/services/dataset/index.js'; import { getServiceListReq } from '@/services/modelDeployment'; +import type { JCCResourceImage, JCCResourceStandard, JCCResourceType } from '@/state/jcdResource'; +import { filterResourceStandard, resourceFieldNames } from '@/state/systemResource'; import { type SelectProps } from 'antd'; -import { pick } from 'lodash'; - -// id 从 number 转换为 string -const convertId = (item: any) => ({ - ...item, - id: JSON.stringify({ - id: `${item.id}`, - name: item.name, - identifier: item.identifier, - owner: item.owner, - }), -}); export type SelectPropsConfig = { - getOptions: () => Promise; // 获取下拉数据 + getOptions?: () => Promise; // 获取下拉数据 fieldNames?: SelectProps['fieldNames']; // 下拉数据字段 optionFilterProp?: SelectProps['optionFilterProp']; // 过滤字段名 filterOption?: SelectProps['filterOption']; // 过滤函数 + isObjectValue: boolean; // value 是对象 + getValue?: (value: any) => string | number; // 对象类型时,获取其值 + getLabel?: (value: any) => string; // 对象类型时,获取其 label }; -export const paramSelectConfig: Record = { +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 = { dataset: { getOptions: async () => { const res = await getDatasetList({ @@ -31,13 +36,16 @@ export const paramSelectConfig: Record = { size: 1000, is_public: false, }); - return res?.data?.content?.map(convertId) ?? []; + return res?.data?.content ?? []; }, - fieldNames: { - label: 'name', - value: 'id', + optionFilterProp: 'label', + getValue: (value: DatasetData) => { + return value.id; + }, + getLabel: (value: DatasetData) => { + return value.name; }, - optionFilterProp: 'name', + isObjectValue: true, }, model: { getOptions: async () => { @@ -46,13 +54,16 @@ export const paramSelectConfig: Record = { size: 1000, is_public: false, }); - return res?.data?.content?.map(convertId) ?? []; + return res?.data?.content ?? []; + }, + optionFilterProp: 'label', + getValue: (value: ModelData) => { + return value.id; }, - fieldNames: { - label: 'name', - value: 'id', + getLabel: (value: ModelData) => { + return value.name; }, - optionFilterProp: 'name', + isObjectValue: true, }, service: { getOptions: async () => { @@ -60,25 +71,58 @@ export const paramSelectConfig: Record = { page: 0, size: 1000, }); - return ( - res?.data?.content?.map((item: ServiceData) => ({ - label: item.service_name, - value: JSON.stringify(pick(item, ['id', 'service_name'])), - })) ?? [] - ); - }, - fieldNames: { - label: 'label', - value: 'value', + return res?.data?.content ?? []; }, optionFilterProp: 'label', + getValue: (value: ServiceData) => { + return value.id; + }, + getLabel: (value: ServiceData) => { + return value.service_name; + }, + isObjectValue: true, }, resource: { - getOptions: async () => { - // 不需要这个函数 - return []; - }, fieldNames: resourceFieldNames, filterOption: filterResourceStandard as SelectProps['filterOption'], + isObjectValue: false, + }, + 'remote-resource-type': { + optionFilterProp: 'label', + isObjectValue: false, + getValue: (value: JCCResourceType) => { + return value.value; + }, + getLabel: (value: JCCResourceType) => { + return value.label; + }, + }, + 'remote-image': { + optionFilterProp: 'label', + getValue: (value: JCCResourceImage) => { + return value.imageID; + }, + getLabel: (value: JCCResourceImage) => { + return value.name; + }, + isObjectValue: true, + }, + 'remote-resource': { + optionFilterProp: 'label', + getValue: (value: JCCResourceStandard) => { + return value.id; + }, + getLabel: (value: JCCResourceStandard) => { + const cpu = value.baseResourceSpecs.find((v) => v.type === 'CPU'); + const ram = value.baseResourceSpecs.find((v) => v.type === 'MEMORY' && v.name === 'RAM'); + const vram = value.baseResourceSpecs.find((v) => v.type === 'MEMORY' && v.name === 'VRAM'); + const cpuText = cpu ? `CPU:${cpu.availableValue}, ` : ''; + const ramText = ram ? `内存: ${ram.availableValue}${ram.availableUnit?.toUpperCase()}` : ''; + const vramText = vram + ? `(显存${vram.availableValue}${vram.availableUnit?.toUpperCase()})` + : ''; + return `${value.type}: ${value.availableCount}*${value.name}${vramText}, ${cpuText}${ramText}`; + }, + isObjectValue: true, }, }; diff --git a/react-ui/src/components/ParameterSelect/index.tsx b/react-ui/src/components/ParameterSelect/index.tsx index c9a78069..bb23bc06 100644 --- a/react-ui/src/components/ParameterSelect/index.tsx +++ b/react-ui/src/components/ParameterSelect/index.tsx @@ -4,19 +4,25 @@ * @Description: 参数下拉选择组件,支持资源规格、数据集、模型、服务 */ -import { useComputingResource } from '@/hooks/useComputingResource'; +import jccResourceState, { getResourceTypes } from '@/state/jcdResource'; +import systemResourceState, { getSystemResources } from '@/state/systemResource'; import { to } from '@/utils/promise'; +import { useSnapshot } from '@umijs/max'; import { Select, type SelectProps } from 'antd'; -import { useEffect, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import FormInfo from '../FormInfo'; -import { paramSelectConfig } from './config'; +import { paramSelectConfig, type ParameterSelectDataType } from './config'; + +export { ParameterSelectTypeList, type ParameterSelectDataType } from './config'; export type ParameterSelectObject = { value: any; [key: string]: any; }; -export type ParameterSelectDataType = 'dataset' | 'model' | 'service' | 'resource'; +type SelectOptions = SelectProps['options']; + +const identityFunc = (value: any) => value; export interface ParameterSelectProps extends SelectProps { /** 类型 */ @@ -25,8 +31,6 @@ export interface ParameterSelectProps extends SelectProps { display?: boolean; /** 值,支持对象,对象必须包含 value */ value?: string | ParameterSelectObject; - /** 用于流水线, 流水线资源规格要求 id 为字符串 */ - isPipeline?: boolean; /** 修改后回调 */ onChange?: (value: string | ParameterSelectObject) => void; } @@ -36,69 +40,127 @@ function ParameterSelect({ dataType, display = false, value, - isPipeline = false, onChange, ...rest }: ParameterSelectProps) { - const [options, setOptions] = useState([]); + const [options, setOptions] = useState([]); const propsConfig = paramSelectConfig[dataType]; - const valueText = typeof value === 'object' && value !== null ? value.value : value; - const [resourceStandardList] = useComputingResource(); - const computingResource = isPipeline - ? resourceStandardList.map((v) => ({ - ...v, - id: String(v.id), - })) - : resourceStandardList; + const { + getLabel = identityFunc, + getValue = identityFunc, + getOptions, + filterOption, + fieldNames, + optionFilterProp, + isObjectValue, + } = propsConfig; + const selectValue = typeof value === 'object' && value !== null ? value.value : value; + // 数据集、模型、服务,对象转换成 json 字符串 + const valueText = + typeof selectValue === 'object' && selectValue !== null ? getValue(selectValue) : selectValue; + const jccResourceSnap = useSnapshot(jccResourceState); + const systemResourceSnap = useSnapshot(systemResourceState); + + const objectOptions = useMemo(() => { + return dataType === 'remote-resource-type' + ? jccResourceSnap.types + : dataType === 'remote-image' + ? jccResourceSnap.images + : dataType === 'remote-resource' + ? jccResourceSnap.resources + : options; + }, [dataType, options, jccResourceSnap.types, jccResourceSnap.images, jccResourceSnap.resources]); + + // 将对象类型转换为 Select Options + const converObjectToOptions = useCallback( + (v: any) => { + return { + label: getLabel(v), + value: getValue(v), + }; + }, + [getLabel, getValue], + ); + + // 数据集、模型、服务获取数据后,进行转换 + const objectSelectOptions = useMemo(() => { + return objectOptions?.map(converObjectToOptions); + }, [converObjectToOptions, objectOptions]); + + // 快速得到选中的对象 + const valueMap = useMemo(() => { + const map = new Map(); + objectOptions?.forEach((v) => { + map.set(getValue(v), v); + }); + + return map; + }, [objectOptions, getValue]); useEffect(() => { // 获取下拉数据 const getSelectOptions = async () => { - if (!propsConfig) { - return; - } - const getOptions = propsConfig.getOptions; - const [res] = await to(getOptions()); - if (res) { - setOptions(res); + if (getOptions) { + const [res] = await to(getOptions()); + if (res) { + setOptions(res); + } + } else if (dataType === 'remote-resource-type') { + if (jccResourceSnap.types.length === 0) { + getResourceTypes(); + } + } else if (dataType === 'resource') { + getSystemResources(); } }; - getSelectOptions(); - }, [propsConfig]); + }, [getOptions, dataType, getResourceTypes, jccResourceSnap.types]); - const selectOptions = dataType === 'resource' ? computingResource : options; + const selectOptions = ( + dataType === 'resource' ? systemResourceSnap.resources : objectSelectOptions + ) as SelectOptions; const handleChange = (text: string) => { - if (typeof value === 'object' && value !== null) { - onChange?.({ - ...value, - value: text, - }); + // 数据集、模型、服务,转换成对象 + if (isObjectValue) { + // 设置为 null 是因为 ant g6 bug + // 如果值为 undefined 时, graph.changeData(data) 会保留前面的值 + const selectValue = text ? valueMap.get(text) : null; + if (typeof value === 'object' && value !== null) { + onChange?.({ + ...value, + value: selectValue, + }); + } else { + onChange?.(selectValue); + } } else { - onChange?.(text); + const selectValue = text ? text : ''; + if (typeof value === 'object' && value !== null) { + onChange?.({ + ...value, + value: selectValue, + }); + } else { + onChange?.(selectValue); + } } }; // 只用于展示,FormInfo 组件带有 Tooltip if (display) { return ( - + ); } return ( + + { + if (value === 'master') { + return Promise.reject(`${name}版本不能为 master`); + } else if (value === 'origin') { + return Promise.reject(`${name}版本不能为 origin`); + } + return Promise.resolve(); + }, + }, + ]} + > + + + + + + + + ); +} + +export default EditVersionModal; diff --git a/react-ui/src/pages/Dataset/components/ResourceInfo/index.tsx b/react-ui/src/pages/Dataset/components/ResourceInfo/index.tsx index 703e5cd5..544d040a 100644 --- a/react-ui/src/pages/Dataset/components/ResourceInfo/index.tsx +++ b/react-ui/src/pages/Dataset/components/ResourceInfo/index.tsx @@ -24,6 +24,7 @@ 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'; @@ -137,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, { diff --git a/react-ui/src/pages/Dataset/config.tsx b/react-ui/src/pages/Dataset/config.tsx index 0369bc8f..2f43bda5 100644 --- a/react-ui/src/pages/Dataset/config.tsx +++ b/react-ui/src/pages/Dataset/config.tsx @@ -9,6 +9,8 @@ import { deleteDatasetVersion, deleteModel, deleteModelVersion, + editDatasetVersion, + editModelVersion, getDatasetInfo, getDatasetList, getDatasetNextVersionReq, @@ -38,6 +40,7 @@ type ResourceTypeInfo = { getVersions: (params: any) => Promise; // 获取版本列表 deleteRecord: (params: any) => Promise; // 删除 addVersion: (params: any) => Promise; // 新增版本 + editVersion: (params: any) => Promise; // 编辑版本 deleteVersion: (params: any) => Promise; // 删除版本 getInfo: (params: any) => Promise; // 获取详情 compareVersion: (params: any) => Promise; // 版本对比 @@ -68,6 +71,7 @@ export const resourceConfig: Record = { getVersions: getDatasetVersionList, deleteRecord: deleteDataset, addVersion: addDatasetVersion, + editVersion: editDatasetVersion, deleteVersion: deleteDatasetVersion, getInfo: getDatasetInfo, compareVersion: compareDatasetVersion, @@ -107,6 +111,7 @@ export const resourceConfig: Record = { getVersions: getModelVersionList, deleteRecord: deleteModel, addVersion: addModelVersion, + editVersion: editModelVersion, deleteVersion: deleteModelVersion, getInfo: getModelInfo, compareVersion: compareModelVersion, diff --git a/react-ui/src/pages/DevelopmentEnvironment/List/index.tsx b/react-ui/src/pages/DevelopmentEnvironment/List/index.tsx index 27944aac..5395d44e 100644 --- a/react-ui/src/pages/DevelopmentEnvironment/List/index.tsx +++ b/react-ui/src/pages/DevelopmentEnvironment/List/index.tsx @@ -4,10 +4,11 @@ * @Description: 开发环境列表 */ +import { CodeConfigData } from '@/components/CodeSelectorModal'; import KFIcon from '@/components/KFIcon'; import { DevEditorStatus } from '@/enums'; import { useCacheState } from '@/hooks/useCacheState'; -import { useComputingResource } from '@/hooks/useComputingResource'; +import { useSystemResource } from '@/hooks/useComputingResource'; import { DatasetData, ModelData } from '@/pages/Dataset/config'; import { deleteEditorReq, @@ -17,12 +18,7 @@ import { } from '@/services/developmentEnvironment'; import themes from '@/styles/theme.less'; import { parseJsonText } from '@/utils'; -import { - formatCodeConfig, - formatDataset, - formatModel, - type SelectedCodeConfig, -} from '@/utils/format'; +import { formatCodeConfig, formatDataset, formatModel } from '@/utils/format'; import { openAntdModal } from '@/utils/modal'; import { to } from '@/utils/promise'; import SessionStorage from '@/utils/sessionStorage'; @@ -55,7 +51,7 @@ export type EditorData = { dataset?: string | DatasetData; model?: string | ModelData; image?: string; - code_config?: string | SelectedCodeConfig; + code_config?: string | CodeConfigData; }; function EditorList() { @@ -70,7 +66,7 @@ function EditorList() { pageSize: 10, }, ); - const getResourceDescription = useComputingResource()[1]; + const getResourceDescription = useSystemResource(); // 获取编辑器列表 const getEditorList = useCallback(async () => { @@ -211,7 +207,7 @@ function EditorList() { const gotoCodeConfig = (record: EditorData, e: React.MouseEvent) => { e.stopPropagation(); - const codeConfig = record.code_config as SelectedCodeConfig; + const codeConfig = record.code_config as CodeConfigData; const url = formatCodeConfig(codeConfig)?.url; if (url) { window.open(url, '_blank'); diff --git a/react-ui/src/pages/Experiment/Info/index.jsx b/react-ui/src/pages/Experiment/Info/index.jsx index 56f7e979..296dbfe3 100644 --- a/react-ui/src/pages/Experiment/Info/index.jsx +++ b/react-ui/src/pages/Experiment/Info/index.jsx @@ -95,16 +95,13 @@ function ExperimentText() { return; } - const workflow = parseJsonText(dag); + const workflow = dag; const experimentStatusObjs = parseJsonText(nodes_status); if (!workflow || !workflow.nodes) { return; } workflow.nodes.forEach((item) => { - item.in_parameters = parseJsonText(item.in_parameters); - item.out_parameters = parseJsonText(item.out_parameters); - item.control_strategy = parseJsonText(item.control_strategy); item.imgName = item.img.slice(0, item.img.length - 4); }); workflowRef.current = workflow; @@ -140,8 +137,11 @@ function ExperimentText() { } else if (status === ExperimentStatus.Running) { // 如果状态是 Running,打开第一个 Running 或者 pending 的节点,如果没有,则打开第一个节点 const node = - workflow.nodes.find((item) => item.experimentStatus === ExperimentStatus.Running || item.experimentStatus === ExperimentStatus.Pending) ?? - workflow.nodes[0]; + workflow.nodes.find( + (item) => + item.experimentStatus === ExperimentStatus.Running || + item.experimentStatus === ExperimentStatus.Pending, + ) ?? workflow.nodes[0]; if (node) { setExperimentNodeData(node); openPropsDrawer(); @@ -567,12 +567,13 @@ function ExperimentText() { instanceNodeStatus={experimentNodeData.experimentStatus} instanceNodeStartTime={experimentNodeData.experimentStartTime} instanceNodeEndTime={experimentNodeData.experimentEndTime} + globalParams={experimentIns?.global_param} > ) : null} ); diff --git a/react-ui/src/pages/Experiment/components/AddExperimentModal/index.less b/react-ui/src/pages/Experiment/components/AddExperimentModal/index.less index 2470e868..8cc0f830 100644 --- a/react-ui/src/pages/Experiment/components/AddExperimentModal/index.less +++ b/react-ui/src/pages/Experiment/components/AddExperimentModal/index.less @@ -6,4 +6,10 @@ border: 1px solid #e6e6e6; border-radius: 6px; } + + :global { + .ant-form-item-row { + align-items: center; + } + } } diff --git a/react-ui/src/pages/Experiment/components/AddExperimentModal/index.tsx b/react-ui/src/pages/Experiment/components/AddExperimentModal/index.tsx index 2a15a8e7..4d576819 100644 --- a/react-ui/src/pages/Experiment/components/AddExperimentModal/index.tsx +++ b/react-ui/src/pages/Experiment/components/AddExperimentModal/index.tsx @@ -1,7 +1,7 @@ import createExperimentIcon from '@/assets/img/create-experiment.png'; import editExperimentIcon from '@/assets/img/edit-experiment.png'; import KFModal from '@/components/KFModal'; -import { type PipelineGlobalParam } from '@/types'; +import { PipelineGlobalParamType, type PipelineGlobalParam } from '@/types'; import { to } from '@/utils/promise'; import { Button, Form, Input, Radio, Select, Typography, type FormRule } from 'antd'; import { useState } from 'react'; @@ -32,7 +32,7 @@ interface Workflow { // 根据参数设置输入组件 export const getParamComponent = (paramType: number): JSX.Element => { // 防止后台返回不是 number 类型 - if (Number(paramType) === 3) { + if (Number(paramType) === PipelineGlobalParamType.Boolean) { return ( @@ -50,7 +50,7 @@ export const getParamComponent = (paramType: number): JSX.Element => { export const getParamRules = (paramType: number, required: boolean = false): FormRule[] => { const rules = []; // 防止后台返回不是 number 类型 - if (Number(paramType) === 2) { + if (Number(paramType) === PipelineGlobalParamType.Number) { rules.push({ pattern: /^-?((0(\.0*[1-9]\d*)?)|([1-9]\d*(\.\d+)?))$/, message: '整型必须是数字', @@ -64,10 +64,10 @@ export const getParamRules = (paramType: number, required: boolean = false): For // 根据参数设置 label export const getParamLabel = (param: PipelineGlobalParam): React.ReactNode => { - const paramTypes: Readonly> = { - 1: '字符串', - 2: '整型', - 3: '布尔类型', + const paramTypes: Readonly> = { + [PipelineGlobalParamType.String]: '字符串', + [PipelineGlobalParamType.Number]: '整型', + [PipelineGlobalParamType.Boolean]: '布尔类型', }; const label = param.param_name + `(${paramTypes[param.param_type]})`; return {label}; diff --git a/react-ui/src/pages/Experiment/components/ExperimentDrawer/index.tsx b/react-ui/src/pages/Experiment/components/ExperimentDrawer/index.tsx index 11d0ff2e..7a52060a 100644 --- a/react-ui/src/pages/Experiment/components/ExperimentDrawer/index.tsx +++ b/react-ui/src/pages/Experiment/components/ExperimentDrawer/index.tsx @@ -1,7 +1,7 @@ import RunDuration from '@/components/RunDuration'; import { ExperimentStatus } from '@/enums'; import { experimentStatusInfo } from '@/pages/Experiment/status'; -import { PipelineNodeModelSerialize } from '@/types'; +import { PipelineNodeModelSerialize, type PipelineGlobalParam } from '@/types'; import { formatDate } from '@/utils/date'; import { CloseOutlined, DatabaseOutlined, ProfileOutlined } from '@ant-design/icons'; import { Drawer, Tabs, Typography } from 'antd'; @@ -25,6 +25,7 @@ type ExperimentDrawerProps = { instanceNodeStatus?: ExperimentStatus; // 实例节点状态 instanceNodeStartTime?: string; // 开始时间 instanceNodeEndTime?: string; // 在定时刷新实验实例状态中,会经常变化 + globalParams?: PipelineGlobalParam[] | null; // 全局参数 }; const ExperimentDrawer = ({ @@ -41,6 +42,7 @@ const ExperimentDrawer = ({ instanceNodeStatus, instanceNodeStartTime, instanceNodeEndTime, + globalParams, }: ExperimentDrawerProps) => { // 如果性能有问题,可以进一步拆解 const items = useMemo( @@ -66,7 +68,7 @@ const ExperimentDrawer = ({ key: '2', label: '配置参数', icon: , - children: , + children: , }, { key: '3', @@ -94,6 +96,7 @@ const ExperimentDrawer = ({ experimentName, experimentId, pipelineId, + globalParams, ], ); diff --git a/react-ui/src/pages/Experiment/components/ExperimentParameter/index.less b/react-ui/src/pages/Experiment/components/ExperimentParameter/index.less index c5d9824e..339ef362 100644 --- a/react-ui/src/pages/Experiment/components/ExperimentParameter/index.less +++ b/react-ui/src/pages/Experiment/components/ExperimentParameter/index.less @@ -15,4 +15,24 @@ font-size: @font-size; background: #f8fbff; } + + &__form-list { + :global { + .ant-row { + padding: 0 !important; + } + } + + &:last-child { + :global { + .ant-form-item { + margin-bottom: 0 !important; + } + } + } + } + + &__list-empty { + color: @text-color-tertiary; + } } diff --git a/react-ui/src/pages/Experiment/components/ExperimentParameter/index.tsx b/react-ui/src/pages/Experiment/components/ExperimentParameter/index.tsx index 2bd39c29..80c6d25d 100644 --- a/react-ui/src/pages/Experiment/components/ExperimentParameter/index.tsx +++ b/react-ui/src/pages/Experiment/components/ExperimentParameter/index.tsx @@ -1,25 +1,102 @@ import FormInfo from '@/components/FormInfo'; -import ParameterSelect from '@/components/ParameterSelect'; +import ParameterSelect, { + type ParameterSelectDataType, + ParameterSelectTypeList, +} from '@/components/ParameterSelect'; import SubAreaTitle from '@/components/SubAreaTitle'; -import { PipelineNodeModelSerialize } from '@/types'; -import { Form } from 'antd'; +import { ComponentType } from '@/enums'; +import { setCurrentType } from '@/state/jcdResource'; +import type { + PipelineGlobalParam, + PipelineNodeModelParameter, + PipelineNodeModelSerialize, +} from '@/types'; +import { Flex, Form } from 'antd'; import styles from './index.less'; type ExperimentParameterProps = { nodeData: PipelineNodeModelSerialize; + globalParams?: PipelineGlobalParam[] | null; // 全局参数 }; -function ExperimentParameter({ nodeData }: 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 }, + parentName: string, + ) => { + return ( + + {item.value.type === ComponentType.Map && ( + + {(fields) => ( + <> + {fields.length > 0 ? ( + fields.map(({ key, name, ...restField }) => ( + + + + + = + + + + + )) + ) : ( +
+ )} + + )} +
+ )} + {item.value.type === ComponentType.Select && + (ParameterSelectTypeList.includes(item.value.item_type as ParameterSelectDataType) ? ( + + ) : null)} + {item.value.type !== ComponentType.Map && item.value.type !== ComponentType.Select && ( + + )} +
+ ); + }; + + // 基本参数 + const basicParametersList = Object.entries(nodeData.task_info ?? {}) + .map(([key, value]) => ({ + key, + value, + })) + .filter((v) => v.value.visible === true); + // 控制策略 - // const controlStrategyList = Object.entries(nodeData.control_strategy ?? {}).map( - // ([key, value]) => ({ key, value }), - // ); - const nodeId = nodeData.id; - const hasTaskInfo = - nodeId && - !nodeId.startsWith('git-clone') && - !nodeId.startsWith('dataset-export') && - !nodeId.startsWith('model-export'); + const controlStrategyList = Object.entries(nodeData.control_strategy ?? {}) + .map(([key, value]) => ({ key, value })) + .filter((v) => v.value.visible === true); // 输入参数 const inParametersList = Object.entries(nodeData.in_parameters ?? {}).map(([key, value]) => ({ @@ -80,96 +157,56 @@ function ExperimentParameter({ nodeData }: ExperimentParameterProps) { > - {hasTaskInfo && ( + + {basicParametersList.length + controlStrategyList.length > 0 && ( +
+ +
+ )} + + {/* 基本参数 */} + {basicParametersList.map((item) => getFormComponent(item, 'task_info'))} + + {/* 控制参数 */} + {controlStrategyList.map((item) => getFormComponent(item, 'control_strategy'))} + + {/* 输入参数 */} + {inParametersList.length > 0 && ( <>
- - - - - - + {inParametersList.map((item) => getFormComponent(item, 'in_parameters'))} + + )} - - - - - - - {/* - - */} - - - - {/* {controlStrategyList.map((item) => ( - - - - ))} */} + {/* 输出参数 */} + {outParametersList.length > 0 && ( + <> +
+ +
+ {outParametersList.map((item) => ( + + + + ))} )} -
- -
- {inParametersList.map((item) => ( - - {item.value.type === 'select' ? ( - ['dataset', 'model', 'service', 'resource'].includes(item.value.item_type) ? ( - - ) : null - ) : ( - - )} - - ))} -
- -
- {outParametersList.map((item) => ( - - - - ))} ); } diff --git a/react-ui/src/pages/Experiment/components/ViewParamsModal/index.less b/react-ui/src/pages/Experiment/components/ViewParamsModal/index.less index 06c5a4f0..819ca7bf 100644 --- a/react-ui/src/pages/Experiment/components/ViewParamsModal/index.less +++ b/react-ui/src/pages/Experiment/components/ViewParamsModal/index.less @@ -4,6 +4,12 @@ overflow-y: auto; border: 1px solid #e6e6e6; border-radius: 8px; + + :global { + .ant-form-item-row { + align-items: center; + } + } } .params-empty { :global { diff --git a/react-ui/src/pages/Experiment/components/ViewParamsModal/index.tsx b/react-ui/src/pages/Experiment/components/ViewParamsModal/index.tsx index 3ad671a4..2000d556 100644 --- a/react-ui/src/pages/Experiment/components/ViewParamsModal/index.tsx +++ b/react-ui/src/pages/Experiment/components/ViewParamsModal/index.tsx @@ -14,10 +14,10 @@ import styles from './index.less'; type ParamsModalProps = { open: boolean; onCancel: () => void; - globalParam?: PipelineGlobalParam[] | null; + globalParams?: PipelineGlobalParam[] | null; }; -function ParamsModal({ open, onCancel, globalParam = [] }: ParamsModalProps) { +function ParamsModal({ open, onCancel, globalParams = [] }: ParamsModalProps) { return ( - {Array.isArray(globalParam) && globalParam.length > 0 ? ( + {Array.isArray(globalParams) && globalParams.length > 0 ? (
@@ -45,9 +45,9 @@ function ParamsModal({ open, onCancel, globalParam = [] }: ParamsModalProps) { {...restField} key={key} name={[name, 'param_value']} - label={getParamLabel(globalParam[name])} + label={getParamLabel(globalParams[name])} > - {getParamComponent(globalParam[name]['param_type'])} + {getParamComponent(globalParams[name]['param_type'])} )) } diff --git a/react-ui/src/pages/Experiment/index.jsx b/react-ui/src/pages/Experiment/index.jsx index 219e7c0d..d0f4b955 100644 --- a/react-ui/src/pages/Experiment/index.jsx +++ b/react-ui/src/pages/Experiment/index.jsx @@ -226,14 +226,14 @@ function Experiment() { if (type === ExperimentCompleted) { const { experimentId, experimentInsId, status, finishTime } = payload; const currentIns = experimentInsList.find((v) => v.id === experimentInsId); - console.log( - '实验实例状态变化', - currentIns?.status, - status, - experimentId, - experimentInsId, - finishTime, - ); + // console.log( + // '实验实例状态变化', + // currentIns?.status, + // status, + // experimentId, + // experimentInsId, + // finishTime, + // ); if ( !currentIns || diff --git a/react-ui/src/pages/Home/components/BlockTitle/index.less b/react-ui/src/pages/Home/components/BlockTitle/index.less index 4bf9347c..8f0a72ed 100644 --- a/react-ui/src/pages/Home/components/BlockTitle/index.less +++ b/react-ui/src/pages/Home/components/BlockTitle/index.less @@ -5,7 +5,7 @@ width: 100%; &__title { - color: #020814; + color: @home-text-color; font-weight: 500; font-size: 2.25rem; text-align: center; diff --git a/react-ui/src/pages/Home/components/CodeConfig/index.less b/react-ui/src/pages/Home/components/CodeConfig/index.less index 73435c0b..89ee9468 100644 --- a/react-ui/src/pages/Home/components/CodeConfig/index.less +++ b/react-ui/src/pages/Home/components/CodeConfig/index.less @@ -1,68 +1,151 @@ -.dataset { +.code { position: relative; display: flex; flex-direction: column; align-items: center; - padding: 0 16.25rem 3.125rem; - .backgroundFullImage(url(@/assets/img/home/model-bg.png)); + padding: 4.375rem @home-padding-x 9.375rem; + .backgroundFullImage(url(@/assets/img/home/code-bg.png)); &__item { position: relative; - width: 27.875rem; - padding: 1.875rem 1.25rem; - color: #8284a4; + width: calc((100% - 2 * 1.25rem) / 3); + padding: 1.625rem; + color: @home-text-color-tertiary; font-size: 0.8125rem; - background-color: transparent; - border-radius: 11px; cursor: pointer; - .backgroundFullImage(url(@/assets/img/home/dataset-item-bg.png)); - - &:hover { - .backgroundFullImage(url(@/assets/img/home/dataset-item-bg-hover.png)); - } - - &__user-avatar { - flex: none; - width: 2.75rem; - height: 2.75rem; - margin-right: 1rem; - } &__title { flex: 1; - color: #020814; + color: @home-text-color; font-size: 1rem; .singleLine(); } - &:hover &__title { - color: @primary-color; - } - &__arrow { display: none; - flex: none; - width: 1.5625rem; - margin-left: 0.75rem; + 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: #8284a4; + 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; } } diff --git a/react-ui/src/pages/Home/components/CodeConfig/index.tsx b/react-ui/src/pages/Home/components/CodeConfig/index.tsx index 9b755683..cf0b6ea2 100644 --- a/react-ui/src/pages/Home/components/CodeConfig/index.tsx +++ b/react-ui/src/pages/Home/components/CodeConfig/index.tsx @@ -3,14 +3,14 @@ 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 { gotoPageIfLogin } from '@/utils/ui'; 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([]); useEffect(() => { @@ -18,57 +18,74 @@ function CodeConfig() { const [res] = await to(getPublicCodeConfigsReq()); if (res && res.data) { const { content = [] } = res.data; - setCodeConfigs(content.slice(0, 6)); + setCodeConfigs(content.slice(0, 9)); } }; getPublicCodeConfigs(); }, []); + const createItem = (item: CodeConfigData, className: string) => { + return ( +
{ + const url = getGitUrl(item.git_url, item.git_branch); + window.open(url, '_blank'); + }} + > + +
{item.code_repo_name}
+ +
+
{item.git_url}
+ + +
{item.create_by}
+ + +
{formatDate(item.create_time)}
+
+
+
+ ); + }; + return ( -
+
navigate('/dataset/codeConfig')} + style={{ marginBottom: '5.25rem' }} + onClick={() => gotoPageIfLogin('/dataset/codeConfig')} > - - {codeCofigs.map((item) => { - return ( - { - const url = getGitUrl(item.git_url, item.git_branch); - window.open(url, '_blank'); - }} - > - -
- -
{item.code_repo_name}
- -
-
{item.git_url}
- -
{item.create_by}
- -
{formatDate(item.create_time)}
-
-
-
-
- ); - })} + + {codeCofigs.slice(0, 3).map((item) => createItem(item, styles['code__item--first']))} + + + {codeCofigs.slice(3).map((item) => createItem(item, styles['code__item--second']))} +
+
); diff --git a/react-ui/src/pages/Home/components/Dataset/index.less b/react-ui/src/pages/Home/components/Dataset/index.less index 1467aa50..686dfa96 100644 --- a/react-ui/src/pages/Home/components/Dataset/index.less +++ b/react-ui/src/pages/Home/components/Dataset/index.less @@ -3,34 +3,33 @@ display: flex; flex-direction: column; align-items: center; - padding: 0 16.25rem 9.125rem; - .backgroundFullImage(url(@/assets/img/home/model-bg.png)); + padding: 0 @home-padding-x 11.125rem; &__item { position: relative; - width: 27.875rem; - padding: 1.875rem 1.25rem; - color: #8284a4; + width: calc((100% - 3 * 1.25rem) / 4); + padding: 1.625rem; + color: @home-text-color-tertiary; font-size: 0.8125rem; background-color: transparent; - border-radius: 11px; + 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)); - &:hover { - .backgroundFullImage(url(@/assets/img/home/dataset-item-bg-hover.png)); + &:nth-child(1), + &:nth-child(2), + &:nth-child(3) { + width: calc((100% - 2 * 1.25rem) / 3); } - - &__user-avatar { - flex: none; - width: 2.75rem; - height: 2.75rem; - margin-right: 1rem; + &:hover { + outline: 2px solid @primary-color; + box-shadow: 0px 0px 0.75rem rgba(33, 73, 212, 0.15); } &__title { flex: 1; - color: #020814; + color: @home-text-color; font-size: 1rem; .singleLine(); } @@ -39,30 +38,55 @@ color: @primary-color; } - &__arrow { - display: none; - flex: none; - width: 1.5625rem; - margin-left: 0.75rem; - } - - &:hover &__arrow { - display: block; + &__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: #8284a4; + background-color: @home-divider-color; + } + + &__timestamp-icon { + flex: none; + width: 1rem; + height: 1rem; + margin-right: 0.375rem; + } + + &__timestamp { + flex: none; } } } diff --git a/react-ui/src/pages/Home/components/Dataset/index.tsx b/react-ui/src/pages/Home/components/Dataset/index.tsx index fedb4049..9bdd4852 100644 --- a/react-ui/src/pages/Home/components/Dataset/index.tsx +++ b/react-ui/src/pages/Home/components/Dataset/index.tsx @@ -1,14 +1,13 @@ import { DatasetData } from '@/pages/Dataset/config'; import { getPublicDatasetsReq } from '@/services/home'; import { to } from '@/utils/promise'; -import { useNavigate } from '@umijs/max'; +import { gotoPageIfLogin } from '@/utils/ui'; 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([]); useEffect(() => { @@ -16,7 +15,7 @@ function DatasetBlock() { const [res] = await to(getPublicDatasetsReq()); if (res && res.data) { const { content = [] } = res.data; - setDatasetData(content.slice(0, 6)); + setDatasetData(content.slice(0, 7)); } }; @@ -27,45 +26,43 @@ function DatasetBlock() {
navigate('/dataset/dataset')} + style={{ marginBottom: '3.875rem' }} + onClick={() => gotoPageIfLogin('/dataset/dataset')} > - - {datasetData.map((item) => { + + {datasetData.map((item, index) => { return ( - { - navigate( + gotoPageIfLogin( `/dataset/dataset/info/${item.id}?name=${item.name}&owner=${item.owner}&identifier=${item.identifier}&is_public=${item.is_public}`, ); }} > - -
- -
{item.name}
- -
-
{item.description}
- -
{item.create_by}
- -
{item.time_ago}更新
-
-
-
-
+ +
{item.name}
+ {index < 3 &&
HOT
} +
+
{item.description}
+ + +
{item.create_by}
+ + +
{item.time_ago}更新
+
+
); })} diff --git a/react-ui/src/pages/Home/components/Footer/index.less b/react-ui/src/pages/Home/components/Footer/index.less new file mode 100644 index 00000000..efb201f8 --- /dev/null +++ b/react-ui/src/pages/Home/components/Footer/index.less @@ -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; + } +} diff --git a/react-ui/src/pages/Home/components/Footer/index.tsx b/react-ui/src/pages/Home/components/Footer/index.tsx new file mode 100644 index 00000000..367e253d --- /dev/null +++ b/react-ui/src/pages/Home/components/Footer/index.tsx @@ -0,0 +1,33 @@ +import { Divider, Flex } from 'antd'; +import styles from './index.less'; + +function Footer() { + return ( +
+ + + + 智能材料科研平台 + +
+
关于我们
+
+ 我是关于我们的文案简介内容我是关于我们的文案简介内容我是关于我们的文案简介内容我是关于我们的文案简介内容我是关于我们的文案简介内容我是关于我们的文案简介内容我是关于我们的文案简介内容我是关于我们的文案简介内容 +
+
+
+
联系我们
+
邮箱:xxxx@163.com
+
地址:湖南省长沙市岳麓区中电软件园
+
+
+ +
© 2025 国防科技大学所有
+
+ ); +} + +export default Footer; diff --git a/react-ui/src/pages/Home/components/Intro/index.less b/react-ui/src/pages/Home/components/Intro/index.less index 6efeec56..6adf597c 100644 --- a/react-ui/src/pages/Home/components/Intro/index.less +++ b/react-ui/src/pages/Home/components/Intro/index.less @@ -1,18 +1,34 @@ .intro { - position: relative; - z-index: 10; - display: flex; - flex-direction: column; - align-items: center; - padding-top: 5.625rem; - background-image: url(@/assets/img/home/header-bg.png); + position: fixed; + top: 0; + right: 0; + left: 0; + z-index: 100; + height: @home-info-height; + overflow: hidden; + background-color: transparent; background-repeat: no-repeat; background-position: left top; background-size: 100% 100%; + &__content { + position: relative; + z-index: 10; + display: flex; + flex-direction: column; + align-items: center; + padding: 1.25rem @home-padding-x 0; + // background-image: url(@/assets/img/home/header-bg.png); + background-repeat: no-repeat; + background-position: left top; + background-size: 100% 100%; + } + &__title { + margin-top: 1.25rem; margin-bottom: 1.125rem; color: #ffffff; + font-weight: 400; font-size: 2.375rem; font-family: WenYiHei; } @@ -28,15 +44,15 @@ &__button { margin-bottom: 4.375rem; - padding: 0.625rem 2.375rem; + padding: 0.75rem 2.375rem; color: #ffffff; font-size: 1rem; text-align: center; background: linear-gradient( - 108.54deg, - #5eb4ff 3.72%, - rgba(42, 92, 255, 0.01) 49.49%, - rgba(232, 239, 255, 0.33) 98.01% + 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; diff --git a/react-ui/src/pages/Home/components/Intro/index.tsx b/react-ui/src/pages/Home/components/Intro/index.tsx index 41397bff..527aa2e8 100644 --- a/react-ui/src/pages/Home/components/Intro/index.tsx +++ b/react-ui/src/pages/Home/components/Intro/index.tsx @@ -1,23 +1,81 @@ -// import NavBar from '../NavBar'; +import miniHeaderImage from '@/assets/img/home/header-bg-mini.png'; +import headerImage from '@/assets/img/home/header-bg.png'; +import { convertRemToPx } from '@/utils'; import { useNavigate } from '@umijs/max'; +import { + motion, + useMotionTemplate, + useMotionValueEvent, + useScroll, + useSpring, + useTransform, +} from 'motion/react'; +import { useState } from 'react'; +import NavBar from '../NavBar'; import StatisticsBlock from '../Statistics'; import styles from './index.less'; function IntroBlock() { + const [backgroundImage1, setBackgroundImage1] = useState(undefined); + const [backgroundImage2, setBackgroundImage2] = useState(headerImage); const navigate = useNavigate(); + const { scrollY } = useScroll(); + const springValue = useSpring(scrollY, { + stiffness: 100, + damping: 30, + restDelta: 0.001, + }); + + const initialHeight = convertRemToPx(35); + const minHeight = convertRemToPx(4.7); + const height = useTransform(() => `max(calc(35rem - ${springValue.get()}px), 4.7rem)`); + const left = useTransform(springValue, [0, initialHeight], [0.0, 16.25]); + const leftRem = useMotionTemplate`${left}rem`; + const radius = useTransform(springValue, [0, initialHeight], [0.0, 2.5]); + const radiusRem = useMotionTemplate`${radius}rem`; + const top = useTransform(springValue, [0, initialHeight], [0, 10]); + const paddingX = useTransform(springValue, [0, initialHeight], [16.25, 10]); + const paddingXRem = useMotionTemplate`1.25rem ${paddingX}rem 0`; + useMotionValueEvent(scrollY, 'change', (value) => { + setBackgroundImage1(value > initialHeight - minHeight ? miniHeaderImage : undefined); + setBackgroundImage2(value > initialHeight - minHeight ? undefined : headerImage); + }); + return ( -
- {/* */} -
智能材料科研平台
-
- 智能材料科研平台是用于材料研究和开发的技术平台,它旨在提供实验数据收集、分析和可视化等功能, - 以支持材料工程师、科学家和研究人员在材料设计、性能评估和工艺优化方面的工作。 -
-
navigate('/workspace')}> - 开始使用 -
- -
+ + + +
智能材料科研平台
+
+ 智能材料科研平台是用于材料研究和开发的技术平台,它旨在提供实验数据收集、分析和可视化等功能, + 以支持材料工程师、科学家和研究人员在材料设计、性能评估和工艺优化方面的工作。 +
+
navigate('/workspace')}> + 开始使用 +
+ +
+
); } diff --git a/react-ui/src/pages/Home/components/Mirror/index.less b/react-ui/src/pages/Home/components/Mirror/index.less index 7683af78..a88a2393 100644 --- a/react-ui/src/pages/Home/components/Mirror/index.less +++ b/react-ui/src/pages/Home/components/Mirror/index.less @@ -1,67 +1,110 @@ -.model { +.mirror { position: relative; display: flex; flex-direction: column; align-items: center; - padding: 0 16.25rem 9.125rem; - .backgroundFullImage(url(@/assets/img/home/model-bg.png)); + padding: 0 @home-padding-x 11.125rem; &__item { position: relative; - width: 27.875rem; - padding: 1.875rem 1.25rem; - color: #8284a4; + width: calc((100% - 2 * 1.25rem) / 4); + padding: 1.625rem; + color: @home-text-color-tertiary; font-size: 0.8125rem; background-color: transparent; - border-radius: 11px; - box-shadow: 0rem 0.0625rem 0.75rem rgba(33, 73, 212, 0.09); + border-radius: 1.125rem; + box-shadow: 0px 0.1875rem 0.75rem rgba(41, 50, 225, 0.09); cursor: pointer; - .backgroundFullImage(url(@/assets/img/home/model-item-bg.png)); + .backgroundFullImage(url(@/assets/img/home/dataset-item-bg.png)); - &:hover { - color: white; - .backgroundFullImage(url(@/assets/img/home/model-item-bg-hover.png)); - } - - &:nth-child(3n + 2) { - top: -3.75rem; + &:nth-child(2), + &:nth-child(4) { + width: calc((100% - 2 * 1.25rem) / 2); } - - &__user-avatar { - flex: none; - width: 2.75rem; - height: 2.75rem; - margin-right: 0.875rem; + &:hover { + outline: 2px solid @primary-color; + box-shadow: 0px 0.1875rem 0.75rem rgba(41, 50, 225, 0.09); } &__title { - margin-bottom: 0.625rem; - color: #020814; + flex: 1; + margin-right: 0.5rem; + color: @home-text-color; font-size: 1rem; .singleLine(); } &:hover &__title { - color: white; + 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: #8284a4; + background-color: @home-divider-color; } - &:hover &__user-divider { - background-color: white; + &__timestamp-icon { + flex: none; + width: 1rem; + height: 1rem; + margin-right: 0.375rem; + } + + &__timestamp { + flex: none; } } } diff --git a/react-ui/src/pages/Home/components/Mirror/index.tsx b/react-ui/src/pages/Home/components/Mirror/index.tsx index cb933c55..b7fcc7c2 100644 --- a/react-ui/src/pages/Home/components/Mirror/index.tsx +++ b/react-ui/src/pages/Home/components/Mirror/index.tsx @@ -2,14 +2,13 @@ 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 { gotoPageIfLogin } from '@/utils/ui'; 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([]); useEffect(() => { @@ -25,40 +24,54 @@ function MirrorBlock() { }, []); return ( -
+
navigate('/dataset/mirror')} + style={{ marginBottom: '5.25rem' }} + onClick={() => gotoPageIfLogin('/dataset/mirror')} > - + {mirrorData.map((item) => { return ( - { - navigate(`/dataset/mirror/info/${item.id}`); + gotoPageIfLogin(`/dataset/mirror/info/${item.id}`); }} > - -
-
{item.name}
-
{item.description}
- -
{item.create_by}
- -
{formatDate(item.create_time)}
-
-
-
-
+ + +
{item.name}
+ +
+
{item.description}
+ + + {`版本数:${item.version_count}`} + + +
{item.create_by}
+ + +
+ {formatDate(item.create_time)} +
+
+
+
); })} diff --git a/react-ui/src/pages/Home/components/Model/index.less b/react-ui/src/pages/Home/components/Model/index.less index 2be9cd39..7f3898ef 100644 --- a/react-ui/src/pages/Home/components/Model/index.less +++ b/react-ui/src/pages/Home/components/Model/index.less @@ -3,18 +3,28 @@ display: flex; flex-direction: column; align-items: center; - padding: 5.375rem 16.25rem 9.125rem; + padding: 4.125rem @home-padding-x 14.75rem; .backgroundFullImage(url(@/assets/img/home/model-bg.png)); + &__content { + display: flex; + flex-wrap: wrap; + gap: 1.625rem 1.25rem; + align-items: center; + width: 100%; + } + &__item { position: relative; - width: 27.875rem; + display: flex; + align-items: center; + width: calc((100% - 2 * 1.25rem) / 3); padding: 1.875rem 1.25rem; - color: #8284a4; + color: @home-text-color-tertiary; font-size: 0.8125rem; background-color: transparent; - border-radius: 11px; - box-shadow: 0rem 0.0625rem 0.75rem rgba(33, 73, 212, 0.09); + 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)); @@ -23,8 +33,17 @@ .backgroundFullImage(url(@/assets/img/home/model-item-bg-hover.png)); } - &:nth-child(3n + 2) { - top: -3.75rem; + &__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 { @@ -36,7 +55,7 @@ &__title { margin-bottom: 0.625rem; - color: #020814; + color: @home-text-color; font-size: 1rem; .singleLine(); } @@ -53,15 +72,42 @@ .multiLine(2); } + &__user { + .singleLine(); + } + &__user-divider { + flex: none; height: 0.625rem; margin-right: 0.75rem; margin-left: 0.75rem; - background-color: #8284a4; + 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; + // } } } diff --git a/react-ui/src/pages/Home/components/Model/index.tsx b/react-ui/src/pages/Home/components/Model/index.tsx index 07380dbc..c3b0e621 100644 --- a/react-ui/src/pages/Home/components/Model/index.tsx +++ b/react-ui/src/pages/Home/components/Model/index.tsx @@ -1,14 +1,35 @@ import { ModelData } from '@/pages/Dataset/config'; import { getPublicModelsReq } from '@/services/home'; import { to } from '@/utils/promise'; -import { useNavigate } from '@umijs/max'; +import { gotoPageIfLogin } from '@/utils/ui'; import { Divider, Flex } from 'antd'; +import { motion, type Variants } from 'motion/react'; import { useEffect, useState } from 'react'; import BlockTitle from '../BlockTitle'; import styles from './index.less'; +const modelVariants: Variants = { + offscreen: (index: number) => ({ + y: 0, + opacity: 1, + transition: { + ease: 'linear', + duration: 0.1, + }, + }), + onscreen: { + y: [0, 200, 0], + opacity: [0, 0, 1], + transition: { + ease: 'easeOut', + duration: 0.3, + times: [0, 0, 1], + delay: 0.5, + }, + }, +}; + function ModelBlock() { - const navigate = useNavigate(); const [modelData, setModelData] = useState([]); useEffect(() => { @@ -27,42 +48,51 @@ function ModelBlock() {
navigate('/dataset/model')} + style={{ marginBottom: '5.25rem' }} + onClick={() => gotoPageIfLogin('/dataset/model')} > - - {modelData.map((item) => { +
+ {modelData.map((item, index) => { return ( - { - navigate( + gotoPageIfLogin( `/dataset/model/info/${item.id}?name=${item.name}&owner=${item.owner}&identifier=${item.identifier}&is_public=${item.is_public}`, ); }} > + {index < 3 &&
}
{item.name}
{item.description}
-
{item.create_by}
+
{item.create_by}
-
{item.time_ago}更新
+
{item.time_ago}更新
+ {/* +
电池开发
+
材料研发
+
*/}
-
+ ); })} - +
); } diff --git a/react-ui/src/pages/Home/components/NavBar/index.less b/react-ui/src/pages/Home/components/NavBar/index.less index 18a08c25..6641b46a 100644 --- a/react-ui/src/pages/Home/components/NavBar/index.less +++ b/react-ui/src/pages/Home/components/NavBar/index.less @@ -1,7 +1,52 @@ .nav-bar { display: flex; align-items: center; - padding: 1.25rem 15.625rem; + justify-content: space-between; + width: 100%; + margin-bottom: 4.375rem; + color: white; + font-size: 0.9375rem; - -} \ No newline at end of file + &__app-logo { + width: 1.75rem; + margin-right: 0.625rem; + } + + &__app-name { + margin-right: 6.625rem; + font-size: 1.375rem; + font-family: WenYiHei; + line-height: 2.2rem; + } + + &__menu-item { + margin-right: 3.125rem; + padding: 0.25rem 0.5rem; + border-radius: 0.375rem; + cursor: pointer; + + &:hover { + background-color: rgba(0, 0, 0, 0.06); + } + + &:last-of-type { + margin-right: 0; + } + } + + :global { + .ant-dropdown-trigger { + height: 2.15rem; + + .ant-avatar { + width: 1.875rem; + height: 1.875rem; + margin-right: 0 !important; + } + } + + .ant-dropdown-trigger > .anticon { + display: none; + } + } +} diff --git a/react-ui/src/pages/Home/components/NavBar/index.tsx b/react-ui/src/pages/Home/components/NavBar/index.tsx index 791889fa..62a71c0b 100644 --- a/react-ui/src/pages/Home/components/NavBar/index.tsx +++ b/react-ui/src/pages/Home/components/NavBar/index.tsx @@ -1,10 +1,87 @@ +import { getAccessToken } from '@/access'; +import Avatar from '@/components/RightContent/AvatarDropdown'; +import { gotoPageIfLogin } from '@/utils/ui'; +import { useNavigate } from '@umijs/max'; +import { Flex } from 'antd'; +import classNames from 'classnames'; import styles from './index.less'; function NavBar() { + const navigate = useNavigate(); + const token = getAccessToken(); + + const gotoPage = (page: string) => { + if (page === 'login') { + navigate('/user/login'); + return; + } + + let pathname = ''; + switch (page) { + case 'service': + pathname = '/dataset/modelDeployment'; + break; + + case 'model': + pathname = '/dataset/model'; + break; + + case 'dataset': + pathname = '/dataset/dataset'; + break; + + case 'mirror': + pathname = '/dataset/mirror'; + break; + + case 'codeConfig': + pathname = '/dataset/codeConfig'; + break; + + default: + break; + } + + if (pathname) { + gotoPageIfLogin(pathname); + } + }; + return (
-
智能材料科研平台
-
首页
+ + + 智能材料科研平台 +
gotoPage('service')}> + 服务 +
+
gotoPage('model')}> + 模型 +
+
gotoPage('dataset')}> + 数据集 +
+
gotoPage('mirror')}> + 镜像 +
+
gotoPage('codeConfig')}> + 代码配置 +
+
+ + {token ? ( + + ) : ( +
gotoPage('login')} + > + 登录 +
+ )}
); } diff --git a/react-ui/src/pages/Home/components/ScrollReveal/index.tsx b/react-ui/src/pages/Home/components/ScrollReveal/index.tsx new file mode 100644 index 00000000..a043234e --- /dev/null +++ b/react-ui/src/pages/Home/components/ScrollReveal/index.tsx @@ -0,0 +1,25 @@ +import { motion, useInView, Variants } from 'motion/react'; +import { ReactNode, useRef } from 'react'; + +type ScrollRevealProps = { + children: ReactNode; + variants: Variants; +}; + +function ScrollReveal({ children, variants }: ScrollRevealProps) { + const ref = useRef(null); + const isInView = useInView(ref, { amount: 'all' }); + + return ( + + {children} + + ); +} + +export default ScrollReveal; diff --git a/react-ui/src/pages/Home/components/Service/index.less b/react-ui/src/pages/Home/components/Service/index.less index 62ea8fa9..dd83e60b 100644 --- a/react-ui/src/pages/Home/components/Service/index.less +++ b/react-ui/src/pages/Home/components/Service/index.less @@ -4,12 +4,11 @@ flex-direction: column; align-items: center; width: 100%; - margin-top: -4rem; - padding: 9.625rem 16.25rem 6.25rem; + padding: 5.625rem @home-padding-x 6.25rem; .backgroundFullImage(url(@/assets/img/home/service-bg.png)); &__item { - width: 21rem; + width: 25%; padding: 1.25rem 1.25rem 1.5625rem; background: #ffffff; border-radius: 1.25rem; @@ -42,7 +41,7 @@ &__title { height: 2.625rem; margin-bottom: 0.875rem; - color: #020814; + color: @home-text-color; font-size: 0.9375rem; .multiLine(2); } @@ -52,21 +51,25 @@ } &__user-avatar { + flex: none; width: 1.3125rem; height: 1.3125rem; margin-right: 0.5rem; } - &__user-name { + &__user { margin-right: 0.5rem; color: #191919; font-size: 0.875rem; + .singleLine(); } &__date { - color: #8284a4; + flex: none; + margin-right: 0; + margin-left: auto; + color: @text-color-tertiary; font-size: 0.8125rem; - text-align: right; } } } diff --git a/react-ui/src/pages/Home/components/Service/index.tsx b/react-ui/src/pages/Home/components/Service/index.tsx index de70e411..2f28a5f0 100644 --- a/react-ui/src/pages/Home/components/Service/index.tsx +++ b/react-ui/src/pages/Home/components/Service/index.tsx @@ -6,14 +6,34 @@ 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 { gotoPageIfLogin } from '@/utils/ui'; import { Flex } from 'antd'; +import { motion, type Variants } from 'motion/react'; import { useEffect, useState } from 'react'; import BlockTitle from '../BlockTitle'; import styles from './index.less'; +const serviceVariants: Variants = { + offscreen: { + y: -100, + opacity: 0, + transition: { + ease: 'linear', + duration: 0, + }, + }, + onscreen: (index: number) => ({ + y: 0, + opacity: 1, + transition: { + type: 'spring', + duration: 1, + delay: index * 0.3, + }, + }), +}; + function ServiceBlock() { - const navigate = useNavigate(); const [serviceData, setServiceData] = useState([]); const images = [ServiceImg1, ServiceImg2, ServiceImg3, ServiceImg4]; @@ -32,16 +52,20 @@ function ServiceBlock() {
navigate('/dataset/modelDeployment')} + style={{ marginBottom: '5.25rem' }} + onClick={() => gotoPageIfLogin('/dataset/modelDeployment')} > - + {serviceData.map((item, index) => { return ( -
navigate(`/dataset/modelDeployment/serviceInfo/${item.id}`)} + onClick={() => gotoPageIfLogin(`/dataset/modelDeployment/serviceInfo/${item.id}`)} + initial="offscreen" + whileInView="onscreen" + custom={index} >
{item.service_name}
- - - -
{item.create_by}
-
+ + +
{item.create_by}
{formatDate(item.create_time)}
-
+ ); })}
diff --git a/react-ui/src/pages/Home/components/Statistics/index.less b/react-ui/src/pages/Home/components/Statistics/index.less index 9542fb9c..848e713d 100644 --- a/react-ui/src/pages/Home/components/Statistics/index.less +++ b/react-ui/src/pages/Home/components/Statistics/index.less @@ -3,7 +3,7 @@ align-items: center; justify-content: space-evenly; width: 87.5rem; - height: 7.5rem; + padding: 2.125rem 0 1.625rem; .backgroundFullImage(url(@/assets/img/home/statistics-bg.png)); &__item { @@ -11,19 +11,20 @@ align-items: center; &__icon { - width: 3rem; - height: 3rem; + width: 3.75rem; + height: 3.75rem; margin-right: 1rem; } &__count { - color: #ffffff; + color: @home-text-color; font-size: 2.25rem; font-family: YouSheBiaoTiHei; + line-height: 3rem; } &__name { - color: #ffffff; + color: @home-text-color-secondary; font-size: 0.875rem; } } diff --git a/react-ui/src/pages/Home/components/Statistics/index.tsx b/react-ui/src/pages/Home/components/Statistics/index.tsx index 953dde78..1357532a 100644 --- a/react-ui/src/pages/Home/components/Statistics/index.tsx +++ b/react-ui/src/pages/Home/components/Statistics/index.tsx @@ -6,6 +6,7 @@ import ServiceIcon from '@/assets/img/home/service.png'; import { getAssetPublicCountReq } from '@/services/home'; import { to } from '@/utils/promise'; import { useEffect, useState } from 'react'; +import CountUp from 'react-countup'; import styles from './index.less'; function StatisticsBlock() { @@ -84,7 +85,14 @@ function StatisticsBlock() {
-
{item.value ?? '--'}
+
+ {item.value ? ( + + ) : ( + '--' + )} +
+
{item.title}
diff --git a/react-ui/src/pages/Home/components/ViewMore/index.less b/react-ui/src/pages/Home/components/ViewMore/index.less index 8ce71d6f..302cec87 100644 --- a/react-ui/src/pages/Home/components/ViewMore/index.less +++ b/react-ui/src/pages/Home/components/ViewMore/index.less @@ -3,32 +3,48 @@ right: 16.25rem; display: flex; align-items: center; - color: .addAlpha(#020814, 0.7) []; + color: .addAlpha(@home-text-color, 0.7) []; font-size: 0.9375rem; cursor: pointer; - + transition: color 0.3s; &:hover { - color: #020814; + color: @home-text-color; } - &__img { - display: inline; + &__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; } - &__hover-img { - display: none; - width: 1.5625rem; - margin-left: 0.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 { - display: none; + &:hover &__img--first { + opacity: 0; } - &:hover &__hover-img { - display: inline; + &:hover &__img--second { + opacity: 1; } } diff --git a/react-ui/src/pages/Home/components/ViewMore/index.tsx b/react-ui/src/pages/Home/components/ViewMore/index.tsx index c10d2fb2..b85acafd 100644 --- a/react-ui/src/pages/Home/components/ViewMore/index.tsx +++ b/react-ui/src/pages/Home/components/ViewMore/index.tsx @@ -14,14 +14,16 @@ function ViewMore({ className, style, onClick }: ViewMoreProps) { return (
查看更多 - - +
+ + +
); } diff --git a/react-ui/src/pages/Home/index.less b/react-ui/src/pages/Home/index.less index 97dcf000..aed372c8 100644 --- a/react-ui/src/pages/Home/index.less +++ b/react-ui/src/pages/Home/index.less @@ -1,6 +1,5 @@ .home { - height: 100%; - overflow-y: auto; + padding-top: @home-info-height; font-family: Alibaba; &__separator { @@ -9,7 +8,13 @@ display: block; width: 97.5rem; height: 8.375rem; - margin: 0 auto; - margin-top: -5.875rem; + 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%; } } diff --git a/react-ui/src/pages/Home/index.tsx b/react-ui/src/pages/Home/index.tsx index c275024a..4cfee84c 100644 --- a/react-ui/src/pages/Home/index.tsx +++ b/react-ui/src/pages/Home/index.tsx @@ -1,5 +1,6 @@ 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'; @@ -17,19 +18,12 @@ function Home() { draggable={false} className={styles['home__separator']} > - - - - +
+ + +
+
); } diff --git a/react-ui/src/pages/HyperParameter/components/CreateForm/ExecuteConfig.tsx b/react-ui/src/pages/HyperParameter/components/CreateForm/ExecuteConfig.tsx index 01bc4001..313f8179 100644 --- a/react-ui/src/pages/HyperParameter/components/CreateForm/ExecuteConfig.tsx +++ b/react-ui/src/pages/HyperParameter/components/CreateForm/ExecuteConfig.tsx @@ -323,6 +323,7 @@ function ExecuteConfig() { className={styles['hyper-parameter__body__name']} {...restField} name={[name, 'name']} + dependencies={fields.map((_, i) => ['parameters', i, 'name'])} required rules={[ { diff --git a/react-ui/src/pages/HyperParameter/components/HyperParameterBasic/index.tsx b/react-ui/src/pages/HyperParameter/components/HyperParameterBasic/index.tsx index 282a5667..ea5ad15b 100644 --- a/react-ui/src/pages/HyperParameter/components/HyperParameterBasic/index.tsx +++ b/react-ui/src/pages/HyperParameter/components/HyperParameterBasic/index.tsx @@ -1,6 +1,6 @@ import ConfigInfo, { type BasicInfoData } from '@/components/ConfigInfo'; import { ExperimentStatus, hyperParameterOptimizedMode } from '@/enums'; -import { useComputingResource } from '@/hooks/useComputingResource'; +import { useSystemResource } from '@/hooks/useComputingResource'; import ExperimentRunBasic from '@/pages/AutoML/components/ExperimentRunBasic'; import { schedulerAlgorithms, @@ -41,7 +41,7 @@ function HyperParameterBasic({ instanceStatus, isInstance = false, }: HyperParameterBasicProps) { - const getResourceDescription = useComputingResource()[1]; + const getResourceDescription = useSystemResource(); const basicDatas: BasicInfoData[] = useMemo(() => { if (!info) { diff --git a/react-ui/src/pages/Mirror/Info/index.tsx b/react-ui/src/pages/Mirror/Info/index.tsx index 30b6ad47..1a925831 100644 --- a/react-ui/src/pages/Mirror/Info/index.tsx +++ b/react-ui/src/pages/Mirror/Info/index.tsx @@ -46,6 +46,7 @@ export type MirrorInfoData = { }; export type MirrorVersionData = { + image_id: number; id: number; version: string; url: string; diff --git a/react-ui/src/pages/Mirror/List/index.tsx b/react-ui/src/pages/Mirror/List/index.tsx index 09127603..7dd57234 100644 --- a/react-ui/src/pages/Mirror/List/index.tsx +++ b/react-ui/src/pages/Mirror/List/index.tsx @@ -48,6 +48,7 @@ export type MirrorData = { description: string; create_time: string; create_by: string; + version_count: number; }; function MirrorList() { diff --git a/react-ui/src/pages/ModelDeployment/CreateVersion/index.tsx b/react-ui/src/pages/ModelDeployment/CreateVersion/index.tsx index cd1992c2..a76d67da 100644 --- a/react-ui/src/pages/ModelDeployment/CreateVersion/index.tsx +++ b/react-ui/src/pages/ModelDeployment/CreateVersion/index.tsx @@ -23,7 +23,7 @@ import { removeFormListItem } from '@/utils/ui'; import { MinusCircleOutlined, PlusCircleOutlined, PlusOutlined } from '@ant-design/icons'; import { useNavigate, useParams } from '@umijs/max'; import { App, Button, Col, Flex, Form, Input, InputNumber, Row } from 'antd'; -import { omit, pick } from 'lodash'; +import { omit } from 'lodash'; import { useEffect, useState } from 'react'; import { CreateServiceVersionFrom, ServiceOperationType, ServiceVersionData } from '../types'; import styles from './index.less'; @@ -79,7 +79,7 @@ function CreateServiceVersion() { if (res.model && typeof res.model === 'object') { model = changePropertyName(res.model, { show_value: 'showValue' }); // 接口返回是数据没有 value 值,但是 form 需要 value - model.value = model.showValue; + // model.value = model.showValue; } // 环境变量 if (res.env_variables && typeof res.env_variables === 'object') { @@ -117,7 +117,6 @@ function CreateServiceVersion() { // 创建版本 const createServiceVersion = async (formData: FormData) => { const envList = formData['env_variables']; - const model = formData['model']; const envVariables = envList?.reduce((acc, cur) => { acc[cur.key] = cur.value; return acc; @@ -125,13 +124,9 @@ function CreateServiceVersion() { // 根据后台要求,修改表单数据 const object = { - ...omit(formData, ['replicas', 'env_variables', 'model']), + ...omit(formData, ['replicas', 'env_variables']), replicas: Number(formData.replicas), env_variables: envVariables, - model: changePropertyName( - pick(model, ['id', 'name', 'version', 'path', 'identifier', 'owner', 'showValue']), - { showValue: 'show_value' }, - ), service_id: serviceId, }; @@ -427,6 +422,7 @@ function CreateServiceVersion() { {...restField} name={[name, 'key']} style={{ flex: 1 }} + dependencies={fields.map((_, i) => ['env_variables', i, 'key'])} rules={[ { validator: (_, value) => { diff --git a/react-ui/src/pages/ModelDeployment/ServiceInfo/index.tsx b/react-ui/src/pages/ModelDeployment/ServiceInfo/index.tsx index 617d9a32..fae1e3be 100644 --- a/react-ui/src/pages/ModelDeployment/ServiceInfo/index.tsx +++ b/react-ui/src/pages/ModelDeployment/ServiceInfo/index.tsx @@ -9,7 +9,8 @@ import PageTitle from '@/components/PageTitle'; import SubAreaTitle from '@/components/SubAreaTitle'; import { ServiceRunStatus, serviceStatusOptions } from '@/enums'; import { useCacheState } from '@/hooks/useCacheState'; -import { useComputingResource } from '@/hooks/useComputingResource'; +import { useSystemResource } from '@/hooks/useComputingResource'; +import { ModelData } from '@/pages/Dataset/config'; import { deleteServiceVersionReq, getServiceInfoReq, @@ -18,6 +19,7 @@ import { } from '@/services/modelDeployment'; import themes from '@/styles/theme.less'; import { formatDate } from '@/utils/date'; +import { formatModel } from '@/utils/format'; import { openAntdModal } from '@/utils/modal'; import { to } from '@/utils/promise'; import SessionStorage from '@/utils/sessionStorage'; @@ -87,7 +89,7 @@ function ServiceInfo() { format: formatDate, }, ]; - const getResourceDescription = useComputingResource()[1]; + const getResourceDescription = useSystemResource(); // 获取服务详情 const getServiceInfo = useCallback(async () => { @@ -110,8 +112,8 @@ function ServiceInfo() { if (res && res.data) { const { content = [], totalElements = 0 } = res.data; content.forEach((item: ServiceVersionData) => { - if (item.model && !item.model.show_value) { - item.model.show_value = `${item.model.name}:${item.model.version}`; + if (item.model && !item.model.showValue) { + item.model.showValue = `${item.model.name}:${item.model.version}`; } }); setTableData(content); @@ -258,6 +260,20 @@ function ServiceInfo() { }, }; + // 去模型 + const gotoModel = (record: ServiceVersionData, e: React.MouseEvent) => { + e.stopPropagation(); + + const model = record.model as any as ModelData; + const link = formatModel(model)?.link; + if (link) { + setCacheState({ + pagination, + }); + navigate(link); + } + }; + const columns: TableProps['columns'] = [ { title: '序号', @@ -278,10 +294,12 @@ function ServiceInfo() { }, { title: '模型版本', - dataIndex: ['model', 'show_value'], + dataIndex: ['model', 'showValue'], key: 'model', width: '20%', - render: tableCellRender(true), + render: tableCellRender(true, TableCellValueType.Link, { + onClick: gotoModel, + }), }, { title: '镜像版本', diff --git a/react-ui/src/pages/ModelDeployment/components/VersionBasicInfo/index.tsx b/react-ui/src/pages/ModelDeployment/components/VersionBasicInfo/index.tsx index 2656b946..47d03d61 100644 --- a/react-ui/src/pages/ModelDeployment/components/VersionBasicInfo/index.tsx +++ b/react-ui/src/pages/ModelDeployment/components/VersionBasicInfo/index.tsx @@ -1,6 +1,6 @@ import BasicInfo, { type BasicInfoData } from '@/components/BasicInfo'; import { ServiceRunStatus } from '@/enums'; -import { useComputingResource } from '@/hooks/useComputingResource'; +import { useSystemResource } from '@/hooks/useComputingResource'; import { ServiceVersionData } from '@/pages/ModelDeployment/types'; import { formatDate } from '@/utils/date'; import { formatMirror, formatModel } from '@/utils/format'; @@ -36,7 +36,7 @@ const formatEnvText = (env?: Record) => { }; function VersionBasicInfo({ info }: BasicInfoProps) { - const getResourceDescription = useComputingResource()[1]; + const getResourceDescription = useSystemResource(); const datas: BasicInfoData[] = [ { diff --git a/react-ui/src/pages/ModelDeployment/components/VersionCompareModal/index.tsx b/react-ui/src/pages/ModelDeployment/components/VersionCompareModal/index.tsx index ee92edb2..c31e4700 100644 --- a/react-ui/src/pages/ModelDeployment/components/VersionCompareModal/index.tsx +++ b/react-ui/src/pages/ModelDeployment/components/VersionCompareModal/index.tsx @@ -1,6 +1,6 @@ import KFModal from '@/components/KFModal'; import { ServiceRunStatus } from '@/enums'; -import { useComputingResource } from '@/hooks/useComputingResource'; +import { useSystemResource } from '@/hooks/useComputingResource'; import { type ServiceVersionData } from '@/pages/ModelDeployment/types'; import { getServiceVersionCompareReq } from '@/services/modelDeployment'; import { isEmpty } from '@/utils'; @@ -42,7 +42,7 @@ const formatEnvText = (env: Record) => { function VersionCompareModal({ version1, version2, ...rest }: VersionCompareModalProps) { const [compareData, setCompareData] = useState(undefined); - const getResourceDescription = useComputingResource()[1]; + const getResourceDescription = useSystemResource(); const fields: FiledType[] = useMemo( () => [ diff --git a/react-ui/src/pages/ModelDeployment/types.ts b/react-ui/src/pages/ModelDeployment/types.ts index 3423ce01..d333d9f2 100644 --- a/react-ui/src/pages/ModelDeployment/types.ts +++ b/react-ui/src/pages/ModelDeployment/types.ts @@ -34,7 +34,7 @@ export type ServiceVersionData = { path: string; identifier: string; owner: string; - show_value: string; + showValue: string; }; code_config: { // 代码配置 diff --git a/react-ui/src/pages/Pipeline/Info/index.jsx b/react-ui/src/pages/Pipeline/Info/index.jsx index 4e86dbbb..c0e0c51c 100644 --- a/react-ui/src/pages/Pipeline/Info/index.jsx +++ b/react-ui/src/pages/Pipeline/Info/index.jsx @@ -3,7 +3,7 @@ import { useStateRef } from '@/hooks/useStateRef'; import { useVisible } from '@/hooks/useVisible'; import { getWorkflowById, saveWorkflow } from '@/services/pipeline/index.js'; import themes from '@/styles/theme.less'; -import { fittingString, parseJsonText, s8 } from '@/utils'; +import { fittingString, s8 } from '@/utils'; import { to } from '@/utils/promise'; import G6 from '@antv/g6'; import { useNavigate, useParams } from '@umijs/max'; @@ -54,11 +54,20 @@ const EditPipeline = () => { const onDragEnd = (val) => { const { x, y } = val; const point = graph.getPointByClient(x, y); + + let label = val.label; + const data = graph.save(); + const nodeLabels = data.nodes.map((v) => v.label); + if (nodeLabels.includes(label)) { + label += '-' + s8(); + } + // 元模型 const model = { ...val, x: point.x, y: point.y, + label, id: val.component_name + '-' + s8(), isCluster: false, formError: true, @@ -90,24 +99,29 @@ const EditPipeline = () => { // 保存 const savePipeline = async (isBack) => { - const [globalParamRes, globalParamError] = await to(paramsDrawerRef.current.validateFields()); - if (globalParamError) { - message.error('全局参数配置有误'); - openParamsDrawer(); - return; - } - closeParamsDrawer(); + // 验证全局参数 + // 现在改为关闭的时候就验证了 + // const [globalParamRes, globalParamError] = await to(paramsDrawerRef.current.validateFields()); + // if (globalParamError) { + // message.error('全局参数配置有误'); + // openParamsDrawer(); + // return; + // } + // closeParamsDrawer(); - const [propsRes, propsError] = await to(propsRef.current.validateFields()); - if (propsError) { - message.error('节点必填项必须配置'); - return; - } - propsRef.current.close(); + // 以前没有遮挡【保存】按钮时有用 + // 验证节点必填参数 + // const [propsRes, propsError] = await to(propsRef.current.validateFields()); + // if (propsError) { + // message.error('节点必填项必须配置'); + // return; + // } + // propsRef.current.close(); setTimeout(() => { const data = graph.save(); // console.log(data); + // 验证节点必填参数 const errorNode = data.nodes.find((item) => item.formError === true); if (errorNode) { message.error(`【${errorNode.label}】节点配置验证失败`); @@ -117,11 +131,25 @@ const EditPipeline = () => { } return; } + + // 验证节点名称是否有重命名 + const nodeLabels = data.nodes.map((v) => v.label); + for (let i = 0; i < nodeLabels.length; i++) { + const current = nodeLabels[i]; + for (let j = i + 1; j < nodeLabels.length; j++) { + const next = nodeLabels[j]; + if (current === next) { + message.error(`存在重名的【${current}】节点`); + return; + } + } + } + const params = { ...locationParams, name: workflowInfo?.name, - dag: JSON.stringify(data), - global_param: JSON.stringify(globalParamRes.global_param), + dag: data, + global_param: globalParam, }; saveWorkflow(params).then((ret) => { message.success('保存成功'); @@ -290,7 +318,7 @@ const EditPipeline = () => { const { global_param, dag } = res.data; setGlobalParam(global_param || []); if (dag) { - getGraphData(parseJsonText(dag)); + getGraphData(dag); } } }; @@ -299,13 +327,19 @@ const EditPipeline = () => { const openNodeDrawer = (node, validate = false) => { // 获取所有的上游节点 const parentNodes = findAllParentNodes(graph, node); - // 如果没有打开过全局参数抽屉,获取不到全局参数 - const globalParams = - paramsDrawerRef.current.getFieldsValue().global_param || globalParamRef.current; + // q全局参数 + const globalParams = globalParamRef.current; // 打开节点编辑抽屉 propsRef.current.showDrawer(node.getModel(), globalParams, parentNodes, validate); }; + // 关闭全局参数节点,获取全局参数 + const closeGlobalParamsDrawer = () => { + const { global_param } = paramsDrawerRef.current.getFieldsValue(); + setGlobalParam(global_param); + closeParamsDrawer(); + }; + // 初始化图 const initGraph = () => { const contextMenu = initMenu(); @@ -730,7 +764,7 @@ const EditPipeline = () => { ref={paramsDrawerRef} open={paramsDrawerOpen} globalParam={globalParam} - onClose={closeParamsDrawer} + onClose={closeGlobalParamsDrawer} >
); diff --git a/react-ui/src/pages/Pipeline/Info/utils.tsx b/react-ui/src/pages/Pipeline/Info/utils.tsx index 336a1354..2feb70c6 100644 --- a/react-ui/src/pages/Pipeline/Info/utils.tsx +++ b/react-ui/src/pages/Pipeline/Info/utils.tsx @@ -1,5 +1,4 @@ import { PipelineGlobalParam, PipelineNodeModelParameter } from '@/types'; -import { parseJsonText } from '@/utils'; import { Graph, INode } from '@antv/g6'; import { type MenuProps } from 'antd'; @@ -42,8 +41,7 @@ export function createMenuItems( ): MenuProps['items'] { const nodes: MenuProps['items'] = parentNodes.map((item) => { const model = item.getModel(); - const out_parameters = model.out_parameters as string | undefined | null; - const out_parametersObj = parseJsonText(out_parameters); + const out_parametersObj = model.out_parameters as Record; const outParametersList = Object.keys(out_parametersObj ?? {}); return { key: model.id as string, @@ -72,15 +70,6 @@ export function createMenuItems( } } -export function getInParameterComponent( - parameter: PipelineNodeModelParameter, -): React.ReactNode | null { - if (parameter.value) { - } - - return null; -} - // 判断是否允许输入 export function canInput(parameter: PipelineNodeModelParameter) { const { type, item_type } = parameter; @@ -89,6 +78,9 @@ export function canInput(parameter: PipelineNodeModelParameter) { (item_type === 'dataset' || item_type === 'model' || item_type === 'image' || - item_type === 'code') + item_type === 'code' || + item_type === 'remote-dataset' || + item_type === 'remote-model' || + item_type === 'remote-code') ); } diff --git a/react-ui/src/pages/Pipeline/components/GlobalParamsDrawer/index.tsx b/react-ui/src/pages/Pipeline/components/GlobalParamsDrawer/index.tsx index da764698..17cdeef9 100644 --- a/react-ui/src/pages/Pipeline/components/GlobalParamsDrawer/index.tsx +++ b/react-ui/src/pages/Pipeline/components/GlobalParamsDrawer/index.tsx @@ -1,6 +1,6 @@ import KFIcon from '@/components/KFIcon'; import { getParamComponent, getParamRules } from '@/pages/Experiment/components/AddExperimentModal'; -import { type PipelineGlobalParam } from '@/types'; +import { type PipelineGlobalParam, PipelineGlobalParamType } from '@/types'; import { to } from '@/utils/promise'; import { modalConfirm } from '@/utils/ui'; import { PlusOutlined } from '@ant-design/icons'; @@ -42,6 +42,7 @@ const GlobalParamsDrawer = forwardRef( form.setFieldValue(name, null); }; + // 处理删除 const removeParameter = (name: number, remove: (param: number) => void) => { modalConfirm({ title: '删除后,该全局参数将不可恢复', @@ -52,6 +53,16 @@ const GlobalParamsDrawer = forwardRef( }); }; + // 处理关闭 + const handleClose = async () => { + try { + await form.validateFields(); + onClose(); + } catch { + return false; + } + }; + return ( @@ -81,7 +92,7 @@ const GlobalParamsDrawer = forwardRef( {...restField} name={[name, 'param_name']} label="参数名称" - validateTrigger={[]} + dependencies={fields.map((_, i) => ['global_param', i, 'param_name'])} rules={[ { required: true, message: '请输入参数名称' }, { @@ -97,11 +108,7 @@ const GlobalParamsDrawer = forwardRef( }, ]} > - form.validateFields()} - /> + handleTypeChange(['global_param', name, 'param_value'])} > - 字符串 - 整型 - 布尔类型 + 字符串 + 整型 + 布尔类型 void; @@ -37,12 +49,6 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete const [stagingItem, setStagingItem] = useState( {} as PipelineNodeModelSerialize, ); - const nodeId = Form.useWatch('id', form) as string; - const hasTaskInfo = - nodeId && - !nodeId.startsWith('git-clone') && - !nodeId.startsWith('dataset-export') && - !nodeId.startsWith('model-export'); const [open, setOpen] = useState(false); const [menuItems, setMenuItems] = useState([]); @@ -53,11 +59,16 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete // 不管是否验证成功,都需要获取表单数据 const fields = form.getFieldsValue(); - // 保存字段顺序 - // const control_strategy = { - // ...stagingItem.control_strategy, - // ...fields.control_strategy, - // }; + // 保持原有字段和顺序 + const task_info = { + ...stagingItem.task_info, + ...fields.task_info, + }; + + const control_strategy = { + ...stagingItem.control_strategy, + ...fields.control_strategy, + }; const in_parameters = { ...stagingItem.in_parameters, @@ -68,18 +79,23 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete ...fields.out_parameters, }; - // console.log('getFieldsValue', fields); + console.log('getFieldsValue', fields); const res = { - ...stagingItem, - ...fields, - // control_strategy: JSON.stringify(control_strategy), - in_parameters: JSON.stringify(in_parameters), - out_parameters: JSON.stringify(out_parameters), + ...omit(stagingItem, ['control_strategy', 'task_info', 'in_parameters', 'out_parameters']), + ...omit(fields, ['control_strategy', 'task_info', 'in_parameters', 'out_parameters']), + task_info: task_info, + control_strategy: control_strategy, + in_parameters: in_parameters, + out_parameters: out_parameters, formError: !!error, }; - // console.log('res', res); - onFormChange(res); + + // ant g6 bug + // 如果值为 undefined 时, graph.changeData(data) 会保留前面的值 + const convertRes = undefinedToNull(res); + console.log('res', convertRes); + onFormChange(convertRes as PipelineNodeModelSerialize); } }; const onClose = () => { @@ -96,19 +112,12 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete validate: boolean = false, ) { try { - const nodeData: PipelineNodeModelSerialize = { - ...model, - in_parameters: JSON.parse(model.in_parameters), - out_parameters: JSON.parse(model.out_parameters), - // control_strategy: JSON.parse(model.control_strategy), - }; - // console.log('model', nodeData); setStagingItem({ - ...nodeData, + ...model, }); form.resetFields(); form.setFieldsValue({ - ...nodeData, + ...model, }); if (validate) { form.validateFields(); @@ -120,6 +129,12 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete // 参数下拉菜单 setMenuItems(createMenuItems(params, parentNodes)); + + // 云际组件,设置 store 当前资源类型 + if (model.id.startsWith('remote-task')) { + const resourceType = model.in_parameters['--resource_type'].value; + setCurrentType(resourceType); + } }, close: () => { onClose(); @@ -145,7 +160,7 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete formItemName: NamePath, item: PipelineNodeModelParameter | Pick, ) => { - if (item.item_type === 'code') { + if (item.item_type === 'code' || item.item_type === 'remote-code') { selectCodeConfig(formItemName, item); } else { selectResource(formItemName, item); @@ -157,47 +172,15 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete formItemName: NamePath, item: PipelineNodeModelParameter | Pick, ) => { - const jsonValue = form.getFieldValue(formItemName)?.value; - const fieldValue = parseJsonText(jsonValue) as ServerCodeData; - const defaultSelected = fieldValue - ? { - id: fieldValue.id, - code_repo_name: fieldValue.name, - git_url: fieldValue.code_path, - git_branch: fieldValue.branch, - git_user_name: fieldValue.username, - git_password: fieldValue.password, - ssh_key: fieldValue.ssh_private_key, - is_public: fieldValue.is_public, - } - : undefined; + const defaultSelected = form.getFieldValue(formItemName)?.value as CodeConfigData; const { close } = openAntdModal(CodeSelectorModal, { defaultSelected, onOk: (res) => { if (res) { - const { - id, - code_repo_name, - git_url, - git_branch, - git_user_name, - git_password, - ssh_key, - is_public, - } = res; - const value = JSON.stringify({ - id, - name: code_repo_name, - code_path: git_url, - branch: git_branch, - username: git_user_name, - password: git_password, - ssh_private_key: ssh_key, - is_public, - }); + const { code_repo_name } = res; form.setFieldValue(formItemName, { ...item, - value, + value: res, showValue: code_repo_name, fromSelect: true, }); @@ -216,9 +199,11 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete let type: ResourceSelectorType; switch (item.item_type) { case 'dataset': + case 'remote-dataset': type = ResourceSelectorType.Dataset; break; case 'model': + case 'remote-model': type = ResourceSelectorType.Model; break; default: @@ -237,36 +222,24 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete onOk: (res) => { if (res) { if (type === ResourceSelectorType.Mirror) { - const { activeTab, id, version, path } = res; - if (formItemName === 'image') { - // 单独的选择镜像 - form.setFieldValue(formItemName, path); - } else { - // 输入参数选择镜像 - form.setFieldValue(formItemName, { - ...item, - value: path, - showValue: path, - fromSelect: true, - activeTab, - expandedKeys: [id], - checkedKeys: [`${id}-${version}`], - }); - } - } else { - const { activeTab, id, name, version, path, identifier, owner } = res; - const value = JSON.stringify({ - id, - name, - version, - path, - identifier, - owner, + const { activeTab, ...rest } = res; + const { url, id, image_id } = rest; + form.setFieldValue(formItemName, { + ...item, + value: rest, + showValue: url, + fromSelect: true, + activeTab, + expandedKeys: [`${image_id}`], + checkedKeys: [`${image_id}-${id}`], }); + } else { + const { activeTab, ...rest } = res; + const { id, name, version } = rest; const showValue = `${name}:${version}`; form.setFieldValue(formItemName, { ...item, - value, + value: rest, showValue, fromSelect: true, activeTab, @@ -275,19 +248,15 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete }); } } else { - if (type === ResourceSelectorType.Mirror && formItemName === 'image') { - form.setFieldValue(formItemName, undefined); - } else { - form.setFieldValue(formItemName, { - ...item, - value: undefined, - showValue: undefined, - fromSelect: false, - activeTab: undefined, - expandedKeys: [], - checkedKeys: [], - }); - } + form.setFieldValue(formItemName, { + ...item, + value: undefined, + showValue: undefined, + fromSelect: false, + activeTab: undefined, + expandedKeys: [], + checkedKeys: [], + }); } form.validateFields([formItemName]); close(); @@ -298,14 +267,14 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete // 获取选择数据集、模型后面按钮 icon const getSelectBtnIcon = (item: { item_type: string }) => { const type = item.item_type; - if (type === 'code') { + if (type === 'code' || type === 'remote-code') { return ; } let selectorType: ResourceSelectorType; - if (type === 'dataset') { + if (type === 'dataset' || type === 'remote-dataset') { selectorType = ResourceSelectorType.Dataset; - } else if (type === 'model') { + } else if (type === 'model' || type === 'remote-model') { selectorType = ResourceSelectorType.Model; } else { selectorType = ResourceSelectorType.Mirror; @@ -323,16 +292,16 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete // form item label const getLabel = ( item: { key: string; value: PipelineNodeModelParameter }, - namePrefix: string, + parentName: string, ) => { - return item.value.type === 'select' ? ( + return item.value.type === ComponentType.Select || item.value.type === ComponentType.Map ? ( item.value.label + '(' + item.key + ')' ) : ( { - handleParameterClick([namePrefix, item.key], { + handleParameterClick([parentName, item.key], { ...item.value, value, fromSelect: true, @@ -380,21 +349,249 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete return rules; }; + // 云际组件,选择类型后,重置镜像和资源,获取镜像、资源列表 + const handleParameterSelect = ( + value: ParameterSelectObject, + itemType: string, + parentName: string, + ) => { + if (itemType === 'remote-resource-type') { + setCurrentType(value.value); + const remoteImage = form.getFieldValue([parentName, '--image']); + form.setFieldValue([parentName, '--image'], { ...remoteImage, value: undefined }); + const remoteResource = form.getFieldValue([parentName, '--resource']); + form.setFieldValue([parentName, '--resource'], { ...remoteResource, value: undefined }); + } + }; + + // 表单组件 + const getFormComponent = ( + item: { key: string; value: PipelineNodeModelParameter }, + parentName: string, + ) => { + return ( + <> + {item.value.type === ComponentType.Ref && ( + + + + + + + + + )} + {item.value.type === ComponentType.Select && + (ParameterSelectTypeList.includes(item.value.item_type as ParameterSelectDataType) ? ( + + + handleParameterSelect( + value as ParameterSelectObject, + item.value.item_type, + parentName, + ) + } + /> + + ) : null)} + {item.value.type === ComponentType.Map && ( + + + {(fields, { add, remove }) => ( + <> + {fields.map(({ key, name, ...restField }, index) => ( + + [ + parentName, + item.key, + 'value', + i, + 'name', + ])} + rules={[ + { + validator: (_, value) => { + if (!value) { + return Promise.reject(new Error('请输入变量名')); + } + if (!/^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(value)) { + return Promise.reject( + new Error( + '变量名只支持字母、数字、下划线、中横线并且必须以字母或下划线开头', + ), + ); + } + // 判断不能重名 + const list = form + .getFieldValue([parentName, item.key, 'value']) + .filter( + (item: FormListVariable | undefined) => + item !== undefined && item !== null, + ); + + const names = list.map((item: FormListVariable) => item.name); + if (new Set(names).size !== names.length) { + return Promise.reject(new Error('变量名不能重复')); + } + return Promise.resolve(); + }, + }, + ]} + > + + + = + + {/* */} + { + handleParameterClick( + [parentName, item.key, 'value', name, 'value'], + { + ...item.value, + value, + fromSelect: true, + showValue: value, + }, + ); + }} + /> + } + > + + + + {index === fields.length - 1 && ( + + )} + + + ))} + {fields.length === 0 && ( + + )} + + )} + + + )} + {item.value.type === ComponentType.Str && ( + + + + )} + + ); + }; + + // 基本参数 + const basicParametersList = Object.entries(stagingItem.task_info ?? {}) + .map(([key, value]) => ({ + key, + value, + })) + .filter((v) => v.value.visible === true); + // 控制策略 - // const controlStrategyList = Object.entries(stagingItem.control_strategy ?? {}).map( - // ([key, value]) => ({ key, value }), - // ); + const controlStrategyList = Object.entries(stagingItem.control_strategy ?? {}) + .map(([key, value]) => ({ key, value })) + .filter((v) => v.value.visible === true); // 输入参数 - const inParametersList = Object.entries(stagingItem.in_parameters ?? {}).map(([key, value]) => ({ - key, - value, - })); + const inParametersList = Object.entries(stagingItem.in_parameters ?? {}) + .map(([key, value]) => ({ + key, + value, + })) + .filter((v) => v.value.visible === true); // 输出参数 - const outParametersList = Object.entries(stagingItem.out_parameters ?? {}).map( - ([key, value]) => ({ key, value }), - ); + const outParametersList = Object.entries(stagingItem.out_parameters ?? {}) + .map(([key, value]) => ({ key, value })) + .filter((v) => v.value.visible === true); return ( - {hasTaskInfo && ( - <> -
- -
- -
- - - - - - -
-
- { - handleParameterClick('working_directory', value); - }} - /> - } - > - - - { - handleParameterClick('command', value); - }} - /> - } - > -