| @@ -1,3 +1,4 @@ | |||
| import { PipelineGlobalParamType, type PipelineGlobalParam } from '@/types'; | |||
| import { formatEnum } from '@/utils/format'; | |||
| import { Typography, type SelectProps } from 'antd'; | |||
| import classNames from 'classnames'; | |||
| @@ -16,6 +17,8 @@ type FormInfoProps = { | |||
| options?: SelectProps['options']; | |||
| /** 自定义节点 label、value 的字段 */ | |||
| fieldNames?: SelectProps['fieldNames']; | |||
| /** 全局参数 */ | |||
| globalParams?: PipelineGlobalParam[] | null; | |||
| /** 自定义类名 */ | |||
| className?: string; | |||
| /** 自定义样式 */ | |||
| @@ -32,12 +35,29 @@ function FormInfo({ | |||
| select = false, | |||
| options, | |||
| fieldNames, | |||
| globalParams, | |||
| className, | |||
| style, | |||
| }: FormInfoProps) { | |||
| let showValue = value; | |||
| if (value && typeof value === 'object' && valuePropName) { | |||
| showValue = value[valuePropName]; | |||
| const reg = /^\$\{(.*)\}$/; | |||
| if (value.fromSelect && Array.isArray(globalParams) && globalParams.length > 0) { | |||
| const match = reg.exec(showValue); | |||
| if (match) { | |||
| const paramName = match[1]; | |||
| const foundParam = globalParams.find((v) => v.param_name === paramName); | |||
| if (foundParam) { | |||
| showValue = | |||
| foundParam.param_type === PipelineGlobalParamType.Boolean // 布尔类型转换 | |||
| ? foundParam.param_value | |||
| ? 'true' | |||
| : 'false' | |||
| : foundParam.param_value; | |||
| } | |||
| } | |||
| } | |||
| } else if (select === true && options) { | |||
| let _options: SelectProps['options'] = options; | |||
| if (fieldNames) { | |||
| @@ -110,13 +110,14 @@ function ParameterSelect({ | |||
| onChange?.(selectValue); | |||
| } | |||
| } else { | |||
| const selectValue = text ? text : ''; | |||
| if (typeof value === 'object' && value !== null) { | |||
| onChange?.({ | |||
| ...value, | |||
| value: text, | |||
| value: selectValue, | |||
| }); | |||
| } else { | |||
| onChange?.(text); | |||
| onChange?.(selectValue); | |||
| } | |||
| } | |||
| }; | |||
| @@ -169,16 +169,16 @@ function ExperimentList({ type }: ExperimentListProps) { | |||
| const handleMessage = (e: MessageEvent) => { | |||
| const { type, payload } = e.data; | |||
| if (type === ExperimentCompleted) { | |||
| const { experimentId, experimentInsId, status, finishTime } = payload; | |||
| const { experimentId, experimentInsId, status /*finishTime*/ } = payload; | |||
| const currentIns = experimentInsList.find((v) => v.id === experimentInsId); | |||
| console.log( | |||
| '实验实例状态变化', | |||
| currentIns?.status, | |||
| status, | |||
| experimentId, | |||
| experimentInsId, | |||
| finishTime, | |||
| ); | |||
| // console.log( | |||
| // '实验实例状态变化', | |||
| // currentIns?.status, | |||
| // status, | |||
| // experimentId, | |||
| // experimentInsId, | |||
| // finishTime, | |||
| // ); | |||
| if ( | |||
| !currentIns || | |||
| @@ -95,16 +95,13 @@ function ExperimentText() { | |||
| return; | |||
| } | |||
| const workflow = parseJsonText(dag); | |||
| const workflow = dag; | |||
| const experimentStatusObjs = parseJsonText(nodes_status); | |||
| if (!workflow || !workflow.nodes) { | |||
| return; | |||
| } | |||
| workflow.nodes.forEach((item) => { | |||
| item.in_parameters = parseJsonText(item.in_parameters); | |||
| item.out_parameters = parseJsonText(item.out_parameters); | |||
| item.control_strategy = parseJsonText(item.control_strategy); | |||
| item.imgName = item.img.slice(0, item.img.length - 4); | |||
| }); | |||
| workflowRef.current = workflow; | |||
| @@ -140,8 +137,11 @@ function ExperimentText() { | |||
| } else if (status === ExperimentStatus.Running) { | |||
| // 如果状态是 Running,打开第一个 Running 或者 pending 的节点,如果没有,则打开第一个节点 | |||
| const node = | |||
| workflow.nodes.find((item) => item.experimentStatus === ExperimentStatus.Running || item.experimentStatus === ExperimentStatus.Pending) ?? | |||
| workflow.nodes[0]; | |||
| workflow.nodes.find( | |||
| (item) => | |||
| item.experimentStatus === ExperimentStatus.Running || | |||
| item.experimentStatus === ExperimentStatus.Pending, | |||
| ) ?? workflow.nodes[0]; | |||
| if (node) { | |||
| setExperimentNodeData(node); | |||
| openPropsDrawer(); | |||
| @@ -567,12 +567,13 @@ function ExperimentText() { | |||
| instanceNodeStatus={experimentNodeData.experimentStatus} | |||
| instanceNodeStartTime={experimentNodeData.experimentStartTime} | |||
| instanceNodeEndTime={experimentNodeData.experimentEndTime} | |||
| globalParams={experimentIns?.global_param} | |||
| ></ExperimentDrawer> | |||
| ) : null} | |||
| <ParamsModal | |||
| open={paramsModalOpen} | |||
| onCancel={closeParamsModal} | |||
| globalParam={experimentIns?.global_param} | |||
| globalParams={experimentIns?.global_param} | |||
| ></ParamsModal> | |||
| </div> | |||
| ); | |||
| @@ -1,7 +1,7 @@ | |||
| import createExperimentIcon from '@/assets/img/create-experiment.png'; | |||
| import editExperimentIcon from '@/assets/img/edit-experiment.png'; | |||
| import KFModal from '@/components/KFModal'; | |||
| import { type PipelineGlobalParam } from '@/types'; | |||
| import { PipelineGlobalParamType, type PipelineGlobalParam } from '@/types'; | |||
| import { to } from '@/utils/promise'; | |||
| import { Button, Form, Input, Radio, Select, Typography, type FormRule } from 'antd'; | |||
| import { useState } from 'react'; | |||
| @@ -32,7 +32,7 @@ interface Workflow { | |||
| // 根据参数设置输入组件 | |||
| export const getParamComponent = (paramType: number): JSX.Element => { | |||
| // 防止后台返回不是 number 类型 | |||
| if (Number(paramType) === 3) { | |||
| if (Number(paramType) === PipelineGlobalParamType.Boolean) { | |||
| return ( | |||
| <Radio.Group> | |||
| <Radio value={1}>是</Radio> | |||
| @@ -50,7 +50,7 @@ export const getParamComponent = (paramType: number): JSX.Element => { | |||
| export const getParamRules = (paramType: number, required: boolean = false): FormRule[] => { | |||
| const rules = []; | |||
| // 防止后台返回不是 number 类型 | |||
| if (Number(paramType) === 2) { | |||
| if (Number(paramType) === PipelineGlobalParamType.Number) { | |||
| rules.push({ | |||
| pattern: /^-?((0(\.0*[1-9]\d*)?)|([1-9]\d*(\.\d+)?))$/, | |||
| message: '整型必须是数字', | |||
| @@ -64,10 +64,10 @@ export const getParamRules = (paramType: number, required: boolean = false): For | |||
| // 根据参数设置 label | |||
| export const getParamLabel = (param: PipelineGlobalParam): React.ReactNode => { | |||
| const paramTypes: Readonly<Record<number, string>> = { | |||
| 1: '字符串', | |||
| 2: '整型', | |||
| 3: '布尔类型', | |||
| const paramTypes: Readonly<Record<PipelineGlobalParamType, string>> = { | |||
| [PipelineGlobalParamType.String]: '字符串', | |||
| [PipelineGlobalParamType.Number]: '整型', | |||
| [PipelineGlobalParamType.Boolean]: '布尔类型', | |||
| }; | |||
| const label = param.param_name + `(${paramTypes[param.param_type]})`; | |||
| return <Typography.Text ellipsis={{ tooltip: label }}>{label}</Typography.Text>; | |||
| @@ -1,7 +1,7 @@ | |||
| import RunDuration from '@/components/RunDuration'; | |||
| import { ExperimentStatus } from '@/enums'; | |||
| import { experimentStatusInfo } from '@/pages/Experiment/status'; | |||
| import { PipelineNodeModelSerialize } from '@/types'; | |||
| import { PipelineNodeModelSerialize, type PipelineGlobalParam } from '@/types'; | |||
| import { formatDate } from '@/utils/date'; | |||
| import { CloseOutlined, DatabaseOutlined, ProfileOutlined } from '@ant-design/icons'; | |||
| import { Drawer, Tabs, Typography } from 'antd'; | |||
| @@ -25,6 +25,7 @@ type ExperimentDrawerProps = { | |||
| instanceNodeStatus?: ExperimentStatus; // 实例节点状态 | |||
| instanceNodeStartTime?: string; // 开始时间 | |||
| instanceNodeEndTime?: string; // 在定时刷新实验实例状态中,会经常变化 | |||
| globalParams?: PipelineGlobalParam[] | null; // 全局参数 | |||
| }; | |||
| const ExperimentDrawer = ({ | |||
| @@ -41,6 +42,7 @@ const ExperimentDrawer = ({ | |||
| instanceNodeStatus, | |||
| instanceNodeStartTime, | |||
| instanceNodeEndTime, | |||
| globalParams, | |||
| }: ExperimentDrawerProps) => { | |||
| // 如果性能有问题,可以进一步拆解 | |||
| const items = useMemo( | |||
| @@ -66,7 +68,7 @@ const ExperimentDrawer = ({ | |||
| key: '2', | |||
| label: '配置参数', | |||
| icon: <DatabaseOutlined />, | |||
| children: <ExperimentParameter nodeData={instanceNodeData} />, | |||
| children: <ExperimentParameter nodeData={instanceNodeData} globalParams={globalParams} />, | |||
| }, | |||
| { | |||
| key: '3', | |||
| @@ -94,6 +96,7 @@ const ExperimentDrawer = ({ | |||
| experimentName, | |||
| experimentId, | |||
| pipelineId, | |||
| globalParams, | |||
| ], | |||
| ); | |||
| @@ -15,4 +15,24 @@ | |||
| font-size: @font-size; | |||
| background: #f8fbff; | |||
| } | |||
| &__form-list { | |||
| :global { | |||
| .ant-row { | |||
| padding: 0 !important; | |||
| } | |||
| } | |||
| &:last-child { | |||
| :global { | |||
| .ant-form-item { | |||
| margin-bottom: 0 !important; | |||
| } | |||
| } | |||
| } | |||
| } | |||
| &__list-empty { | |||
| color: @text-color-tertiary; | |||
| } | |||
| } | |||
| @@ -1,25 +1,92 @@ | |||
| import FormInfo from '@/components/FormInfo'; | |||
| import ParameterSelect from '@/components/ParameterSelect'; | |||
| import ParameterSelect, { type ParameterSelectDataType } from '@/components/ParameterSelect'; | |||
| import SubAreaTitle from '@/components/SubAreaTitle'; | |||
| import { PipelineNodeModelSerialize } from '@/types'; | |||
| import { Form } from 'antd'; | |||
| import { ComponentType } from '@/enums'; | |||
| import type { | |||
| PipelineGlobalParam, | |||
| PipelineNodeModelParameter, | |||
| PipelineNodeModelSerialize, | |||
| } from '@/types'; | |||
| import { Flex, Form } from 'antd'; | |||
| import styles from './index.less'; | |||
| type ExperimentParameterProps = { | |||
| nodeData: PipelineNodeModelSerialize; | |||
| globalParams?: PipelineGlobalParam[] | null; // 全局参数 | |||
| }; | |||
| function ExperimentParameter({ nodeData }: ExperimentParameterProps) { | |||
| function ExperimentParameter({ nodeData, globalParams }: ExperimentParameterProps) { | |||
| // 表单组件 | |||
| const getFormComponent = ( | |||
| item: { key: string; value: PipelineNodeModelParameter }, | |||
| parentName: string, | |||
| ) => { | |||
| return ( | |||
| <Form.Item | |||
| key={item.key} | |||
| name={[parentName, item.key]} | |||
| label={item.value.label + '(' + item.key + ')'} | |||
| rules={[{ required: item.value.require ? true : false }]} | |||
| > | |||
| {item.value.type === ComponentType.Map && ( | |||
| <Form.List name={[parentName, item.key, 'value']}> | |||
| {(fields) => ( | |||
| <> | |||
| {fields.length > 0 ? ( | |||
| fields.map(({ key, name, ...restField }) => ( | |||
| <Flex | |||
| key={key} | |||
| gap="0 8px" | |||
| style={{ width: '100%' }} | |||
| className={styles['experiment-parameter__form-list']} | |||
| > | |||
| <Form.Item | |||
| {...restField} | |||
| name={[name, 'name']} | |||
| style={{ flex: 1, minWidth: 0 }} | |||
| > | |||
| <FormInfo /> | |||
| </Form.Item> | |||
| <span style={{ lineHeight: '32px' }}>=</span> | |||
| <Form.Item | |||
| {...restField} | |||
| name={[name, 'value']} | |||
| style={{ flex: 1, minWidth: 0 }} | |||
| > | |||
| <FormInfo valuePropName="showValue" globalParams={globalParams} /> | |||
| </Form.Item> | |||
| </Flex> | |||
| )) | |||
| ) : ( | |||
| <div className={styles['experiment-parameter__list-empty']}>无参数</div> | |||
| )} | |||
| </> | |||
| )} | |||
| </Form.List> | |||
| )} | |||
| {item.value.type === ComponentType.Select && | |||
| (['dataset', 'model', 'service', 'resource'].includes(item.value.item_type) ? ( | |||
| <ParameterSelect dataType={item.value.item_type as ParameterSelectDataType} display /> | |||
| ) : null)} | |||
| {item.value.type !== ComponentType.Map && item.value.type !== ComponentType.Select && ( | |||
| <FormInfo valuePropName="showValue" globalParams={globalParams} /> | |||
| )} | |||
| </Form.Item> | |||
| ); | |||
| }; | |||
| // 基本参数 | |||
| const basicParametersList = Object.entries(nodeData.task_info ?? {}) | |||
| .map(([key, value]) => ({ | |||
| key, | |||
| value, | |||
| })) | |||
| .filter((v) => v.value.visible === true); | |||
| // 控制策略 | |||
| // const controlStrategyList = Object.entries(nodeData.control_strategy ?? {}).map( | |||
| // ([key, value]) => ({ key, value }), | |||
| // ); | |||
| const nodeId = nodeData.id; | |||
| const hasTaskInfo = | |||
| nodeId && | |||
| !nodeId.startsWith('git-clone') && | |||
| !nodeId.startsWith('dataset-export') && | |||
| !nodeId.startsWith('model-export'); | |||
| const controlStrategyList = Object.entries(nodeData.control_strategy ?? {}) | |||
| .map(([key, value]) => ({ key, value })) | |||
| .filter((v) => v.value.visible === true); | |||
| // 输入参数 | |||
| const inParametersList = Object.entries(nodeData.in_parameters ?? {}).map(([key, value]) => ({ | |||
| @@ -80,96 +147,56 @@ function ExperimentParameter({ nodeData }: ExperimentParameterProps) { | |||
| > | |||
| <FormInfo /> | |||
| </Form.Item> | |||
| {hasTaskInfo && ( | |||
| {basicParametersList.length + controlStrategyList.length > 0 && ( | |||
| <div className={styles['experiment-parameter__title']}> | |||
| <SubAreaTitle | |||
| image={require('@/assets/img/duty-message.png')} | |||
| title="任务信息" | |||
| ></SubAreaTitle> | |||
| </div> | |||
| )} | |||
| {/* 基本参数 */} | |||
| {basicParametersList.map((item) => getFormComponent(item, 'task_info'))} | |||
| {/* 控制参数 */} | |||
| {controlStrategyList.map((item) => getFormComponent(item, 'control_strategy'))} | |||
| {/* 输入参数 */} | |||
| {inParametersList.length > 0 && ( | |||
| <> | |||
| <div className={styles['experiment-parameter__title']}> | |||
| <SubAreaTitle | |||
| image={require('@/assets/img/duty-message.png')} | |||
| title="任务信息" | |||
| title="输入参数" | |||
| ></SubAreaTitle> | |||
| </div> | |||
| <Form.Item | |||
| label="镜像" | |||
| name="image" | |||
| rules={[ | |||
| { | |||
| required: true, | |||
| message: '请输入镜像', | |||
| }, | |||
| ]} | |||
| > | |||
| <FormInfo /> | |||
| </Form.Item> | |||
| <Form.Item label="工作目录" name="working_directory"> | |||
| <FormInfo /> | |||
| </Form.Item> | |||
| {inParametersList.map((item) => getFormComponent(item, 'in_parameters'))} | |||
| </> | |||
| )} | |||
| <Form.Item label="启动命令" name="command"> | |||
| <FormInfo textArea /> | |||
| </Form.Item> | |||
| <Form.Item | |||
| label="资源规格" | |||
| name="resources_standard" | |||
| rules={[ | |||
| { | |||
| required: true, | |||
| message: '请输入资源规格', | |||
| }, | |||
| ]} | |||
| > | |||
| <ParameterSelect dataType="resource" placeholder="请选择资源规格" display /> | |||
| </Form.Item> | |||
| {/* <Form.Item label="挂载路径" name="mount_path"> | |||
| <FormInfo /> | |||
| </Form.Item> */} | |||
| <Form.Item label="环境变量" name="env_variables"> | |||
| <FormInfo textArea /> | |||
| </Form.Item> | |||
| {/* {controlStrategyList.map((item) => ( | |||
| <Form.Item key={item.key} name={['control_strategy', item.key]} label={item.value.label}> | |||
| <FormInfo valuePropName="showValue" /> | |||
| </Form.Item> | |||
| ))} */} | |||
| {/* 输出参数 */} | |||
| {outParametersList.length > 0 && ( | |||
| <> | |||
| <div className={styles['experiment-parameter__title']}> | |||
| <SubAreaTitle | |||
| image={require('@/assets/img/duty-message.png')} | |||
| title="输出参数" | |||
| ></SubAreaTitle> | |||
| </div> | |||
| {outParametersList.map((item) => ( | |||
| <Form.Item | |||
| key={item.key} | |||
| name={['out_parameters', item.key]} | |||
| label={item.value.label + '(' + item.key + ')'} | |||
| rules={[{ required: item.value.require ? true : false }]} | |||
| > | |||
| <FormInfo valuePropName="showValue" /> | |||
| </Form.Item> | |||
| ))} | |||
| </> | |||
| )} | |||
| <div className={styles['experiment-parameter__title']}> | |||
| <SubAreaTitle | |||
| image={require('@/assets/img/duty-message.png')} | |||
| title="输入参数" | |||
| ></SubAreaTitle> | |||
| </div> | |||
| {inParametersList.map((item) => ( | |||
| <Form.Item | |||
| key={item.key} | |||
| name={['in_parameters', item.key]} | |||
| label={item.value.label + '(' + item.key + ')'} | |||
| rules={[{ required: item.value.require ? true : false }]} | |||
| > | |||
| {item.value.type === 'select' ? ( | |||
| ['dataset', 'model', 'service', 'resource'].includes(item.value.item_type) ? ( | |||
| <ParameterSelect dataType={item.value.item_type as any} display /> | |||
| ) : null | |||
| ) : ( | |||
| <FormInfo valuePropName="showValue" /> | |||
| )} | |||
| </Form.Item> | |||
| ))} | |||
| <div className={styles['experiment-parameter__title']}> | |||
| <SubAreaTitle | |||
| image={require('@/assets/img/duty-message.png')} | |||
| title="输出参数" | |||
| ></SubAreaTitle> | |||
| </div> | |||
| {outParametersList.map((item) => ( | |||
| <Form.Item | |||
| key={item.key} | |||
| name={['out_parameters', item.key]} | |||
| label={item.value.label + '(' + item.key + ')'} | |||
| rules={[{ required: item.value.require ? true : false }]} | |||
| > | |||
| <FormInfo valuePropName="showValue" /> | |||
| </Form.Item> | |||
| ))} | |||
| </Form> | |||
| ); | |||
| } | |||
| @@ -14,10 +14,10 @@ import styles from './index.less'; | |||
| type ParamsModalProps = { | |||
| open: boolean; | |||
| onCancel: () => void; | |||
| globalParam?: PipelineGlobalParam[] | null; | |||
| globalParams?: PipelineGlobalParam[] | null; | |||
| }; | |||
| function ParamsModal({ open, onCancel, globalParam = [] }: ParamsModalProps) { | |||
| function ParamsModal({ open, onCancel, globalParams = [] }: ParamsModalProps) { | |||
| return ( | |||
| <KFModal | |||
| title="执行参数" | |||
| @@ -28,13 +28,13 @@ function ParamsModal({ open, onCancel, globalParam = [] }: ParamsModalProps) { | |||
| cancelButtonProps={{ style: { display: 'none' } }} | |||
| width={825} | |||
| > | |||
| {Array.isArray(globalParam) && globalParam.length > 0 ? ( | |||
| {Array.isArray(globalParams) && globalParams.length > 0 ? ( | |||
| <div className={styles['params-container']}> | |||
| <Form | |||
| name="view_params_form" | |||
| labelCol={{ span: 6 }} | |||
| wrapperCol={{ span: 18 }} | |||
| initialValues={{ global_param: globalParam }} | |||
| initialValues={{ global_param: globalParams }} | |||
| labelAlign="left" | |||
| disabled | |||
| > | |||
| @@ -45,9 +45,9 @@ function ParamsModal({ open, onCancel, globalParam = [] }: ParamsModalProps) { | |||
| {...restField} | |||
| key={key} | |||
| name={[name, 'param_value']} | |||
| label={getParamLabel(globalParam[name])} | |||
| label={getParamLabel(globalParams[name])} | |||
| > | |||
| {getParamComponent(globalParam[name]['param_type'])} | |||
| {getParamComponent(globalParams[name]['param_type'])} | |||
| </Form.Item> | |||
| )) | |||
| } | |||
| @@ -226,14 +226,14 @@ function Experiment() { | |||
| if (type === ExperimentCompleted) { | |||
| const { experimentId, experimentInsId, status, finishTime } = payload; | |||
| const currentIns = experimentInsList.find((v) => v.id === experimentInsId); | |||
| console.log( | |||
| '实验实例状态变化', | |||
| currentIns?.status, | |||
| status, | |||
| experimentId, | |||
| experimentInsId, | |||
| finishTime, | |||
| ); | |||
| // console.log( | |||
| // '实验实例状态变化', | |||
| // currentIns?.status, | |||
| // status, | |||
| // experimentId, | |||
| // experimentInsId, | |||
| // finishTime, | |||
| // ); | |||
| if ( | |||
| !currentIns || | |||
| @@ -1,6 +1,6 @@ | |||
| import KFIcon from '@/components/KFIcon'; | |||
| import { getParamComponent, getParamRules } from '@/pages/Experiment/components/AddExperimentModal'; | |||
| import { type PipelineGlobalParam } from '@/types'; | |||
| import { type PipelineGlobalParam, PipelineGlobalParamType } from '@/types'; | |||
| import { to } from '@/utils/promise'; | |||
| import { modalConfirm } from '@/utils/ui'; | |||
| import { PlusOutlined } from '@ant-design/icons'; | |||
| @@ -131,9 +131,9 @@ const GlobalParamsDrawer = forwardRef( | |||
| <Radio.Group | |||
| onChange={() => handleTypeChange(['global_param', name, 'param_value'])} | |||
| > | |||
| <Radio value={1}>字符串</Radio> | |||
| <Radio value={2}>整型</Radio> | |||
| <Radio value={3}>布尔类型</Radio> | |||
| <Radio value={PipelineGlobalParamType.String}>字符串</Radio> | |||
| <Radio value={PipelineGlobalParamType.Number}>整型</Radio> | |||
| <Radio value={PipelineGlobalParamType.Boolean}>布尔类型</Radio> | |||
| </Radio.Group> | |||
| </Form.Item> | |||
| <Form.Item | |||
| @@ -614,12 +614,14 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete | |||
| <Input disabled /> | |||
| </Form.Item> | |||
| <div className={styles['pipeline-drawer__title']}> | |||
| <SubAreaTitle | |||
| image={require('@/assets/img/duty-message.png')} | |||
| title="任务信息" | |||
| ></SubAreaTitle> | |||
| </div> | |||
| {basicParametersList.length + controlStrategyList.length > 0 && ( | |||
| <div className={styles['pipeline-drawer__title']}> | |||
| <SubAreaTitle | |||
| image={require('@/assets/img/duty-message.png')} | |||
| title="任务信息" | |||
| ></SubAreaTitle> | |||
| </div> | |||
| )} | |||
| {/* 基本参数 */} | |||
| {basicParametersList.map((item) => ( | |||
| @@ -663,6 +665,7 @@ const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParamete | |||
| ))} | |||
| </> | |||
| )} | |||
| {/* 输出参数 */} | |||
| {outParametersList.length > 0 && ( | |||
| <> | |||
| @@ -29,11 +29,17 @@ export type GlobalInitialState = { | |||
| clientInfo?: ClientInfo; | |||
| }; | |||
| export enum PipelineGlobalParamType { | |||
| String = 1, | |||
| Number = 2, | |||
| Boolean = 3, | |||
| } | |||
| // 流水线全局参数 | |||
| export type PipelineGlobalParam = { | |||
| param_name: string; | |||
| description: string; | |||
| param_type: number; | |||
| param_type: PipelineGlobalParamType; | |||
| param_value: number | string | boolean; | |||
| is_sensitive: number; | |||
| }; | |||