Browse Source

feat: storybook add msw

pull/170/head
cp3hnu 11 months ago
parent
commit
5e2eba49af
24 changed files with 1508 additions and 139 deletions
  1. +1
    -0
      react-ui/.storybook/main.ts
  2. +8
    -1
      react-ui/.storybook/mock/umijs.mock.tsx
  3. +9
    -0
      react-ui/.storybook/preview.tsx
  4. +10
    -3
      react-ui/.storybook/storybook.css
  5. +8
    -1
      react-ui/package.json
  6. +307
    -0
      react-ui/public/mockServiceWorker.js
  7. +25
    -26
      react-ui/src/components/CodeSelectorModal/index.less
  8. +5
    -5
      react-ui/src/components/CodeSelectorModal/index.tsx
  9. +0
    -1
      react-ui/src/components/InfoGroup/InfoGroupTitle.less
  10. +1
    -1
      react-ui/src/components/PageTitle/index.tsx
  11. +16
    -0
      react-ui/src/components/ParameterInput/index.tsx
  12. +3
    -1
      react-ui/src/components/ResourceSelect/index.tsx
  13. +11
    -2
      react-ui/src/components/ResourceSelectorModal/index.tsx
  14. +1
    -1
      react-ui/src/global.less
  15. +1
    -12
      react-ui/src/stories/BasicInfo.stories.tsx
  16. +30
    -2
      react-ui/src/stories/CodeSelect.stories.tsx
  17. +31
    -23
      react-ui/src/stories/CodeSelectorModal.stories.tsx
  18. +27
    -23
      react-ui/src/stories/KFModal.stories.tsx
  19. +7
    -5
      react-ui/src/stories/MenuIconSelector.stories.tsx
  20. +108
    -0
      react-ui/src/stories/ParameterInput.stories.tsx
  21. +0
    -32
      react-ui/src/stories/ParameterSelect.stories.tsx
  22. +135
    -0
      react-ui/src/stories/ResourceSelect.stories.tsx
  23. +216
    -0
      react-ui/src/stories/ResourceSelectorModal.stories.tsx
  24. +548
    -0
      react-ui/src/stories/mockData.ts

+ 1
- 0
react-ui/.storybook/main.ts View File

@@ -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 = {


+ 8
- 1
react-ui/.storybook/mock/umijs.mock.tsx View File

@@ -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());
}; };

+ 9
- 0
react-ui/.storybook/preview.tsx View File

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

+ 10
- 3
react-ui/.storybook/storybook.css View File

@@ -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;
} }

+ 8
- 1
react-ui/package.json View File

@@ -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"
]
} }
}
}

+ 307
- 0
react-ui/public/mockServiceWorker.js View File

@@ -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
}

+ 25
- 26
react-ui/src/components/CodeSelectorModal/index.less View File

@@ -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;
}
}
} }

+ 5
- 5
react-ui/src/components/CodeSelectorModal/index.tsx View File

@@ -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>
)} )}


+ 0
- 1
react-ui/src/components/InfoGroup/InfoGroupTitle.less View File

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


+ 1
- 1
react-ui/src/components/PageTitle/index.tsx View File

@@ -9,7 +9,7 @@ import './index.less';


type PageTitleProps = { type PageTitleProps = {
/** 标题 */ /** 标题 */
title: string;
title: React.ReactNode;
/** 自定义类名 */ /** 自定义类名 */
className?: string; className?: string;
/** 自定义样式 */ /** 自定义样式 */


+ 16
- 0
react-ui/src/components/ParameterInput/index.tsx View File

@@ -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;
} }




+ 3
- 1
react-ui/src/components/ResourceSelect/index.tsx View File

@@ -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,


+ 11
- 2
react-ui/src/components/ResourceSelectorModal/index.tsx View File

@@ -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 = [],


+ 1
- 1
react-ui/src/global.less View File

@@ -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
- 12
react-ui/src/stories/BasicInfo.stories.tsx View File

@@ -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',


+ 30
- 2
react-ui/src/stories/CodeSelect.stories.tsx View File

@@ -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>
);
},
}; };

+ 31
- 23
react-ui/src/stories/CodeSelectorModal.stories.tsx View File

@@ -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>
);
},
}; };

+ 27
- 23
react-ui/src/stories/KFModal.stories.tsx View File

@@ -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>
);
},
}; };

+ 7
- 5
react-ui/src/stories/MenuIconSelector.stories.tsx View File

@@ -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}
/> />
</> </>
); );


+ 108
- 0
react-ui/src/stories/ParameterInput.stories.tsx View File

@@ -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>
</>
);
},
};

+ 0
- 32
react-ui/src/stories/ParameterSelect.stories.tsx View File

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

+ 135
- 0
react-ui/src/stories/ResourceSelect.stories.tsx View File

@@ -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>
);
},
};

+ 216
- 0
react-ui/src/stories/ResourceSelectorModal.stories.tsx View File

@@ -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>
);
},
};

+ 548
- 0
react-ui/src/stories/mockData.ts View File

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

Loading…
Cancel
Save