diff --git a/react-ui/src/pages/Workspace/components/AssetsManagement/index.tsx b/react-ui/src/pages/Workspace/components/AssetsManagement/index.tsx
index 74227506..7c911318 100644
--- a/react-ui/src/pages/Workspace/components/AssetsManagement/index.tsx
+++ b/react-ui/src/pages/Workspace/components/AssetsManagement/index.tsx
@@ -7,46 +7,48 @@ import styles from './index.less';
function AssetsManagement() {
const [type, setType] = useState(CommonTabKeys.Public);
const [assetCounts, setAssetCounts] = useState<{ title: string; value: number }[]>([]);
+
useEffect(() => {
+ // 获取工作空间资产数量
+ const getWorkspacAssetCount = async () => {
+ const params = {
+ isPublic: type === CommonTabKeys.Public,
+ };
+ const [res] = await to(getWorkspaceAssetCountReq(params));
+ if (res && res.data) {
+ const { component, dataset, image, model, workflow } = res.data;
+ const items = [
+ {
+ title: '数据集',
+ value: dataset,
+ },
+ {
+ title: '模型',
+ value: model,
+ },
+ {
+ title: '镜像',
+ value: image,
+ },
+ {
+ title: '组件',
+ value: component,
+ },
+ // {
+ // title: '代码配置',
+ // value: 0,
+ // },
+ {
+ title: '流水线模版',
+ value: workflow,
+ },
+ ];
+ setAssetCounts(items);
+ }
+ };
+
getWorkspacAssetCount();
}, [type]);
- // 获取工作空间资产数量
- const getWorkspacAssetCount = async () => {
- const params = {
- isPublic: type === CommonTabKeys.Public,
- };
- const [res] = await to(getWorkspaceAssetCountReq(params));
- if (res && res.data) {
- const { component, dataset, image, model, workflow } = res.data;
- const items = [
- {
- title: '数据集',
- value: dataset,
- },
- {
- title: '模型',
- value: model,
- },
- {
- title: '镜像',
- value: image,
- },
- {
- title: '组件',
- value: component,
- },
- // {
- // title: '代码配置',
- // value: 0,
- // },
- {
- title: '流水线模版',
- value: workflow,
- },
- ];
- setAssetCounts(items);
- }
- };
return (
diff --git a/react-ui/src/pages/Workspace/components/ExperimentChart/index.tsx b/react-ui/src/pages/Workspace/components/ExperimentChart/index.tsx
index 3cede1ef..235683ee 100644
--- a/react-ui/src/pages/Workspace/components/ExperimentChart/index.tsx
+++ b/react-ui/src/pages/Workspace/components/ExperimentChart/index.tsx
@@ -1,6 +1,6 @@
import themes from '@/styles/theme.less';
import * as echarts from 'echarts';
-import React, { useEffect, useRef } from 'react';
+import React, { useEffect, useMemo, useRef } from 'react';
import styles from './index.less';
const color1 = new echarts.graphic.LinearGradient(
@@ -94,102 +94,106 @@ function ExperimentChart({ chartData, style }: ExperimentChartProps) {
chartData.Running +
chartData.Succeeded +
chartData.Terminated;
- const options: echarts.EChartsOption = {
- title: {
- show: true,
- left: '29%',
- top: 'center',
- textAlign: 'center',
- text: [`{a|${total}}`, '{b|实验状态}'].join('\n'),
- textStyle: {
- rich: {
- a: {
- color: themes['textColor'],
- fontSize: 20,
- fontWeight: 700,
- lineHeight: 28,
- },
- b: {
- color: themes['textColorSecondary'],
- fontSize: 10,
- fontWeight: 'normal',
+
+ const options: echarts.EChartsOption = useMemo(
+ () => ({
+ title: {
+ show: true,
+ left: '29%',
+ top: 'center',
+ textAlign: 'center',
+ text: [`{a|${total}}`, '{b|实验状态}'].join('\n'),
+ textStyle: {
+ rich: {
+ a: {
+ color: themes['textColor'],
+ fontSize: 20,
+ fontWeight: 700,
+ lineHeight: 28,
+ },
+ b: {
+ color: themes['textColorSecondary'],
+ fontSize: 10,
+ fontWeight: 'normal',
+ },
},
},
},
- },
- tooltip: {
- trigger: 'item',
- },
- legend: {
- top: 'center',
- right: '5%',
- orient: 'vertical',
- icon: 'circle',
- itemWidth: 6,
- itemGap: 20,
- height: 100,
- },
- color: [color1, color2, color3, color4, color5],
- series: [
- {
- type: 'pie',
- radius: ['70%', '80%'],
- center: ['30%', '50%'],
- avoidLabelOverlap: false,
- padAngle: 3,
- itemStyle: {
- borderRadius: 3,
- },
- minAngle: 5,
- label: {
- show: false,
- },
- emphasis: {
+ tooltip: {
+ trigger: 'item',
+ },
+ legend: {
+ top: 'center',
+ right: '5%',
+ orient: 'vertical',
+ icon: 'circle',
+ itemWidth: 6,
+ itemGap: 20,
+ height: 100,
+ },
+ color: [color1, color2, color3, color4, color5],
+ series: [
+ {
+ type: 'pie',
+ radius: ['70%', '80%'],
+ center: ['30%', '50%'],
+ avoidLabelOverlap: false,
+ padAngle: 3,
+ itemStyle: {
+ borderRadius: 3,
+ },
+ minAngle: 5,
label: {
show: false,
},
+ emphasis: {
+ label: {
+ show: false,
+ },
+ },
+ labelLine: {
+ show: false,
+ },
+ data: [
+ { value: chartData.Failed > 0 ? chartData.Failed : undefined, name: '失败' },
+ { value: chartData.Succeeded > 0 ? chartData.Succeeded : undefined, name: '成功' },
+ { value: chartData.Terminated > 0 ? chartData.Terminated : undefined, name: '中止' },
+ { value: chartData.Pending > 0 ? chartData.Pending : undefined, name: '等待' },
+ { value: chartData.Running > 0 ? chartData.Running : undefined, name: '运行中' },
+ ],
},
- labelLine: {
- show: false,
- },
- data: [
- { value: chartData.Failed > 0 ? chartData.Failed : undefined, name: '失败' },
- { value: chartData.Succeeded > 0 ? chartData.Succeeded : undefined, name: '成功' },
- { value: chartData.Terminated > 0 ? chartData.Terminated : undefined, name: '中止' },
- { value: chartData.Pending > 0 ? chartData.Pending : undefined, name: '等待' },
- { value: chartData.Running > 0 ? chartData.Running : undefined, name: '运行中' },
- ],
- },
- {
- type: 'pie',
- radius: '60%',
- center: ['30%', '50%'],
- avoidLabelOverlap: false,
- label: {
- show: false,
- },
- tooltip: {
- show: false,
- },
- emphasis: {
+ {
+ type: 'pie',
+ radius: '60%',
+ center: ['30%', '50%'],
+ avoidLabelOverlap: false,
label: {
show: false,
},
- disabled: true,
- },
- animation: false,
- labelLine: {
- show: false,
- },
- data: [
- {
- value: 100,
- itemStyle: { color: color6, borderColor: 'rgba(22, 100, 255, 0.08)', borderWidth: 1 },
+ tooltip: {
+ show: false,
},
- ],
- },
- ],
- };
+ emphasis: {
+ label: {
+ show: false,
+ },
+ disabled: true,
+ },
+ animation: false,
+ labelLine: {
+ show: false,
+ },
+ data: [
+ {
+ value: 100,
+ itemStyle: { color: color6, borderColor: 'rgba(22, 100, 255, 0.08)', borderWidth: 1 },
+ },
+ ],
+ },
+ ],
+ }),
+ [chartData, total],
+ );
useEffect(() => {
// 创建一个echarts实例,返回echarts实例
@@ -203,7 +207,7 @@ function ExperimentChart({ chartData, style }: ExperimentChartProps) {
// myChart.dispose() 销毁实例
chart.dispose();
};
- }, []);
+ }, [options]);
return (
diff --git a/react-ui/src/pages/Workspace/components/QuickStart/index.tsx b/react-ui/src/pages/Workspace/components/QuickStart/index.tsx
index 621efead..d155dae9 100644
--- a/react-ui/src/pages/Workspace/components/QuickStart/index.tsx
+++ b/react-ui/src/pages/Workspace/components/QuickStart/index.tsx
@@ -92,7 +92,7 @@ function QuickStart() {
buttonTop={20}
x={left + 4 * (192 + space) + 60 + space}
y={263}
- onClick={() => navigate('/modelDeployment')}
+ onClick={() => navigate('/dataset/modelDeployment')}
/>
-
复杂智能软件
+
自主实验平台
- 复杂智能软件平台构建一套完整的版本迭代升级机制、开发与运行态版本依赖关系分析,以及整合开发部署和持续优化的一体化流程,涵盖数据管理、模型建模、服务开发和系统运行等关键环节,以实现高效、稳定的软件生命周期管理。
+ 材料领域的自主实验系统是一种用于材料研究和开发的技术平台,它旨在提供实验数据收集、分析和可视化等功能,
+ 以支持材料工程师、科学家和研究人员在材料设计、性能评估和工艺优化方面的工作
- {/*
![]()
{
content={'很抱歉,您访问的正在开发中,\n请耐心等待。'}
hasFooter={true}
buttonTitle="返回首页"
- onRefresh={() => navigate('/')}
+ onButtonClick={() => navigate('/')}
>
);
};
diff --git a/react-ui/src/services/hyperParameter/index.js b/react-ui/src/services/hyperParameter/index.js
new file mode 100644
index 00000000..96ea52e1
--- /dev/null
+++ b/react-ui/src/services/hyperParameter/index.js
@@ -0,0 +1,100 @@
+/*
+ * @Author: 赵伟
+ * @Date: 2024-11-18 10:18:27
+ * @Description: 超参数自动寻优请求
+ */
+
+import { request } from '@umijs/max';
+
+
+// 分页查询超参数自动寻优
+export function getRayListReq(params) {
+ return request(`/api/mmp/ray`, {
+ method: 'GET',
+ params,
+ });
+}
+
+// 查询超参数自动寻优详情
+export function getRayInfoReq(params) {
+ return request(`/api/mmp/ray/getRayDetail`, {
+ method: 'GET',
+ params,
+ });
+}
+
+// 新增超参数自动寻优
+export function addRayReq(data) {
+ return request(`/api/mmp/ray`, {
+ method: 'POST',
+ data,
+ });
+}
+
+// 编辑超参数自动寻优
+export function updateRayReq(data) {
+ return request(`/api/mmp/ray`, {
+ method: 'PUT',
+ data,
+ });
+}
+
+// 删除超参数自动寻优
+export function deleteRayReq(id) {
+ return request(`/api/mmp/ray/${id}`, {
+ method: 'DELETE',
+ });
+}
+
+// 运行超参数自动寻优
+export function runRayReq(id) {
+ return request(`/api/mmp/ray/run/${id}`, {
+ method: 'POST',
+ });
+}
+
+// ----------------------- 实验实例 -----------------------
+// 获取实验实例列表
+export function getRayInsListReq(params) {
+ return request(`/api/mmp/rayIns`, {
+ method: 'GET',
+ params,
+ });
+}
+
+// 查询实验实例详情
+export function getRayInsReq(id) {
+ return request(`/api/mmp/rayIns/${id}`, {
+ method: 'GET',
+ });
+}
+
+// 停止实验实例
+export function stopRayInsReq(id) {
+ return request(`/api/mmp/rayIns/${id}`, {
+ method: 'PUT',
+ });
+}
+
+// 删除实验实例
+export function deleteRayInsReq(id) {
+ return request(`/api/mmp/rayIns/${id}`, {
+ method: 'DELETE',
+ });
+}
+
+// 批量删除实验实例
+export function batchDeleteRayInsReq(data) {
+ return request(`/api/mmp/rayIns/batchDelete`, {
+ method: 'DELETE',
+ data
+ });
+}
+
+// 获取当前实验的指标对比地址
+export function getExpMetricsReq(data) {
+ return request(`/api/mmp/rayIns/getExpMetrics`, {
+ method: 'POST',
+ data
+ });
+}
diff --git a/react-ui/src/state/computingResourceStore.ts b/react-ui/src/state/computingResourceStore.ts
deleted file mode 100644
index 23d1455b..00000000
--- a/react-ui/src/state/computingResourceStore.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { ComputingResource } from '@/types';
-import { proxy } from 'umi';
-
-type ComputingResourceStore = {
- computingResource: ComputingResource[];
-};
-
-const state = proxy
({
- computingResource: [],
-});
-
-export const setComputingResource = (computingResource: ComputingResource[]) => {
- state.computingResource = computingResource;
-};
-
-export default state;
diff --git a/react-ui/src/stories/BasicInfo.stories.tsx b/react-ui/src/stories/BasicInfo.stories.tsx
new file mode 100644
index 00000000..f669104e
--- /dev/null
+++ b/react-ui/src/stories/BasicInfo.stories.tsx
@@ -0,0 +1,104 @@
+import BasicInfo from '@/components/BasicInfo';
+import { formatDate } from '@/utils/date';
+import { formatList } from '@/utils/format';
+import type { Meta, StoryObj } from '@storybook/react';
+import { Button } from 'antd';
+
+// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
+const meta = {
+ title: 'Components/BasicInfo 基本信息',
+ component: BasicInfo,
+ parameters: {
+ // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
+ // layout: 'centered',
+ },
+ // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
+ tags: ['autodocs'],
+ // More on argTypes: https://storybook.js.org/docs/api/argtypes
+ argTypes: {
+ // backgroundColor: { control: 'color' },
+ },
+ // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
+ // args: { onClick: fn() },
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
+/** 一行两列 */
+export const Primary: Story = {
+ args: {
+ datas: [
+ { label: '服务名称', value: '手写体识别' },
+ {
+ label: '无数据',
+ value: '',
+ },
+ {
+ label: '外部链接',
+ value: 'https://www.baidu.com/',
+ format: (value: string) => {
+ return {
+ value: '百度',
+ url: value,
+ };
+ },
+ },
+ {
+ label: '内部链接',
+ value: 'https://www.baidu.com/',
+ format: () => {
+ return {
+ value: '实验',
+ link: '/pipeline/experiment/instance/1/1',
+ };
+ },
+ },
+ { label: '日期', value: new Date(), format: formatDate },
+ { label: '数组', value: ['a', 'b', 'c'], format: formatList },
+ {
+ label: '带省略号',
+ value: '这是一个很长的字符串这是一个很长的字符串这是一个很长的字符串这是一个很长的字符串',
+ },
+ {
+ label: '多行',
+ value: [
+ {
+ label: '服务名称',
+ value: '手写体识别',
+ },
+ {
+ label: '服务名称',
+ value: '人脸识别',
+ },
+ ],
+ format: (value: any) =>
+ value.map((item: any) => ({
+ value: item.label + ':' + item.value,
+ })),
+ },
+ {
+ label: '自定义组件',
+ value: (
+
+ ),
+ },
+ ],
+ labelWidth: 80,
+ labelAlign: 'justify',
+ threeColumns: false,
+ labelEllipsis: true,
+ },
+};
+
+/** 一行三列 */
+export const ThreeColumn: Story = {
+ args: {
+ ...Primary.args,
+ labelAlign: 'start',
+ threeColumns: true,
+ },
+};
diff --git a/react-ui/src/stories/BasicTableInfo.stories.tsx b/react-ui/src/stories/BasicTableInfo.stories.tsx
new file mode 100644
index 00000000..7f9e9e84
--- /dev/null
+++ b/react-ui/src/stories/BasicTableInfo.stories.tsx
@@ -0,0 +1,75 @@
+import BasicTableInfo from '@/components/BasicTableInfo';
+import type { Meta, StoryObj } from '@storybook/react';
+import * as BasicInfoStories from './BasicInfo.stories';
+
+// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
+const meta = {
+ title: 'Components/BasicTableInfo 基本信息表格版',
+ component: BasicTableInfo,
+ parameters: {
+ // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
+ // layout: 'centered',
+ },
+ // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
+ tags: ['autodocs'],
+ // More on argTypes: https://storybook.js.org/docs/api/argtypes
+ argTypes: {
+ datas: {
+ description: '基础信息',
+ table: {
+ type: { summary: 'BasicInfoData[]' },
+ },
+ type: {
+ required: true,
+ name: 'array',
+ value: {
+ name: 'object',
+ value: {},
+ },
+ },
+ },
+ labelWidth: {
+ description: '标题宽度',
+ type: {
+ required: true,
+ name: 'number',
+ },
+ },
+ labelEllipsis: {
+ description: '标题是否显示省略号',
+ table: {
+ type: { summary: 'boolean' },
+ defaultValue: { summary: 'true' },
+ },
+ control: 'boolean',
+ },
+ className: {
+ description: '自定义类名',
+ table: {
+ type: { summary: 'string' },
+ },
+ control: 'text',
+ },
+ style: {
+ description: '自定义样式',
+ table: {
+ type: { summary: 'ReactCSSProperties' },
+ },
+ control: 'object',
+ },
+ },
+ // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
+ // args: { onClick: fn() },
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
+export const Primary: Story = {
+ args: {
+ datas: BasicInfoStories.Primary.args.datas,
+ labelWidth: 100,
+ labelEllipsis: true,
+ },
+};
diff --git a/react-ui/src/stories/CodeSelect.stories.tsx b/react-ui/src/stories/CodeSelect.stories.tsx
new file mode 100644
index 00000000..b2246700
--- /dev/null
+++ b/react-ui/src/stories/CodeSelect.stories.tsx
@@ -0,0 +1,89 @@
+import CodeSelect, { type ParameterInputValue } from '@/components/CodeSelect';
+import { action } from '@storybook/addon-actions';
+import { useArgs } from '@storybook/preview-api';
+import type { Meta, StoryObj } from '@storybook/react';
+import { fn } from '@storybook/test';
+import { Button, Col, Form, Row } from 'antd';
+import { http, HttpResponse } from 'msw';
+import { codeListData } from './mockData';
+
+// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
+const meta = {
+ title: 'Components/CodeSelect 代码配置选择器',
+ component: CodeSelect,
+ parameters: {
+ // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
+ // layout: 'centered',
+ msw: {
+ handlers: [
+ http.get('/api/mmp/codeConfig', () => {
+ return HttpResponse.json(codeListData);
+ }),
+ ],
+ },
+ },
+ // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
+ tags: ['autodocs'],
+ // More on argTypes: https://storybook.js.org/docs/api/argtypes
+ argTypes: {
+ // backgroundColor: { control: 'color' },
+ },
+ // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
+ args: { onChange: fn() },
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
+export const Primary: Story = {
+ args: {
+ canInput: false,
+ textArea: false,
+ size: 'large',
+ placeholder: '请选择代码配置',
+ style: { width: 400 },
+ },
+ render: function Render(args) {
+ const [{ value }, updateArgs] = useArgs();
+ function handleChange(value?: ParameterInputValue) {
+ updateArgs({ value: value });
+ args.onChange?.(value);
+ }
+
+ return ;
+ },
+};
+
+export const InForm: Story = {
+ render: ({ onChange }) => {
+ return (
+
+ );
+ },
+};
diff --git a/react-ui/src/stories/CodeSelectorModal.stories.tsx b/react-ui/src/stories/CodeSelectorModal.stories.tsx
new file mode 100644
index 00000000..9faae0ae
--- /dev/null
+++ b/react-ui/src/stories/CodeSelectorModal.stories.tsx
@@ -0,0 +1,59 @@
+import CodeSelectorModal from '@/components/CodeSelectorModal';
+import { openAntdModal } from '@/utils/modal';
+import type { Meta, StoryObj } from '@storybook/react';
+import { fn } from '@storybook/test';
+import { Button } from 'antd';
+import { http, HttpResponse } from 'msw';
+import { codeListData } from './mockData';
+
+// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
+const meta = {
+ title: 'Components/CodeSelectorModal 代码选择对话框',
+ component: CodeSelectorModal,
+ parameters: {
+ // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
+ layout: 'centered',
+ msw: {
+ handlers: [
+ http.get('/api/mmp/codeConfig', () => {
+ return HttpResponse.json(codeListData);
+ }),
+ ],
+ },
+ },
+ // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
+ tags: ['autodocs'],
+ // More on argTypes: https://storybook.js.org/docs/api/argtypes
+ argTypes: {
+ // backgroundColor: { control: 'color' },
+ open: {
+ description: '对话框是否可见',
+ },
+ },
+ // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
+ args: { onCancel: fn(), onOk: fn() },
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
+
+export const Primary: Story = {
+ render: function Render(args) {
+ const handleClick = () => {
+ const { close } = openAntdModal(CodeSelectorModal, {
+ onOk: (res) => {
+ const { onOk } = args;
+ onOk?.(res);
+ close();
+ },
+ });
+ };
+ return (
+
+ );
+ },
+};
diff --git a/react-ui/src/stories/Config.stories.tsx b/react-ui/src/stories/Config.stories.tsx
new file mode 100644
index 00000000..5c2a42ce
--- /dev/null
+++ b/react-ui/src/stories/Config.stories.tsx
@@ -0,0 +1,37 @@
+import ConfigInfo from '@/components/ConfigInfo';
+import type { Meta, StoryObj } from '@storybook/react';
+import * as BasicInfoStories from './BasicInfo.stories';
+
+// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
+const meta = {
+ title: 'Components/ConfigInfo 配置信息',
+ component: ConfigInfo,
+ parameters: {
+ // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
+ // layout: 'centered',
+ },
+ // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
+ tags: ['autodocs'],
+ // More on argTypes: https://storybook.js.org/docs/api/argtypes
+ argTypes: {
+ // backgroundColor: { control: 'color' },
+ },
+ // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
+ // args: { onClick: fn() },
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
+export const Primary: Story = {
+ args: {
+ title: '基本信息',
+ datas: BasicInfoStories.Primary.args.datas,
+ labelAlign: 'start',
+ labelEllipsis: true,
+ threeColumns: true,
+ labelWidth: 80,
+ children: I am a child element
,
+ },
+};
diff --git a/react-ui/src/stories/FormInfo.stories.tsx b/react-ui/src/stories/FormInfo.stories.tsx
new file mode 100644
index 00000000..abdf7b5e
--- /dev/null
+++ b/react-ui/src/stories/FormInfo.stories.tsx
@@ -0,0 +1,141 @@
+import FormInfo from '@/components/FormInfo';
+import type { Meta, StoryObj } from '@storybook/react';
+import { Form, Input, Select, Typography } from 'antd';
+
+// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
+const meta = {
+ title: 'Components/FormInfo 表单信息',
+ component: FormInfo,
+ parameters: {
+ // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
+ // layout: 'centered',
+ },
+ // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
+ tags: ['autodocs'],
+ // More on argTypes: https://storybook.js.org/docs/api/argtypes
+ argTypes: {
+ // backgroundColor: { control: 'color' },
+ },
+ // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
+ // args: { onClick: fn() },
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
+export const Primary: Story = {
+ args: {
+ style: { width: '200px' },
+ value:
+ '超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本',
+ },
+};
+
+export const InForm: Story = {
+ render: () => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ },
+};
diff --git a/react-ui/src/stories/FullScreenFrame.stories.tsx b/react-ui/src/stories/FullScreenFrame.stories.tsx
new file mode 100644
index 00000000..74dae50a
--- /dev/null
+++ b/react-ui/src/stories/FullScreenFrame.stories.tsx
@@ -0,0 +1,32 @@
+import FullScreenFrame from '@/components/FullScreenFrame';
+import type { Meta, StoryObj } from '@storybook/react';
+import { fn } from '@storybook/test';
+
+// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
+const meta = {
+ title: 'Components/FullScreenFrame 全屏iframe',
+ component: FullScreenFrame,
+ parameters: {
+ // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
+ // layout: 'centered',
+ },
+ // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
+ tags: ['autodocs'],
+ // More on argTypes: https://storybook.js.org/docs/api/argtypes
+ argTypes: {
+ // backgroundColor: { control: 'color' },
+ },
+ // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
+ args: { onLoad: fn(), onError: fn() },
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
+export const Primary: Story = {
+ args: {
+ url: 'https://www.hao123.com/',
+ style: { height: '500px' },
+ },
+};
diff --git a/react-ui/src/stories/IFramePage.stories.tsx b/react-ui/src/stories/IFramePage.stories.tsx
new file mode 100644
index 00000000..d5c6725b
--- /dev/null
+++ b/react-ui/src/stories/IFramePage.stories.tsx
@@ -0,0 +1,31 @@
+import IFramePage, { IframePageType } from '@/components/IFramePage';
+import type { Meta, StoryObj } from '@storybook/react';
+
+// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
+const meta = {
+ title: 'Components/IFramePage iframe页面',
+ component: IFramePage,
+ parameters: {
+ // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
+ // layout: 'centered',
+ },
+ // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
+ tags: ['autodocs'],
+ // More on argTypes: https://storybook.js.org/docs/api/argtypes
+ argTypes: {
+ type: { control: 'select', options: Object.values(IframePageType) },
+ },
+ // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
+ // args: { onClick: fn() },
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
+export const Primary: Story = {
+ args: {
+ type: IframePageType.GitLink,
+ style: { height: '500px' },
+ },
+};
diff --git a/react-ui/src/stories/InfoGroup.stories.tsx b/react-ui/src/stories/InfoGroup.stories.tsx
new file mode 100644
index 00000000..3fe86b80
--- /dev/null
+++ b/react-ui/src/stories/InfoGroup.stories.tsx
@@ -0,0 +1,31 @@
+import InfoGroup from '@/components/InfoGroup';
+import type { Meta, StoryObj } from '@storybook/react';
+
+// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
+const meta = {
+ title: 'Components/InfoGroup 信息分组',
+ component: InfoGroup,
+ parameters: {
+ // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
+ // layout: 'centered',
+ },
+ // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
+ tags: ['autodocs'],
+ // More on argTypes: https://storybook.js.org/docs/api/argtypes
+ argTypes: {
+ // backgroundColor: { control: 'color' },
+ },
+ // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
+ // args: { onClick: fn() },
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
+export const Primary: Story = {
+ args: {
+ title: '基本信息',
+ children: I am a child element
,
+ },
+};
diff --git a/react-ui/src/stories/KFEmpty.stories.tsx b/react-ui/src/stories/KFEmpty.stories.tsx
new file mode 100644
index 00000000..369695be
--- /dev/null
+++ b/react-ui/src/stories/KFEmpty.stories.tsx
@@ -0,0 +1,53 @@
+import KFEmpty, { EmptyType } from '@/components/KFEmpty';
+import type { Meta, StoryObj } from '@storybook/react';
+import { fn } from '@storybook/test';
+// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
+const meta = {
+ title: 'Components/KFEmpty 空状态',
+ component: KFEmpty,
+ parameters: {
+ // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
+ // layout: 'centered',
+ },
+ // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
+ tags: ['autodocs'],
+ // More on argTypes: https://storybook.js.org/docs/api/argtypes
+ argTypes: {
+ type: { control: 'radio', options: Object.values(EmptyType) },
+ },
+ // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
+ args: { onButtonClick: fn() },
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
+export const Developing: Story = {
+ args: {
+ type: EmptyType.Developing,
+ title: '敬请期待~',
+ content: '很抱歉,您访问的正在开发中,\n请耐心等待。',
+ hasFooter: true,
+ buttonTitle: '返回首页',
+ },
+};
+
+export const NoData: Story = {
+ args: {
+ type: EmptyType.NoData,
+ title: '暂无数据',
+ content: '很抱歉,没有搜索到您想要的内容\n建议刷新试试',
+ hasFooter: true,
+ },
+};
+
+export const NotFound: Story = {
+ args: {
+ type: EmptyType.NotFound,
+ title: '404',
+ content: '很抱歉,您访问的页面地址有误,\n或者该页面不存在。',
+ hasFooter: true,
+ buttonTitle: '返回首页',
+ },
+};
diff --git a/react-ui/src/stories/KFIcon.stories.tsx b/react-ui/src/stories/KFIcon.stories.tsx
new file mode 100644
index 00000000..7367b302
--- /dev/null
+++ b/react-ui/src/stories/KFIcon.stories.tsx
@@ -0,0 +1,41 @@
+import KFIcon from '@/components/KFIcon';
+import type { Meta, StoryObj } from '@storybook/react';
+import { Button } from 'antd';
+// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
+const meta = {
+ title: 'Components/KFIcon 图标',
+ component: KFIcon,
+ parameters: {
+ // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
+ // layout: 'centered',
+ },
+ // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
+ tags: ['autodocs'],
+ // More on argTypes: https://storybook.js.org/docs/api/argtypes
+ argTypes: {},
+ // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
+ args: {},
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
+export const Primary: Story = {
+ args: {
+ type: 'icon-xiazai',
+ },
+};
+
+export const InButton: Story = {
+ args: {
+ type: 'icon-xiazai',
+ },
+ render: function Render(args) {
+ return (
+ } type="primary">
+ 下载
+
+ );
+ },
+};
diff --git a/react-ui/src/stories/KFModal.mdx b/react-ui/src/stories/KFModal.mdx
new file mode 100644
index 00000000..8bd711bf
--- /dev/null
+++ b/react-ui/src/stories/KFModal.mdx
@@ -0,0 +1,45 @@
+import { Meta, Title, Subtitle, Description, Primary, Controls, Stories } from '@storybook/blocks';
+import * as KFModalStories from "./KFModal.stories"
+
+
+
+
+
+
+
+## Usage
+
+为了风格统一,应用中的其它 Modal 应该使用 **KFModal** 进行封装,例如 **CodeSelectorModal**
+
+```ts
+export interface CodeSelectorModalProps extends Omit {
+ onOk?: (params: CodeConfigData | undefined) => void;
+}
+
+function CodeSelectorModal({ onOk, ...rest }: CodeSelectorModalProps) {
+ return (
+
+ children
+
+ );
+}
+
+export default CodeSelectorModal;
+
+```
+
+## Primary
+
+
+
+
+
+
+
diff --git a/react-ui/src/stories/KFModal.stories.tsx b/react-ui/src/stories/KFModal.stories.tsx
new file mode 100644
index 00000000..054634f2
--- /dev/null
+++ b/react-ui/src/stories/KFModal.stories.tsx
@@ -0,0 +1,101 @@
+import CreateExperiment from '@/assets/img/create-experiment.png';
+import KFModal from '@/components/KFModal';
+import { openAntdModal } from '@/utils/modal';
+import { useArgs } from '@storybook/preview-api';
+import type { Meta, StoryObj } from '@storybook/react';
+import { expect, fn, screen, userEvent, within } from '@storybook/test';
+import { Button } from 'antd';
+
+// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
+const meta = {
+ title: 'Components/KFModal 对话框',
+ component: KFModal,
+ parameters: {
+ // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
+ layout: 'centered',
+ },
+ // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
+ tags: ['!autodocs'],
+ // More on argTypes: https://storybook.js.org/docs/api/argtypes
+ argTypes: {
+ // backgroundColor: { control: 'color' },
+ title: {
+ description: '标题',
+ },
+ open: {
+ description: '对话框是否可见',
+ },
+ children: {
+ description: '子元素',
+ type: 'string',
+ },
+ },
+ // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
+ args: { onCancel: fn(), onOk: fn() },
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
+/** 作为子组件时,通过 `open` 属性打开或关闭 */
+export const Primary: Story = {
+ args: {
+ title: '创建实验',
+ image: CreateExperiment,
+ open: false,
+ children: '这是一个模态框',
+ },
+ render: function Render({ onOk, onCancel, ...args }) {
+ const [{ open }, updateArgs] = useArgs();
+ function onClick() {
+ updateArgs({ open: true });
+ }
+ function handleOk() {
+ updateArgs({ open: false });
+ onOk?.();
+ }
+
+ function handleCancel() {
+ updateArgs({ open: false });
+ onCancel?.();
+ }
+
+ return (
+ <>
+
+
+ >
+ );
+ },
+ play: async ({ canvasElement }) => {
+ const canvas = within(canvasElement);
+ await userEvent.click(canvas.getByRole('button', { name: /打开 KFModal/i }));
+ const dialog = await screen.findByRole('dialog');
+ await expect(dialog).toBeInTheDocument();
+ },
+};
+
+/** 推荐通过 `openAntdModal` 函数打开 */
+export const OpenByFunction: Story = {
+ name: '通过函数的方式打开',
+ render: function Render() {
+ const handleClick = () => {
+ const { close } = openAntdModal(KFModal, {
+ title: '创建实验',
+ image: CreateExperiment,
+ children: '这是一个模态框',
+ onOk: () => {
+ close();
+ },
+ });
+ };
+ return (
+
+ );
+ },
+};
diff --git a/react-ui/src/stories/KFRadio.stories.tsx b/react-ui/src/stories/KFRadio.stories.tsx
new file mode 100644
index 00000000..14bfd07c
--- /dev/null
+++ b/react-ui/src/stories/KFRadio.stories.tsx
@@ -0,0 +1,52 @@
+import KFIcon from '@/components/KFIcon';
+import KFRadio from '@/components/KFRadio';
+import { useArgs } from '@storybook/preview-api';
+import type { Meta, StoryObj } from '@storybook/react';
+
+// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
+const meta = {
+ title: 'Components/KFRadio 单选框',
+ component: KFRadio,
+ parameters: {
+ // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
+ // layout: 'centered',
+ },
+ // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
+ tags: ['autodocs'],
+ // More on argTypes: https://storybook.js.org/docs/api/argtypes
+ argTypes: {
+ // backgroundColor: { control: 'color' },
+ },
+ // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
+ // args: { onClick: fn() },
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
+export const Primary: Story = {
+ args: {
+ items: [
+ {
+ title: '英伟达GPU',
+ value: 'GPU',
+ icon: ,
+ },
+ {
+ title: '昇腾NPU',
+ value: 'NPU',
+ icon: ,
+ },
+ ],
+ value: 'GPU',
+ },
+ render: function Render(args) {
+ const [{ value }, updateArgs] = useArgs();
+ function onChange(value: string) {
+ updateArgs({ value: value });
+ }
+
+ return ;
+ },
+};
diff --git a/react-ui/src/stories/KFSpin.stories.tsx b/react-ui/src/stories/KFSpin.stories.tsx
new file mode 100644
index 00000000..75f9a872
--- /dev/null
+++ b/react-ui/src/stories/KFSpin.stories.tsx
@@ -0,0 +1,35 @@
+import KFSpin from '@/components/KFSpin';
+import type { Meta, StoryObj } from '@storybook/react';
+
+// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
+const meta = {
+ title: 'Components/KFSpin 加载器',
+ component: KFSpin,
+ parameters: {
+ // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
+ // layout: 'centered',
+ },
+ // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
+ tags: ['autodocs'],
+ // More on argTypes: https://storybook.js.org/docs/api/argtypes
+ argTypes: {
+ // backgroundColor: { control: 'color' },
+ },
+ decorators: [
+ (Story) => (
+
+
+
+ ),
+ ],
+ // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
+ // args: { onClick: fn() },
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
+export const Primary: Story = {
+ args: {},
+};
diff --git a/react-ui/src/stories/MenuIconSelector.stories.tsx b/react-ui/src/stories/MenuIconSelector.stories.tsx
new file mode 100644
index 00000000..d89e7a6d
--- /dev/null
+++ b/react-ui/src/stories/MenuIconSelector.stories.tsx
@@ -0,0 +1,67 @@
+import MenuIconSelector from '@/components/MenuIconSelector';
+import { useArgs } from '@storybook/preview-api';
+import type { Meta, StoryObj } from '@storybook/react';
+import { fn } from '@storybook/test';
+import { Button } from 'antd';
+
+// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
+const meta = {
+ title: 'Components/MenuIconSelector 菜单图标选择器',
+ component: MenuIconSelector,
+ parameters: {
+ // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
+ layout: 'centered',
+ },
+ // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
+ tags: ['autodocs'],
+ // More on argTypes: https://storybook.js.org/docs/api/argtypes
+ argTypes: {
+ open: {
+ control: 'boolean',
+ description: '对话框是否可见',
+ },
+ },
+ // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
+ args: { onCancel: fn(), onOk: fn() },
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
+export const Primary: Story = {
+ args: {
+ selectedIcon: 'manual-icon',
+ open: false,
+ },
+ render: function Render({ onOk, onCancel, ...args }) {
+ const [{ open, selectedIcon }, updateArgs] = useArgs();
+ function onClick() {
+ updateArgs({ open: true });
+ }
+ function handleOk(value: string) {
+ updateArgs({ selectedIcon: value, open: false });
+ onOk?.(value);
+ }
+
+ function handleCancel() {
+ updateArgs({ open: false });
+ onCancel?.();
+ }
+
+ return (
+ <>
+
+
+ >
+ );
+ },
+};
diff --git a/react-ui/src/stories/PageTitle.stories.tsx b/react-ui/src/stories/PageTitle.stories.tsx
new file mode 100644
index 00000000..b8eb31c7
--- /dev/null
+++ b/react-ui/src/stories/PageTitle.stories.tsx
@@ -0,0 +1,30 @@
+import PageTitle from '@/components/PageTitle';
+import type { Meta, StoryObj } from '@storybook/react';
+
+// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
+const meta = {
+ title: 'Components/PageTitle 页面标题',
+ component: PageTitle,
+ parameters: {
+ // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
+ // layout: 'centered',
+ },
+ // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
+ tags: ['autodocs'],
+ // More on argTypes: https://storybook.js.org/docs/api/argtypes
+ argTypes: {
+ // backgroundColor: { control: 'color' },
+ },
+ // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
+ // args: { onClick: fn() },
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
+export const Primary: Story = {
+ args: {
+ title: '数据集列表',
+ },
+};
diff --git a/react-ui/src/stories/ParameterInput.stories.tsx b/react-ui/src/stories/ParameterInput.stories.tsx
new file mode 100644
index 00000000..3ca116c8
--- /dev/null
+++ b/react-ui/src/stories/ParameterInput.stories.tsx
@@ -0,0 +1,92 @@
+import ParameterInput, { ParameterInputValue } from '@/components/ParameterInput';
+import { action } from '@storybook/addon-actions';
+import type { Meta, StoryObj } from '@storybook/react';
+import { fn } from '@storybook/test';
+import { Button } from 'antd';
+import { useState } from 'react';
+
+// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
+const meta = {
+ title: 'Components/ParameterInput 参数输入框',
+ component: ParameterInput,
+ parameters: {
+ // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
+ // layout: 'centered',
+ },
+ // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
+ tags: ['autodocs'],
+ // More on argTypes: https://storybook.js.org/docs/api/argtypes
+ argTypes: {
+ // backgroundColor: { control: 'color' },
+ },
+ // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
+ args: { onChange: fn() },
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
+export const Input: Story = {
+ args: {
+ placeholder: '请输入工作目录',
+ style: { width: 300 },
+ canInput: true,
+ textArea: false,
+ allowClear: true,
+ size: 'large',
+ },
+};
+
+export const Select: Story = {
+ args: {
+ placeholder: '请输入工作目录',
+ style: { width: 300 },
+ canInput: true,
+ size: 'large',
+ },
+ render: function Render(args) {
+ const [value, setValue] = useState('');
+
+ const onClick = () => {
+ const value = {
+ value: 'storybook',
+ showValue: 'storybook',
+ fromSelect: true,
+ otherValue: 'others',
+ };
+ setValue(value);
+ action('onChange')(value);
+ };
+ return (
+ <>
+ {
+ setValue(value);
+ action('onChange')(value);
+ }}
+ >
+
+ >
+ );
+ },
+};
+
+export const Disabled: Story = {
+ args: {
+ placeholder: '请输入工作目录',
+ style: { width: 300 },
+ value: {
+ value: 'storybook',
+ showValue: 'storybook',
+ fromSelect: true,
+ },
+ canInput: true,
+ size: 'large',
+ disabled: true,
+ },
+};
diff --git a/react-ui/src/stories/ParameterSelect.stories.tsx b/react-ui/src/stories/ParameterSelect.stories.tsx
new file mode 100644
index 00000000..d6399d5d
--- /dev/null
+++ b/react-ui/src/stories/ParameterSelect.stories.tsx
@@ -0,0 +1,166 @@
+import ParameterSelect, { type ParameterSelectObject } from '@/components/ParameterSelect';
+import { action } from '@storybook/addon-actions';
+import { useArgs } from '@storybook/preview-api';
+import type { Meta, StoryObj } from '@storybook/react';
+import { fn } from '@storybook/test';
+import { Button, Col, Form, Row } from 'antd';
+import { http, HttpResponse } from 'msw';
+import { computeResourceData, datasetListData, modelListData, serviceListData } from './mockData';
+
+// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
+const meta = {
+ title: 'Components/ParameterSelect 参数选择器',
+ component: ParameterSelect,
+ parameters: {
+ // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
+ // layout: 'centered',
+ msw: {
+ handlers: [
+ http.get('/api/mmp/newdataset/queryDatasets', () => {
+ return HttpResponse.json(datasetListData);
+ }),
+ http.get('/api/mmp/newmodel/queryModels', () => {
+ return HttpResponse.json(modelListData);
+ }),
+ http.get('/api/mmp/service', () => {
+ return HttpResponse.json(serviceListData);
+ }),
+ http.get('/api/mmp/computingResource', () => {
+ return HttpResponse.json(computeResourceData);
+ }),
+ ],
+ },
+ },
+ // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
+ tags: ['autodocs'],
+ // More on argTypes: https://storybook.js.org/docs/api/argtypes
+ // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
+ args: { onChange: fn() },
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
+export const Primary: Story = {
+ args: {
+ placeholder: '请选择',
+ dataType: 'dataset',
+ style: { width: 400 },
+ size: 'large',
+ },
+ render: function Render(args) {
+ const [{ value }, updateArgs] = useArgs();
+ function handleChange(value?: string | ParameterSelectObject) {
+ updateArgs({ value: value });
+ args.onChange?.(value);
+ }
+
+ return ;
+ },
+};
+
+/** 值可以是一个对象,典型的是流水线节点对象 **PipelineNodeModelParameter** */
+export const Object: Story = {
+ args: {
+ placeholder: '请选择',
+ dataType: 'dataset',
+ style: { width: 400 },
+ size: 'large',
+ value: {
+ value: undefined,
+ },
+ },
+ render: function Render(args) {
+ const [{ value }, updateArgs] = useArgs();
+ function handleChange(value?: string | ParameterSelectObject) {
+ updateArgs({ value: value });
+ args.onChange?.(value);
+ }
+
+ return ;
+ },
+};
+
+export const InForm: Story = {
+ args: {
+ dataType: 'dataset',
+ },
+ render: ({ onChange }) => {
+ return (
+
+ );
+ },
+};
diff --git a/react-ui/src/stories/ResourceSelect.stories.tsx b/react-ui/src/stories/ResourceSelect.stories.tsx
new file mode 100644
index 00000000..3eafc0f7
--- /dev/null
+++ b/react-ui/src/stories/ResourceSelect.stories.tsx
@@ -0,0 +1,161 @@
+import ResourceSelect, {
+ ParameterInputValue,
+ requiredValidator,
+ ResourceSelectorType,
+} from '@/components/ResourceSelect';
+import { action } from '@storybook/addon-actions';
+import { useArgs } from '@storybook/preview-api';
+import type { Meta, StoryObj } from '@storybook/react';
+import { fn } from '@storybook/test';
+import { Button, Col, Form, Row } from 'antd';
+import { http, HttpResponse } from 'msw';
+import {
+ datasetDetailData,
+ datasetListData,
+ datasetVersionData,
+ mirrorListData,
+ mirrorVerionData,
+ modelDetailData,
+ modelListData,
+ modelVersionData,
+} from './mockData';
+
+// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
+const meta = {
+ title: 'Components/ResourceSelect 资源选择器',
+ component: ResourceSelect,
+ parameters: {
+ // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
+ // layout: 'centered',
+ msw: {
+ handlers: [
+ http.get('/api/mmp/newdataset/queryDatasets', () => {
+ return HttpResponse.json(datasetListData);
+ }),
+ http.get('/api/mmp/newdataset/getVersionList', () => {
+ return HttpResponse.json(datasetVersionData);
+ }),
+ http.get('/api/mmp/newdataset/getDatasetDetail', () => {
+ return HttpResponse.json(datasetDetailData);
+ }),
+ http.get('/api/mmp/newmodel/queryModels', () => {
+ return HttpResponse.json(modelListData);
+ }),
+ http.get('/api/mmp/newmodel/getVersionList', () => {
+ return HttpResponse.json(modelVersionData);
+ }),
+ http.get('/api/mmp/newmodel/getModelDetail', () => {
+ return HttpResponse.json(modelDetailData);
+ }),
+ http.get('/api/mmp/image', () => {
+ return HttpResponse.json(mirrorListData);
+ }),
+ http.get('/api/mmp/imageVersion', () => {
+ return HttpResponse.json(mirrorVerionData);
+ }),
+ ],
+ },
+ },
+ // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
+ tags: ['autodocs'],
+ // More on argTypes: https://storybook.js.org/docs/api/argtypes
+ argTypes: {
+ type: { control: 'radio', options: Object.values(ResourceSelectorType) },
+ },
+ // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
+ args: { onChange: fn() },
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
+export const Primary: Story = {
+ args: {
+ type: ResourceSelectorType.Dataset,
+ canInput: false,
+ textArea: false,
+ size: 'large',
+ placeholder: '请选择数据集',
+ style: { width: 400 },
+ },
+ render: function Render(args) {
+ const [{ value }, updateArgs] = useArgs();
+ function handleChange(value?: ParameterInputValue) {
+ updateArgs({ value: value });
+ args.onChange?.(value);
+ }
+
+ return ;
+ },
+};
+
+export const InForm: Story = {
+ args: {
+ type: ResourceSelectorType.Dataset,
+ },
+ render: ({ onChange }) => {
+ return (
+
+ );
+ },
+};
diff --git a/react-ui/src/stories/ResourceSelectorModal.mdx b/react-ui/src/stories/ResourceSelectorModal.mdx
new file mode 100644
index 00000000..f7e0c569
--- /dev/null
+++ b/react-ui/src/stories/ResourceSelectorModal.mdx
@@ -0,0 +1,37 @@
+import { Meta, Title, Subtitle, Description, Primary, Controls, Stories } from '@storybook/blocks';
+import * as ResourceSelectorModalStories from "./ResourceSelectorModal.stories"
+import { StoryName } from "../../.storybook/blocks/StoryName"
+
+
+
+
+
+
+
+### Usage
+
+推荐通过 `openAntdModal` 函数打开 `ResourceSelectorModal`,打开 -> 处理 -> 关闭,整套代码在同一个地方
+
+```ts
+import { openAntdModal } from '@/utils/modal';
+import ResourceSelectorModal } from '@/components/ResourceSelectorModal';
+
+// 打开资源选择对话框
+const handleClick = () => {
+ const { close } = openAntdModal(ResourceSelectorModal, {
+ type: ResourceSelectorType.Dataset,
+ onOk: (res) => {
+ // 处理逻辑
+ close();
+ },
+});
+```
+
+### Primary
+
+
+
+
+
+
+
diff --git a/react-ui/src/stories/ResourceSelectorModal.stories.tsx b/react-ui/src/stories/ResourceSelectorModal.stories.tsx
new file mode 100644
index 00000000..b43a8e5b
--- /dev/null
+++ b/react-ui/src/stories/ResourceSelectorModal.stories.tsx
@@ -0,0 +1,145 @@
+import ResourceSelectorModal, { ResourceSelectorType } from '@/components/ResourceSelectorModal';
+import { openAntdModal } from '@/utils/modal';
+import type { Meta, StoryObj } from '@storybook/react';
+import { fn } from '@storybook/test';
+import { Button } from 'antd';
+import { http, HttpResponse } from 'msw';
+import {
+ datasetDetailData,
+ datasetListData,
+ datasetVersionData,
+ mirrorListData,
+ mirrorVerionData,
+ modelDetailData,
+ modelListData,
+ modelVersionData,
+} from './mockData';
+
+// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
+const meta = {
+ title: 'Components/ResourceSelectorModal 资源选择对话框',
+ component: ResourceSelectorModal,
+ parameters: {
+ // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
+ layout: 'centered',
+ msw: {
+ handlers: [
+ http.get('/api/mmp/newdataset/queryDatasets', () => {
+ return HttpResponse.json(datasetListData);
+ }),
+ http.get('/api/mmp/newdataset/getVersionList', () => {
+ return HttpResponse.json(datasetVersionData);
+ }),
+ http.get('/api/mmp/newdataset/getDatasetDetail', () => {
+ return HttpResponse.json(datasetDetailData);
+ }),
+ http.get('/api/mmp/newmodel/queryModels', () => {
+ return HttpResponse.json(modelListData);
+ }),
+ http.get('/api/mmp/newmodel/getVersionList', () => {
+ return HttpResponse.json(modelVersionData);
+ }),
+ http.get('/api/mmp/newmodel/getModelDetail', () => {
+ return HttpResponse.json(modelDetailData);
+ }),
+ http.get('/api/mmp/image', () => {
+ return HttpResponse.json(mirrorListData);
+ }),
+ http.get('/api/mmp/imageVersion', () => {
+ return HttpResponse.json(mirrorVerionData);
+ }),
+ ],
+ },
+ },
+ // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
+ tags: ['!autodocs'],
+ // More on argTypes: https://storybook.js.org/docs/api/argtypes
+ argTypes: {
+ // backgroundColor: { control: 'color' },
+ open: {
+ description: '对话框是否可见',
+ },
+ type: {
+ control: 'select',
+ options: Object.values(ResourceSelectorType),
+ },
+ },
+ // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
+ args: { onCancel: fn(), onOk: fn() },
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
+/** 选择数据集 */
+export const Dataset: Story = {
+ args: {
+ type: ResourceSelectorType.Dataset,
+ },
+ render: function Render(args) {
+ const handleClick = () => {
+ const { close } = openAntdModal(ResourceSelectorModal, {
+ type: args.type,
+ onOk: (res) => {
+ const { onOk } = args;
+ onOk?.(res);
+ close();
+ },
+ });
+ };
+ return (
+
+ );
+ },
+};
+
+/** 选择模型 */
+export const Model: Story = {
+ args: {
+ type: ResourceSelectorType.Model,
+ },
+ render: function Render(args) {
+ const handleClick = () => {
+ const { close } = openAntdModal(ResourceSelectorModal, {
+ type: args.type,
+ onOk: (res) => {
+ const { onOk } = args;
+ onOk?.(res);
+ close();
+ },
+ });
+ };
+ return (
+
+ );
+ },
+};
+
+/** 选择镜像 */
+export const Mirror: Story = {
+ args: {
+ type: ResourceSelectorType.Mirror,
+ },
+ render: function Render(args) {
+ const handleClick = () => {
+ const { close } = openAntdModal(ResourceSelectorModal, {
+ type: args.type,
+ onOk: (res) => {
+ const { onOk } = args;
+ onOk?.(res);
+ close();
+ },
+ });
+ };
+ return (
+
+ );
+ },
+};
diff --git a/react-ui/src/stories/SubAreaTitle.stories.tsx b/react-ui/src/stories/SubAreaTitle.stories.tsx
new file mode 100644
index 00000000..30506448
--- /dev/null
+++ b/react-ui/src/stories/SubAreaTitle.stories.tsx
@@ -0,0 +1,32 @@
+import MirrorBasic from '@/assets/img/mirror-basic.png';
+import SubAreaTitle from '@/components/SubAreaTitle';
+import type { Meta, StoryObj } from '@storybook/react';
+
+// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
+const meta = {
+ title: 'Components/SubAreaTitle 子区域标题',
+ component: SubAreaTitle,
+ parameters: {
+ // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
+ // layout: 'centered',
+ },
+ // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
+ tags: ['autodocs'],
+ // More on argTypes: https://storybook.js.org/docs/api/argtypes
+ argTypes: {
+ // backgroundColor: { control: 'color' },
+ },
+ // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
+ // args: { onClick: fn() },
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
+export const Primary: Story = {
+ args: {
+ title: '基本信息',
+ image: MirrorBasic,
+ },
+};
diff --git a/react-ui/src/stories/docs/Colors.mdx b/react-ui/src/stories/docs/Colors.mdx
new file mode 100644
index 00000000..c90bddc8
--- /dev/null
+++ b/react-ui/src/stories/docs/Colors.mdx
@@ -0,0 +1,77 @@
+
+import { Meta, ColorPalette, ColorItem } from '@storybook/blocks';
+
+
+
+# Colors
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/react-ui/src/stories/docs/Configure.mdx b/react-ui/src/stories/docs/Configure.mdx
new file mode 100644
index 00000000..7651e269
--- /dev/null
+++ b/react-ui/src/stories/docs/Configure.mdx
@@ -0,0 +1,364 @@
+import { Meta } from "@storybook/blocks";
+
+import Github from "./assets/github.svg";
+import Discord from "./assets/discord.svg";
+import Youtube from "./assets/youtube.svg";
+import Tutorials from "./assets/tutorials.svg";
+import Styling from "./assets/styling.png";
+import Context from "./assets/context.png";
+import Assets from "./assets/assets.png";
+import Docs from "./assets/docs.png";
+import Share from "./assets/share.png";
+import FigmaPlugin from "./assets/figma-plugin.png";
+import Testing from "./assets/testing.png";
+import Accessibility from "./assets/accessibility.png";
+import Theming from "./assets/theming.png";
+import AddonLibrary from "./assets/addon-library.png";
+
+export const RightArrow = () =>
+
+
+
+
+
+ # Configure your project
+
+ Because Storybook works separately from your app, you'll need to configure it for your specific stack and setup. Below, explore guides for configuring Storybook with popular frameworks and tools. If you get stuck, learn how you can ask for help from our community.
+
+
+
+

+
Add styling and CSS
+
Like with web applications, there are many ways to include CSS within Storybook. Learn more about setting up styling within Storybook.
+
Learn more
+
+
+

+
Provide context and mocking
+
Often when a story doesn't render, it's because your component is expecting a specific environment or context (like a theme provider) to be available.
+
Learn more
+
+
+

+
+
Load assets and resources
+
To link static files (like fonts) to your projects and stories, use the
+ `staticDirs` configuration option to specify folders to load when
+ starting Storybook.
+
Learn more
+
+
+
+
+
+
+ # Do more with Storybook
+
+ Now that you know the basics, let's explore other parts of Storybook that will improve your experience. This list is just to get you started. You can customise Storybook in many ways to fit your needs.
+
+
+
+
+
+

+
Autodocs
+
Auto-generate living,
+ interactive reference documentation from your components and stories.
+
Learn more
+
+
+

+
Publish to Chromatic
+
Publish your Storybook to review and collaborate with your entire team.
+
Learn more
+
+
+

+
Figma Plugin
+
Embed your stories into Figma to cross-reference the design and live
+ implementation in one place.
+
Learn more
+
+
+

+
Testing
+
Use stories to test a component in all its variations, no matter how
+ complex.
+
Learn more
+
+
+

+
Accessibility
+
Automatically test your components for a11y issues as you develop.
+
Learn more
+
+
+

+
Theming
+
Theme Storybook's UI to personalize it to your project.
+
Learn more
+
+
+
+
+
+
+
+

+
+
+
+
+
+

+ Join our contributors building the future of UI development.
+
+
Star on GitHub
+
+
+

+
+
+
+

+
+
+
+
+
+
diff --git a/react-ui/src/stories/docs/Git-Commit.mdx b/react-ui/src/stories/docs/Git-Commit.mdx
new file mode 100644
index 00000000..26e81138
--- /dev/null
+++ b/react-ui/src/stories/docs/Git-Commit.mdx
@@ -0,0 +1,25 @@
+import { Meta } from '@storybook/blocks';
+
+
+
+# Git 提交规范
+
+遵循 [Angular Commit Message Guidelines](https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#-commit-message-guidelines) 规范:
+
+- feat: 增加新功能
+- fix: 修复 bug
+- build: 更改构建系统或外部依赖(例如:gulp, broccoli, npm)
+- ci: 更改CI配置文件和脚本(例如:Travis, Circle, BrowserStack, SauceLabs)
+- docs: 更改文档
+- perf: 提高性能的修改
+- refactor: 重构,但是即不是新增功能,也不是修复 bug
+- style: 修改格式或样式,但是不会影响代码的运行(例如:删除空白、添加分号等)
+- test: 添加测试
+- chore: 其它不修改 src 或测试文件的更改
+
+例如
+
+```sh
+$ git commit -m "feat: 集成 Storybook 框架"
+```
+
diff --git a/react-ui/src/stories/docs/Less.mdx b/react-ui/src/stories/docs/Less.mdx
new file mode 100644
index 00000000..24d4dd1b
--- /dev/null
+++ b/react-ui/src/stories/docs/Less.mdx
@@ -0,0 +1,237 @@
+import { Meta } from '@storybook/blocks';
+
+
+
+# Less 规范
+
+## Theme
+
+### 自定义主题
+
+`src/styles/theme.less` 定义了 UI 主题颜色变量、Less 函数、Less 混合。
+
+在开发过程中使用这个文件的定义的变量、函数以及混合。通过 UmiJS 的配置,我们在 Less 文件不需要收到导入这个文件。
+
+```css
+// 颜色
+@primary-color: #1664ff; // 主色调
+@primary-color-secondary: #4e89ff;
+@primary-color-hover: #69b1ff;
+@sider-background-color: #f2f5f7; // 侧边栏背景颜色
+@background-color: #f9fafb; // 页面背景颜色
+@text-color: #1d1d20;
+@text-color-secondary: #575757;
+@text-color-tertiary: #8a8a8a;
+@text-placeholder-color: rgba(0, 0, 0, 0.25);
+@text-disabled-color: rgba(0, 0, 0, 0.25);
+@success-color: #6ac21d;
+@error-color: #c73131;
+@warning-color: #f98e1b;
+@abort-color: #8a8a8a;
+@pending-color: #ecb934;
+@underline-color: #5d93ff;
+@border-color: #eaeaea;
+@link-hover-color: #69b1ff;
+@heading-color: rgba(0, 0, 0, 0.85);
+@input-icon-hover-color: rgba(0, 0, 0, 0.85);
+
+// 字体大小
+@font-size-title: 18px;
+@font-size-content: 16px;
+@font-size: 15px;
+@font-size-input: 14px;
+@font-size-input-lg: @font-size-content;
+
+// padding
+@content-padding: 25px;
+```
+
+
+
+颜色变量还可以在 `js/ts/jsx/tsx` 里使用
+
+```js
+import themes from "@/styles/theme.less"
+
+const primaryColor = themes['primaryColor']; // #1664ff
+```
+
+### Ant Design 主题覆盖
+
+Ant Design 可以[定制主题](https://ant-design.antgroup.com/docs/react/customize-theme-cn),Ant Design 是通过 [ConfigProvider](https://ant-design.antgroup.com/components/config-provider-cn) 组件进行主题定制,而 UmiJS 可以在[配置文件](https://umijs.org/docs/max/antd#%E6%9E%84%E5%BB%BA%E6%97%B6%E9%85%8D%E7%BD%AE)或者 [`app.ts`](https://umijs.org/docs/max/antd#%E8%BF%90%E8%A1%8C%E6%97%B6%E9%85%8D%E7%BD%AE) 里进行配置。我选择在 [`app.ts`](https://umijs.org/docs/max/antd#%E8%BF%90%E8%A1%8C%E6%97%B6%E9%85%8D%E7%BD%AE) 里进行配置,因为这里可以使用主题颜色变量。
+
+```tsx
+// 主题修改
+export const antd: RuntimeAntdConfig = (memo) => {
+ memo.theme ??= {};
+ memo.theme.token = {
+ colorPrimary: themes['primaryColor'],
+ colorSuccess: themes['successColor'],
+ colorError: themes['errorColor'],
+ colorWarning: themes['warningColor'],
+ colorLink: themes['primaryColor'],
+ colorText: themes['textColor'],
+ controlHeightLG: 46,
+ };
+ memo.theme.components ??= {};
+ memo.theme.components.Tabs = {};
+ memo.theme.components.Button = {
+ defaultBg: 'rgba(22, 100, 255, 0.06)',
+ defaultBorderColor: 'rgba(22, 100, 255, 0.11)',
+ defaultColor: themes['textColor'],
+ defaultHoverBg: 'rgba(22, 100, 255, 0.06)',
+ defaultHoverBorderColor: 'rgba(22, 100, 255, 0.5)',
+ defaultHoverColor: '#3F7FFF',
+ defaultActiveBg: 'rgba(22, 100, 255, 0.12)',
+ defaultActiveBorderColor: 'rgba(22, 100, 255, 0.75)',
+ defaultActiveColor: themes['primaryColor'],
+ contentFontSize: parseInt(themes['fontSize']),
+ };
+ memo.theme.components.Input = {
+ inputFontSize: parseInt(themes['fontSizeInput']),
+ inputFontSizeLG: parseInt(themes['fontSizeInputLg']),
+ paddingBlockLG: 10,
+ };
+ memo.theme.components.Select = {
+ singleItemHeightLG: 46,
+ optionSelectedColor: themes['primaryColor'],
+ };
+ memo.theme.components.Table = {
+ headerBg: 'rgba(242, 244, 247, 0.36)',
+ headerBorderRadius: 4,
+ rowSelectedBg: 'rgba(22, 100, 255, 0.05)',
+ };
+ memo.theme.components.Tabs = {
+ titleFontSize: 16,
+ };
+ memo.theme.components.Form = {
+ labelColor: 'rgba(29, 29, 32, 0.8);',
+ };
+ memo.theme.components.Breadcrumb = {
+ iconFontSize: parseInt(themes['fontSize']),
+ linkColor: 'rgba(29, 29, 32, 0.7)',
+ separatorColor: 'rgba(29, 29, 32, 0.7)',
+ };
+
+ memo.theme.cssVar = true;
+
+ memo.appConfig = {
+ message: {
+ // 配置 message 最大显示数,超过限制时,最早的消息会被自动关闭
+ maxCount: 3,
+ },
+ };
+
+ return memo;
+};
+```
+
+覆盖 Ant Design 的默认样式,优先选择这种方式,如果没有相应的变量,才覆盖 Ant Design 的样式,在 `src/overrides.less` 文件里覆盖,请查看 UmiJS 关于[`global.less`](https://umijs.org/docs/guides/directory-structure#globalcsslesssassscss) 与 [`overrides.less`](https://umijs.org/docs/guides/directory-structure#overridescsslesssassscss) 的说明。
+
+## BEM
+
+类名遵循 [BEM - Block, Element, Modifier](https://getbem.com/) 规范
+
+### Block
+
+有意义的独立实体,Block 的类名由小写字母、数字和横线组成,比如 `model`、`form`、`paramneter-input`
+
+### Element
+
+块的一部分,Element 的类名由 `Block 的类名` + `双下划线(__)` + `Element 的名称` 组成,比如 `model__title`、`form__input`、`paramneter-input__content`
+
+### Modifier
+
+块或元素的变种,Modifier 的类名由 `Block 的类名` 或者 `Element 的类名` + `双横线(--)` + `Modifier 的名称` 组成,比如 `button--active`、`form--large`
+
+举个 🌰
+
+```tsx
+// @/components/CodeConfigItem/index.tsx
+
+import classNames from 'classnames';
+import styles from './index.less';
+
+function CodeConfigItem({ item, onClick }: CodeConfigItemProps) {
+ return (
+
+
+
+ {item.code_repo_name}
+
+
+ {item.code_repo_vis === AvailableRange.Public ? '公开' : '私有'}
+
+
+
+ {item.git_url}
+
+
{item.git_branch}
+
+ );
+}
+
+```
+
+### 一点建议
+
+如果你陷入嵌套地狱,比如
+
+```tsx
+function Component() {
+ return (
+
+ )
+}
+```
+
+说明你需要拆分组件了
+
+```tsx
+function Component() {
+ return (
+
+ )
+}
+
+function SubComponent() {
+ return (
+
+ )
+}
+```
+
+既减少了类名的嵌套,又减少了 HTML 的嵌套,使代码逻辑更加清晰,易于理解与维护,同时实现模块化和组件化
+
+
+
diff --git a/react-ui/src/stories/docs/assets/accessibility.png b/react-ui/src/stories/docs/assets/accessibility.png
new file mode 100644
index 00000000..6ffe6fea
Binary files /dev/null and b/react-ui/src/stories/docs/assets/accessibility.png differ
diff --git a/react-ui/src/stories/docs/assets/accessibility.svg b/react-ui/src/stories/docs/assets/accessibility.svg
new file mode 100644
index 00000000..107e93f8
--- /dev/null
+++ b/react-ui/src/stories/docs/assets/accessibility.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/react-ui/src/stories/docs/assets/addon-library.png b/react-ui/src/stories/docs/assets/addon-library.png
new file mode 100644
index 00000000..95deb38a
Binary files /dev/null and b/react-ui/src/stories/docs/assets/addon-library.png differ
diff --git a/react-ui/src/stories/docs/assets/assets.png b/react-ui/src/stories/docs/assets/assets.png
new file mode 100644
index 00000000..cfba6817
Binary files /dev/null and b/react-ui/src/stories/docs/assets/assets.png differ
diff --git a/react-ui/src/stories/docs/assets/avif-test-image.avif b/react-ui/src/stories/docs/assets/avif-test-image.avif
new file mode 100644
index 00000000..530709bc
Binary files /dev/null and b/react-ui/src/stories/docs/assets/avif-test-image.avif differ
diff --git a/react-ui/src/stories/docs/assets/context.png b/react-ui/src/stories/docs/assets/context.png
new file mode 100644
index 00000000..e5cd249a
Binary files /dev/null and b/react-ui/src/stories/docs/assets/context.png differ
diff --git a/react-ui/src/stories/docs/assets/discord.svg b/react-ui/src/stories/docs/assets/discord.svg
new file mode 100644
index 00000000..d638958b
--- /dev/null
+++ b/react-ui/src/stories/docs/assets/discord.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/react-ui/src/stories/docs/assets/docs.png b/react-ui/src/stories/docs/assets/docs.png
new file mode 100644
index 00000000..a749629d
Binary files /dev/null and b/react-ui/src/stories/docs/assets/docs.png differ
diff --git a/react-ui/src/stories/docs/assets/figma-plugin.png b/react-ui/src/stories/docs/assets/figma-plugin.png
new file mode 100644
index 00000000..8f79b08c
Binary files /dev/null and b/react-ui/src/stories/docs/assets/figma-plugin.png differ
diff --git a/react-ui/src/stories/docs/assets/github.svg b/react-ui/src/stories/docs/assets/github.svg
new file mode 100644
index 00000000..dc513528
--- /dev/null
+++ b/react-ui/src/stories/docs/assets/github.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/react-ui/src/stories/docs/assets/share.png b/react-ui/src/stories/docs/assets/share.png
new file mode 100644
index 00000000..8097a370
Binary files /dev/null and b/react-ui/src/stories/docs/assets/share.png differ
diff --git a/react-ui/src/stories/docs/assets/styling.png b/react-ui/src/stories/docs/assets/styling.png
new file mode 100644
index 00000000..d341e826
Binary files /dev/null and b/react-ui/src/stories/docs/assets/styling.png differ
diff --git a/react-ui/src/stories/docs/assets/testing.png b/react-ui/src/stories/docs/assets/testing.png
new file mode 100644
index 00000000..d4ac39a0
Binary files /dev/null and b/react-ui/src/stories/docs/assets/testing.png differ
diff --git a/react-ui/src/stories/docs/assets/theming.png b/react-ui/src/stories/docs/assets/theming.png
new file mode 100644
index 00000000..1535eb9b
Binary files /dev/null and b/react-ui/src/stories/docs/assets/theming.png differ
diff --git a/react-ui/src/stories/docs/assets/tutorials.svg b/react-ui/src/stories/docs/assets/tutorials.svg
new file mode 100644
index 00000000..b492a9c6
--- /dev/null
+++ b/react-ui/src/stories/docs/assets/tutorials.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/react-ui/src/stories/docs/assets/youtube.svg b/react-ui/src/stories/docs/assets/youtube.svg
new file mode 100644
index 00000000..a7515d7e
--- /dev/null
+++ b/react-ui/src/stories/docs/assets/youtube.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/react-ui/src/stories/mockData.ts b/react-ui/src/stories/mockData.ts
new file mode 100644
index 00000000..e5ea5bd1
--- /dev/null
+++ b/react-ui/src/stories/mockData.ts
@@ -0,0 +1,848 @@
+// 数据集列表
+export const datasetListData = {
+ msg: '操作成功',
+ code: 200,
+ data: {
+ content: [
+ {
+ name: '手写体识别训练测试数据集',
+ identifier: 'admin_dataset_20241213140429',
+ description: '手写体识别数据集',
+ is_public: false,
+ time_ago: '2个月前',
+ id: 1454047,
+ visits: 0,
+ create_by: '陈志航',
+ owner: 'chenzhihang',
+ },
+ {
+ name: '手写体识别',
+ identifier: 'admin_dataset_20241213140020',
+ description: '手写体识别数据集',
+ is_public: false,
+ time_ago: '2个月前',
+ id: 1454046,
+ visits: 0,
+ create_by: '陈志航',
+ owner: 'chenzhihang',
+ },
+ {
+ name: '生物活性分子数据集',
+ identifier: 'admin_dataset_20241211151411',
+ description: '生物活性分子数据集',
+ is_public: false,
+ time_ago: '2个月前',
+ id: 1454004,
+ visits: 0,
+ create_by: '陈志航',
+ owner: 'chenzhihang',
+ },
+ {
+ name: '介电材料数据集',
+ identifier: 'admin_dataset_20241211151330',
+ description: '介电材料数据集',
+ is_public: false,
+ time_ago: '2个月前',
+ id: 1454003,
+ visits: 0,
+ create_by: '陈志航',
+ owner: 'chenzhihang',
+ },
+ ],
+ pageable: {
+ sort: {
+ unsorted: true,
+ sorted: false,
+ empty: true,
+ },
+ pageSize: 2000,
+ pageNumber: 0,
+ offset: 0,
+ unpaged: false,
+ paged: true,
+ },
+ last: true,
+ totalElements: 4,
+ totalPages: 1,
+ sort: {
+ unsorted: true,
+ sorted: false,
+ empty: true,
+ },
+ first: true,
+ number: 0,
+ numberOfElements: 4,
+ size: 2000,
+ empty: false,
+ },
+};
+
+// 数据集版本列表
+export const datasetVersionData = {
+ msg: '操作成功',
+ code: 200,
+ data: [
+ {
+ name: 'v2',
+ http_url: 'https://cdn09022024.gitlink.org.cn/chenzhihang/admin_dataset_20241213140429.git',
+ zip_url:
+ 'https://www.gitlink.org.cn/api/chenzhihang/admin_dataset_20241213140429/archive/v2.zip',
+ tar_url:
+ 'https://www.gitlink.org.cn/api/chenzhihang/admin_dataset_20241213140429/archive/v2.tar.gz',
+ },
+ {
+ name: 'v3',
+ http_url: 'https://cdn09022024.gitlink.org.cn/chenzhihang/admin_dataset_20241213140429.git',
+ zip_url:
+ 'https://www.gitlink.org.cn/api/chenzhihang/admin_dataset_20241213140429/archive/v3.zip',
+ tar_url:
+ 'https://www.gitlink.org.cn/api/chenzhihang/admin_dataset_20241213140429/archive/v3.tar.gz',
+ },
+ {
+ name: 'v1',
+ http_url: 'https://cdn09022024.gitlink.org.cn/chenzhihang/admin_dataset_20241213140429.git',
+ zip_url:
+ 'https://www.gitlink.org.cn/api/chenzhihang/admin_dataset_20241213140429/archive/v1.zip',
+ tar_url:
+ 'https://www.gitlink.org.cn/api/chenzhihang/admin_dataset_20241213140429/archive/v1.tar.gz',
+ },
+ ],
+};
+
+// 数据集详情
+export const datasetDetailData = {
+ msg: '操作成功',
+ code: 200,
+ data: {
+ name: '生物活性分子材料数据集',
+ identifier: 'admin_dataset_20250217105241',
+ description: '生物活性分子材料数据集',
+ is_public: false,
+ data_type: '自然语言处理',
+ data_tag: '机器翻译',
+ version: 'v1',
+ dataset_version_vos: [
+ {
+ url: '/home/resource/admin/datasets/1454953/admin_dataset_20250217105241/v1/dataset/BBBP.zip',
+ file_name: 'BBBP.zip',
+ file_size: '42.14 KB',
+ },
+ ],
+ id: 1454953,
+ create_by: '樊帅',
+ version_desc: '生物活性分子材料数据集',
+ usage:
+ '# 克隆数据集配置文件与存储参数到本地\ngit clone -b v1 https://gitlink.org.cn/fanshuai/admin_dataset_20250217105241.git\n# 远程拉取配置文件\ndvc pull\n
',
+ update_time: '2025-02-17 10:52:43',
+ owner: 'fanshuai',
+ dataset_source: 'add',
+ relative_paths: 'admin/datasets/1454953/admin_dataset_20250217105241/v1/dataset',
+ },
+};
+
+// 模型列表
+export const modelListData = {
+ msg: '操作成功',
+ code: 200,
+ data: {
+ content: [
+ {
+ id: 1454208,
+ name: '介电材料模型1',
+ create_by: '陈志航',
+ description: '介电材料模型1',
+ time_ago: '2个月前',
+ owner: 'chenzhihang',
+ identifier: 'admin_model_20241224095928',
+ is_public: false,
+ },
+ {
+ id: 1454007,
+ name: '手写体识别部署模型',
+ create_by: '陈志航',
+ description: '手写体识别部署模型',
+ time_ago: '2个月前',
+ owner: 'chenzhihang',
+ identifier: 'admin_model_20241211151713',
+ is_public: false,
+ },
+ {
+ id: 1454006,
+ name: '生物活性分子材料',
+ create_by: '陈志航',
+ description: '生物活性分子材料',
+ time_ago: '2个月前',
+ owner: 'chenzhihang',
+ identifier: 'admin_model_20241211151645',
+ is_public: false,
+ },
+ {
+ id: 1454005,
+ name: '介电材料模型',
+ create_by: '陈志航',
+ description: '介电材料模型',
+ time_ago: '2个月前',
+ owner: 'chenzhihang',
+ identifier: 'admin_model_20241211151601',
+ is_public: false,
+ },
+ ],
+ pageable: {
+ sort: {
+ unsorted: true,
+ sorted: false,
+ empty: true,
+ },
+ pageSize: 2000,
+ pageNumber: 0,
+ offset: 0,
+ unpaged: false,
+ paged: true,
+ },
+ last: true,
+ totalElements: 4,
+ totalPages: 1,
+ sort: {
+ unsorted: true,
+ sorted: false,
+ empty: true,
+ },
+ first: true,
+ number: 0,
+ numberOfElements: 4,
+ size: 2000,
+ empty: false,
+ },
+};
+
+// 模型版本列表
+export const modelVersionData = {
+ msg: '操作成功',
+ code: 200,
+ data: [
+ {
+ name: 'v1',
+ http_url: 'https://cdn09022024.gitlink.org.cn/chenzhihang/admin_model_20241224095928.git',
+ zip_url:
+ 'https://www.gitlink.org.cn/api/chenzhihang/admin_model_20241224095928/archive/v1.zip',
+ tar_url:
+ 'https://www.gitlink.org.cn/api/chenzhihang/admin_model_20241224095928/archive/v1.tar.gz',
+ },
+ {
+ name: 'v2',
+ http_url: 'https://cdn09022024.gitlink.org.cn/chenzhihang/admin_model_20241224095928.git',
+ zip_url:
+ 'https://www.gitlink.org.cn/api/chenzhihang/admin_model_20241224095928/archive/v1.zip',
+ tar_url:
+ 'https://www.gitlink.org.cn/api/chenzhihang/admin_model_20241224095928/archive/v1.tar.gz',
+ },
+ ],
+};
+
+// 模型详情
+export const modelDetailData = {
+ msg: '操作成功',
+ code: 200,
+ data: {
+ id: 1454208,
+ name: '介电材料模型1',
+ version: 'v1',
+ version_desc: '介电材料模型1',
+ create_by: '陈志航',
+ create_time: '2024-12-24 09:59:31',
+ update_time: '2024-12-24 09:59:31',
+ model_size: '101.90 KB',
+ model_source: 'add',
+ model_tag: '图像分类',
+ model_type: 'PyTorch',
+ description: '介电材料模型1',
+ usage:
+ '# 克隆模型配置文件与存储参数到本地\ngit clone -b v1 https://gitlink.org.cn/chenzhihang/admin_model_20241224095928.git\n# 远程拉取配置文件\ndvc pull\n
',
+ owner: 'chenzhihang',
+ identifier: 'admin_model_20241224095928',
+ is_public: false,
+ relative_paths: 'admin/model/1454208/admin_model_20241224095928/v1/model',
+ model_version_vos: [
+ {
+ url: '/home/resource/admin/model/1454208/admin_model_20241224095928/v1/model/sklearn_svr_good.pkl',
+ file_name: 'sklearn_svr_good.pkl',
+ file_size: '101.90 KB',
+ },
+ ],
+ },
+};
+
+// 镜像列表
+export const mirrorListData = {
+ code: 200,
+ msg: '操作成功',
+ data: {
+ content: [
+ {
+ id: 42,
+ name: 'ccr.ccs.tencentyun.com/somunslotus/httpserver',
+ description: 'htttp服务器镜像',
+ image_type: 0,
+ create_by: 'admin',
+ create_time: '2024-04-30T14:44:37.000+08:00',
+ update_by: 'admin',
+ update_time: '2024-04-30T14:44:37.000+08:00',
+ state: 1,
+ version_count: 1,
+ },
+ {
+ id: 44,
+ name: 'minio-test',
+ description: 'minio镜像',
+ image_type: 0,
+ create_by: 'admin',
+ create_time: '2024-05-08T15:19:00.000+08:00',
+ update_by: 'admin',
+ update_time: '2024-05-08T15:19:00.000+08:00',
+ state: 1,
+ version_count: 1,
+ },
+ {
+ id: 46,
+ name: 'machine-learning/mnist-model-deploy',
+ description: 'mnist手写体识别部署镜像',
+ image_type: 0,
+ create_by: 'admin',
+ create_time: '2024-05-28T10:18:30.000+08:00',
+ update_by: 'admin',
+ update_time: '2024-05-28T10:18:30.000+08:00',
+ state: 1,
+ version_count: 2,
+ },
+ {
+ id: 49,
+ name: 'go_httpsever',
+ description: 'golang httpserver镜像',
+ image_type: 0,
+ create_by: 'admin',
+ create_time: '2024-06-25T11:11:41.000+08:00',
+ update_by: 'admin',
+ update_time: '2024-06-25T11:11:41.000+08:00',
+ state: 1,
+ version_count: 1,
+ },
+ {
+ id: 51,
+ name: 'pytorch2_python3_cuda_12',
+ description: 'pytorch2.1.2_python3.11_cuda_12',
+ image_type: 0,
+ create_by: 'admin',
+ create_time: '2024-10-14T16:16:35.000+08:00',
+ update_by: 'admin',
+ update_time: '2024-10-14T16:16:35.000+08:00',
+ state: 1,
+ version_count: 2,
+ },
+ {
+ id: 53,
+ name: 'jupyterlab',
+ description: 'v1',
+ image_type: 0,
+ create_by: 'admin',
+ create_time: '2024-10-15T16:19:29.000+08:00',
+ update_by: 'admin',
+ update_time: '2024-10-15T16:19:29.000+08:00',
+ state: 1,
+ version_count: 1,
+ },
+ {
+ id: 55,
+ name: 'machine-learning/ax-pytorch',
+ description: '自动机器学习Ax镜像,带pytorch',
+ image_type: 0,
+ create_by: 'admin',
+ create_time: '2024-12-13T11:25:37.000+08:00',
+ update_by: 'admin',
+ update_time: '2024-12-13T11:25:37.000+08:00',
+ state: 1,
+ version_count: 1,
+ },
+ ],
+ pageable: {
+ sort: {
+ unsorted: true,
+ sorted: false,
+ empty: true,
+ },
+ pageSize: 2000,
+ pageNumber: 0,
+ offset: 0,
+ unpaged: false,
+ paged: true,
+ },
+ last: true,
+ totalElements: 7,
+ totalPages: 1,
+ sort: {
+ unsorted: true,
+ sorted: false,
+ empty: true,
+ },
+ first: true,
+ number: 0,
+ numberOfElements: 7,
+ size: 2000,
+ empty: false,
+ },
+};
+
+// 镜像版本列表
+export const mirrorVerionData = {
+ code: 200,
+ msg: '操作成功',
+ data: {
+ content: [
+ {
+ id: 54,
+ image_id: 42,
+ version: null,
+ url: '172.20.32.187/testlib/admin/ccr.ccs.tencentyun.com/somunslotus/httpserver:v1',
+ tag_name: 'v1',
+ file_size: '6.98 MB',
+ status: 'available',
+ create_by: 'admin',
+ create_time: '2024-04-30T14:44:37.000+08:00',
+ update_by: 'admin',
+ update_time: '2024-04-30T14:44:51.000+08:00',
+ state: 1,
+ host_ip: null,
+ },
+ ],
+ pageable: {
+ sort: {
+ unsorted: true,
+ sorted: false,
+ empty: true,
+ },
+ pageSize: 2000,
+ pageNumber: 0,
+ offset: 0,
+ unpaged: false,
+ paged: true,
+ },
+ last: true,
+ totalElements: 1,
+ totalPages: 1,
+ sort: {
+ unsorted: true,
+ sorted: false,
+ empty: true,
+ },
+ first: true,
+ number: 0,
+ numberOfElements: 1,
+ size: 2000,
+ empty: false,
+ },
+};
+
+// 代码配置列表
+export const codeListData = {
+ code: 200,
+ msg: '操作成功',
+ data: {
+ content: [
+ {
+ id: 2,
+ code_repo_name: '介电材料代码',
+ code_repo_vis: 1,
+ git_url: 'https://gitlink.org.cn/fuli/ML_for_Materials.git',
+ git_branch: 'master',
+ verify_mode: null,
+ git_user_name: null,
+ git_password: null,
+ ssh_key: null,
+ create_by: 'admin',
+ create_time: '2024-10-14T16:10:45.000+08:00',
+ update_by: 'admin',
+ update_time: '2024-10-14T16:10:45.000+08:00',
+ state: 1,
+ },
+ {
+ id: 3,
+ code_repo_name: '生物活性材料代码',
+ code_repo_vis: 1,
+ git_url: 'https://gitlink.org.cn/zhaoyihan/test_mole_pre.git',
+ git_branch: 'parse_dataset',
+ verify_mode: null,
+ git_user_name: null,
+ git_password: null,
+ ssh_key: null,
+ create_by: 'admin',
+ create_time: '2024-10-16T08:41:39.000+08:00',
+ update_by: 'admin',
+ update_time: '2024-10-16T08:41:39.000+08:00',
+ state: 1,
+ },
+ {
+ id: 4,
+ code_repo_name: '数据处理',
+ code_repo_vis: 1,
+ git_url: 'https://openi.pcl.ac.cn/somunslotus/somun202304241505581.git',
+ git_branch: 'train_ci_test',
+ verify_mode: null,
+ git_user_name: null,
+ git_password: null,
+ ssh_key: null,
+ create_by: 'admin',
+ create_time: '2024-10-16T14:51:18.000+08:00',
+ update_by: 'admin',
+ update_time: '2024-10-16T14:51:18.000+08:00',
+ state: 1,
+ },
+ {
+ id: 5,
+ code_repo_name: '手写体识别部署',
+ code_repo_vis: 1,
+ git_url: 'https://gitlink.org.cn/somunslotus/mnist-inference.git',
+ git_branch: 'master',
+ verify_mode: null,
+ git_user_name: null,
+ git_password: null,
+ ssh_key: null,
+ create_by: 'admin',
+ create_time: '2024-10-16T16:36:43.000+08:00',
+ update_by: 'admin',
+ update_time: '2024-10-16T16:36:43.000+08:00',
+ state: 1,
+ },
+ {
+ id: 7,
+ code_repo_name: '手写体识别训练',
+ code_repo_vis: 1,
+ git_url: 'https://openi.pcl.ac.cn/somunslotus/somun202304241505581.git',
+ git_branch: 'train_ci_test',
+ verify_mode: null,
+ git_user_name: null,
+ git_password: null,
+ ssh_key: null,
+ create_by: 'admin',
+ create_time: '2024-12-13T13:58:50.000+08:00',
+ update_by: 'admin',
+ update_time: '2024-12-13T13:58:50.000+08:00',
+ state: 1,
+ },
+ ],
+ pageable: {
+ sort: {
+ unsorted: true,
+ sorted: false,
+ empty: true,
+ },
+ pageSize: 20,
+ pageNumber: 0,
+ offset: 0,
+ unpaged: false,
+ paged: true,
+ },
+ last: true,
+ totalElements: 5,
+ totalPages: 1,
+ sort: {
+ unsorted: true,
+ sorted: false,
+ empty: true,
+ },
+ first: true,
+ number: 0,
+ numberOfElements: 5,
+ size: 20,
+ empty: false,
+ },
+};
+
+// 服务列表
+export const serviceListData = {
+ code: 200,
+ msg: '操作成功',
+ data: {
+ content: [
+ {
+ id: 25,
+ service_name: '测试1224',
+ service_type: 'video',
+ service_type_name: '视频',
+ description: '测试',
+ create_by: 'admin',
+ update_by: 'admin',
+ create_time: '2024-12-24T16:01:02.000+08:00',
+ update_time: '2024-12-24T16:01:02.000+08:00',
+ state: 1,
+ version_count: 2,
+ },
+ {
+ id: 12,
+ service_name: '介电材料',
+ service_type: 'text',
+ service_type_name: '文本',
+ description: 'test',
+ create_by: 'admin',
+ update_by: 'admin',
+ create_time: '2024-11-27T09:30:23.000+08:00',
+ update_time: '2024-11-27T09:30:23.000+08:00',
+ state: 1,
+ version_count: 0,
+ },
+ {
+ id: 7,
+ service_name: '手写体识别',
+ service_type: 'image',
+ service_type_name: '图片',
+ description: '手写体识别服务',
+ create_by: 'admin',
+ update_by: 'admin',
+ create_time: '2024-10-10T10:14:00.000+08:00',
+ update_time: '2024-10-10T10:14:00.000+08:00',
+ state: 1,
+ version_count: 5,
+ },
+ ],
+ pageable: {
+ sort: {
+ unsorted: true,
+ sorted: false,
+ empty: true,
+ },
+ pageNumber: 0,
+ pageSize: 10,
+ offset: 0,
+ paged: true,
+ unpaged: false,
+ },
+ last: true,
+ totalPages: 1,
+ totalElements: 3,
+ sort: {
+ unsorted: true,
+ sorted: false,
+ empty: true,
+ },
+ first: true,
+ number: 0,
+ numberOfElements: 3,
+ size: 10,
+ empty: false,
+ },
+};
+
+// 计算资源列表
+export const computeResourceData = {
+ code: 200,
+ msg: '操作成功',
+ data: {
+ content: [
+ {
+ id: 15,
+ computing_resource: 'GPU',
+ standard:
+ '{"name":"CPU-GPU","value":{"detail_type":"3060","gpu":0,"cpu":1,"memory":"2GB"}}',
+ description: 'GPU: 0, CPU:1, 内存: 2GB',
+ create_by: 'admin',
+ create_time: '2024-04-19T00:00:00.000+08:00',
+ update_by: 'admin',
+ update_time: '2024-04-19T00:00:00.000+08:00',
+ state: 1,
+ used_state: null,
+ node: null,
+ },
+ {
+ id: 16,
+ computing_resource: 'GPU',
+ standard:
+ '{"name":"CPU-GPU","value":{"detail_type":"3060","gpu":0,"cpu":2,"memory":"4GB"}}',
+ description: 'GPU: 0, CPU:2, 内存: 4GB',
+ create_by: 'admin',
+ create_time: '2024-04-19T00:00:00.000+08:00',
+ update_by: 'admin',
+ update_time: '2024-04-19T00:00:00.000+08:00',
+ state: 1,
+ used_state: null,
+ node: null,
+ },
+ {
+ id: 17,
+ computing_resource: 'GPU',
+ standard:
+ '{"name":"CPU-GPU","value":{"detail_type":"3060","gpu":0,"cpu":4,"memory":"8GB"}}',
+ description: 'GPU: 0, CPU:4, 内存: 8GB',
+ create_by: 'admin',
+ create_time: '2024-04-19T00:00:00.000+08:00',
+ update_by: 'admin',
+ update_time: '2024-04-19T00:00:00.000+08:00',
+ state: 1,
+ used_state: null,
+ node: null,
+ },
+ {
+ id: 18,
+ computing_resource: 'GPU',
+ standard:
+ '{"name":"CPU-GPU","value":{"detail_type":"3060","gpu":1,"cpu":1,"memory":"2GB"}}',
+ description: 'GPU: 1, CPU:1, 内存: 2GB',
+ create_by: 'admin',
+ create_time: '2024-04-19T00:00:00.000+08:00',
+ update_by: 'admin',
+ update_time: '2024-04-19T00:00:00.000+08:00',
+ state: 1,
+ used_state: null,
+ node: null,
+ },
+ {
+ id: 19,
+ computing_resource: 'GPU',
+ standard:
+ '{"name":"CPU-GPU","value":{"detail_type":"3060","gpu":1,"cpu":2,"memory":"4GB"}}',
+ description: 'GPU: 1, CPU:2, 内存: 4GB',
+ create_by: 'admin',
+ create_time: '2024-04-19T00:00:00.000+08:00',
+ update_by: 'admin',
+ update_time: '2024-04-19T00:00:00.000+08:00',
+ state: 1,
+ used_state: null,
+ node: null,
+ },
+ {
+ id: 20,
+ computing_resource: 'GPU',
+ standard:
+ '{"name":"CPU-GPU","value":{"detail_type":"3060","gpu":1,"cpu":4,"memory":"8GB"}}',
+ description: 'GPU: 1, CPU:4, 内存: 8GB',
+ create_by: 'admin',
+ create_time: '2024-04-19T00:00:00.000+08:00',
+ update_by: 'admin',
+ update_time: '2024-04-19T00:00:00.000+08:00',
+ state: 1,
+ used_state: null,
+ node: null,
+ },
+ {
+ id: 21,
+ computing_resource: 'GPU',
+ standard:
+ '{"name":"CPU-GPU","value":{"detail_type":"3060","gpu":2,"cpu":2,"memory":"4GB"}}',
+ description: 'GPU: 2, CPU:2, 内存: 4GB',
+ create_by: 'admin',
+ create_time: '2024-04-19T00:00:00.000+08:00',
+ update_by: 'admin',
+ update_time: '2024-04-19T00:00:00.000+08:00',
+ state: 1,
+ used_state: null,
+ node: null,
+ },
+ {
+ id: 22,
+ computing_resource: 'GPU',
+ standard:
+ '{"name":"CPU-GPU","value":{"detail_type":"3060","gpu":2,"cpu":4,"memory":"8GB"}}',
+ description: 'GPU: 2, CPU:4, 内存: 8GB',
+ create_by: 'admin',
+ create_time: '2024-04-19T00:00:00.000+08:00',
+ update_by: 'admin',
+ update_time: '2024-04-19T00:00:00.000+08:00',
+ state: 1,
+ used_state: null,
+ node: null,
+ },
+ {
+ id: 23,
+ computing_resource: 'GPU',
+ standard:
+ '{"name":"CPU-GPU","value":{"detail_type":"RTX 3080 Ti","gpu":1,"cpu":1,"memory":"2GB"}}',
+ description: 'GPU: 1, CPU:1, 内存: 2GB, 显存: 12GB',
+ create_by: 'admin',
+ create_time: '2024-04-19T11:38:07.000+08:00',
+ update_by: 'admin',
+ update_time: '2024-04-19T11:38:07.000+08:00',
+ state: 1,
+ used_state: null,
+ node: null,
+ },
+ {
+ id: 24,
+ computing_resource: 'GPU',
+ standard:
+ '{"name":"CPU-GPU","value":{"detail_type":"RTX 3080","gpu":1,"cpu":2,"memory":"4GB"}}',
+ description: 'GPU: 1, CPU:2, 内存: 4GB, 显存: 10GB',
+ 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,
+ used_state: null,
+ node: null,
+ },
+ ],
+ pageable: {
+ sort: {
+ unsorted: true,
+ sorted: false,
+ empty: true,
+ },
+ pageNumber: 0,
+ pageSize: 1000,
+ offset: 0,
+ paged: true,
+ unpaged: false,
+ },
+ last: true,
+ totalPages: 1,
+ totalElements: 10,
+ sort: {
+ unsorted: true,
+ sorted: false,
+ empty: true,
+ },
+ first: true,
+ number: 0,
+ numberOfElements: 10,
+ size: 1000,
+ empty: false,
+ },
+};
+
+// 日志组
+export const logGroupData = {
+ code: 200,
+ msg: '操作成功',
+ data: {
+ log_type: 'normal',
+ log_detail: {
+ pod_name: 'workflow-txpb5-git-clone-05955a53-2484323670',
+ log_content:
+ '[2025-03-06 14:02:23] time="2025-03-06T14:02:23.068Z" level=info msg="capturing logs" argo=true\n[2025-03-06 14:02:23] Cloning into \'/tmp/traincode\'...\n[2025-03-06 14:02:23] Cloning public repository without authentication.\n[2025-03-06 14:02:23] Repository cloned successfully.\n[2025-03-06 14:02:24] time="2025-03-06T14:02:24.069Z" level=info msg="sub-process exited" argo=true error=""\n',
+ start_time: '1741240944069759628',
+ },
+ pods: ['workflow-txpb5-git-clone-05955a53-2484323670'],
+ },
+};
+
+// 日志
+export const logData = {
+ code: 200,
+ msg: '操作成功',
+ data: {
+ log_detail: {
+ pod_name: 'workflow-txpb5-git-clone-05955a53-2484323670',
+ log_content:
+ '[2025-03-06 14:02:24] time="2025-03-06T06:02:24.315Z" level=info msg="Main container completed" error=""\n[2025-03-06 14:02:24] time="2025-03-06T06:02:24.315Z" level=info msg="No Script output reference in workflow. Capturing script output ignored"\n[2025-03-06 14:02:24] time="2025-03-06T06:02:24.315Z" level=info msg="No output parameters"\n[2025-03-06 14:02:24] time="2025-03-06T06:02:24.315Z" level=info msg="No output artifacts"\n[2025-03-06 14:02:24] time="2025-03-06T06:02:24.315Z" level=info msg="S3 Save path: /tmp/argo/outputs/logs/main.log, key: workflow-txpb5/workflow-txpb5-git-clone-05955a53-2484323670/main.log"\n[2025-03-06 14:02:24] time="2025-03-06T06:02:24.315Z" level=info msg="Creating minio client using static credentials" endpoint="minio.argo.svc.cluster.local:9000"\n[2025-03-06 14:02:24] time="2025-03-06T06:02:24.315Z" level=info msg="Saving file to s3" bucket=my-bucket endpoint="minio.argo.svc.cluster.local:9000" key=workflow-txpb5/workflow-txpb5-git-clone-05955a53-2484323670/main.log path=/tmp/argo/outputs/logs/main.log\n[2025-03-06 14:02:25] time="2025-03-06T06:02:25.407Z" level=info msg="Save artifact" artifactName=main-logs duration=1.092064185s error="" key=workflow-txpb5/workflow-txpb5-git-clone-05955a53-2484323670/main.log\n[2025-03-06 14:02:25] time="2025-03-06T06:02:25.407Z" level=info msg="not deleting local artifact" localArtPath=/tmp/argo/outputs/logs/main.log\n[2025-03-06 14:02:25] time="2025-03-06T06:02:25.407Z" level=info msg="Successfully saved file: /tmp/argo/outputs/logs/main.log"\n[2025-03-06 14:02:25] time="2025-03-06T06:02:25.408Z" level=warning msg="failed to patch task result, falling back to legacy/insecure pod patch, see https://argo-workflows.readthedocs.io/en/release-3.5/workflow-rbac/" error="workflowtaskresults.argoproj.io is forbidden: User \\"system:serviceaccount:argo:argo\\" cannot create resource \\"workflowtaskresults\\" in API group \\"argoproj.io\\" in the namespace \\"argo\\""\n[2025-03-06 14:02:25] time="2025-03-06T06:02:25.418Z" level=info msg="Alloc=8553 TotalAlloc=14914 Sys=24421 NumGC=4 Goroutines=10"\n[2025-03-06 14:02:25] time="2025-03-06T06:02:25.419Z" level=warning msg="failed to patch task result, falling back to legacy/insecure pod patch, see https://argo-workflows.readthedocs.io/en/release-3.5/workflow-rbac/" error="workflowtaskresults.argoproj.io \\"workflow-txpb5-2484323670\\" is forbidden: User \\"system:serviceaccount:argo:argo\\" cannot patch resource \\"workflowtaskresults\\" in API group \\"argoproj.io\\" in the namespace \\"argo\\""\n[2025-03-06 14:02:25] time="2025-03-06T06:02:25.427Z" level=info msg="Deadline monitor stopped"\n',
+ start_time: '1741240945427542429',
+ },
+ },
+};
+
+export const logEmptyData = {
+ code: 200,
+ msg: '操作成功',
+ data: {
+ log_detail: {
+ pod_name: 'workflow-txpb5-git-clone-05955a53-2484323670',
+ log_content: '',
+ start_time: '1741240945427542429',
+ },
+ },
+};
diff --git a/react-ui/src/stories/pages/LogList.stories.tsx b/react-ui/src/stories/pages/LogList.stories.tsx
new file mode 100644
index 00000000..5685b8fa
--- /dev/null
+++ b/react-ui/src/stories/pages/LogList.stories.tsx
@@ -0,0 +1,90 @@
+import { ExperimentStatus } from '@/enums';
+import LogList from '@/pages/Experiment/components/LogList';
+import type { Meta, StoryObj } from '@storybook/react';
+import { http, HttpResponse } from 'msw';
+import { logData, logEmptyData, logGroupData } from '../mockData';
+
+let count = 0;
+
+// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
+const meta = {
+ title: 'Pages/LogList 日志组',
+ component: LogList,
+ parameters: {
+ // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout
+ // layout: 'centered',
+ msw: {
+ handlers: [
+ http.post('/api/mmp/experimentIns/realTimeLog', () => {
+ return HttpResponse.json(logGroupData);
+ }),
+ http.get('/api/mmp/experimentIns/pods/log', () => {
+ if (count > 0) {
+ count = 0;
+ return HttpResponse.json(logEmptyData);
+ }
+ count += 1;
+ return HttpResponse.json(logData);
+ }),
+ ],
+ },
+ },
+ // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs
+ tags: ['autodocs'],
+ // More on argTypes: https://storybook.js.org/docs/api/argtypes
+ argTypes: {
+ instanceNodeStatus: { control: 'select', options: Object.values(ExperimentStatus) },
+ },
+ // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args
+ // args: { onClick: fn() },
+} satisfies Meta;
+
+export default meta;
+type Story = StoryObj;
+
+// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
+/** 运行完成时 */
+export const Succeeded: Story = {
+ args: {
+ instanceName: 'workflow-txpb5',
+ instanceNamespace: 'argo',
+ pipelineNodeId: 'git-clone-05955a53',
+ workflowId: 'workflow-txpb5-2484323670',
+ instanceNodeStartTime: '2025-03-06 14:02:14',
+ instanceNodeStatus: ExperimentStatus.Succeeded,
+ },
+};
+
+/** 无状态,空数据 */
+export const NoStatus: Story = {
+ args: {
+ instanceName: 'workflow-txpb5',
+ instanceNamespace: 'argo',
+ pipelineNodeId: 'git-clone-05955a53',
+ workflowId: 'workflow-txpb5-2484323670',
+ instanceNodeStartTime: '2025-03-06 14:02:14',
+ },
+};
+
+/** Pending */
+export const Pending: Story = {
+ args: {
+ instanceName: 'workflow-txpb5',
+ instanceNamespace: 'argo',
+ pipelineNodeId: 'git-clone-05955a53',
+ workflowId: 'workflow-txpb5-2484323670',
+ instanceNodeStartTime: '2025-03-06 14:02:14',
+ instanceNodeStatus: ExperimentStatus.Pending,
+ },
+};
+
+export const Running: Story = {
+ args: {
+ instanceName: 'workflow-txpb5',
+ instanceNamespace: 'argo',
+ pipelineNodeId: 'git-clone-05955a53',
+ workflowId: 'workflow-txpb5-2484323670',
+ instanceNodeStartTime: '2025-03-06 14:02:14',
+ instanceNodeStatus: ExperimentStatus.Running,
+ },
+};
diff --git a/react-ui/src/styles/theme.less b/react-ui/src/styles/theme.less
index 758eec31..d044889f 100644
--- a/react-ui/src/styles/theme.less
+++ b/react-ui/src/styles/theme.less
@@ -8,29 +8,25 @@
@primary-color: #1664ff; // 主色调
@primary-color-secondary: #4e89ff;
@primary-color-hover: #69b1ff;
+@sider-background-color: #f2f5f7; // 侧边栏背景颜色
@background-color: #f9fafb; // 页面背景颜色
@text-color: #1d1d20;
@text-color-secondary: #575757;
@text-color-tertiary: #8a8a8a;
+@text-placeholder-color: rgba(0, 0, 0, 0.25);
+@text-disabled-color: rgba(0, 0, 0, 0.25);
@success-color: #6ac21d;
@error-color: #c73131;
@warning-color: #f98e1b;
@abort-color: #8a8a8a;
@pending-color: #ecb934;
@underline-color: #5d93ff;
+@border-color: #eaeaea;
-@border-color-base: #eaeaea;
-@border-color: rgba(22, 100, 255, 0.3);
-@border-color-secondary: rgba(22, 100, 255, 0.1);
-@background-color-primary: rgba(22, 100, 255, 0.03);
-@background-color-gray: rgba(4, 3, 3, 0.06);
-
+@link-hover-color: #69b1ff;
@heading-color: rgba(0, 0, 0, 0.85);
@input-icon-hover-color: rgba(0, 0, 0, 0.85);
-@link-hover-color: #69b1ff;
-@sider-background-color: #f2f5f7;
-
@workspace-background: linear-gradient(
179.03deg,
rgba(138, 138, 138, 0.06) 0%,
@@ -47,7 +43,7 @@
// padding
@content-padding: 25px;
-// 函数
+// 函数,hex 添加 alpha 值
.addAlpha(@color, @alpha) {
@red: red(@color);
@green: green(@color);
@@ -56,6 +52,7 @@
}
// 混合
+// 单行
.singleLine() {
overflow: hidden;
white-space: nowrap;
@@ -63,6 +60,7 @@
word-break: break-all;
}
+// 多行
.multiLine(@line) {
display: -webkit-box;
overflow: hidden;
diff --git a/react-ui/src/utils/constant.ts b/react-ui/src/utils/constant.ts
new file mode 100644
index 00000000..4fe1ea9b
--- /dev/null
+++ b/react-ui/src/utils/constant.ts
@@ -0,0 +1,3 @@
+export const xlCols = { span: 12 };
+export const xllCols = { span: 10 };
+export const formCols = { xl: xlCols, xxl: xllCols };
diff --git a/react-ui/src/utils/format.ts b/react-ui/src/utils/format.ts
new file mode 100644
index 00000000..9c1d327d
--- /dev/null
+++ b/react-ui/src/utils/format.ts
@@ -0,0 +1,137 @@
+import { ResourceSelectorResponse } from '@/components/ResourceSelectorModal';
+import { ResourceInfoTabKeys } from '@/pages/Dataset/components/ResourceInfo';
+import {
+ DataSource,
+ DatasetData,
+ ModelData,
+ ProjectDependency,
+ TrainTask,
+} from '@/pages/Dataset/config';
+import { getGitUrl } from '@/utils';
+// 格式化日期
+export { formatDate } from '@/utils/date';
+
+type SelectedCodeConfig = {
+ code_path: string;
+ branch: string;
+ showValue?: string; // 前端使用的
+ show_value?: string; // 后端使用的
+};
+
+// 格式化数据集数组
+export const formatDatasets = (datasets?: DatasetData[]) => {
+ if (!datasets || datasets.length === 0) {
+ return undefined;
+ }
+ return datasets.map((item) => ({
+ value: item.name,
+ link: `/dataset/dataset/info/${item.id}?tab=${ResourceInfoTabKeys.Introduction}&version=${item.version}&name=${item.name}&owner=${item.owner}&identifier=${item.identifier}`,
+ }));
+};
+
+// 格式化数据集
+export const formatDataset = (dataset?: DatasetData) => {
+ if (!dataset) {
+ return undefined;
+ }
+ return {
+ value: dataset.name,
+ link: `/dataset/dataset/info/${dataset.id}?tab=${ResourceInfoTabKeys.Introduction}&version=${dataset.version}&name=${dataset.name}&owner=${dataset.owner}&identifier=${dataset.identifier}`,
+ };
+};
+
+// 格式化模型
+export const formatModel = (model: ModelData) => {
+ if (!model) {
+ return undefined;
+ }
+ return {
+ value: model.name,
+ link: `/dataset/model/info/${model.id}?tab=${ResourceInfoTabKeys.Introduction}&version=${model.version}&name=${model.name}&owner=${model.owner}&identifier=${model.identifier}`,
+ };
+};
+
+// 格式化镜像
+export const formatMirror = (mirror: ResourceSelectorResponse) => {
+ if (!mirror) {
+ return undefined;
+ }
+ return mirror.path;
+};
+
+// 格式化代码配置
+export const formatCodeConfig = (project?: ProjectDependency | SelectedCodeConfig) => {
+ if (!project) {
+ return undefined;
+ }
+ // 创建表单,CodeSelect 组件返回,目前有流水线、模型部署、超参数自动寻优创建时选择了代码配置
+ if ('code_path' in project) {
+ const { showValue, show_value, code_path, branch } = project;
+ return {
+ value: showValue || show_value,
+ url: getGitUrl(code_path, branch),
+ };
+ } else {
+ // 数据集和模型的代码配置
+ const { url, branch, name } = project;
+ return {
+ value: name,
+ url: getGitUrl(url, branch),
+ };
+ }
+};
+
+// 格式化训练任务(实验实例)
+export const formatTrainTask = (task?: TrainTask) => {
+ if (!task) {
+ return undefined;
+ }
+ return {
+ value: task.name,
+ url: `/pipeline/experiment/instance/${task.workflow_id}/${task.ins_id}`,
+ };
+};
+
+// 格式化数据来源
+export const formatSource = (source?: string) => {
+ if (source === DataSource.Create) {
+ return '用户上传';
+ } else if (source === DataSource.HandExport) {
+ return '手动导入';
+ } else if (source === DataSource.AutoExport) {
+ return '实验自动导入';
+ } else if (source === DataSource.LabelStudioExport) {
+ return '数据标注导入';
+ }
+ return source;
+};
+
+// 格式化字符串数组,以逗号分隔
+export const formatList = (value: string[] | null | undefined): string => {
+ if (
+ value === undefined ||
+ value === null ||
+ Array.isArray(value) === false ||
+ value.length === 0
+ ) {
+ return '--';
+ }
+ return value.join(',');
+};
+
+// 格式化布尔值
+export const formatBoolean = (value: boolean): string => {
+ return value ? '是' : '否';
+};
+
+type FormatEnumFunc = (value: string | number) => React.ReactNode;
+
+// 格式化枚举
+export const formatEnum = (
+ options: { value?: string | number | null; label?: React.ReactNode }[],
+): FormatEnumFunc => {
+ return (value: string | number) => {
+ const option = options.find((item) => item.value === value);
+ return option && option.label ? option.label : '--';
+ };
+};
diff --git a/react-ui/src/utils/index.ts b/react-ui/src/utils/index.ts
index 3deb9832..4df48c1c 100644
--- a/react-ui/src/utils/index.ts
+++ b/react-ui/src/utils/index.ts
@@ -88,7 +88,10 @@ export function camelCaseToUnderscore(obj: Record) {
}
// null to undefined
-export function nullToUndefined(obj: Record) {
+export function nullToUndefined(obj: Record | null) {
+ if (obj === null) {
+ return undefined;
+ }
if (!isPlainObject(obj)) {
return obj;
}
@@ -111,7 +114,10 @@ export function nullToUndefined(obj: Record) {
}
// undefined to null
-export function undefinedToNull(obj: Record) {
+export function undefinedToNull(obj?: Record) {
+ if (obj === undefined) {
+ return null;
+ }
if (!isPlainObject(obj)) {
return obj;
}
diff --git a/react-ui/src/utils/table.tsx b/react-ui/src/utils/table.tsx
index 0d4b1927..1a1e84c4 100644
--- a/react-ui/src/utils/table.tsx
+++ b/react-ui/src/utils/table.tsx
@@ -4,8 +4,9 @@
* @Description: Table cell 自定义 render
*/
+import { isEmpty } from '@/utils';
import { formatDate } from '@/utils/date';
-import { Tooltip } from 'antd';
+import { Tooltip, TooltipProps, Typography } from 'antd';
import dayjs from 'dayjs';
export enum TableCellValueType {
@@ -64,7 +65,7 @@ function formatArray(property?: string): TableCellFormatter {
}
function tableCellRender(
- ellipsis: boolean = false,
+ ellipsis: boolean | TooltipProps | 'auto' = false,
type: TableCellValueType = TableCellValueType.Text,
options?: TableCellValueOptions,
) {
@@ -91,41 +92,84 @@ function tableCellRender(
break;
}
- if (ellipsis && text) {
+ if (ellipsis === 'auto' && text) {
+ return renderCell(type, text, 'auto', record, options?.onClick);
+ } else if (ellipsis && text) {
+ const tooltipProps = typeof ellipsis === 'object' ? ellipsis : {};
+ const { overlayStyle, ...rest } = tooltipProps;
return (
-
- {renderCell(text, type === TableCellValueType.Link, record, options?.onClick)}
+
+ {renderCell(type, text, true, record, options?.onClick)}
);
} else {
- return renderCell(text, type === TableCellValueType.Link, record, options?.onClick);
+ return renderCell(type, text, false, record, options?.onClick);
}
};
}
function renderCell(
+ type: TableCellValueType,
text: any | undefined | null,
- isLink: boolean,
+ ellipsis: boolean | 'auto',
record: T,
onClick?: (record: T, e: React.MouseEvent) => void,
) {
- return isLink ? renderLink(text, record, onClick) : renderText(text);
-}
-
-function renderText(text: any | undefined | null) {
- return {text ?? '--'};
+ return type === TableCellValueType.Link
+ ? renderLink(text, ellipsis, record, onClick)
+ : renderText(text, ellipsis);
}
function renderLink(
text: any | undefined | null,
+ ellipsis: boolean | 'auto',
record: T,
onClick?: (record: T, e: React.MouseEvent) => void,
) {
return (
onClick?.(record, e)}>
- {text}
+ {renderText(text, ellipsis)}
);
}
+function renderText(text: any | undefined | null, ellipsis: boolean | 'auto') {
+ if (ellipsis === 'auto') {
+ return (
+
+ {!isEmpty(text) ? text : '--'}
+
+ );
+ }
+
+ return (
+
+ {!isEmpty(text) ? text : '--'}
+
+ );
+}
+
export default tableCellRender;
diff --git a/react-ui/src/utils/ui.tsx b/react-ui/src/utils/ui.tsx
index a3a214ec..d9953a3e 100644
--- a/react-ui/src/utils/ui.tsx
+++ b/react-ui/src/utils/ui.tsx
@@ -8,7 +8,16 @@ import { removeAllPageCacheState } from '@/hooks/pageCacheState';
import themes from '@/styles/theme.less';
import { type ClientInfo } from '@/types';
import { history } from '@umijs/max';
-import { Modal, Upload, message, type ModalFuncProps, type UploadFile } from 'antd';
+import {
+ Modal,
+ Upload,
+ message,
+ type FormInstance,
+ type ModalFuncProps,
+ type UploadFile,
+} from 'antd';
+import { NamePath } from 'antd/es/form/interface';
+import { isEmpty } from './index';
import { closeAllModals } from './modal';
import SessionStorage from './sessionStorage';
@@ -143,16 +152,38 @@ export const limitUploadFileType = (type: string) => {
};
/**
- * 滚动到底部
- *
- * @param {boolean} smooth - Determines if the scroll should be smooth
+ * 删除 FormList 表单项,如果表单项没有值,则直接删除,否则弹出确认框
+ * @param form From实例
+ * @param listName FormList 的 name
+ * @param name FormList 的其中一项
+ * @param remove FormList 的删除方法
+ * @param fieldNames FormList 的子项名称数组
+ * @param confirmTitle 弹出确认框的标题
*/
-export const scrollToBottom = (element: HTMLElement | null, smooth: boolean = true) => {
- if (element) {
- const optons: ScrollToOptions = {
- top: element.scrollHeight,
- behavior: smooth ? 'smooth' : 'instant',
- };
- element.scrollTo(optons);
+export const removeFormListItem = (
+ form: FormInstance,
+ listName: NamePath,
+ name: number,
+ remove: (name: number) => void,
+ fieldNames: NamePath[],
+ confirmTitle: string,
+) => {
+ const fields = fieldNames.map((item) => [listName, name, item].flat());
+ const isEmptyField = fields.every((item) => {
+ const value = form.getFieldValue(item);
+ return isEmpty(value);
+ });
+
+ if (isEmptyField) {
+ remove(name);
+ return;
}
+
+ modalConfirm({
+ title: confirmTitle,
+ content: '是否确认删除?',
+ onOk: () => {
+ remove(name);
+ },
+ });
};
diff --git a/react-ui/tsconfig.json b/react-ui/tsconfig.json
index 0afa8788..4eb2830b 100644
--- a/react-ui/tsconfig.json
+++ b/react-ui/tsconfig.json
@@ -9,7 +9,7 @@
"strict": true, // 启用所有严格类型检查选项
"forceConsistentCasingInFileNames": false, // 允许对同一文件的引用使用不一致的大小写
"module": "esnext", // 指定模块代码生成
- "moduleResolution": "node", // 使用Node.js样式解析模块
+ "moduleResolution": "bundler", // 使用bundlers样式解析模块
"isolatedModules": true, // 无条件地为未解析的文件发出导入
"resolveJsonModule": true, // 包含.json扩展名的模块
"noEmit": true, // 不发出输出(即不编译代码,只进行类型检查)
@@ -21,6 +21,7 @@
"incremental": true, // 通过读写磁盘上的文件来启用增量编译
"noFallthroughCasesInSwitch": true, // 报告switch语句中的fallthrough案例错误
"strictNullChecks": true, // 启用严格的null检查
+ "importHelpers": true,
"baseUrl": "./",
"paths": {
"@/*": ["src/*"],
diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysUser.java b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysUser.java
index fc1aa78c..4d960370 100644
--- a/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysUser.java
+++ b/ruoyi-api/ruoyi-api-system/src/main/java/com/ruoyi/system/api/domain/SysUser.java
@@ -131,6 +131,8 @@ public class SysUser extends BaseEntity {
private String gitLinkPassword;
+ private Float credit;
+
public SysUser() {
}
@@ -315,6 +317,14 @@ public class SysUser extends BaseEntity {
return gitLinkPassword;
}
+ public void setCredit(Float credit) {
+ this.credit = credit;
+ }
+
+ public Float getCredit() {
+ return credit;
+ }
+
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
@@ -339,6 +349,7 @@ public class SysUser extends BaseEntity {
.append("dept", getDept())
.append("gitLinkUsername", getGitLinkUsername())
.append("gitLinkPassword", getGitLinkPassword())
+ .append("credit", getCredit())
.toString();
}
}
diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/constant/Constant.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/constant/Constant.java
index fd86febd..f6b273b4 100644
--- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/constant/Constant.java
+++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/constant/Constant.java
@@ -49,4 +49,11 @@ public class Constant {
public final static String Asset_Type_Image = "image";
public final static String Asset_Type_Code = "code";
public final static String Asset_Type_Service = "service";
+
+ // 任务类型
+ public final static String TaskType_Dev = "dev_environment";
+ public final static String TaskType_Workflow = "workflow";
+ public final static String TaskType_Ray = "ray";
+ public final static String TaskType_ActiveLearn = "active_learn";
+ public final static String TaskType_Service = "service";
}
diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/autoML/AutoMlInsController.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/autoML/AutoMlInsController.java
index 874ec59c..d57e7dd8 100644
--- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/autoML/AutoMlInsController.java
+++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/autoML/AutoMlInsController.java
@@ -49,7 +49,7 @@ public class AutoMlInsController extends BaseController {
@PutMapping("{id}")
@ApiOperation("终止实验实例")
- public GenericsAjaxResult terminateAutoMlIns(@PathVariable("id") Long id) {
+ public GenericsAjaxResult terminateAutoMlIns(@PathVariable("id") Long id) throws Exception {
return genericsSuccess(this.autoMLInsService.terminateAutoMlIns(id));
}
diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/dataset/DatasetVersionController.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/dataset/DatasetVersionController.java
index 87715d5d..f6e7c6e5 100644
--- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/dataset/DatasetVersionController.java
+++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/dataset/DatasetVersionController.java
@@ -136,12 +136,12 @@ public class DatasetVersionController extends BaseController {
return genericsSuccess(this.datasetVersionService.deleteDatasetVersion(datasetId, version));
}
- @PostMapping("/addDatasetVersionsFromLabel")
- @ApiOperation("从数据标注添加数据集版本")
- public GenericsAjaxResult> addDatasetVersionsFromLabel(@RequestBody LabelDatasetVersionVo labelDatasetVersionVo) throws Exception {
- datasetVersionService.addDatasetVersionsFromLabel(labelDatasetVersionVo);
-
- return GenericsAjaxResult.success();
- }
+// @PostMapping("/addDatasetVersionsFromLabel")
+// @ApiOperation("从数据标注添加数据集版本")
+// public GenericsAjaxResult> addDatasetVersionsFromLabel(@RequestBody LabelDatasetVersionVo labelDatasetVersionVo) throws Exception {
+// datasetVersionService.addDatasetVersionsFromLabel(labelDatasetVersionVo);
+//
+// return GenericsAjaxResult.success();
+// }
}
diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/dataset/NewDatasetFromGitController.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/dataset/NewDatasetFromGitController.java
index 3e28b850..2c60c3e6 100644
--- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/dataset/NewDatasetFromGitController.java
+++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/dataset/NewDatasetFromGitController.java
@@ -3,6 +3,7 @@ package com.ruoyi.platform.controller.dataset;
import com.ruoyi.common.core.web.domain.AjaxResult;
import com.ruoyi.platform.domain.Dataset;
import com.ruoyi.platform.service.NewDatasetService;
+import com.ruoyi.platform.vo.LabelDatasetVersionVo;
import com.ruoyi.platform.vo.NewDatasetVo;
import com.ruoyi.platform.vo.QueryModelMetricsVo;
import io.swagger.annotations.ApiOperation;
@@ -12,9 +13,11 @@ import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
+import javax.annotation.Nullable;
import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
@RestController
@RequestMapping("newdataset")
@@ -54,14 +57,30 @@ public class NewDatasetFromGitController {
}
+ /**
+ * 新增数据集与版本新
+ *
+ * @param datasetVo 实体
+ * @return 新增结果
+ */
+ @PostMapping("/addVersionFromLabelStudio")
+ @ApiOperation("从labelsudio添加版本")
+ public AjaxResult addVersionFromLabelStudio(@RequestBody LabelDatasetVersionVo datasetVo) throws Exception {
+ return AjaxResult.success(this.newDatasetService.newCreateVersionFromLabelStudio(datasetVo));
+
+ }
+
@GetMapping("/queryDatasets")
@ApiOperation("数据集广场公开数据集分页查询,根据data_type,data_tag筛选,true公开false私有")
- public AjaxResult queryDatasets(@RequestParam("page") int page,
- @RequestParam("size") int size,
- @RequestParam(value = "is_public") Boolean isPublic,
+ public AjaxResult queryDatasets(@RequestParam(value = "page", required = false) @Nullable Integer page,
+ @RequestParam(value = "size", required = false) @Nullable Integer size,
+ @RequestParam(value = "is_public", required = false) @Nullable Boolean isPublic,
@RequestParam(value = "data_type", required = false) String dataType,
@RequestParam(value = "data_tag", required = false) String dataTag,
@RequestParam(value = "name", required = false) String name) throws Exception {
+ page = Optional.ofNullable(page).orElse(0); // 默认 page 为 0
+ size = Optional.ofNullable(size).orElse(10000); // 默认 size 为 10000
+ isPublic = Optional.ofNullable(isPublic).orElse(false);
PageRequest pageRequest = PageRequest.of(page, size);
Dataset dataset = new Dataset();
dataset.setDataTag(dataTag);
diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/devEnvironment/DevEnvironmentController.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/devEnvironment/DevEnvironmentController.java
index 0a3b608d..f7451a23 100644
--- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/devEnvironment/DevEnvironmentController.java
+++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/devEnvironment/DevEnvironmentController.java
@@ -61,7 +61,7 @@ public class DevEnvironmentController extends BaseController {
* @return 新增结果
*/
@PostMapping
- public GenericsAjaxResult add(@RequestBody DevEnvironmentVo devEnvironmentVo) {
+ public GenericsAjaxResult add(@RequestBody DevEnvironmentVo devEnvironmentVo) throws Exception {
return genericsSuccess(this.devEnvironmentService.insert(devEnvironmentVo));
}
@@ -72,7 +72,7 @@ public class DevEnvironmentController extends BaseController {
* @return 编辑结果
*/
@PutMapping
- public GenericsAjaxResult edit(@RequestBody DevEnvironment devEnvironment) {
+ public GenericsAjaxResult edit(@RequestBody DevEnvironment devEnvironment) throws Exception {
return genericsSuccess(this.devEnvironmentService.update(devEnvironment));
}
diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/experiment/ExperimentController.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/experiment/ExperimentController.java
index 7eba2b2c..1faec4d4 100644
--- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/experiment/ExperimentController.java
+++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/experiment/ExperimentController.java
@@ -102,7 +102,7 @@ public class ExperimentController extends BaseController {
* @return 删除是否成功
*/
@DeleteMapping("{id}")
- @ApiOperation("删除流水线")
+ @ApiOperation("删除实验")
public GenericsAjaxResult deleteById(@PathVariable("id") Integer id) throws Exception {
return genericsSuccess(this.experimentService.removeById(id));
}
diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/jupyter/JupyterController.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/jupyter/JupyterController.java
index 6234820b..2772cd5b 100644
--- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/jupyter/JupyterController.java
+++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/jupyter/JupyterController.java
@@ -6,9 +6,7 @@ import com.ruoyi.common.core.web.domain.GenericsAjaxResult;
import com.ruoyi.platform.domain.DevEnvironment;
import com.ruoyi.platform.service.JupyterService;
import com.ruoyi.platform.service.NewDatasetService;
-import com.ruoyi.platform.vo.NewDatasetVo;
import com.ruoyi.platform.vo.PodStatusVo;
-import com.ruoyi.platform.vo.VersionVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
@@ -19,8 +17,6 @@ import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.List;
@RestController
@RequestMapping("/jupyter")
@@ -30,6 +26,7 @@ public class JupyterController extends BaseController {
private JupyterService jupyterService;
@Resource
private NewDatasetService newDatasetService;
+
@GetMapping(value = "/getURL")
@ApiOperation("得到访问地址")
public GenericsAjaxResult getURL() throws IOException {
@@ -47,7 +44,7 @@ public class JupyterController extends BaseController {
@ApiOperation("根据开发环境id启动jupyter pod")
@ApiResponse
public GenericsAjaxResult runJupyter(@PathVariable("id") Integer id) throws Exception {
- return genericsSuccess(this.jupyterService.runJupyterService(id));
+ return genericsSuccess(this.jupyterService.runJupyterService(id));
}
@@ -68,7 +65,7 @@ public class JupyterController extends BaseController {
@ApiOperation("查询jupyter pod状态")
@ApiResponse
public GenericsAjaxResult getStatus(DevEnvironment devEnvironment) throws Exception {
- return genericsSuccess(this.jupyterService.getJupyterStatus(devEnvironment));
+ return genericsSuccess(this.jupyterService.getJupyterStatus(devEnvironment));
}
diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/minio/MinioStorageController.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/minio/MinioStorageController.java
index 66643ef4..a531b21c 100644
--- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/minio/MinioStorageController.java
+++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/minio/MinioStorageController.java
@@ -23,11 +23,20 @@ public class MinioStorageController {
@Resource
private MinioService minioService;
+
+ @GetMapping("/downloadFile")
+ @ApiOperation("下载单个文件")
+ public ResponseEntity downloadFile(@RequestParam("path") String path) throws Exception {
+ String bucketName = path.substring(0, path.indexOf("/"));
+ String prefix = path.substring(path.indexOf("/")+1,path.length());
+ return minioService.downloadFile(bucketName, prefix);
+ }
+
@GetMapping("/download")
@ApiOperation(value = "minio存储下载", notes = "minio存储下载文件为zip包")
public ResponseEntity downloadDataset(@RequestParam("path") String path) {
String bucketName = path.substring(0, path.indexOf("/"));
- String prefix = path.substring(path.indexOf("/")+1,path.length())+"/";
+ String prefix = path.substring(path.indexOf("/")+1,path.length());
return minioService.downloadZipFile(bucketName,prefix);
}
diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/ray/RayController.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/ray/RayController.java
new file mode 100644
index 00000000..719f5747
--- /dev/null
+++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/ray/RayController.java
@@ -0,0 +1,62 @@
+package com.ruoyi.platform.controller.ray;
+
+import com.ruoyi.common.core.web.controller.BaseController;
+import com.ruoyi.common.core.web.domain.GenericsAjaxResult;
+import com.ruoyi.platform.domain.Ray;
+import com.ruoyi.platform.service.RayService;
+import com.ruoyi.platform.vo.RayVo;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.web.bind.annotation.*;
+
+import javax.annotation.Resource;
+import java.io.IOException;
+
+@RestController
+@RequestMapping("ray")
+@Api("自动超参数寻优")
+public class RayController extends BaseController {
+ @Resource
+ private RayService rayService;
+
+ @GetMapping
+ @ApiOperation("分页查询")
+ public GenericsAjaxResult> queryByPage(@RequestParam("page") int page,
+ @RequestParam("size") int size,
+ @RequestParam(value = "name", required = false) String name) {
+ PageRequest pageRequest = PageRequest.of(page, size);
+ return genericsSuccess(this.rayService.queryByPage(name, pageRequest));
+ }
+
+ @PostMapping
+ @ApiOperation("新增自动超参数寻优")
+ public GenericsAjaxResult addRay(@RequestBody RayVo rayVo) throws Exception {
+ return genericsSuccess(this.rayService.save(rayVo));
+ }
+
+ @PutMapping
+ @ApiOperation("编辑自动超参数寻优")
+ public GenericsAjaxResult editRay(@RequestBody RayVo rayVo) throws Exception{
+ return genericsSuccess(this.rayService.edit(rayVo));
+ }
+
+ @GetMapping("/getRayDetail")
+ @ApiOperation("获取自动超参数寻优详细信息")
+ public GenericsAjaxResult getRayDetail(@RequestParam("id") Long id) throws IOException {
+ return genericsSuccess(this.rayService.getRayDetail(id));
+ }
+
+ @DeleteMapping("{id}")
+ @ApiOperation("删除自动超参数寻优")
+ public GenericsAjaxResult deleteRay(@PathVariable("id") Long id) {
+ return genericsSuccess(this.rayService.delete(id));
+ }
+
+ @PostMapping("/run/{id}")
+ @ApiOperation("运行自动超参数寻优实验")
+ public GenericsAjaxResult runRay(@PathVariable("id") Long id) throws Exception {
+ return genericsSuccess(this.rayService.runRayIns(id));
+ }
+}
diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/ray/RayInsController.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/ray/RayInsController.java
new file mode 100644
index 00000000..c13354a3
--- /dev/null
+++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/ray/RayInsController.java
@@ -0,0 +1,68 @@
+package com.ruoyi.platform.controller.ray;
+
+import com.ruoyi.common.core.web.controller.BaseController;
+import com.ruoyi.common.core.web.domain.GenericsAjaxResult;
+import com.ruoyi.platform.domain.RayIns;
+import com.ruoyi.platform.service.RayInsService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.v3.oas.annotations.responses.ApiResponse;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+
+import javax.annotation.Resource;
+import java.io.IOException;
+import java.util.List;
+
+@RestController
+@RequestMapping("rayIns")
+@Api("自动超参数寻优实验实例")
+public class RayInsController extends BaseController {
+ @Resource
+ private RayInsService rayInsService;
+
+ @GetMapping
+ @ApiOperation("分页查询")
+ public GenericsAjaxResult> queryByPage(Long rayId, int page, int size) throws IOException {
+ PageRequest pageRequest = PageRequest.of(page, size);
+ return genericsSuccess(this.rayInsService.queryByPage(rayId, pageRequest));
+ }
+
+ @PostMapping
+ @ApiOperation("新增实验实例")
+ public GenericsAjaxResult add(@RequestBody RayIns rayIns) {
+ return genericsSuccess(this.rayInsService.insert(rayIns));
+ }
+
+ @DeleteMapping("{id}")
+ @ApiOperation("删除实验实例")
+ public GenericsAjaxResult deleteById(@PathVariable("id") Long id) {
+ return genericsSuccess(this.rayInsService.deleteById(id));
+ }
+
+ @DeleteMapping("batchDelete")
+ @ApiOperation("批量删除实验实例")
+ public GenericsAjaxResult batchDelete(@RequestBody List ids) {
+ return genericsSuccess(this.rayInsService.batchDelete(ids));
+ }
+
+ @PutMapping("{id}")
+ @ApiOperation("终止实验实例")
+ public GenericsAjaxResult terminateRayIns(@PathVariable("id") Long id) throws Exception {
+ return genericsSuccess(this.rayInsService.terminateRayIns(id));
+ }
+
+ @GetMapping("{id}")
+ @ApiOperation("查看实验实例详情")
+ public GenericsAjaxResult getDetailById(@PathVariable("id") Long id) throws Exception {
+ return genericsSuccess(this.rayInsService.getDetailById(id));
+ }
+
+ @PostMapping("/getExpMetrics")
+ @ApiOperation("获取当前实验的指标对比地址")
+ @ApiResponse
+ public GenericsAjaxResult getExpMetrics(@RequestBody String trailIds) throws Exception {
+ return genericsSuccess(rayInsService.getExpMetrics(trailIds));
+ }
+}
diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/resources/ComputingResourceController.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/resources/ComputingResourceController.java
index aac53576..d7fd42ca 100644
--- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/resources/ComputingResourceController.java
+++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/controller/resources/ComputingResourceController.java
@@ -3,7 +3,9 @@ package com.ruoyi.platform.controller.resources;
import com.ruoyi.common.core.web.controller.BaseController;
import com.ruoyi.common.core.web.domain.GenericsAjaxResult;
import com.ruoyi.platform.domain.ComputingResource;
+import com.ruoyi.platform.domain.ResourceOccupy;
import com.ruoyi.platform.service.ComputingResourceService;
+import com.ruoyi.platform.service.ResourceOccupyService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.data.domain.Page;
@@ -11,6 +13,7 @@ import org.springframework.data.domain.PageRequest;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
+import java.util.Map;
/**
* (ComputingResource)表控制层
@@ -28,6 +31,9 @@ public class ComputingResourceController extends BaseController {
@Resource
private ComputingResourceService computingResourceService;
+ @Resource
+ private ResourceOccupyService resourceOccupyService;
+
/**
* 分页查询
*
@@ -36,12 +42,12 @@ public class ComputingResourceController extends BaseController {
*/
@GetMapping
@ApiOperation("分页查询")
- public GenericsAjaxResult> queryByPage(ComputingResource computingResource, @RequestParam("page") int page,
+ public GenericsAjaxResult> queryByPage(ComputingResource computingResource, @RequestParam("page") int page,
@RequestParam("size") int size,
- @RequestParam(value = "resource_type") String resourceType ) {
+ @RequestParam(value = "resource_type") String resourceType) {
computingResource.setComputingResource(resourceType);
- PageRequest pageRequest = PageRequest.of(page,size);
- return genericsSuccess(this.computingResourceService.queryByPage(computingResource, pageRequest));
+ PageRequest pageRequest = PageRequest.of(page, size);
+ return genericsSuccess(this.computingResourceService.queryByPage(computingResource, pageRequest));
}
/**
@@ -53,7 +59,7 @@ public class ComputingResourceController extends BaseController {
@GetMapping("{id}")
@ApiOperation("根据id查询")
public GenericsAjaxResult queryById(@PathVariable("id") Integer id) {
- return genericsSuccess(this.computingResourceService.queryById(id));
+ return genericsSuccess(this.computingResourceService.queryById(id));
}
/**
@@ -65,7 +71,7 @@ public class ComputingResourceController extends BaseController {
@PostMapping
@ApiOperation("新增计算资源")
public GenericsAjaxResult add(@RequestBody ComputingResource computingResource) {
- return genericsSuccess(this.computingResourceService.insert(computingResource));
+ return genericsSuccess(this.computingResourceService.insert(computingResource));
}
/**
@@ -77,7 +83,7 @@ public class ComputingResourceController extends BaseController {
@PutMapping
@ApiOperation("编辑计算资源")
public GenericsAjaxResult edit(@RequestBody ComputingResource computingResource) {
- return genericsSuccess(this.computingResourceService.update(computingResource));
+ return genericsSuccess(this.computingResourceService.update(computingResource));
}
/**
@@ -89,8 +95,21 @@ public class ComputingResourceController extends BaseController {
@DeleteMapping("{id}")
@ApiOperation("删除计算资源")
public GenericsAjaxResult deleteById(@PathVariable("id") Integer id) {
- return genericsSuccess(this.computingResourceService.removeById(id));
+ return genericsSuccess(this.computingResourceService.removeById(id));
}
+ @GetMapping("/resouceOccupy")
+ @ApiOperation("分页查询用户资源使用情况")
+ public GenericsAjaxResult> queryResourceOccupyByPage(@RequestParam("page") int page,
+ @RequestParam("size") int size) {
+ PageRequest pageRequest = PageRequest.of(page, size);
+ return genericsSuccess(resourceOccupyService.queryByPage(pageRequest));
+ }
+
+ @GetMapping("/credit")
+ @ApiOperation("查询用户积分使用情况")
+ public GenericsAjaxResult