Browse Source

feat: 修改FormInfo & ParameterSelect 组件

pull/174/head
cp3hnu 11 months ago
parent
commit
3b6d948c3e
7 changed files with 156 additions and 53 deletions
  1. +20
    -6
      react-ui/src/components/FormInfo/index.tsx
  2. +41
    -24
      react-ui/src/components/ParameterSelect/index.tsx
  3. +8
    -6
      react-ui/src/pages/Experiment/components/ExperimentParameter/index.tsx
  4. +13
    -4
      react-ui/src/pages/Pipeline/components/PipelineNodeDrawer/index.tsx
  5. +18
    -1
      react-ui/src/stories/FormInfo.stories.tsx
  6. +53
    -9
      react-ui/src/stories/ParameterSelect.stories.tsx
  7. +3
    -3
      react-ui/src/utils/format.ts

+ 20
- 6
react-ui/src/components/FormInfo/index.tsx View File

@@ -1,5 +1,5 @@
import { formatEnum } from '@/utils/format';
import { Typography } from 'antd';
import { Typography, type SelectProps } from 'antd';
import classNames from 'classnames';
import './index.less';

@@ -13,7 +13,9 @@ type FormInfoProps = {
/** 是否是下拉框 */
select?: boolean;
/** 下拉框数据 */
options?: { label: string; value: any }[];
options?: SelectProps['options'];
/** 自定义节点 label、value 的字段 */
fieldNames?: SelectProps['fieldNames'];
/** 自定义类名 */
className?: string;
/** 自定义样式 */
@@ -26,17 +28,29 @@ type FormInfoProps = {
function FormInfo({
value,
valuePropName,
className,
select,
textArea = false,
select = false,
options,
fieldNames,
className,
style,
textArea = false,
}: FormInfoProps) {
let showValue = value;
if (value && typeof value === 'object' && valuePropName) {
showValue = value[valuePropName];
} else if (select === true && options) {
showValue = formatEnum(options)(value);
let _options: SelectProps['options'] = options;
if (fieldNames) {
_options = options.map((v) => {
return {
...v,
label: fieldNames.label && v[fieldNames.label],
value: fieldNames.value && v[fieldNames.value],
options: fieldNames.options && v[fieldNames.options],
};
});
}
showValue = formatEnum(_options)(value);
}

return (


+ 41
- 24
react-ui/src/components/ParameterSelect/index.tsx View File

@@ -7,43 +7,50 @@
import { to } from '@/utils/promise';
import { Select, type SelectProps } from 'antd';
import { useEffect, useState } from 'react';
import FormInfo from '../FormInfo';
import { paramSelectConfig } from './config';

/** 值类型 */
export type ParameterSelectValue = {
/** 类型,参数名是和后台保持一致的 */
item_type: 'dataset' | 'model' | 'service' | 'resource';
/** 值 */
value?: any;
/** 占位符 */
placeholder?: string;
/** 其它属性 */
type ParameterSelectObject = {
value: any;
[key: string]: any;
};

interface ParameterSelectProps extends SelectProps {
/** 类型 */
dataType: 'dataset' | 'model' | 'service' | 'resource';
/** 是否只是展示信息 */
isInfo?: boolean;
/** 值 */
value?: ParameterSelectValue;
value?: string | ParameterSelectObject;
/** 修改后回调 */
onChange?: (value: ParameterSelectValue) => void;
onChange?: (value: string | ParameterSelectObject) => void;
}

/** 参数选择器,支持资源规格、数据集、模型、服务 */
function ParameterSelect({ value, onChange, ...rest }: ParameterSelectProps) {
function ParameterSelect({
dataType,
isInfo = false,
value,
onChange,
...rest
}: ParameterSelectProps) {
const [options, setOptions] = useState([]);
const valueNotNullable = value ?? ({} as ParameterSelectValue);
const { item_type } = valueNotNullable;
const propsConfig = paramSelectConfig[item_type];
const propsConfig = paramSelectConfig[dataType];
const valueText = typeof value === 'object' && value !== null ? value.value : value;

useEffect(() => {
getSelectOptions();
}, []);

const hangleChange = (e: string) => {
onChange?.({
...valueNotNullable,
value: e,
});
const handleChange = (text: string) => {
if (typeof value === 'object' && value !== null) {
onChange?.({
...value,
value: text,
});
} else {
onChange?.(text);
}
};

// 获取下拉数据
@@ -58,16 +65,26 @@ function ParameterSelect({ value, onChange, ...rest }: ParameterSelectProps) {
}
};

if (isInfo) {
return (
<FormInfo
select
value={valueText}
options={options}
fieldNames={propsConfig?.fieldNames}
></FormInfo>
);
}

return (
<Select
{...rest}
placeholder={valueNotNullable.placeholder || rest.placeholder}
filterOption={propsConfig?.filterOption}
options={options}
fieldNames={propsConfig?.fieldNames}
value={valueNotNullable.value}
optionFilterProp={propsConfig.optionFilterProp}
onChange={hangleChange}
optionFilterProp={propsConfig?.optionFilterProp}
value={valueText}
onChange={handleChange}
showSearch
allowClear
/>


+ 8
- 6
react-ui/src/pages/Experiment/components/ExperimentParameter/index.tsx View File

@@ -3,7 +3,7 @@ import ParameterSelect from '@/components/ParameterSelect';
import SubAreaTitle from '@/components/SubAreaTitle';
import { useComputingResource } from '@/hooks/resource';
import { PipelineNodeModelSerialize } from '@/types';
import { Form, Select } from 'antd';
import { Form } from 'antd';
import styles from './index.less';

type ExperimentParameterProps = {
@@ -100,7 +100,7 @@ function ExperimentParameter({ nodeData }: ExperimentParameterProps) {
</Form.Item>

<Form.Item label="启动命令" name="command">
<FormInfo multiline />
<FormInfo textArea />
</Form.Item>
<Form.Item
label="资源规格"
@@ -112,9 +112,9 @@ function ExperimentParameter({ nodeData }: ExperimentParameterProps) {
},
]}
>
<Select
<FormInfo
select
options={resourceStandardList}
disabled
fieldNames={{
label: 'description',
value: 'standard',
@@ -125,7 +125,7 @@ function ExperimentParameter({ nodeData }: ExperimentParameterProps) {
<FormInfo />
</Form.Item>
<Form.Item label="环境变量" name="env_variables">
<FormInfo multiline />
<FormInfo textArea />
</Form.Item>
{controlStrategyList.map((item) => (
<Form.Item key={item.key} name={['control_strategy', item.key]} label={item.value.label}>
@@ -146,7 +146,9 @@ function ExperimentParameter({ nodeData }: ExperimentParameterProps) {
rules={[{ required: item.value.require ? true : false }]}
>
{item.value.type === 'select' ? (
<ParameterSelect disabled />
['dataset', 'model', 'service', 'resource'].includes(item.value.item_type) ? (
<ParameterSelect dataType={item.value.item_type as any} isInfo />
) : null
) : (
<FormInfo valuePropName="showValue" />
)}


+ 13
- 4
react-ui/src/pages/Pipeline/components/PipelineNodeDrawer/index.tsx View File

@@ -502,7 +502,7 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete
label={getLabel(item, 'control_strategy')}
rules={getFormRules(item)}
>
<ParameterInput allowClear></ParameterInput>
<ParameterInput placeholder={item.value.placeholder} allowClear></ParameterInput>
</Form.Item>
))}
{/* 输入参数 */}
@@ -523,9 +523,18 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete
<div className={styles['pipeline-drawer__ref-row']}>
<Form.Item name={['in_parameters', item.key]} rules={getFormRules(item)} noStyle>
{item.value.type === 'select' ? (
<ParameterSelect />
['dataset', 'model', 'service', 'resource'].includes(item.value.item_type) ? (
<ParameterSelect
dataType={item.value.item_type as any}
placeholder={item.value.placeholder}
/>
) : null
) : (
<ParameterInput canInput={canInput(item.value)} allowClear></ParameterInput>
<ParameterInput
canInput={canInput(item.value)}
placeholder={item.value.placeholder}
allowClear
></ParameterInput>
)}
</Form.Item>
{item.value.type === 'ref' && (
@@ -563,7 +572,7 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete
label={getLabel(item, 'out_parameters')}
rules={getFormRules(item)}
>
<ParameterInput allowClear></ParameterInput>
<ParameterInput placeholder={item.value.placeholder} allowClear></ParameterInput>
</Form.Item>
))}
</>


+ 18
- 1
react-ui/src/stories/FormInfo.stories.tsx View File

@@ -38,7 +38,7 @@ export const InForm: Story = {
<Form
name="form"
style={{ width: 300 }}
labelCol={{ flex: '100px' }}
labelCol={{ flex: '150px' }}
initialValues={{
text: '文本',
large_text:
@@ -49,6 +49,7 @@ export const InForm: Story = {
showValue: '对象文本',
},
select_text: 1,
select_map_text: 1,
ant_input_text:
'超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本',
ant_select_text: 1,
@@ -82,6 +83,22 @@ export const InForm: Story = {
]}
/>
</Form.Item>
<Form.Item label="模拟 Select Map" name="select_map_text">
<FormInfo
select
options={[
{
otherLabel:
'超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本超长文本',
otherValue: 1,
},
]}
fieldNames={{
label: 'otherLabel',
value: 'otherValue',
}}
/>
</Form.Item>
<Form.Item label="Input" name="ant_input_text">
<Input disabled />
</Form.Item>


+ 53
- 9
react-ui/src/stories/ParameterSelect.stories.tsx View File

@@ -2,7 +2,7 @@ import ParameterSelect, { ParameterSelectValue } from '@/components/ParameterSel
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 { Button, Col, Form, Row } from 'antd';
import { http, HttpResponse } from 'msw';
import { computeResourceData, datasetListData, modelListData, serviceListData } from './mockData';

@@ -46,12 +46,32 @@ 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: '请选择数据集',
},
placeholder: '请选择',
dataType: 'dataset',
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>;
},
};

/** 值可以是一个对象,典型的是流水线节点对象 **PipelineNodeModelParameter** */
export const Object: Story = {
args: {
placeholder: '请选择',
dataType: 'dataset',
style: { width: 400 },
size: 'large',
value: {
value: undefined,
},
},
render: function Render(args) {
const [{ value }, updateArgs] = useArgs();
@@ -65,6 +85,9 @@ export const Primary: Story = {
};

export const InForm: Story = {
args: {
dataType: 'dataset',
},
render: ({ onChange }) => {
return (
<Form
@@ -72,54 +95,75 @@ export const InForm: Story = {
labelCol={{ flex: '80px' }}
labelAlign="left"
size="large"
onFinish={(values) => {
console.log('onFinish', values);
}}
autoComplete="off"
initialValues={{
dataset: {
type: 'select',
item_type: 'dataset',
placeholder: '请选择数据集',
label: '数据集',
},
model: {
type: 'select',
item_type: 'model',
placeholder: '请选择模型',
label: '模型',
},
service: {
type: 'select',
item_type: 'service',
placeholder: '请选择服务',
label: '服务',
},
resource: {
type: 'select',
item_type: 'resource',
placeholder: '请选择计算资源',
label: '计算资源',
},
test: '1234',
}}
>
<Row gutter={8}>
<Col span={10}>
<Form.Item label="数据集" name="dataset">
<ParameterSelect onChange={onChange} />
<ParameterSelect dataType="dataset" placeholder="请选择数据集" onChange={onChange} />
</Form.Item>
</Col>
</Row>
<Row gutter={8}>
<Col span={10}>
<Form.Item label="模型" name="model">
<ParameterSelect onChange={onChange} />
<ParameterSelect dataType="model" placeholder="请选择模型" onChange={onChange} />
</Form.Item>
</Col>
</Row>
<Row gutter={8}>
<Col span={10}>
<Form.Item label="服务" name="service">
<ParameterSelect onChange={onChange} />
<ParameterSelect dataType="service" placeholder="请选择服务" onChange={onChange} />
</Form.Item>
</Col>
</Row>
<Row gutter={8}>
<Col span={10}>
<Form.Item label="计算资源" name="resource">
<ParameterSelect onChange={onChange} />
<ParameterSelect
dataType="resource"
placeholder="请选择计算资源"
onChange={onChange}
/>
</Form.Item>
</Col>
</Row>
<Row gutter={8}>
<Button htmlType="submit" type="primary">
提交
</Button>
</Row>
</Form>
);
},


+ 3
- 3
react-ui/src/utils/format.ts View File

@@ -122,14 +122,14 @@ export const formatBoolean = (value: boolean): string => {
return value ? '是' : '否';
};

type FormatEnumFunc = (value: string | number) => string;
type FormatEnumFunc = (value: string | number) => React.ReactNode;

// 格式化枚举
export const formatEnum = (
options: { value: string | number; label: string }[],
options: { value?: string | number | null; label?: React.ReactNode }[],
): FormatEnumFunc => {
return (value: string | number) => {
const option = options.find((item) => item.value === value);
return option ? option.label : '--';
return option && option.label ? option.label : '--';
};
};

Loading…
Cancel
Save