mirror of
https://github.com/csunny/DB-GPT.git
synced 2025-07-30 15:21:02 +00:00
feat: Update AddFlowVariable component to include parameter management
This commit is contained in:
parent
03e3f93a95
commit
1bc77f9148
@ -1,78 +1,115 @@
|
||||
import { apiInterceptors, getFlowNodes } from '@/client/api';
|
||||
import { IFlowNode } from '@/types/flow';
|
||||
import { FLOW_NODES_KEY } from '@/utils';
|
||||
// import { IFlowNode } from '@/types/flow';
|
||||
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import { Button, Form, Input, Modal } from 'antd';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Button, Form, Input, Modal, Select, Space } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
type GroupType = { category: string; categoryLabel: string; nodes: IFlowNode[] };
|
||||
// ype GroupType = { category: string; categoryLabel: string; nodes: IFlowNode[] };
|
||||
type ValueType = 'str' | 'int' | 'float' | 'bool' | 'ref';
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
const DAG_PARAM_KEY = 'dbgpt.core.flow.params';
|
||||
const DAG_PARAM_SCOPE = 'flow_priv';
|
||||
|
||||
const AddFlowVariable: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const [operators, setOperators] = useState<Array<IFlowNode>>([]);
|
||||
const [resources, setResources] = useState<Array<IFlowNode>>([]);
|
||||
const [operatorsGroup, setOperatorsGroup] = useState<GroupType[]>([]);
|
||||
const [resourcesGroup, setResourcesGroup] = useState<GroupType[]>([]);
|
||||
// const [operators, setOperators] = useState<Array<IFlowNode>>([]);
|
||||
// const [resources, setResources] = useState<Array<IFlowNode>>([]);
|
||||
// const [operatorsGroup, setOperatorsGroup] = useState<GroupType[]>([]);
|
||||
// const [resourcesGroup, setResourcesGroup] = useState<GroupType[]>([]);
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [form] = Form.useForm(); // const [form] = Form.useForm<IFlowUpdateParam>();
|
||||
|
||||
const showModal = () => {
|
||||
setIsModalOpen(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getNodes();
|
||||
}, []);
|
||||
// TODO: get keys
|
||||
// useEffect(() => {
|
||||
// getNodes();
|
||||
// }, []);
|
||||
|
||||
async function getNodes() {
|
||||
const [_, data] = await apiInterceptors(getFlowNodes());
|
||||
if (data && data.length > 0) {
|
||||
localStorage.setItem(FLOW_NODES_KEY, JSON.stringify(data));
|
||||
const operatorNodes = data.filter(node => node.flow_type === 'operator');
|
||||
const resourceNodes = data.filter(node => node.flow_type === 'resource');
|
||||
setOperators(operatorNodes);
|
||||
setResources(resourceNodes);
|
||||
setOperatorsGroup(groupNodes(operatorNodes));
|
||||
setResourcesGroup(groupNodes(resourceNodes));
|
||||
}
|
||||
}
|
||||
// async function getNodes() {
|
||||
// const [_, data] = await apiInterceptors(getFlowNodes());
|
||||
// if (data && data.length > 0) {
|
||||
// localStorage.setItem(FLOW_NODES_KEY, JSON.stringify(data));
|
||||
// const operatorNodes = data.filter(node => node.flow_type === 'operator');
|
||||
// const resourceNodes = data.filter(node => node.flow_type === 'resource');
|
||||
// setOperators(operatorNodes);
|
||||
// setResources(resourceNodes);
|
||||
// setOperatorsGroup(groupNodes(operatorNodes));
|
||||
// setResourcesGroup(groupNodes(resourceNodes));
|
||||
// }
|
||||
// }
|
||||
|
||||
function groupNodes(data: IFlowNode[]) {
|
||||
const groups: GroupType[] = [];
|
||||
const categoryMap: Record<string, { category: string; categoryLabel: string; nodes: IFlowNode[] }> = {};
|
||||
data.forEach(item => {
|
||||
const { category, category_label } = item;
|
||||
if (!categoryMap[category]) {
|
||||
categoryMap[category] = { category, categoryLabel: category_label, nodes: [] };
|
||||
groups.push(categoryMap[category]);
|
||||
}
|
||||
categoryMap[category].nodes.push(item);
|
||||
});
|
||||
return groups;
|
||||
}
|
||||
|
||||
const formItemLayout = {
|
||||
labelCol: {
|
||||
xs: { span: 24 },
|
||||
sm: { span: 4 },
|
||||
},
|
||||
wrapperCol: {
|
||||
xs: { span: 24 },
|
||||
sm: { span: 20 },
|
||||
},
|
||||
};
|
||||
|
||||
const formItemLayoutWithOutLabel = {
|
||||
wrapperCol: {
|
||||
xs: { span: 24, offset: 0 },
|
||||
sm: { span: 20, offset: 2 },
|
||||
},
|
||||
};
|
||||
// function groupNodes(data: IFlowNode[]) {
|
||||
// const groups: GroupType[] = [];
|
||||
// const categoryMap: Record<string, { category: string; categoryLabel: string; nodes: IFlowNode[] }> = {};
|
||||
// data.forEach(item => {
|
||||
// const { category, category_label } = item;
|
||||
// if (!categoryMap[category]) {
|
||||
// categoryMap[category] = { category, categoryLabel: category_label, nodes: [] };
|
||||
// groups.push(categoryMap[category]);
|
||||
// }
|
||||
// categoryMap[category].nodes.push(item);
|
||||
// });
|
||||
// return groups;
|
||||
// }
|
||||
|
||||
const onFinish = (values: any) => {
|
||||
console.log('Received values of form:', values);
|
||||
};
|
||||
|
||||
function onNameChange(e: React.ChangeEvent<HTMLInputElement>, index: number) {
|
||||
const name = e.target.value;
|
||||
|
||||
const result = name
|
||||
?.split('_')
|
||||
?.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
?.join(' ');
|
||||
|
||||
form.setFields([
|
||||
{
|
||||
name: ['parameters', index, 'label'],
|
||||
value: result,
|
||||
},
|
||||
]);
|
||||
|
||||
// change value to ref
|
||||
const type = form.getFieldValue(['parameters', index, 'value_type']);
|
||||
|
||||
if (type === 'ref') {
|
||||
const parameters = form.getFieldValue('parameters');
|
||||
const param = parameters?.[index];
|
||||
|
||||
if (param) {
|
||||
const { name = '' } = param;
|
||||
param.value = `${DAG_PARAM_KEY}:${name}@scope:${DAG_PARAM_SCOPE}`;
|
||||
|
||||
form.setFieldsValue({
|
||||
parameters: [...parameters],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onValueTypeChange(type: ValueType, index: number) {
|
||||
if (type === 'ref') {
|
||||
const parameters = form.getFieldValue('parameters');
|
||||
const param = parameters?.[index];
|
||||
|
||||
if (param) {
|
||||
const { name = '' } = param;
|
||||
param.value = `${DAG_PARAM_KEY}:${name}@scope:${DAG_PARAM_SCOPE}`;
|
||||
|
||||
form.setFieldsValue({
|
||||
parameters: [...parameters],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
@ -83,64 +120,111 @@ const AddFlowVariable: React.FC = () => {
|
||||
onClick={showModal}
|
||||
/>
|
||||
|
||||
<Modal title={t('Add_Global_Variable_of_Flow')} open={isModalOpen} footer={null}>
|
||||
<Form name='dynamic_form_item' {...formItemLayoutWithOutLabel} onFinish={onFinish} className='mt-8'>
|
||||
<Form.List
|
||||
name='names'
|
||||
rules={[
|
||||
{
|
||||
validator: async (_, names) => {
|
||||
if (!names || names.length < 2) {
|
||||
return Promise.reject(new Error('At least 2 passengers'));
|
||||
}
|
||||
},
|
||||
},
|
||||
]}
|
||||
>
|
||||
{(fields, { add, remove }, { errors }) => (
|
||||
<Modal
|
||||
title={t('Add_Global_Variable_of_Flow')}
|
||||
open={isModalOpen}
|
||||
footer={null}
|
||||
width={1000}
|
||||
styles={{
|
||||
body: {
|
||||
maxHeight: '70vh',
|
||||
overflow: 'scroll',
|
||||
backgroundColor: 'rgba(0,0,0,0.02)',
|
||||
padding: '0 8px',
|
||||
borderRadius: 4,
|
||||
},
|
||||
}}
|
||||
onClose={() => setIsModalOpen(false)}
|
||||
>
|
||||
<Form
|
||||
name='dynamic_form_nest_item'
|
||||
onFinish={onFinish}
|
||||
form={form}
|
||||
autoComplete='off'
|
||||
layout='vertical'
|
||||
className='mt-8'
|
||||
initialValues={{ parameters: [{}] }}
|
||||
>
|
||||
<Form.List name='parameters'>
|
||||
{(fields, { add, remove }) => (
|
||||
<>
|
||||
{fields.map((field, index) => (
|
||||
<Form.Item
|
||||
{...(index === 0 ? formItemLayout : formItemLayoutWithOutLabel)}
|
||||
label={index === 0 ? 'Passengers' : ''}
|
||||
required={false}
|
||||
key={field.key}
|
||||
>
|
||||
{fields.map(({ key, name, ...restField }, index) => (
|
||||
<Space key={key} style={{ display: 'flex', marginBottom: 8 }} align='baseline'>
|
||||
<Form.Item
|
||||
{...field}
|
||||
validateTrigger={['onChange', 'onBlur']}
|
||||
{...restField}
|
||||
name={[name, 'name']}
|
||||
label={`参数 ${index + 1} 名称`}
|
||||
style={{ width: 140 }}
|
||||
rules={[
|
||||
{ required: true, message: 'Missing parameter name' },
|
||||
{
|
||||
required: true,
|
||||
whitespace: true,
|
||||
message: "Please input passenger's name or delete this field.",
|
||||
pattern: /^[a-zA-Z0-9]+(_[a-zA-Z0-9]+)*$/,
|
||||
message: '名称必须是字母、数字或下划线,并使用下划线分隔多个单词',
|
||||
},
|
||||
]}
|
||||
noStyle
|
||||
>
|
||||
<Input placeholder='passenger name' style={{ width: '60%' }} />
|
||||
<Input placeholder='Parameter Name' onChange={e => onNameChange(e, index)} />
|
||||
</Form.Item>
|
||||
{fields.length > 1 ? (
|
||||
<MinusCircleOutlined className='dynamic-delete-button' onClick={() => remove(field.name)} />
|
||||
) : null}
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
{...restField}
|
||||
name={[name, 'label']}
|
||||
label='标题'
|
||||
style={{ width: 130 }}
|
||||
rules={[{ required: true, message: 'Missing parameter label' }]}
|
||||
>
|
||||
<Input placeholder='Parameter Label' />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
{...restField}
|
||||
name={[name, 'value_type']}
|
||||
label='类型'
|
||||
style={{ width: 100 }}
|
||||
rules={[{ required: true, message: 'Missing parameter type' }]}
|
||||
>
|
||||
<Select placeholder='Select' onChange={value => onValueTypeChange(value, index)}>
|
||||
{['str', 'int', 'float', 'bool', 'ref'].map(type => (
|
||||
<Option key={type} value={type}>
|
||||
{type}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
{...restField}
|
||||
name={[name, 'value']}
|
||||
label='值'
|
||||
style={{ width: 320 }}
|
||||
rules={[{ required: true, message: 'Missing parameter value' }]}
|
||||
>
|
||||
<Input placeholder='Parameter Value' />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item {...restField} name={[name, 'description']} label='描述' style={{ width: 170 }}>
|
||||
<Input placeholder='Parameter Description' />
|
||||
</Form.Item>
|
||||
|
||||
<MinusCircleOutlined onClick={() => remove(name)} className='relative mt-6' />
|
||||
</Space>
|
||||
))}
|
||||
|
||||
<Form.Item>
|
||||
<Button type='dashed' onClick={() => add()} className='w-full' icon={<PlusOutlined />}>
|
||||
Add field
|
||||
<Button type='dashed' onClick={() => add()} block icon={<PlusOutlined />}>
|
||||
{t('Add_Parameter')}
|
||||
</Button>
|
||||
|
||||
<Form.ErrorList errors={errors} />
|
||||
</Form.Item>
|
||||
</>
|
||||
)}
|
||||
</Form.List>
|
||||
|
||||
<Form.Item wrapperCol={{ offset: 18, span: 8 }}>
|
||||
<Button type='primary' htmlType='submit'>
|
||||
Submit
|
||||
</Button>
|
||||
<Form.Item wrapperCol={{ offset: 20, span: 4 }}>
|
||||
<Space>
|
||||
<Button onClick={() => setIsModalOpen(false)}>{t('cancel')}</Button>
|
||||
<Button type='primary' htmlType='submit'>
|
||||
{t('verify')}
|
||||
</Button>
|
||||
</Space>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
|
@ -43,7 +43,6 @@ export const ExportFlowModal: React.FC<Props> = ({
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
centered
|
||||
title={t('Export_Flow')}
|
||||
open={isExportFlowModalOpen}
|
||||
onCancel={() => setIsExportFlowModalOpen(false)}
|
||||
|
@ -61,7 +61,6 @@ export const ImportFlowModal: React.FC<Props> = ({ isImportModalOpen, setIsImpor
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
centered
|
||||
title={t('Import_Flow')}
|
||||
open={isImportModalOpen}
|
||||
onCancel={() => setIsImportFlowModalOpen(false)}
|
||||
|
@ -89,7 +89,6 @@ export const SaveFlowModal: React.FC<Props> = ({
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
centered
|
||||
title={t('flow_modal_title')}
|
||||
open={isSaveFlowModalOpen}
|
||||
onCancel={() => {
|
||||
@ -142,7 +141,7 @@ export const SaveFlowModal: React.FC<Props> = ({
|
||||
<TextArea rows={3} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label='Editable' name='editable' initialValue={flowInfo?.editable} valuePropName='checked'>
|
||||
<Form.Item label='Editable' name='editable' initialValue={flowInfo?.editable || true} valuePropName='checked'>
|
||||
<Checkbox />
|
||||
</Form.Item>
|
||||
|
||||
|
@ -18,4 +18,5 @@ export const FlowEn = {
|
||||
No: 'No',
|
||||
Please_Add_Nodes_First: 'Please add nodes first',
|
||||
Add_Global_Variable_of_Flow: 'Add global variable of flow',
|
||||
Add_Parameter: 'Add Parameter',
|
||||
};
|
||||
|
@ -18,4 +18,5 @@ export const FlowZn = {
|
||||
No: '否',
|
||||
Please_Add_Nodes_First: '请先添加节点',
|
||||
Add_Global_Variable_of_Flow: '添加 Flow 全局变量',
|
||||
Add_Parameter: '添加参数',
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user