| @@ -16,6 +16,7 @@ const config: StorybookConfig = { | |||||
| name: '@storybook/react-webpack5', | name: '@storybook/react-webpack5', | ||||
| options: {}, | options: {}, | ||||
| }, | }, | ||||
| staticDirs: ['../public'], | |||||
| webpackFinal: async (config) => { | webpackFinal: async (config) => { | ||||
| if (config.resolve) { | if (config.resolve) { | ||||
| config.resolve.alias = { | config.resolve.alias = { | ||||
| @@ -5,5 +5,12 @@ export const Link = ({ to, children, ...props }: any) => ( | |||||
| ); | ); | ||||
| export const request = (url: string, options: 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()); | |||||
| }; | }; | ||||
| @@ -4,8 +4,16 @@ import themes from '@/styles/theme.less'; | |||||
| import type { Preview } from '@storybook/react'; | import type { Preview } from '@storybook/react'; | ||||
| import { App, ConfigProvider } from 'antd'; | import { App, ConfigProvider } from 'antd'; | ||||
| import zhCN from 'antd/locale/zh_CN'; | import zhCN from 'antd/locale/zh_CN'; | ||||
| import { initialize, mswLoader } from 'msw-storybook-addon'; | |||||
| import './storybook.css'; | 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 = { | const preview: Preview = { | ||||
| parameters: { | parameters: { | ||||
| controls: { | controls: { | ||||
| @@ -77,6 +85,7 @@ const preview: Preview = { | |||||
| </ConfigProvider> | </ConfigProvider> | ||||
| ), | ), | ||||
| ], | ], | ||||
| loaders: [mswLoader], // 👈 Add the MSW loader to all stories | |||||
| }; | }; | ||||
| export default preview; | export default preview; | ||||
| @@ -6,7 +6,14 @@ body, | |||||
| margin: 0; | margin: 0; | ||||
| padding: 0; | padding: 0; | ||||
| overflow-y: visible; | 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; | |||||
| } | } | ||||
| @@ -119,6 +119,8 @@ | |||||
| "less-loader": "~12.2.0", | "less-loader": "~12.2.0", | ||||
| "lint-staged": "^13.2.0", | "lint-staged": "^13.2.0", | ||||
| "mockjs": "^1.1.0", | "mockjs": "^1.1.0", | ||||
| "msw": "~2.7.0", | |||||
| "msw-storybook-addon": "~2.0.4", | |||||
| "prettier": "^2.8.1", | "prettier": "^2.8.1", | ||||
| "storybook": "~8.5.3", | "storybook": "~8.5.3", | ||||
| "swagger-ui-dist": "^4.18.2", | "swagger-ui-dist": "^4.18.2", | ||||
| @@ -158,5 +160,10 @@ | |||||
| "CNAME", | "CNAME", | ||||
| "create-umi" | "create-umi" | ||||
| ] | ] | ||||
| }, | |||||
| "msw": { | |||||
| "workerDirectory": [ | |||||
| "public" | |||||
| ] | |||||
| } | } | ||||
| } | |||||
| } | |||||
| @@ -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 | |||||
| } | |||||
| @@ -1,4 +1,4 @@ | |||||
| .code-selector { | |||||
| .kf-code-selector-modal { | |||||
| width: 100%; | width: 100%; | ||||
| height: 100%; | height: 100%; | ||||
| @@ -6,31 +6,6 @@ | |||||
| width: 100%; | 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 { | &__content { | ||||
| display: flex; | display: flex; | ||||
| flex-direction: row; | flex-direction: row; | ||||
| @@ -47,4 +22,28 @@ | |||||
| &__empty { | &__empty { | ||||
| padding-top: 40px; | 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; | |||||
| } | |||||
| } | |||||
| } | } | ||||
| @@ -13,7 +13,7 @@ import type { ModalProps, PaginationProps } from 'antd'; | |||||
| import { Empty, Input, Pagination } from 'antd'; | import { Empty, Input, Pagination } from 'antd'; | ||||
| import { useEffect, useState } from 'react'; | import { useEffect, useState } from 'react'; | ||||
| import CodeConfigItem from '../CodeConfigItem'; | import CodeConfigItem from '../CodeConfigItem'; | ||||
| import styles from './index.less'; | |||||
| import './index.less'; | |||||
| export { type CodeConfigData }; | export { type CodeConfigData }; | ||||
| @@ -80,9 +80,9 @@ function CodeSelectorModal({ onOk, ...rest }: CodeSelectorModalProps) { | |||||
| footer={null} | footer={null} | ||||
| destroyOnClose | destroyOnClose | ||||
| > | > | ||||
| <div className={styles['code-selector']}> | |||||
| <div className="kf-code-selector-modal"> | |||||
| <Input.Search | <Input.Search | ||||
| className={styles['code-selector__search']} | |||||
| className="kf-code-selector-modal__search" | |||||
| placeholder="按代码仓库名称筛选" | placeholder="按代码仓库名称筛选" | ||||
| allowClear | allowClear | ||||
| onSearch={handleSearch} | onSearch={handleSearch} | ||||
| @@ -99,7 +99,7 @@ function CodeSelectorModal({ onOk, ...rest }: CodeSelectorModalProps) { | |||||
| /> | /> | ||||
| {dataList?.length !== 0 ? ( | {dataList?.length !== 0 ? ( | ||||
| <> | <> | ||||
| <div className={styles['code-selector__content']}> | |||||
| <div className="kf-code-selector-modal__content"> | |||||
| {dataList?.map((item) => ( | {dataList?.map((item) => ( | ||||
| <CodeConfigItem item={item} key={item.id} onClick={handleClick} /> | <CodeConfigItem item={item} key={item.id} onClick={handleClick} /> | ||||
| ))} | ))} | ||||
| @@ -116,7 +116,7 @@ function CodeSelectorModal({ onOk, ...rest }: CodeSelectorModalProps) { | |||||
| /> | /> | ||||
| </> | </> | ||||
| ) : ( | ) : ( | ||||
| <div className={styles['code-selector__empty']}> | |||||
| <div className="kf-code-selector-modal__empty"> | |||||
| <Empty image={Empty.PRESENTED_IMAGE_SIMPLE}></Empty> | <Empty image={Empty.PRESENTED_IMAGE_SIMPLE}></Empty> | ||||
| </div> | </div> | ||||
| )} | )} | ||||
| @@ -1,5 +1,4 @@ | |||||
| .kf-info-group-title { | .kf-info-group-title { | ||||
| box-sizing: border-box; | |||||
| width: 100%; | width: 100%; | ||||
| height: 56px; | height: 56px; | ||||
| padding: 0 @content-padding; | padding: 0 @content-padding; | ||||
| @@ -9,7 +9,7 @@ import './index.less'; | |||||
| type PageTitleProps = { | type PageTitleProps = { | ||||
| /** 标题 */ | /** 标题 */ | ||||
| title: string; | |||||
| title: React.ReactNode; | |||||
| /** 自定义类名 */ | /** 自定义类名 */ | ||||
| className?: string; | className?: string; | ||||
| /** 自定义样式 */ | /** 自定义样式 */ | ||||
| @@ -26,18 +26,34 @@ export type ParameterInputObject = { | |||||
| export type ParameterInputValue = ParameterInputObject | string; | export type ParameterInputValue = ParameterInputObject | string; | ||||
| export interface ParameterInputProps { | export interface ParameterInputProps { | ||||
| /** 值,可以是字符串,也可以是 ParameterInputObject 对象 */ | |||||
| value?: ParameterInputValue; | value?: ParameterInputValue; | ||||
| /** | |||||
| * 值变化时的回调 | |||||
| * @param value 值,可以是字符串,也可以是 ParameterInputObject 对象 | |||||
| */ | |||||
| onChange?: (value?: ParameterInputValue) => void; | onChange?: (value?: ParameterInputValue) => void; | ||||
| /** 点击时的回调 */ | |||||
| onClick?: () => void; | onClick?: () => void; | ||||
| /** 删除时的回调 */ | |||||
| onRemove?: () => void; | onRemove?: () => void; | ||||
| /** 是否可以手动输入 */ | |||||
| canInput?: boolean; | canInput?: boolean; | ||||
| /** 是否是文本框 */ | |||||
| textArea?: boolean; | textArea?: boolean; | ||||
| /** 占位符 */ | |||||
| placeholder?: string; | placeholder?: string; | ||||
| /** 是否允许清除 */ | |||||
| allowClear?: boolean; | allowClear?: boolean; | ||||
| /** 自定义类名 */ | |||||
| className?: string; | className?: string; | ||||
| /** 自定义样式 */ | |||||
| style?: React.CSSProperties; | style?: React.CSSProperties; | ||||
| /** 大小 */ | |||||
| size?: 'middle' | 'small' | 'large'; | size?: 'middle' | 'small' | 'large'; | ||||
| /** 是否禁用 */ | |||||
| disabled?: boolean; | disabled?: boolean; | ||||
| /** 元素 id */ | |||||
| id?: string; | id?: string; | ||||
| } | } | ||||
| @@ -21,14 +21,16 @@ export { requiredValidator, type ParameterInputObject } from '../ParameterInput' | |||||
| export { ResourceSelectorType, selectorTypeConfig, type ResourceSelectorResponse }; | export { ResourceSelectorType, selectorTypeConfig, type ResourceSelectorResponse }; | ||||
| type ResourceSelectProps = { | type ResourceSelectProps = { | ||||
| /** 类型,数据集、模型、镜像 */ | |||||
| type: ResourceSelectorType; | type: ResourceSelectorType; | ||||
| } & ParameterInputProps; | } & ParameterInputProps; | ||||
| // 获取选择数据集、模型后面按钮 icon | |||||
| // 获取选择数据集、模型、镜像后面按钮 icon | |||||
| const getSelectBtnIcon = (type: ResourceSelectorType) => { | const getSelectBtnIcon = (type: ResourceSelectorType) => { | ||||
| return <KFIcon type={selectorTypeConfig[type].buttonIcon} font={16} />; | return <KFIcon type={selectorTypeConfig[type].buttonIcon} font={16} />; | ||||
| }; | }; | ||||
| /** 数据集、模型、镜像选择表单组件 */ | |||||
| function ResourceSelect({ type, value, onChange, disabled, ...rest }: ResourceSelectProps) { | function ResourceSelect({ type, value, onChange, disabled, ...rest }: ResourceSelectProps) { | ||||
| const [selectedResource, setSelectedResource] = useState<ResourceSelectorResponse | undefined>( | const [selectedResource, setSelectedResource] = useState<ResourceSelectorResponse | undefined>( | ||||
| undefined, | undefined, | ||||
| @@ -16,7 +16,7 @@ import { ResourceSelectorType, selectorTypeConfig } from './config'; | |||||
| import styles from './index.less'; | import styles from './index.less'; | ||||
| export { ResourceSelectorType, selectorTypeConfig }; | export { ResourceSelectorType, selectorTypeConfig }; | ||||
| // 选择数据集\模型\镜像的返回类型 | |||||
| // 选择数据集、模型、镜像的返回类型 | |||||
| export type ResourceSelectorResponse = { | export type ResourceSelectorResponse = { | ||||
| activeTab: CommonTabKeys; // 是我的还是公开的 | activeTab: CommonTabKeys; // 是我的还是公开的 | ||||
| id: string; // 数据集\模型\镜像 id | id: string; // 数据集\模型\镜像 id | ||||
| @@ -28,10 +28,18 @@ export type ResourceSelectorResponse = { | |||||
| }; | }; | ||||
| export interface ResourceSelectorModalProps extends Omit<ModalProps, 'onOk'> { | export interface ResourceSelectorModalProps extends Omit<ModalProps, 'onOk'> { | ||||
| type: ResourceSelectorType; // 数据集\模型\镜像 | |||||
| /** 类型,数据集、模型、镜像 */ | |||||
| type: ResourceSelectorType; | |||||
| /** 默认展开的节点 */ | |||||
| defaultExpandedKeys?: React.Key[]; | defaultExpandedKeys?: React.Key[]; | ||||
| /** 默认展开的节点 */ | |||||
| defaultCheckedKeys?: React.Key[]; | defaultCheckedKeys?: React.Key[]; | ||||
| /** 默认激活的 Tab */ | |||||
| defaultActiveTab?: CommonTabKeys; | defaultActiveTab?: CommonTabKeys; | ||||
| /** | |||||
| * 确认回调 | |||||
| * @param params 选择的数据 | |||||
| */ | |||||
| onOk?: (params: ResourceSelectorResponse | undefined) => void; | onOk?: (params: ResourceSelectorResponse | undefined) => void; | ||||
| } | } | ||||
| @@ -61,6 +69,7 @@ const getIdAndVersion = (versionKey: string) => { | |||||
| }; | }; | ||||
| }; | }; | ||||
| /** 选择 数据集、模型、镜像 弹框 */ | |||||
| function ResourceSelectorModal({ | function ResourceSelectorModal({ | ||||
| type, | type, | ||||
| defaultExpandedKeys = [], | defaultExpandedKeys = [], | ||||
| @@ -5,7 +5,7 @@ body, | |||||
| height: 100%; | height: 100%; | ||||
| margin: 0; | margin: 0; | ||||
| padding: 0; | padding: 0; | ||||
| overflow-y: hidden; | |||||
| overflow-y: visible; | |||||
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, | 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 Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', | ||||
| 'Noto Color Emoji'; | 'Noto Color Emoji'; | ||||
| @@ -1,20 +1,9 @@ | |||||
| import BasicInfo from '@/components/BasicInfo'; | import BasicInfo from '@/components/BasicInfo'; | ||||
| import { formatDate } from '@/utils/date'; | import { formatDate } from '@/utils/date'; | ||||
| import { formatList } from '@/utils/format'; | |||||
| import type { Meta, StoryObj } from '@storybook/react'; | import type { Meta, StoryObj } from '@storybook/react'; | ||||
| import { Button } from 'antd'; | 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 | // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export | ||||
| const meta = { | const meta = { | ||||
| title: 'Components/BasicInfo', | title: 'Components/BasicInfo', | ||||
| @@ -1,5 +1,9 @@ | |||||
| import CodeSelect from '@/components/CodeSelect'; | import CodeSelect from '@/components/CodeSelect'; | ||||
| import type { Meta, StoryObj } from '@storybook/react'; | 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 | // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export | ||||
| const meta = { | const meta = { | ||||
| @@ -8,6 +12,13 @@ const meta = { | |||||
| parameters: { | parameters: { | ||||
| // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout | // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout | ||||
| // layout: 'centered', | // 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 | // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs | ||||
| tags: ['autodocs'], | tags: ['autodocs'], | ||||
| @@ -16,7 +27,7 @@ const meta = { | |||||
| // backgroundColor: { control: 'color' }, | // 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 | // 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<typeof CodeSelect>; | } satisfies Meta<typeof CodeSelect>; | ||||
| export default meta; | export default meta; | ||||
| @@ -24,5 +35,22 @@ type Story = StoryObj<typeof meta>; | |||||
| // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args | // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args | ||||
| export const Primary: Story = { | export const Primary: Story = { | ||||
| args: {}, | |||||
| render: ({ onChange }) => { | |||||
| return ( | |||||
| <Form name="code-select-form" size="large"> | |||||
| <Row gutter={8}> | |||||
| <Col span={10}> | |||||
| <Form.Item label="代码配置" name="code_config"> | |||||
| <CodeSelect | |||||
| placeholder="请选择代码配置" | |||||
| canInput={false} | |||||
| size="large" | |||||
| onChange={onChange} | |||||
| /> | |||||
| </Form.Item> | |||||
| </Col> | |||||
| </Row> | |||||
| </Form> | |||||
| ); | |||||
| }, | |||||
| }; | }; | ||||
| @@ -4,6 +4,8 @@ import { useArgs } from '@storybook/preview-api'; | |||||
| import type { Meta, StoryObj } from '@storybook/react'; | import type { Meta, StoryObj } from '@storybook/react'; | ||||
| import { fn } from '@storybook/test'; | import { fn } from '@storybook/test'; | ||||
| import { Button } from 'antd'; | 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 | // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export | ||||
| const meta = { | const meta = { | ||||
| @@ -12,15 +14,19 @@ const meta = { | |||||
| parameters: { | parameters: { | ||||
| // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout | // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout | ||||
| layout: 'centered', | 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 | // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs | ||||
| tags: ['autodocs'], | tags: ['autodocs'], | ||||
| // More on argTypes: https://storybook.js.org/docs/api/argtypes | // More on argTypes: https://storybook.js.org/docs/api/argtypes | ||||
| argTypes: { | argTypes: { | ||||
| // backgroundColor: { control: 'color' }, | // backgroundColor: { control: 'color' }, | ||||
| title: { | |||||
| description: '标题', | |||||
| }, | |||||
| open: { | open: { | ||||
| description: '对话框是否可见', | description: '对话框是否可见', | ||||
| }, | }, | ||||
| @@ -37,17 +43,19 @@ export const Primary: Story = { | |||||
| args: { | args: { | ||||
| open: false, | open: false, | ||||
| }, | }, | ||||
| render: function Render(args) { | |||||
| render: function Render({ onOk, onCancel, ...args }) { | |||||
| const [{ open }, updateArgs] = useArgs(); | const [{ open }, updateArgs] = useArgs(); | ||||
| function onClick() { | function onClick() { | ||||
| updateArgs({ open: true }); | updateArgs({ open: true }); | ||||
| } | } | ||||
| function onOk() { | |||||
| function onModalOk(res: any) { | |||||
| updateArgs({ open: false }); | updateArgs({ open: false }); | ||||
| onOk?.(res); | |||||
| } | } | ||||
| function onCancel() { | |||||
| function onModalCancel() { | |||||
| updateArgs({ open: false }); | updateArgs({ open: false }); | ||||
| onCancel?.(); | |||||
| } | } | ||||
| return ( | return ( | ||||
| @@ -55,27 +63,27 @@ export const Primary: Story = { | |||||
| <Button type="primary" onClick={onClick}> | <Button type="primary" onClick={onClick}> | ||||
| 选择代码配置 | 选择代码配置 | ||||
| </Button> | </Button> | ||||
| <CodeSelectorModal {...args} open={open} onOk={onOk} onCancel={onCancel} /> | |||||
| <CodeSelectorModal {...args} open={open} onOk={onModalOk} onCancel={onModalCancel} /> | |||||
| </> | </> | ||||
| ); | ); | ||||
| }, | }, | ||||
| }; | }; | ||||
| const OpenModalByFunction = () => { | |||||
| const handleOnChange = () => { | |||||
| const { close } = openAntdModal(CodeSelectorModal, { | |||||
| onOk: () => { | |||||
| close(); | |||||
| }, | |||||
| }); | |||||
| }; | |||||
| return ( | |||||
| <Button type="primary" onClick={handleOnChange}> | |||||
| 以函数的方式打开 | |||||
| </Button> | |||||
| ); | |||||
| }; | |||||
| export const OpenInFunction: Story = { | export const OpenInFunction: Story = { | ||||
| render: () => <OpenModalByFunction />, | |||||
| render: function Render(args) { | |||||
| const handleOnChange = () => { | |||||
| const { close } = openAntdModal(CodeSelectorModal, { | |||||
| onOk: (res) => { | |||||
| const { onOk } = args; | |||||
| onOk?.(res); | |||||
| close(); | |||||
| }, | |||||
| }); | |||||
| }; | |||||
| return ( | |||||
| <Button type="primary" onClick={handleOnChange}> | |||||
| 以函数的方式打开 | |||||
| </Button> | |||||
| ); | |||||
| }, | |||||
| }; | }; | ||||
| @@ -25,6 +25,10 @@ const meta = { | |||||
| open: { | open: { | ||||
| description: '对话框是否可见', | 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 | // 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() }, | args: { onCancel: fn(), onOk: fn() }, | ||||
| @@ -41,17 +45,19 @@ export const Primary: Story = { | |||||
| open: false, | open: false, | ||||
| children: '这是一个模态框', | children: '这是一个模态框', | ||||
| }, | }, | ||||
| render: function Render(args) { | |||||
| render: function Render({ onOk, onCancel, ...args }) { | |||||
| const [{ open }, updateArgs] = useArgs(); | const [{ open }, updateArgs] = useArgs(); | ||||
| function onClick() { | function onClick() { | ||||
| updateArgs({ open: true }); | updateArgs({ open: true }); | ||||
| } | } | ||||
| function onOk() { | |||||
| function onModalOk() { | |||||
| updateArgs({ open: false }); | updateArgs({ open: false }); | ||||
| onOk?.(); | |||||
| } | } | ||||
| function onCancel() { | |||||
| function onModalCancel() { | |||||
| updateArgs({ open: false }); | updateArgs({ open: false }); | ||||
| onCancel?.(); | |||||
| } | } | ||||
| return ( | return ( | ||||
| @@ -59,30 +65,28 @@ export const Primary: Story = { | |||||
| <Button type="primary" onClick={onClick}> | <Button type="primary" onClick={onClick}> | ||||
| 打开 KFModal | 打开 KFModal | ||||
| </Button> | </Button> | ||||
| <KFModal {...args} open={open} onOk={onOk} onCancel={onCancel} /> | |||||
| <KFModal {...args} open={open} onOk={onModalOk} onCancel={onModalCancel} /> | |||||
| </> | </> | ||||
| ); | ); | ||||
| }, | }, | ||||
| }; | }; | ||||
| const OpenModalByFunction = () => { | |||||
| const handleOnChange = () => { | |||||
| const { close } = openAntdModal(KFModal, { | |||||
| title: '创建实验', | |||||
| image: CreateExperiment, | |||||
| children: '这是一个模态框', | |||||
| onOk: () => { | |||||
| close(); | |||||
| }, | |||||
| }); | |||||
| }; | |||||
| return ( | |||||
| <Button type="primary" onClick={handleOnChange}> | |||||
| 以函数的方式打开 | |||||
| </Button> | |||||
| ); | |||||
| }; | |||||
| export const OpenInFunction: Story = { | export const OpenInFunction: Story = { | ||||
| render: () => <OpenModalByFunction />, | |||||
| render: function Render() { | |||||
| const handleOnChange = () => { | |||||
| const { close } = openAntdModal(KFModal, { | |||||
| title: '创建实验', | |||||
| image: CreateExperiment, | |||||
| children: '这是一个模态框', | |||||
| onOk: () => { | |||||
| close(); | |||||
| }, | |||||
| }); | |||||
| }; | |||||
| return ( | |||||
| <Button type="primary" onClick={handleOnChange}> | |||||
| 以函数的方式打开 | |||||
| </Button> | |||||
| ); | |||||
| }, | |||||
| }; | }; | ||||
| @@ -34,17 +34,19 @@ export const Primary: Story = { | |||||
| selectedIcon: 'manual-icon', | selectedIcon: 'manual-icon', | ||||
| open: false, | open: false, | ||||
| }, | }, | ||||
| render: function Render(args) { | |||||
| render: function Render({ onOk, onCancel, ...args }) { | |||||
| const [{ open, selectedIcon }, updateArgs] = useArgs(); | const [{ open, selectedIcon }, updateArgs] = useArgs(); | ||||
| function onClick() { | function onClick() { | ||||
| updateArgs({ open: true }); | updateArgs({ open: true }); | ||||
| } | } | ||||
| function onOk(value: string) { | |||||
| function onModalOk(value: string) { | |||||
| updateArgs({ selectedIcon: value, open: false }); | updateArgs({ selectedIcon: value, open: false }); | ||||
| onOk?.(value); | |||||
| } | } | ||||
| function onCancel() { | |||||
| function onModalCancel() { | |||||
| updateArgs({ open: false }); | updateArgs({ open: false }); | ||||
| onCancel?.(); | |||||
| } | } | ||||
| return ( | return ( | ||||
| @@ -56,8 +58,8 @@ export const Primary: Story = { | |||||
| {...args} | {...args} | ||||
| open={open} | open={open} | ||||
| selectedIcon={selectedIcon} | selectedIcon={selectedIcon} | ||||
| onOk={onOk} | |||||
| onCancel={onCancel} | |||||
| onOk={onModalOk} | |||||
| onCancel={onModalCancel} | |||||
| /> | /> | ||||
| </> | </> | ||||
| ); | ); | ||||
| @@ -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<typeof ParameterInput>; | |||||
| export default meta; | |||||
| type Story = StoryObj<typeof meta>; | |||||
| // 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<ParameterInputValue | undefined>(''); | |||||
| const onClick = () => { | |||||
| setValue({ | |||||
| value: 'storybook', | |||||
| showValue: 'storybook', | |||||
| fromSelect: true, | |||||
| }); | |||||
| }; | |||||
| return ( | |||||
| <> | |||||
| <ParameterInput | |||||
| {...args} | |||||
| value={value} | |||||
| onChange={(value) => setValue(value)} | |||||
| ></ParameterInput> | |||||
| <Button type="primary" style={{ display: 'block', marginTop: 10 }} onClick={onClick}> | |||||
| 模拟从全局参数选择 | |||||
| </Button> | |||||
| </> | |||||
| ); | |||||
| }, | |||||
| }; | |||||
| @@ -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<typeof ParameterSelect>; | |||||
| export default meta; | |||||
| type Story = StoryObj<typeof meta>; | |||||
| // More on writing stories with args: https://storybook.js.org/docs/writing-stories/args | |||||
| export const Primary: Story = { | |||||
| args: { | |||||
| title: '基本信息', | |||||
| image: MirrorBasic, | |||||
| }, | |||||
| }; | |||||
| @@ -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<typeof ResourceSelect>; | |||||
| export default meta; | |||||
| type Story = StoryObj<typeof meta>; | |||||
| // 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 ( | |||||
| <Form | |||||
| name="resource-select-form" | |||||
| labelCol={{ flex: '80px' }} | |||||
| labelAlign="left" | |||||
| size="large" | |||||
| autoComplete="off" | |||||
| > | |||||
| <Row gutter={8}> | |||||
| <Col span={10}> | |||||
| <Form.Item label="数据集" name="dataset"> | |||||
| <ResourceSelect | |||||
| type={ResourceSelectorType.Dataset} | |||||
| placeholder="请选择" | |||||
| canInput={false} | |||||
| size="large" | |||||
| onChange={onChange} | |||||
| /> | |||||
| </Form.Item> | |||||
| </Col> | |||||
| </Row> | |||||
| <Row gutter={8}> | |||||
| <Col span={10}> | |||||
| <Form.Item | |||||
| label="模型" | |||||
| name="model" | |||||
| rules={[ | |||||
| { | |||||
| validator: requiredValidator, | |||||
| message: '请选择镜像', | |||||
| }, | |||||
| ]} | |||||
| required | |||||
| > | |||||
| <ResourceSelect | |||||
| type={ResourceSelectorType.Model} | |||||
| placeholder="请选择" | |||||
| canInput={false} | |||||
| size="large" | |||||
| onChange={onChange} | |||||
| /> | |||||
| </Form.Item> | |||||
| </Col> | |||||
| </Row> | |||||
| <Row gutter={8}> | |||||
| <Col span={10}> | |||||
| <Form.Item label="镜像" name="image"> | |||||
| <ResourceSelect | |||||
| type={ResourceSelectorType.Mirror} | |||||
| placeholder="请选择" | |||||
| canInput={false} | |||||
| size="large" | |||||
| onChange={onChange} | |||||
| /> | |||||
| </Form.Item> | |||||
| </Col> | |||||
| </Row> | |||||
| </Form> | |||||
| ); | |||||
| }, | |||||
| }; | |||||
| @@ -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<typeof ResourceSelectorModal>; | |||||
| export default meta; | |||||
| type Story = StoryObj<typeof meta>; | |||||
| // 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 ( | |||||
| <> | |||||
| <Button type="primary" onClick={onClick}> | |||||
| 选择数据集 | |||||
| </Button> | |||||
| <ResourceSelectorModal {...args} open={open} onOk={onModalOk} onCancel={onModalCancel} /> | |||||
| </> | |||||
| ); | |||||
| }, | |||||
| }; | |||||
| 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 ( | |||||
| <> | |||||
| <Button type="primary" onClick={onClick}> | |||||
| 选择模型 | |||||
| </Button> | |||||
| <ResourceSelectorModal {...args} open={open} onOk={onModalOk} onCancel={onModalCancel} /> | |||||
| </> | |||||
| ); | |||||
| }, | |||||
| }; | |||||
| 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 ( | |||||
| <> | |||||
| <Button type="primary" onClick={onClick}> | |||||
| 选择镜像 | |||||
| </Button> | |||||
| <ResourceSelectorModal {...args} open={open} onOk={onModalOk} onCancel={onModalCancel} /> | |||||
| </> | |||||
| ); | |||||
| }, | |||||
| }; | |||||
| 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 ( | |||||
| <Button type="primary" onClick={handleOnChange}> | |||||
| 以函数的方式打开 | |||||
| </Button> | |||||
| ); | |||||
| }, | |||||
| }; | |||||
| @@ -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: | |||||
| '<pre><code># 克隆数据集配置文件与存储参数到本地\ngit clone -b v1 https://gitlink.org.cn/fanshuai/admin_dataset_20250217105241.git\n# 远程拉取配置文件\ndvc pull\n</code></pre>', | |||||
| 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: | |||||
| '<pre><code># 克隆模型配置文件与存储参数到本地\ngit clone -b v1 https://gitlink.org.cn/chenzhihang/admin_model_20241224095928.git\n# 远程拉取配置文件\ndvc pull\n</code></pre>', | |||||
| 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, | |||||
| }, | |||||
| }; | |||||