| @@ -4,22 +4,35 @@ | |||||
| * @Description: 参数下拉选择组件,支持资源规格、数据集、模型、服务 | * @Description: 参数下拉选择组件,支持资源规格、数据集、模型、服务 | ||||
| */ | */ | ||||
| import { PipelineNodeModelParameter } from '@/types'; | |||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import { Select } from 'antd'; | |||||
| import { Select, type SelectProps } from 'antd'; | |||||
| import { useEffect, useState } from 'react'; | import { useEffect, useState } from 'react'; | ||||
| import { paramSelectConfig } from './config'; | import { paramSelectConfig } from './config'; | ||||
| type ParameterSelectProps = { | |||||
| value?: PipelineNodeModelParameter; | |||||
| onChange?: (value: PipelineNodeModelParameter) => void; | |||||
| disabled?: boolean; | |||||
| /** 值类型 */ | |||||
| export type ParameterSelectValue = { | |||||
| /** 类型,参数名是和后台保持一致的 */ | |||||
| item_type: 'dataset' | 'model' | 'service' | 'resource'; | |||||
| /** 值 */ | |||||
| value?: any; | |||||
| /** 占位符 */ | |||||
| placeholder?: string; | |||||
| /** 其它属性 */ | |||||
| [key: string]: any; | |||||
| }; | }; | ||||
| function ParameterSelect({ value, onChange, disabled = false }: ParameterSelectProps) { | |||||
| interface ParameterSelectProps extends SelectProps { | |||||
| /** 值 */ | |||||
| value?: ParameterSelectValue; | |||||
| /** 修改后回调 */ | |||||
| onChange?: (value: ParameterSelectValue) => void; | |||||
| } | |||||
| /** 参数选择器,支持资源规格、数据集、模型、服务 */ | |||||
| function ParameterSelect({ value, onChange, ...rest }: ParameterSelectProps) { | |||||
| const [options, setOptions] = useState([]); | const [options, setOptions] = useState([]); | ||||
| const valueNonNullable = value ?? ({} as PipelineNodeModelParameter); | |||||
| const { item_type } = valueNonNullable; | |||||
| const valueNotNullable = value ?? ({} as ParameterSelectValue); | |||||
| const { item_type } = valueNotNullable; | |||||
| const propsConfig = paramSelectConfig[item_type]; | const propsConfig = paramSelectConfig[item_type]; | ||||
| useEffect(() => { | useEffect(() => { | ||||
| @@ -28,7 +41,7 @@ function ParameterSelect({ value, onChange, disabled = false }: ParameterSelectP | |||||
| const hangleChange = (e: string) => { | const hangleChange = (e: string) => { | ||||
| onChange?.({ | onChange?.({ | ||||
| ...valueNonNullable, | |||||
| ...valueNotNullable, | |||||
| value: e, | value: e, | ||||
| }); | }); | ||||
| }; | }; | ||||
| @@ -47,14 +60,14 @@ function ParameterSelect({ value, onChange, disabled = false }: ParameterSelectP | |||||
| return ( | return ( | ||||
| <Select | <Select | ||||
| placeholder={valueNonNullable.placeholder} | |||||
| {...rest} | |||||
| placeholder={valueNotNullable.placeholder || rest.placeholder} | |||||
| filterOption={propsConfig?.filterOption} | filterOption={propsConfig?.filterOption} | ||||
| options={options} | options={options} | ||||
| fieldNames={propsConfig?.fieldNames} | fieldNames={propsConfig?.fieldNames} | ||||
| value={valueNonNullable.value} | |||||
| value={valueNotNullable.value} | |||||
| optionFilterProp={propsConfig.optionFilterProp} | optionFilterProp={propsConfig.optionFilterProp} | ||||
| onChange={hangleChange} | onChange={hangleChange} | ||||
| disabled={disabled} | |||||
| showSearch | showSearch | ||||
| allowClear | allowClear | ||||
| /> | /> | ||||
| @@ -56,7 +56,13 @@ export const Primary: Story = { | |||||
| export const InForm: Story = { | export const InForm: Story = { | ||||
| render: ({ onChange }) => { | render: ({ onChange }) => { | ||||
| return ( | return ( | ||||
| <Form name="code-select-form" size="large"> | |||||
| <Form | |||||
| name="code-select-form" | |||||
| labelCol={{ flex: '80px' }} | |||||
| labelAlign="left" | |||||
| size="large" | |||||
| autoComplete="off" | |||||
| > | |||||
| <Row gutter={8}> | <Row gutter={8}> | ||||
| <Col span={10}> | <Col span={10}> | ||||
| <Form.Item label="代码配置" name="code_config"> | <Form.Item label="代码配置" name="code_config"> | ||||
| @@ -1,6 +1,6 @@ | |||||
| import FormInfo from '@/components/FormInfo'; | import FormInfo from '@/components/FormInfo'; | ||||
| import type { Meta, StoryObj } from '@storybook/react'; | import type { Meta, StoryObj } from '@storybook/react'; | ||||
| import { Form, Input, Select } from 'antd'; | |||||
| import { Form, Input, Select, Typography } from 'antd'; | |||||
| // 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 = { | ||||
| @@ -52,6 +52,7 @@ export const InForm: Story = { | |||||
| ant_input_text: | ant_input_text: | ||||
| '超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本', | '超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本', | ||||
| ant_select_text: 1, | ant_select_text: 1, | ||||
| ant_select_ellipsis_text: 1, | |||||
| }} | }} | ||||
| > | > | ||||
| <Form.Item label="文本" name="text"> | <Form.Item label="文本" name="text"> | ||||
| @@ -69,7 +70,7 @@ export const InForm: Story = { | |||||
| <Form.Item label="无内容" name="empty_text"> | <Form.Item label="无内容" name="empty_text"> | ||||
| <FormInfo /> | <FormInfo /> | ||||
| </Form.Item> | </Form.Item> | ||||
| <Form.Item label="Select" name="select_text"> | |||||
| <Form.Item label="模拟 Select" name="select_text"> | |||||
| <FormInfo | <FormInfo | ||||
| select | select | ||||
| options={[ | options={[ | ||||
| @@ -96,6 +97,27 @@ export const InForm: Story = { | |||||
| ]} | ]} | ||||
| /> | /> | ||||
| </Form.Item> | </Form.Item> | ||||
| <Form.Item label="Select Ellipsis" name="ant_select_ellipsis_text"> | |||||
| <Select | |||||
| labelRender={(props) => { | |||||
| return ( | |||||
| <div style={{ width: '100%', lineHeight: 'normal' }}> | |||||
| <Typography.Text ellipsis={{ tooltip: props.label }} style={{ margin: 0 }}> | |||||
| {props.label} | |||||
| </Typography.Text> | |||||
| </div> | |||||
| ); | |||||
| }} | |||||
| disabled | |||||
| options={[ | |||||
| { | |||||
| label: | |||||
| '超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本', | |||||
| value: 1, | |||||
| }, | |||||
| ]} | |||||
| /> | |||||
| </Form.Item> | |||||
| </Form> | </Form> | ||||
| ); | ); | ||||
| }, | }, | ||||
| @@ -0,0 +1,126 @@ | |||||
| import ParameterSelect, { ParameterSelectValue } from '@/components/ParameterSelect'; | |||||
| import { useArgs } from '@storybook/preview-api'; | |||||
| import type { Meta, StoryObj } from '@storybook/react'; | |||||
| import { fn } from '@storybook/test'; | |||||
| import { Col, Form, Row } from 'antd'; | |||||
| import { http, HttpResponse } from 'msw'; | |||||
| import { computeResourceData, datasetListData, modelListData, serviceListData } from './mockData'; | |||||
| // More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export | |||||
| const meta = { | |||||
| title: 'Components/ParameterSelect 参数选择器', | |||||
| component: ParameterSelect, | |||||
| parameters: { | |||||
| // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout | |||||
| // layout: 'centered', | |||||
| msw: { | |||||
| handlers: [ | |||||
| http.get('/api/mmp/newdataset/queryDatasets', () => { | |||||
| return HttpResponse.json(datasetListData); | |||||
| }), | |||||
| http.get('/api/mmp/newmodel/queryModels', () => { | |||||
| return HttpResponse.json(modelListData); | |||||
| }), | |||||
| http.get('/api/mmp/service', () => { | |||||
| return HttpResponse.json(serviceListData); | |||||
| }), | |||||
| http.get('/api/mmp/computingResource', () => { | |||||
| return HttpResponse.json(computeResourceData); | |||||
| }), | |||||
| ], | |||||
| }, | |||||
| }, | |||||
| // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs | |||||
| tags: ['autodocs'], | |||||
| // More on argTypes: https://storybook.js.org/docs/api/argtypes | |||||
| 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 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: { | |||||
| value: { | |||||
| item_type: 'dataset', | |||||
| placeholder: '请选择数据集', | |||||
| }, | |||||
| style: { width: 400 }, | |||||
| size: 'large', | |||||
| }, | |||||
| render: function Render(args) { | |||||
| const [{ value }, updateArgs] = useArgs(); | |||||
| function handleChange(value?: ParameterSelectValue) { | |||||
| updateArgs({ value: value }); | |||||
| args.onChange?.(value); | |||||
| } | |||||
| return <ParameterSelect {...args} value={value} onChange={handleChange}></ParameterSelect>; | |||||
| }, | |||||
| }; | |||||
| export const InForm: Story = { | |||||
| render: ({ onChange }) => { | |||||
| return ( | |||||
| <Form | |||||
| name="parameter-select-form" | |||||
| labelCol={{ flex: '80px' }} | |||||
| labelAlign="left" | |||||
| size="large" | |||||
| autoComplete="off" | |||||
| initialValues={{ | |||||
| dataset: { | |||||
| item_type: 'dataset', | |||||
| placeholder: '请选择数据集', | |||||
| }, | |||||
| model: { | |||||
| item_type: 'model', | |||||
| placeholder: '请选择模型', | |||||
| }, | |||||
| service: { | |||||
| item_type: 'service', | |||||
| placeholder: '请选择服务', | |||||
| }, | |||||
| resource: { | |||||
| item_type: 'resource', | |||||
| placeholder: '请选择计算资源', | |||||
| }, | |||||
| }} | |||||
| > | |||||
| <Row gutter={8}> | |||||
| <Col span={10}> | |||||
| <Form.Item label="数据集" name="dataset"> | |||||
| <ParameterSelect onChange={onChange} /> | |||||
| </Form.Item> | |||||
| </Col> | |||||
| </Row> | |||||
| <Row gutter={8}> | |||||
| <Col span={10}> | |||||
| <Form.Item label="模型" name="model"> | |||||
| <ParameterSelect onChange={onChange} /> | |||||
| </Form.Item> | |||||
| </Col> | |||||
| </Row> | |||||
| <Row gutter={8}> | |||||
| <Col span={10}> | |||||
| <Form.Item label="服务" name="service"> | |||||
| <ParameterSelect onChange={onChange} /> | |||||
| </Form.Item> | |||||
| </Col> | |||||
| </Row> | |||||
| <Row gutter={8}> | |||||
| <Col span={10}> | |||||
| <Form.Item label="计算资源" name="resource"> | |||||
| <ParameterSelect onChange={onChange} /> | |||||
| </Form.Item> | |||||
| </Col> | |||||
| </Row> | |||||
| </Form> | |||||
| ); | |||||
| }, | |||||
| }; | |||||
| @@ -546,3 +546,250 @@ export const codeListData = { | |||||
| empty: false, | empty: false, | ||||
| }, | }, | ||||
| }; | }; | ||||
| export const serviceListData = { | |||||
| code: 200, | |||||
| msg: '操作成功', | |||||
| data: { | |||||
| content: [ | |||||
| { | |||||
| id: 25, | |||||
| service_name: '测试1224', | |||||
| service_type: 'video', | |||||
| service_type_name: '视频', | |||||
| description: '测试', | |||||
| create_by: 'admin', | |||||
| update_by: 'admin', | |||||
| create_time: '2024-12-24T16:01:02.000+08:00', | |||||
| update_time: '2024-12-24T16:01:02.000+08:00', | |||||
| state: 1, | |||||
| version_count: 2, | |||||
| }, | |||||
| { | |||||
| id: 12, | |||||
| service_name: '介电材料', | |||||
| service_type: 'text', | |||||
| service_type_name: '文本', | |||||
| description: 'test', | |||||
| create_by: 'admin', | |||||
| update_by: 'admin', | |||||
| create_time: '2024-11-27T09:30:23.000+08:00', | |||||
| update_time: '2024-11-27T09:30:23.000+08:00', | |||||
| state: 1, | |||||
| version_count: 0, | |||||
| }, | |||||
| { | |||||
| id: 7, | |||||
| service_name: '手写体识别', | |||||
| service_type: 'image', | |||||
| service_type_name: '图片', | |||||
| description: '手写体识别服务', | |||||
| create_by: 'admin', | |||||
| update_by: 'admin', | |||||
| create_time: '2024-10-10T10:14:00.000+08:00', | |||||
| update_time: '2024-10-10T10:14:00.000+08:00', | |||||
| state: 1, | |||||
| version_count: 5, | |||||
| }, | |||||
| ], | |||||
| pageable: { | |||||
| sort: { | |||||
| unsorted: true, | |||||
| sorted: false, | |||||
| empty: true, | |||||
| }, | |||||
| pageNumber: 0, | |||||
| pageSize: 10, | |||||
| offset: 0, | |||||
| paged: true, | |||||
| unpaged: false, | |||||
| }, | |||||
| last: true, | |||||
| totalPages: 1, | |||||
| totalElements: 3, | |||||
| sort: { | |||||
| unsorted: true, | |||||
| sorted: false, | |||||
| empty: true, | |||||
| }, | |||||
| first: true, | |||||
| number: 0, | |||||
| numberOfElements: 3, | |||||
| size: 10, | |||||
| empty: false, | |||||
| }, | |||||
| }; | |||||
| export const computeResourceData = { | |||||
| code: 200, | |||||
| msg: '操作成功', | |||||
| data: { | |||||
| content: [ | |||||
| { | |||||
| id: 15, | |||||
| computing_resource: 'GPU', | |||||
| standard: | |||||
| '{"name":"CPU-GPU","value":{"detail_type":"3060","gpu":0,"cpu":1,"memory":"2GB"}}', | |||||
| description: 'GPU: 0, CPU:1, 内存: 2GB', | |||||
| create_by: 'admin', | |||||
| create_time: '2024-04-19T00:00:00.000+08:00', | |||||
| update_by: 'admin', | |||||
| update_time: '2024-04-19T00:00:00.000+08:00', | |||||
| state: 1, | |||||
| used_state: null, | |||||
| node: null, | |||||
| }, | |||||
| { | |||||
| id: 16, | |||||
| computing_resource: 'GPU', | |||||
| standard: | |||||
| '{"name":"CPU-GPU","value":{"detail_type":"3060","gpu":0,"cpu":2,"memory":"4GB"}}', | |||||
| description: 'GPU: 0, CPU:2, 内存: 4GB', | |||||
| create_by: 'admin', | |||||
| create_time: '2024-04-19T00:00:00.000+08:00', | |||||
| update_by: 'admin', | |||||
| update_time: '2024-04-19T00:00:00.000+08:00', | |||||
| state: 1, | |||||
| used_state: null, | |||||
| node: null, | |||||
| }, | |||||
| { | |||||
| id: 17, | |||||
| computing_resource: 'GPU', | |||||
| standard: | |||||
| '{"name":"CPU-GPU","value":{"detail_type":"3060","gpu":0,"cpu":4,"memory":"8GB"}}', | |||||
| description: 'GPU: 0, CPU:4, 内存: 8GB', | |||||
| create_by: 'admin', | |||||
| create_time: '2024-04-19T00:00:00.000+08:00', | |||||
| update_by: 'admin', | |||||
| update_time: '2024-04-19T00:00:00.000+08:00', | |||||
| state: 1, | |||||
| used_state: null, | |||||
| node: null, | |||||
| }, | |||||
| { | |||||
| id: 18, | |||||
| computing_resource: 'GPU', | |||||
| standard: | |||||
| '{"name":"CPU-GPU","value":{"detail_type":"3060","gpu":1,"cpu":1,"memory":"2GB"}}', | |||||
| description: 'GPU: 1, CPU:1, 内存: 2GB', | |||||
| create_by: 'admin', | |||||
| create_time: '2024-04-19T00:00:00.000+08:00', | |||||
| update_by: 'admin', | |||||
| update_time: '2024-04-19T00:00:00.000+08:00', | |||||
| state: 1, | |||||
| used_state: null, | |||||
| node: null, | |||||
| }, | |||||
| { | |||||
| id: 19, | |||||
| computing_resource: 'GPU', | |||||
| standard: | |||||
| '{"name":"CPU-GPU","value":{"detail_type":"3060","gpu":1,"cpu":2,"memory":"4GB"}}', | |||||
| description: 'GPU: 1, CPU:2, 内存: 4GB', | |||||
| create_by: 'admin', | |||||
| create_time: '2024-04-19T00:00:00.000+08:00', | |||||
| update_by: 'admin', | |||||
| update_time: '2024-04-19T00:00:00.000+08:00', | |||||
| state: 1, | |||||
| used_state: null, | |||||
| node: null, | |||||
| }, | |||||
| { | |||||
| id: 20, | |||||
| computing_resource: 'GPU', | |||||
| standard: | |||||
| '{"name":"CPU-GPU","value":{"detail_type":"3060","gpu":1,"cpu":4,"memory":"8GB"}}', | |||||
| description: 'GPU: 1, CPU:4, 内存: 8GB', | |||||
| create_by: 'admin', | |||||
| create_time: '2024-04-19T00:00:00.000+08:00', | |||||
| update_by: 'admin', | |||||
| update_time: '2024-04-19T00:00:00.000+08:00', | |||||
| state: 1, | |||||
| used_state: null, | |||||
| node: null, | |||||
| }, | |||||
| { | |||||
| id: 21, | |||||
| computing_resource: 'GPU', | |||||
| standard: | |||||
| '{"name":"CPU-GPU","value":{"detail_type":"3060","gpu":2,"cpu":2,"memory":"4GB"}}', | |||||
| description: 'GPU: 2, CPU:2, 内存: 4GB', | |||||
| create_by: 'admin', | |||||
| create_time: '2024-04-19T00:00:00.000+08:00', | |||||
| update_by: 'admin', | |||||
| update_time: '2024-04-19T00:00:00.000+08:00', | |||||
| state: 1, | |||||
| used_state: null, | |||||
| node: null, | |||||
| }, | |||||
| { | |||||
| id: 22, | |||||
| computing_resource: 'GPU', | |||||
| standard: | |||||
| '{"name":"CPU-GPU","value":{"detail_type":"3060","gpu":2,"cpu":4,"memory":"8GB"}}', | |||||
| description: 'GPU: 2, CPU:4, 内存: 8GB', | |||||
| create_by: 'admin', | |||||
| create_time: '2024-04-19T00:00:00.000+08:00', | |||||
| update_by: 'admin', | |||||
| update_time: '2024-04-19T00:00:00.000+08:00', | |||||
| state: 1, | |||||
| used_state: null, | |||||
| node: null, | |||||
| }, | |||||
| { | |||||
| id: 23, | |||||
| computing_resource: 'GPU', | |||||
| standard: | |||||
| '{"name":"CPU-GPU","value":{"detail_type":"RTX 3080 Ti","gpu":1,"cpu":1,"memory":"2GB"}}', | |||||
| description: 'GPU: 1, CPU:1, 内存: 2GB, 显存: 12GB', | |||||
| create_by: 'admin', | |||||
| create_time: '2024-04-19T11:38:07.000+08:00', | |||||
| update_by: 'admin', | |||||
| update_time: '2024-04-19T11:38:07.000+08:00', | |||||
| state: 1, | |||||
| used_state: null, | |||||
| node: null, | |||||
| }, | |||||
| { | |||||
| id: 24, | |||||
| computing_resource: 'GPU', | |||||
| standard: | |||||
| '{"name":"CPU-GPU","value":{"detail_type":"RTX 3080","gpu":1,"cpu":2,"memory":"4GB"}}', | |||||
| description: 'GPU: 1, CPU:2, 内存: 4GB, 显存: 10GB', | |||||
| create_by: 'admin', | |||||
| create_time: '2024-04-19T11:39:40.000+08:00', | |||||
| update_by: 'admin', | |||||
| update_time: '2024-04-19T11:39:40.000+08:00', | |||||
| state: 1, | |||||
| used_state: null, | |||||
| node: null, | |||||
| }, | |||||
| ], | |||||
| pageable: { | |||||
| sort: { | |||||
| unsorted: true, | |||||
| sorted: false, | |||||
| empty: true, | |||||
| }, | |||||
| pageNumber: 0, | |||||
| pageSize: 1000, | |||||
| offset: 0, | |||||
| paged: true, | |||||
| unpaged: false, | |||||
| }, | |||||
| last: true, | |||||
| totalPages: 1, | |||||
| totalElements: 10, | |||||
| sort: { | |||||
| unsorted: true, | |||||
| sorted: false, | |||||
| empty: true, | |||||
| }, | |||||
| first: true, | |||||
| number: 0, | |||||
| numberOfElements: 10, | |||||
| size: 1000, | |||||
| empty: false, | |||||
| }, | |||||
| }; | |||||