|
- import KFIcon from '@/components/KFIcon';
- import ParameterInput, { requiredValidator } from '@/components/ParameterInput';
- import ParameterSelect from '@/components/ParameterSelect';
- import SubAreaTitle from '@/components/SubAreaTitle';
- import { CommonTabKeys } from '@/enums';
- import { useComputingResource } from '@/hooks/resource';
- import { canInput, createMenuItems } from '@/pages/Pipeline/Info/utils';
- import {
- PipelineGlobalParam,
- PipelineNodeModel,
- PipelineNodeModelParameter,
- PipelineNodeModelSerialize,
- } from '@/types';
- import { openAntdModal } from '@/utils/modal';
- import { to } from '@/utils/promise';
- import { INode } from '@antv/g6';
- import { Button, Drawer, Form, Input, MenuProps, Select } from 'antd';
- import { NamePath } from 'antd/es/form/interface';
- import { forwardRef, useImperativeHandle, useState } from 'react';
- import CodeSelectorModal from '../CodeSelectorModal';
- import PropsLabel from '../PropsLabel';
- import ResourceSelectorModal, {
- ResourceSelectorType,
- selectorTypeConfig,
- } from '../ResourceSelectorModal';
- import styles from './index.less';
- const { TextArea } = Input;
-
- type PipelineNodeParameterProps = {
- onFormChange: (data: PipelineNodeModelSerialize) => void;
- };
-
- const PipelineNodeParameter = forwardRef(({ onFormChange }: PipelineNodeParameterProps, ref) => {
- const [form] = Form.useForm();
- const [stagingItem, setStagingItem] = useState<PipelineNodeModelSerialize>(
- {} as PipelineNodeModelSerialize,
- );
- const [open, setOpen] = useState(false);
- const [resourceStandardList, filterResourceStandard] = useComputingResource(); // 资源规模
- const [menuItems, setMenuItems] = useState<MenuProps['items']>([]);
-
- const afterOpenChange = async () => {
- if (!open) {
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- const [_values, error] = await to(form.validateFields());
- const fields = form.getFieldsValue();
- const control_strategy = JSON.stringify(fields.control_strategy);
- const in_parameters = JSON.stringify(fields.in_parameters);
- const out_parameters = JSON.stringify(fields.out_parameters);
- // console.log('getFieldsValue', fields);
-
- const res = {
- ...stagingItem,
- ...fields,
- control_strategy: control_strategy,
- in_parameters: in_parameters,
- out_parameters: out_parameters,
- formError: !!error,
- };
-
- console.log('res', res);
- onFormChange(res);
- }
- };
- const onClose = () => {
- setOpen(false);
- };
-
- useImperativeHandle(ref, () => ({
- showDrawer(
- model: PipelineNodeModel,
- params: PipelineGlobalParam[],
- parentNodes: INode[],
- validate: boolean = false,
- ) {
- try {
- const nodeData: PipelineNodeModelSerialize = {
- ...model,
- in_parameters: JSON.parse(model.in_parameters),
- out_parameters: JSON.parse(model.out_parameters),
- control_strategy: JSON.parse(model.control_strategy),
- };
- console.log('model', nodeData);
- setStagingItem({
- ...nodeData,
- });
- form.resetFields();
- form.setFieldsValue({
- ...nodeData,
- });
- if (validate) {
- form.validateFields();
- }
- } catch (error) {
- console.error('JSON.parse error: ', error);
- }
- setOpen(true);
-
- // 参数下拉菜单
- setMenuItems(createMenuItems(params, parentNodes));
- },
- close: () => {
- onClose();
- },
- validateFields: async () => {
- if (!open) {
- return;
- }
- const [values, error] = await to(form.validateFields());
- if (!error && values) {
- return values;
- } else {
- form.scrollToField((error as any)?.errorFields?.[0]?.name, { block: 'center' });
- return Promise.reject(error);
- }
- },
- }));
-
- // ref 类型选择
- const selectRefData = (
- formItemName: NamePath,
- item: PipelineNodeModelParameter | Pick<PipelineNodeModelParameter, 'item_type'>,
- ) => {
- if (item.item_type === 'code') {
- selectCodeConfig(formItemName, item);
- } else {
- selectResource(formItemName, item);
- }
- };
-
- // 选择代码配置
- const selectCodeConfig = (
- formItemName: NamePath,
- item: PipelineNodeModelParameter | Pick<PipelineNodeModelParameter, 'item_type'>,
- ) => {
- const { close } = openAntdModal(CodeSelectorModal, {
- onOk: (res) => {
- if (res) {
- const value = JSON.stringify({
- id: res.id,
- name: res.code_repo_name,
- code_path: res.git_url,
- branch: res.git_branch,
- username: res.git_user_name,
- password: res.git_password,
- ssh_private_key: res.ssh_key,
- });
- form.setFieldValue(formItemName, {
- ...item,
- value,
- showValue: res.code_repo_name,
- fromSelect: true,
- });
- }
- close();
- },
- });
- };
-
- // 选择数据集、模型、镜像
- const selectResource = (
- formItemName: NamePath,
- item: PipelineNodeModelParameter | Pick<PipelineNodeModelParameter, 'item_type'>,
- ) => {
- let type: ResourceSelectorType;
- switch (item.item_type) {
- case 'dataset':
- type = ResourceSelectorType.Dataset;
- break;
- case 'model':
- type = ResourceSelectorType.Model;
- break;
- default:
- type = ResourceSelectorType.Mirror;
- break;
- }
- const fieldValue = form.getFieldValue(formItemName);
- const activeTab = fieldValue?.activeTab as CommonTabKeys | undefined;
- const expandedKeys = Array.isArray(fieldValue?.expandedKeys) ? fieldValue?.expandedKeys : [];
- const checkedKeys = Array.isArray(fieldValue?.checkedKeys) ? fieldValue?.checkedKeys : [];
- const { close } = openAntdModal(ResourceSelectorModal, {
- type,
- defaultExpandedKeys: expandedKeys,
- defaultCheckedKeys: checkedKeys,
- defaultActiveTab: activeTab,
- onOk: (res) => {
- if (res) {
- if (type === ResourceSelectorType.Mirror) {
- const { activeTab, id, version, path } = res;
- if (formItemName === 'image') {
- // 单独的选择镜像
- form.setFieldValue(formItemName, path);
- } else {
- // 输入参数选择镜像
- form.setFieldValue(formItemName, {
- ...item,
- value: path,
- showValue: path,
- fromSelect: true,
- activeTab,
- expandedKeys: [id],
- checkedKeys: [`${id}-${version}`],
- });
- }
- } else {
- const { activeTab, id, name, version, path, identifier, owner } = res;
- const value = JSON.stringify({
- id,
- name,
- version,
- path,
- identifier,
- owner,
- });
- const showValue = `${name}:${version}`;
- form.setFieldValue(formItemName, {
- ...item,
- value,
- showValue,
- fromSelect: true,
- activeTab,
- expandedKeys: [id],
- checkedKeys: [`${id}-${version}`],
- });
- }
- } else {
- if (type === ResourceSelectorType.Mirror && formItemName === 'image') {
- form.setFieldValue(formItemName, undefined);
- } else {
- form.setFieldValue(formItemName, {
- ...item,
- value: undefined,
- showValue: undefined,
- fromSelect: false,
- activeTab: undefined,
- expandedKeys: [],
- checkedKeys: [],
- });
- }
- }
- form.validateFields([formItemName]);
- close();
- },
- });
- };
-
- // 获取选择数据集、模型后面按钮 icon
- const getSelectBtnIcon = (item: { item_type: string }) => {
- const type = item.item_type;
- if (type === 'code') {
- return <KFIcon type="icon-xuanzedaimapeizhi" />;
- }
-
- let selectorType: ResourceSelectorType;
- if (type === 'dataset') {
- selectorType = ResourceSelectorType.Dataset;
- } else if (type === 'model') {
- selectorType = ResourceSelectorType.Model;
- } else {
- selectorType = ResourceSelectorType.Mirror;
- }
-
- return <KFIcon type={selectorTypeConfig[selectorType].buttonIcon} />;
- };
-
- // 参数回填
- const handleParameterClick = (name: NamePath, value: any) => {
- form.setFieldValue(name, value);
- };
-
- // form item label
- const getLabel = (
- item: { key: string; value: PipelineNodeModelParameter },
- namePrefix: string,
- ) => {
- return item.value.type === 'select' ? (
- item.value.label + '(' + item.key + ')'
- ) : (
- <PropsLabel
- menuItems={menuItems}
- title={item.value.label + '(' + item.key + ')'}
- onClick={(value) => {
- handleParameterClick([namePrefix, item.key], {
- ...item.value,
- value,
- fromSelect: true,
- showValue: value,
- });
- }}
- />
- );
- };
-
- // 必填项校验规则
- const getFormRules = (item: { key: string; value: PipelineNodeModelParameter }) => {
- return item.value.require
- ? [
- {
- validator: requiredValidator,
- message: '必填项',
- },
- ]
- : [];
- };
-
- // 控制策略
- const controlStrategyList = Object.entries(stagingItem.control_strategy ?? {}).map(
- ([key, value]) => ({ key, value }),
- );
-
- // 输入参数
- const inParametersList = Object.entries(stagingItem.in_parameters ?? {}).map(([key, value]) => ({
- key,
- value,
- }));
-
- // 输出参数
- const outParametersList = Object.entries(stagingItem.out_parameters ?? {}).map(
- ([key, value]) => ({ key, value }),
- );
-
- return (
- <Drawer
- title="编辑任务"
- placement="right"
- rootStyle={{ marginTop: '52px' }}
- getContainer={false}
- closeIcon={false}
- onClose={onClose}
- afterOpenChange={afterOpenChange}
- open={open}
- width={520}
- className={styles['pipeline-drawer']}
- >
- <Form
- name="form"
- form={form}
- layout="vertical"
- labelCol={{
- span: 24,
- }}
- wrapperCol={{
- span: 24,
- }}
- style={{
- maxWidth: 600,
- }}
- autoComplete="off"
- scrollToFirstError
- >
- <div className={styles['pipeline-drawer__title']}>
- <SubAreaTitle
- image={require('@/assets/img/static-message.png')}
- title="基本信息"
- ></SubAreaTitle>
- </div>
- <Form.Item
- label="任务名称"
- name="label"
- rules={[
- {
- required: true,
- message: '请输入任务名称',
- },
- ]}
- >
- <Input placeholder="请输入任务名称" allowClear />
- </Form.Item>
- <Form.Item
- label="任务ID"
- name="id"
- rules={[
- {
- required: true,
- message: '请输入任务id',
- },
- ]}
- >
- <Input disabled />
- </Form.Item>
- <div className={styles['pipeline-drawer__title']}>
- <SubAreaTitle
- image={require('@/assets/img/duty-message.png')}
- title="任务信息"
- ></SubAreaTitle>
- </div>
- <Form.Item label="镜像" required>
- <div className={styles['pipeline-drawer__ref-row']}>
- <Form.Item name="image" noStyle rules={[{ required: true, message: '请输入镜像' }]}>
- <Input placeholder="请输入或选择镜像" allowClear />
- </Form.Item>
- <Form.Item noStyle>
- <Button
- type="link"
- size="small"
- icon={getSelectBtnIcon({ item_type: 'image' })}
- onClick={() => selectResource('image', { item_type: 'image' })}
- className={styles['pipeline-drawer__ref-row__select-button']}
- >
- 选择镜像
- </Button>
- </Form.Item>
- </div>
- </Form.Item>
- <Form.Item
- name="working_directory"
- label={
- <PropsLabel
- menuItems={menuItems}
- title="工作目录"
- onClick={(value) => {
- handleParameterClick('working_directory', value);
- }}
- />
- }
- >
- <Input placeholder="请输入工作目录" allowClear />
- </Form.Item>
- <Form.Item
- name="command"
- label={
- <PropsLabel
- menuItems={menuItems}
- title="启动命令"
- onClick={(value) => {
- handleParameterClick('command', value);
- }}
- />
- }
- >
- <TextArea placeholder="请输入启动命令" allowClear />
- </Form.Item>
- <Form.Item
- label="资源规格"
- name="resources_standard"
- rules={[
- {
- required: true,
- message: '请选择资源规格',
- },
- ]}
- >
- <Select
- placeholder="请选择资源规格"
- filterOption={filterResourceStandard}
- options={resourceStandardList}
- fieldNames={{
- label: 'description',
- value: 'standard',
- }}
- showSearch
- />
- </Form.Item>
- <Form.Item
- name="mount_path"
- label={
- <PropsLabel
- menuItems={menuItems}
- title="挂载路径"
- onClick={(value) => {
- handleParameterClick('mount_path', value);
- }}
- />
- }
- >
- <Input placeholder="请输入挂载路径" allowClear />
- </Form.Item>
- <Form.Item
- name="env_variables"
- label={
- <PropsLabel
- menuItems={menuItems}
- title="环境变量"
- onClick={(value) => {
- handleParameterClick('env_variables', value);
- }}
- />
- }
- >
- <TextArea placeholder="请输入环境变量" allowClear />
- </Form.Item>
- {/* 控制参数 */}
- {controlStrategyList.map((item) => (
- <Form.Item
- key={item.key}
- name={['control_strategy', item.key]}
- required={item.value.require ? true : false}
- label={getLabel(item, 'control_strategy')}
- rules={getFormRules(item)}
- >
- <ParameterInput allowClear></ParameterInput>
- </Form.Item>
- ))}
- <div className={styles['pipeline-drawer__title']}>
- <SubAreaTitle
- image={require('@/assets/img/duty-message.png')}
- title="输入参数"
- ></SubAreaTitle>
- </div>
- {inParametersList.map((item) => (
- <Form.Item
- key={item.key}
- label={getLabel(item, 'in_parameters')}
- required={item.value.require ? true : false}
- >
- <div className={styles['pipeline-drawer__ref-row']}>
- <Form.Item name={['in_parameters', item.key]} rules={getFormRules(item)} noStyle>
- {item.value.type === 'select' ? (
- <ParameterSelect />
- ) : (
- <ParameterInput canInput={canInput(item.value)} allowClear></ParameterInput>
- )}
- </Form.Item>
- {item.value.type === 'ref' && (
- <Form.Item noStyle>
- <Button
- size="small"
- type="link"
- icon={getSelectBtnIcon(item.value)}
- onClick={() => selectRefData(['in_parameters', item.key], item.value)}
- className={styles['pipeline-drawer__ref-row__select-button']}
- >
- {item.value.label}
- </Button>
- </Form.Item>
- )}
- </div>
- </Form.Item>
- ))}
- <div className={styles['pipeline-drawer__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]}
- required={item.value.require ? true : false}
- label={getLabel(item, 'out_parameters')}
- rules={getFormRules(item)}
- >
- <ParameterInput allowClear></ParameterInput>
- </Form.Item>
- ))}
- </Form>
- </Drawer>
- );
- });
-
- export default PipelineNodeParameter;
|