| @@ -48,4 +48,12 @@ | |||||
| margin-right: 10px; | margin-right: 10px; | ||||
| } | } | ||||
| } | } | ||||
| .global_param_item { | |||||
| max-height: 230px; | |||||
| padding: 20px 12px; | |||||
| overflow-y: auto; | |||||
| border: 1px solid #e6e6e6; | |||||
| border-radius: 6px; | |||||
| } | |||||
| } | } | ||||
| @@ -1,4 +1,5 @@ | |||||
| import { Form, Input, Modal, Select } from 'antd'; | |||||
| import { type PipelineGlobalParam } from '@/types'; | |||||
| import { Form, Input, Modal, Radio, Select, type FormRule } from 'antd'; | |||||
| import { useState } from 'react'; | import { useState } from 'react'; | ||||
| import styles from './addExperimentModal.less'; | import styles from './addExperimentModal.less'; | ||||
| @@ -6,6 +7,7 @@ type FormData = { | |||||
| name?: string; | name?: string; | ||||
| description?: string; | description?: string; | ||||
| workflow_id?: string | number; | workflow_id?: string | number; | ||||
| global_param?: PipelineGlobalParam[]; | |||||
| }; | }; | ||||
| type AddExperimentModalProps = { | type AddExperimentModalProps = { | ||||
| @@ -17,17 +19,55 @@ type AddExperimentModalProps = { | |||||
| initialValues: FormData; | initialValues: FormData; | ||||
| }; | }; | ||||
| interface GlobalParam { | |||||
| param_name: string; | |||||
| param_value: string; | |||||
| } | |||||
| interface Workflow { | interface Workflow { | ||||
| id: string | number; | id: string | number; | ||||
| name: string; | name: string; | ||||
| global_param?: GlobalParam[] | null; | |||||
| global_param?: PipelineGlobalParam[] | null; | |||||
| } | } | ||||
| // 根据参数设置输入组件 | |||||
| export const getParamComponent = (paramType: number, isSensitive?: number): JSX.Element => { | |||||
| // 防止后台返回不是 number 类型 | |||||
| if (Number(paramType) === 3) { | |||||
| return ( | |||||
| <Radio.Group> | |||||
| <Radio value={1}>是</Radio> | |||||
| <Radio value={0}>否</Radio> | |||||
| </Radio.Group> | |||||
| ); | |||||
| } | |||||
| if (isSensitive && Number(isSensitive) === 1) { | |||||
| return <Input.Password visibilityToggle={false} allowClear />; | |||||
| } | |||||
| return <Input placeholder="请输入值" allowClear />; | |||||
| }; | |||||
| // 根据参数设置校验规则 | |||||
| export const getParamRules = (paramType: number, required: boolean = false): FormRule[] => { | |||||
| const rules = []; | |||||
| // 防止后台返回不是 number 类型 | |||||
| if (Number(paramType) === 2) { | |||||
| rules.push({ | |||||
| pattern: /^-?\d+(\.\d+)?$/, | |||||
| message: '整型必须是数字', | |||||
| }); | |||||
| } | |||||
| if (required) { | |||||
| rules.push({ required: true, message: '请输入值' }); | |||||
| } | |||||
| return rules; | |||||
| }; | |||||
| // 根据参数设置 label | |||||
| const getParamType = (param: PipelineGlobalParam): string => { | |||||
| const paramTypes: Readonly<Record<number, string>> = { | |||||
| 1: '字符串', | |||||
| 2: '整型', | |||||
| 3: '布尔类型', | |||||
| }; | |||||
| return param.param_name + `(${paramTypes[param.param_type]})`; | |||||
| }; | |||||
| function AddExperimentModal({ | function AddExperimentModal({ | ||||
| isAdd, | isAdd, | ||||
| open, | open, | ||||
| @@ -38,20 +78,30 @@ function AddExperimentModal({ | |||||
| }: AddExperimentModalProps) { | }: AddExperimentModalProps) { | ||||
| const dialogTitle = isAdd ? '新建实验' : '编辑实验'; | const dialogTitle = isAdd ? '新建实验' : '编辑实验'; | ||||
| const workflowDisabled = isAdd ? false : true; | const workflowDisabled = isAdd ? false : true; | ||||
| const [globalParam, setGlobalParam] = useState<GlobalParam[]>([]); | |||||
| const [globalParam, setGlobalParam] = useState<PipelineGlobalParam[]>( | |||||
| initialValues.global_param || [], | |||||
| ); | |||||
| const [form] = Form.useForm(); | const [form] = Form.useForm(); | ||||
| const layout = { | |||||
| labelCol: { span: 24 }, | |||||
| wrapperCol: { span: 24 }, | |||||
| }; | |||||
| const tailLayout = { | |||||
| labelCol: { span: 8 }, | |||||
| wrapperCol: { span: 16 }, | |||||
| }; | |||||
| // 除了流水线选择发生变化 | // 除了流水线选择发生变化 | ||||
| const handleWorkflowChange = (id: string) => { | |||||
| const handleWorkflowChange = (id: string | number) => { | |||||
| const pipeline: Workflow | undefined = workflowList.find((v) => v.id === id); | const pipeline: Workflow | undefined = workflowList.find((v) => v.id === id); | ||||
| if (pipeline && pipeline.global_param) { | if (pipeline && pipeline.global_param) { | ||||
| setGlobalParam(pipeline.global_param); | setGlobalParam(pipeline.global_param); | ||||
| const fields = pipeline.global_param.reduce((acc, item) => { | |||||
| acc[item.param_name] = item.param_value; | |||||
| return acc; | |||||
| }, {} as Record<string, string>); | |||||
| form.setFieldsValue(fields); | |||||
| form.setFieldValue('global_param', pipeline.global_param); | |||||
| } else { | } else { | ||||
| setGlobalParam([]); | setGlobalParam([]); | ||||
| form.setFieldValue('global_param', []); | |||||
| } | } | ||||
| }; | }; | ||||
| return ( | return ( | ||||
| @@ -73,11 +123,12 @@ function AddExperimentModal({ | |||||
| > | > | ||||
| <Form | <Form | ||||
| name="form" | name="form" | ||||
| layout="vertical" | |||||
| layout="horizontal" | |||||
| initialValues={initialValues} | initialValues={initialValues} | ||||
| onFinish={onFinish} | onFinish={onFinish} | ||||
| autoComplete="off" | autoComplete="off" | ||||
| form={form} | form={form} | ||||
| {...layout} | |||||
| > | > | ||||
| <Form.Item | <Form.Item | ||||
| label="实验名称" | label="实验名称" | ||||
| @@ -114,11 +165,32 @@ function AddExperimentModal({ | |||||
| : null} | : null} | ||||
| </Select> | </Select> | ||||
| </Form.Item> | </Form.Item> | ||||
| {globalParam.map((item) => ( | |||||
| <Form.Item label={item.param_name} name={item.param_name} key={item.param_name}> | |||||
| <Input /> | |||||
| {globalParam.length > 0 && ( | |||||
| <Form.Item label="运行参数" tooltip="展示关联的流水线的参数,脱敏的参数以xxxx展示"> | |||||
| <div className={styles.global_param_item}> | |||||
| <Form.List name="global_param"> | |||||
| {(fields) => | |||||
| fields.map(({ key, name, ...restField }) => ( | |||||
| <Form.Item | |||||
| {...tailLayout} | |||||
| {...restField} | |||||
| key={key} | |||||
| label={getParamType(globalParam[name])} | |||||
| name={[name, 'param_value']} | |||||
| labelAlign="left" | |||||
| rules={getParamRules(globalParam[name]['param_type'])} | |||||
| > | |||||
| {getParamComponent( | |||||
| globalParam[name]['param_type'], | |||||
| globalParam[name]['is_sensitive'], | |||||
| )} | |||||
| </Form.Item> | |||||
| )) | |||||
| } | |||||
| </Form.List> | |||||
| </div> | |||||
| </Form.Item> | </Form.Item> | ||||
| ))} | |||||
| )} | |||||
| </Form> | </Form> | ||||
| </Modal> | </Modal> | ||||
| ); | ); | ||||
| @@ -119,22 +119,12 @@ function Experiment() { | |||||
| }; | }; | ||||
| // 创建或者编辑实验接口请求 | // 创建或者编辑实验接口请求 | ||||
| const handleAddExperiment = async (values) => { | const handleAddExperiment = async (values) => { | ||||
| const workflow_id = values['workflow_id']; | |||||
| let global_param = undefined; | |||||
| const pipeline = workflowList.find((v) => v.id === workflow_id); | |||||
| if (pipeline && pipeline.global_param) { | |||||
| const globalParamList = [...pipeline.global_param]; | |||||
| for (const item of globalParamList) { | |||||
| item.param_value = values[item.param_name]; | |||||
| values[item.param_name] = undefined; | |||||
| } | |||||
| global_param = JSON.stringify(globalParamList); | |||||
| } | |||||
| const params = { | |||||
| ...values, | |||||
| global_param, | |||||
| }; | |||||
| const global_param = JSON.stringify(values.global_param); | |||||
| if (!experimentId) { | if (!experimentId) { | ||||
| const params = { | |||||
| ...values, | |||||
| global_param, | |||||
| }; | |||||
| const [res, _] = await to(postExperiment(params)); | const [res, _] = await to(postExperiment(params)); | ||||
| if (res) { | if (res) { | ||||
| message.success('新建实验成功'); | message.success('新建实验成功'); | ||||
| @@ -142,7 +132,7 @@ function Experiment() { | |||||
| getList(); | getList(); | ||||
| } | } | ||||
| } else { | } else { | ||||
| const params = { ...values, id: experimentId }; | |||||
| const params = { ...values, global_param, id: experimentId }; | |||||
| const [res, _] = await to(putExperiment(params)); | const [res, _] = await to(putExperiment(params)); | ||||
| if (res) { | if (res) { | ||||
| message.success('编辑实验成功'); | message.success('编辑实验成功'); | ||||
| @@ -431,14 +421,16 @@ function Experiment() { | |||||
| rowExpandable: (record) => true, | rowExpandable: (record) => true, | ||||
| }} | }} | ||||
| /> | /> | ||||
| <AddExperimentModal | |||||
| isAdd={isAdd} | |||||
| open={isModalOpen} | |||||
| initialValues={addFormData} | |||||
| onCancel={handleCancel} | |||||
| onFinish={handleAddExperiment} | |||||
| workflowList={workflowList} | |||||
| /> | |||||
| {isModalOpen && ( | |||||
| <AddExperimentModal | |||||
| isAdd={isAdd} | |||||
| open={isModalOpen} | |||||
| initialValues={addFormData} | |||||
| onCancel={handleCancel} | |||||
| onFinish={handleAddExperiment} | |||||
| workflowList={workflowList} | |||||
| /> | |||||
| )} | |||||
| </div> | </div> | ||||
| ); | ); | ||||
| } | } | ||||
| @@ -8,7 +8,13 @@ | |||||
| top: 5px; | top: 5px; | ||||
| right: 0; | right: 0; | ||||
| } | } | ||||
| .add_button { | |||||
| .add_button_form_item { | |||||
| margin-top: 15px; | margin-top: 15px; | ||||
| &:first-child { | |||||
| margin-top: 0; | |||||
| } | |||||
| } | |||||
| .add_button_form_item .add_button { | |||||
| padding: 0; | padding: 0; | ||||
| } | } | ||||
| @@ -1,7 +1,12 @@ | |||||
| import { | |||||
| getParamComponent, | |||||
| getParamRules, | |||||
| } from '@/pages/Experiment/experimentText/addExperimentModal'; | |||||
| import { type PipelineGlobalParam } from '@/types'; | import { type PipelineGlobalParam } from '@/types'; | ||||
| import { to } from '@/utils/promise'; | import { to } from '@/utils/promise'; | ||||
| import { DeleteOutlined, PlusOutlined } from '@ant-design/icons'; | import { DeleteOutlined, PlusOutlined } from '@ant-design/icons'; | ||||
| import { Button, Drawer, Form, Input, Radio } from 'antd'; | import { Button, Drawer, Form, Input, Radio } from 'antd'; | ||||
| import { NamePath } from 'antd/es/form/interface'; | |||||
| import { forwardRef, useImperativeHandle } from 'react'; | import { forwardRef, useImperativeHandle } from 'react'; | ||||
| import styles from './globalParamsDrawer.less'; | import styles from './globalParamsDrawer.less'; | ||||
| @@ -26,6 +31,11 @@ const GlobalParamsDrawer = forwardRef( | |||||
| } | } | ||||
| }, | }, | ||||
| })); | })); | ||||
| const handleTypeChange = (name: NamePath) => { | |||||
| form.setFieldValue(name, null); | |||||
| }; | |||||
| return ( | return ( | ||||
| <Drawer | <Drawer | ||||
| rootStyle={{ marginTop: '45px' }} | rootStyle={{ marginTop: '45px' }} | ||||
| @@ -77,26 +87,41 @@ const GlobalParamsDrawer = forwardRef( | |||||
| label="类 型" | label="类 型" | ||||
| rules={[{ required: true, message: '请选择类型' }]} | rules={[{ required: true, message: '请选择类型' }]} | ||||
| > | > | ||||
| <Radio.Group> | |||||
| <Radio.Group | |||||
| onChange={() => handleTypeChange(['global_param', name, 'param_value'])} | |||||
| > | |||||
| <Radio value={1}>字符串</Radio> | <Radio value={1}>字符串</Radio> | ||||
| <Radio value={2}>整型</Radio> | <Radio value={2}>整型</Radio> | ||||
| <Radio value={3}>布尔类型</Radio> | <Radio value={3}>布尔类型</Radio> | ||||
| </Radio.Group> | </Radio.Group> | ||||
| </Form.Item> | </Form.Item> | ||||
| <Form.Item | <Form.Item | ||||
| {...restField} | |||||
| name={[name, 'param_value']} | |||||
| label="值" | |||||
| rules={[{ required: true, message: '请输入值' }]} | |||||
| noStyle | |||||
| shouldUpdate={(prev, cur) => | |||||
| prev.global_param?.[name]?.param_type !== | |||||
| cur.global_param?.[name]?.param_type | |||||
| } | |||||
| > | > | ||||
| <Input placeholder="请输入值" allowClear /> | |||||
| {({ getFieldValue }) => ( | |||||
| <Form.Item | |||||
| {...restField} | |||||
| name={[name, 'param_value']} | |||||
| label="值" | |||||
| rules={getParamRules( | |||||
| getFieldValue(['global_param', name, 'param_type']), | |||||
| true, | |||||
| )} | |||||
| > | |||||
| {getParamComponent(getFieldValue(['global_param', name, 'param_type']))} | |||||
| </Form.Item> | |||||
| )} | |||||
| </Form.Item> | </Form.Item> | ||||
| <Form.Item | <Form.Item | ||||
| {...restField} | {...restField} | ||||
| name={[name, 'is_sensitive']} | name={[name, 'is_sensitive']} | ||||
| label="脱敏显示" | label="脱敏显示" | ||||
| rules={[{ required: true, message: '请选择' }]} | rules={[{ required: true, message: '请选择' }]} | ||||
| tooltip="脱敏后的参数以*****显示" | |||||
| tooltip="展示关联的流水线的参数,脱敏的参数以xxxx展示" | |||||
| > | > | ||||
| <Radio.Group> | <Radio.Group> | ||||
| <Radio value={1}>是</Radio> | <Radio value={1}>是</Radio> | ||||
| @@ -111,11 +136,11 @@ const GlobalParamsDrawer = forwardRef( | |||||
| ></Button> | ></Button> | ||||
| </div> | </div> | ||||
| ))} | ))} | ||||
| <Form.Item> | |||||
| <Form.Item className={styles.add_button_form_item}> | |||||
| <Button | <Button | ||||
| className={styles.add_button} | className={styles.add_button} | ||||
| type="link" | type="link" | ||||
| onClick={add} | |||||
| onClick={() => add()} | |||||
| icon={<PlusOutlined />} | icon={<PlusOutlined />} | ||||
| > | > | ||||
| 流水线参数 | 流水线参数 | ||||
| @@ -101,13 +101,14 @@ const EditPipeline = () => { | |||||
| } | } | ||||
| const data = graph.save(); | const data = graph.save(); | ||||
| console.log(data); | console.log(data); | ||||
| let params = { | |||||
| const params = { | |||||
| ...locationParams, | ...locationParams, | ||||
| dag: JSON.stringify(data), | dag: JSON.stringify(data), | ||||
| global_param: JSON.stringify(res.global_param), | global_param: JSON.stringify(res.global_param), | ||||
| }; | }; | ||||
| saveWorkflow(params).then((ret) => { | saveWorkflow(params).then((ret) => { | ||||
| message.success('保存成功'); | message.success('保存成功'); | ||||
| closeParamsDrawer(); | |||||
| setTimeout(() => { | setTimeout(() => { | ||||
| if (val) { | if (val) { | ||||
| navgite({ pathname: `/pipeline` }); | navgite({ pathname: `/pipeline` }); | ||||
| @@ -1,6 +1,5 @@ | |||||
| // 流水线全局参数 | // 流水线全局参数 | ||||
| export type PipelineGlobalParam = { | export type PipelineGlobalParam = { | ||||
| workflow_id: number; | |||||
| param_name: string; | param_name: string; | ||||
| description: string; | description: string; | ||||
| param_type: number; | param_type: number; | ||||
| @@ -9,3 +9,4 @@ export function getNameByCode(list, code) { | |||||
| }); | }); | ||||
| return name; | return name; | ||||
| } | } | ||||