feat: add form input node (#2773)
* add node * dispatch * extract InputTypeConfig component * question tip * fix build * fix * fix
This commit is contained in:
@@ -293,7 +293,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
})();
|
||||
|
||||
const isInteractiveRequest = !!getLastInteractiveValue(histories);
|
||||
const { text: userSelectedVal } = chatValue2RuntimePrompt(userQuestion.value);
|
||||
const { text: userInteractiveVal } = chatValue2RuntimePrompt(userQuestion.value);
|
||||
|
||||
const newTitle = isPlugin
|
||||
? variables.cTime ?? getSystemTime(user.timezone)
|
||||
@@ -312,7 +312,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
appId: app._id,
|
||||
teamId,
|
||||
tmbId: tmbId,
|
||||
userSelectedVal,
|
||||
userInteractiveVal,
|
||||
aiResponse,
|
||||
newVariables,
|
||||
newTitle
|
||||
|
||||
@@ -55,7 +55,8 @@ const nodeTypes: Record<FlowNodeTypeEnum, any> = {
|
||||
[FlowNodeTypeEnum.userSelect]: dynamic(() => import('./nodes/NodeUserSelect')),
|
||||
[FlowNodeTypeEnum.loop]: dynamic(() => import('./nodes/Loop/NodeLoop')),
|
||||
[FlowNodeTypeEnum.loopStart]: dynamic(() => import('./nodes/Loop/NodeLoopStart')),
|
||||
[FlowNodeTypeEnum.loopEnd]: dynamic(() => import('./nodes/Loop/NodeLoopEnd'))
|
||||
[FlowNodeTypeEnum.loopEnd]: dynamic(() => import('./nodes/Loop/NodeLoopEnd')),
|
||||
[FlowNodeTypeEnum.formInput]: dynamic(() => import('./nodes/NodeFormInput'))
|
||||
};
|
||||
const edgeTypes = {
|
||||
[EDGE_TYPE]: ButtonEdge
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
import { Box, Flex, FormLabel, Stack } from '@chakra-ui/react';
|
||||
import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import React, { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { UserInputFormItemType } from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import InputTypeConfig from '../NodePluginIO/InputTypeConfig';
|
||||
|
||||
export const defaultFormInput: UserInputFormItemType = {
|
||||
type: FlowNodeInputTypeEnum.input,
|
||||
key: '',
|
||||
label: '',
|
||||
value: '',
|
||||
valueType: WorkflowIOValueTypeEnum.string,
|
||||
required: false
|
||||
};
|
||||
|
||||
// Modal for add or edit user input form items
|
||||
const InputFormEditModal = ({
|
||||
defaultValue,
|
||||
onClose,
|
||||
onSubmit,
|
||||
keys
|
||||
}: {
|
||||
defaultValue: UserInputFormItemType;
|
||||
onClose: () => void;
|
||||
onSubmit: (data: UserInputFormItemType) => void;
|
||||
keys: string[];
|
||||
}) => {
|
||||
const isEdit = !!defaultValue.key;
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
|
||||
const form = useForm({
|
||||
defaultValues: {
|
||||
...defaultValue,
|
||||
list: defaultValue.list?.length ? defaultValue.list : [{ label: '', value: '' }]
|
||||
}
|
||||
});
|
||||
const { setValue, watch, reset } = form;
|
||||
|
||||
const inputType = watch('type') || FlowNodeInputTypeEnum.input;
|
||||
|
||||
const maxLength = watch('maxLength');
|
||||
const max = watch('max');
|
||||
const min = watch('min');
|
||||
|
||||
const inputTypeList = [
|
||||
{
|
||||
icon: 'core/workflow/inputType/input',
|
||||
label: t('common:core.workflow.inputType.input'),
|
||||
value: FlowNodeInputTypeEnum.input,
|
||||
defaultValueType: WorkflowIOValueTypeEnum.string
|
||||
},
|
||||
{
|
||||
icon: 'core/workflow/inputType/textarea',
|
||||
label: t('common:core.workflow.inputType.textarea'),
|
||||
value: FlowNodeInputTypeEnum.textarea,
|
||||
defaultValueType: WorkflowIOValueTypeEnum.string
|
||||
},
|
||||
{
|
||||
icon: 'core/workflow/inputType/numberInput',
|
||||
label: t('common:core.workflow.inputType.number input'),
|
||||
value: FlowNodeInputTypeEnum.numberInput,
|
||||
defaultValueType: WorkflowIOValueTypeEnum.number
|
||||
},
|
||||
{
|
||||
icon: 'core/workflow/inputType/option',
|
||||
label: t('common:core.workflow.inputType.select'),
|
||||
value: FlowNodeInputTypeEnum.select,
|
||||
defaultValueType: WorkflowIOValueTypeEnum.string
|
||||
}
|
||||
];
|
||||
|
||||
const onSubmitSuccess = useCallback(
|
||||
(data: UserInputFormItemType, action: 'confirm' | 'continue') => {
|
||||
const isChangeKey = defaultValue.key !== data.key;
|
||||
if (keys.includes(data.key)) {
|
||||
if (!isEdit || isChangeKey) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: t('workflow:field_name_already_exists')
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
data.key = data.label;
|
||||
|
||||
if (action === 'confirm') {
|
||||
onSubmit(data);
|
||||
onClose();
|
||||
} else if (action === 'continue') {
|
||||
onSubmit(data);
|
||||
toast({
|
||||
status: 'success',
|
||||
title: t('common:common.Add Success')
|
||||
});
|
||||
reset(defaultFormInput);
|
||||
}
|
||||
},
|
||||
[toast, t, reset, onSubmit, onClose, defaultFormInput]
|
||||
);
|
||||
|
||||
const onSubmitError = useCallback(
|
||||
(e: Object) => {
|
||||
for (const item of Object.values(e)) {
|
||||
if (item.message) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: item.message
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
[toast]
|
||||
);
|
||||
|
||||
return (
|
||||
<MyModal
|
||||
isOpen={true}
|
||||
onClose={onClose}
|
||||
iconSrc="file/fill/manual"
|
||||
title={isEdit ? t('workflow:edit_input') : t('workflow:add_new_input')}
|
||||
maxW={['90vw', '878px']}
|
||||
w={'100%'}
|
||||
isCentered
|
||||
>
|
||||
<Flex h={'494px'}>
|
||||
<Stack gap={4} p={8}>
|
||||
<FormLabel color={'myGray.600'} fontWeight={'medium'}>
|
||||
{t('common:core.module.Input Type')}
|
||||
</FormLabel>
|
||||
<Flex flexDirection={'column'} gap={4}>
|
||||
<Box display={'grid'} gridTemplateColumns={'repeat(2, 1fr)'} gap={4}>
|
||||
{inputTypeList.map((item) => {
|
||||
const isSelected = inputType === item.value;
|
||||
return (
|
||||
<Box
|
||||
display={'flex'}
|
||||
key={item.label}
|
||||
border={isSelected ? '1px solid #3370FF' : '1px solid #DFE2EA'}
|
||||
p={3}
|
||||
rounded={'6px'}
|
||||
fontWeight={'medium'}
|
||||
fontSize={'14px'}
|
||||
alignItems={'center'}
|
||||
cursor={'pointer'}
|
||||
boxShadow={isSelected ? '0px 0px 0px 2.4px rgba(51, 112, 255, 0.15)' : 'none'}
|
||||
_hover={{
|
||||
'& > svg': {
|
||||
color: 'primary.600'
|
||||
},
|
||||
'& > span': {
|
||||
color: 'myGray.900'
|
||||
},
|
||||
border: '1px solid #3370FF',
|
||||
boxShadow: '0px 0px 0px 2.4px rgba(51, 112, 255, 0.15)'
|
||||
}}
|
||||
onClick={() => {
|
||||
setValue('type', item.value);
|
||||
}}
|
||||
>
|
||||
<MyIcon
|
||||
name={item.icon as any}
|
||||
w={'20px'}
|
||||
mr={1.5}
|
||||
color={isSelected ? 'primary.600' : 'myGray.400'}
|
||||
/>
|
||||
<Box as="span" color={isSelected ? 'myGray.900' : 'inherit'} pr={4}>
|
||||
{item.label}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
</Flex>
|
||||
</Stack>
|
||||
<InputTypeConfig
|
||||
form={form}
|
||||
type={'formInput'}
|
||||
isEdit={isEdit}
|
||||
inputType={inputType}
|
||||
maxLength={maxLength}
|
||||
max={max}
|
||||
min={min}
|
||||
onClose={onClose}
|
||||
onSubmitSuccess={onSubmitSuccess}
|
||||
onSubmitError={onSubmitError}
|
||||
/>
|
||||
</Flex>
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(InputFormEditModal);
|
||||
@@ -0,0 +1,224 @@
|
||||
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { NodeProps } from 'reactflow';
|
||||
import NodeCard from '../render/NodeCard';
|
||||
import Container from '../../components/Container';
|
||||
import RenderInput from '../render/RenderInput';
|
||||
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import {
|
||||
FlowNodeInputItemType,
|
||||
FlowNodeOutputItemType
|
||||
} from '@fastgpt/global/core/workflow/type/io';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Flex,
|
||||
FormLabel,
|
||||
HStack,
|
||||
Table,
|
||||
TableContainer,
|
||||
Tbody,
|
||||
Td,
|
||||
Th,
|
||||
Thead,
|
||||
Tr
|
||||
} from '@chakra-ui/react';
|
||||
import { UserInputFormItemType } from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
FlowNodeInputMap,
|
||||
FlowNodeInputTypeEnum,
|
||||
FlowNodeOutputTypeEnum
|
||||
} from '@fastgpt/global/core/workflow/node/constant';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import { SmallAddIcon } from '@chakra-ui/icons';
|
||||
import IOTitle from '../../components/IOTitle';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { WorkflowContext } from '../../../context';
|
||||
import InputFormEditModal, { defaultFormInput } from './InputFormEditModal';
|
||||
import RenderOutput from '../render/RenderOutput';
|
||||
|
||||
const NodeFormInput = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
const { nodeId, inputs, outputs } = data;
|
||||
const { t } = useTranslation();
|
||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||
|
||||
const [editField, setEditField] = useState<UserInputFormItemType>();
|
||||
|
||||
const CustomComponent = useMemo(
|
||||
() => ({
|
||||
[NodeInputKeyEnum.userInputForms]: ({ value, key, ...props }: FlowNodeInputItemType) => {
|
||||
const inputs = value as UserInputFormItemType[];
|
||||
|
||||
const onSubmit = (data: UserInputFormItemType) => {
|
||||
if (!editField?.key) {
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'updateInput',
|
||||
key,
|
||||
value: {
|
||||
...props,
|
||||
key,
|
||||
value: inputs.concat(data)
|
||||
}
|
||||
});
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'addOutput',
|
||||
value: {
|
||||
id: data.key,
|
||||
valueType: data.valueType,
|
||||
key: data.key,
|
||||
label: data.label,
|
||||
type: FlowNodeOutputTypeEnum.static
|
||||
}
|
||||
});
|
||||
} else {
|
||||
const output = outputs.find((output) => output.key === editField.key);
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'updateInput',
|
||||
key,
|
||||
value: {
|
||||
...props,
|
||||
key,
|
||||
value: inputs.map((input) => (input.key === editField.key ? data : input))
|
||||
}
|
||||
});
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'replaceOutput',
|
||||
key: editField.key,
|
||||
value: {
|
||||
...(output as FlowNodeOutputItemType),
|
||||
valueType: data.valueType,
|
||||
key: data.key,
|
||||
label: data.label
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const onDelete = (valueKey: string) => {
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'updateInput',
|
||||
key,
|
||||
value: {
|
||||
...props,
|
||||
key,
|
||||
value: inputs.filter((input) => input.key !== valueKey)
|
||||
}
|
||||
});
|
||||
onChangeNode({
|
||||
nodeId,
|
||||
type: 'delOutput',
|
||||
key: valueKey
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<HStack className="nodrag" cursor={'default'} mb={3}>
|
||||
<FormLabel>{t('common:core.module.input_form')}</FormLabel>
|
||||
<Box flex={'1 0 0'} />
|
||||
<Button
|
||||
variant={'ghost'}
|
||||
leftIcon={<SmallAddIcon />}
|
||||
iconSpacing={1}
|
||||
size={'sm'}
|
||||
onClick={() => {
|
||||
setEditField(defaultFormInput);
|
||||
}}
|
||||
>
|
||||
{t('common:common.Add_new_input')}
|
||||
</Button>
|
||||
{!!editField && (
|
||||
<InputFormEditModal
|
||||
defaultValue={editField}
|
||||
keys={inputs.map((item) => item.key)}
|
||||
onClose={() => {
|
||||
setEditField(undefined);
|
||||
}}
|
||||
onSubmit={onSubmit}
|
||||
/>
|
||||
)}
|
||||
</HStack>
|
||||
|
||||
<TableContainer>
|
||||
<Table bg={'white'}>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th borderBottomLeftRadius={'none !important'}>
|
||||
{t('common:core.module.input_name')}
|
||||
</Th>
|
||||
<Th>{t('common:core.module.input_description')}</Th>
|
||||
<Th>{t('common:common.Require Input')}</Th>
|
||||
<Th borderBottomRightRadius={'none !important'}>{t('user:operations')}</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{inputs.map((item, index) => {
|
||||
const icon = FlowNodeInputMap[item.type as FlowNodeInputTypeEnum]?.icon;
|
||||
return (
|
||||
<Tr key={index}>
|
||||
<Td>
|
||||
<Flex alignItems={'center'}>
|
||||
{!!icon && (
|
||||
<MyIcon name={icon as any} w={'14px'} mr={1} color={'primary.600'} />
|
||||
)}
|
||||
{item.label}
|
||||
</Flex>
|
||||
</Td>
|
||||
<Td>{item.description}</Td>
|
||||
<Td>{item.required ? '✅' : ''}</Td>
|
||||
<Td>
|
||||
<MyIcon
|
||||
mr={3}
|
||||
name={'common/settingLight'}
|
||||
w={'16px'}
|
||||
cursor={'pointer'}
|
||||
_hover={{ color: 'primary.600' }}
|
||||
onClick={() => setEditField(item)}
|
||||
/>
|
||||
<MyIcon
|
||||
className="delete"
|
||||
name={'delete'}
|
||||
w={'16px'}
|
||||
color={'myGray.600'}
|
||||
cursor={'pointer'}
|
||||
ml={2}
|
||||
_hover={{ color: 'red.500' }}
|
||||
onClick={() => {
|
||||
onDelete(item.key);
|
||||
}}
|
||||
/>
|
||||
</Td>
|
||||
</Tr>
|
||||
);
|
||||
})}
|
||||
</Tbody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
}),
|
||||
[nodeId, editField, t, setEditField, onChangeNode]
|
||||
);
|
||||
|
||||
return (
|
||||
<NodeCard minW={'400px'} selected={selected} {...data}>
|
||||
<Container>
|
||||
<IOTitle text={t('common:common.Input')} />
|
||||
<RenderInput nodeId={nodeId} flowInputList={inputs} CustomComponent={CustomComponent} />
|
||||
</Container>
|
||||
<Container>
|
||||
<IOTitle text={t('common:common.Output')} />
|
||||
<RenderOutput nodeId={nodeId} flowOutputList={outputs} />
|
||||
</Container>
|
||||
</NodeCard>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(NodeFormInput);
|
||||
@@ -1,36 +1,18 @@
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Flex,
|
||||
Switch,
|
||||
Input,
|
||||
Textarea,
|
||||
Stack,
|
||||
HStack,
|
||||
FormControl
|
||||
} from '@chakra-ui/react';
|
||||
import { useFieldArray, useForm } from 'react-hook-form';
|
||||
import { Box, Flex, Stack } from '@chakra-ui/react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { FlowValueTypeMap } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { FlowNodeInputTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import MySelect from '@fastgpt/web/components/common/MySelect';
|
||||
import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
|
||||
import dynamic from 'next/dynamic';
|
||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||
import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io';
|
||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||
import MultipleSelect from '@fastgpt/web/components/common/MySelect/MultipleSelect';
|
||||
import { useBoolean } from 'ahooks';
|
||||
|
||||
const MyNumberInput = dynamic(
|
||||
() => import('@fastgpt/web/components/common/Input/NumberInput/index')
|
||||
);
|
||||
const JsonEditor = dynamic(() => import('@fastgpt/web/components/common/Textarea/JsonEditor'));
|
||||
import InputTypeConfig from './InputTypeConfig';
|
||||
|
||||
export const defaultInput: FlowNodeInputItemType = {
|
||||
renderTypeList: [FlowNodeInputTypeEnum.reference], // Can only choose one here
|
||||
@@ -54,7 +36,7 @@ const FieldEditModal = ({
|
||||
keys: string[];
|
||||
hasDynamicInput: boolean;
|
||||
onClose: () => void;
|
||||
onSubmit: (e: { data: FlowNodeInputItemType; isChangeKey: boolean }) => void;
|
||||
onSubmit: (data: FlowNodeInputItemType) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
@@ -150,20 +132,13 @@ const FieldEditModal = ({
|
||||
);
|
||||
|
||||
const isEdit = !!defaultValue.key;
|
||||
const { register, getValues, setValue, handleSubmit, watch, control, reset } = useForm({
|
||||
const form = useForm({
|
||||
defaultValues: {
|
||||
...defaultValue,
|
||||
list: defaultValue.list?.length ? defaultValue.list : [{ label: '', value: '' }]
|
||||
}
|
||||
});
|
||||
const {
|
||||
fields: selectEnums,
|
||||
append: appendEnums,
|
||||
remove: removeEnums
|
||||
} = useFieldArray({
|
||||
control,
|
||||
name: 'list'
|
||||
});
|
||||
const { getValues, setValue, watch, reset } = form;
|
||||
|
||||
const inputType = watch('renderTypeList.0') || FlowNodeInputTypeEnum.reference;
|
||||
const valueType = watch('valueType');
|
||||
@@ -174,41 +149,8 @@ const FieldEditModal = ({
|
||||
const max = watch('max');
|
||||
const min = watch('min');
|
||||
const selectValueTypeList = watch('customInputConfig.selectValueTypeList');
|
||||
const defaultJsonValue = watch('defaultValue');
|
||||
|
||||
const showValueTypeSelect =
|
||||
inputType === FlowNodeInputTypeEnum.reference ||
|
||||
inputType === FlowNodeInputTypeEnum.customVariable;
|
||||
|
||||
// input type config
|
||||
const showRequired = useMemo(() => {
|
||||
const list = [FlowNodeInputTypeEnum.addInputParam, FlowNodeInputTypeEnum.customVariable];
|
||||
return !list.includes(inputType);
|
||||
}, [inputType]);
|
||||
const showDefaultValue = useMemo(() => {
|
||||
const list = [
|
||||
FlowNodeInputTypeEnum.input,
|
||||
FlowNodeInputTypeEnum.textarea,
|
||||
FlowNodeInputTypeEnum.JSONEditor,
|
||||
FlowNodeInputTypeEnum.numberInput,
|
||||
FlowNodeInputTypeEnum.switch
|
||||
];
|
||||
|
||||
return list.includes(inputType);
|
||||
}, [inputType]);
|
||||
const showMaxLenInput = useMemo(() => {
|
||||
const list = [FlowNodeInputTypeEnum.input, FlowNodeInputTypeEnum.textarea];
|
||||
|
||||
return list.includes(inputType);
|
||||
}, [inputType]);
|
||||
const showMinMaxInput = useMemo(() => {
|
||||
const list = [FlowNodeInputTypeEnum.numberInput];
|
||||
return list.includes(inputType);
|
||||
}, [inputType]);
|
||||
|
||||
const valueTypeSelectList = Object.values(FlowValueTypeMap).map((item) => ({
|
||||
label: t(item.label as any),
|
||||
value: item.value
|
||||
}));
|
||||
const defaultValueType =
|
||||
inputTypeList.flat().find((item) => item.value === inputType)?.defaultValueType ||
|
||||
WorkflowIOValueTypeEnum.string;
|
||||
@@ -260,16 +202,10 @@ const FieldEditModal = ({
|
||||
data.label = data.key;
|
||||
|
||||
if (action === 'confirm') {
|
||||
onSubmit({
|
||||
data,
|
||||
isChangeKey
|
||||
});
|
||||
onSubmit(data);
|
||||
onClose();
|
||||
} else if (action === 'continue') {
|
||||
onSubmit({
|
||||
data,
|
||||
isChangeKey
|
||||
});
|
||||
onSubmit(data);
|
||||
toast({
|
||||
status: 'success',
|
||||
title: t('common:common.Add Success')
|
||||
@@ -381,271 +317,24 @@ const FieldEditModal = ({
|
||||
</Box>
|
||||
</Stack>
|
||||
{/* input type config */}
|
||||
<Stack flex={1} borderLeft={'1px solid #F0F1F6'} justifyContent={'space-between'}>
|
||||
<Flex flexDirection={'column'} p={8} gap={4} flex={'1 0 0'} overflow={'auto'}>
|
||||
<Flex alignItems={'center'}>
|
||||
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
|
||||
{t('common:core.module.Field Name')}
|
||||
</FormLabel>
|
||||
<Input
|
||||
bg={'myGray.50'}
|
||||
placeholder="appointment/sql"
|
||||
{...register('key', {
|
||||
required: true
|
||||
})}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex alignItems={'flex-start'}>
|
||||
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
|
||||
{t('workflow:field_description')}
|
||||
</FormLabel>
|
||||
<Textarea
|
||||
bg={'myGray.50'}
|
||||
placeholder={t('workflow:field_description_placeholder')}
|
||||
rows={4}
|
||||
{...register('description', { required: isToolInput ? true : false })}
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
{/* value type */}
|
||||
<Flex alignItems={'center'}>
|
||||
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
|
||||
{t('workflow:value_type')}
|
||||
</FormLabel>
|
||||
{showValueTypeSelect ? (
|
||||
<Box flex={1}>
|
||||
<MySelect<WorkflowIOValueTypeEnum>
|
||||
list={valueTypeSelectList.filter(
|
||||
(item) => item.value !== WorkflowIOValueTypeEnum.arrayAny
|
||||
)}
|
||||
value={valueType}
|
||||
onchange={(e) => {
|
||||
setValue('valueType', e);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
) : (
|
||||
<Box fontSize={'14px'}>{defaultValueType}</Box>
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
{showRequired && (
|
||||
<Flex alignItems={'center'} minH={'40px'}>
|
||||
<FormLabel flex={'1'} fontWeight={'medium'}>
|
||||
{t('workflow:field_required')}
|
||||
</FormLabel>
|
||||
<Switch {...register('required')} />
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
{/* reference */}
|
||||
{inputType === FlowNodeInputTypeEnum.reference && (
|
||||
<>
|
||||
<Flex alignItems={'center'} minH={'40px'}>
|
||||
<FormLabel flex={'1'} fontWeight={'medium'}>
|
||||
{t('workflow:field_used_as_tool_input')}
|
||||
</FormLabel>
|
||||
<Switch
|
||||
isChecked={isToolInput}
|
||||
onChange={(e) => {
|
||||
setIsToolInput();
|
||||
console.log(isToolInput);
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
</>
|
||||
)}
|
||||
|
||||
{showMaxLenInput && (
|
||||
<Flex alignItems={'center'}>
|
||||
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
|
||||
{t('common:core.module.Max Length')}
|
||||
</FormLabel>
|
||||
<MyNumberInput
|
||||
flex={'1 0 0'}
|
||||
bg={'myGray.50'}
|
||||
placeholder={t('common:core.module.Max Length placeholder')}
|
||||
value={maxLength}
|
||||
onChange={(e) => {
|
||||
// @ts-ignore
|
||||
setValue('maxLength', e || '');
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
{showMinMaxInput && (
|
||||
<>
|
||||
<Flex alignItems={'center'}>
|
||||
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
|
||||
{t('common:core.module.Max Value')}
|
||||
</FormLabel>
|
||||
<MyNumberInput
|
||||
flex={'1 0 0'}
|
||||
bg={'myGray.50'}
|
||||
value={watch('max')}
|
||||
onChange={(e) => {
|
||||
// @ts-ignore
|
||||
setValue('max', e || '');
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'}>
|
||||
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
|
||||
{t('common:core.module.Min Value')}
|
||||
</FormLabel>
|
||||
<MyNumberInput
|
||||
flex={'1 0 0'}
|
||||
bg={'myGray.50'}
|
||||
value={watch('min')}
|
||||
onChange={(e) => {
|
||||
// @ts-ignore
|
||||
setValue('min', e || '');
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
</>
|
||||
)}
|
||||
|
||||
{showDefaultValue && (
|
||||
<Flex alignItems={'center'} minH={'40px'}>
|
||||
<FormLabel
|
||||
flex={inputType === FlowNodeInputTypeEnum.switch ? 1 : '0 0 100px'}
|
||||
fontWeight={'medium'}
|
||||
>
|
||||
{t('common:core.module.Default Value')}
|
||||
</FormLabel>
|
||||
{inputType === FlowNodeInputTypeEnum.numberInput && (
|
||||
<Input
|
||||
bg={'myGray.50'}
|
||||
max={max}
|
||||
min={min}
|
||||
type={'number'}
|
||||
{...register('defaultValue')}
|
||||
/>
|
||||
)}
|
||||
{inputType === FlowNodeInputTypeEnum.input && (
|
||||
<Input bg={'myGray.50'} maxLength={maxLength} {...register('defaultValue')} />
|
||||
)}
|
||||
{inputType === FlowNodeInputTypeEnum.textarea && (
|
||||
<Textarea bg={'myGray.50'} maxLength={maxLength} {...register('defaultValue')} />
|
||||
)}
|
||||
{inputType === FlowNodeInputTypeEnum.JSONEditor && (
|
||||
<JsonEditor
|
||||
bg={'myGray.50'}
|
||||
resize
|
||||
w={'full'}
|
||||
onChange={(e) => {
|
||||
setValue('defaultValue', e);
|
||||
}}
|
||||
defaultValue={String(getValues('defaultValue'))}
|
||||
/>
|
||||
)}
|
||||
{inputType === FlowNodeInputTypeEnum.switch && (
|
||||
<Switch {...register('defaultValue')} />
|
||||
)}
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
{inputType === FlowNodeInputTypeEnum.addInputParam && (
|
||||
<>
|
||||
<Flex alignItems={'center'}>
|
||||
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
|
||||
{t('common:core.module.Input Type')}
|
||||
</FormLabel>
|
||||
<Box fontSize={'14px'}>{t('workflow:only_the_reference_type_is_supported')}</Box>
|
||||
</Flex>
|
||||
<Box>
|
||||
<HStack mb={1}>
|
||||
<FormLabel fontWeight={'medium'}>{t('workflow:optional_value_type')}</FormLabel>
|
||||
<QuestionTip label={t('workflow:optional_value_type_tip')} />
|
||||
</HStack>
|
||||
<MultipleSelect<WorkflowIOValueTypeEnum>
|
||||
list={valueTypeSelectList}
|
||||
bg={'myGray.50'}
|
||||
value={selectValueTypeList || []}
|
||||
onSelect={(e) => {
|
||||
setValue('customInputConfig.selectValueTypeList', e);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
|
||||
{inputType === FlowNodeInputTypeEnum.select && (
|
||||
<>
|
||||
<Flex flexDirection={'column'} gap={4}>
|
||||
{selectEnums.map((item, i) => (
|
||||
<Flex key={item.id} alignItems={'center'}>
|
||||
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
|
||||
{`${t('common:core.module.variable.variable options')} ${i + 1}`}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
fontSize={'12px'}
|
||||
bg={'myGray.50'}
|
||||
placeholder={`${t('common:core.module.variable.variable options')} ${i + 1}`}
|
||||
{...register(`list.${i}.label`, {
|
||||
required: true,
|
||||
onChange: (e) => {
|
||||
setValue(`list.${i}.value`, e.target.value);
|
||||
}
|
||||
})}
|
||||
/>
|
||||
</FormControl>
|
||||
{selectEnums.length > 1 && (
|
||||
<MyIcon
|
||||
ml={3}
|
||||
name={'delete'}
|
||||
w={'16px'}
|
||||
cursor={'pointer'}
|
||||
p={2}
|
||||
borderRadius={'md'}
|
||||
_hover={{ bg: 'red.100' }}
|
||||
onClick={() => removeEnums(i)}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
))}
|
||||
</Flex>
|
||||
<Button
|
||||
variant={'whiteBase'}
|
||||
leftIcon={<MyIcon name={'common/addLight'} w={'16px'} />}
|
||||
onClick={() => appendEnums({ label: '', value: '' })}
|
||||
fontWeight={'medium'}
|
||||
fontSize={'12px'}
|
||||
w={'24'}
|
||||
py={2}
|
||||
>
|
||||
{t('common:core.module.variable add option')}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
<Flex justify={'flex-end'} gap={3} pb={8} pr={8}>
|
||||
<Button variant={'whiteBase'} fontWeight={'medium'} onClick={onClose} w={20}>
|
||||
{t('common:common.Close')}
|
||||
</Button>
|
||||
<Button
|
||||
variant={'primaryOutline'}
|
||||
fontWeight={'medium'}
|
||||
onClick={handleSubmit((data) => onSubmitSuccess(data, 'confirm'), onSubmitError)}
|
||||
w={20}
|
||||
>
|
||||
{t('common:common.Confirm')}
|
||||
</Button>
|
||||
{!isEdit && (
|
||||
<Button
|
||||
fontWeight={'medium'}
|
||||
onClick={handleSubmit((data) => onSubmitSuccess(data, 'continue'), onSubmitError)}
|
||||
w={20}
|
||||
>
|
||||
{t('common:comon.Continue_Adding')}
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
</Stack>
|
||||
<InputTypeConfig
|
||||
form={form}
|
||||
type={'plugin'}
|
||||
isEdit={isEdit}
|
||||
onClose={onClose}
|
||||
inputType={inputType}
|
||||
maxLength={maxLength}
|
||||
max={max}
|
||||
min={min}
|
||||
selectValueTypeList={selectValueTypeList}
|
||||
defaultJsonValue={defaultJsonValue}
|
||||
isToolInput={isToolInput}
|
||||
setIsToolInput={setIsToolInput}
|
||||
valueType={valueType}
|
||||
defaultValueType={defaultValueType}
|
||||
onSubmitSuccess={onSubmitSuccess}
|
||||
onSubmitError={onSubmitError}
|
||||
/>
|
||||
</Flex>
|
||||
</MyModal>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,398 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Flex,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
HStack,
|
||||
Input,
|
||||
Stack,
|
||||
Switch,
|
||||
Textarea
|
||||
} from '@chakra-ui/react';
|
||||
import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||
import {
|
||||
FlowNodeInputTypeEnum,
|
||||
FlowValueTypeMap
|
||||
} from '@fastgpt/global/core/workflow/node/constant';
|
||||
import { FlowNodeInputItemType } from '@fastgpt/global/core/workflow/type/io';
|
||||
import MyNumberInput from '@fastgpt/web/components/common/Input/NumberInput';
|
||||
import MySelect from '@fastgpt/web/components/common/MySelect';
|
||||
import MultipleSelect from '@fastgpt/web/components/common/MySelect/MultipleSelect';
|
||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||
import JsonEditor from '@fastgpt/web/components/common/Textarea/JsonEditor';
|
||||
import React, { useMemo } from 'react';
|
||||
import { useFieldArray, UseFormReturn } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
|
||||
const InputTypeConfig = ({
|
||||
form,
|
||||
isEdit,
|
||||
onClose,
|
||||
type,
|
||||
inputType,
|
||||
maxLength,
|
||||
max,
|
||||
min,
|
||||
selectValueTypeList,
|
||||
defaultJsonValue,
|
||||
isToolInput,
|
||||
setIsToolInput,
|
||||
valueType,
|
||||
defaultValueType,
|
||||
onSubmitSuccess,
|
||||
onSubmitError
|
||||
}: {
|
||||
// Common fields
|
||||
form: UseFormReturn<any>;
|
||||
isEdit: boolean;
|
||||
onClose: () => void;
|
||||
type: 'plugin' | 'formInput';
|
||||
inputType: FlowNodeInputTypeEnum;
|
||||
|
||||
maxLength?: number;
|
||||
max?: number;
|
||||
min?: number;
|
||||
|
||||
selectValueTypeList?: WorkflowIOValueTypeEnum[];
|
||||
defaultJsonValue?: string;
|
||||
|
||||
// Plugin-specific fields
|
||||
isToolInput?: boolean;
|
||||
setIsToolInput?: () => void;
|
||||
valueType?: WorkflowIOValueTypeEnum;
|
||||
defaultValueType?: WorkflowIOValueTypeEnum;
|
||||
|
||||
// Update methods
|
||||
onSubmitSuccess: (data: any, action: 'confirm' | 'continue') => void;
|
||||
onSubmitError: (e: Object) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { register, setValue, handleSubmit, control } = form;
|
||||
|
||||
const {
|
||||
fields: selectEnums,
|
||||
append: appendEnums,
|
||||
remove: removeEnums
|
||||
} = useFieldArray({
|
||||
control,
|
||||
name: 'list'
|
||||
});
|
||||
|
||||
const valueTypeSelectList = Object.values(FlowValueTypeMap).map((item) => ({
|
||||
label: t(item.label as any),
|
||||
value: item.value
|
||||
}));
|
||||
|
||||
const showValueTypeSelect =
|
||||
inputType === FlowNodeInputTypeEnum.reference ||
|
||||
inputType === FlowNodeInputTypeEnum.customVariable;
|
||||
|
||||
const showRequired = useMemo(() => {
|
||||
const list = [FlowNodeInputTypeEnum.addInputParam, FlowNodeInputTypeEnum.customVariable];
|
||||
return !list.includes(inputType);
|
||||
}, [inputType]);
|
||||
|
||||
const showMaxLenInput = useMemo(() => {
|
||||
const list = [FlowNodeInputTypeEnum.input, FlowNodeInputTypeEnum.textarea];
|
||||
return list.includes(inputType);
|
||||
}, [inputType]);
|
||||
|
||||
const showMinMaxInput = useMemo(() => {
|
||||
const list = [FlowNodeInputTypeEnum.numberInput];
|
||||
return list.includes(inputType);
|
||||
}, [inputType]);
|
||||
|
||||
const showDefaultValue = useMemo(() => {
|
||||
const list = [
|
||||
FlowNodeInputTypeEnum.input,
|
||||
FlowNodeInputTypeEnum.textarea,
|
||||
FlowNodeInputTypeEnum.JSONEditor,
|
||||
FlowNodeInputTypeEnum.numberInput,
|
||||
FlowNodeInputTypeEnum.switch
|
||||
];
|
||||
|
||||
return list.includes(inputType);
|
||||
}, [inputType]);
|
||||
|
||||
return (
|
||||
<Stack flex={1} borderLeft={'1px solid #F0F1F6'} justifyContent={'space-between'}>
|
||||
<Flex flexDirection={'column'} p={8} gap={4} flex={'1 0 0'} overflow={'auto'}>
|
||||
<Flex alignItems={'center'}>
|
||||
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
|
||||
{type === 'formInput'
|
||||
? t('common:core.module.input_name')
|
||||
: t('common:core.module.Field Name')}
|
||||
</FormLabel>
|
||||
<Input
|
||||
bg={'myGray.50'}
|
||||
placeholder="appointment/sql"
|
||||
{...register(type === 'formInput' ? 'label' : 'key', {
|
||||
required: true
|
||||
})}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex alignItems={'flex-start'}>
|
||||
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
|
||||
{type === 'formInput'
|
||||
? t('common:core.module.input_description')
|
||||
: t('workflow:field_description')}
|
||||
</FormLabel>
|
||||
<Textarea
|
||||
bg={'myGray.50'}
|
||||
placeholder={t('workflow:field_description_placeholder')}
|
||||
rows={3}
|
||||
{...register('description', { required: isToolInput ? true : false })}
|
||||
/>
|
||||
</Flex>
|
||||
|
||||
{/* value type */}
|
||||
{valueType && (
|
||||
<Flex alignItems={'center'}>
|
||||
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
|
||||
{t('common:core.module.Data Type')}
|
||||
</FormLabel>
|
||||
{showValueTypeSelect ? (
|
||||
<Box flex={1}>
|
||||
<MySelect<WorkflowIOValueTypeEnum>
|
||||
list={valueTypeSelectList.filter(
|
||||
(item) => item.value !== WorkflowIOValueTypeEnum.arrayAny
|
||||
)}
|
||||
value={valueType}
|
||||
onchange={(e) => {
|
||||
setValue('valueType', e);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
) : (
|
||||
<Box fontSize={'14px'}>{defaultValueType}</Box>
|
||||
)}
|
||||
</Flex>
|
||||
)}
|
||||
{showRequired && (
|
||||
<Flex alignItems={'center'} minH={'40px'}>
|
||||
<FormLabel flex={'1'} fontWeight={'medium'}>
|
||||
{t('workflow:field_required')}
|
||||
</FormLabel>
|
||||
<Switch {...register('required')} />
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
{/* reference */}
|
||||
{inputType === FlowNodeInputTypeEnum.reference && (
|
||||
<>
|
||||
<Flex alignItems={'center'} minH={'40px'}>
|
||||
<FormLabel flex={'1'} fontWeight={'medium'}>
|
||||
{t('workflow:field_used_as_tool_input')}
|
||||
</FormLabel>
|
||||
<Switch
|
||||
isChecked={isToolInput}
|
||||
onChange={(e) => {
|
||||
setIsToolInput && setIsToolInput();
|
||||
console.log(isToolInput);
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
</>
|
||||
)}
|
||||
|
||||
{showMaxLenInput && (
|
||||
<Flex alignItems={'center'}>
|
||||
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
|
||||
{t('common:core.module.Max Length')}
|
||||
</FormLabel>
|
||||
<MyNumberInput
|
||||
flex={'1 0 0'}
|
||||
bg={'myGray.50'}
|
||||
placeholder={t('common:core.module.Max Length placeholder')}
|
||||
value={maxLength}
|
||||
onChange={(e) => {
|
||||
// @ts-ignore
|
||||
setValue('maxLength', e || '');
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
{showMinMaxInput && (
|
||||
<>
|
||||
<Flex alignItems={'center'}>
|
||||
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
|
||||
{t('common:core.module.Max Value')}
|
||||
</FormLabel>
|
||||
<MyNumberInput
|
||||
flex={'1 0 0'}
|
||||
bg={'myGray.50'}
|
||||
value={max}
|
||||
onChange={(e) => {
|
||||
// @ts-ignore
|
||||
setValue('max', e || '');
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
<Flex alignItems={'center'}>
|
||||
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
|
||||
{t('common:core.module.Min Value')}
|
||||
</FormLabel>
|
||||
<MyNumberInput
|
||||
flex={'1 0 0'}
|
||||
bg={'myGray.50'}
|
||||
value={min}
|
||||
onChange={(e) => {
|
||||
// @ts-ignore
|
||||
setValue('min', e || '');
|
||||
}}
|
||||
/>
|
||||
</Flex>
|
||||
</>
|
||||
)}
|
||||
|
||||
{showDefaultValue && (
|
||||
<Flex alignItems={'center'} minH={'40px'}>
|
||||
<FormLabel
|
||||
flex={inputType === FlowNodeInputTypeEnum.switch ? 1 : '0 0 100px'}
|
||||
fontWeight={'medium'}
|
||||
>
|
||||
{t('common:core.module.Default Value')}
|
||||
</FormLabel>
|
||||
{inputType === FlowNodeInputTypeEnum.numberInput && (
|
||||
<Input
|
||||
bg={'myGray.50'}
|
||||
max={max}
|
||||
min={min}
|
||||
type={'number'}
|
||||
{...register('defaultValue')}
|
||||
/>
|
||||
)}
|
||||
{inputType === FlowNodeInputTypeEnum.input && (
|
||||
<Input bg={'myGray.50'} maxLength={maxLength} {...register('defaultValue')} />
|
||||
)}
|
||||
{inputType === FlowNodeInputTypeEnum.textarea && (
|
||||
<Textarea bg={'myGray.50'} maxLength={maxLength} {...register('defaultValue')} />
|
||||
)}
|
||||
{inputType === FlowNodeInputTypeEnum.JSONEditor && (
|
||||
<JsonEditor
|
||||
bg={'myGray.50'}
|
||||
resize
|
||||
w={'full'}
|
||||
onChange={(e) => {
|
||||
setValue('defaultValue', e);
|
||||
}}
|
||||
defaultValue={String(defaultJsonValue)}
|
||||
/>
|
||||
)}
|
||||
{inputType === FlowNodeInputTypeEnum.switch && <Switch {...register('defaultValue')} />}
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
{inputType === FlowNodeInputTypeEnum.addInputParam && (
|
||||
<>
|
||||
<Flex alignItems={'center'}>
|
||||
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
|
||||
{t('common:core.module.Input Type')}
|
||||
</FormLabel>
|
||||
<Box fontSize={'14px'}>{t('workflow:only_the_reference_type_is_supported')}</Box>
|
||||
</Flex>
|
||||
<Box>
|
||||
<HStack mb={1}>
|
||||
<FormLabel fontWeight={'medium'}>{t('workflow:optional_value_type')}</FormLabel>
|
||||
<QuestionTip label={t('workflow:optional_value_type_tip')} />
|
||||
</HStack>
|
||||
<MultipleSelect<WorkflowIOValueTypeEnum>
|
||||
list={valueTypeSelectList}
|
||||
bg={'myGray.50'}
|
||||
value={selectValueTypeList || []}
|
||||
onSelect={(e) => {
|
||||
setValue('customInputConfig.selectValueTypeList', e);
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
|
||||
{inputType === FlowNodeInputTypeEnum.select && (
|
||||
<>
|
||||
<Flex flexDirection={'column'} gap={4}>
|
||||
{selectEnums.map((item, i) => (
|
||||
<Flex key={item.id} alignItems={'center'}>
|
||||
<FormLabel flex={'0 0 100px'} fontWeight={'medium'}>
|
||||
{`${t('common:core.module.variable.variable options')} ${i + 1}`}
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
fontSize={'12px'}
|
||||
bg={'myGray.50'}
|
||||
placeholder={`${t('common:core.module.variable.variable options')} ${i + 1}`}
|
||||
{...register(`list.${i}.label`, {
|
||||
required: true,
|
||||
onChange: (e: any) => {
|
||||
setValue(`list.${i}.value`, e.target.value);
|
||||
}
|
||||
})}
|
||||
/>
|
||||
</FormControl>
|
||||
{selectEnums.length > 1 && (
|
||||
<MyIcon
|
||||
ml={3}
|
||||
name={'delete'}
|
||||
w={'16px'}
|
||||
cursor={'pointer'}
|
||||
p={2}
|
||||
borderRadius={'md'}
|
||||
_hover={{ bg: 'red.100' }}
|
||||
onClick={() => removeEnums(i)}
|
||||
/>
|
||||
)}
|
||||
</Flex>
|
||||
))}
|
||||
</Flex>
|
||||
<Button
|
||||
variant={'whiteBase'}
|
||||
leftIcon={<MyIcon name={'common/addLight'} w={'16px'} />}
|
||||
onClick={() => appendEnums({ label: '', value: '' })}
|
||||
fontWeight={'medium'}
|
||||
fontSize={'12px'}
|
||||
w={'24'}
|
||||
py={2}
|
||||
>
|
||||
{t('common:core.module.variable add option')}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</Flex>
|
||||
|
||||
<Flex justify={'flex-end'} gap={3} pb={8} pr={8}>
|
||||
<Button variant={'whiteBase'} fontWeight={'medium'} onClick={onClose} w={20}>
|
||||
{t('common:common.Close')}
|
||||
</Button>
|
||||
<Button
|
||||
variant={'primaryOutline'}
|
||||
fontWeight={'medium'}
|
||||
onClick={handleSubmit(
|
||||
(data: FlowNodeInputItemType) => onSubmitSuccess(data, 'confirm'),
|
||||
onSubmitError
|
||||
)}
|
||||
w={20}
|
||||
>
|
||||
{t('common:common.Confirm')}
|
||||
</Button>
|
||||
{!isEdit && (
|
||||
<Button
|
||||
fontWeight={'medium'}
|
||||
onClick={handleSubmit(
|
||||
(data: FlowNodeInputItemType) => onSubmitSuccess(data, 'continue'),
|
||||
onSubmitError
|
||||
)}
|
||||
w={20}
|
||||
>
|
||||
{t('common:common.Continue_Adding')}
|
||||
</Button>
|
||||
)}
|
||||
</Flex>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(InputTypeConfig);
|
||||
@@ -40,7 +40,7 @@ const NodePluginInput = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||
|
||||
const [editField, setEditField] = useState<FlowNodeInputItemType>();
|
||||
|
||||
const onSubmit = ({ data }: { data: FlowNodeInputItemType; isChangeKey: boolean }) => {
|
||||
const onSubmit = (data: FlowNodeInputItemType) => {
|
||||
if (!editField) return;
|
||||
|
||||
if (editField?.key) {
|
||||
|
||||
@@ -15,7 +15,7 @@ import { SourceHandle } from './render/Handle';
|
||||
import { getHandleId } from '@fastgpt/global/core/workflow/utils';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { WorkflowContext } from '../../context';
|
||||
import { UserSelectOptionItemType } from '@fastgpt/global/core/workflow/template/system/userSelect/type';
|
||||
import { UserSelectOptionItemType } from '@fastgpt/global/core/workflow/template/system/interactive/type';
|
||||
import IOTitle from '../components/IOTitle';
|
||||
import RenderOutput from './render/RenderOutput';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user