diff --git a/react-ui/.storybook/main.ts b/react-ui/.storybook/main.ts
index 2eff8e2c..d0c13b18 100644
--- a/react-ui/.storybook/main.ts
+++ b/react-ui/.storybook/main.ts
@@ -16,6 +16,7 @@ const config: StorybookConfig = {
name: '@storybook/react-webpack5',
options: {},
},
+ staticDirs: ['../public'],
webpackFinal: async (config) => {
if (config.resolve) {
config.resolve.alias = {
diff --git a/react-ui/.storybook/mock/umijs.mock.tsx b/react-ui/.storybook/mock/umijs.mock.tsx
index 66ef1e5c..4f25eeb3 100644
--- a/react-ui/.storybook/mock/umijs.mock.tsx
+++ b/react-ui/.storybook/mock/umijs.mock.tsx
@@ -5,5 +5,12 @@ export const Link = ({ to, children, ...props }: any) => (
);
export const request = (url: string, options: any) => {
- return fetch(url, options).then((res) => res.json());
+ return fetch(url, options)
+ .then((res) => {
+ if (!res.ok) {
+ throw new Error(res.statusText);
+ }
+ return res;
+ })
+ .then((res) => res.json());
};
diff --git a/react-ui/.storybook/preview.tsx b/react-ui/.storybook/preview.tsx
index cbdef6b1..8dc3c3fe 100644
--- a/react-ui/.storybook/preview.tsx
+++ b/react-ui/.storybook/preview.tsx
@@ -4,8 +4,16 @@ import themes from '@/styles/theme.less';
import type { Preview } from '@storybook/react';
import { App, ConfigProvider } from 'antd';
import zhCN from 'antd/locale/zh_CN';
+import { initialize, mswLoader } from 'msw-storybook-addon';
import './storybook.css';
+/*
+ * Initializes MSW
+ * See https://github.com/mswjs/msw-storybook-addon#configuring-msw
+ * to learn how to customize it
+ */
+initialize();
+
const preview: Preview = {
parameters: {
controls: {
@@ -77,6 +85,7 @@ const preview: Preview = {
),
],
+ loaders: [mswLoader], // 👈 Add the MSW loader to all stories
};
export default preview;
diff --git a/react-ui/.storybook/storybook.css b/react-ui/.storybook/storybook.css
index 0084b0f8..6c592a3c 100644
--- a/react-ui/.storybook/storybook.css
+++ b/react-ui/.storybook/storybook.css
@@ -6,7 +6,14 @@ body,
margin: 0;
padding: 0;
overflow-y: visible;
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
- 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
- 'Noto Color Emoji';
+}
+
+.ant-input-search-large .ant-input-affix-wrapper, .ant-input-search-large .ant-input-search-button {
+ height: 46px;
+}
+
+*,
+*::before,
+*::after {
+ box-sizing: border-box;
}
diff --git a/react-ui/package.json b/react-ui/package.json
index e69739ad..c98f0286 100644
--- a/react-ui/package.json
+++ b/react-ui/package.json
@@ -119,6 +119,8 @@
"less-loader": "~12.2.0",
"lint-staged": "^13.2.0",
"mockjs": "^1.1.0",
+ "msw": "~2.7.0",
+ "msw-storybook-addon": "~2.0.4",
"prettier": "^2.8.1",
"storybook": "~8.5.3",
"swagger-ui-dist": "^4.18.2",
@@ -158,5 +160,10 @@
"CNAME",
"create-umi"
]
+ },
+ "msw": {
+ "workerDirectory": [
+ "public"
+ ]
}
-}
+}
\ No newline at end of file
diff --git a/react-ui/public/mockServiceWorker.js b/react-ui/public/mockServiceWorker.js
new file mode 100644
index 00000000..ec47a9a5
--- /dev/null
+++ b/react-ui/public/mockServiceWorker.js
@@ -0,0 +1,307 @@
+/* eslint-disable */
+/* tslint:disable */
+
+/**
+ * Mock Service Worker.
+ * @see https://github.com/mswjs/msw
+ * - Please do NOT modify this file.
+ * - Please do NOT serve this file on production.
+ */
+
+const PACKAGE_VERSION = '2.7.0'
+const INTEGRITY_CHECKSUM = '00729d72e3b82faf54ca8b9621dbb96f'
+const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
+const activeClientIds = new Set()
+
+self.addEventListener('install', function () {
+ self.skipWaiting()
+})
+
+self.addEventListener('activate', function (event) {
+ event.waitUntil(self.clients.claim())
+})
+
+self.addEventListener('message', async function (event) {
+ const clientId = event.source.id
+
+ if (!clientId || !self.clients) {
+ return
+ }
+
+ const client = await self.clients.get(clientId)
+
+ if (!client) {
+ return
+ }
+
+ const allClients = await self.clients.matchAll({
+ type: 'window',
+ })
+
+ switch (event.data) {
+ case 'KEEPALIVE_REQUEST': {
+ sendToClient(client, {
+ type: 'KEEPALIVE_RESPONSE',
+ })
+ break
+ }
+
+ case 'INTEGRITY_CHECK_REQUEST': {
+ sendToClient(client, {
+ type: 'INTEGRITY_CHECK_RESPONSE',
+ payload: {
+ packageVersion: PACKAGE_VERSION,
+ checksum: INTEGRITY_CHECKSUM,
+ },
+ })
+ break
+ }
+
+ case 'MOCK_ACTIVATE': {
+ activeClientIds.add(clientId)
+
+ sendToClient(client, {
+ type: 'MOCKING_ENABLED',
+ payload: {
+ client: {
+ id: client.id,
+ frameType: client.frameType,
+ },
+ },
+ })
+ break
+ }
+
+ case 'MOCK_DEACTIVATE': {
+ activeClientIds.delete(clientId)
+ break
+ }
+
+ case 'CLIENT_CLOSED': {
+ activeClientIds.delete(clientId)
+
+ const remainingClients = allClients.filter((client) => {
+ return client.id !== clientId
+ })
+
+ // Unregister itself when there are no more clients
+ if (remainingClients.length === 0) {
+ self.registration.unregister()
+ }
+
+ break
+ }
+ }
+})
+
+self.addEventListener('fetch', function (event) {
+ const { request } = event
+
+ // Bypass navigation requests.
+ if (request.mode === 'navigate') {
+ return
+ }
+
+ // Opening the DevTools triggers the "only-if-cached" request
+ // that cannot be handled by the worker. Bypass such requests.
+ if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') {
+ return
+ }
+
+ // Bypass all requests when there are no active clients.
+ // Prevents the self-unregistered worked from handling requests
+ // after it's been deleted (still remains active until the next reload).
+ if (activeClientIds.size === 0) {
+ return
+ }
+
+ // Generate unique request ID.
+ const requestId = crypto.randomUUID()
+ event.respondWith(handleRequest(event, requestId))
+})
+
+async function handleRequest(event, requestId) {
+ const client = await resolveMainClient(event)
+ const response = await getResponse(event, client, requestId)
+
+ // Send back the response clone for the "response:*" life-cycle events.
+ // Ensure MSW is active and ready to handle the message, otherwise
+ // this message will pend indefinitely.
+ if (client && activeClientIds.has(client.id)) {
+ ;(async function () {
+ const responseClone = response.clone()
+
+ sendToClient(
+ client,
+ {
+ type: 'RESPONSE',
+ payload: {
+ requestId,
+ isMockedResponse: IS_MOCKED_RESPONSE in response,
+ type: responseClone.type,
+ status: responseClone.status,
+ statusText: responseClone.statusText,
+ body: responseClone.body,
+ headers: Object.fromEntries(responseClone.headers.entries()),
+ },
+ },
+ [responseClone.body],
+ )
+ })()
+ }
+
+ return response
+}
+
+// Resolve the main client for the given event.
+// Client that issues a request doesn't necessarily equal the client
+// that registered the worker. It's with the latter the worker should
+// communicate with during the response resolving phase.
+async function resolveMainClient(event) {
+ const client = await self.clients.get(event.clientId)
+
+ if (activeClientIds.has(event.clientId)) {
+ return client
+ }
+
+ if (client?.frameType === 'top-level') {
+ return client
+ }
+
+ const allClients = await self.clients.matchAll({
+ type: 'window',
+ })
+
+ return allClients
+ .filter((client) => {
+ // Get only those clients that are currently visible.
+ return client.visibilityState === 'visible'
+ })
+ .find((client) => {
+ // Find the client ID that's recorded in the
+ // set of clients that have registered the worker.
+ return activeClientIds.has(client.id)
+ })
+}
+
+async function getResponse(event, client, requestId) {
+ const { request } = event
+
+ // Clone the request because it might've been already used
+ // (i.e. its body has been read and sent to the client).
+ const requestClone = request.clone()
+
+ function passthrough() {
+ // Cast the request headers to a new Headers instance
+ // so the headers can be manipulated with.
+ const headers = new Headers(requestClone.headers)
+
+ // Remove the "accept" header value that marked this request as passthrough.
+ // This prevents request alteration and also keeps it compliant with the
+ // user-defined CORS policies.
+ const acceptHeader = headers.get('accept')
+ if (acceptHeader) {
+ const values = acceptHeader.split(',').map((value) => value.trim())
+ const filteredValues = values.filter(
+ (value) => value !== 'msw/passthrough',
+ )
+
+ if (filteredValues.length > 0) {
+ headers.set('accept', filteredValues.join(', '))
+ } else {
+ headers.delete('accept')
+ }
+ }
+
+ return fetch(requestClone, { headers })
+ }
+
+ // Bypass mocking when the client is not active.
+ if (!client) {
+ return passthrough()
+ }
+
+ // Bypass initial page load requests (i.e. static assets).
+ // The absence of the immediate/parent client in the map of the active clients
+ // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet
+ // and is not ready to handle requests.
+ if (!activeClientIds.has(client.id)) {
+ return passthrough()
+ }
+
+ // Notify the client that a request has been intercepted.
+ const requestBuffer = await request.arrayBuffer()
+ const clientMessage = await sendToClient(
+ client,
+ {
+ type: 'REQUEST',
+ payload: {
+ id: requestId,
+ url: request.url,
+ mode: request.mode,
+ method: request.method,
+ headers: Object.fromEntries(request.headers.entries()),
+ cache: request.cache,
+ credentials: request.credentials,
+ destination: request.destination,
+ integrity: request.integrity,
+ redirect: request.redirect,
+ referrer: request.referrer,
+ referrerPolicy: request.referrerPolicy,
+ body: requestBuffer,
+ keepalive: request.keepalive,
+ },
+ },
+ [requestBuffer],
+ )
+
+ switch (clientMessage.type) {
+ case 'MOCK_RESPONSE': {
+ return respondWithMock(clientMessage.data)
+ }
+
+ case 'PASSTHROUGH': {
+ return passthrough()
+ }
+ }
+
+ return passthrough()
+}
+
+function sendToClient(client, message, transferrables = []) {
+ return new Promise((resolve, reject) => {
+ const channel = new MessageChannel()
+
+ channel.port1.onmessage = (event) => {
+ if (event.data && event.data.error) {
+ return reject(event.data.error)
+ }
+
+ resolve(event.data)
+ }
+
+ client.postMessage(
+ message,
+ [channel.port2].concat(transferrables.filter(Boolean)),
+ )
+ })
+}
+
+async function respondWithMock(response) {
+ // Setting response status code to 0 is a no-op.
+ // However, when responding with a "Response.error()", the produced Response
+ // instance will have status code set to 0. Since it's not possible to create
+ // a Response instance with status code 0, handle that use-case separately.
+ if (response.status === 0) {
+ return Response.error()
+ }
+
+ const mockedResponse = new Response(response.body, response)
+
+ Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, {
+ value: true,
+ enumerable: true,
+ })
+
+ return mockedResponse
+}
diff --git a/react-ui/src/components/CodeSelectorModal/index.less b/react-ui/src/components/CodeSelectorModal/index.less
index cb77da6d..82d73ff8 100644
--- a/react-ui/src/components/CodeSelectorModal/index.less
+++ b/react-ui/src/components/CodeSelectorModal/index.less
@@ -1,4 +1,4 @@
-.code-selector {
+.kf-code-selector-modal {
width: 100%;
height: 100%;
@@ -6,31 +6,6 @@
width: 100%;
}
- :global {
- .ant-input-affix-wrapper {
- border-radius: 23px !important;
- .ant-input-prefix {
- margin-inline-end: 12px;
- }
- .ant-input-suffix {
- margin-inline-end: 12px;
- }
- .ant-input-clear-icon {
- font-size: 16px;
- }
- }
-
- .ant-input-group-addon {
- display: none;
- }
-
- .ant-pagination {
- .ant-select-single {
- height: 32px !important;
- }
- }
- }
-
&__content {
display: flex;
flex-direction: row;
@@ -47,4 +22,28 @@
&__empty {
padding-top: 40px;
}
+
+ // 覆盖 antd 样式
+ .ant-input-affix-wrapper {
+ border-radius: 23px !important;
+ .ant-input-prefix {
+ margin-inline-end: 12px;
+ }
+ .ant-input-suffix {
+ margin-inline-end: 12px;
+ }
+ .ant-input-clear-icon {
+ font-size: 16px;
+ }
+ }
+
+ .ant-input-group-addon {
+ display: none;
+ }
+
+ .ant-pagination {
+ .ant-select-single {
+ height: 32px !important;
+ }
+ }
}
diff --git a/react-ui/src/components/CodeSelectorModal/index.tsx b/react-ui/src/components/CodeSelectorModal/index.tsx
index 6a965fd7..6426e8c7 100644
--- a/react-ui/src/components/CodeSelectorModal/index.tsx
+++ b/react-ui/src/components/CodeSelectorModal/index.tsx
@@ -13,7 +13,7 @@ import type { ModalProps, PaginationProps } from 'antd';
import { Empty, Input, Pagination } from 'antd';
import { useEffect, useState } from 'react';
import CodeConfigItem from '../CodeConfigItem';
-import styles from './index.less';
+import './index.less';
export { type CodeConfigData };
@@ -80,9 +80,9 @@ function CodeSelectorModal({ onOk, ...rest }: CodeSelectorModalProps) {
footer={null}
destroyOnClose
>
-
+
{dataList?.length !== 0 ? (
<>
-
+
{dataList?.map((item) => (
))}
@@ -116,7 +116,7 @@ function CodeSelectorModal({ onOk, ...rest }: CodeSelectorModalProps) {
/>
>
) : (
-
+
)}
diff --git a/react-ui/src/components/InfoGroup/InfoGroupTitle.less b/react-ui/src/components/InfoGroup/InfoGroupTitle.less
index 26ed375a..b8aff97c 100644
--- a/react-ui/src/components/InfoGroup/InfoGroupTitle.less
+++ b/react-ui/src/components/InfoGroup/InfoGroupTitle.less
@@ -1,5 +1,4 @@
.kf-info-group-title {
- box-sizing: border-box;
width: 100%;
height: 56px;
padding: 0 @content-padding;
diff --git a/react-ui/src/components/PageTitle/index.tsx b/react-ui/src/components/PageTitle/index.tsx
index ea8a65de..2703e032 100644
--- a/react-ui/src/components/PageTitle/index.tsx
+++ b/react-ui/src/components/PageTitle/index.tsx
@@ -9,7 +9,7 @@ import './index.less';
type PageTitleProps = {
/** 标题 */
- title: string;
+ title: React.ReactNode;
/** 自定义类名 */
className?: string;
/** 自定义样式 */
diff --git a/react-ui/src/components/ParameterInput/index.tsx b/react-ui/src/components/ParameterInput/index.tsx
index be56c96d..99bb78af 100644
--- a/react-ui/src/components/ParameterInput/index.tsx
+++ b/react-ui/src/components/ParameterInput/index.tsx
@@ -26,18 +26,34 @@ export type ParameterInputObject = {
export type ParameterInputValue = ParameterInputObject | string;
export interface ParameterInputProps {
+ /** 值,可以是字符串,也可以是 ParameterInputObject 对象 */
value?: ParameterInputValue;
+ /**
+ * 值变化时的回调
+ * @param value 值,可以是字符串,也可以是 ParameterInputObject 对象
+ */
onChange?: (value?: ParameterInputValue) => void;
+ /** 点击时的回调 */
onClick?: () => void;
+ /** 删除时的回调 */
onRemove?: () => void;
+ /** 是否可以手动输入 */
canInput?: boolean;
+ /** 是否是文本框 */
textArea?: boolean;
+ /** 占位符 */
placeholder?: string;
+ /** 是否允许清除 */
allowClear?: boolean;
+ /** 自定义类名 */
className?: string;
+ /** 自定义样式 */
style?: React.CSSProperties;
+ /** 大小 */
size?: 'middle' | 'small' | 'large';
+ /** 是否禁用 */
disabled?: boolean;
+ /** 元素 id */
id?: string;
}
diff --git a/react-ui/src/components/ResourceSelect/index.tsx b/react-ui/src/components/ResourceSelect/index.tsx
index 6e0179d4..bc8d08cf 100644
--- a/react-ui/src/components/ResourceSelect/index.tsx
+++ b/react-ui/src/components/ResourceSelect/index.tsx
@@ -21,14 +21,16 @@ export { requiredValidator, type ParameterInputObject } from '../ParameterInput'
export { ResourceSelectorType, selectorTypeConfig, type ResourceSelectorResponse };
type ResourceSelectProps = {
+ /** 类型,数据集、模型、镜像 */
type: ResourceSelectorType;
} & ParameterInputProps;
-// 获取选择数据集、模型后面按钮 icon
+// 获取选择数据集、模型、镜像后面按钮 icon
const getSelectBtnIcon = (type: ResourceSelectorType) => {
return
;
};
+/** 数据集、模型、镜像选择表单组件 */
function ResourceSelect({ type, value, onChange, disabled, ...rest }: ResourceSelectProps) {
const [selectedResource, setSelectedResource] = useState
(
undefined,
diff --git a/react-ui/src/components/ResourceSelectorModal/index.tsx b/react-ui/src/components/ResourceSelectorModal/index.tsx
index 1a62819c..e65f1f02 100644
--- a/react-ui/src/components/ResourceSelectorModal/index.tsx
+++ b/react-ui/src/components/ResourceSelectorModal/index.tsx
@@ -16,7 +16,7 @@ import { ResourceSelectorType, selectorTypeConfig } from './config';
import styles from './index.less';
export { ResourceSelectorType, selectorTypeConfig };
-// 选择数据集\模型\镜像的返回类型
+// 选择数据集、模型、镜像的返回类型
export type ResourceSelectorResponse = {
activeTab: CommonTabKeys; // 是我的还是公开的
id: string; // 数据集\模型\镜像 id
@@ -28,10 +28,18 @@ export type ResourceSelectorResponse = {
};
export interface ResourceSelectorModalProps extends Omit {
- type: ResourceSelectorType; // 数据集\模型\镜像
+ /** 类型,数据集、模型、镜像 */
+ type: ResourceSelectorType;
+ /** 默认展开的节点 */
defaultExpandedKeys?: React.Key[];
+ /** 默认展开的节点 */
defaultCheckedKeys?: React.Key[];
+ /** 默认激活的 Tab */
defaultActiveTab?: CommonTabKeys;
+ /**
+ * 确认回调
+ * @param params 选择的数据
+ */
onOk?: (params: ResourceSelectorResponse | undefined) => void;
}
@@ -61,6 +69,7 @@ const getIdAndVersion = (versionKey: string) => {
};
};
+/** 选择 数据集、模型、镜像 弹框 */
function ResourceSelectorModal({
type,
defaultExpandedKeys = [],
diff --git a/react-ui/src/global.less b/react-ui/src/global.less
index fbbfa34d..9944c70e 100644
--- a/react-ui/src/global.less
+++ b/react-ui/src/global.less
@@ -5,7 +5,7 @@ body,
height: 100%;
margin: 0;
padding: 0;
- overflow-y: hidden;
+ overflow-y: visible;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
'Noto Color Emoji';
diff --git a/react-ui/src/stories/BasicInfo.stories.tsx b/react-ui/src/stories/BasicInfo.stories.tsx
index e6e71255..6fece79a 100644
--- a/react-ui/src/stories/BasicInfo.stories.tsx
+++ b/react-ui/src/stories/BasicInfo.stories.tsx
@@ -1,20 +1,9 @@
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';
-const formatList = (value: string[] | null | undefined): string => {
- if (
- value === undefined ||
- value === null ||
- Array.isArray(value) === false ||
- value.length === 0
- ) {
- return '--';
- }
- return value.join(',');
-};
-
// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export
const meta = {
title: 'Components/BasicInfo',
diff --git a/react-ui/src/stories/CodeSelect.stories.tsx b/react-ui/src/stories/CodeSelect.stories.tsx
index 3f52e159..8be5b620 100644
--- a/react-ui/src/stories/CodeSelect.stories.tsx
+++ b/react-ui/src/stories/CodeSelect.stories.tsx
@@ -1,5 +1,9 @@
import CodeSelect from '@/components/CodeSelect';
import type { Meta, StoryObj } from '@storybook/react';
+import { fn } from '@storybook/test';
+import { 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 = {
@@ -8,6 +12,13 @@ const meta = {
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'],
@@ -16,7 +27,7 @@ const meta = {
// 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() },
+ args: { onChange: fn() },
} satisfies Meta;
export default meta;
@@ -24,5 +35,22 @@ type Story = StoryObj;
// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
export const Primary: Story = {
- args: {},
+ render: ({ onChange }) => {
+ return (
+
+ );
+ },
};
diff --git a/react-ui/src/stories/CodeSelectorModal.stories.tsx b/react-ui/src/stories/CodeSelectorModal.stories.tsx
index 2df1b5fa..deab369c 100644
--- a/react-ui/src/stories/CodeSelectorModal.stories.tsx
+++ b/react-ui/src/stories/CodeSelectorModal.stories.tsx
@@ -4,6 +4,8 @@ import { useArgs } from '@storybook/preview-api';
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 = {
@@ -12,15 +14,19 @@ const meta = {
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' },
- title: {
- description: '标题',
- },
open: {
description: '对话框是否可见',
},
@@ -37,17 +43,19 @@ export const Primary: Story = {
args: {
open: false,
},
- render: function Render(args) {
+ render: function Render({ onOk, onCancel, ...args }) {
const [{ open }, updateArgs] = useArgs();
function onClick() {
updateArgs({ open: true });
}
- function onOk() {
+ function onModalOk(res: any) {
updateArgs({ open: false });
+ onOk?.(res);
}
- function onCancel() {
+ function onModalCancel() {
updateArgs({ open: false });
+ onCancel?.();
}
return (
@@ -55,27 +63,27 @@ export const Primary: Story = {
-
+
>
);
},
};
-const OpenModalByFunction = () => {
- const handleOnChange = () => {
- const { close } = openAntdModal(CodeSelectorModal, {
- onOk: () => {
- close();
- },
- });
- };
- return (
-
- );
-};
-
export const OpenInFunction: Story = {
- render: () => ,
+ render: function Render(args) {
+ const handleOnChange = () => {
+ const { close } = openAntdModal(CodeSelectorModal, {
+ onOk: (res) => {
+ const { onOk } = args;
+ onOk?.(res);
+ close();
+ },
+ });
+ };
+ return (
+
+ );
+ },
};
diff --git a/react-ui/src/stories/KFModal.stories.tsx b/react-ui/src/stories/KFModal.stories.tsx
index 3efe5e99..208ced34 100644
--- a/react-ui/src/stories/KFModal.stories.tsx
+++ b/react-ui/src/stories/KFModal.stories.tsx
@@ -25,6 +25,10 @@ const meta = {
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() },
@@ -41,17 +45,19 @@ export const Primary: Story = {
open: false,
children: '这是一个模态框',
},
- render: function Render(args) {
+ render: function Render({ onOk, onCancel, ...args }) {
const [{ open }, updateArgs] = useArgs();
function onClick() {
updateArgs({ open: true });
}
- function onOk() {
+ function onModalOk() {
updateArgs({ open: false });
+ onOk?.();
}
- function onCancel() {
+ function onModalCancel() {
updateArgs({ open: false });
+ onCancel?.();
}
return (
@@ -59,30 +65,28 @@ export const Primary: Story = {
-
+
>
);
},
};
-const OpenModalByFunction = () => {
- const handleOnChange = () => {
- const { close } = openAntdModal(KFModal, {
- title: '创建实验',
- image: CreateExperiment,
- children: '这是一个模态框',
- onOk: () => {
- close();
- },
- });
- };
- return (
-
- );
-};
-
export const OpenInFunction: Story = {
- render: () => ,
+ render: function Render() {
+ const handleOnChange = () => {
+ const { close } = openAntdModal(KFModal, {
+ title: '创建实验',
+ image: CreateExperiment,
+ children: '这是一个模态框',
+ onOk: () => {
+ close();
+ },
+ });
+ };
+ return (
+
+ );
+ },
};
diff --git a/react-ui/src/stories/MenuIconSelector.stories.tsx b/react-ui/src/stories/MenuIconSelector.stories.tsx
index 45088835..e2950549 100644
--- a/react-ui/src/stories/MenuIconSelector.stories.tsx
+++ b/react-ui/src/stories/MenuIconSelector.stories.tsx
@@ -34,17 +34,19 @@ export const Primary: Story = {
selectedIcon: 'manual-icon',
open: false,
},
- render: function Render(args) {
+ render: function Render({ onOk, onCancel, ...args }) {
const [{ open, selectedIcon }, updateArgs] = useArgs();
function onClick() {
updateArgs({ open: true });
}
- function onOk(value: string) {
+ function onModalOk(value: string) {
updateArgs({ selectedIcon: value, open: false });
+ onOk?.(value);
}
- function onCancel() {
+ function onModalCancel() {
updateArgs({ open: false });
+ onCancel?.();
}
return (
@@ -56,8 +58,8 @@ export const Primary: Story = {
{...args}
open={open}
selectedIcon={selectedIcon}
- onOk={onOk}
- onCancel={onCancel}
+ onOk={onModalOk}
+ onCancel={onModalCancel}
/>
>
);
diff --git a/react-ui/src/stories/ParameterInput.stories.tsx b/react-ui/src/stories/ParameterInput.stories.tsx
new file mode 100644
index 00000000..9d9525e2
--- /dev/null
+++ b/react-ui/src/stories/ParameterInput.stories.tsx
@@ -0,0 +1,108 @@
+import ParameterInput, { ParameterInputValue } from '@/components/ParameterInput';
+import type { Meta, StoryObj } from '@storybook/react';
+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: { 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 Input: Story = {
+ args: {
+ placeholder: '请输入工作目录',
+ style: { width: 300 },
+ canInput: true,
+ textArea: false,
+ allowClear: true,
+ size: 'large',
+ },
+};
+
+export const Select: Story = {
+ args: {
+ placeholder: '请输入工作目录',
+ style: { width: 300 },
+ value: 'storybook',
+ canInput: false,
+ size: 'large',
+ },
+};
+
+export const SelectWithObjctValue: Story = {
+ args: {
+ placeholder: '请输入工作目录',
+ style: { width: 300 },
+ value: {
+ value: 'storybook',
+ showValue: 'storybook',
+ fromSelect: true,
+ },
+ canInput: true,
+ size: 'large',
+ },
+};
+
+export const Disabled: Story = {
+ args: {
+ placeholder: '请输入工作目录',
+ style: { width: 300 },
+ value: {
+ value: 'storybook',
+ showValue: 'storybook',
+ fromSelect: true,
+ },
+ canInput: true,
+ size: 'large',
+ disabled: true,
+ },
+};
+
+export const Application: Story = {
+ args: {
+ placeholder: '请输入工作目录',
+ style: { width: 300 },
+ canInput: true,
+ size: 'large',
+ },
+ render: function Render(args) {
+ const [value, setValue] = useState('');
+
+ const onClick = () => {
+ setValue({
+ value: 'storybook',
+ showValue: 'storybook',
+ fromSelect: true,
+ });
+ };
+ return (
+ <>
+ setValue(value)}
+ >
+
+ >
+ );
+ },
+};
diff --git a/react-ui/src/stories/ParameterSelect.stories.tsx b/react-ui/src/stories/ParameterSelect.stories.tsx
deleted file mode 100644
index 4a1907ee..00000000
--- a/react-ui/src/stories/ParameterSelect.stories.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import MirrorBasic from '@/assets/img/mirror-basic.png';
-import ParameterSelect from '@/components/ParameterSelect';
-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/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',
- },
- // 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/ResourceSelect.stories.tsx b/react-ui/src/stories/ResourceSelect.stories.tsx
new file mode 100644
index 00000000..b9db1633
--- /dev/null
+++ b/react-ui/src/stories/ResourceSelect.stories.tsx
@@ -0,0 +1,135 @@
+import ResourceSelect, {
+ requiredValidator,
+ ResourceSelectorType,
+} from '@/components/ResourceSelect';
+import type { Meta, StoryObj } from '@storybook/react';
+import { fn } from '@storybook/test';
+import { 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: {
+ // 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: {
+ type: ResourceSelectorType.Dataset,
+ },
+ render: ({ onChange }) => {
+ return (
+
+ );
+ },
+};
diff --git a/react-ui/src/stories/ResourceSelectorModal.stories.tsx b/react-ui/src/stories/ResourceSelectorModal.stories.tsx
new file mode 100644
index 00000000..e2e0ebd2
--- /dev/null
+++ b/react-ui/src/stories/ResourceSelectorModal.stories.tsx
@@ -0,0 +1,216 @@
+import ResourceSelectorModal, { ResourceSelectorType } from '@/components/ResourceSelectorModal';
+import { openAntdModal } from '@/utils/modal';
+import { useArgs } from '@storybook/preview-api';
+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',
+ },
+ // 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,
+ open: false,
+ },
+ parameters: {
+ 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);
+ }),
+ ],
+ },
+ },
+ render: function Render({ onOk, onCancel, ...args }) {
+ const [{ open }, updateArgs] = useArgs();
+ function onClick() {
+ updateArgs({ open: true });
+ }
+ function onModalOk(res: any) {
+ updateArgs({ open: false });
+ onOk?.(res);
+ }
+
+ function onModalCancel() {
+ updateArgs({ open: false });
+ onCancel?.();
+ }
+
+ return (
+ <>
+
+
+ >
+ );
+ },
+};
+
+export const Model: Story = {
+ args: {
+ type: ResourceSelectorType.Model,
+ open: false,
+ },
+ parameters: {
+ msw: {
+ handlers: [
+ 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);
+ }),
+ ],
+ },
+ },
+ render: function Render({ onOk, onCancel, ...args }) {
+ const [{ open }, updateArgs] = useArgs();
+ function onClick() {
+ updateArgs({ open: true });
+ }
+ function onModalOk(res: any) {
+ updateArgs({ open: false });
+ onOk?.(res);
+ }
+
+ function onModalCancel() {
+ updateArgs({ open: false });
+ onCancel?.();
+ }
+
+ return (
+ <>
+
+
+ >
+ );
+ },
+};
+
+export const Mirror: Story = {
+ args: {
+ type: ResourceSelectorType.Mirror,
+ open: false,
+ },
+ parameters: {
+ msw: {
+ handlers: [
+ http.get('/api/mmp/image', () => {
+ return HttpResponse.json(mirrorListData);
+ }),
+ http.get('/api/mmp/imageVersion', () => {
+ return HttpResponse.json(mirrorVerionData);
+ }),
+ ],
+ },
+ },
+ render: function Render({ onOk, onCancel, ...args }) {
+ const [{ open }, updateArgs] = useArgs();
+ function onClick() {
+ updateArgs({ open: true });
+ }
+ function onModalOk(res: any) {
+ updateArgs({ open: false });
+ onOk?.(res);
+ }
+
+ function onModalCancel() {
+ updateArgs({ open: false });
+ onCancel?.();
+ }
+
+ return (
+ <>
+
+
+ >
+ );
+ },
+};
+
+export const OpenInFunction: Story = {
+ args: {
+ type: ResourceSelectorType.Mirror,
+ },
+ parameters: {
+ msw: {
+ handlers: [
+ http.get('/api/mmp/image', () => {
+ return HttpResponse.json(mirrorListData);
+ }),
+ http.get('/api/mmp/imageVersion', () => {
+ return HttpResponse.json(mirrorVerionData);
+ }),
+ ],
+ },
+ },
+ render: function Render(args) {
+ const handleOnChange = () => {
+ const { close } = openAntdModal(ResourceSelectorModal, {
+ type: args.type,
+ onOk: (res) => {
+ const { onOk } = args;
+ onOk?.(res);
+ close();
+ },
+ });
+ };
+ return (
+
+ );
+ },
+};
diff --git a/react-ui/src/stories/mockData.ts b/react-ui/src/stories/mockData.ts
new file mode 100644
index 00000000..3d910b06
--- /dev/null
+++ b/react-ui/src/stories/mockData.ts
@@ -0,0 +1,548 @@
+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,
+ },
+};